Compare commits
5 Commits
026dd1ed9c
...
59103cb2a1
Author | SHA1 | Date |
---|---|---|
Astro | 59103cb2a1 | |
Astro | 5acebbef9f | |
Astro | 12e713dc19 | |
Astro | 1b4a030e7e | |
Astro | 438da74721 |
|
@ -69,10 +69,11 @@ The scope of this setting is per TCP session.
|
||||||
| `pid <0/1> integral_min <value>` | Set integral lower bound |
|
| `pid <0/1> integral_min <value>` | Set integral lower bound |
|
||||||
| `pid <0/1> integral_max <value>` | Set integral upper bound |
|
| `pid <0/1> integral_max <value>` | Set integral upper bound |
|
||||||
| `s-h` | Show Steinhart-Hart equation parameters |
|
| `s-h` | Show Steinhart-Hart equation parameters |
|
||||||
| `s-h <0/1> <t/b/r0> <value>` | Set Steinhart-Hart parameter for a channel |
|
| `s-h <0/1> <t0/b/r0> <value>` | Set Steinhart-Hart parameter for a channel |
|
||||||
| `postfilter` | Show postfilter settings |
|
| `postfilter` | Show postfilter settings |
|
||||||
| `postfilter <0/1> off` | Disable postfilter |
|
| `postfilter <0/1> off` | Disable postfilter |
|
||||||
| `postfilter <0/1> rate <rate>` | Set postfilter output data rate |
|
| `postfilter <0/1> rate <rate>` | Set postfilter output data rate |
|
||||||
| `load` | Restore configuration from EEPROM |
|
| `load` | Restore configuration from EEPROM |
|
||||||
| `save` | Save configuration to EEPROM |
|
| `save` | Save configuration to EEPROM |
|
||||||
| `reset` | Reset the device |
|
| `reset` | Reset the device |
|
||||||
|
| `ipv4 <X.X.X.X>` | Configure IPv4 address |
|
||||||
|
|
|
@ -34,6 +34,7 @@ series = {
|
||||||
'i_set': Series(),
|
'i_set': Series(),
|
||||||
'pid_output': Series(),
|
'pid_output': Series(),
|
||||||
'vref': Series(),
|
'vref': Series(),
|
||||||
|
'dac_value': Series(),
|
||||||
'dac_feedback': Series(),
|
'dac_feedback': Series(),
|
||||||
'i_tec': Series(),
|
'i_tec': Series(),
|
||||||
'tec_i': Series(),
|
'tec_i': Series(),
|
||||||
|
|
|
@ -34,24 +34,92 @@ class Client:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_pwm(self):
|
def get_pwm(self):
|
||||||
|
"""Retrieve PWM limits for the TEC
|
||||||
|
|
||||||
|
Example::
|
||||||
|
[{'channel': 0,
|
||||||
|
'center': 'vref',
|
||||||
|
'i_set': {'max': 2.9802790335151985, 'value': -0.02002179650216762},
|
||||||
|
'max_i_neg': {'max': 3.0, 'value': 3.0},
|
||||||
|
'max_v': {'max': 5.988, 'value': 5.988},
|
||||||
|
'max_i_pos': {'max': 3.0, 'value': 3.0}},
|
||||||
|
{'channel': 1,
|
||||||
|
'center': 'vref',
|
||||||
|
'i_set': {'max': 2.9802790335151985, 'value': -0.02002179650216762},
|
||||||
|
'max_i_neg': {'max': 3.0, 'value': 3.0},
|
||||||
|
'max_v': {'max': 5.988, 'value': 5.988},
|
||||||
|
'max_i_pos': {'max': 3.0, 'value': 3.0}}
|
||||||
|
]
|
||||||
|
"""
|
||||||
return self._get_conf("pwm")
|
return self._get_conf("pwm")
|
||||||
|
|
||||||
def get_pid(self):
|
def get_pid(self):
|
||||||
|
"""Retrieve PID control state
|
||||||
|
|
||||||
|
Example::
|
||||||
|
[{'channel': 0,
|
||||||
|
'parameters': {
|
||||||
|
'kp': 10.0,
|
||||||
|
'ki': 0.02,
|
||||||
|
'kd': 0.0,
|
||||||
|
'output_min': 0.0,
|
||||||
|
'output_max': 3.0,
|
||||||
|
'integral_min': -100.0,
|
||||||
|
'integral_max': 100.0},
|
||||||
|
'target': 37.0,
|
||||||
|
'integral': 38.41138597026372},
|
||||||
|
{'channel': 1,
|
||||||
|
'parameters': {
|
||||||
|
'kp': 10.0,
|
||||||
|
'ki': 0.02,
|
||||||
|
'kd': 0.0,
|
||||||
|
'output_min': 0.0,
|
||||||
|
'output_max': 3.0,
|
||||||
|
'integral_min': -100.0,
|
||||||
|
'integral_max': 100.0},
|
||||||
|
'target': 36.5,
|
||||||
|
'integral': nan}]
|
||||||
|
"""
|
||||||
return self._get_conf("pid")
|
return self._get_conf("pid")
|
||||||
|
|
||||||
def get_steinhart_hart(self):
|
def get_steinhart_hart(self):
|
||||||
|
"""Retrieve Steinhart-Hart parameters for resistance to temperature conversion
|
||||||
|
|
||||||
|
Example::
|
||||||
|
[{'params': {'b': 3800.0, 'r0': 10000.0, 't0': 298.15}, 'channel': 0},
|
||||||
|
{'params': {'b': 3800.0, 'r0': 10000.0, 't0': 298.15}, 'channel': 1}]
|
||||||
|
"""
|
||||||
return self._get_conf("s-h")
|
return self._get_conf("s-h")
|
||||||
|
|
||||||
def get_postfilter(self):
|
def get_postfilter(self):
|
||||||
|
"""Retrieve DAC postfilter configuration
|
||||||
|
|
||||||
|
Example::
|
||||||
|
[{'rate': None, 'channel': 0},
|
||||||
|
{'rate': 21.25, 'channel': 1}]
|
||||||
|
"""
|
||||||
return self._get_conf("postfilter")
|
return self._get_conf("postfilter")
|
||||||
|
|
||||||
def report_mode(self):
|
def report_mode(self):
|
||||||
"""Start reporting measurement values
|
"""Start reporting measurement values
|
||||||
|
|
||||||
|
Example of yielded data::
|
||||||
|
{'channel': 0,
|
||||||
|
'time': 2302524,
|
||||||
|
'adc': 0.6199188965423515,
|
||||||
|
'sens': 6138.519310282602,
|
||||||
|
'temperature': 36.87032392655527,
|
||||||
|
'pid_engaged': True,
|
||||||
|
'i_set': 2.0635816680889123,
|
||||||
|
'vref': 1.494,
|
||||||
|
'dac_value': 2.527790834044456,
|
||||||
|
'dac_feedback': 2.523,
|
||||||
|
'i_tec': 2.331,
|
||||||
|
'tec_i': 2.0925,
|
||||||
|
'tec_u_meas': 2.5340000000000003,
|
||||||
|
'pid_output': 2.067581958092247}
|
||||||
"""
|
"""
|
||||||
self._command("report mode", "on")
|
self._command("report mode", "on")
|
||||||
self._read_line()
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
line = self._read_line()
|
line = self._read_line()
|
||||||
|
@ -63,15 +131,33 @@ class Client:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_param(self, topic, channel, field="", value=""):
|
def set_param(self, topic, channel, field="", value=""):
|
||||||
|
"""Set configuration parameters
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
tec.set_param("pwm", 0, "max_v", 2.0)
|
||||||
|
tec.set_param("pid", 1, "output_max", 2.5)
|
||||||
|
tec.set_param("s-h", 0, "t0", 20.0)
|
||||||
|
tec.set_param("center", 0, "vref")
|
||||||
|
tec.set_param("postfilter", 1, 21)
|
||||||
|
|
||||||
|
See the firmware's README.md for a full list.
|
||||||
|
"""
|
||||||
if type(value) is float:
|
if type(value) is float:
|
||||||
value = "{:f}".format(value)
|
value = "{:f}".format(value)
|
||||||
if type(value) is not str:
|
if type(value) is not str:
|
||||||
value = str(value)
|
value = str(value)
|
||||||
self._command(topic, str(channel), field, value)
|
self._command(topic, str(channel), field, value)
|
||||||
|
|
||||||
# read response line
|
|
||||||
self._read_line()
|
|
||||||
|
|
||||||
def power_up(self, channel, target):
|
def power_up(self, channel, target):
|
||||||
|
"""Start closed-loop mode"""
|
||||||
self.set_param("pid", channel, "target", value=target)
|
self.set_param("pid", channel, "target", value=target)
|
||||||
self.set_param("pwm", channel, "pid")
|
self.set_param("pwm", channel, "pid")
|
||||||
|
|
||||||
|
def save_config(self):
|
||||||
|
"""Save current configuration to EEPROM"""
|
||||||
|
self._command("save")
|
||||||
|
|
||||||
|
def load_config(self):
|
||||||
|
"""Load current configuration from EEPROM"""
|
||||||
|
self._command("load")
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from pytec.client import Client
|
from pytec.client import Client
|
||||||
|
|
||||||
tec = Client() #(host="localhost", port=6667)
|
tec = Client() #(host="localhost", port=6667)
|
||||||
tec.set_param("s-h", 0, "t", 20)
|
tec.set_param("s-h", 1, "t0", 20)
|
||||||
|
print(tec.get_pid())
|
||||||
|
print(tec.get_steinhart_hart())
|
||||||
for data in tec.report_mode():
|
for data in tec.report_mode():
|
||||||
print(data)
|
print(data)
|
||||||
|
|
|
@ -135,6 +135,7 @@ pub enum Command {
|
||||||
Load,
|
Load,
|
||||||
Save,
|
Save,
|
||||||
Reset,
|
Reset,
|
||||||
|
Ipv4([u8; 4]),
|
||||||
Show(ShowCommand),
|
Show(ShowCommand),
|
||||||
Reporting(bool),
|
Reporting(bool),
|
||||||
/// PWM parameter setting
|
/// PWM parameter setting
|
||||||
|
@ -181,6 +182,19 @@ fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
|
||||||
fold_many1(char(' '), (), |(), _| ())(input)
|
fold_many1(char(' '), (), |(), _| ())(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u32, Error>> {
|
||||||
|
take_while1(is_digit)(input)
|
||||||
|
.map(|(input, digits)| {
|
||||||
|
let result =
|
||||||
|
from_utf8(digits)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
.and_then(|digits| u32::from_str_radix(digits, 10)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
);
|
||||||
|
(input, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> {
|
fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> {
|
||||||
let (input, sign) = opt(is_a("-"))(input)?;
|
let (input, sign) = opt(is_a("-"))(input)?;
|
||||||
let negative = sign.is_some();
|
let negative = sign.is_some();
|
||||||
|
@ -415,11 +429,30 @@ fn postfilter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ipv4(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
|
let (input, _) = tag("ipv4")(input)?;
|
||||||
|
let (input, _) = whitespace(input)?;
|
||||||
|
let (input, a) = unsigned(input)?;
|
||||||
|
let (input, _) = tag(".")(input)?;
|
||||||
|
let (input, b) = unsigned(input)?;
|
||||||
|
let (input, _) = tag(".")(input)?;
|
||||||
|
let (input, c) = unsigned(input)?;
|
||||||
|
let (input, _) = tag(".")(input)?;
|
||||||
|
let (input, d) = unsigned(input)?;
|
||||||
|
end(input)?;
|
||||||
|
|
||||||
|
let result = a.and_then(|a| b.and_then(|b| c.and_then(|c| d.map(|d|
|
||||||
|
Command::Ipv4([a as u8, b as u8, c as u8, d as u8])
|
||||||
|
))));
|
||||||
|
Ok((input, result))
|
||||||
|
}
|
||||||
|
|
||||||
fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
alt((value(Ok(Command::Quit), tag("quit")),
|
alt((value(Ok(Command::Quit), tag("quit")),
|
||||||
value(Ok(Command::Load), tag("load")),
|
value(Ok(Command::Load), tag("load")),
|
||||||
value(Ok(Command::Save), tag("save")),
|
value(Ok(Command::Save), tag("save")),
|
||||||
value(Ok(Command::Reset), tag("reset")),
|
value(Ok(Command::Reset), tag("reset")),
|
||||||
|
ipv4,
|
||||||
map(report, Ok),
|
map(report, Ok),
|
||||||
pwm,
|
pwm,
|
||||||
center_point,
|
center_point,
|
||||||
|
@ -464,6 +497,12 @@ mod test {
|
||||||
assert_eq!(command, Ok(Command::Save));
|
assert_eq!(command, Ok(Command::Save));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_ipv4() {
|
||||||
|
let command = Command::parse(b"ipv4 192.168.1.26");
|
||||||
|
assert_eq!(command, Ok(Command::Ipv4([192, 168, 1, 26])));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_report() {
|
fn parse_report() {
|
||||||
let command = Command::parse(b"report");
|
let command = Command::parse(b"report");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use postcard::{from_bytes, to_slice};
|
use postcard::{from_bytes, to_slice};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use smoltcp::wire::Ipv4Address;
|
||||||
use stm32f4xx_hal::i2c;
|
use stm32f4xx_hal::i2c;
|
||||||
use uom::si::{
|
use uom::si::{
|
||||||
electric_potential::volt,
|
electric_potential::volt,
|
||||||
|
@ -40,15 +41,17 @@ impl From<postcard::Error> for Error {
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
channels: [ChannelConfig; CHANNELS],
|
channels: [ChannelConfig; CHANNELS],
|
||||||
|
pub ipv4_address: [u8; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(channels: &mut Channels) -> Self {
|
pub fn new(channels: &mut Channels, ipv4_address: Ipv4Address) -> Self {
|
||||||
Config {
|
Config {
|
||||||
channels: [
|
channels: [
|
||||||
ChannelConfig::new(channels, 0),
|
ChannelConfig::new(channels, 0),
|
||||||
ChannelConfig::new(channels, 1),
|
ChannelConfig::new(channels, 1),
|
||||||
],
|
],
|
||||||
|
ipv4_address: ipv4_address.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +194,7 @@ impl PwmLimits {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::DEFAULT_IPV4_ADDRESS;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fit_eeprom() {
|
fn test_fit_eeprom() {
|
||||||
|
@ -211,6 +215,7 @@ mod test {
|
||||||
channel_config.clone(),
|
channel_config.clone(),
|
||||||
channel_config.clone(),
|
channel_config.clone(),
|
||||||
],
|
],
|
||||||
|
ipv4_address: DEFAULT_IPV4_ADDRESS.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buffer = [0; EEPROM_SIZE];
|
let mut buffer = [0; EEPROM_SIZE];
|
||||||
|
@ -237,6 +242,7 @@ mod test {
|
||||||
channel_config.clone(),
|
channel_config.clone(),
|
||||||
channel_config.clone(),
|
channel_config.clone(),
|
||||||
],
|
],
|
||||||
|
ipv4_address: DEFAULT_IPV4_ADDRESS.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buffer = [0; EEPROM_SIZE];
|
let mut buffer = [0; EEPROM_SIZE];
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::usb;
|
||||||
pub fn init_log() {
|
pub fn init_log() {
|
||||||
static USB_LOGGER: usb::Logger = usb::Logger;
|
static USB_LOGGER: usb::Logger = usb::Logger;
|
||||||
let _ = log::set_logger(&USB_LOGGER);
|
let _ = log::set_logger(&USB_LOGGER);
|
||||||
log::set_max_level(log::LevelFilter::Trace);
|
log::set_max_level(log::LevelFilter::Debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "semihosting")]
|
#[cfg(feature = "semihosting")]
|
||||||
|
|
114
src/main.rs
114
src/main.rs
|
@ -24,10 +24,9 @@ use stm32f4xx_hal::{
|
||||||
use smoltcp::{
|
use smoltcp::{
|
||||||
time::Instant,
|
time::Instant,
|
||||||
socket::TcpSocket,
|
socket::TcpSocket,
|
||||||
wire::EthernetAddress,
|
wire::{EthernetAddress, Ipv4Address},
|
||||||
};
|
};
|
||||||
use uom::{
|
use uom::{
|
||||||
fmt::DisplayStyle::Abbreviation,
|
|
||||||
si::{
|
si::{
|
||||||
f64::{
|
f64::{
|
||||||
ElectricCurrent,
|
ElectricCurrent,
|
||||||
|
@ -77,6 +76,7 @@ const WATCHDOG_INTERVAL: u32 = 30_000;
|
||||||
pub const EEPROM_PAGE_SIZE: usize = 8;
|
pub const EEPROM_PAGE_SIZE: usize = 8;
|
||||||
pub const EEPROM_SIZE: usize = 128;
|
pub const EEPROM_SIZE: usize = 128;
|
||||||
|
|
||||||
|
pub const DEFAULT_IPV4_ADDRESS: Ipv4Address = Ipv4Address([192, 168, 1, 26]);
|
||||||
const TCP_PORT: u16 = 23;
|
const TCP_PORT: u16 = 23;
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,10 +160,15 @@ fn main() -> ! {
|
||||||
|
|
||||||
usb::State::setup(usb);
|
usb::State::setup(usb);
|
||||||
|
|
||||||
|
let mut ipv4_address = DEFAULT_IPV4_ADDRESS;
|
||||||
let mut channels = Channels::new(pins);
|
let mut channels = Channels::new(pins);
|
||||||
let _ = Config::load(&mut eeprom)
|
let _ = Config::load(&mut eeprom)
|
||||||
.map(|config| config.apply(&mut channels))
|
.map(|config| {
|
||||||
|
config.apply(&mut channels);
|
||||||
|
ipv4_address = Ipv4Address::from_bytes(&config.ipv4_address);
|
||||||
|
})
|
||||||
.map_err(|e| warn!("error loading config: {:?}", e));
|
.map_err(|e| warn!("error loading config: {:?}", e));
|
||||||
|
info!("IPv4 address: {}", ipv4_address);
|
||||||
|
|
||||||
// EEPROM ships with a read-only EUI-48 identifier
|
// EEPROM ships with a read-only EUI-48 identifier
|
||||||
let mut eui48 = [0; 6];
|
let mut eui48 = [0; 6];
|
||||||
|
@ -171,7 +176,8 @@ fn main() -> ! {
|
||||||
let hwaddr = EthernetAddress(eui48);
|
let hwaddr = EthernetAddress(eui48);
|
||||||
info!("EEPROM MAC address: {}", hwaddr);
|
info!("EEPROM MAC address: {}", hwaddr);
|
||||||
|
|
||||||
net::run(clocks, dp.ETHERNET_MAC, dp.ETHERNET_DMA, eth_pins, hwaddr, |iface| {
|
net::run(clocks, dp.ETHERNET_MAC, dp.ETHERNET_DMA, eth_pins, hwaddr, ipv4_address, |iface| {
|
||||||
|
let mut new_ipv4_address = None;
|
||||||
Server::<Session>::run(iface, |server| {
|
Server::<Session>::run(iface, |server| {
|
||||||
leds.r1.off();
|
leds.r1.off();
|
||||||
|
|
||||||
|
@ -202,11 +208,11 @@ fn main() -> ! {
|
||||||
Ok(SessionInput::Command(command)) => match command {
|
Ok(SessionInput::Command(command)) => match command {
|
||||||
Command::Quit =>
|
Command::Quit =>
|
||||||
socket.close(),
|
socket.close(),
|
||||||
Command::Reporting(reporting) => {
|
Command::Reporting(_reporting) => {
|
||||||
let _ = writeln!(socket, "report={}", if reporting { "on" } else { "off" });
|
// handled by session
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::Reporting) => {
|
Command::Show(ShowCommand::Reporting) => {
|
||||||
let _ = writeln!(socket, "report={}", if session.reporting() { "on" } else { "off" });
|
let _ = writeln!(socket, "{{ \"report\": {:?} }}", session.reporting());
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::Input) => {
|
Command::Show(ShowCommand::Input) => {
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
|
@ -260,8 +266,6 @@ fn main() -> ! {
|
||||||
Command::PwmPid { channel } => {
|
Command::PwmPid { channel } => {
|
||||||
channels.channel_state(channel).pid_engaged = true;
|
channels.channel_state(channel).pid_engaged = true;
|
||||||
leds.g3.on();
|
leds.g3.on();
|
||||||
let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Command::Pwm { channel, pin, value } => {
|
Command::Pwm { channel, pin, value } => {
|
||||||
match pin {
|
match pin {
|
||||||
|
@ -269,44 +273,20 @@ fn main() -> ! {
|
||||||
channels.channel_state(channel).pid_engaged = false;
|
channels.channel_state(channel).pid_engaged = false;
|
||||||
leds.g3.off();
|
leds.g3.off();
|
||||||
let current = ElectricCurrent::new::<ampere>(value);
|
let current = ElectricCurrent::new::<ampere>(value);
|
||||||
let (current, max) = channels.set_i(channel, current);
|
channels.set_i(channel, current);
|
||||||
channels.power_up(channel);
|
channels.power_up(channel);
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: i_set DAC output set to {:.3} / {:.3}",
|
|
||||||
channel,
|
|
||||||
current.into_format_args(ampere, Abbreviation),
|
|
||||||
max.into_format_args(ampere, Abbreviation),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
PwmPin::MaxV => {
|
PwmPin::MaxV => {
|
||||||
let voltage = ElectricPotential::new::<volt>(value);
|
let voltage = ElectricPotential::new::<volt>(value);
|
||||||
let (voltage, max) = channels.set_max_v(channel, voltage);
|
channels.set_max_v(channel, voltage);
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: max_v set to {:.3} / {:.3}",
|
|
||||||
channel,
|
|
||||||
voltage.into_format_args(volt, Abbreviation),
|
|
||||||
max.into_format_args(volt, Abbreviation),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
PwmPin::MaxIPos => {
|
PwmPin::MaxIPos => {
|
||||||
let current = ElectricCurrent::new::<ampere>(value);
|
let current = ElectricCurrent::new::<ampere>(value);
|
||||||
let (current, max) = channels.set_max_i_pos(channel, current);
|
channels.set_max_i_pos(channel, current);
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: max_i_pos set to {:.3} / {:.3}",
|
|
||||||
channel,
|
|
||||||
current.into_format_args(ampere, Abbreviation),
|
|
||||||
max.into_format_args(ampere, Abbreviation),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
PwmPin::MaxINeg => {
|
PwmPin::MaxINeg => {
|
||||||
let current = ElectricCurrent::new::<ampere>(value);
|
let current = ElectricCurrent::new::<ampere>(value);
|
||||||
let (current, max) = channels.set_max_i_neg(channel, current);
|
channels.set_max_i_neg(channel, current);
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: max_i_neg set to {:.3} / {:.3}",
|
|
||||||
channel,
|
|
||||||
current.into_format_args(ampere, Abbreviation),
|
|
||||||
max.into_format_args(ampere, Abbreviation),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,15 +296,6 @@ fn main() -> ! {
|
||||||
state.center = center;
|
state.center = center;
|
||||||
if !state.pid_engaged {
|
if !state.pid_engaged {
|
||||||
channels.set_i(channel, i_tec);
|
channels.set_i(channel, i_tec);
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: center point updated, output readjusted for {:.3}",
|
|
||||||
channel, i_tec.into_format_args(ampere, Abbreviation),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: center point updated",
|
|
||||||
channel,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Pid { channel, parameter, value } => {
|
Command::Pid { channel, parameter, value } => {
|
||||||
|
@ -351,7 +322,6 @@ fn main() -> ! {
|
||||||
IntegralMax =>
|
IntegralMax =>
|
||||||
pid.parameters.integral_max = value as f32,
|
pid.parameters.integral_max = value as f32,
|
||||||
}
|
}
|
||||||
let _ = writeln!(socket, "PID parameter updated");
|
|
||||||
}
|
}
|
||||||
Command::SteinhartHart { channel, parameter, value } => {
|
Command::SteinhartHart { channel, parameter, value } => {
|
||||||
let sh = &mut channels.channel_state(channel).sh;
|
let sh = &mut channels.channel_state(channel).sh;
|
||||||
|
@ -361,52 +331,40 @@ fn main() -> ! {
|
||||||
B => sh.b = value,
|
B => sh.b = value,
|
||||||
R0 => sh.r0 = ElectricalResistance::new::<ohm>(value),
|
R0 => sh.r0 = ElectricalResistance::new::<ohm>(value),
|
||||||
}
|
}
|
||||||
let _ = writeln!(socket, "Steinhart-Hart equation parameter updated");
|
|
||||||
}
|
}
|
||||||
Command::PostFilter { channel, rate: None } => {
|
Command::PostFilter { channel, rate: None } => {
|
||||||
channels.adc.set_postfilter(channel as u8, None).unwrap();
|
channels.adc.set_postfilter(channel as u8, None).unwrap();
|
||||||
let _ = writeln!(
|
|
||||||
socket, "channel {}: postfilter disabled",
|
|
||||||
channel
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Command::PostFilter { channel, rate: Some(rate) } => {
|
Command::PostFilter { channel, rate: Some(rate) } => {
|
||||||
let filter = ad7172::PostFilter::closest(rate);
|
let filter = ad7172::PostFilter::closest(rate);
|
||||||
match filter {
|
match filter {
|
||||||
Some(filter) => {
|
Some(filter) =>
|
||||||
channels.adc.set_postfilter(channel as u8, Some(filter)).unwrap();
|
channels.adc.set_postfilter(channel as u8, Some(filter)).unwrap(),
|
||||||
let _ = writeln!(
|
None =>
|
||||||
socket, "channel {}: postfilter set to {:.2} SPS",
|
error!("unable to choose postfilter for rate {:.3}", rate),
|
||||||
channel, filter.output_rate().unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let _ = writeln!(socket, "Unable to choose postfilter");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Load => {
|
Command::Load => {
|
||||||
match Config::load(&mut eeprom) {
|
match Config::load(&mut eeprom) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
config.apply(&mut channels);
|
config.apply(&mut channels);
|
||||||
let _ = writeln!(socket, "Config loaded from EEPROM.");
|
new_ipv4_address = Some(Ipv4Address::from_bytes(&config.ipv4_address));
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let _ = writeln!(socket, "Error: {:?}", e);
|
|
||||||
}
|
}
|
||||||
|
Err(e) =>
|
||||||
|
error!("unable to load eeprom config: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Save => {
|
Command::Save => {
|
||||||
let config = Config::new(&mut channels);
|
let config = Config::new(&mut channels, ipv4_address);
|
||||||
match config.save(&mut eeprom) {
|
match config.save(&mut eeprom) {
|
||||||
Ok(()) => {
|
Ok(()) => {},
|
||||||
let _ = writeln!(socket, "Config saved to EEPROM.");
|
Err(e) =>
|
||||||
}
|
error!("unable to save eeprom config: {:?}", e),
|
||||||
Err(e) => {
|
|
||||||
let _ = writeln!(socket, "Error saving config: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Command::Ipv4(address) => {
|
||||||
|
new_ipv4_address = Some(Ipv4Address::from_bytes(&address));
|
||||||
|
}
|
||||||
Command::Reset => {
|
Command::Reset => {
|
||||||
for i in 0..CHANNELS {
|
for i in 0..CHANNELS {
|
||||||
channels.power_down(i);
|
channels.power_down(i);
|
||||||
|
@ -415,9 +373,8 @@ fn main() -> ! {
|
||||||
SCB::sys_reset();
|
SCB::sys_reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(SessionInput::Error(e)) => {
|
Ok(SessionInput::Error(e)) =>
|
||||||
let _ = writeln!(socket, "Command error: {:?}", e);
|
error!("session input: {:?}", e),
|
||||||
}
|
|
||||||
Err(_) =>
|
Err(_) =>
|
||||||
socket.close(),
|
socket.close(),
|
||||||
}
|
}
|
||||||
|
@ -430,6 +387,11 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Apply new IPv4 address
|
||||||
|
new_ipv4_address.map(|ipv4_address|
|
||||||
|
server.set_ipv4_address(ipv4_address)
|
||||||
|
);
|
||||||
|
|
||||||
// Update watchdog
|
// Update watchdog
|
||||||
wd.feed();
|
wd.feed();
|
||||||
|
|
||||||
|
|
11
src/net.rs
11
src/net.rs
|
@ -8,7 +8,7 @@ use stm32f4xx_hal::{
|
||||||
rcc::Clocks,
|
rcc::Clocks,
|
||||||
stm32::{interrupt, Peripherals, ETHERNET_MAC, ETHERNET_DMA},
|
stm32::{interrupt, Peripherals, ETHERNET_MAC, ETHERNET_DMA},
|
||||||
};
|
};
|
||||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address};
|
||||||
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
|
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
|
||||||
use stm32_eth::{Eth, RingEntry, PhyAddress, RxDescriptor, TxDescriptor};
|
use stm32_eth::{Eth, RingEntry, PhyAddress, RxDescriptor, TxDescriptor};
|
||||||
use crate::pins::EthernetPins;
|
use crate::pins::EthernetPins;
|
||||||
|
@ -29,7 +29,9 @@ pub fn run<F>(
|
||||||
clocks: Clocks,
|
clocks: Clocks,
|
||||||
ethernet_mac: ETHERNET_MAC, ethernet_dma: ETHERNET_DMA,
|
ethernet_mac: ETHERNET_MAC, ethernet_dma: ETHERNET_DMA,
|
||||||
eth_pins: EthernetPins,
|
eth_pins: EthernetPins,
|
||||||
ethernet_addr: EthernetAddress, f: F
|
ethernet_addr: EthernetAddress,
|
||||||
|
local_addr: Ipv4Address,
|
||||||
|
f: F
|
||||||
) where
|
) where
|
||||||
F: FnOnce(EthernetInterface<&mut stm32_eth::Eth<'static, 'static>>),
|
F: FnOnce(EthernetInterface<&mut stm32_eth::Eth<'static, 'static>>),
|
||||||
{
|
{
|
||||||
|
@ -50,8 +52,9 @@ pub fn run<F>(
|
||||||
eth_dev.enable_interrupt();
|
eth_dev.enable_interrupt();
|
||||||
|
|
||||||
// IP stack
|
// IP stack
|
||||||
let local_addr = IpAddress::v4(192, 168, 1, 26);
|
// Netmask 0 means we expect any IP address on the local segment.
|
||||||
let mut ip_addrs = [IpCidr::new(local_addr, 24)];
|
// No routing.
|
||||||
|
let mut ip_addrs = [IpCidr::new(local_addr.into(), 0)];
|
||||||
let mut neighbor_storage = [None; 16];
|
let mut neighbor_storage = [None; 16];
|
||||||
let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]);
|
let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]);
|
||||||
let iface = EthernetInterfaceBuilder::new(&mut eth_dev)
|
let iface = EthernetInterfaceBuilder::new(&mut eth_dev)
|
||||||
|
|
|
@ -3,6 +3,7 @@ use smoltcp::{
|
||||||
iface::EthernetInterface,
|
iface::EthernetInterface,
|
||||||
socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef},
|
socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
|
wire::{IpCidr, Ipv4Address, Ipv4Cidr},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,4 +84,21 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> {
|
||||||
callback(socket, &mut state.state);
|
callback(socket, &mut state.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_ipv4_address(&mut self, ipv4_address: Ipv4Address) {
|
||||||
|
self.net.update_ip_addrs(|addrs| {
|
||||||
|
for addr in addrs.iter_mut() {
|
||||||
|
match addr {
|
||||||
|
IpCidr::Ipv4(_) => {
|
||||||
|
*addr = IpCidr::Ipv4(Ipv4Cidr::new(ipv4_address, 0));
|
||||||
|
// done
|
||||||
|
break
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue