initial commit
This commit is contained in:
1
src/hw/mod.rs
Normal file
1
src/hw/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod serial;
|
17
src/hw/serial.rs
Normal file
17
src/hw/serial.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use std::io;
|
||||
use std::time::Duration;
|
||||
use serialport::{SerialPort, DataBits, FlowControl, Parity, StopBits};
|
||||
|
||||
pub fn xm_pcr_serial(port_name: &str, baud_rate: u32, timeout: Duration) -> Result<Box<dyn SerialPort>, io::Error> {
|
||||
|
||||
let port: Result<Box<dyn SerialPort>, io::Error> = serialport::new(port_name, baud_rate)
|
||||
.data_bits(DataBits::Eight)
|
||||
.flow_control(FlowControl::None)
|
||||
.parity(Parity::None)
|
||||
.stop_bits(StopBits::One)
|
||||
.timeout(timeout)
|
||||
.open()
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to open serial port: {}", e)));
|
||||
|
||||
return port;
|
||||
}
|
32
src/main.rs
Normal file
32
src/main.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
mod xm;
|
||||
mod hw;
|
||||
|
||||
use xm::command::{send_command, XMCommand};
|
||||
use hw::serial::xm_pcr_serial;
|
||||
use std::time::Duration;
|
||||
use std::io;
|
||||
|
||||
fn main() -> Result<(), io::Error> {
|
||||
let port_name: &str = "/dev/ttyUSB0"; // Adjust as per your system
|
||||
let baud_rate: u32 = 9600; // Adjust baud rate as needed
|
||||
let timeout: Duration = Duration::from_millis(500); // Short timeout
|
||||
|
||||
let mut port = match xm_pcr_serial(port_name, baud_rate, timeout) {
|
||||
Ok(p) => p, // Successfully got the serial port
|
||||
Err(e) => {
|
||||
eprintln!("Error opening serial port: {}", e);
|
||||
return Err(e); // Return the error if we can't open the port
|
||||
}
|
||||
};
|
||||
|
||||
match send_command(&mut *port, XMCommand::get_radio_id(), timeout) {
|
||||
Ok(serial_number_str) => {
|
||||
println!("Received valid XMRadioID: {}", serial_number_str);
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
69
src/xm/command.rs
Normal file
69
src/xm/command.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use serialport::SerialPort;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use std::io;
|
||||
|
||||
use super::packet::XMPacket;
|
||||
use super::radio_id::XMRadioID;
|
||||
|
||||
pub struct XMCommand;
|
||||
|
||||
impl XMCommand {
|
||||
#[allow(dead_code)]
|
||||
pub fn power_on() -> XMPacket {
|
||||
XMPacket::new(vec![0x00, 0x10, 0x10, 0x10, 0x01])
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn power_off() -> XMPacket {
|
||||
XMPacket::new(vec![0x01, 0x00])
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn select_channel(channel: u8) -> XMPacket {
|
||||
XMPacket::new(vec![0x10, 0x02, channel, 0x00, 0x00, 0x01])
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn get_radio_id() -> XMPacket {
|
||||
XMPacket::new(vec![0x31])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_command(port: &mut dyn SerialPort, command: XMPacket, timeout: Duration) -> Result<String, io::Error> {
|
||||
loop {
|
||||
// Send the command
|
||||
port.write_all(&command.to_bytes())?;
|
||||
port.flush()?;
|
||||
|
||||
// Read the response from the serial port
|
||||
let mut response: Vec<u8> = vec![0; 1024]; // Buffer for response
|
||||
match port.read(&mut response) {
|
||||
Ok(n) => {
|
||||
response.truncate(n);
|
||||
|
||||
// Validate the response as an XMPacket
|
||||
if XMPacket::is_valid_response(&response) {
|
||||
let packet = XMPacket::new(response);
|
||||
|
||||
// Parse the XMPacket into an XMRadioID
|
||||
match XMRadioID::from_packet(&packet) {
|
||||
Ok(radio_id) => {
|
||||
let serial_number_bytes = radio_id.get_radio_id();
|
||||
let serial_number_str = String::from_utf8_lossy(serial_number_bytes).to_string();
|
||||
return Ok(serial_number_str); // Return the serial number string
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Error parsing XMRadioID: {}", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Received invalid XMPacket");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Error reading from serial port: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the timeout period before retrying
|
||||
sleep(timeout);
|
||||
}
|
||||
}
|
3
src/xm/mod.rs
Normal file
3
src/xm/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod packet;
|
||||
pub mod command;
|
||||
pub mod radio_id;
|
45
src/xm/packet.rs
Normal file
45
src/xm/packet.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
pub struct XMPacket {
|
||||
data_length: u16,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl XMPacket {
|
||||
pub(crate) fn new(data: Vec<u8>) -> Self {
|
||||
let data_length = data.len() as u16;
|
||||
XMPacket { data_length, data }
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut command: Vec<u8> = vec![0x5A, 0xA5]; // Start bytes
|
||||
command.extend_from_slice(&self.data_length.to_be_bytes()); // Length in big-endian
|
||||
command.extend_from_slice(&self.data); // Command data
|
||||
command.extend_from_slice(&[0xED, 0xED]); // Footer
|
||||
command
|
||||
}
|
||||
|
||||
// Helper function to validate the response format
|
||||
pub(crate) fn is_valid_response(response: &[u8]) -> bool {
|
||||
// Validate that the response is at least the minimum size for a valid packet (6 bytes)
|
||||
if response.len() < 6 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the start of the response (5A A5)
|
||||
if response[0] != 0x5A || response[1] != 0xA5 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the length field (xx xx), which is in big-endian format
|
||||
let length = u16::from_be_bytes([response[2], response[3]]) as usize;
|
||||
|
||||
// Ensure the total response length matches what is indicated in the length field + header + footer
|
||||
// Header (4 bytes) + Data (length bytes) + Footer (2 bytes)
|
||||
if response.len() != length + 6 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If all checks pass, the response is valid
|
||||
true
|
||||
}
|
||||
|
||||
}
|
28
src/xm/radio_id.rs
Normal file
28
src/xm/radio_id.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use super::packet::XMPacket;
|
||||
|
||||
pub struct XMRadioID {
|
||||
radio_id: Vec<u8>,
|
||||
}
|
||||
|
||||
impl XMRadioID {
|
||||
pub(crate) fn from_packet(packet: &XMPacket) -> Result<Self, &'static str> {
|
||||
let packet_bytes = packet.to_bytes();
|
||||
|
||||
// Validate the packet
|
||||
if !XMPacket::is_valid_response(&packet_bytes) {
|
||||
return Err("Invalid packet");
|
||||
}
|
||||
|
||||
// Extract the serial number (12 bytes starting from the 8th byte)
|
||||
if packet_bytes.len() < 20 {
|
||||
return Err("Packet too short to contain an XM Radio ID");
|
||||
}
|
||||
|
||||
let radio_id = packet_bytes[8..20].to_vec();
|
||||
Ok(XMRadioID { radio_id })
|
||||
}
|
||||
|
||||
pub fn get_radio_id(&self) -> &Vec<u8> {
|
||||
&self.radio_id
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user