Initial check-in

This commit is contained in:
Manuel Bleichenbacher 2018-07-15 22:11:11 +02:00
parent a0f9347cc5
commit ba33e35591
21 changed files with 5946 additions and 52 deletions

54
.gitignore vendored
View File

@ -1,52 +1,2 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
build/
sdkconfig

32
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,32 @@
{
"configurations": [
{
"name": "ESP32",
"includePath": [
"${XTENSA_TOOLCHAIN}/xtensa-esp32-elf/include",
"${XTENSA_TOOLCHAIN}/lib/gcc/xtensa-esp32-elf/5.2.0/include",
"${IDF_PATH}/components/driver/include",
"${IDF_PATH}/components/esp32/include",
"${IDF_PATH}/components/freertos/include",
"${IDF_PATH}/components/heap/include",
"${IDF_PATH}/components/log/include",
"${IDF_PATH}/components/lwip/include/lwip",
"${IDF_PATH}/components/lwip/include/lwip/port",
"${IDF_PATH}/components/soc/include",
"${IDF_PATH}/components/soc/esp32/include",
"${IDF_PATH}/components/tcpip_adapter/include",
"${workspaceRoot}/examples/send_msg/build/include",
"${workspaceRoot}/include",
"${workspaceRoot}/src"
],
"defines": [
"_DEBUG"
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}

35
Kconfig Normal file
View File

@ -0,0 +1,35 @@
menu "The Things Network"
choice TTN_LORA_FREQ
prompt "TTN LoRa Frequency"
default TTN_LORA_FREQ_NONE
help
LoRa frequency must match the geographic region the device is operated in.
Running it with the incorrect frequency most like violates the law.
config TTN_LORA_FREQ_NONE
bool "None"
config TTN_LORA_FREQ_EU_868
bool "868 MHz (Europe)"
config TTN_LORA_FREQ_US_915
bool "915 Mhz (North America)"
endchoice
choice TTN_RADIO_CHIP
prompt "TTN Radio Chip"
default TTN_RADIO_SX1276_77_78_79
help
The chip type used for LoRa radio.
config TTN_RADIO_SX1272_73
bool "SX1272/SX1273 (used in HopeRF RFM92 boards)"
config TTN_RADIO_SX1276_77_78_79
bool "SX1276/SX1277/SX1278/SX1279 (used in HopeRF RFM96 and ttgo LoRa boards)"
endchoice
endmenu

4
component.mk Executable file
View File

@ -0,0 +1,4 @@
CFLAGS += -Wno-error=maybe-uninitialized -Wno-error=unused-value
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := src

View File

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

View File

@ -0,0 +1 @@
../../../../ttn-esp32

View File

View File

@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (c) 2018 Manuel Bleichenbacher
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Sample program showing how to send a test message every 30 second.
*******************************************************************************/
#include "freertos/FreeRTOS.h"
#include "esp_event.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 two lines from bottom of your device's overview page in the TTN console
const char *appEui = "????????????????";
const char *appKey = "????????????????????????????????";
// Copy the below hex string from the "Device EUI" field on the same pag.
const char *devEui = "????????????????";;
static TheThingsNetwork ttn;
const unsigned TX_INTERVAL = 30;
static uint8_t msgData[] = "Hello, world";
void send_messages(void* pvParameter)
{
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
while(1) {
printf("Sending message...\n");
ttn_response_t res = ttn.sendBytes(msgData, sizeof(msgData) - 1);
if (res == TTN_SUCCESSFUL_TRANSMISSION)
printf("Message sent.\n");
else
printf("Message transmission failed.\n");
vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS);
}
}
extern "C" void app_main(void)
{
gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
// Initialize SPI bus
spi_bus_config_t spi_bus_config;
spi_bus_config.miso_io_num = 19;
spi_bus_config.mosi_io_num = 27;
spi_bus_config.sclk_io_num = 5;
spi_bus_config.quadwp_io_num = -1;
spi_bus_config.quadhd_io_num = -1;
spi_bus_config.max_transfer_sz = 0;
esp_err_t ret = spi_bus_initialize(HSPI_HOST, &spi_bus_config, 1);
assert(ret == ESP_OK);
ttn.configurePins(HSPI_HOST, 18, TTN_NOT_CONNECTED, 14, 26, 33);
ttn.provision(appEui, appKey, devEui);
ttn.join();
xTaskCreate(send_messages, "send_messages", 1024 * 4, (void* )0, 3, NULL);
}

133
include/TheThingsNetwork.h Normal file
View File

@ -0,0 +1,133 @@
/*******************************************************************************
* Copyright (c) 2018 Manuel Bleichenbacher
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This the hardware abstraction layer to run LMIC in on ESP32 using ESP-iDF.
*******************************************************************************/
#ifndef _THETHINGSNETWORK_H_
#define _THETHINGSNETWORK_H_
#include <stdint.h>
#include "driver/spi_master.h"
#define TTN_DEFAULT_SF 7
#define TTN_DEFAULT_FSB 2
/**
* @brief Constant for indicating that a pin is not connected
*/
#define TTN_NOT_CONNECTED 0xff
typedef uint8_t port_t;
/**
* @brief Response codes
*/
enum ttn_response_t
{
TTN_ERROR_SEND_COMMAND_FAILED = (-1),
TTN_ERROR_UNEXPECTED_RESPONSE = (-10),
TTN_SUCCESSFUL_TRANSMISSION = 1,
TTN_SUCCESSFUL_RECEIVE = 2
};
/**
* @brief TTN device
*
* The TheThingsNetwork class enables ESP32 devices with SX1272/73/76/77/78/79 LoRaWAN chips
* to communicate via The Things Network.
*
* Only one instance of this class must be created.
*/
class TheThingsNetwork
{
public:
/**
* @brief Construct a new The Things Network device
*
* @param sf The spreading factor. 7 to 10 for US915 frequency plan. 7 to 12 for other frequency plans. Defaults to 7.
* @param fsb Optional custom frequency subband. 1 to 8. Defaults to 2.
*/
TheThingsNetwork(uint8_t sf = TTN_DEFAULT_SF, uint8_t fsb = TTN_DEFAULT_FSB);
/**
* @brief Destroy the The Things Network device.
*/
~TheThingsNetwork();
/**
* @brief Reset the LoRaWAN radio.
*
* Does not clear provisioned keys.
*/
void reset();
/**
* @brief Configures the pins used to communicate with the LoRaWAN radio chip.
*
* The SPI bus must be first configured using spi_bus_initialize(). Then it is passed as the first parameter.
*
* @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)
* @param rxtx The GPIO pin number connected to the radio chip's RXTX pin (TTN_NOT_CONNECTED if not connected)
* @param rst The GPIO pin number connected to the radio chip's RST pin (TTN_NOT_CONNECTED if not connected)
* @param dio0 The GPIO pin number connected to the radio chip's DIO0 pin
* @param dio1 The GPIO pin number connected to the radio chip's DIO1 pin
*/
void configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1);
/**
* @brief Sets the information needed to activate the device via OTAA, without actually activating.
*
* Call join() without the first 2 arguments to activate.
*
* @param appEui Application EUI of the device (16 character string with hexadecimal data)
* @param appKey App Key of the device (32 character string with hexadecimal data)
* @param devEui Device EUI (16 character string with hexadecimal data) or NULL if already set
* @return true if the provisioning was successful
* @return false if the provisioning failed
*/
bool provision(const char *appEui, const char *appKey, const char *devEui = NULL);
/**
* @brief Activate the device via OTAA.
*
* @param appEui Application EUI of the device (16 character string with hexadecimal data)
* @param appKey App Key of the device (32 character string with hexadecimal data)
* @param devEui Device EUI (16 character string with hexadecimal data) or NULL if already set
* @param retries Number of times to retry after failed or unconfirmed join. Defaults to -1 which means infinite.
* @param retryDelay Delay in ms between attempts. Defaults to 10 seconds.
* @return true
* @return false
*/
bool join(const char *appEui, const char *appKey, const char *devEui = NULL, int8_t retries = -1, uint32_t retryDelay = 10000);
/**
* @brief Activate the device via OTAA.
*
* The app EUI and key must already have been provisioned.
*
* @param retries Number of times to retry after failed or unconfirmed join. Defaults to -1 which means infinite.
* @param retryDelay Delay in ms between attempts. Defaults to 10 seconds.
* @return true
* @return false
*/
bool join(int8_t retries = -1, uint32_t retryDelay = 10000);
ttn_response_t sendBytes(const uint8_t *payload, size_t length, port_t port = 1, bool confirm = false, uint8_t sf = 0);
private:
uint8_t spreadingFactor = TTN_DEFAULT_SF;
uint8_t frequencySubband = TTN_DEFAULT_FSB;
bool decodeKeys(const char *appEui, const char *appKey, const char *devEui);
};
#endif

238
src/TheThingsNetwork.cpp Normal file
View File

@ -0,0 +1,238 @@
/*******************************************************************************
* Copyright (c) 2018 Manuel Bleichenbacher
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This the hardware abstraction layer to run LMIC in on ESP32 using ESP-iDF.
*******************************************************************************/
#include "TheThingsNetwork.h"
#include "esp_log.h"
#include "oslmic.h"
#include "hal.h"
#include "hal_esp32.h"
#include "lmic.h"
static const char *TAG = "ttn";
static TheThingsNetwork* ttnInstance;
static uint8_t appEui[8];
static uint8_t appKey[16];
static uint8_t devEui[8];
static bool hexStringToBin(const char *hex, uint8_t *buf, int len);
static int hexTupleToByte(const char *hex);
static int hexDigitToVal(char ch);
static void swapByteOrder(uint8_t* buf, int len);
TheThingsNetwork::TheThingsNetwork(uint8_t sf, uint8_t fsb)
{
ttnInstance = this;
spreadingFactor = sf;
frequencySubband = fsb;
hal_initCriticalSection();
}
TheThingsNetwork::~TheThingsNetwork()
{
// nothing to do
}
void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1)
{
lmic_pins.spi_host = spi_host;
lmic_pins.nss = nss;
lmic_pins.rxtx = rxtx;
lmic_pins.rst = rst;
lmic_pins.dio0 = dio0;
lmic_pins.dio1 = dio1;
os_init();
reset();
hal_startBgTask();
}
void TheThingsNetwork::reset()
{
hal_enterCriticalSection();
LMIC_reset();
hal_leaveCriticalSection();
}
bool TheThingsNetwork::provision(const char *appEui, const char *appKey, const char* devEui)
{
return decodeKeys(appEui, appKey, devEui);
}
bool TheThingsNetwork::decodeKeys(const char *appEui, const char *appKey, const char* devEui)
{
if (strlen(appEui) != 16 || !hexStringToBin(appEui, ::appEui, 8))
{
ESP_LOGW(TAG, "Invalid application EUI: %s", appEui);
return false;
}
swapByteOrder(::appEui, 8);
if (strlen(appKey) != 32 || !hexStringToBin(appKey, ::appKey, 16))
{
ESP_LOGW(TAG, "Invalid application key: %s", appEui);
return false;
}
if (strlen(devEui) != 16 || !hexStringToBin(devEui, ::devEui, 8))
{
ESP_LOGW(TAG, "Invalid device EUI: %s", devEui);
return false;
}
swapByteOrder(::devEui, 8);
return true;
}
bool TheThingsNetwork::join(const char *appEui, const char *appKey, const char *devEui, int8_t retries, uint32_t retryDelay)
{
if (!decodeKeys(appEui, appKey, devEui))
return false;
return join(retries, retryDelay);
}
bool TheThingsNetwork::join(int8_t retries, uint32_t retryDelay)
{
hal_enterCriticalSection();
LMIC_startJoining();
hal_wakeUp();
hal_leaveCriticalSection();
return true;
}
ttn_response_t TheThingsNetwork::sendBytes(const uint8_t *payload, size_t length, port_t port, bool confirm, uint8_t sf)
{
hal_enterCriticalSection();
if (LMIC.opmode & OP_TXRXPEND)
{
hal_leaveCriticalSection();
return TTN_ERROR_SEND_COMMAND_FAILED;
}
LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm);
hal_wakeUp();
hal_leaveCriticalSection();
return TTN_SUCCESSFUL_TRANSMISSION;
}
// --- LMIC functions ---
#if CONFIG_LOG_DEFAULT_LEVEL >= 3
static const char *eventNames[] = {
NULL,
"EV_SCAN_TIMEOUT", "EV_BEACON_FOUND",
"EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING",
"EV_JOINED", "EV_RFU1", "EV_JOIN_FAILED", "EV_REJOIN_FAILED",
"EV_TXCOMPLETE", "EV_LOST_TSYNC", "EV_RESET",
"EV_RXCOMPLETE", "EV_LINK_DEAD", "EV_LINK_ALIVE"
};
#endif
// This EUI must be in little-endian format, so least-significant-byte first.
// When copying an EUI from ttnctl output, this means to reverse the bytes.
// For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 0x70.
// The order is swapped in TheThingsNetwork::provision().
void os_getArtEui (u1_t* buf)
{
memcpy(buf, appEui, 8);
}
// This should also be in little endian format, see above.
void os_getDevEui (u1_t* buf)
{
memcpy(buf, devEui, 8);
}
// This key should be in big endian format (or, since it is not really a number
// but a block of memory, endianness does not really apply). In practice, a key
// taken from ttnctl can be copied as-is.
void os_getDevKey (u1_t* buf)
{
memcpy(buf, appKey, 16);
}
void onEvent (ev_t ev) {
#if CONFIG_LOG_DEFAULT_LEVEL >= 3
ESP_LOGI(TAG, "%ld: event %s", os_getTime(), eventNames[ev]);
#endif
if (ev == EV_JOINED)
{
// Disable link check validation (automatically enabled
// during join, but not supported by TTN at this time).
LMIC_setLinkCheckMode(0);
} else if (ev == EV_TXCOMPLETE) {
if (LMIC.txrxFlags & TXRX_ACK)
printf("Received ack\n");
if (LMIC.dataLen) {
printf("Received %d bytes of payload\n", LMIC.dataLen);
}
}
}
// --- Helper functions ---
bool hexStringToBin(const char *hex, uint8_t *buf, int len)
{
const char* ptr = hex;
for (int i = 0; i < len; i++)
{
int val = hexTupleToByte(ptr);
if (val < 0)
return false;
buf[i] = val;
ptr += 2;
}
return true;
}
int hexTupleToByte(const char *hex)
{
int nibble1 = hexDigitToVal(hex[0]);
if (nibble1 < 0)
return -1;
int nibble2 = hexDigitToVal(hex[1]);
if (nibble2 < 0)
return -1;
return (nibble1 << 4) | nibble2;
}
int hexDigitToVal(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch + 10 - 'A';
if (ch >= 'a' && ch <= 'f')
return ch + 10 - 'a';
return -1;
}
void swapByteOrder(uint8_t* buf, int len)
{
uint8_t* p1 = buf;
uint8_t* p2 = buf + len - 1;
while (p1 < p2)
{
uint8_t t = *p1;
*p1 = *p2;
*p2 = t;
p1++;
p2--;
}
}

383
src/aes.c Executable file
View File

@ -0,0 +1,383 @@
/*
* Copyright (c) 2014-2016 IBM 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.
*/
#include "oslmic.h"
#define AES_MICSUB 0x30 // internal use only
static const u4_t AES_RCON[10] = {
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000
};
static const u1_t AES_S[256] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
};
static const u4_t AES_E1[256] = {
0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554,
0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A,
0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B,
0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B,
0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F,
0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F,
0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5,
0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F,
0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB,
0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497,
0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED,
0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A,
0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594,
0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3,
0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504,
0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D,
0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739,
0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395,
0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883,
0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76,
0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4,
0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B,
0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0,
0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818,
0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651,
0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85,
0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12,
0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9,
0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7,
0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A,
0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8,
0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A,
};
static const u4_t AES_E2[256] = {
0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5,
0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676,
0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0,
0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0,
0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC,
0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515,
0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A,
0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575,
0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0,
0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484,
0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B,
0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF,
0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585,
0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8,
0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5,
0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2,
0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717,
0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373,
0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888,
0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB,
0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C,
0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979,
0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9,
0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808,
0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6,
0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A,
0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E,
0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E,
0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494,
0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF,
0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868,
0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616,
};
static const u4_t AES_E3[256] = {
0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5,
0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76,
0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0,
0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0,
0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC,
0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15,
0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A,
0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75,
0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0,
0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384,
0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B,
0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF,
0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185,
0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8,
0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5,
0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2,
0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17,
0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673,
0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88,
0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB,
0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C,
0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279,
0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9,
0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008,
0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6,
0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A,
0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E,
0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E,
0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394,
0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF,
0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068,
0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16,
};
static const u4_t AES_E4[256] = {
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C,
};
#define msbf4_read(p) ((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3])
#define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v)
#define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) )
#define u1(v) ((u1_t)(v))
#define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \
r2 = ki[i+2]; \
r3 = ki[i+3]; \
r0 = ki[i]
#define AES_expr4(r1,r2,r3,r0,i) r1 ^= AES_E4[u1(i)]; \
r2 ^= AES_E3[u1(i>>8)]; \
r3 ^= AES_E2[u1(i>>16)]; \
r0 ^= AES_E1[ (i>>24)]
#define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \
a ^= (AES_S[ r0>>24 ]<<24); \
a ^= (AES_S[u1(r1>>16)]<<16); \
a ^= (AES_S[u1(r2>> 8)]<< 8); \
a ^= AES_S[u1(r3) ]
// global area for passing parameters (aux, key) and for storing round keys
u4_t AESAUX[16/sizeof(u4_t)];
u4_t AESKEY[11*16/sizeof(u4_t)];
// generate 1+10 roundkeys for encryption with 128-bit key
// read 128-bit key from AESKEY in MSBF, generate roundkey words in place
static void aesroundkeys () {
int i;
u4_t b;
for( i=0; i<4; i++) {
AESKEY[i] = swapmsbf(AESKEY[i]);
}
b = AESKEY[3];
for( ; i<44; i++ ) {
if( i%4==0 ) {
// b = SubWord(RotWord(b)) xor Rcon[i/4]
b = (AES_S[u1(b >> 16)] << 24) ^
(AES_S[u1(b >> 8)] << 16) ^
(AES_S[u1(b) ] << 8) ^
(AES_S[ b >> 24 ] ) ^
AES_RCON[(i-4)/4];
}
AESKEY[i] = b ^= AESKEY[i-4];
}
}
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
aesroundkeys();
if( mode & AES_MICNOAUX ) {
AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0;
} else {
AESAUX[0] = swapmsbf(AESAUX[0]);
AESAUX[1] = swapmsbf(AESAUX[1]);
AESAUX[2] = swapmsbf(AESAUX[2]);
AESAUX[3] = swapmsbf(AESAUX[3]);
}
while( (signed char)len > 0 ) {
u4_t a0, a1, a2, a3;
u4_t t0, t1, t2, t3;
u4_t *ki, *ke;
// load input block
if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block
a0 = AESAUX[0];
a1 = AESAUX[1];
a2 = AESAUX[2];
a3 = AESAUX[3];
}
else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block
a0 = a1 = a2 = a3 = 0; // load null block
mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2
} else
LOADDATA: { // load data block (partially)
for(t0=0; t0<16; t0++) {
t1 = (t1<<8) | ((t0<len) ? buf[t0] : (t0==len) ? 0x80 : 0x00);
if((t0&3)==3) {
a0 = a1;
a1 = a2;
a2 = a3;
a3 = t1;
}
}
if( mode & AES_MIC ) {
a0 ^= AESAUX[0];
a1 ^= AESAUX[1];
a2 ^= AESAUX[2];
a3 ^= AESAUX[3];
}
}
// perform AES encryption on block in a0-a3
ki = AESKEY;
ke = ki + 8*4;
a0 ^= ki[0];
a1 ^= ki[1];
a2 ^= ki[2];
a3 ^= ki[3];
do {
AES_key4 (t1,t2,t3,t0,4);
AES_expr4(t1,t2,t3,t0,a0);
AES_expr4(t2,t3,t0,t1,a1);
AES_expr4(t3,t0,t1,t2,a2);
AES_expr4(t0,t1,t2,t3,a3);
AES_key4 (a1,a2,a3,a0,8);
AES_expr4(a1,a2,a3,a0,t0);
AES_expr4(a2,a3,a0,a1,t1);
AES_expr4(a3,a0,a1,a2,t2);
AES_expr4(a0,a1,a2,a3,t3);
} while( (ki+=8) < ke );
AES_key4 (t1,t2,t3,t0,4);
AES_expr4(t1,t2,t3,t0,a0);
AES_expr4(t2,t3,t0,t1,a1);
AES_expr4(t3,t0,t1,t2,a2);
AES_expr4(t0,t1,t2,t3,a3);
AES_expr(a0,t0,t1,t2,t3,8);
AES_expr(a1,t1,t2,t3,t0,9);
AES_expr(a2,t2,t3,t0,t1,10);
AES_expr(a3,t3,t0,t1,t2,11);
// result of AES encryption in a0-a3
if( mode & AES_MIC ) {
if( (t1 = (mode & AES_MICSUB) >> 4) != 0 ) { // last block
do {
// compute CMAC subkey K1 and K2
t0 = a0 >> 31; // save MSB
a0 = (a0 << 1) | (a1 >> 31);
a1 = (a1 << 1) | (a2 >> 31);
a2 = (a2 << 1) | (a3 >> 31);
a3 = (a3 << 1);
if( t0 ) a3 ^= 0x87;
} while( --t1 );
AESAUX[0] ^= a0;
AESAUX[1] ^= a1;
AESAUX[2] ^= a2;
AESAUX[3] ^= a3;
mode &= ~AES_MICSUB;
goto LOADDATA;
} else {
// save cipher block as new iv
AESAUX[0] = a0;
AESAUX[1] = a1;
AESAUX[2] = a2;
AESAUX[3] = a3;
}
} else { // CIPHER
if( mode & AES_CTR ) { // xor block (partially)
t0 = (len > 16) ? 16: len;
for(t1=0; t1<t0; t1++) {
buf[t1] ^= (a0>>24);
a0 <<= 8;
if((t1&3)==3) {
a0 = a1;
a1 = a2;
a2 = a3;
}
}
// update counter
AESAUX[3]++;
} else { // ECB
// store block
msbf4_write(buf+0, a0);
msbf4_write(buf+4, a1);
msbf4_write(buf+8, a2);
msbf4_write(buf+12, a3);
}
}
// update block state
if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) {
buf += 16;
len -= 16;
}
mode |= AES_MICNOAUX;
}
return AESAUX[0];
}

46
src/config.h Normal file
View File

@ -0,0 +1,46 @@
/*******************************************************************************
* Copyright (c) 2018 Manuel Bleichenbacher
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This the hardware abstraction layer to run LMIC in on ESP32 using ESP-iDF.
*******************************************************************************/
#ifndef _lmic_config_h_
#define _lmic_config_h_
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(CONFIG_TTN_LORA_FREQ_EU_868)
#define CFG_eu868 1
#elif defined(CONFIG_TTN_LORA_FREQ_US_915)
#define CFG_us915 1
#else
#error TTN LoRa frequency must be configured
#endif
#if defined(CONFIG_TTN_RADIO_SX1272_73)
#define CFG_sx1272_radio 1
#elif defined(CONFIG_TTN_RADIO_SX1276_77_78_79)
#define CFG_sx1276_radio 1
#else
#error TTN LoRa radio chip must be configured
#endif
// 16 μs per tick
// LMIC requires ticks to be 15.5μs - 100 μs long
#define US_PER_OSTICK 16
#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK)
#ifdef __cplusplus
}
#endif
#endif // _lmic_config_h_

116
src/hal.h Executable file
View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2014-2016 IBM 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 _hal_hpp_
#define _hal_hpp_
#ifdef __cplusplus
extern "C" {
#endif
/*
* initialize hardware (IO, SPI, TIMER, IRQ).
*/
void hal_init (void);
/*
* drive radio NSS pin (0=low, 1=high).
*/
void hal_pin_nss (u1_t val);
/*
* drive radio RX/TX pins (0=rx, 1=tx).
*/
void hal_pin_rxtx (u1_t val);
/*
* control radio RST pin (0=low, 1=high, 2=floating)
*/
void hal_pin_rst (u1_t val);
/*
* perform SPI write transaction with radio
* - write the command byte 'cmd'
* - write 'len' bytes in 'buf'
*/
void hal_spi_write(u1_t cmd, const u1_t* buf, int len);
/*
* perform SPI read transaction with radio
* - write the command byte 'cmd'
* - read 'len' bytes into 'buf'
*/
void hal_spi_read(u1_t cmd, u1_t* buf, int len);
/*
* disable all CPU interrupts.
* - might be invoked nested
* - will be followed by matching call to hal_enableIRQs()
*/
void hal_disableIRQs (void);
/*
* enable CPU interrupts.
*/
void hal_enableIRQs (void);
/*
* put system and CPU in low-power mode, sleep until interrupt.
*/
void hal_sleep (void);
/*
* return 32-bit system time in ticks.
*/
u4_t hal_ticks (void);
/*
* busy-wait until specified timestamp (in ticks) is reached.
*/
void hal_waitUntil (u4_t time);
/*
* check and rewind timer for target time.
* - return 1 if target time is close
* - otherwise rewind timer for target time or full period and return 0
*/
u1_t hal_checkTimer (u4_t targettime);
/*
* perform fatal failure action.
* - called by assertions
* - action could be HALT or reboot
*/
void hal_failed (const char *file, u2_t line);
#ifdef __cplusplus
}
#endif
#endif // _hal_hpp_

409
src/hal_esp32.c Executable file
View File

@ -0,0 +1,409 @@
/*******************************************************************************
* Copyright (c) 2018 Manuel Bleichenbacher
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-iDF.
*******************************************************************************/
#include "lmic.h"
#include "hal_esp32.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "driver/timer.h"
#include "esp_log.h"
#define LMIC_UNUSED_PIN 0xff
static const char *TAG = "ttn_hal";
lmic_pinmap lmic_pins;
typedef enum {
DIO0,
DIO1,
DIO2,
TIMER,
WAKEUP
} event_t;
typedef struct {
ostime_t time;
event_t ev;
} queue_item_t;
// -----------------------------------------------------------------------------
// I/O
static QueueHandle_t dio_queue;
void IRAM_ATTR dio_irq_handler(void *arg)
{
uint64_t now;
timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &now);
event_t ev = (long)arg;
BaseType_t higher_prio_task_woken = pdFALSE;
queue_item_t item = {
.time = (ostime_t)now,
.ev = ev
};
xQueueSendFromISR(dio_queue, &item, &higher_prio_task_woken);
if (higher_prio_task_woken)
portYIELD_FROM_ISR();
}
static void hal_io_init()
{
ESP_LOGI(TAG, "Starting IO initialization");
// NSS and DIO0 and DIO1 are required
ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN);
ASSERT(lmic_pins.dio0 != LMIC_UNUSED_PIN);
ASSERT(lmic_pins.dio1 != LMIC_UNUSED_PIN);
gpio_pad_select_gpio(lmic_pins.nss);
gpio_set_level(lmic_pins.nss, 0);
gpio_set_direction(lmic_pins.nss, GPIO_MODE_OUTPUT);
if (lmic_pins.rxtx != LMIC_UNUSED_PIN)
{
gpio_pad_select_gpio(lmic_pins.rxtx);
gpio_set_level(lmic_pins.rxtx, 0);
gpio_set_direction(lmic_pins.rxtx, GPIO_MODE_OUTPUT);
}
if (lmic_pins.rst != LMIC_UNUSED_PIN)
{
gpio_pad_select_gpio(lmic_pins.rst);
gpio_set_level(lmic_pins.rst, 0);
gpio_set_direction(lmic_pins.rst, GPIO_MODE_OUTPUT);
}
dio_queue = xQueueCreate(12, sizeof(queue_item_t));
assert(dio_queue != NULL);
gpio_pad_select_gpio(lmic_pins.dio0);
gpio_set_direction(lmic_pins.dio0, GPIO_MODE_INPUT);
gpio_set_intr_type(lmic_pins.dio0, GPIO_INTR_POSEDGE);
gpio_isr_handler_add(lmic_pins.dio0, dio_irq_handler, (void *)0);
gpio_pad_select_gpio(lmic_pins.dio1);
gpio_set_direction(lmic_pins.dio1, GPIO_MODE_INPUT);
gpio_set_intr_type(lmic_pins.dio1, GPIO_INTR_POSEDGE);
gpio_isr_handler_add(lmic_pins.dio1, dio_irq_handler, (void *)1);
ESP_LOGI(TAG, "Finished IO initialization");
}
void hal_pin_rxtx(u1_t val)
{
if (lmic_pins.rxtx == LMIC_UNUSED_PIN)
return;
gpio_set_level(lmic_pins.rxtx, val);
}
void hal_pin_rst(u1_t val)
{
if (lmic_pins.rst == LMIC_UNUSED_PIN)
return;
if (val == 0 || val == 1)
{ // drive pin
gpio_set_level(lmic_pins.rst, val);
gpio_set_direction(lmic_pins.rst, GPIO_MODE_OUTPUT);
}
else
{ // keep pin floating
gpio_set_level(lmic_pins.rst, val);
gpio_set_direction(lmic_pins.rst, GPIO_MODE_INPUT);
}
}
// -----------------------------------------------------------------------------
// SPI
#define SPI_QUEUE_SIZE 4
#define SPI_NUM_TRX_SLOTS (SPI_QUEUE_SIZE + 1)
static spi_device_handle_t spi_handle;
static spi_transaction_t spi_trx_queue[SPI_NUM_TRX_SLOTS];
static int spi_trx_queue_head = 0;
static int spi_num_outstanding_trx = 0;
static spi_transaction_t* get_next_spi_trx_desc()
{
spi_transaction_t* trx = spi_trx_queue + spi_trx_queue_head;
memset(trx, 0, sizeof(spi_transaction_t));
return trx;
}
static void collect_spi_result()
{
int head = spi_trx_queue_head;
int tail = head - spi_num_outstanding_trx;
if (tail < 0)
tail += SPI_NUM_TRX_SLOTS;
spi_transaction_t* trx;
esp_err_t err = spi_device_get_trans_result(spi_handle, &trx, 100 / portTICK_PERIOD_MS);
assert(err == ESP_OK);
assert(trx == spi_trx_queue + tail);
spi_num_outstanding_trx--;
}
static void submit_spi_trx()
{
if (spi_num_outstanding_trx >= SPI_QUEUE_SIZE)
collect_spi_result();
int head = spi_trx_queue_head;
esp_err_t err = spi_device_queue_trans(spi_handle, spi_trx_queue + head, 100 / portTICK_PERIOD_MS);
assert(err == ESP_OK);
spi_num_outstanding_trx++;
head++;
if (head >= SPI_NUM_TRX_SLOTS)
head = 0;
spi_trx_queue_head = head;
}
static void hal_spi_init()
{
ESP_LOGI(TAG, "Starting SPI initialization");
esp_err_t ret;
// init device
spi_device_interface_config_t spi_device_intf_config = {
.mode = 0,
.clock_speed_hz = 10000000,
.command_bits = 0,
.address_bits = 8,
.spics_io_num = lmic_pins.nss,
.queue_size = SPI_QUEUE_SIZE
};
ret = spi_bus_add_device(lmic_pins.spi_host, &spi_device_intf_config, &spi_handle);
assert(ret == ESP_OK);
ESP_LOGI(TAG, "Finished SPI initialization");
}
void hal_spi_write(u1_t cmd, const u1_t *buf, int len)
{
spi_transaction_t* trx = get_next_spi_trx_desc();
trx->addr = cmd;
trx->length = 8 * len;
trx->tx_buffer = buf;
submit_spi_trx();
}
void hal_spi_read(u1_t cmd, u1_t *buf, int len)
{
memset(buf, 0, len);
spi_transaction_t* trx = get_next_spi_trx_desc();
trx->addr = cmd;
trx->length = 8 * len;
trx->rxlength = 8 * len;
trx->tx_buffer = buf;
trx->rx_buffer = buf;
submit_spi_trx();
while (spi_num_outstanding_trx > 0)
collect_spi_result();
}
// -----------------------------------------------------------------------------
// TIME
static uint64_t nextTimerEvent = 0xffffffff;
static void IRAM_ATTR timer_irq_handler(void *arg)
{
TIMERG0.int_clr_timers.t1 = 1;
BaseType_t higher_prio_task_woken = pdFALSE;
queue_item_t item = {
.time = (ostime_t)nextTimerEvent,
.ev = TIMER
};
xQueueSendFromISR(dio_queue, &item, &higher_prio_task_woken);
if (higher_prio_task_woken)
portYIELD_FROM_ISR();
}
typedef enum {
CHECK_IO,
WAIT_FOR_ANY_EVENT,
WAIT_FOR_TIMER
} WaitOption;
static bool hal_wait(WaitOption waitOption)
{
TickType_t ticksToWait = waitOption == CHECK_IO ? 0 : portMAX_DELAY;
while (true)
{
queue_item_t item;
if (xQueueReceive(dio_queue, &item, ticksToWait) == pdFALSE)
return false;
if (item.ev == WAKEUP)
{
return true;
// nothing to do; just wake up event
}
else if (item.ev == TIMER) {
ostime_t t = (ostime_t)nextTimerEvent;
if (item.time == t)
{
nextTimerEvent = 0xffffffff;
if (waitOption != CHECK_IO)
return true;
}
}
else
{
hal_enterCriticalSection();
radio_irq_handler(item.ev, item.time);
hal_leaveCriticalSection();
if (waitOption != WAIT_FOR_TIMER)
return true;
}
}
}
static void hal_time_init()
{
ESP_LOGI(TAG, "Starting initialisation of timer");
timer_group_t timer_group = TIMER_GROUP_0;
timer_idx_t timer_idx = TIMER_1;
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
};
timer_init(timer_group, timer_idx, &config);
timer_set_counter_value(timer_group, timer_idx, 0x0);
timer_isr_register(timer_group, timer_idx, timer_irq_handler, NULL, ESP_INTR_FLAG_IRAM, NULL);
timer_start(timer_group, timer_idx);
ESP_LOGI(TAG, "Finished initalisation of timer");
}
u4_t hal_ticks()
{
uint64_t val;
timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &val);
return (u4_t)val;
}
void hal_waitUntil(u4_t time)
{
nextTimerEvent = time;
timer_set_alarm(TIMER_GROUP_0, TIMER_1, TIMER_ALARM_DIS);
timer_set_alarm_value(TIMER_GROUP_0, TIMER_1, nextTimerEvent);
timer_set_alarm(TIMER_GROUP_0, TIMER_1, TIMER_ALARM_EN);
hal_wait(WAIT_FOR_TIMER);
}
void hal_wakeUp() {
queue_item_t item = {
.ev = WAKEUP
};
xQueueSend(dio_queue, &item, 0);
}
// check and rewind for target time
u1_t hal_checkTimer(u4_t time)
{
uint64_t now;
timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &now);
if (time <= now)
return 1;
if (time - now < 5)
return 1;
nextTimerEvent = time;
return 0;
}
// -----------------------------------------------------------------------------
// IRQ
void hal_disableIRQs()
{
// nothing to do as interrupt handlers post message to queue
// and don't access any shared data structures
}
void hal_enableIRQs()
{
// nothing to do as interrupt handlers post message to queue
// and don't access any shared data structures
}
void hal_sleep()
{
if (hal_wait(CHECK_IO))
return;
timer_set_alarm(TIMER_GROUP_0, TIMER_1, TIMER_ALARM_DIS);
timer_set_alarm_value(TIMER_GROUP_0, TIMER_1, nextTimerEvent);
timer_set_alarm(TIMER_GROUP_0, TIMER_1, TIMER_ALARM_EN);
hal_wait(WAIT_FOR_ANY_EVENT);
}
// -----------------------------------------------------------------------------
// Synchronization between application code and background task
static SemaphoreHandle_t mutex;
void hal_initCriticalSection()
{
mutex = xSemaphoreCreateRecursiveMutex();
}
void hal_enterCriticalSection()
{
xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
}
void hal_leaveCriticalSection()
{
xSemaphoreGiveRecursive(mutex);
}
// -----------------------------------------------------------------------------
static void hal_bgTask(void* pvParameter) {
os_runloop();
}
void hal_init()
{
// configure radio I/O and interrupt handler
hal_io_init();
// configure radio SPI
hal_spi_init();
// configure timer and interrupt handler
hal_time_init();
}
void hal_startBgTask() {
xTaskCreate(hal_bgTask, "ttn_lora_task", 1024 * 4, (void* )0, 10, NULL);
}
void hal_failed(const char *file, u2_t line)
{
ESP_LOGE(TAG, "%s:%d", file, line);
assert(0);
}

44
src/hal_esp32.h Normal file
View File

@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2018 Manuel Bleichenbacher
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Hardware abstraction layer to run LMIC on a ESP32 using ESP-iDF.
*******************************************************************************/
#ifndef _hal_esp32_h_
#define _hal_esp32_h_
#include <stdint.h>
#include "driver/spi_master.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct lmic_pinmap {
spi_host_device_t spi_host;
uint8_t nss;
uint8_t rxtx;
uint8_t rst;
uint8_t dio0;
uint8_t dio1;
} lmic_pinmap;
extern lmic_pinmap lmic_pins;
void hal_startBgTask();
void hal_wakeUp();
void hal_initCriticalSection();
void hal_enterCriticalSection();
void hal_leaveCriticalSection();
#ifdef __cplusplus
}
#endif
#endif // _hal_esp32_h_

2400
src/lmic.c Executable file

File diff suppressed because it is too large Load Diff

340
src/lmic.h Executable file
View File

@ -0,0 +1,340 @@
/*
* Copyright (c) 2014-2016 IBM 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.
*/
//! @file
//! @brief LMIC API
#ifndef _lmic_h_
#define _lmic_h_
#include "config.h"
#include "oslmic.h"
#include "lorabase.h"
#ifdef __cplusplus
extern "C" {
#endif
// LMIC version
#define LMIC_VERSION_MAJOR 1
#define LMIC_VERSION_MINOR 6
#define LMIC_VERSION_BUILD 1468577746
enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length
enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames
enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests
enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link
LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead
LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate
LINK_CHECK_OFF =-128 }; // link check disabled
enum { TIME_RESYNC = 6*128 }; // secs
enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon
enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon
enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time
enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send
#if defined(CFG_eu868) // EU868 spectrum ====================================================
enum { MAX_CHANNELS = 16 }; //!< Max supported channels
enum { MAX_BANDS = 4 };
enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels
//! \internal
struct band_t {
u2_t txcap; // duty cycle limitation: 1/txcap
s1_t txpow; // maximum TX power
u1_t lastchnl; // last used channel
ostime_t avail; // channel is blocked until this time
};
TYPEDEF_xref2band_t; //!< \internal
#elif defined(CFG_us915) // US915 spectrum =================================================
enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable
enum { MAX_TXPOW_125kHz = 30 };
#endif // ==========================================================================
// Keep in sync with evdefs.hpp::drChange
enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
enum { KEEP_TXPOW = -128 };
#if !defined(DISABLE_PING)
//! \internal
struct rxsched_t {
u1_t dr;
u1_t intvExp; // 0..7
u1_t slot; // runs from 0 to 128
u1_t rxsyms;
ostime_t rxbase;
ostime_t rxtime; // start of next spot
u4_t freq;
};
TYPEDEF_xref2rxsched_t; //!< \internal
#endif // !DISABLE_PING
#if !defined(DISABLE_BEACONS)
//! Parsing and tracking states of beacons.
enum { BCN_NONE = 0x00, //!< No beacon received
BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous)
BCN_FULL = 0x02, //!< Full beacon decoded
BCN_NODRIFT = 0x04, //!< No drift value measured yet
BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet
//! Information about the last and previous beacons.
struct bcninfo_t {
ostime_t txtime; //!< Time when the beacon was sent
s1_t rssi; //!< Adjusted RSSI value of last received beacon
s1_t snr; //!< Scaled SNR value of last received beacon
u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values.
u4_t time; //!< GPS time in seconds of last beacon (received or surrogate)
//
u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set)
s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set)
s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set)
};
#endif // !DISABLE_BEACONS
// purpose of receive window - lmic_t.rxState
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
// Netid values / lmic_t.netid
enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
// MAC operation modes (lmic_t.opmode).
enum { OP_NONE = 0x0000,
OP_SCAN = 0x0001, // radio scan to find a beacon
OP_TRACK = 0x0002, // track my networks beacon (netid)
OP_JOINING = 0x0004, // device joining in progress (blocks other activities)
OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData)
OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data
OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST
OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything
OP_TXRXPEND = 0x0080, // TX/RX transaction pending
OP_RNDTX = 0x0100, // prevent TX lining up after a beacon
OP_PINGINI = 0x0200, // pingable is initialized and scheduling active
OP_PINGABLE = 0x0400, // we're pingable
OP_NEXTCHNL = 0x0800, // find a new channel
OP_LINKDEAD = 0x1000, // link was reported as dead
OP_TESTMODE = 0x2000, // developer test mode
};
// TX-RX transaction flags - report back to user
enum { TXRX_ACK = 0x80, // confirmed UP frame was acked
TXRX_NACK = 0x40, // confirmed UP frame was not acked
TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port
TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
TXRX_DNW1 = 0x01, // received in 1st DN slot
TXRX_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,
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 };
typedef enum _ev_t ev_t;
enum {
// This value represents 100% error in LMIC.clockError
MAX_CLOCK_ERROR = 65536,
};
struct lmic_t {
// Radio settings TX/RX (also accessed by HAL)
ostime_t txend;
ostime_t rxtime;
u4_t freq;
s1_t rssi;
s1_t snr;
rps_t rps;
u1_t rxsyms;
u1_t dndr;
s1_t txpow; // dBm
osjob_t osjob;
// Channel scheduling
#if defined(CFG_eu868)
band_t bands[MAX_BANDS];
u4_t channelFreq[MAX_CHANNELS];
u2_t channelDrMap[MAX_CHANNELS];
u2_t channelMap;
#elif defined(CFG_us915)
u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater)
u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto
u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits
u2_t chRnd; // channel randomizer
#endif
u1_t txChnl; // channel for next TX
u1_t globalDutyRate; // max rate: 1/2^k
ostime_t globalDutyAvail; // time device can send again
u4_t netid; // current network id (~0 - none)
u2_t opmode;
u1_t upRepeat; // configured up repeat
s1_t adrTxPow; // ADR adjusted TX power
u1_t datarate; // current data rate
u1_t errcr; // error coding rate (used for TX only)
u1_t rejoinCnt; // adjustment for rejoin datarate
#if !defined(DISABLE_BEACONS)
s2_t drift; // last measured drift
s2_t lastDriftDiff;
s2_t maxDriftDiff;
#endif
u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX
// represents +/-100% error
u1_t pendTxPort;
u1_t pendTxConf; // confirmed data
u1_t pendTxLen; // +0x80 = confirmed
u1_t pendTxData[MAX_LEN_PAYLOAD];
u2_t devNonce; // last generated nonce
u1_t nwkKey[16]; // network session key
u1_t artKey[16]; // application router session key
devaddr_t devaddr;
u4_t seqnoDn; // device level down stream seqno
u4_t seqnoUp;
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
s1_t adrAckReq; // counter until we reset data rate (0=off)
u1_t 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
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
// 2nd RX window (after up stream)
u1_t dn2Dr;
u4_t dn2Freq;
#if !defined(DISABLE_MCMD_DN2P_SET)
u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs
#endif
// Class B state
#if !defined(DISABLE_BEACONS)
u1_t missedBcns; // unable to track last N beacons
u1_t bcninfoTries; // how often to try (scan mode only)
#endif
#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING)
u1_t pingSetAns; // answer set cmd and ACK bits
#endif
#if !defined(DISABLE_PING)
rxsched_t ping; // pingable setup
#endif
// Public part of MAC state
u1_t txCnt;
u1_t txrxFlags; // transaction flags (TX-RX combo)
u1_t dataBeg; // 0 or start of data (dataBeg-1 is port)
u1_t dataLen; // 0 no data or zero length data, >0 byte count of data
u1_t frame[MAX_LEN_FRAME];
#if !defined(DISABLE_BEACONS)
u1_t bcnChnl;
u1_t bcnRxsyms; //
ostime_t bcnRxtime;
bcninfo_t bcninfo; // Last received beacon info
#endif
u1_t noRXIQinversion;
};
//! \var struct lmic_t LMIC
//! The state of LMIC MAC layer is encapsulated in this variable.
DECLARE_LMIC; //!< \internal
//! Construct a bit map of allowed datarates from drlo to drhi (both included).
#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
#if defined(CFG_eu868)
enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 };
bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
#endif
bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
void LMIC_disableChannel (u1_t channel);
#if defined(CFG_us915)
void LMIC_enableChannel (u1_t channel);
void LMIC_enableSubBand (u1_t band);
void LMIC_disableSubBand (u1_t band);
void LMIC_selectSubBand (u1_t band);
#endif
void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow
void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off)
#if !defined(DISABLE_JOIN)
bit_t LMIC_startJoining (void);
#endif
void LMIC_shutdown (void);
void LMIC_init (void);
void LMIC_start (void);
void LMIC_reset (void);
void LMIC_clrTxData (void);
void LMIC_setTxData (void);
int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
void LMIC_sendAlive (void);
#if !defined(DISABLE_BEACONS)
bit_t LMIC_enableTracking (u1_t tryBcnInfo);
void LMIC_disableTracking (void);
#endif
#if !defined(DISABLE_PING)
void LMIC_stopPingable (void);
void LMIC_setPingable (u1_t intvExp);
#endif
#if !defined(DISABLE_JOIN)
void LMIC_tryRejoin (void);
#endif
void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
void LMIC_setLinkCheckMode (bit_t enabled);
void LMIC_setClockError(u2_t error);
// Declare onEvent() function, to make sure any definition will have the
// C conventions, even when in a C++ file.
DECL_ON_LMIC_EVENT;
// Special APIs - for development or testing
// !!!See implementation for caveats!!!
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _lmic_h_

407
src/lorabase.h Executable file
View File

@ -0,0 +1,407 @@
/*
* Copyright (c) 2014-2016 IBM 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_h_
#define _lorabase_h_
#ifdef __cplusplus
extern "C" {
#endif
// ================================================================================
// BEG: Keep in sync with lorabase.hpp
//
enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 };
enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu };
enum _bw_t { BW125=0, BW250, BW500, BWrfu };
typedef u1_t cr_t;
typedef u1_t sf_t;
typedef u1_t bw_t;
typedef u1_t dr_t;
// Radio parameter set (encodes SF/BW/CR/IH/NOCRC)
typedef u2_t rps_t;
TYPEDEF_xref2rps_t;
enum { ILLEGAL_RPS = 0xFF };
enum { DR_PAGE_EU868 = 0x00 };
enum { DR_PAGE_US915 = 0x10 };
// Global maximum frame length
enum { STD_PREAMBLE_LEN = 8 };
enum { MAX_LEN_FRAME = 64 };
enum { LEN_DEVNONCE = 2 };
enum { LEN_ARTNONCE = 3 };
enum { LEN_NETID = 3 };
enum { DELAY_JACC1 = 5 }; // in secs
enum { DELAY_DNW1 = 1 }; // in secs down window #1
enum { DELAY_EXTDNW2 = 1 }; // in secs
enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs
enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1
enum { BCN_INTV_exp = 7 };
enum { BCN_INTV_sec = 1<<BCN_INTV_exp };
enum { BCN_INTV_ms = BCN_INTV_sec*1000L };
enum { BCN_INTV_us = BCN_INTV_ms*1000L };
enum { BCN_RESERVE_ms = 2120 }; // space reserved for beacon and NWK management
enum { BCN_GUARD_ms = 3000 }; // end of beacon period to prevent interference with beacon
enum { BCN_SLOT_SPAN_ms = 30 }; // 2^12 reception slots a this span
enum { BCN_WINDOW_ms = BCN_INTV_ms-(int)BCN_GUARD_ms-(int)BCN_RESERVE_ms };
enum { BCN_RESERVE_us = 2120000 };
enum { BCN_GUARD_us = 3000000 };
enum { BCN_SLOT_SPAN_us = 30000 };
#if defined(CFG_eu868) // ==============================================
enum _dr_eu868_t { DR_SF12=0, DR_SF11, DR_SF10, DR_SF9, DR_SF8, DR_SF7, DR_SF7B, DR_FSK, DR_NONE };
enum { DR_DFLTMIN = DR_SF7 };
enum { DR_PAGE = DR_PAGE_EU868 };
// Default frequency plan for EU 868MHz ISM band
// Bands:
// g1 : 1% 14dBm
// g2 : 0.1% 14dBm
// g3 : 10% 27dBm
// freq band datarates
enum { EU868_F1 = 868100000, // g1 SF7-12
EU868_F2 = 868300000, // g1 SF7-12 FSK SF7/250
EU868_F3 = 868500000, // g1 SF7-12
EU868_F4 = 868850000, // g2 SF7-12
EU868_F5 = 869050000, // g2 SF7-12
EU868_F6 = 869525000, // g3 SF7-12
EU868_J4 = 864100000, // g2 SF7-12 used during join
EU868_J5 = 864300000, // g2 SF7-12 ditto
EU868_J6 = 864500000, // g2 SF7-12 ditto
};
enum { EU868_FREQ_MIN = 863000000,
EU868_FREQ_MAX = 870000000 };
enum { CHNL_PING = 5 };
enum { FREQ_PING = EU868_F6 }; // default ping freq
enum { DR_PING = DR_SF9 }; // default ping DR
enum { CHNL_DNW2 = 5 };
enum { FREQ_DNW2 = EU868_F6 };
enum { DR_DNW2 = DR_SF12 };
enum { CHNL_BCN = 5 };
enum { FREQ_BCN = EU868_F6 };
enum { DR_BCN = DR_SF9 };
enum { AIRTIME_BCN = 144384 }; // micros
enum {
// Beacon frame format EU SF9
OFF_BCN_NETID = 0,
OFF_BCN_TIME = 3,
OFF_BCN_CRC1 = 7,
OFF_BCN_INFO = 8,
OFF_BCN_LAT = 9,
OFF_BCN_LON = 12,
OFF_BCN_CRC2 = 15,
LEN_BCN = 17
};
#elif defined(CFG_us915) // =========================================
enum _dr_us915_t { DR_SF10=0, DR_SF9, DR_SF8, DR_SF7, DR_SF8C, DR_NONE,
// Devices behind a router:
DR_SF12CR=8, DR_SF11CR, DR_SF10CR, DR_SF9CR, DR_SF8CR, DR_SF7CR };
enum { DR_DFLTMIN = DR_SF8C };
enum { DR_PAGE = DR_PAGE_US915 };
// Default frequency plan for US 915MHz
enum { US915_125kHz_UPFBASE = 902300000,
US915_125kHz_UPFSTEP = 200000,
US915_500kHz_UPFBASE = 903000000,
US915_500kHz_UPFSTEP = 1600000,
US915_500kHz_DNFBASE = 923300000,
US915_500kHz_DNFSTEP = 600000
};
enum { US915_FREQ_MIN = 902000000,
US915_FREQ_MAX = 928000000 };
enum { CHNL_PING = 0 }; // used only for default init of state (follows beacon - rotating)
enum { FREQ_PING = US915_500kHz_DNFBASE + CHNL_PING*US915_500kHz_DNFSTEP }; // default ping freq
enum { DR_PING = DR_SF10CR }; // default ping DR
enum { CHNL_DNW2 = 0 };
enum { FREQ_DNW2 = US915_500kHz_DNFBASE + CHNL_DNW2*US915_500kHz_DNFSTEP };
enum { DR_DNW2 = DR_SF12CR };
enum { CHNL_BCN = 0 }; // used only for default init of state (rotating beacon scheme)
enum { DR_BCN = DR_SF10CR };
enum { AIRTIME_BCN = 72192 }; // micros
enum {
// Beacon frame format US SF10
OFF_BCN_NETID = 0,
OFF_BCN_TIME = 3,
OFF_BCN_CRC1 = 7,
OFF_BCN_INFO = 9,
OFF_BCN_LAT = 10,
OFF_BCN_LON = 13,
OFF_BCN_RFU1 = 16,
OFF_BCN_CRC2 = 17,
LEN_BCN = 19
};
#endif // ===================================================
enum {
// Join Request frame format
OFF_JR_HDR = 0,
OFF_JR_ARTEUI = 1,
OFF_JR_DEVEUI = 9,
OFF_JR_DEVNONCE = 17,
OFF_JR_MIC = 19,
LEN_JR = 23
};
enum {
// Join Accept frame format
OFF_JA_HDR = 0,
OFF_JA_ARTNONCE = 1,
OFF_JA_NETID = 4,
OFF_JA_DEVADDR = 7,
OFF_JA_RFU = 11,
OFF_JA_DLSET = 11,
OFF_JA_RXDLY = 12,
OFF_CFLIST = 13,
LEN_JA = 17,
LEN_JAEXT = 17+16
};
enum {
// Data frame format
OFF_DAT_HDR = 0,
OFF_DAT_ADDR = 1,
OFF_DAT_FCT = 5,
OFF_DAT_SEQNO = 6,
OFF_DAT_OPTS = 8,
};
enum { MAX_LEN_PAYLOAD = MAX_LEN_FRAME-(int)OFF_DAT_OPTS-4 };
enum {
// Bitfields in frame format octet
HDR_FTYPE = 0xE0,
HDR_RFU = 0x1C,
HDR_MAJOR = 0x03
};
enum { HDR_FTYPE_DNFLAG = 0x20 }; // flags DN frame except for HDR_FTYPE_PROP
enum {
// Values of frame type bit field
HDR_FTYPE_JREQ = 0x00,
HDR_FTYPE_JACC = 0x20,
HDR_FTYPE_DAUP = 0x40, // data (unconfirmed) up
HDR_FTYPE_DADN = 0x60, // data (unconfirmed) dn
HDR_FTYPE_DCUP = 0x80, // data confirmed up
HDR_FTYPE_DCDN = 0xA0, // data confirmed dn
HDR_FTYPE_REJOIN = 0xC0, // rejoin for roaming
HDR_FTYPE_PROP = 0xE0
};
enum {
HDR_MAJOR_V1 = 0x00,
};
enum {
// Bitfields in frame control octet
FCT_ADREN = 0x80,
FCT_ADRARQ = 0x40,
FCT_ACK = 0x20,
FCT_MORE = 0x10, // also in DN direction: Class B indicator
FCT_OPTLEN = 0x0F,
};
enum {
// In UP direction: signals class B enabled
FCT_CLASSB = FCT_MORE
};
enum {
NWKID_MASK = (int)0xFE000000,
NWKID_BITS = 7
};
// MAC uplink commands downwlink too
enum {
// Class A
MCMD_LCHK_REQ = 0x02, // - link check request : -
MCMD_LADR_ANS = 0x03, // - link ADR answer : u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK
MCMD_DCAP_ANS = 0x04, // - duty cycle answer : -
MCMD_DN2P_ANS = 0x05, // - 2nd DN slot status : u1:7-2:RFU 1/0:datarate/channel ack
MCMD_DEVS_ANS = 0x06, // - device status ans : u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31)
MCMD_SNCH_ANS = 0x07, // - set new channel : u1: 7-2=RFU, 1/0:DR/freq ACK
// Class B
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate
MCMD_PING_ANS = 0x11, // - ack ping freq : u1: 7-1:RFU, 0:freq ok
MCMD_BCNI_REQ = 0x12, // - next beacon start : -
};
// MAC downlink commands
enum {
// Class A
MCMD_LCHK_ANS = 0x02, // link check answer : u1:margin 0-254,255=unknown margin / u1:gwcnt
MCMD_LADR_REQ = 0x03, // link ADR request : u1:DR/TXPow, u2:chmask, u1:chpage/repeat
MCMD_DCAP_REQ = 0x04, // duty cycle cap : u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k
MCMD_DN2P_SET = 0x05, // 2nd DN window param: u1:7-4:RFU/3-0:datarate, u3:freq
MCMD_DEVS_REQ = 0x06, // device status req : -
MCMD_SNCH_REQ = 0x07, // set new channel : u1:chidx, u3:freq, u1:DRrange
// Class B
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel
};
enum {
MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis
};
enum {
MCMD_LADR_ANS_RFU = 0xF8, // RFU bits
MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level
MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_LADR_ANS_CHACK = 0x01, // 0=unknown channel enabled
};
enum {
MCMD_DN2P_ANS_RFU = 0xFC, // RFU bits
MCMD_DN2P_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_DN2P_ANS_CHACK = 0x01, // 0=unknown channel enabled
};
enum {
MCMD_SNCH_ANS_RFU = 0xFC, // RFU bits
MCMD_SNCH_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_SNCH_ANS_FQACK = 0x01, // 0=rejected channel frequency
};
enum {
MCMD_PING_ANS_RFU = 0xFE,
MCMD_PING_ANS_FQACK = 0x01
};
enum {
MCMD_DEVS_EXT_POWER = 0x00, // external power supply
MCMD_DEVS_BATT_MIN = 0x01, // min battery value
MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
};
// Bit fields byte#3 of MCMD_LADR_REQ payload
enum {
MCMD_LADR_CHP_125ON = 0x60, // special channel page enable, bits applied to 64..71
MCMD_LADR_CHP_125OFF = 0x70, // ditto
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_LADR_REQ payload
enum {
MCMD_LADR_DR_MASK = 0xF0,
MCMD_LADR_POW_MASK = 0x0F,
MCMD_LADR_DR_SHIFT = 4,
MCMD_LADR_POW_SHIFT = 0,
#if defined(CFG_eu868)
MCMD_LADR_SF12 = DR_SF12<<4,
MCMD_LADR_SF11 = DR_SF11<<4,
MCMD_LADR_SF10 = DR_SF10<<4,
MCMD_LADR_SF9 = DR_SF9 <<4,
MCMD_LADR_SF8 = DR_SF8 <<4,
MCMD_LADR_SF7 = DR_SF7 <<4,
MCMD_LADR_SF7B = DR_SF7B<<4,
MCMD_LADR_FSK = DR_FSK <<4,
MCMD_LADR_20dBm = 0,
MCMD_LADR_14dBm = 1,
MCMD_LADR_11dBm = 2,
MCMD_LADR_8dBm = 3,
MCMD_LADR_5dBm = 4,
MCMD_LADR_2dBm = 5,
#elif defined(CFG_us915)
MCMD_LADR_SF10 = DR_SF10<<4,
MCMD_LADR_SF9 = DR_SF9 <<4,
MCMD_LADR_SF8 = DR_SF8 <<4,
MCMD_LADR_SF7 = DR_SF7 <<4,
MCMD_LADR_SF8C = DR_SF8C<<4,
MCMD_LADR_SF12CR = DR_SF12CR<<4,
MCMD_LADR_SF11CR = DR_SF11CR<<4,
MCMD_LADR_SF10CR = DR_SF10CR<<4,
MCMD_LADR_SF9CR = DR_SF9CR<<4,
MCMD_LADR_SF8CR = DR_SF8CR<<4,
MCMD_LADR_SF7CR = DR_SF7CR<<4,
MCMD_LADR_30dBm = 0,
MCMD_LADR_28dBm = 1,
MCMD_LADR_26dBm = 2,
MCMD_LADR_24dBm = 3,
MCMD_LADR_22dBm = 4,
MCMD_LADR_20dBm = 5,
MCMD_LADR_18dBm = 6,
MCMD_LADR_16dBm = 7,
MCMD_LADR_14dBm = 8,
MCMD_LADR_12dBm = 9,
MCMD_LADR_10dBm = 10
#endif
};
// Device address
typedef u4_t devaddr_t;
// RX quality (device)
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); }
inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); }
inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); }
inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8);
}
#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
// Two frames with params r1/r2 would interfere on air: same SFx + BWx
inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
extern const u1_t _DR2RPS_CRC[];
inline rps_t updr2rps (dr_t dr) { return (rps_t)_DR2RPS_CRC[dr+1]; }
inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
inline dr_t incDR (dr_t dr) { return _DR2RPS_CRC[dr+2]==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
inline dr_t decDR (dr_t dr) { return _DR2RPS_CRC[dr ]==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
inline dr_t assertDR (dr_t dr) { return _DR2RPS_CRC[dr+1]==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR
inline bit_t validDR (dr_t dr) { return _DR2RPS_CRC[dr+1]!=ILLEGAL_RPS; } // in range
inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
//
// BEG: Keep in sync with lorabase.hpp
// ================================================================================
// Convert between dBm values and power codes (MCMD_LADR_XdBm)
s1_t pow2dBm (u1_t mcmd_ladr_p1);
// Calculate airtime
ostime_t calcAirTime (rps_t rps, u1_t plen);
// Sensitivity at given SF/BW
int getSensitivity (rps_t rps);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _lorabase_h_

151
src/oslmic.c Executable file
View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2014-2016 IBM 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.
*/
#include "lmic.h"
#include "hal_esp32.h"
#include <stdbool.h>
// RUNTIME STATE
static struct {
osjob_t* scheduledjobs;
osjob_t* runnablejobs;
} OS;
void os_init () {
memset(&OS, 0x00, sizeof(OS));
hal_init();
radio_init();
LMIC_init();
}
ostime_t os_getTime () {
return hal_ticks();
}
static u1_t unlinkjob (osjob_t** pnext, osjob_t* job) {
for( ; *pnext; pnext = &((*pnext)->next)) {
if(*pnext == job) { // unlink
*pnext = job->next;
return 1;
}
}
return 0;
}
// clear scheduled job
void os_clearCallback (osjob_t* job) {
hal_disableIRQs();
#if LMIC_DEBUG_LEVEL > 1
u1_t res =
#endif
unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job);
hal_enableIRQs();
#if LMIC_DEBUG_LEVEL > 1
if (res)
lmic_printf("%lu: Cleared job %p\n", os_getTime(), job);
#endif
}
// schedule immediately runnable job
void os_setCallback (osjob_t* job, osjobcb_t cb) {
osjob_t** pnext;
hal_disableIRQs();
// remove if job was already queued
os_clearCallback(job);
// fill-in job
job->func = cb;
job->next = NULL;
// add to end of run queue
for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next));
*pnext = job;
hal_enableIRQs();
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: Scheduled job %p, cb %p ASAP\n", os_getTime(), job, cb);
#endif
}
// schedule timed job
void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) {
osjob_t** pnext;
hal_disableIRQs();
// remove if job was already queued
os_clearCallback(job);
// fill-in job
job->deadline = time;
job->func = cb;
job->next = NULL;
// insert into schedule
for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
// enqueue before next element and stop
job->next = *pnext;
break;
}
}
*pnext = job;
hal_enableIRQs();
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: Scheduled job %p, cb %p at %lu\n", os_getTime(), job, cb, time);
#endif
}
// execute jobs from timer and from run queue
void os_runloop () {
while(1) {
os_runloop_once();
}
}
void os_runloop_once() {
#if LMIC_DEBUG_LEVEL > 1
bool has_deadline = false;
#endif
osjob_t* j = NULL;
hal_enterCriticalSection();
// check for runnable jobs
if(OS.runnablejobs) {
j = OS.runnablejobs;
OS.runnablejobs = j->next;
} else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
j = OS.scheduledjobs;
OS.scheduledjobs = j->next;
#if LMIC_DEBUG_LEVEL > 1
has_deadline = true;
#endif
}
if(j) { // run job callback
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: Running job %p, cb %p, deadline %lu\n", os_getTime(), j, j->func, has_deadline ? j->deadline : 0);
#endif
j->func(j);
hal_leaveCriticalSection();
} else { // nothing pending
hal_leaveCriticalSection();
hal_sleep(); // wake by irq (timer already restarted)
}
}

242
src/oslmic.h Executable file
View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2014-2016 IBM 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.
*/
//! \file
#ifndef _oslmic_h_
#define _oslmic_h_
// Dependencies required for the LoRa MAC in C to run.
// These settings can be adapted to the underlying system.
// You should not, however, change the lmic.[hc]
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
//================================================================================
//================================================================================
// Target platform as C library
typedef uint8_t bit_t;
typedef uint8_t u1_t;
typedef int8_t s1_t;
typedef uint16_t u2_t;
typedef int16_t s2_t;
typedef unsigned long u4_t;
typedef long s4_t;
typedef unsigned int uint;
typedef const char* str_t;
#include <string.h>
#include "hal.h"
#define EV(a,b,c) /**/
#define DO_DEVDB(field1,field2) /**/
#if !defined(CFG_noassert)
#define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__)
#else
#define ASSERT(cond) /**/
#endif
#define os_clearMem(a,b) memset(a,0,b)
#define os_copyMem(a,b,c) memcpy(a,b,c)
typedef struct osjob_t osjob_t;
typedef struct band_t band_t;
typedef struct chnldef_t chnldef_t;
typedef struct rxsched_t rxsched_t;
typedef struct bcninfo_t bcninfo_t;
typedef const u1_t* xref2cu1_t;
typedef u1_t* xref2u1_t;
#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t
#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
#define TYPEDEF_xref2band_t typedef band_t* xref2band_t
#define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t
#define SIZEOFEXPR(x) sizeof(x)
#define ON_LMIC_EVENT(ev) onEvent(ev)
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
typedef s4_t ostime_t;
extern u4_t AESAUX[];
extern u4_t AESKEY[];
#define AESkey ((u1_t*)AESKEY)
#define AESaux ((u1_t*)AESAUX)
#define FUNC_ADDR(func) (&(func))
u1_t radio_rand1 (void);
#define os_getRndU1() radio_rand1()
#define DEFINE_LMIC struct lmic_t LMIC
#define DECLARE_LMIC extern struct lmic_t LMIC
void radio_init (void);
void radio_irq_handler (u1_t dio, ostime_t t);
void os_init (void);
void os_runloop (void);
void os_runloop_once();
//================================================================================
#ifndef RX_RAMPUP
#define RX_RAMPUP (us2osticks(2000))
#endif
#ifndef TX_RAMPUP
#define TX_RAMPUP (us2osticks(2000))
#endif
#ifndef OSTICKS_PER_SEC
#define OSTICKS_PER_SEC 32768
#elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516
#error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long.
#endif
#if !HAS_ostick_conv
#define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000))
#define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000))
#define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC))
#define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC))
#define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC))
// Special versions
#define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000))
#define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000))
#define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000))
#define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000))
#endif
struct osjob_t; // fwd decl.
typedef void (*osjobcb_t) (struct osjob_t*);
struct osjob_t {
struct osjob_t* next;
ostime_t deadline;
osjobcb_t func;
};
TYPEDEF_xref2osjob_t;
#ifndef HAS_os_calls
#ifndef os_getDevKey
void os_getDevKey (xref2u1_t buf);
#endif
#ifndef os_getArtEui
void os_getArtEui (xref2u1_t buf);
#endif
#ifndef os_getDevEui
void os_getDevEui (xref2u1_t buf);
#endif
#ifndef os_setCallback
void os_setCallback (xref2osjob_t job, osjobcb_t cb);
#endif
#ifndef os_setTimedCallback
void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb);
#endif
#ifndef os_clearCallback
void os_clearCallback (xref2osjob_t job);
#endif
#ifndef os_getTime
ostime_t os_getTime (void);
#endif
#ifndef os_getTimeSecs
uint os_getTimeSecs (void);
#endif
#ifndef os_radio
void os_radio (u1_t mode);
#endif
#ifndef os_getBattLevel
u1_t os_getBattLevel (void);
#endif
#ifndef os_rlsbf4
//! Read 32-bit quantity from given pointer in little endian byte order.
u4_t os_rlsbf4 (xref2cu1_t buf);
#endif
#ifndef os_wlsbf4
//! Write 32-bit quntity into buffer in little endian byte order.
void os_wlsbf4 (xref2u1_t buf, u4_t value);
#endif
#ifndef os_rmsbf4
//! Read 32-bit quantity from given pointer in big endian byte order.
u4_t os_rmsbf4 (xref2cu1_t buf);
#endif
#ifndef os_wmsbf4
//! Write 32-bit quntity into buffer in big endian byte order.
void os_wmsbf4 (xref2u1_t buf, u4_t value);
#endif
#ifndef os_rlsbf2
//! Read 16-bit quantity from given pointer in little endian byte order.
u2_t os_rlsbf2 (xref2cu1_t buf);
#endif
#ifndef os_wlsbf2
//! Write 16-bit quntity into buffer in little endian byte order.
void os_wlsbf2 (xref2u1_t buf, u2_t value);
#endif
//! Get random number (default impl for u2_t).
#ifndef os_getRndU2
#define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1()))
#endif
#ifndef os_crc16
u2_t os_crc16 (xref2u1_t d, uint len);
#endif
#endif // !HAS_os_calls
#define lmic_printf printf
// ======================================================================
// AES support
// !!Keep in sync with lorabase.hpp!!
#ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too
#define AES_ENC 0x00
#define AES_DEC 0x01
#define AES_MIC 0x02
#define AES_CTR 0x04
#define AES_MICNOAUX 0x08
#endif
#ifndef AESkey // if AESkey is defined as macro all other values must be too
extern xref2u1_t AESkey;
extern xref2u1_t AESaux;
#endif
#ifndef os_aes
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len);
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _oslmic_h_

886
src/radio.c Executable file
View File

@ -0,0 +1,886 @@
/*
* Copyright (c) 2014-2016 IBM 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.
*/
#include "lmic.h"
// ----------------------------------------
// Registers Mapping
#define RegFifo 0x00 // common
#define RegOpMode 0x01 // common
#define FSKRegBitrateMsb 0x02
#define FSKRegBitrateLsb 0x03
#define FSKRegFdevMsb 0x04
#define FSKRegFdevLsb 0x05
#define RegFrfMsb 0x06 // common
#define RegFrfMid 0x07 // common
#define RegFrfLsb 0x08 // common
#define RegPaConfig 0x09 // common
#define RegPaRamp 0x0A // common
#define RegOcp 0x0B // common
#define RegLna 0x0C // common
#define FSKRegRxConfig 0x0D
#define LORARegFifoAddrPtr 0x0D
#define FSKRegRssiConfig 0x0E
#define LORARegFifoTxBaseAddr 0x0E
#define FSKRegRssiCollision 0x0F
#define LORARegFifoRxBaseAddr 0x0F
#define FSKRegRssiThresh 0x10
#define LORARegFifoRxCurrentAddr 0x10
#define FSKRegRssiValue 0x11
#define LORARegIrqFlagsMask 0x11
#define FSKRegRxBw 0x12
#define LORARegIrqFlags 0x12
#define FSKRegAfcBw 0x13
#define LORARegRxNbBytes 0x13
#define FSKRegOokPeak 0x14
#define LORARegRxHeaderCntValueMsb 0x14
#define FSKRegOokFix 0x15
#define LORARegRxHeaderCntValueLsb 0x15
#define FSKRegOokAvg 0x16
#define LORARegRxPacketCntValueMsb 0x16
#define LORARegRxpacketCntValueLsb 0x17
#define LORARegModemStat 0x18
#define LORARegPktSnrValue 0x19
#define FSKRegAfcFei 0x1A
#define LORARegPktRssiValue 0x1A
#define FSKRegAfcMsb 0x1B
#define LORARegRssiValue 0x1B
#define FSKRegAfcLsb 0x1C
#define LORARegHopChannel 0x1C
#define FSKRegFeiMsb 0x1D
#define LORARegModemConfig1 0x1D
#define FSKRegFeiLsb 0x1E
#define LORARegModemConfig2 0x1E
#define FSKRegPreambleDetect 0x1F
#define LORARegSymbTimeoutLsb 0x1F
#define FSKRegRxTimeout1 0x20
#define LORARegPreambleMsb 0x20
#define FSKRegRxTimeout2 0x21
#define LORARegPreambleLsb 0x21
#define FSKRegRxTimeout3 0x22
#define LORARegPayloadLength 0x22
#define FSKRegRxDelay 0x23
#define LORARegPayloadMaxLength 0x23
#define FSKRegOsc 0x24
#define LORARegHopPeriod 0x24
#define FSKRegPreambleMsb 0x25
#define LORARegFifoRxByteAddr 0x25
#define LORARegModemConfig3 0x26
#define FSKRegPreambleLsb 0x26
#define FSKRegSyncConfig 0x27
#define LORARegFeiMsb 0x28
#define FSKRegSyncValue1 0x28
#define LORAFeiMib 0x29
#define FSKRegSyncValue2 0x29
#define LORARegFeiLsb 0x2A
#define FSKRegSyncValue3 0x2A
#define FSKRegSyncValue4 0x2B
#define LORARegRssiWideband 0x2C
#define FSKRegSyncValue5 0x2C
#define FSKRegSyncValue6 0x2D
#define FSKRegSyncValue7 0x2E
#define FSKRegSyncValue8 0x2F
#define FSKRegPacketConfig1 0x30
#define FSKRegPacketConfig2 0x31
#define LORARegDetectOptimize 0x31
#define FSKRegPayloadLength 0x32
#define FSKRegNodeAdrs 0x33
#define LORARegInvertIQ 0x33
#define FSKRegBroadcastAdrs 0x34
#define FSKRegFifoThresh 0x35
#define FSKRegSeqConfig1 0x36
#define FSKRegSeqConfig2 0x37
#define LORARegDetectionThreshold 0x37
#define FSKRegTimerResol 0x38
#define FSKRegTimer1Coef 0x39
#define LORARegSyncWord 0x39
#define FSKRegTimer2Coef 0x3A
#define FSKRegImageCal 0x3B
#define FSKRegTemp 0x3C
#define FSKRegLowBat 0x3D
#define FSKRegIrqFlags1 0x3E
#define FSKRegIrqFlags2 0x3F
#define RegDioMapping1 0x40 // common
#define RegDioMapping2 0x41 // common
#define RegVersion 0x42 // common
// #define RegAgcRef 0x43 // common
// #define RegAgcThresh1 0x44 // common
// #define RegAgcThresh2 0x45 // common
// #define RegAgcThresh3 0x46 // common
// #define RegPllHop 0x4B // common
// #define RegTcxo 0x58 // common
#define RegPaDac 0x5A // common
// #define RegPll 0x5C // common
// #define RegPllLowPn 0x5E // common
// #define RegFormerTemp 0x6C // common
// #define RegBitRateFrac 0x70 // common
// ----------------------------------------
// spread factors and mode for RegModemConfig2
#define SX1272_MC2_FSK 0x00
#define SX1272_MC2_SF7 0x70
#define SX1272_MC2_SF8 0x80
#define SX1272_MC2_SF9 0x90
#define SX1272_MC2_SF10 0xA0
#define SX1272_MC2_SF11 0xB0
#define SX1272_MC2_SF12 0xC0
// bandwidth for RegModemConfig1
#define SX1272_MC1_BW_125 0x00
#define SX1272_MC1_BW_250 0x40
#define SX1272_MC1_BW_500 0x80
// coding rate for RegModemConfig1
#define SX1272_MC1_CR_4_5 0x08
#define SX1272_MC1_CR_4_6 0x10
#define SX1272_MC1_CR_4_7 0x18
#define SX1272_MC1_CR_4_8 0x20
#define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive
#define SX1272_MC1_RX_PAYLOAD_CRCON 0x02
#define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12
// transmit power configuration for RegPaConfig
#define SX1272_PAC_PA_SELECT_PA_BOOST 0x80
#define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00
// sx1276 RegModemConfig1
#define SX1276_MC1_BW_125 0x70
#define SX1276_MC1_BW_250 0x80
#define SX1276_MC1_BW_500 0x90
#define SX1276_MC1_CR_4_5 0x02
#define SX1276_MC1_CR_4_6 0x04
#define SX1276_MC1_CR_4_7 0x06
#define SX1276_MC1_CR_4_8 0x08
#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01
// sx1276 RegModemConfig2
#define SX1276_MC2_RX_PAYLOAD_CRCON 0x04
// sx1276 RegModemConfig3
#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08
#define SX1276_MC3_AGCAUTO 0x04
// preamble for lora networks (nibbles swapped)
#define LORA_MAC_PREAMBLE 0x34
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A
#ifdef CFG_sx1276_radio
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70
#elif CFG_sx1272_radio
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74
#endif
// ----------------------------------------
// Constants for radio registers
#define OPMODE_LORA 0x80
#define OPMODE_MASK 0x07
#define OPMODE_SLEEP 0x00
#define OPMODE_STANDBY 0x01
#define OPMODE_FSTX 0x02
#define OPMODE_TX 0x03
#define OPMODE_FSRX 0x04
#define OPMODE_RX 0x05
#define OPMODE_RX_SINGLE 0x06
#define OPMODE_CAD 0x07
// ----------------------------------------
// Bits masking the corresponding IRQs from the radio
#define IRQ_LORA_RXTOUT_MASK 0x80
#define IRQ_LORA_RXDONE_MASK 0x40
#define IRQ_LORA_CRCERR_MASK 0x20
#define IRQ_LORA_HEADER_MASK 0x10
#define IRQ_LORA_TXDONE_MASK 0x08
#define IRQ_LORA_CDDONE_MASK 0x04
#define IRQ_LORA_FHSSCH_MASK 0x02
#define IRQ_LORA_CDDETD_MASK 0x01
#define IRQ_FSK1_MODEREADY_MASK 0x80
#define IRQ_FSK1_RXREADY_MASK 0x40
#define IRQ_FSK1_TXREADY_MASK 0x20
#define IRQ_FSK1_PLLLOCK_MASK 0x10
#define IRQ_FSK1_RSSI_MASK 0x08
#define IRQ_FSK1_TIMEOUT_MASK 0x04
#define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02
#define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01
#define IRQ_FSK2_FIFOFULL_MASK 0x80
#define IRQ_FSK2_FIFOEMPTY_MASK 0x40
#define IRQ_FSK2_FIFOLEVEL_MASK 0x20
#define IRQ_FSK2_FIFOOVERRUN_MASK 0x10
#define IRQ_FSK2_PACKETSENT_MASK 0x08
#define IRQ_FSK2_PAYLOADREADY_MASK 0x04
#define IRQ_FSK2_CRCOK_MASK 0x02
#define IRQ_FSK2_LOWBAT_MASK 0x01
// ----------------------------------------
// DIO function mappings D0D1D2D3
#define MAP_DIO0_LORA_RXDONE 0x00 // 00------
#define MAP_DIO0_LORA_TXDONE 0x40 // 01------
#define MAP_DIO1_LORA_RXTOUT 0x00 // --00----
#define MAP_DIO1_LORA_NOP 0x30 // --11----
#define MAP_DIO2_LORA_NOP 0xC0 // ----11--
#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready)
#define MAP_DIO1_FSK_NOP 0x30 // --11----
#define MAP_DIO2_FSK_TXNOP 0x04 // ----01--
#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10--
// FSK IMAGECAL defines
#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F
#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80
#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default
#define RF_IMAGECAL_IMAGECAL_MASK 0xBF
#define RF_IMAGECAL_IMAGECAL_START 0x40
#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20
#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default
// RADIO STATE
// (initialized by radio_init(), used by radio_rand1())
static u1_t randbuf[16];
#ifdef CFG_sx1276_radio
#define LNA_RX_GAIN (0x20|0x1)
#elif CFG_sx1272_radio
#define LNA_RX_GAIN (0x20|0x03)
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif
static void writeReg (u1_t addr, u1_t data ) {
hal_spi_write(addr | 0x80, &data, 1);
/*
hal_pin_nss(0);
hal_spi(addr | 0x80);
hal_spi(data);
hal_pin_nss(1);
*/
}
static u1_t readReg (u1_t addr) {
u1_t buf[1];
hal_spi_read(addr & 0x7f, buf, 1);
return buf[0];
/*
hal_pin_nss(0);
hal_spi(addr & 0x7F);
u1_t val = hal_spi(0x00);
hal_pin_nss(1);
return val;
*/
}
static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) {
hal_spi_write(addr | 0x80, buf, len);
/*
hal_pin_nss(0);
hal_spi(addr | 0x80);
for (u1_t i=0; i<len; i++) {
hal_spi(buf[i]);
}
hal_pin_nss(1);
*/
}
static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) {
hal_spi_read(addr & 0x7f, buf, len);
/*
hal_pin_nss(0);
hal_spi(addr & 0x7F);
for (u1_t i=0; i<len; i++) {
buf[i] = hal_spi(0x00);
}
hal_pin_nss(1);
*/
}
static void opmode (u1_t mode) {
writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode);
}
static void opmodeLora() {
u1_t u = OPMODE_LORA;
#ifdef CFG_sx1276_radio
u |= 0x8; // TBD: sx1276 high freq
#endif
writeReg(RegOpMode, u);
}
static void opmodeFSK() {
u1_t u = 0;
#ifdef CFG_sx1276_radio
u |= 0x8; // TBD: sx1276 high freq
#endif
writeReg(RegOpMode, u);
}
// configure LoRa modem (cfg1, cfg2)
static void configLoraModem () {
sf_t sf = getSf(LMIC.rps);
#ifdef CFG_sx1276_radio
u1_t mc1 = 0, mc2 = 0, mc3 = 0;
switch (getBw(LMIC.rps)) {
case BW125: mc1 |= SX1276_MC1_BW_125; break;
case BW250: mc1 |= SX1276_MC1_BW_250; break;
case BW500: mc1 |= SX1276_MC1_BW_500; break;
default:
ASSERT(0);
}
switch( getCr(LMIC.rps) ) {
case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
default:
ASSERT(0);
}
if (getIh(LMIC.rps)) {
mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
}
// set ModemConfig1
writeReg(LORARegModemConfig1, mc1);
mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
if (getNocrc(LMIC.rps) == 0) {
mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
}
writeReg(LORARegModemConfig2, mc2);
mc3 = SX1276_MC3_AGCAUTO;
if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
}
writeReg(LORARegModemConfig3, mc3);
#elif CFG_sx1272_radio
u1_t mc1 = (getBw(LMIC.rps)<<6);
switch( getCr(LMIC.rps) ) {
case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
}
if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
}
if (getNocrc(LMIC.rps) == 0) {
mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
}
if (getIh(LMIC.rps)) {
mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
}
// set ModemConfig1
writeReg(LORARegModemConfig1, mc1);
// set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif /* CFG_sx1272_radio */
}
static void configChannel () {
// set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19)
uint64_t frf = ((uint64_t)LMIC.freq << 19) / 32000000;
writeReg(RegFrfMsb, (u1_t)(frf>>16));
writeReg(RegFrfMid, (u1_t)(frf>> 8));
writeReg(RegFrfLsb, (u1_t)(frf>> 0));
}
static void configPower () {
#ifdef CFG_sx1276_radio
// no boost used for now
s1_t pw = (s1_t)LMIC.txpow;
if(pw >= 17) {
pw = 15;
} else if(pw < 2) {
pw = 2;
}
// check board type for BOOST pin
writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
writeReg(RegPaDac, readReg(RegPaDac)|0x4);
#elif CFG_sx1272_radio
// set PA config (2-17 dBm using PA_BOOST)
s1_t pw = (s1_t)LMIC.txpow;
if(pw > 17) {
pw = 17;
} else if(pw < 2) {
pw = 2;
}
writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif /* CFG_sx1272_radio */
}
static void txfsk () {
// select FSK modem (from sleep mode)
writeReg(RegOpMode, 0x10); // FSK, BT=0.5
ASSERT(readReg(RegOpMode) == 0x10);
// enter standby mode (required for FIFO loading))
opmode(OPMODE_STANDBY);
// set bitrate
writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
writeReg(FSKRegBitrateLsb, 0x80);
// set frequency deviation
writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
writeReg(FSKRegFdevLsb, 0x99);
// frame and packet handler settings
writeReg(FSKRegPreambleMsb, 0x00);
writeReg(FSKRegPreambleLsb, 0x05);
writeReg(FSKRegSyncConfig, 0x12);
writeReg(FSKRegPacketConfig1, 0xD0);
writeReg(FSKRegPacketConfig2, 0x40);
writeReg(FSKRegSyncValue1, 0xC1);
writeReg(FSKRegSyncValue2, 0x94);
writeReg(FSKRegSyncValue3, 0xC1);
// configure frequency
configChannel();
// configure output power
configPower();
// set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP
writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP);
// initialize the payload size and address pointers
writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload))
// download length byte and buffer to the radio FIFO
writeReg(RegFifo, LMIC.dataLen);
writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// enable antenna switch for TX
hal_pin_rxtx(1);
// now we actually start the transmission
opmode(OPMODE_TX);
}
static void txlora () {
// select LoRa modem (from sleep mode)
//writeReg(RegOpMode, OPMODE_LORA);
opmodeLora();
ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
// enter standby mode (required for FIFO loading))
opmode(OPMODE_STANDBY);
// configure LoRa modem (cfg1, cfg2)
configLoraModem();
// configure frequency
configChannel();
// configure output power
writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
configPower();
// set sync word
writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP);
// clear all radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF);
// mask all IRQs but TxDone
writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK);
// initialize the payload size and address pointers
writeReg(LORARegFifoTxBaseAddr, 0x00);
writeReg(LORARegFifoAddrPtr, 0x00);
writeReg(LORARegPayloadLength, LMIC.dataLen);
// download buffer to the radio FIFO
writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// enable antenna switch for TX
hal_pin_rxtx(1);
// now we actually start the transmission
opmode(OPMODE_TX);
#if LMIC_DEBUG_LEVEL > 0
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
u1_t bw = getBw(LMIC.rps);
u1_t cr = getCr(LMIC.rps);
lmic_printf("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
os_getTime(), LMIC.freq, LMIC.dataLen, sf,
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
getIh(LMIC.rps)
);
#endif
}
// start transmitter (buf=LMIC.frame, len=LMIC.dataLen)
static void starttx () {
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
if(getSf(LMIC.rps) == FSK) { // FSK modem
txfsk();
} else { // LoRa modem
txlora();
}
// the radio will go back to STANDBY mode as soon as the TX is finished
// the corresponding IRQ will inform us about completion.
}
enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI };
static const u1_t rxlorairqmask[] = {
[RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK,
[RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK,
[RXMODE_RSSI] = 0x00,
};
// start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen])
static void rxlora (u1_t rxmode) {
// select LoRa modem (from sleep mode)
opmodeLora();
ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
// enter standby mode (warm up))
opmode(OPMODE_STANDBY);
// don't use MAC settings at startup
if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2);
} else { // single or continuous rx mode
// configure LoRa modem (cfg1, cfg2)
configLoraModem();
// configure frequency
configChannel();
}
// set LNA gain
writeReg(RegLna, LNA_RX_GAIN);
// set max payload size
writeReg(LORARegPayloadMaxLength, 64);
#if !defined(DISABLE_INVERT_IQ_ON_RX)
// use inverted I/Q signal (prevent mote-to-mote communication)
// XXX: use flag to switch on/off inversion
if (LMIC.noRXIQinversion) {
writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ) & ~(1<<6));
} else {
writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6));
}
#endif
// set symbol timeout (for single rx)
writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms);
// set sync word
writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
// configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP
writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP);
// clear all radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF);
// enable required radio IRQs
writeReg(LORARegIrqFlagsMask, ~rxlorairqmask[rxmode]);
// enable antenna switch for RX
hal_pin_rxtx(0);
// now instruct the radio to receive
if (rxmode == RXMODE_SINGLE) { // single rx
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
opmode(OPMODE_RX_SINGLE);
} else { // continous rx (scan or rssi)
opmode(OPMODE_RX);
}
#if LMIC_DEBUG_LEVEL > 0
if (rxmode == RXMODE_RSSI) {
lmic_printf("RXMODE_RSSI\n");
} else {
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
u1_t bw = getBw(LMIC.rps);
u1_t cr = getCr(LMIC.rps);
lmic_printf("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
os_getTime(),
rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
LMIC.freq, sf,
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
getIh(LMIC.rps)
);
}
#endif
}
static void rxfsk (u1_t rxmode) {
// only single rx (no continuous scanning, no noise sampling)
ASSERT( rxmode == RXMODE_SINGLE );
// select FSK modem (from sleep mode)
//writeReg(RegOpMode, 0x00); // (not LoRa)
opmodeFSK();
ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0);
// enter standby mode (warm up))
opmode(OPMODE_STANDBY);
// configure frequency
configChannel();
// set LNA gain
//writeReg(RegLna, 0x20|0x03); // max gain, boost enable
writeReg(RegLna, LNA_RX_GAIN);
// configure receiver
writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!?
// set receiver bandwidth
writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb
// set AFC bandwidth
writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB
// set preamble detection
writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors
// set sync config
writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync
// set packet config
writeReg(FSKRegPacketConfig1, 0xD8); // var-length, whitening, crc, no auto-clear, no adr filter
writeReg(FSKRegPacketConfig2, 0x40); // packet mode
// set sync value
writeReg(FSKRegSyncValue1, 0xC1);
writeReg(FSKRegSyncValue2, 0x94);
writeReg(FSKRegSyncValue3, 0xC1);
// set preamble timeout
writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2);
// set bitrate
writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
writeReg(FSKRegBitrateLsb, 0x80);
// set frequency deviation
writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
writeReg(FSKRegFdevLsb, 0x99);
// configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut
writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT);
// enable antenna switch for RX
hal_pin_rxtx(0);
// now instruct the radio to receive
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
opmode(OPMODE_RX); // no single rx mode available in FSK
}
static void startrx (u1_t rxmode) {
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
if(getSf(LMIC.rps) == FSK) { // FSK modem
rxfsk(rxmode);
} else { // LoRa modem
rxlora(rxmode);
}
// the radio will go back to STANDBY mode as soon as the RX is finished
// or timed out, and the corresponding IRQ will inform us about completion.
}
// get random seed from wideband noise rssi
void radio_init () {
hal_disableIRQs();
// manually reset radio
#ifdef CFG_sx1276_radio
hal_pin_rst(0); // drive RST pin low
#else
hal_pin_rst(1); // drive RST pin high
#endif
hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us
hal_pin_rst(2); // configure RST pin floating!
hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms
opmode(OPMODE_SLEEP);
// some sanity checks, e.g., read version number
u1_t v = readReg(RegVersion);
#ifdef CFG_sx1276_radio
ASSERT(v == 0x12 );
#elif CFG_sx1272_radio
ASSERT(v == 0x22);
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif
// seed 15-byte randomness via noise rssi
rxlora(RXMODE_RSSI);
while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx
for(int i=1; i<16; i++) {
for(int j=0; j<8; j++) {
u1_t b; // wait for two non-identical subsequent least-significant bits
while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) );
randbuf[i] = (randbuf[i] << 1) | b;
}
}
randbuf[0] = 16; // set initial index
#ifdef CFG_sx1276mb1_board
// chain calibration
writeReg(RegPaConfig, 0);
// Launch Rx chain calibration for LF band
writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; }
// Sets a Frequency in HF band
u4_t frf = 868000000;
writeReg(RegFrfMsb, (u1_t)(frf>>16));
writeReg(RegFrfMid, (u1_t)(frf>> 8));
writeReg(RegFrfLsb, (u1_t)(frf>> 0));
// Launch Rx chain calibration for HF band
writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; }
#endif /* CFG_sx1276mb1_board */
opmode(OPMODE_SLEEP);
hal_enableIRQs();
}
// return next random byte derived from seed buffer
// (buf[0] holds index of next byte to be returned)
u1_t radio_rand1 () {
u1_t i = randbuf[0];
ASSERT( i != 0 );
if( i==16 ) {
os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key
i = 0;
}
u1_t v = randbuf[i++];
randbuf[0] = i;
return v;
}
u1_t radio_rssi () {
hal_disableIRQs();
u1_t r = readReg(LORARegRssiValue);
hal_enableIRQs();
return r;
}
static const u2_t LORA_RXDONE_FIXUP[] = {
[FSK] = us2osticks(0), // ( 0 ticks)
[SF7] = us2osticks(0), // ( 0 ticks)
[SF8] = us2osticks(1648), // ( 54 ticks)
[SF9] = us2osticks(3265), // ( 107 ticks)
[SF10] = us2osticks(7049), // ( 231 ticks)
[SF11] = us2osticks(13641), // ( 447 ticks)
[SF12] = us2osticks(31189), // (1022 ticks)
};
// called by hal ext IRQ handler
// (radio goes to stanby mode after tx/rx operations)
void radio_irq_handler (u1_t dio, ostime_t t) {
if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
u1_t flags = readReg(LORARegIrqFlags);
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: irq: dio: 0x%x flags: 0x%x\n", t, dio, flags);
#endif
if( flags & IRQ_LORA_TXDONE_MASK ) {
// save exact tx time
LMIC.txend = t - us2osticks(43); // TXDONE FIXUP
} else if( flags & IRQ_LORA_RXDONE_MASK ) {
// save exact rx time
if(getBw(LMIC.rps) == BW125) {
t -= LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
}
LMIC.rxtime = t;
// read the PDU and inform the MAC that we received something
LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
// set FIFO read address pointer
writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
// now read the FIFO
readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// read rx quality parameters
LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
} else if( flags & IRQ_LORA_RXTOUT_MASK ) {
// indicate timeout
LMIC.dataLen = 0;
}
// mask all radio IRQs
writeReg(LORARegIrqFlagsMask, 0xFF);
// clear radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF);
} else { // FSK modem
u1_t flags1 = readReg(FSKRegIrqFlags1);
u1_t flags2 = readReg(FSKRegIrqFlags2);
if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
// save exact tx time
LMIC.txend = t;
} else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) {
// save exact rx time
LMIC.rxtime = t;
// read the PDU and inform the MAC that we received something
LMIC.dataLen = readReg(FSKRegPayloadLength);
// now read the FIFO
readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// read rx quality parameters
LMIC.snr = 0; // determine snr
LMIC.rssi = 0; // determine rssi
} else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
// indicate timeout
LMIC.dataLen = 0;
} else {
ASSERT(0);
}
}
// go from stanby to sleep
opmode(OPMODE_SLEEP);
// run os job (use preset func ptr)
os_setCallback(&LMIC.osjob, LMIC.osjob.func);
}
void os_radio (u1_t mode) {
hal_disableIRQs();
switch (mode) {
case RADIO_RST:
// put radio to sleep
opmode(OPMODE_SLEEP);
break;
case RADIO_TX:
// transmit frame now
starttx(); // buf=LMIC.frame, len=LMIC.dataLen
break;
case RADIO_RX:
// receive frame now (exactly at rxtime)
startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms
break;
case RADIO_RXON:
// start scanning for beacon now
startrx(RXMODE_SCAN); // buf=LMIC.frame
break;
}
hal_enableIRQs();
}