├── README.md ├── niku-circuit.png ├── niku-impl.png ├── niku-rp2.fzz ├── niku.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | # niku.py - PID Controller for sous vide 2 | 3 | ## Hardware 4 | 5 | ![circuit](niku-circuit.png) 6 | ![impl](niku-impl.png) 7 | 8 | ## Usage 9 | 10 | $ niku 60 11 | -------------------------------------------------------------------------------- /niku-circuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamano/python-niku/52f47c7608aecb5365707572be5370439dd75e7b/niku-circuit.png -------------------------------------------------------------------------------- /niku-impl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamano/python-niku/52f47c7608aecb5365707572be5370439dd75e7b/niku-impl.png -------------------------------------------------------------------------------- /niku-rp2.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamano/python-niku/52f47c7608aecb5365707572be5370439dd75e7b/niku-rp2.fzz -------------------------------------------------------------------------------- /niku.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import glob 6 | import RPi.GPIO as GPIO 7 | 8 | devices = glob.glob('/sys/bus/w1/devices/*/w1_slave') 9 | if len(devices) == 0: 10 | raise Exception("DS18B20 not found.") 11 | elif len(devices) > 1: 12 | raise Exception("multiple 1-wire devices found.") 13 | 14 | DEVICE_FILE = devices[0] 15 | GPIO_RELAY = 21 16 | DEBUG = False 17 | 18 | 19 | def read_temp(): 20 | file = open(DEVICE_FILE) 21 | lines = file.readlines() 22 | temp = lines[1].split('t=')[1] 23 | return float(temp) / 1000 24 | 25 | 26 | def output(power): 27 | if power > 1: 28 | power = 1 29 | on = power * 10 30 | off = (1 - power) * 10 31 | if on > 0: 32 | GPIO.output(GPIO_RELAY, GPIO.HIGH) 33 | time.sleep(on) 34 | if off > 0: 35 | GPIO.output(GPIO_RELAY, GPIO.LOW) 36 | time.sleep(off) 37 | 38 | 39 | def p(temp, target, kp): 40 | d = target - temp 41 | if d < 0: 42 | return 0 43 | power = d / target * kp 44 | return power 45 | 46 | 47 | def i(hist, target, ki): 48 | s = 0 49 | for i in range(len(hist)-1): 50 | d1 = target - hist[i] 51 | d2 = target - hist[i+1] 52 | s += (d1 + d2) / 2 * ki 53 | return s 54 | 55 | 56 | def main(): 57 | if len(sys.argv) < 2: 58 | print('usafge: %s TARGET_TEMPERTURE' % (sys.argv[0])) 59 | sys.exit(0) 60 | target = float(sys.argv[1]) 61 | GPIO.setwarnings(False) 62 | GPIO.setmode(GPIO.BCM) 63 | GPIO.setup(GPIO_RELAY, GPIO.OUT) 64 | hist = [] 65 | prev = 0 66 | t = 0 67 | while True: 68 | try: 69 | temp = read_temp() 70 | except Exception as e: 71 | print(e) 72 | time.sleep(1) 73 | continue 74 | hist.insert(0, temp) 75 | if len(hist) > 7: 76 | hist.pop() 77 | pg = p(temp, target, 3) 78 | ig = i(hist, target, 0.01) 79 | if DEBUG: 80 | print("pg: %f" % (pg)) 81 | print("ig: %f" % (ig)) 82 | print(hist) 83 | power = pg + ig 84 | if power > 0: 85 | power += 0.1 86 | prev = temp 87 | print t, temp, power 88 | sys.stdout.flush() 89 | output(power) 90 | t += 10 91 | 92 | 93 | if __name__ == "__main__": 94 | main() 95 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from setuptools import setup 5 | 6 | setup( 7 | name='niku', 8 | version='2.0.1', 9 | description="PID Controller for sous vide", 10 | url="http://www.cuspy.org/diary/raspberrypi-roastbeef/", 11 | author = "Tsukasa Hamano", 12 | author_email = "code@cuspy.org", 13 | entry_points=""" 14 | [console_scripts] 15 | niku = niku:main 16 | """, 17 | py_modules=['niku'], 18 | license="BSD", 19 | ) 20 | --------------------------------------------------------------------------------