1
Fork 0

Compare commits

...

13 commits

Author SHA1 Message Date
ad82a25329
improved telemetry link
reduced send speed and improved receive logic to recover faster

a next step is to try forgoing tokio-serial and the codec trait
for (hopefully) better real-time performance
2025-03-05 22:30:18 -05:00
ad1f552151
fixed southbridge
this was a two week blocker, storing to AtomicBools hangs in some
scenarios, which is really hard to debug. works now.
2025-03-02 12:15:29 -05:00
cc18f76e55
switched to a state instead of change architecture
southbridge currently hangs, embassy does not make
such things easy to diagnose
2025-02-14 17:24:43 -05:00
b131ad8957
two types of command at the same time (sputters, lags, then dies) 2025-02-13 15:57:34 -05:00
0a2f22a4f1
underglow 2025-02-09 18:44:34 -05:00
4af8cb7aa7
display pico temperature 2025-02-09 15:33:17 -05:00
ef93458e94
handle more than one peice of sensor data at a time 2025-02-09 14:19:21 -05:00
1457a035ee
send ambient temperature to northbridge 2025-02-09 13:49:13 -05:00
025b05e3de
implemented brownout on northbridge
this is unsound in that only the most recent command gets modulated by
the bus voltage. In practice this should be fine as commands should come
over the pipe regularly in situations that cause a brownout
2025-02-09 13:37:52 -05:00
bd683d9b74
don't drop connection on spurious lapse
also reduced logging (systemd is a killer)
2025-02-09 12:56:09 -05:00
852b95dcc0
propagate voltage to controller 2025-02-08 22:22:17 -05:00
498287aedf
calibrated voltmeter 2025-02-08 21:55:29 -05:00
e4516bc804
send bus voltage over pipe 2025-02-08 20:13:47 -05:00
12 changed files with 626 additions and 241 deletions

View file

@ -1,7 +1,7 @@
//! Data types shared between the northbridge and southbridge for serial communications
#![no_std]
use nalgebra::Vector3;
use nalgebra::{SimdPartialOrd, Vector3};
use serde::{Deserialize, Serialize};
pub const BAUDRATE: u32 = 115200;
@ -20,9 +20,34 @@ pub enum Command {
Disable,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RobotState {
/// forward. clockwise
pub drive: (f32,f32),
/// RGB led brightness
pub underglow: Vector3<u8>,
}
impl RobotState {
/// limit exertion to factor of full output (0 to 1)
pub fn brownout(self, factor: f32) -> Self {
Self {
drive: (
self.drive.0.clamp(-factor, factor),
self.drive.1.clamp(-factor, factor),
),
underglow: self.underglow.simd_min(Vector3::repeat((factor * u8::MAX as f32) as u8))
}
}
/// Safe command to send in uncertian states
pub const fn stopped() -> Self {
Self { drive: (0.0, 0.0), underglow: Vector3::new(100, 0, 0) }
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Response {
pub enabled: bool,
pub sensor_data: Option<SensorData>,
pub uptime_micros: u64,
}
@ -37,4 +62,6 @@ pub enum SensorData {
Magnetometer(Vector3<f32>),
/// V
BusVoltage(f32),
/// C
AmbientTemperature(f32),
}

81
interface/Cargo.lock generated
View file

@ -74,6 +74,12 @@ version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "bytemuck"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]]
name = "byteorder"
version = "1.5.0"
@ -208,7 +214,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.98",
]
[[package]]
@ -332,6 +338,7 @@ dependencies = [
"common",
"futures",
"gilrs",
"nalgebra",
"postcard",
"serde",
"tokio",
@ -398,6 +405,16 @@ dependencies = [
"libc",
]
[[package]]
name = "matrixmultiply"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "memchr"
version = "2.7.4"
@ -431,6 +448,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20bd243ab3dbb395b39ee730402d2e5405e448c75133ec49cc977762c4cba3d1"
dependencies = [
"approx",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
@ -439,6 +458,17 @@ dependencies = [
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "nix"
version = "0.29.0"
@ -582,6 +612,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "redox_syscall"
version = "0.5.8"
@ -612,6 +648,15 @@ version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]]
name = "safe_arch"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
dependencies = [
"bytemuck",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -641,7 +686,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.98",
]
[[package]]
@ -663,6 +708,7 @@ dependencies = [
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]]
@ -705,6 +751,17 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.98"
@ -742,7 +799,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.98",
]
[[package]]
@ -797,7 +854,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn",
"syn 2.0.98",
"wasm-bindgen-shared",
]
@ -819,7 +876,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.98",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -843,6 +900,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "wide"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]]
name = "windows"
version = "0.59.0"
@ -874,7 +941,7 @@ checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.98",
]
[[package]]
@ -885,7 +952,7 @@ checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.98",
]
[[package]]

View file

@ -11,3 +11,4 @@ gilrs = "0.11.0"
futures = "0.3.31"
serde = { version = "1.0.217", features = ["alloc"] }
postcard = { version = "1.0.0", features = ["alloc", "use-std"] }
nalgebra = "0.31.2"

View file

@ -1,9 +1,10 @@
use std::time::Duration;
use anyhow::Context;
use common::Command;
use common::{RobotState, SensorData};
use gilrs::{Axis, Event, Gilrs};
use tokio::{io::{AsyncWriteExt, BufWriter}, net::TcpStream, time::sleep};
use nalgebra::Vector3;
use tokio::{io::{AsyncReadExt, AsyncWriteExt, BufWriter}, net::{tcp::OwnedReadHalf, TcpStream}, task::JoinHandle, time::{sleep, Instant}};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
@ -13,20 +14,27 @@ async fn main() -> anyhow::Result<()> {
let (_, gamepad) = gamepads.gamepads().nth(0).context("no gamepads")?;
let ly = gamepad.axis_code(Axis::LeftStickY).context("no left joystick")?;
let rx = gamepad.axis_code(Axis::RightStickX).context("no right joystick")?;
let lb = gamepad.button_code(gilrs::Button::LeftTrigger).context("no lb")?;
let a = gamepad.button_code(gilrs::Button::South).context("no a")?;
let mut last_a = false;
let mut robot = TcpStream::connect("pelican.dyn.wpi.edu:3322").await?;
let server = "pelican.dyn.wpi.edu:3322";
let mut robot = TcpStream::connect(server).await?;
robot.set_nodelay(true)?;
let (telem, robot_controller) = robot.into_split();
let (_telem, robot_controller) = robot.split();
tokio::spawn(print_telem(telem));
let mut robot_controller = BufWriter::new(robot_controller);
let mut active_gamepad = None;
let mut last_event = Instant::now();
loop {
while let Some(Event { id,..}) = gamepads.next_event() {
active_gamepad = Some(id);
last_event = Instant::now();
}
let Some(gamepad) = active_gamepad else {
@ -35,28 +43,75 @@ async fn main() -> anyhow::Result<()> {
let gamepad = gamepads.gamepad(gamepad);
let values = gamepad.state();
dbg!(values);
let mut ly= values.axis_data(ly).map(|d| d.value()).unwrap_or(0.0);
let mut rx= values.axis_data(rx).map(|d| d.value()).unwrap_or(0.0);
let boost = values.button_data(lb).map(|b| b.is_pressed()).unwrap_or(false);
let a = values.button_data(a).map(|b| b.is_pressed()).unwrap_or(false);
for axis in [&mut rx, &mut ly] {
if axis.abs() < 0.05 {
*axis = 0.0;
}
if !boost {
*axis *= 0.3;
}
}
let cmd = Command::Twist(ly, rx);
if !gamepad.is_connected() {
ly = 0.0;
rx = 0.0;
}
let black = Vector3::repeat(0u8);
let green = Vector3::new(254u8, 0, 254);
let color = if a {green} else {black};
let cmd = RobotState {
drive: (ly, rx),
underglow: color,
};
let encoded = postcard::to_stdvec(&cmd)?;
robot_controller.write_u32(encoded.len() as u32).await?;
robot_controller.write_all(&encoded).await?;
robot_controller.flush().await?;
sleep(Duration::from_micros(3500)).await;
if let Err(e) = robot_controller.flush().await {
println!("flush fail: {e}, reconnecting");
robot = TcpStream::connect(server).await?;
robot.set_nodelay(true)?;
let (telem, robot) = robot.into_split();
tokio::spawn(print_telem(telem));
robot_controller = BufWriter::new(robot);
};
sleep(Duration::from_millis(25)).await;
}
}
async fn print_telem(mut telem: OwnedReadHalf) -> anyhow::Result<()> {
loop {
let len = telem.read_u32().await? as usize;
let mut buf = vec![0; len];
telem.read_exact(&mut buf).await?;
let telem: SensorData = postcard::from_bytes(&buf)?;
match telem {
SensorData::BusVoltage(voltage) => {
println!("voltage: {voltage}");
}
SensorData::AmbientTemperature(temp) => {
println!("pico temp {}", temp * 9./5. + 32.);
}
_ => {}
}
}
}

57
northbridge/Cargo.lock generated
View file

@ -146,6 +146,62 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crossbeam"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "embedded-io"
version = "0.4.0"
@ -443,6 +499,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"common",
"crossbeam",
"framed",
"futures",
"futures-core",

View file

@ -15,3 +15,4 @@ nalgebra = { version = "0.31.2", default-features=false, features = ["serde-seri
tokio-util = { version = "0.7.13", features = ["codec"] }
futures-core = "0.3.31"
futures = "0.3.31"
crossbeam = "0.8.4"

View file

@ -1,3 +1,6 @@
server := 'pelican.dyn.wpi.edu'
deploy:
cross build --target=armv7-unknown-linux-gnueabihf --release
scp target/armv7-unknown-linux-gnueabihf/release/northbridge cruise@pelican.dyn.wpi.edu:
scp target/armv7-unknown-linux-gnueabihf/release/northbridge cruise@{{server}}:
ssh cruise@{{server}} systemctl --user restart nightstand

View file

@ -1,9 +1,9 @@
use std::result;
use anyhow::{anyhow, Ok};
use common::{Command, Response};
use common::{Response, RobotState};
use framed::{BoxPayload, FRAME_END_SYMBOL};
use tokio_util::{bytes::BytesMut, codec::{Decoder, Encoder}};
use tokio_util::{bytes::{Buf, BytesMut}, codec::{Decoder, Encoder}};
pub struct FramedCodec {
codec: framed::bytes::Codec,
@ -21,20 +21,40 @@ impl Decoder for FramedCodec {
type Error = anyhow::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
println!("len: {}", src.len());
if !src.contains(&0) {
return Ok(None);
}
let terminator_idx = src.iter().position(|b| *b == 0).unwrap();
let (message, remainder) = match postcard::take_from_bytes_cobs(src) {
result::Result::Ok(v) => v,
result::Result::Err(e) => {
println!("telem err: {e}");
// skip to the next terminator and try one more time
src.advance(terminator_idx+1);
if !src.contains(&0) {
println!("incomplete frame");
return Ok(None);
}
match postcard::take_from_bytes_cobs(src) {
result::Result::Ok(v) => v,
result::Result::Err(e) => {
src.clear();
Err(e)?
println!("telem err: {e}, cleared");
return Ok(None);
}
}
}
};
let remainder = remainder.to_vec();
src.clear();
if remainder.len() < 200 { // magic number
src.extend_from_slice(&remainder);
}
Ok(Some(message))
}
@ -51,10 +71,10 @@ impl Encoder<Vec<u8>> for FramedCodec {
}
}
impl Encoder<Command> for FramedCodec {
impl Encoder<RobotState> for FramedCodec {
type Error = anyhow::Error;
fn encode(&mut self, item: Command, dst: &mut BytesMut) -> Result<(), Self::Error> {
fn encode(&mut self, item: RobotState, dst: &mut BytesMut) -> Result<(), Self::Error> {
let data = postcard::to_stdvec(&item)?;
Ok(self.encode(data, dst).map_err(|e| anyhow!("encode fail: {e:?}"))?)
}

View file

@ -1,92 +1,123 @@
#![feature(async_closure)]
use std::time::Duration;
use std::{sync::Arc, time::Duration};
use anyhow::{Context, Result};
use common::{Command, Response, BAUDRATE};
use common::{Response, RobotState, SensorData, BAUDRATE};
use crossbeam::queue::ArrayQueue;
use framed_codec::FramedCodec;
use futures::{SinkExt, StreamExt};
use tokio::{io::AsyncReadExt, net::{TcpListener, TcpSocket}, sync::{broadcast, watch::{self, Sender}}, task::JoinHandle, time::timeout};
use futures::{FutureExt, SinkExt, StreamExt, TryStreamExt};
use nalgebra::{SimdPartialOrd, SimdValue, Vector3};
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpSocket}, sync::{broadcast::{self, error::RecvError, Receiver, Sender}, watch::{self, channel }, RwLock}, task::JoinHandle, time::{error::Elapsed, sleep, timeout}};
use tokio_serial::SerialPortBuilderExt;
use tokio_util::codec::Framed;
mod framed_codec;
#[derive(Default)]
struct RobotInfo {
bus_voltage: f32,
}
#[tokio::main]
async fn main() -> Result<()> {
let mut serial = tokio_serial::new("/dev/ttyAMA0", BAUDRATE).open_native_async()?;
let (sensor_sender, mut sensor_data) = broadcast::channel(5);
let (sensor_sender, sensor_data) = broadcast::channel(5);
serial.set_exclusive(false)?;
let (mut write, mut read) = Framed::new(serial, FramedCodec::new()).split();
let (send, commands) = watch::channel(Command::Stop);
let (sender, mut command_receiver) = channel(RobotState::stopped());
let _: JoinHandle<Result<()>> = tokio::spawn(async move {
loop {
println!("sensor {:?}", sensor_data.recv().await?);
}
});
let state = Arc::new(RwLock::new(RobotInfo {
bus_voltage: f32::MAX,
..Default::default()
}));
tokio::spawn(update_telem(state.clone(), sensor_data.resubscribe()));
let control_telem = sensor_data.resubscribe();
tokio::spawn(async move {
loop {
let _ = send.send(Command::Stop);
if let Err(e) = control(send.clone()).await {
if let Err(e) = control(sender.clone(), control_telem.resubscribe()).await {
println!("controller exited: {e}");
}
}
});
println!("starting");
write.send(Command::Enable).await?;
write.flush().await?;
tokio::spawn(async move {
let telem_sender = sensor_sender.clone();
read.for_each( async |telem| {
let Ok(telem) = telem else {return;};
let Some(data) = telem.sensor_data else {return;};
telem_sender.send(data).unwrap();
}).await;
});
loop {
let Some(Ok(data)) = read.next().await else {
continue;
};
if data.enabled {
write.send(Command::FeedWatchdog).await?;
write.flush().await?;
let _ = timeout(Duration::from_millis(20), command_receiver.changed());
let command = command_receiver.borrow();
} else {
println!("enabled southbridge");
write.send(Command::Enable).await?;
}
println!("sending {command:?}");
write.send(Command::Enable).await?;
write.flush().await?;
//dbg!(&data);
if let Some(data) = data.sensor_data {
let _ = sensor_sender.send(data);
}
write.send(dbg!(commands.borrow().clone())).await?;
write.flush().await?;
write.send(command.clone()).await?;
}
}
async fn control(sender: Sender<Command>) -> Result<()> {
async fn control(sender: watch::Sender<RobotState>, mut telem: Receiver<SensorData>) -> Result<()> {
let listener = TcpListener::bind("0.0.0.0:3322").await?;
let (mut stream, addr) = listener.accept().await?;
let (stream, addr) = listener.accept().await?;
println!("connected to {addr:?}");
let (mut read, mut write) = stream.into_split();
let _: JoinHandle<Result<()>> = tokio::spawn(async move {
loop {
let data = match telem.recv().await {
Ok(it) => it,
Err(RecvError::Lagged(_)) => {continue;},
Err(e) => {Err(e)?},
};
let data = postcard::to_stdvec(&data)?;
write.write_u32(data.len() as u32).await?;
write.write_all(&data).await?;
}
});
let len = timeout(Duration::from_millis(30), stream.read_u32()).await??;
loop {
let len = loop {
match timeout(Duration::from_millis(150), read.read_u32()).await {
Ok(it) => { break it?; },
Err(Elapsed) => {
sender.send(RobotState::stopped())?;
continue;
},
};
};
let mut buf = vec![0; len as usize];
timeout(Duration::from_millis(30), stream.read_exact(&mut buf)).await??;
timeout(Duration::from_millis(100), read.read_exact(&mut buf)).await??;
let cmd: Command = postcard::from_bytes(&buf)?;
println!("recv {cmd:?}");
let cmd: RobotState = postcard::from_bytes(&buf)?;
sender.send(cmd)?;
}
}
async fn update_telem(state: Arc<RwLock<RobotInfo>>, mut telem: Receiver<SensorData>) -> Result<()> {
loop {
let telem = telem.recv().await?;
println!("sensor {telem:?}");
match telem {
SensorData::BusVoltage(voltage) => {
state.write().await.bus_voltage = voltage;
}
_ => {}
}
}
}

267
southbridge/Cargo.lock generated
View file

@ -79,18 +79,18 @@ dependencies = [
[[package]]
name = "bit-set"
version = "0.5.3"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
[[package]]
name = "bitfield"
@ -110,6 +110,15 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bytemuck"
version = "1.21.0"
@ -184,6 +193,15 @@ dependencies = [
"syn 2.0.96",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc-any"
version = "2.5.0"
@ -211,6 +229,16 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "darling"
version = "0.20.10"
@ -246,6 +274,15 @@ dependencies = [
"syn 2.0.96",
]
[[package]]
name = "dcmimu"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692b1c8923315ac22103c1890512597baf543b95f2eebd66f572ffb1ff8f0170"
dependencies = [
"libm 0.1.4",
]
[[package]]
name = "debug-helper"
version = "0.3.13"
@ -253,10 +290,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
[[package]]
name = "diff"
version = "0.1.13"
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "dirs-next"
@ -297,10 +338,10 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "embassy-embedded-hal"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"embassy-futures",
"embassy-hal-internal",
"embassy-sync",
"embassy-time",
"embedded-hal 0.2.7",
@ -314,8 +355,7 @@ dependencies = [
[[package]]
name = "embassy-executor"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"cortex-m",
"critical-section",
@ -326,8 +366,7 @@ dependencies = [
[[package]]
name = "embassy-executor-macros"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"darling",
"proc-macro2",
@ -338,14 +377,12 @@ dependencies = [
[[package]]
name = "embassy-futures"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
[[package]]
name = "embassy-hal-internal"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef3bac31ec146321248a169e9c7b5799f1e0b3829c7a9b324cb4600a7438f59"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"cortex-m",
"critical-section",
@ -355,14 +392,12 @@ dependencies = [
[[package]]
name = "embassy-net-driver"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
[[package]]
name = "embassy-net-driver-channel"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4818c32afec43e3cae234f324bad9a976c9aa7501022d26ff60a4017a1a006b7"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"embassy-futures",
"embassy-net-driver",
@ -372,8 +407,7 @@ dependencies = [
[[package]]
name = "embassy-rp"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfe90d82ebbfed9de96d9edc6d63b6619c0977b570a3ab1724f76023e72773b"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"atomic-polyfill",
"cfg-if",
@ -412,8 +446,7 @@ dependencies = [
[[package]]
name = "embassy-sync"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"cfg-if",
"critical-section",
@ -426,8 +459,7 @@ dependencies = [
[[package]]
name = "embassy-time"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"cfg-if",
"critical-section",
@ -442,8 +474,7 @@ dependencies = [
[[package]]
name = "embassy-time-driver"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"document-features",
]
@ -451,8 +482,7 @@ dependencies = [
[[package]]
name = "embassy-time-queue-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"embassy-executor",
"heapless 0.8.0",
@ -461,8 +491,7 @@ dependencies = [
[[package]]
name = "embassy-usb"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e651b9b7b47b514e6e6d1940a6e2e300891a2c33641917130643602a0cb6386"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"embassy-futures",
"embassy-net-driver-channel",
@ -476,14 +505,12 @@ dependencies = [
[[package]]
name = "embassy-usb-driver"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
[[package]]
name = "embassy-usb-logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad40e3ec749d83f4b7ab0c0599c9bef0c51a73ce1c1087d8599ff2448d00c2aa"
source = "git+https://github.com/embassy-rs/embassy/#0ce6da9706b9d929691c9292c923117fcbc2f9c6"
dependencies = [
"embassy-futures",
"embassy-sync",
@ -644,6 +671,16 @@ dependencies = [
"pin-utils",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.15"
@ -722,12 +759,6 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "hermit-abi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "ident_case"
version = "1.0.1"
@ -744,55 +775,53 @@ dependencies = [
"hashbrown 0.15.2",
]
[[package]]
name = "is-terminal"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.5"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "lalrpop"
version = "0.19.12"
name = "keccak"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b"
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
dependencies = [
"cpufeatures",
]
[[package]]
name = "lalrpop"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e56f323e2d610628d1f5bdd39168a774674ac7989ed67011963bb3f71edd797"
dependencies = [
"ascii-canvas",
"bit-set",
"diff",
"ena",
"is-terminal",
"itertools",
"lalrpop-util",
"petgraph",
"pico-args",
"regex",
"regex-syntax 0.6.29",
"regex-syntax",
"sha3",
"string_cache",
"term",
"tiny-keccak",
"unicode-xid",
"walkdir",
]
[[package]]
name = "lalrpop-util"
version = "0.19.12"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed"
checksum = "108dc8f5dabad92c65a03523055577d847f5dcc00f3e7d3a68bc4d48e01d8fe1"
dependencies = [
"regex",
"regex-automata",
]
[[package]]
@ -801,6 +830,12 @@ version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libm"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
[[package]]
name = "libm"
version = "0.2.11"
@ -851,7 +886,7 @@ version = "0.1.6"
source = "git+https://git.ank.dev/ank/mpu6050#32604cc16c6a604fd0a4a35bf66bd9df800da18e"
dependencies = [
"embedded-hal 1.0.0",
"libm",
"libm 0.2.11",
"nalgebra",
]
@ -931,22 +966,22 @@ dependencies = [
[[package]]
name = "num_enum"
version = "0.5.11"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.11"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.96",
]
[[package]]
@ -1003,6 +1038,12 @@ dependencies = [
"siphasher",
]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
@ -1018,8 +1059,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pio"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3"
source = "git+https://github.com/rp-rs/pio-rs?rev=fa586448b0b223217eec8c92c19fe6823dd04cc4#fa586448b0b223217eec8c92c19fe6823dd04cc4"
dependencies = [
"arrayvec",
"num_enum",
@ -1029,30 +1069,26 @@ dependencies = [
[[package]]
name = "pio-parser"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77532c2b8279aef98dfc7207ef15298a5a3d6b6cc76ccc8b65913d69f3a8dd6b"
source = "git+https://github.com/rp-rs/pio-rs?rev=fa586448b0b223217eec8c92c19fe6823dd04cc4#fa586448b0b223217eec8c92c19fe6823dd04cc4"
dependencies = [
"lalrpop",
"lalrpop-util",
"pio",
"regex-syntax 0.6.29",
]
[[package]]
name = "pio-proc"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b04dc870fb3a4fd8b3e4ca8c61b53bc8ac4eb78b66805d2b3c2e5c4829e0d7a"
source = "git+https://github.com/rp-rs/pio-rs?rev=fa586448b0b223217eec8c92c19fe6823dd04cc4#fa586448b0b223217eec8c92c19fe6823dd04cc4"
dependencies = [
"codespan-reporting",
"lalrpop-util",
"pio",
"pio-parser",
"proc-macro-error",
"proc-macro-error2",
"proc-macro2",
"quote",
"regex-syntax 0.6.29",
"syn 1.0.109",
"syn 2.0.96",
]
[[package]]
@ -1082,27 +1118,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"version_check",
"syn 2.0.96",
]
[[package]]
@ -1164,7 +1198,7 @@ dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax 0.8.5",
"regex-syntax",
]
[[package]]
@ -1175,15 +1209,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.5",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.5"
@ -1248,6 +1276,15 @@ version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -1301,6 +1338,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9"
[[package]]
name = "sha3"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
dependencies = [
"digest",
"keccak",
]
[[package]]
name = "simba"
version = "0.7.3"
@ -1350,6 +1397,7 @@ dependencies = [
"common",
"cortex-m",
"cortex-m-rt",
"dcmimu",
"embassy-executor",
"embassy-rp",
"embassy-sync",
@ -1486,15 +1534,6 @@ dependencies = [
"syn 2.0.96",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "typenum"
version = "1.17.0"
@ -1593,6 +1632,16 @@ dependencies = [
"vcell",
]
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"

View file

@ -25,9 +25,18 @@ log = "0.4"
framed = {git = "https://git.ank.dev/ank/framed-rs", default-features=false, features = ["use_nightly"]}
common = { path = "../common" }
mpu6050 = { git = "https://git.ank.dev/ank/mpu6050" }
dcmimu = "0.2.2"
nalgebra = { version = "0.31.2", default-features=false, features = ["serde-serialize-no-std"] }
serde = { version = "1.0.203", default-features = false, features = ["derive"] }
postcard = "1.0.0"
static_cell = "2.1"
portable-atomic = { version = "1.5", features = ["critical-section"] }
heapless = "0.8"
[patch.crates-io]
# https://github.com/embassy-rs/embassy/pull/3763/
embassy-rp = { git = 'https://github.com/embassy-rs/embassy/' }
embassy-time = { git = 'https://github.com/embassy-rs/embassy/' }
embassy-usb-logger = { git = 'https://github.com/embassy-rs/embassy/' }
embassy-sync = { git = 'https://github.com/embassy-rs/embassy/' }
embassy-executor = { git = 'https://github.com/embassy-rs/embassy/' }

View file

@ -1,16 +1,16 @@
#![no_std]
#![no_main]
use core::{panic::PanicInfo, sync::atomic::Ordering};
use core::{panic::PanicInfo, sync::atomic::{AtomicU16, Ordering}};
use common::{Command, Response, SensorData, BAUDRATE};
use common::{Response, RobotState, SensorData, BAUDRATE};
use embassy_executor::Spawner;
use embassy_rp::{bind_interrupts, block::ImageDef, gpio::{Level, Output}, peripherals::{UART0, UART1, USB}, pwm::{self, Pwm}, uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, BufferedUartTx, Config}, usb::Driver};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, once_lock::OnceLock};
use embassy_rp::{adc::{self, Adc}, bind_interrupts, block::ImageDef, gpio::{Level, Output, Pull}, peripherals::{ADC, ADC_TEMP_SENSOR, PIN_28, UART0, UART1, USB}, pwm::{self, Pwm}, uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, BufferedUartTx, Config}, usb::Driver};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, mutex::Mutex, once_lock::OnceLock, pubsub::PubSubChannel, signal::Signal, watch::Watch};
use embassy_time::{with_deadline, with_timeout, Duration, Instant, Timer};
use embedded_io_async::{BufRead, Read, Write};
use log::{error, info, trace, warn};
use nalgebra::clamp;
use nalgebra::{clamp, Vector3};
use portable_atomic::AtomicBool;
use static_cell::{ConstStaticCell, StaticCell};
@ -18,18 +18,27 @@ bind_interrupts!(struct Irqs {
I2C1_IRQ => embassy_rp::i2c::InterruptHandler<embassy_rp::peripherals::I2C1>;
USBCTRL_IRQ => embassy_rp::usb::InterruptHandler<USB>;
UART1_IRQ => BufferedInterruptHandler<UART1>;
ADC_IRQ_FIFO => adc::InterruptHandler;
});
pub static COMMANDS: Channel<CriticalSectionRawMutex, Command, 5> = Channel::new();
pub static COMMANDS: Watch<CriticalSectionRawMutex, (RobotState, Instant), 3> = Watch::new();
pub static SENSOR_DATA: Channel<CriticalSectionRawMutex, SensorData, 5> = Channel::new();
pub static BUS_VOLTAGE: AtomicU16 = AtomicU16::new(u16::MAX);
const BUS_ADC_TO_VOLTS: f32 = 251.6763848397;
/// volts
fn bus_voltage() -> f32 {
BUS_VOLTAGE.load(Ordering::Acquire) as f32 / BUS_ADC_TO_VOLTS
}
#[link_section = ".start_block"]
#[used]
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
#[embassy_executor::task]
async fn logger_task(driver: Driver<'static, USB>) {
embassy_usb_logger::run!(1024, log::LevelFilter::Trace, driver);
embassy_usb_logger::run!(2048, log::LevelFilter::Trace, driver);
}
#[panic_handler]
@ -48,6 +57,7 @@ async fn main(spawner: Spawner) {
let driver = Driver::new(p.USB, Irqs);
spawner.spawn(logger_task(driver)).unwrap();
info!("logger enabled");
let mut drive_conf: pwm::Config = Default::default();
drive_conf.divider = 150.into();
@ -57,12 +67,33 @@ async fn main(spawner: Spawner) {
let stopped = drive_conf.clone();
let mut drive_pwm = Pwm::new_output_ab(p.PWM_SLICE0, p.PIN_16, p.PIN_17, stopped.clone());
let disabled_orange = Vector3::new(254, 100, 0);
let enabled_green = Vector3::new(0, 140, 0);
let mut light_conf_rg: pwm::Config = Default::default();
light_conf_rg.divider = 1.into();
light_conf_rg.top = 255;
light_conf_rg.compare_b = 0;
light_conf_rg.compare_a = 0;
let mut light_pwm_rg = Pwm::new_output_ab(p.PWM_SLICE7, p.PIN_14, p.PIN_15, light_conf_rg);
let mut light_conf_b: pwm::Config = Default::default();
light_conf_b.divider = 1.into();
light_conf_b.top = 255;
light_conf_b.compare_b = 0;
let mut light_pwm_b = Pwm::new_output_b(p.PWM_SLICE6, p.PIN_13, light_conf_b);
set_underglow(&mut light_pwm_rg, &mut light_pwm_b, disabled_orange.clone());
Timer::after_secs(13).await; // pi 0 serial ports act strange during the boot process
spawner.spawn(bus_voltage_monitor(p.ADC, p.PIN_28, p.ADC_TEMP_SENSOR)).unwrap();
let config = embassy_rp::i2c::Config::default();
let bus = embassy_rp::i2c::I2c::new_async(p.I2C1, p.PIN_19, p.PIN_18, Irqs, config);
static TX_BUF: ConstStaticCell<[u8; 1024]> = ConstStaticCell::new([0u8;1024]);
static TX_BUF: ConstStaticCell<[u8; 2048]> = ConstStaticCell::new([0u8;2048]);
let tx_buf = TX_BUF.take();
static RX_BUF: ConstStaticCell<[u8; 1024]> = ConstStaticCell::new([0u8;1024]);
static RX_BUF: ConstStaticCell<[u8; 2048]> = ConstStaticCell::new([0u8;2048]);
let rx_buf = RX_BUF.take();
let mut uart_config = Config::default();
@ -73,58 +104,41 @@ async fn main(spawner: Spawner) {
spawner.spawn(decoder(rx)).unwrap();
static STARTUP: OnceLock<()> = OnceLock::new();
spawner.spawn(telemetry(tx)).unwrap();
static ENABLED: AtomicBool = AtomicBool::new(false);
spawner.spawn(telemetry(tx, &ENABLED, &STARTUP)).unwrap();
let enabled = &ENABLED;
let mut enable_watchdog = Instant::now();
let enable_watchdog_time = Duration::from_millis(150);
let watchdog_time = Duration::from_millis(150);
info!("ready");
let mut commands = COMMANDS.receiver().unwrap();
loop {
let command = if enabled.load(Ordering::Acquire) {
let Ok(command) = with_deadline(enable_watchdog + enable_watchdog_time, COMMANDS.receive()).await else {
warn!("no message received");
enabled.store(false, Ordering::Release);
continue;
};
command
} else {
// stop all motors
let motor_update = Timer::after_millis(3);
let (command, time) = commands.get().await;
if time.elapsed() > watchdog_time {
drive_pwm.set_config(&stopped);
info!("waiting for command");
COMMANDS.receive().await
};
set_underglow(&mut light_pwm_rg, &mut light_pwm_b, disabled_orange.clone());
commands.changed().await;
set_underglow(&mut light_pwm_rg, &mut light_pwm_b, enabled_green.clone());
continue;
}
STARTUP.get_or_init(||());
match command {
Command::Twist(forward, right) => {
// 100% effort at 9.5v, 0% effort at 7.5v
let brownout = ((bus_voltage()-7.5)/2.0).clamp(0., 1.);
let command = command.clone().brownout(brownout);
// drive motors
let (forward, right) = command.drive;
drive_conf.compare_a = calc_speed(-forward - right);
drive_conf.compare_b = calc_speed(forward - right);
drive_pwm.set_config(&drive_conf);
},
Command::Stop => {
drive_pwm.set_config(&stopped);
},
Command::Enable => {
enabled.store(true, Ordering::Release);
enable_watchdog = Instant::now();
},
Command::Disable => {
enabled.store(false, Ordering::Release);
drive_pwm.set_config(&stopped);
},
Command::FeedWatchdog => {
enable_watchdog = Instant::now();
}
c => { error!("{c:?} unimplemented") }
};
info!("Blink");
// underglow
set_underglow(&mut light_pwm_rg, &mut light_pwm_b, command.underglow);
motor_update.await;
}
}
@ -132,6 +146,7 @@ async fn main(spawner: Spawner) {
#[embassy_executor::task]
async fn decoder(mut rx: BufferedUartRx<'static, UART1>) {
info!("Reading...");
let sender = COMMANDS.sender();
loop {
let mut len = [0;4];
@ -139,30 +154,31 @@ async fn decoder(mut rx: BufferedUartRx<'static, UART1>) {
let len = u32::from_be_bytes(len) as usize;
let mut buf = [0;1024];
rx.read_exact(&mut buf[..len]).await.unwrap();
if let Err(e) = rx.read_exact(&mut buf[..len]).await {
trace!("read fail {e:?}");
};
let Ok(data) = postcard::from_bytes::<Command>(&buf[..len]) else {
let Ok(data) = postcard::from_bytes::<RobotState>(&buf[..len]) else {
error!("message decode fail");
continue;
};
trace!("received {data:?}");
COMMANDS.send(data).await;
sender.send((data, Instant::now()));
}
}
/// Receive data from channel and send it over UART
#[embassy_executor::task]
async fn telemetry(mut tx: BufferedUartTx<'static, UART1>, enabled: &'static AtomicBool, startup: &'static OnceLock<()>) {
async fn telemetry(mut tx: BufferedUartTx<'static, UART1>) {
info!("Telemetry waiting...");
startup.get().await;
COMMANDS.receiver().unwrap().get().await;
info!("Telemetry started...");
loop {
let data = with_timeout(Duration::from_millis(20), SENSOR_DATA.receive()).await.ok();
let packet = Response {
enabled: enabled.load(Ordering::Acquire),
sensor_data: data,
uptime_micros: Instant::now().as_micros(),
};
@ -175,7 +191,9 @@ async fn telemetry(mut tx: BufferedUartTx<'static, UART1>, enabled: &'static Ato
if let Err(e) = tx.write_all(&serialized).await {
error!("transport error {e:?}");
}
tx.flush().await.unwrap();
if let Err(e) = tx.flush().await {
error!("flush err {e:?}");
}
}
}
@ -188,3 +206,50 @@ fn calc_speed(speed: f32) -> u16 {
(COUNTS_PER_MS * ms) as u16
}
#[embassy_executor::task]
async fn bus_voltage_monitor(adc: ADC, bus: PIN_28, temp: ADC_TEMP_SENSOR) {
let mut adc = Adc::new(adc, Irqs, adc::Config::default());
let mut bus_voltage = adc::Channel::new_pin(bus, Pull::None);
let mut ts = adc::Channel::new_temp_sensor(temp);
// from embassy examples
fn convert_to_celsius(raw_temp: u16) -> f32 {
let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;
let sign = if temp < 0.0 { -1.0 } else { 1.0 };
let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;
(rounded_temp_x10 as f32) / 10.0
}
let mut i = 0;
loop {
i += 1;
let level = adc.read(&mut bus_voltage).await.unwrap();
BUS_VOLTAGE.store(level, Ordering::Release);
// empirically calculated against $20 microcenter voltmeter (10k & 33k divider circuit)
let temp = adc.read(&mut ts).await.unwrap();
if i % 10 == 0 { // only send telemetry every 30 milliseconds
SENSOR_DATA.send(SensorData::BusVoltage(level as f32 / BUS_ADC_TO_VOLTS)).await;
SENSOR_DATA.send(SensorData::AmbientTemperature(convert_to_celsius(temp))).await;
}
Timer::after_millis(3).await;
}
}
fn set_underglow(rg: &mut Pwm, b: &mut Pwm, color: Vector3<u8>) {
let mut light_conf_rg: pwm::Config = Default::default();
light_conf_rg.divider = 1.into();
light_conf_rg.top = 255;
light_conf_rg.compare_b = color[1] as u16;
light_conf_rg.compare_a = color[0] as u16;
rg.set_config(&light_conf_rg);
let mut light_conf_b: pwm::Config = Default::default();
light_conf_b.divider = 1.into();
light_conf_b.top = 255;
light_conf_b.compare_b = color[2] as u16;
b.set_config(&light_conf_b);
}