コドモン Product Team Blog

株式会社コドモンの開発チームで運営しているブログです。エンジニアやPdMメンバーが、プロダクトや技術やチームについて発信します!

Raspberry Piでミニチュア暖炉の中身を作ってみた

これはコドモン Advent Calendar 2022 6日目の記事です。

コドモンのくまたです。

早速ですが、

アドベントカレンダ → クリスマス → サンタクロース → 煙突 → 暖炉

という個人的な連想からRaspberry Piでミニチュアの暖炉を作ることにしました。ただ、表題のとおり「中身」だけ、すなわち炎の部分のみ今回お話し、ガワというか暖炉の見た目に関してはお好みで作っていただければと思います。

用意するもの

以下のとおりです。なお、Grove1を選んだ理由は、各種センサ(LEDやスイッチや湿度計などなど)の扱いが非常に簡単で、抵抗が云々、センサの電圧が云々、ピンが云々を考えなくてもいいからです。 またGrove Base Hatというものもありますが、導入の容易性を考慮してGrovePi+にしてます。

  • Raspberry Pi 3B+2
  • GrovePi+3
  • Chainable RGB LED4 v1.0(Grove)

以後Chanable RGB LED v1.0をLEDと呼称します。

準備

Raspberry Pi FoundationとDexter Industryそれぞれが簡潔かつ明瞭な手順を公開しているのでリンクだけ紹介します。

  1. Raspberry Pi OSの導入
  2. GrovePi+のライブラリやフレームワークやサンプルの導入

概要は以下のとおりです。

  1. Raspberry PiとGrovePi+をGPIO接続する
  2. Raspberry Pi Imagerをダウンロードして実行する
  3. Raspberry Pi ImagerでRaspberry Pi OSのダウンロードとSDカードへの書き込みを行う
  4. Raspberry Piに挿して電源に接続する
  5. 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を始めました! アドベントカレンダーの新着記事も毎日ツイートしていくので、ぜひフォローしてください😊

twitter.com


  1. Raspberry PiやArduino上で、各種センサを4 pinケーブルで扱えるようにしたシステムのことです。
  2. 2Bでも問題ないです4Bは公式をご参照ください。
  3. Groveシステムを使えるようにする拡張ボードです。GrovePi+のファームウェアv1.2.6で、めちゃくちゃな英語で恥ずかしいのですが、自分のPull Requestが通り製品に反映されたときは嬉しかったです。今は残念ながら残っておりません。
  4. Chainable RGB LED v1.0はGrove版とGPIO版がありますが、今回はGrove版なのでご注意ください。体積は約20mm x 20mm x 30mmです。v2.0は40mm x 20mm x15mmと横長になってます。