Stmievač LED pásika - Softvér v. 1.2

Zápisník experimentátora

Hierarchy: Stmievač LED pásika pomocou ATtiny85

V tomto článku vysvetlím princíp činnosti stmievača LED pásikov. Je to tretí zo série článkov, na ktorých vám ukážem, ako postupne prebieha vývoj programov a ako diametrálne sa môže líšiť výsledný program. Postupne prejdeme zdrojové kódy programu od verzie 1.0 po verziu 1.3.

Tento text popisuje zastaranú verziu kódu. V čase písania článku bola aktuálna verzia kódu 1.3. Táto verzia je tu analyzovaná preto, aby ste si mohli pozrieť, ako prebieha vývoj programov metódou postupného približovania.

V ideálnom svete napíšete ideálny program hneď na prvý raz a nebudú v ňom žiadne chyby a bude robiť presne to, čo ste si predstavovali. A teraz vitajte v reálnom svete, v ktorom som sa pokúsil naprogramovať logiku stmievania tak, aby vyhovovala práci v kuchyni.

Arduino IDE

Kód bol vyvíjaný ešte pre verziu 1.0.6, ale bez problémov ho možno skompilovať aj v 1.6.13 alebo 1.8.2. Zaberá po skompilovaní 2894 (6110 so zapnutým debuggovaním) bajtov. Je nutné mať doinštalovanú podporu pre programovanie ATtiny85.

Programovanie

V tomto programe som sa sústredil na zmenu programu na stavový automat. Celý vývoj prebiehal na vývojovej doske pre ATtiny85, ktorú som už popisoval v predchádzajúcom článku.

  • Debuggovanie pomocou SoftwareSerial - ATtiny85 nemá normálny sériový port, ale pomocou tejto knižnice sa dá nahradiť. Platíte za to ale vysokú daň v podobe veľkej spotreby pamäte, ktorej máte iba 8 kB.
  • Stavy - Pretože osvetlenie sa má správať rozdielne pri rôznych okrajových podmienkach, je logické použiť stavy a pre každý stav naprogramovať konkrétne správanie programu.
  • Lineárne stmievanie - Lineárne stmievanie sa lepšie prispôsobí ľudskému oku. Podrobnosti nájdete v samostatnom článku.
  • Dva kroky počas stmievania - Praktické skúsenosti z používania ukázali, že je vhodné stmievanie rozdeliť na dva úseky. Prvé stmievanie stlmí jas na 30 percent a potom nasleduje dlhšia pauza, počas ktorej má osoba v miestnosti čas zareagovať. Alebo odíde preč a svetlo úplne zhasne, alebo sa začne pohybovať v miestnosti a svetlo sa nastaví zase na maximálnu intenzitu.

Debuggovanie pomocou SoftwareSerial

Debuggovanie je založené na existencii alebo neexistencii makra RDEBUG. Ak neexistuje (finálna verzia kódu), potom nie sú vôbec použité piny 3 a 4 na ktoré je nastavená knižnica SoftwareSerial, čo znamená, že prekladač všetok ten kód zo zdrojáku jednoducho odstráni.

Ak je makro definované (ladiaca verzia kódu), na sériový port sa odosielajú ladiace výpisy. Ešte stojí za zmienku, že počas ladenia mám skrátený interval hold_value na 5 sekúnd. Je to z praktických dôvodov, pretože počas ladenia nie je dôvod čakať desiatky sekúnd na zhasnutie osvetlenia.

//#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;
}

Stavy

V programe mám tieto stavy:

  • stBoot - Implicitný stav počas bootovania. Je okamžite nahradený stavom stDark vo funkcii setup.
  • stDark - Popisuje stav osvetlenia, ktoré je vypnuté.
  • stOn - Stav, počas ktorého sa lineárne zapne osvetlenie na plnú hodnotu.
  • stHold - Plná hodnota osvetlenia, pokiaľ prichádzajú signály zo senzora.
  • stOff - Stav, počas ktorého klesne osvetlenie na 30 percent. Ak príde signál senzora, skočí sa na stav stOn.
  • stOff30 - Stav, počas ktorého klesne osvetlenie na nulu. Ak príde signál senzora, skočí sa na stav stOn.
  • stHold30 - Stav udržiavanie osvetlenia na 30 percent. Ak príde signál senzora, skočí sa na stav stOn.
enum states {stBoot, stDark, stOn, stHold, stOff, stOff30, stHold30};
states state=stBoot;

​A jednotlivé stavy majú v kóde svoje funkcie, ktoré riadia správanie osvetlenia.

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árne stmievanie

Lineárne stmievanie je zabezpečené pomocou tabuľky predpočítaných hodnôt.

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
};

Napríklad počas zhasínania vyzerá kód takto. Keď to teraz spätne popisujem, uvedomujem si, že táto funkcia má svoje muchy a časť kódu sa nikdy nevykoná. Lebo keď klesne hodnota v premennej value pod 20, vyskočí sa von z funkcie a kód na jej konci sa nikdy nevykoná.

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;
}

Dva kroky stmievania

Toto je veľmi dôležitá úprava. Stmievanie sa totiž vykonáva iba pár sekúnd a pokiaľ sa začalo stmievanie preto, lebo ste sa v miestnosti prestali pohybovať (napríklad ste sa začítali do časopisu), nemali by ste dostatok času na zamávanie rukou, aby sa svetlo zase zaplo na plnú intenzitu. Preto je stmievanie rozdelené na dva intervaly. Medzi nimi je vložený udržiavací interval v šere, ktorý vám dáva dostatok času na reakciu.

///
/// 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

Zdrojový kód sa nachádza na GitHub.


10.05.2017


Menu