这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 成果贴-使用Androidstudio编写蓝牙APP,并实现Zephyr的蓝牙数

共1条 1/1 1 跳转至

成果贴-使用Androidstudio编写蓝牙APP,并实现Zephyr的蓝牙数据传输和解析

菜鸟
2026-05-26 16:47:07     打赏

首先就是先配置好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();
    }
}

然后实际的展示图片如下

image.png0f121d9044c2b2a69ff2445bba1f8666.jpg





关键词: 嵌入式     蓝牙     Android studio    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]