{"id":51035407,"url":"https://github.com/tostmann/improv-wifi-busware","last_synced_at":"2026-06-22T05:03:19.568Z","repository":{"id":359071097,"uuid":"1224773154","full_name":"tostmann/improv-wifi-busware","owner":"tostmann","description":"Improv-Serial Wi-Fi provisioning library for ESP32 firmwares that share their serial port with regular application traffic.","archived":false,"fork":false,"pushed_at":"2026-05-20T08:12:56.000Z","size":1321,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-20T11:56:36.600Z","etag":null,"topics":["arduino","busware","esp32","improv","improv-serial","improv-wifi","wifi-provisioning"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tostmann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-29T16:01:03.000Z","updated_at":"2026-05-20T08:13:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tostmann/improv-wifi-busware","commit_stats":null,"previous_names":["tostmann/improv-wifi-busware"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/tostmann/improv-wifi-busware","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tostmann%2Fimprov-wifi-busware","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tostmann%2Fimprov-wifi-busware/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tostmann%2Fimprov-wifi-busware/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tostmann%2Fimprov-wifi-busware/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tostmann","download_url":"https://codeload.github.com/tostmann/improv-wifi-busware/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tostmann%2Fimprov-wifi-busware/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34635047,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","busware","esp32","improv","improv-serial","improv-wifi","wifi-provisioning"],"created_at":"2026-06-22T05:03:18.999Z","updated_at":"2026-06-22T05:03:19.563Z","avatar_url":"https://github.com/tostmann.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# improv-wifi-busware\n\n[Improv-Serial][improv-spec] WiFi provisioning library for ESP32-family\ndevices that share their serial port with regular application traffic.\nOriginally extracted from Busware's TUL/TUL32/CUL32/EUL32 firmware\nprojects, but carries no Busware-specific code — drop it into any ESP32\nproject that needs Improv-Serial.\n\n[improv-spec]: https://www.improv-wifi.com/serial/\n\n```\n+----------------------+        +-----------------------+\n| ESP Web Tools (web)  | \u003c----\u003e |  improv-wifi-busware  |\n| / Improv Web Comp.   |  Improv|  (your firmware)      |\n+----------------------+ Serial +-----------------------+\n                                            |\n                                            v\n                                +-----------------------+\n                                |  WiFiBackend          |\n                                | (esp_wifi or Arduino) |\n                                +-----------------------+\n```\n\nKey differences from the upstream [jnthas/Improv-WiFi-Library][upstream]\nthat motivated this fork:\n\n| | upstream | improv-wifi-busware |\n|---|---|---|\n| **Transport coupling** | hard `Stream*` (Arduino) | `feedByte(uint8_t)` byte-feeder, no transport types in the public API |\n| **WiFi coupling** | hard `\u003cWiFi.h\u003e` | pluggable `WiFiBackend`, ships `EspIdf*` and `Arduino*` |\n| **ESP-IDF support** | Arduino-only | first-class IDF (`idf_component_register`, `esp_wifi_*`) |\n| **Window after boot** | always-on | bounded **120 s** window, then hard silent — UART is yours again |\n| **WiFi scan** | blocking 1–2 s | non-blocking, polled from `tick()` |\n| **Frame robustness** | length truncation \u003e 255 bytes, VLA, no bounds check on `WIFI_SETTINGS` parse | guarded against all of those |\n\n[upstream]: https://github.com/jnthas/Improv-WiFi-Library\n\nThe hard 120 s post-boot shutdown is the load-bearing design choice: the\nlibrary is meant for devices whose UART continues to carry the\napplication's regular protocol after provisioning. Improv must not\nlisten forever; one Improv pass per power cycle, then out of the way.\n\n\n## Try it (60 seconds)\n\nFlash a pre-built ESP32-C6 test firmware via the browser:\n\n```bash\ngit clone https://github.com/tostmann/improv-wifi-busware\ncd improv-wifi-busware/html\npython3 -m http.server 8000\n# then open http://localhost:8000/ in Chrome / Edge / Opera\n```\n\nThe landing page offers two installer buttons — one for an ESP-IDF build,\none for an Arduino-Core build of the same demo firmware. Both use the\nlibrary, both expose the Improv-Serial handshake, and both bring up a\nsmall `hallo!` HTTP greeter on port 80 after WiFi provisioning succeeds.\nUse that to verify your hardware before integrating the library into\nyour own firmware.\n\n(Web Serial is locked to HTTPS or `http://localhost`. Plain HTTP from a\nLAN IP will not work; the page detects this and tells the user.)\n\n\n## Add it to your project\n\n### PlatformIO (Arduino-Core)\n\n```ini\n; platformio.ini\n[env:my_board]\nplatform  = espressif32\nframework = arduino\nlib_deps =\n    Network\n    WiFi\n    https://github.com/tostmann/improv-wifi-busware.git\n```\n\n`Network` is needed because arduino-esp32 v3 split the network layer out\nof `WiFi` — it must be listed explicitly, otherwise the linker fails on\n`NetworkInterface::*` symbols. (The same workaround is used in\n`examples/arduino-test/platformio.ini`.)\n\n### ESP-IDF\n\nEither add this repo as a submodule and point `EXTRA_COMPONENT_DIRS` at\nthe `components/` directory:\n\n```cmake\n# top-level CMakeLists.txt of your IDF project\nset(EXTRA_COMPONENT_DIRS\n    ${EXTRA_COMPONENT_DIRS}\n    \"${CMAKE_SOURCE_DIR}/path/to/improv-wifi-busware/components\")\ninclude($ENV{IDF_PATH}/tools/cmake/project.cmake)\nproject(your_app)\n```\n\nThen in your component `REQUIRES` list (typically `main/CMakeLists.txt`):\n\n```cmake\nidf_component_register(\n    SRCS \"main.cpp\"\n    INCLUDE_DIRS \".\"\n    REQUIRES improv_wifi_busware\n)\n```\n\n### Arduino IDE\n\nCopy `components/improv_wifi_busware/` into your sketchbook's\n`libraries/` folder, then `#include \u003cImprovWiFiLibrary.h\u003e`.\n\n\n## Use it (modern API)\n\nThe transport-neutral core is what you should reach for in new code. It\nnever owns the serial transport; you feed bytes to it from whatever\ndriver you use (`Stream`, `usb_serial_jtag_*`, UART, BLE-NUS, …).\n\n```cpp\n#include \u003cimprov_wifi/improv_wifi.h\u003e\n#include \u003cimprov_wifi/idf_backend.h\u003e   // or arduino_backend.h\n\nnamespace ipw = improv_wifi_busware;\n\nstatic void writeFn(const uint8_t* d, size_t n, void*) {\n    // Forward to your serial transport. For ESP-IDF USB CDC:\n    usb_serial_jtag_write_bytes(d, n, pdMS_TO_TICKS(100));\n}\n\nstatic void onConnected(const char* ssid, const char*, void*) {\n    ESP_LOGI(\"app\", \"wifi up on '%s'\", ssid);\n}\n\nstatic ipw::EspIdfWiFiBackend backend{};\n\nvoid app_main() {\n    nvs_flash_init();\n    usb_serial_jtag_driver_install(\u0026jtag_cfg);\n\n    ipw::Config cfg;\n    cfg.backend                 = \u0026backend;\n    cfg.write                   = \u0026writeFn;\n    cfg.windowMs                = 120'000;     // default; override if you must\n    cfg.device.chipFamily       = ipw::ChipFamily::Esp32C6;\n    cfg.device.firmwareName     = \"my-firmware\";\n    cfg.device.firmwareVersion  = \"1.0.0\";\n    cfg.device.deviceName       = \"MyDevice\";\n    cfg.device.deviceUrl        = nullptr;     // -\u003e http://\u003cip\u003e\n    cfg.onConnected             = \u0026onConnected;\n\n    ipw::ImprovWiFi improv{cfg};\n\n    while (true) {\n        improv.tick(esp_timer_get_time() / 1000);\n\n        uint8_t buf[64];\n        int n = usb_serial_jtag_read_bytes(buf, sizeof(buf), pdMS_TO_TICKS(50));\n        if (n \u003e 0 \u0026\u0026 improv.isArmed()) {\n            improv.feedBytes(buf, n);\n        }\n        // After improv.isArmed() goes false the bytes are yours again --\n        // route them to your application's normal command parser.\n    }\n}\n```\n\n### Public API surface\n\n```cpp\nclass ImprovWiFi {\n    explicit ImprovWiFi(const Config\u0026 cfg);\n\n    void feedByte(uint8_t b);                       // one byte from the wire\n    void feedBytes(const uint8_t* data, size_t n);  // convenience\n    void tick(uint32_t nowMs);                      // drive timer + scan\n    bool isArmed() const;                           // window still open?\n    uint32_t windowMsRemaining(uint32_t nowMs) const;  // 0 once expired\n    bool isConnected();                             // proxy to backend\n    void setDeviceInfo(const DeviceInfo\u0026 info);\n};\n\nclass SerialFilter {  // optional helper, see \"Coexisting with a console\"\n    using SinkFn = void (*)(const uint8_t* data, size_t len, void* user);\n    void setSinks(SinkFn toConsole, void* consoleUser,\n                  SinkFn toImprov,  void* improvUser);\n    void reset();\n    void feed(const uint8_t* data, size_t len);\n};\n\nstruct Config {\n    WiFiBackend* backend;        // required: see below\n    WriteFn      write;          // required: void (*)(const uint8_t*, size_t, void*)\n    void*        userCtx;        // forwarded to write/onError/onConnected\n    uint32_t     windowMs;       // default 120'000\n    DeviceInfo   device;         // chip family, firmware name/version, device name, optional URL\n    OnErrorFn    onError;\n    OnConnectFn  onConnected;\n};\n\nclass WiFiBackend {  // implement your own or use the bundled ones\n    virtual bool isConnected() = 0;\n    virtual std::string currentIp() = 0;\n    virtual bool tryConnect(const char* ssid, const char* password) = 0;\n\n    static constexpr int kScanRunning = -1;\n    static constexpr int kScanFailed  = -2;\n    virtual void     startScan() = 0;\n    virtual int      scanResult() = 0;            // -1 / -2 / count\n    virtual ApRecord apRecord(int index) = 0;\n    virtual void     clearScan() = 0;\n};\n```\n\nThe bundled backends:\n\n- **`EspIdfWiFiBackend`** (`\u003cimprov_wifi/idf_backend.h\u003e`): `esp_wifi_*`,\n  `esp_event`, `esp_netif`. Initializes WiFi/netif/event-loop idempotently\n  if the application has not done so yet.\n- **`ArduinoWiFiBackend`** (`\u003cimprov_wifi/arduino_backend.h\u003e`): wraps\n  `\u003cWiFi.h\u003e` / `\u003cESP8266WiFi.h\u003e`. Uses the non-blocking\n  `WiFi.scanNetworks(true, ...)` + `WiFi.scanComplete()` flavour so a\n  scan does not stall the application loop.\n\nRoll your own backend if you manage WiFi differently (existing connection\nmanager, captive portal, BLE-NUS bridge, …) — implementing five virtual\nmethods is enough.\n\n### Legacy API (for drop-in replacement of jnthas's lib)\n\nIf you have existing code written against\n[jnthas/Improv-WiFi-Library][upstream]:\n\n```cpp\n#include \"ImprovWiFiLibrary.h\"\n\nImprovWiFi improvSerial(\u0026Serial);\n\nvoid setup() {\n    Serial.begin(115200);\n    improvSerial.setDeviceInfo(\n        ImprovTypes::CF_ESP32_C6,\n        \"MyFirmware\", \"1.0.0\", \"MyDevice\");\n    improvSerial.onImprovConnected(onConnected);\n}\nvoid loop() { improvSerial.handleSerial(); }\n```\n\n…this still works. The `ImprovWiFi(Stream*)` class is preserved as a thin\nArduino-only facade over the new core; only your `lib_deps` line needs\nto change.\n\n\n## Lifecycle contract (read this before debugging)\n\nThe library opens its provisioning window **unconditionally** on every\nboot, regardless of the device's WiFi state. The window:\n\n1. Opens immediately when the application calls `tick()` for the first\n   time (the lib uses *your* clock, not `millis()`, so it is hardened\n   against `millis()` instability at construction time).\n2. Stays open for `Config::windowMs` (default 120 s) of monotonic time.\n   Successful provisioning does **not** shorten it — ESP Web Tools\n   continues querying `GET_DEVICE_INFO` after the WiFi credentials are\n   accepted, and we let it.\n3. After expiry: the library is **hard-silent** on the wire. `feedByte`\n   becomes a no-op, no `CURRENT_STATE`/`ERROR_STATE`/`RPC_RESPONSE`\n   frames are written. The application owns the UART again until the\n   next reboot.\n\nThe window is *unconditional* on purpose: even a device that is already\nprovisioned and currently connected to WiFi opens its window on every\npower cycle, so a user can re-provision (move to a new SSID, rotate\nPSK, …) by simply unplugging and re-plugging the device — no factory\nreset, no dedicated button, no console command.\n\n\n## Coexisting with a user-facing console (`SerialFilter`)\n\nMost Busware products expose a CLI / REPL / vendor binary protocol on the\nsame serial line that Improv-Serial uses. Naïve \"feed every RX byte to\nboth Improv and the console\" routing is **not enough**: the moment the\nhost sends `IMPROV\u003c…\u003e\\n`, a line-oriented console will typically reply\nwith something like\n\n```\n? (IMPROV\u003cgarbage\u003e is unknown)\n```\n\nThat reply contains the literal `IMPROV` magic, which then trips up any\nstrict Improv parser on the host (ESP Web Tools, `tools/improv_client.py`)\ninto trying to decode the noise as a malformed Improv frame. Subsequent\nvalid responses from the device are dropped while the host is mid-discard.\n\nThe library ships a small reusable state machine that solves this cleanly:\n\n```cpp\n#include \u003cimprov_wifi/serial_filter.h\u003e\n\nipw::SerialFilter   filter;\nipw::ImprovWiFi     improv{cfg};\nStreamBufferHandle_t lib_rx_buf = xStreamBufferCreate(256, 1);\n\nfilter.setSinks(\n    /*toConsole=*/ [](const uint8_t* d, size_t n, void* u) {\n        // forward to your CLI / line-buffer / fntab dispatcher\n        my_console_feed(d, n);\n    },\n    /*consoleUser=*/ nullptr,\n    /*toImprov=*/ [](const uint8_t* d, size_t n, void* u) {\n        xStreamBufferSend(static_cast\u003cStreamBufferHandle_t\u003e(u), d, n, 0);\n    },\n    /*improvUser=*/ lib_rx_buf\n);\n\n// In your transport's RX-task:\nfilter.feed(rx_buf, n);\n\n// In a separate Improv-task:\nwhile (improv.isArmed()) {\n    improv.tick(now_ms());\n    uint8_t buf[64];\n    size_t got = xStreamBufferReceive(lib_rx_buf, buf, sizeof(buf), 100);\n    if (got) improv.feedBytes(buf, got);\n}\n```\n\nThe filter routes every byte to exactly one sink:\n\n* bytes that are **not** part of an Improv frame go to the console;\n* bytes that **are** part of a fully-matched Improv frame go to the lib;\n* a stray `I` followed by a non-`M` is held back, then flushed to the\n  console as a single block — so a user who literally types `I\u003center\u003e`\n  on the console still sees their `I` echoed normally.\n\nOutside the Improv window, simply stop calling `filter.feed()` (or detach\nthe filter from the transport) and the console gets the raw byte stream\nback. Reusing the filter across boots is supported via `reset()`.\n\n\n## Examples\n\nTwo minimal but complete example firmwares ship in `examples/`. Both\ntarget ESP32-C6 and use the same `\u003cimprov_wifi/...\u003e` source tree:\n\n- **`examples/idf-test/`** — pure ESP-IDF, consumed via\n  `EXTRA_COMPONENT_DIRS`. The path firmware-stacks like CULFW32 follow.\n- **`examples/arduino-test/`** — Arduino-Core via PlatformIO, consumed\n  via `lib_deps = symlink://../../components/improv_wifi_busware`.\n  The path projects like ip4knx follow.\n\nBoth:\n- print `hallo from improv-wifi-busware (idf|arduino)` at boot,\n- accept Improv-Serial provisioning during the 120 s window,\n- serve a `\u003ch1\u003ehallo!\u003c/h1\u003e` page on port 80 once WiFi is up,\n- and answer a single-character probe (`?`) with\n  `STATUS armed=\u003c0|1\u003e ms_left=\u003cN\u003e` so scripts can verify arm state\n  out-of-band. `ms_left` is the live countdown reported by\n  `ImprovWiFi::windowMsRemaining()`; `0` once the window has closed.\n\nBuild:\n\n```bash\n# ESP-IDF\ncd examples/idf-test\n. $IDF_PATH/export.sh\nidf.py set-target esp32c6        # one-time\nidf.py -p /dev/ttyACM0 build flash\n\n# Arduino-Core (PlatformIO)\ncd examples/arduino-test\npio run -e c6_arduino -t upload --upload-port /dev/ttyACM0\n```\n\n\n## Testing\n\nTwo host-side scripts live under `tools/` for use against a flashed\ndevice, no browser needed:\n\n- **`tools/improv_client.py`** — one-shot Improv-Serial client.\n\n  ```bash\n  tools/improv_client.py --port /dev/ttyACM0 --info\n  tools/improv_client.py --port /dev/ttyACM0 --scan\n  tools/improv_client.py --port /dev/ttyACM0 --validate \\\n                         --ssid 'MyWiFi' --password 'Secret123'\n  ```\n\n  The client opens the port with a DTR/RTS toggle that **resets the\n  device** by default. The very first request after that race usually\n  hits the chip mid-boot and times out. Two ways out:\n\n  - pass `--no-reset` (recommended for repeated calls in a script);\n  - pass `--boot-marker '\u003cyour firmware banner\u003e'` so the client waits\n    for a known line before talking. Pick something your firmware\n    deterministically emits late in boot — e.g. CULFW32 prints\n    `improv-serial armed for 120000 ms` exactly when the lib is ready,\n    so `--boot-marker 'improv-serial armed'` is reliable there.\n\n- **`tools/test_lifecycle.py`** — regression test for the bounded-window\n  contract. Resets the device, probes Improv at t=5 s and t=70 s\n  (expects responses), then probes at t=135 s (expects silence). Total\n  runtime ~150 s; run after any change to the timer or the parser.\n\n  ```bash\n  tools/test_lifecycle.py --port /dev/ttyACM0\n  ```\n\n- **`tests/test_serial_filter.cpp`** — host-side C++ unit test for the\n  `SerialFilter` state machine. Exercises clean frames, byte-by-byte\n  feed, partial-magic hold-back, the console-echo bug the filter was\n  built to defeat, null sinks, `reset()`, max-len payloads, and stray\n  bytes between frames. No hardware required — runs in milliseconds:\n\n  ```bash\n  g++ -std=c++17 -Wall -Wextra -O2 \\\n      -I components/improv_wifi_busware/include \\\n      components/improv_wifi_busware/src/serial_filter.cpp \\\n      tests/test_serial_filter.cpp \\\n      -o /tmp/test_serial_filter \u0026\u0026 /tmp/test_serial_filter\n  ```\n\nAlways pass `--port` explicitly to the on-device scripts. Auto-detect on\nmulti-board lab hosts will frequently pick the wrong device.\n\n\n## Repository layout\n\n```\nimprov-wifi-busware/\n├── components/\n│   └── improv_wifi_busware/         the library payload\n│       ├── include/\n│       │   ├── ImprovTypes.h        legacy Arduino types (jnthas-compat)\n│       │   ├── ImprovWiFiLibrary.h  legacy Arduino class facade\n│       │   └── improv_wifi/         modern transport-neutral API\n│       │       ├── improv_wifi.h\n│       │       ├── types.h\n│       │       ├── wifi_backend.h\n│       │       ├── arduino_backend.h\n│       │       └── idf_backend.h\n│       ├── src/\n│       │   ├── improv_wifi.cpp      core (no platform deps)\n│       │   ├── backend_idf.cpp      ESP-IDF backend\n│       │   ├── backend_arduino.cpp  Arduino backend\n│       │   └── arduino_facade.cpp   legacy class wrapper\n│       ├── CMakeLists.txt           ESP-IDF (idf_component_register)\n│       ├── library.json             PlatformIO\n│       └── library.properties       Arduino IDE\n├── examples/\n│   ├── idf-test/                    minimal ESP-IDF demo\n│   └── arduino-test/                minimal Arduino-Core demo\n├── tools/\n│   ├── improv_client.py             host-side Improv-Serial client\n│   └── test_lifecycle.py            regression test for the 120 s contract\n├── tests/\n│   └── test_serial_filter.cpp       host-side unit test for SerialFilter\n├── html/                            ESP Web Tools landing page\n└── README.md\n```\n\nThe library lives one level deep in `components/` so the repo can grow\ntooling, examples and tests around it without polluting what consumers\nvendor.\n\n\n## Compatibility\n\nTested end-to-end on ESP32-C6 (ESP-IDF 5.5 + arduino-esp32 3.x).\nShould compile without modification on ESP32 / ESP32-S2 / ESP32-S3 /\nESP32-C3 — the protocol is platform-agnostic, only the bundled WiFi\nbackends pull in target-specific WiFi headers. ESP8266 is supported\nthrough the Arduino-Core backend (`\u003cESP8266WiFi.h\u003e`) but has not been\ntested in this repo.\n\n\n## License\n\nMIT. See [`LICENSE`](LICENSE). Original Improv-WiFi-Library © 2023\nJonathas Amaral Barbosa ([@jnthas][upstream-author]); refactor and\nBusware-specific work © 2026 Dirk Tostmann / Busware. The Improv-Serial\nprotocol itself is © Open Home Foundation, see [improv-wifi.com][improv-spec].\n\n[upstream-author]: https://github.com/jnthas\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftostmann%2Fimprov-wifi-busware","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftostmann%2Fimprov-wifi-busware","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftostmann%2Fimprov-wifi-busware/lists"}