1#![cfg_attr(not(feature = "std"), no_std)]
6
7#[cfg(not(feature = "std"))]
8extern crate alloc;
9
10#[cfg(feature = "std")]
12mod std_impl {
13 pub use std::{format, mem, string::String, vec::Vec};
14
15 pub const DEVICE_KEY: &str = "device";
16 pub const BAUD_KEY: &str = "baudrate";
17 pub const TIMEOUT_KEY: &str = "timeout_ms";
18 pub const DEFAULT_DEVICE: &str = "/dev/ttyUSB0";
19 pub const DEFAULT_BAUDRATE: u32 = 115_200;
20 pub const DEFAULT_TIMEOUT_MS: u64 = 50;
21}
22
23#[cfg(not(feature = "std"))]
25mod no_std_impl {
26 pub use alloc::{format, vec::Vec};
27 pub use core::mem;
28
29 pub const SERIAL_INDEX_KEY: &str = "serial_port_index";
30}
31
32#[cfg(not(feature = "std"))]
33use no_std_impl::*;
34#[cfg(feature = "std")]
35use std_impl::*;
36
37use bincode::de::Decoder;
38use bincode::enc::Encoder;
39use bincode::error::{DecodeError, EncodeError};
40use bincode::{Decode, Encode};
41use cu29::cubridge::{
42 BridgeChannel, BridgeChannelConfig, BridgeChannelInfo, BridgeChannelSet, CuBridge,
43};
44use cu29::prelude::*;
45use cu_msp_lib::structs::{MspRequest, MspResponse};
46use cu_msp_lib::{MspPacket, MspParser};
47use embedded_io::{Read, Write};
48use serde::{Deserialize, Serialize};
49use smallvec::SmallVec;
50
51const READ_BUFFER_SIZE: usize = 512;
52const MAX_REQUESTS_PER_BATCH: usize = 8;
53const MAX_RESPONSES_PER_BATCH: usize = 16;
54
55pub trait SerialFactory<E>: Write<Error = E> + Read<Error = E> + Send + 'static {
56 fn try_new(config: Option<&ComponentConfig>) -> CuResult<Self>
57 where
58 Self: Sized;
59}
60
61#[cfg(not(feature = "std"))]
62impl<S, E> SerialFactory<E> for S
63where
64 S: Write<Error = E> + Read<Error = E> + Send + 'static,
65{
66 fn try_new(config: Option<&ComponentConfig>) -> CuResult<Self> {
67 let slot = config
68 .and_then(|cfg| cfg.get::<u32>(SERIAL_INDEX_KEY))
69 .map(|v| v as usize)
70 .unwrap_or(0);
71
72 cu_embedded_registry::take(slot).ok_or_else(|| {
73 CuError::from(format!(
74 "MSP bridge missing serial for slot {slot} (max index {})",
75 cu_embedded_registry::MAX_SERIAL_SLOTS - 1
76 ))
77 })
78 }
79}
80
81#[cfg(feature = "std")]
82impl SerialFactory<std::io::Error> for std_serial::StdSerial {
83 fn try_new(config: Option<&ComponentConfig>) -> CuResult<Self> {
84 let device = config
85 .and_then(|cfg| cfg.get::<String>(DEVICE_KEY))
86 .filter(|s| !s.is_empty())
87 .unwrap_or_else(|| DEFAULT_DEVICE.to_string());
88
89 let baudrate = config
90 .and_then(|cfg| cfg.get::<u32>(BAUD_KEY))
91 .unwrap_or(DEFAULT_BAUDRATE);
92
93 let timeout_ms = config
94 .and_then(|cfg| cfg.get::<u64>(TIMEOUT_KEY))
95 .unwrap_or(DEFAULT_TIMEOUT_MS);
96
97 std_serial::open(&device, baudrate, timeout_ms).map_err(|err| {
98 CuError::from(format!(
99 "MSP bridge failed to open serial `{device}` at {baudrate} baud: {err}"
100 ))
101 })
102 }
103}
104
105#[derive(Debug, Clone, Default, Serialize, Deserialize)]
107pub struct MspRequestBatch(pub SmallVec<[MspRequest; MAX_REQUESTS_PER_BATCH]>);
108
109impl MspRequestBatch {
110 pub fn new() -> Self {
111 Self(SmallVec::new())
112 }
113
114 pub fn push(&mut self, req: MspRequest) {
115 let Self(ref mut vec) = self;
116 vec.push(req);
117 }
118
119 pub fn iter(&self) -> impl Iterator<Item = &MspRequest> {
120 let Self(ref vec) = self;
121 vec.iter()
122 }
123}
124
125impl Encode for MspRequestBatch {
126 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
127 Encode::encode(&self.0.as_slice(), encoder)
128 }
129}
130
131impl Decode<()> for MspRequestBatch {
132 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
133 let values = <Vec<MspRequest> as Decode<()>>::decode(decoder)?;
134 Ok(Self(values.into()))
135 }
136}
137
138#[derive(Debug, Clone, Default, Serialize, Deserialize)]
140pub struct MspResponseBatch(pub SmallVec<[MspResponse; MAX_RESPONSES_PER_BATCH]>);
141
142impl MspResponseBatch {
143 pub fn new() -> Self {
144 Self(SmallVec::new())
145 }
146
147 pub fn push(&mut self, resp: MspResponse) {
148 let Self(ref mut vec) = self;
149 vec.push(resp);
150 }
151
152 pub fn clear(&mut self) {
153 self.0.clear();
154 }
155
156 pub fn is_empty(&self) -> bool {
157 self.0.is_empty()
158 }
159}
160
161impl Encode for MspResponseBatch {
162 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
163 Encode::encode(&self.0.as_slice(), encoder)
164 }
165}
166
167impl Decode<()> for MspResponseBatch {
168 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
169 let values = <Vec<MspResponse> as Decode<()>>::decode(decoder)?;
170 Ok(Self(values.into()))
171 }
172}
173
174tx_channels! {
175 pub struct TxChannels : TxId {
176 requests => MspRequestBatch,
177 }
178}
179
180rx_channels! {
181 pub struct RxChannels : RxId {
182 responses => MspResponseBatch,
183 }
184}
185
186pub struct CuMspBridge<S, E>
188where
189 S: Write<Error = E> + Read<Error = E>,
190{
191 serial: S,
192 parser: MspParser,
193 read_buffer: [u8; READ_BUFFER_SIZE],
194 pending_responses: MspResponseBatch,
195 tx_buffer: SmallVec<[u8; 256]>,
196}
197
198impl<S, E> CuMspBridge<S, E>
199where
200 S: Write<Error = E> + Read<Error = E>,
201{
202 fn from_serial(serial: S) -> Self {
203 Self {
204 serial,
205 parser: MspParser::new(),
206 read_buffer: [0; READ_BUFFER_SIZE],
207 pending_responses: MspResponseBatch::new(),
208 tx_buffer: SmallVec::new(),
209 }
210 }
211
212 fn send_request(&mut self, request: &MspRequest) -> CuResult<()> {
213 let packet: MspPacket = request.into();
214 let size = packet.packet_size_bytes();
215 self.tx_buffer.resize(size, 0);
216 packet.serialize(&mut self.tx_buffer).map_err(|err| {
217 CuError::new_with_cause("MSP bridge failed to serialize request", err)
218 })?;
219 self.serial
220 .write_all(&self.tx_buffer)
221 .map_err(|_| CuError::from("MSP bridge failed to write serial"))
222 }
223
224 fn poll_serial(&mut self) -> CuResult<()> {
225 loop {
226 match self.serial.read(&mut self.read_buffer) {
227 Ok(0) => break,
228 Ok(n) => {
229 for &byte in &self.read_buffer[..n] {
230 match self.parser.parse(byte) {
231 Ok(Some(packet)) => {
232 let response = MspResponse::from(packet);
233 self.pending_responses.push(response);
234 if self.pending_responses.0.len() >= MAX_RESPONSES_PER_BATCH {
235 break;
236 }
237 }
238 Ok(None) => {}
239 Err(err) => {
240 error!("MSP bridge parser error: {}", err.to_string());
241 }
242 }
243 }
244 }
245 Err(_) => break,
246 }
247 }
248 Ok(())
249 }
250}
251
252#[cfg(not(feature = "std"))]
253impl<S, E> CuMspBridge<S, E>
254where
255 S: SerialFactory<E>,
256{
257 pub fn register_serial(slot: usize, serial_port: S) -> CuResult<()> {
259 cu_embedded_registry::register(slot, serial_port)
260 }
261}
262
263impl<S, E> Freezable for CuMspBridge<S, E> where S: Write<Error = E> + Read<Error = E> {}
264
265impl<S, E> CuBridge for CuMspBridge<S, E>
266where
267 S: SerialFactory<E>,
268{
269 type Tx = TxChannels;
270 type Rx = RxChannels;
271
272 fn new(
273 config: Option<&ComponentConfig>,
274 tx_channels: &[BridgeChannelConfig<<Self::Tx as BridgeChannelSet>::Id>],
275 rx_channels: &[BridgeChannelConfig<<Self::Rx as BridgeChannelSet>::Id>],
276 ) -> CuResult<Self>
277 where
278 Self: Sized,
279 {
280 let _ = tx_channels;
281 let _ = rx_channels;
282 Ok(Self::from_serial(S::try_new(config)?))
283 }
284
285 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
286 self.poll_serial()
287 }
288
289 fn send<'a, Payload>(
290 &mut self,
291 _clock: &RobotClock,
292 channel: &'static BridgeChannel<<Self::Tx as BridgeChannelSet>::Id, Payload>,
293 msg: &CuMsg<Payload>,
294 ) -> CuResult<()>
295 where
296 Payload: CuMsgPayload + 'a,
297 {
298 match channel.id() {
299 TxId::Requests => {
300 let request_msg: &CuMsg<MspRequestBatch> = msg.downcast_ref()?;
301 if let Some(batch) = request_msg.payload() {
302 for request in batch.iter() {
303 self.send_request(request)?;
304 }
305 }
306 }
307 }
308 Ok(())
309 }
310
311 fn receive<'a, Payload>(
312 &mut self,
313 _clock: &RobotClock,
314 channel: &'static BridgeChannel<<Self::Rx as BridgeChannelSet>::Id, Payload>,
315 msg: &mut CuMsg<Payload>,
316 ) -> CuResult<()>
317 where
318 Payload: CuMsgPayload + 'a,
319 {
320 match channel.id() {
321 RxId::Responses => {
322 let response_msg: &mut CuMsg<MspResponseBatch> = msg.downcast_mut()?;
323 let mut batch = MspResponseBatch::new();
324 mem::swap(&mut batch, &mut self.pending_responses);
325 response_msg.set_payload(batch);
326 }
327 }
328 Ok(())
329 }
330}
331
332#[cfg(feature = "std")]
333pub mod std_serial {
334 use embedded_io_adapters::std::FromStd;
335 #[cfg(feature = "std")]
336 use std::boxed::Box;
337 #[cfg(feature = "std")]
338 use std::time::Duration;
339
340 pub type StdSerial = FromStd<Box<dyn serialport::SerialPort>>;
341
342 pub fn open(path: &str, baud: u32, timeout_ms: u64) -> std::io::Result<StdSerial> {
343 let port = serialport::new(path, baud)
344 .timeout(Duration::from_millis(timeout_ms))
345 .open()?;
346 Ok(FromStd::new(port))
347 }
348}
349
350#[cfg(feature = "std")]
352pub type CuMspBridgeStd = CuMspBridge<std_serial::StdSerial, std::io::Error>;