1#![feature(type_changing_struct_update)]
5#![cfg_attr(not(test), no_std)]
6
7use core::convert::Infallible;
8use core::marker::PhantomData;
9
10#[allow(unused_imports)]
11use defmt::{debug, error, info, trace, warn};
12
13use embedded_hal::blocking::delay::DelayMs;
14use embedded_hal::blocking::i2c::{Read, SevenBitAddress, Write};
15use heapless::{String, Vec};
16use serde::{de::DeserializeOwned, Deserialize, Serialize};
17
18pub mod card;
19pub mod dfu;
20pub mod hub;
21pub mod note;
22pub mod web;
23pub mod ntn;
24
25const RESPONSE_DELAY: u16 = 25;
27
28pub const DEFAULT_BUF_SIZE: usize = 18 * 1024;
31
32#[derive(Debug, defmt::Format)]
33pub struct NotecardConfig {
34 pub i2c_addr: u8,
36
37 pub response_timeout: u16,
39
40 pub chunk_delay: u16,
47
48 pub segment_delay: u16,
56}
57
58impl Default for NotecardConfig {
59 fn default() -> Self {
60 NotecardConfig {
61 i2c_addr: 0x17,
62 response_timeout: 5000,
63 chunk_delay: 20,
64 segment_delay: 250,
65 }
66 }
67}
68
69#[derive(Debug, defmt::Format)]
70pub enum NoteState {
71 Handshake,
73
74 Request,
76
77 Poll(usize),
79
80 Response(usize),
82
83 ResponseReady,
85}
86
87#[derive(Debug, defmt::Format, Clone)]
88pub enum NoteError {
89 I2cWriteError,
90
91 I2cReadError,
92
93 DeserError(String<256>),
94
95 SerError,
96
97 InvalidRequest,
99
100 RemainingData,
101
102 TimeOut,
103
104 BufOverflow,
105
106 WrongState,
108
109 DFUInProgress,
111
112 NonPortNoteInPackageMode,
114
115 FileStorageFull(String<256>),
117
118 ErrorAddingNote(String<256>),
120
121 NotecardErr(String<256>),
122}
123
124impl NoteError {
125 pub fn new_desererror(msg: &[u8]) -> NoteError {
126 let msg = core::str::from_utf8(msg).unwrap_or("[invalid utf-8]");
127 let mut s = String::new();
128 s.push_str(msg).ok();
129 NoteError::DeserError(s)
130 }
131
132 pub fn string_err(_e: Infallible) -> NoteError {
133 NoteError::BufOverflow
134 }
135}
136
137pub(crate) fn str_string<const N: usize>(
138 a: Option<&str>,
139) -> Result<Option<heapless::String<N>>, NoteError> {
140 a.map(heapless::String::try_from)
141 .transpose()
142 .map_err(NoteError::string_err)
143}
144
145#[derive(Deserialize, Debug, defmt::Format)]
146pub struct NotecardError {
147 err: String<256>,
148}
149
150impl From<NotecardError> for NoteError {
151 fn from(n: NotecardError) -> NoteError {
152 if n.err.contains("{dfu-in-progress}") {
153 NoteError::DFUInProgress
154 } else if n.err.contains("adding notes to a non-uplinked port is not allowed") {
155 NoteError::NonPortNoteInPackageMode
156 } else if n.err.contains("{file-storage-full}") {
157 NoteError::FileStorageFull(n.err)
158 } else if n.err.contains("error adding note") {
159 NoteError::ErrorAddingNote(n.err)
160 } else {
161 NoteError::NotecardErr(n.err)
162 }
163 }
164}
165
166pub struct Notecard<
168 IOM: Write<SevenBitAddress> + Read<SevenBitAddress>,
169 const BUF_SIZE: usize = DEFAULT_BUF_SIZE,
170> {
171 i2c: IOM,
172 addr: u8,
173 state: NoteState,
174
175 buf: Vec<u8, BUF_SIZE>,
177
178 response_timeout: u16,
179 chunk_delay: u16,
180 segment_delay: u16,
181}
182
183pub struct SuspendState<const BUF_SIZE: usize> {
184 addr: u8,
185 state: NoteState,
186 buf: Vec<u8, BUF_SIZE>,
187 response_timeout: u16,
188 chunk_delay: u16,
189 segment_delay: u16,
190}
191
192impl<IOM: Write<SevenBitAddress> + Read<SevenBitAddress>, const BUF_SIZE: usize>
193 Notecard<IOM, BUF_SIZE>
194{
195 pub fn new(i2c: IOM) -> Notecard<IOM, BUF_SIZE> {
196 Self::new_with_config(i2c, NotecardConfig::default())
197 }
198
199 pub fn new_with_config(i2c: IOM, c: NotecardConfig) -> Notecard<IOM, BUF_SIZE> {
200 Notecard {
201 i2c,
202 addr: c.i2c_addr,
203 state: NoteState::Handshake,
204 buf: Vec::new(),
205
206 response_timeout: c.response_timeout,
207 chunk_delay: c.chunk_delay,
208 segment_delay: c.segment_delay,
209 }
210 }
211
212 pub fn resize_buf<const B: usize>(self) -> Result<Notecard<IOM, B>, NoteError> {
215 if B < self.buf.len() {
216 Err(NoteError::BufOverflow)
217 } else {
218 Ok(Notecard {
219 buf: Vec::<_, B>::from_slice(&self.buf).unwrap(),
220 ..self
221 })
222 }
223 }
224
225 pub fn suspend(self) -> (IOM, SuspendState<BUF_SIZE>) {
229 (
230 self.i2c,
231 SuspendState {
232 state: self.state,
233 buf: self.buf,
234 addr: self.addr,
235 response_timeout: self.response_timeout,
236 chunk_delay: self.chunk_delay,
237 segment_delay: self.segment_delay,
238 },
239 )
240 }
241
242 pub fn resume(i2c: IOM, state: SuspendState<BUF_SIZE>) -> Notecard<IOM, BUF_SIZE> {
244 Notecard {
245 i2c,
246 addr: state.addr,
247 state: state.state,
248 buf: state.buf,
249 response_timeout: state.response_timeout,
250 chunk_delay: state.chunk_delay,
251 segment_delay: state.segment_delay,
252 }
253 }
254
255 pub fn initialize(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
257 info!("note: initializing.");
258 self.reset(delay)
259 }
260
261 pub fn ping(&mut self) -> bool {
265 self.i2c.write(self.addr, &[]).is_ok()
266 }
267
268 pub fn data_query(&mut self) -> Result<usize, NoteError> {
270 trace!("note: data_query: {:?}", self.state);
271 if !matches!(self.state, NoteState::Response(_)) {
272 self.i2c
274 .write(self.addr, &[0, 0])
275 .map_err(|_| NoteError::I2cWriteError)?;
276
277 let mut buf = [0u8; 2];
278
279 self.i2c
281 .read(self.addr, &mut buf)
282 .map_err(|_| NoteError::I2cReadError)?;
283
284 let available = buf[0] as usize;
285 let sent = buf[1] as usize;
286
287 if available > 0 {
288 self.buf.clear();
289 self.state = NoteState::Response(available);
290 }
291
292 trace!("avail = {}, sent = {}", available, sent);
293
294 if sent > 0 {
295 error!(
296 "data query: bytes sent when querying available bytes: {}",
297 sent
298 );
299 Err(NoteError::RemainingData)
300 } else {
301 Ok(available)
302 }
303 } else {
304 error!("note: data_query called while reading response.");
305 Err(NoteError::WrongState)
306 }
307 }
308
309 fn read(&mut self) -> Result<usize, NoteError> {
311 if let NoteState::Response(avail) = self.state {
312 let mut bytes = Vec::<u8, 128>::new();
314
315 let sz = (bytes.capacity() - 2).min(avail);
316 bytes.resize(sz + 2, 0).unwrap();
317
318 debug!("asking to read: {} of available {} bytes", sz, avail);
319
320 self.i2c
322 .write(self.addr, &[0, sz as u8])
323 .map_err(|_| NoteError::I2cWriteError)?;
324
325 self.i2c
327 .read(self.addr, &mut bytes)
328 .map_err(|_| NoteError::I2cReadError)?;
329
330 let available = bytes[0] as usize;
331 let sent = bytes[1] as usize;
332
333 self.buf.extend_from_slice(&bytes[2..]).unwrap(); trace!("read: {}", unsafe {
336 core::str::from_utf8_unchecked(&bytes)
337 });
338
339 trace!("avail = {}, sent = {}", available, sent);
340
341 if available > 0 {
342 self.state = NoteState::Response(available);
343 } else {
344 self.state = NoteState::ResponseReady;
345 }
346
347 Ok(available)
348 } else {
349 error!("read: called when not waiting for response");
350 Err(NoteError::WrongState)
351 }
352 }
353
354 fn take_response(&mut self) -> Result<&[u8], NoteError> {
363 if matches!(self.state, NoteState::ResponseReady) {
364 self.state = NoteState::Request;
365
366 Ok(&self.buf)
367 } else {
368 error!("take response called when response not ready");
369 Err(NoteError::WrongState)
370 }
371 }
372
373 fn poll(&mut self) -> Result<Option<&[u8]>, NoteError> {
375 trace!("note: poll: {:?}", self.state);
376 match self.state {
377 NoteState::Poll(_) => {
378 let sz = self.data_query()?;
380 if sz > 0 {
381 debug!("response ready: {} bytes..", sz);
382
383 self.poll()
384 } else {
385 Ok(None)
387 }
388 }
389 NoteState::Response(_) => {
390 let avail = self.read()?;
391 if avail == 0 {
392 self.poll()
393 } else {
394 Ok(None)
396 }
397 }
398 NoteState::ResponseReady => {
399 debug!("response read, deserializing.");
400 Ok(Some(self.take_response()?))
401 }
402 _ => {
403 error!("poll called when not receiving response");
404 Err(NoteError::WrongState)
405 }
406 }
407 }
408
409 unsafe fn consume_response(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
412 warn!("note: trying to consume any left-over response.");
413 let mut waited = 0;
414
415 while waited < self.response_timeout {
416 if (self.poll()?).is_some() {
417 self.buf.clear();
418 return Ok(());
419 }
420
421 delay.delay_ms(RESPONSE_DELAY);
422 waited += RESPONSE_DELAY;
423 }
424
425 self.buf.clear();
426
427 error!("response timed out (>= {}).", self.response_timeout);
428 Err(NoteError::TimeOut)
429 }
430
431 pub fn reset(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
435 warn!("resetting: consuming any left-over response and perform a new handshake.");
436
437 self.buf.clear(); self.state = NoteState::Handshake;
439 self.handshake(delay)
440 }
441
442 fn handshake(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
443 if matches!(self.state, NoteState::Handshake) {
444 debug!("note: handshake");
445 if self.data_query()? > 0 {
446 error!("note: handshake: remaining data in queue, consuming..");
447 unsafe { self.consume_response(delay)? };
448 }
449
450 self.state = NoteState::Request;
451 }
452 Ok(())
453 }
454
455 fn send_request(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
457 const CHUNK_LENGTH_MAX: usize = 127;
459 const CHUNK_LENGTH_I: usize = 30;
462 const CHUNK_LENGTH: usize = if CHUNK_LENGTH_I < CHUNK_LENGTH_MAX {
463 CHUNK_LENGTH_I
464 } else {
465 CHUNK_LENGTH_MAX
466 };
467
468 const SEGMENT_LENGTH: usize = (250 / CHUNK_LENGTH) * CHUNK_LENGTH;
472
473 if !matches!(self.state, NoteState::Request) {
474 warn!("note: request: wrong-state, resetting before new request.");
475 let buf = self.buf.clone();
476
477 self.reset(delay)?; self.buf.clear();
480 self.buf
481 .resize(buf.len(), 0)
482 .map_err(|_| NoteError::BufOverflow)?;
483 self.buf.copy_from_slice(&buf);
484 }
485
486 if self.buf.last() != Some(&b'\n') {
487 return Err(NoteError::InvalidRequest);
488 }
489
490 trace!("note: making request: {}", unsafe {
491 core::str::from_utf8_unchecked(&self.buf)
492 });
493
494 let mut buf = Vec::<u8, { CHUNK_LENGTH + 1 }>::new();
495 for segment in self.buf.chunks(SEGMENT_LENGTH) {
496 for c in segment.chunks(buf.capacity() - 1) {
497 buf.push(c.len() as u8).unwrap();
498 buf.extend_from_slice(c).unwrap();
499
500 trace!("note: sending chunk: {} => {}", &buf, unsafe {
501 core::str::from_utf8_unchecked(&buf)
502 });
503
504 self.i2c
505 .write(self.addr, &buf)
506 .map_err(|_| NoteError::I2cWriteError)?;
507
508 buf.clear();
509 delay.delay_ms(self.chunk_delay);
510 }
511 delay.delay_ms(self.segment_delay);
512 }
513
514 self.state = NoteState::Poll(0);
515
516 Ok(())
517 }
518
519 pub(crate) fn request_raw(
522 &mut self,
523 delay: &mut impl DelayMs<u16>,
524 cmd: &[u8],
525 ) -> Result<(), NoteError> {
526 self.buf.clear();
527 self.buf
528 .resize(cmd.len(), 0)
529 .map_err(|_| NoteError::BufOverflow)?;
530
531 self.buf.copy_from_slice(cmd);
532
533 self.send_request(delay)
534 }
535
536 pub(crate) fn request<T: Serialize>(
540 &mut self,
541 delay: &mut impl DelayMs<u16>,
542 cmd: T,
543 ) -> Result<(), NoteError> {
544 self.buf.clear();
545 self.buf.resize(self.buf.capacity(), 0).unwrap(); let sz = serde_json_core::to_slice(&cmd, &mut self.buf).map_err(|_| NoteError::SerError)?;
548 self.buf.truncate(sz);
549
550 self.buf.push(b'\n').map_err(|_| NoteError::SerError)?;
552 self.send_request(delay)
553 }
554
555 pub fn card(&mut self) -> card::Card<'_, IOM, BUF_SIZE> {
557 card::Card::from(self)
558 }
559
560 pub fn note(&mut self) -> note::Note<'_, IOM, BUF_SIZE> {
562 note::Note::from(self)
563 }
564
565 pub fn web(&mut self) -> web::Web<'_, IOM, BUF_SIZE> {
567 web::Web::from(self)
568 }
569
570 pub fn hub(&mut self) -> hub::Hub<'_, IOM, BUF_SIZE> {
572 hub::Hub::from(self)
573 }
574
575 pub fn dfu(&mut self) -> dfu::DFU<'_, IOM, BUF_SIZE> {
577 dfu::DFU::from(self)
578 }
579
580 pub fn ntn(&mut self) -> ntn::NTN<'_, IOM, BUF_SIZE> {
582 ntn::NTN::from(self)
583 }
584}
585
586#[must_use = "The response must be waited for and consumed, otherwise the notecard is left in an inconsistent state"]
593pub struct FutureResponse<
594 'a,
595 T: DeserializeOwned,
596 IOM: Write<SevenBitAddress> + Read<SevenBitAddress>,
597 const BUF_SIZE: usize,
598> {
599 note: &'a mut Notecard<IOM, BUF_SIZE>,
600 _r: PhantomData<T>,
601}
602
603impl<
604 'a,
605 T: DeserializeOwned,
606 IOM: Write<SevenBitAddress> + Read<SevenBitAddress>,
607 const BUF_SIZE: usize,
608 > FutureResponse<'a, T, IOM, BUF_SIZE>
609{
610 fn from(note: &'a mut Notecard<IOM, BUF_SIZE>) -> FutureResponse<'a, T, IOM, BUF_SIZE> {
611 FutureResponse {
612 note,
613 _r: PhantomData,
614 }
615 }
616
617 pub fn poll(&mut self) -> Result<Option<T>, NoteError> {
619 match self.note.poll()? {
620 Some(body) if body.starts_with(br##"{"err":"##) => {
621 debug!(
622 "response is error response, parsing error..: {}",
623 core::str::from_utf8(body).unwrap_or("[invalid utf-8]")
624 );
625 Err(
626 serde_json_core::from_slice::<NotecardError>(body).map_or_else(
627 |_| {
628 error!(
629 "failed to deserialize: {}",
630 core::str::from_utf8(body).unwrap_or("[invalid utf-8]")
631 );
632 NoteError::new_desererror(body)
633 },
634 |(e, _)| NoteError::from(e),
635 ),
636 )
637 }
638 Some(body) => {
639 trace!("response is regular, parsing..");
640 Ok(Some(
641 serde_json_core::from_slice::<T>(body)
642 .map_err(|_| {
643 error!(
644 "failed to deserialize: {}",
645 core::str::from_utf8(body).unwrap_or("[invalid utf-8]")
646 );
647 NoteError::new_desererror(body)
648 })?
649 .0,
650 ))
651 }
652 None => Ok(None),
653 }
654 }
655
656 pub fn wait_raw(mut self, delay: &mut impl DelayMs<u16>) -> Result<&'a [u8], NoteError> {
659 let mut waited = 0;
660
661 while waited < self.note.response_timeout {
662 if self.poll()?.is_some() {
663 return self.note.take_response()
664 }
665
666 delay.delay_ms(RESPONSE_DELAY);
667 waited += RESPONSE_DELAY;
668 }
669
670 error!("response timed out (>= {}).", self.note.response_timeout);
671 Err(NoteError::TimeOut)
672 }
673
674 pub fn wait(mut self, delay: &mut impl DelayMs<u16>) -> Result<T, NoteError> {
676 let mut waited = 0;
677
678 while waited < self.note.response_timeout {
679 if let Some(r) = self.poll()? {
680 return Ok(r)
681 }
682
683 delay.delay_ms(RESPONSE_DELAY);
684 waited += RESPONSE_DELAY;
685 }
686
687 error!("response timed out (>= {}).", self.note.response_timeout);
688 Err(NoteError::TimeOut)
689 }
690}
691
692#[cfg(test)]
693mod tests {
694 use super::*;
695 use embedded_hal_mock::eh0::delay::StdSleep;
696 use embedded_hal_mock::eh0::i2c::{Mock, Transaction};
697
698 pub fn new_mock() -> Notecard<Mock> {
699 let i2c = Mock::new(&[]);
701 Notecard::new(i2c)
702 }
703
704 #[test]
705 fn resize_buf() {
706 let c = new_mock();
707 assert_eq!(c.buf.capacity(), DEFAULT_BUF_SIZE);
708
709 let mut c = c.resize_buf::<1024>().unwrap();
710 assert_eq!(c.buf.capacity(), 1024);
711
712 c.i2c.done();
713 }
714
715 #[test]
716 fn raw_request() {
717 let mut expect = b"{\"req\":\"card.location\"}\n".to_vec();
718 expect.insert(0, 24);
719 let exp = [
720 Transaction::write(0x17, vec![0, 0]),
721 Transaction::read(0x17, vec![0, 0]),
722 Transaction::write(0x17, expect),
723 ];
724 let i2c = Mock::new(&exp);
725 let mut c: Notecard<Mock> = Notecard::new(i2c);
726 let mut delay = StdSleep::new();
727 c.request_raw(&mut delay, b"{\"req\":\"card.location\"}\n")
728 .unwrap();
729
730 c.i2c.done();
731 }
732}