RaspberryPiでIHコンロを制御して低温調理器を作ってみた

#はじめに
最近、低温調理をすることが多いのですが、温度計と見ながらIHコンロの火力調整を自力でやるのが面倒になってきました。
そこで、RaspberryPiに「温度センサ」と「サーボモータ」をつないでIHコンロの火力調整を自動化できるのではないかと思い、簡易的な低温調理器を実際に作ってみました。

#低温調理器の概要
低温調理の方法としては水を張った鍋を使用し、その鍋の中に低温調理する食材を入れておく。鍋の水温が調理温度を維持し続けられるようIHコンロの火力を調整する。
この制御をRaspberryPiに「温度センサ」と「サーボモータ」を接続することで実現する。
「温度センサ」と「サーボモータ」のそれぞれの役割は以下の通り。

* 温度センサの役割 → 温度センサで水温を監視するために使用する。
* サーボモータの役割 → サーボモータでIHコンロの火力を調整するために使用する。水温が低ければ火力を上げて、逆に水温が高ければ火力を下げる。
![全体像.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1094765/2958f4dc-506e-f3fa-ae1a-8b59a80427b6.jpeg)

#必要機材
必要機材については以下の通り。機材はインターネット通販や100均で機材は購入した。

* IHコンロ
* RaspberryPi2B
* 温度センサ（型番：DS18B20 防水タイプ）→ インターネット通販で購入
* サーボモータ（型番：SG90）→ インターネット通販で購入）
* ブレッドボード → インターネット通販で購入
* 抵抗（4.7kΩ）→ インターネット通販で購入
* ジャンパーワイヤー（5本）→ インターネット通販で購入
* 耐震マット → 100均で購入

#ソフトウェア
使用するソフトウェアは以下の通り。

#####RaspberryPi
* RaspberryPi OS
* Python

#####開発PC
* Teraterm（RaspberryPiとのSSH接続で使用）

#機器の接続方法
RaspberryPiの端子と、それぞれの機器の接続イメージは以下の通り。
![回路図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1094765/7316059c-c9a5-4b22-f7d7-303bb63704ce.jpeg)
※抵抗は10kΩだと温度センサが正常動作しなかった。そのため4.7kΩを使用した
※温度センサは3.3Vに接続すること。自分は誤って5Vに接続して温度センサが壊れた

参考までに実際に機器を接続したときのブレッドボードの写真は以下の通り。
![ブレッドボード.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1094765/519106ca-4a27-3f9a-3311-2682cebf4527.jpeg)

#実装内容
RaspberryPi上で実行するプログラム（低温調理プログラム）について説明する。
プログラミング言語は、温度センサ、サーボモータのどちらも手軽に制御できるPythonを使用する。

###温度センサの制御
温度センサ（DS18B20）との通信で1-wireを使用するため、事前にRaspberryPi OSの1-wire設定を有効化する必要がある。
`/boot/config.txt`をエディタで開き、下記の内容を追記する。

```text:/boot/config.txt
dtoverlay=w1-gpio
```

###サーボモータの制御
サーボモータはPWMで制御する。パルス波のデューティー比を変化させることでサーボモータの回転角を制御する。なお、PWMは特に前準備をする必要なく使用することが可能である。
参考までにサーボモータの回転角をデューティー比に変換する式は以下の通り。

```
dutyCycle = 2.5 + (12.0 - 2.5) / 180 * (degree + 90)

```
※dutyCycleはデューティー比として設定する値、degreeは回転角

###低温調理プログラムの概要
低温調理プログラムの処理の流れを簡単に説明する。

1．サーボモータ、温度センサの初期化
2．調理時間の取得（Teratermからの入力待ち）
3．調理温度の取得（Teratermからの入力待ち）
4．調理温度になるまで水温監視
5．調理温度になったらサーボモータでIHコンロの火力を最小にする
6．水温を監視し、調理温度の±3℃の範囲を超えたらサーボモータで火力を調整する

###ソースコード
低温調理プログラムのソースコードは以下の通り。

```python:low_temp_cooking.py
import os
import glob
import time
import subprocess

GPIO_PORT_SERVO = 12
PWM_FREQ_SERVO = 50
PUSH_LEFT_BUTTON = 45
PUSH_RIGHT_BUTTON = -45
RELEASE_BUTTON = 0
DEFAULT_IH_FIREPOWER = 5
TEMPERATURE_MARGIN = 2

def initServomotor():
    import RPi.GPIO as GPIO
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_PORT_SERVO, GPIO.OUT)
    pwmInstance = GPIO.PWM(GPIO_PORT_SERVO, PWM_FREQ_SERVO)
    pwmInstance.start(0.0)
    return pwmInstance

def rotateServomotor(pwmInstance, degree):
    dutyCycle = 2.5 + (12.0 - 2.5) / 180 * (degree + 90)
    print(dutyCycle)
    pwmInstance.ChangeDutyCycle(dutyCycle)
    time.sleep(0.5)
    pwmInstance.ChangeDutyCycle(0.0)

def initOneWare():
    os.system('modprobe w1-gpio')
    os.system('modprobe w1-therm')

def getOneWareFilePath():
    baseDir = '/sys/bus/w1/devices/'
    deviceFolder = glob.glob(baseDir + '28*')[0]
    deviceFile = deviceFolder + '/w1_slave'
    return deviceFile

def readTempRaw(filePath):
    catData = subprocess.Popen(['cat',filePath], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    out,err = catData.communicate()
    outDecode = out.decode('utf-8')
    lines = outDecode.split('\n')
    return lines

def readTemp(filePath):
    lines = readTempRaw(filePath)
    while lines[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines = readTempRaw(filePath)
    equalsPos = lines[1].find('t=')
    if equalsPos != -1:
        tempString = lines[1][equalsPos + 2:]
        temp = float(tempString) / 1000.0
        return temp


# Main
# Initialize servomotor
pwmInstance = initServomotor()

# Initialize thermometer
initOneWare()
oneWareFilePath = getOneWareFilePath()

# Get the temperature
print("input Temperature (e.g. 60 - 80)")
targetTemperature = float(input())

# Get the Minutes
print("input Minutes (e.g. 30 - 120)")
endingTime = float(input()) * 60

# Get the Temperature
currentTemp = readTemp(oneWareFilePath)
print("currentTemp = %f, targetTemp = %f" % (currentTemp, targetTemperature))

# Heating phase
print("Heating phase")
while currentTemp < targetTemperature:
    currentTemp = readTemp(oneWareFilePath)
    print("currentTemp = %f" % (currentTemp))
    time.sleep(1.0)

for i in range(DEFAULT_IH_FIREPOWER - 1):
    rotateServomotor(pwmInstance, PUSH_LEFT_BUTTON)
    rotateServomotor(pwmInstance, RELEASE_BUTTON)

# Calculate low temperature cooking time
currentTime = time.time()
endingTime = endingTime + currentTime

# Insulation phase
print("Insulation phase")
while endingTime > currentTime:
    currentTemp = readTemp(oneWareFilePath)
    print("Temp = %f, Time = %f" % (currentTemp, (endingTime - currentTime)))
    if currentTemp < (targetTemperature - TEMPERATURE_MARGIN):
        rotateServomotor(pwmInstance, PUSH_RIGHT_BUTTON)
        rotateServomotor(pwmInstance, RELEASE_BUTTON)
        print("Push right button")
        time.sleep(30.0)
    elif currentTemp > (targetTemperature + TEMPERATURE_MARGIN):
        rotateServomotor(pwmInstance, PUSH_LEFT_BUTTON)
        rotateServomotor(pwmInstance, RELEASE_BUTTON)
        print("Push left button")
        time.sleep(30.0)
    time.sleep(1.0)
    currentTime = time.time()

print("Finished cooking")
```

#動作確認
以下の手順で動作確認を実施。

1. Teratermから`$ sudo python low_temp_cooking.py`を入力して低温調理プログラムを実行する。
1. 温度や時間を聞かれるのでTeratermから値を入力する。今回は、調理温度64度、調理時間60分で動作確認を行う。
1. IHコンロの電源を入れて調理開始（残念ながらここは手動。。。）
1. 調理温度まで達するとサーボモータが動作し、IHコンロの温度調整が行われる。
1. 低温調理開始から60分経過すると調理終了を示すログ`Finished cooking`がTeratermに出力される。このタイミングでIHコンロの電源を切る。（残念ながらここも手動。。。）
![低温調理機.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1094765/ef0efcf3-b5cd-eb09-876e-00b34a0adb3b.png)

#まとめ
とりあえず、RaspberryPiで簡易的な低温調理器を作成することはできた。
サーボモータの固定方法には100均の耐震マットを使用した。しかし、耐震マットだとサーボモータを固定する力が弱く、IHコンロのスイッチを押せない場合があった。
サーボモータの固定方法は、もう少し考えたほうがよさそう。