初始化项目
参考 项目创建 新建一个基于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(())
}
效果展示
成功接收到客户端消息
评论 (0)