M5 / プログラミング

ARGBライトでゲーミング苔

こんにちはLineaです。

前回、湿度センサとUSB加湿器で苔の水やりを自動化してみました。あれからシステムとしては安定して、水を2,3日おきに補充するだけになったので楽になりました。

しかし、なんとなく見た目が寂しいなと思っていたのですが、明かりが足りないことに気が付きました。アクアリウムではライトで照らしてやるのが基本ですから、苔にもそういったライトがあれば映えるのではないかと。

アクアリウム用のライトを買ってきても良かったのですが、折角M5Atomを使っているならライトも制御したいと考え、最近流行りのLEDテープを使って光り方を制御してみようというのが今回の趣旨です。

今回の成果としてこんな感じになりました。虹色に光らせるとゲーミング感(?)満点になったので満足です。

LEDの選定

今回、購入して使用したLEDがこちらです。

LEDテープ類を色々調べているとゲーミングPCの装飾用の製品がおしゃれで丁度よさそうでした。シリコンカバーに覆われているため、光が拡散して線としてきれいに見えます。また、ある程度の防水性も期待できそうです。

こういったLEDテープ類で虹色など光り方を自由に変えられるタイプは軒並みNeoPixel系と呼ばれるLEDチップが使われています。LEDチップ内にICが入っていて、シリアル通信で光り方を個別に制御できるため虹色など様々な光らせ方ができます。また、制御用のライブラリも公開されているので比較的簡単に制御できます。

今回使ったのはゲーミングPC用の製品(いわゆる3ピンARGB)ですが中身は電子工作用のLEDテープと同じなのでそのままM5Atomなどで制御できました。コネクタが配線済みと考えるとむしろPC用はお得かも?

配線

PC用の3ピンARGBタイプのコネクタは次のようになっています。

5VとGNDはそれぞれM5Atomの5V出力、GNDへ接続します。データピンはシリアル通信ができるピンならどこでもいいので、今回は5V,GNDに隣接する25ピンを使用しました。

設置

今回購入した製品には設置用のステー(直線、90°)が付属していたので、利用して水槽の天板に両面テープで固定しました。

LED制御

今回使うNeoPixel系のLEDテープを制御するライブラリとしてFastLEDを使用します。FsatLEDはM5Atom内蔵のLED制御にも使われていて、既にライブラリが入っているはずなのでインストールについては割愛します。

FastLEDライブラリでLED制御する際はライブラリ内のstatic変数で競合するため、M5AtomライブラリのLED制御と同時に使うことができません。M5Atomの初期化時に本体側のLEDをオフにする必要があります(後述)。

プログラム

購入したLEDテープ用に下の制御クラスをM5AtomのLED処理を参考に作成しました。

#ifndef __LEDBar_H
#define __LEDBar_H

#include <FastLED.h>
#include "utility/Task.h"

#define NUM_LEDS_BAR 30
#define DATA_PIN_BAR 25

enum LightingMode 
{
    Static,
    Move,
    Breath,
    Rainbow,
};

class LEDBar: public Task
{
private:
    CRGB _ledbuff[NUM_LEDS_BAR];
    uint8_t _ledNumber;

    LightingMode _mode;
    CRGB _mainColor;
    CHSV _maniHSV;

    uint8_t _movePos;

    SemaphoreHandle_t _xSemaphore = NULL;

public:
    LEDBar();
    ~LEDBar();

    void begin(uint8_t LEDPin = DATA_PIN_BAR ,uint8_t LEDNumber = NUM_LEDS_BAR);
    void run(void *data);

    void setBrightness(uint8_t brightness);
    void setLightingMode(LightingMode mode, CRGB color);
    void lightOff();
};

#endif
#include "LEDBar.h"

LEDBar::LEDBar()
{

}

LEDBar::~LEDBar()
{

}

void LEDBar::begin(uint8_t LEDPin ,uint8_t LEDNumber)
{
    FastLED.addLeds<WS2812, DATA_PIN_BAR>(_ledbuff, LEDNumber);
    _xSemaphore = xSemaphoreCreateMutex();
    _ledNumber = LEDNumber;
}

void LEDBar::run(void *data)
{
    data = nullptr;

    for (int num = 0; num < NUM_LEDS_BAR; num++)
    {
        _ledbuff[num] = 0x000000;
    }
    FastLED.show();
    FastLED.setBrightness(20);

    while (1)
    {
        xSemaphoreTake(_xSemaphore, portMAX_DELAY);
        switch (_mode)
        {
            case LightingMode::Static:
                fill_solid(_ledbuff,_ledNumber,_mainColor);
                break;
            case LightingMode::Move:
                fadeLightBy(_ledbuff,_ledNumber,30);
                _movePos++;
                if(_movePos >= _ledNumber)
                {
                    _movePos = 0;
                }
                _ledbuff[_movePos] = _mainColor;
                break;
            case LightingMode::Breath:
            {
                uint8_t BeatsPerMinute = 16;
                uint8_t beat = beatsin8( BeatsPerMinute, 0, 255);
                _maniHSV.value = beat;
                fill_solid(_ledbuff,_ledNumber,CRGB(_maniHSV));
                break;
            }
            case LightingMode::Rainbow:
                fill_rainbow(_ledbuff,_ledNumber,_maniHSV.hue);
                _maniHSV.hue++;
                break;
            default:
                break;
        }
        FastLED.show();
        xSemaphoreGive(_xSemaphore);
        delay(20);
    }
}

void LEDBar::setBrightness(uint8_t brightness)
{
    xSemaphoreTake(_xSemaphore, portMAX_DELAY);
    FastLED.setBrightness(brightness);
    xSemaphoreGive(_xSemaphore);
}

void LEDBar::setLightingMode(LightingMode mode, CRGB color)
{
    xSemaphoreTake(_xSemaphore, portMAX_DELAY);
    _mode = mode;
    _mainColor = color;
    _maniHSV = rgb2hsv_approximate(color);
    xSemaphoreGive(_xSemaphore);
}

void LEDBar::lightOff()
{
    setLightingMode(LightingMode::Static,CRGB::Black);
}

FastLEDライブラリの基本的な使い方として、LEDの色情報バッファをFastLED.show()で実際にLEDに送信します。
光が動いているように見せるには定期的に色情報バッファの更新と送信を行う必要があるため、表示更新関数run()をタスクで定期実行させています。
マルチスレッドで動作するため、発光モードや色などの設定処理はセマフォで排他制御を行っています

発光のパターンについてはとりあえず4種類を定義して用意してみました。

  • 単色
  • 流れる
  • 明滅
  • レインボー

レインボーはゲーミングの定番だけあってか、ライブラリで既に関数が用意されていて呼ぶだけなので実は1番簡単だったりします。意外と苦労したのは流れる表示の方ですね。

使い方

上のLEDBarクラスは次のように使用します

#include <M5Atom.h>
#include "LEDBar.h"

//LEDBarのインスタンス
LEDBar ledBar;

void setup() {
  //M5スタート 3つ目の引数が内蔵LED制御の有効化なのでFalseにしておく
  M5.begin(true,false,false);

  //LEDバーを初期化 第一引数 データピン、第二引数 LED個数
  ledBar.begin(25,30);
  //LEDバーの更新タスクを開始
  ledBar.setTaskName("LEDBar");
	ledBar.setTaskPriority(2);
	ledBar.start();
  delay(10);
  //LEDバーの明るさを設定
  ledBar.setBrightness(64);
  //LEDバーの発光モードと色を指定
 ledBar.setLightingMode(LightingMode::Breath,CRGB::Red);
 delay(5000);
  //虹色
  ledBar.setLightingMode(LightingMode::Rainbow,CRGB::Blue);
}

begin()関数でLEDのピンと個数を設定します。その後、タスクを開始します。タスク開始後に明るさや発光モード、色を指定して発光させることができます。
FastLEDライブラリの仕様で数値は8bit=256での指定のため、明るさは0~255、色はCRGB型で表します。色についてはFastLEDライブラリでデフォルト定義の色やRGB(0~255,0~255,0~255)で指定することができます。
ただ、LEDの種類によってはBRGの順で光る物があるようで今回購入したものもそうでした。その場合色の読み替えなどが必要になるので注意してください。

今回はここまでです。
ゲーミングPCなどでもおなじみのレインボー発光などが自分で制御して作れるということがわかって楽しかったです。
オリジナルの発光パターンも今後増やしてみたいですね。
それではまた!

投稿者

Linea
本業はゲームプログラマー プログラミングやIT系の記事を書いていきます アイコンは愛車のシトロエン DS3です

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA