new feature deep sleep (wokring alpha)

This commit is contained in:
Klaus K Wilting 2020-12-09 10:15:12 +01:00
parent 016d69b5bb
commit 36afe66df9
12 changed files with 262 additions and 179 deletions

View File

@ -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,

View File

@ -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_

View File

@ -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}

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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);
}

View File

@ -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);
// 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 timer
if (wakeup_sec)
esp_sleep_enable_timer_wakeup(wakeup_sec * 1000000);
#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));
// set wakeup gpio
if (wakeup_gpio != NOT_A_PIN) {
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();
}

View File

@ -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;
}

View File

@ -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;
}
@ -96,6 +97,9 @@ void spi_slave_task(void *param) {
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)) {
rcommand(rxbuf + HEADER_SIZE, spi_transaction.trans_len - HEADER_SIZE);
@ -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