mirror of
https://github.com/manuelbl/ttn-esp32.git
synced 2025-08-17 12:10:34 +02:00
C implementation for TheThingsNetwork
This commit is contained in:
@ -2,422 +2,23 @@
|
||||
*
|
||||
* 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
|
||||
* 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 "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)
|
||||
{
|
||||
int index = static_cast<int>(window) & 0x03;
|
||||
return lastRfSettings[index];
|
||||
}
|
||||
|
||||
TTNRFSettings TheThingsNetwork::txSettings()
|
||||
{
|
||||
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));
|
||||
ttn_rf_settings_t settings = ttn_get_rf_settings(static_cast<ttn_rx_tx_window_t>(window));
|
||||
TTNRFSettings result;
|
||||
result.spreadingFactor = static_cast<TTNSpreadingFactor>(settings.spreading_factor);
|
||||
result.bandwidth = static_cast<TTNBandwidth>(settings.bandwidth);
|
||||
result.frequency = settings.frequency;
|
||||
return result;
|
||||
}
|
||||
|
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