Select Git revision
symfony.lock
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));
}
}