mirror of
				https://github.com/manuelbl/ttn-esp32.git
				synced 2025-10-31 10:40:35 +01:00 
			
		
		
		
	Initial check-in
This commit is contained in:
		
							
								
								
									
										54
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -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
									
								
							
							
						
						
									
										32
									
								
								.vscode/c_cpp_properties.json
									
									
									
									
										vendored
									
									
										Normal 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
									
								
							
							
						
						
									
										35
									
								
								Kconfig
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										4
									
								
								component.mk
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,4 @@ | ||||
| CFLAGS += -Wno-error=maybe-uninitialized -Wno-error=unused-value | ||||
|  | ||||
| COMPONENT_ADD_INCLUDEDIRS := include | ||||
| COMPONENT_SRCDIRS := src | ||||
							
								
								
									
										3
									
								
								examples/send_msg/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/send_msg/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| PROJECT_NAME := send_msg | ||||
|  | ||||
| include $(IDF_PATH)/make/project.mk | ||||
							
								
								
									
										1
									
								
								examples/send_msg/components/ttn-esp32
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								examples/send_msg/components/ttn-esp32
									
									
									
									
									
										Symbolic link
									
								
							| @ -0,0 +1 @@ | ||||
| ../../../../ttn-esp32 | ||||
							
								
								
									
										0
									
								
								examples/send_msg/main/component.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								examples/send_msg/main/component.mk
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										74
									
								
								examples/send_msg/main/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								examples/send_msg/main/main.cpp
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										133
									
								
								include/TheThingsNetwork.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										238
									
								
								src/TheThingsNetwork.cpp
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										383
									
								
								src/aes.c
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										46
									
								
								src/config.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										116
									
								
								src/hal.h
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										409
									
								
								src/hal_esp32.c
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										44
									
								
								src/hal_esp32.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										2400
									
								
								src/lmic.c
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										340
									
								
								src/lmic.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										340
									
								
								src/lmic.h
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										407
									
								
								src/lorabase.h
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										151
									
								
								src/oslmic.c
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										242
									
								
								src/oslmic.h
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										886
									
								
								src/radio.c
									
									
									
									
									
										Executable 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(); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user