1use crate::{find_characteristic, Error, H10MeasurementType, PolarResult};
7
8use btleplug::api::{Characteristic, Peripheral as _, WriteType};
9use btleplug::platform::Peripheral;
10use uuid::Uuid;
11
12const PMD_CP_UUID: Uuid = Uuid::from_u128(0xfb005c81_02e7_f387_1cad_8acd2d8df0c8);
14const PMD_DATA_UUID: Uuid = Uuid::from_u128(0xfb005c82_02e7_f387_1cad_8acd2d8df0c8);
16
17#[derive(Debug, PartialEq, Eq)]
19pub enum ControlPointCommand {
20 Null = 0,
22 GetMeasurementSettings,
24 RequestMeasurementStart,
26 StopMeasurement,
28}
29
30impl TryFrom<u8> for ControlPointCommand {
31 type Error = ();
32
33 fn try_from(val: u8) -> Result<ControlPointCommand, ()> {
34 match val {
35 0 => Ok(ControlPointCommand::Null),
36 1 => Ok(ControlPointCommand::GetMeasurementSettings),
37 2 => Ok(ControlPointCommand::RequestMeasurementStart),
38 3 => Ok(ControlPointCommand::StopMeasurement),
39 _ => {
40 println!("Invalid ControlPointCommand {}", val);
41 Err(())
42 }
43 }
44 }
45}
46
47#[derive(Debug, PartialEq, Eq)]
49pub enum ControlPointResponseCode {
50 Success = 0,
52 InvalidOpCode,
54 InvalidMeasurementType,
56 NotSupported,
58 InvalidLength,
60 InvalidParameter,
62 AlreadyInState,
64 InvalidResolution,
66 InvalidSampleRate,
68 InvalidRange,
70 InvalidMTU,
72 InvalidNumberOfChannels,
74 InvalidState,
76 DeviceInCharger,
78}
79
80impl TryFrom<u8> for ControlPointResponseCode {
81 type Error = ();
82
83 fn try_from(val: u8) -> Result<ControlPointResponseCode, ()> {
84 match val {
85 0 => Ok(ControlPointResponseCode::Success),
86 1 => Ok(ControlPointResponseCode::InvalidOpCode),
87 2 => Ok(ControlPointResponseCode::InvalidMeasurementType),
88 3 => Ok(ControlPointResponseCode::NotSupported),
89 4 => Ok(ControlPointResponseCode::InvalidLength),
90 5 => Ok(ControlPointResponseCode::InvalidParameter),
91 6 => Ok(ControlPointResponseCode::AlreadyInState),
92 7 => Ok(ControlPointResponseCode::InvalidResolution),
93 8 => Ok(ControlPointResponseCode::InvalidSampleRate),
94 9 => Ok(ControlPointResponseCode::InvalidRange),
95 10 => Ok(ControlPointResponseCode::InvalidMTU),
96 11 => Ok(ControlPointResponseCode::InvalidNumberOfChannels),
97 12 => Ok(ControlPointResponseCode::InvalidState),
98 13 => Ok(ControlPointResponseCode::DeviceInCharger),
99 _ => {
100 println!("Invalid ControlPointResponseCode {}", val);
101 Err(())
102 }
103 }
104 }
105}
106
107#[derive(Debug, PartialEq, Eq)]
108enum ResponseCode {
109 Success,
110 InvalidHandle,
111 ReadNotPermitted,
112 WriteNotPermitted,
113 InvalidPdu,
114 InsufficientAuthentication,
115 RequestNotSupported,
116 InvalidOffset,
117 InsufficientAuthorization,
118 PrepareQueueFull,
119 AttributeNotFound,
120 AttributeNotLong,
121 InsufficientEncryptionKeySize,
122 InsufficientAttributeValueLength,
123 UnlikelyError,
124 InsufficientEncryption,
125 UnsupportedGroupType,
126 InsufficientResources,
127}
128
129impl TryFrom<u8> for ResponseCode {
130 type Error = ();
131
132 fn try_from(val: u8) -> Result<ResponseCode, ()> {
133 match val {
134 0 => Ok(ResponseCode::Success),
135 1 => Ok(ResponseCode::InvalidHandle),
136 2 => Ok(ResponseCode::ReadNotPermitted),
137 3 => Ok(ResponseCode::WriteNotPermitted),
138 4 => Ok(ResponseCode::InvalidPdu),
139 5 => Ok(ResponseCode::InsufficientAuthentication),
140 6 => Ok(ResponseCode::RequestNotSupported),
141 7 => Ok(ResponseCode::InvalidOffset),
142 8 => Ok(ResponseCode::InsufficientAuthorization),
143 9 => Ok(ResponseCode::PrepareQueueFull),
144 10 => Ok(ResponseCode::AttributeNotFound),
145 11 => Ok(ResponseCode::AttributeNotLong),
146 12 => Ok(ResponseCode::InsufficientEncryptionKeySize),
147 13 => Ok(ResponseCode::InsufficientAttributeValueLength),
148 14 => Ok(ResponseCode::UnlikelyError),
149 15 => Ok(ResponseCode::InsufficientEncryption),
150 16 => Ok(ResponseCode::UnsupportedGroupType),
151 17 => Ok(ResponseCode::InsufficientResources),
152 _ => {
153 println!("Invalid ResponseCode {}", val);
154 Err(())
155 }
156 }
157 }
158}
159
160#[derive(Clone, Copy, Debug)]
161enum SettingType {
162 SampleRate,
163 Resolution,
164 Range,
165}
166
167impl SettingType {
168 fn from(byte: u8) -> SettingType {
169 match byte {
170 0x00 => SettingType::SampleRate,
171 0x01 => SettingType::Resolution,
172 _ => SettingType::Range,
173 }
174 }
175}
176
177enum PmdByteType {
178 Setting,
179 ArrLen,
180 Data,
181}
182
183#[derive(Debug, PartialEq, Eq)]
185pub struct StreamSettings {
186 ty: H10MeasurementType,
187 resolution: u8,
188 range: Option<Vec<u8>>,
189 sample_rate: Vec<u8>,
190}
191
192impl StreamSettings {
193 pub fn new(resp: &ControlResponse) -> PolarResult<StreamSettings> {
195 if *resp.opcode() != ControlPointCommand::GetMeasurementSettings {
196 return Err(Error::WrongResponse);
197 }
198
199 let mut resolution: u8 = 0;
200 let mut ranges: Vec<u8> = vec![];
201 let mut sample_rate: Vec<u8> = vec![];
202
203 let mut setting: SettingType = SettingType::from(resp.parameters[0]);
204 let mut next_byte: PmdByteType = PmdByteType::ArrLen;
205 let mut len_remaining = 0u8;
206
207 let mut data = resp.parameters()[1..].iter();
208
209 while let Some(i) = data.next() {
210 match next_byte {
211 PmdByteType::Setting => {
212 setting = SettingType::from(*i);
213 next_byte = PmdByteType::ArrLen;
214 }
215 PmdByteType::ArrLen => {
216 len_remaining = *i;
217 next_byte = PmdByteType::Data;
218 }
219 PmdByteType::Data => {
220 match setting {
221 SettingType::SampleRate => {
222 sample_rate.push(*i);
223 let _ = data.next().unwrap();
224 }
225 SettingType::Resolution => {
226 resolution = *i;
227 let _ = data.next().unwrap();
228 }
229 SettingType::Range => {
230 ranges.push(*i);
231 let _ = data.next().unwrap();
232 }
233 }
234
235 len_remaining -= 1;
236 if len_remaining == 0 {
237 next_byte = PmdByteType::Setting;
238 }
239 }
240 }
241 }
242
243 let range = if !ranges.is_empty() {
244 Some(ranges)
245 } else {
246 None
247 };
248
249 Ok(StreamSettings {
250 ty: *resp.data_type(),
251 resolution,
252 range,
253 sample_rate,
254 })
255 }
256
257 pub fn resolution(&self) -> u8 {
259 self.resolution
260 }
261
262 pub fn range(&self) -> &Option<Vec<u8>> {
264 &self.range
265 }
266
267 pub fn sample_rate(&self) -> &Vec<u8> {
269 &self.sample_rate
270 }
271}
272
273#[derive(Debug)]
275pub struct ControlResponse {
276 opcode: ControlPointCommand,
277 measurement_type: H10MeasurementType,
278 status: ControlPointResponseCode,
279 parameters: Vec<u8>,
280}
281
282impl ControlResponse {
283 pub async fn new(data: Vec<u8>) -> PolarResult<ControlResponse> {
285 if data.len() < 4 {
287 return Err(Error::InvalidData);
288 }
289 if data[0] != 0xf0 {
291 return Err(Error::InvalidData);
292 }
293 let opcode = ControlPointCommand::try_from(data[1]).map_err(|_| Error::InvalidData)?;
294 let measurement_type =
295 H10MeasurementType::try_from(data[2]).map_err(|_| Error::InvalidData)?;
296 let status = ControlPointResponseCode::try_from(data[3]).map_err(|_| Error::InvalidData)?;
297 let mut parameters = Vec::new();
298
299 if data.len() > 5 {
300 parameters = data[5..].to_vec();
301 }
302
303 Ok(ControlResponse {
304 opcode,
305 measurement_type,
306 status,
307 parameters,
308 })
309 }
310
311 pub fn parameters(&self) -> &Vec<u8> {
313 &self.parameters
314 }
315
316 pub fn opcode(&self) -> &ControlPointCommand {
318 &self.opcode
319 }
320
321 pub fn data_type(&self) -> &H10MeasurementType {
323 &self.measurement_type
324 }
325
326 pub fn status(&self) -> &ControlPointResponseCode {
328 &self.status
329 }
330}
331
332#[derive(Debug, PartialEq, Eq)]
334pub struct ControlPoint {
335 control_point: Characteristic,
336 measurement_data: Characteristic,
337}
338
339impl ControlPoint {
340 pub async fn new(device: &Peripheral) -> PolarResult<ControlPoint> {
342 let control_point = find_characteristic(device, PMD_CP_UUID).await?;
343 let measurement_data = find_characteristic(device, PMD_DATA_UUID).await?;
344
345 Ok(ControlPoint {
346 control_point,
347 measurement_data,
348 })
349 }
350
351 pub async fn send_command(&self, device: &Peripheral, data: Vec<u8>) -> PolarResult<()> {
353 self.write(device, data).await?;
354
355 Ok(())
356 }
357
358 async fn write(&self, device: &Peripheral, data: Vec<u8>) -> PolarResult<()> {
359 device
360 .write(&self.control_point, &data, WriteType::WithResponse)
361 .await
362 .map_err(Error::BleError)
363 }
364
365 pub async fn read(&self, device: &Peripheral) -> PolarResult<Vec<u8>> {
367 device
368 .read(&self.control_point)
369 .await
370 .map_err(Error::BleError)
371 }
372}
373
374#[cfg(test)]
375mod test {
376 use super::*;
377
378 macro_rules! aw {
380 ($e:expr) => {
381 tokio_test::block_on($e)
382 };
383 }
384
385 #[test]
386 fn settings_ecg() {
387 let norm = StreamSettings {
388 ty: H10MeasurementType::Ecg,
389 resolution: 14,
390 range: None,
391 sample_rate: vec![130],
392 };
393
394 let data = aw!(ControlResponse::new(vec![
395 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x82, 0x00, 0x01, 0x01, 0x0e, 0x00
396 ]))
397 .unwrap();
398
399 assert_eq!(norm, StreamSettings::new(&data).unwrap());
400 }
401
402 #[test]
403 fn settings_acc() {
404 let norm = StreamSettings {
405 ty: H10MeasurementType::Acc,
406 resolution: 16,
407 range: Some(vec![2, 4, 8]),
408 sample_rate: vec![25, 50, 100, 200],
409 };
410
411 let data = aw!(ControlResponse::new(vec![
412 0xf0, 0x01, 0x02, 0x00, 0x00, 0x00, 0x04, 0x19, 0x00, 0x32, 0x00, 0x64, 0x00, 0xC8,
413 0x00, 0x01, 0x01, 0x10, 0x00, 0x02, 0x03, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00
414 ]))
415 .unwrap();
416
417 assert_eq!(norm, StreamSettings::new(&data).unwrap());
418 }
419}