スクラッチ&スクラップ

簡単なプログラミングや電子工作など。しょうもない工作の記録。

NeoPixel カラーピッカー

f:id:macrochelys99:20180210161802j:plain:w600

NeoPixel 楽しいんだけども

狙った色が出なくて RGB値を変更しては書き込みを繰り返したりとか、ありがちです。割とどうでもいいところなのに時間がかかります。

書き込む前に光らせて確認してみよう


NeoPixel カラーピッカー

というわけで NeoPixel を指定した RGB値 で光らせるだけのモノです。色合いを保ったまま明るさを変えて…とかできるように HSV値での設定もできるようにしました。

ハード

Arduinoキーパッドシールド

NeoPixel は WS2812B

改良の余地

NeoPixel には、今回使った WS2812B の他にも、赤緑青白の4色の素子の入った SK6812 もあります。そちらはまたちょっと色合いが変わってくるので両方対応できるといいかも。

スケッチ

#define ITEM  "ColorPicker"
#define VER   "180124"

#include <Adafruit_NeoPixel.h>
#define PIN_PIXELS  A1
#define PIXELS_N    9

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXELS_N, PIN_PIXELS, NEO_GRB + NEO_KHZ800);

int   hue = 0,    // 色相
      sat = 255,  // 彩度
      val = 128,  // 明度
      red = 128,  // 赤
      grn = 0,    // 青
      blu = 0;    // 緑

int   sp = 0,   // selected position 設定項目
      ap = 1;   // active pixels 点灯数

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// LCDへの表示位置
#define LCD_H_T (0,0)   // hue title
#define LCD_H_V (1,0)   // hue value
#define LCD_S_T (6,0)   // sat title
#define LCD_S_V (7,0)   // sat value
#define LCD_V_T (12,0)  // val title
#define LCD_V_V (13,0)  // val value
#define LCD_R_T (0,1)   // red title
#define LCD_R_V (1,1)   // red value
#define LCD_G_T (6,1)   // grn title
#define LCD_G_V (7,1)   // grn value
#define LCD_B_T (12,1)  // blu title
#define LCD_B_V (13,1)  // blu value

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

void setup() {
  lcd.begin(16, 2);
  pixels.begin();

  opening();
  change_value(0);
  pixels_set();

  lcd.clear();
  set_lcd_title();
  set_lcd_value();
  lcd.cursor();
  lcd.blink();
  change_sp(0); // カーソル移動
}

void loop() {
  load_key();
  delay(10);
  //  test_ad_value(); // キー入力デバッグ用
  //  delay(100);
}

void opening() {
  lcd.clear();
  lcd.print(ITEM);
  lcd.setCursor(0, 1);
  lcd.print(VER);


  int pix[] = {0, 1, 2, 5, 8, 7, 6, 3}; // NeoPixelを点灯させる順番
  for (int n = 0; n < sizeof(pix) / sizeof(pix[0]); n++) {
    hsv_to_rgb(n * 40 - 3, 255, 128);
    pixels.setPixelColor(pix[n], pixels.Color(red, grn, blu));
    pixels.show();
    delay(200);
  }

  hsv_to_rgb(0, 0, 128);
  pixels.setPixelColor(4, pixels.Color(red, grn, blu));
  pixels.show();
  delay(500);
}

void change_sp(int i) {
  sp = sp + i;
  if (sp > 5) sp = 0;
  if (sp < 0) sp = 5;
  switch (sp) {
    case 0:
      lcd.setCursor LCD_H_T;
      break;
    case 1:
      lcd.setCursor LCD_S_T;
      break;
    case 2:
      lcd.setCursor LCD_V_T;
      break;
    case 3:
      lcd.setCursor LCD_R_T;
      break;
    case 4:
      lcd.setCursor LCD_G_T;
      break;
    case 5:
      lcd.setCursor LCD_B_T;
      break;
  }
}

void change_value(int i) {
  int h = hue,
      s = sat,
      v = val,
      r = red,
      g = grn,
      b = blu;
  switch (sp) {
    case 0:
      h = h + i;
      if (h < 0) h = h + 360;
      if (h >= 360) h = h - 360;
      hsv_to_rgb(h, s, v);
      hue = h;
      break;
    case 1:
      s = s + i;
      if (s > 255) s = 255;
      if (s < 0) s = 0;
      hsv_to_rgb(h, s, v);
      sat = s;
      break;
    case 2:
      v = v + i;
      if (v > 255) v = 255;
      if (v < 0) v = 0;
      hsv_to_rgb(h, s, v);
      val = v;
      break;
    case 3:
      r = r + i;
      if (r > 255) r = 255;
      if (r < 0) r = 0;
      rgb_to_hsv(r, g, b);
      red = r;
      break;
    case 4:
      g = g + i;
      if (g > 255) g = 255;
      if (g < 0) g = 0;
      rgb_to_hsv(r, g, b);
      grn = g;
      break;
    case 5:
      b = b + i;
      if (b > 255) b = 255;
      if (b < 0) b = 0;
      rgb_to_hsv(r, g, b);
      blu = b;
      break;
  }
}

/*----------------------------------------
    LCD FUNCTIONS
  ----------------------------------------*/
void set_lcd_title() {
  lcd.setCursor LCD_H_T;
  lcd.print("H");

  lcd.setCursor LCD_S_T;
  lcd.print("S");

  lcd.setCursor LCD_V_T;
  lcd.print("V");

  lcd.setCursor LCD_R_T;
  lcd.print("R");

  lcd.setCursor LCD_G_T;
  lcd.print("G");

  lcd.setCursor LCD_B_T;
  lcd.print("B");
}

void set_lcd_value() {
  lcd.setCursor LCD_H_V;
  aline3(hue);

  lcd.setCursor LCD_S_V;
  aline3(sat);

  lcd.setCursor LCD_V_V;
  aline3(val);

  lcd.setCursor LCD_R_V;
  aline3(red);

  lcd.setCursor LCD_G_V;
  aline3(grn);

  lcd.setCursor LCD_B_V;
  aline3(blu);
}

void aline3(int num) {
  if (num < 100) lcd.print(" ");
  if (num < 10) lcd.print(" ");
  lcd.print(num);
}

/*----------------------------------------
    KEY FUNCTIONS
  ----------------------------------------*/
void load_key() {
  static int  prev_key = btnNONE,
              prev2_key = btnNONE;

  static unsigned long on_time = millis();
  unsigned long now = millis();

  int key = read_LCD_buttons();
  if (prev_key == btnNONE && key != btnNONE) on_time = millis();

  // UP/DOWN用 有効判定
  boolean   valid = prev_key == btnNONE && key != btnNONE;
  valid = valid || now - on_time > 500;

  switch (key) {
    case btnRIGHT:
      //      if (prev_key == btnNONE) delay(100);
      if (prev_key == key && prev2_key == btnNONE) {
        change_sp(1);
      }
      break;
    case btnLEFT:
      //      if (prev_key == btnNONE) delay(100);
      if (prev_key == key && prev2_key == btnNONE) {
        change_sp(-1);
      }
      break;
    case btnUP:
      if (valid) {
        change_value(1);
        set_lcd_value();
        change_sp(0);
        pixels_set();
      }
      break;
    case btnDOWN:
      if (valid) {
        change_value(-1);
        set_lcd_value();
        change_sp(0);
        pixels_set();
      }
      break;
    case btnSELECT:
      //      if (prev_key == btnNONE) delay(100);
      if (prev_key == key && prev2_key == btnNONE) {
        ap++;
        if (ap > 4) ap = 0;
        pixels_set();
      }
      break;
    case btnNONE:
      break;
  }
  prev2_key = prev_key;
  prev_key = key;
}

// read the buttons
int read_LCD_buttons() {
  int adc_key_in = analogRead(0); // キー入力のAD値 読み込み
  if (adc_key_in > 1000)                    return btnNONE;
  if (adc_key_in < 10)                      return btnRIGHT;  // AD実測 0
  if (111 < adc_key_in && adc_key_in < 141) return btnUP;     // AD実測 131
  if (297 < adc_key_in && adc_key_in < 317) return btnDOWN;   // AD実測 307
  if (469 < adc_key_in && adc_key_in < 489) return btnLEFT;   // AD実測 479
  if (711 < adc_key_in && adc_key_in < 731) return btnSELECT; // AD実測 721
  return btnNONE;  // どれにも該当しなかったら何も押されてないものとする
}

/*----------------------------------------
    COLOR FUNCTIONS
  ----------------------------------------*/
void rgb_to_hsv(int r, int g, int b) {
  if (r > 255) r = 255;
  if (r < 0) r = 0;
  if (g > 255) g = 255;
  if (g < 0) g = 0;
  if (b > 255) b = 255;
  if (b < 0) b = 0;

  float h, s, v;

  // r,g,b の最大値と最小値を求める
  float ma = max(r, g);
  ma = max(ma, b);
  float mi = min(r, g);
  mi = min(mi, b);

  if (r == g && r == b) {
    h = 0;
  } else if (r >= g && r >= b) {
    h = 60.0 * (g - b) / (ma - mi);
  } else if (g >= r && g >= b) {
    h = 60.0 * (b - r) / (ma - mi) + 120.0;
  } else if (b >= r && b >= g) {
    h = 60.0 * (r - g) / (ma - mi) + 240.0;
  }

  if (h < 0) h = h + 360.0;
  hue = h + 0.5;
  if (hue >= 360) hue = hue - 360;

  s = (ma - mi) / ma * 255.0;
  v = ma;
  sat = s + 0.5;
  val = v + 0.5;
}

void hsv_to_rgb(float h, float s, float v) {

  while (h < 0) h += 360.0;
  while (h >= 360) h -= 360.0;

  float ma = v;
  float mi = ma - (s / 255.0 * ma);
  float r, g, b;

  switch ((int)h / 60) {
    case 0:
      r = ma;
      g = (h / 60.0) * (ma - mi) + mi;
      b = mi;
      break;
    case 1:
      r = ((120.0 - h) / 60.0) * (ma - mi) + mi;
      g = ma;
      b = mi;
      break;
    case 2:
      r = mi;
      g = ma;
      b = ((h - 120.0) / 60.0) * (ma - mi) + mi;
      break;
    case 3:
      r = mi;
      g = ((240.0 - h) / 60.0) * (ma - mi) + mi;
      b = ma;
      break;
    case 4:
      r = ((h - 240.0) / 60.0) * (ma - mi) + mi;
      g = mi;
      b = ma;
      break;
    case 5:
      r = ma;
      g = mi;
      b = ((360.0 - h) / 60.0) * (ma - mi) + mi;
      break;
  }

  // 四捨五入してグローバル変数に返す
  red = r + 0.5;
  grn = g + 0.5;
  blu = b + 0.5;
}

/*----------------------------------------
    NEOPIXEL FUNCTIONS
  ----------------------------------------*/
void pixels_off() {
  for (int n = 0; n < PIXELS_N; n++) {
    pixels.setPixelColor(n, pixels.Color(0, 0, 0));
  }
  pixels.show();
}

void pixels_set() {
  switch (ap) {
    case 0:
      pixels_off();
      break;
    case 1:
      for (int n = 0; n < PIXELS_N; n++) {
        switch (n) {
          case 4:
            pixels.setPixelColor(n, pixels.Color(red, grn, blu));
            break;
          default:
            pixels.setPixelColor(n, pixels.Color(0, 0, 0));
        }
      }
      break;
    case 2:
      for (int n = 0; n < PIXELS_N; n++) {
        switch (n) {
          case 4:
          case 5:
            pixels.setPixelColor(n, pixels.Color(red, grn, blu));
            break;
          default:
            pixels.setPixelColor(n, pixels.Color(0, 0, 0));
        }
      }
      break;

    case 3:
      for (int n = 0; n < PIXELS_N; n++) {
        switch (n) {
          case 4:
          case 5:
          case 7:
          case 8:
            pixels.setPixelColor(n, pixels.Color(red, grn, blu));
            break;
          default:
            pixels.setPixelColor(n, pixels.Color(0, 0, 0));
        }
      }
      break;

    case 4:
      for (int n = 0; n < PIXELS_N; n++) {
        pixels.setPixelColor(n, pixels.Color(red, grn, blu));
      }
      break;
  }
  pixels.show();
}

/*----------------------------------------
    TEST FUNCTIONS
  ----------------------------------------*/
void test_ad_value() {
  lcd.clear();
  lcd.print(analogRead(A0));
}