├── .gitignore ├── .gitmodules ├── API.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples ├── default_dev_eui │ ├── CMakeLists.txt │ └── main.c ├── hello_abp │ ├── CMakeLists.txt │ ├── config.h │ └── main.c ├── hello_otaa │ ├── CMakeLists.txt │ ├── config.h │ └── main.c └── otaa_temperature_led │ ├── CMakeLists.txt │ ├── config.h │ └── main.c ├── pico_sdk_import.cmake └── src ├── LICENSE ├── boards └── rp2040 │ ├── LICENSE │ ├── board.c │ ├── delay-board.c │ ├── eeprom-board.c │ ├── gpio-board.c │ ├── rtc-board.c │ ├── spi-board.c │ └── sx1276-board.c ├── include └── pico │ └── lorawan.h └── lorawan.c /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | 13 | build/ 14 | 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/LoRaMac-node"] 2 | path = lib/LoRaMac-node 3 | url = https://github.com/Lora-net/LoRaMac-node.git 4 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # pico-lorawan API 2 | 3 | ## Include Library 4 | 5 | ```c 6 | #include 7 | ``` 8 | 9 | ## Initialization 10 | 11 | ### SX1276 Settings 12 | 13 | ```c 14 | // pin configuration for SX1276 radio module 15 | struct lorawan_sx1276_settings sx1276_settings = { 16 | .spi = { 17 | .inst = PICO_DEFAULT_SPI_INSTANCE, // RP2040 SPI instance 18 | .mosi = PICO_DEFAULT_SPI_TX_PIN, // SPI MOSI GPIO 19 | .miso = PICO_DEFAULT_SPI_RX_PIN, // SPI MISO GPIO 20 | .sck = PICO_DEFAULT_SPI_SCK_PIN, // SPI SCK GPIO 21 | .nss = 8. // SPI NSS / CS GPIO 22 | }, 23 | .reset = 9, // SX1276 RESET GPIO 24 | .dio0 = 7, // SX1276 DIO0 / G0 GPIO 25 | .dio1 = 10 // SX1276 DIO0 / G1 GPIO 26 | }; 27 | ``` 28 | 29 | ### ABP 30 | 31 | Initialize the library for ABP. 32 | 33 | ```c 34 | // ABP settings 35 | const struct lorawan_abp_settings abp_settings = { 36 | // LoRaWAN device address (32-bit) 37 | .device_address = "00000000", 38 | 39 | // LoRaWAN Network Session Key (128-bit) 40 | .network_session_key = "00000000000000000000000000000000", 41 | 42 | // LoRaWAN Application Session Key (128-bit) 43 | .app_session_key = "00000000000000000000000000000000", 44 | 45 | // LoRaWAN Channel Mask, NULL value will use the default channel mask 46 | // for US915 with TTN use "FF0000000000000000020000" 47 | .channel_mask = NULL, 48 | }; 49 | 50 | int lorawan_init_abp(const struct lorawan_sx1276_settings* sx1276_settings, LoRaMacRegion_t region, const struct lorawan_abp_settings* abp_settings); 51 | ``` 52 | 53 | - `sx1276_settings` - pointer to settings for SX1276 SPI and GPIO pins 54 | - `region` - region to use, see [`enum LoRaMacRegion_t 55 | `](http://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.5.1/group___l_o_r_a_m_a_c.html#ga3b9d54f0355b51e85df8b33fd1757eec)for supported values] 56 | - `abp_settings` - pointer to LoRaWAN ABP settings 57 | 58 | Returns `0` on success, `-1` on error. 59 | 60 | ### OTAA 61 | 62 | Initialize the library for OTAA. 63 | 64 | ```c 65 | const struct lorawan_otaa_settings otaa_settings = { 66 | // LoRaWAN Device EUI (64-bit), NULL value will use Default Dev EUI 67 | .device_eui = "0000000000000000", 68 | 69 | // LoRaWAN Application / Join EUI (64-bit) 70 | .app_eui = "0000000000000000", 71 | 72 | // LoRaWAN Application Key (128-bit) 73 | .app_key = "00000000000000000000000000000000", 74 | 75 | // LoRaWAN Channel Mask, NULL value will use the default channel mask 76 | // for US915 with TTN use "FF0000000000000000020000" 77 | .channel_mask = NULL, 78 | }; 79 | 80 | int lorawan_init_otaa(const struct lorawan_sx1276_settings* sx1276_settings, LoRaMacRegion_t region, const struct lorawan_otaa_settings* otaa_settings); 81 | ``` 82 | 83 | - `sx1276_settings` - pointer to settings for SX1276 SPI and GPIO pins 84 | - `region` - region to use, see [`enum LoRaMacRegion_t 85 | `](http://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.5.1/group___l_o_r_a_m_a_c.html#ga3b9d54f0355b51e85df8b33fd1757eec)for supported values] 86 | - `otaa_settings` - pointer to LoRaWAN OTAA settings 87 | 88 | Returns `0` on success, `-1` on error. 89 | 90 | 91 | ## Joining 92 | 93 | ### Start Join 94 | 95 | Start the LoRaWAN network join process. 96 | 97 | ```c 98 | int lorawan_join(); 99 | ``` 100 | 101 | Returns `0` on success, `-1` on error. 102 | 103 | ### Join Status 104 | 105 | Query the LoRaWAN network join status. 106 | 107 | ```c 108 | int lorawan_is_joined(); 109 | ``` 110 | 111 | Returns `1` if the board has successfully joined the LoRaWAN network, `0` otherwise. 112 | 113 | ## Processing Pending Events 114 | 115 | ### Without Timeout 116 | 117 | Let the LoRaWAN library process pending events. 118 | 119 | ```c 120 | int lorawan_process(); 121 | ``` 122 | 123 | Returns `0` if there is a pending event, `1` if there are no pending events and the calling application can go into low power sleep mode. 124 | 125 | ### With Timeout 126 | 127 | Let the lorwan library process pending events for up to `n` milliseconds. 128 | 129 | ```c 130 | int lorawan_process_timeout_ms(uint32_t timeout_ms); 131 | ``` 132 | 133 | - `timeout_ms` in milliseconds to wait for LoRaWAN event. 134 | 135 | Returns `0` on event, `1` on timeout. 136 | 137 | 138 | ## Sending Uplink Messages 139 | 140 | ### Unconfirmed 141 | 142 | Send an unconfirmed uplink message. 143 | 144 | ```c 145 | int lorawan_send_unconfirmed(const void* data, uint8_t data_len, uint8_t app_port); 146 | ``` 147 | 148 | - `data` - message data buffer to send 149 | - `data_len` - size of message in bytes 150 | - `app_port` - application port to use for message 151 | 152 | Returns `0` on success, `-1` on failure. 153 | 154 | ## Receiving Downlink Messages 155 | 156 | ```c 157 | int lorawan_receive(void* data, uint8_t data_len, uint8_t* app_port); 158 | ``` 159 | 160 | - `data` - message data buffer to store received data 161 | - `data_len` - size of message data buffer in bytes 162 | - `app_port` - pointer to store application port of received message 163 | 164 | Returns length of received message on success, `-1` on failure. 165 | 166 | ## Other 167 | 168 | ### Default Dev EUI 169 | 170 | Read the board's default Dev EUI Dev EUI which is based on the Pico SDK's [pico_get_unique_board_id(...)](https://raspberrypi.github.io/pico-sdk-doxygen/group__pico__unique__id.html) API which uses the on board NOR flash device 64-bit unique ID. 171 | 172 | ```c 173 | const char* lorawan_default_dev_eui(char* dev_eui); 174 | ``` 175 | 176 | - `dev_eui` - 17 byte buffer to store default Dev EUI as c-string 177 | 178 | Returns `dev_eui` argument. 179 | 180 | ### Debugging Ouput 181 | 182 | Enable or disable debug output from the library. 183 | 184 | ```c 185 | void lorawan_debug(bool debug); 186 | ``` 187 | 188 | - `debug` - `true` to enable debug output, `false` to disable debug output 189 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # initialize pico_sdk from GIT 4 | # (note this can come from environment, CMake cache etc) 5 | # set(PICO_SDK_FETCH_FROM_GIT on) 6 | 7 | # pico_sdk_import.cmake is a single file copied from this SDK 8 | # note: this must happen before project() 9 | include(pico_sdk_import.cmake) 10 | 11 | project(pico_lorwan) 12 | 13 | # initialize the Pico SDK 14 | pico_sdk_init() 15 | 16 | set(LORAMAC_NODE_PATH ${CMAKE_CURRENT_LIST_DIR}/lib/LoRaMac-node) 17 | 18 | add_library(pico_loramac_node INTERFACE) 19 | 20 | target_sources(pico_loramac_node INTERFACE 21 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/CayenneLpp.c 22 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/cli.c 23 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandlerMsgDisplay.c 24 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/NvmDataMgmt.c 25 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler/LmHandler.c 26 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler/packages/FragDecoder.c 27 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler/packages/LmhpClockSync.c 28 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler/packages/LmhpCompliance.c 29 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler/packages/LmhpFragmentation.c 30 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler/packages/LmhpRemoteMcastSetup.c 31 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/NvmDataMgmt.c 32 | 33 | ${LORAMAC_NODE_PATH}/src/boards/mcu/utilities.c 34 | 35 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/NvmDataMgmt.c 36 | 37 | ${LORAMAC_NODE_PATH}/src/mac/region/Region.c 38 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionAS923.c 39 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionAU915.c 40 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionBaseUS.c 41 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionCN470.c 42 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionCN470A20.c 43 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionCN470A26.c 44 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionCN470B20.c 45 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionCN470B26.c 46 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionCN779.c 47 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionCommon.c 48 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionEU433.c 49 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionEU868.c 50 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionIN865.c 51 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionKR920.c 52 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionRU864.c 53 | ${LORAMAC_NODE_PATH}/src/mac/region/RegionUS915.c 54 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMac.c 55 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMacAdr.c 56 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMacClassB.c 57 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMacCommands.c 58 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMacConfirmQueue.c 59 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMacCrypto.c 60 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMacParser.c 61 | ${LORAMAC_NODE_PATH}/src/mac/LoRaMacSerializer.c 62 | 63 | ${LORAMAC_NODE_PATH}/src/peripherals/soft-se/aes.c 64 | ${LORAMAC_NODE_PATH}/src/peripherals/soft-se/cmac.c 65 | ${LORAMAC_NODE_PATH}/src/peripherals/soft-se/soft-se-hal.c 66 | ${LORAMAC_NODE_PATH}/src/peripherals/soft-se/soft-se.c 67 | 68 | ${LORAMAC_NODE_PATH}/src/radio/sx1276/sx1276.c 69 | 70 | ${LORAMAC_NODE_PATH}/src/system/delay.c 71 | ${LORAMAC_NODE_PATH}/src/system/gpio.c 72 | ${LORAMAC_NODE_PATH}/src/system/nvmm.c 73 | ${LORAMAC_NODE_PATH}/src/system/systime.c 74 | ${LORAMAC_NODE_PATH}/src/system/timer.c 75 | 76 | ${CMAKE_CURRENT_LIST_DIR}/src/boards/rp2040/board.c 77 | ${CMAKE_CURRENT_LIST_DIR}/src/boards/rp2040/delay-board.c 78 | ${CMAKE_CURRENT_LIST_DIR}/src/boards/rp2040/eeprom-board.c 79 | ${CMAKE_CURRENT_LIST_DIR}/src/boards/rp2040/gpio-board.c 80 | ${CMAKE_CURRENT_LIST_DIR}/src/boards/rp2040/rtc-board.c 81 | ${CMAKE_CURRENT_LIST_DIR}/src/boards/rp2040/spi-board.c 82 | ${CMAKE_CURRENT_LIST_DIR}/src/boards/rp2040/sx1276-board.c 83 | ) 84 | 85 | target_include_directories(pico_loramac_node INTERFACE 86 | ${LORAMAC_NODE_PATH}/src 87 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common 88 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler 89 | ${LORAMAC_NODE_PATH}/src/apps/LoRaMac/common/LmHandler/packages 90 | ${LORAMAC_NODE_PATH}/src/boards 91 | ${LORAMAC_NODE_PATH}/src/mac 92 | ${LORAMAC_NODE_PATH}/src/mac/region 93 | ${LORAMAC_NODE_PATH}/src/peripherals/soft-se 94 | ${LORAMAC_NODE_PATH}/src/radio 95 | ${LORAMAC_NODE_PATH}/src/system 96 | ) 97 | 98 | target_link_libraries(pico_loramac_node INTERFACE pico_stdlib pico_unique_id hardware_spi) 99 | 100 | target_compile_definitions(pico_loramac_node INTERFACE -DSOFT_SE) 101 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_EU868) 102 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_US915) 103 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_CN779) 104 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_EU433) 105 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_AU915) 106 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_AS923) 107 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_CN470) 108 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_KR920) 109 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_IN865) 110 | target_compile_definitions(pico_loramac_node INTERFACE -DREGION_RU864) 111 | target_compile_definitions(pico_loramac_node INTERFACE -DACTIVE_REGION=LORAMAC_REGION_US915) 112 | 113 | add_library(pico_lorawan INTERFACE) 114 | 115 | target_sources(pico_lorawan INTERFACE 116 | ${CMAKE_CURRENT_LIST_DIR}/src/lorawan.c 117 | ) 118 | 119 | target_include_directories(pico_lorawan INTERFACE 120 | ${CMAKE_CURRENT_LIST_DIR}/src/include 121 | ) 122 | 123 | target_link_libraries(pico_lorawan INTERFACE pico_loramac_node) 124 | 125 | add_subdirectory("examples/default_dev_eui") 126 | add_subdirectory("examples/hello_abp") 127 | add_subdirectory("examples/hello_otaa") 128 | add_subdirectory("examples/otaa_temperature_led") 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Arm Limited 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pico-lorawan 2 | Enable LoRaWAN communications on your [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/) or any RP2040 based board using a [Semtech SX1276 radio module](https://www.semtech.com/apps/product.php?pn=SX1276). 3 | 4 | Based on the Semtech's [LoRaWAN end-device stack implementation and example projects](https://github.com/Lora-net/LoRaMac-node). 5 | 6 | ## Hardware 7 | 8 | * RP2040 board 9 | * [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/) 10 | * [Adafruit Feather RP2040](https://www.adafruit.com/product/4884) 11 | * Semtech SX1276 board 12 | * [Adafruit RFM95W LoRa Radio Transceiver Breakout - 868 or 915 MHz - RadioFruit](https://www.adafruit.com/product/3072) 13 | * [Adafruit LoRa Radio FeatherWing - RFM95W 900 MHz - RadioFruit](https://www.adafruit.com/product/3231) 14 | 15 | ### Default Pinout 16 | 17 | | Raspberry Pi Pico / RP2040 | Semtech SX1276 | 18 | | ----------------- | -------------- | 19 | | 3.3V | VCC | 20 | | GND | GND | 21 | | GPIO 18 | SCK | 22 | | GPIO 19 | MOSI | 23 | | GPIO 16 | MISO | 24 | | GPIO 7 | DIO0 / G0 | 25 | | GPIO 8 | NSS / CS | 26 | | GPIO 9 | RESET | 27 | | GPIO 10 | DIO1 / G1 | 28 | 29 | GPIO pins are configurable in examples or API. 30 | 31 | ## Examples 32 | 33 | See [examples](examples/) folder. 34 | 35 | There is a `config.h` file to your ABP or OTAA node configuration for each example. 36 | 37 | ## Cloning 38 | 39 | ```sh 40 | git clone --recurse-submodules https://github.com/sandeepmistry/pico-lorawan.git 41 | ``` 42 | 43 | ## Building 44 | 45 | 1. [Set up the Pico C/C++ SDK](https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf) 46 | 2. Set `PICO_SDK_PATH` 47 | ```sh 48 | export PICO_SDK_PATH=/path/to/pico-sdk 49 | ``` 50 | 3. Create `build` dir, run `cmake` and `make`: 51 | ``` 52 | mkdir build 53 | cd build 54 | cmake .. -DPICO_BOARD=pico 55 | make 56 | ``` 57 | 4. Copy example `.uf2` to Pico when in BOOT mode. 58 | 59 | ## Acknowledgements 60 | 61 | A big thanks to [Alasdair Allan](https://github.com/aallan) for his initial testing of EU868 support! 62 | 63 | This project was created on behalf of the [Arm Software Developers](https://developer.arm.com/) team, follow them on Twitter: [@ArmSoftwareDev](https://twitter.com/armsoftwaredev) and YouTube: [Arm Software Developers](https://www.youtube.com/channel/UCHUAckhCfRom2EHDGxwhfOg) for more resources! 64 | 65 | -------------------------------------------------------------------------------- /examples/default_dev_eui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # rest of your project 4 | add_executable(pico_lorawan_default_dev_eui 5 | main.c 6 | ) 7 | 8 | target_link_libraries(pico_lorawan_default_dev_eui pico_lorawan) 9 | 10 | # enable usb output, disable uart output 11 | pico_enable_stdio_usb(pico_lorawan_default_dev_eui 1) 12 | pico_enable_stdio_uart(pico_lorawan_default_dev_eui 0) 13 | 14 | # create map/bin/hex/uf2 file in addition to ELF. 15 | pico_add_extra_outputs(pico_lorawan_default_dev_eui) 16 | -------------------------------------------------------------------------------- /examples/default_dev_eui/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | * 7 | * This example displays devices default LoRaWAN Dev EUI which is based 8 | * on the Pico SDK's pico_get_unique_board_id(...) API which uses the 9 | * on board NOR flash device 64-bit unique ID. 10 | * 11 | * https://raspberrypi.github.io/pico-sdk-doxygen/group__pico__unique__id.html 12 | * 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | #include "pico/stdlib.h" 19 | #include "pico/lorawan.h" 20 | #include "tusb.h" 21 | 22 | int main(void) 23 | { 24 | char devEui[17]; 25 | 26 | // initialize stdio and wait for USB CDC connect 27 | stdio_init_all(); 28 | 29 | while (!tud_cdc_connected()) { 30 | tight_loop_contents(); 31 | } 32 | 33 | // get the default Dev EUI as a string and print it out 34 | printf("Pico LoRaWAN - Default Dev EUI = %s\n", lorawan_default_dev_eui(devEui)); 35 | 36 | // do nothing 37 | while (1) { 38 | tight_loop_contents(); 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /examples/hello_abp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # rest of your project 4 | add_executable(pico_lorawan_hello_abp 5 | main.c 6 | ) 7 | 8 | target_link_libraries(pico_lorawan_hello_abp pico_lorawan) 9 | 10 | # enable usb output, disable uart output 11 | pico_enable_stdio_usb(pico_lorawan_hello_abp 1) 12 | pico_enable_stdio_uart(pico_lorawan_hello_abp 0) 13 | 14 | # create map/bin/hex/uf2 file in addition to ELF. 15 | pico_add_extra_outputs(pico_lorawan_hello_abp) 16 | -------------------------------------------------------------------------------- /examples/hello_abp/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | // LoRaWAN region to use, full list of regions can be found at: 9 | // http://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.5.1/group___l_o_r_a_m_a_c.html#ga3b9d54f0355b51e85df8b33fd1757eec 10 | #define LORAWAN_REGION LORAMAC_REGION_US915 11 | 12 | // LoRaWAN device address (32-bit) 13 | #define LORAWAN_DEV_ADDR "00000000" 14 | 15 | // LoRaWAN Network Session Key (128-bit) 16 | #define LORAWAN_NETWORK_SESSION_KEY "00000000000000000000000000000000" 17 | 18 | // LoRaWAN Application Session Key (128-bit) 19 | #define LORAWAN_APP_SESSION_KEY "00000000000000000000000000000000" 20 | 21 | // LoRaWAN Channel Mask, NULL value will use the default channel mask 22 | // for the region 23 | #define LORAWAN_CHANNEL_MASK NULL 24 | -------------------------------------------------------------------------------- /examples/hello_abp/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | * 7 | * This example uses ABP to join the LoRaWAN network and then sends a 8 | * "hello world" uplink message periodically and prints out the 9 | * contents of any downlink message. 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "pico/stdlib.h" 16 | #include "pico/lorawan.h" 17 | #include "tusb.h" 18 | 19 | // edit with LoRaWAN Node Region and ABP settings 20 | #include "config.h" 21 | 22 | // pin configuration for SX1276 radio module 23 | const struct lorawan_sx1276_settings sx1276_settings = { 24 | .spi = { 25 | .inst = PICO_DEFAULT_SPI_INSTANCE, 26 | .mosi = PICO_DEFAULT_SPI_TX_PIN, 27 | .miso = PICO_DEFAULT_SPI_RX_PIN, 28 | .sck = PICO_DEFAULT_SPI_SCK_PIN, 29 | .nss = 8 30 | }, 31 | .reset = 9, 32 | .dio0 = 7, 33 | .dio1 = 10 34 | }; 35 | 36 | // ABP settings 37 | const struct lorawan_abp_settings abp_settings = { 38 | .device_address = LORAWAN_DEV_ADDR, 39 | .network_session_key = LORAWAN_NETWORK_SESSION_KEY, 40 | .app_session_key = LORAWAN_APP_SESSION_KEY, 41 | .channel_mask = LORAWAN_CHANNEL_MASK 42 | }; 43 | 44 | // variables for receiving data 45 | int receive_length = 0; 46 | uint8_t receive_buffer[242]; 47 | uint8_t receive_port = 0; 48 | 49 | int main( void ) 50 | { 51 | // initialize stdio and wait for USB CDC connect 52 | stdio_init_all(); 53 | 54 | while (!tud_cdc_connected()) { 55 | tight_loop_contents(); 56 | } 57 | 58 | printf("Pico LoRaWAN - Hello ABP\n\n"); 59 | 60 | // uncomment next line to enable debug 61 | // lorawan_debug(true); 62 | 63 | // initialize the LoRaWAN stack 64 | printf("Initilizating LoRaWAN ... "); 65 | if (lorawan_init_abp(&sx1276_settings, LORAWAN_REGION, &abp_settings) < 0) { 66 | printf("failed!!!\n"); 67 | while (1) { 68 | tight_loop_contents(); 69 | } 70 | } else { 71 | printf("success!\n"); 72 | } 73 | 74 | // Start the join process and wait 75 | printf("Joining LoRaWAN network ... "); 76 | lorawan_join(); 77 | 78 | while (!lorawan_is_joined()) { 79 | lorawan_process(); 80 | } 81 | printf("joined successfully!\n"); 82 | 83 | uint32_t last_message_time = 0; 84 | 85 | // loop forever 86 | while (1) { 87 | // let the lorwan library process pending events 88 | lorawan_process(); 89 | 90 | // get the current time and see if 5 seconds have passed 91 | // since the last message was sent 92 | uint32_t now = to_ms_since_boot(get_absolute_time()); 93 | 94 | if ((now - last_message_time) > 5000) { 95 | const char* message = "hello world!"; 96 | 97 | // try to send an unconfirmed uplink message 98 | printf("sending unconfirmed message '%s' ... ", message); 99 | if (lorawan_send_unconfirmed(message, strlen(message), 2) < 0) { 100 | printf("failed!!!\n"); 101 | } else { 102 | printf("success!\n"); 103 | } 104 | 105 | last_message_time = now; 106 | } 107 | 108 | // check if a downlink message was received 109 | receive_length = lorawan_receive(receive_buffer, sizeof(receive_buffer), &receive_port); 110 | if (receive_length > -1) { 111 | printf("received a %d byte message on port %d: ", receive_length, receive_port); 112 | 113 | for (int i = 0; i < receive_length; i++) { 114 | printf("%02x", receive_buffer[i]); 115 | } 116 | printf("\n"); 117 | } 118 | } 119 | 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /examples/hello_otaa/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # rest of your project 4 | add_executable(pico_lorawan_hello_otaa 5 | main.c 6 | ) 7 | 8 | target_link_libraries(pico_lorawan_hello_otaa pico_lorawan) 9 | 10 | # enable usb output, disable uart output 11 | pico_enable_stdio_usb(pico_lorawan_hello_otaa 1) 12 | pico_enable_stdio_uart(pico_lorawan_hello_otaa 0) 13 | 14 | # create map/bin/hex/uf2 file in addition to ELF. 15 | pico_add_extra_outputs(pico_lorawan_hello_otaa) 16 | -------------------------------------------------------------------------------- /examples/hello_otaa/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | // LoRaWAN region to use, full list of regions can be found at: 9 | // http://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.5.1/group___l_o_r_a_m_a_c.html#ga3b9d54f0355b51e85df8b33fd1757eec 10 | #define LORAWAN_REGION LORAMAC_REGION_US915 11 | 12 | // LoRaWAN Device EUI (64-bit), NULL value will use Default Dev EUI 13 | #define LORAWAN_DEVICE_EUI "0000000000000000" 14 | 15 | // LoRaWAN Application / Join EUI (64-bit) 16 | #define LORAWAN_APP_EUI "0000000000000000" 17 | 18 | // LoRaWAN Application Key (128-bit) 19 | #define LORAWAN_APP_KEY "00000000000000000000000000000000" 20 | 21 | // LoRaWAN Channel Mask, NULL value will use the default channel mask 22 | // for the region 23 | #define LORAWAN_CHANNEL_MASK NULL 24 | -------------------------------------------------------------------------------- /examples/hello_otaa/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | * This example uses OTAA to join the LoRaWAN network and then sends a 7 | * "hello world" uplink message periodically and prints out the 8 | * contents of any downlink message. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include "pico/stdlib.h" 15 | #include "pico/lorawan.h" 16 | #include "tusb.h" 17 | 18 | // edit with LoRaWAN Node Region and OTAA settings 19 | #include "config.h" 20 | 21 | // pin configuration for SX1276 radio module 22 | const struct lorawan_sx1276_settings sx1276_settings = { 23 | .spi = { 24 | .inst = PICO_DEFAULT_SPI_INSTANCE, 25 | .mosi = PICO_DEFAULT_SPI_TX_PIN, 26 | .miso = PICO_DEFAULT_SPI_RX_PIN, 27 | .sck = PICO_DEFAULT_SPI_SCK_PIN, 28 | .nss = 8 29 | }, 30 | .reset = 9, 31 | .dio0 = 7, 32 | .dio1 = 10 33 | }; 34 | 35 | // OTAA settings 36 | const struct lorawan_otaa_settings otaa_settings = { 37 | .device_eui = LORAWAN_DEVICE_EUI, 38 | .app_eui = LORAWAN_APP_EUI, 39 | .app_key = LORAWAN_APP_KEY, 40 | .channel_mask = LORAWAN_CHANNEL_MASK 41 | }; 42 | 43 | // variables for receiving data 44 | int receive_length = 0; 45 | uint8_t receive_buffer[242]; 46 | uint8_t receive_port = 0; 47 | 48 | int main( void ) 49 | { 50 | // initialize stdio and wait for USB CDC connect 51 | stdio_init_all(); 52 | 53 | while (!tud_cdc_connected()) { 54 | tight_loop_contents(); 55 | } 56 | 57 | printf("Pico LoRaWAN - Hello OTAA\n\n"); 58 | 59 | // uncomment next line to enable debug 60 | // lorawan_debug(true); 61 | 62 | // initialize the LoRaWAN stack 63 | printf("Initilizating LoRaWAN ... "); 64 | if (lorawan_init_otaa(&sx1276_settings, LORAWAN_REGION, &otaa_settings) < 0) { 65 | printf("failed!!!\n"); 66 | while (1) { 67 | tight_loop_contents(); 68 | } 69 | } else { 70 | printf("success!\n"); 71 | } 72 | 73 | // Start the join process and wait 74 | printf("Joining LoRaWAN network ... "); 75 | lorawan_join(); 76 | 77 | while (!lorawan_is_joined()) { 78 | lorawan_process(); 79 | } 80 | printf("joined successfully!\n"); 81 | 82 | uint32_t last_message_time = 0; 83 | 84 | // loop forever 85 | while (1) { 86 | // let the lorwan library process pending events 87 | lorawan_process(); 88 | 89 | // get the current time and see if 5 seconds have passed 90 | // since the last message was sent 91 | uint32_t now = to_ms_since_boot(get_absolute_time()); 92 | 93 | if ((now - last_message_time) > 5000) { 94 | const char* message = "hello world!"; 95 | 96 | // try to send an unconfirmed uplink message 97 | printf("sending unconfirmed message '%s' ... ", message); 98 | if (lorawan_send_unconfirmed(message, strlen(message), 2) < 0) { 99 | printf("failed!!!\n"); 100 | } else { 101 | printf("success!\n"); 102 | } 103 | 104 | last_message_time = now; 105 | } 106 | 107 | // check if a downlink message was received 108 | receive_length = lorawan_receive(receive_buffer, sizeof(receive_buffer), &receive_port); 109 | if (receive_length > -1) { 110 | printf("received a %d byte message on port %d: ", receive_length, receive_port); 111 | 112 | for (int i = 0; i < receive_length; i++) { 113 | printf("%02x", receive_buffer[i]); 114 | } 115 | printf("\n"); 116 | } 117 | } 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /examples/otaa_temperature_led/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # rest of your project 4 | add_executable(pico_lorawan_otaa_temperature_led 5 | main.c 6 | ) 7 | 8 | target_link_libraries(pico_lorawan_otaa_temperature_led pico_lorawan hardware_adc) 9 | 10 | # enable usb output, disable uart output 11 | pico_enable_stdio_usb(pico_lorawan_otaa_temperature_led 1) 12 | pico_enable_stdio_uart(pico_lorawan_otaa_temperature_led 0) 13 | 14 | # create map/bin/hex/uf2 file in addition to ELF. 15 | pico_add_extra_outputs(pico_lorawan_otaa_temperature_led) 16 | -------------------------------------------------------------------------------- /examples/otaa_temperature_led/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | // LoRaWAN region to use, full list of regions can be found at: 9 | // http://stackforce.github.io/LoRaMac-doc/LoRaMac-doc-v4.5.1/group___l_o_r_a_m_a_c.html#ga3b9d54f0355b51e85df8b33fd1757eec 10 | #define LORAWAN_REGION LORAMAC_REGION_US915 11 | 12 | // LoRaWAN Device EUI (64-bit), NULL value will use Default Dev EUI 13 | #define LORAWAN_DEVICE_EUI "0000000000000000" 14 | 15 | // LoRaWAN Application / Join EUI (64-bit) 16 | #define LORAWAN_APP_EUI "0000000000000000" 17 | 18 | // LoRaWAN Application Key (128-bit) 19 | #define LORAWAN_APP_KEY "00000000000000000000000000000000" 20 | 21 | // LoRaWAN Channel Mask, NULL value will use the default channel mask 22 | // for the region 23 | #define LORAWAN_CHANNEL_MASK NULL 24 | -------------------------------------------------------------------------------- /examples/otaa_temperature_led/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | * This example uses OTAA to join the LoRaWAN network and then sends the 7 | * internal temperature sensors value up as an uplink message periodically 8 | * and the first byte of any uplink messages received controls the boards 9 | * built-in LED. 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "hardware/adc.h" 16 | #include "hardware/gpio.h" 17 | 18 | #include "pico/stdlib.h" 19 | #include "pico/lorawan.h" 20 | #include "tusb.h" 21 | 22 | // edit with LoRaWAN Node Region and OTAA settings 23 | #include "config.h" 24 | 25 | // pin configuration for SX1276 radio module 26 | const struct lorawan_sx1276_settings sx1276_settings = { 27 | .spi = { 28 | .inst = PICO_DEFAULT_SPI_INSTANCE, 29 | .mosi = PICO_DEFAULT_SPI_TX_PIN, 30 | .miso = PICO_DEFAULT_SPI_RX_PIN, 31 | .sck = PICO_DEFAULT_SPI_SCK_PIN, 32 | .nss = 8 33 | }, 34 | .reset = 9, 35 | .dio0 = 7, 36 | .dio1 = 10 37 | }; 38 | 39 | // OTAA settings 40 | const struct lorawan_otaa_settings otaa_settings = { 41 | .device_eui = LORAWAN_DEVICE_EUI, 42 | .app_eui = LORAWAN_APP_EUI, 43 | .app_key = LORAWAN_APP_KEY, 44 | .channel_mask = LORAWAN_CHANNEL_MASK 45 | }; 46 | 47 | // variables for receiving data 48 | int receive_length = 0; 49 | uint8_t receive_buffer[242]; 50 | uint8_t receive_port = 0; 51 | 52 | // functions used in main 53 | void internal_temperature_init(); 54 | float internal_temperature_get(); 55 | 56 | int main( void ) 57 | { 58 | // initialize stdio and wait for USB CDC connect 59 | stdio_init_all(); 60 | while (!tud_cdc_connected()) { 61 | tight_loop_contents(); 62 | } 63 | 64 | printf("Pico LoRaWAN - OTAA - Temperature + LED\n\n"); 65 | 66 | // initialize the LED pin and internal temperature ADC 67 | gpio_init(PICO_DEFAULT_LED_PIN); 68 | gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 69 | 70 | internal_temperature_init(); 71 | 72 | // uncomment next line to enable debug 73 | // lorawan_debug(true); 74 | 75 | // initialize the LoRaWAN stack 76 | printf("Initilizating LoRaWAN ... "); 77 | if (lorawan_init_otaa(&sx1276_settings, LORAWAN_REGION, &otaa_settings) < 0) { 78 | printf("failed!!!\n"); 79 | while (1) { 80 | tight_loop_contents(); 81 | } 82 | } else { 83 | printf("success!\n"); 84 | } 85 | 86 | // Start the join process and wait 87 | printf("Joining LoRaWAN network ..."); 88 | lorawan_join(); 89 | 90 | while (!lorawan_is_joined()) { 91 | lorawan_process_timeout_ms(1000); 92 | printf("."); 93 | } 94 | printf(" joined successfully!\n"); 95 | 96 | // loop forever 97 | while (1) { 98 | // get the internal temperature 99 | int8_t adc_temperature_byte = internal_temperature_get(); 100 | 101 | // send the internal temperature as a (signed) byte in an unconfirmed uplink message 102 | printf("sending internal temperature: %d °C (0x%02x)... ", adc_temperature_byte, adc_temperature_byte); 103 | if (lorawan_send_unconfirmed(&adc_temperature_byte, sizeof(adc_temperature_byte), 2) < 0) { 104 | printf("failed!!!\n"); 105 | } else { 106 | printf("success!\n"); 107 | } 108 | 109 | // wait for up to 30 seconds for an event 110 | if (lorawan_process_timeout_ms(30000) == 0) { 111 | // check if a downlink message was received 112 | receive_length = lorawan_receive(receive_buffer, sizeof(receive_buffer), &receive_port); 113 | if (receive_length > -1) { 114 | printf("received a %d byte message on port %d: ", receive_length, receive_port); 115 | 116 | for (int i = 0; i < receive_length; i++) { 117 | printf("%02x", receive_buffer[i]); 118 | } 119 | printf("\n"); 120 | 121 | // the first byte of the received message controls the on board LED 122 | gpio_put(PICO_DEFAULT_LED_PIN, receive_buffer[0]); 123 | } 124 | } 125 | } 126 | 127 | return 0; 128 | } 129 | 130 | void internal_temperature_init() 131 | { 132 | adc_init(); 133 | adc_set_temp_sensor_enabled(true); 134 | adc_select_input(4); 135 | } 136 | 137 | float internal_temperature_get() 138 | { 139 | const float v_ref = 3.3; 140 | 141 | // select and read the ADC 142 | adc_select_input(4); 143 | uint16_t adc_raw = adc_read(); 144 | 145 | // convert the raw ADC value to a voltage 146 | float adc_voltage = adc_raw * v_ref / 4095.0f; 147 | 148 | // convert voltage to temperature, using the formula from 149 | // section 4.9.4 in the RP2040 datasheet 150 | // https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf 151 | float adc_temperature = 27.0 - ((adc_voltage - 0.706) / 0.001721); 152 | 153 | return adc_temperature; 154 | } 155 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | Revised BSD License 2 | Copyright Semtech Corporation 2013. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Semtech corporation nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE LIABLE FOR ANY DIRECT, 19 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/boards/rp2040/LICENSE: -------------------------------------------------------------------------------- 1 | Revised BSD License 2 | Copyright Semtech Corporation 2013. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Semtech corporation nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE LIABLE FOR ANY DIRECT, 19 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/boards/rp2040/board.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "pico.h" 12 | #include "pico/unique_id.h" 13 | #include "hardware/sync.h" 14 | 15 | #include "board.h" 16 | 17 | void BoardInitMcu( void ) 18 | { 19 | } 20 | 21 | void BoardInitPeriph( void ) 22 | { 23 | } 24 | 25 | void BoardLowPowerHandler( void ) 26 | { 27 | __wfi(); 28 | } 29 | 30 | uint8_t BoardGetBatteryLevel( void ) 31 | { 32 | return 0; 33 | } 34 | 35 | uint32_t BoardGetRandomSeed( void ) 36 | { 37 | uint8_t id[8]; 38 | 39 | BoardGetUniqueId(id); 40 | 41 | return (id[3] << 24) | (id[2] << 16) | (id[1] << 1) | id[0]; 42 | } 43 | 44 | void BoardGetUniqueId( uint8_t *id ) 45 | { 46 | pico_unique_board_id_t board_id; 47 | 48 | pico_get_unique_board_id(&board_id); 49 | 50 | memcpy(id, board_id.id, 8); 51 | } 52 | 53 | void BoardCriticalSectionBegin( uint32_t *mask ) 54 | { 55 | *mask = save_and_disable_interrupts(); 56 | } 57 | 58 | void BoardCriticalSectionEnd( uint32_t *mask ) 59 | { 60 | restore_interrupts(*mask); 61 | } 62 | 63 | void BoardResetMcu( void ) 64 | { 65 | } 66 | -------------------------------------------------------------------------------- /src/boards/rp2040/delay-board.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | #include "hardware/timer.h" 9 | 10 | #include "delay-board.h" 11 | 12 | void DelayMsMcu( uint32_t ms ) 13 | { 14 | busy_wait_us_32(ms * 1000); 15 | } 16 | -------------------------------------------------------------------------------- /src/boards/rp2040/eeprom-board.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | #include "utilities.h" 9 | #include "eeprom-board.h" 10 | 11 | uint8_t EepromMcuReadBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ) 12 | { 13 | return FAIL; 14 | } 15 | 16 | uint8_t EepromMcuWriteBuffer( uint16_t addr, uint8_t *buffer, uint16_t size ) 17 | { 18 | return FAIL; 19 | } 20 | -------------------------------------------------------------------------------- /src/boards/rp2040/gpio-board.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | #include "hardware/gpio.h" 9 | 10 | #include "gpio-board.h" 11 | 12 | void GpioMcuInit( Gpio_t *obj, PinNames pin, PinModes mode, PinConfigs config, PinTypes type, uint32_t value ) 13 | { 14 | obj->pin = pin; 15 | 16 | if( pin == NC ) 17 | { 18 | return; 19 | } 20 | 21 | gpio_init(pin); 22 | 23 | if (mode == PIN_INPUT) 24 | { 25 | gpio_set_dir(pin, GPIO_IN); 26 | } 27 | else if (mode == PIN_OUTPUT) 28 | { 29 | gpio_set_dir(pin, GPIO_OUT); 30 | } 31 | 32 | if (config == PIN_PUSH_PULL) 33 | { 34 | if (type == PIN_NO_PULL) 35 | { 36 | gpio_disable_pulls(pin); 37 | } 38 | else if (type == PIN_PULL_UP) 39 | { 40 | gpio_pull_up(pin); 41 | } 42 | else if (type == PIN_PULL_DOWN) 43 | { 44 | gpio_pull_down(pin); 45 | } 46 | } 47 | 48 | if( mode == PIN_OUTPUT ) 49 | { 50 | GpioMcuWrite( obj, value ); 51 | } 52 | } 53 | 54 | void GpioMcuWrite( Gpio_t *obj, uint32_t value ) 55 | { 56 | gpio_put(obj->pin, value); 57 | } 58 | 59 | uint32_t GpioMcuRead( Gpio_t *obj ) 60 | { 61 | return gpio_get(obj->pin); 62 | } 63 | -------------------------------------------------------------------------------- /src/boards/rp2040/rtc-board.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | #include "pico/time.h" 9 | #include "pico/stdlib.h" 10 | 11 | #include "rtc-board.h" 12 | 13 | static alarm_pool_t* rtc_alarm_pool = NULL; 14 | static absolute_time_t rtc_timer_context; 15 | static alarm_id_t last_rtc_alarm_id = -1; 16 | 17 | void RtcInit( void ) 18 | { 19 | rtc_alarm_pool = alarm_pool_create(2, 16); 20 | 21 | RtcSetTimerContext(); 22 | } 23 | 24 | uint32_t RtcGetCalendarTime( uint16_t *milliseconds ) 25 | { 26 | uint32_t now = to_ms_since_boot(get_absolute_time()); 27 | 28 | *milliseconds = (now % 1000); 29 | 30 | return (now / 1000); 31 | } 32 | 33 | void RtcBkupRead( uint32_t *data0, uint32_t *data1 ) 34 | { 35 | *data0 = 0; 36 | *data1 = 0; 37 | } 38 | 39 | uint32_t RtcGetTimerElapsedTime( void ) 40 | { 41 | int64_t delta = absolute_time_diff_us(rtc_timer_context, get_absolute_time()); 42 | 43 | return delta; 44 | } 45 | 46 | uint32_t RtcSetTimerContext( void ) 47 | { 48 | rtc_timer_context = get_absolute_time(); 49 | 50 | uint64_t ticks = to_us_since_boot(rtc_timer_context); 51 | 52 | return ticks; 53 | } 54 | 55 | uint32_t RtcGetTimerContext( void ) 56 | { 57 | uint64_t ticks = to_us_since_boot(rtc_timer_context); 58 | 59 | return ticks; 60 | } 61 | 62 | uint32_t RtcGetMinimumTimeout( void ) 63 | { 64 | return 1; 65 | } 66 | 67 | static int64_t alarm_callback(alarm_id_t id, void *user_data) { 68 | TimerIrqHandler( ); 69 | 70 | return 0; 71 | } 72 | 73 | void RtcSetAlarm( uint32_t timeout ) 74 | { 75 | if (last_rtc_alarm_id > -1) { 76 | alarm_pool_cancel_alarm(rtc_alarm_pool, last_rtc_alarm_id); 77 | } 78 | 79 | last_rtc_alarm_id = alarm_pool_add_alarm_at(rtc_alarm_pool, delayed_by_us(rtc_timer_context, timeout), alarm_callback, NULL, true); 80 | } 81 | 82 | void RtcStopAlarm( void ) 83 | { 84 | if (last_rtc_alarm_id > -1) { 85 | alarm_pool_cancel_alarm(rtc_alarm_pool, last_rtc_alarm_id); 86 | } 87 | } 88 | 89 | uint32_t RtcMs2Tick( TimerTime_t milliseconds ) 90 | { 91 | return milliseconds * 1000; 92 | } 93 | 94 | uint32_t RtcGetTimerValue( void ) 95 | { 96 | uint64_t now = to_us_since_boot(get_absolute_time()); 97 | 98 | return now; 99 | } 100 | 101 | TimerTime_t RtcTick2Ms( uint32_t tick ) 102 | { 103 | return us_to_ms(tick); 104 | } 105 | 106 | void RtcBkupWrite( uint32_t data0, uint32_t data1 ) 107 | { 108 | } 109 | 110 | void RtcProcess( void ) 111 | { 112 | // Not used on this platform. 113 | } 114 | -------------------------------------------------------------------------------- /src/boards/rp2040/spi-board.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | #include "pico/stdlib.h" 9 | #include "hardware/spi.h" 10 | 11 | #include "spi-board.h" 12 | 13 | void SpiInit( Spi_t *obj, SpiId_t spiId, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss ) 14 | { 15 | spi_init((spiId == 0) ? spi0 : spi1, 10 * 1000 * 1000); 16 | spi_set_format((spiId == 0) ? spi0 : spi1, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); 17 | gpio_set_function(mosi, GPIO_FUNC_SPI); 18 | gpio_set_function(miso, GPIO_FUNC_SPI); 19 | gpio_set_function(sclk, GPIO_FUNC_SPI); 20 | } 21 | 22 | uint16_t SpiInOut( Spi_t *obj, uint16_t outData ) 23 | { 24 | const uint8_t outDataB = (outData & 0xff); 25 | uint8_t inDataB = 0x00; 26 | 27 | spi_write_read_blocking((obj->SpiId == 0) ? spi0 : spi1, &outDataB, &inDataB, 1); 28 | 29 | return inDataB; 30 | } 31 | -------------------------------------------------------------------------------- /src/boards/rp2040/sx1276-board.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file sx1276-board.c 3 | * 4 | * \brief Target board SX1276 driver implementation 5 | * 6 | * \remark This is based on 7 | * https://github.com/Lora-net/LoRaMac-node/blob/master/src/boards/B-L072Z-LRWAN1/sx1276-board.c 8 | * 9 | * \copyright Revised BSD License, see section \ref LICENSE. 10 | * 11 | * \code 12 | * ______ _ 13 | * / _____) _ | | 14 | * ( (____ _____ ____ _| |_ _____ ____| |__ 15 | * \____ \| ___ | (_ _) ___ |/ ___) _ \ 16 | * _____) ) ____| | | || |_| ____( (___| | | | 17 | * (______/|_____)_|_|_| \__)_____)\____)_| |_| 18 | * (C)2013-2017 Semtech 19 | * 20 | * \endcode 21 | * 22 | * \author Miguel Luis ( Semtech ) 23 | * 24 | * \author Gregory Cristian ( Semtech ) 25 | * 26 | */ 27 | 28 | #include 29 | 30 | #include "hardware/gpio.h" 31 | 32 | #include "delay.h" 33 | #include "sx1276-board.h" 34 | 35 | #include "radio/radio.h" 36 | 37 | const struct Radio_s Radio = 38 | { 39 | SX1276Init, 40 | SX1276GetStatus, 41 | SX1276SetModem, 42 | SX1276SetChannel, 43 | SX1276IsChannelFree, 44 | SX1276Random, 45 | SX1276SetRxConfig, 46 | SX1276SetTxConfig, 47 | SX1276CheckRfFrequency, 48 | SX1276GetTimeOnAir, 49 | SX1276Send, 50 | SX1276SetSleep, 51 | SX1276SetStby, 52 | SX1276SetRx, 53 | SX1276StartCad, 54 | SX1276SetTxContinuousWave, 55 | SX1276ReadRssi, 56 | SX1276Write, 57 | SX1276Read, 58 | SX1276WriteBuffer, 59 | SX1276ReadBuffer, 60 | SX1276SetMaxPayloadLength, 61 | SX1276SetPublicNetwork, 62 | SX1276GetWakeupTime, 63 | NULL, // void ( *IrqProcess )( void ) 64 | NULL, // void ( *RxBoosted )( uint32_t timeout ) - SX126x Only 65 | NULL, // void ( *SetRxDutyCycle )( uint32_t rxTime, uint32_t sleepTime ) - SX126x Only 66 | }; 67 | 68 | static DioIrqHandler** irq_handlers; 69 | 70 | void dio_gpio_callback(uint gpio, uint32_t events) 71 | { 72 | if (gpio == SX1276.DIO0.pin) { 73 | irq_handlers[0](NULL); 74 | } else if (gpio == SX1276.DIO1.pin) { 75 | irq_handlers[1](NULL); 76 | } 77 | } 78 | 79 | void SX1276SetAntSwLowPower( bool status ) 80 | { 81 | } 82 | 83 | bool SX1276CheckRfFrequency( uint32_t frequency ) 84 | { 85 | return true; 86 | } 87 | 88 | void SX1276SetBoardTcxo( uint8_t state ) 89 | { 90 | } 91 | 92 | uint32_t SX1276GetDio1PinState( void ) 93 | { 94 | return GpioRead(&SX1276.DIO1); 95 | } 96 | 97 | void SX1276SetAntSw( uint8_t opMode ) 98 | { 99 | } 100 | 101 | void SX1276Reset( void ) 102 | { 103 | GpioInit( &SX1276.Reset, SX1276.Reset.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); // RST 104 | 105 | DelayMs (1); 106 | 107 | GpioInit( &SX1276.Reset, SX1276.Reset.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 ); // RST 108 | 109 | DelayMs (6); 110 | } 111 | 112 | void SX1276IoInit( void ) 113 | { 114 | GpioInit( &SX1276.Spi.Nss, SX1276.Spi.Nss.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); // CS 115 | GpioInit( &SX1276.Reset, SX1276.Reset.pin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 ); // RST 116 | 117 | GpioInit( &SX1276.DIO0, SX1276.DIO0.pin, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); // IRQ / DIO0 118 | GpioInit( &SX1276.DIO1, SX1276.DIO1.pin, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); // DI01 119 | } 120 | 121 | void SX1276IoIrqInit( DioIrqHandler **irqHandlers ) 122 | { 123 | irq_handlers = irqHandlers; 124 | 125 | gpio_set_irq_enabled_with_callback(SX1276.DIO0.pin, GPIO_IRQ_EDGE_RISE, true, &dio_gpio_callback); 126 | gpio_set_irq_enabled_with_callback(SX1276.DIO1.pin, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &dio_gpio_callback); 127 | } 128 | 129 | /*! 130 | * \brief Gets the board PA selection configuration 131 | * 132 | * \param [IN] power Selects the right PA according to the wanted power. 133 | * \retval PaSelect RegPaConfig PaSelect value 134 | */ 135 | static uint8_t SX1276GetPaSelect( int8_t power ); 136 | 137 | void SX1276SetRfTxPower( int8_t power ) 138 | { 139 | uint8_t paConfig = 0; 140 | uint8_t paDac = 0; 141 | 142 | paConfig = SX1276Read( REG_PACONFIG ); 143 | paDac = SX1276Read( REG_PADAC ); 144 | 145 | paConfig = ( paConfig & RF_PACONFIG_PASELECT_MASK ) | SX1276GetPaSelect( power ); 146 | 147 | if( ( paConfig & RF_PACONFIG_PASELECT_PABOOST ) == RF_PACONFIG_PASELECT_PABOOST ) 148 | { 149 | if( power > 17 ) 150 | { 151 | paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_ON; 152 | } 153 | else 154 | { 155 | paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_OFF; 156 | } 157 | if( ( paDac & RF_PADAC_20DBM_ON ) == RF_PADAC_20DBM_ON ) 158 | { 159 | if( power < 5 ) 160 | { 161 | power = 5; 162 | } 163 | if( power > 20 ) 164 | { 165 | power = 20; 166 | } 167 | paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 5 ) & 0x0F ); 168 | } 169 | else 170 | { 171 | if( power < 2 ) 172 | { 173 | power = 2; 174 | } 175 | if( power > 17 ) 176 | { 177 | power = 17; 178 | } 179 | paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 2 ) & 0x0F ); 180 | } 181 | } 182 | else 183 | { 184 | if( power > 0 ) 185 | { 186 | if( power > 15 ) 187 | { 188 | power = 15; 189 | } 190 | paConfig = ( paConfig & RF_PACONFIG_MAX_POWER_MASK & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( 7 << 4 ) | ( power ); 191 | } 192 | else 193 | { 194 | if( power < -4 ) 195 | { 196 | power = -4; 197 | } 198 | paConfig = ( paConfig & RF_PACONFIG_MAX_POWER_MASK & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( 0 << 4 ) | ( power + 4 ); 199 | } 200 | } 201 | SX1276Write( REG_PACONFIG, paConfig ); 202 | SX1276Write( REG_PADAC, paDac ); 203 | } 204 | 205 | static uint8_t SX1276GetPaSelect( int8_t power ) 206 | { 207 | if( power > 14 ) 208 | { 209 | return RF_PACONFIG_PASELECT_PABOOST; 210 | } 211 | else 212 | { 213 | return RF_PACONFIG_PASELECT_RFO; 214 | } 215 | } 216 | 217 | uint32_t SX1276GetBoardTcxoWakeupTime( void ) 218 | { 219 | return 0; 220 | } 221 | -------------------------------------------------------------------------------- /src/include/pico/lorawan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * 6 | */ 7 | 8 | #ifndef _PICO_LORAWAN_H_ 9 | #define _PICO_LORAWAN_H_ 10 | 11 | #include "hardware/gpio.h" 12 | #include "hardware/spi.h" 13 | 14 | #include "LoRaMac.h" 15 | 16 | struct lorawan_sx1276_settings { 17 | struct { 18 | spi_inst_t* inst; 19 | uint mosi; 20 | uint miso; 21 | uint sck; 22 | uint nss; 23 | } spi; 24 | uint reset; 25 | uint dio0; 26 | uint dio1; 27 | }; 28 | 29 | struct lorawan_abp_settings { 30 | const char* device_address; 31 | const char* network_session_key; 32 | const char* app_session_key; 33 | const char* channel_mask; 34 | }; 35 | 36 | struct lorawan_otaa_settings { 37 | const char* device_eui; 38 | const char* app_eui; 39 | const char* app_key; 40 | const char* channel_mask; 41 | }; 42 | 43 | const char* lorawan_default_dev_eui(char* dev_eui); 44 | 45 | int lorawan_init_abp(const struct lorawan_sx1276_settings* sx1276_settings, LoRaMacRegion_t region, const struct lorawan_abp_settings* abp_settings); 46 | 47 | int lorawan_init_otaa(const struct lorawan_sx1276_settings* sx1276_settings, LoRaMacRegion_t region, const struct lorawan_otaa_settings* otaa_settings); 48 | 49 | int lorawan_join(); 50 | 51 | int lorawan_is_joined(); 52 | 53 | int lorawan_process(); 54 | 55 | int lorawan_process_timeout_ms(uint32_t timeout_ms); 56 | 57 | int lorawan_send_unconfirmed(const void* data, uint8_t data_len, uint8_t app_port); 58 | 59 | int lorawan_receive(void* data, uint8_t data_len, uint8_t* app_port); 60 | 61 | void lorawan_debug(bool debug); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/lorawan.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file lorawan.c 3 | * 4 | * \brief Implements the LoRaMac layer handling. 5 | * Provides the possibility to register applicative packages. 6 | * 7 | * \remark This file is based on 8 | * https://github.com/Lora-net/LoRaMac-node/blob/master/src/apps/LoRaMac/periodic-uplink-lpp/B-L072Z-LRWAN1/main.c 9 | * 10 | * \copyright Revised BSD License, see section \ref LICENSE. 11 | * 12 | * \code 13 | * ______ _ 14 | * / _____) _ | | 15 | * ( (____ _____ ____ _| |_ _____ ____| |__ 16 | * \____ \| ___ | (_ _) ___ |/ ___) _ \ 17 | * _____) ) ____| | | || |_| ____( (___| | | | 18 | * (______/|_____)_|_|_| \__)_____)\____)_| |_| 19 | * (C)2013-2018 Semtech 20 | * 21 | * \endcode 22 | * 23 | * \author Miguel Luis ( Semtech ) 24 | * 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include "pico/lorawan.h" 31 | 32 | #include "board.h" 33 | #include "rtc-board.h" 34 | #include "sx1276-board.h" 35 | 36 | #include "../../periodic-uplink-lpp/firmwareVersion.h" 37 | #include "Commissioning.h" 38 | #include "RegionCommon.h" 39 | #include "LmHandler.h" 40 | #include "LmhpCompliance.h" 41 | #include "LmHandlerMsgDisplay.h" 42 | 43 | /*! 44 | * LoRaWAN default end-device class 45 | */ 46 | #define LORAWAN_DEFAULT_CLASS CLASS_A 47 | 48 | /*! 49 | * LoRaWAN Adaptive Data Rate 50 | * 51 | * \remark Please note that when ADR is enabled the end-device should be static 52 | */ 53 | #define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON 54 | 55 | /*! 56 | * Default datarate 57 | * 58 | * \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled 59 | */ 60 | #define LORAWAN_DEFAULT_DATARATE DR_0 61 | 62 | /*! 63 | * LoRaWAN confirmed messages 64 | */ 65 | #define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG 66 | 67 | /*! 68 | * User application data buffer size 69 | */ 70 | #define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 71 | 72 | /*! 73 | * LoRaWAN ETSI duty cycle control enable/disable 74 | * 75 | * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes 76 | */ 77 | #define LORAWAN_DUTYCYCLE_ON true 78 | 79 | /*! 80 | * Indicates if the end-device is to be connected to a private or public network 81 | */ 82 | #define LORAWAN_PUBLIC_NETWORK true 83 | 84 | /*! 85 | * User application data 86 | */ 87 | static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; 88 | 89 | /*! 90 | * User application data structure 91 | */ 92 | static LmHandlerAppData_t AppData = 93 | { 94 | .Buffer = AppDataBuffer, 95 | .BufferSize = 0, 96 | .Port = 0, 97 | }; 98 | 99 | static void OnMacProcessNotify( void ); 100 | static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); 101 | static void OnNetworkParametersChange( CommissioningParams_t* params ); 102 | static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); 103 | static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); 104 | static void OnJoinRequest( LmHandlerJoinParams_t* params ); 105 | static void OnTxData( LmHandlerTxParams_t* params ); 106 | static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); 107 | static void OnClassChange( DeviceClass_t deviceClass ); 108 | static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); 109 | #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) 110 | static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); 111 | #else 112 | static void OnSysTimeUpdate( void ); 113 | #endif 114 | 115 | static void OnTxPeriodicityChanged( uint32_t periodicity ); 116 | static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); 117 | static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); 118 | 119 | static LmHandlerCallbacks_t LmHandlerCallbacks = 120 | { 121 | .GetBatteryLevel = BoardGetBatteryLevel, 122 | .GetTemperature = NULL, 123 | .GetRandomSeed = BoardGetRandomSeed, 124 | .OnMacProcess = OnMacProcessNotify, 125 | .OnNvmDataChange = OnNvmDataChange, 126 | .OnNetworkParametersChange = OnNetworkParametersChange, 127 | .OnMacMcpsRequest = OnMacMcpsRequest, 128 | .OnMacMlmeRequest = OnMacMlmeRequest, 129 | .OnJoinRequest = OnJoinRequest, 130 | .OnTxData = OnTxData, 131 | .OnRxData = OnRxData, 132 | .OnClassChange= OnClassChange, 133 | .OnBeaconStatusChange = OnBeaconStatusChange, 134 | .OnSysTimeUpdate = OnSysTimeUpdate, 135 | }; 136 | 137 | static LmHandlerParams_t LmHandlerParams = 138 | { 139 | .Region = ACTIVE_REGION, 140 | .AdrEnable = LORAWAN_ADR_STATE, 141 | .IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, 142 | .TxDatarate = LORAWAN_DEFAULT_DATARATE, 143 | .PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, 144 | .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, 145 | .DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, 146 | .DataBuffer = AppDataBuffer, 147 | .PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, 148 | }; 149 | 150 | static LmhpComplianceParams_t LmhpComplianceParams = 151 | { 152 | .FwVersion.Value = FIRMWARE_VERSION, 153 | .OnTxPeriodicityChanged = OnTxPeriodicityChanged, 154 | .OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, 155 | .OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, 156 | }; 157 | 158 | /*! 159 | * Indicates if LoRaMacProcess call is pending. 160 | * 161 | * \warning If variable is equal to 0 then the MCU can be set in low power mode 162 | */ 163 | static volatile uint8_t IsMacProcessPending = 0; 164 | 165 | static volatile uint32_t TxPeriodicity = 0; 166 | 167 | static const struct lorawan_abp_settings* AbpSettings = NULL; 168 | 169 | static const struct lorawan_otaa_settings* OtaaSettings = NULL; 170 | 171 | static uint8_t AppRxDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; 172 | 173 | static LmHandlerAppData_t AppRxData = 174 | { 175 | .Buffer = AppRxDataBuffer, 176 | .BufferSize = 0, 177 | .Port = 0, 178 | }; 179 | 180 | static bool Debug = false; 181 | 182 | const char* lorawan_default_dev_eui(char* dev_eui) 183 | { 184 | uint8_t boardId[8]; 185 | 186 | BoardGetUniqueId(boardId); 187 | 188 | sprintf( 189 | dev_eui, "%02X%02X%02X%02X%02X%02X%02X%02X", 190 | boardId[0], boardId[1], boardId[2], boardId[3], 191 | boardId[4], boardId[5], boardId[6], boardId[7] 192 | ); 193 | 194 | return dev_eui; 195 | } 196 | 197 | static int lorawan_init(const struct lorawan_sx1276_settings* sx1276_settings, LoRaMacRegion_t region) 198 | { 199 | RtcInit(); 200 | SpiInit( 201 | &SX1276.Spi, 202 | (SpiId_t)((sx1276_settings->spi.inst == spi0) ? 0 : 1), 203 | sx1276_settings->spi.mosi /*MOSI*/, 204 | sx1276_settings->spi.miso /*MISO*/, 205 | sx1276_settings->spi.sck /*SCK*/, 206 | NC 207 | ); 208 | 209 | SX1276.Spi.Nss.pin = sx1276_settings->spi.nss; 210 | SX1276.Reset.pin = sx1276_settings->reset; 211 | SX1276.DIO0.pin = sx1276_settings->dio0; 212 | SX1276.DIO1.pin = sx1276_settings->dio1; 213 | 214 | SX1276IoInit(); 215 | 216 | // check version register 217 | if (SX1276Read(REG_LR_VERSION) != 0x12) { 218 | return -1; 219 | } 220 | 221 | LmHandlerParams.Region = region; 222 | 223 | if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) 224 | { 225 | return -1; 226 | } 227 | 228 | // Set system maximum tolerated rx error in milliseconds 229 | LmHandlerSetSystemMaxRxError( 20 ); 230 | 231 | // The LoRa-Alliance Compliance protocol package should always be 232 | // initialized and activated. 233 | LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); 234 | 235 | return 0; 236 | } 237 | 238 | int lorawan_init_abp(const struct lorawan_sx1276_settings* sx1276_settings, LoRaMacRegion_t region, const struct lorawan_abp_settings* abp_settings) 239 | { 240 | AbpSettings = abp_settings; 241 | OtaaSettings = NULL; 242 | 243 | return lorawan_init(sx1276_settings, region); 244 | } 245 | 246 | int lorawan_init_otaa(const struct lorawan_sx1276_settings* sx1276_settings, LoRaMacRegion_t region, const struct lorawan_otaa_settings* otaa_settings) 247 | { 248 | AbpSettings = NULL; 249 | OtaaSettings = otaa_settings; 250 | 251 | return lorawan_init(sx1276_settings, region); 252 | } 253 | 254 | int lorawan_join() 255 | { 256 | LmHandlerJoin( ); 257 | 258 | return 0; 259 | } 260 | 261 | int lorawan_is_joined() 262 | { 263 | return (LmHandlerJoinStatus() == LORAMAC_HANDLER_SET); 264 | } 265 | 266 | int lorawan_process() 267 | { 268 | int sleep = 0; 269 | 270 | // Processes the LoRaMac events 271 | LmHandlerProcess( ); 272 | 273 | CRITICAL_SECTION_BEGIN( ); 274 | if( IsMacProcessPending == 1 ) 275 | { 276 | // Clear flag and prevent MCU to go into low power modes. 277 | IsMacProcessPending = 0; 278 | } 279 | else 280 | { 281 | // The MCU wakes up through events 282 | sleep = 1; 283 | } 284 | CRITICAL_SECTION_END( ); 285 | 286 | return sleep; 287 | } 288 | 289 | int lorawan_process_timeout_ms(uint32_t timeout_ms) 290 | { 291 | absolute_time_t timeout_time = make_timeout_time_ms(timeout_ms); 292 | 293 | bool joined = lorawan_is_joined(); 294 | 295 | do { 296 | lorawan_process(); 297 | 298 | if (AppRxData.Port) { 299 | return 0; 300 | } else if (joined != lorawan_is_joined()) { 301 | return 0; 302 | } 303 | } while (!best_effort_wfe_or_timeout(timeout_time)); 304 | 305 | return 1; // timed out 306 | } 307 | 308 | int lorawan_send_unconfirmed(const void* data, uint8_t data_len, uint8_t app_port) 309 | { 310 | LmHandlerAppData_t appData; 311 | 312 | appData.Port = app_port; 313 | appData.BufferSize = data_len; 314 | appData.Buffer = (uint8_t*)data; 315 | 316 | if (LmHandlerSend(&appData, LORAMAC_HANDLER_UNCONFIRMED_MSG) != LORAMAC_HANDLER_SUCCESS) { 317 | return -1; 318 | } 319 | 320 | return 0; 321 | } 322 | 323 | int lorawan_receive(void* data, uint8_t data_len, uint8_t* app_port) 324 | { 325 | *app_port = AppRxData.Port; 326 | if (*app_port == 0) { 327 | return -1; 328 | } 329 | 330 | int receive_length = AppRxData.BufferSize; 331 | 332 | if (data_len < receive_length) { 333 | receive_length = data_len; 334 | } 335 | 336 | memcpy(data, AppRxData.Buffer, receive_length); 337 | AppRxData.Port = 0; 338 | 339 | return receive_length; 340 | } 341 | 342 | void lorawan_debug(bool debug) 343 | { 344 | Debug = debug; 345 | } 346 | 347 | static void OnMacProcessNotify( void ) 348 | { 349 | IsMacProcessPending = 1; 350 | } 351 | 352 | static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) 353 | { 354 | if (Debug) { 355 | DisplayNvmDataChange( state, size ); 356 | } 357 | } 358 | 359 | static void OnNetworkParametersChange( CommissioningParams_t* params ) 360 | { 361 | MibRequestConfirm_t mibReq; 362 | 363 | const char* device_eui = NULL; 364 | const char* app_eui = NULL; 365 | const char* device_address = NULL; 366 | const char* app_key = NULL; 367 | const char* app_session_key = NULL; 368 | const char* network_session_key = NULL; 369 | const char* channel_mask = NULL; 370 | 371 | if (OtaaSettings != NULL) { 372 | params->IsOtaaActivation = 1; 373 | 374 | device_eui = OtaaSettings->device_eui; 375 | app_eui = OtaaSettings->app_eui; 376 | app_key = OtaaSettings->app_key; 377 | channel_mask = OtaaSettings->channel_mask; 378 | } 379 | 380 | if (AbpSettings != NULL) { 381 | params->IsOtaaActivation = 0; 382 | 383 | device_address = AbpSettings->device_address; 384 | app_session_key = AbpSettings->app_session_key; 385 | network_session_key = AbpSettings->network_session_key; 386 | channel_mask = AbpSettings->channel_mask; 387 | 388 | // Tell the MAC layer which network server version are we connecting too. 389 | mibReq.Type = MIB_ABP_LORAWAN_VERSION; 390 | mibReq.Param.AbpLrWanVersion.Value = ABP_ACTIVATION_LRWAN_VERSION; 391 | LoRaMacMibSetRequestConfirm( &mibReq ); 392 | 393 | mibReq.Type = MIB_NET_ID; 394 | mibReq.Param.NetID = LORAWAN_NETWORK_ID; 395 | LoRaMacMibSetRequestConfirm( &mibReq ); 396 | 397 | if (device_address != NULL) { 398 | params->DevAddr = 0; 399 | 400 | for (int i = 0; i < 4; i++) { 401 | uint8_t b; 402 | 403 | sscanf(device_address + i * 2, "%2hhx", &b); 404 | 405 | params->DevAddr = (params->DevAddr << 8) | b; 406 | } 407 | } else { 408 | // Random seed initialization 409 | srand1( LmHandlerCallbacks.GetRandomSeed( ) ); 410 | // Choose a random device address 411 | params->DevAddr = randr( 0, 0x01FFFFFF ); 412 | } 413 | 414 | mibReq.Type = MIB_DEV_ADDR; 415 | mibReq.Param.DevAddr = params->DevAddr; 416 | LoRaMacMibSetRequestConfirm( &mibReq ); 417 | } 418 | 419 | if (device_eui != NULL) { 420 | uint8_t deviceEui[8]; 421 | 422 | for (int i = 0; i < 8; i++) { 423 | sscanf(device_eui + i * 2, "%2hhx", &deviceEui[i]); 424 | } 425 | 426 | mibReq.Type = MIB_DEV_EUI; 427 | mibReq.Param.DevEui = deviceEui; 428 | LoRaMacMibSetRequestConfirm( &mibReq ); 429 | memcpy1( params->DevEui, mibReq.Param.DevEui, 8 ); 430 | } 431 | 432 | if (app_eui != NULL) { 433 | uint8_t joinEui[8]; 434 | 435 | for (int i = 0; i < 8; i++) { 436 | sscanf(app_eui + i * 2, "%2hhx", &joinEui[i]); 437 | } 438 | 439 | mibReq.Type = MIB_JOIN_EUI; 440 | mibReq.Param.JoinEui = joinEui; 441 | LoRaMacMibSetRequestConfirm( &mibReq ); 442 | memcpy1( params->JoinEui, mibReq.Param.JoinEui, 8 ); 443 | } 444 | 445 | if (app_key) { 446 | uint8_t appKey[16]; 447 | 448 | for (int i = 0; i < 16; i++) { 449 | sscanf(app_key + i * 2, "%2hhx", &appKey[i]); 450 | } 451 | 452 | mibReq.Type = MIB_APP_KEY; 453 | mibReq.Param.AppKey = appKey; 454 | LoRaMacMibSetRequestConfirm( &mibReq ); 455 | 456 | mibReq.Type = MIB_NWK_KEY; 457 | mibReq.Param.NwkKey = appKey; 458 | LoRaMacMibSetRequestConfirm( &mibReq ); 459 | } 460 | 461 | if (app_session_key) { 462 | uint8_t appSessionKey[16]; 463 | 464 | for (int i = 0; i < 16; i++) { 465 | sscanf(app_session_key + i * 2, "%2hhx", &appSessionKey[i]); 466 | } 467 | 468 | mibReq.Type = MIB_APP_S_KEY; 469 | mibReq.Param.AppSKey = appSessionKey; 470 | LoRaMacMibSetRequestConfirm( &mibReq ); 471 | } 472 | 473 | if (network_session_key) { 474 | uint8_t networkSessionKey[16]; 475 | 476 | for (int i = 0; i < 16; i++) { 477 | sscanf(network_session_key + i * 2, "%2hhx", &networkSessionKey[i]); 478 | } 479 | 480 | mibReq.Type = MIB_F_NWK_S_INT_KEY; 481 | mibReq.Param.FNwkSIntKey = networkSessionKey; 482 | LoRaMacMibSetRequestConfirm( &mibReq ); 483 | 484 | mibReq.Type = MIB_S_NWK_S_INT_KEY; 485 | mibReq.Param.SNwkSIntKey = networkSessionKey; 486 | LoRaMacMibSetRequestConfirm( &mibReq ); 487 | 488 | mibReq.Type = MIB_NWK_S_ENC_KEY; 489 | mibReq.Param.NwkSEncKey = networkSessionKey; 490 | LoRaMacMibSetRequestConfirm( &mibReq ); 491 | } 492 | 493 | if (channel_mask != NULL) { 494 | uint16_t channelMask[6]; 495 | 496 | for (int i = 0; i < 6; i++) { 497 | uint8_t b[2]; 498 | 499 | sscanf(channel_mask + i * 4 + 0, "%2hhx", &b[0]); 500 | sscanf(channel_mask + i * 4 + 2, "%2hhx", &b[1]); 501 | 502 | channelMask[i] = (b[0] << 8) | b[1]; 503 | } 504 | 505 | mibReq.Type = MIB_CHANNELS_MASK; 506 | mibReq.Param.ChannelsMask = channelMask; 507 | LoRaMacMibSetRequestConfirm( &mibReq ); 508 | 509 | mibReq.Type = MIB_CHANNELS_DEFAULT_MASK; 510 | mibReq.Param.ChannelsDefaultMask = channelMask; 511 | LoRaMacMibSetRequestConfirm( &mibReq ); 512 | } 513 | 514 | if (Debug) { 515 | DisplayNetworkParametersUpdate( params ); 516 | } 517 | } 518 | 519 | static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) 520 | { 521 | if (Debug) { 522 | DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); 523 | } 524 | } 525 | 526 | static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) 527 | { 528 | if (Debug) { 529 | DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); 530 | } 531 | } 532 | 533 | static void OnJoinRequest( LmHandlerJoinParams_t* params ) 534 | { 535 | if (Debug) { 536 | DisplayJoinRequestUpdate( params ); 537 | } 538 | 539 | if( params->Status == LORAMAC_HANDLER_ERROR ) 540 | { 541 | LmHandlerJoin( ); 542 | } 543 | else 544 | { 545 | LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); 546 | } 547 | } 548 | 549 | static void OnTxData( LmHandlerTxParams_t* params ) 550 | { 551 | if (Debug) { 552 | DisplayTxUpdate( params ); 553 | } 554 | } 555 | 556 | static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) 557 | { 558 | if (Debug) { 559 | DisplayRxUpdate( appData, params ); 560 | } 561 | 562 | memcpy(AppRxData.Buffer, appData->Buffer, appData->BufferSize); 563 | AppRxData.BufferSize = appData->BufferSize; 564 | AppRxData.Port = appData->Port; 565 | } 566 | 567 | static void OnClassChange( DeviceClass_t deviceClass ) 568 | { 569 | if (Debug) { 570 | DisplayClassUpdate( deviceClass ); 571 | } 572 | 573 | // Inform the server as soon as possible that the end-device has switched to ClassB 574 | LmHandlerAppData_t appData = 575 | { 576 | .Buffer = NULL, 577 | .BufferSize = 0, 578 | .Port = 0, 579 | }; 580 | LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); 581 | } 582 | 583 | static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) 584 | { 585 | switch( params->State ) 586 | { 587 | case LORAMAC_HANDLER_BEACON_RX: 588 | { 589 | break; 590 | } 591 | case LORAMAC_HANDLER_BEACON_LOST: 592 | case LORAMAC_HANDLER_BEACON_NRX: 593 | { 594 | break; 595 | } 596 | default: 597 | { 598 | break; 599 | } 600 | } 601 | 602 | if (Debug) { 603 | DisplayBeaconUpdate( params ); 604 | } 605 | } 606 | 607 | #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) 608 | static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) 609 | { 610 | 611 | } 612 | #else 613 | static void OnSysTimeUpdate( void ) 614 | { 615 | 616 | } 617 | #endif 618 | 619 | static void OnTxPeriodicityChanged( uint32_t periodicity ) 620 | { 621 | TxPeriodicity = periodicity; 622 | } 623 | 624 | static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) 625 | { 626 | LmHandlerParams.IsTxConfirmed = isTxConfirmed; 627 | } 628 | 629 | static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) 630 | { 631 | LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; 632 | } 633 | --------------------------------------------------------------------------------