Control a NeoPixel RGB Strip with Opera GX (MQTT + Raspberry Pi + ESP32 – Full Working Guide)
This article documents a fully tested and working setup where changing the Accent Color in Opera GX instantly changes the color of a physical NeoPixel (WS2812) RGB strip.
Opera GX → MQTT (Mosquitto on Raspberry Pi) → Python Bridge → USB Serial → ESP32 → NeoPixel RGB Strip
1. Requirements
1.1 Hardware
- Raspberry Pi (any model)
- ESP32 board
- NeoPixel / WS2812 RGB strip (3 wires: 5V, GND, DATA)
- USB data cable (ESP32 ↔ Raspberry Pi)
- 5V power supply (required for larger LED counts)
1.2 Wiring
| NeoPixel Strip | ESP32 |
|---|---|
| 5V | 5V |
| GND | GND |
| DATA | GPIO13 (recommended) or GPIO4 |
2. ESP32 Firmware (Final Working Code)
This firmware listens for text commands over Serial
(RED, GREEN, BLUE, OFF, …),
controls the RGB strip, prints READY at boot,
and confirms each command with OK ....
#include <Adafruit_NeoPixel.h>
#define LED_PIN 13 // Stable pin for NeoPixel
#define LED_COUNT 30 // Change to match your strip length
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
String line;
void setAll(uint8_t r, uint8_t g, uint8_t b) {
for (int i = 0; i < LED_COUNT; i++) {
strip.setPixelColor(i, strip.Color(r, g, b));
}
strip.show();
}
void handleCmd(String s) {
s.trim();
s.toUpperCase();
if (s == "OFF") { setAll(0,0,0); Serial.println("OK OFF"); return; }
if (s == "RED") { setAll(255,0,0); Serial.println("OK RED"); return; }
if (s == "GREEN") { setAll(0,255,0); Serial.println("OK GREEN"); return; }
if (s == "BLUE") { setAll(0,0,255); Serial.println("OK BLUE"); return; }
if (s == "WHITE") { setAll(255,255,255); Serial.println("OK WHITE"); return; }
if (s == "YELLOW") { setAll(255,255,0); Serial.println("OK YELLOW"); return; }
if (s == "CYAN") { setAll(0,255,255); Serial.println("OK CYAN"); return; }
if (s == "MAGENTA") { setAll(255,0,255); Serial.println("OK MAGENTA"); return; }
if (s == "ORANGE") { setAll(255,120,0); Serial.println("OK ORANGE"); return; }
if (s == "PURPLE") { setAll(140,0,255); Serial.println("OK PURPLE"); return; }
Serial.print("ERR ");
Serial.println(s);
}
void setup() {
Serial.begin(115200);
delay(2000); // USB/Serial stabilization
strip.begin();
strip.setBrightness(80);
strip.clear();
strip.show();
Serial.println("READY");
}
void loop() {
while (Serial.available()) {
char c = Serial.read();
if (c == '\r') continue;
if (c == '\n') {
if (line.length()) handleCmd(line);
line = "";
} else {
line += c;
if (line.length() > 64) line = "";
}
}
}
3. Raspberry Pi Setup
3.1 Install Required Packages
sudo apt update
sudo apt install -y mosquitto mosquitto-clients python3-serial python3-paho-mqtt
3.2 Enable MQTT Broker
sudo systemctl enable mosquitto
sudo systemctl start mosquitto
sudo systemctl status mosquitto
3.3 Find ESP32 Serial Port
ls -l /dev/serial/by-id/
You should see a stable path like:
/dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
4. Python Bridge: MQTT → Serial
Create the Script
nano ~/gx_rgb_serial.py
Full Working Code
#!/usr/bin/env python3
import serial
import time
import paho.mqtt.client as mqtt
SERIAL_PORT = "/dev/serial/by-id/usb-1a86_USB_Serial-if00-port0"
BAUD = 115200
MQTT_BROKER = "localhost"
MQTT_TOPIC = "gx/rgb"
print("[SERIAL] using:", SERIAL_PORT)
ser = serial.Serial(SERIAL_PORT, BAUD, timeout=1)
time.sleep(0.5)
# Wait for READY from ESP32
start = time.time()
while time.time() - start < 5:
line = ser.readline().decode(errors="ignore").strip()
if line:
print("[SERIAL] rx:", line)
if line == "READY":
break
def on_connect(client, userdata, flags, rc):
print("[MQTT] connected:", rc)
client.subscribe(MQTT_TOPIC)
def on_message(client, userdata, msg):
cmd = msg.payload.decode().strip()
print("[MQTT] cmd:", cmd)
ser.write((cmd + "\n").encode())
ser.flush()
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_BROKER, 1883, 60)
client.loop_forever()
5. Find Raspberry Pi IP Address (Host)
hostname -I
Example output:
192.168.1.50
Use this IP address in Opera GX as the MQTT Host with port 1883. Both devices must be on the same local network.
6. Opera GX → RGB Color Bridge
Opera GX publishes its accent color as a HEX value (for example #FF9900).
This script converts it to the nearest named color and sends it to gx/rgb.
nano ~/opera_to_gx_rgb.py
#!/usr/bin/env python3
import paho.mqtt.client as mqtt
MQTT_BROKER = "localhost"
OPERA_TOPIC = "opera/+/sensor/accent_color"
OUT_TOPIC = "gx/rgb"
PALETTE = {
"RED": (255,0,0), "GREEN": (0,255,0), "BLUE": (0,0,255),
"WHITE": (255,255,255), "YELLOW": (255,255,0),
"CYAN": (0,255,255), "MAGENTA": (255,0,255),
"ORANGE": (255,120,0), "PURPLE": (140,0,255)
}
def hex_to_rgb(h):
h = h.lstrip("#")
return tuple(int(h[i:i+2],16) for i in (0,2,4))
def nearest(rgb):
return min(PALETTE, key=lambda k:
sum((rgb[i]-PALETTE[k][i])**2 for i in range(3)))
def on_connect(c,u,f,rc):
c.subscribe(OPERA_TOPIC)
def on_message(c,u,m):
rgb = hex_to_rgb(m.payload.decode())
c.publish(OUT_TOPIC, nearest(rgb))
c = mqtt.Client()
c.on_connect = on_connect
c.on_message = on_message
c.connect(MQTT_BROKER,1883,60)
c.loop_forever()
7. Run Everything
python3 ~/gx_rgb_serial.py
python3 ~/opera_to_gx_rgb.py
8. Increasing LED Count
#define LED_COUNT 60
WS2812 LEDs can draw up to ~60mA per LED at full white. Use an external 5V power supply for more than 30–40 LEDs.
References
- Adafruit NeoPixel Library: https://github.com/adafruit/Adafruit_NeoPixel
- NeoPixel Power Guide: https://learn.adafruit.com/adafruit-neopixel-uberguide
- Mosquitto MQTT: https://mosquitto.org/
End of guide. This setup is stable, reproducible, and fully working.