From 50bdd69c4f28fcdfbca0a93da4b23bc6a98d3b0f Mon Sep 17 00:00:00 2001 From: anschrammh Date: Tue, 4 Nov 2025 22:03:12 +0100 Subject: [PATCH] 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 --- src/app/app.ino | 186 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 124 insertions(+), 62 deletions(-) diff --git a/src/app/app.ino b/src/app/app.ino index 6a45635..61e1e65 100644 --- a/src/app/app.ino +++ b/src/app/app.ino @@ -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 @@ -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(pin), static_cast(mode)); + PCF.pinMode(static_cast(pin), static_cast(mode)); } void PCFDigitalWrite(uint8_t pin, uint8_t val) { - PCF.digitalWrite(static_cast(pin), static_cast(val)); + PCF.digitalWrite(static_cast(pin), static_cast(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.