Controlling RGB LEDs with Go and Arduino over WiFi

Father of two. DevOps Engineer. Interested in Cloud Development, Scalability, Java, CICD, and Seeking/Sharing Knowledge
Search for a command to run...

Father of two. DevOps Engineer. Interested in Cloud Development, Scalability, Java, CICD, and Seeking/Sharing Knowledge
No comments yet. Be the first to comment.
Dive into the core concept of "Shift Left" within the DevOps landscape. While this guide zeroes in on Java, Python, and Go configurations, the insights shared are valuable for developers across the board. Ready to explore? Let's jump in! 🚀 Pro Tip!...

🍃💻 Spring Boot + Observability (OpenTracing) showcase

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.
Control an RGB LED Ring using an Arduino Nano RP2040 Connect by fetching color values from a Go-based web server.
Arduino Nano RP2040 Connect (with built-in WiFi).
NeoPixel/WS2812 LED Ring.
A simple web server in Go.
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.
Let’s dive into the Arduino code:
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";
httpClient.get("/rgb");
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();
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;
...
}
/*
* 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;
}
On the server side, we have a lightweight Go application to serve the RGB values.
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.
// 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
}
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.