Skip to main content

BLE peripheral emulator that impersonates Bluetooth multimeters for hardware-free decode verification

Project description

fakemeter

A Linux BLE peripheral emulator that impersonates Bluetooth multimeters, so a meter's official phone app connects to it and decodes frames you craft. Set a known reading (e.g. 4.200 V DC); if the app displays it, the decode is correct — if it differs, you've found a bug. A hardware-free way to verify BLE multimeter decode logic (built to validate the uni-t-mmu-ble drivers).

   ┌─────────────────────────┐         BLE         ┌──────────────────────────┐
   │  fakemeter (this tool)  │  advertise 0xFFF0   │   phone                  │
   │  ───────────────────    │ ──────────────────▶ │   ┌────────────────────┐ │
   │  craft bytes for a      │                     │   │ vendor app /       │ │
   │  KNOWN reading, e.g.    │   notify (0xFFF4)   │   │ Web-Bluetooth app  │ │
   │  "4.200 V DC"           │ ──── frame bytes ──▶│   │ DECODES + DISPLAYS │ │
   │                         │                     │   └────────────────────┘ │
   │  (you know the input)   │ ◀── writes (0xFFF3) │   (human reads output)   │
   └─────────────────────────┘     logged          └──────────────────────────┘

Eleven profiles across the OWON, Voltcraft, UNI-T, AiCare and BDM families — five live-validated against the real vendor apps (table below).

Requirements

  • Linux with BlueZ (tested on 5.72) and a working BLE adapter — hciconfig should show it UP RUNNING.
  • bluezero, which sits on the distro's python3-gi + python3-dbus (PyGObject). Those don't pip-build cleanly, so install them from apt and let the install see them (below).

Install

sudo apt install python3-gi python3-dbus            # system BLE stack

pipx install --system-site-packages fakemeter       # isolated CLI, recommended
# — or into a venv —
python3 -m venv --system-site-packages .venv && . .venv/bin/activate
pip install fakemeter

Both give you the fakemeter command. (From source: clone, then pip install -e . in the same kind of --system-site-packages venv.)

Run

fakemeter --profile voltcraft        # advertises + opens a REPL

--profile takes any id from the table. Connect the vendor app to the advertised device — it shows the initial 4.200 V DC. Drive it from the REPL:

p           play a preset             v 230.5 V    set value / function / prefix
f hold      toggle a flag             r            re-send the current frame
raw <hex>   inject an arbitrary frame s            voltcraft bit-sweep
series <id> / auth <mode> / walk on|off            ?  help        q  quit

Useful flags:

  • --name NAME — advertised name. Several apps only list an exact model name (see Gotchas) — pass the real one, not the *-FAKE default.
  • --adapter hciN — which adapter (default hci0; also accepts a BD address).
  • --self-check — publish, verify advert + GATT, run encoder round-trips, exit. No phone needed; confirms the host is sane.
  • --no-walk — fixed reading (for precise byte-mapping). --no-unsolicited — disable the raw-HCI no-CCCD delivery path. -v — log every notify / write.

Profiles

"Live-validated" = a reading was read off the real vendor app's screen. "Byte-verified" = the encoder round-trips bit-exact against a port of the app's own decoder, but hasn't been put on-screen yet.

Profile Family / format Vendor app (Android pkg) Status Notes / quirks
voltcraft OWON R10W, 15-byte LE Voltcraft VC800/900 (com.voltcraft.series800, OWON iMeter rebadge) live-validated Flag order settled (LSB-first). Interactive buttons + value-walk + HOLD all on-screen. ⚠️ device-card shows a red "disconnected" badge even while live data flows — app quirk, does NOT block the reading.
owon-plus OWON 6-byte binary (R2W) OWON iMeter (com.owon.imeter) live-validated + real-HW corroborated The OWON workhorse. Confirmed against a physical B35T+. The "+" meters (B35T+/B41T+) are this binary format. Use iMeter (writes the CCCD), not BLE4.0.
owon-old OWON 14-byte ASCII (B35) OWON BLE4.0 (com.owon.MultimeterBLE) ⚠️ byte-verified, LEGACY 31/31 round-trip green, but no live oracle and no real hardware exists in the wild — every real meter is binary. Flagged for likely removal (see its module docstring).
bdm YSCoCo XOR-scrambled Bluetooth DMM (com.yscoco.wyboem), AN9002 live-validated Needed a device-type-byte fix (descrambled byte[2]=0x03, AB_300) to render the right unit. Advert name must be exactly Bluetooth DMM or ZY.
ai-care AiCare self-addressing (FFB0) INTELLIGENT MULTIMETER (aicare.net.cn.iMultimeter) live-validated Scan gate is manufacturer-data, not name — the emulator advertises AC FF <mac-reversed> so the app lists it (see Gotchas). The readout only updates after you tap the green "Start" button.
uni-t UNI-T AB-CD, 19-byte (polled) UNI-T Smart Measure (com.uni_t.multimeter), as UT60BT live-validated Handshake-then-stream. Needed a range-index unit fix (range 0 = mV, not V). Advert name must be exactly a supported model (e.g. UT60BT).
ut202bt UNI-T (shares uni_t.encode) Smart Measure 🟡 inherits uni-t fix Same encoder as uni-t, so it inherits the range fix; not separately put on-screen.
ut117c UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified Own encoder; per-model unit/range sweep against its app still owed.
ut171 UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified Own encoder; not live-swept.
ut181a UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified, partial MAIN value block only; secondary block + datalog deferred (need a HW capture).
ut219p UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified, partial Standard live-data frame only; daoPos→param dispatch + battery-gate handshake deferred.

Verifying a decode

  1. fakemeter --profile <id> --name <exact-model>.
  2. Open the vendor app (or nRF Connect / a Web-Bluetooth client) and connect.
  3. It shows the initial reading. Set values (v 230.5 V), toggle flags (f hold), play presets (p) — if the display matches what you sent, the decode is right. Changing the value live proves it's not a static template; raw <hex> maps any byte/bit to the screen.

Gotchas

The practical ones — full detail in docs/PROGRESS.md.

  • Use the real model name. App scan lists are filtered: Bluetooth DMM accepts only Bluetooth DMM/ZY; UNI-T Smart Measure only exact models (UT60BT, UT219P, …). ai-care instead gates on advertised manufacturer-data, which the profile emits automatically — so any --name works there.
  • OWON apps need an LE-only adapter. They connectGatt(AUTO); on a dual-mode adapter Android picks Classic and bonds, blocking LE GATT. Run sudo btmgmt --index 0 bredr off and clear the phone's cached device record.
  • The OWON BLE4.0 (Java) app never subscribes via CCCD. fakemeter still reaches it by injecting ATT notifications over a raw HCI socket (on by default for stream profiles). That path needs CAP_NET_RAW: sudo setcap cap_net_raw+ep "$(readlink -f "$(which python3)")". Apps that subscribe normally use the standard BlueZ path and need no extra privilege.
  • Restarting drops a phone's bond (stale LTK → "incorrect PIN"). Clear it on both sides: bluetoothctl remove <addr> + toggle the phone's Bluetooth, then re-add from the app's scan screen.

Tests

pip install pytest && pytest -q      # 262 passed

Pure-Python (no BLE hardware needed): each profile round-trips readings through its encoder and a port of the matching decoder (tests/decode_*.py), checking value / unit / decimal point / sign, plus the FFF1 auth math, the interactive reactions, the meter-core engine, and the raw-HCI PDU layout. CI runs them on Python 3.10–3.13.

Docs

  • docs/PROGRESS.md — per-profile validation log, protocol details, and gotchas in full.
  • docs/adding-a-profile.md — how to add a profile + the layering map (basemeter_coreowon_base/uni_t_base → per-model).
  • docs/voltcraft-measurement-protocol.md, docs/owon-voltcraft-handshake.md — the R10W measurement frame and the OWON FFF1/FFF2 connect handshake.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fakemeter-0.1.1.tar.gz (101.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fakemeter-0.1.1-py3-none-any.whl (99.4 kB view details)

Uploaded Python 3

File details

Details for the file fakemeter-0.1.1.tar.gz.

File metadata

  • Download URL: fakemeter-0.1.1.tar.gz
  • Upload date:
  • Size: 101.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fakemeter-0.1.1.tar.gz
Algorithm Hash digest
SHA256 7d5a932ef20e2676d2dbbb77ad9749e73c4342cfee3a54f077a9509bffb7bf80
MD5 1f90246e4d0f6708ebd5be3a36abef19
BLAKE2b-256 f1c72a2e4989396b7d2264dba6108c18a97e22f7580b626b9ec2ed4e4730285d

See more details on using hashes here.

Provenance

The following attestation bundles were made for fakemeter-0.1.1.tar.gz:

Publisher: release.yml on ble-multimeter/fakemeter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fakemeter-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: fakemeter-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 99.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fakemeter-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 342c4c30a9e1e4554117aae2bb36b9a42ac6d4b9ede3374669193dd2e6ca67cd
MD5 812d481421e9018d2b53d42f66cb1552
BLAKE2b-256 e42c675fee085862e4313ab3337b4b1ccb7364d90ea13aa43715c4258e4f59cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for fakemeter-0.1.1-py3-none-any.whl:

Publisher: release.yml on ble-multimeter/fakemeter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page