Skip to main content

cu_crsf/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4
5#[cfg(all(feature = "embedded-io-06", feature = "embedded-io-07"))]
6compile_error!("Enable only one of `embedded-io-06` or `embedded-io-07` for cu-crsf.");
7
8#[cfg(not(any(feature = "embedded-io-06", feature = "embedded-io-07")))]
9compile_error!("Enable one of `embedded-io-06` or `embedded-io-07` for cu-crsf.");
10
11#[cfg(all(feature = "std", feature = "embedded-io-07"))]
12compile_error!(
13    "The `std` feature requires `embedded-io-06`. \
14     Use `embedded-io-07` only for no_std embedded targets."
15);
16
17#[cfg(feature = "embedded-io-06")]
18use embedded_io_06 as embedded_io;
19#[cfg(feature = "embedded-io-07")]
20use embedded_io_07 as embedded_io;
21
22pub mod messages;
23
24use crate::messages::{LinkStatisticsPayload, RcChannelsPayload};
25use crsf::{LinkStatistics, Packet, PacketAddress, PacketParser, RcChannels};
26use cu29::cubridge::{
27    BridgeChannel, BridgeChannelConfig, BridgeChannelInfo, BridgeChannelSet, CuBridge,
28};
29use cu29::prelude::*;
30use cu29::resources;
31use embedded_io::{Read, Write};
32
33const READ_BUFFER_SIZE: usize = 1024;
34const PARSER_BUFFER_SIZE: usize = 1024;
35
36rx_channels! {
37    lq_rx => LinkStatisticsPayload,
38    rc_rx => RcChannelsPayload
39    // TODO(gbin): add other types
40}
41
42tx_channels! {
43    lq_tx => LinkStatisticsPayload,
44    rc_tx => RcChannelsPayload
45}
46
47resources!(for<S, E> where S: Write<Error = E> + Read<Error = E> + Send + Sync + 'static {
48    serial => Owned<S>,
49});
50
51/// Crossfire bridge for Copper-rs.
52#[derive(Reflect)]
53#[reflect(from_reflect = false, no_field_bounds, type_path = false)]
54pub struct CrsfBridge<S, E>
55where
56    S: Write<Error = E> + Read<Error = E> + Send + Sync + 'static,
57    E: 'static,
58{
59    #[reflect(ignore)]
60    serial_port: S,
61    #[reflect(ignore)]
62    parser: PacketParser<PARSER_BUFFER_SIZE>,
63    #[reflect(ignore)]
64    last_lq: Option<LinkStatistics>,
65    #[reflect(ignore)]
66    last_rc: Option<RcChannels>,
67}
68
69impl<S, E> CrsfBridge<S, E>
70where
71    S: Write<Error = E> + Read<Error = E> + Send + Sync + 'static,
72    E: 'static,
73{
74    fn from_serial(serial_port: S) -> Self {
75        Self {
76            serial_port,
77            parser: PacketParser::<PARSER_BUFFER_SIZE>::new(),
78            last_lq: None,
79            last_rc: None,
80        }
81    }
82
83    fn write_packet(&mut self, packet: Packet) -> CuResult<()> {
84        let raw_packet = packet.into_raw(PacketAddress::Transmitter);
85        self.serial_port
86            .write_all(raw_packet.data())
87            .map_err(|_| CuError::from("CRSF: Serial port write error"))
88    }
89
90    // decode from the serial buffer and update to the last received values
91    fn update(&mut self) -> CuResult<()> {
92        let mut buf = [0; READ_BUFFER_SIZE];
93        match self.serial_port.read(buf.as_mut_slice()) {
94            Ok(n) => {
95                if n > 0 {
96                    self.parser.push_bytes(&buf[..n]);
97                    while let Some(Ok((_, packet))) = self.parser.next_packet() {
98                        match packet {
99                            Packet::LinkStatistics(link_statistics) => {
100                                debug!(
101                                    "LinkStatistics: Download LQ:{}",
102                                    link_statistics.downlink_link_quality
103                                );
104                                self.last_lq = Some(link_statistics);
105                            }
106                            Packet::RcChannels(channels) => {
107                                self.last_rc = Some(channels);
108                                for (i, value) in self.last_rc.iter().enumerate() {
109                                    debug!("RC Channel {}: {}", i, value.as_ref());
110                                }
111                            }
112                            _ => {
113                                info!("CRSF: Received other packet");
114                            }
115                        }
116                    }
117                }
118            }
119            _ => {
120                error!("CRSF: Serial port read error");
121            }
122        }
123        Ok(())
124    }
125}
126
127impl<S, E> cu29::reflect::TypePath for CrsfBridge<S, E>
128where
129    S: Write<Error = E> + Read<Error = E> + Send + Sync + 'static,
130    E: 'static,
131{
132    fn type_path() -> &'static str {
133        "cu_crsf::CrsfBridge"
134    }
135
136    fn short_type_path() -> &'static str {
137        "CrsfBridge"
138    }
139
140    fn type_ident() -> Option<&'static str> {
141        Some("CrsfBridge")
142    }
143
144    fn crate_name() -> Option<&'static str> {
145        Some("cu_crsf")
146    }
147
148    fn module_path() -> Option<&'static str> {
149        Some("cu_crsf")
150    }
151}
152
153impl<S, E> Freezable for CrsfBridge<S, E>
154where
155    S: Write<Error = E> + Read<Error = E> + Send + Sync + 'static,
156    E: 'static,
157{
158}
159
160impl<S, E> CuBridge for CrsfBridge<S, E>
161where
162    S: Write<Error = E> + Read<Error = E> + Send + Sync + 'static,
163    E: 'static,
164{
165    type Tx = TxChannels;
166    type Rx = RxChannels;
167    type Resources<'r> = Resources<S, E>;
168
169    fn new(
170        config: Option<&ComponentConfig>,
171        tx_channels: &[BridgeChannelConfig<<Self::Tx as BridgeChannelSet>::Id>],
172        rx_channels: &[BridgeChannelConfig<<Self::Rx as BridgeChannelSet>::Id>],
173        resources: Self::Resources<'_>,
174    ) -> CuResult<Self>
175    where
176        Self: Sized,
177    {
178        let _ = tx_channels;
179        let _ = rx_channels;
180
181        let _ = config;
182
183        Ok(Self::from_serial(resources.serial.0))
184    }
185
186    fn send<'a, Payload>(
187        &mut self,
188        _clock: &RobotClock,
189        channel: &'static BridgeChannel<<Self::Tx as BridgeChannelSet>::Id, Payload>,
190        msg: &CuMsg<Payload>,
191    ) -> CuResult<()>
192    where
193        Payload: CuMsgPayload + 'a,
194    {
195        match channel.id() {
196            TxId::LqTx => {
197                let lsi: &CuMsg<LinkStatisticsPayload> = msg.downcast_ref()?;
198                if let Some(lq) = lsi.payload() {
199                    debug!(
200                        "CRSF: Sent LinkStatistics: Downlink LQ:{}",
201                        lq.0.downlink_link_quality
202                    );
203                    self.write_packet(Packet::LinkStatistics(lq.0.clone()))?;
204                }
205            }
206            TxId::RcTx => {
207                let rccs: &CuMsg<RcChannelsPayload> = msg.downcast_ref()?;
208                if let Some(rc) = rccs.payload() {
209                    for (i, value) in rc.0.iter().enumerate() {
210                        debug!("Sending RC Channel {}: {}", i, value);
211                    }
212                    self.write_packet(Packet::RcChannels(rc.0.clone()))?;
213                }
214            }
215        }
216        Ok(())
217    }
218
219    fn receive<'a, Payload>(
220        &mut self,
221        clock: &RobotClock,
222        channel: &'static BridgeChannel<<Self::Rx as BridgeChannelSet>::Id, Payload>,
223        msg: &mut CuMsg<Payload>,
224    ) -> CuResult<()>
225    where
226        Payload: CuMsgPayload + 'a,
227    {
228        self.update()?;
229        msg.tov = Tov::Time(clock.now());
230        match channel.id() {
231            RxId::LqRx => {
232                if let Some(lq) = self.last_lq.as_ref() {
233                    let lqp = LinkStatisticsPayload::from(lq.clone());
234                    let lq_msg: &mut CuMsg<LinkStatisticsPayload> = msg.downcast_mut()?;
235                    lq_msg.set_payload(lqp);
236                }
237            }
238            RxId::RcRx => {
239                if let Some(rc) = self.last_rc.as_ref() {
240                    let rc = RcChannelsPayload::from(rc.clone());
241                    let rc_msg: &mut CuMsg<RcChannelsPayload> = msg.downcast_mut()?;
242                    rc_msg.set_payload(rc);
243                }
244            }
245        }
246        Ok(())
247    }
248}
249
250/// Convenience alias for std targets.
251#[cfg(feature = "std")]
252pub type CrsfBridgeStd = CrsfBridge<cu_linux_resources::LinuxSerialPort, std::io::Error>;