diff --git a/docs/fixes/packet-parse-len.md b/docs/fixes/packet-parse-len.md new file mode 100644 index 00000000000..9bd51baae86 --- /dev/null +++ b/docs/fixes/packet-parse-len.md @@ -0,0 +1,213 @@ +# The packet parsers read header fields before testing the length + +Status: fixed, measured on the host, no hardware run (ohne Brett). Behaviour change: +none. Every packet is accepted or refused as before, and an accepted packet has the +same fields; only the bytes the parsers read change. + +## Symptom + +`Dispatcher::tryParsePacket()` (`src/Dispatcher.cpp`) turns raw bytes into a +`mesh::Packet`. Every received radio packet goes through it (`checkRecv()`), and so +does every companion `CMD_SEND_RAW_PACKET` frame (code 65, +`MyMesh::cmdSendRawPacket()`). For the two route types with transport codes +(`ROUTE_TYPE_TRANSPORT_FLOOD` 0 and `ROUTE_TYPE_TRANSPORT_DIRECT` 3) it copies the +4 code bytes and reads the path length byte before it compares anything with `len`. +For the other two it reads the path length byte unconditionally, and for `len == 0` +the header. + +`Packet::readFrom()` (`src/Packet.cpp`) has the same shape and goes one step +further: it also copies the whole path, up to 64 bytes, before its only length test +(`if (i >= len)`). + +The answer was never wrong. Every packet that is too short for its header is still +refused by the later test, so nothing that was read past `len` ever reached a +caller. Step 10c measured this with AddressSanitizer on the companion +(`docs/refactoring/0010-companion-command-table.md`, "Open findings", point 1), +and the same probe on `integration` 07fa9c5c, before this change: + +``` +$ python3 tools/refactoring/companion_asan_probe_summary.py probe_before.log +... +10 ('MyMesh::cmdSendRawPacket', 'mesh::Dispatcher::tryParsePacket', 'READ') codes ['65'] fills ['00', '3f'] lens 4 .. 6 +``` + +Frames of 4 to 6 bytes are a raw packet of 2 to 4 bytes after the code and the +priority. Fills `00` and `3f` are the two headers with version 0 and a route type +with transport codes. + +## Where it is reached + +| Caller | Input | Read past `len` before the fix | +|---|---|---| +| `Dispatcher::checkRecv()` | every radio packet, `len` 1 to 255 | yes, into the rest of the 256 byte stack buffer | +| `MyMesh::cmdSendRawPacket()` | companion frame, raw `len` 2 to 174 | yes, into the tail of the previous frame in `cmd_frame` | +| `RS232Bridge::loop()` via `readFrom()` | bridge frame with a valid checksum, `len` 0 to 256 (256 arrives as 0: `readFrom()` takes a `uint8_t`) | yes, into the checksum and older bytes of `_rx_buffer` | +| `ESPNowBridge::onDataRecv()` via `readFrom()` | ESP-NOW frame with a valid checksum | yes, into the stack copy `decrypted` | +| `BaseChatMesh::importContact()` via `readFrom()` | companion `CMD_IMPORT_CONTACT` | no: the command needs 99 bytes, the parser reads at most 70 | +| `BaseChatMesh::shareContactZeroHop()` via `readFrom()` | an advert the node stored itself | no in practice: the blob was written by `writeTo()` | + +In none of them does the read leave the buffer it is in, so this is a read of stale +or unrelated bytes, not an out of bounds access. What was read only decided which +of two `return false` lines refused the packet. Whether it has any effect on a +board beyond that, for example on timing, is not measured. + +## Fix + +Both parsers test the length before each read: + +- `tryParsePacket()`: `len < 1` is refused before the header is read. After the + version check, which stays first, a packet shorter than its fixed fields (header, + transport codes if any, path length byte: 6 or 2 bytes) is refused with the + existing "partial or corrupt packet" debug line. The path test that follows is + unchanged. +- `readFrom()`: the same two tests, and the existing `i >= len` test moves in front + of the path `memcpy` as `i + bl >= len`. It is the same condition, since `i` is + only advanced by `bl` after it. + +### Alternatives considered + +- **A `FrameReader` over the raw bytes**, as step 10c uses for the companion + frames. Rejected for this fix: `Packet.cpp` and `Dispatcher.cpp` are in every + build, and a structural change of the parsers belongs in its own step with its + own review, not in a bounds fix. +- **One shared parser for both.** They differ on purpose: `readFrom()` needs one + payload byte at least and checks no version, `tryParsePacket()` accepts an empty + payload and refuses versions above `PAYLOAD_VER_1`. Merging them would change + which packets are accepted. +- **Only `tryParsePacket()`.** Rejected: `readFrom()` reads further past `len` + (the whole path) and is reached from both bridges. + +## Verification + +### Host tests + +`test/test_packet_parse` runs in `native_core`, which builds the real `Packet.cpp`, +`Dispatcher.cpp` and `Mesh.cpp`. It has three parts: + +- **Guard page.** Each input is placed at the very end of a readable page with a + `PROT_NONE` page behind it, for every route type, every `len` from 0 to 72 and 13 + path length bytes. Each case runs in a death test child, which exits with code 3 + on `SIGSEGV`/`SIGBUS`, so a read at or above `len` fails that case. +- **Reference.** The code before the fix is kept in the test, verbatim except for + its debug output, and runs on a buffer large enough for every read it makes. Both + parsers are compared with it for all 16 pairs of version and route type, all 256 + path length bytes, `len` 0 to 80 and 180 to 256, and two fills behind `len`. The + result must be the same, and for an accepted packet the header, transport codes, + path length, path, payload length and payload must be equal. The test also + requires both outcomes to occur at least 50,000 times, so it cannot pass vacuously. +- **Radio receive.** The same inputs through `Dispatcher::loop()` with a fake radio + that fills the whole receive buffer with a stale byte first: a packet the + reference accepts reaches `onRecvPacket()` with the same fields, any other goes + back to the pool. + +`CommandTest.SendRawPacketShortFramesOfEveryRouteType` in `test_companion_frames` +sends raw packets of 2 to 10 bytes for every route type and four path length +bytes, after a full length frame of `5a` or `c0` (or nothing) has left its bytes +in `cmd_frame`. A packet is taken exactly when its fixed fields and path are inside +the frame, and goes out byte for byte as it came in. The frame sweep golden +(`frame_sweep.golden`) is unchanged. + +Before the fix (the tests of d748d432 with `src/` of 07fa9c5c), the guard page +cases fail exactly where the analysis says, each with exit code 3, and nothing +else fails: + +``` +tryParsePacket route 0 lens 0 .. 5 route 1 lens 0 .. 1 +tryParsePacket route 2 lens 0 .. 1 route 3 lens 0 .. 5 +readFrom route 0 lens 0 .. 69 route 1 lens 0 .. 65 +readFrom route 2 lens 0 .. 65 route 3 lens 0 .. 69 +PacketParse.*MatchesReference, RadioReceiveMatchesReference PASSED +CommandTest.SendRawPacketShortFramesOfEveryRouteType PASSED +``` + +For `readFrom()` the limit is the longest path among the tested path length bytes, +`0x60` (32 hashes of 2 bytes, 64 bytes): 6 + 64 bytes with transport codes, 2 + 64 +without. + +After the fix all of them pass. + +### Mutation tests + +Each mutation on its own, `pio test -e native_core -f test_packet_parse`: + +| Id | Mutation | Failing cases | +|---|---|---| +| M0 | both files as before the fix | `ReadFromReadsNothingPastLen`, `TryParsePacketReadsNothingPastLen` | +| D1 | `tryParsePacket()`: fixed field test removed | `TryParsePacketReadsNothingPastLen` | +| D2 | `tryParsePacket()`: 6 becomes 5 | `TryParsePacketReadsNothingPastLen` | +| D3 | `tryParsePacket()`: 6 becomes 7 | `RadioReceiveMatchesReference`, `TryParsePacketMatchesReference` | +| D4 | `tryParsePacket()`: 2 becomes 1 | `TryParsePacketReadsNothingPastLen` | +| D5 | `tryParsePacket()`: 2 becomes 3 | `RadioReceiveMatchesReference`, `TryParsePacketMatchesReference` | +| D6 | `tryParsePacket()`: `len < 1` removed | `TryParsePacketReadsNothingPastLen` | +| P1 | `readFrom()`: fixed field test removed | `ReadFromReadsNothingPastLen` | +| P2 | `readFrom()`: 6 becomes 5 | `ReadFromReadsNothingPastLen` | +| P3 | `readFrom()`: 6 becomes 7 | none, equivalent (below) | +| P4 | `readFrom()`: `i + bl >= len` becomes `>` | `ReadFromMatchesReference` | +| P5 | `readFrom()`: path test removed | `ReadFromMatchesReference`, `ReadFromReadsNothingPastLen` | +| P6 | `readFrom()`: `len < 1` removed | `ReadFromReadsNothingPastLen` | + +`D3` is also caught by `SendRawPacketShortFramesOfEveryRouteType` and by the frame +sweep golden in `native_companion`. + +`P3` is an equivalent mutant: a packet with transport codes and exactly 6 bytes has +no payload byte, so the path test refuses it whatever the first test says. +`readFrom()` with `2 -> 3` is equivalent for the same reason and was not run. + +### AddressSanitizer + +`tools/refactoring/companion_asan_probe.patch` with `PROBE_SKIP=1` (the patch needs +`git apply -C1` on this base, its context in `platformio.ini` has moved). After the +fix the `cmdSendRawPacket` / `tryParsePacket` line is gone; the three +`cmdRunCliCommand` lines of open finding 2 stay, with the same counts: + +``` +150 ('MyMesh::cmdRunCliCommand', '', 'READ') codes ['66'] ... +630 ('MyMesh::cmdRunCliCommand', 'CommonRadioPrefs::handleCommand', 'READ') codes ['66'] ... +220 ('MyMesh::cmdRunCliCommand', 'MyMesh::handleCommand', 'READ') codes ['66'] ... +``` + +### Native environments + +All 22 `native*` environments of `platformio.ini` (the 17 of +`.github/workflows/run-unit-tests.yml` and five more) on c0a25f58, on macOS and in +a Linux container (`python:3.12-slim` with `pip install platformio`, the tree +copied in with `git archive`): + +``` +macOS 793 test cases: 1 skipped, 792 succeeded in 00:09:55.643 +Linux 793 test cases: 1 skipped, 792 succeeded in 00:13:11.503 +native_core test_packet_parse PASSED (both) +``` + +The skipped case is `ChipWrapperTest/7.SetParamsGivesTheChipItsDeadlines`, which +skips itself for the SX1276 (`GTEST_SKIP`), as on the base. + +### Builds + +A repeater, a room server and a companion per platform (ESP32, nRF52, RP2040, +STM32), plus the two bridge repeaters that reach `readFrom()`, built from +07fa9c5c and from c0a25f58 with the same toolchain. The STM32 board `wio-e5` has +no room server environment. All 26 builds succeed, RAM is unchanged everywhere, +flash grows by the two length tests: + +| Environment | Flash 07fa9c5c | Flash fix | Delta | RAM 07fa9c5c | RAM fix | +|---|---:|---:|---:|---:|---:| +| `Heltec_v3_repeater` | 1,174,961 | 1,174,977 | +16 | 62,512 | 62,512 | +| `Heltec_v3_room_server` | 1,170,101 | 1,170,113 | +12 | 66,200 | 66,200 | +| `Heltec_v3_companion_radio_ble` | 1,671,589 | 1,671,605 | +16 | 194,844 | 194,844 | +| `Heltec_v3_repeater_bridge_espnow` | 1,185,425 | 1,185,445 | +20 | 64,168 | 64,168 | +| `RAK_4631_repeater` | 441,916 | 441,932 | +16 | 37,240 | 37,240 | +| `RAK_4631_room_server` | 435,068 | 435,084 | +16 | 40,928 | 40,928 | +| `RAK_4631_companion_radio_ble` | 465,176 | 465,208 | +32 | 149,768 | 149,768 | +| `RAK_4631_repeater_bridge_rs232_serial1` | 407,992 | 408,024 | +32 | 38,144 | 38,144 | +| `RAK_11310_repeater` | 236,172 | 236,180 | +8 | 24,728 | 24,728 | +| `RAK_11310_room_server` | 232,124 | 232,140 | +16 | 28,432 | 28,432 | +| `RAK_11310_companion_radio_usb` | 240,532 | 240,556 | +24 | 40,596 | 40,596 | +| `wio-e5_repeater` | 196,848 | 196,856 | +8 | 17,084 | 17,084 | +| `wio-e5_companion_radio_usb` | 199,968 | 199,976 | +8 | 32,940 | 32,940 | + +### Hardware + +None (ohne Brett). The host tests cover both parsers directly and the two paths a +board takes into `tryParsePacket()`; the bridge paths into `readFrom()` are covered +only through the parser itself. diff --git a/docs/refactoring/0009-radio-chip-adapter.md b/docs/refactoring/0009-radio-chip-adapter.md index 344b21c2923..716da926ed9 100644 --- a/docs/refactoring/0009-radio-chip-adapter.md +++ b/docs/refactoring/0009-radio-chip-adapter.md @@ -1,7 +1,7 @@ # 0009: One RadioLib adapter per chip family, part one: the receive detection -Status: steps 9a and 9b implemented. Behaviour change: none. Steps 9c and 9d are -planned below and need their own branches. +Status: steps 9a, 9b and 9c implemented. Behaviour change: none. Step 9d is +planned below and needs its own branch. ## Context @@ -1092,3 +1092,988 @@ No board test for 9b. The release images of SX1262 and SX1268 boards run the sam `std_init()` calls in the same order as before (host suites), and differ in flash by the few bytes above; the debug build output is pinned on the host. LLCC68 has no firmware environment and is covered by the host suites only. + +## Step 9c: one wrapper template per chip family + +Status: implemented. Behaviour change: none. + +### Measured inventory + +`tools/refactoring/scripts/wrapper_inventory.py` cuts every member out of the +eight `CustomWrapper.h`, removes comments and white space, replaces the +chip name with a placeholder and compares the copies. On `integration` +e0d05fde (normalized lines per copy in brackets): + +| Member | Groups of identical copies | +|--------|----------------------------| +| constructor | all eight (1) | +| `setParams()` | SX1262, SX1268, LLCC68, STM32WLx, LR1110, LR1121 (10) / LR2021 (11) / SX1276 (7) | +| `isReceivingPacket()`, `getLastRSSI()`, `getLastSNR()` | all eight (3, 1, 1) | +| `getCurrentRSSI()` | SX1262, SX1268, LLCC68, STM32WLx, SX1276 (3) / LR1110, LR1121, LR2021 (5) | +| `getSpreadingFactor()` | SX1262, SX1268, LLCC68, STM32WLx, SX1276 (1) / LR1110, LR1121, LR2021 (1) | +| `doResetAGC()` | SX1262, SX1268, LLCC68, STM32WLx (1) / LR1110, LR1121 (1) / LR2021, SX1276 absent | +| `setRxBoostedGainMode()` | SX1262, SX1268, LLCC68, LR1110, LR1121 (3) / LR2021 (6) / STM32WLx, SX1276 absent | +| `getRxBoostedGainMode()` | SX1262, SX1268, LLCC68, LR1110, LR1121, LR2021 (3) / STM32WLx, SX1276 absent | +| `onSendFinished()` | LR1110, LR1121, LR2021 (4) / the rest absent | +| `getEstAirtimeFor()` | LR1110, LR1121 (4) / the rest absent | +| `powerOff()` | SX1262 (3) / the rest absent | +| `configSideDetectors()`, `applySideDetectorConfig()`, `calcTsym()`, `_sideDet`, `_numSideDet` | LR2021 only | + +Whole wrappers, identical after normalization: SX1268 and LLCC68; LR1110 and +LR1121. The setParams() differences: LR2021 calls `applySideDetectorConfig()` +between `updatePreamble()` and the deadlines, SX1276 sets no deadlines (its chip +class has no preamble IRQ, see 9a). + +Every cast is `(Custom *)_radio` on a `PhysicalLayer*`. In RadioLib 7.7.1 +every chip class reaches `PhysicalLayer` by single inheritance (`SX126x`, +`SX127x` and `LRxxxx` derive from it directly; `SX1262`, `SX1268`, `LLCC68`, +`STM32WLx`, `SX1276`, `LR1110`, `LR1121`, `LR2021` below those), and the +`Custom` classes add no base, so the C cast is a `static_cast` without pointer +adjustment. `doResetAGC()` casts to `SX126x*` or `LR11x0*` for the reset helper; +both are bases of the chip, so the implicit upcast replaces that cast. + +No class in `src`, `variants` or `examples` derives from a wrapper; the variants +name them as `WRAPPER_CLASS` (86 SX1262, 15 SX1268, 7 LR1110, 4 STM32WLx, 4 +SX1276, 2 LR1121, 2 LR2021 lines in the `.ini` files) and in `target.h`. + +### Template form + +Three templates, each in its own header next to the wrappers: + +```cpp +// CustomChipWrapper.h: what all eight share +template +class CustomChipWrapper : public RadioLibWrapper { +protected: + Chip* chip() const { return static_cast(_radio); } + // first half of every setParams(): the four modem setters, then the preamble + void setModemParams(float freq, float bw, uint8_t sf, uint8_t cr); + // second half for the chips with the receive tracker of 9a + void setRxDeadlines(float bw, uint8_t sf, uint8_t cr); +public: + CustomChipWrapper(Chip& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + bool isReceivingPacket() override { return chip()->isReceiving(); } + float getLastRSSI() const override { return chip()->getRSSI(); } + float getLastSNR() const override { return chip()->getSNR(); } +}; + +// CustomSX126xWrapper.h: SX1262, SX1268, LLCC68, STM32WLx +template +class CustomSX126xWrapper : public CustomChipWrapper { +public: + void setParams(...) override; // setModemParams + setRxDeadlines + float getCurrentRSSI() override; // chip()->getRSSI(false) + uint8_t getSpreadingFactor() const override; // chip()->spreadingFactor + void doResetAGC() override; // sx126xResetAGC(chip(), getRxBoostedGainMode()) +}; + +// CustomLR11x0Wrapper.h: LR1110, LR1121 +template +class CustomLR11x0Wrapper : public CustomChipWrapper { +public: + void setParams(...) override; // setModemParams + setRxDeadlines + float getCurrentRSSI() override; // getRssiInst, -110 if it writes nothing + uint8_t getSpreadingFactor() const override; // chip()->getSpreadingFactor() + void doResetAGC() override; // lr11x0ResetAGC(chip(), chip()->getFreqMHz(), ...) + uint32_t getEstAirtimeFor(int) override; // at least 200 ms + void onSendFinished() override; // preamble length again + bool setRxBoostedGainMode(bool) override; + bool getRxBoostedGainMode() const override; +}; +``` + +`setModemParams()` and `setRxDeadlines()` are plain (not virtual) member +functions of a class template, so they are only instantiated where a wrapper +calls them: SX1276 never calls `setRxDeadlines()` and needs no +`setPreambleMillis()`. Members of the dependent base are named through +`this->`. Everything is C++11 (no `if constexpr`), as in 9b. + +The eight wrapper classes stay real classes with the same names, headers, +constructors and `USE_` defines: + +| Wrapper | Derives from | Keeps in its own body | +|---------|--------------|------------------------| +| `CustomSX1262Wrapper` | `CustomSX126xWrapper` | `powerOff()`, boosted gain set/get | +| `CustomSX1268Wrapper` | `CustomSX126xWrapper` | boosted gain set/get | +| `CustomLLCC68Wrapper` | `CustomSX126xWrapper` | boosted gain set/get | +| `CustomSTM32WLxWrapper` | `CustomSX126xWrapper` | nothing | +| `CustomLR1110Wrapper` | `CustomLR11x0Wrapper` | nothing | +| `CustomLR1121Wrapper` | `CustomLR11x0Wrapper` | nothing | +| `CustomLR2021Wrapper` | `CustomChipWrapper` | `setParams()` (with the side detectors), `getCurrentRSSI()`, `getSpreadingFactor()`, `onSendFinished()`, boosted gain, the side detector members | +| `CustomSX1276Wrapper` | `CustomChipWrapper` | `setParams()` (no deadlines), `getCurrentRSSI()`, `getSpreadingFactor()` | + +`LR2021::setParams()` becomes `setModemParams(); applySideDetectorConfig(); +setRxDeadlines();`, `SX1276::setParams()` becomes `setModemParams()`: same calls, +same order. Every method keeps its virtual signature, so the vtables have the +same slots; only the class that defines a slot changes. + +### Exceptions, kept as they are + +- **STM32WLx has no boosted gain override.** Its wrapper inherits + `RadioLibWrapper::getRxBoostedGainMode()`, which returns `false`, and passes + that to `sx126xResetAGC()`. Moving the SX1262 boosted gain pair into the SX126x + template would make `rxboost` start working on the 16 STM32WL environments: + a behaviour change. So the pair stays in the three SX126x subclasses (three + copies of six lines), and a host test pins that STM32WLx still reports `false`. +- **LR2021 shares with LR11x0** `getCurrentRSSI()`, `getSpreadingFactor()` and + `onSendFinished()`, and **SX1276 shares with SX126x** `getCurrentRSSI()` and + `getSpreadingFactor()`. Each of these is one to five lines; a third template + level (an `LRxxxx` layer, an "RSSI by status" layer) for them would cost more + reading than the copies. They stay in the subclass. +- **`powerOff()`** of SX1262 (`sleep(false)`) is SX1262 only and stays there. + Whether SX1268 and LLCC68 should do the same is a behaviour question. +- **LR2021 boosted gain** goes through standby and a level, a different method. +- `RadioLibWrapper`, `SX126xReset.h`, `LR11x0Reset.h` and the chip classes are + untouched. + +### Alternatives considered for 9c + +- **One template with a traits struct, as in 9b.** The family differences are + different calls (`getRSSI(false)` against `getRssiInst()`, a member against a + getter), not flags. Without `if constexpr`, both branches of `if + (Traits::x)` must compile for every chip, and `getRssiInst()` does not exist on + an SX126x. It would need tag dispatch per call, more machinery than two family + templates. +- **Free functions overloaded on the RadioLib family base** (`rssiNow(SX126x&)`, + `rssiNow(LR11x0&)`), picked by derived to base conversion. Works, but which + overload a chip gets is implicit (LLCC68 and STM32WLx derive from SX1262 in + RadioLib), and a wrong pick compiles. The family templates say it in the class + head. +- **Override in the subclass what differs, keep everything in one template.** + Virtual members of a class template are instantiated with the class, so a + `setParams()` calling `setPreambleMillis()` would not compile for SX1276 even + if the subclass overrides it. +- **`using CustomSX1262Wrapper = CustomSX126xWrapper;`** Breaks a + downstream forward declaration and leaves no place for SX1262 only members + (`powerOff()`, #3638's profile hooks). Rejected as in 9b. +- **Storing a `Chip*` next to `_radio`.** Saves the cast at run time, which costs + nothing anyway (no pointer adjustment), and grows every wrapper object by a + pointer. +- **Moving the boosted gain pair into the SX126x template and overriding it back + to `false` in STM32WLx.** Same behaviour, but reads as if STM32WLx turned a + feature off on purpose. The copies say what is true today. + +### Upstream collisions (checked 21 October 2026) + +All open pull requests on `meshcore-dev/MeshCore` (499) were listed with their +files; these touch one of the eight wrapper headers: + +| PR | Touches | Collision with 9c | +|----|---------|-------------------| +| #3638 dual radio profiles | all eight wrappers (+7 to +22): `setParams()` becomes `bool applyParams()` with a check per setter; LR1110 uses `setLoRaModulationParams()`, LR1121 passes a high bandwidth flag; profile hooks and `minimumAirtimeMillis()` in LR1110 and LR1121; `getLastRSSI()`/`getLastSNR()` removed in all | Heavy, semantic. Its `applyParams()` differs per chip in the LR11x0 family, so on rebase LR1110 and LR1121 override it in their subclass, or the template grows a hook. Its removal of `getLastRSSI()`/`getLastSNR()` becomes one deletion in `CustomChipWrapper`. | +| #3496 KISS gain control | `supportsRxBoostedGain()` in six wrappers, one line each | Textual; with 9c it lands in the three SX126x subclasses and once in `CustomLR11x0Wrapper`, plus LR2021. | +| #2933 noise floor | `verifyRxChipMode()` in four SX126x wrappers | Textual; one method in `CustomSX126xWrapper` instead of four. | +| #2670 repeated direct send | `verifyRxChipMode()` in four SX126x wrappers | As #2933. | +| #2131 error aware receive hook | `tryReadRawWithMeta()` in four SX126x wrappers, 10 lines each | Textual; one method in `CustomSX126xWrapper`. | +| #2842 RSSI and noise floor | removes `getLastRSSI()`/`getLastSNR()` from six wrappers | Textual; one deletion in `CustomChipWrapper`. | +| #1923, #2367 coding rate | `setCodingRate()` in six wrappers | Textual; one method in `CustomChipWrapper`. | +| #2916 dynamic CAD | `getCADDetPeakBase()`: SX126x formula in four, a table in LR1110 | Textual; one method per family template. | +| #2986 sleep fails | `CustomSX1262Wrapper::powerOff()` +1 | Textual, stays in the SX1262 subclass. | +| #2624 DX-LR20 | `CustomLLCC68Wrapper.h` +4 | Textual, LLCC68 subclass. | +| #2999, #2696, #2682, #2198 LR1121 boards | create `CustomLR1121Wrapper.h` as a new file | Already conflict with the file the tree has; 9c changes nothing for them. | +| #2944 LR2021 kit | creates `CustomLR2021Wrapper.h` | As above. | + +#3638 is the one with a semantic collision. The other ten add the same member to +four to six wrappers by hand and would each shrink to one edit with 9c. + +### Verification plan + +- Characterization first, committed before the move and passing unchanged on + both sides: a third test program `test/test_radio_chip_wrappers` in the + `native_radio_chips` environment (no new environment, still 17) builds all + eight wrappers over the real chip classes and the chip stubs, which gain a + `PhysicalLayer` with a call log, `getRSSI(bool)`, `getRssiInst()`, a + `getSpreadingFactor()` that returns a value other than the `spreadingFactor` + member, the calls of both reset helpers, and the LR2021 side detector call. + Pinned per wrapper: the ordered calls of `setParams()` and the deadlines that + reach the chip, `isReceivingPacket()`, last and current RSSI including the + -110 default, SNR, which spreading factor is read, the calls of `doResetAGC()` + (with the boosted gain macros set, so the value passed is pinned, `false` for + STM32WLx), boosted gain set/get, `onSendFinished()`, the 200 ms floor of + `getEstAirtimeFor()`, `powerOff()`. +- Mutation tests on the code commit. +- Build matrix: every firmware environment whose `WRAPPER_CLASS` is one of the + eight, on the start commit and the code commit in the same worktree, flash + and RAM per environment; image comparison of a sample built with + `SOURCE_DATE_EPOCH` set. +- All 17 host environments. + +### What changed + +`CustomChipWrapper.h`, `CustomSX126xWrapper.h` and `CustomLR11x0Wrapper.h` hold +the three templates as designed above. The eight wrapper headers keep their +class, now derived from them, with only the members listed under "Keeps in its +own body". No `(Custom *)_radio` cast is left in `src/helpers/radiolib`. +In `src`, 255 lines go and 153 come in. Callers, `RadioLibWrapper`, the reset +helpers and the chip classes are unchanged. + +After the step, `wrapper_inventory.py` finds no member that is identical in two +wrapper bodies except the constructors and the boosted gain pair of SX1262, +SX1268 and LLCC68 (the exception above). + +The design held with one finding: moving the boosted gain getter into the SX126x +template is not only a behaviour change for STM32WLx, it does not compile +(`CustomSTM32WLx` has no `getRxBoostedGainMode()`; mutation M10b below). + +### Characterization tests + +Committed first (fb0003c2), passing unchanged on that commit and on the code +commit 1234eb98: `test/test_radio_chip_wrappers`, a third test program in the +`native_radio_chips` environment (header only wrappers, and it sets the boosted +gain macros of the reset helpers, so it must not share a binary with +`test_radio_chips`). The environment now also links `RadioLibWrappers.cpp`; +`native` ignores the new program, as it does the other two. One typed suite of +10 tests runs against all eight wrappers, 80 cases (one skipped: SX1276 has no +receive deadlines): + +| Test | Pins | +|------|------| +| `SetParamsCallsTheChipInOrder` | frequency, spreading factor, bandwidth, coding rate, preamble length 32/16; LR2021 then side detectors and standby | +| `IsReceivingPacketAsksTheChip` | IRQ flags, SX1276 the modem status | +| `SetParamsGivesTheChipItsDeadlines` | both deadlines of the new parameters reach the chip (preamble and payload, at and one millisecond after) | +| `SignalReadings` | last RSSI and SNR, instant RSSI `getRSSI(false)` or `getRssiInst()` with the -110 default | +| `SpreadingFactorIsTheChips` | the chip's value, not `LORA_SF` | +| `BoostedGain` | SX126x register 0x8AC, LR11x0 flag, LR2021 level 7/0 from standby, STM32WLx and SX1276 `false` without a chip call | +| `ResetAGC` | the calls of `sx126xResetAGC()` and `lr11x0ResetAGC()` with the boosted gain value passed (`false` for STM32WLx), LR2021 and SX1276 warm sleep only | +| `OnSendFinished` | LR chips set the preamble length again | +| `EstimatedAirtime` | the 200 ms floor of LR1110 and LR1121 | +| `PowerOff` | SX1262 `sleep(false)`, the others `sleep()` | + +For this the chip stubs in `test/test_radio_chips/stubs/RadioLib.h` gain a +`PhysicalLayer` (as in `test/mocks`, with a call log shared with the chip calls), +a module with a HAL for the SX126x reset, `getRSSI(bool)`, `getRssiInst()`, the +modem setters, the calls of both reset helpers, `setSideDetector()` and the +SX127x classes. The 140 existing chip cases pass unchanged with them. + +All 17 host environments pass, 696 cases (616 before, 80 new, one skipped), on +fb0003c2 and on 1234eb98. + +### Mutation tests + +Each mutation was applied to the code commit, `test_radio_chip_wrappers` run, +and the change reverted, one after the other by a script. + +| # | Mutation | Failing cases | +|---|----------|---------------| +| M1 | modem setters: bandwidth before spreading factor | 8, all eight | +| M2 | preamble and payload deadline swapped | 7, all with deadlines | +| M3 | SX126x `setParams()` without deadlines | 4, SX126x family | +| M4 | SX126x instant RSSI reads the packet RSSI | 4, SX126x family | +| M5 | LR11x0 instant RSSI default -100 | 2, LR1110, LR1121 | +| M6 | LR11x0 air time floor 199 ms | 2, LR1110, LR1121 | +| M7 | LR11x0 `onSendFinished()` without the preamble length | 2, LR1110, LR1121 | +| M8 | SX126x reset passes `false` for boosted gain | 3, SX1262, SX1268, LLCC68 | +| M9 | LR11x0 reset passes `false` for boosted gain | 2, LR1110, LR1121 | +| M10 | STM32WLx gets the SX126x boosted gain setter | 1, STM32WLx | +| M10b | STM32WLx gets the getter too | does not compile | +| M11 | LR2021 `setParams()` without side detectors | 1, LR2021 | +| M12 | last RSSI reads the instant RSSI | 8, all eight | +| M13 | SX1276 without its spreading factor override | 1, SX1276 | +| M14 | SX1262 powers off with `sleep(true)` | 1, SX1262 | +| M15 | LR11x0 reset at 0 MHz instead of `getFreqMHz()` | 2, LR1110, LR1121 | +| M16 | SX1276 `setParams()` sets the preamble length twice | 1, SX1276 | + +### Build matrix + +Every firmware environment whose `WRAPPER_CLASS` is one of the eight wrappers: +612 (no environment uses `CustomLLCC68Wrapper`; it is covered by the host suite +only). Each built in the same worktree on `integration` e0d05fde and on the code +commit 1234eb98, same toolchain, with `SOURCE_DATE_EPOCH=1792540800` for every +build, so that equal sources give equal images (see 9b). + +597 build on both sides. The same 15 fail on both sides, none in a radio header: +the `setTxBufferSize` error of the ESP32 `kiss_modem` builds (`Heltec_E213`, +`Heltec_E290`, `Heltec_T190`, `LilyGo_TBeam_1W`, `Station_G3_ESP32`, +`heltec_tracker_v2`), the ESP-NOW callback signature on ESP32-C3 +(`Heltec_ct62`, `Tenstar_C3_sx1262`, `Tenstar_C3_sx1268`, each +`_repeater_bridge_espnow`), image too large (`Station_G2_companion_radio_ble`, +`Station_G3_ESP32_companion_radio_ble`), `D7` undeclared +(`ThinkNode_M5_companion_radio_serial`) and the `EnvironmentSensorManager` link +error (`ThinkNode_M8_repeater`, `_room_server`, `_kiss_modem`). The first error +line of each log is the same on both sides. + +RAM is unchanged in all 597. Flash, after minus before, in bytes: + +| Platform | Wrapper | Environments | Flash difference (environments) | +|----------|---------|--------------|---------------------------------| +| ESP32 | SX1262 | 252 | +0 (171), -4 (22), +4 (14), +12 (11), -52 (8), +8 (8), -56 (4), -16 (4), -44 (2), -20 (2), -8 (2), -2 (2), -60 (1), -48 (1) | +| RP2040 | SX1262 | 25 | +0 (24), +8 (1) | +| nRF52 | SX1262 | 202 | -48 (107), -64 (95) | +| ESP32 | SX1268 | 11 | +0 (8), -48 (1), +4 (1), +8 (1) | +| STM32 | STM32WLx | 16 | +0 (14), +8 (2) | +| ESP32 | SX1276 | 29 | +84 (6), +0 (5), +76 (3), +56 (1), +72 (1), +80 (1), +88 (1), +108 (1), +144 (1), +308 (1), +320 (1), +328 (1), +332 (1), +352 (1), +364 (1), +368 (1), +372 (1), +384 (1) | +| ESP32 | LR1110 | 13 | +0 (6), +4 (5), -12 (2) | +| nRF52 | LR1110 | 25 | +0 (20), -16 (5) | +| ESP32 | LR1121 | 5 | +0 (3), -20 (2) | +| nRF52 | LR1121 | 5 | +0 (4), -16 (1) | +| ESP32 | LR2021 | 9 | +0 (7), -64 (1), +72 (1) | +| nRF52 | LR2021 | 5 | +0 (3), -16 (2) | + +#### Image comparison and where the bytes go + +With `SOURCE_DATE_EPOCH` set for every build, no image of the 597 is byte +identical: the template members have other symbol names, so the linker places +them in another order even where nothing changes size. 20 environments were +built again on both sides keeping the ELF files, at least one per wrapper and +platform and the extremes of each group: `RAK_4631_repeater`, +`GAT562_30S_Mesh_Kit_repeater` (-64), `Heltec_v3_repeater`, +`heltec_v4_tft_companion_radio_usb` (+12), `Ebyte_EoRa-S3_Repeater`, +`waveshare_rp2040_lora_repeater`, `Generic_E22_sx1268_repeater`, +`Meshadventurer_sx1268_terminal_chat` (-48), `wio-e5-mini_repeater`, +`Tiny_Relay_companion_radio_usb` (+8), `Heltec_v2_repeater`, +`Heltec_v2_kiss_modem`, `LilyGo_TLora_V2_1_1_6_terminal_chat` (+384), +`t1000e_repeater`, `ThinkNode_M9_companion_radio_wifi_` (+4), +`Waveshare_S3_LR1121_repeater`, `Waveshare_S3_LR1121_companion_radio_ble` (-20), +`meshnology_w12_repeater`, `meshnology_w12_kiss_modem` (+72), +`MeshTracker_X1_repeater`. All 20 reproduce the image hash of the matrix on both +sides. `nm -S -C` of both ELF files was compared symbol by symbol, with the +template names folded back into the wrapper name and the numbers of compiler +clones (`$isra$N`) folded: + +- **9 have no symbol of another size** (`Heltec_v3_repeater`, + `Ebyte_EoRa-S3_Repeater`, `waveshare_rp2040_lora_repeater`, + `Generic_E22_sx1268_repeater`, `wio-e5-mini_repeater`, + `Tiny_Relay_companion_radio_usb`, `Heltec_v2_kiss_modem`, + `Waveshare_S3_LR1121_repeater`, `meshnology_w12_repeater`; the +8 of + `Tiny_Relay` is section padding). Only the order changed. +- **nRF52 SX1262 (-48, -64)**: `doResetAGC()` shrinks from 208 to 154 bytes, + and `ServerMesh::begin()` and `checkPendingRadioParams()` by 4 each. + Disassembled in `RAK_4631_repeater`: before, GCC speculatively devirtualized + `getRxBoostedGainMode()` (compare the vtable slot at offset 96 with + `CustomSX1262Wrapper::getRxBoostedGainMode`, then inline the read of register + 0x8AC); after, it is a plain virtual call through the same slot (`ldr r3, + [r2, #96]; blx r3`), which reaches the same override. The calls that follow + (`sleep`, `standby`, `SPIwriteStream`, `calibrateImage`, `setDio2AsRfSwitch`, + `setRxBoostedGainMode`) are the same in the same order. The template does not + know the SX1262 override, so the guess is no longer offered. +- **ESP32 SX1262, SX1268, LR chips (-64 to +12)**: only callers of + `setParams()` and the companion command handlers change by -6 to +4 bytes each + (`MyMesh::begin()`, `cmdSetRadioParams()`, `setup()` of the terminal chat + -76, `onSetRadio()` of the LR2021 KISS modem +94, where `setParams()` is now + inlined), plus padding. No wrapper method changes size. +- **ESP32 SX1276 at `-Os` (+56 to +144, 19 environments)**: `setModemParams()` + is emitted out of line (78 bytes), and the code that used to inline the four + setters calls it. +- **LilyGo T-Lora V2.1 1.6 (+308 to +384, 9 environments)**: these build + without optimization. The variant's `build_unflags = -Os` also removes the + `-Os` its own `build_flags` add; the DWARF producer string of the ELF shows no + `-O` option, so GCC's default `-O0` applies. Nothing is inlined there, so each + template level is a real function: `chip()` (17 bytes), the constructor of + `CustomChipWrapper` and its vtable (112 bytes), and + `setModemParams()`. The same code at `-Os` costs the 56 to 144 bytes above. + +So the flash differences are inlining and devirtualization decisions and, on the +one unoptimized board, the cost of the extra class level. No behaviour depends on +them; the host suite pins the calls each wrapper makes. + +
+Flash and RAM per environment (597) + +| Environment | Platform | Wrapper | Flash before | Flash after | Flash | RAM | +|---|---|---|---|---|---|---| +| Ebyte_EoRa-S3_Repeater | ESP32 | SX1262 | 1108433 | 1108433 | +0 | +0 | +| Ebyte_EoRa-S3_companion_radio_ble | ESP32 | SX1262 | 1217029 | 1217021 | -8 | +0 | +| Ebyte_EoRa-S3_companion_radio_usb | ESP32 | SX1262 | 591533 | 591529 | -4 | +0 | +| Ebyte_EoRa-S3_kiss_modem | ESP32 | SX1262 | 507821 | 507821 | +0 | +0 | +| Ebyte_EoRa-S3_room_server | ESP32 | SX1262 | 1103745 | 1103745 | +0 | +0 | +| Ebyte_EoRa-S3_terminal_chat | ESP32 | SX1262 | 535737 | 535685 | -52 | +0 | +| GAT562_30S_Mesh_Kit_companion_radio_ble | nRF52 | SX1262 | 437012 | 436964 | -48 | +0 | +| GAT562_30S_Mesh_Kit_companion_radio_usb | nRF52 | SX1262 | 427988 | 427940 | -48 | +0 | +| GAT562_30S_Mesh_Kit_kiss_modem | nRF52 | SX1262 | 341280 | 341232 | -48 | +0 | +| GAT562_30S_Mesh_Kit_repeater | nRF52 | SX1262 | 411568 | 411504 | -64 | +0 | +| GAT562_30S_Mesh_Kit_room_server | nRF52 | SX1262 | 404656 | 404592 | -64 | +0 | +| GAT562_Mesh_EVB_Pro_kiss_modem | nRF52 | SX1262 | 310916 | 310868 | -48 | +0 | +| GAT562_Mesh_EVB_Pro_repeater | nRF52 | SX1262 | 365764 | 365716 | -48 | +0 | +| GAT562_Mesh_EVB_Pro_room_server | nRF52 | SX1262 | 359260 | 359196 | -64 | +0 | +| GAT562_Mesh_Tracker_Pro_companion_radio_ble | nRF52 | SX1262 | 404184 | 404136 | -48 | +0 | +| GAT562_Mesh_Tracker_Pro_companion_radio_usb | nRF52 | SX1262 | 395176 | 395128 | -48 | +0 | +| GAT562_Mesh_Tracker_Pro_kiss_modem | nRF52 | SX1262 | 310900 | 310852 | -48 | +0 | +| GAT562_Mesh_Tracker_Pro_repeater | nRF52 | SX1262 | 381276 | 381212 | -64 | +0 | +| GAT562_Mesh_Tracker_Pro_room_server | nRF52 | SX1262 | 374364 | 374300 | -64 | +0 | +| GAT562_Mesh_Watch13_companion_radio_ble | nRF52 | SX1262 | 400552 | 400504 | -48 | +0 | +| GAT562_Mesh_Watch13_kiss_modem | nRF52 | SX1262 | 304048 | 304000 | -48 | +0 | +| Generic_E22_sx1262_repeater | ESP32 | SX1262 | 1143573 | 1143573 | +0 | +0 | +| Generic_E22_sx1262_repeater_bridge_espnow | ESP32 | SX1262 | 1154277 | 1154277 | +0 | +0 | +| Generic_E22_sx1268_repeater | ESP32 | SX1268 | 1143361 | 1143361 | +0 | +0 | +| Generic_E22_sx1268_repeater_bridge_espnow | ESP32 | SX1268 | 1154057 | 1154057 | +0 | +0 | +| Heltec_E213_companion_radio_ble | ESP32 | SX1262 | 1274401 | 1274397 | -4 | +0 | +| Heltec_E213_companion_radio_usb | ESP32 | SX1262 | 669857 | 669853 | -4 | +0 | +| Heltec_E213_repeater | ESP32 | SX1262 | 1162233 | 1162233 | +0 | +0 | +| Heltec_E213_repeater_bridge_espnow | ESP32 | SX1262 | 1172677 | 1172677 | +0 | +0 | +| Heltec_E213_room_server | ESP32 | SX1262 | 1157477 | 1157477 | +0 | +0 | +| Heltec_E290_companion_ble | ESP32 | SX1262 | 1272313 | 1272309 | -4 | +0 | +| Heltec_E290_companion_usb | ESP32 | SX1262 | 667645 | 667641 | -4 | +0 | +| Heltec_E290_repeater | ESP32 | SX1262 | 1160185 | 1160185 | +0 | +0 | +| Heltec_E290_repeater_bridge_espnow | ESP32 | SX1262 | 1170613 | 1170613 | +0 | +0 | +| Heltec_E290_room_server | ESP32 | SX1262 | 1155401 | 1155401 | +0 | +0 | +| Heltec_T190_companion_radio_ble_ | ESP32 | SX1262 | 1251489 | 1251485 | -4 | +0 | +| Heltec_T190_companion_radio_usb_ | ESP32 | SX1262 | 647581 | 647577 | -4 | +0 | +| Heltec_T190_repeater_ | ESP32 | SX1262 | 1139397 | 1139397 | +0 | +0 | +| Heltec_T190_repeater_bridge_espnow_ | ESP32 | SX1262 | 1149833 | 1149833 | +0 | +0 | +| Heltec_T190_room_server_ | ESP32 | SX1262 | 1134653 | 1134653 | +0 | +0 | +| Heltec_WSL3_companion_radio_ble | ESP32 | SX1262 | 1649165 | 1649161 | -4 | +0 | +| Heltec_WSL3_companion_radio_usb | ESP32 | SX1262 | 638037 | 638041 | +4 | +0 | +| Heltec_WSL3_companion_radio_wifi | ESP32 | SX1262 | 1087557 | 1087553 | -4 | +0 | +| Heltec_WSL3_repeater | ESP32 | SX1262 | 1161337 | 1161337 | +0 | +0 | +| Heltec_WSL3_repeater_bridge_espnow | ESP32 | SX1262 | 1171765 | 1171765 | +0 | +0 | +| Heltec_WSL3_repeater_bridge_rs232 | ESP32 | SX1262 | 1163785 | 1163785 | +0 | +0 | +| Heltec_WSL3_room_server | ESP32 | SX1262 | 1156821 | 1156821 | +0 | +0 | +| Heltec_WSL3_sensor | ESP32 | SX1262 | 1168233 | 1168233 | +0 | +0 | +| Heltec_Wireless_Paper_companion_radio_ble | ESP32 | SX1262 | 1616301 | 1616301 | +0 | +0 | +| Heltec_Wireless_Paper_companion_radio_usb | ESP32 | SX1262 | 626549 | 626545 | -4 | +0 | +| Heltec_Wireless_Paper_kiss_modem | ESP32 | SX1262 | 517461 | 517461 | +0 | +0 | +| Heltec_Wireless_Paper_repeater | ESP32 | SX1262 | 1121873 | 1121873 | +0 | +0 | +| Heltec_Wireless_Paper_repeater_bridge_espnow | ESP32 | SX1262 | 1132325 | 1132325 | +0 | +0 | +| Heltec_Wireless_Paper_room_server | ESP32 | SX1262 | 1117125 | 1117125 | +0 | +0 | +| Heltec_Wireless_Tracker_companion_radio_ble | ESP32 | SX1262 | 1266077 | 1266081 | +4 | +0 | +| Heltec_Wireless_Tracker_companion_radio_usb | ESP32 | SX1262 | 662693 | 662697 | +4 | +0 | +| Heltec_Wireless_Tracker_kiss_modem | ESP32 | SX1262 | 526709 | 526709 | +0 | +0 | +| Heltec_Wireless_Tracker_repeater | ESP32 | SX1262 | 1157621 | 1157621 | +0 | +0 | +| Heltec_Wireless_Tracker_repeater_bridge_espnow | ESP32 | SX1262 | 1168109 | 1168109 | +0 | +0 | +| Heltec_Wireless_Tracker_room_server | ESP32 | SX1262 | 1152893 | 1152893 | +0 | +0 | +| Heltec_ct62_companion_radio_ble | ESP32 | SX1262 | 1277060 | 1277060 | +0 | +0 | +| Heltec_ct62_companion_radio_usb | ESP32 | SX1262 | 564990 | 564990 | +0 | +0 | +| Heltec_ct62_kiss_modem | ESP32 | SX1262 | 497028 | 497028 | +0 | +0 | +| Heltec_ct62_repeater | ESP32 | SX1262 | 1151874 | 1151874 | +0 | +0 | +| Heltec_ct62_sensor | ESP32 | SX1262 | 1157740 | 1157740 | +0 | +0 | +| Heltec_mesh_solar_companion_radio_ble | nRF52 | SX1262 | 375004 | 374956 | -48 | +0 | +| Heltec_mesh_solar_companion_radio_usb | nRF52 | SX1262 | 370004 | 369956 | -48 | +0 | +| Heltec_mesh_solar_kiss_modem | nRF52 | SX1262 | 309024 | 308976 | -48 | +0 | +| Heltec_mesh_solar_repeater | nRF52 | SX1262 | 365600 | 365536 | -64 | +0 | +| Heltec_mesh_solar_room_server | nRF52 | SX1262 | 359072 | 359008 | -64 | +0 | +| Heltec_t096_companion_radio_ble | nRF52 | SX1262 | 434516 | 434452 | -64 | +0 | +| Heltec_t096_companion_radio_usb | nRF52 | SX1262 | 427780 | 427732 | -48 | +0 | +| Heltec_t096_kiss_modem | nRF52 | SX1262 | 340776 | 340728 | -48 | +0 | +| Heltec_t096_repeater | nRF52 | SX1262 | 414464 | 414416 | -48 | +0 | +| Heltec_t096_repeater_bridge_rs232 | nRF52 | SX1262 | 416736 | 416688 | -48 | +0 | +| Heltec_t096_room_server | nRF52 | SX1262 | 407584 | 407520 | -64 | +0 | +| Heltec_t096_sensor | nRF52 | SX1262 | 411348 | 411300 | -48 | +0 | +| Heltec_t114_companion_radio_ble | nRF52 | SX1262 | 418584 | 418536 | -48 | +0 | +| Heltec_t114_companion_radio_usb | nRF52 | SX1262 | 411848 | 411800 | -48 | +0 | +| Heltec_t114_kiss_modem | nRF52 | SX1262 | 311268 | 311204 | -64 | +0 | +| Heltec_t114_repeater | nRF52 | SX1262 | 398460 | 398396 | -64 | +0 | +| Heltec_t114_repeater_bridge_rs232 | nRF52 | SX1262 | 368532 | 368468 | -64 | +0 | +| Heltec_t114_room_server | nRF52 | SX1262 | 391580 | 391516 | -64 | +0 | +| Heltec_t114_without_display_companion_radio_ble | nRF52 | SX1262 | 392832 | 392784 | -48 | +0 | +| Heltec_t114_without_display_companion_radio_usb | nRF52 | SX1262 | 386000 | 385952 | -48 | +0 | +| Heltec_t114_without_display_repeater | nRF52 | SX1262 | 366260 | 366196 | -64 | +0 | +| Heltec_t114_without_display_repeater_bridge_rs232 | nRF52 | SX1262 | 368532 | 368468 | -64 | +0 | +| Heltec_t114_without_display_room_server | nRF52 | SX1262 | 359740 | 359676 | -64 | +0 | +| Heltec_t1_companion_radio_ble | nRF52 | SX1262 | 436016 | 435952 | -64 | +0 | +| Heltec_t1_companion_radio_usb | nRF52 | SX1262 | 429312 | 429248 | -64 | +0 | +| Heltec_t1_kiss_modem | nRF52 | SX1262 | 339884 | 339836 | -48 | +0 | +| Heltec_t1_repeater | nRF52 | SX1262 | 413508 | 413460 | -48 | +0 | +| Heltec_t1_room_server | nRF52 | SX1262 | 406644 | 406596 | -48 | +0 | +| Heltec_tower_v2_companion_radio_ble | nRF52 | SX1262 | 335884 | 335836 | -48 | +0 | +| Heltec_tower_v2_companion_radio_usb | nRF52 | SX1262 | 329044 | 328996 | -48 | +0 | +| Heltec_tower_v2_kiss_modem | nRF52 | SX1262 | 251864 | 251800 | -64 | +0 | +| Heltec_tower_v2_repeater | nRF52 | SX1262 | 309528 | 309480 | -48 | +0 | +| Heltec_tower_v2_room_server | nRF52 | SX1262 | 303000 | 302952 | -48 | +0 | +| Heltec_v2_companion_radio_ble | ESP32 | SX1276 | 1431145 | 1431229 | +84 | +0 | +| Heltec_v2_companion_radio_usb | ESP32 | SX1276 | 613269 | 613349 | +80 | +0 | +| Heltec_v2_companion_radio_wifi | ESP32 | SX1276 | 1084281 | 1084365 | +84 | +0 | +| Heltec_v2_kiss_modem | ESP32 | SX1276 | 506169 | 506169 | +0 | +0 | +| Heltec_v2_repeater | ESP32 | SX1276 | 1151913 | 1151997 | +84 | +0 | +| Heltec_v2_repeater_bridge_espnow | ESP32 | SX1276 | 1162757 | 1162813 | +56 | +0 | +| Heltec_v2_room_server | ESP32 | SX1276 | 1147261 | 1147337 | +76 | +0 | +| Heltec_v2_terminal_chat | ESP32 | SX1276 | 534429 | 534429 | +0 | +0 | +| Heltec_v3_companion_radio_ble | ESP32 | SX1262 | 1671657 | 1671653 | -4 | +0 | +| Heltec_v3_companion_radio_usb | ESP32 | SX1262 | 658197 | 658201 | +4 | +0 | +| Heltec_v3_companion_radio_wifi | ESP32 | SX1262 | 1107825 | 1107821 | -4 | +0 | +| Heltec_v3_kiss_modem | ESP32 | SX1262 | 581253 | 581253 | +0 | +0 | +| Heltec_v3_repeater | ESP32 | SX1262 | 1175025 | 1175025 | +0 | +0 | +| Heltec_v3_repeater_bridge_espnow | ESP32 | SX1262 | 1185493 | 1185493 | +0 | +0 | +| Heltec_v3_repeater_bridge_rs232 | ESP32 | SX1262 | 1177493 | 1177493 | +0 | +0 | +| Heltec_v3_room_server | ESP32 | SX1262 | 1170173 | 1170173 | +0 | +0 | +| Heltec_v3_sensor | ESP32 | SX1262 | 1181561 | 1181561 | +0 | +0 | +| Heltec_v3_terminal_chat | ESP32 | SX1262 | 602381 | 602337 | -44 | +0 | +| KeepteenLT1_companion_radio_ble | nRF52 | SX1262 | 353648 | 353600 | -48 | +0 | +| KeepteenLT1_companion_radio_usb | nRF52 | SX1262 | 335720 | 335656 | -64 | +0 | +| KeepteenLT1_kiss_modem | nRF52 | SX1262 | 248868 | 248820 | -48 | +0 | +| KeepteenLT1_repeater | nRF52 | SX1262 | 322028 | 321964 | -64 | +0 | +| KeepteenLT1_room_server | nRF52 | SX1262 | 315108 | 315044 | -64 | +0 | +| LilyGo_T-Echo-Lite_companion_radio_ble | nRF52 | SX1262 | 357416 | 357352 | -64 | +0 | +| LilyGo_T-Echo-Lite_companion_radio_usb | nRF52 | SX1262 | 352512 | 352448 | -64 | +0 | +| LilyGo_T-Echo-Lite_kiss_modem | nRF52 | SX1262 | 276892 | 276844 | -48 | +0 | +| LilyGo_T-Echo-Lite_non_shell_companion_radio_ble | nRF52 | SX1262 | 316504 | 316440 | -64 | +0 | +| LilyGo_T-Echo-Lite_non_shell_companion_radio_usb | nRF52 | SX1262 | 311360 | 311312 | -48 | +0 | +| LilyGo_T-Echo-Lite_repeater | nRF52 | SX1262 | 335772 | 335708 | -64 | +0 | +| LilyGo_T-Echo-Lite_room_server | nRF52 | SX1262 | 328964 | 328900 | -64 | +0 | +| LilyGo_T-Echo_Card_companion_radio_ble | nRF52 | SX1262 | 350208 | 350160 | -48 | +0 | +| LilyGo_T-Echo_Card_companion_radio_usb | nRF52 | SX1262 | 344448 | 344400 | -48 | +0 | +| LilyGo_T-Echo_Card_kiss_modem | nRF52 | SX1262 | 261536 | 261472 | -64 | +0 | +| LilyGo_T-Echo_Card_repeater | nRF52 | SX1262 | 322368 | 322304 | -64 | +0 | +| LilyGo_T-Echo_Card_room_server | nRF52 | SX1262 | 314112 | 314064 | -48 | +0 | +| LilyGo_T-Echo_companion_radio_ble | nRF52 | SX1262 | 376640 | 376592 | -48 | +0 | +| LilyGo_T-Echo_companion_radio_usb | nRF52 | SX1262 | 369248 | 369200 | -48 | +0 | +| LilyGo_T-Echo_kiss_modem | nRF52 | SX1262 | 291496 | 291432 | -64 | +0 | +| LilyGo_T-Echo_repeater | nRF52 | SX1262 | 350096 | 350032 | -64 | +0 | +| LilyGo_T-Echo_room_server | nRF52 | SX1262 | 343288 | 343240 | -48 | +0 | +| LilyGo_T3S3_sx1262_companion_radio_ble | ESP32 | SX1262 | 1217077 | 1217069 | -8 | +0 | +| LilyGo_T3S3_sx1262_companion_radio_usb | ESP32 | SX1262 | 591581 | 591577 | -4 | +0 | +| LilyGo_T3S3_sx1262_kiss_modem | ESP32 | SX1262 | 507869 | 507869 | +0 | +0 | +| LilyGo_T3S3_sx1262_repeater | ESP32 | SX1262 | 1108533 | 1108533 | +0 | +0 | +| LilyGo_T3S3_sx1262_repeater_bridge_espnow | ESP32 | SX1262 | 1118825 | 1118825 | +0 | +0 | +| LilyGo_T3S3_sx1262_room_server | ESP32 | SX1262 | 1103801 | 1103801 | +0 | +0 | +| LilyGo_T3S3_sx1262_terminal_chat | ESP32 | SX1262 | 535785 | 535733 | -52 | +0 | +| LilyGo_T3S3_sx1276_companion_radio_ble | ESP32 | SX1276 | 1214793 | 1214869 | +76 | +0 | +| LilyGo_T3S3_sx1276_companion_radio_usb | ESP32 | SX1276 | 589469 | 589545 | +76 | +0 | +| LilyGo_T3S3_sx1276_kiss_modem | ESP32 | SX1276 | 505769 | 505769 | +0 | +0 | +| LilyGo_T3S3_sx1276_repeater | ESP32 | SX1276 | 1105521 | 1105605 | +84 | +0 | +| LilyGo_T3S3_sx1276_repeater_bridge_espnow | ESP32 | SX1276 | 1115821 | 1115905 | +84 | +0 | +| LilyGo_T3S3_sx1276_room_server | ESP32 | SX1276 | 1100725 | 1100813 | +88 | +0 | +| LilyGo_T3S3_sx1276_terminal_chat | ESP32 | SX1276 | 533621 | 533621 | +0 | +0 | +| LilyGo_TBeam_1W_companion_radio_ble | ESP32 | SX1262 | 1267529 | 1267537 | +8 | +0 | +| LilyGo_TBeam_1W_companion_radio_usb | ESP32 | SX1262 | 642565 | 642577 | +12 | +0 | +| LilyGo_TBeam_1W_companion_radio_wifi | ESP32 | SX1262 | 1088761 | 1088761 | +0 | +0 | +| LilyGo_TBeam_1W_repeater | ESP32 | SX1262 | 1154973 | 1154973 | +0 | +0 | +| LilyGo_TBeam_1W_repeater_bridge_espnow | ESP32 | SX1262 | 1165461 | 1165461 | +0 | +0 | +| LilyGo_TBeam_1W_room_server | ESP32 | SX1262 | 1150141 | 1150141 | +0 | +0 | +| LilyGo_TDeck_companion_radio_ble | ESP32 | SX1262 | 1253177 | 1253181 | +4 | +0 | +| LilyGo_TDeck_companion_radio_usb | ESP32 | SX1262 | 649749 | 649753 | +4 | +0 | +| LilyGo_TDeck_kiss_modem | ESP32 | SX1262 | 563305 | 563305 | +0 | +0 | +| LilyGo_TDeck_repeater | ESP32 | SX1262 | 1144713 | 1144713 | +0 | +0 | +| LilyGo_TETH_Elite_sx1262_companion_radio_ble | ESP32 | SX1262 | 1178633 | 1178613 | -20 | +0 | +| LilyGo_TETH_Elite_sx1262_companion_radio_usb | ESP32 | SX1262 | 553953 | 553933 | -20 | +0 | +| LilyGo_TETH_Elite_sx1262_kiss_modem | ESP32 | SX1262 | 489269 | 489269 | +0 | +0 | +| LilyGo_TETH_Elite_sx1262_repeater | ESP32 | SX1262 | 1076289 | 1076289 | +0 | +0 | +| LilyGo_TETH_Elite_sx1262_room_server | ESP32 | SX1262 | 1071653 | 1071653 | +0 | +0 | +| LilyGo_TLora_V2_1_1_6_companion_radio_ble | ESP32 | SX1276 | 1917873 | 1918181 | +308 | +0 | +| LilyGo_TLora_V2_1_1_6_companion_radio_usb | ESP32 | SX1276 | 993025 | 993389 | +364 | +0 | +| LilyGo_TLora_V2_1_1_6_companion_radio_wifi | ESP32 | SX1276 | 1520857 | 1521209 | +352 | +0 | +| LilyGo_TLora_V2_1_1_6_kiss_modem | ESP32 | SX1276 | 862433 | 862805 | +372 | +0 | +| LilyGo_TLora_V2_1_1_6_repeater | ESP32 | SX1276 | 1663989 | 1664357 | +368 | +0 | +| LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow | ESP32 | SX1276 | 1677925 | 1678253 | +328 | +0 | +| LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232 | ESP32 | SX1276 | 1669385 | 1669705 | +320 | +0 | +| LilyGo_TLora_V2_1_1_6_room_server | ESP32 | SX1276 | 1642141 | 1642473 | +332 | +0 | +| LilyGo_TLora_V2_1_1_6_terminal_chat | ESP32 | SX1276 | 907025 | 907409 | +384 | +0 | +| LilyGo_T_Impulse_Plus_companion_radio_ble | nRF52 | SX1262 | 406948 | 406900 | -48 | +0 | +| LilyGo_T_Impulse_Plus_kiss_modem | nRF52 | SX1262 | 311364 | 311300 | -64 | +0 | +| LilyGo_T_Impulse_Plus_repeater | nRF52 | SX1262 | 366508 | 366444 | -64 | +0 | +| LilyGo_Tlora_C6_companion_radio_ble_ | ESP32 | SX1262 | 1446667 | 1446667 | +0 | +0 | +| LilyGo_Tlora_C6_kiss_modem | ESP32 | SX1262 | 472809 | 472809 | +0 | +0 | +| LilyGo_Tlora_C6_repeater_ | ESP32 | SX1262 | 542447 | 542447 | +0 | +0 | +| LilyGo_Tlora_C6_room_server_ | ESP32 | SX1262 | 536981 | 536979 | -2 | +0 | +| M5Stack_Unit_C6L_companion_radio_ble | ESP32 | SX1262 | 1454227 | 1454239 | +12 | +0 | +| M5Stack_Unit_C6L_companion_radio_usb | ESP32 | SX1262 | 546345 | 546345 | +0 | +0 | +| M5Stack_Unit_C6L_kiss_modem | ESP32 | SX1262 | 480175 | 480175 | +0 | +0 | +| M5Stack_Unit_C6L_repeater | ESP32 | SX1262 | 552821 | 552821 | +0 | +0 | +| M5Stack_Unit_C6L_room_server | ESP32 | SX1262 | 547355 | 547355 | +0 | +0 | +| MeshTracker_X1_companion_radio_ble | nRF52 | LR2021 | 325484 | 325484 | +0 | +0 | +| MeshTracker_X1_companion_radio_usb | nRF52 | LR2021 | 319852 | 319836 | -16 | +0 | +| MeshTracker_X1_kiss_modem | nRF52 | LR2021 | 241380 | 241380 | +0 | +0 | +| MeshTracker_X1_repeater | nRF52 | LR2021 | 301152 | 301152 | +0 | +0 | +| MeshTracker_X1_room_server | nRF52 | LR2021 | 293652 | 293636 | -16 | +0 | +| Mesh_pocket_companion_radio_ble | nRF52 | SX1262 | 345996 | 345948 | -48 | +0 | +| Mesh_pocket_companion_radio_usb | nRF52 | SX1262 | 339196 | 339132 | -64 | +0 | +| Mesh_pocket_kiss_modem | nRF52 | SX1262 | 268408 | 268360 | -48 | +0 | +| Mesh_pocket_repeater | nRF52 | SX1262 | 326840 | 326792 | -48 | +0 | +| Mesh_pocket_room_server | nRF52 | SX1262 | 320096 | 320048 | -48 | +0 | +| Meshadventurer_sx1262_companion_radio_ble | ESP32 | SX1262 | 1442253 | 1442257 | +4 | +0 | +| Meshadventurer_sx1262_companion_radio_usb | ESP32 | SX1262 | 603441 | 603445 | +4 | +0 | +| Meshadventurer_sx1262_kiss_modem | ESP32 | SX1262 | 529933 | 529933 | +0 | +0 | +| Meshadventurer_sx1262_repeater | ESP32 | SX1262 | 1162097 | 1162097 | +0 | +0 | +| Meshadventurer_sx1262_repeater_bridge_espnow | ESP32 | SX1262 | 1172881 | 1172881 | +0 | +0 | +| Meshadventurer_sx1262_room_server | ESP32 | SX1262 | 1157353 | 1157353 | +0 | +0 | +| Meshadventurer_sx1262_terminal_chat | ESP32 | SX1262 | 558457 | 558413 | -44 | +0 | +| Meshadventurer_sx1268_companion_radio_ble | ESP32 | SX1268 | 1442029 | 1442033 | +4 | +0 | +| Meshadventurer_sx1268_companion_radio_usb | ESP32 | SX1268 | 603205 | 603213 | +8 | +0 | +| Meshadventurer_sx1268_kiss_modem | ESP32 | SX1268 | 529701 | 529701 | +0 | +0 | +| Meshadventurer_sx1268_repeater | ESP32 | SX1268 | 1161869 | 1161869 | +0 | +0 | +| Meshadventurer_sx1268_repeater_bridge_espnow | ESP32 | SX1268 | 1172661 | 1172661 | +0 | +0 | +| Meshadventurer_sx1268_room_server | ESP32 | SX1268 | 1157133 | 1157133 | +0 | +0 | +| Meshadventurer_sx1268_terminal_chat | ESP32 | SX1268 | 558233 | 558185 | -48 | +0 | +| Meshimi_companion_radio_ble_ | ESP32 | SX1262 | 1446739 | 1446739 | +0 | +0 | +| Meshimi_repeater_ | ESP32 | SX1262 | 542535 | 542535 | +0 | +0 | +| Meshtiny_companion_radio_ble | nRF52 | SX1262 | 338512 | 338464 | -48 | +0 | +| Meshtiny_companion_radio_usb | nRF52 | SX1262 | 331112 | 331064 | -48 | +0 | +| Meshtiny_kiss_modem | nRF52 | SX1262 | 243268 | 243220 | -48 | +0 | +| Minewsemi_me25ls01_companion_radio_ble | nRF52 | LR1110 | 319852 | 319852 | +0 | +0 | +| Minewsemi_me25ls01_companion_radio_usb | nRF52 | LR1110 | 314156 | 314140 | -16 | +0 | +| Minewsemi_me25ls01_kiss_modem | nRF52 | LR1110 | 244700 | 244700 | +0 | +0 | +| Minewsemi_me25ls01_repeater | nRF52 | LR1110 | 306976 | 306976 | +0 | +0 | +| Minewsemi_me25ls01_room_server | nRF52 | LR1110 | 298820 | 298820 | +0 | +0 | +| Minewsemi_me25ls01_terminal_chat | nRF52 | LR1110 | 271188 | 271188 | +0 | +0 | +| Nano_G2_Ultra_companion_radio_ble | nRF52 | SX1262 | 346684 | 346636 | -48 | +0 | +| Nano_G2_Ultra_companion_radio_usb | nRF52 | SX1262 | 339276 | 339212 | -64 | +0 | +| Nano_G2_Ultra_kiss_modem | nRF52 | SX1262 | 249476 | 249428 | -48 | +0 | +| Nano_G2_Ultra_repeater | nRF52 | SX1262 | 319500 | 319452 | -48 | +0 | +| Nano_G2_Ultra_room_server | nRF52 | SX1262 | 312580 | 312516 | -64 | +0 | +| PicoW_companion_radio | RP2040 | SX1262 | 667496 | 667496 | +0 | +0 | +| PicoW_companion_radio_ble | RP2040 | SX1262 | 635332 | 635332 | +0 | +0 | +| PicoW_companion_radio_usb | RP2040 | SX1262 | 497012 | 497012 | +0 | +0 | +| PicoW_companion_radio_wifi | RP2040 | SX1262 | 530428 | 530428 | +0 | +0 | +| PicoW_kiss_modem | RP2040 | SX1262 | 439572 | 439572 | +0 | +0 | +| PicoW_repeater | RP2040 | SX1262 | 492668 | 492668 | +0 | +0 | +| PicoW_room_server | RP2040 | SX1262 | 488612 | 488612 | +0 | +0 | +| PicoW_terminal_chat | RP2040 | SX1262 | 469148 | 469148 | +0 | +0 | +| ProMicro_companion_radio_ble | nRF52 | SX1262 | 368804 | 368756 | -48 | +0 | +| ProMicro_companion_radio_usb | nRF52 | SX1262 | 359796 | 359732 | -64 | +0 | +| ProMicro_kiss_modem | nRF52 | SX1262 | 273400 | 273336 | -64 | +0 | +| ProMicro_repeater | nRF52 | SX1262 | 346240 | 346176 | -64 | +0 | +| ProMicro_repeater_bridge_rs232_serial1 | nRF52 | SX1262 | 342544 | 342496 | -48 | +0 | +| ProMicro_room_server | nRF52 | SX1262 | 339288 | 339224 | -64 | +0 | +| ProMicro_sensor | nRF52 | SX1262 | 342956 | 342892 | -64 | +0 | +| ProMicro_terminal_chat | nRF52 | SX1262 | 298384 | 298320 | -64 | +0 | +| R1Neo_companion_radio_ble | nRF52 | SX1262 | 386296 | 386248 | -48 | +0 | +| R1Neo_companion_radio_usb | nRF52 | SX1262 | 378888 | 378840 | -48 | +0 | +| R1Neo_kiss_modem | nRF52 | SX1262 | 312020 | 311956 | -64 | +0 | +| R1Neo_repeater | nRF52 | SX1262 | 368916 | 368852 | -64 | +0 | +| R1Neo_room_server | nRF52 | SX1262 | 361420 | 361356 | -64 | +0 | +| R1Neo_sensor | nRF52 | SX1262 | 365104 | 365040 | -64 | +0 | +| R1Neo_terminal_chat | nRF52 | SX1262 | 334572 | 334524 | -48 | +0 | +| RAK_11310_companion_radio_usb | RP2040 | SX1262 | 240532 | 240532 | +0 | +0 | +| RAK_11310_kiss_modem | RP2040 | SX1262 | 183084 | 183084 | +0 | +0 | +| RAK_11310_repeater | RP2040 | SX1262 | 236172 | 236172 | +0 | +0 | +| RAK_11310_repeater_bridge_rs232 | RP2040 | SX1262 | 240664 | 240664 | +0 | +0 | +| RAK_11310_room_server | RP2040 | SX1262 | 232124 | 232124 | +0 | +0 | +| RAK_11310_terminal_chat | RP2040 | SX1262 | 212668 | 212668 | +0 | +0 | +| RAK_3112_companion_radio_ble | ESP32 | SX1262 | 1654533 | 1654529 | -4 | +0 | +| RAK_3112_companion_radio_usb | ESP32 | SX1262 | 643549 | 643553 | +4 | +0 | +| RAK_3112_companion_radio_wifi | ESP32 | SX1262 | 1092901 | 1092897 | -4 | +0 | +| RAK_3112_kiss_modem | ESP32 | SX1262 | 586073 | 586073 | +0 | +0 | +| RAK_3112_repeater | ESP32 | SX1262 | 1165829 | 1165829 | +0 | +0 | +| RAK_3112_repeater_bridge_espnow | ESP32 | SX1262 | 1176333 | 1176333 | +0 | +0 | +| RAK_3112_repeater_bridge_rs232 | ESP32 | SX1262 | 1168329 | 1168329 | +0 | +0 | +| RAK_3112_room_server | ESP32 | SX1262 | 1161341 | 1161341 | +0 | +0 | +| RAK_3112_sensor | ESP32 | SX1262 | 1172777 | 1172777 | +0 | +0 | +| RAK_3112_terminal_chat | ESP32 | SX1262 | 607193 | 607145 | -48 | +0 | +| RAK_3401_companion_radio_ble | nRF52 | SX1262 | 433916 | 433868 | -48 | +0 | +| RAK_3401_companion_radio_usb | nRF52 | SX1262 | 424908 | 424860 | -48 | +0 | +| RAK_3401_kiss_modem | nRF52 | SX1262 | 340928 | 340880 | -48 | +0 | +| RAK_3401_repeater | nRF52 | SX1262 | 411040 | 410992 | -48 | +0 | +| RAK_3401_room_server | nRF52 | SX1262 | 404192 | 404144 | -48 | +0 | +| RAK_3401_sensor | nRF52 | SX1262 | 407888 | 407824 | -64 | +0 | +| RAK_3401_terminal_chat | nRF52 | SX1262 | 363424 | 363376 | -48 | +0 | +| RAK_3x72_companion_radio_usb | STM32 | STM32WLx | 199816 | 199824 | +8 | +0 | +| RAK_3x72_kiss_modem | STM32 | STM32WLx | 143164 | 143164 | +0 | +0 | +| RAK_3x72_repeater | STM32 | STM32WLx | 196776 | 196776 | +0 | +0 | +| RAK_3x72_sensor | STM32 | STM32WLx | 196376 | 196376 | +0 | +0 | +| RAK_4631_companion_radio_ble | nRF52 | SX1262 | 465172 | 465124 | -48 | +0 | +| RAK_4631_companion_radio_ethernet | nRF52 | SX1262 | 470712 | 470664 | -48 | +0 | +| RAK_4631_companion_radio_usb | nRF52 | SX1262 | 456148 | 456100 | -48 | +0 | +| RAK_4631_kiss_modem | nRF52 | SX1262 | 371976 | 371928 | -48 | +0 | +| RAK_4631_repeater | nRF52 | SX1262 | 441944 | 441880 | -64 | +0 | +| RAK_4631_repeater_bridge_rs232_serial1 | nRF52 | SX1262 | 408052 | 407988 | -64 | +0 | +| RAK_4631_repeater_bridge_rs232_serial2 | nRF52 | SX1262 | 408052 | 407988 | -64 | +0 | +| RAK_4631_repeater_ethernet | nRF52 | SX1262 | 459588 | 459540 | -48 | +0 | +| RAK_4631_room_server | nRF52 | SX1262 | 435096 | 435032 | -64 | +0 | +| RAK_4631_room_server_ethernet | nRF52 | SX1262 | 452732 | 452684 | -48 | +0 | +| RAK_4631_sensor | nRF52 | SX1262 | 438776 | 438712 | -64 | +0 | +| RAK_4631_terminal_chat | nRF52 | SX1262 | 394376 | 394328 | -48 | +0 | +| RAK_WisMesh_Tag_companion_radio_ble | nRF52 | SX1262 | 386600 | 386552 | -48 | +0 | +| RAK_WisMesh_Tag_companion_radio_usb | nRF52 | SX1262 | 379176 | 379128 | -48 | +0 | +| RAK_WisMesh_Tag_kiss_modem | nRF52 | SX1262 | 312180 | 312116 | -64 | +0 | +| RAK_WisMesh_Tag_repeater | nRF52 | SX1262 | 369076 | 369012 | -64 | +0 | +| RAK_WisMesh_Tag_room_server | nRF52 | SX1262 | 361548 | 361500 | -48 | +0 | +| RAK_WisMesh_Tag_sensor | nRF52 | SX1262 | 365232 | 365184 | -48 | +0 | +| SenseCap_Solar_companion_radio_ble | nRF52 | SX1262 | 375040 | 374976 | -64 | +0 | +| SenseCap_Solar_companion_radio_usb | nRF52 | SX1262 | 369904 | 369840 | -64 | +0 | +| SenseCap_Solar_kiss_modem | nRF52 | SX1262 | 311028 | 310980 | -48 | +0 | +| SenseCap_Solar_repeater | nRF52 | SX1262 | 366108 | 366044 | -64 | +0 | +| SenseCap_Solar_room_server | nRF52 | SX1262 | 359372 | 359324 | -48 | +0 | +| Station_G2_companion_radio_usb | ESP32 | SX1262 | 715897 | 715897 | +0 | +0 | +| Station_G2_companion_radio_wifi | ESP32 | SX1262 | 1141001 | 1141005 | +4 | +0 | +| Station_G2_kiss_modem | ESP32 | SX1262 | 598649 | 598649 | +0 | +0 | +| Station_G2_logging_repeater | ESP32 | SX1262 | 1207909 | 1207909 | +0 | +0 | +| Station_G2_logging_repeater_bridge_espnow | ESP32 | SX1262 | 1218365 | 1218365 | +0 | +0 | +| Station_G2_repeater | ESP32 | SX1262 | 1207885 | 1207885 | +0 | +0 | +| Station_G2_repeater_bridge_espnow | ESP32 | SX1262 | 1218341 | 1218341 | +0 | +0 | +| Station_G2_room_server | ESP32 | SX1262 | 1203069 | 1203069 | +0 | +0 | +| Station_G3_ESP32_companion_radio_usb | ESP32 | SX1262 | 717525 | 717525 | +0 | +0 | +| Station_G3_ESP32_companion_radio_wifi | ESP32 | SX1262 | 1142589 | 1142593 | +4 | +0 | +| Station_G3_ESP32_logging_repeater | ESP32 | SX1262 | 1209509 | 1209509 | +0 | +0 | +| Station_G3_ESP32_repeater | ESP32 | SX1262 | 1209469 | 1209469 | +0 | +0 | +| Station_G3_ESP32_room_server | ESP32 | SX1262 | 1204681 | 1204681 | +0 | +0 | +| T_Beam_S3_Supreme_SX1262_companion_radio_ble | ESP32 | SX1262 | 1264157 | 1264161 | +4 | +0 | +| T_Beam_S3_Supreme_SX1262_companion_radio_wifi | ESP32 | SX1262 | 1085749 | 1085745 | -4 | +0 | +| T_Beam_S3_Supreme_SX1262_kiss_modem | ESP32 | SX1262 | 570321 | 570321 | +0 | +0 | +| T_Beam_S3_Supreme_SX1262_repeater | ESP32 | SX1262 | 1155517 | 1155517 | +0 | +0 | +| T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow | ESP32 | SX1262 | 1166001 | 1166001 | +0 | +0 | +| T_Beam_S3_Supreme_SX1262_room_server | ESP32 | SX1262 | 1150717 | 1150717 | +0 | +0 | +| Tbeam_SX1262_companion_radio_ble | ESP32 | SX1262 | 1485265 | 1485273 | +8 | +0 | +| Tbeam_SX1262_kiss_modem | ESP32 | SX1262 | 567437 | 567437 | +0 | +0 | +| Tbeam_SX1262_repeater | ESP32 | SX1262 | 1205213 | 1205213 | +0 | +0 | +| Tbeam_SX1262_repeater_bridge_espnow | ESP32 | SX1262 | 1216165 | 1216165 | +0 | +0 | +| Tbeam_SX1262_room_server | ESP32 | SX1262 | 1200065 | 1200065 | +0 | +0 | +| Tbeam_SX1276_companion_radio_ble | ESP32 | SX1276 | 1481229 | 1481313 | +84 | +0 | +| Tbeam_SX1276_kiss_modem | ESP32 | SX1276 | 563485 | 563485 | +0 | +0 | +| Tbeam_SX1276_repeater | ESP32 | SX1276 | 1201181 | 1201325 | +144 | +0 | +| Tbeam_SX1276_repeater_bridge_espnow | ESP32 | SX1276 | 1212213 | 1212285 | +72 | +0 | +| Tbeam_SX1276_room_server | ESP32 | SX1276 | 1196049 | 1196157 | +108 | +0 | +| Tenstar_C3_sx1262_kiss_modem | ESP32 | SX1262 | 497480 | 497480 | +0 | +0 | +| Tenstar_C3_sx1262_repeater | ESP32 | SX1262 | 1152392 | 1152392 | +0 | +0 | +| Tenstar_C3_sx1268_kiss_modem | ESP32 | SX1268 | 497412 | 497412 | +0 | +0 | +| Tenstar_C3_sx1268_repeater | ESP32 | SX1268 | 1152294 | 1152294 | +0 | +0 | +| ThinkNode_M1_companion_radio_ble | nRF52 | SX1262 | 365532 | 365484 | -48 | +0 | +| ThinkNode_M1_companion_radio_usb | nRF52 | SX1262 | 354748 | 354684 | -64 | +0 | +| ThinkNode_M1_kiss_modem | nRF52 | SX1262 | 249444 | 249396 | -48 | +0 | +| ThinkNode_M1_repeater | nRF52 | SX1262 | 306180 | 306116 | -64 | +0 | +| ThinkNode_M1_room_server | nRF52 | SX1262 | 299636 | 299572 | -64 | +0 | +| ThinkNode_M2_Repeater | ESP32 | SX1262 | 1110181 | 1110181 | +0 | +0 | +| ThinkNode_M2_Repeater_bridge_espnow | ESP32 | SX1262 | 1120509 | 1120509 | +0 | +0 | +| ThinkNode_M2_companion_radio_ble | ESP32 | SX1262 | 1220049 | 1220045 | -4 | +0 | +| ThinkNode_M2_companion_radio_serial | ESP32 | SX1262 | 617201 | 617197 | -4 | +0 | +| ThinkNode_M2_companion_radio_usb | ESP32 | SX1262 | 617125 | 617121 | -4 | +0 | +| ThinkNode_M2_companion_radio_wifi | ESP32 | SX1262 | 1045021 | 1045021 | +0 | +0 | +| ThinkNode_M2_kiss_modem | ESP32 | SX1262 | 520617 | 520617 | +0 | +0 | +| ThinkNode_M2_room_server | ESP32 | SX1262 | 1105477 | 1105477 | +0 | +0 | +| ThinkNode_M2_terminal_chat | ESP32 | SX1262 | 548569 | 548517 | -52 | +0 | +| ThinkNode_M3_companion_radio_ble | nRF52 | LR1110 | 320304 | 320288 | -16 | +0 | +| ThinkNode_M3_companion_radio_usb | nRF52 | LR1110 | 314584 | 314584 | +0 | +0 | +| ThinkNode_M3_kiss_modem | nRF52 | LR1110 | 244580 | 244580 | +0 | +0 | +| ThinkNode_M3_repeater | nRF52 | LR1110 | 302260 | 302260 | +0 | +0 | +| ThinkNode_M3_room_server | nRF52 | LR1110 | 295732 | 295732 | +0 | +0 | +| ThinkNode_M4_companion_radio_ble | nRF52 | LR1110 | 320688 | 320672 | -16 | +0 | +| ThinkNode_M4_companion_radio_usb | nRF52 | LR1110 | 314968 | 314968 | +0 | +0 | +| ThinkNode_M4_kiss_modem | nRF52 | LR1110 | 244564 | 244564 | +0 | +0 | +| ThinkNode_M4_repeater | nRF52 | LR1110 | 302268 | 302252 | -16 | +0 | +| ThinkNode_M4_room_server | nRF52 | LR1110 | 295716 | 295716 | +0 | +0 | +| ThinkNode_M5_Repeater | ESP32 | SX1262 | 1127465 | 1127465 | +0 | +0 | +| ThinkNode_M5_Repeater_bridge_espnow | ESP32 | SX1262 | 1137969 | 1137969 | +0 | +0 | +| ThinkNode_M5_companion_radio_ble | ESP32 | SX1262 | 1237889 | 1237901 | +12 | +0 | +| ThinkNode_M5_companion_radio_usb | ESP32 | SX1262 | 634917 | 634925 | +8 | +0 | +| ThinkNode_M5_companion_radio_wifi | ESP32 | SX1262 | 1062609 | 1062609 | +0 | +0 | +| ThinkNode_M5_kiss_modem | ESP32 | SX1262 | 538413 | 538413 | +0 | +0 | +| ThinkNode_M5_room_server | ESP32 | SX1262 | 1122813 | 1122813 | +0 | +0 | +| ThinkNode_M5_terminal_chat | ESP32 | SX1262 | 566685 | 566629 | -56 | +0 | +| ThinkNode_M6_companion_radio_ble | nRF52 | SX1262 | 377424 | 377360 | -64 | +0 | +| ThinkNode_M6_companion_radio_usb | nRF52 | SX1262 | 369392 | 369344 | -48 | +0 | +| ThinkNode_M6_kiss_modem | nRF52 | SX1262 | 311444 | 311380 | -64 | +0 | +| ThinkNode_M6_repeater | nRF52 | SX1262 | 366212 | 366164 | -48 | +0 | +| ThinkNode_M6_room_server | nRF52 | SX1262 | 359676 | 359628 | -48 | +0 | +| ThinkNode_M7_companion_radio_ble | ESP32 | LR1110 | 1629677 | 1629681 | +4 | +0 | +| ThinkNode_M7_companion_radio_ethernet | ESP32 | LR1110 | 1062465 | 1062453 | -12 | +0 | +| ThinkNode_M7_companion_radio_usb | ESP32 | LR1110 | 589309 | 589297 | -12 | +0 | +| ThinkNode_M7_companion_radio_wifi | ESP32 | LR1110 | 1028821 | 1028825 | +4 | +0 | +| ThinkNode_M7_kiss_modem | ESP32 | LR1110 | 502001 | 502001 | +0 | +0 | +| ThinkNode_M7_repeater | ESP32 | LR1110 | 1089241 | 1089241 | +0 | +0 | +| ThinkNode_M7_room_server | ESP32 | LR1110 | 1084621 | 1084621 | +0 | +0 | +| ThinkNode_M8_companion_radio_ble | nRF52 | SX1262 | 371116 | 371052 | -64 | +0 | +| ThinkNode_M8_companion_radio_usb | nRF52 | SX1262 | 346856 | 346792 | -64 | +0 | +| ThinkNode_M9_companion_radio_ble_ | ESP32 | LR1110 | 1292989 | 1292993 | +4 | +0 | +| ThinkNode_M9_companion_radio_usb_ | ESP32 | LR1110 | 689357 | 689361 | +4 | +0 | +| ThinkNode_M9_companion_radio_wifi_ | ESP32 | LR1110 | 1117505 | 1117509 | +4 | +0 | +| ThinkNode_M9_kiss_modem | ESP32 | LR1110 | 601349 | 601349 | +0 | +0 | +| ThinkNode_M9_repeater_ | ESP32 | LR1110 | 1182977 | 1182977 | +0 | +0 | +| ThinkNode_M9_room_server_ | ESP32 | LR1110 | 1178285 | 1178285 | +0 | +0 | +| Tiny_Relay_companion_radio_usb | STM32 | STM32WLx | 199824 | 199832 | +8 | +0 | +| Tiny_Relay_kiss_modem | STM32 | STM32WLx | 143164 | 143164 | +0 | +0 | +| Tiny_Relay_repeater | STM32 | STM32WLx | 196784 | 196784 | +0 | +0 | +| Tiny_Relay_sensor | STM32 | STM32WLx | 196376 | 196376 | +0 | +0 | +| WHY2025_badge_companion_radio_ble_ | ESP32 | SX1262 | 1446639 | 1446639 | +0 | +0 | +| WHY2025_badge_repeater_ | ESP32 | SX1262 | 542435 | 542435 | +0 | +0 | +| Waveshare_S3_LR1121_companion_radio_ble | ESP32 | LR1121 | 1185873 | 1185853 | -20 | +0 | +| Waveshare_S3_LR1121_companion_radio_usb | ESP32 | LR1121 | 584221 | 584201 | -20 | +0 | +| Waveshare_S3_LR1121_kiss_modem | ESP32 | LR1121 | 497809 | 497809 | +0 | +0 | +| Waveshare_S3_LR1121_repeater | ESP32 | LR1121 | 1084869 | 1084869 | +0 | +0 | +| Waveshare_S3_LR1121_room_server | ESP32 | LR1121 | 1080313 | 1080313 | +0 | +0 | +| WioTrackerL1-1W_companion_radio_ble | nRF52 | SX1262 | 408732 | 408684 | -48 | +0 | +| WioTrackerL1-1W_companion_radio_usb | nRF52 | SX1262 | 399052 | 398988 | -64 | +0 | +| WioTrackerL1-1W_kiss_modem | nRF52 | SX1262 | 312084 | 312020 | -64 | +0 | +| WioTrackerL1-1W_repeater | nRF52 | SX1262 | 378716 | 378668 | -48 | +0 | +| WioTrackerL1-1W_room_server | nRF52 | SX1262 | 371812 | 371764 | -48 | +0 | +| WioTrackerL1Eink_companion_radio_ble | nRF52 | SX1262 | 425124 | 425076 | -48 | +0 | +| WioTrackerL1Eink_kiss_modem | nRF52 | SX1262 | 311236 | 311172 | -64 | +0 | +| WioTrackerL1_companion_radio_ble | nRF52 | SX1262 | 407676 | 407612 | -64 | +0 | +| WioTrackerL1_companion_radio_usb | nRF52 | SX1262 | 397980 | 397932 | -48 | +0 | +| WioTrackerL1_kiss_modem | nRF52 | SX1262 | 311140 | 311092 | -48 | +0 | +| WioTrackerL1_repeater | nRF52 | SX1262 | 377724 | 377660 | -64 | +0 | +| WioTrackerL1_room_server | nRF52 | SX1262 | 370804 | 370740 | -64 | +0 | +| Xiao_C3_companion_radio_ble | ESP32 | SX1262 | 1256962 | 1256962 | +0 | +0 | +| Xiao_C3_companion_radio_usb | ESP32 | SX1262 | 544902 | 544902 | +0 | +0 | +| Xiao_C3_companion_radio_wifi | ESP32 | SX1262 | 1012330 | 1012328 | -2 | +0 | +| Xiao_C3_kiss_modem | ESP32 | SX1262 | 473692 | 473692 | +0 | +0 | +| Xiao_C3_repeater | ESP32 | SX1262 | 1158046 | 1158046 | +0 | +0 | +| Xiao_C3_room_server | ESP32 | SX1262 | 1152902 | 1152902 | +0 | +0 | +| Xiao_C6_companion_radio_ble_ | ESP32 | SX1262 | 1446683 | 1446683 | +0 | +0 | +| Xiao_C6_kiss_modem | ESP32 | SX1262 | 472835 | 472835 | +0 | +0 | +| Xiao_C6_repeater_ | ESP32 | SX1262 | 542479 | 542479 | +0 | +0 | +| Xiao_S3_WIO_companion_radio_ble | ESP32 | SX1262 | 1643821 | 1643833 | +12 | +0 | +| Xiao_S3_WIO_companion_radio_serial | ESP32 | SX1262 | 672893 | 672893 | +0 | +0 | +| Xiao_S3_WIO_companion_radio_usb | ESP32 | SX1262 | 653989 | 653989 | +0 | +0 | +| Xiao_S3_WIO_companion_radio_wifi | ESP32 | SX1262 | 1055781 | 1055785 | +4 | +0 | +| Xiao_S3_WIO_kiss_modem | ESP32 | SX1262 | 549837 | 549837 | +0 | +0 | +| Xiao_S3_WIO_repeater | ESP32 | SX1262 | 1127969 | 1127969 | +0 | +0 | +| Xiao_S3_WIO_repeater_bridge_espnow | ESP32 | SX1262 | 1138525 | 1138525 | +0 | +0 | +| Xiao_S3_WIO_room_server | ESP32 | SX1262 | 1123353 | 1123353 | +0 | +0 | +| Xiao_S3_WIO_sensor | ESP32 | SX1262 | 1134745 | 1134745 | +0 | +0 | +| Xiao_S3_WIO_terminal_chat | ESP32 | SX1262 | 571045 | 570989 | -56 | +0 | +| Xiao_S3_companion_radio_ble | ESP32 | SX1262 | 1646569 | 1646565 | -4 | +0 | +| Xiao_S3_companion_radio_usb | ESP32 | SX1262 | 656689 | 656693 | +4 | +0 | +| Xiao_S3_kiss_modem | ESP32 | SX1262 | 559569 | 559569 | +0 | +0 | +| Xiao_S3_repeater | ESP32 | SX1262 | 1137605 | 1137605 | +0 | +0 | +| Xiao_S3_repeater_bridge_espnow | ESP32 | SX1262 | 1148121 | 1148121 | +0 | +0 | +| Xiao_S3_room_server | ESP32 | SX1262 | 1132949 | 1132949 | +0 | +0 | +| Xiao_S3_sensor | ESP32 | SX1262 | 1144397 | 1144397 | +0 | +0 | +| Xiao_nrf52_companion_radio_ble | nRF52 | SX1262 | 380568 | 380504 | -64 | +0 | +| Xiao_nrf52_companion_radio_usb | nRF52 | SX1262 | 374872 | 374808 | -64 | +0 | +| Xiao_nrf52_kiss_modem | nRF52 | SX1262 | 305840 | 305792 | -48 | +0 | +| Xiao_nrf52_repeater | nRF52 | SX1262 | 361800 | 361736 | -64 | +0 | +| Xiao_nrf52_room_server | nRF52 | SX1262 | 354264 | 354200 | -64 | +0 | +| Xiao_rp2040_companion_radio_usb | RP2040 | SX1262 | 240148 | 240148 | +0 | +0 | +| Xiao_rp2040_kiss_modem | RP2040 | SX1262 | 182708 | 182708 | +0 | +0 | +| Xiao_rp2040_repeater | RP2040 | SX1262 | 235796 | 235796 | +0 | +0 | +| Xiao_rp2040_room_server | RP2040 | SX1262 | 231740 | 231740 | +0 | +0 | +| Xiao_rp2040_terminal_chat | RP2040 | SX1262 | 212284 | 212284 | +0 | +0 | +| heltec_rc32_companion_radio_ble | ESP32 | SX1262 | 1281841 | 1281849 | +8 | +0 | +| heltec_rc32_companion_radio_usb | ESP32 | SX1262 | 676905 | 676917 | +12 | +0 | +| heltec_rc32_companion_radio_wifi | ESP32 | SX1262 | 1104789 | 1104789 | +0 | +0 | +| heltec_rc32_kiss_modem | ESP32 | SX1262 | 582197 | 582197 | +0 | +0 | +| heltec_rc32_repeater | ESP32 | SX1262 | 1169069 | 1169069 | +0 | +0 | +| heltec_rc32_repeater_bridge_espnow | ESP32 | SX1262 | 1179565 | 1179565 | +0 | +0 | +| heltec_rc32_room_server | ESP32 | SX1262 | 1164401 | 1164401 | +0 | +0 | +| heltec_rc32_sensor | ESP32 | SX1262 | 1175813 | 1175813 | +0 | +0 | +| heltec_rc32_terminal_chat | ESP32 | SX1262 | 603285 | 603233 | -52 | +0 | +| heltec_rc32_without_display_companion_radio_ble | ESP32 | SX1262 | 1276937 | 1276945 | +8 | +0 | +| heltec_rc32_without_display_companion_radio_usb | ESP32 | SX1262 | 671725 | 671737 | +12 | +0 | +| heltec_rc32_without_display_companion_radio_wifi | ESP32 | SX1262 | 1099669 | 1099669 | +0 | +0 | +| heltec_rc32_without_display_repeater | ESP32 | SX1262 | 1162169 | 1162169 | +0 | +0 | +| heltec_rc32_without_display_repeater_bridge_espnow | ESP32 | SX1262 | 1172545 | 1172545 | +0 | +0 | +| heltec_rc32_without_display_room_server | ESP32 | SX1262 | 1157585 | 1157585 | +0 | +0 | +| heltec_rc32_without_display_sensor | ESP32 | SX1262 | 1169053 | 1169053 | +0 | +0 | +| heltec_tracker_v2_companion_radio_ble | ESP32 | SX1262 | 1339277 | 1339285 | +8 | +0 | +| heltec_tracker_v2_companion_radio_usb | ESP32 | SX1262 | 712585 | 712597 | +12 | +0 | +| heltec_tracker_v2_companion_radio_wifi | ESP32 | SX1262 | 1159269 | 1159269 | +0 | +0 | +| heltec_tracker_v2_repeater | ESP32 | SX1262 | 1226257 | 1226257 | +0 | +0 | +| heltec_tracker_v2_repeater_bridge_espnow | ESP32 | SX1262 | 1236713 | 1236713 | +0 | +0 | +| heltec_tracker_v2_room_server | ESP32 | SX1262 | 1221505 | 1221505 | +0 | +0 | +| heltec_tracker_v2_sensor | ESP32 | SX1262 | 1232881 | 1232881 | +0 | +0 | +| heltec_tracker_v2_terminal_chat | ESP32 | SX1262 | 628193 | 628133 | -60 | +0 | +| heltec_v4_companion_radio_ble | ESP32 | SX1262 | 1669997 | 1669997 | +0 | +0 | +| heltec_v4_companion_radio_usb | ESP32 | SX1262 | 656597 | 656609 | +12 | +0 | +| heltec_v4_companion_radio_wifi | ESP32 | SX1262 | 1106165 | 1106165 | +0 | +0 | +| heltec_v4_expansionkit_repeater | ESP32 | SX1262 | 1173189 | 1173189 | +0 | +0 | +| heltec_v4_kiss_modem | ESP32 | SX1262 | 579645 | 579645 | +0 | +0 | +| heltec_v4_r8_companion_radio_ble | ESP32 | SX1262 | 1673481 | 1673481 | +0 | +0 | +| heltec_v4_r8_companion_radio_usb | ESP32 | SX1262 | 660077 | 660089 | +12 | +0 | +| heltec_v4_r8_companion_radio_wifi | ESP32 | SX1262 | 1109629 | 1109629 | +0 | +0 | +| heltec_v4_r8_kiss_modem | ESP32 | SX1262 | 583089 | 583089 | +0 | +0 | +| heltec_v4_r8_repeater | ESP32 | SX1262 | 1176629 | 1176629 | +0 | +0 | +| heltec_v4_r8_room_server | ESP32 | SX1262 | 1171913 | 1171913 | +0 | +0 | +| heltec_v4_r8_sensor | ESP32 | SX1262 | 1183245 | 1183245 | +0 | +0 | +| heltec_v4_r8_terminal_chat | ESP32 | SX1262 | 604189 | 604137 | -52 | +0 | +| heltec_v4_r8_tft_companion_radio_ble | ESP32 | SX1262 | 1290425 | 1290433 | +8 | +0 | +| heltec_v4_r8_tft_companion_radio_usb | ESP32 | SX1262 | 663853 | 663865 | +12 | +0 | +| heltec_v4_r8_tft_companion_radio_wifi | ESP32 | SX1262 | 1113457 | 1113457 | +0 | +0 | +| heltec_v4_r8_tft_kiss_modem | ESP32 | SX1262 | 583481 | 583481 | +0 | +0 | +| heltec_v4_r8_tft_repeater | ESP32 | SX1262 | 1178717 | 1178717 | +0 | +0 | +| heltec_v4_r8_tft_room_server | ESP32 | SX1262 | 1173901 | 1173901 | +0 | +0 | +| heltec_v4_r8_tft_sensor | ESP32 | SX1262 | 1185277 | 1185277 | +0 | +0 | +| heltec_v4_r8_tft_terminal_chat | ESP32 | SX1262 | 604577 | 604525 | -52 | +0 | +| heltec_v4_repeater | ESP32 | SX1262 | 1173137 | 1173137 | +0 | +0 | +| heltec_v4_repeater_bridge_espnow | ESP32 | SX1262 | 1183673 | 1183673 | +0 | +0 | +| heltec_v4_room_server | ESP32 | SX1262 | 1168421 | 1168421 | +0 | +0 | +| heltec_v4_sensor | ESP32 | SX1262 | 1179785 | 1179785 | +0 | +0 | +| heltec_v4_terminal_chat | ESP32 | SX1262 | 600737 | 600681 | -56 | +0 | +| heltec_v4_tft_companion_radio_ble | ESP32 | SX1262 | 1284809 | 1284817 | +8 | +0 | +| heltec_v4_tft_companion_radio_usb | ESP32 | SX1262 | 658273 | 658285 | +12 | +0 | +| heltec_v4_tft_companion_radio_wifi | ESP32 | SX1262 | 1107773 | 1107773 | +0 | +0 | +| heltec_v4_tft_repeater | ESP32 | SX1262 | 1174849 | 1174849 | +0 | +0 | +| heltec_v4_tft_repeater_bridge_espnow | ESP32 | SX1262 | 1185325 | 1185325 | +0 | +0 | +| heltec_v4_tft_room_server | ESP32 | SX1262 | 1170009 | 1170009 | +0 | +0 | +| heltec_v4_tft_sensor | ESP32 | SX1262 | 1181413 | 1181413 | +0 | +0 | +| heltec_v4_tft_terminal_chat | ESP32 | SX1262 | 600737 | 600681 | -56 | +0 | +| ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble | nRF52 | SX1262 | 398560 | 398496 | -64 | +0 | +| ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb | nRF52 | SX1262 | 391168 | 391120 | -48 | +0 | +| ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble | nRF52 | SX1262 | 398608 | 398560 | -48 | +0 | +| ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb | nRF52 | SX1262 | 391232 | 391168 | -64 | +0 | +| ikoka_handheld_nrf_e22_30dbm_repeater | nRF52 | SX1262 | 358296 | 358248 | -48 | +0 | +| ikoka_handheld_nrf_e22_30dbm_room_server | nRF52 | SX1262 | 351752 | 351688 | -64 | +0 | +| ikoka_handheld_nrf_kiss_modem | nRF52 | SX1262 | 304272 | 304224 | -48 | +0 | +| ikoka_nano_nrf_22dbm_companion_radio_ble | nRF52 | SX1262 | 387712 | 387648 | -64 | +0 | +| ikoka_nano_nrf_22dbm_companion_radio_usb | nRF52 | SX1262 | 380768 | 380704 | -64 | +0 | +| ikoka_nano_nrf_22dbm_kiss_modem | nRF52 | SX1262 | 305264 | 305216 | -48 | +0 | +| ikoka_nano_nrf_22dbm_repeater | nRF52 | SX1262 | 361176 | 361128 | -48 | +0 | +| ikoka_nano_nrf_22dbm_room_server | nRF52 | SX1262 | 353720 | 353656 | -64 | +0 | +| ikoka_nano_nrf_30dbm_companion_radio_ble | nRF52 | SX1262 | 387712 | 387648 | -64 | +0 | +| ikoka_nano_nrf_30dbm_companion_radio_usb | nRF52 | SX1262 | 380768 | 380704 | -64 | +0 | +| ikoka_nano_nrf_30dbm_kiss_modem | nRF52 | SX1262 | 305264 | 305216 | -48 | +0 | +| ikoka_nano_nrf_30dbm_repeater | nRF52 | SX1262 | 361176 | 361128 | -48 | +0 | +| ikoka_nano_nrf_30dbm_room_server | nRF52 | SX1262 | 353720 | 353656 | -64 | +0 | +| ikoka_nano_nrf_33dbm_companion_radio_ble | nRF52 | SX1262 | 387712 | 387648 | -64 | +0 | +| ikoka_nano_nrf_33dbm_companion_radio_usb | nRF52 | SX1262 | 380768 | 380704 | -64 | +0 | +| ikoka_nano_nrf_33dbm_kiss_modem | nRF52 | SX1262 | 305264 | 305216 | -48 | +0 | +| ikoka_nano_nrf_33dbm_repeater | nRF52 | SX1262 | 361176 | 361128 | -48 | +0 | +| ikoka_nano_nrf_33dbm_room_server | nRF52 | SX1262 | 353720 | 353656 | -64 | +0 | +| ikoka_stick_nrf_22dbm_companion_radio_ble | nRF52 | SX1262 | 398624 | 398560 | -64 | +0 | +| ikoka_stick_nrf_22dbm_companion_radio_usb | nRF52 | SX1262 | 391824 | 391776 | -48 | +0 | +| ikoka_stick_nrf_22dbm_kiss_modem | nRF52 | SX1262 | 318112 | 318048 | -64 | +0 | +| ikoka_stick_nrf_22dbm_repeater | nRF52 | SX1262 | 373768 | 373720 | -48 | +0 | +| ikoka_stick_nrf_22dbm_room_server | nRF52 | SX1262 | 366816 | 366768 | -48 | +0 | +| ikoka_stick_nrf_30dbm_companion_radio_ble | nRF52 | SX1262 | 398624 | 398560 | -64 | +0 | +| ikoka_stick_nrf_30dbm_companion_radio_usb | nRF52 | SX1262 | 391824 | 391776 | -48 | +0 | +| ikoka_stick_nrf_30dbm_kiss_modem | nRF52 | SX1262 | 318112 | 318048 | -64 | +0 | +| ikoka_stick_nrf_30dbm_repeater | nRF52 | SX1262 | 373768 | 373720 | -48 | +0 | +| ikoka_stick_nrf_30dbm_room_server | nRF52 | SX1262 | 366816 | 366768 | -48 | +0 | +| ikoka_stick_nrf_33dbm_companion_radio_ble | nRF52 | SX1262 | 398624 | 398560 | -64 | +0 | +| ikoka_stick_nrf_33dbm_companion_radio_usb | nRF52 | SX1262 | 391824 | 391776 | -48 | +0 | +| ikoka_stick_nrf_33dbm_kiss_modem | nRF52 | SX1262 | 318112 | 318048 | -64 | +0 | +| ikoka_stick_nrf_33dbm_repeater | nRF52 | SX1262 | 373768 | 373720 | -48 | +0 | +| ikoka_stick_nrf_33dbm_room_server | nRF52 | SX1262 | 366816 | 366768 | -48 | +0 | +| meshnology_w12_companion_radio_ble | ESP32 | LR2021 | 1291233 | 1291233 | +0 | +0 | +| meshnology_w12_companion_radio_usb | ESP32 | LR2021 | 666013 | 666013 | +0 | +0 | +| meshnology_w12_companion_radio_wifi | ESP32 | LR2021 | 1113313 | 1113313 | +0 | +0 | +| meshnology_w12_kiss_modem | ESP32 | LR2021 | 589353 | 589425 | +72 | +0 | +| meshnology_w12_repeater | ESP32 | LR2021 | 1183449 | 1183449 | +0 | +0 | +| meshnology_w12_repeater_bridge_espnow | ESP32 | LR2021 | 1193929 | 1193929 | +0 | +0 | +| meshnology_w12_room_server | ESP32 | LR2021 | 1178341 | 1178341 | +0 | +0 | +| meshnology_w12_sensor | ESP32 | LR2021 | 1189749 | 1189749 | +0 | +0 | +| meshnology_w12_terminal_chat | ESP32 | LR2021 | 610585 | 610521 | -64 | +0 | +| muzi_base_duo_companion_radio_ble | nRF52 | LR1121 | 327124 | 327124 | +0 | +0 | +| muzi_base_duo_companion_radio_usb | nRF52 | LR1121 | 322084 | 322084 | +0 | +0 | +| muzi_base_duo_repeater | nRF52 | LR1121 | 297316 | 297316 | +0 | +0 | +| muzi_base_duo_room_server | nRF52 | LR1121 | 290820 | 290804 | -16 | +0 | +| muzi_base_duo_superIO_companion_radio_ble | nRF52 | LR1121 | 346168 | 346168 | +0 | +0 | +| muzi_base_uno_companion_radio_ble | nRF52 | SX1262 | 331148 | 331100 | -48 | +0 | +| muzi_base_uno_companion_radio_usb | nRF52 | SX1262 | 326124 | 326060 | -64 | +0 | +| muzi_base_uno_repeater | nRF52 | SX1262 | 301236 | 301172 | -64 | +0 | +| muzi_base_uno_room_server | nRF52 | SX1262 | 294724 | 294660 | -64 | +0 | +| muzi_base_uno_superIO_companion_radio_ble | nRF52 | SX1262 | 350208 | 350160 | -48 | +0 | +| nibble_screen_connect_companion_radio_ble_ | ESP32 | SX1262 | 1211705 | 1211689 | -16 | +0 | +| nibble_screen_connect_companion_radio_usb_ | ESP32 | SX1262 | 586329 | 586313 | -16 | +0 | +| nibble_screen_connect_companion_radio_wifi_ | ESP32 | SX1262 | 1035369 | 1035365 | -4 | +0 | +| nibble_screen_connect_kiss_modem_ | ESP32 | SX1262 | 495625 | 495625 | +0 | +0 | +| nibble_screen_connect_repeater_ | ESP32 | SX1262 | 1103205 | 1103205 | +0 | +0 | +| nibble_screen_connect_repeater_bridge_espnow_ | ESP32 | SX1262 | 1113529 | 1113529 | +0 | +0 | +| nibble_screen_connect_room_server_ | ESP32 | SX1262 | 1098497 | 1098497 | +0 | +0 | +| nibble_screen_connect_terminal_chat_ | ESP32 | SX1262 | 523457 | 523405 | -52 | +0 | +| nibble_zero_connect_companion_radio_ble_ | ESP32 | SX1262 | 1211937 | 1211921 | -16 | +0 | +| nibble_zero_connect_companion_radio_usb_ | ESP32 | SX1262 | 586537 | 586521 | -16 | +0 | +| nibble_zero_connect_companion_radio_wifi_ | ESP32 | SX1262 | 1035601 | 1035597 | -4 | +0 | +| nibble_zero_connect_repeater_ | ESP32 | SX1262 | 1103309 | 1103309 | +0 | +0 | +| nibble_zero_connect_repeater_bridge_espnow_ | ESP32 | SX1262 | 1113613 | 1113613 | +0 | +0 | +| nibble_zero_connect_room_server_ | ESP32 | SX1262 | 1098589 | 1098589 | +0 | +0 | +| nibble_zero_connect_terminal_chat_ | ESP32 | SX1262 | 523537 | 523485 | -52 | +0 | +| t1000e_companion_radio_ble | nRF52 | LR1110 | 311644 | 311644 | +0 | +0 | +| t1000e_companion_radio_usb | nRF52 | LR1110 | 306020 | 306020 | +0 | +0 | +| t1000e_kiss_modem | nRF52 | LR1110 | 235436 | 235436 | +0 | +0 | +| t1000e_repeater | nRF52 | LR1110 | 294712 | 294712 | +0 | +0 | +| t1000e_room_server | nRF52 | LR1110 | 287564 | 287548 | -16 | +0 | +| waveshare_rp2040_lora_companion_radio_usb | RP2040 | SX1262 | 240940 | 240948 | +8 | +0 | +| waveshare_rp2040_lora_kiss_modem | RP2040 | SX1262 | 183500 | 183500 | +0 | +0 | +| waveshare_rp2040_lora_repeater | RP2040 | SX1262 | 236596 | 236596 | +0 | +0 | +| waveshare_rp2040_lora_repeater_bridge_rs232 | RP2040 | SX1262 | 241080 | 241080 | +0 | +0 | +| waveshare_rp2040_lora_room_server | RP2040 | SX1262 | 232548 | 232548 | +0 | +0 | +| waveshare_rp2040_lora_terminal_chat | RP2040 | SX1262 | 213084 | 213084 | +0 | +0 | +| wio-e5-mini_companion_radio_usb | STM32 | STM32WLx | 211296 | 211296 | +0 | +0 | +| wio-e5-mini_kiss_modem | STM32 | STM32WLx | 151208 | 151208 | +0 | +0 | +| wio-e5-mini_repeater | STM32 | STM32WLx | 204136 | 204136 | +0 | +0 | +| wio-e5-mini_sensor | STM32 | STM32WLx | 203664 | 203664 | +0 | +0 | +| wio-e5-repeater_bridge_rs232 | STM32 | STM32WLx | 199064 | 199064 | +0 | +0 | +| wio-e5_companion_radio_usb | STM32 | STM32WLx | 199968 | 199968 | +0 | +0 | +| wio-e5_kiss_modem | STM32 | STM32WLx | 143248 | 143248 | +0 | +0 | +| wio-e5_repeater | STM32 | STM32WLx | 196848 | 196848 | +0 | +0 | +| wio_wm1110_companion_radio_ble | nRF52 | LR1110 | 358792 | 358792 | +0 | +0 | +| wio_wm1110_kiss_modem | nRF52 | LR1110 | 289992 | 289992 | +0 | +0 | +| wio_wm1110_repeater | nRF52 | LR1110 | 345388 | 345388 | +0 | +0 | +| wio_wm1110_room_server | nRF52 | LR1110 | 338224 | 338224 | +0 | +0 | + +
+ +### What was not verified on hardware + +No board test for 9c. Every wrapper makes the same chip calls in the same order +as before (host suite, 80 cases, 16 mutations), and the firmware differences +are explained above down to the symbol. LLCC68 has no firmware environment and +is covered by the host suite only. diff --git a/docs/refactoring/0010-companion-command-table.md b/docs/refactoring/0010-companion-command-table.md index d2842db9388..98caeebf3fa 100644 --- a/docs/refactoring/0010-companion-command-table.md +++ b/docs/refactoring/0010-companion-command-table.md @@ -911,7 +911,9 @@ Each needs its own fix and proposal; 10c leaves the code as it is. `tools/refactoring/companion_asan_probe.patch`, run it with `PROBE_SKIP=1`, and `companion_asan_probe_summary.py` lists `MyMesh::cmdSendRawPacket` with `mesh::Dispatcher::tryParsePacket` for code 65, fills `00` and `3f`, lengths 4 - to 6 (see `tools/refactoring/README.md`). + to 6 (see `tools/refactoring/README.md`). Fixed in + `docs/fixes/packet-parse-len.md`, together with the same pattern in + `Packet::readFrom()`; after it the probe no longer lists code 65. 2. **The CLI parser compares prefixes past the terminator.** `CommonRadioPrefs::handleCommand()`, `MyMesh::handleCommand()` and `isGet()` use `memcmp()` with prefixes longer than the command text. The result is decided at diff --git a/docs/refactoring/0011-variant-target.md b/docs/refactoring/0011-variant-target.md index b163c540967..94fd7d3406e 100644 --- a/docs/refactoring/0011-variant-target.md +++ b/docs/refactoring/0011-variant-target.md @@ -3,7 +3,9 @@ Status: cut A (one shared `radio_new_identity()`) and cut B (one shared FEM RX gain handler) implemented, each its own pull request, B stacked on A. Cut C (one default `radio_init()` for 31 nRF52 variants) implemented, its own pull request, on top of -A and B. Cuts D and E are planned and need their own review before code. Behaviour change: none. +A and B. Cut D (the radio, wrapper and RTC globals) is implemented as two pull requests, +D1 (31 nRF52 variants) and D2 (22 ESP32 variants) on top of it, see "Cut D". Cut E is +planned. Behaviour change: none. ## Context @@ -87,7 +89,7 @@ weak" below). | A | one `radio_new_identity()` for all RadioLib variants | 87 `target.cpp`, 1 new source, 1 header | implemented, see "Cut A" | | B | one class for the FEM gain handlers of HeltecV4, HeltecV4R8, T096, HeltecTrackerV2 | 4 board classes, 1 new helper | implemented, see "Cut B" | | C | a shared default `radio_init()` for the bodies that need no new description (28 + 3 of 89, all nRF52) | 31 `target.cpp`, 1 source, 1 header | implemented, see "Cut C" | -| D | the radio, wrapper and RTC globals from the board's build flags | about 87 `target.cpp` | planned (description: build flags, decided) | +| D | the radio, wrapper and RTC globals from the board description (D1: 31 nRF52, D2: 22 ESP32 variants) | 53 `target.cpp` and `target.h`, 1 source | D1 and D2 implemented, see "Cut D" | | E | sensors, display and button from the board description | the rest | planned | Cut B in more detail: the four bodies are the same once the class name is replaced, @@ -1033,6 +1035,599 @@ definition. If a reviewer wants a board run, the check on any of the 31: flash t repeater firmware, see it boot past `radio_init()` (no `Radio init failed`), and `get radio` answers with the stored settings. +## Cut D: the radio, wrapper and RTC globals from the board description + +The design below was written and committed before any code of this cut. + +### Inventory of the globals + +Measured on `e0d05fde` (`integration`, cuts A to C, 9b and 10c merged) with +`tools/refactoring/variant_target_globals.py` (in this cut). For every +`target.cpp` it takes the "radio block": the declarations of the SPI object, +`radio`, `radio_driver`, `fallback_clock` and `rtc_clock` and the preprocessor +lines around them, compares the blocks normalized and groups them, with the +environments and platforms of each group from `pio project config`. 89 +`target.cpp`, 606 firmware environments build one of them. + +| variants | envs | block (normalized, `PINS` = `P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY`) | platform | +|---:|---:|---|---| +| 30 | 185 | `radio(new Module(PINS, SPI))`, Volatile fallback, AutoDiscover | nRF52 | +| 20 | 175 | `#if P_LORA_SCLK`: `static SPIClass spi;` and `Module(PINS, spi)`, else `Module(PINS)`; ESP32 fallback, AutoDiscover | ESP32 | +| 8 | 61 | as the 20, without the `#if` (`P_LORA_SCLK` is defined in all their environments) | ESP32 | +| 4 | 34 | as the 20, with `spi(FSPI)` | ESP32 | +| 4 | 20 | `Module(PINS, SPI)`, `VolatileRTCClock rtc_clock` without fallback (LR11x0, LR2021) | nRF52 | +| 3 | 23 | `#if P_LORA_SCLK`: `Module(PINS, SPI)`, else `Module(PINS)`; Volatile fallback | nRF52 | +| 3 | 20 | as the 20, with the SX1276 pins `P_LORA_DIO_0, ..., P_LORA_DIO_1` | ESP32 | +| 3 | 20 | `Module(PINS, SPI1)`, Volatile fallback | RP2040 | +| 4 | 16 | `STM32WLx_Module`, RF switch tables in between, `VolatileRTCClock rtc_clock` | STM32 | +| 2 | 5 | `ESP32RTCClock rtc_clock` only (ESP-NOW, no LoRa radio) | ESP32 | +| 8 | 47 | one each: `spi(HSPI)`, `spi(0)`, SX1276 pins without `#if`, `Module(PINS)` on ESP32 with `P_LORA_SCLK` (`heltec_ct62`), `Module(PINS, SPI)` on ESP32 (`thinknode_m9`), `Module(PINS)` on RP2040, and two nRF52 blocks with sensors or a display declared in between (`mesh_pocket`, `muziworks_r1_neo`) | mixed | + +So the block is not one text but a few rules, and almost all of it follows from +macros the variants already have: + +- **Pins**: SX1276 (`USE_SX1276`, defined by `CustomSX1276Wrapper.h`) takes + `P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1` after `P_LORA_NSS`, every other chip + `P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY`. +- **Fallback clock**: `ESP32RTCClock` on ESP32, `VolatileRTCClock` elsewhere, and + `AutoDiscoverRTCClock rtc_clock(fallback_clock)` on top. +- **SPI**: nRF52 passes `SPI`; ESP32 has its own `static SPIClass spi` exactly when + `P_LORA_SCLK` is defined and passes nothing otherwise. The three nRF52 blocks with + `#if P_LORA_SCLK` are the `SPI` rule: `P_LORA_SCLK` is defined in all 23 of their + environments (20 by build flag, `heltec_t1` in its `variant.h`). +- **Not derivable**: the SPI host of the ESP32 bus (`FSPI` in 4, `0` in + `lilygo_tlora_c6`; `HSPI` in `lilygo_teth_elite` is the `SPIClass` default). + +"`Module(PINS)`" and "`Module(PINS, SPI)`" are not the same thing, even where +RadioLib's default bus is `SPI`: the first makes RadioLib call `SPI.begin()` itself +(`ArduinoHal()` sets `initInterface`), the second leaves it to the caller +(`ArduinoHal(spi, settings)`). The rules keep exactly the constructor each variant +calls. + +### Which variants can take their globals from shared code + +A variant can drop its block only if nothing else in its `target.cpp` names +`radio`, `spi` or `fallback_clock`, since those then live in another translation +unit (the script checks this, after removing a `radio_init()` body that a shared +default can take over): + +- **31 nRF52 variants, 195 environments**: exactly the 31 of cut C, whose + `radio_init()` is already the shared one. Their blocks are the "30" and "3" + groups above minus `muzi_base`, `thinknode_m3`, `thinknode_m4` (own + `radio_init()`), plus `muziworks_r1_neo`. +- **22 ESP32 variants, 195 environments**: the 18 and 5 `radio_init()` groups that + cut C left for this cut, minus `lilygo_teth_elite` (its `target.cpp` has four + more functions on `radio`). Their two `radio_init()` bodies are one function: + the 5 call `std_init(&spi)` without the `#if`, and `P_LORA_SCLK` is defined in + all their environments, so with the `#if` they take the same branch. +- **34 stay as they are**: their own code names the globals (LR11x0 and STM32WL + setup, RP2040 on `SPI1`, the ESP32 bodies with `spi.begin()`, `pinMode()` or + extra calls, `mesh_pocket` and the nRF52 LR variants, `heltec_ct62`, + `thinknode_m9`). Sharing their globals would mean adding `extern` declarations + of `radio` to their `target.cpp`, which is not "deletions only"; that is left to + a later cut, or to a board that is touched anyway. + +### Why the globals cannot be weak + +Cuts A and C shared functions with weak definitions, so a variant that keeps its +own still links. Objects with a constructor cannot be shared that way. A weak +object is still constructed by the initializer of its translation unit, and that +initializer writes to the symbol the linker resolved, which is the variant's +object. Measured: a host program with a weak `Obj radio_driver(1)` in one file and +a strong `Obj radio_driver(2)` in another runs the constructor twice, at the same +address (`Obj ctor #1 (1) at 0x100db000c`, `Obj ctor #2 (2) at 0x100db000c`). With +`arm-none-eabi-g++` the weak file's initializer is guarded only by a guard variable +of its own (`_ZGV12radio_driver`), which the strong file never sets, so the same +happens on the firmware toolchain. For `radio` that would mean a second +`new Module`, for `spi` a second mutex, in every variant that keeps its block. + +So the variant has to say that it takes the shared globals. That is the one new +description macro every variant of this cut gets. + +### What changes + +- `RADIOLIB_TARGET_GLOBALS` (new), defined in the variant's `target.h`: the + variant takes `radio`, `radio_driver`, `fallback_clock`, `rtc_clock` and, on + ESP32 with `P_LORA_SCLK`, `spi` from `RadioLibTarget.cpp`. In `target.h` because + both translation units read it (the variant's and the shared one). +- `P_LORA_SPI_HOST` (new, optional): the host of the ESP32 radio's own SPI bus, + for the 5 variants that do not use the `SPIClass` default. +- `RadioLibTarget.cpp` defines, under `WRAPPER_CLASS` and `RADIOLIB_TARGET_GLOBALS`, + in the order the variants have them: + ```cpp + #if defined(ESP32) && defined(P_LORA_SCLK) + static SPIClass spi; // or spi(P_LORA_SPI_HOST) + RADIO_CLASS radio = new Module(PINS, spi); + #elif defined(NRF52_PLATFORM) + RADIO_CLASS radio = new Module(PINS, SPI); + #else + RADIO_CLASS radio = new Module(PINS); + #endif + WRAPPER_CLASS radio_driver(radio, board); + FALLBACK_CLOCK fallback_clock; // ESP32RTCClock or VolatileRTCClock + AutoDiscoverRTCClock rtc_clock(fallback_clock); + ``` + `radio` and `spi` keep internal linkage (`static`), as `spi` had: no new name + becomes visible to the rest of the firmware. `fallback_clock` keeps the external + linkage it has today, because `UnitC6LBoard.cpp` and `XiaoC6Board.cpp` define one + of their own and are not touched (they have no `target.h` with the macro). +- For ESP32, a weak default `radio_init()` next to cut C's, under `ESP32` and + `RADIOLIB_TARGET_GLOBALS`: `fallback_clock.begin(); rtc_clock.begin(Wire);` then + `std_init(&spi)` with `P_LORA_SCLK`, else `std_init()`. It uses the shared `radio` + directly, no accessor. +- In each variant: `target.cpp` loses its block (and on ESP32 its `radio_init()`), + deletions only; `target.h` gains the macro line (and `P_LORA_SPI_HOST` where + needed), additions only. + +Initialization order: the five objects are now constructed by the shared file's +initializer instead of the variant's, so their order relative to the variant's +other globals (`board`, `sensors`, `display`, `user_btn`) is the link order instead +of the source order. Among the five the order stays the same. None of the +constructors involved reads another global: `RadioLibWrapper` and +`AutoDiscoverRTCClock` store references, `new Module` allocates, `SPIClass` +allocates a mutex, `VolatileRTCClock` reads `millis()`, which is 0 before the +scheduler starts on nRF52 (`tick2ms(xTaskGetTickCount())`), and the variant's +objects that use the globals take their address (`MicroNMEALocationProvider(Serial1, +&rtc_clock)`). What can change is the order of heap allocations at start-up, so +heap addresses, not anything a user, an app or the mesh sees. The pull requests +show this by reading the constructors of every global the 53 `target.cpp` keep, +and by comparing images (see "Verification plan"). + +The constructors of the moved globals, read in the sources the builds use +(RadioLib at `6d893483`, the libraries PlatformIO installed for the environments; +arduino-esp32 3.20017 for ESP32, 3.1.3 and 3.3.9 for the C6 builds): none of them +reads another global object. They store their arguments, allocate, or read a clock. + +| global | constructor chain | what it does | source | +|---|---|---|---| +| `radio` (`new Module(...)`, 4 pins) | `Module(cs, irq, rst, gpio)` | stores the pins, `new ArduinoHal()`, which stores the address of RadioLib's default bus (`spi(&RADIOLIB_DEFAULT_SPI)`) and constants | RadioLib `src/Module.cpp:10`, `src/hal/Arduino/ArduinoHal.cpp:5`, `src/Hal.cpp:5` | +| `radio` (`new Module(..., SPI)` or `spi`) | `Module(cs, irq, rst, gpio, SPIClass&, SPISettings)` | stores the pins, `new ArduinoHal(spi, settings)`, which stores the bus's address and the settings | `src/Module.cpp:14`, `src/hal/Arduino/ArduinoHal.cpp:7` | +| `radio` (SX1262, SX1268) | `CustomSX126x(Module*)`, `SX1262`/`SX1268(Module*)`, `SX126x(Module*)`, `PhysicalLayer()` | sets members (chip type, step size, `mod`) | `src/helpers/radiolib/CustomSX126x.h:16`, RadioLib `src/modules/SX126x/SX1262.cpp:44`, `SX1268.cpp:44`, `SX126x.cpp:6`, `src/protocols/PhysicalLayer/PhysicalLayer.cpp:5` | +| `radio` (SX1276) | `CustomSX1276(Module*)`, `SX1276`, `SX1278`, `SX127x(Module*)`, `PhysicalLayer()` | sets members | `src/helpers/radiolib/CustomSX1276.h:13`, RadioLib `src/modules/SX127x/SX1276.cpp:4`, `SX1278.cpp:5`, `SX127x.cpp:5` | +| `radio_driver` | `CustomSX1262Wrapper`, `CustomSX1268Wrapper`, `CustomSX1276Wrapper(radio, board)`, `RadioLibWrapper(PhysicalLayer&, MainBoard&)` | stores the addresses of `radio` and `board`, zeroes counters | `src/helpers/radiolib/CustomSX1262Wrapper.h:13`, `CustomSX1268Wrapper.h:13`, `CustomSX1276Wrapper.h:12`, `RadioLibWrappers.h:34` | +| `fallback_clock` (nRF52) | `VolatileRTCClock()` | sets a fixed base time and reads `millis()`, which is `tick2ms(xTaskGetTickCount())` and 0 before the scheduler starts, in either order | `src/helpers/ArduinoHelpers.h:11`, Adafruit nRF52 core `cores/nRF5/delay.c:28` | +| `fallback_clock` (ESP32) | `ESP32RTCClock()` | nothing | `src/helpers/ESP32Board.h:210` | +| `rtc_clock` | `AutoDiscoverRTCClock(RTCClock&)` | stores the address of `fallback_clock` | `src/helpers/AutoDiscoverRTCClock.h:12` | +| `spi` (ESP32) | `SPIClass(uint8_t spi_bus)` | sets members, creates its parameter mutex (`xSemaphoreCreateMutex()`), which allocates | arduino-esp32 `libraries/SPI/src/SPI.cpp:34` (3.20017), `:38` (3.1.3, 3.3.9) | + +What the order can change is therefore the order of the heap allocations +(`new Module`, `new ArduinoHal`, the mutex) relative to the variant's other +globals, so heap addresses. The builds bound the image differences to that: see +"D1: builds" and "D2: builds" (`nm` and `objdump`, `SOURCE_DATE_EPOCH` fixed, same +worktree). + +### Two pull requests + +- **D1, nRF52**: the globals of the 31 variants of cut C. No new `radio_init()` + (cut C's stays as it is), no `P_LORA_SPI_HOST`. 31 `target.cpp` (deletions), 31 + `target.h` (one line each), the shared file. +- **D2, ESP32**: the globals and the weak `radio_init()` of the 22 variants, + `P_LORA_SPI_HOST` for 5 of them. Stacked on D1. Globals and `radio_init()` go + together because the body names `spi`, which must be in the same translation + unit. + +Each is one concern per platform, with one platform's toolchain and boards to +verify, and each is about the size of cut C. + +### Alternatives considered + +- **Weak globals, like cuts A and C.** Rejected: constructed twice where a variant + keeps its own (measured above). +- **A header the variant includes in place of its block** + (`#include ` in `target.cpp`). Keeps + the translation unit and the initialization order, so the variant's object would + stay the same. Rejected: the shared `radio_init()` of D2 lives in another + translation unit and needs `spi`, which would stay file local in the variant, + and `RadioLibTarget.cpp` cannot see whether a variant included the header, so a + macro in `target.h` would be needed anyway. Two lines per variant instead of one, + and the block is still written out in every variant's source. +- **Give `radio` external linkage and declare it in a shared header**, so that the + 34 variants with own code could take the shared globals as well. Rejected: it + makes `radio` part of the interface every role sees, which cut A deliberately + avoided, and it adds lines to 34 `target.cpp`. +- **Opt-in by build flag in `platformio.ini` instead of `target.h`.** Not chosen: + equally one line, but a variant's environments are spread over several sections + in some `platformio.ini`, and `target.h` is the one file per variant both + translation units read. +- **A constexpr struct per board, or a generator** (options 2 and 3). Rejected by + the decision above. + +### Verification plan + +- Host tests in `native_variant_target`: a first commit pins the variants' block + and ESP32 body (copied verbatim into a test variant) with recording stand-ins: + which `Module` constructor was called with which pins and which bus, that + `radio_driver` wraps `radio` and `board`, that `rtc_clock` falls back to + `fallback_clock`, and for ESP32 the order clock, RTC, `std_init(&spi)` or + `std_init()`. The code commit runs the same cases against the shared definitions, + in a native environment per rule (nRF52; ESP32 with and without `P_LORA_SCLK`, + with `P_LORA_SPI_HOST`; SX1276 pins). Mutation tests on the shared code (wrong + pin order, wrong bus, `SPI` passed where nothing was, wrong clock class, ESP32 + default not weak, guard without the opt-in). +- Builds from the start commit and from the code commit, in the same worktree: one + environment per edited variant, the controls of each platform (variants that + keep their block, with and without own `radio_init()`), the series' matrix of 18, + the two C6 variants without `target.cpp`. Flash and RAM per environment, `nm` for + the symbols that moved (`radio`, `spi` now local symbols of `RadioLibTarget.cpp`, + `radio_init()` weak), and an image comparison of a sample built with + `SOURCE_DATE_EPOCH` set (RadioLib writes `__DATE__`/`__TIME__` into the image + otherwise), to show that what differs is the initializer and the placement. +- All 17 host test environments green on both commits. +- Hardware only with a board from `kb device claim`, otherwise recorded as without + board. + +### Upstream collisions + +Checked on 21.10.2026 against meshcore-dev/MeshCore: the file lists of all 499 +open pull requests (100 files each by GraphQL, the 4 with more through the REST +file list), and for every pull request that touches one of the 53 `target.cpp` or +`target.h`, the changed lines of its diff matched against the block and +`radio_init()`. + +- **#1962** (nRF52 time across warm resets, updated 2026-10-12) changes + `VolatileRTCClock fallback_clock;` to `NRF52RTCClock` in 19 of the 31 D1 files. + D1 deletes that line there, so whichever lands second has to follow: after D1, + #1962 changes one line of the shared code (the nRF52 fallback class) instead of + 19 files, and the 12 variants it did not touch get the same clock unless the + shared rule keeps them apart (which would then be a new description macro, + decided in #1962's rebase, not here). +- **#3213** (ESP32-C3 SPI host, updated 2026-08-14) changes `static SPIClass spi;` + to `spi(FSPI)` in `tenstar_c3`. After D2 that is `P_LORA_SPI_HOST=FSPI` in its + `target.h`. +- **#3638** (dual radio profiles, updated 2026-10-20) constructs `heltec_v4`'s + radio with its own HAL. After D2, `heltec_v4` would have to go back to its own + block (drop the macro), which still links. +- **#2682** lists `thinknode_m8` and `wio-tracker-l1-1w` with the whole block as + added lines; for cuts B and C its head was identical to `upstream/dev` in both + files (a merge, not a change). Not checked again for this cut. +- Every other open pull request on these files changes other lines (GPS, sensors, + display; #1799, #1913, #1924, #1937, #1972, #2058, #2185, #2679, #2794, #2837, + #2882, #2927, #3051, #3141, #3222, #3289, #3307, #3331, #3346, #3374, #3527, + #3613, #3651). Those that change `target.cpp` lines next to the block conflict by + context at most; those on `target.h` add lines elsewhere in the file. +- New board pull requests carry their own block and keep linking: the shared + globals exist only where a `target.h` asks for them. + +### D1: host tests + +`native_variant_target_globals` (new) builds `RadioLibTarget.cpp` against the +target.h of an nRF52 variant (`test/mocks/radiolib_variant_globals`) with the real +`AutoDiscoverRTCClock` (its out-of-line members, which need the RTC chip libraries, +stood in by the test) and a `Module` stand-in that keeps its pins and tells its two +constructors apart. The variant stand-ins move into +`test/mocks/radiolib_variant/variant_stubs.h`. + +The first commit (`6510e4da`) has the block of the 31 variants twice, copied +verbatim: once at file scope, where a variant's `target.cpp` has it, and once in a +namespace as the reference. Six cases run on both: + +- the radio is built from a `Module`, +- with `P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY` in that order, +- with `SPI` passed (the constructor that leaves starting the bus to `std_init()`), +- `radio_driver` wraps the board, +- `rtc_clock` reads and sets the time through `fallback_clock`, +- `rtc_clock.tick()` advances `fallback_clock` by `millis()`, which is what + `VolatileRTCClock` does. + +and one more: cut C's shared `radio_init()` starts the RTC, then the radio that +`radio_driver` wraps, on `SPI`, once. + +The code commit deletes the file-scope block from the test, as from the variants, +and defines the macro in the stand-in `target.h`; the cases are unchanged and now +run against the shared globals. All 18 host environments pass on both commits: 629 +test cases each (616 on `e0d05fde`). + +Mutations of the shared globals, each of which must fail: + +| mutation | result | +|---|---| +| M1: `P_LORA_BUSY` and `P_LORA_DIO_1` swapped | 1 test fails | +| M2: no `SPI` argument (RadioLib starts the bus) | 1 test fails | +| M3: `rtc_clock` falls back to another clock | 2 tests fail | +| M4: defined without the opt-in | `native_variant_target` does not compile (pins and `board` undeclared, `rtc_clock` redefined) | +| M5: opt-in misspelt | `native_variant_target_globals` does not link (globals undefined) | +| M6: `radio_driver` built on another board | 1 test fails | + +### D1: what the code commit changes + +`a8dc76a6`: the globals in `RadioLibTarget.cpp` (with an `#error` for a platform +or chip whose description is not written yet), and in `variants/`: 31 `target.cpp`, +deletions only (215 lines, the block with the blank line after it, and the +`#if defined(P_LORA_SCLK)` around the radio in `heltec_t096`, `heltec_t1` and +`heltec_t114`); 31 `target.h`, one line each, additions only. No file uses CRLF. +`tools/refactoring/variant_target_globals.py` afterwards lists no nRF52 radio block +that could still be shared. + +### D1: builds + +The 53 environments of cut C were built from `6510e4da` (the test commit, firmware +sources as on `e0d05fde`) and from the code commit `a8dc76a6`, both in the same +worktree, with `SOURCE_DATE_EPOCH=1760000000` so that `__DATE__` and `__TIME__` are +the same on both sides. + +- 52 build on both. `ThinkNode_M8_repeater` fails on both, as since cut A. +- **Every environment outside the 31 variants has the same image.** The nRF52, + RP2040 and STM32 controls (`Mesh_pocket_repeater`, `muzi_base_uno_repeater`, + `MeshTracker_X1_repeater`, `t1000e_repeater`, `PicoW_repeater`, + `waveshare_rp2040_lora_repeater_bridge_rs232`, the two `wio-e5-mini`) are byte + for byte identical. The 8 ESP32 ones (the five `Heltec_v3`, `RAK_3112`, the two + C6) differ only in the 65 bytes of the image that hash the ELF file + (`esp_app_desc_t.app_elf_sha256` and the checksum and SHA-256 appended to the + image), which change with any byte of the ELF, debug information included + (`RadioLibTarget.cpp` has new lines); checked by comparing the images byte by byte + outside those fields. +- In the 36 environments of the 31 variants, RAM changes by -8 to +8 bytes and + flash grows by 4 to 84 bytes. Measured with `nm` (all 36, symbols compared as + name, size and binding): + - `radio` becomes a local symbol (`b` instead of `B`), same size; + `radio_driver`, `fallback_clock` and `rtc_clock` keep name, size and binding. + - The variant's static initializer (`_GLOBAL__sub_I_board`; in `xiao_nrf52`, + whose first global is the display, `_GLOBAL__sub_I_display`) loses 0xb8 to + 0xe4 bytes and `RadioLibTarget.cpp` gets one of 0xd8 or 0xdc bytes + (`_GLOBAL__sub_I_radio_driver`): the construction moved, as designed. + - `MicroNMEALocationProvider::loop()` (30 environments, an inline function + compiled in `target.cpp`), and `NanoG2UltraSensorManager::loop()`, + `ThinkNodeM1SensorManager::loop()` and `SolarSensorManager::loop()` (one each) + grow by 28 bytes: the compiler now devirtualizes the call to the clock's + `setCurrentTime()` speculatively, it compares the function pointer with + `VolatileRTCClock::setCurrentTime` and runs that function inlined, otherwise + makes the indirect call as before (`objdump`: the address of + `VolatileRTCClock::setCurrentTime` is in the functions' literal pool after the + change, not before). Same function called with the same arguments either way. + - No other symbol changes. The RAM differences of 8 bytes are alignment: every + data and bss symbol keeps its size, `radio` now sits in another object file's + `.bss`. +- No hardware evidence (without board), for the reasons given in cut C; the pool + boards of these variants are kept for other purposes. A board run would check + that the radio starts (`Radio init failed` absent) and `get radio` answers. + +
+All 53 environments + +| Environment | Flash before | Flash after | Flash delta | RAM before | RAM after | RAM delta | image | +|---|---:|---:|---:|---:|---:|---:|---| +| `GAT562_30S_Mesh_Kit_repeater` | 411,568 | 411,604 | +36 | 35,932 | 35,932 | +0 | differs (hex) | +| `GAT562_Mesh_EVB_Pro_repeater` | 365,764 | 365,816 | +52 | 35,124 | 35,124 | +0 | differs (hex) | +| `GAT562_Mesh_Tracker_Pro_repeater` | 381,276 | 381,328 | +52 | 35,492 | 35,492 | +0 | differs (hex) | +| `GAT562_Mesh_Watch13_companion_radio_ble` | 400,552 | 400,572 | +20 | 147,888 | 147,888 | +0 | differs (hex) | +| `Heltec_mesh_solar_repeater` | 365,600 | 365,668 | +68 | 34,284 | 34,292 | +8 | differs (hex) | +| `Heltec_t096_repeater` | 414,464 | 414,532 | +68 | 35,564 | 35,572 | +8 | differs (hex) | +| `Heltec_t114_without_display_repeater` | 366,260 | 366,312 | +52 | 35,140 | 35,140 | +0 | differs (hex) | +| `Heltec_t1_repeater` | 413,508 | 413,560 | +52 | 35,204 | 35,204 | +0 | differs (hex) | +| `Heltec_tower_v2_repeater` | 309,528 | 309,596 | +68 | 33,292 | 33,292 | +0 | differs (hex) | +| `Heltec_v3_companion_radio_ble` | 1,671,657 | 1,671,657 | +0 | 194,844 | 194,844 | +0 | differs (bin) | +| `Heltec_v3_companion_radio_wifi` | 1,107,825 | 1,107,825 | +0 | 172,384 | 172,384 | +0 | differs (bin) | +| `Heltec_v3_repeater` | 1,175,025 | 1,175,025 | +0 | 62,512 | 62,512 | +0 | differs (bin) | +| `Heltec_v3_room_server` | 1,170,173 | 1,170,173 | +0 | 66,200 | 66,200 | +0 | differs (bin) | +| `Heltec_v3_sensor` | 1,181,561 | 1,181,561 | +0 | 60,600 | 60,600 | +0 | differs (bin) | +| `KeepteenLT1_repeater` | 322,028 | 322,096 | +68 | 33,436 | 33,436 | +0 | differs (hex) | +| `LilyGo_T-Echo-Lite_repeater` | 335,772 | 335,792 | +20 | 37,716 | 37,716 | +0 | differs (hex) | +| `LilyGo_T-Echo_Card_repeater` | 322,368 | 322,404 | +36 | 34,004 | 34,012 | +8 | differs (hex) | +| `LilyGo_T-Echo_repeater` | 350,096 | 350,100 | +4 | 38,484 | 38,492 | +8 | differs (hex) | +| `LilyGo_T_Impulse_Plus_repeater` | 366,508 | 366,560 | +52 | 35,236 | 35,236 | +0 | differs (hex) | +| `M5Stack_Unit_C6L_repeater` | 552,821 | 552,821 | +0 | 29,752 | 29,752 | +0 | differs (bin) | +| `MeshTracker_X1_repeater` | 301,152 | 301,152 | +0 | 32,956 | 32,956 | +0 | identical | +| `Mesh_pocket_repeater` | 326,840 | 326,840 | +0 | 36,820 | 36,820 | +0 | identical | +| `Meshtiny_companion_radio_usb` | 331,112 | 331,132 | +20 | 141,364 | 141,372 | +8 | differs (hex) | +| `Nano_G2_Ultra_repeater` | 319,500 | 319,568 | +68 | 33,332 | 33,332 | +0 | differs (hex) | +| `PicoW_repeater` | 492,668 | 492,668 | +0 | 85,368 | 85,368 | +0 | identical | +| `ProMicro_companion_radio_usb` | 359,796 | 359,816 | +20 | 99,468 | 99,468 | +0 | differs (hex) | +| `ProMicro_repeater` | 346,240 | 346,276 | +36 | 33,892 | 33,900 | +8 | differs (hex) | +| `ProMicro_room_server` | 339,288 | 339,340 | +52 | 37,588 | 37,596 | +8 | differs (hex) | +| `ProMicro_sensor` | 342,956 | 343,008 | +52 | 31,988 | 31,996 | +8 | differs (hex) | +| `R1Neo_repeater` | 368,916 | 368,984 | +68 | 34,892 | 34,892 | +0 | differs (hex) | +| `RAK_3112_repeater_bridge_espnow` | 1,176,333 | 1,176,333 | +0 | 64,072 | 64,072 | +0 | differs (bin) | +| `RAK_3401_repeater` | 411,040 | 411,092 | +52 | 35,636 | 35,636 | +0 | differs (hex) | +| `RAK_4631_companion_radio_ble` | 465,172 | 465,224 | +52 | 149,768 | 149,768 | +0 | differs (hex) | +| `RAK_4631_repeater` | 441,784 | 441,820 | +36 | 37,240 | 37,240 | +0 | differs (hex) | +| `RAK_4631_room_server` | 435,096 | 435,116 | +20 | 40,928 | 40,928 | +0 | differs (hex) | +| `RAK_4631_sensor` | 438,776 | 438,812 | +36 | 35,328 | 35,328 | +0 | differs (hex) | +| `RAK_WisMesh_Tag_repeater` | 369,076 | 369,128 | +52 | 34,916 | 34,908 | -8 | differs (hex) | +| `SenseCap_Solar_repeater` | 366,108 | 366,160 | +52 | 34,876 | 34,876 | +0 | differs (hex) | +| `ThinkNode_M1_repeater` | 306,180 | 306,264 | +84 | 32,980 | 32,972 | -8 | differs (hex) | +| `ThinkNode_M6_repeater` | 366,212 | 366,280 | +68 | 34,932 | 34,932 | +0 | differs (hex) | +| `ThinkNode_M8_repeater` | FAIL | FAIL | | FAIL | FAIL | | | +| `WioTrackerL1-1W_repeater` | 378,716 | 378,768 | +52 | 35,436 | 35,436 | +0 | differs (hex) | +| `WioTrackerL1_repeater` | 377,724 | 377,760 | +36 | 35,428 | 35,428 | +0 | differs (hex) | +| `Xiao_C6_repeater_` | 542,479 | 542,479 | +0 | 29,856 | 29,856 | +0 | differs (bin) | +| `Xiao_nrf52_repeater` | 361,800 | 361,836 | +36 | 34,596 | 34,604 | +8 | differs (hex) | +| `ikoka_handheld_nrf_e22_30dbm_repeater` | 358,296 | 358,316 | +20 | 34,468 | 34,476 | +8 | differs (hex) | +| `ikoka_nano_nrf_22dbm_repeater` | 361,176 | 361,212 | +36 | 34,556 | 34,564 | +8 | differs (hex) | +| `ikoka_stick_nrf_22dbm_repeater` | 373,768 | 373,804 | +36 | 34,708 | 34,716 | +8 | differs (hex) | +| `muzi_base_uno_repeater` | 301,236 | 301,236 | +0 | 32,932 | 32,932 | +0 | identical | +| `t1000e_repeater` | 294,712 | 294,712 | +0 | 32,876 | 32,876 | +0 | identical | +| `waveshare_rp2040_lora_repeater_bridge_rs232` | 241,080 | 241,080 | +0 | 26,704 | 26,704 | +0 | identical | +| `wio-e5-mini_repeater` | 204,136 | 204,136 | +0 | 17,172 | 17,172 | +0 | identical | +| `wio-e5-mini_sensor` | 203,664 | 203,664 | +0 | 15,276 | 15,276 | +0 | identical | + +
+ +### D2: host tests + +`native_variant_target_esp32` and three siblings (new) build `RadioLibTarget.cpp` +against the target.h of an ESP32 variant (`test/mocks/radiolib_variant_esp32`, +with a stand-in `helpers/ESP32Board.h` whose `ESP32RTCClock` records `begin()`), +one environment per board description that occurs among the 22: SX1262 on an own +bus on the default host; the same without `P_LORA_SCLK`; SX1276; own bus on +`P_LORA_SPI_HOST=FSPI`. The SPI stand-in gets bus numbers, the radio stand-in tells +"the module's own bus" apart from `SPI`. + +The first commit (`02978dd3`) has, per environment, the variants' block and +`radio_init()` verbatim at file scope (heltec_v3 and the others with the plain +block, heltec_v2 for the SX1276, heltec_e213 for the own host) and once more in a +namespace as reference; where `P_LORA_SCLK` is set also the blocks written without +the `#if` (lilygo_t3s3, meshadventurer, the nibble boards, lilygo_tlora_v2_1), +the bus of lilygo_tlora_c6 (`spi(0)`) and the `radio_init()` body without the +`#if`. Seven cases run on each: + +- the radio is built from a `Module`, with the pins in the chip's order + (SX1276: `P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1`), +- on an own bus that is not `SPI`, on the variant's host or the default one, or + with no bus where `P_LORA_SCLK` is not set, +- `radio_driver` wraps the board, `rtc_clock` falls back to `fallback_clock`, +- `radio_init()` starts the fallback clock, then the RTC on `Wire`, then the radio + once on its own bus (or with none), and passes `false` through. + +The code commit deletes the file-scope block and `radio_init()` from the test and +defines the macro in the stand-in `target.h`; the cases are unchanged. +`test_variant_target_esp32_override` (new, in the first ESP32 environment) defines +its own `radio_init()` with the shared globals and checks it wins. All 22 host +environments pass: 706 test cases on the test commit, 707 on the code commit (the +override case). + +Mutations of the shared code, each of which must fail: + +| mutation | result | +|---|---| +| M1: SX1276 pins in SX126x order | 1 test fails | +| M2: `P_LORA_SPI_HOST` ignored | 1 test fails | +| M3: own bus created but not passed to the `Module` | 6 tests fail | +| M4: RTC started before the fallback clock | 4 tests fail | +| M5: fallback clock not started | 8 tests fail | +| M6: `std_init()` without the bus where there is one | 3 tests fail | +| M7: `std_init()` result dropped | 3 tests fail | +| M8: `radio_init()` not weak | the override suite does not link (`duplicate symbol 'radio_init()'`) | +| M9: `rtc_clock` falls back to another clock | 4 tests fail | +| M10: `SPI` passed where `P_LORA_SCLK` is not set | 1 test fails | + +### D2: what the code commit changes + +`67619b76`: `RadioLibTarget.cpp` describes the globals for ESP32 too (pins by chip, +own `SPIClass` with `P_LORA_SCLK`, on `P_LORA_SPI_HOST` if set, `ESP32RTCClock` +from `helpers/ESP32Board.h`) and gets the weak `radio_init()` for ESP32 with an +SX1262, SX1268 or SX1276; the D1 `#error` for SX1276 goes, since the pin order is +now described. In `variants/`: 22 `target.cpp`, deletions only (455 lines: block, +`radio_init()`, blank lines); 22 `target.h` with the opt-in line and 5 of them with +`#define P_LORA_SPI_HOST FSPI` (`heltec_e213`, `heltec_e290`, `heltec_rc32`, +`heltec_t190`) or `0` (`lilygo_tlora_c6`), additions only. `nibble_screen_connect` +uses CRLF, kept. + +`ESP32Board.h` defines `static RTC_NOINIT_ATTR uint32_t _rtc_backup_time` and +`_rtc_backup_magic` in the header, used by the inline members of `ESP32RTCClock`, +so every translation unit that uses those members gets its own copy. Moving +`fallback_clock` and its `begin()` to `RadioLibTarget.cpp` must not leave two +copies, or one that `begin()` reads and `setCurrentTime()` writes another. Checked +with `objdump -t` in all 29 edited environments: exactly one copy of each before and +after, before in `target.cpp`, after in `RadioLibTarget.cpp`, at the same address +in `.rtc_noinit` (so a time saved by the old image is read by the new one). + +Initialization order, as designed: in the 53 `target.cpp` of D1 and D2, the only +remaining globals that name a moved object are the 35 GPS providers +(`MicroNMEALocationProvider(Serial1, &rtc_clock, ...)`), which store the pointer; +the moved constructors store references (`board`, `fallback_clock`) and do not read +any other global. + +### D2: builds + +50 environments, built from `02978dd3` (the test commit, firmware sources as on +D1's code commit) and from `67619b76`, in the same worktree, with +`SOURCE_DATE_EPOCH=1760000000`: one per edited variant (22, a repeater each), the +SX1268 builds of `generic-e22` and `meshadventurer`, the other roles of +`heltec_v3` and `RAK_3112`, ESP32 variants that keep their block (`heltec_ct62`, +`thinknode_m9`, `station_g2`, `lilygo_teth_elite`, `xiao_c3`, `thinknode_m7`, +`lilygo_tbeam_supreme_SX1262`), the two C6, and the nRF52, RP2040 and STM32 +environments of the series' matrix. + +- All 50 build on both commits. +- **Every environment outside the 22 variants has the same image**: nRF52, RP2040 + and STM32 byte for byte, ESP32 and C6 outside the 65 bytes that hash the ELF (as + for D1). +- In the 29 environments of the 22 variants, flash changes by -100 to +76 bytes + (21 smaller, 3 equal, 5 larger), RAM by -16 to +8. +- To tell what in those images differs, 9 of the environments (SX1262, SX1276, + own host, C3 and C6 on RISC-V) were built a second time from `02978dd3` in + another worktree; those images equal the first build's outside the ELF hash, and + their object files were compared with the code commit's, as disassembly with + relocations and without comments: **in all 9, only `RadioLibTarget.cpp.o` and the + variant's `target.cpp.o` differ**; the other 249 to 291 object files are the same. + So any other difference in the images comes from linking. +- `nm` on all 29 (symbols as name, size and binding): `radio` becomes local, + `radio_init()` becomes weak (`W`) and comes from `RadioLibTarget.cpp`, the + construction moves from the variant's initializer to `_GLOBAL__sub_I_radio_driver`, + and the destructor registration with it (`_GLOBAL__sub_D_radio_driver`; + `SPIClass` has a destructor; in 27 of the 29, not in the C3 and C6 builds). + Besides those, up to 8 functions per environment change size; for each, the link + map says which object file its code comes from: + - from an unchanged object file (`Dispatcher.cpp`, `Mesh.cpp`, `Packet.cpp`, + `main.cpp`'s `loop()`, `AutoDiscoverRTCClock.cpp`, `ESP32Board.cpp`, + `SSD1306Display.cpp`, `RadioLibWrappers.cpp`, even the prebuilt `libesp_wifi.a`): + the code is the same before linking, the size changes with the linker's + relaxation on Xtensa and RISC-V, which picks shorter or longer instruction + forms depending on the final addresses (2 or 4 bytes on RISC-V); + - inline functions whose kept copy moved from `target.cpp.o` to + `RadioLibTarget.cpp.o` with the construction of the radio and the wrapper + (`sx126xResetAGC()`, `CustomSX126x::isReceiving()`, `CustomSX1276::std_init()`, + `ESP32RTCClock::begin()`, `CustomSX1262Wrapper::doResetAGC()` and + `isReceivingPacket()`), and `MicroNMEALocationProvider::waitFor()` and + `formatAge()`, which stay in the changed `target.cpp.o`: compared function by + function in the object files, the same instructions with the same relocation + targets; the differences are Xtensa's narrow and wide encodings of the same + instruction (`mov.n a11, a3` for `or a11, a3, a3`), alignment fill, and + offsets; + - `radio_init()` itself: the shared one equals each variant's in all 9, apart + from the relocation to `radio`, which is now a local symbol (on the C3 also the + numbering of its float constants, whose contents are the same). +- No hardware evidence (without board). A board run would check the same as for + D1, plus on an ESP32 that the time survives a reset (the RTC backup above). + +
+All 50 environments + +| Environment | Flash before | Flash after | Flash delta | RAM before | RAM after | RAM delta | image | +|---|---:|---:|---:|---:|---:|---:|---| +| `Generic_E22_sx1262_repeater` | 1,143,573 | 1,143,573 | +0 | 60,620 | 60,620 | +0 | differs (bin) | +| `Heltec_E213_repeater` | 1,162,233 | 1,162,241 | +8 | 71,932 | 71,940 | +8 | differs (bin) | +| `Heltec_E290_repeater` | 1,160,185 | 1,160,261 | +76 | 72,116 | 72,124 | +8 | differs (bin) | +| `heltec_rc32_without_display_repeater` | 1,162,169 | 1,162,121 | -48 | 62,176 | 62,176 | +0 | differs (bin) | +| `Heltec_T190_repeater_` | 1,139,397 | 1,139,373 | -24 | 71,872 | 71,872 | +0 | differs (bin) | +| `Heltec_Wireless_Tracker_repeater` | 1,157,621 | 1,157,521 | -100 | 61,088 | 61,088 | +0 | differs (bin) | +| `heltec_tracker_v2_repeater` | 1,226,257 | 1,226,209 | -48 | 74,344 | 74,352 | +8 | differs (bin) | +| `Heltec_v2_repeater` | 1,151,913 | 1,151,901 | -12 | 60,604 | 60,604 | +0 | differs (bin) | +| `Heltec_v3_repeater` | 1,175,025 | 1,174,961 | -64 | 62,512 | 62,512 | +0 | differs (bin) | +| `heltec_v4_repeater` | 1,173,137 | 1,173,089 | -48 | 62,476 | 62,484 | +8 | differs (bin) | +| `heltec_v4_r8_repeater` | 1,176,629 | 1,176,589 | -40 | 62,400 | 62,400 | +0 | differs (bin) | +| `Heltec_Wireless_Paper_repeater` | 1,121,873 | 1,121,873 | +0 | 60,376 | 60,376 | +0 | differs (bin) | +| `LilyGo_T3S3_sx1262_repeater` | 1,108,533 | 1,108,537 | +4 | 60,500 | 60,500 | +0 | differs (bin) | +| `Tbeam_SX1262_repeater` | 1,205,213 | 1,205,157 | -56 | 61,212 | 61,212 | +0 | differs (bin) | +| `Tbeam_SX1276_repeater` | 1,201,181 | 1,201,113 | -68 | 61,092 | 61,092 | +0 | differs (bin) | +| `LilyGo_Tlora_C6_repeater_` | 542,447 | 542,449 | +2 | 29,856 | 29,856 | +0 | differs (bin) | +| `LilyGo_TLora_V2_1_1_6_repeater` | 1,663,989 | 1,663,977 | -12 | 62,472 | 62,472 | +0 | differs (bin) | +| `Meshadventurer_sx1262_repeater` | 1,162,097 | 1,162,029 | -68 | 61,084 | 61,084 | +0 | differs (bin) | +| `nibble_screen_connect_repeater_` | 1,103,205 | 1,103,209 | +4 | 60,216 | 60,216 | +0 | differs (bin) | +| `nibble_zero_connect_repeater_` | 1,103,309 | 1,103,301 | -8 | 60,216 | 60,216 | +0 | differs (bin) | +| `RAK_3112_repeater` | 1,165,829 | 1,165,765 | -64 | 62,416 | 62,424 | +8 | differs (bin) | +| `Tenstar_C3_sx1262_repeater` | 1,152,392 | 1,152,378 | -14 | 53,812 | 53,796 | -16 | differs (bin) | +| `Heltec_ct62_repeater` | 1,151,874 | 1,151,874 | +0 | 53,772 | 53,772 | +0 | differs (bin) | +| `ThinkNode_M9_repeater_` | 1,182,977 | 1,182,977 | +0 | 62,144 | 62,144 | +0 | differs (bin) | +| `Station_G2_repeater` | 1,207,885 | 1,207,885 | +0 | 74,712 | 74,712 | +0 | differs (bin) | +| `LilyGo_TETH_Elite_sx1262_repeater` | 1,076,289 | 1,076,289 | +0 | 59,408 | 59,408 | +0 | differs (bin) | +| `Xiao_C3_repeater` | 1,158,046 | 1,158,046 | +0 | 54,076 | 54,076 | +0 | differs (bin) | +| `ThinkNode_M7_repeater` | 1,089,241 | 1,089,241 | +0 | 59,688 | 59,688 | +0 | differs (bin) | +| `T_Beam_S3_Supreme_SX1262_repeater` | 1,155,517 | 1,155,517 | +0 | 61,072 | 61,072 | +0 | differs (bin) | +| `RAK_4631_repeater` | 441,820 | 441,820 | +0 | 37,240 | 37,240 | +0 | identical | +| `ProMicro_repeater` | 346,276 | 346,276 | +0 | 33,900 | 33,900 | +0 | identical | +| `Heltec_v3_room_server` | 1,170,173 | 1,170,101 | -72 | 66,200 | 66,200 | +0 | differs (bin) | +| `Heltec_v3_companion_radio_ble` | 1,671,657 | 1,671,593 | -64 | 194,844 | 194,844 | +0 | differs (bin) | +| `Heltec_v3_companion_radio_wifi` | 1,107,825 | 1,107,797 | -28 | 172,384 | 172,384 | +0 | differs (bin) | +| `Heltec_v3_sensor` | 1,181,561 | 1,181,489 | -72 | 60,600 | 60,600 | +0 | differs (bin) | +| `RAK_4631_room_server` | 435,116 | 435,116 | +0 | 40,928 | 40,928 | +0 | identical | +| `RAK_4631_companion_radio_ble` | 465,224 | 465,224 | +0 | 149,768 | 149,768 | +0 | identical | +| `RAK_4631_sensor` | 438,812 | 438,812 | +0 | 35,328 | 35,328 | +0 | identical | +| `PicoW_repeater` | 492,668 | 492,668 | +0 | 85,368 | 85,368 | +0 | identical | +| `wio-e5-mini_repeater` | 204,136 | 204,136 | +0 | 17,172 | 17,172 | +0 | identical | +| `wio-e5-mini_sensor` | 203,664 | 203,664 | +0 | 15,276 | 15,276 | +0 | identical | +| `RAK_3112_repeater_bridge_espnow` | 1,176,333 | 1,176,269 | -64 | 64,072 | 64,072 | +0 | differs (bin) | +| `waveshare_rp2040_lora_repeater_bridge_rs232` | 241,080 | 241,080 | +0 | 26,704 | 26,704 | +0 | identical | +| `ProMicro_room_server` | 339,340 | 339,340 | +0 | 37,596 | 37,596 | +0 | identical | +| `ProMicro_companion_radio_usb` | 359,816 | 359,816 | +0 | 99,468 | 99,468 | +0 | identical | +| `ProMicro_sensor` | 343,008 | 343,008 | +0 | 31,996 | 31,996 | +0 | identical | +| `Xiao_C6_repeater_` | 542,479 | 542,479 | +0 | 29,856 | 29,856 | +0 | differs (bin) | +| `M5Stack_Unit_C6L_repeater` | 552,821 | 552,821 | +0 | 29,752 | 29,752 | +0 | differs (bin) | +| `Generic_E22_sx1268_repeater` | 1,143,361 | 1,143,361 | +0 | 60,620 | 60,620 | +0 | differs (bin) | +| `Meshadventurer_sx1268_repeater` | 1,161,869 | 1,161,797 | -72 | 61,084 | 61,084 | +0 | differs (bin) | + +
+ ## Upstream Searched meshcore-dev/MeshCore on 20.10.2026: text queries over all pull requests diff --git a/docs/refactoring/README.md b/docs/refactoring/README.md index 939c63c1161..973da9fa969 100644 --- a/docs/refactoring/README.md +++ b/docs/refactoring/README.md @@ -51,9 +51,9 @@ The full analysis, with file and line references for every finding, is in | 2 | Let the core read its tuning values from the radio settings instead of a getter per role | [0002](0002-core-reads-radio-prefs.md) | ready for review | | 3 | Drive the radio settings (JSON, CLI, key/value store) from one table | [0003](0003-radio-settings-table.md) | ready for review | | 4 | Share the underpinnings of repeater, room server and sensor in one base class (steps 4a to 4g; the boot sequence and the forwarding checks are the last two) | [0004](0004-shared-server-base.md), [0007](0007-boot-sequence.md), [0008](0008-forward-policy.md) | ready for review | -| 9 | One RadioLib adapter per chip family: the receive detection the seven chip classes shared as copies (9a), one template for the SX126x chip classes SX1262, SX1268 and LLCC68 (9b); 9c and 9d planned in the note | [0009](0009-radio-chip-adapter.md) | 9a and 9b ready for review | +| 9 | One RadioLib adapter per chip family: the receive detection the seven chip classes shared as copies (9a), one template for the SX126x chip classes SX1262, SX1268 and LLCC68 (9b), one wrapper template per chip family instead of a cast in every method (9c); 9d planned in the note | [0009](0009-radio-chip-adapter.md) | 9a to 9c ready for review | | 10 | A command table for the companion protocol: a host harness that pins every frame (10a), one row per command code instead of the if chain (10b), then a bounded frame reader in the handlers instead of hand counted offsets (10c); 10d proposed in the note | [0010](0010-companion-command-table.md) | 10a to 10c ready for review | -| 11 | One generic variant target built from a board description, cut A: one shared `radio_new_identity()`; cut B: one FEM RX gain handler for four Heltec boards; cut C: one default `radio_init()` for 31 nRF52 boards (cuts D and E planned in the note) | [0011](0011-variant-target.md) | cuts A, B and C ready for review | +| 11 | One generic variant target built from a board description, cut A: one shared `radio_new_identity()`; cut B: one FEM RX gain handler for four Heltec boards; cut C: one default `radio_init()` for 31 nRF52 boards; cut D: the radio globals of 31 nRF52 boards (D1) and the radio globals and `radio_init()` of 22 ESP32 boards (D2) from the board description, opt in through `RADIOLIB_TARGET_GLOBALS` (cut E planned in the note) | [0011](0011-variant-target.md) | cuts A to D ready for review | Steps are numbered after their design note, so the numbers have gaps: 5 to 8 are notes of fixes and of step 4. See the review for the reasoning behind the diff --git a/platformio.ini b/platformio.ini index 4bbc58bb5fc..aab5e14690a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -176,13 +176,18 @@ test_ignore = test_radio_wrappers test_radio_chips test_radio_chips_init + test_radio_chip_wrappers test_server_base test_sensor_role test_repeater_role test_region_map test_variant_target test_variant_target_override + test_variant_target_globals + test_variant_target_esp32 + test_variant_target_esp32_override test_companion_frames + test_packet_parse build_src_filter = -<*> +<../src/Utils.cpp> @@ -396,7 +401,9 @@ build_flags = -std=c++17 -I src -I test/mocks test_build_src = yes -test_filter = test_core_tuning +test_filter = + test_core_tuning + test_packet_parse build_src_filter = -<*> +<../src/Utils.cpp> @@ -521,6 +528,114 @@ build_src_filter = ${env:native_variant_target.build_src_filter} lib_deps = google/googletest @ 1.17.0 +[env:native_variant_target_globals] +; The radio, wrapper and RTC globals of a RadioLib variant (cut D of note 0011): +; the shared code against the target.h of a variant with the real +; AutoDiscoverRTCClock (test/mocks/radiolib_variant_globals, ahead of the other +; stand-ins). The pins and NRF52_PLATFORM are what an nRF52 variant's build +; flags and variant.h set; their values only have to differ from each other. +platform = native +test_framework = googletest +build_flags = -std=c++17 + -I test/mocks/radiolib_variant_globals + -I test/mocks/radiolib_variant + -I src + -I test/mocks + -D LORA_SF=8 + -D WRAPPER_CLASS=TestRadioWrapper + -D RADIO_CLASS=TestRadio + -D NRF52_PLATFORM + -D P_LORA_NSS=11 + -D P_LORA_DIO_1=12 + -D P_LORA_RESET=13 + -D P_LORA_BUSY=14 +test_build_src = yes +test_filter = test_variant_target_globals +build_src_filter = ${env:native_variant_target.build_src_filter} +lib_deps = + google/googletest @ 1.17.0 + +[env:native_variant_target_esp32] +; The radio globals and radio_init() of an ESP32 variant of cut D2 of note 0011, +; with an SX1262 on its own SPI bus (P_LORA_SCLK) on the default host, and a +; variant with its own radio_init() (test_variant_target_esp32_override); the +; three below are the other board descriptions that occur. ESP32 and the pins +; are what the variants' build flags set; the values only have to differ. +; ICACHE_RAM_ATTR is what the ESP32 core defines for RadioLibWrappers.cpp. +platform = native +test_framework = googletest +build_flags = -std=c++17 + -I test/mocks/radiolib_variant_esp32 + -I test/mocks/radiolib_variant + -I src + -I test/mocks + -D LORA_SF=8 + -D WRAPPER_CLASS=TestRadioWrapper + -D RADIO_CLASS=TestRadio + -D ESP32 + -D ICACHE_RAM_ATTR= + -D P_LORA_NSS=11 + -D P_LORA_DIO_1=12 + -D P_LORA_RESET=13 + -D P_LORA_BUSY=14 + -D P_LORA_DIO_0=15 + -D P_LORA_SCLK=16 +test_build_src = yes +test_filter = test_variant_target_esp32* +build_src_filter = ${env:native_variant_target.build_src_filter} +lib_deps = + google/googletest @ 1.17.0 + +[env:native_variant_target_esp32_no_sclk] +; The same without P_LORA_SCLK: no own bus, RadioLib starts the default one. +platform = native +test_framework = googletest +build_flags = -std=c++17 + -I test/mocks/radiolib_variant_esp32 + -I test/mocks/radiolib_variant + -I src + -I test/mocks + -D LORA_SF=8 + -D WRAPPER_CLASS=TestRadioWrapper + -D RADIO_CLASS=TestRadio + -D ESP32 + -D ICACHE_RAM_ATTR= + -D P_LORA_NSS=11 + -D P_LORA_DIO_1=12 + -D P_LORA_RESET=13 + -D P_LORA_BUSY=14 + -D P_LORA_DIO_0=15 +test_build_src = yes +test_filter = test_variant_target_esp32 +build_src_filter = ${env:native_variant_target.build_src_filter} +lib_deps = + google/googletest @ 1.17.0 + +[env:native_variant_target_esp32_sx1276] +; The same with an SX1276, whose Module takes DIO0 and DIO1 instead of DIO1 +; and BUSY (USE_SX1276 is what CustomSX1276Wrapper.h defines). +platform = native +test_framework = googletest +build_flags = ${env:native_variant_target_esp32.build_flags} + -D USE_SX1276 +test_build_src = yes +test_filter = test_variant_target_esp32 +build_src_filter = ${env:native_variant_target.build_src_filter} +lib_deps = + google/googletest @ 1.17.0 + +[env:native_variant_target_esp32_spi_host] +; The same with the own bus on another host than the SPIClass default. +platform = native +test_framework = googletest +build_flags = ${env:native_variant_target_esp32.build_flags} + -D P_LORA_SPI_HOST=FSPI +test_build_src = yes +test_filter = test_variant_target_esp32 +build_src_filter = ${env:native_variant_target.build_src_filter} +lib_deps = + google/googletest @ 1.17.0 + [env:native_region] ; RegionMap on the host. It reads packets and keys and writes nothing but its own ; table, so it builds against the small stubs in test/test_region_map/stubs, which are @@ -563,7 +678,7 @@ lib_deps = ; The real chip classes in src/helpers/radiolib against a host stand-in for the ; RadioLib chip classes (test/test_radio_chips/stubs, listed first so it takes the ; place of the PhysicalLayer mock), so their receive detection can be checked on -; the host. +; the host, and the chip wrappers over them, with the real RadioLibWrapper. platform = native test_framework = googletest build_flags = -std=c++17 @@ -579,7 +694,9 @@ test_build_src = yes test_filter = test_radio_chips test_radio_chips_init + test_radio_chip_wrappers build_src_filter = -<*> + +<../src/helpers/radiolib/RadioLibWrappers.cpp> lib_deps = google/googletest @ 1.17.0 diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 09761dfe9f0..2ebdd2e43d3 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -153,12 +153,19 @@ void Dispatcher::loop() { bool Dispatcher::tryParsePacket(Packet* pkt, const uint8_t* raw, int len) { int i = 0; + if (len < 1) return false; pkt->header = raw[i++]; if (pkt->getPayloadVer() > PAYLOAD_VER_1) { MESH_DEBUG_PRINTLN("%s Dispatcher::checkRecv(): unsupported packet version", getLogDateTime()); return false; } + // header, transport codes (if any) and path length byte must all be inside 'len' + if (len < (pkt->hasTransportCodes() ? 6 : 2)) { + MESH_DEBUG_PRINTLN("%s Dispatcher::checkRecv(): partial or corrupt packet received, len=%d", getLogDateTime(), len); + return false; + } + if (pkt->hasTransportCodes()) { memcpy(&pkt->transport_codes[0], &raw[i], 2); i += 2; memcpy(&pkt->transport_codes[1], &raw[i], 2); i += 2; diff --git a/src/Packet.cpp b/src/Packet.cpp index aad3e2f48ee..b3dc8eb4b80 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -64,7 +64,9 @@ uint8_t Packet::writeTo(uint8_t dest[]) const { bool Packet::readFrom(const uint8_t src[], uint8_t len) { uint8_t i = 0; + if (len < 1) return false; // bad encoding header = src[i++]; + if (len < (hasTransportCodes() ? 6 : 2)) return false; // bad encoding if (hasTransportCodes()) { memcpy(&transport_codes[0], &src[i], 2); i += 2; memcpy(&transport_codes[1], &src[i], 2); i += 2; @@ -75,9 +77,9 @@ bool Packet::readFrom(const uint8_t src[], uint8_t len) { if (!isValidPathLen(path_len)) return false; // bad encoding uint8_t bl = getPathByteLen(); + if (i + bl >= len) return false; // bad encoding: needs one payload byte at least memcpy(path, &src[i], bl); i += bl; - if (i >= len) return false; // bad encoding payload_len = len - i; if (payload_len > sizeof(payload)) return false; // bad encoding memcpy(payload, &src[i], payload_len); //i += payload_len; diff --git a/src/helpers/radiolib/CustomChipWrapper.h b/src/helpers/radiolib/CustomChipWrapper.h new file mode 100644 index 00000000000..df1f6ab371f --- /dev/null +++ b/src/helpers/radiolib/CustomChipWrapper.h @@ -0,0 +1,40 @@ +#pragma once + +#include "RadioLibWrappers.h" + +// What every chip wrapper shares: access to its chip class and the calls that +// are the same for all of them. Chip is the Custom class the wrapper +// drives; it reaches PhysicalLayer by single inheritance, so chip() is the +// radio without any pointer adjustment. +template +class CustomChipWrapper : public RadioLibWrapper { +protected: + Chip* chip() const { return static_cast(_radio); } + + // The first half of every setParams(): the four modem settings, then the + // preamble length for the new spreading factor. + void setModemParams(float freq, float bw, uint8_t sf, uint8_t cr) { + chip()->setFrequency(freq); + chip()->setSpreadingFactor(sf); + chip()->setBandwidth(bw); + chip()->setCodingRate(cr); + updatePreamble(sf); + } + + // The second half, for the chips with a receive tracker (all but SX1276): + // the deadlines of the receive detection for the new parameters. + void setRxDeadlines(float bw, uint8_t sf, uint8_t cr) { + PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); + chip()->setPreambleMillis(pm.preambleMillis); + chip()->setMaxPayloadMillis(pm.payloadMillis); + } + +public: + CustomChipWrapper(Chip& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + + bool isReceivingPacket() override { + return chip()->isReceiving(); + } + float getLastRSSI() const override { return chip()->getRSSI(); } + float getLastSNR() const override { return chip()->getSNR(); } +}; diff --git a/src/helpers/radiolib/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h index 8857c343c65..d444d51f0b4 100644 --- a/src/helpers/radiolib/CustomLLCC68Wrapper.h +++ b/src/helpers/radiolib/CustomLLCC68Wrapper.h @@ -1,42 +1,16 @@ #pragma once #include "CustomLLCC68.h" -#include "RadioLibWrappers.h" -#include "SX126xReset.h" +#include "CustomSX126xWrapper.h" -class CustomLLCC68Wrapper : public RadioLibWrapper { +class CustomLLCC68Wrapper : public CustomSX126xWrapper { public: - CustomLLCC68Wrapper(CustomLLCC68& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - - void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomLLCC68 *)_radio)->setFrequency(freq); - ((CustomLLCC68 *)_radio)->setSpreadingFactor(sf); - ((CustomLLCC68 *)_radio)->setBandwidth(bw); - ((CustomLLCC68 *)_radio)->setCodingRate(cr); - updatePreamble(sf); - PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); - ((CustomLLCC68 *)_radio)->setPreambleMillis(pm.preambleMillis); - ((CustomLLCC68 *)_radio)->setMaxPayloadMillis(pm.payloadMillis); - - } - - bool isReceivingPacket() override { - return ((CustomLLCC68 *)_radio)->isReceiving(); - } - float getCurrentRSSI() override { - return ((CustomLLCC68 *)_radio)->getRSSI(false); - } - float getLastRSSI() const override { return ((CustomLLCC68 *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomLLCC68 *)_radio)->getSNR(); } - - uint8_t getSpreadingFactor() const override { return ((CustomLLCC68 *)_radio)->spreadingFactor; } - - void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); } + CustomLLCC68Wrapper(CustomLLCC68& radio, mesh::MainBoard& board) : CustomSX126xWrapper(radio, board) { } bool setRxBoostedGainMode(bool en) override { - return ((CustomLLCC68 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; + return chip()->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { - return ((CustomLLCC68 *)_radio)->getRxBoostedGainMode(); + return chip()->getRxBoostedGainMode(); } }; diff --git a/src/helpers/radiolib/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h index e7aaeb937a7..4cab9ab795c 100644 --- a/src/helpers/radiolib/CustomLR1110Wrapper.h +++ b/src/helpers/radiolib/CustomLR1110Wrapper.h @@ -1,54 +1,9 @@ #pragma once #include "CustomLR1110.h" -#include "RadioLibWrappers.h" -#include "LR11x0Reset.h" +#include "CustomLR11x0Wrapper.h" -class CustomLR1110Wrapper : public RadioLibWrapper { +class CustomLR1110Wrapper : public CustomLR11x0Wrapper { public: - CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - - void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomLR1110 *)_radio)->setFrequency(freq); - ((CustomLR1110 *)_radio)->setSpreadingFactor(sf); - ((CustomLR1110 *)_radio)->setBandwidth(bw); - ((CustomLR1110 *)_radio)->setCodingRate(cr); - updatePreamble(sf); - PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); - ((CustomLR1110 *)_radio)->setPreambleMillis(pm.preambleMillis); - ((CustomLR1110 *)_radio)->setMaxPayloadMillis(pm.payloadMillis); - } - - bool isReceivingPacket() override { - return ((CustomLR1110 *)_radio)->isReceiving(); - } - float getCurrentRSSI() override { - float rssi = -110; - ((CustomLR1110 *)_radio)->getRssiInst(&rssi); - return rssi; - } - - uint32_t getEstAirtimeFor(int len_bytes) override { - auto airtime = RadioLibWrapper::getEstAirtimeFor(len_bytes); - return airtime < 200 ? 200 : airtime; // at least 200 millis - } - - void onSendFinished() override { - RadioLibWrapper::onSendFinished(); - _radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts - } - - float getLastRSSI() const override { return ((CustomLR1110 *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomLR1110 *)_radio)->getSNR(); } - - uint8_t getSpreadingFactor() const override { return ((CustomLR1110 *)_radio)->getSpreadingFactor(); } - - bool setRxBoostedGainMode(bool en) override { - return ((CustomLR1110 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; - } - bool getRxBoostedGainMode() const override { - return ((CustomLR1110 *)_radio)->getRxBoostedGainMode(); - } - - void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz(), getRxBoostedGainMode()); } + CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : CustomLR11x0Wrapper(radio, board) { } }; diff --git a/src/helpers/radiolib/CustomLR1121Wrapper.h b/src/helpers/radiolib/CustomLR1121Wrapper.h index 5a754cee839..84ce4f86559 100644 --- a/src/helpers/radiolib/CustomLR1121Wrapper.h +++ b/src/helpers/radiolib/CustomLR1121Wrapper.h @@ -1,53 +1,9 @@ #pragma once #include "CustomLR1121.h" -#include "RadioLibWrappers.h" -#include "LR11x0Reset.h" +#include "CustomLR11x0Wrapper.h" -class CustomLR1121Wrapper : public RadioLibWrapper { +class CustomLR1121Wrapper : public CustomLR11x0Wrapper { public: - CustomLR1121Wrapper(CustomLR1121& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - - void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomLR1121 *)_radio)->setFrequency(freq); - ((CustomLR1121 *)_radio)->setSpreadingFactor(sf); - ((CustomLR1121 *)_radio)->setBandwidth(bw); - ((CustomLR1121 *)_radio)->setCodingRate(cr); - updatePreamble(sf); - PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); - ((CustomLR1121 *)_radio)->setPreambleMillis(pm.preambleMillis); - ((CustomLR1121 *)_radio)->setMaxPayloadMillis(pm.payloadMillis); - } - - void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1121 *)_radio)->getFreqMHz(), getRxBoostedGainMode()); } - bool isReceivingPacket() override { - return ((CustomLR1121 *)_radio)->isReceiving(); - } - float getCurrentRSSI() override { - float rssi = -110; - ((CustomLR1121 *)_radio)->getRssiInst(&rssi); - return rssi; - } - - void onSendFinished() override { - RadioLibWrapper::onSendFinished(); - _radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts - } - - uint32_t getEstAirtimeFor(int len_bytes) override { - auto airtime = RadioLibWrapper::getEstAirtimeFor(len_bytes); - return airtime < 200 ? 200 : airtime; // at least 200 millis - } - - float getLastRSSI() const override { return ((CustomLR1121 *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomLR1121 *)_radio)->getSNR(); } - - uint8_t getSpreadingFactor() const override { return ((CustomLR1121 *)_radio)->getSpreadingFactor(); } - - bool setRxBoostedGainMode(bool en) override { - return ((CustomLR1121 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; - } - bool getRxBoostedGainMode() const override { - return ((CustomLR1121 *)_radio)->getRxBoostedGainMode(); - } + CustomLR1121Wrapper(CustomLR1121& radio, mesh::MainBoard& board) : CustomLR11x0Wrapper(radio, board) { } }; diff --git a/src/helpers/radiolib/CustomLR11x0Wrapper.h b/src/helpers/radiolib/CustomLR11x0Wrapper.h new file mode 100644 index 00000000000..9f9304e4c8b --- /dev/null +++ b/src/helpers/radiolib/CustomLR11x0Wrapper.h @@ -0,0 +1,43 @@ +#pragma once + +#include "CustomChipWrapper.h" +#include "LR11x0Reset.h" + +// The wrapper of the LR11x0 family: LR1110 and LR1121. +template +class CustomLR11x0Wrapper : public CustomChipWrapper { +public: + CustomLR11x0Wrapper(Chip& radio, mesh::MainBoard& board) : CustomChipWrapper(radio, board) { } + + void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { + this->setModemParams(freq, bw, sf, cr); + this->setRxDeadlines(bw, sf, cr); + } + + float getCurrentRSSI() override { + float rssi = -110; + this->chip()->getRssiInst(&rssi); + return rssi; + } + + uint32_t getEstAirtimeFor(int len_bytes) override { + auto airtime = RadioLibWrapper::getEstAirtimeFor(len_bytes); + return airtime < 200 ? 200 : airtime; // at least 200 millis + } + + void onSendFinished() override { + RadioLibWrapper::onSendFinished(); + this->_radio->setPreambleLength(this->preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts + } + + uint8_t getSpreadingFactor() const override { return this->chip()->getSpreadingFactor(); } + + bool setRxBoostedGainMode(bool en) override { + return this->chip()->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; + } + bool getRxBoostedGainMode() const override { + return this->chip()->getRxBoostedGainMode(); + } + + void doResetAGC() override { lr11x0ResetAGC(this->chip(), this->chip()->getFreqMHz(), getRxBoostedGainMode()); } +}; diff --git a/src/helpers/radiolib/CustomLR2021Wrapper.h b/src/helpers/radiolib/CustomLR2021Wrapper.h index 8299e2bf55b..cb55388ea66 100644 --- a/src/helpers/radiolib/CustomLR2021Wrapper.h +++ b/src/helpers/radiolib/CustomLR2021Wrapper.h @@ -1,7 +1,7 @@ #pragma once #include "CustomLR2021.h" -#include "RadioLibWrappers.h" +#include "CustomChipWrapper.h" #ifndef USE_LR2021 #define USE_LR2021 @@ -11,21 +11,14 @@ #define LR2021_RX_BOOST_LEVEL 7 #endif -class CustomLR2021Wrapper : public RadioLibWrapper { +class CustomLR2021Wrapper : public CustomChipWrapper { public: - CustomLR2021Wrapper(CustomLR2021& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + CustomLR2021Wrapper(CustomLR2021& radio, mesh::MainBoard& board) : CustomChipWrapper(radio, board) { } void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomLR2021 *)_radio)->setFrequency(freq); - ((CustomLR2021 *)_radio)->setSpreadingFactor(sf); - ((CustomLR2021 *)_radio)->setBandwidth(bw); - ((CustomLR2021 *)_radio)->setCodingRate(cr); - updatePreamble(sf); + setModemParams(freq, bw, sf, cr); applySideDetectorConfig(); - PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); - ((CustomLR2021 *)_radio)->setPreambleMillis(pm.preambleMillis); - ((CustomLR2021 *)_radio)->setMaxPayloadMillis(pm.payloadMillis); - + setRxDeadlines(bw, sf, cr); } bool configSideDetectors(const uint8_t* sideDetSFs, uint8_t num, float bw) override { @@ -48,7 +41,7 @@ class CustomLR2021Wrapper : public RadioLibWrapper { tmp[i].invertIQ = false; tmp[i].syncWord = 0x12; } - int16_t status = ((CustomLR2021 *)_radio)->setSideDetector(tmp, num); + int16_t status = chip()->setSideDetector(tmp, num); RadioLibWrapper::idle(); // trigger startReceive() MESH_DEBUG_PRINTLN("setSideDetector() returned %d", status); @@ -63,7 +56,7 @@ class CustomLR2021Wrapper : public RadioLibWrapper { } int16_t applySideDetectorConfig() { - int16_t status = ((CustomLR2021 *)_radio)->setSideDetector(_sideDet, _numSideDet); + int16_t status = chip()->setSideDetector(_sideDet, _numSideDet); RadioLibWrapper::idle(); // trigger startReceive() return status; } @@ -73,13 +66,9 @@ class CustomLR2021Wrapper : public RadioLibWrapper { return tSym; } - bool isReceivingPacket() override { - return ((CustomLR2021 *)_radio)->isReceiving(); - } - float getCurrentRSSI() override { float rssi = -110; - ((CustomLR2021 *)_radio)->getRssiInst(&rssi); + chip()->getRssiInst(&rssi); return rssi; } @@ -88,20 +77,17 @@ class CustomLR2021Wrapper : public RadioLibWrapper { _radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts } - float getLastRSSI() const override { return ((CustomLR2021 *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomLR2021 *)_radio)->getSNR(); } - - uint8_t getSpreadingFactor() const override { return ((CustomLR2021 *)_radio)->getSpreadingFactor(); } + uint8_t getSpreadingFactor() const override { return chip()->getSpreadingFactor(); } bool setRxBoostedGainMode(bool en) override { - ((CustomLR2021 *)_radio)->standby(); // LR2021 must be in standby to accept setRxBoostedGainMode - int16_t status = ((CustomLR2021 *)_radio)->setRxBoostedGainMode(en ? LR2021_RX_BOOST_LEVEL: 0); + chip()->standby(); // LR2021 must be in standby to accept setRxBoostedGainMode + int16_t status = chip()->setRxBoostedGainMode(en ? LR2021_RX_BOOST_LEVEL: 0); RadioLibWrapper::idle(); // trigger startReceive() return status == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { - return ((CustomLR2021 *)_radio)->getRxBoostedGainMode(); + return chip()->getRxBoostedGainMode(); } protected: diff --git a/src/helpers/radiolib/CustomSTM32WLxWrapper.h b/src/helpers/radiolib/CustomSTM32WLxWrapper.h index 521a451e384..bc9af73f917 100644 --- a/src/helpers/radiolib/CustomSTM32WLxWrapper.h +++ b/src/helpers/radiolib/CustomSTM32WLxWrapper.h @@ -1,35 +1,11 @@ #pragma once #include "CustomSTM32WLx.h" -#include "RadioLibWrappers.h" -#include "SX126xReset.h" +#include "CustomSX126xWrapper.h" #include -class CustomSTM32WLxWrapper : public RadioLibWrapper { +// No boosted gain override: the reset passes the base class's false, as before. +class CustomSTM32WLxWrapper : public CustomSX126xWrapper { public: - CustomSTM32WLxWrapper(CustomSTM32WLx& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - - void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomSTM32WLx *)_radio)->setFrequency(freq); - ((CustomSTM32WLx *)_radio)->setSpreadingFactor(sf); - ((CustomSTM32WLx *)_radio)->setBandwidth(bw); - ((CustomSTM32WLx *)_radio)->setCodingRate(cr); - updatePreamble(sf); - PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); - ((CustomSTM32WLx *)_radio)->setPreambleMillis(pm.preambleMillis); - ((CustomSTM32WLx *)_radio)->setMaxPayloadMillis(pm.payloadMillis); - } - - bool isReceivingPacket() override { - return ((CustomSTM32WLx *)_radio)->isReceiving(); - } - float getCurrentRSSI() override { - return ((CustomSTM32WLx *)_radio)->getRSSI(false); - } - float getLastRSSI() const override { return ((CustomSTM32WLx *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomSTM32WLx *)_radio)->getSNR(); } - - uint8_t getSpreadingFactor() const override { return ((CustomSTM32WLx *)_radio)->spreadingFactor; } - - void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); } + CustomSTM32WLxWrapper(CustomSTM32WLx& radio, mesh::MainBoard& board) : CustomSX126xWrapper(radio, board) { } }; diff --git a/src/helpers/radiolib/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h index c6af5fdbeaa..e23b89e0796 100644 --- a/src/helpers/radiolib/CustomSX1262Wrapper.h +++ b/src/helpers/radiolib/CustomSX1262Wrapper.h @@ -1,48 +1,24 @@ #pragma once #include "CustomSX1262.h" -#include "RadioLibWrappers.h" -#include "SX126xReset.h" +#include "CustomSX126xWrapper.h" #ifndef USE_SX1262 #define USE_SX1262 #endif -class CustomSX1262Wrapper : public RadioLibWrapper { +class CustomSX1262Wrapper : public CustomSX126xWrapper { public: - CustomSX1262Wrapper(CustomSX1262& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + CustomSX1262Wrapper(CustomSX1262& radio, mesh::MainBoard& board) : CustomSX126xWrapper(radio, board) { } - void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomSX1262 *)_radio)->setFrequency(freq); - ((CustomSX1262 *)_radio)->setSpreadingFactor(sf); - ((CustomSX1262 *)_radio)->setBandwidth(bw); - ((CustomSX1262 *)_radio)->setCodingRate(cr); - updatePreamble(sf); - PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); - ((CustomSX1262 *)_radio)->setPreambleMillis(pm.preambleMillis); - ((CustomSX1262 *)_radio)->setMaxPayloadMillis(pm.payloadMillis); - } - - bool isReceivingPacket() override { - return ((CustomSX1262 *)_radio)->isReceiving(); - } - float getCurrentRSSI() override { - return ((CustomSX1262 *)_radio)->getRSSI(false); - } - float getLastRSSI() const override { return ((CustomSX1262 *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomSX1262 *)_radio)->getSNR(); } - - uint8_t getSpreadingFactor() const override { return ((CustomSX1262 *)_radio)->spreadingFactor; } virtual void powerOff() override { - ((CustomSX1262 *)_radio)->sleep(false); + chip()->sleep(false); } bool setRxBoostedGainMode(bool en) override { - return ((CustomSX1262 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; + return chip()->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { - return ((CustomSX1262 *)_radio)->getRxBoostedGainMode(); + return chip()->getRxBoostedGainMode(); } - - void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); } }; diff --git a/src/helpers/radiolib/CustomSX1268Wrapper.h b/src/helpers/radiolib/CustomSX1268Wrapper.h index 0fdc6a98bb3..a7409b1b282 100644 --- a/src/helpers/radiolib/CustomSX1268Wrapper.h +++ b/src/helpers/radiolib/CustomSX1268Wrapper.h @@ -1,45 +1,20 @@ #pragma once #include "CustomSX1268.h" -#include "RadioLibWrappers.h" -#include "SX126xReset.h" +#include "CustomSX126xWrapper.h" #ifndef USE_SX1268 #define USE_SX1268 #endif -class CustomSX1268Wrapper : public RadioLibWrapper { +class CustomSX1268Wrapper : public CustomSX126xWrapper { public: - CustomSX1268Wrapper(CustomSX1268& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - - void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomSX1268 *)_radio)->setFrequency(freq); - ((CustomSX1268 *)_radio)->setSpreadingFactor(sf); - ((CustomSX1268 *)_radio)->setBandwidth(bw); - ((CustomSX1268 *)_radio)->setCodingRate(cr); - updatePreamble(sf); - PacketMillis pm = calcMaxPacketMillis(sf, bw, cr, preambleLengthForSF(sf)); - ((CustomSX1268 *)_radio)->setPreambleMillis(pm.preambleMillis); - ((CustomSX1268 *)_radio)->setMaxPayloadMillis(pm.payloadMillis); - } - - bool isReceivingPacket() override { - return ((CustomSX1268 *)_radio)->isReceiving(); - } - float getCurrentRSSI() override { - return ((CustomSX1268 *)_radio)->getRSSI(false); - } - float getLastRSSI() const override { return ((CustomSX1268 *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomSX1268 *)_radio)->getSNR(); } - - uint8_t getSpreadingFactor() const override { return ((CustomSX1268 *)_radio)->spreadingFactor; } + CustomSX1268Wrapper(CustomSX1268& radio, mesh::MainBoard& board) : CustomSX126xWrapper(radio, board) { } bool setRxBoostedGainMode(bool en) override { - return ((CustomSX1268 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; + return chip()->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { - return ((CustomSX1268 *)_radio)->getRxBoostedGainMode(); + return chip()->getRxBoostedGainMode(); } - - void doResetAGC() override { sx126xResetAGC((SX126x *)_radio, getRxBoostedGainMode()); } }; diff --git a/src/helpers/radiolib/CustomSX126xWrapper.h b/src/helpers/radiolib/CustomSX126xWrapper.h new file mode 100644 index 00000000000..bec45f741e5 --- /dev/null +++ b/src/helpers/radiolib/CustomSX126xWrapper.h @@ -0,0 +1,25 @@ +#pragma once + +#include "CustomChipWrapper.h" +#include "SX126xReset.h" + +// The wrapper of the SX126x family: SX1262, SX1268, LLCC68 and STM32WLx. +// Boosted gain is not here, because STM32WLx has no override for it. +template +class CustomSX126xWrapper : public CustomChipWrapper { +public: + CustomSX126xWrapper(Chip& radio, mesh::MainBoard& board) : CustomChipWrapper(radio, board) { } + + void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { + this->setModemParams(freq, bw, sf, cr); + this->setRxDeadlines(bw, sf, cr); + } + + float getCurrentRSSI() override { + return this->chip()->getRSSI(false); + } + + uint8_t getSpreadingFactor() const override { return this->chip()->spreadingFactor; } + + void doResetAGC() override { sx126xResetAGC(this->chip(), this->getRxBoostedGainMode()); } +}; diff --git a/src/helpers/radiolib/CustomSX1276Wrapper.h b/src/helpers/radiolib/CustomSX1276Wrapper.h index 71a19962a95..201f92e95fc 100644 --- a/src/helpers/radiolib/CustomSX1276Wrapper.h +++ b/src/helpers/radiolib/CustomSX1276Wrapper.h @@ -1,32 +1,24 @@ #pragma once #include "CustomSX1276.h" -#include "RadioLibWrappers.h" +#include "CustomChipWrapper.h" #ifndef USE_SX1276 #define USE_SX1276 #endif -class CustomSX1276Wrapper : public RadioLibWrapper { +class CustomSX1276Wrapper : public CustomChipWrapper { public: - CustomSX1276Wrapper(CustomSX1276& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + CustomSX1276Wrapper(CustomSX1276& radio, mesh::MainBoard& board) : CustomChipWrapper(radio, board) { } + // no receive deadlines: the chip class reads its modem status instead void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override { - ((CustomSX1276 *)_radio)->setFrequency(freq); - ((CustomSX1276 *)_radio)->setSpreadingFactor(sf); - ((CustomSX1276 *)_radio)->setBandwidth(bw); - ((CustomSX1276 *)_radio)->setCodingRate(cr); - updatePreamble(sf); + setModemParams(freq, bw, sf, cr); } - bool isReceivingPacket() override { - return ((CustomSX1276 *)_radio)->isReceiving(); - } float getCurrentRSSI() override { - return ((CustomSX1276 *)_radio)->getRSSI(false); + return chip()->getRSSI(false); } - float getLastRSSI() const override { return ((CustomSX1276 *)_radio)->getRSSI(); } - float getLastSNR() const override { return ((CustomSX1276 *)_radio)->getSNR(); } - uint8_t getSpreadingFactor() const override { return ((CustomSX1276 *)_radio)->spreadingFactor; } + uint8_t getSpreadingFactor() const override { return chip()->spreadingFactor; } }; diff --git a/src/helpers/radiolib/RadioLibTarget.cpp b/src/helpers/radiolib/RadioLibTarget.cpp index a9259e2b180..7b4dc01ced5 100644 --- a/src/helpers/radiolib/RadioLibTarget.cpp +++ b/src/helpers/radiolib/RadioLibTarget.cpp @@ -1,5 +1,5 @@ // The parts of a variant's target.cpp that every board with a RadioLib radio -// writes the same way, defined once here. They are weak: a variant (or a +// writes the same way, defined once here. The functions are weak: a variant (or a // downstream fork's variant) that still defines its own keeps compiling, and its // definition takes precedence. WRAPPER_CLASS is what tells a RadioLib variant // apart; the ESP-NOW variants have no LoRa radio and define their own. @@ -9,6 +9,81 @@ #include +// The radio, its wrapper and the RTC clock, for the variants whose target.h +// asks for them with RADIOLIB_TARGET_GLOBALS. Built from the board's +// description: the P_LORA_* pins, the chip, the platform, and on ESP32 +// P_LORA_SCLK (an own SPI bus) and P_LORA_SPI_HOST (its host, if not the +// SPIClass default). Not weak, unlike the functions below: a weak object is +// still constructed here, into the variant's object where the variant defines +// its own, so a variant that keeps its globals simply does not define the macro. +#ifdef RADIOLIB_TARGET_GLOBALS + +#if !defined(NRF52_PLATFORM) && !defined(ESP32) + #error "RADIOLIB_TARGET_GLOBALS: the radio globals are described for nRF52 and ESP32 only so far; define them in target.cpp" +#endif + +#include +#include +#if defined(ESP32) + #include +#else + #include +#endif + +// The pins in the order the chip's Module takes them. +#if defined(USE_SX1276) + #define RADIOLIB_TARGET_PINS P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1 +#else + #define RADIOLIB_TARGET_PINS P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY +#endif + +// On nRF52 the radio is on SPI, passed to RadioLib, so that std_init() and not +// RadioLib starts the bus. On ESP32 it has a bus of its own where the board +// names the SPI pins, and otherwise RadioLib starts its default bus itself. +#if defined(ESP32) + #if defined(P_LORA_SCLK) + #if defined(P_LORA_SPI_HOST) + static SPIClass spi(P_LORA_SPI_HOST); + #else + static SPIClass spi; + #endif + static RADIO_CLASS radio = new Module(RADIOLIB_TARGET_PINS, spi); + #else + static RADIO_CLASS radio = new Module(RADIOLIB_TARGET_PINS); + #endif +#else + static RADIO_CLASS radio = new Module(RADIOLIB_TARGET_PINS, SPI); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +#if defined(ESP32) + ESP32RTCClock fallback_clock; +#else + VolatileRTCClock fallback_clock; +#endif +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +// The radio_init() of these variants on ESP32 with a chip that has std_init(): +// start the fallback clock and the RTC, then the radio on its bus. +#if defined(ESP32) && (defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_SX1276)) + +#include + +__attribute__((weak)) bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); +#if defined(P_LORA_SCLK) + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} + +#endif + +#endif + // A new random identity, from the noise of the radio radio_driver wraps (which // is the variant's global radio). Slow, so only for creating the node's identity. __attribute__((weak)) mesh::LocalIdentity radio_new_identity() { diff --git a/test/mocks/radiolib_variant/SPI.h b/test/mocks/radiolib_variant/SPI.h index 6bfbe442e22..971d06aa146 100644 --- a/test/mocks/radiolib_variant/SPI.h +++ b/test/mocks/radiolib_variant/SPI.h @@ -1,7 +1,20 @@ #pragma once -// Stand-in for the Arduino SPI library: only the type and the global, so a test -// can see which bus the radio is started on. -class SPIClass { }; +#include + +// Stand-in for the Arduino SPI library: the type, the global and the bus +// numbers, so a test can see which bus the radio is started on and which host +// an ESP32 variant's own bus is on. FSPI and HSPI as on the ESP32-S3 and C3; +// the default host is HSPI, as in the ESP32 core. +#ifndef FSPI +#define FSPI 0 +#define HSPI 1 +#endif + +class SPIClass { +public: + uint8_t bus; + SPIClass(uint8_t spi_bus = HSPI) : bus(spi_bus) { } +}; inline SPIClass SPI; diff --git a/test/mocks/radiolib_variant/target.h b/test/mocks/radiolib_variant/target.h index 65dcede0fb6..736ba0a3bee 100644 --- a/test/mocks/radiolib_variant/target.h +++ b/test/mocks/radiolib_variant/target.h @@ -6,57 +6,7 @@ // As on a board, the environment sets WRAPPER_CLASS and the variant's target.cpp // (here: the test) defines the globals declared below. -#include -#include -#include - -#include -#include - -// What the stand-ins below were asked to do, in order, for the tests to compare. -inline std::vector target_calls; - -// Stand-in for a chip class with std_init() (CustomSX1262 and its siblings): -// records the call and the SPI bus it was given, and counts the calls per radio. -class TestRadio : public PhysicalLayer { -public: - bool std_init_result = true; - int std_init_calls = 0; - - bool std_init(SPIClass* spi = nullptr) { - target_calls.push_back(spi == &SPI ? "std_init(&SPI)" : spi ? "std_init(other SPI)" : "std_init()"); - std_init_calls++; - return std_init_result; - } -}; - -// Stand-in for AutoDiscoverRTCClock: records which bus it was started on. -class TestRTCClock { -public: - void begin(TwoWire& wire) { target_calls.push_back(&wire == &Wire ? "rtc_clock.begin(Wire)" : "rtc_clock.begin(other)"); } -}; - -// What CustomSX1262Wrapper.h defines, which every target.h of an SX1262 variant -// includes. -#ifndef USE_SX1262 -#define USE_SX1262 -#endif - -class TestRadioWrapper : public RadioLibWrapper { -public: - TestRadioWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } - void setParams(float, float, uint8_t, uint8_t) override { } - bool isReceivingPacket() override { return false; } - float getCurrentRSSI() override { return -100.0f; } -}; - -class TestBoard : public mesh::MainBoard { -public: - uint16_t getBattMilliVolts() override { return 4200; } - const char* getManufacturerName() const override { return "test"; } - void reboot() override { } - uint8_t getStartupReason() const override { return 0; } -}; +#include "variant_stubs.h" extern WRAPPER_CLASS radio_driver; extern TestRTCClock rtc_clock; diff --git a/test/mocks/radiolib_variant/target_calls.h b/test/mocks/radiolib_variant/target_calls.h new file mode 100644 index 00000000000..c7e98265ede --- /dev/null +++ b/test/mocks/radiolib_variant/target_calls.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +// What the variant stand-ins were asked to do, in order, for the tests to compare. +inline std::vector target_calls; diff --git a/test/mocks/radiolib_variant/variant_stubs.h b/test/mocks/radiolib_variant/variant_stubs.h new file mode 100644 index 00000000000..2f236b41c34 --- /dev/null +++ b/test/mocks/radiolib_variant/variant_stubs.h @@ -0,0 +1,84 @@ +#pragma once + +// The parts of the RadioLib variant stand-ins that do not depend on how the +// variant defines its globals: a recording radio, wrapper, board and RTC clock, +// and RadioLib's Module. Included by test/mocks/radiolib_variant/target.h (a +// variant that defines its globals in its target.cpp) and by +// test/mocks/radiolib_variant_globals/target.h (one that takes them from +// RadioLibTarget.cpp). + +#include +#include +#include + +#include +#include + +#include "target_calls.h" + +// Stand-in for RadioLib's Module, with its two Arduino constructors, which do +// different things: without an SPI bus RadioLib starts the default bus itself, +// with one it leaves that to the caller. `spi` tells them apart (null for the +// first), the pins are kept as given. +class Module { +public: + uint32_t cs, irq, rst, gpio; + SPIClass* spi; + + Module(uint32_t cs, uint32_t irq, uint32_t rst, uint32_t gpio = (uint32_t)-1) + : cs(cs), irq(irq), rst(rst), gpio(gpio), spi(nullptr) { } + Module(uint32_t cs, uint32_t irq, uint32_t rst, uint32_t gpio, SPIClass& spi) + : cs(cs), irq(irq), rst(rst), gpio(gpio), spi(&spi) { } +}; + +// Stand-in for a chip class with std_init() (CustomSX1262 and its siblings): +// records the call and the SPI bus it was given, and counts the calls per radio. +class TestRadio : public PhysicalLayer { +public: + Module* module = nullptr; // what the variant constructed it from, as `new Module(...)` + bool std_init_result = true; + int std_init_calls = 0; + + bool std_init(SPIClass* spi = nullptr) { + target_calls.push_back(spi == &SPI ? "std_init(&SPI)" + : spi && module && spi == module->spi ? "std_init(module bus)" + : spi ? "std_init(other SPI)" : "std_init()"); + std_init_calls++; + return std_init_result; + } + + TestRadio() = default; + TestRadio(Module* mod) : module(mod) { } +}; + +// Stand-in for AutoDiscoverRTCClock: records which bus it was started on. +class TestRTCClock { +public: + void begin(TwoWire& wire) { target_calls.push_back(&wire == &Wire ? "rtc_clock.begin(Wire)" : "rtc_clock.begin(other)"); } +}; + +// What CustomSX1262Wrapper.h defines, which every target.h of an SX1262 variant +// includes; an environment for an SX1276 variant sets USE_SX1276 instead. +#if !defined(USE_SX1262) && !defined(USE_SX1276) +#define USE_SX1262 +#endif + +class TestRadioWrapper : public RadioLibWrapper { +public: + TestRadioWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } + // What it was constructed with, for the tests (the real wrappers keep them + // protected as well). + PhysicalLayer* wrapped_radio() const { return _radio; } + mesh::MainBoard* wrapped_board() const { return _board; } + void setParams(float, float, uint8_t, uint8_t) override { } + bool isReceivingPacket() override { return false; } + float getCurrentRSSI() override { return -100.0f; } +}; + +class TestBoard : public mesh::MainBoard { +public: + uint16_t getBattMilliVolts() override { return 4200; } + const char* getManufacturerName() const override { return "test"; } + void reboot() override { } + uint8_t getStartupReason() const override { return 0; } +}; diff --git a/test/mocks/radiolib_variant_esp32/helpers/ESP32Board.h b/test/mocks/radiolib_variant_esp32/helpers/ESP32Board.h new file mode 100644 index 00000000000..56c38632acc --- /dev/null +++ b/test/mocks/radiolib_variant_esp32/helpers/ESP32Board.h @@ -0,0 +1,16 @@ +#pragma once + +// Stand-in for src/helpers/ESP32Board.h, which the ESP32 variants include +// through their board header: only ESP32RTCClock, recording begin(), since the +// real one sets the system time from the ESP32's RTC memory. + +#include +#include "../../radiolib_variant/target_calls.h" + +class ESP32RTCClock : public mesh::RTCClock { + uint32_t _time = 0; +public: + void begin() { target_calls.push_back("fallback_clock.begin()"); } + uint32_t getCurrentTime() override { return _time; } + void setCurrentTime(uint32_t time) override { _time = time; } +}; diff --git a/test/mocks/radiolib_variant_esp32/target.h b/test/mocks/radiolib_variant_esp32/target.h new file mode 100644 index 00000000000..8843938b199 --- /dev/null +++ b/test/mocks/radiolib_variant_esp32/target.h @@ -0,0 +1,20 @@ +#pragma once + +// Stand-in for the target.h of an ESP32 variant of cut D2 of note 0011, for the +// suite that pins its radio, wrapper and RTC globals and its radio_init(). The +// RTC clock is the real AutoDiscoverRTCClock (its out-of-line members are stood +// in by the test); helpers/ESP32Board.h, which the variants include through +// their board header, is a stand-in next to this file. The environment sets the pins, P_LORA_SCLK and ESP32, +// as the variants' build flags do. Listed ahead of test/mocks/radiolib_variant. + +#include +#include "../radiolib_variant/variant_stubs.h" +#include + +extern TestBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity(); diff --git a/test/mocks/radiolib_variant_globals/target.h b/test/mocks/radiolib_variant_globals/target.h new file mode 100644 index 00000000000..c719c466fef --- /dev/null +++ b/test/mocks/radiolib_variant_globals/target.h @@ -0,0 +1,20 @@ +#pragma once + +// Stand-in for the target.h of an nRF52 variant of cut D of note 0011, for the +// suite that pins its radio, wrapper and RTC globals. As in the variants, the +// RTC clock is the real AutoDiscoverRTCClock (its out-of-line members are stood +// in by the test), and the environment sets the pins and the platform, as the +// variants' build flags and variant.h do. Listed ahead of +// test/mocks/radiolib_variant, whose target.h stands in for a variant whose +// RTC clock is a recording stand-in. + +#include "../radiolib_variant/variant_stubs.h" +#include + +extern TestBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity(); diff --git a/test/test_companion_frames/test_commands.cpp b/test/test_companion_frames/test_commands.cpp index faf63e25b28..a11e0178ce6 100644 --- a/test/test_companion_frames/test_commands.cpp +++ b/test/test_companion_frames/test_commands.cpp @@ -489,6 +489,47 @@ TEST_F(CommandTest, ExportSelfThenSendItAsRawPacket) { EXPECT_EQ(h.reply(withKey(EXPORT_CONTACT, peerKey())), err(ERR_NOT_FOUND)); } +// Short raw packets of every route type: a packet is taken exactly when its header, +// path length byte and path are inside the frame, and goes out byte for byte as it +// came in. Whatever an earlier frame left in the buffer behind it does not matter +// (docs/fixes/packet-parse-len.md). +TEST_F(CommandTest, SendRawPacketShortFramesOfEveryRouteType) { + for (uint8_t route = 0; route < 4; route++) { + bool transport = route == 0 || route == 3; + size_t head = transport ? 5 : 1; // header, transport codes + for (uint8_t path_byte : {0x00, 0x01, 0x42, 0xC0}) { + size_t path = path_byte == 0x42 ? 4 : (path_byte & 63); // 0x42: 2 hashes of 2 bytes + for (size_t raw_len = 2; raw_len <= 10; raw_len++) { + for (int stale : {-1, 0x5A, 0xC0}) { + h.boot(); + if (stale >= 0) { + Bytes before = {GET_DEVICE_TIME}; + putFill(before, MAX_FRAME_SIZE - 1, (uint8_t)stale); + h.send(before); + } + Bytes raw; + for (size_t i = 0; i < raw_len; i++) raw.push_back((uint8_t)(0x21 + 3 * i)); + raw[0] = (uint8_t)(PAYLOAD_TYPE_TXT_MSG << PH_TYPE_SHIFT | route); + if (head < raw_len) raw[head] = path_byte; + bool takes = head < raw_len && (path_byte >> 6) != 3 && head + 1 + path <= raw_len; + + Bytes f = {SEND_RAW_PACKET, 1}; + put(f, raw.data(), raw.size()); + SCOPED_TRACE(testing::Message() << "route " << (int)route << " path " << (int)path_byte + << " raw_len " << raw_len << " stale " << stale); + if (takes) { + ASSERT_EQ(h.reply(f), OK); + ASSERT_EQ(h.mesh().outbound(), std::vector{raw}); + } else { + ASSERT_EQ(h.reply(f), err(ERR_ILLEGAL_ARG)); + ASSERT_EQ(h.mesh().outboundTotal(), 0); + } + } + } + } + } +} + TEST_F(CommandTest, ExportAndImportAContact) { addPeer(); Bytes r = h.reply(withKey(EXPORT_CONTACT, peerKey())); diff --git a/test/test_companion_frames/test_frame_bounds.cpp b/test/test_companion_frames/test_frame_bounds.cpp index dccd6589c1c..0dab1faf16e 100644 --- a/test/test_companion_frames/test_frame_bounds.cpp +++ b/test/test_companion_frames/test_frame_bounds.cpp @@ -195,8 +195,8 @@ std::vector catalog() { // no zero anywhere in the frame: the name runs into the key and past the end add("SET_DEFAULT_FLOOD_SCOPE unterminated", cat({{SET_DEFAULT_FLOOD_SCOPE}, fill(47, 0x61)})); add("SEND_RAW_PACKET flood", {SEND_RAW_PACKET, 0, 0x11, 0, 0x55}); - // route type 0 carries 4 transport code bytes, which tryParsePacket() reads - // before it tests the length + // route type 0 carries 4 transport code bytes, which tryParsePacket() read + // before it tested the length (docs/fixes/packet-parse-len.md) add("SEND_RAW_PACKET transport past end", {SEND_RAW_PACKET, 0, 0x00, 0}); return c; } diff --git a/test/test_packet_parse/identity_stubs.cpp b/test/test_packet_parse/identity_stubs.cpp new file mode 100644 index 00000000000..c4e27357901 --- /dev/null +++ b/test/test_packet_parse/identity_stubs.cpp @@ -0,0 +1,28 @@ +// Link-time stand-ins for the parts of mesh::Identity that Dispatcher.cpp and Mesh.cpp reference. +// The real implementations need the board crypto libraries, which do not exist on +// the host. The parse tests never sign, verify or derive secrets, so any call is a +// test failure. + +#include +#include + +namespace mesh { + +Identity::Identity() { memset(pub_key, 0, sizeof(pub_key)); } + +LocalIdentity::LocalIdentity() { memset(prv_key, 0, sizeof(prv_key)); } + +bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) const { + ADD_FAILURE() << "Identity::verify must not be called by the parse tests"; + return false; +} + +void LocalIdentity::sign(uint8_t* sig, const uint8_t* message, int msg_len) const { + ADD_FAILURE() << "LocalIdentity::sign must not be called by the parse tests"; +} + +void LocalIdentity::calcSharedSecret(uint8_t* secret, const uint8_t* other_pub_key) const { + ADD_FAILURE() << "LocalIdentity::calcSharedSecret must not be called by the parse tests"; +} + +} // namespace mesh diff --git a/test/test_packet_parse/test_packet_parse.cpp b/test/test_packet_parse/test_packet_parse.cpp new file mode 100644 index 00000000000..ede4c3d5159 --- /dev/null +++ b/test/test_packet_parse/test_packet_parse.cpp @@ -0,0 +1,407 @@ +// Tests for the two parsers that turn raw bytes into a mesh::Packet: +// Dispatcher::tryParsePacket(), which every received radio packet and every +// CMD_SEND_RAW_PACKET frame of the companion goes through, and Packet::readFrom(), +// which restores stored adverts, imports contacts and receives bridge packets. +// +// Both used to read the header fields before testing the length (see +// docs/fixes/packet-parse-len.md). The tests here pin two things: +// +// - Neither parser touches a byte at or above `len`. The input sits at the end of a +// readable page with an unreadable page behind it, so such a read ends the +// process; each case runs in a child process, so it shows as a failed case. +// - Every input is accepted or refused exactly as by the code before the fix, and an +// accepted packet is field for field the same. The code before the fix is kept +// below as the reference, verbatim except for its debug output; it runs on a +// buffer that is large enough for every read it makes. + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace { + +typedef std::vector Bytes; + +// tryParsePacket() as of integration 07fa9c5c. +bool legacyTryParsePacket(mesh::Packet* pkt, const uint8_t* raw, int len) { + int i = 0; + + pkt->header = raw[i++]; + if (pkt->getPayloadVer() > PAYLOAD_VER_1) { + return false; + } + + if (pkt->hasTransportCodes()) { + memcpy(&pkt->transport_codes[0], &raw[i], 2); i += 2; + memcpy(&pkt->transport_codes[1], &raw[i], 2); i += 2; + } else { + pkt->transport_codes[0] = pkt->transport_codes[1] = 0; + } + + pkt->path_len = raw[i++]; + uint8_t path_mode = pkt->path_len >> 6; // upper 2 bits (legacy firmware: 00) + if (path_mode == 3) { // Reserved for future + return false; + } + + uint8_t path_byte_len = (pkt->path_len & 63) * pkt->getPathHashSize(); + if (path_byte_len > MAX_PATH_SIZE || i + path_byte_len > len) { + return false; + } + + memcpy(pkt->path, &raw[i], path_byte_len); i += path_byte_len; + + pkt->payload_len = len - i; // payload is remainder + if (pkt->payload_len > sizeof(pkt->payload)) { + return false; + } + + memcpy(pkt->payload, &raw[i], pkt->payload_len); + + return true; // success +} + +// Packet::readFrom() as of integration 07fa9c5c. +bool legacyReadFrom(mesh::Packet* pkt, const uint8_t src[], uint8_t len) { + uint8_t i = 0; + pkt->header = src[i++]; + if (pkt->hasTransportCodes()) { + memcpy(&pkt->transport_codes[0], &src[i], 2); i += 2; + memcpy(&pkt->transport_codes[1], &src[i], 2); i += 2; + } else { + pkt->transport_codes[0] = pkt->transport_codes[1] = 0; + } + pkt->path_len = src[i++]; + if (!mesh::Packet::isValidPathLen(pkt->path_len)) return false; // bad encoding + + uint8_t bl = pkt->getPathByteLen(); + memcpy(pkt->path, &src[i], bl); i += bl; + + if (i >= len) return false; // bad encoding + pkt->payload_len = len - i; + if (pkt->payload_len > sizeof(pkt->payload)) return false; // bad encoding + memcpy(pkt->payload, &src[i], pkt->payload_len); //i += payload_len; + return true; // success +} + +// What a caller can see of an accepted packet. +Bytes fields(const mesh::Packet& p) { + Bytes b; + auto put = [&b](const void* v, size_t n) { + const uint8_t* s = (const uint8_t*)v; + b.insert(b.end(), s, s + n); + }; + put(&p.header, sizeof(p.header)); + put(p.transport_codes, sizeof(p.transport_codes)); + put(&p.path_len, sizeof(p.path_len)); + put(p.path, p.getPathByteLen()); + put(&p.payload_len, sizeof(p.payload_len)); + put(p.payload, p.payload_len); + return b; +} + +class FakeRadio : public mesh::Radio { +public: + Bytes next; // the next packet recvRaw() delivers, once + uint8_t stale = 0; // what the rest of the receive buffer holds + int recvRaw(uint8_t* bytes, int sz) override { + memset(bytes, stale, sz); + int n = (int)next.size(); + memcpy(bytes, next.data(), n); + next.clear(); + return n; + } + uint32_t getEstAirtimeFor(int len_bytes) override { return 10; } + float packetScore(float snr, int packet_len) override { return 1.0f; } + bool startSendRaw(const uint8_t* bytes, int len) override { return false; } + bool isSendComplete() override { return true; } + void onSendFinished() override { } + bool isInRecvMode() const override { return true; } +}; + +class FakeClock : public mesh::MillisecondClock { +public: + unsigned long getMillis() override { return 0; } +}; + +// One packet in the pool; counts how often it comes back. +class OnePacketManager : public mesh::PacketManager { +public: + mesh::Packet pkt; + bool in_use = false; + int frees = 0; + mesh::Packet* allocNew() override { + if (in_use) return nullptr; + in_use = true; + return &pkt; + } + void free(mesh::Packet* packet) override { in_use = false; frees++; } + void queueOutbound(mesh::Packet* packet, uint8_t priority, uint32_t scheduled_for) override { } + mesh::Packet* getNextOutbound(uint32_t now) override { return nullptr; } + int getOutboundCount(uint32_t now) const override { return 0; } + int getOutboundTotal() const override { return 0; } + int getFreeCount() const override { return in_use ? 0 : 1; } + mesh::Packet* getOutboundByIdx(int i) override { return nullptr; } + mesh::Packet* removeOutboundByIdx(int i) override { return nullptr; } + void queueInbound(mesh::Packet* packet, uint32_t scheduled_for) override { } + mesh::Packet* getNextInbound(uint32_t now) override { return nullptr; } +}; + +// Records the fields of every packet the receive path hands on. +class RecordingDispatcher : public mesh::Dispatcher { +public: + std::vector received; + RecordingDispatcher(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::PacketManager& mgr) + : mesh::Dispatcher(radio, ms, mgr) { } + mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) override { + received.push_back(fields(*pkt)); + return ACTION_RELEASE; + } +}; + +const uint8_t kRoutes[] = { + ROUTE_TYPE_TRANSPORT_FLOOD, ROUTE_TYPE_FLOOD, ROUTE_TYPE_DIRECT, ROUTE_TYPE_TRANSPORT_DIRECT, +}; + +// Path length bytes: no path, short and long paths of each hash size, the largest +// count, and the reserved mode 3. +const uint8_t kPathBytes[] = { 0x00, 0x01, 0x05, 0x3f, 0x40, 0x41, 0x60, 0x7f, 0x80, 0x95, 0xbf, 0xc0, 0xff }; + +int headerLen(uint8_t header) { + uint8_t route = header & PH_ROUTE_MASK; + return (route == ROUTE_TYPE_TRANSPORT_FLOOD || route == ROUTE_TYPE_TRANSPORT_DIRECT) ? 5 : 1; +} + +// A packet of `len` bytes: the header, then a counting pattern, with the path +// length byte (if it fits) replaced by `path_byte`. +Bytes makeRaw(uint8_t header, uint8_t path_byte, int len) { + Bytes b(len); + for (int i = 0; i < len; i++) b[i] = (uint8_t)(0x11 + 37 * i); + if (len > 0) b[0] = header; + int at = headerLen(header); + if (at < len) b[at] = path_byte; + return b; +} + +// `len` bytes at the very end of a readable page, followed by a page that cannot +// be read. +class GuardedBuffer { + size_t _page; + uint8_t* _base; +public: + GuardedBuffer() { + _page = (size_t)sysconf(_SC_PAGESIZE); + _base = (uint8_t*)mmap(nullptr, 2 * _page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (_base == MAP_FAILED) abort(); + if (mprotect(_base + _page, _page, PROT_NONE) != 0) abort(); + } + ~GuardedBuffer() { munmap(_base, 2 * _page); } + const uint8_t* place(const Bytes& b) { + uint8_t* p = _base + _page - b.size(); + memset(_base, 0xA5, _page); + memcpy(p, b.data(), b.size()); + return p; + } +}; + +const int kMaxGuardedLen = 1 + 4 + 1 + MAX_PATH_SIZE + 2; // past the longest path + +} // namespace + +namespace { + +// A dispatcher to call tryParsePacket() on; the parser uses none of its state. +struct Parser { + FakeRadio radio; + FakeClock clock; + OnePacketManager mgr; + RecordingDispatcher d{radio, clock, mgr}; +}; + +// A read of the guard page ends the child with exit code 3 instead of a crash, so +// the system crash reporter is not started for every failing case. +void exitOnFault(int) { _exit(3); } + +// Runs every path length byte of one route and length through a parser on a +// guarded buffer, then exits 0. A read at or above `len` ends it with code 3. +void parseGuarded(uint8_t route, int len, bool read_from) { + signal(SIGSEGV, exitOnFault); + signal(SIGBUS, exitOnFault); + uint8_t header = route | (PAYLOAD_TYPE_TXT_MSG << PH_TYPE_SHIFT); + GuardedBuffer g; + Parser p; + mesh::Packet pkt; + for (uint8_t pb : kPathBytes) { + Bytes raw = makeRaw(header, pb, len); + if (read_from) { + pkt.readFrom(g.place(raw), (uint8_t)len); + } else { + p.d.tryParsePacket(&pkt, g.place(raw), len); + } + } + exit(0); +} + +} // namespace + +// Every route type, every length up to past the longest path, the path length +// bytes above: a read at or above `len` kills the child, which fails the case. +TEST(PacketParseDeathTest, TryParsePacketReadsNothingPastLen) { + for (uint8_t route : kRoutes) { + for (int len = 0; len <= kMaxGuardedLen; len++) { + EXPECT_EXIT(parseGuarded(route, len, false), testing::ExitedWithCode(0), "") + << "route " << (int)route << " len " << len; + } + } +} + +TEST(PacketParseDeathTest, ReadFromReadsNothingPastLen) { + for (uint8_t route : kRoutes) { + for (int len = 0; len <= kMaxGuardedLen; len++) { + EXPECT_EXIT(parseGuarded(route, len, true), testing::ExitedWithCode(0), "") + << "route " << (int)route << " len " << len; + } + } +} + +namespace { + +// Lengths up to past the longest header and path, and around the payload limit. +std::vector referenceLengths(int max_len) { + std::vector lens; + for (int len = 0; len <= kMaxGuardedLen + 8; len++) lens.push_back(len); + for (int len = 180; len <= max_len; len++) lens.push_back(len); + return lens; +} + +// The same input in a buffer large enough for every read the reference makes, +// with the bytes behind `len` set to `stale`. +const uint8_t* padded(uint8_t* buf, size_t size, const Bytes& raw, uint8_t stale) { + memset(buf, stale, size); + memcpy(buf, raw.data(), raw.size()); + return buf; +} + +const uint8_t kStale[] = { 0x00, 0xff }; + +// The parsers tell headers apart by version and route type only; each of the 16 +// pairs once, with a payload type that differs between them. +std::vector referenceHeaders() { + std::vector h; + for (int ver = 0; ver < 4; ver++) + for (int route = 0; route < 4; route++) + h.push_back((uint8_t)(ver << PH_VER_SHIFT | ((ver * 4 + route) % 16) << PH_TYPE_SHIFT | route)); + return h; +} + +} // namespace + +// Every version and route type, every path length byte, lengths 0 to 80 and 180 to +// 256, two fills behind `len`: accepted and refused as before, and accepted packets equal. +TEST(PacketParse, TryParsePacketMatchesReference) { + uint8_t buf[512]; + Parser p; + long accepted = 0, refused = 0; + for (int header : referenceHeaders()) { + for (int pb = 0; pb < 256; pb++) { + for (int len : referenceLengths(MAX_TRANS_UNIT + 1)) { + Bytes raw = makeRaw((uint8_t)header, (uint8_t)pb, len); + for (uint8_t stale : kStale) { + mesh::Packet want, got; + bool w = legacyTryParsePacket(&want, padded(buf, sizeof(buf), raw, stale), len); + bool g = p.d.tryParsePacket(&got, padded(buf, sizeof(buf), raw, stale), len); + ASSERT_EQ(w, g) << "header " << header << " path " << pb << " len " << len << " stale " << (int)stale; + if (w) { + ASSERT_EQ(fields(want), fields(got)) << "header " << header << " path " << pb << " len " << len; + accepted++; + } else { + refused++; + } + } + } + } + } + // both outcomes are reached in large numbers, so the comparison is not vacuous + EXPECT_GT(accepted, 50000); + EXPECT_GT(refused, 50000); +} + +TEST(PacketParse, ReadFromMatchesReference) { + uint8_t buf[512]; + long accepted = 0, refused = 0; + for (int header : referenceHeaders()) { + for (int pb = 0; pb < 256; pb++) { + for (int len : referenceLengths(255)) { + Bytes raw = makeRaw((uint8_t)header, (uint8_t)pb, len); + for (uint8_t stale : kStale) { + mesh::Packet want, got; + bool w = legacyReadFrom(&want, padded(buf, sizeof(buf), raw, stale), (uint8_t)len); + bool g = got.readFrom(padded(buf, sizeof(buf), raw, stale), (uint8_t)len); + ASSERT_EQ(w, g) << "header " << header << " path " << pb << " len " << len << " stale " << (int)stale; + if (w) { + ASSERT_EQ(fields(want), fields(got)) << "header " << header << " path " << pb << " len " << len; + accepted++; + } else { + refused++; + } + } + } + } + } + EXPECT_GT(accepted, 50000); + EXPECT_GT(refused, 50000); +} + +// The radio receive path: a packet the reference accepts reaches onRecvPacket() +// with the same fields, any other is put back into the pool, whatever the rest of +// the receive buffer holds. +TEST(PacketParse, RadioReceiveMatchesReference) { + uint8_t buf[512]; + int accepted = 0, refused = 0; + for (int header : referenceHeaders()) { + for (uint8_t pb : kPathBytes) { + for (int len : referenceLengths(MAX_TRANS_UNIT)) { + if (len == 0) continue; // recvRaw() returning 0 means nothing was received + Bytes raw = makeRaw((uint8_t)header, pb, len); + for (uint8_t stale : kStale) { + FakeRadio radio; + FakeClock clock; + OnePacketManager mgr; + RecordingDispatcher d(radio, clock, mgr); + radio.next = raw; + radio.stale = stale; + d.loop(); + + mesh::Packet want; + bool w = legacyTryParsePacket(&want, padded(buf, sizeof(buf), raw, stale), len); + SCOPED_TRACE(testing::Message() << "header " << header << " path " << (int)pb << " len " << len); + ASSERT_EQ(mgr.frees, 1); // put back either way: refused, or released by onRecvPacket() + if (w) { + ASSERT_EQ(d.received.size(), 1u); + ASSERT_EQ(d.received[0], fields(want)); + accepted++; + } else { + ASSERT_EQ(d.received.size(), 0u); + refused++; + } + } + } + } + } + EXPECT_GT(accepted, 1000); + EXPECT_GT(refused, 1000); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_radio_chip_wrappers/test_radio_chip_wrappers.cpp b/test/test_radio_chip_wrappers/test_radio_chip_wrappers.cpp new file mode 100644 index 00000000000..1c03245eba6 --- /dev/null +++ b/test/test_radio_chip_wrappers/test_radio_chip_wrappers.cpp @@ -0,0 +1,304 @@ +// The boosted gain macros the two reset helpers read, so the value a wrapper +// passes to them shows up in the call log. Its own test program: the wrappers +// and reset helpers are header only, and test_radio_chips builds the chip +// classes without these macros. +#define SX126X_RX_BOOSTED_GAIN 1 +#define RX_BOOSTED_GAIN 1 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Characterization of the eight chip wrappers (CustomWrapper.h): what each +// one asks of its chip class when the mesh sets the radio parameters, reads the +// signal, resets the receiver, finishes a transmission or powers off. The same +// expectations run against every wrapper; where the families differ, the traits +// below say how, so a refactor that shares code between them has to keep each +// difference. + +namespace { + +class StubBoard : public mesh::MainBoard { +public: + uint16_t getBattMilliVolts() override { return 4200; } + const char* getManufacturerName() const override { return "stub"; } + void reboot() override { } + uint8_t getStartupReason() const override { return 0; } +}; + +enum Family { SX126X, LR11X0, LR2021_, SX127X }; + +// boost: which boosted gain override the wrapper has (NONE inherits the base +// class, which reports false and touches nothing). +enum Boost { NONE, SX126X_REG, LR11X0_FLAG, LR2021_LEVEL }; + +template +struct Wrap { + using Wrapper = W; + using Radio = R; + static constexpr Family family = F; + static constexpr Boost boost = B; + static constexpr bool powerOffSleepFalse = SleepFalse; +}; + +using Wrappers = ::testing::Types< + Wrap, + Wrap, + Wrap, + Wrap, + Wrap, + Wrap, + Wrap, + Wrap>; + +// The IRQ bits each family reports a preamble and a header with. +template struct Bits; +template <> struct Bits { static constexpr uint32_t preamble = RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED, header = RADIOLIB_SX126X_IRQ_HEADER_VALID; }; +template <> struct Bits : Bits { }; +template <> struct Bits : Bits { }; +template <> struct Bits : Bits { }; +template <> struct Bits { static constexpr uint32_t preamble = RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED, header = RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID; }; +template <> struct Bits : Bits { }; +template <> struct Bits { static constexpr uint32_t preamble = RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED, header = RADIOLIB_LR2021_IRQ_LORA_HEADER_VALID; }; +template <> struct Bits { static constexpr uint32_t preamble = 0, header = 0; }; // no IRQ flags, see below + +// SX1276 reports reception through its modem status register. +inline void setModemStatus(CustomSX1276& r, uint8_t status) { r.modem_status = status; } +template void setModemStatus(R&, uint8_t) { } + +constexpr uint32_t T0 = 1000; + +template +struct ChipWrapperTest : public ::testing::Test { + using Wrapper = typename T::Wrapper; + using Radio = typename T::Radio; + Module mod; + Radio radio{&mod}; + StubBoard board; + Wrapper w{radio, board}; + + void SetUp() override { g_mock_millis = T0; } + + std::vector take() { + std::vector c = radio.calls; + radio.calls.clear(); + return c; + } +}; +TYPED_TEST_SUITE(ChipWrapperTest, Wrappers); + +using Log = std::vector; + +// setParams(): the four modem setters in this order, then the preamble length +// for the new spreading factor; LR2021 then reapplies its side detectors and +// returns to standby, so the next poll restarts reception. +TYPED_TEST(ChipWrapperTest, SetParamsCallsTheChipInOrder) { + this->w.setParams(869.618f, 62.5f, 8, 8); + Log expect = {"setFrequency(869.618)", "setSpreadingFactor(8)", "setBandwidth(62.5)", + "setCodingRate(8)", "setPreambleLength(32)"}; + if (TypeParam::family == LR2021_) { + expect.push_back("setSideDetector(0)"); + expect.push_back("standby()"); + } + EXPECT_EQ(this->take(), expect); + + this->w.setParams(869.525f, 250.0f, 11, 5); + expect = {"setFrequency(869.525)", "setSpreadingFactor(11)", "setBandwidth(250)", + "setCodingRate(5)", "setPreambleLength(16)"}; + if (TypeParam::family == LR2021_) { + expect.push_back("setSideDetector(0)"); + expect.push_back("standby()"); + } + EXPECT_EQ(this->take(), expect); +} + +// The receive detection: through isReceivingPacket() the wrapper asks the chip +// class. SX1276 reads its modem status, the others their IRQ flags. +TYPED_TEST(ChipWrapperTest, IsReceivingPacketAsksTheChip) { + if (TypeParam::family == SX127X) { + setModemStatus(this->radio, 0); + EXPECT_FALSE(this->w.isReceivingPacket()); + setModemStatus(this->radio, RH_RF95_MODEM_STATUS_SIGNAL_DETECTED); + EXPECT_TRUE(this->w.isReceivingPacket()); + setModemStatus(this->radio, RH_RF95_MODEM_STATUS_CLEAR); + EXPECT_FALSE(this->w.isReceivingPacket()); + return; + } + this->radio.irq = 0; + EXPECT_FALSE(this->w.isReceivingPacket()); + this->radio.irq = Bits::preamble; + EXPECT_TRUE(this->w.isReceivingPacket()); +} + +// setParams() also hands the chip class the two deadlines of the receive +// detection, computed from the new parameters; SX1276 has none. +TYPED_TEST(ChipWrapperTest, SetParamsGivesTheChipItsDeadlines) { + if (TypeParam::family == SX127X) GTEST_SKIP() << "SX1276 has no receive deadlines"; + using B = Bits; + const uint8_t sf = 7, cr = 5; + const float bw = 500.0f; + this->w.setParams(869.525f, bw, sf, cr); + PacketMillis pm = this->w.calcMaxPacketMillis(sf, bw, cr, RadioLibWrapper::preambleLengthForSF(sf)); + ASSERT_NE(pm.preambleMillis, 66u); // not the chip's default + ASSERT_NE(pm.payloadMillis, 3934u); + + this->radio.irq = B::preamble; + g_mock_millis = T0; + EXPECT_TRUE(this->w.isReceivingPacket()); + g_mock_millis = T0 + pm.preambleMillis; + EXPECT_TRUE(this->w.isReceivingPacket()); + g_mock_millis = T0 + pm.preambleMillis + 1; + EXPECT_FALSE(this->w.isReceivingPacket()); + + this->radio.irq = B::preamble | B::header; + g_mock_millis = T0 + 10000; + EXPECT_TRUE(this->w.isReceivingPacket()); + g_mock_millis = T0 + 10000 + pm.payloadMillis; + EXPECT_TRUE(this->w.isReceivingPacket()); + g_mock_millis = T0 + 10000 + pm.payloadMillis + 1; + EXPECT_FALSE(this->w.isReceivingPacket()); +} + +// The last packet's RSSI and SNR come from the chip; the current RSSI is the +// instant reading: getRSSI(false) on SX126x and SX127x, getRssiInst() on the LR +// chips, which report -110 when the chip writes nothing. +TYPED_TEST(ChipWrapperTest, SignalReadings) { + this->radio.rssi = -80.25f; + this->radio.snr = 7.5f; + EXPECT_FLOAT_EQ(this->w.getLastRSSI(), -80.25f); + EXPECT_FLOAT_EQ(this->w.getLastSNR(), 7.5f); + + if (TypeParam::family == SX126X || TypeParam::family == SX127X) { + EXPECT_FLOAT_EQ(this->w.getCurrentRSSI(), this->radio.rssi_now); + } else { + EXPECT_FLOAT_EQ(this->w.getCurrentRSSI(), this->radio.rssi_inst); + this->radio.rssi_inst_written = false; + EXPECT_FLOAT_EQ(this->w.getCurrentRSSI(), -110.0f); + } + EXPECT_TRUE(this->take().empty()); +} + +TYPED_TEST(ChipWrapperTest, SpreadingFactorIsTheChips) { + for (uint8_t sf : {7, 11, 12}) { + this->radio.spreadingFactor = sf; + EXPECT_EQ(this->w.getSpreadingFactor(), sf); + } +} + +// Boosted gain: SX1262, SX1268, LLCC68 set it on the chip and read register +// 0x8AC back; LR1110 and LR1121 set it and remember it; LR2021 sets a level +// from standby; STM32WLx and SX1276 have no override and report false. +TYPED_TEST(ChipWrapperTest, BoostedGain) { + this->radio.regs[RADIOLIB_SX126X_REG_RX_GAIN] = RADIOLIB_SX126X_RX_GAIN_BOOSTED; + switch (TypeParam::boost) { + case NONE: + EXPECT_FALSE(this->w.setRxBoostedGainMode(true)); + EXPECT_FALSE(this->w.getRxBoostedGainMode()); + EXPECT_EQ(this->take(), Log{}); + break; + case SX126X_REG: + EXPECT_TRUE(this->w.setRxBoostedGainMode(true)); + EXPECT_EQ(this->take(), Log{"setRxBoostedGainMode(1)"}); + EXPECT_TRUE(this->w.getRxBoostedGainMode()); + EXPECT_EQ(this->take(), Log{"readRegister(2220,1)"}); + this->radio.regs[RADIOLIB_SX126X_REG_RX_GAIN] = 0x94; + EXPECT_FALSE(this->w.getRxBoostedGainMode()); + break; + case LR11X0_FLAG: + EXPECT_FALSE(this->w.getRxBoostedGainMode()); + EXPECT_TRUE(this->w.setRxBoostedGainMode(true)); + EXPECT_EQ(this->take(), Log{"setRxBoostedGainMode(1)"}); + EXPECT_TRUE(this->w.getRxBoostedGainMode()); + EXPECT_TRUE(this->w.setRxBoostedGainMode(false)); + EXPECT_FALSE(this->w.getRxBoostedGainMode()); + break; + case LR2021_LEVEL: + EXPECT_TRUE(this->w.setRxBoostedGainMode(true)); + EXPECT_EQ(this->take(), (Log{"standby()", "setRxBoostedGainMode(7)", "standby()"})); + EXPECT_TRUE(this->w.setRxBoostedGainMode(false)); + EXPECT_EQ(this->take(), (Log{"standby()", "setRxBoostedGainMode(0)", "standby()"})); + EXPECT_FALSE(this->w.getRxBoostedGainMode()); + break; + } +} + +// The receiver reset resetAGC() runs when no packet is on the air: the SX126x +// family runs sx126xResetAGC() with its boosted gain state (STM32WLx passes the +// base class's false), LR1110 and LR1121 run lr11x0ResetAGC() around their +// frequency, LR2021 and SX1276 only the base class's warm sleep. +TYPED_TEST(ChipWrapperTest, ResetAGC) { + this->radio.regs[RADIOLIB_SX126X_REG_RX_GAIN] = RADIOLIB_SX126X_RX_GAIN_BOOSTED; + this->radio.freqMHz = 869.5f; + if (TypeParam::boost == LR11X0_FLAG) { + this->w.setRxBoostedGainMode(true); + this->take(); + } + this->w.resetAGC(); + Log c = this->take(); + switch (TypeParam::family) { + case SX126X: { + Log expect; + if (TypeParam::boost == SX126X_REG) expect.push_back("readRegister(2220,1)"); + expect.push_back("sleep(1)"); + expect.push_back("standby(0,1)"); + expect.push_back("calibrateImage(869.5)"); + expect.push_back(TypeParam::boost == SX126X_REG ? "setRxBoostedGainMode(1)" : "setRxBoostedGainMode(0)"); + EXPECT_EQ(c, expect); + EXPECT_EQ(this->mod.halStub.calls, (Log{"SPIwriteStream(137,1,1,0):127", "delay(5)"})); + break; + } + case LR11X0: + EXPECT_EQ(c, (Log{"sleep(1,0)", "standby(0,1)", "calibrate(63)", + "calibrateImageRejection(865.5,873.5)", "setRxBoostedGainMode(1)"})); + break; + case LR2021_: + case SX127X: + EXPECT_EQ(c, Log{"sleep()"}); + break; + } +} + +// After a transmission the LR chips set the preamble length again for the +// current spreading factor; the others only finish the transmission. +TYPED_TEST(ChipWrapperTest, OnSendFinished) { + for (uint8_t sf : {8, 9}) { + this->radio.spreadingFactor = sf; + this->w.onSendFinished(); + Log expect = {"finishTransmit()"}; + if (TypeParam::family == LR11X0 || TypeParam::family == LR2021_) { + expect.push_back(sf <= 8 ? "setPreambleLength(32)" : "setPreambleLength(16)"); + } + EXPECT_EQ(this->take(), expect); + } +} + +// LR1110 and LR1121 estimate at least 200 ms of air time. +TYPED_TEST(ChipWrapperTest, EstimatedAirtime) { + this->radio.time_on_air = 150000; + EXPECT_EQ(this->w.getEstAirtimeFor(20), TypeParam::family == LR11X0 ? 200u : 150u); + this->radio.time_on_air = 300000; + EXPECT_EQ(this->w.getEstAirtimeFor(20), 300u); +} + +// SX1262 powers off with sleep(false), the others with the base class's sleep(). +TYPED_TEST(ChipWrapperTest, PowerOff) { + this->w.powerOff(); + EXPECT_EQ(this->take(), TypeParam::powerOffSleepFalse ? Log{"sleep(0)"} : Log{"sleep()"}); +} + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_radio_chips/stubs/RadioLib.h b/test/test_radio_chips/stubs/RadioLib.h index 7cea666da9a..16567a4743e 100644 --- a/test/test_radio_chips/stubs/RadioLib.h +++ b/test/test_radio_chips/stubs/RadioLib.h @@ -9,6 +9,13 @@ // builds against, so a test that names a bit means the bit the chip reports. // Every call the chip classes make that the tests do not care about is accepted // by a catch-all template and does nothing. +// +// It also carries the PhysicalLayer that RadioLibWrapper talks to, so the chip +// wrappers (CustomWrapper.h) can be built over the real chip classes. The +// calls a wrapper makes, on the chip class or on PhysicalLayer, go into one +// ordered log. Unlike RadioLib, the PhysicalLayer members here are not virtual: +// the log shows the call the wrapper code makes, not where RadioLib would +// dispatch it. #include "Arduino.h" @@ -20,6 +27,7 @@ #include #define RADIOLIB_ERR_NONE 0 +#define RADIOLIB_CHANNEL_FREE (-2) #define RADIOLIB_ERR_SPI_CMD_INVALID (-706) #define RADIOLIB_ERR_SPI_CMD_FAILED (-707) #define RADIOLIB_NC (0xFFFFFFFF) @@ -51,6 +59,9 @@ enum { #define RADIOLIB_SX126X_SYNC_WORD_PRIVATE 0x12 #define RADIOLIB_SX126X_REG_RX_GAIN 0x08AC #define RADIOLIB_SX126X_RX_GAIN_BOOSTED 0x96 +#define RADIOLIB_SX126X_CMD_CALIBRATE 0x89 +#define RADIOLIB_SX126X_STANDBY_RC 0x00 +#define RADIOLIB_SX126X_CALIBRATE_ALL 0b01111111 // LR11x0 family (LR11x0_commands.h) #define RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED (0x01UL << 4) @@ -58,6 +69,8 @@ enum { #define RADIOLIB_LR11X0_IRQ_HEADER_ERR (0x01UL << 6) #define RADIOLIB_LR11X0_RX_TIMEOUT_INF (0xFFFFFFUL) #define RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE (0x12) +#define RADIOLIB_LR11X0_CALIBRATE_ALL (0x3FUL << 0) +#define RADIOLIB_LR11X0_STANDBY_RC (0x00UL << 0) // LR2021 (LR2021_commands.h) #define RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED (0x01UL << 5) @@ -66,6 +79,14 @@ enum { #define RADIOLIB_LR2021_RX_TIMEOUT_INF (0xFFFFFFUL) #define RADIOLIB_LR2021_LORA_SYNC_WORD_PRIVATE (0x12) +// LR2021.h +struct LR2021LoRaSideDetector_t { + uint8_t sf; + bool ldro; + bool invertIQ; + uint8_t syncWord; +}; + // Formats a call and its arguments for the call logs below, so a test can pin // which calls a chip class makes, with which arguments, in which order. inline std::string stubArg(double v) { char b[32]; snprintf(b, sizeof(b), "%g", v); return b; } @@ -86,20 +107,84 @@ class SPIClass { template void setPins(A... a) { calls.push_back(stubCall("setPins", a...)); } }; +// The pins and SPI access sx126xResetAGC() and CustomSX1276::tryScanChannel() +// reach through chip->mod. Logged on their own, since the module does not know +// its chip. +class StubHal { +public: + std::vector calls; + void delay(unsigned long ms) { calls.push_back(stubCall("delay", ms)); } + uint32_t digitalRead(uint32_t) { return 0; } + void yield() { } +}; + class Module { public: + StubHal halStub; + StubHal* hal = &halStub; Module() { } + uint32_t getGpio() { return 4; } + uint32_t getIrq() { return 5; } + int16_t SPIwriteStream(uint16_t cmd, const uint8_t* data, size_t len, bool waitForGpio = true, bool verify = true) { + std::string c = stubCall("SPIwriteStream", cmd, len, waitForGpio, verify); + for (size_t i = 0; i < len; i++) c += ":" + std::to_string(data[i]); + hal->calls.push_back(c); + return RADIOLIB_ERR_NONE; + } +}; + +// The members of RadioLib's PhysicalLayer that RadioLibWrapper calls, as in +// test/mocks/RadioLib.h, with the calls that change the radio logged. +class PhysicalLayer { +public: + virtual ~PhysicalLayer() = default; + + // The configuration calls, in order, with their arguments: those std_init() + // makes on the chip class, and those a wrapper makes on either. + std::vector calls; + + float rssi = -100.0f; // the last packet's RSSI + float snr = 0.0f; + uint32_t time_on_air = 1000; + size_t packet_length = 0; + int16_t scan_result = RADIOLIB_CHANNEL_FREE; + size_t preamble_length = 0; + int8_t output_power = 0; + uint8_t random_byte = 0x5A; + + int16_t sleep() { calls.push_back("sleep()"); return RADIOLIB_ERR_NONE; } + int16_t standby() { calls.push_back("standby()"); return RADIOLIB_ERR_NONE; } + int16_t startReceive() { return RADIOLIB_ERR_NONE; } + int16_t startTransmit(uint8_t*, size_t) { return RADIOLIB_ERR_NONE; } + int16_t finishTransmit() { calls.push_back("finishTransmit()"); return RADIOLIB_ERR_NONE; } + size_t getPacketLength() { return packet_length; } + int16_t readData(uint8_t*, size_t) { return RADIOLIB_ERR_NONE; } + float getRSSI() { return rssi; } + float getSNR() { return snr; } + uint32_t getTimeOnAir(size_t) { return time_on_air; } + int16_t scanChannel() { return scan_result; } + int16_t setOutputPower(int8_t dbm) { output_power = dbm; return RADIOLIB_ERR_NONE; } + void setPacketReceivedAction(void (*)(void)) { } + int16_t setPreambleLength(size_t len) { + calls.push_back(stubCall("setPreambleLength", len)); + preamble_length = len; + return RADIOLIB_ERR_NONE; + } + uint8_t randomByte() { return random_byte; } + int32_t random(int32_t upper) { return upper / 2; } }; using STM32WLx_Module = Module; using SX1262_Module = Module; // One base for every chip: the IRQ register is a plain field a test sets, and // every clear the chip class asks for is recorded and applied to it. -class StubChip { +class StubChip : public PhysicalLayer { public: - explicit StubChip(Module*) { } + explicit StubChip(Module* m) : mod(m) { } virtual ~StubChip() = default; + Module* mod; + uint32_t irq = 0; std::vector clears; @@ -125,12 +210,28 @@ class StubChip { virtual size_t getPacketLength(bool) { return 0; } virtual int16_t setOutputPower(int8_t) { return RADIOLIB_ERR_NONE; } int16_t setOutputPower(int8_t, bool) { return RADIOLIB_ERR_NONE; } - virtual int16_t setFrequency(float) { return RADIOLIB_ERR_NONE; } + virtual int16_t setFrequency(float freq) { calls.push_back(stubCall("setFrequency", freq)); return RADIOLIB_ERR_NONE; } template int16_t begin(A...) { return RADIOLIB_ERR_NONE; } - template int16_t standby(A...) { return RADIOLIB_ERR_NONE; } - // The configuration calls std_init() makes, in order, with their arguments. - std::vector calls; + template int16_t standby(A... a) { calls.push_back(stubCall("standby", a...)); return RADIOLIB_ERR_NONE; } + template int16_t sleep(A... a) { calls.push_back(stubCall("sleep", a...)); return RADIOLIB_ERR_NONE; } + + // What the wrappers read and set. + float rssi_now = -111.5f; // getRSSI(false), the SX126x and SX127x instant RSSI + float rssi_inst = -97.5f; // what getRssiInst() reports on the LR chips + bool rssi_inst_written = true; // whether getRssiInst() writes its result + float getRSSI(bool packet = true) { return packet ? rssi : rssi_now; } + int16_t getRssiInst(float* out) { + if (rssi_inst_written) *out = rssi_inst; + return RADIOLIB_ERR_NONE; + } + template int16_t setSpreadingFactor(A... a) { calls.push_back(stubCall("setSpreadingFactor", a...)); return RADIOLIB_ERR_NONE; } + template int16_t setBandwidth(A... a) { calls.push_back(stubCall("setBandwidth", a...)); return RADIOLIB_ERR_NONE; } + template int16_t setCodingRate(A... a) { calls.push_back(stubCall("setCodingRate", a...)); return RADIOLIB_ERR_NONE; } + // The calls of the two reset helpers. + template int16_t calibrateImage(A... a) { calls.push_back(stubCall("calibrateImage", a...)); return RADIOLIB_ERR_NONE; } + template int16_t calibrate(A... a) { calls.push_back(stubCall("calibrate", a...)); return RADIOLIB_ERR_NONE; } + template int16_t calibrateImageRejection(A... a) { calls.push_back(stubCall("calibrateImageRejection", a...)); return RADIOLIB_ERR_NONE; } // Register contents readRegister() returns and writeRegister() changes. std::map regs; @@ -186,4 +287,20 @@ class LR2021 : public StubChip { public: using StubChip::StubChip; uint8_t irqDioNum = 0; + int16_t setSideDetector(LR2021LoRaSideDetector_t* det, uint8_t num) { + std::string c = stubCall("setSideDetector", num); + for (uint8_t i = 0; i < num; i++) c += ":" + std::to_string(det[i].sf); + calls.push_back(c); + return RADIOLIB_ERR_NONE; + } +}; +// SX127x family: the modem status register stands in for the IRQ flags. +class SX127x : public StubChip { +public: + using StubChip::StubChip; + uint8_t modem_status = 0; + uint8_t getModemStatus() { return modem_status; } + template int16_t startChannelScan(A...) { return RADIOLIB_ERR_NONE; } }; +class SX1278 : public SX127x { public: using SX127x::SX127x; }; +class SX1276 : public SX1278 { public: using SX1278::SX1278; }; diff --git a/test/test_variant_target_esp32/test_variant_target_esp32.cpp b/test/test_variant_target_esp32/test_variant_target_esp32.cpp new file mode 100644 index 00000000000..82e3b757299 --- /dev/null +++ b/test/test_variant_target_esp32/test_variant_target_esp32.cpp @@ -0,0 +1,211 @@ +#include + +#include + +#include +#include "target.h" + +// The radio, wrapper and RTC globals and radio_init() of the 22 ESP32 variants +// of cut D2, pinned on the host: which RadioLib Module constructor the radio is +// built with (pins, own SPI bus and its host, or none), what radio_driver +// wraps, which clock rtc_clock falls back to, and how radio_init() starts them. +// Built in four environments, one per board description that occurs (see +// platformio.ini); each compiles the variant blocks of its description, +// verbatim. See docs/refactoring/0011-variant-target.md, "Cut D". + +mesh::Identity::Identity() { memset(pub_key, 0, sizeof(pub_key)); } +mesh::LocalIdentity::LocalIdentity(mesh::RNG* rng) { rng->random(pub_key, SEED_SIZE); } +void AutoDiscoverRTCClock::begin(TwoWire& wire) { + target_calls.push_back(&wire == &Wire ? "rtc_clock.begin(Wire)" : "rtc_clock.begin(other)"); +} +uint32_t AutoDiscoverRTCClock::getCurrentTime() { return _fallback->getCurrentTime(); } +void AutoDiscoverRTCClock::setCurrentTime(uint32_t time) { _fallback->setCurrentTime(time); } + +TestBoard board; + +// No target.h declares the fallback clock; the test needs it to see where +// rtc_clock falls back to. +extern ESP32RTCClock fallback_clock; + +// The same block and body once more, as the reference the cases are checked +// against on every commit, whatever defines the globals above. +namespace copy { +#if defined(USE_SX1276) +#if defined(P_LORA_SCLK) + static SPIClass spi; + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1); +#endif +#elif defined(P_LORA_SPI_HOST) +#if defined(P_LORA_SCLK) + static SPIClass spi(FSPI); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif +#else +#if defined(P_LORA_SCLK) + static SPIClass spi; + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + +#if defined(P_LORA_SCLK) + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} +} // namespace copy + +// The blocks of the variants that have their own bus in every environment and +// so write it without the #if (lilygo_t3s3, meshadventurer and the two nibble +// boards; lilygo_tlora_v2_1 with the SX1276), the bus of lilygo_tlora_c6 (own +// host 0, the branch its #if takes), and the radio_init() body four of them +// write without the #if (lilygo_t3s3, lilygo_tbeam_SX1262 and the two nibble +// boards). Only where their description is this environment's. +#if defined(P_LORA_SCLK) +namespace unconditional { +#if defined(USE_SX1276) +static SPIClass spi; +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); +#elif defined(P_LORA_SPI_HOST) + static SPIClass spi(0); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else +static SPIClass spi; +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + return radio.std_init(&spi); +} +} // namespace unconditional +#endif + +namespace { + +struct Globals { + const char* name; + TestRadioWrapper* driver; + AutoDiscoverRTCClock* rtc; + ESP32RTCClock* fallback; + bool (*init)(); +}; + +std::string name_of(const ::testing::TestParamInfo& info) { return info.param.name; } + +class RadioGlobals : public ::testing::TestWithParam { +protected: + TestRadio* radio() { return static_cast(GetParam().driver->wrapped_radio()); } + void SetUp() override { + target_calls.clear(); + if (radio()) { + radio()->std_init_calls = 0; + radio()->std_init_result = true; + } + } +}; + +TEST_P(RadioGlobals, RadioIsBuiltFromAModule) { + ASSERT_NE(nullptr, radio()); + EXPECT_NE(nullptr, radio()->module); +} + +TEST_P(RadioGlobals, ModuleHasTheBoardPinsInTheChipsOrder) { + ASSERT_NE(nullptr, radio()->module); + EXPECT_EQ((uint32_t)P_LORA_NSS, radio()->module->cs); +#if defined(USE_SX1276) + EXPECT_EQ((uint32_t)P_LORA_DIO_0, radio()->module->irq); + EXPECT_EQ((uint32_t)P_LORA_RESET, radio()->module->rst); + EXPECT_EQ((uint32_t)P_LORA_DIO_1, radio()->module->gpio); +#else + EXPECT_EQ((uint32_t)P_LORA_DIO_1, radio()->module->irq); + EXPECT_EQ((uint32_t)P_LORA_RESET, radio()->module->rst); + EXPECT_EQ((uint32_t)P_LORA_BUSY, radio()->module->gpio); +#endif +} + +TEST_P(RadioGlobals, ModuleBus) { + ASSERT_NE(nullptr, radio()->module); +#if defined(P_LORA_SCLK) + // An own bus, passed to RadioLib, on the variant's host or the default one. + ASSERT_NE(nullptr, radio()->module->spi); + EXPECT_NE(&SPI, radio()->module->spi); +#if defined(P_LORA_SPI_HOST) + EXPECT_EQ(FSPI, radio()->module->spi->bus); +#else + EXPECT_EQ(HSPI, radio()->module->spi->bus); +#endif +#else + // No bus: RadioLib starts its default bus itself. + EXPECT_EQ(nullptr, radio()->module->spi); +#endif +} + +TEST_P(RadioGlobals, DriverWrapsTheBoard) { + EXPECT_EQ(&board, GetParam().driver->wrapped_board()); +} + +TEST_P(RadioGlobals, RtcClockFallsBackToTheFallbackClock) { + GetParam().fallback->setCurrentTime(1234567); + EXPECT_EQ(1234567u, GetParam().rtc->getCurrentTime()); + GetParam().rtc->setCurrentTime(7654321); + EXPECT_EQ(7654321u, GetParam().fallback->getCurrentTime()); +} + +TEST_P(RadioGlobals, InitStartsTheFallbackClockThenTheRtcThenTheRadio) { + EXPECT_TRUE(GetParam().init()); +#if defined(P_LORA_SCLK) + std::vector expected = {"fallback_clock.begin()", "rtc_clock.begin(Wire)", "std_init(module bus)"}; +#else + std::vector expected = {"fallback_clock.begin()", "rtc_clock.begin(Wire)", "std_init()"}; +#endif + EXPECT_EQ(expected, target_calls); + EXPECT_EQ(1, radio()->std_init_calls); +} + +TEST_P(RadioGlobals, InitReturnsFalseWhenTheRadioFails) { + radio()->std_init_result = false; + EXPECT_FALSE(GetParam().init()); + EXPECT_EQ(3u, target_calls.size()); +} + +INSTANTIATE_TEST_SUITE_P(Copy, RadioGlobals, + ::testing::Values(Globals{"Copy", ©::radio_driver, ©::rtc_clock, ©::fallback_clock, ©::radio_init}), name_of); + +#if defined(P_LORA_SCLK) +INSTANTIATE_TEST_SUITE_P(Unconditional, RadioGlobals, + ::testing::Values(Globals{"Unconditional", &unconditional::radio_driver, &unconditional::rtc_clock, + &unconditional::fallback_clock, &unconditional::radio_init}), name_of); +#endif + +// The globals and radio_init() the variant gets, whoever defines them. +INSTANTIATE_TEST_SUITE_P(Variant, RadioGlobals, + ::testing::Values(Globals{"Variant", &radio_driver, &rtc_clock, &fallback_clock, &radio_init}), name_of); + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_variant_target_esp32_override/test_variant_target_esp32_override.cpp b/test/test_variant_target_esp32_override/test_variant_target_esp32_override.cpp new file mode 100644 index 00000000000..646dec2aed1 --- /dev/null +++ b/test/test_variant_target_esp32_override/test_variant_target_esp32_override.cpp @@ -0,0 +1,41 @@ +#include + +#include + +#include +#include "target.h" + +// An ESP32 variant that takes its globals from RadioLibTarget.cpp but keeps its +// own radio_init(), as a downstream fork's variant may. Its definition must take +// precedence over the shared default, which is built into this binary as well; +// if the default were not weak, this suite would not link. + +mesh::Identity::Identity() { memset(pub_key, 0, sizeof(pub_key)); } +mesh::LocalIdentity::LocalIdentity(mesh::RNG* rng) { rng->random(pub_key, SEED_SIZE); } +void AutoDiscoverRTCClock::begin(TwoWire&) { target_calls.push_back("rtc_clock.begin(Wire)"); } +uint32_t AutoDiscoverRTCClock::getCurrentTime() { return _fallback->getCurrentTime(); } +void AutoDiscoverRTCClock::setCurrentTime(uint32_t time) { _fallback->setCurrentTime(time); } + +TestBoard board; + +bool radio_init() { + target_calls.push_back("variant radio_init()"); + return false; +} + +namespace { + +TEST(VariantTargetEsp32Override, VariantRadioInitTakesPrecedence) { + target_calls.clear(); + EXPECT_FALSE(radio_init()); + std::vector expected = {"variant radio_init()"}; + EXPECT_EQ(expected, target_calls); + EXPECT_EQ(0, static_cast(radio_driver.wrapped_radio())->std_init_calls); +} + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_variant_target_globals/test_variant_target_globals.cpp b/test/test_variant_target_globals/test_variant_target_globals.cpp new file mode 100644 index 00000000000..2aecd2befff --- /dev/null +++ b/test/test_variant_target_globals/test_variant_target_globals.cpp @@ -0,0 +1,123 @@ +#include + +#include + +#include +#include +#include "target.h" + +// The radio, wrapper and RTC globals of a variant's target.cpp, pinned on the +// host: which RadioLib Module constructor the radio is built with (pins and SPI +// bus), what radio_driver wraps, and which clock rtc_clock falls back to. See +// docs/refactoring/0011-variant-target.md, "Cut D". + +// Stand-ins for src/Identity.cpp (Ed25519) and for the out-of-line members of +// AutoDiscoverRTCClock (the RTC chip libraries). The real getCurrentTime() and +// setCurrentTime() use the fallback clock when no RTC chip answered, which is +// the case these tests look at; begin() is recorded as the variant stand-in's +// RTC clock does. +mesh::Identity::Identity() { memset(pub_key, 0, sizeof(pub_key)); } +mesh::LocalIdentity::LocalIdentity(mesh::RNG* rng) { rng->random(pub_key, SEED_SIZE); } +void AutoDiscoverRTCClock::begin(TwoWire& wire) { + target_calls.push_back(&wire == &Wire ? "rtc_clock.begin(Wire)" : "rtc_clock.begin(other)"); +} +uint32_t AutoDiscoverRTCClock::getCurrentTime() { return _fallback->getCurrentTime(); } +void AutoDiscoverRTCClock::setCurrentTime(uint32_t time) { _fallback->setCurrentTime(time); } + +TestBoard board; + +// No target.h declares the fallback clock; the test needs it to see where +// rtc_clock falls back to. +extern VolatileRTCClock fallback_clock; + +// The same block once more, as the reference the cases are checked against on +// every commit, whatever defines the globals above. +namespace copy { +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +} // namespace copy + +namespace { + +struct Globals { + const char* name; + TestRadioWrapper* driver; + AutoDiscoverRTCClock* rtc; + VolatileRTCClock* fallback; +}; + +std::string name_of(const ::testing::TestParamInfo& info) { return info.param.name; } + +class RadioGlobals : public ::testing::TestWithParam { +protected: + TestRadio* radio() { return static_cast(GetParam().driver->wrapped_radio()); } +}; + +TEST_P(RadioGlobals, RadioIsBuiltFromAModule) { + ASSERT_NE(nullptr, radio()); + EXPECT_NE(nullptr, radio()->module); +} + +TEST_P(RadioGlobals, ModuleHasTheBoardPinsInSx126xOrder) { + ASSERT_NE(nullptr, radio()->module); + EXPECT_EQ((uint32_t)P_LORA_NSS, radio()->module->cs); + EXPECT_EQ((uint32_t)P_LORA_DIO_1, radio()->module->irq); + EXPECT_EQ((uint32_t)P_LORA_RESET, radio()->module->rst); + EXPECT_EQ((uint32_t)P_LORA_BUSY, radio()->module->gpio); +} + +TEST_P(RadioGlobals, ModuleIsOnTheDefaultSpiBusPassedExplicitly) { + // Passed, not left to RadioLib: with the bus as argument RadioLib does not + // start it itself, std_init() does. + ASSERT_NE(nullptr, radio()->module); + EXPECT_EQ(&SPI, radio()->module->spi); +} + +TEST_P(RadioGlobals, DriverWrapsTheBoard) { + EXPECT_EQ(&board, GetParam().driver->wrapped_board()); +} + +TEST_P(RadioGlobals, RtcClockFallsBackToTheFallbackClock) { + GetParam().fallback->setCurrentTime(1234567); + EXPECT_EQ(1234567u, GetParam().rtc->getCurrentTime()); + GetParam().rtc->setCurrentTime(7654321); + EXPECT_EQ(7654321u, GetParam().fallback->getCurrentTime()); +} + +TEST_P(RadioGlobals, FallbackClockCountsMillisWhenTheRtcClockTicks) { + // What VolatileRTCClock does, and AutoDiscoverRTCClock::tick() forwards to it. + GetParam().fallback->setCurrentTime(1000); + g_mock_millis += 4000; + GetParam().rtc->tick(); + EXPECT_EQ(1004u, GetParam().fallback->getCurrentTime()); +} + +INSTANTIATE_TEST_SUITE_P(Copy, RadioGlobals, + ::testing::Values(Globals{"Copy", ©::radio_driver, ©::rtc_clock, ©::fallback_clock}), name_of); + +// The globals the variant gets, whoever defines them. +INSTANTIATE_TEST_SUITE_P(Variant, RadioGlobals, + ::testing::Values(Globals{"Variant", &radio_driver, &rtc_clock, &fallback_clock}), name_of); + +TEST(RadioGlobalsBoot, RadioInitStartsTheRtcThenTheWrappedRadioOnSpi) { + // Cut C's shared radio_init() on these globals: the boot path of the 31. + target_calls.clear(); + TestRadio* r = static_cast(radio_driver.wrapped_radio()); + r->std_init_calls = 0; + r->std_init_result = true; + EXPECT_TRUE(radio_init()); + std::vector expected = {"rtc_clock.begin(Wire)", "std_init(&SPI)"}; + EXPECT_EQ(expected, target_calls); + EXPECT_EQ(1, r->std_init_calls); +} + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tools/refactoring/scripts/wrapper_inventory.py b/tools/refactoring/scripts/wrapper_inventory.py new file mode 100644 index 00000000000..316a82a5554 --- /dev/null +++ b/tools/refactoring/scripts/wrapper_inventory.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Measure how far the chip wrappers in src/helpers/radiolib differ. + +Usage: python3 tools/refactoring/scripts/wrapper_inventory.py [repo root] + +Cuts every member out of the eight CustomWrapper.h files, removes comments +and white space and replaces the chip name with a placeholder, then prints per +member which wrappers carry an identical copy (groups separated by " / ", +"absent" for wrappers without the member). The table at the end lists, per +wrapper, which members it overrides at all. +""" +import hashlib +import re +import sys +from pathlib import Path + +CHIPS = ["SX1262", "SX1268", "LLCC68", "STM32WLx", "LR1110", "LR1121", "LR2021", "SX1276"] + + +def normalize(text, chip): + text = re.sub(r"/\*.*?\*/", "", text, flags=re.S) + text = re.sub(r"//[^\n]*", "", text) + text = text.replace("Custom" + chip, "Custom@CHIP@").replace(chip, "@CHIP@") + lines = [re.sub(r"\s+", " ", l).strip() for l in text.splitlines()] + return [l for l in lines if l] + + +def members(lines): + """Split the normalized class body into members by brace depth.""" + out, cur, depth, in_class = {}, [], 0, False + for l in lines: + if not in_class: + if l.startswith("class "): + in_class = True + depth = l.count("{") - l.count("}") + continue + if depth == 1 and not cur and l in ("public:", "protected:", "private:", "};"): + continue + cur.append(l) + depth += l.count("{") - l.count("}") + if depth == 1 and (l.endswith("}") or l.endswith(";")): + sig = cur[0] + name = re.search(r"([~\w]+)\s*\(", sig) + key = name.group(1) if name else re.sub(r"\s*(=.*)?;$", "", sig).split()[-1] + key = key.replace("@CHIP@", "") or "(constructor)" + if key == "Wrapper" or (key.startswith("Custom") and key.endswith("Wrapper")): + key = "(constructor)" + out[key] = cur + cur = [] + return out + + +def main(): + root = Path(sys.argv[1] if len(sys.argv) > 1 else ".") + parsed = {} + for chip in CHIPS: + src = (root / "src/helpers/radiolib" / f"Custom{chip}Wrapper.h").read_text() + parsed[chip] = members(normalize(src, chip)) + + names = [] + for chip in CHIPS: + for n in parsed[chip]: + if n not in names: + names.append(n) + + print(f"{'member':<22} groups of identical copies (normalized lines)") + for n in names: + groups = {} + for chip in CHIPS: + b = parsed[chip].get(n) + h = hashlib.sha1("\n".join(b).encode()).hexdigest()[:8] if b is not None else "absent" + groups.setdefault(h, []).append(chip) + parts = [] + for h, cs in groups.items(): + if h == "absent": + parts.append(", ".join(cs) + " (absent)") + else: + parts.append(", ".join(cs) + f" ({len(parsed[cs[0]][n])})") + print(f"{n:<22} {' / '.join(parts)}") + + print() + print("whole wrapper bodies, identical after normalization:") + whole = {} + for chip in CHIPS: + h = hashlib.sha1(repr(sorted(parsed[chip].items())).encode()).hexdigest()[:8] + whole.setdefault(h, []).append(chip) + for cs in whole.values(): + print(" " + ", ".join(cs)) + + +if __name__ == "__main__": + main() diff --git a/tools/refactoring/variant_target_globals.py b/tools/refactoring/variant_target_globals.py new file mode 100644 index 00000000000..ca70492a7eb --- /dev/null +++ b/tools/refactoring/variant_target_globals.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +"""Inventory of the radio, wrapper and RTC globals in the variants' target.cpp. + +Used for cut D of step 11 of the refactoring series +(docs/refactoring/0011-variant-target.md). Python 3 standard library plus the +`pio` command; run from the repository root: + + python3 tools/refactoring/variant_target_globals.py [--detail] + +For every variants/*/target.cpp it extracts the "radio block": the declarations +of the SPI object, `radio`, `radio_driver`, `fallback_clock` and `rtc_clock`, +together with the preprocessor lines around them, from the first of those lines +to the last. Blocks are compared normalized (comments and white space removed) +and grouped. For each group it reports the variants, their firmware environments +(an environment belongs to a variant when its build_src_filter names +variants/), the platforms and whether the block is contiguous (nothing but +those declarations and preprocessor lines between the first and the last). +It then does the same for the bodies of radio_init(), and lists for every +variant which of `radio`, `spi` and `fallback_clock` its target.cpp still names +once the radio block and a radio_init() body that a shared default can take over +are gone (the variants that name none can take their globals from shared code). +""" + +import collections +import glob +import json +import re +import subprocess +import sys + +DECL = re.compile( + r"^\s*(?:static\s+)?(?:" + r"SPIClass\s+\w+|" + r"RADIO_CLASS\s+radio\b|" + r"WRAPPER_CLASS\s+radio_driver\b|" + r"\w*RTCClock\s+fallback_clock\b|" + r"\w*RTCClock\s+rtc_clock\b)" +) +PREPROC = re.compile(r"^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b") + + +def strip_comments(text): + text = re.sub(r"/\*.*?\*/", "", text, flags=re.S) + return re.sub(r"//[^\n]*", "", text) + + +def norm(line): + return re.sub(r"\s+", " ", line).strip() + + +def radio_block(src): + """(block lines, contiguous, depth problem) of the radio declarations.""" + lines = [norm(l) for l in strip_comments(src).splitlines()] + lines = [l for l in lines if l] + idx = [i for i, l in enumerate(lines) if DECL.match(l)] + if not idx: + return [], True, [] + first, last = idx[0], idx[-1] + # widen to the enclosing preprocessor lines that open/close inside the block + depth, lo = 0, first + for i in range(first, last + 1): + m = PREPROC.match(lines[i]) + if m and m.group(1) in ("if", "ifdef", "ifndef"): + depth += 1 + elif m and m.group(1) == "endif": + depth -= 1 + hi = last + while depth > 0 and hi + 1 < len(lines): + hi += 1 + m = PREPROC.match(lines[hi]) + if m and m.group(1) in ("if", "ifdef", "ifndef"): + depth += 1 + elif m and m.group(1) == "endif": + depth -= 1 + while depth < 0 and lo > 0: + lo -= 1 + m = PREPROC.match(lines[lo]) + if m and m.group(1) in ("if", "ifdef", "ifndef"): + depth += 1 + elif m and m.group(1) == "endif": + depth -= 1 + block = lines[lo:hi + 1] + foreign = [l for l in block if not DECL.match(l) and not PREPROC.match(l)] + # preprocessor context the first declaration sits in (unclosed #if before it) + ctx = [] + for l in lines[:lo]: + m = PREPROC.match(l) + if m and m.group(1) in ("if", "ifdef", "ifndef"): + ctx.append(l) + elif m and m.group(1) == "endif" and ctx: + ctx.pop() + return block, not foreign, ctx + + +def function_body(src, name): + src = strip_comments(src) + m = re.search(r"^[^\n;]*\b" + re.escape(name) + r"\s*\(\s*\)\s*\{", src, re.M) + if m is None: + return None + depth, i = 1, m.end() + while depth: + depth += (src[i] == "{") - (src[i] == "}") + i += 1 + return norm(src[m.end():i - 1]) + + +# radio_init() bodies a shared default can take over: none (cut C's nRF52 +# default), or the two ESP32 bodies that differ only where P_LORA_SCLK is +# undefined, which their variants' blocks never are. +SHAREABLE_INIT = { + None, + "fallback_clock.begin(); rtc_clock.begin(Wire); #if defined(P_LORA_SCLK) " + "return radio.std_init(&spi); #else return radio.std_init(); #endif", + "fallback_clock.begin(); rtc_clock.begin(Wire); return radio.std_init(&spi);", +} + + +def remaining_refs(src, init_body): + """Names of the radio block target.cpp still uses outside the block.""" + src = strip_comments(src) + rest = "\n".join(l for l in src.splitlines() if not DECL.match(norm(l))) + if init_body in SHAREABLE_INIT: + m = re.search(r"bool\s+radio_init\s*\(\s*\)\s*\{", rest) + if m: + depth, i = 1, m.end() + while depth: + depth += (rest[i] == "{") - (rest[i] == "}") + i += 1 + rest = rest[:m.start()] + rest[i:] + return sorted(set(re.findall(r"\b(radio|spi|fallback_clock)\b", rest))) + + +def environments(): + out = subprocess.run(["pio", "project", "config", "--json-output"], + capture_output=True, text=True, check=True).stdout + envs = {} + for section, items in json.loads(out): + if not section.startswith("env:"): + continue + kv = dict(items) + flags = " ".join(kv.get("build_flags", [])) + envs[section[4:]] = { + "src": " ".join(kv.get("build_src_filter", [])), + "platform": kv.get("platform", ""), + "flags": flags, + } + return envs + + +def platform_name(p): + if "espressif32" in p: + return "ESP32" + if "nordicnrf52" in p: + return "nRF52" + if "raspberrypi" in p: + return "RP2040" + if "ststm32" in p: + return "STM32" + return p or "native" + + +def flag(flags, name): + m = re.search(r"-D\s*" + name + r"(?:=(\S+))?", flags) + return None if m is None else (m.group(1) or "1") + + +def main(): + detail = "--detail" in sys.argv + envs = environments() + variants = sorted(p.split("/")[1] for p in glob.glob("variants/*/target.cpp")) + info = {} + for v in variants: + src = open(f"variants/{v}/target.cpp", encoding="utf-8", errors="replace").read() + block, contiguous, ctx = radio_block(src) + mine = {e: d for e, d in envs.items() + if re.search(r"variants/" + re.escape(v) + r"(?:>|/)", d["src"])} + info[v] = { + "block": block, "contiguous": contiguous, "ctx": ctx, + "envs": mine, + "platforms": sorted({platform_name(d["platform"]) for d in mine.values()}), + "sclk": sorted({flag(d["flags"], "P_LORA_SCLK") is not None for d in mine.values()}), + "wrapper": sorted({flag(d["flags"], "WRAPPER_CLASS") or "-" for d in mine.values()}), + "radio_init": function_body(src, "radio_init"), + } + info[v]["refs"] = remaining_refs(src, info[v]["radio_init"]) + + print(f"target.cpp: {len(variants)}, firmware environments: " + f"{sum(len(i['envs']) for i in info.values())} of {len(envs)}") + print() + print("== radio blocks (normalized) ==") + groups = collections.defaultdict(list) + for v, i in info.items(): + groups[tuple(i["block"])].append(v) + for block, vs in sorted(groups.items(), key=lambda kv: -len(kv[1])): + nenv = sum(len(info[v]["envs"]) for v in vs) + plats = sorted({p for v in vs for p in info[v]["platforms"]}) + cont = sum(info[v]["contiguous"] for v in vs) + ctx = sorted({" / ".join(info[v]["ctx"]) for v in vs if info[v]["ctx"]}) + sclk = sorted({str(s) for v in vs for s in info[v]["sclk"]}) + print(f"-- {len(vs)} variants, {nenv} envs, {','.join(plats)}, contiguous {cont}/{len(vs)}," + f" P_LORA_SCLK in envs: {'/'.join(sclk)}" + (f", inside: {ctx}" if ctx else "")) + for l in block: + print(f" {l}") + print(f" variants: {', '.join(vs)}") + print() + print("== radio_init() bodies (normalized), by platform ==") + rg = collections.defaultdict(list) + for v, i in info.items(): + if i["radio_init"] is not None: + rg[(",".join(i["platforms"]), i["radio_init"])].append(v) + for (plat, body), vs in sorted(rg.items(), key=lambda kv: (kv[0][0], -len(kv[1]))): + nenv = sum(len(info[v]["envs"]) for v in vs) + print(f"-- {plat}: {len(vs)} variants, {nenv} envs") + print(f" {body}") + print(f" variants: {', '.join(vs)}") + without = [v for v, i in info.items() if i["radio_init"] is None] + print(f"-- without own radio_init() (cut C default or none): {len(without)}") + print() + print("== variants whose target.cpp names no radio global once block and shareable radio_init() are gone ==") + cand = collections.defaultdict(list) + for v, i in info.items(): + if not i["refs"] and i["block"] and "radio_driver" in " ".join(i["block"]): + cand[(",".join(i["platforms"]), i["radio_init"] is None)].append(v) + for (plat, default), vs in sorted(cand.items()): + nenv = sum(len(info[v]["envs"]) for v in vs) + how = "radio_init() already shared" if default else "own radio_init() shareable" + print(f"-- {plat}, {how}: {len(vs)} variants, {nenv} envs") + print(f" {', '.join(vs)}") + rest = [f"{v} ({'/'.join(i['refs'])})" for v, i in info.items() if i["refs"]] + print(f"-- still naming them: {len(rest)}: {', '.join(rest)}") + if detail: + print() + for v, i in info.items(): + print(v, i["platforms"], len(i["envs"]), "sclk", i["sclk"], i["wrapper"]) + + +if __name__ == "__main__": + main() diff --git a/variants/gat562_30s_mesh_kit/target.cpp b/variants/gat562_30s_mesh_kit/target.cpp index 11ee09f07aa..c635aad5323 100644 --- a/variants/gat562_30s_mesh_kit/target.cpp +++ b/variants/gat562_30s_mesh_kit/target.cpp @@ -18,13 +18,6 @@ GAT56230SMeshKitBoard board; #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/gat562_30s_mesh_kit/target.h b/variants/gat562_30s_mesh_kit/target.h index d526a5d0379..eef1b5b1e5a 100644 --- a/variants/gat562_30s_mesh_kit/target.h +++ b/variants/gat562_30s_mesh_kit/target.h @@ -19,6 +19,7 @@ #endif extern GAT56230SMeshKitBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/gat562_mesh_evb_pro/target.cpp b/variants/gat562_mesh_evb_pro/target.cpp index d1b021c8cf4..b198c5e2949 100644 --- a/variants/gat562_mesh_evb_pro/target.cpp +++ b/variants/gat562_mesh_evb_pro/target.cpp @@ -17,13 +17,6 @@ GAT562EVBProBoard board; #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/gat562_mesh_evb_pro/target.h b/variants/gat562_mesh_evb_pro/target.h index 9111704fca2..d01a95ef84f 100644 --- a/variants/gat562_mesh_evb_pro/target.h +++ b/variants/gat562_mesh_evb_pro/target.h @@ -18,6 +18,7 @@ #endif extern GAT562EVBProBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/gat562_mesh_tracker_pro/target.cpp b/variants/gat562_mesh_tracker_pro/target.cpp index ad85c842f2d..57f7fef3e2c 100644 --- a/variants/gat562_mesh_tracker_pro/target.cpp +++ b/variants/gat562_mesh_tracker_pro/target.cpp @@ -18,13 +18,6 @@ GAT562MeshTrackerProBoard board; #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/gat562_mesh_tracker_pro/target.h b/variants/gat562_mesh_tracker_pro/target.h index 0d3b7d76ea9..6627d110529 100644 --- a/variants/gat562_mesh_tracker_pro/target.h +++ b/variants/gat562_mesh_tracker_pro/target.h @@ -19,6 +19,7 @@ #endif extern GAT562MeshTrackerProBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/gat562_mesh_watch13/target.cpp b/variants/gat562_mesh_watch13/target.cpp index dddd24485ab..b996b021016 100644 --- a/variants/gat562_mesh_watch13/target.cpp +++ b/variants/gat562_mesh_watch13/target.cpp @@ -15,11 +15,5 @@ GAT56MeshWatch13Board board; #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors; diff --git a/variants/gat562_mesh_watch13/target.h b/variants/gat562_mesh_watch13/target.h index 9ed5cda7172..1932114be60 100644 --- a/variants/gat562_mesh_watch13/target.h +++ b/variants/gat562_mesh_watch13/target.h @@ -20,6 +20,7 @@ #endif extern GAT56MeshWatch13Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/generic-e22/target.cpp b/variants/generic-e22/target.cpp index 0e7e69d0a3e..d76285b2249 100644 --- a/variants/generic-e22/target.cpp +++ b/variants/generic-e22/target.cpp @@ -3,26 +3,4 @@ ESP32Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - - #if defined(P_LORA_SCLK) - return radio.std_init(&spi); - #else - return radio.std_init(); - #endif -} diff --git a/variants/generic-e22/target.h b/variants/generic-e22/target.h index 16612f67b6c..a8c7749428d 100644 --- a/variants/generic-e22/target.h +++ b/variants/generic-e22/target.h @@ -10,6 +10,7 @@ #include extern ESP32Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/heltec_e213/target.cpp b/variants/heltec_e213/target.cpp index 12e22b30a8c..a836c1f5c6e 100644 --- a/variants/heltec_e213/target.cpp +++ b/variants/heltec_e213/target.cpp @@ -3,32 +3,9 @@ HeltecE213Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi(FSPI); - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display(&board.periph_power); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_e213/target.h b/variants/heltec_e213/target.h index 6f2c291bb8d..26fc647e0a5 100644 --- a/variants/heltec_e213/target.h +++ b/variants/heltec_e213/target.h @@ -13,6 +13,8 @@ #endif extern HeltecE213Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp +#define P_LORA_SPI_HOST FSPI // the radio's own SPI bus extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/heltec_e290/target.cpp b/variants/heltec_e290/target.cpp index bcfdbcdba26..8612d534df4 100644 --- a/variants/heltec_e290/target.cpp +++ b/variants/heltec_e290/target.cpp @@ -3,32 +3,9 @@ HeltecE290Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi(FSPI); - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display(&board.periph_power); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_e290/target.h b/variants/heltec_e290/target.h index d78521fcfb8..26d807cdee5 100644 --- a/variants/heltec_e290/target.h +++ b/variants/heltec_e290/target.h @@ -13,6 +13,8 @@ #endif extern HeltecE290Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp +#define P_LORA_SPI_HOST FSPI // the radio's own SPI bus extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/heltec_mesh_solar/target.cpp b/variants/heltec_mesh_solar/target.cpp index c89eea6b800..10110b0c170 100644 --- a/variants/heltec_mesh_solar/target.cpp +++ b/variants/heltec_mesh_solar/target.cpp @@ -6,12 +6,6 @@ MeshSolarBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); SolarSensorManager sensors = SolarSensorManager(nmea); SolarExternalWatchdog external_watchdog; diff --git a/variants/heltec_mesh_solar/target.h b/variants/heltec_mesh_solar/target.h index 8e13a9601c5..48261a9eb0a 100644 --- a/variants/heltec_mesh_solar/target.h +++ b/variants/heltec_mesh_solar/target.h @@ -41,6 +41,7 @@ class SolarExternalWatchdog : public ExternalWatchdogManager { }; extern MeshSolarBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SolarSensorManager sensors; diff --git a/variants/heltec_rc32/target.cpp b/variants/heltec_rc32/target.cpp index be6d73b87bc..e47fb35e81f 100644 --- a/variants/heltec_rc32/target.cpp +++ b/variants/heltec_rc32/target.cpp @@ -6,18 +6,6 @@ HeltecRC32Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi(FSPI); - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, PIN_GPS_RESET, PIN_GPS_EN); @@ -34,14 +22,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); RotaryInput& rotary_input = rotaryInputImpl; #endif #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_rc32/target.h b/variants/heltec_rc32/target.h index 04cb6d94a49..34d2c66f114 100644 --- a/variants/heltec_rc32/target.h +++ b/variants/heltec_rc32/target.h @@ -21,6 +21,8 @@ #endif extern HeltecRC32Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp +#define P_LORA_SPI_HOST FSPI // the radio's own SPI bus extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_t096/target.cpp b/variants/heltec_t096/target.cpp index 34452dfbf3f..f84fd9efd9c 100644 --- a/variants/heltec_t096/target.cpp +++ b/variants/heltec_t096/target.cpp @@ -9,17 +9,6 @@ T096Board board; -#if defined(P_LORA_SCLK) -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); -#else -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); diff --git a/variants/heltec_t096/target.h b/variants/heltec_t096/target.h index 137d8db5ec4..297db2ca159 100644 --- a/variants/heltec_t096/target.h +++ b/variants/heltec_t096/target.h @@ -17,6 +17,7 @@ #endif extern T096Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_t1/target.cpp b/variants/heltec_t1/target.cpp index cc372637d90..1c76573ee2b 100644 --- a/variants/heltec_t1/target.cpp +++ b/variants/heltec_t1/target.cpp @@ -9,17 +9,6 @@ T1Board board; -#if defined(P_LORA_SCLK) -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); -#else -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/heltec_t1/target.h b/variants/heltec_t1/target.h index ffd9434fac4..6868e9790af 100644 --- a/variants/heltec_t1/target.h +++ b/variants/heltec_t1/target.h @@ -17,6 +17,7 @@ #endif extern T1Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_t114/target.cpp b/variants/heltec_t114/target.cpp index 625812d2d56..ab5787fb222 100644 --- a/variants/heltec_t114/target.cpp +++ b/variants/heltec_t114/target.cpp @@ -9,17 +9,6 @@ T114Board board; -#if defined(P_LORA_SCLK) -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); -#else -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/heltec_t114/target.h b/variants/heltec_t114/target.h index 5aac84dbe22..8f9510a335d 100644 --- a/variants/heltec_t114/target.h +++ b/variants/heltec_t114/target.h @@ -19,6 +19,7 @@ #endif extern T114Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_t190/target.cpp b/variants/heltec_t190/target.cpp index 3c32f047eb4..7fcc8a8dd46 100644 --- a/variants/heltec_t190/target.cpp +++ b/variants/heltec_t190/target.cpp @@ -3,32 +3,9 @@ HeltecT190Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi(FSPI); - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_t190/target.h b/variants/heltec_t190/target.h index cde63220a84..597a6a91e91 100644 --- a/variants/heltec_t190/target.h +++ b/variants/heltec_t190/target.h @@ -13,6 +13,8 @@ #endif extern HeltecT190Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp +#define P_LORA_SPI_HOST FSPI // the radio's own SPI bus extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/heltec_tower_v2/target.cpp b/variants/heltec_tower_v2/target.cpp index 54c2621e80b..647a774b0b4 100644 --- a/variants/heltec_tower_v2/target.cpp +++ b/variants/heltec_tower_v2/target.cpp @@ -6,12 +6,6 @@ HeltecTowerV2Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); TowerV2ExternalWatchdog external_watchdog; diff --git a/variants/heltec_tower_v2/target.h b/variants/heltec_tower_v2/target.h index 0b9de40ca44..f61ab0d5c9b 100644 --- a/variants/heltec_tower_v2/target.h +++ b/variants/heltec_tower_v2/target.h @@ -25,6 +25,7 @@ class TowerV2ExternalWatchdog : public ExternalWatchdogManager { }; extern HeltecTowerV2Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_tracker/target.cpp b/variants/heltec_tracker/target.cpp index 7d88a33210e..2fbe32740b7 100644 --- a/variants/heltec_tracker/target.cpp +++ b/variants/heltec_tracker/target.cpp @@ -6,17 +6,6 @@ HeltecV3Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); // GPS_EN (GPIO35) drives N-ch MOSFET → P-ch high-side switch; GPS_RESET (GPIO36) active LOW MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); HWTSensorManager sensors = HWTSensorManager(nmea); @@ -26,18 +15,6 @@ HWTSensorManager sensors = HWTSensorManager(nmea); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif - -} - void HWTSensorManager::start_gps() { if (!gps_active) { _location->begin(); // Claims periph_power via RefCountedDigitalPin diff --git a/variants/heltec_tracker/target.h b/variants/heltec_tracker/target.h index 2931eda816d..16578dca7c6 100644 --- a/variants/heltec_tracker/target.h +++ b/variants/heltec_tracker/target.h @@ -32,6 +32,7 @@ class HWTSensorManager : public SensorManager { }; extern HeltecV3Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern HWTSensorManager sensors; diff --git a/variants/heltec_tracker_v2/target.cpp b/variants/heltec_tracker_v2/target.cpp index 6d042913666..6498a1858be 100644 --- a/variants/heltec_tracker_v2/target.cpp +++ b/variants/heltec_tracker_v2/target.cpp @@ -3,18 +3,6 @@ HeltecTrackerV2Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); @@ -27,14 +15,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display(&board.periph_power); // peripheral power pin is shared MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_tracker_v2/target.h b/variants/heltec_tracker_v2/target.h index 240d9d4c58e..e37da63405f 100644 --- a/variants/heltec_tracker_v2/target.h +++ b/variants/heltec_tracker_v2/target.h @@ -14,6 +14,7 @@ #endif extern HeltecTrackerV2Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_v2/target.cpp b/variants/heltec_v2/target.cpp index 5698f4095cc..1a1de17d181 100644 --- a/variants/heltec_v2/target.cpp +++ b/variants/heltec_v2/target.cpp @@ -3,31 +3,9 @@ HeltecV2Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_v2/target.h b/variants/heltec_v2/target.h index cdffc18a172..0cf22352044 100644 --- a/variants/heltec_v2/target.h +++ b/variants/heltec_v2/target.h @@ -13,6 +13,7 @@ #endif extern HeltecV2Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/heltec_v3/target.cpp b/variants/heltec_v3/target.cpp index b467bd98c5a..9c6f5b1f4e7 100644 --- a/variants/heltec_v3/target.cpp +++ b/variants/heltec_v3/target.cpp @@ -3,18 +3,6 @@ HeltecV3Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); @@ -27,14 +15,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_v3/target.h b/variants/heltec_v3/target.h index 2944b384356..1743af20e0f 100644 --- a/variants/heltec_v3/target.h +++ b/variants/heltec_v3/target.h @@ -14,6 +14,7 @@ #endif extern HeltecV3Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_v4/target.cpp b/variants/heltec_v4/target.cpp index 9f163e1a8db..a29bd70daa4 100644 --- a/variants/heltec_v4/target.cpp +++ b/variants/heltec_v4/target.cpp @@ -3,18 +3,6 @@ HeltecV4Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); @@ -27,14 +15,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display(NULL); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_v4/target.h b/variants/heltec_v4/target.h index 5d668186b2b..dfc446de52e 100644 --- a/variants/heltec_v4/target.h +++ b/variants/heltec_v4/target.h @@ -18,6 +18,7 @@ #endif extern HeltecV4Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_v4_r8/target.cpp b/variants/heltec_v4_r8/target.cpp index 2f5390c5961..8d18c3ce05f 100644 --- a/variants/heltec_v4_r8/target.cpp +++ b/variants/heltec_v4_r8/target.cpp @@ -3,18 +3,6 @@ HeltecV4R8Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); @@ -27,14 +15,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display(&board.periph_power); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_v4_r8/target.h b/variants/heltec_v4_r8/target.h index 2d1d7a5bb45..a83052f300a 100644 --- a/variants/heltec_v4_r8/target.h +++ b/variants/heltec_v4_r8/target.h @@ -18,6 +18,7 @@ #endif extern HeltecV4R8Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/heltec_wireless_paper/target.cpp b/variants/heltec_wireless_paper/target.cpp index 91ee526f119..0373576cd6e 100644 --- a/variants/heltec_wireless_paper/target.cpp +++ b/variants/heltec_wireless_paper/target.cpp @@ -3,31 +3,9 @@ HeltecWirelessPaperBoard board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/heltec_wireless_paper/target.h b/variants/heltec_wireless_paper/target.h index 7206678181b..b6e77d2b914 100644 --- a/variants/heltec_wireless_paper/target.h +++ b/variants/heltec_wireless_paper/target.h @@ -19,6 +19,7 @@ class HeltecWirelessPaperBoard : public HeltecV3Board { #endif extern HeltecWirelessPaperBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/ikoka_handheld_nrf/target.cpp b/variants/ikoka_handheld_nrf/target.cpp index 6f658f83195..1de48ae35b2 100644 --- a/variants/ikoka_handheld_nrf/target.cpp +++ b/variants/ikoka_handheld_nrf/target.cpp @@ -4,13 +4,6 @@ IkokaNrf52Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS diff --git a/variants/ikoka_handheld_nrf/target.h b/variants/ikoka_handheld_nrf/target.h index 18f7c9c5ee0..f11dca027ce 100644 --- a/variants/ikoka_handheld_nrf/target.h +++ b/variants/ikoka_handheld_nrf/target.h @@ -18,6 +18,7 @@ extern IkokaNrf52Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/ikoka_nano_nrf/target.cpp b/variants/ikoka_nano_nrf/target.cpp index 85177c24a05..cd329c78e1a 100644 --- a/variants/ikoka_nano_nrf/target.cpp +++ b/variants/ikoka_nano_nrf/target.cpp @@ -9,10 +9,4 @@ IkokaNanoNRFBoard board; // MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors; diff --git a/variants/ikoka_nano_nrf/target.h b/variants/ikoka_nano_nrf/target.h index 30ee5cb69ee..83b8d5e0d6b 100644 --- a/variants/ikoka_nano_nrf/target.h +++ b/variants/ikoka_nano_nrf/target.h @@ -17,6 +17,7 @@ #endif extern IkokaNanoNRFBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/ikoka_stick_nrf/target.cpp b/variants/ikoka_stick_nrf/target.cpp index c48f677de60..5f4607a7fb9 100644 --- a/variants/ikoka_stick_nrf/target.cpp +++ b/variants/ikoka_stick_nrf/target.cpp @@ -9,10 +9,4 @@ IkokaStickNRFBoard board; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors; diff --git a/variants/ikoka_stick_nrf/target.h b/variants/ikoka_stick_nrf/target.h index 46b05e8b2e0..97f7984b20f 100644 --- a/variants/ikoka_stick_nrf/target.h +++ b/variants/ikoka_stick_nrf/target.h @@ -17,6 +17,7 @@ #endif extern IkokaStickNRFBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/keepteen_lt1/target.cpp b/variants/keepteen_lt1/target.cpp index 396c2fa711e..a6bf135bcad 100644 --- a/variants/keepteen_lt1/target.cpp +++ b/variants/keepteen_lt1/target.cpp @@ -4,12 +4,6 @@ KeepteenLT1Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/keepteen_lt1/target.h b/variants/keepteen_lt1/target.h index 5b73bb66847..151cea2b2c7 100644 --- a/variants/keepteen_lt1/target.h +++ b/variants/keepteen_lt1/target.h @@ -14,6 +14,7 @@ #include extern KeepteenLT1Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_t3s3/target.cpp b/variants/lilygo_t3s3/target.cpp index 2679f6f914e..6dcf5878d5e 100644 --- a/variants/lilygo_t3s3/target.cpp +++ b/variants/lilygo_t3s3/target.cpp @@ -3,12 +3,6 @@ ESP32Board board; -static SPIClass spi; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; #ifdef DISPLAY_CLASS @@ -19,10 +13,3 @@ SensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - - return radio.std_init(&spi); -} diff --git a/variants/lilygo_t3s3/target.h b/variants/lilygo_t3s3/target.h index 965cdd1a2e5..490f4654f8f 100644 --- a/variants/lilygo_t3s3/target.h +++ b/variants/lilygo_t3s3/target.h @@ -13,6 +13,7 @@ #endif extern ESP32Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/lilygo_t_impulse_plus/target.cpp b/variants/lilygo_t_impulse_plus/target.cpp index cb34ae6e83f..0aa8b3b75dd 100644 --- a/variants/lilygo_t_impulse_plus/target.cpp +++ b/variants/lilygo_t_impulse_plus/target.cpp @@ -5,13 +5,6 @@ TImpulsePlusBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/lilygo_t_impulse_plus/target.h b/variants/lilygo_t_impulse_plus/target.h index ff168fbedf4..e52f12c91b3 100644 --- a/variants/lilygo_t_impulse_plus/target.h +++ b/variants/lilygo_t_impulse_plus/target.h @@ -14,6 +14,7 @@ #endif extern TImpulsePlusBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_tbeam_SX1262/target.cpp b/variants/lilygo_tbeam_SX1262/target.cpp index d5ea9ad1f8c..25baf47ecec 100644 --- a/variants/lilygo_tbeam_SX1262/target.cpp +++ b/variants/lilygo_tbeam_SX1262/target.cpp @@ -3,18 +3,6 @@ TBeamBoard board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); @@ -27,9 +15,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - return radio.std_init(&spi); -} diff --git a/variants/lilygo_tbeam_SX1262/target.h b/variants/lilygo_tbeam_SX1262/target.h index c532a73af9a..2674694f315 100644 --- a/variants/lilygo_tbeam_SX1262/target.h +++ b/variants/lilygo_tbeam_SX1262/target.h @@ -13,6 +13,7 @@ #endif extern TBeamBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_tbeam_SX1276/target.cpp b/variants/lilygo_tbeam_SX1276/target.cpp index 35b2c0af3fc..feb4c5fdfb4 100644 --- a/variants/lilygo_tbeam_SX1276/target.cpp +++ b/variants/lilygo_tbeam_SX1276/target.cpp @@ -3,18 +3,6 @@ LilygoTBeamSX1276Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); @@ -27,14 +15,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/lilygo_tbeam_SX1276/target.h b/variants/lilygo_tbeam_SX1276/target.h index 0d56f88f8b9..3184c2b8e6d 100644 --- a/variants/lilygo_tbeam_SX1276/target.h +++ b/variants/lilygo_tbeam_SX1276/target.h @@ -13,6 +13,7 @@ #endif extern LilygoTBeamSX1276Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_techo/target.cpp b/variants/lilygo_techo/target.cpp index e3f797e5c94..e1451599afa 100644 --- a/variants/lilygo_techo/target.cpp +++ b/variants/lilygo_techo/target.cpp @@ -5,13 +5,6 @@ TechoBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/lilygo_techo/target.h b/variants/lilygo_techo/target.h index 2b8a1783f06..1388853e1eb 100644 --- a/variants/lilygo_techo/target.h +++ b/variants/lilygo_techo/target.h @@ -15,6 +15,7 @@ #endif extern TechoBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_techo_card/target.cpp b/variants/lilygo_techo_card/target.cpp index 15059da30f7..637157982fb 100644 --- a/variants/lilygo_techo_card/target.cpp +++ b/variants/lilygo_techo_card/target.cpp @@ -5,13 +5,6 @@ TechoCardBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/lilygo_techo_card/target.h b/variants/lilygo_techo_card/target.h index f394f7dafb0..8a210b0479a 100644 --- a/variants/lilygo_techo_card/target.h +++ b/variants/lilygo_techo_card/target.h @@ -15,6 +15,7 @@ #endif extern TechoCardBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_techo_lite/target.cpp b/variants/lilygo_techo_lite/target.cpp index 6bff6ad95dd..4d49d478fb6 100644 --- a/variants/lilygo_techo_lite/target.cpp +++ b/variants/lilygo_techo_lite/target.cpp @@ -5,13 +5,6 @@ TechoBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/lilygo_techo_lite/target.h b/variants/lilygo_techo_lite/target.h index 2b8a1783f06..1388853e1eb 100644 --- a/variants/lilygo_techo_lite/target.h +++ b/variants/lilygo_techo_lite/target.h @@ -15,6 +15,7 @@ #endif extern TechoBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_tlora_c6/target.cpp b/variants/lilygo_tlora_c6/target.cpp index 02dfcd52a35..d76285b2249 100644 --- a/variants/lilygo_tlora_c6/target.cpp +++ b/variants/lilygo_tlora_c6/target.cpp @@ -3,26 +3,4 @@ ESP32Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi(0); - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/lilygo_tlora_c6/target.h b/variants/lilygo_tlora_c6/target.h index 81e99ee3ab8..1af86e66ae8 100644 --- a/variants/lilygo_tlora_c6/target.h +++ b/variants/lilygo_tlora_c6/target.h @@ -9,6 +9,8 @@ #include extern ESP32Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp +#define P_LORA_SPI_HOST 0 // the radio's own SPI bus extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/lilygo_tlora_v2_1/target.cpp b/variants/lilygo_tlora_v2_1/target.cpp index 00ae597b84c..3f43f6e1a9b 100644 --- a/variants/lilygo_tlora_v2_1/target.cpp +++ b/variants/lilygo_tlora_v2_1/target.cpp @@ -3,27 +3,9 @@ LilyGoTLoraBoard board; -static SPIClass spi; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/lilygo_tlora_v2_1/target.h b/variants/lilygo_tlora_v2_1/target.h index fa954b329b1..75de03a5e3f 100644 --- a/variants/lilygo_tlora_v2_1/target.h +++ b/variants/lilygo_tlora_v2_1/target.h @@ -13,6 +13,7 @@ #endif extern LilyGoTLoraBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/meshadventurer/target.cpp b/variants/meshadventurer/target.cpp index 8241cac144d..4a42b965906 100644 --- a/variants/meshadventurer/target.cpp +++ b/variants/meshadventurer/target.cpp @@ -6,12 +6,6 @@ MeshadventurerBoard board; -static SPIClass spi; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); MASensorManager sensors = MASensorManager(nmea); @@ -20,17 +14,6 @@ MASensorManager sensors = MASensorManager(nmea); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} - void MASensorManager::start_gps() { if(!gps_active) { diff --git a/variants/meshadventurer/target.h b/variants/meshadventurer/target.h index 0749565d363..b051e1c80f0 100644 --- a/variants/meshadventurer/target.h +++ b/variants/meshadventurer/target.h @@ -32,6 +32,7 @@ class MASensorManager : public SensorManager { }; extern MeshadventurerBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern MASensorManager sensors; diff --git a/variants/meshtiny/target.cpp b/variants/meshtiny/target.cpp index 179ed75d31a..61726bf2ba8 100644 --- a/variants/meshtiny/target.cpp +++ b/variants/meshtiny/target.cpp @@ -5,12 +5,6 @@ MeshtinyBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(); #ifdef DISPLAY_CLASS diff --git a/variants/meshtiny/target.h b/variants/meshtiny/target.h index 3987e18c29b..3546d64521f 100644 --- a/variants/meshtiny/target.h +++ b/variants/meshtiny/target.h @@ -14,6 +14,7 @@ #include extern MeshtinyBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/muziworks_r1_neo/target.cpp b/variants/muziworks_r1_neo/target.cpp index 401eab17b0e..f9d82328ff1 100644 --- a/variants/muziworks_r1_neo/target.cpp +++ b/variants/muziworks_r1_neo/target.cpp @@ -4,9 +4,6 @@ R1NeoBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); -WRAPPER_CLASS radio_driver(radio, board); - #ifdef DISPLAY_CLASS NullDisplayDriver display; #endif @@ -15,9 +12,6 @@ WRAPPER_CLASS radio_driver(radio, board); MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/muziworks_r1_neo/target.h b/variants/muziworks_r1_neo/target.h index 5bf3f268503..676a95ebdaa 100644 --- a/variants/muziworks_r1_neo/target.h +++ b/variants/muziworks_r1_neo/target.h @@ -9,6 +9,7 @@ #include extern R1NeoBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/nano_g2_ultra/target.cpp b/variants/nano_g2_ultra/target.cpp index 2e3c9c5622c..c86ddc83dc6 100644 --- a/variants/nano_g2_ultra/target.cpp +++ b/variants/nano_g2_ultra/target.cpp @@ -7,12 +7,6 @@ NanoG2Ultra board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); NanoG2UltraSensorManager sensors = NanoG2UltraSensorManager(nmea); diff --git a/variants/nano_g2_ultra/target.h b/variants/nano_g2_ultra/target.h index 5c6ebee11df..c34f9975dfb 100644 --- a/variants/nano_g2_ultra/target.h +++ b/variants/nano_g2_ultra/target.h @@ -33,6 +33,7 @@ class NanoG2UltraSensorManager : public SensorManager { }; extern NanoG2Ultra board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern NanoG2UltraSensorManager sensors; diff --git a/variants/nibble_screen_connect/target.cpp b/variants/nibble_screen_connect/target.cpp index 6609338e757..c46e511a831 100644 --- a/variants/nibble_screen_connect/target.cpp +++ b/variants/nibble_screen_connect/target.cpp @@ -3,12 +3,6 @@ ESP32Board board; -static SPIClass spi; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; #ifdef DISPLAY_CLASS @@ -19,10 +13,3 @@ SensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - - return radio.std_init(&spi); -} diff --git a/variants/nibble_screen_connect/target.h b/variants/nibble_screen_connect/target.h index dcbcf8e1dff..1b65464d200 100644 --- a/variants/nibble_screen_connect/target.h +++ b/variants/nibble_screen_connect/target.h @@ -13,6 +13,7 @@ #endif extern ESP32Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/nibble_zero_connect/target.cpp b/variants/nibble_zero_connect/target.cpp index 2679f6f914e..6dcf5878d5e 100644 --- a/variants/nibble_zero_connect/target.cpp +++ b/variants/nibble_zero_connect/target.cpp @@ -3,12 +3,6 @@ ESP32Board board; -static SPIClass spi; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; #ifdef DISPLAY_CLASS @@ -19,10 +13,3 @@ SensorManager sensors; #ifndef LORA_CR #define LORA_CR 5 #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - - return radio.std_init(&spi); -} diff --git a/variants/nibble_zero_connect/target.h b/variants/nibble_zero_connect/target.h index 87c55f0f2e1..24da363ec24 100644 --- a/variants/nibble_zero_connect/target.h +++ b/variants/nibble_zero_connect/target.h @@ -13,6 +13,7 @@ #endif extern ESP32Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index a476bf7e487..1a03e838246 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -4,12 +4,6 @@ PromicroBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/promicro/target.h b/variants/promicro/target.h index e577ab4e979..35f0bb6251b 100644 --- a/variants/promicro/target.h +++ b/variants/promicro/target.h @@ -14,6 +14,7 @@ #include extern PromicroBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/rak3112/target.cpp b/variants/rak3112/target.cpp index 214426d474c..42076afc8e7 100644 --- a/variants/rak3112/target.cpp +++ b/variants/rak3112/target.cpp @@ -3,18 +3,6 @@ RAK3112Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); @@ -27,14 +15,3 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/rak3112/target.h b/variants/rak3112/target.h index f93cd53996b..4388532e45c 100644 --- a/variants/rak3112/target.h +++ b/variants/rak3112/target.h @@ -14,6 +14,7 @@ #endif extern RAK3112Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/rak3401/target.cpp b/variants/rak3401/target.cpp index a1a73588577..ff8af0a137f 100644 --- a/variants/rak3401/target.cpp +++ b/variants/rak3401/target.cpp @@ -17,13 +17,6 @@ RAK3401Board board; #endif #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/rak3401/target.h b/variants/rak3401/target.h index 00653e90e5e..758d6c7895a 100644 --- a/variants/rak3401/target.h +++ b/variants/rak3401/target.h @@ -19,6 +19,7 @@ #endif extern RAK3401Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index b7691dd26c3..ce7b9b2adf4 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -17,13 +17,6 @@ RAK4631Board board; #endif #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index 1529f904246..d5599f79340 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -19,6 +19,7 @@ #endif extern RAK4631Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/rak_wismesh_tag/target.cpp b/variants/rak_wismesh_tag/target.cpp index 95f8961eb2e..84475ef5e40 100644 --- a/variants/rak_wismesh_tag/target.cpp +++ b/variants/rak_wismesh_tag/target.cpp @@ -13,13 +13,6 @@ RAKWismeshTagBoard board; MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); #endif -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #if ENV_INCLUDE_GPS #include MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/rak_wismesh_tag/target.h b/variants/rak_wismesh_tag/target.h index fbe18d2d11a..65b902e995e 100644 --- a/variants/rak_wismesh_tag/target.h +++ b/variants/rak_wismesh_tag/target.h @@ -16,6 +16,7 @@ #endif extern RAKWismeshTagBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/sensecap_solar/target.cpp b/variants/sensecap_solar/target.cpp index 01574e9f796..0203af79824 100644 --- a/variants/sensecap_solar/target.cpp +++ b/variants/sensecap_solar/target.cpp @@ -6,12 +6,6 @@ #endif SenseCapSolarBoard board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/sensecap_solar/target.h b/variants/sensecap_solar/target.h index a1c01f1368d..276aee702f4 100644 --- a/variants/sensecap_solar/target.h +++ b/variants/sensecap_solar/target.h @@ -10,6 +10,7 @@ #include extern SenseCapSolarBoard board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/tenstar_c3/target.cpp b/variants/tenstar_c3/target.cpp index b800e2df6c4..13400fecf76 100644 --- a/variants/tenstar_c3/target.cpp +++ b/variants/tenstar_c3/target.cpp @@ -3,26 +3,4 @@ XiaoC3Board board; -#if defined(P_LORA_SCLK) - static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif - -WRAPPER_CLASS radio_driver(radio, board); - -ESP32RTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; - -bool radio_init() { - fallback_clock.begin(); - rtc_clock.begin(Wire); - -#if defined(P_LORA_SCLK) - return radio.std_init(&spi); -#else - return radio.std_init(); -#endif -} diff --git a/variants/tenstar_c3/target.h b/variants/tenstar_c3/target.h index 7839fa973aa..1700fcaa92b 100644 --- a/variants/tenstar_c3/target.h +++ b/variants/tenstar_c3/target.h @@ -10,6 +10,7 @@ #include extern XiaoC3Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/thinknode_m1/target.cpp b/variants/thinknode_m1/target.cpp index 3a0a7f04f98..f12cb32d345 100644 --- a/variants/thinknode_m1/target.cpp +++ b/variants/thinknode_m1/target.cpp @@ -6,12 +6,6 @@ ThinkNodeM1Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); ThinkNodeM1SensorManager sensors = ThinkNodeM1SensorManager(nmea); diff --git a/variants/thinknode_m1/target.h b/variants/thinknode_m1/target.h index ec1297beffc..d41dbaaaaf2 100644 --- a/variants/thinknode_m1/target.h +++ b/variants/thinknode_m1/target.h @@ -33,6 +33,7 @@ class ThinkNodeM1SensorManager : public SensorManager { }; extern ThinkNodeM1Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern ThinkNodeM1SensorManager sensors; diff --git a/variants/thinknode_m6/target.cpp b/variants/thinknode_m6/target.cpp index f8258af1d02..335896c3846 100644 --- a/variants/thinknode_m6/target.cpp +++ b/variants/thinknode_m6/target.cpp @@ -5,12 +5,6 @@ ThinkNodeM6Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/thinknode_m6/target.h b/variants/thinknode_m6/target.h index 76188e584e0..30150b0790b 100644 --- a/variants/thinknode_m6/target.h +++ b/variants/thinknode_m6/target.h @@ -15,6 +15,7 @@ #endif extern ThinkNodeM6Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/thinknode_m8/target.cpp b/variants/thinknode_m8/target.cpp index 5bf588139a1..6ef6e9d8780 100644 --- a/variants/thinknode_m8/target.cpp +++ b/variants/thinknode_m8/target.cpp @@ -6,12 +6,6 @@ ThinkNodeM8Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/thinknode_m8/target.h b/variants/thinknode_m8/target.h index c3402a01a7e..17d98cbfbcd 100644 --- a/variants/thinknode_m8/target.h +++ b/variants/thinknode_m8/target.h @@ -16,6 +16,7 @@ #endif extern ThinkNodeM8Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/wio-tracker-l1-1w/target.cpp b/variants/wio-tracker-l1-1w/target.cpp index 1170282a50f..c7100385d09 100644 --- a/variants/wio-tracker-l1-1w/target.cpp +++ b/variants/wio-tracker-l1-1w/target.cpp @@ -5,13 +5,6 @@ WioTrackerL1Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/wio-tracker-l1-1w/target.h b/variants/wio-tracker-l1-1w/target.h index 05bc7ff1cae..7d918811a23 100644 --- a/variants/wio-tracker-l1-1w/target.h +++ b/variants/wio-tracker-l1-1w/target.h @@ -19,6 +19,7 @@ #include extern WioTrackerL1Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/wio-tracker-l1/target.cpp b/variants/wio-tracker-l1/target.cpp index 1170282a50f..c7100385d09 100644 --- a/variants/wio-tracker-l1/target.cpp +++ b/variants/wio-tracker-l1/target.cpp @@ -5,13 +5,6 @@ WioTrackerL1Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); diff --git a/variants/wio-tracker-l1/target.h b/variants/wio-tracker-l1/target.h index 05bc7ff1cae..7d918811a23 100644 --- a/variants/wio-tracker-l1/target.h +++ b/variants/wio-tracker-l1/target.h @@ -19,6 +19,7 @@ #include extern WioTrackerL1Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/xiao_nrf52/target.cpp b/variants/xiao_nrf52/target.cpp index ecdcbdb6e9c..932095066e0 100644 --- a/variants/xiao_nrf52/target.cpp +++ b/variants/xiao_nrf52/target.cpp @@ -9,11 +9,4 @@ XiaoNrf52Board board; -RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); - -WRAPPER_CLASS radio_driver(radio, board); - -VolatileRTCClock fallback_clock; -AutoDiscoverRTCClock rtc_clock(fallback_clock); - EnvironmentSensorManager sensors; diff --git a/variants/xiao_nrf52/target.h b/variants/xiao_nrf52/target.h index 20093904aad..504b055ca88 100644 --- a/variants/xiao_nrf52/target.h +++ b/variants/xiao_nrf52/target.h @@ -17,6 +17,7 @@ #endif extern XiaoNrf52Board board; +#define RADIOLIB_TARGET_GLOBALS // radio, radio_driver and rtc_clock come from RadioLibTarget.cpp extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors;