注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
许大锤的个人空间 https://home.eeworld.com.cn/space-uid-1303215.html [收藏] [复制] [分享] [RSS]
日志

Pyrhon获取小米蓝牙温湿度计中的数据信息

已有 466 次阅读2024-1-12 00:59

我的温湿度计是秒秒蓝牙温湿度计(MHO-C401),和小米是一样的。

1. 环境配置

首先,确保您的树莓派的操作系统(如Raspberry Pi OS)是最新的,并且蓝牙功能是启用的。安装必要的蓝牙相关工具和库:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev

2. 安装Python库

使用Python来编写脚本。首先,安装Python及必要的库,如bluepy,这是一个Python模块,用于与蓝牙LE设备通信。

sudo apt-get install python3-pip
pip3 install bluepy

3. 扫描蓝牙设备

编写一个简单的Python脚本来扫描周围的蓝牙设备,以找到秒秒蓝牙温湿度计的MAC地址。下面是一个基本的示例:

from bluepy.btle import Scanner
​
scanner = Scanner()
devices = scanner.scan(10.0)  # 扫描10秒
​
for dev in devices:
   print("Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi))
   for (adtype, desc, value) in dev.getScanData():
       print(" %s = %s" % (desc, value))

结果:

sudo python3 blue.py 
Device 61:ad:7b:8c:03:bd (random), RSSI=-70 dB
Flags = 1a
Manufacturer = 4c000c0e083c5c0762c09c6fc77e15aa223010064b1d62288c18
Device 6d:e0:8f:a6:ac:59 (random), RSSI=-69 dB
Flags = 1a
Manufacturer = 4c0009081302c0a800051b58
Device 63:c6:21:60:56:2c (random), RSSI=-77 dB
Flags = 1a
Manufacturer = 4c000c0e083c5c0762c09c6fc77e15aa223010064b1d62288c18
Device 7a:93:04:3d:5e:cf (random), RSSI=-75 dB
Flags = 1a
Tx Power = 11
Manufacturer = 4c001006471d1cc1f568
Device 79:c9:33:67:86:46 (random), RSSI=-76 dB
Flags = 1a
Manufacturer = 4c0010050318b52fab
Device a4:c1:38:6c:9c:96 (public), RSSI=-85 dB
Flags = 06
16b Service Data = 95fe30588703a7969c6c38c1a408
Complete Local Name = MHO-C401
Device e1:3b:d1:16:0e:e2 (random), RSSI=-68 dB
Manufacturer = 4c0012020002
Device c6:a0:73:ab:c1:ae (random), RSSI=-76 dB
Manufacturer = 4c0012020001
Device 04:e2:29:b3:4a:e7 (public), RSSI=-98 dB
Flags = 06
0x1c = 00
Manufacturer = 290910000004e229b34ae60100e903

这里“a4:c1:38:6c:9c:96”就是我的“秒秒测蓝牙温湿度计”

4. 连接和读取数据

找到秒秒蓝牙温湿度计的MAC地址后,您需要编写代码来连接设备并读取数据。这可能需要一些尝试和错误,因为您需要知道正确的服务和特性UUID来读取数据。这些信息可能在设备的文档中或者需要通过一些实验来确定。

这里提供一个基本的框架,但请注意,您可能需要调整服务和特性的UUID:

from bluepy.btle import Peripheral
​
try:
   p = Peripheral("a4:c1:38:6c:9c:96", "public")
   # 您可能需要根据实际情况更改服务和特性的UUID
   services = p.getServices()
   for service in services:
      print(service)
   # 假设您找到了正确的服务和特性
   # characteristic = p.getCharacteristics(uuid='特性的UUID')[0]
   # if characteristic.supportsRead():
   #     while True:
   #         data = characteristic.read()
   #         print(data)
   #         time.sleep(1)
​
finally:
   p.disconnect()

结果:

Service <uuid=Generic Access handleStart=1 handleEnd=7>
Service <uuid=Generic Attribute handleStart=8 handleEnd=11>
Service <uuid=Device Information handleStart=12 handleEnd=24>
Service <uuid=Battery Service handleStart=25 handleEnd=28>
Service <uuid=00010203-0405-0607-0809-0a0b0c0d1912 handleStart=29 handleEnd=32>
Service <uuid=ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6 handleStart=33 handleEnd=78>
Service <uuid=fe95 handleStart=79 handleEnd=98>
Service <uuid=00000100-0065-6c62-2e74-6f696d2e696d handleStart=99 handleEnd=107>

这些输出显示了您连接的蓝牙设备提供的服务列表。每个服务由一个UUID(Universally Unique Identifier)标识,而且每个服务都有一个起始和结束句柄。这些服务可能包括设备信息、电池服务和其他特定于设备的服务。

以下是您列出的一些服务的可能含义:

  1. Generic Access (UUID: Generic Access): 这是标准的蓝牙服务,用于提供设备的基本信息,例如名称和可见性。

  2. Generic Attribute (UUID: Generic Attribute): 这个服务负责管理设备上的服务和特性的发现。

  3. Device Information (UUID: Device Information): 这个服务通常包含设备的制造商名称、型号、序列号等信息。

  4. Battery Service (UUID: Battery Service): 提供有关设备电池状态的信息,如电量百分比。

  5. 其他服务 (UUID: 特定值): 这些服务可能是特定于设备的,例如用于传输特定数据(例如温湿度数据)的自定义服务。特别是UUID为ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6的服务可能是与您的秒秒蓝牙温湿度计相关的关键服务。

为了获取温湿度数据,您需要进一步探索与UUID ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6 对应的服务。这个服务下可能有特定的特性(Characteristics),这些特性存储了温度和湿度的实际数据。

接下来,您可以使用 getCharacteristics() 方法来列出此服务下的所有特性,并尝试读取这些特性来查找包含温湿度数据的特性。代码示例可能如下:

service_uuid = "ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"
svc = p.getServiceByUUID(service_uuid)
characteristics = svc.getCharacteristics()
for char in characteristics:
   print("Characteristic %s" % char)
   print(" - UUID: %s" % char.uuid)
   print(" - Handle: %s" % char.getHandle())
   print(" - Properties: %s" % char.propertiesToString())
   # 尝试读取特性
   if char.supportsRead():
       try:
           value = char.read()
           print(" - Value: %s" % value)
       except Exception as e:
           print("Error reading characteristic: %s" % str(e))

这个脚本将遍历指定服务下的所有特性,并尝试读取它们的值。您需要根据实际情况解析这些值来获取温度和湿度数据。请注意,这可能需要一些试验和错误,因为需要确定哪个特性包含了您需要的数据。

Characteristic Characteristic <ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 35
 - Properties: READ WRITE 
Characteristic Characteristic <ebe0ccb9-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccb9-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 38
 - Properties: READ 
Characteristic Characteristic <ebe0ccba-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccba-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 41
 - Properties: READ WRITE 
Characteristic Characteristic <ebe0ccbb-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccbb-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 44
 - Properties: READ 
Characteristic Characteristic <ebe0ccbc-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccbc-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 47
 - Properties: NOTIFY 
Characteristic Characteristic <ebe0ccbe-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccbe-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 51
 - Properties: READ WRITE 
Characteristic Characteristic <ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 54
 - Properties: READ NOTIFY 
Characteristic Characteristic <ebe0ccc4-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccc4-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 58
 - Properties: READ 
Characteristic Characteristic <ebe0ccc8-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccc8-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 61
 - Properties: WRITE 
Characteristic Characteristic <ebe0ccd1-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccd1-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 64
 - Properties: WRITE 
Characteristic Characteristic <ebe0ccd4-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccd4-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 67
 - Properties: WRITE 
Characteristic Characteristic <ebe0ccd7-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccd7-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 70
 - Properties: READ WRITE 
Characteristic Characteristic <ebe0ccd8-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccd8-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 73
 - Properties: WRITE 
Characteristic Characteristic <ebe0ccd9-7a0a-4b0c-8a1a-6ff2997da3a6>
 - UUID: ebe0ccd9-7a0a-4b0c-8a1a-6ff2997da3a6
 - Handle: 76
 - Properties: WRITE NOTIFY 

 

每个特性都有一个唯一的UUID、句柄(Handle)以及一组属性(如 READ, WRITE, NOTIFY 等)。

现在,需要确定哪个特性包含温度和湿度数据。基于这些特性的属性,我们可以进行一些推断:

  1. READ 属性的特性可用于读取数据。对于温湿度计来说,温度和湿度数据很可能是可以读取的。

  2. NOTIFY 属性的特性可能用于实时更新数据。当温度或湿度发生变化时,设备可能会通过这些特性发送通知。

  3. WRITE 属性的特性可能用于更改设备的设置或配置。

要找到包含温湿度数据的特性,您可以尝试读取那些具有 READ 属性的特性。例如:

characteristics = svc.getCharacteristics()
for char in characteristics:
   if "READ NOTIFY" in char.propertiesToString():
       try:
           value = char.read()
           print(f"Characteristic {char.uuid} Value: {value}")
       except Exception as e:
           print(f"Error reading characteristic {char.uuid}: {e}")

利用BLE助手进行分析,尝试有“NOTIFY,READ”不同的通知,分析结果。

接收到的数据 52 09 1F 89 0A 注意大小端 数据转为10进制 09 52->2386->即23.86摄氏度 1f->31->湿度31% 0A 89->26.97->电池电压26.97 mv

----------- 数据来源

UUID:ebe0ccc1-7a0a-4b0c-8a1a-6ff-2997da3a6 Properties:READ NOTIFY

 

 

import time

from bluepy import btle
from dataclasses import dataclass

mac = "a4:c1:38:6c:9c:96"

@dataclass
class Result:
    temperature: float
    humidity: int
    voltage: float
    battery: int = 0


class Measure(btle.DefaultDelegate):
    def __init__(self, params):
        btle.DefaultDelegate.__init__(self)
        self.temperature = None


    def handleNotification(self, cHandle, data):
        try:
            result = Result(0, 0, 0, 0)
            temp = int.from_bytes(data[0:2], byteorder='little', signed=True) / 100
            humidity = int.from_bytes(data[2:3], byteorder='little')
            voltage = int.from_bytes(data[3:5], byteorder='little') / 1000
            battery = round((voltage - 2) / (3.261 - 2) * 100, 2)
            result.temperature = temp
            result.humidity = humidity
            result.voltage = voltage
            result.battery = battery
            print(result)
            self.temperature = result.temperature
        except Exception as e:
            print(e)

class Connect:
    def __init__(self):
        self.measure = Measure("mijia")

    def connect(self):
        self.measure.temperature = None
        p = btle.Peripheral(mac)
        p.writeCharacteristic(0x0038, b'\x01\x00', True)
        p.writeCharacteristic(0x0046, b'\xf4\x01\x00', True)
        self.measure = Measure("mijia")
        p.withDelegate(self.measure)
        return p

    def getTemperature(self):
        return self.measure.temperature


if __name__ == '__main__':
    while True:
        p = Connect().connect()
        time.sleep(1)
        if p.waitForNotifications(3000):
            p.disconnect()

 

 

本文来自论坛,点击查看完整帖子内容。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章