Skip to content
Snippets Groups Projects
Select Git revision
  • 59e6659611741c4a199bf6ffc65b72ebd85b136f
  • main default protected
  • variant
3 results

SimulatorApplication.class

Blame
  • Forked from LABOUREL Arnaud / Firefighter template
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    main.c 11.74 KiB
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/adc.h>
    #include <zephyr/lorawan/lorawan.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/drivers/sensor.h>
    #include <zephyr/devicetree.h>
    #include <stdio.h>
    #include <stdint.h>
    
    #define LORAWAN_DEV_EUI         { 0xDD, 0xEE, 0xAA, 0xDD, 0xBB, 0xEE, 0xEE, 0xFE }
    #define LORAWAN_JOIN_EUI        { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
    #define LORAWAN_APP_KEY         { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }
    #define DELAY                   K_MSEC(60000)
    #define MAX_RETRY               5
    #define ADC_NODE                DT_ALIAS(adc0)
    #define ADC_RESOLUTION          12
    
    static int16_t sample_buffer[1];
    
    LOG_MODULE_REGISTER(lorawan_class_a);
    
    /* Structure pour contenir les données des capteurs */
    struct sensor_data {
        uint16_t pm25;
        uint16_t pm10;
        uint16_t ppm;
        uint16_t humidity;
        int16_t temperature;
        uint32_t pressure;
        uint8_t battery;
        uint16_t co2;
    };
    
    /* Initialisation des données capteurs (certaines valeurs restent simulées) */
    static struct sensor_data data = {
        .pm25 = 0x000F,
        .pm10 = 0x0014,
        .ppm  = 0x015E,
        .humidity = 0x003C,
        .temperature = 0x00EB,
        .pressure = 0,
        .battery = 0x62,
        .co2 = 0x01F4
    };
    
    /* Périphérique ADC et configuration du canal */
    static const struct device *adc = DEVICE_DT_GET(ADC_NODE);
    static const struct adc_channel_cfg channel_cfg = {
        .gain = ADC_GAIN_1_6,
        .reference = ADC_REF_INTERNAL,
        .acquisition_time = ADC_ACQ_TIME_DEFAULT,
        .channel_id = 0,
        .input_positive = SAADC_CH_PSELP_PSELP_VDD,
    };
    
    
    // Fonction pour convertir les données de la structure en tableau de bytes
    static void get_payload(uint8_t *payload, struct sensor_data *data) {
        payload[0] = data->pm25 & 0xFF;
        payload[1] = (data->pm25 >> 8) & 0xFF;
        payload[2] = data->pm10 & 0xFF;
        payload[3] = (data->pm10 >> 8) & 0xFF;
        payload[4] = data->ppm & 0xFF;
        payload[5] = (data->ppm >> 8) & 0xFF;
        payload[6] = data->humidity & 0xFF;
        payload[7] = (data->humidity >> 8) & 0xFF;
        payload[8] = data->temperature & 0xFF;
        payload[9] = (data->temperature >> 8) & 0xFF;
        payload[10] = data->pressure & 0xFF;
        payload[11] = (data->pressure >> 8) & 0xFF;
        payload[12] = (data->pressure >> 16) & 0xFF;
        payload[13] = data->battery;
        payload[14] =  data->co2 & 0xFF;
        payload[15] =(data->co2 >> 8) & 0xFF;
    }
    
    /* Callback de réception en downlink */
    static void dl_callback(uint8_t port, bool data_pending,
                int16_t rssi, int8_t snr,
                uint8_t len, const uint8_t *hex_data)
    {
        LOG_INF("Port %d, Pending %d, RSSI %ddB, SNR %ddBm", port, data_pending, rssi, snr);
        if (hex_data) {
            LOG_HEXDUMP_INF(hex_data, len, "Payload reçu : ");
        }
    }
    
    /* Callback sur changement de datarate */
    static void lorwan_datarate_changed(enum lorawan_datarate dr)
    {
        uint8_t unused, max_size;
        lorawan_get_payload_sizes(&unused, &max_size);
        LOG_INF("Nouvelle datarate : DR_%d, Taille max du payload : %d", dr, max_size);
    }
    
    /* Configuration du réseau LoRaWAN */
    static int configure_lorawan(void) {
        int ret;
        ret = lorawan_set_region(LORAWAN_REGION_EU868);
        if (ret < 0) {
            LOG_ERR("Erreur de configuration de la région : %d", ret);
            return ret;
        }
        ret = lorawan_start();
        if (ret < 0) {
            LOG_ERR("Échec du démarrage de LoRaWAN : %d", ret);
            return ret;
        }
        ret = lorawan_set_datarate(LORAWAN_DR_5);
        if (ret < 0) {
            LOG_ERR("Échec de la configuration du datarate : %d", ret);
            return ret;
        }
        return 0;
    }
    
    /* Lecture de la valeur ADC */
    static int read_adc_value(int *adc_value) {
        int ret;
        struct adc_sequence sequence = {
            .channels    = BIT(channel_cfg.channel_id),
            .buffer      = sample_buffer,
            .buffer_size = sizeof(sample_buffer),
            .resolution  = ADC_RESOLUTION,
        };
        ret = adc_read(adc, &sequence);
        if (ret < 0) {
            LOG_ERR("Erreur de lecture de l'ADC : %d", ret);
            return ret;
        }
        *adc_value = sample_buffer[0];
        return 0;
    }
    
    /* Calcul de la tension de la batterie à partir de la valeur ADC */
    double get_Tension(int32_t adc_value) {
        double gain = 1.0 / 6.0;
        double vref = 0.6;
        double tension = ((adc_value * vref) / (4095 * gain));
        return tension;
    }
    
    /* Calcul de la capacité restante de la batterie NiMH sur 2 cellules */
    double calculer_capacite_restante_NiMH_2_cells(double tension_batterie) {
        double capacite_restante;
        if (tension_batterie >= 2.7) {
            capacite_restante = (tension_batterie - 2.7) * (100 - 90) / (2.8 - 2.7) + 90;
        } else if (tension_batterie >= 2.6) {
            capacite_restante = (tension_batterie - 2.6) * (90 - 80) / (2.7 - 2.6) + 80;
        } else if (tension_batterie >= 2.5) {
            capacite_restante = (tension_batterie - 2.5) * (80 - 70) / (2.6 - 2.5) + 70;
        } else if (tension_batterie >= 2.4) {
            capacite_restante = (tension_batterie - 2.4) * (70 - 60) / (2.5 - 2.4) + 60;
        } else if (tension_batterie >= 2.3) {
            capacite_restante = (tension_batterie - 2.3) * (60 - 50) / (2.4 - 2.3) + 50;
        } else if (tension_batterie >= 2.2) {
            capacite_restante = (tension_batterie - 2.2) * (50 - 40) / (2.3 - 2.2) + 40;
        } else if (tension_batterie >= 2.1) {
            capacite_restante = (tension_batterie - 2.1) * (40 - 30) / (2.2 - 2.1) + 30;
        } else if (tension_batterie >= 2.0) {
            capacite_restante = (tension_batterie - 2.0) * (30 - 20) / (2.1 - 2.0) + 20;
        } else {
            capacite_restante = 0;
        }
        return capacite_restante;
    }
    
    /* Mise à jour des données du capteur BME280 et stockage dans la structure globale */
    static int update_bme280_data(const struct device *dev) {
        struct sensor_value temperature, humidity, pressure, pm25, pm10;
        int ret;
        ret = sensor_sample_fetch(dev);
        if (ret < 0) {
            LOG_ERR("Erreur lors de la récupération des données du BME280 : %d", ret);
            return ret;
        }
        ret = sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temperature);
        if (ret < 0) {
            LOG_ERR("Erreur lors de la récupération de la température : %d", ret);
            return ret;
        }
        ret = sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity);
        if (ret < 0) {
            LOG_ERR("Erreur lors de la récupération de l'humidité : %d", ret);
            return ret;
        }
        ret = sensor_channel_get(dev, SENSOR_CHAN_PRESS, &pressure);
        if (ret < 0) {
            LOG_ERR("Erreur lors de la récupération de la pression : %d", ret);
            return ret;
        }
        // val1 is integer part, val2 is in 1/1000000 
        LOG_INF("Température : %d.%d°C", temperature.val1, temperature.val2/100000); // val2 as 1/10
        data.temperature = 10 * temperature.val1 + (temperature.val1 < 0 ? - temperature.val2 : temperature.val2)/100000;
        LOG_INF("Humidité : %d.%2d%%", humidity.val1, humidity.val2/10000); // 1/100 resolution
        data.humidity= 100* humidity.val1 + humidity.val2/10000;
        LOG_INF("Pression : %d.%3d kPa", pressure.val1, pressure.val2/1000);
        data.pressure = pressure.val1*1000 + pressure.val2 / 1000; // 1/100 hPa unit
    
    
        sensor_channel_get(dev, SENSOR_CHAN_PM_2_5, &pm25);
        sensor_channel_get(dev, SENSOR_CHAN_PM_10, &pm10);
    
        LOG_INF("pm25 est : %d ", pm25.val1);
        data.pm25 = pm25.val1;
    
        LOG_INF("pm10 est : %d", pm10.val1);
        data.pm10 = pm10.val1;
    
        return 0;
    }
    
    /* Fonction d'envoi des données avec réessais */
    static int send_data_with_retry(uint8_t *payload, size_t size) {
        int ret, retry_count = 0;
        do {
            ret = lorawan_send(2, payload, size, LORAWAN_MSG_CONFIRMED);
            if (ret < 0) {
                LOG_ERR("Échec de l'envoi des données (tentative %d) : %d", retry_count + 1, ret);
                retry_count++;
                k_sleep(DELAY);
            }
        } while (ret < 0 && retry_count < MAX_RETRY);
        return ret;
    }
    
    /* Fonction pour rejoindre le réseau LoRaWAN */
    static int join_lorawan(void) {
        struct lorawan_join_config join_cfg;
        uint8_t dev_eui[] = LORAWAN_DEV_EUI;
        uint8_t join_eui[] = LORAWAN_JOIN_EUI;
        uint8_t app_key[] = LORAWAN_APP_KEY;
        int ret;
        join_cfg.mode = LORAWAN_ACT_OTAA;
        join_cfg.dev_eui = dev_eui;
        join_cfg.otaa.join_eui = join_eui;
        join_cfg.otaa.app_key = app_key;
        join_cfg.otaa.nwk_key = app_key;
        join_cfg.otaa.dev_nonce = 0u;
        LOG_INF("Tentative de joindre le réseau en OTAA");
        do {
            ret = lorawan_join(&join_cfg);
            if (ret < 0) {
                LOG_ERR("Échec de lorawan_join_network : %d", ret);
                k_sleep(K_MSEC(1000));
            }
        } while (ret < 0);
        return 0;
    }
    
    /* Déclaration d'un sémaphore pour signaler la disponibilité de nouvelles données */
    K_SEM_DEFINE(sensor_data_sem, 0, 1);
    
    /* Thread dédié à la lecture des capteurs (BME280 et ADC) */
    void sensor_thread(void *arg1, void *arg2, void *arg3) {
        int adc_value;
        int ret;
        double tension_batterie, capacite_restante;
        const struct device *bme_dev = DEVICE_DT_GET_ANY(bosch_bme280);
        if (!device_is_ready(bme_dev)) {
            LOG_ERR("Capteur BME280 non disponible");
            return;
        }
        /* Configuration du canal ADC */
        ret = adc_channel_setup(adc, &channel_cfg);
        if (ret < 0) {
            LOG_ERR("Échec de la configuration du canal ADC : %d", ret);
            return;
        }
        while (1) {
            /* Lecture de la valeur ADC pour la tension de la batterie */
            ret = read_adc_value(&adc_value);
            if (ret < 0) {
                LOG_ERR("Erreur de lecture de l'ADC.");
            } else {
                tension_batterie = get_Tension(adc_value);
                capacite_restante = calculer_capacite_restante_NiMH_2_cells(tension_batterie);
                data.battery = (uint8_t)capacite_restante;
                LOG_INF("Tension de la batterie : %0.2f V, Capacité restante : %0.2f%%", tension_batterie, capacite_restante);
            }
            /* Mise à jour des données du capteur BME280 */
            ret = update_bme280_data(bme_dev);
            if (ret < 0) {
                LOG_ERR("Erreur de récupération des données BME280.");
            }
            /* Signaler au thread LoRa que de nouvelles données sont disponibles */
            k_sem_give(&sensor_data_sem);
            k_sleep(DELAY);
        }
    }
    
    /* Thread dédié à la communication LoRaWAN */
    void lora_thread(void *arg1, void *arg2, void *arg3) {
        int ret;
        uint8_t payload[16];
        struct lorawan_downlink_cb downlink_cb = {
            .port = LW_RECV_PORT_ANY,
            .cb = dl_callback
        };
        const struct device *lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
        if (!device_is_ready(lora_dev)) {
            LOG_ERR("%s : périphérique non prêt.", lora_dev->name);
            return;
        }
        /* Configuration et démarrage du LoRaWAN */
        ret = configure_lorawan();
        if (ret < 0) {
            return;
        }
        lorawan_register_downlink_callback(&downlink_cb);
        lorawan_register_dr_changed_callback(lorwan_datarate_changed);
        if (join_lorawan() < 0) {
            return;
        }
        while (1) {
            /* Attendre que le thread capteur signale que de nouvelles données sont prêtes */
            k_sem_take(&sensor_data_sem, K_FOREVER);
            get_payload(payload, &data);
            ret = send_data_with_retry(payload, sizeof(payload));
            if (ret < 0) {
                LOG_ERR("Échec de l'envoi des données après %d tentatives.", MAX_RETRY);
            } else {
                LOG_INF("Données envoyées avec succès.");
            }
        }
    }
    
    /* Création des threads via K_THREAD_DEFINE */
    K_THREAD_DEFINE(sensor_thread_id, 1024, sensor_thread, NULL, NULL, NULL, 5, 0, 0);
    K_THREAD_DEFINE(lora_thread_id, 1024, lora_thread, NULL, NULL, NULL, 5, 0, 0);
    
    int main(void) {
        /* Les threads sensor_thread et lora_thread démarrent automatiquement.
         * Le thread main peut ainsi rester en veille ou réaliser d'autres tâches.
         */
        while (1) {
            k_sleep(K_MSEC(1000));
        }
    }