Skip to main content

cu_crsf/
lib.rs

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