209 lines
5.4 KiB
C++
209 lines
5.4 KiB
C++
// Basic Config
|
|
#include "globals.h"
|
|
#include "reset.h"
|
|
|
|
// Conversion factor for micro seconds to seconds
|
|
#define uS_TO_S_FACTOR 1000000ULL
|
|
|
|
// RTC_NOINIT_ATTR -> keeps value after a software restart or system crash
|
|
RTC_NOINIT_ATTR runmode_t RTC_runmode;
|
|
RTC_NOINIT_ATTR uint32_t RTC_restarts;
|
|
// RTC_DATA_ATTR -> keeps value after a wakeup from sleep
|
|
RTC_DATA_ATTR struct timeval sleep_start_time;
|
|
RTC_DATA_ATTR int64_t RTC_millis = 0;
|
|
|
|
struct timeval sleep_stop_time;
|
|
|
|
void reset_rtc_vars(void) {
|
|
RTC_runmode = RUNMODE_POWERCYCLE;
|
|
RTC_restarts = 0;
|
|
}
|
|
|
|
#if (HAS_TIME)
|
|
void adjust_wakeup(uint32_t *wakeuptime) {
|
|
// only adjust wakeup if we have a valid time
|
|
if ((timeSource == _unsynced) ||
|
|
(sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS)) {
|
|
ESP_LOGI(TAG, "Syncwakeup: No valid time for sync");
|
|
return;
|
|
}
|
|
|
|
time_t now;
|
|
time(&now);
|
|
|
|
// 1..3600 seconds between next wakeup time and following top-of-hour
|
|
uint16_t shift_sec = 3600 - (now + *wakeuptime) % 3600;
|
|
|
|
if (shift_sec <= SYNCWAKEUP) {
|
|
*wakeuptime += shift_sec; // delay wakeup to catch top-of-hour
|
|
ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec postponed", shift_sec);
|
|
} else if (shift_sec >= (3600 - SYNCWAKEUP)) {
|
|
*wakeuptime = 3600 - shift_sec; // shorten wake up to next top-of-hour
|
|
ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec preponed", shift_sec);
|
|
} else
|
|
ESP_LOGI(TAG, "Syncwakeup: Wakeup keeping unshifted");
|
|
}
|
|
#endif
|
|
|
|
void do_reset(bool warmstart) {
|
|
if (warmstart) {
|
|
ESP_LOGI(TAG, "restarting device (warmstart)");
|
|
} else {
|
|
#if (HAS_LORA)
|
|
if (RTC_runmode == RUNMODE_NORMAL) {
|
|
LMIC_shutdown();
|
|
}
|
|
#endif
|
|
reset_rtc_vars();
|
|
ESP_LOGI(TAG, "restarting device (coldstart)");
|
|
}
|
|
esp_restart();
|
|
}
|
|
|
|
void do_after_reset(void) {
|
|
struct timeval sleep_stop_time;
|
|
int64_t sleep_time_ms;
|
|
|
|
// read (and initialize on first run) runtime settings from NVRAM
|
|
loadConfig();
|
|
|
|
// set time zone to user value from paxcounter.conf
|
|
#ifdef TIME_SYNC_TIMEZONE
|
|
setenv("TZ", TIME_SYNC_TIMEZONE, 1);
|
|
tzset();
|
|
ESP_LOGD(TAG, "Timezone set to %s", TIME_SYNC_TIMEZONE);
|
|
#endif
|
|
|
|
switch (rtc_get_reset_reason(0)) {
|
|
case RESET_REASON_CHIP_POWER_ON:
|
|
case RESET_REASON_SYS_BROWN_OUT:
|
|
reset_rtc_vars();
|
|
break;
|
|
|
|
case RESET_REASON_CPU0_SW:
|
|
// keep previous set runmode (update / normal / maintenance)
|
|
RTC_restarts++;
|
|
break;
|
|
|
|
case RESET_REASON_CORE_DEEP_SLEEP:
|
|
// calculate time spent in deep sleep
|
|
gettimeofday(&sleep_stop_time, NULL);
|
|
sleep_time_ms = ((int64_t)sleep_stop_time.tv_sec * 1000000L +
|
|
(int64_t)sleep_stop_time.tv_usec -
|
|
(int64_t)sleep_start_time.tv_sec * 1000000L -
|
|
(int64_t)sleep_start_time.tv_usec) /
|
|
1000LL;
|
|
RTC_millis += sleep_time_ms; // increment system monotonic time
|
|
ESP_LOGI(TAG, "Time spent in deep sleep: %llu ms", sleep_time_ms);
|
|
// do we have a valid time? -> set global variable
|
|
timeSource = timeIsValid(sleep_stop_time.tv_sec) ? _set : _unsynced;
|
|
// set wakeup state, not if we have pending OTA update
|
|
if (RTC_runmode == RUNMODE_SLEEP)
|
|
RTC_runmode = RUNMODE_WAKEUP;
|
|
break;
|
|
|
|
default:
|
|
RTC_runmode = RUNMODE_POWERCYCLE;
|
|
RTC_restarts++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void enter_deepsleep(uint32_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
|
ESP_LOGI(TAG, "Preparing to sleep...");
|
|
|
|
RTC_runmode = RUNMODE_SLEEP;
|
|
|
|
// validate wake up pin, if we have
|
|
if (!GPIO_IS_VALID_GPIO(wakeup_gpio))
|
|
wakeup_gpio = GPIO_NUM_MAX;
|
|
// stop further enqueuing of senddata and MAC processing
|
|
libpax_counter_stop();
|
|
|
|
// switch off any power consuming hardware
|
|
#if (HAS_SDS011)
|
|
sds011_sleep();
|
|
#endif
|
|
|
|
// wait a while (max 100 sec) to clear send queues
|
|
ESP_LOGI(TAG, "Waiting until send queues are empty...");
|
|
for (int i = 100; i > 0; i--) {
|
|
if (allQueuesEmtpy())
|
|
break;
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
}
|
|
|
|
// wait up to 100secs until LMIC is idle
|
|
#if (HAS_LORA)
|
|
lora_waitforidle(100);
|
|
#endif // (HAS_LORA)
|
|
|
|
// shutdown MQTT safely
|
|
#ifdef HAS_MQTT
|
|
mqtt_deinit();
|
|
#endif
|
|
|
|
// shutdown SPI safely
|
|
#ifdef HAS_SPI
|
|
spi_deinit();
|
|
#endif
|
|
|
|
// save LMIC state to RTC RAM
|
|
#if (HAS_LORA)
|
|
SaveLMICToRTC(wakeup_sec);
|
|
#endif // (HAS_LORA)
|
|
|
|
// set display to power save mode
|
|
#ifdef HAS_DISPLAY
|
|
dp_shutdown();
|
|
#endif
|
|
|
|
// reduce power if has PMU or VEXT
|
|
#ifdef HAS_PMU
|
|
AXP192_power(pmu_power_sleep);
|
|
#elif EXT_POWER_SW
|
|
digitalWrite(EXT_POWER_SW, EXT_POWER_OFF);
|
|
#endif
|
|
|
|
// halt interrupts accessing i2c bus
|
|
mask_user_IRQ();
|
|
|
|
// shutdown i2c bus
|
|
i2c_deinit();
|
|
|
|
#if (HAS_TIME)
|
|
if (cfg.wakesync && cfg.sleepcycle)
|
|
adjust_wakeup(&wakeup_sec);
|
|
#endif
|
|
|
|
// configure wakeup sources
|
|
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html
|
|
|
|
// 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); // minimize deep sleep current
|
|
esp_sleep_enable_ext1_wakeup(1ULL << wakeup_gpio, ESP_EXT1_WAKEUP_ALL_LOW);
|
|
}
|
|
|
|
// time stamp sleep start time and save system monotonic time. Deep sleep.
|
|
gettimeofday(&sleep_start_time, NULL);
|
|
RTC_millis += esp_timer_get_time() / 1000LL;
|
|
ESP_LOGI(TAG, "Going to sleep, good bye.");
|
|
|
|
// flush & close sd card, if we have
|
|
#if (HAS_SDCARD)
|
|
sdcard_close();
|
|
#endif
|
|
|
|
esp_deep_sleep_start();
|
|
}
|
|
|
|
uint64_t uptime() {
|
|
return (uint64_t)(RTC_millis + esp_timer_get_time() / 1000LL);
|
|
}
|