Webseiten-Werkzeuge


weitere_hardware:nesso_n1

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
weitere_hardware:nesso_n1 [18.04.2026 18:06] – angelegt mellinuxweitere_hardware:nesso_n1 [21.04.2026 14:55] (aktuell) mellinux
Zeile 1: Zeile 1:
 **HINWEIS:** Befindet sich in der Testphase! **HINWEIS:** Befindet sich in der Testphase!
  
-# MeshCore — Arduino Nesso N1 (ESP32-C6)+**HINWEIS:** team-nessoN1-meshcore muss aus Gesundheitlichen Gründen Pausieren. Wer mag kann Ihn wegen Details der Projektübergabe unter E-Mail --- //[[@team-nessoN1-meshcore@posteo.de@|team-nessoN1-meshcore]]// kontaktieren.
  
-**Author:** team-nessoN1-meshcore — team-nessoN1-meshcore@posteo.de  +====== MeshCore — Arduino Nesso N1 (ESP32-C6) ====== 
 + 
 +**Autor:** team-nessoN1-meshcore — team-nessoN1-meshcore@posteo.de\\
 **Stand:** April 2026 **Stand:** April 2026
  
-Dieses Verzeichnis enthält die Hardware-Abstraktionsschicht (HAL) für den +Diese Seite beschreibt die Hardware-Abstraktionsschicht (HAL) für den **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, Wi-Fi 6, Bluetooth 5.3, Thread/Zigbee und einem 1,14″-Touchscreen in einem kompakten, batteriebetriebenen Gehäuse.
-**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, Wi-Fi 6, Bluetooth 5.3, Thread/Zigbee und +
-einem 1,14″-Touchscreen in einem kompakten, batteriebetriebenen Gehäuse.+
  
-## Aktueller Status April 2026:+===== Hardware-Überblick =====
  
-Die Migration läuft (Repeater, Companion). Umfangreiche Tests stehen an. Das  +Komponente Details ^
-eigentliche Produkt Nesso N1 ist jung und hat einige Fehler in der Doku. +
-Der Source wurden noch nicht veröffentlicht, weil Alpha Versionstatus. +
- +
- +
---- +
- +
-## Hardware-Überblick +
- +
-Komponente Details +
-|---|---|+
 | MCU | Espressif ESP32-C6 (RISC-V, 160 MHz) | | MCU | Espressif ESP32-C6 (RISC-V, 160 MHz) |
 | Flash | 16 MB | | Flash | 16 MB |
Zeile 36: Zeile 24:
 | Konnektivität | Wi-Fi 6, BT 5.3, Thread/Zigbee (802.15.4), LoRa | | Konnektivität | Wi-Fi 6, BT 5.3, Thread/Zigbee (802.15.4), LoRa |
  
---- +===== Besonderheiten dieser Portierung =====
- +
-## Besonderheiten dieser Portierung+
  
-### 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 Expander-Pin Funktion +Signal Expander-Pin Funktion ^
-|---|---|---|+
 | 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 54: Zeile 37:
 | KEY2 | P1 | Taste B (Input) | | KEY2 | P1 | Taste B (Input) |
  
-Konsequenz: RadioLib'`setRfSwitchTable()funktioniert nicht (nur direkte GPIOs). +Konsequenz: RadioLib'''setRfSwitchTable()'' funktioniert nicht (nur direkte GPIOs). ''NessoN1Board'' implementiert stattdessen das ''RfSwitchCallback''-Interface, das ''CustomSX1262Wrapper'' bei jedem TX/RX-Übergang aufruft.
-`NessoN1Boardimplementiert stattdessen das `RfSwitchCallback`-Interface, +
-das `CustomSX1262Wrapperbei jedem TX/RX-Übergang aufruft.+
  
-### Geteilter SPI-Bus (SX1262 + ST7789)+==== Geteilter SPI-Bus (SX1262 + ST7789) ====
  
-Signal GPIO +Signal GPIO ^
-|---|---|+
 | MOSI | G21 | | MOSI | G21 |
 | MISO | G22 | | MISO | G22 |
Zeile 69: Zeile 49:
 | LCD DC | G16 | | LCD DC | G16 |
  
-`lora_spi.begin()wird **ohne NSS-Argument** aufgerufen. Mit NSS würde +''lora_spi.begin()'' wird **ohne NSS-Argument** aufgerufen. Mit NSS würde Arduino einen APB-Hardware-CS-Callback für GPIO23 registrieren; M5GFX tut dasselbe für GPIO17 — zwei Callbacks auf demselben Bus führen zu ''addApbChangeCallback: duplicate'' und SPI-Korruption.
-Arduino einen APB-Hardware-CS-Callback für GPIO23 registrieren; M5GFX tut +
-dasselbe für GPIO17 — zwei Callbacks auf demselben Bus führen zu +
-`addApbChangeCallback: duplicateund SPI-Korruption.+
  
-### FSPI statt Default-SPI+==== FSPI statt Default-SPI ====
  
-Der ESP32-C6 hat keinen VSPI-Bus: `SPIClass lora_spi(FSPI)`.+Der ESP32-C6 hat keinen VSPI-Bus: ''SPIClass lora_spi(FSPI)''.
  
-### USB-CDC und BLE schließen sich aus+==== USB-CDC und BLE schließen sich aus ====
  
-Firmware | `ARDUINO_USB_CDC_ON_BOOT` | Debug-Output +Firmware ^ ''ARDUINO_USB_CDC_ON_BOOT''Debug-Output ^ 
-|---|---|---| +| Repeater | ''1'' | USB-Serial aktiv | 
-| Repeater | `1| USB-Serial aktiv | +| Companion BLE | ''0'' | kein USB-Serial | 
-| Companion BLE | `0| kein USB-Serial | +| Companion WiFi | ''0'' | kein USB-Serial |
-| Companion WiFi | `0| kein USB-Serial |+
  
----+===== Display-Architektur =====
  
-## Display-Architektur+==== Auflösung und UITask-Layoutprüfung ====
  
-### Auflösung und UITask-Layoutprüfung+Das ST7789P3 hat **240×135 Pixel** (Landscape, ''setRotation(1)''). Alle UITask-Standardlayouts passen ohne Überlauf:
  
-Das ST7789P3 hat **240×135 Pixel** (Landscape, `setRotation(1)`). +Element Breite x (zentriert) Rechte Kante Passt ^
-Alle UITask-Standardlayouts passen ohne Überlauf: +
- +
-Element Breite x (zentriert) Rechte Kante Passt +
-|---|---|---|---|---|+
 | 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 104: Zeile 76:
 Alle vier Elemente belegen zusammen ~100 px von 135 px Höhe — kein vertikaler Überlauf. Alle vier Elemente belegen zusammen ~100 px von 135 px Höhe — kein vertikaler Überlauf.
  
-### Pflichtsequenz der Initialisierung+==== Pflichtsequenz der Initialisierung ====
  
-```+<code>
 radio_init() in target.cpp: radio_init() in target.cpp:
   1. board.begin()           SPI2, Wire, Expander, loraReset()   1. board.begin()           SPI2, Wire, Expander, loraReset()
Zeile 112: Zeile 84:
   3. radio.std_init(nullptr) RadioLib — nullptr: kein zweites spi->begin()   3. radio.std_init(nullptr) RadioLib — nullptr: kein zweites spi->begin()
   4. display.begin()         M5GFX Lazy Construction — zwingend nach std_init!   4. display.begin()         M5GFX Lazy Construction — zwingend nach std_init!
-``` +</code>
- +
---- +
- +
-## Bekannte Probleme und ihre Fixes+
  
----+===== Bekannte Probleme und ihre Fixes =====
  
-### Fix 1 — Display schwarz nach wenigen Sekunden+==== Fix 1 — Display schwarz nach wenigen Sekunden ====
  
-**Datum:** April 2026   +**Datum:** April 2026\\ 
-**Betroffene Datei:** `platformio.ini``[env:NessoN1_repeater_display]`+**Betroffene Datei:** ''platformio.ini''''[env:NessoN1_repeater_display]''
  
-**Symptom:** Display zeigt Boot-Screen (~4 Sek.) und Home-Screen (~10 Sek.), +**Symptom:** Display zeigt Boot-Screen (~4 Sek.) und Home-Screen (~10 Sek.), dann dauerhaft schwarz. Kein Wakeup durch Tastendruck.
-dann dauerhaft schwarz. Kein Wakeup durch Tastendruck.+
  
-**Ursache — präzise:** +**Ursache:** ''UITask.h'' (MeshCore, ''examples/simple_repeater/UITask.h'') definiert den Makro ''SCREEN_TIMEOUT'' (Standardwert: 10 000 ms). Nach Ablauf wird intern ''display.turnOff()'' aufgerufen. Der Wakeup-Pfad in UITask prüft ''PIN_USER_BTN'' — einen direkten GPIO-Pin. Auf dem Nesso N1 hängen KEY1 und KEY2 **über I²C-Expander 0** (Adresse 0x43, Pins P0/P1). UITask hat damit **keinen Wakeup-Mechanismus** — das Display bleibt nach Timeout-Ablauf permanent schwarz.
-`UITask.h(MeshCore, `examples/simple_repeater/UITask.h`) definiert den +
-Makro `SCREEN_TIMEOUT(Standardwert: 10 000 ms). Nach Ablauf wird intern +
-`display.turnOff()aufgerufen. Der Wakeup-Pfad in UITask prüft `PIN_USER_BTN+
-— einen direkten GPIO-Pin der bei einem Tastendruck HIGH wird. Auf dem Nesso N1 +
-hängen KEY1 und KEY2 **über I²C-Expander 0** (Adresse 0x43, Pins P0/P1)+
-Ein direkter GPIO als `PIN_USER_BTN` steht nicht zur Verfügung. UITask hat +
-damit **keinen Wakeup-Mechanismus** — das Display bleibt nach Timeout-Ablauf +
-permanent schwarz.+
  
 **Fix:** **Fix:**
-```ini+<code ini>
 ; platformio.ini — [env:NessoN1_repeater_display]: ; platformio.ini — [env:NessoN1_repeater_display]:
 -D SCREEN_TIMEOUT=0 -D SCREEN_TIMEOUT=0
 -D DISPLAY_TIMEOUT=0 -D DISPLAY_TIMEOUT=0
-``` +</code>
-Beide Flags auf 0 deaktivieren den Timeout vollständig. `DISPLAY_TIMEOUT` +
-wird zusätzlich gesetzt weil verschiedene UITask-Versionen im MeshCore-Repository +
-unterschiedliche Makronamen verwenden.+
  
-**Hinweis Akku-Betrieb:** Sinnvoller Wert z.B. `-D SCREEN_TIMEOUT=30000`. +Beide Flags auf 0 deaktivieren den Timeout vollständig''DISPLAY_TIMEOUT'' wird zusätzlich gesetzt, weil verschiedene UITask-Versionen unterschiedliche Makronamen verwenden.
-Der Button-Wakeup ist über `nesso_ui_tick()` bereits integriert (siehe Fix 7).+
  
----+> **Hinweis Akku-Betrieb:** Sinnvoller Wert z.B. ''-D SCREEN_TIMEOUT=30000''. Der Button-Wakeup ist über ''nesso_ui_tick()'' bereits integriert (siehe Fix 7).
  
-### Fix — Tasten ohne Funktion im Repeater-Display-Modus+==== Fix — Display im Portrait-Modus (135×240 statt 240×135) ====
  
-**Datum:** April 2026   +**Datum:** April 2026\\ 
-**Betroffene Dateien:** `simple_repeater/main.cpp`, `simple_repeater/UITask.h`, +**Betroffene Datei:** ''NessoDisplayDriver.cpp''
-`simple_repeater/UITask.cpp`, `simple_repeater/MyMesh.h`+
  
-**Symptom:** KEY1 und KEY2 reagieren scheinbar nicht. Im Serial-Log erscheint +**Symptom:** Serial meldet ''M5GFX meldet Display: 135 x 240 px''. UITask-Texte werden mit negativem x-Offset abgeschnitten und sind nicht lesbar.
-`[ui] KEY1 erkannt` — die Taste wird erkannt, aber es passiert nichts am Display.+
  
-**Ursache — präzise:**   +**Ursache:** ''setRotation(1)'' wurde in ''begin()'' **nach** ''_gfx->width()'' / ''_gfx->height()'' aufgerufen. M5GFX tauscht die Dimensionen erst nach ''setRotation()'' intern um. Zusätzlich trat das Problem auf, wenn ''display.begin()'' in ''main.cpp'' vor ''radio_init()'' aufgerufen wurde: Der idempotente Guard beim zweiten ''begin()''-Aufruf hat ''setRotation(1)'' dann gar nicht mehr ausgeführt. 
-`nesso_ui_tick()gibt bei erkannter Taste `-1(KEY1) oder `-2(KEY2) zurück, + 
-wenn das Display bereits an ist. In `main.cppwar die Auswertung dieser Werte +**Fix in ''NessoDisplayDriver.cpp'':** 
-vollständig **auskommentiert**. Zusätzlich hatte `UITaskkeine navigierbaren +<code cpp> 
-Screens und keine Verbindung zu `MyMesh`, um Nachbardaten oder einen +// setRotation ZUERST — M5GFX tauscht Dimensionen erst danach: 
-Discover-Request abrufen zu können.+_gfx->setRotation(1); 
 +// Jetzt korrekte Werte: 
 +Serial.printf("[display] M5GFX bereit: %d x %d px\n", _gfx->width(), _gfx->height()); 
 +// Ausgabe: 240 x 135 px ✓ 
 +</code> 
 + 
 +==== Fix 3 — ''radio init failed: -2'' durch doppelten board.begin()-Aufruf ==== 
 + 
 +**Datum:** April 2026\\ 
 +**Betroffene Datei:** ''NessoN1Board.cpp'' 
 + 
 +**Symptom:** ''ERROR: radio init failed: -2'', Serial zeigt ''[loraReset] TIMEOUT!''
 + 
 +**Ursache:** ''examples/simple_repeater/main.cpp'' ruft unter ''#ifdef DISPLAY_CLASS'' in ''setup()'' explizit ''board.begin()'' auf — **vor** ''radio_init()''. Auf dem Nesso N1 führt ''board.begin()'' intern ''loraReset()'' aus: der SX1262 bootet, BUSY geht HIGH. Anschließend ruft ''radio_init()'' erneut ''board.begin()'' auf → zweites ''loraReset()'' → SX1262 bootet erneut, BUSY wieder HIGH → ''radio.std_init()'' trifft Chip mitten im Bootvorgang → ''RADIOLIB_ERR_CHIP_NOT_FOUND'' = ''-2''
 + 
 +**Fix in ''NessoN1Board::begin()'':** 
 +<code cpp> 
 +static bool s_boardBeginCalled = false; 
 +if (s_boardBeginCalled) { 
 +    Serial.println("[board] begin() bereits initialisiert — Guard greift"); 
 +    return;   // loraReset() wird NICHT nochmals ausgeführt 
 +
 +s_boardBeginCalled = true; 
 +</code> 
 + 
 +**Vollständige Lösung:** In ''main.cpp'' ''board.begin()'' und ''display.begin()'' aus ''setup()'' entfernen — beide werden intern durch ''radio_init()'' erledigt (siehe ''main_cpp_setup_hinweis.txt''). 
 + 
 +==== Fix 4 — APB-Callback-Duplikat und Wire.setPins-Warnung ==== 
 + 
 +**Datum:** April 2026\\ 
 +**Betroffene Dateien:** ''NessoN1Board.cpp'', ''NessoDisplayDriver.cpp'' 
 + 
 +**Symptom:** 
 +<code> 
 +[E][Wire.cpp:131] setPins(): bus already initialized. 
 +[E][esp32-hal-cpu.c:123] addApbChangeCallback(): duplicate func=... 
 +[E][esp32-hal-cpu.c:146] removeApbChangeCallback(): not found func=... 
 +</code> 
 + 
 +**Ursache:** ''main.cpp'' ruft ''display.begin()'' vor ''radio_init()'' auf. M5GFX ruft intern ''spi_bus_initialize(SPI2_HOST)'' und ''Wire.begin()'' auf. Wenn anschließend ''radio_init()'' → ''board.begin()'' → ''lora_spi.begin()'' läuft, versucht der ESP-IDF SPI-Treiber denselben APB-Callback erneut zu registrieren → Duplikat. 
 + 
 +**Fix:** Der Board-Guard (Fix 3) verhindert den zweiten ''lora_spi.begin()''-Aufruf. Die drei ''[E]''-Zeilen sind **funktional harmlos**, aber ein sichtbarer Indikator für die unvollständig behobene Aufruf-Reihenfolge. 
 + 
 +==== Fix 5 — BUSY-Puls-Timing nach loraReset() ==== 
 + 
 +**Datum:** April 2026\\ 
 +**Betroffene Datei:** ''NessoN1Board.cpp'' 
 + 
 +**Symptom:** Log meldet ''BUSY direkt nach NRST HIGH: LOW (unerwartet)'' obwohl Reset korrekt funktioniert und ''std_init'' erfolgreich ist. 
 + 
 +**Ursache:** Der ''delay(20)'' nach NRST HIGH war zu lang. Der SX1262 auf dem Nesso N1 hat einen BUSY-HIGH-Puls von unter 1 ms nach dem Reset — der Chip hatte BUSY längst wieder LOW gezogen, wenn ''digitalRead()'' nach 20 ms lief. 
 + 
 +**Fix:** 
 +<code cpp> 
 +_exp0.digitalWrite(NESSO_EXP0_SX_NRST, true); 
 +delay(2);   // war: delay(20) — 2 ms reicht, BUSY-Puls auf Nesso N1 < 1 ms 
 +</code> 
 + 
 +Logmeldung geändert zu: 
 +<code> 
 +[loraReset] BUSY 2 ms nach NRST HIGH: LOW (Chip hat BUSY-Puls bereits abgeschlossen — normal auf Nesso N1) 
 +</code> 
 + 
 +==== Fix 7 — Tasten ohne Funktion im Repeater-Display-Modus ==== 
 + 
 +**Datum:** April 2026\\ 
 +**Betroffene Dateien:** ''simple_repeater/main.cpp'', ''simple_repeater/UITask.h'', ''simple_repeater/UITask.cpp'', ''simple_repeater/MyMesh.h'' 
 + 
 +**Symptom:** KEY1 und KEY2 reagieren scheinbar nicht. Im Serial-Log erscheint ''[ui] KEY1 erkannt'' — die Taste wird erkannt, aber es passiert nichts am Display. 
 + 
 +**Ursache:** ''nesso_ui_tick()'' gibt bei erkannter Taste ''-1'' (KEY1) oder ''-2'' (KEY2) zurück, wenn das Display bereits an ist. In ''main.cpp'' war die Auswertung dieser Werte vollständig **auskommentiert**. Zusätzlich hatte ''UITask'' keine navigierbaren Screens und keine Verbindung zu ''MyMesh''.
  
 **Fix — drei Ebenen:** **Fix — drei Ebenen:**
  
-1. **Neues Interface `UIActions.h`** — entkoppelt UITask von MyMesh: +**1. Neues Interface ''UIActions.h''** — entkoppelt UITask von MyMesh: 
-```cpp+<code cpp>
 class UIActions { class UIActions {
 public: public:
Zeile 178: Zeile 201:
   virtual void uiSendDiscover() = 0;   virtual void uiSendDiscover() = 0;
 }; };
-```+</code>
  
-2. **`MyMeshimplementiert `UIActions`** — in `MyMesh.h`+**2. ''MyMesh'' implementiert ''UIActions''** — in ''MyMesh.h''
-```cpp+<code cpp>
 class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public UIActions { class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public UIActions {
-  ... 
   void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf); }   void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf); }
   void uiSendDiscover() override { sendNodeDiscoverReq(); }   void uiSendDiscover() override { sendNodeDiscoverReq(); }
 }; };
-```+</code>
  
-3. **`UITaskbekommt 3 Screens + Navigation**, verbunden via `setActions()`+**3. ''UITask'' bekommt 3 Screens + Navigation**, verbunden via ''setActions()''
-```+<code>
 SCREEN_HOME       — Node-Name, Frequenz, SF/BW/CR SCREEN_HOME       — Node-Name, Frequenz, SF/BW/CR
 SCREEN_NEIGHBOURS — Nachbarliste (HEX-ID, Alter, SNR) SCREEN_NEIGHBOURS — Nachbarliste (HEX-ID, Alter, SNR)
 SCREEN_POLL_SENT  — Feedback "Poll gesendet" für 2 Sek. SCREEN_POLL_SENT  — Feedback "Poll gesendet" für 2 Sek.
-```+</code>
  
-4. **`main.cppwertet `btnaus** (vorher auskommentiert): +**4. ''main.cpp'' wertet ''btn'' aus** (vorher auskommentiert): 
-```cpp+<code cpp>
 if (btn == -1) ui_task.nextScreen();   // KEY1: vorwärts / Poll if (btn == -1) ui_task.nextScreen();   // KEY1: vorwärts / Poll
 if (btn == -2) ui_task.prevScreen();   // KEY2: zurück if (btn == -2) ui_task.prevScreen();   // KEY2: zurück
-```+</code>
  
 **Bedienung nach dem Patch:** **Bedienung nach dem Patch:**
  
-Taste Display AUS Display AN (Home) Display AN (Nachbarn) +Taste Display AUS Display AN (Home) Display AN (Nachbarn) ^
-|-------|-------------|-------------------|-----------------------|+
 | KEY1 | Wakeup | → Nachbarn-Screen | → Poll senden | | KEY1 | Wakeup | → Nachbarn-Screen | → Poll senden |
 | KEY2 | Wakeup | bleibt Home | → Home-Screen | | KEY2 | Wakeup | bleibt Home | → Home-Screen |
  
-Nach dem Poll zeigt der Screen 2 Sekunden "Poll gesendet", +Nach dem Poll zeigt der Screen 2 Sekunden "Poll gesendet", dann wechselt er automatisch zurück zur aktualisierten Nachbarliste.
-dann wechselt er automatisch zurück zur (aktualisiertenNachbarliste. +
- +
---- +
- +
----+
  
-### Fix 8 — Tasten nicht erkannt: Wire-Konflikt zwischen M5GFX und Expander-Treiber+==== Fix 8 — Tasten nicht erkannt: Wire-Konflikt zwischen M5GFX und Expander-Treiber ====
  
-**Datum:** April 2026   +**Datum:** April 2026\\ 
-**Betroffene Dateien:** `NessoDisplayDriver.cpp``NessoDisplayDriver.h``platformio.ini`+**Betroffene Dateien:** ''NessoDisplayDriver.cpp''''NessoDisplayDriver.h''''platformio.ini''
  
-**Symptom:** KEY1 und KEY2 werden trotz korrekter Flanken-Erkennung und Boot-Sperre +**Symptom:** KEY1 und KEY2 werden trotz korrekter Flanken-Erkennung nicht oder unzuverlässig registriert. Im Serial-Log: 
-nicht oder unzuverlässig registriert. Im Serial-Log erscheinen+<code>
-```+
 [E][Wire.cpp:131] setPins(): bus already initialized. change pins only when not. [E][Wire.cpp:131] setPins(): bus already initialized. change pins only when not.
 [E][esp32-hal-cpu.c:123] addApbChangeCallback(): duplicate func=... [E][esp32-hal-cpu.c:123] addApbChangeCallback(): duplicate func=...
-```+</code>
  
-**Ursache — präzise:**   +**Ursache:** M5GFX initialisiert den I²C-Bus intern über ''WireInternal.begin(SDA, SCL)''. Der eigene ''PI4IOE5V6408''-Treiber in ''NessoN1Board'' initialisiert ''Wire'' ebenfalls direkt. Beide greifen auf **denselben Bus** (SDA=GPIO10, SCL=GPIO8) zu mit unterschiedlichen ''TwoWire''-Instanzen → Bus-Kollisionen. Dokumentiert im [[https://forum.arduino.cc/t/cant-use-buttons-and-graphics-at-the-same-time/1415099|Arduino Forum (November 2025)]].
-M5GFX initialisiert den I²C-Bus intern über `WireInternal.begin(SDA, SCL)`. +
-Der eigene `PI4IOE5V6408`-Treiber in `NessoN1Boardinitialisiert `Wireebenfalls +
-direkt. Beide greifen auf **denselben Bus** (SDA=GPIO10, SCL=GPIO8) zu — +
-mit unterschiedlichen `TwoWire`-Instanzen. Das führt zu Bus-Kollisionen und +
-instabilen Expander-Reads. Dokumentiert im Arduino-Forum (November 2025): +
-https://forum.arduino.cc/t/cant-use-buttons-and-graphics-at-the-same-time/1415099+
  
-**Fix:**   +**Fix:** ''M5Unified'' übernimmt Button-Handling und Display-Init koordiniert. ''M5.begin()'' initialisiert Wire, M5GFX und GPIO-Expander in der richtigen Reihenfolge.
-`M5Unifiedübernimmt Button-Handling und Display-Init koordiniert. +
-`M5.begin()initialisiert Wire, M5GFX und GPIO-Expander in der richtigen +
-Reihenfolge — kein Wire-Konflikt mehr.+
  
-```cpp+<code cpp>
 // NessoDisplayDriver.cpp — begin(): // NessoDisplayDriver.cpp — begin():
 M5.begin(cfg);              // statt: _gfx->begin() M5.begin(cfg);              // statt: _gfx->begin()
Zeile 250: Zeile 256:
 if (M5.BtnA.wasPressed()) return 1;  // KEY1 if (M5.BtnA.wasPressed()) return 1;  // KEY1
 if (M5.BtnB.wasPressed()) return 2;  // KEY2 if (M5.BtnB.wasPressed()) return 2;  // KEY2
-```+</code>
  
-```ini+<code ini>
 ; platformio.ini — nesso_n1_base: ; platformio.ini — nesso_n1_base:
 lib_deps = lib_deps =
   m5stack/M5GFX   m5stack/M5GFX
   m5stack/M5Unified   ; NEU: Fix 8   m5stack/M5Unified   ; NEU: Fix 8
-```+</code>
  
-Entfernt: `_prevBtnState``_btnReadyAt``_lastBtnCheck` (manueller +Entfernt: ''_prevBtnState''''_btnReadyAt''''_lastBtnCheck'' — manueller Debounce entfälltM5Unified macht Flanken-Erkennung intern.
-Debounce entfällt — M5Unified macht Flanken-Erkennung intern).+
  
-### Fix 2 — Display im Portrait-Modus (135×240 statt 240×135)+===== Schnellstart =====
  
-**Datum:** April 2026   +==== 1Voraussetzungen ====
-**Betroffene Datei:** `NessoDisplayDriver.cpp`+
  
-**Symptom:** Serial meldet `M5GFX meldet Display: 135 x 240 px`+  [[https://platformio.org/|PlatformIO]] (CLI oder VS Code Extension) 
-UITask-Texte werden mit negativem x-Offset abgeschnitten und sind nicht +  * Arduino Nesso N1 per USB-C verbunden
-lesbar (Versionszeile bei x=−11, Node-Name bei x=−29).+
  
-**Ursache — präzise:** +==== 2Zugangsdaten einrichten ====
-`setRotation(1)` wurde in `begin()` **nach** `_gfx->width()` / `_gfx->height()` +
-aufgerufen. M5GFX tauscht die Dimensionen erst nach `setRotation()` intern um. +
-Die Abfrage vor `setRotation()` lieferte daher immer die Portrait-Werte 135×240. +
-Zusätzlich trat das Problem auf wenn `display.begin()` in `main.cpp` vor +
-`radio_init()` aufgerufen wurde: Der idempotente Guard beim zweiten `begin()`-Aufruf +
-aus `radio_init()` hat `setRotation(1)` dann gar nicht mehr ausgeführt — der +
-Controller lief weiter im Portrait-Modus obwohl `DisplayDriver(240, 135)` +
-im Konstruktor richtig gesetzt war.+
  
-**Fix in `NessoDisplayDriver.cpp`:** +<code bash>
-```cpp +
-// setRotation ZUERST — M5GFX tauscht Dimensionen erst danach: +
-_gfx->setRotation(1); +
-// Jetzt korrekte Werte: +
-Serial.printf("[display] M5GFX bereit: %d x %d px\n", _gfx->width(), _gfx->height()); +
-// Ausgabe: 240 x 135 px ✓ +
-``` +
- +
---- +
- +
-### Fix 3 — `radio init failed: -2` durch doppelten board.begin()-Aufruf +
- +
-**Datum:** April 2026   +
-**Betroffene Datei:** `NessoN1Board.cpp` +
- +
-**Symptom:** `ERROR: radio init failed: -2`, Serial zeigt `[loraReset] TIMEOUT!`. +
- +
-**Ursache — präzise:** +
-`examples/simple_repeater/main.cpp` (MeshCore-eigener Code) ruft unter +
-`#ifdef DISPLAY_CLASS` in `setup()` explizit `board.begin()` auf — **vor** +
-`radio_init()`. Das generische MeshCore-Muster funktioniert auf anderen Boards +
-problemlos, weil dort Board-Init und Radio-Init unabhängig sind. Auf dem Nesso N1 +
-führt `board.begin()` intern `loraReset()` aus: der SX1262 bootet, BUSY geht HIGH. +
-Anschließend ruft `radio_init()` erneut `board.begin()` auf → zweites `loraReset()` → +
-SX1262 bootet erneut, BUSY wieder HIGH → `radio.std_init()` trifft Chip mitten im +
-Bootvorgang → `RADIOLIB_ERR_CHIP_NOT_FOUND` = `-2`. +
- +
-**Fix in `NessoN1Board::begin()`:** +
-```cpp +
-static bool s_boardBeginCalled = false; +
-if (s_boardBeginCalled) { +
-    Serial.println("[board] begin() bereits initialisiert — Guard greift"); +
-    return;   // loraReset() wird NICHT nochmals ausgeführt +
-+
-s_boardBeginCalled = true; +
-``` +
-Der Guard stellt sicher dass `loraReset()` und `lora_spi.begin()` genau einmal +
-laufen, unabhängig von der Aufruf-Reihenfolge in `main.cpp`. +
- +
-**Vollständige Lösung:** In `main.cpp` `board.begin()` und `display.begin()` +
-aus `setup()` entfernen — beide werden intern durch `radio_init()` erledigt. +
-Da `main.cpp` MeshCore-eigener Code ist, ist der Guard die robustere Variante +
-ohne Repository-Fork. Hinweis: `main_cpp_setup_hinweis.txt`. +
- +
---- +
- +
-### Fix 4 — APB-Callback-Duplikat und Wire.setPins-Warnung +
- +
-**Datum:** April 2026   +
-**Betroffene Dateien:** `NessoN1Board.cpp`, `NessoDisplayDriver.cpp` +
- +
-**Symptom:** +
-``` +
-[E][Wire.cpp:131] setPins(): bus already initialized. +
-[E][esp32-hal-cpu.c:123] addApbChangeCallback(): duplicate func=... +
-[E][esp32-hal-cpu.c:146] removeApbChangeCallback(): not found func=... +
-``` +
- +
-**Ursache — präzise:** +
-`main.cpp` ruft `display.begin()` vor `radio_init()` auf. Beim ersten +
-`display.begin()`-Aufruf konstruiert `NessoDisplayDriver` M5GFX als +
-static-local. M5GFX ruft intern `spi_bus_initialize(SPI2_HOST)` und +
-`Wire.begin()` auf. Wenn anschließend `radio_init()` → `board.begin()` → +
-`lora_spi.begin()` läuft, versucht der ESP-IDF SPI-Treiber denselben +
-APB-Callback erneut zu registrieren → `duplicate`. `Wire.begin()` auf +
-bereits initialisierten Bus → `setPins warning`. +
- +
-**Fix:** +
-Der Board-Guard (Fix 3) verhindert den zweiten `lora_spi.begin()`-Aufruf. +
-`BUSY` bleibt LOW, `std_init` läuft durch. Die drei `[E]`-Zeilen erscheinen +
-trotzdem solange `main.cpp` die falsche Reihenfolge hat — sie sind +
-**funktional harmlos** (System funktioniert korrekt), aber ein sichtbarer +
-Indikator für die unvollständig behobene Aufruf-Reihenfolge. +
- +
---- +
- +
-### Fix 5 — BUSY-Puls-Timing nach loraReset() +
- +
-**Datum:** April 2026   +
-**Betroffene Datei:** `NessoN1Board.cpp` +
- +
-**Symptom:** Log meldet `BUSY direkt nach NRST HIGH: LOW (unerwartet — evtl. kein Reset angekommen)` obwohl Reset korrekt funktioniert und `std_init` erfolgreich ist. +
- +
-**Ursache — präzise:** +
-Der `delay(20)` nach NRST HIGH war zu lang. Der SX1262 auf dem Nesso N1 hat +
-einen BUSY-HIGH-Puls von unter 1 ms nach dem Reset — der Chip hatte BUSY längst +
-wieder LOW gezogen wenn `digitalRead()` nach 20 ms lief. Es handelte sich um +
-keinen echten Fehler, sondern um ein zu konservatives Timing kombiniert mit einer +
-zu alarmistischen Logmeldung. +
- +
-**Fix:** +
-```cpp +
-_exp0.digitalWrite(NESSO_EXP0_SX_NRST, true); +
-delay(2);   // war: delay(20) — 2 ms reicht, BUSY-Puls auf Nesso N1 1 ms +
-``` +
-Logmeldung geändert zu: +
-``` +
-[loraReset] BUSY 2 ms nach NRST HIGH: LOW (Chip hat BUSY-Puls bereits abgeschlossen — normal auf Nesso N1) +
-``` +
- +
---- +
- +
-## Verzeichnisstruktur +
- +
-``` +
-variants/arduino_nesso_n1/ +
-├── NessoN1Board.h             — Pin-Konstanten, PI4IOE5V6408-Treiber, Board-Klasse +
-├── NessoN1Board.cpp           — begin() mit Guard (Fix 3), loraReset() (Fix 5) +
-├── NessoDisplayDriver.h       — DisplayDriver-Erweiterung, Lazy Construction +
-├── NessoDisplayDriver.cpp     — begin() mit setRotation-Fix (Fix 2) +
-├── target.h                   — Externe Deklarationen, nesso_check_buttons() +
-├── target.cpp                 — radio_init() Pflichtsequenz, globale Objekte +
-├── platformio.ini             — incl. SCREEN_TIMEOUT=0 (Fix 1) +
-├── credentials.ini.example    — Vorlage für lokale Zugangsdaten +
-└── main_cpp_setup_hinweis.txt — Hinweis zur korrekten main.cpp-Struktur (Fix 3/4) +
- +
-src/helpers/ui/ +
-└── DisplayDriver.h            — Abstraktes Interface (Pure-Virtuals) +
- +
-src/helpers/radiolib/ +
-├── RfSwitchCallback.h         — Abstraktes Interface für externen RF-Switch +
-└── CustomSX1262Wrapper.h      — SX1262-Wrapper mit RF-Switch-Callback +
-``` +
- +
---- +
- +
-## Schnellstart +
- +
-### 1. Voraussetzungen +
- +
-- [PlatformIO](https://platformio.org/) (CLI oder VS Code Extension) +
-- Arduino Nesso N1 per USB-C verbunden +
- +
-### 2. Zugangsdaten einrichten +
- +
-```bash+
 cd <projektwurzel> cd <projektwurzel>
 cp variants/arduino_nesso_n1/credentials.ini.example credentials.ini cp variants/arduino_nesso_n1/credentials.ini.example credentials.ini
-```+</code>
  
-`credentials.inianpassen:+''credentials.ini'' anpassen:
  
-```ini+<code ini>
 [credentials] [credentials]
 build_flags_repeater = build_flags_repeater =
Zeile 432: Zeile 291:
   -D WIFI_SSID='"dein_wlan"'   -D WIFI_SSID='"dein_wlan"'
   -D WIFI_PWD='"dein_passwort"'   -D WIFI_PWD='"dein_passwort"'
-```+</code>
  
-`credentials.iniist in `.gitignoreund wird **nicht** ins Repository eingecheckt.+''credentials.ini'' ist in ''.gitignore'' und wird **nicht** ins Repository eingecheckt.
  
-### 3. Firmware bauen und flashen+==== 3. Firmware bauen und flashen ====
  
 **Repeater mit Display** (empfohlen als Einstieg): **Repeater mit Display** (empfohlen als Einstieg):
-```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
-```+</code>
  
 **Repeater ohne Display:** **Repeater ohne Display:**
-```bash+<code bash>
 pio run -e NessoN1_repeater --target upload pio run -e NessoN1_repeater --target upload
-```+</code>
  
 **Companion Radio — BLE:** **Companion Radio — BLE:**
-```bash+<code bash>
 pio run -e NessoN1_companion_ble --target upload pio run -e NessoN1_companion_ble --target upload
-```+</code>
  
 **Companion Radio — WiFi/TCP:** **Companion Radio — WiFi/TCP:**
-```bash+<code bash>
 pio run -e NessoN1_companion_wifi --target upload pio run -e NessoN1_companion_wifi --target upload
-```+</code>
  
-### 4. Erwartete Serial-Ausgabe beim Boot (Repeater mit Display)+==== 4. Erwartete Serial-Ausgabe beim Boot (Repeater mit Display) ====
  
-```+<code>
 [board] begin() gestartet [board] begin() gestartet
 [board]      I2C-Scan: [board]      I2C-Scan:
Zeile 477: Zeile 336:
 [init] === radio_init() abgeschlossen — Radio bereit === [init] === radio_init() abgeschlossen — Radio bereit ===
 Repeater ID: ... Repeater ID: ...
-```+</code>
  
-Drei `[E]`-Zeilen (Wire.setPins, APB-Duplikat) erscheinen noch solange +Drei ''[E]''-Zeilen (Wire.setPins, APB-Duplikat) erscheinen noch solange ''main.cpp'' die falsche Aufruf-Reihenfolge hat — sie sind harmlos.
-`main.cppdie Reihenfolge aus Fix 3/4 hat — sie sind harmlos.+
  
----+===== Pin-Referenz =====
  
-## Pin-Referenz+==== Direkte ESP32-C6 GPIOs ====
  
-### Direkte ESP32-C6 GPIOs +Funktion GPIO ^
- +
-Funktion GPIO +
-|---|---|+
 | LoRa MOSI | G21 | | LoRa MOSI | G21 |
 | LoRa MISO | G22 | | LoRa MISO | G22 |
Zeile 502: Zeile 357:
 | Touch INT | G3 | | Touch INT | G3 |
  
-### Über I²C-Expander 0 (0x43)+==== Über I²C-Expander 0 (0x43) ====
  
-Funktion Expander-Pin +Funktion Expander-Pin ^
-|---|---|+
 | Taste A (KEY1) | P0 | | Taste A (KEY1) | P0 |
 | Taste B (KEY2) | P1 | | Taste B (KEY2) | P1 |
Zeile 512: Zeile 366:
 | LoRa Reset (NRST) | P7 | | LoRa Reset (NRST) | P7 |
  
-### Über I²C-Expander 1 (0x44)+==== Über I²C-Expander 1 (0x44) ====
  
-Funktion Expander-Pin +Funktion Expander-Pin ^
-|---|---|+
 | LCD Reset | P1 | | LCD Reset | P1 |
 | LCD Backlight | P6 | | LCD Backlight | P6 |
  
----+===== RF-Switch-Schaltlogik =====
  
-## RF-Switch-Schaltlogik +Modus ANT_SW LNA_EN Beschreibung ^
- +
-Modus ANT_SW LNA_EN Beschreibung +
-|---|---|---|---|+
 | 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+
  
-```+<code>
 target.cpp target.cpp
   └─ radio_init()   └─ radio_init()
Zeile 552: Zeile 400:
   ├─ onRfRx()     ANT_SW=HIGH, LNA_EN=HIGH   ├─ onRfRx()     ANT_SW=HIGH, LNA_EN=HIGH
   └─ onRfIdle()   ANT_SW=LOW,  LNA_EN=LOW   └─ onRfIdle()   ANT_SW=LOW,  LNA_EN=LOW
-```+</code>
  
----+===== Verzeichnisstruktur =====
  
-## Offene Punkte+<code> 
 +variants/arduino_nesso_n1/ 
 +├── NessoN1Board.h             — Pin-Konstanten, PI4IOE5V6408-Treiber, Board-Klasse 
 +├── NessoN1Board.cpp           — begin() mit Guard (Fix 3), loraReset() (Fix 5) 
 +├── NessoDisplayDriver.h       — DisplayDriver-Erweiterung, Lazy Construction 
 +├── NessoDisplayDriver.cpp     — begin() mit setRotation-Fix (Fix 2) 
 +├── target.h                   — Externe Deklarationen, nesso_check_buttons() 
 +├── target.cpp                 — radio_init() Pflichtsequenz, globale Objekte 
 +├── platformio.ini             — incl. SCREEN_TIMEOUT=0 (Fix 1) 
 +├── credentials.ini.example    — Vorlage für lokale Zugangsdaten 
 +└── main_cpp_setup_hinweis.txt — Hinweis zur korrekten main.cpp-Struktur (Fix 3/4)
  
-| # | Thema | Status | +src/helpers/ui/ 
-|---|---|---| +└── DisplayDriver.h            — Abstraktes Interface (Pure-Virtuals) 
-| 1 | `main.cpp``board.begin()`/`display.begin()vor `radio_init()entfernen | Workaround aktiv (Guard) |+ 
 +src/helpers/radiolib/ 
 +├── RfSwitchCallback.h         — Abstraktes Interface für externen RF-Switch 
 +└── CustomSX1262Wrapper.h      — SX1262-Wrapper mit RF-Switch-Callback 
 +</code> 
 + 
 +===== Offene Punkte ===== 
 + 
 +^ # ^ Thema ^ Status ^ 
 +| 1 | ''main.cpp''''board.begin()'' ''display.begin()'' vor ''radio_init()'' entfernen | Workaround aktiv (Guard) |
 | 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 |
Zeile 566: Zeile 433:
 | 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 des Repositories.
  
weitere_hardware/nesso_n1.1776528408.txt.gz · Zuletzt geändert: von mellinux