Project tutorial
RGB LED Strips Controller

RGB LED Strips Controller © GPL3+

You just bought a RGB LED strip kit and you would like to get rid of that cheap hand-held IR remote and to have REAL control.

  • 16,647 views
  • 2 comments
  • 18 respects

Components and supplies

Aluminium enclosure
https://www.aliexpress.com/item/1-piece-power-aluminium-extruded-enclosure-electrical-junction-box-for-pcb-48-204-160mm/32548295254.html
×1
A000066 iso both
Arduino UNO & Genuino UNO
×1
M6hlqueeutbzfkhid4ph
Arduino Ethernet Shield 2
×1
12 VDC power supply
https://www.aliexpress.com/item/30W-Dual-output-5V-12V-Switching-power-supply-AC-to-DC/628632725.html
×1
AC housing
https://www.aliexpress.com/item/New-Hot-5-Pcs-3P-IEC-320-C14-Male-Plug-Panel-Power-Inlet-Sockets-Connectors-AC/32656480250.html
×1
JST connectors
https://www.aliexpress.com/item/100sets-lot-SM-2-3-4-5-6Pin-2-54-Female-and-Male-JST-2-54MM/32351785873.html
×1
Extension PCB for Arduino
https://www.aliexpress.com/item/I-O-Extension-PCB-for-UNO-R3-Board-DIY-bte16-08/32676031613.html?spm=2114.13010608.0.0.Pb4lDe
×1
FQP47P06 MOSFET
×4
2N3904 transistor
×4
Mfr 25frf52 1k sml
Resistor 1k ohm
×8

About this project

Overview

In LED strips world, there are (so) many models.

As explained on Wikipedia... "The most common design differences are in how individual LEDs are controlled, specifically differences in color and whether or not each LED is addressable.

  • Single Color, non-addressable: Every LED on the strand is a single white colour, typically ranging from 2700K to 6500K in colour temperature, or any of several monochrome colors covering the range of the visible spectrum (generally from 400-700 nanometers in wavelength).
  • Multicolor, non addressable: Each LED is capable of displaying red, green, blue, or all three (white), driven by three input power rails. All the LEDs display the same colour at any one time, but the colour can be manipulated by varying the voltage applied to each of the three power inputs.
  • RGB, addressable: Multiple colours and addresses. Each LED has its own chip meaning they can be individually triggered for chasing, strobing, and colour changing"

As a generic rule of thumb, you can consider that "cheap RGB LED strip" = "non addressable". There are numerous tutorials on addressable LED strips but not that much on simple, non addressable ones.

LED Strip

Here is how my LED strip looks like. The strip is divided into segments, each segment hosting 3 RGB LEDs:

Here is how my LED strip is wired, internally:

Some models share a common POSITIVE, other shares a common NEGATIVE. My LED strip is as per the following:

Input Voltage

The next topic to consider is .... input voltage. LED strip lights most commonly operate on 12 or 24 volts DC from a power supply. Mine was 12 VDC. Depending on the strip length, you should pay attention to current as well and select a power supply that can handle LED strip current requirements.

If you just want to turn LED strip on/off (as a whole or per color), relays are okay and easy to setup but if you want to control intensity as well (dimming), then MOSFETs are a great solution.

In order to control colors (and intensity), I need to control A1, A2 and A3. The LED strip requires 12 VDC feed and, even if you had a 5 VDC LED strip, it would require to much current to be controlled directly, that's why I need some kind of circuit based on transistors.

Control Circuit

Here is how my control circuit looks like (duplicate it 3 times, one for each color):

Here is how it looks like once integrated in a box:

Notes:

  • This tutorial is about "how to control LED strips". In the picture, you may notice that it does more than that and you are totally right. I will come back with other tutorials with more information on this.
  • The LED strip control circuit is on the right: 3 MOSFETS are used to control a RGB LED strip and there is a fourth one to control another B/W LED strip.

Code

Arduino scriptC/C++
Note: the script does much more than controlling LED strips, just focus on that part for this tutorial
#include <ArduinoJson.h>
#include <avr/wdt.h>
#include <Ethernet.h>
#include <Network.h>
#include <PubSubClient.h>
#include <QueueArray.h>
#include <SPI.h>
#include <Timer.h>

/* Here is how the HW works

Therea are three subsystems:
- the main box :
    - an Arduino Uno with and Ethernet shield
    - an orange LED (alarm) 
          - steady when water is detected 
          - blinking when ...                  
          - off otherwise
    - a pink LED (fault)
          - blinking when MQTT broker is unreachable (for any reason),
		  - off otherwise
    - a blue LED (system) 
          - steady when system is monitoring 	                                        
          - blinking when commands are received from MQTT
		  - off is system is down
    - a push button : once pressed, a self-test is triggered 
    - a set of 3 MOSFETs to control a strip of RGB LEDs    
    - a MOSFET that control a strip of WHITE LEDs    
- a set of water detectors (all in parallel) 
- a set of 3 12VDC ligthing fixtures 
- a RGB led strip , 12VDC as well

*/
// Network
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xAE, 0xFC, 0xEA };      // Arduino's MAC address
IPAddress ip(192, 168, 1, 218);                           // Arduino's IP address
IPAddress server(192, 168, 1, 100);                       // MQTT broker's address
EthernetClient ethClient;

// MQTT 
PubSubClient client(ethClient); 
#define mqttClientPrefix "GLX"                            // Prefix to use any MQTT publication/subscription 
#define mqttClientLocation "GROUNDFLOOR"                  // Second part of the client identifier
#define mqttClientUID "001"                               // Last part of the client identifier
#define mqttClientStatusTopic "Status"                    // Topic to be used to publish device status 
#define mqttClientFaultTopic "Fault"                      // Topic to be used to publish/subscribe to Faults
const int mqttInterval = 30;                              // determines how often the system will report to MQTT broker (ie. every mqttInterval * mainLoopDelay ms )
int mqttIntervalCnt = 0;                                  // local variable used to count down
int isConnectedToBroker = -1;                             // 1 when connected, -1 = unknown, 0 = unable to connected

// Pin-out
const int SystemLedPin = A0;                              // Blue led 
const int FaultLedPin = A1;                               // Pink led 
const int AlarmLedPin = A2;                               // Orange led 
const int ToggleButtonPin = 3;                            // goes to LOW when someone press on the button and then goes to HIGH when button is released, otherwise pull-down to GND
const int SdCardPin = 4;                                  // SD card on ethernet shield, not used
const int WaterDetectorPin = 7;                           // goes to LOW when water is detected, otherwise pulled-up to VCC
const int LEDFixturesCtrlPin = 2;                         // to control a set of 3 white LEDs
const int LEDStripRedControlPin = 5;                      // to control Red color 
const int LEDStripGreenControlPin = 6;                    // to control Green color
const int LEDStripBlueControlPin = 9;                     // to control Blue color
                                                          // Note do not use D10, D11, D12 and D13 since those pins are reserved for Ethernet shield
// WaterLeakage (local)                                                           
int isWaterDetected = 0;                                  // status as per the last good reading (0 = no water, 1 = water leakage detected)
                     
// Local queue
volatile QueueArray <byte> cmdQueue;                      // each command received from MQTT broker is stored here
byte* incoming;                                           // an incoming message to route to Insteon network
int outByte[26]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};     // a structured message with ligthing commands

// Manual RESET button
volatile boolean isResetRequested = 0;                    // this one will change when button triggers an interrupt

// Logic
const int mainLoopDelay = 500;                            // a fixed delay within main loop, in ms
void(* resetFunc) (void) = 0;
const int verbosity = 0;  
bool isKaraokeMode = 0;                                   // 0 = No, 1 = yes, repeat same sequence continuously

// Initialization 
void setup()
{
  wdt_disable(); //always good to disable it, if it was left 'on' or you need init time
  Serial.begin(19200);
  Serial.println(F("Begin of setup"));   
  // HW setup
  pinMode (SystemLedPin, OUTPUT);
  pinMode (FaultLedPin, OUTPUT);
  pinMode (AlarmLedPin, OUTPUT);
  pinMode (ToggleButtonPin, INPUT_PULLUP); 
  pinMode(SdCardPin, OUTPUT);
  digitalWrite(SdCardPin, HIGH); // to disable SD card since we do not use it
  pinMode (WaterDetectorPin, INPUT);
  pinMode (LEDFixturesCtrlPin, OUTPUT);
  pinMode (LEDStripRedControlPin, OUTPUT);
  pinMode (LEDStripGreenControlPin, OUTPUT);
  pinMode (LEDStripBlueControlPin, OUTPUT);
  digitalWrite(LEDFixturesCtrlPin, LOW);  
  analogWrite(LEDStripRedControlPin, LOW);  
  analogWrite(LEDStripGreenControlPin, LOW);  
  analogWrite(LEDStripBlueControlPin, LOW);  
  
  cmdQueue.setPrinter (Serial);                                    // setup local queue  
  
  // Self test
  testLeds(); 
  testLEDstrip();
  testLEDFixtures(); 
  
  // Network and MQTT setup
  client.setServer(server, 1883);
  client.setCallback(MQTTBrokerCallback);
  Ethernet.begin(mac, ip);
  Serial.print(F("Current IP is : "));
  Serial.print(Ethernet.localIP());  
  Serial.print(F(" - MQTT broker IP is : "));
  Serial.println(server);  
  enableInterruptOnResetButton(); 
  subscribeToToDoLists();                                           // from here, callbacks are trapped   
  delay(1500);  // allow hardware to sort itself out  
  Serial.println(F("End of setup"));       
}

// Main loop
void loop()
{  
  // LEDs
  configureLedsWithInitialStates(); 
  
  // Part 1 : react to reset request
  if (isResetRequested == 1)
  {
     Serial.println(F("Someone pushed on the button to reset this device")); 
     publishStatus();  
     wdt_enable(WDTO_1S); //enable watchdog, will fire in 1 second   
     delay(5000); 
     Serial.println(F("this message should never appear")); 
  }  
  
  // Part 2 : check now whether any water leakage has been detected
  readLocalWaterSensor();     
  if (isWaterDetected == 1) 
  {
     publishWaterFault();
  }  

  // Part 3 : process messages originating from MQTT Broker. Those messages are waiting in a local queue
  while (!cmdQueue.isEmpty ())
  {
     if (parseMessage() == 0)
     {
        processLigthingCommand(); 
     };
  }    
  
  // Part 4 : report device status to MQTT broker
  
  if (mqttIntervalCnt == 0)
  { 
      if (isWaterDetected == 1){ publishWaterFault();}     
      publishStatus();        
      mqttIntervalCnt = mqttInterval;
  }    
  else
  {                   
      mqttIntervalCnt = mqttIntervalCnt - 1;      
  }  
  
  // Part 5 : play continuous sequence (in Karaoke mode only)
  if (isKaraokeMode) 
  { 
     playKaraoke();
  }
  else
  {
     delay(mainLoopDelay / 2 );  // Take some rest
  }
  client.loop();  
  
  // LEDs
  configureLedsWithFinalStates(); 
  displayProgressBar();
  delay(mainLoopDelay / 2);    
}

//
// Lighting commands management
//

void processLigthingCommand()
{
   if (outByte[0] != 0x0A) 
   {
       Serial.print(F("Unknown lighting command type: ")); 
	     Serial.println(outByte[0]);
       return;	   
   }
   isKaraokeMode = 0; 
   switch(outByte[2])
   {
       case 0x0B:                // On command
		       turnOn();
		       break;
		   case 0x0D:                // Off command
		       turnOff();
		       break;
		   case 0xAA:                // Karaoke command
		       isKaraokeMode = 1; 
		       playKaraoke();
		       break;
		   default:
		       Serial.print(F("Unknown command: ")); 
           Serial.println(outByte[2]);		   
   }   
}

void testLEDFixtures()
{
  Serial.println(F("Testing LED fixture - setting to LOW"));  
  digitalWrite(LEDFixturesCtrlPin, HIGH); 
  delay(1000);
  Serial.println(F("Testing LED fixture - setting to HIGH"));  
  digitalWrite(LEDFixturesCtrlPin, LOW);  
}

void testLEDstrip()
{
  Serial.println(F("Testing LED strip")); 
  setLEDStripColor(255, 0, 0);
  delay(200);
  setLEDStripColor(0, 0, 0);
  delay(200);    
  setLEDStripColor(0, 255, 0);  
  delay(200);
  setLEDStripColor(0, 0, 0);
  delay(200);    
  setLEDStripColor(0, 0, 255);
  delay(200);
  setLEDStripColor(0, 0, 0);
  Serial.println(F("End of testing LED strip")); 
}

void setLEDStripColor(int red, int green, int blue)
{
  analogWrite(LEDStripRedControlPin, red);
  analogWrite(LEDStripGreenControlPin, green);
  analogWrite(LEDStripBlueControlPin, blue); 
}

void turnOff()
{
   if (outByte[1] == 0x0A || outByte[1] == 0x00) { setLEDStripColor(0, 0, 0);}
   if (outByte[1] == 0x0B || outByte[1] == 0x00) { digitalWrite(LEDFixturesCtrlPin, LOW);}
}
void turnOn()
{
   if (outByte[1] == 0x0A ) { setLEDStripColor(outByte[3], outByte[4], outByte[5]);}
   if (outByte[1] == 0x0B ) { digitalWrite(LEDFixturesCtrlPin, LOW);}
}
void playKaraoke()
{
   if (outByte[1] == 0x0A ) 
   {    
        
	  for (int i=0;i<255;i++)                     // Changing Red brightness
      {
         setLEDStripColor(i, 0, 0);
		 for (int i=0;i<255;i++)                  // Changing Green brightness
         {
            setLEDStripColor(0, i, 0);
			for (int i=0;i<255;i++)               // Changing Blue brightness
            {
               setLEDStripColor(0, 0, i);
               delay (10);
            }
            delay (10);
         }
         delay (10);
      }  
   }
   else
   {
      Serial.print(F("Karaoke mode not supported by device : ")); 
	    Serial.println(outByte[1]);	
   }
}

//
// Local water sensor management
//
void readLocalWaterSensor()
{         
     isWaterDetected = !getDebouncedValue(WaterDetectorPin, 100, 10);  
     //Serial.print("Water : ");    
     //Serial.println(isWaterDetected); 
}

//
// Reset button management
//
void enableInterruptOnResetButton()
{
    isResetRequested = 0;
    attachInterrupt(1, onResetRequested, CHANGE);
}
void onResetRequested()
{
    detachInterrupt(1);
    isResetRequested = 1;
}

//
// Front panel LEDs management
//
void configureLedsWithInitialStates()
{
  clearLeds();
  // Re-evaluate
  if (isWaterDetected == 1 ) { digitalWrite(AlarmLedPin, HIGH);};
  if (isConnectedToBroker == 0) { digitalWrite(FaultLedPin, HIGH);}; 
  digitalWrite(SystemLedPin, HIGH);
}
void configureLedsWithFinalStates()
{      
  if (isConnectedToBroker == 0) { digitalWrite(FaultLedPin, LOW);};       
}
void clearLeds()
{
  digitalWrite(AlarmLedPin, LOW);
  digitalWrite(FaultLedPin, LOW);
  digitalWrite(SystemLedPin, LOW);
}
void testLeds()
{
  clearLeds();
  digitalWrite(AlarmLedPin, HIGH);
  delay(1000);
  digitalWrite(FaultLedPin, HIGH);
  delay(1000);
  digitalWrite(SystemLedPin, HIGH);
  delay(1000);
  clearLeds();
  delay(1000);
}

//
// MQTT related functions
//

// Subscribe to the MQTT broker to get list of commands to forward to Insteon devices (topic = GLI/Gateway/ToDo)

void subscribeToToDoLists()   
{ 
  if (connectToBroker() == true)
  {   	
	 char topic[50];
	 strcpy(topic, "GLX");  
     strcat(topic, "/");
     strcat(topic, "KitchenLighting");  
     strcat(topic,"/");
     strcat(topic, "ToDo");  
	 client.subscribe(topic);                  // otherwise subscriptions will growth forever..
     if (client.subscribe(topic) == true)
     {
        isConnectedToBroker = 1;    
        Serial.print(F("Registred to MQTT broker as a subscriber for the following topic: ")); 
        Serial.println(topic);
     }
     else
     {     
        Serial.println(F("Not registred to MQTT broker as a subscriber")); 
        isConnectedToBroker = 0;
     }    
     client.loop();          
  }
  else
  {
    isConnectedToBroker = 0;
	  Serial.println(F("Cannot subscribe to any topic since connection to MQTT broker is not established"));
  } 
}

// Handle incoming MQTT messages

void MQTTBrokerCallback(char* subscribedTopic, byte* payload, unsigned int msgLength)
{
  Serial.print(F("New message received from MQTT broker. Topic = "));
  Serial.print(subscribedTopic);
  Serial.print(F(", Length = "));
  Serial.println(msgLength);
  cmdQueue.enqueue('0');
  cmdQueue.enqueue('2');
  for (int i=0;i<msgLength;i++)
  {     
     cmdQueue.enqueue(payload[i]);   // store msg in local Queue     
  }  
  Serial.println();   
}

// Build the client identifier

String buildClientIdentifier()
{
  String data = mqttClientPrefix;
  data+="_";
  data+= mqttClientLocation;
  data+="_";
  data+= mqttClientUID; 
  return data; 
}

// Build the topic name to be used to publish status

String buildDeviceStatusTopic()
{
  String data = mqttClientPrefix;
  data+="/";
  data+=mqttClientLocation;
  data+="/";
  data+=mqttClientUID; 
  data+="/";
  data+=mqttClientStatusTopic;  
  return data; 
}

// Build the topic name to be used to publish/subscribe to Faults 

String buildFaultTopic()
{
  String data = mqttClientPrefix;
  data+="/";
  data+=mqttClientLocation;
  data+="/";
  data+=mqttClientUID; 
  data+="/";
  data+=mqttClientFaultTopic;  
  return data; 
}

// Build the topic name to be used to publish/subscribe to Faults 

String buildAnyFaultTopic()
{
  String data = mqttClientPrefix;
  data+="/";
  data+="+";
  data+="/";
  data+="+"; 
  data+="/";
  data+=mqttClientFaultTopic;  
  return data; 
}

// Build a JSON message to send to MQTT Broker
// NOTE : MQTT_MAX_PACKET_SIZE = 128 bytes.. therefore not more than 100 for the payload 
//        unless you change it in /Arduino/libraries/pubSubClient/src/PubSubClient.h

String buildDeviceStatusJson()
{ 
  String data = "{";
  data+="\n"; 
  data+="\"ResetByOperator\": ";
  data+=(int)isResetRequested;
  data+= ",";
  data+="\n"; 
  data+="\"WaterDetected\": ";
  data+=(int)isWaterDetected;  
  data+="\n";
  data+="}"; 
  return data; 
}

// Build a JSON message to send to MQTT Broker
// NOTE : MQTT_MAX_PACKET_SIZE = 128 bytes.. therefore not more than 100 for the payload 
//        unless you change it in /Arduino/libraries/pubSubClient/src/PubSubClient.h

String buildWaterFaultJson()
{ 
  String data = "{";
  data+="\n"; 
  data+="\"WaterDetected\": ";
  data+=(int)isWaterDetected;
  data+= ","; 
  data+="\n";
  data+="\"Location\": ";
  data+=mqttClientLocation;
  data+="\n";
  data+="}"; 
  return data; 
}

// Report to MQTT broker

void publishMsg(char (&topic)[200], char (&payload)[200] )  
{
  if (connectToBroker() == true)
  {       
     if (client.publish(topic, payload) == true)
     {
        isConnectedToBroker = 1;    
        Serial.print(F("Message sent to MQTT broker using the following topic "));  
        Serial.println(topic);
     }
     else
     {     
        Serial.print(F("Message NOT sent to MQTT broker using the following topic "));  
        Serial.println(topic);
        isConnectedToBroker = 0;
     }    
     client.loop();      
  }
}

// Report faults to MQTT broker

void publishWaterFault()
{
	// Topic
	char topicBuffer[200];
	buildFaultTopic().toCharArray(topicBuffer, 200);
	// Payload
	char payloadBuffer[200];
	buildWaterFaultJson().toCharArray(payloadBuffer, 200); ;
	// Publish message
	publishMsg(topicBuffer, payloadBuffer);
}

// Report faults to MQTT broker

void publishStatus()
{
	// Topic
	char topicBuffer[200];
	buildDeviceStatusTopic().toCharArray(topicBuffer, 200);
	// Payload
	char payloadBuffer[200];
	buildDeviceStatusJson().toCharArray(payloadBuffer, 200); ;
	// Publish message
	publishMsg(topicBuffer, payloadBuffer);
}

// Manage connection with MQTT broker

int connectToBroker() 
{
  Serial.println(F(""));        
  Serial.print(F("Connecting to network and to MQTT Broker... "));    
  char tempBuffer[200];
  buildClientIdentifier().toCharArray(tempBuffer,200);
  if (client.connect(tempBuffer) == true)
  {        
    Serial.print(F("connected as ")); 
    Serial.println(tempBuffer);
  } 
  else
  {
     switch (client.state())
     {
        case -4:
          Serial.println(F("MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time"));
          break;
        case -3:
          Serial.println(F("MQTT_CONNECTION_LOST - the network connection was broken"));
          break;
        case -2:
          Serial.println(F("MQTT_CONNECT_FAILED - the network connection failed"));
          break;
        case -1:
          Serial.println(F("MQTT_DISCONNECTED - the client is disconnected cleanly"));
          break;
        case 0:
          break;
        case 1:
          Serial.println(F("MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT"));
          break;
        case 2:
          Serial.println(F("MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier"));
          break;
        case 3:
          Serial.println(F("MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection"));
          break;
        case 4:
          Serial.println(F("MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected"));
          break;
        case 5:
          Serial.println(F("MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect"));
          break;
        default:
          Serial.print("failed, rc=");
          Serial.println(client.state()); 
          break;        
     }
  }
  return client.connected();    
}

// Local queue management
// Parse a message stored in the local queue

int parseMessage()
{
  byte b;
  int hex_digit_to_read = 2;
  bool reading_hex = true;
  byte hex = 0;
  for(int j=0;j<26;j++) outByte[j] = 0xFF;
  int j;
  while (!cmdQueue.isEmpty ())
  { 
     b = cmdQueue.dequeue(); 
     if (b == ':')   // delimiter between each set of 2 characters 
     {
         hex = 0;
         hex_digit_to_read = 2;
         reading_hex = true;
         continue;
     };
     if (hex_digit_to_read > 0)  
     {
        hex = (hex << 4) | CharToHex(b);
        hex_digit_to_read--;
     };
     if (reading_hex && hex_digit_to_read == 0)
     {
        reading_hex = false;
        if (hex == 0x02)   // end of message
        {
          hex_digit_to_read = 2;
          reading_hex = true;
          hex = 0;
          j = 0;
          return 0;
        }
        else
        {
          outByte[j] = hex;
          Serial.print(hex, HEX);
          Serial.print(' ');
          j++;
        };
     };     
  };
}

//
// Utilities
//

// This routine helps to avoid false alarms

int getDebouncedValue(int inputPin, int intervalInMs, int requiredConfirmations)
{
    int confirmations = 1;
    int currentValue = digitalRead(inputPin);
    while (confirmations <= requiredConfirmations)
    {      
      delay(intervalInMs);
      if (currentValue == digitalRead(inputPin)) 
      {
         confirmations = confirmations + 1;
      }
      else
      {       
        confirmations = 1;
        currentValue = digitalRead(inputPin);
      }
      //Serial.print(currentValue);
    }  
    return currentValue;
}

// A simple routine that helps to format serial log

void displayProgressBar()
{
    if (isWaterDetected == 1) 
    {
        Serial.print(F("W"));        
    }
    else
    {
        Serial.print(F("."));        
    };      
}

byte CharToHex(char c)
{
    byte out = 0;
    if( c >= '0' && c <= '9'){
        out = (byte)(c - '0');
    } 
    else if ( c >= 'A' && c <= 'F'){
        out = (byte) (c - 'A') + 10;
    }
    else if ( c >= 'a' && c <= 'f'){
        out = (byte) (c - 'a') + 10;
    }

    return out;
}

Comments

Similar projects you might like

LED Emergency Lights using WS2812 RGB LED Module

Project tutorial by bigboystoys13

  • 10,667 views
  • 7 comments
  • 27 respects

Multifunctional RGB LED Light

by PauliusPlus

  • 5,781 views
  • 1 comment
  • 29 respects

RGB LED Button Controller

Project showcase by Re

  • 7,939 views
  • 1 comment
  • 12 respects

Arduino-Controlled RGB LED Infinity Mirror

Project tutorial by Ben Finio

  • 9,497 views
  • 2 comments
  • 29 respects

Control RGB LED by Dragging – Arduino 101 & App Inventor

Project tutorial by DFRobot and CAVEDU Education

  • 4,437 views
  • 0 comments
  • 12 respects

Arduino controlled RGB LED strip

Project tutorial by Raphael Nestler and Danilo Bargen

  • 21,695 views
  • 2 comments
  • 19 respects
Add projectSign up / Login