sensor sweep tester
This commit is contained in:
		| @@ -1,10 +1,9 @@ | ||||
| use crate::hal::Sensor; | ||||
| use bincode::{Decode, Encode}; | ||||
|  | ||||
| pub(crate) const SENSOR_BASE_ADDRESS: u16 = 1000; | ||||
| #[derive(Debug, Clone, Copy, Encode, Decode)] | ||||
| pub(crate) struct RequestMoisture { | ||||
|     pub(crate) sensor: Sensor, | ||||
| } | ||||
| pub(crate) struct AutoDetectRequest {} | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, Encode, Decode)] | ||||
| pub(crate) struct ResponseMoisture { | ||||
| @@ -12,9 +11,3 @@ pub(crate) struct ResponseMoisture { | ||||
|     pub sensor: Sensor, | ||||
|     pub hz: u32, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, Encode, Decode, PartialEq, Eq)] | ||||
| pub(crate) enum Sensor { | ||||
|     A, | ||||
|     B, | ||||
| } | ||||
|   | ||||
| @@ -56,6 +56,7 @@ use alloc::boxed::Box; | ||||
| use alloc::format; | ||||
| use alloc::sync::Arc; | ||||
| use async_trait::async_trait; | ||||
| use bincode::{Decode, Encode}; | ||||
| use bq34z100::Bq34z100g1Driver; | ||||
| use chrono::{DateTime, FixedOffset, Utc}; | ||||
| use core::cell::RefCell; | ||||
| @@ -117,7 +118,7 @@ pub static I2C_DRIVER: OnceLock< | ||||
|     embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<I2c<Blocking>>>, | ||||
| > = OnceLock::new(); | ||||
|  | ||||
| #[derive(Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq, Clone, Copy, Encode, Decode)] | ||||
| pub enum Sensor { | ||||
|     A, | ||||
|     B, | ||||
| @@ -152,6 +153,11 @@ pub trait BoardInteraction<'a> { | ||||
|     async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError>; | ||||
|     async fn get_mptt_current(&mut self) -> Result<Current, FatError>; | ||||
|  | ||||
|     // Return JSON string with autodetected sensors per plant. Default: not supported. | ||||
|     async fn detect_sensors(&mut self) -> Result<alloc::string::String, FatError> { | ||||
|         bail!("Autodetection is only available on v4 HAL with CAN bus"); | ||||
|     } | ||||
|  | ||||
|     async fn progress(&mut self, counter: u32) { | ||||
|         // Indicate progress is active to suppress default wait_infinity blinking | ||||
|         crate::hal::PROGRESS_ACTIVE.store(true, core::sync::atomic::Ordering::Relaxed); | ||||
|   | ||||
| @@ -201,8 +201,8 @@ pub(crate) async fn create_v4( | ||||
|             log::info!("Can bus mode "); | ||||
|             let twai_config = Some(twai::TwaiConfiguration::new( | ||||
|                 peripherals.twai, | ||||
|                 peripherals.gpio0, | ||||
|                 peripherals.gpio2, | ||||
|                 peripherals.gpio0, | ||||
|                 TWAI_BAUDRATE, | ||||
|                 TwaiMode::Normal, | ||||
|             )); | ||||
| @@ -458,4 +458,24 @@ impl<'a> BoardInteraction<'a> for V4<'a> { | ||||
|     async fn get_mptt_current(&mut self) -> Result<Current, FatError> { | ||||
|         self.charger.get_mppt_current() | ||||
|     } | ||||
|  | ||||
|     async fn detect_sensors(&mut self) -> Result<alloc::string::String, FatError> { | ||||
|         // Delegate to sensor autodetect and build JSON | ||||
|         use alloc::string::ToString; | ||||
|         let detected = self.sensor.autodetect().await?; | ||||
|         // Build JSON manually to avoid exposing internal types | ||||
|         let mut s = alloc::string::String::from("{\"plants\":["); | ||||
|         for (i, (a, b)) in detected.iter().enumerate() { | ||||
|             if i != 0 { | ||||
|                 s.push(','); | ||||
|             } | ||||
|             s.push_str("{\"a\":"); | ||||
|             s.push_str(if *a { "true" } else { "false" }); | ||||
|             s.push_str(",\"b\":"); | ||||
|             s.push_str(if *b { "true" } else { "false" }); | ||||
|             s.push('}'); | ||||
|         } | ||||
|         s.push_str("]}"); | ||||
|         Ok(s) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,15 +9,17 @@ use alloc::string::ToString; | ||||
| use async_trait::async_trait; | ||||
| use bincode::config; | ||||
| use bincode::error::DecodeError; | ||||
| use can_api::RequestMoisture; | ||||
| use core::mem; | ||||
| use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_time::{Instant, Timer}; | ||||
| use embassy_sync::mutex::Mutex; | ||||
| use embassy_time::{Instant, Timer, WithTimeout}; | ||||
| use embedded_can::nb::Can; | ||||
| use embedded_can::Frame; | ||||
| use esp_hal::gpio::Output; | ||||
| use esp_hal::i2c::master::I2c; | ||||
| use esp_hal::pcnt::unit::Unit; | ||||
| use esp_hal::twai::{EspTwaiFrame, StandardId, Twai, TwaiConfiguration}; | ||||
| use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration}; | ||||
| use esp_hal::Blocking; | ||||
| use log::info; | ||||
| use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||
| @@ -140,9 +142,22 @@ impl SensorInteraction for SensorImpl { | ||||
|                 let config = twai_config.take().expect("twai config not set"); | ||||
|                 let mut twai = config.start(); | ||||
|  | ||||
|                 loop { | ||||
|                     let rec = twai.receive(); | ||||
|                     match rec { | ||||
|                         Ok(_) => {} | ||||
|                         Err(err) => { | ||||
|                             info!("Error receiving CAN message: {:?}", err); | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 Timer::after_millis(10).await; | ||||
|                 let can = Self::inner_can(plant, sensor, &mut twai).await; | ||||
|  | ||||
|                 can_power.set_low(); | ||||
|  | ||||
|                 let config = twai.stop(); | ||||
|                 twai_config.replace(config); | ||||
|  | ||||
| @@ -154,21 +169,104 @@ impl SensorInteraction for SensorImpl { | ||||
| } | ||||
|  | ||||
| impl SensorImpl { | ||||
|     pub async fn autodetect(&mut self) -> FatResult<[(bool, bool); crate::hal::PLANT_COUNT]> { | ||||
|         match self { | ||||
|             SensorImpl::PulseCounter { .. } => { | ||||
|                 bail!("Only CAN bus implementation supports autodetection") | ||||
|             } | ||||
|             SensorImpl::CanBus { | ||||
|                 twai_config, | ||||
|                 can_power, | ||||
|             } => { | ||||
|                 // Power on CAN transceiver and start controller | ||||
|                 can_power.set_high(); | ||||
|                 let config = twai_config.take().expect("twai config not set"); | ||||
|                 let mut twai = config.start(); | ||||
|  | ||||
|                 // Give CAN some time to stabilize | ||||
|                 Timer::after_millis(10).await; | ||||
|  | ||||
|                 // Send a few test messages per potential sensor node | ||||
|                 for plant in 0..crate::hal::PLANT_COUNT { | ||||
|                     for sensor in [Sensor::A, Sensor::B] { | ||||
|                         // Reuse CAN addressing scheme from moisture request | ||||
|                         let can_buffer = [0_u8; 8]; | ||||
|                         let cfg = config::standard(); | ||||
|                         if let Some(address) = | ||||
|                             StandardId::new(can_api::SENSOR_BASE_ADDRESS + plant as u16) | ||||
|                         { | ||||
|                             if let Some(frame) = EspTwaiFrame::new(address, &can_buffer) { | ||||
|                                 // Try a few times; we intentionally ignore rx here and rely on stub logic | ||||
|                                 let resu = twai.transmit(&frame); | ||||
|                                 match resu { | ||||
|                                     Ok(_) => { | ||||
|                                         info!( | ||||
|                                             "Sent test message to plant {} sensor {:?}", | ||||
|                                             plant, sensor | ||||
|                                         ); | ||||
|                                     } | ||||
|                                     Err(err) => { | ||||
|                                         info!("Error sending test message to plant {} sensor {:?}: {:?}", plant, sensor, err); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 info!("Error building CAN frame"); | ||||
|                             } | ||||
|                         } else { | ||||
|                             info!("Error creating address for sensor"); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 loop { | ||||
|                     let rec = twai | ||||
|                         .receive() | ||||
|                         .with_timeout(embassy_time::Duration::from_millis(100)) | ||||
|                         .await; | ||||
|                     match rec { | ||||
|                         Ok(msg) => match msg { | ||||
|                             Ok(or) => { | ||||
|                                 info!("Received CAN message: {:?}", or); | ||||
|                             } | ||||
|                             Err(err) => { | ||||
|                                 info!("Error receiving CAN message: {:?}", err); | ||||
|                                 break; | ||||
|                             } | ||||
|                         }, | ||||
|                         Err(err) => { | ||||
|                             info!("Error receiving CAN message: {:?}", err); | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // Wait for acknowledgements on the bus (stub: just wait 5 seconds) | ||||
|                 Timer::after_millis(5_000).await; | ||||
|                 // Stop CAN and power down | ||||
|                 can_power.set_low(); | ||||
|                 twai_config.replace(config); | ||||
|  | ||||
|                 // Stub: return no detections yet | ||||
|                 let mut result = [(false, false); crate::hal::PLANT_COUNT]; | ||||
|                 Ok(result) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn inner_can( | ||||
|         plant: usize, | ||||
|         sensor: Sensor, | ||||
|         twai: &mut Twai<'static, Blocking>, | ||||
|     ) -> FatResult<f32> { | ||||
|         let can_sensor: can_api::Sensor = sensor.into(); | ||||
|         let request = RequestMoisture { sensor: can_sensor }; | ||||
|         let mut can_buffer = [0_u8; 8]; | ||||
|         let can_sensor: Sensor = sensor.into(); | ||||
|         //let request = RequestMoisture { sensor: can_sensor }; | ||||
|         let can_buffer = [0_u8; 8]; | ||||
|         let config = config::standard(); | ||||
|         let encoded = bincode::encode_into_slice(&request, &mut can_buffer, config)?; | ||||
|         //let encoded = bincode::encode_into_slice(&request, &mut can_buffer, config)?; | ||||
|  | ||||
|         let address = StandardId::new(can_api::SENSOR_BASE_ADDRESS + plant as u16) | ||||
|             .context(">> Could not create address for sensor! (plant: {}) <<")?; | ||||
|         let request = EspTwaiFrame::new(address, &can_buffer[0..encoded]) | ||||
|             .context("Error building CAN frame")?; | ||||
|         let request = | ||||
|             EspTwaiFrame::new(address, &can_buffer[0..8]).context("Error building CAN frame")?; | ||||
|         twai.transmit(&request)?; | ||||
|  | ||||
|         let timeout = Instant::now() | ||||
| @@ -214,12 +312,3 @@ impl SensorImpl { | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Sensor> for can_api::Sensor { | ||||
|     fn from(value: Sensor) -> Self { | ||||
|         match value { | ||||
|             Sensor::A => can_api::Sensor::A, | ||||
|             Sensor::B => can_api::Sensor::B, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ use crate::webserver::get_log::get_log; | ||||
| use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index}; | ||||
| use crate::webserver::ota::ota_operations; | ||||
| use crate::webserver::post_json::{ | ||||
|     board_test, night_lamp_test, pump_test, set_config, wifi_scan, write_time, | ||||
|     board_test, night_lamp_test, pump_test, set_config, wifi_scan, write_time, detect_sensors, | ||||
| }; | ||||
| use crate::{bail, BOARD_ACCESS}; | ||||
| use alloc::borrow::ToOwned; | ||||
| @@ -151,6 +151,7 @@ impl Handler for HTTPRequestRouter { | ||||
|                         "/pumptest" => Some(pump_test(conn).await), | ||||
|                         "/lamptest" => Some(night_lamp_test(conn).await), | ||||
|                         "/boardtest" => Some(board_test().await), | ||||
|                         "/detect_sensors" => Some(detect_sensors().await), | ||||
|                         "/reboot" => { | ||||
|                             let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|                             board.board_hal.get_esp().set_restart_to_conf(true); | ||||
|   | ||||
| @@ -50,6 +50,12 @@ pub(crate) async fn board_test() -> FatResult<Option<String>> { | ||||
|     Ok(None) | ||||
| } | ||||
|  | ||||
| pub(crate) async fn detect_sensors() -> FatResult<Option<String>> { | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|     let json = board.board_hal.detect_sensors().await?; | ||||
|     Ok(Some(json)) | ||||
| } | ||||
|  | ||||
| pub(crate) async fn pump_test<T, const N: usize>( | ||||
|     request: &mut Connection<'_, T, N>, | ||||
| ) -> FatResult<Option<String>> | ||||
|   | ||||
| @@ -173,6 +173,15 @@ export interface BatteryState { | ||||
|     state_of_health: string | ||||
| } | ||||
|  | ||||
| export interface DetectionPlant { | ||||
|     a: boolean, | ||||
|     b: boolean | ||||
| } | ||||
|  | ||||
| export interface DetectionResult { | ||||
|     plants: DetectionPlant[] | ||||
| } | ||||
|  | ||||
| export interface TankInfo { | ||||
|     /// is there enough water in the tank | ||||
|     enough_water: boolean, | ||||
|   | ||||
| @@ -163,6 +163,7 @@ | ||||
|  | ||||
|   <h3>Plants:</h3> | ||||
|   <button id="measure_moisture">Measure Moisture</button> | ||||
|   <button id="detect_sensors" style="display:none">Detect/Test Sensors</button> | ||||
|   <div id="plants" class="plantlist"></div> | ||||
|  | ||||
|   <div class="flexcontainer-rev"> | ||||
|   | ||||
| @@ -358,6 +358,36 @@ export class Controller { | ||||
|             ) | ||||
|     } | ||||
|  | ||||
|     async detectSensors() { | ||||
|         let counter = 0 | ||||
|         let limit = 5 | ||||
|         controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s") | ||||
|  | ||||
|         let timerId: string | number | NodeJS.Timeout | undefined | ||||
|  | ||||
|         function updateProgress() { | ||||
|             counter++; | ||||
|             controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s") | ||||
|             timerId = setTimeout(updateProgress, 1000); | ||||
|         } | ||||
|  | ||||
|         timerId = setTimeout(updateProgress, 1000); | ||||
|  | ||||
|         fetch(PUBLIC_URL + "/detect_sensors", { method: "POST" }) | ||||
|             .then(response => response.json()) | ||||
|             .then(json => { | ||||
|                 clearTimeout(timerId); | ||||
|                 controller.progressview.removeProgress("detect_sensors"); | ||||
|                 const pretty = JSON.stringify(json); | ||||
|                 toast.info("Detection result: " + pretty); | ||||
|             }) | ||||
|             .catch(error => { | ||||
|                 clearTimeout(timerId); | ||||
|                 controller.progressview.removeProgress("detect_sensors"); | ||||
|                 toast.error("Autodetect failed: " + error); | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     getConfig(): PlantControllerConfig { | ||||
|         return { | ||||
|             hardware: controller.hardwareView.getConfig(), | ||||
| @@ -405,6 +435,12 @@ export class Controller { | ||||
|     } | ||||
|  | ||||
|     setConfig(current: PlantControllerConfig) { | ||||
|         // Show Detect/Test button only for V4 HAL | ||||
|         if (current.hardware && (current.hardware as any).board === "V4") { | ||||
|             this.detectBtn.style.display = "inline-block"; | ||||
|         } else { | ||||
|             this.detectBtn.style.display = "none"; | ||||
|         } | ||||
|         this.tankView.setConfig(current.tank); | ||||
|         this.networkView.setConfig(current.network); | ||||
|         this.nightLampView.setConfig(current.night_lamp); | ||||
| @@ -500,6 +536,7 @@ export class Controller { | ||||
|     readonly solarView: SolarView; | ||||
|     readonly fileview: FileView; | ||||
|     readonly logView: LogView | ||||
|     readonly detectBtn: HTMLButtonElement | ||||
|  | ||||
|     constructor() { | ||||
|         this.timeView = new TimeView(this) | ||||
| @@ -515,6 +552,8 @@ export class Controller { | ||||
|         this.fileview = new FileView(this) | ||||
|         this.logView = new LogView(this) | ||||
|         this.hardwareView = new HardwareConfigView(this) | ||||
|         this.detectBtn = document.getElementById("detect_sensors") as HTMLButtonElement | ||||
|         this.detectBtn.onclick = () => { controller.detectSensors(); } | ||||
|         this.rebootBtn = document.getElementById("reboot") as HTMLButtonElement | ||||
|         this.rebootBtn.onclick = () => { | ||||
|             controller.reboot(); | ||||
|   | ||||
| @@ -26,6 +26,7 @@ embassy-usb = { version = "0.3.0" } | ||||
| embassy-futures = { version = "0.1.0" } | ||||
| embassy-sync = { version = "0.6.0" } | ||||
| embedded-can = "0.4.1" | ||||
| embedded-alloc = { version = "0.6.0", default-features = false, features = ["llff"] } | ||||
|  | ||||
| # This is okay because we should automatically use whatever ch32-hal uses | ||||
| qingke-rt = "*" | ||||
|   | ||||
| @@ -28,12 +28,6 @@ If you need to map a label to code, use the same letter+number as in the silkscr | ||||
| cargo build --release | ||||
| ``` | ||||
|  | ||||
| ## USB CDC Console (optional) | ||||
|  | ||||
| This project includes an optional software USB CDC-ACM device stack using embassy-usb. It runs on the CH32V203’s USB device peripheral but implements the protocol fully in software (no built-in USB class firmware is required). | ||||
|  | ||||
| How to enable: | ||||
| - Build with the `usb-cdc` feature: `cargo build --release --features usb-cdc` | ||||
| - Wire the MCU’s USB pins to a USB connector: | ||||
|   - D+ (PA12) | ||||
|   - D− (PA11) | ||||
| @@ -46,12 +40,6 @@ Example: | ||||
| - macOS: `screen /dev/tty.usbmodemXXXX 115200` | ||||
| - Windows: Use PuTTY on the shown COM port. | ||||
|  | ||||
| Notes: | ||||
| - The firmware currently implements an echo console: bytes you type are echoed back. You can extend it to print logs or interact with your application. | ||||
| - If you don’t see a device, ensure D+ (PA12) and D− (PA11) are connected and the cable supports data. | ||||
|  | ||||
| ## Flash | ||||
|  | ||||
| You can flash the built ELF using wchisp (WCH ISP tool): | ||||
|  | ||||
| ``` sh | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #![no_std] | ||||
| #![no_main] | ||||
|  | ||||
| extern crate alloc; | ||||
|  | ||||
| use crate::hal::peripherals::CAN1; | ||||
| use core::fmt::Write as _; | ||||
| @@ -8,7 +8,7 @@ use ch32_hal::gpio::{Level, Output, Speed}; | ||||
| use ch32_hal::adc::{Adc, SampleTime, ADC_MAX}; | ||||
| use ch32_hal::can; | ||||
| use ch32_hal::can::{Can, CanFifo, CanFilter, CanFrame, CanMode}; | ||||
| use ch32_hal::mode::{Blocking, Mode}; | ||||
| use ch32_hal::mode::{Blocking}; | ||||
| use ch32_hal::peripherals::USBD; | ||||
| // use ch32_hal::delay::Delay; | ||||
| use embassy_executor::{Spawner, task}; | ||||
| @@ -18,10 +18,10 @@ use embassy_futures::yield_now; | ||||
| use hal::usbd::{Driver}; | ||||
| use hal::{bind_interrupts}; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::channel::{Channel, TrySendError}; | ||||
| use embassy_time::{Timer, Instant, Duration, Ticker}; | ||||
| use embassy_sync::channel::{Channel}; | ||||
| use embassy_time::{Instant, Duration}; | ||||
| use embedded_can::blocking::Can as bcan; | ||||
| use embedded_can::StandardId; | ||||
| use heapless::String; | ||||
| use {ch32_hal as hal, panic_halt as _}; | ||||
|  | ||||
| macro_rules! mk_static { | ||||
| @@ -37,10 +37,22 @@ bind_interrupts!(struct Irqs { | ||||
|     USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>; | ||||
| }); | ||||
|  | ||||
|  | ||||
| use embedded_alloc::LlffHeap as Heap; | ||||
|  | ||||
| #[global_allocator] | ||||
| static HEAP: Heap = Heap::empty(); | ||||
|  | ||||
|  | ||||
| static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new(); | ||||
|  | ||||
| #[embassy_executor::main(entry = "qingke_rt::entry")] | ||||
| async fn main(spawner: Spawner) { | ||||
|     unsafe { | ||||
|         static mut HEAP_SPACE: [u8; 4096] = [0; 4096]; // 4 KiB heap, adjust as needed | ||||
|         HEAP.init(HEAP_SPACE.as_ptr() as usize, HEAP_SPACE.len()); | ||||
|     } | ||||
|  | ||||
|     let p = hal::init(hal::Config { | ||||
|         rcc: hal::rcc::Config::SYSCLK_FREQ_144MHZ_HSI, | ||||
|         ..Default::default() | ||||
| @@ -105,13 +117,11 @@ async fn main(spawner: Spawner) { | ||||
|     can.add_filter(CanFilter::accept_all()); | ||||
|  | ||||
|  | ||||
|     // Spawn independent tasks using 'static references | ||||
|     unsafe { | ||||
|         spawner.spawn(usb_task(usb)).unwrap(); | ||||
|         spawner.spawn(usb_writer(class)).unwrap(); | ||||
|         // move Q output, LED, ADC and analog input into worker task | ||||
|         spawner.spawn(worker(q_out, led, adc, ain, can)).unwrap(); | ||||
|     } | ||||
|     spawner.spawn(usb_task(usb)).unwrap(); | ||||
|     spawner.spawn(usb_writer(class)).unwrap(); | ||||
|     // move Q output, LED, ADC and analog input into worker task | ||||
|     spawner.spawn(worker(q_out, led, adc, ain, can)).unwrap(); | ||||
|  | ||||
|  | ||||
|  | ||||
|     // Prevent main from exiting | ||||
| @@ -182,6 +192,31 @@ async fn worker( | ||||
|             pulses, freq_hz | ||||
|         ); | ||||
|         log(msg); | ||||
|  | ||||
|         let address = StandardId::new(0x580 | 0x42).unwrap(); | ||||
|         let moisture = CanFrame::new(address, &[freq_hz as u8]).unwrap(); | ||||
|         match bcan::transmit(&mut can, &moisture) { | ||||
|             Ok(..) => { | ||||
|                 let mut msg: heapless::String<128> = heapless::String::new(); | ||||
|                 let _ = write!( | ||||
|                     &mut msg, | ||||
|                     "Send to canbus" | ||||
|                 ); | ||||
|                 log(msg); | ||||
|             } | ||||
|             Err(err) => { | ||||
|  | ||||
|  | ||||
|  | ||||
|                 let mut msg: heapless::String<128> = heapless::String::new(); | ||||
|                 let _ = write!( | ||||
|                     &mut msg, | ||||
|                     "err {}" | ||||
|                     ,err | ||||
|                 ); | ||||
|                 log(msg); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user