#![no_std]
use embedded_io::{ErrorType, Read, Write};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error<E> {
Read(E),
Write(E),
InvalidFrame,
UnknownReportType(u8),
InvalidReport,
InvalidAck,
BufferOverflow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TargetState {
NoTarget = 0x00,
Moving = 0x01,
Stationary = 0x02,
Both = 0x03,
}
impl TargetState {
fn from_u8(v: u8) -> Option<Self> {
match v {
0x00 => Some(Self::NoTarget),
0x01 => Some(Self::Moving),
0x02 => Some(Self::Stationary),
0x03 => Some(Self::Both),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PresenceData {
pub target_state: TargetState,
pub moving_distance_cm: u16,
pub moving_energy: u8,
pub still_distance_cm: u16,
pub still_energy: u8,
pub detection_distance_cm: u16,
}
impl PresenceData {
pub fn presence(&self) -> bool {
self.target_state != TargetState::NoTarget
}
}
pub mod cmd {
pub const ENABLE_CONFIG: u16 = 0x00FF;
pub const END_CONFIG: u16 = 0x00FE;
pub const SET_MAX_GATES_AND_DURATION: u16 = 0x0060;
pub const READ_PARAMETERS: u16 = 0x0061;
pub const ENABLE_ENGINEERING: u16 = 0x0062;
pub const DISABLE_ENGINEERING: u16 = 0x0063;
pub const SET_SENSITIVITY: u16 = 0x0064;
pub const READ_FIRMWARE: u16 = 0x00A0;
pub const SET_BAUD_RATE: u16 = 0x00A1;
pub const FACTORY_RESET: u16 = 0x00A2;
pub const RESTART: u16 = 0x00A3;
}
pub mod frame {
pub const REPORT_HEADER: [u8; 4] = [0xF4, 0xF3, 0xF2, 0xF1];
pub const REPORT_FOOTER: [u8; 4] = [0xF8, 0xF7, 0xF6, 0xF5];
pub const CMD_HEADER: [u8; 4] = [0xFD, 0xFC, 0xFB, 0xFA];
pub const CMD_FOOTER: [u8; 4] = [0x04, 0x03, 0x02, 0x01];
pub const INNER_HEAD: u8 = 0xAA;
pub const INNER_TAIL: u8 = 0x55;
pub const INNER_CHECK: u8 = 0x00;
pub const MAX_REPORT_DATA_LEN: usize = 128;
pub const MAX_CMD_DATA_LEN: usize = 128;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ack<'a> {
pub ack_cmd: u16,
pub status: u16,
pub payload: &'a [u8],
}
pub struct LD2410<UART> {
uart: UART,
buf: [u8; 256],
}
impl<UART> LD2410<UART> {
pub fn new(uart: UART) -> Self {
Self {
uart,
buf: [0u8; 256],
}
}
pub fn free(self) -> UART {
self.uart
}
}
impl<UART> LD2410<UART>
where
UART: Read + Write + ErrorType,
{
pub fn read_presence(&mut self) -> Result<PresenceData, Error<UART::Error>> {
loop {
let frame_data = self.read_report_frame_data()?;
if frame_data.len() < 4 {
continue;
}
let r#type = frame_data[0];
if r#type != 0x02 {
continue;
}
return parse_normal_report(&frame_data).map_err(|_| Error::InvalidReport);
}
}
pub fn enter_config_mode(&mut self) -> Result<(), Error<UART::Error>> {
let payload = [0x01, 0x00];
self.send_command(cmd::ENABLE_CONFIG, &payload)?;
let ack = self.read_ack()?;
if ack.status != 0x0000 {
return Err(Error::InvalidAck);
}
Ok(())
}
pub fn exit_config_mode(&mut self) -> Result<(), Error<UART::Error>> {
self.send_command(cmd::END_CONFIG, &[])?;
let ack = self.read_ack()?;
if ack.status != 0x0000 {
return Err(Error::InvalidAck);
}
Ok(())
}
pub fn enable_engineering_mode(&mut self) -> Result<(), Error<UART::Error>> {
self.send_command(cmd::ENABLE_ENGINEERING, &[])?;
let ack = self.read_ack()?;
if ack.status != 0x0000 {
return Err(Error::InvalidAck);
}
Ok(())
}
pub fn disable_engineering_mode(&mut self) -> Result<(), Error<UART::Error>> {
self.send_command(cmd::DISABLE_ENGINEERING, &[])?;
let ack = self.read_ack()?;
if ack.status != 0x0000 {
return Err(Error::InvalidAck);
}
Ok(())
}
pub fn read_firmware_raw(&mut self) -> Result<[u8; 8], Error<UART::Error>> {
self.send_command(cmd::READ_FIRMWARE, &[])?;
let ack = self.read_ack()?;
if ack.status != 0x0000 {
return Err(Error::InvalidAck);
}
if ack.payload.len() < 8 {
return Err(Error::InvalidAck);
}
let mut out = [0u8; 8];
out.copy_from_slice(&ack.payload[..8]);
Ok(out)
}
pub fn restart(&mut self) -> Result<(), Error<UART::Error>> {
self.send_command(cmd::RESTART, &[])?;
let ack = self.read_ack()?;
if ack.status != 0x0000 {
return Err(Error::InvalidAck);
}
Ok(())
}
pub fn send_command(&mut self, cmd_word: u16, payload: &[u8]) -> Result<(), Error<UART::Error>> {
if payload.len() > frame::MAX_CMD_DATA_LEN {
return Err(Error::BufferOverflow);
}
let intra_len = 2usize + payload.len();
let len_le = (intra_len as u16).to_le_bytes();
let cmd_le = cmd_word.to_le_bytes();
self.uart.write_all(&frame::CMD_HEADER).map_err(Error::Write)?;
self.uart.write_all(&len_le).map_err(Error::Write)?;
self.uart.write_all(&cmd_le).map_err(Error::Write)?;
if !payload.is_empty() {
self.uart.write_all(payload).map_err(Error::Write)?;
}
self.uart.write_all(&frame::CMD_FOOTER).map_err(Error::Write)?;
Ok(())
}
pub fn read_ack(&mut self) -> Result<Ack<'_>, Error<UART::Error>> {
let frame_bytes_len = self.read_cmd_frame_into_buf()?;
let buf = &self.buf[..frame_bytes_len];
if frame_bytes_len < 4 + 2 + 2 + 2 + 4 {
return Err(Error::InvalidAck);
}
let intra_len = u16::from_le_bytes([buf[4], buf[5]]) as usize;
let intra_start = 6;
let intra_end = intra_start + intra_len;
if intra_end + 4 != frame_bytes_len {
return Err(Error::InvalidAck);
}
let ack_cmd = u16::from_le_bytes([buf[intra_start], buf[intra_start + 1]]);
if intra_len < 4 {
return Err(Error::InvalidAck);
}
let status = u16::from_le_bytes([buf[intra_start + 2], buf[intra_start + 3]]);
let payload = &buf[(intra_start + 4)..intra_end];
Ok(Ack {
ack_cmd,
status,
payload,
})
}
fn read_report_frame_data(&mut self) -> Result<&[u8], Error<UART::Error>> {
let total_len = self.read_report_frame_into_buf()?;
if total_len < 4 + 2 + 4 {
return Err(Error::InvalidFrame);
}
let data_len = u16::from_le_bytes([self.buf[4], self.buf[5]]) as usize;
let data_start = 6;
let data_end = data_start + data_len;
if data_end + 4 != total_len {
return Err(Error::InvalidFrame);
}
Ok(&self.buf[data_start..data_end])
}
fn read_report_frame_into_buf(&mut self) -> Result<usize, Error<UART::Error>> {
self.scan_for_header(&frame::REPORT_HEADER)?;
self.read_exact_into(4, 2)?;
let data_len = u16::from_le_bytes([self.buf[4], self.buf[5]]) as usize;
if data_len == 0 || data_len > frame::MAX_REPORT_DATA_LEN {
return Err(Error::InvalidFrame);
}
let total_len = 4 + 2 + data_len + 4;
if total_len > self.buf.len() {
return Err(Error::BufferOverflow);
}
self.read_exact_into(6, data_len + 4)?;
let footer_start = 6 + data_len;
if &self.buf[footer_start..footer_start + 4] != &frame::REPORT_FOOTER {
return Err(Error::InvalidFrame);
}
Ok(total_len)
}
fn read_cmd_frame_into_buf(&mut self) -> Result<usize, Error<UART::Error>> {
self.scan_for_header(&frame::CMD_HEADER)?;
self.read_exact_into(4, 2)?;
let intra_len = u16::from_le_bytes([self.buf[4], self.buf[5]]) as usize;
if intra_len == 0 || intra_len > frame::MAX_CMD_DATA_LEN {
return Err(Error::InvalidFrame);
}
let total_len = 4 + 2 + intra_len + 4;
if total_len > self.buf.len() {
return Err(Error::BufferOverflow);
}
self.read_exact_into(6, intra_len + 4)?;
let footer_start = 6 + intra_len;
if &self.buf[footer_start..footer_start + 4] != &frame::CMD_FOOTER {
return Err(Error::InvalidFrame);
}
Ok(total_len)
}
fn scan_for_header(&mut self, header: &[u8; 4]) -> Result<(), Error<UART::Error>> {
let mut b = [0u8; 1];
let mut idx = 0usize;
loop {
match self.uart.read_exact(&mut b) {
Ok(()) => {
let byte = b[0];
if byte == header[idx] {
self.buf[idx] = byte;
idx += 1;
if idx == 4 {
return Ok(());
}
} else {
if byte == header[0] {
self.buf[0] = byte;
idx = 1;
} else {
idx = 0;
}
}
}
Err(embedded_io::ReadExactError::UnexpectedEof) => return Err(Error::InvalidFrame),
Err(embedded_io::ReadExactError::Other(e)) => return Err(Error::Read(e)),
}
}
}
fn read_exact_into(&mut self, offset: usize, len: usize) -> Result<(), Error<UART::Error>> {
let end = offset + len;
if end > self.buf.len() {
return Err(Error::BufferOverflow);
}
match self.uart.read_exact(&mut self.buf[offset..end]) {
Ok(()) => Ok(()),
Err(embedded_io::ReadExactError::UnexpectedEof) => Err(Error::InvalidFrame),
Err(embedded_io::ReadExactError::Other(e)) => Err(Error::Read(e)),
}
}
}
fn parse_normal_report(frame_data: &[u8]) -> Result<PresenceData, ()> {
if frame_data.len() < 6 {
return Err(());
}
let r#type = frame_data[0];
if r#type != 0x02 {
return Err(());
}
if frame_data[1] != frame::INNER_HEAD {
return Err(());
}
if frame_data[frame_data.len() - 2] != frame::INNER_TAIL {
return Err(());
}
if frame_data[frame_data.len() - 1] != frame::INNER_CHECK {
return Err(());
}
let payload = &frame_data[2..frame_data.len() - 2];
if payload.len() < 1 + 2 + 1 + 1 + 1 + 1 {
return Err(());
}
let target_state = TargetState::from_u8(payload[0]).ok_or(())?;
let moving_distance_cm = u16::from_le_bytes([payload[1], payload[2]]);
let moving_energy = payload[3];
let mut idx = 4;
if idx >= payload.len() {
return Err(());
}
let still_distance_cm = payload[idx] as u16;
idx += 1;
if idx >= payload.len() {
return Err(());
}
let still_energy = payload[idx];
idx += 1;
if idx >= payload.len() {
return Err(());
}
let detection_distance_cm = if (idx + 1) < payload.len() && payload[idx + 1] == 0x00 {
let d = u16::from_le_bytes([payload[idx], payload[idx + 1]]);
d
} else {
payload[idx] as u16
};
Ok(PresenceData {
target_state,
moving_distance_cm,
moving_energy,
still_distance_cm,
still_energy,
detection_distance_cm,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_normal_report_example() {
let frame_data: [u8; 13] = [
0x02, 0xAA, 0x02, 0x51, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x55, 0x00,
];
let p = super::parse_normal_report(&frame_data).unwrap();
assert_eq!(p.target_state, TargetState::Stationary);
assert_eq!(p.moving_distance_cm, 0x0051);
assert_eq!(p.moving_energy, 0x00);
assert_eq!(p.still_distance_cm, 0x00);
assert_eq!(p.still_energy, 0x00);
assert_eq!(p.detection_distance_cm, 0x003B);
assert!(p.presence());
}
}