rustymind/
lib.rs

1use log::{debug, error, info, warn};
2
3pub const HEADSETID_AUTOCONNECT: [u8; 1] = [0xc2];
4
5#[derive(PartialEq, Eq, Debug)]
6pub enum PacketType {
7    HeadsetConnected(u16),
8    HeadsetConnectedUndefined,
9    HeadsetNotFound(u16),
10    NoHeadsetFound,
11    NotFoundUndefined,
12    HeadsetDisconnected(u16),
13    HeadsetDisconnectedUndefined,
14    RequestDenied,
15    Standby,
16    FindHeadset,
17    StandbyPacketUndefined,
18    StandbyLengthUndefined,
19    PoorSignal(u8),
20    Attention(u8),
21    Meditation(u8),
22    Blink(u8),
23    RawValue(i16),
24    AsicEeg(AsicEeg),
25    PacketUndefined(u8),
26}
27
28pub enum State {
29    NoSync,
30    FirstSync,
31    SecondSync,
32    ValidPacket,
33}
34
35#[derive(PartialEq, Eq, Debug)]
36pub struct AsicEeg {
37    pub delta: u32,
38    pub theta: u32,
39    pub low_alpha: u32,
40    pub high_alpha: u32,
41    pub low_beta: u32,
42    pub high_beta: u32,
43    pub low_gamma: u32,
44    pub mid_gamma: u32,
45}
46
47pub struct Parser {
48    state: State,
49    plength: u8,
50    payload: Vec<u8>,
51    checksum: u8,
52}
53
54impl Parser {
55    pub fn new() -> Parser {
56        Parser {
57            state: State::NoSync,
58            plength: 0,
59            payload: Vec::new(),
60            checksum: 0,
61        }
62    }
63}
64
65impl Parser {
66    pub fn parse(&mut self, data: u8) -> Option<Vec<PacketType>> {
67        match self.state {
68            State::NoSync => {
69                self.handle_nosync(data);
70                None
71            }
72            State::FirstSync => {
73                self.handle_firstsync(data);
74                None
75            }
76            State::SecondSync => {
77                self.handle_secondsync(data);
78                None
79            }
80            State::ValidPacket => self.handle_validpacket(data),
81        }
82    }
83
84    fn reset(&mut self) {
85        *self = Parser::new();
86    }
87
88    fn handle_nosync(&mut self, data: u8) {
89        if data == 0xaa {
90            self.state = State::FirstSync;
91            debug!("Standby for a valid packet");
92        }
93    }
94
95    fn handle_firstsync(&mut self, data: u8) {
96        if data == 0xaa {
97            self.state = State::SecondSync;
98            debug!("Packet synced");
99        } else {
100            self.state = State::NoSync;
101        }
102    }
103
104    fn handle_secondsync(&mut self, data: u8) {
105        if data > 0xaa {
106            self.state = State::NoSync;
107            error!("Plength larger than 170!");
108        } else if data < 0xaa {
109            self.state = State::ValidPacket;
110            self.plength = data;
111            debug!("Valid packet available, len({})", self.plength);
112        }
113    }
114
115    fn handle_validpacket(&mut self, data: u8) -> Option<Vec<PacketType>> {
116        if self.plength == 0 {
117            self.checksum = !self.checksum;
118            let re = if data != self.checksum {
119                debug!("Checksum failed");
120                None
121            } else {
122                debug!("Checksum matched, start parsing");
123                Some(self.handle_parser())
124            };
125            self.reset();
126            re
127        } else {
128            self.payload.push(data);
129            self.checksum = self.checksum.overflowing_add(data).0;
130            self.plength -= 1;
131            None
132        }
133    }
134
135    fn handle_parser(&mut self) -> Vec<PacketType> {
136        let mut n = 0;
137        let mut result: Vec<PacketType> = Vec::new();
138        while n < self.payload.len() {
139            if self.payload[n] == 0xd0 {
140                // Headset Connected
141                if self.payload[n + 1] == 0x02 {
142                    info!(
143                        "headset connected, ID {:#04x} {:#04x}",
144                        self.payload[n + 2],
145                        self.payload[n + 3]
146                    );
147                    result.push(PacketType::HeadsetConnected(
148                        ((self.payload[n + 2] as u16) << 8) | (self.payload[n + 3] as u16),
149                    ));
150                } else {
151                    warn!("undefined packet while headset connected");
152                    result.push(PacketType::HeadsetConnectedUndefined);
153                }
154
155                n += 4;
156            } else if self.payload[n] == 0xd1 {
157                // Headset Not Found
158                if self.payload[n + 1] == 0x02 {
159                    warn!(
160                        "Headset {:#04x} {:#04x} not found",
161                        self.payload[n + 2],
162                        self.payload[n + 3]
163                    );
164                    result.push(PacketType::HeadsetNotFound(
165                        ((self.payload[n + 2] as u16) << 8) | (self.payload[n + 3] as u16),
166                    ));
167                    n += 4;
168                } else if self.payload[n + 1] == 0x00 {
169                    warn!("no headset could be found during Connect All.");
170                    result.push(PacketType::NoHeadsetFound);
171                    n += 2;
172                } else {
173                    warn!("undefined packetLength while headset not found");
174                    result.push(PacketType::NotFoundUndefined);
175                }
176            } else if self.payload[n] == 0xd2 {
177                if self.payload[n + 1] == 0x02 {
178                    info!(
179                        "disconnected from headset {:#04x} {:#04x}",
180                        self.payload[n + 2],
181                        self.payload[n + 3]
182                    );
183                    result.push(PacketType::HeadsetDisconnected(
184                        ((self.payload[n + 2] as u16) << 8) | (self.payload[n + 3] as u16),
185                    ));
186                } else {
187                    warn!("undefined packetLength while headset disconnected");
188                    result.push(PacketType::HeadsetDisconnectedUndefined);
189                }
190                n += 4;
191            } else if self.payload[n] == 0xd3 {
192                if self.payload[n + 1] == 0x00 {
193                    warn!("the last command request was denied");
194                    result.push(PacketType::RequestDenied);
195                } else {
196                    warn!("undefined packetLength while headset disconnected");
197                    result.push(PacketType::HeadsetDisconnectedUndefined);
198                }
199                n += 2;
200            } else if self.payload[n] == 0xd4 {
201                if self.payload[n + 1] == 0x01 {
202                    if self.payload[n + 2] == 0x00 {
203                        debug!("headset is in standby mode awaiting for a command");
204                        result.push(PacketType::Standby);
205                    } else if self.payload[n + 2] == 0x01 {
206                        debug!("connecting to a headset");
207                        result.push(PacketType::FindHeadset);
208                    } else {
209                        warn!("undefined packet code while standby");
210                        result.push(PacketType::StandbyPacketUndefined);
211                    }
212                } else {
213                    warn!("undefined packet length while standby");
214                    result.push(PacketType::StandbyLengthUndefined);
215                }
216                n += 3;
217            } else if self.payload[n] == 0x02 {
218                // poor signal
219                if self.payload[n + 1] == 200 {
220                    warn!("the ThinkGear contacts are not touching the user's skin");
221                } else {
222                    debug!("Poor signal quality {:#04x}", self.payload[n + 1]);
223                }
224                result.push(PacketType::PoorSignal(self.payload[n + 1]));
225                n += 2;
226            } else if self.payload[n] == 0x04 {
227                // attention
228                debug!("Attention esense {:#04x}", self.payload[n + 1]);
229                result.push(PacketType::Attention(self.payload[n + 1]));
230                n += 2;
231            } else if self.payload[n] == 0x05 {
232                // meditation
233                debug!("Meditation esense {:#04x}", self.payload[n + 1]);
234                result.push(PacketType::Meditation(self.payload[n + 1]));
235                n += 2;
236            } else if self.payload[n] == 0x16 {
237                // blink
238                debug!("Blink strength {:#04x}", self.payload[n + 1]);
239                result.push(PacketType::Blink(self.payload[n + 1]));
240                n += 2;
241            } else if self.payload[n] == 0x80 {
242                // RAW Wave Value: a single big-endian 16-bit two's-compliment signed value
243                // (high-order byte followed by low-order byte) (-32768 to 32767)
244                let raw_val: i16 =
245                    ((self.payload[n + 2] as i16) << 8) | (self.payload[n + 3] as i16);
246                debug!("Raw value {:#04x}", raw_val);
247                result.push(PacketType::RawValue(raw_val));
248                n += 4;
249            } else if self.payload[n] == 0x83 {
250                //ASIC_EEG_POWER: eight big-endian 3-byte unsigned integer values representing
251                //delta, theta, low-alpha high-alpha, low-beta, high-beta, low-gamma, and mid-gamma
252                //EEG band power values
253                let mut eeg_vec: Vec<u32> = vec![];
254
255                for i in 0..8 {
256                    let asic = ((self.payload[n + 2 + i * 3] as u32) << 16)
257                        | ((self.payload[n + 3 + i * 3] as u32) << 8)
258                        | (self.payload[n + 4 + i * 3] as u32);
259                    eeg_vec.push(asic);
260                }
261
262                let eeg_power = AsicEeg {
263                    delta: eeg_vec[0],
264                    theta: eeg_vec[1],
265                    low_alpha: eeg_vec[2],
266                    high_alpha: eeg_vec[3],
267                    low_beta: eeg_vec[4],
268                    high_beta: eeg_vec[5],
269                    low_gamma: eeg_vec[6],
270                    mid_gamma: eeg_vec[7],
271                };
272                debug!("EEG power values = {:?}", eeg_power);
273                result.push(PacketType::AsicEeg(eeg_power));
274                n += 26;
275            } else {
276                warn!("packet code undefined {:#04x}", self.payload[n]);
277                result.push(PacketType::PacketUndefined(self.payload[n]));
278                n += 1;
279            }
280        }
281        debug!("end of packet");
282        result
283    }
284}
285
286pub fn connect_headset(
287    path: &str,
288    headset: &[u8],
289) -> Result<Box<dyn serialport::SerialPort>, &'static str> {
290    let mut port = serialport::new(path, 115_200)
291        .timeout(core::time::Duration::from_millis(1000))
292        .open()
293        .map_err(|_| "Cannot connect to dongle. Please make sure the serial number of your dongle is correct.")?;
294
295    const DISCONNECT: u8 = 0xc1;
296    const CONNECT: u8 = 0xc0;
297    let mut serial_buf: Vec<u8> = vec![0];
298
299    port.write(&[DISCONNECT])
300        .map_err(|_| "Failed to write DISCONNECT to dongle.")?;
301    port.read(serial_buf.as_mut_slice())
302        .map_err(|_| "Cannot read data from dongle.")?;
303    if headset.len() != 1 {
304        port.write(&[CONNECT])
305            .map_err(|_| "Failed to write CONNECT to dongle.")?;
306    }
307    port.write(&headset)
308        .map_err(|_| "Failed to write headset ID to dongle.")?;
309    return Ok(port);
310}
311
312#[cfg(test)]
313mod tests {
314    use super::*;
315    use pretty_assertions::assert_eq;
316
317    #[test]
318    fn test_parser() {
319        let test_vec: Vec<u8> = vec![
320            0xAA, // [SYNC]
321            0xAA, // [SYNC]
322            0x20, // [PLENGTH] (payload length) of 32 bytes
323            0x02, // [POOR_SIGNAL] Quality
324            0x00, // No poor signal detected (0/200)
325            0x83, // [ASIC_EEG_POWER_INT]
326            0x18, // [VLENGTH] 24 bytes
327            0x00, // (1/3) Begin Delta bytes
328            0x00, // (2/3)
329            0x94, // (3/3)
330            0x00, // (1/3)
331            0x00, // (2/3)
332            0x42, // (3/3)
333            0x00, // (1/3)
334            0x00, // (2/3)
335            0x0B, // (3/3)
336            0x00, // (1/3)
337            0x00, // (2/3)
338            0x64, // (3/3)
339            0x00, // (1/3)
340            0x00, // (2/3)
341            0x4D, // (3/3)
342            0x00, // (1/3)
343            0x00, // (2/3)
344            0x3D, // (3/3)
345            0x00, // (1/3)
346            0x00, // (2/3)
347            0x07, // (3/3)
348            0x00, // (1/3)
349            0x00, // (2/3)
350            0x05, // (3/3)
351            0x04, // [ATTENTION] eSense
352            0x0D, // eSense Attention level of 13
353            0x05, // [MEDITATION] eSense
354            0x3D, // eSense Meditation level of 61
355            0x34, // [CHKSUM] (1's comp inverse of 8-bit Payload sum of 0xCB)
356        ];
357        let mut result: Vec<PacketType> = Vec::new();
358        let mut parser = Parser::new();
359        for data in test_vec {
360            if let Some(x) = parser.parse(data) {
361                result = x;
362            }
363        }
364
365        let test_asic = AsicEeg {
366            delta: 0x94,
367            theta: 0x42,
368            low_alpha: 0x0b,
369            high_alpha: 0x64,
370            low_beta: 0x4d,
371            high_beta: 0x3d,
372            low_gamma: 0x07,
373            mid_gamma: 0x05,
374        };
375
376        assert_eq!(
377            result,
378            vec![
379                PacketType::PoorSignal(0x00),
380                PacketType::AsicEeg(test_asic),
381                PacketType::Attention(0x0d),
382                PacketType::Meditation(0x3d)
383            ]
384        );
385    }
386}