首页
苏兮影视
随笔记
壁纸
更多
直播
时光轴
友联
关于
统计
Search
1
软件添加id功能按钮
775 阅读
2
v2ray节点搭建
774 阅读
3
typecho非常有特色的模块
563 阅读
4
QQ扫码无法登录的解决方案
516 阅读
5
QQxml消息卡片生成源码
514 阅读
谈天说地
建站源码
经验教程
资源分享
动漫美图
登录
Search
标签搜索
java
rust
flutter
esp32c3
springboot
安卓
linux
vue
docker
joe
快捷键
git
fish shell
maven
redis
netty
dart
groovy
js
设计模式
尽意
累计撰写
109
篇文章
累计收到
39
条评论
首页
栏目
谈天说地
建站源码
经验教程
资源分享
动漫美图
页面
苏兮影视
随笔记
壁纸
直播
时光轴
友联
关于
统计
搜索到
1
篇与
的结果
2025-05-01
ESP32C3使用BLE
初始化项目参考 项目创建 新建一个基于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(()) } 效果展示成功接收到客户端消息
2025年05月01日
19 阅读
0 评论
0 点赞