今回はスイッチサイエンスさんの「WS2812C搭載ネコユニット」を指定した箇所を中心にピカッと光らせるコードを作成したので紹介します。
👆実際にこのコードを使用してWS2812C搭載ネコユニットを光らせている様子。この制作物では音がした方向に対応した箇所を中心に光らせています。光ったあとは中心に向かって徐々に色が元に戻るのが特徴です。
紹介するコードについて
今回のプログラムは「WS2812C搭載ネコユニット」というネオピクセルが70個入ったパーツをピカッと光らせます。具体的には「今の色をあらかじめ設定した色に徐々に近づける」ことと「指定した箇所の中心を明るく、左右20LEDは徐々に暗くなるように色を設定」することを組み合わせて、ピカッと光らせます。
ネオピクセルはICチップ入りのLEDで光る色や明るさを1つずつ変えることができる商品です。これを使えば何十個、何百個のLEDを簡単に操作することができます。今回使用しているWS2812C搭載ネコユニットの他にもテープ状のものや輪っか状の物があるのでぜひ調べてみてください。
このコードの利点は実装が簡単な割に動作をリッチにできる点、光らせ方が急激に変わる場合に自然な表現になる点があります。今回のようにピカッと光らせたい場合やモードの切り替えで光り方が急激に変わる場合に役立ちます。
コードの紹介
こちらが今回紹介するコード全体になります。詳細な解説は解説ボタン💬をクリックすると表示できます。分かりにくいところがある場合などにご活用ください。
Adafruit_NeoPixel.hについて
ネオピクセルをマイコンで制御するのに必要なライブラリです。Arduino IDEのライブラリマネージャーから"Adafruit NeoPixel"を検索してインストールする必要があります。
- #include <Adafruit_NeoPixel.h>
使用する変数について
ここでは、デフォルトのカラーの指定、現在のLEDの色(3色それぞれの明るさ)を70個格納する変数、光らせる中心の位置の宣言をしています。
このLED_nowの値を変更・反映させてLEDの色を変えていきます。
- // LED制御用の変数
- int default_color[3] = {40,40,50};
- float LED_now[70][3];
- int location = 0;
PIN, オブジェクト作成について
ここではネオピクセルを接続するピンの番号、ネオピクセルのLEDの個数の定義とネオピクセルを制御するためのオブジェクト"pixels"の宣言をしています。
DELAYVALはループに使用するディレイを指定します。今回は10msごとに計算と更新を行います。
- // ネコミミPINとか
- #define PIN 2 // ネコ耳LEDに接続するピン
- #define NUMPIXELS 70 // ネコ耳モジュールは左右合わせて35 * 2 の 70LED
- Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
- #define DELAYVAL 10 // ループに使用するディレイ
setup()関数での処理について
setup()関数はArduinoプログラムの中で最初に1度だけ実行される関数です。ネオピクセルや配列の初期化、ランダムシードの設定をします。
- pixels.setBrightness(20)はネオピクセルの明るさを指定します
→ネオピクセルの明るさは256段階で設定できますが焼き切れる恐れがあるので20程度が推奨されています。 - pixel.begin()は初期化と通信を開始します。
- 配列の初期化ではLED_nowの配列すべてに250を入れています。
→起動時の色を変えることができます。今回は起動時に明るい白(250, 250, 250)から落ち着いた青(40, 40, 50)に変化させたいため、250で初期化してます。 - randomSeed()関数ではanalogRead(0)という起動するたびに変わる値を設定します。
random()とrandomSeed()について
Arduinoではrandam()関数によってランダムな値を生成することができます。ただし、そのままでは起動後全く同じ順番で同じ数字が生成されてしまいます。これはランダムな値を生成するアルゴリズムは基となる値「ランダムシード」を元に計算を行うからです。そのため、同じランダムシードでは同じ乱数が生成され、「時間」や「不安定な値」をランダムシードに設定すると予測できない乱数が生成されます。今回はanalogRead(0)という不安定な値を使用しています。
- void setup() {
- pixels.setBrightness(20);
- pixels.begin();
- // 配列初期化
- for (int i = 0; i < 70; i++) {
- for (int j = 0; j < 3; j++){
- LED_now[i][j] = 250;
- }
- }
- // ランダムシード
- randomSeed(analogRead(0));
- }
loop関数での処理について
loop()関数はArduinoプログラムの中でsetup()関数の次に実行され、その後繰り返し実行される関数です。光らせる位置の入手、delayAndFlashLED()関数でその位置を光らせる、ということを繰り返します。
- 30行目:random()関数によって70個あるLEDのうち光らせる中心のLEDの位置を計算します。
- 31行目:光らせる位置と待機させる時間をdelayAndFlashLED()関数に引き渡します。
random()関数について
random()関数は1つ、もしくは2のlong型の引数を元に生成したランダムな値をlong型で返す関数です。引数が1つの場合は 0から引数の値 - 1 のlong型の引数を返します。引数が2つの場合は 第一引数の値から第二引数の値 - 1 のlong型の引数を返します。long型はint型と同じく整数値を扱う変数で扱える大きさがint型よりも大きいという特徴があります。
- void loop(void) {
- location = (int)random(NUMPIXELS);
- delayAndFlashLED(300, location); // Adjust the delay as needed
- }
delayAndFlashLED()関数の解説
この関数では35行目〜43行目で「2つ目の引数で指定したLEDを中心に左右20ピクセル色をつける」、44行目〜54行目で「1つ目の引数で指定した時間の間、色をデフォルトの色に徐々に変化させる」という2つのことを行います。まずは35行目〜43行目の詳細な説明をします。
- 35行目:random()関数を使い0もしくは1の値をcolor_selectに格納します。
- 36行目〜43行目:for文により左右20ピクセル(正確には中心1ピクセルと左右19ピクセル)の色を変更しています。色の反映はこの後pixelオブジェクトのsetPixelColor()関数とshow()関数によって行います。
for文の中身を見ると center + i と center - i と中央からそれぞれ左右に遠ざかるように色の指定をしているのがわかると思います。また、LED_nowの配列の外を指定しないようにif文によって左端と右端を越えていないか調べてから更新しています。
次に44行目〜54行目の詳細な説明をします。
- 44行目:for文によって cnt を0から第一引数のdelay_timeになるまでDELAYVALずつ増加させます。delay_timeが300でDELAYVALが10ならば30回ループすることになります。
- 46行目〜51行目:LED_now配列の値の変更、変更した色をネオピクセルに設定します。
- 52行目:50行目で設定した色を反映させます。反映にはpixels.setPixelColor()メソッド、その第二引数にpixels.Color()メソッドを使用しています。
- 53行目:DELAYVALミリ秒待機させます。44行目のループの間で合計delay_timeミリ秒待機することになります。
今回使用した、ネオピクセルのメソッドをまとめると次のようになります。
今回は、①②をsetup()関数内で、③④⑤をdelayAndFlashLED()関数内で使用しています。
- void delayAndFlashLED(int delay_time, int center) {
- int color_select = (int)random(2);
- for (int i = 0; i < 20; i++){
- if (center + i < NUMPIXELS) { // 右側20個の色を指定
- LED_now[center + i][color_select] = 250 - i * 8;
- }
- if (center - i >= 0) { // 左側20個の色を指定
- LED_now[center - i][color_select] = 250 - i * 8;
- }
- }
- for (int cnt = 0; cnt < delay_time; cnt += DELAYVAL){
- // LED動作
- for (int i = 0; i < NUMPIXELS; i++){
- for (int j = 0; j < 3; j++){
- LED_now[i][j] = LED_now[i][j] + ((float)default_color[j] - LED_now[i][j]) / 20 ;
- }
- pixels.setPixelColor(i, pixels.Color((int)LED_now[i][0], (int)LED_now[i][1], (int)LED_now[i][2]));
- }
- pixels.show();
- delay(DELAYVAL);
- }
- }
まとめ
今回は「WS2812C搭載ネコユニット」をピカッと光らせるコードを紹介しました。「今の色をあらかじめ設定した色に徐々に近づける機能」と「指定した箇所の中心を明るく、左右20LEDは徐々に暗くなるように色を設定する機能」に分けて考えてもらえるとより活用の幅が広がると思います。ぜひ、ご活用ください。
謝辞
このコードはTechSeeker Hackathon 2024の成果物の一部です。このような学習の機会を用意してくださった方々、同じチームとして支えてくださったデジもく会のみなさんに感謝します。本当にありがとうございました。