Webseiten-Werkzeuge


weitere_hardware:nesso_n1

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
weitere_hardware:nesso_n1 [18.04.2026 19:22] 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!
 +
 +**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.
  
 ====== 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 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: ===== +
- +
-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, weil Alpha Versionstatus. +
- +
-----+
  
 ===== Hardware-Überblick ===== ===== Hardware-Überblick =====
  
-Komponente Details +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 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/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 =====
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 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 53: Zeile 37:
 | KEY2 | P1 | Taste B (Input) | | KEY2 | P1 | Taste B (Input) |
  
-Konsequenz: RadioLib's ''setRfSwitchTable()'' funktioniert nicht (nur direkte GPIOs). +Konsequenz: RadioLib's ''setRfSwitchTable()'' funktioniert nicht (nur direkte GPIOs). ''NessoN1Board'' implementiert stattdessen das ''RfSwitchCallback''-Interface, das ''CustomSX1262Wrapper'' bei jedem TX/RX-Übergang aufruft.
-''NessoN1Board'' implementiert stattdessen das ''RfSwitchCallback''-Interface, +
-das ''CustomSX1262Wrapper'' bei 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 68: 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: duplicate'' und SPI-Korruption.+
  
 ==== 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 ''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 =====
Zeile 91: Zeile 66:
 ==== Auflösung und UITask-Layoutprüfung ==== ==== Auflösung und UITask-Layoutprüfung ====
  
-Das ST7789P3 hat **240×135 Pixel** (Landscape, ''setRotation(1)''). +Das ST7789P3 hat **240×135 Pixel** (Landscape, ''setRotation(1)''). Alle UITask-Standardlayouts passen ohne Überlauf:
-Alle UITask-Standardlayouts passen ohne Überlauf:+
  
-Element Breite x (zentriert) Rechte Kante Passt +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 112: Zeile 85:
   4. display.begin()         M5GFX Lazy Construction — zwingend nach std_init!   4. display.begin()         M5GFX Lazy Construction — zwingend nach std_init!
 </code> </code>
- 
----- 
  
 ===== 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:** ''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:**
-<code>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> </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 gesetztweil verschiedene UITask-Versionen unterschiedliche Makronamen verwenden.
-Der Button-Wakeup ist über ''nesso_ui_tick()'' bereits integriert (siehe Fix 7). +
- +
----- +
- +
-==== 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 erkanntaber es passiert nichts am Display. +
- +
-**Ursache — präzise:**   +
-''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'', um Nachbardaten oder einen +
-Discover-Request abrufen zu können. +
- +
-**Fix — drei Ebenen:** +
- +
-1. **Neues Interface ''UIActions.h''** — entkoppelt UITask von MyMesh: +
-<code>cpp +
-class UIActions { +
-public: +
-  virtual void uiGetNeighborList(char* buf, int bufSize) = 0; +
-  virtual void uiSendDiscover() = 0; +
-}; +
-</code> +
- +
-2. **''MyMesh'' implementiert ''UIActions''** — in ''MyMesh.h'': +
-<code>cpp +
-class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public UIActions { +
-  ... +
-  void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf);+
-  void uiSendDiscover() override { sendNodeDiscoverReq();+
-}; +
-</code> +
- +
-3. **''UITask'' bekommt 3 Screens + Navigation**, verbunden via ''setActions()'': +
-<code> +
-SCREEN_HOME       — Node-Name, Frequenz, SF/BW/CR +
-SCREEN_NEIGHBOURS — Nachbarliste (HEX-ID, Alter, SNR) +
-SCREEN_POLL_SENT  — Feedback "Poll gesendet" für 2 Sek. +
-</code> +
- +
-4. **''main.cpp'' wertet ''btn'' aus** (vorher auskommentiert): +
-<code>cpp +
-if (btn == -1) ui_task.nextScreen();   // KEY1: vorwärts / Poll +
-if (btn == -2) ui_task.prevScreen();   // KEY2: zurück +
-</code> +
- +
-**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:** ''NessoDisplayDriver.cpp'', ''NessoDisplayDriver.h'', ''platformio.ini'' +
- +
-**Symptom:** KEY1 und KEY2 werden trotz korrekter Flanken-Erkennung und Boot-Sperre +
-nicht oder unzuverlässig registriert. Im Serial-Log erscheinen: +
-<code> +
-[E][Wire.cpp:131] setPins(): bus already initialized. change pins only when not. +
-[E][esp32-hal-cpu.c:123] addApbChangeCallback(): duplicate func=... +
-</code> +
- +
-**Ursache — präzise:**   +
-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. 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:**   +
-''M5Unified'' übernimmt Button-Handling und Display-Init koordiniert. +
-''M5.begin()'' initialisiert Wire, M5GFX und GPIO-Expander in der richtigen +
-Reihenfolge — kein Wire-Konflikt mehr. +
- +
-<code>cpp +
-// NessoDisplayDriver.cpp — begin(): +
-M5.begin(cfg);              // statt: _gfx->begin() +
-_gfx = &M5.Display;        // statt: static M5GFX gfx_instance +
- +
-// checkButtons(): +
-M5.update(); +
-if (M5.BtnA.wasPressed()) return 1;  // KEY1 +
-if (M5.BtnB.wasPressed()) return 2;  // KEY2 +
-</code> +
- +
-<code>ini +
-; platformio.ini — nesso_n1_base: +
-lib_deps = +
-  m5stack/M5GFX +
-  m5stack/M5Unified   ; NEU: Fix 8 +
-</code>+
  
-Entfernt: ''_prevBtnState''''_btnReadyAt'', ''_lastBtnCheck'' (manueller +> **Hinweis Akku-Betrieb:** Sinnvoller Wert z.B. ''-D SCREEN_TIMEOUT=30000''. Der Button-Wakeup ist über ''nesso_ui_tick()'' bereits integriert (siehe Fix 7).
-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:** ''NessoDisplayDriver.cpp'' **Betroffene Datei:** ''NessoDisplayDriver.cpp''
  
-**Symptom:** Serial meldet ''M5GFX meldet Display: 135 x 240 px''. +**Symptom:** Serial meldet ''M5GFX meldet Display: 135 x 240 px''. UITask-Texte werden mit negativem x-Offset abgeschnitten und sind nicht lesbar.
-UITask-Texte werden mit negativem x-Offset abgeschnitten und sind nicht +
-lesbar (Versionszeile bei x=−11, Node-Name bei x=−29).+
  
-**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 aufwenn ''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.
-''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'':** **Fix in ''NessoDisplayDriver.cpp'':**
-<code>cpp+<code cpp>
 // setRotation ZUERST — M5GFX tauscht Dimensionen erst danach: // setRotation ZUERST — M5GFX tauscht Dimensionen erst danach:
 _gfx->setRotation(1); _gfx->setRotation(1);
Zeile 284: Zeile 125:
 // Ausgabe: 240 x 135 px ✓ // Ausgabe: 240 x 135 px ✓
 </code> </code>
- 
----- 
  
 ==== Fix 3 — ''radio init failed: -2'' durch doppelten board.begin()-Aufruf ==== ==== Fix 3 — ''radio init failed: -2'' durch doppelten board.begin()-Aufruf ====
  
-**Datum:** April 2026  +**Datum:** April 2026\\
 **Betroffene Datei:** ''NessoN1Board.cpp'' **Betroffene Datei:** ''NessoN1Board.cpp''
  
 **Symptom:** ''ERROR: radio init failed: -2'', Serial zeigt ''[loraReset] TIMEOUT!''. **Symptom:** ''ERROR: radio init failed: -2'', Serial zeigt ''[loraReset] TIMEOUT!''.
  
-**Ursache — präzise:** +**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''.
-''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()'':** **Fix in ''NessoN1Board::begin()'':**
-<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;
 </code> </code>
-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()'' +**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'').
-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 ==== ==== Fix 4 — APB-Callback-Duplikat und Wire.setPins-Warnung ====
  
-**Datum:** April 2026  +**Datum:** April 2026\\
 **Betroffene Dateien:** ''NessoN1Board.cpp'', ''NessoDisplayDriver.cpp'' **Betroffene Dateien:** ''NessoN1Board.cpp'', ''NessoDisplayDriver.cpp''
  
Zeile 335: Zeile 159:
 </code> </code>
  
-**Ursache — präzise:** +**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.
-''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:** 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() ==== ==== Fix 5 — BUSY-Puls-Timing nach loraReset() ====
  
-**Datum:** April 2026  +**Datum:** April 2026\\
 **Betroffene Datei:** ''NessoN1Board.cpp'' **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.+**Symptom:** Log meldet ''BUSY direkt nach NRST HIGH: LOW (unerwartet)'' obwohl Reset korrekt funktioniert und ''std_init'' erfolgreich ist.
  
-**Ursache — präzise:** +**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 gezogenwenn ''digitalRead()'' nach 20 ms lief.
-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:** **Fix:**
-<code>cpp+<code cpp>
 _exp0.digitalWrite(NESSO_EXP0_SX_NRST, true); _exp0.digitalWrite(NESSO_EXP0_SX_NRST, true);
 delay(2);   // war: delay(20) — 2 ms reicht, BUSY-Puls auf Nesso N1 < 1 ms delay(2);   // war: delay(20) — 2 ms reicht, BUSY-Puls auf Nesso N1 < 1 ms
 </code> </code>
 +
 Logmeldung geändert zu: Logmeldung geändert zu:
 <code> <code>
Zeile 377: Zeile 183:
 </code> </code>
  
-----+==== Fix 7 — Tasten ohne Funktion im Repeater-Display-Modus ====
  
-===== Verzeichnisstruktur =====+**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:** 
 + 
 +**1. Neues Interface ''UIActions.h''** — entkoppelt UITask von MyMesh: 
 +<code cpp> 
 +class UIActions { 
 +public: 
 +  virtual void uiGetNeighborList(char* buf, int bufSize) 0; 
 +  virtual void uiSendDiscover() 0; 
 +}; 
 +</code> 
 + 
 +**2. ''MyMesh'' implementiert ''UIActions''** — in ''MyMesh.h'': 
 +<code cpp> 
 +class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public UIActions { 
 +  void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf);
 +  void uiSendDiscover() override { sendNodeDiscoverReq();
 +}; 
 +</code>
  
 +**3. ''UITask'' bekommt 3 Screens + Navigation**, verbunden via ''setActions()'':
 <code> <code>
-variants/arduino_nesso_n1/ +SCREEN_HOME       — Node-NameFrequenzSF/BW/CR 
-├── NessoN1Board.h             — Pin-KonstantenPI4IOE5V6408-TreiberBoard-Klasse +SCREEN_NEIGHBOURS — Nachbarliste (HEX-IDAlterSNR
-├── NessoN1Board.cpp           — begin() mit Guard (Fix 3), loraReset() (Fix 5) +SCREEN_POLL_SENT  — Feedback "Poll gesendet" für 2 Sek
-├── NessoDisplayDriver.h       — DisplayDriver-ErweiterungLazy Construction +</code>
-├── NessoDisplayDriver.cpp     — begin() mit setRotation-Fix (Fix 2) +
-├── target.h                   — Externe Deklarationennesso_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+**4. ''main.cpp'' wertet ''btn'' aus** (vorher auskommentiert): 
-└── DisplayDriver.h            — Abstraktes Interface (Pure-Virtuals)+<code cpp> 
 +if (btn == -1) ui_task.nextScreen();   // KEY1: vorwärts Poll 
 +if (btn == -2ui_task.prevScreen();   // KEY2: zurück 
 +</code>
  
-src/helpers/radiolib/ +**Bedienung nach dem Patch:** 
-├── RfSwitchCallback.h         — Abstraktes Interface für externen RF-Switch + 
-└── CustomSX1262Wrapper.h      — SX1262-Wrapper mit RF-Switch-Callback+^ 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:** ''NessoDisplayDriver.cpp'', ''NessoDisplayDriver.h'', ''platformio.ini'' 
 + 
 +**Symptom:** KEY1 und KEY2 werden trotz korrekter Flanken-Erkennung nicht oder unzuverlässig registriert. Im Serial-Log: 
 +<code> 
 +[E][Wire.cpp:131] setPins(): bus already initialized. change pins only when not. 
 +[E][esp32-hal-cpu.c:123] addApbChangeCallback(): duplicate func=... 
 +</code> 
 + 
 +**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)]]. 
 + 
 +**Fix:** ''M5Unified'' übernimmt Button-Handling und Display-Init koordiniert. ''M5.begin()'' initialisiert Wire, M5GFX und GPIO-Expander in der richtigen Reihenfolge. 
 + 
 +<code cpp> 
 +// NessoDisplayDriver.cpp — begin(): 
 +M5.begin(cfg);              // statt: _gfx->begin() 
 +_gfx = &M5.Display;        // statt: static M5GFX gfx_instance 
 + 
 +// checkButtons(): 
 +M5.update(); 
 +if (M5.BtnA.wasPressed()) return 1;  // KEY1 
 +if (M5.BtnB.wasPressed()) return 2;  // KEY2 
 +</code> 
 + 
 +<code ini> 
 +; platformio.ini — nesso_n1_base: 
 +lib_deps = 
 +  m5stack/M5GFX 
 +  m5stack/M5Unified   ; NEU: Fix 8
 </code> </code>
  
-----+Entfernt: ''_prevBtnState'', ''_btnReadyAt'', ''_lastBtnCheck'' — manueller Debounce entfällt, M5Unified macht Flanken-Erkennung intern.
  
 ===== Schnellstart ===== ===== Schnellstart =====
Zeile 407: Zeile 271:
 ==== 1. Voraussetzungen ==== ==== 1. Voraussetzungen ====
  
-  * [PlatformIO](https://platformio.org/(CLI oder VS Code Extension)+  * [[https://platformio.org/|PlatformIO]] (CLI oder VS Code Extension)
   * 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 <projektwurzel> cd <projektwurzel>
 cp variants/arduino_nesso_n1/credentials.ini.example credentials.ini cp variants/arduino_nesso_n1/credentials.ini.example credentials.ini
Zeile 419: Zeile 283:
 ''credentials.ini'' anpassen: ''credentials.ini'' anpassen:
  
-<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
 </code> </code>
  
 **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
 </code> </code>
  
 **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
 </code> </code>
Zeile 474: Zeile 338:
 </code> </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.cpp'' die Reihenfolge aus Fix 3/4 hat — sie sind harmlos. +
- +
-----+
  
 ===== Pin-Referenz ===== ===== Pin-Referenz =====
Zeile 483: Zeile 344:
 ==== 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 499: Zeile 359:
 ==== Ü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 509: Zeile 368:
 ==== Ü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 =====
Zeile 549: Zeile 402:
 </code> </code>
  
-----+===== Verzeichnisstruktur ===== 
 + 
 +<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) 
 + 
 +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 
 +</code>
  
 ===== Offene Punkte ===== ===== Offene Punkte =====
  
-Thema Status +Thema Status ^ 
-|---|---|---| +| 1 | ''main.cpp'': ''board.begin()'' / ''display.begin()'' vor ''radio_init()'' entfernen | Workaround aktiv (Guard) |
-| 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 |
 | 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 des Repositories.
- +
- +
----- +
- +
-====== 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, Wi-Fi 6, Bluetooth 5.3, Thread/Zigbee, and a 1.14\u2033 touchscreen +
-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/Zigbee (802.15.4), LoRa | +
- +
----- +
- +
-===== 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's ''setRfSwitchTable()'' (direct GPIO only) cannot be used. +
-''NessoN1Board'' implements the ''RfSwitchCallback'' interface instead. +
- +
-==== 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 | +
- +
-''lora_spi.begin()'' is called **without the NSS argument** to avoid registering +
-a duplicate APB hardware CS-callback (see Fix 4). +
- +
-==== FSPI instead of default SPI ==== +
- +
-The ESP32-C6 has no VSPI bus: ''SPIClass lora_spi(FSPI)''+
- +
-==== USB-CDC and BLE are mutually exclusive ==== +
- +
-| Firmware | ''ARDUINO_USB_CDC_ON_BOOT'' | Debug output | +
-|---|---|---| +
-| Repeater | ''1'' | USB-Serial active | +
-| Companion BLE | ''0'' | no USB-Serial | +
-| Companion WiFi | ''0'' | no USB-Serial | +
- +
----- +
- +
-===== Display Architecture ===== +
- +
-==== Resolution and UITask Layout Verification ==== +
- +
-The ST7789P3 has **240\u00d7135 pixels** (Landscape, ''setRotation(1)''). +
-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 \"NessoN1 Repeater\" | 192 px | 24 | 216 | \u2705 | +
-| Frequency line \"869.6 SF8 BW62.5\" | 204 px | 18 | 222 | \u2705 | +
- +
-All four elements together occupy ~100 px of 135 px height \u2014 no vertical overflow. +
- +
-==== Mandatory Initialisation Order ==== +
- +
-<code> +
-radio_init() in target.cpp: +
-  1. board.begin()           SPI2, Wire, expanders, loraReset() +
-  2. board.onRfRx()          RF switch active for calibration +
-  3. radio.std_init(nullptr) RadioLib \u2014 nullptr: no second spi->begin() +
-  4. display.begin()         M5GFX lazy construction \u2014 must follow std_init! +
-</code> +
- +
----- +
- +
-===== Known Issues and Their Fixes ===== +
- +
-==== Fix 1 \u2014 Display Goes Black After a Few Seconds ==== +
- +
-**Date:** April 2026   +
-**Affected file:** ''platformio.ini'', ''[env:NessoN1_repeater_display]'' +
- +
-**Symptom:** Display shows boot screen (~4 s) and home screen (~10 s), +
-then stays permanently black. Button presses have no effect. +
- +
-**Root cause:** +
-''UITask.h'' (MeshCore, ''examples/simple_repeater/UITask.h'') defines +
-''SCREEN_TIMEOUT'' (default: 10 000 ms). After expiry it calls +
-''display.turnOff()''. The wakeup path in UITask polls ''PIN_USER_BTN'' \u2014 +
-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:** +
-<code>ini +
-; platformio.ini \u2014 [env:NessoN1_repeater_display]: +
--D SCREEN_TIMEOUT=0 +
--D DISPLAY_TIMEOUT=0 +
-</code> +
-Both flags set to 0 disable the automatic timeout entirely. +
-''DISPLAY_TIMEOUT'' is also set because different UITask versions use different macro names. +
- +
-**Note for battery operation:** Use e.g. ''-D SCREEN_TIMEOUT=30000''+
-Button wakeup is already integrated via ''nesso_ui_tick()'' (see Fix 7). +
- +
----- +
- +
-==== Fix 7 \u2014 Buttons Non-Functional in Repeater Display Mode ==== +
- +
-**Date:** April 2026   +
-**Affected files:** ''simple_repeater/main.cpp'', ''simple_repeater/UITask.h'', +
-''simple_repeater/UITask.cpp'', ''simple_repeater/MyMesh.h'' +
- +
-**Symptom:** KEY1 and KEY2 appear to do nothing. Serial log shows +
-''[ui] KEY1 erkannt'' \u2014 the keypress is detected, but the display does not change. +
- +
-**Root cause:**   +
-''nesso_ui_tick()'' correctly returns ''-1'' (KEY1) or ''-2'' (KEY2) when the display +
-is already on. In ''main.cpp'', evaluation of these return values was entirely +
-**commented out**. Additionally, ''UITask'' had no navigable screens and no +
-connection to ''MyMesh'' to fetch neighbour data or send a discover request. +
- +
-**Fix \u2014 three levels:** +
- +
-1. **New interface ''UIActions.h''** \u2014 decouples UITask from MyMesh: +
-<code>cpp +
-class UIActions { +
-public: +
-  virtual void uiGetNeighborList(char* buf, int bufSize) = 0; +
-  virtual void uiSendDiscover() = 0; +
-}; +
-</code> +
- +
-2. **''MyMesh'' implements ''UIActions''** \u2014 in ''MyMesh.h'': +
-<code>cpp +
-class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public UIActions { +
-  void uiGetNeighborList(char* buf, int bufSize) override { formatNeighborsReply(buf);+
-  void uiSendDiscover() override { sendNodeDiscoverReq();+
-}; +
-</code> +
- +
-3. **''UITask'' gains 3 screens + navigation**, connected via ''setActions()'': +
-<code> +
-SCREEN_HOME       \u2014 node name, frequency, SF/BW/CR +
-SCREEN_NEIGHBOURS \u2014 neighbour list (hex ID, age, SNR) +
-SCREEN_POLL_SENT  \u2014 "Poll sent" feedback for 2 seconds +
-</code> +
- +
-4. **''main.cpp'' evaluates ''btn''** (previously commented out): +
-<code>cpp +
-if (btn == -1) ui_task.nextScreen();   // KEY1: forward / poll +
-if (btn == -2) ui_task.prevScreen();   // KEY2: back +
-</code> +
- +
-**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:** ''NessoDisplayDriver.cpp'', ''NessoDisplayDriver.h'', ''platformio.ini'' +
- +
-**Symptom:** KEY1 and KEY2 are not reliably registered despite correct edge detection +
-and boot lock. Serial log shows: +
-<code> +
-[E][Wire.cpp:131] setPins(): bus already initialized. change pins only when not. +
-[E][esp32-hal-cpu.c:123] addApbChangeCallback(): duplicate func=... +
-</code> +
- +
-**Root cause:**   +
-M5GFX initialises the I\u00b2C bus internally via ''WireInternal.begin(SDA, SCL)''+
-The custom ''PI4IOE5V6408'' driver in ''NessoN1Board'' also initialises ''Wire'' directly. +
-Both access the **same bus** (SDA=GPIO10, SCL=GPIO8) with different ''TwoWire'' instances, +
-causing bus collisions and unstable expander reads. Documented in the Arduino Forum +
-(November 2025): https://forum.arduino.cc/t/cant-use-buttons-and-graphics-at-the-same-time/1415099 +
- +
-**Fix:**   +
-''M5Unified'' takes over button handling and display initialisation in a coordinated way. +
-''M5.begin()'' initialises Wire, M5GFX and the GPIO expander in the correct order \u2014 +
-no more Wire conflict. +
- +
-<code>cpp +
-// NessoDisplayDriver.cpp \u2014 begin(): +
-M5.begin(cfg);              // instead of: _gfx->begin() +
-_gfx = &M5.Display;        // instead of: static M5GFX gfx_instance +
- +
-// checkButtons(): +
-M5.update(); +
-if (M5.BtnA.wasPressed()) return 1;  // KEY1 +
-if (M5.BtnB.wasPressed()) return 2;  // KEY2 +
-</code> +
- +
-<code>ini +
-; platformio.ini \u2014 nesso_n1_base: +
-lib_deps = +
-  m5stack/M5GFX +
-  m5stack/M5Unified   ; NEW: Fix 8 +
-</code> +
- +
-Removed: ''_prevBtnState'', ''_btnReadyAt'', ''_lastBtnCheck'' \u2014 manual debounce +
-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:** ''NessoDisplayDriver.cpp'' +
- +
-**Symptom:** Serial reports ''M5GFX reports display: 135 x 240 px''+
-UITask text is clipped with a negative x-offset and is not readable. +
- +
-**Root cause:** +
-''setRotation(1)'' was called **after** querying ''width()'' and ''height()''+
-M5GFX swaps dimensions only after ''setRotation()'', so the pre-rotation query +
-always returned 135\u00d7240. The problem also occurred when ''display.begin()'' ran +
-before ''radio_init()'' from ''main.cpp'': the idempotent guard on the second +
-''begin()'' call skipped ''setRotation(1)'' entirely. +
- +
-**Fix in ''NessoDisplayDriver.cpp'':** +
-<code>cpp +
-_gfx->setRotation(1);  // FIRST \u2014 M5GFX swaps dimensions only after this +
-Serial.printf("[display] M5GFX ready: %d x %d px\n", +
-    _gfx->width(), _gfx->height());  // now correctly 240 x 135 +
-</code> +
- +
----- +
- +
-==== Fix 3 \u2014 ''radio init failed: -2'' from Double board.begin() Call ==== +
- +
-**Date:** April 2026   +
-**Affected file:** ''NessoN1Board.cpp'' +
- +
-**Symptom:** ''ERROR: radio init failed: -2'', serial shows ''[loraReset] TIMEOUT!''+
- +
-**Root cause:** +
-''examples/simple_repeater/main.cpp'' calls ''board.begin()'' in ''setup()'' under +
-''#ifdef DISPLAY_CLASS'' \u2014 **before** ''radio_init()''. On the Nesso N1, +
-''board.begin()'' calls ''loraReset()'': SX1262 boots, BUSY goes HIGH. Then +
-''radio_init()'' calls ''board.begin()'' again \u2014 second ''loraReset()'' \u2014 SX1262 +
-boots again, BUSY HIGH again \u2014 ''radio.std_init()'' hits the chip during boot +
-\u2014 ''RADIOLIB_ERR_CHIP_NOT_FOUND'' = ''-2''+
- +
-**Fix in ''NessoN1Board::begin()'':** +
-<code>cpp +
-static bool s_boardBeginCalled = false; +
-if (s_boardBeginCalled) { +
-    Serial.println("[board] begin() already initialised \u2014 guard active"); +
-    return;   // loraReset() is NOT called again +
-+
-s_boardBeginCalled = true; +
-</code> +
- +
-**Complete solution:** Remove ''board.begin()'' and ''display.begin()'' from +
-''setup()'' in ''main.cpp'' (see ''main_cpp_setup_hinweis.txt''). +
- +
----- +
- +
-==== Fix 4 \u2014 APB Callback Duplicate and Wire.setPins Warning ==== +
- +
-**Date:** April 2026   +
-**Affected files:** ''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> +
- +
-**Root cause:** +
-''main.cpp'' calls ''display.begin()'' before ''radio_init()''. M5GFX (constructed +
-on first ''display.begin()'') calls ''spi_bus_initialize(SPI2_HOST)'' and +
-''Wire.begin()''. When ''radio_init()'' \u2192 ''board.begin()'' \u2192 ''lora_spi.begin()'' +
-runs afterwards, the ESP-IDF SPI driver tries to register the same APB +
-callback again \u2192 ''duplicate''+
- +
-**Fix:** The board guard (Fix 3) prevents the second ''lora_spi.begin()'' call. +
-The three ''[E]'' lines are **functionally harmless** but remain visible while +
-''main.cpp'' retains the wrong call order. +
- +
----- +
- +
-==== Fix 5 \u2014 BUSY Pulse Timing After loraReset() ==== +
- +
-**Date:** April 2026   +
-**Affected file:** ''NessoN1Board.cpp'' +
- +
-**Symptom:** Log reports ''BUSY immediately after NRST HIGH: LOW (unexpected)'' +
-even though ''std_init'' succeeds. +
- +
-**Root cause:** +
-''delay(20)'' after NRST HIGH was too long. The SX1262 on the Nesso N1 produces +
-a BUSY-HIGH pulse of less than 1 ms after reset \u2014 already LOW by the time +
-''digitalRead()'' ran after 20 ms. No hardware fault, just overly conservative timing. +
- +
-**Fix:** ''delay(20)'' \u2192 ''delay(2)'', updated log message: +
-<code> +
-[loraReset] BUSY 2 ms after NRST HIGH: LOW (pulse complete \u2014 normal on Nesso N1) +
-</code> +
- +
----- +
- +
-===== Directory Structure ===== +
- +
-<code> +
-variants/arduino_nesso_n1/ +
-\u251c\u2500\u2500 NessoN1Board.h             \u2014 pin constants, PI4IOE5V6408 driver, board class +
-\u251c\u2500\u2500 NessoN1Board.cpp           \u2014 begin() with guard (Fix 3), loraReset() (Fix 5) +
-\u251c\u2500\u2500 NessoDisplayDriver.h       \u2014 DisplayDriver extension, lazy construction +
-\u251c\u2500\u2500 NessoDisplayDriver.cpp     \u2014 begin() with setRotation fix (Fix 2) +
-\u251c\u2500\u2500 target.h                   \u2014 extern declarations, nesso_check_buttons() +
-\u251c\u2500\u2500 target.cpp                 \u2014 radio_init() mandatory sequence, global objects +
-\u251c\u2500\u2500 platformio.ini             \u2014 incl. SCREEN_TIMEOUT=0 (Fix 1) +
-\u251c\u2500\u2500 credentials.ini.example    \u2014 template for local credentials +
-\u2514\u2500\u2500 main_cpp_setup_hinweis.txt \u2014 correct main.cpp structure (Fix 3/4) +
-</code> +
- +
----- +
- +
-===== Quick Start ===== +
- +
-==== 1. Prerequisites ==== +
- +
-  * [PlatformIO](https://platformio.org/) (CLI or VS Code extension) +
-  * Arduino Nesso N1 connected via USB-C +
- +
-==== 2. Set up credentials ==== +
- +
-<code>bash +
-cd <project root> +
-cp variants/arduino_nesso_n1/credentials.ini.example credentials.ini +
-</code> +
- +
-Edit ''credentials.ini'': +
-<code>ini +
-[credentials] +
-build_flags_repeater = +
-  -D ADMIN_PASSWORD='"your_password"' +
-build_flags_wifi = +
-  -D WIFI_SSID='"your_network"' +
-  -D WIFI_PWD='"your_password"' +
-</code> +
- +
-==== 3. Build and flash ==== +
- +
-<code>bash +
-pio run -e NessoN1_repeater_display --target upload && pio device monitor -b 115200 +
-</code> +
- +
-==== 4. Expected serial output (repeater with display) ==== +
- +
-<code> +
-[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 === +
-</code> +
- +
----- +
- +
-===== 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, low power | +
- +
----- +
- +
-===== Open Items ===== +
- +
-| # | Topic | Status | +
-|---|---|---| +
-| 1 | Remove ''board.begin()''/''display.begin()'' from ''main.cpp'' ''setup()'' | workaround active (guard) | +
-| 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.+
  
weitere_hardware/nesso_n1.1776532952.txt.gz · Zuletzt geändert: von mellinux