1fn main() {
20 #[cfg(target_os = "linux")]
21 {
22 if let Err(e) = linux::run() {
23 eprintln!("vcan_loopback FAILED: {e}");
24 std::process::exit(1);
25 }
26 }
27 #[cfg(not(target_os = "linux"))]
28 {
29 eprintln!("This example requires Linux SocketCAN (vcan0); see tools/vcan_setup.sh.");
30 }
31}
32
33#[cfg(target_os = "linux")]
34mod linux {
35 use std::error::Error;
36 use std::sync::mpsc;
37 use std::thread;
38 use std::time::Duration;
39
40 use canopen_host::transport::{Received, SocketCan};
41 use canopen_rs::lss::{
42 self, encode_configure_node_id, encode_store, encode_switch_global, LssAddress,
43 };
44 use canopen_rs::nmt::NMT_COMMAND_COB_ID;
45 use canopen_rs::node::{Node, MAX_PDO_MAPPING};
46 use canopen_rs::sdo::block::{
47 self, decode_end, decode_sub_segment, encode_download_initiate, BlockReceiver, BlockWriter,
48 };
49 use canopen_rs::sync::SYNC_COB_ID;
50 use canopen_rs::{
51 Address, DataType, Entry, MappingEntry, NmtCommand, NodeId, ObjectDictionary, PdoMapping,
52 TransmissionType, Value,
53 };
54
55 const IFACE: &str = "vcan0";
56 const BLOCK_REQ: u16 = 0x123;
58 const BLOCK_RESP: u16 = 0x124;
59
60 pub fn run() -> Result<(), Box<dyn Error>> {
61 let (ready_tx, ready_rx) = mpsc::channel::<()>();
62 thread::spawn(move || {
63 if let Err(e) = serve(ready_tx) {
64 eprintln!("server thread error: {e}");
65 }
66 });
67 ready_rx.recv().map_err(|_| "server failed to start")?;
68
69 let bus = SocketCan::open(IFACE)?;
70 bus.set_read_timeout(Duration::from_secs(2))?;
71
72 bus.send(lss::LSS_MASTER_COB_ID, &encode_switch_global(true))?;
74 bus.send(lss::LSS_MASTER_COB_ID, &encode_configure_node_id(0x10))?;
75 let cfg = recv_cob(&bus, lss::LSS_SLAVE_COB_ID)?;
76 assert_eq!(&cfg.data()[..2], &[0x11, 0x00]); bus.send(lss::LSS_MASTER_COB_ID, &encode_store())?;
78 recv_cob(&bus, lss::LSS_SLAVE_COB_ID)?; bus.send(lss::LSS_MASTER_COB_ID, &encode_switch_global(false))?;
80 bus.send_nmt(NmtCommand::ResetCommunication, NodeId::BROADCAST)?;
81 let bootup = recv_cob(&bus, 0x700 + 0x10)?; println!(
83 "LSS: assigned node-id 0x10; node booted (bootup {:02X?})",
84 bootup.data()
85 );
86
87 let node = NodeId::new(0x10)?;
88
89 let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
91 println!("SDO: read 0x1000 device type = {device_type:?}");
92 assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
93 bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
94 assert_eq!(
95 bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?,
96 Value::Unsigned16(2500)
97 );
98 let big = Value::Unsigned64(0x0102_0304_0506_0708);
99 bus.sdo_write(node, Address::new(0x2000, 0), big)?; assert_eq!(
101 bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?,
102 big
103 );
104 println!("SDO: expedited + segmented read/write OK");
105
106 bus.send_nmt(NmtCommand::StartRemoteNode, node)?;
108 bus.send(0x200 + 0x10, &[0xCD, 0xAB])?; bus.send(SYNC_COB_ID, &[])?;
110 let tpdo = recv_cob(&bus, 0x180 + 0x10)?;
111 assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
112 println!(
113 "NMT+PDO: RPDO in 0xABCD -> SYNC -> TPDO out {:02X?}",
114 tpdo.data()
115 );
116
117 let payload: [u8; 50] = core::array::from_fn(|i| i as u8);
119 bus.send(
120 BLOCK_REQ,
121 &encode_download_initiate(Address::new(0x3000, 0), Some(payload.len() as u32), true),
122 )?;
123 let mut writer = BlockWriter::new(&payload, block::MAX_BLKSIZE);
124 while let Some(segment) = writer.next_segment() {
125 bus.send(BLOCK_REQ, &segment)?;
126 }
127 bus.send(BLOCK_REQ, &writer.end_frame(true))?;
128 let confirm = recv_cob(&bus, BLOCK_RESP)?;
129 assert_eq!(confirm.data(), &[1], "server CRC-verified the block");
130 println!(
131 "BLOCK: downloaded {} bytes -> server CRC-verified",
132 payload.len()
133 );
134
135 println!("\nvcan0 loopback OK — LSS, SDO, NMT, PDO, and block transfer all round-tripped.");
136 Ok(())
137 }
138
139 fn recv_cob(bus: &SocketCan, cob_id: u16) -> Result<Received, Box<dyn Error>> {
141 loop {
142 let frame = bus.recv()?;
143 if frame.cob_id == cob_id {
144 return Ok(frame);
145 }
146 }
147 }
148
149 fn serve(ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
151 let bus = SocketCan::open(IFACE)?;
152 bus.set_read_timeout(Duration::from_secs(3))?;
153
154 let mut od = ObjectDictionary::<8>::new();
155 od.insert(
156 Address::new(0x1000, 0),
157 Entry::constant(Value::Unsigned32(0x0004_0192)),
158 )?;
159 od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
160 od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
161 od.insert(Address::new(0x6000, 1), Entry::rw(Value::Unsigned16(0)))?;
162
163 let mut node = Node::new(NodeId::new(1)?, od);
165 node.enable_lss(LssAddress {
166 vendor_id: 0x1F,
167 product_code: 0x2A,
168 revision_number: 1,
169 serial_number: 0x99,
170 });
171 ready.send(()).map_err(|_| "client went away")?;
172
173 let mut block_rx = BlockReceiver::<256>::new();
174 let mut booted = false;
175
176 while let Ok(frame) = bus.recv() {
177 if frame.cob_id == BLOCK_REQ {
178 handle_block(&bus, &mut block_rx, &frame)?;
179 continue;
180 }
181 if let Some(tx) = node.on_frame(frame.cob_id, frame.data()) {
182 bus.send(tx.cob_id, tx.data())?;
183 }
184 if !booted && frame.cob_id == NMT_COMMAND_COB_ID {
186 node.apply_lss_node_id();
187 let id = node.node_id().raw() as u16;
188 node.add_rpdo(0x200 + id, single_map(0x6000, 1, 16))?;
189 node.add_tpdo(
190 0x180 + id,
191 single_map(0x6000, 1, 16),
192 TransmissionType::SynchronousAcyclic,
193 )?;
194 let boot = node.boot();
195 bus.send(boot.cob_id, boot.data())?;
196 booted = true;
197 }
198 if frame.cob_id == SYNC_COB_ID {
199 for tx in node.sync_tpdos() {
200 bus.send(tx.cob_id, tx.data())?;
201 }
202 }
203 }
204 Ok(())
205 }
206
207 fn handle_block(
209 bus: &SocketCan,
210 rx: &mut BlockReceiver<256>,
211 frame: &Received,
212 ) -> Result<(), Box<dyn Error>> {
213 let p = pad8(frame.data());
214 let cmd = p[0];
215 if cmd & 0xE0 == 0xC0 && cmd & 0x01 == 0 {
216 *rx = BlockReceiver::new(); } else if cmd & 0xE0 == 0xC0 && cmd & 0x01 == 1 {
218 let (unused, crc) = decode_end(&p)?;
219 let ok = rx.finish(unused, crc, true).is_ok();
220 bus.send(BLOCK_RESP, &[ok as u8])?; } else {
222 rx.push(&decode_sub_segment(&p))?; }
224 Ok(())
225 }
226
227 fn pad8(data: &[u8]) -> [u8; 8] {
228 let mut p = [0u8; 8];
229 let n = data.len().min(8);
230 p[..n].copy_from_slice(&data[..n]);
231 p
232 }
233
234 fn single_map(index: u16, sub: u8, bits: u8) -> PdoMapping<MAX_PDO_MAPPING> {
235 let mut m = PdoMapping::new();
236 m.push(MappingEntry::new(index, sub, bits))
237 .expect("one entry fits");
238 m
239 }
240}