Compare commits
No commits in common. "63d85d3385ea1bf53841910b4206ce4a9eca2954" and "475dffe6b2df08e13a38c35ab5af10a76ed53056" have entirely different histories.
63d85d3385
...
475dffe6b2
8 changed files with 26 additions and 239 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,3 +1,2 @@
|
||||||
/kicad/*-backups/
|
/kicad/*-backups/
|
||||||
/kicad/*.lck
|
/kicad/*.lck
|
||||||
/firmware/cfg.toml
|
|
||||||
|
|
@ -3,7 +3,7 @@ target = "xtensa-esp32s2-espidf"
|
||||||
|
|
||||||
[target.xtensa-esp32s2-espidf]
|
[target.xtensa-esp32s2-espidf]
|
||||||
linker = "ldproxy"
|
linker = "ldproxy"
|
||||||
runner = "espflash flash --monitor --before no-reset -a no-reset --no-stub -T part_table.csv"
|
runner = "espflash flash --monitor --before no-reset -a no-reset --no-stub"
|
||||||
rustflags = [ "--cfg", "espidf_time64"]
|
rustflags = [ "--cfg", "espidf_time64"]
|
||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@ esp-idf-svc = { version = "0.51", features = ["critical-section", "embassy-time-
|
||||||
anyhow = "1.0.97"
|
anyhow = "1.0.97"
|
||||||
embedded-graphics = "0.8.1"
|
embedded-graphics = "0.8.1"
|
||||||
ssd1306 = "0.9.0"
|
ssd1306 = "0.9.0"
|
||||||
toml-cfg = "0.2.0"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
embuild = "0.33"
|
embuild = "0.33"
|
||||||
toml-cfg = "0.2.0"
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,3 @@
|
||||||
#[toml_cfg::toml_config]
|
|
||||||
pub struct Config {
|
|
||||||
#[default("")]
|
|
||||||
wifi_ssid: &'static str,
|
|
||||||
#[default("")]
|
|
||||||
wifi_psk: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if !std::path::Path::new("cfg.toml").exists() {
|
|
||||||
panic!("You need to create a `cfg.toml` file with your Wi-Fi credentials! Use `cfg.toml.template` as a template.");
|
|
||||||
}
|
|
||||||
|
|
||||||
embuild::espidf::sysenv::output();
|
embuild::espidf::sysenv::output();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
[http-server]
|
|
||||||
wifi_ssid = "beans32"
|
|
||||||
wifi_psk = "12345678"
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# ESP-IDF Partition Table
|
|
||||||
# Name, Type, SubType, Offset, Size, Flags
|
|
||||||
nvs, data, nvs, 0x9000, 0x6000,
|
|
||||||
phy_init, data, phy, 0xf000, 0x1000,
|
|
||||||
factory, app, factory, 0x10000, 2M,
|
|
||||||
|
|
|
@ -1,61 +1,21 @@
|
||||||
mod hid;
|
mod hid;
|
||||||
mod wifi;
|
|
||||||
|
|
||||||
use std::{sync::{atomic::{AtomicBool, AtomicPtr, Ordering}, Arc, Mutex, RwLock}, thread, time::{Duration, Instant}};
|
use std::{thread, time::{Duration, Instant}};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
|
mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
|
||||||
pixelcolor::BinaryColor,
|
pixelcolor::BinaryColor,
|
||||||
};
|
};
|
||||||
use esp_idf_svc::{eventloop::EspSystemEventLoop, hal::{
|
use esp_idf_svc::hal::{
|
||||||
adc::{attenuation::DB_11, oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver}},
|
adc::{attenuation::DB_11, oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver}},
|
||||||
gpio::{InterruptType, PinDriver},
|
gpio::{InterruptType, PinDriver},
|
||||||
i2c::{I2cConfig, I2cDriver},
|
i2c::{I2cConfig, I2cDriver},
|
||||||
prelude::Peripherals,
|
prelude::Peripherals,
|
||||||
units::*
|
units::*
|
||||||
}, http::{server::{Configuration, EspHttpServer}, Method}, io::{EspIOError, Write}};
|
};
|
||||||
use hid::{Button, GPIOIndicatedButton, Indicator};
|
use hid::{Button, GPIOIndicatedButton, Indicator};
|
||||||
use ssd1306::{mode::DisplayConfig, prelude::DisplayRotation, size::DisplaySize128x32, I2CDisplayInterface, Ssd1306};
|
use ssd1306::{mode::DisplayConfig, prelude::DisplayRotation, size::DisplaySize128x32, I2CDisplayInterface, Ssd1306};
|
||||||
|
|
||||||
#[toml_cfg::toml_config]
|
|
||||||
pub struct Config {
|
|
||||||
#[default("")]
|
|
||||||
wifi_ssid: &'static str,
|
|
||||||
#[default("")]
|
|
||||||
wifi_psk: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PIDController {
|
|
||||||
p_gain: f32,
|
|
||||||
i_gain: f32,
|
|
||||||
d_gain: f32,
|
|
||||||
prev_err: f32,
|
|
||||||
sum_err: f32,
|
|
||||||
target: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PIDController {
|
|
||||||
pub fn new(p_gain: f32, i_gain: f32, d_gain: f32, target: f32) -> Self {
|
|
||||||
return Self {
|
|
||||||
p_gain,
|
|
||||||
i_gain,
|
|
||||||
d_gain,
|
|
||||||
prev_err: 0_f32,
|
|
||||||
sum_err: 0_f32,
|
|
||||||
target,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_control(&mut self, measured: f32) -> f32 {
|
|
||||||
let ctrl_err = self.target - measured;
|
|
||||||
let delta_err = ctrl_err - self.prev_err;
|
|
||||||
let control = self.p_gain * ctrl_err + self.i_gain * self.sum_err + self.d_gain * delta_err;
|
|
||||||
self.sum_err += ctrl_err;
|
|
||||||
self.prev_err = ctrl_err;
|
|
||||||
|
|
||||||
return control;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// It is necessary to call this function once. Otherwise some patches to the runtime
|
// It is necessary to call this function once. Otherwise some patches to the runtime
|
||||||
|
|
@ -103,22 +63,6 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
log::info!("Hello, world!");
|
log::info!("Hello, world!");
|
||||||
|
|
||||||
let sysloop = EspSystemEventLoop::take()?;
|
|
||||||
let app_config = CONFIG;
|
|
||||||
|
|
||||||
// Connect to the Wi-Fi network
|
|
||||||
let _wifi = wifi::wifi(
|
|
||||||
app_config.wifi_ssid,
|
|
||||||
app_config.wifi_psk,
|
|
||||||
peripherals.modem,
|
|
||||||
sysloop,
|
|
||||||
)?;
|
|
||||||
let mut server = EspHttpServer::new(&Configuration::default())?;
|
|
||||||
|
|
||||||
const PID_TARGET: f32 = 70_f32;
|
|
||||||
|
|
||||||
let mut pid = PIDController::new(0.5, 0.01, 0.3, PID_TARGET);
|
|
||||||
|
|
||||||
const R1: f32 = 100000_f32;
|
const R1: f32 = 100000_f32;
|
||||||
const R0: f32 = 100000_f32;
|
const R0: f32 = 100000_f32;
|
||||||
const V1: f32 = 5_f32;
|
const V1: f32 = 5_f32;
|
||||||
|
|
@ -126,69 +70,43 @@ fn main() -> Result<()> {
|
||||||
const B: f32 = 4285_f32;
|
const B: f32 = 4285_f32;
|
||||||
|
|
||||||
let mut samples_sum = 0_f32;
|
let mut samples_sum = 0_f32;
|
||||||
let temperature = Arc::new(Mutex::new(0_f32));
|
let mut temperature = 0_f32;
|
||||||
let c_temperature = Arc::clone(&temperature);
|
|
||||||
|
|
||||||
let pid_control = Arc::new(Mutex::new(0_f32));
|
|
||||||
let c_pid_control = Arc::clone(&pid_control);
|
|
||||||
|
|
||||||
let heater_state = Arc::new(AtomicBool::new(heater_drv_pin.is_set_high()));
|
|
||||||
let c_heater_state = Arc::clone(&heater_state);
|
|
||||||
|
|
||||||
let mut i = 0_u32;
|
let mut i = 0_u32;
|
||||||
|
|
||||||
let headers = &[("Content-Type", "text/plain")];
|
|
||||||
|
|
||||||
server.fn_handler(
|
|
||||||
"/metrics",
|
|
||||||
Method::Get,
|
|
||||||
move |request| -> core::result::Result<(), EspIOError> {
|
|
||||||
let mut response = request.into_response(200, Option::None, headers)?;
|
|
||||||
let (pidv, tempv) = {
|
|
||||||
(*pid_control.lock().unwrap(), *temperature.lock().unwrap())
|
|
||||||
};
|
|
||||||
response.write(format!("pid_controller_action {}\n", pidv).as_bytes())?;
|
|
||||||
response.write(format!("pid_controller_target {}\n", PID_TARGET).as_bytes())?;
|
|
||||||
response.write(format!("boiler_temperature {}\n", tempv).as_bytes())?;
|
|
||||||
response.write(format!("boiler_heater_state {}\n", heater_state.load(Ordering::Relaxed) as i32 as f32).as_bytes())?;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut blink_time = Instant::now();
|
let mut blink_time = Instant::now();
|
||||||
let mut measure_time = Instant::now();
|
let mut measure_time = Instant::now();
|
||||||
let mut input_time = Option::from(Instant::now());
|
let mut input_time = Option::from(Instant::now());
|
||||||
|
|
||||||
let mut is_meltdown_state = false;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// when we receive button event, button interrupt will be automatically disabled until we enable it manually
|
// when we receive button event, button interrupt will be automatically disabled until we enable it manually
|
||||||
// so we use this feature to "debounce" our button by enabling interrupt only after some time passes
|
// so we use this feature to "debounce" our button by enabling interrupt only after some time passes
|
||||||
let not = btn1.button.wait_event(esp_idf_svc::hal::delay::NON_BLOCK);
|
let not = btn1.button.wait_event(esp_idf_svc::hal::delay::NON_BLOCK);
|
||||||
if not.is_some() {
|
match not {
|
||||||
println!("Button 1 pressed!");
|
Some(n) => {
|
||||||
|
println!("Button 1 pressed!");
|
||||||
|
|
||||||
if heater_drv_pin.is_set_high() {
|
if heater_drv_pin.is_set_high() {
|
||||||
heater_drv_pin.set_low()?;
|
heater_drv_pin.set_low()?;
|
||||||
btn1.indicator.set_state(false)?;
|
btn1.indicator.set_state(false)?;
|
||||||
} else {
|
} else {
|
||||||
if !is_meltdown_state {
|
|
||||||
heater_drv_pin.set_high()?;
|
heater_drv_pin.set_high()?;
|
||||||
btn1.indicator.set_state(true)?;
|
btn1.indicator.set_state(true)?;
|
||||||
} else {
|
|
||||||
println!("DON'T CLICK WE'RE ABOUT TO EXPLODE!");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
input_time = Option::from(Instant::now());
|
input_time = Option::from(Instant::now());
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable button interrupt again after 300ms from last click
|
// enable button interrupt again after 300ms from last click
|
||||||
if let Some(time) = input_time {
|
match input_time {
|
||||||
if time.elapsed().as_millis() >= 300 {
|
Some(time) => {
|
||||||
btn1.button.enable_interrupt()?;
|
if time.elapsed().as_millis() >= 300 {
|
||||||
input_time.take();
|
btn1.button.enable_interrupt()?;
|
||||||
}
|
input_time.take();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if measure_time.elapsed().as_millis() >= 10 {
|
if measure_time.elapsed().as_millis() >= 10 {
|
||||||
|
|
@ -199,37 +117,8 @@ fn main() -> Result<()> {
|
||||||
samples_sum += temp;
|
samples_sum += temp;
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
let tempv = samples_sum / 64_f32;
|
temperature = samples_sum / 64_f32;
|
||||||
let pidv = pid.get_control(tempv);
|
println!("Temperature value: {}", temperature);
|
||||||
{
|
|
||||||
let mut t = c_temperature.lock().unwrap();
|
|
||||||
let mut p = c_pid_control.lock().unwrap();
|
|
||||||
*t = tempv;
|
|
||||||
*p = pidv;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Temperature value: {}", tempv);
|
|
||||||
println!("PID Control Value: {}", pidv);
|
|
||||||
|
|
||||||
if !is_meltdown_state {
|
|
||||||
if pidv > 12_f32 && !heater_drv_pin.is_set_high() {
|
|
||||||
println!("[PID] Set heater to high");
|
|
||||||
heater_drv_pin.set_high()?;
|
|
||||||
} else if pidv < 10_f32 && !heater_drv_pin.is_set_low(){
|
|
||||||
println!("[PID] Set heater to low");
|
|
||||||
heater_drv_pin.set_low()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if tempv > 85_f32 {
|
|
||||||
println!("CRITICAL TEMPERATURE SHUTTING HEATER DOWN");
|
|
||||||
println!("CRITICAL TEMPERATURE SHUTTING HEATER DOWN");
|
|
||||||
println!("CRITICAL TEMPERATURE SHUTTING HEATER DOWN");
|
|
||||||
println!("CRITICAL TEMPERATURE SHUTTING HEATER DOWN");
|
|
||||||
heater_drv_pin.set_low()?;
|
|
||||||
is_meltdown_state = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
samples_sum = 0_f32;
|
samples_sum = 0_f32;
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +148,6 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
blink_time = Instant::now();
|
blink_time = Instant::now();
|
||||||
}
|
}
|
||||||
c_heater_state.store(heater_drv_pin.is_set_high(), Ordering::Relaxed);
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(10));
|
thread::sleep(Duration::from_millis(10));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
use anyhow::{bail, Result};
|
|
||||||
use esp_idf_svc::{
|
|
||||||
eventloop::EspSystemEventLoop,
|
|
||||||
hal::peripheral,
|
|
||||||
wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi},
|
|
||||||
};
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
pub fn wifi(
|
|
||||||
ssid: &str,
|
|
||||||
pass: &str,
|
|
||||||
modem: impl peripheral::Peripheral<P = esp_idf_svc::hal::modem::Modem> + 'static,
|
|
||||||
sysloop: EspSystemEventLoop,
|
|
||||||
) -> Result<Box<EspWifi<'static>>> {
|
|
||||||
let mut auth_method = AuthMethod::WPA2Personal;
|
|
||||||
if ssid.is_empty() {
|
|
||||||
bail!("Missing WiFi name")
|
|
||||||
}
|
|
||||||
if pass.is_empty() {
|
|
||||||
auth_method = AuthMethod::None;
|
|
||||||
info!("Wifi password is empty");
|
|
||||||
}
|
|
||||||
let mut esp_wifi = EspWifi::new(modem, sysloop.clone(), None)?;
|
|
||||||
|
|
||||||
let mut wifi = BlockingWifi::wrap(&mut esp_wifi, sysloop)?;
|
|
||||||
|
|
||||||
wifi.set_configuration(&Configuration::Client(ClientConfiguration::default()))?;
|
|
||||||
|
|
||||||
info!("Starting wifi...");
|
|
||||||
|
|
||||||
wifi.start()?;
|
|
||||||
|
|
||||||
info!("Scanning...");
|
|
||||||
|
|
||||||
let ap_infos = wifi.scan()?;
|
|
||||||
|
|
||||||
let ours = ap_infos.into_iter().find(|a| a.ssid == ssid);
|
|
||||||
|
|
||||||
let channel = if let Some(ours) = ours {
|
|
||||||
info!(
|
|
||||||
"Found configured access point {} on channel {}",
|
|
||||||
ssid, ours.channel
|
|
||||||
);
|
|
||||||
Some(ours.channel)
|
|
||||||
} else {
|
|
||||||
info!(
|
|
||||||
"Configured access point {} not found during scanning, will go with unknown channel",
|
|
||||||
ssid
|
|
||||||
);
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
wifi.set_configuration(&Configuration::Client(ClientConfiguration {
|
|
||||||
ssid: ssid
|
|
||||||
.try_into()
|
|
||||||
.expect("Could not parse the given SSID into WiFi config"),
|
|
||||||
password: pass
|
|
||||||
.try_into()
|
|
||||||
.expect("Could not parse the given password into WiFi config"),
|
|
||||||
channel,
|
|
||||||
auth_method,
|
|
||||||
..Default::default()
|
|
||||||
}))?;
|
|
||||||
|
|
||||||
info!("Connecting wifi...");
|
|
||||||
|
|
||||||
wifi.connect()?;
|
|
||||||
|
|
||||||
info!("Waiting for DHCP lease...");
|
|
||||||
|
|
||||||
wifi.wait_netif_up()?;
|
|
||||||
|
|
||||||
let ip_info = wifi.wifi().sta_netif().get_ip_info()?;
|
|
||||||
|
|
||||||
info!("Wifi DHCP info: {:?}", ip_info);
|
|
||||||
|
|
||||||
Ok(Box::new(esp_wifi))
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue