Compare commits

...

20 Commits

Author SHA1 Message Date
atse 76547be90a i_tec -> i_set
i_tec is reserved for the voltage signal coming out of the MAX1968 chip
for now.
2024-02-14 17:27:12 +08:00
atse 8b975e656e Stop i_set from fluctuating in every report
i_set is a user-provided value that shouldn't fluctuate with every VREF
measurement. Storing i_set as channel state is the simplest way to avoid
that.
2024-02-14 17:21:39 +08:00
atse ae3d8b51d4 Disable feedback current readout on flawed HW Revs
Thermostats v2.2 and below have a noisy and offset feedback current
`tec_i` caused by missing hardware on 2 MAX1968 TEC driver pins:

1. A missing RC filter on the ITEC pin that would have isolated CPU
sampling pulses from the signal; and
2. Some missing buffering on the VREF pin that would have avoided
loading the VREF signal, preventing voltage drops from the nominal 1.5V.

Since the resulting signal `tec_i` derived from these two signals can
have an error of around +/- 100mA, and readback may affect the stability
performance of the Thermostat, disable current readback entirely on
affected hardware revisions for now.

See https://github.com/sinara-hw/Thermostat/issues/117 and
https://github.com/sinara-hw/Thermostat/issues/120.

On hardware revisions v3.x and above, this would be fixed.
2024-01-31 12:12:22 +08:00
atse 17edae44fb README: Proofread fan control documentation 2024-01-30 12:43:19 +08:00
atse 03b4561142 Refactor current_abs_max_tec_i to use uom 2024-01-30 11:41:52 +08:00
atse 631a10938d README: Remove VREF 2024-01-26 17:00:27 +08:00
atse 6cd6a6a2c2 Fix warning '...not permit being left uninit..d'
Put SocketState initialisation logic in new. This avoids using an unsafe
and unnerving MaybeUninit::uninit().assume_init() to initialise an
array, which the compiler yells at since it causes undefined behavior.
2024-01-17 15:29:56 +08:00
atse b93e2fbb7b Update rust edition 2024-01-17 15:29:56 +08:00
atse 76b95f66e0 Use latest working stable rust 2024-01-17 15:29:41 +08:00
atse 8008870bc1 Switch panic_handler to panic_halt
Move away from panic_abort as it uses intrinsics, which is nightly only.
2024-01-17 15:29:15 +08:00
atse 7646ff9037 README: Avoid deprecated OpenOCD ST-Link config
The config file interface/stlink-v2-1.cfg is deprecated, and the warning
message encourages the switch to interface/stlink.cfg. Do accordingly.
2024-01-04 12:44:52 +08:00
atse 6f81a63d12 Remove unused LED parameters 2023-09-20 15:51:37 +08:00
atse 78012f6fdd flake: Use rust from manifest, not from pkgs
Fix the rustPlatform deprecation warnings properly.
2023-09-20 11:29:38 +08:00
atse bb4f43fe1c Remove stale reference to channel_state vref 2023-08-22 17:16:25 +08:00
atse 9df0fe406f Remove VREF in reports
Since VREF is an implementation detail, there shouldn't be a need to
include it in reports.

The ChannelState vref is removed along with it as its only use was to
save VREF measurements for later reporting.
2023-08-22 11:40:42 +08:00
topquark12 5ba74c6d9b README: Correct expected TEC polarity
Adhere to the general convention of TECs cooling down with positive
voltages.
2023-08-15 16:37:51 +08:00
atse 6f0acc73b8 Update LED L3 for PID status on every cycle
Check if PID is engaged on any channel every cycle, and match the status
with LED L3.
2023-08-10 16:43:19 +08:00
atse f29e86310d Update nix repos 2023-08-09 11:23:32 +08:00
atse b04a61c414 Turn off LED L3 only when all channels have no PID
Change the behaviour of LED L3 to turn off only when all channels have
PID disengaged, as opposed to when any channel disengages PID.

Otherwise, when disengaging PID on a Thermostat that has had both
channels engaged in PID, the LED would turn off, even when PID is still
engaged on the other channel.

This lets the LED better reflect the status of the Thermostat as a
whole, as it would stay on as long as PID is engaged on at least one
channel.
2023-08-07 16:09:54 +08:00
atse cd680dd6cd README: Correct unit of time in reports 2023-07-20 17:45:16 +08:00
12 changed files with 118 additions and 102 deletions

8
Cargo.lock generated
View File

@ -327,10 +327,10 @@ dependencies = [
]
[[package]]
name = "panic-abort"
version = "0.3.2"
name = "panic-halt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e20e6499bbbc412f280b04a42346b356c6fa0753d5fd22b7bd752ff34c778ee"
checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
[[package]]
name = "panic-semihosting"
@ -563,7 +563,7 @@ dependencies = [
"nb 1.0.0",
"nom",
"num-traits",
"panic-abort",
"panic-halt",
"panic-semihosting",
"serde",
"serde-json-core",

View File

@ -7,14 +7,14 @@ authors = ["Astro <astro@spaceboyz.net>"]
version = "0.0.0"
keywords = ["thermostat", "laser", "physics"]
repository = "https://git.m-labs.hk/M-Labs/thermostat"
edition = "2018"
edition = "2021"
[package.metadata.docs.rs]
features = []
default-target = "thumbv7em-none-eabihf"
[dependencies]
panic-abort = "0.3"
panic-halt = "0.2"
panic-semihosting = { version = "0.5", optional = true }
log = "0.4"
bare-metal = "1"

View File

@ -29,7 +29,7 @@ Alternatively, you can install the Rust toolchain without Nix using rustup; see
Connect SWDIO/SWCLK/RST/GND to a programmer such as ST-Link v2.1. Run OpenOCD:
```shell
openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
```
You may need to power up the programmer before powering the device.
@ -64,7 +64,7 @@ On a Windows machine install [st.com](https://st.com) DfuSe USB device firmware
### OpenOCD
```shell
openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg -c "program target/thumbv7em-none-eabihf/release/thermostat verify reset;exit"
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program target/thumbv7em-none-eabihf/release/thermostat verify reset;exit"
```
## Network
@ -184,7 +184,7 @@ postfilter rate can be tuned with the `postfilter` command.
- Connect TEC module device 1 to TEC1- and TEC1+.
- The GND pin is for shielding not for sinking TEC module currents.
When using a TEC module with the Thermostat, the Thermostat expects the thermal load (where the thermistor is located) to heat up with a positive software current set point, and cool down with a negative current set point.
When using a TEC module with the Thermostat, the Thermostat expects the thermal load (where the thermistor is located) to cool down with a positive software current set point, and heat up with a negative current set point.
Testing heat flow direction with a low set current is recommended before installation of the TEC module.
@ -258,13 +258,12 @@ with the following keys.
| Key | Unit | Description |
| --- | :---: | --- |
| `channel` | Integer | Channel `0`, or `1` |
| `time` | Milliseconds | Temperature measurement time |
| `time` | Seconds | Temperature measurement time |
| `adc` | Volts | AD7172 input |
| `sens` | Ohms | Thermistor resistance derived from `adc` |
| `temperature` | Degrees Celsius | Steinhart-Hart conversion result derived from `sens` |
| `pid_engaged` | Boolean | `true` if in closed-loop mode |
| `i_set` | Amperes | TEC output current |
| `vref` | Volts | MAX1968 VREF (1.5 V) |
| `dac_value` | Volts | AD5680 output derived from `i_set` |
| `dac_feedback` | Volts | ADC measurement of the AD5680 output |
| `i_tec` | Volts | MAX1968 TEC current monitor |
@ -272,18 +271,19 @@ with the following keys.
| `tec_u_meas` | Volts | Measurement of the voltage across the TEC |
| `pid_output` | Amperes | PID control output |
Note: With Thermostat v2 and below, the voltage and current readouts `i_tec` and `tec_i` are disabled and null due to faulty hardware that introduces a lot of noise in the signal.
## PID Tuning
The thermostat implements a PID control loop for each of the TEC channels, more details on setting up the PID control loop can be found [here](./doc/PID%20tuning.md).
## Fan control
Fan control is available for the thermostat revisions with integrated fan system. For this purpose four commands are available:
Fan control commands are available for thermostat revisions with an integrated fan system:
1. `fan` - show fan stats: `fan_pwm`, `abs_max_tec_i`, `auto_mode`, `k_a`, `k_b`, `k_c`.
2. `fan auto` - enable auto speed controller mode, which correlates with fan curve `fcurve`.
3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to disable the fan.
2. `fan auto` - enable auto speed controller mode, where fan speed is controlled by the fan curve `fcurve`.
3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to completely disable the fan.
Please note that power doesn't correlate with the actual speed linearly.
4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `abs_max_tec_i/MAX_TEC_I`,
i.e. receives values from 0 to 1 linearly tied to the maximum current. The controlling curve should produce values from 0 to 1,
as below and beyond values would be substituted by 0 and 1 respectively.
5. `fcurve default` - restore fan curve settings to defaults: `a = 1.0, b = 0.0, c = 0.0`.
4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `abs_max_tec_i/MAX_TEC_I`, a normalized value in range [0,1],
i.e. the (linear) proportion of current output capacity used, on the channel with the largest current flow. The controlling curve is also clamped to [0,1].
5. `fcurve default` - restore fan curve coefficients to defaults: `a = 1.0, b = 0.0, c = 0.0`.

View File

@ -3,11 +3,11 @@
"mozilla-overlay": {
"flake": false,
"locked": {
"lastModified": 1638887313,
"narHash": "sha256-FMYV6rVtvSIfthgC1sK1xugh3y7muoQcvduMdriz4ag=",
"lastModified": 1690536331,
"narHash": "sha256-aRIf2FB2GTdfF7gl13WyETmiV/J7EhBGkSWXfZvlxcA=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "7c1e8b1dd6ed0043fb4ee0b12b815256b0b9de6f",
"rev": "db89c8707edcffefcd8e738459d511543a339ff5",
"type": "github"
},
"original": {
@ -18,16 +18,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1641870998,
"narHash": "sha256-6HkxR2WZsm37VoQS7jgp6Omd71iw6t1kP8bDbaqCDuI=",
"lastModified": 1691421349,
"narHash": "sha256-RRJyX0CUrs4uW4gMhd/X4rcDG8PTgaaCQM5rXEJOx6g=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "386234e2a61e1e8acf94dfa3a3d3ca19a6776efb",
"rev": "011567f35433879aae5024fc6ec53f2a0568a6c4",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-21.11",
"ref": "nixos-23.05",
"repo": "nixpkgs",
"type": "github"
}

View File

@ -1,15 +1,15 @@
{
description = "Firmware for the Sinara 8451 Thermostat";
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-21.11;
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-23.05;
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
outputs = { self, nixpkgs, mozilla-overlay }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
rustManifest = pkgs.fetchurl {
url = "https://static.rust-lang.org/dist/2021-10-26/channel-rust-nightly.toml";
sha256 = "sha256-1hLbypXA+nuH7o3AHCokzSBZAvQxvef4x9+XxO3aBao=";
url = "https://static.rust-lang.org/dist/2022-12-15/channel-rust-stable.toml";
hash = "sha256-S7epLlflwt0d1GZP44u5Xosgf6dRrmr8xxC+Ml2Pq7c=";
};
targets = [
@ -22,12 +22,12 @@
inherit targets;
extensions = ["rust-src"];
};
rust = rustChannelOfTargets "nightly" null targets;
rust = rustChannelOfTargets "stable" null targets;
rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
rustc = rust;
cargo = rust;
});
thermostat = rustPlatform.buildRustPackage rec {
thermostat = rustPlatform.buildRustPackage {
name = "thermostat";
version = "0.0.0";
@ -67,9 +67,7 @@
devShell.x86_64-linux = pkgs.mkShell {
name = "thermostat-dev-shell";
buildInputs = with pkgs; [
rustPlatform.rust.rustc
rustPlatform.rust.cargo
openocd dfu-util
rust openocd dfu-util
] ++ (with python3Packages; [
numpy matplotlib
]);

View File

@ -2,11 +2,13 @@ use smoltcp::time::{Duration, Instant};
use uom::si::{
f64::{
ElectricPotential,
ElectricCurrent,
ElectricalResistance,
ThermodynamicTemperature,
Time,
},
electric_potential::volt,
electric_current::ampere,
electrical_resistance::ohm,
thermodynamic_temperature::degree_celsius,
time::millisecond,
@ -26,11 +28,10 @@ pub struct ChannelState {
pub adc_calibration: ad7172::ChannelCalibration,
pub adc_time: Instant,
pub adc_interval: Duration,
/// VREF for the TEC (1.5V)
pub vref: ElectricPotential,
/// i_set 0A center point
pub center: CenterPoint,
pub dac_value: ElectricPotential,
pub i_set: ElectricCurrent,
pub pid_engaged: bool,
pub pid: pid::Controller,
pub sh: sh::Parameters,
@ -44,10 +45,9 @@ impl ChannelState {
adc_time: Instant::from_secs(0),
// default: 10 Hz
adc_interval: Duration::from_millis(100),
// updated later with Channels.read_vref()
vref: ElectricPotential::new::<volt>(1.5),
center: CenterPoint::Vref,
dac_value: ElectricPotential::new::<volt>(0.0),
i_set: ElectricCurrent::new::<ampere>(0.0),
pid_engaged: false,
pid: pid::Controller::new(pid::Parameters::default()),
sh: sh::Parameters::default(),

View File

@ -20,6 +20,7 @@ use crate::{
command_handler::JsonBuffer,
pins,
steinhart_hart,
hw_rev,
};
pub const CHANNELS: usize = 2;
@ -28,17 +29,18 @@ pub const R_SENSE: f64 = 0.05;
const DAC_OUT_V_MAX: f64 = 3.0;
// TODO: -pub
pub struct Channels {
pub struct Channels<'a> {
channel0: Channel<Channel0>,
channel1: Channel<Channel1>,
pub adc: ad7172::Adc<pins::AdcSpi, pins::AdcNss>,
/// stm32f4 integrated adc
pins_adc: pins::PinsAdc,
pub pwm: pins::PwmPins,
hwrev: &'a hw_rev::HWRev,
}
impl Channels {
pub fn new(pins: pins::Pins) -> Self {
impl<'a> Channels<'a> {
pub fn new(pins: pins::Pins, hwrev: &'a hw_rev::HWRev) -> Self {
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
// Feature not used
adc.set_sync_enable(false).unwrap();
@ -56,9 +58,8 @@ impl Channels {
let channel1 = Channel::new(pins.channel1, adc_calibration1);
let pins_adc = pins.pins_adc;
let pwm = pins.pwm;
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm };
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm, hwrev };
for channel in 0..CHANNELS {
channels.channel_state(channel).vref = channels.read_vref(channel);
channels.calibrate_dac_value(channel);
channels.set_i(channel, ElectricCurrent::new::<ampere>(0.0));
}
@ -98,11 +99,8 @@ impl Channels {
/// calculate the TEC i_set centerpoint
pub fn get_center(&mut self, channel: usize) -> ElectricPotential {
match self.channel_state(channel).center {
CenterPoint::Vref => {
let vref = self.read_vref(channel);
self.channel_state(channel).vref = vref;
vref
},
CenterPoint::Vref =>
self.read_vref(channel),
CenterPoint::Override(center_point) =>
ElectricPotential::new::<volt>(center_point.into()),
}
@ -115,11 +113,8 @@ impl Channels {
}
pub fn get_i(&mut self, channel: usize) -> ElectricCurrent {
let center_point = self.get_center(channel);
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
let voltage = self.get_dac(channel);
let i_tec = (voltage - center_point) / (10.0 * r_sense);
i_tec
let i_set = self.channel_state(channel).i_set;
i_set
}
/// i_set DAC
@ -134,7 +129,7 @@ impl Channels {
voltage
}
pub fn set_i(&mut self, channel: usize, i_tec: ElectricCurrent) -> ElectricCurrent {
pub fn set_i(&mut self, channel: usize, i_set: ElectricCurrent) -> ElectricCurrent {
let vref_meas = match channel.into() {
0 => self.channel0.vref_meas,
1 => self.channel1.vref_meas,
@ -142,10 +137,11 @@ impl Channels {
};
let center_point = vref_meas;
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
let voltage = i_tec * 10.0 * r_sense + center_point;
let voltage = i_set * 10.0 * r_sense + center_point;
let voltage = self.set_dac(channel, voltage);
let i_tec = (voltage - center_point) / (10.0 * r_sense);
i_tec
let i_set = (voltage - center_point) / (10.0 * r_sense);
self.channel_state(channel).i_set = i_set;
i_set
}
pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential {
@ -432,10 +428,9 @@ impl Channels {
}
fn report(&mut self, channel: usize) -> Report {
let vref = self.channel_state(channel).vref;
let i_set = self.get_i(channel);
let i_tec = self.read_itec(channel);
let tec_i = self.get_tec_i(channel);
let i_tec = if self.hwrev.major > 2 {Some(self.read_itec(channel))} else {None};
let tec_i = if self.hwrev.major > 2 {Some(self.get_tec_i(channel))} else {None};
let dac_value = self.get_dac(channel);
let state = self.channel_state(channel);
let pid_output = ElectricCurrent::new::<ampere>(state.pid.y1);
@ -449,7 +444,6 @@ impl Channels {
.map(|temperature| temperature.get::<degree_celsius>()),
pid_engaged: state.pid_engaged,
i_set,
vref,
dac_value,
dac_feedback: self.read_dac_feedback(channel),
i_tec,
@ -475,6 +469,15 @@ impl Channels {
serde_json_core::to_vec(&summaries)
}
pub fn pid_engaged(&mut self) -> bool {
for channel in 0..CHANNELS {
if self.channel_state(channel).pid_engaged {
return true;
}
}
false
}
fn pwm_summary(&mut self, channel: usize) -> PwmSummary {
PwmSummary {
channel,
@ -521,9 +524,9 @@ impl Channels {
serde_json_core::to_vec(&summaries)
}
pub fn current_abs_max_tec_i(&mut self) -> f64 {
max_by(self.get_tec_i(0).abs().get::<ampere>(),
self.get_tec_i(1).abs().get::<ampere>(),
pub fn current_abs_max_tec_i(&mut self) -> ElectricCurrent {
max_by(self.get_tec_i(0).abs(),
self.get_tec_i(1).abs(),
|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal))
}
}
@ -538,11 +541,10 @@ pub struct Report {
temperature: Option<f64>,
pid_engaged: bool,
i_set: ElectricCurrent,
vref: ElectricPotential,
dac_value: ElectricPotential,
dac_feedback: ElectricPotential,
i_tec: ElectricPotential,
tec_i: ElectricCurrent,
i_tec: Option<ElectricPotential>,
tec_i: Option<ElectricCurrent>,
tec_u_meas: ElectricPotential,
pid_output: ElectricCurrent,
}

View File

@ -13,7 +13,6 @@ use super::{
PwmPin,
ShParameter
},
leds::Leds,
ad7172,
CHANNEL_CONFIG_KEY,
channels::{
@ -176,18 +175,16 @@ impl Handler {
Ok(Handler::Handled)
}
fn engage_pid (socket: &mut TcpSocket, channels: &mut Channels, leds: &mut Leds, channel: usize) -> Result<Handler, Error> {
fn engage_pid (socket: &mut TcpSocket, channels: &mut Channels, channel: usize) -> Result<Handler, Error> {
channels.channel_state(channel).pid_engaged = true;
leds.g3.on();
send_line(socket, b"{}");
Ok(Handler::Handled)
}
fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, leds: &mut Leds, channel: usize, pin: PwmPin, value: f64) -> Result<Handler, Error> {
fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, pin: PwmPin, value: f64) -> Result<Handler, Error> {
match pin {
PwmPin::ISet => {
channels.channel_state(channel).pid_engaged = false;
leds.g3.off();
let current = ElectricCurrent::new::<ampere>(value);
channels.set_i(channel, current);
channels.power_up(channel);
@ -210,11 +207,11 @@ impl Handler {
}
fn set_center_point(socket: &mut TcpSocket, channels: &mut Channels, channel: usize, center: CenterPoint) -> Result<Handler, Error> {
let i_tec = channels.get_i(channel);
let i_set = channels.get_i(channel);
let state = channels.channel_state(channel);
state.center = center;
if !state.pid_engaged {
channels.set_i(channel, i_tec);
channels.set_i(channel, i_set);
}
send_line(socket, b"{}");
Ok(Handler::Handled)
@ -415,7 +412,7 @@ impl Handler {
}
}
pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, leds: &mut Leds, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result<Self, Error> {
pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result<Self, Error> {
match command {
Command::Quit => Ok(Handler::CloseSocket),
Command::Reporting(_reporting) => Handler::reporting(socket),
@ -426,8 +423,8 @@ impl Handler {
Command::Show(ShowCommand::SteinhartHart) => Handler::show_steinhart_hart(socket, channels),
Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels),
Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config),
Command::PwmPid { channel } => Handler::engage_pid(socket, channels, leds, channel),
Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, leds, channel, pin, value),
Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel),
Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, channel, pin, value),
Command::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center),
Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value),
Command::SteinhartHart { channel, parameter, value } => Handler::set_steinhart_hart(socket, channels, channel, parameter, value),

View File

@ -1,3 +1,4 @@
use core::arch::asm;
use cortex_m_rt::pre_init;
use stm32f4xx_hal::stm32::{RCC, SYSCFG};

View File

@ -4,7 +4,10 @@ use stm32f4xx_hal::{
pwm::{self, PwmChannels},
pac::TIM8,
};
use uom::si::{
f64::ElectricCurrent,
electric_current::ampere,
};
use crate::{
hw_rev::HWSettings,
command_handler::JsonBuffer,
@ -50,8 +53,8 @@ impl FanCtrl {
fan_ctrl
}
pub fn cycle(&mut self, abs_max_tec_i: f32) {
self.abs_max_tec_i = abs_max_tec_i;
pub fn cycle(&mut self, abs_max_tec_i: ElectricCurrent) {
self.abs_max_tec_i = abs_max_tec_i.get::<ampere>() as f32;
if self.fan_auto && self.hw_settings.fan_available {
let scaled_current = self.abs_max_tec_i / MAX_TEC_I;
// do not limit upper bound, as it will be limited in the set_pwm()

View File

@ -1,11 +1,10 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]
#![feature(maybe_uninit_extra, asm)]
#![cfg_attr(test, allow(unused))]
// TODO: #![deny(warnings, unused)]
#[cfg(not(any(feature = "semihosting", test)))]
use panic_abort as _;
use panic_halt as _;
#[cfg(all(feature = "semihosting", not(test)))]
use panic_semihosting as _;
@ -139,7 +138,7 @@ fn main() -> ! {
let mut store = flash_store::store(dp.FLASH);
let mut channels = Channels::new(pins);
let mut channels = Channels::new(pins, &hwrev);
for c in 0..CHANNELS {
match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) {
Ok(Some(config)) =>
@ -186,7 +185,13 @@ fn main() -> ! {
server.for_each(|_, session| session.set_report_pending(channel.into()));
}
fan_ctrl.cycle(channels.current_abs_max_tec_i() as f32);
fan_ctrl.cycle(channels.current_abs_max_tec_i());
if channels.pid_engaged() {
leds.g3.on();
} else {
leds.g3.off();
}
let instant = Instant::from_millis(i64::from(timer::now()));
cortex_m::interrupt::free(net::clear_pending);
@ -211,7 +216,7 @@ fn main() -> ! {
// Do nothing and feed more data to the line reader in the next loop cycle.
Ok(SessionInput::Nothing) => {}
Ok(SessionInput::Command(command)) => {
match Handler::handle_command(command, &mut socket, &mut channels, session, &mut leds, &mut store, &mut ipv4_config, &mut fan_ctrl, hwrev) {
match Handler::handle_command(command, &mut socket, &mut channels, session, &mut store, &mut ipv4_config, &mut fan_ctrl, hwrev) {
Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
Ok(Handler::Handled) => {},
Ok(Handler::CloseSocket) => socket.close(),

View File

@ -1,4 +1,3 @@
use core::mem::MaybeUninit;
use smoltcp::{
iface::EthernetInterface,
socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef},
@ -13,6 +12,18 @@ pub struct SocketState<S> {
state: S,
}
impl<'a, S: Default> SocketState<S>{
fn new(sockets: &mut SocketSet<'a>, tcp_rx_storage: &'a mut [u8; TCP_RX_BUFFER_SIZE], tcp_tx_storage: &'a mut [u8; TCP_TX_BUFFER_SIZE]) -> SocketState<S> {
let tcp_rx_buffer = TcpSocketBuffer::new(&mut tcp_rx_storage[..]);
let tcp_tx_buffer = TcpSocketBuffer::new(&mut tcp_tx_storage[..]);
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
SocketState::<S> {
handle: sockets.add(tcp_socket),
state: S::default()
}
}
}
/// Number of server sockets and therefore concurrent client
/// sessions. Many data structures in `Server::run()` correspond to
/// this const.
@ -35,28 +46,27 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> {
where
F: FnOnce(&mut Server<'a, '_, S>),
{
let mut sockets_storage: [_; SOCKET_COUNT] = Default::default();
let mut sockets = SocketSet::new(&mut sockets_storage[..]);
let mut states: [SocketState<S>; SOCKET_COUNT] = unsafe { MaybeUninit::uninit().assume_init() };
macro_rules! create_socket {
($set:ident, $rx_storage:ident, $tx_storage:ident, $target:expr) => {
macro_rules! create_rtx_storage {
($rx_storage:ident, $tx_storage:ident) => {
let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE];
let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE];
let tcp_rx_buffer = TcpSocketBuffer::new(&mut $rx_storage[..]);
let tcp_tx_buffer = TcpSocketBuffer::new(&mut $tx_storage[..]);
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
$target = $set.add(tcp_socket);
}
}
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, states[0].handle);
create_socket!(sockets, tcp_rx_storage1, tcp_tx_storage1, states[1].handle);
create_socket!(sockets, tcp_rx_storage2, tcp_tx_storage2, states[2].handle);
create_socket!(sockets, tcp_rx_storage3, tcp_tx_storage3, states[3].handle);
for state in &mut states {
state.state = S::default();
}
create_rtx_storage!(tcp_rx_storage0, tcp_tx_storage0);
create_rtx_storage!(tcp_rx_storage1, tcp_tx_storage1);
create_rtx_storage!(tcp_rx_storage2, tcp_tx_storage2);
create_rtx_storage!(tcp_rx_storage3, tcp_tx_storage3);
let mut sockets_storage: [_; SOCKET_COUNT] = Default::default();
let mut sockets = SocketSet::new(&mut sockets_storage[..]);
let states: [SocketState<S>; SOCKET_COUNT] = [
SocketState::<S>::new(&mut sockets, &mut tcp_rx_storage0, &mut tcp_tx_storage0),
SocketState::<S>::new(&mut sockets, &mut tcp_rx_storage1, &mut tcp_tx_storage1),
SocketState::<S>::new(&mut sockets, &mut tcp_rx_storage2, &mut tcp_tx_storage2),
SocketState::<S>::new(&mut sockets, &mut tcp_rx_storage3, &mut tcp_tx_storage3),
];
let mut server = Server {
states,