mirror of
				https://github.com/manuelbl/ttn-esp32.git
				synced 2025-10-31 18:50:33 +01:00 
			
		
		
		
	C implementation for TheThingsNetwork
This commit is contained in:
		
							
								
								
									
										14
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @ -1,19 +1,11 @@ | |||||||
| { | { | ||||||
|     "files.associations": { |     "files.associations": { | ||||||
|         "provisioning.h": "c", |  | ||||||
|         "config.h": "c", |  | ||||||
|         "sdkconfig.h": "c", |         "sdkconfig.h": "c", | ||||||
|         "oslmic.h": "c", |         "oslmic.h": "c", | ||||||
|         "hal_esp32.h": "c", |         "hal_esp32.h": "c", | ||||||
|         "esp_log.h": "c", |         "esp_log.h": "c", | ||||||
|         "nvs_flash.h": "c", |         "lmic.h": "c", | ||||||
|         "__config": "c", |         "ttn_provisioning.h": "c", | ||||||
|         "__nullptr": "c", |         "lorabase.h": "c" | ||||||
|         "stdint.h": "c", |  | ||||||
|         "*.ipp": "c", |  | ||||||
|         "algorithm": "c", |  | ||||||
|         "random": "c", |  | ||||||
|         "complex": "c", |  | ||||||
|         "valarray": "c" |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -2,37 +2,30 @@ | |||||||
|  *  |  *  | ||||||
|  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x |  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x | ||||||
|  *  |  *  | ||||||
|  * Copyright (c) 2018 Manuel Bleichenbacher |  * Copyright (c) 2018-2021 Manuel Bleichenbacher | ||||||
|  *  |  *  | ||||||
|  * Licensed under MIT License |  * Licensed under MIT License | ||||||
|  * https://opensource.org/licenses/MIT |  * https://opensource.org/licenses/MIT | ||||||
|  * |  * | ||||||
|  * High-level API for ttn-esp32. |  * High-level C++ API for ttn-esp32. | ||||||
|  *******************************************************************************/ |  *******************************************************************************/ | ||||||
|  |  | ||||||
| #ifndef _THETHINGSNETWORK_H_ | #ifndef _THETHINGSNETWORK_H_ | ||||||
| #define _THETHINGSNETWORK_H_ | #define _THETHINGSNETWORK_H_ | ||||||
|  |  | ||||||
| #include <stdint.h> | #include "ttn.h" | ||||||
| #include "driver/spi_master.h" |  | ||||||
|  |  | ||||||
| /** | typedef ttn_port_t port_t; | ||||||
|  * @brief Constant for indicating that a pin is not connected |  | ||||||
|  */ |  | ||||||
| #define TTN_NOT_CONNECTED 0xff |  | ||||||
|  |  | ||||||
|  |  | ||||||
| typedef uint8_t port_t; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @brief Response codes |  * @brief Response codes | ||||||
|  */ |  */ | ||||||
| enum TTNResponseCode | enum TTNResponseCode | ||||||
| { | { | ||||||
|     kTTNErrorTransmissionFailed = -1, |     kTTNErrorTransmissionFailed = TTN_ERROR_TRANSMISSION_FAILED, | ||||||
|     kTTNErrorUnexpected = -10, |     kTTNErrorUnexpected = TTN_ERROR_UNEXPECTED, | ||||||
|     kTTNSuccessfulTransmission = 1, |     kTTNSuccessfulTransmission = TTN_SUCCESSFUL_TRANSMISSION, | ||||||
|     kTTNSuccessfulReceive = 2 |     kTTNSuccessfulReceive = TTN_SUCCESSFUL_RECEIVE | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -44,19 +37,19 @@ enum TTNRxTxWindow | |||||||
|     /** |     /** | ||||||
|      * @brief Outside RX/TX window |      * @brief Outside RX/TX window | ||||||
|      */ |      */ | ||||||
|     kTTNIdleWindow = 0, |     kTTNIdleWindow = TTN_WINDOW_IDLE, | ||||||
|     /** |     /** | ||||||
|      * @brief Transmission window (up to RX1 window) |      * @brief Transmission window (up to RX1 window) | ||||||
|      */ |      */ | ||||||
|     kTTNTxWindow = 1, |     kTTNTxWindow = TTN_WINDOW_TX, | ||||||
|     /** |     /** | ||||||
|      * @brief Reception window 1 (up to RX2 window) |      * @brief Reception window 1 (up to RX2 window) | ||||||
|      */ |      */ | ||||||
|     kTTNRx1Window = 2, |     kTTNRx1Window = TTN_WINDOW_RX1, | ||||||
|     /** |     /** | ||||||
|      * @brief Reception window 2 |      * @brief Reception window 2 | ||||||
|      */ |      */ | ||||||
|     kTTNRx2Window = 3 |     kTTNRx2Window = TTN_WINDOW_RX2 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -68,35 +61,35 @@ enum TTNSpreadingFactor | |||||||
|     /** |     /** | ||||||
|      * @brief Unused / undefined spreading factor |      * @brief Unused / undefined spreading factor | ||||||
|      */ |      */ | ||||||
|     kTTNSFNone = 0, |     kTTNSFNone = TTN_SF_NONE, | ||||||
|     /** |     /** | ||||||
|      * @brief Frequency Shift Keying (FSK) |      * @brief Frequency Shift Keying (FSK) | ||||||
|      */ |      */ | ||||||
|     kTTNFSK = 1, |     kTTNFSK = TTN_FSK, | ||||||
|     /** |     /** | ||||||
|      * @brief Spreading Factor 7 (SF7) |      * @brief Spreading Factor 7 (SF7) | ||||||
|      */ |      */ | ||||||
|     kTTNSF7 = 2, |     kTTNSF7 = TTN_SF7, | ||||||
|     /** |     /** | ||||||
|      * @brief Spreading Factor 8 (SF8) |      * @brief Spreading Factor 8 (SF8) | ||||||
|      */ |      */ | ||||||
|     kTTNSF8 = 3, |     kTTNSF8 = TTN_SF8, | ||||||
|     /** |     /** | ||||||
|      * @brief Spreading Factor 9 (SF9) |      * @brief Spreading Factor 9 (SF9) | ||||||
|      */ |      */ | ||||||
|     kTTNSF9 = 4, |     kTTNSF9 = TTN_SF9, | ||||||
|     /** |     /** | ||||||
|      * @brief Spreading Factor 10 (SF10) |      * @brief Spreading Factor 10 (SF10) | ||||||
|      */ |      */ | ||||||
|     kTTNSF10 = 5, |     kTTNSF10 = TTN_SF10, | ||||||
|     /** |     /** | ||||||
|      * @brief Spreading Factor 11 (SF11) |      * @brief Spreading Factor 11 (SF11) | ||||||
|      */ |      */ | ||||||
|     kTTNSF11 = 6, |     kTTNSF11 = TTN_SF11, | ||||||
|     /** |     /** | ||||||
|      * @brief Spreading Factor 12 (SF12) |      * @brief Spreading Factor 12 (SF12) | ||||||
|      */ |      */ | ||||||
|     kTTNSF12 = 7 |     kTTNSF12 = TTN_SF12 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -108,19 +101,19 @@ enum TTNBandwidth | |||||||
|     /** |     /** | ||||||
|      * @brief Undefined/unused bandwidth |      * @brief Undefined/unused bandwidth | ||||||
|      */ |      */ | ||||||
|     kTTNBWNone = 0, |     kTTNBWNone = TTN_BW_NONE, | ||||||
|     /** |     /** | ||||||
|      * @brief Bandwidth of 125 kHz |      * @brief Bandwidth of 125 kHz | ||||||
|      */ |      */ | ||||||
|     kTTNBW125 = 1, |     kTTNBW125 = TTN_BW125, | ||||||
|     /** |     /** | ||||||
|      * @brief Bandwidth of 250 kHz |      * @brief Bandwidth of 250 kHz | ||||||
|      */ |      */ | ||||||
|     kTTNBW250 = 2, |     kTTNBW250 = TTN_BW250, | ||||||
|     /** |     /** | ||||||
|      * @brief Bandwidth of 500 kHz |      * @brief Bandwidth of 500 kHz | ||||||
|      */ |      */ | ||||||
|     kTTNBW500 = 3 |     kTTNBW500 = TTN_BW500 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -167,12 +160,12 @@ public: | |||||||
|     /** |     /** | ||||||
|      * @brief Constructs a new The Things Network device instance. |      * @brief Constructs a new The Things Network device instance. | ||||||
|      */ |      */ | ||||||
|     TheThingsNetwork(); |     TheThingsNetwork() { ttn_init(); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Destroys the The Things Network device instance. |      * @brief Destroys the The Things Network device instance. | ||||||
|      */ |      */ | ||||||
|     ~TheThingsNetwork(); |     ~TheThingsNetwork() { } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Resets the LoRaWAN radio. |      * @brief Resets the LoRaWAN radio. | ||||||
| @ -180,7 +173,7 @@ public: | |||||||
|      * To restart communication, join() must be called. |      * To restart communication, join() must be called. | ||||||
|      * It neither clears the provisioned keys nor the configured pins. |      * It neither clears the provisioned keys nor the configured pins. | ||||||
|      */ |      */ | ||||||
|     void reset(); |     void reset() { ttn_reset(); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Configures the pins used to communicate with the LoRaWAN radio chip. |      * @brief Configures the pins used to communicate with the LoRaWAN radio chip. | ||||||
| @ -195,7 +188,10 @@ public: | |||||||
|      * @param dio0      The GPIO pin number connected to the radio chip's DIO0 pin |      * @param dio0      The GPIO pin number connected to the radio chip's DIO0 pin | ||||||
|      * @param dio1      The GPIO pin number connected to the radio chip's DIO1 pin |      * @param dio1      The GPIO pin number connected to the radio chip's DIO1 pin | ||||||
|      */ |      */ | ||||||
|     void configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1); |     void configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1) | ||||||
|  |     { | ||||||
|  |         ttn_configure_pins(spi_host, nss, rxtx, rst, dio0, dio1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Sets the credentials needed to activate the device via OTAA, without activating it. |      * @brief Sets the credentials needed to activate the device via OTAA, without activating it. | ||||||
| @ -210,7 +206,7 @@ public: | |||||||
|      * @param appKey  App Key of the device (32 character string with hexadecimal data) |      * @param appKey  App Key of the device (32 character string with hexadecimal data) | ||||||
|      * @return `true` if the provisioning was successful, `false`  if the provisioning failed |      * @return `true` if the provisioning was successful, `false`  if the provisioning failed | ||||||
|      */ |      */ | ||||||
|     bool provision(const char *devEui, const char *appEui, const char *appKey); |     bool provision(const char *devEui, const char *appEui, const char *appKey) { return ttn_provision(devEui, appEui, appKey); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Sets the information needed to activate the device via OTAA, using the MAC to generate the device EUI |      * @brief Sets the information needed to activate the device via OTAA, using the MAC to generate the device EUI | ||||||
| @ -234,14 +230,14 @@ public: | |||||||
|      * @param appKey  App Key of the device (32 character string with hexadecimal data) |      * @param appKey  App Key of the device (32 character string with hexadecimal data) | ||||||
|      * @return `true` if the provisioning was successful, `false`  if the provisioning failed |      * @return `true` if the provisioning was successful, `false`  if the provisioning failed | ||||||
|      */ |      */ | ||||||
|     bool provisionWithMAC(const char *appEui, const char *appKey); |     bool provisionWithMAC(const char *appEui, const char *appKey) { return ttn_provision_with_mac(appEui, appKey); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Starts task listening on configured UART for AT commands. |      * @brief Starts task listening on configured UART for AT commands. | ||||||
|      *  |      *  | ||||||
|      * Run `make menuconfig` to configure it. |      * Run `make menuconfig` to configure it. | ||||||
|      */ |      */ | ||||||
|     void startProvisioningTask(); |     void startProvisioningTask() { ttn_start_provisioning_task(); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Waits until the device EUI, app EUI and app key have been provisioned |      * @brief Waits until the device EUI, app EUI and app key have been provisioned | ||||||
| @ -251,7 +247,7 @@ public: | |||||||
|      * or call of join(const char*, const char*, const char*), this function |      * or call of join(const char*, const char*, const char*), this function | ||||||
|      * immediately returns. |      * immediately returns. | ||||||
|      */ |      */ | ||||||
|     void waitForProvisioning(); |     void waitForProvisioning() { ttn_wait_for_provisioning(); } | ||||||
|  |  | ||||||
|      /** |      /** | ||||||
|      * @brief Activates the device via OTAA. |      * @brief Activates the device via OTAA. | ||||||
| @ -263,7 +259,7 @@ public: | |||||||
|      *  |      *  | ||||||
|      * @return `true` if the activation was succesful, `false` if the activation failed |      * @return `true` if the activation was succesful, `false` if the activation failed | ||||||
|      */ |      */ | ||||||
|     bool join(); |     bool join() { return ttn_join_provisioned(); } | ||||||
|  |  | ||||||
|    /** |    /** | ||||||
|      * @brief Sets the device EUI, app EUI and app key and activate the device via OTAA. |      * @brief Sets the device EUI, app EUI and app key and activate the device via OTAA. | ||||||
| @ -277,7 +273,7 @@ public: | |||||||
|      * @param appKey  App Key of the device (32 character string with hexadecimal data) |      * @param appKey  App Key of the device (32 character string with hexadecimal data) | ||||||
|      * @return `true` if the activation was succesful, `false` if the activation failed |      * @return `true` if the activation was succesful, `false` if the activation failed | ||||||
|      */ |      */ | ||||||
|     bool join(const char *devEui, const char *appEui, const char *appKey); |     bool join(const char *devEui, const char *appEui, const char *appKey) { return ttn_join(devEui, appEui, appKey); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Transmits a message |      * @brief Transmits a message | ||||||
| @ -292,7 +288,10 @@ public: | |||||||
|      * @param confirm  flag indicating if a confirmation should be requested. Defaults to `false` |      * @param confirm  flag indicating if a confirmation should be requested. Defaults to `false` | ||||||
|      * @return `kTTNSuccessfulTransmission` for successful transmission, `kTTNErrorTransmissionFailed` for failed transmission, `kTTNErrorUnexpected` for unexpected error |      * @return `kTTNSuccessfulTransmission` for successful transmission, `kTTNErrorTransmissionFailed` for failed transmission, `kTTNErrorUnexpected` for unexpected error | ||||||
|      */ |      */ | ||||||
|     TTNResponseCode transmitMessage(const uint8_t *payload, size_t length, port_t port = 1, bool confirm = false); |     TTNResponseCode transmitMessage(const uint8_t *payload, size_t length, port_t port = 1, bool confirm = false) | ||||||
|  |     { | ||||||
|  |         return static_cast<TTNResponseCode>(ttn_transmit_message(payload, length, port, confirm)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Sets the function to be called when a message is received |      * @brief Sets the function to be called when a message is received | ||||||
| @ -308,7 +307,7 @@ public: | |||||||
|      *  |      *  | ||||||
|      * @param callback  the callback function |      * @param callback  the callback function | ||||||
|      */ |      */ | ||||||
|     void onMessage(TTNMessageCallback callback); |     void onMessage(TTNMessageCallback callback) { ttn_on_message(callback); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Checks if device EUI, app EUI and app key have been stored in non-volatile storage |      * @brief Checks if device EUI, app EUI and app key have been stored in non-volatile storage | ||||||
| @ -316,7 +315,7 @@ public: | |||||||
|      *  |      *  | ||||||
|      * @return `true` if they are stored, complete and of the correct size, `false` otherwise |      * @return `true` if they are stored, complete and of the correct size, `false` otherwise | ||||||
|      */ |      */ | ||||||
|     bool isProvisioned(); |     bool isProvisioned() { return ttn_is_provisioned(); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Sets the RSSI calibration value for LBT (Listen Before Talk). |      * @brief Sets the RSSI calibration value for LBT (Listen Before Talk). | ||||||
| @ -327,14 +326,14 @@ public: | |||||||
|      *  |      *  | ||||||
|      * @param rssiCal RSSI calibration value, in dB |      * @param rssiCal RSSI calibration value, in dB | ||||||
|      */ |      */ | ||||||
|     void setRSSICal(int8_t rssiCal); |     void setRSSICal(int8_t rssiCal) { ttn_set_rssi_cal(rssiCal); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns whether Adaptive Data Rate (ADR) is enabled. |      * Returns whether Adaptive Data Rate (ADR) is enabled. | ||||||
|      *  |      *  | ||||||
|      * @return `true` if enabled, `false` if disabled |      * @return `true` if enabled, `false` if disabled | ||||||
|      */ |      */ | ||||||
|     bool adrEnabled(); |     bool adrEnabled() { return ttn_adr_enabled(); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Enables or disabled Adaptive Data Rate (ADR). |      * @brief Enables or disabled Adaptive Data Rate (ADR). | ||||||
| @ -344,7 +343,7 @@ public: | |||||||
|      *  |      *  | ||||||
|      * @param enabled `true` to enable, `false` to disable |      * @param enabled `true` to enable, `false` to disable | ||||||
|      */  |      */  | ||||||
|     void setAdrEnabled(bool enabled); |     void setAdrEnabled(bool enabled) { ttn_set_adr_enabled(enabled); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Stops all activies and shuts down the RF module and the background tasks. |      * @brief Stops all activies and shuts down the RF module and the background tasks. | ||||||
| @ -352,20 +351,20 @@ public: | |||||||
|      * To restart communication, startup() and join() must be called. |      * To restart communication, startup() and join() must be called. | ||||||
|      * it neither clears the provisioned keys nor the configured pins. |      * it neither clears the provisioned keys nor the configured pins. | ||||||
|      */ |      */ | ||||||
|     void shutdown(); |     void shutdown() { ttn_shutdown(); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Restarts the background tasks and RF module. |      * @brief Restarts the background tasks and RF module. | ||||||
|      *  |      *  | ||||||
|      * This member function must only be called after a call to shutdowna(). |      * This member function must only be called after a call to shutdowna(). | ||||||
|      */ |      */ | ||||||
|     void startup(); |     void startup() { ttn_startup(); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Gets current RX/TX window |      * @brief Gets current RX/TX window | ||||||
|      * @return window |      * @return window | ||||||
|      */ |      */ | ||||||
|     TTNRxTxWindow rxTxWindow(); |     TTNRxTxWindow rxTxWindow() { return static_cast<TTNRxTxWindow>(ttn_rx_tx_window()); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Gets the RF settings for the specified window |      * @brief Gets the RF settings for the specified window | ||||||
| @ -377,19 +376,19 @@ public: | |||||||
|      * @brief Gets the RF settings of the last (or ongoing) transmission. |      * @brief Gets the RF settings of the last (or ongoing) transmission. | ||||||
|      * @return RF settings |      * @return RF settings | ||||||
|      */ |      */ | ||||||
|     TTNRFSettings txSettings(); |     TTNRFSettings txSettings() { return getRFSettings(kTTNTxWindow); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Gets the RF settings of the last (or ongoing) reception of RX window 1. |      * @brief Gets the RF settings of the last (or ongoing) reception of RX window 1. | ||||||
|      * @return RF settings |      * @return RF settings | ||||||
|      */ |      */ | ||||||
|     TTNRFSettings rx1Settings(); |     TTNRFSettings rx1Settings() { return getRFSettings(kTTNRx1Window); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Gets the RF settings of the last (or ongoing) reception of RX window 2. |      * @brief Gets the RF settings of the last (or ongoing) reception of RX window 2. | ||||||
|      * @return RF settings |      * @return RF settings | ||||||
|      */ |      */ | ||||||
|     TTNRFSettings rx2Settings(); |     TTNRFSettings rx2Settings() { return getRFSettings(kTTNRx2Window); } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @brief Gets the received signal strength indicator (RSSI). |      * @brief Gets the received signal strength indicator (RSSI). | ||||||
| @ -398,12 +397,7 @@ public: | |||||||
|      *  |      *  | ||||||
|      * @return RSSI, in dBm |      * @return RSSI, in dBm | ||||||
|      */ |      */ | ||||||
|     int rssi(); |     int rssi() { return ttn_rssi(); } | ||||||
|  |  | ||||||
| private: |  | ||||||
|     TTNMessageCallback messageCallback; |  | ||||||
|  |  | ||||||
|     bool joinCore(); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  | |||||||
							
								
								
									
										400
									
								
								include/ttn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								include/ttn.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,400 @@ | |||||||
|  | /******************************************************************************* | ||||||
|  |  *  | ||||||
|  |  * 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 | ||||||
|  |  * | ||||||
|  |  * High-level C API for ttn-esp32. | ||||||
|  |  *******************************************************************************/ | ||||||
|  |  | ||||||
|  | #ifndef TTN_C_H | ||||||
|  | #define TTN_C_H | ||||||
|  |  | ||||||
|  | #include <stdint.h> | ||||||
|  | #include "driver/spi_master.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Constant for indicating that a pin is not connected | ||||||
|  |  */ | ||||||
|  | #define TTN_NOT_CONNECTED 0xff | ||||||
|  |  | ||||||
|  |  | ||||||
|  | typedef uint8_t ttn_port_t; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Response codes | ||||||
|  |  */ | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |     TTN_ERROR_TRANSMISSION_FAILED = -1, | ||||||
|  |     TTN_ERROR_UNEXPECTED = -10, | ||||||
|  |     TTN_SUCCESSFUL_TRANSMISSION = 1, | ||||||
|  |     TTN_SUCCESSFUL_RECEIVE = 2 | ||||||
|  | } ttn_response_code_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief RX/TX window | ||||||
|  |  */ | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * @brief Outside RX/TX window | ||||||
|  |      */ | ||||||
|  |     TTN_WINDOW_IDLE = 0, | ||||||
|  |     /** | ||||||
|  |      * @brief Transmission window (up to RX1 window) | ||||||
|  |      */ | ||||||
|  |     TTN_WINDOW_TX = 1, | ||||||
|  |     /** | ||||||
|  |      * @brief Reception window 1 (up to RX2 window) | ||||||
|  |      */ | ||||||
|  |     TTN_WINDOW_RX1 = 2, | ||||||
|  |     /** | ||||||
|  |      * @brief Reception window 2 | ||||||
|  |      */ | ||||||
|  |     TTN_WINDOW_RX2 = 3 | ||||||
|  | } ttn_rx_tx_window_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Spreading Factor | ||||||
|  |  */ | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * @brief Unused / undefined spreading factor | ||||||
|  |      */ | ||||||
|  |     TTN_SF_NONE = 0, | ||||||
|  |     /** | ||||||
|  |      * @brief Frequency Shift Keying (FSK) | ||||||
|  |      */ | ||||||
|  |     TTN_FSK = 1, | ||||||
|  |     /** | ||||||
|  |      * @brief Spreading Factor 7 (SF7) | ||||||
|  |      */ | ||||||
|  |     TTN_SF7 = 2, | ||||||
|  |     /** | ||||||
|  |      * @brief Spreading Factor 8 (SF8) | ||||||
|  |      */ | ||||||
|  |     TTN_SF8 = 3, | ||||||
|  |     /** | ||||||
|  |      * @brief Spreading Factor 9 (SF9) | ||||||
|  |      */ | ||||||
|  |     TTN_SF9 = 4, | ||||||
|  |     /** | ||||||
|  |      * @brief Spreading Factor 10 (SF10) | ||||||
|  |      */ | ||||||
|  |     TTN_SF10 = 5, | ||||||
|  |     /** | ||||||
|  |      * @brief Spreading Factor 11 (SF11) | ||||||
|  |      */ | ||||||
|  |     TTN_SF11 = 6, | ||||||
|  |     /** | ||||||
|  |      * @brief Spreading Factor 12 (SF12) | ||||||
|  |      */ | ||||||
|  |     TTN_SF12 = 7 | ||||||
|  | } ttn_spreading_factor_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Bandwidth | ||||||
|  |  */ | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * @brief Undefined/unused bandwidth | ||||||
|  |      */ | ||||||
|  |     TTN_BW_NONE = 0, | ||||||
|  |     /** | ||||||
|  |      * @brief Bandwidth of 125 kHz | ||||||
|  |      */ | ||||||
|  |     TTN_BW125 = 1, | ||||||
|  |     /** | ||||||
|  |      * @brief Bandwidth of 250 kHz | ||||||
|  |      */ | ||||||
|  |     TTN_BW250 = 2, | ||||||
|  |     /** | ||||||
|  |      * @brief Bandwidth of 500 kHz | ||||||
|  |      */ | ||||||
|  |     TTN_BW500 = 3 | ||||||
|  | } ttn_bandwidth_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief RF settings for TX or RX | ||||||
|  |  */ | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * @brief Spreading Factor (SF) | ||||||
|  |      */ | ||||||
|  |     ttn_spreading_factor_t spreading_factor; | ||||||
|  |     /** | ||||||
|  |      * @brief Bandwidth (BW) | ||||||
|  |      */ | ||||||
|  |     ttn_bandwidth_t bandwidth; | ||||||
|  |     /** | ||||||
|  |      * @brief Frequency, in Hz | ||||||
|  |      */ | ||||||
|  |     uint32_t frequency; | ||||||
|  | } ttn_rf_settings_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Callback for recieved messages | ||||||
|  |  *  | ||||||
|  |  * @param payload  pointer to the received bytes | ||||||
|  |  * @param length   number of received bytes | ||||||
|  |  * @param port     port the message was received on | ||||||
|  |  */ | ||||||
|  | typedef void (*ttn_message_cb)(const uint8_t* payload, size_t length, ttn_port_t port); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Constructs a new The Things Network device instance. | ||||||
|  |  */ | ||||||
|  | void ttn_init(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Resets the LoRaWAN radio. | ||||||
|  |  *  | ||||||
|  |  * To restart communication, join() must be called. | ||||||
|  |  * It neither clears the provisioned keys nor the configured pins. | ||||||
|  |  */ | ||||||
|  | void ttn_reset(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Configures the pins used to communicate with the LoRaWAN radio chip. | ||||||
|  |  *  | ||||||
|  |  * Before calling this member function, the SPI bus needs to be configured using `spi_bus_initialize()`.  | ||||||
|  |  * Additionally, `gpio_install_isr_service()` must have been called to initialize the GPIO ISR handler service. | ||||||
|  |  *  | ||||||
|  |  * @param spi_host  The SPI bus/peripherial to use (`SPI_HOST`, `HSPI_HOST` or `VSPI_HOST`). | ||||||
|  |  * @param nss       The GPIO pin number connected to the radio chip's NSS pin (serving as the SPI chip select) | ||||||
|  |  * @param rxtx      The GPIO pin number connected to the radio chip's RXTX pin (`TTN_NOT_CONNECTED` if not connected) | ||||||
|  |  * @param rst       The GPIO pin number connected to the radio chip's RST pin (`TTN_NOT_CONNECTED` if not connected) | ||||||
|  |  * @param dio0      The GPIO pin number connected to the radio chip's DIO0 pin | ||||||
|  |  * @param dio1      The GPIO pin number connected to the radio chip's DIO1 pin | ||||||
|  |  */ | ||||||
|  | void ttn_configure_pins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Sets the credentials needed to activate the device via OTAA, without activating it. | ||||||
|  |  *  | ||||||
|  |  * The provided device EUI, app EUI and app key are saved in non-volatile memory. Before | ||||||
|  |  * this function is called, `nvs_flash_init()` must have been called once. | ||||||
|  |  *  | ||||||
|  |  * Call join() to activate the device. | ||||||
|  |  *  | ||||||
|  |  * @param dev_eui  Device EUI (16 character string with hexadecimal data) | ||||||
|  |  * @param app_eui  Application EUI of the device (16 character string with hexadecimal data) | ||||||
|  |  * @param app_key  App Key of the device (32 character string with hexadecimal data) | ||||||
|  |  * @return `true` if the provisioning was successful, `false`  if the provisioning failed | ||||||
|  |  */ | ||||||
|  | bool ttn_provision(const char *dev_eui, const char *app_eui, const char *app_key); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Sets the information needed to activate the device via OTAA, using the MAC to generate the device EUI | ||||||
|  |  * and without activating it. | ||||||
|  |  *  | ||||||
|  |  * The generated device EUI and the provided app EUI and app key are saved in non-volatile memory. Before | ||||||
|  |  * this function is called, 'nvs_flash_init' must have been called once. | ||||||
|  |  *  | ||||||
|  |  * The device EUI is generated by retrieving the ESP32's WiFi MAC address and expanding it into a device EUI | ||||||
|  |  * by adding FFFE in the middle. So the MAC address A0:B1:C2:01:02:03 becomes the EUI A0B1C2FFFE010203. | ||||||
|  |  * This hexadecimal data can be entered into the Device EUI field in the TTN console. | ||||||
|  |  *  | ||||||
|  |  * Generating the device EUI from the MAC address allows to flash the same app EUI and app key to a batch of | ||||||
|  |  * devices. However, using the same app key for multiple devices is insecure. Only use this approach if | ||||||
|  |  * it is okay for that the LoRa communication of your application can easily be intercepted and that | ||||||
|  |  * forged data can be injected. | ||||||
|  |  *  | ||||||
|  |  * Call join() to activate. | ||||||
|  |  *  | ||||||
|  |  * @param app_eui  Application EUI of the device (16 character string with hexadecimal data) | ||||||
|  |  * @param app_key  App Key of the device (32 character string with hexadecimal data) | ||||||
|  |  * @return `true` if the provisioning was successful, `false`  if the provisioning failed | ||||||
|  |  */ | ||||||
|  | bool ttn_provision_with_mac(const char *app_eui, const char *app_key); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Starts task listening on configured UART for AT commands. | ||||||
|  |  *  | ||||||
|  |  * Run `make menuconfig` to configure it. | ||||||
|  |  */ | ||||||
|  | void ttn_start_provisioning_task(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Waits until the device EUI, app EUI and app key have been provisioned | ||||||
|  |  * by the provisioning task. | ||||||
|  |  *  | ||||||
|  |  * If the device has already been provisioned (stored data in NVS, call of provision() | ||||||
|  |  * or call of join(const char*, const char*, const char*), this function | ||||||
|  |  * immediately returns. | ||||||
|  |  */ | ||||||
|  | void ttn_wait_for_provisioning(void); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |  * @brief Activates the device via OTAA. | ||||||
|  |  *  | ||||||
|  |  * The app EUI, app key and dev EUI must have already been provisioned by a call to provision(). | ||||||
|  |  * Before this function is called, `nvs_flash_init()` must have been called once. | ||||||
|  |  *  | ||||||
|  |  * The function blocks until the activation has completed or failed. | ||||||
|  |  *  | ||||||
|  |  * @return `true` if the activation was succesful, `false` if the activation failed | ||||||
|  |  */ | ||||||
|  | bool ttn_join_provisioned(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Sets the device EUI, app EUI and app key and activate the device via OTAA. | ||||||
|  |  *  | ||||||
|  |  * The device EUI, app EUI and app key are NOT saved in non-volatile memory. | ||||||
|  |  *  | ||||||
|  |  * The function blocks until the activation has completed or failed. | ||||||
|  |  *  | ||||||
|  |  * @param dev_eui  Device EUI (16 character string with hexadecimal data) | ||||||
|  |  * @param app_eui  Application EUI of the device (16 character string with hexadecimal data) | ||||||
|  |  * @param app_key  App Key of the device (32 character string with hexadecimal data) | ||||||
|  |  * @return `true` if the activation was succesful, `false` if the activation failed | ||||||
|  |  */ | ||||||
|  | bool ttn_join(const char *dev_eui, const char *app_eui, const char *app_key); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Transmits a message | ||||||
|  |  *  | ||||||
|  |  * The function blocks until the message could be transmitted and a message has been received | ||||||
|  |  * in the subsequent receive window (or the window expires). Additionally, the function will | ||||||
|  |  * first wait until the duty cycle allows a transmission (enforcing the duty cycle limits). | ||||||
|  |  *  | ||||||
|  |  * @param payload  bytes to be transmitted | ||||||
|  |  * @param length   number of bytes to be transmitted | ||||||
|  |  * @param port     port (use 1 as default) | ||||||
|  |  * @param confirm  flag indicating if a confirmation should be requested (use `false` as default) | ||||||
|  |  * @return `kTTNSuccessfulTransmission` for successful transmission, `kTTNErrorTransmissionFailed` for failed transmission, `kTTNErrorUnexpected` for unexpected error | ||||||
|  |  */ | ||||||
|  | ttn_response_code_t ttn_transmit_message(const uint8_t *payload, size_t length, ttn_port_t port, bool confirm); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Sets the function to be called when a message is received | ||||||
|  |  *  | ||||||
|  |  * When a message is received, the specified function is called. The | ||||||
|  |  * message, its length and the port number are provided as | ||||||
|  |  * parameters. The values are only valid during the duration of the | ||||||
|  |  * callback. So they must be immediately processed or copied. | ||||||
|  |  *  | ||||||
|  |  * Messages are received as a result of transmitMessage(). The callback is called | ||||||
|  |  * in the task that called any of these functions and it occurs before these functions | ||||||
|  |  * return control to the caller. | ||||||
|  |  *  | ||||||
|  |  * @param callback  the callback function | ||||||
|  |  */ | ||||||
|  | void ttn_on_message(ttn_message_cb callback); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Checks if device EUI, app EUI and app key have been stored in non-volatile storage | ||||||
|  |  * or have been provided as by a call to join(const char*, const char*, const char*). | ||||||
|  |  *  | ||||||
|  |  * @return `true` if they are stored, complete and of the correct size, `false` otherwise | ||||||
|  |  */ | ||||||
|  | bool ttn_is_provisioned(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Sets the RSSI calibration value for LBT (Listen Before Talk). | ||||||
|  |  *  | ||||||
|  |  * This value is added to RSSI measured prior to decision. It must include the guardband. | ||||||
|  |  * Ignored in US, EU, IN and other countries where LBT is not required. | ||||||
|  |  * Defaults to 10 dB. | ||||||
|  |  *  | ||||||
|  |  * @param rssi_cal RSSI calibration value, in dB | ||||||
|  |  */ | ||||||
|  | void ttn_set_rssi_cal(int8_t rssi_cal); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Returns whether Adaptive Data Rate (ADR) is enabled. | ||||||
|  |  *  | ||||||
|  |  * @return `true` if enabled, `false` if disabled | ||||||
|  |  */ | ||||||
|  | bool ttn_adr_enabled(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Enables or disabled Adaptive Data Rate (ADR). | ||||||
|  |  *  | ||||||
|  |  * ADR is enabled by default. It optimizes data rate, airtime and energy consumption | ||||||
|  |  * for devices with stable RF conditions. It should be turned off for mobile devices. | ||||||
|  |  *  | ||||||
|  |  * @param enabled `true` to enable, `false` to disable | ||||||
|  |  */  | ||||||
|  | void ttn_set_adr_enabled(bool enabled); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Stops all activies and shuts down the RF module and the background tasks. | ||||||
|  |  *  | ||||||
|  |  * To restart communication, startup() and join() must be called. | ||||||
|  |  * it neither clears the provisioned keys nor the configured pins. | ||||||
|  |  */ | ||||||
|  | void ttn_shutdown(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Restarts the background tasks and RF module. | ||||||
|  |  *  | ||||||
|  |  * This member function must only be called after a call to shutdowna(). | ||||||
|  |  */ | ||||||
|  | void ttn_startup(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Gets current RX/TX window | ||||||
|  |  * @return window | ||||||
|  |  */ | ||||||
|  | ttn_rx_tx_window_t ttn_rx_tx_window(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Gets the RF settings for the specified window | ||||||
|  |  * @param window RX/TX windows (valid values are `kTTNTxWindow`, `kTTNRx1Window` and `kTTNRx2Window`) | ||||||
|  |  */ | ||||||
|  | ttn_rf_settings_t ttn_get_rf_settings(ttn_rx_tx_window_t window); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Gets the RF settings of the last (or ongoing) transmission. | ||||||
|  |  * @return RF settings | ||||||
|  |  */ | ||||||
|  | ttn_rf_settings_t ttn_tx_settings(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Gets the RF settings of the last (or ongoing) reception of RX window 1. | ||||||
|  |  * @return RF settings | ||||||
|  |  */ | ||||||
|  | ttn_rf_settings_t ttn_rx1_settings(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Gets the RF settings of the last (or ongoing) reception of RX window 2. | ||||||
|  |  * @return RF settings | ||||||
|  |  */ | ||||||
|  | ttn_rf_settings_t ttn_rx2_settings(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Gets the received signal strength indicator (RSSI). | ||||||
|  |  *  | ||||||
|  |  * RSSI is the measured signal strength of the last recevied message (incl. join responses). | ||||||
|  |  *  | ||||||
|  |  * @return RSSI, in dBm | ||||||
|  |  */ | ||||||
|  | int ttn_rssi(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @ -2,422 +2,23 @@ | |||||||
|  *  |  *  | ||||||
|  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x |  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x | ||||||
|  *  |  *  | ||||||
|  * Copyright (c) 2018-2019 Manuel Bleichenbacher |  * Copyright (c) 2018-2021 Manuel Bleichenbacher | ||||||
|  *  |  *  | ||||||
|  * Licensed under MIT License |  * Licensed under MIT License | ||||||
|  * https://opensource.org/licenses/MIT |  * https://opensource.org/licenses/MIT | ||||||
|  * |  * | ||||||
|  * High-level API for ttn-esp32. |  * High-level C++ API for ttn-esp32. | ||||||
|  *******************************************************************************/ |  *******************************************************************************/ | ||||||
|  |  | ||||||
| #include "freertos/FreeRTOS.h" |  | ||||||
| #include "esp_event.h" |  | ||||||
| #include "esp_log.h" |  | ||||||
| #include "hal/hal_esp32.h" |  | ||||||
| #include "lmic/lmic.h" |  | ||||||
| #include "TheThingsNetwork.h" | #include "TheThingsNetwork.h" | ||||||
| #include "ttn_provisioning.h" |  | ||||||
| #include "ttn_logging.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @brief Reason the user code is waiting |  | ||||||
|  */ |  | ||||||
| enum TTNWaitingReason |  | ||||||
| { |  | ||||||
|     eWaitingNone, |  | ||||||
|     eWaitingForJoin, |  | ||||||
|     eWaitingForTransmission |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @brief Event type |  | ||||||
|  */ |  | ||||||
| enum TTNEvent { |  | ||||||
|     eEvtNone, |  | ||||||
|     eEvtJoinCompleted, |  | ||||||
|     eEvtJoinFailed, |  | ||||||
|     eEvtMessageReceived, |  | ||||||
|     eEvtTransmissionCompleted, |  | ||||||
|     eEvtTransmissionFailed |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @brief Event message sent from LMIC task to waiting client task |  | ||||||
|  */ |  | ||||||
| struct TTNLmicEvent { |  | ||||||
|     TTNLmicEvent(TTNEvent ev = eEvtNone): event(ev) { } |  | ||||||
|  |  | ||||||
|     TTNEvent event; |  | ||||||
|     uint8_t port; |  | ||||||
|     const uint8_t* message; |  | ||||||
|     size_t messageSize; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static const char *TAG = "ttn"; |  | ||||||
|  |  | ||||||
| static TheThingsNetwork* ttnInstance; |  | ||||||
| static QueueHandle_t lmicEventQueue = nullptr; |  | ||||||
| static TTNWaitingReason waitingReason = eWaitingNone; |  | ||||||
| static TTNRFSettings lastRfSettings[4]; |  | ||||||
| static TTNRxTxWindow currentWindow; |  | ||||||
|  |  | ||||||
| static void eventCallback(void* userData, ev_t event); |  | ||||||
| static void messageReceivedCallback(void *userData, uint8_t port, const uint8_t *message, size_t messageSize); |  | ||||||
| static void messageTransmittedCallback(void *userData, int success); |  | ||||||
| static void saveRFSettings(TTNRFSettings& rfSettings); |  | ||||||
| static void clearRFSettings(TTNRFSettings& rfSettings); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| TheThingsNetwork::TheThingsNetwork() |  | ||||||
|     : messageCallback(nullptr) |  | ||||||
| { |  | ||||||
| #if defined(TTN_IS_DISABLED) |  | ||||||
|     ESP_LOGE(TAG, "TTN is disabled. Configure a frequency plan using 'make menuconfig'"); |  | ||||||
|     ASSERT(0); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     ASSERT(ttnInstance == nullptr); |  | ||||||
|     ttnInstance = this; |  | ||||||
|     hal_esp32_init_critical_section(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TheThingsNetwork::~TheThingsNetwork() |  | ||||||
| { |  | ||||||
|     // nothing to do |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1) |  | ||||||
| { |  | ||||||
|     hal_esp32_configure_pins(spi_host, nss, rxtx, rst, dio0, dio1); |  | ||||||
|  |  | ||||||
| #if LMIC_ENABLE_event_logging |  | ||||||
|     ttn_log_init(); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     LMIC_registerEventCb(eventCallback, nullptr); |  | ||||||
|     LMIC_registerRxMessageCb(messageReceivedCallback, nullptr); |  | ||||||
|  |  | ||||||
|     os_init_ex(nullptr); |  | ||||||
|     reset(); |  | ||||||
|  |  | ||||||
|     lmicEventQueue = xQueueCreate(4, sizeof(TTNLmicEvent)); |  | ||||||
|     ASSERT(lmicEventQueue != nullptr); |  | ||||||
|     hal_esp32_start_lmic_task(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::reset() |  | ||||||
| { |  | ||||||
|     hal_esp32_enter_critical_section(); |  | ||||||
|     LMIC_reset(); |  | ||||||
|     LMIC_setClockError(MAX_CLOCK_ERROR * 4 / 100); |  | ||||||
|     waitingReason = eWaitingNone; |  | ||||||
|     hal_esp32_leave_critical_section(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::shutdown() |  | ||||||
| { |  | ||||||
|     hal_esp32_enter_critical_section(); |  | ||||||
|     LMIC_shutdown(); |  | ||||||
|     hal_esp32_stop_lmic_task(); |  | ||||||
|     waitingReason = eWaitingNone; |  | ||||||
|     hal_esp32_leave_critical_section(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::startup() |  | ||||||
| { |  | ||||||
|     hal_esp32_enter_critical_section(); |  | ||||||
|     LMIC_reset(); |  | ||||||
|     hal_esp32_start_lmic_task(); |  | ||||||
|     hal_esp32_leave_critical_section(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey) |  | ||||||
| { |  | ||||||
|     if (!ttn_provisioning_decode_keys(devEui, appEui, appKey)) |  | ||||||
|         return false; |  | ||||||
|      |  | ||||||
|     return ttn_provisioning_save_keys(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool TheThingsNetwork::provisionWithMAC(const char *appEui, const char *appKey) |  | ||||||
| { |  | ||||||
|     if (!ttn_provisioning_from_mac(appEui, appKey)) |  | ||||||
|         return false; |  | ||||||
|      |  | ||||||
|     return ttn_provisioning_save_keys(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::startProvisioningTask() |  | ||||||
| { |  | ||||||
| #if defined(TTN_HAS_AT_COMMANDS) |  | ||||||
|     ttn_provisioning_start_task(); |  | ||||||
| #else |  | ||||||
|     ESP_LOGE(TAG, "AT commands are disabled. Change the configuration using 'make menuconfig'"); |  | ||||||
|     ASSERT(0); |  | ||||||
|     esp_restart(); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::waitForProvisioning() |  | ||||||
| { |  | ||||||
| #if defined(TTN_HAS_AT_COMMANDS) |  | ||||||
|     if (isProvisioned()) |  | ||||||
|     { |  | ||||||
|         ESP_LOGI(TAG, "Device is already provisioned"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (!ttn_provisioning_have_keys()) |  | ||||||
|         vTaskDelay(pdMS_TO_TICKS(1000)); |  | ||||||
|  |  | ||||||
|     ESP_LOGI(TAG, "Device successfully provisioned"); |  | ||||||
| #else |  | ||||||
|     ESP_LOGE(TAG, "AT commands are disabled. Change the configuration using 'make menuconfig'"); |  | ||||||
|     ASSERT(0); |  | ||||||
|     esp_restart(); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char *appKey) |  | ||||||
| { |  | ||||||
|     if (!ttn_provisioning_decode_keys(devEui, appEui, appKey)) |  | ||||||
|         return false; |  | ||||||
|      |  | ||||||
|     return joinCore(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool TheThingsNetwork::join() |  | ||||||
| { |  | ||||||
|     if (!ttn_provisioning_have_keys()) |  | ||||||
|     { |  | ||||||
|         if (!ttn_provisioning_restore_keys(false)) |  | ||||||
|             return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return joinCore(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool TheThingsNetwork::joinCore() |  | ||||||
| { |  | ||||||
|     if (!ttn_provisioning_have_keys()) |  | ||||||
|     { |  | ||||||
|         ESP_LOGW(TAG, "Device EUI, App EUI and/or App key have not been provided"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     hal_esp32_enter_critical_section(); |  | ||||||
|     xQueueReset(lmicEventQueue); |  | ||||||
|     waitingReason = eWaitingForJoin; |  | ||||||
|     LMIC_startJoining(); |  | ||||||
|     hal_esp32_wake_up(); |  | ||||||
|     hal_esp32_leave_critical_section(); |  | ||||||
|  |  | ||||||
|     TTNLmicEvent event; |  | ||||||
|     xQueueReceive(lmicEventQueue, &event, portMAX_DELAY); |  | ||||||
|     return event.event == eEvtJoinCompleted; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t length, port_t port, bool confirm) |  | ||||||
| { |  | ||||||
|     hal_esp32_enter_critical_section(); |  | ||||||
|     if (waitingReason != eWaitingNone || (LMIC.opmode & OP_TXRXPEND) != 0) |  | ||||||
|     { |  | ||||||
|         hal_esp32_leave_critical_section(); |  | ||||||
|         return kTTNErrorTransmissionFailed; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     waitingReason = eWaitingForTransmission; |  | ||||||
|     LMIC.client.txMessageCb = messageTransmittedCallback; |  | ||||||
|     LMIC.client.txMessageUserData = nullptr; |  | ||||||
|     LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm); |  | ||||||
|     hal_esp32_wake_up(); |  | ||||||
|     hal_esp32_leave_critical_section(); |  | ||||||
|  |  | ||||||
|     while (true) |  | ||||||
|     { |  | ||||||
|         TTNLmicEvent result; |  | ||||||
|         xQueueReceive(lmicEventQueue, &result, portMAX_DELAY); |  | ||||||
|  |  | ||||||
|         switch (result.event) |  | ||||||
|         { |  | ||||||
|             case eEvtMessageReceived: |  | ||||||
|                 if (messageCallback != nullptr) |  | ||||||
|                     messageCallback(result.message, result.messageSize, result.port); |  | ||||||
|                 break; |  | ||||||
|  |  | ||||||
|             case eEvtTransmissionCompleted: |  | ||||||
|                 return kTTNSuccessfulTransmission; |  | ||||||
|  |  | ||||||
|             case eEvtTransmissionFailed: |  | ||||||
|                 return kTTNErrorTransmissionFailed; |  | ||||||
|  |  | ||||||
|             default: |  | ||||||
|                 ASSERT(0); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::onMessage(TTNMessageCallback callback) |  | ||||||
| { |  | ||||||
|     messageCallback = callback; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| bool TheThingsNetwork::isProvisioned() |  | ||||||
| { |  | ||||||
|     if (ttn_provisioning_have_keys()) |  | ||||||
|         return true; |  | ||||||
|      |  | ||||||
|     ttn_provisioning_restore_keys(true); |  | ||||||
|  |  | ||||||
|     return ttn_provisioning_have_keys(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::setRSSICal(int8_t rssiCal) |  | ||||||
| { |  | ||||||
|     hal_esp32_set_rssi_cal(rssiCal); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool TheThingsNetwork::adrEnabled() |  | ||||||
| { |  | ||||||
|     return LMIC.adrEnabled != 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void TheThingsNetwork::setAdrEnabled(bool enabled) |  | ||||||
| { |  | ||||||
|     LMIC_setAdrMode(enabled); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TTNRFSettings TheThingsNetwork::getRFSettings(TTNRxTxWindow window) | TTNRFSettings TheThingsNetwork::getRFSettings(TTNRxTxWindow window) | ||||||
| { | { | ||||||
|     int index = static_cast<int>(window) & 0x03; |     ttn_rf_settings_t settings = ttn_get_rf_settings(static_cast<ttn_rx_tx_window_t>(window)); | ||||||
|     return lastRfSettings[index]; |     TTNRFSettings result; | ||||||
| } |     result.spreadingFactor = static_cast<TTNSpreadingFactor>(settings.spreading_factor); | ||||||
|  |     result.bandwidth = static_cast<TTNBandwidth>(settings.bandwidth); | ||||||
| TTNRFSettings TheThingsNetwork::txSettings() |     result.frequency = settings.frequency; | ||||||
| { |     return result; | ||||||
|     return lastRfSettings[static_cast<int>(kTTNTxWindow)]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TTNRFSettings TheThingsNetwork::rx1Settings() |  | ||||||
| { |  | ||||||
|     return lastRfSettings[static_cast<int>(kTTNRx1Window)]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TTNRFSettings TheThingsNetwork::rx2Settings() |  | ||||||
| { |  | ||||||
|     return lastRfSettings[static_cast<int>(kTTNRx2Window)]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TTNRxTxWindow TheThingsNetwork::rxTxWindow() |  | ||||||
| { |  | ||||||
|     return currentWindow; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int TheThingsNetwork::rssi() |  | ||||||
| { |  | ||||||
|     return LMIC.rssi; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // --- Callbacks --- |  | ||||||
|  |  | ||||||
| #if CONFIG_LOG_DEFAULT_LEVEL >= 3 || LMIC_ENABLE_event_logging |  | ||||||
| const char *eventNames[] = { LMIC_EVENT_NAME_TABLE__INIT }; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Called by LMIC when an LMIC event (join, join failed, reset etc.) occurs |  | ||||||
| void eventCallback(void* userData, ev_t event) |  | ||||||
| { |  | ||||||
|     // update monitoring information |  | ||||||
|     switch(event) |  | ||||||
|     { |  | ||||||
|         case EV_TXSTART: |  | ||||||
|             currentWindow = kTTNTxWindow; |  | ||||||
|             saveRFSettings(lastRfSettings[static_cast<int>(kTTNTxWindow)]); |  | ||||||
|             clearRFSettings(lastRfSettings[static_cast<int>(kTTNRx1Window)]); |  | ||||||
|             clearRFSettings(lastRfSettings[static_cast<int>(kTTNRx2Window)]); |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case EV_RXSTART: |  | ||||||
|             if (currentWindow != kTTNRx1Window) |  | ||||||
|             { |  | ||||||
|                 currentWindow = kTTNRx1Window; |  | ||||||
|                 saveRFSettings(lastRfSettings[static_cast<int>(kTTNRx1Window)]); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 currentWindow = kTTNRx2Window; |  | ||||||
|                 saveRFSettings(lastRfSettings[static_cast<int>(kTTNRx2Window)]); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         default: |  | ||||||
|             currentWindow = kTTNIdleWindow; |  | ||||||
|             break; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
| #if LMIC_ENABLE_event_logging |  | ||||||
|     ttn_log_event(event, eventNames[event], 0); |  | ||||||
| #elif CONFIG_LOG_DEFAULT_LEVEL >= 3 |  | ||||||
|     ESP_LOGI(TAG, "event %s", eventNames[event]); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     TTNEvent ttnEvent = eEvtNone; |  | ||||||
|  |  | ||||||
|     if (waitingReason == eWaitingForJoin) |  | ||||||
|     { |  | ||||||
|         if (event == EV_JOINED) |  | ||||||
|         { |  | ||||||
|             ttnEvent = eEvtJoinCompleted; |  | ||||||
|         } |  | ||||||
|         else if (event == EV_REJOIN_FAILED || event == EV_RESET) |  | ||||||
|         { |  | ||||||
|             ttnEvent = eEvtJoinFailed; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (ttnEvent == eEvtNone) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     TTNLmicEvent result(ttnEvent); |  | ||||||
|     waitingReason = eWaitingNone; |  | ||||||
|     xQueueSend(lmicEventQueue, &result, pdMS_TO_TICKS(100)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Called by LMIC when a message has been received |  | ||||||
| void messageReceivedCallback(void *userData, uint8_t port, const uint8_t *message, size_t nMessage) |  | ||||||
| { |  | ||||||
|     TTNLmicEvent result(eEvtMessageReceived); |  | ||||||
|     result.port = port; |  | ||||||
|     result.message = message; |  | ||||||
|     result.messageSize = nMessage; |  | ||||||
|     xQueueSend(lmicEventQueue, &result, pdMS_TO_TICKS(100)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Called by LMIC when a message has been transmitted (or the transmission failed) |  | ||||||
| void messageTransmittedCallback(void *userData, int success) |  | ||||||
| { |  | ||||||
|     waitingReason = eWaitingNone; |  | ||||||
|     TTNLmicEvent result(success ? eEvtTransmissionCompleted : eEvtTransmissionFailed); |  | ||||||
|     xQueueSend(lmicEventQueue, &result, pdMS_TO_TICKS(100)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // --- Helpers |  | ||||||
|  |  | ||||||
|  |  | ||||||
| void saveRFSettings(TTNRFSettings& rfSettings) |  | ||||||
| { |  | ||||||
|     rfSettings.spreadingFactor = static_cast<TTNSpreadingFactor>(getSf(LMIC.rps) + 1); |  | ||||||
|     rfSettings.bandwidth = static_cast<TTNBandwidth>(getBw(LMIC.rps) + 1); |  | ||||||
|     rfSettings.frequency = LMIC.freq; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void clearRFSettings(TTNRFSettings& rfSettings) |  | ||||||
| { |  | ||||||
|     memset(&rfSettings, 0, sizeof(rfSettings)); |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										422
									
								
								src/ttn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										422
									
								
								src/ttn.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,422 @@ | |||||||
|  | /******************************************************************************* | ||||||
|  |  *  | ||||||
|  |  * 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 | ||||||
|  |  * | ||||||
|  |  * High-level C API for ttn-esp32. | ||||||
|  |  *******************************************************************************/ | ||||||
|  |  | ||||||
|  | #include "lmic/lmic.h" | ||||||
|  | #include "ttn.h" | ||||||
|  | #include "ttn_provisioning.h" | ||||||
|  | #include "ttn_logging.h" | ||||||
|  | #include "hal/hal_esp32.h" | ||||||
|  | #include "freertos/FreeRTOS.h" | ||||||
|  | #include "esp_event.h" | ||||||
|  | #include "esp_log.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define TAG "ttn" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Reason the user code is waiting | ||||||
|  |  */ | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |     TTN_WAITING_NONE, | ||||||
|  |     TTN_WAITING_FOR_JOIN, | ||||||
|  |     TTN_WAITING_FOR_TRANSMISSION | ||||||
|  | } ttn_waiting_reason_t; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Event type | ||||||
|  |  */ | ||||||
|  | typedef enum { | ||||||
|  |     TTN_EVENT_NONE, | ||||||
|  |     TTN_EVNT_JOIN_COMPLETED, | ||||||
|  |     TTN_EVENT_JOIN_FAILED, | ||||||
|  |     TTN_EVENT_MESSAGE_RECEIVED, | ||||||
|  |     TTN_EVENT_TRANSMISSION_COMPLETED, | ||||||
|  |     TTN_EVENT_TRANSMISSION_FAILED | ||||||
|  | } ttn_event_t; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @brief Event message sent from LMIC task to waiting client task | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     ttn_event_t event; | ||||||
|  |     uint8_t port; | ||||||
|  |     const uint8_t* message; | ||||||
|  |     size_t message_size; | ||||||
|  | } ttn_lmic_event_t; | ||||||
|  |  | ||||||
|  | static QueueHandle_t lmic_event_queue; | ||||||
|  | static ttn_message_cb message_callback; | ||||||
|  | static ttn_waiting_reason_t waiting_reason = TTN_WAITING_NONE; | ||||||
|  | static ttn_rf_settings_t last_rf_settings[4]; | ||||||
|  | static ttn_rx_tx_window_t current_rx_tx_window; | ||||||
|  |  | ||||||
|  | static bool join_core(void); | ||||||
|  | static void event_callback(void* user_data, ev_t event); | ||||||
|  | static void message_received_callback(void *user_data, uint8_t port, const uint8_t *message, size_t message_size); | ||||||
|  | static void message_transmitted_callback(void *user_data, int success); | ||||||
|  | static void save_rf_settings(ttn_rf_settings_t* rf_settings); | ||||||
|  | static void clear_rf_settings(ttn_rf_settings_t* rf_settings); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void ttn_init(void) | ||||||
|  | { | ||||||
|  | #if defined(TTN_IS_DISABLED) | ||||||
|  |     ESP_LOGE(TAG, "TTN is disabled. Configure a frequency plan using 'make menuconfig'"); | ||||||
|  |     ASSERT(0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     message_callback = NULL; | ||||||
|  |     hal_esp32_init_critical_section(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_configure_pins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1) | ||||||
|  | { | ||||||
|  |     hal_esp32_configure_pins(spi_host, nss, rxtx, rst, dio0, dio1); | ||||||
|  |  | ||||||
|  | #if LMIC_ENABLE_event_logging | ||||||
|  |     ttn_log_init(); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     LMIC_registerEventCb(event_callback, NULL); | ||||||
|  |     LMIC_registerRxMessageCb(message_received_callback, NULL); | ||||||
|  |  | ||||||
|  |     os_init_ex(NULL); | ||||||
|  |     ttn_reset(); | ||||||
|  |  | ||||||
|  |     lmic_event_queue = xQueueCreate(4, sizeof(ttn_lmic_event_t)); | ||||||
|  |     ASSERT(lmic_event_queue != NULL); | ||||||
|  |     hal_esp32_start_lmic_task(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_reset(void) | ||||||
|  | { | ||||||
|  |     hal_esp32_enter_critical_section(); | ||||||
|  |     LMIC_reset(); | ||||||
|  |     LMIC_setClockError(MAX_CLOCK_ERROR * 4 / 100); | ||||||
|  |     waiting_reason = TTN_WAITING_NONE; | ||||||
|  |     hal_esp32_leave_critical_section(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_shutdown(void) | ||||||
|  | { | ||||||
|  |     hal_esp32_enter_critical_section(); | ||||||
|  |     LMIC_shutdown(); | ||||||
|  |     hal_esp32_stop_lmic_task(); | ||||||
|  |     waiting_reason = TTN_WAITING_NONE; | ||||||
|  |     hal_esp32_leave_critical_section(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_startup(void) | ||||||
|  | { | ||||||
|  |     hal_esp32_enter_critical_section(); | ||||||
|  |     LMIC_reset(); | ||||||
|  |     hal_esp32_start_lmic_task(); | ||||||
|  |     hal_esp32_leave_critical_section(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ttn_provision(const char *dev_eui, const char *app_eui, const char *app_key) | ||||||
|  | { | ||||||
|  |     if (!ttn_provisioning_decode_keys(dev_eui, app_eui, app_key)) | ||||||
|  |         return false; | ||||||
|  |      | ||||||
|  |     return ttn_provisioning_save_keys(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ttn_provision_with_mac(const char *app_eui, const char *app_key) | ||||||
|  | { | ||||||
|  |     if (!ttn_provisioning_from_mac(app_eui, app_key)) | ||||||
|  |         return false; | ||||||
|  |      | ||||||
|  |     return ttn_provisioning_save_keys(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void ttn_start_provisioning_task(void) | ||||||
|  | { | ||||||
|  | #if defined(TTN_HAS_AT_COMMANDS) | ||||||
|  |     ttn_provisioning_start_task(); | ||||||
|  | #else | ||||||
|  |     ESP_LOGE(TAG, "AT commands are disabled. Change the configuration using 'make menuconfig'"); | ||||||
|  |     ASSERT(0); | ||||||
|  |     esp_restart(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_wait_for_provisioning(void) | ||||||
|  | { | ||||||
|  | #if defined(TTN_HAS_AT_COMMANDS) | ||||||
|  |     if (ttn_is_provisioned()) | ||||||
|  |     { | ||||||
|  |         ESP_LOGI(TAG, "Device is already provisioned"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (!ttn_provisioning_have_keys()) | ||||||
|  |         vTaskDelay(pdMS_TO_TICKS(1000)); | ||||||
|  |  | ||||||
|  |     ESP_LOGI(TAG, "Device successfully provisioned"); | ||||||
|  | #else | ||||||
|  |     ESP_LOGE(TAG, "AT commands are disabled. Change the configuration using 'make menuconfig'"); | ||||||
|  |     ASSERT(0); | ||||||
|  |     esp_restart(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ttn_join(const char *dev_eui, const char *app_eui, const char *app_key) | ||||||
|  | { | ||||||
|  |     if (!ttn_provisioning_decode_keys(dev_eui, app_eui, app_key)) | ||||||
|  |         return false; | ||||||
|  |      | ||||||
|  |     return join_core(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ttn_join_provisioned(void) | ||||||
|  | { | ||||||
|  |     if (!ttn_provisioning_have_keys()) | ||||||
|  |     { | ||||||
|  |         if (!ttn_provisioning_restore_keys(false)) | ||||||
|  |             return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return join_core(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool join_core() | ||||||
|  | { | ||||||
|  |     if (!ttn_provisioning_have_keys()) | ||||||
|  |     { | ||||||
|  |         ESP_LOGW(TAG, "Device EUI, App EUI and/or App key have not been provided"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hal_esp32_enter_critical_section(); | ||||||
|  |     xQueueReset(lmic_event_queue); | ||||||
|  |     waiting_reason = TTN_WAITING_FOR_JOIN; | ||||||
|  |     LMIC_startJoining(); | ||||||
|  |     hal_esp32_wake_up(); | ||||||
|  |     hal_esp32_leave_critical_section(); | ||||||
|  |  | ||||||
|  |     ttn_lmic_event_t event; | ||||||
|  |     xQueueReceive(lmic_event_queue, &event, portMAX_DELAY); | ||||||
|  |     return event.event == TTN_EVNT_JOIN_COMPLETED; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ttn_response_code_t ttn_transmit_message(const uint8_t *payload, size_t length, ttn_port_t port, bool confirm) | ||||||
|  | { | ||||||
|  |     hal_esp32_enter_critical_section(); | ||||||
|  |     if (waiting_reason != TTN_WAITING_NONE || (LMIC.opmode & OP_TXRXPEND) != 0) | ||||||
|  |     { | ||||||
|  |         hal_esp32_leave_critical_section(); | ||||||
|  |         return TTN_ERROR_TRANSMISSION_FAILED; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     waiting_reason = TTN_WAITING_FOR_TRANSMISSION; | ||||||
|  |     LMIC.client.txMessageCb = message_transmitted_callback; | ||||||
|  |     LMIC.client.txMessageUserData = NULL; | ||||||
|  |     LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm); | ||||||
|  |     hal_esp32_wake_up(); | ||||||
|  |     hal_esp32_leave_critical_section(); | ||||||
|  |  | ||||||
|  |     while (true) | ||||||
|  |     { | ||||||
|  |         ttn_lmic_event_t result; | ||||||
|  |         xQueueReceive(lmic_event_queue, &result, portMAX_DELAY); | ||||||
|  |  | ||||||
|  |         switch (result.event) | ||||||
|  |         { | ||||||
|  |             case TTN_EVENT_MESSAGE_RECEIVED: | ||||||
|  |                 if (message_callback != NULL) | ||||||
|  |                     message_callback(result.message, result.message_size, result.port); | ||||||
|  |                 break; | ||||||
|  |  | ||||||
|  |             case TTN_EVENT_TRANSMISSION_COMPLETED: | ||||||
|  |                 return TTN_SUCCESSFUL_TRANSMISSION; | ||||||
|  |  | ||||||
|  |             case TTN_EVENT_TRANSMISSION_FAILED: | ||||||
|  |                 return TTN_ERROR_TRANSMISSION_FAILED; | ||||||
|  |  | ||||||
|  |             default: | ||||||
|  |                 ASSERT(0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_on_message(ttn_message_cb callback) | ||||||
|  | { | ||||||
|  |     message_callback = callback; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | bool ttn_is_provisioned(void) | ||||||
|  | { | ||||||
|  |     if (ttn_provisioning_have_keys()) | ||||||
|  |         return true; | ||||||
|  |      | ||||||
|  |     ttn_provisioning_restore_keys(true); | ||||||
|  |  | ||||||
|  |     return ttn_provisioning_have_keys(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_set_rssi_cal(int8_t rssi_cal) | ||||||
|  | { | ||||||
|  |     hal_esp32_set_rssi_cal(rssi_cal); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ttn_adr_enabled(void) | ||||||
|  | { | ||||||
|  |     return LMIC.adrEnabled != 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ttn_set_adr_nabled(bool enabled) | ||||||
|  | { | ||||||
|  |     LMIC_setAdrMode(enabled); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ttn_rf_settings_t ttn_getrf_settings(ttn_rx_tx_window_t window) | ||||||
|  | { | ||||||
|  |     int index = ((int)window) & 0x03; | ||||||
|  |     return last_rf_settings[index]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ttn_rf_settings_t ttn_tx_settings(void) | ||||||
|  | { | ||||||
|  |     return last_rf_settings[TTN_WINDOW_TX]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ttn_rf_settings_t ttn_rx1_settings(void) | ||||||
|  | { | ||||||
|  |     return last_rf_settings[TTN_WINDOW_RX1]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ttn_rf_settings_t ttn_rx2_settings(void) | ||||||
|  | { | ||||||
|  |     return last_rf_settings[TTN_WINDOW_RX2]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ttn_rx_tx_window_t ttn_rx_tx_window(void) | ||||||
|  | { | ||||||
|  |     return current_rx_tx_window; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ttn_rssi(void) | ||||||
|  | { | ||||||
|  |     return LMIC.rssi; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // --- Callbacks --- | ||||||
|  |  | ||||||
|  | #if CONFIG_LOG_DEFAULT_LEVEL >= 3 || LMIC_ENABLE_event_logging | ||||||
|  | static const char *event_names[] = { LMIC_EVENT_NAME_TABLE__INIT }; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Called by LMIC when an LMIC event (join, join failed, reset etc.) occurs | ||||||
|  | void event_callback(void* user_data, ev_t event) | ||||||
|  | { | ||||||
|  |     // update monitoring information | ||||||
|  |     switch(event) | ||||||
|  |     { | ||||||
|  |         case EV_TXSTART: | ||||||
|  |             current_rx_tx_window = TTN_WINDOW_TX; | ||||||
|  |             save_rf_settings(&last_rf_settings[TTN_WINDOW_TX]); | ||||||
|  |             clear_rf_settings(&last_rf_settings[TTN_WINDOW_RX1]); | ||||||
|  |             clear_rf_settings(&last_rf_settings[TTN_WINDOW_RX2]); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case EV_RXSTART: | ||||||
|  |             if (current_rx_tx_window != TTN_WINDOW_RX1) | ||||||
|  |             { | ||||||
|  |                 current_rx_tx_window = TTN_WINDOW_RX1; | ||||||
|  |                 save_rf_settings(&last_rf_settings[TTN_WINDOW_RX1]); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 current_rx_tx_window = TTN_WINDOW_RX2; | ||||||
|  |                 save_rf_settings(&last_rf_settings[TTN_WINDOW_RX2]); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             current_rx_tx_window = TTN_WINDOW_IDLE; | ||||||
|  |             break; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  | #if LMIC_ENABLE_event_logging | ||||||
|  |     ttn_log_event(event, event_names[event], 0); | ||||||
|  | #elif CONFIG_LOG_DEFAULT_LEVEL >= 3 | ||||||
|  |     ESP_LOGI(TAG, "event %s", event_names[event]); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     ttn_event_t ttn_event = TTN_EVENT_NONE; | ||||||
|  |  | ||||||
|  |     if (waiting_reason == TTN_WAITING_FOR_JOIN) | ||||||
|  |     { | ||||||
|  |         if (event == EV_JOINED) | ||||||
|  |         { | ||||||
|  |             ttn_event = TTN_EVNT_JOIN_COMPLETED; | ||||||
|  |         } | ||||||
|  |         else if (event == EV_REJOIN_FAILED || event == EV_RESET) | ||||||
|  |         { | ||||||
|  |             ttn_event = TTN_EVENT_JOIN_FAILED; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ttn_event == TTN_EVENT_NONE) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     ttn_lmic_event_t result = { | ||||||
|  |         .event = ttn_event | ||||||
|  |     }; | ||||||
|  |     waiting_reason = TTN_WAITING_NONE; | ||||||
|  |     xQueueSend(lmic_event_queue, &result, pdMS_TO_TICKS(100)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Called by LMIC when a message has been received | ||||||
|  | void message_received_callback(void *user_data, uint8_t port, const uint8_t *message, size_t message_size) | ||||||
|  | { | ||||||
|  |     ttn_lmic_event_t result = { | ||||||
|  |         .event = TTN_EVENT_MESSAGE_RECEIVED, | ||||||
|  |         .port = port, | ||||||
|  |         .message = message, | ||||||
|  |         .message_size = message_size | ||||||
|  |     }; | ||||||
|  |     xQueueSend(lmic_event_queue, &result, pdMS_TO_TICKS(100)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Called by LMIC when a message has been transmitted (or the transmission failed) | ||||||
|  | void message_transmitted_callback(void *user_data, int success) | ||||||
|  | { | ||||||
|  |     waiting_reason = TTN_WAITING_NONE; | ||||||
|  |     ttn_lmic_event_t result = { | ||||||
|  |         .event = success ? TTN_EVENT_TRANSMISSION_COMPLETED : TTN_EVENT_TRANSMISSION_FAILED | ||||||
|  |     }; | ||||||
|  |     xQueueSend(lmic_event_queue, &result, pdMS_TO_TICKS(100)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // --- Helpers | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void save_rf_settings(ttn_rf_settings_t* rf_settings) | ||||||
|  | { | ||||||
|  |     rf_settings->spreading_factor = (ttn_spreading_factor_t)(getSf(LMIC.rps) + 1); | ||||||
|  |     rf_settings->bandwidth = (ttn_bandwidth_t)(getBw(LMIC.rps) + 1); | ||||||
|  |     rf_settings->frequency = LMIC.freq; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void clear_rf_settings(ttn_rf_settings_t* rf_settings) | ||||||
|  | { | ||||||
|  |     memset(rf_settings, 0, sizeof(*rf_settings)); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user