eefocus_3914144 发表于 2025-11-24 20:54:41

【FRDM-I.MX93】体验低功耗蓝牙读取小米温湿度计

【前言】

前面我测试了一下蓝牙,是没有成功的,今天我重新从镜像包中复制了一份驱动文件到驱动包中,然后蓝牙就正常工作了,现在分享一下读取小米温湿度计的工程。
【原因查找】
我原先工作是正常的,编译了开机加载wifi的脚本,脚本代码:
#!/bin/sh -e
# 功能:Wi-Fi自动联网 + 蓝牙自动启用+绑定 + 时间同步 + 时区设置
# 适配:无 bluetoothctl/hciconfig,依赖 hcitool/rfcomm/ntpdate
# 日志:所有输出写入 /var/log/rc_local.log,方便排查
# ==============================================

# --------------------------
# 基础配置(用户按需修改!)
# --------------------------
WIFI_SSID="HUAWEI-H10R9U"          # 替换为你的Wi-Fi名称(已在wpa_supplicant.conf配置可忽略)
WIFI_CONF="/etc/wpa_supplicant/wpa_supplicant.conf"# Wi-Fi配置文件路径
BLUETOOTH_MAC="" # 替换为你的蓝牙设备MAC(无需自动绑定则留空)
BLUETOOTH_BAUD=9600               # 蓝牙串口波特率(如HC-05默认9600)
NTP_SERVERS="ntp.aliyun.com ntp.tencent.com cn.pool.ntp.org"# NTP同步服务器(多服务器冗余)
TIMEZONE="Asia/Shanghai"          # 国内时区(固定无需改)

# --------------------------
# 日志函数(统一输出格式)
# --------------------------
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> /var/log/rc_local.log
}

# --------------------------
# 1. 初始化:时区设置(优先执行)
# --------------------------
log "=== 开始初始化系统配置 ==="
export TZ="${TIMEZONE}"
ln -sf /usr/share/zoneinfo/"${TIMEZONE}" /etc/localtime# 兼容无 timedatectl 环境
log "时区已设置为:${TIMEZONE}"

# --------------------------
# 2. Wi-Fi 自动联网(核心优化:增加重试+延时)
# --------------------------
log "=== 开始 Wi-Fi 联网 ==="
# 加载 Wi-Fi 驱动(增加 3 秒延时,确保硬件就绪)
modprobe moal mod_para=nxp/wifi_mod_para.conf || { log "Wi-Fi驱动加载失败"; exit 1; }
log "Wi-Fi驱动加载成功"
sleep 3

# 启用以太网接口(mlan0)
ip link set mlan0 up || { log "mlan0接口启用失败"; exit 1; }
log "mlan0接口已启用"
sleep 2

# 启动 wpa_supplicant 连接 Wi-Fi(后台运行,失败重试1次)
wpa_supplicant -i mlan0 -c "${WIFI_CONF}" -B || {
    log "Wi-Fi连接失败,重试1次...";
    sleep 2;
    wpa_supplicant -i mlan0 -c "${WIFI_CONF}" -B || { log "Wi-Fi连接最终失败"; exit 1; }
}
log "wpa_supplicant启动成功,正在连接Wi-Fi: ${WIFI_SSID}"
sleep 3

# DHCP获取IP(重试3次,避免网络波动)
for i in 1 2 3; do
    dhclient mlan0 -v && { log "DHCP获取IP成功"; break; }
    log "第${i}次DHCP获取IP失败,等待2秒重试...";
    sleep 2;
    [ ${i} -eq 3 ] && { log "DHCP获取IP失败(3次重试)"; exit 1; }
done

# --------------------------
# 3. 蓝牙自动启用+绑定(适配 hcitool/rfcomm)
# --------------------------
log "=== 开始蓝牙初始化 ==="
# 加载蓝牙驱动(btnxpuart)
modprobe btnxpuart || { log "蓝牙驱动加载失败"; }
log "蓝牙驱动加载成功"
sleep 2

# 重启蓝牙服务(确保服务就绪)
systemctl restart bluetooth || { log "蓝牙服务重启失败"; }
log "蓝牙服务已重启"
sleep 1

# 启用蓝牙接口(hcitool替代hciconfig hci0 up)
hcitool cmd 0x03 0x0001 0x01 || { log "蓝牙接口启用失败"; }
log "蓝牙接口hci0已启用"
sleep 2

# 自动绑定蓝牙设备(BLUETOOTH_MAC非空则执行)
if [ -n "${BLUETOOTH_MAC}" ]; then
    log "正在绑定蓝牙设备: ${BLUETOOTH_MAC}"
    rfcomm release 0 2>/dev/null# 先解除旧绑定(避免冲突)
    sleep 1
    rfcomm bind 0 "${BLUETOOTH_MAC}" || { log "蓝牙绑定失败(检查MAC/设备可配对)"; }
    log "蓝牙设备绑定成功,映射串口: /dev/rfcomm0(波特率: ${BLUETOOTH_BAUD})"
fi

# --------------------------
# 4. 时间同步(多服务器重试,确保成功)
# --------------------------
log "=== 开始时间同步 ==="
sleep 5# 等待网络完全稳定
SYNC_SUCCESS=0
for server in ${NTP_SERVERS}; do
    ntpdate -u "${server}" && {
      log "时间同步成功(服务器: ${server})";
      hwclock -w# 同步到硬件时钟,重启不失效
      SYNC_SUCCESS=1;
      break;
    }
    log "时间同步失败(服务器: ${server}),尝试下一个...";
done

[ ${SYNC_SUCCESS} -eq 0 ] && log "所有NTP服务器同步失败,请检查网络"

# --------------------------
# 脚本结束(必须保留 exit 0)
# --------------------------
log "=== 自启脚本执行完成 ==="
exit 0
今天开机,在加载日志查找到有报错信息:
Found device dev-ttyLP0.device - /dev/ttyLP0.
Failed to start gpuconfig.…NXP i.MX/LS GPU (Former rc_gpu.S).
See 'systemctl status gpuconfig.service' for details.
[    6.649813] fec 42890000.ethernet end0: renamed from eth0
Started dbus.service - D-Bus System Message Bus.
Started avahi-daemon.service - Avahi mDNS/DNS-SD Stack.
         Starting wpa_supplicant.service - WPA supplicant...
Started wpa_supplicant.service - WPA supplicant.
Reached target network.target - Network.
         Starting ntpsec.service - Network Time Service...
         Starting rc-local.service▒▒m - /etc/rc.local Compatibility...
         Starting ssh.service - OpenBSD Secure Shell server...
         Starting systemd-user-sess…vice - Permit User Sessions...
Finished e2scrub_reap.serv…ine ext4 Metadata Check Snapshots.
Started loadcpufreq.servic… needed to enable cpufreq scaling.
Started ntpsec.service - Network Time Service.
Finished systemd-user-sess…ervice - Permit User Sessions.
Started systemd-logind.service - User Login Management.
         Starting cpufrequtils.serv…: set CPUFreq kernel parameters...
         Starting lightdm.service - Light Display Manager...
[    7.446894] mlan: loading out-of-tree module taints kernel.
         Starting polkit.service - Authorization Manager...
[    7.580534] wlan: Loading MWLAN driver
[    7.611959] wlan: Register to Bus Driver...
vendor=0x0471 device=0x0205 class=0 function=1
m] Started cpufrequtils.servi…SB: set CPUFreq kernel parameters.
[    7.650750] Attach moal handle ops, card interface type: 0x109
[    7.665562] rps set to 0 from module param
[    7.936006] SDIW612: init module param from usr cfg
[    7.941478] card_type: SDIW612, config block: 0
[    7.949769] cfg80211_wext=0xf
[    7.952938] max_vir_bss=1
[    7.955799] cal_data_cfg=none
[    7.958853] ps_mode = 1
[    7.961504] auto_ds = 1
[    7.963954] host_mlme=enable
[    7.966939] drv_mode = 1
[    7.969609] fw_name=nxp/sduart_nw61x_v1.bin.se
[    7.974239] SDIO: max_segs=128 max_seg_size=65535
[    7.979021] rx_work=1 cpu_num=2
[    7.982265] Enable moal_recv_amsdu_packet
[    7.986434] Attach mlan adapter operations.card_type is 0x109.
[    7.999043] wlan: Enable TX SG mode
[    8.005623] wlan: Enable RX SG mode
[    8.013492] Request firmware: nxp/sduart_nw61x_v1.bin.se
[    8.019144] wlan_sdio mmc2:0001:1: loading /lib/firmware/nxp/sduart_nw61x_v1.bin.se failed with error -22
[    8.028823] wlan_sdio mmc2:0001:1: Direct firmware load for nxp/sduart_nw61x_v1.bin.se failed with error -22
[    8.038689] wlan_sdio mmc2:0001:1: Falling back to sysfs fallback for: nxp/sduart_nw61x_v1.bin.se
Started polkit.service - Authori[    8.286184] cfg80211: failed to load regulatory.db
zation Manager.
[    8.383945] imx8_media_dev: module is from the staging directory, the quality is unknown, you have been warned.
[    8.395358] mx8-img-md: Registered mxc_isi.0.capture as /dev/video0
[    8.423629] unregister ISI channel: mxc_isi.0
[    8.586076] CAN device driver interface
[    8.728557] rtc-pcf2131-i2c 2-0053: oscillator stop detected, date/time is not reliable
[    8.795744] rtc-pcf2131-i2c 2-0053: registered as rtc1
[    9.089033] fsl_mc_err_probe: No ECC DIMMs discovered
[    9.257536] usb 1-1.2: Found UVC 1.00 device USB Camera (1bcf:28c4)
[    9.318054] usbcore: registered new interface driver uvcvideo
[    9.327884] WLAN: request_firmware() failed, error code = -2
[    9.333629] woal_request_fw failed
[    9.337095] Firmware Init Failed
[    9.343103] Free module params
[    9.350458] woal_add_card failed
[    9.354932] usb 1-1.2: 3:1: cannot get freq at ep 0x86
[    9.360275] wlan_sdio: probe of mmc2:0001:1 failed with error -1
[    9.370562] wlan: Register to Bus Driver Done
[    9.378650] wlan: Driver loaded successfully
[    9.397460] usb 1-1.2: 3:2: cannot get freq at ep 0x86
[    9.438675] usb 1-1.2: 3:3: cannot get freq at ep 0x86
[    9.472384] usb 1-1.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[    9.481152] usb 1-1.2: FU ch = 1, val = 0/4096/1
[    9.489006] usbcore: registered new interface driver snd-usb-audio
[   12.391091] rc.local: Cannot find device "mlan0"
Failed to start rc-local.s…查找到出错信息:
loading /lib/firmware/nxp/sduart_nw61x_v1.bin.se failed with error -22
然后查看这个文件:-rw-r--r-- 1 root root      0 Nov 232025 sduart_nw61x_v1.bin.se
发现他的文件大小是0,因此考虑这个文件出错了。
【解决方法】
网上搜了好久都没有找到,因此想起我原来的安装包中有这个镜像的源文件,打开原来烧写SD卡的虚拟机找到压缩包,刚好找到了这个文件:

我将这个文件拷到/lib/firmware/nxp/目录,替换这个文件,然后重启系统,发现报错信息没有了。
【蓝牙测试】
安装bluez bluetooth库
apt install -y bluez bluetooth扫描发现可以成功的使用了:
(venv_mediapipe) root@localhost:/home/mypro# bluetoothctl
Agent registered
Controller 80:A1:97:50:27:AC Pairable: yes
#
【编写小米测试程序】
from dataclasses import dataclass
import asyncio
from bleak import BleakClient, BleakError

# 你的设备配置(和 Windows 脚本一致,无需修改)
MAC = "A4:C1:38:99:9A:07"
CHARACTERISTIC_UUID = "ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6"# 已验证正确的特征值


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


async def read_ble_data(client):
    """读取并解析温湿度数据(完全复用你的 Windows 解析逻辑)"""
    try:
      # 读取特征值(和 Windows 脚本完全一致)
      buff = await client.read_gatt_char(CHARACTERISTIC_UUID)
      
      # 你的原始解析逻辑(保留不变,确保数据格式和 Windows 一致)
      temp = int.from_bytes(buff, byteorder='little', signed=True) / 100
      humidity = int.from_bytes(buff, byteorder='little')
      voltage = int.from_bytes(buff, byteorder='little') / 1000
      battery = round((voltage - 2) / (3.261 - 2) * 100, 2)
      
      # 确保电池百分比在 0-100 之间(避免异常值)
      battery = max(0, min(100, battery))
      
      result = Result(
            temperature=temp,
            humidity=humidity,
            voltage=voltage,
            battery=int(battery)# 转为整数更直观
      )
      return result
    except Exception as e:
      print(f"数据读取/解析失败:{str(e)}")
      return None


async def main(address):
    client = BleakClient(address, timeout=30.0)
    connect_attempts = 3# 连接重试次数(解决连接不稳定)
   
    try:
      # 连接重试逻辑(适配 Debian 蓝牙连接波动)
      for attempt in range(connect_attempts):
            print(f"\n🔌 连接设备 {address}(尝试 {attempt+1}/{connect_attempts})")
            print("💡 请立即短按 LYWSD03MMC 侧面按键唤醒设备!")
            await asyncio.sleep(0.5)# 给用户 0.5 秒按按键
            
            try:
                if not client.is_connected:
                  await client.connect()
                if client.is_connected:
                  print("✅ 连接成功!")
                  print("📌 若后续读取失败,请再次短按设备按键唤醒\n")
                  break
            except BleakError as e:
                print(f"❌ 第 {attempt+1} 次连接失败:{str(e)}")
                if attempt < connect_attempts - 1:
                  print("⏳ 3 秒后重试...")
                  await asyncio.sleep(3)
      
      # 连接失败后的退出逻辑
      if not client.is_connected:
            print("\n❌ 所有连接尝试失败!")
            print("排查:1. 设备是否有电 2. 距离是否 ≤ 1 米 3. 关闭手机等其他蓝牙设备")
            return
      
      # 循环读取数据(和 Windows 脚本一致,间隔 1 秒)
      while True:
            result = await read_ble_data(client)
            if result:
                print(f"[读取成功] {result}")
            else:
                print("⚠️未读取到有效数据,请短按设备唤醒")
            
            # 异步睡眠(替代 time.sleep,不阻塞事件循环)
            await asyncio.sleep(1)
   
    except BleakError as e:
      print(f"\n🔴 蓝牙连接异常:{str(e)}")
      print("💡 解决方案:重新运行脚本,按提示及时唤醒设备")
    except KeyboardInterrupt:
      print("\n\n👋 手动退出程序")
    finally:
      # 确保断开连接
      if client.is_connected:
            await client.disconnect()
            print("✅ 已断开设备连接")


if __name__ == "__main__":
    # 适配 Debian 系统的 asyncio 事件循环(解决嵌入式 Linux 兼容问题)
    try:
      asyncio.run(main(MAC))
    except RuntimeError as e:
      # 若出现事件循环错误,用备用方式启动
      loop = asyncio.get_event_loop()
      loop.run_until_complete(main(MAC))

最后测试效果如下:
(venv_mediapipe) root@localhost:/home/mypro# python lywsd03mmc_read.py

🔌 连接设备 A4:C1:38:99:9A:07(尝试 1/3)
💡 请立即短按 LYWSD03MMC 侧面按键唤醒设备!
✅ 连接成功!
📌 若后续读取失败,请再次短按设备按键唤醒

[读取成功] Result(temperature=19.97, humidity=51, voltage=2.897, battery=71)
[读取成功] Result(temperature=19.97, humidity=51, voltage=2.897, battery=71)
[读取成功] Result(temperature=19.97, humidity=51, voltage=2.897, battery=71)
[读取成功] Result(temperature=19.97, humidity=51, voltage=2.897, battery=71)
[读取成功] Result(temperature=19.97, humidity=51, voltage=2.897, battery=71)
【总结】
经过一天一夜的排查,终于把蓝牙给用上了,下一步,我将实现与mcxw71的交互。

页: [1]
查看完整版本: 【FRDM-I.MX93】体验低功耗蓝牙读取小米温湿度计

Baidu
map