Compare commits

..

No commits in common. "980d27ebfc80d6e3543871bead6f91cc0085a2d0" and "b7e6cdbec23cde9caac94377ac1f9d86373636f2" have entirely different histories.

6 changed files with 27 additions and 46 deletions

View File

@ -110,8 +110,8 @@ formatted as line-delimited JSON.
| `pid` | Show PID configuration | | `pid` | Show PID configuration |
| `pid <0/1> target <deg_celsius>` | Set the PID controller target temperature | | `pid <0/1> target <deg_celsius>` | Set the PID controller target temperature |
| `pid <0/1> kp <value>` | Set proportional gain | | `pid <0/1> kp <value>` | Set proportional gain |
| `pid <0/1> ki <value>` | Set integral gain | | `pid <0/1> ki <value>` | Set integral gain (unit: 10 Hz) |
| `pid <0/1> kd <value>` | Set differential gain | | `pid <0/1> kd <value>` | Set differential gain (unit: 0.1 seconds) |
| `pid <0/1> output_min <amp>` | Set mininum output | | `pid <0/1> output_min <amp>` | Set mininum output |
| `pid <0/1> output_max <amp>` | Set maximum output | | `pid <0/1> output_max <amp>` | Set maximum output |
| `pid <0/1> integral_min <value>` | Set integral lower bound | | `pid <0/1> integral_min <value>` | Set integral lower bound |

View File

@ -44,22 +44,26 @@ series = {
series_lock = Lock() series_lock = Lock()
quit = False quit = False
last_packet_time = None
def recv_data(tec): def recv_data(tec):
global last_packet_time global last_packet_time
for data in tec.report_mode(): for data in tec.report_mode():
ch0 = data[0] if data['channel'] == 0:
series_lock.acquire() series_lock.acquire()
try: try:
time = ch0['time'] / 1000.0 time = data['time'] / 1000.0
if last_packet_time:
data['interval'] = time - last_packet_time
last_packet_time = time
for k, s in series.items(): for k, s in series.items():
if k in ch0: if k in data:
v = ch0[k] v = data[k]
if type(v) is float: if type(v) is float:
s.append(time, v) s.append(time, v)
finally: finally:
series_lock.release() series_lock.release()
if quit: if quit:
break break

View File

@ -164,3 +164,4 @@ class Client:
def load_config(self): def load_config(self):
"""Load current configuration from EEPROM""" """Load current configuration from EEPROM"""
self._command("load") self._command("load")

View File

@ -1,15 +1,13 @@
use smoltcp::time::{Duration, Instant}; use smoltcp::time::Instant;
use uom::si::{ use uom::si::{
f64::{ f64::{
ElectricPotential, ElectricPotential,
ElectricalResistance, ElectricalResistance,
ThermodynamicTemperature, ThermodynamicTemperature,
Time,
}, },
electric_potential::volt, electric_potential::volt,
electrical_resistance::ohm, electrical_resistance::ohm,
thermodynamic_temperature::degree_celsius, thermodynamic_temperature::degree_celsius,
time::millisecond,
}; };
use crate::{ use crate::{
ad7172, ad7172,
@ -25,7 +23,6 @@ pub struct ChannelState {
pub adc_data: Option<u32>, pub adc_data: Option<u32>,
pub adc_calibration: ad7172::ChannelCalibration, pub adc_calibration: ad7172::ChannelCalibration,
pub adc_time: Instant, pub adc_time: Instant,
pub adc_interval: Duration,
/// VREF for the TEC (1.5V) /// VREF for the TEC (1.5V)
pub vref: ElectricPotential, pub vref: ElectricPotential,
/// i_set 0A center point /// i_set 0A center point
@ -42,8 +39,6 @@ impl ChannelState {
adc_data: None, adc_data: None,
adc_calibration, adc_calibration,
adc_time: Instant::from_secs(0), adc_time: Instant::from_secs(0),
// default: 10 Hz
adc_interval: Duration::from_millis(100),
// updated later with Channels.read_vref() // updated later with Channels.read_vref()
vref: ElectricPotential::new::<volt>(1.5), vref: ElectricPotential::new::<volt>(1.5),
center: CenterPoint::Vref, center: CenterPoint::Vref,
@ -61,7 +56,6 @@ impl ChannelState {
} else { } else {
Some(adc_data) Some(adc_data)
}; };
self.adc_interval = now - self.adc_time;
self.adc_time = now; self.adc_time = now;
} }
@ -69,18 +63,10 @@ impl ChannelState {
pub fn update_pid(&mut self) -> Option<f64> { pub fn update_pid(&mut self) -> Option<f64> {
let temperature = self.get_temperature()? let temperature = self.get_temperature()?
.get::<degree_celsius>(); .get::<degree_celsius>();
let pid_output = self.pid.update(temperature, self.get_adc_interval()); let pid_output = self.pid.update(temperature);
Some(pid_output) Some(pid_output)
} }
pub fn get_adc_time(&self) -> Time {
Time::new::<millisecond>(self.adc_time.total_millis() as f64)
}
pub fn get_adc_interval(&self) -> Time {
Time::new::<millisecond>(self.adc_interval.total_millis() as f64)
}
pub fn get_adc(&self) -> Option<ElectricPotential> { pub fn get_adc(&self) -> Option<ElectricPotential> {
Some(self.adc_calibration.convert_data(self.adc_data?)) Some(self.adc_calibration.convert_data(self.adc_data?))
} }

View File

@ -3,7 +3,7 @@ use serde::{Serialize, Serializer};
use smoltcp::time::Instant; use smoltcp::time::Instant;
use stm32f4xx_hal::hal; use stm32f4xx_hal::hal;
use uom::si::{ use uom::si::{
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time}, f64::{ElectricCurrent, ElectricPotential, ElectricalResistance},
electric_potential::{millivolt, volt}, electric_potential::{millivolt, volt},
electric_current::ampere, electric_current::ampere,
electrical_resistance::ohm, electrical_resistance::ohm,
@ -425,8 +425,7 @@ impl Channels {
); );
Report { Report {
channel, channel,
time: state.get_adc_time(), time: state.adc_time.total_millis(),
interval: state.get_adc_interval(),
adc: state.get_adc(), adc: state.get_adc(),
sens: state.get_sens(), sens: state.get_sens(),
temperature: state.get_temperature() temperature: state.get_temperature()
@ -511,8 +510,7 @@ type JsonBuffer = Vec<u8, U1024>;
#[derive(Serialize)] #[derive(Serialize)]
pub struct Report { pub struct Report {
channel: usize, channel: usize,
time: Time, time: i64,
interval: Time,
adc: Option<ElectricPotential>, adc: Option<ElectricPotential>,
sens: Option<ElectricalResistance>, sens: Option<ElectricalResistance>,
temperature: Option<f64>, temperature: Option<f64>,

View File

@ -1,8 +1,4 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use uom::si::{
f64::Time,
time::second,
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Parameters { pub struct Parameters {
@ -49,9 +45,7 @@ impl Controller {
} }
} }
pub fn update(&mut self, input: f64, time_delta: Time) -> f64 { pub fn update(&mut self, input: f64) -> f64 {
let time_delta = time_delta.get::<second>();
// error // error
let error = input - self.target; let error = input - self.target;
@ -59,7 +53,7 @@ impl Controller {
let p = f64::from(self.parameters.kp) * error; let p = f64::from(self.parameters.kp) * error;
// integral // integral
self.integral += f64::from(self.parameters.ki) * error * time_delta; self.integral += f64::from(self.parameters.ki) * error;
if self.integral < self.parameters.integral_min.into() { if self.integral < self.parameters.integral_min.into() {
self.integral = self.parameters.integral_min.into(); self.integral = self.parameters.integral_min.into();
} }
@ -70,10 +64,8 @@ impl Controller {
// derivative // derivative
let d = match self.last_input { let d = match self.last_input {
None => None => 0.0,
0.0, Some(last_input) => f64::from(self.parameters.kd) * (input - last_input),
Some(last_input) =>
f64::from(self.parameters.kd) * (input - last_input) / time_delta,
}; };
self.last_input = Some(input); self.last_input = Some(input);