Skip to content

Feature: Add LilyGo T-Echo Card board variant#2451

Open
pelgraine wants to merge 10 commits intomeshcore-dev:devfrom
pelgraine:lilygo_techo_card
Open

Feature: Add LilyGo T-Echo Card board variant#2451
pelgraine wants to merge 10 commits intomeshcore-dev:devfrom
pelgraine:lilygo_techo_card

Conversation

@pelgraine
Copy link
Copy Markdown

@pelgraine pelgraine commented Apr 30, 2026

Adds board support for the LilyGo T-Echo Card — an nRF52840-based LoRa node with SX1262 radio, 72×40 SSD1315 OLED, L76K GPS, WS2812 RGB LEDs, and buzzer. Pin definitions for additional onboard peripherals (MAX98357 speaker amp, MP34DT05 PDM mic, ICM20948 IMU, BQ25896 charge controller) are included in variant.h but no driver integration exists yet.

Please note this was AI-coded and could do with several reviews.

New files

Board definition:

  • boards/lilygo_techo_card.json — PlatformIO board definition (nRF52840, SoftDevice S140 v6)

Variant directory (variants/lilygo_techo_card/):

  • variant.h / variant.cpp — Pin definitions and g_ADigitalPinMap, cross-referenced against LilyGo's t_echo_card_config.h and Meshtastic PR #10267. Fix GPS RX/TX pin mapping (vendor labels are from the GPS chip's perspective; pins 19/21 were reversed). #ifndef guards on buzzer defines.
  • target.h / target.cpp — Hardware interface declarations and instantiation (radio, RTC, GPS, display, sensors). Wire GPSStreamCounter around Serial1 before MicroNMEA.
  • TechoCardBoard.h / TechoCardBoard.cpp — Board class extending NRF52BoardDCDC with GPS power control, WS2812 NeoPixel support (3 LEDs daisy-chained on pin 39), buzzer, speaker enable, and gated battery ADC. Add getMCUTemperature() override using sd_temp_get() (SoftDevice-safe; direct register access hard faults). BQ25896 charger IC driver (I2C 0x6B): probe, register read/write, continuous ADC, charge status, battery mV, thermistor %. ICM20948/AK09916 compass: I2C bypass init, burst magnetometer read, sleep for power saving (~3-4 mA).
  • TechoCardHomeScreen.h — Compact home screen for the 72x40 display with eight pages (Status, Radio, BLE, Advert, GPS, Compass, Battery, Hibernate). NMEA sentence rate display when GPS has no fix. GPS coordinates split across two lines when fix acquired. battPercent() recalibrated to 4.16V full-charge ceiling. Compass heading corrected for PCB axis orientation. On-device magnetometer calibration with persistence.
  • platformio.ini — Five build environments: companion BLE, companion USB, repeater, room server, sensor. AUTO_OFF_MILLIS=60000 for BLE companion build (60 second screen timeout; double-click A to wake).
  • 'GPSStreamCounter.h' -- Transparent Stream wrapper that counts NMEA sentences flowing from the GPS serial port. Displays a live sentences/sec rate on the GPS home screen page when there's no fix yet, confirming the baud rate is correct and the chip is transmitting.

New display driver:

  • src/helpers/ui/U8g2Display.hDisplayDriver implementation using U8g2, targeting U8G2_SSD1306_72X40_ER_F_HW_I2C. This handles all GDDRAM column/page offset mapping natively, avoiding manual offset calculations. Reusable for any future U8g2-based board.

Modified files

  • examples/companion_radio/ui-new/UITask.cpp — Guarded additions for T-Echo Card (#if defined(LILYGO_TECHO_CARD) / #if !defined(LILYGO_TECHO_CARD)); Widen _version_info buffer from [12] to [24]. Double-click screen on/off toggle for battery saving (#if defined(LILYGO_TECHO_CARD)). Hardware GPS power control in toggleGPS() (#if defined(LILYGO_TECHO_CARD)). Triple-click A (user button) toggles buzzer on/off (#if defined(LILYGO_TECHO_CARD)).
    • Include TechoCardHomeScreen.h
    • Text-only splash screen (no room for 128px logo on 72×40)
    • TechoCardHomeScreen instantiation instead of HomeScreen
    • PIN_BOOT_BTN handler for second navigation button + double-click torch toggle
    • MsgPreviewScreen guarded out — the 72×40 display is too small for message previews. The unread count still updates and the display still wakes on incoming messages, but stays on the home screen. Also saves nRF52 heap by skipping the allocation.
    • 'examples/companion_radio/MyMesh.cpp' -- Add MCU die temperature to both telemetry response paths (all platforms). Add hasPendingWork() method checking outbound queue and bridge state.
    • 'examples/companion_radio/MyMesh.h' -- hasPendingWork() declaration. Call board.enableGPS() at boot if GPS was persisted as off (#if defined(LILYGO_TECHO_CARD)).
    • 'examples/companion_radio/main.cpp' -- Call board.sleep(0) when no pending work (#if defined(NRF52_PLATFORM)).

Hardware notes

  • WS2812 boot fix: The data line (pin 39) is driven LOW before Adafruit_NeoPixel::begin() with a 300µs reset pulse to prevent stray HIGH latching green into the first LED during power-on.
  • RT9080 3V3 rail: Clean HIGH→LOW→HIGH toggle on PIN_OLED_EN at boot to prevent brown-out when LoRa TX fires at full power (from Meshtastic PR #10267).
  • LED_BUILTIN pointed at unused GPIO 46 (P1.14) to prevent the Adafruit BSP's BLE status driver from holding the WS2812 data line HIGH.
  • WS2812 topology: Hardware-verified as 3 LEDs daisy-chained on pin 39 (not 3 separate GPIOs as mapped in Meshtastic PR #10267).
  • TCXO: DIO3 at 1.8V (SX126X_DIO3_TCXO_VOLTAGE), DIO2 as RF switch.
  • GPS: L76K at 9600 baud with separate power enable (pin 47) and RF frontend enable (pin 29). Toggle on/off from home screen GPS page.
  • GPS pin mapping: Vendor schematic labels GPS_UART_TX / GPS_UART_RX refer to the GPS chip's perspective. The nRF's PIN_SERIAL1_RX is pin 19 (connected to GPS TX), and PIN_SERIAL1_TX is pin 21 (connected to GPS RX).
  • Torch: Double-click the C/Boot button (pin 24) to toggle a single WS2812 LED to white. Driving all three simultaneously causes a reboot (likely exceeds RT9080 current budget). Works from any home screen page.
  • BQ25896 charger: I2C at 0x6B. ADC provides battery voltage and thermistor readings.
  • ICM20948 compass: I2C bypass mode exposes the AK09916 magnetometer directly on Wire at 0x0C. Single-measurement mode (continuous mode is unreliable through bypass with OLED sharing the I2C bus). On-device calibration: long-press A on compass page, rotate slowly for ~20 seconds, hard-iron offsets and soft-iron scale factors computed and persisted to InternalFS. EMA smoothing for stable heading display. Y axis inverted in atan2 to match PCB orientation.
  • nRF52 idle sleep: board.sleep(0) called between loop iterations when no outbound packets are queued, reducing idle current.

Testing status

Build Status
techo_card_companion_radio_ble ✅ Hardware-verified -- BLE pairing, display, buttons, GPS, NeoPixels, buzzer, compass with on-device calibration, battery page, MCU temp telemetry
techo_card_companion_radio_usb ⚙️ Compiles — untested on hardware
techo_card_repeater ⚙️ Compiles — untested on hardware
techo_card_room_server ⚙️ Compiles — untested on hardware
techo_card_sensor ⚙️ Compiles — untested on hardware

Dependencies added

  • olikraus/U8g2 @ ^2.35.19
  • adafruit/Adafruit NeoPixel @ ^1.12.3
  • adafruit/Adafruit SSD1306 @ ^2.5.12 (+ Adafruit GFX, BusIO)
  • end2endzone/NonBlockingRtttl @ ^1.3.0
  • stevemarple/MicroNMEA @ ^2.0.6
IMG_2660 IMG_2663 IMG_2664 IMG_2665 IMG_2666
IMG_2667 IMG_2668 IMG_2669 IMG_2661 IMG_2674

…_msgcount still updates and the display still wakes on new messages, but it stays on the home screen instead of switching to a preview that's unreadable at 72×40. Also saves a bit of nRF52 heap by skipping the allocation.

TechoCardHomeScreen.h — Battery % moved from Y0 down to Y1, right-aligned opposite the MSG count. Node name now has the full top line to itself.
@pelgraine pelgraine marked this pull request as ready for review April 30, 2026 14:27
@pelgraine pelgraine marked this pull request as draft April 30, 2026 23:04
pelgraine added 2 commits May 1, 2026 10:08
…le sleep.

New file:
- GPSStreamCounter.h: Stream wrapper counting NMEA sentences for
  live baud-rate confirmation on the GPS home screen page.

Variant fixes:
- variant.h: swap GPS RX/TX pins (vendor labels are chip-perspective,
  not nRF-perspective); #ifndef guards on buzzer defines.
- target.cpp/h: wire GPSStreamCounter around Serial1.
- TechoCardBoard: getMCUTemperature() via sd_temp_get (SoftDevice-safe),
  BQ25896 charger I2C API, ICM20948/AK09916 compass init/read/sleep.
- TechoCardHomeScreen.h: COMPASS + BATTERY pages, NMEA rate when no
  GPS fix, battPercent() recalibrated to 4.16V full-charge ceiling.

Cross-platform (all guarded behind LILYGO_TECHO_CARD or NRF52_PLATFORM):
- MyMesh.cpp: MCU die temp in both telemetry paths; hasPendingWork().
- MyMesh.h: hasPendingWork() decl; GPS enable-at-boot (#if TECHO_CARD).
- main.cpp: board.sleep(0) when idle (#if NRF52_PLATFORM).
- UITask.cpp: _version_info[24]; double-click screen toggle and GPS
  hardware enable/disable (#if LILYGO_TECHO_CARD).
…o off milis to 60 seconds instead of default 15 in platformio; compass fix and calibration fix in techoboardhome
@pelgraine pelgraine marked this pull request as ready for review May 1, 2026 02:15
@pelgraine pelgraine changed the title Add LilyGo T-Echo Card support (nRF52840 + SX1262 + SSD1315 72×40 OLED) Feature: Add LilyGo T-Echo Card board variant May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant