hls_lfcd_lds_driver/lib.rs
1//
2// Copyright (c) 2022 Gabriele Baldoni
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12// Gabriele Baldoni, <gabriele@gabrielebaldoni.com>
13//
14
15//! hls_lfcd_lds_driver provides a rust version of the LDS01 driver from robotis.
16//! This crate facilitates reading information from that specific lidar.
17
18#[cfg(feature = "async_tokio")]
19use tokio::io::AsyncReadExt;
20#[cfg(feature = "async_tokio")]
21use tokio_serial::{SerialPortBuilderExt, SerialStream};
22
23#[cfg(feature = "async_smol")]
24use futures::prelude::*;
25#[cfg(feature = "async_smol")]
26use mio_serial::{SerialPortBuilderExt, SerialStream};
27#[cfg(feature = "async_smol")]
28use smol::Async;
29
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Serialize};
32#[cfg(feature = "serde")]
33use serde_big_array::BigArray;
34
35#[cfg(feature = "sync")]
36use std::io::Read;
37
38#[cfg(feature = "sync")]
39use serialport::TTYPort;
40
41/// Default serial port of the lidar
42pub static DEFAULT_PORT: &str = "/dev/ttyUSB0";
43/// Default baud_rate of the lidar
44pub static DEFAULT_BAUD_RATE: &str = "230400";
45
46/// Byte sent to stop the lidar, 101 = ASCII 'e'
47static STOP_BYTE: u8 = 101;
48
49/// Byte sent to start the lidar, 98 = ASCII 'b'
50static START_BYTE: u8 = 98;
51
52/// This struct contains the reading from the lidar.
53/// The `ranges` array contains 360 elements, one for each degree,
54/// with a value from 0 to 1000, indicating the distance.
55///
56/// The `intensites` array contains 360 elements, one for each degree,
57/// with a value, indicating accuracy of the reading
58///
59/// The `rmps` field gets the lidar RPMs
60#[cfg(feature = "ser_de")]
61#[derive(Debug, Serialize, Deserialize, Clone)]
62pub struct LaserReading {
63 #[serde(with = "BigArray")]
64 pub ranges: [u16; 360],
65 #[serde(with = "BigArray")]
66 pub intensities: [u16; 360],
67 pub rpms: u16,
68}
69
70/// This struct contains the reading from the lidar.
71/// The `ranges` array contains 360 elements, one for each degree,
72/// with a value from 0 to 1000, indicating the distance.
73///
74/// The `intensites` array contains 360 elements, one for each degree,
75/// with a value, indicating accuracy of the reading
76///
77/// The `rmps` field gets the lidar RPMs
78#[cfg(not(feature = "ser_de"))]
79#[derive(Debug, Clone)]
80pub struct LaserReading {
81 pub ranges: [u16; 360],
82 pub intensities: [u16; 360],
83 pub rpms: u16,
84}
85
86impl LaserReading {
87 pub fn new() -> Self {
88 Self {
89 ranges: [0u16; 360],
90 intensities: [0u16; 360],
91 rpms: 0,
92 }
93 }
94}
95
96impl Default for LaserReading {
97 fn default() -> Self {
98 Self::new()
99 }
100}
101
102/// This struct allows to read lidar information and to "shutdown" the driver
103
104pub struct LFCDLaser {
105 port: String,
106 baud_rate: u32,
107 shutting_down: bool,
108 motor_speed: u16,
109 rpms: u16,
110 #[cfg(feature = "async_tokio")]
111 serial: SerialStream,
112 #[cfg(feature = "async_smol")]
113 serial: Async<SerialStream>,
114 #[cfg(feature = "sync")]
115 serial: TTYPort,
116 buff: [u8; 2520],
117}
118
119impl LFCDLaser {
120 /// Creates the `LFCDLaser`
121 pub fn close(&mut self) {
122 self.shutting_down = true;
123
124 // Stopping the Lidar, ignoring the result.
125 #[cfg(not(feature = "async_smol"))]
126 std::io::Write::write_all(&mut self.serial, &[STOP_BYTE]).ok();
127 #[cfg(feature = "async_smol")]
128 std::io::Write::write_all(&mut self.serial.get_mut(), &[STOP_BYTE]).ok();
129 }
130
131 /// Gets lidar speed.
132 pub fn speed(&self) -> u16 {
133 self.motor_speed
134 }
135
136 /// Gets the configured baud rate
137 pub fn baud_rate(&self) -> u32 {
138 self.baud_rate
139 }
140
141 /// Gets the configured serial port
142 pub fn port(&self) -> String {
143 self.port.clone()
144 }
145
146 /// Gets the lidars rmp from the last reading
147 pub fn rpms(&self) -> u16 {
148 self.rpms
149 }
150
151 // Starts the Lidar
152 pub fn start(&mut self) {
153 // Starting the Lidar
154 #[cfg(not(feature = "async_smol"))]
155 std::io::Write::write_all(&mut self.serial, &[START_BYTE]).ok();
156
157 #[cfg(feature = "async_smol")]
158 std::io::Write::write_all(&mut self.serial.get_mut(), &[START_BYTE]).ok();
159
160 self.shutting_down = false;
161 }
162}
163
164impl Drop for LFCDLaser {
165 fn drop(&mut self) {
166 self.close();
167 }
168}
169
170#[cfg(feature = "async_tokio")]
171impl LFCDLaser {
172 /// Creates a new `LFCDLaser` with the given parameters.
173 ///
174 /// # Errors
175 /// An error variant is returned in case of:
176 /// - unable to open the specified serial port
177 /// - unable to set the port to non-exclusive (only on unix)
178 pub fn new(port: String, baud_rate: u32) -> tokio_serial::Result<Self> {
179 let mut serial = tokio_serial::new(port.clone(), baud_rate).open_native_async()?;
180
181 #[cfg(unix)]
182 serial.set_exclusive(false)?;
183
184 let mut lidar = Self {
185 port,
186 baud_rate,
187 shutting_down: false,
188 motor_speed: 0,
189 rpms: 0,
190 serial,
191 buff: [0u8; 2520],
192 };
193
194 lidar.start();
195
196 Ok(lidar)
197 }
198
199 /// Gets a reading from the lidar, returing a `LaserReading` object.
200 ///
201 /// # Errors
202 /// An error variant is returned in case of:
203 /// - unable to read form the serial port
204 /// - the driver is closed
205 pub async fn read(&mut self) -> tokio_serial::Result<LaserReading> {
206 let mut start_count: usize = 0;
207 let mut good_sets: u8 = 0;
208
209 let mut scan = LaserReading::new();
210
211 if self.shutting_down {
212 return Err(tokio_serial::Error::new(
213 tokio_serial::ErrorKind::Unknown,
214 "Driver is closed",
215 ));
216 }
217
218 loop {
219 // Wait for data sync of frame: 0xFA, 0XA0
220
221 // Read one byte
222 self.serial
223 .read_exact(std::slice::from_mut(&mut self.buff[start_count]))
224 .await?;
225 //println!("start_count : {start_count} = Read {:02X?}", buff[start_count]);
226 if start_count == 0 {
227 if self.buff[start_count] == 0xFA {
228 start_count = 1
229 }
230 } else if start_count == 1 {
231 if self.buff[start_count] == 0xA0 {
232 self.serial.read_exact(&mut self.buff[2..]).await?;
233
234 //read data in sets of 6
235
236 for i in (0..self.buff.len()).step_by(42) {
237 if self.buff[i] == 0xFA && usize::from(self.buff[i + 1]) == (0xA0 + i / 42)
238 {
239 good_sets = good_sets.wrapping_add(1);
240
241 let b_rmp0: u16 = self.buff[i + 3] as u16;
242 let b_rmp1: u16 = self.buff[i + 2] as u16;
243
244 // motor_speed = motor_speed.wrapping_add((b_rmp0 as u32) << 8 + (b_rmp1 as u32)); // accumulate count for avg. time increment
245 let rpms = (b_rmp0 << 8 | b_rmp1) / 10;
246 scan.rpms = rpms;
247 self.rpms = rpms;
248
249 for j in ((i + 4)..(i + 40)).step_by(6) {
250 let index = 6 * (i / 42) + (j - 4 - i) / 6;
251 // Four bytes `per reading
252 let b0: u16 = self.buff[j] as u16;
253 let b1: u16 = self.buff[j + 1] as u16;
254 let b2: u16 = self.buff[j + 2] as u16;
255 let b3: u16 = self.buff[j + 3] as u16;
256
257 // Remaining bits are the range in mm
258 let range: u16 = (b3 << 8) + b2;
259
260 // Last two bytes represents the uncertanity or intensity, might also
261 // be pixel area of target...
262 // let intensity = (b3 << 8) + b2;
263 let intensity: u16 = (b1 << 8) + b0;
264
265 scan.ranges[359 - index] = range;
266 scan.intensities[359 - index] = intensity;
267 }
268 }
269 }
270
271 // self.time_increment = motor_speed/good_sets/1e8;
272 return Ok(scan);
273 } else {
274 start_count = 0;
275 }
276 }
277 }
278 }
279}
280
281#[cfg(feature = "sync")]
282impl LFCDLaser {
283 /// Creates a new `LFCDLaser` with the given parameters.
284 ///
285 /// # Errors
286 /// An error variant is returned in case of:
287 /// - unable to open the specified serial port
288 /// - unable to set the port to non-exclusive (only on unix)
289 pub fn new(port: String, baud_rate: u32) -> serialport::Result<Self> {
290 let mut serial = serialport::new(port.clone(), baud_rate).open_native()?;
291
292 #[cfg(unix)]
293 serial.set_exclusive(false)?;
294
295 let mut lidar = Self {
296 port,
297 baud_rate,
298 shutting_down: false,
299 motor_speed: 0,
300 rpms: 0,
301 serial,
302 buff: [0u8; 2520],
303 };
304
305 lidar.start();
306
307 Ok(lidar)
308 }
309
310 /// Gets a reading from the lidar, returing a `LaserReading` object.
311 ///
312 /// # Errors
313 /// An error variant is returned in case of:
314 /// - unable to read form the serial port
315 /// - the driver is closed
316 pub fn read(&mut self) -> serialport::Result<LaserReading> {
317 let mut start_count: usize = 0;
318 let mut good_sets: u8 = 0;
319
320 let mut scan = LaserReading::new();
321
322 if self.shutting_down {
323 return Err(serialport::Error::new(
324 serialport::ErrorKind::Unknown,
325 "Driver is closed",
326 ));
327 }
328
329 loop {
330 // Wait for data sync of frame: 0xFA, 0XA0
331
332 // Read one byte
333 self.serial
334 .read_exact(std::slice::from_mut(&mut self.buff[start_count]))?;
335 //println!("start_count : {start_count} = Read {:02X?}", buff[start_count]);
336 if start_count == 0 {
337 if self.buff[start_count] == 0xFA {
338 start_count = 1
339 }
340 } else if start_count == 1 {
341 if self.buff[start_count] == 0xA0 {
342 self.serial.read_exact(&mut self.buff[2..])?;
343
344 //read data in sets of 6
345
346 for i in (0..self.buff.len()).step_by(42) {
347 if self.buff[i] == 0xFA && usize::from(self.buff[i + 1]) == (0xA0 + i / 42)
348 {
349 good_sets = good_sets.wrapping_add(1);
350
351 let b_rmp0: u16 = self.buff[i + 3] as u16;
352 let b_rmp1: u16 = self.buff[i + 2] as u16;
353
354 // motor_speed = motor_speed.wrapping_add((b_rmp0 as u32) << 8 + (b_rmp1 as u32)); // accumulate count for avg. time increment
355 let rpms = (b_rmp0 << 8 | b_rmp1) / 10;
356 scan.rpms = rpms;
357 self.rpms = rpms;
358
359 for j in ((i + 4)..(i + 40)).step_by(6) {
360 let index = 6 * (i / 42) + (j - 4 - i) / 6;
361 // Four bytes `per reading
362 let b0: u16 = self.buff[j] as u16;
363 let b1: u16 = self.buff[j + 1] as u16;
364 let b2: u16 = self.buff[j + 2] as u16;
365 let b3: u16 = self.buff[j + 3] as u16;
366
367 // Remaining bits are the range in mm
368 let range: u16 = (b3 << 8) + b2;
369
370 // Last two bytes represents the uncertanity or intensity, might also
371 // be pixel area of target...
372 // let intensity = (b3 << 8) + b2;
373 let intensity: u16 = (b1 << 8) + b0;
374
375 scan.ranges[359 - index] = range;
376 scan.intensities[359 - index] = intensity;
377 }
378 }
379 }
380
381 // self.time_increment = motor_speed/good_sets/1e8;
382 return Ok(scan);
383 } else {
384 start_count = 0;
385 }
386 }
387 }
388 }
389}
390
391#[cfg(feature = "async_smol")]
392impl LFCDLaser {
393 /// Creates a new `LFCDLaser` with the given parameters.
394 ///
395 /// # Errors
396 /// An error variant is returned in case of:
397 /// - unable to open the specified serial port
398 /// - unable to set the port to non-exclusive (only on unix)
399 pub fn new(port: String, baud_rate: u32) -> mio_serial::Result<Self> {
400 let mut serial = mio_serial::new(port.clone(), baud_rate).open_native_async()?;
401
402 #[cfg(unix)]
403 serial.set_exclusive(false)?;
404
405 // Wrapping into smol::Async to make it "async", similar to what tokio-serial does.
406 let serial = Async::new(serial).map_err(|e| {
407 mio_serial::Error::new(
408 mio_serial::ErrorKind::Unknown,
409 format!("Unable to wrap mio-serial in smol::Async: {e}"),
410 )
411 })?;
412
413 let mut lidar = Self {
414 port,
415 baud_rate,
416 shutting_down: false,
417 motor_speed: 0,
418 rpms: 0,
419 serial,
420 buff: [0u8; 2520],
421 };
422
423 lidar.start();
424
425 Ok(lidar)
426 }
427
428 /// Gets a reading from the lidar, returing a `LaserReading` object.
429 ///
430 /// # Errors
431 /// An error variant is returned in case of:
432 /// - unable to read form the serial port
433 /// - the driver is closed
434 pub async fn read(&mut self) -> mio_serial::Result<LaserReading> {
435 let mut start_count: usize = 0;
436 let mut good_sets: u8 = 0;
437
438 let mut scan = LaserReading::new();
439
440 if self.shutting_down {
441 return Err(mio_serial::Error::new(
442 mio_serial::ErrorKind::Unknown,
443 "Driver is closed",
444 ));
445 }
446
447 loop {
448 // Wait for data sync of frame: 0xFA, 0XA0
449
450 // Read one byte
451 self.serial
452 .read_exact(std::slice::from_mut(&mut self.buff[start_count]))
453 .await?;
454 //println!("start_count : {start_count} = Read {:02X?}", buff[start_count]);
455 if start_count == 0 {
456 if self.buff[start_count] == 0xFA {
457 start_count = 1
458 }
459 } else if start_count == 1 {
460 if self.buff[start_count] == 0xA0 {
461 self.serial.read_exact(&mut self.buff[2..]).await?;
462
463 //read data in sets of 6
464
465 for i in (0..self.buff.len()).step_by(42) {
466 if self.buff[i] == 0xFA && usize::from(self.buff[i + 1]) == (0xA0 + i / 42)
467 {
468 good_sets = good_sets.wrapping_add(1);
469
470 let b_rmp0: u16 = self.buff[i + 3] as u16;
471 let b_rmp1: u16 = self.buff[i + 2] as u16;
472
473 // motor_speed = motor_speed.wrapping_add((b_rmp0 as u32) << 8 + (b_rmp1 as u32)); // accumulate count for avg. time increment
474 let rpms = (b_rmp0 << 8 | b_rmp1) / 10;
475 scan.rpms = rpms;
476 self.rpms = rpms;
477
478 for j in ((i + 4)..(i + 40)).step_by(6) {
479 let index = 6 * (i / 42) + (j - 4 - i) / 6;
480 // Four bytes `per reading
481 let b0: u16 = self.buff[j] as u16;
482 let b1: u16 = self.buff[j + 1] as u16;
483 let b2: u16 = self.buff[j + 2] as u16;
484 let b3: u16 = self.buff[j + 3] as u16;
485
486 // Remaining bits are the range in mm
487 let range: u16 = (b3 << 8) + b2;
488
489 // Last two bytes represents the uncertanity or intensity, might also
490 // be pixel area of target...
491 // let intensity = (b3 << 8) + b2;
492 let intensity: u16 = (b1 << 8) + b0;
493
494 scan.ranges[359 - index] = range;
495 scan.intensities[359 - index] = intensity;
496 }
497 }
498 }
499
500 // self.time_increment = motor_speed/good_sets/1e8;
501 return Ok(scan);
502 } else {
503 start_count = 0;
504 }
505 }
506 }
507 }
508}