Changes
This commit is contained in:
@@ -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
|
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
timestamp,freq_hz,vol,duration_s,avg_db,min_db,max_db,samples
|
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
timestamp,freq_hz,vol,duration_s,avg_db,min_db,max_db,samples
|
|
||||||
|
@@ -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
|
|
||||||
|
@@ -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
|
|
||||||
|
119
ble_discover.py
Normal file
119
ble_discover.py
Normal file
@@ -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())
|
||||||
@@ -31,13 +31,15 @@ IMX8_URL = f"http://{IMX8_IP}:5000"
|
|||||||
|
|
||||||
BLE_MAC = "B0:D2:78:5C:20:07"
|
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 ─────────────────────────────────────────
|
# ── Test sequence — edit as required ─────────────────────────────────────────
|
||||||
_FREQS = [100, 250, 500, 1000, 2000, 3000, 4000, 5000, 6000, 7000,
|
_FREQS = [100, 250, 500, 1000, 2000, 3000, 4000, 5000, 6000, 7000,
|
||||||
8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000]
|
8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000]
|
||||||
|
|
||||||
TEST_STEPS = [
|
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 freq in _FREQS
|
||||||
for vol in range(0, 11) # 0% → 100% in 10% steps
|
for vol in range(0, 11) # 0% → 100% in 10% steps
|
||||||
]
|
]
|
||||||
@@ -76,9 +78,24 @@ def _http_stop() -> dict:
|
|||||||
# ── Main test runner ──────────────────────────────────────────────────────────
|
# ── Main test runner ──────────────────────────────────────────────────────────
|
||||||
async def run_test():
|
async def run_test():
|
||||||
readings: list[float] = []
|
readings: list[float] = []
|
||||||
|
_buf = bytearray()
|
||||||
|
|
||||||
def on_notify(_sender, data: bytearray):
|
def on_notify(_sender, data: bytearray):
|
||||||
db = _parse_db(data)
|
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:
|
if db is not None:
|
||||||
readings.append(db)
|
readings.append(db)
|
||||||
|
|
||||||
@@ -91,8 +108,12 @@ async def run_test():
|
|||||||
props = ','.join(ch.properties)
|
props = ','.join(ch.properties)
|
||||||
print(f" CHAR {ch.uuid} [{props}]")
|
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)
|
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:
|
with CSV_FILE.open('w', newline='') as f:
|
||||||
writer = csv.writer(f)
|
writer = csv.writer(f)
|
||||||
|
|||||||
Reference in New Issue
Block a user