Skip to content
Open
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
2 changes: 2 additions & 0 deletions components/bldc_haptics/example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ set(EXTRA_COMPONENT_DIRS
"../../../components/bldc_motor"
"../../../components/bldc_types"
"../../../components/cli"
"../../../components/coredump"
"../../../components/dispatcher"
"../../../components/esp-dsp"
"../../../components/filters"
"../../../components/format"
Expand Down
38 changes: 5 additions & 33 deletions components/bldc_haptics/example/PROTOCOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ reply (`OK` / `ERROR`, or the type-specific reply for the getters) before
sending the next. Two device-to-host frame kinds may arrive *unsolicited* and
must be tolerated at any time:

- `TELEMETRY (0x93)` — when streaming is enabled;
- `OTA_PROGRESS (0x83)` — informational during an OTA transfer.
- `TELEMETRY (0x93)` — when streaming is enabled.

The device suspends telemetry while an OTA session is active.
> Firmware update and crash-dump inspection are **not** part of this protocol:
> the example runs the standard espp OTA protocol on dispatcher **module 0** and
> the coredump service on **module 4** (use the `ota` / `coredump` web consoles,
> or the device hub, which discovers all three modules).

### Primitive types

Expand All @@ -59,10 +61,6 @@ The device suspends telemetry while an OTA session is active.

| Type | Name | Payload | Reply |
|------|---------------|-------------------------------------------|-------|
| 0x01 | OTA_BEGIN | `u32 image_size` (0 = unknown/streaming) | OK(0) / ERROR |
| 0x02 | OTA_DATA | raw image bytes (1..4096) | OK(total bytes written) / ERROR |
| 0x03 | OTA_FINISH | — | OK(total bytes written) / ERROR |
| 0x04 | OTA_ABORT | — | OK(bytes written) / ERROR |
| 0x10 | GET_INFO | — | INFO |
| 0x11 | GET_STATUS | — | STATUS |
| 0x12 | GET_MODES | — | MODES |
Expand All @@ -71,20 +69,9 @@ The device suspends telemetry while an OTA session is active.
| 0x15 | SET_ENABLED | `u8` 0 = disable, 1 = enable | OK(0/1) / ERROR |
| 0x16 | PLAY_HAPTIC | `f32 strength` (clamped to 0..10) | OK(0) / ERROR |
| 0x17 | SET_STREAMING | `u8 enable` + `u16 period_ms` (5..1000; 0 = default 20) | OK(period_ms) / ERROR |
| 0x18 | GET_CRASH | none | CRASH |

Notes:

- **OTA** semantics match the espp `ota` example, but the frames are **not**
byte-compatible: the haptics OTA subset rides dispatcher module 2, whereas the
`ota` example / `ota_console.html` use module 0. `OTA_BEGIN` erases
the next OTA app partition (can take several seconds — use a generous
timeout), `OTA_DATA` streams image bytes, `OTA_FINISH` validates the complete
image (structure + appended SHA-256) and sets it as the boot partition, then
the device **reboots ~750 ms after replying OK** (expect a USB disconnect).
With bootloader rollback enabled the new app must mark itself valid on first
boot or the bootloader rolls back. `OTA_DATA`/`OTA_FINISH`/`OTA_ABORT`
without an active session yield `ERROR(operation_not_permitted)`.
- `SET_POSITION` re-labels the detent the knob is currently resting in: it sets
the *logical* detent index (clamped to the active config's
`[min_position, max_position]`) that position/telemetry values count from.
Expand All @@ -106,10 +93,6 @@ Notes:

`u32 code` (a `std::errc` value) followed by a UTF-8 message.

### OTA_PROGRESS (0x83)

`u32 written` + `u32 total` (0 if unknown). Informational; may be ignored.

### INFO (0x90)

Reply to GET_INFO:
Expand Down Expand Up @@ -194,14 +177,3 @@ detent index plus the fractional progress toward the neighboring detent, and it
**decreases as the shaft angle increases** (the firmware's snap convention).
`value` spans `[min_position, max_position]` for bounded modes. This is what the
web app's dial renders.

### CRASH (0x94)

Reply to `GET_CRASH`. The payload is a UTF-8 text report of the previous
abnormal reset, or EMPTY when the boot history is clean. When the previous
reset was a panic with a flash core dump, the report includes the crashed
task, PC, and raw backtrace addresses (decode with
`xtensa-esp32s3-elf-addr2line -pfiaC -e build/bldc_haptics_example.elf <addrs>`);
brownout / watchdog resets are reported by reason (no core dump exists for
those). The web console requests this automatically after connecting and
prints the report in its log pane.
48 changes: 25 additions & 23 deletions components/bldc_haptics/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ install:
* **Mode switching** — select any of the built-in `espp::detail` detent presets
(unbounded, bounded, multi-rev, on/off, coarse/fine, magnetic detents,
return-to-center) from a dropdown.
* **Firmware update (OTA)** — upload a new `.bin` over the same USB interface
(via the espp `ota` component), with progress, image validation (SHA-256) and
bootloader rollback support.
* **Firmware update + crash inspection** — the same USB link also serves the
standard espp OTA protocol (dispatcher module 0) and the coredump service
(module 4), so `ota_console.html` and `coredump_console.html` — or the device
hub, which discovers all three modules — work against this device directly.

The wire protocol is documented in [PROTOCOL.md](./PROTOCOL.md); the browser
console lives in [webapp/index.html](./webapp/index.html).
Expand Down Expand Up @@ -70,7 +71,7 @@ otadata.
> routes the system console to it, so attach any serial terminal (e.g.
> `screen /dev/tty.usbmodem*`) for live logs. Flashing also still works over
> the same connector via the ROM bootloader (hold BOOT while resetting, or
> just use `webapp/index.html` for OTA updates after the first flash).
> just use `ota_console.html` for OTA updates after the first flash).

### Web console

Expand All @@ -86,23 +87,23 @@ otadata.
switch detent presets, enable/disable the motor, move to a detent, or play a
haptic click.

### Firmware update (OTA) flow
### Firmware update + crash inspection

1. Make a change and `idf.py build` (do not flash).
2. In the web console's **Firmware update** panel, pick
`build/bldc_haptics_example.bin` (the app image — NOT the merged /
bootloader image) and click **Upload**.
3. The device streams the image into the inactive OTA slot (progress + rate are
shown), validates it (structure + SHA-256), switches the boot partition and
reboots. Expect a USB disconnect; reconnect after the device re-enumerates.
4. Rollback: the freshly-booted image starts in `PENDING_VERIFY`; this example
marks itself valid after its self-check (motor + haptics up). If the new
image crashes before that, the bootloader automatically rolls back to the
previous slot on the next reset.
OTA and crash-dump download are the **standard** espp protocols on their own
dispatcher modules (not part of the haptics protocol), so the plain consoles
work against this device:

The OTA subset is part of the haptics protocol on **dispatcher module 2**, so it
is *not* interchangeable with the generic espp `ota` example (which is module 0)
— use this example's own web console for OTA here.
- **OTA (module 0)**: build (do not flash), then open `ota_console.html`,
connect, and upload `build/bldc_haptics_example.bin` (the app image — NOT the
merged / bootloader image). The device streams it into the inactive OTA slot,
validates it (structure + SHA-256), switches the boot partition and reboots
(expect a USB disconnect). The freshly-booted image starts in `PENDING_VERIFY`
and marks itself valid after its self-check; a crash before that rolls back.
- **Core dump (module 4)**: after an abnormal reset, open `coredump_console.html`
to download / erase the flash core dump (the boot log also prints a summary).

The **device hub** (`dispatcher_hub.html`) discovers all three modules on this
one device and links to each console.

## Example Behaviors

Expand Down Expand Up @@ -158,10 +159,11 @@ components:
* `espp::BldcHaptics`
* `espp::UsbDevice` — native USB vendor interface with WebUSB + MS OS 2.0
descriptors (driverless browser access)
* `espp::Ota` — transport-agnostic OTA engine fed from the USB protocol
* The `stream_frame` codec (`components/stream_frame/include/stream_frame.hpp`)
as the framing layer for the haptics protocol (module 2)
(see [PROTOCOL.md](./PROTOCOL.md))
* `espp::Ota` — transport-agnostic OTA engine (served on module 0)
* `espp::CoreDump` / `espp::CoreDumpService` — crash core-dump access (module 4)
* `espp::Dispatcher` + the `stream_frame` codec — route the vendor stream to the
OTA (0), haptics (2) and coredump (4) modules, each advertised for capability
discovery (see [PROTOCOL.md](./PROTOCOL.md))

You combine the `Mt6701` and `BldcDriver` together when creating the `BldcMotor`
and then simply pass the `BldcMotor` to the `BldcHaptics` component. At that
Expand Down
5 changes: 3 additions & 2 deletions components/bldc_haptics/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES bldc_driver bldc_haptics bldc_motor i2c motorgo-axis motorgo-mini
mt6701 ota stream_frame task usb_device esp_tinyusb esp_timer espcoredump)
REQUIRES bldc_driver bldc_haptics bldc_motor coredump dispatcher i2c
motorgo-axis motorgo-mini mt6701 ota stream_frame task usb_device
esp_tinyusb esp_timer espcoredump)
Loading