vcan_loopback/
vcan_loopback.rs1fn main() {
16 #[cfg(target_os = "linux")]
17 {
18 if let Err(e) = linux::run() {
19 eprintln!("vcan_loopback FAILED: {e}");
20 std::process::exit(1);
21 }
22 }
23 #[cfg(not(target_os = "linux"))]
24 {
25 eprintln!("This example requires Linux SocketCAN (vcan0); see tools/vcan_setup.sh.");
26 }
27}
28
29#[cfg(target_os = "linux")]
30mod linux {
31 use std::error::Error;
32 use std::sync::mpsc;
33 use std::thread;
34 use std::time::Duration;
35
36 use canopen_host::transport::{Received, SocketCan};
37 use canopen_rs::node::{Node, MAX_PDO_MAPPING};
38 use canopen_rs::sync::SYNC_COB_ID;
39 use canopen_rs::{
40 Address, DataType, Entry, MappingEntry, NmtCommand, NodeId, ObjectDictionary, PdoMapping,
41 TransmissionType, Value,
42 };
43
44 const IFACE: &str = "vcan0";
45
46 pub fn run() -> Result<(), Box<dyn Error>> {
47 let node = NodeId::new(0x10)?;
48
49 let (ready_tx, ready_rx) = mpsc::channel::<()>();
51 thread::spawn(move || {
52 if let Err(e) = serve(node, ready_tx) {
53 eprintln!("server thread error: {e}");
54 }
55 });
56 ready_rx.recv().map_err(|_| "server failed to start")?;
58
59 let bus = SocketCan::open(IFACE)?;
61 bus.set_read_timeout(Duration::from_secs(2))?;
62
63 let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
65 println!("read 0x1000 device type = {device_type:?}");
66 assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
67
68 bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
70 let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
71 println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
72 assert_eq!(heartbeat, Value::Unsigned16(2500));
73
74 let big = Value::Unsigned64(0x0102_0304_0506_0708);
76 bus.sdo_write(node, Address::new(0x2000, 0), big)?;
77 let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
78 println!("write 0x2000 (segmented) -> read = {back:?}");
79 assert_eq!(back, big);
80
81 let rpdo1 = 0x200 + node.raw() as u16;
85 let tpdo1 = 0x180 + node.raw() as u16;
86 bus.send_nmt(NmtCommand::StartRemoteNode, node)?; bus.send(rpdo1, &[0xCD, 0xAB])?; bus.send(SYNC_COB_ID, &[])?; let tpdo = recv_cob(&bus, tpdo1)?;
91 println!("RPDO in 0xABCD -> SYNC -> TPDO out = {:02X?}", tpdo.data());
92 assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
93
94 println!(
95 "\nvcan0 loopback OK — SDO (expedited + segmented), NMT, and PDO all round-tripped."
96 );
97 Ok(())
98 }
99
100 fn recv_cob(bus: &SocketCan, cob_id: u16) -> Result<Received, Box<dyn Error>> {
102 loop {
103 let frame = bus.recv()?;
104 if frame.cob_id == cob_id {
105 return Ok(frame);
106 }
107 }
108 }
109
110 fn serve(node_id: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
113 let bus = SocketCan::open(IFACE)?;
114 bus.set_read_timeout(Duration::from_secs(3))?;
115
116 let mut od = ObjectDictionary::<8>::new();
117 od.insert(
118 Address::new(0x1000, 0),
119 Entry::constant(Value::Unsigned32(0x0004_0192)),
120 )?;
121 od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
122 od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
123 od.insert(Address::new(0x6000, 1), Entry::rw(Value::Unsigned16(0)))?;
124
125 let mut node = Node::new(node_id, od);
126 node.add_rpdo(0x200 + node_id.raw() as u16, mapping(0x6000, 1, 16))?;
128 node.add_tpdo(
129 0x180 + node_id.raw() as u16,
130 mapping(0x6000, 1, 16),
131 TransmissionType::SynchronousAcyclic,
132 )?;
133 node.boot(); ready.send(()).map_err(|_| "client went away")?;
135
136 while let Ok(frame) = bus.recv() {
138 if let Some(tx) = node.on_frame(frame.cob_id, frame.data()) {
139 bus.send(tx.cob_id, tx.data())?;
140 }
141 if frame.cob_id == SYNC_COB_ID {
142 for tx in node.sync_tpdos() {
143 bus.send(tx.cob_id, tx.data())?;
144 }
145 }
146 }
147 Ok(())
148 }
149
150 fn mapping(index: u16, subindex: u8, bits: u8) -> PdoMapping<MAX_PDO_MAPPING> {
152 let mut m = PdoMapping::new();
153 m.push(MappingEntry::new(index, subindex, bits))
154 .expect("one entry fits");
155 m
156 }
157}