From 94b3700b2bec10e169478a608675a22c6eaaac57 Mon Sep 17 00:00:00 2001
From: Stephen D <webmaster@scd31.com>
Date: Mon, 27 Nov 2023 21:20:51 -0400
Subject: [PATCH] voltage meter

---
 src/main.rs    | 16 ++++++++++++++--
 src/shell.rs   | 20 +++++++++++++++-----
 src/voltage.rs | 26 ++++++++++++++++++++++++++
 3 files changed, 55 insertions(+), 7 deletions(-)
 create mode 100644 src/voltage.rs

diff --git a/src/main.rs b/src/main.rs
index 09503c2..626bfe5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ mod gps;
 mod radio;
 mod shell;
 mod status;
+mod voltage;
 
 pub const MAX_PACKET_LEN: usize = 8191;
 
@@ -17,6 +18,7 @@ pub const MAX_PACKET_LEN: usize = 8191;
 mod app {
     use cortex_m::singleton;
     use hal::{
+        adc::{config::AdcConfig, Adc},
         flash::LockedFlash,
         gpio,
         otg_fs::{UsbBus, UsbBusType, USB},
@@ -42,6 +44,7 @@ mod app {
         radio::RadioManager,
         shell::Shell,
         status::Status,
+        voltage::VoltMeter,
         MAX_PACKET_LEN,
     };
 
@@ -73,6 +76,7 @@ mod app {
         red: gpio::Pin<'B', 14, gpio::Output>,
         radio: Option<RadioManager<'static>>,
         gps: GpsModule,
+        volt_meter: VoltMeter,
 
         usb_dev: UsbDevice<'static, UsbBusType>,
         shell: Shell,
@@ -112,6 +116,12 @@ mod app {
         red.set_low();
         green.set_low();
 
+        // setup volt meter
+
+        let volt_pin = gpioc.pc0.into_analog();
+        let volt_adc = Adc::adc1(dp.ADC1, true, AdcConfig::default());
+        let volt_meter = VoltMeter::new(volt_pin, volt_adc);
+
         // Detect if we're connected to USB or not
         let usb_detect = gpioa.pa5.into_pull_down_input();
 
@@ -215,6 +225,7 @@ mod app {
                 red,
                 radio,
                 gps: GpsModule::new(),
+                volt_meter,
 
                 usb_dev,
                 shell,
@@ -359,16 +370,17 @@ mod app {
         }
     }
 
-    #[task(binds=OTG_FS, priority = 2, local=[flash], shared=[usb_dev, shell])]
+    #[task(binds=OTG_FS, priority = 2, local=[flash], shared=[usb_dev, shell, volt_meter])]
     fn usb_fs(cx: usb_fs::Context) {
         let usb_fs::SharedResources {
             mut usb_dev,
             mut shell,
+            mut volt_meter,
         } = cx.shared;
 
         (&mut usb_dev, &mut shell).lock(|usb_dev, shell| {
             while usb_dev.poll(&mut [shell.ushell.get_serial_mut()]) {
-                shell.poll(cx.local.flash);
+                shell.poll(cx.local.flash, &mut volt_meter);
             }
         });
     }
diff --git a/src/shell.rs b/src/shell.rs
index cb8c2fe..0bb2c2c 100644
--- a/src/shell.rs
+++ b/src/shell.rs
@@ -1,6 +1,7 @@
 use core::fmt::Write;
 
 use arrayvec::{ArrayString, CapacityError};
+use rtic::Mutex;
 use stm32f4xx_hal::{
     flash::LockedFlash,
     otg_fs::{UsbBus, USB},
@@ -8,10 +9,13 @@ use stm32f4xx_hal::{
 use usbd_serial::SerialPort;
 use ushell::{autocomplete::StaticAutocomplete, history::LRUHistory, Input, ShellError, UShell};
 
-use crate::config::{BlackHoleSetting, Config, GpsSetting, MaybeBlackHole};
+use crate::{
+    config::{BlackHoleSetting, Config, GpsSetting, MaybeBlackHole},
+    voltage::VoltMeter,
+};
 
 type ShellType =
-    UShell<SerialPort<'static, UsbBus<USB>>, StaticAutocomplete<4>, LRUHistory<256, 8>, 256>;
+    UShell<SerialPort<'static, UsbBus<USB>>, StaticAutocomplete<5>, LRUHistory<256, 8>, 256>;
 
 const SAVE_TEXT: &str =
     "Saved your settings. Remove USB and press the reset button when you're ready.\r\n\
@@ -29,7 +33,8 @@ const HELP_TEXT: &str = concat!(
 	 help                       Display this text\r\n\r\n\
 	 get                        Show the current configuration\r\n\r\n\
 	 set [property] [value]     Update the current configuration\r\n                           For a list of properties, see the `get` command\r\n\r\n\
-	 save                       Save the current settings to flash memory for persistence\r\n\
+	 save                       Save the current settings to flash memory for persistence\r\n\r\n\
+	 volts                      Show the current input voltage. Only reads from the screw terminals - will show ~0V if only connected to USB\r\n\
 	 "
 );
 
@@ -42,14 +47,14 @@ impl Shell {
     pub fn new(config: Config, serial: SerialPort<'static, UsbBus<USB>>) -> Self {
         let ushell = UShell::new(
             serial,
-            StaticAutocomplete(["get", "set", "help", "save"]),
+            StaticAutocomplete(["get", "set", "help", "save", "volts"]),
             LRUHistory::default(),
         );
 
         Self { ushell, config }
     }
 
-    pub fn poll(&mut self, flash: &mut LockedFlash) {
+    pub fn poll<V: Mutex<T = VoltMeter>>(&mut self, flash: &mut LockedFlash, volt_meter: &mut V) {
         loop {
             let ushell = &mut self.ushell;
             match ushell.poll() {
@@ -196,6 +201,11 @@ impl Shell {
 
                             write!(ushell, "{}", SAVE_TEXT).ok();
                         }
+                        "volts" => {
+                            let volts = volt_meter.lock(|vm| vm.voltage());
+
+                            write!(ushell, "\r\n{volts:.2} V\r\n").ok();
+                        }
                         "help" => {
                             write!(ushell, "{}", HELP_TEXT).ok();
                         }
diff --git a/src/voltage.rs b/src/voltage.rs
new file mode 100644
index 0000000..d6193be
--- /dev/null
+++ b/src/voltage.rs
@@ -0,0 +1,26 @@
+use stm32f4xx_hal::{
+    adc::{config::SampleTime, Adc},
+    gpio::{Analog, Pin},
+    pac::ADC1,
+};
+
+const FACTOR: f64 = 1.0 / 0.0757856;
+
+pub struct VoltMeter {
+    pin: Pin<'C', 0, Analog>,
+    adc: Adc<ADC1>,
+}
+
+impl VoltMeter {
+    pub fn new(pin: Pin<'C', 0, Analog>, mut adc: Adc<ADC1>) -> Self {
+        adc.calibrate();
+        Self { pin, adc }
+    }
+
+    pub fn voltage(&mut self) -> f64 {
+        let sample = self.adc.convert(&self.pin, SampleTime::Cycles_480);
+        let mv = self.adc.sample_to_millivolts(sample);
+
+        mv as f64 * FACTOR / 1000.0
+    }
+}
-- 
GitLab