From c5bec82d59e999cbcd7265507c0486429870423b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 21 Dec 2023 00:26:56 +0100 Subject: [PATCH] Added first working UI --- firmware/src/main.rs | 5 +- hostsoftware/Cargo.toml | 1 + hostsoftware/src/main.rs | 104 +++++++++++++++++++++++++++++---------- protocol/src/lib.rs | 11 +++++ 4 files changed, 94 insertions(+), 27 deletions(-) diff --git a/firmware/src/main.rs b/firmware/src/main.rs index ddc2b6c..a34f89e 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#![feature(slice_split_once)] use defmt_rtt as _; // global logger use panic_probe as _; @@ -376,7 +375,9 @@ mod app { } loop { - if let Some((msg, rest)) = buffer.split_once(|&x| x == 0) { + 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); diff --git a/hostsoftware/Cargo.toml b/hostsoftware/Cargo.toml index 0e6efc8..83c26c0 100644 --- a/hostsoftware/Cargo.toml +++ b/hostsoftware/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" [dependencies] cheapsdo-protocol = { path = "../protocol" } eframe = "0.24.1" +egui_plot = "0.24.1" postcard = {version = "1.0.8", features = ["use-std"]} serialport = "4.3.0" diff --git a/hostsoftware/src/main.rs b/hostsoftware/src/main.rs index 9572c9c..32cd28c 100644 --- a/hostsoftware/src/main.rs +++ b/hostsoftware/src/main.rs @@ -1,11 +1,12 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release -use std::sync::{Arc, Mutex}; use std::io; -use std::{io::Write, thread}; +use std::sync::{Arc, Mutex}; use std::time::Duration; +use std::{io::Write, thread}; use eframe::egui; +use egui_plot::{Line, Plot, PlotBounds, PlotPoints}; use cheapsdo_protocol::*; use postcard::{from_bytes_cobs, to_stdvec_cobs}; @@ -22,8 +23,24 @@ fn main() -> Result<(), eframe::Error> { ) } +struct SharedState { + device_state: StatusMessage, + average_points: Vec<[f64; 2]>, + measured_points: Vec<[f64; 2]>, +} + +impl Default for SharedState { + fn default() -> Self { + Self { + device_state: StatusMessage::default(), + average_points: Vec::<[f64; 2]>::new(), + measured_points: Vec::<[f64; 2]>::new(), + } + } +} + struct CheapsdoControl { - devicestate: Arc>, + shared_state: Arc>, serial_device: String, serial_connected: bool, } @@ -31,7 +48,7 @@ struct CheapsdoControl { impl Default for CheapsdoControl { fn default() -> Self { Self { - devicestate: Arc::new(Mutex::new(StatusMessage::default())), + shared_state: Arc::new(Mutex::new(SharedState::default())), serial_device: "".to_owned(), serial_connected: false, } @@ -43,8 +60,6 @@ impl eframe::App for CheapsdoControl { let ports = serialport::available_ports().unwrap_or(Vec::::new()); - - egui::CentralPanel::default().show(ctx, |ui| { ui.horizontal(|ui| { egui::ComboBox::from_label("Select Serialport") @@ -59,40 +74,74 @@ impl eframe::App for CheapsdoControl { } }); - if ui.add_enabled(!self.serial_connected, egui::Button::new("Open")).clicked() { + if ui + .add_enabled(!self.serial_connected, egui::Button::new("Open")) + .clicked() + { self.serial_connected = true; let serial_device = self.serial_device.clone(); - let devicestate = self.devicestate.clone(); + let devicestate = self.shared_state.clone(); + let ctx = ctx.clone(); std::thread::spawn(move || { - poll_device(serial_device, devicestate); + poll_device(serial_device, devicestate, ctx); }); } - if ui.add_enabled(self.serial_connected, egui::Button::new("Close")).clicked() { - - } + if ui + .add_enabled(self.serial_connected, egui::Button::new("Close")) + .clicked() + {} }); ui.separator(); { - let mut state = self.devicestate.lock().unwrap(); + let mut state = self.shared_state.lock().unwrap(); ui.horizontal(|ui| { - ui.label(format!("Measured: {}", state.measured_frequency)); - ui.label(format!("Average: {}", state.average_frequency)); + ui.label(format!( + "Measured: {}", + state.device_state.measured_frequency + )); + ui.label(format!("Average: {}", state.device_state.average_frequency)); }); + + let average_line = Line::new(PlotPoints::new(state.average_points.clone())); + let measured_line = Line::new(PlotPoints::new(state.measured_points.clone())); + Plot::new("my_plot") + .allow_zoom(false) + .allow_scroll(false) + .allow_drag(false) + .allow_boxed_zoom(false) + .y_axis_width(20) + .y_axis_formatter(|val, _, _| { + let rest = (val * 1_000_000_000.0) as u64; + let milli_hz = rest % 1000; + let rest = rest / 1000; + let hz = rest % 1000; + let rest = rest / 1000; + let khz = rest % 1000; + let rest = rest / 1000; + let mhz = rest % 1000; + + format!("{:02}.{:03} {:03} {:03}", mhz, khz, hz, milli_hz) + }) + .show(ui, |plot_ui| { + plot_ui.set_auto_bounds([true, true].into()); + plot_ui.line(average_line); + //plot_ui.line(measured_line); + }) } }); } } - -fn poll_device(port: String, state: Arc>) { +fn poll_device(port: String, state: Arc>, ctx: egui::Context) { let mut port = serialport::new(port, 115_200) - .timeout(Duration::from_millis(10)) - .open().expect("Failed to open port"); + .timeout(Duration::from_millis(10)) + .open() + .expect("Failed to open port"); loop { let host_msg = HostMessage::RequestStatus; @@ -107,20 +156,25 @@ fn poll_device(port: String, state: Arc>) { println!("Data: {:?}", serial_buf); let dev_msg = from_bytes_cobs::(&mut serial_buf).unwrap(); - + match dev_msg { DeviceMessage::Status(status_msg) => { let mut state = state.lock().unwrap(); - *state = status_msg; - }, - } + let x = state.average_points.len() as f64; + state.average_points.push([x, status_msg.average_frequency]); + state + .measured_points + .push([x, status_msg.measured_frequency]); + state.device_state = status_msg; + ctx.request_repaint() + } + } } Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (), Err(e) => eprintln!("{:?}", e), } - thread::sleep(Duration::from_millis(1000)); } -} \ No newline at end of file +} diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 26b4611..c06ce49 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -19,3 +19,14 @@ pub struct StatusMessage { pub average_frequency: f64, pub pwm: u16, } + + +impl Default for StatusMessage { + fn default() -> Self { + Self { + measured_frequency: 0.0f64, + average_frequency: 0.0f64, + pwm: 0u16, + } + } +} \ No newline at end of file