Files
wadamesh/scripts/build/patch_radiolib_lr11x0.py
T
Kaj SchittecatandClaude Fable 5 a38ea1e3bb touch: Elecrow ThinkNode M9 port (ESP32-S3 + LR1110 + I2C QWERTY) — bring-up builds 1-9
New board: ThinkNode_M9_companion_radio_touch env, variants/thinknode_m9/
(board glue, LR1110 target, keyboard driver, partitions, M9_PORT.md pin
reference), boards/thinknode_m9.json, device_caps HAS_THINKNODE_M9 block,
HAS_M9_KEYBOARD input path in the touch UI (d-pad focus nav, tab jump keys,
QWERTY typing).

Based on a contributed bring-up patch by ded (SalishMesh), who also remote-
tested every build on a preproduction unit. Hard-won fixes on top of the
patch, each verified on hardware:

- LR1110 radio init -706: preprod chips run transceiver FW 0x0303, which
  rejects DriveDiosInSleepMode (0x012A, added in FW 0x0308) that RadioLib
  7.x sends unconditionally during begin(). New build-time patch
  scripts/build/patch_radiolib_lr11x0.py tolerates it on old FW;
  radio_init() prints the chip's device/FW/errors either way.
- TCXO: DIO3 at 3.3 V (the patch's active-oscillator/tcxo=0 theory gave
  -707 on hardware).
- Display: rotation 1 (not 3), plus the missing landscape s_ui_rotation
  override so LVGL renders 320x240 instead of portrait-into-landscape.
- Keyboard: the controller is a separate ESP32-S2 I2C slave (0x6C) with
  addressed registers - reads select register 0x01 first (bare reads
  return the HW-version register forever). Boot probe logs the
  controller's HW/FW; backlight duty register wired.
- USB pad release: the keyboard bus lives on GPIO20/21 = the S3's native
  USB D-/D+ pads, owned from reset by the ROM USB-Serial-JTAG (D+ pullup
  on SDA). M9Board::begin() releases them before any bus init.
- GPIO35-37 are octal-PSRAM pins on the S3R8 - never drive them (the
  patch's "SD CS = 36" misread package pin 36 = GPIO48; SD is on the
  shared SPI bus, support returns with PIN_SD_CS=48 later).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:49:25 +02:00

53 lines
2.6 KiB
Python

# Patches the vendored RadioLib so LR11x0::config() tolerates old LR1110
# transceiver firmware that predates DriveDiosInSleepMode (opcode 0x012A,
# added in the 0x0308 firmware release). RadioLib >= 7.x sends it
# unconditionally during begin(); older chip firmware answers CMD_PERR,
# which turns an otherwise healthy radio.begin() into -706
# (RADIOLIB_ERR_SPI_CMD_INVALID). Preproduction Elecrow ThinkNode M9 units
# ship with such firmware (Meshtastic works on them only because it pins an
# older RadioLib that doesn't send the command).
#
# The command is an optional nicety (stops DIO glitches in sleep mode), so
# skipping it on old firmware is safe.
#
# Idempotent. Each PlatformIO env has its own libdeps copy, so this only
# affects envs that list this script in extra_scripts (the M9 env).
# NOTE: on a completely fresh checkout the first build may run before
# RadioLib is downloaded - the script then warns and the patch lands on the
# next build. Re-run `pio run` once if you see the warning.
Import("env")
import os
MARKER = "wadamesh-lr1110-oldfw-patch"
OLD = """ state = this->driveDiosInSleepMode(true);
RADIOLIB_ASSERT(state);"""
NEW = """ state = this->driveDiosInSleepMode(true);
// wadamesh-lr1110-oldfw-patch: LR1110 transceiver FW older than 0x0308
// doesn't implement DriveDiosInSleepMode (0x012A) and answers CMD_PERR,
// which would abort init (-706) on an otherwise healthy radio. The command
// only stops DIO glitches in sleep mode - safe to skip on old firmware.
if(state == RADIOLIB_ERR_SPI_CMD_INVALID) {
RADIOLIB_DEBUG_BASIC_PRINTLN("DriveDiosInSleepMode unsupported (old LR11x0 FW), skipping");
state = RADIOLIB_ERR_NONE;
}
RADIOLIB_ASSERT(state);"""
path = os.path.join(env.subst("$PROJECT_LIBDEPS_DIR"), env.subst("$PIOENV"),
"RadioLib", "src", "modules", "LR11x0", "LR11x0.cpp")
if not os.path.isfile(path):
print("[patch_radiolib_lr11x0] WARNING: %s not found (libdeps not fetched yet?)" % path)
print("[patch_radiolib_lr11x0] WARNING: RadioLib NOT patched - re-run the build once libdeps exist")
else:
with open(path) as f:
src = f.read()
if MARKER in src:
print("[patch_radiolib_lr11x0] already patched")
elif OLD in src:
with open(path, "w") as f:
f.write(src.replace(OLD, NEW, 1))
print("[patch_radiolib_lr11x0] patched LR11x0::config() for old-FW tolerance")
else:
print("[patch_radiolib_lr11x0] WARNING: pattern not found - RadioLib version drift?")
print("[patch_radiolib_lr11x0] WARNING: check LR11x0::config() by hand, NOT patched")