Skip to main content

Command Palette

Search for a command to run...

Part 2: Building the Arduino API & Hardware Layer

Updated
Part 2: Building the Arduino API & Hardware Layer
N

I have a Bachelor's degree in computer science from University of Delhi and I like to work on small open source projects from time to time.

In Part 1 of this series, we drew boxes on a whiteboard and called it "Architecture". Today, we will stop pretending and start building.

Most other tutorials on the internet are very basic and teach you how to build a calculator or some other basic shit, which isn’t very useful and doesn’t provide learning opportunities.

Therefore, I wanted to build something completely different, which leaves a satisfying feeling of having learned something new after you finish it. And I think, if you understand the basics well enough, it will open up the door for an unlimited number of IOT projects that can be integrated with LLMs.

I don’t know about everyone else, but building something tangible (even if it is only a stupid LED blinker) feels much more exciting than another to-do list app. Therefore, in this post, we will be entering the wild west of hardware where Ctrl+Z doesn't fix a burnt-out LED and "debugging" sometimes involves a literal multimeter.

By the end of this post, we will have a real-world agent that will enable our LangGraph agent to extend beyond the screen and interact with the real world.

The Hardware Setup

First, let’s talk about the hardware that we are using for this project.

For the brain, we have our laptop. For the brawn, we need a microcontroller. Now, you could buy an ESP32 module, a breadboard, and a bag of jumper wires. You could individually connect every single GPIO pin, debug loose connections, and look like a mad scientist trying to defuse a bomb.

I, however, value my sanity.

So, I decided to cheat. I bought the Arduino Uno R4 WiFi.
Why? Two reasons:

  1. It’s got a brain upgrade: The R4 is a beast compared to the old Uno. It has a built-in 12x8 LED Matrix (perfect for scrolling text).

  2. The "Hat" Module: I slapped a "Multifunction Shield" (a.k.a. a Hat) on top of it.

Adding Actuators to the Arduino

This shield is the lazy engineer's dream. It clicks right on top of the Arduino (we call this a "Hat" or a "Shield" in the business) and gives us instant access to a suite of Actuators.

Now, if you are new to robotics, that word is important. An actuator is the counterpart to a sensor. While sensors allow a robot to perceive the world (input), actuators allow a robot to change the world (output). They take the digital thoughts of our AI and convert them into physical energy that affects the actual environment.

With this shield, our AI gains three specific ways to manipulate physical reality:

  • A Piezo Buzzer: Turns electricity into sound waves (mostly annoying ones, but hey, it’s physics).

  • A 4-digit 7-segment Display: Turns electricity into information (perfect for our countdown timers).

  • 4 LEDs: Turns electricity into photons (visual feedback).

If you are a wizard with the Arduino ecosystem, you could probably build a much more sophisticated system than this using an ESP32, a breadboard, and a handful of resistors. But I am not a hardware wizard. I am a software guy who is afraid of soldering irons.

So, I’m sticking with this shield because it requires no wires and no breadboards. It’s just a neat little sandwich of silicon ready to obey our commands.

Writing Arduino Firmware

If you are an AI engineer, then you are probably used to writing Python code. But today, you will have to get out of your comfort zone.

We have to write C++ for this particular task. I know, I know. We’re Python people. We like garbage collection and dynamic typing. But the Arduino speaks C++, so we must speak it too. I’ve written a sketch that turns the Arduino into a dumb server. You can grab the full code here:

Here is the breakdown of the code:

The Functions

I’ve defined a few specific actions that correspond to what our Agent might want to do:

The biggest and most complex part of this operation is to handle the incoming HTTP requests and build an API server to respond to those requests. This is handled using the aptly named handleMCPRequest function that listens for incoming HTTP POST requests on the local WiFi network. It parses the JSON payload (using ArduinoJson) to find a tool_name and directs traffic.

After that, we need to configure a few helper functions to perform actions on our Arduino:

  • startLedBlinker: Does exactly what it says on the tin. It grabs the 4 LEDs on the shield and blinks them in a sequence.

  • playSound: Uses sin() math to generate a tone on the buzzer. It’s not Mozart, but it gets your attention.

  • displayText: This is the cool part. It uses the R4’s Arduino_LED_Matrix.h library to scroll text across the board. Perfect for letting the AI say "Hello World."

  • displayCountdown: A dramatic countdown from 9 to 0 on the 7-segment display.

Now, I have a confession to make. The code uses delay().

If you know embedded engineering, you are screaming at your monitor right now. delay(200) literally pauses the processor. While the LED is blinking or the countdown is running, the Arduino cannot listen for new requests. It is deaf and dumb until the loop finishes.

Why did I do this? Because writing non-blocking state machines with millis() is hard, and I wanted to see blinking lights today. It’s technical debt. I’m owning it.

Note: Making this code asynchronous so it can blink and listen simultaneously is left as an exercise to the reader. (Just like my high school math book used to say when the problem was too hard).

Creating an API Server on Arduino

Okay, let's look at the actual code running on the metal. If you’ve never looked at Arduino code before, it generally has two main parts: setup() (which runs once when you power it on) and loop() (which runs forever until you unplug it or the heat death of the universe occurs).

Here is what is happening in my sketch:

void setup()

This is where we introduce the software to the hardware. Think of it as the Arduino stretching its legs and checking its pockets before starting the day.

  • Pin Configuration (pinMode): We have to explicitly tell the chip which pins are outputs.

  • WiFi Stuff: This is standard boilerplate code. We check if the WiFi module exists, check if the firmware is updated, and then enter a while loop that tries to connect to your ssid (network name). It will just sit there and print dots until it connects.

If you are using the code provided above, then you will need to update these lines in the Arduino sketch to match your local network configuration (SSID and password).

void loop()

This is the heart of the server. It’s a "busy wait" loop, which is exactly as inefficient as it sounds, but it works.

  • The Listener: WiFiClient client = server.available(); checks if anyone (like our Python Agent) is sending a request.

  • The Manual HTTP Parser:

    • Here is where we get a bit caveman-style because we did't use a fancy library to parse the request; we read it character by character.

    • In HTTP protocol, the headers end when you hit an empty line (a newline character \n immediately following another). That’s our signal that the "Body" (the JSON payload) is about to start.

  • The Routing Logic:

    • Once we hit that empty line, we check if (header.indexOf("POST /mcp") >= 0).

    • If the request is hitting our /mcp endpoint, we know it's the Agent trying to control the hardware.

    • We find how long the message is using Content-Length, and then we forcefully read exactly that many characters into a string called body.

    • Finally, we pass that body to handleMCPRequest(client, body), which does the actual work (blinking lights, etc.).

  • The Fallback: If you just go to the IP address in your browser (which sends a GET request, not a POST), it falls into the else block and just serves a basic HTML page saying "Server is running."

Testing the "Body" before the "Brain"

Before we connect the super-intelligent AI (Part 4), we need to make sure the arms and legs actually work. There is nothing worse than debugging an LLM's hallucinations only to realize your wiring is just loose.

We are going to use cURL (or Postman if you prefer GUIs) to manually trigger the tools.

Available Tools on the Arduino:

  • start_led_blinker: Starts the disco.

  • play_sound: Beep boop.

  • display_countdown: The final countdown.

  • display_text: Shows scrolling text on LED matrix.

The "Hello World" Test

Open your terminal and make sure that your computer is connected to the same network where Arduino is connected to. After that we can run the following command in the terminal to send a simple API request to the server over the network wirelessly.

In the above command, remember to replace 192.168.0.13 with the local IP address of your Arduino board.

If you did everything right, you should see "Hello, Arduino!" scrolling across your LED matrix. Here is an image of me triggering the lights from my terminal.

In the next part, we’re going to give this body a brain by wrapping these endpoints into the Model Context Protocol (MCP) so our AI can understand how and when to press these buttons.

More from this blog

B

Bit Byte Blog - Code, Sass, and Everything in Between

14 posts

Explore the exciting world of Artificial Intelligence, Cloud Technologies, and Software Development on my blog. I write simple, engaging articles that break down tech trends into everyday language.