What it is
blz_ble_shim runs AndroWish / Android Bluetooth-LE Tcl code unaltered on Linux/BlueZ. It installs an AndroWish-compatible ble command implemented on top of undroidwish's built-in blz (BlueZ) command. Programs written against AndroWish's ble API — for example the Decent Espresso de1app — run on desktop/embedded Linux with no code changes, save one line:
package require blz_ble_shim
It ships with a simulator (blz_sim) backed by virtual DE1 + Skale devices, for developing and testing AndroWish BLE apps on any platform, including macOS, with no Bluetooth hardware.
AndroWish (Android) provides a ble command. undroidwish on Linux instead provides blz, a different BlueZ-backed command. The two use the same concepts but different verbs and event shapes, so AndroWish BLE apps do not run as-is on Linux. This package presents the AndroWish ble surface and translates every call and event to and from blz.
your AndroWish app ──ble …──▶ blz_ble_shim ──blz …──▶ BlueZ (bluetoothd)
(unchanged) (this package) (undroidwish on Linux)
How it works
blz emits only three callbacks (scan, connection, notification) and its read / write are synchronous with no event. AndroWish apps, however, sequence on events blz never sends — the write-with-response ACK (characteristic access=w), the read-completion (access=r), the per-service discovery stream (characteristic state=discovery), and the notification-enable acknowledgement (descriptor access=w). The shim synthesizes those events, delivered asynchronously (via after 0) so ordering matches Android. It also:
- allocates one
blzcontext per connection (plus one for scanning) and routes events to the right handle; - resolves each scanned device's name from its raw advertising data (AD TLV types 0x08 / 0x09);
- turns
blz's pull-style discovery (blz services/blz characteristics) into AndroWish's push-style discovery events; - fabricates the
sinstance/cinstanceintegers AndroWish apps record and echo back.
The install is guarded: it registers the global ble command only if no real ble already exists and blz is present. It also does package provide ble 1.0, so an app's own package require ble is satisfied. Everywhere else it stays inert: it does not override a real ble (Android, or macOS CoreBluetooth) and does not crash when blz is absent.
Status
- The shim is built and verified against the real
blzcommand contract. test/selftest_mock.tcl— 32/32 pass undertclsh8.6: verifies write-ACK / read / discovery / CCCD synthesis, advertising-name parsing, event shapes, and ordering.test/test_shim_e2e.tcl— 27/27 pass: the full app flow of scan → name-resolve → connect → discovery → enable/notify → write-ACK-gated writes → read → concurrent DE1 + Skale event routing → disconnect → UUID helpers.- The real
blzextension has been compiled from AndroWish source and driven by the shim end-to-end:ble state→poweredOn, UUID helpers, and the BlueZ error paths work. The unmodifiedbledemoBLE-debugger GUI runs live on Linux under nativewish8.6+ realblz+ the shim. - Remaining gap: no live scan/connect against a physical peripheral yet — that step needs Bluetooth hardware (e.g. a Raspberry Pi with a DE1 / Skale) or an emulated LE peripheral.
undroidwish's downloadable prebuilt binaries do not bundle blz; it lives in the AndroWish source tree and must be compiled in (see below).
Building & using
Pick either install form — both make package require blz_ble_shim work.
A. Installer
./install.sh # -> /usr/local/lib (tclsh/wish/undroidwish; may need sudo)
./install.sh --user # -> ~/Library/Tcl (no sudo)
./install.sh /my/libdir # -> a directory of your choice
B. By hand — copy pkgIndex.tcl, blz_ble_shim.tcl, and blz_sim.tcl into a blz_ble_shim/ directory on Tcl's auto_path, or point TCLLIBPATH at wherever you put them. Cloning the repo and putting the clone on auto_path also works — the repo root is a valid package directory.
C. Single-file Tcl Module — copy blz_ble_shim.tcl renamed blz_ble_shim-1.0.tm to a directory on tcl::tm::path.
Then, in your app:
package require blz_ble_shim
Override the BlueZ adapter with the environment variable BLZ_ADAPTER=hciN (default hci0).
Building the real blz (Linux). undroidwish's prebuilt binaries do not bundle blz; it must be compiled from the AndroWish source tree. docs/BUILDING_BLZ.md has a step-by-step recipe. The gotchas:
- Use AndroWish's vendored
blzlib(fromundroid/blz/blzlib/), not the upstreaminfsoft-locaware/blzlib— AndroWish patched it to add the Tcl event-loop hooksblz_get_fd/blz_get_events/blz_handle_read; upstream lacks them and fails at load with undefined symbols. - Link
-lsystemd -lbluetoothexplicitly (blzlib callssd_bus_*directly; the TEA build doesn't add them). - Build deps:
tcl8.6-dev,libbluetooth-dev,libsystemd-dev,gcc,tk8.6.
The hardware-free simulator
The companion blz_sim package is a simulated blz (matching the real command contract) backed by virtual DE1 and Skale peripherals. They advertise, complete discovery, ACK writes, react to writes (a RequestedState write drives a StateInfo notification), and stream weight notifications in the real 18-byte Atomax format. This allows development and testing on any platform, including macOS, where the real blz cannot run (it needs Linux sd-bus + BlueZ).
package require blz_sim ;# simulated blz + virtual devices
package require blz_ble_shim ;# `ble` on top of the simulator
Run the bundled examples:
tclsh examples/scan.tcl --sim # list the virtual devices
wish8.6 examples/run_bledemo_sim.tcl # full GUI BLE debugger, no radio
examples/bledemo.tcl is a LightBlue-style BLE debugger (scan → connect → browse services → read / subscribe / write). It is an unmodified AndroWish app, run as-is on the shim.
Limitations. blz (BlueZ) exposes a smaller surface than Android's GATT layer, so a few AndroWish features are approximated or unavailable — none of which affect typical central-role apps (DE1, scales, sensors):
- Pairing / bonding — BlueZ pairs via its own agent;
pair/unpairare no-ops (fine for Just-Works / unencrypted peripherals). - Arbitrary descriptor I/O — only the CCCD (notifications) is exposed.
- Reliable-write transactions are passed through, not atomic.
- MTU is reported as a fixed default (BlueZ negotiates it in-kernel anyway).
- Duplicate-UUID disambiguation —
sinstance/cinstanceare constants (devices with unique UUIDs are unaffected).
Repository
Source, installer, tests, examples, and the blz build recipe: github.com/johnbuckman/blz_ble_shim. Tcl/Tk (BSD-style) license.