NeoPixel Ring HSV - Neverending story

Zápisník experimentátora

Hierarchy: WS2812

Previous articles (01, 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.

Used parts

  • Arduino Uno {linkArduino} - You can use any version of Arduino.
  • Breadboard {linkBreadboard}
  • NeoPixel Ring 24 LED {linkNeopixel} - Contains 24 RGB LEDs with chip WS2812B.
  • Rezistor 1k {linkR} - The resistor is connected to the NeoPixel Ring data input. It serves to protect the pin.
  • Kondenzátor 220 uF {linkC} - The capacitor is connected between VCC and GND on NeoPixel Ring. It serves for balancing voltage peaks.
  • Tlačidlo {linkButton} - We need two buttons. With one button we will increase the HUE value and the second button will decrease it.
  • OLED {linkOled} - The display will show HUE.

Protective parts are needed to prevent damage to the NeoPixel Ring. Details of protection are provided in the article on powering these diodes.

Used libraries

I used these libraries. Both libraries can be installed using the library manager.

Example 1

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

Example 2

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

Source code

The source code is located on GitHub.



Video


13.06.2019


Menu