首先就是先配置好Zephyr的蓝牙,我的配置如下
CONFIG_CONSOLE=y CONFIG_SERIAL=y CONFIG_PRINTK=y CONFIG_SYS_CLOCK_EXISTS=y CONFIG_SENSOR=y CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_EXT_ADV=n CONFIG_BT_DEVICE_NAME="MCXW71_TH" CONFIG_BT_RX_STACK_SIZE=2048 CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_MAIN_STACK_SIZE=4096
然后就是,应用层代码的编写
主要流程就是这样的:
启动蓝牙 ->蓝牙广播 MCXW71_TH->手机连接 → 订阅 FFF1->每 2 秒读一次温湿度->打包成 4 字节->通过 bt_gatt_notify 推送给手机
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#define DEVICE_NAME "MCXW71_TH"
#define DHT22_NODE DT_INST(0, aosong_dht22)
#define BT_UUID_TH_SERVICE BT_UUID_DECLARE_16(0xFFF0)
#define BT_UUID_TH_CHAR BT_UUID_DECLARE_16(0xFFF1)
static struct bt_conn *g_conn = NULL;
const struct device *dht22 = DEVICE_DT_GET(DHT22_NODE);
static bool notify_subscribed = false;
static void ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
notify_subscribed = (value == BT_GATT_CCC_NOTIFY);
printk("手机订阅状态: %s\n", notify_subscribed ? "已订阅" : "未订阅");
}
static ssize_t th_read_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
uint8_t dummy[4] = {0};
return bt_gatt_attr_read(conn, attr, buf, len, offset, dummy, sizeof(dummy));
}
BT_GATT_SERVICE_DEFINE(th_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_TH_SERVICE),
BT_GATT_CHARACTERISTIC(BT_UUID_TH_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
th_read_cb, NULL, NULL),
BT_GATT_CCC(ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, sizeof(DEVICE_NAME)-1),
};
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("连接失败\n");
return;
}
g_conn = conn;
printk("蓝牙连接成功!\n");
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
g_conn = NULL;
notify_subscribed = false;
printk("蓝牙断开连接\n");
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
static void read_dht22(void)
{
struct sensor_value temp, humi;
uint8_t th_data[4];
if (!device_is_ready(dht22)) return;
if (sensor_sample_fetch(dht22) != 0) return;
sensor_channel_get(dht22, SENSOR_CHAN_AMBIENT_TEMP, &temp);
sensor_channel_get(dht22, SENSOR_CHAN_HUMIDITY, &humi);
printk("温度: %d.%d°C | 湿度: %d.%d%%RH\n",
temp.val1, (temp.val2 / 10000) % 100,
humi.val1, (humi.val2 / 10000) % 100);
if (g_conn && notify_subscribed) {
th_data[0] = temp.val1;
th_data[1] = (temp.val2 / 10000) % 100;
th_data[2] = humi.val1;
th_data[3] = (humi.val2 / 10000) % 100;
bt_gatt_notify(g_conn, &th_service.attrs[1], th_data, sizeof(th_data));
}
}
static void bt_ready(int err)
{
if (err) {
printk("蓝牙初始化失败\n");
return;
}
printk("蓝牙初始化完成\n");
printk("可连接广播运行中\n");
const struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
BT_LE_ADV_OPT_CONN,
BT_GAP_ADV_FAST_INT_MIN_2,
BT_GAP_ADV_FAST_INT_MAX_2,
NULL
);
bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), NULL, 0);
}
static void bt_delayed_init(struct k_work *work)
{
printk("初始化蓝牙中...\n");
bt_enable(bt_ready);
}
K_WORK_DELAYABLE_DEFINE(bt_work, bt_delayed_init);
int main(void)
{
k_work_schedule(&bt_work, K_SECONDS(1));
while (1) {
printk("程序运行中...\n");
read_dht22();
k_sleep(K_SECONDS(2));
}
}然会就是蓝牙APP的编写,蓝牙APP的话,主要就是解析数据
流程是这样的
1.打开 APP → 申请蓝牙权限
2.点击连接 → 扫描蓝牙设备
3.选择 MCXW71_TH
4.连接成功 → 发现 FFF0 服务
5.开启 Notify 订阅
6.Zephyr 每 2 秒发送 4 字节数据
7.APP 解析 → 显示温度、湿度
代码如下
package com.example.w71;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private static final UUID SERVICE_UUID = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
private static final UUID CHAR_UUID = UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
private static final UUID CCCD_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private static final int REQUEST_BLE_DEVICE = 1;
private TextView tv_temp, tv_humi, tv_status;
private Button btn_connect;
private BluetoothAdapter bluetoothAdapter;
private BluetoothGatt bluetoothGatt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_temp = findViewById(R.id.tv_temp);
tv_humi = findViewById(R.id.tv_humi);
tv_status = findViewById(R.id.tv_system_status);
btn_connect = findViewById(R.id.btn_connect);
BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = manager.getAdapter();
// 权限
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.ACCESS_FINE_LOCATION
}, 100);
btn_connect.setOnClickListener(v -> {
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "请打开蓝牙", Toast.LENGTH_SHORT).show();
return;
}
disconnectBLE();
Intent intent = new Intent(MainActivity.this, BluetoothListActivity.class);
startActivityForResult(intent, REQUEST_BLE_DEVICE);
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_BLE_DEVICE && resultCode == RESULT_OK) {
String address = data.getStringExtra("device_address");
connectBLE(address);
}
}
private void connectBLE(String address) {
try {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
bluetoothGatt = device.connectGatt(this, false, gattCallback);
tv_status.setText("正在连接...");
} catch (Exception e) {
tv_status.setText("连接失败");
Toast.makeText(this, "连接失败", Toast.LENGTH_SHORT).show();
}
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == 2) { // 已连接
runOnUiThread(() -> {
tv_status.setText("连接成功,正在发现服务...");
tv_status.setTextColor(Color.GREEN);
});
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
gatt.discoverServices();
} else {
runOnUiThread(() -> {
tv_status.setText("已断开连接");
tv_status.setTextColor(Color.RED);
clearData();
});
disconnectBLE();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
BluetoothGattService service = gatt.getService(SERVICE_UUID);
if (service == null) {
runOnUiThread(() -> tv_status.setText("未找到服务"));
return;
}
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHAR_UUID);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
// 开启通知
gatt.setCharacteristicNotification(characteristic, true);
// 写入CCCD开启Notify
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
runOnUiThread(() -> tv_status.setText("已订阅数据,等待接收..."));
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
byte[] data = characteristic.getValue();
if (data == null || data.length < 4) return;
// 解析数据
int tempInt = data[0] & 0xFF;
int tempDec = data[1] & 0xFF;
int humiInt = data[2] & 0xFF;
int humiDec = data[3] & 0xFF;
float temp = tempInt + tempDec / 100.0f;
float humi = humiInt + humiDec / 100.0f;
// 更新UI
runOnUiThread(() -> {
tv_temp.setText(String.format("%.1f ℃", temp));
tv_humi.setText(String.format("%.1f %%RH", humi));
tv_status.setText("数据接收中...");
});
}
};
private void disconnectBLE() {
if (bluetoothGatt != null) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
bluetoothGatt.disconnect();
bluetoothGatt.close();
bluetoothGatt = null;
}
}
private void clearData() {
tv_temp.setText("-- ℃");
tv_humi.setText("-- %RH");
}
@Override
protected void onDestroy() {
super.onDestroy();
disconnectBLE();
}
}然后实际的展示图片如下


我要赚赏金
