LCOV - code coverage report
Current view: top level - AP_Proximity - AP_Proximity_RPLidarA2.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1 1 100.0 %
Date: 2026-09-09 11:02:08 Functions: 1 1 100.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : /*
      17             :  * ArduPilot device driver for SLAMTEC RPLIDAR A2 (16m range version)
      18             :  *
      19             :  * ALL INFORMATION REGARDING PROTOCOL WAS DERIVED FROM RPLIDAR DATASHEET:
      20             :  *
      21             :  * https://www.slamtec.com/en/Lidar
      22             :  * http://bucket.download.slamtec.com/63ac3f0d8c859d3a10e51c6b3285fcce25a47357/LR001_SLAMTEC_rplidar_protocol_v1.0_en.pdf
      23             :  *
      24             :  * Author: Steven Josefs, IAV GmbH
      25             :  * Based on the LightWare SF40C ArduPilot device driver from Randy Mackay
      26             :  *
      27             :  */
      28             : 
      29             : /*
      30             : 
      31             : # to connect device to SITL:
      32             : ./Tools/autotest/sim_vehicle.py -v Rover --gdb --debug -A --serial5=uart:/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0:115200
      33             : param set SERIAL5_PROTOCOL 11
      34             : param set SERIAL5_BAUD 115200
      35             : param set PRX1_TYPE 5
      36             : reboot
      37             : 
      38             : # short outer-two wires on JST plug to get it to spin
      39             : 
      40             : */
      41             : 
      42             : 
      43             : #pragma once
      44             : 
      45             : #include "AP_Proximity_config.h"
      46             : 
      47             : #if AP_PROXIMITY_RPLIDARA2_ENABLED
      48             : 
      49             : #include "AP_Proximity_Backend_Serial.h"
      50             : 
      51             : class AP_Proximity_RPLidarA2 : public AP_Proximity_Backend_Serial
      52             : {
      53             : 
      54             : public:
      55             : 
      56             :     using AP_Proximity_Backend_Serial::AP_Proximity_Backend_Serial;
      57             : 
      58             :     // update state
      59             :     void update(void) override;
      60             : 
      61             :     // get maximum and minimum distances (in meters) of sensor
      62             :     float distance_max_m() const override;
      63             :     float distance_min_m() const override;
      64             : 
      65             : private:
      66             : 
      67             :     enum class State {
      68             :         RESET = 56,
      69             :         AWAITING_RESPONSE,
      70             :         AWAITING_SCAN_DATA,
      71             :         AWAITING_HEALTH,
      72             :         AWAITING_DEVICE_INFO,
      73             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
      74             :         AWAITING_EXPRESS_DATA,
      75             : #endif
      76             :     } _state = State::RESET;
      77             : 
      78             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
      79             :     // Dense capsulated block size (EXPRESS/DENSE mode)
      80             :     static constexpr uint16_t EXPRESS_BLOCK_SIZE = 84;
      81             :     // Express/Dense capsule sync nibbles (upper 4 bits)
      82             :     static constexpr uint8_t EXPRESS_SYNC1 = 0x0A;
      83             :     static constexpr uint8_t EXPRESS_SYNC2 = 0x05;
      84             : 
      85             :     // S2 Dense Express is ~4 blocks/update at 200Hz; allow 2x headroom.
      86             :     static constexpr uint16_t EXPRESS_STREAM_BUFFER_SIZE = EXPRESS_BLOCK_SIZE * 8;
      87             :     static constexpr uint16_t EXPRESS_MAX_BYTES_CONSUME  = EXPRESS_BLOCK_SIZE * 8;
      88             : #endif
      89             : 
      90             :     // send request for something from sensor
      91             :     void send_request_for_health();
      92             :     void send_scan_mode_request();
      93             :     void send_request_for_device_info();
      94             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
      95             :     void send_express_scan_request();
      96             : #endif
      97             : 
      98             :     void parse_response_data();
      99             :     void parse_response_health();
     100             :     void parse_response_device_info();
     101             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
     102             :     void parse_response_express(const uint8_t *buf);
     103             : #endif
     104             : 
     105             :     void get_readings();
     106             :     void reset_rplidar();
     107             :     void reset();
     108             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
     109             :     void handle_express_data();
     110             :     bool express_time_exceeded(uint32_t start_us) const;
     111             :     void ensure_express_stream_space(uint16_t need);
     112             : #endif
     113             : 
     114             :     // remove bytes from read buffer:
     115             :     void consume_bytes(uint16_t count);
     116             : 
     117             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
     118             :     // verify Dense capsulated (Express) block checksum
     119             :     bool verify_cabin_checksum(const uint8_t *buf, size_t len);
     120             : #endif
     121             : 
     122             :     uint8_t _sync_error;
     123             :     uint16_t _byte_count;
     124             : 
     125             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
     126             :     uint8_t _express_stream[EXPRESS_STREAM_BUFFER_SIZE];
     127             :     uint16_t _express_stream_len;
     128             : #endif
     129             : 
     130             :     // request related variables
     131             :     uint32_t  _last_distance_received_ms;     ///< system time of last distance measurement received from sensor
     132             :     uint32_t  _last_reset_ms;
     133             : 
     134             :     // face related variables
     135             :     AP_Proximity_Boundary_3D::Face _last_face;///< last face requested
     136             :     float _last_angle_deg;                    ///< yaw angle (in degrees) of _last_distance_m
     137             :     float _last_distance_m;                   ///< shortest distance for _last_face
     138             :     bool _last_distance_valid;                ///< true if _last_distance_m is valid
     139             : 
     140             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
     141             :     // use Dense EXPRESS_SCAN path
     142             :     bool _use_dense_express = false;
     143             : #endif
     144             : 
     145             :     struct PACKED _device_info {
     146             :         uint8_t model;
     147             :         uint8_t firmware_minor;
     148             :         uint8_t firmware_major;
     149             :         uint8_t hardware;
     150             :         uint8_t serial[16];
     151             :    };
     152             : 
     153             :     struct PACKED _sensor_scan {
     154             :         uint8_t startbit      : 1;            ///< on the first revolution 1 else 0
     155             :         uint8_t not_startbit  : 1;            ///< complementary to startbit
     156             :         uint8_t quality       : 6;            ///< Related the reflected laser pulse strength
     157             :         uint8_t checkbit      : 1;            ///< always set to 1
     158             :         uint16_t angle_q6     : 15;           ///< Actual heading = angle_q6/64.0 Degree
     159             :         uint16_t distance_q2  : 16;           ///< Actual Distance = distance_q2/4.0 mm
     160             :     };
     161             : 
     162             :     struct PACKED _sensor_health {
     163             :         uint8_t status;                       ///< status definition: 0 good, 1 warning, 2 error
     164             :         uint16_t error_code;                  ///< the related error code
     165             :     };
     166             : 
     167             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
     168             :     // Express/Dense capsule sync header (2 bytes)
     169             :     struct PACKED _express_header {
     170             :         uint8_t sync1_checksum_low;   // upper nibble = 0xA
     171             :         uint8_t sync2_checksum_high;  // upper nibble = 0x5
     172             :     };
     173             : #endif
     174             : 
     175             :     struct PACKED _descriptor {
     176             :         uint8_t bytes[7];
     177             :     };
     178             : 
     179             :     // we don't actually *need* to store this.  If we don't, _payload
     180             :     // can be just 7 bytes, but that doesn't make for efficient
     181             :     // reading.  It also simplifies the state machine to have the read
     182             :     // buffer at least this big.  Note that we force the buffer to a
     183             :     // larger size below anyway.
     184             :     struct PACKED _rpi_information {
     185             :         uint8_t bytes[63];
     186             :     };
     187             : 
     188             :     union PACKED {
     189      147138 :         DEFINE_BYTE_ARRAY_METHODS
     190             :         _sensor_scan sensor_scan;
     191             :         _sensor_health sensor_health;
     192             :         _descriptor descriptor;
     193             :         _rpi_information information;
     194             :         _device_info device_info;
     195             : #if AP_PROXIMITY_RPLIDAR_EXPRESSSCAN_ENABLED
     196             :         _express_header express_header;
     197             : #endif
     198             :         uint8_t forced_buffer_size[256]; // just so we read(...) efficiently
     199             :     } _payload;
     200             :     static_assert(sizeof(_payload) >= 63, "Needed for parsing out reboot data");
     201             : 
     202             :     enum class Model {
     203             :         UNKNOWN,
     204             :         A1,
     205             :         A2,
     206             :         A2M12,
     207             :         C1,
     208             :         S1,
     209             :         S2,
     210             :     } model = Model::UNKNOWN;
     211             : 
     212             :     bool make_first_byte_in_payload(uint8_t desired_byte);
     213             : };
     214             : 
     215             : #endif // AP_PROXIMITY_RPLIDARA2_ENABLED

Generated by: LCOV version 1.14