@@ -0,0 +1,28 @@ | |||
lst/ | |||
obj/ | |||
bin/ | |||
build/ | |||
tmp/ | |||
*.bin | |||
.kdev4 | |||
*.log | |||
*.dep | |||
*.uvgui.* | |||
*.uvgui_*.bak | |||
*.uvguix.* | |||
*.uvguix_*.bak | |||
*_uvopt.bak | |||
*_uvoptx.bak | |||
*_uvproj.bak | |||
*_uvprojx.bak | |||
*.ctx | |||
*.dbi | |||
*.xdb | |||
*.svn | |||
*.pbxproj | |||
*.cogui | |||
*.comarker | |||
.directory | |||
.vscode/launch.json | |||
.cmaketools.json | |||
ipch/ |
@@ -0,0 +1,6 @@ | |||
[submodule "src/radio/lr1110/lr1110_driver"] | |||
path = src/radio/lr1110/lr1110_driver | |||
url = https://github.com/Lora-net/lr1110_driver.git | |||
[submodule "src/peripherals/atecc608a-tnglora-se/cryptoauthlib"] | |||
path = src/peripherals/atecc608a-tnglora-se/cryptoauthlib | |||
url = https://github.com/MicrochipTech/cryptoauthlib |
@@ -0,0 +1,18 @@ | |||
{ | |||
"configurations": [ | |||
{ | |||
"name": "ARM", | |||
"compileCommands": "${workspaceRoot}/build/compile_commands.json", | |||
"intelliSenseMode": "clang-x64", | |||
"browse": { | |||
"path": [ | |||
"${workspaceFolder}" | |||
] | |||
}, | |||
"cStandard": "c11", | |||
"cppStandard": "c++17", | |||
"configurationProvider": "ms-vscode.cmake-tools" | |||
} | |||
], | |||
"version": 4 | |||
} |
@@ -0,0 +1,83 @@ | |||
// Place your settings in this file to overwrite default and user settings. | |||
{ | |||
"cmake.configureSettings": { | |||
// In case your GNU ARM-Toolchain is not installed under the default | |||
// path: | |||
// Windows : No default path. Specify the path where the | |||
// toolchain is installed. i.e: | |||
// "C:/PROGRA~2/GNUTOO~1/92019-~1". | |||
// Linux : /usr | |||
// OSX : /usr/local | |||
// It is required to uncomment and to fill the following line. | |||
//"TOOLCHAIN_PREFIX":"/path/to/toolchain", | |||
// In case your OpenOCD is not installed under the default path: | |||
// Windows : C:/openocd/bin/openocd.exe | |||
// Linux : /usr/bin/openocd | |||
// OSX : /usr/local/bin/openocd | |||
// Please uncomment the following line and fill it accordingly. | |||
//"OPENOCD_BIN":"C:/openocd/bin/openocd.exe", | |||
// Specifies the path to the CMAKE toolchain file. | |||
"CMAKE_TOOLCHAIN_FILE":"cmake/toolchain-arm-none-eabi.cmake", | |||
// Determines the application. You can choose between: | |||
// LoRaMac (Default), ping-pong, rx-sensi, tx-cw. | |||
"APPLICATION":"LoRaMac", | |||
// Select LoRaMac sub project. You can choose between: | |||
// periodic-uplink-lpp, fuota-test-01. | |||
"SUB_PROJECT":"periodic-uplink-lpp", | |||
// Switch for Class B support of LoRaMac: | |||
"CLASSB_ENABLED":"ON", | |||
// Select the active region for which the stack will be initialized. | |||
// You can choose between: | |||
// LORAMAC_REGION_EU868, LORAMAC_REGION_US915, .. | |||
"ACTIVE_REGION":"LORAMAC_REGION_EU868", | |||
// Select the type of modulation, applicable to the ping-pong or | |||
// rx-sensi applications. You can choose between: | |||
// LORA or FSK | |||
"MODULATION":"LORA", | |||
// Target board, the following boards are supported: | |||
// NAMote72, NucleoL073 (Default), NucleoL152, NucleoL476, SAMR34, SKiM880B, SKiM980A, SKiM881AXL, B-L072Z-LRWAN1. | |||
"BOARD":"NucleoL073", | |||
// MBED Radio shield selection. (Applies only to Nucleo platforms) | |||
// The following shields are supported: | |||
// SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS(Default), SX1262MBXCAS, SX1262MBXDAS, LR1110MB1XXS. | |||
"MBED_RADIO_SHIELD":"SX1261MBXBAS", | |||
// Secure element type selection the following are supported | |||
// SOFT_SE(Default), LR1110_SE, ATECC608A_TNGLORA_SE | |||
"SECURE_ELEMENT":"SOFT_SE", | |||
// Secure element is pre-provisioned | |||
"SECURE_ELEMENT_PRE_PROVISIONED":"ON", | |||
// Region support activation, Select the ones you want to support. | |||
// By default only REGION_EU868 support is enabled. | |||
"REGION_EU868":"ON", | |||
"REGION_US915":"OFF", | |||
"REGION_CN779":"OFF", | |||
"REGION_EU433":"OFF", | |||
"REGION_AU915":"OFF", | |||
"REGION_CN470":"OFF", | |||
"REGION_AS923":"OFF", | |||
"REGION_KR920":"OFF", | |||
"REGION_IN865":"OFF", | |||
"REGION_RU864":"OFF", | |||
// Default channel plan for region AS923. Possible selections: | |||
// CHANNEL_PLAN_GROUP_AS923_1, CHANNEL_PLAN_GROUP_AS923_2, CHANNEL_PLAN_GROUP_AS923_3, CHANNEL_PLAN_GROUP_AS923_1_JP | |||
"REGION_AS923_DEFAULT_CHANNEL_PLAN":"CHANNEL_PLAN_GROUP_AS923_1", | |||
// Default channel plan for region CN470. Possible selections: | |||
// CHANNEL_PLAN_20MHZ_TYPE_A, CHANNEL_PLAN_20MHZ_TYPE_B, CHANNEL_PLAN_26MHZ_TYPE_A, CHANNEL_PLAN_26MHZ_TYPE_B | |||
"REGION_CN470_DEFAULT_CHANNEL_PLAN":"CHANNEL_PLAN_20MHZ_TYPE_A" | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder (STACKFORCE), Miguel Luis (Semtech) | |||
## | |||
project(loramac-node) | |||
cmake_minimum_required(VERSION 3.6) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src) |
@@ -0,0 +1,24 @@ | |||
Revised BSD License | |||
Copyright Semtech Corporation 2013. 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 Semtech corporation 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 SEMTECH CORPORATION 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. |
@@ -0,0 +1,395 @@ | |||
# LoRaWAN end-device stack implementation and example projects | |||
______ _ | |||
/ _____) _ | | | |||
( (____ _____ ____ _| |_ _____ ____| |__ | |||
\____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
_____) ) ____| | | || |_| ____( (___| | | | | |||
(______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
(C)2013-2020 Semtech | |||
___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
embedded.connectivity.solutions=============== | |||
## Introduction | |||
The aim of this project is to show an example of an end-device LoRaWAN stack implementation. | |||
This project has 2 active branches in place. | |||
| Branch | L2 spec | RP spec | Tag/Milestone | Class | Comments | | |||
| ------------- |:-------------:|:---------:|:---------:|:---------:|:--------------| | |||
| | [1.0.4](https://lora-alliance.org/resource-hub/lorawan-104-specification-package) | [2-1.0.1](https://lora-alliance.org/sites/default/files/2020-02/rp_2-1.0.1.pdf) | [v4.5.1](https://github.com/Lora-net/LoRaMac-node/releases/tag/v4.5.1) | A/B/C | LoRaWAN L2 1.0.4 - **_Released_** | | |||
| | [1.0.3](https://lora-alliance.org/resource-hub/lorawanr-specification-v103) | [v1.0.3revA](https://www.lora-alliance.org/resource-hub/lorawanr-regional-parameters-v103reva) | [v4.4.7](https://github.com/Lora-net/LoRaMac-node/releases/tag/v4.4.7) | A/B/C | LoRaWAN L2 1.0.3 - **_Released_ (last release based on 1.0.3)** | | |||
| [master](https://github.com/Lora-net/LoRaMac-node/tree/master) | [1.0.4](https://lora-alliance.org/resource-hub/lorawan-104-specification-package) | [2-1.0.1](https://lora-alliance.org/sites/default/files/2020-02/rp_2-1.0.1.pdf) | [M4.5.2](https://github.com/Lora-net/LoRaMac-node/milestone/9) | A/B/C | LoRaWAN L2 1.0.4 | | |||
| [develop](https://github.com/Lora-net/LoRaMac-node/tree/develop) | [1.0.4](https://lora-alliance.org/resource-hub/lorawan-104-specification-package) / [1.1.1](https://lora-alliance.org/resource-hub/lorawanr-specification-v11) | [2-1.0.1](https://lora-alliance.org/sites/default/files/2020-02/rp_2-1.0.1.pdf) | [M 4.6.0](https://github.com/Lora-net/LoRaMac-node/milestone/3) | A/B/C | LoRaWAN L2 1.0.4 / 1.1.1 | | |||
This project fully implements ClassA, ClassB and ClassC end-device classes and it also provides SX1272/73, SX1276/77/78/79, SX1261/2 and LR1110 radio drivers. | |||
For each currently supported platform example applications are provided. | |||
* **LoRaMac/fuota-test-01**: FUOTA test scenario 01 end-device example application. (Based on provided application common packages) | |||
* **LoRaMac/periodic-uplink-lpp**: ClassA/B/C end-device example application. Periodically uplinks a frame using the Cayenne LPP protocol. (Based on provided application common packages) | |||
* **ping-pong**: Point to point RF link example application. | |||
* **rx-sensi**: Example application useful to measure the radio sensitivity level using an RF generator. | |||
* **tx-cw**: Example application to show how to generate an RF Continuous Wave transmission. | |||
**Note**: *Each LoRaWAN application example (LoRaMac/\*) includes an implementation of the LoRa-Alliance; LoRaWAN certification protocol.* | |||
**Note**: *The LoRaWAN stack API documentation can be found at: http://stackforce.github.io/LoRaMac-doc/* | |||
## Supported platforms | |||
This project currently provides support for the below platforms. | |||
This project can be ported to other platforms using different MCU than the ones currently supported. | |||
The [Porting Guide](https://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.5.1/_p_o_r_t_i_n_g__g_u_i_d_e.html) document provides guide lines on how to port the project to other platforms. | |||
* NAMote72 | |||
* [NAMote72 platform documentation](doc/NAMote72-platform.md) | |||
* NucleoLxxx - Discovery kit | |||
* [NucleoLxxx and Discovery kit platforms documentation](doc/NucleoLxxx-platform.md) | |||
* SKiM880B, SKiM980A, SKiM881AXL | |||
* [SKiM88xx platforms documentation](doc/SKiM88xx-platform.md) | |||
* SAMR34 | |||
* [SAMR34 platform documentation](doc/SAMR34-platform.md) | |||
## Getting Started | |||
### Prerequisites | |||
Please follow instructions provided by [Development environment](doc/development-environment.md) document. | |||
### Cloning the repository | |||
Clone the repository from GitHub | |||
```bash | |||
$ git clone https://github.com/lora-net/loramac-node.git loramac-node | |||
``` | |||
LoRaMac-node project contains Git submodules that must be initialized | |||
```bash | |||
$ cd loramac-node | |||
$ git submodule update --init | |||
``` | |||
### Secure-element commissioning | |||
This project currently supports 3 different secure-elements `soft-se`, `lr1110-se` | |||
and `atecc608a-tnglora-se` implementations. | |||
In order to personalize the MCU binary file with LoRaWAN EUIs or/and AES128 keys | |||
one must follow the instructions provided by [soft-se](####soft-se), | |||
[lr1110-se](####lr1110-se) and [atecc608a-tnglora-se](####atecc608a-tnglora-se) chapters | |||
#### soft-se | |||
*soft-se* is a pure software emulation of a secure-element. It means that everything is located on the host MCU memories. The `DevEUI`, `JoinEUI` and `AES128 keys` may be stored on a non-volatile memory through dedicated APIs. | |||
In order to update the end-device identity (`DevEUI`, `JoinEUI` and `AES128 keys`) one must update the `se-identity.h` file located under `./src/peripherals/soft-se/` directory. | |||
**Note:** In previous versions of this project this was done inside `Commissioning.h` files located under each provided example directory. | |||
#### lr1110-se | |||
*lr1110-se* abstraction implementation handles all the required exchanges with the LR1110 radio crypto-engine. | |||
All LR1110 radio chips are pre-provisioned out of factory in order to be used with [LoRa Cloud Device Join Service](https://www.loracloud.com/documentation/join_service). | |||
In case other Join Servers are to be used the `DevEUI`, `Pin`, `JoinEUI` and `AES128 keys` can be updated by following the instructions provided on chapter "13. LR1110 Provisioning" of the [LR1110 User Manual](https://semtech.my.salesforce.com/sfc/p/#E0000000JelG/a/2R000000Q2PM/KGm1YHDoHhtaicNYHCIAnh0CbG3yodEuWWJ2WrFRafM). | |||
When the compile option `SECURE_ELEMENT_PRE_PROVISIONED` is set to `ON` the *lr1110-se* will use the factory provisioned data (`DevEUI`, `JoinEUI` and `AES128 keys`). | |||
When the compile option `SECURE_ELEMENT_PRE_PROVISIONED` is set to `OFF` the *lr1110-se* has to be provisioned by following one of the methods described on chapter "13. LR1110 Provisioning" of the [LR1110 User Manual](https://semtech.my.salesforce.com/sfc/p/#E0000000JelG/a/2R000000Q2PM/KGm1YHDoHhtaicNYHCIAnh0CbG3yodEuWWJ2WrFRafM). | |||
The `DevEUI`, `Pin` and `JoinEUI` can be changed by editing the `se-identity.h` file located in `./src/peripherals/lr1110-se/` directory. | |||
#### atecc608a-tnglora-se | |||
The *atecc608a-tnglora-se* abstraction implementation handles all the required exchanges with the ATECC608A-TNGLORA secure-element. | |||
ATECC608A-TNGLORA secure-element is always pre-provisioned and its contents can't be changed. | |||
### Building Process | |||
#### Command line | |||
**periodic-uplink-lpp** example for NucleoL476 platform with LR1110MB1DIS MBED shield and using LR1110 pre-provisioned secure-element | |||
```bash | |||
$ mkdir build | |||
$ cd build | |||
$ cmake -DCMAKE_BUILD_TYPE=Release \ | |||
-DTOOLCHAIN_PREFIX="<replace by toolchain path>" \ | |||
-DCMAKE_TOOLCHAIN_FILE="../cmake/toolchain-arm-none-eabi.cmake" \ | |||
-DAPPLICATION="LoRaMac" \ | |||
-DSUB_PROJECT="periodic-uplink-lpp" \ | |||
-DCLASSB_ENABLED="ON" \ | |||
-DACTIVE_REGION="LORAMAC_REGION_EU868" \ | |||
-DREGION_EU868="ON" \ | |||
-DREGION_US915="OFF" \ | |||
-DREGION_CN779="OFF" \ | |||
-DREGION_EU433="OFF" \ | |||
-DREGION_AU915="OFF" \ | |||
-DREGION_AS923="OFF" \ | |||
-DREGION_CN470="OFF" \ | |||
-DREGION_KR920="OFF" \ | |||
-DREGION_IN865="OFF" \ | |||
-DREGION_RU864="OFF" \ | |||
-DBOARD="NucleoL476" \ | |||
-DMBED_RADIO_SHIELD="LR1110MB1XXS" \ | |||
-DSECURE_ELEMENT="LR1110_SE" \ | |||
-DSECURE_ELEMENT_PRE_PROVISIONED="ON" \ | |||
-DUSE_RADIO_DEBUG="ON" .. | |||
$ make | |||
``` | |||
**ping-pong** example using LoRa modulation for NucleoL476 platform with LR1110MB1DIS MBED shield | |||
```bash | |||
$ mkdir build | |||
$ cd build | |||
$ cmake -DCMAKE_BUILD_TYPE=Release \ | |||
-DTOOLCHAIN_PREFIX="<replace by toolchain path>" \ | |||
-DCMAKE_TOOLCHAIN_FILE="../cmake/toolchain-arm-none-eabi.cmake" \ | |||
-DAPPLICATION="ping-pong" \ | |||
-DMODULATION:"LORA" \ | |||
-DREGION_EU868="ON" \ | |||
-DREGION_US915="OFF" \ | |||
-DREGION_CN779="OFF" \ | |||
-DREGION_EU433="OFF" \ | |||
-DREGION_AU915="OFF" \ | |||
-DREGION_AS923="OFF" \ | |||
-DREGION_CN470="OFF" \ | |||
-DREGION_KR920="OFF" \ | |||
-DREGION_IN865="OFF" \ | |||
-DREGION_RU864="OFF" \ | |||
-DBOARD="NucleoL476" \ | |||
-DMBED_RADIO_SHIELD="LR1110MB1XXS" \ | |||
-DUSE_RADIO_DEBUG="ON" .. | |||
$ make | |||
``` | |||
#### VSCode | |||
**periodic-uplink-lpp** example for NucleoL476 platform with LR1110MB1DIS MBED shield and using LR1110 pre-provisioned secure-element | |||
* Please edit .vscode/settings.json file | |||
<details> | |||
<summary>Click to expand!</summary> | |||
<p> | |||
```json | |||
// Place your settings in this file to overwrite default and user settings. | |||
{ | |||
"cmake.configureSettings": { | |||
// In case your GNU ARM-Toolchain is not installed under the default | |||
// path: | |||
// Windows : No default path. Specify the path where the | |||
// toolchain is installed. i.e: | |||
// "C:/PROGRA~2/GNUTOO~1/92019-~1". | |||
// Linux : /usr | |||
// OSX : /usr/local | |||
// It is required to uncomment and to fill the following line. | |||
"TOOLCHAIN_PREFIX":"/path/to/toolchain", | |||
// In case your OpenOCD is not installed under the default path: | |||
// Windows : C:/openocd/bin/openocd.exe | |||
// Linux : /usr/bin/openocd | |||
// OSX : /usr/local/bin/openocd | |||
// Please uncomment the following line and fill it accordingly. | |||
//"OPENOCD_BIN":"C:/openocd/bin/openocd.exe", | |||
// Specifies the path to the CMAKE toolchain file. | |||
"CMAKE_TOOLCHAIN_FILE":"cmake/toolchain-arm-none-eabi.cmake", | |||
// Determines the application. You can choose between: | |||
// LoRaMac (Default), ping-pong, rx-sensi, tx-cw. | |||
"APPLICATION":"LoRaMac", | |||
// Select LoRaMac sub project. You can choose between: | |||
// periodic-uplink-lpp, fuota-test-01. | |||
"SUB_PROJECT":"periodic-uplink-lpp", | |||
// Switch for Class B support of LoRaMac: | |||
"CLASSB_ENABLED":"ON", | |||
// Select the active region for which the stack will be initialized. | |||
// You can choose between: | |||
// LORAMAC_REGION_EU868, LORAMAC_REGION_US915, .. | |||
"ACTIVE_REGION":"LORAMAC_REGION_EU868", | |||
// Select the type of modulation, applicable to the ping-pong or | |||
// rx-sensi applications. You can choose between: | |||
// LORA or FSK | |||
"MODULATION":"LORA", | |||
// Target board, the following boards are supported: | |||
// NAMote72, NucleoL073 (Default), NucleoL152, NucleoL476, SAMR34, SKiM880B, SKiM980A, SKiM881AXL, B-L072Z-LRWAN1. | |||
"BOARD":"NucleoL476", | |||
// MBED Radio shield selection. (Applies only to Nucleo platforms) | |||
// The following shields are supported: | |||
// SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS(Default), SX1262MBXCAS, SX1262MBXDAS, LR1110MB1XXS. | |||
"MBED_RADIO_SHIELD":"LR1110MB1XXS", | |||
// Secure element type selection the following are supported | |||
// SOFT_SE(Default), LR1110_SE, ATECC608A_TNGLORA_SE | |||
"SECURE_ELEMENT":"LR1110_SE", | |||
// Secure element is pre-provisioned | |||
"SECURE_ELEMENT_PRE_PROVISIONED":"ON", | |||
// Region support activation, Select the ones you want to support. | |||
// By default only REGION_EU868 support is enabled. | |||
"REGION_EU868":"ON", | |||
"REGION_US915":"OFF", | |||
"REGION_CN779":"OFF", | |||
"REGION_EU433":"OFF", | |||
"REGION_AU915":"OFF", | |||
"REGION_AS923":"OFF", | |||
"REGION_CN470":"OFF", | |||
"REGION_KR920":"OFF", | |||
"REGION_IN865":"OFF", | |||
"REGION_RU864":"OFF", | |||
"USE_RADIO_DEBUG":"ON" | |||
} | |||
} | |||
``` | |||
</p> | |||
</details> | |||
* Click on "CMake: Debug: Ready" and select build type Debug or Release. | |||
![cmake configure](doc/images/vscode-cmake-configure.png) | |||
* Wait for configuration process to finish | |||
* Click on "Build" to build the project. | |||
![cmake build](doc/images/vscode-cmake-build.png) | |||
* Wait for build process to finish | |||
* Binary files will be available under `./build/src/apps/LoRaMac/` | |||
* LoRaMac-periodic-uplink-lpp - elf format | |||
* LoRaMac-periodic-uplink-lpp.bin - binary format | |||
* LoRaMac-periodic-uplink-lpp.hex - hex format | |||
**ping-pong** example using LoRa modulation for NucleoL476 platform with LR1110MB1DIS MBED shield | |||
* Please edit .vscode/settings.json file | |||
<details> | |||
<summary>Click to expand!</summary> | |||
<p> | |||
```json | |||
// Place your settings in this file to overwrite default and user settings. | |||
{ | |||
"cmake.configureSettings": { | |||
// In case your GNU ARM-Toolchain is not installed under the default | |||
// path: | |||
// Windows : No default path. Specify the path where the | |||
// toolchain is installed. i.e: | |||
// "C:/PROGRA~2/GNUTOO~1/92019-~1". | |||
// Linux : /usr | |||
// OSX : /usr/local | |||
// It is required to uncomment and to fill the following line. | |||
"TOOLCHAIN_PREFIX":"/path/to/toolchain", | |||
// In case your OpenOCD is not installed under the default path: | |||
// Windows : C:/openocd/bin/openocd.exe | |||
// Linux : /usr/bin/openocd | |||
// OSX : /usr/local/bin/openocd | |||
// Please uncomment the following line and fill it accordingly. | |||
//"OPENOCD_BIN":"C:/openocd/bin/openocd.exe", | |||
// Specifies the path to the CMAKE toolchain file. | |||
"CMAKE_TOOLCHAIN_FILE":"cmake/toolchain-arm-none-eabi.cmake", | |||
// Determines the application. You can choose between: | |||
// LoRaMac (Default), ping-pong, rx-sensi, tx-cw. | |||
"APPLICATION":"ping-pong", | |||
// Select LoRaMac sub project. You can choose between: | |||
// periodic-uplink-lpp, fuota-test-01. | |||
"SUB_PROJECT":"periodic-uplink-lpp", | |||
// Switch for Class B support of LoRaMac: | |||
"CLASSB_ENABLED":"ON", | |||
// Select the active region for which the stack will be initialized. | |||
// You can choose between: | |||
// LORAMAC_REGION_EU868, LORAMAC_REGION_US915, .. | |||
"ACTIVE_REGION":"LORAMAC_REGION_EU868", | |||
// Select the type of modulation, applicable to the ping-pong or | |||
// rx-sensi applications. You can choose between: | |||
// LORA or FSK | |||
"MODULATION":"LORA", | |||
// Target board, the following boards are supported: | |||
// NAMote72, NucleoL073 (Default), NucleoL152, NucleoL476, SAMR34, SKiM880B, SKiM980A, SKiM881AXL, B-L072Z-LRWAN1. | |||
"BOARD":"NucleoL476", | |||
// MBED Radio shield selection. (Applies only to Nucleo platforms) | |||
// The following shields are supported: | |||
// SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS(Default), SX1262MBXCAS, SX1262MBXDAS, LR1110MB1XXS. | |||
"MBED_RADIO_SHIELD":"SX1261MBXBAS", | |||
// Secure element type selection the following are supported | |||
// SOFT_SE(Default), LR1110_SE, ATECC608A_TNGLORA_SE | |||
"SECURE_ELEMENT":"SOFT_SE", | |||
// Secure element is pre-provisioned | |||
"SECURE_ELEMENT_PRE_PROVISIONED":"ON", | |||
// Region support activation, Select the ones you want to support. | |||
// By default only REGION_EU868 support is enabled. | |||
"REGION_EU868":"ON", | |||
"REGION_US915":"OFF", | |||
"REGION_CN779":"OFF", | |||
"REGION_EU433":"OFF", | |||
"REGION_AU915":"OFF", | |||
"REGION_AS923":"OFF", | |||
"REGION_CN470":"OFF", | |||
"REGION_KR920":"OFF", | |||
"REGION_IN865":"OFF", | |||
"REGION_RU864":"OFF", | |||
"USE_RADIO_DEBUG":"ON" | |||
} | |||
} | |||
``` | |||
</p> | |||
</details> | |||
* Click on "CMake: Debug: Ready" and select build type Debug or Release. | |||
![cmake configure](doc/images/vscode-cmake-configure.png) | |||
* Wait for configuration process to finish | |||
* Click on "Build" to build the project. | |||
![cmake build](doc/images/vscode-cmake-build.png) | |||
* Wait for build process to finish | |||
* Binary files will be available under `./build/src/apps/ping-pong/` | |||
* ping-pong - elf format | |||
* ping-pong.bin - binary format | |||
* ping-pong.hex - hex format | |||
## Acknowledgments | |||
* The mbed (https://mbed.org/) project was used at the beginning as source of | |||
inspiration. | |||
* This program uses the AES algorithm implementation (http://www.gladman.me.uk/) by Brian Gladman. | |||
* This program uses the CMAC algorithm implementation | |||
(http://www.cse.chalmers.se/research/group/dcs/masters/contikisec/) by Lander Casado, Philippas Tsigas. | |||
* [The Things Industries](https://www.thethingsindustries.com/) for providing | |||
Microchip/Atmel SAMR34 platform and ATECC608A-TNGLORA secure-element support. | |||
* Tencent Blade Team for security breach findings and solving propositions. |
@@ -0,0 +1,16 @@ | |||
# Security Policy | |||
## Supported Versions | |||
Vulnerability fixes will always be applied on [develop](https://github.com/Lora-net/LoRaMac-node/tree/develop) branch and included in next release. | |||
We may consider to hotfix the most recent release depending on the circumstances. Older releases will not be hotfixed. | |||
| Version | Supported | | |||
| ------- | ------------------ | | |||
| [develop](https://github.com/Lora-net/LoRaMac-node/tree/develop) | :white_check_mark: | | |||
| [latest release](https://github.com/Lora-net/LoRaMac-node/releases/latest) | :question: | | |||
| older releases | :x: | | |||
## Reporting a Vulnerability | |||
Please report security concerns, perceived or implemented vulnerabilities in the source code of this project to: [LoRa-Net@semtech.com](mailto:LoRa-Net@semtech.com) |
@@ -0,0 +1,50 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ) | |||
## | |||
## | |||
## CMake arm-none-eabi binutils integration and helper functions | |||
## | |||
#--------------------------------------------------------------------------------------- | |||
# Set tools | |||
#--------------------------------------------------------------------------------------- | |||
set(CMAKE_OBJCOPY ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-objcopy${TOOLCHAIN_EXT}) | |||
set(CMAKE_OBJDUMP ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-objdump${TOOLCHAIN_EXT}) | |||
set(CMAKE_SIZE ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-size${TOOLCHAIN_EXT}) | |||
#--------------------------------------------------------------------------------------- | |||
# Prints the section sizes | |||
#--------------------------------------------------------------------------------------- | |||
function(print_section_sizes TARGET) | |||
add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND ${CMAKE_SIZE} ${TARGET}) | |||
endfunction() | |||
#--------------------------------------------------------------------------------------- | |||
# Creates output in hex format | |||
#--------------------------------------------------------------------------------------- | |||
function(create_hex_output TARGET) | |||
add_custom_target(${TARGET}.hex ALL DEPENDS ${TARGET} COMMAND ${CMAKE_OBJCOPY} -Oihex ${TARGET} ${TARGET}.hex) | |||
endfunction() | |||
#--------------------------------------------------------------------------------------- | |||
# Creates output in binary format | |||
#--------------------------------------------------------------------------------------- | |||
function(create_bin_output TARGET) | |||
add_custom_target(${TARGET}.bin ALL DEPENDS ${TARGET} COMMAND ${CMAKE_OBJCOPY} -Obinary ${TARGET} ${TARGET}.bin) | |||
endfunction() |
@@ -0,0 +1,101 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ) | |||
## | |||
## Collection of functions to generate different GDB debugging configurations | |||
## | |||
# Get the path of this module | |||
set(CURRENT_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR}) | |||
#--------------------------------------------------------------------------------------- | |||
# Set tools | |||
#--------------------------------------------------------------------------------------- | |||
set(GDB_BIN ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gdb${TOOLCHAIN_EXT}) | |||
if(NOT OPENOCD_BIN) | |||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) | |||
set(OPENOCD_BIN "/usr/bin/openocd" CACHE STRING "OpenOCD executable") | |||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) | |||
set(OPENOCD_BIN "/usr/local/bin/openocd" CACHE STRING "OpenOCD executable") | |||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) | |||
set(OPENOCD_BIN "C:/openocd/bin/openocd.exe" CACHE STRING "OpenOCD executable") | |||
endif() | |||
endif() | |||
#--------------------------------------------------------------------------------------- | |||
# Generates a GDB run script for debugging with STLINKv1/v2/v2-1 programmer and texane stlink tool. | |||
# More infos check: https://github.com/texane/stlink | |||
#--------------------------------------------------------------------------------------- | |||
function(generate_run_gdb_stlink TARGET) | |||
get_target_property( TARGET_NAME ${TARGET} NAME ) | |||
configure_file(${CURRENT_MODULE_DIR}/stlink-run.gdb.in ${PROJECT_BINARY_DIR}/stlink-run.gdb @ONLY) | |||
endfunction() | |||
#--------------------------------------------------------------------------------------- | |||
# Generates a GDB run script for debugging with any supported programmer and openOCD. | |||
#--------------------------------------------------------------------------------------- | |||
function(generate_run_gdb_openocd TARGET) | |||
get_target_property( TARGET_NAME ${TARGET} NAME ) | |||
configure_file(${CURRENT_MODULE_DIR}/openocd-run.gdb.in ${PROJECT_BINARY_DIR}/openocd-run.gdb @ONLY) | |||
endfunction() | |||
#--------------------------------------------------------------------------------------- | |||
# Generates a Visual Studio Code launch configuration for debugging with openOCD. | |||
#--------------------------------------------------------------------------------------- | |||
function(generate_vscode_launch_openocd TARGET) | |||
get_target_property( TARGET_NAME ${TARGET} NAME ) | |||
# Available OpenOCD interfaces | |||
# Use stlink-v2-1.cfg when stlink interface is built in. Otherwise use stlink-v2.cfg | |||
set(OPENOCD_INTERFACE_LIST stlink-v2-1.cfg stlink-v2.cfg) | |||
set(OPENOCD_INTERFACE stlink-v2.cfg CACHE STRING "Default OPENOCD Interface is stlink-v2.cfg") | |||
set_property(CACHE OPENOCD_INTERFACE PROPERTY STRINGS ${OPENOCD_INTERFACE_LIST}) | |||
# Available OpenOCD targets | |||
set(OPENOCD_TARGET_LIST stm32l0.cfg stm32l1.cfg) | |||
set(OPENOCD_TARGET stm32l1.cfg CACHE STRING "Default OPENOCD Target is stm32l1.cfg") | |||
set_property(CACHE OPENOCD_TARGET PROPERTY STRINGS ${OPENOCD_TARGET_LIST}) | |||
# Available OpenOCD targets | |||
set(OPENOCD_BOARD_LIST atmel_saml21_xplained_pro.cfg ) | |||
set(OPENOCD_BOARD atmel_saml21_xplained_pro.cfg CACHE STRING "Default OPENOCD board is atmel_saml21_xplained_pro.cfg") | |||
set_property(CACHE OPENOCD_BOARD PROPERTY STRINGS ${OPENOCD_BOARD_LIST}) | |||
# Set the OPENOCD_TARGET and OPENOCD_INTERFACE variables according to BOARD | |||
if(BOARD STREQUAL NAMote72 OR BOARD STREQUAL NucleoL152) | |||
set(OPENOCD_INTERFACE stlink-v2-1.cfg) | |||
set(OPENOCD_TARGET stm32l1.cfg) | |||
elseif(BOARD STREQUAL NucleoL073 OR BOARD STREQUAL B-L072Z-LRWAN1) | |||
set(OPENOCD_INTERFACE stlink-v2-1.cfg) | |||
set(OPENOCD_TARGET stm32l0.cfg) | |||
elseif(BOARD STREQUAL NucleoL476) | |||
set(OPENOCD_INTERFACE stlink-v2-1.cfg) | |||
set(OPENOCD_TARGET stm32l4x.cfg) | |||
elseif(BOARD STREQUAL SKiM880B OR BOARD STREQUAL SKiM980A) | |||
set(OPENOCD_INTERFACE stlink-v2.cfg) | |||
set(OPENOCD_TARGET stm32l1.cfg) | |||
elseif(BOARD STREQUAL SKiM881AXL) | |||
set(OPENOCD_INTERFACE stlink-v2.cfg) | |||
set(OPENOCD_TARGET stm32l0.cfg) | |||
elseif(BOARD STREQUAL SAMR34) | |||
set(OPENOCD_INTERFACE cmsis-dap.cfg) | |||
set(OPENOCD_TARGET at91samdXX.cfg) | |||
endif() | |||
configure_file(${CURRENT_MODULE_DIR}/launch.json.in ${CMAKE_SOURCE_DIR}/.vscode/launch.json @ONLY) | |||
endfunction() |
@@ -0,0 +1,40 @@ | |||
{ | |||
"version": "0.2.0", | |||
"configurations": [ | |||
{ | |||
"name": "Debug-@TARGET_NAME@", | |||
"type": "cppdbg", | |||
"request": "launch", | |||
"program": "${workspaceRoot}/build/src/apps/@APPLICATION@/@TARGET_NAME@", | |||
"args": [], | |||
"stopAtEntry": true, | |||
"cwd": "${workspaceRoot}", | |||
"environment": [], | |||
"externalConsole": false, | |||
"debugServerArgs": "-f interface/@OPENOCD_INTERFACE@ -f target/@OPENOCD_TARGET@", | |||
"serverLaunchTimeout": 20000, | |||
"filterStderr": true, | |||
"filterStdout": false, | |||
"serverStarted": "Info\\ :\\ [\\w\\d\\.]*:\\ hardware", | |||
"setupCommands": [ | |||
{ "text": "cd ${workspaceRoot}/build" }, | |||
{ "text": "file src/apps/@APPLICATION@/@TARGET_NAME@", "description": "load file", "ignoreFailures": false}, | |||
{ "text": "target extended-remote localhost:3333", "description": "connect to target", "ignoreFailures": false }, | |||
{ "text": "monitor reset halt", "description": "perform a reset and halt the target", "ignoreFailures": false }, | |||
{ "text": "load", "description": "flash target", "ignoreFailures": false }, | |||
{ "text": "monitor reset init", "description": "perform a reset and init the target", "ignoreFailures": false }, | |||
{ "text": "set output-radix 16", "description": "set the default numeric base to 16", "ignoreFailures": false } | |||
], | |||
"logging": { | |||
"moduleLoad": true, | |||
"trace": true, | |||
"engineLogging": true, | |||
"programOutput": true, | |||
"exceptions": true | |||
}, | |||
"MIMode": "gdb", | |||
"miDebuggerPath": "@GDB_BIN@", | |||
"debugServerPath": "@OPENOCD_BIN@" | |||
} | |||
] | |||
} |
@@ -0,0 +1,5 @@ | |||
file @TARGET_NAME@ | |||
target extended-remote localhost:3333 | |||
monitor reset halt | |||
load | |||
thbreak main |
@@ -0,0 +1,41 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ), | |||
## Marten Lootsma(TWTG) on behalf of Microchip/Atmel (c)2017 | |||
## | |||
## | |||
## SAMR34 target specific CMake file | |||
## | |||
if(NOT DEFINED LINKER_SCRIPT) | |||
message(FATAL_ERROR "No linker script defined") | |||
endif(NOT DEFINED LINKER_SCRIPT) | |||
message("Linker script: ${LINKER_SCRIPT}") | |||
#--------------------------------------------------------------------------------------- | |||
# Set compiler/linker flags | |||
#--------------------------------------------------------------------------------------- | |||
# Object build options | |||
set(OBJECT_GEN_FLAGS "-Og -g -mthumb -g2 -fno-builtin -mcpu=cortex-m0plus -Wall -ffunction-sections -fdata-sections -fomit-frame-pointer -mabi=aapcs -fno-unroll-loops -ffast-math -ftree-vectorize -mlong-calls") | |||
set(CMAKE_C_FLAGS "${OBJECT_GEN_FLAGS} -std=gnu99 " CACHE INTERNAL "C Compiler options") | |||
set(CMAKE_CXX_FLAGS "${OBJECT_GEN_FLAGS} -std=c++11 " CACHE INTERNAL "C++ Compiler options") | |||
set(CMAKE_ASM_FLAGS "${OBJECT_GEN_FLAGS} -x assembler-with-cpp " CACHE INTERNAL "ASM Compiler options") | |||
# Linker flags | |||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections --specs=nano.specs --specs=nosys.specs -mthumb -g2 -mcpu=cortex-m0plus -mabi=aapcs -T${LINKER_SCRIPT} -Wl,-Map=${CMAKE_PROJECT_NAME}.map" CACHE INTERNAL "Linker options") |
@@ -0,0 +1,6 @@ | |||
file @TARGET_NAME@ | |||
target extended localhost:4242 | |||
monitor reset halt | |||
shell sleep 1 | |||
load | |||
thbreak main |
@@ -0,0 +1,40 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ) | |||
## | |||
## | |||
## STM32L0 target specific CMake file | |||
## | |||
if(NOT DEFINED LINKER_SCRIPT) | |||
message(FATAL_ERROR "No linker script defined") | |||
endif(NOT DEFINED LINKER_SCRIPT) | |||
message("Linker script: ${LINKER_SCRIPT}") | |||
#--------------------------------------------------------------------------------------- | |||
# Set compiler/linker flags | |||
#--------------------------------------------------------------------------------------- | |||
# Object build options | |||
set(OBJECT_GEN_FLAGS "-Og -g -mthumb -g2 -fno-builtin -mcpu=cortex-m0plus -Wall -Wextra -pedantic -Wno-unused-parameter -ffunction-sections -fdata-sections -fomit-frame-pointer -mabi=aapcs -fno-unroll-loops -ffast-math -ftree-vectorize") | |||
set(CMAKE_C_FLAGS "${OBJECT_GEN_FLAGS} -std=gnu99 " CACHE INTERNAL "C Compiler options") | |||
set(CMAKE_CXX_FLAGS "${OBJECT_GEN_FLAGS} -std=c++11 " CACHE INTERNAL "C++ Compiler options") | |||
set(CMAKE_ASM_FLAGS "${OBJECT_GEN_FLAGS} -x assembler-with-cpp " CACHE INTERNAL "ASM Compiler options") | |||
# Linker flags | |||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections --specs=nano.specs --specs=nosys.specs -mthumb -g2 -mcpu=cortex-m0plus -mabi=aapcs -T${LINKER_SCRIPT} -Wl,-Map=${CMAKE_PROJECT_NAME}.map" CACHE INTERNAL "Linker options") |
@@ -0,0 +1,40 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ) | |||
## | |||
## | |||
## STM32L1 target specific CMake file | |||
## | |||
if(NOT DEFINED LINKER_SCRIPT) | |||
message(FATAL_ERROR "No linker script defined") | |||
endif(NOT DEFINED LINKER_SCRIPT) | |||
message("Linker script: ${LINKER_SCRIPT}") | |||
#--------------------------------------------------------------------------------------- | |||
# Set compiler/linker flags | |||
#--------------------------------------------------------------------------------------- | |||
# Object build options | |||
set(OBJECT_GEN_FLAGS "-Og -g -mthumb -g2 -fno-builtin -mcpu=cortex-m3 -Wall -Wextra -pedantic -Wno-unused-parameter -ffunction-sections -fdata-sections -fomit-frame-pointer -mabi=aapcs -fno-unroll-loops -ffast-math -ftree-vectorize") | |||
set(CMAKE_C_FLAGS "${OBJECT_GEN_FLAGS} -std=gnu99 " CACHE INTERNAL "C Compiler options") | |||
set(CMAKE_CXX_FLAGS "${OBJECT_GEN_FLAGS} -std=c++11 " CACHE INTERNAL "C++ Compiler options") | |||
set(CMAKE_ASM_FLAGS "${OBJECT_GEN_FLAGS} -x assembler-with-cpp " CACHE INTERNAL "ASM Compiler options") | |||
# Linker flags | |||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections --specs=nano.specs --specs=nosys.specs -mthumb -g2 -mcpu=cortex-m3 -mabi=aapcs -T${LINKER_SCRIPT} -Wl,-Map=${CMAKE_PROJECT_NAME}.map" CACHE INTERNAL "Linker options") |
@@ -0,0 +1,40 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2018 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ) | |||
## | |||
## | |||
## STM32L4 target specific CMake file | |||
## | |||
if(NOT DEFINED LINKER_SCRIPT) | |||
message(FATAL_ERROR "No linker script defined") | |||
endif(NOT DEFINED LINKER_SCRIPT) | |||
message("Linker script: ${LINKER_SCRIPT}") | |||
#--------------------------------------------------------------------------------------- | |||
# Set compiler/linker flags | |||
#--------------------------------------------------------------------------------------- | |||
# Object build options | |||
set(OBJECT_GEN_FLAGS "-Og -g -mthumb -g2 -fno-builtin -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -Wall -Wextra -pedantic -Wno-unused-parameter -ffunction-sections -fdata-sections -fomit-frame-pointer -mabi=aapcs -fno-unroll-loops -ffast-math -ftree-vectorize") | |||
set(CMAKE_C_FLAGS "${OBJECT_GEN_FLAGS} -std=gnu99 " CACHE INTERNAL "C Compiler options") | |||
set(CMAKE_CXX_FLAGS "${OBJECT_GEN_FLAGS} -std=c++11 " CACHE INTERNAL "C++ Compiler options") | |||
set(CMAKE_ASM_FLAGS "${OBJECT_GEN_FLAGS} -x assembler-with-cpp " CACHE INTERNAL "ASM Compiler options") | |||
# Linker flags | |||
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections --specs=nano.specs --specs=nosys.specs -mthumb -g2 -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -mabi=aapcs -T${LINKER_SCRIPT} -Wl,-Map=${CMAKE_PROJECT_NAME}.map" CACHE INTERNAL "Linker options") |
@@ -0,0 +1,90 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ) | |||
## | |||
## | |||
## CMake arm-none-eabi toolchain file | |||
## | |||
# Append current directory to CMAKE_MODULE_PATH for making device specific cmake modules visible | |||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) | |||
# Target definition | |||
set(CMAKE_SYSTEM_NAME Generic) | |||
set(CMAKE_SYSTEM_PROCESSOR ARM) | |||
#--------------------------------------------------------------------------------------- | |||
# Set toolchain paths | |||
#--------------------------------------------------------------------------------------- | |||
set(TOOLCHAIN arm-none-eabi) | |||
if(NOT DEFINED TOOLCHAIN_PREFIX) | |||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) | |||
set(TOOLCHAIN_PREFIX "/usr") | |||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) | |||
set(TOOLCHAIN_PREFIX "/usr/local") | |||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) | |||
message(STATUS "Please specify the TOOLCHAIN_PREFIX !\n For example: -DTOOLCHAIN_PREFIX=\"C:/Program Files/GNU Tools ARM Embedded\" ") | |||
else() | |||
set(TOOLCHAIN_PREFIX "/usr") | |||
message(STATUS "No TOOLCHAIN_PREFIX specified, using default: " ${TOOLCHAIN_PREFIX}) | |||
endif() | |||
endif() | |||
set(TOOLCHAIN_BIN_DIR ${TOOLCHAIN_PREFIX}/bin) | |||
set(TOOLCHAIN_INC_DIR ${TOOLCHAIN_PREFIX}/${TOOLCHAIN}/include) | |||
set(TOOLCHAIN_LIB_DIR ${TOOLCHAIN_PREFIX}/${TOOLCHAIN}/lib) | |||
# Set system depended extensions | |||
if(WIN32) | |||
set(TOOLCHAIN_EXT ".exe" ) | |||
else() | |||
set(TOOLCHAIN_EXT "" ) | |||
endif() | |||
# Perform compiler test with static library | |||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) | |||
#--------------------------------------------------------------------------------------- | |||
# Preset some general GCC Options | |||
#--------------------------------------------------------------------------------------- | |||
# Options for DEBUG build | |||
# -Og enables optimizations that do not interfere with debugging | |||
# -g produce debugging information in the operating system’s native format | |||
set(CMAKE_C_FLAGS_DEBUG "-Og -g -DDEBUG" CACHE INTERNAL "C Compiler options for debug build type") | |||
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DDEBUG" CACHE INTERNAL "C++ Compiler options for debug build type") | |||
set(CMAKE_ASM_FLAGS_DEBUG "-g" CACHE INTERNAL "ASM Compiler options for debug build type") | |||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "" CACHE INTERNAL "Linker options for debug build type") | |||
# Options for RELEASE build | |||
# -Os Optimize for size. -Os enables all -O2 optimizations | |||
set(CMAKE_C_FLAGS_RELEASE "-Os" CACHE INTERNAL "C Compiler options for release build type") | |||
set(CMAKE_CXX_FLAGS_RELEASE "-Os" CACHE INTERNAL "C++ Compiler options for release build type") | |||
set(CMAKE_ASM_FLAGS_RELEASE "" CACHE INTERNAL "ASM Compiler options for release build type") | |||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "" CACHE INTERNAL "Linker options for release build type") | |||
#--------------------------------------------------------------------------------------- | |||
# Set compilers | |||
#--------------------------------------------------------------------------------------- | |||
set(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc${TOOLCHAIN_EXT} CACHE INTERNAL "C Compiler") | |||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-g++${TOOLCHAIN_EXT} CACHE INTERNAL "C++ Compiler") | |||
set(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc${TOOLCHAIN_EXT} CACHE INTERNAL "ASM Compiler") | |||
set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_PREFIX}/${${TOOLCHAIN}} ${CMAKE_PREFIX_PATH}) | |||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) | |||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) | |||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) | |||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) | |||
@@ -0,0 +1,3 @@ | |||
# ATECC608A-TNGLORA secure element module support documents | |||
[ATECC608A-TNGLORA](https://www.microchip.com/wwwproducts/en/ATECC608A-TNGLORA) acts as a HW secure element, see [The Things Industries reference repository](https://github.com/TheThingsIndustries/lorawan-example-atecc608a-tnglora) for further documentation and examples. |
@@ -0,0 +1,3 @@ | |||
# NAMote72 platform support documents | |||
* [NAMote72](https://os.mbed.com/platforms/NAMote-72/) |
@@ -0,0 +1,37 @@ | |||
# NucleoLxxx platforms support documents | |||
* [NucleoL073RZ](https://os.mbed.com/platforms/ST-Nucleo-L073RZ/) | |||
* [NucleoL152RE](https://os.mbed.com/platforms/ST-Nucleo-L152RE/) | |||
* [NucleoL476RG](https://os.mbed.com/platforms/ST-Nucleo-L476RG/) | |||
The following mbed shields may be used with NucleoLxxx platforms: | |||
* [SX1272MB2DAS](https://os.mbed.com/components/SX1272MB2xAS/) | |||
* [SX1276MB1LAS](https://os.mbed.com/components/SX1276MB1xAS/) | |||
* [SX1276MB1MAS](https://os.mbed.com/components/SX1276MB1xAS/) | |||
* [SX1261MB2BAS](https://os.mbed.com/components/SX126xMB2xAS/) | |||
* [SX1262MB2CAS](https://os.mbed.com/components/SX126xMB2xAS/) | |||
* [LR1110MB1DIS]() | |||
* [LR1110MB1DJS]() | |||
* [LR1110MB1GIS]() | |||
* [LR1110MB1GJS]() | |||
Development kit: | |||
* [SX126xDVK1xAS](https://os.mbed.com/components/SX126xDVK1xAS/) | |||
* SX1261DVK1BAS uses a SX1261MB1BAS shield | |||
* SX1262DVK1CAS uses a SX1262MB1CAS shield | |||
* SX1262DVK1DAS uses a SX1262MB1DAS shield | |||
* Other variants can also be used but require some adjustments | |||
_**Note**_: The MBX abbreviation used by the shield name variables(i.e: SX1261MBXBAS) on this project means that the shields named SX126xMB1xAS (SX126xDVK1xAS development kit only) and SX126xMB2xAS (standalone shields) are supported. | |||
* [LR1110DVK1TXKS]() | |||
* LR1110DVK1TBKS uses a LR1110MB1DIS or LR1110MB1DJS shield | |||
* LR1110DVK1TCKS uses a LR1110MB1DIS or LR1110MB1DJS shield | |||
* LR1110DVK1TGKS uses a LR1110MB1GJS or LR1110MB1GJS shield | |||
# Discovery kit platform support documents | |||
* [B-L072Z-LRWAN1](https://os.mbed.com/platforms/ST-Discovery-LRWAN1/) |
@@ -0,0 +1,5 @@ | |||
# SAMR34 platform support documents | |||
The supported SAMR34 platform can be evaluated via SAMR34-XPRO evaluation kit [User Guide](http://ww1.microchip.com/downloads/en/DeviceDoc/SAM-R34-Xplained-Pro-User-Guide-DS50002803C.pdf) | |||
The SAM R34 family data sheet can be found at [SAM R34 Family Data Sheet](http://ww1.microchip.com/downloads/en/DeviceDoc/SAMR34-R35-Low-Power-LoRa-Sub-GHz-SiP-Data-Sheet-DS70005356B.pdf) |
@@ -0,0 +1,6 @@ | |||
# SKiM88xx platforms support documents | |||
The supported SKiM88xx platforms are: | |||
1. [SK-iM880B - Long Range Radio Starter Kit](https://wireless-solutions.de/products/starterkits/sk-im880b.html) | |||
2. [SK-iM980A - Long Range Radio Starter Kit](https://wireless-solutions.de/products/long-range-radio/im980a.html) | |||
3. [SK-iM881A-XL - Long Range Radio Starter Kit](https://wireless-solutions.de/products/long-range-radio/im881a-xl.html) |
@@ -0,0 +1,260 @@ | |||
# Introduction | |||
This project uses CMake and the GNU ARM-Toolchain as build system and GDB/OpenOCD are used for debugging purposes. | |||
By using these tools the development environment is platform agnostic and independent of chip manufacturer specific Integrated Development Environments. | |||
It allows to build the project either by using a command line terminal or by using IDE's like [VSCode](#vscode) or [KDevelop](#kdevelop). | |||
# Prerequisites | |||
* CMake >= 3.6 | |||
* GNU/Linux: | |||
* Ubuntu 16.04/ Linux Mint 18: Since the official repository version is too old, one can use e.g. [PPA](https://launchpad.net/~adrozdoff/+archive/ubuntu/cmake) | |||
* Linux Arch: `pacman -S cmake` | |||
* Windows: | |||
* [CMake Download](https://cmake.org/download/) | |||
**Note**: Please use the latest full release and ensure that CMake path is added to the system path variable. (On Windows 10 search for _Environment Variables_ at start menu and add your CMake installation path e.g. `C:\Program Files\CMake\bin` to `Path` variable ) | |||
* OSX: | |||
* Homebrew: `brew install cmake` | |||
* GNU ARM-Toolchain | |||
* GNU/Linux: | |||
* Ubuntu 16.04/ Linux Mint 18: Since the official repository version is too old, one can use e.g. [PPA](https://launchpad.net/~team-gcc-arm-embedded/+archive/ubuntu/ppa) | |||
* Ubuntu 18.04: the toolchain has been updated but there is a bug with [`libnewlib`](https://github.com/bbcmicrobit/micropython/issues/514#issuecomment-404759614) causing the linker to fail. `sudo apt install gcc-arm-none-eabi` | |||
* Linux Arch: `pacman -S arm-none-eabi-gcc arm-none-eabi-newlib` | |||
* Windows: | |||
* [GNU Arm Embedded Toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm) | |||
* The Make utility is also required, one can use e.g. [MSYS2](http://www.msys2.org) or [MinGW](http://mingw.org/) | |||
* With MSYS2: | |||
* Follow the installation instructions provided by them. | |||
* Install MinGW Make with `pacman -S mingw-w64-x86_64-make`. | |||
**Note**: Please ensure that both paths are added to the system PATH variable. Add for example: `C:\msys64\mingw64\bin` and `C:\Program Files (x86)\GNU Tools ARM Embedded\6 2017-q2-update\bin` to `Path` variable. | |||
* OSX: | |||
* Homebrew: `brew tap ARMmbed/homebrew-formulae && brew install arm-none-eabi-gcc` | |||
* OpenOCD | |||
* GNU/Linux: | |||
* Ubuntu 16.04/ Linux Mint 18: `apt-get install openocd` | |||
* Linux Arch: `pacman -S openocd` | |||
* Windows: | |||
* Unofficial binary packages are available [here](http://openocd.org/getting-openocd/) for download | |||
**Note**: The debug configuration for [VSCode](#vscode) will assume that OpenOCD is installed under `C:/openocd`. If it is not the case one must change the `OPENOCD_BIN` variable according to right location. | |||
* OSX: | |||
* Homebrew: `brew install openocd` | |||
# Commandline build instructions | |||
1. Go to root directory of the project | |||
`cd path/to/project/directory` | |||
2. Create a directory named 'build' | |||
`mkdir build` | |||
3. Go to the created `build` directory | |||
`cd build` | |||
4. run | |||
`cmake -DCMAKE_TOOLCHAIN_FILE="cmake/toolchain-arm-none-eabi.cmake" ..` | |||
**Note**: If the GNU ARM-Toolchain is not installed under the default path (GNU Linux:`/usr`, Mac OS `/usr/local`) a prefix has to be provided: | |||
`cmake -DCMAKE_TOOLCHAIN_FILE="cmake/toolchain-arm-none-eabi.cmake" -DTOOLCHAIN_PREFIX="/path/to/the/toolchain" ..` | |||
For Windows platforms the prefix has to be provided anyway and additionally the CMake Generator for MinGW Makefiles has to be chosen: | |||
`cmake -DCMAKE_TOOLCHAIN_FILE="cmake/toolchain-arm-none-eabi.cmake" -DTOOLCHAIN_PREFIX="/path/to/the/toolchain" -G "MinGW Makefiles" ..` | |||
## Available configuration options for CMake | |||
The possibility to choose the application, target board and more options can be done using the provided configuration options. | |||
These configuration options can be set through additional commandline parameters, for example: | |||
`cmake -DCMAKE_TOOLCHAIN_FILE="cmake/toolchain-arm-none-eabi.cmake" -DAPPLICATION="LoRaMac" -DSUB_PROJECT="periodic-uplink-lpp" ..` | |||
Alternatively one can use a graphical interface to configure CMake, drop down menus and check boxes will provide to the user the possible options. | |||
* CMake QT GUI with `cmake-gui ..` | |||
* CMake curses interface with `ccmake ..` | |||
### Options that can be choose by the user | |||
* `APPLICATION` - Application example choice. | |||
The possible choices are: | |||
* LoRaMac (Default) | |||
* ping-pong | |||
* rx-sensi | |||
* tx-cw | |||
* `SUB_PROJECT` - LoRaMac sub project example choice. | |||
**Note**: Only applicable to LoRaMac `APPLICATION` choice. | |||
The possible choices are: | |||
* periodic-uplink-lpp | |||
* fuota-test-01 | |||
* `ACTIVE_REGION` - Active region for which the stack will be initialized. | |||
**Note**: Only applicable to LoRaMac `APPLICATION` choice. | |||
The possible choices are: | |||
* LORAMAC_REGION_EU868 | |||
* LORAMAC_REGION_US915 | |||
* LORAMAC_REGION_CN779 | |||
* LORAMAC_REGION_EU433 | |||
* LORAMAC_REGION_AU915 | |||
* LORAMAC_REGION_AS923 | |||
* LORAMAC_REGION_CN470 | |||
* LORAMAC_REGION_KR920 | |||
* LORAMAC_REGION_IN865 | |||
* LORAMAC_REGION_RU864 | |||
* `MODULATION` - Type of modulation choice. | |||
**Note**: Only applicable to ping-pong or rx-sensi `APPLICATION` choice. | |||
The possible choices are: | |||
* LORA | |||
* FSK | |||
* `USE_DEBUGGER`- Enables debugger support. (Default ON) | |||
* `BOARD` - Target board choice. | |||
The possible choices are: | |||
* NAMote72 | |||
* NucleoL073 (default) | |||
* NucleoL152 | |||
* NucleoL476 | |||
* SAMR34 | |||
* SKiM880B | |||
* SKiM980A | |||
* SKiM881AXL | |||
* `REGION_EU868` - Enables support for the Region EU868 (Default ON) | |||
* `REGION_US915` - Enables support for the Region US915 (Default OFF) | |||
* `REGION_CN779` - Enables support for the Region CN779 (Default OFF) | |||
* `REGION_EU433` - Enables support for the Region EU433 (Default OFF) | |||
* `REGION_AU915` - Enables support for the Region AU915 (Default OFF) | |||
* `REGION_AS923` - Enables support for the Region AS923 (Default OFF) | |||
* `REGION_CN470` - Enables support for the Region CN470 (Default OFF) | |||
* `REGION_KR920` - Enables support for the Region IN865 (Default OFF) | |||
* `REGION_IN865` - Enables support for the Region AS923 (Default OFF) | |||
* `REGION_RU864` - Enables support for the Region RU864 (Default OFF) | |||
### Options that are automatically set | |||
* `RADIO` - Defines the radio to be used. | |||
The possible choices are: | |||
* sx1272 | |||
* sx1276 | |||
* `LINKER_SCRIPT` - Defines the target specific linker script path. | |||
* `OPENOCD_BIN` - Defines the OpenOCD path. | |||
* `OPENOCD_INTERFACE` - Defines the interface configuration file to be used by OpenOCD. | |||
* `OPENOCD_TARGET` - Defines the target configuration file to be used by OpenOCD. | |||
# Debugging | |||
1. OpenOCD | |||
OpenOCD has to be started with parameters that depend on the used debugger device and target board. | |||
Some examples are shown below: | |||
* NucleoL073 + STLinkV2-1 (On board debugger): | |||
`openocd -f interface/stlink-v2-1.cfg -f target/stm32l0.cfg` | |||
* SAMR34 Xplained Pro (On board debugger, tested with openocd 0.10, did not work with 0.9): | |||
`openocd -f interface/cmsis-dap.cfg -f target/at91samdXX.cfg` | |||
2. GDB | |||
The below GDB usage example shows how to start a debug session, writing the program to the flash and run. | |||
* Run ARM-GNU GDB with: | |||
`arm-none-eabi-gdb` | |||
* Choose the program you want to debug: | |||
`file src/apps/LoRaMac/LoRaMac-classA` | |||
* Connect GDB to OpenOCD: | |||
`target extended-remote localhost:3333` | |||
* Execute a reset and halt of the target: | |||
`monitor reset halt` | |||
* Flash the program to the target Flash memory: | |||
`load` | |||
* Add a one-time break point at main: | |||
`thbreak main` | |||
* Run the program until the break point: | |||
`continue` | |||
* Finally run the program: | |||
`continue` | |||
# IDE Support | |||
## VSCode | |||
### Additional Prerequisites | |||
* Visual Studio Code: | |||
* GNU/Linux, Windows and OSX: | |||
* [Download](https://code.visualstudio.com/Download) | |||
* Extensions: Open `VSCode ->EXTENSION (Crtl+Shift+x)` and search for: | |||
* C/C++ | |||
* CMake | |||
* CMake Tools | |||
* Native Debug | |||
### Configuration | |||
For Windows platforms it is necessary to make some additional configurations. Open your settings under *File->Preferences->Settings* and add the following lines: | |||
Add MinGW Makefiles as preferred Generator: | |||
```json | |||
"cmake.preferredGenerators": [ | |||
"MinGW Makefiles", | |||
"Ninja", | |||
"Unix Makefiles" | |||
] | |||
``` | |||
Set the CMake path: | |||
```json | |||
"cmake.cmakePath": "path/to/cmake.exe" | |||
``` | |||
### Usage | |||
1. Open the directory of the cloned repository. | |||
The *CMake Tools* extension will automatically generate a *`.cmaketools.json`* file based on the CMakeLists. | |||
2. The *`settings.json`* file under `.vscode` directory is the place where one can change the build options. | |||
These are the build options that will be provided to CMake. | |||
Please see chapter [Commandline Build Instructions](#commandline-build-instructions) for information about build options. | |||
3. Click on the blue status bar of *CMake Tools* to choose a build type (`Debug` or `Release`). | |||
A CMake configuration process will be performed. | |||
4. A `Build` button will now be available. | |||
Click this button to build the target. | |||
5. The CMake build system will automatically generate a *`launch.json`* file which setups the debugging process for the given board. | |||
6. Press the `F5` key to start a debug session. | |||
This will automatically start the GDB and OpenOCD processes. | |||
### Useful Hints | |||
* Change CMake options: Open the Command palette (Crtl+Shift+P) and type `CMake: Edit the CMake Cache` | |||
* Execute a clean rebuild: Open the Command palette (Crtl+Shift+P) and type `CMake: Clean rebuild` | |||
For detailed information about CMake Tools extension please see their [github repository](https://github.com/vector-of-bool/vscode-cmake-tools). | |||
## KDevelop | |||
### Additional Prerequisites | |||
* KDevelop: | |||
* GNU/Linux: | |||
* Ubuntu 16.04/ Linux Mint 18: `apt-get install kdevelop` | |||
* Linux Arch: `pacman -S kdevelop` | |||
* Windows: | |||
* [KDevelop Download](https://www.kdevelop.org/download) | |||
**Note**: Currently there is no GDB support but it is planned for future releases. | |||
* OSX: | |||
* [KDevelop Download](https://www.kdevelop.org/download). | |||
No official binaries are available. Must be built from source code. | |||
### Usage | |||
1. To open the project click on *`Project->Open /Import Project...`* and choose the top level `CMakeLists.txt` directory of the cloned repository. | |||
Follow the indications to setup the project and add `-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm-none-eabi.cmake` to the *`Extra Arguments`*. | |||
2. The CMake options and variables can be changed by right-clicking on project and selecting *`Open configuration...`*. | |||
A graphical interface will pop-up. | |||
Please see chapter [Commandline Build Instructions](#commandline-build-instructions) for information about build options. | |||
3. Click on `Build` to build the project. | |||
4. Create a launch configuration for debugging: | |||
* Click on *`Run->Configure Launches...`* and add a new *`Compiled Binary Launcher`*. | |||
* Set the field *`Debugger executable`* according to your system. For example `/usr/bin/arm-none-eabi-gdb` . | |||
* Choose the `Run gdb script` according to the application you want to debug. | |||
For example: `loramac-node/build/src/apps/LoRaMac/openocd-run.gdb`. | |||
**Note**: The CMake build system will automatically generate the GDB run script. | |||
5. Start OpenOCD in a command line terminal as described on chapter [Debugging](#debugging). | |||
6. Click on "Debug" to launch a debug session. |
@@ -0,0 +1,222 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder (STACKFORCE), Miguel Luis (Semtech) | |||
## | |||
project(lora-mac) | |||
cmake_minimum_required(VERSION 3.6) | |||
#--------------------------------------------------------------------------------------- | |||
# Options | |||
#--------------------------------------------------------------------------------------- | |||
# Allow switching of target platform | |||
set(BOARD_LIST NAMote72 NucleoL073 NucleoL152 NucleoL476 SAMR34 SKiM880B SKiM980A SKiM881AXL B-L072Z-LRWAN1) | |||
set(BOARD NucleoL073 CACHE STRING "Default target platform is NucleoL073") | |||
set_property(CACHE BOARD PROPERTY STRINGS ${BOARD_LIST}) | |||
# Allow switching of MBED shields | |||
set(MBED_RADIO_SHIELD_LIST SX1272MB2DAS SX1276MB1LAS SX1276MB1MAS SX1261MBXBAS SX1262MBXCAS SX1262MBXDAS ) | |||
set(MBED_RADIO_SHIELD SX1261MBXBAS CACHE STRING "Default MBED radio shield is SX1261MBXBAS") | |||
set_property(CACHE MBED_RADIO_SHIELD PROPERTY STRINGS ${MBED_RADIO_SHIELD_LIST}) | |||
# Allow switching of secure-elements | |||
set(SECURE_ELEMENT_LIST SOFT_SE LR1110_SE) | |||
set(SECURE_ELEMENT SOFT_SE CACHE STRING "Default secure element is SOFT_SE") | |||
set_property(CACHE SECURE_ELEMENT PROPERTY STRINGS ${SECURE_ELEMENT_LIST}) | |||
# Allow switching of Applications | |||
set(APPLICATION_LIST LoRaMac ping-pong rx-sensi tx-cw ) | |||
set(APPLICATION LoRaMac CACHE STRING "Default Application is LoRaMac") | |||
set_property(CACHE APPLICATION PROPERTY STRINGS ${APPLICATION_LIST}) | |||
# Switch for USB-Uart support, enable it for some Applications who needs it. | |||
option(USE_USB_CDC "Use USB-Uart" OFF) | |||
# Switch for debugger support. | |||
option(USE_DEBUGGER "Use Debugger" ON) | |||
# Switch for Class B support of LoRaMac. | |||
option(CLASSB_ENABLED "Class B support of LoRaMac" OFF) | |||
#--------------------------------------------------------------------------------------- | |||
# Target Boards | |||
#--------------------------------------------------------------------------------------- | |||
if(BOARD STREQUAL NAMote72) | |||
# Configure toolchain for NAMote72 | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/NAMote72/cmsis/arm-gcc/stm32l152xc_flash.ld) | |||
include(stm32l1) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/NAMote72) | |||
# Configure radio | |||
set(RADIO sx1272 CACHE INTERNAL "Radio sx1272 selected") | |||
elseif(BOARD STREQUAL NucleoL073) | |||
# Configure toolchain for NucleoL073 | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/NucleoL073/cmsis/arm-gcc/stm32l073xx_flash.ld) | |||
include(stm32l0) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/NucleoL073) | |||
# Configure radio | |||
if(MBED_RADIO_SHIELD STREQUAL SX1272MB2DAS) | |||
set(RADIO sx1272 CACHE INTERNAL "Radio sx1272 selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL SX1276MB1LAS OR MBED_RADIO_SHIELD STREQUAL SX1276MB1MAS) | |||
set(RADIO sx1276 CACHE INTERNAL "Radio sx1276 selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL SX1261MBXBAS OR MBED_RADIO_SHIELD STREQUAL SX1262MBXCAS OR MBED_RADIO_SHIELD STREQUAL SX1262MBXDAS) | |||
set(RADIO sx126x CACHE INTERNAL "Radio sx126x selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL LR1110MB1XXS) | |||
set(RADIO lr1110 CACHE INTERNAL "Radio lr1110 selected") | |||
else() | |||
message(STATUS "Please specify the MBED_RADIO_SHIELD!\nPossible values are: SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS, SX1262MBXCAS, SX1262MBXDAS and LR1110MB1XXS.") | |||
endif() | |||
elseif(BOARD STREQUAL NucleoL152) | |||
# Configure toolchain for NucleoL152 | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/NucleoL152/cmsis/arm-gcc/stm32l152xe_flash.ld) | |||
include(stm32l1) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/NucleoL152) | |||
# Configure radio | |||
if(MBED_RADIO_SHIELD STREQUAL SX1272MB2DAS) | |||
set(RADIO sx1272 CACHE INTERNAL "Radio sx1272 selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL SX1276MB1LAS OR MBED_RADIO_SHIELD STREQUAL SX1276MB1MAS) | |||
set(RADIO sx1276 CACHE INTERNAL "Radio sx1276 selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL SX1261MBXBAS OR MBED_RADIO_SHIELD STREQUAL SX1262MBXCAS OR MBED_RADIO_SHIELD STREQUAL SX1262MBXDAS) | |||
set(RADIO sx126x CACHE INTERNAL "Radio sx126x selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL LR1110MB1XXS) | |||
set(RADIO lr1110 CACHE INTERNAL "Radio lr1110 selected") | |||
else() | |||
message(STATUS "Please specify the MBED_RADIO_SHIELD!\nPossible values are: SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS, SX1262MBXCAS, SX1262MBXDAS and LR1110MB1XXS.") | |||
endif() | |||
elseif(BOARD STREQUAL NucleoL476) | |||
# Configure toolchain for NucleoL476 | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/NucleoL476/cmsis/arm-gcc/stm32l476rgtx_flash.ld) | |||
include(stm32l4) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/NucleoL476) | |||
# Configure radio | |||
if(MBED_RADIO_SHIELD STREQUAL SX1272MB2DAS) | |||
set(RADIO sx1272 CACHE INTERNAL "Radio sx1272 selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL SX1276MB1LAS OR MBED_RADIO_SHIELD STREQUAL SX1276MB1MAS) | |||
set(RADIO sx1276 CACHE INTERNAL "Radio sx1276 selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL SX1261MBXBAS OR MBED_RADIO_SHIELD STREQUAL SX1262MBXCAS OR MBED_RADIO_SHIELD STREQUAL SX1262MBXDAS) | |||
set(RADIO sx126x CACHE INTERNAL "Radio sx126x selected") | |||
elseif(MBED_RADIO_SHIELD STREQUAL LR1110MB1XXS) | |||
set(RADIO lr1110 CACHE INTERNAL "Radio lr1110 selected") | |||
else() | |||
message(STATUS "Please specify the MBED_RADIO_SHIELD!\nPossible values are: SX1272MB2DAS, SX1276MB1LAS, SX1276MB1MAS, SX1261MBXBAS, SX1262MBXCAS, SX1262MBXDAS and LR1110MB1XXS.") | |||
endif() | |||
elseif(BOARD STREQUAL SAMR34) | |||
# Configure toolchain for SAMR34 | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/mcu/saml21/saml21b/gcc/gcc/saml21j18b_flash.ld) | |||
include(samr34) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/SAMR34) | |||
# Configure radio | |||
set(RADIO sx1276 CACHE INTERNAL "Radio sx1276 selected") | |||
elseif(BOARD STREQUAL SKiM880B) | |||
# Configure toolchain for SKiM881AXL | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/SKiM880B/cmsis/arm-gcc/stm32l151xba_flash.ld) | |||
include(stm32l1) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/SKiM880B) | |||
# Configure radio | |||
set(RADIO sx1272 CACHE INTERNAL "Radio sx1272 selected") | |||
elseif(BOARD STREQUAL SKiM980A) | |||
# Configure toolchain for SKiM881AXL | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/SKiM980A/cmsis/arm-gcc/stm32l151xba_flash.ld) | |||
include(stm32l1) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/SKiM980A) | |||
# Configure radio | |||
set(RADIO sx1272 CACHE INTERNAL "Radio sx1272 selected") | |||
elseif(BOARD STREQUAL SKiM881AXL) | |||
# Configure toolchain for SKiM881AXL | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/SKiM881AXL/cmsis/arm-gcc/stm32l081xx_flash.ld) | |||
include(stm32l0) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/SKiM881AXL) | |||
# Configure radio | |||
set(RADIO sx1272 CACHE INTERNAL "Radio sx1272 selected") | |||
elseif(BOARD STREQUAL B-L072Z-LRWAN1) | |||
# Configure toolchain for B-L072Z-LRWAN1 | |||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/boards/B-L072Z-LRWAN1/cmsis/arm-gcc/stm32l072xx_flash.ld) | |||
include(stm32l0) | |||
# Build platform specific board implementation | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards/B-L072Z-LRWAN1) | |||
# Configure radio | |||
set(RADIO sx1276 CACHE INTERNAL "Radio sx1276 selected") | |||
endif() | |||
#--------------------------------------------------------------------------------------- | |||
# General Components | |||
#--------------------------------------------------------------------------------------- | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/boards) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/radio) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/system) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mac) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/peripherals) | |||
#--------------------------------------------------------------------------------------- | |||
# Applications | |||
#--------------------------------------------------------------------------------------- | |||
if(APPLICATION STREQUAL LoRaMac) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/apps/LoRaMac) | |||
elseif(APPLICATION STREQUAL ping-pong) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/apps/ping-pong) | |||
elseif(APPLICATION STREQUAL rx-sensi) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/apps/rx-sensi) | |||
elseif(APPLICATION STREQUAL tx-cw) | |||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/apps/tx-cw) | |||
endif() |
@@ -0,0 +1,170 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder (STACKFORCE), Miguel Luis (Semtech) | |||
## | |||
project(LoRaMac) | |||
cmake_minimum_required(VERSION 3.6) | |||
#--------------------------------------------------------------------------------------- | |||
# Options | |||
#--------------------------------------------------------------------------------------- | |||
# Allow switching of sub projects | |||
set(SUB_PROJECT_LIST periodic-uplink-lpp fuota-test-01) | |||
set(SUB_PROJECT periodic-uplink-lpp CACHE STRING "Default sub project is periodic-uplink-lpp") | |||
set_property(CACHE SUB_PROJECT PROPERTY STRINGS ${SUB_PROJECT_LIST}) | |||
# Allow switching of active region | |||
set(ACTIVE_REGION_LIST LORAMAC_REGION_EU868 LORAMAC_REGION_US915 LORAMAC_REGION_CN779 | |||
LORAMAC_REGION_EU433 LORAMAC_REGION_AU915 LORAMAC_REGION_AS923 LORAMAC_REGION_CN470 | |||
LORAMAC_REGION_KR920 LORAMAC_REGION_IN865 LORAMAC_REGION_RU864 | |||
) | |||
set(ACTIVE_REGION LORAMAC_REGION_EU868 CACHE STRING "Default active region is EU868") | |||
set_property(CACHE ACTIVE_REGION PROPERTY STRINGS ${ACTIVE_REGION_LIST}) | |||
if((SUB_PROJECT STREQUAL periodic-uplink-lpp OR SUB_PROJECT STREQUAL fuota-test-01) AND NOT CLASSB_ENABLED ) | |||
message(FATAL_ERROR "Please turn on Class B support of LoRaMac ( CLASSB_ENABLED=ON ) to use periodic-uplink-lpp, fuota-test-01 sub projects") | |||
endif() | |||
# Allow selection of secure-element provisioning method | |||
option(SECURE_ELEMENT_PRE_PROVISIONED "Secure-element pre-provisioning" ON) | |||
if(SUB_PROJECT STREQUAL periodic-uplink-lpp) | |||
#--------------------------------------------------------------------------------------- | |||
# Application common features handling | |||
#--------------------------------------------------------------------------------------- | |||
list(APPEND ${PROJECT_NAME}_COMMON | |||
"${CMAKE_CURRENT_LIST_DIR}/common/CayenneLpp.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/cli.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandlerMsgDisplay.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/NvmDataMgmt.c" | |||
) | |||
#--------------------------------------------------------------------------------------- | |||
# Application LoRaMac handler | |||
#--------------------------------------------------------------------------------------- | |||
list(APPEND ${PROJECT_NAME}_LMH | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/LmHandler.c" | |||
) | |||
#--------------------------------------------------------------------------------------- | |||
# LoRaMac handler applicative packages | |||
#--------------------------------------------------------------------------------------- | |||
list(APPEND ${PROJECT_NAME}_LMHP | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/FragDecoder.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpClockSync.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpCompliance.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpFragmentation.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpRemoteMcastSetup.c" | |||
) | |||
elseif(SUB_PROJECT STREQUAL fuota-test-01) | |||
#--------------------------------------------------------------------------------------- | |||
# Application common features handling | |||
#--------------------------------------------------------------------------------------- | |||
list(APPEND ${PROJECT_NAME}_COMMON | |||
"${CMAKE_CURRENT_LIST_DIR}/common/cli.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandlerMsgDisplay.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/NvmDataMgmt.c" | |||
) | |||
#--------------------------------------------------------------------------------------- | |||
# Application LoRaMac handler | |||
#--------------------------------------------------------------------------------------- | |||
list(APPEND ${PROJECT_NAME}_LMH | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/LmHandler.c" | |||
) | |||
#--------------------------------------------------------------------------------------- | |||
# LoRaMac handler applicative packages | |||
#--------------------------------------------------------------------------------------- | |||
list(APPEND ${PROJECT_NAME}_LMHP | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/FragDecoder.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpClockSync.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpCompliance.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpFragmentation.c" | |||
"${CMAKE_CURRENT_LIST_DIR}/common/LmHandler/packages/LmhpRemoteMcastSetup.c" | |||
) | |||
else() | |||
message(FATAL_ERROR "Unknown SUB_PROJECT") | |||
endif() | |||
#--------------------------------------------------------------------------------------- | |||
# Application | |||
#--------------------------------------------------------------------------------------- | |||
file(GLOB ${PROJECT_NAME}_SOURCES "${CMAKE_CURRENT_LIST_DIR}/${SUB_PROJECT}/${BOARD}/*.c") | |||
add_executable(${PROJECT_NAME}-${SUB_PROJECT} | |||
${${PROJECT_NAME}_COMMON} | |||
${${PROJECT_NAME}_LMH} | |||
${${PROJECT_NAME}_LMHP} | |||
${${PROJECT_NAME}_SOURCES} | |||
$<TARGET_OBJECTS:mac> | |||
$<TARGET_OBJECTS:system> | |||
$<TARGET_OBJECTS:radio> | |||
$<TARGET_OBJECTS:peripherals> | |||
$<TARGET_OBJECTS:${BOARD}> | |||
) | |||
target_compile_definitions(${PROJECT_NAME}-${SUB_PROJECT} PRIVATE $<$<BOOL:${CLASSB_ENABLED}>:LORAMAC_CLASSB_ENABLED>) | |||
target_compile_definitions(${PROJECT_NAME}-${SUB_PROJECT} PRIVATE ACTIVE_REGION=${ACTIVE_REGION}) | |||
if(${SECURE_ELEMENT_PRE_PROVISIONED} MATCHES ON) | |||
target_compile_definitions(${PROJECT_NAME}-${SUB_PROJECT} PRIVATE -DSECURE_ELEMENT_PRE_PROVISIONED) | |||
endif() | |||
if(${SECURE_ELEMENT} MATCHES SOFT_SE) | |||
target_compile_definitions(${PROJECT_NAME}-${SUB_PROJECT} PRIVATE -DSOFT_SE) | |||
endif() | |||
target_compile_definitions(${PROJECT_NAME}-${SUB_PROJECT} PUBLIC | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:mac,INTERFACE_COMPILE_DEFINITIONS>> | |||
) | |||
target_include_directories(${PROJECT_NAME}-${SUB_PROJECT} PUBLIC | |||
${CMAKE_CURRENT_SOURCE_DIR}/common | |||
${CMAKE_CURRENT_SOURCE_DIR}/common/LmHandler | |||
${CMAKE_CURRENT_SOURCE_DIR}/common/LmHandler/packages | |||
${CMAKE_CURRENT_SOURCE_DIR}/${SUB_PROJECT}/${BOARD} | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:mac,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:system,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:radio,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:peripherals,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:${BOARD},INTERFACE_INCLUDE_DIRECTORIES>> | |||
) | |||
set_property(TARGET ${PROJECT_NAME}-${SUB_PROJECT} PROPERTY C_STANDARD 11) | |||
target_link_libraries(${PROJECT_NAME}-${SUB_PROJECT} m) | |||
#--------------------------------------------------------------------------------------- | |||
# Debugging and Binutils | |||
#--------------------------------------------------------------------------------------- | |||
include(gdb-helper) | |||
include(binutils-arm-none-eabi) | |||
# Generate debugger configurations | |||
generate_run_gdb_stlink(${PROJECT_NAME}-${SUB_PROJECT}) | |||
generate_run_gdb_openocd(${PROJECT_NAME}-${SUB_PROJECT}) | |||
generate_vscode_launch_openocd(${PROJECT_NAME}-${SUB_PROJECT}) | |||
# Print section sizes of target | |||
print_section_sizes(${PROJECT_NAME}-${SUB_PROJECT}) | |||
# Create output in hex and binary format | |||
create_bin_output(${PROJECT_NAME}-${SUB_PROJECT}) | |||
create_hex_output(${PROJECT_NAME}-${SUB_PROJECT}) |
@@ -0,0 +1,257 @@ | |||
/*! | |||
* \file CayenneLpp.c | |||
* | |||
* \brief Implements the Cayenne Low Power Protocol | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#include <stdint.h> | |||
#include "utilities.h" | |||
#include "CayenneLpp.h" | |||
#define CAYENNE_LPP_MAXBUFFER_SIZE 242 | |||
static uint8_t CayenneLppBuffer[CAYENNE_LPP_MAXBUFFER_SIZE]; | |||
static uint8_t CayenneLppCursor = 0; | |||
void CayenneLppInit( void ) | |||
{ | |||
CayenneLppCursor = 0; | |||
} | |||
void CayenneLppReset( void ) | |||
{ | |||
CayenneLppCursor = 0; | |||
} | |||
uint8_t CayenneLppGetSize( void ) | |||
{ | |||
return CayenneLppCursor; | |||
} | |||
uint8_t* CayenneLppGetBuffer( void ) | |||
{ | |||
return CayenneLppBuffer; | |||
} | |||
uint8_t CayenneLppCopy( uint8_t* dst ) | |||
{ | |||
memcpy1( dst, CayenneLppBuffer, CayenneLppCursor ); | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddDigitalInput( uint8_t channel, uint8_t value ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_DIGITAL_INPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_DIGITAL_INPUT; | |||
CayenneLppBuffer[CayenneLppCursor++] = value; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddDigitalOutput( uint8_t channel, uint8_t value ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_DIGITAL_OUTPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_DIGITAL_OUTPUT; | |||
CayenneLppBuffer[CayenneLppCursor++] = value; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddAnalogInput( uint8_t channel, float value ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_ANALOG_INPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
int16_t val = ( int16_t ) ( value * 100 ); | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_ANALOG_INPUT; | |||
CayenneLppBuffer[CayenneLppCursor++] = val >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = val; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddAnalogOutput( uint8_t channel, float value ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_ANALOG_OUTPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
int16_t val = ( int16_t ) ( value * 100 ); | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_ANALOG_OUTPUT; | |||
CayenneLppBuffer[CayenneLppCursor++] = val >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = val; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddLuminosity( uint8_t channel, uint16_t lux ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_LUMINOSITY_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_LUMINOSITY; | |||
CayenneLppBuffer[CayenneLppCursor++] = lux >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = lux; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddPresence( uint8_t channel, uint8_t value ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_PRESENCE_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_PRESENCE; | |||
CayenneLppBuffer[CayenneLppCursor++] = value; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddTemperature( uint8_t channel, float celsius ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_TEMPERATURE_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
int16_t val = ( int16_t) ( celsius * 10 ); | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_TEMPERATURE; | |||
CayenneLppBuffer[CayenneLppCursor++] = val >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = val; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddRelativeHumidity( uint8_t channel, float rh ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_RELATIVE_HUMIDITY_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_RELATIVE_HUMIDITY; | |||
CayenneLppBuffer[CayenneLppCursor++] = (uint8_t ) ( rh * 2 ); | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddAccelerometer( uint8_t channel, float x, float y, float z ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_ACCELEROMETER_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
int16_t vx = ( int16_t ) ( x * 1000 ); | |||
int16_t vy = ( int16_t ) ( y * 1000 ); | |||
int16_t vz = ( int16_t ) ( z * 1000 ); | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_ACCELEROMETER; | |||
CayenneLppBuffer[CayenneLppCursor++] = vx >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = vx; | |||
CayenneLppBuffer[CayenneLppCursor++] = vy >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = vy; | |||
CayenneLppBuffer[CayenneLppCursor++] = vz >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = vz; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddBarometricPressure( uint8_t channel, float hpa ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_BAROMETRIC_PRESSURE_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
int16_t val = ( int16_t ) ( hpa * 10 ); | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_BAROMETRIC_PRESSURE; | |||
CayenneLppBuffer[CayenneLppCursor++] = val >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = val; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddGyrometer( uint8_t channel, float x, float y, float z ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_GYROMETER_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
int16_t vx = ( int16_t ) ( x * 100 ); | |||
int16_t vy = ( int16_t ) ( y * 100 ); | |||
int16_t vz = ( int16_t ) ( z * 100 ); | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_GYROMETER; | |||
CayenneLppBuffer[CayenneLppCursor++] = vx >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = vx; | |||
CayenneLppBuffer[CayenneLppCursor++] = vy >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = vy; | |||
CayenneLppBuffer[CayenneLppCursor++] = vz >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = vz; | |||
return CayenneLppCursor; | |||
} | |||
uint8_t CayenneLppAddGps( uint8_t channel, float latitude, float longitude, float meters ) | |||
{ | |||
if( ( CayenneLppCursor + LPP_GPS_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) | |||
{ | |||
return 0; | |||
} | |||
int32_t lat = ( int32_t ) ( latitude * 10000 ); | |||
int32_t lon = ( int32_t ) ( longitude * 10000 ); | |||
int32_t alt = ( int32_t ) ( meters * 100 ); | |||
CayenneLppBuffer[CayenneLppCursor++] = channel; | |||
CayenneLppBuffer[CayenneLppCursor++] = LPP_GPS; | |||
CayenneLppBuffer[CayenneLppCursor++] = lat >> 16; | |||
CayenneLppBuffer[CayenneLppCursor++] = lat >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = lat; | |||
CayenneLppBuffer[CayenneLppCursor++] = lon >> 16; | |||
CayenneLppBuffer[CayenneLppCursor++] = lon >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = lon; | |||
CayenneLppBuffer[CayenneLppCursor++] = alt >> 16; | |||
CayenneLppBuffer[CayenneLppCursor++] = alt >> 8; | |||
CayenneLppBuffer[CayenneLppCursor++] = alt; | |||
return CayenneLppCursor; | |||
} |
@@ -0,0 +1,76 @@ | |||
/*! | |||
* \file CayenneLpp.h | |||
* | |||
* \brief Implements the Cayenne Low Power Protocol | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __CAYENNE_LPP_H__ | |||
#define __CAYENNE_LPP_H__ | |||
#include <stdint.h> | |||
#define LPP_DIGITAL_INPUT 0 // 1 byte | |||
#define LPP_DIGITAL_OUTPUT 1 // 1 byte | |||
#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed | |||
#define LPP_ANALOG_OUTPUT 3 // 2 bytes, 0.01 signed | |||
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned | |||
#define LPP_PRESENCE 102 // 1 byte, 1 | |||
#define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed | |||
#define LPP_RELATIVE_HUMIDITY 104 // 1 byte, 0.5% unsigned | |||
#define LPP_ACCELEROMETER 113 // 2 bytes per axis, 0.001G | |||
#define LPP_BAROMETRIC_PRESSURE 115 // 2 bytes 0.1 hPa Unsigned | |||
#define LPP_GYROMETER 134 // 2 bytes per axis, 0.01 °/s | |||
#define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m | |||
// Data ID + Data Type + Data Size | |||
#define LPP_DIGITAL_INPUT_SIZE 3 | |||
#define LPP_DIGITAL_OUTPUT_SIZE 3 | |||
#define LPP_ANALOG_INPUT_SIZE 4 | |||
#define LPP_ANALOG_OUTPUT_SIZE 4 | |||
#define LPP_LUMINOSITY_SIZE 4 | |||
#define LPP_PRESENCE_SIZE 3 | |||
#define LPP_TEMPERATURE_SIZE 4 | |||
#define LPP_RELATIVE_HUMIDITY_SIZE 3 | |||
#define LPP_ACCELEROMETER_SIZE 8 | |||
#define LPP_BAROMETRIC_PRESSURE_SIZE 4 | |||
#define LPP_GYROMETER_SIZE 8 | |||
#define LPP_GPS_SIZE 11 | |||
void CayenneLppInit( void ); | |||
void CayenneLppReset( void ); | |||
uint8_t CayenneLppGetSize( void ); | |||
uint8_t* CayenneLppGetBuffer( void ); | |||
uint8_t CayenneLppCopy( uint8_t* buffer ); | |||
uint8_t CayenneLppAddDigitalInput( uint8_t channel, uint8_t value ); | |||
uint8_t CayenneLppAddDigitalOutput( uint8_t channel, uint8_t value ); | |||
uint8_t CayenneLppAddAnalogInput( uint8_t channel, float value ); | |||
uint8_t CayenneLppAddAnalogOutput( uint8_t channel, float value ); | |||
uint8_t CayenneLppAddLuminosity( uint8_t channel, uint16_t lux ); | |||
uint8_t CayenneLppAddPresence( uint8_t channel, uint8_t value ); | |||
uint8_t CayenneLppAddTemperature( uint8_t channel, float celsius ); | |||
uint8_t CayenneLppAddRelativeHumidity( uint8_t channel, float rh ); | |||
uint8_t CayenneLppAddAccelerometer( uint8_t channel, float x, float y, float z ); | |||
uint8_t CayenneLppAddBarometricPressure( uint8_t channel, float hpa ); | |||
uint8_t CayenneLppAddGyrometer( uint8_t channel, float x, float y, float z ); | |||
uint8_t CayenneLppAddGps( uint8_t channel, float latitude, float longitude, float meters ); | |||
#endif // __CAYENNE_LPP_H__ |
@@ -0,0 +1,63 @@ | |||
/*! | |||
* \file Commissioning.h | |||
* | |||
* \brief End-device commissioning parameters | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2020 Semtech | |||
* | |||
* \endcode | |||
*/ | |||
#ifndef __COMMISSIONING_H__ | |||
#define __COMMISSIONING_H__ | |||
/*! | |||
****************************************************************************** | |||
********************************** WARNING *********************************** | |||
****************************************************************************** | |||
The LoRaWAN AES128 keys are stored and provisionned on secure-elements. | |||
This project providdes a software emulated secure-element. | |||
The LoRaWAN AES128 keys SHALL be updated under | |||
src/peripherals/<secure-element name>-se\se-identity.h file. | |||
****************************************************************************** | |||
****************************************************************************** | |||
****************************************************************************** | |||
*/ | |||
#include "se-identity.h" | |||
/*! | |||
* When set to 1 the application uses the Over-the-Air activation procedure | |||
* When set to 0 the application uses the Personalization activation procedure | |||
*/ | |||
#define OVER_THE_AIR_ACTIVATION 1 | |||
/*! | |||
* When using ABP activation the MAC layer must know in advance to which server | |||
* version it will be connected. | |||
*/ | |||
#define ABP_ACTIVATION_LRWAN_VERSION_V10x 0x01000400 // 1.0.4.0 | |||
#define ABP_ACTIVATION_LRWAN_VERSION ABP_ACTIVATION_LRWAN_VERSION_V10x | |||
/*! | |||
* Indicates if the end-device is to be connected to a private or public network | |||
*/ | |||
#define LORAWAN_PUBLIC_NETWORK true | |||
/*! | |||
* Current network ID | |||
*/ | |||
#define LORAWAN_NETWORK_ID ( uint32_t )0 | |||
#endif // __COMMISSIONING_H__ |
@@ -0,0 +1,349 @@ | |||
/*! | |||
* \file LmHandler.h | |||
* | |||
* \brief Implements the LoRaMac layer handling. | |||
* Provides the possibility to register applicative packages. | |||
* | |||
* \remark Inspired by the examples provided on the en.i-cube_lrwan fork. | |||
* MCD Application Team ( STMicroelectronics International ) | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LORAMAC_HANDLER_H__ | |||
#define __LORAMAC_HANDLER_H__ | |||
#ifdef __cplusplus | |||
extern "C" | |||
{ | |||
#endif | |||
#include "LmHandlerTypes.h" | |||
typedef struct LmHandlerJoinParams_s | |||
{ | |||
CommissioningParams_t *CommissioningParams; | |||
int8_t Datarate; | |||
LmHandlerErrorStatus_t Status; | |||
}LmHandlerJoinParams_t; | |||
typedef struct LmHandlerTxParams_s | |||
{ | |||
uint8_t IsMcpsConfirm; | |||
LoRaMacEventInfoStatus_t Status; | |||
CommissioningParams_t *CommissioningParams; | |||
LmHandlerMsgTypes_t MsgType; | |||
uint8_t AckReceived; | |||
int8_t Datarate; | |||
uint32_t UplinkCounter; | |||
LmHandlerAppData_t AppData; | |||
int8_t TxPower; | |||
uint8_t Channel; | |||
}LmHandlerTxParams_t; | |||
typedef struct LmHandlerRxParams_s | |||
{ | |||
uint8_t IsMcpsIndication; | |||
LoRaMacEventInfoStatus_t Status; | |||
CommissioningParams_t *CommissioningParams; | |||
int8_t Datarate; | |||
int8_t Rssi; | |||
int8_t Snr; | |||
uint32_t DownlinkCounter; | |||
int8_t RxSlot; | |||
}LmHandlerRxParams_t; | |||
typedef struct LoRaMacHandlerBeaconParams_s | |||
{ | |||
LoRaMacEventInfoStatus_t Status; | |||
LmHandlerBeaconState_t State; | |||
BeaconInfo_t Info; | |||
}LoRaMacHandlerBeaconParams_t; | |||
typedef struct LmHandlerParams_s | |||
{ | |||
/*! | |||
* Region | |||
*/ | |||
LoRaMacRegion_t Region; | |||
/*! | |||
* Holds the ADR state | |||
*/ | |||
bool AdrEnable; | |||
/*! | |||
* Uplink frame type | |||
*/ | |||
LmHandlerMsgTypes_t IsTxConfirmed; | |||
/*! | |||
* Uplink datarate, when \ref AdrEnable is OFF | |||
*/ | |||
int8_t TxDatarate; | |||
/*! | |||
* Enables/Disables a public network usage | |||
*/ | |||
bool PublicNetworkEnable; | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
bool DutyCycleEnabled; | |||
/*! | |||
* Application data buffer maximum size | |||
*/ | |||
uint8_t DataBufferMaxSize; | |||
/*! | |||
* Application data buffer pointer | |||
*/ | |||
uint8_t *DataBuffer; | |||
/*! | |||
* Class B ping-slot periodicity. | |||
*/ | |||
bool PingSlotPeriodicity; | |||
}LmHandlerParams_t; | |||
typedef struct LmHandlerCallbacks_s | |||
{ | |||
/*! | |||
* Get the current battery level | |||
* | |||
* \retval value Battery level ( 0: very low, 254: fully charged ) | |||
*/ | |||
uint8_t ( *GetBatteryLevel )( void ); | |||
/*! | |||
* Get the current temperature | |||
* | |||
* \retval value Temperature in degree Celsius | |||
*/ | |||
float ( *GetTemperature )( void ); | |||
/*! | |||
* Returns a pseudo random seed generated using the MCU Unique ID | |||
* | |||
* \retval seed Generated pseudo random seed | |||
*/ | |||
uint32_t ( *GetRandomSeed )( void ); | |||
/*! | |||
*\brief Will be called each time a Radio IRQ is handled by the MAC | |||
* layer. | |||
* | |||
*\warning Runs in a IRQ context. Should only change variables state. | |||
*/ | |||
void ( *OnMacProcess )( void ); | |||
/*! | |||
* Notifies the upper layer that the NVM context has changed | |||
* | |||
* \param [IN] state Indicates if we are storing (true) or | |||
* restoring (false) the NVM context | |||
* | |||
* \param [IN] size Number of data bytes which were stored or restored. | |||
*/ | |||
void ( *OnNvmDataChange )( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
/*! | |||
* Notifies the upper layer that a network parameters have been set | |||
* | |||
* \param [IN] params notification parameters | |||
*/ | |||
void ( *OnNetworkParametersChange )( CommissioningParams_t *params ); | |||
/*! | |||
* Notifies the upper layer that a MCPS request has been made to the MAC layer | |||
* | |||
* \param [IN] status - Request returned status | |||
* \param [IN] mcpsRequest - Performed MCPS-Request. Refer to \ref McpsReq_t. | |||
* \param [IN] nextTxDelay - Time to wait until another TX is possible. | |||
*/ | |||
void ( *OnMacMcpsRequest )( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxDelay ); | |||
/*! | |||
* Notifies the upper layer that a MLME request has been made to the MAC layer | |||
* | |||
* \param [IN] status - Request returned status | |||
* \param [IN] mlmeRequest - Performed MLME-Request. Refer to \ref MlmeReq_t. | |||
* \param [IN] nextTxDelay - Time to wait until another TX is possible. | |||
*/ | |||
void ( *OnMacMlmeRequest )( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxDelay ); | |||
/*! | |||
* Notifies the upper layer that a network has been joined | |||
* | |||
* \param [IN] params notification parameters | |||
*/ | |||
void ( *OnJoinRequest )( LmHandlerJoinParams_t *params ); | |||
/*! | |||
* Notifies upper layer that a frame has been transmitted | |||
* | |||
* \param [IN] params notification parameters | |||
*/ | |||
void ( *OnTxData )( LmHandlerTxParams_t *params ); | |||
/*! | |||
* Notifies the upper layer that an applicative frame has been received | |||
* | |||
* \param [IN] appData Received applicative data | |||
* \param [IN] params notification parameters | |||
*/ | |||
void ( *OnRxData )( LmHandlerAppData_t *appData, LmHandlerRxParams_t *params ); | |||
/*! | |||
* Confirms the LoRaWAN device class change | |||
* | |||
* \param [IN] deviceClass New end-device class | |||
*/ | |||
void ( *OnClassChange )( DeviceClass_t deviceClass ); | |||
/*! | |||
* Notifies the upper layer that the beacon status has changed | |||
* | |||
* \param [IN] params notification parameters | |||
*/ | |||
void ( *OnBeaconStatusChange )( LoRaMacHandlerBeaconParams_t *params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
/*! | |||
* Notifies the upper layer that the system time has been updated. | |||
* | |||
* \param [in] isSynchronized Indicates if the system time is synchronized in the range +/-1 second | |||
* \param [in] timeCorrection Received time correction value | |||
*/ | |||
void ( *OnSysTimeUpdate )( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
/*! | |||
* Notifies the upper layer that the system time has been updated. | |||
*/ | |||
void ( *OnSysTimeUpdate )( void ); | |||
#endif | |||
}LmHandlerCallbacks_t; | |||
/*! | |||
* LoRaMac handler initialisation | |||
* | |||
* \param [IN] callbacks LoRaMac handler callbacks | |||
* \param [IN] handlerParams LoRaMac handler parameters | |||
* | |||
* \retval none | |||
*/ | |||
LmHandlerErrorStatus_t LmHandlerInit( LmHandlerCallbacks_t *callbacks, | |||
LmHandlerParams_t *handlerParams ); | |||
/*! | |||
* Indicates if the LoRaMacHandler is busy | |||
* | |||
* \retval status [true] Busy, [false] free | |||
*/ | |||
bool LmHandlerIsBusy( void ); | |||
/*! | |||
* Processes the LoRaMac and Radio events. | |||
* When no pendig operation asks to go in low power mode. | |||
* | |||
* \remark This function must be called in the main loop. | |||
*/ | |||
void LmHandlerProcess( void ); | |||
/*! | |||
* Gets current duty-cycle wait time | |||
* | |||
* \retval time to wait in ms | |||
*/ | |||
TimerTime_t LmHandlerGetDutyCycleWaitTime( void ); | |||
/*! | |||
* Instructs the MAC layer to send a ClassA uplink | |||
* | |||
* \param [IN] appData Data to be sent | |||
* \param [IN] isTxConfirmed Indicates if the uplink requires an acknowledgement | |||
* | |||
* \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been | |||
* processed else \ref LORAMAC_HANDLER_ERROR | |||
*/ | |||
LmHandlerErrorStatus_t LmHandlerSend( LmHandlerAppData_t *appData, LmHandlerMsgTypes_t isTxConfirmed ); | |||
/*! | |||
* Join a LoRa Network in classA | |||
* | |||
* \Note if the device is ABP, this is a pass through function | |||
*/ | |||
void LmHandlerJoin( void ); | |||
/*! | |||
* Check whether the Device is joined to the network | |||
* | |||
* \param [IN] none | |||
* | |||
* \retval status Returns \ref LORAMAC_HANDLER_SET if joined else \ref LORAMAC_HANDLER_RESET | |||
*/ | |||
LmHandlerFlagStatus_t LmHandlerJoinStatus( void ); | |||
/*! | |||
* Informs the server on the ping-slot periodicity to use | |||
* | |||
* \param [IN] periodicity Is equal to 2^periodicity seconds. | |||
* Example: 2^3 = 8 seconds. The end-device will open an Rx slot every 8 seconds. | |||
* | |||
* \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been | |||
* processed else \ref LORAMAC_HANDLER_ERROR | |||
*/ | |||
LmHandlerErrorStatus_t LmHandlerPingSlotReq( uint8_t periodicity ); | |||
/*! | |||
* Request the MAC layer to change LoRaWAN class | |||
* | |||
* \Note Callback \ref LmHandlerConfirmClass informs upper layer that the change has occurred | |||
* \Note Only switch from class A to class B/C OR from class B/C to class A is allowed | |||
* | |||
* \param [IN] newClass New class to be requested | |||
* | |||
* \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been | |||
* processed else \ref LORAMAC_HANDLER_ERROR | |||
*/ | |||
LmHandlerErrorStatus_t LmHandlerRequestClass( DeviceClass_t newClass ); | |||
/*! | |||
* Gets the current LoRaWAN class | |||
* | |||
* \retval currentClass Current LoRaWAN class | |||
*/ | |||
DeviceClass_t LmHandlerGetCurrentClass( void ); | |||
/*! | |||
* Gets the current datarate | |||
* | |||
* \retval currentDatarate Current datarate | |||
*/ | |||
int8_t LmHandlerGetCurrentDatarate( void ); | |||
/*! | |||
* Gets the current active region | |||
* | |||
* \retval currentRegion Current active region | |||
*/ | |||
LoRaMacRegion_t LmHandlerGetActiveRegion( void ); | |||
/*! | |||
* Set system maximum tolerated rx error in milliseconds | |||
* | |||
* \param [IN] maxErrorInMs Maximum tolerated error in milliseconds | |||
* | |||
* \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been | |||
* processed else \ref LORAMAC_HANDLER_ERROR | |||
*/ | |||
LmHandlerErrorStatus_t LmHandlerSetSystemMaxRxError( uint32_t maxErrorInMs ); | |||
/* | |||
*============================================================================= | |||
* PACKAGES HANDLING | |||
*============================================================================= | |||
*/ | |||
LmHandlerErrorStatus_t LmHandlerPackageRegister( uint8_t id, void *params ); | |||
bool LmHandlerPackageIsInitialized( uint8_t id ); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif // __LORAMAC_HANDLER_H__ |
@@ -0,0 +1,124 @@ | |||
/*! | |||
* \file LmHandlerTypes.h | |||
* | |||
* \brief Defines the types used by LmHandler | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LORAMAC_HANDLER_TYPES_H__ | |||
#define __LORAMAC_HANDLER_TYPES_H__ | |||
#include "LoRaMac.h" | |||
/*! | |||
* If set to 1 the new API defining \ref OnSysTimeUpdate callback is used. | |||
*/ | |||
#define LMH_SYS_TIME_UPDATE_NEW_API 1 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_ADR_OFF = 0, | |||
LORAMAC_HANDLER_ADR_ON = !LORAMAC_HANDLER_ADR_OFF | |||
}LmHandlerAdrStates_t; | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_RESET = 0, | |||
LORAMAC_HANDLER_SET = !LORAMAC_HANDLER_RESET | |||
}LmHandlerFlagStatus_t; | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_ERROR = -1, | |||
LORAMAC_HANDLER_SUCCESS = 0 | |||
}LmHandlerErrorStatus_t; | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_UNCONFIRMED_MSG = 0, | |||
LORAMAC_HANDLER_CONFIRMED_MSG = !LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
}LmHandlerMsgTypes_t; | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_FALSE = 0, | |||
LORAMAC_HANDLER_TRUE = !LORAMAC_HANDLER_FALSE | |||
}LmHandlerBoolean_t; | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_BEACON_ACQUIRING, | |||
LORAMAC_HANDLER_BEACON_LOST, | |||
LORAMAC_HANDLER_BEACON_RX, | |||
LORAMAC_HANDLER_BEACON_NRX | |||
}LmHandlerBeaconState_t; | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_NVM_RESTORE, | |||
LORAMAC_HANDLER_NVM_STORE, | |||
}LmHandlerNvmContextStates_t; | |||
/*! | |||
* Commissioning parameters | |||
*/ | |||
typedef struct CommissioningParams_s | |||
{ | |||
bool IsOtaaActivation; | |||
uint8_t DevEui[8]; | |||
uint8_t JoinEui[8]; | |||
uint8_t SePin[4]; | |||
uint32_t NetworkId; | |||
uint32_t DevAddr; | |||
}CommissioningParams_t; | |||
/*! | |||
* Application data structure | |||
*/ | |||
typedef struct LmHandlerAppData_s | |||
{ | |||
uint8_t Port; | |||
uint8_t BufferSize; | |||
uint8_t *Buffer; | |||
}LmHandlerAppData_t; | |||
typedef struct LmHandlerRequestParams_s | |||
{ | |||
uint8_t IsMcpsRequest; | |||
LoRaMacStatus_t Status; | |||
union | |||
{ | |||
Mcps_t Mcps; | |||
Mlme_t Mlme; | |||
}RequestType; | |||
}LmHandlerRequestParams_t; | |||
#endif // __LORAMAC_HANDLER_TYPES_H__ |
@@ -0,0 +1,751 @@ | |||
/*! | |||
* \file FragDecoder.c | |||
* | |||
* \brief Implements the LoRa-Alliance fragmentation decoder | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Fabien Holin ( Semtech ) | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#include <stddef.h> | |||
#include <stdbool.h> | |||
#include "utilities.h" | |||
#include "FragDecoder.h" | |||
#define DBG_TRACE 0 | |||
#if DBG_TRACE == 1 | |||
#include <stdio.h> | |||
/*! | |||
* Works in the same way as the printf function does. | |||
*/ | |||
#define DBG( ... ) \ | |||
do \ | |||
{ \ | |||
printf( __VA_ARGS__ ); \ | |||
}while( 0 ) | |||
#else | |||
#define DBG( fmt, ... ) | |||
#endif | |||
/* | |||
*============================================================================= | |||
* Fragmentation decoder algorithm utilities | |||
*============================================================================= | |||
*/ | |||
typedef struct | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
FragDecoderCallbacks_t *Callbacks; | |||
#else | |||
uint8_t *File; | |||
uint32_t FileSize; | |||
#endif | |||
uint16_t FragNb; | |||
uint8_t FragSize; | |||
uint32_t M2BLine; | |||
uint8_t MatrixM2B[( ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ) * FRAG_MAX_REDUNDANCY]; | |||
uint16_t FragNbMissingIndex[FRAG_MAX_NB]; | |||
uint8_t S[( FRAG_MAX_REDUNDANCY >> 3 ) + 1]; | |||
FragDecoderStatus_t Status; | |||
}FragDecoder_t; | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
/*! | |||
* \brief Sets a row from source into file destination | |||
* | |||
* \param [IN] src Source buffer pointer | |||
* \param [IN] row Destination index of the row to be copied | |||
* \param [IN] size Source number of bytes to be copied | |||
*/ | |||
static void SetRow( uint8_t *src, uint16_t row, uint16_t size ); | |||
#else | |||
/*! | |||
* \brief Sets a row from source into destination | |||
* | |||
* \param [IN] dst Destination buffer pointer | |||
* \param [IN] src Source buffer pointer | |||
* \param [IN] row Destination index of the row to be copied | |||
* \param [IN] size Source number of bytes to be copied | |||
*/ | |||
static void SetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
/*! | |||
* \brief Gets a row from source and stores it into file destination | |||
* | |||
* \param [IN] src Source buffer pointer | |||
* \param [IN] row Source index of the row to be copied | |||
* \param [IN] size Source number of bytes to be copied | |||
*/ | |||
static void GetRow( uint8_t *src, uint16_t row, uint16_t size ); | |||
#else | |||
/*! | |||
* \brief Gets a row from source and stores it into destination | |||
* | |||
* \param [IN] dst Destination buffer pointer | |||
* \param [IN] src Source buffer pointer | |||
* \param [IN] row Source index of the row to be copied | |||
* \param [IN] size Source number of bytes to be copied | |||
*/ | |||
static void GetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ); | |||
#endif | |||
/*! | |||
* \brief Gets the parity value from a given row of the parity matrix | |||
* | |||
* \param [IN] index The index of the row to be computed | |||
* \param [IN] matrixRow Pointer to the parity matrix (parity bit array) | |||
* | |||
* \retval parity Parity value at the given index | |||
*/ | |||
static uint8_t GetParity( uint16_t index, uint8_t *matrixRow ); | |||
/*! | |||
* \brief Sets the parity value on the given row of the parity matrix | |||
* | |||
* \param [IN] index The index of the row to be computed | |||
* \param [IN/OUT] matrixRow Pointer to the parity matrix. | |||
* \param [IN] parity The parity value to be set in the parity matrix | |||
*/ | |||
static void SetParity( uint16_t index, uint8_t *matrixRow, uint8_t parity ); | |||
/*! | |||
* \brief Check if the provided value is a power of 2 | |||
* | |||
* \param [IN] x Value to be tested | |||
* | |||
* \retval status Return true if frame is a power of two | |||
*/ | |||
static bool IsPowerOfTwo( uint32_t x ); | |||
/*! | |||
* \brief XOrs two data lines | |||
* | |||
* \param [IN] line1 1st Data line to be XORed | |||
* \param [IN] line2 2nd Data line to be XORed | |||
* \param [IN] size Number of elements in line1 | |||
* | |||
* \param [OUT] result XOR( line1, line2 ) result stored in line1 | |||
*/ | |||
static void XorDataLine( uint8_t *line1, uint8_t *line2, int32_t size ); | |||
/*! | |||
* \brief XORs two parity lines | |||
* | |||
* \param [IN] line1 1st Parity line to be XORed | |||
* \param [IN] line2 2nd Parity line to be XORed | |||
* \param [IN] size Number of elements in line1 | |||
* | |||
* \param [OUT] result XOR( line1, line2 ) result stored in line1 | |||
*/ | |||
static void XorParityLine( uint8_t* line1, uint8_t* line2, int32_t size ); | |||
/*! | |||
* \brief Generates a pseudo random number : PRBS23 | |||
* | |||
* \param [IN] value The input of the PRBS23 generator | |||
* | |||
* \retval nextValue Returns the next pseudo random number | |||
*/ | |||
static int32_t FragPrbs23( int32_t value ); | |||
/*! | |||
* \brief Gets and fills the parity matrix | |||
* | |||
* \param [IN] n Fragment N | |||
* \param [IN] m Fragment number | |||
* \param [OUT] matrixRow Parity matrix | |||
*/ | |||
static void FragGetParityMatrixRow( int32_t n, int32_t m, uint8_t *matrixRow ); | |||
/*! | |||
* \brief Finds the index of the first one in a bit array | |||
* | |||
* \param [IN] bitArray Pointer to the bit array | |||
* \param [IN] size Bit array size | |||
* \retval index The index of the first 1 in the bit array | |||
*/ | |||
static uint16_t BitArrayFindFirstOne( uint8_t *bitArray, uint16_t size ); | |||
/*! | |||
* \brief Checks if the provided bit array only contains zeros | |||
* | |||
* \param [IN] bitArray Pointer to the bit array | |||
* \param [IN] size Bit array size | |||
* \retval isAllZeros [0: Contains ones, 1: Contains all zeros] | |||
*/ | |||
static uint8_t BitArrayIsAllZeros( uint8_t *bitArray, uint16_t size ); | |||
/*! | |||
* \brief Finds & marks missing fragments | |||
* | |||
* \param [IN] counter Current fragment counter | |||
* \param [OUT] FragDecoder.FragNbMissingIndex[] array is updated in place | |||
*/ | |||
static void FragFindMissingFrags( uint16_t counter ); | |||
/*! | |||
* \brief Finds the index (frag counter) of the x th missing frag | |||
* | |||
* \param [IN] x x th missing frag | |||
* | |||
* \retval counter The counter value associated to the x th missing frag | |||
*/ | |||
static uint16_t FragFindMissingIndex( uint16_t x ); | |||
/*! | |||
* \brief Extacts a row from the binary matrix and expands it to a bitArray | |||
* | |||
* \param [IN] bitArray Pointer to the bit array | |||
* \param [IN] rowIndex Matrix row index | |||
* \param [IN] bitsInRow Number of bits in one row | |||
*/ | |||
static void FragExtractLineFromBinaryMatrix( uint8_t* bitArray, uint16_t rowIndex, uint16_t bitsInRow ); | |||
/*! | |||
* \brief Collapses and Pushs a row of a bit array to the matrix | |||
* | |||
* \param [IN] bitArray Pointer to the bit array | |||
* \param [IN] rowIndex Matrix row index | |||
* \param [IN] bitsInRow Number of bits in one row | |||
*/ | |||
static void FragPushLineToBinaryMatrix( uint8_t *bitArray, uint16_t rowIndex, uint16_t bitsInRow ); | |||
/* | |||
*============================================================================= | |||
* Fragmentation decoder algorithm | |||
*============================================================================= | |||
*/ | |||
static FragDecoder_t FragDecoder; | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, FragDecoderCallbacks_t *callbacks ) | |||
#else | |||
void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, uint8_t *file, uint32_t fileSize ) | |||
#endif | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
FragDecoder.Callbacks = callbacks; | |||
#else | |||
FragDecoder.File = file; | |||
FragDecoder.FileSize = fileSize; | |||
#endif | |||
FragDecoder.FragNb = fragNb; // FragNb = FRAG_MAX_SIZE | |||
FragDecoder.FragSize = fragSize; // number of byte on a row | |||
FragDecoder.Status.FragNbLastRx = 0; | |||
FragDecoder.Status.FragNbLost = 0; | |||
FragDecoder.M2BLine = 0; | |||
// Initialize missing fragments index array | |||
for( uint16_t i = 0; i < FRAG_MAX_NB; i++ ) | |||
{ | |||
FragDecoder.FragNbMissingIndex[i] = 1; | |||
} | |||
// Initialize parity matrix | |||
for( uint32_t i = 0; i < ( ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ); i++ ) | |||
{ | |||
FragDecoder.S[i] = 0; | |||
} | |||
for( uint32_t i = 0; i < ( ( ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ) * FRAG_MAX_REDUNDANCY ); i++ ) | |||
{ | |||
FragDecoder.MatrixM2B[i] = 0xFF; | |||
} | |||
// Initialize final uncoded data buffer ( FRAG_MAX_NB * FRAG_MAX_SIZE ) | |||
for( uint32_t i = 0; i < ( fragNb * fragSize ); i++ ) | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
if( ( FragDecoder.Callbacks != NULL ) && ( FragDecoder.Callbacks->FragDecoderWrite != NULL ) ) | |||
{ | |||
uint8_t buffer[1] = { 0xFF }; | |||
FragDecoder.Callbacks->FragDecoderWrite( i, buffer, 1 ); | |||
} | |||
#else | |||
FragDecoder.File[i] = 0xFF; | |||
#endif | |||
} | |||
FragDecoder.Status.FragNbLost = 0; | |||
FragDecoder.Status.FragNbLastRx = 0; | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
uint32_t FragDecoderGetMaxFileSize( void ) | |||
{ | |||
return FRAG_MAX_NB * FRAG_MAX_SIZE; | |||
} | |||
#endif | |||
int32_t FragDecoderProcess( uint16_t fragCounter, uint8_t *rawData ) | |||
{ | |||
uint16_t firstOneInRow = 0; | |||
int32_t first = 0; | |||
int32_t noInfo = 0; | |||
uint8_t matrixRow[(FRAG_MAX_NB >> 3 ) + 1]; | |||
uint8_t matrixDataTemp[FRAG_MAX_SIZE]; | |||
uint8_t dataTempVector[( FRAG_MAX_REDUNDANCY >> 3 ) + 1]; | |||
uint8_t dataTempVector2[( FRAG_MAX_REDUNDANCY >> 3 ) + 1]; | |||
memset1( matrixRow, 0, ( FRAG_MAX_NB >> 3 ) + 1 ); | |||
memset1( matrixDataTemp, 0, FRAG_MAX_SIZE ); | |||
memset1( dataTempVector, 0, ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ); | |||
memset1( dataTempVector2, 0, ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ); | |||
FragDecoder.Status.FragNbRx = fragCounter; | |||
if( fragCounter < FragDecoder.Status.FragNbLastRx ) | |||
{ | |||
return FRAG_SESSION_ONGOING; // Drop frame out of order | |||
} | |||
// The M (FragNb) first packets aren't encoded or in other words they are | |||
// encoded with the unitary matrix | |||
if( fragCounter < ( FragDecoder.FragNb + 1 ) ) | |||
{ | |||
// The M first frame are not encoded store them | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
SetRow( rawData, fragCounter - 1, FragDecoder.FragSize ); | |||
#else | |||
SetRow( FragDecoder.File, rawData, fragCounter - 1, FragDecoder.FragSize ); | |||
#endif | |||
FragDecoder.FragNbMissingIndex[fragCounter - 1] = 0; | |||
// Update the FragDecoder.FragNbMissingIndex with the loosing frame | |||
FragFindMissingFrags( fragCounter ); | |||
} | |||
else | |||
{ | |||
if( FragDecoder.Status.FragNbLost > FRAG_MAX_REDUNDANCY ) | |||
{ | |||
FragDecoder.Status.MatrixError = 1; | |||
return FRAG_SESSION_FINISHED; | |||
} | |||
// At this point we receive encoded frames and the number of loosing frames | |||
// is well known: FragDecoder.FragNbLost - 1; | |||
// In case of the end of true data is missing | |||
FragFindMissingFrags( fragCounter ); | |||
if( FragDecoder.Status.FragNbLost == 0 ) | |||
{ | |||
// the case : all the M(FragNb) first rows have been transmitted with no error | |||
return FragDecoder.Status.FragNbLost; | |||
} | |||
// fragCounter - FragDecoder.FragNb | |||
FragGetParityMatrixRow( fragCounter - FragDecoder.FragNb, FragDecoder.FragNb, matrixRow ); | |||
for( int32_t i = 0; i < FragDecoder.FragNb; i++ ) | |||
{ | |||
if( GetParity( i , matrixRow ) == 1 ) | |||
{ | |||
if( FragDecoder.FragNbMissingIndex[i] == 0 ) | |||
{ | |||
// XOR with already receive frag | |||
SetParity( i, matrixRow, 0 ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
GetRow( matrixDataTemp, i, FragDecoder.FragSize ); | |||
#else | |||
GetRow( matrixDataTemp, FragDecoder.File, i, FragDecoder.FragSize ); | |||
#endif | |||
XorDataLine( rawData, matrixDataTemp, FragDecoder.FragSize ); | |||
} | |||
else | |||
{ | |||
// Fill the "little" boolean matrix m2b | |||
SetParity( FragDecoder.FragNbMissingIndex[i] - 1, dataTempVector, 1 ); | |||
if( first == 0 ) | |||
{ | |||
first = 1; | |||
} | |||
} | |||
} | |||
} | |||
firstOneInRow = BitArrayFindFirstOne( dataTempVector, FragDecoder.Status.FragNbLost ); | |||
if( first > 0 ) | |||
{ | |||
int32_t li; | |||
int32_t lj; | |||
// Manage a new line in MatrixM2B | |||
while( GetParity( firstOneInRow, FragDecoder.S ) == 1 ) | |||
{ | |||
// Row already diagonalized exist & ( FragDecoder.MatrixM2B[firstOneInRow][0] ) | |||
FragExtractLineFromBinaryMatrix( dataTempVector2, firstOneInRow, FragDecoder.Status.FragNbLost ); | |||
XorParityLine( dataTempVector, dataTempVector2, FragDecoder.Status.FragNbLost ); | |||
// Have to store it in the mi th position of the missing frag | |||
li = FragFindMissingIndex( firstOneInRow ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
GetRow( matrixDataTemp, li, FragDecoder.FragSize ); | |||
#else | |||
GetRow( matrixDataTemp, FragDecoder.File, li, FragDecoder.FragSize ); | |||
#endif | |||
XorDataLine( rawData, matrixDataTemp, FragDecoder.FragSize ); | |||
if( BitArrayIsAllZeros( dataTempVector, FragDecoder.Status.FragNbLost ) ) | |||
{ | |||
noInfo = 1; | |||
break; | |||
} | |||
firstOneInRow = BitArrayFindFirstOne( dataTempVector, FragDecoder.Status.FragNbLost ); | |||
} | |||
if( noInfo == 0 ) | |||
{ | |||
FragPushLineToBinaryMatrix( dataTempVector, firstOneInRow, FragDecoder.Status.FragNbLost ); | |||
li = FragFindMissingIndex( firstOneInRow ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
SetRow( rawData, li, FragDecoder.FragSize ); | |||
#else | |||
SetRow( FragDecoder.File, rawData, li, FragDecoder.FragSize ); | |||
#endif | |||
SetParity( firstOneInRow, FragDecoder.S, 1 ); | |||
FragDecoder.M2BLine++; | |||
} | |||
if( FragDecoder.M2BLine == FragDecoder.Status.FragNbLost ) | |||
{ | |||
// Then last step diagonalized | |||
if( FragDecoder.Status.FragNbLost > 1 ) | |||
{ | |||
int32_t i, j; | |||
for( i = ( FragDecoder.Status.FragNbLost - 2 ); i >= 0 ; i-- ) | |||
{ | |||
li = FragFindMissingIndex( i ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
GetRow( matrixDataTemp, li, FragDecoder.FragSize ); | |||
#else | |||
GetRow( matrixDataTemp, FragDecoder.File, li, FragDecoder.FragSize ); | |||
#endif | |||
for( j = ( FragDecoder.Status.FragNbLost - 1 ); j > i; j--) | |||
{ | |||
FragExtractLineFromBinaryMatrix( dataTempVector2, i, FragDecoder.Status.FragNbLost ); | |||
FragExtractLineFromBinaryMatrix( dataTempVector, j, FragDecoder.Status.FragNbLost ); | |||
if( GetParity( j, dataTempVector2 ) == 1 ) | |||
{ | |||
XorParityLine( dataTempVector2, dataTempVector, FragDecoder.Status.FragNbLost ); | |||
lj = FragFindMissingIndex( j ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
GetRow( rawData, lj, FragDecoder.FragSize ); | |||
#else | |||
GetRow( rawData, FragDecoder.File, lj, FragDecoder.FragSize ); | |||
#endif | |||
XorDataLine( matrixDataTemp , rawData , FragDecoder.FragSize ); | |||
} | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
SetRow( matrixDataTemp, li, FragDecoder.FragSize ); | |||
#else | |||
SetRow( FragDecoder.File, matrixDataTemp, li, FragDecoder.FragSize ); | |||
#endif | |||
} | |||
return FragDecoder.Status.FragNbLost; | |||
} | |||
else | |||
{ | |||
//If not ( FragDecoder.FragNbLost > 1 ) | |||
return FragDecoder.Status.FragNbLost; | |||
} | |||
} | |||
} | |||
} | |||
return FRAG_SESSION_ONGOING; | |||
} | |||
FragDecoderStatus_t FragDecoderGetStatus( void ) | |||
{ | |||
return FragDecoder.Status; | |||
} | |||
/* | |||
*============================================================================= | |||
* Fragmentation decoder algorithm utilities | |||
*============================================================================= | |||
*/ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void SetRow( uint8_t *src, uint16_t row, uint16_t size ) | |||
{ | |||
if( ( FragDecoder.Callbacks != NULL ) && ( FragDecoder.Callbacks->FragDecoderWrite != NULL ) ) | |||
{ | |||
FragDecoder.Callbacks->FragDecoderWrite( row * size, src, size ); | |||
} | |||
} | |||
static void GetRow( uint8_t *dst, uint16_t row, uint16_t size ) | |||
{ | |||
if( ( FragDecoder.Callbacks != NULL ) && ( FragDecoder.Callbacks->FragDecoderRead != NULL ) ) | |||
{ | |||
FragDecoder.Callbacks->FragDecoderRead( row * size, dst, size ); | |||
} | |||
} | |||
#else | |||
static void SetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ) | |||
{ | |||
memcpy1( &dst[row * size], src, size ); | |||
} | |||
static void GetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ) | |||
{ | |||
memcpy1( dst, &src[row * size], size ); | |||
} | |||
#endif | |||
static uint8_t GetParity( uint16_t index, uint8_t *matrixRow ) | |||
{ | |||
uint8_t parity; | |||
parity = matrixRow[index >> 3]; | |||
parity = ( parity >> ( 7 - ( index % 8 ) ) ) & 0x01; | |||
return parity; | |||
} | |||
static void SetParity( uint16_t index, uint8_t *matrixRow, uint8_t parity ) | |||
{ | |||
uint8_t mask = 0xFF - ( 1 << ( 7 - ( index % 8 ) ) ); | |||
parity = parity << ( 7 - ( index % 8 ) ); | |||
matrixRow[index >> 3] = ( matrixRow[index >> 3] & mask ) + parity; | |||
} | |||
static bool IsPowerOfTwo( uint32_t x ) | |||
{ | |||
uint8_t sumBit = 0; | |||
for( uint8_t i = 0; i < 32; i++ ) | |||
{ | |||
sumBit += ( x & ( 1 << i ) ) >> i; | |||
} | |||
if( sumBit == 1 ) | |||
{ | |||
return true; | |||
} | |||
return false; | |||
} | |||
static void XorDataLine( uint8_t *line1, uint8_t *line2, int32_t size ) | |||
{ | |||
for( int32_t i = 0; i < size; i++ ) | |||
{ | |||
line1[i] = line1[i] ^ line2[i]; | |||
} | |||
} | |||
static void XorParityLine( uint8_t* line1, uint8_t* line2, int32_t size ) | |||
{ | |||
for( int32_t i = 0; i < size; i++ ) | |||
{ | |||
SetParity( i, line1, ( GetParity( i, line1 ) ^ GetParity( i, line2 ) ) ); | |||
} | |||
} | |||
static int32_t FragPrbs23( int32_t value ) | |||
{ | |||
int32_t b0 = value & 0x01; | |||
int32_t b1 = ( value & 0x20 ) >> 5; | |||
return ( value >> 1 ) + ( ( b0 ^ b1 ) << 22 ); | |||
} | |||
static void FragGetParityMatrixRow( int32_t n, int32_t m, uint8_t *matrixRow ) | |||
{ | |||
int32_t mTemp; | |||
int32_t x; | |||
int32_t nbCoeff = 0; | |||
int32_t r; | |||
if( IsPowerOfTwo( m ) != false ) | |||
{ | |||
mTemp = 1; | |||
} | |||
else | |||
{ | |||
mTemp = 0; | |||
} | |||
x = 1 + ( 1001 * n ); | |||
for( uint16_t i = 0; i < ( ( m >> 3 ) + 1 ); i++ ) | |||
{ | |||
matrixRow[i] = 0; | |||
} | |||
while( nbCoeff < ( m >> 1 ) ) | |||
{ | |||
r = 1 << 16; | |||
while( r >= m ) | |||
{ | |||
x = FragPrbs23( x ); | |||
r = x % ( m + mTemp ); | |||
} | |||
SetParity( r, matrixRow, 1 ); | |||
nbCoeff += 1; | |||
} | |||
} | |||
static uint16_t BitArrayFindFirstOne( uint8_t *bitArray, uint16_t size ) | |||
{ | |||
for( uint16_t i = 0; i < size; i++) | |||
{ | |||
if ( GetParity( i, bitArray ) == 1 ) | |||
{ | |||
return i; | |||
} | |||
} | |||
return 0; | |||
} | |||
static uint8_t BitArrayIsAllZeros( uint8_t *bitArray, uint16_t size ) | |||
{ | |||
for( uint16_t i = 0; i < size; i++ ) | |||
{ | |||
if( GetParity( i, bitArray ) == 1 ) | |||
{ | |||
return 0; | |||
} | |||
} | |||
return 1; | |||
} | |||
/*! | |||
* \brief Finds & marks missing fragments | |||
* | |||
* \param [IN] counter Current fragment counter | |||
* \param [OUT] FragDecoder.FragNbMissingIndex[] array is updated in place | |||
*/ | |||
static void FragFindMissingFrags( uint16_t counter ) | |||
{ | |||
int32_t i; | |||
for( i = FragDecoder.Status.FragNbLastRx; i < ( counter - 1 ); i++ ) | |||
{ | |||
if( i < FragDecoder.FragNb ) | |||
{ | |||
FragDecoder.Status.FragNbLost++; | |||
FragDecoder.FragNbMissingIndex[i] = FragDecoder.Status.FragNbLost; | |||
} | |||
} | |||
if( i < FragDecoder.FragNb ) | |||
{ | |||
FragDecoder.Status.FragNbLastRx = counter; | |||
} | |||
else | |||
{ | |||
FragDecoder.Status.FragNbLastRx = FragDecoder.FragNb + 1; | |||
} | |||
DBG( "RECEIVED : %5d / %5d Fragments\n", FragDecoder.Status.FragNbRx, FragDecoder.FragNb ); | |||
DBG( " %5d / %5d Bytes\n", FragDecoder.Status.FragNbRx * FragDecoder.FragSize, FragDecoder.FragNb * FragDecoder.FragSize ); | |||
DBG( "LOST : %7d Fragments\n\n", FragDecoder.Status.FragNbLost ); | |||
} | |||
/*! | |||
* \brief Finds the index (frag counter) of the x th missing frag | |||
* | |||
* \param [IN] x x th missing frag | |||
* | |||
* \retval counter The counter value associated to the x th missing frag | |||
*/ | |||
static uint16_t FragFindMissingIndex( uint16_t x ) | |||
{ | |||
for( uint16_t i = 0; i < FragDecoder.FragNb; i++ ) | |||
{ | |||
if( FragDecoder.FragNbMissingIndex[i] == ( x + 1 ) ) | |||
{ | |||
return i; | |||
} | |||
} | |||
return 0; | |||
} | |||
/*! | |||
* \brief Extacts a row from the binary matrix and expands it to a bitArray | |||
* | |||
* \param [IN] bitArray Pointer to the bit array | |||
* \param [IN] rowIndex Matrix row index | |||
* \param [IN] bitsInRow Number of bits in one row | |||
*/ | |||
static void FragExtractLineFromBinaryMatrix( uint8_t* bitArray, uint16_t rowIndex, uint16_t bitsInRow ) | |||
{ | |||
uint32_t findByte = 0; | |||
uint32_t findBitInByte = 0; | |||
if( rowIndex > 0 ) | |||
{ | |||
findByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) >> 3; | |||
findBitInByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) % 8; | |||
} | |||
if( rowIndex > 0 ) | |||
{ | |||
for( uint16_t i = 0; i < rowIndex; i++ ) | |||
{ | |||
SetParity( i, bitArray, 0 ); | |||
} | |||
} | |||
for( uint16_t i = rowIndex; i < bitsInRow; i++ ) | |||
{ | |||
SetParity( i, | |||
bitArray, | |||
( FragDecoder.MatrixM2B[findByte] >> ( 7 - findBitInByte ) ) & 0x01 ); | |||
findBitInByte++; | |||
if( findBitInByte == 8 ) | |||
{ | |||
findBitInByte = 0; | |||
findByte++; | |||
} | |||
} | |||
} | |||
/*! | |||
* \brief Collapses and Pushs a row of a bit array to the matrix | |||
* | |||
* \param [IN] bitArray Pointer to the bit array | |||
* \param [IN] rowIndex Matrix row index | |||
* \param [IN] bitsInRow Number of bits in one row | |||
*/ | |||
static void FragPushLineToBinaryMatrix( uint8_t *bitArray, uint16_t rowIndex, uint16_t bitsInRow ) | |||
{ | |||
uint32_t findByte = 0; | |||
uint32_t findBitInByte = 0; | |||
if ( rowIndex > 0) { | |||
findByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) >> 3; | |||
findBitInByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) % 8; | |||
} | |||
for( uint16_t i = rowIndex; i < bitsInRow; i++ ) | |||
{ | |||
if( GetParity( i, bitArray ) == 0 ) | |||
{ | |||
FragDecoder.MatrixM2B[findByte] = FragDecoder.MatrixM2B[findByte] & ( 0xFF - ( 1 << ( 7 - findBitInByte ) ) ); | |||
} | |||
findBitInByte++; | |||
if( findBitInByte == 8 ) | |||
{ | |||
findBitInByte = 0; | |||
findByte++; | |||
} | |||
} | |||
} |
@@ -0,0 +1,143 @@ | |||
/*! | |||
* \file FragDecoder.h | |||
* | |||
* \brief Implements the LoRa-Alliance fragmentation decoder | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Fabien Holin ( Semtech ) | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __FRAG_DECODER_H__ | |||
#define __FRAG_DECODER_H__ | |||
#include <stdint.h> | |||
/*! | |||
* If set to 1 the new API defining \ref FragDecoderWrite and | |||
* \ref FragDecoderReadfunction callbacks is used. | |||
*/ | |||
#define FRAG_DECODER_FILE_HANDLING_NEW_API 1 | |||
/*! | |||
* Maximum number of fragment that can be handled. | |||
* | |||
* \remark This parameter has an impact on the memory footprint. | |||
*/ | |||
#define FRAG_MAX_NB 21 | |||
/*! | |||
* Maximum fragment size that can be handled. | |||
* | |||
* \remark This parameter has an impact on the memory footprint. | |||
*/ | |||
#define FRAG_MAX_SIZE 50 | |||
/*! | |||
* Maximum number of extra frames that can be handled. | |||
* | |||
* \remark This parameter has an impact on the memory footprint. | |||
*/ | |||
#define FRAG_MAX_REDUNDANCY 5 | |||
#define FRAG_SESSION_FINISHED ( int32_t )0 | |||
#define FRAG_SESSION_NOT_STARTED ( int32_t )-2 | |||
#define FRAG_SESSION_ONGOING ( int32_t )-1 | |||
typedef struct sFragDecoderStatus | |||
{ | |||
uint16_t FragNbRx; | |||
uint16_t FragNbLost; | |||
uint16_t FragNbLastRx; | |||
uint8_t MatrixError; | |||
}FragDecoderStatus_t; | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
typedef struct sFragDecoderCallbacks | |||
{ | |||
/*! | |||
* Writes `data` buffer of `size` starting at address `addr` | |||
* | |||
* \param [IN] addr Address start index to write to. | |||
* \param [IN] data Data buffer to be written. | |||
* \param [IN] size Size of data buffer to be written. | |||
* | |||
* \retval status Write operation status [0: Success, -1 Fail] | |||
*/ | |||
int8_t ( *FragDecoderWrite )( uint32_t addr, uint8_t *data, uint32_t size ); | |||
/*! | |||
* Reads `data` buffer of `size` starting at address `addr` | |||
* | |||
* \param [IN] addr Address start index to read from. | |||
* \param [IN] data Data buffer to be read. | |||
* \param [IN] size Size of data buffer to be read. | |||
* | |||
* \retval status Read operation status [0: Success, -1 Fail] | |||
*/ | |||
int8_t ( *FragDecoderRead )( uint32_t addr, uint8_t *data, uint32_t size ); | |||
}FragDecoderCallbacks_t; | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
/*! | |||
* \brief Initializes the fragmentation decoder | |||
* | |||
* \param [IN] fragNb Number of expected fragments (without redundancy packets) | |||
* \param [IN] fragSize Size of a fragment | |||
* \param [IN] callbacks Pointer to the Write/Read functions. | |||
*/ | |||
void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, FragDecoderCallbacks_t *callbacks ); | |||
#else | |||
/*! | |||
* \brief Initializes the fragmentation decoder | |||
* | |||
* \param [IN] fragNb Number of expected fragments (without redundancy packets) | |||
* \param [IN] fragSize Size of a fragment | |||
* \param [IN] file Pointer to file buffer size | |||
* \param [IN] fileSize File buffer size | |||
*/ | |||
void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, uint8_t *file, uint32_t fileSize ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
/*! | |||
* \brief Gets the maximum file size that can be received | |||
* | |||
* \retval size FileSize | |||
*/ | |||
uint32_t FragDecoderGetMaxFileSize( void ); | |||
#endif | |||
/*! | |||
* \brief Function to decode and reconstruct the binary file | |||
* Called for each receive frame | |||
* | |||
* \param [IN] fragCounter Fragment counter [1..(FragDecoder.FragNb + FragDecoder.Redundancy)] | |||
* \param [IN] rawData Pointer to the fragment to be processed (length = FragDecoder.FragSize) | |||
* | |||
* \retval status Process status. [FRAG_SESSION_ONGOING, | |||
* FRAG_SESSION_FINISHED or | |||
* FragDecoder.Status.FragNbLost] | |||
*/ | |||
int32_t FragDecoderProcess( uint16_t fragCounter, uint8_t *rawData ); | |||
/*! | |||
* \brief Gets the current fragmentation status | |||
* | |||
* \retval status Fragmentation decoder status | |||
*/ | |||
FragDecoderStatus_t FragDecoderGetStatus( void ); | |||
#endif // __FRAG_DECODER_H__ |
@@ -0,0 +1,146 @@ | |||
/*! | |||
* \file LmPackage.h | |||
* | |||
* \brief Defines the packages API | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LMH_PACKAGE_H__ | |||
#define __LMH_PACKAGE_H__ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include "LmHandlerTypes.h" | |||
/*! | |||
* Maximum number of packages | |||
*/ | |||
#define PKG_MAX_NUMBER 4 | |||
typedef struct LmhPackage_s | |||
{ | |||
uint8_t Port; | |||
/* | |||
*========================================================================= | |||
* Below callbacks must be initialized in package variable declaration | |||
*========================================================================= | |||
*/ | |||
/*! | |||
* Initializes the package with provided parameters | |||
* | |||
* \param [IN] params Pointer to the package parameters | |||
* \param [IN] dataBuffer Pointer to main application buffer | |||
* \param [IN] dataBufferMaxSize Main application buffer maximum size | |||
*/ | |||
void ( *Init )( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); | |||
/*! | |||
* Returns the current package initialization status. | |||
* | |||
* \retval status Package initialization status | |||
* [true: Initialized, false: Not initialized] | |||
*/ | |||
bool ( *IsInitialized )( void ); | |||
/*! | |||
* Returns if a package transmission is pending or not. | |||
* | |||
* \retval status Package transmission status | |||
* [true: pending, false: Not pending] | |||
*/ | |||
bool ( *IsTxPending )( void ); | |||
/*! | |||
* Processes the internal package events. | |||
*/ | |||
void ( *Process )( void ); | |||
/*! | |||
* Processes the MCSP Confirm | |||
* | |||
* \param [IN] mcpsConfirm MCPS confirmation primitive data | |||
*/ | |||
void ( *OnMcpsConfirmProcess )( McpsConfirm_t *mcpsConfirm ); | |||
/*! | |||
* Processes the MCPS Indication | |||
* | |||
* \param [IN] mcpsIndication MCPS indication primitive data | |||
*/ | |||
void ( *OnMcpsIndicationProcess )( McpsIndication_t *mcpsIndication ); | |||
/*! | |||
* Processes the MLME Confirm | |||
* | |||
* \param [IN] mlmeConfirm MLME confirmation primitive data | |||
*/ | |||
void ( *OnMlmeConfirmProcess )( MlmeConfirm_t *mlmeConfirm ); | |||
/*! | |||
* Processes the MLME Indication | |||
* | |||
* \param [IN] mlmeIndication MLME indication primitive data | |||
*/ | |||
void ( *OnMlmeIndicationProcess )( MlmeIndication_t *mlmeIndication ); | |||
/* | |||
*========================================================================= | |||
* Below callbacks must be initialized in LmHandler initialization with | |||
* provideded LmHandlerSend and OnMacRequest functions | |||
*========================================================================= | |||
*/ | |||
/*! | |||
* Notifies the upper layer that a MCPS request has been made to the MAC layer | |||
* | |||
* \param [IN] status - Request returned status | |||
* \param [IN] mcpsRequest - Performed MCPS-Request. Refer to \ref McpsReq_t. | |||
* \param [IN] nextTxDelay - Time to wait until another TX is possible. | |||
*/ | |||
void ( *OnMacMcpsRequest )( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxDelay ); | |||
/*! | |||
* Notifies the upper layer that a MLME request has been made to the MAC layer | |||
* | |||
* \param [IN] status - Request returned status | |||
* \param [IN] mlmeRequest - Performed MLME-Request. Refer to \ref MlmeReq_t. | |||
* \param [IN] nextTxDelay - Time to wait until another TX is possible. | |||
*/ | |||
void ( *OnMacMlmeRequest )( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxDelay ); | |||
/*! | |||
* Join a LoRa Network in classA | |||
* | |||
* \Note if the device is ABP, this is a pass through function | |||
* | |||
* \param [IN] isOtaa Indicates which activation mode must be used | |||
*/ | |||
void ( *OnJoinRequest )( bool isOtaa ); | |||
/*! | |||
* Requests network server time update | |||
* | |||
* \retval status Returns \ref LORAMAC_HANDLER_SET if joined else \ref LORAMAC_HANDLER_RESET | |||
*/ | |||
LmHandlerErrorStatus_t ( *OnDeviceTimeRequest )( void ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
/*! | |||
* Notifies the upper layer that the system time has been updated. | |||
* | |||
* \param [in] isSynchronized Indicates if the system time is synchronized in the range +/-1 second | |||
* \param [in] timeCorrection Received time correction value | |||
*/ | |||
void ( *OnSysTimeUpdate )( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
/*! | |||
* Notifies the upper layer that the system time has been updated. | |||
*/ | |||
void ( *OnSysTimeUpdate )( void ); | |||
#endif | |||
}LmhPackage_t; | |||
#endif // __LMH_PACKAGE_H__ |
@@ -0,0 +1,372 @@ | |||
/*! | |||
* \file LmhpClockSync.c | |||
* | |||
* \brief Implements the LoRa-Alliance clock synchronization package | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/application_layer_clock_synchronization_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#include "LmHandler.h" | |||
#include "LmhpClockSync.h" | |||
/*! | |||
* LoRaWAN Application Layer Clock Synchronization Specification | |||
*/ | |||
#define CLOCK_SYNC_PORT 202 | |||
#define CLOCK_SYNC_ID 1 | |||
#define CLOCK_SYNC_VERSION 1 | |||
/*! | |||
* Package current context | |||
*/ | |||
typedef struct LmhpClockSyncState_s | |||
{ | |||
bool Initialized; | |||
bool IsTxPending; | |||
uint8_t DataBufferMaxSize; | |||
uint8_t *DataBuffer; | |||
union | |||
{ | |||
uint8_t Value; | |||
struct | |||
{ | |||
uint8_t TokenReq: 4; | |||
uint8_t AnsRequired: 1; | |||
uint8_t RFU: 3; | |||
}Fields; | |||
}TimeReqParam; | |||
bool AppTimeReqPending; | |||
bool AdrEnabledPrev; | |||
uint8_t NbTransPrev; | |||
uint8_t DataratePrev; | |||
uint8_t NbTransmissions; | |||
}LmhpClockSyncState_t; | |||
typedef enum LmhpClockSyncMoteCmd_e | |||
{ | |||
CLOCK_SYNC_PKG_VERSION_ANS = 0x00, | |||
CLOCK_SYNC_APP_TIME_REQ = 0x01, | |||
CLOCK_SYNC_APP_TIME_PERIOD_ANS = 0x02, | |||
CLOCK_SYNC_FORCE_RESYNC_ANS = 0x03, | |||
}LmhpClockSyncMoteCmd_t; | |||
typedef enum LmhpClockSyncSrvCmd_e | |||
{ | |||
CLOCK_SYNC_PKG_VERSION_REQ = 0x00, | |||
CLOCK_SYNC_APP_TIME_ANS = 0x01, | |||
CLOCK_SYNC_APP_TIME_PERIOD_REQ = 0x02, | |||
CLOCK_SYNC_FORCE_RESYNC_REQ = 0x03, | |||
}LmhpClockSyncSrvCmd_t; | |||
/*! | |||
* Initializes the package with provided parameters | |||
* | |||
* \param [IN] params Pointer to the package parameters | |||
* \param [IN] dataBuffer Pointer to main application buffer | |||
* \param [IN] dataBufferMaxSize Main application buffer maximum size | |||
*/ | |||
static void LmhpClockSyncInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); | |||
/*! | |||
* Returns the current package initialization status. | |||
* | |||
* \retval status Package initialization status | |||
* [true: Initialized, false: Not initialized] | |||
*/ | |||
static bool LmhpClockSyncIsInitialized( void ); | |||
/*! | |||
* Returns if a package transmission is pending or not. | |||
* | |||
* \retval status Package transmission status | |||
* [true: pending, false: Not pending] | |||
*/ | |||
static bool LmhpClockSyncIsTxPending( void ); | |||
/*! | |||
* Processes the internal package events. | |||
*/ | |||
static void LmhpClockSyncProcess( void ); | |||
/*! | |||
* Processes the MCSP Confirm | |||
* | |||
* \param [IN] mcpsConfirm MCPS confirmation primitive data | |||
*/ | |||
static void LmhpClockSyncOnMcpsConfirm( McpsConfirm_t *mcpsConfirm ); | |||
/*! | |||
* Processes the MCPS Indication | |||
* | |||
* \param [IN] mcpsIndication MCPS indication primitive data | |||
*/ | |||
static void LmhpClockSyncOnMcpsIndication( McpsIndication_t *mcpsIndication ); | |||
static LmhpClockSyncState_t LmhpClockSyncState = | |||
{ | |||
.Initialized = false, | |||
.IsTxPending = false, | |||
.TimeReqParam.Value = 0, | |||
.AppTimeReqPending = false, | |||
.AdrEnabledPrev = false, | |||
.NbTransPrev = 0, | |||
.NbTransmissions = 0, | |||
}; | |||
static LmhPackage_t LmhpClockSyncPackage = | |||
{ | |||
.Port = CLOCK_SYNC_PORT, | |||
.Init = LmhpClockSyncInit, | |||
.IsInitialized = LmhpClockSyncIsInitialized, | |||
.IsTxPending = LmhpClockSyncIsTxPending, | |||
.Process = LmhpClockSyncProcess, | |||
.OnMcpsConfirmProcess = LmhpClockSyncOnMcpsConfirm, | |||
.OnMcpsIndicationProcess = LmhpClockSyncOnMcpsIndication, | |||
.OnMlmeConfirmProcess = NULL, // Not used in this package | |||
.OnMlmeIndicationProcess = NULL, // Not used in this package | |||
.OnMacMcpsRequest = NULL, // To be initialized by LmHandler | |||
.OnMacMlmeRequest = NULL, // To be initialized by LmHandler | |||
.OnJoinRequest = NULL, // To be initialized by LmHandler | |||
.OnDeviceTimeRequest = NULL, // To be initialized by LmHandler | |||
.OnSysTimeUpdate = NULL, // To be initialized by LmHandler | |||
}; | |||
LmhPackage_t *LmphClockSyncPackageFactory( void ) | |||
{ | |||
return &LmhpClockSyncPackage; | |||
} | |||
static void LmhpClockSyncInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ) | |||
{ | |||
if( dataBuffer != NULL ) | |||
{ | |||
LmhpClockSyncState.DataBuffer = dataBuffer; | |||
LmhpClockSyncState.DataBufferMaxSize = dataBufferMaxSize; | |||
LmhpClockSyncState.Initialized = true; | |||
} | |||
else | |||
{ | |||
LmhpClockSyncState.Initialized = false; | |||
} | |||
LmhpClockSyncState.IsTxPending = false; | |||
} | |||
static bool LmhpClockSyncIsInitialized( void ) | |||
{ | |||
return LmhpClockSyncState.Initialized; | |||
} | |||
static bool LmhpClockSyncIsTxPending( void ) | |||
{ | |||
return LmhpClockSyncState.IsTxPending; | |||
} | |||
static void LmhpClockSyncProcess( void ) | |||
{ | |||
if( LmhpClockSyncState.NbTransmissions > 0 ) | |||
{ | |||
if( LmhpClockSyncAppTimeReq( ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
LmhpClockSyncState.NbTransmissions--; | |||
} | |||
} | |||
} | |||
static void LmhpClockSyncOnMcpsConfirm( McpsConfirm_t *mcpsConfirm ) | |||
{ | |||
MibRequestConfirm_t mibReq; | |||
if( LmhpClockSyncState.AppTimeReqPending == true ) | |||
{ | |||
// Revert ADR setting | |||
mibReq.Type = MIB_ADR; | |||
mibReq.Param.AdrEnable = LmhpClockSyncState.AdrEnabledPrev; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
// Revert NbTrans setting | |||
mibReq.Type = MIB_CHANNELS_NB_TRANS; | |||
mibReq.Param.ChannelsNbTrans = LmhpClockSyncState.NbTransPrev; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
// Revert data rate setting | |||
mibReq.Type = MIB_CHANNELS_DATARATE; | |||
mibReq.Param.ChannelsDatarate = LmhpClockSyncState.DataratePrev; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
LmhpClockSyncState.AppTimeReqPending = false; | |||
} | |||
} | |||
static void LmhpClockSyncOnMcpsIndication( McpsIndication_t *mcpsIndication ) | |||
{ | |||
uint8_t cmdIndex = 0; | |||
uint8_t dataBufferIndex = 0; | |||
if( mcpsIndication->Port != CLOCK_SYNC_PORT ) | |||
{ | |||
return; | |||
} | |||
while( cmdIndex < mcpsIndication->BufferSize ) | |||
{ | |||
switch( mcpsIndication->Buffer[cmdIndex++] ) | |||
{ | |||
case CLOCK_SYNC_PKG_VERSION_REQ: | |||
{ | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_PKG_VERSION_ANS; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_ID; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_VERSION; | |||
break; | |||
} | |||
case CLOCK_SYNC_APP_TIME_ANS: | |||
{ | |||
LmhpClockSyncState.NbTransmissions = 0; | |||
// Check if a more precise time correction has been received. | |||
// If yes then don't process and ignore this answer. | |||
if( mcpsIndication->DeviceTimeAnsReceived == true ) | |||
{ | |||
cmdIndex += 5; | |||
break; | |||
} | |||
int32_t timeCorrection = 0; | |||
timeCorrection = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; | |||
timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; | |||
timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; | |||
timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; | |||
if( ( mcpsIndication->Buffer[cmdIndex++] & 0x0F ) == LmhpClockSyncState.TimeReqParam.Fields.TokenReq ) | |||
{ | |||
SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 }; | |||
curTime = SysTimeGet( ); | |||
curTime.Seconds += timeCorrection; | |||
SysTimeSet( curTime ); | |||
LmhpClockSyncState.TimeReqParam.Fields.TokenReq = ( LmhpClockSyncState.TimeReqParam.Fields.TokenReq + 1 ) & 0x0F; | |||
if( LmhpClockSyncPackage.OnSysTimeUpdate != NULL ) | |||
{ | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
LmhpClockSyncPackage.OnSysTimeUpdate( | |||
( timeCorrection >= -1 ) && ( timeCorrection <= 1 ), | |||
timeCorrection ); | |||
#else | |||
if( ( timeCorrection >= -1 ) && ( timeCorrection <= 1 ) ) | |||
{ | |||
LmhpClockSyncPackage.OnSysTimeUpdate( ); | |||
} | |||
#endif | |||
} | |||
} | |||
break; | |||
} | |||
case CLOCK_SYNC_APP_TIME_PERIOD_REQ: | |||
{ | |||
// Increment index | |||
cmdIndex++; | |||
// TODO implement command prosessing and handling | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_APP_TIME_PERIOD_ANS; | |||
// Answer status not supported. | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = 0x01; | |||
SysTime_t curTime = SysTimeGet( ); | |||
// Substract Unix to Gps epcoh offset. The system time is based on Unix time. | |||
curTime.Seconds -= UNIX_GPS_EPOCH_OFFSET; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 0 ) & 0xFF; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 8 ) & 0xFF; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 16 ) & 0xFF; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 24 ) & 0xFF; | |||
break; | |||
} | |||
case CLOCK_SYNC_FORCE_RESYNC_REQ: | |||
{ | |||
LmhpClockSyncState.NbTransmissions = mcpsIndication->Buffer[cmdIndex++] & 0X07; | |||
break; | |||
} | |||
} | |||
} | |||
if( dataBufferIndex != 0 ) | |||
{ | |||
// Answer commands | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = LmhpClockSyncState.DataBuffer, | |||
.BufferSize = dataBufferIndex, | |||
.Port = CLOCK_SYNC_PORT | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
} | |||
LmHandlerErrorStatus_t LmhpClockSyncAppTimeReq( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return LORAMAC_HANDLER_ERROR; | |||
} | |||
if( LmhpClockSyncState.AppTimeReqPending == false ) | |||
{ | |||
MibRequestConfirm_t mibReq; | |||
// Disable ADR | |||
mibReq.Type = MIB_ADR; | |||
LoRaMacMibGetRequestConfirm( &mibReq ); | |||
LmhpClockSyncState.AdrEnabledPrev = mibReq.Param.AdrEnable; | |||
mibReq.Param.AdrEnable = false; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
// Set NbTrans = 1 | |||
mibReq.Type = MIB_CHANNELS_NB_TRANS; | |||
LoRaMacMibGetRequestConfirm( &mibReq ); | |||
LmhpClockSyncState.NbTransPrev = mibReq.Param.ChannelsNbTrans; | |||
mibReq.Param.ChannelsNbTrans = 1; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
// Store data rate | |||
mibReq.Type = MIB_CHANNELS_DATARATE; | |||
LoRaMacMibGetRequestConfirm( &mibReq ); | |||
LmhpClockSyncState.DataratePrev = mibReq.Param.ChannelsDatarate; | |||
// Add DeviceTimeReq MAC command. | |||
// In case the network server supports this more precise command | |||
// this package will use DeviceTimeAns answer as clock synchronization | |||
// mechanism. | |||
LmhpClockSyncPackage.OnDeviceTimeRequest( ); | |||
} | |||
SysTime_t curTime = SysTimeGet( ); | |||
uint8_t dataBufferIndex = 0; | |||
// Substract Unix to Gps epcoh offset. The system time is based on Unix time. | |||
curTime.Seconds -= UNIX_GPS_EPOCH_OFFSET; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_APP_TIME_REQ; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 0 ) & 0xFF; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 8 ) & 0xFF; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 16 ) & 0xFF; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 24 ) & 0xFF; | |||
LmhpClockSyncState.TimeReqParam.Fields.AnsRequired = 0; | |||
LmhpClockSyncState.DataBuffer[dataBufferIndex++] = LmhpClockSyncState.TimeReqParam.Value; | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = LmhpClockSyncState.DataBuffer, | |||
.BufferSize = dataBufferIndex, | |||
.Port = CLOCK_SYNC_PORT | |||
}; | |||
LmhpClockSyncState.AppTimeReqPending = true; | |||
return LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} |
@@ -0,0 +1,49 @@ | |||
/*! | |||
* \file LmhpClockSync.h | |||
* | |||
* \brief Implements the LoRa-Alliance clock synchronization package | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/application_layer_clock_synchronization_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LMHP_CLOCK_SYNC_H__ | |||
#define __LMHP_CLOCK_SYNC_H__ | |||
#include "LoRaMac.h" | |||
#include "LmHandlerTypes.h" | |||
#include "LmhPackage.h" | |||
/*! | |||
* Clock sync package identifier. | |||
* | |||
* \remark This value must be unique amongst the packages | |||
*/ | |||
#define PACKAGE_ID_CLOCK_SYNC 1 | |||
/*! | |||
* Clock sync package parameters | |||
* | |||
* This package doesn't require parameters | |||
*/ | |||
//typedef struct LmphClockSyncParams_s | |||
//{ | |||
//}LmphClockSyncParams_t; | |||
LmhPackage_t *LmphClockSyncPackageFactory( void ); | |||
LmHandlerErrorStatus_t LmhpClockSyncAppTimeReq( void ); | |||
#endif // __LMHP_CLOCK_SYNC_H__ |
@@ -0,0 +1,590 @@ | |||
/*! | |||
* \file LmhpCompliance.c | |||
* | |||
* \brief Implements the LoRa-Alliance certification protocol handling | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#include <stdlib.h> | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include "board.h" | |||
#include "NvmDataMgmt.h" | |||
#include "LoRaMacTest.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
/*! | |||
* LoRaWAN compliance certification protocol port number. | |||
* | |||
* LoRaWAN Specification V1.x.x, chapter 4.3.2 | |||
*/ | |||
#define COMPLIANCE_PORT 224 | |||
#define COMPLIANCE_ID 6 | |||
#define COMPLIANCE_VERSION 1 | |||
typedef struct ClassBStatus_s | |||
{ | |||
bool IsBeaconRxOn; | |||
uint8_t PingSlotPeriodicity; | |||
uint16_t BeaconCnt; | |||
BeaconInfo_t Info; | |||
} ClassBStatus_t; | |||
/*! | |||
* LoRaWAN compliance tests support data | |||
*/ | |||
typedef struct ComplianceTestState_s | |||
{ | |||
bool Initialized; | |||
bool IsTxPending; | |||
TimerTime_t TxPendingTimestamp; | |||
LmHandlerMsgTypes_t IsTxConfirmed; | |||
uint8_t DataBufferMaxSize; | |||
uint8_t DataBufferSize; | |||
uint8_t* DataBuffer; | |||
uint16_t RxAppCnt; | |||
ClassBStatus_t ClassBStatus; | |||
bool IsResetCmdPending; | |||
} ComplianceTestState_t; | |||
typedef enum ComplianceMoteCmd_e | |||
{ | |||
COMPLIANCE_PKG_VERSION_ANS = 0x00, | |||
COMPLIANCE_ECHO_PAYLOAD_ANS = 0x08, | |||
COMPLIANCE_RX_APP_CNT_ANS = 0x09, | |||
COMPLIANCE_BEACON_RX_STATUS_IND = 0x40, | |||
COMPLIANCE_BEACON_CNT_ANS = 0x41, | |||
COMPLIANCE_DUT_VERSION_ANS = 0x7F, | |||
} ComplianceMoteCmd_t; | |||
typedef enum ComplianceSrvCmd_e | |||
{ | |||
COMPLIANCE_PKG_VERSION_REQ = 0x00, | |||
COMPLIANCE_DUT_RESET_REQ = 0x01, | |||
COMPLIANCE_DUT_JOIN_REQ = 0x02, | |||
COMPLIANCE_SWITCH_CLASS_REQ = 0x03, | |||
COMPLIANCE_ADR_BIT_CHANGE_REQ = 0x04, | |||
COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ = 0x05, | |||
COMPLIANCE_TX_PERIODICITY_CHANGE_REQ = 0x06, | |||
COMPLIANCE_TX_FRAMES_CTRL_REQ = 0x07, | |||
COMPLIANCE_ECHO_PAYLOAD_REQ = 0x08, | |||
COMPLIANCE_RX_APP_CNT_REQ = 0x09, | |||
COMPLIANCE_RX_APP_CNT_RESET_REQ = 0x0A, | |||
COMPLIANCE_LINK_CHECK_REQ = 0x20, | |||
COMPLIANCE_DEVICE_TIME_REQ = 0x21, | |||
COMPLIANCE_PING_SLOT_INFO_REQ = 0x22, | |||
COMPLIANCE_BEACON_CNT_REQ = 0x41, | |||
COMPLIANCE_BEACON_CNT_RESET_REQ = 0x42, | |||
COMPLIANCE_TX_CW_REQ = 0x7D, | |||
COMPLIANCE_DUT_FPORT_224_DISABLE_REQ = 0x7E, | |||
COMPLIANCE_DUT_VERSION_REQ = 0x7F, | |||
} ComplianceSrvCmd_t; | |||
/*! | |||
* Holds the compliance test current context | |||
*/ | |||
static ComplianceTestState_t ComplianceTestState = { | |||
.Initialized = false, | |||
.IsTxPending = false, | |||
.TxPendingTimestamp = 0, | |||
.IsTxConfirmed = LORAMAC_HANDLER_UNCONFIRMED_MSG, | |||
.DataBufferMaxSize = 0, | |||
.DataBufferSize = 0, | |||
.DataBuffer = NULL, | |||
.RxAppCnt = 0, | |||
.ClassBStatus = { 0 }, | |||
.IsResetCmdPending = false, | |||
}; | |||
/*! | |||
* LoRaWAN compliance tests protocol handler parameters | |||
*/ | |||
static LmhpComplianceParams_t* ComplianceParams; | |||
/*! | |||
* Reset Beacon status structure | |||
*/ | |||
static inline void ClassBStatusReset( void ) | |||
{ | |||
memset1( ( uint8_t* ) &ComplianceTestState.ClassBStatus, 0, sizeof( ClassBStatus_t ) / sizeof( uint8_t ) ); | |||
} | |||
/*! | |||
* Initializes the compliance tests with provided parameters | |||
* | |||
* \param [IN] params Structure containing the initial compliance | |||
* tests parameters. | |||
* \param [IN] dataBuffer Pointer to main application buffer | |||
* \param [IN] dataBufferMaxSize Application buffer maximum size | |||
*/ | |||
static void LmhpComplianceInit( void* params, uint8_t* dataBuffer, uint8_t dataBufferMaxSize ); | |||
/*! | |||
* Returns the current compliance certification protocol initialization status. | |||
* | |||
* \retval status Compliance certification protocol initialization status | |||
* [true: Initialized, false: Not initialized] | |||
*/ | |||
static bool LmhpComplianceIsInitialized( void ); | |||
/*! | |||
* Returns if a package transmission is pending or not. | |||
* | |||
* \retval status Package transmission status | |||
* [true: pending, false: Not pending] | |||
*/ | |||
static bool LmhpComplianceIsTxPending( void ); | |||
/*! | |||
* Processes the LoRaMac Compliance events. | |||
*/ | |||
static void LmhpComplianceProcess( void ); | |||
/*! | |||
* Processes the MCPS Indication | |||
* | |||
* \param [IN] mcpsIndication MCPS indication primitive data | |||
*/ | |||
static void LmhpComplianceOnMcpsIndication( McpsIndication_t* mcpsIndication ); | |||
/*! | |||
* Processes the MLME Confirm | |||
* | |||
* \param [IN] mlmeConfirm MLME confirmation primitive data | |||
*/ | |||
static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm ); | |||
/*! | |||
* Processes the MLME Indication | |||
* | |||
* \param [IN] mlmeIndication MLME indication primitive data | |||
*/ | |||
static void LmhpComplianceOnMlmeIndication( MlmeIndication_t* mlmeIndication ); | |||
/*! | |||
* Helper function to send the BeaconRxStatusInd message | |||
*/ | |||
static void SendBeaconRxStatusInd( void ); | |||
LmhPackage_t CompliancePackage = { | |||
.Port = COMPLIANCE_PORT, | |||
.Init = LmhpComplianceInit, | |||
.IsInitialized = LmhpComplianceIsInitialized, | |||
.IsTxPending = LmhpComplianceIsTxPending, | |||
.Process = LmhpComplianceProcess, | |||
.OnMcpsConfirmProcess = NULL, // Not used in this package | |||
.OnMcpsIndicationProcess = LmhpComplianceOnMcpsIndication, | |||
.OnMlmeConfirmProcess = LmhpComplianceOnMlmeConfirm, | |||
.OnMlmeIndicationProcess = LmhpComplianceOnMlmeIndication, | |||
.OnMacMcpsRequest = NULL, // To be initialized by LmHandler | |||
.OnMacMlmeRequest = NULL, // To be initialized by LmHandler | |||
.OnJoinRequest = NULL, // To be initialized by LmHandler | |||
.OnDeviceTimeRequest = NULL, // To be initialized by LmHandler | |||
.OnSysTimeUpdate = NULL, // To be initialized by LmHandler | |||
}; | |||
LmhPackage_t* LmphCompliancePackageFactory( void ) | |||
{ | |||
return &CompliancePackage; | |||
} | |||
static void LmhpComplianceInit( void* params, uint8_t* dataBuffer, uint8_t dataBufferMaxSize ) | |||
{ | |||
if( ( params != NULL ) && ( dataBuffer != NULL ) ) | |||
{ | |||
ComplianceParams = ( LmhpComplianceParams_t* ) params; | |||
ComplianceTestState.DataBuffer = dataBuffer; | |||
ComplianceTestState.DataBufferMaxSize = dataBufferMaxSize; | |||
ComplianceTestState.Initialized = true; | |||
} | |||
else | |||
{ | |||
ComplianceParams = NULL; | |||
ComplianceTestState.Initialized = false; | |||
} | |||
ComplianceTestState.RxAppCnt = 0; | |||
ClassBStatusReset( ); | |||
ComplianceTestState.IsTxPending = false; | |||
ComplianceTestState.IsResetCmdPending = false; | |||
} | |||
static bool LmhpComplianceIsInitialized( void ) | |||
{ | |||
return ComplianceTestState.Initialized; | |||
} | |||
static bool LmhpComplianceIsTxPending( void ) | |||
{ | |||
return ComplianceTestState.IsTxPending; | |||
} | |||
static void LmhpComplianceProcess( void ) | |||
{ | |||
if( ComplianceTestState.IsTxPending == true ) | |||
{ | |||
TimerTime_t now = TimerGetCurrentTime( ); | |||
if( now > ( ComplianceTestState.TxPendingTimestamp + LmHandlerGetDutyCycleWaitTime( ) ) ) | |||
{ | |||
if( ComplianceTestState.DataBufferSize != 0 ) | |||
{ | |||
// Answer commands | |||
LmHandlerAppData_t appData = { | |||
.Buffer = ComplianceTestState.DataBuffer, | |||
.BufferSize = ComplianceTestState.DataBufferSize, | |||
.Port = COMPLIANCE_PORT, | |||
}; | |||
if( LmHandlerSend( &appData, ComplianceTestState.IsTxConfirmed ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// try to send the message again | |||
ComplianceTestState.IsTxPending = true; | |||
} | |||
else | |||
{ | |||
ComplianceTestState.IsTxPending = false; | |||
} | |||
ComplianceTestState.TxPendingTimestamp = now; | |||
} | |||
} | |||
} | |||
if( ComplianceTestState.IsResetCmdPending == true ) | |||
{ | |||
ComplianceTestState.IsResetCmdPending = false; | |||
// Call platform MCU reset API | |||
BoardResetMcu( ); | |||
} | |||
} | |||
static void LmhpComplianceOnMcpsIndication( McpsIndication_t* mcpsIndication ) | |||
{ | |||
uint8_t cmdIndex = 0; | |||
MibRequestConfirm_t mibReq; | |||
if( ComplianceTestState.Initialized == false ) | |||
{ | |||
return; | |||
} | |||
// Increment the compliance certification protocol downlink counter | |||
// Not counting downlinks on FPort 0 | |||
if( ( mcpsIndication->Port > 0 ) || ( mcpsIndication->AckReceived == true ) ) | |||
{ | |||
ComplianceTestState.RxAppCnt++; | |||
} | |||
if( mcpsIndication->RxData == false ) | |||
{ | |||
return; | |||
} | |||
if( mcpsIndication->Port != COMPLIANCE_PORT ) | |||
{ | |||
return; | |||
} | |||
ComplianceTestState.DataBufferSize = 0; | |||
switch( mcpsIndication->Buffer[cmdIndex++] ) | |||
{ | |||
case COMPLIANCE_PKG_VERSION_REQ: | |||
{ | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_PKG_VERSION_ANS; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ID; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_VERSION; | |||
break; | |||
} | |||
case COMPLIANCE_DUT_RESET_REQ: | |||
{ | |||
ComplianceTestState.IsResetCmdPending = true; | |||
break; | |||
} | |||
case COMPLIANCE_DUT_JOIN_REQ: | |||
{ | |||
CompliancePackage.OnJoinRequest( true ); | |||
break; | |||
} | |||
case COMPLIANCE_SWITCH_CLASS_REQ: | |||
{ | |||
MibRequestConfirm_t mibReq; | |||
mibReq.Type = MIB_DEVICE_CLASS; | |||
// CLASS_A = 0, CLASS_B = 1, CLASS_C = 2 | |||
mibReq.Param.Class = ( DeviceClass_t ) mcpsIndication->Buffer[cmdIndex++]; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
break; | |||
} | |||
case COMPLIANCE_ADR_BIT_CHANGE_REQ: | |||
{ | |||
MibRequestConfirm_t mibReq; | |||
mibReq.Type = MIB_ADR; | |||
mibReq.Param.AdrEnable = mcpsIndication->Buffer[cmdIndex++]; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
break; | |||
} | |||
case COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ: | |||
{ | |||
LoRaMacTestSetDutyCycleOn( mcpsIndication->Buffer[cmdIndex++] ); | |||
break; | |||
} | |||
case COMPLIANCE_TX_PERIODICITY_CHANGE_REQ: | |||
{ | |||
// Periodicity in milli-seconds | |||
uint32_t periodicity[] = { 0, 5000, 10000, 20000, 30000, 40000, 50000, 60000, 120000, 240000, 480000 }; | |||
uint8_t index = mcpsIndication->Buffer[cmdIndex++]; | |||
if( index < ( sizeof( periodicity ) / sizeof( uint32_t ) ) ) | |||
{ | |||
if( ComplianceParams->OnTxPeriodicityChanged != NULL ) | |||
{ | |||
ComplianceParams->OnTxPeriodicityChanged( periodicity[index] ); | |||
} | |||
} | |||
break; | |||
} | |||
case COMPLIANCE_TX_FRAMES_CTRL_REQ: | |||
{ | |||
uint8_t frameType = mcpsIndication->Buffer[cmdIndex++]; | |||
if( ( frameType == 1 ) || ( frameType == 2 ) ) | |||
{ | |||
ComplianceTestState.IsTxConfirmed = ( frameType != 1 ) ? LORAMAC_HANDLER_CONFIRMED_MSG : LORAMAC_HANDLER_UNCONFIRMED_MSG; | |||
ComplianceParams->OnTxFrameCtrlChanged( ComplianceTestState.IsTxConfirmed ); | |||
} | |||
break; | |||
} | |||
case COMPLIANCE_ECHO_PAYLOAD_REQ: | |||
{ | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ECHO_PAYLOAD_ANS; | |||
for( uint8_t i = 1; i < MIN( mcpsIndication->BufferSize, ComplianceTestState.DataBufferMaxSize ); | |||
i++ ) | |||
{ | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = mcpsIndication->Buffer[cmdIndex++] + 1; | |||
} | |||
break; | |||
} | |||
case COMPLIANCE_RX_APP_CNT_REQ: | |||
{ | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_RX_APP_CNT_ANS; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt >> 8; | |||
break; | |||
} | |||
case COMPLIANCE_RX_APP_CNT_RESET_REQ: | |||
{ | |||
ComplianceTestState.RxAppCnt = 0; | |||
break; | |||
} | |||
case COMPLIANCE_LINK_CHECK_REQ: | |||
{ | |||
MlmeReq_t mlmeReq; | |||
mlmeReq.Type = MLME_LINK_CHECK; | |||
CompliancePackage.OnMacMlmeRequest( LoRaMacMlmeRequest( &mlmeReq ), &mlmeReq, | |||
mlmeReq.ReqReturn.DutyCycleWaitTime ); | |||
break; | |||
} | |||
case COMPLIANCE_DEVICE_TIME_REQ: | |||
{ | |||
CompliancePackage.OnDeviceTimeRequest( ); | |||
break; | |||
} | |||
case COMPLIANCE_PING_SLOT_INFO_REQ: | |||
{ | |||
ComplianceTestState.ClassBStatus.PingSlotPeriodicity = mcpsIndication->Buffer[cmdIndex++]; | |||
ComplianceParams->OnPingSlotPeriodicityChanged( ComplianceTestState.ClassBStatus.PingSlotPeriodicity ); | |||
break; | |||
} | |||
case COMPLIANCE_BEACON_CNT_REQ: | |||
{ | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_CNT_ANS; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt >> 8; | |||
break; | |||
} | |||
case COMPLIANCE_BEACON_CNT_RESET_REQ: | |||
{ | |||
ComplianceTestState.ClassBStatus.BeaconCnt = 0; | |||
break; | |||
} | |||
case COMPLIANCE_TX_CW_REQ: | |||
{ | |||
MlmeReq_t mlmeReq; | |||
if( mcpsIndication->BufferSize == 7 ) | |||
{ | |||
mlmeReq.Type = MLME_TXCW; | |||
mlmeReq.Req.TxCw.Timeout = | |||
( uint16_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) ); | |||
cmdIndex += 2; | |||
mlmeReq.Req.TxCw.Frequency = | |||
( uint32_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) | | |||
( mcpsIndication->Buffer[cmdIndex + 2] << 16 ) ) * | |||
100; | |||
cmdIndex += 3; | |||
mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[cmdIndex++]; | |||
CompliancePackage.OnMacMlmeRequest( LoRaMacMlmeRequest( &mlmeReq ), &mlmeReq, | |||
mlmeReq.ReqReturn.DutyCycleWaitTime ); | |||
} | |||
break; | |||
} | |||
case COMPLIANCE_DUT_FPORT_224_DISABLE_REQ: | |||
{ | |||
mibReq.Type = MIB_IS_CERT_FPORT_ON; | |||
mibReq.Param.IsCertPortOn = false; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
ComplianceTestState.IsResetCmdPending = true; | |||
break; | |||
} | |||
case COMPLIANCE_DUT_VERSION_REQ: | |||
{ | |||
Version_t lrwanVersion; | |||
Version_t lrwanRpVersion; | |||
MibRequestConfirm_t mibReq; | |||
mibReq.Type = MIB_LORAWAN_VERSION; | |||
LoRaMacMibGetRequestConfirm( &mibReq ); | |||
lrwanVersion = mibReq.Param.LrWanVersion.LoRaWan; | |||
lrwanRpVersion = mibReq.Param.LrWanVersion.LoRaWanRegion; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_DUT_VERSION_ANS; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Major; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Minor; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Patch; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Revision; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Major; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Minor; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Patch; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Revision; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Major; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Minor; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Patch; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Revision; | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
if( ComplianceTestState.DataBufferSize != 0 ) | |||
{ | |||
ComplianceTestState.IsTxPending = true; | |||
} | |||
else | |||
{ | |||
// Abort any pending Tx as a new command has been processed | |||
ComplianceTestState.IsTxPending = false; | |||
} | |||
} | |||
static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm ) | |||
{ | |||
switch( mlmeConfirm->MlmeRequest ) | |||
{ | |||
case MLME_BEACON_ACQUISITION: | |||
{ | |||
if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) | |||
{ | |||
ClassBStatusReset( ); | |||
ComplianceTestState.ClassBStatus.IsBeaconRxOn = true; | |||
} | |||
else | |||
{ | |||
ComplianceTestState.ClassBStatus.IsBeaconRxOn = false; | |||
} | |||
SendBeaconRxStatusInd( ); | |||
break; | |||
} | |||
default: | |||
break; | |||
} | |||
} | |||
static void LmhpComplianceOnMlmeIndication( MlmeIndication_t* mlmeIndication ) | |||
{ | |||
if( ComplianceTestState.Initialized == false ) | |||
{ | |||
return; | |||
} | |||
switch( mlmeIndication->MlmeIndication ) | |||
{ | |||
case MLME_BEACON_LOST: | |||
{ | |||
ClassBStatusReset( ); | |||
SendBeaconRxStatusInd( ); | |||
break; | |||
} | |||
case MLME_BEACON: | |||
{ | |||
if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED ) | |||
{ | |||
// As we received a beacon ensure that IsBeaconRxOn is set to true | |||
if( ComplianceTestState.ClassBStatus.IsBeaconRxOn == false ) | |||
{ | |||
ComplianceTestState.ClassBStatus.IsBeaconRxOn = true; | |||
} | |||
ComplianceTestState.ClassBStatus.BeaconCnt++; | |||
} | |||
ComplianceTestState.ClassBStatus.Info = mlmeIndication->BeaconInfo; | |||
SendBeaconRxStatusInd( ); | |||
break; | |||
} | |||
default: | |||
break; | |||
} | |||
} | |||
static void SendBeaconRxStatusInd( void ) | |||
{ | |||
uint32_t frequency = ComplianceTestState.ClassBStatus.Info.Frequency / 100; | |||
ComplianceTestState.DataBufferSize = 0; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_RX_STATUS_IND; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( ComplianceTestState.ClassBStatus.IsBeaconRxOn == true ) ? 1 : 0; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt >> 8 ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 8 ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 16 ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.Info.Datarate; | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi >> 8 ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Snr ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Param ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 8 ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 16 ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 24 ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.InfoDesc ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[0] ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[1] ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[2] ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[3] ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[4] ); | |||
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[5] ); | |||
ComplianceTestState.IsTxPending = true; | |||
} |
@@ -0,0 +1,60 @@ | |||
/*! | |||
* \file LmhpCompliance.h | |||
* | |||
* \brief Implements the LoRa-Alliance certification protocol handling | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LMHP_COMPLIANCE__ | |||
#define __LMHP_COMPLIANCE__ | |||
#include "LoRaMac.h" | |||
#include "LmHandlerTypes.h" | |||
#include "LmhPackage.h" | |||
/*! | |||
* Compliance package identifier. | |||
* | |||
* \remark This value must be unique amongst the packages | |||
*/ | |||
#define PACKAGE_ID_COMPLIANCE 0 | |||
/*! | |||
* Compliance test protocol handler parameters | |||
*/ | |||
typedef struct LmhpComplianceParams_s | |||
{ | |||
/*! | |||
* Current firmware version | |||
*/ | |||
Version_t FwVersion; | |||
/*! | |||
* | |||
*/ | |||
void ( *OnTxPeriodicityChanged )( uint32_t periodicity ); | |||
/*! | |||
* | |||
*/ | |||
void ( *OnTxFrameCtrlChanged )( LmHandlerMsgTypes_t isTxConfirmed ); | |||
/*! | |||
* | |||
*/ | |||
void ( *OnPingSlotPeriodicityChanged )( uint8_t pingSlotPeriodicity ); | |||
}LmhpComplianceParams_t; | |||
LmhPackage_t *LmphCompliancePackageFactory( void ); | |||
#endif // __LMHP_COMPLIANCE__ |
@@ -0,0 +1,529 @@ | |||
/*! | |||
* \file LmhpFragmentation.c | |||
* | |||
* \brief Implements the LoRa-Alliance fragmented data block transport package | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#include "LmHandler.h" | |||
#include "LmhpFragmentation.h" | |||
#include "FragDecoder.h" | |||
/*! | |||
* LoRaWAN Application Layer Fragmented Data Block Transport Specification | |||
*/ | |||
#define FRAGMENTATION_PORT 201 | |||
#define FRAGMENTATION_ID 3 | |||
#define FRAGMENTATION_VERSION 1 | |||
#define FRAGMENTATION_MAX_SESSIONS 4 | |||
// Fragmentation Tx delay state | |||
typedef enum LmhpFragmentationTxDelayStates_e | |||
{ | |||
// Tx delay in idle state. | |||
FRAGMENTATION_TX_DELAY_STATE_IDLE, | |||
// Tx delay to be started. | |||
FRAGMENTATION_TX_DELAY_STATE_START, | |||
// Tx delay to be stopped. | |||
FRAGMENTATION_TX_DELAY_STATE_STOP, | |||
}LmhpFragmentationTxDelayStates_t; | |||
/*! | |||
* Package current context | |||
*/ | |||
typedef struct LmhpFragmentationState_s | |||
{ | |||
bool Initialized; | |||
bool IsTxPending; | |||
LmhpFragmentationTxDelayStates_t TxDelayState; | |||
uint8_t DataBufferMaxSize; | |||
uint8_t *DataBuffer; | |||
uint8_t *file; | |||
}LmhpFragmentationState_t; | |||
typedef enum LmhpFragmentationMoteCmd_e | |||
{ | |||
FRAGMENTATION_PKG_VERSION_ANS = 0x00, | |||
FRAGMENTATION_FRAG_STATUS_ANS = 0x01, | |||
FRAGMENTATION_FRAG_SESSION_SETUP_ANS = 0x02, | |||
FRAGMENTATION_FRAG_SESSION_DELETE_ANS = 0x03, | |||
}LmhpFragmentationMoteCmd_t; | |||
typedef enum LmhpFragmentationSrvCmd_e | |||
{ | |||
FRAGMENTATION_PKG_VERSION_REQ = 0x00, | |||
FRAGMENTATION_FRAG_STATUS_REQ = 0x01, | |||
FRAGMENTATION_FRAG_SESSION_SETUP_REQ = 0x02, | |||
FRAGMENTATION_FRAG_SESSION_DELETE_REQ = 0x03, | |||
FRAGMENTATION_DATA_FRAGMENT = 0x08, | |||
}LmhpFragmentationSrvCmd_t; | |||
/*! | |||
* LoRaWAN fragmented data block transport handler parameters | |||
*/ | |||
static LmhpFragmentationParams_t* LmhpFragmentationParams; | |||
/*! | |||
* Initializes the package with provided parameters | |||
* | |||
* \param [IN] params Pointer to the package parameters | |||
* \param [IN] dataBuffer Pointer to main application buffer | |||
* \param [IN] dataBufferMaxSize Main application buffer maximum size | |||
*/ | |||
static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); | |||
/*! | |||
* Returns the current package initialization status. | |||
* | |||
* \retval status Package initialization status | |||
* [true: Initialized, false: Not initialized] | |||
*/ | |||
static bool LmhpFragmentationIsInitialized( void ); | |||
/*! | |||
* Returns if a package transmission is pending or not. | |||
* | |||
* \retval status Package transmission status | |||
* [true: pending, false: Not pending] | |||
*/ | |||
static bool LmhpFragmentationIsTxPending( void ); | |||
/*! | |||
* Processes the internal package events. | |||
*/ | |||
static void LmhpFragmentationProcess( void ); | |||
/*! | |||
* Processes the MCPS Indication | |||
* | |||
* \param [IN] mcpsIndication MCPS indication primitive data | |||
*/ | |||
static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication ); | |||
static LmhpFragmentationState_t LmhpFragmentationState = | |||
{ | |||
.Initialized = false, | |||
.IsTxPending = false, | |||
.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_IDLE, | |||
}; | |||
typedef struct FragGroupData_s | |||
{ | |||
bool IsActive; | |||
union | |||
{ | |||
uint8_t Value; | |||
struct | |||
{ | |||
uint8_t McGroupBitMask: 4; | |||
uint8_t FragIndex: 2; | |||
uint8_t RFU: 2; | |||
}Fields; | |||
}FragSession; | |||
uint16_t FragNb; | |||
uint8_t FragSize; | |||
union | |||
{ | |||
uint8_t Value; | |||
struct | |||
{ | |||
uint8_t BlockAckDelay: 3; | |||
uint8_t FragAlgo: 3; | |||
uint8_t RFU: 2; | |||
}Fields; | |||
}Control; | |||
uint8_t Padding; | |||
uint32_t Descriptor; | |||
}FragGroupData_t; | |||
typedef struct FragSessionData_s | |||
{ | |||
FragGroupData_t FragGroupData; | |||
FragDecoderStatus_t FragDecoderStatus; | |||
int32_t FragDecoderPorcessStatus; | |||
}FragSessionData_t; | |||
FragSessionData_t FragSessionData[FRAGMENTATION_MAX_SESSIONS]; | |||
// Answer struct for the commands. | |||
LmHandlerAppData_t DelayedReplyAppData; | |||
static LmhPackage_t LmhpFragmentationPackage = | |||
{ | |||
.Port = FRAGMENTATION_PORT, | |||
.Init = LmhpFragmentationInit, | |||
.IsInitialized = LmhpFragmentationIsInitialized, | |||
.IsTxPending = LmhpFragmentationIsTxPending, | |||
.Process = LmhpFragmentationProcess, | |||
.OnMcpsConfirmProcess = NULL, // Not used in this package | |||
.OnMcpsIndicationProcess = LmhpFragmentationOnMcpsIndication, | |||
.OnMlmeConfirmProcess = NULL, // Not used in this package | |||
.OnMlmeIndicationProcess = NULL, // Not used in this package | |||
.OnMacMcpsRequest = NULL, // To be initialized by LmHandler | |||
.OnMacMlmeRequest = NULL, // To be initialized by LmHandler | |||
.OnJoinRequest = NULL, // To be initialized by LmHandler | |||
.OnDeviceTimeRequest = NULL, // To be initialized by LmHandler | |||
.OnSysTimeUpdate = NULL, // To be initialized by LmHandler | |||
}; | |||
// Delay value. | |||
static uint32_t TxDelayTime; | |||
// Fragment Delay Timer struct | |||
static TimerEvent_t FragmentTxDelayTimer; | |||
/*! | |||
* \brief Callback function for Fragment delay timer. | |||
*/ | |||
static void OnFragmentTxDelay( void* context ) | |||
{ | |||
// Stop the timer. | |||
TimerStop( &FragmentTxDelayTimer ); | |||
// Set the state. | |||
LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_STOP; | |||
} | |||
LmhPackage_t *LmhpFragmentationPackageFactory( void ) | |||
{ | |||
return &LmhpFragmentationPackage; | |||
} | |||
static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ) | |||
{ | |||
if( ( params != NULL ) && ( dataBuffer != NULL ) ) | |||
{ | |||
LmhpFragmentationParams = ( LmhpFragmentationParams_t* )params; | |||
LmhpFragmentationState.DataBuffer = dataBuffer; | |||
LmhpFragmentationState.DataBufferMaxSize = dataBufferMaxSize; | |||
LmhpFragmentationState.Initialized = true; | |||
// Initialize Fragmentation delay time. | |||
TxDelayTime = 0; | |||
// Initialize Fragmentation delay timer. | |||
TimerInit( &FragmentTxDelayTimer, OnFragmentTxDelay ); | |||
} | |||
else | |||
{ | |||
LmhpFragmentationParams = NULL; | |||
LmhpFragmentationState.Initialized = false; | |||
} | |||
LmhpFragmentationState.IsTxPending = false; | |||
} | |||
static bool LmhpFragmentationIsInitialized( void ) | |||
{ | |||
return LmhpFragmentationState.Initialized; | |||
} | |||
static bool LmhpFragmentationIsTxPending( void ) | |||
{ | |||
return LmhpFragmentationState.IsTxPending; | |||
} | |||
static void LmhpFragmentationProcess( void ) | |||
{ | |||
LmhpFragmentationTxDelayStates_t delayTimerState; | |||
CRITICAL_SECTION_BEGIN( ); | |||
delayTimerState = LmhpFragmentationState.TxDelayState; | |||
// Set the state to idle so that the other states are executed only when they are set | |||
// in the appropriate functions. | |||
LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_IDLE; | |||
CRITICAL_SECTION_END( ); | |||
switch( delayTimerState ) | |||
{ | |||
case FRAGMENTATION_TX_DELAY_STATE_START: | |||
// Set the timer with the initially calculated Delay value. | |||
TimerSetValue( &FragmentTxDelayTimer, TxDelayTime ); | |||
// Start the timer. | |||
TimerStart( &FragmentTxDelayTimer ); | |||
break; | |||
case FRAGMENTATION_TX_DELAY_STATE_STOP: | |||
// Send the reply. | |||
LmHandlerSend( &DelayedReplyAppData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
break; | |||
case FRAGMENTATION_TX_DELAY_STATE_IDLE: | |||
// Intentional fall through | |||
default: | |||
// Nothing to do. | |||
break; | |||
} | |||
} | |||
static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication ) | |||
{ | |||
uint8_t cmdIndex = 0; | |||
uint8_t dataBufferIndex = 0; | |||
bool isAnswerDelayed = false; | |||
// Answer struct for the commands. | |||
LmHandlerAppData_t cmdReplyAppData; | |||
// Co-efficient used to calculate delay. | |||
uint8_t blockAckDelay = 0; | |||
if( mcpsIndication->Port != FRAGMENTATION_PORT ) | |||
{ | |||
return; | |||
} | |||
while( cmdIndex < mcpsIndication->BufferSize ) | |||
{ | |||
switch( mcpsIndication->Buffer[cmdIndex++] ) | |||
{ | |||
case FRAGMENTATION_PKG_VERSION_REQ: | |||
{ | |||
if( mcpsIndication->Multicast == 1 ) | |||
{ | |||
// Multicast channel. Don't process command. | |||
break; | |||
} | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_PKG_VERSION_ANS; | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_ID; | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_VERSION; | |||
break; | |||
} | |||
case FRAGMENTATION_FRAG_STATUS_REQ: | |||
{ | |||
uint8_t fragIndex = mcpsIndication->Buffer[cmdIndex++]; | |||
uint8_t participants = fragIndex & 0x01; | |||
fragIndex >>= 1; | |||
FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( ); | |||
if( ( participants == 1 ) || | |||
( ( participants == 0 ) && ( FragSessionData[fragIndex].FragDecoderStatus.FragNbLost > 0 ) ) ) | |||
{ | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_STATUS_ANS; | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbRx & 0xFF; | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = ( fragIndex << 6 ) | | |||
( ( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx >> 8 ) & 0x3F ); | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbLost; | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.MatrixError & 0x01; | |||
// Fetch the co-efficient value required to calculate delay of that respective session. | |||
blockAckDelay = FragSessionData[fragIndex].FragGroupData.Control.Fields.BlockAckDelay; | |||
isAnswerDelayed = true; | |||
} | |||
break; | |||
} | |||
case FRAGMENTATION_FRAG_SESSION_SETUP_REQ: | |||
{ | |||
if( mcpsIndication->Multicast == 1 ) | |||
{ | |||
// Multicast channel. Don't process command. | |||
break; | |||
} | |||
FragSessionData_t fragSessionData; | |||
uint8_t status = 0x00; | |||
fragSessionData.FragGroupData.FragSession.Value = mcpsIndication->Buffer[cmdIndex++]; | |||
fragSessionData.FragGroupData.FragNb = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF; | |||
fragSessionData.FragGroupData.FragNb |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00; | |||
fragSessionData.FragGroupData.FragSize = mcpsIndication->Buffer[cmdIndex++]; | |||
fragSessionData.FragGroupData.Control.Value = mcpsIndication->Buffer[cmdIndex++]; | |||
fragSessionData.FragGroupData.Padding = mcpsIndication->Buffer[cmdIndex++]; | |||
fragSessionData.FragGroupData.Descriptor = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; | |||
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; | |||
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; | |||
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; | |||
if( fragSessionData.FragGroupData.Control.Fields.FragAlgo > 0 ) | |||
{ | |||
status |= 0x01; // Encoding unsupported | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
if( ( fragSessionData.FragGroupData.FragNb > FRAG_MAX_NB ) || | |||
( fragSessionData.FragGroupData.FragSize > FRAG_MAX_SIZE ) || | |||
( ( fragSessionData.FragGroupData.FragNb * fragSessionData.FragGroupData.FragSize ) > FragDecoderGetMaxFileSize( ) ) ) | |||
{ | |||
status |= 0x02; // Not enough Memory | |||
} | |||
#else | |||
if( ( fragSessionData.FragGroupData.FragNb > FRAG_MAX_NB ) || | |||
( fragSessionData.FragGroupData.FragSize > FRAG_MAX_SIZE ) || | |||
( ( fragSessionData.FragGroupData.FragNb * fragSessionData.FragGroupData.FragSize ) > LmhpFragmentationParams->BufferSize ) ) | |||
{ | |||
status |= 0x02; // Not enough Memory | |||
} | |||
#endif | |||
status |= ( fragSessionData.FragGroupData.FragSession.Fields.FragIndex << 6 ) & 0xC0; | |||
if( fragSessionData.FragGroupData.FragSession.Fields.FragIndex >= FRAGMENTATION_MAX_SESSIONS ) | |||
{ | |||
status |= 0x04; // FragSession index not supported | |||
} | |||
// Descriptor is not really defined in the specification | |||
// Not clear how to handle this. | |||
// Currently the descriptor is always correct | |||
if( fragSessionData.FragGroupData.Descriptor != 0x01020304 ) | |||
{ | |||
//status |= 0x08; // Wrong Descriptor | |||
} | |||
if( ( status & 0x0F ) == 0 ) | |||
{ | |||
// The FragSessionSetup is accepted | |||
fragSessionData.FragGroupData.IsActive = true; | |||
fragSessionData.FragDecoderPorcessStatus = FRAG_SESSION_ONGOING; | |||
FragSessionData[fragSessionData.FragGroupData.FragSession.Fields.FragIndex] = fragSessionData; | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
FragDecoderInit( fragSessionData.FragGroupData.FragNb, | |||
fragSessionData.FragGroupData.FragSize, | |||
&LmhpFragmentationParams->DecoderCallbacks ); | |||
#else | |||
FragDecoderInit( fragSessionData.FragGroupData.FragNb, | |||
fragSessionData.FragGroupData.FragSize, | |||
LmhpFragmentationParams->Buffer, | |||
LmhpFragmentationParams->BufferSize ); | |||
#endif | |||
} | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_SETUP_ANS; | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status; | |||
isAnswerDelayed = false; | |||
break; | |||
} | |||
case FRAGMENTATION_FRAG_SESSION_DELETE_REQ: | |||
{ | |||
if( mcpsIndication->Multicast == 1 ) | |||
{ | |||
// Multicast channel. Don't process command. | |||
break; | |||
} | |||
uint8_t status = 0x00; | |||
uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03; | |||
status |= id; | |||
if( ( id >= FRAGMENTATION_MAX_SESSIONS ) || ( FragSessionData[id].FragGroupData.IsActive == false ) ) | |||
{ | |||
status |= 0x04; // Session does not exist | |||
} | |||
else | |||
{ | |||
// Delete session | |||
FragSessionData[id].FragGroupData.IsActive = false; | |||
} | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_DELETE_ANS; | |||
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status; | |||
isAnswerDelayed = false; | |||
break; | |||
} | |||
case FRAGMENTATION_DATA_FRAGMENT: | |||
{ | |||
uint8_t fragIndex = 0; | |||
uint16_t fragCounter = 0; | |||
fragCounter = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF; | |||
fragCounter |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00; | |||
fragIndex = ( fragCounter >> 14 ) & 0x03; | |||
fragCounter &= 0x3FFF; | |||
if( mcpsIndication->Multicast == 1 ) | |||
{ | |||
// Message received on a multicast address | |||
// | |||
// TODO: Not working yet | |||
// | |||
// Check McGroupBitMask | |||
//uint8_t groupId = LoRaMacMcChannelGetGroupId( mcpsIndication->DevAddress ); | |||
//if( ( groupId == 0xFF ) || | |||
// ( ( FragSessionData[fragIndex].FragGroupData.FragSession.Fields.McGroupBitMask & ( 1 << groupId ) ) == 0 ) ) | |||
//{ | |||
// // Ignore message | |||
// break; | |||
//} | |||
} | |||
if( FragSessionData[fragIndex].FragDecoderPorcessStatus == FRAG_SESSION_ONGOING ) | |||
{ | |||
FragSessionData[fragIndex].FragDecoderPorcessStatus = FragDecoderProcess( fragCounter, &mcpsIndication->Buffer[cmdIndex] ); | |||
FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( ); | |||
if( LmhpFragmentationParams->OnProgress != NULL ) | |||
{ | |||
LmhpFragmentationParams->OnProgress( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx, | |||
FragSessionData[fragIndex].FragGroupData.FragNb, | |||
FragSessionData[fragIndex].FragGroupData.FragSize, | |||
FragSessionData[fragIndex].FragDecoderStatus.FragNbLost ); | |||
} | |||
} | |||
else | |||
{ | |||
if( FragSessionData[fragIndex].FragDecoderPorcessStatus >= 0 ) | |||
{ | |||
// Fragmentation successfully done | |||
FragSessionData[fragIndex].FragDecoderPorcessStatus = FRAG_SESSION_NOT_STARTED; | |||
if( LmhpFragmentationParams->OnDone != NULL ) | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
LmhpFragmentationParams->OnDone( FragSessionData[fragIndex].FragDecoderPorcessStatus, | |||
( FragSessionData[fragIndex].FragGroupData.FragNb * FragSessionData[fragIndex].FragGroupData.FragSize ) - FragSessionData[fragIndex].FragGroupData.Padding ); | |||
#else | |||
LmhpFragmentationParams->OnDone( FragSessionData[fragIndex].FragDecoderPorcessStatus, | |||
LmhpFragmentationParams->Buffer, | |||
( FragSessionData[fragIndex].FragGroupData.FragNb * FragSessionData[fragIndex].FragGroupData.FragSize ) - FragSessionData[fragIndex].FragGroupData.Padding ); | |||
#endif | |||
} | |||
} | |||
} | |||
cmdIndex += FragSessionData[fragIndex].FragGroupData.FragSize; | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
} | |||
// After processing the commands, if the end-node has to reply back then a flag is checked if the | |||
// reply is to be sent immediately or with a delay. | |||
// In some scenarios it is not desired that multiple end-notes send uplinks at the same time to | |||
// the same server. (Example: Fragment status during a multicast FUOTA) | |||
if( dataBufferIndex != 0 ) | |||
{ | |||
// Prepare Answer that is to be transmitted | |||
cmdReplyAppData.Buffer = LmhpFragmentationState.DataBuffer; | |||
cmdReplyAppData.BufferSize = dataBufferIndex; | |||
cmdReplyAppData.Port = FRAGMENTATION_PORT; | |||
if( isAnswerDelayed == true ) | |||
{ | |||
// Delay value is calculated using BlockAckDelay which is communicated by server during the FragSessionSetupReq | |||
// Pseudo Random Delay = rand(0:1) * 2^(blockAckDelay + 4) Seconds. | |||
// Delay = Pseudo Random Delay * 1000 milli seconds. | |||
// Eg: blockAckDelay = 7 | |||
// Pseudo Random Delay = rand(0:1) * 2^11 | |||
// rand(0:1) seconds = rand(0:1000) milliseconds | |||
// Delay = rand(0:1000) * 2048 => 2048000ms = 34 minutes | |||
TxDelayTime = randr( 0, 1000 ) * ( 1 << ( blockAckDelay + 4 ) ); | |||
DelayedReplyAppData = cmdReplyAppData; | |||
LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_START; | |||
} | |||
else | |||
{ | |||
// Send the prepared answer | |||
LmHandlerSend( &cmdReplyAppData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
} | |||
} |
@@ -0,0 +1,92 @@ | |||
/*! | |||
* \file LmhpFragmentation.h | |||
* | |||
* \brief Implements the LoRa-Alliance fragmented data block transport package | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LMHP_FRAGMENTATION_H__ | |||
#define __LMHP_FRAGMENTATION_H__ | |||
#include "LoRaMac.h" | |||
#include "LmHandlerTypes.h" | |||
#include "LmhPackage.h" | |||
#include "FragDecoder.h" | |||
/*! | |||
* Fragmentation data block transport package identifier. | |||
* | |||
* \remark This value must be unique amongst the packages | |||
*/ | |||
#define PACKAGE_ID_FRAGMENTATION 3 | |||
/*! | |||
* Fragmentation package parameters | |||
*/ | |||
typedef struct LmhpFragmentationParams_s | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
/*! | |||
* FragDecoder Write/Read function callbacks | |||
*/ | |||
FragDecoderCallbacks_t DecoderCallbacks; | |||
#else | |||
/*! | |||
* Pointer to the un-fragmented received buffer. | |||
*/ | |||
uint8_t *Buffer; | |||
/*! | |||
* Size of the un-fragmented received buffer. | |||
*/ | |||
uint32_t BufferSize; | |||
#endif | |||
/*! | |||
* Notifies the progress of the current fragmentation session | |||
* | |||
* \param [IN] fragCounter Fragment counter | |||
* \param [IN] fragNb Number of fragments | |||
* \param [IN] fragSize Size of fragments | |||
* \param [IN] fragNbLost Number of lost fragments | |||
*/ | |||
void ( *OnProgress )( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
/*! | |||
* Notifies that the fragmentation session is finished | |||
* | |||
* \param [IN] status Fragmentation session status [FRAG_SESSION_ONGOING, | |||
* FRAG_SESSION_FINISHED or | |||
* FragDecoder.Status.FragNbLost] | |||
* \param [IN] size Received file size | |||
*/ | |||
void ( *OnDone )( int32_t status, uint32_t size ); | |||
#else | |||
/*! | |||
* Notifies that the fragmentation session is finished | |||
* | |||
* \param [IN] status Fragmentation session status [FRAG_SESSION_ONGOING, | |||
* FRAG_SESSION_FINISHED or | |||
* FragDecoder.Status.FragNbLost] | |||
* \param [IN] file Pointer to the reception file buffer | |||
* \param [IN] size Received file size | |||
*/ | |||
void ( *OnDone )( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
}LmhpFragmentationParams_t; | |||
LmhPackage_t *LmhpFragmentationPackageFactory( void ); | |||
#endif // __LMHP_FRAGMENTATION_H__ |
@@ -0,0 +1,457 @@ | |||
/*! | |||
* \file LmhpRemoteMcastSetup.c | |||
* | |||
* \brief Implements the LoRa-Alliance remote multicast setup package | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/remote_multicast_setup_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#include "LmHandler.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#define DBG_TRACE 1 | |||
#if DBG_TRACE == 1 | |||
#include <stdio.h> | |||
/*! | |||
* Works in the same way as the printf function does. | |||
*/ | |||
#define DBG( ... ) \ | |||
do \ | |||
{ \ | |||
printf( __VA_ARGS__ ); \ | |||
}while( 0 ) | |||
#else | |||
#define DBG( ... ) | |||
#endif | |||
/*! | |||
* LoRaWAN Application Layer Remote multicast setup Specification | |||
*/ | |||
#define REMOTE_MCAST_SETUP_PORT 200 | |||
#define REMOTE_MCAST_SETUP_ID 2 | |||
#define REMOTE_MCAST_SETUP_VERSION 1 | |||
typedef enum LmhpRemoteMcastSetupSessionStates_e | |||
{ | |||
REMOTE_MCAST_SETUP_SESSION_STATE_IDLE, | |||
REMOTE_MCAST_SETUP_SESSION_STATE_START, | |||
REMOTE_MCAST_SETUP_SESSION_STATE_STOP, | |||
}LmhpRemoteMcastSetupSessionStates_t; | |||
/*! | |||
* Package current context | |||
*/ | |||
typedef struct LmhpRemoteMcastSetupState_s | |||
{ | |||
bool Initialized; | |||
bool IsTxPending; | |||
LmhpRemoteMcastSetupSessionStates_t SessionState; | |||
uint8_t DataBufferMaxSize; | |||
uint8_t *DataBuffer; | |||
}LmhpRemoteMcastSetupState_t; | |||
typedef enum LmhpRemoteMcastSetupMoteCmd_e | |||
{ | |||
REMOTE_MCAST_SETUP_PKG_VERSION_ANS = 0x00, | |||
REMOTE_MCAST_SETUP_MC_GROUP_STATUS_ANS = 0x01, | |||
REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS = 0x02, | |||
REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS = 0x03, | |||
REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS = 0x04, | |||
REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_ANS = 0x05, | |||
}LmhpRemoteMcastSetupMoteCmd_t; | |||
typedef enum LmhpRemoteMcastSetupSrvCmd_e | |||
{ | |||
REMOTE_MCAST_SETUP_PKG_VERSION_REQ = 0x00, | |||
REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ = 0x01, | |||
REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ = 0x02, | |||
REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ = 0x03, | |||
REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ = 0x04, | |||
REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ = 0x05, | |||
}LmhpRemoteMcastSetupSrvCmd_t; | |||
/*! | |||
* Initializes the package with provided parameters | |||
* | |||
* \param [IN] params Pointer to the package parameters | |||
* \param [IN] dataBuffer Pointer to main application buffer | |||
* \param [IN] dataBufferMaxSize Main application buffer maximum size | |||
*/ | |||
static void LmhpRemoteMcastSetupInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); | |||
/*! | |||
* Returns the current package initialization status. | |||
* | |||
* \retval status Package initialization status | |||
* [true: Initialized, false: Not initialized] | |||
*/ | |||
static bool LmhpRemoteMcastSetupIsInitialized( void ); | |||
/*! | |||
* Returns if a package transmission is pending or not. | |||
* | |||
* \retval status Package transmission status | |||
* [true: pending, false: Not pending] | |||
*/ | |||
static bool LmhpRemoteMcastSetupIsTxPending( void ); | |||
/*! | |||
* Processes the internal package events. | |||
*/ | |||
static void LmhpRemoteMcastSetupProcess( void ); | |||
/*! | |||
* Processes the MCPS Indication | |||
* | |||
* \param [IN] mcpsIndication MCPS indication primitive data | |||
*/ | |||
static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication ); | |||
static void OnSessionStartTimer( void *context ); | |||
static void OnSessionStopTimer( void *context ); | |||
static LmhpRemoteMcastSetupState_t LmhpRemoteMcastSetupState = | |||
{ | |||
.Initialized = false, | |||
.IsTxPending = false, | |||
.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE, | |||
}; | |||
typedef struct McGroupData_s | |||
{ | |||
union | |||
{ | |||
uint8_t Value; | |||
struct | |||
{ | |||
uint8_t McGroupId: 2; | |||
uint8_t RFU: 6; | |||
}Fields; | |||
}IdHeader; | |||
uint32_t McAddr; | |||
uint8_t McKeyEncrypted[16]; | |||
uint32_t McFCountMin; | |||
uint32_t McFCountMax; | |||
}McGroupData_t; | |||
typedef enum eSessionState | |||
{ | |||
SESSION_STOPED, | |||
SESSION_STARTED | |||
}SessionState_t; | |||
typedef struct McSessionData_s | |||
{ | |||
McGroupData_t McGroupData; | |||
SessionState_t SessionState; | |||
uint32_t SessionTime; | |||
uint8_t SessionTimeout; | |||
McRxParams_t RxParams; | |||
}McSessionData_t; | |||
McSessionData_t McSessionData[LORAMAC_MAX_MC_CTX]; | |||
/*! | |||
* Session start timer | |||
*/ | |||
static TimerEvent_t SessionStartTimer; | |||
/*! | |||
* Session start timer | |||
*/ | |||
static TimerEvent_t SessionStopTimer; | |||
static LmhPackage_t LmhpRemoteMcastSetupPackage = | |||
{ | |||
.Port = REMOTE_MCAST_SETUP_PORT, | |||
.Init = LmhpRemoteMcastSetupInit, | |||
.IsInitialized = LmhpRemoteMcastSetupIsInitialized, | |||
.IsTxPending = LmhpRemoteMcastSetupIsTxPending, | |||
.Process = LmhpRemoteMcastSetupProcess, | |||
.OnMcpsConfirmProcess = NULL, // Not used in this package | |||
.OnMcpsIndicationProcess = LmhpRemoteMcastSetupOnMcpsIndication, | |||
.OnMlmeConfirmProcess = NULL, // Not used in this package | |||
.OnMlmeIndicationProcess = NULL, // Not used in this package | |||
.OnMacMcpsRequest = NULL, // To be initialized by LmHandler | |||
.OnMacMlmeRequest = NULL, // To be initialized by LmHandler | |||
.OnJoinRequest = NULL, // To be initialized by LmHandler | |||
.OnDeviceTimeRequest = NULL, // To be initialized by LmHandler | |||
.OnSysTimeUpdate = NULL, // To be initialized by LmHandler | |||
}; | |||
LmhPackage_t *LmhpRemoteMcastSetupPackageFactory( void ) | |||
{ | |||
return &LmhpRemoteMcastSetupPackage; | |||
} | |||
static void LmhpRemoteMcastSetupInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ) | |||
{ | |||
if( dataBuffer != NULL ) | |||
{ | |||
LmhpRemoteMcastSetupState.DataBuffer = dataBuffer; | |||
LmhpRemoteMcastSetupState.DataBufferMaxSize = dataBufferMaxSize; | |||
LmhpRemoteMcastSetupState.Initialized = true; | |||
TimerInit( &SessionStartTimer, OnSessionStartTimer ); | |||
TimerInit( &SessionStopTimer, OnSessionStopTimer ); | |||
} | |||
else | |||
{ | |||
LmhpRemoteMcastSetupState.Initialized = false; | |||
} | |||
LmhpRemoteMcastSetupState.IsTxPending = false; | |||
} | |||
static bool LmhpRemoteMcastSetupIsInitialized( void ) | |||
{ | |||
return LmhpRemoteMcastSetupState.Initialized; | |||
} | |||
static bool LmhpRemoteMcastSetupIsTxPending( void ) | |||
{ | |||
return LmhpRemoteMcastSetupState.IsTxPending; | |||
} | |||
static void LmhpRemoteMcastSetupProcess( void ) | |||
{ | |||
LmhpRemoteMcastSetupSessionStates_t state; | |||
CRITICAL_SECTION_BEGIN( ); | |||
state = LmhpRemoteMcastSetupState.SessionState; | |||
LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE; | |||
CRITICAL_SECTION_END( ); | |||
switch( state ) | |||
{ | |||
case REMOTE_MCAST_SETUP_SESSION_STATE_START: | |||
// Switch to Class C | |||
LmHandlerRequestClass( CLASS_C ); | |||
TimerSetValue( &SessionStopTimer, ( 1 << McSessionData[0].SessionTimeout ) * 1000 ); | |||
TimerStart( &SessionStopTimer ); | |||
break; | |||
case REMOTE_MCAST_SETUP_SESSION_STATE_STOP: | |||
// Switch back to Class A | |||
LmHandlerRequestClass( CLASS_A ); | |||
break; | |||
case REMOTE_MCAST_SETUP_SESSION_STATE_IDLE: | |||
// Intentional fall through | |||
default: | |||
// Nothing to do. | |||
break; | |||
} | |||
} | |||
static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication ) | |||
{ | |||
uint8_t cmdIndex = 0; | |||
uint8_t dataBufferIndex = 0; | |||
if( mcpsIndication->Port != REMOTE_MCAST_SETUP_PORT ) | |||
{ | |||
return; | |||
} | |||
while( cmdIndex < mcpsIndication->BufferSize ) | |||
{ | |||
switch( mcpsIndication->Buffer[cmdIndex++] ) | |||
{ | |||
case REMOTE_MCAST_SETUP_PKG_VERSION_REQ: | |||
{ | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_PKG_VERSION_ANS; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_ID; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_VERSION; | |||
break; | |||
} | |||
case REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ: | |||
{ | |||
// TODO implement command prosessing and handling | |||
break; | |||
} | |||
case REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ: | |||
{ | |||
uint8_t id = mcpsIndication->Buffer[cmdIndex++]; | |||
McSessionData[id].McGroupData.IdHeader.Value = id; | |||
McSessionData[id].McGroupData.McAddr = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; | |||
McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; | |||
McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; | |||
McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; | |||
for( int8_t i = 0; i < 16; i++ ) | |||
{ | |||
McSessionData[id].McGroupData.McKeyEncrypted[i] = mcpsIndication->Buffer[cmdIndex++]; | |||
} | |||
McSessionData[id].McGroupData.McFCountMin = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; | |||
McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; | |||
McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; | |||
McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; | |||
McSessionData[id].McGroupData.McFCountMax = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; | |||
McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; | |||
McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; | |||
McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; | |||
McChannelParams_t channel = | |||
{ | |||
.IsRemotelySetup = true, | |||
.Class = CLASS_C, // Field not used for multicast channel setup. Must be initialized to something | |||
.IsEnabled = true, | |||
.GroupID = ( AddressIdentifier_t )McSessionData[id].McGroupData.IdHeader.Fields.McGroupId, | |||
.Address = McSessionData[id].McGroupData.McAddr, | |||
.McKeys.McKeyE = McSessionData[id].McGroupData.McKeyEncrypted, | |||
.FCountMin = McSessionData[id].McGroupData.McFCountMin, | |||
.FCountMax = McSessionData[id].McGroupData.McFCountMax, | |||
.RxParams.ClassC = // Field not used for multicast channel setup. Must be initialized to something | |||
{ | |||
.Frequency = 0, | |||
.Datarate = 0 | |||
} | |||
}; | |||
uint8_t idError = 0x01; // One bit value | |||
if( LoRaMacMcChannelSetup( &channel ) == LORAMAC_STATUS_OK ) | |||
{ | |||
idError = 0x00; | |||
} | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( idError << 2 ) | McSessionData[id].McGroupData.IdHeader.Fields.McGroupId; | |||
break; | |||
} | |||
case REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ: | |||
{ | |||
uint8_t status = 0x00; | |||
uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03; | |||
status = id; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS; | |||
if( LoRaMacMcChannelDelete( ( AddressIdentifier_t )id ) != LORAMAC_STATUS_OK ) | |||
{ | |||
status |= 0x04; // McGroupUndefined bit set | |||
} | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; | |||
break; | |||
} | |||
case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ: | |||
{ | |||
uint8_t status = 0x00; | |||
uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03; | |||
McSessionData[id].SessionTime = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; | |||
McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; | |||
McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; | |||
McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; | |||
// Add Unix to Gps epcoh offset. The system time is based on Unix time. | |||
McSessionData[id].SessionTime += UNIX_GPS_EPOCH_OFFSET; | |||
McSessionData[id].SessionTimeout = mcpsIndication->Buffer[cmdIndex++] & 0x0F; | |||
McSessionData[id].RxParams.ClassC.Frequency = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; | |||
McSessionData[id].RxParams.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; | |||
McSessionData[id].RxParams.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; | |||
McSessionData[id].RxParams.ClassC.Frequency *= 100; | |||
McSessionData[id].RxParams.ClassC.Datarate = mcpsIndication->Buffer[cmdIndex++]; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS; | |||
if( LoRaMacMcChannelSetupRxParams( ( AddressIdentifier_t )id, &McSessionData[id].RxParams, &status ) == LORAMAC_STATUS_OK ) | |||
{ | |||
SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 }; | |||
curTime = SysTimeGet( ); | |||
int32_t timeToSessionStart = McSessionData[id].SessionTime - curTime.Seconds; | |||
if( timeToSessionStart > 0 ) | |||
{ | |||
// Start session start timer | |||
TimerSetValue( &SessionStartTimer, timeToSessionStart * 1000 ); | |||
TimerStart( &SessionStartTimer ); | |||
DBG( "Time2SessionStart: %ld ms\n", timeToSessionStart * 1000 ); | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 0 ) & 0xFF; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 8 ) & 0xFF; | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 16 ) & 0xFF; | |||
break; | |||
} | |||
else | |||
{ | |||
// Session start time before current device time | |||
status |= 0x10; | |||
} | |||
} | |||
LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; | |||
break; | |||
} | |||
case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ: | |||
{ | |||
// TODO implement command prosessing and handling | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
} | |||
if( dataBufferIndex != 0 ) | |||
{ | |||
// Answer commands | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = LmhpRemoteMcastSetupState.DataBuffer, | |||
.BufferSize = dataBufferIndex, | |||
.Port = REMOTE_MCAST_SETUP_PORT | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
DBG( "ID : %d\n", McSessionData[0].McGroupData.IdHeader.Fields.McGroupId ); | |||
DBG( "McAddr : %08lX\n", McSessionData[0].McGroupData.McAddr ); | |||
DBG( "McKey : %02X", McSessionData[0].McGroupData.McKeyEncrypted[0] ); | |||
for( int i = 1; i < 16; i++ ) | |||
{ | |||
DBG( "-%02X", McSessionData[0].McGroupData.McKeyEncrypted[i] ); | |||
} | |||
DBG( "\n" ); | |||
DBG( "McFCountMin : %lu\n", McSessionData[0].McGroupData.McFCountMin ); | |||
DBG( "McFCountMax : %lu\n", McSessionData[0].McGroupData.McFCountMax ); | |||
DBG( "SessionTime : %lu\n", McSessionData[0].SessionTime ); | |||
DBG( "SessionTimeT: %d\n", McSessionData[0].SessionTimeout ); | |||
DBG( "Rx Freq : %lu\n", McSessionData[0].RxParams.ClassC.Frequency ); | |||
DBG( "Rx DR : DR_%d\n", McSessionData[0].RxParams.ClassC.Datarate ); | |||
} | |||
} | |||
static void OnSessionStartTimer( void *context ) | |||
{ | |||
TimerStop( &SessionStartTimer ); | |||
LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_START; | |||
} | |||
static void OnSessionStopTimer( void *context ) | |||
{ | |||
TimerStop( &SessionStopTimer ); | |||
LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_STOP; | |||
} |
@@ -0,0 +1,47 @@ | |||
/*! | |||
* \file LmhpRemoteMcastSetup.h | |||
* | |||
* \brief Implements the LoRa-Alliance remote multicast setup package | |||
* Specification: https://lora-alliance.org/sites/default/files/2018-09/remote_multicast_setup_v1.0.0.pdf | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LMHP_REMOTE_MCAST_SETUP_H__ | |||
#define __LMHP_REMOTE_MCAST_SETUP_H__ | |||
#include "LoRaMac.h" | |||
#include "LmHandlerTypes.h" | |||
#include "LmhPackage.h" | |||
/*! | |||
* Remote multicast setup package identifier. | |||
* | |||
* \remark This value must be unique amongst the packages | |||
*/ | |||
#define PACKAGE_ID_REMOTE_MCAST_SETUP 2 | |||
/*! | |||
* Remote multicast setup package parameters | |||
* | |||
* This package doesn't require parameters | |||
*/ | |||
//typedef struct LmhpRemoteMcastSetupParams_s | |||
//{ | |||
//}LmhpRemoteMcastSetupParams_t; | |||
LmhPackage_t *LmhpRemoteMcastSetupPackageFactory( void ); | |||
#endif // __LMHP_REMOTE_MCAST_SETUP_H__ |
@@ -0,0 +1,428 @@ | |||
/*! | |||
* \file LmHandlerMsgDisplay.h | |||
* | |||
* \brief Common set of functions to display default messages from | |||
* LoRaMacHandler. | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2019 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#include <stdlib.h> | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <stdio.h> | |||
#include "utilities.h" | |||
#include "timer.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
/*! | |||
* MAC status strings | |||
*/ | |||
const char* MacStatusStrings[] = | |||
{ | |||
"OK", // LORAMAC_STATUS_OK | |||
"Busy", // LORAMAC_STATUS_BUSY | |||
"Service unknown", // LORAMAC_STATUS_SERVICE_UNKNOWN | |||
"Parameter invalid", // LORAMAC_STATUS_PARAMETER_INVALID | |||
"Frequency invalid", // LORAMAC_STATUS_FREQUENCY_INVALID | |||
"Datarate invalid", // LORAMAC_STATUS_DATARATE_INVALID | |||
"Frequency or datarate invalid", // LORAMAC_STATUS_FREQ_AND_DR_INVALID | |||
"No network joined", // LORAMAC_STATUS_NO_NETWORK_JOINED | |||
"Length error", // LORAMAC_STATUS_LENGTH_ERROR | |||
"Region not supported", // LORAMAC_STATUS_REGION_NOT_SUPPORTED | |||
"Skipped APP data", // LORAMAC_STATUS_SKIPPED_APP_DATA | |||
"Duty-cycle restricted", // LORAMAC_STATUS_DUTYCYCLE_RESTRICTED | |||
"No channel found", // LORAMAC_STATUS_NO_CHANNEL_FOUND | |||
"No free channel found", // LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND | |||
"Busy beacon reserved time", // LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME | |||
"Busy ping-slot window time", // LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME | |||
"Busy uplink collision", // LORAMAC_STATUS_BUSY_UPLINK_COLLISION | |||
"Crypto error", // LORAMAC_STATUS_CRYPTO_ERROR | |||
"FCnt handler error", // LORAMAC_STATUS_FCNT_HANDLER_ERROR | |||
"MAC command error", // LORAMAC_STATUS_MAC_COMMAD_ERROR | |||
"ClassB error", // LORAMAC_STATUS_CLASS_B_ERROR | |||
"Confirm queue error", // LORAMAC_STATUS_CONFIRM_QUEUE_ERROR | |||
"Multicast group undefined", // LORAMAC_STATUS_MC_GROUP_UNDEFINED | |||
"Unknown error", // LORAMAC_STATUS_ERROR | |||
}; | |||
/*! | |||
* MAC event info status strings. | |||
*/ | |||
const char* EventInfoStatusStrings[] = | |||
{ | |||
"OK", // LORAMAC_EVENT_INFO_STATUS_OK | |||
"Error", // LORAMAC_EVENT_INFO_STATUS_ERROR | |||
"Tx timeout", // LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT | |||
"Rx 1 timeout", // LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT | |||
"Rx 2 timeout", // LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT | |||
"Rx1 error", // LORAMAC_EVENT_INFO_STATUS_RX1_ERROR | |||
"Rx2 error", // LORAMAC_EVENT_INFO_STATUS_RX2_ERROR | |||
"Join failed", // LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL | |||
"Downlink repeated", // LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED | |||
"Tx DR payload size error", // LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR | |||
"Address fail", // LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL | |||
"MIC fail", // LORAMAC_EVENT_INFO_STATUS_MIC_FAIL | |||
"Multicast fail", // LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL | |||
"Beacon locked", // LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED | |||
"Beacon lost", // LORAMAC_EVENT_INFO_STATUS_BEACON_LOST | |||
"Beacon not found" // LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND | |||
}; | |||
/*! | |||
* Prints the provided buffer in HEX | |||
* | |||
* \param buffer Buffer to be printed | |||
* \param size Buffer size to be printed | |||
*/ | |||
void PrintHexBuffer( uint8_t *buffer, uint8_t size ) | |||
{ | |||
uint8_t newline = 0; | |||
for( uint8_t i = 0; i < size; i++ ) | |||
{ | |||
if( newline != 0 ) | |||
{ | |||
printf( "\n" ); | |||
newline = 0; | |||
} | |||
printf( "%02X ", buffer[i] ); | |||
if( ( ( i + 1 ) % 16 ) == 0 ) | |||
{ | |||
newline = 1; | |||
} | |||
} | |||
printf( "\n" ); | |||
} | |||
void DisplayNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
if( state == LORAMAC_HANDLER_NVM_STORE ) | |||
{ | |||
printf( "\n###### ============ CTXS STORED ============ ######\n" ); | |||
} | |||
else | |||
{ | |||
printf( "\n###### =========== CTXS RESTORED =========== ######\n" ); | |||
} | |||
printf( "Size : %i\n\n", size ); | |||
} | |||
void DisplayNetworkParametersUpdate( CommissioningParams_t *commissioningParams ) | |||
{ | |||
printf( "DevEui : %02X", commissioningParams->DevEui[0] ); | |||
for( int i = 1; i < 8; i++ ) | |||
{ | |||
printf( "-%02X", commissioningParams->DevEui[i] ); | |||
} | |||
printf( "\n" ); | |||
printf( "JoinEui : %02X", commissioningParams->JoinEui[0] ); | |||
for( int i = 1; i < 8; i++ ) | |||
{ | |||
printf( "-%02X", commissioningParams->JoinEui[i] ); | |||
} | |||
printf( "\n" ); | |||
printf( "Pin : %02X", commissioningParams->SePin[0] ); | |||
for( int i = 1; i < 4; i++ ) | |||
{ | |||
printf( "-%02X", commissioningParams->SePin[i] ); | |||
} | |||
printf( "\n\n" ); | |||
} | |||
void DisplayMacMcpsRequestUpdate( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
switch( mcpsReq->Type ) | |||
{ | |||
case MCPS_CONFIRMED: | |||
{ | |||
printf( "\n###### =========== MCPS-Request ============ ######\n" ); | |||
printf( "###### MCPS_CONFIRMED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
case MCPS_UNCONFIRMED: | |||
{ | |||
printf( "\n###### =========== MCPS-Request ============ ######\n" ); | |||
printf( "###### MCPS_UNCONFIRMED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
case MCPS_PROPRIETARY: | |||
{ | |||
printf( "\n###### =========== MCPS-Request ============ ######\n" ); | |||
printf( "###### MCPS_PROPRIETARY ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
default: | |||
{ | |||
printf( "\n###### =========== MCPS-Request ============ ######\n" ); | |||
printf( "###### MCPS_ERROR ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
} | |||
printf( "STATUS : %s\n", MacStatusStrings[status] ); | |||
if( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) | |||
{ | |||
printf( "Next Tx in : %lu [ms]\n", nextTxIn ); | |||
} | |||
} | |||
void DisplayMacMlmeRequestUpdate( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
switch( mlmeReq->Type ) | |||
{ | |||
case MLME_JOIN: | |||
{ | |||
printf( "\n###### =========== MLME-Request ============ ######\n" ); | |||
printf( "###### MLME_JOIN ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
case MLME_LINK_CHECK: | |||
{ | |||
printf( "\n###### =========== MLME-Request ============ ######\n" ); | |||
printf( "###### MLME_LINK_CHECK ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
case MLME_DEVICE_TIME: | |||
{ | |||
printf( "\n###### =========== MLME-Request ============ ######\n" ); | |||
printf( "###### MLME_DEVICE_TIME ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
case MLME_TXCW: | |||
{ | |||
printf( "\n###### =========== MLME-Request ============ ######\n" ); | |||
printf( "###### MLME_TXCW ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
default: | |||
{ | |||
printf( "\n###### =========== MLME-Request ============ ######\n" ); | |||
printf( "###### MLME_UNKNOWN ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
break; | |||
} | |||
} | |||
printf( "STATUS : %s\n", MacStatusStrings[status] ); | |||
if( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) | |||
{ | |||
printf( "Next Tx in : %lu [ms]\n", nextTxIn ); | |||
} | |||
} | |||
void DisplayJoinRequestUpdate( LmHandlerJoinParams_t *params ) | |||
{ | |||
if( params->CommissioningParams->IsOtaaActivation == true ) | |||
{ | |||
if( params->Status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "###### =========== JOINED ============ ######\n" ); | |||
printf( "\nOTAA\n\n" ); | |||
printf( "DevAddr : %08lX\n", params->CommissioningParams->DevAddr ); | |||
printf( "\n\n" ); | |||
printf( "DATA RATE : DR_%d\n\n", params->Datarate ); | |||
} | |||
} | |||
#if ( OVER_THE_AIR_ACTIVATION == 0 ) | |||
else | |||
{ | |||
printf( "###### =========== JOINED ============ ######\n" ); | |||
printf( "\nABP\n\n" ); | |||
printf( "DevAddr : %08lX\n", params->CommissioningParams->DevAddr ); | |||
printf( "\n\n" ); | |||
} | |||
#endif | |||
} | |||
void DisplayTxUpdate( LmHandlerTxParams_t *params ) | |||
{ | |||
MibRequestConfirm_t mibGet; | |||
if( params->IsMcpsConfirm == 0 ) | |||
{ | |||
printf( "\n###### =========== MLME-Confirm ============ ######\n" ); | |||
printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); | |||
return; | |||
} | |||
printf( "\n###### =========== MCPS-Confirm ============ ######\n" ); | |||
printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); | |||
printf( "\n###### ===== UPLINK FRAME %8lu ===== ######\n", params->UplinkCounter ); | |||
printf( "\n" ); | |||
printf( "CLASS : %c\n", "ABC"[LmHandlerGetCurrentClass( )] ); | |||
printf( "\n" ); | |||
printf( "TX PORT : %d\n", params->AppData.Port ); | |||
if( params->AppData.BufferSize != 0 ) | |||
{ | |||
printf( "TX DATA : " ); | |||
if( params->MsgType == LORAMAC_HANDLER_CONFIRMED_MSG ) | |||
{ | |||
printf( "CONFIRMED - %s\n", ( params->AckReceived != 0 ) ? "ACK" : "NACK" ); | |||
} | |||
else | |||
{ | |||
printf( "UNCONFIRMED\n" ); | |||
} | |||
PrintHexBuffer( params->AppData.Buffer, params->AppData.BufferSize ); | |||
} | |||
printf( "\n" ); | |||
printf( "DATA RATE : DR_%d\n", params->Datarate ); | |||
mibGet.Type = MIB_CHANNELS; | |||
if( LoRaMacMibGetRequestConfirm( &mibGet ) == LORAMAC_STATUS_OK ) | |||
{ | |||
printf( "U/L FREQ : %lu\n", mibGet.Param.ChannelList[params->Channel].Frequency ); | |||
} | |||
printf( "TX POWER : %d\n", params->TxPower ); | |||
mibGet.Type = MIB_CHANNELS_MASK; | |||
if( LoRaMacMibGetRequestConfirm( &mibGet ) == LORAMAC_STATUS_OK ) | |||
{ | |||
printf("CHANNEL MASK: "); | |||
switch( LmHandlerGetActiveRegion( ) ) | |||
{ | |||
case LORAMAC_REGION_AS923: | |||
case LORAMAC_REGION_CN779: | |||
case LORAMAC_REGION_EU868: | |||
case LORAMAC_REGION_IN865: | |||
case LORAMAC_REGION_KR920: | |||
case LORAMAC_REGION_EU433: | |||
case LORAMAC_REGION_RU864: | |||
{ | |||
printf( "%04X ", mibGet.Param.ChannelsMask[0] ); | |||
break; | |||
} | |||
case LORAMAC_REGION_AU915: | |||
case LORAMAC_REGION_CN470: | |||
case LORAMAC_REGION_US915: | |||
{ | |||
for( uint8_t i = 0; i < 5; i++) | |||
{ | |||
printf( "%04X ", mibGet.Param.ChannelsMask[i] ); | |||
} | |||
break; | |||
} | |||
default: | |||
{ | |||
printf( "\n###### ========= Unknown Region ============ ######" ); | |||
break; | |||
} | |||
} | |||
printf("\n"); | |||
} | |||
printf( "\n" ); | |||
} | |||
void DisplayRxUpdate( LmHandlerAppData_t *appData, LmHandlerRxParams_t *params ) | |||
{ | |||
const char *slotStrings[] = { "1", "2", "C", "C Multicast", "B Ping-Slot", "B Multicast Ping-Slot" }; | |||
if( params->IsMcpsIndication == 0 ) | |||
{ | |||
printf( "\n###### ========== MLME-Indication ========== ######\n" ); | |||
printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); | |||
return; | |||
} | |||
printf( "\n###### ========== MCPS-Indication ========== ######\n" ); | |||
printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); | |||
printf( "\n###### ===== DOWNLINK FRAME %8lu ===== ######\n", params->DownlinkCounter ); | |||
printf( "RX WINDOW : %s\n", slotStrings[params->RxSlot] ); | |||
printf( "RX PORT : %d\n", appData->Port ); | |||
if( appData->BufferSize != 0 ) | |||
{ | |||
printf( "RX DATA : \n" ); | |||
PrintHexBuffer( appData->Buffer, appData->BufferSize ); | |||
} | |||
printf( "\n" ); | |||
printf( "DATA RATE : DR_%d\n", params->Datarate ); | |||
printf( "RX RSSI : %d\n", params->Rssi ); | |||
printf( "RX SNR : %d\n", params->Snr ); | |||
printf( "\n" ); | |||
} | |||
void DisplayBeaconUpdate( LoRaMacHandlerBeaconParams_t *params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
default: | |||
case LORAMAC_HANDLER_BEACON_ACQUIRING: | |||
{ | |||
printf( "\n###### ========= BEACON ACQUIRING ========== ######\n" ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
{ | |||
printf( "\n###### ============ BEACON LOST ============ ######\n" ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
printf( "\n###### ===== BEACON %8lu ==== ######\n", params->Info.Time.Seconds ); | |||
printf( "GW DESC : %d\n", params->Info.GwSpecific.InfoDesc ); | |||
printf( "GW INFO : " ); | |||
PrintHexBuffer( params->Info.GwSpecific.Info, 6 ); | |||
printf( "\n" ); | |||
printf( "FREQ : %lu\n", params->Info.Frequency ); | |||
printf( "DATA RATE : DR_%d\n", params->Info.Datarate ); | |||
printf( "RX RSSI : %d\n", params->Info.Rssi ); | |||
printf( "RX SNR : %d\n", params->Info.Snr ); | |||
printf( "\n" ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
printf( "\n###### ======== BEACON NOT RECEIVED ======== ######\n" ); | |||
break; | |||
} | |||
} | |||
} | |||
void DisplayClassUpdate( DeviceClass_t deviceClass ) | |||
{ | |||
printf( "\n\n###### ===== Switch to Class %c done. ===== ######\n\n", "ABC"[deviceClass] ); | |||
} | |||
void DisplayAppInfo( const char* appName, const Version_t* appVersion, const Version_t* gitHubVersion ) | |||
{ | |||
printf( "\n###### ===================================== ######\n\n" ); | |||
printf( "Application name : %s\n", appName ); | |||
printf( "Application version: %d.%d.%d\n", appVersion->Fields.Major, appVersion->Fields.Minor, appVersion->Fields.Patch ); | |||
printf( "GitHub base version: %d.%d.%d\n", gitHubVersion->Fields.Major, gitHubVersion->Fields.Minor, gitHubVersion->Fields.Patch ); | |||
printf( "\n###### ===================================== ######\n\n" ); | |||
} |
@@ -0,0 +1,104 @@ | |||
/*! | |||
* \file LmHandlerMsgDisplay.h | |||
* | |||
* \brief Common set of functions to display default messages from | |||
* LoRaMacHandler. | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2019 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
#ifndef __LMHANDLER_MSG_DISPLAY_H__ | |||
#define __LMHANDLER_MSG_DISPLAY_H__ | |||
#include "utilities.h" | |||
#include "LmHandler.h" | |||
/*! | |||
* \brief Displays NVM context operation state | |||
* | |||
* \param [IN] state Indicates if we are storing (true) or | |||
* restoring (false) the NVM context | |||
* | |||
* \param [IN] size Number of data bytes which were stored or restored. | |||
*/ | |||
void DisplayNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
/*! | |||
* \brief Displays updated network parameters | |||
* | |||
* \param [IN] commissioningParams Commissioning provided parameters | |||
*/ | |||
void DisplayNetworkParametersUpdate( CommissioningParams_t* commissioningParams ); | |||
/*! | |||
* \brief Displays updated McpsRequest | |||
* | |||
* \param [IN] status McpsRequest execution status | |||
* \param [IN] mcpsReq McpsRequest command executed | |||
* \param [IN] nextTxIn Time to wait for the next uplink transmission | |||
*/ | |||
void DisplayMacMcpsRequestUpdate( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
/*! | |||
* \brief Displays updated MlmeRequest | |||
* | |||
* \param [IN] status MlmeRequest execution status | |||
* \param [IN] mlmeReq MlmeRequest command executed | |||
* \param [IN] nextTxIn Time to wait for the next uplink transmission | |||
*/ | |||
void DisplayMacMlmeRequestUpdate( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
/*! | |||
* \brief Displays updated JoinRequest | |||
* | |||
* \param [IN] params Executed JoinRequest parameters | |||
*/ | |||
void DisplayJoinRequestUpdate( LmHandlerJoinParams_t* params ); | |||
/*! | |||
* \brief Displays Tx params | |||
* | |||
* \param [IN] params Tx parameters | |||
*/ | |||
void DisplayTxUpdate( LmHandlerTxParams_t* params ); | |||
/*! | |||
* \brief Displays Rx params | |||
* | |||
* \param [IN] appData Receive data payload and port number | |||
* \param [IN] params Rx parameters | |||
*/ | |||
void DisplayRxUpdate( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
/*! | |||
* \brief Displays beacon status update | |||
* | |||
* \param [IN] params Beacon parameters | |||
*/ | |||
void DisplayBeaconUpdate( LoRaMacHandlerBeaconParams_t* params ); | |||
/*! | |||
* \brief Displays end-device class update | |||
* | |||
* \param [IN] deviceClass Current end-device class | |||
*/ | |||
void DisplayClassUpdate( DeviceClass_t deviceClass ); | |||
/*! | |||
* \brief Displays application information | |||
*/ | |||
void DisplayAppInfo( const char* appName, const Version_t* appVersion, const Version_t* gitHubVersion ); | |||
#endif // __LMHANDLER_MSG_DISPLAY_H__ |
@@ -0,0 +1,272 @@ | |||
/*! | |||
* \file NvmDataMgmt.c | |||
* | |||
* \brief NVM context management implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
* / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
* \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
* |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
* embedded.connectivity.solutions=============== | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
* | |||
* \author Daniel Jaeckle ( STACKFORCE ) | |||
* | |||
* \author Johannes Bruder ( STACKFORCE ) | |||
*/ | |||
#include <stdio.h> | |||
#include "utilities.h" | |||
#include "nvmm.h" | |||
#include "LoRaMac.h" | |||
#include "NvmDataMgmt.h" | |||
/*! | |||
* Enables/Disables the context storage management storage. | |||
* Must be enabled for LoRaWAN 1.0.4 or later. | |||
*/ | |||
#ifndef CONTEXT_MANAGEMENT_ENABLED | |||
#define CONTEXT_MANAGEMENT_ENABLED 1 | |||
#endif | |||
static uint16_t NvmNotifyFlags = 0; | |||
void NvmDataMgmtEvent( uint16_t notifyFlags ) | |||
{ | |||
NvmNotifyFlags = notifyFlags; | |||
} | |||
uint16_t NvmDataMgmtStore( void ) | |||
{ | |||
#if( CONTEXT_MANAGEMENT_ENABLED == 1 ) | |||
uint16_t offset = 0; | |||
uint16_t dataSize = 0; | |||
MibRequestConfirm_t mibReq; | |||
mibReq.Type = MIB_NVM_CTXS; | |||
LoRaMacMibGetRequestConfirm( &mibReq ); | |||
LoRaMacNvmData_t* nvm = mibReq.Param.Contexts; | |||
// Input checks | |||
if( NvmNotifyFlags == LORAMAC_NVM_NOTIFY_FLAG_NONE ) | |||
{ | |||
// There was no update. | |||
return 0; | |||
} | |||
if( LoRaMacStop( ) != LORAMAC_STATUS_OK ) | |||
{ | |||
return 0; | |||
} | |||
// Crypto | |||
if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_CRYPTO ) == | |||
LORAMAC_NVM_NOTIFY_FLAG_CRYPTO ) | |||
{ | |||
dataSize += NvmmWrite( ( uint8_t* ) &nvm->Crypto, sizeof( nvm->Crypto ), | |||
offset ); | |||
} | |||
offset += sizeof( nvm->Crypto ); | |||
// MacGroup1 | |||
if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1 ) == | |||
LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1 ) | |||
{ | |||
dataSize += NvmmWrite( ( uint8_t* ) &nvm->MacGroup1, | |||
sizeof( nvm->MacGroup1 ), offset ); | |||
} | |||
offset += sizeof( nvm->MacGroup1 ); | |||
// MacGroup2 | |||
if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2 ) == | |||
LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2 ) | |||
{ | |||
dataSize += NvmmWrite( ( uint8_t* ) &nvm->MacGroup2, | |||
sizeof( nvm->MacGroup2 ), offset ); | |||
} | |||
offset += sizeof( nvm->MacGroup2 ); | |||
// Secure element | |||
if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT ) == | |||
LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT ) | |||
{ | |||
dataSize += NvmmWrite( ( uint8_t* ) &nvm->SecureElement, sizeof( nvm->SecureElement ), | |||
offset ); | |||
} | |||
offset += sizeof( nvm->SecureElement ); | |||
// Region group 1 | |||
if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1 ) == | |||
LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1 ) | |||
{ | |||
dataSize += NvmmWrite( ( uint8_t* ) &nvm->RegionGroup1, | |||
sizeof( nvm->RegionGroup1 ), offset ); | |||
} | |||
offset += sizeof( nvm->RegionGroup1 ); | |||
// Region group 2 | |||
if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2 ) == | |||
LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2 ) | |||
{ | |||
dataSize += NvmmWrite( ( uint8_t* ) &nvm->RegionGroup2, | |||
sizeof( nvm->RegionGroup2 ), offset ); | |||
} | |||
offset += sizeof( nvm->RegionGroup2 ); | |||
// Class b | |||
if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_CLASS_B ) == | |||
LORAMAC_NVM_NOTIFY_FLAG_CLASS_B ) | |||
{ | |||
dataSize += NvmmWrite( ( uint8_t* ) &nvm->ClassB, sizeof( nvm->ClassB ), | |||
offset ); | |||
} | |||
offset += sizeof( nvm->ClassB ); | |||
// Reset notification flags | |||
NvmNotifyFlags = LORAMAC_NVM_NOTIFY_FLAG_NONE; | |||
// Resume LoRaMac | |||
LoRaMacStart( ); | |||
return dataSize; | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
uint16_t NvmDataMgmtRestore( void ) | |||
{ | |||
#if( CONTEXT_MANAGEMENT_ENABLED == 1 ) | |||
MibRequestConfirm_t mibReq; | |||
mibReq.Type = MIB_NVM_CTXS; | |||
LoRaMacMibGetRequestConfirm( &mibReq ); | |||
LoRaMacNvmData_t* nvm = mibReq.Param.Contexts; | |||
uint16_t offset = 0; | |||
// Crypto | |||
if( NvmmCrc32Check( sizeof( LoRaMacCryptoNvmData_t ), offset ) == false ) | |||
{ | |||
return 0; | |||
} | |||
offset += sizeof( LoRaMacCryptoNvmData_t ); | |||
// Mac Group 1 | |||
if( NvmmCrc32Check( sizeof( LoRaMacNvmDataGroup1_t ), offset ) == false ) | |||
{ | |||
return 0; | |||
} | |||
offset += sizeof( LoRaMacNvmDataGroup1_t ); | |||
// Mac Group 2 | |||
if( NvmmCrc32Check( sizeof( LoRaMacNvmDataGroup2_t ), offset ) == false ) | |||
{ | |||
return 0; | |||
} | |||
offset += sizeof( LoRaMacNvmDataGroup2_t ); | |||
// Secure element | |||
if( NvmmCrc32Check( sizeof( SecureElementNvmData_t ), offset ) == false ) | |||
{ | |||
return 0; | |||
} | |||
offset += sizeof( SecureElementNvmData_t ); | |||
// Region group 1 | |||
if( NvmmCrc32Check( sizeof( RegionNvmDataGroup1_t ), offset ) == false ) | |||
{ | |||
return 0; | |||
} | |||
offset += sizeof( RegionNvmDataGroup1_t ); | |||
// Region group 2 | |||
if( NvmmCrc32Check( sizeof( RegionNvmDataGroup2_t ), offset ) == false ) | |||
{ | |||
return 0; | |||
} | |||
offset += sizeof( RegionNvmDataGroup2_t ); | |||
// Class b | |||
if( NvmmCrc32Check( sizeof( LoRaMacClassBNvmData_t ), offset ) == false ) | |||
{ | |||
return 0; | |||
} | |||
offset += sizeof( LoRaMacClassBNvmData_t ); | |||
if( NvmmRead( ( uint8_t* ) nvm, sizeof( LoRaMacNvmData_t ), 0 ) == | |||
sizeof( LoRaMacNvmData_t ) ) | |||
{ | |||
return sizeof( LoRaMacNvmData_t ); | |||
} | |||
#endif | |||
return 0; | |||
} | |||
bool NvmDataMgmtFactoryReset( void ) | |||
{ | |||
uint16_t offset = 0; | |||
#if( CONTEXT_MANAGEMENT_ENABLED == 1 ) | |||
// Crypto | |||
if( NvmmReset( sizeof( LoRaMacCryptoNvmData_t ), offset ) == false ) | |||
{ | |||
return false; | |||
} | |||
offset += sizeof( LoRaMacCryptoNvmData_t ); | |||
// Mac Group 1 | |||
if( NvmmReset( sizeof( LoRaMacNvmDataGroup1_t ), offset ) == false ) | |||
{ | |||
return false; | |||
} | |||
offset += sizeof( LoRaMacNvmDataGroup1_t ); | |||
// Mac Group 2 | |||
if( NvmmReset( sizeof( LoRaMacNvmDataGroup2_t ), offset ) == false ) | |||
{ | |||
return false; | |||
} | |||
offset += sizeof( LoRaMacNvmDataGroup2_t ); | |||
// Secure element | |||
if( NvmmReset( sizeof( SecureElementNvmData_t ), offset ) == false ) | |||
{ | |||
return false; | |||
} | |||
offset += sizeof( SecureElementNvmData_t ); | |||
// Region group 1 | |||
if( NvmmReset( sizeof( RegionNvmDataGroup1_t ), offset ) == false ) | |||
{ | |||
return false; | |||
} | |||
offset += sizeof( RegionNvmDataGroup1_t ); | |||
// Region group 2 | |||
if( NvmmReset( sizeof( RegionNvmDataGroup2_t ), offset ) == false ) | |||
{ | |||
return false; | |||
} | |||
offset += sizeof( RegionNvmDataGroup2_t ); | |||
// Class b | |||
if( NvmmReset( sizeof( LoRaMacClassBNvmData_t ), offset ) == false ) | |||
{ | |||
return false; | |||
} | |||
offset += sizeof( LoRaMacClassBNvmData_t ); | |||
#endif | |||
return true; | |||
} |
@@ -0,0 +1,71 @@ | |||
/*! | |||
* \file NvmDataMgmt.h | |||
* | |||
* \brief NVM context management implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
* / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
* \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
* |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
* embedded.connectivity.solutions=============== | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
* | |||
* \author Daniel Jaeckle ( STACKFORCE ) | |||
* | |||
* \author Johannes Bruder ( STACKFORCE ) | |||
* | |||
* \defgroup NVMDATAMGMT NVM context management implementation | |||
* This module implements the NVM context handling | |||
* \{ | |||
*/ | |||
#ifndef __NVMDATAMGMT_H__ | |||
#define __NVMDATAMGMT_H__ | |||
/*! | |||
* \brief NVM Management event. | |||
* | |||
* \param [IN] notifyFlags Bitmap which contains the information about modules that | |||
* changed. | |||
*/ | |||
void NvmDataMgmtEvent( uint16_t notifyFlags ); | |||
/*! | |||
* \brief Function which stores the MAC data into NVM, if required. | |||
* | |||
* \retval Number of bytes which were stored. | |||
*/ | |||
uint16_t NvmDataMgmtStore( void ); | |||
/*! | |||
* \brief Function which restores the MAC data from NVM, if required. | |||
* | |||
* \retval Number of bytes which were restored. | |||
*/ | |||
uint16_t NvmDataMgmtRestore(void ); | |||
/*! | |||
* \brief Resets the NVM data. | |||
* | |||
* \retval Returns true, if successful. | |||
*/ | |||
bool NvmDataMgmtFactoryReset( void ); | |||
/* \} */ | |||
#endif // __NVMDATAMGMT_H__ |
@@ -0,0 +1,56 @@ | |||
/*! | |||
* \file cli.h | |||
* | |||
* \brief Command Line Interface handling implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2020 Semtech | |||
* | |||
* \endcode | |||
*/ | |||
#include <stdio.h> | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include "NvmDataMgmt.h" | |||
#include "cli.h" | |||
void CliProcess( Uart_t* uart ) | |||
{ | |||
uint8_t data = 0; | |||
if( UartGetChar( uart, &data ) == 0 ) | |||
{ | |||
if( data == '\x1B' ) | |||
{ // Escape character has been received | |||
printf( "ESC + " ); | |||
while( UartGetChar( uart, &data ) != 0 ) | |||
{ | |||
} | |||
printf( "%c\n", data ); | |||
if( data == 'N' ) | |||
{ // N character has been received | |||
data = 0; | |||
// Reset NVM | |||
if( NvmDataMgmtFactoryReset( ) == true ) | |||
{ | |||
printf( "\n\nNVM factory reset succeed\n" ); | |||
} | |||
else | |||
{ | |||
printf( "\n\nNVM factory reset failed\n" ); | |||
} | |||
printf( "\n\nPLEASE RESET THE END-DEVICE\n\n" ); | |||
while( 1 ); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
/*! | |||
* \file cli.h | |||
* | |||
* \brief Command Line Interface handling definition | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2020 Semtech | |||
* | |||
* \endcode | |||
*/ | |||
#ifndef CLI_H | |||
#define CLI_H | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#include "uart.h" | |||
/*! | |||
* Process characters received on the serial interface | |||
* \remark Characters sequence 'ESC' + 'N' execute a NVM factory reset | |||
* All other sequences are ignored | |||
* | |||
* \param [IN] uart UART interface object used by the command line interface | |||
*/ | |||
void CliProcess( Uart_t* uart ); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif // CLI_H |
@@ -0,0 +1,33 @@ | |||
/*! | |||
* \file githubVersion.h | |||
* | |||
* \brief GitHub version definition | |||
* | |||
* \copyright Revised BSD License, see file LICENSE.txt | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2019-2020 Semtech | |||
* | |||
* \endcode | |||
*/ | |||
#ifndef __GITHUB_VERSION_H__ | |||
#define __GITHUB_VERSION_H__ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#define GITHUB_VERSION 0x04050000 // 4.5.0.0 | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif // __GITHUB_VERSION_H__ |
@@ -0,0 +1,746 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/B-L072Z-LRWAN1/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED3 | |||
*/ | |||
static TimerEvent_t Led3Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Led 3 Timeout event | |||
*/ | |||
static void OnLed3TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Blinks every 5 seconds when beacon is acquired | |||
extern Gpio_t Led3; // Rx | |||
extern Gpio_t Led4; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &Led3Timer, OnLed3TimerEvent ); | |||
TimerSetValue( &Led3Timer, 100 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 3 ON | |||
GpioWrite( &Led3, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 3 OFF for each received downlink | |||
GpioWrite( &Led3, 0 ); | |||
TimerStart( &Led3Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 3 OFF | |||
GpioWrite( &Led3, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 3 OFF | |||
GpioWrite( &Led3, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Led 3 Timeout event | |||
*/ | |||
static void OnLed3TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led3Timer ); | |||
// Switch LED 3 ON | |||
GpioWrite( &Led3, 1 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,725 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/NAMote72/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board-config.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "gps.h" | |||
#include "mpl3115.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = MPL3115ReadTemperature, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 100 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 0 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 2 OFF for each received downlink | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 1 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 1 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 1 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,721 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/NucleoL073/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 100 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 2 OFF for each received downlink | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,721 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/NucleoL152/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 100 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 2 OFF for each received downlink | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,721 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/NucleoL476/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 100 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 2 OFF for each received downlink | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,666 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/SAMR34/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Rx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 100 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 50 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Tick the RTC to execute callback in context of the main loop (in stead of the IRQ) | |||
TimerProcess( ); | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 1 OFF for each received downlink | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
} |
@@ -0,0 +1,697 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/SKiM880B/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED4 | |||
*/ | |||
static TimerEvent_t Led4Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; // Tx | |||
extern Gpio_t Led2; // Rx | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led4Timer, OnLed4TimerEvent ); | |||
TimerSetValue( &Led4Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 100 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 2 OFF for each received downlink | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led4, 1 ); | |||
TimerStart( &Led4Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led4Timer ); | |||
// Switch LED 4 OFF | |||
GpioWrite( &Led4, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
} |
@@ -0,0 +1,697 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/SKiM881AXL/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED4 | |||
*/ | |||
static TimerEvent_t Led4Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; // Tx | |||
extern Gpio_t Led2; // Rx | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led4Timer, OnLed4TimerEvent ); | |||
TimerSetValue( &Led4Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 100 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 2 OFF for each received downlink | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led4, 1 ); | |||
TimerStart( &Led4Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led4Timer ); | |||
// Switch LED 4 OFF | |||
GpioWrite( &Led4, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
} |
@@ -0,0 +1,697 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief FUOTA interop tests - test 01 | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file fuota-test-01/SKiM980A/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "LmhpClockSync.h" | |||
#include "LmhpRemoteMcastSetup.h" | |||
#include "LmhpFragmentation.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 40s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 40000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 5s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 5000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_3 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED4 | |||
*/ | |||
static TimerEvent_t Led4Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ); | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ); | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ); | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ); | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Defines the maximum size for the buffer receiving the fragmentation result. | |||
* | |||
* \remark By default FragDecoder.h defines: | |||
* \ref FRAG_MAX_NB 21 | |||
* \ref FRAG_MAX_SIZE 50 | |||
* | |||
* FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE | |||
* | |||
* If bigger file size is to be received or is fragmented differently | |||
* one must update those parameters. | |||
*/ | |||
#define UNFRAGMENTED_DATA_SIZE ( 21 * 50 ) | |||
/* | |||
* Un-fragmented data storage. | |||
*/ | |||
static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE]; | |||
static LmhpFragmentationParams_t FragmentationParams = | |||
{ | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
.DecoderCallbacks = | |||
{ | |||
.FragDecoderWrite = FragDecoderWrite, | |||
.FragDecoderRead = FragDecoderRead, | |||
}, | |||
#else | |||
.Buffer = UnfragmentedData, | |||
.BufferSize = UNFRAGMENTED_DATA_SIZE, | |||
#endif | |||
.OnProgress = OnFragProgress, | |||
.OnDone = OnFragDone | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/* | |||
* Indicates if the system time has been synchronized | |||
*/ | |||
static volatile bool IsClockSynched = false; | |||
/* | |||
* MC Session Started | |||
*/ | |||
static volatile bool IsMcSessionStarted = false; | |||
/* | |||
* Indicates if the file transfer is done | |||
*/ | |||
static volatile bool IsFileTransferDone = false; | |||
/* | |||
* Received file computed CRC32 | |||
*/ | |||
static volatile uint32_t FileRxCrc = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; // Tx | |||
extern Gpio_t Led2; // Rx | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led4Timer, OnLed4TimerEvent ); | |||
TimerSetValue( &Led4Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 100 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "fuota-test-01", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL ); | |||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams ); | |||
IsClockSynched = false; | |||
IsFileTransferDone = false; | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
switch( deviceClass ) | |||
{ | |||
default: | |||
case CLASS_A: | |||
{ | |||
IsMcSessionStarted = false; | |||
break; | |||
} | |||
case CLASS_B: | |||
{ | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
IsMcSessionStarted = true; | |||
break; | |||
} | |||
case CLASS_C: | |||
{ | |||
IsMcSessionStarted = true; | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
break; | |||
} | |||
} | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
IsClockSynched = isSynchronized; | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
IsClockSynched = true; | |||
} | |||
#endif | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
UnfragmentedData[addr + i] = data[i]; | |||
} | |||
return 0; // Success | |||
} | |||
static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size ) | |||
{ | |||
if( size >= UNFRAGMENTED_DATA_SIZE ) | |||
{ | |||
return -1; // Fail | |||
} | |||
for(uint32_t i = 0; i < size; i++ ) | |||
{ | |||
data[i] = UnfragmentedData[addr + i]; | |||
} | |||
return 0; // Success | |||
} | |||
#endif | |||
static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ) | |||
{ | |||
// Switch LED 2 OFF for each received downlink | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### PROGRESS ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "RECEIVED : %5d / %5d Fragments\n", fragCounter, fragNb ); | |||
printf( " %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize ); | |||
printf( "LOST : %7d Fragments\n\n", fragNbLost ); | |||
} | |||
#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) | |||
static void OnFragDone( int32_t status, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( UnfragmentedData, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#else | |||
static void OnFragDone( int32_t status, uint8_t *file, uint32_t size ) | |||
{ | |||
FileRxCrc = Crc32( file, size ); | |||
IsFileTransferDone = true; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
printf( "\n###### =========== FRAG_DECODER ============ ######\n" ); | |||
printf( "###### FINISHED ######\n"); | |||
printf( "###### ===================================== ######\n"); | |||
printf( "STATUS : %ld\n", status ); | |||
printf( "CRC : %08lX\n\n", FileRxCrc ); | |||
} | |||
#endif | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR; | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
if( IsMcSessionStarted == false ) | |||
{ | |||
if( IsFileTransferDone == false ) | |||
{ | |||
if( IsClockSynched == false ) | |||
{ | |||
status = LmhpClockSyncAppTimeReq( ); | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = randr( 0, 255 ); | |||
// Send random packet | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 1, | |||
.Port = 1, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
} | |||
else | |||
{ | |||
AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq | |||
AppDataBuffer[1] = FileRxCrc & 0x000000FF; | |||
AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF; | |||
AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF; | |||
AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF; | |||
// Send FragAuthReq | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 5, | |||
.Port = 201, | |||
}; | |||
status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed ); | |||
} | |||
if( status == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led4, 1 ); | |||
TimerStart( &Led4Timer ); | |||
} | |||
} | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led4Timer ); | |||
// Switch LED 4 OFF | |||
GpioWrite( &Led4, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 1 ); | |||
} |
@@ -0,0 +1,33 @@ | |||
/*! | |||
* \file firmwareVersion.h | |||
* | |||
* \brief Firmware version definition | |||
* | |||
* \copyright Revised BSD License, see file LICENSE.txt | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2019-2020 Semtech | |||
* | |||
* \endcode | |||
*/ | |||
#ifndef __FIRMWARE_VERSION_H__ | |||
#define __FIRMWARE_VERSION_H__ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#define FIRMWARE_VERSION 0x01020000 // 1.2.0.0 | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif // __FIRMWARE_VERSION_H__ |
@@ -0,0 +1,597 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/B-L072Z-LRWAN1/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 2 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED3 | |||
*/ | |||
static TimerEvent_t Led3Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Led 3 Timeout event | |||
*/ | |||
static void OnLed3TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Blinks every 5 seconds when beacon is acquired | |||
extern Gpio_t Led3; // Rx | |||
extern Gpio_t Led4; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &Led3Timer, OnLed3TimerEvent ); | |||
TimerSetValue( &Led3Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
GpioWrite( &Led4, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 ); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led3, 1 ); | |||
TimerStart( &Led3Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Led 3 Timeout event | |||
*/ | |||
static void OnLed3TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led3Timer ); | |||
// Switch LED 3 OFF | |||
GpioWrite( &Led3, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,616 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/NAMote72/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board-config.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "gps.h" | |||
#include "mpl3115.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 2 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = MPL3115ReadTemperature, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx and blinks every 5 seconds when beacon is acquired | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 ); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
#if defined( REGION_US915 ) | |||
MibRequestConfirm_t mibReq; | |||
if( BoardGetBatteryVoltage( ) < LOW_BAT_THRESHOLD ) | |||
{ | |||
mibReq.Type = MIB_CHANNELS_TX_POWER; | |||
LoRaMacMibGetRequestConfirm( &mibReq ); | |||
// 30 dBm = TX_POWER_0, 28 dBm = TX_POWER_1, ..., 20 dBm = TX_POWER_5, ..., 10 dBm = TX_POWER_10 | |||
// The if condition is then "less than" to check if the power is greater than 20 dBm | |||
if( mibReq.Param.ChannelsTxPower < TX_POWER_5 ) | |||
{ | |||
mibReq.Param.ChannelsTxPower = TX_POWER_5; | |||
LoRaMacMibSetRequestConfirm( &mibReq ); | |||
} | |||
} | |||
#endif | |||
static uint8_t TxGpsData = 1; // GPS data transmission control | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
if( TxGpsData == 0 ) | |||
{ | |||
CayenneLppAddDigitalInput( 0, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( 1, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppAddTemperature( 2, MPL3115ReadTemperature( ) ); | |||
CayenneLppAddBarometricPressure( 3, MPL3115ReadPressure( ) / 100 ); | |||
} | |||
else | |||
{ | |||
if( GpsHasFix( ) == true ) | |||
{ | |||
double latitude = 0, longitude = 0; | |||
uint16_t altitudeGps = 0xFFFF; | |||
GpsGetLatestGpsPositionDouble( &latitude, &longitude ); | |||
altitudeGps = GpsGetLatestGpsAltitude( ); // in m | |||
CayenneLppAddGps( 4, latitude, longitude, altitudeGps ); | |||
} | |||
else | |||
{ | |||
CayenneLppAddGps( 4, 0, 0, 0 ); | |||
} | |||
} | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
TxGpsData = ( TxGpsData + 1 ) & 0x01; // Send GPS data every 2 uplinks | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 1 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 1 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,571 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/NucleoL073/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 2 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,571 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/NucleoL152/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 2 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,571 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/NucleoL476/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 2 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
extern Gpio_t Led2; // Rx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart2; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart2 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,573 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/SAMR34/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 2 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED1 | |||
*/ | |||
static TimerEvent_t Led1Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; // Tx | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 50 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Tick the RTC to execute callback in context of the main loop (in stead of the IRQ) | |||
TimerProcess( ); | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 1 ON for each received downlink | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 1 Timeout event | |||
*/ | |||
static void OnLed1TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led1Timer ); | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led1, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,586 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/SKiM880B/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 3 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED4 | |||
*/ | |||
static TimerEvent_t Led4Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; // Tx | |||
extern Gpio_t Led2; // Rx and blinks every 5 seconds when beacon is acquired | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led4Timer, OnLed4TimerEvent ); | |||
TimerSetValue( &Led4Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 ); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
uint8_t potiPercentage = 0; | |||
uint16_t vdd = 0; | |||
// Read the current potentiometer setting in percent | |||
potiPercentage = BoardGetPotiLevel( ); | |||
// Read the current voltage level | |||
BoardGetBatteryLevel( ); // Updates the value returned by BoardGetBatteryVoltage( ) function. | |||
vdd = BoardGetBatteryVoltage( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppAddAnalogInput( channel++, potiPercentage ); | |||
CayenneLppAddAnalogInput( channel++, vdd ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 4 ON | |||
GpioWrite( &Led4, 1 ); | |||
TimerStart( &Led4Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led4Timer ); | |||
// Switch LED 4 OFF | |||
GpioWrite( &Led4, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,586 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/SKiM881AXL/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 3 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED4 | |||
*/ | |||
static TimerEvent_t Led4Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; // Tx | |||
extern Gpio_t Led2; // Rx and blinks every 5 seconds when beacon is acquired | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led4Timer, OnLed4TimerEvent ); | |||
TimerSetValue( &Led4Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 ); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
uint8_t potiPercentage = 0; | |||
uint16_t vdd = 0; | |||
// Read the current potentiometer setting in percent | |||
potiPercentage = BoardGetPotiLevel( ); | |||
// Read the current voltage level | |||
BoardGetBatteryLevel( ); // Updates the value returned by BoardGetBatteryVoltage( ) function. | |||
vdd = BoardGetBatteryVoltage( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppAddAnalogInput( channel++, potiPercentage ); | |||
CayenneLppAddAnalogInput( channel++, vdd ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 4 ON | |||
GpioWrite( &Led4, 1 ); | |||
TimerStart( &Led4Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led4Timer ); | |||
// Switch LED 4 OFF | |||
GpioWrite( &Led4, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,586 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Performs a periodic uplink | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2018 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
*/ | |||
/*! \file periodic-uplink/SKiM980A/main.c */ | |||
#include <stdio.h> | |||
#include "../firmwareVersion.h" | |||
#include "../../common/githubVersion.h" | |||
#include "utilities.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "uart.h" | |||
#include "RegionCommon.h" | |||
#include "cli.h" | |||
#include "Commissioning.h" | |||
#include "LmHandler.h" | |||
#include "LmhpCompliance.h" | |||
#include "CayenneLpp.h" | |||
#include "LmHandlerMsgDisplay.h" | |||
#ifndef ACTIVE_REGION | |||
#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." | |||
#define ACTIVE_REGION LORAMAC_REGION_EU868 | |||
#endif | |||
/*! | |||
* LoRaWAN default end-device class | |||
*/ | |||
#define LORAWAN_DEFAULT_CLASS CLASS_A | |||
/*! | |||
* Defines the application data transmission duty cycle. 5s, value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE 5000 | |||
/*! | |||
* Defines a random delay for application data transmission duty cycle. 1s, | |||
* value in [ms]. | |||
*/ | |||
#define APP_TX_DUTYCYCLE_RND 1000 | |||
/*! | |||
* LoRaWAN Adaptive Data Rate | |||
* | |||
* \remark Please note that when ADR is enabled the end-device should be static | |||
*/ | |||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON | |||
/*! | |||
* Default datarate | |||
* | |||
* \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled | |||
*/ | |||
#define LORAWAN_DEFAULT_DATARATE DR_0 | |||
/*! | |||
* LoRaWAN confirmed messages | |||
*/ | |||
#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG | |||
/*! | |||
* User application data buffer size | |||
*/ | |||
#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 | |||
/*! | |||
* LoRaWAN ETSI duty cycle control enable/disable | |||
* | |||
* \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes | |||
*/ | |||
#define LORAWAN_DUTYCYCLE_ON true | |||
/*! | |||
* LoRaWAN application port | |||
* @remark The allowed port range is from 1 up to 223. Other values are reserved. | |||
*/ | |||
#define LORAWAN_APP_PORT 3 | |||
/*! | |||
* | |||
*/ | |||
typedef enum | |||
{ | |||
LORAMAC_HANDLER_TX_ON_TIMER, | |||
LORAMAC_HANDLER_TX_ON_EVENT, | |||
}LmHandlerTxEvents_t; | |||
/*! | |||
* User application data | |||
*/ | |||
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; | |||
/*! | |||
* User application data structure | |||
*/ | |||
static LmHandlerAppData_t AppData = | |||
{ | |||
.Buffer = AppDataBuffer, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
/*! | |||
* Specifies the state of the application LED | |||
*/ | |||
static bool AppLedStateOn = false; | |||
/*! | |||
* Timer to handle the application data transmission duty cycle | |||
*/ | |||
static TimerEvent_t TxTimer; | |||
/*! | |||
* Timer to handle the state of LED4 | |||
*/ | |||
static TimerEvent_t Led4Timer; | |||
/*! | |||
* Timer to handle the state of LED2 | |||
*/ | |||
static TimerEvent_t Led2Timer; | |||
/*! | |||
* Timer to handle the state of LED beacon indicator | |||
*/ | |||
static TimerEvent_t LedBeaconTimer; | |||
static void OnMacProcessNotify( void ); | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ); | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ); | |||
static void OnTxData( LmHandlerTxParams_t* params ); | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); | |||
static void OnClassChange( DeviceClass_t deviceClass ); | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); | |||
#else | |||
static void OnSysTimeUpdate( void ); | |||
#endif | |||
static void PrepareTxFrame( void ); | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ); | |||
static void UplinkProcess( void ); | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ); | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ); | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ); | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ); | |||
static LmHandlerCallbacks_t LmHandlerCallbacks = | |||
{ | |||
.GetBatteryLevel = BoardGetBatteryLevel, | |||
.GetTemperature = NULL, | |||
.GetRandomSeed = BoardGetRandomSeed, | |||
.OnMacProcess = OnMacProcessNotify, | |||
.OnNvmDataChange = OnNvmDataChange, | |||
.OnNetworkParametersChange = OnNetworkParametersChange, | |||
.OnMacMcpsRequest = OnMacMcpsRequest, | |||
.OnMacMlmeRequest = OnMacMlmeRequest, | |||
.OnJoinRequest = OnJoinRequest, | |||
.OnTxData = OnTxData, | |||
.OnRxData = OnRxData, | |||
.OnClassChange= OnClassChange, | |||
.OnBeaconStatusChange = OnBeaconStatusChange, | |||
.OnSysTimeUpdate = OnSysTimeUpdate, | |||
}; | |||
static LmHandlerParams_t LmHandlerParams = | |||
{ | |||
.Region = ACTIVE_REGION, | |||
.AdrEnable = LORAWAN_ADR_STATE, | |||
.IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, | |||
.TxDatarate = LORAWAN_DEFAULT_DATARATE, | |||
.PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, | |||
.DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, | |||
.DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, | |||
.DataBuffer = AppDataBuffer, | |||
.PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, | |||
}; | |||
static LmhpComplianceParams_t LmhpComplianceParams = | |||
{ | |||
.FwVersion.Value = FIRMWARE_VERSION, | |||
.OnTxPeriodicityChanged = OnTxPeriodicityChanged, | |||
.OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, | |||
.OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, | |||
}; | |||
/*! | |||
* Indicates if LoRaMacProcess call is pending. | |||
* | |||
* \warning If variable is equal to 0 then the MCU can be set in low power mode | |||
*/ | |||
static volatile uint8_t IsMacProcessPending = 0; | |||
static volatile uint8_t IsTxFramePending = 0; | |||
static volatile uint32_t TxPeriodicity = 0; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; // Tx | |||
extern Gpio_t Led2; // Rx and blinks every 5 seconds when beacon is acquired | |||
extern Gpio_t Led3; // App | |||
/*! | |||
* UART object used for command line interface handling | |||
*/ | |||
extern Uart_t Uart1; | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led4Timer, OnLed4TimerEvent ); | |||
TimerSetValue( &Led4Timer, 25 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 25 ); | |||
TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent ); | |||
TimerSetValue( &LedBeaconTimer, 5000 ); | |||
// Initialize transmission periodicity variable | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
const Version_t appVersion = { .Value = FIRMWARE_VERSION }; | |||
const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; | |||
DisplayAppInfo( "periodic-uplink-lpp", | |||
&appVersion, | |||
&gitHubVersion ); | |||
if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
printf( "LoRaMac wasn't properly initialized\n" ); | |||
// Fatal error, endless loop. | |||
while ( 1 ) | |||
{ | |||
} | |||
} | |||
// Set system maximum tolerated rx error in milliseconds | |||
LmHandlerSetSystemMaxRxError( 20 ); | |||
// The LoRa-Alliance Compliance protocol package should always be | |||
// initialized and activated. | |||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); | |||
LmHandlerJoin( ); | |||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); | |||
while( 1 ) | |||
{ | |||
// Process characters sent over the command line interface | |||
CliProcess( &Uart1 ); | |||
// Processes the LoRaMac events | |||
LmHandlerProcess( ); | |||
// Process application uplinks management | |||
UplinkProcess( ); | |||
CRITICAL_SECTION_BEGIN( ); | |||
if( IsMacProcessPending == 1 ) | |||
{ | |||
// Clear flag and prevent MCU to go into low power modes. | |||
IsMacProcessPending = 0; | |||
} | |||
else | |||
{ | |||
// The MCU wakes up through events | |||
BoardLowPowerHandler( ); | |||
} | |||
CRITICAL_SECTION_END( ); | |||
} | |||
} | |||
static void OnMacProcessNotify( void ) | |||
{ | |||
IsMacProcessPending = 1; | |||
} | |||
static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) | |||
{ | |||
DisplayNvmDataChange( state, size ); | |||
} | |||
static void OnNetworkParametersChange( CommissioningParams_t* params ) | |||
{ | |||
DisplayNetworkParametersUpdate( params ); | |||
} | |||
static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); | |||
} | |||
static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) | |||
{ | |||
DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); | |||
} | |||
static void OnJoinRequest( LmHandlerJoinParams_t* params ) | |||
{ | |||
DisplayJoinRequestUpdate( params ); | |||
if( params->Status == LORAMAC_HANDLER_ERROR ) | |||
{ | |||
LmHandlerJoin( ); | |||
} | |||
else | |||
{ | |||
LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); | |||
} | |||
} | |||
static void OnTxData( LmHandlerTxParams_t* params ) | |||
{ | |||
DisplayTxUpdate( params ); | |||
} | |||
static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) | |||
{ | |||
DisplayRxUpdate( appData, params ); | |||
switch( appData->Port ) | |||
{ | |||
case 1: // The application LED can be controlled on port 1 or 2 | |||
case LORAWAN_APP_PORT: | |||
{ | |||
AppLedStateOn = appData->Buffer[0] & 0x01; | |||
GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 ); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
// Switch LED 2 ON for each received downlink | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
static void OnClassChange( DeviceClass_t deviceClass ) | |||
{ | |||
DisplayClassUpdate( deviceClass ); | |||
// Inform the server as soon as possible that the end-device has switched to ClassB | |||
LmHandlerAppData_t appData = | |||
{ | |||
.Buffer = NULL, | |||
.BufferSize = 0, | |||
.Port = 0, | |||
}; | |||
LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); | |||
} | |||
static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) | |||
{ | |||
switch( params->State ) | |||
{ | |||
case LORAMAC_HANDLER_BEACON_RX: | |||
{ | |||
TimerStart( &LedBeaconTimer ); | |||
break; | |||
} | |||
case LORAMAC_HANDLER_BEACON_LOST: | |||
case LORAMAC_HANDLER_BEACON_NRX: | |||
{ | |||
TimerStop( &LedBeaconTimer ); | |||
break; | |||
} | |||
default: | |||
{ | |||
break; | |||
} | |||
} | |||
DisplayBeaconUpdate( params ); | |||
} | |||
#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) | |||
static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) | |||
{ | |||
} | |||
#else | |||
static void OnSysTimeUpdate( void ) | |||
{ | |||
} | |||
#endif | |||
/*! | |||
* Prepares the payload of the frame and transmits it. | |||
*/ | |||
static void PrepareTxFrame( void ) | |||
{ | |||
if( LmHandlerIsBusy( ) == true ) | |||
{ | |||
return; | |||
} | |||
uint8_t channel = 0; | |||
AppData.Port = LORAWAN_APP_PORT; | |||
CayenneLppReset( ); | |||
uint8_t potiPercentage = 0; | |||
uint16_t vdd = 0; | |||
// Read the current potentiometer setting in percent | |||
potiPercentage = BoardGetPotiLevel( ); | |||
// Read the current voltage level | |||
BoardGetBatteryLevel( ); // Updates the value returned by BoardGetBatteryVoltage( ) function. | |||
vdd = BoardGetBatteryVoltage( ); | |||
CayenneLppAddDigitalInput( channel++, AppLedStateOn ); | |||
CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); | |||
CayenneLppAddAnalogInput( channel++, potiPercentage ); | |||
CayenneLppAddAnalogInput( channel++, vdd ); | |||
CayenneLppCopy( AppData.Buffer ); | |||
AppData.BufferSize = CayenneLppGetSize( ); | |||
if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) | |||
{ | |||
// Switch LED 4 ON | |||
GpioWrite( &Led4, 1 ); | |||
TimerStart( &Led4Timer ); | |||
} | |||
} | |||
static void StartTxProcess( LmHandlerTxEvents_t txEvent ) | |||
{ | |||
switch( txEvent ) | |||
{ | |||
default: | |||
// Intentional fall through | |||
case LORAMAC_HANDLER_TX_ON_TIMER: | |||
{ | |||
// Schedule 1st packet transmission | |||
TimerInit( &TxTimer, OnTxTimerEvent ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
OnTxTimerEvent( NULL ); | |||
} | |||
break; | |||
case LORAMAC_HANDLER_TX_ON_EVENT: | |||
{ | |||
} | |||
break; | |||
} | |||
} | |||
static void UplinkProcess( void ) | |||
{ | |||
uint8_t isPending = 0; | |||
CRITICAL_SECTION_BEGIN( ); | |||
isPending = IsTxFramePending; | |||
IsTxFramePending = 0; | |||
CRITICAL_SECTION_END( ); | |||
if( isPending == 1 ) | |||
{ | |||
PrepareTxFrame( ); | |||
} | |||
} | |||
static void OnTxPeriodicityChanged( uint32_t periodicity ) | |||
{ | |||
TxPeriodicity = periodicity; | |||
if( TxPeriodicity == 0 ) | |||
{ // Revert to application default periodicity | |||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |||
} | |||
// Update timer periodicity | |||
TimerStop( &TxTimer ); | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) | |||
{ | |||
LmHandlerParams.IsTxConfirmed = isTxConfirmed; | |||
} | |||
static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) | |||
{ | |||
LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; | |||
} | |||
/*! | |||
* Function executed on TxTimer event | |||
*/ | |||
static void OnTxTimerEvent( void* context ) | |||
{ | |||
TimerStop( &TxTimer ); | |||
IsTxFramePending = 1; | |||
// Schedule next transmission | |||
TimerSetValue( &TxTimer, TxPeriodicity ); | |||
TimerStart( &TxTimer ); | |||
} | |||
/*! | |||
* Function executed on Led 4 Timeout event | |||
*/ | |||
static void OnLed4TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led4Timer ); | |||
// Switch LED 4 OFF | |||
GpioWrite( &Led4, 0 ); | |||
} | |||
/*! | |||
* Function executed on Led 2 Timeout event | |||
*/ | |||
static void OnLed2TimerEvent( void* context ) | |||
{ | |||
TimerStop( &Led2Timer ); | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 0 ); | |||
} | |||
/*! | |||
* \brief Function executed on Beacon timer Timeout event | |||
*/ | |||
static void OnLedBeaconTimerEvent( void* context ) | |||
{ | |||
GpioWrite( &Led2, 1 ); | |||
TimerStart( &Led2Timer ); | |||
TimerStart( &LedBeaconTimer ); | |||
} |
@@ -0,0 +1,33 @@ | |||
/*! | |||
* \file firmwareVersion.h | |||
* | |||
* \brief Firmware version definition | |||
* | |||
* \copyright Revised BSD License, see file LICENSE.txt | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2019-2020 Semtech | |||
* | |||
* \endcode | |||
*/ | |||
#ifndef __FIRMWARE_VERSION_H__ | |||
#define __FIRMWARE_VERSION_H__ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#define FIRMWARE_VERSION 0x01020000 // 1.2.0.0 | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif // __FIRMWARE_VERSION_H__ |
@@ -0,0 +1,369 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioWrite( &Led1, GpioRead( &Led1 ) ^ 1 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioWrite( &Led2, 1 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioWrite( &Led1, GpioRead( &Led1 ) ^ 1 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioWrite( &Led2, GpioRead( &Led2 ) ^ 1 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,105 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder (STACKFORCE), Miguel Luis (Semtech) | |||
## | |||
project(ping-pong) | |||
cmake_minimum_required(VERSION 3.6) | |||
#--------------------------------------------------------------------------------------- | |||
# Options | |||
#--------------------------------------------------------------------------------------- | |||
# Allow selection of region | |||
option(REGION_EU868 "Region EU868" ON) | |||
option(REGION_US915 "Region US915" OFF) | |||
option(REGION_CN779 "Region CN779" OFF) | |||
option(REGION_EU433 "Region EU433" OFF) | |||
option(REGION_AU915 "Region AU915" OFF) | |||
option(REGION_AS923 "Region AS923" OFF) | |||
option(REGION_CN470 "Region CN470" OFF) | |||
option(REGION_KR920 "Region KR920" OFF) | |||
option(REGION_IN865 "Region IN865" OFF) | |||
option(REGION_RU864 "Region RU864" OFF) | |||
set(REGION_LIST REGION_EU868 REGION_US915 REGION_CN779 REGION_EU433 REGION_AU915 REGION_AS923 REGION_CN470 REGION_KR920 REGION_IN865 REGION_RU864) | |||
# Allow switching of modulation | |||
set(MODULATION_LIST LORA FSK) | |||
set(MODULATION LORA CACHE STRING "Default modulation is LoRa") | |||
set_property(CACHE MODULATION PROPERTY STRINGS ${MODEM_LIST}) | |||
#--------------------------------------------------------------------------------------- | |||
# Target | |||
#--------------------------------------------------------------------------------------- | |||
file(GLOB ${PROJECT_NAME}_SOURCES "${CMAKE_CURRENT_LIST_DIR}/${BOARD}/*.c") | |||
add_executable(${PROJECT_NAME} | |||
${${PROJECT_NAME}_SOURCES} | |||
$<TARGET_OBJECTS:system> | |||
$<TARGET_OBJECTS:radio> | |||
$<TARGET_OBJECTS:peripherals> | |||
$<TARGET_OBJECTS:${BOARD}> | |||
) | |||
# Loops through all regions and add compile time definitions for the enabled ones. | |||
foreach( REGION ${REGION_LIST} ) | |||
if(${REGION}) | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC -D"${REGION}") | |||
endif() | |||
endforeach() | |||
if(MODULATION STREQUAL LORA) | |||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_MODEM_LORA) | |||
elseif(MODULATION STREQUAL FSK) | |||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_MODEM_FSK) | |||
endif() | |||
# Add compile time definition for the mbed shield if set. | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC -D${MBED_RADIO_SHIELD}) | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:mac,INTERFACE_COMPILE_DEFINITIONS>> | |||
) | |||
target_include_directories(${PROJECT_NAME} PUBLIC | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:system,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:radio,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:peripherals,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:${BOARD},INTERFACE_INCLUDE_DIRECTORIES>> | |||
) | |||
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) | |||
target_link_libraries(${PROJECT_NAME} m) | |||
#--------------------------------------------------------------------------------------- | |||
# Debugging and Binutils | |||
#--------------------------------------------------------------------------------------- | |||
include(gdb-helper) | |||
include(binutils-arm-none-eabi) | |||
# Generate debugger configurations | |||
generate_run_gdb_stlink(${PROJECT_NAME}) | |||
generate_run_gdb_openocd(${PROJECT_NAME}) | |||
generate_vscode_launch_openocd(${PROJECT_NAME}) | |||
# Print section sizes of target | |||
print_section_sizes(${PROJECT_NAME}) | |||
# Create output in hex and binary format | |||
create_bin_output(${PROJECT_NAME}) | |||
create_hex_output(${PROJECT_NAME}) |
@@ -0,0 +1,362 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board-config.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioWrite( &Led1, GpioRead( &Led1 ) ^ 1 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioWrite( &Led2, 1 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioWrite( &Led1, GpioRead( &Led1 ) ^ 1 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioWrite( &Led2, GpioRead( &Led2 ) ^ 1 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,373 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioToggle( &Led1 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioToggle( &Led2 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioToggle( &Led1 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioToggle( &Led2 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,373 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioToggle( &Led1 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioToggle( &Led2 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioToggle( &Led1 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioToggle( &Led2 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,373 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioToggle( &Led1 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioToggle( &Led2 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioToggle( &Led1 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioToggle( &Led2 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,371 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
* | |||
* \author Marten Lootsma(TWTG) on behalf of Microchip/Atmel (c)2017 | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
// Tick the RTC to execute callback in context of the main loop (in stead of the IRQ) | |||
TimerProcess( ); | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioWrite( &Led1, GpioRead( &Led1 ) ^ 1 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioWrite( &Led1, GpioRead( &Led1 ) ^ 1 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,361 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led3; | |||
extern Gpio_t Led4; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioToggle( &Led4 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioToggle( &Led3 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioToggle( &Led4 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioToggle( &Led3 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,361 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led3; | |||
extern Gpio_t Led4; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioToggle( &Led4 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioToggle( &Led3 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioToggle( &Led4 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioToggle( &Led3 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,361 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Ping-Pong implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include <string.h> | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "delay.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 14 // dBm | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_FDEV 25000 // Hz | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
typedef enum | |||
{ | |||
LOWPOWER, | |||
RX, | |||
RX_TIMEOUT, | |||
RX_ERROR, | |||
TX, | |||
TX_TIMEOUT, | |||
}States_t; | |||
#define RX_TIMEOUT_VALUE 1000 | |||
#define BUFFER_SIZE 64 // Define the payload size here | |||
const uint8_t PingMsg[] = "PING"; | |||
const uint8_t PongMsg[] = "PONG"; | |||
uint16_t BufferSize = BUFFER_SIZE; | |||
uint8_t Buffer[BUFFER_SIZE]; | |||
States_t State = LOWPOWER; | |||
int8_t RssiValue = 0; | |||
int8_t SnrValue = 0; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led3; | |||
extern Gpio_t Led4; | |||
/*! | |||
* \brief Function to be executed on Radio Tx Done event | |||
*/ | |||
void OnTxDone( void ); | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnTxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Timeout event | |||
*/ | |||
void OnRxTimeout( void ); | |||
/*! | |||
* \brief Function executed on Radio Rx Error event | |||
*/ | |||
void OnRxError( void ); | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
bool isMaster = true; | |||
uint8_t i; | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.TxDone = OnTxDone; | |||
RadioEvents.RxDone = OnRxDone; | |||
RadioEvents.TxTimeout = OnTxTimeout; | |||
RadioEvents.RxTimeout = OnRxTimeout; | |||
RadioEvents.RxError = OnRxError; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, | |||
LORA_SPREADING_FACTOR, LORA_CODINGRATE, | |||
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 ); | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, BUFFER_SIZE ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, | |||
FSK_DATARATE, 0, | |||
FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, | |||
true, 0, 0, 0, 3000 ); | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0,false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, BUFFER_SIZE ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
while( 1 ) | |||
{ | |||
switch( State ) | |||
{ | |||
case RX: | |||
if( isMaster == true ) | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PONG | |||
GpioToggle( &Led4 ); | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ // A master already exists then become a slave | |||
isMaster = false; | |||
GpioToggle( &Led3 ); // Set LED off | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
else // valid reception but neither a PING or a PONG message | |||
{ // Set device as master ans start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if( BufferSize > 0 ) | |||
{ | |||
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 ) | |||
{ | |||
// Indicates on a LED that the received frame is a PING | |||
GpioToggle( &Led4 ); | |||
// Send the reply to the PONG string | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'O'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
// We fill the buffer with numbers for the payload | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else // valid reception but not a PING as expected | |||
{ // Set device as master and start again | |||
isMaster = true; | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
} | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX: | |||
// Indicates on a LED that we have sent a PING [Master] | |||
// Indicates on a LED that we have sent a PONG [Slave] | |||
GpioToggle( &Led3 ); | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case RX_TIMEOUT: | |||
case RX_ERROR: | |||
if( isMaster == true ) | |||
{ | |||
// Send the next PING frame | |||
Buffer[0] = 'P'; | |||
Buffer[1] = 'I'; | |||
Buffer[2] = 'N'; | |||
Buffer[3] = 'G'; | |||
for( i = 4; i < BufferSize; i++ ) | |||
{ | |||
Buffer[i] = i - 4; | |||
} | |||
DelayMs( 1 ); | |||
Radio.Send( Buffer, BufferSize ); | |||
} | |||
else | |||
{ | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
} | |||
State = LOWPOWER; | |||
break; | |||
case TX_TIMEOUT: | |||
Radio.Rx( RX_TIMEOUT_VALUE ); | |||
State = LOWPOWER; | |||
break; | |||
case LOWPOWER: | |||
default: | |||
// Set low power | |||
break; | |||
} | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnTxDone( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX; | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
Radio.Sleep( ); | |||
BufferSize = size; | |||
memcpy( Buffer, payload, BufferSize ); | |||
RssiValue = rssi; | |||
SnrValue = snr; | |||
State = RX; | |||
} | |||
void OnTxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = TX_TIMEOUT; | |||
} | |||
void OnRxTimeout( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_TIMEOUT; | |||
} | |||
void OnRxError( void ) | |||
{ | |||
Radio.Sleep( ); | |||
State = RX_ERROR; | |||
} |
@@ -0,0 +1,172 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 1 | |||
ledState ^= 1; | |||
GpioWrite( &Led1, ledState ); | |||
} |
@@ -0,0 +1,105 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder (STACKFORCE), Miguel Luis (Semtech) | |||
## | |||
project(rx-sensi) | |||
cmake_minimum_required(VERSION 3.6) | |||
#--------------------------------------------------------------------------------------- | |||
# Options | |||
#--------------------------------------------------------------------------------------- | |||
# Allow selection of region | |||
option(REGION_EU868 "Region EU868" ON) | |||
option(REGION_US915 "Region US915" OFF) | |||
option(REGION_CN779 "Region CN779" OFF) | |||
option(REGION_EU433 "Region EU433" OFF) | |||
option(REGION_AU915 "Region AU915" OFF) | |||
option(REGION_AS923 "Region AS923" OFF) | |||
option(REGION_CN470 "Region CN470" OFF) | |||
option(REGION_KR920 "Region KR920" OFF) | |||
option(REGION_IN865 "Region IN865" OFF) | |||
option(REGION_RU864 "Region RU864" OFF) | |||
set(REGION_LIST REGION_EU868 REGION_US915 REGION_CN779 REGION_EU433 REGION_AU915 REGION_AS923 REGION_CN470 REGION_KR920 REGION_IN865 REGION_RU864) | |||
# Allow switching of modulation | |||
set(MODULATION_LIST LORA FSK) | |||
set(MODULATION LORA CACHE STRING "Default modulation is LoRa") | |||
set_property(CACHE MODULATION PROPERTY STRINGS ${MODEM_LIST}) | |||
#--------------------------------------------------------------------------------------- | |||
# Target | |||
#--------------------------------------------------------------------------------------- | |||
file(GLOB ${PROJECT_NAME}_SOURCES "${CMAKE_CURRENT_LIST_DIR}/${BOARD}/*.c") | |||
add_executable(${PROJECT_NAME} | |||
${${PROJECT_NAME}_SOURCES} | |||
$<TARGET_OBJECTS:system> | |||
$<TARGET_OBJECTS:radio> | |||
$<TARGET_OBJECTS:peripherals> | |||
$<TARGET_OBJECTS:${BOARD}> | |||
) | |||
# Loops through all regions and add compile time definitions for the enabled ones. | |||
foreach( REGION ${REGION_LIST} ) | |||
if(${REGION}) | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC -D"${REGION}") | |||
endif() | |||
endforeach() | |||
if(MODULATION STREQUAL LORA) | |||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_MODEM_LORA) | |||
elseif(MODULATION STREQUAL FSK) | |||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_MODEM_FSK) | |||
endif() | |||
# Add compile time definition for the mbed shield if set. | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC -D${MBED_RADIO_SHIELD}) | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:mac,INTERFACE_COMPILE_DEFINITIONS>> | |||
) | |||
target_include_directories(${PROJECT_NAME} PUBLIC | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:system,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:radio,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:peripherals,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:${BOARD},INTERFACE_INCLUDE_DIRECTORIES>> | |||
) | |||
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) | |||
target_link_libraries(${PROJECT_NAME} m) | |||
#--------------------------------------------------------------------------------------- | |||
# Debugging and Binutils | |||
#--------------------------------------------------------------------------------------- | |||
include(gdb-helper) | |||
include(binutils-arm-none-eabi) | |||
# Generate debugger configurations | |||
generate_run_gdb_stlink(${PROJECT_NAME}) | |||
generate_run_gdb_openocd(${PROJECT_NAME}) | |||
generate_vscode_launch_openocd(${PROJECT_NAME}) | |||
# Print section sizes of target | |||
print_section_sizes(${PROJECT_NAME}) | |||
# Create output in hex and binary format | |||
create_bin_output(${PROJECT_NAME}) | |||
create_hex_output(${PROJECT_NAME}) |
@@ -0,0 +1,165 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board-config.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 1 | |||
ledState ^= 1; | |||
GpioWrite( &Led1, ledState ); | |||
} |
@@ -0,0 +1,177 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 1 | |||
ledState ^= 1; | |||
GpioWrite( &Led1, ledState ); | |||
} |
@@ -0,0 +1,177 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 1 | |||
ledState ^= 1; | |||
GpioWrite( &Led1, ledState ); | |||
} |
@@ -0,0 +1,177 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 1 | |||
ledState ^= 1; | |||
GpioWrite( &Led1, ledState ); | |||
} |
@@ -0,0 +1,177 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
* | |||
* \author Marten Lootsma(TWTG) on behalf of Microchip/Atmel (c)2017 | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
// Tick the RTC to execute callback in context of the main loop (in stead of the IRQ) | |||
TimerProcess( ); | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 1 | |||
ledState ^= 1; | |||
GpioWrite( &Led1, ledState ); | |||
} |
@@ -0,0 +1,164 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 4 | |||
ledState ^= 1; | |||
GpioWrite( &Led4, ledState ); | |||
} |
@@ -0,0 +1,164 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 4 | |||
ledState ^= 1; | |||
GpioWrite( &Led4, ledState ); | |||
} |
@@ -0,0 +1,164 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Radio sensitivity test | |||
* | |||
* \remark When LED1 stops blinking LoRa packets aren't received any more and | |||
* the sensitivity level has been reached. | |||
* By reading the RF generator output power we can estimate the board | |||
* sensitivity | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#if defined( USE_MODEM_LORA ) | |||
#define LORA_BANDWIDTH 0 // [0: 125 kHz, | |||
// 1: 250 kHz, | |||
// 2: 500 kHz, | |||
// 3: Reserved] | |||
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12] | |||
#define LORA_CODINGRATE 1 // [1: 4/5, | |||
// 2: 4/6, | |||
// 3: 4/7, | |||
// 4: 4/8] | |||
#define LORA_SYMBOL_TIMEOUT 5 // Symbols | |||
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx | |||
#define LORA_FIX_LENGTH_PAYLOAD_ON false | |||
#define LORA_IQ_INVERSION_ON false | |||
#elif defined( USE_MODEM_FSK ) | |||
#define FSK_DATARATE 50000 // bps | |||
#define FSK_BANDWIDTH 50000 // Hz | |||
#define FSK_AFC_BANDWIDTH 83333 // Hz | |||
#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx | |||
#define FSK_FIX_LENGTH_PAYLOAD_ON false | |||
#else | |||
#error "Please define a modem in the compiler options." | |||
#endif | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led4; | |||
/*! | |||
* \brief Function to be executed on Radio Rx Done event | |||
*/ | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); | |||
/*! | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
// Radio initialization | |||
RadioEvents.RxDone = OnRxDone; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetChannel( RF_FREQUENCY ); | |||
#if defined( USE_MODEM_LORA ) | |||
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, | |||
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, | |||
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, | |||
0, true, 0, 0, LORA_IQ_INVERSION_ON, true ); | |||
Radio.SetMaxPayloadLength( MODEM_LORA, 255 ); | |||
#elif defined( USE_MODEM_FSK ) | |||
Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, | |||
0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, | |||
0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true, | |||
0, 0, false, true ); | |||
Radio.SetMaxPayloadLength( MODEM_FSK, 255 ); | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
Radio.Rx( 0 ); // Continuous Rx | |||
while( 1 ) | |||
{ | |||
BoardLowPowerHandler( ); | |||
} | |||
} | |||
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) | |||
{ | |||
static uint8_t ledState = 1; | |||
// Toggle LED 4 | |||
ledState ^= 1; | |||
GpioWrite( &Led4, ledState ); | |||
} |
@@ -0,0 +1,203 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Tx Continuous Wave implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#define TX_OUTPUT_POWER 20 // 20 dBm | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#define TX_OUTPUT_POWER 20 // 20 dBm | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_TIMEOUT 65535 // seconds (MAX value) | |||
static TimerEvent_t Led1Timer; | |||
volatile bool Led1TimerEvent = false; | |||
static TimerEvent_t Led2Timer; | |||
volatile bool Led2TimerEvent = false; | |||
static TimerEvent_t Led3Timer; | |||
volatile bool Led3TimerEvent = false; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
extern Gpio_t Led3; | |||
/*! | |||
* \brief Function executed on Led 1 Timeout event | |||
*/ | |||
void OnLed1TimerEvent( void* context ) | |||
{ | |||
Led1TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Led 2 Timeout event | |||
*/ | |||
void OnLed2TimerEvent( void* context ) | |||
{ | |||
Led2TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Led 3 Timeout event | |||
*/ | |||
void OnLed3TimerEvent( void* context ) | |||
{ | |||
Led3TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnRadioTxTimeout( void ) | |||
{ | |||
// Restarts continuous wave transmission when timeout expires | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
} | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 90 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 90 ); | |||
TimerInit( &Led3Timer, OnLed3TimerEvent ); | |||
TimerSetValue( &Led3Timer, 90 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
// Radio initialization | |||
RadioEvents.TxTimeout = OnRadioTxTimeout; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
// Blink LEDs just to show some activity | |||
while( 1 ) | |||
{ | |||
if( Led1TimerEvent == true ) | |||
{ | |||
Led1TimerEvent = false; | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 1 ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
if( Led2TimerEvent == true ) | |||
{ | |||
Led2TimerEvent = false; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 1 ); | |||
// Switch LED 3 ON | |||
GpioWrite( &Led3, 0 ); | |||
TimerStart( &Led3Timer ); | |||
} | |||
if( Led3TimerEvent == true ) | |||
{ | |||
Led3TimerEvent = false; | |||
// Switch LED 3 OFF | |||
GpioWrite( &Led3, 1 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} |
@@ -0,0 +1,91 @@ | |||
## | |||
## ______ _ | |||
## / _____) _ | | | |||
## ( (____ _____ ____ _| |_ _____ ____| |__ | |||
## \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
## _____) ) ____| | | || |_| ____( (___| | | | | |||
## (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
## (C)2013-2017 Semtech | |||
## ___ _____ _ ___ _ _____ ___ ___ ___ ___ | |||
## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| | |||
## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| | |||
## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| | |||
## embedded.connectivity.solutions.============== | |||
## | |||
## License: Revised BSD License, see LICENSE.TXT file included in the project | |||
## Authors: Johannes Bruder (STACKFORCE), Miguel Luis (Semtech) | |||
## | |||
project(tx-cw) | |||
cmake_minimum_required(VERSION 3.6) | |||
#--------------------------------------------------------------------------------------- | |||
# Options | |||
#--------------------------------------------------------------------------------------- | |||
# Allow selection of region | |||
option(REGION_EU868 "Region EU868" ON) | |||
option(REGION_US915 "Region US915" OFF) | |||
option(REGION_CN779 "Region CN779" OFF) | |||
option(REGION_EU433 "Region EU433" OFF) | |||
option(REGION_AU915 "Region AU915" OFF) | |||
option(REGION_AS923 "Region AS923" OFF) | |||
option(REGION_CN470 "Region CN470" OFF) | |||
option(REGION_KR920 "Region KR920" OFF) | |||
option(REGION_IN865 "Region IN865" OFF) | |||
option(REGION_RU864 "Region RU864" OFF) | |||
set(REGION_LIST REGION_EU868 REGION_US915 REGION_CN779 REGION_EU433 REGION_AU915 REGION_AS923 REGION_CN470 REGION_KR920 REGION_IN865 REGION_RU864) | |||
#--------------------------------------------------------------------------------------- | |||
# Target | |||
#--------------------------------------------------------------------------------------- | |||
file(GLOB ${PROJECT_NAME}_SOURCES "${CMAKE_CURRENT_LIST_DIR}/${BOARD}/*.c") | |||
add_executable(${PROJECT_NAME} | |||
${${PROJECT_NAME}_SOURCES} | |||
$<TARGET_OBJECTS:system> | |||
$<TARGET_OBJECTS:radio> | |||
$<TARGET_OBJECTS:peripherals> | |||
$<TARGET_OBJECTS:${BOARD}> | |||
) | |||
# Loops through all regions and add compile time definitions for the enabled ones. | |||
foreach( REGION ${REGION_LIST} ) | |||
if(${REGION}) | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC -D"${REGION}") | |||
endif() | |||
endforeach() | |||
target_compile_definitions(${PROJECT_NAME} PUBLIC | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:mac,INTERFACE_COMPILE_DEFINITIONS>> | |||
) | |||
target_include_directories(${PROJECT_NAME} PUBLIC | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:system,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:radio,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:peripherals,INTERFACE_INCLUDE_DIRECTORIES>> | |||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:${BOARD},INTERFACE_INCLUDE_DIRECTORIES>> | |||
) | |||
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) | |||
target_link_libraries(${PROJECT_NAME} m) | |||
#--------------------------------------------------------------------------------------- | |||
# Debugging and Binutils | |||
#--------------------------------------------------------------------------------------- | |||
include(gdb-helper) | |||
include(binutils-arm-none-eabi) | |||
# Generate debugger configurations | |||
generate_run_gdb_stlink(${PROJECT_NAME}) | |||
generate_run_gdb_openocd(${PROJECT_NAME}) | |||
generate_vscode_launch_openocd(${PROJECT_NAME}) | |||
# Print section sizes of target | |||
print_section_sizes(${PROJECT_NAME}) | |||
# Create output in hex and binary format | |||
create_bin_output(${PROJECT_NAME}) | |||
create_hex_output(${PROJECT_NAME}) |
@@ -0,0 +1,188 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Tx Continuous Wave implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board-config.h" | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_OUTPUT_POWER 20 // 20 dBm | |||
#define TX_TIMEOUT 65535 // seconds (MAX value) | |||
static TimerEvent_t Led1Timer; | |||
volatile bool Led1TimerEvent = false; | |||
static TimerEvent_t Led2Timer; | |||
volatile bool Led2TimerEvent = false; | |||
static TimerEvent_t Led3Timer; | |||
volatile bool Led3TimerEvent = false; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
extern Gpio_t Led3; | |||
/*! | |||
* \brief Function executed on Led 1 Timeout event | |||
*/ | |||
void OnLed1TimerEvent( void* context ) | |||
{ | |||
Led1TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Led 2 Timeout event | |||
*/ | |||
void OnLed2TimerEvent( void* context ) | |||
{ | |||
Led2TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Led 3 Timeout event | |||
*/ | |||
void OnLed3TimerEvent( void* context ) | |||
{ | |||
Led3TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnRadioTxTimeout( void ) | |||
{ | |||
// Restarts continuous wave transmission when timeout expires | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
} | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 90 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 90 ); | |||
TimerInit( &Led3Timer, OnLed3TimerEvent ); | |||
TimerSetValue( &Led3Timer, 90 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
// Radio initialization | |||
RadioEvents.TxTimeout = OnRadioTxTimeout; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
// Blink LEDs just to show some activity | |||
while( 1 ) | |||
{ | |||
if( Led1TimerEvent == true ) | |||
{ | |||
Led1TimerEvent = false; | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 1 ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
if( Led2TimerEvent == true ) | |||
{ | |||
Led2TimerEvent = false; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 1 ); | |||
// Switch LED 3 ON | |||
GpioWrite( &Led3, 0 ); | |||
TimerStart( &Led3Timer ); | |||
} | |||
if( Led3TimerEvent == true ) | |||
{ | |||
Led3TimerEvent = false; | |||
// Switch LED 3 OFF | |||
GpioWrite( &Led3, 1 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} |
@@ -0,0 +1,182 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Tx Continuous Wave implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#define TX_OUTPUT_POWER 20 // 20 dBm | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#define TX_OUTPUT_POWER 20 // 20 dBm | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_TIMEOUT 10 // seconds (MAX value) | |||
static TimerEvent_t Led1Timer; | |||
volatile bool Led1TimerEvent = false; | |||
static TimerEvent_t Led2Timer; | |||
volatile bool Led2TimerEvent = false; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
/*! | |||
* \brief Function executed on Led 1 Timeout event | |||
*/ | |||
void OnLed1TimerEvent( void* context ) | |||
{ | |||
Led1TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Led 2 Timeout event | |||
*/ | |||
void OnLed2TimerEvent( void* context ) | |||
{ | |||
Led2TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnRadioTxTimeout( void ) | |||
{ | |||
// Restarts continuous wave transmission when timeout expires | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
} | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 90 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 90 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
// Radio initialization | |||
RadioEvents.TxTimeout = OnRadioTxTimeout; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
// Blink LEDs just to show some activity | |||
while( 1 ) | |||
{ | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
if( Led1TimerEvent == true ) | |||
{ | |||
Led1TimerEvent = false; | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 1 ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
if( Led2TimerEvent == true ) | |||
{ | |||
Led2TimerEvent = false; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 1 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} |
@@ -0,0 +1,182 @@ | |||
/*! | |||
* \file main.c | |||
* | |||
* \brief Tx Continuous Wave implementation | |||
* | |||
* \copyright Revised BSD License, see section \ref LICENSE. | |||
* | |||
* \code | |||
* ______ _ | |||
* / _____) _ | | | |||
* ( (____ _____ ____ _| |_ _____ ____| |__ | |||
* \____ \| ___ | (_ _) ___ |/ ___) _ \ | |||
* _____) ) ____| | | || |_| ____( (___| | | | | |||
* (______/|_____)_|_|_| \__)_____)\____)_| |_| | |||
* (C)2013-2017 Semtech | |||
* | |||
* \endcode | |||
* | |||
* \author Miguel Luis ( Semtech ) | |||
* | |||
* \author Gregory Cristian ( Semtech ) | |||
*/ | |||
#include "board.h" | |||
#include "gpio.h" | |||
#include "timer.h" | |||
#include "radio.h" | |||
#if defined( REGION_AS923 ) | |||
#define RF_FREQUENCY 923000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_AU915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_CN470 ) | |||
#define RF_FREQUENCY 470000000 // Hz | |||
#define TX_OUTPUT_POWER 20 // 20 dBm | |||
#elif defined( REGION_CN779 ) | |||
#define RF_FREQUENCY 779000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_EU433 ) | |||
#define RF_FREQUENCY 433000000 // Hz | |||
#define TX_OUTPUT_POWER 20 // 20 dBm | |||
#elif defined( REGION_EU868 ) | |||
#define RF_FREQUENCY 868000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_KR920 ) | |||
#define RF_FREQUENCY 920000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_IN865 ) | |||
#define RF_FREQUENCY 865000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_US915 ) | |||
#define RF_FREQUENCY 915000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#elif defined( REGION_RU864 ) | |||
#define RF_FREQUENCY 864000000 // Hz | |||
#define TX_OUTPUT_POWER 14 // 14 dBm | |||
#else | |||
#error "Please define a frequency band in the compiler options." | |||
#endif | |||
#define TX_TIMEOUT 10 // seconds (MAX value) | |||
static TimerEvent_t Led1Timer; | |||
volatile bool Led1TimerEvent = false; | |||
static TimerEvent_t Led2Timer; | |||
volatile bool Led2TimerEvent = false; | |||
/*! | |||
* Radio events function pointer | |||
*/ | |||
static RadioEvents_t RadioEvents; | |||
/*! | |||
* LED GPIO pins objects | |||
*/ | |||
extern Gpio_t Led1; | |||
extern Gpio_t Led2; | |||
/*! | |||
* \brief Function executed on Led 1 Timeout event | |||
*/ | |||
void OnLed1TimerEvent( void* context ) | |||
{ | |||
Led1TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Led 2 Timeout event | |||
*/ | |||
void OnLed2TimerEvent( void* context ) | |||
{ | |||
Led2TimerEvent = true; | |||
} | |||
/*! | |||
* \brief Function executed on Radio Tx Timeout event | |||
*/ | |||
void OnRadioTxTimeout( void ) | |||
{ | |||
// Restarts continuous wave transmission when timeout expires | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
} | |||
/** | |||
* Main application entry point. | |||
*/ | |||
int main( void ) | |||
{ | |||
// Target board initialization | |||
BoardInitMcu( ); | |||
BoardInitPeriph( ); | |||
TimerInit( &Led1Timer, OnLed1TimerEvent ); | |||
TimerSetValue( &Led1Timer, 90 ); | |||
TimerInit( &Led2Timer, OnLed2TimerEvent ); | |||
TimerSetValue( &Led2Timer, 90 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
// Radio initialization | |||
RadioEvents.TxTimeout = OnRadioTxTimeout; | |||
Radio.Init( &RadioEvents ); | |||
Radio.SetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT ); | |||
// Blink LEDs just to show some activity | |||
while( 1 ) | |||
{ | |||
// Process Radio IRQ | |||
if( Radio.IrqProcess != NULL ) | |||
{ | |||
Radio.IrqProcess( ); | |||
} | |||
if( Led1TimerEvent == true ) | |||
{ | |||
Led1TimerEvent = false; | |||
// Switch LED 1 OFF | |||
GpioWrite( &Led1, 1 ); | |||
// Switch LED 2 ON | |||
GpioWrite( &Led2, 0 ); | |||
TimerStart( &Led2Timer ); | |||
} | |||
if( Led2TimerEvent == true ) | |||
{ | |||
Led2TimerEvent = false; | |||
// Switch LED 2 OFF | |||
GpioWrite( &Led2, 1 ); | |||
// Switch LED 1 ON | |||
GpioWrite( &Led1, 0 ); | |||
TimerStart( &Led1Timer ); | |||
} | |||
} | |||
} |