Skip to content

Commit d4c7e2e

Browse files
committed
Web tool flash page, CICD
1 parent aa0d48d commit d4c7e2e

15 files changed

Lines changed: 298 additions & 35 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Build env azimuth_main and deploy web-flasher/ (USB install via esp-web-tools).
2+
# Repo: Settings → Pages → Source: GitHub Actions.
3+
4+
name: Deploy web flasher (GitHub Pages)
5+
6+
on:
7+
push:
8+
branches:
9+
- main
10+
11+
permissions:
12+
contents: read
13+
pages: write
14+
id-token: write
15+
16+
concurrency:
17+
group: github-pages-flasher
18+
cancel-in-progress: true
19+
20+
jobs:
21+
build:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- uses: actions/cache@v4
27+
with:
28+
path: |
29+
~/.cache/pip
30+
~/.platformio/.cache
31+
key: pio-${{ runner.os }}-${{ hashFiles('platformio.ini') }}
32+
33+
- name: Install PlatformIO
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install "platformio>=6.1"
37+
38+
- name: Placeholder secrets for CI build
39+
run: cp include/secrets.h.example include/secrets.h
40+
41+
- name: Build firmware (azimuth_main)
42+
run: pio run -e azimuth_main
43+
44+
- name: Copy images for esp-web-tools
45+
run: |
46+
chmod +x scripts/prepare_web_flasher_firmware.sh
47+
./scripts/prepare_web_flasher_firmware.sh azimuth_main web-flasher/firmware
48+
49+
- name: Stamp manifest version (git SHA prefix)
50+
env:
51+
GITHUB_SHA: ${{ github.sha }}
52+
run: |
53+
python3 <<'PY'
54+
import json, os
55+
path = "web-flasher/manifest.json"
56+
with open(path) as f:
57+
m = json.load(f)
58+
sha = os.environ.get("GITHUB_SHA", "")
59+
if sha:
60+
m["version"] = sha[:7]
61+
with open(path, "w") as f:
62+
json.dump(m, f, indent=2)
63+
f.write("\n")
64+
PY
65+
66+
- uses: actions/upload-pages-artifact@v3
67+
with:
68+
path: web-flasher
69+
70+
deploy:
71+
needs: build
72+
runs-on: ubuntu-latest
73+
environment:
74+
name: github-pages
75+
url: ${{ steps.deployment.outputs.page_url }}
76+
steps:
77+
- id: deployment
78+
uses: actions/deploy-pages@v4

.gitlab-ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# PlatformIO: build release firmware on main (artifacts = web-flashable .bin set).
2+
# CI copies secrets.h.example → secrets.h so the build does not require a committed secrets.h.
3+
4+
variables:
5+
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
6+
AZIMUTH_PIO_ENV: azimuth_main
7+
8+
cache:
9+
key: "${CI_COMMIT_REF_SLUG}-pip"
10+
paths:
11+
- .cache/pip
12+
13+
stages:
14+
- build
15+
16+
.build_azimuth_main:
17+
stage: build
18+
image: python:3.12-slim
19+
before_script:
20+
- apt-get update -qq && apt-get install -y -qq git wget && rm -rf /var/lib/apt/lists/*
21+
- pip install --upgrade pip
22+
- pip install "platformio>=6.1"
23+
- cp include/secrets.h.example include/secrets.h
24+
script:
25+
- pio run -e "$AZIMUTH_PIO_ENV"
26+
- chmod +x scripts/prepare_web_flasher_firmware.sh
27+
- ./scripts/prepare_web_flasher_firmware.sh "$AZIMUTH_PIO_ENV" ci-artifacts/firmware
28+
artifacts:
29+
name: "azimuth-main-$CI_COMMIT_SHORT_SHA"
30+
expire_in: 30 days
31+
paths:
32+
- ci-artifacts/firmware/
33+
34+
build:azimuth_main:
35+
extends: .build_azimuth_main
36+
rules:
37+
- if: $CI_COMMIT_BRANCH == "main"

README.md

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ This repository holds firmware, PCB designs, 3D print designs, and documentation
2828
|------|:--------:|--------|
2929
| Hardware / BOM | 100% | Parts chosen ([docs/parts-list.md](docs/parts-list.md)). |
3030
| Custom PCB | ~95% | KiCad aligned with [docs/wiring.md](docs/wiring.md); **panelization** (fab-ready panel) is the remaining PCB task before ordering. |
31-
| Firmware | ~40% | SPI IMU, USB debug, **Hatire + WiFi → OpenTrack UDP**, **on-device settings** over HTTP (NVS + `secrets.h` fallback; see below). **Board I/O** (LED, button, buzzer), **battery/ADC** still ahead ([roadmap](docs/roadmap.md)). |
31+
| Firmware | ~40% | SPI IMU, **`azimuth_debug`** / **`azimuth_main`**, **Hatire + Wi‑Fi → OpenTrack UDP**, **on-device settings** (NVS portal; optional `secrets.h`). **Board I/O**, **battery/ADC** still ahead ([roadmap](docs/roadmap.md)). |
3232
| 3D enclosure | 0% | Not started. Plan: **battery-sized** shell first; optional slimmer **wired-only** enclosure if PH2 is omitted on those builds ([roadmap](docs/roadmap.md)). |
3333

3434
```
@@ -57,8 +57,8 @@ Enclosure [░░░░░░░░░░░░░░░░░░░░] 0%
5757
| Area | Status |
5858
|------|--------|
5959
| **Firmware** | PlatformIO project for **Seeed XIAO ESP32-C3** + **BNO08x** over **SPI**: fused yaw / pitch / roll from the rotation-vector report. |
60-
| **OpenTrack** | `xiao_esp32c3_hatire`: **Hatire Arduino** over USB (30-byte frames) **and** optional **WiFi → UDP** to the PC using OpenTrack’s **UDP over network** input (`double`, port 4242 by default). **HTTP settings** on port **8080** (`http://azimuth.local:8080`) for Wi‑Fi / UDP / reboot (NVS + `secrets.h` fallback). |
61-
| **Debug** | Text telemetry over USB serial (`xiao_esp32c3` build). |
60+
| **OpenTrack** | **`azimuth_main`**: **Hatire Arduino** over USB (30-byte frames) **and** optional **Wi‑Fi → UDP** (OpenTrack **UDP over network**, `double`, port 4242 by default). **HTTP settings** on **8080** (`http://azimuth.local:8080`) **NVS** (portal); optional compile‑time **`secrets.h`** when NVS is empty. |
61+
| **Debug** | **`azimuth_debug`**: yaw / pitch / roll over USB serial only (no Wi‑Fi / portal). |
6262
| **Hardware docs** | **[docs/wiring.md](docs/wiring.md)** (signals, power, GPIO map) · **[docs/parts-list.md](docs/parts-list.md)** (BOM + passives notes) · **[docs/kicad.md](docs/kicad.md)** (custom KiCad libs + collaboration). |
6363

6464
Planned work (board I/O, battery, web flashing / settings UX, enclosure, richer calibration) is tracked in **[docs/roadmap.md](docs/roadmap.md)**.
@@ -86,30 +86,39 @@ Summary SPI / control pin map (full table, battery, buttons, LED, buzzer in **[d
8686

8787
## Building and flashing
8888

89-
Requires [PlatformIO](https://platformio.org/). Default environment: **`xiao_esp32c3`** (debug text).
89+
### CI (GitLab) and browser flasher (GitHub Pages)
90+
91+
| What | Where |
92+
|------|--------|
93+
| **GitLab pipeline** | Pushes to **`main`** run **`.gitlab-ci.yml`**: builds **`azimuth_main`** using **`include/secrets.h.example`****`include/secrets.h`** in CI. **Artifacts****`ci-artifacts/firmware/`** (`bootloader.bin`, `partitions.bin`, `boot_app0.bin`, `firmware.bin`). |
94+
| **GitHub Pages USB flasher** | Workflow **`.github/workflows/github-pages-flasher.yml`** runs on **`main`**, builds the same env, runs **`scripts/prepare_web_flasher_firmware.sh`**, and deploys **`web-flasher/`** (portal-styled page using [esp-web-tools](https://github.com/esphome/esp-web-tools)). In the repo: **Settings → Pages → Build and deployment → Source: GitHub Actions**. Users need **Chrome** or **Edge** (Web Serial) and a **USB data** cable. When the installer offers **erase flash**, use it for a **factory-clean** device (clears NVS like on-device “Erase saved settings”). The page links to **`http://azimuth.local:8080/`** for the settings portal after install. |
95+
96+
Local check: `pio run -e azimuth_main` then `./scripts/prepare_web_flasher_firmware.sh` copies binaries into **`web-flasher/firmware/`** for testing.
97+
98+
Requires [PlatformIO](https://platformio.org/). **Default environment:** **`azimuth_main`** (release). Use **`azimuth_debug`** for serial-only bring-up.
9099

91100
**Debug (serial monitor, yaw/pitch/roll):**
92101

93102
```bash
94-
python3 -m platformio run -e xiao_esp32c3 -t upload
103+
python3 -m platformio run -e azimuth_debug -t upload
95104
python3 -m platformio device monitor
96105
```
97106

98107
**OpenTrack — Hatire (USB) + optional UDP (WiFi):**
99108

100109
```bash
101-
python3 -m platformio run -e xiao_esp32c3_hatire -t upload
110+
python3 -m platformio run -e azimuth_main -t upload
102111
```
103112

104-
Copy **`include/secrets.h.example`** to **`include/secrets.h`** and set **`WIFI_SSID`**, **`WIFI_PASSWORD`**, and **`OPENTRACK_UDP_HOST`** (your PC’s LAN IP). `secrets.h` is **gitignored** so credentials are not committed. If there is **no SSID in NVS and `WIFI_SSID` is empty**, or if it **tries to join a saved network and fails** (wrong password, etc.), the board starts an open provisioning network **`Azimuth-Setup`**. It runs a small **captive portal**: DNS sends lookups to the board, HTTP on **port 80** answers OS “sign in to Wi‑Fi” checks with a redirect to **`http://192.168.4.1/`** (same settings UI as on the LAN). **`azimuth.local` does not apply on this network**—mDNS is only advertised after the board joins your home Wi‑Fi. After you save home Wi‑Fi and reboot, **`Azimuth-Setup` turns off** and normal mode uses **`http://azimuth.local:8080`** (or **`http://<LAN-IP>:8080`**). UDP port **`OPENTRACK_UDP_PORT`** is set in **`platformio.ini`** (default **4242**).
113+
Most users configure Wi‑Fi and OpenTrack **only in the portal** (NVS) and leave **`include/secrets.h`** empty (copy from **`secrets.h.example`** for a valid build; file is **gitignored**). You *may* set **`WIFI_SSID`**, **`WIFI_PASSWORD`**, **`OPENTRACK_UDP_HOST`** there as compile‑time defaults when NVS has no SSID. If there is **no usable home SSID** (NVS + `secrets.h`), or **STA fails** (wrong password, AP missing), the board opens **`Azimuth-Setup`** and a **captive portal**: HTTP **port 80** redirects to **`http://192.168.4.1/`** (same UI as on the LAN). **`azimuth.local` does not apply** on that AP—mDNS starts after joining home Wi‑Fi. Normal use: **`http://azimuth.local:8080`** or **`http://<LAN-IP>:8080`**. UDP port **`OPENTRACK_UDP_PORT`** is in **`platformio.ini`** (default **4242**).
105114

106115
- **USB:** Input **Hatire Arduino**, **115200**, **DTR** on; start tracking and **recenter** after the filter settles. Do not leave a text serial monitor open on that port.
107116
- **Hatire axis mapping (important):** In the Hatire tracker settings, set **Yaw axis = Rot 0**, **Pitch axis = Rot 1**, **Roll axis = Rot 2** (some UIs say “axis 0 / 1 / 2”). That lines up with how this firmware fills the packet and keeps USB and UDP identical. OpenTrack’s *old* Hatire defaults use **0 / 2 / 1**, which swaps pitch and roll—change to **0 / 1 / 2** for the simplest setup.
108117
- **WiFi / UDP:** Input **UDP over network**, same port as in **`platformio.ini`** (`OPENTRACK_UDP_PORT`); allow the port through the PC firewall. Hatire and UDP both run from the same firmware.
109118

110119
### On-device settings (WiFi + OpenTrack)
111120

112-
With the **hatire** build:
121+
With **`azimuth_main`**:
113122

114123
- **Already on your LAN:** open **`http://<hostname>.local:8080`** (default hostname **`azimuth`**) or **`http://<device-ip>:8080`**.
115124
- **Provisioning (`Azimuth-Setup`):** join that network (no password). Many phones **open the settings page automatically**; if not, go to **`http://192.168.4.1/`** on **port 80**. After you save a real SSID (and password if needed), the device **reboots** into **station-only** mode—**`Azimuth-Setup` does not stay on**.
@@ -122,9 +131,9 @@ The portal is grouped into **Wi‑Fi**, **LAN & discovery**, **OpenTrack (PC)**,
122131
| **LAN & discovery** | **mDNS** on/off, **device hostname** (letters, digits, hyphen; max 24). Changing these → **reboot** so DHCP/mDNS apply. |
123132
| **OpenTrack (PC)** | **USB Hatire** on/off (Wi‑Fi‑only use), **UDP** on/off, **UDP address** / **port**, **axis mapping** (which fusion yaw/pitch/roll feeds Hatire/UDP **Rot 0–2**, each axis once, optional **invert** per slot). **`something.local` (mDNS)** often **does not** resolve from the ESP32; prefer numeric LAN IP or a DHCP hostname. **This browser’s IP** + **Fill address** when the portal is opened on the PC running OpenTrack. |
124133
| **Tracking & radio** | **IMU report interval** (5 / 10 / 20 / 40 ms → 200 / 100 / 50 / 25 Hz). Change → **reboot** so the BNO08x report rate is reapplied. **Wi‑Fi TX power** (low / balanced / high) applies on save without reboot. |
125-
| **Device** | Firmware version string, **Reboot**, **Erase saved settings** (clears NVS `azimuth` namespace and reboots into provisioning if no compile-time Wi‑Fi in `secrets.h`). |
134+
| **Device** | Firmware version string, **Reboot**, **Erase saved settings** (clears NVS `azimuth` and reboots; provisioning AP if no home SSID in NVS / `secrets.h`). |
126135

127-
Values live in **NVS** (`Preferences` namespace **`azimuth`**); empty NVS keys still fall back to **`include/secrets.h`**. Firmware version is set at build time (`AZIMUTH_FW_VERSION` in **`platformio.ini`** for the hatire env).
136+
**NVS** (`Preferences` **`azimuth`**) is the normal source of truth; unset fields fall back to **`include/secrets.h`** (often empty). **`AZIMUTH_FW_VERSION`** in **`platformio.ini`** applies to **`azimuth_main`** only.
128137

129138
The page is served by the stock Arduino **`WebServer`**: when no browser is connected, the firmware only calls **`handleClient()`** once per main loop (no background worker). **Wi‑Fi scan** runs only when you press **Scan networks** and can stall tracking briefly for a second or two.
130139

@@ -144,7 +153,7 @@ Use **either** **Hatire Arduino** (USB) **or** **UDP over network** as the **Inp
144153

145154
| Step | What to do |
146155
|------|------------|
147-
| **Input** | **Hatire Arduino** (correct COM port, **115200**, **DTR** on) **or** **UDP over network** (OpenTrack listens on **`OPENTRACK_UDP_PORT`** from **`platformio.ini`**, usually **4242**; set **`OPENTRACK_UDP_HOST`** in `secrets.h` to **this PC’s LAN IP**; allow UDP in the firewall). |
156+
| **Input** | **Hatire Arduino** (COM port, **115200**, **DTR** on) **or** **UDP over network** (OpenTrack listens on **`OPENTRACK_UDP_PORT`**, usually **4242**; set UDP target in the **portal** or **`secrets.h`**; allow UDP in the firewall). |
148157
| **Hatire axes** | **Yaw / Pitch / Roll****Rot 0 / Rot 1 / Rot 2** (see bullet above). |
149158
| **Filter** | **Natural motion** filter (name in the filter dropdown may vary slightly by OpenTrack version; pick the **Natural motion** / natural-style preset if available). |
150159
| **Responsiveness** | Turn **responsiveness** up to **maximum** (slider all the way up) so head motion matches the tracker with minimal lag. |
@@ -180,9 +189,9 @@ So **~4–9 hours** on a **400 mAh** cell is a **reasonable band** until you b
180189

181190
- **`src/main.cpp`** — IMU bring-up, rotation vector, Hatire + optional OpenTrack UDP; `kPinCs` / `kPinInt` / `kPinRst` match the **ESP32_BNO086** PCB (see [docs/wiring.md](docs/wiring.md)).
182191
- **`include/opentrack_pose.h`** — Fusion Euler (deg) → Hatire / OpenTrack UDP **Rot 0–2** with NVS‑configurable **per‑slot axis + invert** (defaults match README **Yaw→0, Roll→1, Pitch→2** with pitch negated).
183-
- **`src/track_network.cpp`**Hatire build only: Wi‑Fi STA, provisioning AP + captive portal, HTTP handlers (NVS + `secrets.h`), OpenTrack UDP client.
184-
- **`src/portal_html.cpp`** — PROGMEM settings UI (linked only in the Hatire env; see `platformio.ini`).
192+
- **`src/track_network.cpp`**Full Wi‑Fi / portal / OpenTrack UDP in **`azimuth_main`**; no‑op stubs in **`azimuth_debug`** (`IMU_DEBUG_MODE`).
193+
- **`src/portal_html.cpp`** — PROGMEM settings UI (built only in **`azimuth_main`**; excluded from **`azimuth_debug`** via `build_src_filter`).
185194
- **`platformio.ini`**`espressif32`, `seeed_xiao_esp32c3`, **SparkFun BNO08x** library.
186-
- **`include/secrets.h`**local WiFi + OpenTrack host (copy from `secrets.h.example`; not tracked by git).
195+
- **`include/secrets.h`**optional compile‑time Wi‑Fi / OpenTrack defaults (copy from `secrets.h.example`; gitignored).
187196

188197
If you move SPI off the default D8–D10 pins, call `SPI.begin(sck, miso, mosi, -1)` **before** `imu.beginSPI(...)` so the bus matches your board (the SparkFun driver initializes `SPI` internally; ESP32 keeps an already-started bus).

docs/roadmap.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This document tracks **estimated** progress and planned work toward a **V1** rel
1010
|------------|----------|-------|
1111
| Hardware selection | **100%** | ESP32-C3 XIAO + BNO086-class IMU, etc. |
1212
| Custom PCB (KiCad) | **~95%** | Layout + nets aligned with [wiring.md](wiring.md); DRC/ERC + BOM lock before fab; **panelization** remaining for production ordering |
13-
| Firmware | **~40%** | SPI IMU; USB debug; **Hatire** + **WiFi → OpenTrack UDP**; **HTTP settings** + **NVS** + `secrets.h` fallback; **provisioning AP** (`Azimuth-Setup`) + captive portal. Still ahead: board I/O, battery ADC, OTA, layered `imu/` / `io/` refactor (Phase 1 table below). |
13+
| Firmware | **~40%** | SPI IMU; **`azimuth_debug`** serial bring-up; **`azimuth_main`**: Hatire + Wi‑Fi OpenTrack UDP, HTTP **NVS** portal (optional `secrets.h`); **Azimuth-Setup** + captive portal. Still ahead: board I/O, battery ADC, OTA, layered `imu/` / `io/` refactor (Phase 1 table below). |
1414
| 3D enclosure | **0%** | Not started |
1515
| End-user docs & release | **TBD** | README covers OpenTrack inputs, axis mapping, filter baseline; expand with remaining firmware / release artifacts |
1616

@@ -75,7 +75,7 @@ Use this as a checklist; tighten or relax before tagging V1.
7575
| WiFi STA | ✅ (UDP client to PC; credentials via NVS + `include/secrets.h`) |
7676
| WiFi AP / onboarding portal | ✅ (`Azimuth-Setup`, captive DNS + HTTP :80; recovery if STA fails) |
7777
| Transport: OpenTrack **UDP** (6× `double`, default port 4242) ||
78-
| Semantics aligned with Hatire (yaw / pitch sign / roll) | ✅ (shared `opentrackMapEulerDegToRot` in `include/opentrack_pose.h`) |
78+
| Semantics aligned with Hatire (yaw / pitch sign / roll) | ✅ (`opentrackMapEulerToRot` + NVS axis map in `include/opentrack_pose.h`) |
7979
| Coexistence with USB (Hatire + UDP same build) ||
8080
| Security baseline (WiFi credentials **not** in git) | 🟨 (`secrets.h` local; NVS on device; migration / hardening in Phase 3) |
8181

@@ -100,7 +100,7 @@ Use this as a checklist; tighten or relax before tagging V1.
100100

101101
| Task | Status |
102102
|------|--------|
103-
| Captive portal or small HTTP server on ESP (when in AP or dual mode) | ✅ (provisioning + STA settings on :8080; portal HTML in `src/portal_html.cpp`) |
103+
| Captive portal or small HTTP server on ESP (when in AP or dual mode) | ✅ (provisioning + STA settings on :8080; `src/portal_html.cpp` in **`azimuth_main`**) |
104104
| **Or** BLE GATT for lightweight config (often nicer for phones; more firmware work) ||
105105
| Same settings backend as Phase 3 (one model, multiple UIs) | 🟨 (HTTP uses same NVS namespace; BLE not started) |
106106

@@ -134,3 +134,4 @@ These are common for head trackers; pick what matches your audience.
134134
| 2026-03-25 | Docs + BOM aligned with KiCad (**PWR1**, **`vcc`**, **BUZZER1**, **MLT-5020**, **FUNC1** footprint). |
135135
| 2026-03-27 | Firmware ~30%: Hatire + WiFi/OpenTrack UDP; `secrets.h`; PCB ~95% with **panelization** left before fab. |
136136
| 2026-03-27 | Firmware ~40%: NVS + HTTP settings, provisioning portal; roadmap/tasks aligned; shared OpenTrack axis helper; portal HTML split to `portal_html.cpp`. |
137+
| 2026-03-28 | PlatformIO envs renamed **`azimuth_main`** / **`azimuth_debug`**; GitLab CI + GitHub Pages web flasher; OpenTrack axis map in NVS / portal. |

docs/wiring.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ GPIO21 is also **UART TX**; fine for the buzzer if you do not need that UART for
9595
2. **Clock/checks:** **CLKSEL0** pulled high (internal clock selection with `CLKSEL1` left unconnected), and **CAP** has dedicated **100 nF** to GND.
9696
3. **ENV bus/checks:** **ENV_SCL** / **ENV_SDA** have pull-ups (R7/R8) even if no external environmental sensor is populated.
9797
4. **SCK**, **INT**, and **CS** traces short; solid **GND** return.
98-
5. After assembly, run firmware **`xiao_esp32c3`** and confirm serial prints before Hatire mode.
98+
5. After assembly, run **`azimuth_debug`** (`pio run -e azimuth_debug`) and confirm serial prints before switching to **`azimuth_main`** for OpenTrack / Wi‑Fi.
9999
6. If init fails: check **3.3 V**, **NRST**, **H_INTN**, **SPI** order, then re-run DRC in KiCad.
100100

101101
---

include/secrets.h.example

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#pragma once
22
/**
3-
* Copy this file to secrets.h (same folder) and fill in.
4-
* include/secrets.h is gitignored — never commit real credentials.
3+
* Copy to secrets.h (same folder). Gitignored — never commit real credentials.
4+
* Optional: compile-time Wi‑Fi / OpenTrack defaults when NVS is empty.
5+
* Most users leave these blank and configure everything in the HTTP portal (NVS).
56
*/
67

78
#define WIFI_SSID ""

0 commit comments

Comments
 (0)