prelim techo card WIP files while I wait for hardware to arrive. claude putting excessive comments in platformio descriptors

This commit is contained in:
pelgraine
2026-04-12 09:24:37 +10:00
parent 570776478c
commit aacf8c777f
8 changed files with 778 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": [
"-DARDUINO_NRF52840_TECHO_CARD",
"-DNRF52840_XXAA",
"-DNRF52_SERIES"
],
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x8029"]],
"mcu": "nrf52840",
"variant": "lilygo_techo_card",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"usb_product": "T-Echo Card"
},
"connectivity": ["bluetooth", "lora"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"openocd_target": "nrf52840"
},
"frameworks": ["arduino"],
"name": "LilyGo T-Echo Card (nRF52840, SX1262, 4MB Flash)",
"upload": {
"flash_size": "796KB",
"maximum_ram_size": 248832,
"maximum_size": 815104,
"native_usb": true,
"protocol": "nrfutil",
"protocols": ["nrfutil", "jlink", "cmsis-dap"],
"require_upload_port": true,
"speed": 115200,
"use_1200bps_touch": true,
"wait_for_upload_port": true
},
"url": "https://github.com/Xinyuan-LilyGO/T-Echo-Card",
"vendor": "LILYGO"
}
@@ -0,0 +1,169 @@
// =============================================================================
// TechoCardBoard — Implementation for LilyGo T-Echo Card
// =============================================================================
#include "TechoCardBoard.h"
#include "variant.h"
// nRF52840 SAADC includes
#include "nrf.h"
void TechoCardBoard::begin() {
NRF52BoardDCDC::begin();
// Configure battery ADC pin as analog input
pinMode(PIN_VBAT_READ, INPUT);
// Configure button(s)
pinMode(PIN_BUTTON_A, INPUT_PULLUP);
pinMode(PIN_BUTTON_BOOT, INPUT_PULLUP);
// Buzzer off
#if defined(HAS_BUZZER) && PIN_BUZZER >= 0
pinMode(PIN_BUZZER, OUTPUT);
digitalWrite(PIN_BUZZER, LOW);
#endif
// RGB LED off at boot
#if defined(HAS_RGB_LED)
ledOff();
#endif
}
// -----------------------------------------------------------------------------
// Battery voltage reading via nRF52840 SAADC
//
// The T-Echo Card has a voltage divider on AIN0 (P0.02).
// nRF52840 SAADC: 12-bit, internal 0.6V reference, configurable gain.
// With 1/6 gain: input range 03.6V. Multiply by divider ratio (ADC_MULTIPLIER).
//
// NOTE: The T-Echo Lite has a known issue reading 100% / 6.00V constantly.
// This is likely caused by incorrect SAADC configuration (wrong gain, wrong
// reference, or the pin being pulled high by the charging circuit).
// We use explicit SAADC register programming to avoid that issue.
// -----------------------------------------------------------------------------
float TechoCardBoard::getBatteryVoltage() {
uint32_t now = millis();
// Cache battery reading — only read every 10 seconds
if (_cached_battery_mv > 0 && (now - _last_battery_read) < 10000) {
return _cached_battery_mv;
}
// Configure SAADC for single-shot reading
NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_12bit;
// Channel 0: AIN0 (P0.02), 1/6 gain, internal 0.6V reference
// Effective range: 0 3.6V
NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput0; // AIN0
NRF_SAADC->CH[0].PSELN = SAADC_CH_PSELN_PSELN_NC; // Single-ended
NRF_SAADC->CH[0].CONFIG =
(SAADC_CH_CONFIG_GAIN_Gain1_6 << SAADC_CH_CONFIG_GAIN_Pos) |
(SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
(SAADC_CH_CONFIG_TACQ_40us << SAADC_CH_CONFIG_TACQ_Pos) |
(SAADC_CH_CONFIG_MODE_SE << SAADC_CH_CONFIG_MODE_Pos) |
(SAADC_CH_CONFIG_BURST_Disabled << SAADC_CH_CONFIG_BURST_Pos) |
(SAADC_CH_CONFIG_RESP_Bypass << SAADC_CH_CONFIG_RESP_Pos) |
(SAADC_CH_CONFIG_RESN_Bypass << SAADC_CH_CONFIG_RESN_Pos);
// Set up result buffer
volatile int16_t result = 0;
NRF_SAADC->RESULT.PTR = (uint32_t)&result;
NRF_SAADC->RESULT.MAXCNT = 1;
// Enable, calibrate on first use
NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled;
// Start and wait for sample
NRF_SAADC->EVENTS_END = 0;
NRF_SAADC->TASKS_START = 1;
while (!NRF_SAADC->EVENTS_STARTED);
NRF_SAADC->EVENTS_STARTED = 0;
NRF_SAADC->TASKS_SAMPLE = 1;
while (!NRF_SAADC->EVENTS_END);
NRF_SAADC->EVENTS_END = 0;
NRF_SAADC->TASKS_STOP = 1;
while (!NRF_SAADC->EVENTS_STOPPED);
NRF_SAADC->EVENTS_STOPPED = 0;
// Disable SAADC to save power
NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Disabled;
// Convert: voltage = (result / 4096) * 3.6V * ADC_MULTIPLIER * 1000 (mV)
if (result < 0) result = 0;
float voltage_mv = ((float)result / 4096.0f) * 3600.0f * _adc_multiplier;
_cached_battery_mv = voltage_mv;
_last_battery_read = now;
return voltage_mv;
}
uint8_t TechoCardBoard::getBatteryPercent() {
float mv = getBatteryVoltage();
if (mv <= 0) return 0;
// Simple linear approximation for single-cell LiPo
// 3200 mV = 0%, 4200 mV = 100%
if (mv >= 4200.0f) return 100;
if (mv <= 3200.0f) return 0;
return (uint8_t)(((mv - 3200.0f) / 1000.0f) * 100.0f);
}
// -----------------------------------------------------------------------------
// GPS power control
// -----------------------------------------------------------------------------
void TechoCardBoard::enableGPS(bool enable) {
#if defined(HAS_GPS) && PIN_GPS_EN >= 0
digitalWrite(PIN_GPS_EN, enable ? HIGH : LOW);
#endif
#if defined(HAS_GPS) && PIN_GPS_RF_EN >= 0
digitalWrite(PIN_GPS_RF_EN, enable ? HIGH : LOW);
#endif
}
// -----------------------------------------------------------------------------
// Speaker power control
// -----------------------------------------------------------------------------
void TechoCardBoard::enableSpeaker(bool enable) {
#if defined(HAS_SPEAKER)
digitalWrite(PIN_SPK_EN, enable ? HIGH : LOW);
#if PIN_SPK_EN2 >= 0
digitalWrite(PIN_SPK_EN2, enable ? HIGH : LOW);
#endif
#endif
}
// -----------------------------------------------------------------------------
// RGB LED — WS2812 via NeoPixel protocol
// Simple bit-bang implementation for nRF52840 at 64MHz
// -----------------------------------------------------------------------------
void TechoCardBoard::setLED(uint8_t r, uint8_t g, uint8_t b) {
#if defined(HAS_RGB_LED)
// TODO: Implement WS2812 bit-bang or use Adafruit_NeoPixel library
// For initial bringup, just use the Adafruit_NeoPixel library
// which is available in the Adafruit nRF52 Arduino core
(void)r; (void)g; (void)b;
#endif
}
void TechoCardBoard::ledOff() {
setLED(0, 0, 0);
}
// -----------------------------------------------------------------------------
// Buzzer — PWM tone generation
// -----------------------------------------------------------------------------
void TechoCardBoard::buzz(uint16_t freq_hz, uint16_t duration_ms) {
#if defined(HAS_BUZZER) && PIN_BUZZER >= 0
if (freq_hz == 0 || duration_ms == 0) {
noTone(PIN_BUZZER);
return;
}
tone(PIN_BUZZER, freq_hz, duration_ms);
#else
(void)freq_hz; (void)duration_ms;
#endif
}
@@ -0,0 +1,68 @@
#pragma once
// =============================================================================
// TechoCardBoard — Board class for LilyGo T-Echo Card
//
// Extends NRF52BoardDCDC with:
// - Battery ADC (AIN0, P0.02) with solar charging via BQ25896
// - GPS power control (L76K)
// - Speaker/mic enable
// - RGB LED control
// - Buzzer
// - NFC NDEF contact sharing
// =============================================================================
#include <Arduino.h>
#include <helpers/NRF52Board.h>
#include "variant.h"
#ifdef NRF52_POWER_MANAGEMENT
// Power management config for T-Echo Card
// AIN0 (P0.02) for battery voltage sensing
// REFSEL=4 → 5/8 VDD ≈ 2.0625V threshold (with 2:1 divider → ~4.125V cell)
static const PowerMgtConfig TECHO_CARD_POWER_CONFIG = {
.lpcomp_ain_channel = BATTERY_ADC_AIN, // AIN0
.lpcomp_refsel = 4, // 5/8 VDD
.voltage_bootlock = 3100, // Don't boot below 3.1V
};
#endif
class TechoCardBoard : public NRF52BoardDCDC {
private:
float _adc_multiplier;
uint32_t _last_battery_read;
float _cached_battery_mv;
public:
TechoCardBoard()
: _adc_multiplier(ADC_MULTIPLIER),
_last_battery_read(0),
_cached_battery_mv(0) {}
void begin() override;
// Battery
float getBatteryVoltage() override;
uint8_t getBatteryPercent() override;
float getAdcMultiplier() override { return _adc_multiplier; }
void setAdcMultiplier(float mult) { _adc_multiplier = mult; }
// GPS power control
void enableGPS(bool enable);
// Speaker power control
void enableSpeaker(bool enable);
// RGB LED
void setLED(uint8_t r, uint8_t g, uint8_t b);
void ledOff();
// Buzzer
void buzz(uint16_t freq_hz, uint16_t duration_ms);
#ifdef NRF52_POWER_MANAGEMENT
const PowerMgtConfig* getPowerConfig() const override {
return &TECHO_CARD_POWER_CONFIG;
}
#endif
};
+60
View File
@@ -0,0 +1,60 @@
#pragma once
// =============================================================================
// Arduino pin compatibility header for LilyGo T-Echo Card
//
// This file provides Arduino-standard pin name aliases for the nRF52840 GPIOs.
// Only needed if creating a custom board variant inside the Adafruit nRF52
// Arduino framework package. If using build flag overrides in platformio.ini,
// this file is optional.
// =============================================================================
// On nRF52840, Arduino digital pin numbers map 1:1 to nRF GPIO numbers
// (047 for port 0 and port 1).
// LED
#define LED_BUILTIN PIN_LED1
#define PIN_LED1 39 // WS2812 RGB LED data (1, 7)
#define LED_STATE_ON 1
// Buttons
#define PIN_BUTTON1 42 // Button A — orange front button (1, 10)
#define PIN_BUTTON2 24 // Boot button (0, 24)
// Serial (USB CDC)
// nRF52840 native USB — no UART pin assignment needed for Serial
// Serial1 is used for GPS
#define PIN_SERIAL1_RX 21 // GPS TX → nRF RX (0, 21)
#define PIN_SERIAL1_TX 19 // nRF TX → GPS RX (0, 19)
// I2C
#define PIN_WIRE_SDA 36 // (1, 4)
#define PIN_WIRE_SCL 34 // (1, 2)
// SPI (LoRa — directly mapped, RadioLib handles pin control)
#define PIN_SPI_MISO 17 // (0, 17)
#define PIN_SPI_MOSI 15 // (0, 15)
#define PIN_SPI_SCK 13 // (0, 13)
// Analog
#define PIN_A0 2 // (0, 2) — Battery ADC / AIN0
// QSPI Flash (ZD25WQ32CEIGR 4MB)
// nRF52840 QSPI uses fixed pins — framework handles these
// #define PIN_QSPI_SCK 19 // Conflict with GPS TX — check if QSPI is on different pins
// NOTE: QSPI pin mapping needs verification on actual hardware.
// The T-Echo Card may use SPI (not QSPI) for external flash.
// NFC (dedicated nRF52840 NFC pins — not GPIO-assignable)
// NFC1 = P0.09, NFC2 = P0.10
// These are only usable as NFC when NFC is enabled in UICR.
// If NFC is disabled, they become GPIO9 and GPIO10.
// PDM Microphone
#define PIN_PDM_CLK 35 // (1, 3)
#define PIN_PDM_DIN 37 // (1, 5)
// I2S Speaker (MAX98357)
#define PIN_I2S_SCK 16 // BCLK (0, 16)
#define PIN_I2S_LRCK 22 // LRCK / WS (0, 22)
#define PIN_I2S_SDOUT 20 // DATA (0, 20)
+106
View File
@@ -0,0 +1,106 @@
; =============================================================================
; LilyGo T-Echo Card — Meck variant configuration
;
; nRF52840 + SX1262 (HPB16B3) + SSD1315 OLED (72×40) + L76K GPS
; + MAX98357 Speaker + MP34DT05 PDM Mic + ICM20948 IMU + Solar + NFC
;
; Platform: nRF52 (Adafruit nRF52 Arduino)
; =============================================================================
; --- Base configuration for all T-Echo Card builds ---
[lilygo_techo_card]
extends = nrf52_base
platform = https://github.com/maxgerhardt/platform-nordicnrf52.git
board = lilygo_techo_card
board_check = false
extra_scripts =
create-uf2.py
build_flags = ${nrf52_base.build_flags}
-I variants/lilygo_techo_card
-D LILYGO_TECHO_CARD
-D NRF52_POWER_MANAGEMENT
; I2C
-D PIN_BOARD_SDA=36
-D PIN_BOARD_SCL=34
; LoRa SX1262 (HPB16B3 module)
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D P_LORA_NSS=11
-D P_LORA_DIO_1=40
-D P_LORA_RESET=7
-D P_LORA_BUSY=14
-D P_LORA_SCLK=13
-D P_LORA_MISO=17
-D P_LORA_MOSI=15
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D SX126X_DIO2_AS_RF_SWITCH=1
; Display — SSD1315 OLED (72×40, I2C, SSD1306-compatible)
-D DISPLAY_CLASS=SSD1306Display
-D PIN_OLED_RESET=-1
; GPS — L76K
-D HAS_GPS=1
-D PIN_GPS_TX=19
-D PIN_GPS_RX=21
-D PIN_GPS_EN=47
-D GPS_BAUDRATE=9600
-D ENV_INCLUDE_GPS=1
; Battery ADC
-D PIN_VBAT_READ=2
; User button
-D PIN_USER_BTN=42
; Board class
-D BOARD_CLASS=TechoCardBoard
build_src_filter = ${nrf52_base.build_src_filter}
+<../variants/lilygo_techo_card/*.cpp>
lib_deps = ${nrf52_base.lib_deps}
olikraus/U8g2 @ ^2.35.19
stevemarple/MicroNMEA @ ^2.0.6
; =============================================================================
; Build Environments
; =============================================================================
; --- BLE Companion Radio ---
; Pairs with MeshCore companion app (Android/iOS/Web) over Bluetooth.
; Includes GPS for location and time sync.
[env:meck_techo_card_companion_radio_ble]
extends = lilygo_techo_card
build_flags = ${lilygo_techo_card.build_flags}
-D FIRMWARE_NAME='"Meck T-Echo Card BLE"'
; Debug (disable for release)
; -D MESH_DEBUG
; -D BLE_DEBUG_LOGGING
build_src_filter = ${lilygo_techo_card.build_src_filter}
+<../examples/companion_radio/*.cpp>
; --- Repeater ---
; Standalone LoRa repeater node. GPS for position adverts.
; OLED shows status. Solar + 800mAh battery for outdoor deployment.
[env:meck_techo_card_repeater]
extends = lilygo_techo_card
build_flags = ${lilygo_techo_card.build_flags}
-D FIRMWARE_NAME='"Meck T-Echo Card Repeater"'
; -D MESH_DEBUG
build_src_filter = ${lilygo_techo_card.build_src_filter}
+<../examples/simple_repeater/*.cpp>
; --- Room Server ---
; BBS-style message board node.
[env:meck_techo_card_room_server]
extends = lilygo_techo_card
build_flags = ${lilygo_techo_card.build_flags}
-D FIRMWARE_NAME='"Meck T-Echo Card Room"'
build_src_filter = ${lilygo_techo_card.build_src_filter}
+<../examples/simple_room_server/*.cpp>
; --- Sensor Node ---
; Telemetry node with GPS position reporting.
; IMU data (ICM20948) can be added as a custom sensor source.
[env:meck_techo_card_sensor]
extends = lilygo_techo_card
build_flags = ${lilygo_techo_card.build_flags}
-D FIRMWARE_NAME='"Meck T-Echo Card Sensor"'
build_src_filter = ${lilygo_techo_card.build_src_filter}
+<../examples/simple_sensor/*.cpp>
+95
View File
@@ -0,0 +1,95 @@
// =============================================================================
// MeshCore target implementation for LilyGo T-Echo Card
//
// nRF52840 + SX1262 (HPB16B3 module) + SSD1315 OLED + L76K GPS
// =============================================================================
#include "target.h"
#include "variant.h"
// --- SPI for LoRa radio (software SPI on nRF52) ---
// The HPB16B3 SX1262 module uses dedicated SPI pins, not shared with anything else.
// RadioLib Module handles the SPI internally when given pin numbers.
static Module radio_module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
// --- Radio driver ---
RADIO_CLASS radio_driver(&radio_module);
WRAPPER_CLASS radio_wrapper(radio_driver);
// --- Board ---
TechoCardBoard board;
// --- Display (SSD1306-compatible, SSD1315 at 0x3C, 72x40) ---
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display(OLED_WIDTH, OLED_HEIGHT);
#endif
// --- MeshCore stores ---
mesh::IdentityStore identity_store;
mesh::NodePrefs node_prefs;
mesh::DataStore data_store;
// --- Sensor manager ---
#if defined(ENV_INCLUDE_GPS) && ENV_INCLUDE_GPS
#include <helpers/sensors/MicroNMEALocationProvider.h>
static MicroNMEALocationProvider gps_provider(Serial1);
EnvironmentSensorManager sensor_manager(gps_provider);
#else
EnvironmentSensorManager sensor_manager;
#endif
// --- Target initialization ---
void target_setup() {
// Enable OLED power
#if PIN_OLED_EN >= 0
pinMode(PIN_OLED_EN, OUTPUT);
digitalWrite(PIN_OLED_EN, HIGH);
delay(10);
#endif
// Enable GPS power
#if defined(HAS_GPS) && PIN_GPS_EN >= 0
pinMode(PIN_GPS_EN, OUTPUT);
digitalWrite(PIN_GPS_EN, HIGH);
delay(10);
#endif
// GPS RF/LNA enable
#if defined(HAS_GPS) && PIN_GPS_RF_EN >= 0
pinMode(PIN_GPS_RF_EN, OUTPUT);
digitalWrite(PIN_GPS_RF_EN, HIGH);
#endif
// Initialize GPS UART
#if defined(HAS_GPS)
Serial1.setPins(PIN_GPS_RX, PIN_GPS_TX);
Serial1.begin(GPS_BAUDRATE);
#endif
// Speaker off by default (save power)
#if defined(HAS_SPEAKER)
pinMode(PIN_SPK_EN, OUTPUT);
digitalWrite(PIN_SPK_EN, LOW);
#if PIN_SPK_EN2 >= 0
pinMode(PIN_SPK_EN2, OUTPUT);
digitalWrite(PIN_SPK_EN2, LOW);
#endif
#endif
// Initialize I2C
Wire.setPins(I2C_SDA, I2C_SCL);
Wire.begin();
Wire.setClock(400000);
// Initialize display
#ifdef DISPLAY_CLASS
display.begin(OLED_I2C_ADDR);
#endif
// Board-level init
board.begin();
// Initialize LoRa radio
// SX1262 DIO2 as RF switch control (common for HPB16B3 modules)
radio_driver.setDio2AsRfSwitch(true);
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
// =============================================================================
// MeshCore target declarations for LilyGo T-Echo Card
// =============================================================================
#include <Arduino.h>
#include <RadioLib.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/NRF52Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/IdentityStore.h>
#include <helpers/NodePrefs.h>
#include <helpers/DataStore.h>
#include <helpers/SensorManager.h>
#ifdef DISPLAY_CLASS
#include <helpers/ui/DisplaySSD1306.h>
#endif
#include "TechoCardBoard.h"
// Hardware object declarations (instantiated in target.cpp)
extern RADIO_CLASS radio_driver;
extern WRAPPER_CLASS radio_wrapper;
extern TechoCardBoard board;
#ifdef DISPLAY_CLASS
extern DISPLAY_CLASS display;
#endif
extern mesh::IdentityStore identity_store;
extern mesh::NodePrefs node_prefs;
extern mesh::DataStore data_store;
extern EnvironmentSensorManager sensor_manager;
// Target initialization
void target_setup();
+193
View File
@@ -0,0 +1,193 @@
#pragma once
// =============================================================================
// LilyGo T-Echo Card — Pin Definitions for Meck Firmware
//
// nRF52840 + SX1262 + SSD1315 (72×40 OLED) + L76K GPS + MAX98357 Speaker
// + MP34DT05 PDM Microphone + ICM20948 IMU + BQ25896 Charger + Solar
//
// Pin notation from LilyGo pinmap: (port, pin) → nRF GPIO = port*32 + pin
// =============================================================================
#define LILYGO_TECHO_CARD
// -----------------------------------------------------------------------------
// I2C Bus (shared: OLED, IMU ICM20948, fuel gauge BQ27220 if present)
// -----------------------------------------------------------------------------
#define I2C_SDA 36 // (1, 4)
#define I2C_SCL 34 // (1, 2)
#define PIN_BOARD_SDA I2C_SDA
#define PIN_BOARD_SCL I2C_SCL
// -----------------------------------------------------------------------------
// SX1262 LoRa Radio (SPI bit-bang on nRF52)
// -----------------------------------------------------------------------------
#define P_LORA_NSS 11 // (0, 11) — CS
#define P_LORA_RESET 7 // (0, 7) — RST
#define P_LORA_SCLK 13 // (0, 13) — SCK
#define P_LORA_MOSI 15 // (0, 15) — MOSI
#define P_LORA_MISO 17 // (0, 17) — MISO
#define P_LORA_BUSY 14 // (0, 14) — BUSY
#define P_LORA_DIO_1 40 // (1, 8) — DIO1 / interrupt
// RF switch control (HPB16B3 module)
#define LORA_DIO2 5 // (0, 5) — DIO2 (TXCO / RF switch)
#define LORA_RF_VC1 27 // (0, 27) — RF_VC1
#define LORA_RF_VC2 33 // (1, 1) — RF_VC2
// LoRa radio power enable (from schematic: LORA_EN drives RT9080 for LORA_VDD)
// NOTE: Confirm actual GPIO — pinmap shows dedicated enable pin
// For now use -1 if always powered
#define PIN_LORA_EN -1
// Default radio settings (Australia)
#ifndef LORA_TX_POWER
#define LORA_TX_POWER 22
#endif
// -----------------------------------------------------------------------------
// 0.42" OLED Display — SSD1315 (SSD1306-compatible), 72×40, I2C
// -----------------------------------------------------------------------------
#define HAS_OLED 1
#define OLED_I2C_ADDR 0x3C
#define OLED_WIDTH 72
#define OLED_HEIGHT 40
#define OLED_SDA I2C_SDA
#define OLED_SCL I2C_SCL
// OLED power control via RT9080 enable pin
#define PIN_OLED_EN 30 // (0, 30) — RT9080_EN
// No hardware reset pin for OLED on T-Echo Card
#define PIN_OLED_RESET -1
// -----------------------------------------------------------------------------
// GPS — L76K Multi-GNSS (GPS, GLONASS, BeiDou, QZSS)
// -----------------------------------------------------------------------------
#define HAS_GPS 1
#define GPS_BAUDRATE 9600
#define PIN_GPS_TX 19 // (0, 19) — nRF TX → GPS RX
#define PIN_GPS_RX 21 // (0, 21) — nRF RX ← GPS TX
#define PIN_GPS_EN 47 // (1, 15) — GPS power enable
#define PIN_GPS_WAKEUP 25 // (0, 25) — GPS wakeup
#define PIN_GPS_1PPS 23 // (0, 23) — 1PPS time pulse
#define PIN_GPS_RF_EN 29 // (0, 29) — GPS RF / LNA enable
// -----------------------------------------------------------------------------
// Battery & Power
// -----------------------------------------------------------------------------
#define PIN_VBAT_READ 2 // (0, 2) = AIN0 — battery voltage ADC
#define BATTERY_ADC_AIN 0 // nRF SAADC AIN channel number
#define BATTERY_CAPACITY_MAH 800
// Battery voltage divider calibration
// The T-Echo Lite has issues reading battery correctly — we'll need to
// calibrate this with actual hardware. Starting with a reasonable default.
// nRF52840 SAADC: 0.6V internal ref, 1/6 gain → 03.6V range
// If there's a voltage divider (e.g. 2:1), multiply by 2.
// Adjust ADC_MULTIPLIER after measuring real voltage vs ADC reading.
#ifndef ADC_MULTIPLIER
#define ADC_MULTIPLIER 2.0f
#endif
// BQ25896 charger is on I2C but managed by hardware — no software control needed
// Solar panel input: 0.25W 5V via VBUS
// Auto-shutdown threshold (millivolts)
#define AUTO_SHUTDOWN_MILLIVOLTS 3200
// -----------------------------------------------------------------------------
// Speaker — MAX98357 I2S Class-D Mono Amp (8Ω, 1W)
// -----------------------------------------------------------------------------
#define HAS_SPEAKER 1
#define PIN_SPK_EN 43 // (1, 11) — speaker amplifier enable
#define PIN_SPK_EN2 3 // (0, 3) — secondary enable
#define PIN_SPK_BCLK 16 // (0, 16) — I2S bit clock
#define PIN_SPK_DATA 20 // (0, 20) — I2S data out
#define PIN_SPK_LRCK 22 // (0, 22) — I2S word select / LRCK
// -----------------------------------------------------------------------------
// Microphone — MP34DT05 Digital MEMS PDM
// -----------------------------------------------------------------------------
#define HAS_MICROPHONE 1
#define PIN_MIC_CLK 35 // (1, 3) — PDM clock
#define PIN_MIC_DATA 37 // (1, 5) — PDM data
// -----------------------------------------------------------------------------
// Buttons
// -----------------------------------------------------------------------------
#define PIN_BUTTON_A 42 // (1, 10) — orange button (front, main user button)
#define PIN_BUTTON_BOOT 24 // (0, 24) — boot button (nRF52840 BOOT)
#define PIN_USER_BTN PIN_BUTTON_A
// Button_B is RESET — hardware only, no GPIO
// Active LOW for nRF52 buttons (internal pull-up, press = LOW)
#define BUTTON_ACTIVE_LOW 1
// -----------------------------------------------------------------------------
// Buzzer
// -----------------------------------------------------------------------------
#define HAS_BUZZER 1
#define PIN_BUZZER 38 // (1, 6) — piezo buzzer data / PWM
// -----------------------------------------------------------------------------
// WS2812 RGB LED (3 LEDs in series)
// -----------------------------------------------------------------------------
#define HAS_RGB_LED 1
#define PIN_RGB_LED_1 39 // (1, 7) — WS2812 data 1
#define PIN_RGB_LED_2 44 // (1, 12) — WS2812 data 2
#define PIN_RGB_LED_3 28 // (0, 28) — WS2812 data 3
// Typically only one data pin drives the chain; confirm wiring on hardware
#define PIN_NEOPIXEL PIN_RGB_LED_1
#define NUM_NEOPIXELS 3
// -----------------------------------------------------------------------------
// IMU — ICM20948 9-axis MotionTracking (accelerometer + gyro + compass)
// -----------------------------------------------------------------------------
#define HAS_IMU 1
#define IMU_I2C_ADDR 0x68
#define IMU_SDA I2C_SDA
#define IMU_SCL I2C_SCL
// -----------------------------------------------------------------------------
// NFC — nRF52840 NFC-A (Type 2 Tag)
// NFC uses dedicated NFC1/NFC2 pins on nRF52840 (P0.09 / P0.10)
// These are fixed by silicon — no GPIO config needed.
// NFC is handled by the nRF52 SDK nfc_t2t_lib.
// -----------------------------------------------------------------------------
#define HAS_NFC 1
// -----------------------------------------------------------------------------
// External Flash — ZD25WQ32CEIGR (4MB SPI Flash)
// Uses QSPI interface on nRF52840
// Pin mapping from nRF52840 QSPI peripheral (typically fixed)
// -----------------------------------------------------------------------------
#define HAS_EXT_FLASH 1
// -----------------------------------------------------------------------------
// No SD Card on T-Echo Card (unlike T-Echo which has e-paper + no SD either)
// Settings stored in LittleFS on internal/external flash
// -----------------------------------------------------------------------------
// #define HAS_SDCARD
// -----------------------------------------------------------------------------
// No dedicated RTC chip — time from GPS or BLE companion sync
// nRF52840 has a 32.768 kHz RTC peripheral for timekeeping while running
// -----------------------------------------------------------------------------
// #define HAS_PCF85063_RTC
// -----------------------------------------------------------------------------
// Misc / Compatibility
// -----------------------------------------------------------------------------
// No e-ink display
// #define HAS_EINK
// This board has no physical keyboard
// #define HAS_PHYSICAL_KEYBOARD
// Fallback for code that references GPS_BAUDRATE without HAS_GPS guard
#ifndef GPS_BAUDRATE
#define GPS_BAUDRATE 9600
#endif