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}