WS2812の話題がZRouter.orgのircでもあったので、ちょっといじってみました。
AmazonでWS2812の安いモジュールは中国発送で、クリスマスに間に合わないので、ヤフーショッピングで安いものを購入しました。送料無料で秋葉までの電車代より安かったです。
ESP32ではRMTというOne-Wireをサポートする機構があり、それを使っているようですが、このような機構が無い場合はSPIで処理することもあるようです。
WS2812の仕様を確認してみると300nsくらいの波形が必要になるようです。
SPIはかなり苦戦しているようなので、FTDIのBitBangを使って試してみることにしました。
FreeBSDのmrubyで以前パラレルLCDを処理するのにmruby-ftdiを作ったので、これを使います。
# WS2812 sample script
HI = 0xff
LO = 0x00
class WS2812
RESET = [LO]
ZERO = [HI, LO, LO]
ONE = [HI, HI, LO, LO]
def initialize(ftdi)
@f = ftdi
end
def send_hex(hex)
b = []
for h in hex do
for n in 0..23 do
if (h >> (23 - n)) & 1 == 1
b += ONE
else
b += ZERO
end
end
end
dat = b + [HI]
@f.write(RESET)
usleep 200
@f.write(dat)
end
end
begin
# FT232H
ftdi = Ftdi.new(0x6014, 0xff)
ftdi.baudrate(9600*20)
ftdi.write([HI])
ws = WS2812.new(ftdi)
# Green, Red, Blue
pat = [0x400000] * 7
ws.send_hex(pat)
end
光りました。これで25mAくらいで、無点灯でも4.5mAくらいでした。
古いFTDIのチップはだめで、USB 2.0のFT232Hでないとだめでした。
ビットの0は3バイト,1が4バイトで送られて、LEDが1つあたり24ビットなのでMAX96バイトで、7つの場合は7倍で672バイトで終端の1バイトを入れると673バイトになります。
8x8のような大きなモジュールを使う場合にはmruby-ftdiのバッファを大きくする必要がありますが、動作は未確認です。
WS2812は送られてくる信号の先頭のGRB(24ビット)を拾って、残りを次のデバイスに渡すことで、複数のデバイスをサポートしているようです。このモジュールでは真ん中が一番最後です。
baudrateはオシロで波形を確認して決めました。
上記のコードでは全ビット同じものがでますが、別々にしてそれぞれWS2812に入れれば8倍の高速化が実現できますね。
やっぱクリスマスは光物がいいですね。
Python先輩にはFTDIのMPSSEを使ったライブラリがあるようですが、わざわざより複雑なMPSSEを使うまでもなく、BitBangで十分だと思います。
おまけ
正月も近いので、ぐるぐる回るアニメーションを作ってみました。
n = 0
loop do
pat = [0x000000] * n
pat += [0x400000]
pat += [0x000000] * (6 - n)
ws.send_hex(pat)
n += 1
n = 0 if n == 6
usleep 100_000
end
いつもより多く回っております。
こうしても同じように回ります。
n = 0
pat = [0x400000] + [0x000000] * 6
loop do
ws.send_hex(pat.rotate(-n))
n += 1
n = 0 if n == 6
usleep 100_000
end
こうすると
n = 0
loop do
pat = [0x400000] * n + [0x000000] * (7 - n)
ws.send_hex(pat)
n += 1
n = 0 if n == 7
usleep 100_000
end
こうなります。
虹色も試してみました。まずWS2812クラスに以下のメソッドを追加します。ESP32のコードを参考にしました。
def hsb_to_hex(h, s, b)
h = h % 360
s = s / 100.0
b = b / 100.0
if s == 0
gray = (b * 255).to_i
return [gray, gray, gray]
end
h_sector = h / 60.0
sector = h_sector.to_i
fraction = h_sector - sector
tint1 = b * (1 - s)
tint2 = b * (1 - s * fraction)
tint3 = b * (1 - s * (1 - fraction))
r, g, b_rgb = case sector
when 0
[b, tint3, tint1]
when 1
[tint2, b, tint1]
when 2
[tint1, b, tint3]
when 3
[tint1, tint2, b]
when 4
[tint3, tint1, b]
else
[b, tint1, tint2]
end
(g * 255).to_i << 16 | (r * 255).to_i << 8 | (b_rgb * 255).to_i
end
これで表示します。
loop do
for n in 0..360 do
pat = [ws.hsb_to_hex(n, 100, 100)] * 7
ws.send_hex(pat)
usleep 50_000
end
end
なんか、あまり綺麗ではありません。



