This commit is contained in:
Robin Appelman 2024-02-18 11:19:26 +01:00
commit 0a6f3270d8
5 changed files with 547 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/target
.env
result
.direnv

355
Cargo.lock generated Normal file
View file

@ -0,0 +1,355 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "CoreFoundation-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
dependencies = [
"libc",
"mach",
]
[[package]]
name = "IOKit-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
dependencies = [
"CoreFoundation-sys",
"libc",
"mach",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "array-init"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
[[package]]
name = "binrw"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "173901312e9850391d4d7c1318c4e099fdc037d61870fca427429830efdb4e5f"
dependencies = [
"array-init",
"binrw_derive",
"bytemuck",
]
[[package]]
name = "binrw_derive"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb515fdd6f8d3a357c8e19b8ec59ef53880807864329b1cb1cba5c53bf76557e"
dependencies = [
"either",
"owo-colors",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "hlk_ld6002"
version = "0.1.0"
dependencies = [
"binrw",
"bytemuck",
"serialport",
"termion",
]
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "libredox"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
dependencies = [
"bitflags 2.0.2",
"libc",
"redox_syscall",
]
[[package]]
name = "libudev"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
dependencies = [
"libc",
"libudev-sys",
]
[[package]]
name = "libudev-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
dependencies = [
"libc",
"pkg-config",
]
[[package]]
name = "mach"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
dependencies = [
"libc",
]
[[package]]
name = "mach2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "pkg-config"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_termios"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serialport"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c32634e2bd4311420caa504404a55fad2131292c485c97014cbed89a5899885f"
dependencies = [
"CoreFoundation-sys",
"IOKit-sys",
"bitflags 2.0.2",
"cfg-if",
"libudev",
"mach2",
"nix",
"regex",
"scopeguard",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termion"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4648c7def6f2043b2568617b9f9b75eae88ca185dbc1f1fda30e95a85d49d7d"
dependencies = [
"libc",
"libredox",
"numtoa",
"redox_termios",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

14
Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "hlk_ld6002"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bytemuck = { version = "1.14.0", features = ["derive"] }
binrw = { version = "0.13.3" }
[dev-dependencies]
serialport = "4.2.2"
termion = "2.0.3"

31
examples/test.rs Normal file
View file

@ -0,0 +1,31 @@
use std::env::args;
use std::time::{Duration, Instant};
use serialport::ClearBuffer;
use hlk_ld6002::{Data, MessageStream};
fn main() {
let port = args().nth(1).expect("no port provided");
let port = serialport::new(&port, 1_382_400)
.timeout(Duration::from_millis(50))
.open().expect("Failed to open port");
port.clear(ClearBuffer::All).expect("clear");
let mut messages = MessageStream::new(port);
let mut data = Data::default();
let mut last = Instant::now();
print!("{}", termion::cursor::Save);
loop {
if let Some(message) = messages.next() {
data.update(message);
if last.elapsed() > Duration::from_millis(100) {
last = Instant::now();
print!("{:?}{}", data, termion::cursor::Restore);
}
}
}
}

143
src/lib.rs Normal file
View file

@ -0,0 +1,143 @@
use std::io::{Read};
use binrw::io::NoSeek;
use binrw::prelude::*;
use bytemuck::{cast, cast_slice};
#[derive(Debug)]
pub enum Error {
InvalidDataLength { expected: u16, got: u16, ty: MessageType },
}
#[derive(Debug, Clone, BinRead, Copy)]
#[repr(u16)]
#[brw(big, repr(u16))]
pub enum MessageType {
Phase = 0x0a13,
Respiratory = 0x0a14,
Heartbeat = 0x0a15,
Distance = 0x0a16,
}
impl MessageType {
fn expected_length(self) -> u16 {
match self {
MessageType::Phase => 12,
MessageType::Respiratory => 4,
MessageType::Heartbeat => 4,
MessageType::Distance => 8,
}
}
}
#[derive(Clone, Debug, BinRead)]
#[brw(big)]
#[allow(dead_code)]
struct Frame {
id: u16,
length: u16,
ty: MessageType,
header_checksum: u8,
#[br(count = length)]
data: Vec<u8>,
data_checksum: u8,
}
impl Frame {
fn body(&self) -> Result<MessageBody, Error> {
let numbers = cast_slice::<_, u32>(&self.data);
match (self.ty, self.data.len()) {
(MessageType::Phase, 12) => {
let numbers: [u32; 3] = numbers.try_into().unwrap();
Ok(MessageBody::Phase(cast(numbers)))
}
(MessageType::Respiratory, 4) => {
Ok(MessageBody::Respiratory(f32::from_bits(numbers[0])))
}
(MessageType::Heartbeat, 4) => {
Ok(MessageBody::Heartbeat(f32::from_bits(numbers[0])))
}
(MessageType::Distance, 8) => {
let distance = if numbers[0] == 1 { f32::from_bits(numbers[1]) } else { 0.0 };
Ok(MessageBody::Distance(Some(distance)))
}
(MessageType::Distance, 4) => {
Ok(MessageBody::Distance(None))
}
_ => {
Err(Error::InvalidDataLength {
got: self.data.len() as u16,
expected: self.ty.expected_length(),
ty: self.ty,
})
}
}
}
}
#[derive(Clone, Debug)]
pub enum MessageBody {
Phase([f32; 3]),
Respiratory(f32),
Heartbeat(f32),
Distance(Option<f32>),
}
pub struct MessageStream<R> {
reader: NoSeek<R>,
}
impl<R: Read> MessageStream<R> {
pub fn new(reader: R) -> Self {
Self{
reader:NoSeek::new(reader)
}
}
fn read(&mut self) -> Result<Frame, binrw::Error> {
let mut byte = [0];
loop {
self.reader.read(&mut byte).ok();
if byte[0] == 0x01 {
return Frame::read(&mut self.reader)
}
}
}
}
impl<R: Read> Iterator for MessageStream<R> {
type Item = MessageBody;
fn next(&mut self) -> Option<Self::Item> {
let frame = self.read().ok()?;
frame.body().ok()
}
}
#[derive(Default, Debug, Copy, Clone)]
pub struct Data {
respiratory: f32,
distance: f32,
heartbeat: f32,
}
impl Data {
pub fn update(&mut self, message: MessageBody) {
match message {
MessageBody::Respiratory(rate) => {
self.respiratory = rate;
}
MessageBody::Distance(Some(distance)) if distance > 0.0 => {
self.distance = distance;
}
MessageBody::Heartbeat(rate) => {
self.heartbeat = rate;
}
_ => {}
}
}
}