mirror of
https://github.com/manuelbl/ttn-esp32.git
synced 2025-06-14 20:14:27 +02:00
C implementation for TheThingsNetwork
This commit is contained in:
parent
36edf92944
commit
281ba52155
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));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user