Zápisník experimentátora
Hierarchy: WS2812
Predchádzajúce články (0, 1, 2) a videá o používaní farebného modelu HSV (hue, saturation, value) ukázali, že čitatelia majú stále problém nastaviť správnu hodnotu tak, aby získali želanú farbu na rotujúcom prstenci. V tomto článku sa to pokúsime vyriešiť. Pomocou dvoch tlačidiel a OLED displeja nastavíme vhodnú farbu a jej hodnotu si prečítame priamo na displeji.
V komentároch k videu nachádzam pravidelne takúto otázku. Tá červená farba je pekná, ale ako to mám urobiť, aby tam rotovala modrá farba? Alebo zelená farba? Alebo žltá farba? Raz som sa to už pokúsil vyriešiť a urobil som ešte jedno video, v ktorom som používal tri potenciometre na nastavenie HSV a rýchlosti rotácie. Nepomohlo to. Otázky sa stále opakovali.
Opäť som si poskladal na breadboarde pôvodné zapojenie elektrického obvodu, upravil som zdrojový kód o výpisy na sériový port a k videu som dopísal tabuľku hodnôt HUE. A keď som tú tabuľku doplnil pod video, napadla ma myšlienka. Čo je najjednoduchší ovládací prvok k Arduinu? Tlačidlo. Akékoľvek čítanie výpisu na sériovom porte je zjavne pre mnohých čitateľov komplikované. Ale tlačidlá používame každý deň. Či už si nimi nastavujeme čas ohrievania v mikrovlnke, alebo si len nastavujeme čas na hodinách, sme naučení používať dve tlačidlá. Jedno na zvýšenie hodnoty a druhé na jej zníženie. A tak som vymyslel tento príklad s dvomi tlačidla a jedným OLED displejom, na ktorom si každý môže prečítať aktuálnu hodnotu HUE, ktorú vidí rotovať na prstenci.
Ochranné súčiastky sú potrebné preto, aby sa NeoPixel Ring nepoškodil. Podrobnosti ochrany sú uvedené v článku o napájaní týchto diód.
Použil som tieto knižnice. Obe knižnice sa dajú nainštalovať pomocou správcu knižníc.
Takto vyzerá zdrojový kód k prvému príkladu. Najprv niekoľko definícií konštánt. NeoPixel Ring mám pripojený k pinu 6. Použil som NeoPixel Ring s 24 LED diódami. Potom nasleduje niekoľko globálnych premenných. Premenné hue
a oldhue
používam na testovanie, či sa zmenila hodnota HUE. Vtedy vypíšem aktuálnu hodnotu na displeji. SPI OLED displej používam na takých pinoch, aby sa dal využívať rýchly SPI prenos. Môžete použiť aj I2C displej, ale vtedy si musíte vybrať iný konštruktor, ktorý bude zodpovedať vášmu displeju.
Tlačidlá sú zapojené tak, aby používali interné pull-up rezistory v Arduine. V tomto prípade sa nemusíme zapodievať ošetrovaním zákmitov tlačidiel, pretože si len periodicky kontrolujem, či je dané tlačidlo stlačené a podľa toho zvyšujem alebo znižujem hodnotu HUE. Displej prekresľujem iba ak sa zmení hodnota HUE. Na prvý pohľad to nie je viditeľné v kóde, ale prekreslenie displeja trvá niekoľko milisekúnd a to má vplyv na to, že pri stlačení tlačidla sa animácia na NeoPixel Ring mierne spomalí. Je to nedokonalosť, ktorú sa pokúsim vyriešiť v druhom príklade.
#include <Adafruit_NeoPixel.h>
#include <U8g2lib.h>
#include "hsv.h"
// data pin
#define PIN 6
// led count
#define CNT 24
// max Hue
#define MAXHUE 256*6
int position = 0;
int hue = 0;
int oldhue = -1;
char output[128];
U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(CNT, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
u8g2.begin();
u8g2.setFont(u8g2_font_ncenB10_tr);
}
void loop() {
// hue - red
// saturation - max
// value - 0-255
for (int i = 0; i < CNT; i++) {
strip.setPixelColor((i + position) % CNT, getPixelColorHsv(i, hue, 255, strip.gamma8(i * (255 / CNT))));
}
strip.show();
position++;
position %= CNT;
if (digitalRead(2) == LOW) {
hue += 1;
hue %= MAXHUE;
}
if (digitalRead(3) == LOW) {
hue -= 1;
if (hue < 0)
hue = MAXHUE - 1;
}
if (oldhue != hue) {
oldhue = hue;
sprintf(output, "hue: %d", hue);
u8g2.firstPage();
do {
u8g2.drawStr(16, 32, output); // write something to the internal memory
for (int j = 0; j <= 128; j += 2)
u8g2.drawLine(j, 0, j, (j % 10 == 0 ? 2 : 1));
for (int j = 0; j <= 64; j += 2)
u8g2.drawLine(0, j, (j % 10 == 0 ? 2 : 1), j);
} while ( u8g2.nextPage() );
}
delay(50);
}
V tomto príklade sa pokúsim upraviť algoritmus prekreslenia displeja tak, aby sa pri stlačení tlačidla animácia na NeoPixel Ring nespomalila. Dá sa to vyriešiť viacerými spôsobmi. V tomto prípade využijem to, že medzi jednotlivými posunmi na NeoPixel Ring program čaká 50 ms. Odhadneme, koľko trvá vykreslenie na displeji a vtedy budeme čakať kratší čas. Po niekoľkých pokusoch som odhadol, že vykreslenie trvá približne 40 ms a podľa toho som upravil hodnotu v pomocnej premennej dly
.
#include <Adafruit_NeoPixel.h>
#include <U8g2lib.h>
#include "hsv.h"
// data pin
#define PIN 6
// led count
#define CNT 24
// max Hue
#define MAXHUE 256*6
int position = 0;
int hue = 0;
int oldhue = -1;
char output[128];
U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(CNT, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
u8g2.begin();
u8g2.setFont(u8g2_font_ncenB10_tr);
}
void loop() {
// hue - red
// saturation - max
// value - 0-255
for (int i = 0; i < CNT; i++) {
strip.setPixelColor((i + position) % CNT, getPixelColorHsv(i, hue, 255, strip.gamma8(i * (255 / CNT))));
}
strip.show();
position++;
position %= CNT;
if (digitalRead(2) == LOW) {
hue += 1;
hue %= MAXHUE;
}
if (digitalRead(3) == LOW) {
hue -= 1;
if (hue < 0)
hue = MAXHUE - 1;
}
int dly = 50;
if (oldhue != hue) {
dly = 10;
oldhue = hue;
sprintf(output, "hue: %d", hue);
u8g2.firstPage();
do {
u8g2.drawStr(16, 32, output); // write something to the internal memory
for (int j = 0; j <= 128; j += 2)
u8g2.drawLine(j, 0, j, (j % 10 == 0 ? 2 : 1));
for (int j = 0; j <= 64; j += 2)
u8g2.drawLine(0, j, (j % 10 == 0 ? 2 : 1), j);
} while ( u8g2.nextPage() );
}
delay(dly);
}
Zdrojový kód sa nachádza na serveri GitHub.
13.06.2019