eefocus_3914144 发表于 2025-12-11 19:31:38

【FRDM-MCXW71】【FRDM-IMX93】用蓝牙打通任督二脉

【前言】
在前面,想了许多的办法,都没有能让【FRDM-MCXW71】与【FRDM-IMX93】建立起通信,经过九九八十一难,终于让他们连起来了。
【FRDM-MCXW71开发环境】
Zephyr
【代码】
1、prj.conf

```
# Increased stack due to settings API usage
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

# 启用 I2C(LPI2C1)
CONFIG_I2C=y
CONFIG_I2C_MCUX_LPI2C=y

# 启用传感器子系统
CONFIG_SENSOR=y

# 启用 FXLS8974 驱动(兼容 FXLS**AF)
CONFIG_FXLS8974=y

CONFIG_BT=y
CONFIG_LOG=y
CONFIG_BT_SMP=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DIS=y
CONFIG_BT_ATT_PREPARE_COUNT=5
CONFIG_BT_BAS=y
CONFIG_BT_HRS=y
CONFIG_BT_IAS=y
CONFIG_BT_CTS=y
CONFIG_BT_CTS_HELPER_API=y
CONFIG_BT_PRIVACY=y
CONFIG_BT_DEVICE_NAME="NXP_FLX_DEMO"
CONFIG_BT_DEVICE_APPEARANCE=833
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_DEVICE_NAME_MAX=65

CONFIG_BT_KEYS_OVERWRITE_OLDEST=y
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
```

main.c

```
/* main.c - 蓝牙应用 + FXLS**AF传感器支持(修复版) */
/* SPDX-License-Identifier: Apache-2.0 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.h>

#include <zephyr/settings/settings.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/services/bas.h>

/* 添加传感器相关头文件 */
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/sensor.h>

/* 自定义服务UUID */
#define BT_UUID_CUSTOM_SERVICE_VAL \
        BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)

static const struct bt_uuid_128 vnd_uuid = BT_UUID_INIT_128(BT_UUID_CUSTOM_SERVICE_VAL);

#define ACCEL_NODE DT_NODELABEL(accelerometer)

#if !DT_NODE_EXISTS(ACCEL_NODE)
#error "Accelerometer node not found in devicetree"
#endif

/* 传感器数据UUID */
#define BT_UUID_SENSOR_DATA_VAL \
        BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef5)
static const struct bt_uuid_128 sensor_data_uuid = BT_UUID_INIT_128(BT_UUID_SENSOR_DATA_VAL);

/* 数据长度定义 */
#define SENSOR_DATA_LEN 12/* 3轴数据,每轴4字节 */

/* 传感器数据缓存 */
static uint8_t sensor_data;

/* 延迟重启广播的工作队列 */
static struct k_work_delayable adv_restart_work;

/* 传感器相关变量 */
static struct k_work sensor_work;
static const struct device *fxls**_dev;

/* 连接状态标志 */
static bool is_connected = false;
static uint32_t sensor_read_count = 0;/* 添加读取计数器 */

/* 读取传感器数据的回调函数 */
static ssize_t read_sensor_data(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                             void *buf, uint16_t len, uint16_t offset)
{
        printk("Sensor data read request\n");/* 添加调试信息 */
        return bt_gatt_attr_read(conn, attr, buf, len, offset, sensor_data, sizeof(sensor_data));
}

/* 简化版自定义服务定义 */
BT_GATT_SERVICE_DEFINE(vnd_svc,
        BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
        /* 传感器数据特性 */
        BT_GATT_CHARACTERISTIC(&sensor_data_uuid.uuid,
                             BT_GATT_CHRC_READ,
                             BT_GATT_PERM_READ,
                             read_sensor_data, NULL, sensor_data),
);

/* 广播数据配置 */
static const struct bt_data ad[] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
        BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
        BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_CUSTOM_SERVICE_VAL),
};

static const struct bt_data sd[] = {
        BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

/* 延迟重启广播的工作函数 */
static void adv_restart_handler(struct k_work *work)
{
        int err;
       
        printk("Restarting advertisement...\n");/* 添加调试信息 */
       
        /* 停止当前广播 */
        bt_le_adv_stop();
        k_sleep(K_MSEC(100));
       
        /* 重启广播 */
        err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
        if (err) {
                printk("Advertising restart failed (err: %d), retry in 1s\n", err);
                k_sleep(K_SECONDS(1));
                err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
                if (err) {
                        printk("Advertising restart retry failed (err: %d)\n", err);
                } else {
                        printk("Advertising restart succeeded (retry)\n");
                }
        } else {
                printk("Advertising restart succeeded\n");
        }
}

/* 传感器数据处理工作函数 */
static void sensor_work_handler(struct k_work *work)
{
        struct sensor_value accel_values; /* x, y, z */
        int rc;
        int16_t x, y, z;

        sensor_read_count++;/* 增加读取计数 */
        printk("Sensor work handler called (count: %d)\n", sensor_read_count);/* 调试信息 */

        // 检查连接状态和设备就绪状态
        if (!is_connected) {
                printk("Not connected, skipping sensor read\n");
                return;
        }
       
        if (!fxls**_dev) {
                printk("Sensor device not initialized\n");
                return;
        }

        /* 获取传感器数据 */
        printk("Fetching sensor sample...\n");
        rc = sensor_sample_fetch(fxls**_dev);
        if (rc) {
                printk("Failed to fetch sensor sample: %d\n", rc);
                return;
        }

        rc = sensor_channel_get(fxls**_dev, SENSOR_CHAN_ACCEL_XYZ, accel_values);
        if (rc) {
                printk("Failed to get sensor channel data: %d\n", rc);
                return;
        }

        /* 转换为整数数值(以mg为单位) */
        x = (int16_t)(sensor_value_to_double(&accel_values) * 1000);
        y = (int16_t)(sensor_value_to_double(&accel_values) * 1000);
        z = (int16_t)(sensor_value_to_double(&accel_values) * 1000);

        /* 存储到传感器数据缓冲区(小端序) */
        sys_put_le16(x, &sensor_data);
        sys_put_le16(y, &sensor_data);
        sys_put_le16(z, &sensor_data);
        sys_put_le16(0, &sensor_data);/* 预留 */
        sys_put_le16(0, &sensor_data);/* 预留 */
        sys_put_le16(0, &sensor_data); /* 预留 */

        printk("Accel: X=%d mg, Y=%d mg, Z=%d mg\n", x, y, z);
}

/* 连接状态回调 */
static void connected(struct bt_conn *conn, uint8_t err)
{
        char addr;
        bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

        if (err) {
                printk("Connection failed: %s (err: 0x%02x)\n", addr, err);
                /* 连接失败时延迟重启广播 */
                k_work_schedule(&adv_restart_work, K_NO_WAIT);
        } else {
                printk("Connected: %s\n", addr);
                is_connected = true;// 设置连接状态为true
        }
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
        char addr;
        bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

        printk("Disconnected: %s (reason: 0x%02x, %s)\n",
             addr, reason, bt_hci_err_to_str(reason));

        is_connected = false;// 设置连接状态为false
       
        /* 延迟500ms重启广播 */
        k_work_schedule(&adv_restart_work, K_MSEC(500));
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
        .connected = connected,
        .disconnected = disconnected,
};

/* 蓝牙初始化完成回调 */
static void bt_ready(void)
{
        int err;

        printk("Bluetooth initialized\n");

        if (IS_ENABLED(CONFIG_SETTINGS)) {
                settings_load();
        }

        /* 初始启动广播 */
        err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
        if (err) {
                printk("Initial advertising start failed (err: %d)\n", err);
                return;
        }

        printk("Device name: %s\n", CONFIG_BT_DEVICE_NAME);
        printk("Advertising started, waiting for connections...\n");
}

/* 电池服务模拟 */
static void bas_notify(void)
{
        static uint8_t battery_level = 100;
        battery_level = (battery_level == 0) ? 100 : battery_level - 1;
        bt_bas_set_battery_level(battery_level);
}

/* 传感器初始化函数 */
static int sensor_init(void)
{
        printk("Initializing FXLS**AF sensor...\n");/* 添加调试信息 */
       
        /* 使用标签方式获取传感器设备 */
        fxls**_dev = DEVICE_DT_GET(ACCEL_NODE);

        if (!fxls**_dev) {
                printk("Failed to get FXLS**AF sensor device\n");
                return -ENODEV;
        }

        if (!device_is_ready(fxls**_dev)) {
                printk("FXLS**AF sensor not ready\n");
                return -ENODEV;
        }

        printk("FXLS**AF sensor initialized successfully\n");
        return 0;
}

/* 手势识别优化版 */
int main(void)
{
    int err;

    printk("Starting FRDM-MCXW71 BLE application with gesture support\n");

    /* 初始化延时工作 */
    k_work_init_delayable(&adv_restart_work, adv_restart_handler);
   
    /* 初始化传感器工作 */
    k_work_init(&sensor_work, sensor_work_handler);

    /* 初始化蓝牙 */
    err = bt_enable(NULL);
    if (err) {
      printk("Bluetooth init failed (err: %d)\n", err);
      return 0;
    }

    /* 初始化传感器 */
    err = sensor_init();
    if (err) {
      printk("Sensor init failed (err: %d)\n", err);
    }

    /* 注册回调函数 */
    bt_ready();

    /* 主循环 - 手势识别优化 */
    printk("Entering gesture recognition mode\n");
    while (1) {
      /* 对于手势识别,建议使用20-50ms间隔 */
      k_sleep(K_MSEC(20)); /* 50Hz采样率,适合大多数手势识别 */

      /* 读取传感器数据 - 只有在连接时才会实际执行 */
      if (is_connected && fxls**_dev) {
            k_work_submit(&sensor_work);
      }

      /* 电池电量更新 - 可以降低频率 */
      static uint32_t battery_counter = 0;
      battery_counter++;
      if (battery_counter >= 25) { /* 每500ms更新一次电池电量 */
            bas_notify();
            battery_counter = 0;
      }
    }
    return 0;
}
```

主要是思路是,建立蓝牙通信,创建一个FXLS**AF的数据采集任务,当有客户端连接上来时,将其数据发送。
【FRDM-IMX93开发环境】
操作系统为debian12
代码:

```
#!/usr/bin/env python3
# trajectory_gesture_recognition_client.py
# 轨迹手势识别客户端

import asyncio
import struct
import sys
import os
from bleak import BleakScanner, BleakClient
from datetime import datetime
import math
from collections import deque

# 服务和特征UUID(与Zephyr设备中定义的UUID匹配)
SERVICE_UUID = "12345678-1234-5678-1234-56789abcdef0"
SENSOR_DATA_CHAR_UUID = "12345678-1234-5678-1234-56789abcdef5"


class TrajectoryGestureRecognizer:
    def __init__(self):
      self.client = None
      self.device_address = None
      self.is_connected = False

      # 数据缓冲区用于轨迹分析
      self.data_buffer = deque(maxlen=10)# 10个样本的滑动窗口
      self.history_buffer = deque(maxlen=20)# 20个样本的历史缓冲区

      # 手势识别参数
      self.movement_threshold = 300# 运动检测阈值 (mg)
      self.gesture_threshold = 800# 手势识别阈值 (mg)
      self.quiet_threshold = 150# 静止状态阈值 (mg)

      # 时间控制
      self.last_gesture_time = 0
      self.gesture_cooldown = 1.0# 手势冷却时间(秒)

      # 统计信息
      self.data_count = 0
      self.gesture_count = {"UP": 0, "DOWN": 0, "LEFT": 0, "RIGHT": 0}

    async def scan_for_devices(self, timeout=10.0):
      """扫描蓝牙设备"""
      print("正在扫描蓝牙设备...")

      try:
            devices = await BleakScanner.discover(timeout=timeout)

            target_devices = []
            for device in devices:
                if device.name and "NXP" in device.name:
                  target_devices.append(device)
                  print(f"发现目标设备: {device.name} [{device.address}]")
                elif device.name:
                  print(f"发现设备: {device.name} [{device.address}]")

            if target_devices:
                selected_device = target_devices
                self.device_address = selected_device.address
                print(f"\n自动选择设备: {selected_device.name}")
                return True
            else:
                print("\n未找到NXP设备,请确保设备已开启并靠近电脑")
                return False

      except Exception as e:
            print(f"扫描设备时出错: {e}")
            return False

    def detect_movement(self, x, y, z):
      """检测是否有明显的运动"""
      magnitude = math.sqrt(x * x + y * y + z * z)
      # 考虑重力影响,减去1g (约1000mg)
      motion = abs(magnitude - 1000)
      return motion > self.movement_threshold

    def analyze_trajectory(self):
      """分析轨迹数据以识别手势"""
      if len(self.history_buffer) < 10:
            return None

      # 获取开始和结束的几帧数据
      start_frames = list(self.history_buffer)[:5]
      end_frames = list(self.history_buffer)[-5:]

      # 计算平均值
      start_avg_x = sum( for frame in start_frames]) / len(start_frames)
      start_avg_y = sum( for frame in start_frames]) / len(start_frames)

      end_avg_x = sum( for frame in end_frames]) / len(end_frames)
      end_avg_y = sum( for frame in end_frames]) / len(end_frames)

      # 计算变化量
      delta_x = end_avg_x - start_avg_x
      delta_y = end_avg_y - start_avg_y

      # 判断主要运动方向
      abs_delta_x = abs(delta_x)
      abs_delta_y = abs(delta_y)

      # 检查运动幅度是否足够大
      if abs_delta_x < self.gesture_threshold and abs_delta_y < self.gesture_threshold:
            return None

      # 识别手势方向
      if abs_delta_x > abs_delta_y:
            # 水平运动为主
            if abs_delta_x > self.gesture_threshold:
                return "RIGHT" if delta_x > 0 else "LEFT"
      else:
            # 垂直运动为主
            if abs_delta_y > self.gesture_threshold:
                return "UP" if delta_y > 0 else "DOWN"

      return None

    def is_quiet_state(self, x, y, z):
      """检测是否处于静止状态"""
      magnitude = math.sqrt(x * x + y * y + z * z)
      motion = abs(magnitude - 1000)
      return motion < self.quiet_threshold

    def data_handler(self, sender, data):
      """处理接收到的传感器数据"""
      try:
            self.data_count += 1

            # 解析传感器数据
            if len(data) >= 6:
                x, y, z = struct.unpack('<hhh', data[:6])

                # 将数据添加到缓冲区
                current_time = datetime.now().timestamp()
                self.data_buffer.append((x, y, z, current_time))
                self.history_buffer.append((x, y, z, current_time))

                # 检测运动状态
                if self.detect_movement(x, y, z):
                  # 检查是否可以识别手势(冷却时间已过)
                  if current_time - self.last_gesture_time > self.gesture_cooldown:
                        # 分析轨迹
                        gesture = self.analyze_trajectory()
                        if gesture:
                            self.last_gesture_time = current_time
                            self.gesture_count += 1

                            timestamp = datetime.now().strftime("%H:%M:%S")
                            print(f"[{timestamp}] 手势识别: {gesture:5s} | "
                                  f"ΔX:{x:6d} ΔY:{y:6d} ΔZ:{z:6d}")

                            # 显示统计信息
                            total_gestures = sum(self.gesture_count.values())
                            if total_gestures % 5 == 0 and total_gestures > 0:
                              print(f"统计 - 上:{self.gesture_count['UP']} 下:{self.gesture_count['DOWN']} "
                                    f"左:{self.gesture_count['LEFT']} 右:{self.gesture_count['RIGHT']}")

      except Exception as e:
            print(f"处理数据时出错: {e}")

    async def connect_and_monitor(self):
      """连接设备并开始监控传感器数据"""
      if not self.device_address:
            print("错误: 未指定设备地址")
            return False

      try:
            print(f"\n正在连接到设备 [{self.device_address}]...")
            self.client = BleakClient(self.device_address)

            # 连接设备
            await self.client.connect(timeout=15.0)
            self.is_connected = True
            print("✓ 连接成功!")

            # 查找传感器数据特征
            services = self.client.services
            sensor_char = None

            print("\n查找传感器服务和特征...")
            for service in services:
                if str(service.uuid).lower() == SERVICE_UUID.lower():
                  print(f"找到传感器服务: {service.uuid}")
                  for char in service.characteristics:
                        if str(char.uuid).lower() == SENSOR_DATA_CHAR_UUID.lower():
                            print(f"找到传感器数据特征: {char.uuid}")
                            sensor_char = char
                            break

            if not sensor_char:
                print("✗ 未找到传感器数据特征")
                return False

            # 开始定期读取数据
            print("\n开始轨迹手势识别 (按 Ctrl+C 停止)...")
            print("手势说明:")
            print("LEFT- 从右向左挥动")
            print("RIGHT - 从左向右挥动")
            print("UP    - 从下向上挥动")
            print("DOWN- 从上向下挥动")
            print("-" * 50)

            while self.is_connected and self.client.is_connected:
                try:
                  # 读取传感器数据
                  data = await self.client.read_gatt_char(sensor_char.uuid)
                  self.data_handler(None, data)

                except Exception as e:
                  print(f"读取传感器数据时出错: {e}")

                # 以50Hz频率读取数据(20ms间隔)
                await asyncio.sleep(0.02)

      except KeyboardInterrupt:
            print(f"\n\n用户中断程序")
      except Exception as e:
            print(f"监控过程中出错: {e}")
      finally:
            await self.disconnect()

    async def disconnect(self):
      """断开连接"""
      try:
            if self.client and self.is_connected:
                print("正在断开连接...")
                await self.client.disconnect()
                print("✓ 已断开连接")
      except Exception as e:
            print(f"断开连接时出错: {e}")
      finally:
            self.is_connected = False

    async def run(self):
      """运行主程序"""
      print("=== FRDM-MCXW71 轨迹手势识别客户端 ===")
      print("识别四个轨迹手势: 上(UP)、下(DOWN)、左(LEFT)、右(RIGHT)")

      try:
            # 扫描设备
            if not await self.scan_for_devices(timeout=10.0):
                return

            # 连接并监控
            await self.connect_and_monitor()

      except KeyboardInterrupt:
            print(f"\n\n程序被用户中断")
      except Exception as e:
            print(f"运行时出错: {e}")
      finally:
            # 显示最终统计
            total_gestures = sum(self.gesture_count.values())
            if total_gestures > 0:
                print(f"\n最终统计:")
                print(f"总数据点: {self.data_count}")
                print(f"手势识别: 上:{self.gesture_count['UP']} 下:{self.gesture_count['DOWN']} "
                      f"左:{self.gesture_count['LEFT']} 右:{self.gesture_count['RIGHT']}")

    def __del__(self):
      """析构函数,确保正确清理"""
      if self.is_connected:
            try:
                asyncio.run(self.disconnect())
            except:
                pass


async def main():
    """主函数"""
    recognizer = TrajectoryGestureRecognizer()
    await recognizer.run()


if __name__ == "__main__":
    try:
      asyncio.run(main())
    except KeyboardInterrupt:
      print("\n程序退出")
    except Exception as e:
      print(f"程序执行出错: {e}")
      sys.exit(1)
```

【实验效果】

=== FRDM-MCXW71 轨迹手势识别客户端 ===
识别四个轨迹手势: 上(UP)、下(DOWN)、左(LEFT)、右(RIGHT)
正在扫描蓝牙设备...
发现目标设备: NXP_FLX_DEMO
发现设备: NZ78dLxw+LpDM3MoBOy0QG860

自动选择设备: NXP_FLX_DEMO

正在连接到设备 ...
✓ 连接成功!

查找传感器服务和特征...
找到传感器服务: 12345678-1234-5678-1234-56789abcdef0
找到传感器数据特征: 12345678-1234-5678-1234-56789abcdef5

开始轨迹手势识别 (按 Ctrl+C 停止)...
手势说明:
LEFT- 从右向左挥动
RIGHT - 从左向右挥动
UP    - 从下向上挥动
DOWN- 从上向下挥动
--------------------

手势识别: DOWN| ΔX: -1053 ΔY: -3342 ΔZ: 15121
手势识别: RIGHT | ΔX:9461 ΔY:-488 ΔZ:3102
手势识别: RIGHT | ΔX:9423 ΔY:    28 ΔZ:2595
手势识别: LEFT| ΔX: -1197 ΔY:-11990 ΔZ:2777
手势识别: DOWN| ΔX:   890 ΔY: -9988 ΔZ:2049
【经验心得】
使用zephyr可以快速的实现蓝牙功能,其与IMX93的配置也是非常好。

页: [1]
查看完整版本: 【FRDM-MCXW71】【FRDM-IMX93】用蓝牙打通任督二脉

Baidu
map