From 39b0dbf61b1296259e5ad0950a92ac897b986563 Mon Sep 17 00:00:00 2001
From: Benjamin Koch <bbbsnowball@gmail.com>
Date: Sun, 11 Jun 2023 04:36:54 +0200
Subject: [PATCH] put default Modbus settings into program info

---
 firmware/rust1/src/bin/heizung.rs  | 69 ++++++++++++++++++++++++------
 firmware/rust1/src/program_info.rs | 38 +++++++++++++++-
 2 files changed, 93 insertions(+), 14 deletions(-)

diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs
index 4aa316a..a0d06aa 100644
--- a/firmware/rust1/src/bin/heizung.rs
+++ b/firmware/rust1/src/bin/heizung.rs
@@ -573,6 +573,60 @@ async fn dogfeeder(watchdog: peripherals::WATCHDOG) {
     WatchdogFixed::new(watchdog).start_and_feed_continuously().await;
 }
 
+// use 19200 baud in 8E1 mode - not great but it's the Modbus default
+const DEFAULT_MODBUS_CONFIG_BAUDRATE: u32 = 19200;
+//const DEFAULT_MODBUS_CONFIG_BAUD = 115200*2;
+const DEFAULT_MODBUS_CONFIG_PARITY: Parity = Parity::ParityEven;
+const DEFAULT_MODBUS_CONFIG_STRING: ([u8; 30], usize) = {
+    let mut x = [0 as u8; 30];
+    x[0] = '8' as u8;
+    x[1] = match DEFAULT_MODBUS_CONFIG_PARITY {
+        Parity::ParityEven => 'E',
+        Parity::ParityOdd => 'O',
+        Parity::ParityNone => 'N',
+    } as u8;
+    x[2] = '1' as u8;
+    x[3] = ',' as u8;
+    x[4] = ' ' as u8;
+
+    const fn reverse(mut acc: ([u8; 30], usize), index: usize) -> ([u8; 30], usize) {
+        if index >= acc.1/2 {
+            acc
+        } else {
+            let x = acc.0[index];
+            acc.0[index] = acc.0[acc.1-index-1];
+            acc.0[acc.1-index-1] = x;
+            reverse(acc, index+1)
+        }
+    }
+    const fn num_to_str(mut acc: [u8; 30], index: usize, value: u32) -> ([u8; 30], usize) {
+        if value == 0 && index > 0 {
+            reverse((acc, index), 0)
+        } else {
+            acc[index] = '0' as u8 + (value % 10) as u8;
+            num_to_str(acc, index+1, value/10)
+        }
+    }
+    const fn append(mut acc: ([u8; 30], usize), index: usize, source: ([u8; 30], usize)) -> ([u8; 30], usize) {
+        if index >= source.1 {
+            acc
+        } else {
+            acc.0[acc.1] = source.0[index];
+            acc.1 += 1;
+            append(acc, index+1, source)
+        }
+    }
+
+    append((x, 5), 0, num_to_str([0 as u8; 30], 0, DEFAULT_MODBUS_CONFIG_BAUDRATE))
+};
+fn default_modbus_config() -> uart::Config {
+    let mut uart_config = uart::Config::default();
+    uart_config.baudrate = DEFAULT_MODBUS_CONFIG_BAUDRATE;
+    //uart_config.baudrate = 115200*2;
+    uart_config.parity = DEFAULT_MODBUS_CONFIG_PARITY;
+    uart_config
+}
+
 //#[embassy_executor::main]
 async fn main2(spawner: Spawner) {
     let p = embassy_rp::init(Default::default());
@@ -621,13 +675,8 @@ async fn main2(spawner: Spawner) {
     static OUTPUTS_ACTIVE: AtomicU8 = AtomicU8::new(0);
     unwrap!(spawner.spawn(adc_task(&ADC_DATA, p.ADC, en_measure_current, analog_in1, current2, current1, measure_vcc, &OUTPUTS_ACTIVE)));
 
-    // use 19200 baud in 8E1 mode - not great but it's the Modbus default
-    let mut uart_config = uart::Config::default();
-    uart_config.baudrate = 19200;
-    //uart_config.baudrate = 115200*2;
-    uart_config.parity = Parity::ParityEven;
     let rs485 = RS485::new(
-        p.UART0, rx, tx, tx_en, interrupt::take!(UART0_IRQ), p.DMA_CH1, uart_config,
+        p.UART0, rx, tx, tx_en, interrupt::take!(UART0_IRQ), p.DMA_CH1, default_modbus_config(),
         p.PIO0, p.DMA_CH0, p.DMA_CH2,
         ModbusServer::new(ModBusRegs {
             led_g,
@@ -760,12 +809,8 @@ pub static PROGRAM_INFO: [u8; 4096] = {
             (28, "Current Measurement Channel 1"),
             (29, "VCC Measurement"),
         ])
-        .program_feature("abc")
-        .program_feature("abc2")
-        .program_feature("abc 3")
-        .program_feature_group_with_flags(['C', '3'], 2, "def", heizung::program_info::GroupFlags::ADVANCED)
-        .feature("abc 4")
-        .feature("abc 5")
+        .program_feature_group(['C', '3'], 0x27a7c59b, "Modbus")
+        .feature_utf8_part::<{DEFAULT_MODBUS_CONFIG_STRING.1}>(&DEFAULT_MODBUS_CONFIG_STRING.0)
         .group_done()
         .build()
 };
diff --git a/firmware/rust1/src/program_info.rs b/firmware/rust1/src/program_info.rs
index 737d355..eddfa31 100644
--- a/firmware/rust1/src/program_info.rs
+++ b/firmware/rust1/src/program_info.rs
@@ -50,6 +50,18 @@ const fn copy(mut buf: [u8; 4096], offset: usize, source: &[u8], source_offset:
     }
 }
 
+const fn copy2<const N: usize, const N2: usize>(mut acc: [u8; N], source: &[u8], index: usize) -> [u8; N] {
+    if index >= N2 {
+        acc
+    } else {
+        acc[index] = source[index];
+        copy2::<N, N2>(acc, source, index+1)
+    }
+}
+const fn copy3<const N: usize, const N2: usize>(source: &[u8]) -> [u8; N] {
+    copy2::<N, N2>([0; N], source, 0)
+}
+
 impl ProgramInfoBuilder {
     pub const fn new(addr: u32, info_table_size: u32, copy_table_size: u32) -> Self {
         let copy_table_size = copy_table_size as usize;
@@ -181,7 +193,7 @@ impl ProgramInfoBuilder {
     }
 
     //NOTE This will push the address and then add the string so this must be the last item of the data record.
-    const fn push_string(mut self: Self, value: &str) -> Self {
+    const fn push_string_utf8(mut self: Self, value: &[u8]) -> Self {
         if self.data_pos - self.info_pos < value.len() + 1 {
             core::panic!("ProgramInfoBuilder: too much data")
         }
@@ -190,7 +202,7 @@ impl ProgramInfoBuilder {
         let x1 = self.data_pos;
         self = self.push_u32(0);
 
-        let (self2, addr) = self.push_extra_data(value.as_bytes());
+        let (self2, addr) = self.push_extra_data(value);
         self = self2;
         (self, _) = self.push_extra_data(&[0]);
 
@@ -203,6 +215,11 @@ impl ProgramInfoBuilder {
         self
     }
 
+    //NOTE This will push the address and then add the string so this must be the last item of the data record.
+    const fn push_string(self: Self, value: &str) -> Self {
+        self.push_string_utf8(value.as_bytes())
+    }
+
     const fn append_to_last_string(mut self: Self, value: &str) -> Self {
         if self.data_pos - self.info_pos < value.len() {
             core::panic!("ProgramInfoBuilder: too much data")
@@ -231,6 +248,14 @@ impl ProgramInfoBuilder {
             .push_string(value)
     }
 
+    const fn bi_string_utf8(self: Self, tag: [char; 2], id: u32, value: &[u8]) -> Self {
+        self.push_current_info_ptr()
+            .push_u16(Self::BINARY_INFO_TYPE_ID_AND_STRING)
+            .push_tag(tag)
+            .push_u32(id)
+            .push_string_utf8(value)
+    }
+
     const fn bi_named_group(self: Self, tag: [char; 2], id: u32, flags: u16, group_tag: [char; 2], group_id: u32, label: &str) -> Self {
         self.push_current_info_ptr()
             .push_u16(Self::BINARY_INFO_TYPE_NAMED_GROUP)
@@ -387,6 +412,15 @@ impl ProgramInfoBuilderForGroup {
         self.builder = self.builder.bi_string(self.tag, self.id, name);
         self
     }
+    pub const fn feature_utf8(mut self: Self, name: &[u8]) -> Self {
+        self.builder = self.builder.bi_string_utf8(self.tag, self.id, name);
+        self
+    }
+    pub const fn feature_utf8_part<const N: usize>(mut self: Self, name: &[u8]) -> Self {
+        let name: [u8; N] = copy3::<N, N>(name);
+        self.builder = self.builder.bi_string_utf8(self.tag, self.id, &name);
+        self
+    }
     pub const fn group_done(self: Self) -> ProgramInfoBuilder {
         self.builder
     }
-- 
GitLab