Updated the app source file to add support to the RFM95W LoRa module. The gateway is now able to handle packets received by LoRa sensors. LoRa settings are : SF 10, 868Mhz band, CRC enabled

This commit is contained in:
anschrammh 2025-11-04 22:03:12 +01:00
parent 19d71b8735
commit 97bdcec0cf

View File

@ -1,7 +1,8 @@
/**
* Author : Anatole SCHRAMM-HENRY
* Created the : 11/07/2021
* This is a quick and dirty firmewire to receive and save the data in the database
* Last updated : 04/11/2025
* This is a quick and dirty gateway firmware to receive and save data from sensors to a database
*/
#include <SPI.h>
@ -13,17 +14,27 @@
#include "definition.h"
#include "credentials.h"
/* NRF pinout definition */
#define NRF_1_CE 15 //chip enable
#define NRF_2_CE 16 //chip enable
/* NRF pinout definition and settings */
#define NRF_1_CE (15) //chip enable
#define NRF_1_CS (5) //chip select
#define NRF_1_IRQ (PCF8574::P1)
#define NRF_1_CS 5 //chip select
#define NRF_2_CS 4 //chip select
#define NRF_2_CE (16) //chip enable
#define NRF_2_CS (4) //chip select
#define NRF_2_IRQ (PCF8574::P0)
#define CHAN 108 //0-125
#define RECV_CHECK 5000 //We test every 5s if we received something
#define WIFI_CHECK_TIMEOUT 20000
#define PREVENTIVE_RESET_DELAY 18000000 //Every 5 hours
#define NRF_CHANNEL (108) //0-125
#define NRF_PA_LEVEL (RF24_PA_MAX)
#define NRF_DATA_RATE (RF24_250KBPS) //250 Kb/s, 1 Mb/s & 2 Mb/s
#define RECV_CHECK 5000 //We test every 5s if we received something that did not trigger an IRQ (missed)
#define WIFI_CHECK_TIMEOUT 20000 //We check every 20 seconds the WiFi state
#define PREVENTIVE_RESET_DELAY 18000000 //Every 5 hours the MCU is reseted, this SHOULD NOT be necessary
/* RGB LED pinout */
#define LED_RED_PIN (PCF8574::P2)
#define LED_GREEN_PIN (PCF8574::P3)
#define LED_BLUE_PIN (PCF8574::P4)
/* LoRa module pinout definition (using the IO expander) */
#define LORA_CS_PIN (PCF8574::P5)
@ -31,12 +42,13 @@
#define LORA_DI0_PIN (PCF8574::P7)
#define LORA_BAND (868E6)
#define LORA_SPREADING_FACTOR (10) //6-12
/* PCF8574 IO expander pinout */
#define PCF_INT_PIN (D9)
uint8_t payload[32] = {0};
DataPacket dp;
DataPacket packetHeader;
WeatherStationDataPacket wsdp;
MailboxDataPacket mdp;
@ -55,31 +67,34 @@ uint32_t timeStamp(0), irqSaver(0), resetTimeStamp(0);
uint32_t freeMem(0);
uint16_t biggestContigMemBlock(0);
uint8_t frag(0);
int insertIntoDBError(0);
HttpClient::HttpQueryStatus insertIntoDBError(HttpClient::HttpQueryStatus::SUCCESS);
volatile boolean IRQFlag(false);
volatile boolean IRQIsLoRa(false);
boolean waitingForResetSignal(false), resetNow(false);
void PCFPinMode(uint8_t pin, uint8_t mode)
{
PCF.pinMode(static_cast<PCF8574::Pin>(pin), static_cast<boolean>(mode));
PCF.pinMode(static_cast<PCF8574::Pin>(pin), static_cast<boolean>(mode));
}
void PCFDigitalWrite(uint8_t pin, uint8_t val)
{
PCF.digitalWrite(static_cast<PCF8574::Pin>(pin), static_cast<boolean>(val));
PCF.digitalWrite(static_cast<PCF8574::Pin>(pin), static_cast<boolean>(val));
}
void onLoRaReceive(int payload_size)
{
PCF.digitalWrite(PCF8574::P2, LOW);
Serial.printf("LoRa packet size : %d\n", payload_size);
IRQIsLoRa = true;
PCF.digitalWrite(LED_RED_PIN, LOW);
for(int i = 0; i < payload_size; i++)
{
Serial.printf("%c", LoRa.read());
}
Serial.printf("\nPacket RSSI : %d dBm\n", LoRa.packetRssi());
PCF.digitalWrite(PCF8574::P2, HIGH);
int read_size = LoRa.read(payload, sizeof(payload));
Serial.printf("Read LoRa payload size of : %d\n", read_size);
if (read_size != payload_size)
Serial.printf("/!\\Read size(%d) != payload size(%d)\n", read_size, payload_size);
Serial.printf("Packet RSSI : %d dBm\n", LoRa.packetRssi());
PCF.digitalWrite(LED_RED_PIN, HIGH);
}
IRAM_ATTR void PCFIRQHandler(void *p)
@ -89,7 +104,7 @@ IRAM_ATTR void PCFIRQHandler(void *p)
PCF.digitalReadAll(pinStates);
*(boolean *)p = true;
/* If the IRQ pin is the DI0 one, manually call the LoRa IRQ handler */
/* If the IRQ pin is the DIO1, manually call the LoRa IRQ handler */
if(pinStates[7] == HIGH)
{
LoRa.handleDio0Rise();
@ -111,8 +126,9 @@ void setup()
WiFi.softAPdisconnect(true);
Serial.println("Connecting to the access point");
WiFi.begin(SSID, PWD);
//We initialize the dictionary
//These keys are used to post data for the weather station :
//We initialize the dictionary
//These keys are used to post data for the weather station :
weatherStationPostData.add("accessCode", DictionaryHelper::StringEntity(ACCESS_CODE));
weatherStationPostData.add("deviceType", DictionaryHelper::StringEntity(WEATHER_STATION_DEV_TYPE));
weatherStationPostData.add("packetUID", DictionaryHelper::StringEntity(NULL));
@ -123,23 +139,24 @@ void setup()
weatherStationPostData.add("humidity", DictionaryHelper::StringEntity(NULL));
weatherStationPostData.add("compensated_humidity", DictionaryHelper::StringEntity(NULL));
weatherStationPostData.add("htu_tmp", DictionaryHelper::StringEntity(NULL));
//These keys are used to post data for the mailbox
//These keys are used to post data for the mailbox :
mailboxPostData.add("accessCode", DictionaryHelper::StringEntity(ACCESS_CODE));
mailboxPostData.add("deviceType", DictionaryHelper::StringEntity(MAILBOX_DEV_TYPE));
mailboxPostData.add("packetUID", DictionaryHelper::StringEntity(NULL));
mailboxPostData.add("battery", DictionaryHelper::StringEntity(NULL));
mailboxPostData.add("event", DictionaryHelper::StringEntity(NULL));
Serial.printf("\nNRF 1 %s\n",NRF_1.begin() ? "started" : "error");
if(!NRF_1.isChipConnected())
Serial.println("NRF 1 is missing");
else
Serial.println("NRF 1 is detected");
NRF_1.setChannel(CHAN);
NRF_1.setPALevel(RF24_PA_MIN);
NRF_1.setDataRate(RF24_250KBPS);
NRF_1.setChannel(NRF_CHANNEL);
NRF_1.setPALevel(NRF_PA_LEVEL);
NRF_1.setDataRate(NRF_DATA_RATE);
NRF_1.setRetries(8,15);
NRF_1.openReadingPipe(1, ADDR);
NRF_1.startListening();
@ -150,9 +167,9 @@ void setup()
else
Serial.println("NRF 2 is detected");
NRF_2.setChannel(CHAN);
NRF_2.setPALevel(RF24_PA_MIN);
NRF_2.setDataRate(RF24_250KBPS);
NRF_2.setChannel(NRF_CHANNEL);
NRF_2.setPALevel(NRF_PA_LEVEL);
NRF_2.setDataRate(NRF_DATA_RATE);
NRF_2.setRetries(8,15);
NRF_2.openReadingPipe(1, ADDR);
NRF_2.startListening();
@ -160,38 +177,82 @@ void setup()
//Setting the I2C pins and the PCF8574
Wire.begin(0,2);
Serial.printf("PCF %s\n", PCF.begin() ? "found" : "not found");
PCF.pinMode(PCF8574::P2, OUTPUT);
PCF.pinMode(PCF8574::P3, OUTPUT);
PCF.pinMode(PCF8574::P4, OUTPUT);
PCF.pinMode(LED_RED_PIN, OUTPUT);
PCF.pinMode(LED_GREEN_PIN, OUTPUT);
PCF.pinMode(LED_BLUE_PIN, OUTPUT);
/* Configuring LoRa module and RF settings */
LoRa.setPins(LORA_CS_PIN, LORA_RST_PIN, LORA_DI0_PIN);
/* Setting custom GPIO as the LoRa module signals are driven by the IO Expender */
LoRa.setCustomGPIOFn(&(PCFPinMode), &(PCFDigitalWrite));
Serial.printf("LoRa module %s\n", LoRa.begin(LORA_BAND) ? "found" : "not found");
Serial.println("######### LoRa regs : #########");
LoRa.dumpRegisters(Serial);
Serial.println("###############################");
LoRa.setSpreadingFactor(10);
LoRa.setSpreadingFactor(LORA_SPREADING_FACTOR);
LoRa.enableCrc();
LoRa.onReceive(&(onLoRaReceive));
/* Let's start listening to LoRa packets */
// Let's start listening to LoRa packets
LoRa.receive();
//We set the RXD0 as a GPIO
//We set the esp8266's RXD0 as a GPIO
pinMode(PCF_INT_PIN, INPUT_PULLUP);
attachInterruptArg(PCF_INT_PIN, &(PCFIRQHandler), (void *)&IRQFlag, ONLOW /*FALLING*/);
attachInterruptArg(PCF_INT_PIN, &(PCFIRQHandler), (void *)&IRQFlag, ONLOW);
Serial.println("End setup");
}
void loop()
{
//We check if we got and IRQ from one, both NRFs
//We check if we got and IRQ from one, both NRFs or the LoRa module
if(IRQFlag || (millis() - irqSaver > RECV_CHECK))
{
if(!IRQIsLoRa && PCF.digitalRead(LORA_DI0_PIN))
{
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!IRQ MISSED FOR LoRa MODULE");
if(LoRa.parsePacket() > 0)
{
LoRa.read(payload, sizeof(payload));
//Set IRQ to true to handle LoRa received payload
IRQIsLoRa = true;
}
LoRa.receive();
}
if(IRQIsLoRa)
{
//Payload already retrieved in LoRa module IRQ or in the previous code part
memcpy(&packetHeader, payload, sizeof(packetHeader));
switch(packetHeader.header)
{
case WEATHER_STATION:
{
memcpy(&wsdp, payload, sizeof(wsdp));
debugStruct(&wsdp);
insertIntoDBError = insertIntoDB(&wsdp);
}
break;
case CONNECTED_MAILBOX:
{
memcpy(&mdp, payload, sizeof(mdp));
debugStruct(&mdp);
insertIntoDBError = insertIntoDB(&mdp);
}
break;
default:
break;
}
if(waitingForResetSignal)resetNow = true;
IRQIsLoRa = false;
}
if(PCF.digitalRead(LORA_DI0_PIN))
{
Serial.println("LoRa module DI0 stuck high !");
}
bool tx_ok, tx_fail, rx_ready;
//We read the PCFs IO to check which NRF raised the IRQ :
if(!PCF.digitalRead(PCF8574::P1)) //IRQs are active low
if(!PCF.digitalRead(NRF_1_IRQ)) //IRQs are active low
{
if(!IRQFlag)
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!IRQ MISSED FOR NRF 1");
@ -210,9 +271,9 @@ void loop()
Serial.printf("NRF 1 Received %u bytes with sig(%s) : \n",NRF_1.getPayloadSize(), NRF_1.testRPD()?"good":"bad");
NRF_1.read(payload, sizeof(payload));
memcpy(&dp, payload, sizeof(dp));
memcpy(&packetHeader, payload, sizeof(packetHeader));
switch(dp.header)
switch(packetHeader.header)
{
case WEATHER_STATION:
{
@ -236,7 +297,7 @@ void loop()
}
}
if(!PCF.digitalRead(PCF8574::P0)) //IRQs are active low
if(!PCF.digitalRead(NRF_2_IRQ)) //IRQs are active low
{
if(!IRQFlag)
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!IRQ MISSED FOR NRF 2");
@ -255,9 +316,9 @@ void loop()
Serial.printf("NRF 2 Received %u bytes with sig(%s) : \n",NRF_2.getPayloadSize(), NRF_2.testRPD()?"good":"bad");
NRF_2.read(payload, sizeof(payload));
memcpy(&dp, payload, sizeof(dp));
memcpy(&packetHeader, payload, sizeof(packetHeader));
switch(dp.header)
switch(packetHeader.header)
{
case WEATHER_STATION:
{
@ -301,11 +362,12 @@ void loop()
//Here we check if we are still connected
if(millis() - timeStamp > WIFI_CHECK_TIMEOUT)
{
//PCF.togglePin(PCF8574::P2);
//PCF.togglePin(PCF8574::P3);
//PCF.togglePin(PCF8574::P4);
ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag);
printf("Memory Info :\n - Free Mem > %u\n - Heap frag > %u\n - Max block > %u\nSign strength : %d\n", freeMem, frag, biggestContigMemBlock,WiFi.RSSI());
printf("Memory Info :\n - Free Mem > %u\n - Heap frag > %u\n - Max block > %u\nWiFi RSSI : %d\n",
freeMem, frag,
biggestContigMemBlock,
WiFi.RSSI());
Serial.println("Checking wifi link : ");
if(WiFi.isConnected())
{
@ -327,7 +389,7 @@ void loop()
resetTimeStamp = millis();
}
if((waitingForResetSignal && resetNow) || insertIntoDBError == -125)
if((waitingForResetSignal && resetNow) || insertIntoDBError == HttpClient::HttpQueryStatus::ERR_CONN)
{
ESP.reset();
}
@ -348,10 +410,10 @@ void lostConnectionFunc(const WiFiEventStationModeDisconnected &event)
Serial.println("Lost connection, will try to reconnect ...");
}
int insertIntoDB(WeatherStationDataPacket *p)
HttpClient::HttpQueryStatus insertIntoDB(WeatherStationDataPacket *p)
{
char buffer[100] = "";
int result(0);
HttpClient::HttpQueryStatus result(HttpClient::HttpQueryStatus::SUCCESS);
//We get the values and put it in our dictionary
sprintf(buffer ,"%u", p->id);
weatherStationPostData("packetUID")->setString(buffer);
@ -377,7 +439,7 @@ int insertIntoDB(WeatherStationDataPacket *p)
sprintf(buffer, "%f", p->htuTemp);
weatherStationPostData("htu_tmp")->setString(buffer);
if((result = DBHost.sendHttpQuery(HttpClient::HttpRequestMethod::POST, NULL, &weatherStationPostData)) == 0)
if((result = DBHost.sendHttpQuery(HttpClient::HttpRequestMethod::POST, NULL, &weatherStationPostData)) == HttpClient::HttpQueryStatus::SUCCESS)
{
Serial.println("Data posted successfully");
HttpClient::HTTP_CODE response = DBHost.isReplyAvailable(2000);
@ -427,10 +489,10 @@ void debugStruct(WeatherStationDataPacket *p)
Serial.println(" *C");
}
int insertIntoDB(MailboxDataPacket *p)
HttpClient::HttpQueryStatus insertIntoDB(MailboxDataPacket *p)
{
char buffer[100] = "";
int result(0);
HttpClient::HttpQueryStatus result(HttpClient::HttpQueryStatus::SUCCESS);
//We get the values and put it in our dictionary
sprintf(buffer ,"%u", p->id);
mailboxPostData("packetUID")->setString(buffer);
@ -453,7 +515,7 @@ int insertIntoDB(MailboxDataPacket *p)
break;
}
if((result = DBHost.sendHttpQuery(HttpClient::HttpRequestMethod::POST, NULL, &mailboxPostData)) == 0)
if((result = DBHost.sendHttpQuery(HttpClient::HttpRequestMethod::POST, NULL, &mailboxPostData)) == HttpClient::HttpQueryStatus::SUCCESS)
{
Serial.println("Data posted successfully");
HttpClient::HTTP_CODE response = DBHost.isReplyAvailable(5000); //We increase the timeout because sending a mail on the server side takes some time.