Refactor HAL modules: update async support in Water module and reorganize detect_sensors logic

- Replaced `Blocking` with `Async` for ADC operations in `Water` module.
- Improved `detect_sensors` implementation with better structure for sensor messages and autodetection.
- Updated tank ADC sampling to yield between readings, improving efficiency.
This commit is contained in:
2026-04-26 21:01:27 +02:00
parent f1c85d1d74
commit eb276cfa68
2 changed files with 146 additions and 150 deletions

View File

@@ -396,6 +396,145 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
res
}
async fn general_fault(&mut self, enable: bool) {
hold_disable(23);
self.general_fault.set_level(enable.into());
hold_enable(23);
}
async fn test(&mut self) -> Result<(), FatError> {
self.general_fault(true).await;
Timer::after_millis(100).await;
self.general_fault(false).await;
Timer::after_millis(500).await;
self.extra1.set_high();
Timer::after_millis(500).await;
self.extra1.set_low();
Timer::after_millis(500).await;
self.extra2.set_high();
Timer::after_millis(500).await;
self.extra2.set_low();
Timer::after_millis(500).await;
self.light(true).await?;
Timer::after_millis(500).await;
self.light(false).await?;
Timer::after_millis(500).await;
for i in 0..PLANT_COUNT {
self.fault(i, true).await?;
Timer::after_millis(500).await;
self.fault(i, false).await?;
Timer::after_millis(500).await;
}
for i in 0..PLANT_COUNT {
self.pump(i, true).await?;
Timer::after_millis(100).await;
self.pump(i, false).await?;
Timer::after_millis(100).await;
}
let moisture = self.measure_moisture_hz().await?;
for plant in 0..PLANT_COUNT {
let a = moisture.sensor_a_hz[plant].unwrap_or(0.0) as u32;
let b = moisture.sensor_b_hz[plant].unwrap_or(0.0) as u32;
log(LogMessage::TestSensor, a, b, &(plant + 1).to_string(), "");
}
Timer::after_millis(10).await;
Ok(())
}
fn set_config(&mut self, config: PlantControllerConfig) {
self.config = config;
}
async fn get_mptt_voltage(&mut self) -> FatResult<Voltage> {
self.charger.get_mptt_voltage()
}
async fn get_mptt_current(&mut self) -> FatResult<Current> {
self.charger.get_mppt_current()
}
async fn can_power(&mut self, state: bool) -> FatResult<()> {
if state && self.can_power.is_set_low() {
self.can_power.set_high();
} else {
self.can_power.set_low();
}
Ok(())
}
async fn backup_config(&mut self, controller_config: &PlantControllerConfig) -> FatResult<()> {
let mut buffer: [u8; 4096 - BACKUP_HEADER_MAX_SIZE] = [0; 4096 - BACKUP_HEADER_MAX_SIZE];
let length = postcard::to_slice(controller_config, &mut buffer)?.len();
info!("Writing backup config of size {}", length);
let mut checksum = X25.digest();
checksum.update(&buffer[..length]);
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
let time = self.rtc_module.get_rtc_time().await?.timestamp_millis();
let header = BackupHeader {
crc16: checksum.finalize(),
timestamp: time,
size: length as u16,
};
info!("Header is {:?}", header);
postcard::to_slice(&header, &mut header_page_buffer)?;
info!("Header is serialized");
self.get_rtc_module().write(0, &header_page_buffer)?;
info!("Header written");
let mut to_write = length;
let mut chunk: usize = 0;
while to_write > 0 {
self.progress(chunk as u32).await;
let start = chunk * EEPROM_PAGE;
let end = start + min(EEPROM_PAGE, to_write);
let part = &buffer[start..end];
info!(
"Writing chunk {} of size {} to offset {}",
chunk,
part.len(),
start
);
to_write -= part.len();
self.get_rtc_module()
.write((BACKUP_HEADER_MAX_SIZE + chunk * EEPROM_PAGE) as u32, part)?;
chunk += 1;
}
info!("Backup complete");
self.clear_progress().await;
Ok(())
}
async fn read_backup(&mut self) -> FatResult<PlantControllerConfig> {
let info = self.backup_info().await?;
let mut store = alloc::vec![0_u8; info.size as usize];
self.rtc_module
.read(BACKUP_HEADER_MAX_SIZE as u32, store.as_mut_slice())?;
info!("Read backup data of size {}", store.len());
let mut checksum = X25.digest();
info!("Calculating CRC");
checksum.update(&store[..]);
let crc = checksum.finalize();
info!("CRC is {:04x}", crc);
if crc != info.crc16 {
warn!("CRC mismatch in backup data");
bail!("CRC mismatch in backup data")
}
info!("CRC is correct");
let decoded = postcard::from_bytes(&store[..])?;
info!("Backup data decoded");
Ok(decoded)
}
async fn backup_info(&mut self) -> FatResult<BackupHeader> {
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
self.get_rtc_module().read(0, &mut header_page_buffer)?;
info!("Read header page");
let info = postcard::take_from_bytes::<BackupHeader>(&header_page_buffer[..]);
info!("decoding header: {:?}", info);
let (header, _) = info.context("Could not read backup header")?;
Ok(header)
}
async fn detect_sensors(&mut self, request: Detection) -> FatResult<Detection> {
self.can_power.set_high();
Timer::after_millis(500).await;
@@ -473,145 +612,6 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
res
}
async fn general_fault(&mut self, enable: bool) {
hold_disable(23);
self.general_fault.set_level(enable.into());
hold_enable(23);
}
async fn test(&mut self) -> Result<(), FatError> {
self.general_fault(true).await;
Timer::after_millis(100).await;
self.general_fault(false).await;
Timer::after_millis(500).await;
self.extra1.set_high();
Timer::after_millis(500).await;
self.extra1.set_low();
Timer::after_millis(500).await;
self.extra2.set_high();
Timer::after_millis(500).await;
self.extra2.set_low();
Timer::after_millis(500).await;
self.light(true).await?;
Timer::after_millis(500).await;
self.light(false).await?;
Timer::after_millis(500).await;
for i in 0..PLANT_COUNT {
self.fault(i, true).await?;
Timer::after_millis(500).await;
self.fault(i, false).await?;
Timer::after_millis(500).await;
}
for i in 0..PLANT_COUNT {
self.pump(i, true).await?;
Timer::after_millis(100).await;
self.pump(i, false).await?;
Timer::after_millis(100).await;
}
let moisture = self.measure_moisture_hz().await?;
for plant in 0..PLANT_COUNT {
let a = moisture.sensor_a_hz[plant].unwrap_or(0.0) as u32;
let b = moisture.sensor_b_hz[plant].unwrap_or(0.0) as u32;
log(LogMessage::TestSensor, a, b, &(plant + 1).to_string(), "");
}
Timer::after_millis(10).await;
Ok(())
}
fn set_config(&mut self, config: PlantControllerConfig) {
self.config = config;
}
async fn get_mptt_voltage(&mut self) -> FatResult<Voltage> {
self.charger.get_mptt_voltage()
}
async fn get_mptt_current(&mut self) -> FatResult<Current> {
self.charger.get_mppt_current()
}
async fn can_power(&mut self, state: bool) -> FatResult<()> {
if state && self.can_power.is_set_low() {
self.can_power.set_high();
} else {
self.can_power.set_low();
}
Ok(())
}
async fn backup_config(&mut self, controller_config: &PlantControllerConfig) -> FatResult<()> {
let mut buffer: [u8; 4096 - BACKUP_HEADER_MAX_SIZE] = [0; 4096 - BACKUP_HEADER_MAX_SIZE];
let length = postcard::to_slice(controller_config, &mut buffer)?.len();
info!("Writing backup config of size {}", length);
let mut checksum = X25.digest();
checksum.update(&buffer[..length]);
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
let time = self.rtc_module.get_rtc_time().await?.timestamp_millis();
let header = BackupHeader {
crc16: checksum.finalize(),
timestamp: time,
size: length as u16,
};
info!("Header is {:?}", header);
postcard::to_slice(&header, &mut header_page_buffer)?;
info!("Header is serialized");
self.get_rtc_module().write(0, &header_page_buffer)?;
info!("Header written");
let mut to_write = length;
let mut chunk: usize = 0;
while to_write > 0 {
self.progress(chunk as u32).await;
let start = chunk * EEPROM_PAGE;
let end = start + min(EEPROM_PAGE, to_write);
let part = &buffer[start..end];
info!(
"Writing chunk {} of size {} to offset {}",
chunk,
part.len(),
start
);
to_write -= part.len();
self.get_rtc_module()
.write((BACKUP_HEADER_MAX_SIZE + chunk * EEPROM_PAGE) as u32, part)?;
chunk += 1;
}
info!("Backup complete");
self.clear_progress().await;
Ok(())
}
async fn read_backup(&mut self) -> FatResult<PlantControllerConfig> {
let info = self.backup_info().await?;
let mut store = alloc::vec![0_u8; info.size as usize];
self.rtc_module
.read(BACKUP_HEADER_MAX_SIZE as u32, store.as_mut_slice())?;
info!("Read backup data of size {}", store.len());
let mut checksum = X25.digest();
info!("Calculating CRC");
checksum.update(&store[..]);
let crc = checksum.finalize();
info!("CRC is {:04x}", crc);
if crc != info.crc16 {
warn!("CRC mismatch in backup data");
bail!("CRC mismatch in backup data")
}
info!("CRC is correct");
let decoded = postcard::from_bytes(&store[..])?;
info!("Backup data decoded");
Ok(decoded)
}
async fn backup_info(&mut self) -> FatResult<BackupHeader> {
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
self.get_rtc_module().read(0, &mut header_page_buffer)?;
info!("Read header page");
let info = postcard::take_from_bytes::<BackupHeader>(&header_page_buffer[..]);
info!("decoding header: {:?}", info);
let (header, _) = info.context("Could not read backup header")?;
Ok(header)
}
}
async fn wait_for_can_measurements(

View File

@@ -9,13 +9,15 @@ use esp_hal::pcnt::channel::CtrlMode::Keep;
use esp_hal::pcnt::channel::EdgeMode::{Hold, Increment};
use esp_hal::pcnt::unit::Unit;
use esp_hal::peripherals::GPIO5;
use esp_hal::Blocking;
use esp_hal::Async;
use esp_println::println;
use onewire::{ds18b20, Device, DeviceSearch, OneWire, DS18B20};
unsafe impl Send for TankSensor<'_> {}
pub struct TankSensor<'a> {
one_wire_bus: OneWire<Flex<'a>>,
tank_channel: Adc<'a, ADC1<'a>, Blocking>,
tank_channel: Adc<'a, ADC1<'a>, Async>,
tank_power: Output<'a>,
tank_pin: AdcPin<GPIO5<'a>, ADC1<'a>, AdcCalLine<ADC1<'a>>>,
flow_counter: Unit<'a, 1>,
@@ -35,7 +37,7 @@ impl<'a> TankSensor<'a> {
let mut adc1_config = AdcConfig::new();
let tank_pin =
adc1_config.enable_pin_with_cal::<_, AdcCalLine<_>>(gpio5, Attenuation::_11dB);
let tank_channel = Adc::new(adc1, adc1_config);
let tank_channel = Adc::new(adc1, adc1_config).into_async();
let one_wire_bus = OneWire::new(one_wire_pin, false);
@@ -139,15 +141,9 @@ impl<'a> TankSensor<'a> {
let mut store = [0_u16; TANK_MULTI_SAMPLE];
for sample in store.iter_mut() {
let value = self.tank_channel.read_oneshot(&mut self.tank_pin);
//force yield
*sample = self.tank_channel.read_oneshot(&mut self.tank_pin).await;
//force yield between successful samples
Timer::after_millis(10).await;
match value {
Ok(v) => *sample = v,
Err(e) => {
bail!("ADC Hardware error: {:?}", e);
}
};
}
self.tank_power.set_low();