Save and restore for deep sleep

This commit is contained in:
Manuel Bl
2021-09-28 17:35:05 +02:00
parent de4297a8f4
commit e71d584fca
7 changed files with 278 additions and 21 deletions

View File

@ -22,6 +22,8 @@
#include "driver/timer.h"
#include "esp_timer.h"
#include "esp_log.h"
#include <time.h>
#include <sys/time.h>
#define LMIC_UNUSED_PIN 0xff
@ -46,7 +48,7 @@ static void lmic_background_task(void* pvParameter);
static void qio_irq_handler(void* arg);
static void timer_callback(void *arg);
static int64_t os_time_to_esp_time(int64_t esp_now, uint32_t os_time);
static int64_t get_current_time();
static void init_io(void);
static void init_spi(void);
static void init_timer(void);
@ -64,7 +66,6 @@ static gpio_num_t pin_dio0;
static gpio_num_t pin_dio1;
static int8_t rssi_cal = 10;
static TaskHandle_t lmic_task;
static uint32_t dio_interrupt_time;
static uint8_t dio_num;
@ -73,6 +74,7 @@ static spi_device_handle_t spi_handle;
static spi_transaction_t spi_transaction;
static SemaphoreHandle_t mutex;
static esp_timer_handle_t timer;
static int64_t time_offset;
static int64_t next_alarm;
static volatile bool run_background_task;
static volatile wait_kind_e current_wait_kind;
@ -249,11 +251,14 @@ void hal_spi_read(u1_t cmd, u1_t *buf, size_t len)
/*
* LIMIC uses a 32 bit time system (ostime_t) counting ticks. In this
* implementation each tick is 16µs. It will wrap arounnd every 19 hours.
* implementation, each tick is 16µs. It will wrap arounnd every 19 hours.
*
* The ESP32 has a 64 bit timer counting microseconds. It will wrap around
* every 584,000 years. So we don't need to bother.
*
* The time includes an offset initialized from `gettimeofday()`
* to ensure the time correctly advances during deep sleep.
*
* Based on this timer, future callbacks can be scheduled. This is used to
* schedule the next LMIC job.
*/
@ -276,15 +281,18 @@ int64_t os_time_to_esp_time(int64_t esp_now, uint32_t os_time)
}
else
{
// one's complement instead of two's complement:
// off by 1 µs and ignored
os_diff = ~os_diff;
os_diff = -os_diff;
esp_time = esp_now - (((int64_t)os_diff) << 4);
}
return esp_time;
}
int64_t get_current_time()
{
return esp_timer_get_time() + time_offset;
}
void init_timer(void)
{
esp_timer_create_args_t timer_config = {
@ -296,6 +304,10 @@ void init_timer(void)
esp_err_t err = esp_timer_create(&timer_config, &timer);
ESP_ERROR_CHECK(err);
struct timeval now;
gettimeofday(&now, NULL);
time_offset = (int64_t)now.tv_sec * 1000000;
ESP_LOGI(TAG, "Timer initialized");
}
@ -308,7 +320,7 @@ void arm_timer(int64_t esp_now)
{
if (next_alarm == 0)
return;
int64_t timeout = next_alarm - esp_timer_get_time();
int64_t timeout = next_alarm - get_current_time();
if (timeout < 0)
timeout = 10;
esp_timer_start_once(timer, timeout);
@ -379,7 +391,7 @@ TickType_t hal_esp32_get_timer_duration(void)
return 1; // busy, not waiting
if (alarm_time != 0)
return pdMS_TO_TICKS((alarm_time - esp_timer_get_time() + 999) / 1000);
return pdMS_TO_TICKS((alarm_time - get_current_time() + 999) / 1000);
return 0; // waiting indefinitely
@ -391,7 +403,7 @@ u4_t IRAM_ATTR hal_ticks(void)
{
// LMIC tick unit: 16µs
// esp_timer unit: 1µs
return (u4_t)(esp_timer_get_time() >> 4);
return (u4_t)(get_current_time() >> 4);
}
// Wait until the specified time.
@ -399,7 +411,7 @@ u4_t IRAM_ATTR hal_ticks(void)
// All other events are ignored and will be served later.
u4_t hal_waitUntil(u4_t time)
{
int64_t esp_now = esp_timer_get_time();
int64_t esp_now = get_current_time();
int64_t esp_time = os_time_to_esp_time(esp_now, time);
set_next_alarm(esp_time);
arm_timer(esp_now);
@ -423,7 +435,7 @@ void hal_esp32_wake_up(void)
// in the queue. If the job is not due yet, LMIC will go to sleep.
u1_t hal_checkTimer(uint32_t time)
{
int64_t esp_now = esp_timer_get_time();
int64_t esp_now = get_current_time();
int64_t esp_time = os_time_to_esp_time(esp_now, time);
int64_t diff = esp_time - esp_now;
if (diff < 100)
@ -440,7 +452,7 @@ void hal_sleep(void)
if (wait(WAIT_KIND_CHECK_IO))
return;
arm_timer(esp_timer_get_time());
arm_timer(get_current_time());
wait(WAIT_KIND_WAIT_FOR_ANY_EVENT);
}

View File

@ -18,6 +18,7 @@
#include "lmic/lmic.h"
#include "ttn_logging.h"
#include "ttn_provisioning.h"
#include "ttn_rtc.h"
#define TAG "ttn"
@ -61,7 +62,7 @@ static bool is_started;
static bool has_joined;
static QueueHandle_t lmic_event_queue;
static ttn_message_cb message_callback;
static ttn_waiting_reason_t waiting_reason = TTN_WAITING_NONE;
static ttn_waiting_reason_t waiting_reason;
static ttn_rf_settings_t last_rf_settings[4];
static ttn_rx_tx_window_t current_rx_tx_window;
static int subband = 2;
@ -212,6 +213,29 @@ bool ttn_join(void)
return join_core();
}
bool ttn_resume_after_deep_sleep(void)
{
if (!ttn_provisioning_have_keys())
{
if (!ttn_provisioning_restore_keys(false))
return false;
}
if (!ttn_provisioning_have_keys())
{
ESP_LOGW(TAG, "DevEUI, AppEUI/JoinEUI and/or AppKey have not been provided");
return false;
}
start();
if (!ttn_rtc_restore())
return false;
has_joined = true;
return true;
}
// Called immediately before sending join request message
void config_rf_params(void)
{
@ -310,6 +334,12 @@ bool ttn_is_provisioned(void)
return ttn_provisioning_have_keys();
}
void ttn_prepare_for_deep_sleep(void)
{
ttn_rtc_save();
}
void ttn_wait_for_idle(void)
{
while (true)
@ -353,30 +383,26 @@ void ttn_set_adr_enabled(bool enabled)
void ttn_set_data_rate(ttn_data_rate_t data_rate)
{
join_data_rate = data_rate;
if (has_joined)
{
hal_esp32_enter_critical_section();
LMIC_setDrTxpow(data_rate, LMIC.adrTxPow);
hal_esp32_leave_critical_section();
}
else
{
join_data_rate = data_rate;
}
}
void ttn_set_max_tx_pow(int tx_pow)
{
max_tx_power = tx_pow;
if (has_joined)
{
hal_esp32_enter_critical_section();
LMIC_setDrTxpow(LMIC.datarate, tx_pow);
hal_esp32_leave_critical_section();
}
else
{
max_tx_power = tx_pow;
}
}
ttn_rf_settings_t ttn_get_rf_settings(ttn_rx_tx_window_t window)

58
src/ttn_rtc.c Normal file
View File

@ -0,0 +1,58 @@
/*******************************************************************************
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2021 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* Functions for storing and retrieving TTN communication state from RTC memory.
*******************************************************************************/
#include "ttn_rtc.h"
#include "esp_system.h"
#include "lmic/lmic.h"
#include <string.h>
#define LMIC_OFFSET(field) __builtin_offsetof(struct lmic_t, field)
#define LMIC_DIST(field1, field2) (LMIC_OFFSET(field2) - LMIC_OFFSET(field1))
#define TTN_RTC_MEM_SIZE (sizeof(struct lmic_t) - LMIC_OFFSET(radio) - MAX_LEN_PAYLOAD - MAX_LEN_FRAME)
#define TTN_RTC_FLAG_VALUE 0xf30b84ce
RTC_DATA_ATTR uint8_t ttn_rtc_mem_buf[TTN_RTC_MEM_SIZE];
RTC_DATA_ATTR uint32_t ttn_rtc_flag;
void ttn_rtc_save()
{
// Copy LMIC struct except client, osjob, pendTxData and frame
size_t len1 = LMIC_DIST(radio, pendTxData);
memcpy(ttn_rtc_mem_buf, &LMIC.radio, len1);
size_t len2 = LMIC_DIST(pendTxData, frame) - MAX_LEN_PAYLOAD;
memcpy(ttn_rtc_mem_buf + len1, (u1_t *)&LMIC.pendTxData + MAX_LEN_PAYLOAD, len2);
size_t len3 = sizeof(struct lmic_t) - LMIC_OFFSET(frame) - MAX_LEN_FRAME;
memcpy(ttn_rtc_mem_buf + len1 + len2, (u1_t *)&LMIC.frame + MAX_LEN_FRAME, len3);
ttn_rtc_flag = TTN_RTC_FLAG_VALUE;
}
bool ttn_rtc_restore()
{
if (ttn_rtc_flag != TTN_RTC_FLAG_VALUE)
return false;
// Restore data
size_t len1 = LMIC_DIST(radio, pendTxData);
memcpy(&LMIC.radio, ttn_rtc_mem_buf, len1);
memset(LMIC.pendTxData, 0, MAX_LEN_PAYLOAD);
size_t len2 = LMIC_DIST(pendTxData, frame) - MAX_LEN_PAYLOAD;
memcpy((u1_t *)&LMIC.pendTxData + MAX_LEN_PAYLOAD, ttn_rtc_mem_buf + len1, len2);
memset(LMIC.frame, 0, MAX_LEN_FRAME);
size_t len3 = sizeof(struct lmic_t) - LMIC_OFFSET(frame) - MAX_LEN_FRAME;
memcpy((u1_t *)&LMIC.frame + MAX_LEN_FRAME, ttn_rtc_mem_buf + len1 + len2, len3);
ttn_rtc_flag = 0xffffffff; // invalidate RTC data
return true;
}

30
src/ttn_rtc.h Normal file
View File

@ -0,0 +1,30 @@
/*******************************************************************************
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2021 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* Functions for storing and retrieving TTN communication state from RTC memory.
*******************************************************************************/
#ifndef TTN_RTC_H
#define TTN_RTC_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
void ttn_rtc_save();
bool ttn_rtc_restore();
#ifdef __cplusplus
}
#endif
#endif