mirror of
				https://github.com/manuelbl/ttn-esp32.git
				synced 2025-10-31 10:40:35 +01:00 
			
		
		
		
	Save and restore for deep sleep
This commit is contained in:
		
							
								
								
									
										10
									
								
								examples/deep_sleep_in_c/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/deep_sleep_in_c/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| cmake_minimum_required(VERSION 3.5) | ||||
| include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||||
|  | ||||
| # Update the below line to match the path to the ttn-esp32 library, | ||||
| # e.g. list(APPEND EXTRA_COMPONENT_DIRS "/Users/me/Documents/ttn-esp32") | ||||
| list(APPEND EXTRA_COMPONENT_DIRS "../..") | ||||
|  | ||||
| #add_definitions(-DLMIC_ENABLE_event_logging=1) | ||||
|  | ||||
| project(deep_sleep) | ||||
							
								
								
									
										4
									
								
								examples/deep_sleep_in_c/main/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/deep_sleep_in_c/main/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| idf_component_register( | ||||
|     SRCS "main.c" | ||||
|     INCLUDE_DIRS "." | ||||
|     REQUIRES ttn-esp32) | ||||
							
								
								
									
										117
									
								
								examples/deep_sleep_in_c/main/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								examples/deep_sleep_in_c/main/main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | ||||
| /******************************************************************************* | ||||
|  *  | ||||
|  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x | ||||
|  *  | ||||
|  * Copyright (c) 2021 Manuel Bleichenbacher | ||||
|  *  | ||||
|  * Licensed under MIT License | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  * | ||||
|  * Sample program (in C) sending messages and going to deep sleep in-between. | ||||
|  *******************************************************************************/ | ||||
|  | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "esp_event.h" | ||||
| #include "driver/gpio.h" | ||||
| #include "nvs_flash.h" | ||||
| #include "esp_log.h" | ||||
| #include "esp_sleep.h" | ||||
|  | ||||
| #include "ttn.h" | ||||
|  | ||||
| // NOTE: | ||||
| // The LoRaWAN frequency and the radio chip must be configured by running 'idf.py menuconfig'. | ||||
| // Go to Components / The Things Network, select the appropriate values and save. | ||||
|  | ||||
| // Copy the below hex strings from the TTN console (Applications > Your application > End devices | ||||
| // > Your device > Activation information) | ||||
|  | ||||
| // AppEUI (sometimes called JoinEUI) | ||||
| const char *appEui = "????????????????"; | ||||
| // DevEUI | ||||
| const char *devEui = "????????????????"; | ||||
| // AppKey | ||||
| const char *appKey = "????????????????????????????????"; | ||||
|  | ||||
| // Pins and other resources | ||||
| #define TTN_SPI_HOST      HSPI_HOST | ||||
| #define TTN_SPI_DMA_CHAN  1 | ||||
| #define TTN_PIN_SPI_SCLK  5 | ||||
| #define TTN_PIN_SPI_MOSI  27 | ||||
| #define TTN_PIN_SPI_MISO  19 | ||||
| #define TTN_PIN_NSS       18 | ||||
| #define TTN_PIN_RXTX      TTN_NOT_CONNECTED | ||||
| #define TTN_PIN_RST       14 | ||||
| #define TTN_PIN_DIO0      26 | ||||
| #define TTN_PIN_DIO1      35 | ||||
|  | ||||
| #define TX_INTERVAL 60 // in sec | ||||
| static uint8_t msgData[] = "Hello, world"; | ||||
|  | ||||
|  | ||||
| void app_main(void) | ||||
| { | ||||
|     esp_err_t err; | ||||
|     // Initialize the GPIO ISR handler service | ||||
|     err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM); | ||||
|     ESP_ERROR_CHECK(err); | ||||
|      | ||||
|     // Initialize the NVS (non-volatile storage) for saving and restoring the keys | ||||
|     err = nvs_flash_init(); | ||||
|     ESP_ERROR_CHECK(err); | ||||
|  | ||||
|     // Initialize SPI bus | ||||
|     spi_bus_config_t spi_bus_config = { | ||||
|         .miso_io_num = TTN_PIN_SPI_MISO, | ||||
|         .mosi_io_num = TTN_PIN_SPI_MOSI, | ||||
|         .sclk_io_num = TTN_PIN_SPI_SCLK, | ||||
|         .quadwp_io_num = -1, | ||||
|         .quadhd_io_num = -1 | ||||
|     };  | ||||
|     err = spi_bus_initialize(TTN_SPI_HOST, &spi_bus_config, TTN_SPI_DMA_CHAN); | ||||
|     ESP_ERROR_CHECK(err); | ||||
|  | ||||
|     // Initialize TTN | ||||
|     ttn_init(); | ||||
|  | ||||
|     // Configure the SX127x pins | ||||
|     ttn_configure_pins(TTN_SPI_HOST, TTN_PIN_NSS, TTN_PIN_RXTX, TTN_PIN_RST, TTN_PIN_DIO0, TTN_PIN_DIO1); | ||||
|  | ||||
|     // The below line can be commented after the first run as the data is saved in NVS | ||||
|     ttn_provision(devEui, appEui, appKey); | ||||
|  | ||||
|     // ttn_set_adr_enabled(false); | ||||
|     // ttn_set_data_rate(TTN_DR_US915_SF7); | ||||
|     // ttn_set_max_tx_pow(14); | ||||
|  | ||||
|     if (ttn_resume_after_deep_sleep()) { | ||||
|         printf("Resumed from deep sleep.\n"); | ||||
|      | ||||
|     } else { | ||||
|  | ||||
|         printf("Joining...\n"); | ||||
|         if (ttn_join()) | ||||
|         { | ||||
|             printf("Joined.\n"); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             printf("Join failed. Goodbye\n"); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     printf("Sending message...\n"); | ||||
|     ttn_response_code_t res = ttn_transmit_message(msgData, sizeof(msgData) - 1, 1, false); | ||||
|     printf(res == TTN_SUCCESSFUL_TRANSMISSION ? "Message sent.\n" : "Transmission failed.\n"); | ||||
|  | ||||
|     // Wait until TTN communication is idle and save state | ||||
|     ttn_wait_for_idle(); | ||||
|     ttn_prepare_for_deep_sleep(); | ||||
|  | ||||
|     // Schedule wake up | ||||
|     esp_sleep_enable_timer_wakeup(TX_INTERVAL * 1000000LL); | ||||
|  | ||||
|     printf("Going to deep sleep...\n"); | ||||
|     esp_deep_sleep_start(); | ||||
| } | ||||
| @ -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