FixTheRobot Explore Robotics / Systems Engineering / Engineering Guides / Module 04

Engineering Guides · Module 04

Connecting a sensor to the onboard computer

A link must survive noise and restarts: stable device names, framed messages with a checksum, and a parser that resynchronises.

  • Intermediate
  • Module 04: Embedded Systems & Hardware Interfaces
  • Written for ROS 2 Jazzy where ROS 2 is used
  • Sources checked 2026-11-06

Assumptions: Numbers in the examples are illustrative and the code is executed by the site’s tests. Facts taken from a cited source are marked; other statements are general engineering knowledge and must be confirmed on the datasheet of the part you use.

1. Engineering problem

A sensor that works once on a desk is not integrated. On the robot the device may enumerate under a different name after a reboot, bytes arrive corrupted, and packets can start in the middle. A reliable connection handles all three.

2. When is this needed?

  • Writing or choosing the interface for a new sensor.
  • When a USB serial device changes its name between boots.
  • When corrupted frames produce wrong readings.

3. Selection and design decisions

Typical choices: USB or UART for simple sensors, Ethernet for high-rate sensors, CAN for distributed actuators. On Linux a USB serial device appears as /dev/ttyUSB* or /dev/ttyACM*, and stable names can be provided by /dev/serial/by-id/ links or by udev rules keyed to the device’s vendor and product IDs and serial number.

Overview
ConcernTechnique
Stable device nameUse /dev/serial/by-id/ paths or a udev rule with a symlink
PermissionsAdd the user to the group that owns the device (commonly dialout) or set the group in the udev rule
FramingSync byte, length, payload and checksum so the receiver can find packet boundaries
ResynchronisationOn a bad checksum, skip one byte and search for the next sync byte
TimestampsStamp at arrival in the driver, and use the device clock if it provides one (see the timing guides)

4. Advantages and disadvantages

  • Binary framing is compact and needs a careful parser; text framing is easy to debug and costs bandwidth.
  • Checksum vs CRC: a CRC detects more errors at a small compute cost.
  • A longer maximum length allows bigger packets and makes false headers more costly.

5. Requirements and constraints

  • A maximum payload length so a corrupt length byte cannot stall the parser.
  • A checksum or CRC on every packet.
  • The device name does not depend on enumeration order.

6. Architecture

sensor ──bytes──▶ OS serial driver ──▶ driver node: [find sync] [check length] [verify checksum] ──▶ ROS 2 message (stamped)

7. Implementation

  1. Identify the device by vendor and product ID and create a stable name.
  2. Set the serial parameters from the datasheet.
  3. Implement framing with a sync byte, length and checksum.
  4. Implement the parser to resynchronise (below) and count the bad packets.
  5. Publish the standard ROS 2 message with a correct frame_id and timestamp, and verify with ros2 topic hz and echo.
def parse(stream, max_len=16):
    out, i = [], 0
    while i + 3 <= len(stream):
        if stream[i] != 0xAA:
            i += 1
            continue
        n = stream[i + 1]
        if n > max_len:
            i += 1                                   # implausible length: not a real header
            continue
        if i + 3 + n > len(stream):
            break
        payload, chk = stream[i + 2:i + 2 + n], stream[i + 2 + n]
        if (sum(payload) + n) & 0xFF == chk:
            out.append(list(payload)); i += 3 + n
        else:
            i += 1                                   # bad checksum: resynchronise on the next byte
    return out
good = [0xAA, 3, 1, 2, 3, 9]
noise = [0x00, 0xAA, 0x77]
bad = [0xAA, 2, 5, 5, 0x00]
print(parse(noise + good + bad + good))

Printed (checked by the site’s tests):

[[1, 2, 3], [1, 2, 3]]

Reading it: the stream starts with noise, then a good packet, a packet with a bad checksum, and another good packet. The parser recovers both good packets ([1, 2, 3] twice) and skips the corrupt one. Without the maximum-length check, the noise byte 0x77 after a stray 0xAA would be read as a length of 119 and the parser would wait for bytes that never come. The packet format here is invented for illustration.

8. Real examples

  • GNSS receiver on USB: a udev symlink such as /dev/gnss0 and a driver that publishes sensor_msgs/NavSatFix.

9. Failure scenarios

  • The device appears as ttyUSB1 after a reboot and the driver opens ttyUSB0 (another device).
  • A parser that blocks waiting for a long bogus packet.
  • No permission to open the port after an update.

10. Troubleshooting

  1. Check dmesg for the device and its name.
  2. Read raw bytes with a terminal tool at the configured baud and compare with the datasheet.
  3. Count checksum failures: many means noise or wrong settings.

11. Validation

  • The parser example prints the lines shown (executed by the site tests).
  • The device name is the same after 20 reboots and re-plugs.
  • Message rate equals the configured rate with no gaps.

12. Engineering checklist

13. Further references

  • Wikipedia, UART, SPI and I²C, read 2026-11-06 (secondary sources; the I²C specification itself was not read).
  • Linux kernel documentation and your distribution’s udev manual for device naming rules.

Related on FixTheRobot