/**
Programmation noeud SDI12
Capteurs : RTC EM3028 + GS3 (Volumetric water content, température, conductivité électrique)
**/
#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 (30000) //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
uint32_t vbat_pin = PIN_VBAT;
#define PIN_LORA_DIO_1 47 // DIO1 GPIO pin for RAK4631
#define PROJET_NODE_BAND (RAK_REGION_EU868)
#define PROJET_NODE_DEVEUI \
{ 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x18, 0x3A, 0xBD } //Déclaration du DEVEUI
#define PROJET_NODE_APPEUI \
{ 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x18, 0x3A, 0xBD } // 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
/* variables de chaque capteur */
typedef struct __attribute__ ((__packed__)) {
float Raw_dielectric;
float EC;
float temperature;
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
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 parseResponse(String response, int sensor_address);
void uplink_routine();
//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
*************************************/
/** 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()));
#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);
/** Cayenne Low Power Payload */
uint8_t data_len = 0;
collected_data[data_len++] = highByte((int)sensor.Raw_dielectric);
collected_data[data_len++] = lowByte((int)sensor.Raw_dielectric);
collected_data[data_len++] = highByte((int)sensor.EC);
collected_data[data_len++] = lowByte((int)sensor.EC);
collected_data[data_len++] = highByte((int)sensor.temperature);
collected_data[data_len++] = lowByte((int)sensor.temperature);
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();
//sdiResponse="";
//delay(100);
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; // expliciter
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.EC = (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.Raw_dielectric= (value.toFloat());
#ifndef MAX_SAVE
Serial.print(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();
}