Skip to content

Commit b3656a2

Browse files
committed
finish and document CAN message types, add engine, ereg subsystems to FC
1 parent 236ed0d commit b3656a2

14 files changed

Lines changed: 1241 additions & 67 deletions

File tree

firmwares/flight_computer/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,8 @@ telemetry = { path = "../../telemetry" }
7070
defmt-test = "0.3"
7171

7272
[features]
73-
gcs = []
74-
rev1 = []
73+
default = ["ethernet"]
74+
# gcs = []
75+
# rev1 = []
76+
usb = []
77+
ethernet = []

firmwares/flight_computer/src/bin/rocket.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ async fn main(low_priority_spawner: Spawner) -> ! {
9696
#[cfg(feature = "usb")]
9797
let (usb_downlink, usb_uplink) = fw::usb::start(board.usb_driver, low_priority_spawner);
9898

99+
#[cfg(feature = "ethernet")]
99100
let (eth_downlink, eth_uplink) = fw::ethernet::start(board.ethernet, low_priority_spawner, seed);
100101

101102
// subsystems
@@ -120,6 +121,7 @@ async fn main(low_priority_spawner: Spawner) -> ! {
120121
(can2_tx_pub, can2_rx_sub),
121122
#[cfg(feature = "usb")]
122123
(usb_downlink, usb_uplink),
124+
#[cfg(feature = "ethernet")]
123125
(eth_downlink, eth_uplink),
124126
(lora_downlink, lora_uplink),
125127
subsystems,

firmwares/flight_computer/src/buzzer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Driver for the on-board buzzer, responsible for playing mode change beeps and
22
//! warning tones using the STM32's timers for PWM generation.
33
4+
use defmt::warn;
45
use embassy_executor::Spawner;
56
use embassy_futures::select::{Either, select};
67
use embassy_stm32::pac::gpio::Gpio;
@@ -305,7 +306,6 @@ const NO_BATTERY_ATTACHED_MELODY: [Note; 4] = [
305306
pub async fn run(mut pwm: SimplePwm<'static, embassy_stm32::peripherals::TIM2>, channel: Channel) -> ! {
306307
let max_duty = pwm.max_duty_cycle();
307308

308-
pwm.channel(channel).enable();
309309
pwm.channel(channel).set_duty_cycle(max_duty / 2);
310310

311311
let mut flight_mode = FlightMode::default();

firmwares/flight_computer/src/can.rs

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
11
use defmt::*;
22
use embassy_executor::SendSpawner;
3-
// use embassy_stm32::can::bxcan::{Frame, Id, StandardId};
43
use embassy_stm32::can::{Can, CanRx, CanTx, Frame};
5-
// use embassy_stm32::peripherals::CAN;
64
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7-
85
use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber};
6+
97
use embedded_can::{Id, StandardId};
108
use heapless::Vec;
11-
use nalgebra::inf;
129
use static_cell::StaticCell;
1310

1411
use shared_types::can::{
1512
CanMessage,
1613
structure::{Can2aFrame, CanFrameId},
1714
};
1815

19-
pub const CAN_QUEUE_SIZE: usize = 20;
16+
pub const CAN_RX_QUEUE_SIZE: usize = 20;
17+
pub const CAN_TX_QUEUE_SIZE: usize = 20;
2018
pub const NUM_CAN_SUBSCRIBERS: usize = 1;
2119
pub const NUM_CAN_PUBLISHERS: usize = 1;
2220

23-
pub type CanRxChannel = PubSubChannel<CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
21+
pub type CanRxChannel = PubSubChannel<CriticalSectionRawMutex, CanMessage, CAN_RX_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
2422
pub type CanRxSubscriber =
25-
Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
26-
pub type CanTxChannel = PubSubChannel<CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
23+
Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_RX_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
24+
pub type CanTxChannel = PubSubChannel<CriticalSectionRawMutex, CanMessage, CAN_TX_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
2725
pub type CanTxPublisher =
28-
Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
26+
Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_TX_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
2927

3028
// --- can1
3129
pub static CAN1_RX_CH: StaticCell<CanRxChannel> = StaticCell::new();
@@ -41,12 +39,11 @@ pub static CAN2_TX_CH: StaticCell<CanTxChannel> = StaticCell::new();
4139
static CAN2_TX: StaticCell<CanTx<'static>> = StaticCell::new();
4240
static CAN2_RX: StaticCell<CanRx<'static>> = StaticCell::new();
4341

42+
// --- dedicated tasks for receiving and sending CAN messages for each hardware Bus
4443
async fn run_can_rx(can_rx: &'static mut CanRx<'static>, publisher: CanTxPublisher) -> ! {
45-
info!("Run Can RX");
4644
loop {
4745
match can_rx.read().await {
4846
Ok(envelope) => {
49-
info!("Read Can Message");
5047
let frame = envelope.frame;
5148
let Id::Standard(id) = frame.id() else {
5249
warn!("Unexpected extended 29bit Can Frame Id, dropping.");
@@ -58,52 +55,46 @@ async fn run_can_rx(can_rx: &'static mut CanRx<'static>, publisher: CanTxPublish
5855
};
5956
let Ok(id) = CanFrameId::try_from(id.as_raw()) else {
6057
warn!(
61-
"Can Frame could not be converted, this is a bug in the implementation and should never happen"
58+
"Can Frame could not parsed into type, either the senders or this software might be outdated."
6259
);
6360
continue;
6461
};
6562
let message = Can2aFrame { id, payload: data };
6663
let Ok(message_parsed) = CanMessage::try_from(message) else {
67-
warn!("Malformed Can Message, dropping"); //id: {}, payload: {}", message.id, message.payload);
64+
warn!("Malformed Can Message, message could not be parsed into type, dropping"); //id: {}, payload: {}", message.id, message.payload);
6865
continue;
6966
};
7067
publisher.publish_immediate(message_parsed); // TODO: think about publish immediate
7168
}
7269
Err(e) => {
73-
error!("Failed to read can envelope: {:?}", Debug2Format(&e))
70+
error!("Can Bus Error: Failed to read can envelope: {:?}", Debug2Format(&e))
7471
}
7572
}
7673
}
7774
}
7875

79-
async fn run_can_tx(
80-
can_tx: &'static mut CanTx<'static>,
81-
// mut subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
82-
mut subscriber: CanRxSubscriber,
83-
) -> ! {
84-
info!("Run can tx");
76+
async fn run_can_tx(can_tx: &'static mut CanTx<'static>, mut subscriber: CanRxSubscriber) -> ! {
8577
loop {
8678
let message = subscriber.next_message_pure().await; // should we care about lag?
87-
info!("Can message transmit");
8879
let frame = Can2aFrame::from(message);
8980

9081
let Some(sid) = StandardId::new(frame.id.into()) else {
82+
error!("Can2.0A Frame could not be converted to StandardId, this is a bug in the implementation.");
9183
continue;
9284
};
9385

9486
// unwrap is safe, because payload slice is never larger than 8 bytes
9587
let frame = Frame::new_data(sid, frame.payload.as_slice()).unwrap();
96-
info!("embassy can frame write start");
9788
can_tx.write(&frame).await;
98-
info!("embassy can frame write finished");
9989
}
10090
}
10191

92+
// --- CAN1
10293
pub async fn spawn_can1(
10394
can: Can<'static>,
10495
spawner: SendSpawner,
105-
publisher: Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
106-
subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
96+
publisher: Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_RX_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
97+
subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_RX_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
10798
) {
10899
let (can_tx, can_rx, _properties) = can.split();
109100
let can_tx = CAN1_TX.init(can_tx);
@@ -114,11 +105,7 @@ pub async fn spawn_can1(
114105
}
115106

116107
#[embassy_executor::task]
117-
async fn run_can1_tx(
118-
can_tx: &'static mut CanTx<'static>,
119-
// mut subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
120-
mut subscriber: CanRxSubscriber,
121-
) -> ! {
108+
async fn run_can1_tx(can_tx: &'static mut CanTx<'static>, mut subscriber: CanRxSubscriber) -> ! {
122109
run_can_tx(can_tx, subscriber).await
123110
}
124111

@@ -131,8 +118,8 @@ async fn run_can1_rx(can_rx: &'static mut CanRx<'static>, publisher: CanTxPublis
131118
pub async fn spawn_can2(
132119
can: Can<'static>,
133120
spawner: SendSpawner,
134-
publisher: Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
135-
subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
121+
publisher: Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_RX_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
122+
subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_RX_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
136123
) {
137124
let (can_tx, can_rx, _properties) = can.split();
138125
let can_tx = CAN2_TX.init(can_tx);

firmwares/flight_computer/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,11 @@ pub async fn init_board()
297297
let lora2_iv = GenericSx126xInterfaceVariant::new(lora2_reset, lora2_irq, lora2_busy, None, None).unwrap();
298298
let mut lora2 = LoRa::new(Sx126x::new(lora2_spi, lora2_iv, lora2_config), false, Delay).await.unwrap();
299299

300-
let mut can1 = CanConfigurator::new(p.FDCAN1, p.PB8, p.PB9, Irqs);
301-
let mut can2 = CanConfigurator::new(p.FDCAN2, p.PB5, p.PB6, Irqs);
300+
let mut can1 = CanConfigurator::new(p.FDCAN2, p.PB5, p.PB6, Irqs);
301+
let mut can2 = CanConfigurator::new(p.FDCAN1, p.PB8, p.PB9, Irqs);
302302

303-
can1.set_bitrate(1_000_000);
304-
can2.set_bitrate(1_000_000);
303+
can1.set_bitrate(125_000);
304+
can2.set_bitrate(125_000);
305305

306306
let mut can1 = can1.into_normal_mode();
307307
let mut can2 = can2.into_normal_mode();

firmwares/flight_computer/src/lora.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,13 @@ async fn run_gcs_downlink(
283283
let timeout = Duration::from_millis(DOWNLINK_PACKET_INTERVAL_MILLIS - 10);
284284
if let Some((msg, status)) = attempt_receive_downlink(&mut lora, &key, f, timeout, &local_settings).await {
285285
downlink_sender.send(msg.clone()).await;
286-
downlink_sender.send(DownlinkMessage::TelemetryGCS(shared_types::telemetry::TelemetryGCS {
287-
time: msg.time_produced().unwrap_or(0),
288-
lora_rssi: status.rssi as i32,
289-
lora_snr: status.snr as i32,
290-
})).await;
286+
downlink_sender
287+
.send(DownlinkMessage::TelemetryGCS(shared_types::telemetry::TelemetryGCS {
288+
time: msg.time_produced().unwrap_or(0),
289+
lora_rssi: status.rssi as i32,
290+
lora_snr: status.snr as i32,
291+
}))
292+
.await;
291293
failure_counter = 0;
292294
} else {
293295
failure_counter += 1;
@@ -325,7 +327,9 @@ async fn attempt_receive_downlink(
325327
lora.prepare_for_rx(RxMode::Continuous, &mod_params, &packet_params).await.unwrap();
326328

327329
match with_timeout(timeout, lora.rx(&packet_params, &mut rx_buffer)).await {
328-
Ok(Ok((len, status))) => decode_and_verify::<_, DOWNLINK_HMAC_LEN>(&key, &mut rx_buffer[..(len as usize)]).map(|msg| (msg, status)),
330+
Ok(Ok((len, status))) => {
331+
decode_and_verify::<_, DOWNLINK_HMAC_LEN>(&key, &mut rx_buffer[..(len as usize)]).map(|msg| (msg, status))
332+
}
329333
_ => None,
330334
}
331335
}
@@ -347,8 +351,8 @@ async fn run_gcs_uplink(
347351

348352
let serialized = serialize_and_hmac::<_, UPLINK_HMAC_LEN>(&key, &msg);
349353
tx_buffer[..serialized.len()].copy_from_slice(&serialized);
350-
351-
for _ in 0..3 {
354+
355+
for _ in 0..3 {
352356
let mod_params = lora.create_modulation_params(UPLINK_SF, UPLINK_BW, UPLINK_CR, frequency).unwrap();
353357
let mut packet_params =
354358
lora.create_rx_packet_params(4, false, UPLINK_MSG_LEN as u8, true, false, &mod_params).unwrap();
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use defmt::info;
2+
use embassy_time::{Duration, Instant};
3+
use shared_types::can::{
4+
CanMessage, SubsystemCommand, SubsystemInfo,
5+
engine::{EngineInfoMsg, EngineState, LaunchCode},
6+
};
7+
8+
use super::{ConnectivityStatus, Subsystem};
9+
use crate::can::CanTxPublisher;
10+
11+
const CONECTIVITY_THRESHOLD: Duration = Duration::from_secs(2);
12+
const MIN_MSG_INTERVAL: Duration = Duration::from_millis(1000);
13+
14+
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
15+
pub struct EngineMeasurements {
16+
/// Pressure at the bottom of the oxidizer tank
17+
pub pressure_ox: u8,
18+
/// Pressure at combustion chamber
19+
pub pressure_combustion_chamber: u16,
20+
/// Temperature of the oxidizer / oxidizer tank
21+
pub temp_ox: u8,
22+
/// How open the main valve is: 1 = open to 0 = closed
23+
pub main_valve: u8,
24+
/// How open the main valve is: 1 = open to 0 = closed
25+
pub fill_and_dump_valve: u8,
26+
}
27+
impl From<EngineInfoMsg> for EngineMeasurements {
28+
fn from(value: EngineInfoMsg) -> Self {
29+
EngineMeasurements {
30+
pressure_ox: value.pressure_ox,
31+
pressure_combustion_chamber: value.pressure_combustion_chamber,
32+
temp_ox: value.temp_ox,
33+
main_valve: value.main_valve,
34+
fill_and_dump_valve: value.fill_and_dump_valve,
35+
}
36+
}
37+
}
38+
39+
#[derive(Default)]
40+
pub struct EngineSubsystem {
41+
target_state: EngineState,
42+
// last state reported by the engine
43+
current_state: Option<EngineState>,
44+
// last measurements reported by the engine
45+
measurements: Option<EngineMeasurements>,
46+
// timestamp, for keeping track of connection quality
47+
last_received_message: Option<(CanMessage, Instant)>,
48+
// timestamp for preventing successive repeats
49+
last_sent_state_message: Option<Instant>,
50+
// Contains launch code
51+
ignition: Option<u64>,
52+
}
53+
impl EngineSubsystem {
54+
/// Send messages based on internal state, update internal state.
55+
pub async fn tick(&mut self, can_publisher: &mut CanTxPublisher) {
56+
self.check_publish_state(can_publisher).await;
57+
}
58+
/// Update internal state based on received CAN messages.
59+
pub fn process_message(&mut self, message: CanMessage) {
60+
if let CanMessage::Info(SubsystemInfo::Engine(msg)) = message {
61+
self.last_received_message = Some((message, Instant::now()));
62+
self.measurements = Some(msg.into());
63+
self.current_state = Some(msg.engine_state);
64+
}
65+
}
66+
pub fn set_target_state(&mut self, state: EngineState) {
67+
if state == EngineState::Abort {
68+
self.ignition = None;
69+
}
70+
if self.target_state != state {
71+
// reset spam protection
72+
self.last_sent_state_message = None;
73+
}
74+
// should we verify this here?
75+
self.target_state = state;
76+
}
77+
pub fn set_ignition_true(&mut self, code: u64) {
78+
self.target_state = EngineState::Ignition;
79+
self.ignition = Some(code);
80+
}
81+
pub fn get_engine_measurements(&self) -> Option<EngineMeasurements> {
82+
self.measurements
83+
}
84+
85+
async fn check_publish_state(&mut self, can_publisher: &mut CanTxPublisher) {
86+
let mut needs_publishing: bool = match self.target_state {
87+
EngineState::Disarmed
88+
| EngineState::Filling
89+
| EngineState::Ready
90+
| EngineState::Ignition
91+
| EngineState::Abort
92+
| EngineState::Scrub
93+
| EngineState::Dump => true,
94+
EngineState::Burn => false,
95+
};
96+
let state_automatically_changed = self.current_state == Some(EngineState::Burn);
97+
let states_differ =
98+
!state_automatically_changed && (self.target_state != self.current_state.unwrap_or_default());
99+
// TODO: check anomalies with automatic burn state
100+
let min_time_elapsed =
101+
self.last_sent_state_message.map(|inst| inst.elapsed() > MIN_MSG_INTERVAL).unwrap_or(true);
102+
let unresponsive = self.conectivity_status() != ConnectivityStatus::Online;
103+
104+
if needs_publishing && min_time_elapsed && (states_differ | unresponsive) {
105+
let msg = match self.ignition {
106+
// Don't send ignition in abort state.
107+
Some(code) if self.target_state == EngineState::Ignition => {
108+
CanMessage::Cmd(SubsystemCommand::IgniteEngine(LaunchCode(code)))
109+
}
110+
Some(_) | None => CanMessage::Cmd(SubsystemCommand::SetEngineState(self.target_state)),
111+
};
112+
// NOTE: think about publish_immediate
113+
can_publisher.publish_immediate(msg);
114+
// info!("Sending 'SetEngineState' Can Message");
115+
self.last_sent_state_message = Some(Instant::now());
116+
}
117+
}
118+
}
119+
120+
impl Subsystem for EngineSubsystem {
121+
fn conectivity_status(&self) -> super::ConnectivityStatus {
122+
match self.last_received_message {
123+
None => ConnectivityStatus::Offline,
124+
Some((_, inst)) => {
125+
if inst.elapsed() > CONECTIVITY_THRESHOLD {
126+
ConnectivityStatus::OfflineAgain
127+
} else {
128+
ConnectivityStatus::Online
129+
}
130+
}
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)