Zápisník experimentátora
Hierarchy: Stmievač LED pásika pomocou ATtiny85
V tomto článku vysvětlím princip činnosti stmívače LED pásků. Je to třetí ze série článků, na kterých vám ukážu, jak postupně probíhá vývoj programů a jak diametrálně se může lišit výsledný program. Postupně projdeme zdrojové kódy programu od verze 1.0 po verzi 1.3.
Tento text popisuje zastaralou verzi kódu. V době psaní článku byla aktuální verze kódu 1.3. Tato verze je zde analyzována proto, abyste si mohli podívat, jak probíhá vývoj programů metodou postupného přibližování.
V ideálním světě napíšete ideální program hned napoprvé a nebudou v něm žádné chyby a bude dělat přesně to, co jste si představovali. A teď vítejte v reálném světě, ve kterém jsem se pokusil naprogramovat logiku stmívání tak, aby vyhovovala práci v kuchyni.
Kód byl vyvíjen ještě pro verzi 1.0.6, ale bez problémů jej lze zkompilovat iv 1.6.13 nebo 1.8.2. Zabírá po zkompilovaný 2894 (6110 se zapnutým debuggování) bajtů. Je nutné mít instalovanou podporu pro programování ATtiny85.
V tomto programu jsem se soustředil na změnu programu na stavový automat. Celý vývoj probíhal na vývojové desce pro ATtiny85, kterou jsem již popisoval v předchozím článku.
Debuggování je založeno na existenci nebo neexistenci makra RDEBUG
. Pokud neexistuje (finální verze kódu), potom nejsou vůbec použity piny 3 a 4 na které je nastavena knihovna SoftwareSerial
, což znamená, že překladač všechen ten kód ze zdrojáků jednoduše odstraní.
Pokud je makro definováno (ladící verze kódu), na sériový port se odesílají ladící výpisy. Ještě stojí za zmínku, že během ladění mám zkrácen interval hold_value
na 5 sekund. Je to z praktických důvodů, protože během ladění není důvod čekat desítky sekund na zhasnutí osvětlení.
//#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;
}
V programu mám tyto stavy:
enum states {stBoot, stDark, stOn, stHold, stOff, stOff30, stHold30};
states state=stBoot;
A jednotlivé stavy mají v kódu své funkce, které řídí chování osvětlení.
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;
}
}
Lineární stmívání je zajištěno pomocí tabulky predpočítaných hodnot.
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
};
Například během zhasínání vypadá kód takto. Když to teď zpětně popisuji, uvědomuji si, že tato funkce má své mouchy a část kódu se nikdy neprovede. Neboť když klesne hodnota v proměnné value
pod 20, vyskočí se ven z funkce a kód na jejím konci se nikdy neprovede.
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;
}
Toto je velmi důležitá úprava. Stmívání se totiž provádí pouze pár sekund a pokud se začalo stmívání, protože jste se v místnosti přestali pohybovat (například jste se začetli do časopisu), neměli byste dostatek času na zamávání rukou, aby se světlo zase zapnulo na plnou intenzitu. Proto je stmívání rozděleno na dva intervaly. Mezi nimi je vložen udržovací interval v šeru, který vám dává dostatek času na reakci.
///
/// 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;
}
Zdrojový kód se nachází na GitHub.
30.07.2017