pl: async serial

This commit is contained in:
Alex Janka 2024-12-28 22:39:15 +11:00
parent bc0da6be11
commit bda2d63e99
4 changed files with 42 additions and 49 deletions
Cargo.lock
charge-controller-supervisor

1
Cargo.lock generated
View file

@ -252,7 +252,6 @@ dependencies = [
"rocket", "rocket",
"serde", "serde",
"serde_json", "serde_json",
"serialport",
"tokio", "tokio",
"tokio-modbus", "tokio-modbus",
"tokio-serial", "tokio-serial",

View file

@ -21,7 +21,6 @@ chrono = "0.4.39"
prometheus = "0.13.4" prometheus = "0.13.4"
env_logger = "0.11.6" env_logger = "0.11.6"
log = "0.4.22" log = "0.4.22"
serialport = "4.6.1"
futures = "0.3.31" futures = "0.3.31"
tokio-modbus = "0.16.1" tokio-modbus = "0.16.1"
tokio-serial = "5.4.4" tokio-serial = "5.4.4"

View file

@ -69,7 +69,7 @@ impl ControllerInner {
pub async fn refresh(&mut self) -> eyre::Result<CommonData> { pub async fn refresh(&mut self) -> eyre::Result<CommonData> {
match self { match self {
ControllerInner::Pl(pli) => { ControllerInner::Pl(pli) => {
let pl_data = pli.refresh()?; let pl_data = pli.refresh().await?;
Ok(pl_data) Ok(pl_data)
} }
ControllerInner::Tristar(tristar) => { ControllerInner::Tristar(tristar) => {

View file

@ -1,8 +1,7 @@
use std::{io::Write, time::Duration};
use chrono::Timelike; use chrono::Timelike;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serialport::SerialPort; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_serial::{SerialPort, SerialPortBuilderExt};
use crate::gauges::{ use crate::gauges::{
BATTERY_TEMP, BATTERY_VOLTAGE, CHARGE_STATE, INPUT_CURRENT, PL_DUTY_CYCLE, PL_LOAD_CURRENT, BATTERY_TEMP, BATTERY_VOLTAGE, CHARGE_STATE, INPUT_CURRENT, PL_DUTY_CYCLE, PL_LOAD_CURRENT,
@ -11,7 +10,7 @@ use crate::gauges::{
pub struct Pli { pub struct Pli {
friendly_name: String, friendly_name: String,
port: Box<dyn SerialPort>, port: tokio_serial::SerialStream,
} }
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
@ -111,10 +110,10 @@ impl Pli {
friendly_name: &str, friendly_name: &str,
baud_rate: u32, baud_rate: u32,
timeout: u64, timeout: u64,
) -> Result<Self, serialport::Error> { ) -> Result<Self, tokio_serial::Error> {
let port = serialport::new(serial_port, baud_rate) let port = tokio_serial::new(serial_port, baud_rate)
.timeout(Duration::from_millis(timeout)) .timeout(std::time::Duration::from_millis(timeout))
.open()?; .open_native_async()?;
Ok(Self { Ok(Self {
friendly_name: friendly_name.to_owned(), friendly_name: friendly_name.to_owned(),
@ -122,8 +121,8 @@ impl Pli {
}) })
} }
pub fn refresh(&mut self) -> eyre::Result<crate::controller::CommonData> { pub async fn refresh(&mut self) -> eyre::Result<crate::controller::CommonData> {
let new_state = self.read_state()?; let new_state = self.read_state().await?;
BATTERY_VOLTAGE BATTERY_VOLTAGE
.with_label_values(&[&self.friendly_name]) .with_label_values(&[&self.friendly_name])
.set(new_state.battery_voltage); .set(new_state.battery_voltage);
@ -153,14 +152,14 @@ impl Pli {
} }
#[expect(dead_code, reason = "writing settings is not yet implemented")] #[expect(dead_code, reason = "writing settings is not yet implemented")]
pub fn process_request(&mut self, message: PliRequest) -> eyre::Result<()> { pub async fn process_request(&mut self, message: PliRequest) -> eyre::Result<()> {
match message { match message {
PliRequest::ReadRam(address) => { PliRequest::ReadRam(address) => {
let data = self.read_ram(address)?; let data = self.read_ram(address).await?;
log::warn!("Read RAM at {address}: data {data}"); log::warn!("Read RAM at {address}: data {data}");
} }
PliRequest::ReadEeprom(address) => { PliRequest::ReadEeprom(address) => {
let data = self.read_eeprom(address)?; let data = self.read_eeprom(address).await?;
log::warn!("Read EEPROM at {address}: data {data}"); log::warn!("Read EEPROM at {address}: data {data}");
} }
PliRequest::SyncTime => { PliRequest::SyncTime => {
@ -168,85 +167,81 @@ impl Pli {
let timestamp = (((now.hour() * 10) + (now.minute() / 6)) & 0xFF) as u8; let timestamp = (((now.hour() * 10) + (now.minute() / 6)) & 0xFF) as u8;
let min = (now.minute() % 6) as u8; let min = (now.minute() % 6) as u8;
let sec = (now.second() / 2).min(29) as u8; let sec = (now.second() / 2).min(29) as u8;
self.write_ram(PlRamAddress::Hour, timestamp)?; self.write_ram(PlRamAddress::Hour, timestamp).await?;
self.write_ram(PlRamAddress::Min, min)?; self.write_ram(PlRamAddress::Min, min).await?;
self.write_ram(PlRamAddress::Sec, sec)?; self.write_ram(PlRamAddress::Sec, sec).await?;
log::warn!( log::warn!(
"Set time: {now} corresponds to {timestamp} + minutes {min} + seconds {sec}" "Set time: {now} corresponds to {timestamp} + minutes {min} + seconds {sec}"
); );
} }
PliRequest::SetRegulatorState(state) => { PliRequest::SetRegulatorState(state) => {
log::warn!("Setting regulator state to {state:?}"); log::warn!("Setting regulator state to {state:?}");
self.write_ram(PlRamAddress::Rstate, state.into())?; self.write_ram(PlRamAddress::Rstate, state.into()).await?;
} }
} }
Ok(()) Ok(())
} }
fn read_state(&mut self) -> eyre::Result<PlState> { async fn read_state(&mut self) -> eyre::Result<PlState> {
Ok(PlState { Ok(PlState {
battery_voltage: f64::from(self.read_ram(PlRamAddress::Batv)?) * (4. / 10.), battery_voltage: f64::from(self.read_ram(PlRamAddress::Batv).await?) * (4. / 10.),
target_voltage: f64::from(self.read_ram(PlRamAddress::Vreg)?) * (4. / 10.), target_voltage: f64::from(self.read_ram(PlRamAddress::Vreg).await?) * (4. / 10.),
duty_cycle: f64::from(self.read_ram(PlRamAddress::Dutycyc)?) / 255., duty_cycle: f64::from(self.read_ram(PlRamAddress::Dutycyc).await?) / 255.,
internal_charge_current: f64::from(self.read_ram(PlRamAddress::Cint)?) * (4. / 10.), internal_charge_current: f64::from(self.read_ram(PlRamAddress::Cint).await?)
internal_load_current: f64::from(self.read_ram(PlRamAddress::Lint)?) * (4. / 10.), * (4. / 10.),
battery_temp: f64::from(self.read_ram(PlRamAddress::Battemp)?), internal_load_current: f64::from(self.read_ram(PlRamAddress::Lint).await?) * (4. / 10.),
regulator_state: self.read_ram(PlRamAddress::Rstate)?.into(), battery_temp: f64::from(self.read_ram(PlRamAddress::Battemp).await?),
regulator_state: self.read_ram(PlRamAddress::Rstate).await?.into(),
}) })
} }
fn send_command(&mut self, req: [u8; 4]) -> eyre::Result<()> { async fn send_command(&mut self, req: [u8; 4]) -> eyre::Result<()> {
self.flush()?; self.flush()?;
self.port.write_all(&req)?; self.port.write_all(&req).await?;
Ok(()) Ok(())
} }
fn flush(&mut self) -> eyre::Result<()> { fn flush(&mut self) -> eyre::Result<()> {
self.port.flush()?; self.port.clear(tokio_serial::ClearBuffer::All)?;
while let Ok(num) = self.port.bytes_to_read() {
if num == 0 {
return Ok(());
}
let _ = self.port.read(&mut [0; 8]);
}
Ok(()) Ok(())
} }
fn receive<const LENGTH: usize>(&mut self) -> eyre::Result<[u8; LENGTH]> { async fn receive<const LENGTH: usize>(&mut self) -> eyre::Result<[u8; LENGTH]> {
let mut buf = [0; LENGTH]; let mut buf = [0; LENGTH];
self.port.read_exact(&mut buf)?; self.port.read_exact(&mut buf).await?;
Ok(buf) Ok(buf)
} }
fn read_ram<T>(&mut self, address: T) -> eyre::Result<u8> async fn read_ram<T>(&mut self, address: T) -> eyre::Result<u8>
where where
T: Into<u8>, T: Into<u8>,
{ {
self.send_command(command(20, address.into(), 0))?; self.send_command(command(20, address.into(), 0)).await?;
let buf = self.receive::<1>()?; let buf = self.receive::<1>().await?;
if buf[0] == 200 { if buf[0] == 200 {
Ok(self.receive::<1>()?[0]) Ok(self.receive::<1>().await?[0])
} else { } else {
Err(eyre::eyre!("read error: result is {}", buf[0])) Err(eyre::eyre!("read error: result is {}", buf[0]))
} }
} }
fn write_ram<T>(&mut self, address: T, data: u8) -> eyre::Result<()> async fn write_ram<T>(&mut self, address: T, data: u8) -> eyre::Result<()>
where where
T: Into<u8>, T: Into<u8>,
{ {
self.send_command(command(152, address.into(), data))?; self.send_command(command(152, address.into(), data))
.await?;
Ok(()) Ok(())
} }
fn read_eeprom<T>(&mut self, address: T) -> eyre::Result<u8> async fn read_eeprom<T>(&mut self, address: T) -> eyre::Result<u8>
where where
T: Into<u8>, T: Into<u8>,
{ {
self.send_command(command(72, address.into(), 0))?; self.send_command(command(72, address.into(), 0)).await?;
let buf = self.receive::<1>()?; let buf = self.receive::<1>().await?;
if buf[0] == 200 { if buf[0] == 200 {
Ok(self.receive::<1>()?[0]) Ok(self.receive::<1>().await?[0])
} else { } else {
Err(eyre::eyre!("read error: result is {}", buf[0])) Err(eyre::eyre!("read error: result is {}", buf[0]))
} }