#![feature(split_array)]
#![cfg_attr(not(test), no_std)]
use core::marker::PhantomData;
#[allow(unused_imports)]
use defmt::{debug, error, info, trace, warn};
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::blocking::i2c::{Read, SevenBitAddress, Write};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub mod card;
pub mod hub;
pub mod note;
const RESPONSE_TIMEOUT: u16 = 5000;
const RESPONSE_DELAY: u16 = 25;
pub const DEFAULT_BUF_SIZE: usize = 18 * 1024;
#[derive(Debug, defmt::Format)]
pub enum NoteState {
Handshake,
Request,
Poll(usize),
Response(usize),
ResponseReady,
}
#[derive(Debug, defmt::Format, Clone)]
pub enum NoteError {
I2cWriteError,
I2cReadError,
DeserError,
SerError,
InvalidRequest,
RemainingData,
TimeOut,
BufOverflow,
WrongState,
NotecardErr(heapless::String<256>),
}
#[derive(Deserialize, defmt::Format)]
pub struct NotecardError {
err: heapless::String<256>,
}
impl From<NotecardError> for NoteError {
fn from(n: NotecardError) -> NoteError {
NoteError::NotecardErr(n.err)
}
}
pub struct Notecard<
IOM: Write<SevenBitAddress> + Read<SevenBitAddress>,
const BUF_SIZE: usize = DEFAULT_BUF_SIZE,
> {
i2c: IOM,
addr: u8,
state: NoteState,
buf: heapless::Vec<u8, BUF_SIZE>,
}
pub struct SuspendState<const BUF_SIZE: usize> {
addr: u8,
state: NoteState,
buf: heapless::Vec<u8, BUF_SIZE>,
}
impl<IOM: Write<SevenBitAddress> + Read<SevenBitAddress>, const BUF_SIZE: usize>
Notecard<IOM, BUF_SIZE>
{
pub fn new(i2c: IOM) -> Notecard<IOM, BUF_SIZE> {
Notecard {
i2c,
addr: 0x17,
state: NoteState::Handshake,
buf: heapless::Vec::new(),
}
}
pub fn resize_buf<const B: usize>(self) -> Result<Notecard<IOM, B>, NoteError> {
if B < self.buf.len() {
return Err(NoteError::BufOverflow);
} else {
let (buf, _) = self.buf.split_array_ref::<B>();
Ok(Notecard {
i2c: self.i2c,
addr: self.addr,
state: self.state,
buf: heapless::Vec::<_, B>::from_slice(buf).unwrap(),
})
}
}
pub fn suspend(self) -> (IOM, SuspendState<BUF_SIZE>) {
(
self.i2c,
SuspendState {
state: self.state,
buf: self.buf,
addr: self.addr,
},
)
}
pub fn resume(i2c: IOM, state: SuspendState<BUF_SIZE>) -> Notecard<IOM, BUF_SIZE> {
Notecard {
i2c,
addr: state.addr,
state: state.state,
buf: state.buf,
}
}
pub fn initialize(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
info!("note: initializing.");
self.reset(delay)
}
pub fn ping(&mut self) -> bool {
self.i2c.write(self.addr, &[]).is_ok()
}
pub fn data_query(&mut self) -> Result<usize, NoteError> {
trace!("note: data_query: {:?}", self.state);
if !matches!(self.state, NoteState::Response(_)) {
self.i2c
.write(self.addr, &[0, 0])
.map_err(|_| NoteError::I2cWriteError)?;
let mut buf = [0u8; 2];
self.i2c
.read(self.addr, &mut buf)
.map_err(|_| NoteError::I2cReadError)?;
let available = buf[0] as usize;
let sent = buf[1] as usize;
if available > 0 {
self.buf.clear();
self.state = NoteState::Response(available);
}
trace!("avail = {}, sent = {}", available, sent);
if sent > 0 {
error!(
"data query: bytes sent when querying available bytes: {}",
sent
);
Err(NoteError::RemainingData)
} else {
Ok(available)
}
} else {
error!("note: data_query called while reading response.");
Err(NoteError::WrongState)
}
}
fn read(&mut self) -> Result<usize, NoteError> {
if let NoteState::Response(avail) = self.state {
let mut bytes = heapless::Vec::<u8, 128>::new();
let sz = (bytes.capacity() - 2).min(avail);
bytes.resize(sz + 2, 0).unwrap();
debug!("asking to read: {} of available {} bytes", sz, avail);
self.i2c
.write(self.addr, &[0, sz as u8])
.map_err(|_| NoteError::I2cWriteError)?;
self.i2c
.read(self.addr, &mut bytes)
.map_err(|_| NoteError::I2cReadError)?;
let available = bytes[0] as usize;
let sent = bytes[1] as usize;
self.buf.extend_from_slice(&bytes[2..]).unwrap();
trace!("read: {}", unsafe {
core::str::from_utf8_unchecked(&bytes)
});
trace!("avail = {}, sent = {}", available, sent);
if available > 0 {
self.state = NoteState::Response(available);
} else {
self.state = NoteState::ResponseReady;
}
Ok(available)
} else {
error!("read: called when not waiting for response");
Err(NoteError::WrongState)
}
}
fn take_response(&mut self) -> Result<&[u8], NoteError> {
if matches!(self.state, NoteState::ResponseReady) {
self.state = NoteState::Request;
Ok(&self.buf)
} else {
error!("take response called when response not ready");
Err(NoteError::WrongState)
}
}
fn poll(&mut self) -> Result<Option<&[u8]>, NoteError> {
trace!("note: poll: {:?}", self.state);
match self.state {
NoteState::Poll(_) => {
let sz = self.data_query()?;
if sz > 0 {
debug!("response ready: {} bytes..", sz);
self.poll()
} else {
Ok(None)
}
}
NoteState::Response(_) => {
let avail = self.read()?;
if avail == 0 {
self.poll()
} else {
Ok(None)
}
}
NoteState::ResponseReady => {
debug!("response read, deserializing.");
Ok(Some(self.take_response()?))
}
_ => {
error!("poll called when not receiving response");
Err(NoteError::WrongState)
}
}
}
unsafe fn consume_response(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
warn!("note: trying to consume any left-over response.");
let mut waited = 0;
while waited < RESPONSE_TIMEOUT {
if matches!(self.poll()?, Some(_)) {
self.buf.clear();
return Ok(());
}
delay.delay_ms(RESPONSE_DELAY);
waited += RESPONSE_DELAY;
}
self.buf.clear();
error!("response timed out (>= {}).", RESPONSE_TIMEOUT);
Err(NoteError::TimeOut)
}
pub fn reset(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
warn!("resetting: consuming any left-over response and perform a new handshake.");
self.state = NoteState::Handshake;
self.handshake(delay)
}
fn handshake(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
if matches!(self.state, NoteState::Handshake) {
debug!("note: handshake");
if self.data_query()? > 0 {
error!("note: handshake: remaining data in queue, consuming..");
unsafe { self.consume_response(delay)? };
}
self.state = NoteState::Request;
}
Ok(())
}
pub(crate) fn request_raw(
&mut self,
delay: &mut impl DelayMs<u16>,
cmd: &[u8],
) -> Result<(), NoteError> {
self.buf
.resize(cmd.len(), 0)
.map_err(|_| NoteError::SerError)?;
let buf: &mut [u8] = self.buf.as_mut();
buf.copy_from_slice(&cmd);
self.send_request(delay)
}
fn send_request(&mut self, delay: &mut impl DelayMs<u16>) -> Result<(), NoteError> {
const CHUNK_LENGTH_MAX: usize = 127;
const CHUNK_LENGTH_I: usize = 30;
const CHUNK_LENGTH: usize = if CHUNK_LENGTH_I < CHUNK_LENGTH_MAX {
CHUNK_LENGTH_I
} else {
CHUNK_LENGTH_MAX
};
const SEGMENT_LENGTH: usize = (250 / CHUNK_LENGTH) * CHUNK_LENGTH;
const CHUNK_DELAY: u16 = 20; const SEGMENT_DELAY: u16 = 100;
if !matches!(self.state, NoteState::Request) {
warn!("note: request: wrong-state, resetting before new request.");
self.reset(delay)?;
}
match self.buf.last() {
Some(c) if *c == b'\n' => Ok(()),
_ => Err(NoteError::InvalidRequest),
}?;
trace!("note: making request: {}", unsafe {
core::str::from_utf8_unchecked(&self.buf)
});
let mut buf = heapless::Vec::<u8, { CHUNK_LENGTH + 1 }>::new();
for segment in self.buf.chunks(SEGMENT_LENGTH) {
for c in segment.chunks(buf.capacity() - 1) {
buf.push(c.len() as u8).unwrap();
buf.extend_from_slice(c).unwrap();
trace!("note: sending chunk: {} => {}", &buf, unsafe {
core::str::from_utf8_unchecked(&buf)
});
self.i2c
.write(self.addr, &buf)
.map_err(|_| NoteError::I2cWriteError)?;
buf.clear();
delay.delay_ms(CHUNK_DELAY);
}
delay.delay_ms(SEGMENT_DELAY);
}
self.state = NoteState::Poll(0);
Ok(())
}
pub(crate) fn request<T: Serialize>(
&mut self,
delay: &mut impl DelayMs<u16>,
cmd: T,
) -> Result<(), NoteError> {
self.buf.clear();
self.buf.resize(self.buf.capacity(), 0).unwrap();
let sz = serde_json_core::to_slice(&cmd, &mut self.buf).map_err(|_| NoteError::SerError)?;
self.buf.truncate(sz);
self.buf.push(b'\n').map_err(|_| NoteError::SerError)?;
self.send_request(delay)
}
pub fn card(&mut self) -> card::Card<IOM, BUF_SIZE> {
card::Card::from(self)
}
pub fn note(&mut self) -> note::Note<IOM, BUF_SIZE> {
note::Note::from(self)
}
pub fn hub(&mut self) -> hub::Hub<IOM, BUF_SIZE> {
hub::Hub::from(self)
}
}
#[must_use = "The response must be waited for and consumed, otherwise the notecard is left in an inconsistent state"]
pub struct FutureResponse<
'a,
T: DeserializeOwned,
IOM: Write<SevenBitAddress> + Read<SevenBitAddress>,
const BUF_SIZE: usize,
> {
note: &'a mut Notecard<IOM, BUF_SIZE>,
_r: PhantomData<T>,
}
impl<
'a,
T: DeserializeOwned,
IOM: Write<SevenBitAddress> + Read<SevenBitAddress>,
const BUF_SIZE: usize,
> FutureResponse<'a, T, IOM, BUF_SIZE>
{
fn from(note: &'a mut Notecard<IOM, BUF_SIZE>) -> FutureResponse<'a, T, IOM, BUF_SIZE> {
FutureResponse {
note,
_r: PhantomData,
}
}
pub fn poll(&mut self) -> Result<Option<T>, NoteError> {
match self.note.poll()? {
Some(body) if body.starts_with(br##"{"err":"##) => {
debug!(
"response is error response, parsing error..: {}",
core::str::from_utf8(&body).unwrap_or("[invalid utf-8]")
);
Err(serde_json_core::from_slice::<NotecardError>(body)
.map_err(|_| {
error!(
"failed to deserialize: {}",
core::str::from_utf8(&body).unwrap_or("[invalid utf-8]")
);
NoteError::DeserError
})?
.0
.into())
}
Some(body) => {
trace!("response is regular, parsing..");
Ok(Some(
serde_json_core::from_slice::<T>(body)
.map_err(|_| {
error!(
"failed to deserialize: {}",
core::str::from_utf8(&body).unwrap_or("[invalid utf-8]")
);
NoteError::DeserError
})?
.0,
))
}
None => Ok(None),
}
}
pub fn wait_raw(mut self, delay: &mut impl DelayMs<u16>) -> Result<&'a [u8], NoteError> {
let mut waited = 0;
while waited < RESPONSE_TIMEOUT {
match self.poll()? {
Some(_) => return Ok(self.note.take_response()?),
None => (),
}
delay.delay_ms(RESPONSE_DELAY);
waited += RESPONSE_DELAY;
}
error!("response timed out (>= {}).", RESPONSE_TIMEOUT);
Err(NoteError::TimeOut)
}
pub fn wait(mut self, delay: &mut impl DelayMs<u16>) -> Result<T, NoteError> {
let mut waited = 0;
while waited < RESPONSE_TIMEOUT {
match self.poll()? {
Some(r) => return Ok(r),
None => (),
}
delay.delay_ms(RESPONSE_DELAY);
waited += RESPONSE_DELAY;
}
error!("response timed out (>= {}).", RESPONSE_TIMEOUT);
Err(NoteError::TimeOut)
}
}
#[cfg(test)]
mod tests {}