DEV Community

Tommy Yu
Tommy Yu

Posted on

Reading a 905 nm Laser Rangefinder Module Over UART: Arduino, Raspberry Pi and ROS 2

Disclosure: I work for the manufacturer of these modules (ERDILRF, Chengdu). Every frame, baud rate and unit below is transcribed from the manufacturer manuals and implemented in an MIT-licensed driver. I am not publishing bench measurements here, and the last section lists what has not been verified.

The two classic first-integration failures with a UART laser rangefinder are a silent serial port and readings that are off by a factor of ten. Both usually trace back to protocol details, not wiring. This walkthrough goes in the order that saves time: identify the protocol family, wire it, then run code.

Parts

  • One ERDI 905 nm module (this write-up uses the LRF3000A1 as the worked example)
  • An Arduino with a spare hardware UART (Mega, ESP32, etc.), a Raspberry Pi with a USB-TTL adapter, or a ROS 2 machine
  • Jumper wires and a supply that matches your exact model

Step 1: Identify the protocol family

ERDI's 905 nm catalogue uses two UART protocols.

Family A: 8-byte 55 AA command/response. LR1000E2/E4, LR1200E2/E4, LR1500E2/E4, LR2000E2/E4, LRF1200A1, LRF3000A1, SPD1200N0/N2/N4, SPD1200D03. Default 115200 bps.

Family B: free-running 5C frames. LRF50H/100H/200H/300H/600H/1500H and LRF22VB/LRF50VB. Default 460800 bps (adjustable), 8 data bits, 1 stop bit, no parity, little-endian.

Step 2: Know the frames

Family A request, single shot: 55 AA 88 FF FF FF FF 84.
Reply: 55 AA 88 STA FF DIS_H DIS_L SUM. STA = 1 means success. Distance is DIS_H<<8 | DIS_L, and the manual states results are output as the real value x 10, so one count is 0.1 m.

The classic trap: transmit and reply checksums cover different bytes.

  • Transmit: low 8 bits of Function + D1..D4
  • Reply: low 8 bits of 0x55 + 0xAA + Function + D1..D4

They differ by exactly 0xFF. Use the transmit formula on replies and you will be off by one on almost every frame. A hand-computed example (constructed, not captured from hardware): 55 AA 88 01 FF 04 D2 5D sums to 0x35D, low byte 0x5D, and the distance 0x04D2 = 1234 counts = 123.4 m.

Family B frame: 5C dist_lo dist_hi check, e.g. 5C 02 11 EC. The check byte is the bitwise inverse of the sum of the bytes between the header and the check: ~(0x02 + 0x11) = 0xEC.

Units are per model, not per family. The same four bytes 5C 02 11 EC mean 43.54 m on an H-series module (centimetres) and 4.354 m on a VB-series module (millimetres). LRF1500H is the only 5-byte frame, with a 3-byte distance field.

Step 3: Wire it

Supply voltage and UART signal level are separate specifications. Do not infer one from the other.

LRF3000A1, 6-pin connector (A1002WV-S-6P) per its manual:

Pin Signal Note
1 GND Ground
2 Power 3.3-5 V DC
3 NC Reserved
4 TTL_TXD Module transmit, 3.3 V TTL
5 TTL_RXD Module receive, 3.3 V TTL
6 Enable Power ON at low level

Module TX goes to host RX, module RX to host TX, and grounds are common. Other models differ: LRF50H takes 9-36 V and LRF22VB takes 3.0-3.6 V, and some manuals do not state a signal level at all. If yours does not, get the level confirmed in writing before connecting. Never reuse another model's electrical numbers.

Step 4: Arduino

Install ErdiLRF from the Library Manager (or PlatformIO: erdi-tech/ErdiLRF).

#include <ErdiLRF.h>

ErdiLRF lrf(Serial1, ERDI_LRF3000A1);

void setup() {
  Serial.begin(115200);
  Serial1.begin(erdiDefaultBaud(ERDI_LRF3000A1));  // 115200 for family A, 460800 for family B
}

void loop() {
  ErdiMeasurement m;
  if (lrf.measure(&m) == ERDI_OK) Serial.println(m.metres, 3);
  delay(500);
}
Enter fullscreen mode Exit fullscreen mode

Note: SoftwareSerial on an Uno cannot reach 460800 bps. Family B needs a hardware UART, or set the module to a lower documented rate first.

Step 5: Raspberry Pi (Python)

pip install https://github.com/yu911517778-a11y/erdi-lrf-drivers/releases/download/v0.1.0/erdi_lrf-0.1.0-py3-none-any.whl
erdi-lrf --port /dev/ttyUSB0 --model LRF3000A1 measure
Enter fullscreen mode Exit fullscreen mode
from erdi_lrf import ErdiLRF

with ErdiLRF("/dev/ttyUSB0", model="LRF3000A1") as lrf:
    print(f"{lrf.measure().metres:.1f} m")

with ErdiLRF("/dev/ttyUSB0", model="LRF100H") as lrf:
    for m in lrf.stream():
        print(m.metres, "(out of range)" if m.out_of_range else "")
Enter fullscreen mode Exit fullscreen mode

The frame codecs are pure functions with no serial dependency, so you can unit-test them offline.

Step 6: ROS 2

cp -r ros2/erdi_lrf_ros2 ~/ros2_ws/src/
cd ~/ros2_ws && colcon build --packages-select erdi_lrf_ros2
source install/setup.bash
ros2 run erdi_lrf_ros2 range_node --ros-args -p port:=/dev/ttyUSB0 -p model:=LRF3000A1 -p rate:=10.0 -p frame_id:=lrf_link
Enter fullscreen mode Exit fullscreen mode

The node publishes sensor_msgs/Range; min_range/max_range come from the model manual, and field_of_view defaults to 0 because the protocol manuals carry no beam divergence.

Where the manuals disagree with themselves

  • SPD1200D03: the protocol section says 115200 bps, the technical table says 15200/9600, and the manual says to confirm the delivered configuration first. The driver demands an explicit baud and returns no metric distance.
  • LRF1200A1 / LRF3000A1: continuous ranging is requested with 0x89 but the recorded reply uses 0x88, and the manual declines to authorise a reply parser. Single-shot is unaffected.
  • LRF50VB: the printed serial-number request fails the manual's own checksum function.

The drivers surface these instead of guessing.

What is not verified

  • No measurement results or performance claims are made here. Read the datasheet for your model.
  • The ROS 2 node has been tested in software replay only, not against a live ROS 2 runtime with hardware.
  • The driver tests are built from the frames printed in the manuals.

Links

Vendor disclosure: the author is affiliated with ERDI TECH LTD (ERDILRF). For samples, datasheets or protocol questions, see https://erdilrf.com or tommy@erdimail.com.

Top comments (0)