diff --git a/include/lorawan.h b/include/lorawan.h index 3e54175d..e2239739 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -20,9 +20,10 @@ extern TaskHandle_t lmicTask, lorasendTask; -void lora_stack_reset(); -esp_err_t lora_stack_init(bool do_join); +esp_err_t lmic_init(void); void lora_setupForNetwork(bool preJoin); +void SaveLMICToRTC(int deepsleep_sec); +void LoadLMICFromRTC(); void lmictask(void *pvParameters); void gen_lora_deveui(uint8_t *pdeveui); void RevBytes(unsigned char *b, size_t c); @@ -33,6 +34,7 @@ void os_getDevEui(u1_t *buf); void lora_send(void *pvParameters); void lora_enqueuedata(MessageBuffer_t *message); void lora_queuereset(void); +uint32_t lora_queuewaiting(void); uint8_t myBattLevelCb(void *pUserData); void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev); void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg, diff --git a/include/senddata.h b/include/senddata.h index dcebb809..008e439c 100644 --- a/include/senddata.h +++ b/include/senddata.h @@ -18,7 +18,8 @@ extern Ticker sendTimer; void SendPayload(uint8_t port, sendprio_t prio); void sendData(void); void checkSendQueues(void); -void flushQueues(); +void flushQueues(void); +bool allQueuesEmtpy(void); void setSendIRQ(void); #endif // _SENDDATA_H_ diff --git a/platformio_orig.ini b/platformio_orig.ini index def51a91..883960b2 100644 --- a/platformio_orig.ini +++ b/platformio_orig.ini @@ -7,7 +7,7 @@ ; ---> SELECT THE TARGET PLATFORM HERE! <--- [board] -halfile = generic.h +;halfile = generic.h ;halfile = ebox.h ;halfile = eboxtube.h ;halfile = ecopower.h @@ -19,7 +19,7 @@ halfile = generic.h ;halfile = ttgov21new.h ;halfile = ttgofox.h ;halfile = ttgobeam.h -;halfile = ttgobeam10.h +halfile = ttgobeam10.h ;halfile = ttgotdisplay.h ;halfile = fipy.h ;halfile = lopy.h @@ -47,7 +47,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 2.0.4 +release_version = 2.0.51 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 3 @@ -55,7 +55,7 @@ extra_scripts = pre:build.py otakeyfile = ota.conf lorakeyfile = loraconf.h lmicconfigfile = lmic_config.h -platform_espressif32 = espressif32@2.0.0 +platform_espressif32 = espressif32@2.1.0 monitor_speed = 115200 upload_speed = 115200 ; set by build.py and taken from hal file display_library = ; set by build.py and taken from hal file @@ -65,7 +65,7 @@ lib_deps_display = bitbank2/OneBitDisplay @ 1.9.0 bitbank2/BitBang_I2C @ ^2.1.3 ricmoo/QRCode @ ^0.0.1 - bodmer/TFT_eSPI @ ^2.2.20 + bodmer/TFT_eSPI @ ^2.3.51 lib_deps_ledmatrix = seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0 lib_deps_rgbled = @@ -76,8 +76,7 @@ lib_deps_sensors = adafruit/Adafruit Unified Sensor @ ^1.1.4 adafruit/Adafruit BME280 Library @ ^2.1.1 adafruit/Adafruit BMP085 Library @ ^1.1.0 - ;boschsensortec/BSEC Software Library @ 1.6.1480 - https://github.com/BoschSensortec/BSEC-Arduino-library.git + boschsensortec/BSEC Software Library @ 1.6.1480 https://github.com/ricki-z/SDS011.git lib_deps_basic = bblanchon/ArduinoJson @ <6 @@ -117,7 +116,7 @@ framework = arduino board = esp32dev board_build.partitions = min_spiffs.csv upload_speed = ${common.upload_speed} -;upload_port = COM8 +;upload_port = COM3 platform = ${common.platform_espressif32} lib_deps = ${common.lib_deps_all} build_flags = ${common.build_flags_all} @@ -136,4 +135,4 @@ upload_protocol = esptool upload_protocol = esptool build_type = debug platform = https://github.com/platformio/platform-espressif32.git#develop -platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git +platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git \ No newline at end of file diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index 82b1690c..e8316119 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -95,6 +95,13 @@ void irqHandler(void *pvParameters) { if (InterruptStatus & SENDCYCLE_IRQ) { sendData(); InterruptStatus &= ~SENDCYCLE_IRQ; + // goto sleep if we have a sleep cycle + if (cfg.sleepcycle) +#ifdef HAS_BUTTON + enter_deepsleep(cfg.sleepcycle * 2, HAS_BUTTON); +#else + enter_deepsleep(cfg.sleepcycle * 2); +#endif } } // for } // irqHandler() diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 97865bf6..f2c75a0f 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -6,6 +6,9 @@ // Local logging Tag static const char TAG[] = "lora"; +// Saves the LMIC structure during deep sleep +RTC_DATA_ATTR lmic_t RTC_LMIC; + #if CLOCK_ERROR_PROCENTAGE > 7 #warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects #endif @@ -16,11 +19,6 @@ static const char TAG[] = "lora"; #endif #endif -// variable keep its values after restart or wakeup from sleep -RTC_NOINIT_ATTR u4_t RTCnetid, RTCdevaddr; -RTC_NOINIT_ATTR u1_t RTCnwkKey[16], RTCartKey[16]; -RTC_NOINIT_ATTR int RTCseqnoUp, RTCseqnoDn; - QueueHandle_t LoraSendQueue; TaskHandle_t lmicTask = NULL, lorasendTask = NULL; @@ -83,8 +81,6 @@ void lora_setupForNetwork(bool preJoin) { getSfName(updr2rps(LMIC.datarate)), getBwName(updr2rps(LMIC.datarate)), getCrName(updr2rps(LMIC.datarate))); - // store LMIC keys and counters in RTC memory - LMIC_getSessionKeys(&RTCnetid, &RTCdevaddr, RTCnwkKey, RTCartKey); } } @@ -197,59 +193,47 @@ void lora_send(void *pvParameters) { } // fetch next or wait for payload to send from queue - if (xQueueReceive(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) { + // do not delete item from queue until it is transmitted + if (xQueuePeek(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) { ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!"); continue; } // attempt to transmit payload - else { - - switch (LMIC_setTxData2_strict(SendBuffer.MessagePort, SendBuffer.Message, - SendBuffer.MessageSize, - (cfg.countermode & 0x02))) { - - case LMIC_ERROR_SUCCESS: - // save current Fcnt to RTC RAM - RTCseqnoUp = LMIC.seqnoUp; - RTCseqnoDn = LMIC.seqnoDn; + switch (LMIC_setTxData2_strict(SendBuffer.MessagePort, SendBuffer.Message, + SendBuffer.MessageSize, + (cfg.countermode & 0x02))) { + case LMIC_ERROR_SUCCESS: #if (TIME_SYNC_LORASERVER) - // if last packet sent was a timesync request, store TX timestamp - if (SendBuffer.MessagePort == TIMEPORT) - // store LMIC time when we started transmit of timesync request - timesync_store(osticks2ms(os_getTime()), timesync_tx); + // if last packet sent was a timesync request, store TX timestamp + if (SendBuffer.MessagePort == TIMEPORT) + // store LMIC time when we started transmit of timesync request + timesync_store(osticks2ms(os_getTime()), timesync_tx); #endif + ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize); + // delete sent item from queue + xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0); + break; + case LMIC_ERROR_TX_BUSY: // LMIC already has a tx message pending + case LMIC_ERROR_TX_FAILED: // message was not sent + vTaskDelay(pdMS_TO_TICKS(500 + random(400))); // wait a while + break; + case LMIC_ERROR_TX_TOO_LARGE: // message size exceeds LMIC buffer size + case LMIC_ERROR_TX_NOT_FEASIBLE: // message too large for current + // datarate + ESP_LOGI(TAG, "Message too large to send, message not sent and deleted"); + // we need some kind of error handling here -> to be done + break; + default: // other LMIC return code + ESP_LOGE(TAG, "LMIC error, message not sent and deleted"); - ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize); - break; - case LMIC_ERROR_TX_BUSY: // LMIC already has a tx message pending - case LMIC_ERROR_TX_FAILED: // message was not sent - // ESP_LOGD(TAG, "LMIC busy, message re-enqueued"); // very noisy - vTaskDelay(pdMS_TO_TICKS(1000 + random(500))); // wait a while - lora_enqueuedata(&SendBuffer); // re-enqueue the undelivered message - break; - case LMIC_ERROR_TX_TOO_LARGE: // message size exceeds LMIC buffer size - case LMIC_ERROR_TX_NOT_FEASIBLE: // message too large for current - // datarate - ESP_LOGI(TAG, - "Message too large to send, message not sent and deleted"); - // we need some kind of error handling here -> to be done - break; - default: // other LMIC return code - ESP_LOGE(TAG, "LMIC error, message not sent and deleted"); - - } // switch - } + } // switch delay(2); // yield to CPU - } + } // while(1) } -void lora_stack_reset() { - LMIC_reset(); // reset LMIC MAC -} - -esp_err_t lora_stack_init(bool do_join) { +esp_err_t lmic_init(void) { _ASSERT(SEND_QUEUE_SIZE > 0); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (LoraSendQueue == 0) { @@ -259,7 +243,58 @@ esp_err_t lora_stack_init(bool do_join) { ESP_LOGI(TAG, "LORA send queue created, size %d Bytes", SEND_QUEUE_SIZE * sizeof(MessageBuffer_t)); - // start lorawan stack + // setup LMIC stack + os_init_ex(&myPinmap); // initialize lmic run-time environment + + // register a callback for downlink messages and lmic events. + // We aren't trying to write reentrant code, so pUserData is NULL. + // LMIC_reset() doesn't affect callbacks, so we can do this first. + LMIC_registerRxMessageCb(myRxCallback, NULL); + LMIC_registerEventCb(myEventCallback, NULL); + // to come with future LMIC version + // LMIC_registerBattLevelCb(myBattLevelCb, NULL); + + // Reset the MAC state. Session and pending data transfers will be + // discarded. + LMIC_reset(); + +// This tells LMIC to make the receive windows bigger, in case your clock is +// faster or slower. This causes the transceiver to be earlier switched on, +// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE +// in src/lmic_config.h if you are limited on battery. +#ifdef CLOCK_ERROR_PROCENTAGE + LMIC_setClockError(CLOCK_ERROR_PROCENTAGE * MAX_CLOCK_ERROR / 1000); +#endif + +// Pass ABP parameters to LMIC_setSession +#ifdef LORA_ABP + setABPParameters(); // These parameters are defined as macro in loraconf.h + + // load saved session from RTC, if we have one + if (RTC_runmode == RUNMODE_WAKEUP) { + LoadLMICFromRTC(); + } else { + uint8_t appskey[sizeof(APPSKEY)]; + uint8_t nwkskey[sizeof(NWKSKEY)]; + memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession(NETID, DEVADDR, nwkskey, appskey); + } + + // Pass OTA parameters to LMIC_setSession +#else + // load saved session from RTC, if we have one + if (RTC_runmode == RUNMODE_WAKEUP) { + LoadLMICFromRTC(); + } + // otherwise start join procedure if not already joined + else { + if (!LMIC_startJoining()) + ESP_LOGI(TAG, "Already joined"); + } +#endif + + // start lmic loop task ESP_LOGI(TAG, "Starting LMIC..."); xTaskCreatePinnedToCore(lmictask, // task function "lmictask", // name of task @@ -269,31 +304,7 @@ esp_err_t lora_stack_init(bool do_join) { &lmicTask, // task handle 1); // CPU core -#ifdef LORA_ABP - // Pass ABP parameters to LMIC_setSession - lora_stack_reset(); - uint8_t appskey[sizeof(APPSKEY)]; - uint8_t nwkskey[sizeof(NWKSKEY)]; - memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); - memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); - LMIC_setSession(NETID, DEVADDR, nwkskey, appskey); - // These parameters are defined as macro in loraconf.h - setABPParameters(); -#else - // Start join procedure if not already joined, - // lora_setupForNetwork(true) is called by eventhandler when joined - // else continue current session - if (do_join) { - if (!LMIC_startJoining()) - ESP_LOGI(TAG, "Already joined"); - } else { - lora_stack_reset(); - LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey); - LMIC.seqnoUp = RTCseqnoUp; - LMIC.seqnoDn = RTCseqnoDn; - } -#endif - // start lmic send task + // start lora send task xTaskCreatePinnedToCore(lora_send, // task function "lorasendtask", // name of task 3072, // stack size of task @@ -319,11 +330,11 @@ void lora_enqueuedata(MessageBuffer_t *message) { ESP_LOGW(TAG, "LORA sendqueue purged, data is lost"); } case prio_normal: - ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0); + ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); break; case prio_low: default: - ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); + ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0); break; } if (ret != pdTRUE) { @@ -338,38 +349,18 @@ void lora_enqueuedata(MessageBuffer_t *message) { void lora_queuereset(void) { xQueueReset(LoraSendQueue); } -// LMIC lorawan stack task +uint32_t lora_queuewaiting(void) { + return uxQueueMessagesWaiting(LoraSendQueue); +} + +// LMIC loop task void lmictask(void *pvParameters) { _ASSERT((uint32_t)pvParameters == 1); - - // setup LMIC stack - os_init_ex(&myPinmap); // initialize lmic run-time environment - - // register a callback for downlink messages and lmic events. - // We aren't trying to write reentrant code, so pUserData is NULL. - // LMIC_reset() doesn't affect callbacks, so we can do this first. - LMIC_registerRxMessageCb(myRxCallback, NULL); - LMIC_registerEventCb(myEventCallback, NULL); - // to come with future LMIC version - // LMIC_registerBattLevelCb(myBattLevelCb, NULL); - - // Reset the MAC state. Session and pending data transfers will be - // discarded. - lora_stack_reset(); - -// This tells LMIC to make the receive windows bigger, in case your clock is -// faster or slower. This causes the transceiver to be earlier switched on, -// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE -// in src/lmic_config.h if you are limited on battery. -#ifdef CLOCK_ERROR_PROCENTAGE - LMIC_setClockError(CLOCK_ERROR_PROCENTAGE * MAX_CLOCK_ERROR / 1000); -#endif - while (1) { os_runloop_once(); // execute lmic scheduled jobs and events delay(2); // yield to CPU } -} // lmictask +} // lmic event handler void myEventCallback(void *pUserData, ev_t ev) { @@ -410,7 +401,7 @@ void myEventCallback(void *pUserData, ev_t ev) { case EV_JOIN_FAILED: // must call LMIC_reset() to stop joining // otherwise join procedure continues. - lora_stack_reset(); + LMIC_reset(); break; case EV_JOIN_TXCOMPLETE: @@ -560,4 +551,44 @@ void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, bool is_down) { } // mac_decode() #endif // VERBOSE +// following code snippet was taken from +// https://github.com/JackGruber/ESP32-LMIC-DeepSleep-example/blob/master/src/main.cpp + +void SaveLMICToRTC(int deepsleep_sec) { + RTC_LMIC = LMIC; + + // ESP32 can't track millis during DeepSleep and no option to advance + // millis after DeepSleep. Therefore reset DutyCyles + + unsigned long now = millis(); + + // EU Like Bands +#if defined(CFG_LMIC_EU_like) + for (int i = 0; i < MAX_BANDS; i++) { + ostime_t correctedAvail = + RTC_LMIC.bands[i].avail - + ((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC); + if (correctedAvail < 0) { + correctedAvail = 0; + } + RTC_LMIC.bands[i].avail = correctedAvail; + } + + RTC_LMIC.globalDutyAvail = RTC_LMIC.globalDutyAvail - + ((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC); + if (RTC_LMIC.globalDutyAvail < 0) { + RTC_LMIC.globalDutyAvail = 0; + } +#else + ESP_LOGW(TAG, "No DutyCycle recalculation function!"); +#endif + + ESP_LOGI(TAG, "LMIC state saved"); +} + +void LoadLMICFromRTC() { + LMIC = RTC_LMIC; + ESP_LOGI(TAG, "LMIC state loaded"); +} + #endif // HAS_LORA \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 687a926d..ea6206ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -348,9 +348,7 @@ void setup() { // initialize LoRa #if (HAS_LORA) strcat_P(features, " LORA"); - // kick off join, except we come from sleep - _ASSERT(lora_stack_init(RTC_runmode == RUNMODE_WAKEUP ? false : true) == - ESP_OK); + _ASSERT(lmic_init() == ESP_OK); #endif // initialize SPI diff --git a/src/mqttclient.cpp b/src/mqttclient.cpp index 511f02b5..eb647c0c 100644 --- a/src/mqttclient.cpp +++ b/src/mqttclient.cpp @@ -115,7 +115,8 @@ void mqtt_client_task(void *param) { while (1) { // fetch next or wait for payload to send from queue - if (xQueueReceive(MQTTSendQueue, &msg, portMAX_DELAY) != pdTRUE) { + // do not delete item from queue until it is transmitted + if (xQueuePeek(MQTTSendQueue, &msg, portMAX_DELAY) != pdTRUE) { ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!"); continue; } @@ -129,19 +130,17 @@ void mqtt_client_task(void *param) { if (mqttClient.publish(MQTT_OUTTOPIC, buffer)) { ESP_LOGI(TAG, "%d byte(s) sent to MQTT server", msg.MessageSize + 2); + // delete sent item from queue + xQueueReceive(MQTTSendQueue, &msg, (TickType_t)0); continue; - } else { - mqtt_enqueuedata(&msg); // postpone the undelivered message - ESP_LOGD(TAG, - "Couldn't sent message to MQTT server, message postponed"); - } + } else + ESP_LOGD(TAG, "Couldn't sent message to MQTT server"); } else { // attempt to reconnect to MQTT server ESP_LOGD(TAG, "MQTT client reconnecting..."); ESP_LOGD(TAG, "MQTT last_error = %d / rc = %d", mqttClient.lastError(), mqttClient.returnCode()); - mqtt_enqueuedata(&msg); // postpone the undelivered message delay(MQTT_RETRYSEC * 1000); mqtt_connect(MQTT_SERVER, MQTT_PORT); } @@ -161,11 +160,11 @@ void mqtt_enqueuedata(MessageBuffer_t *message) { if (!uxQueueSpacesAvailable(MQTTSendQueue)) xQueueReceive(MQTTSendQueue, &DummyBuffer, (TickType_t)0); case prio_normal: - ret = xQueueSendToFront(MQTTSendQueue, (void *)message, (TickType_t)0); + ret = xQueueSendToBack(MQTTSendQueue, (void *)message, (TickType_t)0); break; case prio_low: default: - ret = xQueueSendToBack(MQTTSendQueue, (void *)message, (TickType_t)0); + ret = xQueueSendToFront(MQTTSendQueue, (void *)message, (TickType_t)0); break; } if (ret != pdTRUE) @@ -185,6 +184,11 @@ void mqtt_loop(void) { } void mqtt_queuereset(void) { xQueueReset(MQTTSendQueue); } + +uint32_t mqtt_queuewaiting(void) { + return uxQueueMessagesWaitingMQTTSendQueue); +} + void setMqttIRQ(void) { xTaskNotify(irqHandlerTask, MQTT_IRQ, eSetBits); } #endif // HAS_MQTT \ No newline at end of file diff --git a/src/paxcounter_orig.conf b/src/paxcounter_orig.conf index 0a7b88d1..c0dc5816 100644 --- a/src/paxcounter_orig.conf +++ b/src/paxcounter_orig.conf @@ -11,12 +11,13 @@ // Payload send cycle and encoding #define SENDCYCLE 30 // payload send cycle [seconds/2], 0 .. 255 +#define SLEEPCYCLE 0 // sleep time after a send cycle [seconds/2], 0 .. 255; 0 means no sleep [default = 0] #define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed #define COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed // MAC sniffing parameters #define VENDORFILTER 0 // set to 0 if you want to scan all devices, not filtering smartphone OUIs -#define BLECOUNTER 0 // set to 0 if you do not want to install the BLE sniffer +#define BLECOUNTER 1 // set to 0 if you do not want to install the BLE sniffer #define WIFICOUNTER 1 // set to 0 if you do not want to install the WIFI sniffer #define MAC_QUEUE_SIZE 50 // size of MAC processing buffer (number of MACs) [default = 50] @@ -26,11 +27,11 @@ #define BLESCANINTERVAL 80 // [illiseconds] scan interval, see below, 3 .. 10240, default 80ms = 100% duty cycle // Corona Exposure Notification Service(ENS) counter -#define COUNT_ENS 0 // count found number of devices which advertise Exposure Notification Service - // set to 0 if you do not want to enable this function +#define COUNT_ENS 1 // count found number of devices which advertise Exposure Notification Service + // set to 1 if you want to enable this function [default=0] // for additional sensors (added by some user) -#define HAS_SENSOR_1 0 // set to 1 to enable data transfer of user sensor #1 (also used as ENS counter) [default=0] +#define HAS_SENSOR_1 1 // set to 1 to enable data transfer of user sensor #1 (also used as ENS counter) [default=0] #define HAS_SENSOR_2 0 // set to 1 to enable data transfer of user sensor #2 [default=0] #define HAS_SENSOR_3 0 // set to 1 to enable data transfer of user sensor #3 [default=0] @@ -83,10 +84,10 @@ #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] // settings for syncing time of node with a time source (network / gps / rtc / timeserver) -#define TIME_SYNC_LORAWAN 1 // set to 1 to use LORA network as time source, 0 means off [default = 1] +#define TIME_SYNC_LORAWAN 0 // set to 1 to use LORA network as time source, 0 means off [default = 1] #define TIME_SYNC_LORASERVER 0 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0] #define TIME_SYNC_INTERVAL 60 // sync time attempt each .. minutes from time source [default = 60], 0 means off -#define TIME_SYNC_INTERVAL_RETRY 10 // retry time sync after lost sync each .. minutes [default = 10], 0 means off +#define TIME_SYNC_INTERVAL_RETRY 10 // retry time sync after lost sync each .. minutes [default = 10], 0 means off #define TIME_SYNC_SAMPLES 1 // number of time requests for averaging, max. 255 #define TIME_SYNC_CYCLE 60 // delay between two time samples [seconds] #define TIME_SYNC_TIMEOUT 400 // timeout waiting for timeserver answer [seconds] diff --git a/src/power.cpp b/src/power.cpp index 3355bfa3..9b16995e 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -48,7 +48,7 @@ void AXP192_powerevent_IRQ(void) { ESP_LOGI(TAG, "Battery low temperature."); // short press -> esp32 deep sleep mode, can be exited by pressing user button - if (pmu.isPEKShortPressIRQ() && (RTC_runmode == RUNMODE_NORMAL)) { + if (pmu.isPEKShortPressIRQ()) { enter_deepsleep(0, HAS_BUTTON); } diff --git a/src/reset.cpp b/src/reset.cpp index 92a7af68..fe13a66e 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -5,21 +5,27 @@ // Local logging tag static const char TAG[] = __FILE__; +// Conversion factor for micro seconds to seconds +#define uS_TO_S_FACTOR 1000000ULL + // variable keep its values after restart or wakeup from sleep RTC_NOINIT_ATTR runmode_t RTC_runmode; +const char *runmode[4] = {"powercycle", "normal", "wakeup", "update"}; + void do_reset(bool warmstart) { if (warmstart) { - // store LMIC keys and counters in RTC memory - ESP_LOGI(TAG, "restarting device (warmstart), keeping runmode %d", - RTC_runmode); + ESP_LOGI(TAG, "restarting device (warmstart), keeping runmode %s", + runmode[RTC_runmode]); } else { #if (HAS_LORA) - if (RTC_runmode == RUNMODE_NORMAL) + if (RTC_runmode == RUNMODE_NORMAL) { LMIC_shutdown(); + } #endif RTC_runmode = RUNMODE_POWERCYCLE; - ESP_LOGI(TAG, "restarting device (coldstart), set runmode %d", RTC_runmode); + ESP_LOGI(TAG, "restarting device (coldstart), setting runmode %s", + runmode[RTC_runmode]); } esp_restart(); } @@ -40,9 +46,6 @@ void do_after_reset(int reason) { case DEEPSLEEP_RESET: // 0x05 Deep Sleep reset digital core RTC_runmode = RUNMODE_WAKEUP; -#if (HAS_LORA) - // to be done: restore LoRaWAN channel configuration and datarate here -#endif break; case SW_RESET: // 0x03 Software reset digital core @@ -61,32 +64,61 @@ void do_after_reset(int reason) { break; } - ESP_LOGI(TAG, "Starting Software v%s, runmode %d", PROGVERSION, RTC_runmode); + ESP_LOGI(TAG, "Starting Software v%s, runmode %s", PROGVERSION, + runmode[RTC_runmode]); } -void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio) { +void enter_deepsleep(const int wakeup_sec = 60, + const gpio_num_t wakeup_gpio = GPIO_NUM_MAX) { - if ((!wakeup_sec) && (!wakeup_gpio) && (RTC_runmode == RUNMODE_NORMAL)) - return; - -// wait until LMIC is in safe state before going to sleep + // ensure we are in normal runmode, not udpate or wakeup + if ((RTC_runmode != RUNMODE_NORMAL) #if (HAS_LORA) - while (os_queryTimeCriticalJobs(ms2osticks(wakeup_sec * 1000))) - vTaskDelay(pdMS_TO_TICKS(100)); - - // to be done: save current LoRaWAN configuration here + || (LMIC.opmode & (OP_JOINING | OP_REJOIN)) +#endif + ) { + ESP_LOGE(TAG, "Can't go to sleep now"); + return; + } else { + ESP_LOGI(TAG, "Attempting to sleep..."); + } + // switch off radio +#if (BLECOUNTER) + stop_BLEscan(); + btStop(); +#endif +#if (WIFICOUNTER) + switch_wifi_sniffer(0); #endif - // set up power domains - //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); - - // set wakeup timer - if (wakeup_sec) - esp_sleep_enable_timer_wakeup(wakeup_sec * 1000000); + // wait until all send queues are empty + ESP_LOGI(TAG, "Waiting until send queues are empty..."); + while (!allQueuesEmtpy()) + vTaskDelay(pdMS_TO_TICKS(100)); - // set wakeup gpio - if (wakeup_gpio != NOT_A_PIN) { +#if (HAS_LORA) + // shutdown LMIC safely + ESP_LOGI(TAG, "Waiting until LMIC is idle..."); + while ((LMIC.opmode & OP_TXRXPEND) || + os_queryTimeCriticalJobs(sec2osticks(wakeup_sec))) + vTaskDelay(pdMS_TO_TICKS(100)); + + SaveLMICToRTC(wakeup_sec); +// vTaskDelete(lmicTask); +// LMIC_shutdown(); +#endif // (HAS_LORA) + + // set up RTC power domains + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); + + // set up RTC wakeup timer, if we have + if (wakeup_sec > 0) { + esp_sleep_enable_timer_wakeup(wakeup_sec * uS_TO_S_FACTOR); + } + + // set wakeup gpio, if we have + if (wakeup_gpio != GPIO_NUM_MAX) { rtc_gpio_isolate(wakeup_gpio); esp_sleep_enable_ext1_wakeup(1ULL << wakeup_gpio, ESP_EXT1_WAKEUP_ALL_LOW); } @@ -99,15 +131,6 @@ void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio) { dp_shutdown(); #endif -// switch off radio -#if (BLECOUNTER) - stop_BLEscan(); - btStop(); -#endif -#if (WIFICOUNTER) - switch_wifi_sniffer(0); -#endif - // reduce power if has PMU #ifdef HAS_PMU AXP192_power(pmu_power_sleep); @@ -117,6 +140,6 @@ void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio) { i2c_deinit(); // enter sleep mode - ESP_LOGI(TAG, "Going to sleep..."); + ESP_LOGI(TAG, "Going to sleep, good bye."); esp_deep_sleep_start(); } \ No newline at end of file diff --git a/src/senddata.cpp b/src/senddata.cpp index 4ac5e67a..31a1096c 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -10,6 +10,8 @@ void setSendIRQ() { // put data to send in RTos Queues used for transmit over channels Lora and SPI void SendPayload(uint8_t port, sendprio_t prio) { + ESP_LOGD(TAG, "sending Payload for Port %d (prio %d)", port, prio); + MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[] @@ -187,14 +189,9 @@ void sendData() { bitmask &= ~mask; mask <<= 1; } // while (bitmask) - - // goto sleep if we have a sleep cycle - if ((cfg.sleepcycle) && (RTC_runmode == RUNMODE_NORMAL)) - enter_deepsleep(cfg.sleepcycle * 2, HAS_BUTTON); - } // sendData() -void flushQueues() { +void flushQueues(void) { #if (HAS_LORA) lora_queuereset(); #endif @@ -205,3 +202,17 @@ void flushQueues() { mqtt_queuereset(); #endif } + +bool allQueuesEmtpy(void) { + uint32_t rc = 0; +#if (HAS_LORA) + rc += lora_queuewaiting(); +#endif +#ifdef HAS_SPI + rc += spi_queuewaiting(); +#endif +#ifdef HAS_MQTT + rc += mqtt_queuewaiting(); +#endif + return (rc == 0) ? true : false; +} diff --git a/src/spislave.cpp b/src/spislave.cpp index 3e581318..1555b5ec 100644 --- a/src/spislave.cpp +++ b/src/spislave.cpp @@ -57,7 +57,8 @@ void spi_slave_task(void *param) { memset(rxbuf, 0, sizeof(rxbuf)); // fetch next or wait for payload to send from queue - if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) { + // do not delete item from queue until it is transmitted + if (xQueuePeek(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) { ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!"); continue; } @@ -95,6 +96,9 @@ void spi_slave_task(void *param) { ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG); ESP_LOGI(TAG, "Transaction finished with size %zu bits", spi_transaction.trans_len); + + // delete sent item from queue + xQueueReceive(SPISendQueue, &msg, (TickType_t)0); // check if command was received, then call interpreter with command payload if ((spi_transaction.trans_len) && ((rxbuf[2]) == RCMDPORT)) { @@ -159,11 +163,11 @@ void spi_enqueuedata(MessageBuffer_t *message) { if (!uxQueueSpacesAvailable(SPISendQueue)) xQueueReceive(SPISendQueue, &DummyBuffer, (TickType_t)0); case prio_normal: - ret = xQueueSendToFront(SPISendQueue, (void *)message, (TickType_t)0); + ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); break; case prio_low: default: - ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); + ret = xQueueSendToFront(SPISendQueue, (void *)message, (TickType_t)0); break; } if (ret != pdTRUE) @@ -172,4 +176,6 @@ void spi_enqueuedata(MessageBuffer_t *message) { void spi_queuereset(void) { xQueueReset(SPISendQueue); } +uint32_t spi_queuewaiting(void) { return uxQueueMessagesWaiting(SPISendQueue); } + #endif // HAS_SPI \ No newline at end of file