mirror of
https://github.com/manuelbl/ttn-esp32.git
synced 2025-08-17 12:10:34 +02:00
Save and restore for deep sleep
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
|
44
src/ttn.c
44
src/ttn.c
@ -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
58
src/ttn_rtc.c
Normal 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
30
src/ttn_rtc.h
Normal 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
|
Reference in New Issue
Block a user