Arduino LEDky

& Tlačítka

NSWI170, 2025, Labs 03

Jáchym Bártík

Čas je relativní

  • Neměli bychom čekat, že událost nastane v nějakém čase
  • Při velké zátěži může jeden běh loop trvat více milisekund
    • Např. millis() vrací: 1, 3, 4, 6, 9, 11, ...
constexpr int PERIOD = 10;

void loop() {
    int currentTime = millis();
    if (currentTime - lastTime == PERIOD) {     // Don't do this!
        lastTime = currentTime;
        
        doSomething();
    }
}
    if (currentTime - lastTime >= PERIOD) {     // Still not perfect
        lastTime = currentTime;
  • Nyní máme jistotu, že čas nebude přeskočen
    • Průměrná perioda ale bude větší, než 10 ms
constexpr int PERIOD = 10;

void loop() {
    int currentTime = millis();
    if (currentTime - lastTime >= PERIOD) {     // Do this instead
        lastTime += PERIOD;
        
        doSomething();
    }
}
  • Ideální řešení
    • Čas nebude nikdy přeskočen
    • V dlouhodobém průměru se limitně blížíme k 10 ms

Kletba #3 - DRY

  • Don't Repeat Yourself
  • Vše je napsáno jen jednou
    • Rychlejší vývoj, snadnější změny
    • Lepší kontrola chyb, optimalizace
    • Větší přehlednost
  • Nástroje - funkce, konstanty, smyčky, objekty, ...
  • Čemu se vyhnout:
    • Sjednocování příliš různorodého kódu
    • Horší rozšiřitelnost, plno if-else podmínek
    • Zbytečně moc abstrakcí

KISS

  • Keep It Simple, Stupid
  • Jednodušší systémy jsou lepší, než složitější
    • Rychlejší vývoj, snadnější změny
    • Lepší kontrola chyb, optimalizace
    • Větší přehlednost
  • Může však být v rozporu s velmi DRY kódem
    • ​Abstrakce, "obecná" řešení, ...
  • Úspěsná kombinace obou principů je skutečným uměním

Cvičení #1

  • Vytvořte funkci, která zobrazí číslo na LEDkách
    • Modulo 16
    • Převedení do binární soustavy
    • Zobrazení bitů pomocí čtyř LED
  • Hint: binární operátory
    • & (and), | (or), ~ (negation)
    • <<, >> (posun doleva, doprava)
7 & 2  // 0111 & 0010 -> 0010 = 2
7 | 8  // 0111 | 1000 -> 1111 = 15

2 << 1 // 0010 -> 0100 = 4
3 << 2 // 0011 -> 1100 = 12

Cvičení #2

  • Vytvořte čítač, který se periodicky inkrementuje
    • Hodnotu vypisujte pomocí funkce ze cvičení #1
    • Modulo není nutně potřeba - stejně jde jen o poslední čtyři bity

Tlačítka

  • Nastavení pinu pro čtení
    • Je to tak sice defaultně, ale stejně by se to mělo dělat kvůli čitelnosti
void setup() {
    pinMode(button1_pin, INPUT);
}
  • Čtení je podobné jako zapisování
    • Pokud funkce vrátí ON, tlačítko je zapnuté
void loop() {
    bool isPressed = digitalRead(button1_pin) == ON;
}

Cvičení #3

  • Zařiďte,  aby LEDka svítila právě tehdy, když je stisknuté tlačítko
  • Bonus: Druhé tlačítko přepíná, která LEDka to bude
#include <funshield.h>

void setup() {
    pinMode(led1_pin, OUTPUT);
    pinMode(button1_pin, INPUT);
}

void loop() {
    digitalWrite(led1_pin, digitalRead(button1_pin));
}

Cvičení #4

  • Vytvořte čítač, který se periodicky inkrementuje pokud je stisknuto tlačítko
    • Vyjděte z úlohy #2
    • Čas se měří jen pokud je stisknuto tlačítko
      • Pozor, reálný čas samozřejmě běží pořád

Cvičení #5

  • Vytvořte čítač, který se inkrementuje stisknutím tlačítka
    • Hodnota se časem (sama) nijak nemění
    • Při stisknutí tlačítka se zvýší o 1
    • Je nutné rozlišit, ve kterém průběhu loop bylo tlačítko stisknuto, tj. kdy digitalRead poprvé vrátila false
  • Bonus: Debounce
    • Tlačítko může oscilovat, neboli několikrát rychle za sebou změnit stav
    • Řešení - po každém stisknutí jej na chvíli zablokujte
    • Vyzkoušejte různé délky blokačního intervalu

Cvičení #6

  • Přidejte další tlačítka
    • Jedno dekrementuje čítač
      • Tady už bude nutné použít modulo
      • x = (x + 15) % 16
    • Druhé jej zvýší o 5
    • Všechna tři tlačítka pracují se stejným čítačem
  • Všimněte si, že všechna tlačítka dělají v podstatě to samé
    • Zaměřte se na odstranění duplicitní logiky
    • Dále si můžete všimnout, že už je tu neprakticky moc proměnných ...

Lepší řešení

  • Cílem je oddělit logiku tlačítek od logiky počítání času a dalších částí programu
    • K tomu se hodí funkce
    • Ale vyžadují předávání příliš mnoha proměnných
    • Navíc neoddělují data, pouze funkcionality
  • Struktury:
struct Button {
    int pin;
    bool isPressed;
};

void processButton(Button &button) {
    if (digitalRead(button.pin) == ON && !button.isPressed)
        ... // Handle the click event
        
}

Button buttons[3];
processButton(buttons[0]);
  • Navíc mohou obsahovat i funkce
    • Kombinují data s funkcionalitami, které se jich týkají
  • Pro každý prvek lze specifikovat viditelnost
    • public vs. private
    • Defaultně je vše public
  • Třídy - úplně stejné jako struktury, akorát že defaultně je vše private
class Button { // This class does exactly the same as the previous structure
public:
    int pin;
    bool isPressed;
};
  • Používejte výhradně třídy
    • Struktury jsou specifikem C/C++
    • Jinde se nepoužívají
    • Proto mohou být dost matoucí

Domácí úkol #3

  • Vytvořte tlačítka, která inkrementují čítač jak spojitě, tak diskrétně
    • Tlačítko inkrementuje čítač ihned po stisknutí
    • Zároveň, pokud jej držíte dostatečně dlouho, začne samo periodicky inkrementovat čítač