mirror of
https://github.com/manuelbl/ttn-esp32.git
synced 2025-06-14 20:14:27 +02:00
Convert hal_esp32 to C
This commit is contained in:
parent
973a7c41c8
commit
8e2886db27
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -6,6 +6,10 @@
|
||||
"oslmic.h": "c",
|
||||
"hal_esp32.h": "c",
|
||||
"esp_log.h": "c",
|
||||
"nvs_flash.h": "c"
|
||||
"nvs_flash.h": "c",
|
||||
"__config": "c",
|
||||
"__nullptr": "c",
|
||||
"stdint.h": "c",
|
||||
"*.ipp": "c"
|
||||
}
|
||||
}
|
@ -82,7 +82,7 @@ TheThingsNetwork::TheThingsNetwork()
|
||||
|
||||
ASSERT(ttnInstance == nullptr);
|
||||
ttnInstance = this;
|
||||
ttn_hal.initCriticalSection();
|
||||
hal_esp32_init_critical_section();
|
||||
}
|
||||
|
||||
TheThingsNetwork::~TheThingsNetwork()
|
||||
@ -92,7 +92,7 @@ TheThingsNetwork::~TheThingsNetwork()
|
||||
|
||||
void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1)
|
||||
{
|
||||
ttn_hal.configurePins(spi_host, nss, rxtx, rst, dio0, dio1);
|
||||
hal_esp32_configure_pins(spi_host, nss, rxtx, rst, dio0, dio1);
|
||||
|
||||
#if LMIC_ENABLE_event_logging
|
||||
logging = TTNLogging::initInstance();
|
||||
@ -106,33 +106,33 @@ void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, ui
|
||||
|
||||
lmicEventQueue = xQueueCreate(4, sizeof(TTNLmicEvent));
|
||||
ASSERT(lmicEventQueue != nullptr);
|
||||
ttn_hal.startLMICTask();
|
||||
hal_esp32_start_lmic_task();
|
||||
}
|
||||
|
||||
void TheThingsNetwork::reset()
|
||||
{
|
||||
ttn_hal.enterCriticalSection();
|
||||
hal_esp32_enter_critical_section();
|
||||
LMIC_reset();
|
||||
LMIC_setClockError(MAX_CLOCK_ERROR * 4 / 100);
|
||||
waitingReason = eWaitingNone;
|
||||
ttn_hal.leaveCriticalSection();
|
||||
hal_esp32_leave_critical_section();
|
||||
}
|
||||
|
||||
void TheThingsNetwork::shutdown()
|
||||
{
|
||||
ttn_hal.enterCriticalSection();
|
||||
hal_esp32_enter_critical_section();
|
||||
LMIC_shutdown();
|
||||
ttn_hal.stopLMICTask();
|
||||
hal_esp32_stop_lmic_task();
|
||||
waitingReason = eWaitingNone;
|
||||
ttn_hal.leaveCriticalSection();
|
||||
hal_esp32_leave_critical_section();
|
||||
}
|
||||
|
||||
void TheThingsNetwork::startup()
|
||||
{
|
||||
ttn_hal.enterCriticalSection();
|
||||
hal_esp32_enter_critical_section();
|
||||
LMIC_reset();
|
||||
ttn_hal.startLMICTask();
|
||||
ttn_hal.leaveCriticalSection();
|
||||
hal_esp32_start_lmic_task();
|
||||
hal_esp32_leave_critical_section();
|
||||
}
|
||||
|
||||
bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey)
|
||||
@ -210,12 +210,12 @@ bool TheThingsNetwork::joinCore()
|
||||
return false;
|
||||
}
|
||||
|
||||
ttn_hal.enterCriticalSection();
|
||||
hal_esp32_enter_critical_section();
|
||||
xQueueReset(lmicEventQueue);
|
||||
waitingReason = eWaitingForJoin;
|
||||
LMIC_startJoining();
|
||||
ttn_hal.wakeUp();
|
||||
ttn_hal.leaveCriticalSection();
|
||||
hal_esp32_wake_up();
|
||||
hal_esp32_leave_critical_section();
|
||||
|
||||
TTNLmicEvent event;
|
||||
xQueueReceive(lmicEventQueue, &event, portMAX_DELAY);
|
||||
@ -224,10 +224,10 @@ bool TheThingsNetwork::joinCore()
|
||||
|
||||
TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t length, port_t port, bool confirm)
|
||||
{
|
||||
ttn_hal.enterCriticalSection();
|
||||
hal_esp32_enter_critical_section();
|
||||
if (waitingReason != eWaitingNone || (LMIC.opmode & OP_TXRXPEND) != 0)
|
||||
{
|
||||
ttn_hal.leaveCriticalSection();
|
||||
hal_esp32_leave_critical_section();
|
||||
return kTTNErrorTransmissionFailed;
|
||||
}
|
||||
|
||||
@ -235,8 +235,8 @@ TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t
|
||||
LMIC.client.txMessageCb = messageTransmittedCallback;
|
||||
LMIC.client.txMessageUserData = nullptr;
|
||||
LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm);
|
||||
ttn_hal.wakeUp();
|
||||
ttn_hal.leaveCriticalSection();
|
||||
hal_esp32_wake_up();
|
||||
hal_esp32_leave_critical_section();
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -280,7 +280,7 @@ bool TheThingsNetwork::isProvisioned()
|
||||
|
||||
void TheThingsNetwork::setRSSICal(int8_t rssiCal)
|
||||
{
|
||||
ttn_hal.rssiCal = rssiCal;
|
||||
hal_esp32_set_rssi_cal(rssiCal);
|
||||
}
|
||||
|
||||
bool TheThingsNetwork::adrEnabled()
|
||||
|
538
src/hal/hal_esp32.c
Executable file
538
src/hal/hal_esp32.c
Executable file
@ -0,0 +1,538 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
|
||||
*
|
||||
* Copyright (c) 2018-2019 Manuel Bleichenbacher
|
||||
*
|
||||
* Licensed under MIT License
|
||||
* https://opensource.org/licenses/MIT
|
||||
*
|
||||
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-IDF.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "hal_esp32.h"
|
||||
#include "../lmic/lmic.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/timer.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define LMIC_UNUSED_PIN 0xff
|
||||
|
||||
#define NOTIFY_BIT_DIO 1
|
||||
#define NOTIFY_BIT_TIMER 2
|
||||
#define NOTIFY_BIT_WAKEUP 4
|
||||
#define NOTIFY_BIT_STOP 8
|
||||
|
||||
|
||||
#define TAG "ttn_hal"
|
||||
|
||||
|
||||
typedef enum {
|
||||
WAIT_KIND_CHECK_IO,
|
||||
WAIT_KIND_WAIT_FOR_ANY_EVENT,
|
||||
WAIT_KIND_WAIT_FOR_TIMER
|
||||
} wait_kind_e;
|
||||
|
||||
|
||||
static void lmic_background_task(void* pvParameter);
|
||||
static void qio_irq_handler(void* arg);
|
||||
static void timer_callback(void *arg);
|
||||
static int64_t os_time_to_esp_time(int64_t esp_now, uint32_t os_time);
|
||||
|
||||
static void init_io(void);
|
||||
static void init_spi(void);
|
||||
static void init_timer(void);
|
||||
|
||||
static void set_next_alarm(int64_t time);
|
||||
static void arm_timer(int64_t esp_now);
|
||||
static void disarm_timer(void);
|
||||
static bool wait(wait_kind_e wait_kind);
|
||||
|
||||
static spi_host_device_t spi_host;
|
||||
static gpio_num_t pin_nss;
|
||||
static gpio_num_t pin_rx_tx;
|
||||
static gpio_num_t pin_rst;
|
||||
static gpio_num_t pin_dio0;
|
||||
static gpio_num_t pin_dio1;
|
||||
static int8_t rssi_cal = 10;
|
||||
|
||||
|
||||
static TaskHandle_t lmic_task;
|
||||
static uint32_t dio_interrupt_time;
|
||||
static uint8_t dio_num;
|
||||
|
||||
static spi_device_handle_t spi_handle;
|
||||
static spi_transaction_t spi_transaction;
|
||||
static SemaphoreHandle_t mutex;
|
||||
static esp_timer_handle_t timer;
|
||||
static int64_t next_alarm;
|
||||
static volatile bool run_background_task;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// I/O
|
||||
|
||||
void hal_esp32_configure_pins(spi_host_device_t host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1)
|
||||
{
|
||||
spi_host = host;
|
||||
pin_nss = (gpio_num_t)nss;
|
||||
pin_rx_tx = (gpio_num_t)rxtx;
|
||||
pin_rst = (gpio_num_t)rst;
|
||||
pin_dio0 = (gpio_num_t)dio0;
|
||||
pin_dio1 = (gpio_num_t)dio1;
|
||||
|
||||
// Until the background process has been started, use the current task
|
||||
// for supporting calls like `hal_waitUntil()`.
|
||||
lmic_task = xTaskGetCurrentTaskHandle();
|
||||
}
|
||||
|
||||
|
||||
void IRAM_ATTR qio_irq_handler(void *arg)
|
||||
{
|
||||
dio_interrupt_time = hal_ticks();
|
||||
dio_num = (u1_t)(long)arg;
|
||||
BaseType_t higher_prio_task_woken = pdFALSE;
|
||||
xTaskNotifyFromISR(lmic_task, NOTIFY_BIT_DIO, eSetBits, &higher_prio_task_woken);
|
||||
if (higher_prio_task_woken)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
void init_io(void)
|
||||
{
|
||||
// pin_nss and pin_dio0 and pin_dio1 are required
|
||||
ASSERT(pin_nss != LMIC_UNUSED_PIN);
|
||||
ASSERT(pin_dio0 != LMIC_UNUSED_PIN);
|
||||
ASSERT(pin_dio1 != LMIC_UNUSED_PIN);
|
||||
|
||||
gpio_pad_select_gpio(pin_nss);
|
||||
gpio_set_level(pin_nss, 0);
|
||||
gpio_set_direction(pin_nss, GPIO_MODE_OUTPUT);
|
||||
|
||||
if (pin_rx_tx != LMIC_UNUSED_PIN)
|
||||
{
|
||||
gpio_pad_select_gpio(pin_rx_tx);
|
||||
gpio_set_level(pin_rx_tx, 0);
|
||||
gpio_set_direction(pin_rx_tx, GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
if (pin_rst != LMIC_UNUSED_PIN)
|
||||
{
|
||||
gpio_pad_select_gpio(pin_rst);
|
||||
gpio_set_level(pin_rst, 0);
|
||||
gpio_set_direction(pin_rst, GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
// DIO pins with interrupt handlers
|
||||
gpio_pad_select_gpio(pin_dio0);
|
||||
gpio_set_direction(pin_dio0, GPIO_MODE_INPUT);
|
||||
gpio_set_intr_type(pin_dio0, GPIO_INTR_POSEDGE);
|
||||
|
||||
gpio_pad_select_gpio(pin_dio1);
|
||||
gpio_set_direction(pin_dio1, GPIO_MODE_INPUT);
|
||||
gpio_set_intr_type(pin_dio1, GPIO_INTR_POSEDGE);
|
||||
|
||||
ESP_LOGI(TAG, "IO initialized");
|
||||
}
|
||||
|
||||
void hal_pin_rxtx(u1_t val)
|
||||
{
|
||||
if (pin_rx_tx == LMIC_UNUSED_PIN)
|
||||
return;
|
||||
|
||||
gpio_set_level(pin_rx_tx, val);
|
||||
}
|
||||
|
||||
void hal_pin_rst(u1_t val)
|
||||
{
|
||||
if (pin_rst == LMIC_UNUSED_PIN)
|
||||
return;
|
||||
|
||||
if (val == 0 || val == 1)
|
||||
{
|
||||
// drive pin
|
||||
gpio_set_level(pin_rst, val);
|
||||
gpio_set_direction(pin_rst, GPIO_MODE_OUTPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(CONFIG_TTN_RESET_STATES_ASSERTED)
|
||||
// drive up the pin because the hardware is nonstandard
|
||||
gpio_set_level(pin_rst, 1);
|
||||
gpio_set_direction(pin_rst, GPIO_MODE_OUTPUT);
|
||||
#else
|
||||
// keep pin floating
|
||||
gpio_set_level(pin_rst, val);
|
||||
gpio_set_direction(pin_rst, GPIO_MODE_INPUT);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
s1_t hal_getRssiCal(void)
|
||||
{
|
||||
return rssi_cal;
|
||||
}
|
||||
|
||||
ostime_t hal_setModuleActive(bit_t val)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bit_t hal_queryUsingTcxo(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t hal_getTxPowerPolicy(u1_t inputPolicy, s1_t requestedPower, u4_t frequency)
|
||||
{
|
||||
return LMICHAL_radio_tx_power_policy_paboost;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SPI
|
||||
|
||||
void init_spi(void)
|
||||
{
|
||||
// init device
|
||||
spi_device_interface_config_t spi_config = {
|
||||
.mode = 1,
|
||||
.clock_speed_hz = CONFIG_TTN_SPI_FREQ,
|
||||
.command_bits = 0,
|
||||
.address_bits = 8,
|
||||
.spics_io_num = pin_nss,
|
||||
.queue_size = 1,
|
||||
.cs_ena_posttrans = 2
|
||||
};
|
||||
|
||||
esp_err_t ret = spi_bus_add_device(spi_host, &spi_config, &spi_handle);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_LOGI(TAG, "SPI initialized");
|
||||
}
|
||||
|
||||
void hal_spi_write(u1_t cmd, const u1_t *buf, size_t len)
|
||||
{
|
||||
memset(&spi_transaction, 0, sizeof(spi_transaction));
|
||||
spi_transaction.addr = cmd;
|
||||
spi_transaction.length = 8 * len;
|
||||
spi_transaction.tx_buffer = buf;
|
||||
esp_err_t err = spi_device_transmit(spi_handle, &spi_transaction);
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
void hal_spi_read(u1_t cmd, u1_t *buf, size_t len)
|
||||
{
|
||||
memset(buf, 0, len);
|
||||
memset(&spi_transaction, 0, sizeof(spi_transaction));
|
||||
spi_transaction.addr = cmd;
|
||||
spi_transaction.length = 8 * len;
|
||||
spi_transaction.rxlength = 8 * len;
|
||||
spi_transaction.tx_buffer = buf;
|
||||
spi_transaction.rx_buffer = buf;
|
||||
esp_err_t err = spi_device_transmit(spi_handle, &spi_transaction);
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// TIME
|
||||
|
||||
/*
|
||||
* LIMIC uses a 32 bit time system (ostime_t) counting ticks. In this
|
||||
* implementation each tick is 16µs. It will wrap arounnd every 19 hours.
|
||||
*
|
||||
* The ESP32 has a 64 bit timer counting microseconds. It will wrap around
|
||||
* every 584,000 years. So we don't need to bother.
|
||||
*
|
||||
* Based on this timer, future callbacks can be scheduled. This is used to
|
||||
* schedule the next LMIC job.
|
||||
*/
|
||||
|
||||
// Convert LMIC tick time (ostime_t) to ESP absolute time.
|
||||
// `os_time` is assumed to be somewhere between one hour in the past and
|
||||
// 18 hours into the future.
|
||||
int64_t os_time_to_esp_time(int64_t esp_now, uint32_t os_time)
|
||||
{
|
||||
int64_t esp_time;
|
||||
uint32_t os_now = (uint32_t)(esp_now >> 4);
|
||||
|
||||
// unsigned difference:
|
||||
// 0x00000000 - 0xefffffff: future (0 to about 18 hours)
|
||||
// 0xf0000000 - 0xffffffff: past (about 1 to 0 hours)
|
||||
uint32_t os_diff = os_time - os_now;
|
||||
if (os_diff < 0xf0000000)
|
||||
{
|
||||
esp_time = esp_now + (((int64_t)os_diff) << 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// one's complement instead of two's complement:
|
||||
// off by 1 µs and ignored
|
||||
os_diff = ~os_diff;
|
||||
esp_time = esp_now - (((int64_t)os_diff) << 4);
|
||||
}
|
||||
|
||||
return esp_time;
|
||||
}
|
||||
|
||||
void init_timer(void)
|
||||
{
|
||||
esp_timer_create_args_t timer_config = {
|
||||
.callback = &timer_callback,
|
||||
.arg = NULL,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "lmic_job"
|
||||
};
|
||||
esp_err_t err = esp_timer_create(&timer_config, &timer);
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
ESP_LOGI(TAG, "Timer initialized");
|
||||
}
|
||||
|
||||
void set_next_alarm(int64_t time)
|
||||
{
|
||||
next_alarm = time;
|
||||
}
|
||||
|
||||
void arm_timer(int64_t esp_now)
|
||||
{
|
||||
if (next_alarm == 0)
|
||||
return;
|
||||
int64_t timeout = next_alarm - esp_timer_get_time();
|
||||
if (timeout < 0)
|
||||
timeout = 10;
|
||||
esp_timer_start_once(timer, timeout);
|
||||
}
|
||||
|
||||
void disarm_timer(void)
|
||||
{
|
||||
esp_timer_stop(timer);
|
||||
}
|
||||
|
||||
void timer_callback(void *arg)
|
||||
{
|
||||
xTaskNotify(lmic_task, NOTIFY_BIT_TIMER, eSetBits);
|
||||
}
|
||||
|
||||
// Wait for the next external event. Either:
|
||||
// - scheduled timer due to scheduled job or waiting for a given time
|
||||
// - wake up event from the client code
|
||||
// - I/O interrupt (DIO0 or DIO1 pin)
|
||||
bool wait(wait_kind_e wait_kind)
|
||||
{
|
||||
TickType_t ticks_to_wait = wait_kind == WAIT_KIND_CHECK_IO ? 0 : portMAX_DELAY;
|
||||
while (true)
|
||||
{
|
||||
uint32_t bits = ulTaskNotifyTake(pdTRUE, ticks_to_wait);
|
||||
if (bits == 0)
|
||||
return false;
|
||||
|
||||
if ((bits & NOTIFY_BIT_STOP) != 0)
|
||||
return false;
|
||||
|
||||
if ((bits & NOTIFY_BIT_WAKEUP) != 0)
|
||||
{
|
||||
if (wait_kind != WAIT_KIND_WAIT_FOR_TIMER)
|
||||
{
|
||||
disarm_timer();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((bits & NOTIFY_BIT_TIMER) != 0)
|
||||
{
|
||||
disarm_timer();
|
||||
set_next_alarm(0);
|
||||
if (wait_kind != WAIT_KIND_CHECK_IO)
|
||||
return true;
|
||||
}
|
||||
else // IO interrupt
|
||||
{
|
||||
if (wait_kind != WAIT_KIND_WAIT_FOR_TIMER)
|
||||
disarm_timer();
|
||||
hal_esp32_enter_critical_section();
|
||||
radio_irq_handler_v2(dio_num, dio_interrupt_time);
|
||||
hal_esp32_leave_critical_section();
|
||||
if (wait_kind != WAIT_KIND_WAIT_FOR_TIMER)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gets current time in LMIC ticks
|
||||
u4_t IRAM_ATTR hal_ticks(void)
|
||||
{
|
||||
// LMIC tick unit: 16µs
|
||||
// esp_timer unit: 1µs
|
||||
return (u4_t)(esp_timer_get_time() >> 4);
|
||||
}
|
||||
|
||||
// Wait until the specified time.
|
||||
// Called if the LMIC code needs to wait for a precise time.
|
||||
// All other events are ignored and will be served later.
|
||||
u4_t hal_waitUntil(u4_t time)
|
||||
{
|
||||
int64_t esp_now = esp_timer_get_time();
|
||||
int64_t esp_time = os_time_to_esp_time(esp_now, time);
|
||||
set_next_alarm(esp_time);
|
||||
arm_timer(esp_now);
|
||||
wait(WAIT_KIND_WAIT_FOR_TIMER);
|
||||
|
||||
u4_t os_now = hal_ticks();
|
||||
u4_t diff = os_now - time;
|
||||
return diff < 0x80000000U ? diff : 0;
|
||||
}
|
||||
|
||||
// Called by client code to wake up LMIC to do something,
|
||||
// e.g. send a submitted messages.
|
||||
void hal_esp32_wake_up(void)
|
||||
{
|
||||
xTaskNotify(lmic_task, NOTIFY_BIT_WAKEUP, eSetBits);
|
||||
}
|
||||
|
||||
// Check if the specified time has been reached or almost reached.
|
||||
// Otherwise, save it as alarm time.
|
||||
// LMIC calls this function with the scheduled time of the next job
|
||||
// in the queue. If the job is not due yet, LMIC will go to sleep.
|
||||
u1_t hal_checkTimer(uint32_t time)
|
||||
{
|
||||
int64_t esp_now = esp_timer_get_time();
|
||||
int64_t esp_time = os_time_to_esp_time(esp_now, time);
|
||||
int64_t diff = esp_time - esp_now;
|
||||
if (diff < 100)
|
||||
return 1; // timer has expired or will expire very soon
|
||||
|
||||
set_next_alarm(esp_time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Go to sleep until next event.
|
||||
// Called when LMIC is not busy and not job is due to be executed.
|
||||
void hal_sleep(void)
|
||||
{
|
||||
if (wait(WAIT_KIND_CHECK_IO))
|
||||
return;
|
||||
|
||||
arm_timer(esp_timer_get_time());
|
||||
wait(WAIT_KIND_WAIT_FOR_ANY_EVENT);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// IRQ
|
||||
|
||||
void hal_disableIRQs(void)
|
||||
{
|
||||
// nothing to do as interrupt handlers post message to queue
|
||||
// and don't access any shared data structures
|
||||
}
|
||||
|
||||
void hal_enableIRQs(void)
|
||||
{
|
||||
// nothing to do as interrupt handlers post message to queue
|
||||
// and don't access any shared data structures
|
||||
}
|
||||
|
||||
void hal_processPendingIRQs(void)
|
||||
{
|
||||
// nothing to do as interrupt handlers post message to queue
|
||||
// and don't access any shared data structures
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Synchronization between application code and background task
|
||||
|
||||
void hal_esp32_init_critical_section(void)
|
||||
{
|
||||
mutex = xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
|
||||
void hal_esp32_enter_critical_section(void)
|
||||
{
|
||||
xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void hal_esp32_leave_critical_section(void)
|
||||
{
|
||||
xSemaphoreGiveRecursive(mutex);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void lmic_background_task(void* pvParameter)
|
||||
{
|
||||
while (run_background_task)
|
||||
os_runloop_once();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void hal_init_ex(const void *pContext)
|
||||
{
|
||||
// configure radio I/O and interrupt handler
|
||||
init_io();
|
||||
// configure radio SPI
|
||||
init_spi();
|
||||
// configure timer and alarm callback
|
||||
init_timer();
|
||||
}
|
||||
|
||||
void hal_esp32_start_lmic_task(void)
|
||||
{
|
||||
run_background_task = true;
|
||||
xTaskCreate(lmic_background_task, "ttn_lmic", 1024 * 4, NULL, CONFIG_TTN_BG_TASK_PRIO, &lmic_task);
|
||||
|
||||
// enable interrupts
|
||||
gpio_isr_handler_add(pin_dio0, qio_irq_handler, (void *)0);
|
||||
gpio_isr_handler_add(pin_dio1, qio_irq_handler, (void *)1);
|
||||
}
|
||||
|
||||
void hal_esp32_stop_lmic_task(void)
|
||||
{
|
||||
run_background_task = false;
|
||||
gpio_isr_handler_remove(pin_dio0);
|
||||
gpio_isr_handler_remove(pin_dio1);
|
||||
disarm_timer();
|
||||
xTaskNotify(lmic_task, NOTIFY_BIT_STOP, eSetBits);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Fatal failure
|
||||
|
||||
static hal_failure_handler_t* custom_hal_failure_handler = NULL;
|
||||
|
||||
void hal_set_failure_handler(const hal_failure_handler_t* const handler)
|
||||
{
|
||||
custom_hal_failure_handler = handler;
|
||||
}
|
||||
|
||||
void hal_failed(const char *file, u2_t line)
|
||||
{
|
||||
if (custom_hal_failure_handler != NULL)
|
||||
(*custom_hal_failure_handler)(file, line);
|
||||
|
||||
ESP_LOGE(TAG, "LMIC failed and stopped: %s:%d", file, line);
|
||||
|
||||
// go to sleep forever
|
||||
while (true)
|
||||
{
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RSSI
|
||||
|
||||
void hal_esp32_set_rssi_cal(int8_t cal)
|
||||
{
|
||||
rssi_cal = cal;
|
||||
}
|
@ -1,529 +0,0 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
|
||||
*
|
||||
* Copyright (c) 2018-2019 Manuel Bleichenbacher
|
||||
*
|
||||
* Licensed under MIT License
|
||||
* https://opensource.org/licenses/MIT
|
||||
*
|
||||
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-IDF.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "../lmic/lmic.h"
|
||||
#include "../hal/hal_esp32.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/timer.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define LMIC_UNUSED_PIN 0xff
|
||||
|
||||
#define NOTIFY_BIT_DIO 1
|
||||
#define NOTIFY_BIT_TIMER 2
|
||||
#define NOTIFY_BIT_WAKEUP 4
|
||||
#define NOTIFY_BIT_STOP 8
|
||||
|
||||
|
||||
static const char* const TAG = "ttn_hal";
|
||||
|
||||
HAL_ESP32 ttn_hal;
|
||||
|
||||
TaskHandle_t HAL_ESP32::lmicTask = nullptr;
|
||||
uint32_t HAL_ESP32::dioInterruptTime = 0;
|
||||
uint8_t HAL_ESP32::dioNum = 0;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
|
||||
HAL_ESP32::HAL_ESP32()
|
||||
: rssiCal(10), nextAlarm(0)
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// I/O
|
||||
|
||||
void HAL_ESP32::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1)
|
||||
{
|
||||
spiHost = spi_host;
|
||||
pinNSS = (gpio_num_t)nss;
|
||||
pinRxTx = (gpio_num_t)rxtx;
|
||||
pinRst = (gpio_num_t)rst;
|
||||
pinDIO0 = (gpio_num_t)dio0;
|
||||
pinDIO1 = (gpio_num_t)dio1;
|
||||
|
||||
// Until the background process has been started, use the current task
|
||||
// for supporting calls like `hal_waitUntil()`.
|
||||
lmicTask = xTaskGetCurrentTaskHandle();
|
||||
}
|
||||
|
||||
|
||||
void IRAM_ATTR HAL_ESP32::dioIrqHandler(void *arg)
|
||||
{
|
||||
dioInterruptTime = hal_ticks();
|
||||
dioNum = (u1_t)(long)arg;
|
||||
BaseType_t higherPrioTaskWoken = pdFALSE;
|
||||
xTaskNotifyFromISR(lmicTask, NOTIFY_BIT_DIO, eSetBits, &higherPrioTaskWoken);
|
||||
if (higherPrioTaskWoken)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
void HAL_ESP32::ioInit()
|
||||
{
|
||||
// pinNSS and pinDIO0 and pinDIO1 are required
|
||||
ASSERT(pinNSS != LMIC_UNUSED_PIN);
|
||||
ASSERT(pinDIO0 != LMIC_UNUSED_PIN);
|
||||
ASSERT(pinDIO1 != LMIC_UNUSED_PIN);
|
||||
|
||||
gpio_pad_select_gpio(pinNSS);
|
||||
gpio_set_level(pinNSS, 0);
|
||||
gpio_set_direction(pinNSS, GPIO_MODE_OUTPUT);
|
||||
|
||||
if (pinRxTx != LMIC_UNUSED_PIN)
|
||||
{
|
||||
gpio_pad_select_gpio(pinRxTx);
|
||||
gpio_set_level(pinRxTx, 0);
|
||||
gpio_set_direction(pinRxTx, GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
if (pinRst != LMIC_UNUSED_PIN)
|
||||
{
|
||||
gpio_pad_select_gpio(pinRst);
|
||||
gpio_set_level(pinRst, 0);
|
||||
gpio_set_direction(pinRst, GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
// DIO pins with interrupt handlers
|
||||
gpio_pad_select_gpio(pinDIO0);
|
||||
gpio_set_direction(pinDIO0, GPIO_MODE_INPUT);
|
||||
gpio_set_intr_type(pinDIO0, GPIO_INTR_POSEDGE);
|
||||
|
||||
gpio_pad_select_gpio(pinDIO1);
|
||||
gpio_set_direction(pinDIO1, GPIO_MODE_INPUT);
|
||||
gpio_set_intr_type(pinDIO1, GPIO_INTR_POSEDGE);
|
||||
|
||||
ESP_LOGI(TAG, "IO initialized");
|
||||
}
|
||||
|
||||
void hal_pin_rxtx(u1_t val)
|
||||
{
|
||||
if (ttn_hal.pinRxTx == LMIC_UNUSED_PIN)
|
||||
return;
|
||||
|
||||
gpio_set_level(ttn_hal.pinRxTx, val);
|
||||
}
|
||||
|
||||
void hal_pin_rst(u1_t val)
|
||||
{
|
||||
if (ttn_hal.pinRst == LMIC_UNUSED_PIN)
|
||||
return;
|
||||
|
||||
if (val == 0 || val == 1)
|
||||
{
|
||||
// drive pin
|
||||
gpio_set_level(ttn_hal.pinRst, val);
|
||||
gpio_set_direction(ttn_hal.pinRst, GPIO_MODE_OUTPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(CONFIG_TTN_RESET_STATES_ASSERTED)
|
||||
// drive up the pin because the hardware is nonstandard
|
||||
gpio_set_level(ttn_hal.pinRst, 1);
|
||||
gpio_set_direction(ttn_hal.pinRst, GPIO_MODE_OUTPUT);
|
||||
#else
|
||||
// keep pin floating
|
||||
gpio_set_level(ttn_hal.pinRst, val);
|
||||
gpio_set_direction(ttn_hal.pinRst, GPIO_MODE_INPUT);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
s1_t hal_getRssiCal (void)
|
||||
{
|
||||
return ttn_hal.rssiCal;
|
||||
}
|
||||
|
||||
ostime_t hal_setModuleActive (bit_t val)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bit_t hal_queryUsingTcxo(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t hal_getTxPowerPolicy(u1_t inputPolicy, s1_t requestedPower, u4_t frequency)
|
||||
{
|
||||
return LMICHAL_radio_tx_power_policy_paboost;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SPI
|
||||
|
||||
void HAL_ESP32::spiInit()
|
||||
{
|
||||
// init device
|
||||
spi_device_interface_config_t spiConfig;
|
||||
memset(&spiConfig, 0, sizeof(spiConfig));
|
||||
spiConfig.mode = 1;
|
||||
spiConfig.clock_speed_hz = CONFIG_TTN_SPI_FREQ;
|
||||
spiConfig.command_bits = 0;
|
||||
spiConfig.address_bits = 8;
|
||||
spiConfig.spics_io_num = pinNSS;
|
||||
spiConfig.queue_size = 1;
|
||||
spiConfig.cs_ena_posttrans = 2;
|
||||
|
||||
esp_err_t ret = spi_bus_add_device(spiHost, &spiConfig, &spiHandle);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_LOGI(TAG, "SPI initialized");
|
||||
}
|
||||
|
||||
void hal_spi_write(u1_t cmd, const u1_t *buf, size_t len)
|
||||
{
|
||||
ttn_hal.spiWrite(cmd, buf, len);
|
||||
}
|
||||
|
||||
void HAL_ESP32::spiWrite(uint8_t cmd, const uint8_t *buf, size_t len)
|
||||
{
|
||||
memset(&spiTransaction, 0, sizeof(spiTransaction));
|
||||
spiTransaction.addr = cmd;
|
||||
spiTransaction.length = 8 * len;
|
||||
spiTransaction.tx_buffer = buf;
|
||||
esp_err_t err = spi_device_transmit(spiHandle, &spiTransaction);
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
void hal_spi_read(u1_t cmd, u1_t *buf, size_t len)
|
||||
{
|
||||
ttn_hal.spiRead(cmd, buf, len);
|
||||
}
|
||||
|
||||
void HAL_ESP32::spiRead(uint8_t cmd, uint8_t *buf, size_t len)
|
||||
{
|
||||
memset(buf, 0, len);
|
||||
memset(&spiTransaction, 0, sizeof(spiTransaction));
|
||||
spiTransaction.addr = cmd;
|
||||
spiTransaction.length = 8 * len;
|
||||
spiTransaction.rxlength = 8 * len;
|
||||
spiTransaction.tx_buffer = buf;
|
||||
spiTransaction.rx_buffer = buf;
|
||||
esp_err_t err = spi_device_transmit(spiHandle, &spiTransaction);
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// TIME
|
||||
|
||||
/*
|
||||
* LIMIC uses a 32 bit time system (ostime_t) counting ticks. In this
|
||||
* implementation each tick is 16µs. It will wrap arounnd every 19 hours.
|
||||
*
|
||||
* The ESP32 has a 64 bit timer counting microseconds. It will wrap around
|
||||
* every 584,000 years. So we don't need to bother.
|
||||
*
|
||||
* Based on this timer, future callbacks can be scheduled. This is used to
|
||||
* schedule the next LMIC job.
|
||||
*/
|
||||
|
||||
// Convert LMIC tick time (ostime_t) to ESP absolute time.
|
||||
// `osTime` is assumed to be somewhere between one hour in the past and
|
||||
// 18 hours into the future.
|
||||
int64_t HAL_ESP32::osTimeToEspTime(int64_t espNow, uint32_t osTime)
|
||||
{
|
||||
int64_t espTime;
|
||||
uint32_t osNow = (uint32_t)(espNow >> 4);
|
||||
|
||||
// unsigned difference:
|
||||
// 0x00000000 - 0xefffffff: future (0 to about 18 hours)
|
||||
// 0xf0000000 - 0xffffffff: past (about 1 to 0 hours)
|
||||
uint32_t osDiff = osTime - osNow;
|
||||
if (osDiff < 0xf0000000)
|
||||
{
|
||||
espTime = espNow + (((int64_t)osDiff) << 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// one's complement instead of two's complement:
|
||||
// off by 1 µs and ignored
|
||||
osDiff = ~osDiff;
|
||||
espTime = espNow - (((int64_t)osDiff) << 4);
|
||||
}
|
||||
|
||||
return espTime;
|
||||
}
|
||||
|
||||
void HAL_ESP32::timerInit()
|
||||
{
|
||||
esp_timer_create_args_t timerConfig = {
|
||||
.callback = &timerCallback,
|
||||
.arg = nullptr,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "lmic_job"
|
||||
};
|
||||
esp_err_t err = esp_timer_create(&timerConfig, &timer);
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
ESP_LOGI(TAG, "Timer initialized");
|
||||
}
|
||||
|
||||
void HAL_ESP32::setNextAlarm(int64_t time)
|
||||
{
|
||||
nextAlarm = time;
|
||||
}
|
||||
|
||||
void HAL_ESP32::armTimer(int64_t espNow)
|
||||
{
|
||||
if (nextAlarm == 0)
|
||||
return;
|
||||
int64_t timeout = nextAlarm - esp_timer_get_time();
|
||||
if (timeout < 0)
|
||||
timeout = 10;
|
||||
esp_timer_start_once(timer, timeout);
|
||||
}
|
||||
|
||||
void HAL_ESP32::disarmTimer()
|
||||
{
|
||||
esp_timer_stop(timer);
|
||||
}
|
||||
|
||||
void HAL_ESP32::timerCallback(void *arg)
|
||||
{
|
||||
xTaskNotify(lmicTask, NOTIFY_BIT_TIMER, eSetBits);
|
||||
}
|
||||
|
||||
// Wait for the next external event. Either:
|
||||
// - scheduled timer due to scheduled job or waiting for a given time
|
||||
// - wake up event from the client code
|
||||
// - I/O interrupt (DIO0 or DIO1 pin)
|
||||
bool HAL_ESP32::wait(WaitKind waitKind)
|
||||
{
|
||||
TickType_t ticksToWait = waitKind == CHECK_IO ? 0 : portMAX_DELAY;
|
||||
while (true)
|
||||
{
|
||||
uint32_t bits = ulTaskNotifyTake(pdTRUE, ticksToWait);
|
||||
if (bits == 0)
|
||||
return false;
|
||||
|
||||
if ((bits & NOTIFY_BIT_STOP) != 0)
|
||||
return false;
|
||||
|
||||
if ((bits & NOTIFY_BIT_WAKEUP) != 0)
|
||||
{
|
||||
if (waitKind != WAIT_FOR_TIMER)
|
||||
{
|
||||
disarmTimer();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((bits & NOTIFY_BIT_TIMER) != 0)
|
||||
{
|
||||
disarmTimer();
|
||||
setNextAlarm(0);
|
||||
if (waitKind != CHECK_IO)
|
||||
return true;
|
||||
}
|
||||
else // IO interrupt
|
||||
{
|
||||
if (waitKind != WAIT_FOR_TIMER)
|
||||
disarmTimer();
|
||||
enterCriticalSection();
|
||||
radio_irq_handler_v2(dioNum, dioInterruptTime);
|
||||
leaveCriticalSection();
|
||||
if (waitKind != WAIT_FOR_TIMER)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gets current time in LMIC ticks
|
||||
u4_t IRAM_ATTR hal_ticks()
|
||||
{
|
||||
// LMIC tick unit: 16µs
|
||||
// esp_timer unit: 1µs
|
||||
return (u4_t)(esp_timer_get_time() >> 4);
|
||||
}
|
||||
|
||||
// Wait until the specified time.
|
||||
// Called if the LMIC code needs to wait for a precise time.
|
||||
// All other events are ignored and will be served later.
|
||||
u4_t hal_waitUntil(u4_t time)
|
||||
{
|
||||
return ttn_hal.waitUntil(time);
|
||||
}
|
||||
|
||||
uint32_t HAL_ESP32::waitUntil(uint32_t osTime)
|
||||
{
|
||||
int64_t espNow = esp_timer_get_time();
|
||||
int64_t espTime = osTimeToEspTime(espNow, osTime);
|
||||
setNextAlarm(espTime);
|
||||
armTimer(espNow);
|
||||
wait(WAIT_FOR_TIMER);
|
||||
|
||||
u4_t osNow = hal_ticks();
|
||||
u4_t diff = osNow - osTime;
|
||||
return diff < 0x80000000U ? diff : 0;
|
||||
}
|
||||
|
||||
// Called by client code to wake up LMIC to do something,
|
||||
// e.g. send a submitted messages.
|
||||
void HAL_ESP32::wakeUp()
|
||||
{
|
||||
xTaskNotify(lmicTask, NOTIFY_BIT_WAKEUP, eSetBits);
|
||||
}
|
||||
|
||||
// Check if the specified time has been reached or almost reached.
|
||||
// Otherwise, save it as alarm time.
|
||||
// LMIC calls this function with the scheduled time of the next job
|
||||
// in the queue. If the job is not due yet, LMIC will go to sleep.
|
||||
u1_t hal_checkTimer(uint32_t time)
|
||||
{
|
||||
return ttn_hal.checkTimer(time);
|
||||
}
|
||||
|
||||
uint8_t HAL_ESP32::checkTimer(u4_t osTime)
|
||||
{
|
||||
int64_t espNow = esp_timer_get_time();
|
||||
int64_t espTime = osTimeToEspTime(espNow, osTime);
|
||||
int64_t diff = espTime - espNow;
|
||||
if (diff < 100)
|
||||
return 1; // timer has expired or will expire very soon
|
||||
|
||||
setNextAlarm(espTime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Go to sleep until next event.
|
||||
// Called when LMIC is not busy and not job is due to be executed.
|
||||
void hal_sleep()
|
||||
{
|
||||
ttn_hal.sleep();
|
||||
}
|
||||
|
||||
void HAL_ESP32::sleep()
|
||||
{
|
||||
if (wait(CHECK_IO))
|
||||
return;
|
||||
|
||||
armTimer(esp_timer_get_time());
|
||||
wait(WAIT_FOR_ANY_EVENT);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// IRQ
|
||||
|
||||
void hal_disableIRQs()
|
||||
{
|
||||
// nothing to do as interrupt handlers post message to queue
|
||||
// and don't access any shared data structures
|
||||
}
|
||||
|
||||
void hal_enableIRQs()
|
||||
{
|
||||
// nothing to do as interrupt handlers post message to queue
|
||||
// and don't access any shared data structures
|
||||
}
|
||||
|
||||
void hal_processPendingIRQs()
|
||||
{
|
||||
// nothing to do as interrupt handlers post message to queue
|
||||
// and don't access any shared data structures
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Synchronization between application code and background task
|
||||
|
||||
void HAL_ESP32::initCriticalSection()
|
||||
{
|
||||
mutex = xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
|
||||
void HAL_ESP32::enterCriticalSection()
|
||||
{
|
||||
xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void HAL_ESP32::leaveCriticalSection()
|
||||
{
|
||||
xSemaphoreGiveRecursive(mutex);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void HAL_ESP32::lmicBackgroundTask(void* pvParameter)
|
||||
{
|
||||
HAL_ESP32* instance = (HAL_ESP32*)pvParameter;
|
||||
while (instance->runBackgroundTask)
|
||||
os_runloop_once();
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
void hal_init_ex(const void *pContext)
|
||||
{
|
||||
ttn_hal.init();
|
||||
}
|
||||
|
||||
void HAL_ESP32::init()
|
||||
{
|
||||
// configure radio I/O and interrupt handler
|
||||
ioInit();
|
||||
// configure radio SPI
|
||||
spiInit();
|
||||
// configure timer and alarm callback
|
||||
timerInit();
|
||||
}
|
||||
|
||||
void HAL_ESP32::startLMICTask()
|
||||
{
|
||||
runBackgroundTask = true;
|
||||
xTaskCreate(lmicBackgroundTask, "ttn_lmic", 1024 * 4, this, CONFIG_TTN_BG_TASK_PRIO, &lmicTask);
|
||||
|
||||
// enable interrupts
|
||||
gpio_isr_handler_add(pinDIO0, dioIrqHandler, (void *)0);
|
||||
gpio_isr_handler_add(pinDIO1, dioIrqHandler, (void *)1);
|
||||
}
|
||||
|
||||
void HAL_ESP32::stopLMICTask()
|
||||
{
|
||||
runBackgroundTask = false;
|
||||
gpio_isr_handler_remove(pinDIO0);
|
||||
gpio_isr_handler_remove(pinDIO1);
|
||||
disarmTimer();
|
||||
xTaskNotify(lmicTask, NOTIFY_BIT_STOP, eSetBits);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Fatal failure
|
||||
|
||||
static hal_failure_handler_t* custom_hal_failure_handler = nullptr;
|
||||
|
||||
void hal_set_failure_handler(const hal_failure_handler_t* const handler)
|
||||
{
|
||||
custom_hal_failure_handler = handler;
|
||||
}
|
||||
|
||||
void hal_failed(const char *file, u2_t line)
|
||||
{
|
||||
if (custom_hal_failure_handler != nullptr)
|
||||
(*custom_hal_failure_handler)(file, line);
|
||||
|
||||
ESP_LOGE(TAG, "LMIC failed and stopped: %s:%d", file, line);
|
||||
|
||||
// go to sleep forever
|
||||
while (true)
|
||||
{
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* 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
|
||||
@ -10,85 +10,33 @@
|
||||
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-IDF.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef _hal_esp32_h_
|
||||
#define _hal_esp32_h_
|
||||
#ifndef HAL_ESP32_H
|
||||
#define HAL_ESP32_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <esp_timer.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "driver/spi_master.h"
|
||||
|
||||
|
||||
enum WaitKind {
|
||||
CHECK_IO,
|
||||
WAIT_FOR_ANY_EVENT,
|
||||
WAIT_FOR_TIMER
|
||||
};
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
void hal_esp32_configure_pins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1);
|
||||
void hal_esp32_start_lmic_task(void);
|
||||
void hal_esp32_stop_lmic_task(void);
|
||||
|
||||
class HAL_ESP32
|
||||
{
|
||||
public:
|
||||
HAL_ESP32();
|
||||
void hal_esp32_wake_up(void);
|
||||
void hal_esp32_init_critical_section(void);
|
||||
void hal_esp32_enter_critical_section(void);
|
||||
void hal_esp32_leave_critical_section(void);
|
||||
|
||||
void configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1);
|
||||
void init();
|
||||
void startLMICTask();
|
||||
void stopLMICTask();
|
||||
|
||||
void wakeUp();
|
||||
void initCriticalSection();
|
||||
void enterCriticalSection();
|
||||
void leaveCriticalSection();
|
||||
|
||||
void spiWrite(uint8_t cmd, const uint8_t *buf, size_t len);
|
||||
void spiRead(uint8_t cmd, uint8_t *buf, size_t len);
|
||||
uint8_t checkTimer(uint32_t osTime);
|
||||
void sleep();
|
||||
|
||||
uint32_t waitUntil(uint32_t osTime);
|
||||
|
||||
spi_host_device_t spiHost;
|
||||
gpio_num_t pinNSS;
|
||||
gpio_num_t pinRxTx;
|
||||
gpio_num_t pinRst;
|
||||
gpio_num_t pinDIO0;
|
||||
gpio_num_t pinDIO1;
|
||||
int8_t rssiCal;
|
||||
|
||||
private:
|
||||
static void lmicBackgroundTask(void* pvParameter);
|
||||
static void dioIrqHandler(void* arg);
|
||||
static void timerCallback(void *arg);
|
||||
static int64_t osTimeToEspTime(int64_t espNow, uint32_t osTime);
|
||||
|
||||
void ioInit();
|
||||
void spiInit();
|
||||
void timerInit();
|
||||
|
||||
void setNextAlarm(int64_t time);
|
||||
void armTimer(int64_t espNow);
|
||||
void disarmTimer();
|
||||
bool wait(WaitKind waitKind);
|
||||
|
||||
static TaskHandle_t lmicTask;
|
||||
static uint32_t dioInterruptTime;
|
||||
static uint8_t dioNum;
|
||||
|
||||
spi_device_handle_t spiHandle;
|
||||
spi_transaction_t spiTransaction;
|
||||
SemaphoreHandle_t mutex;
|
||||
esp_timer_handle_t timer;
|
||||
int64_t nextAlarm;
|
||||
volatile bool runBackgroundTask;
|
||||
};
|
||||
|
||||
extern HAL_ESP32 ttn_hal;
|
||||
void hal_esp32_set_rssi_cal(int8_t rssi_cal);
|
||||
|
||||
|
||||
#endif // _hal_esp32_h_
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // HAL_ESP32_H
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "lmic/lmic.h"
|
||||
//#include "hal/hal_esp32.h"
|
||||
#include "hal/hal_esp32.h"
|
||||
|
||||
#if defined(TTN_HAS_AT_COMMANDS)
|
||||
#define UART_NUM CONFIG_TTN_PROVISION_UART_NUM
|
||||
@ -305,10 +305,9 @@ void process_line(void)
|
||||
|
||||
if (reset_needed)
|
||||
{
|
||||
// TODO
|
||||
// ttn_hal.enterCriticalSection();
|
||||
hal_esp32_enter_critical_section();
|
||||
LMIC_reset();
|
||||
// ttn_hal.leaveCriticalSection();
|
||||
hal_esp32_leave_critical_section();
|
||||
LMIC.client.eventCb(LMIC.client.eventUserData, EV_RESET);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user