config for sensor
This commit is contained in:
		@@ -51,3 +51,11 @@ strip = true   # symbols are not flashed to the microcontroller, so don't strip
 | 
			
		||||
lto = true
 | 
			
		||||
debug = false
 | 
			
		||||
opt-level = "z" # Optimize for size.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[bin]]
 | 
			
		||||
name = "sensor"
 | 
			
		||||
path = "src/main.rs"
 | 
			
		||||
test = false
 | 
			
		||||
bench = false
 | 
			
		||||
doctest = false
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,12 @@
 | 
			
		||||
extern crate alloc;
 | 
			
		||||
 | 
			
		||||
use crate::hal::peripherals::CAN1;
 | 
			
		||||
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
 | 
			
		||||
use canapi::id::{plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
 | 
			
		||||
use canapi::SensorSlot;
 | 
			
		||||
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
 | 
			
		||||
use ch32_hal::can;
 | 
			
		||||
use ch32_hal::can::{Can, CanFifo, CanFilter, CanFrame, CanMode};
 | 
			
		||||
use ch32_hal::gpio::{Level, Output, Speed};
 | 
			
		||||
use ch32_hal::gpio::{Flex, Level, Output, Pull, Speed};
 | 
			
		||||
use ch32_hal::mode::NonBlocking;
 | 
			
		||||
use ch32_hal::peripherals::USBD;
 | 
			
		||||
use core::fmt::Write as _;
 | 
			
		||||
@@ -23,6 +23,9 @@ use embedded_can::{Id, StandardId};
 | 
			
		||||
use hal::bind_interrupts;
 | 
			
		||||
use hal::usbd::Driver;
 | 
			
		||||
use {ch32_hal as hal, panic_halt as _};
 | 
			
		||||
use embedded_alloc::LlffHeap as Heap;
 | 
			
		||||
use embedded_can::nb::Can as nb_can;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
macro_rules! mk_static {
 | 
			
		||||
    ($t:ty,$val:expr) => {{
 | 
			
		||||
@@ -37,20 +40,14 @@ bind_interrupts!(struct Irqs {
 | 
			
		||||
    USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
use embedded_alloc::LlffHeap as Heap;
 | 
			
		||||
use embedded_can::nb::Can as nb_can;
 | 
			
		||||
use log::log;
 | 
			
		||||
use qingke::riscv::asm::delay;
 | 
			
		||||
 | 
			
		||||
#[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) {
 | 
			
		||||
    ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    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());
 | 
			
		||||
@@ -97,9 +94,53 @@ async fn main(spawner: Spawner) {
 | 
			
		||||
    // Create GPIO for 555 Q output (PB0)
 | 
			
		||||
    let q_out = Output::new(p.PB0, Level::Low, Speed::Low);
 | 
			
		||||
    // Built-in LED on PB2 mirrors Q state
 | 
			
		||||
    let led = Output::new(p.PB2, Level::Low, Speed::Low);
 | 
			
		||||
    let mut info = Output::new(p.PB2, Level::Low, Speed::Low);
 | 
			
		||||
 | 
			
		||||
    let info = Output::new(p.PA3, Level::Low, Speed::Low);
 | 
			
		||||
    // Read configuration switches on PB3..PB7 at startup with floating detection
 | 
			
		||||
    // PB3: Sensor A/B selector (Low=A, High=B)
 | 
			
		||||
    // PB4..PB7: address bits (1,2,4,8)
 | 
			
		||||
    let mut pb3 = Flex::new(p.PB3);
 | 
			
		||||
    let mut pb4 = Flex::new(p.PB4);
 | 
			
		||||
    let mut pb5 = Flex::new(p.PB5);
 | 
			
		||||
    let mut pb6 = Flex::new(p.PB6);
 | 
			
		||||
    let mut pb7 = Flex::new(p.PB7);
 | 
			
		||||
 | 
			
		||||
    // Validate all config pins; if any is floating, stay in an error loop until fixed
 | 
			
		||||
    // Try read PB3..PB7
 | 
			
		||||
    let res_pb3 = detect_stable_pin(&mut pb3).await;
 | 
			
		||||
    let res_pb4 = detect_stable_pin(&mut pb4).await;
 | 
			
		||||
    let res_pb5 = detect_stable_pin(&mut pb5).await;
 | 
			
		||||
    let res_pb6 = detect_stable_pin(&mut pb6).await;
 | 
			
		||||
    let res_pb7 = detect_stable_pin(&mut pb7).await;
 | 
			
		||||
 | 
			
		||||
    if res_pb3.is_none() || res_pb4.is_none() || res_pb5.is_none() || res_pb6.is_none() || res_pb7.is_none() {
 | 
			
		||||
        // At least one floating: report and blink code for the first one found.
 | 
			
		||||
        let mut msg: heapless::String<128> = heapless::String::new();
 | 
			
		||||
        let code = if res_pb4.is_none() { 1 } else if res_pb5.is_none() { 2 } else if res_pb6.is_none() { 3 } else if res_pb7.is_none() { 4 } else { 5 }; // PB3 -> 5
 | 
			
		||||
        let which = match code { 1 => "PB4", 2 => "PB5", 3 => "PB6", 4 => "PB7", _ => "PB3 (A/B)" };
 | 
			
		||||
        let _ = core::fmt::Write::write_fmt(&mut msg, format_args!("Config pin floating detected on {} -> blinking code {}. Fix jumpers.\r\n", which, code));
 | 
			
		||||
        log(msg);
 | 
			
		||||
        blink_error(&mut info, code).await;
 | 
			
		||||
    };
 | 
			
		||||
    let slot = if res_pb3.unwrap() { SensorSlot::B } else { SensorSlot::A };
 | 
			
		||||
    let mut addr: u8 = 0;
 | 
			
		||||
    if res_pb4.unwrap() { addr |= 1; }
 | 
			
		||||
    if res_pb5.unwrap() { addr |= 2; }
 | 
			
		||||
    if res_pb6.unwrap() { addr |= 4; }
 | 
			
		||||
    if res_pb7.unwrap() { addr |= 8; }
 | 
			
		||||
 | 
			
		||||
    // Log startup configuration and derived CAN IDs
 | 
			
		||||
    let moisture_id = plant_id(MOISTURE_DATA_OFFSET, slot, addr as u16);
 | 
			
		||||
    let identify_id = plant_id(IDENTIFY_CMD_OFFSET, slot, addr as u16);
 | 
			
		||||
    {
 | 
			
		||||
        let mut msg: heapless::String<128> = heapless::String::new();
 | 
			
		||||
        let slot_chr = match slot { SensorSlot::A => 'a', SensorSlot::B => 'b' };
 | 
			
		||||
        let _ = core::fmt::Write::write_fmt(&mut msg, format_args!(
 | 
			
		||||
            "Startup: slot={} addr={} moisture_id=0x{:03X} identity_id=0x{:03X}\r\n",
 | 
			
		||||
            slot_chr, addr, moisture_id, identify_id
 | 
			
		||||
        ));
 | 
			
		||||
        log(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
 | 
			
		||||
    let adc = Adc::new(p.ADC1, Default::default());
 | 
			
		||||
@@ -120,36 +161,59 @@ async fn main(spawner: Spawner) {
 | 
			
		||||
    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(worker(q_out, info, adc, ain, can, StandardId::new(moisture_id).unwrap(), StandardId::new(identify_id).unwrap())).unwrap();
 | 
			
		||||
 | 
			
		||||
    // Prevent main from exiting
 | 
			
		||||
    core::future::pending::<()>().await;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Helper closure: detect stable pin by comparing readings under Pull::Down and Pull::Up
 | 
			
		||||
async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
 | 
			
		||||
    pin.set_as_input(Pull::Down);
 | 
			
		||||
    Timer::after_millis(2).await;
 | 
			
		||||
    let low_read = pin.is_high();
 | 
			
		||||
    pin.set_as_input(Pull::Up);
 | 
			
		||||
    Timer::after_millis(2).await;
 | 
			
		||||
    let high_read = pin.is_high();
 | 
			
		||||
    if low_read == high_read { Some(high_read) } else { None }
 | 
			
		||||
}
 | 
			
		||||
async fn blink_error(mut info_led: &mut Output<'static>, code: u8) -> !{
 | 
			
		||||
    loop {
 | 
			
		||||
        // code: 1-4 for PB4..PB7, 5 for PB3 (A/B)
 | 
			
		||||
        for _ in 0..code {
 | 
			
		||||
            info_led.set_high();
 | 
			
		||||
            Timer::after_millis(200).await;
 | 
			
		||||
            info_led.set_low();
 | 
			
		||||
            Timer::after_millis(200).await;
 | 
			
		||||
        }
 | 
			
		||||
        // Pause between sequences
 | 
			
		||||
        Timer::after_secs(2).await;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[task]
 | 
			
		||||
async fn worker(
 | 
			
		||||
    mut q: Output<'static>,
 | 
			
		||||
    mut led: Output<'static>,
 | 
			
		||||
    mut info: Output<'static>,
 | 
			
		||||
    mut adc: Adc<'static, hal::peripherals::ADC1>,
 | 
			
		||||
    mut ain: hal::peripherals::PA1,
 | 
			
		||||
    mut can: Can<'static, CAN1, NonBlocking>,
 | 
			
		||||
    moisture_id: StandardId,
 | 
			
		||||
    identify_id: StandardId
 | 
			
		||||
) {
 | 
			
		||||
    // 555 emulation state: Q initially Low
 | 
			
		||||
    let mut q_high = false;
 | 
			
		||||
    let low_th: u16 = (ADC_MAX as u16) / 3; // ~1/3 Vref
 | 
			
		||||
    let high_th: u16 = ((ADC_MAX as u32 * 2) / 3) as u16; // ~2/3 Vref
 | 
			
		||||
 | 
			
		||||
    let moisture_address =
 | 
			
		||||
        StandardId::new(plant_id(MOISTURE_DATA_OFFSET, SensorSlot::A, 0)).unwrap();
 | 
			
		||||
    let identity_address =
 | 
			
		||||
        StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, SensorSlot::A, 0)).unwrap();
 | 
			
		||||
 | 
			
		||||
    let mut filter = CanFilter::new_id_list();
 | 
			
		||||
 | 
			
		||||
    filter
 | 
			
		||||
        .get(0)
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .set(identity_address.into(), Default::default());
 | 
			
		||||
        .set(identify_id.into(), Default::default());
 | 
			
		||||
 | 
			
		||||
    can.add_filter(filter);
 | 
			
		||||
    //can.add_filter(CanFilter::accept_all());
 | 
			
		||||
@@ -206,7 +270,7 @@ async fn worker(
 | 
			
		||||
        );
 | 
			
		||||
        log(msg);
 | 
			
		||||
 | 
			
		||||
        let mut moisture = CanFrame::new(moisture_address, &[freq_hz as u8]).unwrap();
 | 
			
		||||
        let mut moisture = CanFrame::new(moisture_id, &[freq_hz as u8]).unwrap();
 | 
			
		||||
        match can.transmit(&mut moisture) {
 | 
			
		||||
            Ok(..) => {
 | 
			
		||||
                let mut msg: heapless::String<128> = heapless::String::new();
 | 
			
		||||
@@ -230,15 +294,15 @@ async fn worker(
 | 
			
		||||
                            &mut msg,
 | 
			
		||||
                            "Received from canbus: {:?} ident is {:?} \r\n",
 | 
			
		||||
                            s_frame.as_raw(),
 | 
			
		||||
                            identity_address.as_raw()
 | 
			
		||||
                            identify_id.as_raw()
 | 
			
		||||
                        );
 | 
			
		||||
                        log(msg);
 | 
			
		||||
                        if s_frame.as_raw() == identity_address.as_raw() {
 | 
			
		||||
                        if s_frame.as_raw() == identify_id.as_raw() {
 | 
			
		||||
                            for _ in 0..10 {
 | 
			
		||||
                                Timer::after_millis(250).await;
 | 
			
		||||
                                led.toggle();
 | 
			
		||||
                                info.toggle();
 | 
			
		||||
                            }
 | 
			
		||||
                            led.set_low();
 | 
			
		||||
                            info.set_low();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Id::Extended(_) => {}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user