get most stuff working again, by upgrading to newer esp-hal version
this involved adding a lot of code from the develop branch step by step there are still some bugs, but at least i can get into the web interface and configure stuff again \o/ … measuring and pumping is working as well
This commit is contained in:
503
rust/src/main.rs
503
rust/src/main.rs
@@ -11,20 +11,22 @@
|
||||
|
||||
//TODO insert version here and read it in other parts, also read this for the ota webview
|
||||
esp_bootloader_esp_idf::esp_app_desc!();
|
||||
use alloc::vec::Vec;
|
||||
use config::PlantControllerConfig;
|
||||
use esp_backtrace as _;
|
||||
use hal::PROGRESS_ACTIVE;
|
||||
|
||||
use crate::config::{NetworkConfig, PlantConfig};
|
||||
use crate::fat_error::FatResult;
|
||||
use crate::hal::esp::MQTT_STAY_ALIVE;
|
||||
use crate::hal::{esp_time, TIME_ACCESS};
|
||||
use crate::log::LOG_ACCESS;
|
||||
use crate::log::log;
|
||||
use crate::tank::{determine_tank_state, TankError, TankState, WATER_FROZEN_THRESH};
|
||||
use crate::webserver::http_server;
|
||||
use crate::{
|
||||
config::BoardVersion::INITIAL,
|
||||
hal::{PlantHal, HAL, PLANT_COUNT},
|
||||
};
|
||||
use ::log::{info, warn};
|
||||
use ::log::{info, warn, error};
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::sync::Arc;
|
||||
@@ -37,7 +39,7 @@ use embassy_net::Stack;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
||||
use embassy_sync::once_lock::OnceLock;
|
||||
use embassy_time::Timer;
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use esp_hal::rom::ets_delay_us;
|
||||
use esp_hal::system::software_reset;
|
||||
use esp_println::{logger, println};
|
||||
@@ -165,35 +167,28 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
let cur = match board.board_hal.get_rtc_module().get_rtc_time().await {
|
||||
Ok(value) => {
|
||||
{
|
||||
let guard = TIME_ACCESS.get().await.lock().await;
|
||||
guard.set_current_time_us(value.timestamp_micros() as u64);
|
||||
board.board_hal.set_time(&value.fixed_offset()).await;
|
||||
}
|
||||
value
|
||||
}
|
||||
Err(err) => {
|
||||
info!("rtc module error: {:?}", err);
|
||||
board.board_hal.general_fault(true).await;
|
||||
esp_time().await
|
||||
board.board_hal.get_time().await
|
||||
}
|
||||
};
|
||||
|
||||
info!("Step 1");
|
||||
|
||||
//check if we know the time current > 2020 (plausibility checks, this code is newer than 2020)
|
||||
if cur.year() < 2020 {
|
||||
to_config = true;
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "")
|
||||
.await;
|
||||
log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "");
|
||||
}
|
||||
info!("cur is {}", cur);
|
||||
update_charge_indicator(&mut board).await;
|
||||
if board.board_hal.get_esp().get_restart_to_conf() {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "")
|
||||
.await;
|
||||
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
|
||||
for _i in 0..2 {
|
||||
board.board_hal.general_fault(true).await;
|
||||
Timer::after_millis(100).await;
|
||||
@@ -205,11 +200,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
board.board_hal.get_esp().set_restart_to_conf(false);
|
||||
} else if board.board_hal.get_esp().mode_override_pressed() {
|
||||
board.board_hal.general_fault(true).await;
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(LogMessage::ConfigModeButtonOverride, 0, 0, "", "")
|
||||
.await;
|
||||
log(LogMessage::ConfigModeButtonOverride, 0, 0, "", "");
|
||||
for _i in 0..5 {
|
||||
board.board_hal.general_fault(true).await;
|
||||
Timer::after_millis(100).await;
|
||||
@@ -232,18 +223,18 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
{
|
||||
info!("No wifi configured, starting initial config mode");
|
||||
|
||||
let stack = board.board_hal.get_esp().wifi_ap().await?;
|
||||
let stack = board.board_hal.get_esp().wifi_ap(spawner).await?;
|
||||
|
||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||
println!("starting webserver");
|
||||
|
||||
spawner.spawn(http_server(reboot_now.clone(), stack))?;
|
||||
wait_infinity(board, WaitType::MissingConfig, reboot_now.clone()).await;
|
||||
spawner.spawn(http_server(reboot_now.clone(), stack)?);
|
||||
wait_infinity(board, WaitType::MissingConfig, reboot_now.clone(), UTC).await;
|
||||
}
|
||||
|
||||
let mut stack: OptionLock<Stack> = OptionLock::empty();
|
||||
let network_mode = if board.board_hal.get_config().network.ssid.is_some() {
|
||||
try_connect_wifi_sntp_mqtt(&mut board, &mut stack).await
|
||||
try_connect_wifi_sntp_mqtt(&mut board, &mut stack, spawner).await
|
||||
} else {
|
||||
info!("No wifi configured");
|
||||
//the current sensors require this amount to stabilize, in the case of Wi-Fi this is already handled due to connect timings;
|
||||
@@ -256,7 +247,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
|
||||
let res = {
|
||||
let esp = board.board_hal.get_esp();
|
||||
esp.wifi_ap().await
|
||||
esp.wifi_ap(spawner).await
|
||||
};
|
||||
match res {
|
||||
Ok(ap_stack) => {
|
||||
@@ -291,39 +282,36 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
let _ = publish_mppt_state(&mut board).await;
|
||||
}
|
||||
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(
|
||||
LogMessage::StartupInfo,
|
||||
matches!(network_mode, NetworkMode::WIFI { .. }) as u32,
|
||||
matches!(
|
||||
network_mode,
|
||||
NetworkMode::WIFI {
|
||||
sntp: SntpMode::SYNC { .. },
|
||||
..
|
||||
}
|
||||
) as u32,
|
||||
matches!(network_mode, NetworkMode::WIFI { mqtt: true, .. })
|
||||
.to_string()
|
||||
.as_str(),
|
||||
"",
|
||||
)
|
||||
.await;
|
||||
log(
|
||||
LogMessage::StartupInfo,
|
||||
matches!(network_mode, NetworkMode::WIFI { .. }) as u32,
|
||||
matches!(
|
||||
network_mode,
|
||||
NetworkMode::WIFI {
|
||||
sntp: SntpMode::SYNC { .. },
|
||||
..
|
||||
}
|
||||
) as u32,
|
||||
matches!(network_mode, NetworkMode::WIFI { mqtt: true, .. })
|
||||
.to_string()
|
||||
.as_str(),
|
||||
"",
|
||||
);
|
||||
|
||||
if to_config {
|
||||
//check if client or ap mode and init Wi-Fi
|
||||
info!("executing config mode override");
|
||||
//config upload will trigger reboot!
|
||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||
spawner.spawn(http_server(reboot_now.clone(), stack.take().unwrap()))?;
|
||||
wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await;
|
||||
let stack_val = stack.take();
|
||||
if let Some(s) = stack_val {
|
||||
spawner.spawn(http_server(reboot_now.clone(), s)?);
|
||||
} else {
|
||||
bail!("Network stack missing, hard abort")
|
||||
}
|
||||
wait_infinity(board, WaitType::ConfigButton, reboot_now.clone(), UTC).await;
|
||||
} else {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(LogMessage::NormalRun, 0, 0, "", "")
|
||||
.await;
|
||||
log(LogMessage::NormalRun, 0, 0, "", "");
|
||||
}
|
||||
|
||||
let _dry_run = false;
|
||||
@@ -334,38 +322,22 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
if let Some(err) = tank_state.got_error(&board.board_hal.get_config().tank) {
|
||||
match err {
|
||||
TankError::SensorDisabled => { /* unreachable */ }
|
||||
TankError::SensorMissing(raw_value_mv) => {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(
|
||||
LogMessage::TankSensorMissing,
|
||||
raw_value_mv as u32,
|
||||
0,
|
||||
"",
|
||||
"",
|
||||
)
|
||||
.await
|
||||
}
|
||||
TankError::SensorValueError { value, min, max } => {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(
|
||||
LogMessage::TankSensorValueRangeError,
|
||||
min as u32,
|
||||
max as u32,
|
||||
&format!("{}", value),
|
||||
"",
|
||||
)
|
||||
.await
|
||||
}
|
||||
TankError::SensorMissing(raw_value_mv) => log(
|
||||
LogMessage::TankSensorMissing,
|
||||
raw_value_mv as u32,
|
||||
0,
|
||||
"",
|
||||
"",
|
||||
),
|
||||
TankError::SensorValueError { value, min, max } => log(
|
||||
LogMessage::TankSensorValueRangeError,
|
||||
min as u32,
|
||||
max as u32,
|
||||
&format!("{value}"),
|
||||
"",
|
||||
),
|
||||
TankError::BoardError(err) => {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(LogMessage::TankSensorBoardError, 0, 0, "", &err.to_string())
|
||||
.await
|
||||
log(LogMessage::TankSensorBoardError, 0, 0, "", &err.to_string())
|
||||
}
|
||||
}
|
||||
// disabled cannot trigger this because of wrapping if is_enabled
|
||||
@@ -374,11 +346,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
.warn_level(&board.board_hal.get_config().tank)
|
||||
.is_ok_and(|warn| warn)
|
||||
{
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(LogMessage::TankWaterLevelLow, 0, 0, "", "")
|
||||
.await;
|
||||
log(LogMessage::TankWaterLevelLow, 0, 0, "", "");
|
||||
board.board_hal.general_fault(true).await;
|
||||
}
|
||||
}
|
||||
@@ -620,7 +588,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
if stay_alive {
|
||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||
let _webserver = http_server(reboot_now.clone(), stack.take().unwrap());
|
||||
wait_infinity(board, WaitType::MqttConfig, reboot_now.clone()).await;
|
||||
wait_infinity(board, WaitType::MqttConfig, reboot_now.clone(), UTC).await;
|
||||
} else {
|
||||
//TODO wait for all mqtt publishes?
|
||||
Timer::after_millis(5000).await;
|
||||
@@ -671,17 +639,13 @@ pub async fn do_secure_pump(
|
||||
let high_current = current_ma > plant_config.max_pump_current_ma;
|
||||
if high_current {
|
||||
if first_error {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(
|
||||
LogMessage::PumpOverCurrent,
|
||||
plant_id as u32 + 1,
|
||||
current_ma as u32,
|
||||
plant_config.max_pump_current_ma.to_string().as_str(),
|
||||
step.to_string().as_str(),
|
||||
)
|
||||
.await;
|
||||
log(
|
||||
LogMessage::PumpOverCurrent,
|
||||
plant_id as u32 + 1,
|
||||
current_ma as u32,
|
||||
plant_config.max_pump_current_ma.to_string().as_str(),
|
||||
step.to_string().as_str(),
|
||||
);
|
||||
board.board_hal.general_fault(true).await;
|
||||
board.board_hal.fault(plant_id, true).await?;
|
||||
if !plant_config.ignore_current_error {
|
||||
@@ -694,17 +658,13 @@ pub async fn do_secure_pump(
|
||||
let low_current = current_ma < plant_config.min_pump_current_ma;
|
||||
if low_current {
|
||||
if first_error {
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(
|
||||
LogMessage::PumpOpenLoopCurrent,
|
||||
plant_id as u32 + 1,
|
||||
current_ma as u32,
|
||||
plant_config.min_pump_current_ma.to_string().as_str(),
|
||||
step.to_string().as_str(),
|
||||
)
|
||||
.await;
|
||||
log(
|
||||
LogMessage::PumpOpenLoopCurrent,
|
||||
plant_id as u32 + 1,
|
||||
current_ma as u32,
|
||||
plant_config.min_pump_current_ma.to_string().as_str(),
|
||||
step.to_string().as_str(),
|
||||
);
|
||||
board.board_hal.general_fault(true).await;
|
||||
board.board_hal.fault(plant_id, true).await?;
|
||||
if !plant_config.ignore_current_error {
|
||||
@@ -718,17 +678,13 @@ pub async fn do_secure_pump(
|
||||
Err(err) => {
|
||||
if !plant_config.ignore_current_error {
|
||||
info!("Error getting pump current: {}", err);
|
||||
LOG_ACCESS
|
||||
.lock()
|
||||
.await
|
||||
.log(
|
||||
LogMessage::PumpMissingSensorCurrent,
|
||||
plant_id as u32,
|
||||
0,
|
||||
"",
|
||||
"",
|
||||
)
|
||||
.await;
|
||||
log(
|
||||
LogMessage::PumpMissingSensorCurrent,
|
||||
plant_id as u32,
|
||||
0,
|
||||
"",
|
||||
"",
|
||||
);
|
||||
error = true;
|
||||
break;
|
||||
} else {
|
||||
@@ -761,25 +717,15 @@ pub async fn do_secure_pump(
|
||||
|
||||
async fn update_charge_indicator(
|
||||
board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>,
|
||||
) {
|
||||
) -> FatResult<()> {
|
||||
//FIXME add config and code to allow power supply mode, in this case this is a nop
|
||||
//we have mppt controller, ask it for charging current
|
||||
if let Ok(current) = board.board_hal.get_mptt_current().await {
|
||||
let _ = board
|
||||
.board_hal
|
||||
.set_charge_indicator(current.as_milliamperes() > 20_f64);
|
||||
}
|
||||
//fallback to battery controller and ask it instead
|
||||
else if let Ok(charging) = board
|
||||
let current = board.board_hal.get_mptt_current().await?;
|
||||
board
|
||||
.board_hal
|
||||
.get_battery_monitor()
|
||||
.average_current_milli_ampere()
|
||||
.await
|
||||
{
|
||||
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
||||
} else {
|
||||
//who knows
|
||||
let _ = board.board_hal.set_charge_indicator(false);
|
||||
}
|
||||
.set_charge_indicator(current.as_milliamperes() > 20_f64)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn publish_tank_state(
|
||||
@@ -818,25 +764,16 @@ async fn publish_plant_states(
|
||||
async fn publish_firmware_info(
|
||||
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
||||
version: VersionInfo,
|
||||
ip_address: &String,
|
||||
timezone_time: &String,
|
||||
ip_address: &str,
|
||||
timezone_time: &str,
|
||||
) {
|
||||
let esp = board.board_hal.get_esp();
|
||||
let _ = esp.mqtt_publish("/firmware/address", ip_address).await;
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/githash", &version.git_hash)
|
||||
esp.mqtt_publish("/firmware/address", ip_address).await;
|
||||
esp.mqtt_publish("/firmware/state", format!("{:?}", &version).as_str())
|
||||
.await;
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/buildtime", &version.build_time)
|
||||
esp.mqtt_publish("/firmware/last_online", timezone_time)
|
||||
.await;
|
||||
let _ = esp.mqtt_publish("/firmware/last_online", timezone_time);
|
||||
let state = esp.get_ota_state();
|
||||
let _ = esp.mqtt_publish("/firmware/ota_state", &state).await;
|
||||
let slot = esp.get_ota_slot();
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/ota_slot", &format!("slot{slot}"))
|
||||
.await;
|
||||
let _ = esp.mqtt_publish("/state", "online").await;
|
||||
esp.mqtt_publish("/state", "online").await;
|
||||
}
|
||||
macro_rules! mk_static {
|
||||
($t:ty,$val:expr) => {{
|
||||
@@ -849,25 +786,25 @@ macro_rules! mk_static {
|
||||
async fn try_connect_wifi_sntp_mqtt(
|
||||
board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>,
|
||||
stack_store: &mut OptionLock<Stack<'static>>,
|
||||
spawner: Spawner,
|
||||
) -> NetworkMode {
|
||||
let nw_conf = &board.board_hal.get_config().network.clone();
|
||||
match board.board_hal.get_esp().wifi(nw_conf).await {
|
||||
match board.board_hal.get_esp().wifi(nw_conf, spawner).await {
|
||||
Ok(stack) => {
|
||||
stack_store.replace(stack);
|
||||
|
||||
let sntp_mode: SntpMode = match board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.sntp(1000 * 10, stack.clone())
|
||||
.await
|
||||
{
|
||||
let sntp_mode: SntpMode = match board.board_hal.get_esp().sntp(1000 * 10, stack).await {
|
||||
Ok(new_time) => {
|
||||
info!("Using time from sntp {}", new_time.to_rfc3339());
|
||||
let _ = board.board_hal.get_rtc_module().set_rtc_time(&new_time);
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_rtc_module()
|
||||
.set_rtc_time(&new_time)
|
||||
.await;
|
||||
SntpMode::SYNC { current: new_time }
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("sntp error: {}", err);
|
||||
warn!("sntp error: {err}");
|
||||
board.board_hal.general_fault(true).await;
|
||||
SntpMode::OFFLINE
|
||||
}
|
||||
@@ -876,27 +813,40 @@ async fn try_connect_wifi_sntp_mqtt(
|
||||
let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() {
|
||||
let nw_config = board.board_hal.get_config().network.clone();
|
||||
let nw_config = mk_static!(NetworkConfig, nw_config);
|
||||
match board.board_hal.get_esp().mqtt(nw_config, stack).await {
|
||||
match board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt(nw_config, stack, spawner)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
info!("Mqtt connection ready");
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Could not connect mqtt due to {}", err);
|
||||
warn!("Could not connect mqtt due to {err}");
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let ip = match stack.config_v4() {
|
||||
Some(config) => config.address.address().to_string(),
|
||||
None => match stack.config_v6() {
|
||||
Some(config) => config.address.address().to_string(),
|
||||
None => String::from("No IP"),
|
||||
},
|
||||
};
|
||||
NetworkMode::WIFI {
|
||||
sntp: sntp_mode,
|
||||
mqtt: mqtt_connected,
|
||||
ip_address: stack.hardware_address().to_string(),
|
||||
ip_address: ip,
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
info!("Offline mode due to {}", err);
|
||||
info!("Offline mode due to {err}");
|
||||
board.board_hal.general_fault(true).await;
|
||||
NetworkMode::OFFLINE
|
||||
}
|
||||
@@ -985,6 +935,7 @@ async fn wait_infinity(
|
||||
board: MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
||||
wait_type: WaitType,
|
||||
reboot_now: Arc<AtomicBool>,
|
||||
timezone: Tz,
|
||||
) -> ! {
|
||||
//since we force to have the lock when entering, we can release it to ensure the caller does not forget to dispose of it
|
||||
drop(board);
|
||||
@@ -992,55 +943,155 @@ async fn wait_infinity(
|
||||
let delay = wait_type.blink_pattern();
|
||||
let mut led_count = 8;
|
||||
let mut pattern_step = 0;
|
||||
let serial_config_receive = AtomicBool::new(false);
|
||||
let mut suppress_further_mppt_error = false;
|
||||
let mut last_mqtt_update: Option<Instant> = None;
|
||||
|
||||
// Long-press exit (for webserver config modes): hold boot button for 5 seconds.
|
||||
let mut exit_hold_started: Option<Instant> = None;
|
||||
let exit_hold_duration = Duration::from_secs(5);
|
||||
let mut exit_hold_blink = false;
|
||||
loop {
|
||||
// While in config webserver mode, allow exiting via long-press.
|
||||
if matches!(wait_type, WaitType::MissingConfig | WaitType::ConfigButton) {
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
let pressed = board.board_hal.get_esp().mode_override_pressed();
|
||||
|
||||
match (pressed, exit_hold_started) {
|
||||
(true, None) => {
|
||||
exit_hold_started = Some(Instant::now());
|
||||
PROGRESS_ACTIVE.store(true, Ordering::Relaxed);
|
||||
}
|
||||
(false, Some(_)) => {
|
||||
exit_hold_started = None;
|
||||
// Clear any interim hold display.
|
||||
board.board_hal.clear_progress().await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(started) = exit_hold_started {
|
||||
let elapsed = Instant::now() - started;
|
||||
// Visible countdown: fill LEDs progressively during the hold.
|
||||
// Also toggle general fault LED to match the "enter config" visibility.
|
||||
exit_hold_blink = !exit_hold_blink;
|
||||
|
||||
let progress = core::cmp::min(elapsed, exit_hold_duration);
|
||||
let lit = ((progress.as_millis() * 8) / exit_hold_duration.as_millis())
|
||||
.saturating_add(1)
|
||||
.min(8) as usize;
|
||||
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, i < lit).await;
|
||||
}
|
||||
board.board_hal.general_fault(exit_hold_blink).await;
|
||||
|
||||
if elapsed >= exit_hold_duration {
|
||||
info!("Exiting config mode due to 5s button hold");
|
||||
board.board_hal.get_esp().set_restart_to_conf(false);
|
||||
// ensure clean http answer / visible confirmation
|
||||
Timer::after_millis(500).await;
|
||||
board.board_hal.deep_sleep(0).await;
|
||||
}
|
||||
|
||||
// Short tick while holding so the pattern updates smoothly.
|
||||
drop(board);
|
||||
Timer::after_millis(100).await;
|
||||
continue;
|
||||
}
|
||||
// Release lock and continue with normal wait blinking.
|
||||
drop(board);
|
||||
}
|
||||
|
||||
{
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
update_charge_indicator(&mut board).await;
|
||||
match update_charge_indicator(&mut board).await {
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
if !suppress_further_mppt_error {
|
||||
error!("Error updating charge indicator: {error}");
|
||||
suppress_further_mppt_error = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match wait_type {
|
||||
WaitType::MissingConfig => {
|
||||
// Keep existing behavior: circular filling pattern
|
||||
led_count %= 8;
|
||||
led_count += 1;
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, i < led_count);
|
||||
}
|
||||
}
|
||||
WaitType::ConfigButton => {
|
||||
// Alternating pattern: 1010 1010 -> 0101 0101
|
||||
pattern_step = (pattern_step + 1) % 2;
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0);
|
||||
}
|
||||
}
|
||||
WaitType::MqttConfig => {
|
||||
// Moving dot pattern
|
||||
pattern_step = (pattern_step + 1) % 8;
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, i == pattern_step);
|
||||
}
|
||||
match handle_serial_config(&mut board, &serial_config_receive, &reboot_now).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("Error handling serial config: {e}");
|
||||
}
|
||||
}
|
||||
board.board_hal.general_fault(true).await;
|
||||
|
||||
// MQTT updates in config mode
|
||||
let now = Instant::now();
|
||||
if last_mqtt_update.is_none()
|
||||
|| now.duration_since(last_mqtt_update.unwrap_or(Instant::from_secs(0)))
|
||||
>= Duration::from_secs(60)
|
||||
{
|
||||
let cur = board.board_hal.get_time().await;
|
||||
let timezone_time = cur.with_timezone(&timezone);
|
||||
|
||||
let esp = board.board_hal.get_esp();
|
||||
esp.mqtt_publish("/state", "config").await;
|
||||
esp.mqtt_publish("/firmware/last_online", &timezone_time.to_rfc3339())
|
||||
.await;
|
||||
last_mqtt_update = Some(now);
|
||||
}
|
||||
|
||||
// Skip default blink code when a progress display is active
|
||||
if !PROGRESS_ACTIVE.load(Ordering::Relaxed) {
|
||||
match wait_type {
|
||||
WaitType::MissingConfig => {
|
||||
// Keep existing behavior: circular filling pattern
|
||||
led_count %= 8;
|
||||
led_count += 1;
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, i < led_count).await;
|
||||
}
|
||||
}
|
||||
WaitType::ConfigButton => {
|
||||
// Alternating pattern: 1010 1010 -> 0101 0101
|
||||
pattern_step = (pattern_step + 1) % 2;
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0).await;
|
||||
}
|
||||
}
|
||||
WaitType::MqttConfig => {
|
||||
// Moving dot pattern
|
||||
pattern_step = (pattern_step + 1) % 8;
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, i == pattern_step).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
board.board_hal.general_fault(true).await;
|
||||
}
|
||||
}
|
||||
|
||||
Timer::after_millis(delay).await;
|
||||
{
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.general_fault(false).await;
|
||||
|
||||
// Clear all LEDs
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, false);
|
||||
// Skip clearing LEDs when progress is active to avoid interrupting the progress display
|
||||
if !PROGRESS_ACTIVE.load(Ordering::Relaxed) {
|
||||
board.board_hal.general_fault(false).await;
|
||||
|
||||
// Clear all LEDs
|
||||
for i in 0..8 {
|
||||
let _ = board.board_hal.fault(i, false).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer::after_millis(delay).await;
|
||||
|
||||
hal::PlantHal::feed_watchdog();
|
||||
|
||||
if wait_type == WaitType::MqttConfig && !MQTT_STAY_ALIVE.load(Ordering::Relaxed) {
|
||||
reboot_now.store(true, Ordering::Relaxed);
|
||||
}
|
||||
if reboot_now.load(Ordering::Relaxed) {
|
||||
info!("Rebooting now");
|
||||
//ensure clean http answer
|
||||
Timer::after_millis(500).await;
|
||||
BOARD_ACCESS
|
||||
@@ -1055,7 +1106,47 @@ async fn wait_infinity(
|
||||
}
|
||||
}
|
||||
|
||||
#[esp_hal_embassy::main]
|
||||
async fn handle_serial_config(
|
||||
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'_>>,
|
||||
serial_config_receive: &AtomicBool,
|
||||
reboot_now: &AtomicBool,
|
||||
) -> FatResult<()> {
|
||||
match board.board_hal.get_esp().read_serial_line().await {
|
||||
Ok(serial_line) => match serial_line {
|
||||
None => Ok(()),
|
||||
Some(line) => {
|
||||
if serial_config_receive.load(Ordering::Relaxed) {
|
||||
let ll = line.as_str();
|
||||
let config: PlantControllerConfig = serde_json::from_str(ll)?;
|
||||
board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.save_config(Vec::from(ll.as_bytes()))
|
||||
.await?;
|
||||
board.board_hal.set_config(config);
|
||||
serial_config_receive.store(false, Ordering::Relaxed);
|
||||
info!("Config received, rebooting");
|
||||
board.board_hal.get_esp().set_restart_to_conf(false);
|
||||
reboot_now.store(true, Ordering::Relaxed);
|
||||
Ok(())
|
||||
} else {
|
||||
if line == "automation:streamconfig" {
|
||||
serial_config_receive.store(true, Ordering::Relaxed);
|
||||
info!("streamconfig:recieving");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
error!("Error reading serial line");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use embassy_time::WithTimeout;
|
||||
#[esp_rtos::main]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
// intialize embassy
|
||||
logger::init_logger_from_env();
|
||||
@@ -1099,11 +1190,17 @@ async fn get_version(
|
||||
let hash = &env!("VERGEN_GIT_SHA")[0..8];
|
||||
|
||||
let board = board.board_hal.get_esp();
|
||||
let ota_slot = board.get_ota_slot();
|
||||
let heap = esp_alloc::HEAP.stats();
|
||||
VersionInfo {
|
||||
git_hash: branch + "@" + hash,
|
||||
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
|
||||
partition: ota_slot,
|
||||
current: format!("{:?}", board.current),
|
||||
slot0_state: format!("{:?}", board.slot0_state),
|
||||
slot1_state: format!("{:?}", board.slot1_state),
|
||||
heap_total: heap.size,
|
||||
heap_used: heap.current_usage,
|
||||
heap_free: heap.size.saturating_sub(heap.current_usage),
|
||||
heap_max_used: heap.max_usage,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1111,5 +1208,11 @@ async fn get_version(
|
||||
struct VersionInfo {
|
||||
git_hash: String,
|
||||
build_time: String,
|
||||
partition: String,
|
||||
current: String,
|
||||
slot0_state: String,
|
||||
slot1_state: String,
|
||||
heap_total: usize,
|
||||
heap_used: usize,
|
||||
heap_free: usize,
|
||||
heap_max_used: usize,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user