startup and ota state detection working, now wifi_ap next
This commit is contained in:
209
rust/src/main.rs
209
rust/src/main.rs
@@ -10,6 +10,7 @@ esp_bootloader_esp_idf::esp_app_desc!();
|
||||
use esp_backtrace as _;
|
||||
|
||||
use crate::config::PlantConfig;
|
||||
use crate::tank::WATER_FROZEN_THRESH;
|
||||
use crate::{
|
||||
config::BoardVersion::INITIAL,
|
||||
hal::{PlantHal, HAL, PLANT_COUNT},
|
||||
@@ -20,23 +21,36 @@ use alloc::borrow::ToOwned;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{format, vec};
|
||||
use core::any::Any;
|
||||
use anyhow::{bail, Context};
|
||||
use chrono::{DateTime, Datelike, Timelike, Utc};
|
||||
use chrono_tz::Tz::{self};
|
||||
use core::sync::atomic::Ordering;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
|
||||
use embassy_sync::lazy_lock::LazyLock;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
||||
use embassy_sync::once_lock::OnceLock;
|
||||
use embassy_time::Timer;
|
||||
use esp_bootloader_esp_idf::ota::OtaImageState;
|
||||
use esp_hal::rom::ets_delay_us;
|
||||
use esp_hal::system::software_reset;
|
||||
use esp_println::{logger, println};
|
||||
use hal::battery::BatteryState;
|
||||
use log::{log, LogMessage};
|
||||
use plant_state::PlantState;
|
||||
use portable_atomic::AtomicBool;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::tank::{TankError, WATER_FROZEN_THRESH};
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn custom_halt() -> ! {
|
||||
println!("Fatal error occurred. Restarting in 10 seconds...");
|
||||
|
||||
for _delay in 0..30 {
|
||||
ets_delay_us(1_000_000);
|
||||
}
|
||||
println!("resetting");
|
||||
//give serial transmit time to finish
|
||||
ets_delay_us(500_000);
|
||||
software_reset()
|
||||
}
|
||||
|
||||
//use tank::*;
|
||||
mod config;
|
||||
@@ -48,10 +62,8 @@ mod tank;
|
||||
extern crate alloc;
|
||||
//mod webserver;
|
||||
|
||||
pub static BOARD_ACCESS: LazyLock<Mutex<CriticalSectionRawMutex, HAL>> =
|
||||
LazyLock::new(|| {
|
||||
PlantHal::create().unwrap()
|
||||
});
|
||||
pub static BOARD_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, HAL>> = OnceLock::new();
|
||||
|
||||
pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
@@ -140,14 +152,9 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
version.git_hash, version.build_time
|
||||
);
|
||||
|
||||
let _esp = BOARD_ACCESS.get().await.lock().await.board_hal.get_esp();
|
||||
|
||||
//TODO
|
||||
//let count = unsafe { esp_ota_get_app_partition_count() };
|
||||
//log::info!("Partition count is {}", count);
|
||||
//let mut ota_state: esp_ota_img_states_t = 0;
|
||||
//let running_partition = unsafe { esp_ota_get_running_partition() };
|
||||
//let partition_address = unsafe { (*running_partition).address };
|
||||
//log::info!("Partition address is {}", address);
|
||||
let partition_address = 0x1337;
|
||||
|
||||
// TODO
|
||||
//let ota_state_string = unsafe {
|
||||
@@ -169,9 +176,9 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
//}
|
||||
//};
|
||||
//log(LogMessage::PartitionState, 0, 0, "", ota_state_string);
|
||||
let ota_state_string = "unknown";
|
||||
let _ota_state_string = "unknown";
|
||||
println!("faul led");
|
||||
let mut board = BOARD_ACCESS.get().lock().await;
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.general_fault(false).await;
|
||||
println!("faul led2");
|
||||
let cur = board
|
||||
@@ -184,7 +191,6 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
board.board_hal.general_fault(true);
|
||||
anyhow::Ok(board.board_hal.get_esp().time())
|
||||
})?;
|
||||
|
||||
//check if we know the time current > 2020 (plausibility checks, this code is newer than 2020)
|
||||
if cur.year() < 2020 {
|
||||
to_config = true;
|
||||
@@ -192,10 +198,10 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
info!("cur is {}", cur);
|
||||
update_charge_indicator().await;
|
||||
|
||||
update_charge_indicator(&mut board).await;
|
||||
println!("faul led3");
|
||||
if board.board_hal.get_esp().get_restart_to_conf() {
|
||||
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
|
||||
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "").await;
|
||||
for _i in 0..2 {
|
||||
board.board_hal.general_fault(true).await;
|
||||
Timer::after_millis(100).await;
|
||||
@@ -221,12 +227,15 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
} else {
|
||||
board.board_hal.general_fault(false).await;
|
||||
}
|
||||
} else {
|
||||
info!("no mode override");
|
||||
}
|
||||
|
||||
if board.board_hal.get_config().hardware.board == INITIAL
|
||||
&& board.board_hal.get_config().network.ssid.is_none()
|
||||
{
|
||||
let _ = board.board_hal.get_esp().wifi_ap();
|
||||
info!("No wifi configured, starting initial config mode");
|
||||
board.board_hal.get_esp().wifi_ap().await.unwrap();
|
||||
drop(board);
|
||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||
//TODO
|
||||
@@ -254,7 +263,6 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// let timezone = match &board.board_hal.get_config().timezone {
|
||||
// Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| {
|
||||
// info!("Invalid timezone '{}', falling back to UTC", tz_str);
|
||||
@@ -262,25 +270,18 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
// }),
|
||||
// None => UTC, // Fallback to UTC if no timezone is set
|
||||
// };
|
||||
let timezone = Tz::UTC;
|
||||
let _timezone = Tz::UTC;
|
||||
|
||||
let timezone_time = cur;//TODO.with_timezone(&timezone);
|
||||
drop(board);
|
||||
|
||||
let timezone_time = cur; //TODO.with_timezone(&timezone);
|
||||
info!(
|
||||
"Running logic at utc {} and {} {}",
|
||||
cur,
|
||||
"todo timezone.name()",
|
||||
timezone_time
|
||||
cur, "todo timezone.name()", timezone_time
|
||||
);
|
||||
|
||||
if let NetworkMode::WIFI { ref ip_address, .. } = network_mode {
|
||||
publish_firmware_info(
|
||||
version,
|
||||
partition_address,
|
||||
ota_state_string,
|
||||
ip_address,
|
||||
&timezone_time.to_rfc3339(),
|
||||
)
|
||||
.await;
|
||||
publish_firmware_info(version, ip_address, &timezone_time.to_rfc3339()).await;
|
||||
publish_battery_state().await;
|
||||
let _ = publish_mppt_state().await;
|
||||
}
|
||||
@@ -299,10 +300,8 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
.to_string()
|
||||
.as_str(),
|
||||
"",
|
||||
).await;
|
||||
|
||||
//TODO must drop board here?
|
||||
//drop(board);
|
||||
)
|
||||
.await;
|
||||
|
||||
if to_config {
|
||||
//check if client or ap mode and init Wi-Fi
|
||||
@@ -316,7 +315,7 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
log(LogMessage::NormalRun, 0, 0, "", "").await;
|
||||
}
|
||||
|
||||
let dry_run = false;
|
||||
let _dry_run = false;
|
||||
//
|
||||
// let tank_state = determine_tank_state(&mut board);
|
||||
//
|
||||
@@ -353,7 +352,7 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
// }
|
||||
// }
|
||||
|
||||
let mut water_frozen = false;
|
||||
let mut _water_frozen = false;
|
||||
//TODO
|
||||
let water_temp = anyhow::Ok(12_f32);
|
||||
// board
|
||||
@@ -364,14 +363,14 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
|
||||
if let Ok(res) = water_temp {
|
||||
if res < WATER_FROZEN_THRESH {
|
||||
water_frozen = true;
|
||||
_water_frozen = true;
|
||||
}
|
||||
}
|
||||
|
||||
//publish_tank_state(&tank_state, &water_temp).await;
|
||||
|
||||
|
||||
let plantstate: [PlantState; PLANT_COUNT] = [
|
||||
board = BOARD_ACCESS.get().await.lock().await;
|
||||
let _plantstate: [PlantState; PLANT_COUNT] = [
|
||||
PlantState::read_hardware_state(0, &mut board).await,
|
||||
PlantState::read_hardware_state(1, &mut board).await,
|
||||
PlantState::read_hardware_state(2, &mut board).await,
|
||||
@@ -468,7 +467,7 @@ async fn safe_main() -> anyhow::Result<()> {
|
||||
.await
|
||||
.unwrap_or(BatteryState::Unknown);
|
||||
|
||||
let mut light_state = LightState {
|
||||
let light_state = LightState {
|
||||
enabled: board.board_hal.get_config().night_lamp.enabled,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -611,7 +610,7 @@ pub async fn do_secure_pump(
|
||||
let mut error = false;
|
||||
let mut first_error = true;
|
||||
let mut pump_time_s = 0;
|
||||
let board = &mut BOARD_ACCESS.get().lock().await;
|
||||
let board = &mut BOARD_ACCESS.get().await.lock().await;
|
||||
if !dry_run {
|
||||
// board
|
||||
// .board_hal
|
||||
@@ -733,8 +732,7 @@ pub async fn do_secure_pump(
|
||||
})
|
||||
}
|
||||
|
||||
async fn update_charge_indicator() {
|
||||
let board = &mut BOARD_ACCESS.get().lock().await;
|
||||
async fn update_charge_indicator(board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'_>>) {
|
||||
//we have mppt controller, ask it for charging current
|
||||
// let tank_state = determine_tank_state(&mut board);
|
||||
//
|
||||
@@ -779,7 +777,8 @@ async fn update_charge_indicator() {
|
||||
else if let Ok(charging) = board
|
||||
.board_hal
|
||||
.get_battery_monitor()
|
||||
.average_current_milli_ampere().await
|
||||
.average_current_milli_ampere()
|
||||
.await
|
||||
{
|
||||
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
||||
} else {
|
||||
@@ -830,51 +829,45 @@ async fn update_charge_indicator() {
|
||||
// }
|
||||
// }
|
||||
|
||||
async fn publish_firmware_info(
|
||||
version: VersionInfo,
|
||||
address: u32,
|
||||
ota_state_string: &str,
|
||||
ip_address: &String,
|
||||
timezone_time: &String,
|
||||
) {
|
||||
let board = &mut BOARD_ACCESS.get().lock().await;
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
async fn publish_firmware_info(version: VersionInfo, ip_address: &String, timezone_time: &String) {
|
||||
let board = &mut BOARD_ACCESS.get().await.lock().await;
|
||||
let esp = board.board_hal.get_esp();
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/address", ip_address.as_bytes())
|
||||
.await;
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/githash", version.git_hash.as_bytes())
|
||||
.await;
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/buildtime", version.build_time.as_bytes())
|
||||
.await;
|
||||
let _ = board.board_hal.get_esp().mqtt_publish(
|
||||
"/firmware/last_online",
|
||||
timezone_time.as_bytes(),
|
||||
);
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt_publish("/firmware/ota_state", ota_state_string.as_bytes())
|
||||
let _ = esp.mqtt_publish("/firmware/last_online", timezone_time.as_bytes());
|
||||
let _ = esp
|
||||
.mqtt_publish(
|
||||
"/firmware/ota_state",
|
||||
state_to_string(esp.ota_state).as_bytes(),
|
||||
)
|
||||
.await;
|
||||
let _ = board.board_hal.get_esp().mqtt_publish(
|
||||
"/firmware/partition_address",
|
||||
format!("{:#06x}", address).as_bytes(),
|
||||
);
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt_publish("/state", "online".as_bytes())
|
||||
let slot = esp.slot;
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/ota_slot", format!("slot{slot}").as_bytes())
|
||||
.await;
|
||||
let _ = esp.mqtt_publish("/state", "online".as_bytes()).await;
|
||||
}
|
||||
|
||||
fn state_to_string(state: OtaImageState) -> &'static str {
|
||||
match state {
|
||||
OtaImageState::New => "New",
|
||||
OtaImageState::PendingVerify => "PendingVerify",
|
||||
OtaImageState::Valid => "Valid",
|
||||
OtaImageState::Invalid => "Invalid",
|
||||
OtaImageState::Aborted => "Aborted",
|
||||
OtaImageState::Undefined => "Undefined",
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_connect_wifi_sntp_mqtt() -> NetworkMode {
|
||||
let board = &mut BOARD_ACCESS.get().lock().await;
|
||||
let board = &mut BOARD_ACCESS.get().await.lock().await;
|
||||
let nw_conf = &board.board_hal.get_config().network.clone();
|
||||
match board.board_hal.get_esp().wifi(nw_conf).await {
|
||||
Ok(ip_info) => {
|
||||
@@ -941,6 +934,7 @@ async fn pump_info(
|
||||
Ok(state) => {
|
||||
let _ = BOARD_ACCESS
|
||||
.get()
|
||||
.await
|
||||
.lock()
|
||||
.await
|
||||
.board_hal
|
||||
@@ -957,7 +951,7 @@ async fn pump_info(
|
||||
}
|
||||
|
||||
async fn publish_mppt_state() -> anyhow::Result<()> {
|
||||
let board_hal = &mut BOARD_ACCESS.get().lock().await.board_hal;
|
||||
let board_hal = &mut BOARD_ACCESS.get().await.lock().await.board_hal;
|
||||
let current = board_hal.get_mptt_current().await?;
|
||||
let voltage = board_hal.get_mptt_voltage().await?;
|
||||
let solar_state = Solar {
|
||||
@@ -975,8 +969,12 @@ async fn publish_mppt_state() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
async fn publish_battery_state() -> () {
|
||||
let board = &mut BOARD_ACCESS.get().lock().await;
|
||||
let state = board.board_hal.get_battery_monitor().get_battery_state().await;
|
||||
let board = &mut BOARD_ACCESS.get().await.lock().await;
|
||||
let state = board
|
||||
.board_hal
|
||||
.get_battery_monitor()
|
||||
.get_battery_state()
|
||||
.await;
|
||||
if let Ok(serialized_battery_state_bytes) =
|
||||
serde_json::to_string(&state).map(|s| s.into_bytes())
|
||||
{
|
||||
@@ -992,8 +990,9 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
let mut led_count = 8;
|
||||
let mut pattern_step = 0;
|
||||
loop {
|
||||
update_charge_indicator().await;
|
||||
let mut board = BOARD_ACCESS.get().lock().await;
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
update_charge_indicator(&mut board).await;
|
||||
|
||||
match wait_type {
|
||||
WaitType::MissingConfig => {
|
||||
// Keep existing behavior: circular filling pattern
|
||||
@@ -1023,7 +1022,7 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
drop(board);
|
||||
|
||||
Timer::after_millis(delay).await;
|
||||
let mut board = BOARD_ACCESS.get().lock().await;
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.general_fault(false);
|
||||
|
||||
// Clear all LEDs
|
||||
@@ -1039,7 +1038,13 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
if reboot_now.load(Ordering::Relaxed) {
|
||||
//ensure clean http answer
|
||||
Timer::after_millis(500).await;
|
||||
BOARD_ACCESS.get().lock().await.board_hal.deep_sleep(1);
|
||||
BOARD_ACCESS
|
||||
.get()
|
||||
.await
|
||||
.lock()
|
||||
.await
|
||||
.board_hal
|
||||
.deep_sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1049,19 +1054,21 @@ async fn main(spawner: Spawner) {
|
||||
// intialize embassy
|
||||
logger::init_logger_from_env();
|
||||
//force init here!
|
||||
let board = BOARD_ACCESS.get().lock().await;
|
||||
drop(board);
|
||||
println!("test");
|
||||
println!("Hal init");
|
||||
match BOARD_ACCESS.init(PlantHal::create(spawner).unwrap()) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
panic!("Could not set hal to static")
|
||||
}
|
||||
}
|
||||
println!("Hal init done, starting logic");
|
||||
|
||||
info!("Embassy initialized!");
|
||||
|
||||
let result = safe_main().await;
|
||||
match result {
|
||||
match safe_main().await {
|
||||
// this should not get triggered, safe_main should not return but go into deep sleep with sensible
|
||||
// timeout, this is just a fallback
|
||||
Ok(_) => {
|
||||
warn!("Main app finished, but should never do, restarting");
|
||||
let board = &mut BOARD_ACCESS.get().lock().await.board_hal;
|
||||
let board = &mut BOARD_ACCESS.get().await.lock().await.board_hal;
|
||||
|
||||
board.get_esp().set_restart_to_conf(false);
|
||||
board.deep_sleep(1);
|
||||
|
||||
Reference in New Issue
Block a user