#![deny(unsafe_code)]
#![deny(missing_docs)]
#![no_std]
use arrayvec::ArrayVec;
use core::convert::From;
use ieee754::*;
use nb::Error as nbError;
use sensirion_hdlc::{decode, encode, HDLCError, SpecialChars};
const MAX_BUFFER: usize = 600;
#[derive(Debug)]
pub enum Error<E, F> {
SerialR(nb::Error<F>),
SerialW(E),
SHDLC(HDLCError),
InvalidFrame,
EmptyResult,
ChecksumFailed,
InvalidRespose,
StatusError,
}
impl<E, F> From<nbError<F>> for Error<E, F> {
fn from(f: nbError<F>) -> Self {
Error::SerialR(f)
}
}
#[repr(u8)]
pub enum DeviceInfo {
ProductName = 1,
ArticleCode = 2,
SerialNumber = 3,
}
#[repr(u8)]
pub enum CommandType {
StartMeasurement = 0,
StopMeasurement = 1,
ReadMeasuredData = 3,
ReadWriteAutoCleaningInterval = 0x80,
StartFanCleaning = 0x56,
DeviceInformation = 0xD0,
Reset = 0xD3,
}
fn compute_cksum(data: &[u8]) -> u8 {
let mut cksum: u8 = 0;
for &byte in data.iter() {
let val: u16 = cksum as u16 + byte as u16;
let lsb = val % 256;
cksum = lsb as u8;
}
255 - cksum
}
#[derive(Debug, Default)]
pub struct Sps30<SERIAL> {
serial: SERIAL,
}
impl<SERIAL, E, F> Sps30<SERIAL>
where
SERIAL: embedded_hal::blocking::serial::Write<u8, Error = E>
+ embedded_hal::serial::Read<u8, Error = F>,
{
pub fn new(serial: SERIAL) -> Self {
Sps30 { serial }
}
fn send_uart_data(&mut self, data: &[u8]) -> Result<(), Error<E, F>> {
let s_chars = SpecialChars::default();
let output = encode(&data, s_chars).unwrap();
self.serial.bwrite_all(&output).map_err(Error::SerialW)
}
fn read_uart_data(&mut self) -> Result<ArrayVec<[u8; 1024]>, Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let mut seen = 0;
while seen != 2 {
let byte = self.serial.read();
match byte {
Ok(value) => {
if value == 0x7e {
seen += 1;
}
output.push(value);
}
Err(e) => {
return Err(Error::from(e));
}
}
if output.len() > MAX_BUFFER {
return Err(Error::InvalidFrame);
}
}
match decode(&output, SpecialChars::default()) {
Ok(v) => {
if v[v.len() - 1] == compute_cksum(&v[..v.len() - 1]) {
return Ok(v);
}
Err(Error::ChecksumFailed)
}
Err(e) => Err(Error::SHDLC(e)),
}
}
fn check_miso_frame<'a>(
&self,
data: &'a [u8],
cmd_type: CommandType,
) -> Result<&'a [u8], Error<E, F>> {
if data.len() < 5 {
return Err(Error::InvalidRespose);
}
if data[1] != cmd_type as u8 {
return Err(Error::InvalidRespose);
}
if data[2] != 0 {
return Err(Error::StatusError);
}
if data[3] as usize != data.len() - 5 {
return Err(Error::InvalidRespose);
}
Ok(data)
}
pub fn start_measurement(&mut self) -> Result<(), Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0x00, 0x02, 0x01, 0x03];
for item in &cmd {
output.push(*item);
}
output.push(compute_cksum(&output));
self.send_uart_data(&output)?;
match self.read_uart_data() {
Ok(response) => self
.check_miso_frame(&response, CommandType::StartMeasurement)
.map(|_| ()),
Err(e) => Err(e),
}
}
pub fn stop_measurement(&mut self) -> Result<(), Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0x01, 0x00];
for item in &cmd {
output.push(*item);
}
output.push(compute_cksum(&output));
self.send_uart_data(&output)?;
match self.read_uart_data() {
Ok(response) => self
.check_miso_frame(&response, CommandType::StopMeasurement)
.map(|_| ()),
Err(e) => Err(e),
}
}
pub fn read_measurement(&mut self) -> Result<[f32; 10], Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0x03, 0x00];
for item in &cmd {
output.push(*item);
}
output.push(compute_cksum(&cmd));
self.send_uart_data(&output)?;
let data = self.read_uart_data();
let mut res: [f32; 10] = [0.0; 10];
match data {
Ok(v) => match v.len() {
45 => {
self.check_miso_frame(&v, CommandType::ReadMeasuredData)?;
for i in 0..res.len() {
let mut bits: u32 = 0;
for &byte in v[4 + 4 * i..4 + 4 * (i + 1)].iter() {
bits = (bits << 8) + byte as u32;
}
res[i] = Ieee754::from_bits(bits);
}
Ok(res)
}
5 => Err(Error::EmptyResult),
_ => Err(Error::InvalidFrame),
},
Err(e) => Err(e),
}
}
pub fn read_cleaning_interval(&mut self) -> Result<u32, Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0x80, 0x01, 0x00];
for item in &cmd {
output.push(*item);
}
output.push(compute_cksum(&output));
self.send_uart_data(&output)?;
match self.read_uart_data() {
Ok(response) => {
match self.check_miso_frame(&response, CommandType::ReadWriteAutoCleaningInterval) {
Ok(v) => {
if v[3] != 4 {
return Err(Error::InvalidRespose);
}
let mut ret: u32 = 0;
for &byte in v[4..8].iter() {
ret = ret * 256 + byte as u32;
}
Ok(ret)
}
Err(e) => Err(e),
}
}
Err(e) => Err(e),
}
}
pub fn write_cleaning_interval(&mut self, val: u32) -> Result<(), Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0x80, 0x05, 0x00];
for item in &cmd {
output.push(*item);
}
for item in &val.to_be_bytes() {
output.push(*item);
}
output.push(compute_cksum(&output));
self.send_uart_data(&output)?;
match self.read_uart_data() {
Ok(response) => {
match self.check_miso_frame(&response, CommandType::ReadWriteAutoCleaningInterval) {
Ok(v) => {
if v[3] != 0 {
return Err(Error::InvalidRespose);
}
Ok(())
}
Err(e) => Err(e),
}
}
Err(e) => Err(e),
}
}
pub fn start_fan_cleaning(&mut self) -> Result<(), Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0x56, 0x00];
for item in &cmd {
output.push(*item);
}
output.push(compute_cksum(&output));
self.send_uart_data(&output)?;
match self.read_uart_data() {
Ok(response) => self
.check_miso_frame(&response, CommandType::StartFanCleaning)
.map(|_| ()),
Err(e) => Err(e),
}
}
pub fn device_info(&mut self, info: DeviceInfo) -> Result<[u8; 32], Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0xD0, 0x01];
for item in &cmd {
output.push(*item);
}
output.push(info as u8);
output.push(compute_cksum(&output));
self.send_uart_data(&output)?;
match self.read_uart_data() {
Ok(response) => {
match self.check_miso_frame(&response, CommandType::DeviceInformation) {
Ok(val) => {
let mut ret: [u8; 32] = [0; 32];
if val[3] < 33 {
for i in 0..val[3] {
ret[i as usize] = val[3 + i as usize];
}
return Ok(ret);
}
Err(Error::EmptyResult)
}
Err(e) => Err(e),
}
}
Err(e) => Err(e),
}
}
pub fn reset(&mut self) -> Result<(), Error<E, F>> {
let mut output = ArrayVec::<[u8; 1024]>::new();
let cmd = [0x00, 0xD3, 0x00];
for item in &cmd {
output.push(*item);
}
output.push(compute_cksum(&output));
self.send_uart_data(&output)?;
match self.read_uart_data() {
Ok(response) => self
.check_miso_frame(&response, CommandType::Reset)
.map(|_| ()),
Err(e) => Err(e),
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}