Files
wadamesh/variants/tdisplay_p4/target.cpp
T
Kaj SchittecatandClaude Opus 4.8 2353c56adc p4: T-Display P4 port — web VNC/remote, time-sync hardening, brightness + sound
LilyGo T-Display P4 (ESP32-P4 + factory ESP-AT C6) reaches POC: LoRa mesh, Wi-Fi
companion (TCP:5000), GPS, SD, battery gauge, chat persistence, and now:

- Web VNC/REMOTE/terminal via a first-byte router on the single ESP-AT listener
  (HTTP verbs -> WS server, '<' frames -> phone companion; both live at once).
  C6Server gains a _begun gate so never-listening instances stay inert.
- VNC freeze fixed: bounded one-CIPSEND-chunk drains under the WS client mutex
  (a whole-band blocking write starved the main loop), fast-drop of stalled
  mirror clients, SEND-OK wait 8s->4s.
- Wi-Fi robustness: CWJAP? miss-streak tolerance, CIPSERVER liveness verify +
  re-arm (a re-association silently killed the listener until reboot).
- Time sync: HTTP-Date fallback when SNTP is blocked; ClockFloorRTC gains a
  MAX_PLAUSIBLE_EPOCH guard (all boards) after a garbage-future hardware RTC
  read (2043) latched the ratchet.
- Brightness: RM69A10 DCS 0x51 driven live (CC slider + persisted pref).
- Sound: ES8311 + NS4150B notification chimes (P4Audio, esp-bsp-derived
  register sequence on Wire1), full HAS_UI_SOUND surface + CC Screen/Sound
  sliders; ccVolumeReleaseCb's hardcoded tanBeep -> uiSoundPreview.
- tdisplay_p4/: IDF project (build.sh, c6_at AT-over-SDIO driver, sdkconfigs,
  fetch-deps.sh vendoring, .gitignore for local payloads).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:13:23 +02:00

80 lines
3.0 KiB
C++

#include <Arduino.h>
#include <Wire.h> // BQ27220 fuel gauge (getBattMilliVolts)
#include "target.h"
#include <helpers/ArduinoHelpers.h> // RadioNoiseListener / StdRNG
TDisplayP4Board board;
Xl9535 xl9535; // board-global expander (declared extern in Xl9535.h)
// SX1262 on the P4 SPI (SCLK=2/MOSI=3/MISO=4, CS=24, BUSY=6). RESET + DIO1 are on the XL9535
// (P_LORA_RESET/DIO_1 == -1 = RADIOLIB_NC): reset is pulsed via the expander in radio_init(); with
// DIO1 == NC RadioLib polls for RX/TX-done instead of taking a hardware IRQ.
// TODO(device): if polling RX is unreliable, route DIO1 through the XL9535 INT (GPIO5).
static SPIClass spi(FSPI);
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);
DISPLAY_CLASS display; // RM69A10 MIPI-DSI
ESP32RTCClock fallback_clock;
ClockFloorRTC rtc_clock(fallback_clock);
#if ENV_INCLUDE_GPS
#include <helpers/sensors/MicroNMEALocationProvider.h>
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock);
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
#else
EnvironmentSensorManager sensors;
#endif
void TDisplayP4Board::begin() {
ESP32Board::begin();
}
// BQ27220 fuel gauge (I2C_1 @0x55, same shared Wire bus as the XL9535/RTC/touch — TwoWire's
// internal lock makes each transaction atomic across tasks). Standard command reads: 16-bit
// little-endian at Voltage()=0x08 (mV). Reads are rate-limited and sanity-windowed; on any I2C
// hiccup the last good value is kept so the battery UI never flickers to nonsense.
static uint16_t bq27220ReadU16(uint8_t cmd) {
Wire.beginTransmission(0x55);
Wire.write(cmd);
if (Wire.endTransmission(false) != 0) return 0;
if (Wire.requestFrom(0x55, 2) != 2) return 0;
uint16_t lo = Wire.read(), hi = Wire.read();
return (uint16_t)(lo | (hi << 8));
}
uint16_t TDisplayP4Board::getBattMilliVolts() {
static uint32_t last_ms = 0;
static uint16_t last_mv = 3800; // pre-first-read placeholder
static bool logged = false;
const uint32_t now = millis();
if (last_ms == 0 || now - last_ms >= 5000) {
last_ms = now;
const uint16_t mv = bq27220ReadU16(0x08); // Voltage(), mV
if (mv >= 2500 && mv <= 4600) {
last_mv = mv;
if (!logged) { printf("[BATT] BQ27220 alive: %u mV\n", mv); logged = true; }
} else if (!logged) {
printf("[BATT] BQ27220 read invalid (%u) — placeholder in use\n", mv);
logged = true;
}
}
return last_mv;
}
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire); // PCF8563 RTC is on I2C_1 (shared Wire, SDA=7/SCL=8)
// SX1262 RESET is on the XL9535 — pulse it before RadioLib init.
xl9535.sx1262Reset();
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI, P_LORA_NSS);
return radio.std_init(&spi);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // fresh random identity from SX1262 RSSI noise
}