1 Commits

75 changed files with 1492 additions and 5692 deletions

View File

@ -12,7 +12,3 @@ set(COMPONENT_REQUIRES
)
register_component()
if (IDF_VER STRGREATER_EQUAL "v4.0")
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-expansion-to-defined)
endif()

44
Kconfig
View File

@ -14,10 +14,10 @@ config TTN_LORA_FREQ_EU_868
bool "Europe (868 MHz)"
config TTN_LORA_FREQ_US_915
bool "North and South America (915 MHz)"
bool "North America (915 MHz)"
config TTN_LORA_FREQ_AU_915
bool "Australia (915 MHz)"
config TTN_LORA_FREQ_AU_921
bool "Australia (921 MHz)"
config TTN_LORA_FREQ_AS_923
bool "Asia (923 MHz)"
@ -25,9 +25,6 @@ config TTN_LORA_FREQ_AS_923
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)"
@ -49,27 +46,32 @@ 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

View File

@ -17,17 +17,12 @@
#elif defined(CONFIG_TTN_LORA_FREQ_US_915)
#define CFG_us915 1
#elif defined(CONFIG_TTN_LORA_FREQ_AU_921)
# 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
#define CFG_au921 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
@ -43,6 +38,26 @@
#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'
#endif
#if !defined(CONFIG_TTN_PROVISION_UART_NONE)
#define TTN_HAS_AT_COMMANDS 1
#if defined(CONFIG_TTN_PROVISION_UART_CONFIG_YES)
@ -63,8 +78,6 @@
#include <stdio.h>
#endif
#define LMIC_ENABLE_onEvent 0
#define DISABLE_PING
#define DISABLE_BEACONS

View File

@ -1,8 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
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,4 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@ -7,12 +7,11 @@
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* Sample program showing how to send and receive messages.
* Sample program showing how to send a test message every 30 second.
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "esp_event.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "TheThingsNetwork.h"
@ -37,9 +36,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 14
#define TTN_PIN_RST TTN_NOT_CONNECTED
#define TTN_PIN_DIO0 26
#define TTN_PIN_DIO1 35
#define TTN_PIN_DIO1 33
static TheThingsNetwork ttn;
@ -54,18 +53,10 @@ void sendMessages(void* pvParameter)
TTNResponseCode res = ttn.transmitMessage(msgData, sizeof(msgData) - 1);
printf(res == kTTNSuccessfulTransmission ? "Message sent.\n" : "Transmission failed.\n");
vTaskDelay(TX_INTERVAL * pdMS_TO_TICKS(1000));
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
}
}
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;
@ -94,9 +85,6 @@ 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())
{

View File

@ -1,8 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
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,4 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@ -11,7 +11,6 @@
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "esp_event.h"
#include "nvs_flash.h"
@ -51,7 +50,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 * pdMS_TO_TICKS(1000));
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
}
}

View File

@ -1,8 +0,0 @@
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

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

View File

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

View File

@ -1,142 +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
*
* 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

@ -1,8 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
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,4 +1,4 @@
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES ttn-esp32)
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@ -11,7 +11,6 @@
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "esp_event.h"
#include "nvs_flash.h"
@ -46,7 +45,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 * pdMS_TO_TICKS(1000));
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
}
}

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(send_recv)

View File

@ -1,4 +1,4 @@
PROJECT_NAME := shutdown
PROJECT_NAME := send_recv
EXTRA_COMPONENT_DIRS := $(abspath ../..)

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@ -12,7 +12,6 @@
#include "freertos/FreeRTOS.h"
#include "esp_event.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "TheThingsNetwork.h"
@ -39,56 +38,22 @@ const char *appKey = "????????????????????????????????";
#define TTN_PIN_RXTX TTN_NOT_CONNECTED
#define TTN_PIN_RST 14
#define TTN_PIN_DIO0 26
#define TTN_PIN_DIO1 35
#define TTN_PIN_DIO1 33
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) {
// 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;
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
}
}
@ -130,8 +95,14 @@ extern "C" void app_main(void)
ttn.onMessage(messageReceived);
if (join())
printf("Joining...\n");
if (ttn.join())
{
printf("Joined.\n");
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, nullptr);
}
else
{
printf("Join failed. Goodbye\n");
}
}

View File

@ -1,8 +0,0 @@
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

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

View File

@ -35,115 +35,6 @@ enum TTNResponseCode
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
*
@ -177,8 +68,7 @@ public:
/**
* @brief Reset the LoRaWAN radio.
*
* To restart communication, `join()` must be called.
* Neither clears the provisioned keys nor the configured pins.
* Does not clear provisioned keys.
*/
void reset();
@ -186,8 +76,8 @@ public:
* @brief Configures the pins used to communicate with the LoRaWAN radio chip.
*
*
* Before calling this member function, the SPI bus needs to be configured using `spi_bus_initialize()`.
* Additionally, `gpio_install_isr_service()` must have been called to initialize the GPIO ISR handler service.
* 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.
*
* @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)
@ -309,7 +199,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'. The callback is called
* Messages are received as a result of 'transmitMessage' or 'poll'. The callback is called
* in the task that called any of these functions and it occurs before these functions
* return control to the caller.
*
@ -326,89 +216,6 @@ 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;

View File

@ -11,14 +11,15 @@
"repository": {
"type": "git",
"url": "https://github.com/manuelbl/ttn-esp32.git",
"branch": "master"
"branch": "dev"
},
"version": "3.2.0",
"version": "2.3.2",
"license": "MIT License",
"export": {
"include": [
"include",
"src"
"src",
"esp_idf_lmic_config.h"
]
},
"frameworks": "espidf",

View File

@ -1,390 +0,0 @@
/*******************************************************************************
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2019 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* 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

View File

@ -1,55 +0,0 @@
/*******************************************************************************
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2019 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* 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-2019 Manuel Bleichenbacher
* Copyright (c) 2018 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
@ -90,14 +90,13 @@ void TTNProvisioning::startTask()
esp_err_t err = uart_driver_install(UART_NUM, 2048, 2048, 20, &uart_queue, 0);
ESP_ERROR_CHECK(err);
xTaskCreate(ttn_provisioning_task_caller, "ttn_provision", 2048, this, 1, nullptr);
xTaskCreate(ttn_provisioning_task_caller, "provisioning", 2048, this, 1, nullptr);
}
void ttn_provisioning_task_caller(void* pvParameter)
{
TTNProvisioning* provisioning = (TTNProvisioning*)pvParameter;
provisioning->provisioningTask();
vTaskDelete(nullptr);
}
void TTNProvisioning::provisioningTask()
@ -133,6 +132,7 @@ void TTNProvisioning::provisioningTask()
free(line_buf);
uart_driver_delete(UART_NUM);
vTaskDelete(nullptr);
}
void TTNProvisioning::addLineData(int numBytes)
@ -286,7 +286,7 @@ void TTNProvisioning::processLine()
ttn_hal.enterCriticalSection();
LMIC_reset();
ttn_hal.leaveCriticalSection();
LMIC.client.eventCb(LMIC.client.eventUserData, EV_RESET);
onEvent(EV_RESET);
}
uart_write_bytes(UART_NUM, is_ok ? "OK\r\n" : "ERROR\r\n", is_ok ? 4 : 7);

View File

@ -2,7 +2,7 @@
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2019 Manuel Bleichenbacher
* Copyright (c) 2018 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT

View File

@ -2,7 +2,7 @@
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2019 Manuel Bleichenbacher
* Copyright (c) 2018 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
@ -13,64 +13,25 @@
#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 "TheThingsNetwork.h"
#include "TTNProvisioning.h"
#include "TTNLogging.h"
/**
* @brief Reason the user code is waiting
*/
enum TTNWaitingReason
enum ClientAction
{
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;
eActionUnrelated,
eActionJoining,
eActionSending
};
static const char *TAG = "ttn";
static TheThingsNetwork* ttnInstance;
static QueueHandle_t lmicEventQueue = nullptr;
static TTNWaitingReason waitingReason = eWaitingNone;
static QueueHandle_t resultQueue;
static ClientAction clientAction = eActionUnrelated;
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()
@ -79,6 +40,7 @@ TheThingsNetwork::TheThingsNetwork()
#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 == nullptr);
@ -93,46 +55,25 @@ TheThingsNetwork::~TheThingsNetwork()
void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1)
{
ttn_hal.configurePins(spi_host, nss, rxtx, rst, dio0, dio1);
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;
#if LMIC_ENABLE_event_logging
logging = TTNLogging::initInstance();
#endif
LMIC_registerEventCb(eventCallback, nullptr);
LMIC_registerRxMessageCb(messageReceivedCallback, nullptr);
os_init_ex(nullptr);
os_init();
reset();
lmicEventQueue = xQueueCreate(4, sizeof(TTNLmicEvent));
ASSERT(lmicEventQueue != nullptr);
ttn_hal.startLMICTask();
resultQueue = xQueueCreate(12, sizeof(int));
ASSERT(resultQueue != nullptr);
ttn_hal.startBackgroundTask();
}
void TheThingsNetwork::reset()
{
ttn_hal.enterCriticalSection();
LMIC_reset();
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();
}
@ -174,7 +115,7 @@ void TheThingsNetwork::waitForProvisioning()
}
while (!provisioning.haveKeys())
vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelay(1000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Device successfully provisioned");
#else
@ -212,55 +153,51 @@ bool TheThingsNetwork::joinCore()
}
ttn_hal.enterCriticalSection();
xQueueReset(lmicEventQueue);
waitingReason = eWaitingForJoin;
clientAction = eActionJoining;
LMIC_startJoining();
ttn_hal.wakeUp();
ttn_hal.leaveCriticalSection();
TTNLmicEvent event;
xQueueReceive(lmicEventQueue, &event, portMAX_DELAY);
return event.event == eEvtJoinCompleted;
int result = 0;
xQueueReceive(resultQueue, &result, portMAX_DELAY);
return result == EV_JOINED;
}
TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t length, port_t port, bool confirm)
{
ttn_hal.enterCriticalSection();
if (waitingReason != eWaitingNone || (LMIC.opmode & OP_TXRXPEND) != 0)
if (LMIC.opmode & OP_TXRXPEND)
{
ttn_hal.leaveCriticalSection();
return kTTNErrorTransmissionFailed;
}
waitingReason = eWaitingForTransmission;
LMIC.client.txMessageCb = messageTransmittedCallback;
LMIC.client.txMessageUserData = nullptr;
clientAction = eActionSending;
LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm);
ttn_hal.wakeUp();
ttn_hal.leaveCriticalSection();
while (true)
{
TTNLmicEvent result;
xQueueReceive(lmicEventQueue, &result, portMAX_DELAY);
int result = 0;
xQueueReceive(resultQueue, &result, portMAX_DELAY);
switch (result.event)
if (result == EV_TXCOMPLETE)
{
case eEvtMessageReceived:
if (messageCallback != nullptr)
messageCallback(result.message, result.messageSize, result.port);
break;
bool hasRecevied = (LMIC.txrxFlags & (TXRX_DNW1 | TXRX_DNW2)) != 0;
if (hasRecevied && messageCallback != nullptr)
{
port_t port = 0;
if ((LMIC.txrxFlags & TXRX_PORT))
port = LMIC.frame[LMIC.dataBeg - 1];
const uint8_t* msg = nullptr;
if (LMIC.dataLen > 0)
msg = LMIC.frame + LMIC.dataBeg;
messageCallback(msg, LMIC.dataLen, port);
}
case eEvtTransmissionCompleted:
return kTTNSuccessfulTransmission;
}
case eEvtTransmissionFailed:
return kTTNErrorTransmissionFailed;
default:
ASSERT(0);
}
}
}
void TheThingsNetwork::onMessage(TTNMessageCallback callback)
@ -279,149 +216,47 @@ bool TheThingsNetwork::isProvisioned()
return provisioning.haveKeys();
}
void TheThingsNetwork::setRSSICal(int8_t rssiCal)
{
ttn_hal.rssiCal = rssiCal;
}
bool TheThingsNetwork::adrEnabled()
{
return LMIC.adrEnabled != 0;
}
// --- LMIC functions ---
void TheThingsNetwork::setAdrEnabled(bool enabled)
{
LMIC_setAdrMode(enabled);
}
TTNRFSettings TheThingsNetwork::getRFSettings(TTNRxTxWindow window)
{
int index = static_cast<int>(window) & 0x03;
return lastRfSettings[index];
}
TTNRFSettings TheThingsNetwork::txSettings()
{
return lastRfSettings[static_cast<int>(kTTNTxWindow)];
}
TTNRFSettings TheThingsNetwork::rx1Settings()
{
return lastRfSettings[static_cast<int>(kTTNRx1Window)];
}
TTNRFSettings TheThingsNetwork::rx2Settings()
{
return lastRfSettings[static_cast<int>(kTTNRx2Window)];
}
TTNRxTxWindow TheThingsNetwork::rxTxWindow()
{
return currentWindow;
}
int TheThingsNetwork::rssi()
{
return LMIC.rssi;
}
// --- Callbacks ---
#if CONFIG_LOG_DEFAULT_LEVEL >= 3 || LMIC_ENABLE_event_logging
const char *eventNames[] = { LMIC_EVENT_NAME_TABLE__INIT };
#if CONFIG_LOG_DEFAULT_LEVEL >= 3
static const char *eventNames[] = {
nullptr,
"EV_SCAN_TIMEOUT", "EV_BEACON_FOUND",
"EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING",
"EV_JOINED", "EV_RFU1", "EV_JOIN_FAILED", "EV_REJOIN_FAILED",
"EV_TXCOMPLETE", "EV_LOST_TSYNC", "EV_RESET",
"EV_RXCOMPLETE", "EV_LINK_DEAD", "EV_LINK_ALIVE", "EV_SCAN_FOUND",
"EV_TXSTART"
};
#endif
void onEvent (ev_t ev) {
#if CONFIG_LOG_DEFAULT_LEVEL >= 3
ESP_LOGI(TAG, "event %s", eventNames[ev]);
#endif
// 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;
if (ev == EV_TXCOMPLETE) {
if (LMIC.txrxFlags & TXRX_ACK)
ESP_LOGI(TAG, "ACK received\n");
}
case EV_RXSTART:
if (currentWindow != kTTNRx1Window)
if (clientAction == eActionUnrelated)
{
currentWindow = kTTNRx1Window;
saveRFSettings(lastRfSettings[static_cast<int>(kTTNRx1Window)]);
return;
}
else if (clientAction == eActionJoining)
{
if (ev != EV_JOINED && ev != EV_REJOIN_FAILED && ev != EV_RESET)
return;
}
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 (ttnEvent == eEvtNone)
if (ev != EV_TXCOMPLETE && ev != EV_LINK_DEAD && ev != EV_RESET)
return;
TTNLmicEvent result(ttnEvent);
waitingReason = eWaitingNone;
xQueueSend(lmicEventQueue, &result, pdMS_TO_TICKS(100));
}
// Called by LMIC when a message has been received
void messageReceivedCallback(void *userData, uint8_t port, const uint8_t *message, size_t nMessage)
{
TTNLmicEvent result(eEvtMessageReceived);
result.port = port;
result.message = message;
result.messageSize = nMessage;
xQueueSend(lmicEventQueue, &result, pdMS_TO_TICKS(100));
}
// Called by LMIC when a message has been transmitted (or the transmission failed)
void messageTransmittedCallback(void *userData, int success)
{
waitingReason = eWaitingNone;
TTNLmicEvent result(success ? eEvtTransmissionCompleted : eEvtTransmissionFailed);
xQueueSend(lmicEventQueue, &result, pdMS_TO_TICKS(100));
}
// --- Helpers
void saveRFSettings(TTNRFSettings& rfSettings)
{
rfSettings.spreadingFactor = static_cast<TTNSpreadingFactor>(getSf(LMIC.rps) + 1);
rfSettings.bandwidth = static_cast<TTNBandwidth>(getBw(LMIC.rps) + 1);
rfSettings.frequency = LMIC.freq;
}
void clearRFSettings(TTNRFSettings& rfSettings)
{
memset(&rfSettings, 0, sizeof(rfSettings));
int result = ev;
clientAction = eActionUnrelated;
xQueueSend(resultQueue, &result, 100 / portTICK_PERIOD_MS);
}

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

0
src/aes/other.c Normal file → Executable file
View File

View File

@ -2,12 +2,12 @@
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2019 Manuel Bleichenbacher
* 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.
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-iDF.
*******************************************************************************/
#include "../lmic/lmic.h"
@ -22,130 +22,112 @@
#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";
lmic_pinmap lmic_pins;
HAL_ESP32 ttn_hal;
TaskHandle_t HAL_ESP32::lmicTask = nullptr;
uint32_t HAL_ESP32::dioInterruptTime = 0;
uint8_t HAL_ESP32::dioNum = 0;
struct HALQueueItem {
ostime_t time;
HAL_Event ev;
HALQueueItem() : time(0), ev(DIO0) { }
HALQueueItem(HAL_Event e, ostime_t t = 0)
: time(t), ev(e) { }
};
// -----------------------------------------------------------------------------
// Constructor
HAL_ESP32::HAL_ESP32()
: rssiCal(10), nextAlarm(0)
: nextTimerEvent(0x200000000)
{
}
// -----------------------------------------------------------------------------
// 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;
uint64_t now;
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
BaseType_t higherPrioTaskWoken = pdFALSE;
xTaskNotifyFromISR(lmicTask, NOTIFY_BIT_DIO, eSetBits, &higherPrioTaskWoken);
HALQueueItem item { (HAL_Event)(long)arg, (ostime_t)now };
xQueueSendFromISR(ttn_hal.dioQueue, &item, &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);
// 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(pinNSS);
gpio_set_level(pinNSS, 0);
gpio_set_direction(pinNSS, GPIO_MODE_OUTPUT);
gpio_pad_select_gpio(lmic_pins.nss);
gpio_set_level((gpio_num_t)lmic_pins.nss, 0);
gpio_set_direction((gpio_num_t)lmic_pins.nss, GPIO_MODE_OUTPUT);
if (pinRxTx != LMIC_UNUSED_PIN)
if (lmic_pins.rxtx != LMIC_UNUSED_PIN)
{
gpio_pad_select_gpio(pinRxTx);
gpio_set_level(pinRxTx, 0);
gpio_set_direction(pinRxTx, GPIO_MODE_OUTPUT);
gpio_pad_select_gpio(lmic_pins.rxtx);
gpio_set_level((gpio_num_t)lmic_pins.rxtx, 0);
gpio_set_direction((gpio_num_t)lmic_pins.rxtx, GPIO_MODE_OUTPUT);
}
if (pinRst != LMIC_UNUSED_PIN)
if (lmic_pins.rst != LMIC_UNUSED_PIN)
{
gpio_pad_select_gpio(pinRst);
gpio_set_level(pinRst, 0);
gpio_set_direction(pinRst, GPIO_MODE_OUTPUT);
gpio_pad_select_gpio((gpio_num_t)lmic_pins.rst);
gpio_set_level((gpio_num_t)lmic_pins.rst, 0);
gpio_set_direction((gpio_num_t)lmic_pins.rst, 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);
dioQueue = xQueueCreate(12, sizeof(HALQueueItem));
ASSERT(dioQueue != NULL);
gpio_pad_select_gpio(pinDIO1);
gpio_set_direction(pinDIO1, GPIO_MODE_INPUT);
gpio_set_intr_type(pinDIO1, GPIO_INTR_POSEDGE);
gpio_pad_select_gpio(lmic_pins.dio0);
gpio_set_direction((gpio_num_t)lmic_pins.dio0, GPIO_MODE_INPUT);
gpio_set_intr_type((gpio_num_t)lmic_pins.dio0, GPIO_INTR_POSEDGE);
gpio_isr_handler_add((gpio_num_t)lmic_pins.dio0, dioIrqHandler, (void *)0);
gpio_pad_select_gpio((gpio_num_t)lmic_pins.dio1);
gpio_set_direction((gpio_num_t)lmic_pins.dio1, GPIO_MODE_INPUT);
gpio_set_intr_type((gpio_num_t)lmic_pins.dio1, GPIO_INTR_POSEDGE);
gpio_isr_handler_add((gpio_num_t)lmic_pins.dio1, dioIrqHandler, (void *)1);
ESP_LOGI(TAG, "IO initialized");
}
void hal_pin_rxtx(u1_t val)
{
if (ttn_hal.pinRxTx == LMIC_UNUSED_PIN)
if (lmic_pins.rxtx == LMIC_UNUSED_PIN)
return;
gpio_set_level(ttn_hal.pinRxTx, val);
gpio_set_level((gpio_num_t)lmic_pins.rxtx, val);
}
void hal_pin_rst(u1_t val)
{
if (ttn_hal.pinRst == LMIC_UNUSED_PIN)
if (lmic_pins.rst == 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);
{ // drive pin
gpio_set_level((gpio_num_t)lmic_pins.rst, val);
gpio_set_direction((gpio_num_t)lmic_pins.rst, 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
{ // keep pin floating
gpio_set_level((gpio_num_t)lmic_pins.rst, val);
gpio_set_direction((gpio_num_t)lmic_pins.rst, GPIO_MODE_INPUT);
}
}
s1_t hal_getRssiCal (void)
{
return ttn_hal.rssiCal;
return lmic_pins.rssi_cal;
}
ostime_t hal_setModuleActive (bit_t val)
@ -158,12 +140,6 @@ 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
@ -177,11 +153,11 @@ void HAL_ESP32::spiInit()
spiConfig.clock_speed_hz = CONFIG_TTN_SPI_FREQ;
spiConfig.command_bits = 0;
spiConfig.address_bits = 8;
spiConfig.spics_io_num = pinNSS;
spiConfig.spics_io_num = lmic_pins.nss;
spiConfig.queue_size = 1;
spiConfig.cs_ena_posttrans = 2;
esp_err_t ret = spi_bus_add_device(spiHost, &spiConfig, &spiHandle);
esp_err_t ret = spi_bus_add_device(lmic_pins.spi_host, &spiConfig, &spiHandle);
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "SPI initialized");
@ -220,104 +196,101 @@ void HAL_ESP32::spiRead(uint8_t cmd, uint8_t *buf, size_t len)
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.
* 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.
*
* The ESP32 has a 64 bit timer counting microseconds. It will wrap around
* every 584,000 years. So we don't need to bother.
* 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.
*
* Based on this timer, future callbacks can be scheduled. This is used to
* schedule the next LMIC job.
* 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.
*/
// 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;
}
static const ostime_t OVERRUN_TRESHOLD = 0x10000; // approx 10 seconds
void HAL_ESP32::timerInit()
{
esp_timer_create_args_t timerConfig = {
.callback = &timerCallback,
.arg = nullptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "lmic_job"
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 */
};
esp_err_t err = esp_timer_create(&timerConfig, &timer);
ESP_ERROR_CHECK(err);
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, timerIrqHandler, NULL, ESP_INTR_FLAG_IRAM, NULL);
timer_start(TTN_TIMER_GROUP, TTN_TIMER);
ESP_LOGI(TAG, "Timer initialized");
}
void HAL_ESP32::setNextAlarm(int64_t time)
void HAL_ESP32::prepareNextAlarm(u4_t time)
{
nextAlarm = 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);
}
void HAL_ESP32::armTimer(int64_t espNow)
nextTimerEvent = time;
if (now32 > time && now32 - time > OVERRUN_TRESHOLD)
nextTimerEvent += 0x100000000;
}
void HAL_ESP32::armTimer()
{
if (nextAlarm == 0)
return;
int64_t timeout = nextAlarm - esp_timer_get_time();
if (timeout < 0)
timeout = 10;
esp_timer_start_once(timer, timeout);
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_DIS);
timer_set_alarm_value(TTN_TIMER_GROUP, TTN_TIMER, nextTimerEvent);
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_EN);
}
void HAL_ESP32::disarmTimer()
{
esp_timer_stop(timer);
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_DIS);
nextTimerEvent = 0x200000000; // wait indefinitely (almost)
}
void HAL_ESP32::timerCallback(void *arg)
void IRAM_ATTR HAL_ESP32::timerIrqHandler(void *arg)
{
xTaskNotify(lmicTask, NOTIFY_BIT_TIMER, eSetBits);
TTN_CLEAR_TIMER_ALARM;
BaseType_t higherPrioTaskWoken = pdFALSE;
HALQueueItem item { TIMER };
xQueueSendFromISR(ttn_hal.dioQueue, &item, &higherPrioTaskWoken);
if (higherPrioTaskWoken)
portYIELD_FROM_ISR();
}
// 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)
HALQueueItem item;
if (xQueueReceive(dioQueue, &item, ticksToWait) == pdFALSE)
return false;
if ((bits & NOTIFY_BIT_STOP) != 0)
return false;
if ((bits & NOTIFY_BIT_WAKEUP) != 0)
if (item.ev == WAKEUP)
{
if (waitKind != WAIT_FOR_TIMER)
{
@ -325,10 +298,9 @@ bool HAL_ESP32::wait(WaitKind waitKind)
return true;
}
}
else if ((bits & NOTIFY_BIT_TIMER) != 0)
else if (item.ev == TIMER)
{
disarmTimer();
setNextAlarm(0);
if (waitKind != CHECK_IO)
return true;
}
@ -337,7 +309,7 @@ bool HAL_ESP32::wait(WaitKind waitKind)
if (waitKind != WAIT_FOR_TIMER)
disarmTimer();
enterCriticalSection();
radio_irq_handler_v2(dioNum, dioInterruptTime);
radio_irq_handler_v2(item.ev, item.time);
leaveCriticalSection();
if (waitKind != WAIT_FOR_TIMER)
return true;
@ -345,65 +317,58 @@ bool HAL_ESP32::wait(WaitKind waitKind)
}
}
// 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);
uint64_t val;
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &val);
return (u4_t)val;
}
// 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)
void hal_waitUntil(u4_t time)
{
return ttn_hal.waitUntil(time);
ttn_hal.waitUntil(time);
}
uint32_t HAL_ESP32::waitUntil(uint32_t osTime)
void HAL_ESP32::waitUntil(uint32_t time)
{
int64_t espNow = esp_timer_get_time();
int64_t espTime = osTimeToEspTime(espNow, osTime);
setNextAlarm(espTime);
armTimer(espNow);
prepareNextAlarm(time);
armTimer();
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);
HALQueueItem item { WAKEUP };
xQueueSend(dioQueue, &item, 0);
}
// 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)
// check and rewind for target time
u1_t hal_checkTimer(u4_t time)
{
return ttn_hal.checkTimer(time);
}
uint8_t HAL_ESP32::checkTimer(u4_t osTime)
uint8_t HAL_ESP32::checkTimer(uint32_t time)
{
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
uint64_t now;
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
u4_t now32 = (u4_t)now;
setNextAlarm(espTime);
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
}
prepareNextAlarm(time);
return 0;
}
// Go to sleep until next event.
// Called when LMIC is not busy and not job is due to be executed.
void hal_sleep()
{
ttn_hal.sleep();
@ -414,7 +379,7 @@ void HAL_ESP32::sleep()
if (wait(CHECK_IO))
return;
armTimer(esp_timer_get_time());
armTimer();
wait(WAIT_FOR_ANY_EVENT);
}
@ -434,12 +399,6 @@ void hal_enableIRQs()
// 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
@ -461,12 +420,8 @@ void HAL_ESP32::leaveCriticalSection()
// -----------------------------------------------------------------------------
void HAL_ESP32::lmicBackgroundTask(void* pvParameter)
{
HAL_ESP32* instance = (HAL_ESP32*)pvParameter;
while (instance->runBackgroundTask)
os_runloop_once();
vTaskDelete(nullptr);
void HAL_ESP32::backgroundTask(void* pvParameter) {
os_runloop();
}
void hal_init_ex(const void *pContext)
@ -480,50 +435,16 @@ void HAL_ESP32::init()
ioInit();
// configure radio SPI
spiInit();
// configure timer and alarm callback
// configure timer and interrupt handler
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_ESP32::startBackgroundTask() {
xTaskCreate(backgroundTask, "ttn_lora_task", 1024 * 4, NULL, CONFIG_TTN_BG_TASK_PRIO, NULL);
}
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);
}
ESP_LOGE(TAG, "%s:%d", file, line);
ASSERT(0);
}

View File

@ -2,12 +2,12 @@
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018-2019 Manuel Bleichenbacher
* 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.
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-iDF.
*******************************************************************************/
#ifndef _hal_esp32_h_
@ -16,11 +16,32 @@
#include <stdint.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_timer.h>
#include "driver/spi_master.h"
extern "C" {
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;
}
enum HAL_Event {
DIO0 = 0,
DIO1,
DIO2,
TIMER,
WAKEUP
};
enum WaitKind {
@ -36,56 +57,35 @@ 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 startBackgroundTask();
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);
uint8_t checkTimer(uint32_t time);
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;
void waitUntil(uint32_t time);
private:
static void lmicBackgroundTask(void* pvParameter);
static void backgroundTask(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 prepareNextAlarm(uint32_t time);
void armTimer();
void disarmTimer();
static void IRAM_ATTR timerIrqHandler(void *arg);
bool wait(WaitKind waitKind);
static TaskHandle_t lmicTask;
static uint32_t dioInterruptTime;
static uint8_t dioNum;
QueueHandle_t dioQueue;
spi_device_handle_t spiHandle;
spi_transaction_t spiTransaction;
uint64_t nextTimerEvent;
SemaphoreHandle_t mutex;
esp_timer_handle_t timer;
int64_t nextAlarm;
volatile bool runBackgroundTask;
};
extern HAL_ESP32 ttn_hal;

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

@ -116,14 +116,12 @@
// define these in lmic_project_config.h to disable the corresponding MAC commands.
// Class A
//#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
//#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
// Class B
//#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
//#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
// DEPRECATED(tmm@mcci.com); replaced by LMIC.noRXIQinversion (dynamic). Don't define this.
//#define DISABLE_INVERT_IQ_ON_RX
@ -192,24 +190,4 @@
# 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_

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

@ -110,10 +110,9 @@ void hal_sleep (void);
u4_t hal_ticks (void);
/*
* busy-wait until specified timestamp (in ticks) is reached. If on-time, return 0,
* otherwise return the number of ticks we were late.
* busy-wait until specified timestamp (in ticks) is reached.
*/
u4_t hal_waitUntil (u4_t time);
void hal_waitUntil (u4_t time);
/*
* check and rewind timer for target time.
@ -148,38 +147,8 @@ s1_t hal_getRssiCal (void);
*/
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

1322
src/lmic/lmic.c Normal file → Executable file

File diff suppressed because it is too large Load Diff

215
src/lmic/lmic.h Normal file → Executable file
View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* Copyright (c) 2016 Matthijs Kooijman.
* Copyright (c) 2016-2020 MCCI Corporation.
* Copyright (c) 2016-2019 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)*UINT32_C(1)) << 24) | (((minor)*UINT32_C(1)) << 16) | (((patch)*UINT32_C(1)) << 8) | (((local)*UINT32_C(1)) << 0))
(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 2, 0, 0) /* v3.2.0 */
#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(2, 3, 2, 50) /* v2.3.2.50 */
#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \
((((v)*UINT32_C(1)) >> 24u) & 0xFFu)
(((v) >> 24u) & 0xFFu)
#define ARDUINO_LMIC_VERSION_GET_MINOR(v) \
((((v)*UINT32_C(1)) >> 16u) & 0xFFu)
(((v) >> 16u) & 0xFFu)
#define ARDUINO_LMIC_VERSION_GET_PATCH(v) \
((((v)*UINT32_C(1)) >> 8u) & 0xFFu)
(((v) >> 8u) & 0xFFu)
#define ARDUINO_LMIC_VERSION_GET_LOCAL(v) \
((v) & 0xFFu)
@ -127,20 +127,11 @@ extern "C"{
enum { MAX_FRAME_LEN = MAX_LEN_FRAME }; //!< Library cap on max frame length
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 { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests
enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
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
@ -176,8 +167,6 @@ enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are imm
struct lmic_saved_adr_state_s {
u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits
u2_t activeChannels125khz;
u2_t activeChannels500khz;
};
#endif // ==========================================================================
@ -185,17 +174,17 @@ struct lmic_saved_adr_state_s {
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, DRCHG_FRAMESIZE };
enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
enum { KEEP_TXPOW = -128 };
#if !defined(DISABLE_PING)
//! \internal
struct rxsched_t {
dr_t dr;
u1_t dr;
u1_t intvExp; // 0..7
u1_t slot; // runs from 0 to 128
rxsyms_t rxsyms;
u1_t rxsyms;
ostime_t rxbase;
ostime_t rxtime; // start of next spot
u4_t freq;
@ -226,7 +215,7 @@ struct bcninfo_t {
#endif // !DISABLE_BEACONS
// purpose of receive window - lmic_t.rxState
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TX_AT=4, };
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
// Netid values / lmic_t.netid
enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
// MAC operation modes (lmic_t.opmode).
@ -245,19 +234,15 @@ 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_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_DNW1 = 0x01, // received in 1st DN slot
};
TXRX_DNW2 = 0x02, // received in 2dn DN slot
TXRX_PING = 0x04 }; // received in a scheduled RX slot
// Event types for event callback
enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
@ -267,85 +252,9 @@ enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
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.
@ -383,14 +292,6 @@ enum lmic_request_time_state_e {
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
@ -440,32 +341,6 @@ struct lmic_client_data_s {
/*
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:
@ -488,9 +363,6 @@ struct lmic_t {
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)
@ -525,10 +397,6 @@ struct lmic_t {
#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
@ -540,31 +408,24 @@ struct lmic_t {
#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 rxsyms;
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 radio_txpow; // the radio driver's copy of txpow, limited by adrTxPow.
s1_t lbt_dbmax; // max permissible dB on our channel (eg -80)
u1_t txChnl; // channel for next TX
@ -576,33 +437,36 @@ struct lmic_t {
u1_t errcr; // error coding rate (used for TX only)
u1_t rejoinCnt; // adjustment for rejoin datarate
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; // count of bytes in pendTxData.
u1_t pendTxLen; // +0x80 = confirmed
u1_t pendTxData[MAX_LEN_PAYLOAD];
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
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
u1_t lastDnConf; // downlink with seqnoDn-1 requested confirmation
s1_t adrAckReq; // counter until we reset data rate (0=off)
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
@ -615,20 +479,17 @@ struct lmic_t {
// 2nd RX window (after up stream)
u1_t dn2Dr;
#if !defined(DISABLE_MCMD_RXParamSetupReq)
#if !defined(DISABLE_MCMD_DN2P_SET)
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
// Public part of MAC state
u1_t txCnt;
@ -639,6 +500,7 @@ struct lmic_t {
#if !defined(DISABLE_BEACONS)
u1_t bcnChnl;
u1_t bcnRxsyms; //
#endif
u1_t noRXIQinversion;
@ -666,7 +528,6 @@ void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn
bit_t LMIC_startJoining (void);
void LMIC_tryRejoin (void);
void LMIC_unjoin (void);
void LMIC_unjoinAndRejoin (void);
#endif
void LMIC_shutdown (void);
@ -674,11 +535,8 @@ void LMIC_init (void);
void LMIC_reset (void);
void LMIC_clrTxData (void);
void LMIC_setTxData (void);
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);
int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
int LMIC_sendWithCallback(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)
@ -730,7 +588,4 @@ DECL_ON_LMIC_EVENT;
} // extern "C"
#endif
// names for backward compatibility
#include "lmic_compat.h"
#endif // _lmic_h_

78
src/lmic/lmic_as923.c Normal file → Executable file
View File

@ -56,10 +56,10 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = {
59+5, // [1]
59+5, // [2]
123+5, // [3]
250+5, // [4]
250+5, // [5]
250+5, // [6]
250+5 // [7]
230+5, // [4]
230+5, // [5]
230+5, // [6]
230+5 // [7]
};
// see table in 2.7.6 -- this assumes UplinkDwellTime = 1.
@ -76,18 +76,16 @@ static CONST_TABLE(u1_t, maxFrameLens_dwell1)[] = {
static uint8_t
LMICas923_getUplinkDwellBit(uint8_t mcmd_txparam) {
if (mcmd_txparam == 0xFF)
return AS923_INITIAL_TxParam_UplinkDwellTime;
LMIC_API_PARAMETER(mcmd_txparam);
return (mcmd_txparam & MCMD_TxParam_TxDWELL_MASK) != 0;
return (LMIC.txParam & MCMD_TxParam_TxDWELL_MASK) != 0;
}
static uint8_t
LMICas923_getDownlinkDwellBit(uint8_t mcmd_txparam) {
if (mcmd_txparam == 0xFF)
return AS923_INITIAL_TxParam_DownlinkDwellTime;
LMIC_API_PARAMETER(mcmd_txparam);
return (mcmd_txparam & MCMD_TxParam_RxDWELL_MASK) != 0;
return (LMIC.txParam & MCMD_TxParam_RxDWELL_MASK) != 0;
}
uint8_t LMICas923_maxFrameLen(uint8_t dr) {
@ -97,7 +95,7 @@ uint8_t LMICas923_maxFrameLen(uint8_t dr) {
else
return TABLE_GET_U1(maxFrameLens_dwell0, dr);
} else {
return 0;
return 0xFF;
}
}
@ -112,6 +110,7 @@ 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
@ -120,7 +119,6 @@ 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
@ -132,20 +130,15 @@ static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) {
}
// translate from an encoded power to an actual power using
// the maxeirp setting; return -128 if not legal.
// the maxeirp setting.
int8_t LMICas923_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)) {
s1_t const adj =
TABLE_GET_S1(
TXPOWLEVELS,
pindex
(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT
);
return LMICas923_getMaxEIRP(LMIC.txParam) + adj;
} else {
return -128;
}
}
// only used in this module, but used by variant macro dr2hsym().
@ -178,9 +171,6 @@ 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));
@ -227,18 +217,6 @@ 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) {
@ -251,10 +229,7 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
LMIC.channelDrMap[chidx] =
drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B)
: drmap;
if (fEnable)
LMIC.channelMap |= 1 << chidx; // enabled right away
else
LMIC.channelMap &= ~(1 << chidx);
return 1;
}
@ -285,8 +260,6 @@ void LMICas923_setRx1Params(void) {
int effective_rx1DrOffset;
int candidateDr;
LMICeulike_setRx1Freq();
effective_rx1DrOffset = LMIC.rx1DrOffset;
// per section 2.7.7 of regional, lines 1101:1103:
switch (effective_rx1DrOffset) {
@ -361,18 +334,23 @@ 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 is set to 0xFF by the central code at init time.
LMIC.txParam = 0xFF;
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
@ -380,18 +358,8 @@ LMICas923_updateTx(ostime_t txbeg) {
LMIC.freq = freq & ~(u4_t)3;
LMIC.txpow = LMICas923_getMaxEIRP(LMIC.txParam);
band->avail = txbeg + airtime * band->txcap;
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;
if (LMIC.globalDutyRate != 0)
LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate);
}

115
src/lmic/lmic_au915.c → src/lmic/lmic_au921.c Normal file → Executable file
View File

@ -30,10 +30,10 @@
#include "lmic_bandplan.h"
#if defined(CFG_au915)
#if defined(CFG_au921)
// ================================================================================
//
// BEG: AU915 related stuff
// BEG: AU921 related stuff
//
CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
@ -55,60 +55,15 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS
};
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 };
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_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);
uint8_t LMICau921_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, 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)));
}
return 0xFF;
}
static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = {
@ -131,20 +86,20 @@ static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = {
// 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 LMICau915_dr2hsym(uint8_t dr) {
ostime_t LMICau921_dr2hsym(uint8_t dr) {
return TABLE_GET_OSTIME(DR2HSYM_osticks, dr);
}
u4_t LMICau915_convFreq(xref2cu1_t ptr) {
u4_t LMICau921_convFreq(xref2cu1_t ptr) {
u4_t freq = (os_rlsbf4(ptr - 1) >> 8) * 100;
if (freq < AU915_FREQ_MIN || freq > AU915_FREQ_MAX)
if (freq < AU921_FREQ_MIN || freq > AU921_FREQ_MAX)
freq = 0;
return freq;
}
// au915: no support for xchannels.
// au921: 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);
@ -229,53 +184,40 @@ bit_t LMIC_selectSubBand(u1_t band) {
return result;
}
void LMICau915_updateTx(ostime_t txbeg) {
void LMICau921_updateTx(ostime_t txbeg) {
u1_t chnl = LMIC.txChnl;
LMIC.txpow = LMICau915_getMaxEIRP(LMIC.txParam);
LMIC.txpow = AU921_TX_EIRP_MAX_DBM;
if (chnl < 64) {
LMIC.freq = AU915_125kHz_UPFBASE + chnl*AU915_125kHz_UPFSTEP;
LMIC.freq = AU921_125kHz_UPFBASE + chnl*AU921_125kHz_UPFSTEP;
} else {
ASSERT(chnl < 64 + 8);
LMIC.freq = AU915_500kHz_UPFBASE + (chnl - 64)*AU915_500kHz_UPFSTEP;
LMIC.freq = AU921_500kHz_UPFBASE + (chnl - 64)*AU921_500kHz_UPFSTEP;
}
// Update global duty cycle stat and deal with dwell time.
u4_t dwellDelay;
u4_t globalDutyDelay;
dwellDelay = globalDutyDelay = 0;
// Update global duty cycle stats
if (LMIC.globalDutyRate != 0) {
ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
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;
LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate);
}
}
#if !defined(DISABLE_BEACONS)
void LMICau915_setBcnRxParams(void) {
void LMICau921_setBcnRxParams(void) {
LMIC.dataLen = 0;
LMIC.freq = AU915_500kHz_DNFBASE + LMIC.bcnChnl * AU915_500kHz_DNFSTEP;
LMIC.freq = AU921_500kHz_DNFBASE + LMIC.bcnChnl * AU921_500kHz_DNFSTEP;
LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN), 1), LEN_BCN);
}
#endif // !DISABLE_BEACONS
// set the Rx1 dndr, rps.
void LMICau915_setRx1Params(void) {
void LMICau921_setRx1Params(void) {
u1_t const txdr = LMIC.dndr;
u1_t candidateDr;
LMIC.freq = AU915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * AU915_500kHz_DNFSTEP;
if ( /* TX datarate */txdr < AU915_DR_SF8C)
LMIC.freq = AU921_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * AU921_500kHz_DNFSTEP;
if ( /* TX datarate */txdr < AU921_DR_SF8C)
candidateDr = txdr + 8 - LMIC.rx1DrOffset;
else
candidateDr = AU915_DR_SF7CR;
candidateDr = AU921_DR_SF7CR;
if (candidateDr < LORAWAN_DR8)
candidateDr = LORAWAN_DR8;
@ -286,17 +228,16 @@ void LMICau915_setRx1Params(void) {
LMIC.rps = dndr2rps(LMIC.dndr);
}
void LMICau915_initJoinLoop(void) {
// LMIC.txParam is set to 0xFF by the central code at init time.
void LMICau921_initJoinLoop(void) {
LMICuslike_initJoinLoop();
// initialize the adrTxPower.
LMIC.adrTxPow = LMICau915_getMaxEIRP(LMIC.txParam); // dBm
LMIC.adrTxPow = 30; // dBm
}
//
// END: AU915 related stuff
// END: AU921 related stuff
//
// ================================================================================
#endif

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

@ -37,12 +37,10 @@
# include "lmic_bandplan_eu868.h"
#elif defined(CFG_us915)
# include "lmic_bandplan_us915.h"
#elif defined(CFG_au915)
# include "lmic_bandplan_au915.h"
#elif defined(CFG_au921)
# include "lmic_bandplan_au921.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
@ -54,8 +52,8 @@
# error "DNW2_SAFETY_ZONE not defined by bandplan"
#endif
#ifndef LMICbandplan_maxFrameLen
# error "LMICbandplan_maxFrameLen() not defined by bandplan"
#ifndef maxFrameLen
# error "maxFrameLen() not defined by bandplan"
#endif
#ifndef pow2dBm
@ -158,56 +156,16 @@
# 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 LMICbandplan_MINRX_SYMS_LoRa_ClassB
#define MINRX_SYMS 5
#endif // !defined(MINRX_SYMS)
#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 PAMBL_SYMS 8
#define PAMBL_FSK 5
#define PRERX_FSK 1
#define RXLEN_FSK (1+5+2)
#define BCN_INTV_osticks sec2osticks(BCN_INTV_sec)
#define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms)
@ -226,6 +184,5 @@
// 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_

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

@ -33,10 +33,8 @@
# 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);
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICas923_maxFrameLen(dr)
#define maxFrameLen(dr) LMICas923_maxFrameLen(dr)
int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICas923_pow2dBm(mcmd_ladr_p1)
@ -72,7 +70,13 @@ void LMICas923_init(void);
// override default for LMICbandplan_isFSK()
#undef LMICbandplan_isFSK
#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == AS923_DR_FSK)
#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_getInitialDrJoin() (AS923_DR_SF10)

View File

@ -26,8 +26,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lmic_bandplan_au915_h_
# define _lmic_bandplan_au915_h_
#ifndef _lmic_bandplan_au921_h_
# define _lmic_bandplan_au921_h_
// preconditions for lmic_us_like.h
#define LMICuslike_getFirst500kHzDR() (LORAWAN_DR6)
@ -37,33 +37,30 @@
# include "lmic_us_like.h"
#endif
// 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)
uint8_t LMICau921_maxFrameLen(uint8_t dr);
#define maxFrameLen(dr) LMICau921_maxFrameLen(dr)
int8_t LMICau915_pow2dbm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICau915_pow2dbm(mcmd_ladr_p1)
#define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1)))
ostime_t LMICau915_dr2hsym(uint8_t dr);
#define dr2hsym(dr) LMICau915_dr2hsym(dr)
ostime_t LMICau921_dr2hsym(uint8_t dr);
#define dr2hsym(dr) LMICau921_dr2hsym(dr)
#define LMICbandplan_getInitialDrJoin() (LORAWAN_DR2)
void LMICau915_initJoinLoop(void);
#define LMICbandplan_initJoinLoop() LMICau915_initJoinLoop()
void LMICau921_initJoinLoop(void);
#define LMICbandplan_initJoinLoop() LMICau921_initJoinLoop()
void LMICau915_setBcnRxParams(void);
#define LMICbandplan_setBcnRxParams() LMICau915_setBcnRxParams()
void LMICau921_setBcnRxParams(void);
#define LMICbandplan_setBcnRxParams() LMICau921_setBcnRxParams()
u4_t LMICau915_convFreq(xref2cu1_t ptr);
#define LMICbandplan_convFreq(ptr) LMICau915_convFreq(ptr)
u4_t LMICau921_convFreq(xref2cu1_t ptr);
#define LMICbandplan_convFreq(ptr) LMICau921_convFreq(ptr)
void LMICau915_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICau915_setRx1Params()
void LMICau921_setRx1Params(void);
#define LMICbandplan_setRx1Params() LMICau921_setRx1Params()
void LMICau915_updateTx(ostime_t txbeg);
#define LMICbandplan_updateTx(txbeg) LMICau915_updateTx(txbeg)
void LMICau921_updateTx(ostime_t txbeg);
#define LMICbandplan_updateTx(txbeg) LMICau921_updateTx(txbeg)
#endif // _lmic_bandplan_au915_h_
#endif // _lmic_bandplan_au921_h_

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

@ -33,10 +33,8 @@
# 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);
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICeu868_maxFrameLen(dr)
#define maxFrameLen(dr) LMICeu868_maxFrameLen(dr)
int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICeu868_pow2dBm(mcmd_ladr_p1)
@ -59,7 +57,13 @@ LMICeu868_isValidBeacon1(const uint8_t *d) {
// override default for LMICbandplan_isFSK()
#undef LMICbandplan_isFSK
#define LMICbandplan_isFSK() (/* RX datarate */LMIC.dndr == EU868_DR_FSK)
#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_getInitialDrJoin() (EU868_DR_SF7)

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

@ -33,10 +33,8 @@
# 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);
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICin866_maxFrameLen(dr)
#define maxFrameLen(dr) LMICin866_maxFrameLen(dr)
int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICin866_pow2dBm(mcmd_ladr_p1)
@ -56,7 +54,13 @@ LMICin866_isValidBeacon1(const uint8_t *d) {
// override default for LMICbandplan_isFSK()
#undef LMICbandplan_isFSK
#define LMICbandplan_isFSK() (/* TX datarate */LMIC.dndr == IN866_DR_FSK)
#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_getInitialDrJoin() (IN866_DR_SF7)

View File

@ -1,91 +0,0 @@
/*
* 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_

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

@ -37,13 +37,10 @@
# 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);
// return maximum frame length (including PHY header) for this data rate; 0 --> not valid dr.
#define LMICbandplan_maxFrameLen(dr) LMICus915_maxFrameLen(dr)
#define maxFrameLen(dr) LMICus915_maxFrameLen(dr)
int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1);
#define pow2dBm(mcmd_ladr_p1) LMICus915_pow2dbm(mcmd_ladr_p1)
#define pow2dBm(mcmd_ladr_p1) ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1)))
ostime_t LMICus915_dr2hsym(uint8_t dr);
#define dr2hsym(dr) LMICus915_dr2hsym(dr)

View File

@ -1,72 +0,0 @@
/*
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_ */

View File

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

View File

@ -1,138 +0,0 @@
/*
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_ */

47
src/lmic/lmic_config_preconditions.h Normal file → Executable file
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_au915 5
#define LMIC_REGION_au921 5
#define LMIC_REGION_cn490 6
#define LMIC_REGION_as923 7
#define LMIC_REGION_kr920 8
#define LMIC_REGION_kr921 8
#define LMIC_REGION_in866 9
// Some regions have country-specific overrides. For generality, we specify
@ -93,16 +93,6 @@ 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.
@ -111,10 +101,10 @@ Revision history:
(1 << LMIC_REGION_us915) | \
/* (1 << LMIC_REGION_cn783) | */ \
/* (1 << LMIC_REGION_eu433) | */ \
(1 << LMIC_REGION_au915) | \
(1 << LMIC_REGION_au921) | \
/* (1 << LMIC_REGION_cn490) | */ \
(1 << LMIC_REGION_as923) | \
(1 << LMIC_REGION_kr920) | \
/* (1 << LMIC_REGION_kr921) | */ \
(1 << LMIC_REGION_in866) | \
0)
@ -135,10 +125,10 @@ Revision history:
(defined(CFG_us915) << LMIC_REGION_us915) | \
(defined(CFG_cn783) << LMIC_REGION_cn783) | \
(defined(CFG_eu433) << LMIC_REGION_eu433) | \
(defined(CFG_au915) << LMIC_REGION_au915) | \
(defined(CFG_au921) << LMIC_REGION_au921) | \
(defined(CFG_cn490) << LMIC_REGION_cn490) | \
(defined(CFG_as923) << LMIC_REGION_as923) | \
(defined(CFG_kr920) << LMIC_REGION_kr920) | \
(defined(CFG_kr921) << LMIC_REGION_kr921) | \
(defined(CFG_in866) << LMIC_REGION_in866) | \
0)
@ -153,8 +143,8 @@ Revision history:
# define CFG_region LMIC_REGION_cn783
#elif defined(CFG_eu433)
# define CFG_region LMIC_REGION_eu433
#elif defined(CFG_au915)
# define CFG_region LMIC_REGION_au915
#elif defined(CFG_au921)
# define CFG_region LMIC_REGION_au921
#elif defined(CFG_cn490)
# define CFG_region LMIC_REGION_cn490
#elif defined(CFG_as923jp)
@ -163,8 +153,8 @@ Revision history:
# define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP
#elif defined(CFG_as923)
# define CFG_region LMIC_REGION_as923
#elif defined(CFG_kr920)
# define CFG_region LMIC_REGION_kr920
#elif defined(CFG_kr921)
# define CFG_region LMIC_REGION_kr921
#elif defined(CFG_in866)
# define CFG_region LMIC_REGION_in866
#else
@ -181,10 +171,10 @@ Revision history:
/* (1 << LMIC_REGION_us915) | */ \
(1 << LMIC_REGION_cn783) | \
(1 << LMIC_REGION_eu433) | \
/* (1 << LMIC_REGION_au915) | */ \
/* (1 << LMIC_REGION_au921) | */ \
/* (1 << LMIC_REGION_cn490) | */ \
(1 << LMIC_REGION_as923) | \
(1 << LMIC_REGION_kr920) | \
(1 << LMIC_REGION_kr921) | \
(1 << LMIC_REGION_in866) | \
0)
@ -199,10 +189,10 @@ Revision history:
(1 << LMIC_REGION_us915) | \
/* (1 << LMIC_REGION_cn783) | */ \
/* (1 << LMIC_REGION_eu433) | */ \
(1 << LMIC_REGION_au915) | \
(1 << LMIC_REGION_au921) | \
/* (1 << LMIC_REGION_cn490) | */ \
/* (1 << LMIC_REGION_as923) | */ \
/* (1 << LMIC_REGION_kr920) | */ \
/* (1 << LMIC_REGION_kr921) | */ \
/* (1 << LMIC_REGION_in866) | */ \
0)
@ -214,13 +204,4 @@ Revision history:
#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_ */

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

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

@ -49,28 +49,21 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS
};
static CONST_TABLE(u1_t, maxFrameLens)[] = {
59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 250+5
};
static CONST_TABLE(u1_t, maxFrameLens)[] = { 64,64,64,123 };
uint8_t LMICeu868_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
return 0;
return 0xFF;
}
static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
16, 14, 12, 10, 8, 6, 4, 2
16, 14, 12, 10, 8, 6, 4, 2, 0,0,0,0, 0,0,0,0
};
int8_t LMICeu868_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;
}
return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT);
}
// only used in this module, but used by variant macro dr2hsym().
@ -82,7 +75,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 -- time for 1/2 byte (unused by LMIC)
us2osticksRound(80) // FSK -- not used (time for 1/2 byte)
};
ostime_t LMICeu868_dr2hsym(uint8_t dr) {
@ -100,9 +93,6 @@ 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));
@ -114,9 +104,18 @@ void LMICeu868_initDefaultChannels(bit_t join) {
LMIC.channelDrMap[fu] = DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7);
}
(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% */);
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();
}
bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) {
@ -130,59 +129,26 @@ 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) {
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;
LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7) : drmap;
if (fEnable)
LMIC.channelMap |= 1 << chidx; // enabled right away
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
LMIC.channelMap &= ~(1 << chidx);
freq |= BAND_MILLI; // 0.1% 14dBm
}
else {
if (band > BAND_AUX) return 0;
freq = (freq&~3) | 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
return 1;
}
@ -247,14 +213,20 @@ 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

114
src/lmic/lmic_eu_like.c Normal file → Executable file
View File

@ -56,71 +56,21 @@ bit_t LMIC_enableChannel(u1_t channel) {
return 0;
}
// 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.
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]&~3) == 0)
if ((chmap & (1 << chnl)) != 0 && LMIC.channelFreq[chnl] == 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.
// assumes that LMICeulike_canMapChannels passed. Return true if something changed.
bit_t LMICeulike_mapChannels(u1_t chpage, u2_t chmap) {
switch (chpage) {
case MCMD_LinkADRReq_ChMaskCntl_EULIKE_DIRECT:
u2_t const old_chmap = LMIC.channelMap;
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;
}
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;
return old_chmap != chmap;
}
#if !defined(DISABLE_JOIN)
@ -174,26 +124,25 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) {
if ((++LMIC.txCnt % nDefaultChannels) == 0) {
// Lower DR every nth try (having all default channels with same DR)
//
// TODO(tmm@mcci.com) add new DR_REGION_JOIN_MIN instead of LORAWAN_DR0;
// 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()
//
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
#if CFG_region != LMIC_REGION_as923
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;
failed = 1;
#else
if (LMIC.datarate == LORAWAN_DR0)
failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
else {
LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
}
#endif
}
}
// Clear NEXTCHNL because join state engine controls channel hopping
LMIC.opmode &= ~OP_NEXTCHNL;
// Move txend to randomize synchronized concurrent joins.
@ -221,7 +170,7 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) {
#endif // !DISABLE_JOIN
void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) {
os_copyMem(
memcpy(
pStateBuffer->channelFreq,
LMIC.channelFreq,
sizeof(LMIC.channelFreq)
@ -235,35 +184,4 @@ bit_t LMICeulike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) {
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

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

@ -56,8 +56,7 @@ LMICeulike_isValidBeacon1(const uint8_t *d) {
#define LMICbandplan_isFSK() (0)
// provide a default LMICbandplan_txDoneDoFSK()
void LMICeulike_txDoneFSK(ostime_t delay, osjobcb_t func);
#define LMICbandplan_txDoneFSK(delay, func) LMICeulike_txDoneFSK(delay, func)
#define LMICbandplan_txDoneFSK(delay, func) do { } while (0)
#define LMICbandplan_joinAcceptChannelClear() LMICbandplan_initDefaultChannels(/* normal, not join */ 0)
@ -102,14 +101,4 @@ void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer);
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_

45
src/lmic/lmic_in866.c Normal file → Executable file
View File

@ -49,28 +49,21 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS
};
static CONST_TABLE(u1_t, maxFrameLens)[] = {
59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 0, 250+5
};
static CONST_TABLE(u1_t, maxFrameLens)[] = { 59+5,59+5,59+5,123+5, 230+5, 230+5 };
uint8_t LMICin866_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
return 0;
return 0xFF;
}
static CONST_TABLE(s1_t, TXPOWLEVELS)[] = {
30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10
30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 0, 0,0,0,0
};
int8_t LMICin866_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;
}
return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT);
}
// only used in this module, but used by variant macro dr2hsym().
@ -105,9 +98,6 @@ 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));
@ -135,32 +125,17 @@ 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 = (freq&~3) | BAND_MILLI;
freq |= 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;
if (fEnable)
LMIC.channelMap |= 1 << chidx; // enabled right away
else
LMIC.channelMap &= ~(1 << chidx);
return 1;
}
@ -212,14 +187,20 @@ 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

View File

@ -1,274 +0,0 @@
/*
* 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

17
src/lmic/lmic_us915.c Normal file → Executable file
View File

@ -55,26 +55,13 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = {
ILLEGAL_RPS // [14]
};
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
};
static CONST_TABLE(u1_t, maxFrameLens)[] = { 24,66,142,255,255,255,255,255, 66,142 };
uint8_t LMICus915_maxFrameLen(uint8_t dr) {
if (dr < LENOF_TABLE(maxFrameLens))
return TABLE_GET_U1(maxFrameLens, dr);
else
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)));
return 0xFF;
}
static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = {

100
src/lmic/lmic_us_like.c Normal file → Executable file
View File

@ -94,29 +94,29 @@ void LMICuslike_initDefaultChannels(bit_t fJoin) {
// verify that a given setting is permitted
bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap) {
/*
|| MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF are special. The
|| MCMD_LADR_CHP_125ON and MCMD_LADR_CHP_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_SPECIAL) {
if (chpage < MCMD_LADR_CHP_USLIKE_SPECIAL) {
if (chmap == 0)
return 0;
// operate on channels 0..15, 16..31, 32..47, 48..63, 64..71
if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K) {
if (chpage == (64 >> 4)) {
if (chmap & 0xFF00) {
// those are reserved bits, fail.
return 0;
}
} else {
return 1;
}
} else if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK) {
} else if (chpage == MCMD_LADR_CHP_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;
} else if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) {
u1_t const en125 = chpage == MCMD_LADR_CHP_125ON;
// if disabling all 125kHz chans, must have at least one 500kHz chan
// don't allow reserved bits to be set in chmap.
@ -130,45 +130,45 @@ bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap) {
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
|| MCMD_LADR_CHP_125ON and MCMD_LADR_CHP_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) {
u1_t base, top;
bit_t result = 0;
if (chpage == MCMD_LADR_CHP_BANK) {
// each bit enables a bank of channels
for (u1_t subband = 0; subband < 8; ++subband, chmap >>= 1) {
if (chmap & 1) {
LMIC_enableSubBand(subband);
result |= LMIC_enableSubBand(subband);
} else {
LMIC_disableSubBand(subband);
result |= LMIC_disableSubBand(subband);
}
}
} else {
u1_t base, top;
if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) {
return result;
}
if (chpage < MCMD_LADR_CHP_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;
base = chpage << 4;
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;
} else /* if (chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF) */ {
u1_t const en125 = chpage == MCMD_LADR_CHP_125ON;
// enable or disable all 125kHz channels
for (u1_t chnl = 0; chnl < 64; ++chnl) {
if (en125)
LMIC_enableChannel(chnl);
result |= LMIC_enableChannel(chnl);
else
LMIC_disableChannel(chnl);
result |= LMIC_disableChannel(chnl);
}
// then apply mask to top 8 channels.
@ -180,14 +180,11 @@ bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) {
// 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);
result |= LMIC_enableChannel(chnl);
else
LMIC_disableChannel(chnl);
result |= LMIC_disableChannel(chnl);
}
}
LMICOS_logEventUint32("LMICuslike_mapChannels", ((u4_t)LMIC.activeChannels125khz << 16u)|(LMIC.activeChannels500khz << 0u));
return (LMIC.activeChannels125khz > 0) || (LMIC.activeChannels500khz > 0);
return result;
}
// US does not have duty cycling - return now as earliest TX time
@ -195,38 +192,16 @@ bit_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
if (LMIC.activeChannels500khz > 0) {
ASSERT(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
if (LMIC.activeChannels125khz > 0) {
ASSERT(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
@ -312,26 +287,13 @@ ostime_t LMICuslike_nextJoinState(void) {
#endif
void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) {
os_copyMem(
memcpy(
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;
}

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

@ -106,10 +106,4 @@ void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer);
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 Normal file → Executable file
View File

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

275
src/lmic/lorabase.h Normal file → Executable file
View File

@ -44,7 +44,6 @@ 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
@ -105,8 +104,6 @@ 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
@ -227,28 +224,28 @@ enum _dr_configured_t {
};
# endif // LMIC_DR_LEGACY
#elif defined(CFG_au915) // =========================================
#elif defined(CFG_au921) // =========================================
#include "lorabase_au915.h"
#include "lorabase_au921.h"
// per 2.5.3: must be implemented
#define LMIC_ENABLE_TxParamSetupReq 1
// per 2.5.3: not implemented
#define LMIC_ENABLE_TxParamSetupReq 0
enum { DR_DFLTMIN = AU915_DR_SF7 }; // DR5
enum { DR_DFLTMIN = AU921_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_AU915 };
enum { DR_PAGE = DR_PAGE_AU921 };
//enum { CHNL_PING = 0 }; // used only for default init of state (follows beacon - rotating)
enum { FREQ_PING = AU915_500kHz_DNFBASE + 0*AU915_500kHz_DNFSTEP }; // default ping freq
enum { DR_PING = AU915_DR_SF10CR }; // default ping DR
enum { FREQ_PING = AU921_500kHz_DNFBASE + 0*AU921_500kHz_DNFSTEP }; // default ping freq
enum { DR_PING = AU921_DR_SF10CR }; // default ping DR
//enum { CHNL_DNW2 = 0 };
enum { FREQ_DNW2 = AU915_500kHz_DNFBASE + 0*AU915_500kHz_DNFSTEP };
enum { DR_DNW2 = AU915_DR_SF12CR }; // DR8
enum { FREQ_DNW2 = AU921_500kHz_DNFBASE + 0*AU921_500kHz_DNFSTEP };
enum { DR_DNW2 = AU921_DR_SF12CR }; // DR8
enum { CHNL_BCN = 0 }; // used only for default init of state (rotating beacon scheme)
enum { DR_BCN = AU915_DR_SF10CR };
enum { DR_BCN = AU921_DR_SF10CR };
enum { AIRTIME_BCN = 72192 }; // micros ... TODO(tmm@mcci.com) check.
enum { LMIC_REGION_EIRP = AU915_LMIC_REGION_EIRP }; // region uses EIRP
enum { LMIC_REGION_EIRP = AU921_LMIC_REGION_EIRP }; // region uses EIRP
enum {
// Beacon frame format AU DR10/SF10 500kHz
@ -265,20 +262,20 @@ enum {
# if LMIC_DR_LEGACY
enum _dr_configured_t {
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
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
};
# endif // LMIC_DR_LEGACY
@ -329,51 +326,6 @@ 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"
@ -477,7 +429,7 @@ enum {
enum {
// Bitfields in frame control octet
FCT_ADREN = 0x80,
FCT_ADRACKReq = 0x40,
FCT_ADRARQ = 0x40,
FCT_ACK = 0x20,
FCT_MORE = 0x10, // also in DN direction: Class B indicator
FCT_OPTLEN = 0x0F,
@ -486,11 +438,6 @@ 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
@ -499,78 +446,65 @@ enum {
// MAC uplink commands downwlink too
enum {
// Class A
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, // -
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_DeviceTimeReq = 0x0D,
// Class B
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
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 : -
};
// MAC downlink commands
enum {
// Class A
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
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_DeviceTimeAns = 0x0D,
// Class B
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
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel
};
enum {
MCMD_BeaconTimingAns_TUNIT = 30 // time unit of delay value in millis
MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis
};
enum {
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
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
};
enum {
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
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
};
enum {
MCMD_NewChannelAns_RFU = 0xFC, // RFU bits
MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate
MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency
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
};
enum {
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,
MCMD_PING_ANS_RFU = 0xFE,
MCMD_PING_ANS_FQACK = 0x01
};
enum {
@ -580,30 +514,65 @@ enum {
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
};
// Bit fields byte#3 of MCMD_LinkADRReq payload
// Bit fields byte#3 of MCMD_LADR_REQ payload
enum {
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.
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
};
// Bit fields byte#0 of MCMD_LinkADRReq payload
// Bit fields byte#0 of MCMD_LADR_REQ payload
enum {
MCMD_LinkADRReq_DR_MASK = 0xF0,
MCMD_LinkADRReq_POW_MASK = 0x0F,
MCMD_LinkADRReq_DR_SHIFT = 4,
MCMD_LinkADRReq_POW_SHIFT = 0,
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
};
// bit fields of the TxParam request

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

@ -93,13 +93,4 @@ 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_au915.h → src/lmic/lorabase_au921.h Normal file → Executable file
View File

@ -28,8 +28,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _lorabase_au915_h_
#define _lorabase_au915_h_
#ifndef _lorabase_au921_h_
#define _lorabase_au921_h_
#ifndef _LMIC_CONFIG_PRECONDITIONS_H_
# include "lmic_config_preconditions.h"
@ -37,53 +37,48 @@
/****************************************************************************\
|
| Basic definitions for AU 915 (always in scope)
| Basic definitions for AS921 (always in scope)
|
\****************************************************************************/
// 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,
// 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,
// Devices behind a router:
AU915_DR_SF12CR = 8,
AU915_DR_SF11CR,
AU915_DR_SF10CR,
AU915_DR_SF9CR,
AU915_DR_SF8CR,
AU915_DR_SF7CR
AU921_DR_SF12CR = 8,
AU921_DR_SF11CR,
AU921_DR_SF10CR,
AU921_DR_SF9CR,
AU921_DR_SF8CR,
AU921_DR_SF7CR
};
// Default frequency plan for AU 915MHz
// Default frequency plan for AU 921MHz
enum {
AU915_125kHz_UPFBASE = 915200000,
AU915_125kHz_UPFSTEP = 200000,
AU915_500kHz_UPFBASE = 915900000,
AU915_500kHz_UPFSTEP = 1600000,
AU915_500kHz_DNFBASE = 923300000,
AU915_500kHz_DNFSTEP = 600000
AU921_125kHz_UPFBASE = 915200000,
AU921_125kHz_UPFSTEP = 200000,
AU921_500kHz_UPFBASE = 915900000,
AU921_500kHz_UPFSTEP = 1600000,
AU921_500kHz_DNFBASE = 923300000,
AU921_500kHz_DNFSTEP = 600000
};
enum {
AU915_FREQ_MIN = 915000000,
AU915_FREQ_MAX = 928000000
AU921_FREQ_MIN = 915000000,
AU921_FREQ_MAX = 928000000
};
enum {
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),
AU921_TX_EIRP_MAX_DBM = 30 // 30 dBm
};
enum { DR_PAGE_AU915 = 0x10 * (LMIC_REGION_au915 - 1) };
enum { DR_PAGE_AU921 = 0x10 * (LMIC_REGION_au921 - 1) };
enum { AU915_LMIC_REGION_EIRP = 1 }; // region uses EIRP
enum { AU921_LMIC_REGION_EIRP = 1 }; // region uses EIRP
#endif /* _lorabase_au915_h_ */
#endif /* _lorabase_au921_h_ */

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

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

View File

@ -1,84 +0,0 @@
/*
* 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_ */

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

@ -77,12 +77,6 @@ enum {
// 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

@ -1,64 +0,0 @@
/*
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_ */

2
src/lmic/oslmic.c Normal file → Executable file
View File

@ -139,8 +139,6 @@ void os_runloop () {
void os_runloop_once() {
osjob_t* j = NULL;
hal_processPendingIRQs();
hal_disableIRQs();
// check for runnable jobs
if(OS.runnablejobs) {

32
src/lmic/oslmic.h Normal file → Executable file
View File

@ -119,21 +119,12 @@ void radio_monitor_rssi(ostime_t n, oslmic_radio_rssi_t *pRssi);
//================================================================================
#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 RX_RAMPUP
#define RX_RAMPUP (us2osticks(2000))
#endif
#ifndef TX_RAMPUP
// 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))
#define TX_RAMPUP (us2osticks(2000))
#endif
#ifndef OSTICKS_PER_SEC
@ -197,9 +188,6 @@ 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
@ -336,18 +324,6 @@ extern xref2u1_t AESaux;
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len);
#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_

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

723
src/lmic/radio.c Normal file → Executable file

File diff suppressed because it is too large Load Diff