前面已经尝试过对接IOT Toolbox的sensor模块,但发现貌似nxp的上位机有一定的限制,因此不得不找到到这个上位机的限制到底是什么。
尝试过程
1. 坛子里的方法,使用peripheral_hr作参考,确实可以跑通,但这个例程是心率计的,单位不太对,即使使用界面的电池电量上报,湿度范围也会丢失精度,电池上报只能作为备选。
2. 之后在github上找到了nxp的官方对接zepyhr的代码仓库nxp_zsdk,便尝试使用里面的方案去对接。优先尝试的是wireless_uart,但对接后发现貌似IOT Toolbox写的有毛病,明明已经按照标准实现了,nrf connect也能准确拿到内容了,偏偏IOT Toolbox无法拿到下面报上来的数据。也就是说,IOT Toolbox的wireless uart模块,只实现了向下写的接口,并没有实现向上读的接口
3. 尝试使用Thread shell模块去通信(还是可以用wireless uart的方法连上),发现问题还是一样,此路径目前看走不通
4. 尝试使用peripheral_ht模块,发现确实可以对接上温度接口,此时的温度上报信息精度也和底层读到的一致,因此先使用次接口将温度信息上报
5. 湿度信息,既然sensor那块走不通,通过尝试把心跳数据范围改为0~65535来实现上报,这步尝试发现可行,IOT Toolbox可以读到这个范围的数据。
6. 另外发现一个问题,貌似BAS虽然一直上报,但是IOT Toolbox上只能做到只有连上时会更新,后面就一直不变,感觉像是nxp的上位机没做好一样。
代码修改
经过了上面的尝试,最终采用的策略是:温度信息采用HTS上报,湿度信息使用HRS上报,上报数据除以100即为传感器读到的湿度。
此外,由于IOT Toolbox温度和心跳界面都有电量信息,因此默认把电量信息也上报了。
最终代码结构
(.venv) oxlm@oxlm-ThinkPad-X280:~/zephyrproject/zephyr/app$ tree . ├── CMakeLists.txt // 里面主要的修改就是把文件改成了按宏去选择是否编译了 ├── Kconfig // 新增一些app所使用到的宏 ├── prj.conf // 工程配置 └── src ├── ess.c // ess服务从main里面抽出,貌似后面对接微信就需要走这个服务 ├── ess.h ├── hts.c // hts服务,温度信息就是使用这个服务实现的 ├── hts.h ├── main.c // dis, bas,hrs服务走的是zephyr蓝牙协议栈实现的部分,就直接在main里面调用了
最终代码
ess.c
#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "ess.h"
/* Notification enable flags for temperature and humidity characteristics */
static bool temp_notify_enabled;
static bool hum_notify_enabled;
struct bt_ess_cb *cb = NULL;
/* Read callback for signed 16-bit temperature characteristic values */
static ssize_t read_temperature(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct sensor_value temp;
int ret = cb->read_temperature(&temp);
if (ret) {
printk("BLE read: Failed to read temperature sensor (err %d)\n", ret);
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
}
int16_t temp_raw = temp.val1 * 100 + temp.val2 / 10000;
int16_t value = sys_cpu_to_le16(temp_raw);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, sizeof(value));
}
/* Read callback for unsigned 16-bit humidity characteristic values */
static ssize_t read_humidity(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct sensor_value hum;
int ret = cb->read_humidity(&hum);
if (ret) {
printk("BLE read: Failed to read humidity sensor (err %d)\n", ret);
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
}
uint16_t hum_raw = hum.val1 * 100 + hum.val2 / 10000;
uint16_t value = sys_cpu_to_le16(hum_raw);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, sizeof(value));
}
/* Callback when the temperature characteristic CCC descriptor changes */
static void temp_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
temp_notify_enabled = (value == BT_GATT_CCC_NOTIFY);
}
/* Callback when the humidity characteristic CCC descriptor changes */
static void hum_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
hum_notify_enabled = (value == BT_GATT_CCC_NOTIFY);
}
/* Environmental Sensing Service (ESS) with temperature and humidity characteristics.
* This service exposes sensor data over BLE GATT, allowing clients to read current
* values and subscribe to notifications for real-time updates.
*/
BT_GATT_SERVICE_DEFINE(ess_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_ESS),
BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ, read_temperature, NULL, NULL),
BT_GATT_CCC(temp_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
BT_GATT_CHARACTERISTIC(BT_UUID_HUMIDITY,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ, read_humidity, NULL, NULL),
BT_GATT_CCC(hum_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), );
/* Send BLE notification for temperature if enabled. */
void notify_temperature(struct sensor_value temp)
{
if (temp_notify_enabled) {
int16_t temp_raw = temp.val1 * 100 + temp.val2 / 10000;
int16_t value = sys_cpu_to_le16(temp_raw);
bt_gatt_notify(NULL, &ess_svc.attrs[2], &value, sizeof(value));
}
}
/* Send BLE notification for humidity if enabled. */
void notify_humidity(struct sensor_value hum)
{
if (hum_notify_enabled) {
uint16_t hum_raw = hum.val1 * 100 + hum.val2 / 10000;
uint16_t value = sys_cpu_to_le16(hum_raw);
bt_gatt_notify(NULL, &ess_svc.attrs[5], &value, sizeof(value));
}
}
void ess_init(struct bt_ess_cb *init_param)
{
cb = init_param;
printk("ESS Service Initialized\n");
}ess.h
#ifndef APP_ESS_H
#define APP_ESS_H
#include <zephyr/bluetooth/bluetooth.h>
struct bt_ess_cb {
int (*read_temperature)(struct sensor_value *temp);
int (*read_humidity)(struct sensor_value *hum);
};
void ess_init(struct bt_ess_cb *cb);
/* Send BLE notification for humidity if enabled. */
void notify_humidity(struct sensor_value hum);
/* Send BLE notification for temperature if enabled. */
void notify_temperature(struct sensor_value temp);
#endifhts.c
/** @file
* @brief HTS Service sample
*/
/*
* Copyright (c) 2020 SixOctets Systems
* Copyright (c) 2019 Aaron Tsui <aaron.tsui@outlook.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.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>
static uint8_t simulate_htm;
static uint8_t indicating;
static struct bt_gatt_indicate_params ind_params;
static void htmc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
simulate_htm = (value == BT_GATT_CCC_INDICATE) ? 1 : 0;
}
static void indicate_cb(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err)
{
// printk("Indication %s\n", err != 0U ? "fail" : "success");
}
static void indicate_destroy(struct bt_gatt_indicate_params *params)
{
// printk("Indication complete\n");
indicating = 0U;
}
/* Health Thermometer Service Declaration */
BT_GATT_SERVICE_DEFINE(hts_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_HTS),
BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE,
BT_GATT_PERM_NONE, NULL, NULL, NULL),
BT_GATT_CCC(htmc_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
/* more optional Characteristics */
);
void hts_init(void)
{
// Do nothing
}
void hts_indicate(struct sensor_value temp_value)
{
if (!simulate_htm) {
return;
}
static uint8_t htm[5];
static double temperature = 20U;
uint32_t mantissa;
uint8_t exponent;
if (indicating) {
return;
}
temperature = sensor_value_to_double(&temp_value);
printf("temperature is %gC\n", temperature);
mantissa = (uint32_t)(temperature * 100);
exponent = (uint8_t)-2;
htm[0] = 0; /* temperature in celsius */
sys_put_le24(mantissa, (uint8_t *)&htm[1]);
htm[4] = exponent;
ind_params.attr = &hts_svc.attrs[2];
ind_params.func = indicate_cb;
ind_params.destroy = indicate_destroy;
ind_params.data = &htm;
ind_params.len = sizeof(htm);
if (bt_gatt_indicate(NULL, &ind_params) == 0) {
indicating = 1U;
}
}hts.h
/** @file
* @brief HTS Service sample
*/
/*
* Copyright (c) 2019 Aaron Tsui <aaron.tsui@outlook.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef __cplusplus
extern "C" {
#endif
void hts_init(void);
void hts_indicate(struct sensor_value temp_value);
#ifdef __cplusplus
}
#endifmain.c
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/bluetooth/services/hrs.h>
#include "hts.h"
#include "ess.h"
static bool hrf_ntf_enabled;
/* BLE device name advertised by this peripheral, sourced from CONFIG_BT_DEVICE_NAME */
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
/* Advertisement flags for LE general discoverable mode, BR/EDR disabled */
static const uint8_t ad_flags[] = {BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR};
static const uint8_t sv_flags[] = {
BT_UUID_16_ENCODE(BT_UUID_BAS_VAL), BT_UUID_16_ENCODE(BT_UUID_DIS_VAL),
BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
#ifdef CONFIG_APP_ESS_SUPPORT
BT_UUID_16_ENCODE(BT_UUID_ESS_VAL),
#endif
#ifdef CONFIG_APP_HTS_SUPPORT
BT_UUID_16_ENCODE(BT_UUID_HTS_VAL),
#endif
};
static const struct bt_data ad[] = {
BT_DATA(BT_DATA_FLAGS, ad_flags, sizeof(ad_flags)),
BT_DATA(BT_DATA_UUID16_ALL, sv_flags, sizeof(sv_flags)),
#if defined(CONFIG_BT_EXT_ADV)
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, sizeof(DEVICE_NAME) - 1),
#endif /* CONFIG_BT_EXT_ADV */
};
#if !defined(CONFIG_BT_EXT_ADV)
static const struct bt_data sd[] = {
/* Scan response data - include device name here like peripheral_hr */
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, sizeof(DEVICE_NAME) - 1),
};
#endif /* CONFIG_BT_EXT_ADV */
/* Device tree bindings for AHT20 temperature and humidity virtual sensors */
static const struct device *temp_dev = DEVICE_DT_GET_ONE(aosong_aht20_temp);
static const struct device *hum_dev = DEVICE_DT_GET_ONE(aosong_aht20_hum);
/* Fetch the latest temperature value from the AHT20 sensor.
* Returns 0 on success or a negative error code if the fetch fails.
*/
static int read_sensor_temperature(struct sensor_value *temp)
{
int ret;
if (!temp) {
return -1;
}
ret = sensor_sample_fetch(temp_dev);
if (ret) {
return ret;
}
sensor_channel_get(temp_dev, SENSOR_CHAN_AMBIENT_TEMP, temp);
return 0;
}
/* Fetch the latest humidity value from the AHT20 sensor.
* Returns 0 on success or a negative error code if the fetch fails.
*/
static int read_sensor_humidity(struct sensor_value *hum)
{
int ret;
if (!hum) {
return -1;
}
ret = sensor_sample_fetch(hum_dev);
if (ret) {
return ret;
}
sensor_channel_get(hum_dev, SENSOR_CHAN_HUMIDITY, hum);
return 0;
}
/* Log the temperature value to the console. */
static void log_temperature(struct sensor_value temp)
{
int16_t temp_raw = temp.val1 * 100 + temp.val2 / 10000;
printk("Temperature: %d.%02d C\n", temp_raw / 100, abs(temp_raw % 100));
}
/* Log the humidity value to the console. */
static void log_humidity(struct sensor_value hum)
{
uint16_t hum_raw = hum.val1 * 100 + hum.val2 / 10000;
printk("Humidity: %d.%02d %%\n", hum_raw / 100, hum_raw % 100);
}
/* Initialize and verify AHT20 temperature and humidity sensors are ready.
* Returns 0 on success or a negative error code if sensors are not available.
*/
static int sensors_init(void)
{
if (!device_is_ready(temp_dev)) {
printk("AHT20 temperature sensor not ready\n");
return -ENODEV;
}
if (!device_is_ready(hum_dev)) {
printk("AHT20 humidity sensor not ready\n");
return -ENODEV;
}
return 0;
}
enum {
STATE_DISCONNECTED,
};
static ATOMIC_DEFINE(state, 1);
struct bt_conn *g_wu_conn = NULL;
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("Disconnected, reason 0x%02x\n", reason);
(void)atomic_set_bit(state, STATE_DISCONNECTED);
if (g_wu_conn) {
bt_conn_unref(g_wu_conn);
g_wu_conn = NULL;
}
}
static void check_for_advising(bool force)
{
int err;
if (!force && (!atomic_test_and_clear_bit(state, STATE_DISCONNECTED))) {
return;
}
printk("Advertising as: %s\n", DEVICE_NAME);
#if !defined(CONFIG_BT_EXT_ADV)
printk("Starting Legacy Advertising (connectable and scannable)\n");
err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return;
}
#else /* CONFIG_BT_EXT_ADV */
struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT,
.sid = 0U,
.secondary_max_skip = 0U,
.options = (BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_CODED),
.interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
.interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
.peer = NULL,
};
struct bt_le_ext_adv *adv;
printk("Creating a Coded PHY connectable non-scannable advertising set\n");
err = bt_le_ext_adv_create(&adv_param, NULL, &adv);
if (err) {
printk("Failed to create Coded PHY extended advertising set (err "
"%d)\n",
err);
printk("Creating a non-Coded PHY connectable non-scannable "
"advertising set\n");
adv_param.options &= ~BT_LE_ADV_OPT_CODED;
err = bt_le_ext_adv_create(&adv_param, NULL, &adv);
if (err) {
printk("Failed to create extended advertising set (err "
"%d)\n",
err);
return;
}
}
printk("Setting extended advertising data\n");
err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Failed to set extended advertising data (err %d)\n", err);
return;
}
printk("Starting Extended Advertising (connectable non-scannable)\n");
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
printk("Failed to start extended advertising set (err %d)\n", err);
return;
}
#endif /* CONFIG_BT_EXT_ADV */
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("Connection failed, err 0x%02x\n", err);
} else {
printk("Connected\n");
g_wu_conn = bt_conn_ref(conn); // 保存连接
}
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.disconnected = disconnected,
.connected = connected,
};
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Pairing cancelled: %s\n", addr);
}
static struct bt_conn_auth_cb auth_cb_display = {
.cancel = auth_cancel,
};
static void hrs_ntf_changed(bool enabled)
{
hrf_ntf_enabled = enabled;
printk("HRS notification status changed: %s\n", enabled ? "enabled" : "disabled");
}
static struct bt_hrs_cb hrs_cb = {
.ntf_changed = hrs_ntf_changed,
};
static void hrs_notify(uint16_t heartrate)
{
if (hrf_ntf_enabled) {
bt_hrs_notify(heartrate);
}
}
static void bas_notify(void)
{
uint8_t battery_level = bt_bas_get_battery_level();
battery_level--;
if (!battery_level) {
battery_level = 100U;
}
bt_bas_set_battery_level(battery_level);
}
#ifdef CONFIG_APP_ESS_SUPPORT
static struct bt_ess_cb ess_cb = {
.read_temperature = read_sensor_temperature,
.read_humidity = read_sensor_humidity,
};
#endif
/* Initialize Bluetooth stack and start advertising as a connectable peripheral.
* Returns 0 on success or a negative error code on failure.
*/
static int bluetooth_init(void)
{
int ret;
ret = bt_enable(NULL);
if (ret) {
printk("Bluetooth init failed (err %d)\n", ret);
return ret;
}
printk("Bluetooth initialized\n");
bt_conn_auth_cb_register(&auth_cb_display);
bt_hrs_cb_register(&hrs_cb);
#ifdef CONFIG_APP_ESS_SUPPORT
ess_init(&ess_cb);
#endif
#ifdef CONFIG_APP_HTS_SUPPORT
hts_init();
#endif
check_for_advising(true);
return 0;
}
/* Main sensor reading and notification loop.
* Continuously reads sensor values, logs them, and notifies BLE clients every second.
* Handles sensor read failures independently.
*/
static void run_sensor_loop(void)
{
struct sensor_value hum, temp;
while (1) {
check_for_advising(false);
bas_notify();
int8_t temp_getted = false, hum_getted = false;
if (read_sensor_temperature(&temp) == 0) {
temp_getted = true;
log_temperature(temp);
} else {
printk("Temperature fetch failed\n");
}
if (read_sensor_humidity(&hum) == 0) {
hum_getted = true;
log_humidity(hum);
} else {
printk("Humidity fetch failed\n");
}
if (temp_getted) {
#ifdef CONFIG_APP_ESS_SUPPORT
notify_temperature(temp);
#endif
#ifdef CONFIG_APP_HTS_SUPPORT
hts_indicate(temp);
#endif
}
if (hum_getted) {
#ifdef CONFIG_APP_ESS_SUPPORT
notify_humidity(hum);
#endif
uint16_t hum_raw = hum.val1 * 100 + hum.val2 / 10000;
hrs_notify(hum_raw);
}
k_sleep(K_SECONDS(1));
}
}
/* Application entry point.
* Initializes sensors and Bluetooth, then enters the sensor monitoring loop.
* Exits on initialization failure.
*/
int main(void)
{
int ret;
ret = sensors_init();
if (ret) {
return 0;
}
ret = bluetooth_init();
if (ret) {
return 0;
}
run_sensor_loop();
return 0;
}CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(aht20_app)
file(GLOB app_sources src/main.c)
if(CONFIG_APP_HTS_SUPPORT)
file(GLOB hts_sources src/hts.c)
list(APPEND app_sources ${hts_sources})
endif()
if(CONFIG_APP_ESS_SUPPORT)
file(GLOB ess_sources src/ess.c)
list(APPEND app_sources ${ess_sources})
endif()
target_sources(app PRIVATE ${app_sources})
target_include_directories(app PRIVATE include)Kconfig
# Copyright (c) 2021 oxlm # SPDX-License-Identifier: Apache-2.0 mainmenu "Bluetooth: function table" config APP_HTS_SUPPORT bool "App set to Heart Rate" default n help If enabled, the app start with Heart Rate. config APP_ESS_SUPPORT bool "App set to ess service" default n help If enabled, the app start with ess service. source "Kconfig.zephyr"
prj.conf
CONFIG_SENSOR=y CONFIG_I2C=y CONFIG_SENSOR_AHT20=y CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="FRDM-MCXW72-AHT20" CONFIG_BT_GATT_NOTIFY_MULTIPLE=y CONFIG_DHT20=n CONFIG_BT_SMP=y CONFIG_BT_HRS=y CONFIG_BT_BAS=y CONFIG_BT_DIS=y CONFIG_BT_DIS_PNP=n CONFIG_APP_HTS_SUPPORT=y CONFIG_APP_ESS_SUPPORT=y
额外修改部分
为了让hrs上报范围变宽,直接把hrs.c的实现给修改了,具体修改如下:
int bt_hrs_notify(uint16_t heartrate)
{
int rc;
#if 1
static uint8_t hrm[3];
hrm[0] = 0x01; /* uint16, sensor contact */
hrm[2] = heartrate / 256;
hrm[1] = heartrate % 256;
#else
static uint8_t hrm[2];
hrm[0] = 0x06; /* uint8, sensor contact */
hrm[1] = heartrate;
#endif
rc = bt_gatt_notify(NULL, &hrs_svc.attrs[1], &hrm, sizeof(hrm));
return rc == -ENOTCONN ? 0 : rc;
}这么修改的依据是这个数据格式有这种要求:
Flag定义: Bit0: 心率值格式 0=UINT8(1字节),1=UINT16(2字节) Bit1~2: 传感器接触状态 00=不支持接触检测 01=支持但未检测到 11=支持且已检测到 Bit3: 能量消耗 0=无,1=有(2字节 UINT16,单位 kJ) Bit4: RR间隔 0=无,1=有(每个 2字节 UINT16,单位 1/1024 秒) Bit5~7: 保留,必须为0 具体组包格式为: [Flags][心率值][能量消耗(可选)][RR间隔(可选)...] 其中: Flag占1字节 心率值根据flag的bit0决定是1字节还是2字节 能量消耗和RR间隔,都占2字节,分别由bit3和bit4决定是否上报
运行效果

其中,温度变化是因为我手指捏住了传感器,湿度上升是因为对着传感器吹气了。
下一步计划
1. 尝试了适配电源管理部分,但是启用电源管理(CONFIG_PM)后,连串口打印都不见了,根本不知道板子跑到哪去了,由于之前未适配过这块,也不清楚这块的逻辑,也就暂时不花精力去折腾FRDM-MCXW72对zepyhr的低功耗这块的适配了。
2. 计划研究一下微信读取ble ess数据的逻辑,搞个微信显示看看
我要赚赏金
