From 783a572c3a03f0bfa55052800e34d4ec1ce01f88 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Wed, 21 Oct 2026 23:03:24 +0200 Subject: [PATCH 01/15] docs(refactoring): design cut D of note 0011, the radio globals from the board description Inventory of the radio blocks in the 89 target.cpp (tools/refactoring/ variant_target_globals.py), why objects cannot be weak, the opt-in macro RADIOLIB_TARGET_GLOBALS in target.h, the split into D1 (31 nRF52) and D2 (22 ESP32 with the shared radio_init()), rejected alternatives and the open upstream pull requests that touch the same lines. --- docs/refactoring/0011-variant-target.md | 235 ++++++++++++++++++- tools/refactoring/variant_target_globals.py | 238 ++++++++++++++++++++ 2 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 tools/refactoring/variant_target_globals.py diff --git a/docs/refactoring/0011-variant-target.md b/docs/refactoring/0011-variant-target.md index b163c54096..0a2d674ad0 100644 --- a/docs/refactoring/0011-variant-target.md +++ b/docs/refactoring/0011-variant-target.md @@ -3,7 +3,8 @@ 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 designed, as two pull requests +(D1 nRF52, D2 ESP32), see "Cut D". Cut E is planned. Behaviour change: none. ## Context @@ -87,7 +88,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 | designed, 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 +1034,236 @@ 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"). + +### 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. + ## Upstream Searched meshcore-dev/MeshCore on 20.10.2026: text queries over all pull requests diff --git a/tools/refactoring/variant_target_globals.py b/tools/refactoring/variant_target_globals.py new file mode 100644 index 0000000000..ca70492a7e --- /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() From 6510e4dae049670a118ab200c6f46700ee847f9a Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Wed, 21 Oct 2026 23:12:26 +0200 Subject: [PATCH 02/15] test(variants): pin the radio, wrapper and RTC globals of 31 nRF52 variants A new host suite (native_variant_target_globals) builds the shared variant code against the target.h of a variant with the real AutoDiscoverRTCClock, with the radio block of the 31 nRF52 variants of cut C copied verbatim: the radio is built from a Module on the board pins in SX126x order with SPI passed, radio_driver wraps it and the board, rtc_clock falls back to and ticks fallback_clock, and the shared radio_init() starts that radio. The variant stand-ins move into variant_stubs.h and gain a Module stand-in. --- platformio.ini | 28 ++++ test/mocks/radiolib_variant/target.h | 52 +------ test/mocks/radiolib_variant/variant_stubs.h | 83 +++++++++++ test/mocks/radiolib_variant_globals/target.h | 19 +++ .../test_variant_target_globals.cpp | 132 ++++++++++++++++++ 5 files changed, 263 insertions(+), 51 deletions(-) create mode 100644 test/mocks/radiolib_variant/variant_stubs.h create mode 100644 test/mocks/radiolib_variant_globals/target.h create mode 100644 test/test_variant_target_globals/test_variant_target_globals.cpp diff --git a/platformio.ini b/platformio.ini index 4bbc58bb5f..f04688a3cd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -182,6 +182,7 @@ test_ignore = test_region_map test_variant_target test_variant_target_override + test_variant_target_globals test_companion_frames build_src_filter = -<*> @@ -521,6 +522,33 @@ 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_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 diff --git a/test/mocks/radiolib_variant/target.h b/test/mocks/radiolib_variant/target.h index 65dcede0fb..736ba0a3be 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/variant_stubs.h b/test/mocks/radiolib_variant/variant_stubs.h new file mode 100644 index 0000000000..54508ca8be --- /dev/null +++ b/test/mocks/radiolib_variant/variant_stubs.h @@ -0,0 +1,83 @@ +#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 + +// What the stand-ins below were asked to do, in order, for the tests to compare. +inline std::vector target_calls; + +// 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 ? "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. +#ifndef USE_SX1262 +#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_globals/target.h b/test/mocks/radiolib_variant_globals/target.h new file mode 100644 index 0000000000..fc1b0c57fd --- /dev/null +++ b/test/mocks/radiolib_variant_globals/target.h @@ -0,0 +1,19 @@ +#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; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity(); 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 0000000000..b825c804f3 --- /dev/null +++ b/test/test_variant_target_globals/test_variant_target_globals.cpp @@ -0,0 +1,132 @@ +#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 radio block of the 31 nRF52 variants of cut D1, verbatim, as their +// target.cpp define it. +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); + +// 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(); +} From a8dc76a6c6db567afe588dcf7e3482eee88ddd0a Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Wed, 21 Oct 2026 23:15:57 +0200 Subject: [PATCH 03/15] refactor(variants): define the radio globals once for 31 nRF52 SX1262 variants RadioLibTarget.cpp defines radio, radio_driver, fallback_clock and rtc_clock for the variants whose target.h defines the new opt-in macro RADIOLIB_TARGET_GLOBALS, built from the P_LORA_* pins: on nRF52 the radio is a Module on SPI, the fallback clock a VolatileRTCClock. Not weak: a weak object would still be constructed into a variant's own one. Other platforms and the SX1276 pin order stop the build with an #error until described. The 31 nRF52 variants of cut C delete their block from target.cpp (deletions only) and add the macro line to target.h (additions only). The host suite now runs its cases against the shared globals. --- src/helpers/radiolib/RadioLibTarget.cpp | 32 ++++++++++++++++++- test/mocks/radiolib_variant_globals/target.h | 1 + .../test_variant_target_globals.cpp | 9 ------ variants/gat562_30s_mesh_kit/target.cpp | 7 ---- variants/gat562_30s_mesh_kit/target.h | 1 + variants/gat562_mesh_evb_pro/target.cpp | 7 ---- variants/gat562_mesh_evb_pro/target.h | 1 + variants/gat562_mesh_tracker_pro/target.cpp | 7 ---- variants/gat562_mesh_tracker_pro/target.h | 1 + variants/gat562_mesh_watch13/target.cpp | 6 ---- variants/gat562_mesh_watch13/target.h | 1 + variants/heltec_mesh_solar/target.cpp | 6 ---- variants/heltec_mesh_solar/target.h | 1 + variants/heltec_t096/target.cpp | 11 ------- variants/heltec_t096/target.h | 1 + variants/heltec_t1/target.cpp | 11 ------- variants/heltec_t1/target.h | 1 + variants/heltec_t114/target.cpp | 11 ------- variants/heltec_t114/target.h | 1 + variants/heltec_tower_v2/target.cpp | 6 ---- variants/heltec_tower_v2/target.h | 1 + variants/ikoka_handheld_nrf/target.cpp | 7 ---- variants/ikoka_handheld_nrf/target.h | 1 + variants/ikoka_nano_nrf/target.cpp | 6 ---- variants/ikoka_nano_nrf/target.h | 1 + variants/ikoka_stick_nrf/target.cpp | 6 ---- variants/ikoka_stick_nrf/target.h | 1 + variants/keepteen_lt1/target.cpp | 6 ---- variants/keepteen_lt1/target.h | 1 + variants/lilygo_t_impulse_plus/target.cpp | 7 ---- variants/lilygo_t_impulse_plus/target.h | 1 + variants/lilygo_techo/target.cpp | 7 ---- variants/lilygo_techo/target.h | 1 + variants/lilygo_techo_card/target.cpp | 7 ---- variants/lilygo_techo_card/target.h | 1 + variants/lilygo_techo_lite/target.cpp | 7 ---- variants/lilygo_techo_lite/target.h | 1 + variants/meshtiny/target.cpp | 6 ---- variants/meshtiny/target.h | 1 + variants/muziworks_r1_neo/target.cpp | 6 ---- variants/muziworks_r1_neo/target.h | 1 + variants/nano_g2_ultra/target.cpp | 6 ---- variants/nano_g2_ultra/target.h | 1 + variants/promicro/target.cpp | 6 ---- variants/promicro/target.h | 1 + variants/rak3401/target.cpp | 7 ---- variants/rak3401/target.h | 1 + variants/rak4631/target.cpp | 7 ---- variants/rak4631/target.h | 1 + variants/rak_wismesh_tag/target.cpp | 7 ---- variants/rak_wismesh_tag/target.h | 1 + variants/sensecap_solar/target.cpp | 6 ---- variants/sensecap_solar/target.h | 1 + variants/thinknode_m1/target.cpp | 6 ---- variants/thinknode_m1/target.h | 1 + variants/thinknode_m6/target.cpp | 6 ---- variants/thinknode_m6/target.h | 1 + variants/thinknode_m8/target.cpp | 6 ---- variants/thinknode_m8/target.h | 1 + variants/wio-tracker-l1-1w/target.cpp | 7 ---- variants/wio-tracker-l1-1w/target.h | 1 + variants/wio-tracker-l1/target.cpp | 7 ---- variants/wio-tracker-l1/target.h | 1 + variants/xiao_nrf52/target.cpp | 7 ---- variants/xiao_nrf52/target.h | 1 + 65 files changed, 63 insertions(+), 225 deletions(-) diff --git a/src/helpers/radiolib/RadioLibTarget.cpp b/src/helpers/radiolib/RadioLibTarget.cpp index a9259e2b18..eb1ae67e4c 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,36 @@ #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 and the platform. 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) + #error "RADIOLIB_TARGET_GLOBALS: the radio globals are described for nRF52 only so far; define them in target.cpp" +#endif +#if defined(USE_SX1276) + #error "RADIOLIB_TARGET_GLOBALS: the SX1276 pin order is not described yet; define the radio in target.cpp" +#endif + +#include +#include +#include + +// On nRF52 the radio is on SPI, passed to RadioLib, so that std_init() and not +// RadioLib starts the bus. +static 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); + +#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_globals/target.h b/test/mocks/radiolib_variant_globals/target.h index fc1b0c57fd..c719c466fe 100644 --- a/test/mocks/radiolib_variant_globals/target.h +++ b/test/mocks/radiolib_variant_globals/target.h @@ -12,6 +12,7 @@ #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; diff --git a/test/test_variant_target_globals/test_variant_target_globals.cpp b/test/test_variant_target_globals/test_variant_target_globals.cpp index b825c804f3..2aecd2beff 100644 --- a/test/test_variant_target_globals/test_variant_target_globals.cpp +++ b/test/test_variant_target_globals/test_variant_target_globals.cpp @@ -30,15 +30,6 @@ TestBoard board; // rtc_clock falls back to. extern VolatileRTCClock fallback_clock; -// The radio block of the 31 nRF52 variants of cut D1, verbatim, as their -// target.cpp define it. -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); - // The same block once more, as the reference the cases are checked against on // every commit, whatever defines the globals above. namespace copy { diff --git a/variants/gat562_30s_mesh_kit/target.cpp b/variants/gat562_30s_mesh_kit/target.cpp index 11ee09f07a..c635aad532 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 d526a5d037..eef1b5b1e5 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 d1b021c8cf..b198c5e294 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 9111704fca..d01a95ef84 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 ad85c842f2..57f7fef3e2 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 0d3b7d76ea..6627d11052 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 dddd24485a..b996b02101 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 9ed5cda717..1932114be6 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/heltec_mesh_solar/target.cpp b/variants/heltec_mesh_solar/target.cpp index c89eea6b80..10110b0c17 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 8e13a9601c..48261a9eb0 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_t096/target.cpp b/variants/heltec_t096/target.cpp index 34452dfbf3..f84fd9efd9 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 137d8db5ec..297db2ca15 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 cc372637d9..1c76573ee2 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 ffd9434fac..6868e9790a 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 625812d2d5..ab5787fb22 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 5aac84dbe2..8f9510a335 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_tower_v2/target.cpp b/variants/heltec_tower_v2/target.cpp index 54c2621e80..647a774b0b 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 0b9de40ca4..f61ab0d5c9 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/ikoka_handheld_nrf/target.cpp b/variants/ikoka_handheld_nrf/target.cpp index 6f658f8319..1de48ae35b 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 18f7c9c5ee..f11dca027c 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 85177c24a0..cd329c78e1 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 30ee5cb69e..83b8d5e0d6 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 c48f677de6..5f4607a7fb 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 46b05e8b2e..97f7984b20 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 396c2fa711..a6bf135bca 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 5b73bb6684..151cea2b2c 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_t_impulse_plus/target.cpp b/variants/lilygo_t_impulse_plus/target.cpp index cb34ae6e83..0aa8b3b75d 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 ff168fbedf..e52f12c91b 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_techo/target.cpp b/variants/lilygo_techo/target.cpp index e3f797e5c9..e1451599af 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 2b8a1783f0..1388853e1e 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 15059da30f..637157982f 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 f394f7dafb..8a210b0479 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 6bff6ad95d..4d49d478fb 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 2b8a1783f0..1388853e1e 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/meshtiny/target.cpp b/variants/meshtiny/target.cpp index 179ed75d31..61726bf2ba 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 3987e18c29..3546d64521 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 401eab17b0..f9d82328ff 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 5bf3f26850..676a95ebda 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 2e3c9c5622..c86ddc83dc 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 5c6ebee11d..c34f9975df 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/promicro/target.cpp b/variants/promicro/target.cpp index a476bf7e48..1a03e83824 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 e577ab4e97..35f0bb6251 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/rak3401/target.cpp b/variants/rak3401/target.cpp index a1a7358857..ff8af0a137 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 00653e90e5..758d6c7895 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 b7691dd26c..ce7b9b2adf 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 1529f90424..d5599f7934 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 95f8961eb2..84475ef5e4 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 fbe18d2d11..65b902e995 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 01574e9f79..0203af7982 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 a1c01f1368..276aee702f 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/thinknode_m1/target.cpp b/variants/thinknode_m1/target.cpp index 3a0a7f04f9..f12cb32d34 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 ec1297beff..d41dbaaaaf 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 f8258af1d0..335896c384 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 76188e584e..30150b0790 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 5bf588139a..6ef6e9d878 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 c3402a01a7..17d98cbfbc 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 1170282a50..c7100385d0 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 05bc7ff1ca..7d918811a2 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 1170282a50..c7100385d0 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 05bc7ff1ca..7d918811a2 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 ecdcbdb6e9..932095066e 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 20093904aa..504b055ca8 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; From 060481b6704f14a3feed3c2547abf13f17752750 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Thu, 22 Oct 2026 00:41:22 +0200 Subject: [PATCH 04/15] docs(refactoring): record cut D1 of note 0011, the radio globals of 31 nRF52 variants --- docs/refactoring/0011-variant-target.md | 161 +++++++++++++++++++++++- 1 file changed, 158 insertions(+), 3 deletions(-) diff --git a/docs/refactoring/0011-variant-target.md b/docs/refactoring/0011-variant-target.md index 0a2d674ad0..fff0e6ac01 100644 --- a/docs/refactoring/0011-variant-target.md +++ b/docs/refactoring/0011-variant-target.md @@ -3,8 +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. Cut D (the radio, wrapper and RTC globals) is designed, as two pull requests -(D1 nRF52, D2 ESP32), see "Cut D". Cut E is planned. Behaviour change: none. +A and B. Cut D (the radio, wrapper and RTC globals) is designed as two pull requests; +D1 (31 nRF52 variants) is implemented, D2 (22 ESP32 variants) follows on top of it, +see "Cut D". Cut E is planned. Behaviour change: none. ## Context @@ -88,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 description (D1: 31 nRF52, D2: 22 ESP32 variants) | 53 `target.cpp` and `target.h`, 1 source | designed, see "Cut D" | +| 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 implemented, D2 next, 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, @@ -1264,6 +1265,160 @@ file list), and for every pull request that touches one of the 53 `target.cpp` o - 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 | + +
+ ## Upstream Searched meshcore-dev/MeshCore on 20.10.2026: text queries over all pull requests From 02978dd3e6248e9b05408caf0e67123e65521237 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Wed, 21 Oct 2026 23:21:56 +0200 Subject: [PATCH 05/15] test(variants): pin the radio globals and radio_init() of 22 ESP32 variants A host suite in four environments, one per board description that occurs among the 22 ESP32 variants of cut D2 (SX1262 on an own bus on the default host, without P_LORA_SCLK, SX1276, own bus on another host), with their blocks and radio_init() bodies copied verbatim: the Module's pins in the chip's order, its bus (own, on which host, or none), what radio_driver wraps, the fallback clock, and that radio_init() starts the fallback clock, the RTC and then the radio on its bus, passing the result through. The SPI stand-in gets bus numbers, the radio stand-in tells the module's bus apart. --- platformio.ini | 81 ++++++ test/mocks/radiolib_variant/SPI.h | 19 +- test/mocks/radiolib_variant/target_calls.h | 7 + test/mocks/radiolib_variant/variant_stubs.h | 11 +- .../helpers/ESP32Board.h | 16 ++ test/mocks/radiolib_variant_esp32/target.h | 19 ++ .../test_variant_target_esp32.cpp | 254 ++++++++++++++++++ 7 files changed, 399 insertions(+), 8 deletions(-) create mode 100644 test/mocks/radiolib_variant/target_calls.h create mode 100644 test/mocks/radiolib_variant_esp32/helpers/ESP32Board.h create mode 100644 test/mocks/radiolib_variant_esp32/target.h create mode 100644 test/test_variant_target_esp32/test_variant_target_esp32.cpp diff --git a/platformio.ini b/platformio.ini index f04688a3cd..076238bd4f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -183,6 +183,7 @@ test_ignore = test_variant_target test_variant_target_override test_variant_target_globals + test_variant_target_esp32 test_companion_frames build_src_filter = -<*> @@ -549,6 +550,86 @@ 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; 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 diff --git a/test/mocks/radiolib_variant/SPI.h b/test/mocks/radiolib_variant/SPI.h index 6bfbe442e2..971d06aa14 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_calls.h b/test/mocks/radiolib_variant/target_calls.h new file mode 100644 index 0000000000..c7e98265ed --- /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 index 54508ca8be..2f236b41c3 100644 --- a/test/mocks/radiolib_variant/variant_stubs.h +++ b/test/mocks/radiolib_variant/variant_stubs.h @@ -14,8 +14,7 @@ #include #include -// What the stand-ins below were asked to do, in order, for the tests to compare. -inline std::vector target_calls; +#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, @@ -41,7 +40,9 @@ class TestRadio : public PhysicalLayer { 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()"); + 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; } @@ -57,8 +58,8 @@ class TestRTCClock { }; // What CustomSX1262Wrapper.h defines, which every target.h of an SX1262 variant -// includes. -#ifndef USE_SX1262 +// includes; an environment for an SX1276 variant sets USE_SX1276 instead. +#if !defined(USE_SX1262) && !defined(USE_SX1276) #define USE_SX1262 #endif 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 0000000000..56c38632ac --- /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 0000000000..037bfb9dbe --- /dev/null +++ b/test/mocks/radiolib_variant_esp32/target.h @@ -0,0 +1,19 @@ +#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; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity(); 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 0000000000..9339e077c5 --- /dev/null +++ b/test/test_variant_target_esp32/test_variant_target_esp32.cpp @@ -0,0 +1,254 @@ +#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 variant's block and radio_init(), verbatim from the variants with this +// environment's description: SX1276 from heltec_v2 and lilygo_tbeam_SX1276, +// an own SPI host from heltec_e213, heltec_e290, heltec_rc32 and heltec_t190, +// otherwise from the 10 with the plain block (heltec_v3 and others). +#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 +} + +// 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(); +} From 67619b76e7fff5bf94fbf716956c38ec48cb12d1 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Wed, 21 Oct 2026 23:36:32 +0200 Subject: [PATCH 06/15] refactor(variants): define the radio globals and radio_init() once for 22 ESP32 variants RadioLibTarget.cpp describes the radio globals for ESP32 as well: the pins in the chip's order (SX1276: DIO0, RESET, DIO1), an own SPIClass where the board names P_LORA_SCLK, on P_LORA_SPI_HOST if set, otherwise no bus so that RadioLib starts its default one, and an ESP32RTCClock as fallback clock. For those variants with an SX126x or SX1276 it adds a weak radio_init(): start the fallback clock and the RTC, then the radio on its bus. The 22 ESP32 variants left by cut C delete their block and radio_init() from target.cpp (deletions only) and add the opt-in, and five of them P_LORA_SPI_HOST (FSPI, or 0 on the C6), to target.h (additions only). --- platformio.ini | 6 +- src/helpers/radiolib/RadioLibTarget.cpp | 71 +++++++++++++++---- test/mocks/radiolib_variant_esp32/target.h | 1 + .../test_variant_target_esp32.cpp | 43 ----------- .../test_variant_target_esp32_override.cpp | 41 +++++++++++ variants/generic-e22/target.cpp | 22 ------ variants/generic-e22/target.h | 1 + variants/heltec_e213/target.cpp | 23 ------ variants/heltec_e213/target.h | 2 + variants/heltec_e290/target.cpp | 23 ------ variants/heltec_e290/target.h | 2 + variants/heltec_rc32/target.cpp | 23 ------ variants/heltec_rc32/target.h | 2 + variants/heltec_t190/target.cpp | 23 ------ variants/heltec_t190/target.h | 2 + variants/heltec_tracker/target.cpp | 23 ------ variants/heltec_tracker/target.h | 1 + variants/heltec_tracker_v2/target.cpp | 23 ------ variants/heltec_tracker_v2/target.h | 1 + variants/heltec_v2/target.cpp | 22 ------ variants/heltec_v2/target.h | 1 + variants/heltec_v3/target.cpp | 23 ------ variants/heltec_v3/target.h | 1 + variants/heltec_v4/target.cpp | 23 ------ variants/heltec_v4/target.h | 1 + variants/heltec_v4_r8/target.cpp | 23 ------ variants/heltec_v4_r8/target.h | 1 + variants/heltec_wireless_paper/target.cpp | 22 ------ variants/heltec_wireless_paper/target.h | 1 + variants/lilygo_t3s3/target.cpp | 13 ---- variants/lilygo_t3s3/target.h | 1 + variants/lilygo_tbeam_SX1262/target.cpp | 18 ----- variants/lilygo_tbeam_SX1262/target.h | 1 + variants/lilygo_tbeam_SX1276/target.cpp | 23 ------ variants/lilygo_tbeam_SX1276/target.h | 1 + variants/lilygo_tlora_c6/target.cpp | 22 ------ variants/lilygo_tlora_c6/target.h | 2 + variants/lilygo_tlora_v2_1/target.cpp | 18 ----- variants/lilygo_tlora_v2_1/target.h | 1 + variants/meshadventurer/target.cpp | 17 ----- variants/meshadventurer/target.h | 1 + variants/nibble_screen_connect/target.cpp | 13 ---- variants/nibble_screen_connect/target.h | 1 + variants/nibble_zero_connect/target.cpp | 13 ---- variants/nibble_zero_connect/target.h | 1 + variants/rak3112/target.cpp | 23 ------ variants/rak3112/target.h | 1 + variants/tenstar_c3/target.cpp | 22 ------ variants/tenstar_c3/target.h | 1 + 49 files changed, 131 insertions(+), 513 deletions(-) create mode 100644 test/test_variant_target_esp32_override/test_variant_target_esp32_override.cpp diff --git a/platformio.ini b/platformio.ini index 076238bd4f..cf41a9766b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -184,6 +184,7 @@ test_ignore = test_variant_target_override test_variant_target_globals test_variant_target_esp32 + test_variant_target_esp32_override test_companion_frames build_src_filter = -<*> @@ -552,7 +553,8 @@ lib_deps = [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; the +; 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. @@ -575,7 +577,7 @@ build_flags = -std=c++17 -D P_LORA_DIO_0=15 -D P_LORA_SCLK=16 test_build_src = yes -test_filter = test_variant_target_esp32 +test_filter = test_variant_target_esp32* build_src_filter = ${env:native_variant_target.build_src_filter} lib_deps = google/googletest @ 1.17.0 diff --git a/src/helpers/radiolib/RadioLibTarget.cpp b/src/helpers/radiolib/RadioLibTarget.cpp index eb1ae67e4c..7b4dc01ced 100644 --- a/src/helpers/radiolib/RadioLibTarget.cpp +++ b/src/helpers/radiolib/RadioLibTarget.cpp @@ -11,32 +11,77 @@ // 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 and the platform. 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. +// 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) - #error "RADIOLIB_TARGET_GLOBALS: the radio globals are described for nRF52 only so far; define them in target.cpp" -#endif -#if defined(USE_SX1276) - #error "RADIOLIB_TARGET_GLOBALS: the SX1276 pin order is not described yet; define the radio in target.cpp" +#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 #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. -static RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); +// 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); -VolatileRTCClock fallback_clock; +#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 diff --git a/test/mocks/radiolib_variant_esp32/target.h b/test/mocks/radiolib_variant_esp32/target.h index 037bfb9dbe..8843938b19 100644 --- a/test/mocks/radiolib_variant_esp32/target.h +++ b/test/mocks/radiolib_variant_esp32/target.h @@ -12,6 +12,7 @@ #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; diff --git a/test/test_variant_target_esp32/test_variant_target_esp32.cpp b/test/test_variant_target_esp32/test_variant_target_esp32.cpp index 9339e077c5..82e3b75729 100644 --- a/test/test_variant_target_esp32/test_variant_target_esp32.cpp +++ b/test/test_variant_target_esp32/test_variant_target_esp32.cpp @@ -27,49 +27,6 @@ TestBoard board; // rtc_clock falls back to. extern ESP32RTCClock fallback_clock; -// The variant's block and radio_init(), verbatim from the variants with this -// environment's description: SX1276 from heltec_v2 and lilygo_tbeam_SX1276, -// an own SPI host from heltec_e213, heltec_e290, heltec_rc32 and heltec_t190, -// otherwise from the 10 with the plain block (heltec_v3 and others). -#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 -} - // 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 { 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 0000000000..646dec2aed --- /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/variants/generic-e22/target.cpp b/variants/generic-e22/target.cpp index 0e7e69d0a3..d76285b224 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 16612f67b6..a8c7749428 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 12e22b30a8..a836c1f5c6 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 6f2c291bb8..26fc647e0a 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 bcfdbcdba2..8612d534df 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 d78521fcfb..26d807cdee 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_rc32/target.cpp b/variants/heltec_rc32/target.cpp index be6d73b87b..e47fb35e81 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 04cb6d94a4..34d2c66f11 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_t190/target.cpp b/variants/heltec_t190/target.cpp index 3c32f047eb..7fcc8a8dd4 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 cde63220a8..597a6a91e9 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_tracker/target.cpp b/variants/heltec_tracker/target.cpp index 7d88a33210..2fbe32740b 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 2931eda816..16578dca7c 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 6d04291366..6498a1858b 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 240d9d4c58..e37da63405 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 5698f4095c..1a1de17d18 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 cdffc18a17..0cf2235204 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 b467bd98c5..9c6f5b1f4e 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 2944b38435..1743af20e0 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 9f163e1a8d..a29bd70daa 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 5d668186b2..dfc446de52 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 2f5390c596..8d18c3ce05 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 2d1d7a5bb4..a83052f300 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 91ee526f11..0373576cd6 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 7206678181..b6e77d2b91 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/lilygo_t3s3/target.cpp b/variants/lilygo_t3s3/target.cpp index 2679f6f914..6dcf5878d5 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 965cdd1a2e..490f4654f8 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_tbeam_SX1262/target.cpp b/variants/lilygo_tbeam_SX1262/target.cpp index d5ea9ad1f8..25baf47ece 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 c532a73af9..2674694f31 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 35b2c0af3f..feb4c5fdfb 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 0d56f88f8b..3184c2b8e6 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_tlora_c6/target.cpp b/variants/lilygo_tlora_c6/target.cpp index 02dfcd52a3..d76285b224 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 81e99ee3ab..1af86e66ae 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 00ae597b84..3f43f6e1a9 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 fa954b329b..75de03a5e3 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 8241cac144..4a42b96590 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 0749565d36..b051e1c80f 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/nibble_screen_connect/target.cpp b/variants/nibble_screen_connect/target.cpp index 6609338e75..c46e511a83 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 dcbcf8e1df..1b65464d20 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 2679f6f914..6dcf5878d5 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 87c55f0f2e..24da363ec2 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/rak3112/target.cpp b/variants/rak3112/target.cpp index 214426d474..42076afc8e 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 f93cd53996..4388532e45 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/tenstar_c3/target.cpp b/variants/tenstar_c3/target.cpp index b800e2df6c..13400fecf7 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 7839fa973a..1700fcaa92 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; From 33a5241f8e5051ad9aab4d9758843cba5416068c Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Thu, 22 Oct 2026 03:11:48 +0200 Subject: [PATCH 07/15] docs(refactoring): record cut D2 of note 0011, the radio globals and radio_init() of 22 ESP32 variants --- docs/refactoring/0011-variant-target.md | 192 +++++++++++++++++++++++- 1 file changed, 188 insertions(+), 4 deletions(-) diff --git a/docs/refactoring/0011-variant-target.md b/docs/refactoring/0011-variant-target.md index fff0e6ac01..302ef9cb5f 100644 --- a/docs/refactoring/0011-variant-target.md +++ b/docs/refactoring/0011-variant-target.md @@ -3,9 +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. Cut D (the radio, wrapper and RTC globals) is designed as two pull requests; -D1 (31 nRF52 variants) is implemented, D2 (22 ESP32 variants) follows on top of it, -see "Cut D". Cut E is planned. 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 @@ -89,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 description (D1: 31 nRF52, D2: 22 ESP32 variants) | 53 `target.cpp` and `target.h`, 1 source | D1 implemented, D2 next, see "Cut D" | +| 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, @@ -1419,6 +1419,190 @@ the same on both sides. +### 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 From 50a1f74093aab2eb124a589c6fd5478ead32c001 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Thu, 22 Oct 2026 03:11:57 +0200 Subject: [PATCH 08/15] docs(refactoring): wrap a line in the D2 record --- docs/refactoring/0011-variant-target.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/refactoring/0011-variant-target.md b/docs/refactoring/0011-variant-target.md index 302ef9cb5f..f5e2871e68 100644 --- a/docs/refactoring/0011-variant-target.md +++ b/docs/refactoring/0011-variant-target.md @@ -1522,7 +1522,9 @@ environments of the series' matrix. `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: + `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`): From a0511463f69cb9158f6ff15fd18d2f1a86f6fdfb Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Thu, 22 Oct 2026 03:14:29 +0200 Subject: [PATCH 09/15] docs(refactoring): cut D, the constructors of the moved globals with their source lines --- docs/refactoring/0011-variant-target.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/refactoring/0011-variant-target.md b/docs/refactoring/0011-variant-target.md index f5e2871e68..94fd7d3406 100644 --- a/docs/refactoring/0011-variant-target.md +++ b/docs/refactoring/0011-variant-target.md @@ -1173,6 +1173,29 @@ 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()` From 12e37302739839dbe3bde15e18d41959d218ab7c Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Fri, 23 Oct 2026 01:30:57 +0200 Subject: [PATCH 10/15] test(esp32): pin the RTC backup of ESP32RTCClock across two translation units The real ESP32Board.h and ESP32Board.cpp on the host, against stand-ins for the ESP-IDF headers and the variant's target.h, used from two translation units. Fails on this commit: the header defines the backup as static, so the test's unit has its own copy (5 of 9 tests). --- .github/workflows/run-unit-tests.yml | 2 +- platformio.ini | 23 ++++ test/mocks/esp32_idf/Wire.h | 10 ++ test/mocks/esp32_idf/driver/rtc_io.h | 4 + test/mocks/esp32_idf/esp32_idf_stubs.h | 71 +++++++++++ test/mocks/esp32_idf/esp_system.h | 4 + test/mocks/esp32_idf/rom/rtc.h | 4 + test/mocks/esp32_idf/soc/rtc.h | 4 + test/mocks/esp32_idf/target.h | 19 +++ test/test_esp32_rtc_backup/other_unit.cpp | 21 ++++ .../test_esp32_rtc_backup.cpp | 119 ++++++++++++++++++ 11 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 test/mocks/esp32_idf/Wire.h create mode 100644 test/mocks/esp32_idf/driver/rtc_io.h create mode 100644 test/mocks/esp32_idf/esp32_idf_stubs.h create mode 100644 test/mocks/esp32_idf/esp_system.h create mode 100644 test/mocks/esp32_idf/rom/rtc.h create mode 100644 test/mocks/esp32_idf/soc/rtc.h create mode 100644 test/mocks/esp32_idf/target.h create mode 100644 test/test_esp32_rtc_backup/other_unit.cpp create mode 100644 test/test_esp32_rtc_backup/test_esp32_rtc_backup.cpp diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index 2a23a20a08..9768b290b5 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -20,7 +20,7 @@ jobs: uses: ./.github/actions/setup-build-environment - name: Run Unit Tests - run: pio test -e native -e native_kiss_modem -e native_core -e native_ofast -e native_chat -e native_identity_store -e native_node_prefs -e native_radio_wrappers -e native_server -e native_sensor -e native_repeater -e native_repeater_full -e native_region -e native_radio_chips -e native_variant_target -e native_variant_target_override -e native_companion -vv + run: pio test -e native -e native_kiss_modem -e native_core -e native_ofast -e native_chat -e native_identity_store -e native_node_prefs -e native_radio_wrappers -e native_server -e native_sensor -e native_repeater -e native_repeater_full -e native_region -e native_radio_chips -e native_variant_target -e native_variant_target_override -e native_companion -e native_esp32_rtc_backup -vv - name: Upload Test Results # Upload test results even if the test step failed. diff --git a/platformio.ini b/platformio.ini index cf41a9766b..2919369805 100644 --- a/platformio.ini +++ b/platformio.ini @@ -620,6 +620,29 @@ build_src_filter = ${env:native_variant_target.build_src_filter} lib_deps = google/googletest @ 1.17.0 +[env:native_esp32_rtc_backup] +; The RTC backup of ESP32RTCClock: the real src/helpers/ESP32Board.h and +; ESP32Board.cpp, which defines the backup, against stand-ins for the ESP-IDF +; headers and the variant's target.h, used from two translation units +; (test/test_esp32_rtc_backup/other_unit.cpp). The pins are what ESP32Board +; uses; their values are not. +platform = native +test_framework = googletest +build_flags = -std=c++17 + -I test/mocks/esp32_idf + -I src + -I test/mocks + -D ESP_PLATFORM + -D P_LORA_DIO_1=1 + -D P_LORA_NSS=2 +test_build_src = yes +test_filter = test_esp32_rtc_backup +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 diff --git a/test/mocks/esp32_idf/Wire.h b/test/mocks/esp32_idf/Wire.h new file mode 100644 index 0000000000..7539b1c24b --- /dev/null +++ b/test/mocks/esp32_idf/Wire.h @@ -0,0 +1,10 @@ +#pragma once + +// Stand-in for the Arduino Wire library, as far as ESP32Board::begin() uses it. +class TwoWire { +public: + bool begin() { return true; } + bool begin(int sda, int scl) { return true; } +}; + +inline TwoWire Wire; diff --git a/test/mocks/esp32_idf/driver/rtc_io.h b/test/mocks/esp32_idf/driver/rtc_io.h new file mode 100644 index 0000000000..f39de99bb6 --- /dev/null +++ b/test/mocks/esp32_idf/driver/rtc_io.h @@ -0,0 +1,4 @@ +#pragma once + +// Stand-in for the ESP-IDF header of the same name, see esp32_idf_stubs.h. +#include "../esp32_idf_stubs.h" diff --git a/test/mocks/esp32_idf/esp32_idf_stubs.h b/test/mocks/esp32_idf/esp32_idf_stubs.h new file mode 100644 index 0000000000..dfe5abfbe9 --- /dev/null +++ b/test/mocks/esp32_idf/esp32_idf_stubs.h @@ -0,0 +1,71 @@ +#pragma once + +// Stand-in for the parts of the ESP32 Arduino core and ESP-IDF that +// src/helpers/ESP32Board.h and ESP32Board.cpp name, so both compile on the host. +// Only ESP32RTCClock is exercised: esp_reset_reason() returns what a test sets, +// and settimeofday() is redirected to a recorder, so a test sees which time +// begin() and setCurrentTime() set without touching the host's clock. The other +// declarations only have to compile. + +#include +#include + +// esp_attr.h: on the ESP32 a section in RTC memory that a reset does not clear; +// on the host ordinary (zero initialized) memory. +#define RTC_NOINIT_ATTR + +typedef enum { + ESP_RST_UNKNOWN, + ESP_RST_POWERON, + ESP_RST_EXT, + ESP_RST_SW, + ESP_RST_PANIC, + ESP_RST_INT_WDT, + ESP_RST_TASK_WDT, + ESP_RST_WDT, + ESP_RST_DEEPSLEEP, + ESP_RST_BROWNOUT, + ESP_RST_SDIO, +} esp_reset_reason_t; + +inline esp_reset_reason_t g_esp_reset_reason = ESP_RST_POWERON; +inline esp_reset_reason_t esp_reset_reason() { return g_esp_reset_reason; } +inline void esp_restart() { } + +// settimeofday() as ESP32RTCClock calls it: records the time instead of setting +// the host's clock. is included above, so the header's own include +// of it does not see the macro. +inline int g_settimeofday_calls = 0; +inline time_t g_settimeofday_sec = 0; +inline int esp32_test_settimeofday(const struct timeval* tv, const void*) { + g_settimeofday_calls++; + g_settimeofday_sec = tv->tv_sec; + return 0; +} +#define settimeofday esp32_test_settimeofday + +typedef int gpio_num_t; +enum { GPIO_INTR_POSEDGE = 1, GPIO_INTR_HIGH_LEVEL = 5 }; +#ifndef HIGH +#define HIGH 1 +#endif +inline int gpio_get_level(gpio_num_t) { return 0; } +inline void gpio_wakeup_enable(gpio_num_t, int) { } +inline void gpio_wakeup_disable(gpio_num_t) { } +inline void gpio_set_intr_type(gpio_num_t, int) { } +inline void esp_sleep_enable_timer_wakeup(uint64_t) { } +inline void esp_sleep_enable_gpio_wakeup() { } +inline void esp_light_sleep_start() { } +enum { ESP_SLEEP_WAKEUP_ALL = 0 }; +inline void esp_sleep_disable_wakeup_source(int) { } +inline void esp_deep_sleep_start() { } +inline void rtc_gpio_hold_en(gpio_num_t) { } +inline void gpio_hold_en(gpio_num_t) { } +inline void digitalWrite(int, int) { } + +typedef int portMUX_TYPE; +#define portMUX_INITIALIZER_UNLOCKED 0 +#define portENTER_CRITICAL(mux) ((void)(mux)) +#define portEXIT_CRITICAL(mux) ((void)(mux)) + +inline float temperatureRead() { return 0.0f; } diff --git a/test/mocks/esp32_idf/esp_system.h b/test/mocks/esp32_idf/esp_system.h new file mode 100644 index 0000000000..6d8152b3ff --- /dev/null +++ b/test/mocks/esp32_idf/esp_system.h @@ -0,0 +1,4 @@ +#pragma once + +// Stand-in for the ESP-IDF header of the same name, see esp32_idf_stubs.h. +#include "esp32_idf_stubs.h" diff --git a/test/mocks/esp32_idf/rom/rtc.h b/test/mocks/esp32_idf/rom/rtc.h new file mode 100644 index 0000000000..f39de99bb6 --- /dev/null +++ b/test/mocks/esp32_idf/rom/rtc.h @@ -0,0 +1,4 @@ +#pragma once + +// Stand-in for the ESP-IDF header of the same name, see esp32_idf_stubs.h. +#include "../esp32_idf_stubs.h" diff --git a/test/mocks/esp32_idf/soc/rtc.h b/test/mocks/esp32_idf/soc/rtc.h new file mode 100644 index 0000000000..f39de99bb6 --- /dev/null +++ b/test/mocks/esp32_idf/soc/rtc.h @@ -0,0 +1,4 @@ +#pragma once + +// Stand-in for the ESP-IDF header of the same name, see esp32_idf_stubs.h. +#include "../esp32_idf_stubs.h" diff --git a/test/mocks/esp32_idf/target.h b/test/mocks/esp32_idf/target.h new file mode 100644 index 0000000000..4f9808ea91 --- /dev/null +++ b/test/mocks/esp32_idf/target.h @@ -0,0 +1,19 @@ +#pragma once + +// Stand-in for a variant's target.h, as far as src/helpers/ESP32Board.cpp uses +// it: ESP32Board::enterDeepSleep() powers off the radio and stops the GPS. + +#include + +struct TestRadioDriver { + void powerOff() { } +}; +inline TestRadioDriver radio_driver; + +struct TestLocationProvider { + void stop() { } +}; +struct TestSensors { + TestLocationProvider* getLocationProvider() { return nullptr; } +}; +inline TestSensors sensors; diff --git a/test/test_esp32_rtc_backup/other_unit.cpp b/test/test_esp32_rtc_backup/other_unit.cpp new file mode 100644 index 0000000000..4d8c9d6a06 --- /dev/null +++ b/test/test_esp32_rtc_backup/other_unit.cpp @@ -0,0 +1,21 @@ +// A second translation unit that uses ESP32RTCClock, as a variant's target.cpp +// and src/helpers/radiolib/RadioLibTarget.cpp would if both did. The test in +// test_esp32_rtc_backup.cpp calls these and checks that begin(), setCurrentTime() +// and tick() see the same RTC backup whichever unit they run in. + +#include + +void other_unit_set_time(uint32_t time) { + ESP32RTCClock clock; + clock.setCurrentTime(time); +} + +void other_unit_tick() { + ESP32RTCClock clock; + clock.tick(); +} + +void other_unit_begin() { + ESP32RTCClock clock; + clock.begin(); +} diff --git a/test/test_esp32_rtc_backup/test_esp32_rtc_backup.cpp b/test/test_esp32_rtc_backup/test_esp32_rtc_backup.cpp new file mode 100644 index 0000000000..fd828bae5e --- /dev/null +++ b/test/test_esp32_rtc_backup/test_esp32_rtc_backup.cpp @@ -0,0 +1,119 @@ +// The RTC backup of ESP32RTCClock (src/helpers/ESP32Board.h): setCurrentTime() +// and tick() save the time in RTC memory that a reset does not clear, begin() +// restores it after a reset that lost the system time. The backup has to be one +// object for the whole image, so the tests write it in one translation unit +// (other_unit.cpp) and read it in another (this one), in both directions. + +#include +#include + +#include + +void other_unit_set_time(uint32_t time); +void other_unit_tick(); +void other_unit_begin(); + +namespace { + +const uint32_t SAVED_TIME = RTC_TIME_MIN + 123456; + +class Esp32RtcBackupTest : public ::testing::Test { +protected: + void SetUp() override { + // RTC_NOINIT memory holds anything after power-on; start without a backup. + _rtc_backup_time = 0; + _rtc_backup_magic = 0; + g_esp_reset_reason = ESP_RST_POWERON; + g_settimeofday_calls = 0; + g_settimeofday_sec = 0; + } + + // A reset that lost the system time, then begin() in this unit. + void resetAndBeginHere(esp_reset_reason_t reason = ESP_RST_SW) { + g_esp_reset_reason = reason; + g_settimeofday_calls = 0; + ESP32RTCClock clock; + clock.begin(); + } +}; + +TEST_F(Esp32RtcBackupTest, BeginRestoresTimeSetInAnotherUnit) { + other_unit_set_time(SAVED_TIME); + resetAndBeginHere(); + EXPECT_EQ(g_settimeofday_calls, 1); + EXPECT_EQ(g_settimeofday_sec, (time_t)SAVED_TIME); +} + +TEST_F(Esp32RtcBackupTest, BeginInAnotherUnitRestoresTimeSetHere) { + ESP32RTCClock clock; + clock.setCurrentTime(SAVED_TIME); + g_esp_reset_reason = ESP_RST_PANIC; + g_settimeofday_calls = 0; + other_unit_begin(); + EXPECT_EQ(g_settimeofday_calls, 1); + EXPECT_EQ(g_settimeofday_sec, (time_t)SAVED_TIME); +} + +TEST_F(Esp32RtcBackupTest, BeginRestoresTimeSavedByTickInAnotherUnit) { + // tick() saves the system time, here the host's, which is after RTC_TIME_MIN. + time_t before = time(NULL); + other_unit_tick(); + time_t after = time(NULL); + ASSERT_GT(before, (time_t)RTC_TIME_MIN); + resetAndBeginHere(ESP_RST_TASK_WDT); + EXPECT_EQ(g_settimeofday_calls, 1); + EXPECT_GE(g_settimeofday_sec, before); + EXPECT_LE(g_settimeofday_sec, after); +} + +TEST_F(Esp32RtcBackupTest, SetCurrentTimeSetsSystemTimeAndBackup) { + ESP32RTCClock clock; + clock.setCurrentTime(SAVED_TIME); + EXPECT_EQ(g_settimeofday_calls, 1); + EXPECT_EQ(g_settimeofday_sec, (time_t)SAVED_TIME); + EXPECT_EQ(_rtc_backup_time, SAVED_TIME); + EXPECT_EQ(_rtc_backup_magic, (uint32_t)RTC_BACKUP_MAGIC); +} + +TEST_F(Esp32RtcBackupTest, BeginWithoutBackupSeedsMinimumTime) { + resetAndBeginHere(ESP_RST_POWERON); + EXPECT_EQ(g_settimeofday_calls, 1); + EXPECT_EQ(g_settimeofday_sec, (time_t)RTC_TIME_MIN); +} + +TEST_F(Esp32RtcBackupTest, BeginIgnoresBackupWithoutMagic) { + _rtc_backup_time = SAVED_TIME; + _rtc_backup_magic = RTC_BACKUP_MAGIC ^ 1; + resetAndBeginHere(); + EXPECT_EQ(g_settimeofday_sec, (time_t)RTC_TIME_MIN); +} + +TEST_F(Esp32RtcBackupTest, BeginIgnoresBackupBeforeMinimumTime) { + _rtc_backup_time = RTC_TIME_MIN - 1; + _rtc_backup_magic = RTC_BACKUP_MAGIC; + resetAndBeginHere(); + EXPECT_EQ(g_settimeofday_sec, (time_t)RTC_TIME_MIN); +} + +TEST_F(Esp32RtcBackupTest, BeginAfterDeepSleepKeepsSystemTime) { + other_unit_set_time(SAVED_TIME); + resetAndBeginHere(ESP_RST_DEEPSLEEP); + EXPECT_EQ(g_settimeofday_calls, 0); +} + +TEST_F(Esp32RtcBackupTest, TickSavesSystemTimeOverOlderBackup) { + // setCurrentTime() saves any time; tick() saves the system time when it is + // after RTC_TIME_MIN and differs from the backup, as the host's clock does. + other_unit_set_time(RTC_TIME_MIN - 10); + EXPECT_EQ(_rtc_backup_time, (uint32_t)(RTC_TIME_MIN - 10)); + ESP32RTCClock clock; + clock.tick(); + EXPECT_GT(_rtc_backup_time, (uint32_t)RTC_TIME_MIN); +} + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 836678d246d624ed6fce5f58d1043409658487c8 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Fri, 23 Oct 2026 01:30:57 +0200 Subject: [PATCH 11/15] fix(esp32): define the RTC backup of ESP32RTCClock once ESP32Board.h defined _rtc_backup_time and _rtc_backup_magic as static in the header, so every translation unit that includes it has its own copy, and the inline members of ESP32RTCClock read and write the copy of the unit they are compiled in. Today one unit per image uses them; a second one could restore a copy that setCurrentTime() never wrote. The header now declares them, and ESP32Board.cpp defines them once. --- src/helpers/ESP32Board.cpp | 4 ++++ src/helpers/ESP32Board.h | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index a55abb264e..d190fca190 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -3,6 +3,10 @@ #include "ESP32Board.h" #include +// The RTC backup of ESP32RTCClock, declared in ESP32Board.h. +RTC_NOINIT_ATTR uint32_t _rtc_backup_time; +RTC_NOINIT_ATTR uint32_t _rtc_backup_magic; + #if defined(ADMIN_PASSWORD) && !defined(DISABLE_WIFI_OTA) // Repeater or Room Server only #include #include diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 3f95e6923f..cd274cd931 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -200,8 +200,11 @@ class ESP32Board : public mesh::MainBoard { } }; -static RTC_NOINIT_ATTR uint32_t _rtc_backup_time; -static RTC_NOINIT_ATTR uint32_t _rtc_backup_magic; +// The time ESP32RTCClock restores after a reset, in RTC memory that a reset does +// not clear. Defined once, in ESP32Board.cpp: the inline members below run in +// every translation unit that uses them and must all see the same backup. +extern uint32_t _rtc_backup_time; +extern uint32_t _rtc_backup_magic; #define RTC_BACKUP_MAGIC 0xAA55CC33 #define RTC_TIME_MIN 1772323200 // 1 Mar 2026 From e60722613dfebbc61d6ea6fb67a6d293986b0c17 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Fri, 23 Oct 2026 02:02:23 +0200 Subject: [PATCH 12/15] test(esp32): keep the RTC backup suite out of the native environment It needs the ESP-IDF stand-ins of its own environment, native_esp32_rtc_backup. --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 2919369805..cd38837dcd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -186,6 +186,7 @@ test_ignore = test_variant_target_esp32 test_variant_target_esp32_override test_companion_frames + test_esp32_rtc_backup build_src_filter = -<*> +<../src/Utils.cpp> From a154984992e27c6fc53b654d5c69f8010fde46df Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Fri, 23 Oct 2026 02:06:33 +0200 Subject: [PATCH 13/15] docs(refactoring): note 0012, one RTC backup for ESP32RTCClock --- docs/refactoring/0012-esp32-rtc-backup.md | 266 ++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 docs/refactoring/0012-esp32-rtc-backup.md diff --git a/docs/refactoring/0012-esp32-rtc-backup.md b/docs/refactoring/0012-esp32-rtc-backup.md new file mode 100644 index 0000000000..688c369205 --- /dev/null +++ b/docs/refactoring/0012-esp32-rtc-backup.md @@ -0,0 +1,266 @@ +# 0012: One RTC backup for ESP32RTCClock + +Status: implemented. Stacked on cut D2 of step 11 +([0011](0011-variant-target.md), section "D2: what the code commit changes"), where +the trap was found. It fixes a definition, not a behaviour: every ESP32 image keeps +its one copy of the backup at the address it had, except two that never restore it +(below). + +## Context + +`ESP32RTCClock` (`src/helpers/ESP32Board.h`) keeps the time across a reset that +loses the system time: `setCurrentTime()` and `tick()` write it to +`_rtc_backup_time` and `_rtc_backup_magic` in RTC memory (`RTC_NOINIT_ATTR`, the +section `.rtc_noinit`, which a reset does not clear), and `begin()` restores it when +the magic matches. + +The header defined both as `static`: + +```cpp +static RTC_NOINIT_ATTR uint32_t _rtc_backup_time; +static RTC_NOINIT_ATTR uint32_t _rtc_backup_magic; +``` + +`static` at namespace scope gives every translation unit that includes the header +its own pair. The members that use them are inline, so each unit that calls them +compiles a copy of `begin()`, `setCurrentTime()` and `tick()` against its own pair. +That is a violation of the one definition rule (an inline function that names an +object with internal linkage, defined in several units), and what an image does with +it depends on the compiler and the linker: + +- A call the compiler inlines reads and writes the pair of the unit it is in. +- A call that is not inlined goes to the one out of line copy the linker keeps of + each inline member, which uses the pair of whichever unit that copy came from. + +Today it does no harm because exactly one unit per image uses the members: the +variant's `target.cpp` before cut D, `src/helpers/radiolib/RadioLibTarget.cpp` after +it for the 22 variants of D2, `XiaoC6Board.cpp` and `UnitC6LBoard.cpp` for the two +C6 board files. The day a second unit calls one of them (a variant that starts the +clock in its board file and sets it in `target.cpp`, a helper that saves the time +before a deep sleep), `begin()` can read a pair that `setCurrentTime()` never wrote, +and the time no longer survives a reset, with no error and no warning. + +## What changes + +- `ESP32Board.h` declares the two variables `extern`, with a comment why. +- `ESP32Board.cpp` defines them once, with `RTC_NOINIT_ATTR`, inside its existing + `#ifdef ESP_PLATFORM`. + +Names, types and the section stay the same, so code that uses the variables keeps +compiling. With one definition the linker enforces the rest: a second definition +does not link (`multiple definition`), a missing one does not either (`undefined +reference`). + +## Alternatives + +- **Define them in a file of their own** (`src/helpers/ESP32RTCBackup.cpp`). Built + first and measured: the ESP32 images are the same as with `ESP32Board.cpp`, but + the new object file, empty on every other platform, is one more input to every + link, and on RP2040 that alone reorders the linker's long branch veneers + (`PicoW_repeater`: 157 bytes differ; built without the file, the image equals the + start commit's byte for byte). `ESP32Board.cpp` is compiled for every platform + already, so the other platforms' images stay byte for byte the same. +- **`inline` variables** (C++17, `inline RTC_NOINIT_ATTR uint32_t ...` in the + header). Not built. Rejected: the definition would still be in every unit that + uses it, with the linker choosing one, and `RTC_NOINIT_ATTR` gives each unit its + own section name (`.rtc_noinit.<__COUNTER__>`); which copy lands where is the kind + of link question this change removes. +- **Move the backup into `ESP32RTCClock`** as static members. Rejected for this + step: it renames the variables for anyone who uses them, and it still needs one + out of line definition, so it would be this change plus a rename. + +## The address in `.rtc_noinit` + +A time saved by the old image is read by the new one only if both variables keep +their address. The address follows from the link: `.rtc_noinit` collects +`*(.rtc_noinit .rtc_noinit.*)` in input order, without `KEEP`. Before, every unit +that included the header carried the pair's sections (the link maps list them for +`main.cpp.o`, `MyMesh.cpp.o`, `UITask.cpp.o` and others), and `--gc-sections` dropped +all but those of the one unit that uses the members; after, they come from +`ESP32Board.cpp.o`. ESP-IDF's own variables there (`s_rtc_last_ticks`, +`s_esp_rtc_time_us`) come from archives linked after the project's objects. Within +the object, the order of the two is the compiler's. + +Measured with `objdump -t` on every ESP32 image of the matrix below: + +- In 55 of the 57 ESP32 images, before and after: exactly one `_rtc_backup_time` and + one `_rtc_backup_magic`, at the same addresses (for example `0x50000000`/`0x50000004` on the + S3, `0x50000200`/`0x50000204` on the ESP32, `0x50000018`/`0x5000001c` on the C3, + `0x50000048`/`0x5000004c` on the C6), and every other symbol in `.rtc_noinit` at + the same address too. The order of the two is not the same in every image (time + first in `LilyGo_TLora_V2_1_1_6` and the three C3 builds, magic first elsewhere), + and each image keeps its own. +- In `ThinkNode_M7_repeater` and `ThinkNode_M9_repeater_`, the address of + `_rtc_backup_time` changes, from `0x50000000` to `0x50000004`, and + `_rtc_backup_magic` appears at `0x50000000`. These two variants never call + `fallback_clock.begin()` (`variants/thinknode_m7/target.cpp`, + `variants/thinknode_m9/target.cpp`), so the image never reads the backup: with a + `static` pair, the compiler saw `_rtc_backup_magic` written and never read in the + unit and dropped it with its stores (disassembly: before, `setCurrentTime()` + stores the time only; after, it stores both). Nothing is restored on these two + boards before or after, so the moved address loses nothing. The ESP-IDF variables + after them keep their addresses (`s_rtc_last_ticks` at `0x50000008`, + `s_esp_rtc_time_us` at `0x50000010`), since the 4 bytes of the magic fill what was + alignment padding. + +## Verification + +### Tests first + +`test/test_esp32_rtc_backup` (`pio test -e native_esp32_rtc_backup`, added to the +unit test workflow): the real `ESP32Board.h` and `ESP32Board.cpp` on the host, +against stand ins for the ESP-IDF headers they include (`test/mocks/esp32_idf/`: +`esp_reset_reason()` returns what a test sets, `settimeofday()` records instead of +setting the host's clock, `RTC_NOINIT_ATTR` is empty) and for the variant's +`target.h`. The members are called from two translation units, the test and +`other_unit.cpp`, in both directions: a time set in one is restored by `begin()` in +the other, also when `tick()` saved it, and the backup the test reads directly is +the one the members wrote. Further tests pin `begin()`: no backup or a wrong magic +seeds `RTC_TIME_MIN`, a time before `RTC_TIME_MIN` is not restored, a wake from deep +sleep keeps the system time. + +On the start commit (`12e37302`, the tests with the firmware sources of +`a0511463`) 5 of the 9 tests fail: the test's unit sees its own zero pair. The 4 +cross unit tests that call members in both units pass there, because at `-O0` +nothing is inlined and the host linker kept the out of line members of +`other_unit.cpp`, so all calls agreed on that unit's pair (as the 5 failures, +all reading the test unit's pair, show): the hazard depends on +inlining, which is why the tests also read the pair directly. On the code commit +(`836678d2`) all 9 pass. + +`e6072261` adds the suite to `test_ignore` of the `native` environment, which would +otherwise pick it up without the stand ins; it changes no firmware source. + +### Host suites + +All 23 native environments: 716 tests pass (707 on the head of D2 plus the 9 new). + +### Mutation tests + +| mutation | result | +|---|---| +| M1: the start commit's `ESP32Board.h` and `ESP32Board.cpp` (`static` in the header) | 5 tests fail | +| M2: `setCurrentTime()` does not write the magic | 3 tests fail | +| M3: `begin()` ignores the magic | 1 test fails | +| M4: `begin()` restores after a wake from deep sleep | 1 test fails | +| M5: `tick()` saves nothing | 2 tests fail | +| M6: `begin()` without the lower bound `RTC_TIME_MIN` | 1 test fails | +| M7: `begin()` accepts a time equal to `RTC_TIME_MIN` | all pass: equivalent, restoring `RTC_TIME_MIN` and seeding it set the same time | +| M8: `_rtc_backup_magic` not defined | does not link (`Undefined symbols ... _rtc_backup_magic`) | + +### Build matrix + +64 environments, built from `12e37302` and `836678d2` in the same worktree with +`SOURCE_DATE_EPOCH=1760000000`: one per ESP32 variant folder (44; all 44 use +`ESP32RTCClock`), every role of `heltec_v3` (repeater, both bridges, room server, +terminal chat, companion USB, BLE and WiFi, sensor, KISS modem), the SX1268 builds +of `generic-e22` and `meshadventurer`, `RAK_3112_repeater_bridge_espnow`, more C6 +builds (`Xiao_C6_companion_radio_ble_`, `Meshimi_repeater_`, +`M5Stack_Unit_C6L_companion_radio_ble`), and nRF52, RP2040 and STM32 controls +(`RAK_4631_repeater`, `RAK_4631_companion_radio_ble`, `ProMicro_repeater`, +`PicoW_repeater`, `wio-e5-mini_repeater`). + +- 62 build on both commits. `Generic_ESPNOW_terminal_chat` and + `SenseCapIndicator-ESPNow_comp_radio_usb` fail on both, with the same error + (`'P_LORA_DIO_1' was not declared in this scope` in `ESP32Board.h`, a board + without LoRa); not touched here. +- **nRF52, RP2040 and STM32: the same image, byte for byte** (5 of 5). +- **ESP32, C3 and C6: flash and RAM unchanged** in 55 of 57; `ThinkNode_M7` +28 and + `ThinkNode_M9` +24 bytes of flash (the magic stores the compiler now keeps), RAM + unchanged. +- The ESP32, S3 and C6 images (52) differ only in the 64 or 65 bytes that hash the ELF + (at `0xb0` and at the end of the image), so their contents are the same. The three + C3 builds (`Heltec_ct62`, `Tenstar_C3_sx1262`, `Xiao_C3`) differ in 16 or 17 more bytes, + all in `ESP32RTCClock::tick()`: same length, same instructions apart from how the + address of `_rtc_backup_time` is formed (before `lui a5,0x50000; add a5,a5,24; + lw a3,0(a5)`, after `lui a4,0x50000; lw a3,24(a4)`, because a global symbol's + offset can be folded into the load), and the registers that follow from it. +- `.rtc_noinit`: as in the section above. +- The two C6 board files (`variants/xiao_c6/XiaoC6Board.cpp`, + `variants/m5stack_unit_c6l/UnitC6LBoard.cpp`) are the one unit that uses the + members in their images, as `target.cpp` is elsewhere; their five builds above + have one copy at `0x50000048`/`0x5000004c` before and after. They need no change. + +No hardware evidence (without board). A board run would set the time on an ESP32 +with the old image, flash the new one, and read the time after the reset that +follows the flash: the same, not `RTC_TIME_MIN`. + +## Follow-up outside this step + +`ThinkNode_M7` and `ThinkNode_M9` never call `fallback_clock.begin()`, so on these +two boards the time is not restored after a reset, even without the trap above. +Whether that is meant +(both have an RTC chip that `AutoDiscoverRTCClock` may find) is a question for the +boards' maintainers, not a refactoring. + +
+All 64 environments + +| Environment | Flash before | Flash after | Flash delta | RAM before | RAM after | RAM delta | image | backup before | backup after | +|---|---:|---:|---:|---:|---:|---:|---|---|---| +| `Ebyte_EoRa-S3_Repeater` | 1,108,433 | 1,108,433 | +0 | 60,500 | 60,500 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Generic_E22_sx1262_repeater` | 1,143,573 | 1,143,573 | +0 | 60,620 | 60,620 | +0 | differs (65 bytes) | time @ 0x50000204, magic @ 0x50000200 | magic @ 0x50000200, time @ 0x50000204 | +| `Generic_ESPNOW_terminal_chat` | | | | | | | does not build on either commit | | | +| `Heltec_ct62_repeater` | 1,151,874 | 1,151,874 | +0 | 53,772 | 53,772 | +0 | differs (81 bytes) | time @ 0x50000018, magic @ 0x5000001c | magic @ 0x5000001c, time @ 0x50000018 | +| `Heltec_E213_repeater` | 1,162,241 | 1,162,241 | +0 | 71,940 | 71,940 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_E290_repeater` | 1,160,261 | 1,160,261 | +0 | 72,124 | 72,124 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `heltec_rc32_without_display_repeater` | 1,162,121 | 1,162,121 | +0 | 62,176 | 62,176 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_T190_repeater_` | 1,139,373 | 1,139,373 | +0 | 71,872 | 71,872 | +0 | differs (64 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_Wireless_Tracker_repeater` | 1,157,521 | 1,157,521 | +0 | 61,088 | 61,088 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `heltec_tracker_v2_repeater` | 1,226,209 | 1,226,209 | +0 | 74,352 | 74,352 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v2_repeater` | 1,151,901 | 1,151,901 | +0 | 60,604 | 60,604 | +0 | differs (65 bytes) | time @ 0x50000204, magic @ 0x50000200 | magic @ 0x50000200, time @ 0x50000204 | +| `Heltec_v3_repeater` | 1,174,961 | 1,174,961 | +0 | 62,512 | 62,512 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `heltec_v4_repeater` | 1,173,089 | 1,173,089 | +0 | 62,484 | 62,484 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `heltec_v4_r8_repeater` | 1,176,589 | 1,176,589 | +0 | 62,400 | 62,400 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_Wireless_Paper_repeater` | 1,121,873 | 1,121,873 | +0 | 60,376 | 60,376 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `LilyGo_T3S3_sx1262_repeater` | 1,108,537 | 1,108,537 | +0 | 60,500 | 60,500 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `LilyGo_T3S3_sx1276_repeater` | 1,105,521 | 1,105,521 | +0 | 60,484 | 60,484 | +0 | differs (64 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `LilyGo_TBeam_1W_repeater` | 1,154,973 | 1,154,973 | +0 | 72,752 | 72,752 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Tbeam_SX1262_repeater` | 1,205,157 | 1,205,157 | +0 | 61,212 | 61,212 | +0 | differs (64 bytes) | time @ 0x50000204, magic @ 0x50000200 | magic @ 0x50000200, time @ 0x50000204 | +| `Tbeam_SX1276_repeater` | 1,201,113 | 1,201,113 | +0 | 61,092 | 61,092 | +0 | differs (65 bytes) | time @ 0x50000204, magic @ 0x50000200 | magic @ 0x50000200, time @ 0x50000204 | +| `T_Beam_S3_Supreme_SX1262_repeater` | 1,155,517 | 1,155,517 | +0 | 61,072 | 61,072 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `LilyGo_TDeck_repeater` | 1,144,713 | 1,144,713 | +0 | 61,424 | 61,424 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `LilyGo_TETH_Elite_sx1262_repeater` | 1,076,289 | 1,076,289 | +0 | 59,408 | 59,408 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `LilyGo_Tlora_C6_repeater_` | 542,449 | 542,449 | +0 | 29,856 | 29,856 | +0 | differs (64 bytes) | time @ 0x5000004c, magic @ 0x50000048 | magic @ 0x50000048, time @ 0x5000004c | +| `LilyGo_TLora_V2_1_1_6_repeater` | 1,663,977 | 1,663,977 | +0 | 62,472 | 62,472 | +0 | differs (65 bytes) | time @ 0x50000200, magic @ 0x50000204 | magic @ 0x50000204, time @ 0x50000200 | +| `M5Stack_Unit_C6L_repeater` | 552,821 | 552,821 | +0 | 29,752 | 29,752 | +0 | differs (65 bytes) | time @ 0x5000004c, magic @ 0x50000048 | magic @ 0x50000048, time @ 0x5000004c | +| `Meshadventurer_sx1262_repeater` | 1,162,029 | 1,162,029 | +0 | 61,084 | 61,084 | +0 | differs (65 bytes) | time @ 0x50000204, magic @ 0x50000200 | magic @ 0x50000200, time @ 0x50000204 | +| `meshnology_w12_repeater` | 1,183,449 | 1,183,449 | +0 | 62,800 | 62,800 | +0 | differs (64 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `nibble_screen_connect_repeater_` | 1,103,209 | 1,103,209 | +0 | 60,216 | 60,216 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `nibble_zero_connect_repeater_` | 1,103,301 | 1,103,301 | +0 | 60,216 | 60,216 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `RAK_3112_repeater` | 1,165,765 | 1,165,765 | +0 | 62,424 | 62,424 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `SenseCapIndicator-ESPNow_comp_radio_usb` | | | | | | | does not build on either commit | | | +| `Station_G2_repeater` | 1,207,885 | 1,207,885 | +0 | 74,712 | 74,712 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Station_G3_ESP32_repeater` | 1,209,469 | 1,209,469 | +0 | 74,720 | 74,720 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Tenstar_C3_sx1262_repeater` | 1,152,378 | 1,152,378 | +0 | 53,796 | 53,796 | +0 | differs (82 bytes) | time @ 0x50000018, magic @ 0x5000001c | magic @ 0x5000001c, time @ 0x50000018 | +| `ThinkNode_M2_Repeater` | 1,110,181 | 1,110,181 | +0 | 58,160 | 58,160 | +0 | differs (64 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `ThinkNode_M5_Repeater` | 1,127,465 | 1,127,465 | +0 | 63,648 | 63,648 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `ThinkNode_M7_repeater` | 1,089,241 | 1,089,269 | +28 | 59,688 | 59,688 | +0 | differs: length 1089664 -> 1089680 | time @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `ThinkNode_M9_repeater_` | 1,182,977 | 1,183,001 | +24 | 62,144 | 62,144 | +0 | differs: length 1183392 -> 1183424 | time @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Waveshare_S3_LR1121_repeater` | 1,084,869 | 1,084,869 | +0 | 59,868 | 59,868 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Xiao_C3_repeater` | 1,158,046 | 1,158,046 | +0 | 54,076 | 54,076 | +0 | differs (82 bytes) | time @ 0x50000018, magic @ 0x5000001c | magic @ 0x5000001c, time @ 0x50000018 | +| `Xiao_C6_repeater_` | 542,479 | 542,479 | +0 | 29,856 | 29,856 | +0 | differs (64 bytes) | time @ 0x5000004c, magic @ 0x50000048 | magic @ 0x50000048, time @ 0x5000004c | +| `Xiao_S3_repeater` | 1,137,605 | 1,137,605 | +0 | 61,680 | 61,680 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Xiao_S3_WIO_repeater` | 1,127,969 | 1,127,969 | +0 | 61,320 | 61,320 | +0 | differs (64 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_repeater_bridge_rs232` | 1,177,429 | 1,177,429 | +0 | 64,096 | 64,096 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_repeater_bridge_espnow` | 1,185,425 | 1,185,425 | +0 | 64,168 | 64,168 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_room_server` | 1,170,101 | 1,170,101 | +0 | 66,200 | 66,200 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_terminal_chat` | 602,317 | 602,317 | +0 | 95,868 | 95,868 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_companion_radio_usb` | 658,149 | 658,149 | +0 | 103,404 | 103,404 | +0 | differs (64 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_companion_radio_ble` | 1,671,593 | 1,671,593 | +0 | 194,844 | 194,844 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_companion_radio_wifi` | 1,107,797 | 1,107,797 | +0 | 172,384 | 172,384 | +0 | differs (64 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_sensor` | 1,181,489 | 1,181,489 | +0 | 60,600 | 60,600 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Heltec_v3_kiss_modem` | 581,185 | 581,185 | +0 | 24,580 | 24,580 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Generic_E22_sx1268_repeater` | 1,143,361 | 1,143,361 | +0 | 60,620 | 60,620 | +0 | differs (65 bytes) | time @ 0x50000204, magic @ 0x50000200 | magic @ 0x50000200, time @ 0x50000204 | +| `Meshadventurer_sx1268_repeater` | 1,161,797 | 1,161,797 | +0 | 61,084 | 61,084 | +0 | differs (64 bytes) | time @ 0x50000204, magic @ 0x50000200 | magic @ 0x50000200, time @ 0x50000204 | +| `RAK_3112_repeater_bridge_espnow` | 1,176,269 | 1,176,269 | +0 | 64,072 | 64,072 | +0 | differs (65 bytes) | time @ 0x50000004, magic @ 0x50000000 | magic @ 0x50000000, time @ 0x50000004 | +| `Xiao_C6_companion_radio_ble_` | 1,446,683 | 1,446,683 | +0 | 164,292 | 164,292 | +0 | differs (64 bytes) | time @ 0x5000004c, magic @ 0x50000048 | magic @ 0x50000048, time @ 0x5000004c | +| `RAK_4631_repeater` | 441,964 | 441,964 | +0 | 37,240 | 37,240 | +0 | same | none | none | +| `RAK_4631_companion_radio_ble` | 465,224 | 465,224 | +0 | 149,768 | 149,768 | +0 | same | none | none | +| `PicoW_repeater` | 492,668 | 492,668 | +0 | 85,368 | 85,368 | +0 | same | none | none | +| `wio-e5-mini_repeater` | 204,136 | 204,136 | +0 | 17,172 | 17,172 | +0 | same | none | none | +| `ProMicro_repeater` | 346,276 | 346,276 | +0 | 33,900 | 33,900 | +0 | same | none | none | +| `Meshimi_repeater_` | 542,535 | 542,535 | +0 | 29,856 | 29,856 | +0 | differs (64 bytes) | time @ 0x5000004c, magic @ 0x50000048 | magic @ 0x50000048, time @ 0x5000004c | +| `M5Stack_Unit_C6L_companion_radio_ble` | 1,454,227 | 1,454,227 | +0 | 164,180 | 164,180 | +0 | differs (65 bytes) | time @ 0x5000004c, magic @ 0x50000048 | magic @ 0x50000048, time @ 0x5000004c | + +
From 24e3ead48809918e6e1c69998449b8efe880dcb0 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Mon, 26 Oct 2026 15:44:18 +0100 Subject: [PATCH 14/15] test(esp32): include in the ESP-IDF stand-ins ESP32RTCClock calls time(), which the ESP32 core declares through Arduino.h. On macOS brings it in, with glibc it does not (CI: 'time' was not declared in this scope). --- test/mocks/esp32_idf/esp32_idf_stubs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/test/mocks/esp32_idf/esp32_idf_stubs.h b/test/mocks/esp32_idf/esp32_idf_stubs.h index dfe5abfbe9..1ad186cada 100644 --- a/test/mocks/esp32_idf/esp32_idf_stubs.h +++ b/test/mocks/esp32_idf/esp32_idf_stubs.h @@ -9,6 +9,7 @@ #include #include +#include // time(), which the ESP32 core declares through Arduino.h // esp_attr.h: on the ESP32 a section in RTC memory that a reset does not clear; // on the host ordinary (zero initialized) memory. From 37cd6a0416039bfae3f2f61ec0b2910022615ca7 Mon Sep 17 00:00:00 2001 From: Marcel Verdult Date: Mon, 26 Oct 2026 15:44:35 +0100 Subject: [PATCH 15/15] docs(refactoring): note 0012, the fix of the stand-ins --- docs/refactoring/0012-esp32-rtc-backup.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/refactoring/0012-esp32-rtc-backup.md b/docs/refactoring/0012-esp32-rtc-backup.md index 688c369205..686ab57e38 100644 --- a/docs/refactoring/0012-esp32-rtc-backup.md +++ b/docs/refactoring/0012-esp32-rtc-backup.md @@ -130,6 +130,9 @@ inlining, which is why the tests also read the pair directly. On the code commit `e6072261` adds the suite to `test_ignore` of the `native` environment, which would otherwise pick it up without the stand ins; it changes no firmware source. +`24e3ead4` includes `` in the stand ins: on macOS `` declares +`time()`, with glibc it does not, and the unit test workflow on Linux failed to +compile the suite without it. It changes no firmware source either. ### Host suites