diff --git a/.github/workflows/build-examples.yml b/.github/workflows/build-examples.yml index dc9449d..a5184c3 100644 --- a/.github/workflows/build-examples.yml +++ b/.github/workflows/build-examples.yml @@ -13,11 +13,14 @@ jobs: run: | python -m pip install --upgrade pip pip install platformio + platformio platform install native # Workaround. pip install "click!=8.0.2" + - name: Run unit test + run: platformio test -e native - name: Build simple exemple - run: platformio ci --lib="." --board=pro8MHzatmega328 --project-option="lib_deps=ArduinoSTL" examples/simple + run: platformio ci --lib="." --board=pro8MHzatmega328 --project-option="lib_deps=ciband/avr_stl" examples/simple - name: Build simple sx1262 exemple - run: platformio ci --lib="." --board=pro8MHzatmega328 --project-option="lib_deps=ArduinoSTL" examples/simple_sx1262 + run: platformio ci --lib="." --board=pro8MHzatmega328 --project-option="lib_deps=ciband/avr_stl" examples/simple_sx1262 - name: Build esp32 exemple run: platformio ci --lib="." --board=heltec_wifi_lora_32 examples/esp32 diff --git a/.vscode/settings.json b/.vscode/settings.json index 0f093cb..50399a0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -53,6 +53,7 @@ "string_iostream": "cpp", "type_traits": "cpp", "typeinfo": "cpp", - "utility": "cpp" + "utility": "cpp", + "array": "cpp" }, } \ No newline at end of file diff --git a/README.md b/README.md index 6083fbe..59fa023 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This library try to comply with lorawan 1.0.x but do not implement all feature. For a more complete Lorawan library based on lmic, please have a look to -:warning: This library do not compile in Arduino IDE due to dependency to STL (ArduinoSTL in case of AVR platform). +:warning: This library do not compile in Arduino IDE due to dependency to STL (ciband/avr_stl in case of AVR platform). It need PlatfomIO for the dependencies to be handle correctly. For the SX1262 it only support board with TCXO. diff --git a/examples/BME280_2.4v/platformio.ini b/examples/BME280_2.4v/platformio.ini index ac3a3ce..89cb92f 100644 --- a/examples/BME280_2.4v/platformio.ini +++ b/examples/BME280_2.4v/platformio.ini @@ -23,7 +23,7 @@ board_build.f_cpu = 2000000L lib_deps = - ArduinoSTL + ciband/avr_stl https://github.com/ngraziano/LMICPP-Arduino.git \ No newline at end of file diff --git a/examples/balise/platformio.ini b/examples/balise/platformio.ini index 78e438b..120d44a 100644 --- a/examples/balise/platformio.ini +++ b/examples/balise/platformio.ini @@ -21,6 +21,6 @@ build_flags = -Wall -Wextra -O3 upload_speed = 38400 lib_deps = - ArduinoSTL + ciband/avr_stl https://github.com/ngraziano/LMICPP-Arduino.git \ No newline at end of file diff --git a/examples/balise_2.4v/platformio.ini b/examples/balise_2.4v/platformio.ini index 8bf85f4..836eed4 100644 --- a/examples/balise_2.4v/platformio.ini +++ b/examples/balise_2.4v/platformio.ini @@ -23,7 +23,7 @@ board_build.f_cpu = 2000000L lib_deps = - ArduinoSTL + ciband/avr_stl https://github.com/ngraziano/LMICPP-Arduino.git \ No newline at end of file diff --git a/examples/simple/platformio.ini b/examples/simple/platformio.ini index e8ae2dc..f98e95a 100644 --- a/examples/simple/platformio.ini +++ b/examples/simple/platformio.ini @@ -21,6 +21,6 @@ build_flags = -Wall -Wextra -O3 # upload_speed = 38400 lib_deps = - ArduinoSTL + ciband/avr_stl ngraziano/LMICPP-Arduino \ No newline at end of file diff --git a/examples/simple_sx1262/platformio.ini b/examples/simple_sx1262/platformio.ini index 4adcf88..1eb15c0 100644 --- a/examples/simple_sx1262/platformio.ini +++ b/examples/simple_sx1262/platformio.ini @@ -23,6 +23,6 @@ board_build.f_cpu = 8000000L upload_speed = 38400 lib_deps = - ArduinoSTL + ciband/avr_stl https://github.com/ngraziano/LMICPP-Arduino.git \ No newline at end of file diff --git a/examples/tempsensor/platformio.ini b/examples/tempsensor/platformio.ini index 7fa13c0..933617a 100644 --- a/examples/tempsensor/platformio.ini +++ b/examples/tempsensor/platformio.ini @@ -21,7 +21,7 @@ build_flags = -Wall -Wextra -O3 upload_speed = 38400 lib_deps = - ArduinoSTL + ciband/avr_stl DallasTemperature https://github.com/ngraziano/LMICPP-Arduino.git \ No newline at end of file diff --git a/library.json b/library.json index cb070d7..0a74843 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "LMICPP-Arduino", - "version": "2.1.2", + "version": "2.2.0", "keywords": "Lora", "description": "Modified Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. Changed to C++ format.", "frameworks": ["arduino"], diff --git a/library.properties b/library.properties index 11fc152..6bc9793 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=LMICPP-Arduino -version=2.1.2 +version=2.2.0 author=IBM maintainer=Nicolas Graziano sentence=Modified Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. Changed to C++ format. diff --git a/platformio.ini b/platformio.ini index 896b92b..e9ae6e4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,7 +27,7 @@ build_flags = -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE test_build_project_src = true lib_deps = - ArduinoSTL + ciband/avr_stl [env:esp32] platform = espressif32 @@ -40,4 +40,22 @@ monitor_speed = 19200 test_build_project_src=true build_flags = -Wall -Wextra -O3 -DENABLE_SAVE_RESTORE +lib_deps = + + +[env:windows] +platform = windows_x86 +# lib_compat_mode = off +build_flags = -std=c++17 -Wall -Wextra -O3 +test_build_project_src=true +test_transport = native +lib_deps = + + +[env:native] +platform = native +# lib_compat_mode = off +build_flags = -std=c++17 -Wall -Wextra -O3 +test_build_project_src=true +test_transport = native lib_deps = \ No newline at end of file diff --git a/src/aes/aes_encrypt.h b/src/aes/aes_encrypt.h index 012ac9a..4d98cef 100644 --- a/src/aes/aes_encrypt.h +++ b/src/aes/aes_encrypt.h @@ -2,30 +2,19 @@ #ifndef __aes_tiny_h__ #define __aes_tiny_h__ +#include #include -struct AesKey { - static const uint8_t key_size = 16; - uint8_t data[key_size]; - - uint8_t *begin() { return data; }; - uint8_t *end() { return data + key_size; }; - - - uint8_t const *begin() const { return data; }; - uint8_t const *end() const { return data + key_size; }; - - - AesKey() = default; -}; +constexpr uint8_t key_size = 16; +using AesKey = std::array; void aes_tiny_128_encrypt(uint8_t *buffer, AesKey const &key); #ifdef ARDUINO_ARCH_ESP32 void aes_esp_128_encrypt(uint8_t *buffer, AesKey const &key); -constexpr auto aes_128_encrypt=aes_esp_128_encrypt; +constexpr auto aes_128_encrypt = aes_esp_128_encrypt; #else -constexpr auto aes_128_encrypt=aes_tiny_128_encrypt; +constexpr auto aes_128_encrypt = aes_tiny_128_encrypt; #endif #endif \ No newline at end of file diff --git a/src/aes/aes_tiny.cpp b/src/aes/aes_tiny.cpp index 3b722e0..4035375 100644 --- a/src/aes/aes_tiny.cpp +++ b/src/aes/aes_tiny.cpp @@ -31,31 +31,41 @@ Change to use this project type and adapt to some C++ type. #include "aes_encrypt.h" #include +#include #include + +#ifdef ARDUINO #ifdef __AVR__ #include -#else +#else #include #endif - +#else +#define PROGMEM +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif namespace { struct Indices { - uint8_t col; - uint8_t row; + const uint8_t col; + const uint8_t row; }; struct DataBlock { static constexpr uint8_t data_size = 16; - uint8_t data[data_size]; - uint8_t *begin() { return data; }; + uint8_t *begin() { return data.begin(); }; + uint8_t const *begin() const { return data.begin(); }; - uint8_t *end() { return data + data_size; }; + uint8_t *end() { return data.end(); }; + uint8_t const *end() const { return data.end(); }; - uint8_t &operator[](Indices i) { return data[i.col * 4 + i.row]; } - uint8_t *column(uint8_t col) { return data + (4 * col); } + uint8_t &operator[](Indices const i) { return data[i.col * 4 + i.row]; } + uint8_t *column(uint8_t const col) { return data.begin() + (4 * col); } + +private: + std::array data; }; constexpr uint8_t sbox[256] PROGMEM = { @@ -98,9 +108,8 @@ static inline uint8_t readsbox(uint8_t val) { // Rcon(i), 2^(i+1) in the Rijndael finite field, for i = 0..9. // http://en.wikipedia.org/wiki/Rijndael_key_schedule -constexpr uint8_t const rcon[10] PROGMEM = {0x01, 0x02, 0x04, - 0x08, 0x10, 0x20, 0x40, - 0x80, 0x1B, 0x36}; +constexpr uint8_t const rcon[10] PROGMEM = {0x01, 0x02, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x80, 0x1B, 0x36}; static inline void keyScheduleCore(uint8_t *output, const uint8_t *input, uint8_t iteration) { @@ -176,16 +185,16 @@ void mixColumn(uint8_t *buffer) { buffer[3] = a2 ^ a ^ b ^ c ^ d2; } -void kcore(uint8_t n, uint8_t schedule[16]) { +void kcore(uint8_t n, AesKey &schedule) { uint8_t temp[4]; - keyScheduleCore(temp, schedule + 12, n); + keyScheduleCore(temp, schedule.begin() + 12, n); schedule[0] ^= temp[0]; schedule[1] ^= temp[1]; schedule[2] ^= temp[2]; schedule[3] ^= temp[3]; } -void kxor(uint8_t a, uint8_t b, uint8_t schedule[16]) { +void kxor(uint8_t const a, uint8_t const b, AesKey &schedule) { schedule[a * 4] ^= schedule[b * 4]; schedule[a * 4 + 1] ^= schedule[b * 4 + 1]; schedule[a * 4 + 2] ^= schedule[b * 4 + 2]; @@ -193,17 +202,29 @@ void kxor(uint8_t a, uint8_t b, uint8_t schedule[16]) { } void expand_key(AesKey &schedule, uint8_t round) { - kcore(round, schedule.data); - kxor(1, 0, schedule.data); - kxor(2, 1, schedule.data); - kxor(3, 2, schedule.data); + kcore(round, schedule); + kxor(1, 0, schedule); + kxor(2, 1, schedule); + kxor(3, 2, schedule); } void xorbuffer(uint8_t const *source1, AesKey &source2, uint8_t *dest) { - std::transform(source1, source1 + 16, source2.data, dest, + std::transform(source1, source1 + 16, source2.begin(), dest, [](uint8_t a, uint8_t b) { return a ^ b; }); } +void xorbuffer(DataBlock const &source1, AesKey &source2, uint8_t *dest) { + xorbuffer(source1.begin(), source2, dest); +} + +void xorbuffer(uint8_t const *source1, AesKey &source2, DataBlock &dest) { + xorbuffer(source1, source2, dest.begin()); +} + +void xorbuffer(DataBlock const &source1, AesKey &source2, DataBlock &dest) { + xorbuffer(source1.begin(), source2, dest.begin()); +} + } // namespace void aes_tiny_128_encrypt(uint8_t *buffer, AesKey const &key) { @@ -213,7 +234,7 @@ void aes_tiny_128_encrypt(uint8_t *buffer, AesKey const &key) { DataBlock state1; // Copy the input into the state and XOR with the key schedule. - xorbuffer(buffer, schedule, state1.data); + xorbuffer(buffer, schedule, state1); // Perform the first 9 rounds of the cipher. for (uint8_t round = 0; round < 9; ++round) { @@ -226,7 +247,7 @@ void aes_tiny_128_encrypt(uint8_t *buffer, AesKey const &key) { mixColumn(state1.column(1)); mixColumn(state1.column(2)); mixColumn(state1.column(3)); - xorbuffer(state1.data, schedule, state1.data); + xorbuffer(state1, schedule, state1); } // Expand the final 16 bytes of the key schedule. @@ -234,5 +255,5 @@ void aes_tiny_128_encrypt(uint8_t *buffer, AesKey const &key) { // Perform the final round. subBytesAndShiftRows(state1); - xorbuffer(state1.data, schedule, buffer); + xorbuffer(state1, schedule, buffer); } diff --git a/src/aes/asp_esp.cpp b/src/aes/asp_esp.cpp index 47e925a..18c86c6 100644 --- a/src/aes/asp_esp.cpp +++ b/src/aes/asp_esp.cpp @@ -7,7 +7,7 @@ void aes_esp_128_encrypt(uint8_t *buffer, AesKey const &key) { mbedtls_aes_context keyCtx; mbedtls_aes_init(&keyCtx); - mbedtls_aes_setkey_enc(&keyCtx, key.data, 128); + mbedtls_aes_setkey_enc(&keyCtx, key.data(), 128); mbedtls_aes_crypt_ecb(&keyCtx, ESP_AES_ENCRYPT, buffer, buffer); mbedtls_aes_free(&keyCtx); } diff --git a/src/aes/limc_aes.cpp b/src/aes/limc_aes.cpp index 8e39a87..0b7adcf 100644 --- a/src/aes/limc_aes.cpp +++ b/src/aes/limc_aes.cpp @@ -17,27 +17,33 @@ #include "../lmic/lorawanpacket.h" #include "lmic_aes.h" #include +#include using namespace lorawan; +void block_encrypt(AesBlock &block, AesKey const &key) { + aes_128_encrypt(block.begin(), key); +} + void Aes::setDevKey(AesKey const &key) { AESDevKey = key; } void Aes::setNetworkSessionKey(AesKey const &key) { nwkSKey = key; } void Aes::setApplicationSessionKey(AesKey const &key) { appSKey = key; } // Get B0 value in buf -void Aes::micB0(const uint32_t devaddr, const uint32_t seqno, - const PktDir dndir, const uint8_t len, - uint8_t buf[AES_BLCK_SIZE]) { +AesBlock Aes::micB0(const uint32_t devaddr, const uint32_t seqno, + const PktDir dndir, const uint8_t len) { + AesBlock buf; buf[0] = 0x49; buf[1] = 0; buf[2] = 0; buf[3] = 0; buf[4] = 0; buf[5] = static_cast(dndir); - wlsbf4(buf + 6, devaddr); - wlsbf4(buf + 10, seqno); + wlsbf4(buf.begin() + 6, devaddr); + wlsbf4(buf.begin() + 10, seqno); buf[14] = 0; buf[15] = len; + return buf; } /** @@ -47,11 +53,11 @@ void Aes::micB0(const uint32_t devaddr, const uint32_t seqno, bool Aes::verifyMic(const uint32_t devaddr, const uint32_t seqno, const PktDir dndir, const uint8_t *const pdu, const uint8_t len) const { - uint8_t buf[AES_BLCK_SIZE]; const uint8_t lenWithoutMic = len - lengths::MIC; - micB0(devaddr, seqno, dndir, lenWithoutMic, buf); + AesBlock buf = micB0(devaddr, seqno, dndir, lenWithoutMic); aes_cmac(pdu, lenWithoutMic, true, nwkSKey, buf); - return std::equal(buf, buf + lengths::MIC, pdu + lenWithoutMic); + return std::equal(buf.begin(), buf.begin() + lengths::MIC, + pdu + lenWithoutMic); } /** @@ -61,12 +67,11 @@ bool Aes::verifyMic(const uint32_t devaddr, const uint32_t seqno, void Aes::appendMic(const uint32_t devaddr, const uint32_t seqno, const PktDir dndir, uint8_t *const pdu, const uint8_t len) const { - uint8_t buf[AES_BLCK_SIZE]; const uint8_t lenWithoutMic = len - lengths::MIC; - micB0(devaddr, seqno, dndir, lenWithoutMic, buf); + AesBlock buf = micB0(devaddr, seqno, dndir, lenWithoutMic); aes_cmac(pdu, lenWithoutMic, true, nwkSKey, buf); // Copy MIC at the end - std::copy(buf, buf + lengths::MIC, pdu + lenWithoutMic); + std::copy(buf.begin(), buf.begin() + lengths::MIC, pdu + lenWithoutMic); } /** @@ -74,11 +79,11 @@ void Aes::appendMic(const uint32_t devaddr, const uint32_t seqno, * len : total length (MIC included) */ void Aes::appendMic0(uint8_t *const pdu, const uint8_t len) const { - uint8_t buf[AES_BLCK_SIZE] = {0}; + AesBlock buf = {0}; const uint8_t lenWithoutMic = len - lengths::MIC; aes_cmac(pdu, lenWithoutMic, false, AESDevKey, buf); // Copy MIC0 at the end - std::copy(buf, buf + lengths::MIC, pdu + lenWithoutMic); + std::copy(buf.begin(), buf.begin() + lengths::MIC, pdu + lenWithoutMic); } /** @@ -86,10 +91,11 @@ void Aes::appendMic0(uint8_t *const pdu, const uint8_t len) const { * len : total length (MIC included) */ bool Aes::verifyMic0(const uint8_t *const pdu, const uint8_t len) const { - uint8_t buf[AES_BLCK_SIZE] = {0}; + AesBlock buf = {0}; const uint8_t lenWithoutMic = len - lengths::MIC; - aes_cmac(pdu, lenWithoutMic, 0, AESDevKey, buf); - return std::equal(buf, buf + lengths::MIC, pdu + lenWithoutMic); + aes_cmac(pdu, lenWithoutMic, false, AESDevKey, buf); + return std::equal(buf.begin(), buf.begin() + lengths::MIC, + pdu + lenWithoutMic); } void Aes::encrypt(uint8_t *const pdu, const uint8_t len) const { @@ -106,26 +112,26 @@ void Aes::framePayloadEncryption(const uint8_t port, const uint32_t devaddr, uint8_t *payload, uint8_t len) const { const auto &key = port == 0 ? nwkSKey : appSKey; // Generate - uint8_t blockAi[AES_BLCK_SIZE]; + AesBlock blockAi; blockAi[0] = 1; // mode=cipher blockAi[1] = 0; blockAi[2] = 0; blockAi[3] = 0; blockAi[4] = 0; blockAi[5] = static_cast(dndir); // direction (0=up 1=down) - wlsbf4(blockAi + 6, devaddr); - wlsbf4(blockAi + 10, seqno); + wlsbf4(blockAi.begin() + 6, devaddr); + wlsbf4(blockAi.begin() + 10, seqno); blockAi[14] = 0; blockAi[15] = 0; // block counter while (len) { - uint8_t blockSi[AES_BLCK_SIZE]; - // Increment the block index byte blockAi[15]++; + // Encrypt the counter block with the selected key - std::copy(blockAi, blockAi + AES_BLCK_SIZE, blockSi); - aes_128_encrypt(blockSi, key); + AesBlock blockSi = blockAi; + + block_encrypt(blockSi, key); // Xor the payload with the resulting ciphertext for (uint8_t i = 0; i < AES_BLCK_SIZE && len > 0; i++, len--, payload++) @@ -135,28 +141,30 @@ void Aes::framePayloadEncryption(const uint8_t port, const uint32_t devaddr, // Extract session keys void Aes::sessKeys(const uint16_t devnonce, const uint8_t *const artnonce) { - nwkSKey.data[0] = 0x01; + nwkSKey[0] = 0x01; std::copy(artnonce, artnonce + join_accept::lengths::appNonce + join_accept::lengths::netId, - nwkSKey.data + 1); - wlsbf2(nwkSKey.data + 1 + join_accept::lengths::appNonce + + nwkSKey.begin() + 1); + wlsbf2(nwkSKey.begin() + 1 + join_accept::lengths::appNonce + join_accept::lengths::netId, devnonce); // add pading - std::fill(nwkSKey.data + 1 + join_accept::lengths::appNonce + + std::fill(nwkSKey.begin() + 1 + join_accept::lengths::appNonce + join_accept::lengths::netId + join_request::lengths::devNonce, - nwkSKey.data + AES_BLCK_SIZE, 0); + nwkSKey.end(), 0); appSKey = nwkSKey; - appSKey.data[0] = 0x02; + appSKey[0] = 0x02; - aes_128_encrypt(nwkSKey.data, AESDevKey); - aes_128_encrypt(appSKey.data, AESDevKey); + block_encrypt(nwkSKey, AESDevKey); + block_encrypt(appSKey, AESDevKey); } // Shift the given buffer left one bit -static void shift_left(uint8_t *buf, uint8_t len) { +static void shift_left(AesBlock &block) { + auto buf = block.begin(); + auto len = block.max_size(); while (len--) { uint8_t next = len ? buf[1] : 0; @@ -172,19 +180,19 @@ static void shift_left(uint8_t *buf, uint8_t len) { // it can be set to "B0" for MIC. The CMAC result is returned in result // as well. void Aes::aes_cmac(const uint8_t *buf, uint8_t len, const bool prepend_aux, - AesKey const &key, uint8_t result[AES_BLCK_SIZE]) { + AesKey const &key, AesBlock &result) { if (prepend_aux) - aes_128_encrypt(result, key); + block_encrypt(result, key); while (len > 0) { - uint8_t need_padding = 0; + bool need_padding = false; for (uint8_t i = 0; i < AES_BLCK_SIZE; ++i, ++buf, --len) { if (len == 0) { // The message is padded with 0x80 and then zeroes. // Since zeroes are no-op for xor, we can just skip them // and leave AESAUX unchanged for them. result[i] ^= 0x80; - need_padding = 1; + need_padding = true; break; } result[i] ^= *buf; @@ -194,30 +202,30 @@ void Aes::aes_cmac(const uint8_t *buf, uint8_t len, const bool prepend_aux, // Final block, xor with K1 or K2. K1 and K2 are calculated // by encrypting the all-zeroes block and then applying some // shifts and xor on that. - uint8_t final_key[AES_BLCK_SIZE]; - std::fill(final_key, final_key + AES_BLCK_SIZE, 0); - aes_128_encrypt(final_key, key); + AesBlock final_key = {0}; + // std::fill(final_key, final_key + AES_BLCK_SIZE, 0); + block_encrypt(final_key, key); // Calculate K1 uint8_t msb = final_key[0] & 0x80; - shift_left(final_key, sizeof(final_key)); + shift_left(final_key); if (msb) - final_key[sizeof(final_key) - 1] ^= 0x87; + final_key.back() ^= 0x87; // If the final block was not complete, calculate K2 from K1 if (need_padding) { msb = final_key[0] & 0x80; - shift_left(final_key, sizeof(final_key)); + shift_left(final_key); if (msb) - final_key[sizeof(final_key) - 1] ^= 0x87; + final_key.back() ^= 0x87; } // Xor with K1 or K2 - for (uint8_t i = 0; i < sizeof(final_key); ++i) + for (uint8_t i = 0; i < final_key.max_size(); ++i) result[i] ^= final_key[i]; } - aes_128_encrypt(result, key); + block_encrypt(result, key); } } @@ -228,7 +236,7 @@ void Aes::saveState(StoringAbtract &store) const { store.write(appSKey); } -void Aes::loadState(RetrieveAbtract& store) { +void Aes::loadState(RetrieveAbtract &store) { // Do not load devkey (should be fix valuse) // save 2 keys store.read(nwkSKey); diff --git a/src/aes/lmic_aes.h b/src/aes/lmic_aes.h index a48a309..98a16ed 100644 --- a/src/aes/lmic_aes.h +++ b/src/aes/lmic_aes.h @@ -1,19 +1,18 @@ #ifndef __aes_h__ #define __aes_h__ +#include "../lmic/bufferpack.h" #include "../lmic/config.h" #include "../lmic/lorabase.h" -#include"aes_encrypt.h" -#include "../lmic/bufferpack.h" +#include "aes_encrypt.h" #include // ====================================================================== // AES support -// void lmic_aes_encrypt(uint8_t *data, const uint8_t *key); - constexpr uint8_t AES_BLCK_SIZE = 16; +using AesBlock = std::array; class Aes { private: @@ -23,10 +22,9 @@ class Aes { // application session key AesKey appSKey; - static void micB0(uint32_t devaddr, uint32_t seqno, PktDir dndir, uint8_t len, - uint8_t buf[AES_BLCK_SIZE]); + static AesBlock micB0(uint32_t devaddr, uint32_t seqno, PktDir dndir, uint8_t len); static void aes_cmac(const uint8_t *buf, uint8_t len, bool prepend_aux, - AesKey const &key, uint8_t result[AES_BLCK_SIZE]); + AesKey const &key, AesBlock &result); public: /* Set device key @@ -46,9 +44,8 @@ class Aes { void appendMic(uint32_t devaddr, uint32_t seqno, PktDir dndir, uint8_t *pdu, uint8_t len) const; void appendMic0(uint8_t *pdu, uint8_t len) const; - void saveState(StoringAbtract& buffer) const; - void loadState(RetrieveAbtract& store); - + void saveState(StoringAbtract &buffer) const; + void loadState(RetrieveAbtract &store); }; #endif // __aes_h__ \ No newline at end of file diff --git a/src/boardconfig.h b/src/boardconfig.h new file mode 100644 index 0000000..b477a68 --- /dev/null +++ b/src/boardconfig.h @@ -0,0 +1,44 @@ +#ifndef boardconfig_h +#define boardconfig_h + +#define LMIC_GENERIC 1 +#define LMIC_ARDUINO 2 +#define LMIC_ESP32 3 + +#ifdef ARDUINO + + +#ifndef LMIC_HAL_IO +#define LMIC_HAL_IO LMIC_ARDUINO +#endif + + +// ESP32 using Arduino +#ifdef ARDUINO_ARCH_ESP32 +#ifndef LMIC_HAL +#define LMIC_HAL LMIC_ESP32 +#endif + +// Other arduino +#else +#ifndef LMIC_HAL +#define LMIC_HAL LMIC_ARDUINO +#endif + +#endif + +// not arduino +#else +#ifndef LMIC_HAL +#define LMIC_HAL LMIC_GENERIC +#endif + + +#ifndef LMIC_HAL_IO +#define LMIC_HAL_IO LMIC_GENERIC +#endif + + +#endif + +#endif \ No newline at end of file diff --git a/src/hal/hal.cpp b/src/hal/hal.cpp index 677c385..0342d9c 100644 --- a/src/hal/hal.cpp +++ b/src/hal/hal.cpp @@ -8,7 +8,8 @@ * This the HAL to run LMIC on top of the Arduino environment. *******************************************************************************/ -#ifndef ARDUINO_ARCH_ESP32 +#include "../boardconfig.h" +#if LMIC_HAL == LMIC_ARDUINO #include "hal.h" #include "print_debug.h" #include @@ -131,4 +132,4 @@ void hal_failed(const char *file, uint16_t line) { while (1) ; } -#endif \ No newline at end of file +#endif diff --git a/src/hal/hal_esp.cpp b/src/hal/hal_esp.cpp index 3f4d6f2..5f2bc6b 100644 --- a/src/hal/hal_esp.cpp +++ b/src/hal/hal_esp.cpp @@ -7,10 +7,10 @@ * * This the HAL to run LMIC on top of the Arduino environment. *******************************************************************************/ +#include "../boardconfig.h" +#if LMIC_HAL == LMIC_ESP32 -#ifdef ARDUINO_ARCH_ESP32 #include "hal.h" -#include #include #include "print_debug.h" #include diff --git a/src/hal/hal_generic.cpp b/src/hal/hal_generic.cpp new file mode 100644 index 0000000..ab370d7 --- /dev/null +++ b/src/hal/hal_generic.cpp @@ -0,0 +1,59 @@ +/******************************************************************************* + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on other environment than arduino. + *******************************************************************************/ +#include "../boardconfig.h" +#if LMIC_HAL == LMIC_GENERIC + +#include "hal.h" +#include "print_debug.h" +#include +#include +#include + +// ----------------------------------------------------------------------------- +// TIME + +OsTime hal_ticks() { + timeval val; + gettimeofday(&val, nullptr); + return OsTime((val.tv_sec * OSTICKS_PER_SEC) + + (val.tv_usec >> US_PER_OSTICK_EXPONENT)); +} + +void hal_waitUntil(OsTime time) { + OsDeltaTime delta = time - hal_ticks(); + hal_wait(delta); +} + +void hal_wait(OsDeltaTime delta) { usleep(delta.to_us()); } + +DisableIRQsGard::DisableIRQsGard() {} +DisableIRQsGard::~DisableIRQsGard() {} + +void hal_init() { + // nothing to do +} + +void hal_failed(const char *file, uint16_t line) { + (void)file; + (void)line; + + /* +#if defined(LMIC_FAILURE_TO) + LMIC_FAILURE_TO.println("FAILURE "); + LMIC_FAILURE_TO.print(file); + LMIC_FAILURE_TO.print(':'); + LMIC_FAILURE_TO.println(line); + LMIC_FAILURE_TO.flush(); +#endif +*/ + + while (1) + ; +} +#endif \ No newline at end of file diff --git a/src/hal/hal_io.cpp b/src/hal/hal_io.cpp index 05a7760..cf7af60 100644 --- a/src/hal/hal_io.cpp +++ b/src/hal/hal_io.cpp @@ -1,3 +1,6 @@ +#include "../boardconfig.h" +#if LMIC_HAL_IO == LMIC_ARDUINO + #include "hal_io.h" #include "../lmic/lmic.h" #include "hal.h" @@ -10,6 +13,10 @@ static const SPISettings settings(10000000, MSBFIRST, SPI_MODE0); HalIo::HalIo(lmic_pinmap const &pins) : lmic_pins(pins) {} +void HalIo::yield() const { + ::yield(); +} + void HalIo::write_reg(uint8_t const addr, uint8_t const data) const { beginspi(); spi(addr | 0x80); @@ -122,3 +129,5 @@ void HalIo::init() const { // configure radio SPI } + +#endif \ No newline at end of file diff --git a/src/hal/hal_io.h b/src/hal/hal_io.h index 0075795..9685c26 100644 --- a/src/hal/hal_io.h +++ b/src/hal/hal_io.h @@ -28,6 +28,8 @@ class HalIo final { void write_buffer(uint8_t addr, uint8_t const *buf, uint8_t len) const; void read_buffer(uint8_t addr, uint8_t *buf, uint8_t len) const; + void yield() const; + /** * drive radio NSS pin for start transfer. */ diff --git a/src/hal/hal_io_empty.cpp b/src/hal/hal_io_empty.cpp new file mode 100644 index 0000000..0ab7b2d --- /dev/null +++ b/src/hal/hal_io_empty.cpp @@ -0,0 +1,135 @@ +#include "../boardconfig.h" +#if LMIC_HAL_IO == LMIC_GENERIC + +#include "hal_io.h" +#include "../lmic/lmic.h" +#include "hal.h" +// #include +// #include +#include +#include + + +HalIo::HalIo(lmic_pinmap const &pins) : lmic_pins(pins) {} + +void HalIo::yield() const { + +} + +void HalIo::write_reg(uint8_t const addr, uint8_t const data) const { + beginspi(); + spi(addr | 0x80); + spi(data); + endspi(); +} + +uint8_t HalIo::read_reg(uint8_t const addr) const { + beginspi(); + spi(addr & 0x7F); + uint8_t const val = spi(0x00); + endspi(); + return val; +} + +void HalIo::write_buffer(uint8_t const addr, uint8_t const *const buf, + uint8_t const len) const { + beginspi(); + spi(addr | 0x80); + for (uint8_t i = 0; i < len; i++) { + spi(buf[i]); + } + endspi(); +} + +void HalIo::read_buffer(uint8_t const addr, uint8_t *const buf, + uint8_t const len) const { + beginspi(); + spi(addr & 0x7F); + std::generate_n(buf, len, [this]() { return spi(0x00); }); + endspi(); +} + +void HalIo::beginspi() const { + // SPI.beginTransaction(settings); + // digitalWrite(lmic_pins.nss, 0); +} + +void HalIo::endspi() const { + // digitalWrite(lmic_pins.nss, 1); + // SPI.endTransaction(); +} + +// perform SPI transaction with radio +uint8_t HalIo::spi(uint8_t const out) const { + // uint8_t res = SPI.transfer(out); + /* + Serial.print(">"); + Serial.print(out, HEX); + Serial.print("<"); + Serial.println(res, HEX); + */ + // return res; + return 0; +} + +void HalIo::pin_switch_antenna_tx(bool isTx) const { + // val == 1 => tx 1 + if (lmic_pins.prepare_antenna_tx) + lmic_pins.prepare_antenna_tx(isTx); +} + +// set radio RST pin to given value (or keep floating!) +void HalIo::pin_rst(uint8_t val) const { + if (lmic_pins.rst == LMIC_UNUSED_PIN) + return; + + if (val == 0 || val == 1) { // drive pin + // pinMode(lmic_pins.rst, OUTPUT); + // digitalWrite(lmic_pins.rst, val); + } else { // keep pin floating + // pinMode(lmic_pins.rst, INPUT); + } +} + +bool HalIo::io_check() const { + for (uint8_t i = 0; i < NUM_DIO; ++i) { + // uint8_t newVal = digitalRead(lmic_pins.dio[i]); + // PRINT_DEBUG(2, F("Check DIO%d Value=%d"), i, newVal); + //if (newVal) { + return true; + //} + } + return false; +} + +bool HalIo::io_check0() const { + // return digitalRead(lmic_pins.dio[0]) ? true : false; + return true; +} + +bool HalIo::io_check1() const { + // return digitalRead(lmic_pins.dio[1]) ? true : false; + return true; +} + +void HalIo::init() const { + // NSS, DIO0 , DIO1 are required for LoRa + ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN); + + PRINT_DEBUG(2, F("NSS:%d, RST:%d, DIO0:%d, DIO1:%d"), lmic_pins.nss, + lmic_pins.rst, lmic_pins.dio[0], lmic_pins.dio[1]); + + // pinMode(lmic_pins.nss, OUTPUT); + + // if (lmic_pins.rst != LMIC_UNUSED_PIN) + // pinMode(lmic_pins.rst, OUTPUT); + + // pinMode(lmic_pins.dio[0], INPUT); + // pinMode(lmic_pins.dio[1], INPUT); + + // configure radio SPI +} + +#endif \ No newline at end of file diff --git a/src/hal/print_debug.cpp b/src/hal/print_debug.cpp index 595418f..505c1c6 100644 --- a/src/hal/print_debug.cpp +++ b/src/hal/print_debug.cpp @@ -2,16 +2,19 @@ #include "print_debug.h" -#include #include -#if defined(LMIC_PRINTF_TO) +#ifdef ARDUINO_ARCH_AVR +#include + + static int uart_putchar(char c, FILE *) { LMIC_PRINTF_TO.write(c); return 0; } void hal_printf_init() { + // create a FILE structure to reference our UART output function static FILE uartout = {}; @@ -22,7 +25,7 @@ void hal_printf_init() { stdout = &uartout; } #else -void hal_printf_init() { - +void hal_printf_init() { + // no init for other than AVR } #endif // defined(LMIC_PRINTF_TO) diff --git a/src/hal/print_debug.h b/src/hal/print_debug.h index 456bbbc..933c74e 100644 --- a/src/hal/print_debug.h +++ b/src/hal/print_debug.h @@ -1,25 +1,61 @@ #ifndef _print_debug_h_ #define _print_debug_ -#include "WString.h" #include "hal.h" #include "stdio.h" +#include + +#ifdef ARDUINO + +#include "WString.h" +#include +#ifdef ARDUINO_ARCH_AVR template void PRINT_DEBUG(int X, const __FlashStringHelper *str, T const... div) { if (debugLevel >= X) { -#ifdef ARDUINO_ARCH_ESP32 - // on ESP the MACRO PRIu32 cause a problem with syntax below - printf("%u ", hal_ticks().tick()); -#else printf_P(PSTR("%" PRIu32 " "), hal_ticks().tick()); -#endif PGM_P p = reinterpret_cast(str); printf_P(p, div...); // printf_P(PSTR("\n")); printf("\n"); } } +#else + +namespace { +template +void serialPrintf(const __FlashStringHelper *ifsh, T const... div) { + // Cast the special class use by AVR to standart char * + LMIC_PRINTF_TO.printf(reinterpret_cast(ifsh), div...); +} + +} // namespace + +template +void PRINT_DEBUG(int X, const __FlashStringHelper *str, T const... div) { + if (debugLevel >= X) { + serialPrintf(F("%" PRIu32 " "), hal_ticks().tick()); + serialPrintf(str, div...); + serialPrintf(F("\n")); + } +} + +#endif + +#else +#define F(string_literal) string_literal + +template +void PRINT_DEBUG(int X, const char *str, T const... div) { + if (debugLevel >= X) { + printf("%" PRIu32 " ", hal_ticks().tick()); + printf(str, div...); + printf("\n"); + } +} + +#endif constexpr bool IS_DEBUG_ENABLE(int x) { return debugLevel >= x; } diff --git a/src/keyhandler.h b/src/keyhandler.h index a6ab677..12f21c8 100644 --- a/src/keyhandler.h +++ b/src/keyhandler.h @@ -1,16 +1,22 @@ #ifndef _lorakeyhandler_h_ #define _lorakeyhandler_h_ +#ifdef ARDUINO #include +#else +#define PROGMEM +#define memcpy_P memcpy +#endif + + #include constexpr uint8_t HexCharToInt(char const char1) { - return (char1 >= '0' && char1 <= '9') - ? char1 - '0' - : (char1 >= 'A' && char1 <= 'F') - ? char1 - 'A' + 0x0A - : (char1 >= 'a' && char1 <= 'f') ? char1 - 'a' + 0x0A : 0; + return (char1 >= '0' && char1 <= '9') ? char1 - '0' + : (char1 >= 'A' && char1 <= 'F') ? char1 - 'A' + 0x0A + : (char1 >= 'a' && char1 <= 'f') ? char1 - 'a' + 0x0A + : 0; } constexpr uint8_t HexCharToInt(char const char1, char const char2) { @@ -43,7 +49,7 @@ template class KeyGetter { public: static AesKey getKey() { AesKey lmicKey; - memcpy_P(lmicKey.data, key, SIZE); + memcpy_P(lmicKey.data(), key, lmicKey.size()); return lmicKey; } }; diff --git a/src/lmic/band.eu868.cpp b/src/lmic/band.eu868.cpp index d1b9dc5..5b5c6bc 100644 --- a/src/lmic/band.eu868.cpp +++ b/src/lmic/band.eu868.cpp @@ -2,6 +2,7 @@ #include "../hal/print_debug.h" #include "bufferpack.h" #include "oslmic.h" +#include namespace { constexpr uint32_t MIN_BAND1_CENTI = 868000000; @@ -17,9 +18,7 @@ enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2 }; void BandsEu868::init() { auto now = os_getTime(); - for (uint8_t i = 0; i < MAX_BAND; i++) { - avail[i] = now; - } + avail.fill(now); } void BandsEu868::updateBandAvailability(uint8_t const band, @@ -62,15 +61,14 @@ uint8_t BandsEu868::getBandForFrequency(uint32_t const frequency) const { #if defined(ENABLE_SAVE_RESTORE) void BandsEu868::saveState(StoringAbtract &store) const { - for (int i = 0; i < MAX_BAND; i++) { - store.write(avail[i]); - } + + std::for_each(begin(avail), end(avail), + [&store](OsTime const date) { store.write(date); }); } void BandsEu868::loadState(RetrieveAbtract &store) { - for (int i = 0; i < MAX_BAND; i++) { - store.read(avail[i]); - } + std::for_each(begin(avail), end(avail), + [&store](OsTime &date) { store.read(date); }); } #endif diff --git a/src/lmic/band.eu868.h b/src/lmic/band.eu868.h index cca0416..82c4211 100644 --- a/src/lmic/band.eu868.h +++ b/src/lmic/band.eu868.h @@ -6,6 +6,7 @@ #include "bands.h" #include "bufferpack.h" #include "osticks.h" +#include class BandsEu868 : public Bands { public: @@ -25,7 +26,7 @@ class BandsEu868 : public Bands { #endif private: - OsTime avail[MAX_BAND]; + std::array avail; }; #endif \ No newline at end of file diff --git a/src/lmic/bands.h b/src/lmic/bands.h index 954f610..132e733 100644 --- a/src/lmic/bands.h +++ b/src/lmic/bands.h @@ -8,24 +8,24 @@ class Bands { public: - virtual void init(); + virtual void init() = 0; virtual void updateBandAvailability(uint8_t band, OsTime lastusage, - OsDeltaTime duration); - virtual void print_state() const; - virtual OsTime getAvailability(uint8_t band) const; + OsDeltaTime duration) = 0; + virtual void print_state() const = 0; + virtual OsTime getAvailability(uint8_t band) const = 0; - virtual uint8_t getBandForFrequency(uint32_t frequency) const; + virtual uint8_t getBandForFrequency(uint32_t frequency) const = 0; #if defined(ENABLE_SAVE_RESTORE) - virtual void saveState(StoringAbtract &store) const; - virtual void loadState(RetrieveAbtract &store); + virtual void saveState(StoringAbtract &store) const = 0; + virtual void loadState(RetrieveAbtract &store) = 0; #endif }; class BandSingle : public Bands { public: - BandSingle(uint16_t duty); + explicit BandSingle(uint16_t duty); void init() final; void updateBandAvailability(uint8_t band, OsTime lastusage, OsDeltaTime duration) final; @@ -42,7 +42,7 @@ class BandSingle : public Bands { #endif private: - const uint16_t dutyCycle; + const uint16_t dutyCycle; OsTime avail; }; diff --git a/src/lmic/bufferpack.h b/src/lmic/bufferpack.h index e1d32e6..0e73129 100644 --- a/src/lmic/bufferpack.h +++ b/src/lmic/bufferpack.h @@ -42,7 +42,7 @@ class StoringAbtract { template void write(T const val) { store(&val, sizeof(T)); } protected: - virtual void store(void const *val, size_t size); + virtual void store(void const *val, size_t size) = 0; }; class RetrieveAbtract { @@ -50,12 +50,13 @@ class RetrieveAbtract { template void read(T &val) { retrieve(&val, sizeof(T)); } protected: - virtual void retrieve(void *val, size_t size); + virtual void retrieve(void *val, size_t size) = 0; }; class StoringBuffer final : public StoringAbtract { public: - explicit StoringBuffer(uint8_t *const buffer) : original(buffer), current(buffer){}; + explicit StoringBuffer(uint8_t *const buffer) + : original(buffer), current(buffer){}; size_t length() const; protected: diff --git a/src/lmic/channelList.h b/src/lmic/channelList.h index 74e5781..cf3ec06 100644 --- a/src/lmic/channelList.h +++ b/src/lmic/channelList.h @@ -1,9 +1,10 @@ #ifndef channel_list_h #define channel_list_h +#include "bands.h" #include "bufferpack.h" #include "lorabase.h" -#include "bands.h" +#include #include struct ChannelDetail { @@ -36,18 +37,16 @@ struct ChannelDetail { #endif }; - - class ChannelList { public: // Channel map store a maximum of 16 channel // (in rp_2-1.0.1 all dynamic channel region have a minumum of 16 ) - constexpr static const uint8_t LIMIT_CHANNELS = 16 ; + constexpr static const uint8_t LIMIT_CHANNELS = 16; private: - ChannelDetail channels[LIMIT_CHANNELS] = {}; + std::array channels = {}; uint16_t channelMap = 0; - Bands & bands; + Bands &bands; uint8_t getBand(uint8_t const channel) const { return bands.getBandForFrequency(getFrequency(channel)); @@ -65,7 +64,7 @@ class ChannelList { } } void enableAll() { - for (uint8_t channel = 0; channel < LIMIT_CHANNELS; channel++) { + for (uint8_t channel = 0; channel < channels.max_size(); channel++) { enable(channel); } } @@ -82,7 +81,6 @@ class ChannelList { channelMap |= 1 << channel; } - void updateAvailabitility(uint8_t const channel, OsTime const txbeg, OsDeltaTime const airtime) { // Update band specific duty cycle stats @@ -94,7 +92,7 @@ class ChannelList { return bands.getAvailability(band); }; - constexpr uint32_t getFrequency(uint8_t const channel) const { + uint32_t getFrequency(uint8_t const channel) const { return channels[channel].getFrequency(); }; @@ -105,9 +103,10 @@ class ChannelList { }; void saveStateWithoutTimeData(StoringAbtract &store) const { - for (uint8_t channel = 0; channel < LIMIT_CHANNELS; channel++) { - channels[channel].saveState(store); + for (auto &&channel : channels) { + channel.saveState(store); } + store.write(channelMap); }; @@ -117,8 +116,8 @@ class ChannelList { }; void loadStateWithoutTimeData(RetrieveAbtract &store) { - for (uint8_t channel = 0; channel < LIMIT_CHANNELS; channel++) { - channels[channel].loadState(store); + for (auto &&channel : channels) { + channel.loadState(store); } store.read(channelMap); }; diff --git a/src/lmic/config.h b/src/lmic/config.h index 9b7f640..7209c42 100644 --- a/src/lmic/config.h +++ b/src/lmic/config.h @@ -24,9 +24,8 @@ constexpr int debugLevel = 1; #endif // Enable this to allow using printf() to print to the given serial port -// (or any other Print object). This can be easy for debugging. The -// current implementation only works on AVR, though. -#ifdef __AVR__ +// (or any other Print object). +#ifndef LMIC_PRINTF_TO #define LMIC_PRINTF_TO Serial #endif diff --git a/src/lmic/lmic.cpp b/src/lmic/lmic.cpp index 6123d00..b467b5b 100644 --- a/src/lmic/lmic.cpp +++ b/src/lmic/lmic.cpp @@ -109,7 +109,7 @@ void Lmic::txDelay(OsTime reftime, uint8_t secSpan) { } /** - * Set the battery level from : + * Set the battery level from : * 0 : external power source * 1-254 : battery level * 255 : no info @@ -268,6 +268,7 @@ void Lmic::parse_rx_timing_setup(const uint8_t *const opts) { void Lmic::parseMacCommands(const uint8_t *const opts, uint8_t const olen) { uint8_t oidx = 0; while (oidx < olen) { + PRINT_DEBUG(1, F("Parse Mac command %d"), opts[oidx]); switch (opts[oidx]) { // LinkCheckReq LoRaWAN™ Specification §5.1 case MCMD_LCHK_ANS: { @@ -375,8 +376,7 @@ bool Lmic::decodeFrame() { return false; } - uint8_t *const d = frame; - const uint8_t hdr = d[0]; + const uint8_t hdr = frame[0]; const uint8_t ftype = hdr & mhdr::ftype_mask; const uint8_t dlen = dataLen; @@ -388,13 +388,13 @@ bool Lmic::decodeFrame() { return false; } - const uint32_t addr = rlsbf4(&d[mac_payload::offsets::devAddr]); + const uint32_t addr = rlsbf4(frame.cbegin() + mac_payload::offsets::devAddr); if (addr != devaddr) { PRINT_DEBUG(1, F("Invalid address")); return false; } - const uint8_t fct = d[mac_payload::offsets::fctrl]; + const uint8_t fct = frame[mac_payload::offsets::fctrl]; const uint8_t olen = fct & FCT_OPTLEN; const bool ackup = (fct & FCT_ACK) != 0 ? true : false; // ACK last up frame const uint8_t poff = mac_payload::offsets::fopts + olen; @@ -405,9 +405,10 @@ bool Lmic::decodeFrame() { return false; } - const uint32_t seqno = read_seqno(&d[mac_payload::offsets::fcnt]); + const uint32_t seqno = + read_seqno(frame.cbegin() + mac_payload::offsets::fcnt); - if (!aes.verifyMic(devaddr, seqno, PktDir::DOWN, d, dlen)) { + if (!aes.verifyMic(devaddr, seqno, PktDir::DOWN, frame.cbegin(), dlen)) { PRINT_DEBUG(1, F("Fail to verify aes mic")); return false; } @@ -425,21 +426,21 @@ bool Lmic::decodeFrame() { if (dnConf || (fct & FCT_MORE)) opmode.set(OpState::POLL); - parseMacCommands(d + mac_payload::offsets::fopts, olen); + parseMacCommands(frame.cbegin() + mac_payload::offsets::fopts, olen); if (!replayConf) { // Handle payload only if not a replay if (pend > poff) { - const auto port = d[poff]; + const auto port = frame[poff]; dataBeg = poff + 1; dataLen = pend - dataBeg; // Decrypt payload - if any aes.framePayloadEncryption(port, devaddr, seqno, PktDir::DOWN, - d + dataBeg, dataLen); + frame.begin() + dataBeg, dataLen); txrxFlags.set(TxRxStatus::PORT); if (port == 0) { - parseMacCommands(d + dataBeg, dataLen); + parseMacCommands(frame.cbegin() + dataBeg, dataLen); } } else { txrxFlags.set(TxRxStatus::NOPORT); @@ -528,10 +529,10 @@ OsTime Lmic::schedRx12(OsDeltaTime delay, dr_t dr) { } // Called by HAL once TX complete and delivers exact end of TX time stamp in -// rxtime -void Lmic::txDone(OsDeltaTime delay) { - auto waitime = schedRx12(delay, getRx1Parameter().datarate); - osjob.setTimedCallback(waitime, &Lmic::setupRx1); +// rxtime. Schedule first receive. +void Lmic::txDone() { + auto waitime = schedRx12(rxDelay, getRx1Parameter().datarate); + next_job = Job(&Lmic::setupRx1, waitime); } // ======================================== Join frames @@ -572,9 +573,14 @@ void Lmic::processJoinAcceptNoJoinFrame() { txend += OsDeltaTime::rnd_delay(rand, 255 >> datarate); PRINT_DEBUG(1, F("Next Join delay : %i s"), (txend - os_getTime()).to_s()); - osjob.setCallbackRunnable( - succes ? &Lmic::runEngineUpdate // next step to be delayed - : &Lmic::onJoinFailed); // one JOIN iteration done and failed + + if (succes) { + // next step to be delayed + next_job = Job(&Lmic::runEngineUpdate); + } else { + // one JOIN iteration done and failed + next_job = Job(&Lmic::onJoinFailed); + } } bool Lmic::processJoinAccept() { @@ -597,24 +603,25 @@ bool Lmic::processJoinAccept() { // unexpected frame return false; } - aes.encrypt(frame + 1, dlen - 1); - if (!aes.verifyMic0(frame, dlen)) { + + aes.encrypt(frame.begin() + 1, dlen - 1); + if (!aes.verifyMic0(frame.cbegin(), dlen)) { PRINT_DEBUG(1, F("Join Accept BAD MIC")); // bad mic return false; } - devaddr = rlsbf4(frame + join_accept::offset::devAddr); - netid = rlsbf4(frame + join_accept::offset::netId) & 0xFFFFFF; + devaddr = rlsbf4(frame.cbegin() + join_accept::offset::devAddr); + netid = rlsbf4(frame.cbegin() + join_accept::offset::netId) & 0xFFFFFF; if (dlen > join_accept::lengths::total) { // some region just ignore cflist. - handleCFList(frame + join_accept::offset::cfList); + handleCFList(frame.cbegin() + join_accept::offset::cfList); } // already incremented when JOIN REQ got sent off - aes.sessKeys(devNonce - 1, frame + join_accept::offset::appNonce); + aes.sessKeys(devNonce - 1, frame.cbegin() + join_accept::offset::appNonce); ASSERT(opmode.test(OpState::JOINING)); @@ -645,7 +652,7 @@ void Lmic::processRxJacc() { // wait for RX2 auto waitime = schedRx12(OsDeltaTime::from_sec(DELAY_JACC2), rx2Parameter.datarate); - osjob.setTimedCallback(waitime, &Lmic::setupRx2); + next_job = Job(&Lmic::setupRx2, waitime); } else { // nothing in 1st/2nd DN slot txrxFlags.reset(); @@ -654,8 +661,6 @@ void Lmic::processRxJacc() { } } -void Lmic::jreqDone() { txDone(OsDeltaTime::from_sec(DELAY_JACC1)); } - // ======================================== Data frames void Lmic::processRxDnData() { @@ -711,7 +716,7 @@ void Lmic::processRx1DnData() { // if nothing receive, wait for RX2 before take actions auto waitime = schedRx12(rxDelay + OsDeltaTime::from_sec(DELAY_EXTDNW2), rx2Parameter.datarate); - osjob.setTimedCallback(waitime, &Lmic::setupRx2); + next_job = Job(&Lmic::setupRx2, waitime); } else { resetAdrCount(); @@ -750,7 +755,6 @@ void Lmic::resetAdrCount() { } } -void Lmic::updataDone() { txDone(rxDelay); } uint8_t *Lmic::add_opt_dcap(uint8_t *pos) { #if !defined(DISABLE_MCMD_DCAP_REQ) @@ -782,8 +786,9 @@ uint8_t *Lmic::add_opt_devs(uint8_t *pos) { // lorawan 1.0.2 §5.5. the margin is the SNR. // Convert to real SNR; rounding towards zero. const int8_t snr = (radio.get_last_packet_snr_x4() + 2) / 4; - *(pos++) = static_cast( - (0x3F & (snr <= -32 ? -32 : snr >= 31 ? 31 : snr))); + *(pos++) = static_cast((0x3F & (snr <= -32 ? -32 + : snr >= 31 ? 31 + : snr))); devsAns = false; } return pos; @@ -821,7 +826,7 @@ void Lmic::buildDataFrame() { // Piggyback MAC options // Prioritize by importance - uint8_t *pos = frame + mac_payload::offsets::fopts; + uint8_t *pos = frame.begin() + mac_payload::offsets::fopts; pos = add_opt_dcap(pos); pos = add_opt_dn2p(pos); pos = add_opt_devs(pos); @@ -829,13 +834,13 @@ void Lmic::buildDataFrame() { pos = add_opt_rxtiming(pos); pos = add_opt_snch(pos); - const uint8_t end = pos - frame; + const uint8_t end = pos - frame.cbegin(); ASSERT(end <= mac_payload::offsets::fopts + 16); bool txdata = opmode.test(OpState::TXDATA); uint8_t flen = end + (txdata ? 1 + lengths::MIC + pendTxLen : lengths::MIC); - if (flen > MAX_LEN_FRAME) { + if (flen > frame.max_size()) { // Options and payload too big - delay payload txdata = false; flen = end + lengths::MIC; @@ -845,14 +850,14 @@ void Lmic::buildDataFrame() { frame[mac_payload::offsets::fctrl] = (dnConf | (adrAckReq != LINK_CHECK_OFF ? FCT_ADREN : 0) | (adrAckReq >= 0 ? FCT_ADRARQ : 0) | (end - mac_payload::offsets::fopts)); - wlsbf4(frame + mac_payload::offsets::devAddr, devaddr); + wlsbf4(frame.begin() + mac_payload::offsets::devAddr, devaddr); // if not a resend if (txCnt == 0) { seqnoUp++; } const uint32_t current_seq_no = seqnoUp - 1; - wlsbf2(frame + mac_payload::offsets::fcnt, current_seq_no); + wlsbf2(frame.begin() + mac_payload::offsets::fcnt, current_seq_no); // Clear pending DN confirmation dnConf = 0; @@ -864,14 +869,14 @@ void Lmic::buildDataFrame() { if (txCnt == 0) txCnt = 1; } - uint8_t *buffer_pos = frame + end; + uint8_t *buffer_pos = frame.begin() + end; *(buffer_pos++) = pendTxPort; - std::copy(pendTxData, pendTxData + pendTxLen, buffer_pos); + std::copy(begin(pendTxData), begin(pendTxData) + pendTxLen, buffer_pos); aes.framePayloadEncryption(pendTxPort, devaddr, current_seq_no, PktDir::UP, buffer_pos, pendTxLen); } - aes.appendMic(devaddr, current_seq_no, PktDir::UP, frame, flen); + aes.appendMic(devaddr, current_seq_no, PktDir::UP, frame.begin(), flen); dataLen = flen; @@ -888,10 +893,10 @@ void Lmic::buildJoinRequest() { // Do not use pendTxData since we might have a pending // user level frame in there. Use RX holding area instead. frame[join_request::offset::MHDR] = mhdr::ftype_join_req | mhdr::major_v1; - artEuiCallBack(frame + join_request::offset::appEUI); - devEuiCallBack(frame + join_request::offset::devEUI); - wlsbf2(frame + join_request::offset::devNonce, devNonce); - aes.appendMic0(frame, join_request::lengths::totalWithMic); + artEuiCallBack(frame.begin() + join_request::offset::appEUI); + devEuiCallBack(frame.begin() + join_request::offset::devEUI); + wlsbf2(frame.begin() + join_request::offset::devNonce, devNonce); + aes.appendMic0(frame.begin(), join_request::lengths::totalWithMic); dataLen = join_request::lengths::totalWithMic; devNonce++; @@ -915,12 +920,13 @@ bool Lmic::startJoining() { .set(OpState::JOINING); // Setup state txCnt = 0; + rxDelay = OsDeltaTime::from_sec(DELAY_JACC1); initJoinLoop(); // reportEvent will call engineUpdate which then starts sending JOIN // REQUESTS - osjob.setCallbackRunnable(&Lmic::startJoiningCallBack); + next_job = Job(&Lmic::startJoiningCallBack); return true; } return false; // already joined @@ -991,7 +997,7 @@ void Lmic::engineUpdate() { PRINT_DEBUG(1, F("Uplink delayed until %" PRIu32), txbeg.tick()); // Cannot yet TX // wait for the time to TX - osjob.setTimedCallback(txbeg - TX_RAMPUP, &Lmic::runEngineUpdate); + next_job = Job(&Lmic::runEngineUpdate, txbeg - TX_RAMPUP); txend = txbeg; return; } @@ -1005,14 +1011,14 @@ void Lmic::engineUpdate() { // Imminent roll over - proactively reset MAC // Device has to react! NWK will not roll over and just stop sending. // Thus, we have N frames to detect a possible lock up. - osjob.setCallbackRunnable(&Lmic::runReset); + next_job = Job(&Lmic::runReset); return; } if ((txCnt == 0 && seqnoUp == 0xFFFFFFFF)) { // Roll over of up seq counter // Do not run RESET event callback from here! // App code might do some stuff after send unaware of RESET. - osjob.setCallbackRunnable(&Lmic::runReset); + next_job = Job(&Lmic::runReset); return; } buildDataFrame(); @@ -1031,8 +1037,8 @@ void Lmic::engineUpdate() { PRINT_DEBUG(2, F("Updating global duty avail to %" PRIu32 ""), globalDutyAvail.tick()); - radio.tx(getTxFrequency(), rps, getTxPower() + antennaPowerAdjustment, frame, - dataLen); + radio.tx(getTxFrequency(), rps, getTxPower() + antennaPowerAdjustment, + frame.cbegin(), dataLen); wait_end_tx(); } @@ -1041,14 +1047,14 @@ void Lmic::setAntennaPowerAdjustment(int8_t power) { } void Lmic::shutdown() { - osjob.clearCallback(); + next_job = {}; radio.rst(); opmode.set(OpState::SHUTDOWN); } void Lmic::reset() { radio.rst(); - osjob.clearCallback(); + next_job = {}; devaddr = 0; devNonce = rand.uint16(); opmode.reset(); @@ -1071,7 +1077,7 @@ void Lmic::clrTxData() { pendTxLen = 0; if (opmode.test(OpState::JOINING)) // do not interfere with JOINING return; - osjob.clearCallback(); + next_job = {}; radio.rst(); engineUpdate(); } @@ -1086,10 +1092,10 @@ void Lmic::setTxData() { // int8_t Lmic::setTxData2(uint8_t port, uint8_t *data, uint8_t dlen, bool confirmed) { - if (dlen > MAX_LEN_PAYLOAD) + if (dlen > pendTxData.max_size()) return -2; if (data) - std::copy(data, data + dlen, pendTxData); + std::copy(data, data + dlen, begin(pendTxData)); pendTxConf = confirmed; pendTxPort = port; pendTxLen = dlen; @@ -1211,7 +1217,7 @@ void Lmic::wait_end_rx() { } } else { // if radio has not finish come back later (loop). - osjob.setCallbackRunnable(&Lmic::wait_end_rx); + next_job = Job(&Lmic::wait_end_rx); } } @@ -1223,16 +1229,10 @@ void Lmic::wait_end_tx() { radio.handle_end_tx(); PRINT_DEBUG(1, F("End TX %" PRIu32 ""), txend.tick()); - - // if radio task ended, activate next job. - if (opmode.test(OpState::JOINING)) { - jreqDone(); - } else { - updataDone(); - } + txDone(); } else { // if radio has not finish come back later (loop). - osjob.setCallbackRunnable(&Lmic::wait_end_tx); + next_job = Job(&Lmic::wait_end_tx); } } @@ -1327,9 +1327,6 @@ void Lmic::loadStateWithoutTimeData(RetrieveAbtract &store) { #endif -OsDeltaTime Lmic::run() { - return osjob.run(*this); -} +OsDeltaTime Lmic::run() { return next_job.run(*this); } -Lmic::Lmic(Radio &aradio) - : radio(aradio), rand(aes) {} +Lmic::Lmic(Radio &aradio) : radio(aradio), rand(aes) {} diff --git a/src/lmic/lmic.eu433.cpp b/src/lmic/lmic.eu433.cpp index 6c1bfbd..e5b42d6 100644 --- a/src/lmic/lmic.eu433.cpp +++ b/src/lmic/lmic.eu433.cpp @@ -32,20 +32,13 @@ constexpr uint32_t EU433_FREQ_MAX = 434665000; constexpr uint32_t FREQ_DNW2 = EU433_R2; constexpr LmicEu433::Dr DR_DNW2 = LmicEu433::Dr::SF12; -constexpr uint8_t rps_DR0 = - rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR1 = - rps_t{SF11, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR2 = - rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR3 = - rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR4 = - rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR5 = - rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR6 = - rps_t{SF7, BandWidth::BW250, CodingRate::CR_4_5, false}.rawValue(); +constexpr uint8_t rps_DR0 = rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR1 = rps_t{SF11, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR2 = rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR3 = rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR4 = rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR5 = rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR6 = rps_t{SF7, BandWidth::BW250, CodingRate::CR_4_5}; CONST_TABLE(uint8_t, _DR2RPS_CRC) [] = {ILLEGAL_RPS, rps_DR0, rps_DR1, rps_DR2, rps_DR3, @@ -123,10 +116,9 @@ uint32_t LmicEu433::convFreq(const uint8_t *ptr) const { return newfreq; } - FrequencyAndRate LmicEu433::defaultRX2Parameter() const { return {FREQ_DNW2, static_cast(DR_DNW2)}; } LmicEu433::LmicEu433(Radio &aradio) - : LmicDynamicChannel(aradio, MaxEIRPValue, 5,0, bands) {} \ No newline at end of file + : LmicDynamicChannel(aradio, MaxEIRPValue, 5, 0, bands) {} \ No newline at end of file diff --git a/src/lmic/lmic.eu868.cpp b/src/lmic/lmic.eu868.cpp index 5ff3abf..c9a8e24 100644 --- a/src/lmic/lmic.eu868.cpp +++ b/src/lmic/lmic.eu868.cpp @@ -41,20 +41,13 @@ constexpr LmicEu868::Dr DR_DNW2 = LmicEu868::Dr::SF12; constexpr OsDeltaTime DNW2_SAFETY_ZONE = OsDeltaTime::from_ms(3000); -constexpr uint8_t rps_DR0 = - rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR1 = - rps_t{SF11, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR2 = - rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR3 = - rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR4 = - rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR5 = - rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR6 = - rps_t{SF7, BandWidth::BW250, CodingRate::CR_4_5, false}.rawValue(); +constexpr uint8_t rps_DR0 = rps_t{SF12, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR1 = rps_t{SF11, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR2 = rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR3 = rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR4 = rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR5 = rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR6 = rps_t{SF7, BandWidth::BW250, CodingRate::CR_4_5}; CONST_TABLE(uint8_t, _DR2RPS_CRC) [] = {ILLEGAL_RPS, rps_DR0, rps_DR1, rps_DR2, rps_DR3, @@ -82,7 +75,6 @@ CONST_TABLE(int32_t, DR2HSYM) OsDeltaTime::from_us_round(128 << 3).tick(), // DR_SF8 OsDeltaTime::from_us_round(128 << 2).tick(), // DR_SF7 OsDeltaTime::from_us_round(128 << 1).tick(), // DR_SF7B - OsDeltaTime::from_us_round(80).tick() // FSK -- not used (time for 1/2 byte) }; } // namespace @@ -131,10 +123,9 @@ uint32_t LmicEu868::convFreq(const uint8_t *ptr) const { return newfreq; } - FrequencyAndRate LmicEu868::defaultRX2Parameter() const { return {FREQ_DNW2, static_cast(DR_DNW2)}; } LmicEu868::LmicEu868(Radio &aradio) - : LmicDynamicChannel(aradio, MaxEIRPValue, 5,0, bandeu) {} \ No newline at end of file + : LmicDynamicChannel(aradio, MaxEIRPValue, 5, 0, bandeu) {} \ No newline at end of file diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 816376d..04864e3 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -19,10 +19,7 @@ #include "lorabase.h" #include "oslmic.h" #include "radio.h" - -// LMIC version -#define LMIC_VERSION_MAJOR 1 -#define LMIC_VERSION_MINOR 5 +#include //!< Transmit attempts for confirmed frames const uint8_t TXCONF_ATTEMPTS = 8; @@ -111,8 +108,9 @@ class Lmic { static OsDeltaTime calcAirTime(rps_t rps, uint8_t plen); private: + using Job = OsJobType; Radio &radio; - OsJobType osjob; + Job next_job; // Radio settings TX/RX (also accessed by HAL) OsTime rxtime; // time of detect of change of state of radio module @@ -128,7 +126,7 @@ class Lmic { // ADR adjusted TX power, limit power to this value. // dBm - int8_t adrTxPow; + int8_t adrTxPow = 0; dr_t datarate = 0; // current data rate private: @@ -144,10 +142,10 @@ class Lmic { // time device can send again OsTime globalDutyAvail; // current network id (~0 - none) - uint32_t netid; + uint32_t netid = 0; // configured up repeat for unconfirmed message, reset after join. // Not handle properly cf: LoRaWAN™ Specification §5.2 - uint8_t upRepeat; + uint8_t upRepeat = 0; uint8_t clockError = 0; // Inaccuracy in the clock. CLOCK_ERROR_MAX // represents +/-100% error @@ -155,46 +153,46 @@ class Lmic { // pending data length uint8_t pendTxLen = 0; // pending data ask for confirmation - bool pendTxConf; + bool pendTxConf =false; // pending data port - uint8_t pendTxPort; + uint8_t pendTxPort = 0; // pending data - uint8_t pendTxData[MAX_LEN_PAYLOAD]; + std::array pendTxData; // last generated nonce // set at random value at reset. - uint16_t devNonce; + uint16_t devNonce = 0; // device address, set at 0 at reset. - devaddr_t devaddr; + devaddr_t devaddr = 0; // device level down stream seqno, reset after join. - uint32_t seqnoDn; + uint32_t seqnoDn = 0; // device level up stream seqno, reset after join. - uint32_t seqnoUp; + uint32_t seqnoUp = 0; // dn frame confirm pending: LORA::FCT_ACK or 0, reset after join - uint8_t dnConf; + uint8_t dnConf = 0; // counter until we reset data rate (-128=off), reset after join // ask for confirmation if > 0 // lower data rate if > LINK_CHECK_DEAD - int8_t adrAckReq; + int8_t adrAckReq = 0; // Rx delay after TX, init at reset OsDeltaTime rxDelay; // link adr adapt answer pending, init after join // use bit 15 as flag, other as value for acq - uint8_t ladrAns; + uint8_t ladrAns = 0; // device status answer pending, init after join - bool devsAns; + bool devsAns =false; // RX timing setup answer pending, init after join - bool rxTimingSetupAns; + bool rxTimingSetupAns =false;; #if !defined(DISABLE_MCMD_DCAP_REQ) // have to ACK duty cycle settings, init after join - bool dutyCapAns; + bool dutyCapAns = false; #endif #if !defined(DISABLE_MCMD_SNCH_REQ) // answer set new channel, init after join. - uint8_t snchAns; + uint8_t snchAns = 0; #endif private: @@ -203,11 +201,10 @@ class Lmic { #if !defined(DISABLE_MCMD_DN2P_SET) // 0=no answer pend, 0x80+ACKs, init after join - uint8_t dn2Ans; + uint8_t dn2Ans =0; #endif - // Public part of MAC state - uint8_t frame[MAX_LEN_FRAME]; + FrameBuffer frame; // transaction flags (TX-RX combo) TxRxStatusValue txrxFlags; // 0 no data or zero length data, >0 byte count of data @@ -219,7 +216,7 @@ class Lmic { protected: // 1 RX window DR offset - uint8_t rx1DrOffset; + uint8_t rx1DrOffset =0; uint8_t txCnt = 0; LmicRand rand; @@ -233,7 +230,7 @@ class Lmic { void setupRx2(); OsTime schedRx12(OsDeltaTime delay, dr_t dr); - void txDone(OsDeltaTime delay); + void txDone(); void runReset(); void runEngineUpdate(); @@ -243,13 +240,10 @@ class Lmic { bool processJoinAccept(); void processRxJacc(); - void jreqDone(); void startJoiningCallBack(); void buildJoinRequest(); - void updataDone(); - void stateJustJoined(); void reportEvent(EventType ev); @@ -317,7 +311,7 @@ class Lmic { TxRxStatusValue getTxRxFlags() const { return txrxFlags; }; uint8_t getDataLen() const { return dataLen; }; uint8_t const *getData() const { - return dataBeg ? frame + dataBeg : nullptr; + return dataBeg ? frame.cbegin() + dataBeg : nullptr; }; uint8_t getPort() const { return txrxFlags.test(TxRxStatus::PORT) ? frame[dataBeg - 1] : 0; diff --git a/src/lmic/lmic.us915.cpp b/src/lmic/lmic.us915.cpp index 7028428..ab70610 100644 --- a/src/lmic/lmic.us915.cpp +++ b/src/lmic/lmic.us915.cpp @@ -11,8 +11,8 @@ *******************************************************************************/ #include "../hal/print_debug.h" -#include "lmic.us915.h" #include "bufferpack.h" +#include "lmic.us915.h" #include "lmic_table.h" #include @@ -42,37 +42,42 @@ CONST_TABLE(uint8_t, maxFrameLens) [] = {24, 66, 142, 255, 255, 255, 255, 255, 66, 142}; namespace { -constexpr uint8_t rps_DR0 = - rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR1 = - rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR2 = - rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR3 = - rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR4 = - rps_t{SF8, BandWidth::BW500, CodingRate::CR_4_5, false}.rawValue(); - -constexpr uint8_t rps_DR8 = - rps_t{SF12, BandWidth::BW500, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR9 = - rps_t{SF11, BandWidth::BW500, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR10 = - rps_t{SF10, BandWidth::BW500, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR11 = - rps_t{SF9, BandWidth::BW500, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR12 = - rps_t{SF8, BandWidth::BW500, CodingRate::CR_4_5, false}.rawValue(); -constexpr uint8_t rps_DR13 = - rps_t{SF7, BandWidth::BW500, CodingRate::CR_4_5, false}.rawValue(); +constexpr uint8_t rps_DR0 = rps_t{SF10, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR1 = rps_t{SF9, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR2 = rps_t{SF8, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR3 = rps_t{SF7, BandWidth::BW125, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR4 = rps_t{SF8, BandWidth::BW500, CodingRate::CR_4_5}; + +constexpr uint8_t rps_DR8 = rps_t{SF12, BandWidth::BW500, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR9 = rps_t{SF11, BandWidth::BW500, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR10 = rps_t{SF10, BandWidth::BW500, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR11 = rps_t{SF9, BandWidth::BW500, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR12 = rps_t{SF8, BandWidth::BW500, CodingRate::CR_4_5}; +constexpr uint8_t rps_DR13 = rps_t{SF7, BandWidth::BW500, CodingRate::CR_4_5}; } // namespace +// clang-format off CONST_TABLE(uint8_t, _DR2RPS_CRC) -[] = {ILLEGAL_RPS, rps_DR0, rps_DR1, rps_DR2, rps_DR3, rps_DR4, - ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, rps_DR8, rps_DR9, rps_DR10, - rps_DR11, rps_DR12, rps_DR13, ILLEGAL_RPS}; - +[] = { + ILLEGAL_RPS, + rps_DR0, + rps_DR1, + rps_DR2, + rps_DR3, + rps_DR4, + ILLEGAL_RPS, + ILLEGAL_RPS, + ILLEGAL_RPS, + rps_DR8, + rps_DR9, + rps_DR10, + rps_DR11, + rps_DR12, + rps_DR13, + ILLEGAL_RPS + }; +// clang-format on uint8_t LmicUs915::getRawRps(dr_t dr) const { return TABLE_GET_U1(_DR2RPS_CRC, dr + 1); } @@ -319,5 +324,4 @@ FrequencyAndRate LmicUs915::defaultRX2Parameter() const { return {FREQ_DNW2, DR_DNW2}; } -LmicUs915::LmicUs915(Radio &aradio) - : Lmic(aradio) {} +LmicUs915::LmicUs915(Radio &aradio) : Lmic(aradio) {} diff --git a/src/lmic/lmic.us915.h b/src/lmic/lmic.us915.h index dcc122d..1c641e1 100644 --- a/src/lmic/lmic.us915.h +++ b/src/lmic/lmic.us915.h @@ -63,8 +63,8 @@ class LmicUs915 final : public Lmic { FrequencyAndRate defaultRX2Parameter() const final; private: - uint16_t channelMap[(72 + 15) / 16]; // enabled bits - uint16_t chRnd; + uint16_t channelMap[(72 + 15) / 16] = {0}; // enabled bits + uint16_t chRnd = 0; // channel for next TX uint8_t txChnl = 0; diff --git a/src/lmic/lmic_table.h b/src/lmic/lmic_table.h index 47457c9..985a5cf 100644 --- a/src/lmic/lmic_table.h +++ b/src/lmic/lmic_table.h @@ -13,7 +13,6 @@ #ifndef _lmic_table_h_ #define _lmic_table_h_ -#include "Arduino.h" #include // ====================================================================== diff --git a/src/lmic/lmicdynamicchannel.h b/src/lmic/lmicdynamicchannel.h index 82ee696..cc3cad3 100644 --- a/src/lmic/lmicdynamicchannel.h +++ b/src/lmic/lmicdynamicchannel.h @@ -28,7 +28,7 @@ class LmicDynamicChannel : public Lmic { virtual void loadStateWithoutTimeData(RetrieveAbtract &store) final; #endif - bool setupChannel(uint8_t channel, uint32_t newfreq, uint16_t drmap) = 0; + bool setupChannel(uint8_t channel, uint32_t newfreq, uint16_t drmap) override = 0; protected: explicit LmicDynamicChannel(Radio &radio, @@ -40,14 +40,14 @@ class LmicDynamicChannel : public Lmic { int8_t getTxPower() const final; FrequencyAndRate getRx1Parameter() const final; - uint8_t getRawRps(dr_t dr) const =0; - int8_t pow2dBm(uint8_t powerIndex) const =0; + uint8_t getRawRps(dr_t dr) const override =0; + int8_t pow2dBm(uint8_t powerIndex) const override =0; OsDeltaTime getDwn2SafetyZone() const final; - OsDeltaTime dr2hsym(dr_t dr) const =0; - uint32_t convFreq(const uint8_t *ptr) const =0; - bool validRx1DrOffset(uint8_t drOffset) const =0; + OsDeltaTime dr2hsym(dr_t dr) const override =0; + uint32_t convFreq(const uint8_t *ptr) const override =0; + bool validRx1DrOffset(uint8_t drOffset) const override =0; - virtual void initDefaultChannels(); + void initDefaultChannels() override; void disableChannel(uint8_t channel) final; void handleCFList(const uint8_t *ptr) final; @@ -58,7 +58,7 @@ class LmicDynamicChannel : public Lmic { OsTime nextTx(OsTime now) final; void initJoinLoop() final; bool nextJoinState() final; - FrequencyAndRate defaultRX2Parameter() const = 0; + FrequencyAndRate defaultRX2Parameter() const override = 0; const int8_t MaxEIRP; const dr_t MaxJoinDR; diff --git a/src/lmic/lmicrand.h b/src/lmic/lmicrand.h index 7960c72..030f4b7 100644 --- a/src/lmic/lmicrand.h +++ b/src/lmic/lmicrand.h @@ -14,6 +14,7 @@ #define _lmicrand_h_ #include +#include class Radio; class Aes; @@ -39,9 +40,8 @@ class LmicRand { private: Aes &aes; - // (initialized by init() with radio RSSI, used by rand1()) - uint8_t index; - uint8_t randbuf[16]; + uint8_t index = 16; + std::array randbuf; }; #endif diff --git a/src/lmic/lmicrandfromaes.cpp b/src/lmic/lmicrandfromaes.cpp index 2792399..3d5fe4e 100644 --- a/src/lmic/lmicrandfromaes.cpp +++ b/src/lmic/lmicrandfromaes.cpp @@ -26,9 +26,9 @@ void LmicRand::init(Radio &radio) { // return next random byte derived from seed buffer uint8_t LmicRand::uint8() { - if (index > 15) { + if (index >= randbuf.size()) { // encrypt seed with any key - aes.encrypt(randbuf, 16); + aes.encrypt(randbuf.begin(), randbuf.size()); index = 0; } return randbuf[index++]; diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index 1776dd5..15b4435 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -14,6 +14,7 @@ #define _lorabase_h_ #include "oslmic.h" +#include enum class CodingRate : uint8_t { CR_4_5 = 0, CR_4_6, CR_4_7, CR_4_8 }; enum _sf_t { FSK = 0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; @@ -31,19 +32,17 @@ struct rps_t { constexpr BandWidth getBw() const { return static_cast(bwRaw); }; constexpr CodingRate getCr() const { return static_cast(crRaw); }; - constexpr uint8_t rawValue() { + + constexpr operator uint8_t() const { return (sf | (bwRaw << 3) | (crRaw << 5) | (nocrc ? (1 << 7) : 0)); } - constexpr rps_t(sf_t asf, BandWidth bw, CodingRate cr, bool anocrc) + constexpr rps_t(sf_t asf, BandWidth bw, CodingRate cr, bool anocrc = false) : sf(asf), bwRaw(static_cast(bw)), crRaw(static_cast(cr)), nocrc(anocrc){}; explicit constexpr rps_t(uint8_t rawValue) : sf(rawValue & 0x07), bwRaw((rawValue >> 3) & 0x03), - crRaw((rawValue >> 5) & 0x03), nocrc(rawValue & (1 << 7)) - //: rawValue(rawValue) - {}; - rps_t(){}; + crRaw((rawValue >> 5) & 0x03), nocrc(rawValue & (1 << 7)){}; }; constexpr uint8_t ILLEGAL_RPS = 0xFF; @@ -58,6 +57,8 @@ constexpr uint8_t DELAY_JACC2 = DELAY_JACC1 + DELAY_EXTDNW2; // in secs constexpr uint8_t DELAY_DNW2 = DELAY_DNW1 + DELAY_EXTDNW2; // in secs down window #1 +using FrameBuffer = std::array; + enum class PktDir : uint8_t { UP = 0, DOWN = 1, diff --git a/src/lmic/lorawanpacket.h b/src/lmic/lorawanpacket.h index 55f1abc..399e6fc 100644 --- a/src/lmic/lorawanpacket.h +++ b/src/lmic/lorawanpacket.h @@ -98,8 +98,6 @@ constexpr uint8_t ftype_proprietary = 0x07 << ftype_offset; constexpr uint8_t major_mask = 0x03; constexpr uint8_t major_v1 = 0x00; - - } // namespace mhdr } // namespace lorawan diff --git a/src/lmic/oslmic.h b/src/lmic/oslmic.h index 69f1965..123773d 100644 --- a/src/lmic/oslmic.h +++ b/src/lmic/oslmic.h @@ -14,7 +14,6 @@ #define _oslmic_h_ #include "../hal/hal.h" -#include "Arduino.h" #include "config.h" #include "osticks.h" #include @@ -45,7 +44,6 @@ OsTime os_getTime(void); #endif // !HAS_os_calls - template class OsJobType final { public: using osjobcbTyped_t = void (T::*)(); @@ -55,24 +53,9 @@ template class OsJobType final { OsTime deadline; public: - // clear scheduled job - void clearCallback() { funcTyped = nullptr; } - - void setCallbackFuture(osjobcbTyped_t cb) { funcTyped = cb; }; - void setCallbackRunnable(osjobcbTyped_t cb) { - setCallbackFuture(cb); - setRunnable(); - }; - void setRunnable() { setTimed(os_getTime()); } - void setTimedCallback(OsTime time, osjobcbTyped_t cb) { - setCallbackFuture(cb); - setTimed(time); - }; - // schedule timed job - void setTimed(OsTime time) { - // fill-in job - deadline = time; - } + OsJobType() : OsJobType(nullptr, OsTime()){}; + explicit OsJobType(osjobcbTyped_t cb) : OsJobType(cb, os_getTime()){}; + explicit OsJobType(osjobcbTyped_t cb, OsTime time) : funcTyped(cb), deadline(time){}; OsDeltaTime run(T &refClass) { diff --git a/src/lmic/radio.cpp b/src/lmic/radio.cpp index 8ff2a8e..2cac875 100644 --- a/src/lmic/radio.cpp +++ b/src/lmic/radio.cpp @@ -11,4 +11,4 @@ int8_t Radio::get_last_packet_snr_x4() const { } -Radio::Radio(lmic_pinmap const &pins) : hal(pins) {} +Radio::Radio() {} diff --git a/src/lmic/radio.h b/src/lmic/radio.h index f2a41e0..c1a502a 100644 --- a/src/lmic/radio.h +++ b/src/lmic/radio.h @@ -17,19 +17,20 @@ #include "lorabase.h" #include "osticks.h" #include +#include class Radio { public: - explicit Radio(lmic_pinmap const &pins); + explicit Radio(); virtual void init(void) = 0; virtual void rst() const = 0; virtual void tx(uint32_t freq, rps_t rps, int8_t txpow, uint8_t const *framePtr, uint8_t frameLength) = 0; virtual void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) = 0; - virtual void init_random(uint8_t randbuf[16]) = 0; - virtual uint8_t handle_end_rx(uint8_t *framePtr) = 0; + virtual void init_random(std::array &randbuf) = 0; + virtual uint8_t handle_end_rx(FrameBuffer &frame) = 0; virtual void handle_end_tx() const = 0; virtual uint8_t rssi() const = 0; @@ -41,7 +42,7 @@ class Radio { protected: int8_t last_packet_snr_reg = 0; uint8_t last_packet_rssi_reg = 0; - HalIo hal; + }; #endif \ No newline at end of file diff --git a/src/lmic/radio_fake.cpp b/src/lmic/radio_fake.cpp new file mode 100644 index 0000000..a7a2862 --- /dev/null +++ b/src/lmic/radio_fake.cpp @@ -0,0 +1,110 @@ +/******************************************************************************* + + *******************************************************************************/ + +#include "radio_fake.h" +#include "../hal/print_debug.h" + +#include "../aes/lmic_aes.h" +#include "bufferpack.h" +#include "lmic.h" +#include "lmic_table.h" + +#include + +void RadioFake::init() { PRINT_DEBUG(1, F("Radio Init")); } + +// get random seed from wideband noise rssi +void RadioFake::init_random(std::array &randbuf) { + PRINT_DEBUG(1, F("Init random")); + PRINT_DEBUG(1, F("Fake init with constant values")); + + uint8_t i = 0; + std::generate_n(begin(randbuf), end(randbuf), [&i]() { return i++; }); +} + +uint8_t RadioFake::rssi() const { return 0; } + +// called by hal ext IRQ handler +// (radio goes to stanby mode after tx/rx operations) +uint8_t RadioFake::handle_end_rx(FrameBuffer &frame) { + PRINT_DEBUG(1, F("Handle end rx")); + uint8_t length = + std::min(simulateReceiveSize, static_cast(frame.max_size())); + // max frame size 64 + std::copy(begin(simulateReceive), begin(simulateReceive) + length, + begin(frame)); + simulateReceiveSize = 0; + return length; +} + +void RadioFake::handle_end_tx() const { PRINT_DEBUG(1, F("Handle end tx")); } + +void RadioFake::rst() const { PRINT_DEBUG(1, F("Radio reset")); } + +constexpr uint8_t crForLog(rps_t const rps) { + return (5 - static_cast(CodingRate::CR_4_5) + + static_cast(rps.getCr())); +} + +CONST_TABLE(uint16_t, BW_ENUM_TO_VAL)[] = {125, 250, 500, 0}; + +uint16_t bwForLog(rps_t const rps) { + auto index = static_cast(rps.getBw()); + return TABLE_GET_U2(BW_ENUM_TO_VAL, index); +} + +void RadioFake::tx(uint32_t const freq, rps_t const rps, int8_t const txpow, + uint8_t const *const framePtr, uint8_t const frameLength) { + endOfOperation = hal_ticks() + Lmic::calcAirTime(rps, frameLength); + char buffer[64 * 2]; + char *pos = buffer; + std::for_each(framePtr, framePtr + frameLength, [&pos](uint8_t const elem) { + // *pos= elem; + sprintf(pos, "%02X", elem); + pos += 2; + }); + PRINT_DEBUG(1, + F("TXDATA: { \"ts\":%" PRIu32 ", \"f\":%" PRIu32 + ", \"len\":%d, \"SF\":%d, \"BW\":%d, \"CR\":\"4/%d\", " + "\"pow\":%d, \"data\":\"%s\" }"), + endOfOperation, freq, frameLength, rps.sf + 6, bwForLog(rps), + crForLog(rps), txpow, buffer); +} + +void RadioFake::rx(uint32_t const freq, rps_t const rps, uint8_t const rxsyms, + OsTime const rxtime) { + // now instruct the radio to receive + // busy wait until exact rx time + if (rxtime < os_getTime()) { + PRINT_DEBUG(1, F("RX LATE : %" PRIu32 " WANTED, late %" PRIi32 " ms"), + rxtime, (os_getTime() - rxtime).to_ms()); + } + hal_waitUntil(rxtime); + if (simulateReceiveSize > 0) { + endOfOperation = rxTime + Lmic::calcAirTime(rps, simulateReceiveSize); + + } else { + endOfOperation = hal_ticks() + Lmic::calcAirTime(rps, rxsyms); + } + + PRINT_DEBUG(1, + F("RXMODE, freq=%" PRIu32 ", rxsymb=%d, SF=%d, BW=%d, CR=4/%d"), + freq, rxsyms, rps.sf + 6, bwForLog(rps), crForLog(rps)); +} + +/** + * Check the IO pin. + * Return true if the radio has finish it's operation + */ +bool RadioFake::io_check() const { return (endOfOperation < hal_ticks()); } + +void RadioFake::simulateRx(OsTime const timeOfReceive, + uint8_t const *const buffer, uint8_t const size) { + rxTime = timeOfReceive; + simulateReceiveSize = + std::min(size, static_cast(simulateReceive.max_size())); + std::copy(buffer, buffer + simulateReceiveSize, begin(simulateReceive)); +} + +RadioFake::RadioFake() {} diff --git a/src/lmic/radio_fake.h b/src/lmic/radio_fake.h new file mode 100644 index 0000000..a186475 --- /dev/null +++ b/src/lmic/radio_fake.h @@ -0,0 +1,41 @@ +/******************************************************************************* + + *******************************************************************************/ + +#ifndef radio_fake_h +#define radio_fake_h + +#include "lorabase.h" +#include "osticks.h" +#include "radio.h" +#include +#include + +class RadioFake final : public Radio { +private: + std::array simulateReceive; + uint8_t simulateReceiveSize = 0; + OsTime rxTime; + + OsTime endOfOperation; + +public: + explicit RadioFake(); + + void simulateRx(OsTime timeofreceive, uint8_t const *buffer, uint8_t size); + + void init() final; + void rst() const final; + void tx(uint32_t freq, rps_t rps, int8_t txpow, uint8_t const *framePtr, + uint8_t frameLength) final; + void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) final; + + void init_random(std::array &randbuf) final; + uint8_t handle_end_rx(FrameBuffer &frame) final; + void handle_end_tx() const final; + bool io_check() const final; + + uint8_t rssi() const final; +}; + +#endif \ No newline at end of file diff --git a/src/lmic/radio_sx1262.cpp b/src/lmic/radio_sx1262.cpp index 6016fd5..1781830 100644 --- a/src/lmic/radio_sx1262.cpp +++ b/src/lmic/radio_sx1262.cpp @@ -12,6 +12,11 @@ #include +#ifndef ARDUINO +#define PROGMEM +#define memcpy_P memcpy +#endif + namespace { enum RadioCommand : uint8_t { @@ -62,7 +67,7 @@ enum RadioCommand : uint8_t { void wait_ready(HalIo const &hal) { // wait for busy pin to go low while (hal.io_check0()) { - yield(); + hal.yield(); } } @@ -337,7 +342,7 @@ void RadioSx1262::init() { } // get random seed from wideband noise rssi -void RadioSx1262::init_random(uint8_t randbuf[16]) { +void RadioSx1262::init_random(std::array &randbuf) { PRINT_DEBUG(1, F("Init random")); init_config(); @@ -345,12 +350,13 @@ void RadioSx1262::init_random(uint8_t randbuf[16]) { hal_wait(OsDeltaTime::from_ms(100)); Sx1262Register<4> random_register = {0x0819, {0x00}}; - for (int i = 0; i < 4; i++) { + for (uint8_t i = 0; i < randbuf.size() / 4; i++) { read_register(hal, random_register); PRINT_DEBUG(2, F("Random %x %x %x %x "), random_register.data[0], random_register.data[1], random_register.data[2], random_register.data[3]); - std::copy(random_register.begin(), random_register.end(), randbuf + 4 * i); + std::copy(random_register.begin(), random_register.end(), + randbuf.begin() + 4 * i); } set_standby(false); set_sleep(); @@ -360,7 +366,7 @@ uint8_t RadioSx1262::rssi() const { return 0; } // called by hal ext IRQ handler // (radio goes to stanby mode after tx/rx operations) -uint8_t RadioSx1262::handle_end_rx(uint8_t *const framePtr) { +uint8_t RadioSx1262::handle_end_rx(FrameBuffer &frame) { uint16_t flags = get_irq_status(); uint16_t const RxDone = 1 << 1; @@ -369,12 +375,8 @@ uint8_t RadioSx1262::handle_end_rx(uint8_t *const framePtr) { uint8_t length = 0; if (flags & RxDone) { // read message length - length = read_frame(framePtr); - // read rx quality parameters - // SNR [dB] * 4 - // last_packet_snr_reg = - // static_cast(hal.read_reg(LORARegPktSnrValue)); RSSI [dBm] - 139 - // last_packet_rssi_reg = hal.read_reg(LORARegPktRssiValue); + length = read_frame(frame); + read_packet_status(); } else if (flags & Timeout) { // indicate timeout PRINT_DEBUG(1, F("RX timeout")); @@ -469,7 +471,7 @@ RadioSx1262::RadioSx1262(lmic_pinmap const &pins, RadioSx1262::RadioSx1262(lmic_pinmap const &pins, ImageCalibrationBand const calibration_band, bool dio2_as_rf_switch_ctrl) - : Radio(pins), + : hal(pins), image_calibration_params(TABLE_GET_U2( CALIBRATION_CMD, static_cast(calibration_band))), DIO2_as_rf_switch_ctrl(dio2_as_rf_switch_ctrl) {} @@ -558,7 +560,7 @@ void RadioSx1262::init_config() const { calibrate_all(); set_standby(true); - if(DIO2_as_rf_switch_ctrl) { + if (DIO2_as_rf_switch_ctrl) { set_DIO2_as_rf_switch_ctrl(); } @@ -599,13 +601,14 @@ void RadioSx1262::write_frame(uint8_t const *framePtr, hal.endspi(); } -uint8_t RadioSx1262::read_frame(uint8_t *framePtr) const { +uint8_t RadioSx1262::read_frame(FrameBuffer &frame) const { // read frame status Sx1262Command<2> frame_status = {RadioCommand::GetRxBufferStatus, {0x00, 0x00}}; read_command(hal, frame_status); - uint8_t const len = std::min(frame_status.parameter[0], MAX_LEN_FRAME); + uint8_t const len = std::min(frame_status.parameter[0], + static_cast(frame.max_size())); uint8_t const offset = frame_status.parameter[1]; hal.beginspi(); @@ -614,7 +617,7 @@ uint8_t RadioSx1262::read_frame(uint8_t *framePtr) const { hal.spi(offset); hal.spi(0x00); - std::generate_n(framePtr, len, [this]() { return hal.spi(0x00); }); + std::generate_n(begin(frame), len, [this]() { return hal.spi(0x00); }); hal.endspi(); return len; @@ -641,6 +644,13 @@ uint16_t RadioSx1262::get_irq_status() const { return rmsbf2(cmd.parameter); } +void RadioSx1262::read_packet_status() { + Sx1262Command<3> cmd = {RadioCommand::GetPacketStatus, {}}; + read_command(hal, cmd); + last_packet_rssi_reg = 139 - cmd.parameter[0] / 2; + last_packet_snr_reg = static_cast(cmd.parameter[1]); +} + void RadioSx1262::clear_all_irq() const { send_command(hal, cmds::clear_all_irq); } diff --git a/src/lmic/radio_sx1262.h b/src/lmic/radio_sx1262.h index fc89865..f56ee40 100644 --- a/src/lmic/radio_sx1262.h +++ b/src/lmic/radio_sx1262.h @@ -20,6 +20,7 @@ enum class ImageCalibrationBand : uint8_t { class RadioSx1262 final : public Radio { private: + HalIo hal; // ImageCalibrationBand const image_calibration_band; uint16_t const image_calibration_params; bool const DIO2_as_rf_switch_ctrl; @@ -36,8 +37,8 @@ class RadioSx1262 final : public Radio { uint8_t frameLength) final; void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) final; - void init_random(uint8_t randbuf[16]) final; - uint8_t handle_end_rx(uint8_t *framePtr) final; + void init_random(std::array &randbuf) final; + uint8_t handle_end_rx(FrameBuffer &frame) final; void handle_end_tx() const final; bool io_check() const final; @@ -57,10 +58,11 @@ class RadioSx1262 final : public Radio { void init_config() const; void write_frame(uint8_t const *framePtr, uint8_t frameLength) const; - uint8_t read_frame(uint8_t *framePtr) const; + uint8_t read_frame(FrameBuffer &frame) const; uint8_t get_status() const; uint16_t get_device_errors() const; uint16_t get_irq_status() const; + void read_packet_status(); void clear_all_irq() const; void set_dio1_irq_params(uint16_t mask) const; @@ -75,6 +77,7 @@ class RadioSx1262 final : public Radio { void calibrate_all() const; void clear_device_errors() const; void set_DIO3_as_tcxo_ctrl() const; + }; #endif \ No newline at end of file diff --git a/src/lmic/radio_sx1272.h b/src/lmic/radio_sx1272.h index d46a4c3..f85ef45 100644 --- a/src/lmic/radio_sx1272.h +++ b/src/lmic/radio_sx1272.h @@ -28,7 +28,7 @@ class Radio { uint8_t frameLength); void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime); - void init_random(uint8_t randbuf[16]); + void init_random(std::array &randbuf); uint8_t handle_end_rx(uint8_t *framePtr); void handle_end_tx() const; diff --git a/src/lmic/radio_sx1276.cpp b/src/lmic/radio_sx1276.cpp index b2410a9..911b674 100644 --- a/src/lmic/radio_sx1276.cpp +++ b/src/lmic/radio_sx1276.cpp @@ -174,12 +174,12 @@ void RadioSx1276::opmodeLora() const { hal.write_reg(RegOpMode, OPMODE_LORA); } // configure LoRa modem (cfg1, cfg2) void RadioSx1276::configLoraModem(rps_t rps) { - auto const sf = rps.sf; auto const mc1 = bw_to_mc1(rps.getBw()) | cr_to_mc1(rps.getCr()); // set ModemConfig1 hal.write_reg(LORARegModemConfig1, mc1); + auto const sf = rps.sf; uint8_t mc2 = sf_to_mc2(sf); if (!rps.nocrc) { mc2 |= MC2_RX_PAYLOAD_CRCON; @@ -297,12 +297,12 @@ void RadioSx1276::init() { } // get random seed from wideband noise rssi -void RadioSx1276::init_random(uint8_t randbuf[16]) { +void RadioSx1276::init_random(std::array &randbuf) { // seed 15-byte randomness via noise rssi rxrssi(); while ((hal.read_reg(RegOpMode) & OPMODE_MASK) != OPMODE_RX) ; // continuous rx - for (uint8_t i = 1; i < 16; i++) { + for (uint8_t i = 1; i < randbuf.size(); i++) { for (uint8_t j = 0; j < 8; j++) { uint8_t b; // wait for two non-identical subsequent least-significant bits while ((b = hal.read_reg(LORARegRssiWideband) & 0x01) == @@ -323,7 +323,7 @@ uint8_t RadioSx1276::rssi() const { // called by hal ext IRQ handler // (radio goes to stanby mode after tx/rx operations) -uint8_t RadioSx1276::handle_end_rx(uint8_t *const framePtr) { +uint8_t RadioSx1276::handle_end_rx(FrameBuffer &frame) { uint8_t const flags = hal.read_reg(LORARegIrqFlags); PRINT_DEBUG(2, F("irq: flags: 0x%x\n"), flags); @@ -334,12 +334,12 @@ uint8_t RadioSx1276::handle_end_rx(uint8_t *const framePtr) { length = hal.read_reg(LORARegRxNbBytes); // for security clamp length of data - length = std::min(length, MAX_LEN_FRAME); + length = std::min(length, static_cast(frame.max_size())); // set FIFO read address pointer hal.write_reg(LORARegFifoAddrPtr, hal.read_reg(LORARegFifoRxCurrentAddr)); // now read the FIFO - hal.read_buffer(RegFifo, framePtr, length); + hal.read_buffer(RegFifo, frame.begin(), length); // read rx quality parameters // SNR [dB] * 4 @@ -499,4 +499,4 @@ void RadioSx1276::rx(uint32_t const freq, rps_t const rps, uint8_t const rxsyms, */ bool RadioSx1276::io_check() const { return hal.io_check(); } -RadioSx1276::RadioSx1276(lmic_pinmap const &pins) : Radio(pins) {} +RadioSx1276::RadioSx1276(lmic_pinmap const &pins) : hal(pins) {} diff --git a/src/lmic/radio_sx1276.h b/src/lmic/radio_sx1276.h index 2f29948..9671df2 100644 --- a/src/lmic/radio_sx1276.h +++ b/src/lmic/radio_sx1276.h @@ -24,10 +24,9 @@ struct RegSet { constexpr uint16_t raw() const { return reg << 8 | val; }; constexpr RegSet(uint8_t areg, uint8_t aval) : reg(areg), val(aval){}; - constexpr RegSet(uint16_t raw) : reg(raw >> 8), val(raw & 0xFF){}; + explicit constexpr RegSet(uint16_t raw) : reg(raw >> 8), val(raw & 0xFF){}; }; - class RadioSx1276 final : public Radio { public: @@ -38,8 +37,8 @@ class RadioSx1276 final : public Radio { uint8_t frameLength) final; void rx(uint32_t freq, rps_t rps, uint8_t rxsyms, OsTime rxtime) final; - void init_random(uint8_t randbuf[16]) final; - uint8_t handle_end_rx(uint8_t *framePtr) final; + void init_random(std::array &randbuf) final; + uint8_t handle_end_rx(FrameBuffer &frame) final; void handle_end_tx() const final; bool io_check() const final; @@ -53,8 +52,8 @@ class RadioSx1276 final : public Radio { void configPower(int8_t pw) const; void rxrssi() const; void clear_irq() const; - void write_list_of_reg(uint16_t const * listcmd, uint8_t nb_cmd) const; - + void write_list_of_reg(uint16_t const *listcmd, uint8_t nb_cmd) const; + HalIo hal; }; #endif \ No newline at end of file diff --git a/test/test_aes.cpp b/test/test_aes.cpp index 29fefbb..a6718d6 100644 --- a/test/test_aes.cpp +++ b/test/test_aes.cpp @@ -1,130 +1,189 @@ #include "test_aes.h" -#include #include "aes/aes_encrypt.h" +#include "aes/lmic_aes.h" +#include +#include #include namespace { -constexpr uint8_t HexCharToInt(char const char1) -{ - - return (char1 >= '0' && char1 <= '9') - ? char1 - '0' - : (char1 >= 'A' && char1 <= 'F') - ? char1 - 'A' + 0x0A - : (char1 >= 'a' && char1 <= 'f') ? char1 - 'a' + 0x0A : 0; -} -constexpr uint8_t HexCharToInt(char const char1, char const char2) -{ - - return (HexCharToInt(char1) * 0x10) + HexCharToInt(char2); -} - -class ValGetter -{ -public: - static const uint8_t size = 16; - uint8_t const val[size]; - - uint8_t const *begin() const { return val; }; - uint8_t const *end() const { return val + size; }; - - ValGetter(char const *VAL) : val{HexCharToInt(VAL[0], VAL[1]), HexCharToInt(VAL[2], VAL[3]), - HexCharToInt(VAL[4], VAL[5]), HexCharToInt(VAL[6], VAL[7]), - HexCharToInt(VAL[8], VAL[9]), HexCharToInt(VAL[10], VAL[11]), - HexCharToInt(VAL[12], VAL[13]), HexCharToInt(VAL[14], VAL[15]), - HexCharToInt(VAL[16], VAL[17]), HexCharToInt(VAL[18], VAL[19]), - HexCharToInt(VAL[20], VAL[21]), HexCharToInt(VAL[22], VAL[23]), - HexCharToInt(VAL[24], VAL[25]), HexCharToInt(VAL[26], VAL[27]), - HexCharToInt(VAL[28], VAL[29]), HexCharToInt(VAL[30], VAL[31])} {}; -}; + constexpr uint8_t HexCharToInt(char const char1) + { + + return (char1 >= '0' && char1 <= '9') ? char1 - '0' + : (char1 >= 'A' && char1 <= 'F') ? char1 - 'A' + 0x0A + : (char1 >= 'a' && char1 <= 'f') ? char1 - 'a' + 0x0A + : 0; + } + constexpr uint8_t HexCharToInt(char const char1, char const char2) + { + + return (HexCharToInt(char1) * 0x10) + HexCharToInt(char2); + } + + class ValGetter + { + public: + static const uint8_t size = 16; + uint8_t const val[size]; + + uint8_t const *begin() const { return val; }; + uint8_t const *end() const { return val + size; }; + + ValGetter(char const *VAL) + : val{HexCharToInt(VAL[0], VAL[1]), HexCharToInt(VAL[2], VAL[3]), + HexCharToInt(VAL[4], VAL[5]), HexCharToInt(VAL[6], VAL[7]), + HexCharToInt(VAL[8], VAL[9]), HexCharToInt(VAL[10], VAL[11]), + HexCharToInt(VAL[12], VAL[13]), HexCharToInt(VAL[14], VAL[15]), + HexCharToInt(VAL[16], VAL[17]), HexCharToInt(VAL[18], VAL[19]), + HexCharToInt(VAL[20], VAL[21]), HexCharToInt(VAL[22], VAL[23]), + HexCharToInt(VAL[24], VAL[25]), HexCharToInt(VAL[26], VAL[27]), + HexCharToInt(VAL[28], VAL[29]), HexCharToInt(VAL[30], VAL[31])} {}; + }; } // namespace namespace test_aes { -void run() -{ - RUN_TEST(test_aes_key); - RUN_TEST(test_aes_encript_with_key0); - RUN_TEST(test_aes_encript_with_buff0); -} + void run() + { + RUN_TEST(test_aes_key); + RUN_TEST(test_aes_encript_with_key0); + RUN_TEST(test_aes_encript_with_buff0); + RUN_TEST(test_aes_micverify); + RUN_TEST(test_aes_mic); + RUN_TEST(test_aes_mic_packet); + } -static ValGetter fake_key("000102030405060708090A0B0C0D0E0F"); + static ValGetter fake_key("000102030405060708090A0B0C0D0E0F"); -/** + /** * Test AES Key object for copy */ -void test_aes_key() -{ - AesKey test_key; - TEST_ASSERT_EQUAL(16, test_key.key_size); - - std::copy(fake_key.begin(), fake_key.end(), test_key.data); - AesKey copy_key = test_key; - std::fill(test_key.begin(), test_key.end(), 0); - TEST_ASSERT_EQUAL_MEMORY(fake_key.val, copy_key.data, AesKey::key_size); -} - -void encrypt_run_key0(ValGetter const &plaintext, ValGetter const &result) -{ - - AesKey key; - std::fill(key.begin(), key.end(), 0); - uint8_t buffer[16]; - - std::copy(plaintext.begin(), plaintext.end(), buffer); - aes_128_encrypt(buffer, key); - TEST_ASSERT_EQUAL_MEMORY(result.val, buffer, result.size); -} - -static ValGetter plaintext0("f34481ec3cc627bacd5dc3fb08f273e6"); -static ValGetter result0("0336763e966d92595a567cc9ce537f5e"); -static ValGetter plaintext1("9798c4640bad75c7c3227db910174e72"); -static ValGetter result1("a9a1631bf4996954ebc093957b234589"); -static ValGetter plaintext2("96ab5c2ff612d9dfaae8c31f30c42168"); -static ValGetter result2("ff4f8391a6a40ca5b25d23bedd44a597"); - -/** + void test_aes_key() + { + AesKey test_key; + TEST_ASSERT_EQUAL(16, test_key.max_size()); + + std::copy(fake_key.begin(), fake_key.end(), test_key.begin()); + AesKey copy_key = test_key; + std::fill(test_key.begin(), test_key.end(), 0); + TEST_ASSERT_EQUAL_MEMORY(fake_key.val, copy_key.begin(), copy_key.max_size()); + } + + void encrypt_run_key0(ValGetter const &plaintext, ValGetter const &result) + { + + AesKey key; + std::fill(key.begin(), key.end(), 0); + uint8_t buffer[16]; + + std::copy(plaintext.begin(), plaintext.end(), buffer); + aes_128_encrypt(buffer, key); + TEST_ASSERT_EQUAL_MEMORY(result.val, buffer, result.size); + } + + static ValGetter plaintext0("f34481ec3cc627bacd5dc3fb08f273e6"); + static ValGetter result0("0336763e966d92595a567cc9ce537f5e"); + static ValGetter plaintext1("9798c4640bad75c7c3227db910174e72"); + static ValGetter result1("a9a1631bf4996954ebc093957b234589"); + static ValGetter plaintext2("96ab5c2ff612d9dfaae8c31f30c42168"); + static ValGetter result2("ff4f8391a6a40ca5b25d23bedd44a597"); + + /** * Test known value with a key all equal to 0 */ -void test_aes_encript_with_key0() -{ - encrypt_run_key0(plaintext0, result0); - encrypt_run_key0(plaintext1, result1); - encrypt_run_key0(plaintext2, result2); -} - -void encrypt_run_buff0(ValGetter const &key_val, ValGetter const &result) -{ - - AesKey key; - std::copy(key_val.begin(), key_val.end(), key.begin()); - uint8_t buffer[16]; - std::fill(buffer, buffer + 16, 0); - - aes_128_encrypt(buffer, key); - TEST_ASSERT_EQUAL_MEMORY(result.val, buffer, result.size); -} - -static ValGetter test_key10("10a58869d74be5a374cf867cfb473859"); -static ValGetter result10("6d251e6944b051e04eaa6fb4dbf78465"); -static ValGetter test_key11("caea65cdbb75e9169ecd22ebe6e54675"); -static ValGetter result11("6e29201190152df4ee058139def610bb"); -static ValGetter test_key12("a2e2fa9baf7d20822ca9f0542f764a41"); -static ValGetter result12("c3b44b95d9d2f25670eee9a0de099fa3"); - -/** + void test_aes_encript_with_key0() + { + encrypt_run_key0(plaintext0, result0); + encrypt_run_key0(plaintext1, result1); + encrypt_run_key0(plaintext2, result2); + } + + void encrypt_run_buff0(ValGetter const &key_val, ValGetter const &result) + { + + AesKey key; + std::copy(key_val.begin(), key_val.end(), key.begin()); + uint8_t buffer[16]; + std::fill(buffer, buffer + 16, 0); + + aes_128_encrypt(buffer, key); + TEST_ASSERT_EQUAL_MEMORY(result.val, buffer, result.size); + } + + static ValGetter test_key10("10a58869d74be5a374cf867cfb473859"); + static ValGetter result10("6d251e6944b051e04eaa6fb4dbf78465"); + static ValGetter test_key11("caea65cdbb75e9169ecd22ebe6e54675"); + static ValGetter result11("6e29201190152df4ee058139def610bb"); + static ValGetter test_key12("a2e2fa9baf7d20822ca9f0542f764a41"); + static ValGetter result12("c3b44b95d9d2f25670eee9a0de099fa3"); + + /** * Test known value with a buffer all equal to zeron */ -void test_aes_encript_with_buff0() -{ - - encrypt_run_buff0(test_key10, result10); - encrypt_run_buff0(test_key11, result11); - encrypt_run_buff0(test_key12, result12); -} - -} // namespace test_aes \ No newline at end of file + void test_aes_encript_with_buff0() + { + + encrypt_run_buff0(test_key10, result10); + encrypt_run_buff0(test_key11, result11); + encrypt_run_buff0(test_key12, result12); + } + + void test_aes_mic() + { + // + std::array buff = { + 0x00, 0x39, 0x36, 0x34, 0x63, 0x33, 0x69, 0x13, 0xAA, 0x05, 0x69, 0x35, + 0x74, 0x32, 0x38, 0x31, 0x33, 0x04, 0x89, 0x00, 0x00, 0x00, 0x00}; + + AesKey appkey = {0x98, 0x92, 0x9b, 0x92, 0xc4, 0x9e, 0xdb, 0xa9, + 0x67, 0x6d, 0x64, 0x6d, 0x3b, 0x61, 0x24, 0x56}; + Aes aes; + aes.setDevKey(appkey); + aes.appendMic0(buff.begin(), buff.max_size()); + + std::array mic0 = {0xC6, 0x5B, 0x13, 0x04}; + TEST_ASSERT_EQUAL_MEMORY(mic0.begin(), buff.end() - mic0.max_size(), + mic0.max_size()); + } + + void test_aes_micverify() + { + // + std::array buff = { + 0x00, 0x39, 0x36, 0x34, 0x63, 0x33, 0x69, 0x13, 0xAA, 0x05, 0x69, 0x35, + 0x74, 0x32, 0x38, 0x31, 0x33, 0x04, 0x89, 0xC6, 0x5B, 0x13, 0x04}; + AesKey appkey = {0x98, 0x92, 0x9b, 0x92, 0xc4, 0x9e, 0xdb, 0xa9, + 0x67, 0x6d, 0x64, 0x6d, 0x3b, 0x61, 0x24, 0x56}; + + Aes aes; + aes.setDevKey(appkey); + auto result = aes.verifyMic0(buff.begin(), buff.max_size()); + TEST_ASSERT_TRUE(result); + + // alter buffer + buff[2]++; + auto result_after_alter = aes.verifyMic0(buff.begin(), buff.max_size()); + TEST_ASSERT_FALSE(result_after_alter); + } + + void test_aes_mic_packet() + { + // + std::array buff = { + 0x40, 0xF1, 0x7D, 0xBE, 0x49, 0x00, 0x02, 0x00, 0x01, 0x95, 0x43, 0x78, 0x76, 0x00, 0x00, 0x00, 0x00}; + + AesKey appkey = {0x44, 0x02, 0x42, 0x41, 0xed, 0x4c, 0xe9, 0xa6, 0x8c, 0x6a, 0x8b, 0xc0, 0x55, 0x23, 0x3f, 0xd3}; + Aes aes; + aes.setNetworkSessionKey(appkey); + aes.appendMic(0x49BE7DF1, 2, PktDir::UP, buff.begin(), buff.max_size()); + + std::array mic0 = {0x2B, 0x11, 0xFF, 0x0D}; + TEST_ASSERT_EQUAL_MEMORY(mic0.begin(), buff.end() - mic0.max_size(), + mic0.max_size()); + } + +} // namespace test_aes diff --git a/test/test_aes.h b/test/test_aes.h index 51280b6..e765954 100644 --- a/test/test_aes.h +++ b/test/test_aes.h @@ -7,6 +7,9 @@ namespace test_aes { void test_aes_key(); void test_aes_encript_with_key0(); void test_aes_encript_with_buff0(); + void test_aes_mic(); + void test_aes_micverify(); + void test_aes_mic_packet(); } #endif \ No newline at end of file diff --git a/test/test_keyhandler.cpp b/test/test_keyhandler.cpp new file mode 100644 index 0000000..c7ee572 --- /dev/null +++ b/test/test_keyhandler.cpp @@ -0,0 +1,38 @@ +#include "test_keyhandler.h" +#include "keyhandler.h" +#include "unity.h" +#include + +namespace test_keyhandler { + +void run() { RUN_TEST(test_eui_getter); } + +constexpr char const appEui[] = "1112456789ABCDF0"; +const std::array wanted_eui{0xF0, 0xCD, 0xAB, 0x89, + 0x67, 0x45, 0x12, 0x11}; + +void test_eui_getter() { + EuiGetter obj; + + std::array eui; + obj.getEui(eui.begin()); + + TEST_ASSERT_EQUAL_MEMORY(wanted_eui.begin(), eui.begin(), + wanted_eui.max_size()); +} + +constexpr char const appKey[] = "0123456789ABCDEFFFEEDDCCBBAA9988"; +const std::array wanted_key{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88}; + +void test_key_getter() { + KeyGetter obj; + + auto key = obj.getKey(); + + TEST_ASSERT_EQUAL_MEMORY(wanted_eui.begin(), key.begin(), + wanted_eui.max_size()); +} + +} // namespace test_keyhandler diff --git a/test/test_keyhandler.h b/test/test_keyhandler.h new file mode 100644 index 0000000..2656b2b --- /dev/null +++ b/test/test_keyhandler.h @@ -0,0 +1,10 @@ +#ifndef test_keyhandler_h +#define test_keyhandler_h + + +namespace test_keyhandler { + void run(); + void test_eui_getter(); +} + +#endif \ No newline at end of file diff --git a/test/test_main.cpp b/test/test_main.cpp index cfdaf79..0b0de4f 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -1,3 +1,4 @@ +#ifdef ARDUINO #include #include @@ -14,4 +15,5 @@ void loop() { set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); #endif -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/test/test_main_windows.cpp b/test/test_main_windows.cpp new file mode 100644 index 0000000..dbebc48 --- /dev/null +++ b/test/test_main_windows.cpp @@ -0,0 +1,14 @@ +#ifndef ARDUINO +#include + +#include "test_aes.h" +#include "test_keyhandler.h" + +int main() { + UNITY_BEGIN(); + test_keyhandler::run(); + test_aes::run(); + UNITY_END(); +} + +#endif \ No newline at end of file