diff --git a/Software/MainBoard/rust/.idea/plant-ctrl2.iml b/Software/MainBoard/rust/.idea/plant-ctrl2.iml index cf84ae4..bbe0a70 100644 --- a/Software/MainBoard/rust/.idea/plant-ctrl2.iml +++ b/Software/MainBoard/rust/.idea/plant-ctrl2.iml @@ -3,6 +3,7 @@ + diff --git a/Software/MainBoard/rust/src/hal/esp.rs b/Software/MainBoard/rust/src/hal/esp.rs index 25e006b..89d0ef5 100644 --- a/Software/MainBoard/rust/src/hal/esp.rs +++ b/Software/MainBoard/rust/src/hal/esp.rs @@ -25,6 +25,7 @@ use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash}; use esp_bootloader_esp_idf::ota::OtaImageState::Valid; use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; use esp_bootloader_esp_idf::partitions::{AppPartitionSubType, FlashRegion}; +use esp_hal::Blocking; use esp_hal::gpio::{Input, RtcPinWithResistors}; use esp_hal::rng::Rng; use esp_hal::rtc_cntl::{ @@ -32,6 +33,7 @@ use esp_hal::rtc_cntl::{ Rtc, }; use esp_hal::system::software_reset; +use esp_hal::uart::Uart; use esp_println::println; use esp_radio::wifi::{ AccessPointConfig, AccessPointInfo, AuthMethod, ClientConfig, ModeConfig, ScanConfig, @@ -39,7 +41,7 @@ use esp_radio::wifi::{ }; use littlefs2::fs::Filesystem; use littlefs2_core::{FileType, PathBuf, SeekFrom}; -use log::{info, warn}; +use log::{error, info, warn}; use mcutie::{ Error, McutieBuilder, McutieReceiver, McutieTask, MqttMessage, PublishDisplay, Publishable, QoS, Topic, @@ -126,6 +128,7 @@ pub struct Esp<'a> { // RTC-capable GPIO used as external wake source (store the raw peripheral) pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>, + pub uart0: Uart<'a, Blocking>, pub ota: Ota<'static, MutexFlashStorage>, pub ota_target: &'static mut FlashRegion<'static, MutexFlashStorage>, @@ -152,6 +155,37 @@ macro_rules! mk_static { } impl Esp<'_> { + + pub(crate) async fn read_serial_line(&mut self) -> FatResult> { + let mut buf = [0u8; 1]; + let mut line = String::new(); + loop { + match self.uart0.read_buffered(&mut buf) { + Ok(read) => { + if (read == 0) { + return Ok(None); + } + let c = buf[0] as char; + if c == '\n' { + return Ok(Some(line)); + } + line.push(c); + } + Err(error ) => { + if line.is_empty() { + return Ok(None); + } else { + error!("Error reading serial line: {error:?}"); + // If we already have some data, we should probably wait a bit or just return what we have? + // But the protocol expects a full line or message. + // For simplicity in config mode, we can block here or just return None if nothing is there yet. + // However, if we started receiving, we should probably finish or timeout. + continue; + } + } + } + } + } pub(crate) async fn delete_file(&self, filename: String) -> FatResult<()> { let file = PathBuf::try_from(filename.as_str())?; let access = self.fs.lock().await; diff --git a/Software/MainBoard/rust/src/hal/mod.rs b/Software/MainBoard/rust/src/hal/mod.rs index e0515a1..f513b27 100644 --- a/Software/MainBoard/rust/src/hal/mod.rs +++ b/Software/MainBoard/rust/src/hal/mod.rs @@ -1,4 +1,5 @@ use lib_bms_protocol::BmsReadable; +use esp_hal::uart::{Config as UartConfig}; pub(crate) mod battery; // mod can_api; // replaced by external canapi crate pub mod esp; @@ -49,6 +50,7 @@ use crate::{ }; use alloc::boxed::Box; use alloc::format; +use alloc::string::String; use alloc::sync::Arc; use async_trait::async_trait; use bincode::{Decode, Encode}; @@ -94,6 +96,7 @@ use esp_hal::system::reset_reason; use esp_hal::time::Rate; use esp_hal::timer::timg::TimerGroup; use esp_hal::Blocking; +use esp_hal::uart::Uart; use esp_radio::{init, Controller}; use esp_storage::FlashStorage; use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem}; @@ -272,10 +275,6 @@ impl PlantHal { let pcnt_module = Pcnt::new(peripherals.PCNT); let free_pins = FreePeripherals { - // can: peripherals.can, - // adc1: peripherals.adc1, - // pcnt0: peripherals.pcnt0, - // pcnt1: peripherals.pcnt1, gpio0: peripherals.GPIO0, gpio2: peripherals.GPIO2, gpio3: peripherals.GPIO3, @@ -397,6 +396,11 @@ impl PlantHal { lfs2Filesystem::mount(alloc, lfs2filesystem).expect("Could not mount lfs2 filesystem"), )); + let uart0 = Uart::new(peripherals.UART0, UartConfig::default()) + .map_err(|_| FatError::String { + error: "Uart creation failed".to_string(), + })?; + let ap = interfaces.ap; let sta = interfaces.sta; let mut esp = Esp { @@ -412,6 +416,7 @@ impl PlantHal { current: running, slot0_state: state_0, slot1_state: state_1, + uart0 }; //init,reset rtc memory depending on cause diff --git a/Software/MainBoard/rust/src/hal/v4_hal.rs b/Software/MainBoard/rust/src/hal/v4_hal.rs index 08807a4..a920fd9 100644 --- a/Software/MainBoard/rust/src/hal/v4_hal.rs +++ b/Software/MainBoard/rust/src/hal/v4_hal.rs @@ -11,7 +11,8 @@ use crate::hal::{ }; use crate::log::{LogMessage, LOG_ACCESS}; use alloc::boxed::Box; -use alloc::string::ToString; +use alloc::string::{String, ToString}; +use alloc::vec; use async_trait::async_trait; use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET}; use canapi::SensorSlot; @@ -130,6 +131,7 @@ pub struct V4<'a> { >, twai_config: Option>, can_power: Output<'static>, + extra1: Output<'a>, extra2: Output<'a>, } @@ -254,7 +256,7 @@ pub(crate) async fn create_v4( charger, extra1, extra2, - can_power, + can_power }; Ok(Box::new(v)) } diff --git a/Software/MainBoard/rust/src/main.rs b/Software/MainBoard/rust/src/main.rs index 6d7b94e..5600601 100644 --- a/Software/MainBoard/rust/src/main.rs +++ b/Software/MainBoard/rust/src/main.rs @@ -13,7 +13,7 @@ esp_bootloader_esp_idf::esp_app_desc!(); use esp_backtrace as _; -use crate::config::{NetworkConfig, PlantConfig}; +use crate::config::{NetworkConfig, PlantConfig, PlantControllerConfig}; use crate::fat_error::FatResult; use crate::hal::esp::MQTT_STAY_ALIVE; use crate::hal::PROGRESS_ACTIVE; @@ -25,11 +25,12 @@ use crate::{ config::BoardVersion::Initial, hal::{PlantHal, HAL, PLANT_COUNT}, }; -use ::log::{info, warn}; +use ::log::{error, info, warn}; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::{format, vec}; +use alloc::vec::Vec; use chrono::{DateTime, Datelike, Timelike, Utc}; use chrono_tz::Tz::{self, UTC}; use core::sync::atomic::{AtomicBool, Ordering}; @@ -188,7 +189,15 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { .await; } info!("cur is {cur}"); - update_charge_indicator(&mut board).await; + match update_charge_indicator(&mut board).await { + Ok(_) => {} + Err(error) => { + board.board_hal.general_fault(true).await; + error!("Error updating charge indicator: {error}"); + log(LogMessage::MPPTError, 0, 0, "", "").await; + let _ = board.board_hal.set_charge_indicator(false).await; + } + } if board.board_hal.get_esp().get_restart_to_conf() { LOG_ACCESS .lock() @@ -749,23 +758,18 @@ 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) - .await; - } else { - //who knows - board.board_hal.general_fault(true).await; - log(LogMessage::MPPTError, 0, 0, "", "").await; - let _ = board.board_hal.set_charge_indicator(false).await; - - } + let current = board.board_hal.get_mptt_current().await?; + board + .board_hal + .set_charge_indicator(current.as_milliamperes() > 20_f64) + .await?; + Ok(()) } + async fn publish_tank_state( board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>, tank_state: &TankState, @@ -977,10 +981,27 @@ 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; loop { { 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 handle_serial_config(&mut board, &serial_config_receive, &reboot_now).await { + Ok(_) => {} + Err(e) => { + error!("Error handling serial config: {e}"); + } + } // Skip default blink code when a progress display is active if !PROGRESS_ACTIVE.load(Ordering::Relaxed) { @@ -1033,6 +1054,7 @@ async fn wait_infinity( 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 @@ -1047,6 +1069,42 @@ async fn wait_infinity( } } +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(()) + } + } +} + #[esp_rtos::main] async fn main(spawner: Spawner) -> ! { // intialize embassy