mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 15:16:40 +00:00
Add CardKB support for ProMicro via shared I2C bus
This commit is contained in:
@@ -61,7 +61,6 @@ class SettingsScreen : public UIScreen {
|
||||
CUSTOM_FREQ, CUSTOM_SF, CUSTOM_BW, CUSTOM_CR,
|
||||
POWER_SAVE,
|
||||
TX_APC,
|
||||
SCOPE_NAME,
|
||||
// System section
|
||||
SECTION_SYSTEM,
|
||||
DEVICE_NAME,
|
||||
@@ -77,7 +76,7 @@ class SettingsScreen : public UIScreen {
|
||||
KEYBOARD_TYPE,
|
||||
KEYBOARD_MAIN_ALPHABET,
|
||||
KEYBOARD_ALPHABET,
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
#if defined(CARDKB_I2C)
|
||||
KEYBOARD_CARDKB_COMPACT,
|
||||
#endif
|
||||
// Contacts section
|
||||
@@ -556,11 +555,6 @@ class SettingsScreen : public UIScreen {
|
||||
// Suppressed (and locked) while repeating — a repeater holds full TX power.
|
||||
if (p && p->client_repeat) display.print("--");
|
||||
else display.print((p && p->tx_apc) ? "ON" : "OFF");
|
||||
} else if (item == SCOPE_NAME) {
|
||||
display.print("Scope");
|
||||
int vx = valCol(display);
|
||||
display.drawTextEllipsized(vx, y, display.width() - vx - _reserve,
|
||||
(p && p->default_scope_name[0]) ? p->default_scope_name : "(none)");
|
||||
#if AUTO_OFF_MILLIS > 0
|
||||
} else if (item == AUTO_OFF) {
|
||||
display.print("AutoOff");
|
||||
@@ -611,7 +605,7 @@ class SettingsScreen : public UIScreen {
|
||||
display.print("Additional");
|
||||
display.setCursor(valCol(display), y);
|
||||
display.print(NodePrefs::keyboardAlphabetLabel(p ? p->keyboard_alt_alphabet : 0));
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
#if defined(CARDKB_I2C)
|
||||
} else if (item == KEYBOARD_CARDKB_COMPACT) {
|
||||
display.print("Ext. KB");
|
||||
display.setCursor(valCol(display), y);
|
||||
@@ -684,7 +678,6 @@ class SettingsScreen : public UIScreen {
|
||||
// Keyboard state for editing message slots
|
||||
int _edit_slot = -1; // -1 = not editing, 0..9 = slot being edited
|
||||
bool _edit_name = false; // editing DEVICE_NAME via the keyboard
|
||||
bool _edit_scope = false; // editing SCOPE_NAME via the keyboard
|
||||
KeyboardWidget* _kb;
|
||||
|
||||
// Radio preset picker — names are too long for the value column, so Enter on
|
||||
@@ -708,7 +701,6 @@ public:
|
||||
void onShow() override {
|
||||
_dirty = false;
|
||||
_edit_name = false;
|
||||
_edit_scope = false;
|
||||
resetList();
|
||||
_editor.freq.active = false;
|
||||
}
|
||||
@@ -716,7 +708,7 @@ public:
|
||||
int render(DisplayDriver& display) override {
|
||||
display.setTextSize(1);
|
||||
|
||||
if (_edit_slot >= 0 || _edit_name || _edit_scope || _picker.saving) {
|
||||
if (_edit_slot >= 0 || _edit_name || _picker.saving) {
|
||||
return _kb->render(display);
|
||||
}
|
||||
|
||||
@@ -779,19 +771,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keyboard editing mode for the scope name
|
||||
if (_edit_scope) {
|
||||
auto res = _kb->handleInput(c);
|
||||
if (res == KeyboardWidget::DONE) {
|
||||
the_mesh.setPrimaryScope(_kb->buf);
|
||||
_dirty = true;
|
||||
_edit_scope = false;
|
||||
} else if (res == KeyboardWidget::CANCELLED) {
|
||||
_edit_scope = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Digit-by-digit Freq editor
|
||||
if (_editor.active()) {
|
||||
if (_editor.handleFreqInput(c) && p) { _task->applyRadioParams(); _dirty = true; }
|
||||
@@ -987,12 +966,6 @@ public:
|
||||
_kb->clearPlaceholders(); // a device name is literal, not a message
|
||||
return true;
|
||||
}
|
||||
if (_selected == SCOPE_NAME && p && enter) {
|
||||
_edit_scope = true;
|
||||
_kb->begin(p->default_scope_name, (int)sizeof(p->default_scope_name) - 1);
|
||||
_kb->clearPlaceholders(); // a scope name is literal, not a message
|
||||
return true;
|
||||
}
|
||||
if (_selected == REBOOT && enter) {
|
||||
_task->savePrefsIfDirty(_dirty); // don't lose pending edits across the restart
|
||||
_task->showAlert("Rebooting...", 800);
|
||||
@@ -1020,7 +993,7 @@ public:
|
||||
_dirty = true;
|
||||
return true;
|
||||
}
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
#if defined(CARDKB_I2C)
|
||||
if (_selected == KEYBOARD_CARDKB_COMPACT && p && (left || right || enter)) {
|
||||
p->keyboard_cardkb_compact ^= 1;
|
||||
_dirty = true;
|
||||
|
||||
@@ -1397,11 +1397,14 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
uint32_t aoff = autoOffMillis();
|
||||
_auto_off = millis() + (aoff > 0 ? aoff : AUTO_OFF_MILLIS);
|
||||
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
// Wire1 is already brought up by sensors.begin() (EnvironmentSensorManager),
|
||||
// which runs before this -- just probe for a CardKB sitting on it.
|
||||
Wire1.beginTransmission(0x5F);
|
||||
_has_cardkb = (Wire1.endTransmission() == 0);
|
||||
#if defined(CARDKB_I2C)
|
||||
// On the ENV_PIN_SDA/SCL path, Wire1 is already brought up by
|
||||
// sensors.begin() (EnvironmentSensorManager), which runs before this. On
|
||||
// the CARDKB_USE_PRIMARY_WIRE path, Wire is already brought up by the
|
||||
// board's own begin() (display/RTC), also before this -- either way, just
|
||||
// probe for a CardKB sitting on the bus.
|
||||
CARDKB_I2C.beginTransmission(0x5F);
|
||||
_has_cardkb = (CARDKB_I2C.endTransmission() == 0);
|
||||
#endif
|
||||
|
||||
#if defined(PIN_USER_BTN)
|
||||
@@ -2104,7 +2107,7 @@ bool UITask::dequeueKey(char& c) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
#if defined(CARDKB_I2C)
|
||||
// CardKB's "fn" column (key_map in M5Stack's unit_CardKB.cpp): Fn+<physical
|
||||
// key> sends 0x80 + that key's row index, entirely disjoint from every other
|
||||
// code this UI recognises. Indexed by (raw - 0x80); non-letter slots (digits,
|
||||
@@ -2145,7 +2148,7 @@ static const char CARDKB_FN_BASE[48] = {
|
||||
// once), so _cardkb_last_raw debounces it into one press per physical
|
||||
// keypress, same as a MomentaryButton's CLICK event.
|
||||
void UITask::pollCardKB() {
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
#if defined(CARDKB_I2C)
|
||||
if (!_has_cardkb) return;
|
||||
// No artificial throttle: unlike a MomentaryButton (BUTTON_USE_INTERRUPTS
|
||||
// latches every edge in an ISR ring buffer, so it survives a blocking e-ink
|
||||
@@ -2156,9 +2159,9 @@ void UITask::pollCardKB() {
|
||||
// Polling every loop() iteration (same as a digital button's check(), which
|
||||
// has no throttle either) just shrinks that miss window down to exactly the
|
||||
// render() duration instead of render()+30ms.
|
||||
Wire1.requestFrom(0x5F, 1);
|
||||
if (!Wire1.available()) return;
|
||||
uint8_t raw = Wire1.read();
|
||||
CARDKB_I2C.requestFrom(0x5F, 1);
|
||||
if (!CARDKB_I2C.available()) return;
|
||||
uint8_t raw = CARDKB_I2C.read();
|
||||
if (raw == _cardkb_last_raw) return; // still held (or still released) -- no new edge
|
||||
_cardkb_last_raw = raw;
|
||||
if (raw == 0) return; // key just released, nothing to enqueue
|
||||
@@ -2480,7 +2483,7 @@ void UITask::loop() {
|
||||
}
|
||||
// Hint popup at bottom (like alert style)
|
||||
_display->setTextSize(1);
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
#if defined(CARDKB_I2C)
|
||||
const char* hint = _lock_seq_count == 0 ? (_has_cardkb ? "Back+3xEnter/Fn+Esc" : "Hold Back + 3xEnter") :
|
||||
_lock_seq_count == 1 ? "Enter x2 more..." : "Enter x1 more...";
|
||||
#else
|
||||
|
||||
@@ -193,12 +193,21 @@ class UITask : public AbstractUITask {
|
||||
void enqueueKey(char c);
|
||||
bool dequeueKey(char& c);
|
||||
|
||||
// Optional M5Stack CardKB (I2C keyboard, addr 0x5F) on the Grove/Wire1 bus
|
||||
// -- reuses ENV_PIN_SDA/ENV_PIN_SCL (already brought up for
|
||||
// EnvironmentSensorManager) as the "this board has a second I2C bus" gate,
|
||||
// rather than a new board-specific pin define. No-op entirely on boards
|
||||
// without that bus, or when nothing ACKs 0x5F at boot.
|
||||
// Optional M5Stack CardKB (I2C keyboard, addr 0x5F). Two ways to reach it:
|
||||
// - ENV_PIN_SDA/ENV_PIN_SCL defined -> CardKB rides the dedicated second
|
||||
// I2C bus (Wire1) that EnvironmentSensorManager already brings up.
|
||||
// - CARDKB_USE_PRIMARY_WIRE defined instead -> CardKB shares the board's
|
||||
// main Wire bus (whatever the display/RTC already use), for boards
|
||||
// where no second bus is free. Mutually exclusive with the above.
|
||||
// Either way it's a no-op on boards with neither define, or when nothing
|
||||
// ACKs 0x5F at boot.
|
||||
#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL)
|
||||
#define CARDKB_I2C Wire1
|
||||
#elif defined(CARDKB_USE_PRIMARY_WIRE)
|
||||
#define CARDKB_I2C Wire
|
||||
#endif
|
||||
|
||||
#if defined(CARDKB_I2C)
|
||||
bool _has_cardkb = false;
|
||||
// CardKB is level-triggered, not edge-triggered -- it keeps returning the
|
||||
// same byte for as long as the physical key is held, not just once. Track
|
||||
|
||||
@@ -176,4 +176,43 @@ lib_deps =
|
||||
[env:ProMicro_kiss_modem]
|
||||
extends = Promicro
|
||||
build_src_filter = ${Promicro.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
+<../examples/kiss_modem/>
|
||||
; ============================================================
|
||||
; Solo build (UI completa on-device) com CardKB no barramento
|
||||
; I2C ja usado pelo OLED (SDA=D8, SCL=D7 -- confirmado contra o
|
||||
; esquematico oficial da fakeTec V5 rev.B). Requer o patch
|
||||
; CARDKB_USE_PRIMARY_WIRE em UITask.h/.cpp e SettingsScreen.h.
|
||||
; ============================================================
|
||||
[env:ProMicro_companion_solo_dual]
|
||||
extends = Promicro
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
board_upload.maximum_size = 712704
|
||||
; -Ofast (default do core) infla o flash num build solo feature-completo;
|
||||
; -Os cabe com folga.
|
||||
build_unflags = -Ofast
|
||||
build_flags = ${Promicro.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D DUAL_SERIAL=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D FIRMWARE_SOLO_BUILD=1
|
||||
-D UI_SENSORS_PAGE=1
|
||||
-D ENABLE_SCREENSHOT
|
||||
-Os
|
||||
; CardKB compartilhando o barramento do OLED/RTC (D8/D7) --
|
||||
; NAO definir ENV_PIN_SDA/SCL aqui, colidiria com o LoRa (D13/D14).
|
||||
-D CARDKB_USE_PRIMARY_WIRE=1
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Promicro.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps = ${Promicro.lib_deps}
|
||||
adafruit/RTClib @ ^2.1.3
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
Reference in New Issue
Block a user