ESP32C3使用BLE

尽意
2025-05-01 / 0 评论 / 19 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2025年05月01日,已超过16天没有更新,若内容或图片失效,请留言反馈。

初始化项目

参考 项目创建 新建一个基于std的新项目

Cargo.toml添加必要的依赖

[package]
name = "esp32c3-ble-demo"
version = "0.1.0"
authors = ["suxii <m@suxii.cn>"]
edition = "2021"
resolver = "2"
rust-version = "1.77"

[[bin]]
name = "esp32c3-ble-demo"
harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors

[features]
default = []
experimental = ["esp-idf-svc/experimental"]

[dependencies]
log = "0.4"
esp-idf-svc = { version = "0.51", features = ["critical-section", "embassy-time-driver", "embassy-sync","experimental","std"] }
futures = "0.3.31" 
anyhow = "1.0.98"
enumset = "1.1.5"
heapless = "0.8.0"

esp-idf-sys = "0.36.1"
esp-idf-hal = "0.45.2"

# 处理序列化,反序列化
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"


[build-dependencies]
embuild = "0.33" 

开启BLE功能

sdkconfig.defaults 文件替换成

# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7000
CONFIG_BT_BLUEDROID_ENABLED=y
CONFIG_BT_ENABLED=y
CONFIG_BT_BLE_ENABLED=y
CONFIG_BT_GATTS_ENABLE=y
CONFIG_BT_BLE_SMP_ENABLE=y
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE=y
CONFIG_BT_BTC_TASK_STACK_SIZE=7000
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
CONFIG_BT_CTRL_BLE_MAX_ACT=10
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=10
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100
CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20
CONFIG_BT_CTRL_BLE_SCAN_DUPL=y

# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
#CONFIG_FREERTOS_HZ=1000

# Workaround for https://github.com/espressif/esp-idf/issues/7631
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n

并且在同级目录新建一个 sdkconfig.esp32c3文件

# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7000
CONFIG_BT_ENABLED=y
CONFIG_BT_BLE_ENABLED=y
CONFIG_BT_GATTS_ENABLE=y
CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE=y
CONFIG_BT_BTC_TASK_STACK_SIZE=7000

CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n

# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
#CONFIG_FREERTOS_HZ=1000

# Workaround for https://github.com/espressif/esp-idf/issues/7631
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n

代码编写

新建一个ble_server.rs文件,用来管理BLE


    use std::sync::{mpsc, Arc, Condvar, Mutex};
    use std::thread;

    use enumset::enum_set;

    use esp_idf_hal::ledc::{LedcDriver, LedcTimerDriver, Resolution};
    use esp_idf_hal::modem;
    use esp_idf_svc::bt::ble::gap::{AdvConfiguration, BleGapEvent, EspBleGap};
    use esp_idf_svc::bt::ble::gatt::server::{ConnectionId, EspGatts, GattsEvent, TransferId};
    use esp_idf_svc::bt::ble::gatt::{
        AutoResponse, GattCharacteristic, GattDescriptor, GattId, GattInterface, GattResponse,
        GattServiceId, GattStatus, Handle, Permission, Property,
    };
    use esp_idf_svc::bt::{BdAddr, Ble, BtDriver, BtStatus, BtUuid};
    use esp_idf_svc::hal::delay::FreeRtos;
    use esp_idf_svc::nvs::{ EspNvsPartition, NvsDefault};
    use esp_idf_svc::sys::{EspError, ESP_FAIL};

    use log::{error, info, warn};

    use crate::ble_recv_message;
    use crate::message::Message;

    pub fn start(
        modem: modem::Modem,
        nvs_clone:EspNvsPartition<NvsDefault>
    ) -> anyhow::Result<()> {
        // nvs(Non-Volatile Storage,非易失性存储)是持久化存储,保证断电数据不丢失
      
        // 创建ble蓝牙驱动
        let bt = Arc::new(BtDriver::new(modem, Some(nvs_clone))?);

        // 自定义一个蓝牙集合服务,创建gap,跟gatts
        let server = ExampleServer::new(
            Arc::new(EspBleGap::new(bt.clone())?),
            Arc::new(EspGatts::new(bt.clone())?),
        );

        info!("BLE Gap and Gatts 初始化完成");

        let gap_server = server.clone();
        // 处理gap事件回调吗,gap在连接阶段参与,不参与内容传输
        server.gap.subscribe(move |event| {
            gap_server.check_esp_status(gap_server.on_gap_event(event));
        })?;

        let gatts_server = server.clone();
        // gap处理完成,说明连接已经建立了,现在开始处理gatt,也就是数据通信的部分
        server.gatts.subscribe(move |(gatt_if, event)| {
            gatts_server.check_esp_status(gatts_server.on_gatts_event(gatt_if, event))
        })?;

        info!("BLE Gap and Gatts 订阅事件初始化完成");

        server.gatts.register_app(APP_ID)?;

        info!("Gatts BTP app 完成注册");


        // 新线程处理发送消息
        let _ = thread::Builder::new()
            .stack_size(2000)
            .spawn(move||{
                let mut ind_data = 0_u16;
                loop {
                    server.indicate(&ind_data.to_le_bytes()).unwrap();
                    info!("广播给客户端的信息为: {ind_data}");
        
                    ind_data = ind_data.wrapping_add(1);
        
                    FreeRtos::delay_ms(10000);
                }
            });

        anyhow::Ok(())

    }

   
    const APP_ID: u16 = 0;
    const MAX_CONNECTIONS: usize = 2;

    // Our service UUID
    pub const SERVICE_UUID: u128 = 0xad91b201734740479e173bed82d75f9d;

    /// Our "recv" characteristic - i.e. where clients can send data.
    pub const RECV_CHARACTERISTIC_UUID: u128 = 0xb6fccb5087be44f3ae22f85485ea42c4;
    /// Our "indicate" characteristic - i.e. where clients can receive data if they subscribe to it
    pub const IND_CHARACTERISTIC_UUID: u128 = 0x503de214868246c4828fd59144da41be;

    type ExBtDriver = BtDriver<'static, Ble>;
    type ExEspBleGap = Arc<EspBleGap<'static, Ble, Arc<ExBtDriver>>>;
    type ExEspGatts = Arc<EspGatts<'static, Ble, Arc<ExBtDriver>>>;

    #[derive(Debug, Clone)]
    struct Connection {
        peer: BdAddr,
        conn_id: Handle,
        subscribed: bool,
        mtu: Option<u16>,
    }

    #[derive(Default)]
    struct State {
        gatt_if: Option<GattInterface>,
        service_handle: Option<Handle>,
        recv_handle: Option<Handle>,
        ind_handle: Option<Handle>,
        ind_cccd_handle: Option<Handle>,
        connections: heapless::Vec<Connection, MAX_CONNECTIONS>,
        response: GattResponse,
        ind_confirmed: Option<BdAddr>,
    }

    #[derive(Clone)]
    pub struct ExampleServer {
        gap: ExEspBleGap,
        gatts: ExEspGatts,
        state: Arc<Mutex<State>>,
        condvar: Arc<Condvar>,
    }

    impl ExampleServer {
        pub fn new(gap: ExEspBleGap, gatts: ExEspGatts) -> Self {
            Self {
                gap,
                gatts,
                state: Arc::new(Mutex::new(Default::default())),
                condvar: Arc::new(Condvar::new()),
            }
        }
    }

    impl ExampleServer {
       
        // 发送indicate消息
        fn indicate(&self, data: &[u8]) -> Result<(), EspError> {
            for peer_index in 0..MAX_CONNECTIONS {
                // 发送数据给全部已经订阅的服务
                let mut state = self.state.lock().unwrap();

                loop {
                    if state.connections.len() <= peer_index {
                        // 表示已经向全部订阅的客户发送了消息
                        break;
                    }

                    let Some(gatt_if) = state.gatt_if else {
                        // 丢失了gatt的接口连接
                        break;
                    };

                    let Some(ind_handle) = state.ind_handle else {
                        // 丢失了handle
                        break;
                    };

                    // confirmed翻译为 证实
                    // 检查状态:确保上次的指示数据已被客户端确认接收。
                    // 发送数据:如果状态允许,则向指定客户端发送新的指示数据。
                    // 更新状态:记录当前客户端已接收到指示数据。
                    // 等待条件:如果状态不允许发送数据,则等待其他线程的通知。
                    if state.ind_confirmed.is_none() {
                        let conn = &state.connections[peer_index];

                        self.gatts
                            .indicate(gatt_if, conn.conn_id, ind_handle, data)?;

                        state.ind_confirmed = Some(conn.peer);
                        let conn = &state.connections[peer_index];

                        info!("Indicated数据发送给客户端 {}", conn.peer);
                        break;
                    } else {
                        state = self.condvar.wait(state).unwrap();
                    }
                }
            }

            Ok(())
        }

        /// 处理客户端订阅
        fn on_subscribed(&self, addr: BdAddr) {
            // todo 客户端订阅
            warn!("客户端 {addr} 订阅了");
        }

        /// 处理客户端取消订阅
        fn on_unsubscribed(&self, addr: BdAddr) {
            // todo 客户肯定取消订阅
            warn!("客户端 {addr} 取消订阅");
        }

        /// 接收消息回调
        fn on_recv(&self, addr: BdAddr, data: &[u8], offset: u16, mtu: Option<u16>) {
            // todo 这里写接收消息的逻辑

            // 按大端解析
            // let res = u16::from_be_bytes([data[0],data[1]]);
            let res = String::from_utf8_lossy(data);
            if let Err(e) = ble_recv_message::handle_message(&res){
                error!("处理消息错误:{e}")
            }
            warn!("接收消息来自客户端 {addr}: {data:?}, offset: {offset}, mtu: {mtu:?} text:{res}");
        }


        /// GAP 事件的主事件处理程序
        fn on_gap_event(&self, event: BleGapEvent) -> Result<(), EspError> {
            info!("得到gap事件: {event:?}");

            // ble广播配置   Advertising:广告,广播
            if let BleGapEvent::AdvertisingConfigured(status) = event {
                // 从gap事件中,结构出BtStatus状态,然后检查bt的状态
                // 如果bt连接状态检查不通过,就直接往上级抛异常
                self.check_bt_status(status)?;
                // gap处理完成,开启广播
                self.gap.start_advertising()?;
            }

            Ok(())
        }

        ///  GATTS 事件的主事件处理程序
        fn on_gatts_event(
            &self,
            gatt_if: GattInterface,
            event: GattsEvent,
        ) -> Result<(), EspError> {
            info!("得到gatts事件消息: {event:?}");

            match event {
                // 处理服务注册,注册成功后,创建服务
                GattsEvent::ServiceRegistered { status, app_id } => {
                    self.check_gatt_status(status)?;
                    if APP_ID == app_id {
                        self.create_service(gatt_if)?;
                    }
                }
                // 处理服务创建完成后的事件,配置gatts的服务
                GattsEvent::ServiceCreated {
                    status,
                    service_handle,
                    ..
                } => {
                    self.check_gatt_status(status)?;
                    self.configure_and_start_service(service_handle)?;
                }
                // 处理特征添加
                GattsEvent::CharacteristicAdded {
                    status,
                    attr_handle,
                    service_handle,
                    char_uuid,
                } => {
                    self.check_gatt_status(status)?;
                    self.register_characteristic(service_handle, attr_handle, char_uuid)?;
                }
                // 添加描述符
                GattsEvent::DescriptorAdded {
                    status,
                    attr_handle,
                    service_handle,
                    descr_uuid,
                } => {
                    self.check_gatt_status(status)?;
                    self.register_cccd_descriptor(service_handle, attr_handle, descr_uuid)?;
                }
                // 删除服务
                GattsEvent::ServiceDeleted {
                    status,
                    service_handle,
                } => {
                    self.check_gatt_status(status)?;
                    self.delete_service(service_handle)?;
                }
                // 注销服务
                GattsEvent::ServiceUnregistered {
                    status,
                    service_handle,
                    ..
                } => {
                    self.check_gatt_status(status)?;
                    self.unregister_service(service_handle)?;
                }
                // 注册或更新其 MTU(Maximum Transmission Unit 最大传输单元)值
                GattsEvent::Mtu { conn_id, mtu } => {
                    self.register_conn_mtu(conn_id, mtu)?;
                }
                // 处理连接的客户端
                GattsEvent::PeerConnected { conn_id, addr, .. } => {
                    self.create_conn(conn_id, addr)?;
                }
                // 处理客户端断开连接
                GattsEvent::PeerDisconnected { addr, .. } => {
                    self.delete_conn(addr)?;
                    // todo 开启广播
                    self.gap.start_advertising()?;
                }
                GattsEvent::Write {
                    conn_id,
                    trans_id,
                    addr,
                    handle,
                    offset,
                    need_rsp,
                    is_prep,
                    value,
                } => {
                    let handled = self.recv(
                        gatt_if, conn_id, trans_id, addr, handle, offset, need_rsp, is_prep, value,
                    )?;
                    // 值为true,表示是客户端的操作,例如客户端注册,注销,发送消息
                    if handled {
                        self.send_write_response(
                            gatt_if, conn_id, trans_id, handle, offset, need_rsp, is_prep, value,
                        )?;
                    }
                }
                GattsEvent::Confirm { status, .. } => {
                    self.check_gatt_status(status)?;
                    self.confirm_indication()?;
                }
                _ => (),
            }

            Ok(())
        }

        /// 创建服务并开始广播,
        /// 在通知我们 GATTS 应用程序已注册后,从事件回调中调
        fn create_service(&self, gatt_if: GattInterface) -> Result<(), EspError> {
            // 初始化结构体的 GattInterface
            self.state.lock().unwrap().gatt_if = Some(gatt_if);

            // 设置esp32设备的名字
            self.gap.set_device_name("ESP32")?;
            // flag 标志位是一个位掩码,具体含义可以参考 Bluetooth SIG 定义的标准。
            // 常见的标志位组合:
            // 0x01:LE Limited Discoverable Mode(有限可发现模式)。
            // 0x02:LE General Discoverable Mode(通用可发现模式)。
            // 0x04:BR/EDR Not Supported(不支持经典蓝牙)。
            // 0x18:LE and BR/EDR Controller(同时支持 LE 和经典蓝牙)。
            // 在代码中,flag: 2 表示设备处于通用可发现模式(LE General Discoverable Mode)。
            self.gap.set_adv_conf(&AdvConfiguration {
                include_name: true, // 指示是否在广播数据中包含设备名称
                include_txpower: true, // 指示是否在广播数据中包含设备的发射功率
                flag: 2,
                service_uuid: Some(BtUuid::uuid128(SERVICE_UUID)), // 指定设备提供的服务 UUID
                // service_data: todo!(),  // 用于广播与特定服务相关的数据
                // manufacturer_data: todo!(),  // 用于广播厂商自定义的数据
                ..Default::default()
            })?;
            self.gatts.create_service(
                gatt_if,
                &GattServiceId {
                    id: GattId {
                        uuid: BtUuid::uuid128(SERVICE_UUID),
                        inst_id: 0, // 指定服务的实例Id 如果设备支持多个相同 UUID 的服务实例,inst_id 用于区分它们
                    },
                    is_primary: true, // 指示服务是否为主服务
                },
                8,
            )?;

            Ok(())
        }

        /// 删除服务
        fn delete_service(&self, service_handle: Handle) -> Result<(), EspError> {
            let mut state = self.state.lock().unwrap();

            if state.service_handle == Some(service_handle) {
                state.recv_handle = None;
                state.ind_handle = None;
                state.ind_cccd_handle = None;
            }

            Ok(())
        }

        /// 取消注册服务
        fn unregister_service(&self, service_handle: Handle) -> Result<(), EspError> {
            let mut state = self.state.lock().unwrap();

            if state.service_handle == Some(service_handle) {
                state.gatt_if = None;
                state.service_handle = None;
            }

            Ok(())
        }

        /// 配置并且启动服务
        /// 在通知我们创建服务后从事件回调中调用
        fn configure_and_start_service(&self, service_handle: Handle) -> Result<(), EspError> {
            // 初始化service_handle服务
            self.state.lock().unwrap().service_handle = Some(service_handle);

            // 开启gatts服务
            // pub type Handle = u16; 由于u16是基本类型,实现了copy,所以下面两个函数都能使用,本质是copy了一份数据
            self.gatts.start_service(service_handle)?;
            // 添加特性
            self.add_characteristics(service_handle)?;

            Ok(())
        }

        /// 添加注册的两个uuid特性到服务中
        /// 在通知我们创建服务后从事件回调中调用
        fn add_characteristics(&self, service_handle: Handle) -> Result<(), EspError> {
            // permissions 定义了客户端对该特征的操作权限。
            // 在这里,Permission::Write 表示客户端可以写入该特征的值。
            // 其他常见的权限包括:
            // Permission::Read:允许读取特征值。
            // Permission::Notify:允许服务器主动通知客户端特征值的变化。
            // Permission::Indicate:类似于通知,但需要客户端确认。

            // properties 定义了特征的属性(Properties),描述了该特征支持的操作类型。
            // 在这里,Property::Write 表示客户端可以通过写操作更新该特征的值。
            // 其他常见的属性包括:
            // Property::Read:允许客户端读取特征值。
            // Property::Notify:允许服务器发送通知。
            // Property::Indicate:允许服务器发送指示
            self.gatts.add_characteristic(
                service_handle,  // 服务的唯一标识
                &GattCharacteristic {
                    uuid: BtUuid::uuid128(RECV_CHARACTERISTIC_UUID),
                    permissions: enum_set!(Permission::Write),
                    properties: enum_set!(Property::Write),
                    max_len: 200, // 定义了特征值的最大长度(以字节为单位)
                    auto_rsp: AutoResponse::ByApp, // 表示应用程序需要手动响应客户端的请求
                },
                &[], // 表示该特征的描述符(Descriptors)列表,&[] 表示没有附加任何描述符
            )?;

            self.gatts.add_characteristic(
                service_handle,
                &GattCharacteristic {
                    uuid: BtUuid::uuid128(IND_CHARACTERISTIC_UUID),
                    permissions: enum_set!(Permission::Write | Permission::Read),
                    properties: enum_set!(Property::Indicate),
                    max_len: 200, // Mac iondicate data
                    auto_rsp: AutoResponse::ByApp,
                },
                &[],
            )?;

            Ok(())
        }

        /// 添加 CCCD 描述
        /// 只添加特征indicate
        fn register_characteristic(
            &self,
            service_handle: Handle,
            attr_handle: Handle,
            char_uuid: BtUuid,
        ) -> Result<(), EspError> {
            // 在找esp主动推送消息给客户端的uuid服务
            let indicate_char = {
                let mut state = self.state.lock().unwrap();

                if state.service_handle != Some(service_handle) {
                    false
                } else if char_uuid == BtUuid::uuid128(RECV_CHARACTERISTIC_UUID) {
                    state.recv_handle = Some(attr_handle);

                    false
                } else if char_uuid == BtUuid::uuid128(IND_CHARACTERISTIC_UUID) {
                    state.ind_handle = Some(attr_handle);

                    true
                } else {
                    false
                }
            };

            if indicate_char {
                self.gatts.add_descriptor(
                    service_handle,
                    &GattDescriptor {
                        uuid: BtUuid::uuid16(0x2902), // CCCD 通常用于控制特征的通知(Notify)和指示(Indicate)功能,允许客户端订阅或取消订阅特征值的变化
                        permissions: enum_set!(Permission::Read | Permission::Write),
                    },
                )?;
            }

            Ok(())
        }

        /// 注册 CCCD 描述符
        /// 在通知我们添加了描述符后,从事件回调中调用
        /// 这段代码的作用是注册一个 CCCD(Client Characteristic Configuration Descriptor)描述符,并将其相关信息存储到内部状态中。具体功能如下:
        /// 验证描述符的 UUID 是否为标准的 CCCD UUID(0x2902)。
        /// 确认描述符所属的服务句柄是否与内部记录的服务句柄一致。
        /// 如果条件满足,则将描述符的属性句柄(attr_handle)存储到内部状态中,以便后续用于处理通知或指示功能。
        /// 这种设计通常用于 BLE(蓝牙低功耗)设备的 GATT 服务实现中,确保 CCCD 的句柄被正确记录,从而支持客户端订阅或取消订阅特征值的变化
        fn register_cccd_descriptor(
            &self,
            service_handle: Handle,
            attr_handle: Handle,
            descr_uuid: BtUuid,
        ) -> Result<(), EspError> {
            let mut state = self.state.lock().unwrap();

            if descr_uuid == BtUuid::uuid16(0x2902) // CCCD UUID
                && state.service_handle == Some(service_handle)
            {
                state.ind_cccd_handle = Some(attr_handle);
            }

            Ok(())
        }

        /// 从客户端接收消息
        fn register_conn_mtu(&self, conn_id: ConnectionId, mtu: u16) -> Result<(), EspError> {
            let mut state = self.state.lock().unwrap();

            // 找到目标客户端
            if let Some(conn) = state
                .connections
                .iter_mut()
                .find(|conn| conn.conn_id == conn_id)
            {
                // 并且设置最大传输单元的值,字节
                conn.mtu = Some(mtu);
            }

            Ok(())
        }

        /// 创建一个新的连接
        fn create_conn(&self, conn_id: ConnectionId, addr: BdAddr) -> Result<(), EspError> {
            // 创建指定大小的连接,如果超过,新的连接将不再处理
            let added = {
                let mut state = self.state.lock().unwrap();

                if state.connections.len() < MAX_CONNECTIONS {
                    state
                        .connections
                        .push(Connection {
                            peer: addr,
                            conn_id,
                            subscribed: false,
                            mtu: None,
                        })
                        .map_err(|_| ())
                        .unwrap();

                    true
                } else {
                    false
                }
            };

            if added {
                // 如果已经添加,则配置gap参数
                // addr:表示目标设备的蓝牙地址(BdAddr),用于标识要配置连接参数的远程设备。
                // min_int_ms:表示连接间隔的最小值(单位为毫秒)。
                // max_int_ms:表示连接间隔的最大值(单位为毫秒)。
                // latency_ms:表示连接延迟(单位为毫秒)。
                // timeout_ms:表示超时时间(单位为毫秒)。
                self.gap.set_conn_params_conf(addr, 10, 20, 0, 400)?;
            }

            Ok(())
        }

        /// 删除一个连接
        fn delete_conn(&self, addr: BdAddr) -> Result<(), EspError> {
            let mut state = self.state.lock().unwrap();
            
            // Connection { peer, .. }:这是结构体解构语法,用于从 Connection 结构体中提取字段 peer
            if let Some(index) = state
                .connections
                .iter()
                .position(|Connection { peer, .. }| *peer == addr)
            {
                // remove:
                // 从向量中移除指定索引位置的元素。
                // 将该索引之后的所有元素向前移动一位,以保持顺序。
                // 时间复杂度为 O(n),因为可能需要移动多个元素。
                // swap_remove:
                // 从向量中移除指定索引位置的元素。
                // 使用最后一个元素填补空缺,不保证顺序。
                // 时间复杂度为 O(1),因为只涉及少量操作。
                state.connections.swap_remove(index);
            }

            Ok(())
        }

        /// 一个辅助方法,用于处理向我们发送数据到 “recv” 特征的客户端
        #[allow(clippy::too_many_arguments)]
        fn recv(
            &self,
            _gatt_if: GattInterface,
            conn_id: ConnectionId,
            _trans_id: TransferId,
            addr: BdAddr,
            handle: Handle,
            offset: u16,
            _need_rsp: bool,
            _is_prep: bool,
            value: &[u8],
        ) -> Result<bool, EspError> {
            let mut state = self.state.lock().unwrap();

            let recv_handle = state.recv_handle;
            let ind_cccd_handle = state.ind_cccd_handle;

            let Some(conn) = state
                .connections
                .iter_mut()
                .find(|conn| conn.conn_id == conn_id)
            else {
                return Ok(false);
            };

            if Some(handle) == ind_cccd_handle {
                // 订阅或取消订阅我们的(indication characteristic)指示特性

                if offset == 0 && value.len() == 2 {
                    let value = u16::from_le_bytes([value[0], value[1]]);
                    if value == 0x02 {
                        if !conn.subscribed {
                            conn.subscribed = true;
                            self.on_subscribed(conn.peer);
                        }
                    } else if conn.subscribed {
                        conn.subscribed = false;
                        self.on_unsubscribed(conn.peer);
                    }
                }
            } else if Some(handle) == recv_handle {
                // Receive data on the recv characteristic

                self.on_recv(addr, value, offset, conn.mtu);
            } else {
                return Ok(false);
            }

            Ok(true)
        }

        /// 辅助方法,向Peer客户端发送响应节点,该 Peer 节点刚刚向我们发送了 “recv” 上的一些数据
        /// characteristic. (特征)
        /// 这只是必要的,因为我们支持写确认(与未确认的写入相比,这是更复杂的情况)
        #[allow(clippy::too_many_arguments)]
        fn send_write_response(
            &self,
            gatt_if: GattInterface,
            conn_id: ConnectionId,
            trans_id: TransferId,
            handle: Handle,
            offset: u16,
            need_rsp: bool,
            is_prep: bool,
            value: &[u8],
        ) -> Result<(), EspError> {
            // 是否需要处理 写确认
            if !need_rsp {
                return Ok(());
            }

            // 是否准备好了数据
            if is_prep {
                let mut state = self.state.lock().unwrap();

                state
                    .response
                    .attr_handle(handle)
                    .auth_req(0)
                    .offset(offset)
                    .value(value)
                    .map_err(|_| EspError::from_infallible::<ESP_FAIL>())?;

                self.gatts.send_response(
                    gatt_if,
                    conn_id,
                    trans_id,
                    GattStatus::Ok,
                    Some(&state.response),
                )?;
            } else {
                self.gatts
                    .send_response(gatt_if, conn_id, trans_id, GattStatus::Ok, None)?;
            }

            Ok(())
        }

        /// 处理下一个 indication
        fn confirm_indication(&self) -> Result<(), EspError> {
            let mut state = self.state.lock().unwrap();
            if state.ind_confirmed.is_none() {
                // Should not happen:表示我们已收到
                // 我们没有发送一个指示(indication)
                // unreachable!() 是 Rust 提供的一个宏,用于标记代码中“不应该到达”的位置,到这里会恐慌
                unreachable!();
            }

            // 通知主循环可以发送下一个indication
            state.ind_confirmed = None; // So that the main loop can send the next indication
            self.condvar.notify_all();

            Ok(())
        }

        fn check_esp_status(&self, status: Result<(), EspError>) {
            if let Err(e) = status {
                warn!("检查esp的状态错误为: {:?}", e);
            }
        }

        fn check_bt_status(&self, status: BtStatus) -> Result<(), EspError> {
            // matches! 是匹配模式,!取反
            // bt状态不匹配就抛连接失败异常
            if !matches!(status, BtStatus::Success) {
                warn!("Got status: {:?}", status);
                Err(EspError::from_infallible::<ESP_FAIL>())
            } else {
                Ok(())
            }
        }

        fn check_gatt_status(&self, status: GattStatus) -> Result<(), EspError> {
            if !matches!(status, GattStatus::Ok) {
                warn!("gatt的状态错误: {:?}", status);
                Err(EspError::from_infallible::<ESP_FAIL>())
            } else {
                Ok(())
            }
        }
    }

新建一个ble_recv_message.rs文件,处理BLE的消息接收

use log::{error, info};

use crate::{driver_manage::{GLOBAL_LED}, message::{LedMessage, Message}};


pub fn handle_message(src:&str) -> anyhow::Result<()>{
    let message = Message::from_str(src);
    if let Ok(message) = message{
        match message {
            Message::Led(led_message) => {
                handle_led_message(led_message)?
            },
            Message::Unknown => {
                error!("收到错误的类型")
            },
        }
    }
    anyhow::Ok(())
}

fn handle_led_message(led_message:LedMessage)->anyhow::Result<()>{
    info!("{:?}",led_message);
    if led_message.value == 1{
        let mut guard = GLOBAL_LED.lock().unwrap();
        guard.as_mut().unwrap().set_high()?;    
    }else {
        let mut guard = GLOBAL_LED.lock().unwrap();
        guard.as_mut().unwrap().set_low()?;       
    }
    anyhow::Ok(())
}

新建message.rs,处理消息传输,序列化跟反序列化


use serde::{Deserialize, Serialize};

#[derive(Debug,Serialize,Deserialize)]
#[serde(tag="type")]
pub enum Message{
    #[serde(rename = "led")]
    Led(LedMessage),

    #[serde(other)]
    Unknown

}

#[derive(Debug,Serialize,Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LedMessage{
    // 0关,1开
    pub value:u8
}


impl Message{
    pub fn to_json(&self) -> serde_json::Result<String>{
        serde_json::to_string(self)
    }

    pub fn from_str(src:&str) -> serde_json::Result<Self>{
        serde_json::from_str(src)
    }
}

新建driver_manage.rs文件,用来管理注册的外设

use std::sync::Mutex;

use esp_idf_hal::{gpio::{AnyIOPin, AnyOutputPin, Gpio9, Output, OutputPin, PinDriver}, peripheral::Peripheral, prelude::Peripherals};


pub static GLOBAL_LED :Mutex<Option<PinDriver<'static,AnyOutputPin,Output>>> = Mutex::new(None);
pub fn init_global(led_gpio:AnyOutputPin) -> anyhow::Result<()>{
    
    {
        let led = PinDriver::output(led_gpio)?;
        let mut guard = GLOBAL_LED.lock().unwrap();
        *guard = Some(led);
    }

    anyhow::Ok(())
}

编辑main.rs文件


use esp_idf_hal::{ gpio::OutputPin, prelude::Peripherals};
use esp_idf_svc::{log::EspLogger, nvs::EspDefaultNvsPartition};


mod ble_server;
mod driver_manage;
mod message;
mod ble_recv_message;


fn main() -> anyhow::Result<()> {
    esp_idf_svc::sys::link_patches();
    EspLogger::initialize_default();

    let peripherals = Peripherals::take()?;
    let nvs = EspDefaultNvsPartition::take()?;
    
    driver_manage::init_global(
        peripherals.pins.gpio12.downgrade_output())?;

    ble_server::start(peripherals.modem,nvs.clone())?;

    anyhow::Ok(())

}

效果展示

成功接收到客户端消息
ma54qrt9.png

0

评论 (0)

取消