minimal startup running

This commit is contained in:
Empire Phoenix 2025-09-14 13:19:30 +02:00
parent b3cc313139
commit 4aa25c687b
7 changed files with 146 additions and 119 deletions

View File

@ -57,6 +57,7 @@ esp-hal = { version = "=1.0.0-rc.0", features = [
"esp32c6",
"log-04",
"unstable",
"rt"
] }
log = "0.4.27"

View File

@ -175,7 +175,7 @@ impl Esp<'_> {
}
}
pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<Stack> {
pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<Stack<'static>> {
let _ssid = match self.load_config() {
Ok(config) => config.network.ap_ssid.clone(),
Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(),
@ -207,7 +207,7 @@ impl Esp<'_> {
let controller = self.controller.take().unwrap();
spawner.spawn(connection(controller)).ok();
spawner.spawn(net_task(runner)).ok();
spawner.spawn(run_dhcp(stack, gw_ip_addr_str)).ok();
spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok();
loop {
if stack.is_link_up() {
@ -699,7 +699,7 @@ impl Esp<'_> {
}
#[embassy_executor::task]
async fn run_dhcp(stack: &'static Stack<'static>, gw_ip_addr: &'static str) {
async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
use core::net::{Ipv4Addr, SocketAddrV4};
use edge_dhcp::{
@ -716,7 +716,7 @@ async fn run_dhcp(stack: &'static Stack<'static>, gw_ip_addr: &'static str) {
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
let buffers = UdpBuffers::<3, 1024, 1024, 10>::new();
let unbound_socket = Udp::new(*stack, &buffers);
let unbound_socket = Udp::new(stack, &buffers);
let mut bound_socket = unbound_socket
.bind(core::net::SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::UNSPECIFIED,

View File

@ -11,10 +11,12 @@ use crate::{
use anyhow::{bail, Result};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use esp_hal::gpio::{Level, Output, OutputConfig};
use log::info;
use measurements::{Current, Voltage};
pub struct Initial<'a> {
//pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
pub(crate) general_fault: Output<'a>,
pub(crate) esp: Esp<'a>,
pub(crate) config: PlantControllerConfig,
pub(crate) battery: Box<dyn BatteryInteraction + Send>,
@ -47,21 +49,14 @@ impl RTCModuleInteraction for NoRTC {
}
pub(crate) fn create_initial_board(
//free_pins: FreePeripherals,
_fs_mount_error: bool,
free_pins: FreePeripherals<'static>,
config: PlantControllerConfig,
esp: Esp<'static>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
log::info!("Start initial");
// let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?;
// general_fault.set_pull(Pull::Floating)?;
// general_fault.set_low()?;
//
// if fs_mount_error {
// general_fault.set_high()?
// }
let general_fault = Output::new(free_pins.gpio23, Level::Low, OutputConfig::default());
let v = Initial {
//general_fault,
general_fault,
config,
esp,
battery: Box::new(NoBatteryMonitor {}),
@ -122,8 +117,8 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
bail!("Please configure board revision")
}
async fn general_fault(&mut self, _enable: bool) {
//let _ = self.general_fault.set_state(enable.into());
async fn general_fault(&mut self, enable: bool) {
self.general_fault.set_level(enable.into());
}
async fn test(&mut self) -> Result<()> {

View File

@ -6,6 +6,10 @@ mod rtc;
use crate::alloc::string::ToString;
use crate::hal::rtc::RTCModuleInteraction;
use esp_hal::peripherals::Peripherals;
use esp_hal::peripherals::GPIO23;
use esp_hal::peripherals::GPIO6;
//use crate::hal::water::TankSensor;
use crate::{
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
@ -33,7 +37,7 @@ use embassy_sync::once_lock::OnceLock;
use embassy_time::{Duration, Timer};
use esp_bootloader_esp_idf::partitions::DataPartitionSubType;
use esp_hal::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::gpio::{Input, InputConfig, Io, Pull};
use esp_hal::timer::systimer::SystemTimer;
use esp_println::{print, println};
use measurements::{Current, Voltage};
@ -41,6 +45,7 @@ use measurements::{Current, Voltage};
use embassy_sync::mutex::{Mutex, MutexGuard};
use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::analog::adc::Adc;
use esp_hal::rng::Rng;
use esp_hal::timer::timg::TimerGroup;
use esp_wifi::wifi::{
@ -125,14 +130,14 @@ impl dyn BoardInteraction<'_> {
}
#[allow(dead_code)]
pub struct FreePeripherals {
pub struct FreePeripherals<'a> {
// pub gpio0: Gpio0,
// pub gpio1: Gpio1,
// pub gpio2: Gpio2,
// pub gpio3: Gpio3,
// pub gpio4: Gpio4,
// pub gpio5: Gpio5,
// pub gpio6: Gpio6,
pub gpio6: GPIO6<'a>,
// pub gpio7: Gpio7,
// pub gpio8: Gpio8,
// //config button here
@ -148,7 +153,7 @@ pub struct FreePeripherals {
// //i2c here
// pub gpio21: Gpio21,
// pub gpio22: Gpio22,
// pub gpio23: Gpio23,
pub gpio23: GPIO23<'a>,
// pub gpio24: Gpio24,
// pub gpio25: Gpio25,
// pub gpio26: Gpio26,
@ -192,7 +197,7 @@ impl PlantHal {
pub async fn create(spawner: Spawner) -> Result<Mutex<CriticalSectionRawMutex, HAL<'static>>> {
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
let peripherals: Peripherals = esp_hal::init(config);
esp_alloc::heap_allocator!(size: 64 * 1024);
let systimer = SystemTimer::new(peripherals.SYSTIMER);
@ -215,8 +220,9 @@ impl PlantHal {
use esp_hal::timer::systimer::SystemTimer;
esp_hal_embassy::init(systimer.alarm0);
//let mut adc1 = Adc::new(peripherals.ADC1, adc1_config);
//
// let free_pins = FreePeripherals {
let free_pins = FreePeripherals {
// can: peripherals.can,
// adc1: peripherals.adc1,
// pcnt0: peripherals.pcnt0,
@ -227,7 +233,7 @@ impl PlantHal {
// gpio3: peripherals.pins.gpio3,
// gpio4: peripherals.pins.gpio4,
// gpio5: peripherals.pins.gpio5,
// gpio6: peripherals.pins.gpio6,
gpio6: peripherals.GPIO6,
// gpio7: peripherals.pins.gpio7,
// gpio8: peripherals.pins.gpio8,
// gpio10: peripherals.pins.gpio10,
@ -241,7 +247,7 @@ impl PlantHal {
// gpio18: peripherals.pins.gpio18,
// gpio21: peripherals.pins.gpio21,
// gpio22: peripherals.pins.gpio22,
// gpio23: peripherals.pins.gpio23,
gpio23: peripherals.GPIO23,
// gpio24: peripherals.pins.gpio24,
// gpio25: peripherals.pins.gpio25,
// gpio26: peripherals.pins.gpio26,
@ -249,7 +255,7 @@ impl PlantHal {
// gpio28: peripherals.pins.gpio28,
// gpio29: peripherals.pins.gpio29,
// gpio30: peripherals.pins.gpio30,
// };
};
//
let mut storage = esp_storage::FlashStorage::new();
@ -392,7 +398,7 @@ impl PlantHal {
let board_hal: Box<dyn BoardInteraction + Send> = match config.hardware.board {
BoardVersion::INITIAL => {
initial_hal::create_initial_board(fs_mount_error, config, esp)?
initial_hal::create_initial_board(free_pins, config, esp)?
}
// BoardVersion::V3 => {
// v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)?
@ -417,7 +423,7 @@ impl PlantHal {
);
HAL {
board_hal: initial_hal::create_initial_board(
fs_mount_error,
free_pins,
PlantControllerConfig::default(),
esp,
)?,

View File

@ -5,6 +5,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::lazy_lock::LazyLock;
use embassy_sync::mutex::Mutex;
use embassy_time::Instant;
use esp_println::println;
use log::info;
use serde::Serialize;
use strum_macros::IntoStaticStr;

View File

@ -64,7 +64,7 @@ mod webserver;
extern crate alloc;
//mod webserver;
pub static BOARD_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, HAL>> = OnceLock::new();
pub static BOARD_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, HAL<'static>>> = OnceLock::new();
pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false);
@ -143,7 +143,7 @@ enum NetworkMode {
OFFLINE,
}
async fn safe_main() -> anyhow::Result<()> {
async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
info!("Startup Rust");
let mut to_config = false;
@ -179,10 +179,8 @@ async fn safe_main() -> anyhow::Result<()> {
//};
//log(LogMessage::PartitionState, 0, 0, "", ota_state_string);
let _ota_state_string = "unknown";
println!("faul led");
let mut board = BOARD_ACCESS.get().await.lock().await;
board.board_hal.general_fault(false).await;
println!("faul led2");
let cur = board
.board_hal
.get_rtc_module()
@ -196,7 +194,7 @@ async fn safe_main() -> anyhow::Result<()> {
//check if we know the time current > 2020 (plausibility checks, this code is newer than 2020)
if cur.year() < 2020 {
to_config = true;
log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "");
log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "").await;
}
info!("cur is {}", cur);
@ -237,12 +235,15 @@ async fn safe_main() -> anyhow::Result<()> {
&& board.board_hal.get_config().network.ssid.is_none()
{
info!("No wifi configured, starting initial config mode");
let stack = board.board_hal.get_esp().wifi_ap().await?;
let reboot_now = Arc::new(AtomicBool::new(false));
println!("starting webserver");
let _webserver = httpd(reboot_now.clone(), stack).await;
wait_infinity(WaitType::MissingConfig, reboot_now.clone()).await;
spawner.spawn(httpd(reboot_now.clone(), stack))?;
wait_infinity(board, WaitType::MissingConfig, reboot_now.clone()).await;
}
info!("attempting to connect wifi");
@ -257,7 +258,12 @@ async fn safe_main() -> anyhow::Result<()> {
if matches!(network_mode, NetworkMode::OFFLINE) && to_config {
info!("Could not connect to station and config mode forced, switching to ap mode!");
match board.board_hal.get_esp().wifi_ap().await {
let res = {
let esp = board.board_hal.get_esp();
esp.wifi_ap().await
};
match res {
Ok(_) => {
info!("Started ap, continuing")
}
@ -274,8 +280,6 @@ async fn safe_main() -> anyhow::Result<()> {
// };
let _timezone = Tz::UTC;
drop(board);
let timezone_time = cur; //TODO.with_timezone(&timezone);
info!(
"Running logic at utc {} and {} {}",
@ -283,9 +287,9 @@ async fn safe_main() -> anyhow::Result<()> {
);
if let NetworkMode::WIFI { ref ip_address, .. } = network_mode {
publish_firmware_info(version, ip_address, &timezone_time.to_rfc3339()).await;
publish_battery_state().await;
let _ = publish_mppt_state().await;
publish_firmware_info(&mut board, version, ip_address, &timezone_time.to_rfc3339()).await;
publish_battery_state(&mut board).await;
let _ = publish_mppt_state(&mut board).await;
}
log(
@ -312,7 +316,8 @@ async fn safe_main() -> anyhow::Result<()> {
let reboot_now = Arc::new(AtomicBool::new(false));
//TODO
//let _webserver = httpd(reboot_now.clone());
wait_infinity(WaitType::ConfigButton, reboot_now.clone()).await;
let board = BOARD_ACCESS.get().await.lock().await;
wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await;
} else {
log(LogMessage::NormalRun, 0, 0, "", "").await;
}
@ -589,11 +594,10 @@ async fn safe_main() -> anyhow::Result<()> {
if stay_alive {
info!("Go to stay alive move");
drop(board);
let reboot_now = Arc::new(AtomicBool::new(false));
//TODO
//let _webserver = httpd(reboot_now.clone());
wait_infinity(WaitType::MqttConfig, reboot_now.clone()).await;
wait_infinity(board, WaitType::MqttConfig, reboot_now.clone()).await;
} else {
board.board_hal.get_esp().set_restart_to_conf(false);
board
@ -831,8 +835,12 @@ async fn update_charge_indicator(board: &mut MutexGuard<'_, CriticalSectionRawMu
// }
// }
async fn publish_firmware_info(version: VersionInfo, ip_address: &String, timezone_time: &String) {
let board = &mut BOARD_ACCESS.get().await.lock().await;
async fn publish_firmware_info(
mut board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
version: VersionInfo,
ip_address: &String,
timezone_time: &String,
) {
let esp = board.board_hal.get_esp();
let _ = esp
.mqtt_publish("/firmware/address", ip_address.as_bytes())
@ -952,10 +960,11 @@ async fn pump_info(
};
}
async fn publish_mppt_state() -> anyhow::Result<()> {
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?;
async fn publish_mppt_state(
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
) -> anyhow::Result<()> {
let current = board.board_hal.get_mptt_current().await?;
let voltage = board.board_hal.get_mptt_voltage().await?;
let solar_state = Solar {
current_ma: current.as_milliamperes() as u32,
voltage_ma: voltage.as_millivolts() as u32,
@ -963,15 +972,17 @@ async fn publish_mppt_state() -> anyhow::Result<()> {
if let Ok(serialized_solar_state_bytes) =
serde_json::to_string(&solar_state).map(|s| s.into_bytes())
{
let _ = board_hal
let _ = board
.board_hal
.get_esp()
.mqtt_publish("/mppt", &serialized_solar_state_bytes);
}
Ok(())
}
async fn publish_battery_state() -> () {
let board = &mut BOARD_ACCESS.get().await.lock().await;
async fn publish_battery_state(
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
) -> () {
let state = board
.board_hal
.get_battery_monitor()
@ -983,15 +994,24 @@ async fn publish_battery_state() -> () {
board
.board_hal
.get_esp()
.mqtt_publish("/battery", &serialized_battery_state_bytes);
.mqtt_publish("/battery", &serialized_battery_state_bytes)
.await;
}
}
async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
async fn wait_infinity(
board: MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
wait_type: WaitType,
reboot_now: Arc<AtomicBool>,
) -> ! {
//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);
let delay = wait_type.blink_pattern();
let mut led_count = 8;
let mut pattern_step = 0;
loop {
{
let mut board = BOARD_ACCESS.get().await.lock().await;
update_charge_indicator(&mut board).await;
@ -1019,19 +1039,20 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
}
}
}
board.board_hal.general_fault(true);
drop(board);
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);
board.board_hal.general_fault(false).await;
// Clear all LEDs
for i in 0..8 {
let _ = board.board_hal.fault(i, false);
}
drop(board);
}
Timer::after_millis(delay).await;
if wait_type == WaitType::MqttConfig && !STAY_ALIVE.load(Ordering::Relaxed) {
@ -1065,7 +1086,7 @@ async fn main(spawner: Spawner) {
}
println!("Hal init done, starting logic");
match safe_main().await {
match safe_main(spawner).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(_) => {

View File

@ -16,10 +16,12 @@ use anyhow::{bail, Context};
use chrono::DateTime;
use core::result::Result::Ok;
use core::sync::atomic::AtomicBool;
use embassy_executor::Spawner;
use embassy_net::tcp::TcpSocket;
use embassy_net::{IpListenEndpoint, Stack};
use embassy_time::{Duration, Timer};
use esp_println::{print, println};
use esp_wifi::wifi::WifiController;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Debug)]
@ -388,7 +390,8 @@ pub struct NightLampCommand {
// }
// }
pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'_>) {
#[embassy_executor::task]
pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
let mut rx_buffer = [0; 1536];
let mut tx_buffer = [0; 1536];
println!("Stack {}", stack.is_config_up());