mirror of
https://github.com/manuelbl/ttn-esp32.git
synced 2024-09-28 16:13:59 +02:00
253 lines
5.9 KiB
C++
253 lines
5.9 KiB
C++
/*******************************************************************************
|
|
*
|
|
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
|
|
*
|
|
* Copyright (c) 2018 Manuel Bleichenbacher
|
|
*
|
|
* Licensed under MIT License
|
|
* https://opensource.org/licenses/MIT
|
|
*
|
|
* High-level API for ttn-esp32.
|
|
*******************************************************************************/
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "esp_event.h"
|
|
#include "esp_log.h"
|
|
#include "TheThingsNetwork.h"
|
|
#include "hal/hal_esp32.h"
|
|
#include "lmic/lmic.h"
|
|
#include "provisioning.h"
|
|
|
|
|
|
enum ClientAction
|
|
{
|
|
eActionUnrelated,
|
|
eActionJoining,
|
|
eActionSending
|
|
};
|
|
|
|
static const char *TAG = "ttn";
|
|
|
|
static TheThingsNetwork* ttnInstance;
|
|
static QueueHandle_t resultQueue;
|
|
static ClientAction clientAction = eActionUnrelated;
|
|
|
|
|
|
TheThingsNetwork::TheThingsNetwork()
|
|
: messageCallback(NULL)
|
|
{
|
|
#if defined(TTN_IS_DISABLED)
|
|
ESP_LOGE(TAG, "TTN is disabled. Configure a frequency plan using 'make menuconfig'");
|
|
ASSERT(0);
|
|
esp_restart();
|
|
#endif
|
|
|
|
ASSERT(ttnInstance == NULL);
|
|
ttnInstance = this;
|
|
hal_initCriticalSection();
|
|
}
|
|
|
|
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)
|
|
{
|
|
lmic_pins.spi_host = spi_host;
|
|
lmic_pins.nss = nss;
|
|
lmic_pins.rxtx = rxtx;
|
|
lmic_pins.rst = rst;
|
|
lmic_pins.dio0 = dio0;
|
|
lmic_pins.dio1 = dio1;
|
|
|
|
os_init();
|
|
reset();
|
|
|
|
resultQueue = xQueueCreate(12, sizeof(int));
|
|
ASSERT(resultQueue != NULL);
|
|
hal_startBgTask();
|
|
}
|
|
|
|
void TheThingsNetwork::reset()
|
|
{
|
|
hal_enterCriticalSection();
|
|
LMIC_reset();
|
|
hal_leaveCriticalSection();
|
|
}
|
|
|
|
bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey)
|
|
{
|
|
if (!provisioning_decode_keys(devEui, appEui, appKey))
|
|
return false;
|
|
|
|
return provisioning_save_keys();
|
|
}
|
|
|
|
bool TheThingsNetwork::provisionWithMAC(const char *appEui, const char *appKey)
|
|
{
|
|
if (!provisioning_from_mac(appEui, appKey))
|
|
return false;
|
|
|
|
return provisioning_save_keys();
|
|
}
|
|
|
|
void TheThingsNetwork::startProvisioningTask()
|
|
{
|
|
#if !defined(CONFIG_TTN_PROVISION_UART_NONE)
|
|
provisioning_start_task();
|
|
#endif
|
|
}
|
|
|
|
void TheThingsNetwork::waitForProvisioning()
|
|
{
|
|
#if !defined(CONFIG_TTN_PROVISION_UART_NONE)
|
|
if (isProvisioned())
|
|
{
|
|
ESP_LOGI(TAG, "Device is already provisioned");
|
|
return;
|
|
}
|
|
|
|
while (!provisioning_have_keys())
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
|
|
ESP_LOGI(TAG, "Device successfully provisioned");
|
|
#endif
|
|
}
|
|
|
|
bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char *appKey)
|
|
{
|
|
if (!provisioning_decode_keys(devEui, appEui, appKey))
|
|
return false;
|
|
|
|
return joinCore();
|
|
}
|
|
|
|
bool TheThingsNetwork::join()
|
|
{
|
|
if (!provisioning_have_keys())
|
|
{
|
|
if (!provisioning_restore_keys(false))
|
|
return false;
|
|
}
|
|
|
|
return joinCore();
|
|
}
|
|
|
|
bool TheThingsNetwork::joinCore()
|
|
{
|
|
if (!provisioning_have_keys())
|
|
{
|
|
ESP_LOGW(TAG, "Device EUI, App EUI and/or App key have not been provided");
|
|
return false;
|
|
}
|
|
|
|
hal_enterCriticalSection();
|
|
clientAction = eActionJoining;
|
|
LMIC_startJoining();
|
|
hal_wakeUp();
|
|
hal_leaveCriticalSection();
|
|
|
|
int result = 0;
|
|
xQueueReceive(resultQueue, &result, portMAX_DELAY);
|
|
return result == EV_JOINED;
|
|
}
|
|
|
|
TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t length, port_t port, bool confirm)
|
|
{
|
|
hal_enterCriticalSection();
|
|
if (LMIC.opmode & OP_TXRXPEND)
|
|
{
|
|
hal_leaveCriticalSection();
|
|
return kTTNErrorTransmissionFailed;
|
|
}
|
|
|
|
clientAction = eActionSending;
|
|
LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm);
|
|
hal_wakeUp();
|
|
hal_leaveCriticalSection();
|
|
|
|
int result = 0;
|
|
xQueueReceive(resultQueue, &result, portMAX_DELAY);
|
|
|
|
if (result == EV_TXCOMPLETE)
|
|
{
|
|
bool hasRecevied = (LMIC.txrxFlags & (TXRX_DNW1 | TXRX_DNW2)) != 0;
|
|
if (hasRecevied && messageCallback != NULL)
|
|
{
|
|
port_t port = 0;
|
|
if ((LMIC.txrxFlags & TXRX_PORT))
|
|
port = LMIC.frame[LMIC.dataBeg - 1];
|
|
const uint8_t* msg = NULL;
|
|
if (LMIC.dataLen > 0)
|
|
msg = LMIC.frame + LMIC.dataBeg;
|
|
messageCallback(msg, LMIC.dataLen, port);
|
|
}
|
|
|
|
return kTTNSuccessfulTransmission;
|
|
}
|
|
|
|
return kTTNErrorTransmissionFailed;
|
|
}
|
|
|
|
void TheThingsNetwork::onMessage(TTNMessageCallback callback)
|
|
{
|
|
messageCallback = callback;
|
|
}
|
|
|
|
|
|
bool TheThingsNetwork::isProvisioned()
|
|
{
|
|
if (provisioning_have_keys())
|
|
return true;
|
|
|
|
provisioning_restore_keys(true);
|
|
|
|
return provisioning_have_keys();
|
|
}
|
|
|
|
|
|
// --- LMIC functions ---
|
|
|
|
#if CONFIG_LOG_DEFAULT_LEVEL >= 3
|
|
static const char *eventNames[] = {
|
|
NULL,
|
|
"EV_SCAN_TIMEOUT", "EV_BEACON_FOUND",
|
|
"EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING",
|
|
"EV_JOINED", "EV_RFU1", "EV_JOIN_FAILED", "EV_REJOIN_FAILED",
|
|
"EV_TXCOMPLETE", "EV_LOST_TSYNC", "EV_RESET",
|
|
"EV_RXCOMPLETE", "EV_LINK_DEAD", "EV_LINK_ALIVE", "EV_SCAN_FOUND",
|
|
"EV_TXSTART"
|
|
};
|
|
#endif
|
|
|
|
void onEvent (ev_t ev) {
|
|
#if CONFIG_LOG_DEFAULT_LEVEL >= 3
|
|
ESP_LOGI(TAG, "event %s", eventNames[ev]);
|
|
#endif
|
|
|
|
if (ev == EV_TXCOMPLETE) {
|
|
if (LMIC.txrxFlags & TXRX_ACK)
|
|
ESP_LOGI(TAG, "ACK received\n");
|
|
}
|
|
|
|
if (clientAction == eActionUnrelated)
|
|
{
|
|
return;
|
|
}
|
|
else if (clientAction == eActionJoining)
|
|
{
|
|
if (ev != EV_JOINED && ev != EV_REJOIN_FAILED && ev != EV_RESET)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (ev != EV_TXCOMPLETE && ev != EV_LINK_DEAD && ev != EV_RESET)
|
|
return;
|
|
}
|
|
|
|
int result = ev;
|
|
clientAction = eActionUnrelated;
|
|
xQueueSend(resultQueue, &result, 100 / portTICK_PERIOD_MS);
|
|
}
|