背景
昨今は、在宅勤務を余儀なくされている方々が多いと思います。
嫁さんが「ウェブ会議で映る自分の顔が暗い」と言ってたので
たまたま家にあったLEDリングライトとArduino互換ボードで
小型照明を作ってみました。
ハードウエアなど詳細については、別のブログ記事に書いています。
こちらでは、電気回路とコード内容について書きました。
部品構成
アイテム | 数量 | 備考 |
---|---|---|
Arduino互換ボード | 1個 | わずか80円!謎のArduino互換機を動かしてみた |
LEDリングライト24灯 | 1個 | |
ジョイスティック | 1個 | |
タクトスイッチ | 3個 | お好きなだけ |
LEDカバー | 1個 | 3Dプリンタ品 |
SAMさん情報で買っていた謎の激安ボードを採用しました。
使い方はコチラ、ともさんのQiita記事
LGT8f328P Arduino互換ボードを使ってみる
を参考にしました。この情報が無ければ使えなかったでしょう、感謝!
電気回路図
次のブレッドボード図を参考お願いします。
図は本家のArduino MINIで描いていますので、読み替えお願いします。
MINIと同様にUSBシリアル変換を使って、ArduinoIDEでスケッチ書き込みできました。
スケッチ
Arduino IDE 1.8.11 を使用しました。
以下、コードを少し区切って説明します。
順番に全てを繋げれば動くはずですが、必要に応じて適時修正お願いします。
#include <FastLED.h>
#define NUM_LEDS 24
#define DATA_PIN 8 // Neopixel data pin
CRGB leds[NUM_LEDS];
const int xAxis = A0; // joystick X axis
const int yAxis = A1; // joystick Y axis
const int posButton = 13; // input pin for position mode
const int clrButton = 12; // input pin for color mode
const int briButton = 11; // input pin for bright mode
int threshold = 1000000; // resting threshold
int modeState = 0; // effective mode state
int modeStated = 0; // previous mode state
int resetModes = 3; // countdown to reset
int resetMode = resetModes;
int freetimes = 100; // countdown to mode cancel
int freetime = freetimes;
int posLed = 0; // LED ON position
int posLeds[24] = {};
int hueLed = 64; // LED color type, yellow
int satLed = 128; // 255 = pure color, 0 = plain white
int valLed = 128; // LED brightness
void setup() {
Serial.begin(9600);
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
}
冒頭は、I/Oピン定義、各変数の定義です。
全ての変数の意味は、すみませんが説明省略します。
setupにて、neopixelのライブラリ定義。以降は、並んでいるLEDの番号ごとに
色や明るさを指定してから FastLED.show(); の命令で点灯。とても楽ちん。
void loop() {
pinMode(posButton, INPUT_PULLUP);
pinMode(clrButton, INPUT_PULLUP);
pinMode(briButton, INPUT_PULLUP);
long xReading = (long)readAxis(xAxis) * -1;
long yReading = (long)readAxis(yAxis);
long rReading = (xReading * xReading) + (yReading * yReading);
long degReading = _atan2(yReading , xReading);
// mode select
int posMode = digitalRead(posButton);
int clrMode = digitalRead(clrButton);
int briMode = digitalRead(briButton);
if (posMode + clrMode + briMode == 0) modeState = 1;
else if (posMode + clrMode + briMode == 2 and posMode == 0){
modeState = 2;
for(int j = 2; j < 8; j += 2){
for(int i = 0; i < NUM_LEDS; i += 1) { // mode effect & reset position
if(i % j == 0) leds[i] = CHSV(0, 0, 128);
else leds[i] = CHSV(0, 0, 0);
posLeds[i] = 0;
}
FastLED.show();
delay(200);
}
if(modeState == modeStated) resetMode -= 1;
}else if (posMode + clrMode + briMode == 2 and clrMode == 0){
modeState = 3;
for(int j = 0; j < 256; j += 32){
for(int i = 0; i < NUM_LEDS; i += 1) { // mode effect
if(i > 15 and i < 21) leds[i] = CHSV(0, 0, 0);
else leds[i] = CHSV(j, 128, 128);
}
FastLED.show();
delay(100);
}
satLed = 128; // reset saturation
if(modeState == modeStated) resetMode -= 1;
}else if (posMode + clrMode + briMode == 2 and briMode == 0){
modeState = 4;
for(int j = 0; j < 256; j += 32){
for(int i = 0; i < NUM_LEDS; i += 1) { // mode effect
leds[i] = CHSV(0, 0, j);
}
FastLED.show();
delay(100);
}
if(modeState == modeStated) resetMode -= 1;
}else{
if(resetMode < 0){
switch (modeStated){
case 2: //long press posB = all turn on
for(int i = 0; i < NUM_LEDS; i += 1) {
posLeds[i] = 1;
}
break;
case 3: //long press clrB = plain white
satLed = 0;
break;
case 4: //long press briB = half lit
valLed = 128;
break;
}
}
resetMode = resetModes;
// long time hands-free = mode cancel
if(rReading < threshold) freetime -= 1;
if(freetime < 0) modeState = 0;
}
if(modeState != modeStated) freetime = freetimes;
ブレッドボード図の各種ボタンを押してモード切り替えします。
私は3ボタンにしたので、モードは3パターン。さらに長押しにも意味を持たせています。
ボタン | モード | 長押し |
---|---|---|
右 | 光る場所を指定 | 全周点灯 |
中央 | 色を設定 | 無色(白)に設定 |
左 | 明るさと彩度を変更 | 彩度を50%に設定 |
ボタン押している間は、役割に応じてアニメーション点灯する事で
何のモードに入ったか分かるようにしています。
例えば中央「色を設定」ならば、リングライトが「C」の文字に光ります。
// reset & all black
switch (modeState){
case 1:
for(int i = 0; i < NUM_LEDS; i += 1){
leds[i] = CHSV(0, 0, 0);
}
modeStated = 1;
delay(1000);
break;
// change position mode
case 2:
if(rReading > threshold){
posLed = map(degReading, 0, 36000, 0, 24);
if(posLeds[posLed] == 0) posLeds[posLed] = 1;
}
modeStated = 2;
break;
// change color mode
case 3:
if(rReading > threshold){
hueLed = map(degReading, 0, 36000, 0, 256);
}
modeStated = 3;
break;
// change bright mode
case 4:
if (xReading > threshold/100) satLed -= 4;
else if(xReading < -threshold/100) satLed += 4;
if (yReading > threshold/100) valLed -= 4;
else if(yReading < -threshold/100) valLed += 4;
if(satLed > 255) satLed = 255;
else if(satLed < 0) satLed = 0;
if(valLed > 255) valLed = 255;
else if(valLed < 0) valLed = 0;
modeStated = 4;
break;
}
// LED set & turn on
for(int i = 0; i < NUM_LEDS; i += 1) {
if(posLeds[i] == 1) leds[i] = CHSV(hueLed, satLed, valLed);
else leds[i] = CHSV(0, 0, 0);
}
FastLED.show();
delay(100);
}
各種モードに入った後、ジョイスティックを動かした時の挙動を定義しています。
LEDの色や明るさを変数で設定してから、その通りに点灯させる、を繰り返してます。
// read the analog input
int readAxis(int thisAxis) {
int reading = analogRead(thisAxis);
int distance = reading - 2000; //adjusted center
return distance;
}
// convert xy => degree(0 to 36000)
long _atan2(long _y, long _x) {
long x = abs(_x);
long y = abs(_y);
float z;
bool c;
c = y < x;
if (c)z = (float)y / x;
else z = (float)x / y;
long a;
//a = z * (-1556 * z + 6072); //2次曲線近似
//a = z * (z * (-448 * z - 954) + 5894); //3次曲線近似
a = z * (z * (z * (829 * z - 2011) - 58) + 5741); //4次曲線近似
if (c) {
if (_x > 0) {
if (_y < 0)a = 36000 - a;
}
if (_x < 0) {
if (_y > 0)a = 18000 - a;
if (_y < 0)a = 18000 + a;
}
}
if (!c) {
if (_x > 0) {
if (_y > 0) a = 9000 - a;
if (_y < 0) a = 27000 + a;
}
if (_x < 0) {
if (_y > 0) a = a + 9000;
if (_y < 0) a = 27000 - a;
}
}
return a;
}
ジョイスティックのXY軸入力から、ベクトル角度に換算します。
次の記事を参考させていただきました。
近似式でatan2の処理を高速化してみる
まとめ
今回も先人の方々の知恵や情報がなければ完成できませんでした。感謝感謝です。
この記事が少しでも参考になれば幸いです。