/**
Programmation noeud SDI12
Capteurs : RTC EM3028 + ATMOS14 (pression atmospherique, température, humidité, pression de vapeur)
**/
#include <lib.h>
#include <Arduino.h>
#include <Wire.h>
#include "RAK13010_SDI12.h"
#include "Melopero_RV3028.h" //http://librarymanager/All#Melopero_RV3028 bibliothèque pour le RTC
#include <time.h> /* time_t, struct tm, time, mktime */
#include "timestamp32bits.h"
// Comment la ligne ci-dessous pour debug (print des mesures)
#define MAX_SAVE
//define time interval measurement
#define SDI12_sensor_PERIOD (600000) //timer 600000 (10min)
#define TX_PIN WB_IO6 // The pin of the SDI-12 data bus.
#define RX_PIN WB_IO5 // The pin of the SDI-12 data bus.
#define OE WB_IO4 // Output enable pin, active low.
#define PIN_VBAT WB_A0 // // The pin of the ADC.
uint32_t vbat_pin = PIN_VBAT;
#define PIN_LORA_DIO_1 47 // DIO1 GPIO pin for RAK4631
/* variables de chaque capteur */
typedef struct __attribute__((__packed__)) {
float pressure_vapor;
float temperature;
float humidity;
float pressure;
float batteryVoltage;
uint8_t time_sep[8];
} SensorDataStruct; // sensor_data_t
SensorDataStruct sensor; // creation of the payload structure
uint8_t time_sep[8];
timestamp32bits stamp = timestamp32bits(); // time en format timestamp
int sensor_address = 0; // Adresse du capteur SDI12
Melopero_RV3028 rtc;
RAK_SDI12 mySDI12(RX_PIN, TX_PIN, OE);
String sdiResponse = ""; //variable String : réponse du capteur SDI12
//int sdiCommandIndex = 1;
String sdiCommand = "M!"; // commande SDI12 de lecture des capteurs (voir protocole SDI12)
String sdiCommand1 = "D0!"; // commande SDI12 de lecture des capteurs (voir protocole SDI12)
//Declaration des fonctions
void readFromSensor(int sensor_address);
void uplink_routine();
void parseResponse(String response, int sensor_address);
//PARTIE LORAWAN
/**************************************************************************************************************************
LoRaWAN band setting:
RAK_REGION_EU433
RAK_REGION_CN470
RAK_REGION_RU864
RAK_REGION_IN865
RAK_REGION_EU868
RAK_REGION_US915
RAK_REGION_AU915
RAK_REGION_KR920
RAK_REGION_AS923
*************************************/
#define PROJET_NODE_BAND (RAK_REGION_EU868)
#define PROJET_NODE_DEVEUI \
{ 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x0C, 0xC1, 0x37 } //Déclaration du DEVEUI
#define PROJET_NODE_APPEUI \
{ 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x0C, 0xC1, 0x37 } // Non utilisé
#define PROJET_NODE_APPKEY \
{ 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3E } // Declaration de l'APPEUI
/** Packet buffer for sending */
uint8_t collected_data[64] = { 0 };
void recvCallback(SERVICE_LORA_RECEIVE_T *data) {
if (data->BufferSize > 0) {
Serial.println("Something received!");
for (int i = 0; i < data->BufferSize; i++) {
Serial.printf("%x", data->Buffer[i]);
}
Serial.print("\r\n");
}
}
void joinCallback(int32_t status) {
Serial.printf("Join status: %d\r\n", status);
}
void sendCallback(int32_t status) {
if (status == 0) {
Serial.println("Successfully sent");
} else {
Serial.println("Sending failed");
}
}
/*****************************************************************************************************************************/
void setup() {
Serial.begin(115200, RAK_AT_MODE);
delay(2000);
Wire.begin(); //I2C RTC
rtc.initI2C(); // First initialize and create the rtc device
rtc.writeToRegister(0x35, 0x00);
rtc.writeToRegister(0x37, 0xB4);
rtc.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format
rtc.setTime(2024, 1, 10, 30, 18, 50, 00);
Serial.println("RAKwireless SDI12 sensor");
Serial.println("------------------------------------------------------");
// OTAA Device EUI MSB first
uint8_t node_device_eui[8] = PROJET_NODE_DEVEUI;
// OTAA Application EUI MSB first
uint8_t node_app_eui[8] = PROJET_NODE_APPEUI;
// OTAA Application Key MSB first
uint8_t node_app_key[16] = PROJET_NODE_APPKEY;
if (!api.system.lpm.set(1)) {
Serial.printf("LoRaWan SDI12 sensor - set low power mode is incorrect! \r\n");
return;
}
if (!api.lorawan.nwm.set(1)) {
Serial.printf("LoRaWan SDI12 sensor - set network working mode is incorrect! \r\n");
return;
}
if (!api.lorawan.appeui.set(node_app_eui, 8)) {
Serial.printf("LoRaWan SDI12 sensor - set application EUI is incorrect! \r\n");
return;
}
if (!api.lorawan.appkey.set(node_app_key, 16)) {
Serial.printf("LoRaWan SDI12 sensor - set application key is incorrect! \r\n");
return;
}
if (!api.lorawan.deui.set(node_device_eui, 8)) {
Serial.printf("LoRaWan SDI12 sensor - set device EUI is incorrect! \r\n");
return;
}
if (!api.lorawan.band.set(PROJET_NODE_BAND)) {
Serial.printf("LoRaWan SDI12 sensor - set band is incorrect! \r\n");
return;
}
if (!api.lorawan.deviceClass.set(RAK_LORA_CLASS_A)) {
Serial.printf("LoRaWan SDI12 sensor - set device class is incorrect! \r\n");
return;
}
if (!api.lorawan.njm.set(RAK_LORA_OTAA)) // Set the network join mode to OTAA
{
Serial.printf("LoRaWan SDI12 sensor - set network join mode is incorrect! \r\n");
return;
}
if (!api.lorawan.join()) // Join to Gateway
{
Serial.printf("LoRaWan SDI12 sensor - join fail! \r\n");
return;
}
Serial.println("++++++++++++++++++++++++++");
Serial.println("SDI12 sensor");
Serial.println("++++++++++++++++++++++++++");
/** Wait for Join success */
while (api.lorawan.njs.get() == 0) {
Serial.print("Wait for LoRaWAN join...");
api.lorawan.join();
delay(10000); //10000
}
if (!api.lorawan.adr.set(true)) {
Serial.printf("LoRaWan SDI12 sensor - set adaptive data rate is incorrect! \r\n");
return;
}
if (!api.lorawan.rety.set(1)) {
Serial.printf("LoRaWan SDI12 sensor - set retry times is incorrect! \r\n");
return;
}
if (!api.lorawan.cfm.set(1)) {
Serial.printf("LoRaWan SDI12 sensor - set confirm mode is incorrect! \r\n");
return;
}
/** Check LoRaWan Status*/
Serial.printf("Duty cycle is %s\r\n", api.lorawan.dcs.get() ? "ON" : "OFF"); // Check Duty Cycle status
Serial.printf("Packet is %s\r\n", api.lorawan.cfm.get() ? "CONFIRMED" : "UNCONFIRMED"); // Check Confirm status
uint8_t assigned_dev_addr[4] = { 0 };
api.lorawan.daddr.get(assigned_dev_addr, 4);
Serial.printf("Device Address is %02X%02X%02X%02X\r\n", assigned_dev_addr[0], assigned_dev_addr[1], assigned_dev_addr[2], assigned_dev_addr[3]); // Check Device Address
Serial.printf("Uplink period is %ums\r\n", SDI12_sensor_PERIOD);
Serial.println("");
api.lorawan.registerRecvCallback(recvCallback);
api.lorawan.registerJoinCallback(joinCallback);
api.lorawan.registerSendCallback(sendCallback);
if (api.system.timer.create(RAK_TIMER_0, (RAK_TIMER_HANDLER)uplink_routine, RAK_TIMER_PERIODIC) != true) {
Serial.printf("LoRaWan SDI12 sensor - Creating timer failed.\r\n");
return;
}
if (api.system.timer.start(RAK_TIMER_0, SDI12_sensor_PERIOD, NULL) != true) {
Serial.printf("LoRaWan SDI12 sensor - Starting timer failed.\r\n");
return;
}
}
//Fonction de transfert des data en Lorawan + appel des fonctions de lecture des capteurs
void uplink_routine() {
uint32_t time_sensor = (uint32_t)(stamp.timestamp((rtc.getYear() - 2000), rtc.getMonth(), rtc.getDate(), rtc.getHour(), rtc.getMinute(), rtc.getSecond())); //Formatage au format TimeStamp
#ifndef MAX_SAVE
Serial.println(String(time_sensor));
#endif
for (int8_t j = 0; j < 4; j++) {
sensor.time_sep[j] = ((time_sensor >> j * 8) & 0xFF);
}
#ifndef MAX_SAVE
Serial.println(sensor.time_sep[0], HEX);
Serial.println(sensor.time_sep[1], HEX);
Serial.println(sensor.time_sep[2], HEX);
Serial.println(sensor.time_sep[3], HEX);
uint32_t test = sensor.time_sep[0] | sensor.time_sep[1] << 8 | sensor.time_sep[2] << 16 | sensor.time_sep[3] << 24;
Serial.println(String(test));
#endif
//Lecture du capteur SDI12
readFromSensor(sensor_address); //read sensor SDI_adress=0
//Lecture de la valeur de la batterie
sensor.batteryVoltage = (int)(readVBAT() * 100);
/*Payload */
uint8_t data_len = 0;
collected_data[data_len++] = highByte((int)sensor.pressure_vapor);
collected_data[data_len++] = lowByte((int)sensor.pressure_vapor);
collected_data[data_len++] = highByte((int)sensor.temperature);
collected_data[data_len++] = lowByte((int)sensor.temperature);
collected_data[data_len++] = highByte((int)sensor.humidity);
collected_data[data_len++] = lowByte((int)sensor.humidity);
collected_data[data_len++] = highByte((int)sensor.pressure);
collected_data[data_len++] = lowByte((int)sensor.pressure);
collected_data[data_len++] = highByte((int)sensor.batteryVoltage);
collected_data[data_len++] = lowByte((int)sensor.batteryVoltage);
collected_data[data_len++] = sensor.time_sep[0];
collected_data[data_len++] = sensor.time_sep[1];
collected_data[data_len++] = sensor.time_sep[2];
collected_data[data_len++] = sensor.time_sep[3];
Serial.println("Data Packet:");
for (int i = 0; i < data_len; i++) {
Serial.printf("0x%02X ", collected_data[i]);
}
Serial.println("");
/** Send the data package */
if (api.lorawan.send(data_len, (uint8_t *)&collected_data, 2, true, 1)) {
Serial.println("Sending is requested");
} else {
Serial.println("Sending failed");
}
}
/***********************************************************************************************/
//Mesure du niveau de batterie
float readVBAT(void) {
// Set the analog reference to 3.0V (default = 3.6V)
analogReference(AR_INTERNAL_3_0);
// Set the resolution to 12-bit (0..4095)
analogReadResolution(12); // Can be 8, 10, 12 or 14
// Let the ADC settle
delay(1);
float raw;
// Get the raw 12-bit, 0..3000mV ADC value
raw = analogRead(vbat_pin);
//return raw * REAL_VBAT_MV_PER_LSB;
return raw * (3.0 / 4096) * 1.73;
}
/************************************************************************************************/
//Mesure des capteurs SDI12
void readFromSensor(int sensor_address) {
// Power the sensor.
pinMode(WB_IO2, OUTPUT);
digitalWrite(WB_IO2, HIGH);
delay(500); //1500
mySDI12.begin();
delay(50); //500
sdiResponse = "";
mySDI12.clearBuffer();
delay(50); //500
mySDI12.sendCommand(String(sensor_address) + sdiCommand);
delay(700); //700 pour ATMOS14 ECH20 GS3
mySDI12.clearBuffer();
mySDI12.sendCommand(String(sensor_address) + sdiCommand1);
delay(500);
sdiResponse = mySDI12.readStringUntil('\n');
#ifndef MAX_SAVE
Serial.println("D:" + sdiResponse);
#endif
parseResponse(sdiResponse, sensor_address);
mySDI12.end();
digitalWrite(WB_IO2, LOW);
}
/*******************************************************************************************/
//Formatage de la data SDI12 avant transfert en LoraWan
void parseResponse(String response, int sensor_address) {
int index = 0;
int nextIndex = 0;
int counter = 0;
String value = "";
while (index < response.length()) {
nextIndex = response.indexOf('+', index);
// If no other "+" sign is found, get the rest of the string.
if (nextIndex == -1) {
nextIndex = response.length();
}
value = response.substring(index, nextIndex);
//Serial.println(value);
switch (sensor_address) {
case 0:
switch (counter) {
case 0:
// Address of the sensor
break;
case 1:
sensor.pressure_vapor = (value.toFloat() * 100);
#ifndef MAX_SAVE
Serial.print(String(value.toFloat()) + ",");
#endif
break;
case 2:
sensor.temperature = (value.toFloat() * 10);
#ifndef MAX_SAVE
Serial.print(String(value.toFloat()) + ",");
#endif
break;
case 3:
sensor.humidity = (value.toFloat() * 100);
#ifndef MAX_SAVE
Serial.print(String(value.toFloat()) + ",");
#endif
break;
case 4:
sensor.pressure = (value.toFloat() * 100);
#ifndef MAX_SAVE
Serial.println(String(value.toFloat()));
#endif
break;
default:
break;
}
break;
}
index = nextIndex + 1;
counter = counter + 1;
}
#ifndef MAX_SAVE
Serial.println(sensor.batteryVoltage);
#endif
}
void loop() {
/* Destroy this busy loop and use timer to do what you want instead,
* so that the system thread can auto enter low power mode by api.system.lpm.set(1); */
api.system.scheduler.task.destroy();
}