1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::sync::{Arc, Mutex};
use crate::{
gatt_server::Descriptor,
utilities::{AttributePermissions, BleUuid},
};
use embedded_svc::storage::RawStorage;
use esp_idf_svc::nvs::{EspDefaultNvs, EspDefaultNvsPartition};
use lazy_static::lazy_static;
use log::debug;
lazy_static! {
static ref STORAGE: Arc<Mutex<EspDefaultNvs>> = Arc::new(Mutex::new(
EspDefaultNvs::new(
EspDefaultNvsPartition::take()
.expect("Cannot initialise the default NVS. Did you declare an NVS partition?"),
"ble",
true
)
.expect("Cannot create a new NVS storage. Did you declare an NVS partition?")
));
}
impl Descriptor {
/// Creates a new descriptor with the `0x2901` UUID, and the description string as its value.
///
/// This is a common descriptor used to describe a characteristic to the user.
/// See [`Characteristic::show_name`] for an easier way to assign this kind of descriptor to a [`Characteristic`].
///
/// [`Characteristic::show_name`]: crate::gatt_server::Characteristic::show_name
/// [`Characteristic`]: crate::gatt_server::Characteristic
pub fn user_description<S: AsRef<str>>(description: S) -> Self {
Self::new(BleUuid::from_uuid16(0x2901))
.name("User Description")
.permissions(AttributePermissions::new().read())
.set_value(description.as_ref().as_bytes().to_vec())
.clone()
}
/// Creates a CCCD.
///
/// The contents of the CCCD are stored in NVS and persisted across reboots.
///
/// # Panics
///
/// Panics if the NVS is not configured.
#[must_use]
pub fn cccd() -> Self {
Self::new(BleUuid::from_uuid16(0x2902))
.name("Client Characteristic Configuration")
.permissions(AttributePermissions::new().read().write())
.on_read(
|param: esp_idf_sys::esp_ble_gatts_cb_param_t_gatts_read_evt_param| {
let storage = STORAGE.lock().unwrap();
// Get the descriptor handle.
// TODO: Find the characteristic that contains the handle.
// WARNING: Using the handle is incredibly stupid as the NVS is not erased across flashes.
// Create a key from the connection address.
let key = format!(
"{:02X}{:02X}{:02X}{:02X}-{:04X}",
/* param.bda[1], */ param.bda[2],
param.bda[3],
param.bda[4],
param.bda[5],
param.handle
);
// Prepare buffer and read correct CCCD value from non-volatile storage.
let mut buf: [u8; 2] = [0; 2];
if let Some(value) = storage.get_raw(&key, &mut buf).unwrap() {
debug!("Read CCCD value: {:?} for key {}.", value, key);
value.to_vec()
} else {
debug!("No CCCD value found for key {}.", key);
vec![0, 0]
}
},
)
.on_write(|value, param| {
let mut storage = STORAGE.lock().unwrap();
// Create a key from the connection address.
let key = format!(
"{:02X}{:02X}{:02X}{:02X}-{:04X}",
/* param.bda[1], */ param.bda[2],
param.bda[3],
param.bda[4],
param.bda[5],
param.handle
);
debug!("Write CCCD value: {:?} at key {}", value, key);
// Write CCCD value to non-volatile storage.
storage
.set_raw(&key, &value)
.expect("Cannot put raw value to the NVS. Did you declare an NVS partition?");
})
.clone()
}
}