From ba0b98e53d8946da21e813ccb959427595e1c225 Mon Sep 17 00:00:00 2001
From: Benjamin Koch <bbbsnowball@gmail.com>
Date: Sat, 13 May 2023 03:09:33 +0200
Subject: [PATCH] add very broken I2C scan

---
 firmware/rust1/Cargo.lock         |   1 +
 firmware/rust1/Cargo.toml         |   2 +
 firmware/rust1/src/bin/heizung.rs | 150 +++++++++++++++++++++++-------
 3 files changed, 121 insertions(+), 32 deletions(-)

diff --git a/firmware/rust1/Cargo.lock b/firmware/rust1/Cargo.lock
index 2226689..6dcddbb 100644
--- a/firmware/rust1/Cargo.lock
+++ b/firmware/rust1/Cargo.lock
@@ -616,6 +616,7 @@ dependencies = [
  "fixed",
  "fixed-macro",
  "futures",
+ "heapless",
  "log",
  "lora-phy",
  "lorawan",
diff --git a/firmware/rust1/Cargo.toml b/firmware/rust1/Cargo.toml
index 2548ef4..678eb3e 100644
--- a/firmware/rust1/Cargo.toml
+++ b/firmware/rust1/Cargo.toml
@@ -46,5 +46,7 @@ log = "0.4"
 pio-proc = "0.2"
 pio = "0.2.1"
 
+heapless = "0.7.16"
+
 [profile.release]
 debug = true
diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs
index 42f6588..e461f5e 100644
--- a/firmware/rust1/src/bin/heizung.rs
+++ b/firmware/rust1/src/bin/heizung.rs
@@ -10,12 +10,13 @@ use embassy_rp::gpio::{Input, Level, Output, Pull};
 use embassy_rp::i2c::{self};
 use embassy_rp::interrupt;
 use embedded_hal_async::i2c::I2c;
-use embassy_rp::peripherals::UART0;
-use embassy_rp::uart::{self, Async, UartRx, UartTx, Parity};
+use embassy_rp::peripherals::{UART0, I2C0};
+use embassy_rp::uart::{self, UartRx, Parity};
 use {defmt_rtt as _, panic_probe as _};
+use heapless::String;
 
 #[embassy_executor::task]
-async fn reader(mut rx: UartRx<'static, UART0, Async>) {
+async fn reader(mut rx: UartRx<'static, UART0, uart::Async>) {
     info!("Reading...");
     loop {
         let mut buf = [0; 1];
@@ -26,50 +27,135 @@ async fn reader(mut rx: UartRx<'static, UART0, Async>) {
     }
 }
 
+fn append_hex1<const N : usize>(s : &mut String<N>, num : u8) -> Result<(), ()> {
+    let ch = if num < 10 {
+        ('0' as u8 + num) as char
+    } else {
+        ('a' as u8 + num - 10) as char
+    };
+    s.push(ch)
+}
+
+fn append_hex2<const N : usize>(s : &mut String<N>, num : u8) -> Result<(), ()> {
+    append_hex1(s, num >> 4)?;
+    append_hex1(s, num & 0xf)
+}
+
+#[derive(Copy, Clone, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+enum ScanResult {
+    NAck,
+    Ack,
+    Reserved,
+    Error
+}
+
+fn format_scan_result<const N : usize>(msg : &mut String<N>, scan_result : [ScanResult; 128]) -> Result<(), ()> {
+    const MAX_ADDRESS : u8 = 128;
+    for i in 0..MAX_ADDRESS/16 {
+        let base = i*16;
+        append_hex2(msg, base)?;
+        msg.push_str(":")?;
+        for j in 0..16 {
+            msg.push_str(" ")?;
+            match scan_result[(base+j) as usize] {
+                ScanResult::Ack => append_hex2(msg, base+j)?,
+                ScanResult::NAck => msg.push_str("--")?,
+                ScanResult::Reserved => msg.push_str("rr")?,
+                ScanResult::Error => msg.push_str("!!")?,
+            }
+        }
+        msg.push_str("\n")?;
+    }
+    Ok(())
+}
+
+#[embassy_executor::task]
+async fn i2c_task(mut i2c: i2c::I2c<'static, I2C0, i2c::Async>) {
+    use i2c::Error::*;
+    use i2c::AbortReason::*;
+
+    loop {
+        const MAX_ADDRESS : u8 = 128;
+        let mut scan_result = [ScanResult::Error; MAX_ADDRESS as usize];
+        for i in 0..MAX_ADDRESS {
+            //NOTE It looks like the blocking implementation doesn't accept an empty buffer
+            //     but the async one does. Lucky for us because we need that here!
+            // -> Actually, it doesn't. It just hangs.
+            //info!("I2C testing: {}", i);
+            //FIXME Don't test with buffer size 1 because that will actually write, which can cause all kinds of issues!
+            match i2c.write_async(i.into(), [0; 1]).await {
+                Result::Ok(()) => {
+                    //info!("I2C ok: {}", i);
+                    scan_result[i as usize] = ScanResult::Ack
+                },
+                Result::Err(Abort(NoAcknowledge)) =>
+                    scan_result[i as usize] = ScanResult::NAck,
+                    Result::Err(AddressReserved(_)) =>
+                        scan_result[i as usize] = ScanResult::Reserved,
+                Result::Err(e) => {
+                    scan_result[i as usize] = ScanResult::Error;
+                    warn!("I2C problem: {:?}", e);
+                }
+            }
+        }
+
+        const MSG_LEN : usize = (MAX_ADDRESS as usize)*3 + (MAX_ADDRESS as usize)/16*6;
+        let mut msg = String::<MSG_LEN>::new();
+        match format_scan_result(&mut msg, scan_result) {
+            Ok(()) => info!("I2C scan result:\n{}", msg),
+            Err(()) => info!("I2C scan result: too long for our buffer!"),
+        }
+
+        Timer::after(Duration::from_secs(1)).await;
+    }
+}
 
 #[embassy_executor::main]
 async fn main(spawner: Spawner) {
     let p = embassy_rp::init(Default::default());
+    info!("starting");
 
     // pinout:
-    let mut drive1 = Output::new(p.PIN_0, Level::Low);
-    let mut drive2 = Output::new(p.PIN_1, Level::Low);
+    let mut _drive1 = Output::new(p.PIN_0, Level::Low);
+    let mut _drive2 = Output::new(p.PIN_1, Level::Low);
     //let LED_W = p.PIN_2;
-    let ws2812_display = p.PIN_2;
+    let _ws2812_display = p.PIN_2;
     //let LED_Y = p.PIN_3;
-    let mut en_measure_current = Output::new(p.PIN_3, Level::Low);
+    let mut _en_measure_current = Output::new(p.PIN_3, Level::Low);
     let mut led_b = Output::new(p.PIN_4, Level::Low);
-    let mut led_g = Output::new(p.PIN_5, Level::Low);
-    let mut led_r = Output::new(p.PIN_6, Level::Low);
-    let matrix_in1 = Input::new(p.PIN_7, Pull::Up);
-    let sbu1 = p.PIN_8;
-    let sbu2 = p.PIN_9;
-    let vbus_det = p.PIN_10;
-    let button_boot2 = Input::new(p.PIN_11, Pull::Up);
+    let mut _led_g = Output::new(p.PIN_5, Level::Low);
+    let mut _led_r = Output::new(p.PIN_6, Level::Low);
+    let _matrix_in1 = Input::new(p.PIN_7, Pull::Up);
+    let _sbu1 = p.PIN_8;
+    let _sbu2 = p.PIN_9;
+    let _vbus_det = p.PIN_10;
+    let _button_boot2 = Input::new(p.PIN_11, Pull::Up);
     let sda = p.PIN_12;
     let scl = p.PIN_13;
-    let ws2811_red_in = Input::new(p.PIN_14, Pull::None);
-    let tx_en = p.PIN_15;
-    let tx = p.PIN_16;
+    let _ws2811_red_in = Input::new(p.PIN_14, Pull::None);
+    let _tx_en = p.PIN_15;
+    let _tx = p.PIN_16;
     let rx = p.PIN_17;
-    let matrix_in2 = Input::new(p.PIN_18, Pull::Up);
-    let reed3 = Input::new(p.PIN_19, Pull::Up);
-    let reed4 = Input::new(p.PIN_20, Pull::Up);
-    let reed2 = Input::new(p.PIN_21, Pull::Up);
-    let reed1 = Input::new(p.PIN_22, Pull::Up);
-    let matrix_in3 = Input::new(p.PIN_23, Pull::Up);
-    let digout1 = p.PIN_24;
-    let digout2 = p.PIN_25;
-    let analog_in1 = p.PIN_26;
-    let current2 = p.PIN_27;
-    let current1 = p.PIN_28;
-    let measure_vcc = p.PIN_29;
-
-    info!("set up i2c ");
+    let _matrix_in2 = Input::new(p.PIN_18, Pull::Up);
+    let _reed3 = Input::new(p.PIN_19, Pull::Up);
+    let _reed4 = Input::new(p.PIN_20, Pull::Up);
+    let _reed2 = Input::new(p.PIN_21, Pull::Up);
+    let _reed1 = Input::new(p.PIN_22, Pull::Up);
+    let _matrix_in3 = Input::new(p.PIN_23, Pull::Up);
+    let _digout1 = p.PIN_24;
+    let _digout2 = p.PIN_25;
+    let _analog_in1 = p.PIN_26;
+    let _current2 = p.PIN_27;
+    let _current1 = p.PIN_28;
+    let _measure_vcc = p.PIN_29;
+
+    info!("set up i2c");
     let i2c_irq = interrupt::take!(I2C0_IRQ);
     let mut i2c_config = i2c::Config::default();
     i2c_config.frequency = 400_000;
-    let mut i2c = i2c::I2c::new_async(p.I2C0, scl, sda, i2c_irq, i2c_config);
+    let i2c = i2c::I2c::new_async(p.I2C0, scl, sda, i2c_irq, i2c_config);
+    unwrap!(spawner.spawn(i2c_task(i2c)));
 
     // use 19200 baud in 8E1 mode - not great but Modbus default
     let mut uart_config = uart::Config::default();
-- 
GitLab