Variable brightness controller for Christmas LED lights with Arduino
With a 24V LED system garlands with fixed brightness, animate them with a controller for variable and random brightness with an Arduino.
See also my projects:
- FreeRTOS on Arduino Nano driving Multiple Christmas garlands LED controller
- ESP32 with FreeRTOS driving some Christmas LED garlands
The basis: 24V system of multi-coloured LED garlands for Christmas trees with 220V transformer and rectifier. The brightness of the garlands is fixed.
Project: animate the LED lights with an Arduino to have variations in on-off and variations in brightness, and random choice.
The components:
- 1 Arduino Nano
- 1 Arduino support for tests
- 1 power supply for the Arduino from 24V (Internet shop)
- 1 FET module for garland control (Internet shop)
- 1 interface driver for the FET module with the Arduino
- 1 printed circuit board with holes for mounting
- several connectors for garlands
- 1 plastic box to encapsulate the project
For information, I found 24V LED garlands at lumitronix web site, with transformer and rectifier.
At this stage of the project, I choose to program Arduino to control only one PWM output.
Also, I choose to set seven different control light programs in two sets :
- one set to control ON/OFF light with different ON time and OFF;
- one set to control variable PWM light with different time.
Each program is selected randomly. In my first tests I saw that Arduino random is not so random. So I added a new lib for TrueRandom. With it I saw selected random program is more random.
For PWM control, I choose 32 steps for ON and the same for OFF variations in sine wave because eye light perception is not linear.
Here is the listing:
//
//
/*
Name: Led_Guirlandes.ino
Created: 05/01/2019 20:18:18
Author: Sergio
Gestion et animation d'1 guirlande LED sous 24V
Base : Arduino Nano + tampon de commande + module avec IRF520 transistor + module alim abaisseur tension 24V > 8V for arduino nano
Animation en ON/OFF et en variations lumineuses
1- clignotement
ON 500ms
OFF 1500ms - 30% lum
durée 10000ms - as X 5
2- clignotement
ON 500ms
OFF 500ms - 0%
durée 10000ms - as X 10
3- clignotement
ON 1000ms
OFF 500ms - 0%
durée 15000ms - as X 10
4- clignotement
ON 2500ms
OFF 500ms - 0%
durée 18000ms - as X 6
5- var
OFF-ON 1024ms multiple of 32
ON-OFF 1024ms multiple of 32 may be different from ON
wait OFF 0ms
durée as X 7
6- var
OFF-ON 3008ms multiple of 32
ON-OFF 3008ms multiple of 32 may be different from ON
wait OFF 0ms
durée as X 4
7- var
OFF-ON 1984ms multiple of 32
ON-OFF 3520ms multiple of 32 may be different from ON
wait OFF 0ms
durée as X 5
Var will varying following sine wave values in 32 steps from 0 to max and reverse (analog output) :
0, 13, 26, 38, 51, 63, 76, 88, 100, 111, 123, 134, 145, 155, 165, 175, 184, 192, 200, 208, 215, 222, 228, 233, 238, 243, 246, 249, 252, 253, 254, 255
*/
#include < truerandom.h >
// Defining User Types
// 1 guirlande for this project
#define GUIRL_A 5 // pin for guirlande A digital 5 pin 8 PWM
// #define GUIRL_B 6 // pin for guirlande B digital 6 pin 9 PWM
#define MAXPROGR 7 // Max value of Nb programs
// 32 half sine values for var program led :
int varoutvalue[] = { 0, 13, 26, 38, 51, 63, 76, 88, 100, 111, 123, 134, 145, 155, 165, 175, 184, 192, 200, 208, 215, 222, 228, 233, 238, 243, 246, 249, 252, 253, 254, 255 };
// Guirlande status 0 inactive, pgm ended - 1 pgm selected -
int guirl_status = 0;
// selected program for guirlande 0 to MAXPROGR-1
int sel_guirl_pgm = 0;
// struct for program led
typedef struct /* programled définition de la structure */
{
int ledvar; /* 0 clignotement flashing ; 1 variation de la luminosité */
int ledon; /* time ON */
int ledoff; /* time OFF */
int ledvaloff; /* value for OFF status different of 0 */
int ledxtime; /* repeat x time this program */
} pled_t;
pled_t pled [MAXPROGR]; /* reserve memory for the multiple programs for led */
// values for each step in Var program for leds, 32 steps for OFF to ON and same for ON to OFF
const int varval[] = { 0, 13, 26, 38, 51, 63, 76, 88, 100, 111, 123, 134, 145, 155, 165, 175, 184, 192, 200, 208, 215, 222, 228, 233, 238, 243, 246, 249, 252, 253, 254, 255 };
// Defining Function Prototypes that use User Types
//
// Defining Functions below here or use other .ino or cpp files
//
// select a random program for guirlande
int guirl_pgm_select(void)
{
randomSeed(TrueRandom.random());
return random(MAXPROGR);
// return TrueRandom.random(MAXPROGR);
}
// The setup() function runs once each time the micro-controller starts
void setup(void)
{
// init for the MAXPROGR programs led
pled[0].ledvar = 0;
pled[0].ledon = 500;
pled[0].ledoff = 1500;
pled[0].ledvaloff = 112;
pled[0].ledxtime = 5;
pled[1].ledvar = 0;
pled[1].ledon = 500;
pled[1].ledoff = 500;
pled[1].ledvaloff = 0;
pled[1].ledxtime = 10;
pled[2].ledvar = 0;
pled[2].ledon = 1000;
pled[2].ledoff = 500;
pled[2].ledvaloff = 0;
pled[2].ledxtime = 10;
pled[3].ledvar = 0;
pled[3].ledon = 2500;
pled[3].ledoff = 500;
pled[3].ledvaloff = 0;
pled[3].ledxtime = 6;
pled[4].ledvar = 1;
pled[4].ledon = 1024;
pled[4].ledoff = 1024;
pled[4].ledvaloff = 0;
pled[4].ledxtime = 7;
pled[5].ledvar = 1;
pled[5].ledon = 3008;
pled[5].ledoff = 3008;
pled[5].ledvaloff = 0;
pled[5].ledxtime = 4;
pled[6].ledvar = 1;
pled[6].ledon = 1984;
pled[6].ledoff = 3520;
pled[6].ledvaloff = 0;
pled[6].ledxtime = 5;
pinMode(GUIRL_A,OUTPUT); // pin for guirlande A
analogWrite(GUIRL_A, 0); // set output to 0
Serial.begin(115200); // begin serial print for debug
} // end setup
// Add the main program code into the continuous loop() function
void loop()
{
switch (guirl_status)
{
case 0:
// last program has ended, so select a random program for guirlande, or the first time
sel_guirl_pgm = guirl_pgm_select();
guirl_status = 1;
Serial.println(sel_guirl_pgm); // print the selected program
break;
case 1:
// do the guirlande program
if (pled[sel_guirl_pgm].ledvar == 0) // clignotement, flashing, not varying
{ // not var program
for (int iter = 0; iter < pled[sel_guirl_pgm].ledxtime; iter++)
{
analogWrite(GUIRL_A, 255); // switch led to ON
delay(pled[sel_guirl_pgm].ledon); // delay ON state
analogWrite(GUIRL_A, pled[sel_guirl_pgm].ledvaloff); // switch led to OFF value
delay(pled[sel_guirl_pgm].ledoff); // delay OFF state
}
}
else
{ // var program
int stepon, stepoff;
// calcul du délai du pas ON
stepon = pled[sel_guirl_pgm].ledon / 32; // 32 steps ON, may be different from OFF
// calcul du délai du pas OFF
stepoff = pled[sel_guirl_pgm].ledoff / 32; // 32 steps OFF, may be different from ON
for (int iter = 0; iter < pled[sel_guirl_pgm].ledxtime; iter++) // do loop x time this program led
{
for (int i = 0; i < 32; i++)
{
analogWrite(GUIRL_A, varval[i]); // switch led to var value
delay(stepon); // delay between each step
}
for (int i = 31; i > -1; i--)
{
analogWrite(GUIRL_A, varval[i]); // switch led to var value
delay(stepoff); // delay between each step
}
}
}
guirl_status = 0; // program ended, next select a new one
break;
default:
guirl_status = 0; // program ended, next select a new one
break;
}
}
At this stage of the project, I choose to program Arduino to control only one PWM output.
Also, I choose to set seven different control light programs in two sets :
- one set to control ON/OFF light with different ON time and OFF;
- one set to control variable PWM light with different time.
Each program is selected randomly. In my first tests I saw that Arduino random is not so random. So I added a new lib for TrueRandom. With it I saw selected random program is more random.
For PWM control, I choose 32 steps for ON and the same for OFF variations in sine wave because eye light perception is not linear.
Here is the listing:
//
//
/*
Name: Led_Guirlandes.ino
Created: 05/01/2019 20:18:18
Author: Sergio
Gestion et animation d'1 guirlande LED sous 24V
Base : Arduino Nano + tampon de commande + module avec IRF520 transistor + module alim abaisseur tension 24V > 8V for arduino nano
Animation en ON/OFF et en variations lumineuses
1- clignotement
ON 500ms
OFF 1500ms - 30% lum
durée 10000ms - as X 5
2- clignotement
ON 500ms
OFF 500ms - 0%
durée 10000ms - as X 10
3- clignotement
ON 1000ms
OFF 500ms - 0%
durée 15000ms - as X 10
4- clignotement
ON 2500ms
OFF 500ms - 0%
durée 18000ms - as X 6
5- var
OFF-ON 1024ms multiple of 32
ON-OFF 1024ms multiple of 32 may be different from ON
wait OFF 0ms
durée as X 7
6- var
OFF-ON 3008ms multiple of 32
ON-OFF 3008ms multiple of 32 may be different from ON
wait OFF 0ms
durée as X 4
7- var
OFF-ON 1984ms multiple of 32
ON-OFF 3520ms multiple of 32 may be different from ON
wait OFF 0ms
durée as X 5
Var will varying following sine wave values in 32 steps from 0 to max and reverse (analog output) :
0, 13, 26, 38, 51, 63, 76, 88, 100, 111, 123, 134, 145, 155, 165, 175, 184, 192, 200, 208, 215, 222, 228, 233, 238, 243, 246, 249, 252, 253, 254, 255
*/
#include < truerandom.h >
// Defining User Types
// 1 guirlande for this project
#define GUIRL_A 5 // pin for guirlande A digital 5 pin 8 PWM
// #define GUIRL_B 6 // pin for guirlande B digital 6 pin 9 PWM
#define MAXPROGR 7 // Max value of Nb programs
// 32 half sine values for var program led :
int varoutvalue[] = { 0, 13, 26, 38, 51, 63, 76, 88, 100, 111, 123, 134, 145, 155, 165, 175, 184, 192, 200, 208, 215, 222, 228, 233, 238, 243, 246, 249, 252, 253, 254, 255 };
// Guirlande status 0 inactive, pgm ended - 1 pgm selected -
int guirl_status = 0;
// selected program for guirlande 0 to MAXPROGR-1
int sel_guirl_pgm = 0;
// struct for program led
typedef struct /* programled définition de la structure */
{
int ledvar; /* 0 clignotement flashing ; 1 variation de la luminosité */
int ledon; /* time ON */
int ledoff; /* time OFF */
int ledvaloff; /* value for OFF status different of 0 */
int ledxtime; /* repeat x time this program */
} pled_t;
pled_t pled [MAXPROGR]; /* reserve memory for the multiple programs for led */
// values for each step in Var program for leds, 32 steps for OFF to ON and same for ON to OFF
const int varval[] = { 0, 13, 26, 38, 51, 63, 76, 88, 100, 111, 123, 134, 145, 155, 165, 175, 184, 192, 200, 208, 215, 222, 228, 233, 238, 243, 246, 249, 252, 253, 254, 255 };
// Defining Function Prototypes that use User Types
//
// Defining Functions below here or use other .ino or cpp files
//
// select a random program for guirlande
int guirl_pgm_select(void)
{
randomSeed(TrueRandom.random());
return random(MAXPROGR);
// return TrueRandom.random(MAXPROGR);
}
// The setup() function runs once each time the micro-controller starts
void setup(void)
{
// init for the MAXPROGR programs led
pled[0].ledvar = 0;
pled[0].ledon = 500;
pled[0].ledoff = 1500;
pled[0].ledvaloff = 112;
pled[0].ledxtime = 5;
pled[1].ledvar = 0;
pled[1].ledon = 500;
pled[1].ledoff = 500;
pled[1].ledvaloff = 0;
pled[1].ledxtime = 10;
pled[2].ledvar = 0;
pled[2].ledon = 1000;
pled[2].ledoff = 500;
pled[2].ledvaloff = 0;
pled[2].ledxtime = 10;
pled[3].ledvar = 0;
pled[3].ledon = 2500;
pled[3].ledoff = 500;
pled[3].ledvaloff = 0;
pled[3].ledxtime = 6;
pled[4].ledvar = 1;
pled[4].ledon = 1024;
pled[4].ledoff = 1024;
pled[4].ledvaloff = 0;
pled[4].ledxtime = 7;
pled[5].ledvar = 1;
pled[5].ledon = 3008;
pled[5].ledoff = 3008;
pled[5].ledvaloff = 0;
pled[5].ledxtime = 4;
pled[6].ledvar = 1;
pled[6].ledon = 1984;
pled[6].ledoff = 3520;
pled[6].ledvaloff = 0;
pled[6].ledxtime = 5;
pinMode(GUIRL_A,OUTPUT); // pin for guirlande A
analogWrite(GUIRL_A, 0); // set output to 0
Serial.begin(115200); // begin serial print for debug
} // end setup
// Add the main program code into the continuous loop() function
void loop()
{
switch (guirl_status)
{
case 0:
// last program has ended, so select a random program for guirlande, or the first time
sel_guirl_pgm = guirl_pgm_select();
guirl_status = 1;
Serial.println(sel_guirl_pgm); // print the selected program
break;
case 1:
// do the guirlande program
if (pled[sel_guirl_pgm].ledvar == 0) // clignotement, flashing, not varying
{ // not var program
for (int iter = 0; iter < pled[sel_guirl_pgm].ledxtime; iter++)
{
analogWrite(GUIRL_A, 255); // switch led to ON
delay(pled[sel_guirl_pgm].ledon); // delay ON state
analogWrite(GUIRL_A, pled[sel_guirl_pgm].ledvaloff); // switch led to OFF value
delay(pled[sel_guirl_pgm].ledoff); // delay OFF state
}
}
else
{ // var program
int stepon, stepoff;
// calcul du délai du pas ON
stepon = pled[sel_guirl_pgm].ledon / 32; // 32 steps ON, may be different from OFF
// calcul du délai du pas OFF
stepoff = pled[sel_guirl_pgm].ledoff / 32; // 32 steps OFF, may be different from ON
for (int iter = 0; iter < pled[sel_guirl_pgm].ledxtime; iter++) // do loop x time this program led
{
for (int i = 0; i < 32; i++)
{
analogWrite(GUIRL_A, varval[i]); // switch led to var value
delay(stepon); // delay between each step
}
for (int i = 31; i > -1; i--)
{
analogWrite(GUIRL_A, varval[i]); // switch led to var value
delay(stepoff); // delay between each step
}
}
}
guirl_status = 0; // program ended, next select a new one
break;
default:
guirl_status = 0; // program ended, next select a new one
break;
}
}
Updates from the author