mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-08-14 15:59:54 +00:00
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>
68 lines
3.2 KiB
C
68 lines
3.2 KiB
C
#pragma once
|
|
|
|
// ThinkNode M9 keyboard controller. Unlike the T-Deck — where the keyboard
|
|
// shares an I2C bus with the touch controller and must be polled from a
|
|
// dedicated core-0 task — the M9 keyboard lives on its OWN bus (Wire1,
|
|
// SDA=20 / SCL=21, addr 0x6c) with no touch controller present at all. So
|
|
// this can be polled directly from the UI thread; no cross-core ring buffer
|
|
// is strictly required, but one is kept anyway for interface parity with
|
|
// TDeckKeyboard and so a future move to a dedicated poll task is a no-op.
|
|
//
|
|
// PROTOCOL — from Elecrow's own keyboard-controller firmware
|
|
// (ThinkNode-M9-KB-platformio): the keyboard is a separate ESP32-S2 running a
|
|
// matrix scanner as an I2C slave with ADDRESSED REGISTERS. A key read is
|
|
// write-reg(0x01) then read 1 byte (0x00 = no key; the controller latches a
|
|
// single pending key). The earlier "one raw byte per read" protocol note was
|
|
// WRONG — a bare read returns the last-addressed register (0x00 = HW version,
|
|
// constant 0x03), which is why bring-up builds 1-7 saw no keys. The
|
|
// controller resolves shift/symbol/alt layers itself and emits final ASCII
|
|
// for printable keys; non-ASCII bytes are the d-pad / dedicated function
|
|
// keys (see M9_KEY_* below), passed through unchanged, so
|
|
// m9KeyboardReadKey() returns either a printable ASCII code or a sentinel.
|
|
// Backlight: write-reg(0x02) + duty byte; the controller auto-lights on
|
|
// keypress and times out after 10 s on its own.
|
|
#if defined(HAS_M9_KEYBOARD) && defined(ESP32)
|
|
|
|
#include <stdint.h>
|
|
|
|
// Raw byte values confirmed on hardware (Specter bring-up, notes/m9_keycodes.md).
|
|
#define M9_KEY_LEFT 0xB4
|
|
#define M9_KEY_UP 0xB5
|
|
#define M9_KEY_DOWN 0xB6
|
|
#define M9_KEY_RIGHT 0xB7
|
|
#define M9_KEY_ENTER 0x0D // shared with d-pad centre click — indistinguishable
|
|
#define M9_KEY_DEL 0x08
|
|
#define M9_KEY_MIC 0x88 // triangle/mic glyph key
|
|
#define M9_KEY_LEFT_MESSAGE 0x81
|
|
#define M9_KEY_HOME 0x82
|
|
#define M9_KEY_SUB_MESSAGE 0x83
|
|
#define M9_KEY_SUB_MAP 0x84
|
|
#define M9_KEY_MAP 0x85
|
|
#define M9_KEY_HW_BACK 0x86
|
|
#define M9_KEY_CTRL 0x90
|
|
// From the controller firmware's key tables (not yet bound in the UI):
|
|
// 0x84 long-press emits 0x87 (labeled "GPS toggle"), Enter long-press emits
|
|
// 0xA3, and matrix position col3/row4 emits 0x89 (unlabeled). Shift/Sym/Alt
|
|
// never reach the wire — the controller consumes them as layer modifiers.
|
|
#define M9_KEY_GPS_LONG 0x87
|
|
#define M9_KEY_ENTER_LONG 0xA3
|
|
|
|
/** Bring up Wire1 (SDA=PIN_KB_SDA, SCL=PIN_KB_SCL) and mark the keyboard
|
|
* available. Safe to call from the UI thread — this bus has no other
|
|
* device on it. */
|
|
void m9KeyboardBegin();
|
|
|
|
/** Read one key over I2C and push it into the ring. May be called from any
|
|
* single task (this board has no cross-core contention on Wire1). */
|
|
void m9KeyboardPoll();
|
|
|
|
/** Pop the next buffered raw byte (ASCII or M9_KEY_* sentinel), or 0 if none. */
|
|
int m9KeyboardReadKey();
|
|
|
|
/** Set the keyboard backlight PWM duty (0..255) on the controller. The
|
|
* controller lights up on keypress by itself and times out after 10 s;
|
|
* this just sets HOW bright. No-op until the controller has been found. */
|
|
void m9KeyboardSetBacklight(uint8_t duty);
|
|
|
|
#endif
|