Controlling an Arduino Ring Using a Go Web Server


Sometimes, the best ideas come from playing around with simple projects. I recently set up an Arduino Nano RP2040 Connect to control an RGB LED ring via a web server written in Go. It turned out to be a fun and educational dive into IoT and web programming.
In this blog, I’ll walk you through the setup, share the code, and reflect on what I learned.
What We'll Build!
Control an RGB LED Ring using an Arduino Nano RP2040 Connect by fetching color values from a Go-based web server.
Componenets:
Arduino Nano RP2040 Connect (with built-in WiFi).
NeoPixel/WS2812 LED Ring.
A simple web server in Go.
How it works:
The Arduino connects to WiFi and polls the web server every 3 seconds for RGB values.
The web server responds with JSON data containing the current RGB values.
The Arduino updates the LED ring to match the received colors.
Arduino Code Breakdown
Let’s dive into the Arduino code:
- WiFi Connection: The
WiFiNINA
library handles WiFi connectivity. Replace ssid and password with your network credentials.
const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";
- HTTP Communication
httpClient.get("/rgb");
- LED Control: The
Adafruit_NeoPixel
library manages the LED ring. Each color value (Red, Green, Blue) is applied to all LEDs.
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(red, green, blue));
}
strip.show();
- JSON Parsing
bool parseRGB(String json, int &red, int &green, int &blue) {
int redIndex = json.indexOf("\"red\":") + 6;
int greenIndex = json.indexOf("\"green\":") + 8;
int blueIndex = json.indexOf("\"blue\":") + 7;
...
}
Full Code
/*
* Setup:
* - RGB LED Ring (WS2812/NeoPixel compatible) connected to D2
* - Arduino Nano RP2040 Connect with built-in WiFi
* - Ensure the web server is running at the specified IP and port.
*/
#include <ArduinoHttpClient.h>
#include <WiFiNINA.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
// WiFi credentials
const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";
// Server details (ipconfig getifaddr en0)
const char* serverAddress = "web_server_ip";
const int port = web_server_port;
WiFiClient wifiClient;
HttpClient httpClient = HttpClient(wifiClient, serverAddress, port);
// LED setup
#define LED_PIN 25
#define LED_COUNT 24
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Function prototypes
void connectToWiFi();
void fetchAndSetRGB();
bool parseRGB(String json, int &red, int &green, int &blue);
void setup() {
Serial.begin(9600);
strip.begin();
strip.show();
strip.setBrightness(50);
connectToWiFi();
}
void loop() {
fetchAndSetRGB();
delay(3000); // Fetch every 3 seconds
}
void connectToWiFi() {
Serial.print("Connecting to WiFi...");
while (WiFi.begin(ssid, password) != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("Connected!");
}
void fetchAndSetRGB() {
Serial.println("Fetching RGB values...");
httpClient.get("/rgb");
int statusCode = httpClient.responseStatusCode();
if (statusCode != 200) {
Serial.print("Failed to fetch RGB values. Status code: ");
Serial.println(statusCode);
return;
}
String response = httpClient.responseBody();
Serial.print("Response: ");
Serial.println(response);
int red, green, blue;
if (parseRGB(response, red, green, blue)) {
for(int i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(red, green, blue));
}
strip.show();
Serial.println("RGB values set successfully.");
Serial.print("Red: ");
Serial.print(red);
Serial.print(", Green: ");
Serial.print(green);
Serial.print(", Blue: ");
Serial.println(blue);
} else {
Serial.println("Failed to parse RGB values.");
}
}
bool parseRGB(String json, int &red, int &green, int &blue) {
int redIndex = json.indexOf("\"red\":") + 6;
int greenIndex = json.indexOf("\"green\":") + 8;
int blueIndex = json.indexOf("\"blue\":") + 7;
if (redIndex == -1 || greenIndex == -1 || blueIndex == -1) return false;
red = json.substring(redIndex, json.indexOf(',', redIndex)).toInt();
green = json.substring(greenIndex, json.indexOf(',', greenIndex)).toInt();
blue = json.substring(blueIndex, json.indexOf('}', blueIndex)).toInt();
return true;
}
Go Web Server
On the server side, we have a lightweight Go application to serve the RGB values.
Key Features
Random Initial Colors: The server starts with a random Palestinian flag color (red, green, or white).
2. Concurrency Handling: A sync.Mutex ensures thread-safe access to the RGB struct.
Dual Control: You can update the RGB values either through a CLI or programmatically via HTTP GET requests.
Full Code
// Usage:
// 1. Run the program: go run main.go
// 2. Web server starts on port 8080. Get RGB values with a GET request to http://localhost:8080/rgb.
// 3. Set new RGB values in the CLI using format R,G,B (e.g., 255,0,0 for red). Type 'exit' to quit.
// PS. get the server IP by running ipconfig getifaddr en0
// then access the server from another device using the IP and port 8080
package main
import (
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"os"
"strings"
"sync"
"time"
)
type RGB struct {
Red int `json:"red"`
Green int `json:"green"`
Blue int `json:"blue"`
}
var (
rgb RGB
mu sync.Mutex // To handle concurrent access
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
)
func main() {
// Set default RGB values to randomly pick one of the Palestinian flag colors, since, why not?
switch rnd.Intn(3) {
case 0:
rgb = RGB{Red: 0, Green: 100, Blue: 0} // Dark Green
case 1:
rgb = RGB{Red: 139, Green: 0, Blue: 0} // Dark Red
case 2:
rgb = RGB{Red: 255, Green: 255, Blue: 255} // White
}
fmt.Println("Setting the Palestinian flag colors, since, why not?")
// Start the web server in a separate goroutine
go func() {
http.HandleFunc("/rgb", handleRGB)
fmt.Println("Starting server on port 8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}()
// CLI loop for setting RGB values
reader := os.Stdin
for {
fmt.Print("Enter RGB values (format: R,G,B) or type 'exit' to quit: ")
var input string
fmt.Fscanln(reader, &input)
input = strings.TrimSpace(input)
if strings.ToLower(input) == "exit" {
fmt.Println("Exiting...")
break
}
if setRGB(input) {
fmt.Printf("RGB updated to: %d, %d, %d\n", rgb.Red, rgb.Green, rgb.Blue)
} else {
fmt.Println("Invalid input. Use format R,G,B with values between 0 and 255.")
}
}
}
// Handle HTTP GET requests to /rgb
func handleRGB(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
mu.Lock()
defer mu.Unlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(rgb)
}
// Set the RGB values based on CLI input
func setRGB(input string) bool {
parts := strings.Split(input, ",")
if len(parts) != 3 {
return false
}
var r, g, b int
_, err := fmt.Sscanf(input, "%d,%d,%d", &r, &g, &b)
if err != nil || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 {
return false
}
mu.Lock()
defer mu.Unlock()
rgb = RGB{Red: r, Green: g, Blue: b}
return true
}
Putting it All Together
- Run the Web Server: Compile and run the Go program on your machine:
go run main.go
Connect the Arduino: Upload the code to your Arduino Nano RP2040 and monitor the serial output to ensure it connects to WiFi.
Test the System: Open the RGB endpoint in your browser:
http://<your_server_ip>:8080/rgb
This project combines IoT and web programming in a fun way. Got questions or feedback? Drop a comment below or email me at hasan@alkhatib.tech
Subscribe to my newsletter
Read articles from Hasan Alkhatib directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Hasan Alkhatib
Hasan Alkhatib
Father of two. DevOps Engineer. Interested in Cloud Development, Scalability, Java, CICD, and Seeking/Sharing Knowledge