From bc557ccdebaf6cccd7b4bc01bc3713627cff33fd Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 3 Oct 2024 16:06:17 +0200 Subject: [PATCH 1/6] Added crate for usb-serial protocol --- Cargo.lock | 34 ++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + protocol/Cargo.toml | 10 ++++++++++ protocol/src/lib.rs | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 protocol/Cargo.toml create mode 100644 protocol/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 67e6a78..b8099aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.6.1" @@ -482,12 +488,32 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "serde", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -994,6 +1020,14 @@ dependencies = [ "xca9548a", ] +[[package]] +name = "radomctl-protocol" +version = "0.1.0" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "radomctld" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index fe5402c..ed7d23d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ resolver = "2" members = [ "daemon", "firmware", + "protocol", ] diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml new file mode 100644 index 0000000..201707b --- /dev/null +++ b/protocol/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "radomctl-protocol" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = {version = "1.0.193", default-features = false, features = ["derive"]} +heapless = {version = "0.8.0", features = ["serde"]} \ No newline at end of file diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs new file mode 100644 index 0000000..4793873 --- /dev/null +++ b/protocol/src/lib.rs @@ -0,0 +1,37 @@ +#![no_std] +use heapless::Vec; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum RadomMessage { + Status(StatusMessage), +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum HostMessage { + RequestStatus, + TriggerDFUBootloader, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct StatusMessage { + position: Position, + alarms: Vec, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct Position { + az: f32, + el: f32, + az_endcoder: f32, + el_encoder: f32, + az_magnetic: f32, + el_magnetic: f32, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub enum Alarm { + AZEncoderFault, + ELEncoderFault, +} From 8cf75ac70dbf17a5f31797a7fd3fe6cacca42cea Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 3 Oct 2024 18:22:37 +0200 Subject: [PATCH 2/6] Added usb serial communication --- Cargo.lock | 120 +++++++++++++++++++++++++++++++++++++++++-- firmware/Cargo.toml | 8 ++- firmware/src/main.rs | 118 ++++++++++++++++++++++++++++++++++++++++-- protocol/src/lib.rs | 16 +++--- 4 files changed, 245 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8099aa..3a5c99c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,7 +146,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" dependencies = [ - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -197,6 +197,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "colored" version = "1.9.4" @@ -361,6 +367,12 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "embedded-storage" version = "0.3.1" @@ -488,6 +500,15 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hash32" version = "0.3.1" @@ -503,13 +524,28 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32 0.2.1", + "rustc_version 0.4.1", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "heapless" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "hash32", + "defmt", + "hash32 0.3.1", "serde", "stable_deref_trait", ] @@ -941,6 +977,18 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +[[package]] +name = "postcard" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +dependencies = [ + "cobs", + "defmt", + "heapless 0.7.17", + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1008,15 +1056,20 @@ dependencies = [ "defmt-brtt", "defmt-rtt", "embedded-hal 1.0.0", + "heapless 0.8.0", "nb 1.1.0", "num", "num-traits", "panic-probe", + "postcard", "qmc5883l", + "radomctl-protocol", "rtic", "rtic-monotonics", "stm32f4xx-hal", "ufmt", + "usb-device", + "usbd-serial", "xca9548a", ] @@ -1024,7 +1077,7 @@ dependencies = [ name = "radomctl-protocol" version = "0.1.0" dependencies = [ - "heapless", + "heapless 0.8.0", "serde", ] @@ -1142,7 +1195,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.23", ] [[package]] @@ -1172,6 +1234,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "semver-parser" version = "0.7.0" @@ -1265,6 +1333,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1304,6 +1381,7 @@ dependencies = [ "nb 1.1.0", "rand_core", "stm32f4", + "synopsys-usb-otg", "time", "vcell", "void", @@ -1343,6 +1421,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "synopsys-usb-otg" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e948d523b316939545d8b21a48c27aef150ce25321b9f95ff7978647a806a6fe" +dependencies = [ + "cortex-m", + "embedded-hal 0.2.7", + "usb-device", + "vcell", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -1597,6 +1687,28 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless 0.8.0", + "portable-atomic", +] + +[[package]] +name = "usbd-serial" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065e4eaf93db81d5adac82d9cef8f8da314cb640fa7f89534b972383f1cf80fc" +dependencies = [ + "embedded-hal 0.2.7", + "embedded-io", + "nb 1.1.0", + "usb-device", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index b6f5d64..ccfe594 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -10,7 +10,7 @@ defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] } panic-probe = { version = "0.3", features = ["print-defmt"] } rtic = { version = "2.0.1", features = [ "thumbv7-backend" ] } defmt-rtt = "0.4" -stm32f4xx-hal = { version = "0.21.0", features = ["stm32f401"] } +stm32f4xx-hal = { version = "0.21.0", features = ["stm32f401", "usb_fs"] } embedded-hal = {version = "1.0.0"} nb = "1.0.0" num-traits = { version = "0.2", default-features = false, features = ["libm"] } @@ -20,6 +20,12 @@ qmc5883l = "0.0.1" rtic-monotonics = {version = "2.0.2", features = ["cortex-m-systick"]} xca9548a = "0.2.1" as5048a = { git = "https://github.com/LongHairedHacker/as5048a", rev="b15d716bf47ce4975a6cefebf82006c9b09e8fea"} +usb-device = "0.3.2" +usbd-serial = "0.2.2" +postcard = {version = "1.0.10", features = ["use-defmt"]} +heapless = {version = "0.8.0", features = ["defmt-03"]} +radomctl-protocol = { path = "../protocol" } + [features] # set logging levels here diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 90cb8e0..40e8ceb 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -21,13 +21,19 @@ mod app { use as5048a::AS5048A; + use heapless::Vec; + use postcard::{from_bytes_cobs, to_vec_cobs}; use stm32f4xx_hal::{ gpio::{gpioa, gpiob, gpioc, Output, PushPull}, i2c, + otg_fs::{UsbBus, UsbBusType, USB}, pac::{I2C1, SPI1}, prelude::*, spi, }; + use usb_device::class_prelude::UsbBusAllocator; + use usb_device::prelude::*; + use usbd_serial::SerialPort; use num_traits::{Float, FloatConst}; @@ -35,10 +41,14 @@ mod app { use qmc5883l::{self, QMC5883L}; + use radomctl_protocol::*; + use rtic_monotonics::systick::prelude::*; systick_monotonic!(Mono, 4000); + const USB_BUFFER_SIZE: usize = 64; + // Shared resources go here #[shared] struct Shared { @@ -58,13 +68,17 @@ mod app { spi_cs3: gpiob::PB15>, spi1: spi::Spi, - az_enable: gpioa::PA12>, + az_enable: gpioa::PA10>, az_dir: gpioa::PA15>, az_step: gpiob::PB3>, el_enable: gpiob::PB4>, el_dir: gpioa::PA8>, el_step: gpioa::PA9>, + + usb_dev: UsbDevice<'static, UsbBusType>, + usb_serial: SerialPort<'static, UsbBusType>, + usb_buffer: Vec, } #[init] @@ -89,8 +103,6 @@ mod app { defmt::info!("Clock Setup done"); - defmt::info!("Clock Setup done"); - // Acquire the GPIO peripherials let gpioa = cx.device.GPIOA.split(); let gpiob = cx.device.GPIOB.split(); @@ -98,6 +110,40 @@ mod app { let board_led = gpioc.pc13.into_push_pull_output(); + defmt::info!("Basic gpio setup done"); + + static mut EP_MEMORY: [u32; 1024] = [0; 1024]; + static mut USB_BUS: Option> = None; + + let usb = USB::new( + ( + cx.device.OTG_FS_GLOBAL, + cx.device.OTG_FS_DEVICE, + cx.device.OTG_FS_PWRCLK, + ), + (gpioa.pa11, gpioa.pa12), + &clocks, + ); + + unsafe { + USB_BUS.replace(UsbBus::new(usb, &mut EP_MEMORY)); + } + + let usb_serial = usbd_serial::SerialPort::new(unsafe { USB_BUS.as_ref().unwrap() }); + + let usb_dev = UsbDeviceBuilder::new( + unsafe { USB_BUS.as_ref().unwrap() }, + UsbVidPid(0x16c0, 0x27dd), + ) + .device_class(usbd_serial::USB_CLASS_CDC) + .strings(&[StringDescriptors::default() + .manufacturer("Amteurfunk Forschungs Gruppe") + .product("Radom Controler")]) + .unwrap() + .build(); + + defmt::info!("USB Setup done"); + // Todo: Check if internal pullups work here let scl = gpiob.pb6.into_alternate_open_drain(); let sda = gpiob.pb7.into_alternate_open_drain(); @@ -142,7 +188,7 @@ mod app { defmt::info!("SPI Setup done"); - let mut az_enable = gpioa.pa12.into_push_pull_output(); + let mut az_enable = gpioa.pa10.into_push_pull_output(); az_enable.set_high(); let az_dir = gpioa.pa15.into_push_pull_output(); let az_step = gpiob.pb3.into_push_pull_output(); @@ -179,6 +225,10 @@ mod app { el_enable, el_dir, el_step, + + usb_dev, + usb_serial, + usb_buffer: Vec::new(), }, ) } @@ -317,4 +367,64 @@ mod app { } } } + + #[task(binds=OTG_FS, local=[usb_dev, usb_serial, usb_buffer])] + fn usb_fs(cx: usb_fs::Context) { + let usb_dev = cx.local.usb_dev; + let serial = cx.local.usb_serial; + let buffer = cx.local.usb_buffer; + + if !usb_dev.poll(&mut [serial]) { + return; + } + + let mut tmp = [0u8; 16]; + match serial.read(&mut tmp) { + Ok(count) if count > 0 => { + if buffer.extend_from_slice(&tmp[0..count]).is_err() { + buffer.clear(); + defmt::error!("Buffer overflow while waiting for the end of the packet"); + } + } + _ => {} + } + + loop { + if let Some(idx) = buffer.iter().position(|&x| x == 0) { + let (msg, rest) = buffer.split_at(idx + 1); + + let mut message = [0u8; 128]; + message[0..msg.len()].clone_from_slice(msg); + let host_msg = from_bytes_cobs::(&mut message); + + match host_msg { + Ok(host_msg) => match host_msg { + HostMessage::RequestStatus => { + let status = StatusMessage { + position: Position { + az: 42.0, + el: 23.0, + az_endcoder: 0.0, + el_encoder: 0.0, + az_magnetic: 0.0, + el_magnetic: 0.0, + }, + alarms: Vec::new(), + }; + let device_msg = RadomMessage::Status(status); + let bytes = + to_vec_cobs::(&device_msg).unwrap(); + serial.write(bytes.as_slice()).unwrap(); + } + HostMessage::TriggerDFUBootloader => todo!(), + }, + Err(err) => defmt::error!("Unable to parse host message: {}", err), + }; + + *buffer = Vec::::from_slice(rest).unwrap(); + } else { + break; + } + } + } } diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 4793873..9ae57a1 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -16,18 +16,18 @@ pub enum HostMessage { #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct StatusMessage { - position: Position, - alarms: Vec, + pub position: Position, + pub alarms: Vec, } #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct Position { - az: f32, - el: f32, - az_endcoder: f32, - el_encoder: f32, - az_magnetic: f32, - el_magnetic: f32, + pub az: f32, + pub el: f32, + pub az_endcoder: f32, + pub el_encoder: f32, + pub az_magnetic: f32, + pub el_magnetic: f32, } #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] From 1c4714381bac85f2865322bf193e3f2298b5a80e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 3 Oct 2024 22:54:05 +0200 Subject: [PATCH 3/6] Triggering the dfu bootloader works --- Cargo.lock | 102 +++++++++++++++++++++++++++++++++- daemon/Cargo.toml | 3 + daemon/src/bin/trigger-dfu.rs | 16 ++++++ daemon/src/lib.rs | 3 + daemon/src/main.rs | 13 ++--- firmware/.cargo/config.toml | 2 +- firmware/src/bootloader.rs | 59 ++++++++++++++++++++ firmware/src/main.rs | 53 +++++++++++------- memory.x | 17 +++++- 9 files changed, 235 insertions(+), 33 deletions(-) create mode 100644 daemon/src/bin/trigger-dfu.rs create mode 100644 daemon/src/lib.rs create mode 100644 firmware/src/bootloader.rs diff --git a/Cargo.lock b/Cargo.lock index 3a5c99c..5a45f58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cortex-m" version = "0.7.7" @@ -367,6 +373,12 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "embedded-io" version = "0.6.1" @@ -664,6 +676,16 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", +] + [[package]] name = "is-terminal" version = "0.4.12" @@ -699,6 +721,26 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libudev" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" +dependencies = [ + "libc", + "libudev-sys", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "litrs" version = "0.4.1" @@ -721,6 +763,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "matchit" version = "0.7.3" @@ -791,6 +842,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -971,6 +1033,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "portable-atomic" version = "1.7.0" @@ -985,6 +1053,8 @@ checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" dependencies = [ "cobs", "defmt", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless 0.7.17", "serde", ] @@ -1091,7 +1161,10 @@ dependencies = [ "humantime", "log", "nom", + "postcard", + "radomctl-protocol", "serde_json", + "serialport", "tokio", "tokio-macros 0.2.6", "tower-http", @@ -1299,6 +1372,24 @@ dependencies = [ "serde", ] +[[package]] +name = "serialport" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ba776acc8c373b9175829206229366273225436845c04f9c20aab8099960e2e" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "core-foundation-sys", + "io-kit-sys", + "libudev", + "mach2", + "nix", + "scopeguard", + "unescaper", + "winapi", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1672,6 +1763,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" +[[package]] +name = "unescaper" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815" +dependencies = [ + "thiserror", +] + [[package]] name = "unicase" version = "2.7.0" @@ -1704,7 +1804,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065e4eaf93db81d5adac82d9cef8f8da314cb640fa7f89534b972383f1cf80fc" dependencies = [ "embedded-hal 0.2.7", - "embedded-io", + "embedded-io 0.6.1", "nb 1.1.0", "usb-device", ] diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 8f34f13..bdcee7f 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -11,8 +11,11 @@ humantime = "2.1.0" log = "0.4.21" nom = "7.1.3" serde_json = "1.0.118" +serialport = "4.5.1" tokio = {version = "1.37.0", features = ["full"]} tokio-macros = { version = "0.2.0-alpha.6" } tower-http = { version = "0.5.2", features = ["fs", "trace"] } tracing = "0.1.40" tracing-subscriber = "0.3.18" +radomctl-protocol = { path = "../protocol" } +postcard = {version = "1.0.10", features = ["use-std"]} diff --git a/daemon/src/bin/trigger-dfu.rs b/daemon/src/bin/trigger-dfu.rs new file mode 100644 index 0000000..472f081 --- /dev/null +++ b/daemon/src/bin/trigger-dfu.rs @@ -0,0 +1,16 @@ +use std::time::Duration; + +use postcard::{from_bytes_cobs, to_stdvec_cobs}; +use radomctl_protocol::*; +use radomctld::logger::setup_logger; + +pub fn main() -> () { + let mut port = serialport::new("/dev/ttyACM0", 115_200) + .timeout(Duration::from_millis(10)) + .open() + .expect("Failed to open port"); + + let host_msg = HostMessage::TriggerDFUBootloader; + let msg_bytes = to_stdvec_cobs(&host_msg).unwrap(); + port.write_all(&msg_bytes).unwrap(); +} diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs new file mode 100644 index 0000000..85f9e01 --- /dev/null +++ b/daemon/src/lib.rs @@ -0,0 +1,3 @@ +pub mod logger; +pub mod rotctlprotocol; +pub mod rotor; diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 5698a6c..e52eece 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -1,7 +1,3 @@ -mod logger; -mod rotctlprotocol; -mod rotor; - use anyhow::Result; use fern::colors::{Color, ColoredLevelConfig}; use log::{debug, error, info, warn, Level}; @@ -26,10 +22,11 @@ use tower_http::{ trace::TraceLayer, }; -use logger::setup_logger; -use rotor::control_rotor; - -use rotctlprotocol::{parse_command, Command}; +use radomctld::{ + logger::setup_logger, + rotctlprotocol::{parse_command, Command}, + rotor::control_rotor, +}; async fn process_socket( socket: TcpStream, diff --git a/firmware/.cargo/config.toml b/firmware/.cargo/config.toml index c493dff..c543591 100644 --- a/firmware/.cargo/config.toml +++ b/firmware/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output) -runner = "probe-run --chip STM32F401CCU6" +runner = "probe-rs run --chip STM32F401CCUx" rustflags = [ "-C", "linker=flip-link", "-C", "link-arg=-Tlink.x", diff --git a/firmware/src/bootloader.rs b/firmware/src/bootloader.rs new file mode 100644 index 0000000..382e442 --- /dev/null +++ b/firmware/src/bootloader.rs @@ -0,0 +1,59 @@ +use core::ptr::addr_of_mut; + +use stm32f4xx_hal::pac; + +fn jump_to_bootloader() { + unsafe { + cortex_m::interrupt::disable(); + + let address: u32 = 0x1FFF0000; + + let device = pac::Peripherals::steal(); + device.SYSCFG.memrm.modify(|_, w| w.bits(0x01)); + + let mut p = cortex_m::Peripherals::steal(); + p.SCB.invalidate_icache(); + p.SCB.vtor.write(address as u32); + + cortex_m::interrupt::enable(); + + cortex_m::asm::bootload(address as *const u32); + } +} + +const BOOTLOADER_REQUESTED: u32 = 0xdecafbad; + +fn magic_mut_ptr() -> *mut u32 { + extern "C" { + #[link_name = "_dfu_magic"] + static mut magic: u32; + } + + unsafe { addr_of_mut!(magic) } +} + +fn get_bootloader_flag() -> u32 { + unsafe { magic_mut_ptr().read_volatile() } +} + +fn set_bootloader_flag() { + unsafe { magic_mut_ptr().write_volatile(BOOTLOADER_REQUESTED) }; +} + +fn clear_bootloader_flag() { + unsafe { magic_mut_ptr().write_volatile(BOOTLOADER_REQUESTED) }; +} + +pub fn init() { + let requested = get_bootloader_flag() == BOOTLOADER_REQUESTED; + if requested { + clear_bootloader_flag(); + jump_to_bootloader(); + } +} + +pub fn reboot_to_bootloader() -> ! { + defmt::info!("Rebooting into the bootloader"); + set_bootloader_flag(); + cortex_m::peripheral::SCB::sys_reset(); +} diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 40e8ceb..2fc4956 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -6,6 +6,8 @@ use defmt_brtt as _; // global logger use panic_probe as _; use stm32f4xx_hal as _; +mod bootloader; + // same panicking *behavior* as `panic-probe` but doesn't print a panic message // this prevents the panic message being printed *twice* when `defmt::panic` is invoked #[defmt::panic_handler] @@ -21,7 +23,9 @@ mod app { use as5048a::AS5048A; - use heapless::Vec; + use core::fmt::Write; + use heapless::{String, Vec}; + use num_traits::{Float, FloatConst}; use postcard::{from_bytes_cobs, to_vec_cobs}; use stm32f4xx_hal::{ gpio::{gpioa, gpiob, gpioc, Output, PushPull}, @@ -29,20 +33,19 @@ mod app { otg_fs::{UsbBus, UsbBusType, USB}, pac::{I2C1, SPI1}, prelude::*, - spi, + signature, spi, }; - use usb_device::class_prelude::UsbBusAllocator; use usb_device::prelude::*; + use usb_device::{class_prelude::UsbBusAllocator, device}; use usbd_serial::SerialPort; - use num_traits::{Float, FloatConst}; - use xca9548a::{SlaveAddr, Xca9548a}; use qmc5883l::{self, QMC5883L}; use radomctl_protocol::*; + use crate::bootloader; use rtic_monotonics::systick::prelude::*; systick_monotonic!(Mono, 4000); @@ -83,6 +86,8 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local) { + bootloader::init(); + defmt::info!("init"); let rcc = cx.device.RCC.constrain(); @@ -92,11 +97,8 @@ mod app { let clocks = rcc .cfgr .use_hse(25.MHz()) - .require_pll48clk() .sysclk(84.MHz()) - .hclk(84.MHz()) - .pclk1(42.MHz()) - .pclk2(84.MHz()) + .require_pll48clk() .freeze(); Mono::start(cx.core.SYST, clocks.sysclk().to_Hz()); @@ -131,16 +133,23 @@ mod app { let usb_serial = usbd_serial::SerialPort::new(unsafe { USB_BUS.as_ref().unwrap() }); - let usb_dev = UsbDeviceBuilder::new( - unsafe { USB_BUS.as_ref().unwrap() }, - UsbVidPid(0x16c0, 0x27dd), - ) - .device_class(usbd_serial::USB_CLASS_CDC) - .strings(&[StringDescriptors::default() - .manufacturer("Amteurfunk Forschungs Gruppe") - .product("Radom Controler")]) - .unwrap() - .build(); + let uid = signature::Uid::get(); + + static mut SERIAL: String<16> = String::new(); + unsafe { + write!(SERIAL, "{}{:x}{:x}", uid.lot_num(), uid.x(), uid.y()).unwrap(); + } + + let usb_dev = unsafe { + UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) + .device_class(usbd_serial::USB_CLASS_CDC) + .strings(&[StringDescriptors::default() + .manufacturer("Amteurfunk Forschungs Gruppe") + .product("Radom Controler") + .serial_number(SERIAL.as_ref())]) + .unwrap() + .build() + }; defmt::info!("USB Setup done"); @@ -416,9 +425,11 @@ mod app { to_vec_cobs::(&device_msg).unwrap(); serial.write(bytes.as_slice()).unwrap(); } - HostMessage::TriggerDFUBootloader => todo!(), + HostMessage::TriggerDFUBootloader => { + bootloader::reboot_to_bootloader(); + } }, - Err(err) => defmt::error!("Unable to parse host message: {}", err), + Err(err) => defmt::error!("Unable to parse host message"), }; *buffer = Vec::::from_slice(rest).unwrap(); diff --git a/memory.x b/memory.x index dfe3ff3..606d5fd 100644 --- a/memory.x +++ b/memory.x @@ -1,10 +1,23 @@ MEMORY { - /* NOTE K = KiBi = 1024 bytes */ FLASH : ORIGIN = 0x08000000, LENGTH = 256K - RAM : ORIGIN = 0x20000000, LENGTH = 64K + UNINIT: ORIGIN = 0x20000000, LENGTH = 0x10 + RAM : ORIGIN = 0x20000010, LENGTH = 64K - LENGTH(UNINIT) } +SECTIONS { + .uninit_flags (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit_flags = .; + _dfu_magic = .; . += 4; + *(.uninit_flags .uninit_flags.*) + . = ALIGN(4); + __euninit_flags = .; + } > UNINIT +} + + /* This is where the call stack will be allocated. */ /* The stack is of the full descending type. */ /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ From 77eebdf79599ea75f2d1c246c6a97a654df6e32a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 4 Oct 2024 15:54:23 +0200 Subject: [PATCH 4/6] Added correct serial number to usb device --- firmware/src/main.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 2fc4956..45bd6b7 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -133,11 +133,22 @@ mod app { let usb_serial = usbd_serial::SerialPort::new(unsafe { USB_BUS.as_ref().unwrap() }); - let uid = signature::Uid::get(); + let serial = unsafe { + let u_id0 = 0x1FFF_7A10 as *const u32; + let u_id2 = 0x1FFF_7A18 as *const u32; + + defmt::debug!("UID0: {:x}", u_id0.read()); + defmt::debug!("UID2: {:x}", u_id2.read()); + + // See https://community.st.com/t5/stm32-mcus-products/usb-bootloader-serial-number/td-p/432148 + (u_id0.read() as u64 + u_id2.read() as u64) << 16 + | (u_id2.read() as u64 & 0xFF00) >> 8 + | (u_id2.read() as u64 & 0x00FF) << 8 + }; static mut SERIAL: String<16> = String::new(); unsafe { - write!(SERIAL, "{}{:x}{:x}", uid.lot_num(), uid.x(), uid.y()).unwrap(); + write!(SERIAL, "{:X}", serial).unwrap(); } let usb_dev = unsafe { From b2830a1fbc0b6388fa00c2bba4d183205df7a873 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 4 Oct 2024 16:20:36 +0200 Subject: [PATCH 5/6] Added test application for flashing --- .gitignore | 3 +- Cargo.lock | 73 +++++++++++++++++++++++++++++++++++ daemon/Cargo.toml | 3 ++ daemon/src/bin/trigger-dfu.rs | 34 +++++++++++++++- firmware/flash-dfu.sh | 5 +++ 5 files changed, 115 insertions(+), 3 deletions(-) create mode 100755 firmware/flash-dfu.sh diff --git a/.gitignore b/.gitignore index 4713399..3bf03d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -/*/output.log \ No newline at end of file +/*/output.log +*.bin \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5a45f58..d29716f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,6 +320,42 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "dfu-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3fb34e94bedc8bbd76dfc9f6774896fadbbedac6108fa8a3c46a0125bde188" +dependencies = [ + "bytes", + "displaydoc", + "log", + "pretty-hex", + "thiserror", +] + +[[package]] +name = "dfu-libusb" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e20ae81db46fd54fede9e71d06c06a382926212831cfd2f758b888e00a321d1" +dependencies = [ + "dfu-core", + "libusb1-sys", + "rusb", + "thiserror", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "document-features" version = "0.2.10" @@ -741,6 +777,18 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libusb1-sys" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f6bace2f39082e9787c851afce469e7b2fe0f1cc64bbc68ca96653b63d8f17" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "litrs" version = "0.4.1" @@ -1065,6 +1113,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1157,12 +1211,15 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", + "dfu-libusb", "fern", "humantime", + "libusb1-sys", "log", "nom", "postcard", "radomctl-protocol", + "rusb", "serde_json", "serialport", "tokio", @@ -1256,6 +1313,16 @@ dependencies = [ "rtic-common", ] +[[package]] +name = "rusb" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45fff149b6033f25e825cbb7b2c625a11ee8e6dac09264d49beb125e39aa97bf" +dependencies = [ + "libc", + "libusb1-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1821,6 +1888,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index bdcee7f..f08edfe 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -19,3 +19,6 @@ tracing = "0.1.40" tracing-subscriber = "0.3.18" radomctl-protocol = { path = "../protocol" } postcard = {version = "1.0.10", features = ["use-std"]} +dfu-libusb = "0.3.0" +libusb1-sys = "0.6" +rusb = "0.9" diff --git a/daemon/src/bin/trigger-dfu.rs b/daemon/src/bin/trigger-dfu.rs index 472f081..f229d73 100644 --- a/daemon/src/bin/trigger-dfu.rs +++ b/daemon/src/bin/trigger-dfu.rs @@ -1,10 +1,16 @@ -use std::time::Duration; +use std::{ + io::{self, Seek}, + thread, + time::{self, Duration}, +}; +use anyhow::Context; +use dfu_libusb::{Dfu, DfuLibusb}; use postcard::{from_bytes_cobs, to_stdvec_cobs}; use radomctl_protocol::*; use radomctld::logger::setup_logger; -pub fn main() -> () { +pub fn main() -> anyhow::Result<()> { let mut port = serialport::new("/dev/ttyACM0", 115_200) .timeout(Duration::from_millis(10)) .open() @@ -13,4 +19,28 @@ pub fn main() -> () { let host_msg = HostMessage::TriggerDFUBootloader; let msg_bytes = to_stdvec_cobs(&host_msg).unwrap(); port.write_all(&msg_bytes).unwrap(); + drop(port); + + let context = rusb::Context::new()?; + let mut file = std::fs::File::open("../../firmware/radomctl-firmware.bin") + .context("firmware file not found")?; + + thread::sleep(time::Duration::from_millis(1000)); + + let file_size = + u32::try_from(file.seek(io::SeekFrom::End(0))?).context("the firmware file is too big")?; + file.seek(io::SeekFrom::Start(0))?; + + let vid = 0x0483; + let pid = 0xdf11; + let intf = 0; + let alt = 0; + let mut device: Dfu = + DfuLibusb::open(&context, vid, pid, intf, alt).context("could not open device")?; + + device + .download(file, file_size) + .context("could not write firmware to the device")?; + + Ok(()) } diff --git a/firmware/flash-dfu.sh b/firmware/flash-dfu.sh new file mode 100755 index 0000000..9f1f583 --- /dev/null +++ b/firmware/flash-dfu.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cargo build --release && \ +arm-none-eabi-objcopy -O binary ../target/thumbv7em-none-eabihf/release/radomctl-firmware radomctl-firmware.bin && \ +dfu-util --alt 0 -s 0x08000000:leave -D radomctl-firmware.bin \ No newline at end of file From 57fdf05f00d845ad450fe969b5306d8bc10f32ea Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 6 Oct 2024 13:39:07 +0200 Subject: [PATCH 6/6] Added progress to flash tool --- Cargo.lock | 178 +++++++++++++++++++++++++++++++++- daemon/Cargo.toml | 2 + daemon/src/bin/flash-dfu.rs | 112 +++++++++++++++++++++ daemon/src/bin/trigger-dfu.rs | 46 --------- 4 files changed, 290 insertions(+), 48 deletions(-) create mode 100644 daemon/src/bin/flash-dfu.rs delete mode 100644 daemon/src/bin/trigger-dfu.rs diff --git a/Cargo.lock b/Cargo.lock index d29716f..c5a32f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -17,6 +17,55 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -119,7 +168,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.72", @@ -197,12 +246,58 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cobs" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "colored" version = "1.9.4" @@ -214,6 +309,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -427,6 +535,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "enumflags2" version = "0.7.10" @@ -604,6 +718,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -712,6 +832,28 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "io-kit-sys" version = "0.4.1" @@ -733,6 +875,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.11" @@ -989,6 +1137,12 @@ dependencies = [ "libm", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "object" version = "0.36.2" @@ -1211,9 +1365,11 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", + "clap", "dfu-libusb", "fern", "humantime", + "indicatif", "libusb1-sys", "log", "nom", @@ -1545,6 +1701,12 @@ dependencies = [ "void", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "1.0.109" @@ -1854,6 +2016,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "usb-device" version = "0.3.2" @@ -1876,6 +2044,12 @@ dependencies = [ "usb-device", ] +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.0" diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index f08edfe..95f39bd 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -22,3 +22,5 @@ postcard = {version = "1.0.10", features = ["use-std"]} dfu-libusb = "0.3.0" libusb1-sys = "0.6" rusb = "0.9" +clap = { version = "4.5.19", features = ["derive"] } +indicatif = "0.17.8" diff --git a/daemon/src/bin/flash-dfu.rs b/daemon/src/bin/flash-dfu.rs new file mode 100644 index 0000000..bbe0ffa --- /dev/null +++ b/daemon/src/bin/flash-dfu.rs @@ -0,0 +1,112 @@ +use std::{ + io::{self, Seek}, + thread, + time::{self, Duration}, +}; + +use anyhow::{anyhow, Context}; +use clap::Parser; +use dfu_libusb::{Dfu, DfuLibusb}; +use postcard::{from_bytes_cobs, to_stdvec_cobs}; +use radomctl_protocol::*; +use radomctld::logger::setup_logger; + +#[derive(Parser)] +struct Cli { + /// The usb serial number of the radom-controller + #[arg(short, long)] + serialnumber: String, + + /// The firmware file to flash + #[arg(short, long)] + firmware: std::path::PathBuf, +} + +pub fn main() -> anyhow::Result<()> { + let args = Cli::parse(); + + let ports = serialport::available_ports().unwrap_or(Vec::::new()); + + let mut radom_port: Option = None; + for port in ports { + match port.port_type { + serialport::SerialPortType::UsbPort(usb_port_info) => { + match usb_port_info.serial_number { + Some(serial) => { + if serial == args.serialnumber { + radom_port = Some(port.port_name.to_owned()); + println!("Found radom-controller as {}", port.port_name) + } + } + None => continue, + } + } + _ => continue, + } + } + + let radom_port = match radom_port { + Some(port) => port, + _ => { + return Err(anyhow!("No matching port found.")); + } + }; + + println!("Setting radom-controller to dfu boot..."); + + let mut port = serialport::new(radom_port, 115_200) + .timeout(Duration::from_millis(10)) + .open() + .expect("Failed to open port"); + + let host_msg = HostMessage::TriggerDFUBootloader; + let msg_bytes = to_stdvec_cobs(&host_msg).unwrap(); + port.write_all(&msg_bytes).unwrap(); + drop(port); + + let context = rusb::Context::new()?; + let mut file = std::fs::File::open(args.firmware).context("firmware file not found")?; + + thread::sleep(time::Duration::from_millis(2000)); + + let file_size = + u32::try_from(file.seek(io::SeekFrom::End(0))?).context("the firmware file is too big")?; + file.seek(io::SeekFrom::Start(0))?; + + let vid = 0x0483; + let pid = 0xdf11; + let intf = 0; + let alt = 0; + let mut device: Dfu = + DfuLibusb::open(&context, vid, pid, intf, alt).context("could not open device")?; + + println!("Flashing radom-controller ..."); + + let bar = indicatif::ProgressBar::new(file_size as u64); + bar.set_style( + indicatif::ProgressStyle::default_bar() + .template( + "{spinner:.green} [{elapsed_precise}] [{bar}] \ + {bytes}/{total_bytes} ({bytes_per_sec}) ({eta}) {msg:10}", + )? + .progress_chars("=>-"), + ); + + device.with_progress({ + let bar = bar.clone(); + move |count| { + bar.inc(count as u64); + if bar.position() == file_size as u64 { + bar.finish(); + } + } + }); + + device + .download(file, file_size) + .context("could not write firmware to the device")?; + + println!("Done!"); + + Ok(()) +} diff --git a/daemon/src/bin/trigger-dfu.rs b/daemon/src/bin/trigger-dfu.rs deleted file mode 100644 index f229d73..0000000 --- a/daemon/src/bin/trigger-dfu.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::{ - io::{self, Seek}, - thread, - time::{self, Duration}, -}; - -use anyhow::Context; -use dfu_libusb::{Dfu, DfuLibusb}; -use postcard::{from_bytes_cobs, to_stdvec_cobs}; -use radomctl_protocol::*; -use radomctld::logger::setup_logger; - -pub fn main() -> anyhow::Result<()> { - let mut port = serialport::new("/dev/ttyACM0", 115_200) - .timeout(Duration::from_millis(10)) - .open() - .expect("Failed to open port"); - - let host_msg = HostMessage::TriggerDFUBootloader; - let msg_bytes = to_stdvec_cobs(&host_msg).unwrap(); - port.write_all(&msg_bytes).unwrap(); - drop(port); - - let context = rusb::Context::new()?; - let mut file = std::fs::File::open("../../firmware/radomctl-firmware.bin") - .context("firmware file not found")?; - - thread::sleep(time::Duration::from_millis(1000)); - - let file_size = - u32::try_from(file.seek(io::SeekFrom::End(0))?).context("the firmware file is too big")?; - file.seek(io::SeekFrom::Start(0))?; - - let vid = 0x0483; - let pid = 0xdf11; - let intf = 0; - let alt = 0; - let mut device: Dfu = - DfuLibusb::open(&context, vid, pid, intf, alt).context("could not open device")?; - - device - .download(file, file_size) - .context("could not write firmware to the device")?; - - Ok(()) -}