From 4debbfb39e845b9c3cc130d95bdc0e9d9877f0f9 Mon Sep 17 00:00:00 2001 From: ju6ge Date: Sun, 10 May 2026 17:35:30 +0200 Subject: [PATCH] feat: implement config update via MQTT trigger --- rust/src/mqtt.rs | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/rust/src/mqtt.rs b/rust/src/mqtt.rs index d29f68a..41d410e 100644 --- a/rust/src/mqtt.rs +++ b/rust/src/mqtt.rs @@ -4,7 +4,7 @@ use crate::fat_error::{ContextExt, FatError, FatResult}; use crate::hal::PlantHal; use crate::log::{log, LogMessage}; use alloc::string::String; -use alloc::{format, string::ToString}; +use alloc::{format, string::ToString, vec::Vec}; use core::sync::atomic::Ordering; use embassy_executor::Spawner; use embassy_net::Stack; @@ -145,6 +145,7 @@ pub async fn mqtt_init( let round_trip_topic = format!("{base_topic}/internal/roundtrip"); let stay_alive_topic = format!("{base_topic}/stay_alive"); let config_update_payload_topic = format!("{base_topic}/config/update_payload"); + let config_update_topic = format!("{base_topic}/config/update"); let mut builder: McutieBuilder<'_, String, PublishDisplay, 0> = McutieBuilder::new(stack, "plant ctrl", mqtt_url); @@ -163,11 +164,12 @@ pub async fn mqtt_init( //TODO make configurable builder = builder.with_device_id("plantctrl"); - let builder: McutieBuilder<'_, String, PublishDisplay, 3> = builder + let builder: McutieBuilder<'_, String, PublishDisplay, 4> = builder .with_subscriptions([ Topic::General(round_trip_topic.clone()), Topic::General(stay_alive_topic.clone()), Topic::General(config_update_payload_topic.clone()), + Topic::General(config_update_topic.clone()), ]); let keep_alive = Duration::from_secs(60 * 60 * 2).as_secs() as u16; @@ -178,6 +180,7 @@ pub async fn mqtt_init( round_trip_topic.clone(), stay_alive_topic.clone(), config_update_payload_topic.clone(), + config_update_topic.clone(), )?); spawner.spawn(mqtt_runner(task)?); @@ -224,7 +227,7 @@ pub async fn mqtt_init( #[embassy_executor::task] async fn mqtt_runner( - task: McutieTask<'static, String, PublishDisplay<'static, String, &'static str>, 3>, + task: McutieTask<'static, String, PublishDisplay<'static, String, &'static str>, 4>, ) { task.run().await; } @@ -235,6 +238,7 @@ async fn mqtt_incoming_task( round_trip_topic: String, stay_alive_topic: String, config_update_payload_topic: String, + config_update_topic: String, ) { loop { let message = receiver.receive().await; @@ -265,6 +269,35 @@ async fn mqtt_incoming_task( let mut buffer = MQTT_CONFIG_UPDATE_PAYLOAD.lock().await; *buffer = Some(payload_str); info!("MQTT config update payload received"); + } else if subtopic.eq(config_update_topic.as_str()) { + let update_requested = payload.eq_ignore_ascii_case("true".as_ref()) + || payload.eq_ignore_ascii_case("1".as_ref()); + if update_requested { + info!("MQTT config update requested"); + let payload_lock = MQTT_CONFIG_UPDATE_PAYLOAD.lock().await; + if let Some(payload_str) = payload_lock.as_ref() { + match serde_json::from_str::(payload_str) { + Ok(config) => { + info!("Deserialized config, applying..."); + let board_mutex = crate::BOARD_ACCESS.get().await; + let mut board = board_mutex.lock().await; + if let Err(e) = board.board_hal.get_esp().save_config(payload_str.as_bytes().to_vec()).await { + info!("Error saving config to flash: {}", e); + } else { + board.board_hal.set_config(config); + info!("Config applied, rebooting"); + board.board_hal.get_esp().deep_sleep_ms(0); + } + + } + Err(e) => { + info!("Error deserializing config: {}", e); + } + } + } else { + info!("No config update payload available"); + } + } } else { log(LogMessage::UnknownTopic, 0, 0, "", &topic); }