Created
March 23, 2021 10:54
-
-
Save m3h0w/f609a588b51d1bf7bdd671cd889ba9f4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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