Webseiten-Werkzeuge


weitere_hardware:nesso_n1_en

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
weitere_hardware:nesso_n1_en [18.04.2026 19:25] – angelegt mellinuxweitere_hardware:nesso_n1_en [21.04.2026 14:58] (aktuell) mellinux
Zeile 1: Zeile 1:
 **NOTE:** Currently in the testing phase! **NOTE:** Currently in the testing phase!
 +
 +**NOTE:** team-nessoN1-meshcore is taking a break for health reasons. If you’d like, you can contact him via email at --- //[[@team-nessoN1-meshcore@posteo.de@|team-nessoN1-meshcore]]// for details on handing over the project.
  
 ====== MeshCore — Arduino Nesso N1 (ESP32-C6) ====== ====== MeshCore — Arduino Nesso N1 (ESP32-C6) ======
  
-**Author:** team-nessoN1-meshcore — team-nessoN1-meshcore@posteo.de  +**Author:** team-nessoN1-meshcore — team-nessoN1-meshcore@posteo.de\\
 **Date:** April 2026 **Date:** April 2026
  
-This directory contains the Hardware Abstraction Layer (HAL) for the +This page describes 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″ touchscreen in a compact, battery-powered enclosure.
-**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 ===== ===== Hardware Overview =====
  
-Component Details +Component Details ^
-|---|---|+
 | MCU | Espressif ESP32-C6 (RISC-V, 160 MHz) | | MCU | Espressif ESP32-C6 (RISC-V, 160 MHz) |
 | Flash | 16 MB | | Flash | 16 MB |
 | RAM | 512 KB | | RAM | 512 KB |
-| LoRa | Semtech SX1262, 850\u2013960 MHz, up to +22 dBm | +| LoRa | Semtech SX1262, 850–960 MHz, up to +22 dBm | 
-| RF Switch / LNA | External, controlled via I\u00b2C GPIO expander | +| RF Switch / LNA | External, controlled via I²C GPIO expander | 
-I\u00b2C GPIO Expander | PI4IOE5V6408 (2\u00d7 instances, addresses 0x43 and 0x44) | +I²C GPIO Expander | PI4IOE5V6408 (2× instances, addresses 0x43 and 0x44) | 
-| Display | ST7789P3, 1.14\u2033240\u00d7135 px, touchscreen (FT6336U) |+| Display | ST7789P3, 1.14240×135 px, touchscreen (FT6336U) |
 | IMU | BMI270 (6-axis) | | IMU | BMI270 (6-axis) |
 | Battery | 250 mAh LiPo, USB-C charging | | Battery | 250 mAh LiPo, USB-C charging |
 | Connectivity | Wi-Fi 6, BT 5.3, Thread/Zigbee (802.15.4), LoRa | | Connectivity | Wi-Fi 6, BT 5.3, Thread/Zigbee (802.15.4), LoRa |
- 
----- 
  
 ===== Key Porting Decisions ===== ===== Key Porting Decisions =====
  
-==== SX1262 Reset and RF Switch via I\u00b2C Expander ====+==== SX1262 Reset and RF Switch via I²C Expander ====
  
-Unlike most other MeshCore targets, the LoRa control lines on the Nesso N1 +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²C GPIO expander** (address 0x43):
-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 +Signal Expander Pin Function ^
-|---|---|---|+
 | SX_NRST | P7 | SX1262 hardware reset | | SX_NRST | P7 | SX1262 hardware reset |
 | SX_ANT_SW | P6 | Antenna RF switch | | SX_ANT_SW | P6 | Antenna RF switch |
Zeile 47: Zeile 37:
 | KEY2 | P1 | Button B (input) | | KEY2 | P1 | Button B (input) |
  
-RadioLib's ''setRfSwitchTable()'' (direct GPIO only) cannot be used. +RadioLib's ''setRfSwitchTable()'' (direct GPIO only) cannot be used. ''NessoN1Board'' implements the ''RfSwitchCallback'' interface instead, which ''CustomSX1262Wrapper'' calls on every TX/RX transition.
-''NessoN1Board'' implements the ''RfSwitchCallback'' interface instead.+
  
 ==== Shared SPI Bus (SX1262 + ST7789) ==== ==== Shared SPI Bus (SX1262 + ST7789) ====
  
-Signal GPIO +Signal GPIO ^
-|---|---|+
 | MOSI | G21 | | MOSI | G21 |
 | MISO | G22 | | MISO | G22 |
Zeile 61: Zeile 49:
 | LCD DC | G16 | | LCD DC | G16 |
  
-''lora_spi.begin()'' is called **without the NSS argument** to avoid registering +''lora_spi.begin()'' is called **without the NSS argument** to avoid registering a duplicate APB hardware CS-callback (see Fix 4).
-a duplicate APB hardware CS-callback (see Fix 4).+
  
 ==== FSPI instead of default SPI ==== ==== FSPI instead of default SPI ====
Zeile 70: Zeile 57:
 ==== USB-CDC and BLE are mutually exclusive ==== ==== USB-CDC and BLE are mutually exclusive ====
  
-Firmware ''ARDUINO_USB_CDC_ON_BOOT'' Debug output +Firmware ''ARDUINO_USB_CDC_ON_BOOT'' Debug output ^
-|---|---|---|+
 | Repeater | ''1'' | USB-Serial active | | Repeater | ''1'' | USB-Serial active |
 | Companion BLE | ''0'' | no USB-Serial | | Companion BLE | ''0'' | no USB-Serial |
 | Companion WiFi | ''0'' | no USB-Serial | | Companion WiFi | ''0'' | no USB-Serial |
- 
----- 
  
 ===== Display Architecture ===== ===== Display Architecture =====
Zeile 82: Zeile 66:
 ==== Resolution and UITask Layout Verification ==== ==== Resolution and UITask Layout Verification ====
  
-The ST7789P3 has **240\u00d7135 pixels** (Landscape, ''setRotation(1)''). +The ST7789P3 has **240×135 pixels** (Landscape, ''setRotation(1)''). All UITask default layouts fit without overflow:
-All UITask default layouts fit without overflow:+
  
-Element Width x (centred) Right edge Fits +Element Width x (centred) Right edge Fits ^ 
-|---|---|---|---|---| +| MeshCore logo (64×36 px XBM) | 64 px | 88 | 152 | ✅ 
-| MeshCore logo (64\u00d736 px XBM) | 64 px | 88 | 152 | \u2705 +| Version line (setTextSize 2) | 156 px | 42 | 198 | ✅ 
-| Version line (setTextSize 2) | 156 px | 42 | 198 | \u2705 +| Node name "NessoN1 Repeater" | 192 px | 24 | 216 | ✅ 
-| Node name \"NessoN1 Repeater\" | 192 px | 24 | 216 | \u2705 +| Frequency line "869.6 SF8 BW62.5" | 204 px | 18 | 222 | ✅ |
-| 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.+All four elements together occupy ~100 px of 135 px height — no vertical overflow.
  
 ==== Mandatory Initialisation Order ==== ==== Mandatory Initialisation Order ====
Zeile 100: Zeile 82:
   1. board.begin()           SPI2, Wire, expanders, loraReset()   1. board.begin()           SPI2, Wire, expanders, loraReset()
   2. board.onRfRx()          RF switch active for calibration   2. board.onRfRx()          RF switch active for calibration
-  3. radio.std_init(nullptr) RadioLib \u2014 nullptr: no second spi->begin() +  3. radio.std_init(nullptr) RadioLib — nullptr: no second spi->begin() 
-  4. display.begin()         M5GFX lazy construction \u2014 must follow std_init!+  4. display.begin()         M5GFX lazy construction — must follow std_init!
 </code> </code>
- 
----- 
  
 ===== Known Issues and Their Fixes ===== ===== Known Issues and Their Fixes =====
  
-==== Fix 1 \u2014 Display Goes Black After a Few Seconds ====+==== Fix 1 — Display Goes Black After a Few Seconds ====
  
-**Date:** April 2026  +**Date:** April 2026\\
 **Affected file:** ''platformio.ini'', ''[env:NessoN1_repeater_display]'' **Affected file:** ''platformio.ini'', ''[env:NessoN1_repeater_display]''
  
-**Symptom:** Display shows boot screen (~4 s) and home screen (~10 s), +**Symptom:** Display shows boot screen (~4 s) and home screen (~10 s), then stays permanently black. Button presses have no effect.
-then stays permanently black. Button presses have no effect.+
  
-**Root cause:** +**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'' — a direct GPIO pin. On the Nesso N1, KEY1 and KEY2 are connected **via I²C expander 0** (address 0x43, pins P0/P1) and are **not direct GPIOs**. UITask therefore has **no wakeup path** — once the timeout fires, the display stays off permanently.
-''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:** **Fix:**
-<code>ini +<code ini> 
-; platformio.ini \u2014 [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>
-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''+Both flags set to 0 disable the automatic timeout entirely. ''DISPLAY_TIMEOUT'' is also set because different UITask versions use different macro names.
-Button wakeup is already integrated via ''nesso_ui_tick()'' (see Fix 7).+
  
-----+> **Note for battery operation:** Use e.g. ''-D SCREEN_TIMEOUT=30000''. Button wakeup is already integrated via ''nesso_ui_tick()'' (see Fix 7). 
 + 
 +==== Fix 2 — Display in Portrait Mode (135×240 Instead of 240×135) ==== 
 + 
 +**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×240. 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 — 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 — ''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'' — **before** ''radio_init()''. On the Nesso N1, ''board.begin()'' calls ''loraReset()'': SX1262 boots, BUSY goes HIGH. Then ''radio_init()'' calls ''board.begin()'' again — second ''loraReset()'' — SX1262 boots again, BUSY HIGH again — ''radio.std_init()'' hits the chip during boot — ''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 — 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 — 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 calls ''spi_bus_initialize(SPI2_HOST)'' and ''Wire.begin()'' internally. When ''radio_init()'' → ''board.begin()'' → ''lora_spi.begin()'' runs afterwards, the ESP-IDF SPI driver tries to register the same APB callback again → ''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 — 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 — already LOW by the time ''digitalRead()'' ran after 20 ms. No hardware fault, just overly conservative timing. 
 + 
 +**Fix:** 
 +<code cpp> 
 +_exp0.digitalWrite(NESSO_EXP0_SX_NRST, true); 
 +delay(2);   // was: delay(20) — 2 ms sufficient, BUSY pulse on Nesso N1 < 1 ms 
 +</code> 
 + 
 +Updated log message: 
 +<code> 
 +[loraReset] BUSY 2 ms after NRST HIGH: LOW (pulse complete — normal on Nesso N1) 
 +</code>
  
-==== Fix 7 \u2014 Buttons Non-Functional in Repeater Display Mode ====+==== Fix 7 — Buttons Non-Functional in Repeater Display Mode ====
  
-**Date:** April 2026   +**Date:** April 2026\\ 
-**Affected files:** ''simple_repeater/main.cpp'', ''simple_repeater/UITask.h'', +**Affected files:** ''simple_repeater/main.cpp'', ''simple_repeater/UITask.h'', ''simple_repeater/UITask.cpp'', ''simple_repeater/MyMesh.h''
-''simple_repeater/UITask.cpp'', ''simple_repeater/MyMesh.h''+
  
-**Symptom:** KEY1 and KEY2 appear to do nothing. Serial log shows +**Symptom:** KEY1 and KEY2 appear to do nothing. Serial log shows ''[ui] KEY1 erkannt'' — the keypress is detected, but the display does not change.
-''[ui] KEY1 erkannt'' \u2014 the keypress is detected, but the display does not change.+
  
-**Root cause:**   +**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.
-''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:**+**Fix — three levels:**
  
-1. **New interface ''UIActions.h''** \u2014 decouples UITask from MyMesh: +**1. New interface ''UIActions.h''** — decouples UITask from MyMesh: 
-<code>cpp+<code cpp>
 class UIActions { class UIActions {
 public: public:
Zeile 165: Zeile 201:
 </code> </code>
  
-2. **''MyMesh'' implements ''UIActions''** \u2014 in ''MyMesh.h'': +**2. ''MyMesh'' implements ''UIActions''** — in ''MyMesh.h'': 
-<code>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); }
Zeile 173: Zeile 209:
 </code> </code>
  
-3. **''UITask'' gains 3 screens + navigation**, connected via ''setActions()'':+**3. ''UITask'' gains 3 screens + navigation**, connected via ''setActions()'':
 <code> <code>
-SCREEN_HOME       \u2014 node name, frequency, SF/BW/CR +SCREEN_HOME       — node name, frequency, SF/BW/CR 
-SCREEN_NEIGHBOURS \u2014 neighbour list (hex ID, age, SNR) +SCREEN_NEIGHBOURS — neighbour list (hex ID, age, SNR) 
-SCREEN_POLL_SENT  \u2014 "Poll sent" feedback for 2 seconds+SCREEN_POLL_SENT  — "Poll sent" feedback for 2 seconds
 </code> </code>
  
-4. **''main.cpp'' evaluates ''btn''** (previously commented out): +**4. ''main.cpp'' evaluates ''btn''** (previously commented out): 
-<code>cpp+<code cpp>
 if (btn == -1) ui_task.nextScreen();   // KEY1: forward / poll if (btn == -1) ui_task.nextScreen();   // KEY1: forward / poll
 if (btn == -2) ui_task.prevScreen();   // KEY2: back if (btn == -2) ui_task.prevScreen();   // KEY2: back
Zeile 188: Zeile 224:
 **Button behaviour after patch:** **Button behaviour after patch:**
  
-Button Display OFF Display ON (Home) Display ON (Neighbours) +Button Display OFF Display ON (Home) Display ON (Neighbours) ^ 
-|--------|-------------|-------------------|-------------------------| +| KEY1 | Wakeup | → Neighbours screen | → Send poll | 
-| KEY1 | Wakeup | \u2192 Neighbours screen | \u2192 Send poll | +| KEY2 | Wakeup | stays Home | → Home screen |
-| KEY2 | Wakeup | stays Home | \u2192 Home screen |+
  
-After sending a poll, the screen shows "Poll sent" for 2 seconds, +After sending a poll, the screen shows "Poll sent" for 2 seconds, then automatically returns to the refreshed neighbour list.
-then automatically returns to the (refreshedneighbour list.+
  
-----+==== Fix 8 — Buttons Not Recognised: Wire Conflict Between M5GFX and Expander Driver ====
  
-==== Fix 8 \u2014 Buttons Not Recognised: Wire Conflict Between M5GFX and Expander Driver ==== +**Date:** April 2026\\
- +
-**Date:** April 2026  +
 **Affected files:** ''NessoDisplayDriver.cpp'', ''NessoDisplayDriver.h'', ''platformio.ini'' **Affected files:** ''NessoDisplayDriver.cpp'', ''NessoDisplayDriver.h'', ''platformio.ini''
  
-**Symptom:** KEY1 and KEY2 are not reliably registered despite correct edge detection +**Symptom:** KEY1 and KEY2 are not reliably registered despite correct edge detection and boot lock. Serial log shows:
-and boot lock. Serial log shows:+
 <code> <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.
Zeile 210: Zeile 241:
 </code> </code>
  
-**Root cause:**   +**Root cause:** M5GFX initialises the I²C 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 [[https://forum.arduino.cc/t/cant-use-buttons-and-graphics-at-the-same-time/1415099|Arduino Forum (November 2025)]].
-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:**   +**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 — no more Wire conflict.
-''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 +<code cpp> 
-// NessoDisplayDriver.cpp \u2014 begin():+// NessoDisplayDriver.cpp — begin():
 M5.begin(cfg);              // instead of: _gfx->begin() M5.begin(cfg);              // instead of: _gfx->begin()
 _gfx = &M5.Display;        // instead of: static M5GFX gfx_instance _gfx = &M5.Display;        // instead of: static M5GFX gfx_instance
Zeile 233: Zeile 256:
 </code> </code>
  
-<code>ini +<code ini> 
-; platformio.ini \u2014 nesso_n1_base:+; platformio.ini — nesso_n1_base:
 lib_deps = lib_deps =
   m5stack/M5GFX   m5stack/M5GFX
Zeile 240: Zeile 263:
 </code> </code>
  
-Removed: ''_prevBtnState'', ''_btnReadyAt'', ''_lastBtnCheck'' \u2014 manual debounce +Removed: ''_prevBtnState'', ''_btnReadyAt'', ''_lastBtnCheck'' — manual debounce is no longer needed; M5Unified handles edge detection internally.
-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 ===== ===== Quick Start =====
Zeile 365: Zeile 269:
 ==== 1. Prerequisites ==== ==== 1. Prerequisites ====
  
-  * [PlatformIO](https://platformio.org/(CLI or VS Code extension)+  * [[https://platformio.org/|PlatformIO]] (CLI or VS Code extension)
   * Arduino Nesso N1 connected via USB-C   * Arduino Nesso N1 connected via USB-C
  
 ==== 2. Set up credentials ==== ==== 2. Set up credentials ====
  
-<code>bash+<code bash>
 cd <project root> cd <project root>
 cp variants/arduino_nesso_n1/credentials.ini.example credentials.ini cp variants/arduino_nesso_n1/credentials.ini.example credentials.ini
Zeile 376: Zeile 280:
  
 Edit ''credentials.ini'': Edit ''credentials.ini'':
-<code>ini+ 
 +<code ini>
 [credentials] [credentials]
 build_flags_repeater = build_flags_repeater =
   -D ADMIN_PASSWORD='"your_password"'   -D ADMIN_PASSWORD='"your_password"'
 +
 build_flags_wifi = build_flags_wifi =
   -D WIFI_SSID='"your_network"'   -D WIFI_SSID='"your_network"'
   -D WIFI_PWD='"your_password"'   -D WIFI_PWD='"your_password"'
 </code> </code>
 +
 +> ''credentials.ini'' is listed in ''.gitignore'' and will **not** be committed to the repository.
  
 ==== 3. Build and flash ==== ==== 3. Build and flash ====
  
-<code>bash +**Repeater with display** (recommended starting point): 
-pio run -e NessoN1_repeater_display --target upload && pio device monitor -b 115200+<code bash> 
 +pio run -e NessoN1_repeater_display --target upload 
 +pio device monitor -b 115200 
 +</code> 
 + 
 +**Repeater without display:** 
 +<code bash> 
 +pio run -e NessoN1_repeater --target upload 
 +</code> 
 + 
 +**Companion Radio — BLE:** 
 +<code bash> 
 +pio run -e NessoN1_companion_ble --target upload 
 +</code> 
 + 
 +**Companion Radio — WiFi/TCP:** 
 +<code bash> 
 +pio run -e NessoN1_companion_wifi --target upload
 </code> </code>
  
Zeile 395: Zeile 320:
 <code> <code>
 [loraReset] BUSY 2 ms after NRST HIGH: LOW (normal on Nesso N1) [loraReset] BUSY 2 ms after NRST HIGH: LOW (normal on Nesso N1)
-[loraReset] BUSY=LOW after 0 ms \u2014 SX1262 ready+[loraReset] BUSY=LOW after 0 ms — SX1262 ready
 [display] M5GFX ready: 240 x 135 px (after setRotation(1)) [display] M5GFX ready: 240 x 135 px (after setRotation(1))
 [display] ST7789 ready, backlight on [display] ST7789 ready, backlight on
-[board] begin() already initialised \u2014 guard active+[board] begin() already initialised — guard active
 [radio] std_init OK [radio] std_init OK
-[init] === radio_init() complete \u2014 radio ready ===+[init] === radio_init() complete — radio ready ===
 </code> </code>
- 
----- 
  
 ===== Pin Reference ===== ===== Pin Reference =====
Zeile 409: Zeile 332:
 ==== Direct ESP32-C6 GPIOs ==== ==== Direct ESP32-C6 GPIOs ====
  
-Function GPIO +Function GPIO ^ 
-|---|---| +| LoRa MOSI | G21 
-| LoRa MOSI | G21 | LoRa MISO | G22 | LoRa SCK | G20 | +| LoRa MISO | G22 
-| LoRa CS (NSS) | G23 | LoRa BUSY | G19 | LoRa IRQ (DIO1) | G15 | +| LoRa SCK | G20 | 
-| LCD CS | G17 | LCD DC | G16 | I\u00b2C SDA | G10 | I\u00b2C SCL | G8 | Touch INT | G3 |+| LoRa CS (NSS) | G23 
 +| LoRa BUSY | G19 
 +| LoRa IRQ (DIO1) | G15 | 
 +| LCD CS | G17 
 +| LCD DC | G16 | 
 +| I²C SDA | G10 | 
 +| I²C SCL | G8 
 +| Touch INT | G3 |
  
-==== Via I\u00b2C Expander 0 (0x43) ====+==== Via I²C Expander 0 (0x43) ====
  
-KEY1=P0KEY2=P1LNA Enable=P5Antenna Switch=P6LoRa Reset (NRST)=P7+^ Function ^ Expander Pin ^ 
 +| Button A (KEY1) | P0 
 +| Button B (KEY2) | P1 
 +LNA Enable P5 
 +Antenna Switch P6 
 +LoRa Reset (NRST) P7 |
  
-==== Via I\u00b2C Expander 1 (0x44) ====+==== Via I²C Expander 1 (0x44) ====
  
-LCD Reset=P1LCD Backlight=P6 +^ Function ^ Expander Pin ^ 
- +LCD Reset P1 
-----+LCD Backlight P6 |
  
 ===== RF Switch Logic ===== ===== RF Switch Logic =====
  
-Mode ANT_SW LNA_EN Description +Mode ANT_SW LNA_EN Description ^
-|---|---|---|---|+
 | TX | HIGH | LOW | TX path active, LNA protected | | TX | HIGH | LOW | TX path active, LNA protected |
 | RX | HIGH | HIGH | RX path active with LNA | | RX | HIGH | HIGH | RX path active with LNA |
 | IDLE | LOW | LOW | RF path disconnected, low power | | IDLE | LOW | LOW | RF path disconnected, low power |
  
-----+===== Architecture ===== 
 + 
 +<code> 
 +target.cpp 
 +  └─ radio_init() 
 +       ├─ board.begin()               SPI2, Wire, I²C-scan, expanders, loraReset() 
 +       ├─ rtc_clock.begin(Wire) 
 +       ├─ board.onRfRx()              RF switch active for calibration 
 +       ├─ radio.std_init(nullptr)     no second spi->begin() 
 +       ├─ radio_driver.setRfSwitchCallback(&board) 
 +       └─ display.begin()             M5GFX lazy construction — after std_init 
 + 
 +NessoDisplayDriver 
 +  ├─ begin()         static M5GFX; setRotation(1) before width()/height() 
 +  └─ checkButtons()  polling KEY1/KEY2 via I²C expander 0 (50 ms debounce) 
 + 
 +NessoN1Board (RfSwitchCallback) 
 +  ├─ begin()      guard (single call) + loraReset() 
 +  ├─ onRfTx()     ANT_SW=HIGH, LNA_EN=LOW 
 +  ├─ onRfRx()     ANT_SW=HIGH, LNA_EN=HIGH 
 +  └─ onRfIdle()   ANT_SW=LOW,  LNA_EN=LOW 
 +</code> 
 + 
 +===== Directory Structure ===== 
 + 
 +<code> 
 +variants/arduino_nesso_n1/ 
 +├── NessoN1Board.h             — pin constants, PI4IOE5V6408 driver, board class 
 +├── NessoN1Board.cpp           — begin() with guard (Fix 3), loraReset() (Fix 5) 
 +├── NessoDisplayDriver.h       — DisplayDriver extension, lazy construction 
 +├── NessoDisplayDriver.cpp     — begin() with setRotation fix (Fix 2) 
 +├── target.h                   — extern declarations, nesso_check_buttons() 
 +├── target.cpp                 — radio_init() mandatory sequence, global objects 
 +├── platformio.ini             — incl. SCREEN_TIMEOUT=0 (Fix 1) 
 +├── credentials.ini.example    — template for local credentials 
 +└── main_cpp_setup_hinweis.txt — correct main.cpp structure (Fix 3/4) 
 + 
 +src/helpers/ui/ 
 +└── DisplayDriver.h            — abstract interface (pure virtuals) 
 + 
 +src/helpers/radiolib/ 
 +├── RfSwitchCallback.h         — abstract interface for external RF switch 
 +└── CustomSX1262Wrapper.h      — SX1262 wrapper with RF switch callback 
 +</code>
  
 ===== Open Items ===== ===== Open Items =====
  
-Topic Status +Topic Status ^ 
-|---|---|---| +| 1 | Remove ''board.begin()'' / ''display.begin()'' from ''main.cpp'' ''setup()'' | workaround active (guard) |
-| 1 | Remove ''board.begin()''/''display.begin()'' from ''main.cpp'' ''setup()'' | workaround active (guard) |+
 | 2 | Hook KEY1/KEY2 into UITask screen wakeup | open | | 2 | Hook KEY1/KEY2 into UITask screen wakeup | open |
-| 3 | Touch FT6336U (I\u00b2C 0x38) | open | +| 3 | Touch FT6336U (I²C 0x38) | open | 
-| 4 | IMU BMI270 (I\u00b2C 0x68) for auto-rotation | open | +| 4 | IMU BMI270 (I²C 0x68) for auto-rotation | open | 
-| 5 | Battery monitor (I\u00b2C 0x49) | open | +| 5 | Battery monitor (I²C 0x49) | open |
- +
-----+
  
 ===== License ===== ===== License =====
weitere_hardware/nesso_n1_en.1776533138.txt.gz · Zuletzt geändert: von mellinux