-
Notifications
You must be signed in to change notification settings - Fork 31
feat(wdi): Wheelchair Digital Interface component (device role: USB + BLE) #788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
93e1cf6
feat(wdi): protocol core for the Wheelchair Digital Interface
finger563 bf58ca2
feat(wdi): device role core (WdiDevice) with keepalive state machine
finger563 282488d
feat(wdi): build the HID report descriptor with hid-rp
finger563 dc39a60
feat(wdi): bundle the protocol core into the host C++/Python library
finger563 1c9030a
feat(wdi): BLE peripheral (device role) + example
finger563 ea0d73f
feat(wdi): USB HID device (device role) + example
finger563 2b92ba3
fix(wdi): field-accurate HID descriptor, BLE HID-over-GATT, review fixes
finger563 95089ca
docs(wdi): correct the descriptor/BLE note in the README layering sec…
finger563 77224a1
fix(wdi): null-check createCharacteristic() results in WdiBlePeripheral
finger563 586758d
fix(wdi): make WdiBlePeripheral::make_device_config const (functionCo…
finger563 7f9ab0a
fix(wdi): clamp velocity tenths, py::bytes parse bindings, test comment
finger563 edfe2fa
fix(wdi): WdiBlePeripheral::start() no longer calls the deprecated Ni…
finger563 edac021
fix(wdi): address review round 3 (scope wording, PyBytes check, CMake…
finger563 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Wheelchair Digital Interface (WDI) component. | ||
| # | ||
| # The protocol core (include/detail/wdi_protocol.hpp) is header-only and ESP-free | ||
| # (host-testable — see test/). Registering "include" alone makes both | ||
| # `#include "wdi.hpp"` and `#include "detail/wdi_protocol.hpp"` resolve for | ||
| # consumers (detail/ lives inside include/, as in the ota / odrive_native | ||
| # components). | ||
| # | ||
| # base_component is a public REQUIRES because the transport role classes | ||
| # (wdi_ble.hpp / wdi_usb.hpp and the host-side wrappers) derive from | ||
| # espp::BaseComponent; the protocol core and the WdiDevice / WdiHost cores are | ||
| # dependency-free. The transport-specific roles pull their own dependencies | ||
| # (usb_device for the USB device role; the USB Host HID + BLE stacks for the | ||
| # host / BLE roles) in their own translation units / examples. | ||
| idf_component_register( | ||
| INCLUDE_DIRS "include" | ||
| REQUIRES base_component | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,221 @@ | ||
| # WDI (Wheelchair Digital Interface) Component | ||
|
|
||
| `espp::wdi` implements the [Open-Mobility-Hub **Wheelchair HID** | ||
| specification](https://open-mobility-hub.github.io/wheelchair-digital-interface/) | ||
| (v3.2) — a standard bidirectional interface between a powered wheelchair and an | ||
| app / accessory over **USB** or **Bluetooth LE**. It lets an accessory (special | ||
| switches, an alternative joystick, a phone app, a companion MCU) drive the chair | ||
| and receive status/telemetry back. | ||
|
|
||
| The component is layered so the same protocol serves every combination: | ||
|
|
||
| - **Protocol core** (`include/detail/wdi_protocol.hpp`) — host-testable, ESP-free: | ||
| the five HID reports, their bitfields, and pack/parse helpers. | ||
| - **HID report descriptor** (`include/wdi_hid.hpp`) — the vendor (usage page | ||
| 0xFF00) report descriptor, built with the espp `hid-rp` component. It is used by | ||
| **both** transports: the USB HID interface embeds it, and the BLE profile serves | ||
| the identical bytes through its HID-over-GATT Report Map characteristic | ||
| (`10A50002`). Kept out of the dependency-free core so a protocol-only user need | ||
| not pull in `hid-rp`. | ||
| - **Device role** — the app / accessory: a USB HID **device** (via | ||
| `espp::UsbDevice`) or a BLE **peripheral**. Sends Control, receives Feedback. | ||
| - **Host role** — the wheelchair: a USB **host** (USB Host HID) or a BLE | ||
| **central**. Receives Control, sends Feedback. | ||
|
|
||
| ## Roles and direction | ||
|
|
||
| Report directions are named from the **device** (app/accessory) point of view — | ||
| an *Input* report is device→host, an *Output* report is host→device: | ||
|
|
||
| | Report | ID | Dir | Size | Purpose | | ||
| |--------|----|-----|------|---------| | ||
| | Control | 0x01 | app→host (Input) | 18 B | joystick X/Y + control-flag bitfields | | ||
| | Feedback | 0x02 | host→app (Output) | 19 B | status flags + speed / velocity / odometer | | ||
| | Request Feedback | 0x03 | app→host (Input) | 1 B | poll for a Feedback report (`0x01`) | | ||
| | Keepalive | 0x04 | app→host (Input) | 1 B | connection heartbeat (`0x01`) | | ||
| | Keepalive Response | 0x05 | host→app (Output) | 16 B | the host's 128-bit UUID (manufacturer id + random) | | ||
|
|
||
| All report payloads are little-endian **except** the Host UUID, which is | ||
| big-endian (network byte order) per the spec. | ||
|
|
||
| - **Control** carries an SInt8 `x` (−127 left … +127 right) and `y` (−127 forward | ||
| … +127 reverse) plus four u32 bitfields (Standard1/2, VendorSpecific1/2). A | ||
| `Modifier` bit reverses the seating actuators (e.g. `Tilt | Modifier` = tilt | ||
| back); an all-zero report is a "release". | ||
| - **Feedback** carries a u32 status bitfield, two vendor u32s, packed | ||
| speed/profile and velocity nibbles, and an odometer byte. | ||
| - **Keepalive**: the app sends a Control / Request-Feedback / Keepalive report | ||
| every ~233 ms; the host disconnects and drive-disables after 3 consecutive | ||
| 257 ms windows with no report. | ||
|
|
||
| `ManufacturerId`, the keepalive timing constants, and the BLE GATT UUIDs (service | ||
| `10A50001-C4EA-4B47-AE30-A7D9577FC3F9`; HID-over-GATT descriptor characteristics | ||
| `10A5000{2..5}` = Report Map / HID Information / HID Control Point / Protocol Mode; | ||
| report characteristics `10A5000{6..A}`) are all in the headers. | ||
|
|
||
| ## Component dependencies | ||
|
|
||
| The component itself only `REQUIRES base_component` — the protocol core, `WdiDevice` | ||
| and `WdiHost` need nothing else. The **transport** headers are opt-in and pull in | ||
| their own dependencies, so a project that includes one must add that dependency to | ||
| its own `REQUIRES` (the examples show this): | ||
|
|
||
| | Header | Role | Extra dependencies | | ||
| |--------|------|--------------------| | ||
| | `wdi_hid.hpp` | HID report descriptor | `hid-rp` | | ||
| | `wdi_usb.hpp` | USB device (`WdiUsbPeripheral`) | `usb_device`, `hid-rp` | | ||
| | `wdi_ble.hpp` | BLE peripheral (`WdiBlePeripheral`) | `esp-nimble-cpp` (+ `hid-rp`, for the Report Map) | | ||
| | `wdi_usb_host.hpp` | USB host (`WdiUsbHost`) — *host role, follow-up PR* | `usb_host`, `hid-rp` | | ||
| | `wdi_ble_central.hpp` | BLE central (`WdiBleCentral`) — *host role, follow-up PR* | `esp-nimble-cpp` | | ||
|
|
||
| This keeps a project that only wants the protocol core (or a single transport) | ||
| from pulling in the BLE and USB stacks it does not use. | ||
|
|
||
| ## Usage (protocol core) | ||
|
|
||
| ```cpp | ||
| #include "detail/wdi_protocol.hpp" | ||
| namespace wdi = espp::wdi; | ||
|
|
||
| // Build + serialize a Control report (accessory -> wheelchair): | ||
| wdi::ControlReport c; | ||
| c.x = 0; c.y = -100; // forward | ||
| c.set(wdi::ControlBit::DriveEnable); | ||
| c.set(wdi::ControlBit::SpeedUp); | ||
| std::array<uint8_t, wdi::kControlSize> payload = c.serialize(); | ||
|
|
||
| // Parse a Feedback report (wheelchair -> accessory): | ||
| if (auto fb = wdi::FeedbackReport::parse(bytes)) { | ||
| bool moving_ok = fb->has(wdi::FeedbackBit::DriveEnabled); | ||
| float mph = fb->velocity_mph(); | ||
| } | ||
| ``` | ||
|
|
||
| ## Device role (`espp::WdiDevice`) | ||
|
|
||
| `WdiDevice` (in `wdi.hpp`) is the app / accessory side, transport-agnostic: give | ||
| it a `send` callback (put a report on the wire) and feed it the host's reports via | ||
| `handle_output()`. It owns the keepalive state machine — call `poll()` periodically | ||
| (from an `espp::Timer` / `Task` on device) and it emits a Keepalive when one is due; | ||
| `send_control()` / `request_feedback()` reset that timer per the spec. Time is read | ||
| through a caller-supplied clock (default: a steady ms clock) so it is fully | ||
| host-testable. | ||
|
|
||
| ```cpp | ||
| espp::WdiDevice::Config cfg; | ||
| cfg.send = [&](wdi::ReportId id, std::span<const uint8_t> body) { | ||
| return usb.write_hid_report(static_cast<uint8_t>(id), body); // USB HID Input report | ||
| }; | ||
| cfg.on_feedback = [](const wdi::FeedbackReport &f) { /* update UI */ }; | ||
| espp::WdiDevice dev(cfg); | ||
| // app loop / timer: | ||
| dev.send_control(joystick_report); // drive the chair | ||
| dev.poll(); // keepalive if due | ||
| // transport RX (HID OUT / BLE write): dev.handle_output(id, bytes); | ||
| ``` | ||
|
|
||
| ### BLE peripheral (`espp::WdiBlePeripheral`) | ||
|
|
||
| `wdi_ble.hpp` wraps `WdiDevice` with the WDI GATT service (service `10A50001-…`, | ||
| characteristics `10A5000{6..A}`) on `espp::BleGattServer` (esp-nimble-cpp). After | ||
| `BleGattServer::init()`, create the service, start it, advertise, and poll: | ||
|
|
||
| ```cpp | ||
| espp::WdiBlePeripheral wdi({.on_feedback = [](const espp::wdi::FeedbackReport &f){ /*...*/ }}); | ||
| espp::BleGattServer ble; | ||
| ble.init("espp WDI"); | ||
| wdi.make_service(ble.server()); | ||
| ble.start_services(); | ||
| wdi.start(); | ||
| ble.start(); | ||
| espp::BleGattServer::AdvertisedData adv; | ||
| adv.setName("espp WDI"); | ||
| adv.addServiceUUID(espp::WdiBlePeripheral::service_uuid()); | ||
| ble.set_advertisement_data(adv); | ||
| ble.start_advertising(); | ||
| // loop: wdi.send_control(report); wdi.poll(); // poll() sends keepalives when due | ||
| ``` | ||
|
|
||
| See `ble_example/` for a full runnable example (esp32s3). Control / | ||
| Request-Feedback / Keepalive are Notify characteristics (device→central); | ||
| Feedback / Keepalive-Response are Write-Without-Response (central→device). | ||
|
|
||
| ### USB HID device (`espp::WdiUsbPeripheral`) | ||
|
|
||
| `wdi_usb.hpp` wraps `WdiDevice` with an `espp::UsbDevice` HID interface using the | ||
| WDI report descriptor (`wdi_hid.hpp`). Control / Request-Feedback / Keepalive are | ||
| HID **Input** reports (device→host, `write_hid_report()`); Feedback / | ||
| Keepalive-Response are HID **Output** reports (host→device, delivered via | ||
| `HidFunction::on_receive` — hence `has_out_endpoint`). | ||
|
|
||
| ```cpp | ||
| espp::WdiUsbPeripheral wdi({.on_feedback = [](const espp::wdi::FeedbackReport &f){ /*...*/ }}); | ||
| std::error_code ec; | ||
| wdi.initialize(ec); | ||
| // loop: wdi.send_control(report); wdi.poll(); // poll() sends keepalives when due | ||
| ``` | ||
|
|
||
| See `usb_example/` for a full runnable example (esp32s3). Because the native USB | ||
| port is given to TinyUSB, the console runs on UART0 (with USB-Serial-JTAG as an | ||
| early-boot secondary). | ||
|
|
||
| ## Status | ||
|
|
||
| - [x] Protocol core + host tests (`test/wdi_protocol_host_test.cpp`) | ||
| - [x] Device role core — `WdiDevice`, keepalive state machine, host-tested | ||
| (`test/wdi_device_host_test.cpp`) | ||
| - [x] Device role — **BLE peripheral** (`WdiBlePeripheral`, `wdi_ble.hpp`): the WDI | ||
| GATT service + characteristics on `ble_gatt_server`, with a `ble_example` | ||
| - [x] Device role — **USB HID device** (`WdiUsbPeripheral`, `wdi_usb.hpp`): the WDI | ||
| HID report descriptor on `espp::UsbDevice`, with a `usb_example` | ||
| - [ ] Host role — USB Host HID + BLE central | ||
|
|
||
| ## Testing | ||
|
|
||
| The protocol core and device role build and run on a host with just a C++20 | ||
| standard library: | ||
|
|
||
| ```bash | ||
| c++ -std=c++20 -Wall -Wextra -Werror -I components/wdi/include \ | ||
| components/wdi/test/wdi_protocol_host_test.cpp -o wdi_test && ./wdi_test | ||
| c++ -std=c++20 -Wall -Wextra -Werror -I components/wdi/include \ | ||
| components/wdi/test/wdi_device_host_test.cpp -o wdi_dev_test && ./wdi_dev_test | ||
| ``` | ||
|
|
||
| The hid-rp report descriptor also builds on a host (hid-rp is header-only; add it | ||
| as `-isystem` so its third-party headers don't trip `-Werror`): | ||
|
|
||
| ```bash | ||
| c++ -std=c++20 -Wall -Wextra -Werror -I components/wdi/include \ | ||
| -isystem components/hid-rp/include -isystem components/hid-rp/detail/hid-rp/hid-rp \ | ||
| components/wdi/test/wdi_hid_host_test.cpp -o wdi_hid_test && ./wdi_hid_test | ||
| ``` | ||
|
|
||
| ## Host library (C++ and Python) | ||
|
|
||
| The protocol core is bundled into the espp **host library** (`lib/`), so it is | ||
| available off-device for CI/interop testing and for building the **WDI host** (the | ||
| wheelchair side) on a PC to test a real peripheral against: | ||
|
|
||
| - **C++**: the `wdi/include` headers are on the host library's include path | ||
| (`espp::wdi::ControlReport`, `FeedbackReport`, `HostUuid`, `WdiDevice`, …). | ||
| - **Python**: `espp.wdi` exposes the reports/bitfields/enums | ||
| (`ControlReport`/`FeedbackReport`/`HostUuid` with `serialize()` / `parse()`), | ||
| so a host or an interop test parses Control reports and builds Feedback reports: | ||
|
|
||
| ```python | ||
| import espp | ||
| wdi = espp.wdi | ||
| got = wdi.ControlReport.parse(bytes_from_peripheral) # the wheelchair reads control | ||
| fb = wdi.FeedbackReport(); fb.set(wdi.FeedbackBit.DriveEnabled); fb.speed = 4 | ||
| send(fb.serialize()) # ...and replies with status | ||
| ``` | ||
|
|
||
| Python binding test: `python/wdi_test.py`. | ||
|
|
||
| ## Emulation / safety note | ||
|
|
||
| This component can **emulate** a WDI device or host for development and testing. | ||
| A powered wheelchair is safety-critical: do not connect an emulator to a real | ||
| chair without the manufacturer's guidance, and observe the spec's keepalive / | ||
| drive-disable semantics (a lost link must drop to a safe, stopped state). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # The following lines of boilerplate have to be in your project's CMakeLists | ||
| # in this exact order for cmake to work correctly | ||
| cmake_minimum_required(VERSION 3.20) | ||
|
|
||
| set(ENV{IDF_COMPONENT_MANAGER} "0") | ||
|
finger563 marked this conversation as resolved.
|
||
| include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||
|
|
||
| # add the component directories that we want to use | ||
| set(EXTRA_COMPONENT_DIRS | ||
| "../../../components/" | ||
| ) | ||
|
|
||
| set( | ||
| COMPONENTS | ||
| "main esptool_py wdi ble_gatt_server hid-rp" | ||
| CACHE STRING | ||
| "List of components to include" | ||
| ) | ||
|
|
||
| project(wdi_ble_example) | ||
|
|
||
| set(CMAKE_CXX_STANDARD 20) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| idf_component_register(SRC_DIRS "." INCLUDE_DIRS "." REQUIRES wdi ble_gatt_server hid-rp) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| #include <chrono> | ||
| #include <cmath> | ||
| #include <thread> | ||
|
|
||
| #include "ble_gatt_server.hpp" | ||
| #include "logger.hpp" | ||
| #include "wdi_ble.hpp" | ||
|
|
||
| using namespace std::chrono_literals; | ||
|
|
||
| // WDI (Wheelchair Digital Interface) BLE peripheral example: advertise as a WDI | ||
| // device (an accessory / alternative joystick) and drive a wheelchair (the BLE | ||
| // central) over the standard WDI GATT service. The device sends Control reports + | ||
| // keepalives and receives Feedback; here we sweep a demo joystick pattern. | ||
| extern "C" void app_main(void) { | ||
| espp::Logger logger({.tag = "WDI BLE", .level = espp::Logger::Verbosity::INFO}); | ||
| logger.info("Starting WDI BLE peripheral example"); | ||
|
|
||
| // The WDI device role over BLE. Feedback / host-identity callbacks just log. | ||
| espp::WdiBlePeripheral wdi({ | ||
| .on_feedback = | ||
| [&](const espp::wdi::FeedbackReport &f) { | ||
| logger.info("feedback: drive_enabled={} speed={} {:.1f} mph", | ||
| f.has(espp::wdi::FeedbackBit::DriveEnabled), f.speed, f.velocity_mph()); | ||
| }, | ||
| .on_keepalive_response = | ||
| [&](const espp::wdi::HostUuid &u) { | ||
| logger.info("host uuid: manufacturer=0x{:04x}", u.manufacturer_id()); | ||
| }, | ||
| .log_level = espp::Logger::Verbosity::INFO, | ||
| }); | ||
|
|
||
| // Bring up the GATT server, install the WDI service, advertise it. | ||
| espp::BleGattServer ble; | ||
| ble.set_log_level(espp::Logger::Verbosity::WARN); | ||
| ble.set_callbacks({ | ||
| .connect_callback = [&](NimBLEConnInfo &) { logger.info("wheelchair connected"); }, | ||
| .disconnect_callback = | ||
| [&](NimBLEConnInfo &, espp::BleGattServer::DisconnectReason) { | ||
| logger.info("wheelchair disconnected"); | ||
| }, | ||
| }); | ||
| const std::string device_name = "espp WDI"; | ||
| ble.init(device_name); | ||
| wdi.make_service(ble.server()); | ||
| ble.start_services(); | ||
| wdi.start(); | ||
| ble.start(); | ||
|
|
||
| espp::BleGattServer::AdvertisedData adv; | ||
| adv.setFlags(BLE_HS_ADV_F_DISC_GEN); | ||
| adv.setName(device_name); | ||
| adv.addServiceUUID(espp::WdiBlePeripheral::service_uuid()); | ||
| ble.set_advertisement_data(adv); | ||
| ble.start_advertising(); | ||
| logger.info("Advertising as '{}'; connect a WDI host (wheelchair).", device_name); | ||
|
|
||
| // SAFETY: start from a neutral "release" so the very first report a wheelchair | ||
| // receives on connect does not command motion. | ||
| wdi.send_release(); | ||
|
|
||
| // Demo loop: sweep the joystick in a slow circle, poll for keepalives, and ask | ||
| // for feedback once a second. A real accessory would map physical inputs here. | ||
| // | ||
| // DriveEnable is intentionally NOT set: a spec-compliant chair ignores joystick | ||
| // motion unless DriveEnable is asserted, so this test pattern is safe to run | ||
| // against a real chair (it will not move). Only assert DriveEnable from a | ||
| // deliberate, user-initiated action on a chair you control. | ||
| int step = 0; | ||
| while (true) { | ||
| espp::wdi::ControlReport c; | ||
| const float angle = (step % 60) / 60.0f * 2.0f * 3.14159265f; | ||
| c.x = static_cast<int8_t>(80.0f * std::sin(angle)); // right/left | ||
| c.y = static_cast<int8_t>(-80.0f * std::cos(angle)); // forward/reverse | ||
| wdi.send_control(c); // resets the keepalive timer | ||
| if (step % 20 == 0) | ||
| wdi.request_feedback(); | ||
| wdi.poll(); // send a keepalive if one is due | ||
| ++step; | ||
| std::this_thread::sleep_for(50ms); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Name, Type, SubType, Offset, Size | ||
| nvs, data, nvs, 0x9000, 0x6000 | ||
| phy_init, data, phy, 0xf000, 0x1000 | ||
| factory, app, factory, 0x10000, 2M |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096 | ||
| CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 | ||
| CONFIG_FREERTOS_HZ=1000 | ||
| CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y | ||
| CONFIG_PARTITION_TABLE_CUSTOM=y | ||
| CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" | ||
| CONFIG_BT_ENABLED=y | ||
| CONFIG_BT_BLUEDROID_ENABLED=n | ||
| CONFIG_BT_NIMBLE_ENABLED=y | ||
| CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y | ||
| CONFIG_BT_NIMBLE_NVS_PERSIST=y | ||
| CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192 | ||
| CONFIG_NIMBLE_CPP_LOG_LEVEL_NONE=y |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| CONFIG_IDF_TARGET="esp32s3" | ||
| CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| ## IDF Component Manager Manifest File | ||
| license: "MIT" | ||
| description: "Wheelchair Digital Interface (WDI / Open-Mobility-Hub Wheelchair HID): report protocol + device role over USB and BLE (host role in a follow-up)" | ||
| url: "https://github.com/esp-cpp/espp/tree/main/components/wdi" | ||
|
finger563 marked this conversation as resolved.
|
||
| repository: "https://github.com/esp-cpp/espp.git" | ||
| maintainers: | ||
| - William Emfinger <waemfinger@gmail.com> | ||
| documentation: "https://esp-cpp.github.io/espp/wdi/wdi.html" | ||
|
finger563 marked this conversation as resolved.
|
||
| examples: | ||
| - path: ble_example | ||
| - path: usb_example | ||
| tags: | ||
| - cpp | ||
| - Component | ||
| - WDI | ||
| - Wheelchair | ||
| - HID | ||
| - USB | ||
| - BLE | ||
| - Accessibility | ||
| - Mobility | ||
| dependencies: | ||
| idf: | ||
| version: '>=5.0' | ||
| espp/base_component: '>=1.0' | ||
|
finger563 marked this conversation as resolved.
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.