DreamerDreamのブログ

夢想家の夢です。〜揚げたてのモヤっとしたものをラフレシアと共に〜

RaspberryPiと炊飯器で低温調理器を自作してみた⑨ 「ヒーター制御デーモン」

現在の温度を取得、設定温度と比較してヒーターの制御を行なう、実際に調理を行なうするための主要プログラムです。

f:id:DreamerDream:20190918164220p:plain


<heaterd.py>

 

#!/usr/bin/python2

import RPi.GPIO as GPIO
import time
import sys
import atexit
import file_mng as file
import datetime

heater = 17

set_heat_time = 10.0

#出力値をストック

stock_power =

#温度をストック
stock_temp =

#ストックするサイズ

size_list = 12

#10秒中、何秒ヒーターをONにするか
power_on = 10.0

#ログデータのファイル名

file_log = None

 

#ログデータ保存 

def save_log( now_temp, power_on ):
  global file_log
  if file_log is None:
    now = datetime.datetime.now()
    file_log = now.strftime("%Y%m%d-%H%M%S")
    file.log( file_log, now_temp, power_on )

 

#ヒーター出力ピン設定
def init():
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(heater,GPIO.OUT)
  atexit.register(end)

#異常終了時にGPIOをリセット

def end():
  GPIO.cleanup()
  print "cleanup"

 

#ヒーターのON,OFF

def on():
  GPIO.output(heater,1)
def off():
  GPIO.output(heater,0)

 

#ヒーターをON、OFFにして指定時間待機
def heat( tim_on ):
  tim_off = set_heat_time - tim_on
  if 0.0 < tim_on:
    on()
    file.save_row_4('heating...' + str( tim_on ) +'sec' )
    time.sleep( tim_on )
  if 0.0 < tim_off:
    off()
    file.save_row_4('cooling...' + str( tim_off ) +'sec' )
    time.sleep( tim_off )

 

 

def main():
  global stock_power, stock_temp ,power_on

  #設定温度を読み出し

  set_temp = float( file.get_temp_set() )

  #温度制御開始は設定温度の半分に達してから
  start_temp = set_temp *0.5
  now_temp = file.load_temp()

  #現在温度を読み込む  

  try:
    now_temp = float(now_temp)
  except:

  #読み込み失敗したらストッカーの最新の温度で代用
    if 0 < len(stock_temp):
      now_temp = stock_temp[ len(stock_temp) -1 ]
    else:
      now_temp = 0.0

 

  #設定温度以上ならヒーターOFF 

  if set_temp <= now_temp:
    power_on = 0.0

 

  #制御開始温度以上であればヒーターON時間を計算する

  elif start_temp < now_temp :

    #温度に反比例するように制御(廃案)

    #wari = 1.0 - ( ( now_temp - start_temp ) / ( set_temp - start_temp ) )

    #sinカーブを利用して制御(採用)
    d = 90 * ( set_temp - now_temp ) / ( set_temp - start_temp )
    wari = math.sin( math.radians( d ) )


    if 0 < wari:

      power_on = set_heat_time * wari

      if size_list <= len(stock_temp):
        sum_temp = 0
        sum_power = 0
        count_heat = 0
        for i in range( 1, len(stock_temp) ):
          sum_temp = sum_temp + ( stock_temp[ i ] - stock_temp[ i -1 ] )
          sum_power = sum_power + stock_power[ i -1 ]
        avl_temp = sum_temp / ( len( stock_temp ) -1 )
        avl_power = sum_power / ( len( stock_power ) -1 )

        if avl_power > 0.0:
          #ターボモード、温度が勢いよく下がったらヒーター時間を長くする
          tarbo = ( avl_temp / avl_power )
          if tarbo < 0.0:
            power_on = power_on * ( 1 - tarbo *100 )

 

      #リレーの反応時間を考慮して細かすぎる制御は省く(最低0.5秒)
      if power_on < 0.5:
        power_on = 0.5
      if set_heat_time - 0.5 < power_on:
        power_on = set_heat_time

    else:
      power_on = 0.0

 

  #温度が制御開始温度以下ならヒーターを最高出力

  else:
    power_on = set_heat_time

 

  #ヒーター温度と制御時間を新しい情報にして保存
  stock_power.append( power_on ) 
  stock_power = stock_power[-size_list:]
  stock_temp.append( now_temp )
  stock_temp = stock_temp[-size_list:]

 

  heat( power_on )
  save_log( now_temp, power_on )


if __name__=='__main__':
  init()
  while True:

    #スタートファイルが存在していれば過熱開始
    if file.is_start():
      main()

    #スタートファイルが無ければスタンバイ
    else:
      time.sleep( 2 )
      file.save_row_4('standby...')
      file_log = None

 

<全プログラム内部構造一覧>

dreamerdream.hateblo.jp

 

RaspberryPiと炊飯器で低温調理器を自作してみた⑧ 「タイマーデーモン」

設定温度に達したらタイマーをスタート、カウントアップさせてディスプレイに表示させる。

f:id:DreamerDream:20190918164155p:plain

タイマーが設定時間を超えたらスタートファイルを削除する。

スタートファイルが削除されるので、ヒーターデーモンはヒーターの過熱を停止する。

 

<timerd.py>

 

#!/usr/bin/python
#coding:utf-8

import file_mng as file
import datetime
import time

tim_start = False

start_time = None

set_temp = 0.0
now_temp = 0.0

 

#桁「00」表示に揃える

def keta( num ):
  res = '0' + num
  return res[-2:]

#秒を「00:00:00」時間表示に変換

def get_view_time( sec ):
  sec = int( sec )
  hour = keta( str( sec / 3600 ) )
  min = keta( str( ( sec % 3600 ) / 60 ) )
  sec = keta( str( sec % 60 ) )
  return hour + ':' + min + ':' + sec

 

def main():
  global tim_start, start_time, set_temp, now_temp

  #スタートファイルがある場合
  if file.is_start():

    if tim_start:
      if start_time == None:
        str_time = file.load_start_time()
        start_time = datetime.datetime.strptime(str_time, "%Y/%m/%d %H:%M:%S")

       #現時刻とスタート時の時刻の差分を取得
        now = datetime.datetime.now()
        delta = now - start_time
        sec = delta.total_seconds()
        file.save_time( get_view_time( sec ) )
        set_time = float( file.get_time_set() )

        #設定時間をタイマーが超えたらスタートファイルを削除して終了
        if set_time <= sec/60 :
          file.save_row_0('Complete')
          file.stop()

    else:
      r_now_temp = file.load_temp()
      r_set_temp = file.get_temp_set()
      try:
        r_now_temp = float( r_now_temp )
        r_set_temp = float( r_set_temp )
        now_temp = r_now_temp
        set_temp = r_set_temp
      except:
        pass

      #設定温度に達したらスターとファイルを現日時に置き換えてタイマースタート
      if set_temp <= now_temp:
        tim_start = True
        now = datetime.datetime.now()
        now_str = now.strftime("%Y/%m/%d %H:%M:%S")
        file.start( now_str )
        file.save_row_0(now_str)

 

  #スタートファイルが無い場合
  else:
    tim_start = False
    start_time = None


if __name__=='__main__':
  while True:
    main()
    time.sleep(0.5)

<全プログラム内部構造一覧>

dreamerdream.hateblo.jp

 

RaspberryPiと炊飯器で低温調理器を自作してみた⑦ 「温度、タイマー設定デーモン」

設定ボタンによって温度とタイマーの設定を行なうデーモン

f:id:DreamerDream:20190918164113p:plain

物理ボタン操作で温度とタイマーの設定を変更するためのデーモン。

少しチャタリング対策に難があり、あまりスムーズに動かないorz。まあ、とりあえず動く。

このプログラムが無くても直書きでシステムの動作は可能。
  

<setting.py>

 

#!/usr/bin/python
#coding:utf-8

import RPi.GPIO as GPIO
import time
import os
import datetime
import file_mng as file

Sw_up = 27
Sw_dn = 22
Sw_select = 23

select = 'temp' # temp or time

 

#桁表示を「00」表示に整えるメソッド
def keta( num ):
  sum = '0' + num
  return sum[-2:]

 

#分を時間に直すメソッド

def review_time( tim ):
  tim = int( tim )
  hour = keta( str( tim / 60 ) )
  min = keta( str( tim % 60 ) )
  return hour + ':' + min + ':00'

 

#設定された項目をディスプレイに表示する

def view():
  file.save_row_1( ('> ' if select=='temp' else ' ') + 'SET TEMP: ' + file.get_temp_set() )
  file.save_row_2( ('> ' if select=='time' else ' ') + 'SET TIME: ' + review_time( file.get_time_set() ) )

 

#設定項目が温度の場合

def push_temp( val ):
  file.save_temp_set( str( int( file.get_temp_set() ) + val ) )

 

#設定項目が時間の場合

def push_time( val ):
  file.save_time_set( str( int( file.get_time_set() ) + val ) )

 

#「時間」「温度」切り換えボタン操作の場合

def change_select():
  global select
  if select == 'temp':
    select = 'time'
  else:
    select = 'temp' 

 

#スイッチが押された場合の処理

def switch(gpio_pin):

  time.sleep(0.5)

  if GPIO.input( gpio_pin ) == 0:

    if gpio_pin == Sw_up:
      if select == 'temp':
        push_temp( 5 )
      elif select == 'time':
        push_time( 10 )
    if gpio_pin == Sw_dn:
      if select == 'temp':
        push_temp( -5 )
      elif select == 'time':
        push_time( -10 )

    if gpio_pin == Sw_select:
      change_select()

    view()
    while GPIO.input( gpio_pin ) == 0:
      time.sleep( 0.1 )

 

#スイッチピンの設定プルアップで0V入力時にスイッチ操作
def standby():
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(Sw_up, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  GPIO.add_event_detect(Sw_up, GPIO.FALLING)
  GPIO.setup(Sw_dn, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  GPIO.add_event_detect(Sw_dn, GPIO.FALLING)
  GPIO.setup(Sw_select, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  GPIO.add_event_detect(Sw_select, GPIO.FALLING)
  GPIO.add_event_callback(Sw_up, switch)
  GPIO.add_event_callback(Sw_dn, switch)
  GPIO.add_event_callback(Sw_select, switch)

  view()

  while True:
    time.sleep(60)

 

if __name__=='__main__':
  standby() 

<全プログラム内部構造一覧>

dreamerdream.hateblo.jp

 

RaspberryPiと炊飯器で低温調理器を自作してみた⑥ 「スタート/ストップデーモン」

スタートボタンが押されたら/dev/shm/領域にスタートファイルを生成、ファイルが存在している場合はスタートファイルを削除するデーモン

 

f:id:DreamerDream:20190918164030p:plain

スタートファイルに日時を保存する。

このスタートファイルが存在することを確認したら、ヒーター制御デーモンが指定温度になるようヒーターを制御します。

スタートファイルは、このデーモンの他にタイマーデーモンからも参照、追記、削除されます。

 

<start_stop_btnd.py>

 

#!/usr/bin/python
#coding:utf-8

import RPi.GPIO as GPIO
import time
import os
import datetime
import file_mng as file

Switch = 10

 

#スイッチが押されたときの処理

def switch(gpio_pin):
  time.sleep(0.2)
  if GPIO.input(Switch) == 0:
    time.sleep(0.2)

    #既にスタートファイルがあるときは
    if file.is_start():

      #スタートファイルを消す
      file.stop()
      file.save_row_0('-- STOP --')

    #スタートファイルが無い場合は
    else:
      now = datetime.datetime.now()

      #現在日時をスタートファイルに書き込む
      file.start( now.strftime("%Y/%m/%d %H:%M:%S") )
      file.save_row_0('-- START --')

  while GPIO.input(Switch) == 0:
  time.sleep( 1 )

 

#スタートスイッチをプルアップしておいて、プッシュ時(ピン電圧0V時)にswitchメソッドを読むよう設定

def standby():
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(Switch, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  GPIO.add_event_detect(Switch, GPIO.FALLING)
  GPIO.add_event_callback(Switch, switch)

  while True:
    time.sleep(60)


if __name__=='__main__':
  standby()

 

<全プログラム内部構造一覧>

dreamerdream.hateblo.jp

 

RaspberryPiと炊飯器で低温調理器を自作してみた⑤ 「温度取得デーモン」

単純に、センサモジュールから取得出来る値から℃を抽出して共用ファイルに保存するだけのデーモン。

f:id:DreamerDream:20190918163948p:plain

本来、必用なプログラムが各々センサ値を読めば良いのですが、読み出しに時間が少しかかるため、温度取得専用のプログラムから共用ファイルに書き出しおくとスムーズなので今回はこのようにイチイチ書き出すようにしました。

 

<write_tempd.py>

 

#!/usr/bin/python
#coding:utf-8

import file_mng as file
import time

#温度計のセーブファイル

file_temp = '/sys/bus/w1/devices/28-000007248aac/w1_slave'

 

#センサモジュールの値から℃を抽出する
def load_temp():
  data = file.load( file_temp )
  try:
    return str( float( data.split('t=')[1] ) / 1000 )
  except:
    return '-------'

 

#/dev/shm/tempに温度を保存する

def make_temp():
  temp = load_temp()
  file.save_temp( temp )

 

if __name__=='__main__':
  while True:
    make_temp()
    time.sleep(0.5)

 

<全プログラム内部構造一覧>

dreamerdream.hateblo.jp

 

RaspberryPiと炊飯器で低温調理器を自作してみた④ 「画面表示デーモン」

ディスプレイの表示用プログラムです。

画面は今回たまたま手元にあった2色のカラーOLEDディスプレイをを利用しただけで、表示内容をファイルを読んで変えられるようにしただけで表示方法はほぼ以前に書いた記事のまま特に手を加えていません。↓

dreamerdream.hateblo.jp

 

このプログラムが無くてもシステムは問題無く動きますがLED表示とかより画面があった方が機械としてカッコイイので付けることにしました。

 

f:id:DreamerDream:20190918163901p:plain

/dev/shm/領域からデータを読み出してそのまま表示しています。

デーモンで0.5秒ごとに動かしています。

<temp_displayd.py>

 

#!/usr/bin/python
#coding:utf-8

import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

import time

import file_mng as file

 

#トップの文字(青字領域の高さ)
top_y = 14

#タイトル文字(黄色領域の高さ)
title_y = 0

#各行の移動距離
str_size = 10

#文字のX軸のサイズ
str_size_x = 6
# Raspberry Pi pin configuration
RST = 24
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)

text_title='-- Teion Choriki --'

text_main_top='00:00:00 | Tmp:--.--'

 

# Initialize library.
disp.begin()

# Clear display.
disp.clear()
disp.display()

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# Draw a black filled box to clear the image.
draw.rectangle((0,0,width,height), outline=0, fill=0)

 

#全領域の表示を消す

def clear_all():
  draw.rectangle((0, 0,width, height ), outline=0, fill=0)

#トップ(黄色領域)の表示を消す

def clear_top():
  draw.rectangle((0, 0,width, str_size ), outline=0, fill=0)

#指定行(青文字領域)の表示を消す

def clear_row( row ):
  row = top_y + ( str_size * row )
  draw.rectangle((0, row ,width, row + str_size ), outline=0, fill=0)

#指定行(青文字領域)の表示

def write_row( row ,text ):
  clear_row( row )
  draw.text(( 0 , ( top_y + row * str_size ) ), text, fill=256 )

#トップ(黄色文字領域)の表示

def write_top(text):
  draw.text((0,title_y), text, fill=256)

#実際のディスプレイ描画

def view():
  disp.image(image)
  disp.display()

#立ち上げ時にタイトルを徐々に上に移動させて消すモーション

def remove_title():
  for i in range( str_size ):
    clear_top()
    draw.text( ( 0, -i ), text_title , fill=256)
    view()

#時間表示の部分だけ消して書き換え

def write_time( tim_str ):
  if 0 < len( tim_str ):
    draw.rectangle( ( 0 , title_y , (str_size_x * len(tim_str) ), ( title_y + str_size ) ) , outline=0, fill=0 )
    draw.text(( 0 , title_y ), tim_str , fill=256 )

#温度表示の部分だけ消して書き換え

def write_temp( temp_str ):
  if 0 < len(temp_str):
    draw.rectangle( ( str_size_x * 15 , title_y , ( str_size_x * 15 * len(temp_str) ), ( title_y + str_size ) ) , outline=0, fill=0 )
    draw.text(( str_size_x * 15 , title_y ), temp_str , fill=256 )

 

 

def main():

 #タイトルを表示して
  write_top( text_title )
  view()

  time.sleep(3)

#タイトルをスクロールで消す
  remove_title()

#タイトルを時間と温度表示に書き換えて
  write_top( text_main_top )
  view()

#以降、ファイルを読んで書き換えることを繰り返す

  while True:
    write_time( file.load_time() ) 
    write_temp( file.load_temp() )

    write_row(0, file.load_row_0() )
    write_row(1, file.load_row_1() )
    write_row(2, file.load_row_2() )
    write_row(3, file.load_row_3() )
    write_row(4, file.load_row_4() )

    view()
    time.sleep(0.5)


if __name__=='__main__':
main()

<全プログラム内部構造一覧>

dreamerdream.hateblo.jp

 

RaspberryPiと炊飯器で低温調理器を自作してみた③ 「ファイル統括モジュール」

前回、各種ファイルの説明をしました。

今回はプログラムの中身です。

 

f:id:DreamerDream:20190918163549p:plain

 

<file_mng.py>

これは全てのプログラムから共用するファイルデータを扱います。

(※get、loadが混在している等、突っ込みは無しでお願いします)

 

# -*- coding: utf-8 -*-

import os

dir_shm = '/dev/shm/'

#現在温度
file_temp = dir_shm + 'temp'

#画面表示用タイム

file_time = dir_shm + 'time'

#画面表示_row=行
file_row_0 = dir_shm + 'row_0'
file_row_1 = dir_shm + 'row_1'
file_row_2 = dir_shm + 'row_2'
file_row_3 = dir_shm + 'row_3'
file_row_4 = dir_shm + 'row_4'

#スタートファイル

file_start = dir_shm + 'start'

 

dir_etc = '/usr/local/etc/'

#温度設定ファイル
file_set_temp = dir_etc + 'temp'

#時間設定ファイル
file_set_time = dir_etc + 'time'

 

#ログファイル

dir_log = '/home/machinelog/'

 

def save( filename, text ):
  f = open( filename,'w')
  f.write(text)
  f.close()

 

def load( filename ):
  s = ''
  try:
    f = open( filename, 'r' )
    s = f.read()
    f.close()
    s = s.strip()
  except:
    pass
  return s

 

def remove( filename ):
  os.remove( filename )

 

def is_file( filename ):
  return os.path.isfile( filename )

 

def log( filename, now_temp, power_on ):
  text = str(now_temp) + ',' + str(power_on) + '\n'
  f = open( dir_log + filename, 'a' )
  f.write( text )
  f.close()

 

#温度設定読み出し

def get_temp_set():
  tm = load( file_set_temp )
  if tm == '':
    return '60' #default 60d
  return tm 

#温度設定保存

def save_temp_set( tm ):
  save( file_set_temp, tm )

 

#時間設定読み出し

def get_time_set():
  tm = load( file_set_time )
  if tm == '':
    return '60' # default 60 min
  return tm

#時間設定保存

def save_time_set( tm ):
  save( file_set_time, tm )
 

#スタートファイルの有無

def is_start():
  return is_file( file_start )

#スタートファイル生成

def start( tim ):
  save( file_start, tim )

#ストップ(スタートファイル削除)

def stop():
  remove( file_start )

#スタート時間読み出し

def load_start_time():
  return load( file_start )

 

#現在温度読み出し

def load_temp():
  return load( file_temp )

#現在温度保存
def save_temp( text ):
  save( file_temp , text )

#表示時間読み出し
def load_time():
  return load( file_time )

#表示時間保存
def save_time( text ):
  save( file_time , text )

 

#以下、画面行ごとの表示と保存

def load_row_0():
  return load( file_row_0 )
def save_row_0( text ):
  save( file_row_0 , text)

def load_row_1():
  return load( file_row_1 )
def save_row_1( text ):
  save( file_row_1 , text)

def load_row_2():
  return load( file_row_2 )
def save_row_2( text ):
  save( file_row_2 , text)

def load_row_3():
   return load( file_row_3 )
def save_row_3( text ):
  save( file_row_3 , text)

def load_row_4():

  return load( file_row_4 )
def save_row_4( text ):
  save( file_row_4 , text)

 

 

<全プログラム内部構造一覧>

dreamerdream.hateblo.jp

 

kampa.me