# Controlling RGB LEDs with Go and Arduino over WiFi

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:

1. The Arduino connects to WiFi and polls the web server every 3 seconds for RGB values.
    
2. The web server responds with JSON data containing the current RGB values.
    
3. The Arduino updates the LED ring to match the received colors.
    

## Arduino Code Breakdown

Let’s dive into the Arduino code:

1. **WiFi Connection**: The `WiFiNINA` library handles WiFi connectivity. Replace ssid and password with your network credentials.
    

```cpp
const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";
```

2. HTTP Communication
    

```cpp
httpClient.get("/rgb");
```

3. LED Control: The `Adafruit_NeoPixel` library manages the LED ring. Each color value (Red, Green, Blue) is applied to all LEDs.
    

```cpp
for (int i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, strip.Color(red, green, blue));
}
strip.show();
```

4. JSON Parsing
    

```cpp
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

```cpp
/*
 * 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**

1. **Random Initial Colors**: The server starts with a random Palestinian flag color (red, green, or white).
    
2. 2\. **Concurrency Handling**: A sync.Mutex ensures thread-safe access to the RGB struct.
    
3. **Dual Control**: You can update the RGB values either through a CLI or programmatically via HTTP GET requests.
    

### **Full Code**

```go
// 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

1. **Run the Web Server**: Compile and run the Go program on your machine:
    

```bash
go run main.go
```

2. **Connect the Arduino**: Upload the code to your Arduino Nano RP2040 and monitor the serial output to ensure it connects to WiFi.
    
3. **Test the System**: Open the RGB endpoint in your browser:
    

```http
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](mailto:hasan@alkhatib.tech).

🌐 [alkhatib.tech](https://alkhatib.tech/)
