Zápisník experimentátora
Hierarchy: WS2812
Previous articles (0, 1, 2) and videos on using the HSV (hue, saturation, value) color model have shown that readers still have difficulty setting the right value to obtain the desired color on the rotating ring. We'll try to resolve this in this article. Using the two buttons and an OLED display we set an appropriate color and its value can be read directly on the display.
This question is periodically repeated in the video comments. The red color is nice, but how do I do it to rotate blue color? Or green color? Or yellow? Once I tried to solve it and I made one more video in which I used three potentiometers to set HSV and rotation speed. It didn't help. The questions were repeated.
Again, I put the original circuit on the breadboard, modified the source code for the serial port listings, and added a HUE table to the video. And when I added the table to the video, I thought. What is the simplest control for Arduino? Button. Any reading of a serial port statement is obviously complicated for many readers. But we use buttons every day. Whether we set the microwave heating time or just set the clock time, we are taught to use two buttons. One to increase the value and the other to decrease it. And so I invented this example with two buttons and one OLED display where everyone can read the current HUE value they see rotating on the ring.
Protective parts are needed to prevent damage to the NeoPixel Ring. Details of protection are provided in the article on powering these diodes.
I used these libraries. Both libraries can be installed using the library manager.
This is the source code of the first example. First, several definitions of constants. I have NeoPixel Ring connected to pin 6. I used NeoPixel Ring with 24 LEDs. Then there are several global variables. I use the hue
and oldhue
variables to test if the HUE value has changed. Then I will display the current value on the display. I use SPI OLED display on such pins to use fast SPI transmission. You can also use the I2C display, but then you have to choose a different constructor to match your display.
Buttons are wired to use internal pull-up resistors in Arduine. In this case, we do not need to take care of the button debounce, because I only check periodically that the button is pressed and I increase or decrease the HUE accordingly. I redraw the display only if the HUE value changes. At first glance it is not visible in the code, but the screen redraw takes a few milliseconds, and this affects that when you press the button, the NeoPixel Ring animation slows down slightly. It is an imperfection that I will try to solve in the second example.
#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);
}
In this example, I will try to adjust the display repaint algorithm so that it does not slow down animation on the NeoPixel Ring when the button is pressed. There are several ways to solve this. In this case, I take advantage of the fact that 50 ms is waiting time between the shifts pixels on the NeoPixel Ring. We estimate how long it takes to render on the display and then wait a shorter time. After several attempts, I guessed that the rendering takes about 40 ms and I adjusted the value in the temporary variable dly
accordingly.
#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);
}
The source code is located on GitHub.
13.06.2019