Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

M5Atom と FastLED で 円形 LED 時計を作る

1. はじめに

 LED を円形に並べた置時計が数多くあります。MakerFaire Tokyo 2012 において、Silrium さんの作品1でその存在を知りました。AliExpress, Banggood, eBay には安価な電子工作キット234があります。RGB LED 使用の製品5もあります。Flyandace さんの作品6には独特の雰囲気があります。反省会駆動さんの作品7には薄型のこだわりがあります。tadashi101 さんの作品8は、360 個の LED のはんだ付けが必要で話題になりました。LED を 3 重の円に並べた作品910もあります。私もオリジナルを作ってみたいと思いました。

2. ハードウェア

 プリント基板 (PCB) を作成しました。回路図等を GitHub に置きました11。中国の PCB 製造業者の多くが格安サービスを設定していて、その仕様が FR-4(ガラスエポキシ)、2層、10cm × 10cm 以内、5~10 枚です。このサイズに収めます。

RGB LED

 10cm × 10cm のプリント基板上に LED を円形に 60 個並べます。5050 サイズ (5mm × 5mm) では収まらず、2020 サイズ (2mm × 2.2mm) が必要です。WorldSemi 社の WS2812C-202012を使います。WS2812B-202013は、置時計用には電流値(明るさ)が大きすぎ、発熱も心配です。PWM で抑えると明暗の余地が減ります。WS2812C-2020 は、バイパスコンデンサが必要ですが、発熱対策や表現力で有利です。

WS2812B-2020 WS2812C-2020
バイパスコンデンサ 不要
チャネルあたりの電流 16mA 5mA
1個あたりの消費電力 0.24W 0.075W

NeoPixel

 NeoPixel14は Adafruit 社の LED 製品のブランドです。「デジタル RGB LED」または「スマート LED」と呼ばれる WorldSemi 社15の WS2811, WS2812 や Shenzhen LED Color 社16の SK6812 が使われています。"1/4 60 Ring"17は掛時計の製作にぴったりと思われます。

M5Atom

 M5Stack 社の M5Atom18は、小型で安価な IoT 開発用デバイスです。素材は ESP32 です。単体で WiFi 接続が可能で、時刻合わせに NTP を使えます。Arduino-IDE などの開発環境があります。M5Atom Lite19は NeoPixel 仕様の LED を 1 個、M5Atom Matrix20は 25 個搭載しています。M5Atom.h が FastLED.h をインクルードしているため、Arduino IDE のライブラリ・マネージャで予め FastLED をインストールしておく必要があります。

プリント基板

 LED を円形に 60 列× 3 行、中央に 16 列× 6 行、計 276 個を並べることができました。おもて面に SMT 部品を配置しリフローを片面で済ませます。うら面に M5Atom やコネクタなどの背の高い部品を配置し、スモーク板などの取り付けを邪魔しない様にします。
 PCB CAD には KiCad21を使用しました。フットプリントの円形配置には、Kicad Action Plugins - Place footprints22を利用しました。四隅をマウスバイト23で折り取れる様にはしましたが、部品が接近しているので慎重な作業が必要です。
 おもて面にシルクのスペースがなく、苦肉の策でうら面に移動しました。部品の特定の際は、おもて面に残せたシルクを手がかりにうら面のシルクで確認します。
 M5Atom の周辺にあるグラウンドのベタパターンが影響し、置き方によっては WiFi が繋がり難いかもしれません。プリント基板を外した状態では繋がり易くなる場合、プリント基板の影響と特定できます。

bf-025_front.png
bf-025_back.png

電源

 WS2812C-2020 の電源は 5V です。DC ジャックから供給します。M5Atom のピンソケットへも逆流防止のショットキーバリア・ダイオード (SBD) を経由して供給します。M5Atom は、USB-C か、またはピンソケットからの 5V を受けて動作します。
 WS2812C-2020 は、1 素子あたり (R, G, B 各々) 5mA ですので、仕様上の最大消費電流・電力は以下です。

5mA \times 3/LED \times 276 LED/PCB = 4.14A\\
5V \times 4.14A = 20.7W

 FR-4 基板で放熱も考慮しない状況で 20W は危険です。明るさを絞って運用します。実験中の安全のため、5V 電源(AC アダプタ)側で最大でも 3A 程度24に抑えるのが得策です。

3. FastLED

 FastLED25は、RGB LED を扱うためのライブラリです。特に NeoPixel への対応が充実しています。Arduino IDE で利用できます。Adafruit が提供している NeoPixel ライブラリ26とは別のものです。

I2S

 ESP32 の I2S (Inter IC Sound) 機能を利用して FastLED の処理を効率化できます27FastLED.h (M5Atom.h) をインクルードする前に I2S を有効化する指定をします。
 NeoPixel 仕様では、LED を直列に接続します。LED へのデータをシリアルにして 800kHz の規定のタイミングで途切れることなく送り出す必要があります。276 個 1 列 の LED へのデータ送出には 8.3ms かかります。

\frac {8bit \times 3color \times 276LED}{800kHz} = 8.28ms

 I2S を指定しない場合、FastLED は ESP32 の RMT (Remote Control) 機能を使用します。RMT ではデータ転送をプログラムで行うのに対し、I2S では DMA (Direct Memory Access) を利用します。ソフトウェアの負荷が軽減され、WiFi など他の処理との共存に有利です。I2S を使用する他のソフトウェアには制限が生じます。

ポート

 今回の外付け円形 LED では、M5Atom の GPIO を 1 ポート使用します。
 FastLED は、2 チャネルある I2S のうち 1 チャネルを使用します。I2S の LCD モードを使用して 24 ポート並列にデータ送信ができます。各ポートに接続する LED は同一仕様(タイミング・数)で、全 LED を一括で制御する必要があります。
 クラス CFastLED のインスタンスとして FastLED が予め宣言されています。これとは別のインスタンスを FastLED2 などと宣言しても同様に動作します。しかし、I2S が FastLED と FastLED2 とで干渉するため同時には使用できません。
 M5Atom の内蔵 LED は、今回の外付け円形と統合的に扱うことは可能と考えられます。しかしながら M5Atom ライブラリは、内蔵 LED 用に FastLED を使います。M5Atom ライブラリによる内蔵 LED (display) との共存はあきらめ、M5.begin( ) で無効にしています。
 ちなみに、RMT は 8 チャネルあります。FastLED では順番待ち処理により 16 ポートまで拡張できます。

GPIO

 LED への信号は M5Atom のピンソケットから取り出します。WS2812C-2020 のデータ入力は $V_{IL}<0.7V$、$V_{IH}>2.7V$ です。プリント基板内で配線も短く、3.3V レベルの M5ATom の GPIO を直結できます。
 GPIO の選択にあたり、M5Atom 本体と拡張ユニット (Atom Base) のいくつかについて、ピンソケットの GPIO の使用状況を調べました。

pin socket M5Atom Lite/Matrix M5Atom Echo QR-CODE GPS TF-CARD RS232 RS485 Switch/-D
GPIO19 BCLK MOSI MOSI TX Relay2
GPIO21 SCL (Matrix) SCL (3v3 pull-up)
GPIO22 DataOut TX RX Relay1
GPIO23 GPIO34 (ADC1_CH6) DataIn TRIG CLK CLK TX
GPIO25 SDA (Matrix) SDA (3v3 pull-up)
GPIO33 ADC1-CH5 LRCK DLED MISO MISO RX

以下は候補から外し、将来の拡張用に Grove コネクタに取り出します。

  • GPIO21, GPIO25: I2C (SDA/SCL) に使用。将来の拡張は、RTC, 環境センサなど
  • GPIO23(GPIO34), GPIO33: アナログ入力機能あり。将来の拡張は、環境センサ、十字ボタン、アラームなど

 GPIO22 は、様々な用途に使用されています。LED への信号出力は GPIO22 に決めました。M5Atom Echo の GPIO22 は I2S (DataOut) に使われています。M5Atom Echo は使用できません。GPIO19 は GPIO23, GPIO33 と共に SPI (MOSI, CLK, MISO) に使用されています。GPIO19 は未接続のまま残します。

初期設定

 ハードウェア構成を反映した初期設定のコーディング例です。LED データは CRGB 型の配列 leds[ ] に保持します。FastLED.addLeds( ) で LED データや GPIO を指定します。WS2812C-2020 の RGB の順序 GRB も併せて指定します。FastLED.setBrightness( ) で、消費電力の抑制も考慮して全体的な明るさを設定します。

BF-025.cpp
#define FASTLED_ESP32_I2S true
#include <M5Atom.h>

// for M5Atom
const bool serial_enable   = true;
const bool i2c_enable      = true;
const bool display_disable = false;

// for external LEDs
const int pin_to_leds =  22;  // connect to GPIO22
const int num_of_leds = 276; 
CRGB leds[num_of_leds];
const int brightness  =  64;  // 64 of 255

void setup()
{
  M5.begin(serial_enable, i2c_enable, display_disable);
  FastLED.addLeds<WS2812, pin_to_leds, GRB>(leds, num_of_leds);
  FastLED.setBrightness(brightness);
}

4. 座標

 LED を 1 列に接続します。円形部分では 1 周したら次の列に接続します。長方形部分では、まず左上から下に繋ぎ、次の列は下から上に繋ぎます。これを 8 回繰り返します。この蛇行接続 (serpentine, zigzag) は、市販製品28でも採用されています。

bf-025_led_connection.png

 LED データを保持する 1 次元配列 leds[ ] に対し、円形部分 leds1, 矩形部分 leds2 を仮想的に構成し、各々について (x, y) 座標に色を書き込む関数を用意します。色として foreground_color を用意して予め設定しておきます。

BF_RGB_LED_276.cpp
// circle: leds1[0..60][0..2] --> leds[0..179]
const int leds1_num_of_x = 60;
const int leds1_num_of_y =  3;

// rectangle: leds2[0..15][0..5] --> leds[180..275]
const int leds2_num_of_x = 16;
const int leds2_num_of_y =  6;
const int head_of_leds2 = leds1_num_of_x * leds1_num_of_y;

// for fastLED
const int num_of_leds = leds1_num_of_x * leds1_num_of_y + leds2_num_of_x * leds2_num_of_y; 
CRGB leds[num_of_leds];
CRGB foreground_color = CRGB::SlateGray;

// put a dot on leds1[x][y]
void PutDotLeds1(int x, int y)
{
  if (x >= 0 && x < leds1_num_of_x && y >= 0 && y < leds1_num_of_y)
    leds[x + leds1_num_of_x * y] = foreground_color;
}

// put a dot on leds2[x][y]
void PutDotLeds2(int x, int y)
{
  if (x >= 0 && x < leds2_num_of_x && y >= 0 && y < leds2_num_of_y)
    if (x % 2 == 0)
      leds[head_of_leds2 + leds2_num_of_y * x + y] = foreground_color;
    else
      leds[head_of_leds2 + leds2_num_of_y * x + leds2_num_of_y - 1 - y] = foreground_color;
}

5.色

 色の指定方法は、FastLED の Wiki29 に詳しいです。全体像を分かる範囲で図にしました。中心にあるのが CRGB です。CRGB データは直接指定できます。LED に送り出す際に補正する処理があります。さらに、いくつかの方法で表現された色を CRGB に変換する処理30があります。

bf-025_color_process.jpg

CRGB

 RGB データを詰め込んだ構造体です。Red, Green, Blue を各々 8 bit 符号なし整数 0 ~ 255 で表します。数値表現の他に予め定義された名前(約 150 色)で指定できます。

BF_RGB_LED276.cpp
int red   = 255;  // 0..255
int green = 255;  // 0..255
int blue  = 255;  // 0..255
CRGB color1 = CRGB(red, green, blue);
CRGB color2 = CRGB::DarkOliveGreen;  // pre-defined color

CHSV

 色を色相 (Hue)、彩度 (Saturation/Chroma)、明度 (Value/Brightness) で指定できます。本来の色相 0 ~ 360、彩度 0 ~ 100、明度 0 ~ 100 に対し、すべて 8 bit 符号なし整数 0 ~ 255 で指定します。色相には予め定義された名前(8 色)があります。色相から RGB への変換には、FastLED 独自の Rainbow Hue Chart30が使用されます。本来の Spectrum Hue Chart よりも黄色の幅が広くなっています。

BF_RGB_LED276.cpp
int hue        =   0;  // 0..255 for 0..<360
int satulation = 255;  // 0..255 for 0..100
int value      = 255;  // 0..255 for 0..100
CRGB color1 = CHSV(hue, saturation, value);
CRGB color2 = CHSV(HUE_RED, saturation, value);  // pre-defined hue

カラーパレット

 色調を一括で切り替えるカラーパレット機能があります。カラーパレットに 0-255 の値 (color index) を入れると対応する色が得られます。予め 8 種のカラーパレット31が用意されています。色を例えば 16 色並べて独自のカラーパレットを定義できます。256 の色の生成には、補間なし (NOBLEND)、補間あり (LINEARBLEND) を指定できます。カラーパレットに RainbowColors_p を選択すると、color index は色相 hue に相当します。

Palette 色調 備考
CloudColors_p
LavaColors_p 溶岩
OceanColors_p
ForestColors_p
RainbowColors_p
RainbowStripeColors_p 色の境界に黒帯、RainbowStripesColors_p も同じ
PartyColors_p パーティ 陽気な雰囲気にする
HeatColors_p 黒体放射 0~240
BF_DemoHueRing.cpp
CRGBPalette16 palette_color = RainbowColors_p;
// hue ring 
for (int i = 0; i < 60; ++i) {
  int hue = 256.0 * i / 60.0 + 0.5;
  // by CHSV
  leds[i] = CHSV(hue, 255, 255);
  // through the color palette of RainbowColors_p 
  leds[i] = ColorFromPalette(palette_color, hue, 255);
}

色温度

 色温度 (Temperature)32の補正ができます。黒体放射(9 種)、ガス封入管のスペクトル(10 種)、および「補正なし」が用意されています。

BF_RGB_LED_276.cpp
FastLED.setTemperature(Tungsten40W);

色補正

 LED の R, G, B 間の差を合わせ込む色補正 (Color Correction)33 ができます。4種(実質 2 種)の補正値と「補正なし」が用意されています。

名称 補正値 備考
TypicalSMD5050 0xFFB0F0 5mm ピクセル
TypicalLEDStrip 0xFFB0F0 一般的な LED テープ
Typical8mmPixel 0xFFE08C 8mm ピクセル
TypicalPixelString 0xFFE08C スルーホールパッケージ
UncorrectedColor 0xFFFFFF 補正なし

 WS2812C-2020 用はありません。実際に色相環を表示すると、青が非常に弱く、緑が強すぎます。赤、緑、青が同じ幅になる様な補正値を独自に定義しました。明るさ補正 FastLED.setBrightness( ) の影響もあるかもしれません。

BF_RGB_LED_276.cpp
const int brightness_bf025  = 64;        // original for BF-025(WS2812C-2020)
const CRGB correction_bf025 = 0xB080FF;  // original for BF-025(WS2812C-2020)

FastLED.setBrightness(brightness_bf025);
FastLED.setCorrection(correction_bf025); 

ディザリング

 明るさ補正 FastLED.setBrightness( ) により、各色の明るさを均等に圧縮します。このときビット落ちにより細かな違いが消えてしまいます。ディザリング (dithering)34は、明るさの増減を短時間に繰り返して中間的な明るさを実現します。通常オンで、指定でオフにできます。
 ディザリングを機能させるためには、LED へのデータ書き込み FastLED.show( ) を頻繁に呼び出す必要があります。待機時間中ディザリングを機能させるために FastLED.delay( ) が用意されています。FastLED.delay( ) は、FastLED.show( ) を定期的に呼び出します。
 今回の置時計では FastLED.show( ) を 20ms 周期 (50fps) で呼び出しています。delay( ) の代わりに FastLED.delay( ) とすると激しくちらつきます。FastLED.show( ) をやめて FastLED.delay( ) に任せると細かなちらつきになります。FastLED.show( ) と delay( ) の場合でも 10ms 周期 (100fps) とすると細かなちらつきが出ます。ディザリングをオフにするとちらつきが見えなくなります。今回は、通常の delay( ) としています。ちらつきが気になる場合ディザリングをオフにします。

ディザリング 処理 20ms (50fps) 10ms (100fps)
あり FastLED.show( ); FastLED.delay( ); 激しいちらつき
あり FastLED.delay( ); 細かなちらつき
あり FastLED.show( ); delay( ); ちらつき見えず 細かなちらつき
なし FastLED.show( ); delay( ); ちらつき見えず
BF_RGB_LED_276.cpp
//  FastLED.setDither(DISABLE_DITHER);  // uncomment to prevent flicker

パーリンノイズ

 FastLED には、スケーリング、三角関数、乱数などを整数で高速に行う数学ライブラリがあります35。自然なテクスチャを擬似的に生成するパーリンノイズ (Perlin Noise)36も用意されています。
 8bit 版である inoise8( ) の引数 (16bit) は、上位 8bit が整数部、下位 8bit が小数部です。上位が周期単位の、下位が周期内の変化を決める模様です。3 次元の inoise8(x, y, z) において、x, y を平面座標に、z を時間経過に使用すると、テクスチャが変化する様子を見ることができます。以下のデモでは、平面の短辺 y のサイズを周期スケールとし、時間経過の 1s を周期としてパーリンノイズを色相 Hue としています。ノイズの特性上、0, 255 付近の色(赤)はほとんど現れませんでした。

BF_DemoNoise.cpp
const int loop_ms = 20;  // 20ms
int last_ms = 0;

void DemoNoise()
{
  while (true) {
    for(int y = 0; y < leds2_num_of_y; ++y)
      for(int x = 0; x < leds2_num_of_x; ++x) {
        foreground_color = CHSV(inoise8(ScaleXY(x), ScaleXY(y), ScaleZ()));
        PutDotLeds2(x, y);
      }
    FastLED.show();

    delay(loop_ms - (millis() - last_ms));
    last_ms = millis();
  }
}

int ScaleXY(int xy)
{
  return xy * 256 / leds2_num_of_y;  // scale(integer part) = num_of_y
}

int ScaleZ()
{
  return last_ms * 256 / 1000;  // period(integer part) = 1s
}

6. 例

 FastLED に添付のプログラミング例 (examples) のいくつかを、今回用に修正しました37。動作の様子が YouTube3839404142にあります。修正点は主に以下です。

  • I2S 利用を宣言する
  • ピン番号を 22 にする
  • LED 数を 276 にする
  • 色補正を 0xB080FF にする
example.cpp
#define FASTLED_ESP32_I2S true
#define LED_PIN   22
#define NUM_LEDS 276
const CRGB correction_default = 0xB080FF;
FastLED.setCorrection(correction_default);

7. 置時計

 置時計のデモを作成しました43。動作の様子が YouTube44にあります。以下の機能があります。

  • 時・分・秒・曜日・年・月・日の表示(スクロール)
  • 時・分・秒のアナログ風表示(彗星の尾)
  • 文字盤背景色の変化
  • デモ: 時刻表示中のデモ (DemoClock)
  • デモ: ランプテスト表示 (DemoCyron)
  • デモ: 色相環表示 (DemoHueRing)
  • デモ: パーリンノイズ (DemoNoise)

 時刻表示中のデモには以下があります。

  • 文字色の切替(シアン・赤・緑・青)
  • フォント表示(記号・数字、英大文字、英小文字)
  • テキスト表示(英大文字、英小文字混在)
  • エイリアン

 動作中 M5Atom のボタンで以下を操作します。切替の状況はシリアルモニタで確認できます。

長押し時間 表示 効果
- Pale カラーパレットの切替
>1 秒 Next デモの切替、【クロックデモ】デモ内容の切替(文字色、フォント)
>2 秒 Exit デモの切替
>3 秒 Temp 色温度の切替
>4 秒 Corr 色補正の切替
>5 秒 Scal 【パーリンノイズデモ】スケール (x, y) の切替
>6 秒 Peri 【パーリンノイズデモ】周期 (z) の切替
>7 秒 Null ボタン操作の取消

 実用的には、以下の様な制限があります。

  • 時刻合わせに WiFi接続、インターネット接続 (NTP) が必須
  • 停電時の時刻保持 (RTC、バッテリバックアップ) 機能がない
  • サウンド機能がない
  • アラーム機能(アラーム時刻設定)がない

 また、以下のような機能強化や追加が考えられます。

  • 12 時間表示、ゼロサプレス
  • タイマー、ストップウォッチ、カウント機能
  • 振り子、「クイズタイムショック」、星の瞬きなどのギミック
  • 毎正時のからくり(演奏・演出)
  • 温度・湿度・外気温・天気・電力など、センサー情報・外部情報の取得と表示
  • 外部からのメッセージの指定と表示

デモデータ

 デモのデータを Excel で作成45しました。フォントやパターンは、Excel から Arduino IDE のコードに
コピー・ペーストしました。

  • フォント作成 (Font Editor)
  • パターン作成 (Pattern Editor)
  • 彗星の尾のパラメータ決定 (tail of comet)
  • 色の選択 (CRGB color)

8. おわりに

 実用には、まだ完成度が低いですが、数多くのことを学ぶことができました。RGB と HSV の関係、カラーパレットの考え方、パーリンノイズなどの基本的な内容も初めて知りました。さらに FastLED ライブラリの使い方や可能性、M5Atom ライブラリのボタンの使い方まで、今後に生かしていきたいと思います。


  1. Silrium「Luminous LED Clock 電子工作キット」 

  2. DS1302 Rotating LED Display Alarm Electronic Clock Module DIY Kit Light Control LED Temperature Display For Arduino DS1302 

  3. Geekcreit® Fourth Generation DIY EC1838B DS1302 Light Control Rotation LED Electronic Clock Kit Music Alarm Clock With Housing - Blue Digital Tube + Blue & White LED 

  4. DIY Multifunction Digital LED Electronic Temperature Clock Kits Alarm Time Suite 

  5. 电子时钟diy套件 光控数码管数字显示模块 单片机led时钟元件组装 

  6. Flyandace xo "Circular 80 LED clock version 3.1 DS1302 74hc595 atmega8 Juno AVR HD" 

  7. 反省会駆動 CIRCLE CLOCK 

  8. tadashi101 teen_circle 

  9. Arduino LED clock 

  10. BUILD AN ANALOG-STYLE LED CLOCK — PART 1 

  11. botanicfields/PCB-RGB-LED-276-M5Atom 

  12. WS2812C-2020 

  13. WS2812B-2020 

  14. Adafruit NEOPIXELS 

  15. WorldSemi Co., Limited 

  16. Shenzhen LED Color Co.,LTD 

  17. Adafruit NeoPixel 1/4 60 Ring - 5050 RGBW LED w/ Integrated Drivers - Natural White - ~4500K(色温度違いあり) 

  18. M5Atom 

  19. M5Atom Lite 

  20. M5Atom Matrix 

  21. Kicad 

  22. Kicad_action_plugins 

  23. KiCad においてミシン目を使って面付けをする 

  24. 秋月電子通商 スイッチングACアダプター5V3A AD-D50P300 

  25. FastLED 

  26. Adafruit NeoPixel Library 

  27. New 24-way parallel driver for ESP32 

  28. Flexible 8x32 NeoPixel RGB LED Matrix 

  29. FastLED wiki 

  30. Pixel reference 

  31. colorpalettes.h 

  32. color.h 

  33. FastLED Color Correction 

  34. FastLED Temporal Dithering 

  35. High-performance-math 

  36. noise.h 

  37. [examples]https://github.com/botanicfields/PCB-RGB-LED-276-M5Atom/tree/main/examples

  38. ColorPalette 

  39. DemoReel100 

  40. Pacifica 

  41. Pride2015 

  42. TwinkleFox 

  43. BF-025 

  44. Clock Demo on BF-025: RGB-LED 276 for M5Atom 

  45. BF-025_demo.xlsx 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away