From 1aa81e675836ec43bd7c13cd86a3a891f8004050 Mon Sep 17 00:00:00 2001 From: David Rice Date: Wed, 17 Jun 2026 14:41:43 +0200 Subject: [PATCH] Changes --- audio_test_20260616_164422.csv | 6 -- audio_test_20260616_165924.csv | 1 - audio_test_20260616_170849.csv | 1 - audio_test_20260616_171013.csv | 23 ------- audio_test_20260616_171319.csv | 4 -- ble_discover.py | 119 +++++++++++++++++++++++++++++++++ soundmeter_log.py | 33 +++++++-- 7 files changed, 146 insertions(+), 41 deletions(-) delete mode 100644 audio_test_20260616_164422.csv delete mode 100644 audio_test_20260616_165924.csv delete mode 100644 audio_test_20260616_170849.csv delete mode 100644 audio_test_20260616_171013.csv delete mode 100644 audio_test_20260616_171319.csv create mode 100644 ble_discover.py diff --git a/audio_test_20260616_164422.csv b/audio_test_20260616_164422.csv deleted file mode 100644 index 1cc92ea..0000000 --- a/audio_test_20260616_164422.csv +++ /dev/null @@ -1,6 +0,0 @@ -timestamp,freq_hz,vol,duration_s,avg_db,min_db,max_db,samples -2026-06-16T16:44:31,250,0.2,3.0,56.6,56.3,56.8,7 -2026-06-16T16:44:34,500,0.2,3.0,56.0,56.0,56.1,6 -2026-06-16T16:44:38,1000,0.2,3.0,57.3,56.2,58.1,6 -2026-06-16T16:44:41,2000,0.2,3.0,61.8,58.6,64.4,5 -2026-06-16T16:44:45,4000,0.2,3.0,63.1,61.1,65.2,6 diff --git a/audio_test_20260616_165924.csv b/audio_test_20260616_165924.csv deleted file mode 100644 index 955b9fb..0000000 --- a/audio_test_20260616_165924.csv +++ /dev/null @@ -1 +0,0 @@ -timestamp,freq_hz,vol,duration_s,avg_db,min_db,max_db,samples diff --git a/audio_test_20260616_170849.csv b/audio_test_20260616_170849.csv deleted file mode 100644 index 955b9fb..0000000 --- a/audio_test_20260616_170849.csv +++ /dev/null @@ -1 +0,0 @@ -timestamp,freq_hz,vol,duration_s,avg_db,min_db,max_db,samples diff --git a/audio_test_20260616_171013.csv b/audio_test_20260616_171013.csv deleted file mode 100644 index 763817a..0000000 --- a/audio_test_20260616_171013.csv +++ /dev/null @@ -1,23 +0,0 @@ -timestamp,freq_hz,vol,duration_s,avg_db,min_db,max_db,samples -2026-06-16T17:10:21,100,0.0,5.0,0.0,0.0,0.0,0 -2026-06-16T17:10:27,100,0.1,5.0,0.0,0.0,0.0,0 -2026-06-16T17:10:33,100,0.2,5.0,0.0,0.0,0.0,0 -2026-06-16T17:10:38,100,0.3,5.0,0.0,0.0,0.0,0 -2026-06-16T17:10:44,100,0.4,5.0,0.0,0.0,0.0,0 -2026-06-16T17:10:49,100,0.5,5.0,0.0,0.0,0.0,0 -2026-06-16T17:10:55,100,0.6,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:00,100,0.7,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:06,100,0.8,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:11,100,0.9,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:17,100,1.0,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:22,250,0.0,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:28,250,0.1,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:33,250,0.2,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:39,250,0.3,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:45,250,0.4,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:50,250,0.5,5.0,0.0,0.0,0.0,0 -2026-06-16T17:11:56,250,0.6,5.0,0.0,0.0,0.0,0 -2026-06-16T17:12:01,250,0.7,5.0,0.0,0.0,0.0,0 -2026-06-16T17:12:07,250,0.8,5.0,0.0,0.0,0.0,0 -2026-06-16T17:12:12,250,0.9,5.0,0.0,0.0,0.0,0 -2026-06-16T17:12:18,250,1.0,5.0,0.0,0.0,0.0,0 diff --git a/audio_test_20260616_171319.csv b/audio_test_20260616_171319.csv deleted file mode 100644 index b9f59bd..0000000 --- a/audio_test_20260616_171319.csv +++ /dev/null @@ -1,4 +0,0 @@ -timestamp,freq_hz,vol,duration_s,avg_db,min_db,max_db,samples -2026-06-16T17:13:29,100,0.0,5.0,0.0,0.0,0.0,0 -2026-06-16T17:13:34,100,0.1,5.0,0.0,0.0,0.0,0 -2026-06-16T17:13:40,100,0.2,5.0,0.0,0.0,0.0,0 diff --git a/ble_discover.py b/ble_discover.py new file mode 100644 index 0000000..b294464 --- /dev/null +++ b/ble_discover.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +""" +ble_discover.py — BLE characteristic discovery + wake-up probe. +Connects to the RS-95-EM, subscribes to every NOTIFY characteristic, +then tries writing common "start measurement" byte sequences to every +WRITE characteristic, watching for any data after each write. + +Run this while Meterbox is NOT connected, then compare with traffic +observed when Meterbox IS connected. +""" + +import asyncio +from bleak import BleakClient + +BLE_MAC = "B0:D2:78:5C:20:07" +WATCH_SEC = 2.0 # seconds to listen after each write attempt + +# Common "start measurement" payloads seen on BLE meters +PROBE_PAYLOADS = [ + # Confirmed from Meterbox logcat (MeterBleClass.java onWriteSuccess) + b'\xd5\xfc\x11\x0d', + # Short single-byte + b'\x01', b'\x02', b'\x00', b'\xff', b'\xa0', b'\xa5', + # Common 2-byte wake sequences + b'\xaa\x00', b'\xaa\x01', b'\xaa\x55', b'\xaa\xbb', + b'\xa5\x5a', b'\xab\xbc', b'\xd5\xf0', + # Common longer init sequences seen on BLE meters + b'\xaa\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xaa\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xa5\x5a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', +] + + +async def run(): + print(f"Connecting to {BLE_MAC} …") + async with BleakClient(BLE_MAC, timeout=15.0) as client: + print("Connected.\n") + + # ── 1. Enumerate all characteristics ───────────────────────────────── + notify_chars = [] + write_chars = [] + + for svc in client.services: + print(f"SVC {svc.uuid} — {svc.description}") + for ch in svc.characteristics: + props = ', '.join(ch.properties) + print(f" CHAR {ch.uuid} [{props}]") + if 'notify' in ch.properties or 'indicate' in ch.properties: + notify_chars.append(ch) + if 'write' in ch.properties or 'write-without-response' in ch.properties: + write_chars.append(ch) + print() + + # ── 2. Subscribe to every notify characteristic ─────────────────────── + traffic = {} # uuid → list of bytearray + + def make_handler(uuid): + def handler(sender, data): + traffic.setdefault(uuid, []).append(bytes(data)) + print(f" [NOTIFY {uuid}] {data.hex(' ')}") + return handler + + for ch in notify_chars: + try: + await client.start_notify(ch.uuid, make_handler(ch.uuid)) + print(f"Subscribed to NOTIFY: {ch.uuid}") + except Exception as e: + print(f"Could not subscribe to {ch.uuid}: {e}") + print() + + # ── 3. Listen passively first (maybe it sends on its own) ───────────── + print(f"Listening passively for {WATCH_SEC}s …") + await asyncio.sleep(WATCH_SEC) + + active_passive = {k for k, v in traffic.items() if v} + if active_passive: + print(f"\nPassive traffic on: {active_passive}") + else: + print("No passive traffic — device needs a wake-up write.\n") + + # ── 4. Try each payload on each writable characteristic ─────────────── + for ch in write_chars: + for payload in PROBE_PAYLOADS: + before = {k: len(v) for k, v in traffic.items()} + + try: + if 'write-without-response' in ch.properties: + await client.write_gatt_char(ch.uuid, payload, response=False) + else: + await client.write_gatt_char(ch.uuid, payload, response=True) + print(f"Wrote {payload.hex(' ')} → {ch.uuid} … ", end='', flush=True) + except Exception as e: + print(f"Write {payload.hex(' ')} → {ch.uuid} FAILED: {e}") + continue + + await asyncio.sleep(WATCH_SEC) + + after = {k: len(v) for k, v in traffic.items()} + new_traffic = {k for k, v in after.items() if v > before.get(k, 0)} + if new_traffic: + print(f"\n *** DATA on {new_traffic} after writing {payload.hex(' ')} to {ch.uuid} ***") + else: + print("no response.") + + # ── 5. Summary ──────────────────────────────────────────────────────── + print("\n── Summary ──────────────────────────────────────────") + for uuid, packets in traffic.items(): + if packets: + print(f" {uuid}: {len(packets)} packets received") + print(f" First: {packets[0].hex(' ')}") + if len(packets) > 1: + print(f" Last: {packets[-1].hex(' ')}") + if not any(traffic.values()): + print(" No data received on any characteristic.") + + +if __name__ == '__main__': + asyncio.run(run()) diff --git a/soundmeter_log.py b/soundmeter_log.py index 9a865c2..215ee47 100644 --- a/soundmeter_log.py +++ b/soundmeter_log.py @@ -30,14 +30,16 @@ IMX8_IP = "10.32.34.103" IMX8_URL = f"http://{IMX8_IP}:5000" BLE_MAC = "B0:D2:78:5C:20:07" -BLE_CHAR = "0000fff2-0000-1000-8000-00805f9b34fb" +BLE_CHAR = "0000fff2-0000-1000-8000-00805f9b34fb" +BLE_WAKE_CHAR = "0000fff1-0000-1000-8000-00805f9b34fb" +BLE_WAKE_CMD = bytes([0xD5, 0xFC, 0x11, 0x0D]) # from Meterbox logcat # ── Test sequence — edit as required ───────────────────────────────────────── _FREQS = [100, 250, 500, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000] TEST_STEPS = [ - {"freq": freq, "vol": round(vol / 10, 1), "duration": 5.0} + {"freq": freq, "vol": round(vol / 10, 1), "duration": 10.0} for freq in _FREQS for vol in range(0, 11) # 0% → 100% in 10% steps ] @@ -76,11 +78,26 @@ def _http_stop() -> dict: # ── Main test runner ────────────────────────────────────────────────────────── async def run_test(): readings: list[float] = [] + _buf = bytearray() def on_notify(_sender, data: bytearray): - db = _parse_db(data) - if db is not None: - readings.append(db) + nonlocal _buf + _buf.extend(data) + # reassemble: packets are always 11 bytes starting with D5 F0 + while len(_buf) >= 11: + idx = bytes(_buf).find(b'\xd5\xf0') + if idx == -1: + _buf.clear() + break + if idx > 0: + del _buf[:idx] # discard garbage before header + if len(_buf) < 11: + break + packet = bytes(_buf[:11]) + del _buf[:11] + db = _parse_db(packet) + if db is not None: + readings.append(db) print(f"Connecting to BLE {BLE_MAC} …") async with BleakClient(BLE_MAC, timeout=15.0) as client: @@ -91,8 +108,12 @@ async def run_test(): props = ','.join(ch.properties) print(f" CHAR {ch.uuid} [{props}]") + # Wake-up sequence: write D5 FC 11 0D before subscribing (from Meterbox logcat) + await client.write_gatt_char(BLE_WAKE_CHAR, BLE_WAKE_CMD, response=True) + print(f"Wake-up sent: {BLE_WAKE_CMD.hex(' ')}") + await client.start_notify(BLE_CHAR, on_notify) - print(f"\nSubscribed to {BLE_CHAR}\n") + print(f"Subscribed to {BLE_CHAR}\n") with CSV_FILE.open('w', newline='') as f: writer = csv.writer(f)