42 Commits

Author SHA1 Message Date
f70ad2a996 Version 2.3 2020-08-01 18:32:10 +02:00
fdacae73f8 Add rssi() function 2020-08-01 18:30:47 +02:00
9489f34542 RF parameter monitoring 2020-08-01 18:21:42 +02:00
2a31ef0096 Update example build configuration 2020-08-01 10:28:25 +02:00
e15d936bb4 Merge send_recv example into hello_world 2020-08-01 10:23:30 +02:00
a8cd58214d Modify example for PlatformIO.ini 2020-08-01 10:13:11 +02:00
b3e0b567b3 Merge branch 'cc32d9-dev' into dev 2020-07-31 21:54:26 +02:00
0853fe05ec Rename reset options 2020-07-31 21:54:09 +02:00
625968cd99 Add shutdown function 2020-07-31 20:14:40 +02:00
8d7d157445 Add shutdown function 2020-07-31 20:14:31 +02:00
b73ed23a97 Enable/disable ADR 2020-07-31 17:48:11 +02:00
513ac44268 Upgrade to LMIC version 3.2.0 2020-07-31 16:32:28 +02:00
45778d2186 New config option: TTN_RADIO_RST_KEEP_ASSERTED 2020-05-25 23:00:00 +02:00
e29477fa7e Update to latest LMIC version (3.0.99.10) 2020-01-01 17:00:47 +01:00
3036d16e0d Latest version of issue453 branch 2019-12-03 22:13:45 +01:00
2d09703fc6 More detail LMIC logging 2019-10-16 23:48:12 +02:00
4df057042c Improve compatibility between ESP-IDF v3.3 and v4.x 2019-10-14 22:09:16 +02:00
48668b3295 Compatibility with ESP-IDF v4.x 2019-10-13 23:43:17 +02:00
baa6c93d5f Task notification instead of queue for LMIC task 2019-10-13 20:26:15 +02:00
43080636bc More comments 2019-10-12 00:02:31 +02:00
a8fd68ca5e More logging cases 2019-10-10 23:09:32 +02:00
3ac63d4146 New event handling 2019-10-09 23:06:18 +02:00
dc0bfefe0e Ring buffer for detailed logging 2019-10-08 20:52:08 +02:00
6adf69ea0c Add more comments 2019-10-07 23:59:57 +02:00
c2a5bd8374 Cleanup timers 2019-10-07 23:24:51 +02:00
a601c2b2bf New job scheduling implementation 2019-10-07 23:16:55 +02:00
d7d4a40c4f Get rid of lmic_pins 2019-10-06 21:49:25 +02:00
1e1cadf400 Integrate development version of 3.0.99 2019-10-02 21:37:48 +02:00
cf94bba4af Add library as extra component for examples (cmake) 2019-04-25 19:50:39 +02:00
31b859cdf4 Improve example setup 2019-04-19 12:15:30 +02:00
1c88b1f5be PlatformIO library description 2019-04-17 22:25:26 +02:00
f01b29a3b2 Upgrade to version 2.3.2 of mcci-catena/arduino-lmic 2019-04-16 21:36:03 +02:00
d7e8a920c8 Merge branch 'master' into dev 2019-04-16 21:28:28 +02:00
267e04c833 Add cmake files for examples 2019-03-19 21:49:08 +01:00
f899c38663 Merge branch 'daveol-master' 2019-03-19 21:30:21 +01:00
6afdfe4caf Add nvs_flash requirement for cmake 2019-03-16 20:39:01 +01:00
9bb6788a7b Create CMakeLists for cmake compatibility 2019-03-16 20:06:30 +01:00
58116219bb More transformation to C++ 2018-10-27 23:55:36 +02:00
f03b5c51fd Simplify conditional compilation; add region Japan 2018-10-27 19:19:55 +02:00
99420c6a75 Improve constness 2018-10-27 14:49:08 +02:00
47063256a3 Convert provisioning code to C++ 2018-10-27 14:45:55 +02:00
ee08d2a3c1 Simplify SPI code 2018-10-22 12:57:49 +02:00
80 changed files with 7722 additions and 2268 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ sdkconfig
sdkconfig.old
dev_keys.txt
ttn-esp32
.vscode/

View File

@ -28,7 +28,7 @@
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17",
"cppStandard": "c++11",
"intelliSenseMode": "clang-x64"
}
],

18
CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
set(COMPONENT_SRCDIRS
"src"
"src/aes"
"src/hal"
"src/lmic"
)
set(COMPONENT_ADD_INCLUDEDIRS
"include"
)
set(COMPONENT_REQUIRES
nvs_flash
)
register_component()
if (IDF_VER STRGREATER_EQUAL "v4.0")
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-expansion-to-defined)
endif()

63
Kconfig
View File

@ -14,16 +14,22 @@ config TTN_LORA_FREQ_EU_868
bool "Europe (868 MHz)"
config TTN_LORA_FREQ_US_915
bool "North America (915 Mhz)"
bool "North and South America (915 MHz)"
config TTN_LORA_FREQ_AU_921
bool "Australia (921 Mhz)"
config TTN_LORA_FREQ_AU_915
bool "Australia (915 MHz)"
config TTN_LORA_FREQ_AS_923
bool "Asia (923 Mhz)"
bool "Asia (923 MHz)"
config TTN_LORA_FREQ_AS_923_JP
bool "Asia, region Japan (923 MHz)"
config TTN_LORA_FREQ_KR_920
bool "South Korea (920 MHz)"
config TTN_LORA_FREQ_IN_866
bool "India (866 Mhz)"
bool "India (866 MHz)"
endchoice
@ -43,32 +49,27 @@ config TTN_RADIO_SX1276_77_78_79
endchoice
choice TTN_TIMER
prompt "Timer"
default TTN_TIMER_1_GROUP_0
help
A timer is used to implement the LoRaWAN protocol. This selects the ESP32's timer.
config TTN_TIMER_0_GROUP_0
bool "Timer 0 of group 0"
config TTN_TIMER_1_GROUP_0
bool "Timer 1 of group 0"
config TTN_TIMER_0_GROUP_1
bool "Timer 0 of group 1"
config TTN_TIMER_1_GROUP_1
bool "Timer 1 of group 1"
endchoice
config TTN_SPI_FREQ
int "SPI frequency (in Hz)"
default 10000000
help
SPI frequency to communicate between ESP32 and SX127x radio chip
choice TTN_RESET
prompt "Reset states"
default TTN_RESET_STATES_FLOATING
help
Reset pin can be floating for most boards and shields.
A few boards/shields require the pin to be held high for operation.
config TTN_RESET_STATES_FLOATING
bool "Toggle between low and floating"
config TTN_RESET_STATES_ASSERTED
bool "Toggle between low and high"
endchoice
config TTN_BG_TASK_PRIO
int "Background task priority"
default 10
@ -79,22 +80,22 @@ config TTN_BG_TASK_PRIO
choice TTN_PROVISION_UART
prompt "UART for provisioning"
prompt "AT commands"
default TTN_PROVISION_UART_DEFAULT
help
Select whether to use UART for listening for provisioning commands.
Select whether to listen on UART for AT commands.
- Default is to use UART0 on pins GPIO1(TX) and GPIO3(RX).
- If "Custom" is selected, UART0 or UART1 can be chosen,
and any pins can be selected.
- If "None" is selected, the feature is not available.
- If "None" is selected, AT commands are not available.
config TTN_PROVISION_UART_DEFAULT
bool "Default: UART0, TX=GPIO1, RX=GPIO3, 115,200 baud"
bool "Enabled - default settings: UART0, TX=GPIO1, RX=GPIO3, 115,200 baud"
config TTN_PROVISION_UART_CUSTOM
bool "Custom"
bool "Enabled - custom UART settings"
config TTN_PROVISION_UART_NONE
bool "None"
bool "Disabled"
endchoice
choice TTN_PROVISION_UART_NUM

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# Update the below line to match the path to the ttn-esp32 library,
# e.g. list(APPEND EXTRA_COMPONENT_DIRS "/Users/me/Documents/ttn-esp32")
list(APPEND EXTRA_COMPONENT_DIRS "../..")
project(hello_world)

View File

@ -1,3 +1,5 @@
PROJECT_NAME := hello_world
EXTRA_COMPONENT_DIRS := $(abspath ../..)
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)

View File

@ -7,11 +7,12 @@
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* Sample program showing how to send a test message every 30 second.
* Sample program showing how to send and receive messages.
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "esp_event.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "TheThingsNetwork.h"
@ -36,9 +37,9 @@ const char *appKey = "????????????????????????????????";
#define TTN_PIN_SPI_MISO 19
#define TTN_PIN_NSS 18
#define TTN_PIN_RXTX TTN_NOT_CONNECTED
#define TTN_PIN_RST TTN_NOT_CONNECTED
#define TTN_PIN_RST 14
#define TTN_PIN_DIO0 26
#define TTN_PIN_DIO1 33
#define TTN_PIN_DIO1 35
static TheThingsNetwork ttn;
@ -53,17 +54,25 @@ void sendMessages(void* pvParameter)
TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1);
printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n");
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000));
}
}
void messageReceived(const uint8_t* message, size_t length, port_t port)
{
printf("Message of %d bytes received on port %d:", length, port);
for (int i = 0; i < length; i++)
printf(" %02x", message[i]);
printf("\n");
}
extern "C" void app_main(void)
{
esp_err_t err;
// Initialize the GPIO ISR handler service
err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
ESP_ERROR_CHECK(err);
// Initialize the NVS (non-volatile storage) for saving and restoring the keys
err = nvs_flash_init();
ESP_ERROR_CHECK(err);
@ -85,11 +94,14 @@ extern "C" void app_main(void)
// The below line can be commented after the first run as the data is saved in NVS
ttn.provision(devEui, appEui, appKey);
// Register callback for received messages
ttn.onMessage(messageReceived);
printf("Joining...\n");
if (ttn.join())
{
printf("Joined.\n");
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL);
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr);
}
else
{

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# Update the below line to match the path to the ttn-esp32 library,
# e.g. list(APPEND EXTRA_COMPONENT_DIRS "/Users/me/Documents/ttn-esp32")
list(APPEND EXTRA_COMPONENT_DIRS "../..")
project(mac_address)

View File

@ -1,3 +1,5 @@
PROJECT_NAME := mac_address
EXTRA_COMPONENT_DIRS := $(abspath ../..)
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)

View File

@ -11,6 +11,7 @@
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "esp_event.h"
#include "nvs_flash.h"
@ -50,7 +51,7 @@ void sendMessages(void* pvParameter)
TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1);
printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n");
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000));
}
}
@ -87,7 +88,7 @@ extern "C" void app_main(void)
if (ttn.join())
{
printf("Joined.\n");
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL);
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr);
}
else
{

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# Update the below line to match the path to the ttn-esp32 library,
# e.g. list(APPEND EXTRA_COMPONENT_DIRS "/Users/me/Documents/ttn-esp32")
list(APPEND EXTRA_COMPONENT_DIRS "../..")
project(monitoring)

View File

@ -0,0 +1,5 @@
PROJECT_NAME := monitoring
EXTRA_COMPONENT_DIRS := $(abspath ../..)
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)

View File

@ -0,0 +1,142 @@
/*******************************************************************************
*
* 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
*
* Sample program showing how to send and receive messages.
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "esp_event.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "TheThingsNetwork.h"
// NOTE:
// The LoRaWAN frequency and the radio chip must be configured by running 'make menuconfig'.
// Go to Components / The Things Network, select the appropriate values and save.
// Copy the below hex string from the "Device EUI" field
// on your device's overview page in the TTN console.
const char *devEui = "????????????????";
// Copy the below two lines from bottom of the same page
const char *appEui = "????????????????";
const char *appKey = "????????????????????????????????";
// Pins and other resources
#define TTN_SPI_HOST HSPI_HOST
#define TTN_SPI_DMA_CHAN 1
#define TTN_PIN_SPI_SCLK 5
#define TTN_PIN_SPI_MOSI 27
#define TTN_PIN_SPI_MISO 19
#define TTN_PIN_NSS 18
#define TTN_PIN_RXTX TTN_NOT_CONNECTED
#define TTN_PIN_RST 14
#define TTN_PIN_DIO0 26
#define TTN_PIN_DIO1 35
static TheThingsNetwork ttn;
const unsigned TX_INTERVAL = 30;
static uint8_t msgData[] = "Hello, world";
void printRFSettings(const char* window, const TTNRFSettings& settings)
{
int bw = (1 << (static_cast<int>(settings.bandwidth) - 1)) * 125;
int sf = static_cast<int>(settings.spreadingFactor) + 5;
if (settings.spreadingFactor == kTTNSFNone)
{
printf("%s: not used\n", window);
}
else if (settings.spreadingFactor == kTTNFSK)
{
printf("%s: FSK, BW %dkHz, %d.%d MHz\n",
window, bw, settings.frequency / 1000000, (settings.frequency % 1000000 + 50000) / 100000);
}
else
{
printf("%s: SF%d, BW %dkHz, %d.%d MHz\n",
window, sf, bw, settings.frequency / 1000000, (settings.frequency % 1000000 + 50000) / 100000);
}
}
void printAllRFSettings()
{
printRFSettings("TX ", ttn.txSettings());
printRFSettings("RX1", ttn.rx1Settings());
printRFSettings("RX2", ttn.rx2Settings());
}
void sendMessages(void* pvParameter)
{
while (1) {
printf("Sending message...\n");
TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1);
printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n");
printAllRFSettings();
vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000));
}
}
void messageReceived(const uint8_t* message, size_t length, port_t port)
{
printf("Message of %d bytes received on port %d:", length, port);
for (int i = 0; i < length; i++)
printf(" %02x", message[i]);
printf("\n");
printf("RSSI: %d dBm\n", ttn.rssi());
}
extern "C" void app_main(void)
{
esp_err_t err;
// Initialize the GPIO ISR handler service
err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
ESP_ERROR_CHECK(err);
// Initialize the NVS (non-volatile storage) for saving and restoring the keys
err = nvs_flash_init();
ESP_ERROR_CHECK(err);
// Initialize SPI bus
spi_bus_config_t spi_bus_config;
spi_bus_config.miso_io_num = TTN_PIN_SPI_MISO;
spi_bus_config.mosi_io_num = TTN_PIN_SPI_MOSI;
spi_bus_config.sclk_io_num = TTN_PIN_SPI_SCLK;
spi_bus_config.quadwp_io_num = -1;
spi_bus_config.quadhd_io_num = -1;
spi_bus_config.max_transfer_sz = 0;
err = spi_bus_initialize(TTN_SPI_HOST, &spi_bus_config, TTN_SPI_DMA_CHAN);
ESP_ERROR_CHECK(err);
// Configure the SX127x pins
ttn.configurePins(TTN_SPI_HOST, TTN_PIN_NSS, TTN_PIN_RXTX, TTN_PIN_RST, TTN_PIN_DIO0, TTN_PIN_DIO1);
// The below line can be commented after the first run as the data is saved in NVS
ttn.provision(devEui, appEui, appKey);
// Register callback for received messages
ttn.onMessage(messageReceived);
printf("Joining...\n");
if (ttn.join())
{
printf("Joined.\n");
printAllRFSettings();
printf("RSSI: %d dBm\n", ttn.rssi());
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr);
}
else
{
printf("Join failed. Goodbye\n");
}
}

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# Update the below line to match the path to the ttn-esp32 library,
# e.g. list(APPEND EXTRA_COMPONENT_DIRS "/Users/me/Documents/ttn-esp32")
list(APPEND EXTRA_COMPONENT_DIRS "../..")
project(provisioning)

View File

@ -1,3 +1,5 @@
PROJECT_NAME := provisioning
EXTRA_COMPONENT_DIRS := $(abspath ../..)
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)

View File

@ -11,6 +11,7 @@
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "esp_event.h"
#include "nvs_flash.h"
@ -45,7 +46,7 @@ void sendMessages(void* pvParameter)
TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1);
printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n");
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000));
}
}
@ -82,7 +83,7 @@ extern "C" void app_main(void)
if (ttn.join())
{
printf("Joined.\n");
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL);
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr);
}
else
{

View File

@ -1,3 +0,0 @@
PROJECT_NAME := send_recv
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# Update the below line to match the path to the ttn-esp32 library,
# e.g. list(APPEND EXTRA_COMPONENT_DIRS "/Users/me/Documents/ttn-esp32")
list(APPEND EXTRA_COMPONENT_DIRS "../..")
project(shutdown)

View File

@ -0,0 +1,5 @@
PROJECT_NAME := shutdown
EXTRA_COMPONENT_DIRS := $(abspath ../..)
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)

View File

View File

@ -12,6 +12,7 @@
#include "freertos/FreeRTOS.h"
#include "esp_event.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "TheThingsNetwork.h"
@ -38,22 +39,56 @@ const char *appKey = "????????????????????????????????";
#define TTN_PIN_RXTX TTN_NOT_CONNECTED
#define TTN_PIN_RST 14
#define TTN_PIN_DIO0 26
#define TTN_PIN_DIO1 33
#define TTN_PIN_DIO1 35
static TheThingsNetwork ttn;
const unsigned TX_INTERVAL = 30;
static uint8_t msgData[] = "Hello, world";
bool join()
{
printf("Joining...\n");
if (ttn.join())
{
printf("Joined.\n");
return true;
}
else
{
printf("Join failed. Goodbye\n");
return false;
}
}
void sendMessages(void* pvParameter)
{
while (1) {
printf("Sending message...\n");
TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1);
printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n");
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
// Send 2 messages
for (int i = 0; i < 2; i++)
{
printf("Sending message...\n");
TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1);
printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n");
vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000));
}
// shutdown
ttn.shutdown();
// go to sleep
printf("Sleeping for 30s...\n");
vTaskDelay(pdMS_TO_TICKS(30000));
// startup
ttn.startup();
// join again
if (!join())
return;
}
}
@ -95,14 +130,8 @@ extern "C" void app_main(void)
ttn.onMessage(messageReceived);
printf("Joining...\n");
if (ttn.join())
if (join())
{
printf("Joined.\n");
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL);
}
else
{
printf("Join failed. Goodbye\n");
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr);
}
}

View File

@ -29,12 +29,121 @@ typedef uint8_t port_t;
*/
enum TTNResponseCode
{
kTTNErrorTransmissionFailed = -1,
kTTNErrorUnexpected = -10,
kTTNSuccessfulTransmission = 1,
kTTNSuccessfulReceive = 2
kTTNErrorTransmissionFailed = -1,
kTTNErrorUnexpected = -10,
kTTNSuccessfulTransmission = 1,
kTTNSuccessfulReceive = 2
};
/**
* @brief RX/TX window
*/
enum TTNRxTxWindow
{
/**
* @brief Outside RX/TX window
*/
kTTNIdleWindow = 0,
/**
* @brief Transmission window (up to RX1 window)
*/
kTTNTxWindow = 1,
/**
* @brief Reception window 1 (up to RX2 window)
*/
kTTNRx1Window = 2,
/**
* @brief Reception window 2
*/
kTTNRx2Window = 3
};
/**
* @brief Spreading Factor
*/
enum TTNSpreadingFactor
{
/**
* @brief Unused / undefined spreading factor
*/
kTTNSFNone = 0,
/**
* @brief Frequency Shift Keying (FSK)
*/
kTTNFSK = 1,
/**
* @brief Spreading Factor 7 (SF7)
*/
kTTNSF7 = 2,
/**
* @brief Spreading Factor 8 (SF8)
*/
kTTNSF8 = 3,
/**
* @brief Spreading Factor 9 (SF9)
*/
kTTNSF9 = 4,
/**
* @brief Spreading Factor 10 (SF10)
*/
kTTNSF10 = 5,
/**
* @brief Spreading Factor 11 (SF11)
*/
kTTNSF11 = 6,
/**
* @brief Spreading Factor 12 (SF12)
*/
kTTNSF12 = 7
};
/**
* @brief Bandwidth
*/
enum TTNBandwidth
{
/**
* @brief Undefined/unused bandwidth
*/
kTTNBWNone = 0,
/**
* @brief Bandwidth of 125 kHz
*/
kTTNBW125 = 1,
/**
* @brief Bandwidth of 250 kHz
*/
kTTNBW250 = 2,
/**
* @brief Bandwidth of 500 kHz
*/
kTTNBW500 = 3
};
/**
* @brief RF settings for TX or RX
*/
struct TTNRFSettings
{
/**
* @brief Spreading Factor (SF)
*/
TTNSpreadingFactor spreadingFactor;
/**
* @brief Bandwidth (BW)
*/
TTNBandwidth bandwidth;
/**
* @brief Frequency, in Hz
*/
uint32_t frequency;
};
/**
* @brief Callback for recieved messages
*
@ -68,7 +177,8 @@ public:
/**
* @brief Reset the LoRaWAN radio.
*
* Does not clear provisioned keys.
* To restart communication, `join()` must be called.
* Neither clears the provisioned keys nor the configured pins.
*/
void reset();
@ -76,8 +186,8 @@ public:
* @brief Configures the pins used to communicate with the LoRaWAN radio chip.
*
*
* The SPI bus must be first configured using spi_bus_initialize(). Then it is passed as the first parameter.
* Additionally, 'gpio_install_isr_service()' must be called to initialize the GPIO ISR handler service.
* 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)
@ -130,7 +240,7 @@ public:
bool provisionWithMAC(const char *appEui, const char *appKey);
/**
* @brief Start task that listens on configured UART for provisioning commands.
* @brief Start task that listens on configured UART for AT commands.
*
* Run 'make menuconfig' to configure it.
*/
@ -199,7 +309,7 @@ public:
* 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' or 'poll'. The callback is called
* 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.
*
@ -216,6 +326,89 @@ public:
*/
bool isProvisioned();
/**
* @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 rssiCal RSSI calibration value, in dB
*/
void setRSSICal(int8_t rssiCal);
/**
* Returns whether Adaptive Data Rate (ADR) is enabled.
*
* @return true if enabled
* @return false if disabled
*/
bool adrEnabled();
/**
* @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 setAdrEnabled(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.
* Neither clears the provisioned keys nor the configured pins.
*/
void shutdown();
/**
* @brief Restarts the background tasks and RF module.
*
* This member function must only be called after a call to `shutdowna()`.
*/
void startup();
/**
* @brief Gets currentRX/TX window
* @return window
*/
TTNRxTxWindow rxTxWindow();
/**
* @brief Gets the RF settings for the specified window
* @param window RX/TX windows (valid values are `kTTNTxWindow`, `kTTNRx1Window` and `kTTNRx2Window`)
*/
TTNRFSettings getRFSettings(TTNRxTxWindow window);
/**
* @brief Gets the RF settings of the last (or ongoing) transmission.
* @return RF settings
*/
TTNRFSettings txSettings();
/**
* @brief Gets the RF settings of the last (or ongoing) reception of RX window 1.
* @return RF settings
*/
TTNRFSettings rx1Settings();
/**
* @brief Gets the RF settings of the last (or ongoing) reception of RX window 2.
* @return RF settings
*/
TTNRFSettings rx2Settings();
/**
* @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 rssi();
private:
TTNMessageCallback messageCallback;

26
library.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "ttn-esp32",
"description": "The Things Network device library for ESP32 (ESP-IDF) and SX127x based devices",
"keywords": "esp32, ttn, espidf, lorawan, sx1276, sx1272, rfm92, rfm95, lmic",
"authors": {
"name": "Manuel Bl",
"email": "manuel.bleichenbacher@gmail.com",
"url": "https://github.com/manuelbl/ttn-esp32",
"maintainer": true
},
"repository": {
"type": "git",
"url": "https://github.com/manuelbl/ttn-esp32.git",
"branch": "master"
},
"version": "3.2.0",
"license": "MIT License",
"export": {
"include": [
"include",
"src"
]
},
"frameworks": "espidf",
"platforms": "espressif32"
}

390
src/TTNLogging.cpp Normal file
View File

@ -0,0 +1,390 @@
/*******************************************************************************
*
* 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
*
* Circular buffer for detailed logging without affecting LMIC timing.
*******************************************************************************/
#if LMIC_ENABLE_event_logging
#include <string.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "lmic/lmic.h"
#include "TTNLogging.h"
#define NUM_RINGBUF_MSG 50
static const char* const TAG = "lmic";
static TTNLogging ttnLog;
/**
* @brief Message structure used in ring buffer
*
* The structure is sent from the LMIC task to the logging task.
*/
struct TTNLogMessage {
const char* message;
uint32_t datum;
ev_t event;
ostime_t time;
ostime_t txend;
ostime_t globalDutyAvail;
u4_t freq;
u2_t opmode;
u2_t fcntDn;
u2_t fcntUp;
u2_t rxsyms;
rps_t rps;
u1_t txChnl;
u1_t datarate;
u1_t txrxFlags;
u1_t saveIrqFlags;
};
// Constants for formatting LORA values
static const char* const SF_NAMES[] = { "FSK", "SF7", "SF8", "SF9", "SF10", "SF11", "SF12", "SFrfu" };
static const char* const BW_NAMES[] = { "BW125", "BW250", "BW500", "BWrfu" };
static const char* const CR_NAMES[] = { "CR 4/5", "CR 4/6", "CR 4/7", "CR 4/8" };
static const char* const CRC_NAMES[] = { "NoCrc", "Crc" };
static void printMessage(TTNLogMessage* log);
static void printFatalError(TTNLogMessage* log);
static void printEvent(TTNLogMessage* log);
static void printEvtJoined(TTNLogMessage* log);
static void printEvtJoinFailed(TTNLogMessage* log);
static void printEvtTxComplete(TTNLogMessage* log);
static void printEvtTxStart(TTNLogMessage* log);
static void printEvtRxStart(TTNLogMessage* log);
static void printEvtJoinTxComplete(TTNLogMessage* log);
static void bin2hex(const uint8_t* bin, unsigned len, char* buf, char sep = 0);
// Create singleton instance
TTNLogging* TTNLogging::initInstance()
{
ttnLog.init();
return &ttnLog;
}
// Initialize logging
void TTNLogging::init()
{
ringBuffer = xRingbufferCreate(NUM_RINGBUF_MSG * sizeof(TTNLogMessage), RINGBUF_TYPE_NOSPLIT);
if (ringBuffer == nullptr) {
ESP_LOGE(TAG, "Failed to create ring buffer");
ASSERT(0);
}
xTaskCreate(loggingTask, "ttn_log", 1024 * 4, ringBuffer, 4, nullptr);
hal_set_failure_handler(logFatal);
}
// Record a logging event for later output
void TTNLogging::logEvent(int event, const char* message, uint32_t datum)
{
if (ringBuffer == nullptr)
return;
TTNLogMessage log;
log.message = message;
log.datum = datum;
// capture state
log.time = os_getTime();
log.txend = LMIC.txend;
log.globalDutyAvail = LMIC.globalDutyAvail;
log.event = (ev_t)event;
log.freq = LMIC.freq;
log.opmode = LMIC.opmode;
log.fcntDn = (u2_t) LMIC.seqnoDn;
log.fcntUp = (u2_t) LMIC.seqnoUp;
log.rxsyms = LMIC.rxsyms;
log.rps = LMIC.rps;
log.txChnl = LMIC.txChnl;
log.datarate = LMIC.datarate;
log.txrxFlags = LMIC.txrxFlags;
log.saveIrqFlags = LMIC.saveIrqFlags;
xRingbufferSend(ringBuffer, &log, sizeof(log), 0);
}
// record a fatal event (failed assert) for later output
void TTNLogging::logFatal(const char* file, uint16_t line)
{
ttnLog.logEvent(-3, file, line);
}
// Record an informational message for later output
// The message must not be freed.
extern "C" void LMICOS_logEvent(const char *pMessage)
{
ttnLog.logEvent(-1, pMessage, 0);
}
// Record an information message with an integer value for later output
// The message must not be freed.
extern "C" void LMICOS_logEventUint32(const char *pMessage, uint32_t datum)
{
ttnLog.logEvent(-2, pMessage, datum);
}
// ---------------------------------------------------------------------------
// Log output
// Tasks that receiveds the recorded messages, formats and outputs them.
void TTNLogging::loggingTask(void* param)
{
RingbufHandle_t ringBuffer = (RingbufHandle_t)param;
while (true) {
size_t size;
TTNLogMessage* log = (TTNLogMessage*) xRingbufferReceive(ringBuffer, &size, portMAX_DELAY);
if (log == nullptr)
continue;
printMessage(log);
vRingbufferReturnItem(ringBuffer, log);
}
}
// Format and output a log message
void printMessage(TTNLogMessage* log)
{
switch((int)log->event)
{
case -1:
ESP_LOGI(TAG, "%u (%d ms) - %s: opmode=%x",
log->time, osticks2ms(log->time),
log->message, log->opmode
);
break;
case -2:
ESP_LOGI(TAG, "%u (%d ms) - %s: datum=0x%x, opmode=%x)",
log->time, osticks2ms(log->time),
log->message, log->datum, log->opmode
);
break;
case -3:
printFatalError(log);
break;
default:
printEvent(log);
break;
}
}
void printFatalError(TTNLogMessage* log)
{
ESP_LOGE(TAG, "%u (%d ms) - %s, %d",
log->time, osticks2ms(log->time),
log->message, log->datum
);
ESP_LOGE(TAG, "- freq=%d.%d, txend=%u, avail=%u, ch=%u",
log->freq / 1000000, (log->freq % 1000000) / 100000,
log->txend, log->globalDutyAvail,
(unsigned)log->txChnl
);
rps_t rps = log->rps;
ESP_LOGE(TAG, "- rps=0x%02x (%s, %s, %s, %s, IH=%d)",
rps,
SF_NAMES[getSf(rps)],
BW_NAMES[getBw(rps)],
CR_NAMES[getCr(rps)],
CRC_NAMES[getNocrc(rps)],
getIh(rps)
);
ESP_LOGE(TAG, "- opmode=%x, txrxFlags=0x%02x%s, saveIrqFlags=0x%02x",
log->opmode,
log->txrxFlags,
(log->txrxFlags & TXRX_ACK) != 0 ? "; received ack" : "",
log->saveIrqFlags
);
}
void printEvent(TTNLogMessage* log)
{
ESP_LOGI(TAG, "%u (%d ms) - %s",
log->time, osticks2ms(log->time),
log->message
);
switch((int)log->event)
{
case EV_JOINED:
printEvtJoined(log);
break;
case EV_JOIN_FAILED:
printEvtJoinFailed(log);
break;
case EV_TXCOMPLETE:
printEvtTxComplete(log);
break;
case EV_TXSTART:
printEvtTxStart(log);
break;
case EV_RXSTART:
printEvtRxStart(log);
break;
case EV_JOIN_TXCOMPLETE:
printEvtJoinTxComplete(log);
break;
default:
break;
};
}
// Format and output the detail of a successful network join
void printEvtJoined(TTNLogMessage* log)
{
ESP_LOGI(TAG, "- ch=%d", (unsigned)log->txChnl);
u4_t netid = 0;
devaddr_t devaddr = 0;
u1_t nwkKey[16];
u1_t artKey[16];
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
ESP_LOGI(TAG, "- netid: %d", netid);
ESP_LOGI(TAG, "- devaddr: %08x", devaddr);
char hexBuf[48];
bin2hex((uint8_t*)&artKey, sizeof(artKey), hexBuf, '-');
ESP_LOGI(TAG, "- artKey: %s", hexBuf);
bin2hex((uint8_t*)&nwkKey, sizeof(nwkKey), hexBuf, '-');
ESP_LOGI(TAG, "- nwkKey: %s", hexBuf);
}
// Format and output the detail of a failed network join
void printEvtJoinFailed(TTNLogMessage* log)
{
rps_t rps = log->rps;
ESP_LOGE(TAG, "- freq=%d.%d, opmode=%x, rps=0x%02x (%s, %s, %s, %s, IH=%d)",
log->freq / 1000000, (log->freq % 1000000) / 100000,
log->opmode,
rps,
SF_NAMES[getSf(rps)],
BW_NAMES[getBw(rps)],
CR_NAMES[getCr(rps)],
CRC_NAMES[getNocrc(rps)],
getIh(rps)
);
}
void printEvtTxComplete(TTNLogMessage* log)
{
rps_t rps = log->rps;
ESP_LOGI(TAG, "- ch=%d, rps=0x%02x (%s, %s, %s, %s, IH=%d)",
(unsigned)log->txChnl,
rps,
SF_NAMES[getSf(rps)],
BW_NAMES[getBw(rps)],
CR_NAMES[getCr(rps)],
CRC_NAMES[getNocrc(rps)],
getIh(rps)
);
ESP_LOGI(TAG, "- txrxFlags=0x%02x%s, FcntUp=%04x, FcntDn=%04x, txend=%u",
log->txrxFlags,
(log->txrxFlags & TXRX_ACK) != 0 ? "; received ack" : "",
log->fcntUp, log->fcntDn,
log->txend
);
}
void printEvtTxStart(TTNLogMessage* log)
{
rps_t rps = log->rps;
ESP_LOGI(TAG, "- ch=%d, rps=0x%02x (%s, %s, %s, %s, IH=%d)",
(unsigned)log->txChnl,
rps,
SF_NAMES[getSf(rps)],
BW_NAMES[getBw(rps)],
CR_NAMES[getCr(rps)],
CRC_NAMES[getNocrc(rps)],
getIh(rps)
);
ESP_LOGI(TAG, "- datarate=%u, opmode=%x, txend=%u",
log->datarate,
log->opmode,
log->txend
);
}
void printEvtRxStart(TTNLogMessage* log)
{
rps_t rps = log->rps;
ESP_LOGI(TAG, "- freq=%d.%d, rps=0x%02x (%s, %s, %s, %s, IH=%d)",
log->freq / 1000000, (log->freq % 1000000) / 100000,
rps,
SF_NAMES[getSf(rps)],
BW_NAMES[getBw(rps)],
CR_NAMES[getCr(rps)],
CRC_NAMES[getNocrc(rps)],
getIh(rps)
);
ESP_LOGI(TAG, "- delta=%dms, rxsysm=%u",
osticks2ms(log->time - log->txend),
log->rxsyms
);
}
void printEvtJoinTxComplete(TTNLogMessage* log)
{
ESP_LOGI(TAG, "- saveIrqFlags=0x%02x",
log->saveIrqFlags
);
}
static const char* HEX_DIGITS = "0123456789ABCDEF";
/**
* @brief Convert binary data to hexadecimal representation.
*
* @param bin start of binary data
* @param len length of binary data (in bytes)
* @param buf buffer for hexadecimal result
* @param sep separator used between bytes (or 0 for none)
*/
void bin2hex(const uint8_t* bin, unsigned len, char* buf, char sep)
{
int tgt = 0;
for (int i = 0; i < len; i++) {
if (sep != 0 && i != 0)
buf[tgt++] = sep;
buf[tgt++] = HEX_DIGITS[bin[i] >> 4];
buf[tgt++] = HEX_DIGITS[bin[i] & 0xf];
}
buf[tgt] = 0;
}
#endif

55
src/TTNLogging.h Normal file
View File

@ -0,0 +1,55 @@
/*******************************************************************************
*
* 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
*
* Circular buffer for detailed logging without affecting LMIC timing.
*******************************************************************************/
#ifndef _ttnlogging_h_
#define _ttnlogging_h_
#if LMIC_ENABLE_event_logging
#include <freertos/FreeRTOS.h>
#include <freertos/ringbuf.h>
/**
* @brief Logging class.
*
* Logs internal information from LMIC in an asynchrnous fashion in order
* not to distrub the sensitive LORA timing.
*
* A ring buffer and a separate logging task is ued. The LMIC core records
* relevant values from the current LORA settings and writes them to a ring
* buffer. The logging tasks receives the message and the values, formats
* them and outputs them via the regular ESP-IDF logging mechanism.
*
* In order to activate the detailed logging, set the macro
* `LMIC_ENABLE_event_logging` to 1.
*
* This class is not to be used directly.
*/
class TTNLogging {
public:
static TTNLogging* initInstance();
void init();
void logEvent(int event, const char* message, uint32_t datum);
private:
static void loggingTask(void* param);
static void logFatal(const char* file, uint16_t line);
RingbufHandle_t ringBuffer;
};
#endif
#endif

View File

@ -2,7 +2,7 @@
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018 Manuel Bleichenbacher
* Copyright (c) 2018-2019 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
@ -16,48 +16,28 @@
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "provisioning.h"
#include "TTNProvisioning.h"
#include "lmic/lmic.h"
#include "hal/hal_esp32.h"
#define UART_NUM CONFIG_TTN_PROVISION_UART_NUM
#define MAX_LINE_LENGTH 128
#if defined(TTN_HAS_AT_COMMANDS)
const uart_port_t UART_NUM = (uart_port_t) CONFIG_TTN_PROVISION_UART_NUM;
const int MAX_LINE_LENGTH = 128;
#endif
static const char *TAG = "ttn_prov";
static const char *NVS_FLASH_PARTITION = "ttn";
static const char *NVS_FLASH_KEY_DEV_EUI = "devEui";
static const char *NVS_FLASH_KEY_APP_EUI = "appEui";
static const char *NVS_FLASH_KEY_APP_KEY = "appKey";
static const char* const TAG = "ttn_prov";
static const char* const NVS_FLASH_PARTITION = "ttn";
static const char* const NVS_FLASH_KEY_DEV_EUI = "devEui";
static const char* const NVS_FLASH_KEY_APP_EUI = "appEui";
static const char* const NVS_FLASH_KEY_APP_KEY = "appKey";
static bool provisioning_decode(bool incl_dev_eui, const char *dev_eui, const char *app_eui, const char *app_key);
static void provisioning_task(void* pvParameter);
static void provisioning_add_line_data(int numBytes);
static void provisioning_detect_line_end(int start_at);
static void provisioning_process_line();
static bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent);
static bool write_nvs_value(nvs_handle handle, const char* key, const uint8_t* data, size_t len);
static bool hex_str_to_bin(const char *hex, uint8_t *buf, int len);
static int hex_tuple_to_byte(const char *hex);
static int hex_digit_to_val(char ch);
static void bin_to_hex_str(const uint8_t* buf, int len, char* hex);
static char val_to_hex_digit(int val);
static void swap_bytes(uint8_t* buf, int len);
static bool is_all_zeroes(const uint8_t* buf, int len);
static QueueHandle_t uart_queue = NULL;
static char* line_buf;
static int line_length;
static uint8_t last_line_end_char = 0;
static uint8_t global_dev_eui[8];
static uint8_t global_app_eui[8];
static uint8_t global_app_key[16];
static bool have_keys = false;
static bool quit_task = false;
#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES)
static void provisioning_config_uart();
#if defined(TTN_HAS_AT_COMMANDS)
void ttn_provisioning_task_caller(void* pvParameter);
#endif
@ -86,22 +66,41 @@ void os_getDevKey (u1_t* buf)
memcpy(buf, global_app_key, 16);
}
// --- Constructor
TTNProvisioning::TTNProvisioning()
: have_keys(false)
#if defined(TTN_HAS_AT_COMMANDS)
, uart_queue(nullptr), line_buf(nullptr), line_length(0), last_line_end_char(0), quit_task(false)
#endif
{
}
// --- Provisioning task
void provisioning_start_task()
#if defined(TTN_HAS_AT_COMMANDS)
void TTNProvisioning::startTask()
{
#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES)
provisioning_config_uart();
#if defined(TTN_CONFIG_UART)
configUART();
#endif
esp_err_t err = uart_driver_install(UART_NUM, 2048, 2048, 20, &uart_queue, 0);
ESP_ERROR_CHECK(err);
xTaskCreate(provisioning_task, "provisioning", 2048, NULL, 1, NULL);
xTaskCreate(ttn_provisioning_task_caller, "ttn_provision", 2048, this, 1, nullptr);
}
void provisioning_task(void* pvParameter)
void ttn_provisioning_task_caller(void* pvParameter)
{
TTNProvisioning* provisioning = (TTNProvisioning*)pvParameter;
provisioning->provisioningTask();
vTaskDelete(nullptr);
}
void TTNProvisioning::provisioningTask()
{
line_buf = (char*)malloc(MAX_LINE_LENGTH + 1);
line_length = 0;
@ -118,7 +117,7 @@ void provisioning_task(void* pvParameter)
switch (event.type)
{
case UART_DATA:
provisioning_add_line_data(event.size);
addLineData(event.size);
break;
case UART_FIFO_OVF:
@ -134,10 +133,9 @@ void provisioning_task(void* pvParameter)
free(line_buf);
uart_driver_delete(UART_NUM);
vTaskDelete(NULL);
}
void provisioning_add_line_data(int numBytes)
void TTNProvisioning::addLineData(int numBytes)
{
int n;
top:
@ -149,7 +147,7 @@ top:
int start_at = line_length;
line_length += n;
provisioning_detect_line_end(start_at);
detectLineEnd(start_at);
if (n < numBytes)
{
@ -158,7 +156,7 @@ top:
}
}
void provisioning_detect_line_end(int start_at)
void TTNProvisioning::detectLineEnd(int start_at)
{
top:
for (int p = start_at; p < line_length; p++)
@ -175,7 +173,7 @@ top:
last_line_end_char = ch;
if (p > 0)
provisioning_process_line();
processLine();
memcpy(line_buf, line_buf + p + 1, line_length - p - 1);
line_length -= p + 1;
@ -191,7 +189,7 @@ top:
line_length = 0; // Line too long; flush it
}
void provisioning_process_line()
void TTNProvisioning::processLine()
{
bool is_ok = true;
bool reset_needed = false;
@ -209,14 +207,14 @@ void provisioning_process_line()
char hexbuf[16];
memcpy(binbuf, global_dev_eui, 8);
swap_bytes(binbuf, 8);
bin_to_hex_str(binbuf, 8, hexbuf);
swapBytes(binbuf, 8);
binToHexStr(binbuf, 8, hexbuf);
uart_write_bytes(UART_NUM, hexbuf, 16);
uart_write_bytes(UART_NUM, "-", 1);
memcpy(binbuf, global_app_eui, 8);
swap_bytes(binbuf, 8);
bin_to_hex_str(binbuf, 8, hexbuf);
swapBytes(binbuf, 8);
binToHexStr(binbuf, 8, hexbuf);
uart_write_bytes(UART_NUM, hexbuf, 16);
uart_write_bytes(UART_NUM, "-00000000000000000000000000000000\r\n", 35);
@ -228,7 +226,7 @@ void provisioning_process_line()
{
line_buf[24] = 0;
line_buf[41] = 0;
is_ok = provisioning_decode_keys(line_buf + 8, line_buf + 25, line_buf + 42);
is_ok = decodeKeys(line_buf + 8, line_buf + 25, line_buf + 42);
reset_needed = is_ok;
}
}
@ -238,7 +236,7 @@ void provisioning_process_line()
if (is_ok)
{
line_buf[25] = 0;
is_ok = provisioning_from_mac(line_buf + 9, line_buf + 26);
is_ok = fromMAC(line_buf + 9, line_buf + 26);
reset_needed = is_ok;
}
}
@ -250,7 +248,7 @@ void provisioning_process_line()
esp_err_t err = esp_efuse_mac_get_default(mac);
ESP_ERROR_CHECK(err);
bin_to_hex_str(mac, 6, hexbuf);
binToHexStr(mac, 6, hexbuf);
for (int i = 0; i < 12; i += 2) {
if (i > 0)
uart_write_bytes(UART_NUM, ":", 1);
@ -266,7 +264,7 @@ void provisioning_process_line()
esp_err_t err = esp_efuse_mac_get_default(mac);
ESP_ERROR_CHECK(err);
bin_to_hex_str(mac, 6, hexbuf);
binToHexStr(mac, 6, hexbuf);
for (int i = 0; i < 12; i += 2) {
uart_write_bytes(UART_NUM, hexbuf + i, 2);
if (i == 4)
@ -285,18 +283,21 @@ void provisioning_process_line()
if (reset_needed)
{
hal_enterCriticalSection();
ttn_hal.enterCriticalSection();
LMIC_reset();
hal_leaveCriticalSection();
onEvent(EV_RESET);
ttn_hal.leaveCriticalSection();
LMIC.client.eventCb(LMIC.client.eventUserData, EV_RESET);
}
uart_write_bytes(UART_NUM, is_ok ? "OK\r\n" : "ERROR\r\n", is_ok ? 4 : 7);
}
#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES)
#endif
void provisioning_config_uart()
#if defined(TTN_CONFIG_UART)
void TTNProvisioning::configUART()
{
esp_err_t err;
@ -305,7 +306,9 @@ void provisioning_config_uart()
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 0,
.use_ref_tick = false
};
err = uart_param_config(UART_NUM, &uart_config);
ESP_ERROR_CHECK(err);
@ -319,17 +322,17 @@ void provisioning_config_uart()
// --- Key handling
bool provisioning_have_keys()
bool TTNProvisioning::haveKeys()
{
return have_keys;
}
bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const char *app_key)
bool TTNProvisioning::decodeKeys(const char *dev_eui, const char *app_eui, const char *app_key)
{
return provisioning_decode(true, dev_eui, app_eui, app_key);
return decode(true, dev_eui, app_eui, app_key);
}
bool provisioning_from_mac(const char *app_eui, const char *app_key)
bool TTNProvisioning::fromMAC(const char *app_eui, const char *app_key)
{
uint8_t mac[6];
esp_err_t err = esp_efuse_mac_get_default(mac);
@ -344,33 +347,33 @@ bool provisioning_from_mac(const char *app_eui, const char *app_key)
global_dev_eui[1] = mac[4];
global_dev_eui[0] = mac[5];
return provisioning_decode(false, NULL, app_eui, app_key);
return decode(false, nullptr, app_eui, app_key);
}
bool provisioning_decode(bool incl_dev_eui, const char *dev_eui, const char *app_eui, const char *app_key)
bool TTNProvisioning::decode(bool incl_dev_eui, const char *dev_eui, const char *app_eui, const char *app_key)
{
uint8_t buf_dev_eui[8];
uint8_t buf_app_eui[8];
uint8_t buf_app_key[16];
if (incl_dev_eui && (strlen(dev_eui) != 16 || !hex_str_to_bin(dev_eui, buf_dev_eui, 8)))
if (incl_dev_eui && (strlen(dev_eui) != 16 || !hexStrToBin(dev_eui, buf_dev_eui, 8)))
{
ESP_LOGW(TAG, "Invalid device EUI: %s", dev_eui);
return false;
}
if (incl_dev_eui)
swap_bytes(buf_dev_eui, 8);
swapBytes(buf_dev_eui, 8);
if (strlen(app_eui) != 16 || !hex_str_to_bin(app_eui, buf_app_eui, 8))
if (strlen(app_eui) != 16 || !hexStrToBin(app_eui, buf_app_eui, 8))
{
ESP_LOGW(TAG, "Invalid application EUI: %s", app_eui);
return false;
}
swap_bytes(buf_app_eui, 8);
swapBytes(buf_app_eui, 8);
if (strlen(app_key) != 32 || !hex_str_to_bin(app_key, buf_app_key, 16))
if (strlen(app_key) != 32 || !hexStrToBin(app_key, buf_app_key, 16))
{
ESP_LOGW(TAG, "Invalid application key: %s", app_key);
return false;
@ -381,11 +384,11 @@ bool provisioning_decode(bool incl_dev_eui, const char *dev_eui, const char *app
memcpy(global_app_eui, buf_app_eui, sizeof(global_app_eui));
memcpy(global_app_key, buf_app_key, sizeof(global_app_key));
have_keys = !is_all_zeroes(global_dev_eui, sizeof(global_dev_eui))
&& !is_all_zeroes(global_app_eui, sizeof(global_app_eui))
&& !is_all_zeroes(global_app_key, sizeof(global_app_key));
have_keys = !isAllZeros(global_dev_eui, sizeof(global_dev_eui))
&& !isAllZeros(global_app_eui, sizeof(global_app_eui))
&& !isAllZeros(global_app_key, sizeof(global_app_key));
if (!provisioning_save_keys())
if (!saveKeys())
return false;
return true;
@ -394,7 +397,7 @@ bool provisioning_decode(bool incl_dev_eui, const char *dev_eui, const char *app
// --- Non-volatile storage
bool provisioning_save_keys()
bool TTNProvisioning::saveKeys()
{
bool result = false;
@ -409,13 +412,13 @@ bool provisioning_save_keys()
if (res != ESP_OK)
goto done;
if (!write_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, global_dev_eui, sizeof(global_dev_eui)))
if (!writeNvsValue(handle, NVS_FLASH_KEY_DEV_EUI, global_dev_eui, sizeof(global_dev_eui)))
goto done;
if (!write_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, global_app_eui, sizeof(global_app_eui)))
if (!writeNvsValue(handle, NVS_FLASH_KEY_APP_EUI, global_app_eui, sizeof(global_app_eui)))
goto done;
if (!write_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, global_app_key, sizeof(global_app_key)))
if (!writeNvsValue(handle, NVS_FLASH_KEY_APP_KEY, global_app_key, sizeof(global_app_key)))
goto done;
res = nvs_commit(handle);
@ -429,7 +432,7 @@ done:
return result;
}
bool provisioning_restore_keys(bool silent)
bool TTNProvisioning::restoreKeys(bool silent)
{
uint8_t buf_dev_eui[8];
uint8_t buf_app_eui[8];
@ -448,22 +451,22 @@ bool provisioning_restore_keys(bool silent)
if (res != ESP_OK)
goto done;
if (!read_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, buf_dev_eui, sizeof(global_dev_eui), silent))
if (!readNvsValue(handle, NVS_FLASH_KEY_DEV_EUI, buf_dev_eui, sizeof(global_dev_eui), silent))
goto done;
if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, buf_app_eui, sizeof(global_app_eui), silent))
if (!readNvsValue(handle, NVS_FLASH_KEY_APP_EUI, buf_app_eui, sizeof(global_app_eui), silent))
goto done;
if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, buf_app_key, sizeof(global_app_key), silent))
if (!readNvsValue(handle, NVS_FLASH_KEY_APP_KEY, buf_app_key, sizeof(global_app_key), silent))
goto done;
memcpy(global_dev_eui, buf_dev_eui, sizeof(global_dev_eui));
memcpy(global_app_eui, buf_app_eui, sizeof(global_app_eui));
memcpy(global_app_key, buf_app_key, sizeof(global_app_key));
have_keys = !is_all_zeroes(global_dev_eui, sizeof(global_dev_eui))
&& !is_all_zeroes(global_app_eui, sizeof(global_app_eui))
&& !is_all_zeroes(global_app_key, sizeof(global_app_key));
have_keys = !isAllZeros(global_dev_eui, sizeof(global_dev_eui))
&& !isAllZeros(global_app_eui, sizeof(global_app_eui))
&& !isAllZeros(global_app_key, sizeof(global_app_key));
if (have_keys)
{
@ -479,7 +482,7 @@ done:
return true;
}
bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent)
bool TTNProvisioning::readNvsValue(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent)
{
size_t size = expected_length;
esp_err_t res = nvs_get_blob(handle, key, data, &size);
@ -504,10 +507,10 @@ bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t ex
return false;
}
bool write_nvs_value(nvs_handle handle, const char* key, const uint8_t* data, size_t len)
bool TTNProvisioning::writeNvsValue(nvs_handle handle, const char* key, const uint8_t* data, size_t len)
{
uint8_t buf[16];
if (read_nvs_value(handle, key, buf, len, true) && memcmp(buf, data, len) == 0)
if (readNvsValue(handle, key, buf, len, true) && memcmp(buf, data, len) == 0)
return true; // unchanged
esp_err_t res = nvs_set_blob(handle, key, data, len);
@ -519,12 +522,12 @@ bool write_nvs_value(nvs_handle handle, const char* key, const uint8_t* data, si
// --- Helper functions ---
bool hex_str_to_bin(const char *hex, uint8_t *buf, int len)
bool TTNProvisioning::hexStrToBin(const char *hex, uint8_t *buf, int len)
{
const char* ptr = hex;
for (int i = 0; i < len; i++)
{
int val = hex_tuple_to_byte(ptr);
int val = hexTupleToByte(ptr);
if (val < 0)
return false;
buf[i] = val;
@ -533,18 +536,18 @@ bool hex_str_to_bin(const char *hex, uint8_t *buf, int len)
return true;
}
int hex_tuple_to_byte(const char *hex)
int TTNProvisioning::hexTupleToByte(const char *hex)
{
int nibble1 = hex_digit_to_val(hex[0]);
int nibble1 = hexDigitToVal(hex[0]);
if (nibble1 < 0)
return -1;
int nibble2 = hex_digit_to_val(hex[1]);
int nibble2 = hexDigitToVal(hex[1]);
if (nibble2 < 0)
return -1;
return (nibble1 << 4) | nibble2;
}
int hex_digit_to_val(char ch)
int TTNProvisioning::hexDigitToVal(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
@ -555,24 +558,24 @@ int hex_digit_to_val(char ch)
return -1;
}
void bin_to_hex_str(const uint8_t* buf, int len, char* hex)
void TTNProvisioning::binToHexStr(const uint8_t* buf, int len, char* hex)
{
for (int i = 0; i < len; i++)
{
uint8_t b = buf[i];
*hex = val_to_hex_digit((b & 0xf0) >> 4);
*hex = valToHexDigit((b & 0xf0) >> 4);
hex++;
*hex = val_to_hex_digit(b & 0x0f);
*hex = valToHexDigit(b & 0x0f);
hex++;
}
}
char val_to_hex_digit(int val)
char TTNProvisioning::valToHexDigit(int val)
{
return "0123456789ABCDEF"[val];
}
void swap_bytes(uint8_t* buf, int len)
void TTNProvisioning::swapBytes(uint8_t* buf, int len)
{
uint8_t* p1 = buf;
uint8_t* p2 = buf + len - 1;
@ -586,7 +589,7 @@ void swap_bytes(uint8_t* buf, int len)
}
}
bool is_all_zeroes(const uint8_t* buf, int len)
bool TTNProvisioning::isAllZeros(const uint8_t* buf, int len)
{
for (int i = 0; i < len; i++)
if (buf[i] != 0)

73
src/TTNProvisioning.h Normal file
View File

@ -0,0 +1,73 @@
/*******************************************************************************
*
* 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
*
* Task listening on a UART port for provisioning commands.
*******************************************************************************/
#ifndef _ttnprovisioning_h_
#define _ttnprovisioning_h_
#include "lmic/oslmic.h"
#include "nvs_flash.h"
class TTNProvisioning
{
public:
TTNProvisioning();
bool haveKeys();
bool decodeKeys(const char *dev_eui, const char *app_eui, const char *app_key);
bool fromMAC(const char *app_eui, const char *app_key);
bool saveKeys();
bool restoreKeys(bool silent);
#if defined(TTN_HAS_AT_COMMANDS)
void startTask();
#endif
private:
bool decode(bool incl_dev_eui, const char *dev_eui, const char *app_eui, const char *app_key);
bool readNvsValue(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent);
bool writeNvsValue(nvs_handle handle, const char* key, const uint8_t* data, size_t len);
#if defined(TTN_HAS_AT_COMMANDS)
void provisioningTask();
void addLineData(int numBytes);
void detectLineEnd(int start_at);
void processLine();
#endif
#if defined(TTN_CONFIG_UART)
void configUART();
#endif
static bool hexStrToBin(const char *hex, uint8_t *buf, int len);
static int hexTupleToByte(const char *hex);
static int hexDigitToVal(char ch);
static void binToHexStr(const uint8_t* buf, int len, char* hex);
static char valToHexDigit(int val);
static void swapBytes(uint8_t* buf, int len);
static bool isAllZeros(const uint8_t* buf, int len);
private:
bool have_keys = false;
#if defined(TTN_HAS_AT_COMMANDS)
QueueHandle_t uart_queue;
char* line_buf;
int line_length;
uint8_t last_line_end_char;
bool quit_task;
friend void ttn_provisioning_task_caller(void* pvParameter);
#endif
};
#endif

View File

@ -2,7 +2,7 @@
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018 Manuel Bleichenbacher
* Copyright (c) 2018-2019 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
@ -13,38 +13,77 @@
#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"
#include "TheThingsNetwork.h"
#include "TTNProvisioning.h"
#include "TTNLogging.h"
enum ClientAction
/**
* @brief Reason the user code is waiting
*/
enum TTNWaitingReason
{
eActionUnrelated,
eActionJoining,
eActionSending
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 resultQueue;
static ClientAction clientAction = eActionUnrelated;
static QueueHandle_t lmicEventQueue = nullptr;
static TTNWaitingReason waitingReason = eWaitingNone;
static TTNProvisioning provisioning;
#if LMIC_ENABLE_event_logging
static TTNLogging* logging;
#endif
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(NULL)
: messageCallback(nullptr)
{
#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);
ASSERT(ttnInstance == nullptr);
ttnInstance = this;
hal_initCriticalSection();
ttn_hal.initCriticalSection();
}
TheThingsNetwork::~TheThingsNetwork()
@ -54,70 +93,100 @@ 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)
{
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;
ttn_hal.configurePins(spi_host, nss, rxtx, rst, dio0, dio1);
os_init();
#if LMIC_ENABLE_event_logging
logging = TTNLogging::initInstance();
#endif
LMIC_registerEventCb(eventCallback, nullptr);
LMIC_registerRxMessageCb(messageReceivedCallback, nullptr);
os_init_ex(nullptr);
reset();
resultQueue = xQueueCreate(12, sizeof(int));
ASSERT(resultQueue != NULL);
hal_startBgTask();
lmicEventQueue = xQueueCreate(4, sizeof(TTNLmicEvent));
ASSERT(lmicEventQueue != nullptr);
ttn_hal.startLMICTask();
}
void TheThingsNetwork::reset()
{
hal_enterCriticalSection();
ttn_hal.enterCriticalSection();
LMIC_reset();
hal_leaveCriticalSection();
LMIC_setClockError(MAX_CLOCK_ERROR * 4 / 100);
waitingReason = eWaitingNone;
ttn_hal.leaveCriticalSection();
}
void TheThingsNetwork::shutdown()
{
ttn_hal.enterCriticalSection();
LMIC_shutdown();
ttn_hal.stopLMICTask();
waitingReason = eWaitingNone;
ttn_hal.leaveCriticalSection();
}
void TheThingsNetwork::startup()
{
ttn_hal.enterCriticalSection();
LMIC_reset();
ttn_hal.startLMICTask();
ttn_hal.leaveCriticalSection();
}
bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey)
{
if (!provisioning_decode_keys(devEui, appEui, appKey))
if (!provisioning.decodeKeys(devEui, appEui, appKey))
return false;
return provisioning_save_keys();
return provisioning.saveKeys();
}
bool TheThingsNetwork::provisionWithMAC(const char *appEui, const char *appKey)
{
if (!provisioning_from_mac(appEui, appKey))
if (!provisioning.fromMAC(appEui, appKey))
return false;
return provisioning_save_keys();
return provisioning.saveKeys();
}
void TheThingsNetwork::startProvisioningTask()
{
#if !defined(CONFIG_TTN_PROVISION_UART_NONE)
provisioning_start_task();
#if defined(TTN_HAS_AT_COMMANDS)
provisioning.startTask();
#else
ESP_LOGE(TAG, "AT commands are disabled. Change the configuration using 'make menuconfig'");
ASSERT(0);
esp_restart();
#endif
}
void TheThingsNetwork::waitForProvisioning()
{
#if !defined(CONFIG_TTN_PROVISION_UART_NONE)
#if defined(TTN_HAS_AT_COMMANDS)
if (isProvisioned())
{
ESP_LOGI(TAG, "Device is already provisioned");
return;
}
while (!provisioning_have_keys())
vTaskDelay(1000 / portTICK_PERIOD_MS);
while (!provisioning.haveKeys())
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 (!provisioning_decode_keys(devEui, appEui, appKey))
if (!provisioning.decodeKeys(devEui, appEui, appKey))
return false;
return joinCore();
@ -125,9 +194,9 @@ bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char *
bool TheThingsNetwork::join()
{
if (!provisioning_have_keys())
if (!provisioning.haveKeys())
{
if (!provisioning_restore_keys(false))
if (!provisioning.restoreKeys(false))
return false;
}
@ -136,58 +205,62 @@ bool TheThingsNetwork::join()
bool TheThingsNetwork::joinCore()
{
if (!provisioning_have_keys())
if (!provisioning.haveKeys())
{
ESP_LOGW(TAG, "Device EUI, App EUI and/or App key have not been provided");
return false;
}
hal_enterCriticalSection();
clientAction = eActionJoining;
ttn_hal.enterCriticalSection();
xQueueReset(lmicEventQueue);
waitingReason = eWaitingForJoin;
LMIC_startJoining();
hal_wakeUp();
hal_leaveCriticalSection();
ttn_hal.wakeUp();
ttn_hal.leaveCriticalSection();
int result = 0;
xQueueReceive(resultQueue, &result, portMAX_DELAY);
return result == EV_JOINED;
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_enterCriticalSection();
if (LMIC.opmode & OP_TXRXPEND)
ttn_hal.enterCriticalSection();
if (waitingReason != eWaitingNone || (LMIC.opmode & OP_TXRXPEND) != 0)
{
hal_leaveCriticalSection();
ttn_hal.leaveCriticalSection();
return kTTNErrorTransmissionFailed;
}
clientAction = eActionSending;
waitingReason = eWaitingForTransmission;
LMIC.client.txMessageCb = messageTransmittedCallback;
LMIC.client.txMessageUserData = nullptr;
LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm);
hal_wakeUp();
hal_leaveCriticalSection();
ttn_hal.wakeUp();
ttn_hal.leaveCriticalSection();
int result = 0;
xQueueReceive(resultQueue, &result, portMAX_DELAY);
if (result == EV_TXCOMPLETE)
while (true)
{
bool hasRecevied = (LMIC.txrxFlags & (TXRX_DNW1 | TXRX_DNW2)) != 0;
if (hasRecevied && messageCallback != NULL)
TTNLmicEvent result;
xQueueReceive(lmicEventQueue, &result, portMAX_DELAY);
switch (result.event)
{
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);
case eEvtMessageReceived:
if (messageCallback != nullptr)
messageCallback(result.message, result.messageSize, result.port);
break;
case eEvtTransmissionCompleted:
return kTTNSuccessfulTransmission;
case eEvtTransmissionFailed:
return kTTNErrorTransmissionFailed;
default:
ASSERT(0);
}
return kTTNSuccessfulTransmission;
}
return kTTNErrorTransmissionFailed;
}
void TheThingsNetwork::onMessage(TTNMessageCallback callback)
@ -198,55 +271,157 @@ void TheThingsNetwork::onMessage(TTNMessageCallback callback)
bool TheThingsNetwork::isProvisioned()
{
if (provisioning_have_keys())
if (provisioning.haveKeys())
return true;
provisioning_restore_keys(true);
provisioning.restoreKeys(true);
return provisioning_have_keys();
return provisioning.haveKeys();
}
void TheThingsNetwork::setRSSICal(int8_t rssiCal)
{
ttn_hal.rssiCal = 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;
}
// --- LMIC functions ---
// --- Callbacks ---
#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"
};
#if CONFIG_LOG_DEFAULT_LEVEL >= 3 || LMIC_ENABLE_event_logging
const char *eventNames[] = { LMIC_EVENT_NAME_TABLE__INIT };
#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");
// 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
logging->logEvent(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 (clientAction == eActionUnrelated)
{
if (ttnEvent == eEvtNone)
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);
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));
}

3
src/aes/lmic_aes.c Executable file → Normal file
View File

@ -262,9 +262,6 @@ u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
u4_t a0, a1, a2, a3;
u4_t t0, t1, t2, t3;
u4_t *ki, *ke;
// ttn-esp32 change: prevent error 'x' may be used uninitialized in this function
a0 = a1 = a2 = a3 = 0;
t0 = t1 = 0;
// load input block
if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block

View File

@ -17,9 +17,17 @@
#elif defined(CONFIG_TTN_LORA_FREQ_US_915)
#define CFG_us915 1
#elif defined(CONFIG_TTN_LORA_FREQ_AU_921)
#define CFG_au921 1
# warning "CONFIG_TTN_LORA_FREQ_AU_921 was deprecated in favour of CONFIG_TTN_LORA_FREQ_AU_921. Support for CONFIG_TTN_LORA_FREQ_AU_921 will be removed in the future."
#define CFG_au915 1
#elif defined(CONFIG_TTN_LORA_FREQ_AU_915)
#define CFG_au915 1
#elif defined(CONFIG_TTN_LORA_FREQ_AS_923)
#define CFG_as923 1
#elif defined(CONFIG_TTN_LORA_FREQ_AS_923_JP)
#define CFG_as923 1
#define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP
#elif defined(CONFIG_TTN_LORA_FREQ_KR_920)
#define CFG_kr920 1
#elif defined(CONFIG_TTN_LORA_FREQ_IN_866)
#define CFG_in866 1
#else
@ -35,25 +43,13 @@
#error TTN LoRa radio chip must be configured using 'make menuconfig'
#endif
#if defined(CONFIG_TTN_TIMER_0_GROUP_0)
#define TTN_TIMER TIMER_0
#define TTN_TIMER_GROUP TIMER_GROUP_0
#define TTN_CLEAR_TIMER_ALARM TIMERG0.int_clr_timers.t0 = 1
#elif defined(CONFIG_TTN_TIMER_1_GROUP_0)
#define TTN_TIMER TIMER_1
#define TTN_TIMER_GROUP TIMER_GROUP_0
#define TTN_CLEAR_TIMER_ALARM TIMERG0.int_clr_timers.t1 = 1
#elif defined(CONFIG_TTN_TIMER_0_GROUP_1)
#define TTN_TIMER TIMER_0
#define TTN_TIMER_GROUP TIMER_GROUP_1
#define TTN_CLEAR_TIMER_ALARM TIMERG1.int_clr_timers.t0 = 1
#elif defined(CONFIG_TTN_TIMER_1_GROUP_1)
#define TTN_TIMER TIMER_1
#define TTN_TIMER_GROUP TIMER_GROUP_1
#define TTN_CLEAR_TIMER_ALARM TIMERG1.int_clr_timers.t1 = 1
#else
#error TTN timer must be configured using 'make menuconfig'
#if !defined(CONFIG_TTN_PROVISION_UART_NONE)
#define TTN_HAS_AT_COMMANDS 1
#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES)
#define TTN_CONFIG_UART 1
#endif
#endif
// 16 μs per tick
// LMIC requires ticks to be 15.5μs - 100 μs long
@ -67,6 +63,8 @@
#include <stdio.h>
#endif
#define LMIC_ENABLE_onEvent 0
#define DISABLE_PING
#define DISABLE_BEACONS

View File

@ -1,470 +0,0 @@
/*******************************************************************************
*
* 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
*
* 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
static const char *TAG = "ttn_hal";
lmic_pinmap lmic_pins;
typedef enum {
DIO0 = 0,
DIO1,
DIO2,
TIMER,
WAKEUP
} event_t;
typedef struct {
ostime_t time;
event_t ev;
} queue_item_t;
// -----------------------------------------------------------------------------
// I/O
static QueueHandle_t dio_queue;
void IRAM_ATTR dio_irq_handler(void *arg)
{
uint64_t now;
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
event_t ev = (long)arg;
BaseType_t higher_prio_task_woken = pdFALSE;
queue_item_t item = {
.time = (ostime_t)now,
.ev = ev
};
xQueueSendFromISR(dio_queue, &item, &higher_prio_task_woken);
if (higher_prio_task_woken)
portYIELD_FROM_ISR();
}
static void hal_io_init()
{
// NSS and DIO0 and DIO1 are required
ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN);
ASSERT(lmic_pins.dio0 != LMIC_UNUSED_PIN);
ASSERT(lmic_pins.dio1 != LMIC_UNUSED_PIN);
gpio_pad_select_gpio(lmic_pins.nss);
gpio_set_level(lmic_pins.nss, 0);
gpio_set_direction(lmic_pins.nss, GPIO_MODE_OUTPUT);
if (lmic_pins.rxtx != LMIC_UNUSED_PIN)
{
gpio_pad_select_gpio(lmic_pins.rxtx);
gpio_set_level(lmic_pins.rxtx, 0);
gpio_set_direction(lmic_pins.rxtx, GPIO_MODE_OUTPUT);
}
if (lmic_pins.rst != LMIC_UNUSED_PIN)
{
gpio_pad_select_gpio(lmic_pins.rst);
gpio_set_level(lmic_pins.rst, 0);
gpio_set_direction(lmic_pins.rst, GPIO_MODE_OUTPUT);
}
dio_queue = xQueueCreate(12, sizeof(queue_item_t));
ASSERT(dio_queue != NULL);
gpio_pad_select_gpio(lmic_pins.dio0);
gpio_set_direction(lmic_pins.dio0, GPIO_MODE_INPUT);
gpio_set_intr_type(lmic_pins.dio0, GPIO_INTR_POSEDGE);
gpio_isr_handler_add(lmic_pins.dio0, dio_irq_handler, (void *)0);
gpio_pad_select_gpio(lmic_pins.dio1);
gpio_set_direction(lmic_pins.dio1, GPIO_MODE_INPUT);
gpio_set_intr_type(lmic_pins.dio1, GPIO_INTR_POSEDGE);
gpio_isr_handler_add(lmic_pins.dio1, dio_irq_handler, (void *)1);
ESP_LOGI(TAG, "IO initialized");
}
void hal_pin_rxtx(u1_t val)
{
if (lmic_pins.rxtx == LMIC_UNUSED_PIN)
return;
gpio_set_level(lmic_pins.rxtx, val);
}
void hal_pin_rst(u1_t val)
{
if (lmic_pins.rst == LMIC_UNUSED_PIN)
return;
if (val == 0 || val == 1)
{ // drive pin
gpio_set_level(lmic_pins.rst, val);
gpio_set_direction(lmic_pins.rst, GPIO_MODE_OUTPUT);
}
else
{ // keep pin floating
gpio_set_level(lmic_pins.rst, val);
gpio_set_direction(lmic_pins.rst, GPIO_MODE_INPUT);
}
}
s1_t hal_getRssiCal (void) {
return lmic_pins.rssi_cal;
}
// -----------------------------------------------------------------------------
// SPI
#define SPI_QUEUE_SIZE 4
#define SPI_NUM_TRX_SLOTS (SPI_QUEUE_SIZE + 1)
static spi_device_handle_t spi_handle;
static spi_transaction_t spi_trx_queue[SPI_NUM_TRX_SLOTS];
static int spi_trx_queue_head = 0;
static int spi_num_outstanding_trx = 0;
static spi_transaction_t* get_next_spi_trx_desc()
{
spi_transaction_t* trx = spi_trx_queue + spi_trx_queue_head;
memset(trx, 0, sizeof(spi_transaction_t));
return trx;
}
static void collect_spi_result()
{
int head = spi_trx_queue_head;
int tail = head - spi_num_outstanding_trx;
if (tail < 0)
tail += SPI_NUM_TRX_SLOTS;
spi_transaction_t* trx;
esp_err_t err = spi_device_get_trans_result(spi_handle, &trx, 100 / portTICK_PERIOD_MS);
ESP_ERROR_CHECK(err);
ASSERT(trx == spi_trx_queue + tail);
spi_num_outstanding_trx--;
}
static void submit_spi_trx()
{
if (spi_num_outstanding_trx >= SPI_QUEUE_SIZE)
collect_spi_result();
int head = spi_trx_queue_head;
esp_err_t err = spi_device_queue_trans(spi_handle, spi_trx_queue + head, 100 / portTICK_PERIOD_MS);
ESP_ERROR_CHECK(err);
spi_num_outstanding_trx++;
head++;
if (head >= SPI_NUM_TRX_SLOTS)
head = 0;
spi_trx_queue_head = head;
}
static void hal_spi_init()
{
// init device
spi_device_interface_config_t spi_device_intf_config = {
.mode = 1,
.clock_speed_hz = CONFIG_TTN_SPI_FREQ,
.command_bits = 0,
.address_bits = 8,
.spics_io_num = lmic_pins.nss,
.queue_size = SPI_QUEUE_SIZE,
.cs_ena_posttrans = 2
};
esp_err_t ret = spi_bus_add_device(lmic_pins.spi_host, &spi_device_intf_config, &spi_handle);
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "SPI initialized");
}
void hal_spi_write(u1_t cmd, const u1_t *buf, int len)
{
spi_transaction_t* trx = get_next_spi_trx_desc();
trx->addr = cmd;
trx->length = 8 * len;
trx->tx_buffer = buf;
submit_spi_trx();
}
void hal_spi_read(u1_t cmd, u1_t *buf, int len)
{
memset(buf, 0, len);
spi_transaction_t* trx = get_next_spi_trx_desc();
trx->addr = cmd;
trx->length = 8 * len;
trx->rxlength = 8 * len;
trx->tx_buffer = buf;
trx->rx_buffer = buf;
submit_spi_trx();
while (spi_num_outstanding_trx > 0)
collect_spi_result();
}
// -----------------------------------------------------------------------------
// TIME
/*
* LIMIC uses a 32 bit time (ostime_t) counting ticks. In this implementation
* each tick is 16µs. So the timer will wrap around once every 19 hour.
* The timer alarm should trigger when a specific value has been reached.
* Due to the wrap around, an alarm time in the future can have a lower value
* than the current timer value.
*
* ESP32 has 64 bits counters with a pecularity: the alarm does not only
* trigger when the exact value has been reached but also when the clock is
* higer than the alarm value. Therefore, the wrap around is more difficult to
* handle.
*
* The approach here is to always use a higher value than the current timer
* value. If it would be lower than the timer value, 0x100000000 is added.
* The lower 32 bits still represent the desired value. After the timer has
* triggered an alarm and is higher than 0x100000000, it's value is reduced
* by 0x100000000.
*/
#define OVERRUN_TRESHOLD 0x10000 // approx 10 seconds
static uint64_t next_timer_event = 0x200000000;
static void IRAM_ATTR hal_timer_irq_handler(void *arg);
static void hal_time_init()
{
timer_config_t config = {
.alarm_en = false,
.counter_en = false,
.intr_type = TIMER_INTR_LEVEL,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = false,
.divider = 1280 /* 80 MHz APB_CLK * 16µs */
};
timer_init(TTN_TIMER_GROUP, TTN_TIMER, &config);
timer_set_counter_value(TTN_TIMER_GROUP, TTN_TIMER, 0x0);
timer_isr_register(TTN_TIMER_GROUP, TTN_TIMER, hal_timer_irq_handler, NULL, ESP_INTR_FLAG_IRAM, NULL);
timer_start(TTN_TIMER_GROUP, TTN_TIMER);
ESP_LOGI(TAG, "Timer initialized");
}
static void hal_prepare_next_alarm(u4_t time)
{
uint64_t now;
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
u4_t now32 = (u4_t)now;
if (now != now32)
{
// decrease timer to 32 bit value
now = now32;
timer_pause(TTN_TIMER_GROUP, TTN_TIMER);
timer_set_counter_value(TTN_TIMER_GROUP, TTN_TIMER, now);
timer_start(TTN_TIMER_GROUP, TTN_TIMER);
}
next_timer_event = time;
if (now32 > time && now32 - time > OVERRUN_TRESHOLD)
next_timer_event += 0x100000000;
}
static void hal_arm_timer()
{
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_DIS);
timer_set_alarm_value(TTN_TIMER_GROUP, TTN_TIMER, next_timer_event);
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_EN);
}
static void hal_disarm_timer()
{
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_DIS);
next_timer_event = 0x200000000; // wait indefinitely (almost)
}
static void IRAM_ATTR hal_timer_irq_handler(void *arg)
{
TTN_CLEAR_TIMER_ALARM;
BaseType_t higher_prio_task_woken = pdFALSE;
queue_item_t item = {
.ev = TIMER
};
xQueueSendFromISR(dio_queue, &item, &higher_prio_task_woken);
if (higher_prio_task_woken)
portYIELD_FROM_ISR();
}
typedef enum {
CHECK_IO,
WAIT_FOR_ANY_EVENT,
WAIT_FOR_TIMER
} wait_open_e;
static bool hal_wait(wait_open_e wait_option)
{
TickType_t ticks_to_wait = wait_option == CHECK_IO ? 0 : portMAX_DELAY;
while (true)
{
queue_item_t item;
if (xQueueReceive(dio_queue, &item, ticks_to_wait) == pdFALSE)
return false;
if (item.ev == WAKEUP)
{
if (wait_option != WAIT_FOR_TIMER)
{
hal_disarm_timer();
return true;
}
}
else if (item.ev == TIMER)
{
hal_disarm_timer();
if (wait_option != CHECK_IO)
return true;
}
else // IO interrupt
{
if (wait_option != WAIT_FOR_TIMER)
hal_disarm_timer();
hal_enterCriticalSection();
radio_irq_handler_v2(item.ev, item.time);
hal_leaveCriticalSection();
if (wait_option != WAIT_FOR_TIMER)
return true;
}
}
}
u4_t hal_ticks()
{
uint64_t val;
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &val);
return (u4_t)val;
}
void hal_waitUntil(u4_t time)
{
hal_prepare_next_alarm(time);
hal_arm_timer();
hal_wait(WAIT_FOR_TIMER);
}
void hal_wakeUp()
{
queue_item_t item = {
.ev = WAKEUP
};
xQueueSend(dio_queue, &item, 0);
}
// check and rewind for target time
u1_t hal_checkTimer(u4_t time)
{
uint64_t now;
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
u4_t now32 = (u4_t)now;
if (time >= now32)
{
if (time - now32 < 5)
return 1; // timer will expire very soon
}
else
{
if (now32 - time < OVERRUN_TRESHOLD)
return 1; // timer has expired recently
}
hal_prepare_next_alarm(time);
return 0;
}
void hal_sleep()
{
if (hal_wait(CHECK_IO))
return;
hal_arm_timer();
hal_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
}
// -----------------------------------------------------------------------------
// Synchronization between application code and background task
static SemaphoreHandle_t mutex;
void hal_initCriticalSection()
{
mutex = xSemaphoreCreateRecursiveMutex();
}
void hal_enterCriticalSection()
{
xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
}
void hal_leaveCriticalSection()
{
xSemaphoreGiveRecursive(mutex);
}
// -----------------------------------------------------------------------------
static void hal_bgTask(void* pvParameter) {
os_runloop();
}
void hal_init_ex(const void *pContext)
{
// configure radio I/O and interrupt handler
hal_io_init();
// configure radio SPI
hal_spi_init();
// configure timer and interrupt handler
hal_time_init();
}
void hal_startBgTask() {
xTaskCreate(hal_bgTask, "ttn_lora_task", 1024 * 4, NULL, CONFIG_TTN_BG_TASK_PRIO, NULL);
}
void hal_failed(const char *file, u2_t line)
{
ESP_LOGE(TAG, "%s:%d", file, line);
ASSERT(0);
}

529
src/hal/hal_esp32.cpp Executable file
View File

@ -0,0 +1,529 @@
/*******************************************************************************
*
* 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 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);
}
}

View File

@ -2,45 +2,93 @@
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018 Manuel Bleichenbacher
* 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.
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-IDF.
*******************************************************************************/
#ifndef _hal_esp32_h_
#define _hal_esp32_h_
#include <stdint.h>
#include "driver/spi_master.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct lmic_pinmap {
spi_host_device_t spi_host;
uint8_t nss;
uint8_t rxtx;
uint8_t rst;
uint8_t dio0;
uint8_t dio1;
int8_t rssi_cal; // cal in dB -- added to RSSI measured prior to decision. Must include noise guardband!
} lmic_pinmap;
extern lmic_pinmap lmic_pins;
void hal_startBgTask();
void hal_wakeUp();
void hal_initCriticalSection();
void hal_enterCriticalSection();
void hal_leaveCriticalSection();
#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>
#ifdef __cplusplus
}
#endif
enum WaitKind {
CHECK_IO,
WAIT_FOR_ANY_EVENT,
WAIT_FOR_TIMER
};
class HAL_ESP32
{
public:
HAL_ESP32();
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;
#endif // _hal_esp32_h_

69
src/lmic/config.h Executable file → Normal file
View File

@ -116,22 +116,16 @@
// define these in lmic_project_config.h to disable the corresponding MAC commands.
// Class A
//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap
//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param
//#define DISABLE_MCMD_SNCH_REQ // set new channel
//#define DISABLE_MCMD_DutyCycleReq // duty cycle cap
//#define DISABLE_MCMD_RXParamSetupReq // 2nd DN window param
//#define DISABLE_MCMD_NewChannelReq // set new channel
//#define DISABLE_MCMD_DlChannelReq // set downlink channel for RX1 for given uplink channel.
//#define DISABLE_MCMD_RXTimingSetupReq // delay between TX and RX
// Class B
//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING
//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatically disabled by DISABLE_BEACON
//#define DISABLE_MCMD_PingSlotChannelReq // set ping freq, automatically disabled by DISABLE_PING
//#define ENABLE_MCMD_BeaconTimingAns // next beacon start, DEPRECATED, normally disabled by DISABLE_BEACON
// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the
// same on RX. This ensures that gateways can talk to nodes and vice
// versa, but gateways will not hear other gateways and nodes will not
// hear other nodes. By defining this macro in lmic_project_config.h,
// this inversion is disabled and this node can hear other nodes. If
// two nodes both have this macro set, they can talk to each other
// (but they can no longer hear gateways). This should probably only
// be used when debugging and/or when talking to the radio directly
// (e.g. like in the "raw" example).
// DEPRECATED(tmm@mcci.com); replaced by LMIC.noRXIQinversion (dynamic). Don't define this.
//#define DISABLE_INVERT_IQ_ON_RX
// This allows choosing between multiple included AES implementations.
@ -171,4 +165,51 @@
# endif // defined(LMIC_DISABLE_DR_LEGACY)
#endif // LMIC_DR_LEGACY
// LMIC_ENABLE_DeviceTimeReq
// enable support for MCMD_DeviceTimeReq and MCMD_DeviceTimeAns
// this is always defined, and non-zero to enable it.
#if !defined(LMIC_ENABLE_DeviceTimeReq)
# define LMIC_ENABLE_DeviceTimeReq 0
#endif
// LMIC_ENABLE_user_events
// Enable/disable support for programmable callbacks for events, rx, and tx.
// This is always defined, and non-zero to enable. Default is enabled.
#if !defined(LMIC_ENABLE_user_events)
# define LMIC_ENABLE_user_events 1
#endif
// LMIC_ENABLE_onEvent
// Enable/disable support for out-call to user-supplied `onEvent()` function.
// This is always defined, and non-zero to enable. Default is enabled.
#if !defined(LMIC_ENABLE_onEvent)
# define LMIC_ENABLE_onEvent 1
#endif
// LMIC_ENABLE_long_messages
// LMIC certification requires that this be enabled.
#if !defined(LMIC_ENABLE_long_messages)
# define LMIC_ENABLE_long_messages 1 /* PARAM */
#endif
// LMIC_ENABLE_event_logging
// LMIC debugging for certification tests requires this, because debug prints affect
// timing too dramatically. But normal operation doesn't need this.
#if !defined(LMIC_ENABLE_event_logging)
# define LMIC_ENABLE_event_logging 0 /* PARAM */
#endif
// LMIC_LORAWAN_SPEC_VERSION
#if !defined(LMIC_LORAWAN_SPEC_VERSION)
# define LMIC_LORAWAN_SPEC_VERSION LMIC_LORAWAN_SPEC_VERSION_1_0_3
#endif
// LMIC_ENABLE_arbitrary_clock_error
// We normally don't want to allow users to set wide clock errors, because
// we assume reasonably-disciplined os_getTime() values. But... there might
// be platforms that require wider errors.
#if !defined(LMIC_ENABLE_arbitrary_clock_error)
# define LMIC_ENABLE_arbitrary_clock_error 0 /* PARAM */
#endif
#endif // _lmic_config_h_

108
src/lmic/hal.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2016, 2018 MCCI Corporation.
* Copyright (c) 2016, 2018-2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,30 +26,40 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _hal_hpp_
#define _hal_hpp_
#ifndef _lmic_hal_h_
#define _lmic_hal_h_
#ifndef _oslmic_types_h_
# include "oslmic_types.h"
#endif
#ifndef _lmic_env_h_
# include "lmic_env.h"
#endif
#ifdef __cplusplus
extern "C"{
#endif
// The type of an optional user-defined failure handler routine
typedef void LMIC_ABI_STD hal_failure_handler_t(const char* const file, const uint16_t line);
/*
* initialize hardware (IO, SPI, TIMER, IRQ).
* This API is deprecated as it uses the const global lmic_pins,
* which the platform can't control or change.
*/
void hal_init (void);
/*
* initialize hardware, passing in platform-specific context
* Initialize hardware, passing in platform-specific context
* The pointer is to a HalPinmap_t.
*/
void hal_init_ex (const void *pContext);
/*
* drive radio NSS pin (0=low, 1=high).
*/
void hal_pin_nss (u1_t val);
/*
* drive radio RX/TX pins (0=rx, 1=tx).
* drive radio RX/TX pins (0=rx, 1=tx). Actual polarity
* is determined by the value of HalPinmap_t::rxtx_rx_active.
*/
void hal_pin_rxtx (u1_t val);
@ -58,29 +68,19 @@ void hal_pin_rxtx (u1_t val);
*/
void hal_pin_rst (u1_t val);
// BEGIN ttn-esp32 change
// use higher level SPI functions
/*
* perform SPI write transaction with radio
* Perform SPI write transaction with radio chip
* - write the command byte 'cmd'
* - write 'len' bytes in 'buf'
* - write 'len' bytes out of 'buf'
*/
void hal_spi_write(u1_t cmd, const u1_t* buf, int len);
void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len);
/*
* perform SPI read transaction with radio
* Perform SPI read transaction with radio chip
* - write the command byte 'cmd'
* - read 'len' bytes into 'buf'
*/
void hal_spi_read(u1_t cmd, u1_t* buf, int len);
/*
* perform 8-bit SPI transaction with radio.
* - write given byte 'outval'
* - read byte and return value
*/
//u1_t hal_spi (u1_t outval);
// END ttn-esp32 change
void hal_spi_read(u1_t cmd, u1_t* buf, size_t len);
/*
* disable all CPU interrupts.
@ -94,6 +94,11 @@ void hal_disableIRQs (void);
*/
void hal_enableIRQs (void);
/*
* return CPU interrupt nesting count
*/
uint8_t hal_getIrqLevel (void);
/*
* put system and CPU in low-power mode, sleep until interrupt.
*/
@ -105,9 +110,10 @@ void hal_sleep (void);
u4_t hal_ticks (void);
/*
* busy-wait until specified timestamp (in ticks) is reached.
* busy-wait until specified timestamp (in ticks) is reached. If on-time, return 0,
* otherwise return the number of ticks we were late.
*/
void hal_waitUntil (u4_t time);
u4_t hal_waitUntil (u4_t time);
/*
* check and rewind timer for target time.
@ -123,13 +129,59 @@ u1_t hal_checkTimer (u4_t targettime);
*/
void hal_failed (const char *file, u2_t line);
/*
* set a custom hal failure handler routine. The default behaviour, defined in
* hal_failed(), is to halt by looping infintely.
*/
void hal_set_failure_handler(const hal_failure_handler_t* const);
/*
* get the calibration value for radio_rssi
*/
s1_t hal_getRssiCal (void);
/*
* control the radio state
* - if val == 0, turn tcxo off and otherwise prepare for sleep
* - if val == 1, turn tcxo on and otherwise prep for activity
* - return the number of ticks that we need to wait
*/
ostime_t hal_setModuleActive (bit_t val);
/* find out if we're using Tcxo */
bit_t hal_queryUsingTcxo(void);
/* represent the various radio TX power policy */
enum {
LMICHAL_radio_tx_power_policy_rfo = 0,
LMICHAL_radio_tx_power_policy_paboost = 1,
LMICHAL_radio_tx_power_policy_20dBm = 2,
};
/*
* query the configuration as to the Tx Power Policy
* to be used on this board, given our desires and
* requested power.
*/
uint8_t hal_getTxPowerPolicy(
u1_t inputPolicy,
s1_t requestedPower,
u4_t freq
);
void hal_pollPendingIRQs_helper();
void hal_processPendingIRQs(void);
/// \brief check for any pending interrupts: stub if interrupts are enabled.
static void inline hal_pollPendingIRQs(void)
{
#if !defined(LMIC_USE_INTERRUPTS)
hal_pollPendingIRQs_helper();
#endif /* !defined(LMIC_USE_INTERRUPTS) */
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _hal_hpp_
#endif // _lmic_hal_h_

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2016 Matthijs Kooijman.
* Copyright (c) 2016-2018 MCCI Corporation.
* Copyright (c) 2016-2020 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -103,18 +103,18 @@ extern "C"{
// Arduino LMIC version
#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \
(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))
((((major)*UINT32_C(1)) << 24) | (((minor)*UINT32_C(1)) << 16) | (((patch)*UINT32_C(1)) << 8) | (((local)*UINT32_C(1)) << 0))
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(2, 2, 2, 0)
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 2, 0, 0) /* v3.2.0 */
#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \
(((v) >> 24u) & 0xFFu)
((((v)*UINT32_C(1)) >> 24u) & 0xFFu)
#define ARDUINO_LMIC_VERSION_GET_MINOR(v) \
(((v) >> 16u) & 0xFFu)
((((v)*UINT32_C(1)) >> 16u) & 0xFFu)
#define ARDUINO_LMIC_VERSION_GET_PATCH(v) \
(((v) >> 8u) & 0xFFu)
((((v)*UINT32_C(1)) >> 8u) & 0xFFu)
#define ARDUINO_LMIC_VERSION_GET_LOCAL(v) \
((v) & 0xFFu)
@ -122,14 +122,26 @@ extern "C"{
//! Only For Antenna Tuning Tests !
//#define CFG_TxContinuousMode 1
enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length
enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames
enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests
enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
// since this was annouunced as the API variable, we keep it. But it's not used,
// MAX_LEN_FRAME is what the code uses.
enum { MAX_FRAME_LEN = MAX_LEN_FRAME }; //!< Library cap on max frame length
enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link
LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead
LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate
enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames
enum { MAX_MISSED_BCNS = (2 * 60 * 60 + 127) / 128 }; //!< threshold for dropping out of class B, triggering rejoin requests
// note that we need 100 ppm timing accuracy for
// this, to keep the timing error to +/- 700ms.
enum { MAX_RXSYMS = 350 }; // Stop tracking beacon if sync error grows beyond this. A 0.4% clock error
// at SF9.125k means 512 ms; one sybol is 4.096 ms,
// so this needs to be at least 125 for an STM32L0.
// And for 100ppm clocks and 2 hours of beacon misses,
// this needs to accomodate 1.4 seconds of error at
// 4.096 ms/sym or at least 342 symbols.
enum { LINK_CHECK_CONT = 0 , // continue with this after reported dead link
LINK_CHECK_DEAD = 32 , // after this UP frames and no response to ack from NWK assume link is dead (ADR_ACK_DELAY)
LINK_CHECK_UNJOIN_MIN = LINK_CHECK_DEAD + 4, // this is the minimum value of LINK_CHECK_UNJOIN if we parameterize
LINK_CHECK_UNJOIN = LINK_CHECK_DEAD + (3 * 240), // after this many UP frames and no response, switch to join (by default)
LINK_CHECK_INIT = -64 , // UP frame count until we ask for ack (ADR_ACK_LIMIT)
LINK_CHECK_OFF =-128 }; // link check disabled
enum { TIME_RESYNC = 6*128 }; // secs
@ -153,24 +165,37 @@ struct band_t {
};
TYPEDEF_xref2band_t; //!< \internal
struct lmic_saved_adr_state_s {
u4_t channelFreq[MAX_CHANNELS];
u2_t channelMap;
};
#elif CFG_LMIC_US_like // US915 spectrum =================================================
enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable
struct lmic_saved_adr_state_s {
u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits
u2_t activeChannels125khz;
u2_t activeChannels500khz;
};
#endif // ==========================================================================
typedef struct lmic_saved_adr_state_s lmic_saved_adr_state_t;
// Keep in sync with evdefs.hpp::drChange
enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD, DRCHG_FRAMESIZE };
enum { KEEP_TXPOW = -128 };
#if !defined(DISABLE_PING)
//! \internal
struct rxsched_t {
u1_t dr;
dr_t dr;
u1_t intvExp; // 0..7
u1_t slot; // runs from 0 to 128
u1_t rxsyms;
rxsyms_t rxsyms;
ostime_t rxbase;
ostime_t rxtime; // start of next spot
u4_t freq;
@ -189,19 +214,19 @@ enum { BCN_NONE = 0x00, //!< No beacon received
//! Information about the last and previous beacons.
struct bcninfo_t {
ostime_t txtime; //!< Time when the beacon was sent
u4_t time; //!< GPS time in seconds of last beacon (received or surrogate)
s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set)
s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set)
s1_t rssi; //!< Adjusted RSSI value of last received beacon
s1_t snr; //!< Scaled SNR value of last received beacon
u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values.
u4_t time; //!< GPS time in seconds of last beacon (received or surrogate)
//
u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set)
s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set)
s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set)
};
#endif // !DISABLE_BEACONS
// purpose of receive window - lmic_t.rxState
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TX_AT=4, };
// Netid values / lmic_t.netid
enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
// MAC operation modes (lmic_t.opmode).
@ -220,52 +245,290 @@ enum { OP_NONE = 0x0000,
OP_NEXTCHNL = 0x0800, // find a new channel
OP_LINKDEAD = 0x1000, // link was reported as dead
OP_TESTMODE = 0x2000, // developer test mode
OP_UNJOIN = 0x4000, // unjoin and rejoin on next engineUpdate().
};
// TX-RX transaction flags - report back to user
enum { TXRX_ACK = 0x80, // confirmed UP frame was acked
TXRX_NACK = 0x40, // confirmed UP frame was not acked
TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port
TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
TXRX_DNW1 = 0x01, // received in 1st DN slot
TXRX_LENERR = 0x08, // set if frame was discarded due to length error.
TXRX_PING = 0x04, // received in a scheduled RX slot
TXRX_DNW2 = 0x02, // received in 2dn DN slot
TXRX_PING = 0x04 }; // received in a scheduled RX slot
TXRX_DNW1 = 0x01, // received in 1st DN slot
};
// Event types for event callback
enum _ev_t { EV_SCAN_TIMEOUT=1, 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 };
EV_TXSTART, EV_TXCANCELED, EV_RXSTART, EV_JOIN_TXCOMPLETE };
typedef enum _ev_t ev_t;
// this macro can be used to initalize a normal table of event strings
#define LMIC_EVENT_NAME_TABLE__INIT \
"<<zero>>", \
"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", "EV_TXCANCELED", "EV_RXSTART", "EV_JOIN_TXCOMPLETE"
// if working on an AVR (or worried about it), you can use this multi-zero
// string and put this in a single const F() string. Index through this
// counting up from 0, until you get to the entry you want or to an
// entry that begins with a \0.
#define LMIC_EVENT_NAME_MULTISZ__INIT \
"<<zero>>\0" \
"EV_SCAN_TIMEOUT\0" "EV_BEACON_FOUND\0" \
"EV_BEACON_MISSED\0" "EV_BEACON_TRACKED\0" "EV_JOINING\0" \
"EV_JOINED\0" "EV_RFU1\0" "EV_JOIN_FAILED\0" "EV_REJOIN_FAILED\0" \
"EV_TXCOMPLETE\0" "EV_LOST_TSYNC\0" "EV_RESET\0" \
"EV_RXCOMPLETE\0" "EV_LINK_DEAD\0" "EV_LINK_ALIVE\0" "EV_SCAN_FOUND\0" \
"EV_TXSTART\0" "EV_TXCANCELED\0" "EV_RXSTART\0" "EV_JOIN_TXCOMPLETE\0"
enum {
LMIC_ERROR_SUCCESS = 0,
LMIC_ERROR_TX_BUSY = -1,
LMIC_ERROR_TX_TOO_LARGE = -2,
LMIC_ERROR_TX_NOT_FEASIBLE = -3,
LMIC_ERROR_TX_FAILED = -4,
};
typedef int lmic_tx_error_t;
#define LMIC_ERROR_NAME__INIT \
"LMIC_ERROR_SUCCESS", \
"LMIC_ERROR_TX_BUSY", \
"LMIC_ERROR_TX_TOO_LARGE", \
"LMIC_ERROR_TX_NOT_FEASIBLE", \
"LMIC_ERROR_TX_FAILED"
#define LMIC_ERROR_NAME_MULTISZ__INIT \
"LMIC_ERROR_SUCCESS\0" \
"LMIC_ERROR_TX_BUSY\0" \
"LMIC_ERROR_TX_TOO_LARGE\0" \
"LMIC_ERROR_TX_NOT_FEASIBLE\0" \
"LMIC_ERROR_TX_FAILED"
enum {
LMIC_BEACON_ERROR_INVALID = -2,
LMIC_BEACON_ERROR_WRONG_NETWORK = -1,
LMIC_BEACON_ERROR_SUCCESS_PARTIAL = 0,
LMIC_BEACON_ERROR_SUCCESS_FULL = 1,
};
typedef s1_t lmic_beacon_error_t;
static inline bit_t LMIC_BEACON_SUCCESSFUL(lmic_beacon_error_t e) {
return e < 0;
}
// LMIC_CFG_max_clock_error_ppm
#if !defined(LMIC_CFG_max_clock_error_ppm)
# define LMIC_CFG_max_clock_error_ppm 2000 /* max clock error: 0.2% (2000 ppm) */
#endif
enum {
// This value represents 100% error in LMIC.clockError
MAX_CLOCK_ERROR = 65536,
//! \brief maximum clock error that users can specify: 2000 ppm (0.2%).
//! \details This is the limit for clock error, unless LMIC_ENABLE_arbitrary_clock_error is set.
//! The default is 4,000 ppm, which is .004, or 0.4%; this is what you get on an
//! STM32L0 running with the HSI oscillator after cal. If your clock error is bigger,
//! usually you want to calibrate it so that millis() and micros() are reasonably
//! accurate. Important: do not use clock error to compensate for late serving
//! of the LMIC. If you see that LMIC.radio.rxlate_count is increasing, you need
//! to adjust your application logic so the LMIC gets serviced promptly when a
//! Class A downlink (or beacon) is pending.
LMIC_kMaxClockError_ppm = 4000,
};
// callbacks for client alerts.
// types and functions are always defined, to reduce #ifs in example code and libraries.
typedef void LMIC_ABI_STD lmic_rxmessage_cb_t(void *pUserData, uint8_t port, const uint8_t *pMessage, size_t nMessage);
typedef void LMIC_ABI_STD lmic_txmessage_cb_t(void *pUserData, int fSuccess);
typedef void LMIC_ABI_STD lmic_event_cb_t(void *pUserData, ev_t e);
// network time request callback function
// defined unconditionally, because APIs and types can't change based on config.
// This is called when a time-request succeeds or when we get a downlink
// without time request, "completing" the pending time request.
typedef void LMIC_ABI_STD lmic_request_network_time_cb_t(void *pUserData, int flagSuccess);
// how the network represents time.
typedef u4_t lmic_gpstime_t;
// rather than deal with 1/256 second tick, we adjust ostime back
// (as it's high res) to match tNetwork.
typedef struct lmic_time_reference_s lmic_time_reference_t;
struct lmic_time_reference_s {
// our best idea of when we sent the uplink (end of packet).
ostime_t tLocal;
// the network's best idea of when we sent the uplink.
lmic_gpstime_t tNetwork;
};
enum lmic_request_time_state_e {
lmic_RequestTimeState_idle = 0, // we're not doing anything
lmic_RequestTimeState_tx, // we want to tx a time request on next uplink
lmic_RequestTimeState_rx, // we have tx'ed, next downlink completes.
lmic_RequestTimeState_success // we sucessfully got time.
};
typedef u1_t lmic_request_time_state_t;
enum lmic_engine_update_state_e {
lmic_EngineUpdateState_idle = 0, // engineUpdate is idle.
lmic_EngineUpdateState_busy = 1, // engineUpdate is busy, but has not been reentered.
lmic_EngineUpdateState_again = 2, // engineUpdate is busy, and has to be evaluated again.
};
typedef u1_t lmic_engine_update_state_t;
/*
Structure: lmic_client_data_t
Function:
Holds LMIC client data that must live through LMIC_reset().
Description:
There are a variety of client registration linkage items that
must live through LMIC_reset(), because LMIC_reset() is called
at frame rollover time. We group them together into a structure
to make copies easy.
*/
//! abstract type for collection of client data that survives LMIC_reset().
typedef struct lmic_client_data_s lmic_client_data_t;
//! contents of lmic_client_data_t
struct lmic_client_data_s {
/* pointer-width things come first */
#if LMIC_ENABLE_DeviceTimeReq
lmic_request_network_time_cb_t *pNetworkTimeCb; //! call-back routine for network time
void *pNetworkTimeUserData; //! call-back data for network time.
#endif
#if LMIC_ENABLE_user_events
lmic_event_cb_t *eventCb; //! user-supplied callback function for events.
void *eventUserData; //! data for eventCb
lmic_rxmessage_cb_t *rxMessageCb; //! user-supplied message-received callback
void *rxMessageUserData; //! data for rxMessageCb
lmic_txmessage_cb_t *txMessageCb; //! transmit-complete message handler; reset on each tx complete.
void *txMessageUserData; //! data for txMessageCb.
#endif // LMIC_ENABLE_user_events
/* next we have things that are (u)int32_t */
/* none at the moment */
/* next we have things that are (u)int16_t */
u2_t clockError; //! Inaccuracy in the clock. CLOCK_ERROR_MAX represents +/-100% error
/* finally, things that are (u)int8_t */
/* none at the moment */
};
/*
Structure: lmic_radio_data_t
Function:
Holds LMIC radio driver.
Description:
Eventually this will be used for all portable things for the radio driver,
but for now it's where we can start to add things.
*/
typedef struct lmic_radio_data_s lmic_radio_data_t;
struct lmic_radio_data_s {
// total os ticks of accumulated delay error. Can overflow!
ostime_t rxlate_ticks;
// number of rx late launches.
unsigned rxlate_count;
// total os ticks of accumulated tx delay error. Can overflow!
ostime_t txlate_ticks;
// number of tx late launches.
unsigned txlate_count;
};
/*
Structure: lmic_t
Function:
Provides the instance data for the LMIC.
*/
struct lmic_t {
// client setup data, survives LMIC_reset().
lmic_client_data_t client;
// the OS job object. pointer alignment.
osjob_t osjob;
#if !defined(DISABLE_BEACONS)
bcninfo_t bcninfo; // Last received beacon info
#endif
#if !defined(DISABLE_PING)
rxsched_t ping; // pingable setup
#endif
// the radio driver portable context
lmic_radio_data_t radio;
/* (u)int32_t things */
// Radio settings TX/RX (also accessed by HAL)
ostime_t txend;
ostime_t rxtime;
// LBT info
ostime_t lbt_ticks; // ticks to listen
s1_t lbt_dbmax; // max permissible dB on our channel (eg -80)
u4_t freq;
s1_t rssi;
s1_t snr; // LMIC.snr is SNR times 4
rps_t rps;
u1_t rxsyms;
u1_t dndr;
s1_t txpow; // dBm
osjob_t osjob;
ostime_t globalDutyAvail; // time device can send again
// Channel scheduling
u4_t netid; // current network id (~0 - none)
devaddr_t devaddr;
u4_t seqnoDn; // device level down stream seqno
u4_t seqnoUp;
u4_t dn2Freq;
#if !defined(DISABLE_BEACONS)
ostime_t bcnRxtime;
#endif
#if LMIC_ENABLE_DeviceTimeReq
// put here for alignment, to reduce RAM use.
ostime_t localDeviceTime; // the LMIC.txend value for last DeviceTimeAns
lmic_gpstime_t netDeviceTime; // the netDeviceTime for lastDeviceTimeAns
// zero ==> not valid.
#endif // LMIC_ENABLE_DeviceTimeReq
// Channel scheduling -- very much private
#if CFG_LMIC_EU_like
band_t bands[MAX_BANDS];
u4_t channelFreq[MAX_CHANNELS];
#if !defined(DISABLE_MCMD_DlChannelReq)
u4_t channelDlFreq[MAX_CHANNELS];
#endif
// bit map of enabled datarates for each channel
u2_t channelDrMap[MAX_CHANNELS];
u2_t channelMap;
#elif CFG_LMIC_US_like
@ -275,83 +538,98 @@ struct lmic_t {
u2_t activeChannels125khz;
u2_t activeChannels500khz;
#endif
/* (u)int16_t things */
rps_t rps; // radio parameter selections: SF, BW, CodingRate, NoCrc, implicit hdr
u2_t opmode; // engineUpdate() operating mode flags
u2_t devNonce; // last generated nonce
s2_t adrAckReq; // counter for link integrity tracking (LINK_CHECK_OFF=off)
#if !defined(DISABLE_BEACONS)
s2_t drift; // last measured drift
s2_t lastDriftDiff;
s2_t maxDriftDiff;
rxsyms_t bcnRxsyms; //
#endif
/* (u)int8_t things */
lmic_engine_update_state_t engineUpdateState; // state of the engineUpdate() evaluator.
s1_t rssi;
s1_t snr; // LMIC.snr is SNR times 4
rxsyms_t rxsyms; // symbols for receive timeout.
u1_t dndr;
s1_t txpow; // transmit dBm (administrative)
s1_t radio_txpow; // the radio driver's copy of txpow, in dB limited by adrTxPow, and
// also adjusted for EIRP/antenna gain considerations.
// This is just the radio's idea of power. So if you are
// controlling EIRP, and you have 3 dB antenna gain, this
// needs to reduced by 3 dB.
s1_t lbt_dbmax; // max permissible dB on our channel (eg -80)
u1_t txChnl; // channel for next TX
u1_t globalDutyRate; // max rate: 1/2^k
ostime_t globalDutyAvail; // time device can send again
u4_t netid; // current network id (~0 - none)
u2_t opmode;
u1_t upRepeat; // configured up repeat
s1_t adrTxPow; // ADR adjusted TX power
u1_t datarate; // current data rate
u1_t errcr; // error coding rate (used for TX only)
u1_t rejoinCnt; // adjustment for rejoin datarate
#if !defined(DISABLE_BEACONS)
s2_t drift; // last measured drift
s2_t lastDriftDiff;
s2_t maxDriftDiff;
#endif
u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX
// represents +/-100% error
u1_t upRepeatCount; // current up-repeat
bit_t initBandplanAfterReset; // cleared by LMIC_reset(), set by first join. See issue #244
u1_t pendTxPort;
u1_t pendTxConf; // confirmed data
u1_t pendTxLen; // +0x80 = confirmed
u1_t pendTxLen; // count of bytes in pendTxData.
u1_t pendTxData[MAX_LEN_PAYLOAD];
u2_t devNonce; // last generated nonce
u1_t pendMacLen; // number of bytes of pending Mac response data
bit_t pendMacPiggyback; // received on port 0 or piggyback?
// response data if piggybacked
u1_t pendMacData[LWAN_FCtrl_FOptsLen_MAX];
u1_t nwkKey[16]; // network session key
u1_t artKey[16]; // application router session key
devaddr_t devaddr;
u4_t seqnoDn; // device level down stream seqno
u4_t seqnoUp;
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
s1_t adrAckReq; // counter until we reset data rate (0=off)
u1_t lastDnConf; // downlink with seqnoDn-1 requested confirmation
u1_t adrChanged;
u1_t rxDelay; // Rx delay after TX
u1_t margin;
bit_t ladrAns; // link adr adapt answer pending
bit_t devsAns; // device status answer pending
s1_t devAnsMargin; // SNR value between -32 and 31 (inclusive) for the last successfully received DevStatusReq command
u1_t adrEnabled;
u1_t moreData; // NWK has more data pending
#if !defined(DISABLE_MCMD_DCAP_REQ)
bit_t dutyCapAns; // have to ACK duty cycle settings
#endif
#if !defined(DISABLE_MCMD_SNCH_REQ)
u1_t snchAns; // answer set new channel
#endif
#if LMIC_ENABLE_TxParamSetupReq
bit_t txParamSetupAns; // transmit setup answer pending.
u1_t txParam; // the saved TX param byte.
#endif
#if LMIC_ENABLE_DeviceTimeReq
lmic_request_time_state_t txDeviceTimeReqState; // current state, initially idle.
u1_t netDeviceTimeFrac; // updated on any DeviceTimeAns.
#endif
// rx1DrOffset is the offset from uplink to downlink datarate
u1_t rx1DrOffset; // captured from join. zero by default.
// 2nd RX window (after up stream)
u1_t dn2Dr;
u4_t dn2Freq;
#if !defined(DISABLE_MCMD_DN2P_SET)
#if !defined(DISABLE_MCMD_RXParamSetupReq)
u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs
#endif
#if !defined(DISABLE_MCMD_DlChannelReq)
u1_t macDlChannelAns; // 0 ==> no answer pending, 0x80+ACK bits
#endif
#if !defined(DISABLE_MCMD_RXTimingSetupReq)
bit_t macRxTimingSetupAns; // 0 ==> no answer pend, non-zero inserts response.
#endif
// Class B state
#if !defined(DISABLE_BEACONS)
u1_t missedBcns; // unable to track last N beacons
u1_t bcninfoTries; // how often to try (scan mode only)
#endif
#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING)
u1_t pingSetAns; // answer set cmd and ACK bits
#endif
#if !defined(DISABLE_PING)
rxsched_t ping; // pingable setup
#endif
// Public part of MAC state
u1_t txCnt;
u1_t txrxFlags; // transaction flags (TX-RX combo)
@ -361,13 +639,12 @@ struct lmic_t {
#if !defined(DISABLE_BEACONS)
u1_t bcnChnl;
u1_t bcnRxsyms; //
ostime_t bcnRxtime;
bcninfo_t bcninfo; // Last received beacon info
#endif
u1_t noRXIQinversion;
u1_t saveIrqFlags; // last LoRa IRQ flags
};
//! \var struct lmic_t LMIC
//! The state of LMIC MAC layer is encapsulated in this variable.
DECLARE_LMIC; //!< \internal
@ -376,16 +653,20 @@ DECLARE_LMIC; //!< \internal
#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
void LMIC_disableChannel (u1_t channel);
void LMIC_enableSubBand(u1_t band);
void LMIC_enableChannel(u1_t channel);
void LMIC_disableSubBand(u1_t band);
void LMIC_selectSubBand(u1_t band);
bit_t LMIC_disableChannel (u1_t channel);
bit_t LMIC_enableSubBand(u1_t band);
bit_t LMIC_enableChannel(u1_t channel);
bit_t LMIC_disableSubBand(u1_t band);
bit_t LMIC_selectSubBand(u1_t band);
void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow
void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off)
#if !defined(DISABLE_JOIN)
bit_t LMIC_startJoining (void);
void LMIC_tryRejoin (void);
void LMIC_unjoin (void);
void LMIC_unjoinAndRejoin (void);
#endif
void LMIC_shutdown (void);
@ -393,7 +674,11 @@ void LMIC_init (void);
void LMIC_reset (void);
void LMIC_clrTxData (void);
void LMIC_setTxData (void);
int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
void LMIC_setTxData_strict(void);
lmic_tx_error_t LMIC_setTxData2(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
lmic_tx_error_t LMIC_setTxData2_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
lmic_tx_error_t LMIC_sendWithCallback(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData);
lmic_tx_error_t LMIC_sendWithCallback_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData);
void LMIC_sendAlive (void);
#if !defined(DISABLE_BEACONS)
@ -405,9 +690,6 @@ void LMIC_disableTracking (void);
void LMIC_stopPingable (void);
void LMIC_setPingable (u1_t intvExp);
#endif
#if !defined(DISABLE_JOIN)
void LMIC_tryRejoin (void);
#endif
void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
void LMIC_setLinkCheckMode (bit_t enabled);
@ -417,11 +699,29 @@ u4_t LMIC_getSeqnoUp (void);
u4_t LMIC_setSeqnoUp (u4_t);
void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData);
int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference);
int LMIC_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData);
int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, void *pUserData);
// APIs for client half of compliance.
typedef u1_t lmic_compliance_rx_action_t;
enum lmic_compliance_rx_action_e {
LMIC_COMPLIANCE_RX_ACTION_PROCESS = 0, // process this message normally
LMIC_COMPLIANCE_RX_ACTION_START, // enter compliance mode, discard this message
LMIC_COMPLIANCE_RX_ACTION_IGNORE, // continue in compliance mode, discard this message
LMIC_COMPLIANCE_RX_ACTION_END // exit compliance mode, discard this message
};
lmic_compliance_rx_action_t LMIC_complianceRxMessage(u1_t port, const u1_t *pMessage, size_t nMessage);
// Declare onEvent() function, to make sure any definition will have the
// C conventions, even when in a C++ file.
#if LMIC_ENABLE_onEvent
DECL_ON_LMIC_EVENT;
#endif /* LMIC_ENABLE_onEvent */
// Special APIs - for development or testing
// !!!See implementation for caveats!!!
@ -430,4 +730,7 @@ DECL_ON_LMIC_EVENT;
} // extern "C"
#endif
// names for backward compatibility
#include "lmic_compat.h"
#endif // _lmic_h_

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -51,19 +51,19 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
};
// see table in 2.7.6 -- this assumes UplinkDwellTime = 0.
static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = {
static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = {
59+5, // [0]
59+5, // [1]
59+5, // [2]
123+5, // [3]
230+5, // [4]
230+5, // [5]
230+5, // [6]
230+5 // [7]
250+5, // [4]
250+5, // [5]
250+5, // [6]
250+5 // [7]
};
// see table in 2.7.6 -- this assumes UplinkDwellTime = 1.
static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
0, // [0]
0, // [1]
19+5, // [2]
@ -74,18 +74,20 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
250+5 // [7]
};
static uint8_t
static uint8_t
LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) {
LMIC_API_PARAMETER(mcmd_txparam);
if (mcmd_txparam == 0xFF)
return AS923_INITIAL_TxParam_UplinkDwellTime;
return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0;
return (mcmd_txparam & MCMD_TxParam_TxDWELL_MASK) != 0;
}
static uint8_t
static uint8_t
LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) {
LMIC_API_PARAMETER(mcmd_txparam);
if (mcmd_txparam == 0xFF)
return AS923_INITIAL_TxParam_DownlinkDwellTime;
return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0;
return (mcmd_txparam & MCMD_TxParam_RxDWELL_MASK) != 0;
}
uint8_t LMICas923_maxFrameLen(uint8_t dr) {
@ -95,7 +97,7 @@ uint8_t LMICas923_maxFrameLen(uint8_t dr) {
else
return TABLE_GET_U1(maxFrameLens_dwell0, dr);
} else {
return 0xFF;
return 0;
}
}
@ -110,7 +112,6 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
-10, // [5]: MaxEIRP - 10dB
-12, // [6]: MaxEIRP - 12dB
-14, // [7]: MaxEIRP - 14dB
0, 0, 0, 0, 0, 0, 0, 0
};
// from LoRaWAN 5.8: mapping from txParam to MaxEIRP
@ -119,6 +120,7 @@ static CONST_TABLE(s1_t, TXMAXEIRP)[16] = {
};
static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) {
// if uninitialized, return default.
if (mcmd_txparam == 0xFF)
return AS923_TX_EIRP_MAX_DBM;
else
@ -127,18 +129,23 @@ static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) {
(mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >>
MCMD_TxParam_MaxEIRP_SHIFT
);
}
}
// translate from an encoded power to an actual power using
// the maxeirp setting.
// translate from an encoded power to an actual power using
// the maxeirp setting; return -128 if not legal.
int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1) {
s1_t const adj =
TABLE_GET_S1(
TXPOWLEVELS,
(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT
);
return adj;
uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT;
if (pindex < LENOF_TABLE(TXPOWLEVELS)) {
s1_t const adj =
TABLE_GET_S1(
TXPOWLEVELS,
pindex
);
return LMICas923_getMaxEIRP(LMIC.txParam) + adj;
} else {
return -128;
}
}
// only used in this module, but used by variant macro dr2hsym().
@ -162,7 +169,7 @@ ostime_t LMICas923_dr2hsym(uint8_t dr) {
enum { NUM_DEFAULT_CHANNELS = 2 };
static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = {
// Default operational frequencies
AS923_F1 | BAND_CENTI,
AS923_F1 | BAND_CENTI,
AS923_F2 | BAND_CENTI,
};
@ -171,6 +178,9 @@ void LMICas923_initDefaultChannels(bit_t join) {
LMIC_API_PARAMETER(join);
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
#if !defined(DISABLE_MCMD_DlChannelReq)
os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq));
#endif // !DISABLE_MCMD_DlChannelReq
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
@ -217,6 +227,18 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
}
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
// zero the band bits in freq, just in case.
freq &= ~3;
if (chidx < NUM_DEFAULT_CHANNELS) {
// can't disable a default channel.
if (freq == 0)
return 0;
// can't change a default channel.
else if (freq != (LMIC.channelFreq[chidx] & ~3))
return 0;
}
bit_t fEnable = (freq != 0);
if (chidx >= MAX_CHANNELS)
return 0;
if (band == -1) {
@ -226,10 +248,13 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
freq = (freq&~3) | band;
}
LMIC.channelFreq[chidx] = freq;
LMIC.channelDrMap[chidx] =
drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B)
LMIC.channelDrMap[chidx] =
drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B)
: drmap;
LMIC.channelMap |= 1 << chidx; // enabled right away
if (fEnable)
LMIC.channelMap |= 1 << chidx; // enabled right away
else
LMIC.channelMap &= ~(1 << chidx);
return 1;
}
@ -259,7 +284,9 @@ void LMICas923_setRx1Params(void) {
int const txdr = LMIC.dndr;
int effective_rx1DrOffset;
int candidateDr;
LMICeulike_setRx1Freq();
effective_rx1DrOffset = LMIC.rx1DrOffset;
// per section 2.7.7 of regional, lines 1101:1103:
switch (effective_rx1DrOffset) {
@ -267,22 +294,22 @@ void LMICas923_setRx1Params(void) {
case 7: effective_rx1DrOffset = -2; break;
default: /* no change */ break;
}
// per regional 2.2.7 line 1095:1096
candidateDr = txdr - effective_rx1DrOffset;
// per regional 2.2.7 lines 1097:1100
if (LMICas923_getDownlinkDwellBit(LMIC.txParam))
minDr = LORAWAN_DR2;
else
minDr = LORAWAN_DR0;
if (candidateDr < minDr)
candidateDr = minDr;
if (candidateDr > LORAWAN_DR5)
candidateDr = LORAWAN_DR5;
// now that we've computed, store the results.
LMIC.dndr = (uint8_t) candidateDr;
LMIC.rps = dndr2rps(LMIC.dndr);
@ -334,23 +361,18 @@ ostime_t LMICas923_nextJoinState(void) {
}
#endif // !DISABLE_JOIN
// txDone handling for FSK.
void
LMICas923_txDoneFSK(ostime_t delay, osjobcb_t func) {
LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
LMIC.rxsyms = RXLEN_FSK;
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
}
void
LMICas923_initJoinLoop(void) {
LMIC.txParam = 0xFF;
// LMIC.txParam is set to 0xFF by the central code at init time.
LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ AS923_TX_EIRP_MAX_DBM);
}
void
LMICas923_updateTx(ostime_t txbeg) {
u4_t freq = LMIC.channelFreq[LMIC.txChnl];
u4_t dwellDelay;
u4_t globalDutyDelay;
// Update global/band specific duty cycle stats
ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
// Update channel/global duty cycle stats
@ -358,8 +380,18 @@ LMICas923_updateTx(ostime_t txbeg) {
LMIC.freq = freq & ~(u4_t)3;
LMIC.txpow = LMICas923_getMaxEIRP(LMIC.txParam);
band->avail = txbeg + airtime * band->txcap;
if (LMIC.globalDutyRate != 0)
LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate);
dwellDelay = globalDutyDelay = 0;
if (LMIC.globalDutyRate != 0) {
globalDutyDelay = (airtime << LMIC.globalDutyRate);
}
if (LMICas923_getUplinkDwellBit(LMIC.txParam)) {
dwellDelay = AS923_UPLINK_DWELL_TIME_osticks;
}
if (dwellDelay > globalDutyDelay) {
globalDutyDelay = dwellDelay;
}
if (globalDutyDelay != 0)
LMIC.globalDutyAvail = txbeg + globalDutyDelay;
}

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -30,10 +30,10 @@
#include "lmic_bandplan.h"
#if defined(CFG_au921)
#if defined(CFG_au915)
// ================================================================================
//
// BEG: AU921 related stuff
// BEG: AU915 related stuff
//
CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
@ -52,54 +52,99 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
MAKERPS(SF9 , BW500, CR_4_5, 0, 0), // [11]
MAKERPS(SF8 , BW500, CR_4_5, 0, 0), // [12]
MAKERPS(SF7 , BW500, CR_4_5, 0, 0), // [13]
ILLEGAL_RPS
ILLEGAL_RPS
};
static CONST_TABLE(u1_t, maxFrameLens)[] = {
59+5, 59+5, 59+5, 123+5, 230+5, 230+5, 230+5, 255,
41+5, 117+5, 230+5, 230+5, 230+5, 230+5 };
static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = {
59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 0,
61+5, 137+5, 250+5, 250+5, 250+5, 250+5 };
uint8_t LMICau921_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
return 0xFF;
static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
0, 0, 19+5, 61+5, 133+5, 250+5, 250+5, 0,
61+5, 137+5, 250+5, 250+5, 250+5, 250+5 };
static bit_t
LMICau915_getUplinkDwellBit() {
// if uninitialized, return default.
if (LMIC.txParam == 0xFF) {
return AU915_INITIAL_TxParam_UplinkDwellTime;
}
return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0;
}
uint8_t LMICau915_maxFrameLen(uint8_t dr) {
if (LMICau915_getUplinkDwellBit()) {
if (dr < LENOF_TABLE(maxFrameLens_dwell0))
return TABLE_GET_U1(maxFrameLens_dwell0, dr);
else
return 0;
} else {
if (dr < LENOF_TABLE(maxFrameLens_dwell1))
return TABLE_GET_U1(maxFrameLens_dwell1, dr);
else
return 0;
}
}
// from LoRaWAN 5.8: mapping from txParam to MaxEIRP
static CONST_TABLE(s1_t, TXMAXEIRP)[16] = {
8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36
};
static int8_t LMICau915_getMaxEIRP(uint8_t mcmd_txparam) {
// if uninitialized, return default.
if (mcmd_txparam == 0xFF)
return AU915_TX_EIRP_MAX_DBM;
else
return TABLE_GET_S1(
TXMAXEIRP,
(mcmd_txparam & MCMD_TxParam_MaxEIRP_MASK) >>
MCMD_TxParam_MaxEIRP_SHIFT
);
}
int8_t LMICau915_pow2dbm(uint8_t mcmd_ladr_p1) {
if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) == MCMD_LinkADRReq_POW_MASK)
return -128;
else {
return ((s1_t)(LMICau915_getMaxEIRP(LMIC.txParam) - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1)));
}
}
static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = {
us2osticksRound(128 << 7), // DR_SF12
us2osticksRound(128 << 7), // DR_SF12
us2osticksRound(128 << 6), // DR_SF11
us2osticksRound(128 << 5), // DR_SF10
us2osticksRound(128 << 4), // DR_SF9
us2osticksRound(128 << 3), // DR_SF8
us2osticksRound(128 << 2), // DR_SF7
us2osticksRound(128 << 4), // DR_SF9
us2osticksRound(128 << 3), // DR_SF8
us2osticksRound(128 << 2), // DR_SF7
us2osticksRound(128 << 1), // DR_SF8C
us2osticksRound(128 << 0), // ------
us2osticksRound(128 << 0), // ------
us2osticksRound(128 << 5), // DR_SF12CR
us2osticksRound(128 << 4), // DR_SF11CR
us2osticksRound(128 << 3), // DR_SF10CR
us2osticksRound(128 << 4), // DR_SF11CR
us2osticksRound(128 << 3), // DR_SF10CR
us2osticksRound(128 << 2), // DR_SF9CR
us2osticksRound(128 << 1), // DR_SF8CR
us2osticksRound(128 << 0), // DR_SF7CR
};
// get ostime for symbols based on datarate. This is not like us915,
// get ostime for symbols based on datarate. This is not like us915,
// becuase the times don't match between the upper half and lower half
// of the table.
ostime_t LMICau921_dr2hsym(uint8_t dr) {
ostime_t LMICau915_dr2hsym(uint8_t dr) {
return TABLE_GET_OSTIME(DR2HSYM_osticks, dr);
}
u4_t LMICau921_convFreq(xref2cu1_t ptr) {
u4_t LMICau915_convFreq(xref2cu1_t ptr) {
u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100;
if (freq < AU921_FREQ_MIN || freq > AU921_FREQ_MAX)
if (freq < AU915_FREQ_MIN || freq > AU915_FREQ_MAX)
freq = 0;
return freq;
}
// au921: no support for xchannels.
// au915: no support for xchannels.
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
LMIC_API_PARAMETER(chidx);
LMIC_API_PARAMETER(freq);
@ -109,9 +154,11 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
return 0; // all channels are hardwired.
}
void LMIC_disableChannel(u1_t channel) {
bit_t LMIC_disableChannel(u1_t channel) {
bit_t result = 0;
if (channel < 72) {
if (ENABLED_CHANNEL(channel)) {
result = 1;
if (IS_CHANNEL_125khz(channel))
LMIC.activeChannels125khz--;
else if (IS_CHANNEL_500khz(channel))
@ -119,11 +166,14 @@ void LMIC_disableChannel(u1_t channel) {
}
LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF));
}
return result;
}
void LMIC_enableChannel(u1_t channel) {
bit_t LMIC_enableChannel(u1_t channel) {
bit_t result = 0;
if (channel < 72) {
if (!ENABLED_CHANNEL(channel)) {
result = 1;
if (IS_CHANNEL_125khz(channel))
LMIC.activeChannels125khz++;
else if (IS_CHANNEL_500khz(channel))
@ -131,78 +181,101 @@ void LMIC_enableChannel(u1_t channel) {
}
LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF));
}
return result;
}
void LMIC_enableSubBand(u1_t band) {
bit_t LMIC_enableSubBand(u1_t band) {
ASSERT(band < 8);
u1_t start = band * 8;
u1_t end = start + 8;
bit_t result = 0;
// enable all eight 125 kHz channels in this subband
for (int channel = start; channel < end; ++channel)
LMIC_enableChannel(channel);
result |= LMIC_enableChannel(channel);
// there's a single 500 kHz channel associated with
// each group of 8 125 kHz channels. Enable it, too.
LMIC_enableChannel(64 + band);
result |= LMIC_enableChannel(64 + band);
return result;
}
void LMIC_disableSubBand(u1_t band) {
bit_t LMIC_disableSubBand(u1_t band) {
ASSERT(band < 8);
u1_t start = band * 8;
u1_t end = start + 8;
bit_t result = 0;
// disable all eight 125 kHz channels in this subband
for (int channel = start; channel < end; ++channel)
LMIC_disableChannel(channel);
result |= LMIC_disableChannel(channel);
// there's a single 500 kHz channel associated with
// each group of 8 125 kHz channels. Disable it, too.
LMIC_disableChannel(64 + band);
result |= LMIC_disableChannel(64 + band);
return result;
}
void LMIC_selectSubBand(u1_t band) {
bit_t LMIC_selectSubBand(u1_t band) {
bit_t result = 0;
ASSERT(band < 8);
for (int b = 0; b<8; ++b) {
if (band == b)
LMIC_enableSubBand(b);
result |= LMIC_enableSubBand(b);
else
LMIC_disableSubBand(b);
result |= LMIC_disableSubBand(b);
}
return result;
}
void LMICau921_updateTx(ostime_t txbeg) {
void LMICau915_updateTx(ostime_t txbeg) {
u1_t chnl = LMIC.txChnl;
LMIC.txpow = AU921_TX_EIRP_MAX_DBM;
LMIC.txpow = LMICau915_getMaxEIRP(LMIC.txParam);
if (chnl < 64) {
LMIC.freq = AU921_125kHz_UPFBASE + chnl*AU921_125kHz_UPFSTEP;
LMIC.freq = AU915_125kHz_UPFBASE + chnl*AU915_125kHz_UPFSTEP;
} else {
ASSERT(chnl < 64 + 8);
LMIC.freq = AU921_500kHz_UPFBASE + (chnl - 64)*AU921_500kHz_UPFSTEP;
LMIC.freq = AU915_500kHz_UPFBASE + (chnl - 64)*AU915_500kHz_UPFSTEP;
}
// Update global duty cycle stats
// Update global duty cycle stat and deal with dwell time.
u4_t dwellDelay;
u4_t globalDutyDelay;
dwellDelay = globalDutyDelay = 0;
if (LMIC.globalDutyRate != 0) {
ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate);
globalDutyDelay = txbeg + (airtime << LMIC.globalDutyRate);
}
if (LMICau915_getUplinkDwellBit(LMIC.txParam)) {
dwellDelay = AU915_UPLINK_DWELL_TIME_osticks;
}
if (dwellDelay > globalDutyDelay) {
globalDutyDelay = dwellDelay;
}
if (globalDutyDelay != 0) {
LMIC.globalDutyAvail = txbeg + globalDutyDelay;
}
}
#if !defined(DISABLE_BEACONS)
void LMICau921_setBcnRxParams(void) {
void LMICau915_setBcnRxParams(void) {
LMIC.dataLen = 0;
LMIC.freq = AU921_500kHz_DNFBASE + LMIC.bcnChnl * AU921_500kHz_DNFSTEP;
LMIC.freq = AU915_500kHz_DNFBASE + LMIC.bcnChnl * AU915_500kHz_DNFSTEP;
LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN);
}
#endif // !DISABLE_BEACONS
// set the Rx1 dndr, rps.
void LMICau921_setRx1Params(void) {
void LMICau915_setRx1Params(void) {
u1_t const txdr = LMIC.dndr;
u1_t candidateDr;
LMIC.freq = AU921_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * AU921_500kHz_DNFSTEP;
if ( /* TX datarate */txdr < AU921_DR_SF8C)
LMIC.freq = AU915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * AU915_500kHz_DNFSTEP;
if ( /* TX datarate */txdr < AU915_DR_SF8C)
candidateDr = txdr + 8 - LMIC.rx1DrOffset;
else
candidateDr = AU921_DR_SF7CR;
candidateDr = AU915_DR_SF7CR;
if (candidateDr < LORAWAN_DR8)
candidateDr = LORAWAN_DR8;
@ -213,9 +286,17 @@ void LMICau921_setRx1Params(void) {
LMIC.rps = dndr2rps(LMIC.dndr);
}
void LMICau915_initJoinLoop(void) {
// LMIC.txParam is set to 0xFF by the central code at init time.
LMICuslike_initJoinLoop();
// initialize the adrTxPower.
LMIC.adrTxPow = LMICau915_getMaxEIRP(LMIC.txParam); // dBm
}
//
// END: AU921 related stuff
// END: AU915 related stuff
//
// ================================================================================
#endif

76
src/lmic/lmic_bandplan.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,10 +37,12 @@
# include "lmic_bandplan_eu868.h"
#elif defined(CFG_us915)
# include "lmic_bandplan_us915.h"
#elif defined(CFG_au921)
# include "lmic_bandplan_au921.h"
#elif defined(CFG_au915)
# include "lmic_bandplan_au915.h"
#elif defined(CFG_as923)
# include "lmic_bandplan_as923.h"
#elif defined(CFG_kr920)
# include "lmic_bandplan_kr920.h"
#elif defined(CFG_in866)
# include "lmic_bandplan_in866.h"
#else
@ -52,8 +54,8 @@
# error "DNW2_SAFETY_ZONE not defined by bandplan"
#endif
#ifndef maxFrameLen
# error "maxFrameLen() not defined by bandplan"
#ifndef LMICbandplan_maxFrameLen
# error "LMICbandplan_maxFrameLen() not defined by bandplan"
#endif
#ifndef pow2dBm
@ -104,6 +106,10 @@
# error "LMICbandplan_setBcnRxParams() not defined by bandplan"
#endif
#if !defined(LMICbandplan_canMapChannels)
# error "LMICbandplan_canMapChannels() not defined by bandplan"
#endif
#if !defined(LMICbandplan_mapChannels)
# error "LMICbandplan_mapChannels() not defined by bandplan"
#endif
@ -143,16 +149,65 @@
#if !defined(LMICbandplan_init)
# error "LMICbandplan_init() not defined by bandplan"
#endif
#if !defined(LMICbandplan_saveAdrState)
# error "LMICbandplan_saveAdrState() not defined by bandplan"
#endif
#if !defined(LMICbandplan_compareAdrState)
# error "LMICbandplan_compareAdrState() not defined by bandplan"
#endif
#if !defined(LMICbandplan_restoreAdrState)
# error "LMICbandplan_restoreAdrState() not defined by bandplan"
#endif
#if !defined(LMICbandplan_isDataRateFeasible)
# error "LMICbandplan_isDataRateFeasible() not defined by bandplan"
#endif
//
// Things common to lmic.c code
//
#define LMICbandplan_MINRX_SYMS_LoRa_ClassA 6
#define LMICbandplan_RX_ERROR_ABS_osticks ms2osticks(10)
// Semtech inherently (by calculating in ms and taking ceilings)
// rounds up to the next higher ms. It's a lot easier for us
// to just add margin for things like hardware ramp-up time
// and clock calibration when running from the LSE and HSI
// clocks on an STM32.
#define LMICbandplan_RX_EXTRA_MARGIN_osticks us2osticks(2000)
// probably this should be the same as the Class-A value, but
// we have not the means to thoroughly test this. This is the
// number of rxsyms used in the computations for ping and beacon
// windows.
#define LMICbandplan_MINRX_SYMS_LoRa_ClassB 5
#define LMICbandplan_PAMBL_SYMS 8
#define LMICbandplan_PAMBL_FSK 5
#define LMICbandplan_PRERX_FSK 1
#define LMICbandplan_RXLEN_FSK (1+5+2)
// Legacy names
#if !defined(MINRX_SYMS)
#define MINRX_SYMS 5
# define MINRX_SYMS LMICbandplan_MINRX_SYMS_LoRa_ClassB
#endif // !defined(MINRX_SYMS)
#define PAMBL_SYMS 8
#define PAMBL_FSK 5
#define PRERX_FSK 1
#define RXLEN_FSK (1+5+2)
#define PAMBL_SYMS LMICbandplan_PAMBL_SYMS
#define PAMBL_FSK LMICbandplan_PAMBL_FSK
#define PRERX_FSK LMICbandplan_PRERX_FSK
#define RXLEN_FSK LMICbandplan_RXLEN_FSK
// this is regional, but so far all regions are the same
#if !defined(LMICbandplan_MAX_FCNT_GAP)
# define LMICbandplan_MAX_FCNT_GAP 16384
#endif // !defined LWAN_MAX_FCNT_GAP
// this is probably regional, but for now default can be the same
#if !defined(LMICbandplan_TX_RECOVERY_ms)
# define LMICbandplan_TX_RECOVERY_ms 500
#endif
#define BCN_INTV_osticks sec2osticks(BCN_INTV_sec)
#define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms)
@ -171,5 +226,6 @@
// internal APIs
ostime_t LMICcore_rndDelay(u1_t secSpan);
void LMICcore_setDrJoin(u1_t reason, u1_t dr);
ostime_t LMICcore_adjustForDrift(ostime_t delay, ostime_t hsym, rxsyms_t rxsyms_in);
#endif // _lmic_bandplan_h_

20
src/lmic/lmic_bandplan_as923.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,15 +26,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lmic_as923_h_
# define _lmic_as923_h_
#ifndef _lmic_bandplan_as923_h_
# define _lmic_bandplan_as923_h_
#ifndef _lmic_eu_like_h_
# include "lmic_eu_like.h"
#endif
// return maximum frame length (including PHY header) for this data rate (as923); 0 --> not valid dr.
uint8_t LMICas923_maxFrameLen(uint8_t dr);
#define maxFrameLen(dr) LMICas923_maxFrameLen(dr)
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICas923_maxFrameLen(dr)
int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICas923_pow2dBm(mcmd_ladr_p1)
@ -70,13 +72,7 @@ void LMICas923_init(void);
// override default for LMICbandplan_isFSK()
#undef LMICbandplan_isFSK
#define LMICbandplan_isFSK() (/* TX datarate */LMIC.rxsyms == AS923_DR_FSK)
// txDone handling for FSK.
void
LMICas923_txDoneFSK(ostime_t delay, osjobcb_t func);
#define LMICbandplan_txDoneFsk(delay, func) LMICas923_txDoneFSK(delay, func)
#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == AS923_DR_FSK)
#define LMICbandplan_getInitialDrJoin() (AS923_DR_SF10)
@ -112,4 +108,4 @@ void LMICas923_updateTx(ostime_t txbeg);
ostime_t LMICas923_nextJoinTime(ostime_t now);
#define LMICbandplan_nextJoinTime(now) LMICas923_nextJoinTime(now)
#endif // _lmic_as923_h_
#endif // _lmic_bandplan_as923_h_

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,38 +26,44 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lmic_au921_h_
# define _lmic_au921_h_
#ifndef _lmic_bandplan_au915_h_
# define _lmic_bandplan_au915_h_
// preconditions for lmic_us_like.h
#define LMICuslike_getFirst500kHzDR() (AU921_DR_SF8C)
#define LMICuslike_getFirst500kHzDR() (LORAWAN_DR6)
#define LMICuslike_getJoin125kHzDR() (LORAWAN_DR2)
#ifndef _lmic_us_like_h_
# include "lmic_us_like.h"
#endif
uint8_t LMICau921_maxFrameLen(uint8_t dr);
#define maxFrameLen(dr) LMICau921_maxFrameLen(dr)
// return maximum frame length (including PHY header) for this data rate (au915); 0 --> not valid dr.
uint8_t LMICau915_maxFrameLen(uint8_t dr);
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICau915_maxFrameLen(dr)
#define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1)))
int8_t LMICau915_pow2dbm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICau915_pow2dbm(mcmd_ladr_p1)
ostime_t LMICau921_dr2hsym(uint8_t dr);
#define dr2hsym(dr) LMICau921_dr2hsym(dr)
ostime_t LMICau915_dr2hsym(uint8_t dr);
#define dr2hsym(dr) LMICau915_dr2hsym(dr)
#define LMICbandplan_getInitialDrJoin() (EU868_DR_SF7)
#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR2)
void LMICau921_setBcnRxParams(void);
#define LMICbandplan_setBcnRxParams() LMICau921_setBcnRxParams()
void LMICau915_initJoinLoop(void);
#define LMICbandplan_initJoinLoop() LMICau915_initJoinLoop()
u4_t LMICau921_convFreq(xref2cu1_t ptr);
#define LMICbandplan_convFreq(ptr) LMICau921_convFreq(ptr)
void LMICau915_setBcnRxParams(void);
#define LMICbandplan_setBcnRxParams() LMICau915_setBcnRxParams()
void LMICau921_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICau921_setRx1Params()
u4_t LMICau915_convFreq(xref2cu1_t ptr);
#define LMICbandplan_convFreq(ptr) LMICau915_convFreq(ptr)
void LMICau921_updateTx(ostime_t txbeg);
#define LMICbandplan_updateTx(txbeg) LMICau921_updateTx(txbeg)
void LMICau915_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICau915_setRx1Params()
#endif // _lmic_au921_h_
void LMICau915_updateTx(ostime_t txbeg);
#define LMICbandplan_updateTx(txbeg) LMICau915_updateTx(txbeg)
#endif // _lmic_bandplan_au915_h_

17
src/lmic/lmic_bandplan_eu868.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -33,8 +33,10 @@
# include "lmic_eu_like.h"
#endif
// return maximum frame length (including PHY header) for this data rate (eu868); 0 --> not valid dr.
uint8_t LMICeu868_maxFrameLen(uint8_t dr);
#define maxFrameLen(dr) LMICeu868_maxFrameLen(dr)
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICeu868_maxFrameLen(dr)
int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICeu868_pow2dBm(mcmd_ladr_p1)
@ -57,13 +59,7 @@ LMICeu868_isValidBeacon1(const uint8_t *d) {
// override default for LMICbandplan_isFSK()
#undef LMICbandplan_isFSK
#define LMICbandplan_isFSK() (/* TX datarate */LMIC.rxsyms == EU868_DR_FSK)
// txDone handling for FSK.
void
LMICeu868_txDoneFSK(ostime_t delay, osjobcb_t func);
#define LMICbandplan_txDoneFsk(delay, func) LMICeu868_txDoneFSK(delay, func)
#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == EU868_DR_FSK)
#define LMICbandplan_getInitialDrJoin() (EU868_DR_SF7)
@ -89,4 +85,7 @@ void LMICeu868_initDefaultChannels(bit_t join);
ostime_t LMICeu868_nextJoinTime(ostime_t now);
#define LMICbandplan_nextJoinTime(now) LMICeu868_nextJoinTime(now)
void LMICeu868_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICeu868_setRx1Params()
#endif // _lmic_eu868_h_

23
src/lmic/lmic_bandplan_in866.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,15 +26,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lmic_in866_h_
# define _lmic_in866_h_
#ifndef _lmic_bandplan_in866_h_
# define _lmic_bandplan_in866_h_
#ifndef _lmic_eu_like_h_
# include "lmic_eu_like.h"
#endif
// return maximum frame length (including PHY header) for this data rate (in866); 0 --> not valid dr.
uint8_t LMICin866_maxFrameLen(uint8_t dr);
#define maxFrameLen(dr) LMICin866_maxFrameLen(dr)
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICin866_maxFrameLen(dr)
int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICin866_pow2dBm(mcmd_ladr_p1)
@ -54,13 +56,7 @@ LMICin866_isValidBeacon1(const uint8_t *d) {
// override default for LMICbandplan_isFSK()
#undef LMICbandplan_isFSK
#define LMICbandplan_isFSK() (/* TX datarate */LMIC.rxsyms == IN866_DR_FSK)
// txDone handling for FSK.
void
LMICin866_txDoneFSK(ostime_t delay, osjobcb_t func);
#define LMICbandplan_txDoneFsk(delay, func) LMICin866_txDoneFSK(delay, func)
#define LMICbandplan_isFSK() (/* TX datarate */LMIC.dndr == IN866_DR_FSK)
#define LMICbandplan_getInitialDrJoin() (IN866_DR_SF7)
@ -82,4 +78,7 @@ ostime_t LMICin866_nextJoinState(void);
void LMICin866_initDefaultChannels(bit_t join);
#define LMICbandplan_initDefaultChannels(join) LMICin866_initDefaultChannels(join)
#endif // _lmic_in866_h_
void LMICin866_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICin866_setRx1Params()
#endif // _lmic_bandplan_in866_h_

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lmic_kr920_h_
# define _lmic_kr920_h_
#ifndef _lmic_eu_like_h_
# include "lmic_eu_like.h"
#endif
// return maximum frame length (including PHY header) for this data rate (kr920); 0 --> not valid dr.
uint8_t LMICkr920_maxFrameLen(uint8_t dr);
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICkr920_maxFrameLen(dr)
int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICkr920_pow2dBm(mcmd_ladr_p1)
// Times for half symbol per DR
// Per DR table to minimize rounding errors
ostime_t LMICkr920_dr2hsym(uint8_t dr);
#define dr2hsym(dr) LMICkr920_dr2hsym(dr)
// TODO(tmm@mcci.com) this looks bogus compared to current 1.02 regional
// spec. https://github.com/mcci-catena/arduino-lmic/issues/18
static inline int
LMICkr920_isValidBeacon1(const uint8_t *d) {
return d[OFF_BCN_CRC1] != (u1_t)os_crc16(d, OFF_BCN_CRC1);
}
#undef LMICbandplan_isValidBeacon1
#define LMICbandplan_isValidBeacon1(pFrame) LMICkr920_isValidBeacon1(pFrame)
// override default for LMICbandplan_isFSK()
#undef LMICbandplan_isFSK
#define LMICbandplan_isFSK() (/* always false */ 0)
#define LMICbandplan_getInitialDrJoin() (KR920_DR_SF7)
void LMICkr920_setBcnRxParams(void);
#define LMICbandplan_setBcnRxParams() LMICkr920_setBcnRxParams()
u4_t LMICkr920_convFreq(xref2cu1_t ptr);
#define LMICbandplan_convFreq(ptr) LMICkr920_convFreq(ptr)
void LMICkr920_initJoinLoop(void);
#define LMICbandplan_initJoinLoop() LMICkr920_initJoinLoop()
ostime_t LMICkr920_nextTx(ostime_t now);
#define LMICbandplan_nextTx(now) LMICkr920_nextTx(now)
ostime_t LMICkr920_nextJoinState(void);
#define LMICbandplan_nextJoinState() LMICkr920_nextJoinState()
void LMICkr920_initDefaultChannels(bit_t join);
#define LMICbandplan_initDefaultChannels(join) LMICkr920_initDefaultChannels(join)
void LMICkr920_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICkr920_setRx1Params()
#undef LMICbandplan_updateTx
void LMICkr920_updateTx(ostime_t txbeg);
#define LMICbandplan_updateTx(t) LMICkr920_updateTx(t)
#endif // _lmic_kr920_h_

23
src/lmic/lmic_bandplan_us915.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,26 +26,30 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lmic_us915_h_
# define _lmic_us915_h_
#ifndef _lmic_bandplan_us915_h_
# define _lmic_bandplan_us915_h_
// preconditions for lmic_us_like.h
#define LMICuslike_getFirst500kHzDR() (US915_DR_SF8C)
#define LMICuslike_getFirst500kHzDR() (LORAWAN_DR4)
#define LMICuslike_getJoin125kHzDR() (LORAWAN_DR0)
#ifndef _lmic_us_like_h_
# include "lmic_us_like.h"
#endif
// return maximum frame length (including PHY header) for this data rate (us915); 0 --> not valid dr.
uint8_t LMICus915_maxFrameLen(uint8_t dr);
#define maxFrameLen(dr) LMICus915_maxFrameLen(dr)
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICus915_maxFrameLen(dr)
#define pow2dBm(mcmd_ladr_p1) ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1)))
int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICus915_pow2dbm(mcmd_ladr_p1)
ostime_t LMICus915_dr2hsym(uint8_t dr);
#define dr2hsym(dr) LMICus915_dr2hsym(dr)
#define LMICbandplan_getInitialDrJoin() (US915_DR_SF7)
#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR0)
void LMICus915_setBcnRxParams(void);
#define LMICbandplan_setBcnRxParams() LMICus915_setBcnRxParams()
@ -53,10 +57,13 @@ void LMICus915_setBcnRxParams(void);
u4_t LMICus915_convFreq(xref2cu1_t ptr);
#define LMICbandplan_convFreq(ptr) LMICus915_convFreq(ptr)
void LMICus915_initJoinLoop(void);
#define LMICbandplan_initJoinLoop() LMICus915_initJoinLoop()
void LMICus915_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICus915_setRx1Params()
void LMICus915_updateTx(ostime_t txbeg);
#define LMICbandplan_updateTx(txbeg) LMICus915_updateTx(txbeg)
#endif // _lmic_us915_h_
#endif // _lmic_bandplan_us915_h_

72
src/lmic/lmic_compat.h Normal file
View File

@ -0,0 +1,72 @@
/*
Module: lmic_compat.h
Function:
Symbols that are defined for backward compatibility
Copyright notice and license info:
See LICENSE file accompanying this project.
Author:
Terry Moore, MCCI Corporation January 2020
Description:
This include file centralizes backwards compatibility
definitions. The idea is to centralize the decision,
so it's clear as to what's deprecated.
*/
#ifndef _lmic_compat_h_ /* prevent multiple includes */
#define _lmic_compat_h_
#ifdef __cplusplus
extern "C"{
#endif
#ifndef ARDUINO_LMIC_VERSION
# error "This file is normally included from lmic.h, not stand alone"
#endif
#define LMIC_DEPRECATE(m) _Pragma(#m)
#if ! defined(LMIC_REGION_au921) && ARDUINO_LMIC_VERSION < ARDUINO_LMIC_VERSION_CALC(5,0,0,0)
# define LMIC_REGION_au921 LMIC_DEPRECATE(GCC warning "LMIC_REGION_au921 is deprecated, EOL at V5, use LMIC_REGION_au915") \
LMIC_REGION_au915
// Frequency plan symbols
# define AU921_DR_SF12 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF12
# define AU921_DR_SF11 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF11
# define AU921_DR_SF10 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF10
# define AU921_DR_SF9 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF9
# define AU921_DR_SF8 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8
# define AU921_DR_SF7 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF7
# define AU921_DR_SF8C LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8C
# define AU921_DR_NONE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_NONE
# define AU921_DR_SF12CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF12CR
# define AU921_DR_SF11CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF11CR
# define AU921_DR_SF10CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF10CR
# define AU921_DR_SF9CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF9CR
# define AU921_DR_SF8CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF8CR
# define AU921_DR_SF7CR LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_DR_SF7CR
# define AU921_125kHz_UPFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_125kHz_UPFBASE
# define AU921_125kHz_UPFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_125kHz_UPFSTEP
# define AU921_500kHz_UPFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_UPFBASE
# define AU921_500kHz_UPFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_UPFSTEP
# define AU921_500kHz_DNFBASE LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_DNFBASE
# define AU921_500kHz_DNFSTEP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_500kHz_DNFSTEP
# define AU921_FREQ_MIN LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_FREQ_MIN
# define AU921_FREQ_MAX LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_FREQ_MAX
# define AU921_TX_EIRP_MAX_DBM LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_TX_EIRP_MAX_DBM
# define AU921_INITIAL_TxParam_UplinkDwellTime LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_INITIAL_TxParam_UplinkDwellTime
# define AU921_UPLINK_DWELL_TIME_osticks LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_UPLINK_DWELL_TIME_osticks
# define DR_PAGE_AU921 LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") DR_PAGE_AU915
# define AU921_LMIC_REGION_EIRP LMIC_DEPRECATE(GCC warning "A921 symbols are deprecated EOL V5, use AU915") AU915_LMIC_REGION_EIRP
#endif
#ifdef __cplusplus
}
#endif
#endif /* _lmic_compat_h_ */

771
src/lmic/lmic_compliance.c Normal file
View File

@ -0,0 +1,771 @@
/*
Module: lmic_compliance.c
Function:
Implementation of the compliance engine.
Copyright notice and license info:
See LICENSE file accompanying this project.
Author:
Terry Moore, MCCI Corporation March 2019
Description:
See function descriptions.
*/
#include "lmic.h"
#include "lmic_compliance.h"
#include "lorawan_spec_compliance.h"
#include <stdbool.h>
#include <string.h>
#if defined(LMIC_PRINTF_TO)
# include <stdio.h>
# define LMIC_COMPLIANCE_PRINTF(f, ...) printf(f, ## __VA_ARGS__)
#else
# define LMIC_COMPLIANCE_PRINTF(f, ...) do { ; } while (0)
#endif
/****************************************************************************\
|
| Manifest constants and local declarations.
|
\****************************************************************************/
static void acEnterActiveMode(void);
static void acExitActiveMode(void);
static void acSendUplink(void);
static void acSetTimer(ostime_t);
static void acSendUplinkBuffer(void);
static void evActivate(void);
static void evDeactivate(void);
static void evJoinCommand(void);
static void evMessage(const uint8_t *pMessage, size_t nMessage);
static lmic_compliance_fsmstate_t fsmDispatch(lmic_compliance_fsmstate_t, bool);
static void fsmEval(void);
static void fsmEvalDeferred(void);
static osjobcbfn_t fsmJobCb;
static bool isActivateMessage(const uint8_t *pMessage, size_t nMessage);
static void evEchoCommand(const uint8_t *pMessage, size_t nMessage);
static lmic_event_cb_t lmicEventCb;
static lmic_txmessage_cb_t sendUplinkCompleteCb;
static osjobcbfn_t timerExpiredCb;
/* these are declared global so the optimizer can chuck them without warnings */
const char *LMICcompliance_txSuccessToString(int fSuccess);
const char * LMICcompliance_fsmstate_getName(lmic_compliance_fsmstate_t state);
/****************************************************************************\
|
| Read-only data.
|
\****************************************************************************/
/****************************************************************************\
|
| Variables.
|
\****************************************************************************/
lmic_compliance_t LMIC_Compliance;
/*
Name: LMIC_complianceRxMessage()
Function:
Add compliance-awareness to LMIC applications by filtering messages.
Definition:
lmic_compliance_rx_action_t LMIC_complianceRxMessage(
u1_t port,
const u1_t *pMessage,
size_t nMessage
);
Description:
Clients who want to handle the LoRaWAN compliance protocol on
port 224 should call this routine each time a downlink message is
received. This function will update the internal compliance state,
and return an appropriate action to the user.
If the result is `LMIC_COMPLIANCE_RX_ACTION_PROCESS`, then the client should
process the message as usual. Otherwise, the client should discard the
message. The other values further allow the client to track entry into,
and exit from, compliance state. `LMIC_COMPLIANCE_RX_ACTION_START` signals
entry into compliance state; `LMIC_COMPLIANCE_RX_ACTION_END` signals exit
from compliance state; and `LMIC_COMPLIANCE_RX_ACTION_IGNORE` indicates
a mesage that should be discarded while already in compliance
state.
Returns:
See description.
*/
lmic_compliance_rx_action_t
LMIC_complianceRxMessage(
uint8_t port,
const uint8_t *pMessage,
size_t nMessage
) {
lmic_compliance_state_t const complianceState = LMIC_Compliance.state;
// update the counter used by the status message.
++LMIC_Compliance.downlinkCount;
// filter normal messages.
if (port != LORAWAN_PORT_COMPLIANCE) {
return lmic_compliance_state_IsActive(complianceState)
? LMIC_COMPLIANCE_RX_ACTION_PROCESS
: LMIC_COMPLIANCE_RX_ACTION_IGNORE
;
}
// it's a message to port 224.
// if we're not active, ignore everything but activation messages
if (! lmic_compliance_state_IsActive(complianceState)) {
if (isActivateMessage(pMessage, nMessage)) {
evActivate();
} // else ignore.
} else {
evMessage(pMessage, nMessage);
}
if (lmic_compliance_state_IsActive(complianceState) == lmic_compliance_state_IsActive(LMIC_Compliance.state))
return LMIC_COMPLIANCE_RX_ACTION_IGNORE;
else if (! lmic_compliance_state_IsActive(complianceState))
return LMIC_COMPLIANCE_RX_ACTION_START;
else
return LMIC_COMPLIANCE_RX_ACTION_END;
}
/*
Name: isActivateMessage()
Function:
See whether a message is a LoRaWAN activate test mode message.
Definition:
static bool isActivateMessage(
const uint8_t *pMessage,
size_t nMessage
);
Description:
The message body is compared to an activate message (per the
LoRa Alliance End Device Certification spec).
Returns:
The result is `true` if the message is an activation message;
it's `false` otherwise.
*/
static bool
isActivateMessage(
const uint8_t *pMessage,
size_t nMessage
) {
const uint8_t body[LORAWAN_COMPLIANCE_CMD_ACTIVATE_LEN] = {
LORAWAN_COMPLIANCE_CMD_ACTIVATE,
LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC,
LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC,
LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC,
};
if (nMessage != sizeof(body))
return false;
if (memcmp(pMessage, body, sizeof(body)) == 0)
return true;
else
return false;
}
/*
Name: evActivate()
Function:
Report an activation event to the finite state machine.
Definition:
void evActivate(void);
Description:
We report an activation event, and re-evaluate the FSM.
Returns:
No explicit result.
*/
static void evActivate(void) {
if (! lmic_compliance_state_IsActive(LMIC_Compliance.state)) {
LMIC_Compliance.downlinkCount = 0;
LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_ACTIVATE;
LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_ACTIVATING;
LMIC_Compliance.saveEvent.pEventCb = LMIC.client.eventCb;
LMIC_Compliance.saveEvent.pUserData = LMIC.client.eventUserData;
#if CFG_LMIC_EU_like
band_t *b = LMIC.bands;
lmic_compliance_band_t *b_save = LMIC_Compliance.saveBands;
for (; b < &LMIC.bands[MAX_BANDS]; ++b, ++b_save) {
b_save->txcap = b->txcap;
b->txcap = 1;
b->avail = os_getTime();
}
#endif // CFG_LMIC_EU_like
LMIC_registerEventCb(lmicEventCb, NULL);
fsmEvalDeferred();
} else {
LMIC_COMPLIANCE_PRINTF("Redundant ActivateTM message ignored.\n");
}
}
/*
Name: evMessage()
Function:
Process an inbound message while active.
Definition:
void evMessage(const uint8_t *pMessage, size_t nMessage);
Description:
The event is parsed, and the appropriate event(s) are sent into
the finite state machine. Note that because of the way the LMIC
works, we can assume that no uplink event is pending; so it's safe
to launch a send from here.
Returns:
No explicit result.
*/
static void evMessage(
const uint8_t *pMessage,
size_t nMessage
) {
if (nMessage == 0)
return;
const uint8_t cmd = pMessage[0];
switch (cmd) {
case LORAWAN_COMPLIANCE_CMD_DEACTIVATE: {
evDeactivate();
break;
}
case LORAWAN_COMPLIANCE_CMD_ACTIVATE: {
if (isActivateMessage(pMessage, nMessage))
evActivate();
break;
}
case LORAWAN_COMPLIANCE_CMD_SET_CONFIRM: {
LMIC_Compliance.fsmFlags |= LMIC_COMPLIANCE_FSM_CONFIRM;
break;
}
case LORAWAN_COMPLIANCE_CMD_SET_UNCONFIRM: {
LMIC_Compliance.fsmFlags &= ~LMIC_COMPLIANCE_FSM_CONFIRM;
break;
}
case LORAWAN_COMPLIANCE_CMD_ECHO: {
evEchoCommand(pMessage, nMessage);
break;
}
case LORAWAN_COMPLIANCE_CMD_LINK: {
// not clear what this request does.
break;
}
case LORAWAN_COMPLIANCE_CMD_JOIN: {
evJoinCommand();
break;
}
default:
break;
}
}
/*
Name: evDeactivate()
Function:
Report an deactivation event to the finite state machine.
Definition:
void evDectivate(void);
Description:
We report a deactivation event, and re-evaluate the FSM.
We also set a flag so that we're return the appropriate
status from the compliance entry point to the real
application.
Returns:
No explicit result.
*/
static void evDeactivate(void) {
LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_DEACTIVATE;
LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_STOPPING;
// restore user's event handler.
LMIC_registerEventCb(LMIC_Compliance.saveEvent.pEventCb, LMIC_Compliance.saveEvent.pUserData);
// restore band settings
#if CFG_LMIC_EU_like
band_t *b = LMIC.bands;
lmic_compliance_band_t const *b_save = LMIC_Compliance.saveBands;
for (; b < &LMIC.bands[MAX_BANDS]; ++b, ++b_save) {
b->txcap = b_save->txcap;
}
#endif // CFG_LMIC_EU_like
fsmEvalDeferred();
}
/*
Name: evJoinCommand()
Function:
Report that a join has been commanded.
Definition:
void evJoinCommand(void);
Description:
We unjoin from the network, and then report a deactivation
of test mode. That will get us out of test mode and back
to the compliance app. The next message send will trigger
a join.
Returns:
No explicit result.
*/
static void evJoinCommand(
void
) {
LMIC_unjoin();
evDeactivate();
}
/*
Name: evEchoCommand()
Function:
Format and transmit the response to an echo downlink (aka echo request).
Definition:
void evEchoCommand(
const uint8_t *pMessage,
size_t nMessage
);
Description:
The echo response is formatted and transmitted. Since we just received
a downlink, it's always safe to do this.
Returns:
No explicit result.
*/
static void evEchoCommand(
const uint8_t *pMessage,
size_t nMessage
) {
uint8_t *pResponse;
if (nMessage > sizeof(LMIC_Compliance.uplinkMessage))
return;
// create the echo message.
pResponse = LMIC_Compliance.uplinkMessage;
// copy the command byte unchanged.
*pResponse++ = *pMessage++;
--nMessage;
// each byte in the body has to be incremented by one.
for (; nMessage > 0; --nMessage) {
*pResponse++ = (uint8_t)(*pMessage++ + 1);
}
// now that the message is formatted, tell the fsm to send it;
// need to use a separate job.
LMIC_Compliance.uplinkSize = (uint8_t) (pResponse - LMIC_Compliance.uplinkMessage);
LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_ECHO_REQUEST;
fsmEvalDeferred();
}
/*
Name: fsmEval()
Function:
Evaluate the FSM, preventing recursion.
Definition:
void fsmEval(void);
Description:
We check for a nested call to evaluate the FSM;
if detected, the processing is deferred until the
current evaluation completes. Otherwise, we start
a new FSM evaluation, which proceeds until the FSM
returns a "no-change" result.
Returns:
No explicit result.
*/
const char * LMICcompliance_fsmstate_getName(lmic_compliance_fsmstate_t state) {
const char * const names[] = { LMIC_COMPLIANCE_FSMSTATE__NAMES };
if ((unsigned) state >= sizeof(names)/sizeof(names[0]))
return "<<unknown>>";
else
return names[state];
}
static void fsmEvalDeferred(void) {
os_setCallback(&LMIC_Compliance.fsmJob, fsmJobCb);
}
static void fsmJobCb(osjob_t *j) {
LMIC_API_PARAMETER(j);
fsmEval();
}
static void fsmEval(void) {
bool fNewState;
// check for reentry.
do {
lmic_compliance_fsmflags_t const fsmFlags = LMIC_Compliance.fsmFlags;
if (fsmFlags & LMIC_COMPLIANCE_FSM_ACTIVE) {
LMIC_Compliance.fsmFlags = fsmFlags | LMIC_COMPLIANCE_FSM_REENTERED;
return;
}
// record that we're active
LMIC_Compliance.fsmFlags = fsmFlags | LMIC_COMPLIANCE_FSM_ACTIVE;
} while (0);
// evaluate and change state
fNewState = false;
for (;;) {
lmic_compliance_fsmstate_t const oldState = LMIC_Compliance.fsmState;
lmic_compliance_fsmstate_t newState;
newState = fsmDispatch(oldState, fNewState);
if (newState == LMIC_COMPLIANCE_FSMSTATE_NOCHANGE) {
lmic_compliance_fsmflags_t const fsmFlags = LMIC_Compliance.fsmFlags;
if ((fsmFlags & LMIC_COMPLIANCE_FSM_REENTERED) == 0) {
// not reentered, no change: get out.
LMIC_Compliance.fsmFlags = fsmFlags & ~LMIC_COMPLIANCE_FSM_ACTIVE;
return;
} else {
// reentered. reset reentered flag and keep going.
LMIC_Compliance.fsmFlags = fsmFlags & ~LMIC_COMPLIANCE_FSM_REENTERED;
fNewState = false;
}
} else {
// state change!
LMIC_COMPLIANCE_PRINTF("%s: change state %s(%u) => %s(%u)\n",
__func__,
LMICcompliance_fsmstate_getName(oldState), (unsigned) oldState,
LMICcompliance_fsmstate_getName(newState), (unsigned) newState
);
fNewState = true;
LMIC_Compliance.fsmState = newState;
}
}
}
/*
Name: fsmDispatch()
Function:
Dispatch to the appropriate event handler.
Definition:
lmic_compliance_fsmstate_t fsmDispatch(
lmic_compliance_fsmstate_t state,
bool fEntry
);
Description:
This function is called by the evalutator as needed. `state`
is set to the current state of the FSM, and `fEntry` is
true if and only if this state has just been entered via a
transition arrow. (Might be a transition to self.)
Returns:
This function returns LMIC_COMPLIANCE_FSMSTATE_NOCHANGE if
the FSM is to remain in this state until an event occurs.
Otherwise it returns the new state.
*/
static inline lmic_compliance_eventflags_t
eventflags_TestAndClear(lmic_compliance_eventflags_t flag) {
const lmic_compliance_eventflags_t old = LMIC_Compliance.eventflags;
const lmic_compliance_eventflags_t result = old & flag;
if (result != 0)
LMIC_Compliance.eventflags = old ^ result;
return result;
}
static lmic_compliance_fsmstate_t
fsmDispatch(
lmic_compliance_fsmstate_t state,
bool fEntry
) {
lmic_compliance_fsmstate_t newState;
// currently, this is a stub.
newState = LMIC_COMPLIANCE_FSMSTATE_NOCHANGE;
switch (state) {
case LMIC_COMPLIANCE_FSMSTATE_INITIAL: {
newState = LMIC_COMPLIANCE_FSMSTATE_INACTIVE;
break;
}
case LMIC_COMPLIANCE_FSMSTATE_INACTIVE: {
if (fEntry) {
acExitActiveMode();
}
if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_ACTIVATE)) {
newState = LMIC_COMPLIANCE_FSMSTATE_ACTIVE;
}
break;
}
case LMIC_COMPLIANCE_FSMSTATE_ACTIVE: {
if (fEntry) {
acEnterActiveMode();
acSetTimer(sec2osticks(1));
}
if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) {
newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE;
}
break;
}
case LMIC_COMPLIANCE_FSMSTATE_TXBUSY: {
if (fEntry) {
acSetTimer(sec2osticks(1));
}
if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) {
newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE;
}
break;
}
case LMIC_COMPLIANCE_FSMSTATE_TESTMODE: {
if (LMIC.opmode & OP_TXDATA) {
// go back and wait some more.
newState = LMIC_COMPLIANCE_FSMSTATE_TXBUSY;
}
if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_DEACTIVATE)) {
newState = LMIC_COMPLIANCE_FSMSTATE_INACTIVE;
} else if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_ECHO_REQUEST)) {
newState = LMIC_COMPLIANCE_FSMSTATE_ECHOING;
} else {
newState = LMIC_COMPLIANCE_FSMSTATE_REPORTING;
}
break;
}
case LMIC_COMPLIANCE_FSMSTATE_ECHOING: {
if (fEntry)
acSendUplinkBuffer();
if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE)) {
newState = LMIC_COMPLIANCE_FSMSTATE_RECOVERY;
}
break;
}
case LMIC_COMPLIANCE_FSMSTATE_REPORTING: {
if (fEntry)
acSendUplink();
if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE)) {
newState = LMIC_COMPLIANCE_FSMSTATE_RECOVERY;
}
break;
}
case LMIC_COMPLIANCE_FSMSTATE_RECOVERY: {
if (fEntry) {
if (LMIC_Compliance.eventflags & (LMIC_COMPLIANCE_EVENT_DEACTIVATE |
LMIC_COMPLIANCE_EVENT_ECHO_REQUEST)) {
acSetTimer(sec2osticks(1));
} else {
acSetTimer(sec2osticks(5));
}
}
if (eventflags_TestAndClear(LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED)) {
newState = LMIC_COMPLIANCE_FSMSTATE_TESTMODE;
}
break;
}
default: {
break;
}
}
return newState;
}
static void acEnterActiveMode(void) {
// indicate to the outer world that we're active.
LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_ACTIVE;
}
void acSetTimer(ostime_t delay) {
os_setTimedCallback(&LMIC_Compliance.timerJob, os_getTime() + delay, timerExpiredCb);
}
static void timerExpiredCb(osjob_t *j) {
LMIC_API_PARAMETER(j);
LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED;
fsmEval();
}
static void lmicEventCb(
void *pUserData,
ev_t ev
) {
LMIC_API_PARAMETER(pUserData);
// pass to user handler
if (LMIC_Compliance.saveEvent.pEventCb) {
LMIC_Compliance.saveEvent.pEventCb(
LMIC_Compliance.saveEvent.pUserData, ev
);
}
// if it's a EV_JOINED, or a TXCMOMPLETE, we should tell the FSM.
if ((UINT32_C(1) << ev) & (EV_JOINED | EV_TXCOMPLETE)) {
fsmEvalDeferred();
}
}
static void acExitActiveMode(void) {
LMIC_Compliance.state = LMIC_COMPLIANCE_STATE_IDLE;
os_clearCallback(&LMIC_Compliance.timerJob);
LMIC_clrTxData();
}
static void acSendUplink(void) {
uint8_t payload[2];
uint32_t const downlink = LMIC_Compliance.downlinkCount;
// build the uplink message
payload[0] = (uint8_t) (downlink >> 8);
payload[1] = (uint8_t) downlink;
// reset the flags
LMIC_Compliance.eventflags &= ~LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE;
// don't try to send if busy; might be sending echo message.
lmic_tx_error_t const eSend =
LMIC_sendWithCallback_strict(
LORAWAN_PORT_COMPLIANCE,
payload, sizeof(payload),
/* confirmed? */
!! (LMIC_Compliance.fsmFlags & LMIC_COMPLIANCE_FSM_CONFIRM),
sendUplinkCompleteCb, NULL
);
if (eSend == LMIC_ERROR_SUCCESS) {
// queued successfully
LMIC_COMPLIANCE_PRINTF(
"lmic_compliance.%s: queued uplink message(%u, %p)\n",
__func__,
(unsigned) downlink & 0xFFFF,
LMIC.client.txMessageCb
);
} else {
// failed to queue; just skip this cycle.
LMIC_COMPLIANCE_PRINTF(
"lmic_compliance.%s: error(%d) sending uplink message(%u), %u bytes\n",
__func__,
eSend,
(unsigned) downlink & 0xFFFF,
(unsigned) sizeof(payload)
);
LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE;
fsmEval();
}
}
static void sendUplinkCompleteCb(void *pUserData, int fSuccess) {
LMIC_API_PARAMETER(pUserData);
LMIC_API_PARAMETER(fSuccess);
LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE;
LMIC_COMPLIANCE_PRINTF("%s(%s)\n", __func__, LMICcompliance_txSuccessToString(fSuccess));
fsmEvalDeferred();
}
static void acSendUplinkBuffer(void) {
// send uplink data.
lmic_tx_error_t const eSend =
LMIC_sendWithCallback_strict(
LORAWAN_PORT_COMPLIANCE,
LMIC_Compliance.uplinkMessage, LMIC_Compliance.uplinkSize,
/* confirmed? */ (LMIC_Compliance.fsmFlags & LMIC_COMPLIANCE_FSM_CONFIRM) != 0,
sendUplinkCompleteCb,
NULL);
if (eSend == LMIC_ERROR_SUCCESS) {
LMIC_COMPLIANCE_PRINTF("%s: queued %u bytes\n", __func__, LMIC_Compliance.uplinkSize);
} else {
LMIC_COMPLIANCE_PRINTF("%s: uplink %u bytes failed (error %d)\n", __func__, LMIC_Compliance.uplinkSize, eSend);
if (eSend == LMIC_ERROR_TX_NOT_FEASIBLE) {
// Reverse the increment of the downlink count. Needed for US compliance.
if (CFG_region == LMIC_REGION_us915)
--LMIC_Compliance.downlinkCount;
}
LMIC_Compliance.eventflags |= LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE;
fsmEval();
}
}
const char *LMICcompliance_txSuccessToString(int fSuccess) {
return fSuccess ? "ok" : "failed";
}

138
src/lmic/lmic_compliance.h Normal file
View File

@ -0,0 +1,138 @@
/*
Module: lmic_compliance.h
Function:
Internal header file for compliance-related work.
Copyright notice and license info:
See LICENSE file accompanying this project.
Author:
Terry Moore, MCCI Corporation March 2019
Description:
This header file allows us to break up the compliance
functions into multiple .c files if we wish.
*/
#ifndef _lmic_compliance_h_ /* prevent multiple includes */
#define _lmic_compliance_h_
#ifdef __cplusplus
extern "C"{
#endif
#ifndef _lmic_h_
# include "lmic.h"
#endif
#include <stdbool.h>
#include <stdint.h>
typedef struct lmic_compliance_s lmic_compliance_t;
// concrete type for the state enumeration for the compliance engine.
typedef uint8_t lmic_compliance_state_t;
enum lmic_compliance_state_e {
LMIC_COMPLIANCE_STATE_IDLE = 0, // app state
LMIC_COMPLIANCE_STATE_STOPPING = 1, // transitioning back to app
LMIC_COMPLIANCE_STATE_ACTIVATING = 2, // transitioning to compliance state
LMIC_COMPLIANCE_STATE_ACTIVE = 3, // in compliance state
};
// return true if a state value indicates that the FSM is active.
static inline bool
lmic_compliance_state_IsActive(lmic_compliance_state_t s) {
return s >= LMIC_COMPLIANCE_STATE_ACTIVATING;
}
// events from the outside world to the FSM
typedef uint8_t lmic_compliance_eventflags_t;
enum lmic_compliance_eventflags_e {
LMIC_COMPLIANCE_EVENT_ACTIVATE = 1u << 0,
LMIC_COMPLIANCE_EVENT_DEACTIVATE = 1u << 1,
LMIC_COMPLIANCE_EVENT_TIMER_EXPIRED = 1u << 2,
LMIC_COMPLIANCE_EVENT_UPLINK_COMPLETE = 1u << 3,
LMIC_COMPLIANCE_EVENT_ECHO_REQUEST = 1u << 4,
};
typedef uint8_t lmic_compliance_fsmflags_t;
enum lmic_compliance_fsmflags_e {
LMIC_COMPLIANCE_FSM_ACTIVE = 1u << 0,
LMIC_COMPLIANCE_FSM_REENTERED = 1u << 1,
LMIC_COMPLIANCE_FSM_CONFIRM = 1u << 2,
};
typedef uint8_t lmic_compliance_fsmstate_t;
enum lmic_compliance_fsmstate_e {
LMIC_COMPLIANCE_FSMSTATE_INITIAL = 0,
LMIC_COMPLIANCE_FSMSTATE_NOCHANGE = 1,
LMIC_COMPLIANCE_FSMSTATE_ACTIVE = 2,
LMIC_COMPLIANCE_FSMSTATE_INACTIVE = 3,
LMIC_COMPLIANCE_FSMSTATE_TESTMODE = 4, // sending test uplinks
LMIC_COMPLIANCE_FSMSTATE_ECHOING = 5,
LMIC_COMPLIANCE_FSMSTATE_REPORTING = 6,
LMIC_COMPLIANCE_FSMSTATE_RECOVERY = 7,
LMIC_COMPLIANCE_FSMSTATE_TXBUSY = 8,
};
#define LMIC_COMPLIANCE_FSMSTATE__NAMES \
"INITIAL", "NOCHANGE", "ACTIVE", "INACTIVE", "TESTMODE", \
"ECHOING", "REPORTING", "RECOVERY", "TXBUSY"
typedef struct lmic_compliance_eventcb_s lmic_compliance_eventcb_t;
struct lmic_compliance_eventcb_s {
// save the user's event CB while active.
lmic_event_cb_t *pEventCb;
// save the user's event data while active.
void *pUserData;
};
// structure for saving band settings during test
typedef struct lmic_compliance_band_s lmic_compliance_band_t;
struct lmic_compliance_band_s {
u2_t txcap; // saved 1/duty cycle
};
// the state of the compliance engine.
struct lmic_compliance_s {
// uint64
// uintptr
osjob_t timerJob; // the job for driving uplinks
osjob_t fsmJob; // job for reevaluating the FSM.
lmic_compliance_eventcb_t saveEvent; // the user's event handler.
// uint32
// uint16
#if CFG_LMIC_EU_like
lmic_compliance_band_t saveBands[MAX_BANDS];
#endif // CFG_LMIC_EU_like
// we are required to maintain a downlink count
// that is reset on join/test entry and incremented for
// each valid test message.
uint16_t downlinkCount;
// uint8
lmic_compliance_state_t state; // current state of compliance engine.
lmic_compliance_eventflags_t eventflags; // incoming events.
lmic_compliance_fsmflags_t fsmFlags; // FSM operational flags
lmic_compliance_fsmstate_t fsmState; // FSM current state
uint8_t uplinkSize;
uint8_t uplinkMessage[MAX_LEN_PAYLOAD];
};
extern lmic_compliance_t LMIC_Compliance;
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* _lmic_compliance_h_ */

View File

@ -67,10 +67,10 @@ Revision history:
#define LMIC_REGION_us915 2
#define LMIC_REGION_cn783 3
#define LMIC_REGION_eu433 4
#define LMIC_REGION_au921 5
#define LMIC_REGION_au915 5
#define LMIC_REGION_cn490 6
#define LMIC_REGION_as923 7
#define LMIC_REGION_kr921 8
#define LMIC_REGION_kr920 8
#define LMIC_REGION_in866 9
// Some regions have country-specific overrides. For generality, we specify
@ -93,6 +93,16 @@ Revision history:
# include CFG_TEXT_1(ARDUINO_LMIC_PROJECT_CONFIG_H)
#endif /* ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS */
#if defined(CFG_au921) && !defined(CFG_au915)
# warning "CFG_au921 was deprecated in favour of CFG_au915. Support for CFG_au921 might be removed in the future."
# define CFG_au915
#endif
// for backwards compatibility to legacy code, define CFG_au921 if we see CFG_au915.
#if defined(CFG_au915) && !defined(CFG_au921)
# define CFG_au921
#endif
// a mask of the supported regions
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
// user-editable.
@ -101,10 +111,10 @@ Revision history:
(1 << LMIC_REGION_us915) | \
/* (1 << LMIC_REGION_cn783) | */ \
/* (1 << LMIC_REGION_eu433) | */ \
(1 << LMIC_REGION_au921) | \
(1 << LMIC_REGION_au915) | \
/* (1 << LMIC_REGION_cn490) | */ \
(1 << LMIC_REGION_as923) | \
/* (1 << LMIC_REGION_kr921) | */ \
(1 << LMIC_REGION_kr920) | \
(1 << LMIC_REGION_in866) | \
0)
@ -114,19 +124,27 @@ Revision history:
// following values. These are in order of the sections in the manual. Not all of the
// below are supported yet.
//
// CFG_as923jp is treated as a special case of CFG_as923, so it's not included in
// the below.
//
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
// user-editable.
//
# define CFG_LMIC_REGION_MASK \
((defined(CFG_eu868) << LMIC_REGION_eu868) | \
(defined(CFG_us915) << LMIC_REGION_us915) | \
(defined(CFG_cn783) << LMIC_REGION_cn783) | \
(defined(CFG_eu433) << LMIC_REGION_eu433) | \
(defined(CFG_au921) << LMIC_REGION_au921) | \
(defined(CFG_au915) << LMIC_REGION_au915) | \
(defined(CFG_cn490) << LMIC_REGION_cn490) | \
(defined(CFG_as923) << LMIC_REGION_as923) | \
(defined(CFG_kr921) << LMIC_REGION_kr921) | \
(defined(CFG_kr920) << LMIC_REGION_kr920) | \
(defined(CFG_in866) << LMIC_REGION_in866) | \
0)
// the selected region.
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
// user-editable.
#if defined(CFG_eu868)
# define CFG_region LMIC_REGION_eu868
#elif defined(CFG_us915)
@ -135,48 +153,74 @@ Revision history:
# define CFG_region LMIC_REGION_cn783
#elif defined(CFG_eu433)
# define CFG_region LMIC_REGION_eu433
#elif defined(CFG_au921)
# define CFG_region LMIC_REGION_au921
#elif defined(CFG_au915)
# define CFG_region LMIC_REGION_au915
#elif defined(CFG_cn490)
# define CFG_region LMIC_REGION_cn490
#elif defined(CFG_as923jp)
# define CFG_as923 1 /* CFG_as923jp implies CFG_as923 */
# define CFG_region LMIC_REGION_as923
# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP
#elif defined(CFG_as923)
# define CFG_region LMIC_REGION_as923
#elif defined(CFG_kr921)
# define CFG_region LMIC_REGION_kr921
#elif defined(CFG_kr920)
# define CFG_region LMIC_REGION_kr920
#elif defined(CFG_in866)
# define CFG_region LMIC_REGION_in866
#else
# define CFG_region 0
#endif
// finally the mask of` US-like and EU-like regions
// a bitmask of EU-like regions -- these are regions which have up to 16
// channels indidually programmable via downloink.
//
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
// user-editable.
#define CFG_LMIC_EU_like_MASK ( \
(1 << LMIC_REGION_eu868) | \
/* (1 << LMIC_REGION_us915) | */ \
(1 << LMIC_REGION_cn783) | \
(1 << LMIC_REGION_eu433) | \
/* (1 << LMIC_REGION_au921) | */ \
/* (1 << LMIC_REGION_au915) | */ \
/* (1 << LMIC_REGION_cn490) | */ \
(1 << LMIC_REGION_as923) | \
(1 << LMIC_REGION_kr921) | \
(1 << LMIC_REGION_kr920) | \
(1 << LMIC_REGION_in866) | \
0)
// a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels
// overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but
// subsets of channels can be selected via masks.
//
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
// user-editable.
#define CFG_LMIC_US_like_MASK ( \
/* (1 << LMIC_REGION_eu868) | */ \
(1 << LMIC_REGION_us915) | \
/* (1 << LMIC_REGION_cn783) | */ \
/* (1 << LMIC_REGION_eu433) | */ \
(1 << LMIC_REGION_au921) | \
(1 << LMIC_REGION_au915) | \
/* (1 << LMIC_REGION_cn490) | */ \
/* (1 << LMIC_REGION_as923) | */ \
/* (1 << LMIC_REGION_kr921) | */ \
/* (1 << LMIC_REGION_kr920) | */ \
/* (1 << LMIC_REGION_in866) | */ \
0)
//
// booleans that are true if the configured region is EU-like or US-like.
// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
// user-editable.
//
#define CFG_LMIC_EU_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK))
#define CFG_LMIC_US_like (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK))
//
// The supported LMIC LoRaWAAN spec versions. These need to be numerically ordered,
// so that we can (for example) compare
//
// LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3.
//
#define LMIC_LORAWAN_SPEC_VERSION_1_0_2 0x01000200u
#define LMIC_LORAWAN_SPEC_VERSION_1_0_3 0x01000300u
#endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */

251
src/lmic/lmic_env.h Normal file
View File

@ -0,0 +1,251 @@
/*
Module: lmic_env.h
Function:
Sets up macros etc. to make things a little easier for portabilty
Copyright notice and license info:
See LICENSE file accompanying this project.
Author:
Terry Moore, MCCI Corporation November 2018
Description:
This file is an adaptation of MCCI's standard IOCTL framework.
We duplicate a bit of functionality that we might get from other
libraries, so that the LMIC library can continue to stand alone.
*/
#ifndef _lmic_env_h_ /* prevent multiple includes */
#define _lmic_env_h_
/*
Macro: LMIC_C_ASSERT()
Function:
Declaration-like macro that will cause a compile error if arg is FALSE.
Definition:
LMIC_C_ASSERT(
BOOL fErrorIfFalse
);
Description:
This macro, if used where an external reference declarataion is
permitted, will either compile cleanly, or will cause a compilation
error. The results of using this macro where a declaration is not
permitted are unspecified.
This is different from #if !(fErrorIfFalse) / #error in that the
expression is evaluated by the compiler rather than by the pre-
processor. Therefore things like sizeof() can be used.
Returns:
No explicit result -- either compiles cleanly or causes a compile
error.
*/
#ifndef LMIC_C_ASSERT
# define LMIC_C_ASSERT(e) \
void LMIC_C_ASSERT__(int LMIC_C_ASSERT_x[(e) ? 1: -1])
#endif
/****************************************************************************\
|
| Define the begin/end declaration tags for C++ co-existance
|
\****************************************************************************/
#ifdef __cplusplus
# define LMIC_BEGIN_DECLS extern "C" {
# define LMIC_END_DECLS }
#else
# define LMIC_BEGIN_DECLS /* nothing */
# define LMIC_END_DECLS /* nothing */
#endif
//----------------------------------------------------------------------------
// Annotations to avoid various "unused" warnings. These must appear as a
// statement in the function body; the macro annotates the variable to quiet
// compiler warnings. The way this is done is compiler-specific, and so these
// definitions are fall-backs, which might be overridden.
//
// Although these are all similar, we don't want extra macro expansions,
// so we define each one explicitly rather than relying on a common macro.
//----------------------------------------------------------------------------
// signal that a parameter is intentionally unused.
#ifndef LMIC_UNREFERENCED_PARAMETER
# define LMIC_UNREFERENCED_PARAMETER(v) do { (void) (v); } while (0)
#endif
// an API parameter is a parameter that is required by an API definition, but
// happens to be unreferenced in this implementation. This is a stronger
// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here
// becuase of an API contract, but we have no use for it in this function.
#ifndef LMIC_API_PARAMETER
# define LMIC_API_PARAMETER(v) do { (void) (v); } while (0)
#endif
// an intentionally-unreferenced variable.
#ifndef LMIC_UNREFERENCED_VARIABLE
# define LMIC_UNREFERENCED_VARIABLE(v) do { (void) (v); } while (0)
#endif
// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1,
// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or
// or varables that are only refereneced at the target debug level.
// Parameter referenced only if debugging at level > 0.
#ifndef LMIC_DEBUG1_PARAMETER
# if LMIC_DEBUG_LEVEL > 0
# define LMIC_DEBUG1_PARAMETER(v) do { ; } while (0)
# else
# define LMIC_DEBUG1_PARAMETER(v) do { (void) (v); } while (0)
# endif
#endif
// variable referenced only if debugging at level > 0
#ifndef LMIC_DEBUG1_VARIABLE
# if LMIC_DEBUG_LEVEL > 0
# define LMIC_DEBUG1_VARIABLE(v) do { ; } while (0)
# else
# define LMIC_DEBUG1_VARIABLE(v) do { (void) (v); } while (0)
# endif
#endif
// parameter referenced only if debugging at level > 1
#ifndef LMIC_DEBUG2_PARAMETER
# if LMIC_DEBUG_LEVEL > 1
# define LMIC_DEBUG2_PARAMETER(v) do { ; } while (0)
# else
# define LMIC_DEBUG2_PARAMETER(v) do { (void) (v); } while (0)
# endif
#endif
// variable referenced only if debugging at level > 1
#ifndef LMIC_DEBUG2_VARIABLE
# if LMIC_DEBUG_LEVEL > 1
# define LMIC_DEBUG2_VARIABLE(v) do { ; } while (0)
# else
# define LMIC_DEBUG2_VARIABLE(v) do { (void) (v); } while (0)
# endif
#endif
// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0
#ifndef LMIC_X_DEBUG_PARAMETER
# if LMIC_X_DEBUG_LEVEL > 0
# define LMIC_X_DEBUG_PARAMETER(v) do { ; } while (0)
# else
# define LMIC_X_DEBUG_PARAMETER(v) do { (void) (v); } while (0)
# endif
#endif
// variable referenced only if LMIC_X_DEBUG_LEVEL > 0
#ifndef LMIC_X_DEBUG_VARIABLE
# if LMIC_X_DEBUG_LEVEL > 0
# define LMIC_X_DEBUG_VARIABLE(v) do { ; } while (0)
# else
# define LMIC_X_DEBUG_VARIABLE(v) do { (void) (v); } while (0)
# endif
#endif
// parameter referenced only if EV() macro is enabled (which it never is)
// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and
// this code is really C-99 to its bones.
#ifndef LMIC_EV_PARAMETER
# define LMIC_EV_PARAMETER(v) do { (void) (v); } while (0)
#endif
// variable referenced only if EV() macro is defined.
#ifndef LMIC_EV_VARIABLE
# define LMIC_EV_VARIABLE(v) do { (void) (v); } while (0)
#endif
/*
Macro: LMIC_ABI_STD
Index: Macro: LMIC_ABI_VARARGS
Function:
Annotation macros to force a particular binary calling sequence.
Definition:
#define LMIC_ABI_STD compiler-specific
#define LMIC_ABI_VARARGS compiler-specific
Description:
These macros are used when declaring a function type, and indicate
that a particular calling sequence is to be used. They are normally
used between the type portion of the function declaration and the
name of the function. For example:
typedef void LMIC_ABI_STD myCallBack_t(void);
It's important to use this in libraries on platforms with multiple
calling sequences, because different components can be compiled with
different defaults.
Returns:
Not applicable.
*/
/* ABI marker for normal (fixed parameter count) functions -- used for function types */
#ifndef LMIC_ABI_STD
# ifdef _MSC_VER
# define LMIC_ABI_STD __stdcall
# else
# define LMIC_ABI_STD /* nothing */
# endif
#endif
/* ABI marker for VARARG functions -- used for function types */
#ifndef LMIC_ABI_VARARGS
# ifdef _MSC_VER
# define LMIC_ABI_VARARGS __cdecl
# else
# define LMIC_ABI_VARARGS /* nothing */
# endif
#endif
/*
Macro: LMIC_DECLARE_FUNCTION_WEAK()
Function:
Declare an external function as a weak reference.
Definition:
#define LMIC_DECLARE_FUNCTION_WEAK(ReturnType, FunctionName, Params) ...
Description:
This macro generates a weak reference to the specified function.
Example:
LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e));
This saya that onEvent is a weak external reference. When calling
onEvent, you must always first check whether it's supplied:
if (onEvent != NULL)
onEvent(e);
Returns:
This macro expands to a declaration, without a trailing semicolon.
Notes:
This form allows for compilers that use _Pragma(weak, name) instead
of inline attributes.
*/
#define LMIC_DECLARE_FUNCTION_WEAK(a_ReturnType, a_FunctionName, a_Params) \
a_ReturnType __attribute__((__weak__)) a_FunctionName a_Params
#endif /* _lmic_env_h_ */

124
src/lmic/lmic_eu868.c Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -49,21 +49,28 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS
};
static CONST_TABLE(u1_t, maxFrameLens)[] = { 64,64,64,123 };
static CONST_TABLE(u1_t, maxFrameLens)[] = {
59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 250+5
};
uint8_t LMICeu868_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
return 0xFF;
return 0;
}
static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0
16, 14, 12, 10, 8, 6, 4, 2
};
int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) {
return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT);
uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT;
if (pindex < LENOF_TABLE(TXPOWLEVELS)) {
return TABLE_GET_S1(TXPOWLEVELS, pindex);
} else {
return -128;
}
}
// only used in this module, but used by variant macro dr2hsym().
@ -75,7 +82,7 @@ static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = {
us2osticksRound(128 << 3), // DR_SF8
us2osticksRound(128 << 2), // DR_SF7
us2osticksRound(128 << 1), // DR_SF7B
us2osticksRound(80) // FSK -- not used (time for 1/2 byte)
us2osticksRound(80) // FSK -- time for 1/2 byte (unused by LMIC)
};
ostime_t LMICeu868_dr2hsym(uint8_t dr) {
@ -93,6 +100,9 @@ static CONST_TABLE(u4_t, iniChannelFreq)[6] = {
void LMICeu868_initDefaultChannels(bit_t join) {
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
#if !defined(DISABLE_MCMD_DlChannelReq)
os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq));
#endif // !DISABLE_MCMD_DlChannelReq
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
@ -104,18 +114,9 @@ void LMICeu868_initDefaultChannels(bit_t join) {
LMIC.channelDrMap[fu] = DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7);
}
LMIC.bands[BAND_MILLI].txcap = 1000; // 0.1%
LMIC.bands[BAND_MILLI].txpow = 14;
LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS;
LMIC.bands[BAND_CENTI].txcap = 100; // 1%
LMIC.bands[BAND_CENTI].txpow = 14;
LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
LMIC.bands[BAND_DECI].txcap = 10; // 10%
LMIC.bands[BAND_DECI].txpow = 27;
LMIC.bands[BAND_DECI].lastchnl = os_getRndU1() % MAX_CHANNELS;
LMIC.bands[BAND_MILLI].avail =
LMIC.bands[BAND_CENTI].avail =
LMIC.bands[BAND_DECI].avail = os_getTime();
(void) LMIC_setupBand(BAND_MILLI, 14 /* dBm */, 1000 /* 0.1% */);
(void) LMIC_setupBand(BAND_CENTI, 14 /* dBm */, 100 /* 1% */);
(void) LMIC_setupBand(BAND_DECI, 27 /* dBm */, 10 /* 10% */);
}
bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
@ -129,26 +130,59 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
return 1;
}
// this table is from highest to lowest
static CONST_TABLE(u4_t, bandAssignments)[] = {
870000000 /* .. and above */ | BAND_MILLI,
869700000 /* .. 869700000 */ | BAND_CENTI,
869650000 /* .. 869700000 */ | BAND_MILLI,
869400000 /* .. 869650000 */ | BAND_DECI,
868600000 /* .. 869640000 */ | BAND_MILLI,
865000000 /* .. 868400000 */ | BAND_CENTI,
};
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
// zero the band bits in freq, just in case.
freq &= ~3;
if (chidx < NUM_DEFAULT_CHANNELS) {
// can't disable a default channel.
if (freq == 0)
return 0;
// can't change a default channel.
else if (freq != (LMIC.channelFreq[chidx] & ~3))
return 0;
}
bit_t fEnable = (freq != 0);
if (chidx >= MAX_CHANNELS)
return 0;
if (band == -1) {
if (freq >= 869400000 && freq <= 869650000)
freq |= BAND_DECI; // 10% 27dBm
else if ((freq >= 868000000 && freq <= 868600000) ||
(freq >= 869700000 && freq <= 870000000))
freq |= BAND_CENTI; // 1% 14dBm
else
freq |= BAND_MILLI; // 0.1% 14dBm
}
else {
if (band > BAND_AUX) return 0;
freq = (freq&~3) | band;
for (u1_t i = 0; i < LENOF_TABLE(bandAssignments); ++i) {
const u4_t thisFreqBand = TABLE_GET_U4(bandAssignments, i);
const u4_t thisFreq = thisFreqBand & ~3;
if (freq >= thisFreq) {
band = ((u1_t)thisFreqBand & 3);
break;
}
}
// if we didn't identify a frequency, it's millis.
if (band == -1) {
band = BAND_MILLI;
}
}
if ((u1_t)band > BAND_AUX)
return 0;
freq |= band;
LMIC.channelFreq[chidx] = freq;
// TODO(tmm@mcci.com): don't use US SF directly, use something from the LMIC context or a static const
LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7) : drmap;
LMIC.channelMap |= 1 << chidx; // enabled right away
if (fEnable)
LMIC.channelMap |= 1 << chidx; // enabled right away
else
LMIC.channelMap &= ~(1 << chidx);
return 1;
}
@ -213,12 +247,28 @@ ostime_t LMICeu868_nextJoinState(void) {
}
#endif // !DISABLE_JOIN
// txDone handling for FSK.
void
LMICeu868_txDoneFSK(ostime_t delay, osjobcb_t func) {
LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
LMIC.rxsyms = RXLEN_FSK;
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
// set the Rx1 dndr, rps.
void LMICeu868_setRx1Params(void) {
u1_t const txdr = LMIC.dndr;
s1_t drOffset;
s1_t candidateDr;
LMICeulike_setRx1Freq();
if ( LMIC.rx1DrOffset <= 5)
drOffset = (s1_t) LMIC.rx1DrOffset;
else
// make a reasonable assumption for unspecified value.
drOffset = 5;
candidateDr = (s1_t) txdr - drOffset;
if (candidateDr < LORAWAN_DR0)
candidateDr = 0;
else if (candidateDr > LORAWAN_DR7)
candidateDr = LORAWAN_DR7;
LMIC.dndr = (u1_t) candidateDr;
LMIC.rps = dndr2rps(LMIC.dndr);
}
void

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -32,35 +32,95 @@
#if CFG_LMIC_EU_like
void LMIC_enableSubBand(u1_t band) {
bit_t LMIC_enableSubBand(u1_t band) {
LMIC_API_PARAMETER(band);
return 0;
}
void LMIC_disableSubBand(u1_t band) {
bit_t LMIC_disableSubBand(u1_t band) {
LMIC_API_PARAMETER(band);
return 0;
}
void LMIC_disableChannel(u1_t channel) {
bit_t LMIC_disableChannel(u1_t channel) {
u2_t old_chmap = LMIC.channelMap;
LMIC.channelFreq[channel] = 0;
LMIC.channelDrMap[channel] = 0;
LMIC.channelMap &= ~(1 << channel);
LMIC.channelMap = old_chmap & ~(1 << channel);
return LMIC.channelMap != old_chmap;
}
// this is a no-op provided for compatibilty
void LMIC_enableChannel(u1_t channel) {
bit_t LMIC_enableChannel(u1_t channel) {
LMIC_API_PARAMETER(channel);
return 0;
}
u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
// Bad page, disable all channel, enable non-existent
if (chpage != 0 || chmap == 0 || (chmap & ~LMIC.channelMap) != 0)
return 0; // illegal input
for (u1_t chnl = 0; chnl<MAX_CHANNELS; chnl++) {
if ((chmap & (1 << chnl)) != 0 && LMIC.channelFreq[chnl] == 0)
chmap &= ~(1 << chnl); // ignore - channel is not defined
// check whether a map operation will work.
// chpage is 0 or 6; 6 turns all on; 0 selects channels 0..15 via mask.
// The spec is unclear as to whether we should veto a channel mask that enables
// a channel that hasn't been configured; we veto it.
bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap) {
switch (chpage) {
case MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT:
// we don't allow any channel to be turned on if its frequency is zero.
for (u1_t chnl = 0; chnl<MAX_CHANNELS; chnl++) {
if ((chmap & (1 << chnl)) != 0 && (LMIC.channelFreq[chnl]&~3) == 0)
return 0; // fail - channel is not defined
}
return 1;
case MCMD_LinkADRReq_ChMaskCntl_EULIKE_ALL_ON:
return 1;
default:
return 0;
}
}
// assumes that LMICeulike_canMapChannels passed. Return true if this would
// be a valid final configuration.
// chpage is 0 or 0x60; 0x60 turns all on; 0 selects channels 0..15 via mask.
// Assumes canMapChannels has already approved this change.
bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
switch (chpage) {
case MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT:
LMIC.channelMap = chmap;
break;
case MCMD_LinkADRReq_ChMaskCntl_EULIKE_ALL_ON: {
u2_t new_chmap = 0;
for (u1_t chnl = 0; chnl<MAX_CHANNELS; chnl++) {
if ((LMIC.channelFreq[chnl]&~3) != 0) {
new_chmap |= (1 << chnl);
}
}
LMIC.channelMap = new_chmap;
break;
}
LMIC.channelMap = chmap;
return 1;
default:
// do nothing.
break;
}
return LMIC.channelMap != 0;
}
bit_t LMICeulike_isDataRateFeasible(dr_t dr) {
// if the region uses TxpParam, then someone
// could have changed TxDwell, which makes some
// otherwise-legal DRs infeasible.
#if LMIC_ENABLE_TxParamSetupReq
if (LMICbandplan_maxFrameLen(dr) == 0) {
return 0;
}
#endif
for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) {
if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled
(LMIC.channelDrMap[chnl] & (1 << dr)) != 0)
return 1;
}
return 0;
}
#if !defined(DISABLE_JOIN)
@ -113,25 +173,26 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) {
LMIC.txChnl = 0;
if ((++LMIC.txCnt % nDefaultChannels) == 0) {
// Lower DR every nth try (having all default channels with same DR)
//
// TODO(tmm@mcci.com) add new DR_REGIN_JOIN_MIN instead of LORAWAN_DR0;
// then we can eliminate the LMIC_REGION_as923 below because we'll set
// the failed flag here. This will cause the outer caller to take the
// appropriate join path. Or add new LMICeulike_GetLowestJoinDR()
//
//
// TODO(tmm@mcci.com) add new DR_REGION_JOIN_MIN instead of LORAWAN_DR0;
// then we can eliminate the LMIC_REGION_as923 below because we'll set
// the failed flag here. This will cause the outer caller to take the
// appropriate join path. Or add new LMICeulike_GetLowestJoinDR()
//
// TODO(tmm@mcci.com) - see above; please remove regional dependency from this file.
#if CFG_region == LMIC_REGION_as923
// in the join of AS923 v1.1 or older, only DR2 is used.
// no need to change the DR.
LMIC.datarate = AS923_DR_SF10;
failed = 1;
#else
if (LMIC.datarate == LORAWAN_DR0)
failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
else
{
// TODO(tmm@mcci.com) - see above; please remove regional dependency from this file.
#if CFG_region != LMIC_REGION_as923
else {
LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
#else
// in the join of AS923 v1.1 or older, only DR2 is used.
// no need to change the DR.
LMIC.datarate = AS923_DR_SF10;
#endif
}
#endif
}
// Clear NEXTCHNL because join state engine controls channel hopping
LMIC.opmode &= ~OP_NEXTCHNL;
@ -139,12 +200,12 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) {
// Duty cycle is based on txend.
ostime_t const time = LMICbandplan_nextJoinTime(os_getTime());
// TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized;
// TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized;
// starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty
// cycle on power up. For testability, add a way to set the join start time
// externally (a test API) so we can check this feature.
// See https://github.com/mcci-catena/arduino-lmic/issues/2
// Current code doesn't match LoRaWAN 1.0.2 requirements.
// Current code doesn't match LoRaWAN 1.0.2 requirements.
LMIC.txend = time +
(isTESTMODE()
@ -159,4 +220,50 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) {
}
#endif // !DISABLE_JOIN
void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) {
os_copyMem(
pStateBuffer->channelFreq,
LMIC.channelFreq,
sizeof(LMIC.channelFreq)
);
pStateBuffer->channelMap = LMIC.channelMap;
}
bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) {
if (memcmp(pStateBuffer->channelFreq, LMIC.channelFreq, sizeof(LMIC.channelFreq)) != 0)
return 1;
return pStateBuffer->channelMap != LMIC.channelMap;
}
void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) {
os_copyMem(
LMIC.channelFreq,
pStateBuffer->channelFreq,
sizeof(LMIC.channelFreq)
);
LMIC.channelMap = pStateBuffer->channelMap;
}
void LMICeulike_setRx1Freq(void) {
#if !defined(DISABLE_MCMD_DlChannelReq)
uint32_t dlFreq = LMIC.channelDlFreq[LMIC.txChnl];
if (dlFreq != 0)
LMIC.freq = dlFreq;
#endif // !DISABLE_MCMD_DlChannelReq
}
// Class A txDone handling for FSK.
void
LMICeulike_txDoneFSK(ostime_t delay, osjobcb_t func) {
// one symbol == one bit at 50kHz == 20us.
ostime_t const hsym = us2osticksRound(10);
// start a little earlier. PRERX_FSK is in bytes; one byte at 50 kHz == 160us
delay -= LMICbandplan_PRERX_FSK * us2osticksRound(160);
// set LMIC.rxtime and LMIC.rxsyms:
LMIC.rxtime = LMIC.txend + LMICcore_adjustForDrift(delay, hsym, 8 * LMICbandplan_RXLEN_FSK);
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - os_getRadioRxRampup(), func);
}
#endif // CFG_LMIC_EU_like

29
src/lmic/lmic_eu_like.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -56,7 +56,8 @@ LMICeulike_isValidBeacon1(const uint8_t *d) {
#define LMICbandplan_isFSK() (0)
// provide a default LMICbandplan_txDoneDoFSK()
#define LMICbandplan_txDoneFSK(delay, func) do { } while (0)
void LMICeulike_txDoneFSK(ostime_t delay, osjobcb_t func);
#define LMICbandplan_txDoneFSK(delay, func) LMICeulike_txDoneFSK(delay, func)
#define LMICbandplan_joinAcceptChannelClear() LMICbandplan_initDefaultChannels(/* normal, not join */ 0)
@ -74,14 +75,14 @@ enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2, BAND_AUX = 3 };
#define LMICbandplan_setSessionInitDefaultChannels() \
do { LMICbandplan_initDefaultChannels(/* normal, not join */ 0); } while (0)
u1_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap);
bit_t LMICeulike_canMapChannels(u1_t chpage, u2_t chmap);
#define LMICbandplan_canMapChannels(c, m) LMICeulike_canMapChannels(c, m)
bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap);
#define LMICbandplan_mapChannels(c, m) LMICeulike_mapChannels(c, m)
void LMICeulike_initJoinLoop(u1_t nDefaultChannels, s1_t adrTxPow);
#define LMICbandplan_setRx1Params() \
do { /*LMIC.freq/rps remain unchanged*/ } while (0)
void LMICeulike_updateTx(ostime_t txbeg);
#define LMICbandplan_updateTx(t) LMICeulike_updateTx(t)
@ -95,4 +96,20 @@ static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) {
#define LMICbandplan_init() \
do { /* nothing */ } while (0)
void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer);
#define LMICbandplan_saveAdrState(pState) LMICeulike_saveAdrState(pState)
bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer);
#define LMICbandplan_compareAdrState(pState) LMICeulike_compareAdrState(pState)
void LMICeulike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer);
#define LMICbandplan_restoreAdrState(pState) LMICeulike_restoreAdrState(pState)
// set Rx1 frequency (might be different than uplink).
void LMICeulike_setRx1Freq(void);
bit_t LMICeulike_isDataRateFeasible(dr_t dr);
#define LMICbandplan_isDataRateFeasible(dr) LMICeulike_isDataRateFeasible(dr)
#endif // _lmic_eu_like_h_

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -49,21 +49,28 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS
};
static CONST_TABLE(u1_t, maxFrameLens)[] = { 59+5,59+5,59+5,123+5, 230+5, 230+5 };
static CONST_TABLE(u1_t, maxFrameLens)[] = {
59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 0, 250+5
};
uint8_t LMICin866_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
return 0xFF;
else
return 0;
}
static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0
30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10
};
int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1) {
return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT);
uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT;
if (pindex < LENOF_TABLE(TXPOWLEVELS)) {
return TABLE_GET_S1(TXPOWLEVELS, pindex);
} else {
return -128;
}
}
// only used in this module, but used by variant macro dr2hsym().
@ -88,8 +95,8 @@ ostime_t LMICin866_dr2hsym(uint8_t dr) {
enum { NUM_DEFAULT_CHANNELS = 3 };
static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = {
// Default operational frequencies
IN866_F1 | BAND_MILLI,
IN866_F2 | BAND_MILLI,
IN866_F1 | BAND_MILLI,
IN866_F2 | BAND_MILLI,
IN866_F3 | BAND_MILLI,
};
@ -98,6 +105,9 @@ void LMICin866_initDefaultChannels(bit_t join) {
LMIC_API_PARAMETER(join);
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
#if !defined(DISABLE_MCMD_DlChannelReq)
os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq));
#endif // !DISABLE_MCMD_DlChannelReq
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
@ -125,17 +135,32 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
}
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
// zero the band bits in freq, just in case.
freq &= ~3;
if (chidx < NUM_DEFAULT_CHANNELS) {
// can't disable a default channel.
if (freq == 0)
return 0;
// can't change a default channel.
else if (freq != (LMIC.channelFreq[chidx] & ~3))
return 0;
}
bit_t fEnable = (freq != 0);
if (chidx >= MAX_CHANNELS)
return 0;
if (band == -1) {
freq |= BAND_MILLI;
freq = (freq&~3) | BAND_MILLI;
} else {
if (band > BAND_MILLI) return 0;
freq = (freq&~3) | band;
}
LMIC.channelFreq[chidx] = freq;
LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(IN866_DR_SF12, IN866_DR_SF7) : drmap;
LMIC.channelMap |= 1 << chidx; // enabled right away
if (fEnable)
LMIC.channelMap |= 1 << chidx; // enabled right away
else
LMIC.channelMap &= ~(1 << chidx);
return 1;
}
@ -169,7 +194,7 @@ ostime_t LMICin866_nextTx(ostime_t now) {
}
}
// no enabled channel found! just use the last channel.
// no enabled channel found! just use the last channel.
return now;
}
@ -187,12 +212,27 @@ ostime_t LMICin866_nextJoinState(void) {
}
#endif // !DISABLE_JOIN
// txDone handling for FSK.
void
LMICin866_txDoneFSK(ostime_t delay, osjobcb_t func) {
LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
LMIC.rxsyms = RXLEN_FSK;
os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
// set the Rx1 dndr, rps.
void LMICin866_setRx1Params(void) {
u1_t const txdr = LMIC.dndr;
s1_t drOffset;
s1_t candidateDr;
LMICeulike_setRx1Freq();
if ( LMIC.rx1DrOffset <= 5)
drOffset = (s1_t) LMIC.rx1DrOffset;
else
drOffset = 5 - (s1_t) LMIC.rx1DrOffset;
candidateDr = (s1_t) txdr - drOffset;
if (candidateDr < LORAWAN_DR0)
candidateDr = 0;
else if (candidateDr > LORAWAN_DR5)
candidateDr = LORAWAN_DR5;
LMIC.dndr = (u1_t) candidateDr;
LMIC.rps = dndr2rps(LMIC.dndr);
}
void

274
src/lmic/lmic_kr920.c Normal file
View File

@ -0,0 +1,274 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LMIC_DR_LEGACY 0
#include "lmic_bandplan.h"
#if defined(CFG_kr920)
// ================================================================================
//
// BEG: KR920 related stuff
//
CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS,
(u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0), // [0]
(u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0), // [1]
(u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0), // [2]
(u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0), // [3]
(u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0), // [4]
(u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0), // [5]
ILLEGAL_RPS, // [6]
};
static CONST_TABLE(u1_t, maxFrameLens)[] = {
59+5, 59+5, 59+5, 123+5, 250+5, 250+5
};
uint8_t LMICkr920_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
return 0;
}
static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
14, 12, 10, 8, 6, 4, 2, 0
};
int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1) {
uint8_t const pindex = (mcmd_ladr_p1&MCMD_LinkADRReq_POW_MASK)>>MCMD_LinkADRReq_POW_SHIFT;
if (pindex < LENOF_TABLE(TXPOWLEVELS)) {
return TABLE_GET_S1(TXPOWLEVELS, pindex);
} else {
return -128;
}
}
// only used in this module, but used by variant macro dr2hsym().
static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = {
us2osticksRound(128 << 7), // DR_SF12
us2osticksRound(128 << 6), // DR_SF11
us2osticksRound(128 << 5), // DR_SF10
us2osticksRound(128 << 4), // DR_SF9
us2osticksRound(128 << 3), // DR_SF8
us2osticksRound(128 << 2), // DR_SF7
};
ostime_t LMICkr920_dr2hsym(uint8_t dr) {
return TABLE_GET_OSTIME(DR2HSYM_osticks, dr);
}
// All frequencies are marked as BAND_MILLI, and we don't do duty-cycle. But this lets
// us reuse code.
enum { NUM_DEFAULT_CHANNELS = 3 };
static CONST_TABLE(u4_t, iniChannelFreq)[NUM_DEFAULT_CHANNELS] = {
// Default operational frequencies
KR920_F1 | BAND_MILLI,
KR920_F2 | BAND_MILLI,
KR920_F3 | BAND_MILLI,
};
// korea ignores the join flag, becuase the channel setup is the same either way.
void LMICkr920_initDefaultChannels(bit_t join) {
LMIC_API_PARAMETER(join);
os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
#if !defined(DISABLE_MCMD_DlChannelReq)
os_clearMem(&LMIC.channelDlFreq, sizeof(LMIC.channelDlFreq));
#endif // !DISABLE_MCMD_DlChannelReq
os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
LMIC.channelMap = (1 << NUM_DEFAULT_CHANNELS) - 1;
for (u1_t fu = 0; fu<NUM_DEFAULT_CHANNELS; fu++) {
LMIC.channelFreq[fu] = TABLE_GET_U4(iniChannelFreq, fu);
LMIC.channelDrMap[fu] = DR_RANGE_MAP(KR920_DR_SF12, KR920_DR_SF7);
}
LMIC.bands[BAND_MILLI].txcap = 1; // no limit, in effect.
LMIC.bands[BAND_MILLI].txpow = KR920_TX_EIRP_MAX_DBM;
LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS;
LMIC.bands[BAND_MILLI].avail = os_getTime();
}
void
LMICkr920_init(void) {
// set LBT mode
LMIC.lbt_ticks = us2osticks(KR920_LBT_US);
LMIC.lbt_dbmax = KR920_LBT_DB_MAX;
}
void
LMICas923_resetDefaultChannels(void) {
// set LBT mode
LMIC.lbt_ticks = us2osticks(KR920_LBT_US);
LMIC.lbt_dbmax = KR920_LBT_DB_MAX;
}
bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
if (bandidx > BAND_MILLI) return 0;
//band_t* b = &LMIC.bands[bandidx];
xref2band_t b = &LMIC.bands[bandidx];
b->txpow = txpow;
b->txcap = txcap;
b->avail = os_getTime();
b->lastchnl = os_getRndU1() % MAX_CHANNELS;
return 1;
}
bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
// zero the band bits in freq, just in case.
freq &= ~3;
if (chidx < NUM_DEFAULT_CHANNELS) {
// can't disable a default channel.
if (freq == 0)
return 0;
// can't change a default channel.
else if (freq != (LMIC.channelFreq[chidx] & ~3))
return 0;
}
bit_t fEnable = (freq != 0);
if (chidx >= MAX_CHANNELS)
return 0;
if (band == -1) {
freq = (freq&~3) | BAND_MILLI;
} else {
if (band > BAND_MILLI) return 0;
freq = (freq&~3) | band;
}
LMIC.channelFreq[chidx] = freq;
LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(KR920_DR_SF12, KR920_DR_SF7) : drmap;
if (fEnable)
LMIC.channelMap |= 1 << chidx; // enabled right away
else
LMIC.channelMap &= ~(1 << chidx);
return 1;
}
u4_t LMICkr920_convFreq(xref2cu1_t ptr) {
u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100;
if (freq < KR920_FREQ_MIN || freq > KR920_FREQ_MAX)
freq = 0;
return freq;
}
// return the next time, but also do channel hopping here
// since there's no duty cycle limitation, and no dwell limitation,
// we simply loop through the channels sequentially.
ostime_t LMICkr920_nextTx(ostime_t now) {
const u1_t band = BAND_MILLI;
for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) {
// Find next channel in given band
u1_t chnl = LMIC.bands[band].lastchnl;
for (u1_t ci = 0; ci<MAX_CHANNELS; ci++) {
if ((chnl = (chnl + 1)) >= MAX_CHANNELS)
chnl -= MAX_CHANNELS;
if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled
(LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 &&
band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band
LMIC.txChnl = LMIC.bands[band].lastchnl = chnl;
return now;
}
}
}
// no enabled channel found! just use the last channel.
return now;
}
#if !defined(DISABLE_BEACONS)
void LMICkr920_setBcnRxParams(void) {
LMIC.dataLen = 0;
LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3;
LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN);
}
#endif // !DISABLE_BEACONS
#if !defined(DISABLE_JOIN)
ostime_t LMICkr920_nextJoinState(void) {
return LMICeulike_nextJoinState(NUM_DEFAULT_CHANNELS);
}
#endif // !DISABLE_JOIN
// set the Rx1 dndr, rps.
void LMICkr920_setRx1Params(void) {
u1_t const txdr = LMIC.dndr;
s1_t drOffset;
s1_t candidateDr;
LMICeulike_setRx1Freq();
if ( LMIC.rx1DrOffset <= 5)
drOffset = (s1_t) LMIC.rx1DrOffset;
else
drOffset = 5 - (s1_t) LMIC.rx1DrOffset;
candidateDr = (s1_t) txdr - drOffset;
if (candidateDr < LORAWAN_DR0)
candidateDr = 0;
else if (candidateDr > LORAWAN_DR5)
candidateDr = LORAWAN_DR5;
LMIC.dndr = (u1_t) candidateDr;
LMIC.rps = dndr2rps(LMIC.dndr);
}
void
LMICkr920_initJoinLoop(void) {
LMICeulike_initJoinLoop(NUM_DEFAULT_CHANNELS, /* adr dBm */ KR920_TX_EIRP_MAX_DBM);
}
void LMICkr920_updateTx(ostime_t txbeg) {
u4_t freq = LMIC.channelFreq[LMIC.txChnl];
// Update global/band specific duty cycle stats
ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
// Update channel/global duty cycle stats
xref2band_t band = &LMIC.bands[freq & 0x3];
LMIC.freq = freq & ~(u4_t)3;
LMIC.txpow = band->txpow;
if (LMIC.freq <= KR920_FDOWN && LMIC.txpow > KR920_TX_EIRP_MAX_DBM_LOW) {
LMIC.txpow = KR920_TX_EIRP_MAX_DBM_LOW;
}
band->avail = txbeg + airtime * band->txcap;
if (LMIC.globalDutyRate != 0)
LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate);
}
//
// END: KR920 related stuff
//
// ================================================================================
#endif

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -55,13 +55,26 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS // [14]
};
static CONST_TABLE(u1_t, maxFrameLens)[] = { 24,66,142,255,255,255,255,255, 66,142 };
static CONST_TABLE(u1_t, maxFrameLens)[] = {
19+5, 61+5, 133+5, 250+5, 250+5, 0, 0,0,
61+5, 133+5, 250+5, 250+5, 250+5, 250+5
};
uint8_t LMICus915_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
return 0xFF;
return 0;
}
int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1) {
if ((mcmd_ladr_p1 & MCMD_LinkADRReq_POW_MASK) >
((LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3)
? US915_LinkAdrReq_POW_MAX_1_0_2
: US915_LinkAdrReq_POW_MAX_1_0_3))
return -128;
else
return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LinkADRReq_POW_MASK)<<1)));
}
static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = {
@ -98,9 +111,11 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
return 1;
}
void LMIC_disableChannel(u1_t channel) {
bit_t LMIC_disableChannel(u1_t channel) {
bit_t result = 0;
if (channel < 72 + MAX_XCHANNELS) {
if (ENABLED_CHANNEL(channel)) {
result = 1;
if (IS_CHANNEL_125khz(channel))
LMIC.activeChannels125khz--;
else if (IS_CHANNEL_500khz(channel))
@ -108,11 +123,14 @@ void LMIC_disableChannel(u1_t channel) {
}
LMIC.channelMap[channel >> 4] &= ~(1 << (channel & 0xF));
}
return result;
}
void LMIC_enableChannel(u1_t channel) {
bit_t LMIC_enableChannel(u1_t channel) {
bit_t result = 0;
if (channel < 72 + MAX_XCHANNELS) {
if (!ENABLED_CHANNEL(channel)) {
result = 1;
if (IS_CHANNEL_125khz(channel))
LMIC.activeChannels125khz++;
else if (IS_CHANNEL_500khz(channel))
@ -120,42 +138,52 @@ void LMIC_enableChannel(u1_t channel) {
}
LMIC.channelMap[channel >> 4] |= (1 << (channel & 0xF));
}
return result;
}
void LMIC_enableSubBand(u1_t band) {
bit_t LMIC_enableSubBand(u1_t band) {
ASSERT(band < 8);
u1_t start = band * 8;
u1_t end = start + 8;
bit_t result = 0;
// enable all eight 125 kHz channels in this subband
for (int channel = start; channel < end; ++channel)
LMIC_enableChannel(channel);
result |= LMIC_enableChannel(channel);
// there's a single 500 kHz channel associated with
// each group of 8 125 kHz channels. Enable it, too.
LMIC_enableChannel(64 + band);
result |= LMIC_enableChannel(64 + band);
return result;
}
void LMIC_disableSubBand(u1_t band) {
bit_t LMIC_disableSubBand(u1_t band) {
ASSERT(band < 8);
u1_t start = band * 8;
u1_t end = start + 8;
bit_t result = 0;
// disable all eight 125 kHz channels in this subband
for (int channel = start; channel < end; ++channel)
LMIC_disableChannel(channel);
result |= LMIC_disableChannel(channel);
// there's a single 500 kHz channel associated with
// each group of 8 125 kHz channels. Disable it, too.
LMIC_disableChannel(64 + band);
result |= LMIC_disableChannel(64 + band);
return result;
}
void LMIC_selectSubBand(u1_t band) {
bit_t LMIC_selectSubBand(u1_t band) {
bit_t result = 0;
ASSERT(band < 8);
for (int b = 0; b<8; ++b) {
if (band == b)
LMIC_enableSubBand(b);
result |= LMIC_enableSubBand(b);
else
LMIC_disableSubBand(b);
result |= LMIC_disableSubBand(b);
}
return result;
}
void LMICus915_updateTx(ostime_t txbeg) {
@ -193,16 +221,31 @@ void LMICus915_setBcnRxParams(void) {
}
#endif // !DISABLE_BEACONS
// TODO(tmm@mcci.com): parmeterize for US-like
// set the Rx1 dndr, rps.
void LMICus915_setRx1Params(void) {
u1_t const txdr = LMIC.dndr;
u1_t candidateDr;
LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP;
if( /* TX datarate */LMIC.dndr < US915_DR_SF8C )
LMIC.dndr += US915_DR_SF10CR - US915_DR_SF10;
else if( LMIC.dndr == US915_DR_SF8C )
LMIC.dndr = US915_DR_SF7CR;
if ( /* TX datarate */txdr < LORAWAN_DR4)
candidateDr = txdr + 10 - LMIC.rx1DrOffset;
else
candidateDr = LORAWAN_DR13 - LMIC.rx1DrOffset;
if (candidateDr < LORAWAN_DR8)
candidateDr = LORAWAN_DR8;
else if (candidateDr > LORAWAN_DR13)
candidateDr = LORAWAN_DR13;
LMIC.dndr = candidateDr;
LMIC.rps = dndr2rps(LMIC.dndr);
}
void LMICus915_initJoinLoop(void) {
LMICuslike_initJoinLoop();
// initialize the adrTxPower.
LMIC.adrTxPow = 20; // dBm
}
//
// END: US915 related stuff

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -91,31 +91,54 @@ void LMICuslike_initDefaultChannels(bit_t fJoin) {
LMIC.activeChannels500khz = 8;
}
u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
// verify that a given setting is permitted
bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap) {
/*
|| MCMD_LADR_CHP_125ON and MCMD_LADR_CHP_125OFF are special. The
|| MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF are special. The
|| channel map appllies to 500kHz (ch 64..71) and in addition
|| all channels 0..63 are turned off or on. MCMC_LADR_CHP_BANK
|| is also special, in that it enables subbands.
*/
u1_t base, top;
if (chpage < MCMD_LADR_CHP_USLIKE_SPECIAL) {
// operate on channels 0..15, 16..31, 32..47, 48..63
base = chpage << 4;
top = base + 16;
if (base == 64) {
if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) {
// operate on channels 0..15, 16..31, 32..47, 48..63, 64..71
if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K) {
if (chmap & 0xFF00) {
// those are reserved bits, fail.
return 0;
}
top = 72;
} else {
return 1;
}
} else if (chpage == MCMD_LADR_CHP_BANK) {
if (chmap & 0xFF00) {
// those are resreved bits, fail.
} else if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK) {
if (chmap == 0 || (chmap & 0xFF00) != 0) {
// no bits set, or reserved bitsset , fail.
return 0;
}
} else if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON ||
chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) {
u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON;
// if disabling all 125kHz chans, must have at least one 500kHz chan
// don't allow reserved bits to be set in chmap.
if ((! en125 && chmap == 0) || (chmap & 0xFF00) != 0)
return 0;
} else {
return 0;
}
// if we get here, it looks legal.
return 1;
}
// map channels. return true if configuration looks valid.
bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
/*
|| MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF are special. The
|| channel map appllies to 500kHz (ch 64..71) and in addition
|| all channels 0..63 are turned off or on. MCMC_LADR_CHP_BANK
|| is also special, in that it enables subbands.
*/
if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK) {
// each bit enables a bank of channels
for (u1_t subband = 0; subband < 8; ++subband, chmap >>= 1) {
if (chmap & 1) {
@ -123,37 +146,48 @@ u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
} else {
LMIC_disableSubBand(subband);
}
// don't change any channels below
base = top = 0;
}
} else if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) {
u1_t const en125 = chpage == MCMD_LADR_CHP_125ON;
} else {
u1_t base, top;
// enable or disable all 125kHz channels
for (u1_t chnl = 0; chnl < 64; ++chnl) {
if (en125)
LMIC_enableChannel(chnl);
else
LMIC_disableChannel(chnl);
}
if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) {
// operate on channels 0..15, 16..31, 32..47, 48..63
// note that the chpage hasn't been shifted right, so
// it's really the base.
base = chpage;
top = base + 16;
if (base == 64) {
top = 72;
}
} else /* if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON ||
chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) */ {
u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON;
// then apply mask to top 8 channels.
base = 64;
top = 72;
} else {
return 0;
}
// enable or disable all 125kHz channels
for (u1_t chnl = 0; chnl < 64; ++chnl) {
if (en125)
LMIC_enableChannel(chnl);
else
LMIC_disableChannel(chnl);
}
// apply chmap to channels in [base..top-1].
// Use enable/disable channel to keep activeChannel counts in sync.
for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) {
if (chmap & 0x0001)
LMIC_enableChannel(chnl);
else
LMIC_disableChannel(chnl);
// then apply mask to top 8 channels.
base = 64;
top = 72;
}
// apply chmap to channels in [base..top-1].
// Use enable/disable channel to keep activeChannel counts in sync.
for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) {
if (chmap & 0x0001)
LMIC_enableChannel(chnl);
else
LMIC_disableChannel(chnl);
}
}
return 1;
LMICOS_logEventUint32("LMICuslike_mapChannels", ((u4_t)LMIC.activeChannels125khz << 16u)|(LMIC.activeChannels500khz << 0u));
return (LMIC.activeChannels125khz > 0) || (LMIC.activeChannels500khz > 0);
}
// US does not have duty cycling - return now as earliest TX time
@ -161,16 +195,38 @@ u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
ostime_t LMICuslike_nextTx(ostime_t now) {
// TODO(tmm@mcci.com): use a static const for US-like
if (LMIC.datarate >= LMICuslike_getFirst500kHzDR()) { // 500kHz
ASSERT(LMIC.activeChannels500khz>0);
setNextChannel(64, 64 + 8, LMIC.activeChannels500khz);
if (LMIC.activeChannels500khz > 0) {
setNextChannel(64, 64 + 8, LMIC.activeChannels500khz);
} else if (LMIC.activeChannels125khz > 0) {
LMIC.datarate = lowerDR(LMICuslike_getFirst500kHzDR(), 1);
setNextChannel(0, 64, LMIC.activeChannels125khz);
LMICOS_logEvent("LMICuslike_nextTx: no 500k, choose 125k");
} else {
LMICOS_logEvent("LMICuslike_nextTx: no channels at all (500)");
}
}
else { // 125kHz
ASSERT(LMIC.activeChannels125khz>0);
setNextChannel(0, 64, LMIC.activeChannels125khz);
if (LMIC.activeChannels125khz > 0) {
setNextChannel(0, 64, LMIC.activeChannels125khz);
} else if (LMIC.activeChannels500khz > 0) {
LMIC.datarate = LMICuslike_getFirst500kHzDR();
setNextChannel(64, 64 + 8, LMIC.activeChannels500khz);
LMICOS_logEvent("LMICuslike_nextTx: no 125k, choose 500k");
} else {
LMICOS_logEvent("LMICuslike_nextTx: no channels at all (125)");
}
}
return now;
}
bit_t LMICuslike_isDataRateFeasible(dr_t dr) {
if (dr >= LMICuslike_getFirst500kHzDR()) { // 500kHz
return LMIC.activeChannels500khz > 0;
} else {
return LMIC.activeChannels125khz > 6;
}
}
#if !defined(DISABLE_JOIN)
void LMICuslike_initJoinLoop(void) {
// set an initial condition so that setNextChannel()'s preconds are met
@ -183,18 +239,13 @@ void LMICuslike_initJoinLoop(void) {
// starting point.
setNextChannel(0, 64, LMIC.activeChannels125khz);
// initialize the adrTxPower.
// TODO(tmm@mcci.com): is this right for all US-like regions
LMIC.adrTxPow = 20; // dBm
ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0);
// make sure LMIC.txend is valid.
LMIC.txend = os_getTime();
ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0);
// make sure the datarate is set to DR0 per LoRaWAN regional reqts V1.0.2,
// section 2.2.2
// TODO(tmm@mcci.com): parameterize this for US-like
LMICcore_setDrJoin(DRCHG_SET, LORAWAN_DR0);
// make sure the datarate is set to DR2 per LoRaWAN regional reqts V1.0.2,
// section 2.*.2
LMICcore_setDrJoin(DRCHG_SET, LMICbandplan_getInitialDrJoin());
// TODO(tmm@mcci.com) need to implement the transmit randomization and
// duty cycle restrictions from LoRaWAN V1.0.2 section 7.
@ -233,7 +284,7 @@ ostime_t LMICuslike_nextJoinState(void) {
setNextChannel(0, 64, LMIC.activeChannels125khz);
// TODO(tmm@mcci.com) parameterize
s1_t dr = LORAWAN_DR0;
s1_t dr = LMICuslike_getJoin125kHzDR();
if ((++LMIC.txCnt & 0x7) == 0) {
failed = 1; // All DR exhausted - signal failed
}
@ -260,4 +311,29 @@ ostime_t LMICuslike_nextJoinState(void) {
}
#endif
void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) {
os_copyMem(
pStateBuffer->channelMap,
LMIC.channelMap,
sizeof(LMIC.channelMap)
);
pStateBuffer->activeChannels125khz = LMIC.activeChannels125khz;
pStateBuffer->activeChannels500khz = LMIC.activeChannels500khz;
}
void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) {
os_copyMem(
LMIC.channelMap,
pStateBuffer->channelMap,
sizeof(LMIC.channelMap)
);
LMIC.activeChannels125khz = pStateBuffer->activeChannels125khz;
LMIC.activeChannels500khz = pStateBuffer->activeChannels500khz;
}
bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) {
return memcmp(pStateBuffer->channelMap, LMIC.channelMap, sizeof(LMIC.channelMap)) != 0;
}
#endif // CFG_LMIC_US_like

31
src/lmic/lmic_us_like.h Executable file → Normal file
View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2017 MCCI Corporation.
* Copyright (c) 2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -43,6 +43,9 @@
#define IS_CHANNEL_500khz(c) (c>=64 && c<72)
#define ENABLED_CHANNEL(chnl) ((LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0x0F))) != 0)
// library functions: called from bandplan
void LMICuslike_initJoinLoop(void);
// provide the isValidBeacon1 function -- int for bool.
static inline int
LMICuslike_isValidBeacon1(const uint8_t *d) {
@ -66,7 +69,7 @@ LMICuslike_isValidBeacon1(const uint8_t *d) {
#define LMICbandplan_advanceBeaconChannel() \
do { LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; } while (0)
// TODO(tmm@mcci.com): decide whether we want to do this on every
// TODO(tmm@mcci.com): decide whether we want to do this on every
// reset or just restore the last sub-band selected by the user.
#define LMICbandplan_resetDefaultChannels() \
LMICbandplan_initDefaultChannels(/* normal */ 0)
@ -77,24 +80,36 @@ void LMICuslike_initDefaultChannels(bit_t fJoin);
#define LMICbandplan_setSessionInitDefaultChannels() \
do { /* nothing */} while (0)
u1_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap);
bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap);
#define LMICbandplan_canMapChannels(chpage, chmap) LMICuslike_canMapChannels(chpage, chmap)
bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap);
#define LMICbandplan_mapChannels(chpage, chmap) LMICuslike_mapChannels(chpage, chmap)
ostime_t LMICuslike_nextTx(ostime_t now);
#define LMICbandplan_nextTx(now) LMICuslike_nextTx(now)
void LMICuslike_initJoinLoop(void);
#define LMICbandplan_initJoinLoop() LMICuslike_initJoinLoop()
ostime_t LMICuslike_nextJoinState(void);
#define LMICbandplan_nextJoinState() LMICuslike_nextJoinState();
static inline ostime_t LMICeulike_nextJoinTime(ostime_t now) {
static inline ostime_t LMICuslike_nextJoinTime(ostime_t now) {
return now;
}
#define LMICbandplan_nextJoinTime(now) LMICeulike_nextJoinTime(now)
#define LMICbandplan_nextJoinTime(now) LMICuslike_nextJoinTime(now)
#define LMICbandplan_init() \
do { /* nothing */ } while (0)
void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer);
#define LMICbandplan_saveAdrState(pState) LMICuslike_saveAdrState(pState)
bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer);
#define LMICbandplan_compareAdrState(pState) LMICuslike_compareAdrState(pState)
void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer);
#define LMICbandplan_restoreAdrState(pState) LMICuslike_restoreAdrState(pState)
bit_t LMICuslike_isDataRateFeasible(dr_t dr);
#define LMICbandplan_isDataRateFeasible(dr) LMICuslike_isDataRateFeasible(dr)
#endif // _lmic_us_like_h_

0
src/lmic/lmic_util.c Executable file → Normal file
View File

2
src/lmic/lmic_util.h Executable file → Normal file
View File

@ -9,7 +9,7 @@ Copyright & License:
See accompanying LICENSE file.
Author:
Terry Moore, MCCI September 2019
Terry Moore, MCCI September 2018
*/

View File

@ -44,7 +44,15 @@ typedef u1_t cr_t;
typedef u1_t sf_t;
typedef u1_t bw_t;
typedef u1_t dr_t;
typedef u2_t rxsyms_t;
// Radio parameter set (encodes SF/BW/CR/IH/NOCRC)
// 2..0: Spreading factor
// 4..3: bandwidth: 0 == 125kHz, 1 == 250 kHz, 2 == 500 kHz. 3 == reserved.
// 6..5: coding rate: 0 == 4/5, 1 == 4/6, 2 == 4/7, 3 == 4/8
// 7: nocrc: 0 == with crc, 1 == without crc
// 15..8: Implicit header control: 0 ==> none, 1..0xFF ==> length in bytes.
typedef u2_t rps_t;
TYPEDEF_xref2rps_t;
@ -52,7 +60,7 @@ enum { ILLEGAL_RPS = 0xFF };
// Global maximum frame length
enum { STD_PREAMBLE_LEN = 8 };
enum { MAX_LEN_FRAME = 64 };
enum { MAX_LEN_FRAME = LMIC_ENABLE_long_messages ? 255 : 64 };
enum { LEN_DEVNONCE = 2 };
enum { LEN_ARTNONCE = 3 };
enum { LEN_NETID = 3 };
@ -97,6 +105,8 @@ enum _dr_code_t {
// post conditions from this block: symbols used by general code that is not
// ostensiblly region-specific.
// DR_DFLTMIN must be defined as a suitable substititute value if we get a bogus DR
// It is misnamed, it should be the maximum DR (which is the minimum SF) for
// 125 kHz.
// DR_PAGE is used only for a non-supported debug system, but should be defined.
// CHNL_DNW2 is the channel to be used for RX2
// FREQ_DNW2 is the frequency to be used for RX2
@ -217,28 +227,28 @@ enum _dr_configured_t {
};
# endif // LMIC_DR_LEGACY
#elif defined(CFG_au921) // =========================================
#elif defined(CFG_au915) // =========================================
#include "lorabase_au921.h"
#include "lorabase_au915.h"
// per 2.5.3: not implemented
#define LMIC_ENABLE_TxParamSetupReq 0
// per 2.5.3: must be implemented
#define LMIC_ENABLE_TxParamSetupReq 1
enum { DR_DFLTMIN = AU921_DR_SF7 }; // DR5
enum { DR_DFLTMIN = AU915_DR_SF7 }; // DR5
// DR_PAGE is a debugging parameter; it must be defined but it has no use in arduino-lmic
enum { DR_PAGE = DR_PAGE_AU921 };
enum { DR_PAGE = DR_PAGE_AU915 };
//enum { CHNL_PING = 0 }; // used only for default init of state (follows beacon - rotating)
enum { FREQ_PING = AU921_500kHz_DNFBASE + 0*AU921_500kHz_DNFSTEP }; // default ping freq
enum { DR_PING = AU921_DR_SF10CR }; // default ping DR
enum { FREQ_PING = AU915_500kHz_DNFBASE + 0*AU915_500kHz_DNFSTEP }; // default ping freq
enum { DR_PING = AU915_DR_SF10CR }; // default ping DR
//enum { CHNL_DNW2 = 0 };
enum { FREQ_DNW2 = AU921_500kHz_DNFBASE + 0*AU921_500kHz_DNFSTEP };
enum { DR_DNW2 = AU921_DR_SF12CR }; // DR8
enum { FREQ_DNW2 = AU915_500kHz_DNFBASE + 0*AU915_500kHz_DNFSTEP };
enum { DR_DNW2 = AU915_DR_SF12CR }; // DR8
enum { CHNL_BCN = 0 }; // used only for default init of state (rotating beacon scheme)
enum { DR_BCN = AU921_DR_SF10CR };
enum { DR_BCN = AU915_DR_SF10CR };
enum { AIRTIME_BCN = 72192 }; // micros ... TODO(tmm@mcci.com) check.
enum { LMIC_REGION_EIRP = AU921_LMIC_REGION_EIRP }; // region uses EIRP
enum { LMIC_REGION_EIRP = AU915_LMIC_REGION_EIRP }; // region uses EIRP
enum {
// Beacon frame format AU DR10/SF10 500kHz
@ -255,20 +265,20 @@ enum {
# if LMIC_DR_LEGACY
enum _dr_configured_t {
DR_SF12 = AU921_DR_SF12,
DR_SF11 = AU921_DR_SF11,
DR_SF10 = AU921_DR_SF10,
DR_SF9 = AU921_DR_SF9,
DR_SF8 = AU921_DR_SF8,
DR_SF7 = AU921_DR_SF7,
DR_SF8C = AU921_DR_SF8C,
DR_NONE = AU921_DR_NONE,
DR_SF12CR = AU921_DR_SF12CR,
DR_SF11CR = AU921_DR_SF11CR,
DR_SF10CR = AU921_DR_SF10CR,
DR_SF9CR = AU921_DR_SF9CR,
DR_SF8CR = AU921_DR_SF8CR,
DR_SF7CR = AU921_DR_SF7CR
DR_SF12 = AU915_DR_SF12,
DR_SF11 = AU915_DR_SF11,
DR_SF10 = AU915_DR_SF10,
DR_SF9 = AU915_DR_SF9,
DR_SF8 = AU915_DR_SF8,
DR_SF7 = AU915_DR_SF7,
DR_SF8C = AU915_DR_SF8C,
DR_NONE = AU915_DR_NONE,
DR_SF12CR = AU915_DR_SF12CR,
DR_SF11CR = AU915_DR_SF11CR,
DR_SF10CR = AU915_DR_SF10CR,
DR_SF9CR = AU915_DR_SF9CR,
DR_SF8CR = AU915_DR_SF8CR,
DR_SF7CR = AU915_DR_SF7CR
};
# endif // LMIC_DR_LEGACY
@ -319,6 +329,51 @@ enum _dr_configured_t {
};
# endif // LMIC_DR_LEGACY
#elif defined(CFG_kr920) // ==============================================
#include "lorabase_kr920.h"
// per 2.8.3 (1.0.3 2.9.3): is not implemented
#define LMIC_ENABLE_TxParamSetupReq 0
enum { DR_DFLTMIN = KR920_DR_SF12 }; // DR2
// DR_PAGE is a debugging parameter
enum { DR_PAGE = DR_PAGE_KR920 };
enum { FREQ_PING = KR920_FBCN }; // default ping freq
enum { DR_PING = KR920_DR_SF9 }; // default ping DR: DR3
enum { FREQ_DNW2 = KR920_FDOWN };
enum { DR_DNW2 = KR920_DR_SF12 };
enum { CHNL_BCN = 11 };
enum { FREQ_BCN = KR920_FBCN };
enum { DR_BCN = KR920_DR_SF9 };
enum { AIRTIME_BCN = 144384 }; // micros
enum { LMIC_REGION_EIRP = KR920_LMIC_REGION_EIRP }; // region uses EIRP
enum {
// Beacon frame format KR SF9
OFF_BCN_NETID = 0,
OFF_BCN_TIME = 2,
OFF_BCN_CRC1 = 6,
OFF_BCN_INFO = 8,
OFF_BCN_LAT = 9,
OFF_BCN_LON = 12,
OFF_BCN_CRC2 = 15,
LEN_BCN = 17
};
# if LMIC_DR_LEGACY
enum _dr_configured_t {
DR_SF12 = KR920_DR_SF12,
DR_SF11 = KR920_DR_SF11,
DR_SF10 = KR920_DR_SF10,
DR_SF9 = KR920_DR_SF9,
DR_SF8 = KR920_DR_SF8,
DR_SF7 = KR920_DR_SF7,
DR_NONE = KR920_DR_NONE
};
# endif // LMIC_DR_LEGACY
#elif defined(CFG_in866) // ==============================================
#include "lorabase_in866.h"
@ -414,7 +469,6 @@ enum {
HDR_FTYPE_DADN = 0x60, // data (unconfirmed) dn
HDR_FTYPE_DCUP = 0x80, // data confirmed up
HDR_FTYPE_DCDN = 0xA0, // data confirmed dn
HDR_FTYPE_REJOIN = 0xC0, // rejoin for roaming
HDR_FTYPE_PROP = 0xE0
};
enum {
@ -422,16 +476,21 @@ enum {
};
enum {
// Bitfields in frame control octet
FCT_ADREN = 0x80,
FCT_ADRARQ = 0x40,
FCT_ACK = 0x20,
FCT_MORE = 0x10, // also in DN direction: Class B indicator
FCT_OPTLEN = 0x0F,
FCT_ADREN = 0x80,
FCT_ADRACKReq = 0x40,
FCT_ACK = 0x20,
FCT_MORE = 0x10, // also in DN direction: Class B indicator
FCT_OPTLEN = 0x0F,
};
enum {
// In UP direction: signals class B enabled
FCT_CLASSB = FCT_MORE
};
enum {
LWAN_FCtrl_FOptsLen_MAX = 0x0Fu, // maximum size of embedded MAC commands
};
enum {
NWKID_MASK = (int)0xFE000000,
NWKID_BITS = 7
@ -440,63 +499,78 @@ enum {
// MAC uplink commands downwlink too
enum {
// Class A
MCMD_LCHK_REQ = 0x02, // - LinkCheckReq : -
MCMD_LADR_ANS = 0x03, // - LinkADRAnd : u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK
MCMD_DCAP_ANS = 0x04, // - DutyCycleAns : -
MCMD_DN2P_ANS = 0x05, // - RxParamSetupAns : u1:7-2:RFU 1/0:datarate/channel ack
MCMD_DEVS_ANS = 0x06, // - DevStatusAns : u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31)
MCMD_SNCH_ANS = 0x07, // - NewChannelAns : u1: 7-2=RFU, 1/0:DR/freq ACK
MCMD_RXTimingSetupAns = 0x08, // : -
MCMD_TxParamSetupAns = 0x09, // : -
MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK
MCMD_LinkCheckReq = 0x02, // -
MCMD_LinkADRAns = 0x03, // u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK
MCMD_DutyCycleAns = 0x04, // -
MCMD_RXParamSetupAns = 0x05, // u1:7-2:RFU 1/0:datarate/channel ack
MCMD_DevStatusAns = 0x06, // u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31)
MCMD_NewChannelAns = 0x07, // u1: 7-2=RFU, 1/0:DR/freq ACK
MCMD_RXTimingSetupAns = 0x08, // -
MCMD_TxParamSetupAns = 0x09, // -
MCMD_DlChannelAns = 0x0A, // u1: [7-2]:RFU 1:exists 0:OK
MCMD_DeviceTimeReq = 0x0D, // -
// Class B
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate
MCMD_PING_ANS = 0x11, // - ack ping freq : u1: 7-1:RFU, 0:freq ok
MCMD_BCNI_REQ = 0x12, // - next beacon start : -
MCMD_PingSlotInfoReq = 0x10, // u1: 7=RFU, 6-4:interval, 3-0:datarate
MCMD_PingSlotChannelAns = 0x11, // u1: 7-1:RFU, 0:freq ok
MCMD_BeaconInfoReq = 0x12, // - (DEPRECATED)
MCMD_BeaconFreqAns = 0x13, // u1: 7-1:RFU, 0:freq ok
};
// MAC downlink commands
enum {
// Class A
MCMD_LCHK_ANS = 0x02, // LinkCheckAns : u1:margin 0-254,255=unknown margin / u1:gwcnt LinkCheckReq
MCMD_LADR_REQ = 0x03, // LinkADRReq : u1:DR/TXPow, u2:chmask, u1:chpage/repeat
MCMD_DCAP_REQ = 0x04, // DutyCycleReq : u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k
MCMD_DN2P_SET = 0x05, // RXParamSetupReq : u1:7-4:RFU/3-0:datarate, u3:freq
MCMD_DEVS_REQ = 0x06, // DevStatusReq : -
MCMD_SNCH_REQ = 0x07, // NewChannelReq : u1:chidx, u3:freq, u1:DRrange
MCMD_RXTimingSetupReq = 0x08, // : u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1)
MCMD_TxParamSetupReq = 0x09, // : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP
MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency
MCMD_LinkCheckAns = 0x02, // u1:margin 0-254,255=unknown margin / u1:gwcnt LinkCheckReq
MCMD_LinkADRReq = 0x03, // u1:DR/TXPow, u2:chmask, u1:chpage/repeat
MCMD_DutyCycleReq = 0x04, // u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k
MCMD_RXParamSetupReq = 0x05, // u1:7-4:RFU/3-0:datarate, u3:freq
MCMD_DevStatusReq = 0x06, // -
MCMD_NewChannelReq = 0x07, // u1:chidx, u3:freq, u1:DRrange
MCMD_RXTimingSetupReq = 0x08, // u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1)
MCMD_TxParamSetupReq = 0x09, // u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP
MCMD_DlChannelReq = 0x0A, // u1: channel, u3: frequency
MCMD_DeviceTimeAns = 0x0D, // u4: seconds since epoch, u1: fractional second
// Class B
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel
MCMD_PingSlotInfoAns = 0x10, // -
MCMD_PingSlotChannelReq = 0x11, // u3: freq, u1:dr [7-4]:RFU [3:0]:datarate
MCMD_BeaconTimingAns = 0x12, // u2: delay(in TUNIT millis), u1:channel (DEPRECATED)
MCMD_BeaconFreqReq = 0x13, // u3: freq
};
enum {
MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis
MCMD_BeaconTimingAns_TUNIT = 30 // time unit of delay value in millis
};
enum {
MCMD_LADR_ANS_RFU = 0xF8, // RFU bits
MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level
MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_LADR_ANS_CHACK = 0x01, // 0=unknown channel enabled
MCMD_LinkADRAns_RFU = 0xF8, // RFU bits
MCMD_LinkADRAns_PowerACK = 0x04, // 0=not supported power level
MCMD_LinkADRAns_DataRateACK = 0x02, // 0=unknown data rate
MCMD_LinkADRAns_ChannelACK = 0x01, // 0=unknown channel enabled
};
enum {
MCMD_DN2P_ANS_RFU = 0xF8, // RFU bits
MCMD_DN2P_ANS_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed
MCMD_DN2P_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_DN2P_ANS_CHACK = 0x01, // 0=unknown channel enabled
MCMD_RXParamSetupAns_RFU = 0xF8, // RFU bits
MCMD_RXParamSetupAns_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed
MCMD_RXParamSetupAns_RX2DataRateACK = 0x02, // 0=unknown data rate
MCMD_RXParamSetupAns_ChannelACK = 0x01, // 0=unknown channel enabled
};
enum {
MCMD_SNCH_ANS_RFU = 0xFC, // RFU bits
MCMD_SNCH_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_SNCH_ANS_FQACK = 0x01, // 0=rejected channel frequency
MCMD_NewChannelAns_RFU = 0xFC, // RFU bits
MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate
MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency
};
enum {
MCMD_PING_ANS_RFU = 0xFE,
MCMD_PING_ANS_FQACK = 0x01
MCMD_RXTimingSetupReq_RFU = 0xF0, // RFU bits
MCMD_RXTimingSetupReq_Delay = 0x0F, // delay in secs, 1..15; 0 is mapped to 1.
};
enum {
MCMD_DlChannelAns_RFU = 0xFC, // RFU bits
MCMD_DlChannelAns_FreqACK = 0x02, // 0 = uplink frequency not defined for this channel
MCMD_DlChannelAns_ChannelACK = 0x01, // 0 = rejected channel freq
};
enum {
MCMD_PingSlotFreqAns_RFU = 0xFC,
MCMD_PingSlotFreqAns_DataRateACK = 0x02,
MCMD_PingSlotFreqAns_ChannelACK = 0x01,
};
enum {
@ -506,65 +580,30 @@ enum {
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
};
// Bit fields byte#3 of MCMD_LADR_REQ payload
// Bit fields byte#3 of MCMD_LinkADRReq payload
enum {
MCMD_LADR_CHP_USLIKE_SPECIAL = 0x50, // first special for us-like
MCMD_LADR_CHP_BANK = 0x50, // special: bits are banks.
MCMD_LADR_CHP_125ON = 0x60, // special channel page enable, bits applied to 64..71
MCMD_LADR_CHP_125OFF = 0x70, // special channel page: disble 125K, bits apply to 64..71
MCMD_LADR_N3RFU_MASK = 0x80,
MCMD_LADR_CHPAGE_MASK = 0xF0,
MCMD_LADR_REPEAT_MASK = 0x0F,
MCMD_LADR_REPEAT_1 = 0x01,
MCMD_LADR_CHPAGE_1 = 0x10
MCMD_LinkADRReq_Redundancy_RFU = 0x80,
MCMD_LinkADRReq_Redundancy_ChMaskCntl_MASK= 0x70,
MCMD_LinkADRReq_Redundancy_NbTrans_MASK = 0x0F,
MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT = 0x00, // direct masking for EU
MCMD_LinkADRReq_ChMaskCntl_EULIKE_ALL_ON = 0x60, // EU: enable everything.
MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K = 0x40, // mask is for the 8 us-like 500 kHz channels
MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL = 0x50, // first special for us-like
MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK = 0x50, // special: bits are banks.
MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON = 0x60, // special channel page enable, bits applied to 64..71
MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF = 0x70, // special channel page: disble 125K, bits apply to 64..71
MCMD_LinkADRReq_ChMaskCntl_CN470_ALL_ON = 0x60, // turn all on for China.
};
// Bit fields byte#0 of MCMD_LADR_REQ payload
// Bit fields byte#0 of MCMD_LinkADRReq payload
enum {
MCMD_LADR_DR_MASK = 0xF0,
MCMD_LADR_POW_MASK = 0x0F,
MCMD_LADR_DR_SHIFT = 4,
MCMD_LADR_POW_SHIFT = 0,
#if defined(CFG_eu868) // TODO(tmm@mcci.com): complete refactor.
EU868_MCMD_LADR_SF12 = EU868_DR_SF12<<4,
EU868_MCMD_LADR_SF11 = EU868_DR_SF11<<4,
EU868_MCMD_LADR_SF10 = EU868_DR_SF10<<4,
EU868_MCMD_LADR_SF9 = EU868_DR_SF9 <<4,
EU868_MCMD_LADR_SF8 = EU868_DR_SF8 <<4,
EU868_MCMD_LADR_SF7 = EU868_DR_SF7 <<4,
EU868_MCMD_LADR_SF7B = EU868_DR_SF7B<<4,
EU868_MCMD_LADR_FSK = EU868_DR_FSK <<4,
EU868_MCMD_LADR_20dBm = 0,
EU868_MCMD_LADR_14dBm = 1,
EU868_MCMD_LADR_11dBm = 2,
EU868_MCMD_LADR_8dBm = 3,
EU868_MCMD_LADR_5dBm = 4,
EU868_MCMD_LADR_2dBm = 5,
#elif defined(CFG_us915)
US915_MCMD_LADR_SF10 = US915_DR_SF10<<4,
US915_MCMD_LADR_SF9 = US915_DR_SF9 <<4,
US915_MCMD_LADR_SF8 = US915_DR_SF8 <<4,
US915_MCMD_LADR_SF7 = US915_DR_SF7 <<4,
US915_MCMD_LADR_SF8C = US915_DR_SF8C<<4,
US915_MCMD_LADR_SF12CR = US915_DR_SF12CR<<4,
US915_MCMD_LADR_SF11CR = US915_DR_SF11CR<<4,
US915_MCMD_LADR_SF10CR = US915_DR_SF10CR<<4,
US915_MCMD_LADR_SF9CR = US915_DR_SF9CR<<4,
US915_MCMD_LADR_SF8CR = US915_DR_SF8CR<<4,
US915_MCMD_LADR_SF7CR = US915_DR_SF7CR<<4,
US915_MCMD_LADR_30dBm = 0,
US915_MCMD_LADR_28dBm = 1,
US915_MCMD_LADR_26dBm = 2,
US915_MCMD_LADR_24dBm = 3,
US915_MCMD_LADR_22dBm = 4,
US915_MCMD_LADR_20dBm = 5,
US915_MCMD_LADR_18dBm = 6,
US915_MCMD_LADR_16dBm = 7,
US915_MCMD_LADR_14dBm = 8,
US915_MCMD_LADR_12dBm = 9,
US915_MCMD_LADR_10dBm = 10
#endif
MCMD_LinkADRReq_DR_MASK = 0xF0,
MCMD_LinkADRReq_POW_MASK = 0x0F,
MCMD_LinkADRReq_DR_SHIFT = 4,
MCMD_LinkADRReq_POW_SHIFT = 0,
};
// bit fields of the TxParam request

9
src/lmic/lorabase_as923.h Executable file → Normal file
View File

@ -93,4 +93,13 @@ enum { AS923_V102_TX_CAP = 100 }; // v1.0.2 allows 100%
# define AS923_TX_CAP AS923_V102_TX_CAP
#endif
// TxParam defaults
enum {
// initial value of UplinkDwellTime before TxParamSetupReq received.
AS923_INITIAL_TxParam_UplinkDwellTime = 1,
// initial value of DownlinkDwellTime before TxParamSetupReq received.
AS923_INITIAL_TxParam_DownlinkDwellTime = 1,
AS923_UPLINK_DWELL_TIME_osticks = sec2osticks(20),
};
#endif /* _lorabase_as923_h_ */

69
src/lmic/lorabase_au921.h → src/lmic/lorabase_au915.h Executable file → Normal file
View File

@ -28,8 +28,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lorabase_au921_h_
#define _lorabase_au921_h_
#ifndef _lorabase_au915_h_
#define _lorabase_au915_h_
#ifndef _LMIC_CONFIG_PRECONDITIONS_H_
# include "lmic_config_preconditions.h"
@ -37,48 +37,53 @@
/****************************************************************************\
|
| Basic definitions for AS921 (always in scope)
| Basic definitions for AU 915 (always in scope)
|
\****************************************************************************/
// Frequency plan for AU 921 MHz
enum _dr_as921_t {
AU921_DR_SF12 = 0,
AU921_DR_SF11,
AU921_DR_SF10,
AU921_DR_SF9,
AU921_DR_SF8,
AU921_DR_SF7,
AU921_DR_SF8C,
AU921_DR_NONE,
// Frequency plan for AU 915 MHz
enum _dr_au915_t {
AU915_DR_SF12 = 0,
AU915_DR_SF11,
AU915_DR_SF10,
AU915_DR_SF9,
AU915_DR_SF8,
AU915_DR_SF7,
AU915_DR_SF8C,
AU915_DR_NONE,
// Devices behind a router:
AU921_DR_SF12CR = 8,
AU921_DR_SF11CR,
AU921_DR_SF10CR,
AU921_DR_SF9CR,
AU921_DR_SF8CR,
AU921_DR_SF7CR
AU915_DR_SF12CR = 8,
AU915_DR_SF11CR,
AU915_DR_SF10CR,
AU915_DR_SF9CR,
AU915_DR_SF8CR,
AU915_DR_SF7CR
};
// Default frequency plan for AU 921MHz
// Default frequency plan for AU 915MHz
enum {
AU921_125kHz_UPFBASE = 915200000,
AU921_125kHz_UPFSTEP = 200000,
AU921_500kHz_UPFBASE = 915900000,
AU921_500kHz_UPFSTEP = 1600000,
AU921_500kHz_DNFBASE = 923300000,
AU921_500kHz_DNFSTEP = 600000
AU915_125kHz_UPFBASE = 915200000,
AU915_125kHz_UPFSTEP = 200000,
AU915_500kHz_UPFBASE = 915900000,
AU915_500kHz_UPFSTEP = 1600000,
AU915_500kHz_DNFBASE = 923300000,
AU915_500kHz_DNFSTEP = 600000
};
enum {
AU921_FREQ_MIN = 915000000,
AU921_FREQ_MAX = 928000000
AU915_FREQ_MIN = 915000000,
AU915_FREQ_MAX = 928000000
};
enum {
AU921_TX_EIRP_MAX_DBM = 30 // 30 dBm
AU915_TX_EIRP_MAX_DBM = 30 // 30 dBm
};
enum {
// initial value of UplinkDwellTime before TxParamSetupReq received.
AU915_INITIAL_TxParam_UplinkDwellTime = 1,
AU915_UPLINK_DWELL_TIME_osticks = sec2osticks(20),
};
enum { DR_PAGE_AU921 = 0x10 * (LMIC_REGION_au921 - 1) };
enum { DR_PAGE_AU915 = 0x10 * (LMIC_REGION_au915 - 1) };
enum { AU921_LMIC_REGION_EIRP = 1 }; // region uses EIRP
enum { AU915_LMIC_REGION_EIRP = 1 }; // region uses EIRP
#endif /* _lorabase_au921_h_ */
#endif /* _lorabase_au915_h_ */

0
src/lmic/lorabase_eu868.h Executable file → Normal file
View File

0
src/lmic/lorabase_in866.h Executable file → Normal file
View File

84
src/lmic/lorabase_kr920.h Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* All rights reserved.
*
* Copyright (c) 2017, 2019 MCCI Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lorabase_kr920_h_
#define _lorabase_kr920_h_
#ifndef _LMIC_CONFIG_PRECONDITIONS_H_
# include "lmic_config_preconditions.h"
#endif
/****************************************************************************\
|
| Basic definitions for KR920 (always in scope)
|
\****************************************************************************/
enum _dr_kr920_t {
KR920_DR_SF12 = 0, // DR0
KR920_DR_SF11, // DR1
KR920_DR_SF10, // DR2
KR920_DR_SF9, // DR3
KR920_DR_SF8, // DR4
KR920_DR_SF7, // DR5
KR920_DR_NONE
};
// There is no dwell-time or duty-cycle limitation for IN
//
// max power: 30dBM
//
// freq datarates
enum {
KR920_F1 = 922100000, // SF7-12 (DR0-5)
KR920_F2 = 922300000, // SF7-12 (DR0-5)
KR920_F3 = 922500000, // SF7-12 (DR0-5)
KR920_FBCN = 923100000, // beacon/ping
KR920_F14DBM = 922100000, // Allows 14 dBm (not 10) if >= this.
KR920_FDOWN = 921900000, // RX2 downlink frequency
};
enum {
KR920_FREQ_MIN = 920900000,
KR920_FREQ_MAX = 923300000
};
enum {
KR920_TX_EIRP_MAX_DBM = 14, // 14 dBm for most
KR920_TX_EIRP_MAX_DBM_LOW = 10, // 10 dBm for some
};
enum { DR_PAGE_KR920 = 0x10 * (LMIC_REGION_kr920 - 1) };
enum { KR920_LMIC_REGION_EIRP = 1 }; // region uses EIRP
enum { KR920_LBT_US = 128 }; // microseconds of LBT time.
enum { KR920_LBT_DB_MAX = -80 }; // maximum channel strength in dB; if TX
// we measure more than this, we don't tx.
#endif /* _lorabase_in866_h_ */

8
src/lmic/lorabase_us915.h Executable file → Normal file
View File

@ -73,10 +73,16 @@ enum {
US915_FREQ_MAX = 928000000
};
enum {
US915_TX_MAX_DBM = 30 // 30 dBm (but not EIRP): assumes we're
US915_TX_MAX_DBM = 30 // 30 dBm (but not EIRP): assumes we're
// on an 64-channel bandplan. See code
// that computes tx power.
};
enum {
US915_LinkAdrReq_POW_MAX_1_0_2 = 0xA,
US915_LinkAdrReq_POW_MAX_1_0_3 = 0xE,
};
enum { DR_PAGE_US915 = 0x10 * (LMIC_REGION_us915 - 1) };
enum { US915_LMIC_REGION_EIRP = 0 }; // region doesn't use EIRP, uses tx power

View File

@ -0,0 +1,64 @@
/*
Module: lorawan_spec_compliance.h
Function:
Details from the LoRaWAN specification for compliance.
Copyright notice and license info:
See LICENSE file accompanying this project.
Author:
Terry Moore, MCCI Corporation March 2019
*/
#ifndef _lorawan_spec_COMPLIANCE_H_ /* prevent multiple includes */
#define _lorawan_spec_COMPLIANCE_H_
#ifdef __cplusplus
extern "C"{
#endif
enum {
// the port for MAC commands
LORAWAN_PORT_MAC = 0u,
// the first port available for applications
LORAWAN_PORT_USER_MIN = 1u,
// the last port available for applications
LORAWAN_PORT_USER_MAX = 223u,
// the base of the reserved port numbers
LORAWAN_PORT_RESERVED = 224u,
// the port for the compliance protocol
LORAWAN_PORT_COMPLIANCE = LORAWAN_PORT_RESERVED + 0u,
};
enum lowawan_compliance_cmd_e {
LORAWAN_COMPLIANCE_CMD_DEACTIVATE = 0u,
LORAWAN_COMPLIANCE_CMD_ACTIVATE = 1u,
LORAWAN_COMPLIANCE_CMD_SET_CONFIRM = 2u,
LORAWAN_COMPLIANCE_CMD_SET_UNCONFIRM = 3u,
LORAWAN_COMPLIANCE_CMD_ECHO = 4u,
LORAWAN_COMPLIANCE_CMD_LINK = 5u,
LORAWAN_COMPLIANCE_CMD_JOIN = 6u,
LORAWAN_COMPLIANCE_CMD_CW = 7u,
LORAWAN_COMPLIANCE_CMD_MFG_BASE = 0x80u,
};
typedef unsigned char lorawan_compliance_cmd_t;
// info on specific commands
enum {
LORAWAN_COMPLIANCE_CMD_ACTIVATE_LEN = 4u,
LORAWAN_COMPLIANCE_CMD_ACTIVATE_MAGIC = 1u,
// Maximum crypto frame size; although the spec says 18, it
// is also used for testing max packet size.
LORAWAN_COMPLIANCE_CMD_ECHO_LEN_MAX = 242u,
};
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* _lorawan_spec_COMPLIANCE_H_ */

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2016-2017 MCCI Corporation.
* Copyright (c) 2016-2017, 2019 MCCI Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -68,13 +68,15 @@ static int unlinkjob (osjob_t** pnext, osjob_t* job) {
return 0;
}
static osjob_t** getJobQueue(osjob_t* job) {
return os_jobIsTimed(job) ? &OS.scheduledjobs : &OS.runnablejobs;
}
// clear scheduled job
void os_clearCallback (osjob_t* job) {
hal_disableIRQs();
// if it's not in the scheduled jobs, look in the runnable...
if (! unlinkjob(&OS.scheduledjobs, job))
unlinkjob(&OS.runnablejobs, job);
unlinkjob(getJobQueue(job), job);
hal_enableIRQs();
}
@ -83,11 +85,15 @@ void os_clearCallback (osjob_t* job) {
void os_setCallback (osjob_t* job, osjobcb_t cb) {
osjob_t** pnext;
hal_disableIRQs();
// remove if job was already queued
unlinkjob(&OS.runnablejobs, job);
// fill-in job
job->func = cb;
unlinkjob(getJobQueue(job), job);
// fill-in job. Ascending memory order is write-queue friendly
job->next = NULL;
job->deadline = 0;
job->func = cb;
// add to end of run queue
for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next));
*pnext = job;
@ -97,13 +103,21 @@ void os_setCallback (osjob_t* job, osjobcb_t cb) {
// schedule timed job
void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) {
osjob_t** pnext;
// special case time 0 -- it will be one tick late.
if (time == 0)
time = 1;
hal_disableIRQs();
// remove if job was already queued
unlinkjob(&OS.scheduledjobs, job);
unlinkjob(getJobQueue(job), job);
// fill-in job
job->next = NULL;
job->deadline = time;
job->func = cb;
job->next = NULL;
// insert into schedule
for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
@ -125,6 +139,8 @@ void os_runloop () {
void os_runloop_once() {
osjob_t* j = NULL;
hal_processPendingIRQs();
hal_disableIRQs();
// check for runnable jobs
if(OS.runnablejobs) {
@ -141,3 +157,13 @@ void os_runloop_once() {
j->func(j);
}
}
// return true if there are any jobs scheduled within time ticks from now.
// return false if any jobs scheduled are at least time ticks in the future.
bit_t os_queryTimeCriticalJobs(ostime_t time) {
if (OS.scheduledjobs &&
OS.scheduledjobs->deadline - os_getTime() < time)
return 1;
else
return 0;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2018, 2019 MCCI Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -29,29 +30,22 @@
#ifndef _oslmic_h_
#define _oslmic_h_
// Dependencies required for the LoRa MAC in C to run.
// Dependencies required for the LMIC to run.
// These settings can be adapted to the underlying system.
// You should not, however, change the lmic.[hc]
// You should not, however, change the lmic merely for porting purposes.[hc]
#include "config.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C"{
#ifndef _lmic_env_h_
# include "lmic_env.h"
#endif
//================================================================================
//================================================================================
// Target platform as C library
typedef uint8_t bit_t;
typedef uint8_t u1_t;
typedef int8_t s1_t;
typedef uint16_t u2_t;
typedef int16_t s2_t;
typedef uint32_t u4_t;
typedef int32_t s4_t;
typedef unsigned int uint;
typedef const char* str_t;
#ifndef _oslmic_types_h_
# include "oslmic_types.h"
#endif
LMIC_BEGIN_DECLS
#include <string.h>
#include "hal.h"
@ -73,7 +67,6 @@ typedef struct rxsched_t rxsched_t;
typedef struct bcninfo_t bcninfo_t;
typedef const u1_t* xref2cu1_t;
typedef u1_t* xref2u1_t;
typedef s4_t ostime_t;
// int32_t == s4_t is long on some platforms; and someday
// we will want 64-bit ostime_t. So, we will use a macro for the
@ -91,107 +84,7 @@ typedef s4_t ostime_t;
#define SIZEOFEXPR(x) sizeof(x)
//----------------------------------------------------------------------------
// Annotations to avoid various "unused" warnings. These must appear as a
// statement in the function body; the macro annotates the variable to quiet
// compiler warnings. The way this is done is compiler-specific, and so these
// definitions are fall-backs, which might be overridden.
//
// Although these are all similar, we don't want extra macro expansions,
// so we define each one explicitly rather than relying on a common macro.
//----------------------------------------------------------------------------
// signal that a parameter is intentionally unused.
#ifndef LMIC_UNREFERENCED_PARAMETER
# define LMIC_UNREFERENCED_PARAMETER(v) do { (void) (v); } while (0)
#endif
// an API parameter is a parameter that is required by an API definition, but
// happens to be unreferenced in this implementation. This is a stronger
// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here
// becuase of an API contract, but we have no use for it in this function.
#ifndef LMIC_API_PARAMETER
# define LMIC_API_PARAMETER(v) do { (void) (v); } while (0)
#endif
// an intentionally-unreferenced variable.
#ifndef LMIC_UNREFERENCED_VARIABLE
# define LMIC_UNREFERENCED_VARIABLE(v) do { (void) (v); } while (0)
#endif
// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1,
// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or
// or varables that are only refereneced at the target debug level.
// Parameter referenced only if debugging at level > 0.
#ifndef LMIC_DEBUG1_PARAMETER
# if LMIC_DEBUG_LEVEL > 0
# define LMIC_DEBUG1_PARAMETER(v) do { ; } while (0)
# else
# define LMIC_DEBUG1_PARAMETER(v) do { (void) (v); } while (0)
# endif
#endif
// variable referenced only if debugging at level > 0
#ifndef LMIC_DEBUG1_VARIABLE
# if LMIC_DEBUG_LEVEL > 0
# define LMIC_DEBUG1_VARIABLE(v) do { ; } while (0)
# else
# define LMIC_DEBUG1_VARIABLE(v) do { (void) (v); } while (0)
# endif
#endif
// parameter referenced only if debugging at level > 1
#ifndef LMIC_DEBUG2_PARAMETER
# if LMIC_DEBUG_LEVEL > 1
# define LMIC_DEBUG2_PARAMETER(v) do { ; } while (0)
# else
# define LMIC_DEBUG2_PARAMETER(v) do { (void) (v); } while (0)
# endif
#endif
// variable referenced only if debugging at level > 1
#ifndef LMIC_DEBUG2_VARIABLE
# if LMIC_DEBUG_LEVEL > 1
# define LMIC_DEBUG2_VARIABLE(v) do { ; } while (0)
# else
# define LMIC_DEBUG2_VARIABLE(v) do { (void) (v); } while (0)
# endif
#endif
// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0
#ifndef LMIC_X_DEBUG_PARAMETER
# if LMIC_X_DEBUG_LEVEL > 0
# define LMIC_X_DEBUG_PARAMETER(v) do { ; } while (0)
# else
# define LMIC_X_DEBUG_PARAMETER(v) do { (void) (v); } while (0)
# endif
#endif
// variable referenced only if LMIC_X_DEBUG_LEVEL > 0
#ifndef LMIC_X_DEBUG_VARIABLE
# if LMIC_X_DEBUG_LEVEL > 0
# define LMIC_X_DEBUG_VARIABLE(v) do { ; } while (0)
# else
# define LMIC_X_DEBUG_VARIABLE(v) do { (void) (v); } while (0)
# endif
#endif
// parameter referenced only if EV() macro is enabled (which it never is)
// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and
// this code is really C-99 to its bones.
#ifndef LMIC_EV_PARAMETER
# define LMIC_EV_PARAMETER(v) do { (void) (v); } while (0)
#endif
// variable referenced only if EV() macro is defined.
#ifndef LMIC_EV_VARIABLE
# define LMIC_EV_VARIABLE(v) do { (void) (v); } while (0)
#endif
#define ON_LMIC_EVENT(ev) onEvent(ev)
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
#define DECL_ON_LMIC_EVENT LMIC_DECLARE_FUNCTION_WEAK(void, onEvent, (ev_t e))
extern u4_t AESAUX[];
extern u4_t AESKEY[];
@ -226,12 +119,21 @@ void radio_monitor_rssi(ostime_t n, oslmic_radio_rssi_t *pRssi);
//================================================================================
#ifndef RX_RAMPUP
#define RX_RAMPUP (us2osticks(2000))
#ifndef RX_RAMPUP_DEFAULT
//! \brief RX_RAMPUP_DEFAULT specifies the extra time we must allow to set up an RX event due
//! to platform issues. It's specified in units of ostime_t. It must reflect
//! platform jitter and latency, as well as the speed of the LMIC when running
//! on this plaform. It's not used directly; clients call os_getRadioRxRampup(),
//! which might adaptively vary this based on observed timeouts.
#define RX_RAMPUP_DEFAULT (us2osticks(10000))
#endif
#ifndef TX_RAMPUP
#define TX_RAMPUP (us2osticks(2000))
// TX_RAMPUP specifies the extra time we must allow to set up a TX event) due
// to platform issues. It's specified in units of ostime_t. It must reflect
// platform jitter and latency, as well as the speed of the LMIC when running
// on this plaform.
#define TX_RAMPUP (us2osticks(10000))
#endif
#ifndef OSTICKS_PER_SEC
@ -255,7 +157,13 @@ void radio_monitor_rssi(ostime_t n, oslmic_radio_rssi_t *pRssi);
struct osjob_t; // fwd decl.
typedef void (*osjobcb_t) (struct osjob_t*);
//! the function type for osjob_t callbacks
typedef void (osjobcbfn_t)(struct osjob_t*);
//! the pointer-to-function for osjob_t callbacks
typedef osjobcbfn_t *osjobcb_t;
struct osjob_t {
struct osjob_t* next;
ostime_t deadline;
@ -263,6 +171,11 @@ struct osjob_t {
};
TYPEDEF_xref2osjob_t;
//! determine whether a job is timed or immediate. os_setTimedCallback()
// must treat incoming == 0 as being 1 instead.
static inline int os_jobIsTimed(xref2osjob_t job) {
return (job->deadline != 0);
}
#ifndef HAS_os_calls
@ -284,6 +197,9 @@ void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb);
#ifndef os_clearCallback
void os_clearCallback (xref2osjob_t job);
#endif
#ifndef os_getRadioRxRampup
ostime_t os_getRadioRxRampup (void);
#endif
#ifndef os_getTime
ostime_t os_getTime (void);
#endif
@ -296,6 +212,10 @@ void os_radio (u1_t mode);
#ifndef os_getBattLevel
u1_t os_getBattLevel (void);
#endif
#ifndef os_queryTimeCriticalJobs
//! Return non-zero if any jobs are scheduled between now and now+time.
bit_t os_queryTimeCriticalJobs(ostime_t time);
#endif
#ifndef os_rlsbf4
//! Read 32-bit quantity from given pointer in little endian byte order.
@ -416,8 +336,18 @@ extern xref2u1_t AESaux;
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len);
#endif
#ifdef __cplusplus
} // extern "C"
#endif
// ======================================================================
// Simple logging support. Vanishes unless enabled.
#if LMIC_ENABLE_event_logging
extern void LMICOS_logEvent(const char *pMessage);
extern void LMICOS_logEventUint32(const char *pMessage, uint32_t datum);
#else // ! LMIC_ENABLE_event_logging
# define LMICOS_logEvent(m) do { ; } while (0)
# define LMICOS_logEventUint32(m, d) do { ; } while (0)
#endif // ! LMIC_ENABLE_event_logging
LMIC_END_DECLS
#endif // _oslmic_h_

47
src/lmic/oslmic_types.h Normal file
View File

@ -0,0 +1,47 @@
/*
Module: oslmic_types.h
Function:
Basic types from oslmic.h, shared by all layers.
Copyright & License:
See accompanying LICENSE file.
Author:
Terry Moore, MCCI November 2018
(based on oslmic.h from IBM).
*/
#ifndef _oslmic_types_h_
# define _oslmic_types_h_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
//================================================================================
//================================================================================
// Target platform as C library
typedef uint8_t bit_t;
typedef uint8_t u1_t;
typedef int8_t s1_t;
typedef uint16_t u2_t;
typedef int16_t s2_t;
typedef uint32_t u4_t;
typedef int32_t s4_t;
typedef unsigned int uint;
typedef const char* str_t;
// the HAL needs to give us ticks, so it ought to know the right type.
typedef s4_t ostime_t;
#ifdef __cplusplus
}
#endif
/* end of oslmic_types.h */
#endif /* _oslmic_types_h_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +0,0 @@
/*******************************************************************************
*
* 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
*
* Task listening on a UART port for provisioning commands.
*******************************************************************************/
#ifndef _provision_task_h_
#define _provision_task_h_
#include "lmic/oslmic.h"
#ifdef __cplusplus
extern "C" {
#endif
void provisioning_start_task();
bool provisioning_have_keys();
bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const char *app_key);
bool provisioning_from_mac(const char *app_eui, const char *app_key);
bool provisioning_save_keys();
bool provisioning_restore_keys(bool silent);
#ifdef __cplusplus
}
#endif
#endif