FlashLight Unit買ってみた

ただのLEDではなく、フラッシュまたは照明アプリケーションとして使えるユニット。あんまり、こんなユニット使ってみている人はいなさそう。用途は、なんかあったとき用に高輝度でチカチカさせるとか?

 とりあえず、照明用に明るいのがほしかっただけ。パーツでいろいろ揃えたり、百均に行ってみたりしたけど、これでいいかな。

M5Stack用フラッシュライトユニット — スイッチサイエンス
https://www.switch-science.com/products/8557

 白色LED (色温度5000~5700K)とAW3641ドライバチップを搭載したI/Oユニットです。ボードのモード選択スイッチにより、フラッシュモードとトーチ(常灯)モードを選択できます。通信インターフェースはGPIOです。照明やフラッシュを必要とする用途に使用できます。

 似たような名前のUNIT Lightというのもあるのだけど、それとはちょっと違う。

M5Stack用光センサユニット [U021] — スイッチサイエンス
https://www.switch-science.com/products/4051

 サンプルスケッチはこれ。

M5Unit-FlashLight/SetFlashTimeAndBrightness/SetFlashTimeAndBrightness.ino at main · m5stack/M5Unit-FlashLight
https://github.com/m5stack/M5Unit-FlashLight/blob/main/SetFlashTimeAndBrightness/SetFlashTimeAndBrightness.ino

#include <M5Stack.h>
#include <M5GFX.h>

#define FLASH_EN_PIN 26

M5GFX display;
M5Canvas canvas(&display);

uint8_t flash_brightness_time = 0;

void unit_flash_init(void) {
pinMode(FLASH_EN_PIN, OUTPUT);
digitalWrite(FLASH_EN_PIN, LOW);

 }

// 0: Flashlight off
// 1: 100% brightness + 220ms
// 2: 90% brightness + 220ms
// 3: 80% brightness + 220ms
// 4: 70% brightness + 220ms
// 5: 60% brightness + 220ms
// 6: 50% brightness + 220ms
// 7: 40% brightness + 220ms
// 8: 30% brightness + 220ms
// 9: 100% brightness + 1.3s
// 10: 90% brightness + 1.3s
// 11: 80% brightness + 1.3s
// 12: 70% brightness + 1.3s
// 13: 60% brightness + 1.3s
// 14: 50% brightness + 1.3s
// 15: 40% brightness + 1.3s
// 16: 30% brightness + 1.3s
void unit_flash_set_brightness(uint8_t brightness) {
if ((brightness >= 1) && (brightness <= 16)) {
for (int i = 0; i < brightness; i++) {
digitalWrite(FLASH_EN_PIN, LOW);
delayMicroseconds(4);
digitalWrite(FLASH_EN_PIN, HIGH);
delayMicroseconds(4);

 }

} else {
digitalWrite(FLASH_EN_PIN, LOW);

 }

 }

void setup() {
M5.begin(true, false, true, true);
display.begin();
canvas.setTextDatum(MC_DATUM);
canvas.setColorDepth(8);
canvas.setFont(&fonts::efontCN_14);
canvas.setTextSize(2);
canvas.createSprite(display.width(), display.height());

unit_flash_init();

 }

void loop() {
canvas.fillSprite(BLACK);
canvas.setTextSize(2);
canvas.setTextColor(ORANGE);
canvas.drawString("M5Unit Flashlight", 160, 10);
canvas.setTextColor(WHITE);
switch (flash_brightness_time) {
case 0:
canvas.drawString("Off", 150, 100);
break;
case 1:
canvas.drawString("100% brightness+220ms", 150, 100);
break;
case 2:
canvas.drawString("90% brightness+220ms", 150, 100);
break;
case 3:
canvas.drawString("80% brightness+220ms", 150, 100);
break;
case 4:
canvas.drawString("70% brightness+220ms", 150, 100);
break;
case 5:
canvas.drawString("60% brightness+220ms", 150, 100);
break;
case 6:
canvas.drawString("50% brightness+220ms", 150, 100);
break;
case 7:
canvas.drawString("40% brightness+220ms", 150, 100);
break;
case 8:
canvas.drawString("30% brightness+220ms", 150, 100);
break;
case 9:
canvas.drawString("100% brightness+1.3s", 150, 100);
break;
case 10:
canvas.drawString("90% brightness+1.3s", 150, 100);
break;
case 11:
canvas.drawString("80% brightness+1.3s", 150, 100);
break;
case 12:
canvas.drawString("70% brightness+1.3s", 150, 100);
break;
case 13:
canvas.drawString("60% brightness+1.3s", 150, 100);
break;
case 14:
canvas.drawString("50% brightness+1.3s", 150, 100);
break;
case 15:
canvas.drawString("40% brightness+1.3s", 150, 100);
break;
case 16:
canvas.drawString("30% brightness+1.3s", 150, 100);
break;

 default:

break;

 }

canvas.drawString("level", 70, 220);
canvas.drawString("off", 160, 220);
canvas.drawString("on", 250, 220);
canvas.pushSprite(0, 0);

M5.update();
if (M5.BtnA.wasPressed()) {
flash_brightness_time++;
if (flash_brightness_time > 16) {
flash_brightness_time = 1;

 }

} else if (M5.BtnB.wasPressed()) {
flash_brightness_time = 0;
unit_flash_set_brightness(0);
} else if (M5.BtnC.wasPressed()) {
unit_flash_set_brightness(flash_brightness_time);

 }

 }

 ボタン操作でフラッシュライトの輝度と点灯時間を制御するスケッチになっている。

 AW3641の仕様書。

 このユニットは、普通のLEDのようにPWMで点灯制御するのではなく、内部のAW3641に4μ x 4μのパルスを回数送ってモードを伝えるような仕組み。有効なのは0のオフと、1-16になる。17以上もオフと同じになる。

 それだけなら単純なんだけど、プリセットの挙動がパルスの回数の途中で変わっている。めんどくさいやつだ。

 1-8については、点灯時間は220ms固定で明るさが100%スタートで10%刻みで減ってゆく。

 9-16については、点灯時間は1.3s固定で、明るさは1-8と同じ10%刻みで減ってゆく。

 点灯時間は二種類で、出力は100-30%の刻みで指定できるということだ。

 ぜんぜん直観的じゃない設計。

 んで、これでは一瞬点灯するだけなので、連続点灯したい場合は、ガードタイムを置いてパルスを打ち続ければいい。のだけど、そんなめんどくさいことする?

 トーチモードではPWMでの制御になる。20–50kHzのPWMで連続調光になるそうだ。

 AW3641には連続点灯のためのトーチモードにする物理スイッチをつけることができるようだけど、このユニットに物理的なスイッチはない。どゆこと?

 どうやら、基板上に小型のSPDTスイッチ(S1)で「EN ↔ FLASH」どちらに外部制御線(黄色)を接続するかを切り替える設計のようで、外からは触れない。

 確かにスイッチあるね...

 トーチモードでスケッチを書く。ボタンで出力100%でLEDを光らせるだけ。ターゲットはATOMS3 LITEでM5Unifiedは使わない方針で。

#include <Arduino.h>

constexpr int PIN_SIG = 2;     // FlashLight SIG

 constexpr int PIN_BTN = 41; // 反応なければ 39 に

constexpr int PWM_FREQ = 5000; // 5kHz
constexpr int PWM_RES  = 10;   // 0..1023

bool torch = false;
bool stable = HIGH, lastRaw = HIGH;
uint32_t lastChange = 0;
constexpr uint32_t DEBOUNCE_MS = 50;

inline int dutyPct(int p){ p=constrain(p,0,100); return (p*((1<<PWM_RES)-1)+50)/100; }

void setup() {
pinMode(PIN_BTN, INPUT_PULLUP);

 analogWriteFrequency(PIN_SIG, PWM_FREQ); // ★ピン付きAPI

 analogWriteResolution(PIN_SIG, PWM_RES); // ★ピン付きAPI

analogWrite(PIN_SIG, 0);
stable = lastRaw = digitalRead(PIN_BTN);

 }

void loop() {

 bool raw = digitalRead(PIN_BTN); // 押下LOW

if (raw != lastRaw) lastChange = millis();
if (millis() - lastChange > DEBOUNCE_MS) {
if (raw != stable) {
stable = raw;
if (stable == LOW) {
torch = !torch;
analogWrite(PIN_SIG, torch ? dutyPct(100) : 0);

 }

 }

 }

lastRaw = raw;

 }

 うまく動作した。LEDは熱保護の140度くらいで自動消灯はついているけど、15秒くらいで自動消灯したほうがいいかも。LEDの寿命は5万時間だそうだけど。

#include <Arduino.h>
#if !defined(ESP32)

 #error "ESP32系ボードを選択してください"

#endif

 // ピン設定

 constexpr int PIN_SIG = 2; // HY2.0-4P 黄(G2) → Unit FlashLight SIG

 constexpr int PIN_BTN = 41; // 反応しなければ 39 に変更

 // PWM設定

constexpr int PWM_FREQ = 5000; // 5 kHz
constexpr int PWM_RES  = 10;   // 10-bit (0..1023)

 constexpr int BRIGHTNESS_PERCENT = 100; // 明るさ(%)。必要に応じて下げる

 // 自動消灯

 constexpr uint32_t TORCH_TIMEOUT_MS = 15000; // 15秒

 // デバウンス

constexpr uint32_t DEBOUNCE_MS = 50;
bool stable = HIGH, lastRaw = HIGH;
uint32_t lastChange = 0;

 // 状態

bool torch = false;
uint32_t torchOnSince = 0;

inline int dutyPct(int p) {
p = constrain(p, 0, 100);
const int maxDuty = (1 << PWM_RES) - 1;

 return (p * maxDuty + 50) / 100; // 四捨五入

 }

inline void setTorch(bool on) {
analogWrite(PIN_SIG, on ? dutyPct(BRIGHTNESS_PERCENT) : 0);
torch = on;
if (on) torchOnSince = millis();

 }

void setup() {
pinMode(PIN_BTN, INPUT_PULLUP);

 analogWriteFrequency(PIN_SIG, PWM_FREQ); // M5Stackコアはピン指定

 analogWriteResolution(PIN_SIG, PWM_RES); // 同上

analogWrite(PIN_SIG, 0);

stable = lastRaw = digitalRead(PIN_BTN);

 }

void loop() {

 // ボタン処理(立下りでトグル)

 bool raw = digitalRead(PIN_BTN); // 押下でLOW

if (raw != lastRaw) lastChange = millis();
if (millis() - lastChange > DEBOUNCE_MS) {
if (raw != stable) {
stable = raw;
if (stable == LOW) {
setTorch(!torch);

 }

 }

 }

lastRaw = raw;

 // 自動消灯

if (torch && (millis() - torchOnSince >= TORCH_TIMEOUT_MS)) {
setTorch(false);

 }

 }

 うまくいった。

 しかし、AIに書かせるのは結構面倒ね...

 100円投げ銭  https://kinneko.booth.pm/items/1963720


オリジナル投稿:
FlashLight Unit買ってみた|kinneko|pixivFANBOX
https://kinneko.fanbox.cc/posts/10232381