braid_triggerbox_comms/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(feature = "std"))]
4use defmt::Format;
5
6#[cfg(not(feature = "std"))]
7extern crate core as std;
8
9pub const DEVICE_FIRMWARE_VERSION: u8 = 14;
10
11#[derive(Clone, PartialEq, Debug)]
12#[cfg_attr(not(feature = "std"), derive(Format))]
13pub enum SyncVal {
14    Sync0,
15    Sync1,
16    Sync2,
17}
18
19#[derive(Clone, PartialEq, Debug)]
20#[cfg_attr(not(feature = "std"), derive(Format))]
21pub struct TopAndPrescaler {
22    avr_icr1: u16,
23    prescaler_key: u8,
24}
25
26impl TopAndPrescaler {
27    pub fn new_avr(top: u16, prescaler: Prescaler) -> Self {
28        let prescaler_key = match prescaler {
29            Prescaler::Scale8 => b'1',
30            Prescaler::Scale64 => b'2',
31        };
32
33        Self {
34            avr_icr1: top,
35            prescaler_key,
36        }
37    }
38    #[inline]
39    pub fn avr_icr1(&self) -> u16 {
40        self.avr_icr1
41    }
42
43    #[inline]
44    pub fn prescaler_key(&self) -> u8 {
45        self.prescaler_key
46    }
47}
48
49#[derive(Debug, Clone)]
50pub enum Prescaler {
51    Scale8,
52    Scale64,
53}
54
55impl Prescaler {
56    pub fn as_f64(&self) -> f64 {
57        match self {
58            Prescaler::Scale8 => 8.0,
59            Prescaler::Scale64 => 64.0,
60        }
61    }
62}
63
64#[derive(Clone, PartialEq, Debug)]
65#[cfg_attr(not(feature = "std"), derive(Format))]
66pub struct NewAOut {
67    pub aout0: u16,
68    pub aout1: u16,
69    pub aout_sequence: u8,
70}
71
72#[derive(Clone, PartialEq, Debug)]
73#[cfg_attr(not(feature = "std"), derive(Format))]
74pub enum UdevMsg {
75    Query,
76    Set([u8; 8]),
77}
78
79#[derive(Clone, PartialEq, Debug)]
80#[cfg_attr(not(feature = "std"), derive(Format))]
81pub enum UsbEvent {
82    TimestampQuery(u8),
83    VersionRequest,
84    Sync(SyncVal),
85    SetTop(TopAndPrescaler),
86    SetAOut(NewAOut),
87    Udev(UdevMsg),
88    SetLed(LedInfo),
89}
90
91#[derive(Clone, PartialEq, Debug)]
92#[cfg_attr(not(feature = "std"), derive(Format))]
93pub struct LedInfo {
94    /// The duration the LED is on in each pulse (in microseceonds)
95    ///
96    /// This is the maximum value. Regardless of the value specified here, the
97    /// LED on time cannot be longer than the inter-frame interval. Furthermore,
98    /// it may also be limited by [Self::max_overall_duty_cycle].
99    pub max_duration_usec: u32,
100    /// The maximum fraction of time the LED is on
101    ///
102    /// This is a safety measure to reduce risk of destroying LEDs. When
103    /// overdriving LEDs, it may be that they should only be on for a fraction
104    /// of the time, e.g. 10% of the time. Setting this value to anything less
105    /// than 1.0 will limit the LED on time to a fraction of total time. The
106    /// [Self::nth_frame] field is taken into account within this computation.
107    pub max_overall_duty_cycle: f32,
108    /// What interval the LED is on
109    ///
110    /// 0: never
111    /// 1: every frame
112    /// 2: every second frame
113    /// 3: every third frame
114    /// 4: every fourth frame
115    pub nth_frame: u8,
116}
117
118impl Default for LedInfo {
119    fn default() -> Self {
120        Self {
121            max_duration_usec: 5_000,
122            max_overall_duty_cycle: 1.0,
123            nth_frame: 2,
124        }
125    }
126}
127
128const LED_MSG_BYTES: usize = 11;
129
130impl LedInfo {
131    #[cfg(feature = "std")]
132    pub fn encode(&self) -> [u8; LED_MSG_BYTES] {
133        let mut result: [u8; LED_MSG_BYTES] = [0; LED_MSG_BYTES];
134
135        result[0..2].copy_from_slice(b"L=");
136        result[2..6].copy_from_slice(&self.max_duration_usec.to_le_bytes());
137        result[6..10].copy_from_slice(&self.max_overall_duty_cycle.to_le_bytes());
138        result[10] = self.nth_frame;
139
140        result
141    }
142
143    pub fn raw_decode(buf: &[u8]) -> Result<LedInfo, Error> {
144        assert_eq!(buf.len(), LED_MSG_BYTES);
145        let arr = buf[2..6].try_into().unwrap();
146        let max_duration_usec = u32::from_le_bytes(arr);
147
148        let arr = buf[6..10].try_into().unwrap();
149        let max_overall_duty_cycle = f32::from_le_bytes(arr);
150
151        let nth_frame = buf[10];
152        Ok(LedInfo {
153            max_duration_usec,
154            max_overall_duty_cycle,
155            nth_frame,
156        })
157    }
158}
159
160#[derive(Clone, PartialEq, Debug)]
161#[cfg_attr(not(feature = "std"), derive(Format))]
162struct AccumState {
163    last_update: u64,
164}
165
166impl Default for AccumState {
167    fn default() -> Self {
168        Self { last_update: 0 }
169    }
170}
171
172pub const BUF_MAX_SZ: usize = 32;
173
174/// Drop received data older than this (0.5 seconds).
175const MAX_AGE_USEC: u64 = 500_000;
176
177#[derive(Debug, Clone, PartialEq)]
178#[cfg_attr(not(feature = "std"), derive(Format))]
179pub enum Error {
180    AwaitingMoreData,
181    UnknownSyncValue,
182    UnknownData,
183}
184
185enum PState {
186    Empty,
187    Accumulating(AccumState),
188}
189
190pub struct PacketParser<'bb> {
191    /// buffer of accumulated input data
192    prod: bbqueue::Producer<'bb, BUF_MAX_SZ>,
193    cons: bbqueue::Consumer<'bb, BUF_MAX_SZ>,
194    state: PState,
195}
196
197impl<'bb> PacketParser<'bb> {
198    pub fn new(backing_store: &'bb bbqueue::BBBuffer<BUF_MAX_SZ>) -> Self {
199        // let bb: bbqueue::BBBuffer<BUF_MAX_SZ> = bbqueue::BBBuffer::new();
200        let (prod, cons) = backing_store.try_split().unwrap();
201
202        Self {
203            prod,
204            cons,
205            state: PState::Empty,
206        }
207    }
208
209    /// parse incoming data
210    pub fn got_buf(&mut self, now_usec: u64, buf: &[u8]) -> Result<UsbEvent, Error> {
211        let mut accum_state = match &self.state {
212            PState::Empty => AccumState::default(),
213            PState::Accumulating(old_accum_state) => {
214                if (now_usec - old_accum_state.last_update) <= MAX_AGE_USEC {
215                    old_accum_state.clone()
216                } else {
217                    match self.cons.read() {
218                        Ok(rgrant) => {
219                            let sz = rgrant.len();
220                            rgrant.release(sz);
221                        }
222                        Err(bbqueue::Error::InsufficientSize) => { /*already empty*/ }
223                        Err(e) => {
224                            panic!("error: {:?}", e);
225                        }
226                    }
227                    AccumState::default()
228                }
229            }
230        };
231
232        let mut wgrant = self.prod.grant_exact(buf.len()).unwrap();
233        wgrant.clone_from_slice(buf);
234        wgrant.commit(buf.len());
235        accum_state.last_update = now_usec;
236
237        self.state = PState::Accumulating(accum_state);
238
239        let rgrant = self.cons.read().unwrap();
240        let buf = rgrant.buf();
241
242        let mut consumed_bytes = 0;
243
244        let mut result = Err(Error::AwaitingMoreData);
245
246        if buf.len() >= 2 {
247            if buf == b"V?" {
248                result = Ok(UsbEvent::VersionRequest);
249                consumed_bytes = 2;
250            } else if buf[0] == b'P' {
251                result = Ok(UsbEvent::TimestampQuery(buf[1]));
252                consumed_bytes = 2;
253            } else if buf == b"N?" {
254                result = Ok(UsbEvent::Udev(UdevMsg::Query));
255                consumed_bytes = 2;
256            } else if buf[0] == b'S' {
257                result = match buf[1] {
258                    b'0' => Ok(UsbEvent::Sync(SyncVal::Sync0)),
259                    b'1' => Ok(UsbEvent::Sync(SyncVal::Sync1)),
260                    b'2' => Ok(UsbEvent::Sync(SyncVal::Sync2)),
261                    _ => Err(Error::UnknownSyncValue),
262                };
263                consumed_bytes = 2;
264            } else if buf[0] == b'T' && buf[1] == b'=' {
265                if buf.len() < 5 {
266                    // wait for more data
267                } else {
268                    let value0 = buf[2];
269                    let value1 = buf[3];
270                    let prescaler_key = buf[4];
271
272                    let avr_icr1 = u16::from_le_bytes([value0, value1]);
273                    result = Ok(UsbEvent::SetTop(TopAndPrescaler {
274                        avr_icr1,
275                        prescaler_key,
276                    }));
277                    consumed_bytes = 5;
278                }
279            } else if buf[0] == b'L' && buf[1] == b'=' {
280                if buf.len() < LED_MSG_BYTES {
281                    // wait for more data
282                } else {
283                    let x2 = LedInfo::raw_decode(&buf[..LED_MSG_BYTES]).unwrap();
284                    result = Ok(UsbEvent::SetLed(x2));
285                    consumed_bytes = LED_MSG_BYTES;
286                }
287            } else if buf[0] == b'O' && buf[1] == b'=' {
288                if buf.len() < 7 {
289                    // wait for more data
290                } else {
291                    let aout0_0 = buf[2];
292                    let aout0_1 = buf[3];
293                    let aout0 = u16::from_le_bytes([aout0_0, aout0_1]);
294
295                    let aout1_0 = buf[4];
296                    let aout1_1 = buf[5];
297                    let aout1 = u16::from_le_bytes([aout1_0, aout1_1]);
298
299                    let aout_sequence = buf[6];
300
301                    result = Ok(UsbEvent::SetAOut(NewAOut {
302                        aout0,
303                        aout1,
304                        aout_sequence,
305                    }));
306                    consumed_bytes = 7;
307                }
308            } else {
309                result = Err(Error::UnknownData);
310                consumed_bytes = 2;
311                // let str_buf = core::str::from_utf8(buf).unwrap_or("??");
312                // todo!("unprocessed: '{}' ({:?})", str_buf, buf);
313            }
314        }
315
316        rgrant.release(consumed_bytes);
317        result
318    }
319}
320
321// Run with: cargo test --target x86_64-pc-windows-msvc --features std
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    fn check_simple(buf: &[u8], expected: &UsbEvent) {
327        // test 1 - simple normal situation
328        let bb: bbqueue::BBBuffer<BUF_MAX_SZ> = bbqueue::BBBuffer::new();
329        let mut pp = PacketParser::new(&bb);
330        let parsed = pp.got_buf(0, buf);
331        assert_eq!(parsed, Ok(expected.clone()));
332    }
333
334    fn check_stale(buf: &[u8], expected: &UsbEvent) {
335        // test 2 - old stale data present
336        let bb: bbqueue::BBBuffer<BUF_MAX_SZ> = bbqueue::BBBuffer::new();
337        let mut pp = PacketParser::new(&bb);
338        pp.got_buf(0, b"P").ok();
339        let parsed = pp.got_buf(MAX_AGE_USEC + 1, buf);
340        assert_eq!(parsed, Ok(expected.clone()));
341    }
342
343    fn check_multiple(buf: &[u8], expected: &UsbEvent) {
344        // test 2 - old stale data present
345        let bb: bbqueue::BBBuffer<BUF_MAX_SZ> = bbqueue::BBBuffer::new();
346        let mut pp = PacketParser::new(&bb);
347        assert_eq!(Ok(UsbEvent::TimestampQuery(b'2')), pp.got_buf(0, b"P2"));
348        let parsed = pp.got_buf(0, buf);
349        assert_eq!(parsed, Ok(expected.clone()));
350        assert_eq!(Ok(UsbEvent::TimestampQuery(b'3')), pp.got_buf(0, b"P3"));
351    }
352
353    #[test]
354    fn manual_serialization() {
355        for (buf, expected) in &[
356            (&b"V?"[..], UsbEvent::VersionRequest),
357            (&b"P1"[..], UsbEvent::TimestampQuery(b'1')),
358            (&b"S0"[..], UsbEvent::Sync(SyncVal::Sync0)),
359            (&b"S1"[..], UsbEvent::Sync(SyncVal::Sync1)),
360            (&b"S2"[..], UsbEvent::Sync(SyncVal::Sync2)),
361            /*
362            SetTop(TopAndPrescaler),
363            SetAOut(NewAOut),
364            Udev(UdevMsg),
365            SetLed(LedInfo),
366                    */
367        ][..]
368        {
369            check_simple(buf, &expected);
370            check_stale(buf, &expected);
371            check_multiple(buf, &expected);
372        }
373    }
374
375    #[test]
376    fn set_led_roundtrip() {
377        let x1 = LedInfo::default();
378        let buf = x1.encode();
379        let x2 = LedInfo::raw_decode(&buf[..]).unwrap();
380        assert_eq!(x1, x2);
381    }
382}