use embassy_sync::{
blocking_mutex::raw::CriticalSectionRawMutex,
pubsub::WaitResult,
watch::{Receiver, Sender, Watch},
};
use radio_controllers::{Radio, Rates, RatesConfig, RcModes, RxFrame, RxLinkStatus};
use static_cell::StaticCell;
use crate::{
boards::{RadioUartRx, RadioUartTx},
config::{
ConfigItem, ConfigPublisher, ConfigSubscriber, FastConfigPublisher, RxConfig, config_publisher,
config_subscriber, fast_config_publisher,
},
flight::{RcAdjustments, RxMessage},
tasks::failsafe::{FailsafeSubscriber, failsafe_subscriber},
};
static RX_CTX: StaticCell<RxContext> = StaticCell::new();
const RX_WATCH_COUNT: usize = 4;
static RX_WATCH: Watch<CriticalSectionRawMutex, RxMessage, RX_WATCH_COUNT> = Watch::new();
type RxMessageSender = Sender<'static, CriticalSectionRawMutex, RxMessage, RX_WATCH_COUNT>;
fn rx_message_sender() -> RxMessageSender {
RX_WATCH.sender()
}
pub type RxMessageReceiver = Receiver<'static, CriticalSectionRawMutex, RxMessage, RX_WATCH_COUNT>;
#[allow(clippy::expect_used)]
pub fn rx_message_receiver() -> RxMessageReceiver {
RX_WATCH.receiver().expect("rx_receiver failed")
}
#[cfg(feature = "autopilot")]
use super::autopilot::{AutopilotReceiver, autopilot_receiver};
pub struct RxContext {
pub radio: Radio,
#[allow(unused)]
pub uart_rx: RadioUartRx,
#[allow(unused)]
pub uart_tx: RadioUartTx,
pub rx_message_sender: RxMessageSender,
pub failsafe_subscriber: FailsafeSubscriber,
pub config_subscriber: ConfigSubscriber,
pub config_publisher: ConfigPublisher,
pub fast_config_publisher: FastConfigPublisher,
pub rc_modes: RcModes,
pub rates: Rates,
pub rc_adjustments: RcAdjustments,
pub buf: [u8; Self::BUF_SIZE],
#[cfg(feature = "autopilot")]
pub autopilot_receiver: AutopilotReceiver,
}
impl RxContext {
const BUF_SIZE: usize = 128;
pub fn new(uart_rx: RadioUartRx, uart_tx: RadioUartTx, rx_config: RxConfig, rates_config: RatesConfig) -> Self {
let radio = Radio::new(rx_config.serial_rx_provider);
Self {
radio,
uart_rx,
uart_tx,
rx_message_sender: rx_message_sender(),
failsafe_subscriber: failsafe_subscriber(),
config_subscriber: config_subscriber(),
config_publisher: config_publisher(),
fast_config_publisher: fast_config_publisher(),
rates: Rates::new(rates_config),
rc_modes: RcModes::new().with_mac_arm(),
rc_adjustments: RcAdjustments::new(),
buf: [0u8; Self::BUF_SIZE],
#[cfg(feature = "autopilot")]
autopilot_receiver: autopilot_receiver(),
}
}
}
pub fn init(
uart_rx: RadioUartRx,
uart_tx: RadioUartTx,
rx_config: RxConfig,
rates: RatesConfig,
) -> &'static mut RxContext {
RX_CTX.init(RxContext::new(uart_rx, uart_tx, rx_config, rates))
}
#[embassy_executor::task]
pub async fn run(ctx: &'static mut RxContext) {
let mut loop_count: u32 = 0;
log::info!(" RX: task started");
loop {
if let Ok(n) = ctx.read_packet().await {
for &byte in &ctx.buf[..n] {
if let Some(rx_frame) = ctx.radio.on_byte_received(byte) {
let rx_message = match rx_frame {
RxFrame::ChannelsLinkStatus { mut channels, link_status } => {
ctx.rc_modes.update_activated_modes(&channels);
if link_status == RxLinkStatus::Failsafe {
channels.set_channels_to_failsafe_values();
}
Some(RxMessage::new_from(&channels, link_status, &ctx.rates, &ctx.rc_modes, loop_count))
}
_ => None,
};
if let Some(WaitResult::Message(failsafe_message)) = ctx.failsafe_subscriber.try_next_message() {
_ = failsafe_message;
}
if let Some(WaitResult::Message(ConfigItem::Rates(rates_config))) =
ctx.config_subscriber.try_next_message()
{
ctx.rates.set(rates_config);
}
ctx.rc_adjustments.process_adjustments(&ctx.config_publisher, &ctx.fast_config_publisher).await;
#[allow(unused_mut)]
if let Some(mut rx_message) = rx_message {
#[cfg(feature = "autopilot")]
if let Some(autopilot_message) = ctx.autopilot_receiver.try_changed() {
use radio_controllers::RcMode;
if ctx.rc_modes.is_mode_active(RcMode::AltitudeHold) {
rx_message.rc_controls.throttle_stick = autopilot_message.rc_controls.throttle_stick;
} else if ctx.rc_modes.is_mode_active(RcMode::PositionHold)
|| ctx.rc_modes.is_mode_active(RcMode::GpsRescue)
|| ctx.rc_modes.is_mode_active(RcMode::Autopilot)
{
rx_message.rc_controls = autopilot_message.rc_controls;
}
}
ctx.rx_message_sender.send(rx_message);
}
if loop_count.is_multiple_of(10) {
log::info!(" RX: loop {loop_count}");
}
loop_count = loop_count.wrapping_add(1);
}
}
}
}
}
impl RxContext {
pub async fn read_packet(&mut self) -> Result<usize, ()> {
#[cfg(any(feature = "rp2040", feature = "rp235xa", feature = "rp235xb"))]
{
match self.uart_rx.read_to_break(&mut self.buf).await {
Ok(n) => Ok(n),
Err(_) => Err(()), }
}
#[cfg(feature = "stm32")]
{
match self.uart_rx.read_until_idle(&mut self.buf).await {
Ok(n) => Ok(n),
Err(_) => Err(()),
}
}
#[cfg(feature = "esp32s3")]
{
core::future::ready(()).await;
Err(())
}
#[cfg(feature = "host")]
{
core::future::ready(()).await;
Err(())
}
}
}