How to Code a Robot: Hardware, Firmware and ROS 2 Steps

A chassis rests on the bench. Two motors spin when you touch wires to a battery, and the example code on your laptop does not quite fit. A robot moves reliably only when its software parts work together, so build and test each layer before depending on the next.

Plan it as a distributed system from the first line of code. At the bottom, real-time control runs on a microcontroller, reading sensors and controlling motion within safe speed bounds, with a stop path ready before navigation logic. In the middle, planning and perception use sensor information to choose movement. At the top, application behavior decides what the robot should do. Safety limits constrain the bottom layer; they are not a separate layer. This structure helps isolate faults: a motor-control problem can be found on the bench rather than hidden in navigation code. Skip the order, and faults across layers become harder to separate.

The Build Order From Parts to a Moving Robot

Work through these stages in order. Each one ends with something you can test.

  1. Define the job and its limits. Write a one-page specification covering payload, indoor or outdoor floor, top speed, nearby people, and battery runtime. You're done when someone else could read it and know what the robot must never do.
  2. Choose computing and motor hardware. Pick your microcontroller, Linux computer, motors, and driver. Check the motor driver rating against motor stall current before you buy. You're done when every part has a reason to be there.
  3. Set up the development environment. Confirm the microcontroller answers on a serial port and the Linux computer boots and responds. A blink sketch and a login prompt are enough.
  4. Write bench firmware. Drive the motors and read the encoders with the robot propped up. You're done when both wheels turn both ways and stop on command.
  5. Add high-level control in ROS 2. Run your nodes on the Linux computer and connect them to the firmware. You're done when a velocity command from ROS 2 moves the wheels predictably.
  6. Test in simulation, then wheels off the ground. Catch logic errors where they can't break anything.
  7. Run a supervised field trial. Drive slowly in a cleared space with a stop method within reach.

An infographic titled The Build Order illustrating six numbered steps for constructing a functional moving robot.

The order matters because each stage produces a testable output. Treating your build like a staged software project workflow with clear checkpoints keeps faults small and locatable.

Your code will divide into three layers throughout. Drivers and real-time control sit at the bottom. Planning and perception sit in the middle. Application behavior, such as navigation or inspection, sits at the top.

Choosing Hardware and Platforms for Your Robot

One principle decides most of your hardware choices. Put tight timing on a microcontroller and heavy computing on a Linux computer. Motor control needs consistent, fast responses. Camera processing, mapping, and logging need memory and processing power. Asking one board to do both well is where beginners lose weeks.

What to Check Before You Buy

Start with the electrical facts, not the brand names.

  • Supply voltage and current draw. Your battery, motors, and driver must agree. A driver that overheats under load will cut out in ways that look like software bugs.
  • Encoder resolution. Encoders tell your code how far each wheel turned. Coarse feedback makes straight-line driving and distance control harder to tune.
  • Sensor fit. An IMU helps with heading, a lidar helps with obstacle distance, and a camera helps with recognition. Each one adds data your Linux computer must handle, so don't mount sensors you have no plan to process.
  • Wi-Fi telemetry. Wireless logging is useful during testing because you can watch values without a cable tethering the robot to your desk.

Computing Platforms Compared for a First Robot

Platform Runs Best for Main limit
8-bit Arduino-class microcontroller Single program, no operating system Motor PWM, encoder counting, simple sensors Little memory; cannot run ROS 2 nodes directly
ESP32-class board 32-bit microcontroller with Wi-Fi and Bluetooth Wireless telemetry and small mobile robots Less mature tooling for complex control stacks
Raspberry Pi-class single-board computer Linux Running ROS 2 nodes, camera processing, logging Not a hard real-time controller; motor timing belongs on a microcontroller
Jetson-class AI module Linux with GPU On-robot neural-network perception Higher power draw and cost; overkill for line followers
Pre-built ROS-compatible mobile base Vendor firmware and drivers Skipping chassis and drive design Limited access to drive internals and wiring

The Real Tradeoffs

A pre-built base saves months of mechanical work. You skip chassis design, motor mounting, and much of the wiring, and you start writing behavior code sooner. The cost is control. If the vendor's firmware doesn't expose the setting you need, you may not be able to change how the drive electronics behave.

A bare board teaches you more. You'll understand every wire and every line of firmware, and that knowledge transfers to your next robot. The cost is your time, because mechanical alignment, power distribution, and driver selection all become your problems.

Practical rule: the motor driver is a separate purchase that your microcontroller can't replace. Check its rated current against your motor's stall current before you order anything.

For a first robot, keep it simple. Use a two-wheel base, one microcontroller for motors and encoders, and one Linux computer for ROS 2. That combination is enough to learn every layer without fighting exotic hardware. Add a Jetson-class module only when you have a perception task that genuinely needs a GPU. Add more sensors only when a test shows you need them.

Plenty of beginners buy for the robot they imagine building next year. Buy for the robot on your bench instead. You'll finish sooner, and the lessons will be clearer.

Setting Up Your Development Environment

Setup feels like overhead until a version mismatch eats your weekend. Do it once, carefully, and write down what you installed.

  1. Install firmware tooling first. Use the Arduino IDE or PlatformIO inside VS Code for microcontroller firmware. Confirm the board appears on a serial port and uploads a blink sketch. This proves your cable, driver, and board selection are all correct before you write motor code.
  2. Install Ubuntu LTS on the Linux computer. Choose the version your ROS 2 distribution officially supports, and check the ROS 2 documentation for supported pairings before you start. The pairing matters more than having the newest release of either one.
  3. Install ROS 2 from the official packages. Load its setup script in each new terminal so your shell can find ROS 2 commands and libraries. Forgetting this step produces "command not found" errors that confuse almost every beginner once.
  4. Create a colcon workspace and put it under Git from day one. Keep a separate folder for firmware inside the same repository. Version control means a bad change is an inconvenience instead of a lost afternoon, and it records which code was running during a good test.
  5. Install a simulator such as Gazebo. Confirm a demo world launches. You'll use simulation later to test navigation logic without risking hardware, so it's worth knowing it works before you need it.
  6. Pin your versions and write a short README. Record the board, operating system, ROS 2 distribution, and driver versions. Future you, returning to the project after a month away, will be grateful.

The Version-Mismatch Trap

Mixing operating system and ROS 2 versions produces errors that look exactly like code bugs. Packages fail to build, messages don't match, and nodes refuse to talk to each other.

That's why step two comes before step three. Pick the supported pairing, install it cleanly, and resist upgrading mid-project unless you have a specific reason.

There's a tradeoff between Raspberry Pi OS and Ubuntu for ROS 2 work. Raspberry Pi OS is comfortable on Pi hardware, but Ubuntu matches the officially supported ROS 2 path more directly, which means installation guides and package availability line up with the documentation. For a first ROS 2 robot, following the documented path usually saves more time than it costs.

Your Windows laptop still has a role. It's typically a workstation for writing firmware, viewing visualizations, and editing code, rather than the robot's onboard computer. Keep the robot's computer dedicated to running the robot.

Writing Firmware for Motors and Sensors

Start with the fact that surprises many newcomers. A microcontroller pin can't drive a motor directly. The pin produces a signal, not the current a motor needs. A motor driver sits between them, switching motor current on and off under the microcontroller's control.

The Arduino PWMMotorControl library documentation describes controlling brushed DC motors with pulse-width modulation, or PWM, and optionally using encoders to drive fixed distances. PWM regulates effective motor power by varying the duty cycle of an electrical signal, so your code commands different speeds without continuously changing the supply voltage. Encoders add feedback about wheel or shaft movement, letting your program compare the requested distance with the distance traveled and correct errors.

A diagram illustrating the four-step process for controlling a brushed DC motor using an Arduino microcontroller.

For a basic two-wheel robot, this supports separate left and right motor commands. That's what lets the robot steer by running one wheel faster than the other.

The Firmware Procedure

  1. Wire the driver between the microcontroller and the motor. Use a separate motor supply and a shared ground. The separate supply keeps motor current away from your logic circuits, and the shared ground gives every signal a common reference.
  2. Set the PWM pin and direction pins, then test at a low duty cycle. Do this with the wheels off the ground. A low duty cycle reveals reversed wiring gently instead of launching the robot off the bench.
  3. Read the encoders with interrupts. Encoder pulses arrive whether your code is ready or not. Interrupts let the microcontroller count pulses as they happen, so counts aren't lost while it's doing other work.
  4. Implement a velocity loop, typically PID. The loop compares commanded speed with measured speed and adjusts the PWM output. Tune it with small gain steps. Large jumps make it impossible to tell which change caused which behavior.
  5. Clamp commanded speed to a configured maximum in firmware. Don't enforce the speed limit only on the host computer. If the host sends a bad value, crashes, or gets disconnected, the firmware clamp is the last thing standing between a software error and an unexpectedly fast robot.
  6. Add a watchdog that stops the motors when commands stop arriving. Pick a timeout that suits your robot. If no new command arrives within that window, the motors stop on their own. This one feature prevents the classic failure where a robot keeps driving because the controlling program froze.
  7. Define a simple serial message format. You need velocity commands flowing down to the microcontroller and encoder feedback flowing up. Keep the format small and fixed so both sides can parse it reliably.

Mistakes You'll Probably Meet

Floating grounds cause erratic behavior that's hard to reproduce. If your microcontroller and driver don't share a ground reference, signals become meaningless. Check this first when behavior seems random.

Motor noise can corrupt sensor readings. Brushed motors generate electrical noise, and sensitive sensors sharing the same supply or wiring path may report garbage. Separate supplies and tidy wiring help more than clever filtering code written to compensate for bad wiring.

Missing flyback protection risks damaging your electronics when motor current switches off. Many driver boards include this protection, but confirm yours does instead of assuming.

Firmware guarantees bounded, stoppable motion. The host decides where to go.

Keep that division clean. Your firmware should refuse unreasonable commands, enforce its speed cap, and stop when left alone, no matter what the Linux computer asks for. Route selection, obstacle decisions, and task logic belong upstairs. When something goes wrong at the top, the bottom layer still protects the hardware and anyone standing nearby.

Beginners often want to move intelligence into firmware because it feels closer to the machine. Resist that. Firmware that's small, predictable, and boring is firmware you can trust. Every clever behavior you add down there is harder to debug, because your tools on a microcontroller are limited compared with a Linux computer.

Test each numbered step before moving to the next. A wheel that turns at a low duty cycle tells you the wiring works. Encoder counts that rise and fall correctly tell you feedback works. A watchdog that stops the motors when you unplug the cable tells you your safety net works. Those three confirmations carry you into ROS 2 with a base you can rely on.

Connecting Control Software with ROS 2

A node is a small program with one job, such as reading a lidar, computing a velocity, or driving a motor. Your robot's software is a graph of these nodes passing messages to each other.

Follow the sensor-to-motor chain. A sensor node publishes distance measurements on a topic. A controller node subscribes, works out what to do, and publishes velocity commands. A motor node subscribes to those commands and sends them to your firmware.

A diagram illustrating a robotic data flow from a Lidar node to a velocity node and motor node.

ROS 2 offers three communication patterns, and choosing the wrong one creates subtle problems. The ROS 2 documentation on topics, services, and actions explains that topics use a publish and subscribe model for continuous data such as camera readings, lidar scans, or robot status. Topics suit streams because the publisher doesn't wait for anyone.

Services use synchronous request and response communication for short operations that need an immediate result, such as a calibration request or resetting odometry. The caller waits for the reply. That's fine for something quick, but a service that runs for several seconds freezes the calling node and makes cancellation awkward.

Actions suit long tasks, such as driving to a target pose. They can provide progress feedback, accept cancellation, and return a final result. The cost is more code, because you must handle cancelled and failed goals properly.

For language choice, Python with rclpy is faster to prototype in, while C++ with rclcpp fits where latency matters. Many robots use both.

Two disciplines prevent most integration pain. Message types and topic names must match exactly across nodes, and interface definitions belong in version control alongside the code that uses them.

Before you write a node, list every topic, service, and action it will use. Mark each one as continuous data, short call, or long task. That small exercise forces the right pattern choice while it's still cheap to change.

Testing and Debugging Before the Robot Moves Freely

Treat testing as a ladder with gates. Don't climb to the next rung until the current one passes.

Run navigation and control code in simulation with a fake sensor feed first. Then run the firmware on a bench with the wheels off the ground. Then drive on the floor at low speed with a person holding the stop. Test the stop path and the speed cap before any normal-speed run. Your firmware section set those limits. Here you verify they trigger as intended.

A flowchart outlining four critical safety steps for testing and debugging autonomous robots before full operation.

You can inject test commands with the ROS 2 topic publishing command before the full sensor stack is connected, and record runs with a bag file so you can replay a failure later instead of trying to recreate it. A structured approach to software QA management for staged testing helps you keep those gates consistent across sessions.

For a speed reference, OSHA guidance on robot programming and teaching cites an ANSI recommendation of no more than 10 inches per second, or 250 millimeters per second. That guidance is written for industrial robots. For a small mobile robot, treat it as a conservative starting point rather than a legal threshold, and confirm any obligations with a qualified safety professional.

Common Robot Faults, Causes and Fixes

Symptom Likely cause First fix
Motor hums but does not turn Duty cycle too low for the load, or driver not powered Raise minimum PWM slightly and confirm the driver supply voltage
Wheel turns the wrong way Motor leads reversed or sign inverted in firmware Correct the sign in firmware so wiring stays consistent with the code
Robot drifts in a straight line Left and right wheel diameters or encoder calibration differ Measure distance travelled and calibrate counts per revolution for each wheel
Host nodes cannot see each other Different ROS domain setting, mismatched topic names or message types Check the topic list on both machines and confirm the same domain and message definitions
Robot jerks or oscillates Gains too high or noisy encoder readings Lower the proportional gain and filter the encoder signal before tuning again
Robot keeps moving after a stop request Stop message not handled, or watchdog timeout disabled Test the stop path with the wheels off the ground before any floor test

Finish every session setup with a pre-run checklist. Confirm the stop works, the speed cap is set, the area is clear, and the battery is charged. It takes a minute, and it catches the mistakes that happen when you're eager to see the robot move.

Deciding Whether to Build In-House or Hire Developers

A robot project splits into specialist work faster than many engineers expect. There's embedded firmware, robotics middleware, perception, controls, DevOps for simulation and logging, and QA for hardware-in-the-loop testing. One talented generalist can cover several of these on a small robot. A product usually can't rely on that.

Signals That Favor Outside Help

  • Safety-critical behavior near people. If your robot shares space with staff, customers, or children, the safety layer deserves experienced hands.
  • Real-time control beyond a first prototype. Tight timing guarantees are a specialty, not a side task.
  • A fleet needing remote updates. Updating and monitoring many robots is a DevOps problem as much as a robotics one.
  • A deadline your team can't absorb. Hiring for one stage beats burning out the people you have.

Signals That Favor Building In-House

Building in-house makes sense when learning is the goal, when you're building a single small robot, or when your team already writes firmware and ROS code. In those cases, outside help may cost more in coordination than it saves in effort.

AI-generated code can speed up a prototype, and it's truly useful for boilerplate and examples. It doesn't replace controls and safety expertise. Generated code still needs someone who can judge whether a velocity loop is stable and whether a stop path works.

When you do look outside, structured embedded systems engineer interview questions help you separate engineers who have shipped hardware from those who have only simulated it.

Questions to Ask a Robotics Development Provider

Question to ask Strong answer Red flag
Which robotics stack have you shipped on? Named platforms, middleware version, and at least one deployed robot General claims of robotics experience with no stack named
How do you test on hardware? Describes bench, stop-path, and floor test steps before normal speed Testing described only in simulation
Who owns the safety limits? Names who sets speed caps and stop logic and how changes are reviewed Safety limits left to whoever happens to write the code
How are code and access handed over? Repository ownership, documentation, and credential transfer defined in writing Code and accounts kept in the vendor's name

Your concrete next step is simple. Write your one-page robot specification, list the stages you can't staff, and decide which gap to fill first.

The Takeaway

Coding a robot is a build order, not a single program. Define the job and its limits first. Choose hardware that keeps timing-critical motor control on a microcontroller and heavy computing on a Linux computer. Set up your tools with matching versions so environment errors don't masquerade as code bugs.

Write firmware that bounds and stops motion on its own, connect the layers through ROS 2 nodes with clearly defined messages, and test the stop path before any normal-speed run. The speed and stop figures in this guide are starting points for small robots. Industrial safety rules may apply to larger or shared-space machines, so confirm your obligations with a qualified safety professional.

Now write that one-page specification and list the stages you can't staff in-house. Decide which gap to fill first, whether that's firmware, ROS 2 integration, or hardware testing, and then build in the order described above.


If your specification shows a gap in embedded firmware, ROS 2, or hardware testing, talk to HireDevelopers.com about pre-vetted engineers with robotics and DevOps experience. Bring your one-page specification to the consultation, name the stage you can't staff, and ask how candidates test on real hardware before you commit.

Victor

Victor

Author

Senior Developer Spotify at HireDevelopers

As a Senior Developer at Spotify and part of the HireDevelopers talent network, I bring real-world experience from scaling global platforms to every project I take on. Writing on behalf of HireDevelopers, I share insights from the field—what actually works when building fast, reliable, and user-focused software at scale.

Related Articles

.. .. ..

Ready to make the switch to HireDevelopers?

Hire today
7 day risk-free trial

Want to learn more?

Book a call