Hi guys,
Lately I've been exploring the world of LED-strips. Those nice things which can lower your energy bill. I explored the options to get them wireless controlled. Seen a lot and a lot I didn't like. Overkill solutions or to complicated (WIFI for LED controllers?). I also wanted to keep the price low for each LED-strip. I expect to implement three of them in my house (and one ambilight effect you can view numerous videos on youtube). I settled for the nice new LED node v2 from Jeelabs. Not afraid of doing some soldering and software development.
Bill of materials used for my first LED-strip (which is put on our sleepingroom):
- RGB strip (only 2 meter used) - 7,2 euros
http://samenkopen.net/action_product/909457/822877
- 12v adapter - 6 euros
http://samenkopen.net/action_product/909457/681680
- LED-node - 28 euros
- http://jeelabs.com/products/led-node-v2
- Some corner profile (or whatever it's called) from our local Gamma
Something like this: http://www.bouwmarktconcurrent.nl/lamin ... er-4423704
------ TOTAL => +/- 50 euros
But there wasn't any nice good software on the internet capable of things I wanted the strip to do. Scenario's I had were the following:
- Wireless send commands to control the LED-strip.
- Capable of doing effects continously or just once.
- Signal an alarm by glowing red.
- Signal an incoming telephone conversation by flashing yellow a few times, one second of silence and than flashing again.
- Signal a notification by fading to orange and back to the current color again (whenever I'm sitting with my headphones on I know something is happining).
- Fading effects.
- Plain simpel show color effects.
- Certain beautifull presets available (blacklight, purple, and whites)
- Random show some colors (disco effect).
- Send a "turn up" or "turn down" command for an RGB channel or the whole strip (easy adjusting).
- Queue a series of effects described above in the "continous" queue to be played by the LED node.
- Queue some effects described above in the "do once" queue. Whenever something is added to this queue the strip will do this and return to playing the continous queue.
- Fire and water effects (TODO)
- HUE color codes effects (TODO)
I basically want to mimic events in the house on the LED-strip. In the next code samples I have the LED-strip libraries and a sample sketch (which will take it commands from a Jeelink or from a serial port during development).
[Project] LEDnode from Jeelabs
Moderator: Mdamen
Re: [Project] LEDnode from Jeelabs
One library used from the web:
http://playground.arduino.cc/Code/QueueArray
The ledstrip written libraries:
LEDStrip.h
LEDStrip.cpp
http://playground.arduino.cc/Code/QueueArray
The ledstrip written libraries:
LEDStrip.h
Code: Select all
#ifndef LEDSTRIP_H
#define LEDSTRIP_H
#include "Arduino.h"
#include <QueueArray.h>
#ifndef LED_R
#define LED_R 6 // IRQ
#endif
#ifndef LED_G
#define LED_G 9 // IRQ
#endif
#ifndef LED_B
#define LED_B 5 // IRQ
#endif
#define numBufSize 11
typedef struct {
byte red;
byte green;
byte blue;
} RGB;
struct Ramp {
RGB rgb;
unsigned int seconds;
boolean fade;
};
class LEDStrip {
RGB fromColor;
RGB toColor;
RGB curColor;
boolean fade;
boolean prevOnce;
QueueArray <Ramp> once;
QueueArray <Ramp> continous;
unsigned long beginTime;
unsigned long endTime;
byte calcNewLevel(byte from, byte to, unsigned int diffEnd, unsigned int diffCur);
byte calcUpDown(byte from, byte up, byte amount);
void fadeNext();
unsigned int stringToNumber(String thisString);
public:
LEDStrip();
~LEDStrip();
void process();
boolean isactive();
void processCommand(String command);
void command(String command);
void addonce(Ramp ramp);
void addcontinous(Ramp ramp);
void add(Ramp ramp, boolean addToOnce);
void start();
void reset();
void setcolor(RGB rgb);
void setnext(Ramp ramp);
};
#endif
Code: Select all
#include "Arduino.h"
#include "LEDStrip.h"
#include <QueueArray.h>
// #define DEBUG
LEDStrip::LEDStrip() {
bitSet(TCCR1B, WGM12);
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
pinMode(LED_R, OUTPUT);
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
reset();
}
LEDStrip::~LEDStrip(){/*nothing to destruct*/}
void LEDStrip::reset(){
toColor = (RGB) {0,0,0};
fromColor = (RGB) {0,0,0};
curColor = (RGB) {0,0,0};
beginTime = 0;
endTime = 0;
fade = false;
prevOnce = false;
setcolor((RGB){0,0,0});
}
boolean LEDStrip::isactive() {
return (endTime != 0);
}
unsigned int LEDStrip::stringToNumber(String thisString) {
unsigned int i, value, length;
length = thisString.length();
char blah[(length+1)];
for(i=0; i<length; i++) {
blah[i] = thisString.charAt(i);
}
blah[i]=0;
value = atoi(blah);
return value;
}
void LEDStrip::processCommand(String command) {
#ifdef DEBUG
Serial.println("received: " + command);
#endif
if ( command.charAt(0) == 'B' || command.charAt(0) == 'A' || command.charAt(0) == 'C' ) {
unsigned int secs = stringToNumber(command.substring(2,6));
RGB begin = { (byte)stringToNumber(command.substring(6,9)),
(byte)stringToNumber(command.substring(9,12)),
(byte)stringToNumber(command.substring(12,15)) };
RGB end = {0,0,0};
boolean once = command.charAt(1) == 1;
// B 1000 120 130 140
if ( command.length() > 14 ) {
// B 1000 120 130 140 150 160 170
end.red = (byte)stringToNumber(command.substring(15,18));
end.green = (byte)stringToNumber(command.substring(18,21));
end.blue = (byte)stringToNumber(command.substring(21,24));
}
// add them
if ( command.charAt(0) == 'B' ) {
add((Ramp){begin, secs, false}, once);
add((Ramp){end, secs, false}, once);
} else {
add((Ramp){begin, secs, (command.charAt(0) == 'C')}, once);
}
} else if ( command.charAt(0) == 'D' ) {
int secs = stringToNumber(command.substring(2,6));
byte number = stringToNumber(command.substring(6,8));
boolean once = command.charAt(1) == 1;
for(byte i=0; i<number;i++) {
RGB rgb = {random(0,255),random(0,255),random(0,255)};
Ramp ramp = {rgb, secs, true};
add(ramp, once);
}
} else if ( command.charAt(0) == 'F' ) {
byte whichChannel = stringToNumber(command.substring(1,2)); // 0=r,1=g,2=b,3=all
byte upOrDown = stringToNumber(command.substring(2,3)); // 0 - 1
byte amount = stringToNumber(command.substring(3,5)); // 0 - 99
if ( continous.count() == 1 ) {
Ramp ramp = (Ramp) continous.pop();
if ( whichChannel == 0 || whichChannel == 3 ) {
ramp.rgb.red = calcUpDown(ramp.rgb.red, upOrDown, amount);
}
if ( whichChannel == 1 || whichChannel == 3 ) {
ramp.rgb.green = calcUpDown(ramp.rgb.green, upOrDown, amount);
}
if ( whichChannel == 2 || whichChannel == 3 ) {
ramp.rgb.blue = calcUpDown(ramp.rgb.blue, upOrDown, amount);
}
// read again
continous.push(ramp);
setnext(ramp);
}
} else if ( command.charAt(0) == 'E' ) {
int preset = stringToNumber(command.substring(1,3));
if ( preset == 1 ) {
// fire
//1F00500255040000.F00200255050000.F00300255010000.F00300255070000
for(int i=0; i<15;i++) {
byte r = (byte)random(25);
byte g = (byte)random(10);
addcontinous((Ramp){(RGB){(byte)r+225, (byte)g+50, 0}, 1000, true});
}
} else if ( preset == 2 ) {
// color = purple
addcontinous((Ramp){(RGB){255, 0, 30}, 2000, false});
} else if ( preset >= 20 && preset <= 29 ) {
/** WHITES **/
if ( preset == 20 ) {
// color = warm white effect
addcontinous((Ramp){(RGB){255, 70, 0}, 2000, false});
} else if ( preset == 21 ) {
addcontinous((Ramp){(RGB){255, 244, 229}, 2000, false});
} else if ( preset == 22 ) {
addcontinous((Ramp){(RGB){250, 235, 215}, 2000, false});
} else if ( preset == 23 ) {
addcontinous((Ramp){(RGB){248, 245, 212}, 2000, false});
} else if ( preset == 24 ) {
// candle
addcontinous((Ramp){(RGB){255, 147, 41}, 2000, false});
} else if ( preset == 25 ) {
// Clear Blue Sky
addcontinous((Ramp){(RGB){64, 156, 255}, 2000, false});
} else if ( preset == 26 ) {
// Overcast Sky
addcontinous((Ramp){(RGB){201, 226, 255}, 2000, false});
} else if ( preset == 27 ) {
addcontinous((Ramp){(RGB){255, 241, 224}, 2000, false});
}
} else if ( preset >= 30 && preset <= 39 ) {
// alarm effect (continous!)
if ( preset == 30 ) {
addcontinous((Ramp){(RGB){255, 0, 0}, 250, true});
addcontinous((Ramp){(RGB){0, 0, 0}, 700, true});
} else if ( preset == 31 ) {
addonce((Ramp){(RGB){0, 0, 0}, 700, true});
addonce((Ramp){(RGB){255, 0, 0}, 250, true});
addonce((Ramp){(RGB){0, 0, 0}, 700, true});
} else if ( preset == 32 ) {
// orange notification (once!)
addonce((Ramp){(RGB){0, 0, 0}, 700, true});
addonce((Ramp){(RGB){240, 10, 0}, 250, true});
addonce((Ramp){(RGB){0, 0, 0}, 700, true});
// 1C0100025525525591C01000001001001
// 1C010002400100009C01000000000000
// 1C010002400100009C01000000000000
} else if ( preset == 33 ) {
// yellow ringing notification (two times!)
// 1A11000255100000
addonce((Ramp){(RGB){255, 100, 0}, 100, false});
addonce((Ramp){(RGB){0, 0, 0}, 100, false});
addonce((Ramp){(RGB){255, 100, 0}, 100, false});
addonce((Ramp){(RGB){0, 0, 0}, 100, false});
addonce((Ramp){(RGB){255, 100, 0}, 100, false});
addonce((Ramp){(RGB){0, 0, 0}, 1500, false});
addonce((Ramp){(RGB){255, 100, 0}, 100, false});
addonce((Ramp){(RGB){0, 0, 0}, 100, false});
addonce((Ramp){(RGB){255, 100, 0}, 100, false});
addonce((Ramp){(RGB){0, 0, 0}, 100, false});
addonce((Ramp){(RGB){255, 100, 0}, 100, false});
addonce((Ramp){(RGB){0, 0, 0}, 1000, false});
}
} else if ( preset == 5 ) {
// Black Light Fluorescent
addcontinous((Ramp){(RGB){167, 0, 255}, 2000, false});
} else if ( preset == 6 ) {
// water
// 1C015001001002559C01500100100255
// addcontinous((Ramp){(RGB){201, 226, 255}, 2000, false});
}
}
}
void LEDStrip::command(String command) {
String buffer = "";
if ( command.charAt(0) == '1' ) {
#ifdef DEBUG
Serial.println("empty continous queue");
#endif
while(once.count() > 0) {
once.pop();
}
while(continous.count() > 0) {
continous.pop();
}
reset();
}
for(int i=1; i<command.length();i++) {
if ( command.charAt(i) == '9' ) {
processCommand(buffer);
buffer = "";
} else {
buffer += command.charAt(i);
}
}
processCommand(buffer);
}
void LEDStrip::add(Ramp ramp, boolean addToOnce) {
if ( addToOnce ) {
once.push(ramp);
} else {
continous.push(ramp);
}
}
void LEDStrip::addonce(Ramp ramp) {
once.push(ramp);
}
void LEDStrip::addcontinous(Ramp ramp){
continous.push(ramp);
}
void LEDStrip::setnext(Ramp ramp) {
beginTime = millis();
fromColor = curColor;
toColor = ramp.rgb;
fade = ramp.fade;
endTime = beginTime+ramp.seconds;
}
void LEDStrip::fadeNext() {
if ( once.count() > 0 ) {
setnext((Ramp) once.pop());
prevOnce = true;
} else if ( continous.count() > 0 ) {
if ( prevOnce ) {
prevOnce = false;
Ramp ramp = (Ramp) continous.peek();
ramp.fade = true;
ramp.seconds = 1000;
// fade to the next continous color
setnext(ramp);
} else {
Ramp ramp = (Ramp) continous.pop();
// read again
continous.push(ramp);
setnext(ramp);
}
}
}
void LEDStrip::start() {
fadeNext();
}
void LEDStrip::setcolor(RGB rgb) {
curColor = rgb;
analogWrite(LED_R, curColor.red);
analogWrite(LED_G, curColor.green);
analogWrite(LED_B, curColor.blue);
}
byte LEDStrip::calcUpDown(byte from, byte up, byte amount) {
if ( up == 1 ) { // up
if ( from + amount > 255 ) {
return 255;
} else {
return (byte)(from + amount);
}
} else {
if ( from - amount < 0 ) {
return 0;
} else {
return (byte)(from - amount);
}
}
}
byte LEDStrip::calcNewLevel(byte from, byte to, unsigned int diffEnd, unsigned int diffCur) {
if ( from == to ) {
return from;
}
byte diff = abs(from-to);
float newf = round( ( (float)diff / diffEnd ) * diffCur );
byte newv;
if ( newf > 255 ) {
newv = 255;
} else {
newv = (byte) newf;
}
if ( from > to ) {
newv = diff - newv;
}
return newv;
}
void LEDStrip::process() {
// check if already passed
unsigned long curTime = millis();
if ( isactive() ) {
if ( curTime >= endTime ) {
endTime = 0;
fadeNext();
} else {
// if fade just set the new value
if ( fade ) {
// get the correct color
unsigned int diff = (int) endTime - beginTime;
unsigned int diffToCur = (int) curTime - beginTime;
byte red = calcNewLevel(fromColor.red,toColor.red, diff, diffToCur);
byte green = calcNewLevel(fromColor.green,toColor.green, diff, diffToCur);
byte blue = calcNewLevel(fromColor.blue,toColor.blue, diff, diffToCur);
setcolor((RGB){red,green,blue});
} else {
setcolor(toColor);
}
}
}
}
Re: [Project] LEDnode from Jeelabs
And the sample sketch which I used. Kept some commands in the comments which you can send.
Code: Select all
#include <LEDStrip.h>
#include <QueueArray.h>
#include <JeeLib.h>
LEDStrip strip;
String readString;
String lastCommand;
unsigned long lastCommandTime;
void setup() {
Serial.begin(57600);
rf12_initialize(33 /* node */, RF12_868MHZ, 0);
lastCommand = "";
lastCommandTime = 0;
}
void loop() {
readString = "";
while (Serial.available()) {
delay(10); //delay to allow buffer to fill
if (Serial.available() >0) {
char c = Serial.read(); //gets one byte from serial buffer
readString += c; //makes the string readString
}
}
if (readString.length() >0) {
strip.command(readString);
strip.start();
}
strip.process();
delay(10);
if (rf12_recvDone() && rf12_crc == 0) {
const byte* p = (const byte*) rf12_data;
Serial.println("Received something");
int nodeID = rf12_hdr & 0x1F; // get node ID
if ( nodeID == 1 ) {
String str = "";
for(int i=0; i<rf12_len;i++) {
String newstr = String(rf12_data[i], HEX);
newstr.toUpperCase();
str += newstr;
}
if ( str.charAt(0) == '1' || str.charAt(0) == '0' ) {
// ignore the same messages for three seconds!
unsigned int curTime = millis();
boolean run = true;
if ( str == lastCommand ) {
if ( lastCommandTime + 1000 > curTime ) {
run = false;
}
}
if ( run ) {
Serial.println("Accept command: "+str);
lastCommand = str;
lastCommandTime = curTime;
strip.command(str);
strip.start();
} else {
Serial.println("Ignoring previous commmand");
}
} else {
Serial.println("incorrect data received");
Serial.println(nodeID);
Serial.println(str);
}
if (RF12_WANTS_ACK) {
rf12_sendStart(RF12_ACK_REPLY, 0, 0);
Serial.println("Sending ACK");
} else {
Serial.println("Sending ACK");
}
} else {
// odd node
}
}
/*
Channel test:
1A11000255000000
1A11000000255000
1A11000000000255
1A11000255255255
1A10500255244229
1C010002552552559C010000000002559C01000255255255 // white -> blue -> white
1C010002552552559C010000002550009C01000255255255 // white -> blue -> white
1D1100021 // random
1B01250255070000 // blink
0F0110
1A01000255060000
1B01250255070000 = blink
1D0100012 = random
1C01000255000000 = fade
1A11000255060000 = warm orange/yellow
1C010002552552559C010000000000009C01000255255255 = fade in white .. fade out white
1C010002550400009C010002550500009C01000255010000
1C005002550400009C002002550500009C003002550100009C00300255070000
1C0100025
0D0050012
1E01 == fire
1E02 == preset 2, etc!
*/
}
Re: [Project] LEDnode from Jeelabs
Some documentation about the codes you can test on the serial console and can be sent through the air:
Example: 1A11000255000000
First char [1] = Replace all (once and continous) queues and with the command just send (zero to add instead of replace)
Second char [A] = Effect type (A=Set to RGB immediately)
Next: [1] = Add this command to the once queue (0 = continous)
Next: [1000] = The number of seconds the effect should take (in this case no effect)
Next: [255] = Red
Next: [000] = Green
Next: [000] = Blue
For more documentation on the effects check the source code. Not all effects have the same command setup.
Example: 1A11000255000000
First char [1] = Replace all (once and continous) queues and with the command just send (zero to add instead of replace)
Second char [A] = Effect type (A=Set to RGB immediately)
Next: [1] = Add this command to the once queue (0 = continous)
Next: [1000] = The number of seconds the effect should take (in this case no effect)
Next: [255] = Red
Next: [000] = Green
Next: [000] = Blue
For more documentation on the effects check the source code. Not all effects have the same command setup.
Re: [Project] LEDnode from Jeelabs
Oh and for those wondering how I control this LED-strip in a Wife-Acceptance-Factor fashion. I ordered the following remote control on dx.com:
http://dx.com/p/24-key-wireless-infrare ... 2025-47019
The already in place Raspberry PI with IguanaIR infrared USB module behind the television at the sleeping room picks up the signal and sends it towards my home automation server. This then converts this to the command and sends it through the Jeelink towards the LED-node. Other effects are obviously send whenever an event arrises.
http://dx.com/p/24-key-wireless-infrare ... 2025-47019
The already in place Raspberry PI with IguanaIR infrared USB module behind the television at the sleeping room picks up the signal and sends it towards my home automation server. This then converts this to the command and sends it through the Jeelink towards the LED-node. Other effects are obviously send whenever an event arrises.
Re: [Project] LEDnode from Jeelabs
Nice!!!
Esp the PI behind the bedroom TV
More details on that?
Esp the PI behind the bedroom TV

More details on that?
Re: [Project] LEDnode from Jeelabs
It runs Raspbmc (XBMC) and has an IR usb stick from iguanaworks and a WIFI N adapter attached. The IR usb stick receives IR commands but also to sends IR commands to turn on the TV and on correct input (HDMI) for "Movie mode", "Ambient mode", "Relax mode", etc. I just connected the LIRCD instances from my home automation server and the one running on the Raspberry PI using the --connect and --listen parameters.
The movie, ambient, relax, party, sleeping and normal mode also influence the LED-node light settings
The movie, ambient, relax, party, sleeping and normal mode also influence the LED-node light settings

Re: [Project] LEDnode from Jeelabs
Nice work!
One question though: when will we be invited to have a look at the 'party' mode?
Really interested in that one!

One question though: when will we be invited to have a look at the 'party' mode?
Really interested in that one!

