From c631c6114aeeb6d64c796ad8a8007d7899eacb81 Mon Sep 17 00:00:00 2001
From: DKolter <danielkolter157@gmail.com>
Date: Thu, 14 Nov 2024 19:50:42 +0100
Subject: [PATCH 1/4] Core adoption start, configuration changes, deploy script

---
 .cargo/config.toml |  6 ++++++
 core/.gitignore    |  1 -
 core/config.json   | 18 ++++++++++++++++++
 deploy.sh          | 44 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 .cargo/config.toml
 delete mode 100644 core/.gitignore
 create mode 100644 core/config.json
 create mode 100755 deploy.sh

diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 0000000..d0e0404
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,6 @@
+[build]
+target = "armv7-unknown-linux-musleabihf"
+
+[target.armv7-unknown-linux-musleabihf]
+linker = "/opt/armv7l-linux-musleabihf-cross/bin/armv7l-linux-musleabihf-gcc"
+
diff --git a/core/.gitignore b/core/.gitignore
deleted file mode 100644
index d344ba6..0000000
--- a/core/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-config.json
diff --git a/core/config.json b/core/config.json
new file mode 100644
index 0000000..f00516e
--- /dev/null
+++ b/core/config.json
@@ -0,0 +1,18 @@
+{
+	"mqtt": {
+		"host": "test.mosquitto.org",
+		"port": 1883,
+		"username": "",
+		"password": ""
+	},
+	"drivers": [
+		{
+			"protocol": "vebus",
+			"index": 0,
+			"args": {
+				"port": "/dev/ttySTM5",
+				"sleep": 1
+			}
+		}
+	]
+}
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 0000000..f847793
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+usage() {
+	echo "Usage: $0 [options]"
+	echo "Options:"
+	echo "  -d DOMAIN       SSH domain"
+	echo "  -p PORT         SSH port"
+	echo "  -t TARGET_PATH  Target path on the server"
+	echo "  --release       Build the project in release mode"
+	echo "  -h, --help      Display this help and exit"
+	echo ""
+	echo "Example: $0 -d user@mydomain.net -p 22 -t /home/user/core --release"
+	exit 1
+}
+
+RELEASE_MODE=false
+
+while [[ "$#" -gt 0 ]]; do
+  case "$1" in
+    -d) DOMAIN="$2"; shift 2;;
+    -p) PORT="$2"; shift 2;;
+    -t) TARGET_PATH="$2"; shift 2;;
+    --release) RELEASE_MODE=true; shift;;
+    -h|--help) usage;;
+    *) echo "Unknown option: $1"; usage;;
+  esac
+done
+
+if [[ -z "$DOMAIN" || -z "$PORT" || -z "$TARGET_PATH" ]]; then
+  echo "Error: Missing required arguments."
+  usage
+fi
+
+echo "Building the Rust project"
+if $RELEASE_MODE; then
+  cargo build --release
+  BUILD_PATH="target/armv7-unknown-linux-musleabihf/release/core"
+else
+  cargo build
+  BUILD_PATH="target/armv7-unknown-linux-musleabihf/debug/core"
+fi
+
+echo "Copying the binary to the server"
+scp -P "$PORT" "$BUILD_PATH" "$DOMAIN:$TARGET_PATH"
-- 
GitLab


From 44d6423deca984be5a9ef389418615c12225461a Mon Sep 17 00:00:00 2001
From: DKolter <danielkolter157@gmail.com>
Date: Mon, 25 Nov 2024 18:10:29 +0100
Subject: [PATCH 2/4] Load configuration at runtime, add copy configuration
 option for deploy script

---
 core/src/config.rs | 5 +++--
 core/src/main.rs   | 2 --
 deploy.sh          | 9 +++++++--
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/core/src/config.rs b/core/src/config.rs
index 3732134..9047540 100644
--- a/core/src/config.rs
+++ b/core/src/config.rs
@@ -39,8 +39,9 @@ pub enum DriverProtocol {
 impl Config {
     pub fn load() -> Result<Self> {
         log::info!("Loading configuration file");
-        let config = include_str!("../config.json");
-        let config: Self = serde_json::from_str(config).context("Parsing configuration file")?;
+        let config =
+            std::fs::read_to_string("config.json").context("Reading configuration file")?;
+        let config: Self = serde_json::from_str(&config).context("Parsing configuration file")?;
         log::info!("Configured drivers: {}", config.drivers.len());
         log::info!("MQTT broker host: {}", config.mqtt.host);
         log::info!("MQTT broker port: {}", config.mqtt.port);
diff --git a/core/src/main.rs b/core/src/main.rs
index 32fa1d9..771122b 100644
--- a/core/src/main.rs
+++ b/core/src/main.rs
@@ -1,5 +1,3 @@
-#![no_std]
-
 use adc::AdcDriver;
 use config::{Config, DriverConfig, DriverProtocol};
 use gpio_output::GpioOutputDriver;
diff --git a/deploy.sh b/deploy.sh
index f847793..cef110d 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -6,6 +6,7 @@ usage() {
 	echo "  -d DOMAIN       SSH domain"
 	echo "  -p PORT         SSH port"
 	echo "  -t TARGET_PATH  Target path on the server"
+	echo "  --cc			Copy the configuration file to the server"
 	echo "  --release       Build the project in release mode"
 	echo "  -h, --help      Display this help and exit"
 	echo ""
@@ -14,12 +15,14 @@ usage() {
 }
 
 RELEASE_MODE=false
+COPY_CONFIGURATION=false
 
 while [[ "$#" -gt 0 ]]; do
   case "$1" in
     -d) DOMAIN="$2"; shift 2;;
     -p) PORT="$2"; shift 2;;
     -t) TARGET_PATH="$2"; shift 2;;
+	--cc) COPY_CONFIGURATION=true; shift;;
     --release) RELEASE_MODE=true; shift;;
     -h|--help) usage;;
     *) echo "Unknown option: $1"; usage;;
@@ -40,5 +43,7 @@ else
   BUILD_PATH="target/armv7-unknown-linux-musleabihf/debug/core"
 fi
 
-echo "Copying the binary to the server"
-scp -P "$PORT" "$BUILD_PATH" "$DOMAIN:$TARGET_PATH"
+if $COPY_CONFIGURATION; then
+  echo "Copying the configuration file to the server"
+  scp -P "$PORT" "core/config.json" "$DOMAIN:$TARGET_PATH"
+fi
-- 
GitLab


From 55197fccc9f71dcd881889ca574614f0c0fbdfb3 Mon Sep 17 00:00:00 2001
From: DKolter <danielkolter157@gmail.com>
Date: Tue, 10 Dec 2024 11:31:43 +0100
Subject: [PATCH 3/4] Review changes, ADC driver untested, due to missing files
 on core

---
 .cargo/config.toml       |   6 --
 Cargo.lock               |  13 ----
 core/.gitignore          |   1 +
 core/Cargo.toml          |   2 -
 core/config.json         |  18 -----
 core/src/adc.rs          | 136 ++++++++------------------------
 core/src/config.rs       |  10 +--
 core/src/gpio_output.rs  |  20 +++--
 core/src/main.rs         |  25 +++---
 core/src/mqtt.rs         |  35 +++------
 core/src/pwm.rs          |  16 ++--
 core/src/temperature.rs  |  11 +--
 core/src/topics.rs       | 164 ++++++++++++++++++---------------------
 core/src/utils.rs        |  16 +---
 core/src/vebus.rs        |  45 +++++------
 core/src/vedirect.rs     |  51 ++++++------
 deploy.sh                |  14 +++-
 drivers/lin/src/truma.rs |   4 +-
 18 files changed, 220 insertions(+), 367 deletions(-)
 delete mode 100644 .cargo/config.toml
 create mode 100644 core/.gitignore
 delete mode 100644 core/config.json

diff --git a/.cargo/config.toml b/.cargo/config.toml
deleted file mode 100644
index d0e0404..0000000
--- a/.cargo/config.toml
+++ /dev/null
@@ -1,6 +0,0 @@
-[build]
-target = "armv7-unknown-linux-musleabihf"
-
-[target.armv7-unknown-linux-musleabihf]
-linker = "/opt/armv7l-linux-musleabihf-cross/bin/armv7l-linux-musleabihf-gcc"
-
diff --git a/Cargo.lock b/Cargo.lock
index 96023fe..ca78ab5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,16 +17,6 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
 
-[[package]]
-name = "ads1x1x"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b44310e48193933db0cd788961f450a86f35fdad022e4373883fa8e4640488e"
-dependencies = [
- "embedded-hal 0.2.7",
- "nb 1.1.0",
-]
-
 [[package]]
 name = "aho-corasick"
 version = "1.1.3"
@@ -189,11 +179,9 @@ checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
 name = "core"
 version = "0.1.0"
 dependencies = [
- "ads1x1x",
  "anyhow",
  "embedded-io-adapters",
  "env_logger 0.11.5",
- "heapless",
  "itertools",
  "log",
  "rppal",
@@ -449,7 +437,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
 dependencies = [
  "hash32",
- "serde",
  "stable_deref_trait",
 ]
 
diff --git a/core/.gitignore b/core/.gitignore
new file mode 100644
index 0000000..d344ba6
--- /dev/null
+++ b/core/.gitignore
@@ -0,0 +1 @@
+config.json
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 05c0bab..b65c075 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -5,11 +5,9 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-ads1x1x = "0.2"
 anyhow = "1.0"
 embedded-io-adapters = { version = "0.6", features = ["tokio-1"] }
 env_logger = "0.11"
-heapless = { version = "0.8", features = ["serde"] }
 log = "0.4"
 itertools = "0.13"
 serde = { version = "1.0", features = ["derive"] }
diff --git a/core/config.json b/core/config.json
deleted file mode 100644
index f00516e..0000000
--- a/core/config.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-	"mqtt": {
-		"host": "test.mosquitto.org",
-		"port": 1883,
-		"username": "",
-		"password": ""
-	},
-	"drivers": [
-		{
-			"protocol": "vebus",
-			"index": 0,
-			"args": {
-				"port": "/dev/ttySTM5",
-				"sleep": 1
-			}
-		}
-	]
-}
diff --git a/core/src/adc.rs b/core/src/adc.rs
index 1abfcaa..8c5295d 100644
--- a/core/src/adc.rs
+++ b/core/src/adc.rs
@@ -1,94 +1,67 @@
 use crate::{
     mqtt::{MqttDriver, PublishMessage},
-    topics::LevelTopics,
-    utils::to_mqtt_payload,
-    MAX_MQTT_SUBSCRIBED_TOPICS, MAX_MQTT_TOPIC_SIZE,
-};
-use ads1x1x::{
-    ic::{Ads1115, Resolution16Bit},
-    interface::I2cInterface,
-    mode::OneShot,
-    Ads1x1x, ChannelSelection, DynamicOneShot, FullScaleRange, SlaveAddr,
+    topics::SensorTopics,
 };
+use anyhow::{Context, Result};
 use core::time::Duration;
-use heapless::{String, Vec};
-use itertools::izip;
-use rppal::i2c::I2c;
 use rumqttc::v5::mqttbytes::QoS;
 use serde::Deserialize;
 use serde_json::Value;
 
-const DELAY: u64 = 20;
-
 pub struct AdcDriver {
-    adc: Ads1x1x<I2cInterface<I2c>, Ads1115, Resolution16Bit, OneShot>,
     args: AdcDriverArgs,
-    topics: [LevelTopics; 4],
+    topics: [SensorTopics; 8],
 }
 
 #[derive(Deserialize)]
 struct AdcDriverArgs {
-    bus: u8,
-    fsr: Fsr,
-    bounds: Vec<VoltageBounds, 4>,
+    path: String,
     sleep: u64,
 }
 
 impl AdcDriver {
-    pub async fn new(_index: usize, args: Value) -> Self {
+    pub async fn new(_index: usize, args: Value) -> Result<Self> {
         let args: AdcDriverArgs =
-            serde_json::from_value(args).expect("Failed to parse adc driver args");
-        let i2c = I2c::with_bus(args.bus).unwrap();
-        let addr = SlaveAddr::default();
-        let mut adc = Ads1x1x::new_ads1115(i2c, addr);
-        adc.set_full_scale_range(args.fsr.into())
-            .expect("Failed to set adc driver full scale range");
+            serde_json::from_value(args).context("Parsing adc driver args")?;
 
         let topics = [
-            LevelTopics::new(0),
-            LevelTopics::new(1),
-            LevelTopics::new(2),
-            LevelTopics::new(3),
+            SensorTopics::new("adc", "0"),
+            SensorTopics::new("adc", "1"),
+            SensorTopics::new("adc", "2"),
+            SensorTopics::new("adc", "3"),
+            SensorTopics::new("adc", "4"),
+            SensorTopics::new("adc", "5"),
+            SensorTopics::new("adc", "6"),
+            SensorTopics::new("adc", "7"),
         ];
 
-        Self { adc, args, topics }
+        Ok(Self { args, topics })
     }
 
-    pub fn subscriptions(&self) -> Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS> {
+    pub fn subscriptions(&self) -> Vec<String> {
         Vec::new()
     }
 
     pub async fn run(&mut self, mqtt: MqttDriver) {
-        loop {
-            let channels = [
-                ChannelSelection::SingleA0,
-                ChannelSelection::SingleA1,
-                ChannelSelection::SingleA2,
-                ChannelSelection::SingleA3,
-            ];
+        // Publish the unit of measurement (V) for all topics
+        for topic in self.topics.iter() {
+            mqtt.publish(PublishMessage {
+                topic: topic.unit_of_measurement.clone(),
+                payload: b"V".to_vec(),
+                qos: QoS::AtLeastOnce,
+                retain: true,
+            })
+            .await;
+        }
 
-            for (i, channel, bounds, topic) in izip!(
-                0..4,
-                channels.iter(),
-                self.args.bounds.iter(),
-                self.topics.iter()
-            ) {
-                let reading = loop {
-                    match self.adc.read(*channel) {
-                        Err(_) => tokio::time::sleep(Duration::from_millis(DELAY)).await,
-                        Ok(reading) => break reading,
-                    }
-                };
+        loop {
+            for (i, topic) in self.topics.iter().enumerate() {
+                let value = 0.0; // Read the value from the ADC
 
-                log::debug!("ADC reading on channel {i}: {reading}");
-                let voltage = self.args.fsr.convert_adc_reading(reading);
-                log::debug!("Voltage on channel {i}: {voltage}");
-                let percentage =
-                    ((voltage - bounds.min) / (bounds.max - bounds.min)).clamp(0.0, 1.0);
-                log::debug!("Percentage on channel {i}: {percentage}");
+                // Publish the value
                 mqtt.publish(PublishMessage {
-                    topic: topic.level.clone(),
-                    payload: to_mqtt_payload(percentage),
+                    topic: topic.state_t.clone(),
+                    payload: value.to_string().into_bytes(),
                     qos: QoS::AtLeastOnce,
                     retain: true,
                 })
@@ -99,48 +72,3 @@ impl AdcDriver {
         }
     }
 }
-
-#[derive(Deserialize)]
-pub struct VoltageBounds {
-    min: f32,
-    max: f32,
-}
-
-#[derive(Deserialize, Clone, Copy)]
-#[serde(rename_all = "snake_case")]
-pub enum Fsr {
-    V6_144,
-    V4_096,
-    V2_048,
-    V1_024,
-    V0_512,
-    V0_256,
-}
-
-impl From<Fsr> for FullScaleRange {
-    fn from(range: Fsr) -> Self {
-        match range {
-            Fsr::V6_144 => FullScaleRange::Within6_144V,
-            Fsr::V4_096 => FullScaleRange::Within4_096V,
-            Fsr::V2_048 => FullScaleRange::Within2_048V,
-            Fsr::V1_024 => FullScaleRange::Within1_024V,
-            Fsr::V0_512 => FullScaleRange::Within0_512V,
-            Fsr::V0_256 => FullScaleRange::Within0_256V,
-        }
-    }
-}
-
-impl Fsr {
-    fn convert_adc_reading(&self, reading: i16) -> f32 {
-        let max = match self {
-            Fsr::V6_144 => 6.144,
-            Fsr::V4_096 => 4.096,
-            Fsr::V2_048 => 2.048,
-            Fsr::V1_024 => 1.024,
-            Fsr::V0_512 => 0.512,
-            Fsr::V0_256 => 0.256,
-        };
-        let max_reading = i16::MAX as f32;
-        reading as f32 * max / max_reading
-    }
-}
diff --git a/core/src/config.rs b/core/src/config.rs
index 9047540..06604da 100644
--- a/core/src/config.rs
+++ b/core/src/config.rs
@@ -1,21 +1,19 @@
-use crate::{MAX_DRIVERS, MAX_MQTT_MESSAGE_SIZE};
 use anyhow::{Context, Result};
-use heapless::{String, Vec};
 use serde::Deserialize;
 use serde_json::Value;
 
 #[derive(Deserialize, Debug)]
 pub struct Config {
     pub mqtt: MqttConfig,
-    pub drivers: Vec<DriverConfig, MAX_DRIVERS>,
+    pub drivers: Vec<DriverConfig>,
 }
 
 #[derive(Deserialize, Debug)]
 pub struct MqttConfig {
-    pub host: String<MAX_MQTT_MESSAGE_SIZE>,
+    pub host: String,
     pub port: u16,
-    pub username: Option<String<MAX_MQTT_MESSAGE_SIZE>>,
-    pub password: Option<String<MAX_MQTT_MESSAGE_SIZE>>,
+    pub username: Option<String>,
+    pub password: Option<String>,
 }
 
 #[derive(Deserialize, Debug)]
diff --git a/core/src/gpio_output.rs b/core/src/gpio_output.rs
index 2d881cd..c932ca1 100644
--- a/core/src/gpio_output.rs
+++ b/core/src/gpio_output.rs
@@ -1,11 +1,9 @@
 use crate::{
     mqtt::MqttDriver,
     topics::ToggleTopics,
-    utils::{parse_payload_to_bool, parse_string_mqtt_boolean, to_mqtt_payload},
-    MAX_MQTT_MESSAGE_SIZE, MAX_MQTT_SUBSCRIBED_TOPICS, MAX_MQTT_TOPIC_SIZE,
+    utils::{parse_payload_to_bool, parse_string_mqtt_boolean},
 };
 use core::{str::FromStr, time::Duration};
-use heapless::{String, Vec};
 use rppal::gpio::{Gpio, OutputPin};
 use rumqttc::v5::mqttbytes::QoS;
 use serde::Deserialize;
@@ -50,15 +48,15 @@ impl GpioOutputDriver {
         }
     }
 
-    pub fn subscriptions(&self) -> Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS> {
-        Vec::from_slice(&[self.topics.set_value.clone(), self.topics.set_timed.clone()]).unwrap()
+    pub fn subscriptions(&self) -> Vec<String> {
+        vec![self.topics.set_value.clone(), self.topics.set_timed.clone()]
     }
 
     pub async fn run(&mut self, mut mqtt: MqttDriver) {
         // Publish the initial value of the gpio pin to the topic
         mqtt.publish(crate::mqtt::PublishMessage {
             topic: self.topics.value.clone(),
-            payload: to_mqtt_payload(self.current),
+            payload: self.current.to_string().into_bytes(),
             qos: QoS::AtLeastOnce,
             retain: true,
         })
@@ -81,7 +79,7 @@ impl GpioOutputDriver {
         }
     }
 
-    async fn set_value(&mut self, mqtt: &MqttDriver, payload: Vec<u8, MAX_MQTT_MESSAGE_SIZE>) {
+    async fn set_value(&mut self, mqtt: &MqttDriver, payload: Vec<u8>) {
         // Parse the payload to a boolean value
         let value = parse_payload_to_bool(payload);
 
@@ -101,7 +99,7 @@ impl GpioOutputDriver {
         // Publish the new value to the topic
         mqtt.publish(crate::mqtt::PublishMessage {
             topic: self.topics.value.clone(),
-            payload: to_mqtt_payload(self.current),
+            payload: self.current.to_string().into_bytes(),
             qos: QoS::AtLeastOnce,
             retain: true,
         })
@@ -128,7 +126,7 @@ impl GpioOutputDriver {
         };
 
         // Parse the value as a boolean
-        let value = String::<MAX_MQTT_MESSAGE_SIZE>::from_str(value).unwrap();
+        let value = String::from_str(value).unwrap();
         let value = parse_string_mqtt_boolean(value);
 
         // Ignore the command if the value is the same as the current value
@@ -158,7 +156,7 @@ impl GpioOutputDriver {
         // Publish the new value to the topic
         mqtt.publish(crate::mqtt::PublishMessage {
             topic: self.topics.value.clone(),
-            payload: to_mqtt_payload(value),
+            payload: value.to_string().into_bytes(),
             qos: QoS::AtLeastOnce,
             retain: true,
         })
@@ -181,7 +179,7 @@ impl GpioOutputDriver {
         // Publish the restored value to the topic
         mqtt.publish(crate::mqtt::PublishMessage {
             topic: self.topics.value.clone(),
-            payload: to_mqtt_payload(self.current),
+            payload: self.current.to_string().into_bytes(),
             qos: QoS::AtLeastOnce,
             retain: true,
         })
diff --git a/core/src/main.rs b/core/src/main.rs
index 771122b..dac0f88 100644
--- a/core/src/main.rs
+++ b/core/src/main.rs
@@ -1,7 +1,7 @@
 use adc::AdcDriver;
+use anyhow::Result;
 use config::{Config, DriverConfig, DriverProtocol};
 use gpio_output::GpioOutputDriver;
-use heapless::{String, Vec};
 use mqtt::MqttDriver;
 use pwm::PwmDriver;
 use serde_json::Value;
@@ -21,12 +21,8 @@ mod utils;
 mod vebus;
 mod vedirect;
 
-const MAX_MQTT_SUBSCRIBED_TOPICS: usize = 256;
 const MAX_MQTT_BROADCASTED_MESSAGES: usize = 128;
 const MAX_MQTT_PARALLEL_MESSAGES: usize = 64;
-const MAX_MQTT_MESSAGE_SIZE: usize = 1024;
-const MAX_MQTT_TOPIC_SIZE: usize = 64;
-const MAX_DRIVERS: usize = 16;
 
 /// The driver is an enumeration of all known drivers. Each driver
 /// has its own protocol and is responsible for reading data from
@@ -46,25 +42,25 @@ impl Driver {
     /// The index is determined by the order of the drivers;
     /// in the configuration file at the moment, but will be
     /// determined by a MQTT handshake with the core in the future.
-    async fn new(protocol: DriverProtocol, index: usize, args: Value) -> Self {
-        match protocol {
+    async fn new(protocol: DriverProtocol, index: usize, args: Value) -> Result<Self> {
+        Ok(match protocol {
             DriverProtocol::Vebus => Self::Vebus(VebusDriver::new(index, args).await),
             DriverProtocol::Vedirect => Self::Vedirect(VedirectDriver::new(index, args).await),
             DriverProtocol::OutputGpio => {
                 Self::OutputGpio(GpioOutputDriver::new(index, args).await)
             }
-            DriverProtocol::Adc => Self::Adc(AdcDriver::new(index, args).await),
+            DriverProtocol::Adc => Self::Adc(AdcDriver::new(index, args).await?),
             DriverProtocol::Temperature => {
                 Self::Temperature(TemperatureDriver::new(index, args).await)
             }
             DriverProtocol::Pwm => Self::Pwm(PwmDriver::new(index, args).await),
-        }
+        })
     }
 
     /// Returns a list of MQTT topics that the driver subscribes to.
     /// The index from the `Driver::new` is usually a part of the topic
     /// name to allow multiple instances of the same driver to run.
-    fn subscriptions(&self) -> Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS> {
+    fn subscriptions(&self) -> Vec<String> {
         match self {
             Self::Vebus(driver) => driver.subscriptions(),
             Self::Vedirect(driver) => driver.subscriptions(),
@@ -94,15 +90,18 @@ impl Driver {
 
 /// Create a list of drivers from the given configurations.
 /// When a new driver is added, it should be added to the match statement.
-async fn create_drivers(configs: Vec<DriverConfig, MAX_DRIVERS>) -> Vec<Driver, MAX_DRIVERS> {
-    let mut drivers: Vec<Driver, MAX_DRIVERS> = Vec::new();
+async fn create_drivers(configs: Vec<DriverConfig>) -> Vec<Driver> {
+    let mut drivers: Vec<Driver> = Vec::new();
     for config in configs {
         log::info!(
             "Creating driver with index {} and protocol: {:?}",
             config.index,
             config.protocol
         );
-        let _ = drivers.push(Driver::new(config.protocol, config.index, config.args).await);
+        match Driver::new(config.protocol, config.index, config.args).await {
+            Ok(driver) => drivers.push(driver),
+            Err(e) => log::error!("Failed to create driver: {e}"),
+        }
     }
 
     drivers
diff --git a/core/src/mqtt.rs b/core/src/mqtt.rs
index 908ae94..bdc073a 100644
--- a/core/src/mqtt.rs
+++ b/core/src/mqtt.rs
@@ -1,10 +1,7 @@
 extern crate alloc;
 use crate::{
-    config::MqttConfig, Driver, MAX_MQTT_BROADCASTED_MESSAGES, MAX_MQTT_MESSAGE_SIZE,
-    MAX_MQTT_PARALLEL_MESSAGES, MAX_MQTT_SUBSCRIBED_TOPICS, MAX_MQTT_TOPIC_SIZE,
+    config::MqttConfig, Driver, MAX_MQTT_BROADCASTED_MESSAGES, MAX_MQTT_PARALLEL_MESSAGES,
 };
-use core::str::FromStr;
-use heapless::{String, Vec};
 use rumqttc::v5::{
     mqttbytes::{
         v5::{Filter, Packet},
@@ -24,16 +21,16 @@ pub struct MqttDriver {
 }
 
 pub struct PublishMessage {
-    pub topic: String<MAX_MQTT_TOPIC_SIZE>,
-    pub payload: Vec<u8, MAX_MQTT_MESSAGE_SIZE>,
+    pub topic: String,
+    pub payload: Vec<u8>,
     pub qos: QoS,
     pub retain: bool,
 }
 
 #[derive(Debug, Clone)]
 pub struct SubscribedMessage {
-    pub topic: String<MAX_MQTT_TOPIC_SIZE>,
-    pub payload: Vec<u8, MAX_MQTT_MESSAGE_SIZE>,
+    pub topic: String,
+    pub payload: Vec<u8>,
 }
 
 impl MqttDriver {
@@ -47,7 +44,7 @@ impl MqttDriver {
         let topics = drivers
             .iter()
             .flat_map(|driver| driver.subscriptions())
-            .collect::<Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS>>();
+            .collect::<Vec<String>>();
 
         // Start the MQTT client
         tokio::spawn(Self::run(
@@ -82,7 +79,7 @@ impl MqttDriver {
 
     async fn run(
         config: MqttConfig,
-        topics: Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS>,
+        topics: Vec<String>,
         mut publish_receiver: MpscReceiver<PublishMessage>,
         subscribed_sender: BroadcastSender<SubscribedMessage>,
     ) {
@@ -99,7 +96,7 @@ impl MqttDriver {
         let topics = topics
             .iter()
             .map(|topic| Filter::new(topic.as_str(), QoS::AtLeastOnce))
-            .collect::<Vec<_, MAX_MQTT_SUBSCRIBED_TOPICS>>();
+            .collect::<Vec<_>>();
 
         log::info!("Subscribing to {} MQTT topics", topics.len());
         if !topics.is_empty() {
@@ -116,14 +113,8 @@ impl MqttDriver {
                 notification = connection.poll() => match notification {
                     Ok(Event::Incoming(Packet::Publish(p))) => {
                         // Extract the topic from the received MQTT message
-                        let topic = match core::str::from_utf8(&p.topic) {
-                            Ok(topic) => match String::<MAX_MQTT_TOPIC_SIZE>::from_str(topic) {
-                                Ok(topic) => topic,
-                                Err(_) => {
-                                    log::error!("Failed to parse MQTT topic: {:?}", p.topic);
-                                    continue;
-                                }
-                            }
+                        let topic = match String::from_utf8(p.topic.to_vec()) {
+                            Ok(topic) => topic,
                             Err(_) => {
                                 log::error!("Failed to parse MQTT topic: {:?}", p.topic);
                                 continue;
@@ -132,11 +123,7 @@ impl MqttDriver {
 
 
                         // Extract the payload from the received MQTT message
-                        let mut payload = Vec::<u8, MAX_MQTT_MESSAGE_SIZE>::new();
-                        if payload.extend_from_slice(&p.payload).is_err() {
-                            log::error!("MQTT message is too long: {}", p.payload.len());
-                            continue;
-                        }
+                        let payload = p.payload.to_vec();
 
                         let message = SubscribedMessage { topic, payload };
                         if let Err(e) = subscribed_sender.send(message){
diff --git a/core/src/pwm.rs b/core/src/pwm.rs
index 799fe78..a56ebf8 100644
--- a/core/src/pwm.rs
+++ b/core/src/pwm.rs
@@ -1,11 +1,5 @@
-use crate::{
-    mqtt::MqttDriver,
-    topics::DimmableLightTopics,
-    utils::{parse_payload_to_f64, to_mqtt_payload},
-    MAX_MQTT_SUBSCRIBED_TOPICS, MAX_MQTT_TOPIC_SIZE,
-};
+use crate::{mqtt::MqttDriver, topics::DimmableLightTopics, utils::parse_payload_to_f64};
 use core::{cmp::Ordering, time::Duration};
-use heapless::{String, Vec};
 use rppal::pwm::{Channel, Polarity, Pwm};
 use rumqttc::v5::mqttbytes::QoS;
 use serde::Deserialize;
@@ -57,15 +51,15 @@ impl PwmDriver {
         Self { pwm, topics, args }
     }
 
-    pub fn subscriptions(&self) -> Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS> {
-        Vec::from_slice(&[self.topics.set_value.clone()]).unwrap()
+    pub fn subscriptions(&self) -> Vec<String> {
+        vec![self.topics.set_value.clone()]
     }
 
     pub async fn run(&mut self, mut mqtt: MqttDriver) {
         // Publish the initial value of the PWM channel
         mqtt.publish(crate::mqtt::PublishMessage {
             topic: self.topics.value.clone(),
-            payload: to_mqtt_payload(self.args.initial),
+            payload: self.args.initial.to_string().into_bytes(),
             qos: QoS::AtLeastOnce,
             retain: true,
         })
@@ -83,7 +77,7 @@ impl PwmDriver {
                             Some(value) => {
                                 mqtt.publish(crate::mqtt::PublishMessage {
                                     topic: self.topics.value.clone(),
-                                    payload: to_mqtt_payload(value),
+                                    payload: value.to_string().into_bytes(),
                                     qos: QoS::AtLeastOnce,
                                     retain: true,
                                 })
diff --git a/core/src/temperature.rs b/core/src/temperature.rs
index 47907a4..1698b0c 100644
--- a/core/src/temperature.rs
+++ b/core/src/temperature.rs
@@ -1,11 +1,8 @@
 use crate::{
     mqtt::{MqttDriver, PublishMessage},
     topics::TemperatureTopics,
-    utils::to_mqtt_payload,
-    MAX_MQTT_SUBSCRIBED_TOPICS, MAX_MQTT_TOPIC_SIZE,
 };
 use core::time::Duration;
-use heapless::{String, Vec};
 use rumqttc::v5::mqttbytes::QoS;
 use serde::Deserialize;
 use serde_json::Value;
@@ -17,7 +14,7 @@ pub struct TemperatureDriver {
 
 #[derive(Deserialize)]
 struct TemperatureDriverArgs {
-    path: String<MAX_MQTT_TOPIC_SIZE>,
+    path: String,
     sleep: u64,
 }
 
@@ -32,8 +29,8 @@ impl TemperatureDriver {
         Self { args, topics }
     }
 
-    pub fn subscriptions(&self) -> Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS> {
-        Vec::new()
+    pub fn subscriptions(&self) -> Vec<String> {
+        vec![]
     }
 
     pub async fn run(&mut self, mqtt: MqttDriver) {
@@ -66,7 +63,7 @@ impl TemperatureDriver {
             // Publish the temperature
             mqtt.publish(PublishMessage {
                 topic: self.topics.temperature.clone(),
-                payload: to_mqtt_payload(temperature),
+                payload: temperature.to_string().into_bytes(),
                 qos: QoS::AtLeastOnce,
                 retain: true,
             })
diff --git a/core/src/topics.rs b/core/src/topics.rs
index 957fa81..84f29b1 100644
--- a/core/src/topics.rs
+++ b/core/src/topics.rs
@@ -1,160 +1,150 @@
-use crate::MAX_MQTT_TOPIC_SIZE;
-use core::fmt::Write;
-use heapless::String;
-
-macro_rules! format_topic {
-    ($($arg:tt)*) => {{
-        let mut s: String<MAX_MQTT_TOPIC_SIZE> = String::new();
-        let _ = write!(s, $($arg)*);
-        s
-    }};
-}
-
 #[derive(Debug)]
 pub struct ToggleTopics {
-    pub value: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_value: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_timed: String<MAX_MQTT_TOPIC_SIZE>,
+    pub value: String,
+    pub set_value: String,
+    pub set_timed: String,
 }
 
 impl ToggleTopics {
     pub fn new(index: usize) -> Self {
         Self {
-            value: format_topic!("state/toggle/{index}/value"),
-            set_value: format_topic!("state/toggle/{index}/set/value"),
-            set_timed: format_topic!("state/toggle/{index}/set/timed"),
+            value: format!("state/toggle/{index}/value"),
+            set_value: format!("state/toggle/{index}/set/value"),
+            set_timed: format!("state/toggle/{index}/set/timed"),
         }
     }
 }
 
 #[derive(Debug)]
 pub struct TemperatureTopics {
-    pub temperature: String<MAX_MQTT_TOPIC_SIZE>,
+    pub temperature: String,
 }
 
 impl TemperatureTopics {
     pub fn new(index: usize) -> Self {
         Self {
-            temperature: format_topic!("state/temperature/{index}/temperatureC"),
+            temperature: format!("state/temperature/{index}/temperatureC"),
         }
     }
 }
 
 #[derive(Debug)]
-pub struct LevelTopics {
-    pub level: String<MAX_MQTT_TOPIC_SIZE>,
+pub struct SensorTopics {
+    pub state_t: String,
+    pub unit_of_measurement: String,
 }
 
-impl LevelTopics {
-    pub fn new(index: usize) -> Self {
+impl SensorTopics {
+    pub fn new(node_id: &str, object_id: &str) -> Self {
         Self {
-            level: format_topic!("state/level/{index}/level"),
+            state_t: format!("s/sensor/{node_id}/{object_id}/state_t"),
+            unit_of_measurement: format!("s/sensor/{node_id}/{object_id}/unit_of_measurement"),
         }
     }
 }
 
 #[derive(Debug)]
 pub struct ChargerTopics {
-    pub enabled: String<MAX_MQTT_TOPIC_SIZE>,
-    pub output_voltage: String<MAX_MQTT_TOPIC_SIZE>,
-    pub output_current: String<MAX_MQTT_TOPIC_SIZE>,
-    pub output_power_watts: String<MAX_MQTT_TOPIC_SIZE>,
-    pub input_voltage: String<MAX_MQTT_TOPIC_SIZE>,
-    pub input_current: String<MAX_MQTT_TOPIC_SIZE>,
-    pub input_power_watts: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_enabled: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_max_output_power: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_max_input_power: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_max_output_amps: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_max_input_amps: String<MAX_MQTT_TOPIC_SIZE>,
+    pub enabled: String,
+    pub output_voltage: String,
+    pub output_current: String,
+    pub output_power_watts: String,
+    pub input_voltage: String,
+    pub input_current: String,
+    pub input_power_watts: String,
+    pub set_enabled: String,
+    pub set_max_output_power: String,
+    pub set_max_input_power: String,
+    pub set_max_output_amps: String,
+    pub set_max_input_amps: String,
 }
 
 impl ChargerTopics {
     pub fn new(index: usize) -> Self {
         Self {
-            enabled: format_topic!("state/charger/{index}/enabled"),
-            output_voltage: format_topic!("state/charger/{index}/outputVoltage"),
-            output_current: format_topic!("state/charger/{index}/outputCurrent"),
-            output_power_watts: format_topic!("state/charger/{index}/outputPowerWatts"),
-            input_voltage: format_topic!("state/charger/{index}/inputVoltage"),
-            input_current: format_topic!("state/charger/{index}/inputCurrent"),
-            input_power_watts: format_topic!("state/charger/{index}/inputPowerWatts"),
-            set_enabled: format_topic!("state/charger/{index}/set/enabled"),
-            set_max_output_power: format_topic!("state/charger/{index}/set/maxOutputPower"),
-            set_max_input_power: format_topic!("state/charger/{index}/set/maxInputPower"),
-            set_max_output_amps: format_topic!("state/charger/{index}/set/maxOutputAmps"),
-            set_max_input_amps: format_topic!("state/charger/{index}/set/maxInputAmps"),
+            enabled: format!("state/charger/{index}/enabled"),
+            output_voltage: format!("state/charger/{index}/outputVoltage"),
+            output_current: format!("state/charger/{index}/outputCurrent"),
+            output_power_watts: format!("state/charger/{index}/outputPowerWatts"),
+            input_voltage: format!("state/charger/{index}/inputVoltage"),
+            input_current: format!("state/charger/{index}/inputCurrent"),
+            input_power_watts: format!("state/charger/{index}/inputPowerWatts"),
+            set_enabled: format!("state/charger/{index}/set/enabled"),
+            set_max_output_power: format!("state/charger/{index}/set/maxOutputPower"),
+            set_max_input_power: format!("state/charger/{index}/set/maxInputPower"),
+            set_max_output_amps: format!("state/charger/{index}/set/maxOutputAmps"),
+            set_max_input_amps: format!("state/charger/{index}/set/maxInputAmps"),
         }
     }
 }
 
 #[derive(Debug)]
 pub struct BatteryTopics {
-    pub soc: String<MAX_MQTT_TOPIC_SIZE>,
-    pub voltage: String<MAX_MQTT_TOPIC_SIZE>,
-    pub current: String<MAX_MQTT_TOPIC_SIZE>,
-    pub _discharge_enabled: String<MAX_MQTT_TOPIC_SIZE>,
-    pub _charge_enabled: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_discharge_enabled: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_charge_enabled: String<MAX_MQTT_TOPIC_SIZE>,
+    pub soc: String,
+    pub voltage: String,
+    pub current: String,
+    pub _discharge_enabled: String,
+    pub _charge_enabled: String,
+    pub set_discharge_enabled: String,
+    pub set_charge_enabled: String,
 }
 
 impl BatteryTopics {
     pub fn new(index: usize) -> Self {
         Self {
-            soc: format_topic!("state/battery/{index}/soc"),
-            voltage: format_topic!("state/battery/{index}/voltage"),
-            current: format_topic!("state/battery/{index}/current"),
-            _discharge_enabled: format_topic!("state/battery/{index}/dischargeEnabled"),
-            _charge_enabled: format_topic!("state/battery/{index}/chargeEnabled"),
-            set_discharge_enabled: format_topic!("state/battery/{index}/set/dischargeEnabled"),
-            set_charge_enabled: format_topic!("state/battery/{index}/set/chargeEnabled"),
+            soc: format!("state/battery/{index}/soc"),
+            voltage: format!("state/battery/{index}/voltage"),
+            current: format!("state/battery/{index}/current"),
+            _discharge_enabled: format!("state/battery/{index}/dischargeEnabled"),
+            _charge_enabled: format!("state/battery/{index}/chargeEnabled"),
+            set_discharge_enabled: format!("state/battery/{index}/set/dischargeEnabled"),
+            set_charge_enabled: format!("state/battery/{index}/set/chargeEnabled"),
         }
     }
 }
 
 #[derive(Debug)]
 pub struct InverterTopics {
-    pub enabled: String<MAX_MQTT_TOPIC_SIZE>,
-    pub output_voltage: String<MAX_MQTT_TOPIC_SIZE>,
-    pub output_current: String<MAX_MQTT_TOPIC_SIZE>,
-    pub output_power_watts: String<MAX_MQTT_TOPIC_SIZE>,
-    pub input_voltage: String<MAX_MQTT_TOPIC_SIZE>,
-    pub input_current: String<MAX_MQTT_TOPIC_SIZE>,
-    pub input_power_watts: String<MAX_MQTT_TOPIC_SIZE>,
-    pub _max_shore_amps: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_enabled: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_max_shore_amps: String<MAX_MQTT_TOPIC_SIZE>,
+    pub enabled: String,
+    pub output_voltage: String,
+    pub output_current: String,
+    pub output_power_watts: String,
+    pub input_voltage: String,
+    pub input_current: String,
+    pub input_power_watts: String,
+    pub _max_shore_amps: String,
+    pub set_enabled: String,
+    pub set_max_shore_amps: String,
 }
 
 impl InverterTopics {
     pub fn new(index: usize) -> Self {
         Self {
-            enabled: format_topic!("state/inverter/{index}/enabled"),
-            output_voltage: format_topic!("state/inverter/{index}/outputVoltage"),
-            output_current: format_topic!("state/inverter/{index}/outputCurrent"),
-            output_power_watts: format_topic!("state/inverter/{index}/outputPowerWatts"),
-            input_voltage: format_topic!("state/inverter/{index}/inputVoltage"),
-            input_current: format_topic!("state/inverter/{index}/inputCurrent"),
-            input_power_watts: format_topic!("state/inverter/{index}/inputPowerWatts"),
-            _max_shore_amps: format_topic!("state/inverter/{index}/maxShoreAmps"),
-            set_enabled: format_topic!("state/inverter/{index}/set/enabled"),
-            set_max_shore_amps: format_topic!("state/inverter/{index}/set/maxShoreAmps"),
+            enabled: format!("state/inverter/{index}/enabled"),
+            output_voltage: format!("state/inverter/{index}/outputVoltage"),
+            output_current: format!("state/inverter/{index}/outputCurrent"),
+            output_power_watts: format!("state/inverter/{index}/outputPowerWatts"),
+            input_voltage: format!("state/inverter/{index}/inputVoltage"),
+            input_current: format!("state/inverter/{index}/inputCurrent"),
+            input_power_watts: format!("state/inverter/{index}/inputPowerWatts"),
+            _max_shore_amps: format!("state/inverter/{index}/maxShoreAmps"),
+            set_enabled: format!("state/inverter/{index}/set/enabled"),
+            set_max_shore_amps: format!("state/inverter/{index}/set/maxShoreAmps"),
         }
     }
 }
 
 #[derive(Debug)]
 pub struct DimmableLightTopics {
-    pub value: String<MAX_MQTT_TOPIC_SIZE>,
-    pub set_value: String<MAX_MQTT_TOPIC_SIZE>,
+    pub value: String,
+    pub set_value: String,
 }
 
 impl DimmableLightTopics {
     pub fn new(index: usize) -> Self {
         Self {
-            value: format_topic!("state/dimmableLight/{index}/value"),
-            set_value: format_topic!("state/dimmableLight/{index}/set/value"),
+            value: format!("state/dimmableLight/{index}/value"),
+            set_value: format!("state/dimmableLight/{index}/set/value"),
         }
     }
 }
diff --git a/core/src/utils.rs b/core/src/utils.rs
index 5adb393..06eee3e 100644
--- a/core/src/utils.rs
+++ b/core/src/utils.rs
@@ -1,24 +1,14 @@
-use crate::MAX_MQTT_MESSAGE_SIZE;
-use core::fmt::{Display, Write};
-use heapless::{String, Vec};
-
-pub fn to_mqtt_payload<T: Display>(value: T) -> Vec<u8, MAX_MQTT_MESSAGE_SIZE> {
-    let mut bytes = Vec::new();
-    let _ = write!(bytes, "{value}");
-    bytes
-}
-
-pub fn parse_payload_to_bool(mut payload: Vec<u8, MAX_MQTT_MESSAGE_SIZE>) -> bool {
+pub fn parse_payload_to_bool(mut payload: Vec<u8>) -> bool {
     payload.make_ascii_lowercase();
     !matches!(payload.as_slice(), b"false" | b"0" | b"" | b"off")
 }
 
-pub fn parse_string_mqtt_boolean(mut payload: String<MAX_MQTT_MESSAGE_SIZE>) -> bool {
+pub fn parse_string_mqtt_boolean(mut payload: String) -> bool {
     payload.make_ascii_lowercase();
     !matches!(payload.as_str(), "false" | "0" | "" | "off")
 }
 
-pub fn parse_payload_to_f64(payload: Vec<u8, MAX_MQTT_MESSAGE_SIZE>) -> Option<f64> {
+pub fn parse_payload_to_f64(payload: Vec<u8>) -> Option<f64> {
     let payload = core::str::from_utf8(payload.as_slice()).ok()?;
     payload.parse().ok()
 }
diff --git a/core/src/vebus.rs b/core/src/vebus.rs
index 88be697..31d968b 100644
--- a/core/src/vebus.rs
+++ b/core/src/vebus.rs
@@ -1,9 +1,7 @@
 use crate::mqtt::{MqttDriver, PublishMessage};
 use crate::topics::{ChargerTopics, InverterTopics};
-use crate::utils::{parse_payload_to_bool, to_mqtt_payload};
-use crate::{MAX_MQTT_SUBSCRIBED_TOPICS, MAX_MQTT_TOPIC_SIZE};
+use crate::utils::parse_payload_to_bool;
 use ::vebus::{Command, RamVariables, VebusError, VebusReader, VebusResponse};
-use heapless::{String, Vec};
 use rumqttc::v5::mqttbytes::QoS;
 use serde::Deserialize;
 use serde_json::Value;
@@ -36,7 +34,7 @@ pub struct VebusDriver {
 
 #[derive(Debug, Deserialize)]
 struct VebusDriverArgs {
-    port: String<MAX_MQTT_TOPIC_SIZE>,
+    port: String,
     sleep: u64,
 }
 
@@ -78,8 +76,8 @@ impl VebusDriver {
         }
     }
 
-    pub fn subscriptions(&self) -> Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS> {
-        Vec::from_slice(&[
+    pub fn subscriptions(&self) -> Vec<String> {
+        vec![
             self.topics.charger.set_enabled.clone(),
             self.topics.charger.set_max_output_power.clone(),
             self.topics.charger.set_max_input_power.clone(),
@@ -87,8 +85,7 @@ impl VebusDriver {
             self.topics.charger.set_max_input_amps.clone(),
             self.topics.inverter.set_enabled.clone(),
             self.topics.inverter.set_max_shore_amps.clone(),
-        ])
-        .unwrap()
+        ]
     }
 
     pub async fn run(&mut self, mut mqtt: MqttDriver) {
@@ -194,8 +191,8 @@ impl VebusDriver {
     ) {
         self.cache.charger = frame.switch_register.switch_charger;
         self.cache.inverter = frame.switch_register.switch_inverter;
-        let charger_enabled = to_mqtt_payload(frame.switch_register.switch_charger);
-        let inverter_enabled = to_mqtt_payload(frame.switch_register.switch_inverter);
+        let charger_enabled = frame.switch_register.switch_charger.to_string();
+        let inverter_enabled = frame.switch_register.switch_inverter.to_string();
         let topics = [
             (self.topics.charger.enabled.clone(), charger_enabled),
             (self.topics.inverter.enabled.clone(), inverter_enabled),
@@ -204,7 +201,7 @@ impl VebusDriver {
         for (topic, payload) in topics {
             mqtt.publish(PublishMessage {
                 topic,
-                payload,
+                payload: payload.into(),
                 qos: QoS::AtLeastOnce,
                 retain: true,
             })
@@ -213,9 +210,9 @@ impl VebusDriver {
     }
 
     async fn publish_dc_frame(&self, mqtt: &MqttDriver, frame: DcFrame) {
-        let v = to_mqtt_payload(frame.dc_voltage);
-        let i = to_mqtt_payload(frame.dc_current_used);
-        let p = to_mqtt_payload(frame.dc_voltage * frame.dc_current_used);
+        let v = frame.dc_voltage.to_string();
+        let i = frame.dc_current_used.to_string();
+        let p = (frame.dc_voltage * frame.dc_current_used).to_string();
         let topics = [
             (self.topics.charger.output_voltage.clone(), v.clone()),
             (self.topics.charger.output_current.clone(), i.clone()),
@@ -228,7 +225,7 @@ impl VebusDriver {
         for (topic, payload) in topics {
             mqtt.publish(PublishMessage {
                 topic,
-                payload,
+                payload: payload.into(),
                 qos: QoS::AtLeastOnce,
                 retain: true,
             })
@@ -238,12 +235,12 @@ impl VebusDriver {
 
     async fn publish_ac_frame(&mut self, mqtt: &MqttDriver, frame: AcFrame) {
         log::debug!("Vebus state: {:?}", frame.state);
-        let cv = to_mqtt_payload(frame.mains_voltage);
-        let ci = to_mqtt_payload(frame.mains_current);
-        let cp = to_mqtt_payload(frame.mains_voltage * frame.mains_current);
-        let iv = to_mqtt_payload(frame.inverter_voltage);
-        let ii = to_mqtt_payload(frame.inverter_current);
-        let ip = to_mqtt_payload(frame.inverter_voltage * frame.inverter_current);
+        let cv = frame.mains_voltage;
+        let ci = frame.mains_current;
+        let cp = frame.mains_voltage * frame.mains_current;
+        let iv = frame.inverter_voltage;
+        let ii = frame.inverter_current;
+        let ip = frame.inverter_voltage * frame.inverter_current;
         let topics = [
             (self.topics.charger.input_voltage.clone(), cv),
             (self.topics.charger.input_current.clone(), ci),
@@ -256,7 +253,7 @@ impl VebusDriver {
         for (topic, payload) in topics {
             mqtt.publish(PublishMessage {
                 topic,
-                payload,
+                payload: payload.to_string().into(),
                 qos: QoS::AtLeastOnce,
                 retain: true,
             })
@@ -301,11 +298,11 @@ impl VebusDriver {
     }
 
     async fn read_ram_variables(&mut self) -> Result<RamVariables, VebusCommunicationError> {
-        let mut scale_offsets = Vec::<ScaleOffsetResponse, 8>::new();
+        let mut scale_offsets = Vec::<ScaleOffsetResponse>::new();
         for var in RamVariables::ALL_VARIABLES {
             let response = self.send_command(Command::GetScaleOffset(var)).await?;
             match response {
-                VebusResponse::ScaleOffset(so) => scale_offsets.push(so).unwrap(),
+                VebusResponse::ScaleOffset(so) => scale_offsets.push(so),
                 _ => {
                     return Err(VebusCommunicationError::VebusError(
                         VebusError::UnknownResponseFormat,
diff --git a/core/src/vedirect.rs b/core/src/vedirect.rs
index 7c81605..f2561c5 100644
--- a/core/src/vedirect.rs
+++ b/core/src/vedirect.rs
@@ -1,10 +1,8 @@
 use crate::{
     mqtt::{MqttDriver, PublishMessage},
     topics::{BatteryTopics, ChargerTopics, InverterTopics},
-    utils::{parse_payload_to_bool, to_mqtt_payload},
-    MAX_MQTT_SUBSCRIBED_TOPICS, MAX_MQTT_TOPIC_SIZE,
+    utils::parse_payload_to_bool,
 };
-use heapless::{String, Vec};
 use rumqttc::v5::mqttbytes::QoS;
 use serde::Deserialize;
 use serde_json::Value;
@@ -24,7 +22,7 @@ pub struct VedirectDriver {
 
 #[derive(Deserialize)]
 struct VedirectDriverArgs {
-    port: String<MAX_MQTT_TOPIC_SIZE>,
+    port: String,
 }
 
 impl VedirectDriver {
@@ -49,8 +47,8 @@ impl VedirectDriver {
         }
     }
 
-    pub fn subscriptions(&self) -> Vec<String<MAX_MQTT_TOPIC_SIZE>, MAX_MQTT_SUBSCRIBED_TOPICS> {
-        Vec::from_slice(&[
+    pub fn subscriptions(&self) -> Vec<String> {
+        vec![
             self.topics.charger.set_enabled.clone(),
             self.topics.charger.set_max_output_power.clone(),
             self.topics.charger.set_max_input_power.clone(),
@@ -60,8 +58,7 @@ impl VedirectDriver {
             self.topics.battery.set_charge_enabled.clone(),
             self.topics.inverter.set_enabled.clone(),
             self.topics.inverter.set_max_shore_amps.clone(),
-        ])
-        .unwrap()
+        ]
     }
 
     pub async fn run(&mut self, mut mqtt: MqttDriver) {
@@ -112,12 +109,9 @@ impl VedirectDriver {
     }
 
     async fn publish_battery_record(&self, mqtt: &MqttDriver, record: TextRecord) {
-        let soc = record
-            .soc
-            .map(|soc| soc as f32 / 1000.0)
-            .map(to_mqtt_payload);
-        let voltage = record.v.map(|v| v as f32 / 1000.0).map(to_mqtt_payload);
-        let current = record.i.map(|i| i as f32 / 1000.0).map(to_mqtt_payload);
+        let soc = record.soc.map(|soc| soc as f32 / 1000.0);
+        let voltage = record.v.map(|v| v as f32 / 1000.0);
+        let current = record.i.map(|i| i as f32 / 1000.0);
 
         let topics = [
             (self.topics.battery.soc.clone(), soc),
@@ -129,7 +123,7 @@ impl VedirectDriver {
             if let Some(value) = value {
                 mqtt.publish(PublishMessage {
                     topic,
-                    payload: value,
+                    payload: value.to_string().into(),
                     qos: QoS::AtLeastOnce,
                     retain: true,
                 })
@@ -146,26 +140,39 @@ impl VedirectDriver {
         let enabled = record
             .cs
             .map(|cs| cs != ChargerStatus::Off)
-            .map(to_mqtt_payload);
+            .map(|cs| cs.to_string().into_bytes());
+
+        let output_voltage = record
+            .v
+            .map(|v| v as f32 / 1000.0)
+            .map(|v| v.to_string().into_bytes());
+
+        let output_current = record
+            .i
+            .map(|i| i as f32 / 1000.0)
+            .map(|i| i.to_string().into_bytes());
 
-        let output_voltage = record.v.map(|v| v as f32 / 1000.0).map(to_mqtt_payload);
-        let output_current = record.i.map(|i| i as f32 / 1000.0).map(to_mqtt_payload);
         let output_power = match (record.v, record.i) {
             (Some(v), Some(i)) => Some(v as f32 / 1000.0 * i as f32 / 1000.0),
             _ => None,
         }
-        .map(to_mqtt_payload);
+        .map(|p| p.to_string().into_bytes());
 
         let input_voltage = record
             .vpv
             .map(|vpv| vpv as f32 / 1000.0)
-            .map(to_mqtt_payload);
-        let input_power = record.ppv.map(to_mqtt_payload);
+            .map(|vpv| vpv.to_string().into_bytes());
+
+        let input_power = record
+            .ppv
+            .map(|ppv| ppv as f32 / 1000.0)
+            .map(|ppv| ppv.to_string().into_bytes());
+
         let input_current = match (record.vpv, record.ppv) {
             (Some(v), Some(p)) => Some(p as f32 / (v as f32 / 1000.0)),
             _ => None,
         }
-        .map(to_mqtt_payload);
+        .map(|i| i.to_string().into_bytes());
 
         let topics = [
             (self.topics.charger.enabled.clone(), enabled),
diff --git a/deploy.sh b/deploy.sh
index cef110d..5223ec2 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -4,16 +4,21 @@ usage() {
 	echo "Usage: $0 [options]"
 	echo "Options:"
 	echo "  -d DOMAIN       SSH domain"
-	echo "  -p PORT         SSH port"
+	echo "  -p PORT         SSH port, defaults to 22"
 	echo "  -t TARGET_PATH  Target path on the server"
 	echo "  --cc			Copy the configuration file to the server"
 	echo "  --release       Build the project in release mode"
 	echo "  -h, --help      Display this help and exit"
 	echo ""
+	echo "Requirements:"
+	echo "  - Rust cargo cross installed"
+	echo "  - Docker or podman installed"
+	echo ""
 	echo "Example: $0 -d user@mydomain.net -p 22 -t /home/user/core --release"
 	exit 1
 }
 
+PORT=22
 RELEASE_MODE=false
 COPY_CONFIGURATION=false
 
@@ -36,13 +41,16 @@ fi
 
 echo "Building the Rust project"
 if $RELEASE_MODE; then
-  cargo build --release
+  cross build --target armv7-unknown-linux-musleabihf --release
   BUILD_PATH="target/armv7-unknown-linux-musleabihf/release/core"
 else
-  cargo build
+  cross build --target armv7-unknown-linux-musleabihf
   BUILD_PATH="target/armv7-unknown-linux-musleabihf/debug/core"
 fi
 
+echo "Copying the binary to the server"
+scp -P "$PORT" "$BUILD_PATH" "$DOMAIN:$TARGET_PATH"
+
 if $COPY_CONFIGURATION; then
   echo "Copying the configuration file to the server"
   scp -P "$PORT" "core/config.json" "$DOMAIN:$TARGET_PATH"
diff --git a/drivers/lin/src/truma.rs b/drivers/lin/src/truma.rs
index 92158cc..21dfa22 100644
--- a/drivers/lin/src/truma.rs
+++ b/drivers/lin/src/truma.rs
@@ -1,6 +1,4 @@
-use crate::protocol::{
-    CombinedFrame, Frame, FrameData, FrameId, Rsid, ServiceRequestFrame, ServiceResponseFrame, Sid,
-};
+use crate::protocol::{CombinedFrame, FrameId, Rsid, Sid};
 
 /// Truma frame/command
 #[derive(Debug, PartialEq, Eq, Clone)]
-- 
GitLab


From 6b8631548958e3bbd33346beb874d27fa6d8536e Mon Sep 17 00:00:00 2001
From: DKolter <danielkolter157@gmail.com>
Date: Fri, 13 Dec 2024 16:56:06 +0100
Subject: [PATCH 4/4] Adc driver adoption

---
 core/src/adc.rs    | 71 ++++++++++++++++++++++++++++------------------
 core/src/config.rs |  1 +
 core/src/main.rs   | 15 ++++++----
 core/src/topics.rs |  8 ++----
 4 files changed, 57 insertions(+), 38 deletions(-)

diff --git a/core/src/adc.rs b/core/src/adc.rs
index 8c5295d..8843573 100644
--- a/core/src/adc.rs
+++ b/core/src/adc.rs
@@ -7,35 +7,40 @@ use core::time::Duration;
 use rumqttc::v5::mqttbytes::QoS;
 use serde::Deserialize;
 use serde_json::Value;
+use std::path::{Path, PathBuf};
+
+const REFERENCE_VOLTAGE: f64 = 2.048;
+const ADC_MAXIMUM: u16 = 4096;
 
 pub struct AdcDriver {
     args: AdcDriverArgs,
-    topics: [SensorTopics; 8],
+    pins: Vec<AdcPin>,
+}
+
+struct AdcPin {
+    path: PathBuf,
+    topics: SensorTopics,
 }
 
 #[derive(Deserialize)]
 struct AdcDriverArgs {
-    path: String,
+    path: PathBuf,
     sleep: u64,
 }
 
 impl AdcDriver {
-    pub async fn new(_index: usize, args: Value) -> Result<Self> {
+    pub async fn new(client_id: &str, _index: usize, args: Value) -> Result<Self> {
         let args: AdcDriverArgs =
             serde_json::from_value(args).context("Parsing adc driver args")?;
 
-        let topics = [
-            SensorTopics::new("adc", "0"),
-            SensorTopics::new("adc", "1"),
-            SensorTopics::new("adc", "2"),
-            SensorTopics::new("adc", "3"),
-            SensorTopics::new("adc", "4"),
-            SensorTopics::new("adc", "5"),
-            SensorTopics::new("adc", "6"),
-            SensorTopics::new("adc", "7"),
-        ];
+        let pins = (0..8)
+            .map(|i| AdcPin {
+                path: args.path.join(format!("in_voltage{i}_raw")),
+                topics: SensorTopics::new(&client_id, "adc", &format!("{i}")),
+            })
+            .collect();
 
-        Ok(Self { args, topics })
+        Ok(Self { args, pins })
     }
 
     pub fn subscriptions(&self) -> Vec<String> {
@@ -43,24 +48,20 @@ impl AdcDriver {
     }
 
     pub async fn run(&mut self, mqtt: MqttDriver) {
-        // Publish the unit of measurement (V) for all topics
-        for topic in self.topics.iter() {
-            mqtt.publish(PublishMessage {
-                topic: topic.unit_of_measurement.clone(),
-                payload: b"V".to_vec(),
-                qos: QoS::AtLeastOnce,
-                retain: true,
-            })
-            .await;
-        }
-
         loop {
-            for (i, topic) in self.topics.iter().enumerate() {
-                let value = 0.0; // Read the value from the ADC
+            for pin in &self.pins {
+                // Read the raw value from the pin path
+                let value = match Self::read_raw_value(&pin.path).await {
+                    Ok(value) => value,
+                    Err(err) => {
+                        log::error!("Reading raw value: {:?}", err);
+                        continue;
+                    }
+                };
 
                 // Publish the value
                 mqtt.publish(PublishMessage {
-                    topic: topic.state_t.clone(),
+                    topic: pin.topics.state.clone(),
                     payload: value.to_string().into_bytes(),
                     qos: QoS::AtLeastOnce,
                     retain: true,
@@ -71,4 +72,18 @@ impl AdcDriver {
             tokio::time::sleep(Duration::from_secs(self.args.sleep)).await;
         }
     }
+
+    async fn read_raw_value(path: &Path) -> Result<f64> {
+        let raw = tokio::fs::read_to_string(path)
+            .await
+            .context("Reading raw value")?;
+
+        let raw = raw
+            .trim()
+            .parse::<u16>()
+            .context("Parsing raw value")?
+            .min(ADC_MAXIMUM);
+
+        Ok(raw as f64 / ADC_MAXIMUM as f64 * REFERENCE_VOLTAGE)
+    }
 }
diff --git a/core/src/config.rs b/core/src/config.rs
index 06604da..7d17eaf 100644
--- a/core/src/config.rs
+++ b/core/src/config.rs
@@ -4,6 +4,7 @@ use serde_json::Value;
 
 #[derive(Deserialize, Debug)]
 pub struct Config {
+    pub client_id: String,
     pub mqtt: MqttConfig,
     pub drivers: Vec<DriverConfig>,
 }
diff --git a/core/src/main.rs b/core/src/main.rs
index dac0f88..9047449 100644
--- a/core/src/main.rs
+++ b/core/src/main.rs
@@ -42,14 +42,19 @@ impl Driver {
     /// The index is determined by the order of the drivers;
     /// in the configuration file at the moment, but will be
     /// determined by a MQTT handshake with the core in the future.
-    async fn new(protocol: DriverProtocol, index: usize, args: Value) -> Result<Self> {
+    async fn new(
+        client_id: &str,
+        protocol: DriverProtocol,
+        index: usize,
+        args: Value,
+    ) -> Result<Self> {
         Ok(match protocol {
             DriverProtocol::Vebus => Self::Vebus(VebusDriver::new(index, args).await),
             DriverProtocol::Vedirect => Self::Vedirect(VedirectDriver::new(index, args).await),
             DriverProtocol::OutputGpio => {
                 Self::OutputGpio(GpioOutputDriver::new(index, args).await)
             }
-            DriverProtocol::Adc => Self::Adc(AdcDriver::new(index, args).await?),
+            DriverProtocol::Adc => Self::Adc(AdcDriver::new(client_id, index, args).await?),
             DriverProtocol::Temperature => {
                 Self::Temperature(TemperatureDriver::new(index, args).await)
             }
@@ -90,7 +95,7 @@ impl Driver {
 
 /// Create a list of drivers from the given configurations.
 /// When a new driver is added, it should be added to the match statement.
-async fn create_drivers(configs: Vec<DriverConfig>) -> Vec<Driver> {
+async fn create_drivers(client_id: String, configs: Vec<DriverConfig>) -> Vec<Driver> {
     let mut drivers: Vec<Driver> = Vec::new();
     for config in configs {
         log::info!(
@@ -98,7 +103,7 @@ async fn create_drivers(configs: Vec<DriverConfig>) -> Vec<Driver> {
             config.index,
             config.protocol
         );
-        match Driver::new(config.protocol, config.index, config.args).await {
+        match Driver::new(&client_id, config.protocol, config.index, config.args).await {
             Ok(driver) => drivers.push(driver),
             Err(e) => log::error!("Failed to create driver: {e}"),
         }
@@ -114,7 +119,7 @@ async fn main() {
 
     // Load the configuration file and create respective drivers
     let config = Config::load().expect("Failed to load configuration file");
-    let drivers = create_drivers(config.drivers).await;
+    let drivers = create_drivers(config.client_id, config.drivers).await;
 
     // Create the MQTT driver
     let mqtt = MqttDriver::new(config.mqtt, &drivers).await;
diff --git a/core/src/topics.rs b/core/src/topics.rs
index 84f29b1..cb7f636 100644
--- a/core/src/topics.rs
+++ b/core/src/topics.rs
@@ -30,15 +30,13 @@ impl TemperatureTopics {
 
 #[derive(Debug)]
 pub struct SensorTopics {
-    pub state_t: String,
-    pub unit_of_measurement: String,
+    pub state: String,
 }
 
 impl SensorTopics {
-    pub fn new(node_id: &str, object_id: &str) -> Self {
+    pub fn new(client_id: &str, node_id: &str, object_id: &str) -> Self {
         Self {
-            state_t: format!("s/sensor/{node_id}/{object_id}/state_t"),
-            unit_of_measurement: format!("s/sensor/{node_id}/{object_id}/unit_of_measurement"),
+            state: format!("s/{client_id}/{node_id}/{object_id}/state"),
         }
     }
 }
-- 
GitLab