Add Simpson board support

This commit is contained in:
sim1988son
2026-10-15 20:39:48 +02:00
parent 1f47fc56cd
commit 539271f293
13 changed files with 466 additions and 12 deletions
@@ -670,6 +670,16 @@ public:
: _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0),
_prev_page(0), _shutdown_init(false), sensors_lpp(200) { }
void showBluetoothPage() {
_page = BLUETOOTH;
_quick_sel = 0;
}
void showGpsPage() {
_page = STATUS;
_quick_sel = 0;
}
// Flips home screen between LOCK page and the page that was showing before locking
void setLocked(bool locked) {
if (locked) {
@@ -291,6 +291,9 @@ struct KeyboardWidget {
// next letter, then reverts) — Hold-Enter on Shift toggles caps_lock, which
// keeps it on for a whole run of capitals instead.
bool caps_lock = false;
bool _physical_t9_input = false;
const char* input_mode_hint = nullptr;
bool hide_keyboard_grid = false;
// Set by render() every time it's actually called; UITask clears it before
// curr->render() each frame (beginFrame()) so it reflects only "was the
// keyboard the thing on screen this frame" -- lets the alert overlay (new
@@ -355,6 +358,16 @@ struct KeyboardWidget {
// the #@/abc key's existing cycle (case 4 below) reaches it for free.
int totalPages() const { return hasAltAlphabet() ? 3 : 2; }
bool pageIsSymbols(int pg) const { return pg == totalPages() - 1; }
void openSymbolsPage() {
if (!_visible || isPin() || _ph_menu.active || cursor_mode || accent_active) return;
page = totalPages() - 1;
t9_cell = -1;
}
void backspaceTyped() {
if (_visible) handleInput(0x08);
}
// Which script (Latin/Cyrillic/Greek) the given non-symbols page shows.
uint8_t scriptAt(int pg) const { return pg == 0 ? mainScript() : altScript(); }
@@ -492,6 +505,29 @@ struct KeyboardWidget {
insertGlyph(one, false);
}
#if defined(SIMPSON)
void typePhysicalT9Cell(int cell, bool uppercase) {
if (!_visible || isPin() || cell < 0 || cell >= KB_T9_ROWS * KB_T9_COLS ||
_ph_menu.active || cursor_mode || accent_active) return;
const int saved_row = row;
const int saved_col = col;
const int saved_page = page;
const bool saved_caps = caps;
row = cell / KB_T9_COLS;
col = cell % KB_T9_COLS;
page = 0;
caps = uppercase;
_physical_t9_input = true;
handleInput(KEY_ENTER);
_physical_t9_input = false;
row = saved_row;
col = saved_col;
page = saved_page;
caps = saved_caps;
}
#endif
void clearPlaceholders() { _ph_count = 0; }
void addPlaceholder(const char* ph) {
@@ -604,7 +640,8 @@ struct KeyboardWidget {
// lines, so the preview keeps the same height whichever mode is on.
// A tall screen (the 4.2" panel: 20 lines) gives the keys half a line more each.
const int key_lh = display.height() >= 18 * lh ? lh * 3 / 2 : lh;
const int kb_h = compact_ui ? (KB_T9_ROWS + 1) * lh : (rows + 1) * key_lh;
const bool hide_grid = hide_keyboard_grid && !isPin();
const int kb_h = hide_grid ? lh : (compact_ui ? (KB_T9_ROWS + 1) * lh : (rows + 1) * key_lh);
const int preview_h = display.height() - kb_h - display.sepH();
const int prev_lines = (preview_h / lh) > 1 ? (preview_h / lh) : 1;
const int sep_y = prev_lines * lh;
@@ -794,13 +831,21 @@ struct KeyboardWidget {
// submit -- so they get no hint of their own). Physical buttons (if used
// instead of/alongside CardKB) still drive row/col/page as normal; it
// just won't be visible on this screen which cell is selected.
if (compact_ui) {
display.setColor(UIColor::primary_txt);
if (cursor_mode) {
display.drawTextCentered(display.width() / 2, chars_y, "<> char ^v word");
} else {
display.drawTextCentered(display.width() / 2, chars_y, "Tab: placeholders");
display.drawTextCentered(display.width() / 2, chars_y + lh, "Fn+letter: accent");
if (compact_ui || hide_grid) {
if (!hide_grid) {
display.setColor(UIColor::primary_txt);
if (cursor_mode) {
display.drawTextCentered(display.width() / 2, chars_y, "<> char ^v word");
} else {
display.drawTextCentered(display.width() / 2, chars_y, "Tab: placeholders");
display.drawTextCentered(display.width() / 2, chars_y + lh, "Fn+letter: accent");
}
} else if (input_mode_hint) {
char hint[40];
snprintf(hint, sizeof(hint), "FN: %s", input_mode_hint);
display.setColor(UIColor::primary_txt);
display.setCursor(display.width() - display.getTextWidth(hint) - 2, display.height() - lh);
display.print(hint);
}
} else {
// character grid
@@ -1228,7 +1273,11 @@ struct KeyboardWidget {
// the special row's DONE cell -- same as a physical button. CardKB's
// own Enter is only ever KEY_ENTER when that's true (see pollCardKB()'s
// KEY_KB_ENTER cases just above), so there's no ambiguity here.
if (row < rows && isT9()) {
if (row < rows && isT9()
#if defined(SIMPSON)
&& !_physical_t9_input
#endif
) {
int cell = row * cols + col;
const char* group = t9GroupStr(cell);
int glen = kbUtf8Len(group); // codepoint count, not byte length
+135
View File
@@ -11,6 +11,9 @@
#include "target.h"
#include "CardKB.h"
#if defined(SIMPSON)
#include <string.h>
#endif
#ifdef WIFI_SSID
#include <WiFi.h>
#endif
@@ -120,6 +123,13 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
_sensors = sensors;
_node_prefs = node_prefs;
_kb.prefs = node_prefs;
#if defined(SIMPSON)
if (!_simpson_keypad.begin()) {
MESH_DEBUG_PRINTLN("Simpson keypad initialization failed");
}
_kb.input_mode_hint = "Ctl";
_kb.hide_keyboard_grid = true;
#endif
if (node_prefs) battery::useCurve(node_prefs->batt_curve_mv); // Settings > Battery curve
uint32_t aoff = autoOffMillis();
_auto_off = millis() + (aoff > 0 ? aoff : AUTO_OFF_MILLIS);
@@ -402,6 +412,20 @@ void UITask::gotoMessagesScreen() {
setCurrScreen(messages_screen);
}
#if defined(SIMPSON)
void UITask::gotoBluetoothHomePage() {
setCurrScreen(home);
static_cast<HomeScreen*>(home)->showBluetoothPage();
_next_refresh = 0;
}
void UITask::gotoGpsHomePage() {
setCurrScreen(home);
static_cast<HomeScreen*>(home)->showGpsPage();
_next_refresh = 0;
}
#endif
#ifdef SIM_PLATFORM
void UITask::simMessage(const char* text) {
UiEvent ev = {};
@@ -1066,6 +1090,114 @@ void UITask::pollHallSensor() {
#endif
}
#if defined(SIMPSON)
void UITask::pollSimpsonKeypad() {
const uint8_t key = _simpson_keypad.poll();
if (key != SIMPSON_KEY_NONE) handleSimpsonKeypadKey(key);
}
void UITask::handleSimpsonKeypadKey(uint8_t key) {
if (checkDisplayOn(KEY_ENTER) == 0 || (_locked && !_unlock_kb)) return;
const uint32_t auto_off = autoOffMillis();
if (auto_off > 0) _auto_off = millis() + auto_off;
if (key == SIMPSON_KEY_MODE_CHANGED) {
static const char* const modes[] = { "Ctl", "abc", "ABC", "123" };
_kb.input_mode_hint = modes[_simpson_keypad.mode()];
_kb.hide_keyboard_grid = true;
showAlert(modes[_simpson_keypad.mode()], 1200);
_next_refresh = 0;
return;
}
switch (key) {
case SIMPSON_KEY_CANCEL:
enqueueKey(KEY_CANCEL);
break;
case SIMPSON_KEY_UP:
enqueueKey(KEY_UP);
break;
case SIMPSON_KEY_DOWN:
enqueueKey(KEY_DOWN);
break;
case SIMPSON_KEY_LEFT:
enqueueKey(KEY_LEFT);
break;
case SIMPSON_KEY_RIGHT:
enqueueKey(KEY_RIGHT);
break;
case SIMPSON_KEY_ENTER:
enqueueKey(_unlock_kb ? KEY_KB_ENTER : KEY_ENTER);
break;
case SIMPSON_KEY_BLUETOOTH:
if (!_unlock_kb) gotoBluetoothHomePage();
break;
case SIMPSON_KEY_GPS:
if (!_unlock_kb) gotoGpsHomePage();
break;
case SIMPSON_KEY_MESSAGES:
if (!_unlock_kb) gotoMessagesScreen();
break;
case SIMPSON_KEY_BRIGHTNESS_UP:
case SIMPSON_KEY_BRIGHTNESS_DOWN:
if (!_unlock_kb && _node_prefs) {
uint8_t& brightness = _node_prefs->display_brightness;
if (key == SIMPSON_KEY_BRIGHTNESS_UP && brightness < 4) ++brightness;
if (key == SIMPSON_KEY_BRIGHTNESS_DOWN && brightness > 0) --brightness;
applyBrightness();
bool dirty = true;
savePrefsIfDirty(dirty);
}
break;
case SIMPSON_KEY_SYMBOLS:
if (_unlock_kb) break;
if (_kb.isVisible()) _kb.openSymbolsPage();
else showAlert("Open a text field first", 1200);
break;
case SIMPSON_KEY_BACKSPACE:
if (_kb.isVisible()) {
if (_unlock_kb) handleUnlockKey(0x08);
else _kb.backspaceTyped();
} else {
enqueueKey(KEY_CANCEL);
}
break;
default:
if (_kb.isVisible()) {
const auto typeInput = [this](char c) {
if (_unlock_kb) handleUnlockKey(c);
else _kb.handleInput(c);
};
switch (_simpson_keypad.mode()) {
case SIMPSON_KEYPAD_ABC:
case SIMPSON_KEYPAD_ABC_UPPER:
if (key == ',') {
_kb.typePhysicalT9Cell(0, _simpson_keypad.mode() == SIMPSON_KEYPAD_ABC_UPPER);
} else {
static const char t9_keys[] = "ADGJMPTW";
const char* found = strchr(t9_keys, (char)key);
if (found) {
_kb.typePhysicalT9Cell((int)(found - t9_keys) + 1,
_simpson_keypad.mode() == SIMPSON_KEYPAD_ABC_UPPER);
} else if (key == ' ') {
typeInput(' ');
}
}
break;
case SIMPSON_KEYPAD_NUMERIC:
if (key >= '0' && key <= '9') typeInput((char)key);
break;
case SIMPSON_KEYPAD_CONTROL:
break;
}
}
break;
}
_next_refresh = 0;
}
#endif
#include "PerfL1.h"
void UITask::loop() {
@@ -1293,6 +1425,9 @@ void UITask::loop() {
}
#endif
pollCardKB();
#if defined(SIMPSON)
pollSimpsonKeypad();
#endif
pollHallSensor();
#ifdef ENV_USE_TCA8418
{
+14
View File
@@ -8,6 +8,9 @@
#include <helpers/ContactInfo.h>
#include <Arduino.h>
#include <helpers/sensors/LPPDataHelpers.h>
#if defined(SIMPSON)
#include <SimpsonKeypad.h>
#endif
#ifndef LED_STATE_ON
#define LED_STATE_ON 1
@@ -80,6 +83,9 @@ class UITask : public UITaskBase, public UiCoreHost {
// each) + NUL = 372B, rounded up with margin.
char _bot_buzz_buf[400];
KeyboardWidget _kb; // shared across all screens — only one active at a time
#if defined(SIMPSON)
SimpsonKeypad _simpson_keypad;
#endif
unsigned long _alert_expiry;
unsigned long _alert_t0 = 0; // when the banner began to come down
// Settings > Message alert Compact: the alert is an envelope and a count
@@ -231,6 +237,10 @@ private:
#endif
#endif
void pollCardKB();
#if defined(SIMPSON)
void pollSimpsonKeypad();
void handleSimpsonKeypadKey(uint8_t key);
#endif
void handleCardKBByte(uint8_t raw);
void setCardKBBackground(bool on); // no-op without CardKB / the poll task
void startCardKBCapture(); // end of begin(): start the poll task
@@ -306,6 +316,10 @@ public:
void gotoHomeScreen() { setCurrScreen(home); }
void gotoSettingsScreen();
void gotoMessagesScreen();
#if defined(SIMPSON)
void gotoBluetoothHomePage();
void gotoGpsHomePage();
#endif
#ifdef SIM_PLATFORM
void simCompose(const char* text); // sim tests: a channel post's keyboard holding `text`
void simMessage(const char* text); // sim tests: a channel message arrives