【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]