stm_32/peripherals/
sbus.rs1use core::sync::atomic::{AtomicU32, Ordering};
37
38use embassy_stm32::mode::Async;
39use embassy_stm32::usart::UartRx;
40use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
41use embassy_sync::channel::Channel;
42use embassy_time::Duration;
43use embassy_time::Instant;
44
45use veloxity_core::errors;
46use veloxity_core::packets;
47
48const RC_QUEUE_CAPACITY: usize = 8;
49
50pub static RC_CHANNEL: Channel<
51 CriticalSectionRawMutex,
52 Result<packets::RcPacket, errors::SensorError>,
53 RC_QUEUE_CAPACITY,
54> = Channel::new();
55
56async fn publish_rc(result: Result<packets::RcPacket, errors::SensorError>) {
57 #[cfg(feature = "runtime-diagnostics")]
58 let error = result.is_err();
59 #[cfg(feature = "runtime-diagnostics")]
60 let wait_started = RC_CHANNEL.is_full().then(Instant::now);
61 RC_CHANNEL.send(result).await;
62 #[cfg(feature = "runtime-diagnostics")]
63 crate::runtime_diagnostics::record_rc_queue_publish(
64 error,
65 wait_started.map(|started| started.elapsed().as_micros().min(u32::MAX as u64) as u32),
66 RC_CHANNEL.len(),
67 );
68}
69
70pub const SBUS_11_BIT_CHANNELS: usize = 16;
71pub const SBUS_BINARY_CHANNELS: usize = 2; pub const SBUS_CHANNELS: usize = SBUS_11_BIT_CHANNELS + SBUS_BINARY_CHANNELS;
73
74static SBUS_READ_OK: AtomicU32 = AtomicU32::new(0);
75static SBUS_READ_ERR: AtomicU32 = AtomicU32::new(0);
76static SBUS_LAST_READ_SIZE: AtomicU32 = AtomicU32::new(0);
77static SBUS_SIZE_25: AtomicU32 = AtomicU32::new(0);
78static SBUS_VALID_FRAME: AtomicU32 = AtomicU32::new(0);
79static SBUS_BAD_HEADER: AtomicU32 = AtomicU32::new(0);
80static SBUS_BAD_FOOTER: AtomicU32 = AtomicU32::new(0);
81static SBUS_SIGNAL: AtomicU32 = AtomicU32::new(0);
82static SBUS_TIMEOUT: AtomicU32 = AtomicU32::new(0);
83static SBUS_LAST_STATUS: AtomicU32 = AtomicU32::new(0);
84
85#[derive(Clone, Copy, Debug, Default)]
86pub struct SbusDiagnostics {
87 pub read_ok: u32,
88 pub read_err: u32,
89 pub last_read_size: u32,
90 pub size_25: u32,
91 pub valid_frame: u32,
92 pub bad_header: u32,
93 pub bad_footer: u32,
94 pub signal: u32,
95 pub timeout: u32,
96 pub last_status: u32,
97}
98
99pub fn diagnostics() -> SbusDiagnostics {
100 SbusDiagnostics {
101 read_ok: SBUS_READ_OK.load(Ordering::Relaxed),
102 read_err: SBUS_READ_ERR.load(Ordering::Relaxed),
103 last_read_size: SBUS_LAST_READ_SIZE.load(Ordering::Relaxed),
104 size_25: SBUS_SIZE_25.load(Ordering::Relaxed),
105 valid_frame: SBUS_VALID_FRAME.load(Ordering::Relaxed),
106 bad_header: SBUS_BAD_HEADER.load(Ordering::Relaxed),
107 bad_footer: SBUS_BAD_FOOTER.load(Ordering::Relaxed),
108 signal: SBUS_SIGNAL.load(Ordering::Relaxed),
109 timeout: SBUS_TIMEOUT.load(Ordering::Relaxed),
110 last_status: SBUS_LAST_STATUS.load(Ordering::Relaxed),
111 }
112}
113
114pub struct SbusRC {
115 pub uart: UartRx<'static, Async>,
116}
117
118pub fn extract_chan(bytes: &[u8], bit_offset: usize) -> f32 {
120 let byte_offset = bit_offset / 8;
122 let bit_start = bit_offset % 8;
123
124 let mut value: u16 = 0;
125 for i in 0..11 {
126 let byte_index = byte_offset + (bit_start + i) / 8;
127 let bit_index = (bit_start + i) % 8;
128 let bit = (bytes[byte_index] >> bit_index) & 1;
129 value |= (bit as u16) << i;
130 }
131
132 value as f32
133}
134
135impl SbusRC {
136 pub async fn run(&mut self) {
137 let mut buffer = [0u8; 25];
138
139 let mut chan = [0f32; SBUS_CHANNELS];
140 let mut timeout = Instant::now() + Duration::from_secs(1);
141 let mut rc_chan = [0.0f32; packets::RC_PACKET_CHANNELS];
142 loop {
143 let result = self.uart.read_until_idle(&mut buffer).await;
145 if let Ok(size) = result {
146 SBUS_READ_OK.fetch_add(1, Ordering::Relaxed);
147 SBUS_LAST_READ_SIZE.store(size as u32, Ordering::Relaxed);
148 if size == buffer.len() {
149 SBUS_SIZE_25.fetch_add(1, Ordering::Relaxed);
150 }
151 timeout = Instant::now() + Duration::from_secs(1);
152 let valid_header = buffer[0] == 0x0F;
153 let valid_footer = (buffer[24] == 0x00)
154 || (buffer[24] == 0x04)
155 || (buffer[24] == 0x14)
156 || (buffer[24] == 0x24)
157 || (buffer[24] == 0x34);
158 if valid_header && valid_footer {
159 SBUS_VALID_FRAME.fetch_add(1, Ordering::Relaxed);
160 let dig = buffer[23] as u16;
161 SBUS_LAST_STATUS.store(dig as u32, Ordering::Relaxed);
162
163 for i in 0..SBUS_11_BIT_CHANNELS {
165 chan[i] = (extract_chan(&buffer, 8 + i * 11) - 172.0) / 1639.0;
166 }
167 chan[0 + SBUS_11_BIT_CHANNELS] = ((((dig) & 0x01) as f32) - 172.0) / 1639.0; chan[1 + SBUS_11_BIT_CHANNELS] =
170 ((((dig >> 1) & 0x01) as f32) - 172.0) / 1639.0; let header = packets::RosflightPacketHeader {
173 timestamp: Instant::now().as_micros(),
174 status: dig,
175 };
176
177 rc_chan = [0.0f32; packets::RC_PACKET_CHANNELS];
178
179 let mut len = SBUS_CHANNELS;
180
181 if SBUS_CHANNELS > packets::RC_PACKET_CHANNELS {
182 len = packets::RC_PACKET_CHANNELS;
183 }
184
185 for i in 0..len {
186 rc_chan[i] = chan[i];
187 }
188
189 let rc_packet = packets::RcPacket {
190 header,
191 n_chan: 24,
192 chan: rc_chan,
193 lol: (dig & 0x0C) != 0, };
195 SBUS_SIGNAL.fetch_add(1, Ordering::Relaxed);
196 publish_rc(Ok(rc_packet)).await;
197 } else {
198 if !valid_header {
199 SBUS_BAD_HEADER.fetch_add(1, Ordering::Relaxed);
200 }
201 if valid_header && !valid_footer {
202 SBUS_BAD_FOOTER.fetch_add(1, Ordering::Relaxed);
203 }
204 }
205 } else {
206 SBUS_READ_ERR.fetch_add(1, Ordering::Relaxed);
207 }
208
209 if Instant::now() > timeout {
210 timeout = Instant::now() + Duration::from_secs(1);
211 let dig = 0x1C; SBUS_TIMEOUT.fetch_add(1, Ordering::Relaxed);
213 SBUS_LAST_STATUS.store(dig as u32, Ordering::Relaxed);
214 let header = packets::RosflightPacketHeader {
215 timestamp: Instant::now().as_micros(),
216 status: dig,
217 };
218 let rc_packet = packets::RcPacket {
219 header,
220 n_chan: 24,
221 chan: rc_chan, lol: (dig & 0x1C) != 0, };
224 SBUS_SIGNAL.fetch_add(1, Ordering::Relaxed);
225 publish_rc(Ok(rc_packet)).await;
226 }
227 }
228 }
229}
230
231#[embassy_executor::task]
232pub async fn task(mut sbus: SbusRC) {
233 sbus.run().await;
234}