Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
| weitere_hardware:nesso_n1 [18.04.2026 19:22] – mellinux | weitere_hardware:nesso_n1 [21.04.2026 14:55] (aktuell) – mellinux | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| **HINWEIS: | **HINWEIS: | ||
| + | |||
| + | **HINWEIS: | ||
| ====== MeshCore — Arduino Nesso N1 (ESP32-C6) ====== | ====== MeshCore — Arduino Nesso N1 (ESP32-C6) ====== | ||
| - | **Author:** team-nessoN1-meshcore — team-nessoN1-meshcore@posteo.de | + | **Autor:** team-nessoN1-meshcore — team-nessoN1-meshcore@posteo.de\\ |
| **Stand:** April 2026 | **Stand:** April 2026 | ||
| - | Dieses Verzeichnis enthält | + | Diese Seite beschreibt |
| - | **Arduino Nesso N1** als MeshCore-Zielplattform. Das Board wurde gemeinsam | + | |
| - | von Arduino und M5Stack entwickelt und kombiniert einen ESP32-C6 Mikrocontroller | + | |
| - | mit einem SX1262 LoRa-Transceiver, | + | |
| - | einem 1, | + | |
| - | + | ||
| - | ===== Aktueller Status April 2026: ===== | + | |
| - | + | ||
| - | Die Migration läuft (Repeater, Companion). Umfangreiche Tests stehen an. Das | + | |
| - | eigentliche Produkt Nesso N1 ist jung und hat einige Fehler in der Doku. | + | |
| - | Der Source wurden noch nicht veröffentlicht, | + | |
| - | + | ||
| - | ---- | + | |
| ===== Hardware-Überblick ===== | ===== Hardware-Überblick ===== | ||
| - | | Komponente | + | ^ Komponente |
| - | |---|---| | + | |
| | MCU | Espressif ESP32-C6 (RISC-V, 160 MHz) | | | MCU | Espressif ESP32-C6 (RISC-V, 160 MHz) | | ||
| | Flash | 16 MB | | | Flash | 16 MB | | ||
| Zeile 34: | Zeile 23: | ||
| | Akku | 250 mAh LiPo, USB-C Laden | | | Akku | 250 mAh LiPo, USB-C Laden | | ||
| | Konnektivität | Wi-Fi 6, BT 5.3, Thread/ | | Konnektivität | Wi-Fi 6, BT 5.3, Thread/ | ||
| - | |||
| - | ---- | ||
| ===== Besonderheiten dieser Portierung ===== | ===== Besonderheiten dieser Portierung ===== | ||
| Zeile 41: | Zeile 28: | ||
| ==== SX1262 Reset und RF-Switch über I²C-Expander ==== | ==== SX1262 Reset und RF-Switch über I²C-Expander ==== | ||
| - | Anders als bei den meisten anderen MeshCore-Zielplattformen sind beim Nesso N1 | + | Anders als bei den meisten anderen MeshCore-Zielplattformen sind beim Nesso N1 die LoRa-Steuerleitungen **nicht direkt** mit GPIO-Pins des ESP32-C6 verbunden, sondern laufen über einen **PI4IOE5V6408 I²C-GPIO-Expander** (Adresse 0x43): |
| - | die LoRa-Steuerleitungen **nicht direkt** mit GPIO-Pins des ESP32-C6 verbunden, | + | |
| - | sondern laufen über einen **PI4IOE5V6408 I²C-GPIO-Expander** (Adresse 0x43): | + | |
| - | | Signal | + | ^ Signal |
| - | |---|---|---| | + | |
| | SX_NRST | P7 | Hardware-Reset des SX1262 | | | SX_NRST | P7 | Hardware-Reset des SX1262 | | ||
| | SX_ANT_SW | P6 | Antennen-RF-Schalter | | | SX_ANT_SW | P6 | Antennen-RF-Schalter | | ||
| Zeile 53: | Zeile 37: | ||
| | KEY2 | P1 | Taste B (Input) | | | KEY2 | P1 | Taste B (Input) | | ||
| - | Konsequenz: RadioLib' | + | Konsequenz: RadioLib' |
| - | '' | + | |
| - | das '' | + | |
| ==== Geteilter SPI-Bus (SX1262 + ST7789) ==== | ==== Geteilter SPI-Bus (SX1262 + ST7789) ==== | ||
| - | | Signal | + | ^ Signal |
| - | |---|---| | + | |
| | MOSI | G21 | | | MOSI | G21 | | ||
| | MISO | G22 | | | MISO | G22 | | ||
| Zeile 68: | Zeile 49: | ||
| | LCD DC | G16 | | | LCD DC | G16 | | ||
| - | '' | + | '' |
| - | Arduino einen APB-Hardware-CS-Callback für GPIO23 registrieren; | + | |
| - | dasselbe für GPIO17 — zwei Callbacks auf demselben Bus führen zu | + | |
| - | '' | + | |
| ==== FSPI statt Default-SPI ==== | ==== FSPI statt Default-SPI ==== | ||
| Zeile 79: | Zeile 57: | ||
| ==== USB-CDC und BLE schließen sich aus ==== | ==== USB-CDC und BLE schließen sich aus ==== | ||
| - | | Firmware | + | ^ Firmware |
| - | |---|---|---| | + | |
| | Repeater | '' | | Repeater | '' | ||
| | Companion BLE | '' | | Companion BLE | '' | ||
| | Companion WiFi | '' | | Companion WiFi | '' | ||
| - | |||
| - | ---- | ||
| ===== Display-Architektur ===== | ===== Display-Architektur ===== | ||
| Zeile 91: | Zeile 66: | ||
| ==== Auflösung und UITask-Layoutprüfung ==== | ==== Auflösung und UITask-Layoutprüfung ==== | ||
| - | Das ST7789P3 hat **240×135 Pixel** (Landscape, '' | + | Das ST7789P3 hat **240×135 Pixel** (Landscape, '' |
| - | Alle UITask-Standardlayouts passen ohne Überlauf: | + | |
| - | | Element | + | ^ Element |
| - | |---|---|---|---|---| | + | |
| | MeshCore-Logo (64×36 px XBM) | 64 px | 88 | 152 | ✅ | | | MeshCore-Logo (64×36 px XBM) | 64 px | 88 | 152 | ✅ | | ||
| | Versionszeile (setTextSize 2) | 156 px | 42 | 198 | ✅ | | | Versionszeile (setTextSize 2) | 156 px | 42 | 198 | ✅ | | ||
| Zeile 112: | Zeile 85: | ||
| 4. display.begin() | 4. display.begin() | ||
| </ | </ | ||
| - | |||
| - | ---- | ||
| ===== Bekannte Probleme und ihre Fixes ===== | ===== Bekannte Probleme und ihre Fixes ===== | ||
| Zeile 119: | Zeile 90: | ||
| ==== Fix 1 — Display schwarz nach wenigen Sekunden ==== | ==== Fix 1 — Display schwarz nach wenigen Sekunden ==== | ||
| - | **Datum:** April 2026 | + | **Datum:** April 2026\\ |
| **Betroffene Datei:** '' | **Betroffene Datei:** '' | ||
| - | **Symptom: | + | **Symptom: |
| - | dann dauerhaft schwarz. Kein Wakeup durch Tastendruck. | + | |
| - | **Ursache | + | **Ursache: |
| - | '' | + | |
| - | Makro '' | + | |
| - | '' | + | |
| - | — einen direkten GPIO-Pin | + | |
| - | hängen KEY1 und KEY2 **über I²C-Expander 0** (Adresse 0x43, Pins P0/P1). | + | |
| - | Ein direkter GPIO als '' | + | |
| - | damit **keinen Wakeup-Mechanismus** — das Display bleibt nach Timeout-Ablauf | + | |
| - | permanent schwarz. | + | |
| **Fix:** | **Fix:** | ||
| - | <code>ini | + | <code ini> |
| ; platformio.ini — [env: | ; platformio.ini — [env: | ||
| -D SCREEN_TIMEOUT=0 | -D SCREEN_TIMEOUT=0 | ||
| -D DISPLAY_TIMEOUT=0 | -D DISPLAY_TIMEOUT=0 | ||
| </ | </ | ||
| - | Beide Flags auf 0 deaktivieren den Timeout vollständig. '' | ||
| - | wird zusätzlich gesetzt weil verschiedene UITask-Versionen im MeshCore-Repository | ||
| - | unterschiedliche Makronamen verwenden. | ||
| - | **Hinweis Akku-Betrieb: | + | Beide Flags auf 0 deaktivieren den Timeout vollständig. '' |
| - | Der Button-Wakeup ist über '' | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 7 — Tasten ohne Funktion im Repeater-Display-Modus ==== | + | |
| - | + | ||
| - | **Datum:** April 2026 | + | |
| - | **Betroffene Dateien:** '' | + | |
| - | '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | '' | + | |
| - | + | ||
| - | **Ursache — präzise: | + | |
| - | '' | + | |
| - | wenn das Display bereits an ist. In '' | + | |
| - | vollständig **auskommentiert**. Zusätzlich hatte '' | + | |
| - | Screens und keine Verbindung zu '' | + | |
| - | Discover-Request abrufen zu können. | + | |
| - | + | ||
| - | **Fix — drei Ebenen:** | + | |
| - | + | ||
| - | 1. **Neues Interface '' | + | |
| - | < | + | |
| - | class UIActions { | + | |
| - | public: | + | |
| - | virtual void uiGetNeighborList(char* buf, int bufSize) = 0; | + | |
| - | virtual void uiSendDiscover() = 0; | + | |
| - | }; | + | |
| - | </ | + | |
| - | + | ||
| - | 2. **'' | + | |
| - | < | + | |
| - | class MyMesh : public mesh::Mesh, public CommonCLICallbacks, | + | |
| - | ... | + | |
| - | void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf); | + | |
| - | void uiSendDiscover() override { sendNodeDiscoverReq(); | + | |
| - | }; | + | |
| - | </ | + | |
| - | + | ||
| - | 3. **'' | + | |
| - | < | + | |
| - | SCREEN_HOME | + | |
| - | SCREEN_NEIGHBOURS — Nachbarliste (HEX-ID, Alter, SNR) | + | |
| - | SCREEN_POLL_SENT | + | |
| - | </ | + | |
| - | + | ||
| - | 4. **'' | + | |
| - | < | + | |
| - | if (btn == -1) ui_task.nextScreen(); | + | |
| - | if (btn == -2) ui_task.prevScreen(); | + | |
| - | </ | + | |
| - | + | ||
| - | **Bedienung nach dem Patch:** | + | |
| - | + | ||
| - | | Taste | Display AUS | Display AN (Home) | Display AN (Nachbarn) | | + | |
| - | |-------|-------------|-------------------|-----------------------| | + | |
| - | | KEY1 | Wakeup | → Nachbarn-Screen | → Poll senden | | + | |
| - | | KEY2 | Wakeup | bleibt Home | → Home-Screen | | + | |
| - | + | ||
| - | Nach dem Poll zeigt der Screen 2 Sekunden "Poll gesendet", | + | |
| - | dann wechselt er automatisch zurück zur (aktualisierten) Nachbarliste. | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 8 — Tasten nicht erkannt: Wire-Konflikt zwischen M5GFX und Expander-Treiber ==== | + | |
| - | + | ||
| - | **Datum:** April 2026 | + | |
| - | **Betroffene Dateien:** '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | nicht oder unzuverlässig registriert. Im Serial-Log erscheinen: | + | |
| - | < | + | |
| - | [E][Wire.cpp: | + | |
| - | [E][esp32-hal-cpu.c: | + | |
| - | </ | + | |
| - | + | ||
| - | **Ursache — präzise: | + | |
| - | M5GFX initialisiert den I²C-Bus intern über '' | + | |
| - | Der eigene '' | + | |
| - | direkt. Beide greifen auf **denselben Bus** (SDA=GPIO10, | + | |
| - | mit unterschiedlichen '' | + | |
| - | instabilen Expander-Reads. Dokumentiert im Arduino-Forum (November 2025): | + | |
| - | https:// | + | |
| - | + | ||
| - | **Fix: | + | |
| - | '' | + | |
| - | '' | + | |
| - | Reihenfolge — kein Wire-Konflikt mehr. | + | |
| - | + | ||
| - | < | + | |
| - | // NessoDisplayDriver.cpp — begin(): | + | |
| - | M5.begin(cfg); | + | |
| - | _gfx = & | + | |
| - | + | ||
| - | // checkButtons(): | + | |
| - | M5.update(); | + | |
| - | if (M5.BtnA.wasPressed()) return 1; // KEY1 | + | |
| - | if (M5.BtnB.wasPressed()) return 2; // KEY2 | + | |
| - | </ | + | |
| - | + | ||
| - | < | + | |
| - | ; platformio.ini — nesso_n1_base: | + | |
| - | lib_deps = | + | |
| - | m5stack/ | + | |
| - | m5stack/ | + | |
| - | </ | + | |
| - | Entfernt: '' | + | > **Hinweis Akku-Betrieb:** Sinnvoller Wert z.B. '' |
| - | Debounce entfällt — M5Unified macht Flanken-Erkennung intern). | + | |
| ==== Fix 2 — Display im Portrait-Modus (135×240 statt 240×135) ==== | ==== Fix 2 — Display im Portrait-Modus (135×240 statt 240×135) ==== | ||
| - | **Datum:** April 2026 | + | **Datum:** April 2026\\ |
| **Betroffene Datei:** '' | **Betroffene Datei:** '' | ||
| - | **Symptom: | + | **Symptom: |
| - | UITask-Texte werden mit negativem x-Offset abgeschnitten und sind nicht | + | |
| - | lesbar | + | |
| - | **Ursache | + | **Ursache: |
| - | '' | + | |
| - | aufgerufen. M5GFX tauscht die Dimensionen erst nach '' | + | |
| - | Die Abfrage vor '' | + | |
| - | Zusätzlich trat das Problem auf wenn '' | + | |
| - | '' | + | |
| - | aus '' | + | |
| - | Controller lief weiter im Portrait-Modus obwohl '' | + | |
| - | im Konstruktor richtig gesetzt war. | + | |
| **Fix in '' | **Fix in '' | ||
| - | <code>cpp | + | <code cpp> |
| // setRotation ZUERST — M5GFX tauscht Dimensionen erst danach: | // setRotation ZUERST — M5GFX tauscht Dimensionen erst danach: | ||
| _gfx-> | _gfx-> | ||
| Zeile 284: | Zeile 125: | ||
| // Ausgabe: 240 x 135 px ✓ | // Ausgabe: 240 x 135 px ✓ | ||
| </ | </ | ||
| - | |||
| - | ---- | ||
| ==== Fix 3 — '' | ==== Fix 3 — '' | ||
| - | **Datum:** April 2026 | + | **Datum:** April 2026\\ |
| **Betroffene Datei:** '' | **Betroffene Datei:** '' | ||
| **Symptom: | **Symptom: | ||
| - | **Ursache | + | **Ursache: |
| - | '' | + | |
| - | ''# | + | |
| - | '' | + | |
| - | problemlos, weil dort Board-Init und Radio-Init unabhängig sind. Auf dem Nesso N1 | + | |
| - | führt '' | + | |
| - | Anschließend ruft '' | + | |
| - | SX1262 bootet erneut, BUSY wieder HIGH → '' | + | |
| - | Bootvorgang → '' | + | |
| **Fix in '' | **Fix in '' | ||
| - | <code>cpp | + | <code cpp> |
| static bool s_boardBeginCalled = false; | static bool s_boardBeginCalled = false; | ||
| if (s_boardBeginCalled) { | if (s_boardBeginCalled) { | ||
| Zeile 313: | Zeile 144: | ||
| s_boardBeginCalled = true; | s_boardBeginCalled = true; | ||
| </ | </ | ||
| - | Der Guard stellt sicher dass '' | ||
| - | laufen, unabhängig von der Aufruf-Reihenfolge in '' | ||
| - | **Vollständige Lösung:** In '' | + | **Vollständige Lösung:** In '' |
| - | aus '' | + | |
| - | Da '' | + | |
| - | ohne Repository-Fork. Hinweis: | + | |
| - | + | ||
| - | ---- | + | |
| ==== Fix 4 — APB-Callback-Duplikat und Wire.setPins-Warnung ==== | ==== Fix 4 — APB-Callback-Duplikat und Wire.setPins-Warnung ==== | ||
| - | **Datum:** April 2026 | + | **Datum:** April 2026\\ |
| **Betroffene Dateien:** '' | **Betroffene Dateien:** '' | ||
| Zeile 335: | Zeile 159: | ||
| </ | </ | ||
| - | **Ursache | + | **Ursache: |
| - | '' | + | |
| - | '' | + | |
| - | static-local. M5GFX ruft intern '' | + | |
| - | '' | + | |
| - | '' | + | |
| - | APB-Callback erneut zu registrieren → '' | + | |
| - | bereits initialisierten Bus → '' | + | |
| - | + | ||
| - | **Fix:** | + | |
| - | Der Board-Guard (Fix 3) verhindert den zweiten '' | + | |
| - | '' | + | |
| - | trotzdem solange '' | + | |
| - | **funktional harmlos** (System funktioniert korrekt), aber ein sichtbarer | + | |
| - | Indikator für die unvollständig behobene Aufruf-Reihenfolge. | + | |
| - | ---- | + | **Fix:** Der Board-Guard (Fix 3) verhindert den zweiten '' |
| ==== Fix 5 — BUSY-Puls-Timing nach loraReset() ==== | ==== Fix 5 — BUSY-Puls-Timing nach loraReset() ==== | ||
| - | **Datum:** April 2026 | + | **Datum:** April 2026\\ |
| **Betroffene Datei:** '' | **Betroffene Datei:** '' | ||
| - | **Symptom: | + | **Symptom: |
| - | **Ursache | + | **Ursache: |
| - | Der '' | + | |
| - | einen BUSY-HIGH-Puls von unter 1 ms nach dem Reset — der Chip hatte BUSY längst | + | |
| - | wieder LOW gezogen wenn '' | + | |
| - | keinen echten Fehler, sondern um ein zu konservatives Timing kombiniert mit einer | + | |
| - | zu alarmistischen Logmeldung. | + | |
| **Fix:** | **Fix:** | ||
| - | <code>cpp | + | <code cpp> |
| _exp0.digitalWrite(NESSO_EXP0_SX_NRST, | _exp0.digitalWrite(NESSO_EXP0_SX_NRST, | ||
| delay(2); | delay(2); | ||
| </ | </ | ||
| + | |||
| Logmeldung geändert zu: | Logmeldung geändert zu: | ||
| < | < | ||
| Zeile 377: | Zeile 183: | ||
| </ | </ | ||
| - | ---- | + | ==== Fix 7 — Tasten ohne Funktion im Repeater-Display-Modus ==== |
| - | ===== Verzeichnisstruktur ===== | + | **Datum:** April 2026\\ |
| + | **Betroffene Dateien:** '' | ||
| + | |||
| + | **Symptom: | ||
| + | |||
| + | **Ursache: | ||
| + | |||
| + | **Fix — drei Ebenen:** | ||
| + | |||
| + | **1. Neues Interface '' | ||
| + | <code cpp> | ||
| + | class UIActions { | ||
| + | public: | ||
| + | virtual void uiGetNeighborList(char* buf, int bufSize) | ||
| + | virtual void uiSendDiscover() | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | **2. '' | ||
| + | <code cpp> | ||
| + | class MyMesh : public mesh::Mesh, public CommonCLICallbacks, | ||
| + | void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf); | ||
| + | void uiSendDiscover() override { sendNodeDiscoverReq(); | ||
| + | }; | ||
| + | </ | ||
| + | **3. '' | ||
| < | < | ||
| - | variants/ | + | SCREEN_HOME |
| - | ├── NessoN1Board.h | + | SCREEN_NEIGHBOURS |
| - | ├── NessoN1Board.cpp | + | SCREEN_POLL_SENT |
| - | ├── NessoDisplayDriver.h | + | </code> |
| - | ├── NessoDisplayDriver.cpp | + | |
| - | ├── target.h | + | |
| - | ├── target.cpp | + | |
| - | ├── platformio.ini — incl. SCREEN_TIMEOUT=0 (Fix 1) | + | |
| - | ├── credentials.ini.example | + | |
| - | └── main_cpp_setup_hinweis.txt — Hinweis zur korrekten main.cpp-Struktur (Fix 3/4) | + | |
| - | src/helpers/ui/ | + | **4. '' |
| - | └── DisplayDriver.h | + | <code cpp> |
| + | if (btn == -1) ui_task.nextScreen(); | ||
| + | if (btn == -2) ui_task.prevScreen(); | ||
| + | </ | ||
| - | src/ | + | **Bedienung nach dem Patch:** |
| - | ├── RfSwitchCallback.h — Abstraktes Interface für externen RF-Switch | + | |
| - | └── CustomSX1262Wrapper.h — SX1262-Wrapper | + | ^ Taste ^ Display AUS ^ Display AN (Home) ^ Display AN (Nachbarn) ^ |
| + | | KEY1 | Wakeup | → Nachbarn-Screen | → Poll senden | | ||
| + | | KEY2 | Wakeup | bleibt Home | → Home-Screen | | ||
| + | |||
| + | Nach dem Poll zeigt der Screen 2 Sekunden "Poll gesendet", | ||
| + | |||
| + | ==== Fix 8 — Tasten nicht erkannt: Wire-Konflikt zwischen M5GFX und Expander-Treiber ==== | ||
| + | |||
| + | **Datum:** April 2026\\ | ||
| + | **Betroffene Dateien:** '' | ||
| + | |||
| + | **Symptom: | ||
| + | < | ||
| + | [E][Wire.cpp: | ||
| + | [E][esp32-hal-cpu.c: | ||
| + | </ | ||
| + | |||
| + | **Ursache: | ||
| + | |||
| + | **Fix:** '' | ||
| + | |||
| + | <code cpp> | ||
| + | // NessoDisplayDriver.cpp — begin(): | ||
| + | M5.begin(cfg); | ||
| + | _gfx = & | ||
| + | |||
| + | // checkButtons(): | ||
| + | M5.update(); | ||
| + | if (M5.BtnA.wasPressed()) return 1; // KEY1 | ||
| + | if (M5.BtnB.wasPressed()) return 2; // KEY2 | ||
| + | </ | ||
| + | |||
| + | <code ini> | ||
| + | ; platformio.ini — nesso_n1_base: | ||
| + | lib_deps = | ||
| + | m5stack/ | ||
| + | m5stack/ | ||
| </ | </ | ||
| - | ---- | + | Entfernt: '' |
| ===== Schnellstart ===== | ===== Schnellstart ===== | ||
| Zeile 407: | Zeile 271: | ||
| ==== 1. Voraussetzungen ==== | ==== 1. Voraussetzungen ==== | ||
| - | * [PlatformIO](https:// | + | * [[https:// |
| * Arduino Nesso N1 per USB-C verbunden | * Arduino Nesso N1 per USB-C verbunden | ||
| ==== 2. Zugangsdaten einrichten ==== | ==== 2. Zugangsdaten einrichten ==== | ||
| - | <code>bash | + | <code bash> |
| cd < | cd < | ||
| cp variants/ | cp variants/ | ||
| Zeile 419: | Zeile 283: | ||
| '' | '' | ||
| - | <code>ini | + | <code ini> |
| [credentials] | [credentials] | ||
| build_flags_repeater = | build_flags_repeater = | ||
| Zeile 434: | Zeile 298: | ||
| **Repeater mit Display** (empfohlen als Einstieg): | **Repeater mit Display** (empfohlen als Einstieg): | ||
| - | <code>bash | + | <code bash> |
| pio run -e NessoN1_repeater_display --target upload | pio run -e NessoN1_repeater_display --target upload | ||
| pio device monitor -b 115200 | pio device monitor -b 115200 | ||
| Zeile 440: | Zeile 304: | ||
| **Repeater ohne Display:** | **Repeater ohne Display:** | ||
| - | <code>bash | + | <code bash> |
| pio run -e NessoN1_repeater --target upload | pio run -e NessoN1_repeater --target upload | ||
| </ | </ | ||
| **Companion Radio — BLE:** | **Companion Radio — BLE:** | ||
| - | <code>bash | + | <code bash> |
| pio run -e NessoN1_companion_ble --target upload | pio run -e NessoN1_companion_ble --target upload | ||
| </ | </ | ||
| **Companion Radio — WiFi/TCP:** | **Companion Radio — WiFi/TCP:** | ||
| - | <code>bash | + | <code bash> |
| pio run -e NessoN1_companion_wifi --target upload | pio run -e NessoN1_companion_wifi --target upload | ||
| </ | </ | ||
| Zeile 474: | Zeile 338: | ||
| </ | </ | ||
| - | Drei '' | + | Drei '' |
| - | '' | + | |
| - | + | ||
| - | ---- | + | |
| ===== Pin-Referenz ===== | ===== Pin-Referenz ===== | ||
| Zeile 483: | Zeile 344: | ||
| ==== Direkte ESP32-C6 GPIOs ==== | ==== Direkte ESP32-C6 GPIOs ==== | ||
| - | | Funktion | + | ^ Funktion |
| - | |---|---| | + | |
| | LoRa MOSI | G21 | | | LoRa MOSI | G21 | | ||
| | LoRa MISO | G22 | | | LoRa MISO | G22 | | ||
| Zeile 499: | Zeile 359: | ||
| ==== Über I²C-Expander 0 (0x43) ==== | ==== Über I²C-Expander 0 (0x43) ==== | ||
| - | | Funktion | + | ^ Funktion |
| - | |---|---| | + | |
| | Taste A (KEY1) | P0 | | | Taste A (KEY1) | P0 | | ||
| | Taste B (KEY2) | P1 | | | Taste B (KEY2) | P1 | | ||
| Zeile 509: | Zeile 368: | ||
| ==== Über I²C-Expander 1 (0x44) ==== | ==== Über I²C-Expander 1 (0x44) ==== | ||
| - | | Funktion | + | ^ Funktion |
| - | |---|---| | + | |
| | LCD Reset | P1 | | | LCD Reset | P1 | | ||
| | LCD Backlight | P6 | | | LCD Backlight | P6 | | ||
| - | |||
| - | ---- | ||
| ===== RF-Switch-Schaltlogik ===== | ===== RF-Switch-Schaltlogik ===== | ||
| - | | Modus | ANT_SW | + | ^ Modus ^ ANT_SW |
| - | |---|---|---|---| | + | |
| | TX | HIGH | LOW | Sende-Pfad aktiv, LNA geschützt | | | TX | HIGH | LOW | Sende-Pfad aktiv, LNA geschützt | | ||
| | RX | HIGH | HIGH | Empfangs-Pfad mit LNA aktiv | | | RX | HIGH | HIGH | Empfangs-Pfad mit LNA aktiv | | ||
| | IDLE | LOW | LOW | HF-Pfad getrennt, Stromsparmodus | | | IDLE | LOW | LOW | HF-Pfad getrennt, Stromsparmodus | | ||
| - | |||
| - | ---- | ||
| ===== Architektur ===== | ===== Architektur ===== | ||
| Zeile 549: | Zeile 402: | ||
| </ | </ | ||
| - | ---- | + | ===== Verzeichnisstruktur ===== |
| + | |||
| + | < | ||
| + | variants/ | ||
| + | ├── NessoN1Board.h | ||
| + | ├── NessoN1Board.cpp | ||
| + | ├── NessoDisplayDriver.h | ||
| + | ├── NessoDisplayDriver.cpp | ||
| + | ├── target.h | ||
| + | ├── target.cpp | ||
| + | ├── platformio.ini | ||
| + | ├── credentials.ini.example | ||
| + | └── main_cpp_setup_hinweis.txt — Hinweis zur korrekten main.cpp-Struktur (Fix 3/4) | ||
| + | |||
| + | src/ | ||
| + | └── DisplayDriver.h | ||
| + | |||
| + | src/ | ||
| + | ├── RfSwitchCallback.h | ||
| + | └── CustomSX1262Wrapper.h | ||
| + | </ | ||
| ===== Offene Punkte ===== | ===== Offene Punkte ===== | ||
| - | | # | Thema | Status | + | ^ # ^ Thema ^ Status |
| - | |---|---|---| | + | | 1 | '' |
| - | | 1 | '' | + | |
| | 2 | KEY1/KEY2 als UITask Screen-Wakeup einhängen | offen | | | 2 | KEY1/KEY2 als UITask Screen-Wakeup einhängen | offen | | ||
| | 3 | Touch FT6336U (I²C 0x38) integrieren | offen | | | 3 | Touch FT6336U (I²C 0x38) integrieren | offen | | ||
| | 4 | IMU BMI270 (I²C 0x68) für Auto-Rotation | offen | | | 4 | IMU BMI270 (I²C 0x68) für Auto-Rotation | offen | | ||
| | 5 | Akku-Monitor (I²C 0x49) für Display-Anzeige | offen | | | 5 | Akku-Monitor (I²C 0x49) für Display-Anzeige | offen | | ||
| - | |||
| - | ---- | ||
| ===== Lizenz ===== | ===== Lizenz ===== | ||
| - | MIT-Lizenz, identisch mit MeshCore. Vollständiger Text im Wurzelverzeichnis. | + | MIT-Lizenz, identisch mit MeshCore. Vollständiger Text im Wurzelverzeichnis |
| - | + | ||
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ====== English version ====== | + | |
| - | + | ||
| - | **NOTE:** Currently in the testing phase! | + | |
| - | + | ||
| - | ====== MeshCore — Arduino Nesso N1 (ESP32-C6) ====== | + | |
| - | + | ||
| - | **Author:** team-nessoN1-meshcore — team-nessoN1-meshcore@posteo.de | + | |
| - | **Date:** April 2026 | + | |
| - | + | ||
| - | This directory contains the Hardware Abstraction Layer (HAL) for the | + | |
| - | **Arduino Nesso N1** as a MeshCore target platform. The board was co-developed | + | |
| - | by Arduino and M5Stack, combining an ESP32-C6 microcontroller with an SX1262 | + | |
| - | LoRa transceiver, | + | |
| - | in a compact, battery-powered enclosure. | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Hardware Overview ===== | + | |
| - | + | ||
| - | | Component | Details | | + | |
| - | |---|---| | + | |
| - | | MCU | Espressif ESP32-C6 (RISC-V, 160 MHz) | | + | |
| - | | Flash | 16 MB | | + | |
| - | | RAM | 512 KB | | + | |
| - | | LoRa | Semtech SX1262, 850\u2013960 MHz, up to +22 dBm | | + | |
| - | | RF Switch / LNA | External, controlled via I\u00b2C GPIO expander | | + | |
| - | | I\u00b2C GPIO Expander | PI4IOE5V6408 (2\u00d7 instances, addresses 0x43 and 0x44) | | + | |
| - | | Display | ST7789P3, 1.14\u2033, 240\u00d7135 px, touchscreen (FT6336U) | | + | |
| - | | IMU | BMI270 (6-axis) | | + | |
| - | | Battery | 250 mAh LiPo, USB-C charging | | + | |
| - | | Connectivity | Wi-Fi 6, BT 5.3, Thread/ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Key Porting Decisions ===== | + | |
| - | + | ||
| - | ==== SX1262 Reset and RF Switch via I\u00b2C Expander ==== | + | |
| - | + | ||
| - | Unlike most other MeshCore targets, the LoRa control lines on the Nesso N1 | + | |
| - | are not connected directly to ESP32-C6 GPIO pins. They are routed through a | + | |
| - | **PI4IOE5V6408 I\u00b2C GPIO expander** (address 0x43): | + | |
| - | + | ||
| - | | Signal | Expander Pin | Function | | + | |
| - | |---|---|---| | + | |
| - | | SX_NRST | P7 | SX1262 hardware reset | | + | |
| - | | SX_ANT_SW | P6 | Antenna RF switch | | + | |
| - | | SX_LNA_EN | P5 | Low-noise amplifier enable | | + | |
| - | | KEY1 | P0 | Button A (input) | | + | |
| - | | KEY2 | P1 | Button B (input) | | + | |
| - | + | ||
| - | RadioLib' | + | |
| - | '' | + | |
| - | + | ||
| - | ==== Shared SPI Bus (SX1262 + ST7789) ==== | + | |
| - | + | ||
| - | | Signal | GPIO | | + | |
| - | |---|---| | + | |
| - | | MOSI | G21 | | + | |
| - | | MISO | G22 | | + | |
| - | | SCK | G20 | | + | |
| - | | LoRa CS (NSS) | G23 (software CS) | | + | |
| - | | LCD CS | G17 (software CS) | | + | |
| - | | LCD DC | G16 | | + | |
| - | + | ||
| - | '' | + | |
| - | a duplicate APB hardware CS-callback (see Fix 4). | + | |
| - | + | ||
| - | ==== FSPI instead of default SPI ==== | + | |
| - | + | ||
| - | The ESP32-C6 has no VSPI bus: '' | + | |
| - | + | ||
| - | ==== USB-CDC and BLE are mutually exclusive ==== | + | |
| - | + | ||
| - | | Firmware | '' | + | |
| - | |---|---|---| | + | |
| - | | Repeater | '' | + | |
| - | | Companion BLE | '' | + | |
| - | | Companion WiFi | '' | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Display Architecture ===== | + | |
| - | + | ||
| - | ==== Resolution and UITask Layout Verification ==== | + | |
| - | + | ||
| - | The ST7789P3 has **240\u00d7135 pixels** (Landscape, '' | + | |
| - | All UITask default layouts fit without overflow: | + | |
| - | + | ||
| - | | Element | Width | x (centred) | Right edge | Fits | | + | |
| - | |---|---|---|---|---| | + | |
| - | | MeshCore logo (64\u00d736 px XBM) | 64 px | 88 | 152 | \u2705 | | + | |
| - | | Version line (setTextSize 2) | 156 px | 42 | 198 | \u2705 | | + | |
| - | | Node name \" | + | |
| - | | Frequency line \" | + | |
| - | + | ||
| - | All four elements together occupy ~100 px of 135 px height \u2014 no vertical overflow. | + | |
| - | + | ||
| - | ==== Mandatory Initialisation Order ==== | + | |
| - | + | ||
| - | < | + | |
| - | radio_init() in target.cpp: | + | |
| - | 1. board.begin() | + | |
| - | 2. board.onRfRx() | + | |
| - | 3. radio.std_init(nullptr) RadioLib \u2014 nullptr: no second spi-> | + | |
| - | 4. display.begin() | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Known Issues and Their Fixes ===== | + | |
| - | + | ||
| - | ==== Fix 1 \u2014 Display Goes Black After a Few Seconds ==== | + | |
| - | + | ||
| - | **Date:** April 2026 | + | |
| - | **Affected file:** '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | then stays permanently black. Button presses have no effect. | + | |
| - | + | ||
| - | **Root cause:** | + | |
| - | '' | + | |
| - | '' | + | |
| - | '' | + | |
| - | a direct GPIO pin. On the Nesso N1, KEY1 and KEY2 are connected **via I\u00b2C | + | |
| - | expander 0** (address 0x43, pins P0/P1) and are **not direct GPIOs**. | + | |
| - | UITask therefore has **no wakeup path** \u2014 once the timeout fires, the | + | |
| - | display stays off permanently. | + | |
| - | + | ||
| - | **Fix:** | + | |
| - | < | + | |
| - | ; platformio.ini \u2014 [env: | + | |
| - | -D SCREEN_TIMEOUT=0 | + | |
| - | -D DISPLAY_TIMEOUT=0 | + | |
| - | </ | + | |
| - | Both flags set to 0 disable the automatic timeout entirely. | + | |
| - | '' | + | |
| - | + | ||
| - | **Note for battery operation: | + | |
| - | Button wakeup is already integrated via '' | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 7 \u2014 Buttons Non-Functional in Repeater Display Mode ==== | + | |
| - | + | ||
| - | **Date:** April 2026 | + | |
| - | **Affected files:** '' | + | |
| - | '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | '' | + | |
| - | + | ||
| - | **Root cause: | + | |
| - | '' | + | |
| - | is already on. In '' | + | |
| - | **commented out**. Additionally, | + | |
| - | connection to '' | + | |
| - | + | ||
| - | **Fix \u2014 three levels:** | + | |
| - | + | ||
| - | 1. **New interface '' | + | |
| - | < | + | |
| - | class UIActions { | + | |
| - | public: | + | |
| - | virtual void uiGetNeighborList(char* buf, int bufSize) = 0; | + | |
| - | virtual void uiSendDiscover() = 0; | + | |
| - | }; | + | |
| - | </ | + | |
| - | + | ||
| - | 2. **'' | + | |
| - | < | + | |
| - | class MyMesh : public mesh::Mesh, public CommonCLICallbacks, | + | |
| - | void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf); | + | |
| - | void uiSendDiscover() override { sendNodeDiscoverReq(); | + | |
| - | }; | + | |
| - | </ | + | |
| - | + | ||
| - | 3. **'' | + | |
| - | < | + | |
| - | SCREEN_HOME | + | |
| - | SCREEN_NEIGHBOURS \u2014 neighbour list (hex ID, age, SNR) | + | |
| - | SCREEN_POLL_SENT | + | |
| - | </ | + | |
| - | + | ||
| - | 4. **'' | + | |
| - | < | + | |
| - | if (btn == -1) ui_task.nextScreen(); | + | |
| - | if (btn == -2) ui_task.prevScreen(); | + | |
| - | </ | + | |
| - | + | ||
| - | **Button behaviour after patch:** | + | |
| - | + | ||
| - | | Button | Display OFF | Display ON (Home) | Display ON (Neighbours) | | + | |
| - | |--------|-------------|-------------------|-------------------------| | + | |
| - | | KEY1 | Wakeup | \u2192 Neighbours screen | \u2192 Send poll | | + | |
| - | | KEY2 | Wakeup | stays Home | \u2192 Home screen | | + | |
| - | + | ||
| - | After sending a poll, the screen shows "Poll sent" for 2 seconds, | + | |
| - | then automatically returns to the (refreshed) neighbour list. | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 8 \u2014 Buttons Not Recognised: Wire Conflict Between M5GFX and Expander Driver ==== | + | |
| - | + | ||
| - | **Date:** April 2026 | + | |
| - | **Affected files:** '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | and boot lock. Serial log shows: | + | |
| - | < | + | |
| - | [E][Wire.cpp: | + | |
| - | [E][esp32-hal-cpu.c: | + | |
| - | </ | + | |
| - | + | ||
| - | **Root cause: | + | |
| - | M5GFX initialises the I\u00b2C bus internally via '' | + | |
| - | The custom '' | + | |
| - | Both access the **same bus** (SDA=GPIO10, | + | |
| - | causing bus collisions and unstable expander reads. Documented in the Arduino Forum | + | |
| - | (November 2025): https:// | + | |
| - | + | ||
| - | **Fix: | + | |
| - | '' | + | |
| - | '' | + | |
| - | no more Wire conflict. | + | |
| - | + | ||
| - | < | + | |
| - | // NessoDisplayDriver.cpp \u2014 begin(): | + | |
| - | M5.begin(cfg); | + | |
| - | _gfx = & | + | |
| - | + | ||
| - | // checkButtons(): | + | |
| - | M5.update(); | + | |
| - | if (M5.BtnA.wasPressed()) return 1; // KEY1 | + | |
| - | if (M5.BtnB.wasPressed()) return 2; // KEY2 | + | |
| - | </ | + | |
| - | + | ||
| - | < | + | |
| - | ; platformio.ini \u2014 nesso_n1_base: | + | |
| - | lib_deps = | + | |
| - | m5stack/ | + | |
| - | m5stack/ | + | |
| - | </ | + | |
| - | + | ||
| - | Removed: '' | + | |
| - | is no longer needed; M5Unified handles edge detection internally. | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 2 \u2014 Display in Portrait Mode (135\u00d7240 Instead of 240\u00d7135) ==== | + | |
| - | + | ||
| - | **Date:** April 2026 | + | |
| - | **Affected file:** '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | UITask text is clipped with a negative x-offset and is not readable. | + | |
| - | + | ||
| - | **Root cause:** | + | |
| - | '' | + | |
| - | M5GFX swaps dimensions only after '' | + | |
| - | always returned 135\u00d7240. The problem also occurred when '' | + | |
| - | before '' | + | |
| - | '' | + | |
| - | + | ||
| - | **Fix in '' | + | |
| - | < | + | |
| - | _gfx-> | + | |
| - | Serial.printf(" | + | |
| - | _gfx-> | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 3 \u2014 '' | + | |
| - | + | ||
| - | **Date:** April 2026 | + | |
| - | **Affected file:** '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | + | ||
| - | **Root cause:** | + | |
| - | '' | + | |
| - | ''# | + | |
| - | '' | + | |
| - | '' | + | |
| - | boots again, BUSY HIGH again \u2014 '' | + | |
| - | \u2014 '' | + | |
| - | + | ||
| - | **Fix in '' | + | |
| - | < | + | |
| - | static bool s_boardBeginCalled = false; | + | |
| - | if (s_boardBeginCalled) { | + | |
| - | Serial.println(" | + | |
| - | return; | + | |
| - | } | + | |
| - | s_boardBeginCalled = true; | + | |
| - | </ | + | |
| - | + | ||
| - | **Complete solution:** Remove '' | + | |
| - | '' | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 4 \u2014 APB Callback Duplicate and Wire.setPins Warning ==== | + | |
| - | + | ||
| - | **Date:** April 2026 | + | |
| - | **Affected files:** '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | < | + | |
| - | [E][Wire.cpp: | + | |
| - | [E][esp32-hal-cpu.c: | + | |
| - | [E][esp32-hal-cpu.c: | + | |
| - | </ | + | |
| - | + | ||
| - | **Root cause:** | + | |
| - | '' | + | |
| - | on first '' | + | |
| - | '' | + | |
| - | runs afterwards, the ESP-IDF SPI driver tries to register the same APB | + | |
| - | callback again \u2192 '' | + | |
| - | + | ||
| - | **Fix:** The board guard (Fix 3) prevents the second '' | + | |
| - | The three '' | + | |
| - | '' | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ==== Fix 5 \u2014 BUSY Pulse Timing After loraReset() ==== | + | |
| - | + | ||
| - | **Date:** April 2026 | + | |
| - | **Affected file:** '' | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | even though '' | + | |
| - | + | ||
| - | **Root cause:** | + | |
| - | '' | + | |
| - | a BUSY-HIGH pulse of less than 1 ms after reset \u2014 already LOW by the time | + | |
| - | '' | + | |
| - | + | ||
| - | **Fix:** '' | + | |
| - | < | + | |
| - | [loraReset] BUSY 2 ms after NRST HIGH: LOW (pulse complete \u2014 normal on Nesso N1) | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Directory Structure ===== | + | |
| - | + | ||
| - | < | + | |
| - | variants/ | + | |
| - | \u251c\u2500\u2500 NessoN1Board.h | + | |
| - | \u251c\u2500\u2500 NessoN1Board.cpp | + | |
| - | \u251c\u2500\u2500 NessoDisplayDriver.h | + | |
| - | \u251c\u2500\u2500 NessoDisplayDriver.cpp | + | |
| - | \u251c\u2500\u2500 target.h | + | |
| - | \u251c\u2500\u2500 target.cpp | + | |
| - | \u251c\u2500\u2500 platformio.ini | + | |
| - | \u251c\u2500\u2500 credentials.ini.example | + | |
| - | \u2514\u2500\u2500 main_cpp_setup_hinweis.txt \u2014 correct main.cpp structure (Fix 3/4) | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Quick Start ===== | + | |
| - | + | ||
| - | ==== 1. Prerequisites ==== | + | |
| - | + | ||
| - | * [PlatformIO](https:// | + | |
| - | * Arduino Nesso N1 connected via USB-C | + | |
| - | + | ||
| - | ==== 2. Set up credentials ==== | + | |
| - | + | ||
| - | < | + | |
| - | cd <project root> | + | |
| - | cp variants/ | + | |
| - | </ | + | |
| - | + | ||
| - | Edit '' | + | |
| - | < | + | |
| - | [credentials] | + | |
| - | build_flags_repeater = | + | |
| - | -D ADMIN_PASSWORD='" | + | |
| - | build_flags_wifi = | + | |
| - | -D WIFI_SSID='" | + | |
| - | -D WIFI_PWD='" | + | |
| - | </ | + | |
| - | + | ||
| - | ==== 3. Build and flash ==== | + | |
| - | + | ||
| - | < | + | |
| - | pio run -e NessoN1_repeater_display --target upload && pio device monitor -b 115200 | + | |
| - | </ | + | |
| - | + | ||
| - | ==== 4. Expected serial output (repeater with display) ==== | + | |
| - | + | ||
| - | < | + | |
| - | [loraReset] BUSY 2 ms after NRST HIGH: LOW (normal on Nesso N1) | + | |
| - | [loraReset] BUSY=LOW after 0 ms \u2014 SX1262 ready | + | |
| - | [display] M5GFX ready: 240 x 135 px (after setRotation(1)) | + | |
| - | [display] ST7789 ready, backlight on | + | |
| - | [board] begin() already initialised \u2014 guard active | + | |
| - | [radio] std_init OK | + | |
| - | [init] === radio_init() complete \u2014 radio ready === | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Pin Reference ===== | + | |
| - | + | ||
| - | ==== Direct ESP32-C6 GPIOs ==== | + | |
| - | + | ||
| - | | Function | GPIO | | + | |
| - | |---|---| | + | |
| - | | LoRa MOSI | G21 | LoRa MISO | G22 | LoRa SCK | G20 | | + | |
| - | | LoRa CS (NSS) | G23 | LoRa BUSY | G19 | LoRa IRQ (DIO1) | G15 | | + | |
| - | | LCD CS | G17 | LCD DC | G16 | I\u00b2C SDA | G10 | I\u00b2C SCL | G8 | Touch INT | G3 | | + | |
| - | + | ||
| - | ==== Via I\u00b2C Expander 0 (0x43) ==== | + | |
| - | + | ||
| - | KEY1=P0, KEY2=P1, LNA Enable=P5, Antenna Switch=P6, LoRa Reset (NRST)=P7 | + | |
| - | + | ||
| - | ==== Via I\u00b2C Expander 1 (0x44) ==== | + | |
| - | + | ||
| - | LCD Reset=P1, LCD Backlight=P6 | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== RF Switch Logic ===== | + | |
| - | + | ||
| - | | Mode | ANT_SW | LNA_EN | Description | | + | |
| - | |---|---|---|---| | + | |
| - | | TX | HIGH | LOW | TX path active, LNA protected | | + | |
| - | | RX | HIGH | HIGH | RX path active with LNA | | + | |
| - | | IDLE | LOW | LOW | RF path disconnected, | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== Open Items ===== | + | |
| - | + | ||
| - | | # | Topic | Status | | + | |
| - | |---|---|---| | + | |
| - | | 1 | Remove '' | + | |
| - | | 2 | Hook KEY1/KEY2 into UITask screen wakeup | open | | + | |
| - | | 3 | Touch FT6336U (I\u00b2C 0x38) | open | | + | |
| - | | 4 | IMU BMI270 (I\u00b2C 0x68) for auto-rotation | open | | + | |
| - | | 5 | Battery monitor (I\u00b2C 0x49) | open | | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ===== License ===== | + | |
| - | + | ||
| - | MIT License, identical to MeshCore. Full text in the repository root. | + | |