diff --git a/README.md b/README.md index a5149bf3..4504b72b 100644 --- a/README.md +++ b/README.md @@ -351,10 +351,11 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts. 0x09 reset functions (send this command with confirmed ack only to avoid boot loops!) - 0 = restart device + 0 = restart device (coldstart) 1 = reset MAC counter to zero 2 = reset device to factory settings 3 = flush send queues + 4 = restart device (warmstart) 9 = reboot device to OTA update via Wifi mode 0x0A set LoRaWAN payload send cycle diff --git a/include/display.h b/include/display.h index 0e94b806..8e503ce5 100644 --- a/include/display.h +++ b/include/display.h @@ -7,7 +7,8 @@ extern uint8_t DisplayIsOn; void refreshTheDisplay(bool nextPage = false); -void init_display(uint8_t verbose = 0); +void init_display(bool verbose = false); +void shutdown_display(void); void draw_page(time_t t, uint8_t page); void dp_printf(uint16_t x, uint16_t y, uint8_t font, uint8_t inv, const char *format, ...); diff --git a/include/globals.h b/include/globals.h index 76b733d4..aaecfceb 100644 --- a/include/globals.h +++ b/include/globals.h @@ -52,6 +52,13 @@ enum sendprio_t { prio_low, prio_normal, prio_high }; enum timesource_t { _gps, _rtc, _lora, _unsynced }; +enum runmode_t { + RUNMODE_POWERCYCLE = 0, + RUNMODE_NORMAL, + RUNMODE_WAKEUP, + RUNMODE_UPDATE +}; + // Struct holding devices's runtime configuration typedef struct { uint8_t loradr; // 0-15, lora datarate @@ -120,6 +127,7 @@ extern TaskHandle_t irqHandlerTask, ClockTask; extern TimerHandle_t WifiChanTimer; extern Timezone myTZ; extern time_t userUTCTime; +extern RTC_DATA_ATTR runmode_t RTC_runmode; // application includes #include "led.h" diff --git a/include/i2cscan.h b/include/i2c.h similarity index 80% rename from include/i2cscan.h rename to include/i2c.h index 38487e76..ed042b31 100644 --- a/include/i2cscan.h +++ b/include/i2c.h @@ -1,5 +1,5 @@ -#ifndef _I2CSCAN_H -#define _I2CSCAN_H +#ifndef _I2C_H +#define _I2C_H #include @@ -11,6 +11,8 @@ #define MCP_24AA02E64_PRIMARY_ADDRESS (0x50) #define QUECTEL_GPS_PRIMARY_ADDRESS (0x10) +void i2c_init(void); +void i2c_deinit(void); int i2c_scan(void); #endif \ No newline at end of file diff --git a/include/lorawan.h b/include/lorawan.h index 4341b397..e98da2bf 100644 --- a/include/lorawan.h +++ b/include/lorawan.h @@ -4,6 +4,7 @@ #include "globals.h" #include "rcommand.h" #include "timekeeper.h" +#include #if (TIME_SYNC_LORASERVER) #include "timesync.h" #endif @@ -21,6 +22,9 @@ #endif extern TaskHandle_t lmicTask, lorasendTask; +extern RTC_NOINIT_ATTR u4_t RTCnetid, RTCdevaddr; +extern RTC_NOINIT_ATTR u1_t RTCnwkKey[16], RTCartKey[16]; +extern RTC_NOINIT_ATTR int RTCseqnoUp, RTCseqnoDn; // table of LORAWAN MAC commands typedef struct { @@ -29,7 +33,7 @@ typedef struct { const uint8_t params; } mac_t; -esp_err_t lora_stack_init(); +esp_err_t lora_stack_init(bool do_join); void lora_setupForNetwork(bool preJoin); void lmictask(void *pvParameters); void gen_lora_deveui(uint8_t *pdeveui); @@ -42,10 +46,10 @@ void showLoraKeys(void); void lora_send(void *pvParameters); void lora_enqueuedata(MessageBuffer_t *message); void lora_queuereset(void); -void myEventCallback(void *pUserData, ev_t ev); -void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg, - size_t nMsg); -void myTxCallback(void *pUserData, int fSuccess); +static void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev); +static void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, + const uint8_t *pMsg, size_t nMsg); +static void IRAM_ATTR myTxCallback(void *pUserData, int fSuccess); void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[], const uint8_t tablesize); uint8_t getBattLevel(void); @@ -54,8 +58,8 @@ const char *getBwName(rps_t rps); const char *getCrName(rps_t rps); #if (TIME_SYNC_LORAWAN) -void user_request_network_time_callback(void *pVoidUserUTCTime, - int flagSuccess); +static void user_request_network_time_callback(void *pVoidUserUTCTime, + int flagSuccess); #endif #endif \ No newline at end of file diff --git a/include/main.h b/include/main.h index 4b5617cb..9037ccec 100644 --- a/include/main.h +++ b/include/main.h @@ -7,8 +7,8 @@ #include // needed for showing coex sw version #include "globals.h" -#include "power.h" -#include "i2cscan.h" +#include "reset.h" +#include "i2c.h" #include "blescan.h" #include "wifiscan.h" #include "configmanager.h" @@ -16,8 +16,8 @@ #include "beacon_array.h" #include "ota.h" #include "irqhandler.h" -#include "led.h" #include "spislave.h" + #if (HAS_LORA) #include "lorawan.h" #endif diff --git a/include/ota.h b/include/ota.h index cec28750..6a92720d 100644 --- a/include/ota.h +++ b/include/ota.h @@ -17,7 +17,7 @@ void start_ota_update(); int version_compare(const String v1, const String v2); void ota_display(const uint8_t row, const std::string status, const std::string msg); -void show_progress(unsigned long current, unsigned long size); +static void show_progress(unsigned long current, unsigned long size); #endif // USE_OTA diff --git a/include/power.h b/include/power.h index 1af87f09..40e1a010 100644 --- a/include/power.h +++ b/include/power.h @@ -4,7 +4,8 @@ #include #include #include -#include "i2cscan.h" +#include "i2c.h" +#include "reset.h" #define DEFAULT_VREF 1100 // tbd: use adc2_vref_to_gpio() for better estimate #define NO_OF_SAMPLES 64 // we do some multisampling to get better values @@ -14,13 +15,16 @@ void calibrate_voltage(void); bool batt_sufficient(void); #ifdef HAS_PMU + #include -void power_event_IRQ(void); -void AXP192_power(bool on); +enum pmu_power_t { pmu_power_on, pmu_power_off, pmu_power_sleep }; +void AXP192_powerevent_IRQ(void); +void AXP192_power(pmu_power_t powerlevel); void AXP192_init(void); void AXP192_showstatus(void); uint8_t i2c_writeBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len); uint8_t i2c_readBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len); + #endif // HAS_PMU #endif \ No newline at end of file diff --git a/include/rcommand.h b/include/rcommand.h index 78e1bae4..b3205256 100644 --- a/include/rcommand.h +++ b/include/rcommand.h @@ -24,6 +24,6 @@ typedef struct { } cmd_t; void rcommand(const uint8_t cmd[], const uint8_t cmdlength); -void do_reset(); +void do_reset(bool warmstart); #endif diff --git a/include/reset.h b/include/reset.h new file mode 100644 index 00000000..533a79c1 --- /dev/null +++ b/include/reset.h @@ -0,0 +1,12 @@ +#ifndef _RESET_H +#define _RESET_H + +#include +#include +#include "i2c.h" + +void do_reset(bool warmstart); +void do_after_reset(int reason); +void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio); + +#endif // _RESET_H \ No newline at end of file diff --git a/include/wifiscan.h b/include/wifiscan.h index 0a6e7b6c..35c42e06 100644 --- a/include/wifiscan.h +++ b/include/wifiscan.h @@ -8,7 +8,7 @@ #include "hash.h" void wifi_sniffer_init(void); -void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); +static void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); void switchWifiChannel(TimerHandle_t xTimer); #endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 1975fe69..97e33211 100644 --- a/platformio.ini +++ b/platformio.ini @@ -43,7 +43,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 = 1.9.6 +release_version = 1.9.71 ; 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 diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 0fce294f..0fd6ca39 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -33,7 +33,6 @@ void defaultConfig() { cfg.vendorfilter = VENDORFILTER; // 0=disabled, 1=enabled cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) cfg.monitormode = 0; // 0=disabled, 1=enabled - cfg.runmode = 0; // 0=normal, 1=update cfg.payloadmask = PAYLOADMASK; // all payload switched on cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = { 0}; // init BSEC state for BME680 sensor @@ -157,10 +156,6 @@ void saveConfig() { flash8 != cfg.monitormode) nvs_set_i8(my_handle, "monitormode", cfg.monitormode); - if (nvs_get_i8(my_handle, "runmode", &flash8) != ESP_OK || - flash8 != cfg.runmode) - nvs_set_i8(my_handle, "runmode", cfg.runmode); - if (nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK || flash16 != cfg.rssilimit) nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit); @@ -350,14 +345,6 @@ void loadConfig() { saveConfig(); } - if (nvs_get_i8(my_handle, "runmode", &flash8) == ESP_OK) { - cfg.runmode = flash8; - ESP_LOGI(TAG, "Run mode = %d", flash8); - } else { - ESP_LOGI(TAG, "Run mode set to default %d", cfg.runmode); - saveConfig(); - } - nvs_close(my_handle); ESP_LOGI(TAG, "Done"); } diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 9b75ecef..569ed772 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -20,8 +20,15 @@ void doHousekeeping() { uptime(); // check if update mode trigger switch was set - if (cfg.runmode == 1) - do_reset(); + if (RTC_runmode == RUNMODE_UPDATE) { + // check battery status if we can before doing ota + if (batt_sufficient()) + do_reset(true); // warmstart to runmode update + else { + ESP_LOGE(TAG, "Battery voltage %dmV too low for OTA", batt_voltage); + RTC_runmode == RUNMODE_NORMAL; // keep running in normal mode + } + } // task storage debugging // ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", @@ -87,7 +94,7 @@ void doHousekeeping() { get_salt(); // get new salt for salting hashes if (ESP.getMinFreeHeap() <= MEM_LOW) // check again - do_reset(); // memory leak, reset device + do_reset(true); // memory leak, reset device } // check free PSRAM memory @@ -98,7 +105,7 @@ void doHousekeeping() { get_salt(); // get new salt for salting hashes if (ESP.getMinFreePsram() <= MEM_LOW) // check again - do_reset(); // memory leak, reset device + do_reset(true); // memory leak, reset device } #endif diff --git a/src/display.cpp b/src/display.cpp index 1e888f21..7f4da8f5 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -60,7 +60,7 @@ uint8_t displaybuf[DISPLAY_WIDTH * DISPLAY_HEIGHT / 8] = {0}; QRCode qrcode; -void init_display(uint8_t verbose) { +void init_display(bool verbose) { // block i2c bus access if (!I2C_MUTEX_LOCK()) @@ -70,7 +70,7 @@ void init_display(uint8_t verbose) { // is we have display RST line we toggle it to re-initialize display #ifdef MY_OLED_RST pinMode(MY_OLED_RST, OUTPUT); - digitalWrite(MY_OLED_RST, 0); // iniialization of SSD1306 chip is executed + digitalWrite(MY_OLED_RST, 0); // initialization of SSD1306 chip is executed delay(1); // keep RES low for at least 3us according to SSD1306 datasheet digitalWrite(MY_OLED_RST, 1); // normal operation #endif @@ -182,6 +182,18 @@ void refreshTheDisplay(bool nextPage) { } // mutex } // refreshDisplay() +void shutdown_display(void) { + // block i2c bus access + if (!I2C_MUTEX_LOCK()) + ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0); + else { + cfg.screenon = 0; + oledShutdown(); + delay(DISPLAYREFRESH_MS / 1000 * 1.1); + I2C_MUTEX_UNLOCK(); // release i2c bus access + } +} + void draw_page(time_t t, uint8_t page) { char timeState; diff --git a/src/i2cscan.cpp b/src/i2c.cpp similarity index 78% rename from src/i2cscan.cpp rename to src/i2c.cpp index d6e7ff64..4fe58adc 100644 --- a/src/i2cscan.cpp +++ b/src/i2c.cpp @@ -1,10 +1,30 @@ // Basic config #include "globals.h" -#include "i2cscan.h" +#include "i2c.h" // Local logging tag static const char TAG[] = __FILE__; +void i2c_init(void) { +#ifdef HAS_DISPLAY + Wire.begin(MY_OLED_SDA, MY_OLED_SCL, 400000); +#else + Wire.begin(SDA, SCL, 400000); +#endif +} + +void i2c_deinit(void) { + Wire.~TwoWire(); // shutdown/power off I2C hardware +#ifdef HAS_DISPLAY + // to save power, because Wire.end() enables pullups + pinMode(MY_OLED_SDA, INPUT); + pinMode(MY_OLED_SCL, INPUT); +#else + pinMode(SDA, INPUT); + pinMode(SCL, INPUT); +#endif +} + int i2c_scan(void) { int i2c_ret, addr; diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp index 8d1be880..47172528 100644 --- a/src/irqhandler.cpp +++ b/src/irqhandler.cpp @@ -20,7 +20,15 @@ void irqHandler(void *pvParameters) { if (InterruptStatus & UNMASK_IRQ) // interrupt handler to be enabled? mask_irq = false; - else if (mask_irq) // suppress processing if interrupt handler is disabled + // else if (mask_irq) // suppress processing if interrupt handler is + // disabled +#if (HAS_LORA) + else if (mask_irq || os_queryTimeCriticalJobs(ms2osticks(100))) +#else + else if (mask_irq) +#endif + // suppress processing if interrupt handler is disabled + // or time critical lmic jobs are pending in next 100ms continue; else if (InterruptStatus & MASK_IRQ) { // interrupt handler to be disabled? mask_irq = true; @@ -66,7 +74,7 @@ void irqHandler(void *pvParameters) { // do we have a power event? #if (HAS_PMU) if (InterruptStatus & PMU_IRQ) - power_event_IRQ(); + AXP192_powerevent_IRQ(); #endif // is time to send the payload? diff --git a/src/lmic_config.h b/src/lmic_config.h index e668bc70..fb3a5816 100644 --- a/src/lmic_config.h +++ b/src/lmic_config.h @@ -28,14 +28,14 @@ // so consuming more power. You may sharpen (reduce) this value if you are // limited on battery. // ATTN: VALUES > 7 WILL CAUSE RECEPTION AND JOIN PROBLEMS WITH HIGH SF RATES -//#define CLOCK_ERROR_PROCENTAGE 5 +#define CLOCK_ERROR_PROCENTAGE 5 // Set this to 1 to enable some basic debug output (using printf) about // RF settings used during transmission and reception. Set to 2 to // enable more verbose output. Make sure that printf is actually // configured (e.g. on AVR it is not by default), otherwise using it can // cause crashing. -//#define LMIC_DEBUG_LEVEL 1 +//#define LMIC_DEBUG_LEVEL 2 // Enable this to allow using printf() to print to the given serial port // (or any other Print object). This can be easy for debugging. The @@ -57,8 +57,7 @@ // Uncomment this to disable all code related to ping #define DISABLE_PING // Uncomment this to disable all code related to beacon tracking. -// Requires ping to be disabled too -#define DISABLE_BEACONS +// Requires ping to be disabled too#define DISABLE_BEACONS // Uncomment these to disable the corresponding MAC commands. // Class A diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 398e86f4..281a66ad 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -18,6 +18,11 @@ 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; @@ -86,9 +91,9 @@ void lora_setupForNetwork(bool preJoin) { #elif CFG_LMIC_EU_like // setting for TheThingsNetwork // TTN uses SF9, not SF12, for RX2 window - LMIC.dn2Dr = EU868_DR_SF9; - // Disable link check validation - LMIC_setLinkCheckMode(0); + // LMIC.dn2Dr = EU868_DR_SF9; + // Enable link check validation + LMIC_setLinkCheckMode(true); #endif } else { @@ -244,9 +249,9 @@ void lora_send(void *pvParameters) { while (1) { // postpone until we are joined if we are not - // while (!LMIC.devaddr) { - // vTaskDelay(pdMS_TO_TICKS(500)); - //} + while (!LMIC.devaddr) { + vTaskDelay(pdMS_TO_TICKS(500)); + } // fetch next or wait for payload to send from queue if (xQueueReceive(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) { @@ -287,7 +292,7 @@ void lora_send(void *pvParameters) { } } -esp_err_t lora_stack_init() { +esp_err_t lora_stack_init(bool do_join) { assert(SEND_QUEUE_SIZE); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); if (LoraSendQueue == 0) { @@ -303,13 +308,22 @@ esp_err_t lora_stack_init() { "lmictask", // name of task 4096, // stack size of task (void *)1, // parameter of the task - 5, // priority of the task + 2, // priority of the task &lmicTask, // task handle 1); // CPU core - // start join - if (!LMIC_startJoining()) - ESP_LOGI(TAG, "Already joined"); + // 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 { + LMIC_reset(); + LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey); + LMIC.seqnoUp = RTCseqnoUp; + LMIC.seqnoDn = RTCseqnoDn; + } // start lmic send task xTaskCreatePinnedToCore(lora_send, // task function @@ -357,8 +371,8 @@ void lora_enqueuedata(MessageBuffer_t *message) { void lora_queuereset(void) { xQueueReset(LoraSendQueue); } #if (TIME_SYNC_LORAWAN) -void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime, - int flagSuccess) { +static void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime, + int flagSuccess) { // Explicit conversion from void* to uint32_t* to avoid compiler errors time_t *pUserUTCTime = (time_t *)pVoidUserUTCTime; @@ -428,7 +442,7 @@ void lmictask(void *pvParameters) { // 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 / 100); + LMIC_setClockError(CLOCK_ERROR_PROCENTAGE * MAX_CLOCK_ERROR / 1000); #endif while (1) { @@ -438,7 +452,7 @@ void lmictask(void *pvParameters) { } // lmictask // lmic event handler -void myEventCallback(void *pUserData, ev_t ev) { +static void myEventCallback(void *pUserData, ev_t ev) { // using message descriptors from LMIC library static const char *const evNames[] = {LMIC_EVENT_NAME_TABLE__INIT}; @@ -482,8 +496,8 @@ void myEventCallback(void *pUserData, ev_t ev) { } // receive message handler -void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg, - size_t nMsg) { +static void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg, + size_t nMsg) { // display type of received data if (nMsg) @@ -537,7 +551,7 @@ void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg, } // transmit complete message handler -void myTxCallback(void *pUserData, int fSuccess) { +static void myTxCallback(void *pUserData, int fSuccess) { #if (TIME_SYNC_LORASERVER) // if last packet sent was a timesync request, store TX timestamp diff --git a/src/main.cpp b/src/main.cpp index f9bdce47..bf41a823 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,7 +31,7 @@ ledloop 0 3 blinks LEDs spiloop 0 2 reads/writes data on spi interface IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer -lmictask 1 5 MCCI LMiC LORAWAN stack +lmictask 1 2 MCCI LMiC LORAWAN stack clockloop 1 4 generates realtime telegrams for external clock timesync_req 1 3 processes realtime time sync requests irqhandler 1 1 display, timesync, gps, etc. triggered by timers @@ -129,53 +129,55 @@ void setup() { esp_log_level_set("*", ESP_LOG_NONE); #endif - ESP_LOGI(TAG, "Starting Software v%s", PROGVERSION); + do_after_reset(rtc_get_reset_reason(0)); - // print chip information on startup if in verbose mode + // print chip information on startup if in verbose mode after coldstart #if (VERBOSE) - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - ESP_LOGI(TAG, - "This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision " - "%d, %dMB %s Flash", - chip_info.cores, (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", - (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "", - chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024), - (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" - : "external"); - ESP_LOGI(TAG, "Internal Total heap %d, internal Free Heap %d", - ESP.getHeapSize(), ESP.getFreeHeap()); + + if (RTC_runmode == RUNMODE_POWERCYCLE) { + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + ESP_LOGI(TAG, + "This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision " + "%d, %dMB %s Flash", + chip_info.cores, + (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", + (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "", + chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024), + (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" + : "external"); + ESP_LOGI(TAG, "Internal Total heap %d, internal Free Heap %d", + ESP.getHeapSize(), ESP.getFreeHeap()); #ifdef BOARD_HAS_PSRAM - ESP_LOGI(TAG, "SPIRam Total heap %d, SPIRam Free Heap %d", ESP.getPsramSize(), - ESP.getFreePsram()); + ESP_LOGI(TAG, "SPIRam Total heap %d, SPIRam Free Heap %d", + ESP.getPsramSize(), ESP.getFreePsram()); #endif - ESP_LOGI(TAG, "ChipRevision %d, Cpu Freq %d, SDK Version %s", - ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion()); - ESP_LOGI(TAG, "Flash Size %d, Flash Speed %d", ESP.getFlashChipSize(), - ESP.getFlashChipSpeed()); - ESP_LOGI(TAG, "Wifi/BT software coexist version %s", esp_coex_version_get()); + ESP_LOGI(TAG, "ChipRevision %d, Cpu Freq %d, SDK Version %s", + ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion()); + ESP_LOGI(TAG, "Flash Size %d, Flash Speed %d", ESP.getFlashChipSize(), + ESP.getFlashChipSpeed()); + ESP_LOGI(TAG, "Wifi/BT software coexist version %s", + esp_coex_version_get()); #if (HAS_LORA) - ESP_LOGI(TAG, "IBM LMIC version %d.%d.%d", LMIC_VERSION_MAJOR, - LMIC_VERSION_MINOR, LMIC_VERSION_BUILD); - ESP_LOGI(TAG, "Arduino LMIC version %d.%d.%d.%d", - ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION), - ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION), - ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION), - ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION)); - showLoraKeys(); + ESP_LOGI(TAG, "IBM LMIC version %d.%d.%d", LMIC_VERSION_MAJOR, + LMIC_VERSION_MINOR, LMIC_VERSION_BUILD); + ESP_LOGI(TAG, "Arduino LMIC version %d.%d.%d.%d", + ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION)); + showLoraKeys(); #endif // HAS_LORA #if (HAS_GPS) - ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); + ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); #endif + } +#endif // VERBOSE -// open i2c bus -#ifdef HAS_DISPLAY - Wire.begin(MY_OLED_SDA, MY_OLED_SCL, 400000); -#else - Wire.begin(SDA, SCL, 400000); -#endif + // open i2c bus + i2c_init(); // setup power on boards with power management logic #ifdef EXT_POWER_SW @@ -188,8 +190,6 @@ void setup() { strcat_P(features, " PMU"); #endif -#endif // verbose - // read (and initialize on first run) runtime settings from NVRAM loadConfig(); // includes initialize if necessary @@ -197,7 +197,8 @@ void setup() { #ifdef HAS_DISPLAY strcat_P(features, " OLED"); DisplayIsOn = cfg.screenon; - init_display(!cfg.runmode); // note: blocking call + // display verbose info only after a coldstart (note: blocking call!) + init_display(RTC_runmode == RUNMODE_POWERCYCLE ? true : false); #endif // scan i2c bus for devices @@ -213,7 +214,7 @@ void setup() { pinMode(BAT_MEASURE_EN, OUTPUT); #endif - // initialize leds +// initialize leds #if (HAS_LED != NOT_A_PIN) pinMode(HAS_LED, OUTPUT); strcat_P(features, " LED"); @@ -266,11 +267,8 @@ void setup() { #if (USE_OTA) strcat_P(features, " OTA"); // reboot to firmware update mode if ota trigger switch is set - if (cfg.runmode == 1) { - cfg.runmode = 0; - saveConfig(); + if (RTC_runmode == RUNMODE_UPDATE) start_ota_update(); - } #endif // start BLE scan callback if BLE function is enabled in NVRAM configuration @@ -314,7 +312,9 @@ void setup() { // initialize LoRa #if (HAS_LORA) strcat_P(features, " LORA"); - assert(lora_stack_init() == ESP_OK); + // kick off join, except we come from sleep + assert(lora_stack_init(RTC_runmode == RUNMODE_WAKEUP ? false : true) == + ESP_OK); #endif // initialize SPI @@ -345,7 +345,7 @@ void setup() { strcat_P(features, " LPPPKD"); #endif - // initialize RTC +// initialize RTC #ifdef HAS_RTC strcat_P(features, " RTC"); assert(rtc_init()); @@ -400,7 +400,7 @@ void setup() { assert(irqHandlerTask != NULL); // has interrupt handler task started? ESP_LOGI(TAG, "Starting Timers..."); - // display interrupt +// display interrupt #ifdef HAS_DISPLAY // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up @@ -410,7 +410,7 @@ void setup() { timerAlarmEnable(displayIRQ); #endif - // LED Matrix display interrupt +// LED Matrix display interrupt #ifdef HAS_MATRIX_DISPLAY // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 3, count up @@ -420,7 +420,7 @@ void setup() { timerAlarmEnable(matrixDisplayIRQ); #endif - // initialize button +// initialize button #ifdef HAS_BUTTON strcat_P(features, " BTN_"); #ifdef BUTTON_PULLUP @@ -465,6 +465,9 @@ void setup() { // show compiled features ESP_LOGI(TAG, "Features:%s", features); + // set runmode to normal + RTC_runmode = RUNMODE_NORMAL; + vTaskDelete(NULL); } // setup() diff --git a/src/ota.cpp b/src/ota.cpp index f27b4a8c..d78a6453 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -40,12 +40,6 @@ inline String getHeaderValue(String header, String headerName) { void start_ota_update() { - // check battery status if we can before doing ota - if (!batt_sufficient()) { - ESP_LOGE(TAG, "Battery voltage %dmV too low for OTA", batt_voltage); - return; - } - switch_LED(LED_ON); // init display @@ -105,7 +99,7 @@ end: ESP_LOGI(TAG, "Rebooting to %s firmware", (ret == 0) ? "new" : "current"); ota_display(5, "**", ""); // mark line rebooting delay(5000); - ESP.restart(); + do_reset(false); } // start_ota_update @@ -320,7 +314,7 @@ void ota_display(const uint8_t row, const std::string status, } // callback function to show download progress while streaming data -void show_progress(unsigned long current, unsigned long size) { +static void show_progress(unsigned long current, unsigned long size) { #ifdef HAS_DISPLAY char buf[17]; snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size); diff --git a/src/paxcounter.conf b/src/paxcounter.conf index c29d5d77..1e39ece9 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -7,7 +7,7 @@ // Note: After editing, before "build", use "clean" button in PlatformIO! // Verbose enables serial output -#define VERBOSE 1 // set to 0 to silence the device, for mute use build option +#define VERBOSE 0 // set to 0 to silence the device, for mute use build option // Payload send cycle and encoding #define SENDCYCLE 30 // payload send cycle [seconds/2], 0 .. 255 diff --git a/src/power.cpp b/src/power.cpp index 597f68e8..2afe9d25 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -5,11 +5,24 @@ // Local logging tag static const char TAG[] = __FILE__; -#ifdef HAS_PMU +#ifdef BAT_MEASURE_ADC +esp_adc_cal_characteristics_t *adc_characs = + (esp_adc_cal_characteristics_t *)calloc( + 1, sizeof(esp_adc_cal_characteristics_t)); +#ifndef BAT_MEASURE_ADC_UNIT // ADC1 +static const adc1_channel_t adc_channel = BAT_MEASURE_ADC; +#else // ADC2 +static const adc2_channel_t adc_channel = BAT_MEASURE_ADC; +#endif +static const adc_atten_t atten = ADC_ATTEN_DB_11; +static const adc_unit_t unit = ADC_UNIT_1; +#endif // BAT_MEASURE_ADC + +#ifdef HAS_PMU AXP20X_Class pmu; -void power_event_IRQ(void) { +void AXP192_powerevent_IRQ(void) { pmu.readIRQ(); @@ -34,15 +47,17 @@ void power_event_IRQ(void) { if (pmu.isBattTempHighIRQ()) ESP_LOGI(TAG, "Battery low temperature."); - // display on/off - // if (pmu.isPEKShortPressIRQ()) { - // cfg.screenon = !cfg.screenon; - //} +// short press -> esp32 deep sleep mode, can be exited by pressing user button +#ifdef HAS_BUTTON + if (pmu.isPEKShortPressIRQ() && (RTC_runmode == RUNMODE_NORMAL)) { + enter_deepsleep(0, HAS_BUTTON); + } +#endif - // shutdown power + // long press -> shutdown power, can be exited by another longpress if (pmu.isPEKLongtPressIRQ()) { - AXP192_power(false); // switch off Lora, GPS, display - pmu.shutdown(); // switch off device + AXP192_power(pmu_power_off); // switch off Lora, GPS, display + pmu.shutdown(); // switch off device } pmu.clearIRQ(); @@ -51,18 +66,31 @@ void power_event_IRQ(void) { read_voltage(); } -void AXP192_power(bool on) { - if (on) { - pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0 - pmu.setPowerOutPut(AXP192_LDO3, AXP202_ON); // Gps on T-Beam V1.0 - pmu.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED on T-Beam v1.0 - // pmu.setChgLEDMode(AXP20X_LED_LOW_LEVEL); - pmu.setChgLEDMode(AXP20X_LED_BLINK_1HZ); - } else { +void AXP192_power(pmu_power_t powerlevel) { + + switch (powerlevel) { + + case pmu_power_off: pmu.setChgLEDMode(AXP20X_LED_OFF); pmu.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF); pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF); + // pmu.setPowerOutPut(AXP192_DCDC3, AXP202_OFF); + break; + + case pmu_power_sleep: + pmu.setChgLEDMode(AXP20X_LED_BLINK_1HZ); + // we don't cut off DCDC1, because then display blocks i2c bus + pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // gps off + pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // lora off + break; + + default: // all rails power on + pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0 + pmu.setPowerOutPut(AXP192_LDO3, AXP202_ON); // Gps on T-Beam V1.0 + pmu.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED on T-Beam v1.0 + pmu.setChgLEDMode(AXP20X_LED_LOW_LEVEL); + break; } } @@ -103,7 +131,7 @@ void AXP192_init(void) { pmu.adc1Enable(AXP202_VBUS_CUR_ADC1, true); // switch power rails on - AXP192_power(true); + AXP192_power(pmu_power_on); #ifdef PMU_INT pinMode(PMU_INT, INPUT_PULLUP); @@ -119,7 +147,7 @@ void AXP192_init(void) { } } -// helper functions for mutexing i2c access +// helper functions for mutexing pmu i2c access uint8_t i2c_readBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) { if (I2C_MUTEX_LOCK()) { @@ -170,21 +198,6 @@ uint8_t i2c_writeBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) { #endif // HAS_PMU -#ifdef BAT_MEASURE_ADC -esp_adc_cal_characteristics_t *adc_characs = - (esp_adc_cal_characteristics_t *)calloc( - 1, sizeof(esp_adc_cal_characteristics_t)); - -#ifndef BAT_MEASURE_ADC_UNIT // ADC1 -static const adc1_channel_t adc_channel = BAT_MEASURE_ADC; -#else // ADC2 -static const adc2_channel_t adc_channel = BAT_MEASURE_ADC; -#endif -static const adc_atten_t atten = ADC_ATTEN_DB_11; -static const adc_unit_t unit = ADC_UNIT_1; - -#endif // BAT_MEASURE_ADC - void calibrate_voltage(void) { #ifdef BAT_MEASURE_ADC // configure ADC diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 4290555c..293e9fae 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -5,21 +5,12 @@ // Local logging tag static const char TAG[] = __FILE__; -// helper function -void do_reset() { - ESP_LOGI(TAG, "Remote command: restart device"); -#if (HAS_LORA) - LMIC_shutdown(); -#endif - delay(3000); - esp_restart(); -} - // set of functions that can be triggered by remote commands void set_reset(uint8_t val[]) { switch (val[0]) { - case 0: // restart device - do_reset(); + case 0: // restart device with cold start (clear RTC saved variables) + ESP_LOGI(TAG, "Remote command: restart device cold"); + do_reset(false); break; case 1: // reset MAC counter ESP_LOGI(TAG, "Remote command: reset MAC counter"); @@ -34,10 +25,14 @@ void set_reset(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: flush send queue"); flushQueues(); break; + case 4: // restart device with warm start (keep RTC saved variables) + ESP_LOGI(TAG, "Remote command: restart device warm"); + do_reset(true); + break; case 9: // reset and ask for software update via Wifi OTA ESP_LOGI(TAG, "Remote command: software update via Wifi"); #if (USE_OTA) - cfg.runmode = 1; + RTC_runmode = RUNMODE_UPDATE; #endif // USE_OTA break; @@ -346,7 +341,7 @@ static cmd_t table[] = { {0x03, set_gps, 1, true}, {0x04, set_display, 1, true}, {0x05, set_loradr, 1, true}, {0x06, set_lorapower, 1, true}, {0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true}, - {0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true}, + {0x09, set_reset, 1, false}, {0x0a, set_sendcycle, 1, true}, {0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true}, {0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true}, {0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true}, diff --git a/src/reset.cpp b/src/reset.cpp new file mode 100644 index 00000000..0d30f05b --- /dev/null +++ b/src/reset.cpp @@ -0,0 +1,114 @@ +// Basic Config +#include "globals.h" +#include "reset.h" + +// Local logging tag +static const char TAG[] = __FILE__; + +// variable keep its values after restart or wakeup from sleep +RTC_NOINIT_ATTR runmode_t RTC_runmode; + +void do_reset(bool warmstart) { + if (warmstart) { + // store LMIC keys and counters in RTC memory +#if (HAS_LORA) + LMIC_getSessionKeys(&RTCnetid, &RTCdevaddr, RTCnwkKey, RTCartKey); + RTCseqnoUp = LMIC.seqnoUp; + RTCseqnoDn = LMIC.seqnoDn; +#endif + ESP_LOGI(TAG, "restarting device (warmstart), keeping runmode %d", + RTC_runmode); + } else { +#if (HAS_LORA) + if (RTC_runmode == RUNMODE_NORMAL) + LMIC_shutdown(); +#endif + RTC_runmode = RUNMODE_POWERCYCLE; + ESP_LOGI(TAG, "restarting device (coldstart), set runmode %d", RTC_runmode); + } + esp_restart(); +} + +void do_after_reset(int reason) { + + switch (reason) { + + case POWERON_RESET: // 0x01 Vbat power on reset + case RTCWDT_BROWN_OUT_RESET: // 0x0f Reset when the vdd voltage is not + // stable + RTC_runmode = RUNMODE_POWERCYCLE; + break; + + case DEEPSLEEP_RESET: // 0x05 Deep Sleep reset digital core + RTC_runmode = RUNMODE_WAKEUP; + break; + + case SW_RESET: // 0x03 Software reset digital core + case OWDT_RESET: // 0x04 Legacy watch dog reset digital core + case SDIO_RESET: // 0x06 Reset by SLC module, reset digital core + case TG0WDT_SYS_RESET: // 0x07 Timer Group0 Watch dog reset digital core + case TG1WDT_SYS_RESET: // 0x08 Timer Group1 Watch dog reset digital core + case RTCWDT_SYS_RESET: // 0x09 RTC Watch dog Reset digital core + case INTRUSION_RESET: // 0x0a Instrusion tested to reset CPU + case TGWDT_CPU_RESET: // 0x0b Time Group reset CPU + case SW_CPU_RESET: // 0x0c Software reset CPU + case RTCWDT_CPU_RESET: // 0x0d RTC Watch dog Reset CPU + case EXT_CPU_RESET: // 0x0e for APP CPU, reseted by PRO CPU + case RTCWDT_RTC_RESET: // 0x10 RTC Watch dog reset digital core and rtc mode + default: + break; + } + + ESP_LOGI(TAG, "Starting Software v%s, runmode %d", PROGVERSION, RTC_runmode); +} + +void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio) { + + if ((!wakeup_sec) && (!wakeup_gpio) && (RTC_runmode == RUNMODE_NORMAL)) + return; + + // 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); + + // set wakeup gpio + if (wakeup_gpio != NOT_A_PIN) { + rtc_gpio_isolate(wakeup_gpio); + esp_sleep_enable_ext1_wakeup(1ULL << wakeup_gpio, ESP_EXT1_WAKEUP_ALL_LOW); + } + +// store LMIC keys and counters in RTC memory +#if (HAS_LORA) + LMIC_getSessionKeys(&RTCnetid, &RTCdevaddr, RTCnwkKey, RTCartKey); + RTCseqnoUp = LMIC.seqnoUp; + RTCseqnoDn = LMIC.seqnoDn; +#endif + + // halt interrupts accessing i2c bus + mask_user_IRQ(); + +// switch off display +#ifdef HAS_DISPLAY + shutdown_display(); +#endif + +// switch off wifi & ble +#if (BLECOUNTER) + stop_BLEscan(); +#endif + +// reduce power if has PMU +#ifdef HAS_PMU + AXP192_power(pmu_power_sleep); +#endif + + // shutdown i2c bus + i2c_deinit(); + + // enter sleep mode + ESP_LOGI(TAG, "Going to sleep..."); + esp_deep_sleep_start(); +} \ No newline at end of file diff --git a/src/wifiscan.cpp b/src/wifiscan.cpp index b22fe5f2..d24116fc 100644 --- a/src/wifiscan.cpp +++ b/src/wifiscan.cpp @@ -29,7 +29,7 @@ typedef struct { } wifi_ieee80211_packet_t; // using IRAM_:ATTR here to speed up callback function -IRAM_ATTR void wifi_sniffer_packet_handler(void *buff, +static IRAM_ATTR void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type) { const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff;