LEDテープで大ディスプレイを! (LEDテープその3)
お馴染みピクトグラムをArduinoが次々に描きます。このディスプレイは安価なLED配列フィルムですが、Arduinoから1ワイヤで高速に描画できます。
これまでに書いたLEDテープ記事の続編ですが、前の2つの記事のリンクは次です。
カラーLEDテープ照明を自由にコントロール - 勝手な電子工作・・
LEDテープを使ったディスプレイの実験(Arduino-Unoで) - 勝手な電子工作・・
次のような大型、といっても8x32で256個のLEDが配列された柔らかいシートが売られているのを見つけました。AliExpressで1枚約800円でしたが、注文したら珍しく翌週にサッと到着。LEDテープというよりLEDシートというべきかもしれません。
しめしめ、これを3段つなげば32x24ドットができるぞ!
毎度ながら簡単・迅速に作りたいですし、また、後で部品に戻せるようにしたいもの。ここでは両面テープでプラバンに貼り付け、裏から配線することにします。
真ん中は電源供給ラインを並列につなぎます。信号線は最初の入り口は左上に(信号線とグランドのみ)が入り、右上から出た最後を真ん中左へ(信号線のみ1本でOK、グランドは電源側で接続されているわけなので)と配線。同じく中右から出た最後を左したへと配線して終わり。じつに単純ですね^^ 動かないようにセロテープで止めて出来上がり。電源は5V2AのACアダプターで十分でした。
32x24とはいえ、24x32と見る方が分かりやすい。とはいってもLEDの接続順序は表から見ると次のように少し複雑になります。これをXY座標として扱って、プログラム論理でこれにあうようにすればよいだけです。
柔らかいシートとはいっても、両面テープは時間が経つと、次の写真のようにジワリと剥がれて浮くところが少しでます。強力両面テープを使えばこうはなりませんが、後で分解しいやすくするためには、この程度ですかね。
見易さを兼ねて、たまたま手元にある薄黒い半透明アクリル板をこの上に重ねて使ってみます。なにせLEDは相当に眩しいですから。また、こうすることでディスプレイの平面も浮くことなく保てています。
FastLEDライブラリーは容量を食わずに便利ですが、この場合はLED数768個x3バイト(3色の値)ぶんの配列を内部でこしらえるため、ArduinoはMega2560を使うことにします。
画像データはPCのシリアルから送る方法でもよいと思いますが、持ち運び用に、ここではマイクロSDカードから入力することにしました。
1つのファイルが画面1頁です。中身は3色のデータ#RRGGBBを768個収容したファイルで、ファイル名を順次変化させることで、自動的にページを順番に読ませることにしました。
そのArduinoスケッチは後ろにつけておきます。
簡単にファイルを作るために、画像を24x32ピクセルのBMPにして、それをPCで読み込ませます。簡単なSmallBasicでテスト用にちょこっと作ったWindows用のプログラムですが、ご参考のため記事の最後に添付しておきます。
そして表示の一例が次です。
実際はとても綺麗なのですが、LEDの輝度が高過ぎてうまく撮れませんm(_ _ )m
活字も、「ドットフォント」を絵と同じように扱えば大丈夫。ここで使っているドットフォントはフリーのKHドットフォントのなかにある、小伝馬町16というフォントです。True Typeフォントがしっかり提供されていますので、絵として使うのにも適しています。色々便利に使わせていただいています。OLEDなどの表示も画像としてとり扱うと手間がかからないのでなかなかいい手段ではないかなと勝手に思います。もちろんコードで直接扱うのがベストではありますが。
次がArduino-Megaのスケッチです。
/********************************************************
LED panel display for Arduino-Mega
Read & show RGB data from "RGBtnnn.txt" files.
Version 00 Aug.8, 2021 by Akira Tominaga
*********************************************************/
#include "FastLED.h"
#include "SPI.h"
#include "SD.h"
#define dPin 8 // data pin to LED tape
#define CS 53 // SD MISO=50,MOSI=51,SCK=52
uint16_t X = 0, Y = 0, Pnl = 0; // column, row, and panel
#define maxX 24 // maximum X in total
#define maxY 32 // maximum Y
#define nPnls 3 // nnumber of panels (films)
#define pnlX (maxX/nPnls) // X length of each panel
#define NinPnl (pnlX*maxY) // number of LEDs in a panel
#define nLEDs (NinPnl*nPnls) // number of total LEDs
CRGB myTp[nLEDs]; // construct myTp array
File mySD; // construct mySD
String fNbase = "RGBt"; // for file name RGBtnnn.txt
String fNext = ".txt"; // file name extention
uint16_t fNum = 0; // file number = page (max999)
char fN[4]; // file number in char
String fName = ""; // file name
#define fRlen 9 // file rec length 7char+CRLF
char SDin[fRlen + 1]; // SD-in area with CRLF + Null
uint8_t vRGB[3]; // temporally RGB work data
void setup() { // ***** Arsuino setup() *****
Serial.begin(9600);
delay(2000); // enough time for LED pwr
if (!SD.begin(CS)) {
Serial.println(F("SD err."));
while (true);
}
FastLED.addLeds<WS2812, dPin, GRB>(myTp, nLEDs);
clrLED();
}
void loop() { // ***** Arduino loop() *****
setFname(); //next file name to open
Serial.println(fName);
clrLED();
XY2LED(); // read RGB@(X,Y) and set to LED positions
FastLED.show(); // show the page
delay(1000); // time to display a page
mySD.close(); // close current page
fNum++; // set next file(=page) number
}
/************************************************
User defined functions
************************************************/
// ***** clear LED *** clrLED() *****
void clrLED(void) {
#define Black (0,0,0)
for (int i = 0; i < nLEDs; i++) {
myTp[i].setRGB Black;
}
FastLED.show();
}
// ***** set file name of next page *** setFname() *****
void setFname(void) {
sprintf(fN, "%03d", fNum);
fName = fNbase + String(fN) + fNext;
mySD = SD.open(fName); // open a page
if (!mySD) { // if file not found
Serial.print(F("No "));
Serial.println(fName);
if (fNum > 0) { // if no more page
delay(1000); // wait for a second
fNum = 0; // and return to page 0
fName = fNbase + "000" + fNext; // restart from p.0
mySD = SD.open(fName); // and open page 0
} else { // if page 0 file not found
while (true) {} // then stop
}
}
}
// ***** get RGB[](X,Y) val and set to LED pos. *** XY2LED() *****
void XY2LED(void) {
uint16_t sLED; // sequence # of LED from the top
for (Y = 0; Y < maxY; Y++) {
for (X = 0; X < maxX; X++) {
Pnl = X / pnlX; // get panel number
uint16_t nC = X % pnlX; // get X (column #) in that panel
readRGB(); // read record for RGB values
if (Y % 2 == 1) { // for Ys 1,3,5,,,
sLED=nC + Pnl * NinPnl + Y*pnlX;
} else { // for Ys 0, 2,4,,, reverse X sequence
sLED=(pnlX - 1 - nC) + Pnl * NinPnl + Y*pnlX;
}
myTp[sLED].setRGB(vRGB[0], vRGB[1], vRGB[2]);
}
}
}
// ***** read SD for RGB data *** readRGB() ***
void readRGB(void) {
while (!mySD.available()) {}
mySD.read(SDin, fRlen); // read a record
for (int i = 0; i < 3; i++) { // for RGB data
byte Vh = SDin[i * 2 + 1] & 0x0F; // if numeric, get value
if (SDin[2] > 0x40) { // if alfabet,
Vh = SDin[i * 2 + 1] - 55; // set alphabetic value
}
byte Vl = SDin[i * 2 + 2] & 0x0F; // if numeric, get value
if (SDin[2] > 0x40) { // if alfabet,
Vl = SDin[i * 2 + 2] - 55; // set alphabetic value
}
vRGB[i] = (Vh * 16 + Vl) / (3); // divide R|G|B w/ 3
}
}
// ***** end of program *****
読み込むファイルには次の形式の固定長レコードが、LEDの個数分並んでいます。
#RRGGBB\n
RR、GG、BBは赤、緑、青の強さで、0からFFで表します。しかし、そのままではLEDが明るすぎるため、Arduinoで読んだら各値を1/3に落としています。節電にも重要ですね。
ファイル1個が画面の1頁となり、Arduinoはファイル番号を増やしながら次々読み、次のファイルがなくなると最初に戻ります。
そのファイルをWindowsPCでBMP画像から自動生成する簡単なプログラムは次です。Small Basicでちょこっと作ったテスト用ですが。
myTitle="RGB text builder Aug.8,2021 (c)Akira Tominaga"
TextWindow.Title=myTitle
TextWindow.BackgroundColor="Black"
' Get data path
myPath=File.GetSettingsFilePath()
myPath=Text.GetSubText(myPath,1,8)
myPath=myPath+"DatW\"
' Prepare picture
TextWindow.WriteLine("")
KeyIn:
TextWindow.Write(" Please type-in RGBt#. -> ")
keyinN=TextWindow.Read()
myPict=myPath+"RGBt"+keyinN+".bmp"
PsizeX=ImageList.GetWidthOfImage(myPict)
If PsizeX>5 Then
Goto ImageOK
Else
TextWindow.Write("*** No such bmp as ")
TextWindow.WriteLine(myPict)
Goto Keyin
EndIf
ImageOK:
PsizeY=ImageList.GetHeightOfImage(myPict)
TextWindow.Write("Xsize=")
TextWindow.WriteLine(PsizeX)
TextWindow.Write("Ysize=")
TextWindow.WriteLine(PsizeY)
'Setup output file
myFile=myPath+"RGBt"+KeyinN+".txt"
'lnum=1 'PsizeX,PsizeY (3chars left-zero)
Xs=Text.GetSubText(1000+PsizeX,2,3)
Ys=Text.GetSubText(1000+PsizeY,2,3)
header=Xs+","+Ys
'File.WriteLine(myFile,lnum,header)
lnum=0
'Graphics window
GraphicsWindow.BackgroundColor = "Black"
GraphicsWindow.Width=PsizeX*45
GraphicsWindow.Height=PsizeY*22
GraphicsWindow.Title=myTitle
inPic=ImageList.LoadImage(myPict)
GraphicsWindow.DrawImage(inPic,0,0)
For j= 0 to PsizeY-1
For i=0 To PsizeX-1
color=GraphicsWindow.GetPixel(i,j)
TextWindow.WriteLine(Color)
lnum=lnum+1
File.WriteLine(myFile,lnum,Color)
'Draw big image
XXb=PsizeX+2
YYb=PsizeY+2
XX=i*20+XXb
YY=j*20+YYb
GraphicsWindow.BrushColor=Color
GraphicsWindow.FillRectangle(XX,YY,20,20)
EndFor
EndFor
lnum=lnum+1
eof="FileEnd"
File.WriteLine(myFile,lnum,eof)
TextWindow.WriteLine("**** txt file has been made. Next pls!")
Goto KeyIn
表示が粗くてもとにかく大きなディスプレイが要る際には、こうして作ると便利だと思います。
以上、簡単な記事でしたが皆様のなんらかのお役に立てば幸いです。
©2021 Akira Tominaga, All rights reserved.