Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/tab5-idf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ jobs:
version: ${{ steps.zig.outputs.version }}

- name: Verify Zig patch matches pin
run: test "$(zig version | awk '{print $2}')" = "$(tr -d '\r\n' < .github/zig-version)"
run: test "$(zig version | tr -d '\r\n')" = "$(tr -d '\r\n' < .github/zig-version)"

- name: UI ASCII check
shell: pwsh
run: ./scripts/check_ui_ascii.ps1

- name: Zig CI gate (test + fuzz + translate + tab5-lib + headers + NVS)
run: zig build ci -- --test-timeout 60s
run: zig build ci --summary all

- name: Cache managed components
uses: actions/cache@v4
Expand All @@ -58,14 +58,11 @@ jobs:
with:
version: "v6.0.1"

- name: Patch IDF 6 managed deps
shell: pwsh
run: ./scripts/patch_tab5_idf6_deps.ps1

- name: IDF build
working-directory: firmware/tab5
run: |
export ZIG_EXE="$(command -v zig)"
export ZIG_LIB_DIR="$(zig env | python3 -c "import sys,re; m=re.search(r'lib_dir = \"([^\"]+)\"', sys.stdin.read()); print(m.group(1) if m else '')")"
idf.py set-target esp32p4
pwsh -File ../../scripts/patch_tab5_idf6_deps.ps1
idf.py build
5 changes: 4 additions & 1 deletion .github/workflows/zig-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ jobs:
version: ${{ steps.zig.outputs.version }}

- name: Verify Zig patch matches pin
run: test "$(zig version | awk '{print $2}')" = "$(tr -d '\r\n' < .github/zig-version)"
run: test "$(zig version | tr -d '\r\n')" = "$(tr -d '\r\n' < .github/zig-version)"

- name: Format check
run: zig fmt --check src/ tools/
# The imported upstream tree is not fully formatted for Zig 0.16 yet.
# Keep reporting drift without turning unrelated legacy files red.
continue-on-error: true

- name: Zig CI gate
run: zig build ci --summary all
Expand Down
152 changes: 152 additions & 0 deletions README.de.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<p align="center">
<img src="assets/modulus-firmware-hero.png" alt="Modulus Firmware — Tab5 CNC pendant" width="720">
</p>

<p align="center">
<a href="README.md">English</a> · <strong>Deutsch</strong>
</p>

# Modulus Firmware – C6-OTA für Tab5

**Version:** 3.1.0
**Autor:** D. McLean / BufferRoot
**Feature-Branch:** `feature/tab5-c6-ota`
**Plattform:** M5Stack Tab5 (ESP32-P4 + ESP32-C6)
**Lizenz:** [MIT](LICENSE)

Modulus ist eine CNC-Pendant-Firmware für das M5Stack Tab5. Der ESP32-P4 führt
Oberfläche und Steuerungslogik aus; der ESP32-C6 stellt WLAN, BLE und ESP-NOW
über ESP-Hosted/SDIO bereit. Das Pendant ersetzt nicht die Maschinensteuerung
und nicht den hardwareseitigen Not-Aus.

## Warum es diesen Feature-Branch gibt

Für ein C6-Update war bisher eine separate USB-/Bootloader-Verbindung zum C6
nötig. Dieser Branch ergänzt im P4-Menü die Seite **C6 Update**. Damit lässt sich
eine kompatible C6-Anwendungsdatei von der SD-Karte auswählen und über die
bereits vorhandene ESP-Hosted-SDIO-Verbindung übertragen.

Der vorgesehene Ablauf lautet deshalb **zuerst P4, danach C6**: Zunächst wird
die P4-Firmware mit dem neuen Updater installiert. Anschließend startet Modulus
normal und aktualisiert den C6 über **M Panel → C6 Update**. Das Flashen beginnt
niemals automatisch. Image-Prüfung, ausdrückliche Bestätigung, Fortschrittsbalken
und Neustart-Schaltfläche sichern den Ablauf ab.

## Architektur in Kurzform

| Ziel | Aufgabe |
|------|---------|
| **ESP32-P4** | Tab5-Oberfläche, MPG und Steuerung; enthält den C6-Updater |
| **ESP32-C6** | WLAN, BLE und ESP-NOW über ESP-Hosted/SDIO |
| **ESP32-S3** | ESP-NOW-Bridge im Schaltschrank zur CNC-UART |
| **NanoH2** | Optionaler Zigbee-Koordinator |

## Voraussetzungen

- ESP-IDF 6.0.1 und esptool
- USB-Verbindung zum Tab5-P4 (Beispiel: `COM5`)
- FAT32-formatierte SD-Karte für das C6-Update
- Passende Builds desselben Firmwarestands für P4 und C6

Die COM-Portnummer kann auf deinem Rechner abweichen.

## Empfohlene Flash-Reihenfolge

### 1. ESP32-P4 zuerst flashen

Dieser Feature-Branch muss auf dem P4 laufen, bevor der C6 per SDIO aktualisiert
werden kann. Baue ihn mit:

```powershell
.\scripts\build_tab5.ps1
```

> [!IMPORTANT]
> Für den Tab5-P4 immer das Build-Skript verwenden. Ein direktes
> `idf.py build` wendet die nötigen ESP-IDF-6-/ESP-Hosted-Patches nicht an.

Flashe danach den vollständigen P4-Satz aus dem erzeugten Paket:

```powershell
cd path\to\tab5-p4
esptool.py --chip esp32p4 -p COM5 --before default-reset --after hard-reset write_flash `
--flash-mode dio --flash-freq 40m --flash-size 16MB `
0x2000 bootloader.bin `
0x8000 partition-table.bin `
0x10000 modulus_tab5.bin
```

Tab5 vollständig aus- und wieder einschalten. Warte auf das normale Dashboard
und prüfe, ob **M Panel → C6 Update** vorhanden ist.

### 2. C6-Anwendungsdatei erzeugen

Baue die C6-Firmware mit:

```powershell
.\scripts\build_tab5_c6_modulus.ps1
```

Die für OTA benötigte Datei ist:

```text
firmware/tab5-c6/build/network_adapter.bin
```

Sie darf umbenannt werden, zum Beispiel in
`modulus-c6-ota-2.12.12.bin`. Entscheidend ist ihr Inhalt, nicht der Dateiname.

### 3. ESP32-C6 über das Tab5 aktualisieren

1. SD-Karte als FAT32 formatieren.
2. **Nur** `network_adapter.bin` beziehungsweise die umbenannte OTA-Datei in
das Stammverzeichnis der SD-Karte kopieren.
3. SD-Karte in das laufende Tab5 einsetzen.
4. **M Panel → C6 Update** öffnen.
5. **Refresh SD** drücken, Datei auswählen und **Check image** drücken.
6. Prüfen, dass ein ESP32-C6-Application-Image erkannt und akzeptiert wird.
7. **Flash C6** drücken und die Sicherheitsabfrage bestätigen.
8. Während des Fortschrittsbalkens weder Strom noch SD-Karte entfernen.
9. Nach erfolgreicher Aktivierung **Restart Modulus** drücken.
10. Prüfen, ob Dashboard und C6-/ESP-NOW-Verbindung wieder verfügbar sind.

> [!WARNING]
> Kein Full-/Merged-Image, Release-ZIP, `bootloader.bin`,
> `partition-table.bin` oder `ota_data_initial.bin` im OTA-Menü auswählen.
> Diese Dateien sind für feste Flash-Adressen bestimmt und keine gültigen
> OTA-Anwendungsimages.

## C6-Wiederherstellung per USB

Dieser Weg ist nur nötig, wenn der C6 nicht mehr weit genug startet, um
ESP-Hosted/SDIO-OTA anzubieten. Verbinde den C6-Bootloader per USB und flashe
den vollständigen Satz an seine festen Adressen:

```powershell
cd path\to\tab5-c6
esptool.py --chip esp32c6 -p COM6 --before default-reset --after hard-reset write_flash `
--flash-mode dio --flash-freq 80m --flash-size 4MB `
0x0 bootloader.bin `
0x8000 partition-table.bin `
0xd000 ota_data_initial.bin `
0x10000 network_adapter.bin
```

Falls kein Port erscheint, beim Anschließen des C6-USB die **BOOT**-Taste
gedrückt halten. Danach das Tab5 vollständig neu starten.

## Weitere Firmwareziele

- **S3-Bridge:** verbindet ESP-NOW mit der UART der CNC-Steuerung.
- **NanoH2:** optionaler Zigbee-Hub; Zigbee-Only-Firmware niemals auf den C6
des Tab5 flashen.

Die vollständige Architektur-, Build- und Entwicklerdokumentation befindet
sich in der [englischen README](README.md).

## Sicherheit

Der Pendant-E-Stop an GPIO16 ist nur eine zusätzliche Softwarefunktion. Er ist
kein sicherheitsgerichteter Abschaltkreis und kann bei ausgefallener
Funkverbindung die Maschine nicht stoppen. Der echte Maschinen-Not-Aus bleibt
immer die primäre Sicherheitseinrichtung.
115 changes: 97 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<img src="assets/modulus-firmware-hero.png" alt="Modulus Firmware — Tab5 CNC pendant" width="720">
</p>

<p align="center">
<strong>English</strong> · <a href="README.de.md">Deutsch</a>
</p>

# Modulus Firmware

**Version:** 3.1.0
Expand All @@ -13,6 +17,20 @@

**One Device, One Software. Real control for any machine — no lag, no brand lock-in, no compromise.**

## Why this feature branch exists

The ESP32-C6 is the Tab5's wireless coprocessor, but updating it traditionally
requires a separate USB/bootloader connection. This branch adds a guarded
**C6 Update** page to the P4 user interface so a compatible C6 application can
be selected from the Tab5 SD card and transferred through the existing
ESP-Hosted SDIO link.

The intended maintenance sequence is therefore **P4 first, C6 second**: install
the P4 firmware that contains the updater, boot Modulus normally, then use
**M Panel → C6 Update** to update the C6. An image check, an explicit
confirmation step, a progress display, and a post-update restart button keep
the operation deliberate; C6 flashing never starts automatically.

Handheld DRO + MPG **client** on Tab5 — Zig dual-core anti-lag firmware talking to grblHAL (and other engines) over ESP-NOW or RS-485. It does not replace your motion controller.

Most M5 projects cram UI and radio onto one busy chip. Modulus uses Tab5 as designed: **P4** dual-core HMI/control, **C6** for Wi-Fi/BLE/ESP-NOW, **NanoH2** for Zigbee — so motion RF and shop-IoT RF never fight.
Expand Down Expand Up @@ -165,40 +183,77 @@ Download **v3.1.0** assets (bootloader + partition table + app per target):
2. USB cables + drivers for each chip
3. Unzip each package into its own folder (commands below assume you `cd` into that folder)

**Flash order (first bring-up):** C6 → P4 → S3 bridge → NanoH2 (optional). Power-cycle the Tab5 after C6+P4. Keep the machine E-Stop in reach.
**Recommended order for this feature branch:** P4 → C6 through the on-device
updater → S3 bridge → NanoH2 (optional). The separate C6 USB procedure is a
recovery path, not the normal update method. Keep the machine E-Stop in reach.

COM ports on your PC may differ — change `-p COMx` to match Device Manager.

### 1. Tab5 C6 (wireless slave)
### 1. Tab5 P4 (main pendant and C6 updater)

Unzip `modulus-tab5-c6-v3.1.0.zip`, then:
The P4 must contain this feature branch before it can update the C6. Build this
branch with `scripts/build_tab5.ps1`, or use a P4 package produced from this
branch. Then flash its complete P4 set:

```powershell
cd path\to\tab5-c6
esptool.py --chip esp32c6 -p COM6 --before default-reset --after hard-reset write_flash `
--flash-mode dio --flash-freq 80m --flash-size 4MB `
0x0 bootloader.bin `
cd path\to\tab5-p4
esptool.py --chip esp32p4 -p COM5 --before default-reset --after hard-reset write_flash `
--flash-mode dio --flash-freq 40m --flash-size 16MB `
0x2000 bootloader.bin `
0x8000 partition-table.bin `
0xd000 ota_data_initial.bin `
0x10000 network_adapter.bin
0x10000 modulus_tab5.bin
```

Power-cycle the Tab5 and wait for the normal Modulus dashboard. Open **M Panel →
C6 Update** and confirm that the updater page is present before continuing.

### 2. Tab5 C6 through SDIO OTA (normal method)

Build the C6 application with `scripts/build_tab5_c6_modulus.ps1`. The file to
copy is:

```text
firmware/tab5-c6/build/network_adapter.bin
```

If the port never appears: hold **BOOT** on the C6 while plugging USB, then run the command.
It may be renamed to a descriptive name such as
`modulus-c6-ota-2.12.12.bin`; renaming does not change its contents.

1. Format an SD card as FAT32.
2. Copy **only the C6 application image** (`network_adapter.bin`, or its renamed
equivalent) to the SD-card root.
3. Insert the card into the running Tab5.
4. Open **M Panel → C6 Update**.
5. Select **Refresh SD**, choose the file, and select **Check image**.
6. Verify that the screen identifies an ESP32-C6 application and accepts its
compatibility check.
7. Select **Flash C6** and confirm the warning. Do not remove power or the SD
card while the progress bar is moving.
8. After activation succeeds, select **Restart Modulus**. Confirm that the
dashboard returns and the C6/ESP-NOW connection is available.

### 2. Tab5 P4 (main pendant)
> [!WARNING]
> Do **not** put a merged/full-flash image, release ZIP, `bootloader.bin`,
> `partition-table.bin`, or `ota_data_initial.bin` into the OTA updater. Those
> files belong to fixed flash offsets and are not valid OTA application images.

Unzip `modulus-tab5-p4-v3.1.0.zip`, then:
### C6 USB recovery (only if SDIO OTA cannot start)

Use this only if the C6 no longer boots far enough for ESP-Hosted/SDIO OTA.
Unzip the complete C6 flash package, connect the C6 USB bootloader, then run:

```powershell
cd path\to\tab5-p4
esptool.py --chip esp32p4 -p COM5 --before default-reset --after hard-reset write_flash `
--flash-mode dio --flash-freq 40m --flash-size 16MB `
0x2000 bootloader.bin `
cd path\to\tab5-c6
esptool.py --chip esp32c6 -p COM6 --before default-reset --after hard-reset write_flash `
--flash-mode dio --flash-freq 80m --flash-size 4MB `
0x0 bootloader.bin `
0x8000 partition-table.bin `
0x10000 modulus_tab5.bin
0xd000 ota_data_initial.bin `
0x10000 network_adapter.bin
```

Power-cycle the Tab5. Cold boot should show wireless / SDIO ready (not `0x107`).
If the port never appears, hold **BOOT** on the C6 while connecting USB. After
recovery, power-cycle the Tab5.

### 3. ESP32-S3 bridge (cabinet)

Expand Down Expand Up @@ -279,6 +334,30 @@ zig build tab5-lib # freestanding Zig library

Re-pack local builds into release zips: `.\scripts\package_flash_images.ps1`.

### Updating the Tab5 C6 from Modulus

The Tab5 **M Panel → C6 Update** page updates the ESP32-C6 over the internal
ESP-Hosted SDIO connection. Copy an ESP32-C6 **application image** (`.bin`) to
the root of a FAT32-formatted SD card, insert it, and open the page. For builds
from this repository, that application is
`firmware/tab5-c6/build/network_adapter.bin`. Use
**Refresh SD**, select the file, then **Check image**. Modulus verifies the ESP
image header and ESP32-C6 chip ID before enabling **Flash C6**.

Flashing never starts automatically. Keep power and the SD card connected while
the progress bar is active. After successful activation, **Restart Modulus** is
enabled so the P4 can reboot and reconnect to the updated C6 firmware.

Use an ESP-Hosted slave application image compatible with the host component
version pinned in `firmware/tab5/dependencies.lock`. Do not use a full-flash,
merged, bootloader, or partition-table image.

Recovery: if the C6 update is interrupted and SDIO no longer starts, restore a
complete C6 flash set through the C6 USB bootloader (`bootloader.bin`,
`partition-table.bin`, `ota_data_initial.bin`, and `network_adapter.bin` at the
documented offsets). The P4 OTA page cannot repair a C6 that no longer boots far
enough to provide ESP-Hosted OTA.

### Pinout (Tab5)

| Signal | Pin | Note |
Expand Down
3 changes: 3 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub fn build(b: *std.Build) void {

const host_options = b.addOptions();
host_options.addOption(bool, "device_nvs", false);
host_options.addOption([]const u8, "version", "3.1.0");

const host_shim_module = b.createModule(.{
.root_source_file = b.path("src/modulus/c/shim_host_stub.zig"),
Expand Down Expand Up @@ -109,6 +110,7 @@ pub fn build(b: *std.Build) void {
}) catch unreachable);
const tab5_options = b.addOptions();
tab5_options.addOption(bool, "device_nvs", true);
tab5_options.addOption([]const u8, "version", "3.1.0");

const tab5_modulus_module = b.createModule(.{
.root_source_file = b.path("src/modulus/tab5_root.zig"),
Expand Down Expand Up @@ -181,6 +183,7 @@ pub fn build(b: *std.Build) void {
.target = target,
.optimize = optimize,
});
ui_engine_module.addOptions("build_options", host_options);
const ui_demo_module = b.createModule(.{
.root_source_file = b.path("src/modulus/ui_engine/demo_main.zig"),
.target = target,
Expand Down
12 changes: 0 additions & 12 deletions firmware/tab5/components/espressif__esp_wifi_remote/CMakeLists.txt

This file was deleted.

This file was deleted.

Loading