Skip to content

Commit

Permalink
Merge pull request #84 from jphacks/feature/3_UT201BLE_python
Browse files Browse the repository at this point in the history
UT201 BLE のPython検証実装
  • Loading branch information
greenlaver authored Nov 7, 2020
2 parents b051064 + 67e5ff6 commit e998632
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 0 deletions.
33 changes: 33 additions & 0 deletions hardware/UT-201BLE/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# UT-201BLE
FlutterからのBLE実装で試行すると切り分けができないので、初めにMacOS上のPythonからBleakを使って体温計読み出しを行った。

## 使い方
1. 体温計をペアリングモードにしてから `ble_pair` でペアリング処理を行う
2. ペアリング完了後、自動的に体温計の電源が切れるので、電源を入れ直す
3. 体温計測を行い、計測完了して脇から取り出し、音が鳴ったら `ble_norify.py` で体温情報取得

## 調査結果

### Service Scan Log

|Service Name|UUID|
|-|-|
| HEALTH_SERVICE_UUID | ("00001809-0000-1000-8000-00805f9b34fb") |
| DEVICE_SERVICE_UUID | ("0000180a-0000-1000-8000-00805f9b34fb") |
| BATTERY_SERVICE_UUID | ("0000180f-0000-1000-8000-00805f9b34fb") |
| CUSTOM_SERVICE_UUID | ("233bf000-5a34-1b6d-975c-000d5690abe4") |

### Characteristics Scan Log
- 00002a1c-0000-1000-8000-00805f9b34fb: Temperature Measurement
- 00002a1d-0000-1000-8000-00805f9b34fb: Temperature Type
- 00002a08-0000-1000-8000-00805f9b34fb: Date Time
- 00002a29-0000-1000-8000-00805f9b34fb: Manufacturer Name String
- 00002a24-0000-1000-8000-00805f9b34fb: Model Number String
- 00002a25-0000-1000-8000-00805f9b34fb: Serial Number String
- 00002a27-0000-1000-8000-00805f9b34fb: Hardware Revision String
- 00002a26-0000-1000-8000-00805f9b34fb: Firmware Revision String
- 00002a28-0000-1000-8000-00805f9b34fb: Software Revision String
- 00002a23-0000-1000-8000-00805f9b34fb: System ID
- 00002a2a-0000-1000-8000-00805f9b34fb: IEEE 11073-20601 Regulatory Cert. Data List
- 00002a19-0000-1000-8000-00805f9b34fb: Battery Level
- 233bf001-5a34-1b6d-975c-000d5690abe4: Unknown
119 changes: 119 additions & 0 deletions hardware/UT-201BLE/ble_notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env python3

import asyncio
from bleak import discover, BleakClient
from datetime import datetime
import struct
import math

TEMP_MEASURE_UUID = ("00002a1c-0000-1000-8000-00805f9b34fb")
DATETIME_UUID = ("00002a08-0000-1000-8000-00805f9b34fb")
CUSTOM_CHARA_UUID = ("233bf001-5a34-1b6d-975c-000d5690abe4")

def callback_discon(client):
print("Client with address {} got disconnected!".format(client.address))

def callback_notify(sender: int, data: bytearray):
print(f"{sender}: {data}")

flags = data[0]
index = 1
result = {}

if flags & 0x01:
result['fahrenheit'] = readFloatLE(data, index)
index += 4
else:
result['celsius'] = readFloatLE(data, index)
index += 4

if flags & 0x02:
result['date'] = {
'year': int.from_bytes(data[index:index+2], 'little'),
'month': data[index+2],
'day': data[index+3],
'hour': data[index+4],
'minute': data[index+5],
'second': data[index+6],
}
index += 7

if flags & 0x04:
types = [
"unknown",
"Armpit",
"Body",
"Ear",
"Finger",
"Gastro-intestinal Tract",
"Mouth",
"Rectum",
"Toe",
"Tympanum",
]
value = data[index]
index+=1
result['temperatureType'] = types[value]

print(result)

def readFloatLE(buffer, index):
data = int.from_bytes(buffer[index:index+4], 'little')
mantissa = data & 0x00ffffff
if ((mantissa & 0x00800000) > 0):
mantissa = -1 * (~(mantissa - 0x01) & 0x00ffffff)

# exponenxtial = (data >> 24) & 0xff
exponential = -1
return mantissa * math.pow(10, exponential)

async def run(loop):
# discover
devices = await discover()
ut_201ble = [d for d in devices if "A&D_UT201BLE" in d.name]

if len(ut_201ble) <= 0:
print("UT-201 Not found...")
return

# First found device's address
address = ut_201ble[0].address

# pair
async with BleakClient(address, loop=loop) as client:
client.set_disconnected_callback(callback_discon)
x = await client.is_connected()
if not x:
print("Connect failed.")
return

print("Device Connected")

print("Send all data command")
# write command 'Send all data'
write_value = bytearray(b'\x02\x00\xe1')
await client.write_gatt_char(CUSTOM_CHARA_UUID, write_value)

print("Writing Datetime")
# Write curernt datetime
now = datetime.now()
byte_year = struct.pack("<H", now.year)
byte_month = struct.pack("B", now.month)
byte_day = struct.pack("B", now.day)
byte_hour = struct.pack("B", now.hour)
byte_minute = struct.pack("B", now.minute)
byte_second = struct.pack("B", now.second)
write_value = byte_year + byte_month + byte_day + \
byte_hour + byte_minute + byte_second
await client.write_gatt_char(DATETIME_UUID, bytearray(write_value))

print("Starting Notify...")
# Notify callback func
await client.start_notify(TEMP_MEASURE_UUID, callback_notify)

await asyncio.sleep(5.0)

await client.disconnect()

loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))
61 changes: 61 additions & 0 deletions hardware/UT-201BLE/ble_pair.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env python3

import asyncio
from bleak import discover, BleakClient
from datetime import datetime
import struct

TEMP_MEASURE_UUID = ("00002a1c-0000-1000-8000-00805f9b34fb")
DATETIME_UUID = ("00002a08-0000-1000-8000-00805f9b34fb")
CUSTOM_CHARA_UUID = ("233bf001-5a34-1b6d-975c-000d5690abe4")

def callback_discon(client):
print("Client with address {} got disconnected!".format(client.address))

async def run(loop):
# discover
devices = await discover()
ut_201ble = [d for d in devices if "A&D_UT201BLE" in d.name]

if len(ut_201ble) <= 0:
print("UT-201 Not found...")
return

# First found device's address
address = ut_201ble[0].address

# pair
async with BleakClient(address, loop=loop) as client:
client.set_disconnected_callback(callback_discon)
# await client.pair()
x = await client.is_connected()
if not x:
print("Connect failed.")
return

print("Device Connected")

# Write curernt datetime
print("Writing Datetime")

now = datetime.now()
byte_year = struct.pack("<H", now.year)
byte_month = struct.pack("B", now.month)
byte_day = struct.pack("B", now.day)
byte_hour = struct.pack("B", now.hour)
byte_minute = struct.pack("B", now.minute)
byte_second = struct.pack("B", now.second)
write_value = byte_year + byte_month + byte_day + \
byte_hour + byte_minute + byte_second
await client.write_gatt_char(DATETIME_UUID, bytearray(write_value))

# Disconnect
print("Disconnect Request")
write_value = bytearray(b'\x02\x01\x03')
await client.write_gatt_char(CUSTOM_CHARA_UUID, write_value)

await asyncio.sleep(5.0)


loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))

0 comments on commit e998632

Please sign in to comment.