├── .github └── workflows │ ├── build-examples.yml │ └── realease.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── README-old.md ├── README.md ├── add_msys_path.py ├── copy_msys_dlls.py ├── examples ├── BME280_2.4v │ ├── .clang-format │ ├── .gitignore │ ├── .travis.yml │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── README.md │ ├── boot │ │ ├── README.md │ │ └── optiboot_flash_atmega328p_UART0_9600_1000000L.hex │ ├── lib │ │ ├── bmp280 │ │ │ ├── bme280.cpp │ │ │ └── bme280.h │ │ └── sleepandwatchdog │ │ │ ├── sleepandwatchdog.cpp │ │ │ └── sleepandwatchdog.h │ ├── platformio.ini │ └── src │ │ ├── main.cpp │ │ ├── powersave.cpp │ │ └── powersave.h ├── balise │ ├── .clang-format │ ├── .gitignore │ ├── .travis.yml │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── README.md │ ├── lib │ │ └── sleepandwatchdog │ │ │ ├── sleepandwatchdog.cpp │ │ │ └── sleepandwatchdog.h │ ├── platformio.ini │ └── src │ │ ├── main.cpp │ │ ├── powersave.cpp │ │ └── powersave.h ├── balise_2.4v │ ├── .clang-format │ ├── .gitignore │ ├── .travis.yml │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── README.md │ ├── boot │ │ ├── README.md │ │ └── optiboot_flash_atmega328p_UART0_9600_1000000L.hex │ ├── lib │ │ └── sleepandwatchdog │ │ │ ├── sleepandwatchdog.cpp │ │ │ └── sleepandwatchdog.h │ ├── platformio.ini │ └── src │ │ ├── main.cpp │ │ ├── powersave.cpp │ │ └── powersave.h ├── esp32-deepsleep-abp │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── README.md │ ├── platformio-ci.ini │ ├── platformio.ini │ └── src │ │ └── main.cpp ├── esp32-deepsleep │ ├── .clang-format │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── README.md │ ├── platformio.ini │ └── src │ │ └── main.cpp ├── esp32 │ ├── .clang-format │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── README.md │ ├── platformio-ci.ini │ ├── platformio.ini │ └── src │ │ ├── lorakeys.h │ │ └── main.cpp ├── esp32_classC │ ├── .clang-format │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── README.md │ ├── platformio.ini │ └── src │ │ ├── lorakeys.h │ │ └── main.cpp ├── rak811_gps │ ├── .clang-format │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── platformio-ci.ini │ ├── platformio.ini │ └── src │ │ └── main.cpp ├── simple │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── platformio.ini │ └── src │ │ ├── lorakeys.h │ │ └── main.cpp ├── simple_rak811 │ ├── .clang-format │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ └── settings.json │ ├── platformio.ini │ └── src │ │ ├── lorakeys.h │ │ └── main.cpp ├── simple_sx1262 │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── platformio.ini │ └── src │ │ ├── lorakeys.h │ │ └── main.cpp └── tempsensor │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ └── settings.json │ ├── lib │ └── sleepandwatchdog │ │ ├── sleepandwatchdog.cpp │ │ └── sleepandwatchdog.h │ ├── platformio.ini │ └── src │ ├── main.cpp │ ├── powersave.cpp │ └── powersave.h ├── library.json ├── library.properties ├── platformio.ini ├── src ├── .clang-format ├── aes │ ├── aes_encrypt.h │ ├── aes_tiny.cpp │ ├── asp_esp.cpp │ ├── limc_aes.cpp │ └── lmic_aes.h ├── boardconfig.h ├── certificationprotocol.cpp ├── certificationprotocol.h ├── hal │ ├── hal.cpp │ ├── hal.h │ ├── hal_esp.cpp │ ├── hal_generic.cpp │ ├── hal_io.cpp │ ├── hal_io.h │ ├── hal_io_empty.cpp │ ├── print_debug.cpp │ └── print_debug.h ├── keyhandler.h ├── lmic.h └── lmic │ ├── band.eu868.cpp │ ├── band.eu868.h │ ├── bands.h │ ├── bufferpack.cpp │ ├── bufferpack.h │ ├── channelList.h │ ├── config.h │ ├── enumflagsvalue.h │ ├── lmic.cpp │ ├── lmic.eu433.cpp │ ├── lmic.eu433.h │ ├── lmic.eu868.cpp │ ├── lmic.eu868.h │ ├── lmic.h │ ├── lmic.us915.cpp │ ├── lmic.us915.h │ ├── lmic_table.h │ ├── lmicdynamicchannel.h │ ├── lmicrand.h │ ├── lmicrandesp.cpp │ ├── lmicrandfromaes.cpp │ ├── lorabase.h │ ├── lorawanpacket.h │ ├── oslmic.cpp │ ├── oslmic.h │ ├── osticks.cpp │ ├── osticks.h │ ├── radio.cpp │ ├── radio.h │ ├── radio_fake.cpp │ ├── radio_fake.h │ ├── radio_sx1262.cpp │ ├── radio_sx1262.h │ ├── radio_sx1272.cpp │ ├── radio_sx1272.h │ ├── radio_sx1276.cpp │ └── radio_sx1276.h └── test ├── .clang-format ├── cert ├── activation_pretest.cpp ├── activation_pretest.h ├── aesdecrypt.cpp ├── aesdecrypt.h ├── dut.cpp ├── dut.h ├── packet_util.cpp ├── packet_util.h ├── test_activation_pretest │ └── main.cpp ├── test_device_functionality │ └── main.cpp └── test_mac_command │ ├── common.cpp │ ├── common.h │ ├── dev-status-req.cpp │ ├── incorrect_mac_command.cpp │ ├── main.cpp │ ├── multiple-mac-commands-prioritization.cpp │ ├── new-channel-req.cpp │ ├── rx-timing-setup-req.cpp │ └── tx-param-setup-req.cpp └── test_various ├── test_aes.cpp ├── test_aes.h ├── test_eu868channels.cpp ├── test_eu868channels.h ├── test_keyhandler.cpp ├── test_keyhandler.h └── test_main.cpp /.github/workflows/build-examples.yml: -------------------------------------------------------------------------------- 1 | name: PlatformIO CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v3 11 | - uses: actions/cache@v3 12 | with: 13 | path: | 14 | ~/.cache/pip 15 | ~/.platformio/.cache 16 | key: ${{ runner.os }}-pio 17 | - uses: actions/setup-python@v4 18 | with: 19 | python-version: '3.9' 20 | - name: Install PlatformIO Core 21 | run: | 22 | pip install --upgrade platformio 23 | platformio platform install native 24 | - name: Run unit test 25 | run: platformio test -e native 26 | - name: Build simple exemple 27 | run: platformio ci --lib="." --board=ATmega328P --project-option="lib_deps=https://github.com/ngraziano/avr_stl.git" examples/simple 28 | - name: Build simple sx1262 exemple 29 | run: platformio ci --lib="." --board=ATmega328P --project-option="lib_deps=https://github.com/ngraziano/avr_stl.git" examples/simple_sx1262 30 | - name: Build esp32 exemple 31 | run: platformio ci --lib="." examples/esp32 --project-conf examples/esp32/platformio-ci.ini 32 | - name: Build esp32 abp 33 | run: platformio ci --lib="." examples/esp32-deepsleep-abp --project-conf examples/esp32-deepsleep-abp/platformio-ci.ini 34 | # - name: Build rak811 exemple 35 | # run: platformio ci --lib="." examples/rak811_gps --project-conf examples/rak811_gps/platformio-ci.ini 36 | -------------------------------------------------------------------------------- /.github/workflows/realease.yml: -------------------------------------------------------------------------------- 1 | name: PlatformIO CI Release 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/cache@v3 13 | with: 14 | path: | 15 | ~/.cache/pip 16 | ~/.platformio/.cache 17 | key: ${{ runner.os }}-pio 18 | - uses: actions/setup-python@v4 19 | with: 20 | python-version: '3.9' 21 | - name: Install PlatformIO Core 22 | run: | 23 | pip install --upgrade platformio 24 | - name: Release 25 | env: # Or as an environment variable 26 | PLATFORMIO_AUTH_TOKEN: ${{ secrets.PIO_TOKEN }} 27 | run: platformio package publish --non-interactive 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .piolibdeps 3 | .vscode/.browse.c_cpp.db* 4 | .vscode/.browse.c_cpp.2.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | .vscode/ipch 8 | /.pio 9 | examples/rak811_gps/src/lorakeys.h 10 | compile_commands.json 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LORAWAN Librairie for Arduino and SX1276 , SX1262 lora chip 2 | 3 | * Based on LMIC librairy. 4 | * Modified to get C++ style. 5 | * Only class A and C device (no class B) 6 | * Add some sleep of arduino board and ESP. 7 | * Add SX1262 chip 8 | 9 | ## Limitation 10 | 11 | This library try to comply with lorawan 1.0.3 but do not implement all feature. For example FSK datarate is not implemented. 12 | 13 | For a more complete Lorawan library based on lmic, please have a look to 14 | 15 | :warning: This library do not compile in Arduino IDE due to dependency to STL (ciband/avr_stl in case of AVR platform). 16 | It need PlatfomIO for the dependencies to be handle correctly. 17 | 18 | For the SX1262 it only support board with TCXO. 19 | 20 | ## AVR examples 21 | 22 | Tested with Arduino Pro Mini and RFM95 on EU868 frequencies. Examples: 23 | 24 | * [simple](examples/simple/) Minimal example, send one analog value. 25 | * [balise](examples/balise/) Example, send one analog value with deepsleep using watchdog. 26 | * [tempsensor](examples/tempsensor/) Example, send temps reads from onewire with deepsleep using watchdog. 27 | 28 | ## ESP32 examples 29 | 30 | Tested with HelTec 31 | 32 | * [esp32](examples/esp32/) Minimal example, send one value. 33 | * [esp32-deepsleep](examples/esp32-deepsleep/) Example, send one analog value with deepsleep using RTC and state save in RTC RAM. 34 | 35 | ## RAK811 36 | * [simple_rak811](examples/simple_rak811) Minimal example for RAK811 37 | 38 | ## Usage 39 | 40 | Work with platformio with Arduino framework. 41 | 42 | Copy balise exemple in a new directory. 43 | Open with platformio (VSCODE with Platformio extension) 44 | In ``src`` directory create a file named ``lorakeys.h`` wich contain the keys declared in network (for exemple ) 45 | 46 | Exemple of file: 47 | 48 | ```cpp 49 | // Application in string format. 50 | // For TTN issued EUIs the first bytes should be 70B3D5 51 | constexpr char const appEui[] = "70B3D5XXXXXXXXXX"; 52 | 53 | // Device EUI in string format. 54 | constexpr char const devEui[] = "XXXXXXXXXXXXXXXX"; 55 | // Application key in string format. 56 | constexpr char const appKey[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 57 | 58 | ``` 59 | 60 | Use define in platformio.ini `build_flags` to change activated part. 61 | 62 | * ENABLE_SAVE_RESTORE enable save and restore functions 63 | * LMIC_DEBUG_LEVEL set to 0,1 or 2 for different log levels (default value 1) 64 | 65 | In ``main.cpp`` replace the content of ``do_send()`` with the data you want to send. 66 | 67 | 68 | ## Test under windows 69 | 70 | To launch unit test under windows you need to install msys2 in the following path ``C:\msys64``. 71 | ``add_msys_path.py`` script will add msys64 path to your environment and ``copy_msys_dll.py`` will copy dll to the build directory. 72 | 73 | ## License 74 | 75 | Most source files in this repository are made available under the Eclipse Public License v1.0. 76 | Some of the AES code is available under MIT like license. Refer to each individual source file for more details. 77 | -------------------------------------------------------------------------------- /add_msys_path.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | 4 | if platform.system() == "Windows": 5 | os.environ["path"] += ";C:\\msys64\\mingw64\\bin;C:\\msys64\\ucrt64\\bin;C:\\msys64\\usr\\bin" 6 | 7 | 8 | -------------------------------------------------------------------------------- /copy_msys_dlls.py: -------------------------------------------------------------------------------- 1 | 2 | # ugly but easy to make the test pass : copy the dlls to the folder of the exe 3 | # copy libgcc_s_seh-1 libstdc++-6 libwinpthread-1 to the folder of the exe 4 | 5 | import platform 6 | import shutil 7 | 8 | Import("env") 9 | 10 | 11 | msys_dlls = ["libgcc_s_seh-1.dll", "libstdc++-6.dll", "libwinpthread-1.dll"] 12 | 13 | if platform.system() == "Windows": 14 | for dll in msys_dlls: 15 | source = "C:\\msys64\\ucrt64\\bin\\" + dll 16 | dest = env.subst('$BUILD_DIR\\') + dll 17 | shutil.copyfile(source, dest) 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | .vscode/ipch 8 | 9 | src/lorakeys.h 10 | 11 | lora-cppcheck-build-dir/ 12 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < http://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < http://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < http://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choice one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # - platformio update 39 | # 40 | # script: 41 | # - platformio run 42 | 43 | 44 | # 45 | # Template #2: The project is intended to by used as a library with examples 46 | # 47 | 48 | # language: python 49 | # python: 50 | # - "2.7" 51 | # 52 | # sudo: false 53 | # cache: 54 | # directories: 55 | # - "~/.platformio" 56 | # 57 | # env: 58 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 59 | # - PLATFORMIO_CI_SRC=examples/file.ino 60 | # - PLATFORMIO_CI_SRC=path/to/test/directory 61 | # 62 | # install: 63 | # - pip install -U platformio 64 | # - platformio update 65 | # 66 | # script: 67 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N 68 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/README.md: -------------------------------------------------------------------------------- 1 | # Sample of LMICPP-Arduino for Arduino Pro mini with deepsleep at 2Mhz and with custom bootloader 2 | 3 | *Warning* : Not standart bootloader **must** be installed to handle watchdog and low voltage 4 | 5 | Tested with Arduino Pro Mini and RFM95 on EU868 frequencies. 6 | 7 | ## Usage 8 | 9 | Work with platformio. 10 | 11 | In ``src`` directory create a file named ``lorakeys.h`` wich contain the keys declared in network (for exemple ) 12 | 13 | Exemple of file: 14 | 15 | ```cpp 16 | // Application in string format. 17 | // For TTN issued EUIs the first bytes should be 70B3D5 18 | constexpr char const appEui[] = "70B3D5XXXXXXXXXX"; 19 | 20 | // Device EUI in string format. 21 | constexpr char const devEui[] = "XXXXXXXXXXXXXXXX"; 22 | // Application key in string format. 23 | constexpr char const appKey[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 24 | 25 | ``` 26 | 27 | Calibrate deepsleep duration (see below) 28 | 29 | Int 1 / Pin 3 is use to wake a with a button linked to ground. 30 | 31 | ## Calibration of deep sleep 32 | 33 | During start there is a test of deepsleep time. 34 | You need to calibrate it. 35 | Use a terminal which print the time the message are receive (YAT for example) and mesure time between message `Start Test sleep time.` and `End Test sleep time.` divide this time by the time in `Test Time should be :` message and ajust `sleepAdj` acordingly. 36 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/boot/README.md: -------------------------------------------------------------------------------- 1 | # Bootloader 2 | 3 | File get from 4 | 5 | ## Set Fuse 6 | 7 | E:FE, H:D6, L:7F 8 | 9 | ```bat 10 | c:\TOOLS\avrdude\avrdude.exe -c avrisp -p m328p -P COM9 -b19200 -U lfuse:w:0x7f:m -U hfuse:w:0xd6:m -U efuse:w:0xfe:m 11 | REM avrdude -P comport -b 19200 -c avrisp -p m328p -v -e -U efuse:w:0x05:m -U hfuse:w:0xD6:m -U lfuse:w:0xFF:m 12 | ``` 13 | 14 | ## Intall bootloader 15 | 16 | ```bat 17 | c:\TOOLS\avrdude\avrdude -P COM9 -b 19200 -c avrisp -p m328p -v -e -U flash:w:optiboot_flash_atmega328p_UART0_9600_1000000L.hex 18 | REM -U lock:w:0x0F:m 19 | ``` 20 | 21 | ## Original value 22 | 23 | Fuses OK (E:FD, H:DA, L:FF) 24 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/boot/optiboot_flash_atmega328p_UART0_9600_1000000L.hex: -------------------------------------------------------------------------------- 1 | :107E000001C0B2C0112484B7882361F0982F9A7002 2 | :107E1000923041F081FF02C097EF94BF282E80E09E 3 | :107E2000C6D0EEC085E08093810082E08093C000E0 4 | :107E300088E18093C1008CE08093C40086E0809349 5 | :107E4000C2008EE0B4D0259A84E023EC3FEF91E0AD 6 | :107E5000309385002093840096BBB09BFECF1D9A83 7 | :107E6000A8954091C00047FD02C0815089F793D08A 8 | :107E7000813479F490D0182FA0D0123811F480E01A 9 | :107E800004C088E0113809F083E07ED080E17CD026 10 | :107E9000EECF823419F484E198D0F8CF853411F410 11 | :107EA00085E0FACF853541F476D0C82F74D0D82F2D 12 | :107EB000CC0FDD1F82D0EACF863519F484E085D05F 13 | :107EC000DECF843691F567D066D0F82E64D0D82EF8 14 | :107ED00000E011E058018FEFA81AB80A5CD0F80151 15 | :107EE00080838501FA10F6CF68D0F5E4DF1201C077 16 | :107EF000FFCF50E040E063E0CE0136D08E01E0E0FD 17 | :107F0000F1E06F0182E0C80ED11C4081518161E037 18 | :107F1000C8012AD00E5F1F4FF601FC10F2CF50E0CF 19 | :107F200040E065E0CE0120D0B1CF843771F433D08A 20 | :107F300032D0F82E30D041D08E01F80185918F01DA 21 | :107F400023D0FA94F110F9CFA1CF853739F435D089 22 | :107F50008EE11AD085E918D08FE097CF813509F0EE 23 | :107F6000A9CF88E024D0A6CFFC010A0167BFE8951D 24 | :107F7000112407B600FCFDCF667029F0452B19F4DB 25 | :107F800081E187BFE89508959091C00095FFFCCFEF 26 | :107F90008093C60008958091C00087FFFCCF809138 27 | :107FA000C00084FD01C0A8958091C6000895E0E658 28 | :107FB000F0E098E1908380830895EDDF803219F03E 29 | :107FC00088E0F5DFFFCF84E1DFCFCF93C82FE3DF79 30 | :087FD000C150E9F7CF91F1CF98 31 | :027FFE00000879 32 | :0400000300007E007B 33 | :00000001FF -------------------------------------------------------------------------------- /examples/BME280_2.4v/lib/bmp280/bme280.h: -------------------------------------------------------------------------------- 1 | #ifndef BME280_h 2 | #define BME280_h 3 | 4 | #include "Arduino.h" 5 | #include 6 | 7 | enum class BME280Registers : uint8_t { 8 | dig_T1 = 0x88, 9 | dig_T2 = 0x8A, 10 | dig_T3 = 0x8C, 11 | 12 | dig_P1 = 0x8E, 13 | dig_P2 = 0x90, 14 | dig_P3 = 0x92, 15 | dig_P4 = 0x94, 16 | dig_P5 = 0x96, 17 | dig_P6 = 0x98, 18 | dig_P7 = 0x9A, 19 | dig_P8 = 0x9C, 20 | dig_P9 = 0x9E, 21 | 22 | dig_H1 = 0xA1, 23 | 24 | chipid = 0xD0, 25 | 26 | dig_H2 = 0xE1, 27 | dig_H3 = 0xE3, 28 | dig_H4 = 0xE4, 29 | dig_H45 = 0xE5, 30 | dig_H5 = 0xE6, 31 | dig_H6 = 0xE7, 32 | 33 | ctrl_hum = 0xF2, 34 | status = 0xF3, 35 | ctrl_meas = 0xF4, 36 | 37 | press_msb = 0xF7, 38 | press_lsb = 0xF8, 39 | press_xlsb = 0xF9, 40 | 41 | temp_msb = 0xFA, 42 | temp_lsb = 0xFB, 43 | temp_xlsb = 0xFC, 44 | 45 | }; 46 | 47 | struct BME280_Result { 48 | int32_t T; 49 | uint32_t P; 50 | uint32_t H; 51 | }; 52 | 53 | struct BME280_Result16 { 54 | int16_t T; 55 | uint16_t P; 56 | uint16_t H; 57 | }; 58 | 59 | class BME280 { 60 | public: 61 | static const uint8_t BME280_ADDRESS1 = 0x76; 62 | static const uint8_t BME280_ADDRESS2 = 0x77; 63 | 64 | BME280() = default; 65 | BME280(uint8_t i2cAddress) : i2c_address(i2cAddress){}; 66 | bool begin(); 67 | void singleMeasure() const; 68 | void waitMeasureDone() const; 69 | 70 | BME280_Result getValues() const; 71 | BME280_Result16 getValues16() const; 72 | 73 | private: 74 | const uint8_t i2c_address = BME280_ADDRESS1; 75 | 76 | uint8_t read8(BME280Registers bmeregister) const; 77 | void write8(BME280Registers bmeregister, uint8_t value) const; 78 | uint16_t read16(BME280Registers bmeregister) const; 79 | int16_t read16s(BME280Registers bmeregister) const; 80 | void readBuffer(BME280Registers bmeregister, uint8_t *buffer, uint8_t size) const; 81 | 82 | void readTrimmingParameters(); 83 | 84 | int32_t compensate_T(int32_t adc_T) const; 85 | uint32_t compensate_P(int32_t adc_P, int32_t t_fine) const; 86 | uint32_t compensate_H(int32_t adc_H, int32_t t_fine) const; 87 | 88 | uint16_t dig_T1; 89 | int16_t dig_T2; 90 | int16_t dig_T3; 91 | 92 | uint16_t dig_P1; 93 | int16_t dig_P2; 94 | int16_t dig_P3; 95 | int16_t dig_P4; 96 | int16_t dig_P5; 97 | int16_t dig_P6; 98 | int16_t dig_P7; 99 | int16_t dig_P8; 100 | int16_t dig_P9; 101 | 102 | uint8_t dig_H1; 103 | int16_t dig_H2; 104 | uint8_t dig_H3; 105 | int16_t dig_H4; 106 | int16_t dig_H5; 107 | int8_t dig_H6; 108 | }; 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/lib/sleepandwatchdog/sleepandwatchdog.cpp: -------------------------------------------------------------------------------- 1 | #include "sleepandwatchdog.h" 2 | 3 | #include "Arduino.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace { 10 | volatile bool wdtEnable = false; 11 | } 12 | 13 | void powerDown(Sleep period) { 14 | bool back = wdtEnable; 15 | wdtEnable = false; 16 | ADCSRA &= ~(1 << ADEN); 17 | 18 | if (period != Sleep::FOREVER) { 19 | wdt_enable(static_cast(period)); 20 | WDTCSR |= (1 << WDIE); 21 | } 22 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 23 | cli(); 24 | sleep_enable(); 25 | sleep_bod_disable(); 26 | sei(); 27 | sleep_cpu(); 28 | sleep_disable(); 29 | sei(); 30 | ADCSRA |= (1 << ADEN); 31 | 32 | if (back) 33 | configure_wdt(); 34 | } 35 | 36 | void configure_wdt() { 37 | wdtEnable = true; 38 | wdt_enable(WDTO_8S); 39 | WDTCSR |= (1 << WDIE); 40 | } 41 | 42 | void rst_wdt() { wdt_reset(); } 43 | 44 | ISR(WDT_vect) { 45 | if (!wdtEnable) { 46 | // WDIE & WDIF is cleared in hardware upon entering this ISR 47 | wdt_disable(); 48 | } else { 49 | // enable watchdog without interupt to reboot 50 | wdt_enable(static_cast(Sleep::P8S)); 51 | // reboot 52 | while (1) 53 | ; 54 | } 55 | } -------------------------------------------------------------------------------- /examples/BME280_2.4v/lib/sleepandwatchdog/sleepandwatchdog.h: -------------------------------------------------------------------------------- 1 | #ifndef _sleepandwatchdog_h_ 2 | #define _sleepandwatchdog_h_ 3 | 4 | enum class Sleep : unsigned char { 5 | P15MS, 6 | P30MS, 7 | P60MS, 8 | P120MS, 9 | P250MS, 10 | P500MS, 11 | P1S, 12 | P2S, 13 | P4S, 14 | P8S, 15 | FOREVER 16 | }; 17 | 18 | void powerDown(Sleep period); 19 | void configure_wdt(); 20 | void rst_wdt(); 21 | 22 | #endif -------------------------------------------------------------------------------- /examples/BME280_2.4v/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:atmega328] 12 | platform = atmelavr 13 | board = ATmega328P 14 | framework = arduino 15 | upload_port = COM7 16 | 17 | monitor_port = COM7 18 | monitor_speed = 9600 19 | 20 | build_flags = -std=gnu++17 -Wall -Wextra -O3 -DLMIC_DEBUG_LEVEL=0 21 | upload_speed = 9600 22 | # Board speed is modified at runtime in Setup() 23 | board_build.f_cpu = 2000000L 24 | 25 | 26 | lib_deps = 27 | https://github.com/ngraziano/avr_stl.git 28 | https://github.com/ngraziano/LMICPP-Arduino.git 29 | 30 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/src/powersave.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "powersave.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const int64_t sleepAdj = 1095; 9 | 10 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt) { 11 | OsDeltaTime duration_selected; 12 | Sleep period_selected; 13 | // these value are base on test 14 | if (maxTime > OsDeltaTime::from_ms(8700)) { 15 | duration_selected = OsDeltaTime::from_ms(8000 * sleepAdj / 1000); 16 | period_selected = Sleep::P8S; 17 | } else if (maxTime > OsDeltaTime::from_ms(4600)) { 18 | duration_selected = OsDeltaTime::from_ms(4000 * sleepAdj / 1000); 19 | period_selected = Sleep::P4S; 20 | } else if (maxTime > OsDeltaTime::from_ms(2600)) { 21 | duration_selected = OsDeltaTime::from_ms(2000 * sleepAdj / 1000); 22 | period_selected = Sleep::P2S; 23 | } else if (maxTime > OsDeltaTime::from_ms(1500)) { 24 | duration_selected = OsDeltaTime::from_ms(1000 * sleepAdj / 1000); 25 | period_selected = Sleep::P1S; 26 | } else if (maxTime > OsDeltaTime::from_ms(800)) { 27 | duration_selected = OsDeltaTime::from_ms(500 * sleepAdj / 1000); 28 | period_selected = Sleep::P500MS; 29 | } else if (maxTime > OsDeltaTime::from_ms(500)) { 30 | duration_selected = OsDeltaTime::from_ms(250 * sleepAdj / 1000); 31 | period_selected = Sleep::P250MS; 32 | } else { 33 | return; 34 | } 35 | 36 | PRINT_DEBUG(1, F("Sleep (ostick) :%lix%i"), duration_selected.to_ms(), 37 | maxTime / duration_selected); 38 | if (debugLevel > 0) { 39 | Serial.flush(); 40 | } 41 | 42 | bool stopsleep = false; 43 | for (uint16_t nbsleep = maxTime / duration_selected; 44 | nbsleep > 0 && !stopsleep; nbsleep--) { 45 | powerDown(period_selected); 46 | hal_add_time_in_sleep(duration_selected); 47 | stopsleep = interrupt(); 48 | } 49 | PRINT_DEBUG(1, F("Wakeup")); 50 | } 51 | -------------------------------------------------------------------------------- /examples/BME280_2.4v/src/powersave.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _powersave_h_ 3 | #define _powersave_h_ 4 | 5 | class OsDeltaTime; 6 | 7 | using stopsleepcb_t = bool (*)(); 8 | 9 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt); 10 | 11 | #endif -------------------------------------------------------------------------------- /examples/balise/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/balise/.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .piolibdeps 3 | .pio 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | .vscode/ipch 8 | 9 | src/lorakeys.h 10 | 11 | lora-cppcheck-build-dir/ 12 | -------------------------------------------------------------------------------- /examples/balise/.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < http://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < http://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < http://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choice one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # - platformio update 39 | # 40 | # script: 41 | # - platformio run 42 | 43 | 44 | # 45 | # Template #2: The project is intended to by used as a library with examples 46 | # 47 | 48 | # language: python 49 | # python: 50 | # - "2.7" 51 | # 52 | # sudo: false 53 | # cache: 54 | # directories: 55 | # - "~/.platformio" 56 | # 57 | # env: 58 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 59 | # - PLATFORMIO_CI_SRC=examples/file.ino 60 | # - PLATFORMIO_CI_SRC=path/to/test/directory 61 | # 62 | # install: 63 | # - pip install -U platformio 64 | # - platformio update 65 | # 66 | # script: 67 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N 68 | -------------------------------------------------------------------------------- /examples/balise/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/balise/README.md: -------------------------------------------------------------------------------- 1 | # Sample of LMICPP-Arduino for Arduino Pro mini with deepsleep 2 | 3 | Tested with Arduino Pro Mini and RFM95 on EU868 frequencies. 4 | 5 | ## Usage 6 | 7 | Work with platformio. 8 | 9 | In ``src`` directory create a file named ``lorakeys.h`` wich contain the keys declared in network (for exemple ) 10 | 11 | Exemple of file: 12 | 13 | ```cpp 14 | // Application in string format. 15 | // For TTN issued EUIs the first bytes should be 70B3D5 16 | constexpr char const appEui[] = "70B3D5XXXXXXXXXX"; 17 | 18 | // Device EUI in string format. 19 | constexpr char const devEui[] = "XXXXXXXXXXXXXXXX"; 20 | // Application key in string format. 21 | constexpr char const appKey[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 22 | 23 | ``` 24 | 25 | Calibrate deepsleep duration (see below) 26 | 27 | Int 1 / Pin 3 is use to wake a with a button linked to ground. 28 | 29 | ## Calibration of deep sleep 30 | 31 | During start there is a test of deepsleep time. 32 | You need to calibrate it. 33 | Use a terminal which print the time the message are receive (YAT for example) and mesure time between message `Start Test sleep time.` and `End Test sleep time.` divide this time by the time in `Test Time should be :` message and ajust `sleepAdj` acordingly. 34 | -------------------------------------------------------------------------------- /examples/balise/lib/sleepandwatchdog/sleepandwatchdog.cpp: -------------------------------------------------------------------------------- 1 | #include "sleepandwatchdog.h" 2 | 3 | #include "Arduino.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace { 10 | volatile bool wdtEnable = false; 11 | } 12 | 13 | void powerDown(Sleep period) { 14 | bool back = wdtEnable; 15 | wdtEnable = false; 16 | ADCSRA &= ~(1 << ADEN); 17 | 18 | if (period != Sleep::FOREVER) { 19 | wdt_enable(static_cast(period)); 20 | WDTCSR |= (1 << WDIE); 21 | } 22 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 23 | cli(); 24 | sleep_enable(); 25 | sleep_bod_disable(); 26 | sei(); 27 | sleep_cpu(); 28 | sleep_disable(); 29 | sei(); 30 | ADCSRA |= (1 << ADEN); 31 | 32 | if (back) 33 | configure_wdt(); 34 | } 35 | 36 | void configure_wdt() { 37 | wdtEnable = true; 38 | wdt_enable(WDTO_8S); 39 | WDTCSR |= (1 << WDIE); 40 | } 41 | 42 | void rst_wdt() { wdt_reset(); } 43 | 44 | ISR(WDT_vect) { 45 | if (!wdtEnable) { 46 | // WDIE & WDIF is cleared in hardware upon entering this ISR 47 | wdt_disable(); 48 | } else { 49 | // enable watchdog without interupt to reboot 50 | wdt_enable(static_cast(Sleep::P8S)); 51 | // reboot 52 | while (1) 53 | ; 54 | } 55 | } -------------------------------------------------------------------------------- /examples/balise/lib/sleepandwatchdog/sleepandwatchdog.h: -------------------------------------------------------------------------------- 1 | #ifndef _sleepandwatchdog_h_ 2 | #define _sleepandwatchdog_h_ 3 | 4 | enum class Sleep : unsigned char { 5 | P15MS, 6 | P30MS, 7 | P60MS, 8 | P120MS, 9 | P250MS, 10 | P500MS, 11 | P1S, 12 | P2S, 13 | P4S, 14 | P8S, 15 | FOREVER 16 | }; 17 | 18 | void powerDown(Sleep period); 19 | void configure_wdt(); 20 | void rst_wdt(); 21 | 22 | #endif -------------------------------------------------------------------------------- /examples/balise/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:pro8MHzatmega328] 12 | platform = atmelavr 13 | board = ATmega328P 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -Wall -Wextra -O3 21 | upload_speed = 38400 22 | 23 | lib_deps = 24 | https://github.com/ngraziano/avr_stl.git 25 | https://github.com/ngraziano/LMICPP-Arduino.git 26 | -------------------------------------------------------------------------------- /examples/balise/src/powersave.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "powersave.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const int64_t sleepAdj = 1080; 9 | 10 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt) { 11 | OsDeltaTime duration_selected; 12 | Sleep period_selected; 13 | // these value are base on test 14 | if (maxTime > OsDeltaTime::from_ms(8700)) { 15 | duration_selected = OsDeltaTime::from_ms(8000 * sleepAdj / 1000); 16 | period_selected = Sleep::P8S; 17 | } else if (maxTime > OsDeltaTime::from_ms(4600)) { 18 | duration_selected = OsDeltaTime::from_ms(4000 * sleepAdj / 1000); 19 | period_selected = Sleep::P4S; 20 | } else if (maxTime > OsDeltaTime::from_ms(2600)) { 21 | duration_selected = OsDeltaTime::from_ms(2000 * sleepAdj / 1000); 22 | period_selected = Sleep::P2S; 23 | } else if (maxTime > OsDeltaTime::from_ms(1500)) { 24 | duration_selected = OsDeltaTime::from_ms(1000 * sleepAdj / 1000); 25 | period_selected = Sleep::P1S; 26 | } else if (maxTime > OsDeltaTime::from_ms(800)) { 27 | duration_selected = OsDeltaTime::from_ms(500 * sleepAdj / 1000); 28 | period_selected = Sleep::P500MS; 29 | } else if (maxTime > OsDeltaTime::from_ms(500)) { 30 | duration_selected = OsDeltaTime::from_ms(250 * sleepAdj / 1000); 31 | period_selected = Sleep::P250MS; 32 | } else { 33 | return; 34 | } 35 | 36 | PRINT_DEBUG(1, F("Sleep (ostick) :%lix%i"), duration_selected.to_ms(), 37 | maxTime / duration_selected); 38 | if (debugLevel > 0) { 39 | Serial.flush(); 40 | } 41 | 42 | bool stopsleep = false; 43 | for (uint16_t nbsleep = maxTime / duration_selected; 44 | nbsleep > 0 && !stopsleep; nbsleep--) { 45 | powerDown(period_selected); 46 | hal_add_time_in_sleep(duration_selected); 47 | stopsleep = interrupt(); 48 | } 49 | PRINT_DEBUG(1, F("Wakeup")); 50 | } 51 | -------------------------------------------------------------------------------- /examples/balise/src/powersave.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _powersave_h_ 3 | #define _powersave_h_ 4 | 5 | class OsDeltaTime; 6 | 7 | using stopsleepcb_t = bool (*)(); 8 | 9 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt); 10 | 11 | #endif -------------------------------------------------------------------------------- /examples/balise_2.4v/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/balise_2.4v/.gitignore: -------------------------------------------------------------------------------- 1 | .pio/ 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | .vscode/ipch 8 | 9 | src/lorakeys.h 10 | 11 | lora-cppcheck-build-dir/ 12 | -------------------------------------------------------------------------------- /examples/balise_2.4v/.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < http://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < http://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < http://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choice one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # - platformio update 39 | # 40 | # script: 41 | # - platformio run 42 | 43 | 44 | # 45 | # Template #2: The project is intended to by used as a library with examples 46 | # 47 | 48 | # language: python 49 | # python: 50 | # - "2.7" 51 | # 52 | # sudo: false 53 | # cache: 54 | # directories: 55 | # - "~/.platformio" 56 | # 57 | # env: 58 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 59 | # - PLATFORMIO_CI_SRC=examples/file.ino 60 | # - PLATFORMIO_CI_SRC=path/to/test/directory 61 | # 62 | # install: 63 | # - pip install -U platformio 64 | # - platformio update 65 | # 66 | # script: 67 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N 68 | -------------------------------------------------------------------------------- /examples/balise_2.4v/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/balise_2.4v/README.md: -------------------------------------------------------------------------------- 1 | # Sample of LMICPP-Arduino for Arduino Pro mini with deepsleep at 2Mhz and with custom bootloader 2 | 3 | *Warning* : Not standart bootloader **must** be installed to handle watchdog and low voltage 4 | 5 | Tested with Arduino Pro Mini and RFM95 on EU868 frequencies. 6 | 7 | ## Usage 8 | 9 | Work with platformio. 10 | 11 | In ``src`` directory create a file named ``lorakeys.h`` wich contain the keys declared in network (for exemple ) 12 | 13 | Exemple of file: 14 | 15 | ```cpp 16 | // Application in string format. 17 | // For TTN issued EUIs the first bytes should be 70B3D5 18 | constexpr char const appEui[] = "70B3D5XXXXXXXXXX"; 19 | 20 | // Device EUI in string format. 21 | constexpr char const devEui[] = "XXXXXXXXXXXXXXXX"; 22 | // Application key in string format. 23 | constexpr char const appKey[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 24 | 25 | ``` 26 | 27 | Calibrate deepsleep duration (see below) 28 | 29 | Int 1 / Pin 3 is use to wake a with a button linked to ground. 30 | 31 | ## Calibration of deep sleep 32 | 33 | During start there is a test of deepsleep time. 34 | You need to calibrate it. 35 | Use a terminal which print the time the message are receive (YAT for example) and mesure time between message `Start Test sleep time.` and `End Test sleep time.` divide this time by the time in `Test Time should be :` message and ajust `sleepAdj` acordingly. 36 | -------------------------------------------------------------------------------- /examples/balise_2.4v/boot/README.md: -------------------------------------------------------------------------------- 1 | # Bootloader 2 | 3 | File get from 4 | 5 | ## Set Fuse 6 | 7 | E:FE, H:D6, L:7F 8 | 9 | ```bat 10 | c:\TOOLS\avrdude\avrdude.exe -c avrisp -p m328p -P COM9 -b19200 -U lfuse:w:0x7f:m -U hfuse:w:0xd6:m -U efuse:w:0xfe:m 11 | REM avrdude -P comport -b 19200 -c avrisp -p m328p -v -e -U efuse:w:0x05:m -U hfuse:w:0xD6:m -U lfuse:w:0xFF:m 12 | ``` 13 | 14 | ## Intall bootloader 15 | 16 | ```bat 17 | c:\TOOLS\avrdude\avrdude -P COM9 -b 19200 -c avrisp -p m328p -v -e -U flash:w:optiboot_flash_atmega328p_UART0_9600_1000000L.hex 18 | REM -U lock:w:0x0F:m 19 | ``` 20 | 21 | ## Original value 22 | 23 | Fuses OK (E:FD, H:DA, L:FF) 24 | -------------------------------------------------------------------------------- /examples/balise_2.4v/boot/optiboot_flash_atmega328p_UART0_9600_1000000L.hex: -------------------------------------------------------------------------------- 1 | :107E000001C0ABC0112484B7882361F0982F9A7009 2 | :107E1000923041F081FF02C097EF94BF282E80E09E 3 | :107E2000BFD0EEC085E08093810082E08093C000E7 4 | :107E300088E18093C1008CE08093C40086E0809349 5 | :107E4000C2008EE0ADD0259A84E023EC3FEF91E0B4 6 | :107E5000309385002093840096BBB09BFECF1D9A83 7 | :107E6000A8954091C00047FD02C0815089F78CD091 8 | :107E7000813461F489D0182F99D01238E9F0113883 9 | :107E800011F488E001C083E078D064C0823411F43A 10 | :107E900084E103C0853419F485E090D05BC085355A 11 | :107EA00039F472D0C82F70D0D82FCC0FDD1F51C03D 12 | :107EB000863521F484E082D080E0E6CF843661F517 13 | :107EC00063D062D0D82E60D0F82E00E011E05CD0F4 14 | :107ED000F80181938F01DE12FACF68D0F5E4FF122A 15 | :107EE00001C0FFCF40E050E063E0CE0136D07E011C 16 | :107EF00000E011E0F801419151918F0161E0C7016B 17 | :107F00002CD0F2E0EF0EF11CD012F4CF40E050E0A4 18 | :107F100065E0CE0122D01EC0843771F435D034D054 19 | :107F2000F82E32D043D08E01F80185918F0125D0F3 20 | :107F3000FA94F110F9CF0EC0853739F437D08EE1BD 21 | :107F40001CD085E91AD08FE09FCF813511F488E0ED 22 | :107F500027D02CD080E111D08ACFFC010A0167BF65 23 | :107F6000E895112407B600FCFDCF667029F0452B7B 24 | :107F700019F481E187BFE89508959091C00095FFBD 25 | :107F8000FCCF8093C60008958091C00087FFFCCF8E 26 | :107F90008091C00084FD01C0A8958091C60008951D 27 | :107FA000E0E6F0E098E1908380830895EDDF803291 28 | :107FB00019F088E0F5DFFFCF84E1DFCFCF93C82F42 29 | :0A7FC000E3DFC150E9F7CF91F1CFE4 30 | :027FFE00000879 31 | :0400000300007E007B 32 | :00000001FF 33 | -------------------------------------------------------------------------------- /examples/balise_2.4v/lib/sleepandwatchdog/sleepandwatchdog.cpp: -------------------------------------------------------------------------------- 1 | #include "sleepandwatchdog.h" 2 | 3 | #include "Arduino.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace { 10 | volatile bool wdtEnable = false; 11 | } 12 | 13 | void powerDown(Sleep period) { 14 | bool back = wdtEnable; 15 | wdtEnable = false; 16 | ADCSRA &= ~(1 << ADEN); 17 | 18 | if (period != Sleep::FOREVER) { 19 | wdt_enable(static_cast(period)); 20 | WDTCSR |= (1 << WDIE); 21 | } 22 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 23 | cli(); 24 | sleep_enable(); 25 | sleep_bod_disable(); 26 | sei(); 27 | sleep_cpu(); 28 | sleep_disable(); 29 | sei(); 30 | ADCSRA |= (1 << ADEN); 31 | 32 | if (back) 33 | configure_wdt(); 34 | } 35 | 36 | void configure_wdt() { 37 | wdtEnable = true; 38 | wdt_enable(WDTO_8S); 39 | WDTCSR |= (1 << WDIE); 40 | } 41 | 42 | void rst_wdt() { wdt_reset(); } 43 | 44 | ISR(WDT_vect) { 45 | if (!wdtEnable) { 46 | // WDIE & WDIF is cleared in hardware upon entering this ISR 47 | wdt_disable(); 48 | } else { 49 | // enable watchdog without interupt to reboot 50 | wdt_enable(static_cast(Sleep::P8S)); 51 | // reboot 52 | while (1) 53 | ; 54 | } 55 | } -------------------------------------------------------------------------------- /examples/balise_2.4v/lib/sleepandwatchdog/sleepandwatchdog.h: -------------------------------------------------------------------------------- 1 | #ifndef _sleepandwatchdog_h_ 2 | #define _sleepandwatchdog_h_ 3 | 4 | enum class Sleep : unsigned char { 5 | P15MS, 6 | P30MS, 7 | P60MS, 8 | P120MS, 9 | P250MS, 10 | P500MS, 11 | P1S, 12 | P2S, 13 | P4S, 14 | P8S, 15 | FOREVER 16 | }; 17 | 18 | void powerDown(Sleep period); 19 | void configure_wdt(); 20 | void rst_wdt(); 21 | 22 | #endif -------------------------------------------------------------------------------- /examples/balise_2.4v/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:pro8MHzatmega328] 12 | platform = atmelavr 13 | board = ATMEGA328P 14 | framework = arduino 15 | upload_port = COM3 16 | 17 | monitor_port = COM3 18 | monitor_speed = 9600 19 | 20 | build_flags = -Wall -Wextra -O3 21 | upload_speed = 9600 22 | board_build.f_cpu = 2000000L 23 | 24 | 25 | lib_deps = 26 | https://github.com/ngraziano/avr_stl.git 27 | ngraziano/LMICPP-Arduino 28 | 29 | -------------------------------------------------------------------------------- /examples/balise_2.4v/src/powersave.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "powersave.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const int64_t sleepAdj = 1017; 9 | 10 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt) { 11 | OsDeltaTime duration_selected; 12 | Sleep period_selected; 13 | // these value are base on test 14 | if (maxTime > OsDeltaTime::from_ms(8700)) { 15 | duration_selected = OsDeltaTime::from_ms(8000 * sleepAdj / 1000); 16 | period_selected = Sleep::P8S; 17 | } else if (maxTime > OsDeltaTime::from_ms(4600)) { 18 | duration_selected = OsDeltaTime::from_ms(4000 * sleepAdj / 1000); 19 | period_selected = Sleep::P4S; 20 | } else if (maxTime > OsDeltaTime::from_ms(2600)) { 21 | duration_selected = OsDeltaTime::from_ms(2000 * sleepAdj / 1000); 22 | period_selected = Sleep::P2S; 23 | } else if (maxTime > OsDeltaTime::from_ms(1500)) { 24 | duration_selected = OsDeltaTime::from_ms(1000 * sleepAdj / 1000); 25 | period_selected = Sleep::P1S; 26 | } else if (maxTime > OsDeltaTime::from_ms(800)) { 27 | duration_selected = OsDeltaTime::from_ms(500 * sleepAdj / 1000); 28 | period_selected = Sleep::P500MS; 29 | } else if (maxTime > OsDeltaTime::from_ms(500)) { 30 | duration_selected = OsDeltaTime::from_ms(250 * sleepAdj / 1000); 31 | period_selected = Sleep::P250MS; 32 | } else { 33 | return; 34 | } 35 | 36 | PRINT_DEBUG(1, F("Sleep (ostick) :%lix%i"), duration_selected.to_ms(), 37 | maxTime / duration_selected); 38 | if (debugLevel > 0) { 39 | Serial.flush(); 40 | } 41 | 42 | bool stopsleep = false; 43 | for (uint16_t nbsleep = maxTime / duration_selected; 44 | nbsleep > 0 && !stopsleep; nbsleep--) { 45 | powerDown(period_selected); 46 | hal_add_time_in_sleep(duration_selected); 47 | stopsleep = interrupt(); 48 | } 49 | PRINT_DEBUG(1, F("Wakeup")); 50 | } 51 | -------------------------------------------------------------------------------- /examples/balise_2.4v/src/powersave.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _powersave_h_ 3 | #define _powersave_h_ 4 | 5 | class OsDeltaTime; 6 | 7 | using stopsleepcb_t = bool (*)(); 8 | 9 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt); 10 | 11 | #endif -------------------------------------------------------------------------------- /examples/esp32-deepsleep-abp/.gitignore: -------------------------------------------------------------------------------- 1 | .pio/ 2 | 3 | # Autogenerated by PlatformIO 4 | .vscode/c_cpp_properties.json 5 | .vscode/launch.json 6 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep-abp/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep-abp/README.md: -------------------------------------------------------------------------------- 1 | # Example for ESP32 with DEEP SLEEP 2 | 3 | This is an exemple for ESP32 with arduino framework. 4 | 5 | The system send a value, save the LMIC state and go to deep sleep. 6 | At wake up if a value was saved the systeme restore the saved state. 7 | 8 | This example was tested with HELTEC board. 9 | 10 | Only work on EU868. 11 | 12 | ## Specific to ESP32 13 | 14 | Use: 15 | 16 | - hardware AES for encoding. 17 | - system random generator. 18 | - RTC clock for timing. 19 | 20 | Go deep sleep. 21 | 22 | 23 | ## Usage 24 | 25 | Work with platformio. 26 | 27 | Open with platformio (VSCODE with Platformio extension) 28 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep-abp/platformio-ci.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32] 12 | platform = espressif32 13 | board = heltec_wifi_lora_32 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -std=gnu++17 -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE 21 | build_unflags = -std=gnu++11 22 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep-abp/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32] 12 | platform = espressif32 13 | board = heltec_wifi_lora_32 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -std=gnu++17 -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE 21 | build_unflags = -std=gnu++11 22 | 23 | 24 | lib_deps = 25 | ngraziano/LMICPP-Arduino 26 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | 8 | 9 | src/lorakeys.h 10 | 11 | lora-cppcheck-build-dir/ 12 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } -------------------------------------------------------------------------------- /examples/esp32-deepsleep/README.md: -------------------------------------------------------------------------------- 1 | # Example for ESP32 with DEEP SLEEP 2 | 3 | This is an exemple for ESP32 with arduino framework. 4 | 5 | The system send a value, save the LMIC state and go to deep sleep. 6 | At wake up if a value was saved the systeme restore the saved state. 7 | 8 | This example was tested with HELTEC board. 9 | 10 | Only work on EU868. 11 | 12 | ## Specific to ESP32 13 | 14 | Use: 15 | 16 | - hardware AES for encoding. 17 | - system random generator. 18 | - RTC clock for timing. 19 | 20 | Go deep sleep. 21 | 22 | 23 | ## Usage 24 | 25 | Work with platformio. 26 | 27 | Open with platformio (VSCODE with Platformio extension) 28 | 29 | In ``src`` directory create a file named ``lorakeys.h`` wich contain the keys declared in network (for exemple ) 30 | 31 | Exemple of file: 32 | 33 | ```cpp 34 | // Application in string format. 35 | // For TTN issued EUIs the first bytes should be 70B3D5 36 | constexpr char const appEui[] = "70B3D5XXXXXXXXXX"; 37 | 38 | // Device EUI in string format. 39 | constexpr char const devEui[] = "XXXXXXXXXXXXXXXX"; 40 | // Application key in string format. 41 | constexpr char const appKey[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 42 | 43 | ``` 44 | 45 | In ``main.cpp`` replace the content of ``do_send()`` with the data you want to send. 46 | 47 | Check the pin configuration for your board in ``lmic_pins`` structure. -------------------------------------------------------------------------------- /examples/esp32-deepsleep/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32] 12 | platform = espressif32 13 | board = heltec_wifi_lora_32 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -std=gnu++17 -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE 21 | build_unflags = -std=gnu++11 22 | 23 | lib_deps = 24 | https://github.com/ngraziano/LMICPP-Arduino.git 25 | -------------------------------------------------------------------------------- /examples/esp32-deepsleep/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define DEVICE_TESTESP32 10 | #include "lorakeys.h" 11 | 12 | void do_send(); 13 | 14 | // Schedule TX every this many seconds (might become longer due to duty 15 | // cycle limitations). 16 | constexpr OsDeltaTime TX_INTERVAL = OsDeltaTime::from_sec(135); 17 | 18 | constexpr unsigned int BAUDRATE = 115200; 19 | // Pin mapping 20 | constexpr lmic_pinmap lmic_pins = { 21 | .nss = 18, 22 | .prepare_antenna_tx = nullptr, 23 | .rst = 14, 24 | .dio = {26, 33}, 25 | }; 26 | RadioSx1276 radio{lmic_pins}; 27 | LmicEu868 LMIC{radio}; 28 | 29 | OsTime nextSend; 30 | 31 | // buffer to save current lmic state (size may be reduce) 32 | RTC_DATA_ATTR uint8_t saveState[301]; 33 | 34 | void onEvent(EventType ev) { 35 | switch (ev) { 36 | case EventType::JOINING: 37 | PRINT_DEBUG(2, F("EV_JOINING")); 38 | // LMIC.setDrJoin(0); 39 | break; 40 | case EventType::JOINED: 41 | PRINT_DEBUG(2, F("EV_JOINED")); 42 | // disable ADR because if will be mobile. 43 | // LMIC.setLinkCheckMode(false); 44 | break; 45 | case EventType::JOIN_FAILED: 46 | PRINT_DEBUG(2, F("EV_JOIN_FAILED")); 47 | break; 48 | case EventType::TXCOMPLETE: { 49 | PRINT_DEBUG(2, F("EV_TXCOMPLETE (includes waiting for RX windows)")); 50 | if (LMIC.getTxRxFlags().test(TxRxStatus::ACK)) { 51 | PRINT_DEBUG(1, F("Received ack")); 52 | } 53 | if (LMIC.getDataLen()) { 54 | PRINT_DEBUG(1, F("Received %d bytes of payload"), LMIC.getDataLen()); 55 | auto data = LMIC.getData(); 56 | if (data) { 57 | uint8_t port = LMIC.getPort(); 58 | } 59 | } 60 | // we have transmit 61 | // save before going to deep sleep. 62 | auto store = StoringBuffer{saveState}; 63 | LMIC.saveState(store); 64 | saveState[300] = 51; 65 | PRINT_DEBUG(1, F("State save len = %i"), store.length()); 66 | ESP.deepSleep(TX_INTERVAL.to_us()); 67 | break; 68 | } 69 | case EventType::RESET: 70 | PRINT_DEBUG(2, F("EV_RESET")); 71 | break; 72 | case EventType::LINK_DEAD: 73 | PRINT_DEBUG(2, F("EV_LINK_DEAD")); 74 | break; 75 | case EventType::LINK_ALIVE: 76 | PRINT_DEBUG(2, F("EV_LINK_ALIVE")); 77 | break; 78 | default: 79 | PRINT_DEBUG(2, F("Unknown event")); 80 | break; 81 | } 82 | } 83 | 84 | void do_send() { 85 | // Some analog value 86 | // val = analogRead(A1) >> 4; 87 | uint8_t val = temperatureRead(); 88 | // Prepare upstream data transmission at the next possible time. 89 | LMIC.setTxData2(2, &val, 1, false); 90 | PRINT_DEBUG(1, F("Packet queued")); 91 | nextSend = os_getTime() + TX_INTERVAL; 92 | } 93 | 94 | void setup() { 95 | 96 | if (debugLevel > 0) { 97 | Serial.begin(BAUDRATE); 98 | } 99 | 100 | SPI.begin(); 101 | // LMIC init 102 | os_init(); 103 | LMIC.init(); 104 | // Reset the MAC state. Session and pending data transfers will be discarded. 105 | LMIC.reset(); 106 | LMIC.setEventCallBack(onEvent); 107 | SetupLmicKey::setup(LMIC); 108 | 109 | // set clock error to allow good connection. 110 | LMIC.setClockError(MAX_CLOCK_ERROR * 3 / 100); 111 | // LMIC.setAntennaPowerAdjustment(-14); 112 | 113 | if (saveState[300] == 51) { 114 | auto retrieve = RetrieveBuffer{saveState}; 115 | LMIC.loadState(retrieve); 116 | // PRINT_DEBUG(1, F("State load len = %i"), lbuf); 117 | saveState[300] = 0; 118 | } 119 | // Start job (sending automatically starts OTAA too) 120 | nextSend = os_getTime(); 121 | } 122 | 123 | void loop() { 124 | 125 | OsDeltaTime freeTimeBeforeNextCall = LMIC.run(); 126 | 127 | if (freeTimeBeforeNextCall > OsDeltaTime::from_ms(10)) { 128 | // we have more than 10 ms to do some work. 129 | // the test must be adapted from the time spend in other task 130 | if (nextSend < os_getTime()) { 131 | if (LMIC.getOpMode().test(OpState::TXRXPEND)) { 132 | PRINT_DEBUG(1, F("OpState::TXRXPEND, not sending")); 133 | } else { 134 | do_send(); 135 | } 136 | } else { 137 | OsDeltaTime freeTimeBeforeSend = nextSend - os_getTime(); 138 | OsDeltaTime to_wait = 139 | std::min(freeTimeBeforeNextCall, freeTimeBeforeSend); 140 | delay(to_wait.to_ms() / 2); 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /examples/esp32/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/esp32/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | 8 | lora-cppcheck-build-dir/ 9 | -------------------------------------------------------------------------------- /examples/esp32/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } -------------------------------------------------------------------------------- /examples/esp32/README.md: -------------------------------------------------------------------------------- 1 | # Simple example for ESP32 2 | 3 | This is a simple exemple for ESP32 with arduino framework. 4 | 5 | ## Specific to ESP32 6 | 7 | It use hardware AES for encoding. 8 | 9 | ## Usage 10 | 11 | Work with platformio. 12 | 13 | Open with platformio (VSCODE with Platformio extension) 14 | 15 | In ``src`` directory create a file named ``lorakeys.h`` wich contain the keys declared in network (for exemple ) 16 | 17 | Exemple of file: 18 | 19 | ```cpp 20 | // Application in string format. 21 | // For TTN issued EUIs the first bytes should be 70B3D5 22 | constexpr char const appEui[] = "70B3D5XXXXXXXXXX"; 23 | 24 | // Device EUI in string format. 25 | constexpr char const devEui[] = "XXXXXXXXXXXXXXXX"; 26 | // Application key in string format. 27 | constexpr char const appKey[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 28 | 29 | ``` 30 | 31 | In ``main.cpp`` replace the content of ``do_send()`` with the data you want to send. 32 | 33 | Check the pin configuration for your board in ``lmic_pins`` structure. -------------------------------------------------------------------------------- /examples/esp32/platformio-ci.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32] 12 | platform = espressif32 13 | board = heltec_wifi_lora_32 14 | framework = arduino 15 | 16 | build_flags = -std=gnu++17 -Wall -Wextra 17 | build_unflags = -std=gnu++11 18 | 19 | -------------------------------------------------------------------------------- /examples/esp32/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32] 12 | platform = espressif32 13 | board = heltec_wifi_lora_32 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -std=gnu++17 -Wall -Wextra -O3 21 | build_unflags = -std=gnu++11 22 | 23 | 24 | lib_deps = 25 | https://github.com/ngraziano/LMICPP-Arduino.git 26 | -------------------------------------------------------------------------------- /examples/esp32/src/lorakeys.h: -------------------------------------------------------------------------------- 1 | 2 | // Application in string format. 3 | // For TTN issued EUIs the first bytes should be 70B3D5 4 | constexpr char const appEui[] = "70B3D50000000000"; 5 | 6 | // Device EUI in string format. 7 | constexpr char const devEui[] = "0000000000000000"; 8 | // Application key in string format. 9 | constexpr char const appKey[] = "00000000000000000000000000000000"; 10 | 11 | -------------------------------------------------------------------------------- /examples/esp32/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define DEVICE_TESTESP32 10 | #include "lorakeys.h" 11 | 12 | void do_send(); 13 | 14 | // Schedule TX every this many seconds (might become longer due to duty 15 | // cycle limitations). 16 | constexpr OsDeltaTime TX_INTERVAL = OsDeltaTime::from_sec(135); 17 | 18 | constexpr unsigned int BAUDRATE = 115200; 19 | // Pin mapping 20 | constexpr lmic_pinmap lmic_pins = { 21 | .nss = 18, 22 | .prepare_antenna_tx = nullptr, 23 | .rst = 14, 24 | .dio = {26, 33}, 25 | }; 26 | 27 | RadioSx1276 radio{lmic_pins}; 28 | LmicEu868 LMIC{radio}; 29 | 30 | OsTime nextSend; 31 | 32 | void onEvent(EventType ev) { 33 | switch (ev) { 34 | case EventType::JOINING: 35 | PRINT_DEBUG(2, F("EV_JOINING")); 36 | // LMIC.setDrJoin(0); 37 | break; 38 | case EventType::JOINED: 39 | PRINT_DEBUG(2, F("EV_JOINED")); 40 | // disable ADR because if will be mobile. 41 | // LMIC.setLinkCheckMode(false); 42 | break; 43 | case EventType::JOIN_FAILED: 44 | PRINT_DEBUG(2, F("EV_JOIN_FAILED")); 45 | break; 46 | case EventType::TXCOMPLETE: 47 | PRINT_DEBUG(2, F("EV_TXCOMPLETE (includes waiting for RX windows)")); 48 | if (LMIC.getTxRxFlags().test(TxRxStatus::ACK)) { 49 | PRINT_DEBUG(1, F("Received ack")); 50 | } 51 | if (LMIC.getDataLen()) { 52 | PRINT_DEBUG(1, F("Received %d bytes of payload"), LMIC.getDataLen()); 53 | auto data = LMIC.getData(); 54 | if (data) { 55 | uint8_t port = LMIC.getPort(); 56 | } 57 | } 58 | break; 59 | case EventType::RESET: 60 | PRINT_DEBUG(2, F("EV_RESET")); 61 | break; 62 | case EventType::LINK_DEAD: 63 | PRINT_DEBUG(2, F("EV_LINK_DEAD")); 64 | break; 65 | case EventType::LINK_ALIVE: 66 | PRINT_DEBUG(2, F("EV_LINK_ALIVE")); 67 | break; 68 | default: 69 | PRINT_DEBUG(2, F("Unknown event")); 70 | break; 71 | } 72 | } 73 | 74 | void do_send() { 75 | // Some analog value 76 | // val = analogRead(A1) >> 4; 77 | uint8_t val = temperatureRead(); 78 | // Prepare upstream data transmission at the next possible time. 79 | LMIC.setTxData2(2, &val, 1, false); 80 | PRINT_DEBUG(1, F("Packet queued")); 81 | nextSend = os_getTime() + TX_INTERVAL; 82 | } 83 | 84 | void setup() { 85 | 86 | if (debugLevel > 0) { 87 | Serial.begin(BAUDRATE); 88 | } 89 | 90 | SPI.begin(); 91 | // LMIC init 92 | os_init(); 93 | LMIC.init(); 94 | // Reset the MAC state. Session and pending data transfers will be discarded. 95 | LMIC.reset(); 96 | LMIC.setEventCallBack(onEvent); 97 | SetupLmicKey::setup(LMIC); 98 | 99 | // set clock error to allow good connection. 100 | LMIC.setClockError(MAX_CLOCK_ERROR * 3 / 100); 101 | LMIC.setAntennaPowerAdjustment(-14); 102 | 103 | // Start job (sending automatically starts OTAA too) 104 | nextSend = os_getTime(); 105 | } 106 | 107 | void loop() { 108 | 109 | OsDeltaTime freeTimeBeforeNextCall = LMIC.run(); 110 | 111 | if (freeTimeBeforeNextCall > OsDeltaTime::from_ms(10)) { 112 | // we have more than 10 ms to do some work. 113 | // the test must be adapted from the time spend in other task 114 | if (nextSend < os_getTime()) { 115 | if (LMIC.getOpMode().test(OpState::TXRXPEND)) { 116 | PRINT_DEBUG(1, F("OpState::TXRXPEND, not sending")); 117 | } else { 118 | do_send(); 119 | } 120 | } else { 121 | OsDeltaTime freeTimeBeforeSend = nextSend - os_getTime(); 122 | OsDeltaTime to_wait = 123 | std::min(freeTimeBeforeNextCall, freeTimeBeforeSend); 124 | delay(to_wait.to_ms() / 2); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /examples/esp32_classC/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/esp32_classC/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | 8 | lora-cppcheck-build-dir/ 9 | -------------------------------------------------------------------------------- /examples/esp32_classC/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } -------------------------------------------------------------------------------- /examples/esp32_classC/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.env.windows": { 3 | "PLATFORMIO_CALLER": "vscode" 4 | }, 5 | "files.associations": { 6 | "functional": "cpp", 7 | "basic_definitions": "cpp", 8 | "valarray": "cpp", 9 | "ios": "cpp", 10 | "bitset": "cpp", 11 | "memory": "cpp", 12 | "associative_base": "cpp", 13 | "deque": "cpp", 14 | "initializer_list": "cpp", 15 | "list": "cpp", 16 | "vector": "cpp", 17 | "sstream": "cpp", 18 | "string": "cpp", 19 | "locale": "cpp", 20 | "algorithm": "cpp", 21 | "iterator": "cpp", 22 | "complex": "cpp", 23 | "cctype": "cpp", 24 | "char_traits": "cpp", 25 | "cmath": "cpp", 26 | "cstddef": "cpp", 27 | "cstdio": "cpp", 28 | "cstdlib": "cpp", 29 | "cstring": "cpp", 30 | "cwchar": "cpp", 31 | "cwctype": "cpp", 32 | "exception": "cpp", 33 | "func_exception": "cpp", 34 | "iomanip": "cpp", 35 | "iosfwd": "cpp", 36 | "iostream": "cpp", 37 | "istream": "cpp", 38 | "istream_helpers": "cpp", 39 | "iterator_base": "cpp", 40 | "limits": "cpp", 41 | "map": "cpp", 42 | "new": "cpp", 43 | "numeric": "cpp", 44 | "ostream": "cpp", 45 | "ostream_helpers": "cpp", 46 | "queue": "cpp", 47 | "serstream": "cpp", 48 | "set": "cpp", 49 | "stack": "cpp", 50 | "stdexcept": "cpp", 51 | "streambuf": "cpp", 52 | "string_iostream": "cpp", 53 | "type_traits": "cpp", 54 | "typeinfo": "cpp", 55 | "utility": "cpp" 56 | }, 57 | } -------------------------------------------------------------------------------- /examples/esp32_classC/README.md: -------------------------------------------------------------------------------- 1 | # Class C example for ESP32 2 | 3 | This is a simple exemple for ESP32 with arduino framework. 4 | It activate class C mode and can receive between send windows 5 | 6 | ## Specific to ESP32 7 | 8 | It use hardware AES for encoding. 9 | 10 | ## Usage 11 | 12 | Work with platformio. 13 | 14 | Open with platformio (VSCODE with Platformio extension) 15 | 16 | In ``src`` directory create a file named ``lorakeys.h`` wich contain the keys declared in network (for exemple ) 17 | 18 | Exemple of file: 19 | 20 | ```cpp 21 | // Application in string format. 22 | // For TTN issued EUIs the first bytes should be 70B3D5 23 | constexpr char const appEui[] = "70B3D5XXXXXXXXXX"; 24 | 25 | // Device EUI in string format. 26 | constexpr char const devEui[] = "XXXXXXXXXXXXXXXX"; 27 | // Application key in string format. 28 | constexpr char const appKey[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 29 | 30 | ``` 31 | 32 | In ``main.cpp`` replace the content of ``do_send()`` with the data you want to send. 33 | 34 | Check the pin configuration for your board in ``lmic_pins`` structure. -------------------------------------------------------------------------------- /examples/esp32_classC/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32] 12 | platform = espressif32 13 | board = heltec_wifi_lora_32 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE -DLMIC_DEBUG_LEVEL=1 21 | 22 | 23 | lib_deps = 24 | https://github.com/ngraziano/LMICPP-Arduino.git 25 | thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays @ ^4.2.1 26 | -------------------------------------------------------------------------------- /examples/esp32_classC/src/lorakeys.h: -------------------------------------------------------------------------------- 1 | 2 | // Application in string format. 3 | // For TTN issued EUIs the first bytes should be 70B3D5 4 | constexpr char const appEui[] = "70B3D50000000000"; 5 | 6 | // Device EUI in string format. 7 | constexpr char const devEui[] = "0000000000000000"; 8 | // Application key in string format. 9 | constexpr char const appKey[] = "00000000000000000000000000000000"; 10 | 11 | -------------------------------------------------------------------------------- /examples/rak811_gps/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/rak811_gps/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/.browse.c_cpp.2.db* 6 | .vscode/c_cpp_properties.json 7 | .vscode/launch.json 8 | .vscode/ipch 9 | *.lst 10 | /test 11 | -------------------------------------------------------------------------------- /examples/rak811_gps/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/rak811_gps/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.env.windows": { 3 | "PLATFORMIO_CALLER": "vscode" 4 | }, 5 | "files.associations": { 6 | "algorithm": "cpp", 7 | "initializer_list": "cpp", 8 | "sstream": "cpp", 9 | "memory": "cpp", 10 | "new": "cpp" 11 | } 12 | } -------------------------------------------------------------------------------- /examples/rak811_gps/platformio-ci.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | [env:rak811] 13 | platform = ststm32 14 | board = rak811_tracker_32 15 | framework = arduino 16 | 17 | upload_protocol = stlink 18 | 19 | build_flags = -DENABLE_SAVE_RESTORE 20 | 21 | lib_deps = 22 | https://github.com/ngraziano/SparkFun_u-blox_GNSS_Arduino_Library.git#forraktracker 23 | STM32duino Low Power 24 | STM32duino RTC 25 | 26 | -------------------------------------------------------------------------------- /examples/rak811_gps/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | [env:rak811] 13 | #platform = https://github.com/platformio/platform-ststm32.git 14 | platform = ststm32 15 | board = rak811_tracker_32 16 | framework = arduino 17 | 18 | upload_protocol = stlink 19 | # upload_port = COM30 20 | 21 | debug_tool = stlink 22 | 23 | monitor_port = COM3 24 | monitor_speed = 115200 25 | monitor_filters = time 26 | 27 | build_flags = -O3 -Wall -Wextra -DENABLE_SAVE_RESTORE 28 | 29 | 30 | lib_deps = 31 | ngraziano/LMICPP-Arduino 32 | # sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.0.17 33 | https://github.com/ngraziano/SparkFun_u-blox_GNSS_Arduino_Library.git#forraktracker 34 | STM32duino Low Power 35 | STM32duino RTC 36 | 37 | -------------------------------------------------------------------------------- /examples/simple/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/.browse.c_cpp.2.db* 6 | .vscode/c_cpp_properties.json 7 | .vscode/launch.json 8 | .vscode/ipch 9 | -------------------------------------------------------------------------------- /examples/simple/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/simple/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:pro8MHzatmega328] 12 | platform = atmelavr 13 | board = ATmega328P 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -std=c++17 -Wall -Wextra -O3 21 | # upload_speed = 38400 22 | 23 | lib_deps = 24 | https://github.com/ngraziano/avr_stl.git 25 | ngraziano/LMICPP-Arduino 26 | -------------------------------------------------------------------------------- /examples/simple/src/lorakeys.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #if defined(DEVICE_SIMPLE) 6 | // Application in string format. 7 | // For TTN issued EUIs the first bytes should be 70B3D5 8 | constexpr char const appEui[] = "70B3D50000000000"; 9 | 10 | // Device EUI in string format. 11 | constexpr char const devEui[] = "0000000000000000"; 12 | // Application key in string format. 13 | constexpr char const appKey[] = "00000000000000000000000000000000"; 14 | 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /examples/simple/src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define DEVICE_SIMPLE 12 | #include "lorakeys.h" 13 | 14 | // Schedule TX every this many seconds (might become longer due to duty 15 | // cycle limitations). 16 | constexpr OsDeltaTime TX_INTERVAL = OsDeltaTime::from_sec(135); 17 | 18 | constexpr unsigned int BAUDRATE = 19200; 19 | 20 | // Pin mapping 21 | constexpr lmic_pinmap lmic_pins = { 22 | .nss = 10, 23 | .prepare_antenna_tx = nullptr, 24 | .rst = 14, 25 | .dio = {9, 8}, 26 | }; 27 | 28 | RadioSx1276 radio{lmic_pins}; 29 | LmicEu868 LMIC{radio}; 30 | 31 | OsTime nextSend; 32 | 33 | void do_send() 34 | { 35 | // battery 36 | uint8_t val = ((uint32_t)analogRead(A1)) * 255 / 683; 37 | 38 | // Prepare upstream data transmission at the next possible time. 39 | LMIC.setTxData2(2, &val, 1, false); 40 | PRINT_DEBUG(1, F("Packet queued")); 41 | nextSend = hal_ticks() + TX_INTERVAL; 42 | } 43 | 44 | void setup() 45 | { 46 | if (debugLevel > 0) 47 | { 48 | Serial.begin(BAUDRATE); 49 | } 50 | 51 | SPI.begin(); 52 | // LMIC init 53 | os_init(); 54 | LMIC.init(); 55 | // Reset the MAC state. Session and pending data transfers will be discarded. 56 | LMIC.reset(); 57 | 58 | SetupLmicKey::setup(LMIC); 59 | // set clock error to allow good connection. 60 | LMIC.setClockError(MAX_CLOCK_ERROR * 3 / 100); 61 | // reduce power 62 | // LMIC.setAntennaPowerAdjustment(-14); 63 | 64 | // Start job (sending automatically starts OTAA too) 65 | nextSend = os_getTime(); 66 | } 67 | 68 | void loop() 69 | { 70 | OsDeltaTime freeTimeBeforeNextCall = LMIC.run(); 71 | if (freeTimeBeforeNextCall < OsDeltaTime::from_ms(100)) 72 | { 73 | // not enought time, do nothing else. 74 | return; 75 | } 76 | 77 | if (LMIC.getOpMode().test(OpState::TXRXPEND)) 78 | { 79 | // OpState::TXRXPEND, do not try to send new message 80 | // so wait (with 100ms of marging) and return 81 | auto waittime = freeTimeBeforeNextCall.to_ms() - 100; 82 | PRINT_DEBUG(1, F("Delay TXRXPEND %dms"), waittime); 83 | delay(waittime); 84 | return; 85 | } 86 | 87 | // we have more than 100 ms to do some work. 88 | // the test must be adapted from the time spend in other task 89 | auto timebeforesend = nextSend - hal_ticks(); 90 | PRINT_DEBUG(1, F("Time before send %ds"), timebeforesend.to_s()); 91 | 92 | if (timebeforesend < OsDeltaTime(0)) 93 | { 94 | // time to send now. 95 | do_send(); 96 | } 97 | else 98 | { 99 | // sleep if we have nothing to do. 100 | auto sleeps = std::min(timebeforesend, freeTimeBeforeNextCall).to_s(); 101 | PRINT_DEBUG(1, F("Sleep %ds"), sleeps); 102 | delay(sleeps * 1000); 103 | } 104 | } -------------------------------------------------------------------------------- /examples/simple_rak811/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /examples/simple_rak811/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/.browse.c_cpp.2.db* 6 | .vscode/c_cpp_properties.json 7 | .vscode/launch.json 8 | .vscode/ipch 9 | *.lst 10 | /test 11 | -------------------------------------------------------------------------------- /examples/simple_rak811/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/simple_rak811/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.env.windows": { 3 | "PLATFORMIO_CALLER": "vscode" 4 | }, 5 | "files.associations": { 6 | "algorithm": "cpp", 7 | "initializer_list": "cpp", 8 | "sstream": "cpp" 9 | } 10 | } -------------------------------------------------------------------------------- /examples/simple_rak811/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | [env:rak811] 13 | #platform = https://github.com/platformio/platform-ststm32.git 14 | platform = ststm32 15 | board = rak811_tracker_32 16 | framework = arduino 17 | 18 | upload_protocol = stlink 19 | # upload_port = COM30 20 | 21 | debug_tool = stlink 22 | 23 | monitor_port = COM4 24 | monitor_speed = 9600 25 | 26 | build_flags = -Wall -Wextra -O3 27 | 28 | lib_deps = 29 | ngraziano/LMICPP-Arduino 30 | STM32duino Low Power@1.0.3 31 | STM32duino RTC 32 | 33 | -------------------------------------------------------------------------------- /examples/simple_rak811/src/lorakeys.h: -------------------------------------------------------------------------------- 1 | 2 | // Application in string format. 3 | // For TTN issued EUIs the first bytes should be 70B3D5 4 | constexpr char const appEui[] = "70B3D50000000000"; 5 | 6 | // Device EUI in string format. 7 | constexpr char const devEui[] = "0000000000000000"; 8 | // Application key in string format. 9 | constexpr char const appKey[] = "00000000000000000000000000000000"; 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/simple_rak811/src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "STM32LowPower.h" 11 | #include "lorakeys.h" 12 | 13 | // Schedule TX every this many seconds (might become longer due to duty 14 | // cycle limitations). 15 | constexpr uint32_t TX_INTERVAL = 140; 16 | 17 | constexpr unsigned int BAUDRATE = 9600; 18 | 19 | // Pin mapping 20 | const lmic_pinmap lmic_pins = { 21 | .nss = RADIO_NSS, 22 | .prepare_antenna_tx = 23 | [](bool isTx) { 24 | if (isTx) { 25 | digitalWrite(RADIO_RF_CTX_PA, HIGH); 26 | digitalWrite(RADIO_RF_CBT_HF, LOW); 27 | digitalWrite(RADIO_RF_CRX_RX, LOW); 28 | } else { 29 | digitalWrite(RADIO_RF_CTX_PA, LOW); 30 | digitalWrite(RADIO_RF_CBT_HF, LOW); 31 | digitalWrite(RADIO_RF_CRX_RX, HIGH); 32 | } 33 | }, // = LMIC_UNUSED_PIN, // 34 | .rst = RADIO_RESET, 35 | .dio = {RADIO_DIO_0, RADIO_DIO_1}, 36 | }; 37 | 38 | RadioSx1276 radio{lmic_pins}; 39 | LmicEu868 LMIC{radio}; 40 | 41 | uint32_t nextSendEpoch; 42 | 43 | STM32RTC &rtc = STM32RTC::getInstance(); 44 | 45 | void onEvent(EventType ev) { 46 | switch (ev) { 47 | case EventType::JOINING: 48 | PRINT_DEBUG(2, F("EV_JOINING")); 49 | // LMIC.setDrJoin(0); 50 | break; 51 | case EventType::JOINED: 52 | PRINT_DEBUG(2, F("EV_JOINED")); 53 | // disable ADR because it will be mobile. 54 | // LMIC.setLinkCheckMode(false); 55 | break; 56 | case EventType::JOIN_FAILED: 57 | PRINT_DEBUG(2, F("EV_JOIN_FAILED")); 58 | break; 59 | case EventType::TXCOMPLETE: 60 | PRINT_DEBUG(2, F("EV_TXCOMPLETE (includes waiting for RX windows)")); 61 | if (LMIC.getTxRxFlags().test(TxRxStatus::ACK)) { 62 | PRINT_DEBUG(1, F("Received ack")); 63 | } 64 | if (LMIC.getDataLen()) { 65 | PRINT_DEBUG(1, F("Received %d bytes of payload"), LMIC.getDataLen()); 66 | auto data = LMIC.getData(); 67 | if (data) { 68 | uint8_t port = LMIC.getPort(); 69 | } 70 | } 71 | break; 72 | case EventType::RESET: 73 | PRINT_DEBUG(2, F("EV_RESET")); 74 | break; 75 | case EventType::LINK_DEAD: 76 | PRINT_DEBUG(2, F("EV_LINK_DEAD")); 77 | break; 78 | case EventType::LINK_ALIVE: 79 | PRINT_DEBUG(2, F("EV_LINK_ALIVE")); 80 | break; 81 | default: 82 | PRINT_DEBUG(2, F("Unknown event")); 83 | break; 84 | } 85 | } 86 | 87 | void do_send() { 88 | // battery 89 | // uint8_t val = ((uint32_t)analogRead(A1)) * 255 / 683; 90 | uint32_t bat_value = 100; 91 | PRINT_DEBUG(1, F("Batterie value %i"), bat_value); 92 | uint8_t val = bat_value * 255 / 3000; 93 | 94 | // Prepare upstream data transmission at the next possible time. 95 | LMIC.setTxData2(3, &val, 1, false); 96 | PRINT_DEBUG(1, F("Packet queued")); 97 | nextSendEpoch = rtc.getEpoch() + TX_INTERVAL; 98 | } 99 | 100 | void setup() { 101 | LowPower.begin(); 102 | rtc.begin(true); 103 | 104 | if (debugLevel > 0) { 105 | Serial.begin(BAUDRATE); 106 | } 107 | 108 | // Enable RF switch Pin control 109 | pinMode(RADIO_RF_CTX_PA, OUTPUT); 110 | pinMode(RADIO_RF_CBT_HF, OUTPUT); 111 | pinMode(RADIO_RF_CRX_RX, OUTPUT); 112 | 113 | // enable XTAL for RF 114 | pinMode(RADIO_XTAL_EN, OUTPUT); 115 | digitalWrite(RADIO_XTAL_EN, 1); 116 | 117 | SPI.begin(); 118 | // LMIC init 119 | os_init(); 120 | LMIC.init(); 121 | // Reset the MAC state. Session and pending data transfers will be discarded. 122 | LMIC.reset(); 123 | 124 | LMIC.setEventCallBack(onEvent); 125 | SetupLmicKey::setup(LMIC); 126 | // set clock error to allow good connection. 127 | LMIC.setClockError(MAX_CLOCK_ERROR * 1 / 100); 128 | // reduce power 129 | // LMIC.setAntennaPowerAdjustment(-14); 130 | 131 | // first send 132 | nextSendEpoch = rtc.getEpoch(); 133 | } 134 | 135 | void goToSleep(uint32_t nb_sec_to_sleep) { 136 | if (nb_sec_to_sleep < 20) { 137 | return; 138 | } 139 | PRINT_DEBUG(1, F("Sleep %ds"), nb_sec_to_sleep); 140 | auto const start_sleep_time = rtc.getEpoch(); 141 | LowPower.deepSleep(nb_sec_to_sleep * 1000); 142 | auto const end_sleep_time = rtc.getEpoch(); 143 | hal_add_time_in_sleep( 144 | OsDeltaTime::from_sec(end_sleep_time - start_sleep_time)); 145 | } 146 | 147 | void goToSleep(OsDeltaTime time_to_sleep) { goToSleep(time_to_sleep.to_s()); } 148 | 149 | void loop() { 150 | 151 | OsDeltaTime to_wait = LMIC.run(); 152 | if (to_wait < OsDeltaTime::from_ms(100)) { 153 | // do not try to do something if we have less than 100ms 154 | return; 155 | } 156 | 157 | if (LMIC.getOpMode().test(OpState::TXRXPEND)) { 158 | // a value already waiting to be sent 159 | // just sleep 160 | goToSleep(to_wait); 161 | return; 162 | } 163 | 164 | int32_t timebeforesend = nextSendEpoch - rtc.getEpoch(); 165 | if (timebeforesend < 0) { 166 | do_send(); 167 | } else { 168 | goToSleep(std::min(timebeforesend, to_wait.to_s())); 169 | } 170 | } -------------------------------------------------------------------------------- /examples/simple_sx1262/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/.browse.c_cpp.2.db* 6 | .vscode/c_cpp_properties.json 7 | .vscode/launch.json 8 | .vscode/ipch 9 | -------------------------------------------------------------------------------- /examples/simple_sx1262/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } -------------------------------------------------------------------------------- /examples/simple_sx1262/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:pro8MHzatmega328] 12 | platform = atmelavr 13 | board = ATMEGA328P 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -Wall -Wextra -O3 21 | 22 | board_build.f_cpu = 8000000L 23 | upload_speed = 38400 24 | 25 | lib_deps = 26 | https://github.com/ngraziano/avr_stl.git 27 | https://github.com/ngraziano/LMICPP-Arduino.git 28 | -------------------------------------------------------------------------------- /examples/simple_sx1262/src/lorakeys.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #if defined(DEVICE_SIMPLE_SX1262) 6 | // Application in string format. 7 | // For TTN issued EUIs the first bytes should be 70B3D5 8 | constexpr char const appEui[] = "70B3D50000000000"; 9 | 10 | // Device EUI in string format. 11 | constexpr char const devEui[] = "0000000000000000"; 12 | // Application key in string format. 13 | constexpr char const appKey[] = "00000000000000000000000000000000"; 14 | 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /examples/simple_sx1262/src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define DEVICE_SIMPLE_SX1262 12 | #include "lorakeys.h" 13 | 14 | // Schedule TX every this many seconds (might become longer due to duty 15 | // cycle limitations). 16 | constexpr OsDeltaTime TX_INTERVAL = OsDeltaTime::from_sec(135); 17 | constexpr unsigned int BAUDRATE = 9600; 18 | 19 | // Pin mapping 20 | constexpr lmic_pinmap lmic_pins = { 21 | .nss = 10, 22 | .prepare_antenna_tx = nullptr, 23 | .rst = 14, 24 | .dio = {/* busy */ 9, /* DIO1 */ 8}, 25 | }; 26 | // Radio class for SX1262 27 | RadioSx1262 radio{lmic_pins, ImageCalibrationBand::band_863_870}; 28 | // Create an LMIC object with the right band 29 | LmicEu868 LMIC{radio}; 30 | OsTime nextSend; 31 | 32 | 33 | void do_send() 34 | { 35 | // some value 36 | uint8_t val = ((uint32_t)analogRead(A1)) * 255 / 683; 37 | 38 | // Prepare upstream data transmission at the next possible time. 39 | LMIC.setTxData2(2, &val, 1, false); 40 | PRINT_DEBUG(1, F("Packet queued")); 41 | nextSend = hal_ticks() + TX_INTERVAL; 42 | } 43 | 44 | // lmic_pins.dio[0] = 9 => PCINT1 45 | // lmic_pins.dio[1] = 8 => PCINT0 46 | // PCI2 PCINT[23:16] 47 | // PCI1 PCINT[14:8] 48 | // PCI0 PCINT[7:0] 49 | 50 | /* INTERUPT ON CHANGE OPTIONAL 51 | ISR(PCINT0_vect) 52 | { 53 | // one of pins D8 to D13 has changed 54 | // store time, will be check in LMIC.run() 55 | LMIC.store_trigger(); 56 | } 57 | 58 | void pciSetup(byte pin) 59 | { 60 | *digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin)); // enable pin 61 | PCIFR |= bit(digitalPinToPCICRbit(pin)); // clear any outstanding interrupt 62 | PCICR |= bit(digitalPinToPCICRbit(pin)); // enable interrupt for the group 63 | } 64 | 65 | INTERUPT ON CHANGE OPTIONAL 66 | */ 67 | void setup() 68 | { 69 | // To handle VCC <= 2.4v 70 | // clock start at 8MHz / 8 => 1 MHz 71 | // set clock to 8MHz / 4 => 2MHz 72 | // maybe 4Mhz could also work 73 | // clock_prescale_set(clock_div_4); 74 | if (debugLevel > 0) 75 | { 76 | Serial.begin(BAUDRATE); 77 | } 78 | 79 | if (debugLevel > 0) 80 | { 81 | Serial.begin(BAUDRATE); 82 | } 83 | 84 | // INTERUPT ON CHANGE OPTIONAL 85 | // pciSetup(lmic_pins.dio[1]); 86 | 87 | SPI.begin(); 88 | // LMIC init 89 | os_init(); 90 | LMIC.init(); 91 | // Reset the MAC state. Session and pending data transfers will be discarded. 92 | LMIC.reset(); 93 | 94 | SetupLmicKey::setup(LMIC); 95 | // set clock error to allow good connection. 96 | LMIC.setClockError(MAX_CLOCK_ERROR * 1 / 100); 97 | 98 | // Start job (sending automatically starts OTAA too) 99 | nextSend = os_getTime(); 100 | PRINT_DEBUG(1, F("END SETUP")); 101 | 102 | } 103 | 104 | void loop() 105 | { 106 | OsDeltaTime freeTimeBeforeNextCall = LMIC.run(); 107 | if (freeTimeBeforeNextCall < OsDeltaTime::from_ms(100)) 108 | { 109 | // not enought time, do nothing else. 110 | return; 111 | } 112 | 113 | if (LMIC.getOpMode().test(OpState::TXRXPEND)) 114 | { 115 | // OpState::TXRXPEND, do not try to send new message 116 | // so wait (with 100ms of marging) and return 117 | auto waittime = freeTimeBeforeNextCall.to_ms()-100; 118 | PRINT_DEBUG(1, F("Delay TXRXPEND %dms"), waittime); 119 | delay(waittime); 120 | return; 121 | } 122 | 123 | // we have more than 100 ms to do some work. 124 | // the test must be adapted from the time spend in other task 125 | auto timebeforesend = nextSend - hal_ticks(); 126 | PRINT_DEBUG(1, F("Time before send %ds"), timebeforesend.to_s()); 127 | 128 | if (timebeforesend < OsDeltaTime(0) ) 129 | { 130 | // time to send now. 131 | do_send(); 132 | } 133 | else 134 | { 135 | // sleep if we have nothing to do. 136 | auto sleeps = std::min(timebeforesend, freeTimeBeforeNextCall).to_s(); 137 | PRINT_DEBUG(1, F("Sleep %ds"), sleeps); 138 | delay(sleeps * 1000); 139 | } 140 | } -------------------------------------------------------------------------------- /examples/tempsensor/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | 8 | 9 | src/lorakeys.h 10 | 11 | lora-cppcheck-build-dir/ 12 | -------------------------------------------------------------------------------- /examples/tempsensor/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } -------------------------------------------------------------------------------- /examples/tempsensor/lib/sleepandwatchdog/sleepandwatchdog.cpp: -------------------------------------------------------------------------------- 1 | #include "sleepandwatchdog.h" 2 | 3 | #include "Arduino.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace 10 | { 11 | volatile bool wdtEnable = false; 12 | } 13 | 14 | void powerDown(Sleep period) 15 | { 16 | bool back = wdtEnable; 17 | wdtEnable = false; 18 | ADCSRA &= ~(1 << ADEN); 19 | 20 | if (period != Sleep::FOREVER) 21 | { 22 | wdt_enable(static_cast(period)); 23 | WDTCSR |= (1 << WDIE); 24 | } 25 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 26 | cli(); 27 | sleep_enable(); 28 | sleep_bod_disable(); 29 | sei(); 30 | sleep_cpu(); 31 | sleep_disable(); 32 | sei(); 33 | ADCSRA |= (1 << ADEN); 34 | 35 | if (back) 36 | configure_wdt(); 37 | } 38 | 39 | void configure_wdt() 40 | { 41 | wdtEnable = true; 42 | wdt_enable(WDTO_8S); 43 | WDTCSR |= (1 << WDIE); 44 | } 45 | 46 | void rst_wdt() 47 | { 48 | wdt_reset(); 49 | } 50 | 51 | ISR(WDT_vect) 52 | { 53 | if (!wdtEnable) 54 | { 55 | // WDIE & WDIF is cleared in hardware upon entering this ISR 56 | wdt_disable(); 57 | } 58 | else 59 | { 60 | // enable watchdog without interupt to reboot 61 | wdt_enable(static_cast(Sleep::P8S)); 62 | // reboot 63 | while (1) 64 | ; 65 | } 66 | } -------------------------------------------------------------------------------- /examples/tempsensor/lib/sleepandwatchdog/sleepandwatchdog.h: -------------------------------------------------------------------------------- 1 | #ifndef _sleepandwatchdog_h_ 2 | #define _sleepandwatchdog_h_ 3 | 4 | enum class Sleep : unsigned char 5 | { 6 | P15MS, 7 | P30MS, 8 | P60MS, 9 | P120MS, 10 | P250MS, 11 | P500MS, 12 | P1S, 13 | P2S, 14 | P4S, 15 | P8S, 16 | FOREVER 17 | }; 18 | 19 | void powerDown(Sleep period); 20 | void configure_wdt(); 21 | void rst_wdt(); 22 | 23 | #endif -------------------------------------------------------------------------------- /examples/tempsensor/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:pro8MHzatmega328] 12 | platform = atmelavr 13 | board = ATmega328P 14 | framework = arduino 15 | upload_port = COM9 16 | 17 | monitor_port = COM9 18 | monitor_speed = 19200 19 | 20 | build_flags = -Wall -Wextra -O3 21 | upload_speed = 38400 22 | 23 | lib_deps = 24 | https://github.com/ngraziano/avr_stl.git 25 | DallasTemperature 26 | https://github.com/ngraziano/LMICPP-Arduino.git 27 | -------------------------------------------------------------------------------- /examples/tempsensor/src/powersave.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "powersave.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const int64_t sleepAdj = 1080; 9 | 10 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt) { 11 | OsDeltaTime duration_selected; 12 | Sleep period_selected; 13 | // these value are base on test 14 | if (maxTime > OsDeltaTime::from_ms(8700)) { 15 | duration_selected = OsDeltaTime::from_ms(8000 * sleepAdj / 1000); 16 | period_selected = Sleep::P8S; 17 | } else if (maxTime > OsDeltaTime::from_ms(4600)) { 18 | duration_selected = OsDeltaTime::from_ms(4000 * sleepAdj / 1000); 19 | period_selected = Sleep::P4S; 20 | } else if (maxTime > OsDeltaTime::from_ms(2600)) { 21 | duration_selected = OsDeltaTime::from_ms(2000 * sleepAdj / 1000); 22 | period_selected = Sleep::P2S; 23 | } else if (maxTime > OsDeltaTime::from_ms(1500)) { 24 | duration_selected = OsDeltaTime::from_ms(1000 * sleepAdj / 1000); 25 | period_selected = Sleep::P1S; 26 | } else if (maxTime > OsDeltaTime::from_ms(800)) { 27 | duration_selected = OsDeltaTime::from_ms(500 * sleepAdj / 1000); 28 | period_selected = Sleep::P500MS; 29 | } else if (maxTime > OsDeltaTime::from_ms(500)) { 30 | duration_selected = OsDeltaTime::from_ms(250 * sleepAdj / 1000); 31 | period_selected = Sleep::P250MS; 32 | } else { 33 | return; 34 | } 35 | 36 | PRINT_DEBUG(1, F("Sleep (ostick) :%lix%i"), duration_selected.to_ms(), 37 | maxTime / duration_selected); 38 | if (debugLevel > 0) { 39 | Serial.flush(); 40 | } 41 | 42 | bool stopsleep = false; 43 | for (uint16_t nbsleep = maxTime / duration_selected; 44 | nbsleep > 0 && !stopsleep; nbsleep--) { 45 | powerDown(period_selected); 46 | hal_add_time_in_sleep(duration_selected); 47 | stopsleep = interrupt(); 48 | } 49 | PRINT_DEBUG(1, F("Wakeup")); 50 | } 51 | -------------------------------------------------------------------------------- /examples/tempsensor/src/powersave.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _powersave_h_ 3 | #define _powersave_h_ 4 | 5 | class OsDeltaTime; 6 | 7 | using stopsleepcb_t = bool (*)(); 8 | 9 | void powersave(OsDeltaTime maxTime, stopsleepcb_t interrupt); 10 | 11 | #endif -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LMICPP-Arduino", 3 | "version": "2.5.1", 4 | "keywords": "Lora", 5 | "description": "Modified Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. Changed to C++ format.", 6 | "frameworks": ["arduino"], 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/ngraziano/LMICPP-Arduino.git" 10 | }, 11 | "examples": [ 12 | { 13 | "name": "Simple", 14 | "base": "examples/simple", 15 | "files": [ 16 | "platformio.ini", 17 | "src/main.cpp", 18 | "src/lorakeys.h" 19 | ] 20 | } 21 | ], 22 | "dependencies": [] 23 | } 24 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=LMICPP-Arduino 2 | version=2.5.1 3 | author=IBM 4 | maintainer=Nicolas Graziano 5 | sentence=Modified Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. Changed to C++ format. 6 | paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers 7 | category=Communication 8 | url=https://github.com/ngraziano/LMICPP-Arduino 9 | architectures=* 10 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:atmega328P8MHz] 12 | platform = atmelavr 13 | board = ATMEGA328P 14 | framework = arduino 15 | 16 | board_build.f_cpu = 1000000L 17 | #board_build.f_cpu = 8000000L 18 | upload_speed = 9600 19 | #upload_speed = 57600 20 | upload_port = COM3 21 | # test_port= COM3 22 | test_speed=9600 23 | 24 | monitor_port = COM3 25 | monitor_speed = 9600 26 | 27 | build_flags = -std=c++17 -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE -DLMIC_104_EXPERIMENTAL 28 | 29 | test_build_src = true 30 | 31 | lib_deps = 32 | https://github.com/ngraziano/avr_stl.git 33 | 34 | [env:esp32] 35 | platform = espressif32 36 | board = heltec_wifi_lora_32 37 | framework = arduino 38 | upload_port = COM9 39 | 40 | monitor_port = COM9 41 | monitor_speed = 19200 42 | test_build_src=true 43 | build_flags = -std=c++17 -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE -DLMIC_104_EXPERIMENTAL 44 | 45 | lib_deps = 46 | 47 | 48 | [env:windows] 49 | platform = windows_x86 50 | # lib_compat_mode = off 51 | build_flags = -std=c++17 -Wall -Wextra -O3 -DLMIC_DEBUG_LEVEL=0 -DENABLE_SAVE_RESTORE 52 | -DLMIC_104_EXPERIMENTAL -DLMIC_RX_RAMPUP_MS=1 -DLMIC_TX_RAMPUP_MS=1 53 | -DLMIC_MAX_BUFFER_LENGTH=255 54 | test_build_src=true 55 | lib_deps = 56 | # debug_test = cert/test_device_functionality 57 | 58 | [env:native] 59 | platform = native 60 | extra_scripts = 61 | pre:add_msys_path.py 62 | post:copy_msys_dlls.py 63 | # lib_compat_mode = off 64 | build_flags = -std=c++17 -Wall -Wextra -O3 65 | -DLMIC_DEBUG_LEVEL=0 66 | -DENABLE_SAVE_RESTORE 67 | -DLMIC_104_EXPERIMENTAL 68 | -DLMIC_RX_RAMPUP_MS=1 69 | -DLMIC_TX_RAMPUP_MS=1 70 | -DLMIC_MAX_BUFFER_LENGTH=255 71 | test_build_src=true 72 | lib_deps = 73 | 74 | 75 | [env:bluepill] 76 | platform = ststm32 77 | board = bluepill_f103c8_128k 78 | framework = arduino 79 | upload_protocol = stlink 80 | debug_tool = stlink 81 | test_build_src=true 82 | 83 | build_flags = -std=c++17 -Wall -Wextra -O3 -DLMIC_DEBUG_LEVEL=0 -DENABLE_SAVE_RESTORE -DENABLE_SAVE_RESTORE 84 | lib_deps = 85 | -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /src/aes/aes_encrypt.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef aes_aes_encrypt_h 3 | #define aes_aes_encrypt_h 4 | 5 | #include 6 | #include 7 | 8 | constexpr uint8_t key_size = 16; 9 | using AesKey = std::array; 10 | 11 | void aes_tiny_128_encrypt(uint8_t *buffer, AesKey const &key); 12 | void aes_tiny_128_decrypt(uint8_t *buffer, AesKey const &key); 13 | 14 | #ifdef ARDUINO_ARCH_ESP32 15 | void aes_esp_128_encrypt(uint8_t *buffer, AesKey const &key); 16 | constexpr auto aes_128_encrypt = aes_esp_128_encrypt; 17 | #else 18 | constexpr auto aes_128_encrypt = aes_tiny_128_encrypt; 19 | #endif 20 | 21 | #endif -------------------------------------------------------------------------------- /src/aes/asp_esp.cpp: -------------------------------------------------------------------------------- 1 | 2 | #ifdef ARDUINO_ARCH_ESP32 3 | #include "aes_encrypt.h" 4 | #include "mbedtls/aes.h" 5 | 6 | 7 | void aes_esp_128_encrypt(uint8_t *buffer, AesKey const &key) { 8 | mbedtls_aes_context keyCtx; 9 | mbedtls_aes_init(&keyCtx); 10 | mbedtls_aes_setkey_enc(&keyCtx, key.data(), 128); 11 | mbedtls_aes_crypt_ecb(&keyCtx, ESP_AES_ENCRYPT, buffer, buffer); 12 | mbedtls_aes_free(&keyCtx); 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /src/aes/lmic_aes.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef aes_aes_h 3 | #define aes_aes_h 4 | #include "../lmic/bufferpack.h" 5 | #include "../lmic/config.h" 6 | #include "../lmic/lorabase.h" 7 | #include "aes_encrypt.h" 8 | 9 | #include 10 | 11 | // ====================================================================== 12 | // AES support 13 | 14 | constexpr uint8_t AES_BLCK_SIZE = 16; 15 | using AesBlock = std::array; 16 | 17 | class Aes { 18 | private: 19 | AesKey AESDevKey = {0}; 20 | // network session key 21 | AesKey nwkSKey = {0}; 22 | // application session key 23 | AesKey appSKey = {0}; 24 | 25 | static AesBlock micB0(uint32_t devaddr, uint32_t seqno, PktDir dndir, uint8_t len); 26 | static void aes_cmac(const uint8_t *buf, uint8_t len, bool prepend_aux, 27 | AesKey const &key, AesBlock &result); 28 | 29 | public: 30 | /* Set device key 31 | * Key is copied. 32 | */ 33 | void setDevKey(AesKey const &key); 34 | void setNetworkSessionKey(AesKey const &key); 35 | void setApplicationSessionKey(AesKey const &key); 36 | bool verifyMic(uint32_t devaddr, uint32_t seqno, PktDir dndir, 37 | const uint8_t *pdu, uint8_t len) const; 38 | bool verifyMic0(uint8_t const *pdu, uint8_t len) const; 39 | void framePayloadEncryption(uint8_t port, uint32_t devaddr, uint32_t seqno, 40 | PktDir dndir, uint8_t *payload, 41 | uint8_t len) const; 42 | void encrypt(uint8_t *pdu, uint8_t len) const; 43 | void sessKeys(uint16_t devnonce, uint8_t const *artnonce); 44 | void appendMic(uint32_t devaddr, uint32_t seqno, PktDir dndir, uint8_t *pdu, 45 | uint8_t len) const; 46 | void appendMic0(uint8_t *pdu, uint8_t len) const; 47 | void saveState(StoringAbtract &store) const; 48 | void loadState(RetrieveAbtract &store); 49 | }; 50 | 51 | #endif // __aes_h__ -------------------------------------------------------------------------------- /src/boardconfig.h: -------------------------------------------------------------------------------- 1 | #ifndef boardconfig_h 2 | #define boardconfig_h 3 | 4 | #define LMIC_GENERIC 1 5 | #define LMIC_ARDUINO 2 6 | #define LMIC_ESP32 3 7 | 8 | #ifdef ARDUINO 9 | 10 | 11 | #ifndef LMIC_HAL_IO 12 | // Same IO style for all boards 13 | #define LMIC_HAL_IO LMIC_ARDUINO 14 | #endif 15 | 16 | 17 | // ESP32 ans ESP8266 using Arduino : use gettimeofday 18 | #if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) 19 | #ifndef LMIC_HAL 20 | #define LMIC_HAL LMIC_ESP32 21 | #endif 22 | 23 | // Other arduino 24 | #else 25 | #ifndef LMIC_HAL 26 | #define LMIC_HAL LMIC_ARDUINO 27 | #endif 28 | 29 | #endif 30 | 31 | // not arduino just to compile 32 | #else 33 | #ifndef LMIC_HAL 34 | #define LMIC_HAL LMIC_GENERIC 35 | #endif 36 | 37 | 38 | #ifndef LMIC_HAL_IO 39 | #define LMIC_HAL_IO LMIC_GENERIC 40 | #endif 41 | 42 | 43 | #endif 44 | 45 | #endif -------------------------------------------------------------------------------- /src/certificationprotocol.h: -------------------------------------------------------------------------------- 1 | #ifndef certificateprotocol_h 2 | #define certificateprotocol_h 3 | 4 | #include "lmic.h" 5 | 6 | constexpr uint8_t certificationProtocolPort = 224; 7 | constexpr uint8_t certificationPackageID = 6; 8 | constexpr uint8_t certificationProtocolVersion = 1; 9 | 10 | using VersionDetails = std::array; 11 | 12 | constexpr VersionDetails LORA_1_0_4 = {1, 0, 4, 0}; 13 | constexpr VersionDetails LORA_1_0_3 = {1, 0, 3, 0}; 14 | 15 | class CertificationProtocol { 16 | enum class Request : uint8_t { 17 | PackageVersion = 0x00, 18 | DutReset = 0x01, 19 | DutJoin = 0x02, 20 | SwitchClass = 0x03, 21 | AdrBitChange = 0x04, 22 | RegionalDutyCycleCtrl = 0x05, 23 | TxPeriodicityChange = 0x06, 24 | TxFramesCtrl = 0x07, 25 | EchoPayload = 0x08, 26 | RxAppCnt = 0x09, 27 | RxAppCntReset = 0x0A, 28 | LinkCheck = 0x20, 29 | DeviceTime = 0x21, 30 | PingSlotInfo = 0x22, 31 | TxCw = 0x7D, 32 | DutFPort224Disable = 0x7E, 33 | DutVersions = 0x7F, 34 | 35 | }; 36 | 37 | enum class Response : uint8_t { 38 | PackageVersion = 0x00, 39 | EchoPayload = 0x08, 40 | RxAppCnt = 0x09, 41 | DutVersions = 0x7F, 42 | }; 43 | 44 | private: 45 | Lmic &lmic; 46 | 47 | bool nextFrameIsConfirmed = false; 48 | uint16_t rxAppCnt = 0; 49 | bool enableFPort224 = true; 50 | OsDeltaTime periodicity = OsDeltaTime(0); 51 | VersionDetails fwVersion = {}; 52 | VersionDetails lrwanVersion = LORA_1_0_3; 53 | VersionDetails lrwanRpVersion = {}; 54 | 55 | void handlePort224Message(Request req, const uint8_t *data, uint8_t size); 56 | 57 | void sendPackageVersion(); 58 | void dutReset(); 59 | void dutJoinReq(); 60 | void switchClass(uint8_t const *message, uint8_t size); 61 | void adrBitChange(uint8_t const *message, uint8_t size); 62 | void regionalDutyCycleCtrl(uint8_t const *message, uint8_t size); 63 | void txPeriodicityChange(uint8_t const *message, uint8_t size); 64 | void txFramesCtrl(uint8_t const *message, uint8_t size); 65 | void echoPayload(uint8_t const *message, uint8_t size); 66 | void sendRxAppCnt(); 67 | void rxAppCntReset(); 68 | void sendLinkCheck(); 69 | void sendDeviceTimeReques(); 70 | void transmitContinuousWave(uint8_t const *message, uint8_t size); 71 | void disableFPort224(); 72 | void sendDutVersions(); 73 | 74 | void resetState(); 75 | 76 | public: 77 | CertificationProtocol(Lmic &almic, VersionDetails const &fimwareVersion, 78 | VersionDetails const &lorawanVersion, 79 | VersionDetails const &lorawanRpVersion); 80 | bool handle(EventType ev); 81 | 82 | bool isEnabled() const { return enableFPort224; }; 83 | bool isNextFrameConfirmed() const { return nextFrameIsConfirmed; } 84 | OsDeltaTime getPeriodicity() const { return periodicity; } 85 | }; 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/hal/hal.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * This the HAL to run LMIC on top of the Arduino environment. 9 | *******************************************************************************/ 10 | 11 | #include "../boardconfig.h" 12 | #if LMIC_HAL == LMIC_ARDUINO 13 | #include "hal.h" 14 | #include "print_debug.h" 15 | #include 16 | #include 17 | 18 | // ----------------------------------------------------------------------------- 19 | // TIME 20 | 21 | namespace { 22 | OsDeltaTime time_in_sleep{0}; 23 | uint8_t overflow{0}; 24 | } // namespace 25 | 26 | void hal_add_time_in_sleep(OsDeltaTime nb_tick) { 27 | time_in_sleep += nb_tick; 28 | hal_ticks(); 29 | } 30 | 31 | OsTime hal_ticks() { 32 | // hal_ticks is use in interrupt and is not reentrant 33 | DisableIRQsGard gard; 34 | 35 | // Because micros() is scaled down in this function, micros() will 36 | // overflow before the tick timer should, causing the tick timer to 37 | // miss a significant part of its values if not corrected. To fix 38 | // this, the "overflow" serves as an overflow area for the micros() 39 | // counter. It consists of three parts: 40 | // - The US_PER_OSTICK upper bits are effectively an extension for 41 | // the micros() counter and are added to the result of this 42 | // function. 43 | // - The next bit overlaps with the most significant bit of 44 | // micros(). This is used to detect micros() overflows. 45 | // - The remaining bits are always zero. 46 | // 47 | // By comparing the overlapping bit with the corresponding bit in 48 | // the micros() return value, overflows can be detected and the 49 | // upper bits are incremented. This is done using some clever 50 | // bitwise operations, to remove the need for comparisons and a 51 | // jumps, which should result in efficient code. By avoiding shifts 52 | // other than by multiples of 8 as much as possible, this is also 53 | // efficient on AVR (which only has 1-bit shifts). 54 | 55 | // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, 56 | // the others will be the lower bits of our return value. 57 | uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; 58 | // Most significant byte of scaled 59 | uint8_t msb = scaled >> 24; 60 | // Mask pointing to the overlapping bit in msb and overflow. 61 | const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); 62 | // Update overflow. If the overlapping bit is different 63 | // between overflow and msb, it is added to the stored value, 64 | // so the overlapping bit becomes equal again and, if it changed 65 | // from 1 to 0, the upper bits are incremented. 66 | overflow += (msb ^ overflow) & mask; 67 | 68 | // Return the scaled value with the upper bits of stored added. The 69 | // overlapping bit will be equal and the lower bits will be 0, so 70 | // bitwise or is a no-op for them. 71 | return OsTime((scaled | ((uint32_t)overflow << 24)) + time_in_sleep.tick()); 72 | 73 | // 0 leads to correct, but overly complex code (it could just return 74 | // micros() unmodified), 8 leaves no room for the overlapping bit. 75 | static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, 76 | "Invalid US_PER_OSTICK_EXPONENT value"); 77 | } 78 | 79 | void hal_waitUntil(OsTime time) { 80 | OsDeltaTime delta = time - hal_ticks(); 81 | hal_wait(delta); 82 | } 83 | 84 | void hal_wait(OsDeltaTime delta) { 85 | // From delayMicroseconds docs: Currently, the largest value that 86 | // will produce an accurate delay is 16383. 87 | while (delta > OsDeltaTime::from_us(16000)) { 88 | delay(16); 89 | delta -= OsDeltaTime::from_us(16000); 90 | } 91 | 92 | if (delta > OsDeltaTime(0)) 93 | delayMicroseconds(delta.to_us()); 94 | } 95 | 96 | #ifdef __AVR__ 97 | DisableIRQsGard::DisableIRQsGard() : sreg_save(SREG) { cli(); } 98 | DisableIRQsGard::~DisableIRQsGard() { SREG = sreg_save; } 99 | #else 100 | DisableIRQsGard::DisableIRQsGard() { 101 | noInterrupts(); 102 | ++intNumber; 103 | } 104 | DisableIRQsGard::~DisableIRQsGard() { 105 | if (--intNumber == 0) { 106 | interrupts(); 107 | interrupts(); 108 | } 109 | } 110 | uint8_t DisableIRQsGard::intNumber = 0; 111 | #endif 112 | 113 | // ----------------------------------------------------------------------------- 114 | 115 | void hal_init() { 116 | if constexpr(debugLevel > 0) { 117 | // printf support 118 | hal_printf_init(); 119 | } 120 | } 121 | 122 | void hal_failed(const char *file, uint16_t line) { 123 | (void)file; 124 | (void)line; 125 | #if defined(LMIC_FAILURE_TO) 126 | LMIC_FAILURE_TO.println("FAILURE "); 127 | LMIC_FAILURE_TO.print(file); 128 | LMIC_FAILURE_TO.print(':'); 129 | LMIC_FAILURE_TO.println(line); 130 | LMIC_FAILURE_TO.flush(); 131 | #endif 132 | DisableIRQsGard irqguard; 133 | 134 | while (1) 135 | ; 136 | } 137 | #endif 138 | -------------------------------------------------------------------------------- /src/hal/hal.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * This the HAL to run LMIC on top of the Arduino environment. 9 | *******************************************************************************/ 10 | #ifndef _hal_hal_h_ 11 | #define _hal_hal_h_ 12 | 13 | #include 14 | #include 15 | 16 | #include "../lmic/osticks.h" 17 | 18 | /* 19 | * initialize hardware (IO, SPI, TIMER, IRQ). 20 | */ 21 | void hal_init(void); 22 | 23 | /* 24 | * disable all CPU interrupts for the current scope. 25 | * might be invoked nested. 26 | */ 27 | class DisableIRQsGard { 28 | private: 29 | #ifdef __AVR__ 30 | uint8_t sreg_save; 31 | #else 32 | static uint8_t intNumber; 33 | #endif 34 | public: 35 | DisableIRQsGard(); 36 | ~DisableIRQsGard(); 37 | }; 38 | 39 | /* 40 | * return system time. 41 | */ 42 | OsTime hal_ticks(); 43 | 44 | void hal_add_time_in_sleep(OsDeltaTime nb_tick); 45 | 46 | /* 47 | * busy-wait until specified timestamp is reached. 48 | */ 49 | void hal_waitUntil(OsTime time); 50 | 51 | /* 52 | * wait this interval. 53 | */ 54 | void hal_wait(OsDeltaTime delta); 55 | 56 | /* 57 | * perform fatal failure action. 58 | * - called by assertions 59 | * - action could be HALT or reboot 60 | */ 61 | void hal_failed(const char *file, uint16_t line); 62 | 63 | void hal_store_trigger(); 64 | 65 | #endif // _hal_hal_h_ 66 | -------------------------------------------------------------------------------- /src/hal/hal_esp.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Matthijs Kooijman 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * This the HAL to run LMIC on top of the Arduino environment. 9 | *******************************************************************************/ 10 | #include "../boardconfig.h" 11 | #if LMIC_HAL == LMIC_ESP32 12 | 13 | #include "hal.h" 14 | #include 15 | #include "print_debug.h" 16 | #include 17 | 18 | // ----------------------------------------------------------------------------- 19 | // TIME 20 | 21 | OsTime hal_ticks() { 22 | timeval val; 23 | gettimeofday(&val, nullptr); 24 | return OsTime((val.tv_sec * OSTICKS_PER_SEC) + (val.tv_usec >> US_PER_OSTICK_EXPONENT)); 25 | } 26 | 27 | void hal_waitUntil(OsTime time) { 28 | OsDeltaTime delta = time - hal_ticks(); 29 | hal_wait(delta); 30 | } 31 | 32 | void hal_wait(OsDeltaTime delta) { 33 | // From delayMicroseconds docs: Currently, the largest value that 34 | // will produce an accurate delay is 16383. 35 | while (delta > OsDeltaTime::from_us(16000)) { 36 | delay(16); 37 | delta -= OsDeltaTime::from_us(16000); 38 | } 39 | if (delta > OsDeltaTime(0)) 40 | delayMicroseconds(delta.to_us()); 41 | } 42 | 43 | DisableIRQsGard::DisableIRQsGard() { noInterrupts(); } 44 | DisableIRQsGard::~DisableIRQsGard() { interrupts(); } 45 | 46 | void hal_init() { 47 | // printf support 48 | hal_printf_init(); 49 | } 50 | 51 | void hal_failed(const char *file, uint16_t line) { 52 | (void)file; 53 | (void)line; 54 | #if defined(LMIC_FAILURE_TO) 55 | LMIC_FAILURE_TO.println("FAILURE "); 56 | LMIC_FAILURE_TO.print(file); 57 | LMIC_FAILURE_TO.print(':'); 58 | LMIC_FAILURE_TO.println(line); 59 | LMIC_FAILURE_TO.flush(); 60 | #endif 61 | DisableIRQsGard irqguard; 62 | 63 | while (1) 64 | ; 65 | } 66 | #endif -------------------------------------------------------------------------------- /src/hal/hal_generic.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * All rights reserved. This program and the accompanying materials 3 | * are made available under the terms of the Eclipse Public License v1.0 4 | * which accompanies this distribution, and is available at 5 | * http://www.eclipse.org/legal/epl-v10.html 6 | * 7 | * This the HAL to run LMIC on other environment than arduino. 8 | *******************************************************************************/ 9 | #include "../boardconfig.h" 10 | #if LMIC_HAL == LMIC_GENERIC 11 | 12 | #include "hal.h" 13 | #include "print_debug.h" 14 | #include 15 | #include 16 | #include 17 | 18 | namespace { 19 | OsDeltaTime time_in_sleep{0}; 20 | } // namespace 21 | 22 | void hal_add_time_in_sleep(OsDeltaTime nb_tick) { 23 | time_in_sleep += nb_tick; 24 | hal_ticks(); 25 | } 26 | 27 | // ----------------------------------------------------------------------------- 28 | // TIME 29 | 30 | OsTime hal_ticks() { 31 | timeval val; 32 | gettimeofday(&val, nullptr); 33 | return OsTime((val.tv_sec * OSTICKS_PER_SEC) + 34 | (val.tv_usec >> US_PER_OSTICK_EXPONENT)) + 35 | time_in_sleep; 36 | } 37 | 38 | void hal_waitUntil(OsTime time) { 39 | OsDeltaTime delta = time - hal_ticks(); 40 | hal_wait(delta); 41 | } 42 | 43 | void hal_wait(OsDeltaTime delta) { 44 | auto us = delta.to_us(); 45 | if (us > 0) { 46 | usleep(us); 47 | } 48 | } 49 | 50 | DisableIRQsGard::DisableIRQsGard() {} 51 | DisableIRQsGard::~DisableIRQsGard() {} 52 | 53 | void hal_init() { 54 | // nothing to do 55 | } 56 | 57 | void hal_failed(const char *file, uint16_t line) { 58 | (void)file; 59 | (void)line; 60 | 61 | /* 62 | #if defined(LMIC_FAILURE_TO) 63 | LMIC_FAILURE_TO.println("FAILURE "); 64 | LMIC_FAILURE_TO.print(file); 65 | LMIC_FAILURE_TO.print(':'); 66 | LMIC_FAILURE_TO.println(line); 67 | LMIC_FAILURE_TO.flush(); 68 | #endif 69 | */ 70 | 71 | while (1) 72 | ; 73 | } 74 | #endif -------------------------------------------------------------------------------- /src/hal/hal_io.cpp: -------------------------------------------------------------------------------- 1 | #include "../boardconfig.h" 2 | #if LMIC_HAL_IO == LMIC_ARDUINO 3 | 4 | #include "hal_io.h" 5 | #include "../lmic/lmic.h" 6 | #include "hal.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static const SPISettings settings(10000000, MSBFIRST, SPI_MODE0); 13 | 14 | HalIo::HalIo(lmic_pinmap const &pins) : lmic_pins(pins) {} 15 | 16 | void HalIo::yield() const { 17 | ::yield(); 18 | } 19 | 20 | void HalIo::write_reg(uint8_t const addr, uint8_t const data) const { 21 | beginspi(); 22 | spi(addr | 0x80); 23 | spi(data); 24 | endspi(); 25 | } 26 | 27 | uint8_t HalIo::read_reg(uint8_t const addr) const { 28 | beginspi(); 29 | spi(addr & 0x7F); 30 | uint8_t const val = spi(0x00); 31 | endspi(); 32 | return val; 33 | } 34 | 35 | void HalIo::write_buffer(uint8_t const addr, uint8_t const *const buf, 36 | uint8_t const len) const { 37 | beginspi(); 38 | spi(addr | 0x80); 39 | for (uint8_t i = 0; i < len; i++) { 40 | spi(buf[i]); 41 | } 42 | endspi(); 43 | } 44 | 45 | void HalIo::read_buffer(uint8_t const addr, uint8_t *const buf, 46 | uint8_t const len) const { 47 | beginspi(); 48 | spi(addr & 0x7F); 49 | std::generate_n(buf, len, [this]() { return spi(0x00); }); 50 | endspi(); 51 | } 52 | 53 | void HalIo::beginspi() const { 54 | SPI.beginTransaction(settings); 55 | digitalWrite(lmic_pins.nss, 0); 56 | } 57 | 58 | void HalIo::endspi() const { 59 | digitalWrite(lmic_pins.nss, 1); 60 | SPI.endTransaction(); 61 | } 62 | 63 | // perform SPI transaction with radio 64 | uint8_t HalIo::spi(uint8_t const out) const { 65 | uint8_t res = SPI.transfer(out); 66 | /* 67 | Serial.print(">"); 68 | Serial.print(out, HEX); 69 | Serial.print("<"); 70 | Serial.println(res, HEX); 71 | */ 72 | return res; 73 | } 74 | 75 | void HalIo::pin_switch_antenna_tx(bool isTx) const { 76 | // val == 1 => tx 1 77 | if (lmic_pins.prepare_antenna_tx) 78 | lmic_pins.prepare_antenna_tx(isTx); 79 | } 80 | 81 | // set radio RST pin to given value (or keep floating!) 82 | void HalIo::pin_rst(uint8_t val) const { 83 | if (lmic_pins.rst == LMIC_UNUSED_PIN) 84 | return; 85 | 86 | if (val == 0 || val == 1) { // drive pin 87 | pinMode(lmic_pins.rst, OUTPUT); 88 | digitalWrite(lmic_pins.rst, val); 89 | } else { // keep pin floating 90 | pinMode(lmic_pins.rst, INPUT); 91 | } 92 | } 93 | 94 | bool HalIo::io_check() const { 95 | for (uint8_t i = 0; i < NUM_DIO; ++i) { 96 | uint8_t newVal = digitalRead(lmic_pins.dio[i]); 97 | PRINT_DEBUG(2, F("Check DIO%d Value=%d"), i, newVal); 98 | if (newVal) { 99 | return true; 100 | } 101 | } 102 | return false; 103 | } 104 | 105 | bool HalIo::io_check0() const { 106 | return digitalRead(lmic_pins.dio[0]) ? true : false; 107 | } 108 | 109 | bool HalIo::io_check1() const { 110 | return digitalRead(lmic_pins.dio[1]) ? true : false; 111 | } 112 | 113 | void HalIo::init() const { 114 | // NSS, DIO0 , DIO1 are required for LoRa 115 | ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); 116 | ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); 117 | ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN); 118 | 119 | PRINT_DEBUG(2, F("NSS:%d, RST:%d, DIO0:%d, DIO1:%d"), lmic_pins.nss, 120 | lmic_pins.rst, lmic_pins.dio[0], lmic_pins.dio[1]); 121 | 122 | pinMode(lmic_pins.nss, OUTPUT); 123 | 124 | if (lmic_pins.rst != LMIC_UNUSED_PIN) 125 | pinMode(lmic_pins.rst, OUTPUT); 126 | 127 | pinMode(lmic_pins.dio[0], INPUT); 128 | pinMode(lmic_pins.dio[1], INPUT); 129 | 130 | // configure radio SPI 131 | } 132 | 133 | #endif -------------------------------------------------------------------------------- /src/hal/hal_io.h: -------------------------------------------------------------------------------- 1 | #ifndef hal_hal_io_h 2 | #define hal_hal_io_h 3 | #pragma once 4 | 5 | #include "../lmic/osticks.h" 6 | #include 7 | 8 | constexpr uint8_t NUM_DIO = 2; 9 | 10 | using prepare_antenna_type = void (bool); 11 | 12 | struct lmic_pinmap { 13 | uint8_t nss; 14 | prepare_antenna_type* prepare_antenna_tx; 15 | uint8_t rst; 16 | uint8_t dio[NUM_DIO]; 17 | }; 18 | 19 | // Use this for any unused pins. 20 | const uint8_t LMIC_UNUSED_PIN = 0xff; 21 | 22 | class HalIo final { 23 | public: 24 | explicit HalIo(lmic_pinmap const &pins); 25 | 26 | void write_reg(uint8_t addr, uint8_t data) const; 27 | uint8_t read_reg(uint8_t addr) const; 28 | void write_buffer(uint8_t addr, uint8_t const *buf, uint8_t len) const; 29 | void read_buffer(uint8_t addr, uint8_t *buf, uint8_t len) const; 30 | 31 | void yield() const; 32 | 33 | /** 34 | * drive radio NSS pin for start transfer. 35 | */ 36 | void beginspi() const; 37 | /** 38 | * drive radio NSS pin to end transfer. 39 | */ 40 | void endspi() const; 41 | /** 42 | * perform 8-bit SPI transaction with radio. 43 | * - write given byte 'outval' 44 | * - read byte and return value 45 | */ 46 | uint8_t spi(uint8_t outval) const; 47 | 48 | /** 49 | * drive radio RX/TX pins (false=rx, true=tx). 50 | */ 51 | void pin_switch_antenna_tx(bool isTx) const; 52 | 53 | /** 54 | * control radio RST pin (0=low, 1=high, 2=floating) 55 | */ 56 | void pin_rst(uint8_t val) const; 57 | 58 | /** 59 | * check "interrupt" pin and return if one set to 1. 60 | */ 61 | bool io_check() const; 62 | 63 | /** 64 | * Check pin DI0 or busy and return true if set to 1. 65 | */ 66 | bool io_check0() const; 67 | 68 | /** 69 | * Check pin DI1 and return true if set to 1. 70 | */ 71 | bool io_check1() const; 72 | 73 | // configure radio I/O and interrupt handler and SPI 74 | void init() const; 75 | 76 | private: 77 | const lmic_pinmap &lmic_pins; 78 | }; 79 | 80 | #endif -------------------------------------------------------------------------------- /src/hal/hal_io_empty.cpp: -------------------------------------------------------------------------------- 1 | #include "../boardconfig.h" 2 | #if LMIC_HAL_IO == LMIC_GENERIC 3 | 4 | #include "hal_io.h" 5 | #include "../lmic/lmic.h" 6 | #include "hal.h" 7 | // #include 8 | // #include 9 | #include 10 | #include 11 | 12 | 13 | HalIo::HalIo(lmic_pinmap const &pins) : lmic_pins(pins) {} 14 | 15 | void HalIo::yield() const { 16 | 17 | } 18 | 19 | void HalIo::write_reg(uint8_t const addr, uint8_t const data) const { 20 | beginspi(); 21 | spi(addr | 0x80); 22 | spi(data); 23 | endspi(); 24 | } 25 | 26 | uint8_t HalIo::read_reg(uint8_t const addr) const { 27 | beginspi(); 28 | spi(addr & 0x7F); 29 | uint8_t const val = spi(0x00); 30 | endspi(); 31 | return val; 32 | } 33 | 34 | void HalIo::write_buffer(uint8_t const addr, uint8_t const *const buf, 35 | uint8_t const len) const { 36 | beginspi(); 37 | spi(addr | 0x80); 38 | for (uint8_t i = 0; i < len; i++) { 39 | spi(buf[i]); 40 | } 41 | endspi(); 42 | } 43 | 44 | void HalIo::read_buffer(uint8_t const addr, uint8_t *const buf, 45 | uint8_t const len) const { 46 | beginspi(); 47 | spi(addr & 0x7F); 48 | std::generate_n(buf, len, [this]() { return spi(0x00); }); 49 | endspi(); 50 | } 51 | 52 | void HalIo::beginspi() const { 53 | // SPI.beginTransaction(settings); 54 | // digitalWrite(lmic_pins.nss, 0); 55 | } 56 | 57 | void HalIo::endspi() const { 58 | // digitalWrite(lmic_pins.nss, 1); 59 | // SPI.endTransaction(); 60 | } 61 | 62 | // perform SPI transaction with radio 63 | uint8_t HalIo::spi(uint8_t const /* out */) const { 64 | // uint8_t res = SPI.transfer(out); 65 | /* 66 | Serial.print(">"); 67 | Serial.print(out, HEX); 68 | Serial.print("<"); 69 | Serial.println(res, HEX); 70 | */ 71 | // return res; 72 | return 0; 73 | } 74 | 75 | void HalIo::pin_switch_antenna_tx(bool isTx) const { 76 | // val == 1 => tx 1 77 | if (lmic_pins.prepare_antenna_tx) 78 | lmic_pins.prepare_antenna_tx(isTx); 79 | } 80 | 81 | // set radio RST pin to given value (or keep floating!) 82 | void HalIo::pin_rst(uint8_t val) const { 83 | if (lmic_pins.rst == LMIC_UNUSED_PIN) 84 | return; 85 | 86 | if (val == 0 || val == 1) { // drive pin 87 | // pinMode(lmic_pins.rst, OUTPUT); 88 | // digitalWrite(lmic_pins.rst, val); 89 | } else { // keep pin floating 90 | // pinMode(lmic_pins.rst, INPUT); 91 | } 92 | } 93 | 94 | bool HalIo::io_check() const { 95 | for (uint8_t i = 0; i < NUM_DIO; ++i) { 96 | // uint8_t newVal = digitalRead(lmic_pins.dio[i]); 97 | // PRINT_DEBUG(2, F("Check DIO%d Value=%d"), i, newVal); 98 | //if (newVal) { 99 | return true; 100 | //} 101 | } 102 | return false; 103 | } 104 | 105 | bool HalIo::io_check0() const { 106 | // return digitalRead(lmic_pins.dio[0]) ? true : false; 107 | return true; 108 | } 109 | 110 | bool HalIo::io_check1() const { 111 | // return digitalRead(lmic_pins.dio[1]) ? true : false; 112 | return true; 113 | } 114 | 115 | void HalIo::init() const { 116 | // NSS, DIO0 , DIO1 are required for LoRa 117 | ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); 118 | ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); 119 | ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN); 120 | 121 | PRINT_DEBUG(2, F("NSS:%d, RST:%d, DIO0:%d, DIO1:%d"), lmic_pins.nss, 122 | lmic_pins.rst, lmic_pins.dio[0], lmic_pins.dio[1]); 123 | 124 | // pinMode(lmic_pins.nss, OUTPUT); 125 | 126 | // if (lmic_pins.rst != LMIC_UNUSED_PIN) 127 | // pinMode(lmic_pins.rst, OUTPUT); 128 | 129 | // pinMode(lmic_pins.dio[0], INPUT); 130 | // pinMode(lmic_pins.dio[1], INPUT); 131 | 132 | // configure radio SPI 133 | } 134 | 135 | #endif -------------------------------------------------------------------------------- /src/hal/print_debug.cpp: -------------------------------------------------------------------------------- 1 | #include "../lmic/config.h" 2 | 3 | #include "print_debug.h" 4 | 5 | #include 6 | 7 | #ifdef ARDUINO 8 | 9 | #ifdef ARDUINO_ARCH_AVR 10 | #include 11 | 12 | 13 | static int uart_putchar(char c, FILE *) { 14 | LMIC_PRINTF_TO.write(c); 15 | return 0; 16 | } 17 | 18 | void hal_printf_init() { 19 | 20 | // create a FILE structure to reference our UART output function 21 | static FILE uartout = {}; 22 | 23 | // fill in the UART file descriptor with pointer to writer. 24 | fdev_setup_stream(&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); 25 | 26 | // The uart is the standard output device STDOUT. 27 | stdout = &uartout; 28 | } 29 | #else 30 | 31 | 32 | void hal_printf_init() { 33 | // no init for other than AVR 34 | } 35 | #endif 36 | 37 | #else 38 | #include 39 | 40 | void hal_printf_init() { 41 | // no init for other than AVR 42 | } 43 | 44 | void PRINT_DEBUG(int X, const char *str, ...) { 45 | if (debugLevel >= X) { 46 | va_list ap; 47 | va_start(ap, str); 48 | printf("%" PRIu32 " ", hal_ticks().tick()); 49 | vprintf(str, ap); 50 | printf("\n"); 51 | va_end(ap); 52 | } 53 | } 54 | #endif // defined(LMIC_PRINTF_TO) 55 | -------------------------------------------------------------------------------- /src/hal/print_debug.h: -------------------------------------------------------------------------------- 1 | #ifndef hal_print_debug_h 2 | #define hal_print_debug_h 3 | 4 | #include "hal.h" 5 | #include "stdio.h" 6 | #include 7 | 8 | #ifdef ARDUINO 9 | 10 | #include "WString.h" 11 | #include 12 | 13 | #ifdef ARDUINO_ARCH_AVR 14 | template 15 | void PRINT_DEBUG(int X, const __FlashStringHelper *str, T const... div) { 16 | if (debugLevel >= X) { 17 | printf_P(PSTR("%" PRIu32 " "), hal_ticks().tick()); 18 | PGM_P p = reinterpret_cast(str); 19 | printf_P(p, div...); 20 | // printf_P(PSTR("\n")); 21 | printf("\n"); 22 | } 23 | } 24 | #else 25 | 26 | namespace { 27 | template 28 | void serialPrintf(const __FlashStringHelper *ifsh, T const... div) { 29 | // Cast the special class use by AVR to standart char * 30 | LMIC_PRINTF_TO.printf(reinterpret_cast(ifsh), div...); 31 | } 32 | 33 | } // namespace 34 | 35 | template 36 | void PRINT_DEBUG(int X, const __FlashStringHelper *str, T const... div) { 37 | if (debugLevel >= X) { 38 | serialPrintf(F("%" PRIu32 " "), hal_ticks().tick()); 39 | serialPrintf(str, div...); 40 | serialPrintf(F("\n")); 41 | } 42 | } 43 | 44 | #endif 45 | 46 | #else 47 | #define F(string_literal) string_literal 48 | 49 | __attribute__((format(printf, 2, 3))) 50 | void PRINT_DEBUG(int X, const char *str, ...); 51 | 52 | #endif 53 | 54 | constexpr bool IS_DEBUG_ENABLE(int x) { return debugLevel >= x; } 55 | 56 | void hal_printf_init(); 57 | 58 | #endif -------------------------------------------------------------------------------- /src/keyhandler.h: -------------------------------------------------------------------------------- 1 | #ifndef lorakeyhandler_h 2 | #define lorakeyhandler_h 3 | 4 | #ifdef ARDUINO 5 | #include 6 | #else 7 | #define PROGMEM 8 | #define memcpy_P memcpy 9 | #endif 10 | 11 | 12 | #include 13 | 14 | constexpr uint8_t HexCharToInt(char const char1) { 15 | 16 | return (char1 >= '0' && char1 <= '9') ? char1 - '0' 17 | : (char1 >= 'A' && char1 <= 'F') ? char1 - 'A' + 0x0A 18 | : (char1 >= 'a' && char1 <= 'f') ? char1 - 'a' + 0x0A 19 | : 0; 20 | } 21 | 22 | constexpr uint8_t HexCharToInt(char const char1, char const char2) { 23 | 24 | return (HexCharToInt(char1) * 0x10) + HexCharToInt(char2); 25 | } 26 | 27 | template class EuiGetter { 28 | private: 29 | static constexpr size_t SIZE = 8; 30 | static uint8_t const PROGMEM key[SIZE]; 31 | 32 | public: 33 | static void getEui(uint8_t *buf) { memcpy_P(buf, key, SIZE); } 34 | }; 35 | 36 | template 37 | uint8_t const PROGMEM EuiGetter::key[SIZE] = { 38 | HexCharToInt(KEY[14], KEY[15]), HexCharToInt(KEY[12], KEY[13]), 39 | HexCharToInt(KEY[10], KEY[11]), HexCharToInt(KEY[8], KEY[9]), 40 | HexCharToInt(KEY[6], KEY[7]), HexCharToInt(KEY[4], KEY[5]), 41 | HexCharToInt(KEY[2], KEY[3]), HexCharToInt(KEY[0], KEY[1]), 42 | }; 43 | 44 | template class KeyGetter { 45 | private: 46 | static constexpr size_t SIZE = 16; 47 | static uint8_t const PROGMEM key[SIZE]; 48 | 49 | public: 50 | static AesKey getKey() { 51 | AesKey lmicKey; 52 | memcpy_P(lmicKey.data(), key, lmicKey.size()); 53 | return lmicKey; 54 | } 55 | }; 56 | 57 | template 58 | uint8_t const PROGMEM KeyGetter::key[SIZE] = { 59 | HexCharToInt(KEY[0], KEY[1]), HexCharToInt(KEY[2], KEY[3]), 60 | HexCharToInt(KEY[4], KEY[5]), HexCharToInt(KEY[6], KEY[7]), 61 | HexCharToInt(KEY[8], KEY[9]), HexCharToInt(KEY[10], KEY[11]), 62 | HexCharToInt(KEY[12], KEY[13]), HexCharToInt(KEY[14], KEY[15]), 63 | HexCharToInt(KEY[16], KEY[17]), HexCharToInt(KEY[18], KEY[19]), 64 | HexCharToInt(KEY[20], KEY[21]), HexCharToInt(KEY[22], KEY[23]), 65 | HexCharToInt(KEY[24], KEY[25]), HexCharToInt(KEY[26], KEY[27]), 66 | HexCharToInt(KEY[28], KEY[29]), HexCharToInt(KEY[30], KEY[31]), 67 | }; 68 | 69 | template 70 | class SetupLmicKey { 71 | private: 72 | static EuiGetter const appEuiGetter; 73 | static EuiGetter const devEuiGetter; 74 | static KeyGetter const appKeyGetter; 75 | 76 | public: 77 | static void setup(Lmic &lmic) { 78 | lmic.setDevKey(appKeyGetter.getKey()); 79 | lmic.setDevEuiCallback(devEuiGetter.getEui); 80 | lmic.setArtEuiCallback(appEuiGetter.getEui); 81 | }; 82 | }; 83 | 84 | #endif -------------------------------------------------------------------------------- /src/lmic.h: -------------------------------------------------------------------------------- 1 | #include "lmic/lmic.h" 2 | #include "lmic/lmic.eu868.h" 3 | #include "lmic/lmic.us915.h" 4 | 5 | #include "lmic/radio_sx1272.h" 6 | #include "lmic/radio_sx1276.h" 7 | #include "lmic/radio_sx1262.h" -------------------------------------------------------------------------------- /src/lmic/band.eu868.cpp: -------------------------------------------------------------------------------- 1 | #include "band.eu868.h" 2 | #include "../hal/print_debug.h" 3 | #include "bufferpack.h" 4 | #include "oslmic.h" 5 | #include 6 | 7 | namespace { 8 | constexpr uint32_t MIN_BAND1_CENTI = 868000000; 9 | constexpr uint32_t MAX_BAND1_CENTI = 868600000; 10 | constexpr uint32_t MIN_BAND_DECI = 869400000; 11 | constexpr uint32_t MAX_BAND_DECI = 869650000; 12 | constexpr uint32_t MIN_BAND2_CENTI = 869700000; 13 | constexpr uint32_t MAX_BAND2_CENTI = 870000000; 14 | 15 | enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2 }; 16 | 17 | } // namespace 18 | 19 | BandsEu868::BandsEu868() { 20 | auto now = os_getTime(); 21 | avail.fill(now); 22 | } 23 | 24 | void BandsEu868::updateBandAvailability(uint8_t const band, 25 | OsTime const lastusage, 26 | OsDeltaTime const duration) { 27 | uint16_t cap; 28 | if (band == BAND_MILLI) { 29 | cap = 1000; 30 | } else if (band == BAND_CENTI) { 31 | cap = 100; 32 | } else { 33 | // DECI 34 | cap = 10; 35 | } 36 | avail[band] = lastusage + cap * duration; 37 | 38 | PRINT_DEBUG(2, F("Setting available time for band %d to %" PRIu32 ""), band, 39 | avail[band].tick()); 40 | } 41 | 42 | void BandsEu868::print_state() const { 43 | 44 | for (uint8_t band_index = 0; band_index < MAX_BAND; band_index++) { 45 | PRINT_DEBUG(2, F("Band %d, available at %" PRIu32 "."), band_index, 46 | avail[band_index].tick()); 47 | } 48 | } 49 | 50 | uint8_t BandsEu868::getBandForFrequency(uint32_t const frequency) const { 51 | 52 | if (frequency >= MIN_BAND_DECI && frequency <= MAX_BAND_DECI) 53 | return BAND_DECI; // 10% 54 | else if ((frequency >= MIN_BAND1_CENTI && frequency <= MAX_BAND1_CENTI) || 55 | (frequency >= MIN_BAND2_CENTI && frequency <= MAX_BAND2_CENTI)) 56 | return BAND_CENTI; // 1% 57 | else 58 | return BAND_MILLI; // 0.1% 59 | } 60 | 61 | #if defined(ENABLE_SAVE_RESTORE) 62 | 63 | void BandsEu868::saveState(StoringAbtract &store) const { 64 | 65 | std::for_each(begin(avail), end(avail), 66 | [&store](OsTime const date) { store.write(date); }); 67 | } 68 | 69 | void BandsEu868::loadState(RetrieveAbtract &store) { 70 | std::for_each(begin(avail), end(avail), 71 | [&store](OsTime &date) { store.read(date); }); 72 | } 73 | 74 | constexpr uint16_t BandsEu868::getStateSize() { 75 | return sizeof(avail); 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/lmic/band.eu868.h: -------------------------------------------------------------------------------- 1 | #ifndef lmic_eu868_h 2 | #define lmic_eu868_h 3 | 4 | #include 5 | 6 | #include "bands.h" 7 | #include "bufferpack.h" 8 | #include "osticks.h" 9 | #include 10 | 11 | class BandsEu868 : public Bands { 12 | public: 13 | BandsEu868(); 14 | void updateBandAvailability(uint8_t band, OsTime lastusage, 15 | OsDeltaTime duration) final; 16 | void print_state() const; 17 | OsTime getAvailability(uint8_t band) const final { return avail[band]; }; 18 | 19 | static constexpr uint8_t MAX_BAND = 3; 20 | uint8_t getBandForFrequency(uint32_t frequency) const final; 21 | 22 | #if defined(ENABLE_SAVE_RESTORE) 23 | 24 | void saveState(StoringAbtract &store) const final; 25 | void loadState(RetrieveAbtract &store) final; 26 | static constexpr uint16_t getStateSize(); 27 | #endif 28 | 29 | private: 30 | std::array avail; 31 | }; 32 | 33 | #endif -------------------------------------------------------------------------------- /src/lmic/bands.h: -------------------------------------------------------------------------------- 1 | #ifndef lmic_bands_h 2 | #define lmic_bands_h 3 | 4 | #include 5 | 6 | #include "bufferpack.h" 7 | #include "osticks.h" 8 | #include "oslmic.h" 9 | #include "../hal/print_debug.h" 10 | 11 | class Bands { 12 | public: 13 | virtual void updateBandAvailability(uint8_t band, OsTime lastusage, 14 | OsDeltaTime duration) = 0; 15 | virtual OsTime getAvailability(uint8_t band) const = 0; 16 | 17 | virtual uint8_t getBandForFrequency(uint32_t frequency) const = 0; 18 | 19 | #if defined(ENABLE_SAVE_RESTORE) 20 | 21 | virtual void saveState(StoringAbtract &store) const = 0; 22 | virtual void loadState(RetrieveAbtract &store) = 0; 23 | #endif 24 | }; 25 | 26 | template class BandSingle : public Bands { 27 | public: 28 | BandSingle() : avail{os_getTime()} {}; 29 | 30 | void updateBandAvailability(uint8_t, OsTime lastusage, 31 | OsDeltaTime duration) final { 32 | avail = lastusage + dutyCycle * duration; 33 | 34 | PRINT_DEBUG(2, F("Setting available time for bandto %" PRIu32 ""), 35 | avail.tick()); 36 | }; 37 | void print_state() const { 38 | PRINT_DEBUG(2, F("Band , available at %" PRIu32 "."), avail.tick()); 39 | }; 40 | OsTime getAvailability(uint8_t) const final { return avail; }; 41 | 42 | static constexpr uint8_t MAX_BAND = 1; 43 | uint8_t getBandForFrequency(uint32_t) const final { return 0; }; 44 | 45 | #if defined(ENABLE_SAVE_RESTORE) 46 | 47 | void saveState(StoringAbtract &store) const final { store.write(avail); }; 48 | void loadState(RetrieveAbtract &store) final { store.read(avail); }; 49 | static constexpr uint16_t getStateSize() { return sizeof(avail); }; 50 | #endif 51 | 52 | private: 53 | OsTime avail; 54 | }; 55 | 56 | #endif -------------------------------------------------------------------------------- /src/lmic/bufferpack.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #include "bufferpack.h" 14 | 15 | uint16_t rlsbf2(const uint8_t *const buf) { 16 | return (uint16_t)((uint16_t)buf[0] | ((uint16_t)buf[1] << 8)); 17 | } 18 | 19 | uint32_t rlsbf3(const uint8_t *const buf) { 20 | return (uint32_t)buf[0] | ((uint32_t)buf[1] << 8) | ((uint32_t)buf[2] << 16); 21 | } 22 | 23 | uint32_t rlsbf4(const uint8_t *const buf) { 24 | return (uint32_t)((uint32_t)buf[0] | ((uint32_t)buf[1] << 8) | 25 | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24)); 26 | } 27 | 28 | uint16_t rmsbf2(const uint8_t *const buf) { 29 | return (uint16_t)((uint16_t)buf[1] | ((uint16_t)buf[0] << 8)); 30 | } 31 | 32 | uint32_t rmsbf4(const uint8_t *const buf) { 33 | return (uint32_t)((uint32_t)buf[3] | ((uint32_t)buf[2] << 8) | 34 | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[0] << 24)); 35 | } 36 | 37 | void wlsbf2(uint8_t *const buf, uint16_t const v) { 38 | buf[0] = v; 39 | buf[1] = v >> 8; 40 | } 41 | 42 | void wlsbf4(uint8_t *const buf, uint32_t const v) { 43 | buf[0] = v; 44 | buf[1] = v >> 8; 45 | buf[2] = v >> 16; 46 | buf[3] = v >> 24; 47 | } 48 | 49 | void wmsbf4(uint8_t *const buf, uint32_t const v) { 50 | buf[3] = v; 51 | buf[2] = v >> 8; 52 | buf[1] = v >> 16; 53 | buf[0] = v >> 24; 54 | } 55 | 56 | size_t StoringBuffer::length() const { return current - original; } 57 | 58 | void StoringBuffer::store(void const *val, size_t const size) { 59 | std::memcpy(current, val, size); 60 | current += size; 61 | } 62 | 63 | void RetrieveBuffer::retrieve(void *val, size_t const size) { 64 | std::memcpy(val, current, size); 65 | current += size; 66 | } -------------------------------------------------------------------------------- /src/lmic/bufferpack.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef __bufferpack_h__ 14 | #define __bufferpack_h__ 15 | 16 | #include 17 | #include 18 | 19 | #include "enumflagsvalue.h" 20 | #include "osticks.h" 21 | 22 | //! Read 24-bit quantity from given pointer in little endian byte order (but in 23 | //! uint32_t). 24 | uint32_t rlsbf3(const uint8_t *buf); 25 | //! Read 32-bit quantity from given pointer in little endian byte order. 26 | uint32_t rlsbf4(const uint8_t *buf); 27 | //! Write 32-bit quantity into buffer in little endian byte order. 28 | void wlsbf4(uint8_t *buf, uint32_t value); 29 | //! Read 32-bit quantity from given pointer in big endian byte order. 30 | uint32_t rmsbf4(const uint8_t *buf); 31 | //! Write 32-bit quantity into buffer in big endian byte order. 32 | void wmsbf4(uint8_t *buf, uint32_t value); 33 | //! Read 16-bit quantity from given pointer in little endian byte order. 34 | uint16_t rlsbf2(const uint8_t *buf); 35 | //! Read 16-bit quantity from given pointer in big endian byte order. 36 | uint16_t rmsbf2(const uint8_t *buf); 37 | //! Write 16-bit quantity into buffer in little endian byte order. 38 | void wlsbf2(uint8_t *buf, uint16_t value); 39 | 40 | class StoringAbtract { 41 | public: 42 | template void write(T const val) { store(&val, sizeof(T)); } 43 | 44 | protected: 45 | virtual void store(void const *val, size_t size) = 0; 46 | }; 47 | 48 | class RetrieveAbtract { 49 | public: 50 | template void read(T &val) { retrieve(&val, sizeof(T)); } 51 | 52 | protected: 53 | virtual void retrieve(void *val, size_t size) = 0; 54 | }; 55 | 56 | class StoringBuffer final : public StoringAbtract { 57 | public: 58 | explicit StoringBuffer(uint8_t *const buffer) 59 | : original(buffer), current(buffer){}; 60 | size_t length() const; 61 | 62 | protected: 63 | void store(void const *val, size_t size) final; 64 | 65 | private: 66 | uint8_t *const original; 67 | uint8_t *current; 68 | }; 69 | 70 | class RetrieveBuffer final : public RetrieveAbtract { 71 | public: 72 | explicit RetrieveBuffer(uint8_t const *const buffer) : current(buffer){}; 73 | size_t length() const; 74 | 75 | protected: 76 | void retrieve(void *val, size_t size) final; 77 | 78 | private: 79 | uint8_t const *current; 80 | }; 81 | 82 | /** 83 | * If v compares less than lo, returns lo; 84 | * otherwise if hi compares less than v, returns hi; otherwise returns v. 85 | */ 86 | template 87 | constexpr T const &clamp(T const &v, T const &lo, T const &hi) { 88 | return v < lo ? lo : hi < v ? hi : v; 89 | } 90 | 91 | #endif // __bufferpack_h__ -------------------------------------------------------------------------------- /src/lmic/channelList.h: -------------------------------------------------------------------------------- 1 | #ifndef channel_list_h 2 | #define channel_list_h 3 | 4 | #include "bands.h" 5 | #include "bufferpack.h" 6 | #include "lorabase.h" 7 | #include 8 | #include 9 | #include 10 | 11 | struct ChannelDetail { 12 | private: 13 | // three low bit of freq is used to store band. 14 | uint32_t frequency{}; 15 | uint32_t frequencyRX{}; 16 | uint16_t drMap{}; 17 | 18 | public: 19 | constexpr uint32_t getFrequency() const { return frequency; }; 20 | constexpr uint32_t getFrequencyRX() const { return frequencyRX; }; 21 | constexpr uint16_t getDrMap() const { return drMap; }; 22 | constexpr bool isConfigured() const { return drMap != 0; } 23 | constexpr bool isDrActive(dr_t datarate) const { 24 | return (drMap & (1 << datarate)) != 0; 25 | }; 26 | constexpr ChannelDetail() = default; 27 | constexpr ChannelDetail(uint32_t aFrequency, uint16_t adrMap) 28 | : frequency(aFrequency), frequencyRX(aFrequency), drMap(adrMap){}; 29 | 30 | #if defined(ENABLE_SAVE_RESTORE) 31 | void saveState(StoringAbtract &store) const { 32 | store.write(frequency); 33 | store.write(frequencyRX); 34 | store.write(drMap); 35 | }; 36 | 37 | void loadState(RetrieveAbtract &store) { 38 | store.read(frequency); 39 | store.read(frequencyRX); 40 | store.read(drMap); 41 | }; 42 | 43 | static constexpr uint16_t getStateSize() { 44 | return sizeof(frequency) + sizeof(frequencyRX) + sizeof(drMap); 45 | }; 46 | #endif 47 | }; 48 | 49 | template 51 | class ChannelList { 52 | public: 53 | // Channel map store a maximum of 16 channel 54 | // (in rp_2-1.0.1 all dynamic channel region have a minumum of 16 ) 55 | constexpr static const uint8_t LIMIT_CHANNELS = 16; 56 | constexpr static const uint8_t NB_FIXED_CHANNELS = 57 | sizeof...(defaultChannelFreq); 58 | constexpr static const uint16_t DEFAULT_CHANNEL_DR_MAP = defaultChannelDrMap; 59 | 60 | private: 61 | std::array channels = {}; 62 | uint16_t channelMap = 0; 63 | BandsType bands; 64 | bool checkDutyCycle = true; 65 | 66 | uint8_t getBand(uint8_t const channel) const { 67 | return bands.getBandForFrequency(getFrequency(channel)); 68 | } 69 | 70 | public: 71 | constexpr ChannelList() { 72 | uint8_t chnl = 0; 73 | (configure(chnl++, defaultChannelFreq, defaultChannelDrMap),...); 74 | } 75 | 76 | constexpr void disable(uint8_t channel) { channelMap &= ~(1 << channel); } 77 | constexpr void enable(uint8_t channel) { 78 | // ignore - channel is not defined 79 | if (channels[channel].isConfigured()) { 80 | channelMap |= (1 << channel); 81 | } 82 | } 83 | constexpr void enableAll() { 84 | for (uint8_t channel = 0; channel < channels.max_size(); channel++) { 85 | enable(channel); 86 | } 87 | } 88 | constexpr bool is_enable(uint8_t channel) const { 89 | return channelMap & (1 << channel); 90 | } 91 | 92 | constexpr bool is_enable_at_dr(uint8_t channel, dr_t datarate) const { 93 | return (channelMap & (1 << channel)) && 94 | channels[channel].isDrActive(datarate); 95 | } 96 | 97 | constexpr void configure(uint8_t channel, uint32_t const newfreq, 98 | uint16_t const drmap) { 99 | channels[channel] = ChannelDetail{newfreq, drmap}; 100 | channelMap |= 1 << channel; 101 | } 102 | 103 | constexpr void updateAvailabitility(uint8_t const channel, OsTime const txbeg, 104 | OsDeltaTime const airtime) { 105 | // Update band specific duty cycle stats 106 | bands.updateBandAvailability(getBand(channel), txbeg, airtime); 107 | } 108 | 109 | constexpr OsTime getAvailability(uint8_t const channel) const { 110 | if (!checkDutyCycle) { 111 | return hal_ticks(); 112 | } 113 | auto band = getBand(channel); 114 | return bands.getAvailability(band); 115 | }; 116 | 117 | constexpr uint32_t getFrequency(uint8_t const channel) const { 118 | return channels[channel].getFrequency(); 119 | }; 120 | 121 | constexpr uint32_t getFrequencyRX(uint8_t const channel) const { 122 | return channels[channel].getFrequencyRX(); 123 | }; 124 | 125 | constexpr void setCheckDutyCycle(bool check) { checkDutyCycle = check; } 126 | 127 | #if defined(ENABLE_SAVE_RESTORE) 128 | void saveState(StoringAbtract &store) const { 129 | bands.saveState(store); 130 | saveStateWithoutTimeData(store); 131 | }; 132 | 133 | void saveStateWithoutTimeData(StoringAbtract &store) const { 134 | for (auto &&channel : channels) { 135 | channel.saveState(store); 136 | } 137 | 138 | store.write(channelMap); 139 | }; 140 | 141 | void loadState(RetrieveAbtract &store) { 142 | bands.loadState(store); 143 | loadStateWithoutTimeData(store); 144 | }; 145 | 146 | void loadStateWithoutTimeData(RetrieveAbtract &store) { 147 | for (auto &&channel : channels) { 148 | channel.loadState(store); 149 | } 150 | store.read(channelMap); 151 | }; 152 | 153 | static constexpr uint16_t getStateBytes() { 154 | return BandsType::getStateSize() + 155 | ChannelDetail::getStateSize() * LIMIT_CHANNELS + sizeof(channelMap); 156 | } 157 | #endif 158 | }; 159 | 160 | #endif -------------------------------------------------------------------------------- /src/lmic/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _lmic_config_h_ 2 | #define _lmic_config_h_ 3 | 4 | // In the original LMIC code, these config values were defined on the 5 | // gcc commandline. Since Arduino does not allow easily modifying the 6 | // compiler commandline, use this file instead. 7 | 8 | #ifdef LMIC_104_EXPERIMENTAL 9 | constexpr bool lorawan_v104 = true; 10 | #else 11 | constexpr bool lorawan_v104 = false; 12 | #endif 13 | 14 | // 16 μs per tick 15 | // LMIC requires ticks to be 15.5μs - 100 μs long 16 | #define US_PER_OSTICK_EXPONENT 4 17 | #define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) 18 | #define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) 19 | 20 | // Set this to 1 to enable some basic debug output (using printf) about 21 | // RF settings used during transmission and reception. Set to 2 to 22 | // enable more verbose output. Make sure that printf is actually 23 | // configured (e.g. on AVR it is not by default), otherwise using it can 24 | // cause crashing. 25 | 26 | #ifdef LMIC_DEBUG_LEVEL 27 | constexpr int debugLevel = LMIC_DEBUG_LEVEL; 28 | #else 29 | constexpr int debugLevel = 1; 30 | #endif 31 | 32 | // Define time to prepare radio for RX in ms 33 | // depend on MCU 34 | #ifndef LMIC_RX_RAMPUP_MS 35 | #define LMIC_RX_RAMPUP_MS 40000 36 | #endif 37 | 38 | // Define time to prepare radio for TX in ms 39 | // depend on MCU 40 | #ifndef LMIC_TX_RAMPUP_MS 41 | #define LMIC_TX_RAMPUP_MS 2000 42 | #endif 43 | 44 | // Enable this to allow using printf() to print to the given serial port 45 | // (or any other Print object). 46 | #ifndef LMIC_PRINTF_TO 47 | #define LMIC_PRINTF_TO Serial 48 | #endif 49 | 50 | #ifndef LMIC_MAX_BUFFER_LENGTH 51 | #define LMIC_MAX_BUFFER_LENGTH 64 52 | #endif 53 | 54 | // Any runtime assertion failures are printed to this serial port (or 55 | // any other Print object). If this is unset, any failures just silently 56 | // halt execution. 57 | // #define LMIC_FAILURE_TO Serial 58 | 59 | // In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the 60 | // same on RX. This ensures that gateways can talk to nodes and vice 61 | // versa, but gateways will not hear other gateways and nodes will not 62 | // hear other nodes. By uncommenting this macro, this inversion is 63 | // disabled and this node can hear other nodes. If two nodes both have 64 | // this macro set, they can talk to each other (but they can no longer 65 | // hear gateways). This should probably only be used when debugging 66 | // and/or when talking to the radio directly (e.g. like in the "raw" 67 | // example). 68 | //#define DISABLE_INVERT_IQ_ON_RX 69 | 70 | #define CFG_noassert 71 | 72 | #endif // _lmic_config_h_ 73 | -------------------------------------------------------------------------------- /src/lmic/enumflagsvalue.h: -------------------------------------------------------------------------------- 1 | #ifndef _enumflagsvalue_h_ 2 | #define _enumflagsvalue_h_ 3 | 4 | #include 5 | 6 | template class EnumFlagsValue final { 7 | public: 8 | uint8_t value; 9 | 10 | EnumFlagsValue &set(T const bit) { 11 | value |= static_cast(1 << static_cast(bit)); 12 | return *this; 13 | }; 14 | 15 | EnumFlagsValue &reset() { 16 | value = 0; 17 | return *this; 18 | }; 19 | 20 | EnumFlagsValue &reset(T bit) { 21 | value &= ~static_cast(1 << static_cast(bit)); 22 | return *this; 23 | }; 24 | 25 | constexpr bool test(T bit) const { 26 | return (value & (1 << static_cast(bit))) != 0; 27 | }; 28 | }; 29 | #endif -------------------------------------------------------------------------------- /src/lmic/lmic.eu433.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #include "lmic.eu433.h" 14 | #include "lmic_table.h" 15 | 16 | namespace EU433 { 17 | 18 | extern CONST_TABLE2(uint8_t, _DR2RPS_CRC)[] = { 19 | rps_DR0, rps_DR1, rps_DR2, rps_DR3, rps_DR4, rps_DR5, rps_DR6}; 20 | 21 | } // namespace EU433 22 | 23 | LmicEu433::LmicEu433(Radio &aradio) 24 | : Lmic(aradio, aes, rand, channelParams), aes(), rand(aes), channelParams(rand) {} 25 | -------------------------------------------------------------------------------- /src/lmic/lmic.eu433.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef _lmic_eu433_h_ 14 | #define _lmic_eu433_h_ 15 | 16 | #include "bufferpack.h" 17 | #include "channelList.h" 18 | #include "lmic.h" 19 | #include "lmicdynamicchannel.h" 20 | 21 | namespace EU433 { 22 | 23 | // normaly 12.5 24 | constexpr int8_t MaxEIRPValue = 12; 25 | constexpr uint8_t MaxPowerIndex = 5; 26 | 27 | constexpr uint32_t FREQ_DNW2 = 434665000; 28 | 29 | constexpr rps_t rps_DNW2 = 30 | rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5, true}; 31 | 32 | constexpr uint8_t rps_DR0 = rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5}; 33 | constexpr uint8_t rps_DR1 = rps_t{SF11, BandWidth::BW125, CodingRate::CR_4_5}; 34 | constexpr uint8_t rps_DR2 = rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5}; 35 | constexpr uint8_t rps_DR3 = rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5}; 36 | constexpr uint8_t rps_DR4 = rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5}; 37 | constexpr uint8_t rps_DR5 = rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5}; 38 | constexpr uint8_t rps_DR6 = rps_t{SF7, BandWidth::BW250, CodingRate::CR_4_5}; 39 | 40 | extern CONST_TABLE2(uint8_t, _DR2RPS_CRC)[]; 41 | 42 | constexpr uint8_t limitRX1DrOffset = 6; 43 | 44 | using Bands = BandSingle<100>; 45 | 46 | constexpr uint32_t FREQ_MIN = 433050000; 47 | constexpr uint32_t FREQ_MAX = 434665000; 48 | 49 | enum class Dr : dr_t { SF12 = 0, SF11, SF10, SF9, SF8, SF7, SF7B, FSK, NONE }; 50 | 51 | // Default frequency plan for EU 433 52 | constexpr uint32_t F1 = 433175000; // SF7-12 53 | constexpr uint32_t F2 = 433375000; // SF7-12 54 | constexpr uint32_t F3 = 433575000; // SF7-12 55 | 56 | } // namespace EU433 57 | 58 | using EU433Channels = 59 | ChannelList; 61 | 62 | using Eu433RegionalChannelParams = 63 | DYNAMIC_CHANNEL::DynamicRegionalChannelParams< 64 | EU433Channels, EU433::MaxEIRPValue, 5, 0, 65 | EU433::RESOLVE_TABLE(_DR2RPS_CRC), 7, EU433::FREQ_DNW2, EU433::rps_DNW2, 66 | EU433::MaxPowerIndex, EU433::limitRX1DrOffset, EU433::FREQ_MIN, 67 | EU433::FREQ_MAX>; 68 | 69 | class LmicEu433 final : public Lmic { 70 | public: 71 | explicit LmicEu433(Radio &radio); 72 | 73 | private: 74 | Aes aes; 75 | LmicRand rand; 76 | Eu433RegionalChannelParams channelParams; 77 | }; 78 | 79 | #endif -------------------------------------------------------------------------------- /src/lmic/lmic.eu868.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #include "lmic.eu868.h" 14 | #include "lmic_table.h" 15 | 16 | namespace EU868 { 17 | 18 | CONST_TABLE2(uint8_t, _DR2RPS_CRC) 19 | [] = {rps_DR0, rps_DR1, rps_DR2, rps_DR3, rps_DR4, rps_DR5, rps_DR6}; 20 | 21 | } // namespace EU868 22 | 23 | LmicEu868::LmicEu868(Radio &aradio) 24 | : Lmic(aradio, aes, rand, channelParams), aes(), rand(aes), channelParams(rand) {} -------------------------------------------------------------------------------- /src/lmic/lmic.eu868.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef _lmic_eu868_h_ 14 | #define _lmic_eu868_h_ 15 | 16 | #include "band.eu868.h" 17 | #include "bufferpack.h" 18 | #include "channelList.h" 19 | #include "lmic.h" 20 | #include "lmicdynamicchannel.h" 21 | 22 | namespace EU868 { 23 | 24 | constexpr int8_t MaxEIRPValue = 16; 25 | constexpr uint8_t MaxPowerIndex = 7; 26 | 27 | constexpr uint32_t FREQ_DNW2 = 869525000; 28 | constexpr rps_t rps_DNW2 = 29 | rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5, true}; 30 | 31 | constexpr uint8_t rps_DR0 = rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5}; 32 | constexpr uint8_t rps_DR1 = rps_t{SF11, BandWidth::BW125, CodingRate::CR_4_5}; 33 | constexpr uint8_t rps_DR2 = rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5}; 34 | constexpr uint8_t rps_DR3 = rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5}; 35 | constexpr uint8_t rps_DR4 = rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5}; 36 | constexpr uint8_t rps_DR5 = rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5}; 37 | constexpr uint8_t rps_DR6 = rps_t{SF7, BandWidth::BW250, CodingRate::CR_4_5}; 38 | 39 | extern CONST_TABLE2(uint8_t, _DR2RPS_CRC)[]; 40 | 41 | constexpr uint8_t limitRX1DrOffset = 6; 42 | 43 | constexpr uint32_t FREQ_MIN = 863000000; 44 | constexpr uint32_t FREQ_MAX = 870000000; 45 | 46 | enum class Dr : dr_t { SF12 = 0, SF11, SF10, SF9, SF8, SF7, SF7B, FSK, NONE }; 47 | 48 | constexpr uint32_t F1 = 868100000; // g1 SF7-12 49 | constexpr uint32_t F2 = 868300000; // g1 SF7-12 FSK SF7/250 50 | constexpr uint32_t F3 = 868500000; // g1 SF7-12 51 | 52 | } // namespace EU868 53 | 54 | using EU868Channels = 55 | ChannelList; 57 | 58 | using Eu868RegionalChannelParams = 59 | DYNAMIC_CHANNEL::DynamicRegionalChannelParams< 60 | EU868Channels, EU868::MaxEIRPValue, 5, 0, 61 | EU868::RESOLVE_TABLE(_DR2RPS_CRC), 7, EU868::FREQ_DNW2, EU868::rps_DNW2, 62 | EU868::MaxPowerIndex, EU868::limitRX1DrOffset, EU868::FREQ_MIN, 63 | EU868::FREQ_MAX>; 64 | 65 | class LmicEu868 final : public Lmic { 66 | public: 67 | explicit LmicEu868(Radio &radio); 68 | 69 | private: 70 | Aes aes; 71 | LmicRand rand; 72 | Eu868RegionalChannelParams channelParams; 73 | }; 74 | 75 | #endif -------------------------------------------------------------------------------- /src/lmic/lmic.us915.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef lmic_us915_h 14 | #define lmic_us915_h 15 | 16 | #include "lmic.h" 17 | 18 | class Us915RegionalChannelParams final : public RegionalChannelParams { 19 | public: 20 | enum Dr : dr_t { 21 | SF10 = 0, 22 | SF9, 23 | SF8, 24 | SF7, 25 | SF8C, 26 | // Only for Downlink 27 | SF12CR = 8, 28 | SF11CR, 29 | SF10CR, 30 | SF9CR, 31 | SF8CR, 32 | SF7CR 33 | }; 34 | 35 | // increase data rate 36 | dr_t incDR(dr_t dr) const; 37 | // decrease data rate 38 | dr_t decDR(dr_t dr) const; 39 | // in range 40 | bool validDR(dr_t dr) const final; 41 | // decrease data rate by n steps 42 | dr_t lowerDR(dr_t dr, uint8_t n) const; 43 | 44 | virtual void reduceDr(uint8_t diff) final { 45 | setDrTx(lowerDR(datarate, diff)); 46 | } 47 | 48 | bool setupChannel(uint8_t channel, uint32_t newfreq, uint16_t drmap) final; 49 | void selectSubBand(uint8_t band); 50 | void setRegionalDutyCycleVerification(bool) final{}; 51 | void setDrJoin(dr_t dr) { datarate = dr; } 52 | virtual void setDrTx(uint8_t dr) final { datarate = dr; } 53 | virtual void setAdrTxPow(int8_t newPower) final { adrTxPow = newPower; } 54 | virtual bool setAdrToMaxIfNotAlreadySet() final; 55 | 56 | uint32_t getTxFrequency() const; 57 | int8_t getTxPower() const; 58 | TransmitionParameters getTxParameter() const final; 59 | TransmitionParameters getRx1Parameter() const final; 60 | TransmitionParameters getRx2Parameter() const final; 61 | 62 | int8_t pow2dBm(uint8_t powerIndex) const final; 63 | OsDeltaTime getDwn2SafetyZone() const final; 64 | bool validRx1DrOffset(uint8_t drOffset) const final; 65 | 66 | void initDefaultChannels() final; 67 | 68 | void disableChannel(uint8_t channel) final; 69 | void handleCFList(const uint8_t *ptr) final; 70 | 71 | bool validMapChannels(uint8_t chpage, uint16_t chmap) final; 72 | void mapChannels(uint8_t chpage, uint16_t chmap) final; 73 | void updateTxTimes(OsDeltaTime airtime) final; 74 | OsTime nextTx(OsTime now) final; 75 | OsTime initJoinLoop() final; 76 | TimeAndStatus nextJoinState() final; 77 | void setRx2Parameter(uint32_t rx2frequency, dr_t rx2datarate) final; 78 | void setRx2DataRate(dr_t rx2datarate) final; 79 | void setRx1DrOffset(uint8_t drOffset) final; 80 | 81 | #if defined(ENABLE_SAVE_RESTORE) 82 | virtual void saveState(StoringAbtract &store) const final; 83 | virtual void saveStateWithoutTimeData(StoringAbtract &store) const final; 84 | virtual void loadState(RetrieveAbtract &store) final; 85 | virtual void loadStateWithoutTimeData(RetrieveAbtract &store) final; 86 | #endif 87 | 88 | explicit Us915RegionalChannelParams(LmicRand &arand); 89 | 90 | private: 91 | // enabled bits 92 | std::array channelMap; 93 | uint16_t chRnd = 0; 94 | // channel for next TX 95 | uint8_t txChnl = 0; 96 | // ADR adjusted TX power, limit power to this value. 97 | // dBm 98 | int8_t adrTxPow = 0; 99 | dr_t datarate = 0; // current data rate 100 | // 1 RX window DR offset 101 | uint8_t rx1DrOffset = 0; 102 | TransmitionParameters rx2Parameter; 103 | LmicRand &rand; 104 | 105 | void enableChannel(uint8_t channel); 106 | void enableSubBand(uint8_t band); 107 | void disableSubBand(uint8_t band); 108 | uint32_t getRx1Frequency() const; 109 | dr_t getRx1Dr() const; 110 | uint8_t getRawRps(dr_t dr) const; 111 | }; 112 | 113 | class LmicUs915 final : public Lmic { 114 | public: 115 | explicit LmicUs915(Radio &radio); 116 | 117 | private: 118 | Aes aes; 119 | LmicRand rand; 120 | 121 | Us915RegionalChannelParams channelParams; 122 | }; 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /src/lmic/lmic_table.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef _lmic_table_h_ 14 | #define _lmic_table_h_ 15 | 16 | #include 17 | 18 | // ====================================================================== 19 | // Table support 20 | // These macros for defining a table of constants and retrieving values 21 | // from it makes it easier for other platforms (like AVR) to optimize 22 | // table accesses. 23 | // Use CONST_TABLE() whenever declaring or defining a table, and 24 | // TABLE_GET_xx whenever accessing its values. The actual name of the 25 | // declared variable will be modified to prevent accidental direct 26 | // access. The accessor macros forward to an inline function to allow 27 | // proper type checking of the array element type. 28 | 29 | // Helper to add a prefix to the table name 30 | #define RESOLVE_TABLE(table) constant_table_##table 31 | 32 | // Accessors for table elements 33 | #define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index) 34 | #define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index) 35 | #define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index) 36 | #define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index) 37 | #define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index) 38 | #define TABLE_GET_U1_TWODIM(table, index1, index2) \ 39 | table_get_u1(RESOLVE_TABLE(table)[index1], index2) 40 | 41 | #if defined(__AVR__) 42 | #include 43 | // Macro to define the getter functions. This loads data from 44 | // progmem using pgm_read_xx, or accesses memory directly when the 45 | // index is a constant so gcc can optimize it away; 46 | #define TABLE_GETTER(postfix, type, pgm_type) \ 47 | inline type table_get##postfix(const type *table, size_t index) { \ 48 | return __builtin_constant_p(table[index]) \ 49 | ? table[index] \ 50 | : pgm_read_##pgm_type(&table[index]); \ 51 | } 52 | 53 | TABLE_GETTER(_u1, uint8_t, byte) 54 | TABLE_GETTER(_u2, uint16_t, word) 55 | TABLE_GETTER(_s1, int8_t, byte) 56 | TABLE_GETTER(_u4, uint32_t, dword) 57 | TABLE_GETTER(_s4, int32_t, dword) 58 | 59 | // For AVR, store constants in PROGMEM, saving on RAM usage 60 | #define CONST_TABLE(type, name) constexpr type PROGMEM RESOLVE_TABLE(name) 61 | #define CONST_TABLE2(type, name) const type PROGMEM RESOLVE_TABLE(name) 62 | 63 | #else 64 | inline uint8_t table_get_u1(const uint8_t *table, size_t index) { 65 | return table[index]; 66 | } 67 | inline int8_t table_get_s1(const int8_t *table, size_t index) { 68 | return table[index]; 69 | } 70 | inline uint16_t table_get_u2(const uint16_t *table, size_t index) { 71 | return table[index]; 72 | } 73 | inline uint32_t table_get_u4(const uint32_t *table, size_t index) { 74 | return table[index]; 75 | } 76 | inline int32_t table_get_s4(const int32_t *table, size_t index) { 77 | return table[index]; 78 | } 79 | 80 | // Declare a table 81 | #define CONST_TABLE(type, name) constexpr type RESOLVE_TABLE(name) 82 | #define CONST_TABLE2(type, name) const type RESOLVE_TABLE(name) 83 | 84 | 85 | #endif 86 | 87 | #endif -------------------------------------------------------------------------------- /src/lmic/lmicrand.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef _lmicrand_h_ 14 | #define _lmicrand_h_ 15 | 16 | #include 17 | #include 18 | 19 | class Radio; 20 | class Aes; 21 | 22 | #ifdef ARDUINO_ARCH_ESP32 23 | 24 | class LmicRand { 25 | public: 26 | explicit LmicRand(Aes &){}; 27 | void init(Radio &){}; 28 | uint8_t uint8(); 29 | uint16_t uint16(); 30 | }; 31 | 32 | #else 33 | 34 | class LmicRand { 35 | public: 36 | explicit LmicRand(Aes &aes); 37 | void init(Radio &radio); 38 | uint8_t uint8(); 39 | uint16_t uint16(); 40 | 41 | private: 42 | Aes &aes; 43 | uint8_t index = 16; 44 | std::array randbuf; 45 | }; 46 | #endif 47 | 48 | #endif -------------------------------------------------------------------------------- /src/lmic/lmicrandesp.cpp: -------------------------------------------------------------------------------- 1 | #ifdef ARDUINO_ARCH_ESP32 2 | #include "lmicrand.h" 3 | 4 | #include 5 | #include 6 | 7 | // return next random from esp system random generator 8 | uint8_t LmicRand::uint8() { return esp_random() & 0xFF; } 9 | 10 | //! Get random number . 11 | uint16_t LmicRand::uint16() { return esp_random() & 0xFFFF; } 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/lmic/lmicrandfromaes.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef ARDUINO_ARCH_ESP32 14 | 15 | #include "../aes/lmic_aes.h" 16 | #include "lmicrand.h" 17 | #include "radio.h" 18 | 19 | LmicRand::LmicRand(Aes &anaes) : aes(anaes) {} 20 | 21 | void LmicRand::init(Radio &radio) { 22 | radio.init_random(randbuf); 23 | // set initial index to encrypt at next 24 | index = 16; 25 | } 26 | 27 | // return next random byte derived from seed buffer 28 | uint8_t LmicRand::uint8() { 29 | if (index >= randbuf.size()) { 30 | // encrypt seed with any key 31 | aes.encrypt(randbuf.begin(), randbuf.size()); 32 | index = 0; 33 | } 34 | return randbuf[index++]; 35 | } 36 | 37 | //! Get random number (default impl for uint16_t). 38 | uint16_t LmicRand::uint16() { 39 | return (static_cast(uint8()) << 8U) | uint8(); 40 | } 41 | 42 | #endif -------------------------------------------------------------------------------- /src/lmic/lorawanpacket.h: -------------------------------------------------------------------------------- 1 | #ifndef _lorawanpacket_h_ 2 | #define _lorawanpacket_h_ 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace lorawan { 8 | 9 | namespace lengths { 10 | constexpr uint8_t MHDR = 1; 11 | constexpr uint8_t MIC = 4; 12 | } // namespace lengths 13 | 14 | namespace offsets { 15 | constexpr uint8_t MHDR = 0; 16 | } 17 | 18 | namespace mac_payload { 19 | namespace lengths { 20 | constexpr uint8_t devAddr = 4; 21 | constexpr uint8_t fctrl = 1; 22 | constexpr uint8_t fcnt = 2; 23 | } // namespace lengths 24 | 25 | namespace offsets { 26 | constexpr uint8_t devAddr = lorawan::offsets::MHDR + lorawan::lengths::MHDR; 27 | constexpr uint8_t fctrl = devAddr + lengths::devAddr; 28 | constexpr uint8_t fcnt = fctrl + lengths::fctrl; 29 | constexpr uint8_t fopts = fcnt + lengths::fcnt; 30 | } // namespace offsets 31 | } // namespace mac_payload 32 | 33 | // Join Request frame format 34 | namespace join_request { 35 | namespace lengths { 36 | // cf § 6.2.4 Join-request message 37 | constexpr uint8_t appEUI = 8; 38 | constexpr uint8_t devEUI = 8; 39 | constexpr uint8_t devNonce = 2; 40 | constexpr uint8_t totalWithMic = 1 + appEUI + devEUI + devNonce + 4; 41 | } // namespace lengths 42 | 43 | namespace offset { 44 | constexpr uint8_t MHDR = 0; 45 | constexpr uint8_t appEUI = 1; 46 | constexpr uint8_t devEUI = appEUI + lengths::appEUI; 47 | constexpr uint8_t devNonce = devEUI + lengths::devEUI; 48 | constexpr uint8_t MIC = devNonce + lengths::devNonce; 49 | } // namespace offset 50 | 51 | } // namespace join_request 52 | 53 | namespace join_accept { 54 | namespace lengths { 55 | constexpr uint8_t appNonce = 3; 56 | constexpr uint8_t netId = 3; 57 | constexpr uint8_t devAddr = 4; 58 | constexpr uint8_t dlSettings = 1; 59 | constexpr uint8_t rxDelay = 1; 60 | constexpr uint8_t cfList = 16; 61 | constexpr uint8_t total = 1 + appNonce + netId + devAddr + dlSettings + 62 | rxDelay + lorawan::lengths::MIC; 63 | constexpr uint8_t totalWithOptional = total + cfList; 64 | 65 | } // namespace lengths 66 | 67 | namespace offset { 68 | constexpr uint8_t MHDR = 0; 69 | constexpr uint8_t appNonce = 1; 70 | constexpr uint8_t netId = appNonce + lengths::appNonce; 71 | constexpr uint8_t devAddr = netId + lengths::netId; 72 | constexpr uint8_t dlSettings = devAddr + lengths::devAddr; 73 | constexpr uint8_t rxDelay = dlSettings + lengths::dlSettings; 74 | constexpr uint8_t cfList = rxDelay + lengths::rxDelay; 75 | } // namespace offset 76 | } // namespace join_accept 77 | 78 | namespace mhdr { 79 | constexpr uint8_t ftype_offset = 5; 80 | constexpr uint8_t ftype_mask = 0x07 << ftype_offset; 81 | 82 | // Values of frame type bit field 83 | // join request 84 | constexpr uint8_t ftype_join_req = 0x00 << ftype_offset; 85 | // join accept 86 | constexpr uint8_t ftype_join_acc = 0x01 << ftype_offset; 87 | // data unconfirmed up 88 | constexpr uint8_t ftype_data_up = 0x02 << ftype_offset; 89 | // data unconfirmed dn 90 | constexpr uint8_t ftype_data_down = 0x03 << ftype_offset; 91 | // data confirmed up 92 | constexpr uint8_t ftype_data_conf_up = 0x04 << ftype_offset; 93 | // data confirmed dn 94 | constexpr uint8_t ftype_data_conf_down = 0x05 << ftype_offset; 95 | // Proprietary 96 | constexpr uint8_t ftype_proprietary = 0x07 << ftype_offset; 97 | 98 | constexpr uint8_t major_mask = 0x03; 99 | constexpr uint8_t major_v1 = 0x00; 100 | 101 | } // namespace mhdr 102 | 103 | } // namespace lorawan 104 | 105 | #endif -------------------------------------------------------------------------------- /src/lmic/oslmic.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #include "../hal/print_debug.h" 14 | #include "lmic.h" 15 | #include 16 | 17 | void os_init() { hal_init(); } 18 | 19 | OsTime os_getTime() { return hal_ticks(); } 20 | -------------------------------------------------------------------------------- /src/lmic/oslmic.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef lmic_oslmic_h 14 | #define lmic_oslmic_h 15 | 16 | #include "../hal/hal.h" 17 | #include "config.h" 18 | #include "osticks.h" 19 | #include 20 | #include 21 | 22 | //================================================================================ 23 | 24 | #if !defined(CFG_noassert) 25 | #define ASSERT(cond) \ 26 | if (!(cond)) \ 27 | hal_failed(__FILE__, __LINE__) 28 | #else 29 | #define ASSERT(cond) /**/ 30 | #endif 31 | 32 | void os_init(void); 33 | 34 | //================================================================================ 35 | 36 | constexpr OsDeltaTime rxRampUp = OsDeltaTime::from_us(LMIC_RX_RAMPUP_MS); 37 | constexpr OsDeltaTime txRampUp = OsDeltaTime::from_us(LMIC_TX_RAMPUP_MS); 38 | 39 | #ifndef HAS_os_calls 40 | 41 | #ifndef os_getTime 42 | OsTime os_getTime(void); 43 | #endif 44 | 45 | #endif // !HAS_os_calls 46 | 47 | template class OsJobType final { 48 | public: 49 | using osjobcbTyped_t = void (T::*)(); 50 | 51 | private: 52 | osjobcbTyped_t funcTyped; 53 | OsTime deadline; 54 | 55 | public: 56 | OsJobType() : OsJobType(nullptr, OsTime()){}; 57 | explicit OsJobType(osjobcbTyped_t cb) : OsJobType(cb, os_getTime()){}; 58 | explicit OsJobType(osjobcbTyped_t cb, OsTime time) : funcTyped(cb), deadline(time){}; 59 | 60 | OsDeltaTime run(T &refClass) { 61 | 62 | if (funcTyped && deadline <= hal_ticks()) { 63 | auto called = funcTyped; 64 | funcTyped = nullptr; 65 | (refClass.*called)(); 66 | } 67 | // functyped and deadline may have been updated 68 | if (funcTyped) { 69 | // return before nex action 70 | return deadline - hal_ticks(); 71 | } else { 72 | // nothing to do 73 | return OsInfiniteDeltaTime; 74 | } 75 | } 76 | }; 77 | 78 | #endif // _oslmic_h_ 79 | -------------------------------------------------------------------------------- /src/lmic/osticks.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "osticks.h" 3 | #include "lmicrand.h" 4 | 5 | OsDeltaTime &OsDeltaTime::operator+=(const OsDeltaTime &a) { 6 | value += a.value; 7 | return *this; 8 | } 9 | 10 | OsDeltaTime &OsDeltaTime::operator-=(const OsDeltaTime &a) { 11 | value -= a.value; 12 | return *this; 13 | } 14 | 15 | OsTime &OsTime::operator+=(const OsDeltaTime &a) { 16 | value += a.tick(); 17 | return *this; 18 | } 19 | 20 | OsTime &OsTime::operator-=(const OsDeltaTime &a) { 21 | value -= a.tick(); 22 | return *this; 23 | } 24 | 25 | OsDeltaTime OsDeltaTime::rnd_delay(LmicRand &rand, uint8_t secSpan) { 26 | uint16_t r = rand.uint16(); 27 | OsDeltaTime delay{r}; 28 | // OSTICKS_PER_SEC > max int16_t 29 | // if (delay > OSTICKS_PER_SEC) 30 | // delay = r % (uint16_t)OSTICKS_PER_SEC; 31 | if (secSpan > 0) 32 | delay += (r % secSpan) * OsDeltaTime::from_sec(1); 33 | return delay; 34 | } 35 | 36 | // Some test 37 | 38 | // diff 39 | static_assert(OsTime(2) - OsTime(1) == OsDeltaTime(1), "Simple diff"); 40 | static_assert(OsTime(0x0000001) - OsTime(0xFFFFFFFF) == OsDeltaTime(2), 41 | "diff with roll over"); 42 | static_assert(OsTime(0xFFFFFFFF) - OsTime(0x0000001) == OsDeltaTime(-2), 43 | "diff with roll over"); 44 | 45 | // Comparaison 46 | static_assert(OsTime(1) < OsTime(10), "Comparaison small number"); 47 | static_assert(OsTime(0x7FFFFFFF) < OsTime(0x8FFFFFFF), 48 | "Comparaison mid number"); 49 | static_assert(OsTime(0xFFFFFFFF) < OsTime(0x0000010), "Comparaison roll over"); 50 | 51 | static_assert(OsTime(11) > OsTime(10), "Comparaison small number"); 52 | static_assert(OsTime(0x8FFFFFFF) > OsTime(0x7FFFFFFF), 53 | "Comparaison mid number"); 54 | static_assert(OsTime(0x0000010) > OsTime(0xFFFFFFFF), "Comparaison roll over"); 55 | -------------------------------------------------------------------------------- /src/lmic/osticks.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _osticks_h_ 3 | #define _osticks_h_ 4 | 5 | #include "config.h" 6 | #include 7 | 8 | #ifndef OSTICKS_PER_SEC 9 | #define OSTICKS_PER_SEC 32768 10 | #elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516 11 | #error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long. 12 | #endif 13 | 14 | class LmicRand; 15 | 16 | class OsDeltaTime { 17 | public: 18 | constexpr explicit OsDeltaTime(int32_t init) noexcept : value(init){}; 19 | constexpr OsDeltaTime() noexcept : value(0){}; 20 | 21 | constexpr static OsDeltaTime from_us(int64_t us) { 22 | return OsDeltaTime(us * OSTICKS_PER_SEC / 1000000); 23 | }; 24 | constexpr static OsDeltaTime from_ms(int64_t ms) { 25 | return OsDeltaTime(ms * OSTICKS_PER_SEC / 1000); 26 | }; 27 | constexpr static OsDeltaTime from_sec(int64_t sec) { 28 | return OsDeltaTime(sec * OSTICKS_PER_SEC); 29 | }; 30 | constexpr static OsDeltaTime from_us_round(int64_t us) { 31 | return OsDeltaTime((us * OSTICKS_PER_SEC + 500000) / 1000000); 32 | }; 33 | static OsDeltaTime rnd_delay(LmicRand &rand, uint8_t sec_span); 34 | 35 | constexpr int32_t to_us() const { 36 | return value * (int64_t)1000000 / OSTICKS_PER_SEC; 37 | }; 38 | constexpr int32_t to_ms() const { 39 | return value * (int64_t)1000 / OSTICKS_PER_SEC; 40 | }; 41 | constexpr int32_t to_s() const { 42 | return value * (int64_t)1 / OSTICKS_PER_SEC; 43 | }; 44 | constexpr int32_t tick() const { return value; }; 45 | 46 | OsDeltaTime &operator+=(const OsDeltaTime &a); 47 | OsDeltaTime &operator-=(const OsDeltaTime &a); 48 | 49 | private: 50 | int32_t value; 51 | }; 52 | 53 | constexpr OsDeltaTime OsInfiniteDeltaTime = OsDeltaTime(INT32_MAX); 54 | 55 | class OsTime { 56 | public: 57 | constexpr OsTime() : OsTime(0){}; 58 | constexpr explicit OsTime(uint32_t init) : value(init){}; 59 | constexpr uint32_t tick() const { return value; }; 60 | 61 | OsTime &operator+=(const OsDeltaTime &a); 62 | OsTime &operator-=(const OsDeltaTime &a); 63 | 64 | private: 65 | uint32_t value; 66 | }; 67 | 68 | constexpr bool operator==(OsDeltaTime const &a, OsDeltaTime const &b) { 69 | return a.tick() == b.tick(); 70 | } 71 | 72 | constexpr bool operator!=(OsDeltaTime const &a, OsDeltaTime const &b) { 73 | return !(a == b); 74 | } 75 | 76 | constexpr OsDeltaTime operator+(OsDeltaTime const &a, OsDeltaTime const &b) { 77 | return OsDeltaTime(a.tick() + b.tick()); 78 | } 79 | 80 | constexpr OsDeltaTime operator*(int16_t const &a, OsDeltaTime const &b) { 81 | return OsDeltaTime(a * b.tick()); 82 | } 83 | 84 | constexpr int32_t operator/(OsDeltaTime const &a, OsDeltaTime const &b) { 85 | return a.tick() / b.tick(); 86 | } 87 | 88 | constexpr bool operator<(OsDeltaTime const &lhs, OsDeltaTime const &rhs) { 89 | return lhs.tick() < rhs.tick(); 90 | } 91 | 92 | constexpr bool operator>(OsDeltaTime const &lhs, OsDeltaTime const &rhs) { 93 | return rhs < lhs; 94 | } 95 | 96 | constexpr bool operator<=(OsDeltaTime const &lhs, OsDeltaTime const &rhs) { 97 | return !(lhs > rhs); 98 | } 99 | 100 | constexpr bool operator>=(OsDeltaTime const &lhs, OsDeltaTime const &rhs) { 101 | return !(lhs < rhs); 102 | } 103 | 104 | constexpr OsTime operator+(OsTime const &a, OsDeltaTime const &b) { 105 | return OsTime(a.tick() + b.tick()); 106 | } 107 | 108 | constexpr OsTime operator-(OsTime const &a, OsDeltaTime const &b) { 109 | return OsTime(a.tick() - b.tick()); 110 | } 111 | 112 | constexpr OsDeltaTime operator-(OsTime const &a, OsTime const &b) { 113 | return OsDeltaTime(a.tick() - b.tick()); 114 | } 115 | 116 | constexpr OsDeltaTime operator<<(OsDeltaTime const a, uint8_t const b) { 117 | return OsDeltaTime(a.tick() << b); 118 | } 119 | 120 | constexpr bool operator<(OsTime const &lhs, OsTime const &rhs) { 121 | return lhs - rhs < OsDeltaTime(0); 122 | } 123 | 124 | constexpr bool operator>(OsTime const &lhs, OsTime const &rhs) { 125 | return rhs < lhs; 126 | } 127 | 128 | constexpr bool operator<=(OsTime const &lhs, OsTime const &rhs) { 129 | return !(lhs > rhs); 130 | } 131 | 132 | constexpr bool operator>=(OsTime const &lhs, OsTime const &rhs) { 133 | return !(lhs < rhs); 134 | } 135 | 136 | #endif // _osticks_h_ -------------------------------------------------------------------------------- /src/lmic/radio.cpp: -------------------------------------------------------------------------------- 1 | #include "radio.h" 2 | 3 | int16_t Radio::get_last_packet_rssi() const { 4 | // see documentation for -139 5 | // do not handle snr > 0 6 | return -139 + last_packet_rssi_reg; 7 | } 8 | 9 | int8_t Radio::get_last_packet_snr_x4() const { 10 | return last_packet_snr_reg; 11 | } 12 | 13 | 14 | Radio::Radio() {} 15 | -------------------------------------------------------------------------------- /src/lmic/radio.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef _radio_h_ 14 | #define _radio_h_ 15 | 16 | #include "../hal/hal_io.h" 17 | #include "lorabase.h" 18 | #include "osticks.h" 19 | #include 20 | #include 21 | 22 | class Radio { 23 | 24 | public: 25 | explicit Radio(); 26 | virtual void init(void) = 0; 27 | virtual void rst() const = 0; 28 | virtual void tx(uint32_t freq, rps_t rps, int8_t txpow, 29 | uint8_t const *framePtr, uint8_t frameLength) = 0; 30 | virtual void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) = 0; 31 | // Rx continuous 32 | virtual void rx(uint32_t freq, rps_t rps) = 0; 33 | 34 | virtual void init_random(std::array &randbuf) = 0; 35 | virtual uint8_t handle_end_rx(FrameBuffer &frame, bool goSleep) = 0; 36 | virtual void handle_end_tx() const = 0; 37 | 38 | virtual uint8_t rssi() const = 0; 39 | 40 | virtual bool io_check() const = 0; 41 | int16_t get_last_packet_rssi() const; 42 | int8_t get_last_packet_snr_x4() const; 43 | 44 | protected: 45 | int8_t last_packet_snr_reg = 0; 46 | uint8_t last_packet_rssi_reg = 0; 47 | 48 | }; 49 | 50 | #endif -------------------------------------------------------------------------------- /src/lmic/radio_fake.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | 3 | *******************************************************************************/ 4 | 5 | #ifndef radio_fake_h 6 | #define radio_fake_h 7 | 8 | #include "lorabase.h" 9 | #include "osticks.h" 10 | #include "radio.h" 11 | #include 12 | #include 13 | 14 | class RadioFake final : public Radio { 15 | public: 16 | struct Packet { 17 | // frequency is zero if not a valid packet 18 | uint32_t freq; 19 | rps_t rps; 20 | std::array data; 21 | uint8_t length; 22 | // end of send time, or start of receive time 23 | OsTime time; 24 | bool is_valid() const { return freq != 0; } 25 | }; 26 | 27 | private: 28 | Packet simulateReceive; 29 | bool isReceived = false; 30 | 31 | OsTime endOfOperation; 32 | Packet lastSend; 33 | 34 | public: 35 | explicit RadioFake(); 36 | 37 | void simulateRx(Packet const &packet); 38 | Packet popLastSend(); 39 | 40 | void init() final; 41 | void rst() const final; 42 | void tx(uint32_t freq, rps_t rps, int8_t txpow, uint8_t const *framePtr, 43 | uint8_t frameLength) final; 44 | void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) final; 45 | void rx(uint32_t freq, rps_t rps) final; 46 | 47 | void init_random(std::array &randbuf) final; 48 | uint8_t handle_end_rx(FrameBuffer &frame, bool goSleep) final; 49 | void handle_end_tx() const final; 50 | bool io_check() const final; 51 | 52 | uint8_t rssi() const final; 53 | }; 54 | 55 | #endif -------------------------------------------------------------------------------- /src/lmic/radio_sx1262.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | 3 | *******************************************************************************/ 4 | 5 | #ifndef _radio_sx1262_h_ 6 | #define _radio_sx1262_h_ 7 | 8 | #include "lorabase.h" 9 | #include "osticks.h" 10 | #include "radio.h" 11 | #include 12 | 13 | enum class ImageCalibrationBand : uint8_t { 14 | band_430_440 = 0, 15 | band_470_510, 16 | band_779_787, 17 | band_863_870, 18 | band_902_928, 19 | }; 20 | 21 | class RadioSx1262 final : public Radio { 22 | private: 23 | HalIo hal; 24 | // ImageCalibrationBand const image_calibration_band; 25 | uint16_t const image_calibration_params; 26 | bool const DIO2_as_rf_switch_ctrl; 27 | 28 | public: 29 | explicit RadioSx1262(lmic_pinmap const &pins, 30 | ImageCalibrationBand calibration_band); 31 | explicit RadioSx1262(lmic_pinmap const &pins, 32 | ImageCalibrationBand calibration_band, 33 | bool dio2_as_rf_switch_ctrl); 34 | void init() final; 35 | void rst() const final; 36 | void tx(uint32_t freq, rps_t rps, int8_t txpow, uint8_t const *framePtr, 37 | uint8_t frameLength) final; 38 | void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) final; 39 | void rx(uint32_t freq, rps_t rps) final; 40 | 41 | void init_random(std::array &randbuf) final; 42 | uint8_t handle_end_rx(FrameBuffer &frame, bool goSleep) final; 43 | void handle_end_tx() const final; 44 | bool io_check() const final; 45 | 46 | uint8_t rssi() const final; 47 | 48 | private: 49 | void set_sleep() const; 50 | void set_standby(bool use_xosc) const; 51 | void set_packet_type_lora() const; 52 | void set_modulation_params_lora(rps_t rps) const; 53 | void set_rf_frequency(uint32_t freq) const; 54 | void set_sync_word_lora() const; 55 | void set_packet_params_lora(rps_t rps, uint8_t frameLength, bool inv) const; 56 | void set_tx_power(int8_t txpow) const; 57 | void set_regulator_mode_dcdc() const; 58 | 59 | void init_config() const; 60 | 61 | void write_frame(uint8_t const *framePtr, uint8_t frameLength) const; 62 | uint8_t read_frame(FrameBuffer &frame) const; 63 | uint8_t get_status() const; 64 | uint16_t get_device_errors() const; 65 | uint16_t get_irq_status() const; 66 | void read_packet_status(); 67 | 68 | void clear_all_irq() const; 69 | void set_dio1_irq_params(uint16_t mask) const; 70 | void set_rx() const; 71 | void set_rx_continious() const; 72 | void set_tx() const; 73 | void set_fs() const; 74 | void set_lora_symb_num_timeout(uint8_t rxsyms) const; 75 | void calibrate_image() const; 76 | uint8_t get_rssi_inst() const; 77 | void set_DIO2_as_rf_switch_ctrl() const; 78 | void calibrate_all() const; 79 | void clear_device_errors() const; 80 | void set_DIO3_as_tcxo_ctrl() const; 81 | 82 | }; 83 | 84 | #endif -------------------------------------------------------------------------------- /src/lmic/radio_sx1272.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | /* 13 | #ifndef _radio_sx1272_h_ 14 | #define _radio_sx1272_h_ 15 | 16 | #include "../hal/hal_io.h" 17 | #include "lorabase.h" 18 | #include "osticks.h" 19 | #include 20 | 21 | class Radio { 22 | 23 | public: 24 | explicit Radio(lmic_pinmap const &pins); 25 | void init(void); 26 | void rst() const; 27 | void tx(uint32_t freq, rps_t rps, int8_t txpow, uint8_t const *framePtr, 28 | uint8_t frameLength); 29 | void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime); 30 | 31 | void init_random(std::array &randbuf); 32 | uint8_t handle_end_rx(uint8_t *framePtr); 33 | void handle_end_tx() const; 34 | 35 | bool io_check() const; 36 | uint8_t rssi() const; 37 | 38 | private: 39 | HalIo hal; 40 | 41 | void opmode(uint8_t mode) const; 42 | void opmodeLora() const; 43 | void configLoraModem(rps_t rps); 44 | void configChannel(uint32_t freq) const; 45 | void configPower(int8_t pw) const; 46 | void rxlora(uint8_t rxmode, uint32_t freq, rps_t rps, uint8_t rxsyms, 47 | OsTime rxtime); 48 | void rxrssi() const; 49 | }; 50 | 51 | #endif 52 | */ -------------------------------------------------------------------------------- /src/lmic/radio_sx1276.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014-2015 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Zurich Research Lab - initial API, implementation and documentation 10 | * Nicolas Graziano - cpp style. 11 | *******************************************************************************/ 12 | 13 | #ifndef lmic_radio_sx1276_h 14 | #define lmic_radio_sx1276_h 15 | 16 | #include "lorabase.h" 17 | #include "osticks.h" 18 | #include "radio.h" 19 | #include 20 | 21 | struct RegSet { 22 | const uint8_t reg; 23 | const uint8_t val; 24 | 25 | constexpr uint16_t raw() const { return reg << 8 | val; }; 26 | constexpr RegSet(uint8_t areg, uint8_t aval) : reg(areg), val(aval){}; 27 | explicit constexpr RegSet(uint16_t raw) : reg(raw >> 8), val(raw & 0xFF){}; 28 | }; 29 | 30 | class RadioSx1276 final : public Radio { 31 | 32 | public: 33 | explicit RadioSx1276(lmic_pinmap const &pins); 34 | void init(void) final; 35 | void rst() const final; 36 | void tx(uint32_t freq, rps_t rps, int8_t txpow, uint8_t const *framePtr, 37 | uint8_t frameLength) final; 38 | void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) final; 39 | void rx(uint32_t freq, rps_t rps) final; 40 | 41 | void init_random(std::array &randbuf) final; 42 | uint8_t handle_end_rx(FrameBuffer &frame, bool goSleep) final; 43 | void handle_end_tx() const final; 44 | bool io_check() const final; 45 | 46 | uint8_t rssi() const final; 47 | 48 | private: 49 | void opmode(uint8_t mode) const; 50 | void opmodeLora() const; 51 | void configLoraModem(rps_t rps); 52 | void configChannel(uint32_t freq) const; 53 | void configPower(int8_t pw) const; 54 | void rxrssi() const; 55 | void clear_irq() const; 56 | void clear_and_disable_irq() const; 57 | void write_list_of_reg(uint16_t const *listcmd, uint8_t nb_cmd) const; 58 | HalIo hal; 59 | }; 60 | 61 | #endif -------------------------------------------------------------------------------- /test/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /test/cert/activation_pretest.h: -------------------------------------------------------------------------------- 1 | #ifndef cert_activation_pretest_h 2 | #define cert_activation_pretest_h 3 | 4 | #include "packet_util.h" 5 | 6 | void sp1_intial_join(TestServerState &server_state); 7 | 8 | #endif -------------------------------------------------------------------------------- /test/cert/aesdecrypt.cpp: -------------------------------------------------------------------------------- 1 | #include "aesdecrypt.h" 2 | #include "aes/aes_encrypt.h" 3 | 4 | void aes128_decrypt(std::array const &key, uint8_t *data, 5 | uint8_t len) { 6 | for (uint8_t i = 0; i < len; i += 16) 7 | aes_tiny_128_decrypt(data + i, key); 8 | } 9 | -------------------------------------------------------------------------------- /test/cert/aesdecrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef cert_aesdecrypt_h 2 | #define cert_aesdecrypt_h 3 | 4 | #include 5 | #include 6 | 7 | void aes128_decrypt(std::array const &key, uint8_t *data, 8 | uint8_t len); 9 | 10 | #endif -------------------------------------------------------------------------------- /test/cert/dut.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "dut.h" 3 | 4 | #include "certificationprotocol.h" 5 | #include "hal/print_debug.h" 6 | #include "keyhandler.h" 7 | namespace { 8 | 9 | RadioFake radio; 10 | LmicEu868 LMIC(radio); 11 | VersionDetails version = {0x01, 0x00, 0x00, 0x00}; 12 | VersionDetails lorawanVersion = {0x01, 0x00, 0x03, 0x00}; 13 | VersionDetails lorawanRpVersion = {0x01, 0x00, 0x00, 0x00}; 14 | CertificationProtocol cert(LMIC, version, lorawanVersion, lorawanRpVersion); 15 | 16 | OsTime nextSend; 17 | 18 | // Application in string format. 19 | constexpr char const appEui[] = "0000000000000001"; 20 | // Device EUI in string format. 21 | constexpr char const devEui[] = "0000000000000002"; 22 | // Application key in string format. 23 | constexpr char const appKey[] = "00000000000000010000000000000002"; 24 | 25 | OsDeltaTime interval_to_send() { 26 | return cert.getPeriodicity() == OsDeltaTime(0) ? OsDeltaTime::from_sec(30) 27 | : cert.getPeriodicity(); 28 | } 29 | 30 | void on_event(EventType evt) { cert.handle(evt); } 31 | 32 | bool should_send_data() { 33 | return !LMIC.getOpMode().test(OpState::TXRXPEND) && 34 | !LMIC.getOpMode().test(OpState::TXDATA) && nextSend < os_getTime(); 35 | } 36 | 37 | } // namespace 38 | 39 | namespace dut { 40 | void reset() { 41 | os_init(); 42 | LMIC.init(); 43 | LMIC.reset(); 44 | LMIC.setEventCallBack(on_event); 45 | LMIC.setClockError(5); 46 | SetupLmicKey::setup(LMIC); 47 | nextSend = os_getTime(); 48 | } 49 | 50 | OsDeltaTime loop() { 51 | if (should_send_data()) { 52 | uint8_t val = 1; 53 | // send fake data 54 | PRINT_DEBUG(1, F("Send Fake DATA")); 55 | 56 | LMIC.setTxData2(1, &val, 1, cert.isNextFrameConfirmed()); 57 | nextSend = os_getTime() + interval_to_send(); 58 | } 59 | auto freeTimeBeforeNextCall = LMIC.run(); 60 | auto timeToNextPacket = nextSend - os_getTime(); 61 | 62 | if (timeToNextPacket < freeTimeBeforeNextCall && 63 | timeToNextPacket > OsDeltaTime(0)) { 64 | return timeToNextPacket; 65 | } 66 | return freeTimeBeforeNextCall; 67 | } 68 | 69 | RadioFake::Packet wait_for_data(OsDeltaTime timeout) { 70 | return wait_for_data(os_getTime() + timeout); 71 | } 72 | 73 | RadioFake::Packet wait_for_data(OsTime timeout) { 74 | RadioFake::Packet packet; 75 | do { 76 | auto toWait = loop(); 77 | packet = radio.popLastSend(); 78 | if (!packet.is_valid() && toWait != OsInfiniteDeltaTime) { 79 | auto wait = std::max(OsDeltaTime(toWait.tick() - 2), OsDeltaTime(1)); 80 | 81 | if (toWait.to_ms() > 10) { 82 | wait = toWait + OsDeltaTime::from_ms(-8); 83 | PRINT_DEBUG(1, F("Skip %d ms (speedup clock)"), wait.to_ms()); 84 | } 85 | hal_add_time_in_sleep(wait); 86 | } 87 | 88 | } while (!packet.is_valid() && os_getTime() < timeout); 89 | return packet; 90 | } 91 | 92 | void send_data(RadioFake::Packet const &packet) { radio.simulateRx(packet); } 93 | 94 | } // namespace dut -------------------------------------------------------------------------------- /test/cert/dut.h: -------------------------------------------------------------------------------- 1 | #ifndef test_cert_dut_h 2 | #define test_cert_dut_h 3 | 4 | #include "lmic.h" 5 | #include "lmic/radio_fake.h" 6 | 7 | namespace dut { 8 | 9 | void reset(); 10 | OsDeltaTime loop(); 11 | RadioFake::Packet wait_for_data(OsTime timeout); 12 | RadioFake::Packet wait_for_data(OsDeltaTime timeout); 13 | 14 | void send_data(RadioFake::Packet const &packet); 15 | 16 | } // namespace dut 17 | #endif -------------------------------------------------------------------------------- /test/cert/packet_util.h: -------------------------------------------------------------------------------- 1 | #ifndef cert_packet_test_h 2 | #define cert_packet_test_h 3 | 4 | #include "aes/lmic_aes.h" 5 | #include "lmic/radio_fake.h" 6 | 7 | #include 8 | 9 | struct TestServerState { 10 | Aes aes; 11 | uint8_t joinNonce = 0; 12 | uint32_t fCntUp = 0; 13 | uint32_t fCntDown = 0; 14 | }; 15 | 16 | constexpr OsDeltaTime JOIN_ACCEPT_DELAY1 = OsDeltaTime::from_sec(5); 17 | 18 | constexpr OsDeltaTime JOIN_ACCEPT_DELAY2 = OsDeltaTime::from_sec(6); 19 | 20 | // constexpr OsDeltaTime RECEIVE_DELAY1; 21 | constexpr OsDeltaTime RECEIVE_DELAY2 = OsDeltaTime::from_sec(2); 22 | 23 | void printpacket(RadioFake::Packet const &packet); 24 | bool is_join_request(RadioFake::Packet const &packet); 25 | bool is_data(RadioFake::Packet const &packet); 26 | bool is_confirmed_uplink(RadioFake::Packet const &packet); 27 | bool is_adr(RadioFake::Packet const &packet); 28 | bool is_ack_set(RadioFake::Packet const &packet); 29 | uint16_t get_dev_nonce(RadioFake::Packet const &packet); 30 | uint8_t get_port(RadioFake::Packet const &packet); 31 | 32 | RadioFake::Packet make_join_response(TestServerState &state); 33 | RadioFake::Packet make_data_response(uint8_t port, 34 | std::vector const &data, 35 | bool acknowledged, TestServerState &state, 36 | bool confirmed = false, 37 | std::vector const &fOpts = {}); 38 | RadioFake::Packet make_empty_response(bool acknowledged, 39 | TestServerState &state); 40 | void read_join_key(uint16_t devNonce, TestServerState &state); 41 | bool check_is_next_packet(RadioFake::Packet const &packet, 42 | TestServerState &state); 43 | std::vector get_mac_command_values(RadioFake::Packet const &packet, 44 | TestServerState const &state); 45 | 46 | std::vector get_payload(RadioFake::Packet const &packet, 47 | TestServerState const &state); 48 | 49 | #endif -------------------------------------------------------------------------------- /test/cert/test_activation_pretest/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../activation_pretest.h" 5 | #include "../dut.h" 6 | #include "../packet_util.h" 7 | 8 | #ifdef ARDUINO 9 | #include 10 | #endif 11 | 12 | TestServerState server_state; 13 | 14 | void setUp(void) { dut::reset(); } 15 | 16 | void initial_join() { sp1_intial_join(server_state); } 17 | 18 | void tearDown(void) { 19 | // clean stuff up here 20 | } 21 | 22 | void runUnityTests(void) { 23 | UNITY_BEGIN(); 24 | RUN_TEST(initial_join); 25 | UNITY_END(); 26 | } 27 | 28 | #ifdef ARDUINO 29 | void setup() { 30 | delay(2000); 31 | runUnityTests(); 32 | } 33 | 34 | void loop() { 35 | #if defined(set_sleep_mode) && defined(sleep_mode) 36 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 37 | sleep_mode(); 38 | #endif 39 | } 40 | 41 | #else 42 | 43 | int main() { runUnityTests(); } 44 | 45 | #endif -------------------------------------------------------------------------------- /test/cert/test_mac_command/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | TestServerState server_state; -------------------------------------------------------------------------------- /test/cert/test_mac_command/common.h: -------------------------------------------------------------------------------- 1 | #ifndef cert_test_mac_command_common_h 2 | #define cert_test_mac_command_common_h 3 | 4 | #include 5 | #include 6 | 7 | #include "../activation_pretest.h" 8 | #include "../dut.h" 9 | #include "../packet_util.h" 10 | 11 | extern TestServerState server_state; 12 | 13 | constexpr OsDeltaTime defaultWaitTime = OsDeltaTime::from_sec(60); 14 | 15 | // List of test : 16 | void test_dev_status_req(); 17 | void test_new_channel_req(); 18 | void test_RX_timing_setup_req(); 19 | void test_TX_param_setup_req(); 20 | 21 | // §2.5.13 22 | void test_incorrect_mac_command(); 23 | // §2.5.14 24 | void test_multiple_mac_commands_prioritization(); 25 | 26 | #endif // cert_test_mac_command_common_h -------------------------------------------------------------------------------- /test/cert/test_mac_command/dev-status-req.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | void test_dev_status_req() { 4 | // Step 1 5 | // DUT sends Unconfirmed frame 6 | auto nextPacket = dut::wait_for_data(defaultWaitTime); 7 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 8 | TEST_ASSERT(is_data(nextPacket)); 9 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 10 | 11 | // The TCL sends Unconfirmed frame 12 | // MAC-CMD DevStatusReq 13 | // Payload [0x]06 14 | auto nextResponse = 15 | make_data_response(0, std::vector{0x06}, false, server_state); 16 | nextResponse.time = nextPacket.time + RECEIVE_DELAY2; 17 | dut::send_data(nextResponse); 18 | 19 | // Step 2 20 | // DUT sends Unconfirmed frame 21 | // MAC-CMD DevStatusAns 22 | // RadioStatus >= - 32 and <= 31 23 | // Payload [0x]06XXXX 24 | nextPacket = dut::wait_for_data(defaultWaitTime); 25 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 26 | TEST_ASSERT(is_data(nextPacket)); 27 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 28 | auto devStatusAns = get_mac_command_values(nextPacket, server_state); 29 | TEST_ASSERT_EQUAL_UINT(3, devStatusAns.size()); 30 | TEST_ASSERT_EQUAL_UINT8(0x06, devStatusAns[0]); 31 | } 32 | -------------------------------------------------------------------------------- /test/cert/test_mac_command/incorrect_mac_command.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | void test_incorrect_mac_command() { 4 | // Step 1 5 | // DUT sends Unconfirmed frame 6 | auto nextPacket = dut::wait_for_data(defaultWaitTime); 7 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 8 | TEST_ASSERT(is_data(nextPacket)); 9 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 10 | 11 | // The TCL sends Unconfirmed frame 12 | // MAC-CMD LinkADRReq 13 | // Payload = [0x]0380000000 14 | // FPort = 0 15 | auto nextResponse = 16 | make_data_response(0, std::vector{0x03, 0x80, 0x00, 0x00, 0x00}, 17 | false, server_state); 18 | nextResponse.time = nextPacket.time + RECEIVE_DELAY2; 19 | dut::send_data(nextResponse); 20 | 21 | // Step 2 22 | // DUT sends Unconfirmed frame 23 | // LinkADRAns NOT = OK 24 | nextPacket = dut::wait_for_data(defaultWaitTime); 25 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 26 | TEST_ASSERT(is_data(nextPacket)); 27 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 28 | auto macResponse = get_mac_command_values(nextPacket, server_state); 29 | TEST_ASSERT_EQUAL_UINT(2, macResponse.size()); 30 | TEST_ASSERT_EQUAL_UINT8(0x03, macResponse[0]); 31 | TEST_ASSERT_NOT_EQUAL_UINT8(0b00000111, macResponse[1]); 32 | 33 | // The TCL sends Unconfirmed frame 34 | // MAC-CMD LinkADRReq 35 | // Payload = [0x]03010000 36 | // FPort = 0 37 | nextResponse = make_data_response( 38 | 0, std::vector{0x03, 0x01, 0x00, 0x00}, false, server_state); 39 | nextResponse.time = nextPacket.time + RECEIVE_DELAY2; 40 | dut::send_data(nextResponse); 41 | 42 | // Step 3 43 | // DUT sends Unconfirmed frame 44 | // No response 45 | nextPacket = dut::wait_for_data(defaultWaitTime); 46 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 47 | TEST_ASSERT(is_data(nextPacket)); 48 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 49 | macResponse = get_mac_command_values(nextPacket, server_state); 50 | TEST_ASSERT_EQUAL_UINT(0, macResponse.size()); 51 | 52 | // The TCL sends Unconfirmed frame 53 | // MAC-CMD 54 | // Payload = [0x]7F 55 | // FPort = 0 56 | nextResponse = 57 | make_data_response(0, std::vector{0x7F}, false, server_state); 58 | nextResponse.time = nextPacket.time + RECEIVE_DELAY2; 59 | dut::send_data(nextResponse); 60 | 61 | // Step 4 62 | // DUT sends Unconfirmed frame 63 | // No response 64 | nextPacket = dut::wait_for_data(defaultWaitTime); 65 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 66 | TEST_ASSERT(is_data(nextPacket)); 67 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 68 | macResponse = get_mac_command_values(nextPacket, server_state); 69 | TEST_ASSERT_EQUAL_UINT(0, macResponse.size()); 70 | 71 | // The TCL sends Unconfirmed frame 72 | // MAC-CMD1 DevStatusReq 73 | // MAC-CMD2 incomplete 74 | // LinkADRReq 75 | // Payload = [0x]0603010000 76 | // FPort = 0 77 | nextResponse = 78 | make_data_response(0, std::vector{0x06, 0x03, 0x01, 0x00, 0x00}, 79 | false, server_state); 80 | nextResponse.time = nextPacket.time + RECEIVE_DELAY2; 81 | dut::send_data(nextResponse); 82 | 83 | // Step 5 84 | // DUT sends Unconfirmed frame 85 | // MAC-CMD1 DevStatusAns 86 | // Payload = [0x]06XXXX 87 | nextPacket = dut::wait_for_data(defaultWaitTime); 88 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 89 | TEST_ASSERT(is_data(nextPacket)); 90 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 91 | macResponse = get_mac_command_values(nextPacket, server_state); 92 | TEST_ASSERT_EQUAL_UINT(3, macResponse.size()); 93 | TEST_ASSERT_EQUAL_UINT8(0x06, macResponse[0]); 94 | 95 | // The TCL sends Unconfirmed frame 96 | // MAC-CMD1 LinkADRReq 97 | // MAC-CMD2 [0x]7F 98 | // CMD3 DevStatusReq 99 | // FPort = 0 100 | nextResponse = make_data_response( 101 | 0, std::vector{0x06}, false, server_state, false, 102 | std::vector{0x03, 0x80, 0x00, 0x00, 0x00, 0x7F}); 103 | nextResponse.time = nextPacket.time + RECEIVE_DELAY2; 104 | dut::send_data(nextResponse); 105 | 106 | // Step 6 107 | // DUT sends Unconfirmed frame 108 | // MAC-CMD1 LinkADRAns Payload = [0x]03XXXXXXXX 109 | nextPacket = dut::wait_for_data(defaultWaitTime); 110 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 111 | TEST_ASSERT(is_data(nextPacket)); 112 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 113 | macResponse = get_mac_command_values(nextPacket, server_state); 114 | TEST_ASSERT_EQUAL_UINT(2, macResponse.size()); 115 | TEST_ASSERT_EQUAL_UINT8(0x03, macResponse[0]); 116 | TEST_ASSERT_NOT_EQUAL_UINT8(0b00000111, macResponse[1]); 117 | } -------------------------------------------------------------------------------- /test/cert/test_mac_command/main.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #ifdef ARDUINO 4 | #include 5 | #endif 6 | 7 | void setUp(void) { 8 | dut::reset(); 9 | sp1_intial_join(server_state); 10 | } 11 | 12 | void tearDown(void) { 13 | // clean stuff up here 14 | } 15 | 16 | void runUnityTests(void) { 17 | UNITY_BEGIN(); 18 | RUN_TEST(test_dev_status_req); 19 | RUN_TEST(test_new_channel_req); 20 | RUN_TEST(test_RX_timing_setup_req); 21 | RUN_TEST(test_TX_param_setup_req); 22 | RUN_TEST(test_incorrect_mac_command); 23 | RUN_TEST(test_multiple_mac_commands_prioritization); 24 | UNITY_END(); 25 | } 26 | 27 | #ifdef ARDUINO 28 | void setup() { 29 | delay(2000); 30 | runUnityTests(); 31 | } 32 | 33 | void loop() { 34 | #if defined(set_sleep_mode) && defined(sleep_mode) 35 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 36 | sleep_mode(); 37 | #endif 38 | } 39 | 40 | #else 41 | int main() { runUnityTests(); } 42 | #endif -------------------------------------------------------------------------------- /test/cert/test_mac_command/tx-param-setup-req.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | void test_TX_param_setup_req() { 4 | // test_TX_param_setup_req is not implement in EU868 5 | // so use 2.5.6.a 6 | 7 | // Step 1 8 | // DUT sends Unconfirmed frame 9 | auto nextPacket = dut::wait_for_data(defaultWaitTime); 10 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 11 | TEST_ASSERT(is_data(nextPacket)); 12 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 13 | 14 | // The TCL sends Unconfirmed frame 15 | // MAC-CMD 16 | // TXParamSetupReq 17 | // Payload = [0x]09XX 18 | // UplinkDwellTime = 0 19 | auto nextResponse = 20 | make_data_response(0, std::vector{0x09, 0}, false, server_state); 21 | nextResponse.time = 22 | nextPacket.time + OsDeltaTime::from_sec(16); 23 | dut::send_data(nextResponse); 24 | 25 | // Step 2 26 | // DUT sends Unconfirmed frame 27 | nextPacket = dut::wait_for_data(defaultWaitTime); 28 | TEST_ASSERT(check_is_next_packet(nextPacket, server_state)); 29 | TEST_ASSERT(is_data(nextPacket)); 30 | TEST_ASSERT_FALSE(is_confirmed_uplink(nextPacket)); 31 | auto macResponse = get_mac_command_values(nextPacket, server_state); 32 | TEST_ASSERT_EQUAL_UINT(0, macResponse.size()); 33 | } 34 | -------------------------------------------------------------------------------- /test/test_various/test_aes.h: -------------------------------------------------------------------------------- 1 | #ifndef __test_aes_h__ 2 | #define __test_aes_h__ 3 | 4 | namespace test_aes { 5 | void run(); 6 | void test_aes_key(); 7 | void test_aes_encript_with_key0(); 8 | void test_aes_decript_with_key0(); 9 | void test_aes_encript_with_buff0(); 10 | void test_aes_decript_with_buff0(); 11 | void test_aes_mic(); 12 | void test_aes_micverify(); 13 | void test_aes_mic_packet(); 14 | } // namespace test_aes 15 | 16 | #endif -------------------------------------------------------------------------------- /test/test_various/test_eu868channels.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "test_eu868channels.h" 4 | 5 | #include "lmic/lmic.eu868.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace test_eu868channels { 11 | 12 | void run() { RUN_TEST(test_default_join_frequency); } 13 | 14 | void test_default_join_frequency() { 15 | 16 | Aes aes; 17 | LmicRand rand{aes}; 18 | Eu868RegionalChannelParams testObj(rand); 19 | 20 | testObj.initDefaultChannels(); 21 | 22 | testObj.initJoinLoop(); 23 | 24 | std::set frequencies; 25 | std::set spreadingFactors; 26 | std::set> frequenciesAndSpreadingFactors; 27 | 28 | for (int i = 0; i < 200; i++) { 29 | auto join = testObj.nextJoinState(); 30 | 31 | // pour les premier 10 join, on ne doit pas encore 32 | // etre en echec 33 | if (i < 10) { 34 | TEST_ASSERT_EQUAL(true, join.status); 35 | } 36 | 37 | auto param = testObj.getTxParameter(); 38 | 39 | // param.frequency is in the list (868.10 MHz, 868.30 MHz, 868.50 MHz) 40 | TEST_ASSERT_TRUE(param.frequency == 868100000 || 41 | param.frequency == 868300000 || 42 | param.frequency == 868500000); 43 | 44 | frequencies.insert(param.frequency); 45 | 46 | // datarate is in DR0, DR1, DR2, DR3, DR4, D5 47 | // so spreading factor is in SF12, SF11, SF10, SF9, SF8, SF7 48 | // and bandwidth is 125 kHz 49 | TEST_ASSERT_TRUE(param.rps.sf == SF12 || param.rps.sf == SF11 || 50 | param.rps.sf == SF10 || param.rps.sf == SF9 || 51 | param.rps.sf == SF8 || param.rps.sf == SF7); 52 | spreadingFactors.insert(param.rps.sf); 53 | 54 | sf_t sf = param.rps.sf; 55 | frequenciesAndSpreadingFactors.insert( 56 | std::make_tuple(param.frequency, sf)); 57 | 58 | TEST_ASSERT_EQUAL(BandWidth::BW125, param.rps.getBw()); 59 | } 60 | 61 | // all 3 default frequencies are used 62 | TEST_ASSERT_EQUAL(3, frequencies.size()); 63 | // all 6 spreading factors are used 64 | TEST_ASSERT_EQUAL(6, spreadingFactors.size()); 65 | 66 | // all 18 combinations of frequencies and spreading factors are used 67 | TEST_ASSERT_EQUAL(18, frequenciesAndSpreadingFactors.size()); 68 | } 69 | } // namespace test_eu868channels -------------------------------------------------------------------------------- /test/test_various/test_eu868channels.h: -------------------------------------------------------------------------------- 1 | #ifndef test_eu868channels_h 2 | #define test_eu868channels_h 3 | 4 | namespace test_eu868channels { 5 | void run(); 6 | void test_default_join_frequency(); 7 | } // namespace test_eu868channels 8 | 9 | #endif -------------------------------------------------------------------------------- /test/test_various/test_keyhandler.cpp: -------------------------------------------------------------------------------- 1 | #include "test_keyhandler.h" 2 | #include "keyhandler.h" 3 | #include "unity.h" 4 | #include 5 | 6 | namespace test_keyhandler { 7 | 8 | void run() { RUN_TEST(test_eui_getter); } 9 | 10 | constexpr char const appEui[] = "1112456789ABCDF0"; 11 | const std::array wanted_eui{0xF0, 0xCD, 0xAB, 0x89, 12 | 0x67, 0x45, 0x12, 0x11}; 13 | 14 | void test_eui_getter() { 15 | EuiGetter obj; 16 | 17 | std::array eui; 18 | obj.getEui(eui.begin()); 19 | 20 | TEST_ASSERT_EQUAL_MEMORY(wanted_eui.begin(), eui.begin(), 21 | wanted_eui.max_size()); 22 | } 23 | 24 | constexpr char const appKey[] = "0123456789ABCDEFFFEEDDCCBBAA9988"; 25 | const std::array wanted_key{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 26 | 0xCD, 0xEF, 0xFF, 0xEE, 0xDD, 0xCC, 27 | 0xBB, 0xAA, 0x99, 0x88}; 28 | 29 | void test_key_getter() { 30 | KeyGetter obj; 31 | 32 | auto key = obj.getKey(); 33 | 34 | TEST_ASSERT_EQUAL_MEMORY(wanted_eui.begin(), key.begin(), 35 | wanted_eui.max_size()); 36 | } 37 | 38 | } // namespace test_keyhandler 39 | -------------------------------------------------------------------------------- /test/test_various/test_keyhandler.h: -------------------------------------------------------------------------------- 1 | #ifndef test_keyhandler_h 2 | #define test_keyhandler_h 3 | 4 | namespace test_keyhandler { 5 | void run(); 6 | void test_eui_getter(); 7 | } // namespace test_keyhandler 8 | 9 | #endif -------------------------------------------------------------------------------- /test/test_various/test_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef ARDUINO 4 | #include 5 | #endif 6 | 7 | #include "test_aes.h" 8 | #include "test_keyhandler.h" 9 | #include "test_eu868channels.h" 10 | 11 | void setUp(void) { 12 | // set stuff up here 13 | } 14 | 15 | void tearDown(void) { 16 | // clean stuff up here 17 | } 18 | 19 | int runUnityTests(void) { 20 | UNITY_BEGIN(); 21 | test_keyhandler::run(); 22 | test_aes::run(); 23 | test_eu868channels::run(); 24 | UNITY_END(); 25 | return 0; 26 | } 27 | 28 | #ifdef ARDUINO 29 | void setup() { 30 | delay(2000); 31 | runUnityTests(); 32 | } 33 | 34 | void loop() { 35 | #if defined(set_sleep_mode) && defined(sleep_mode) 36 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); 37 | sleep_mode(); 38 | #endif 39 | } 40 | 41 | #else 42 | int main() { return runUnityTests(); } 43 | 44 | #endif --------------------------------------------------------------------------------