Skip to content

Instantly share code, notes, and snippets.

@m3h0w
Created March 23, 2021 10:54
Show Gist options
  • Save m3h0w/f609a588b51d1bf7bdd671cd889ba9f4 to your computer and use it in GitHub Desktop.
Save m3h0w/f609a588b51d1bf7bdd671cd889ba9f4 to your computer and use it in GitHub Desktop.
// GW V1.7
// Low power store and forward firmware for GW_v1.x hardware
//#define SHOW_WDT_MSGS
//#define AOUT
#define Serial SerialUSB
#define DEBUG
//#define QUEUE_ERASE
#include "settings.h"
#include "definitions.h"
#include <cppQueue.h>
#include <RH_RF95.h>
#include <SPI.h>
#include <utils.h>
#include "gwMqtt.h"
#include "gwGsm.h"
#include "packetGenerator.h"
#include "nvmMgr.h"
#include "ssWdt.h"
#include "init.h"
#include <RTCZero.h>
#include "timeMgt.h"
void setup(void);
void loop(void);
void radioInit(void);
void updatePower(void);
void networkSleepSequence(void);
bool networkWakeSequence(void);
bool msgPush(uint8_t* msg, uint16_t msgLen);
bool modemSigPush(void);
bool modemNsStatPush(void);
bool statPush(uint8_t* msg, uint16_t statLen);
bool logPush(uint8_t* logMsg, uint16_t msgLen);
bool unifiedUploadSequence(void);
void sleepLoop(uint32_t sleepSeconds);
void plainSleepLoop(uint32_t sleepSeconds);
void sleepUc(void);
void updateBypassSw(void);
void enableLoraIrq(void);
void disableLoraIrq(void);
void loraRxIrqNvm(void);
void loraCrcErrIrq(void);
void loraTxDnIrq(void);
void mqttCallbackFunction(char* topic, byte* payload, unsigned int payLoadLength);
void system_reboot(void);
uint32_t nextWakeUp = 0;
uint32_t nextPwrUpd = 0;
uint32_t indexTest = 0;
uint8_t lMsg[256];
uint8_t msgTemp[256];
NVMMgr nvmSettings;
void setup()
{
Serial.begin(SERIAL_BAUDRATE);
Wire.begin();
pinsSetDefaultLevels();
pinsSetDirections();
initBlink();
initPeripherals();
wdt_init(); // THIS MUST BE RAN AFTER initPeriperals as its rtc.begin that sets up clock 2
sim800HardReset();
NVIC_SetPriority(WDT_IRQn, 0);
//NVIC_SetPriority(SYSTICK_IRQn, 1);
NVIC_SetPriority(EIC_IRQn, 1);
NVIC_SetPriority(RTC_IRQn, 2);
switch(PM->RCAUSE.reg)
{
case PM_RCAUSE_SYST:
Serial.println("Reset requested by system");
break;
case PM_RCAUSE_WDT:
Serial.println("Reset requested by Watchdog");
break;
case PM_RCAUSE_EXT:
Serial.println("External reset requested");
break;
case PM_RCAUSE_BOD33:
Serial.println("Reset brown out 3.3V");
break;
case PM_RCAUSE_BOD12:
Serial.println("Reset brown out 1.2v");
break;
case PM_RCAUSE_POR:
Serial.println("Normal power on reset");
break;
}
if(nvmSettings.begin())
Serial.println("Set default settings");
else
Serial.println("Read settings");
#ifdef QUEUE_ERASE
nvmSettings.eraseQueueSection();
#endif
/*for(uint8_t i = 0; i<15; i++)
{
nvmSettings.msgPush((uint8_t*)"test", 4);
}*/
//nvmSettings.printStatuses();
sleepTime = nvmSettings.getSleepTimeS();
gwTxInterval = nvmSettings.getTxItvS();
gwStatMsgItv = nvmSettings.getStatMsgItvS();
Serial.print("NSLT: ");
Serial.println(sleepTime);
Serial.print("GWTI: ");
Serial.println(gwTxInterval);
Serial.print("GSMI: ");
Serial.println(gwStatMsgItv);
// First we build the client ID
// This must be done first always
mqttSetIdAndTopic(GW_ID);
mqttSetCallbackFun(&mqttCallbackFunction);
// the modems that came with the assembled boards came in with a fixed baudrate of 115.2k
// that seems not to be totally gentle on the communication for some reason we will see at some point
// and publish fails, therefore set to autobaud and then a lower rate
Serial0.begin(115200);
modem.factoryDefault();
Serial0.end();
Serial0.begin(SERIAL_AT_BAUDRATE);
Serial.println(GATEWAY_FW_VER);
Serial.print(F("Gateway MQTT client id: "));
Serial.println(mqttClientId);
updatePower();
attachInterrupt(digitalPinToInterrupt(PIN_POWER_STAT_1), updatePower, CHANGE);
attachInterrupt(digitalPinToInterrupt(PIN_POWER_STAT_2), updatePower, CHANGE);
Serial.print(F("Saved node sleep time: "));
Serial.println(sleepTime);
Serial.print(F("Saved Tx interval:"));
Serial.println(gwTxInterval);
// must add proper error handling for this
radioInit();
#ifdef AOUT
initAout();
#endif
if (hSensor.isConnected())
{
Serial.println(F("Humidity sensor OK"));
}
else
{
Serial.println(F("Humidity sensor non responsive"));
memset(lMsg, 0, 255);
sprintf((char*)lMsg, "hSensorFail");
logPush(lMsg, strlen((char*)lMsg));
}
//fuelGauge.enableDebugging();
fuelGauge.begin(20000, 10);
fuelGauge.readBatteryData();
fuelGauge.run();
// this makes the EIC run on GCLK1 (ext 32k clock)
// therefore being able to catch rising edges
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_EIC) |
GCLK_CLKCTRL_GEN_GCLK1 |
GCLK_CLKCTRL_CLKEN;
// primary connection phase
// if stage x fails go to previous unless we are in the first
// if overall failures above threshold then reboot
uint8_t cStage = 0;
if(rtc.isTimeSet())
{
rf95.setModeRx();
enableLoraIrq();
Serial.println("Time is valid, hence lora Rx started");
}
while(true)
{
if(gwReconnCounter >= GATEWAY_MAX_RECONN_RETRIES)
{
Serial.println(F("Maximum reconnection retries reached."));
Serial.println(F("Rebooting"));
memset(lMsg, 0, 255);
sprintf((char*)lMsg, "Too many reconnection attempts, rebooting");
logPush(lMsg, strlen((char*)lMsg));
delay(50);
system_reboot();
}
if(cStage > 1)
{
break;
}
switch(cStage)
{
case 0:
if(gsmColdConnectSequence(GSM_RECONN_RETRIES, GSM_STAGE_RECONN_RETRIES))
{
cStage++;
if(!syncRTCToNTP())
{
memset(lMsg, 0, 255);
sprintf((char*)lMsg, "Couldn't sync to NTP");
logPush(lMsg, strlen((char*)lMsg));
cStage = 0;
gwReconnCounter++;
}
else
{
// Ready to receive lora msgs
rf95.setModeRx();
enableLoraIrq();
}
}
else
{
gwReconnCounter++;
digitalWrite(PIN_SIM800_PWR_EN, false);
plainSleepLoop(ON_GSM_CONN_FAIL_SLEEP);
digitalWrite(PIN_SIM800_PWR_EN, true);
delay(2000);
}
break;
case 1:
if(mqttColdConnectSequence())
{
cStage++;
}
else
{
cStage--;
gwReconnCounter++;
digitalWrite(PIN_SIM800_PWR_EN, false);
plainSleepLoop(ON_GSM_CONN_FAIL_SLEEP);
digitalWrite(PIN_SIM800_PWR_EN, true);
delay(2000);
}
break;
}
}
if(!rtc.isTimeSet())
{
Serial.println("Failed to initialize RTC with NTP time after many tries.");
Serial.println("Falling back to MQTT");
//if()
delay(100);
system_reboot();
}
unifiedUploadSequence();
for(uint8_t i = 0; i < 5; i++) //we loop 5 times to receive stuff
{
mqttLoop();
delay(250);
}
Serial.println(F("Initialization sequence complete"));
Serial.println(F("Going into sleepy mode now."));
}
void loop()
{
uint32_t millisOnWake;
uint16_t gatewaySleepTime = gwTxInterval;
while(true)
{
//rf95.sleep();
networkSleepSequence();
Serial.print("current unix time:");
Serial.println(getUnixTime());
Serial.println("");
delayMicroseconds(10000);
//interrupts();
//enableLoraIrq();
sleepLoop(gatewaySleepTime);
//radioInit();
// we reinitialize the radio in case something went wrong
Serial.println("wokeUp");
retryingFlag = false;
updatePower();
if(networkWakeSequence())
{
syncRTCToNTP();
if(unifiedUploadSequence())
{
gatewaySleepTime = gwTxInterval;
}
else
{
gatewaySleepTime = ON_PUB_RECONN_FAIL_SLEEP_FACT*gwTxInterval;
retryingFlag = true;
}
for(uint32_t i = 0; i<5; i++)
{
mqttLoop();
delay(500);
}
}
else
{
//processPackets();
if(gwReconnCounter >= GATEWAY_MAX_RECONN_RETRIES)
{
Serial.println(F("Maximum reconnection retries reached."));
Serial.println(F("Rebooting"));
delay(50);
system_reboot();
}
gwReconnCounter++;
Serial.print(F("Reconnection counter is: "));
Serial.println(gwReconnCounter);
Serial.print(F("Will sleep "));
Serial.print(gatewaySleepTime);
Serial.println(F(" Seconds and try again."));
gatewaySleepTime = ON_PUB_RECONN_FAIL_SLEEP_FACT*gwTxInterval;
retryingFlag = true;
}
}
}
void radioInit()
{
if(rf95.init())
{
Serial.println(F("LORA radio initialized successfully"));
}
else
{
Serial.println(F("Error initializing LORA radio"));
memset(lMsg, 0, 255);
sprintf((char*)lMsg, "CRITICAL: FAILED TO START LORA RADIO");
logPush(lMsg, strlen((char*)lMsg));
}
// set the callback
rf95.setRxGoodIrqF(&loraRxIrqNvm);
rf95.setTxDoneIrqF(&loraTxDnIrq);
rf95.setRxCrcErrIrqF(&loraCrcErrIrq);
Serial.println(RFM95_FREQUENCY);
rf95.setFrequency(RFM95_FREQUENCY);
rf95.setModemConfig(RFM95_MODULATION);
rf95.setThisAddress(GW_ID);
}
void updatePower()
{
uint8_t stat = (uint8_t)digitalRead(PIN_POWER_STAT_1) | ((uint8_t)digitalRead(PIN_POWER_STAT_2) << 1);
switch(stat)
{
case 0:
digitalWrite(PIN_POWER_BPASS_EN, false);
currentChStat = CH_STAT_NOT_CHARGING;
break;
case 1:
digitalWrite(PIN_POWER_BPASS_EN, true);
currentChStat = CH_STAT_FULL;
break;
case 2:
digitalWrite(PIN_POWER_BPASS_EN, false);
currentChStat = CH_STAT_CHARGING;
break;
case 3:
digitalWrite(PIN_POWER_BPASS_EN, false);
currentChStat = CH_STAT_NOT_CHARGING;
break;
default:
digitalWrite(PIN_POWER_BPASS_EN, false);
currentChStat = CH_STAT_NOT_CHARGING;
break;
}
}
void networkSleepSequence()
{
if(!mqttDisconnectSequence())
{
Serial.println(F("Will proceed with GSM modem shutdown"));
}
if(disableGsmRadio())
{
digitalWrite(PIN_SIM800_PWR_EN, false);
Serial.println(F("GSM radio disabled successfully"));
}
else
{
digitalWrite(PIN_SIM800_PWR_EN, false);
Serial.println(F("GSM radio hard power down"));
}
}
bool networkWakeSequence()
{
digitalWrite(PIN_SIM800_PWR_EN, true);
delay(2000);
uint8_t cStage = 0;
uint8_t localRetries = 0;
while(true)
{
if(localRetries > 2) // we only try thrice
return false;
//processPackets();
//disableLoraIrq();
switch(cStage)
{
case 0:
if(!gsmColdConnectSequence(GSM_RECONN_RETRIES, GSM_STAGE_RECONN_RETRIES))
{
Serial.println(F("Failed to reconnect to GSM."));
localRetries++;
}
else
cStage = 3;
break;
case 1:
if(!enableGsmRadio())
{
Serial.println(F("Couldn't enable GSM radio, restarting modem and reconnecting."));
sim800HardReset();
cStage--;
localRetries++;
}
else
cStage++;
break;
case 2:
if(!gsmWarmConnectSequence(GSM_RECONN_RETRIES, GSM_STAGE_RECONN_RETRIES))
{
sim800HardReset();
cStage = 0;
localRetries++;
}
else
cStage++;
break;
case 3:
if(!mqttWarmConnectSequence())
{
Serial.println(F("Failed to reconnect to MQTT."));
cStage--;
localRetries++;
}
else
return true;
break;
}
//enableLoraIrq();
delay(30);
}
}
bool msgPush(uint8_t* msg, uint16_t msgLen)
{
if(msgLen > 250)
return false;
uint8_t msgStr[256];
memset(msgStr, 0, 256);
memcpy(msgStr+1, msg, msgLen);
msgStr[0] = 'o';
return nvmSettings.msgPush(msgStr, strlen((char*)msgStr));
}
bool modemSigPush()
{
uint8_t str[256];
memset(str, 0, 256);
genStatPkg(str, true);
return statPush(str, strlen((char*)str));
}
bool modemNsStatPush()
{
uint8_t str[256];
memset(str, 0, 256);
genStatPkg(str, false);
return statPush(str, strlen((char*)str));
}
bool statPush(uint8_t* msg, uint16_t msgLen)
{
if(msgLen > 250)
return false;
uint8_t msgStr[256];
memset(msgStr, 0, 256);
memcpy(msgStr+1, msg, msgLen);
msgStr[0] = 's';
return nvmSettings.msgPush(msgStr, strlen((char*)msgStr));
}
bool logPush(uint8_t* logMsg, uint16_t msgLen)
{
if(msgLen > 240)
return false;
uint8_t str[256];
uint8_t logStr[256];
memset(logStr, 0, 256);
memset(str, 0, 256);
memcpy(str, logMsg, msgLen);
if(rtc.isTimeSet())
{
sprintf((char*)logStr, "l{\"d\":{\"dn\":%d,\"msg\":\"%s\",\"dt\":\"GW\",\"dv\":\"%s\",\"fv\":\"%s\",\"tsg\":%Ld}}",
GW_ID, (char*)str, GATEWAY_HW_VER, GATEWAY_FW_VER, getUnixTime());
}
else
{
sprintf((char*)logStr, "l{\"d\":{\"dn\":%d,\"msg\":\"%s\",\"dt\":\"GW\",\"dv\":\"%s\",\"fv\":\"%s\"}}",
GW_ID, (char*)str, GATEWAY_HW_VER, GATEWAY_FW_VER);
}
return nvmSettings.msgPush(logStr, strlen((char*)logStr));
}
bool unifiedUploadSequence()
{
uint8_t pulledMsg[257];
uint8_t tmpMsg[256];
uint16_t pullSz = 256;
uint32_t nPubbed = 0;
uint8_t errStat = 0;
uint32_t rem;
memset(pulledMsg, 0, 257);
disableLoraIrq();
while(nvmSettings.msgPeek(pulledMsg, &pullSz) && errStat == 0)
{
//nvmSettings.printStatuses();
wdt_clear();
switch(pulledMsg[0])
{
case 'l':
wdt_clear();
if(!mqttPublishLog(pulledMsg+1))
{
errStat = 1;
break;
}
if(!mqttLoop()) // if no loop we assiume failure
{
errStat = 2;
break;
}
digitalWrite(PIN_LED_BL, false);
Serial.print(F("Published: "));
Serial.println((char*)pulledMsg+1);
delayMicroseconds(1000);
digitalWrite(PIN_LED_BL, true);
break;
case 'o':
if(!mqttPublishObservation(pulledMsg+1))
{
errStat = 3;
break;
}
//enableLoraIrq();
if(!mqttLoop()) // if no loop we assiume failure
{
errStat = 4;
break;
}
//disableLoraIrq();
digitalWrite(PIN_LED_GR, false);
Serial.print(F("Published: "));
Serial.println((char*)pulledMsg+1);
delayMicroseconds(1000);
digitalWrite(PIN_LED_GR, true);
nPubbed++;
break;
case 's':
if(!mqttPublishStatus(pulledMsg+1))
{
errStat = 5;
break;
}
//enableLoraIrq();
if(!mqttLoop()) // if no loop we assiume failure
{
errStat = 6;
break;
}
//disableLoraIrq();
digitalWrite(PIN_LED_GR, false);
Serial.print(F("Published: "));
Serial.println((char*)pulledMsg+1);
delayMicroseconds(1000);
digitalWrite(PIN_LED_GR, true);
break;
default:
// there was something invalid in the memory
logPush((uint8_t*)"something funny in memory", 25);
logPush(pulledMsg, min(230, pullSz));
Serial.println((char*)pulledMsg);
nvmSettings.msgDrop();
break;
}
wdt_clear();
switch(errStat)
{
case 1:
digitalWrite(PIN_LED_RD, false);
Serial.print(F("Failed to publish: "));
Serial.println((char*)pulledMsg);
Serial.println(F("some logs remain."));
digitalWrite(PIN_LED_RD, true);
enableLoraIrq();
return false;
break;
case 2:
digitalWrite(PIN_LED_RD, false);
Serial.print(F("noLoop, assume fail"));
Serial.println((char*)pulledMsg);
Serial.println(F("some logs remain."));
enableLoraIrq();
digitalWrite(PIN_LED_RD, true);
return false;
break;
case 3:
digitalWrite(PIN_LED_RD, false);
rem = nvmSettings.getObsCount();
Serial.print(F("Failed to publish: "));
Serial.println((char*)pulledMsg);
memset(tmpMsg, 0, 256);
sprintf((char*)tmpMsg, "pubFail: %d rem", rem);
logPush(tmpMsg, strlen((char*)tmpMsg));
Serial.println((char*)tmpMsg);
digitalWrite(PIN_LED_RD, true);
enableLoraIrq();
Serial.print(rem);
Serial.println(F(" observations remaining."));
digitalWrite(PIN_LED_RD, true);
return false;
break;
case 4:
digitalWrite(PIN_LED_RD, false);
rem = nvmSettings.getObsCount();
memset(tmpMsg, 0, 256);
sprintf((char*)tmpMsg, "bLoop, %d pubbed, %d rem", (char*)pulledMsg, nPubbed, rem);
logPush(tmpMsg, strlen((char*)tmpMsg));
enableLoraIrq();
Serial.print(rem);
Serial.println(F(" observations remaining."));
digitalWrite(PIN_LED_RD, true);
return false;
break;
case 5:
digitalWrite(PIN_LED_RD, false);
rem = nvmSettings.getStatCount();
Serial.print(F("Failed to publish: "));
Serial.println((char*)pulledMsg);
memset(tmpMsg, 0, 256);
sprintf((char*)tmpMsg, "pubFail: %d stats rem", rem);
logPush(tmpMsg, strlen((char*)tmpMsg));
Serial.println((char*)tmpMsg);
digitalWrite(PIN_LED_RD, true);
enableLoraIrq();
Serial.print(rem);
Serial.println(F(" observations remaining."));
digitalWrite(PIN_LED_RD, true);
return false;
break;
case 6:
digitalWrite(PIN_LED_RD, false);
rem = nvmSettings.getStatCount();
memset(tmpMsg, 0, 256);
sprintf((char*)tmpMsg, "bLoop, %d rem", (char*)pulledMsg, nPubbed, rem);
logPush(tmpMsg, strlen((char*)tmpMsg));
enableLoraIrq();
Serial.print(rem);
Serial.println(F(" observations remaining."));
digitalWrite(PIN_LED_RD, true);
return false;
break;
}
nvmSettings.msgDrop();
enableLoraIrq();
delayMicroseconds(200); // give it some time to process incoming msgs
disableLoraIrq();
memset(pulledMsg, 0, 257);
pullSz = 256;
}
rem = nvmSettings.getObsCount();
wdt_clear();
memset(tmpMsg, 0, 256);
sprintf((char*)tmpMsg, "pOk, lst-msg %s, %Ld pub %Ld rem", (char*)pulledMsg, nPubbed, rem);
logPush(tmpMsg, strlen((char*)tmpMsg));
enableLoraIrq();
Serial.println((char*)tmpMsg);
nvmSettings.eraseDepleteds();
return true;
}
void sleepLoop(uint32_t sleepSeconds)
{
uint32_t nextStatLog = getUnixTime()+gwStatMsgItv;
nextWakeUp = getUnixTime()+sleepSeconds;
setWakeupEpoch(getUnixTime()+sleepSeconds);
/*for(uint32_t i = 0; i<sleepSeconds; i++)
{
delay(1000);
processPackets();
}*/
wdt_enter_sleep_mode((uint32_t)gwTxInterval);
USBDevice.detach();
while(getUnixTime() < (nextWakeUp-1))
{
if(getUnixTime() > nextStatLog)
{
nextStatLog = getUnixTime()+gwStatMsgItv;
modemNsStatPush();
}
sleepUc();
}
USBDevice.attach();
wdt_exit_sleep_mode();
}
void plainSleepLoop(uint32_t sleepSeconds)
{
uint32_t nextStatLog = getUnixTime()+gwStatMsgItv;
nextWakeUp = getUnixTime()+sleepSeconds;
setWakeupEpoch(getUnixTime()+sleepSeconds);
wdt_enter_sleep_mode((uint32_t)gwTxInterval);
USBDevice.detach();
while(getUnixTime() < nextWakeUp)
{
if(getUnixTime() > nextStatLog)
{
nextStatLog = getUnixTime()+gwStatMsgItv;
modemNsStatPush();
}
sleepUc();
}
USBDevice.attach();
wdt_exit_sleep_mode();
}
void sleepUc()
{
//Disable USB (optional)
//USBDevice.detach();
//Standby - lowest power sleep mode
// idle mode now for test
NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val;
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
SCB->SCR = SCB_SCR_SLEEPDEEP_Msk;
//PM->SLEEP.reg = PM_SLEEP_IDLE_AHB_Val;
noInterrupts();
__DMB();
delayMicroseconds(500);
__DSB();
__WFI(); // Wait For Interrupt call
delayMicroseconds(3000);
__DMB();
//delayMicroseconds(500);
interrupts();
//delayMicroseconds(100);
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
//Sleep until woken by interrupt...
//Enable USB
//USBDevice.attach();
}
void updateBypassSw()
{
if(digitalRead(PIN_POWER_STAT_1) && !digitalRead(PIN_POWER_STAT_2))
{
digitalWrite(PIN_POWER_BPASS_EN, true);
}
else
{
digitalWrite(PIN_POWER_BPASS_EN, false);
}
}
void enableLoraIrq(void)
{
NVIC_EnableIRQ(EIC_IRQn);
}
void disableLoraIrq(void)
{
NVIC_DisableIRQ(EIC_IRQn);
}
void loraCrcErrIrq()
{
digitalWrite(PIN_LED_RD, false);
logPush((uint8_t*)"Crc Rx Error", 12);
digitalWrite(PIN_LED_RD, true);
return;
}
void loraRxIrqNvm()
{
disableLoraIrq();
digitalWrite(PIN_LED_YL, false);
static uint8_t len;
static uint32_t ts;
static int16_t lastRssi;
static uint16_t nodeAddr;
static uint8_t incomingLoraBuffer[RH_RF95_MAX_MESSAGE_LEN];
delayMicroseconds(300);
ts = getUnixTime();
delayMicroseconds(300);
//delayMicroseconds(2000);
memset(incomingLoraBuffer, '\0', RH_RF95_MAX_MESSAGE_LEN);
len = RH_RF95_MAX_MESSAGE_LEN;
rf95.recv(incomingLoraBuffer, &len);
if( incomingLoraBuffer[len-1] != '}' ||
incomingLoraBuffer[len-2] != '}')
{
rf95.setModeRx();
enableLoraIrq();
return;
}
nodeAddr = rf95.headerFrom();
rf95.setHeaderTo(nodeAddr); //Ensure that the reply being sent will only go back to the sender
// response part for the node
memset((void*)outgoingLoraBuffer, 0, OUTGOING_LORA_BUF_SZ);
sprintf((char*)outgoingLoraBuffer, "sn:%d", sleepTime);
lastRssi = rf95.lastRssi(); //saves RSSI of last recieved message
//loraQueue.push((void*)incomingLoraBuffer);
//tsQueue.push((void*)&ts);
//rssiQueue.push((void*)&lastRssi);
memset(fullPktBuffer, 0, MQTT_MAX_PACKET_SIZE);
genFullPkg(incomingLoraBuffer, fullPktBuffer, ts, lastRssi);
msgPush(fullPktBuffer, strlen((char*)fullPktBuffer));
#ifdef AOUT
char* startPtr;
if(rf95.headerFrom() == 130) // replace by in
{
for(uint16_t i = 0; i<240; i++)
{
if(loraString[i] == 'b')
{
if((loraString[i+1] == 'x') && (loraString[i+2] == 't'))
{
startPtr = (char*)&loraString[i+5];
for(uint8_t j = 0; j<7; j++)
{
if(loraString[i+4+j] == ',')
{
loraString[i+4+j] = '\0';
break;
}
else
if(j == 6)
break;
}
tempOut = atof(startPtr);
break;
}
}
}
uint16_t dacData = map((uint32_t)(tempOut*100), -2500, 10000, 0, 4095);
dac.write(dacData);
}
#endif
rf95.send(outgoingLoraBuffer, strlen((char*)outgoingLoraBuffer));
//rf95.setModeRx();
digitalWrite(PIN_LED_YL, true);
enableLoraIrq();
}
void loraTxDnIrq()
{
rf95.setModeRx();
}
void mqttCallbackFunction(char* topic, byte* payload, unsigned int payLoadLength)
{
Serial.println("callBack invoked");
Serial.println(topic);
if(strcmp(topic, GATEWAY_TOPIC) == 0)
{
const uint8_t respSz = 86;
char gwResponse[respSz];
memset(gwResponse, 0, respSz);
wdt_clear();
// mnemonics
// nslt:val node sleep time
// gwti:val gateway transmission interval
// gwfv:? query gateway firmware version
// gwrr gateway restart (with 10 sec delay)
if( (memcmp(payload, mqttGwAckMsg, 10) == 0) |
(memcmp(payload, GATEWAY_FW_VER, strlen(GATEWAY_FW_VER)) == 0)
) // its my own response, do nothing
{}
else
{
Serial.print(F("Someone published on topic: "));
Serial.println(topic);
if(memcmp(payload, "nslt:", 5) == 0) // node sleep time
{
// This is done because of atoi, in case whatever arrives doesn't have a null character
// it is kind of retarded, we should settings transmission in a better way
payload[payLoadLength] = '\0';
uint32_t recvdSleepTime = atoi((char*) (payload+5));
if(sleepTime != recvdSleepTime)
{
if(recvdSleepTime <= MAX_SLEEP_TIME && recvdSleepTime >= MIN_SLEEP_TIME)
{
if(!nvmSettings.setSleepTimeS(recvdSleepTime))
Serial.println("setSleepFailed");
sleepTime = nvmSettings.getSleepTimeS();
sprintf(gwResponse, "Changed node sleep time to %d s, nslt: %d", sleepTime, sleepTime);
Serial.println(gwResponse);
memset(gwResponse, 0, respSz);
sprintf(gwResponse, "%s nslt: %d s", mqttGwAckMsg, sleepTime);
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
}
}
else
{
sprintf(gwResponse, "Received node sleep time same as current: %d s, nslt: %d", sleepTime, sleepTime);
Serial.println(gwResponse);
memset(gwResponse, 0, respSz);
sprintf(gwResponse, "%s Sleep time unchanged, nslt: %d s", mqttGwAckMsg, sleepTime);
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
}
}
else
if(memcmp(payload, "gwti:", 5) == 0) // gateway transmission interval
{
// This is done because of atoi, in case whatever arrives doesn't have a null character
// it is kind of retarded, we should settings transmission in a better way
payload[payLoadLength] = '\0';
uint32_t rcvdGwTxInterval = atoi((char*) (payload+5));
if(gwTxInterval != rcvdGwTxInterval)
{
if(rcvdGwTxInterval <= MSG_MAX_TX_INTERVAL && rcvdGwTxInterval >= MSG_MIN_TX_INTERVAL)
{
if(!nvmSettings.setTxItvS(rcvdGwTxInterval))
Serial.println("setTxItvFailed");
gwTxInterval = nvmSettings.getTxItvS();
sprintf(gwResponse, "Changed gw tx interval to %d s, gwti: %d", gwTxInterval, gwTxInterval);
Serial.println(gwResponse);
memset(gwResponse, 0, respSz);
sprintf(gwResponse, "%s gwti: %d s", mqttGwAckMsg, gwTxInterval);
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
}
}
else
{
sprintf(gwResponse, "Received gateway tx interval same as current: %d s, gwti: %d", gwTxInterval, gwTxInterval);
Serial.println(gwResponse);
memset(gwResponse, 0, respSz);
sprintf(gwResponse, "%s Gateway tx interval unchanged, gwti: %d s", mqttGwAckMsg, gwTxInterval);
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
}
}
else
if(memcmp(payload, "gsmi:", 5) == 0) // gateway status message interval
{
// This is done because of atoi, in case whatever arrives doesn't have a null character
// it is kind of retarded, we should settings transmission in a better way
payload[payLoadLength] = '\0';
uint32_t rcvdGwStatMsgItv = atoi((char*) (payload+5));
if(gwStatMsgItv != rcvdGwStatMsgItv)
{
if(rcvdGwStatMsgItv <= STAT_MSG_MAX_TX_INTERVAL && rcvdGwStatMsgItv >= STAT_MSG_MIN_TX_INTERVAL)
{
if(!nvmSettings.setStatMsgItvS(rcvdGwStatMsgItv))
Serial.println("setTxItvFailed");
gwStatMsgItv = nvmSettings.getStatMsgItvS();
sprintf(gwResponse, "Changed gw stat tx interval to %d s, gsmi: %d", gwStatMsgItv, gwStatMsgItv);
Serial.println(gwResponse);
memset(gwResponse, 0, respSz);
sprintf(gwResponse, "%s gsmi: %d s", mqttGwAckMsg, gwStatMsgItv);
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
}
}
else
{
sprintf(gwResponse, "Received gateway stat tx interval same as current: %d s, gsmi: %d", gwStatMsgItv, gwStatMsgItv);
Serial.println(gwResponse);
memset(gwResponse, 0, respSz);
sprintf(gwResponse, "%s Gateway stat tx interval unchanged, gsmi: %d s", mqttGwAckMsg, gwStatMsgItv);
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
}
}
else
if(memcmp(payload, "gwfv:?", 6) == 0) // gateway firmware version
{
Serial.println(F("Version query received."));
mqttClient.publish(GATEWAY_TOPIC, GATEWAY_FW_VER);
}
else
if(memcmp(payload, "gwrr", 4) == 0) // gateway firmware version
{
Serial.println(F("Restart command received."));
Serial.println(F("Will restart in 10 seconds."));
strcpy(gwResponse, mqttGwAckMsg);
strcat(gwResponse, " Gateway ");
itoa(GW_ID, &gwResponse[strlen(gwResponse)], 10);
strcat(gwResponse, " will restart in 10 seconds.");
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
wdt_clear();
delay(10000);
system_reboot();
}
else
{
Serial.print(F("Invalid message received: "));
Serial.write(payload, payLoadLength);
Serial.println("");
delay(10);
memset(gwResponse, '\0', respSz);
strcpy(gwResponse, mqttGwAckMsg);
strcat(gwResponse, "invMsg: ");
if(payLoadLength > (respSz - 20)) // 20 is the header size (ack+invMsg)
memcpy(gwResponse+20, payload, respSz-20);
else
memcpy(gwResponse+20, payload, payLoadLength);
mqttClient.publish(GATEWAY_TOPIC, gwResponse);
}
}
}
}
void system_reboot()
{
USBDevice.detach();
void (*application_code_entry)(void);
__set_MSP(*(uint32_t *) 0x2000);
SCB->VTOR = ((uint32_t) 0x2000 & SCB_VTOR_TBLOFF_Msk);
application_code_entry = (void (*)(void))(unsigned *)(*(unsigned *)(0x2000 + 4));
application_code_entry();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment