PR

ArduinoのライブラリでRGB LEDを操作する

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

RGB LED用のAdafruit_NeoPixelライブラリを使用するとでRGB LEDで様々な色のパターンで点灯させることができます。Arduino IDEにAdafruit_NeoPixelライブラリを追加してRGB LEDを操作する方法を説明しています。

動作確認にはUNOの拡張ボードであるGrove Base Shield V2(Seeed Studio:秋月電子で購入)及びGrove Mech Keycap(Seeed Studio:秋月電子で購入)を使用しています。

Arduino UNO(以下Arduinoとします。)を対象とします。Arduinoのライブラリを使用して動作確認したことをまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Adafruit NeoPixelライブラリを追加する

Adafruit_NeoPixelライブラリの追加
Adafruit_NeoPixelライブラリの追加

Arduino IDEでライブラリの追加を行います。ライブラリマネージャーを開いて検索欄に「pixel」(adafruitなど)などライブラリ名の一部を入力するとライブラリの候補が表示されます。候補の中から「Adafruit NeoPixel by Adafruit」をインストールします。

RGB LEDを操作するためにArduinoの標準関数であるdigitalWrite()とタイマを使ってDOを操作する場合ライブラリの処理の遅延の為RGB LEDの操作に必要なタイミング波形を生成することができません。

Adafruit NeoPixelライブラリはArduinoマイコンの操作をアセンブラ(機械語)に近づけることで処理による遅延を可能な限り減らすことで精度の良いタイミング波形を生成するように工夫されています。

Adafruit NeoPixelライブラリは上記のことを意識しなくてもメンバー関数をコールすることで点灯パターンを指定できるようになっています。マイコンのDOでRGB LEDを操作する場合のタイミング波形の考え方を下記記事にまとめています。

PICマイコン(PIC16F1827)でRGB LEDを操作する

広告

ライブラリの準備と初期化

#include <Adafruit_NeoPixel.h>

#define NUMPIXELS 1 // Popular NeoPixel ring size
//Adafruit_NeoPixelクラスの型の変数を宣言
Adafruit_NeoPixel pixels(NUMPIXELS, PIXEL_PIN, NEO_GRB  + NEO_KHZ800);

void setup() {
  pixels.begin(); //DOなどの設定を行う
  pixels.clear(); //色パターンクリア
  pixels.show(); //色パターンを表示(点灯)
}

ライブラリを使用するためAdafruit_NeoPixel.hをインクルードします。Adafruit_NeoPixelクラスの型の変数を宣言してインスタンス化します。インスタンス化した変数の第1引数にはRGB LEDを操作する数を指定します。第2引数にはRGB LEDを操作するDOピンを指定します。第3引数にはRGB LEDの色のタイプ及びタイミング波形の周波数を選択します。

Grove Mech KeycapはGRBの順に点灯パターンをつくるためNEO_GRBを指定しています。RGBの順に点灯パターンを作る場合はNEO_RGBを指定します。またGrove Mech Keycapに使用しているRGB LEDの周期は最大で800kHzで動作するものであるためNEO_KHZ800を指定して加算しています。

begin()関数はインスタンス化した変数で指定したDOのピンの設定を行います。clear()関数は点灯のパターンをクリアします。show()関数で指定した点灯のパターンでLEDを点灯します。

広告

ライブラリの使用例

void loop() {
  if( timled == TIME_UP){
    timled = LED_FL;
    if(cntflg){
      cntflg = false;
      pixels.setPixelColor(0,red,green,blue); //色パターンの指定
      pixels.show(); //色パターンで点灯
    }
  }
}

色パターンの指定にはsetPixelColor()関数を使用します。第1引数には点灯させるLEDのインデックス番号を指定します。インデックス番号はRGB LEDを連結して点灯する場合の番号です。第2引数は赤色の点灯パターンを0~255で指定します。第3引数は緑色の点灯パターンを0~255で指定します。第4引数は青色の点灯パターンを0~255で指定します。

setPixelColor()関数で点灯パターンを指定してshow()関数で点灯させます。一度点灯させると点灯パターンを変えない限り点灯し続けるため点灯パターンを切り替える場合のみsetPixelColor()及びshow()を処理するようにします。

スポンサーリンク

動作確認

動作確認用の回路図
動作確認用の回路図

Grove Mech Keycapのボタンを押すと赤→緑→青→赤→・・・繰り返しの順でパターン変更する色の種別を変更します。SW1を押すと選択している色に対してカウントを+1し、長押しすると連続して更新するようにしています。SW2を押すと選択している色に対してカウントを-1し、長押しすると連続して更新するようにしています。シリアルモニタでカウント値を確認しながら点灯のパターンを確認しました。

シリアルモニタの結果
シリアルモニタの結果

シリアルモニタにおいて4つのパターンでRGB LEDの点灯させました。パターン1は赤:255、緑:255、青:0とします。パターン2は赤:255、緑:255、青:255の場合とします。パターン3は赤:0、緑:255、青:255の場合とします。パターン4は赤:255、緑:0、青:255の場合とします。パターンによる点灯の様子は以下の通りです。

パターンによる点灯の様子
パターンによる点灯の様子

パターン1は赤と緑の合成で黄色になっています。パターン2は赤・緑・青のすべてを合成した白になっています。パターン3は緑と青の合成で水色になっています。パターン4は赤と青の合成で紫色になっています。

Grove Mech KeycapのボタンとSW1とSW2の組み合わせによって点灯のパターンが変更できていることが確認できました。

光の三原色で様々な色を作ることができるのでRGB LEDを使ったイルミネーションなどに応用できて楽しそうです。

広告

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include <Adafruit_NeoPixel.h>

#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる
#define LED_CNG 100
#define NUMPIXELS 1 // Popular NeoPixel ring size
#define BUTTON_PIN   2
#define PIXEL_PIN    3
#define DI_FILT_MAX 4
#define TIME_FILTER_MAX 1
#define BTN_ON_KEEP  20
#define BTN_ON_CLR 300
#define LED_FL 10

enum DI_NO{
  DI1 = 0,
  DI2,
  DI3,
  DI_MAX
};

typedef enum{
  LED_R = 0,
  LED_G,
  LED_B,
  LED_MAX        
}LED_MODE_NO;

struct DIFILT_TYP{
  uint8_t wp;
  uint8_t buf[DI_MAX][DI_FILT_MAX];
  uint8_t di[DI_MAX];
};

Adafruit_NeoPixel pixels(NUMPIXELS, PIXEL_PIN, NEO_GRB  + NEO_KHZ800);
DIFILT_TYP difilt;
int8_t timdifilt;
int8_t timled;
int16_t timbtnkeep[DI_MAX];
uint32_t beforetimCnt = millis();
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t colormd;
bool cntflg;
bool btnflg[DI_MAX];

/*** Local function prototypes */
void mainTimer(void);
void DiFilter(void);
void mainApp(void);
void BtnCnt(uint8_t md, bool plus);

void setup() {

  Serial.begin(115200);

  pinMode(BUTTON_PIN, INPUT);
  pinMode(PIN5,INPUT_PULLUP);
  pinMode(PIN6,INPUT_PULLUP);

  for( uint8_t i=0; i < 10; i++ ){
    mainTimer();
    DiFilter();
    delay(10);
  }  

  pixels.begin();
  pixels.clear();
  pixels.show(); // Initialize all pixels to 'off'
}

void loop() {

  mainTimer();
  DiFilter();
  mainApp();
}
/* メイン処理 */
void mainApp(void){
  uint8_t i,j;
  
  if( timled == TIME_UP){
    timled = LED_FL;
    if(cntflg){
      cntflg = false;
      pixels.setPixelColor(0,red,green,blue);
      pixels.show();
      Serial.print("red:");
      Serial.print(red);
      Serial.print(" green:");
      Serial.print(green);
      Serial.print(" blue:");
      Serial.println(blue);
    }
  }

  for(i=0; i < DI_MAX; i++){
    if( difilt.di[i] == 0){
      if( btnflg[i] == false ){
        btnflg[i] = true;
        timbtnkeep[i] = BTN_ON_KEEP;
          
        switch(i){
          case 0:
            BtnCnt(colormd,true);
            cntflg = true;
            break;
          case 1:
            BtnCnt(colormd,false);
            cntflg = true;
            break;
          case 2:
            timbtnkeep[i] = BTN_ON_CLR;
            if(++colormd >=LED_MAX){
              colormd = LED_R;
            }
            break;
        }
      }
      else{
        if(timbtnkeep[i] == TIME_UP){
          switch(i){
            case 0:
              timbtnkeep[i] = BTN_ON_KEEP;
              BtnCnt(colormd,true);
              cntflg = true;
              break;
            case 1:
              timbtnkeep[i] = BTN_ON_KEEP;
              BtnCnt(colormd,false);
              cntflg = true;
              break;
            case 2:
              
              red = 0;
              green = 0;
              blue = 0;
              colormd = LED_R;
              //cntflg = true;
              pixels.clear();
              pixels.show();

              for(j=0; j<DI_MAX; j++ ){
                timbtnkeep[j] = TIME_OFF;
              }
              break;
          }
        }
      }
    }
    else{
      btnflg[i] = false;
      timbtnkeep[i] = TIME_OFF;
    }
  }
}
/* Timer Management function add */
void mainTimer(void){

  if ( millis() - beforetimCnt >= BASE_CNT ){
    beforetimCnt = millis();
    //10msごとにここに遷移する
    if( timled > TIME_UP ){
      timled--;
    }
    if( timdifilt > TIME_UP ){
      timdifilt--;
    }
    for(uint8_t i=0; i < DI_MAX; i++){
      if( timbtnkeep[i] > TIME_UP ){
        timbtnkeep[i]--;
      }
    }
  }
}
/* DiFilter function add */
void DiFilter(void){

  if( timdifilt == TIME_UP ){
    timdifilt = TIME_FILTER_MAX;
    difilt.buf[0][difilt.wp] = digitalRead(PIN5);
    difilt.buf[1][difilt.wp] = digitalRead(PIN6);
    difilt.buf[2][difilt.wp] = !digitalRead(BUTTON_PIN);
    //MECH KEYCAPはボタンを押すとHなので反転する
    for(uint8_t i=0; i<DI_MAX; i++){
      if( difilt.buf[i][0] == difilt.buf[i][1] &&
      difilt.buf[i][1] == difilt.buf[i][2] &&
      difilt.buf[i][2] == difilt.buf[i][3] ){ //4回一致を確認
      difilt.di[i] = difilt.buf[i][0];
      }
    }

    if( ++difilt.wp >= DI_FILT_MAX ){
      difilt.wp = 0;
    }
  }
}
/* ボタンによる値の更新 */
void BtnCnt(uint8_t md, bool plus){
    
  switch(md){
    case LED_R:
      if(plus){
        ++red;
      }
      else{
        --red;
      }
      break;
    case LED_G:
      if(plus){
        ++green;
      }
      else{
        --green;
      }           
      break;
    case LED_B:
      if(plus){
        ++blue;
      }
      else{
        --blue;
      }                   
      break;
  }
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

PR:アクセンチュアの転職なら【コンサルアクシスコンサルティング】

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました