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:
2026-05-04 23:46:27 +02:00
parent ecb7707357
commit db401aac55
23 changed files with 2029 additions and 1292 deletions

108
rust/src/log/interceptor.rs Normal file
View File

@@ -0,0 +1,108 @@
use alloc::string::String;
use alloc::vec::Vec;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex as BlockingMutex;
use log::{LevelFilter, Log, Metadata, Record};
const MAX_LIVE_LOG_ENTRIES: usize = 64;
struct LiveLogBuffer {
entries: Vec<(u64, String)>,
next_seq: u64,
}
impl LiveLogBuffer {
const fn new() -> Self {
Self {
entries: Vec::new(),
next_seq: 0,
}
}
fn push(&mut self, text: String) {
if self.entries.len() >= MAX_LIVE_LOG_ENTRIES {
self.entries.remove(0);
}
self.entries.push((self.next_seq, text));
self.next_seq += 1;
}
fn get_after(&self, after: Option<u64>) -> (Vec<(u64, String)>, bool, u64) {
let next_seq = self.next_seq;
match after {
None => (self.entries.clone(), false, next_seq),
Some(after_seq) => {
let result: Vec<_> = self.entries
.iter()
.filter(|(seq, _)| *seq > after_seq)
.cloned()
.collect();
// Dropped if there are entries that should exist (seq > after_seq) but
// the oldest retained entry has a higher seq than after_seq + 1.
let dropped = if next_seq > after_seq.saturating_add(1) {
if let Some((oldest_seq, _)) = self.entries.first() {
*oldest_seq > after_seq.saturating_add(1)
} else {
// Buffer empty but entries were written — all dropped
true
}
} else {
false
};
(result, dropped, next_seq)
}
}
}
}
pub struct InterceptorLogger {
live_log: BlockingMutex<CriticalSectionRawMutex, core::cell::RefCell<LiveLogBuffer>>,
}
impl InterceptorLogger {
pub const fn new() -> Self {
Self {
live_log: BlockingMutex::new(core::cell::RefCell::new(LiveLogBuffer::new())),
}
}
/// Returns (entries_after, dropped, next_seq).
/// Pass `after = None` to retrieve the entire current buffer.
/// Pass `after = Some(seq)` to retrieve only entries with seq > that value.
pub fn get_live_logs(&self, after: Option<u64>) -> (Vec<(u64, String)>, bool, u64) {
self.live_log.lock(|buf| buf.borrow().get_after(after))
}
pub fn init(&'static self) {
match log::set_logger(self).map(|()| log::set_max_level(LevelFilter::Info)) {
Ok(()) => {}
Err(_e) => {
esp_println::println!("ERROR: Logger already set");
}
}
}
}
impl Log for InterceptorLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let message = alloc::format!("{}: {}", record.level(), record.args());
// Print to serial
esp_println::println!("{}", message);
// Store in live log ring buffer
self.live_log.lock(|buf| {
buf.borrow_mut().push(message);
});
}
}
fn flush(&self) {}
}

View File

@@ -1,20 +1,21 @@
use crate::hal::TIME_ACCESS;
use crate::vec;
use crate::BOARD_ACCESS;
use alloc::string::ToString;
use alloc::vec::Vec;
use bytemuck::{AnyBitPattern, Pod, Zeroable};
use deranged::RangedU8;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel;
use embassy_sync::mutex::Mutex;
use esp_hal::Persistable;
use log::info;
use log::{info, warn};
use serde::Serialize;
use strum_macros::IntoStaticStr;
use unit_enum::UnitEnum;
const LOG_ARRAY_SIZE: u8 = 220;
const MAX_LOG_ARRAY_INDEX: u8 = LOG_ARRAY_SIZE - 1;
#[esp_hal::ram(rtc_fast, persistent)]
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
static mut LOG_ARRAY: LogArray = LogArray {
buffer: [LogEntryInner {
timestamp: 0,
@@ -26,8 +27,45 @@ static mut LOG_ARRAY: LogArray = LogArray {
}; LOG_ARRAY_SIZE as usize],
head: 0,
};
// this is the only reference created for LOG_ARRAY and the only way to access it
#[allow(static_mut_refs)]
pub static LOG_ACCESS: Mutex<CriticalSectionRawMutex, &'static mut LogArray> =
unsafe { Mutex::new(&mut *&raw mut LOG_ARRAY) };
unsafe { Mutex::new(&mut LOG_ARRAY) };
mod interceptor;
pub use interceptor::InterceptorLogger;
pub static INTERCEPTOR: InterceptorLogger = InterceptorLogger::new();
pub struct LogRequest {
pub message_key: LogMessage,
pub number_a: u32,
pub number_b: u32,
pub txt_short: heapless::String<TXT_SHORT_LENGTH>,
pub txt_long: heapless::String<TXT_LONG_LENGTH>,
}
static LOG_CHANNEL: Channel<CriticalSectionRawMutex, LogRequest, 16> = Channel::new();
#[embassy_executor::task]
pub async fn log_task() {
loop {
let request = LOG_CHANNEL.receive().await;
LOG_ACCESS
.lock()
.await
.log(
request.message_key,
request.number_a,
request.number_b,
request.txt_short.as_str(),
request.txt_long.as_str(),
)
.await;
}
}
const TXT_SHORT_LENGTH: usize = 8;
const TXT_LONG_LENGTH: usize = 32;
@@ -77,10 +115,31 @@ impl From<LogEntryInner> for LogEntry {
}
}
pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) {
let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
limit_length(txt_short, &mut txt_short_stack);
limit_length(txt_long, &mut txt_long_stack);
match LOG_CHANNEL.try_send(LogRequest {
message_key,
number_a,
number_b,
txt_short: txt_short_stack,
txt_long: txt_long_stack,
}) {
Ok(_) => {}
Err(_) => {
warn!("Log channel full, dropping log entry");
}
}
}
impl LogArray {
pub fn get(&mut self) -> Vec<LogEntry> {
let head: RangedU8<0, MAX_LOG_ARRAY_INDEX> =
RangedU8::new(self.head).unwrap_or(RangedU8::new(0).unwrap());
RangedU8::new(self.head).unwrap_or(RangedU8::new_saturating(0));
let mut rv: Vec<LogEntry> = Vec::new();
let mut index = head.wrapping_sub(1);
@@ -103,17 +162,11 @@ impl LogArray {
txt_long: &str,
) {
let mut head: RangedU8<0, MAX_LOG_ARRAY_INDEX> =
RangedU8::new(self.head).unwrap_or(RangedU8::new(0).unwrap());
let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
limit_length(txt_short, &mut txt_short_stack);
limit_length(txt_long, &mut txt_long_stack);
RangedU8::new(self.head).unwrap_or(RangedU8::new_saturating(0));
let time = {
let guard = TIME_ACCESS.get().await.lock().await;
guard.current_time_us()
let mut guard = BOARD_ACCESS.get().await.lock().await;
guard.board_hal.get_esp().rtc.current_time_us()
} / 1000;
let ordinal = message_key.ordinal() as u16;
@@ -124,19 +177,15 @@ impl LogArray {
template_string = template_string.replace("${txt_long}", txt_long);
template_string = template_string.replace("${txt_short}", txt_short);
info!("{}", template_string);
info!("{template_string}");
let to_modify = &mut self.buffer[head.get() as usize];
to_modify.timestamp = time;
to_modify.message_id = ordinal;
to_modify.a = number_a;
to_modify.b = number_b;
to_modify
.txt_short
.clone_from_slice(&txt_short_stack.as_bytes());
to_modify
.txt_long
.clone_from_slice(&txt_long_stack.as_bytes());
to_modify.txt_short.clone_from_slice(txt_short.as_bytes());
to_modify.txt_long.clone_from_slice(txt_long.as_bytes());
head = head.wrapping_add(1);
self.head = head.get();
}
@@ -148,18 +197,37 @@ fn limit_length<const LIMIT: usize>(input: &str, target: &mut heapless::String<L
Ok(_) => {} //continue adding chars
Err(_) => {
//clear space for two asci chars
info!("pushing char {char} to limit {LIMIT} current value {target} input {input}");
while target.len() + 2 >= LIMIT {
target.pop().unwrap();
target.pop();
}
//add .. to shortened strings
target.push('.').unwrap();
target.push('.').unwrap();
return;
match target.push('.') {
Ok(_) => {}
Err(_) => {
warn!(
"Error pushin . to limit {LIMIT} current value {target} input {input}"
)
}
}
match target.push('.') {
Ok(_) => {}
Err(_) => {
warn!(
"Error pushin . to limit {LIMIT} current value {target} input {input}"
)
}
}
}
}
}
while target.len() < LIMIT {
target.push(' ').unwrap();
match target.push(' ') {
Ok(_) => {}
Err(_) => {
warn!("Error pushing space to limit {LIMIT} current value {target} input {input}")
}
}
}
}
@@ -243,6 +311,20 @@ pub enum LogMessage {
PumpOpenLoopCurrent,
#[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")]
PumpMissingSensorCurrent,
#[strum(
serialize = "Fertilizer applied for ${number_a}s on plant ${number_b} (last application ${txt_short} minutes ago)"
)]
FertilizerApplied,
#[strum(serialize = "MPPT Current sensor could not be reached")]
MPPTError,
#[strum(
serialize = "Trace: a: ${number_a} b: ${number_b} txt_s ${txt_short} long ${txt_long}"
)]
Trace,
#[strum(serialize = "Parsing error reading message")]
UnknownMessage,
#[strum(serialize = "Going to deep sleep for ${number_a} minutes")]
DeepSleep,
}
#[derive(Serialize)]
@@ -261,9 +343,9 @@ impl From<&LogMessage> for MessageTranslation {
}
impl LogMessage {
pub fn to_log_localisation_config() -> Vec<MessageTranslation> {
pub fn log_localisation_config() -> Vec<MessageTranslation> {
Vec::from_iter((0..LogMessage::len()).map(|i| {
let msg_type = LogMessage::from_ordinal(i).unwrap();
let msg_type = LogMessage::from_ordinal(i).unwrap_or(LogMessage::UnknownMessage);
(&msg_type).into()
}))
}