-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcontroller.py
More file actions
172 lines (145 loc) · 4.41 KB
/
Copy pathcontroller.py
File metadata and controls
172 lines (145 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from settings import Settings
from temp_sensor import TempSensor
from relay import PWMRelay
from program import Program
from simple_pid import PID
from logger import Logger
from current_clamp import CT
import time
log = Logger(__name__)
try:
from machine import WDT
except ImportError:
class WDT:
def __init__(self, timeout):
pass
def feed(self):
pass
class Controller:
duty = 0.0
temp = 0.0
setpoint = 0.0
running = False
cycle_start = 0.0
program = None
paused = False
err_count = 0
def __init__(self):
self.settings = Settings()
self.wdt = WDT(timeout=int(1 + self.settings.controller.Period) * 1000 * 3)
self.pid = PID(
self.settings.controller.Kp,
self.settings.controller.Ki,
self.settings.controller.Kd,
setpoint=25.0,
sample_time=self.settings.controller.Period,
proportional_on_measurement=self.settings.controller.PoM,
)
self.pid.output_limits = (0, 1.0)
self.temp_sensor = TempSensor()
self.relay = PWMRelay()
self.set_program("default.json")
self.ct = CT(
self.settings.pinout.CT,
rating=self.settings.ct.CTRating,
sample_rate=self.settings.ct.CTSampleRate,
sample_count=self.settings.ct.CTSampleCount,
)
self.ct.calibrate()
self.ct.start()
def reset(self):
self.stop()
self.pid = PID(
self.settings.controller.Kp,
self.settings.controller.Ki,
self.settings.controller.Kd,
setpoint=25.0,
sample_time=self.settings.controller.Period,
proportional_on_measurement=self.settings.controller.PoM,
)
self.pid.output_limits = (0, 1.0)
def get_setpoint(self):
if self.program:
setpoint = self.program.get_setpoint(self.runtime())
else:
setpoint = self.setpoint
if setpoint is None:
self.stop()
else:
return setpoint
def runtime(self):
if not self.running:
return 0.0
if self.paused:
return self.paused_time - self.cycle_start
return time.time() - self.cycle_start
def set_program(self, name: str = None):
if name is None:
self.program = None
return
try:
self.program = Program(name)
except Exception as e:
log.error(f"Error loading {name} program: {e}")
self.program = None
def start(self):
if self.running or self.paused:
return
self.relay.start()
self.pid.set_auto_mode(True, last_output=self.duty)
self.cycle_start = time.time()
self.running = True
def pause(self):
if self.paused or not self.running:
return
self.paused = True
self.paused_time = time.time()
def resume(self):
if not self.paused or not self.running:
return
self.cycle_start += time.time() - self.paused_time
self.paused_time = 0
self.paused = False
def stop(self):
if not self.running:
return
self.running = False
self.setpoint = 0.0
self.duty = 0.0
self.paused = False
self.paused_time = 0
self.relay.stop()
self.pid.set_auto_mode(False)
def loop(self):
self.temp = self.temp_sensor.read()
self.wdt.feed()
if not self.running:
return
if self.err_count > 5:
log.error("Too many temp sensor errors")
self.err_count = 0
self.pause()
return
if self.temp is None:
log.error("Temperature sensor not connected")
self.err_count += 1
return
self.err_count = 0
self.setpoint = self.get_setpoint()
self.pid.setpoint = self.setpoint
self.duty = self.pid(self.temp)
self.relay.set_duty(self.duty)
def info(self):
p, i, d = self.pid.components
return {
"temp": self.temp,
"duty": self.duty,
"target": self.setpoint,
"running": self.running,
"runtime": self.runtime(),
"paused": self.paused,
"current": self.ct.current,
"p": p,
"i": i,
"d": d,
}