1
Fork 0

Compare commits

...

5 commits

Author SHA1 Message Date
17553cf2a3
thingy drives
added deadzone to gamepad controller

mended drive polarity

wait to start telem until awakened by northbridge
(linux doesn't boot in 300 cycles)
2025-02-04 15:48:14 -05:00
1c268bba12
converted southbridge -> northbridge to length-data
also divided the pwm clock by ten to align with microseconds
2025-02-04 13:54:01 -05:00
a9573dc8bc
switched northbridge -> southbridge communication to length-data
buffereduart peek does not appear to work, and
coms are very stable with shielded wire
2025-02-04 13:06:38 -05:00
76ba58de69
poll gamepad state 2025-02-04 13:04:42 -05:00
a7efcab3ce
updated cargo cross config
encoder builds for now (not pinning image)
2025-02-03 14:37:27 -05:00
7 changed files with 99 additions and 78 deletions

2
encoder/Cross.toml Normal file
View file

@ -0,0 +1,2 @@
[target.armv7-unknown-linux-gnueabihf]
image = "ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf:main"

View file

@ -2,14 +2,14 @@ use std::time::Duration;
use anyhow::Context; use anyhow::Context;
use common::Command; use common::Command;
use gilrs::{Axis, Gilrs}; use gilrs::{Axis, Event, Gilrs};
use tokio::{io::{AsyncWriteExt, BufWriter}, net::TcpStream, time::sleep}; use tokio::{io::{AsyncWriteExt, BufWriter}, net::TcpStream, time::sleep};
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
println!("Hello, world!"); println!("Hello, world!");
let gamepads = Gilrs::new().unwrap(); let mut gamepads = Gilrs::new().unwrap();
let (_, gamepad) = gamepads.gamepads().nth(0).context("no gamepads")?; let (_, gamepad) = gamepads.gamepads().nth(0).context("no gamepads")?;
let ly = gamepad.axis_code(Axis::LeftStickY).context("no left joystick")?; let ly = gamepad.axis_code(Axis::LeftStickY).context("no left joystick")?;
let rx = gamepad.axis_code(Axis::RightStickX).context("no right joystick")?; let rx = gamepad.axis_code(Axis::RightStickX).context("no right joystick")?;
@ -22,11 +22,32 @@ async fn main() -> anyhow::Result<()> {
let mut robot_controller = BufWriter::new(robot_controller); let mut robot_controller = BufWriter::new(robot_controller);
loop { let mut active_gamepad = None;
let values = gamepad.state();
let ly = values.axis_data(ly).context("no left")?.value(); loop {
let rx = values.axis_data(rx).context("no left")?.value(); while let Some(Event { id,..}) = gamepads.next_event() {
active_gamepad = Some(id);
}
let Some(gamepad) = active_gamepad else {
continue;
};
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);
for axis in [&mut rx, &mut ly] {
if axis.abs() < 0.05 {
*axis = 0.0;
}
}
let cmd = Command::Twist(ly, rx); let cmd = Command::Twist(ly, rx);
@ -36,7 +57,6 @@ async fn main() -> anyhow::Result<()> {
robot_controller.write_all(&encoded).await?; robot_controller.write_all(&encoded).await?;
robot_controller.flush().await?; robot_controller.flush().await?;
sleep(Duration::from_micros(3500)).await; sleep(Duration::from_micros(3500)).await;
} }
} }

View file

@ -1,3 +1,3 @@
deploy: deploy:
cross build --target=armv7-unknown-linux-gnueabihf cross build --target=armv7-unknown-linux-gnueabihf --release
scp target/armv7-unknown-linux-gnueabihf/debug/northbridge cruise@pelican.dyn.wpi.edu: scp target/armv7-unknown-linux-gnueabihf/release/northbridge cruise@pelican.dyn.wpi.edu:

View file

@ -1,4 +1,4 @@
use anyhow::anyhow; use anyhow::{anyhow, Ok};
use common::Command; use common::Command;
use framed::{BoxPayload, FRAME_END_SYMBOL}; use framed::{BoxPayload, FRAME_END_SYMBOL};
use tokio_util::{bytes::BytesMut, codec::{Decoder, Encoder}}; use tokio_util::{bytes::BytesMut, codec::{Decoder, Encoder}};
@ -15,31 +15,35 @@ impl FramedCodec {
} }
impl Decoder for FramedCodec { impl Decoder for FramedCodec {
type Item = BoxPayload; type Item = Vec<u8>;
type Error = framed::Error; type Error = anyhow::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if src.len() < 4 {
match src.iter().position(|e| *e==FRAME_END_SYMBOL) { return Ok(None);
Some(len) => {
let data = src.split_to(len);
Ok(Some(self.codec.decode_to_box(&data)?))
}
None => {
Ok(None)
}
} }
let len = u32::from_be_bytes(src[..4].try_into()?) as usize;
if src.len() < len + 4 {
return Ok(None);
}
let data = src.split_to(len + 4);
let mut out = Vec::new();
out.extend_from_slice(&data[4..]);
Ok(Some(out))
} }
} }
impl Encoder<Vec<u8>> for FramedCodec { impl Encoder<Vec<u8>> for FramedCodec {
type Error = framed::Error; type Error = anyhow::Error;
fn encode(&mut self, item: Vec<u8>, dst: &mut BytesMut) -> Result<(), Self::Error> { fn encode(&mut self, item: Vec<u8>, dst: &mut BytesMut) -> Result<(), Self::Error> {
let encoded = self.codec.encode_to_box(&item)?; dst.extend_from_slice(&item.len().to_be_bytes());
dst.extend_from_slice(&item);
dst.extend_from_slice(&encoded);
Ok(()) Ok(())
} }

View file

@ -30,26 +30,33 @@ async fn main() -> Result<()> {
println!("starting"); println!("starting");
write.send(Command::Enable).await?; write.send(Command::Enable).await?;
write.flush().await?;
while let Some(Ok(data)) = read.next().await { loop {
let Some(Ok(data)) = read.next().await else {
continue;
};
if data.enabled { if data.enabled {
write.send(Command::FeedWatchdog).await?; write.send(Command::FeedWatchdog).await?;
write.flush().await?;
} else { } else {
println!("enabled southbridge"); println!("enabled southbridge");
write.send(Command::Enable).await?; write.send(Command::Enable).await?;
} }
dbg!(&data); write.send(Command::Enable).await?;
write.flush().await?;
//dbg!(&data);
if let Some(data) = data.sensor_data { if let Some(data) = data.sensor_data {
let _ = sensor_sender.send(data); let _ = sensor_sender.send(data);
} }
write.send(commands.borrow().clone()).await?; write.send(dbg!(commands.borrow().clone())).await?;
write.flush().await?;
} }
Ok(())
} }
async fn control(sender: Sender<Command>) -> Result<()> { async fn control(sender: Sender<Command>) -> Result<()> {

View file

@ -13,7 +13,7 @@ embedded-storage = "0.3.1"
cortex-m-rt = "0.7.3" cortex-m-rt = "0.7.3"
embassy-executor = { version = "0.7", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-executor = { version = "0.7", features = ["arch-cortex-m", "executor-thread", "task-arena-size-32768"] }
embassy-sync = { version = "0.6" } embassy-sync = { version = "0.6" }
embassy-time = { version = "0.4" } embassy-time = { version = "0.4" }
cortex-m = { version = "0.7.6" } cortex-m = { version = "0.7.6" }

View file

@ -6,11 +6,9 @@ use core::{panic::PanicInfo, sync::atomic::Ordering};
use common::{Command, Response, SensorData, BAUDRATE}; use common::{Command, Response, SensorData, BAUDRATE};
use embassy_executor::Spawner; 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_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}; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, once_lock::OnceLock};
use embassy_time::{with_deadline, with_timeout, Duration, Instant, Timer}; use embassy_time::{with_deadline, with_timeout, Duration, Instant, Timer};
use embedded_io::Write; use embedded_io_async::{BufRead, Read, Write};
use embedded_io_async::{BufRead, Read};
use framed::{bytes, FRAME_END_SYMBOL};
use log::{error, info, trace, warn}; use log::{error, info, trace, warn};
use nalgebra::clamp; use nalgebra::clamp;
use portable_atomic::AtomicBool; use portable_atomic::AtomicBool;
@ -52,12 +50,12 @@ async fn main(spawner: Spawner) {
spawner.spawn(logger_task(driver)).unwrap(); spawner.spawn(logger_task(driver)).unwrap();
let mut drive_conf: pwm::Config = Default::default(); let mut drive_conf: pwm::Config = Default::default();
drive_conf.divider = 15.into(); drive_conf.divider = 150.into();
drive_conf.top = 50000; // 5ms drive_conf.top = 20000; // 20ms
drive_conf.compare_b = 15000; // 1.5ms drive_conf.compare_b = 1500; // 1.5ms
drive_conf.compare_a = 15000; // 1.5ms drive_conf.compare_a = 1500; // 1.5ms
let stopped = drive_conf.clone(); let stopped = drive_conf.clone();
let mut drive_pwm = Pwm::new_output_ab(p.PWM_SLICE0, p.PIN_16, p.PIN_17, drive_conf.clone()); let mut drive_pwm = Pwm::new_output_ab(p.PWM_SLICE0, p.PIN_16, p.PIN_17, stopped.clone());
let config = embassy_rp::i2c::Config::default(); 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); let bus = embassy_rp::i2c::I2c::new_async(p.I2C1, p.PIN_19, p.PIN_18, Irqs, config);
@ -75,24 +73,18 @@ async fn main(spawner: Spawner) {
spawner.spawn(decoder(rx)).unwrap(); spawner.spawn(decoder(rx)).unwrap();
static STARTUP: OnceLock<()> = OnceLock::new();
static ENABLED: AtomicBool = AtomicBool::new(false); static ENABLED: AtomicBool = AtomicBool::new(false);
spawner.spawn(telemetry(tx, &ENABLED)).unwrap(); spawner.spawn(telemetry(tx, &ENABLED, &STARTUP)).unwrap();
let enabled = &ENABLED; let enabled = &ENABLED;
let mut enable_watchdog = Instant::now(); let mut enable_watchdog = Instant::now();
let enable_watchdog_time = Duration::from_millis(50); let enable_watchdog_time = Duration::from_millis(150);
Timer::after_secs(1).await;
info!("ready"); info!("ready");
Timer::after_secs(1).await;
loop { loop {
if !enabled.load(Ordering::Acquire) {
// stop all motors
drive_pwm.set_config(&stopped);
}
let command = if enabled.load(Ordering::Acquire) { let command = if enabled.load(Ordering::Acquire) {
let Ok(command) = with_deadline(enable_watchdog + enable_watchdog_time, COMMANDS.receive()).await else { let Ok(command) = with_deadline(enable_watchdog + enable_watchdog_time, COMMANDS.receive()).await else {
warn!("no message received"); warn!("no message received");
@ -101,14 +93,18 @@ async fn main(spawner: Spawner) {
}; };
command command
} else { } else {
// stop all motors
//drive_pwm.set_config(&stopped);
info!("waiting for command"); info!("waiting for command");
COMMANDS.receive().await COMMANDS.receive().await
}; };
STARTUP.get_or_init(||());
match command { match command {
Command::Twist(forward, right) => { Command::Twist(forward, right) => {
drive_conf.compare_a = calc_speed(forward - right); drive_conf.compare_a = calc_speed(-forward - right);
drive_conf.compare_b = calc_speed(forward + right); drive_conf.compare_b = calc_speed(forward - right);
drive_pwm.set_config(&drive_conf); drive_pwm.set_config(&drive_conf);
}, },
Command::Stop => { Command::Stop => {
@ -129,7 +125,6 @@ async fn main(spawner: Spawner) {
}; };
info!("Blink"); info!("Blink");
Timer::after(Duration::from_millis(100)).await;
} }
} }
@ -137,18 +132,14 @@ async fn main(spawner: Spawner) {
#[embassy_executor::task] #[embassy_executor::task]
async fn decoder(mut rx: BufferedUartRx<'static, UART1>) { async fn decoder(mut rx: BufferedUartRx<'static, UART1>) {
info!("Reading..."); info!("Reading...");
let mut codec = framed::bytes::Config::default().to_codec();
loop { loop {
let data = rx.fill_buf().await.unwrap(); let mut len = [0;4];
let Some(end) = data.iter().position(|e| *e == FRAME_END_SYMBOL) else { continue; }; rx.read_exact(&mut len).await.unwrap();
let data = &data[..end]; let len = u32::from_be_bytes(len) as usize;
let mut buf = [0u8; 1024]; let mut buf = [0;1024];
let Ok(len) = codec.decode_to_slice(data, &mut buf) else { rx.read_exact(&mut buf[..len]).await.unwrap();
error!("frame decode fail");
continue;
};
let Ok(data) = postcard::from_bytes::<Command>(&buf[..len]) else { let Ok(data) = postcard::from_bytes::<Command>(&buf[..len]) else {
error!("message decode fail"); error!("message decode fail");
@ -158,17 +149,15 @@ async fn decoder(mut rx: BufferedUartRx<'static, UART1>) {
trace!("received {data:?}"); trace!("received {data:?}");
COMMANDS.send(data).await; COMMANDS.send(data).await;
rx.consume(end);
} }
} }
/// Receive data from channel and send it over UART /// Receive data from channel and send it over UART
#[embassy_executor::task] #[embassy_executor::task]
async fn telemetry(mut tx: BufferedUartTx<'static, UART1>, enabled: &'static AtomicBool) { async fn telemetry(mut tx: BufferedUartTx<'static, UART1>, enabled: &'static AtomicBool, startup: &'static OnceLock<()>) {
info!("Reading..."); info!("Telemetry waiting...");
let mut codec = framed::bytes::Config::default().to_codec(); startup.get().await;
info!("Telemetry started...");
loop { loop {
let data = with_timeout(Duration::from_millis(20), SENSOR_DATA.receive()).await.ok(); let data = with_timeout(Duration::from_millis(20), SENSOR_DATA.receive()).await.ok();
@ -183,21 +172,20 @@ async fn telemetry(mut tx: BufferedUartTx<'static, UART1>, enabled: &'static Ato
continue; continue;
}; };
let mut buf = [0u8; 1024]; let len = u32::to_be_bytes(serialized.len() as u32);
let Ok(len) = codec.encode_to_slice(&serialized, &mut buf) else { if let Err(e) = tx.write_all(&len).await {
error!("encoding error");
continue;
};
if let Err(e) = tx.write_all(&buf[..len]) {
error!("transport error {e:?}"); error!("transport error {e:?}");
} }
if let Err(e) = tx.write_all(&serialized).await {
error!("transport error {e:?}");
}
tx.flush().await.unwrap();
} }
} }
/// -1 to 1 /// -1 to 1
fn calc_speed(speed: f32) -> u16 { fn calc_speed(speed: f32) -> u16 {
const COUNTS_PER_MS: f32 = 10000.; const COUNTS_PER_MS: f32 = 1000.;
let speed = speed.clamp(-1., 1.); let speed = speed.clamp(-1., 1.);
let ms = (speed/2.0)+1.5; let ms = (speed/2.0)+1.5;