cleanups
This commit is contained in:
		
							
								
								
									
										14
									
								
								Software/Shared/canapi/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Software/Shared/canapi/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "canapi"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[lib]
 | 
			
		||||
name = "canapi"
 | 
			
		||||
path = "src/lib.rs"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
default = []
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
bincode = { version = "2.0.1", default-features = false, features = ["derive"] }
 | 
			
		||||
							
								
								
									
										138
									
								
								Software/Shared/canapi/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								Software/Shared/canapi/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
//! CAN bus API shared crate for PlantCtrl sensors and controller.
 | 
			
		||||
//! Addressing and messages are defined here to be reused by all bus participants.
 | 
			
		||||
 | 
			
		||||
use bincode::{Decode, Encode};
 | 
			
		||||
 | 
			
		||||
/// Total plants supported by addressing (0..=15)
 | 
			
		||||
pub const MAX_PLANTS: u8 = 16;
 | 
			
		||||
 | 
			
		||||
/// Sensors per plant: 0..=1 => A/B
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum SensorSlot {
 | 
			
		||||
    A = 0,
 | 
			
		||||
    B = 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SensorSlot {
 | 
			
		||||
    pub const fn from_index(idx: u8) -> Option<Self> {
 | 
			
		||||
        match idx {
 | 
			
		||||
            0 => Some(SensorSlot::A),
 | 
			
		||||
            1 => Some(SensorSlot::B),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Legacy sensor base address kept for compatibility with existing code.
 | 
			
		||||
/// Each plant uses SENSOR_BASE_ADDRESS + plant_index (0..PLANT_COUNT-1).
 | 
			
		||||
/// 11-bit standard ID space, safe range.
 | 
			
		||||
pub const SENSOR_BASE_ADDRESS: u16 = 1000;
 | 
			
		||||
 | 
			
		||||
/// Typed topics within the SENSOR_BASE space.
 | 
			
		||||
/// Additional offsets allow distinct message semantics while keeping plant-indexed layout.
 | 
			
		||||
pub mod id {
 | 
			
		||||
    use crate::{SensorSlot, MAX_PLANTS, SENSOR_BASE_ADDRESS};
 | 
			
		||||
 | 
			
		||||
    /// Number of plants addressable per sensor slot group
 | 
			
		||||
    pub const PLANTS_PER_GROUP: u16 = MAX_PLANTS as u16; // 16
 | 
			
		||||
    /// Offset applied for SensorSlot::B within a message group
 | 
			
		||||
    pub const B_OFFSET: u16 = PLANTS_PER_GROUP; // 16
 | 
			
		||||
 | 
			
		||||
    // Message group base offsets relative to SENSOR_BASE_ADDRESS
 | 
			
		||||
    pub const MOISTURE_DATA_OFFSET: u16 = 0; // periodic data from sensor (sensor -> controller)
 | 
			
		||||
    pub const IDENTIFY_CMD_OFFSET: u16 = 32; // identify LED command (controller -> sensor)
 | 
			
		||||
 | 
			
		||||
    // Convenience constants for per-slot base offsets
 | 
			
		||||
    pub const IDENTIFY_CMD_OFFSET_A: u16 = IDENTIFY_CMD_OFFSET + 0;
 | 
			
		||||
    pub const IDENTIFY_CMD_OFFSET_B: u16 = IDENTIFY_CMD_OFFSET + B_OFFSET;
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn plant_id(message_type_offset: u16, sensor: SensorSlot, plant: u16) -> u16 {
 | 
			
		||||
        match sensor {
 | 
			
		||||
            SensorSlot::A => SENSOR_BASE_ADDRESS + message_type_offset + plant,
 | 
			
		||||
            SensorSlot::B => SENSOR_BASE_ADDRESS + message_type_offset + B_OFFSET + plant,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Kinds of message spaces recognized by the addressing scheme.
 | 
			
		||||
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
    pub enum MessageKind {
 | 
			
		||||
        MoistureData,  // sensor -> controller
 | 
			
		||||
        IdentifyCmd,   // controller -> sensor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Try to classify a received 11-bit standard ID into a known message kind and extract plant and sensor slot.
 | 
			
		||||
    /// Returns (kind, plant, slot) on success.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn classify(id: u16) -> Option<(MessageKind, u8, SensorSlot)> {
 | 
			
		||||
        // Ensure the ID is within our base space
 | 
			
		||||
        if id < SENSOR_BASE_ADDRESS {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        let rel = id - SENSOR_BASE_ADDRESS;
 | 
			
		||||
 | 
			
		||||
        // Helper: decode within a given group offset
 | 
			
		||||
        const fn decode_in_group(rel: u16, group_base: u16) -> Option<(u8, SensorSlot)> {
 | 
			
		||||
            if rel < group_base { return None; }
 | 
			
		||||
            let inner = rel - group_base;
 | 
			
		||||
            if inner < PLANTS_PER_GROUP { // A slot
 | 
			
		||||
                Some((inner as u8, SensorSlot::A))
 | 
			
		||||
            } else if inner >= B_OFFSET && inner < B_OFFSET + PLANTS_PER_GROUP { // B slot
 | 
			
		||||
                Some(((inner - B_OFFSET) as u8, SensorSlot::B))
 | 
			
		||||
            } else {
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check known groups in order
 | 
			
		||||
        if let Some((plant, slot)) = decode_in_group(rel, MOISTURE_DATA_OFFSET) {
 | 
			
		||||
            return Some((MessageKind::MoistureData, plant, slot));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some((plant, slot)) = decode_in_group(rel, IDENTIFY_CMD_OFFSET) {
 | 
			
		||||
            return Some((MessageKind::IdentifyCmd, plant, slot));
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns Some((plant, slot)) regardless of message kind, if the id falls into any known group; otherwise None.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn extract_plant_slot(id: u16) -> Option<(u8, SensorSlot)> {
 | 
			
		||||
        match classify(id) {
 | 
			
		||||
            Some((_kind, plant, slot)) => Some((plant, slot)),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if an id corresponds exactly to the given message kind, plant and slot.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn is_identify_for(id: u16, plant: u8, slot: SensorSlot) -> bool {
 | 
			
		||||
        id == plant_id(IDENTIFY_CMD_OFFSET, slot, plant as u16)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn is_moisture_data_for(id: u16, plant: u8, slot: SensorSlot) -> bool {
 | 
			
		||||
        id == plant_id(MOISTURE_DATA_OFFSET, slot, plant as u16)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Periodic moisture data sent by sensors.
 | 
			
		||||
/// Fits into 5 bytes with bincode-v2 (no varint): u8 + u8 + u16 = 4, alignment may keep 4.
 | 
			
		||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
 | 
			
		||||
pub struct MoistureData {
 | 
			
		||||
    pub plant: u8,         // 0..MAX_PLANTS-1
 | 
			
		||||
    pub sensor: SensorSlot, // A/B
 | 
			
		||||
    pub hz: u16,           // measured frequency of moisture sensor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Request a sensor to report immediately (controller -> sensor).
 | 
			
		||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
 | 
			
		||||
pub struct MoistureRequest {
 | 
			
		||||
    pub plant: u8,
 | 
			
		||||
    pub sensor: SensorSlot, // target sensor (sensor filters by this)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Control a sensor's identify LED, if received by sensor, blink for a few seconds
 | 
			
		||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
 | 
			
		||||
pub struct IdentifyLed {}
 | 
			
		||||
		Reference in New Issue
	
	Block a user