Zápisník experimentátora
Hierarchy: Stmievač LED pásika pomocou ATtiny85
In this article I will explain the principle of LED strip dimming. This is the third of a series of articles that show you how the program progresses and how the resulting program may be diametrically different. We will gradually pass the source code of the program from version 1.0 to version 1.3.
This text describes the outdated version of the code. At the time of writing the article was the current version of code 1.3. This version is analyzed here so you can see how the program progresses with a progressive approach.
In the ideal world you will write the ideal program right away for the first time and will not make any mistakes and will do exactly what you imagined. And now welcome to the real world where I tried to program the dimming logic to suit the work in the kitchen.
The code was developed for version 1.0.6, but can be easily compiled in 1.6.13 or 1.8.2. After compilation of 2894 (6110 with debugging enabled), it occupies bytes. You need to have ATtiny85 programming support installed.
In this program, I focused on changing the program to a state machine. The whole development was on the development board for ATtiny85, which I already described in the previous article.
Debugging is based on the existence or absence of the RDEBUG
macro. If it does not exist (the final version of the code), then pins 3 and 4 to which the SoftwareSerial
library is set are not used at all, which means that the compiler will simply remove the code from the source.
If a macro is defined (debug version of code), debug statements are sent to the serial port. It is still worth mentioning that during the debug I have a hold_value
shortened interval for 5 seconds. This is for practical reasons, because during development there is no reason to wait dozens of seconds to turn off the light.
//#define RDEBUG
#if defined RDEBUG
#include <SoftwareSerial.h>
const int dig_tx = 3;
const int dig_rx = 4;
SoftwareSerial s85(dig_rx, dig_tx); // RX, TX
#endif
#if defined RDEBUG
const unsigned long hold_value = 5*1000L;
#else
// base hold period
const unsigned long hold_value = 45*1000L;
#endif
void setup() {
pinMode(led, OUTPUT);
pinMode(dig, INPUT);
#if defined RDEBUG
s85.begin(9600);
s85.println(F("ATtiny85 LED dimmer"));
#else
for(int i=0;i<2;i++)
ShowLedIsLive();
#endif
state=stDark;
}
I have the following states in the program:
enum states {stBoot, stDark, stOn, stHold, stOff, stOff30, stHold30};
states state=stBoot;
And individual states have their functions in the code that control lighting behavior.
void loop() {
switch(state) {
case stDark:
LightDark();
break;
case stOn:
LightOn();
break;
case stHold:
LightHold();
break;
case stOff:
LightOff();
break;
case stHold30:
LightHold30();
break;
case stOff30:
LightOff30();
break;
}
}
Linear dimming is made using a table of pre-calculated values.
const byte table[] PROGMEM = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15,
15, 15, 16, 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21,
22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30,
31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 58, 59, 60, 62,
63, 64, 66, 67, 69, 70, 72, 73, 75, 77, 78, 80, 82, 84, 86, 88,
90, 91, 94, 96, 98, 100, 102, 104, 107, 109, 111, 114, 116, 119, 122, 124,
127, 130, 133, 136, 139, 142, 145, 148, 151, 155, 158, 161, 165, 169, 172, 176,
180, 184, 188, 192, 196, 201, 205, 210, 214, 219, 224, 229, 234, 239, 244, 250
};
For example, during dimming, the code looks like this. Now that I describe it again, I realize that this feature has bugs and that part of the code is never done. Because when the value
drops below the value of 20, it will jump out of the function and the code at its end will never be executed.
void LightOff() {
#if defined RDEBUG
s85.println(F("Light OFF"));
#endif
unsigned long ms=millis();
unsigned long m=0;
unsigned long m2=pot_value*6; // 6x on period
while(m<m2)
{
int p=digitalRead(dig);
if(p)
{
state=stOn;
#if defined RDEBUG
s85.print(F("Signal break at "));
//s85.println(value);
#endif
return;
}
value=map(m,0,m2,255,0);
int vm=pgm_read_byte(&table[value]);
value=vm;
analogWrite(led,vm);
if(value<20) {
state=stHold30;
return;
}
m=millis()-ms;
}
digitalWrite(led, LOW);
value=0;
#if defined RDEBUG
s85.println(F("Dark"));
#endif
state=stDark;
}
This is a very important modification. The darkening is only a few seconds, and if the dimming has begun because you stopped moving around in the room (for example, you were starting to read the magazine), you would not have enough time to swing your hands to turn the light back on at full intensity. Therefore, the dimming is divided into two intervals. Between them there is a maintenance interval in the shade that gives you enough time to react.
///
/// Handled dusk
///
void LightHold30() {
#if defined RDEBUG
s85.println(F("Light HOLD 30"));
#endif
dig_last=millis()+hold_value;
while(millis()<dig_last)
{
int p=digitalRead(dig);
if(p) // still ON
{
#if defined RDEBUG
//s85.print(F("dig_last="));
//s85.println(dig_last);
#endif
state=stOn;
return;
}
}
state=stOff30;
}
///
/// From dusk to darkness
///
void LightOff30() {
#if defined RDEBUG
s85.println(F("OFF 30"));
#endif
unsigned long ms=millis();
unsigned long m=0;
unsigned long m2=pot_value;
while(m<m2)
{
int p=digitalRead(dig);
if(p)
{
state=stOn;
return;
}
int v=map(m,0,m2,value,0);
analogWrite(led,v);
value=v;
m=millis()-ms;
}
digitalWrite(led, LOW);
value=0;
state=stDark;
}
The source code is located on GitHub.
30.07.2017