NeoPixel Ring HSV - Nekonečný príbeh

Zápisník experimentátora

Hierarchy: WS2812

Predchádzajúce články (01, 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.

Použité súčiastky

  • Arduino Uno {linkArduino} - Môžete použiť ľubovoľnú verziu Arduina.
  • Breadboard {linkBreadboard}
  • NeoPixel Ring 24 LED {linkNeopixel} - Obsahuje 24 RGB LED diód s čipom WS2812B.
  • Rezistor 1k {linkR} - Rezistor sa pripojí na dátový vstup NeoPixel Ringu. Slúži ako ochrana pinu.
  • Kondenzátor 220 uF {linkC} - Kondenzátor sa pripojí medzi VCC a GND na NeoPixel Ringu. Slúži na vyrovnávanie napäťových špičiek.
  • Tlačidlo {linkButton} - Potrebujeme dve tlačidlá. Jedným tlačidlom budeme zvyšovať hodnotu HUE a druhým tlačidlom ju budeme znižovať.
  • OLED {linkOled} - Na displeji sa bude zobrazovať hodnota HUE.

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žité knižnice

Použil som tieto knižnice. Obe knižnice sa dajú nainštalovať pomocou správcu knižníc.

Príklad 1

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

Príklad 2

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

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



Video


13.06.2019


Menu