Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
| weitere_hardware:nesso_n1 [18.04.2026 18:06] – angelegt mellinux | weitere_hardware:nesso_n1 [21.04.2026 14:55] (aktuell) – mellinux | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| **HINWEIS: | **HINWEIS: | ||
| - | # MeshCore — Arduino Nesso N1 (ESP32-C6) | + | **HINWEIS: |
| - | **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 | + | 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: | + | ===== Hardware-Überblick ===== |
| - | Die Migration läuft (Repeater, Companion). Umfangreiche Tests stehen an. Das | + | ^ Komponente |
| - | eigentliche Produkt Nesso N1 ist jung und hat einige Fehler in der Doku. | + | |
| - | Der Source wurden noch nicht veröffentlicht, | + | |
| - | + | ||
| - | + | ||
| - | --- | + | |
| - | + | ||
| - | ## Hardware-Überblick | + | |
| - | + | ||
| - | | Komponente | + | |
| - | |---|---| | + | |
| | 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/ | | Konnektivität | Wi-Fi 6, BT 5.3, Thread/ | ||
| - | --- | + | ===== 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 | + | ^ 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 54: | Zeile 37: | ||
| | KEY2 | P1 | Taste B (Input) | | | KEY2 | P1 | Taste B (Input) | | ||
| - | Konsequenz: RadioLib' | + | Konsequenz: RadioLib' |
| - | `NessoN1Board` implementiert stattdessen das `RfSwitchCallback`-Interface, | + | |
| - | das `CustomSX1262Wrapper` bei jedem TX/ | + | |
| - | ### Geteilter SPI-Bus (SX1262 + ST7789) | + | ==== Geteilter SPI-Bus (SX1262 + ST7789) |
| - | | Signal | + | ^ Signal |
| - | |---|---| | + | |
| | 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 | + | '' |
| - | Arduino einen APB-Hardware-CS-Callback für GPIO23 registrieren; | + | |
| - | dasselbe für GPIO17 — zwei Callbacks auf demselben Bus führen zu | + | |
| - | `addApbChangeCallback: | + | |
| - | ### FSPI statt Default-SPI | + | ==== FSPI statt Default-SPI |
| - | Der ESP32-C6 hat keinen VSPI-Bus: | + | Der ESP32-C6 hat keinen VSPI-Bus: |
| - | ### USB-CDC und BLE schließen sich aus | + | ==== USB-CDC und BLE schließen sich aus ==== |
| - | | Firmware | + | ^ Firmware |
| - | |---|---|---| | + | | Repeater | '' |
| - | | Repeater | `1` | USB-Serial aktiv | | + | | Companion BLE | '' |
| - | | Companion BLE | `0` | kein USB-Serial | | + | | Companion WiFi | '' |
| - | | 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, '' |
| - | Das ST7789P3 hat **240×135 Pixel** (Landscape, `setRotation(1)`). | + | ^ Element |
| - | Alle UITask-Standardlayouts passen ohne Überlauf: | + | |
| - | + | ||
| - | | 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 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 |
| - | ``` | + | < |
| radio_init() in target.cpp: | radio_init() in target.cpp: | ||
| 1. board.begin() | 1. board.begin() | ||
| Zeile 112: | Zeile 84: | ||
| 3. radio.std_init(nullptr) RadioLib — nullptr: kein zweites spi-> | 3. radio.std_init(nullptr) RadioLib — nullptr: kein zweites spi-> | ||
| 4. display.begin() | 4. display.begin() | ||
| - | ``` | + | </ |
| - | + | ||
| - | --- | + | |
| - | + | ||
| - | ## 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: | + | **Betroffene Datei: |
| - | **Symptom: | + | **Symptom: |
| - | dann dauerhaft schwarz. Kein Wakeup durch Tastendruck. | + | |
| - | **Ursache | + | **Ursache: |
| - | `UITask.h` (MeshCore, | + | |
| - | Makro `SCREEN_TIMEOUT` (Standardwert: | + | |
| - | `display.turnOff()` aufgerufen. Der Wakeup-Pfad in UITask prüft | + | |
| - | — einen direkten GPIO-Pin | + | |
| - | 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 | + | < |
| ; 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. `DISPLAY_TIMEOUT` | + | |
| - | 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 `nesso_ui_tick()` bereits integriert (siehe Fix 7). | + | |
| - | --- | + | > **Hinweis Akku-Betrieb:** Sinnvoller Wert z.B. '' |
| - | ### Fix 7 — Tasten ohne Funktion | + | ==== Fix 2 — Display |
| - | **Datum:** April 2026 | + | **Datum:** April 2026\\ |
| - | **Betroffene | + | **Betroffene |
| - | `simple_repeater/ | + | |
| - | **Symptom: | + | **Symptom: |
| - | `[ui] KEY1 erkannt` — die Taste wird erkannt, aber es passiert nichts am Display. | + | |
| - | **Ursache — präzise:** | + | **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 | + | **Fix in '' |
| - | vollständig **auskommentiert**. Zusätzlich hatte `UITask` keine navigierbaren | + | <code cpp> |
| - | Screens und keine Verbindung zu `MyMesh`, um Nachbardaten oder einen | + | // setRotation ZUERST |
| - | Discover-Request abrufen zu können. | + | _gfx-> |
| + | // Jetzt korrekte Werte: | ||
| + | Serial.printf(" | ||
| + | // Ausgabe: 240 x 135 px ✓ | ||
| + | </ | ||
| + | |||
| + | ==== Fix 3 — '' | ||
| + | |||
| + | **Datum:** April 2026\\ | ||
| + | **Betroffene Datei:** '' | ||
| + | |||
| + | **Symptom: | ||
| + | |||
| + | **Ursache: | ||
| + | |||
| + | **Fix in '' | ||
| + | <code cpp> | ||
| + | static bool s_boardBeginCalled = false; | ||
| + | if (s_boardBeginCalled) { | ||
| + | Serial.println(" | ||
| + | return; | ||
| + | } | ||
| + | s_boardBeginCalled = true; | ||
| + | </ | ||
| + | |||
| + | **Vollständige Lösung:** In '' | ||
| + | |||
| + | ==== Fix 4 — APB-Callback-Duplikat und Wire.setPins-Warnung ==== | ||
| + | |||
| + | **Datum:** April 2026\\ | ||
| + | **Betroffene Dateien:** '' | ||
| + | |||
| + | **Symptom: | ||
| + | < | ||
| + | [E][Wire.cpp: | ||
| + | [E][esp32-hal-cpu.c: | ||
| + | [E][esp32-hal-cpu.c: | ||
| + | </ | ||
| + | |||
| + | **Ursache: | ||
| + | |||
| + | **Fix:** Der Board-Guard (Fix 3) verhindert den zweiten '' | ||
| + | |||
| + | ==== Fix 5 — BUSY-Puls-Timing nach loraReset() ==== | ||
| + | |||
| + | **Datum:** April 2026\\ | ||
| + | **Betroffene Datei:** '' | ||
| + | |||
| + | **Symptom: | ||
| + | |||
| + | **Ursache: | ||
| + | |||
| + | **Fix:** | ||
| + | <code cpp> | ||
| + | _exp0.digitalWrite(NESSO_EXP0_SX_NRST, | ||
| + | delay(2); | ||
| + | </ | ||
| + | |||
| + | Logmeldung geändert zu: | ||
| + | < | ||
| + | [loraReset] BUSY 2 ms nach NRST HIGH: LOW (Chip hat BUSY-Puls bereits abgeschlossen — normal auf Nesso N1) | ||
| + | </ | ||
| + | |||
| + | ==== Fix 7 — Tasten ohne Funktion im Repeater-Display-Modus ==== | ||
| + | |||
| + | **Datum:** April 2026\\ | ||
| + | **Betroffene Dateien:** '' | ||
| + | |||
| + | **Symptom: | ||
| + | |||
| + | **Ursache: | ||
| **Fix — drei Ebenen:** | **Fix — drei Ebenen:** | ||
| - | 1. **Neues Interface | + | **1. Neues Interface |
| - | ```cpp | + | < |
| class UIActions { | class UIActions { | ||
| public: | public: | ||
| Zeile 178: | Zeile 201: | ||
| virtual void uiSendDiscover() = 0; | virtual void uiSendDiscover() = 0; | ||
| }; | }; | ||
| - | ``` | + | </ |
| - | 2. **`MyMesh` implementiert | + | **2. '' |
| - | ```cpp | + | < |
| class MyMesh : public mesh::Mesh, public CommonCLICallbacks, | class MyMesh : public mesh::Mesh, public CommonCLICallbacks, | ||
| - | ... | ||
| 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(); | ||
| }; | }; | ||
| - | ``` | + | </ |
| - | 3. **`UITask` bekommt 3 Screens + Navigation**, | + | **3. '' |
| - | ``` | + | < |
| SCREEN_HOME | SCREEN_HOME | ||
| SCREEN_NEIGHBOURS — Nachbarliste (HEX-ID, Alter, SNR) | SCREEN_NEIGHBOURS — Nachbarliste (HEX-ID, Alter, SNR) | ||
| SCREEN_POLL_SENT | SCREEN_POLL_SENT | ||
| - | ``` | + | </ |
| - | 4. **`main.cpp` wertet | + | **4. '' |
| - | ```cpp | + | < |
| if (btn == -1) ui_task.nextScreen(); | if (btn == -1) ui_task.nextScreen(); | ||
| if (btn == -2) ui_task.prevScreen(); | if (btn == -2) ui_task.prevScreen(); | ||
| - | ``` | + | </ |
| **Bedienung nach dem Patch:** | **Bedienung nach dem Patch:** | ||
| - | | Taste | Display AUS | Display AN (Home) | + | ^ Taste ^ Display AUS ^ Display AN (Home) |
| - | |-------|-------------|-------------------|-----------------------| | + | |
| | 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. | + | |
| - | + | ||
| - | --- | + | |
| - | + | ||
| - | --- | + | |
| - | ### 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: | + | **Betroffene Dateien: |
| - | **Symptom: | + | **Symptom: |
| - | nicht oder unzuverlässig registriert. Im Serial-Log | + | < |
| - | ``` | + | |
| [E][Wire.cpp: | [E][Wire.cpp: | ||
| [E][esp32-hal-cpu.c: | [E][esp32-hal-cpu.c: | ||
| - | ``` | + | </ |
| - | **Ursache | + | **Ursache: |
| - | M5GFX initialisiert den I²C-Bus intern über `WireInternal.begin(SDA, | + | |
| - | Der eigene | + | |
| - | direkt. Beide greifen auf **denselben Bus** (SDA=GPIO10, | + | |
| - | mit unterschiedlichen | + | |
| - | instabilen Expander-Reads. Dokumentiert im Arduino-Forum (November 2025): | + | |
| - | https:// | + | |
| - | **Fix: | + | **Fix: |
| - | `M5Unified` übernimmt Button-Handling und Display-Init koordiniert. | + | |
| - | `M5.begin()` initialisiert Wire, M5GFX und GPIO-Expander in der richtigen | + | |
| - | Reihenfolge | + | |
| - | ```cpp | + | < |
| // NessoDisplayDriver.cpp — begin(): | // NessoDisplayDriver.cpp — begin(): | ||
| M5.begin(cfg); | M5.begin(cfg); | ||
| 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 | ||
| - | ``` | + | </ |
| - | ```ini | + | < |
| ; platformio.ini — nesso_n1_base: | ; platformio.ini — nesso_n1_base: | ||
| lib_deps = | lib_deps = | ||
| m5stack/ | m5stack/ | ||
| m5stack/ | m5stack/ | ||
| - | ``` | + | </ |
| - | Entfernt: | + | Entfernt: |
| - | Debounce entfällt | + | |
| - | ### Fix 2 — Display im Portrait-Modus (135×240 statt 240×135) | + | ===== Schnellstart ===== |
| - | **Datum:** April 2026 | + | ==== 1. Voraussetzungen ==== |
| - | **Betroffene Datei:** `NessoDisplayDriver.cpp` | + | |
| - | **Symptom:** Serial meldet `M5GFX meldet Display: 135 x 240 px`. | + | |
| - | 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: | + | ==== 2. Zugangsdaten einrichten ==== |
| - | `setRotation(1)` wurde in `begin()` **nach** `_gfx-> | + | |
| - | 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, | + | |
| - | im Konstruktor richtig gesetzt war. | + | |
| - | **Fix in `NessoDisplayDriver.cpp`: | + | <code bash> |
| - | ```cpp | + | |
| - | // setRotation ZUERST — M5GFX tauscht Dimensionen erst danach: | + | |
| - | _gfx-> | + | |
| - | // Jetzt korrekte Werte: | + | |
| - | Serial.printf(" | + | |
| - | // Ausgabe: 240 x 135 px ✓ | + | |
| - | ``` | + | |
| - | + | ||
| - | --- | + | |
| - | + | ||
| - | ### Fix 3 — `radio init failed: -2` durch doppelten board.begin()-Aufruf | + | |
| - | + | ||
| - | **Datum:** April 2026 | + | |
| - | **Betroffene Datei:** `NessoN1Board.cpp` | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | + | ||
| - | **Ursache — präzise: | + | |
| - | `examples/ | + | |
| - | `#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:: | + | |
| - | ```cpp | + | |
| - | static bool s_boardBeginCalled = false; | + | |
| - | if (s_boardBeginCalled) { | + | |
| - | Serial.println(" | + | |
| - | return; | + | |
| - | } | + | |
| - | 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`, | + | |
| - | + | ||
| - | **Symptom: | + | |
| - | ``` | + | |
| - | [E][Wire.cpp: | + | |
| - | [E][esp32-hal-cpu.c: | + | |
| - | [E][esp32-hal-cpu.c: | + | |
| - | ``` | + | |
| - | + | ||
| - | **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: | + | |
| - | + | ||
| - | **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, | + | |
| - | delay(2); | + | |
| - | ``` | + | |
| - | Logmeldung geändert zu: | + | |
| - | ``` | + | |
| - | [loraReset] BUSY 2 ms nach NRST HIGH: LOW (Chip hat BUSY-Puls bereits abgeschlossen — normal auf Nesso N1) | + | |
| - | ``` | + | |
| - | + | ||
| - | --- | + | |
| - | + | ||
| - | ## 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 | + | |
| - | ``` | + | |
| - | + | ||
| - | --- | + | |
| - | + | ||
| - | ## Schnellstart | + | |
| - | + | ||
| - | ### 1. Voraussetzungen | + | |
| - | + | ||
| - | - [PlatformIO](https:// | + | |
| - | - Arduino Nesso N1 per USB-C verbunden | + | |
| - | + | ||
| - | ### 2. Zugangsdaten einrichten | + | |
| - | + | ||
| - | ```bash | + | |
| cd < | cd < | ||
| cp variants/ | cp variants/ | ||
| - | ``` | + | </ |
| - | `credentials.ini` anpassen: | + | '' |
| - | ```ini | + | < |
| [credentials] | [credentials] | ||
| build_flags_repeater = | build_flags_repeater = | ||
| Zeile 432: | Zeile 291: | ||
| -D WIFI_SSID='" | -D WIFI_SSID='" | ||
| -D WIFI_PWD='" | -D WIFI_PWD='" | ||
| - | ``` | + | </ |
| - | > `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 | + | < |
| 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 | ||
| - | ``` | + | </ |
| **Repeater ohne Display:** | **Repeater ohne Display:** | ||
| - | ```bash | + | < |
| pio run -e NessoN1_repeater --target upload | pio run -e NessoN1_repeater --target upload | ||
| - | ``` | + | </ |
| **Companion Radio — BLE:** | **Companion Radio — BLE:** | ||
| - | ```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:** | ||
| - | ```bash | + | < |
| pio run -e NessoN1_companion_wifi --target upload | pio run -e NessoN1_companion_wifi --target upload | ||
| - | ``` | + | </ |
| - | ### 4. Erwartete Serial-Ausgabe beim Boot (Repeater mit Display) | + | ==== 4. Erwartete Serial-Ausgabe beim Boot (Repeater mit Display) |
| - | ``` | + | < |
| [board] begin() gestartet | [board] begin() gestartet | ||
| [board] | [board] | ||
| Zeile 477: | Zeile 336: | ||
| [init] === radio_init() abgeschlossen — Radio bereit === | [init] === radio_init() abgeschlossen — Radio bereit === | ||
| Repeater ID: ... | Repeater ID: ... | ||
| - | ``` | + | </ |
| - | Drei `[E]`-Zeilen (Wire.setPins, | + | Drei '' |
| - | `main.cpp` die Reihenfolge | + | |
| - | --- | + | ===== Pin-Referenz ===== |
| - | ## Pin-Referenz | + | ==== Direkte ESP32-C6 GPIOs ==== |
| - | ### Direkte ESP32-C6 GPIOs | + | ^ Funktion |
| - | + | ||
| - | | Funktion | + | |
| - | |---|---| | + | |
| | 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 | + | ^ Funktion |
| - | |---|---| | + | |
| | 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 | + | ^ 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 | + | |
| - | ``` | + | < |
| target.cpp | target.cpp | ||
| └─ radio_init() | └─ radio_init() | ||
| Zeile 552: | Zeile 400: | ||
| ├─ onRfRx() | ├─ onRfRx() | ||
| └─ onRfIdle() | └─ onRfIdle() | ||
| - | ``` | + | </ |
| - | --- | + | ===== Verzeichnisstruktur ===== |
| - | ## Offene Punkte | + | < |
| + | 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) | ||
| - | | # | Thema | Status | | + | src/ |
| - | |---|---|---| | + | └── DisplayDriver.h |
| - | | 1 | `main.cpp`: `board.begin()`/`display.begin()` vor `radio_init()` entfernen | Workaround aktiv (Guard) | | + | |
| + | src/ | ||
| + | ├── RfSwitchCallback.h | ||
| + | └── CustomSX1262Wrapper.h | ||
| + | </ | ||
| + | |||
| + | ===== Offene Punkte ===== | ||
| + | |||
| + | ^ # ^ Thema ^ Status ^ | ||
| + | | 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 | | ||
| 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 |