これはコドモン Advent Calendar 2022 6日目の記事です。
コドモンのくまたです。
早速ですが、
アドベントカレンダ → クリスマス → サンタクロース → 煙突 → 暖炉
という個人的な連想からRaspberry Piでミニチュアの暖炉を作ることにしました。ただ、表題のとおり「中身」だけ、すなわち炎の部分のみ今回お話し、ガワというか暖炉の見た目に関してはお好みで作っていただければと思います。
用意するもの
以下のとおりです。なお、Grove1を選んだ理由は、各種センサ(LEDやスイッチや湿度計などなど)の扱いが非常に簡単で、抵抗が云々、センサの電圧が云々、ピンが云々を考えなくてもいいからです。 またGrove Base Hatというものもありますが、導入の容易性を考慮してGrovePi+にしてます。
以後Chanable RGB LED v1.0をLEDと呼称します。
準備
Raspberry Pi FoundationとDexter Industryそれぞれが簡潔かつ明瞭な手順を公開しているのでリンクだけ紹介します。
概要は以下のとおりです。
- Raspberry PiとGrovePi+をGPIO接続する
- Raspberry Pi Imagerをダウンロードして実行する
- Raspberry Pi ImagerでRaspberry Pi OSのダウンロードとSDカードへの書き込みを行う
- Raspberry Piに挿して電源に接続する
- Raspberry Pi上で
sudo curl -kL dexterindustries.com/update_grovepi | bash
を実行する
LEDはGrovePi+のD7ポートに接続する前提で話を進めます。
動作確認
準備が完了したら、以下のサンプルコードを実行してLEDが光るか確認します。準備が完了した段階でDexter IndustryのサンプルコードがRaspberry Pi OSのホームディレクトリにでき上がりますが、このサンプルはD2ポートなこととgitコマンドで元に戻ってしまうので、別ファイルにしてお試しします。 ここで、色を変えたり発光タイミングを変えたりするだけでも楽しいです。
以下のサンプルコードは一定秒ごとに色が変わるだけのものです。
サンプルコード(クリックすると開きます)
import time import grovepi pin = 7 numleds = 1 grovepi.pinMode(pin,"OUTPUT") time.sleep(1) testColorBlack = 0 # 0b000 #000000 testColorBlue = 1 # 0b001 #0000FF testColorGreen = 2 # 0b010 #00FF00 testColorCyan = 3 # 0b011 #00FFFF testColorRed = 4 # 0b100 #FF0000 testColorMagenta = 5 # 0b101 #FF00FF testColorYellow = 6 # 0b110 #FFFF00 testColorWhite = 7 # 0b111 #FFFFFF thisLedOnly = 0 allLedsExceptThis = 1 thisLedAndInwards = 2 thisLedAndOutwards = 3 try: print("Test 1) Initialise") grovepi.chainableRgbLed_init(pin, numleds) time.sleep(.5) # change color to green grovepi.storeColor(0,255,0) time.sleep(.5) # set led 1 to green grovepi.chainableRgbLed_pattern(pin, thisLedOnly, 0) time.sleep(.5) # change color to red grovepi.storeColor(255,0,0) time.sleep(.5) # set led 10 to red #grovepi.chainableRgbLed_pattern(pin, thisLedOnly, 9) grovepi.chainableRgbLed_pattern(pin, thisLedOnly, 0) time.sleep(.5) # pause so you can see what happened #time.sleep(2) time.sleep(.5) # reset (all off) grovepi.chainableRgbLed_test(pin, numleds, testColorBlack) time.sleep(.5) print ("Test 2a) Test Patterns - black") # test pattern 0 - black (all off) grovepi.chainableRgbLed_test(pin, numleds, testColorBlack) time.sleep(1) print ("Test 2b) Test Patterns - blue") # test pattern 1 blue grovepi.chainableRgbLed_test(pin, numleds, testColorBlue) time.sleep(1) print ("Test 2c) Test Patterns - green") # test pattern 2 green grovepi.chainableRgbLed_test(pin, numleds, testColorGreen) time.sleep(1) print ("Test 2d) Test Patterns - cyan") # test pattern 3 cyan grovepi.chainableRgbLed_test(pin, numleds, testColorCyan) time.sleep(1) print ("Test 2e) Test Patterns - red") # test pattern 4 red grovepi.chainableRgbLed_test(pin, numleds, testColorRed) time.sleep(1) print ("Test 2f) Test Patterns - magenta") # test pattern 5 magenta grovepi.chainableRgbLed_test(pin, numleds, testColorMagenta) time.sleep(1) print ("Test 2g) Test Patterns - yellow") # test pattern 6 yellow grovepi.chainableRgbLed_test(pin, numleds, testColorYellow) time.sleep(1) print ("Test 2h) Test Patterns - white") # test pattern 7 white grovepi.chainableRgbLed_test(pin, numleds, testColorWhite) time.sleep(1) # pause so you can see what happened time.sleep(1) # reset (all off) grovepi.chainableRgbLed_test(pin, numleds, testColorBlack) time.sleep(.5) except KeyboardInterrupt: # reset (all off) grovepi.chainableRgbLed_test(pin, numleds, testColorBlack) except IOError: print ("Error")
準備がうまくいってれば以下のようになります(Animation GIFをループしてます)。
炎の色について
ロウソクや焚き火で、燃焼した際の温度差による上昇気流に燃えカスが乗り、上昇中の過程で炭素になり、これが熱放射することで色が見えます。炭素の場合は黒体放射といいます。 そのなかでも可視光となると以下になります。
- 赤 640-770 nm
- 黄 550-590 nm
- 橙 590-640 nm
今回はLEDが一つなので、550-770 nmの乱数としてまとめ、これをRGBに変換してPythonの辞書型にします。 色の波長からRGBに変換するには専門的な数学の知識が必要となるので、「spectrum color rgb convert」などで検索してお知恵を頂戴します。 そして、RGBをいくつかのパターンにまとめ以下のように辞書型として保存します。
fire_pattern_1 = [ (161,0,0), (234,35,0), (255,129,0), (242,85,0), (216,0,0) ] fire_pattern_2 = [ (225,122,87), (199,75,75), (161,59,59), (133,43,43), (107,8,8) ] fire_pattern_3 = [ (236,83,0), (249,125,22), (255,151,80), (255,182,5), (189,67,67) ]
あとはこれをランダムに発光させるようにすると擬似的な炎になります。
サンプルコード(クリックすると開きます)
#!/usr/bin/env python #-*- coding:utf-8 -*- # import time import random import grovepi import signal import sys pin7 = 7 numleds = 1 grovepi.pinMode(pin7, "OUTPUT") grovepi.chainableRgbLed_init(pin7, numleds) int = 0 r = 235 g = 35 b = 0 fire_pattern_1 = [ (161,0,0), (234,35,0), (255,129,0), (242,85,0), (216,0,0) ] fire_pattern_2 = [ (225,122,87), (199,75,75), (161,59,59), (133,43,43), (107,8,8) ] fire_pattern_3 = [ (236,83,0), (249,125,22), (255,151,80), (255,182,5), (189,67,67) ] def signal_term_handler(signal, frame): print 'got SIGTERM' grovepi.chainableRgbLed_test(pin7, numleds, 0) sys.exit(0) def generate_rgb_color(i): global r, g, b k = random.randint(0, 1) if k == 0: r = r + i if r > 245: r = 245 g = g + i if g > 55: g = 55 else: r = r - i if r < 215: r = 215 g = g - i if g < 15: g = 15 b = 0 t = 0.05 return (r, g, b, t) if __name__ == '__main__': signal.signal(signal.SIGTERM, signal_term_handler) while True: try: if int > 20: int = 1 (r, g, b, t) = generate_rgb_color(int) grovepi.storeColor(r, g, b) grovepi.chainableRgbLed_pattern(pin7, 0, 0) int = int + 1 time.sleep(t) except KeyboardInterrupt: grovepi.chainableRgbLed_test(pin7, numleds, 0) quit() except IOError: print ("Error")
結果
以下のようになります。画像だと白っぽいですが実際は赤黄橙です(Animation GIFをループしてます)。
お菓子の空き箱で暖炉のガワを作ってもいいですし、タッパーで覆ってもいいですし、100均でアクリルの箱を加工してもいいですし、色のパターンを変えてセロファンと組み合わせても何か発見があるかもしれませんし、ちらつき具合を1/f(ゆらぎ)にしてもいいかもしれません。 コードの変更と見栄えが一目瞭然なので小さなお子さまは興味が湧いたりしませんでしょうか。
冬休みに向けて家族団欒の一助になれば幸いです(そうじゃなくても炎を見つめるのが好きで癒されるわと思っていただけたら幸いです)。
コドモンの開発チームのTwitterを始めました! アドベントカレンダーの新着記事も毎日ツイートしていくので、ぜひフォローしてください😊
- Raspberry PiやArduino上で、各種センサを4 pinケーブルで扱えるようにしたシステムのことです。↩
- 2Bでも問題ないです4Bは公式をご参照ください。↩
- Groveシステムを使えるようにする拡張ボードです。GrovePi+のファームウェアv1.2.6で、めちゃくちゃな英語で恥ずかしいのですが、自分のPull Requestが通り製品に反映されたときは嬉しかったです。今は残念ながら残っておりません。↩
- Chainable RGB LED v1.0はGrove版とGPIO版がありますが、今回はGrove版なのでご注意ください。体積は約20mm x 20mm x 30mmです。v2.0は40mm x 20mm x15mmと横長になってます。↩