Improve documentation for device driver extensibility
Update CLAUDE.md, module docs, and READMEs to reflect current architecture and provide clearer guidance for adding new device implementations. Key changes: - Add comprehensive DeviceDriver trait documentation with examples - Document module structure and factory pattern in devices/mod.rs - Update file path references to reflect crl200s/ organization - Change configuration example from TOML to JSON format - Fix minor documentation inconsistencies across READMEs
This commit is contained in:
@@ -174,23 +174,23 @@ while True:
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit `sangamio.toml`:
|
||||
Edit `hardware.json`:
|
||||
|
||||
```toml
|
||||
[hardware]
|
||||
gd32_port = "/dev/ttyS3" # Motor controller
|
||||
lidar_port = "/dev/ttyS1" # Delta-2D lidar
|
||||
|
||||
[robot]
|
||||
max_ticks_per_sec = 3000.0 # Calibration constant
|
||||
|
||||
[streaming]
|
||||
tcp_pub_address = "0.0.0.0:5555" # Telemetry output
|
||||
tcp_cmd_address = "0.0.0.0:5556" # Command input
|
||||
|
||||
[logging]
|
||||
level = "info"
|
||||
output = "stdout"
|
||||
```json
|
||||
{
|
||||
"device": {
|
||||
"type": "crl200s",
|
||||
"name": "CRL-200S Vacuum Robot",
|
||||
"hardware": {
|
||||
"gd32_port": "/dev/ttyS3",
|
||||
"lidar_port": "/dev/ttyS1"
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"tcp_pub_address": "0.0.0.0:5555",
|
||||
"tcp_cmd_address": "0.0.0.0:5556"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Module Organization
|
||||
@@ -200,37 +200,47 @@ VacuumTiger/
|
||||
├── sangam-io/ # Hardware abstraction daemon
|
||||
│ ├── src/
|
||||
│ │ ├── main.rs # Entry point, arg parsing
|
||||
│ │ ├── app.rs # Application orchestration
|
||||
│ │ ├── config.rs # Configuration structures
|
||||
│ │ ├── error.rs # Error types
|
||||
│ │ ├── serial_io.rs # Serial transport layer
|
||||
│ │ ├── core/ # Core abstractions
|
||||
│ │ │ ├── mod.rs
|
||||
│ │ │ ├── driver.rs # DeviceDriver trait
|
||||
│ │ │ └── types.rs # SensorValue, Command types
|
||||
│ │ ├── devices/ # Hardware drivers
|
||||
│ │ │ ├── mod.rs # DeviceDriver trait
|
||||
│ │ │ ├── crl200s/ # CRL-200S robot platform
|
||||
│ │ │ │ ├── gd32/ # Motor controller driver
|
||||
│ │ │ │ │ ├── mod.rs # Driver orchestration
|
||||
│ │ │ │ │ ├── protocol.rs # Packet encoding/decoding
|
||||
│ │ │ │ │ ├── heartbeat.rs # 20ms watchdog loop
|
||||
│ │ │ │ │ ├── reader.rs # Status parsing
|
||||
│ │ │ │ │ ├── commands.rs # Command handlers
|
||||
│ │ │ │ │ ├── actuators.rs # Actuator control
|
||||
│ │ │ │ │ └── state.rs # Shared atomic state
|
||||
│ │ │ │ └── delta2d/ # 3iRobotix lidar
|
||||
│ │ │ │ ├── mod.rs # LidarDriver impl
|
||||
│ │ │ │ └── protocol.rs # Packet parsing
|
||||
│ │ │ └── constants.rs # Hardware constants
|
||||
│ │ │ ├── mod.rs # Device factory
|
||||
│ │ │ └── crl200s/ # CRL-200S robot platform
|
||||
│ │ │ ├── mod.rs
|
||||
│ │ │ ├── constants.rs # Offsets, flags, timing
|
||||
│ │ │ ├── gd32/ # Motor controller driver
|
||||
│ │ │ │ ├── mod.rs # Driver orchestration
|
||||
│ │ │ │ ├── protocol.rs # RxPacket, PacketReader
|
||||
│ │ │ │ ├── packet.rs # TxPacket, checksum
|
||||
│ │ │ │ ├── ring_buffer.rs # Zero-copy ring buffer
|
||||
│ │ │ │ ├── heartbeat.rs # 20ms watchdog loop
|
||||
│ │ │ │ ├── reader.rs # Status parsing
|
||||
│ │ │ │ ├── commands.rs # Command handlers
|
||||
│ │ │ │ └── state.rs # Shared atomic state
|
||||
│ │ │ └── delta2d/ # 3iRobotix lidar
|
||||
│ │ │ ├── mod.rs # LidarDriver impl
|
||||
│ │ │ └── protocol.rs # Packet parsing
|
||||
│ │ └── streaming/ # TCP communication
|
||||
│ │ ├── mod.rs # Re-exports
|
||||
│ │ ├── messages.rs # Protocol definitions
|
||||
│ │ ├── wire.rs # Serialization layer
|
||||
│ │ ├── tcp_publisher.rs # Outbound stream
|
||||
│ │ └── tcp_receiver.rs # Command handler
|
||||
│ └── hardware.json # Configuration file
|
||||
│ ├── hardware.json # Configuration file
|
||||
│ ├── COMMANDS.md # GD32 command reference
|
||||
│ └── SENSORSTATUS.md # Sensor packet documentation
|
||||
│
|
||||
└── drishti/ # Python visualization client
|
||||
├── drishti.py # Console client
|
||||
├── drishti_ui.py # GUI application
|
||||
└── ui/ # Web interface
|
||||
└── ui/ # PyQt widgets
|
||||
├── main_window.py
|
||||
├── processors/ # Data processing
|
||||
├── threads/ # Background workers
|
||||
└── widgets/ # UI components
|
||||
```
|
||||
|
||||
## Critical Hardware Constraints
|
||||
@@ -257,13 +267,13 @@ VacuumTiger/
|
||||
- **Format**: `[0xFA 0xFB] [LEN] [CMD] [PAYLOAD] [CRC]`
|
||||
- **CRC**: 16-bit big-endian word sum checksum
|
||||
- **Status**: CMD=0x15, 96 bytes @ 500Hz
|
||||
- **Location**: `src/devices/gd32/protocol.rs`
|
||||
- **Location**: `src/devices/crl200s/gd32/protocol.rs`
|
||||
|
||||
### Delta-2D Protocol
|
||||
- **Format**: Variable-length with 8-byte headers
|
||||
- **Data**: Angle (0.01° units), distance (0.25mm units)
|
||||
- **Rate**: ~5Hz complete scans
|
||||
- **Location**: `src/devices/delta2d/protocol.rs`
|
||||
- **Location**: `src/devices/crl200s/delta2d/protocol.rs`
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
@@ -362,13 +372,11 @@ python drishti.py --robot 192.168.68.101 --verbose
|
||||
|
||||
## Documentation References
|
||||
|
||||
- **sangam-io/README.md**: Quick start and overview
|
||||
- **sangam-io/ARCHITECTURE.md**: Design decisions and extensibility
|
||||
- **sangam-io/PROTOCOL.md**: TCP message specifications
|
||||
- **sangam-io/DEPLOYMENT.md**: Production installation guide
|
||||
- **sangam-io/CHANGELOG.md**: Version history and changes
|
||||
- **sangam-io/COMMANDS.md**: GD32 command reference with TCP API
|
||||
- **sangam-io/SENSORSTATUS.md**: Sensor packet byte layout
|
||||
- **drishti/README.md**: Python client documentation
|
||||
- **drishti/UI_README.md**: GUI application guide
|
||||
- **protocol-mitm/README.md**: MITM reverse engineering tools
|
||||
- **protocol-mitm/docs/**: Protocol discovery documentation
|
||||
|
||||
## Future Roadmap
|
||||
|
||||
@@ -390,6 +398,6 @@ When modifying this codebase:
|
||||
|
||||
1. **Maintain simplicity**: Avoid unnecessary abstractions
|
||||
2. **Prioritize reliability**: Real-time constraints matter
|
||||
3. **Document changes**: Update CHANGELOG.md
|
||||
3. **Document changes**: Update relevant documentation
|
||||
4. **Test on hardware**: Virtual tests insufficient
|
||||
5. **Preserve safety**: Never bypass heartbeat mechanism
|
||||
@@ -10,7 +10,7 @@ VacuumTiger is a modular, configuration-driven firmware stack that makes it easy
|
||||
- **Extensible**: Add new robot platforms by implementing one trait
|
||||
- **Generic Protocol**: TCP streaming works with any SLAM application
|
||||
- **Real-time**: 500Hz sensor updates, <25ms command latency
|
||||
- **Hackable**: Small codebase (~3K lines Rust), no dependencies on proprietary SDKs
|
||||
- **Hackable**: Small codebase (~4K lines Rust), no dependencies on proprietary SDKs
|
||||
|
||||
## How It Works
|
||||
|
||||
@@ -223,14 +223,17 @@ That's it! Your new robot works with any existing SLAM application.
|
||||
VacuumTiger/
|
||||
├── sangam-io/ # Hardware abstraction daemon (Rust)
|
||||
│ ├── src/
|
||||
│ │ ├── core/ # DeviceDriver trait, sensor types
|
||||
│ │ ├── core/ # DeviceDriver trait, SensorValue types
|
||||
│ │ ├── devices/ # Robot-specific implementations
|
||||
│ │ │ └── crl200s/ # CRL-200S driver (GD32 + Delta-2D)
|
||||
│ │ └── streaming/ # TCP protocol, wire format
|
||||
│ └── hardware.json # Robot configuration
|
||||
│ ├── hardware.json # Robot configuration
|
||||
│ ├── COMMANDS.md # GD32 command reference
|
||||
│ └── SENSORSTATUS.md # Sensor packet documentation
|
||||
├── drishti/ # Diagnostic visualization (Python)
|
||||
│ ├── drishti_ui.py # GUI with sensor overlays
|
||||
│ └── drishti.py # Console client
|
||||
│ ├── drishti_ui.py # PyQt GUI with sensor overlays
|
||||
│ ├── drishti.py # Console client
|
||||
│ └── ui/ # PyQt widgets and processors
|
||||
└── protocol-mitm/ # Reverse engineering tools
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Drishti - Robot Diagnostic Tool
|
||||
|
||||
Simple monitoring tool for testing sensor diagnosis and TCP protocol communication with SangamIO.
|
||||
PyQt-based monitoring tool for testing sensor diagnosis and TCP protocol communication with SangamIO.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Man-in-the-Middle (MITM) toolset for reverse-engineering proprietary serial prot
|
||||
|
||||
## Overview
|
||||
|
||||
This toolset enables transparent serial port interception using PTY-based forwarding, allowing capture of bidirectional communication without modifying the original firmware or hardware. The captured protocol was successfully reverse-engineered and implemented in the [SangamIO library](../sangam-io/).
|
||||
This toolset enables transparent serial port interception using PTY-based forwarding, allowing capture of bidirectional communication without modifying the original firmware or hardware. The captured protocol was successfully reverse-engineered and implemented in the [SangamIO daemon](../sangam-io/).
|
||||
|
||||
### Key Features
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ Packet Format: [0xFA 0xFB] [0x04] [0x65] [MODE] [CRC]
|
||||
|
||||
**Purpose**: Enables navigation subsystems including lidar control. Without switching to mode 0x02, the GD32 firmware ignores CMD=0xA2, CMD=0x97, and CMD=0x71.
|
||||
|
||||
**Implementation** (sangam-io/src/devices/gd32/mod.rs:462):
|
||||
**Implementation** (sangam-io/src/devices/crl200s/gd32/mod.rs):
|
||||
```rust
|
||||
pub fn set_motor_mode(&mut self, mode: u8) -> Result<()> {
|
||||
log::info!("GD32: Setting motor mode: 0x{:02X}", mode);
|
||||
@@ -66,7 +66,7 @@ Packet Format: [0xFA 0xFB] [0x07] [0xA2] [0x10 0x0E 0x00 0x00] [CRC]
|
||||
|
||||
**Note**: This command was discovered by analyzing MITM logs. In the original AuxCtrl firmware, CMD=0xA2 and CMD=0x97 are sent in a single write operation, suggesting they're tightly coupled.
|
||||
|
||||
**Implementation** (sangam-io/src/devices/gd32/mod.rs:472):
|
||||
**Implementation** (sangam-io/src/devices/crl200s/gd32/mod.rs):
|
||||
```rust
|
||||
pub fn send_lidar_prep(&mut self) -> Result<()> {
|
||||
log::info!("GD32: Sending lidar preparation command");
|
||||
@@ -91,7 +91,7 @@ Packet Format: [0xFA 0xFB] [0x04] [0x97] [POWER] [CRC]
|
||||
- CMD=0x97 [0x01] → GD32 sets `/sys/class/gpio/gpio233/value` = 1
|
||||
- CMD=0x97 [0x00] → GD32 sets `/sys/class/gpio/gpio233/value` = 0
|
||||
|
||||
**Implementation** (sangam-io/src/devices/gd32/mod.rs:482):
|
||||
**Implementation** (sangam-io/src/devices/crl200s/gd32/mod.rs):
|
||||
```rust
|
||||
pub fn set_lidar_power(&mut self, enable: bool) -> Result<()> {
|
||||
log::info!("GD32: Setting lidar power: {}", enable);
|
||||
@@ -112,7 +112,7 @@ Packet Format: [0xFA 0xFB] [0x07] [0x71] [PWM 4 bytes little-endian] [CRC]
|
||||
- CRC: 16-bit big-endian word sum
|
||||
```
|
||||
|
||||
**Implementation** (sangam-io/src/devices/gd32/mod.rs:492):
|
||||
**Implementation** (sangam-io/src/devices/crl200s/gd32/mod.rs):
|
||||
```rust
|
||||
pub fn set_lidar_pwm(&mut self, pwm_percent: i32) -> Result<()> {
|
||||
log::info!("GD32: Setting lidar PWM: {}%", pwm_percent);
|
||||
@@ -126,8 +126,6 @@ pub fn set_lidar_pwm(&mut self, pwm_percent: i32) -> Result<()> {
|
||||
|
||||
### Full Working Example
|
||||
|
||||
From `sangam-io/examples/test_lidar_scenario.rs`:
|
||||
|
||||
```rust
|
||||
// Step 1: Initialize GD32 motor controller
|
||||
let gd32_transport = SerialTransport::open("/dev/ttyS3", 115200)?;
|
||||
@@ -227,13 +225,12 @@ gd32.set_lidar_power(false)?; // Disable power
|
||||
|
||||
| Component | Status | Location |
|
||||
|-----------|--------|----------|
|
||||
| CMD=0x65 (MotorMode) | ✅ Implemented | `src/devices/gd32/protocol.rs:54` |
|
||||
| CMD=0xA2 (LidarPrep) | ✅ Implemented | `src/devices/gd32/protocol.rs:42` |
|
||||
| CMD=0x97 (LidarPower) | ✅ Implemented | `src/devices/gd32/protocol.rs:99` |
|
||||
| CMD=0x71 (LidarPWM) | ✅ Implemented | `src/devices/gd32/protocol.rs:102` |
|
||||
| Heartbeat thread | ✅ Working | `src/devices/gd32/heartbeat.rs` |
|
||||
| Delta-2D driver | ✅ Working | `src/devices/delta2d/mod.rs` |
|
||||
| Test scenario | ✅ Verified | `examples/test_lidar_scenario.rs` |
|
||||
| CMD=0x65 (MotorMode) | ✅ Implemented | `src/devices/crl200s/gd32/commands.rs` |
|
||||
| CMD=0xA2 (LidarPrep) | ✅ Implemented | `src/devices/crl200s/gd32/commands.rs` |
|
||||
| CMD=0x97 (LidarPower) | ✅ Implemented | `src/devices/crl200s/gd32/commands.rs` |
|
||||
| CMD=0x71 (LidarPWM) | ✅ Implemented | `src/devices/crl200s/gd32/commands.rs` |
|
||||
| Heartbeat thread | ✅ Working | `src/devices/crl200s/gd32/heartbeat.rs` |
|
||||
| Delta-2D driver | ✅ Working | `src/devices/crl200s/delta2d/mod.rs` |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -263,14 +260,14 @@ gd32.set_lidar_power(false)?; // Disable power
|
||||
## References
|
||||
|
||||
### Source Code
|
||||
- **Protocol Implementation**: `sangam-io/src/devices/gd32/protocol.rs`
|
||||
- **GD32 Driver**: `sangam-io/src/devices/gd32/mod.rs`
|
||||
- **Test Example**: `sangam-io/examples/test_lidar_scenario.rs`
|
||||
- **Protocol Implementation**: `sangam-io/src/devices/crl200s/gd32/protocol.rs`
|
||||
- **GD32 Driver**: `sangam-io/src/devices/crl200s/gd32/mod.rs`
|
||||
- **Commands**: `sangam-io/src/devices/crl200s/gd32/commands.rs`
|
||||
|
||||
### Documentation
|
||||
- **MITM Logging Guide**: `protocol-mitm/docs/MITM_LOGGING_GUIDE.md`
|
||||
- **GD32 Protocol Spec**: See protocol.rs source code for complete specification
|
||||
- **Project Guide**: `sangam-io/GUIDE.md`
|
||||
- **GD32 Command Reference**: `sangam-io/COMMANDS.md`
|
||||
- **Sensor Status Packet**: `sangam-io/SENSORSTATUS.md`
|
||||
|
||||
### MITM Logs
|
||||
- **Successful Capture**: `protocol-mitm/logs/mitm_gpio/cleaning_session_with_gpio.log`
|
||||
|
||||
@@ -1,21 +1,64 @@
|
||||
//! DeviceDriver trait definition
|
||||
//! DeviceDriver trait - the core abstraction for adding new robot hardware.
|
||||
//!
|
||||
//! To add a new device, implement this trait and register it in [`crate::devices::create_device`].
|
||||
//! See [`crate::devices::crl200s::CRL200SDriver`] for a complete implementation example.
|
||||
|
||||
use crate::core::types::{Command, SensorGroupData};
|
||||
use crate::error::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Device driver trait for hardware abstraction
|
||||
/// Hardware abstraction trait for robot devices.
|
||||
///
|
||||
/// # Lifecycle
|
||||
/// 1. Created via [`crate::devices::create_device`] based on config
|
||||
/// 2. [`initialize`](Self::initialize) called once at daemon startup
|
||||
/// 3. [`send_command`](Self::send_command) called for each inbound TCP command
|
||||
/// 4. `Drop` triggers cleanup when daemon shuts down
|
||||
///
|
||||
/// # Threading
|
||||
/// Drivers typically spawn internal threads for:
|
||||
/// - **Heartbeat**: Safety-critical timing loop (e.g., motor watchdog)
|
||||
/// - **Reader**: Continuous sensor data parsing
|
||||
///
|
||||
/// Use `Arc<Mutex<>>` for state shared across threads, `Arc<AtomicBool>` for shutdown signals.
|
||||
///
|
||||
/// # Example Structure
|
||||
/// ```ignore
|
||||
/// pub struct MyDeviceDriver {
|
||||
/// port: Arc<Mutex<Box<dyn SerialPort>>>, // Shared serial access
|
||||
/// shutdown: Arc<AtomicBool>, // Thread shutdown signal
|
||||
/// reader_handle: Option<JoinHandle<()>>, // Thread handle for cleanup
|
||||
/// }
|
||||
///
|
||||
/// impl DeviceDriver for MyDeviceDriver {
|
||||
/// fn initialize(&mut self) -> Result<HashMap<String, Arc<Mutex<SensorGroupData>>>> {
|
||||
/// // 1. Create sensor groups
|
||||
/// // 2. Spawn reader/heartbeat threads
|
||||
/// // 3. Return sensor map for TCP streaming
|
||||
/// }
|
||||
/// fn send_command(&mut self, cmd: Command) -> Result<()> {
|
||||
/// // Handle ComponentControl commands
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Drop for MyDeviceDriver {
|
||||
/// fn drop(&mut self) {
|
||||
/// self.shutdown.store(true, Ordering::Relaxed);
|
||||
/// // Join threads...
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait DeviceDriver: Send {
|
||||
/// Initialize hardware and return sensor data groups
|
||||
/// Initialize hardware and start background threads.
|
||||
///
|
||||
/// The driver should:
|
||||
/// 1. Create sensor data groups for its sensors
|
||||
/// 2. Open serial ports
|
||||
/// 3. Start internal threads (heartbeat, readers)
|
||||
/// 4. Return the HashMap of sensor groups to be used by TCP publisher
|
||||
/// Returns sensor groups keyed by group ID (e.g., "gd32_status", "lidar").
|
||||
/// The TCP publisher uses these to stream sensor data to clients.
|
||||
fn initialize(&mut self) -> Result<HashMap<String, Arc<Mutex<SensorGroupData>>>>;
|
||||
|
||||
/// Send command to hardware
|
||||
/// Process a command from TCP clients.
|
||||
///
|
||||
/// Most commands are [`Command::ComponentControl`] targeting specific components.
|
||||
/// Return `Err` for invalid commands; the error is logged but doesn't stop the daemon.
|
||||
fn send_command(&mut self, cmd: Command) -> Result<()>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
//! Core module - driver trait and types
|
||||
//! Core abstractions for device drivers.
|
||||
//!
|
||||
//! - [`driver::DeviceDriver`]: Trait to implement for new hardware
|
||||
//! - [`types`]: Sensor values, commands, and data structures
|
||||
|
||||
pub mod driver;
|
||||
pub mod types;
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
//! Core data types for SangamIO
|
||||
//! Core data types for sensors, commands, and device communication.
|
||||
//!
|
||||
//! Key types for device implementers:
|
||||
//! - [`SensorGroupData`]: Container for sensor values, updated by driver threads
|
||||
//! - [`Command`]: Inbound commands from TCP clients (mainly [`Command::ComponentControl`])
|
||||
//! - [`SensorValue`]: Typed sensor values for the `values` HashMap
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -78,13 +78,19 @@ use std::sync::{Arc, Mutex};
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::time::Duration;
|
||||
|
||||
/// GD32 driver managing heartbeat and sensor reading
|
||||
/// GD32 motor controller driver with heartbeat and reader threads.
|
||||
pub struct GD32Driver {
|
||||
/// Serial port shared between heartbeat (write) and reader (read) threads
|
||||
port: Arc<Mutex<Box<dyn SerialPort>>>,
|
||||
/// Heartbeat interval from config (typically 20ms)
|
||||
heartbeat_interval_ms: u64,
|
||||
/// Shutdown signal - set to true to stop all threads
|
||||
shutdown: Arc<AtomicBool>,
|
||||
/// Heartbeat thread handle - joined on shutdown
|
||||
heartbeat_handle: Option<JoinHandle<()>>,
|
||||
/// Reader thread handle - joined on shutdown
|
||||
reader_handle: Option<JoinHandle<()>>,
|
||||
/// Component states (velocity, brushes, etc.) - atomic for lockless heartbeat reads
|
||||
component_state: Arc<ComponentState>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
//! CRL-200S Vacuum Robot device driver
|
||||
//! CRL-200S Vacuum Robot driver - reference implementation for new devices.
|
||||
//!
|
||||
//! This driver manages two subcomponents:
|
||||
//! - **GD32**: Motor controller (wheels, brushes, vacuum) via `/dev/ttyS3`
|
||||
//! - **Delta2D**: Lidar sensor via `/dev/ttyS1`
|
||||
//!
|
||||
//! # Sensor Groups Published
|
||||
//! - `sensor_status`: Bumpers, cliffs, encoders, IMU @ 500Hz
|
||||
//! - `device_version`: Firmware version (one-time)
|
||||
//! - `lidar`: Point cloud @ 5Hz
|
||||
|
||||
pub mod constants;
|
||||
pub mod delta2d;
|
||||
@@ -13,10 +22,12 @@ use gd32::GD32Driver;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// CRL-200S device driver
|
||||
/// CRL-200S device driver coordinating GD32 motor controller and Delta2D lidar.
|
||||
pub struct CRL200SDriver {
|
||||
config: DeviceConfig,
|
||||
/// Motor controller - `None` before `initialize()`, `Some` after
|
||||
gd32: Option<GD32Driver>,
|
||||
/// Lidar driver - `None` before `initialize()`, `Some` after
|
||||
lidar: Option<Delta2DDriver>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
//! Device implementations
|
||||
//! Device driver implementations and factory.
|
||||
//!
|
||||
//! # Adding a New Device
|
||||
//!
|
||||
//! 1. **Create module**: `src/devices/my_device/mod.rs`
|
||||
//! 2. **Implement trait**: `impl DeviceDriver for MyDeviceDriver`
|
||||
//! 3. **Register here**: Add match arm in [`create_device`]
|
||||
//!
|
||||
//! ## Module Structure
|
||||
//! ```text
|
||||
//! devices/my_device/
|
||||
//! ├── mod.rs # MyDeviceDriver struct + DeviceDriver impl
|
||||
//! ├── protocol.rs # Packet parsing (if serial)
|
||||
//! └── commands.rs # Command handlers (optional)
|
||||
//! ```
|
||||
//!
|
||||
//! ## Key Patterns (from CRL200S)
|
||||
//! - Use `Arc<Mutex<>>` for serial port shared across threads
|
||||
//! - Use `Arc<AtomicBool>` for shutdown signal
|
||||
//! - Implement `Drop` to join spawned threads
|
||||
//! - Return sensor groups from `initialize()` for TCP streaming
|
||||
//!
|
||||
//! See [`crl200s::CRL200SDriver`] for a complete example.
|
||||
|
||||
pub mod crl200s;
|
||||
|
||||
@@ -7,7 +29,12 @@ use crate::core::driver::DeviceDriver;
|
||||
use crate::error::{Error, Result};
|
||||
use crl200s::CRL200SDriver;
|
||||
|
||||
/// Create a device driver based on configuration
|
||||
/// Device factory - creates driver based on `device.type` in config.
|
||||
///
|
||||
/// To add a new device type, add a match arm here:
|
||||
/// ```ignore
|
||||
/// "my_device" => Ok(Box::new(MyDeviceDriver::new(config.device.clone())?)),
|
||||
/// ```
|
||||
pub fn create_device(config: &Config) -> Result<Box<dyn DeviceDriver>> {
|
||||
match config.device.device_type.as_str() {
|
||||
"crl200s" => {
|
||||
|
||||
Reference in New Issue
Block a user