From eb5befa55ca0ba46bf8de99393c7acb3eda03e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20K=C3=A4nner?= Date: Tue, 30 Jan 2024 20:15:03 +0100 Subject: [PATCH] A start --- Cargo.toml | 12 +++-- README.md | 4 +- src/lib.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8cfc59a..68e7af4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,13 @@ [package] -name = "lib-rs" +name = "crsf-rs" version = "0.1.0" edition = "2021" -description = "library template" -license_file = "MIT OR Apache-2.0" +description = "This crate provides a #[no_std] parser for the crossfire protocol" +license = "MIT OR Apache-2.0" repository = "https://git.mkaenner.de/max/lib-rs" +keywords = ["crsf", "crossfire"] +categories = ["embedded"] +authors = ["Max Känner "] [lints.rust] unsafe_code = "forbid" @@ -19,3 +22,6 @@ cargo = "warn" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +num_enum = { version = "=0.5", default-features = false } +bitfield = { version = "=0.13" } +heapless = { version = "=0.7" } diff --git a/README.md b/README.md index cfbac2a..97794c6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# lib-rs +# crsf-rs -Template for rust libraries \ No newline at end of file +A library for decoding crossfire packages. This library is fully #[no_std] compatible diff --git a/src/lib.rs b/src/lib.rs index 139597f..56d8576 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,131 @@ +#![cfg_attr(not(test), no_std)] +use bitfield::bitfield; +use heapless::HistoryBuffer; +use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError}; +#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum FrameType { + Gps = 0x02, + Vario = 0x07, + BatterySensor = 0x08, + BaroAltitude = 0x09, + LinkStatistics = 0x14, + OpenTxSync = 0x10, + RadioId = 0x3A, + RcChannelsPacked = 0x16, + Attitude = 0x1E, + FlightMode = 0x21, + // Extended Header Frames + DevicePing = 0x28, + DeviceInfo = 0x29, + ParameterSettingsEntry = 0x2B, + ParameterRead = 0x2C, + ParameterWrite = 0x2D, + ELRSStatus = 0x2E, + Command = 0x32, + // KISS frames + KissReq = 0x78, + KissResp = 0x79, + // MSP commands + MspReq = 0x7A, + MspResp = 0x7B, + MspWrite = 0x7C, + // Ardupilot frames + ArdupilotResp = 0x80, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum Address { + Broadcast = 0x00, + Usb = 0x10, + TbsCorePnpPro = 0x80, + Reserved1 = 0x8A, + CurrentSensor = 0xC0, + Gps = 0xC2, + TbsBlackbox = 0xC4, + FlightController = 0xC8, + Reserved2 = 0xCA, + RaceTag = 0xCC, + RadioTransmitter = 0xEA, + CrsfReceiver = 0xEC, + CrsfTrasmitter = 0xEE, + ElrsLua = 0xEF, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +struct Header { + address: Address, + size: u8, + frame_type: FrameType, +} + +impl Header { + fn try_from_bytes(bytes: [u8; 3]) -> Result { + Ok(Self { + address: bytes[0] + .try_into() + .map_err(|TryFromPrimitiveError { number }| number)?, + size: bytes[1], + frame_type: bytes[2] + .try_into() + .map_err(|TryFromPrimitiveError { number }| number)?, + }) + } +} + +bitfield! { + struct RcChannels([u8]); + impl Debug; + u16; + ch1, set_ch1: Self::upper(1), Self::lower(1); + ch2, set_ch2: Self::upper(2), Self::lower(2); + ch3, set_ch3: Self::upper(3), Self::lower(3); + ch4, set_ch4: Self::upper(4), Self::lower(4); + ch5, set_ch5: Self::upper(5), Self::lower(5); + ch6, set_ch6: Self::upper(6), Self::lower(6); + ch7, set_ch7: Self::upper(7), Self::lower(7); + ch8, set_ch8: Self::upper(8), Self::lower(8); + ch9, set_ch9: Self::upper(9), Self::lower(9); + ch10, set_ch10: Self::upper(10), Self::lower(10); + ch11, set_ch11: Self::upper(11), Self::lower(11); + ch12, set_ch12: Self::upper(12), Self::lower(12); + ch13, set_ch13: Self::upper(13), Self::lower(13); + ch14, set_ch14: Self::upper(14), Self::lower(14); + ch15, set_ch15: Self::upper(15), Self::lower(15); + ch16, set_ch16: Self::upper(16), Self::lower(16); +} + +impl RcChannels { + const BITS_PER_CHANNEL: usize = 11; + + const fn upper(ch: usize) -> usize { + Self::lower(ch) + Self::BITS_PER_CHANNEL - 1 + } + + const fn lower(ch: usize) -> usize { + Self::BITS_PER_CHANNEL * (ch - 1) + } +} + +pub struct CrsfParser { + buffer: HistoryBuffer, +} + +impl CrsfParser { + const MAX_PACKET_SIZE: usize = 64; +} + +#[cfg(test)] +mod tests { + #[test] + fn rc_channels() { + let input = [ + 0xc8, 0x18, 0x16, 0xe0, 0x3, 0x1f, 0xb3, 0xc0, 0xf7, 0xb, 0xee, 0x69, 0x8f, 0x15, 0xe0, + 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0xd9, 0xdb, 0xb4, 0xc8, + ]; + unimplemented!(); + } +}