Skip to content

Commit 574fb58

Browse files
Merge branch 'main' into ph-device
2 parents 772bcf6 + 0d1e025 commit 574fb58

25 files changed

Lines changed: 1103 additions & 123 deletions

src/devices/eeprom.py

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,10 @@ def __init__(self):
1313
The constructor function for the EEPROM class
1414
"""
1515
self._google_sheet_interval = 20
16-
self._heat = bool(True)
17-
self._ignore_bad_ph_slope = False
1816
self._kd_value = 36.0
1917
self._ki_value = 28.0
2018
self._kp_value = 20.0
2119
self._tank_id = 1
22-
self._thermal_correction = 12
2320

2421
def get_google_sheet_interval(self, default):
2522
"""
@@ -29,22 +26,6 @@ def get_google_sheet_interval(self, default):
2926
return default
3027
return self._google_sheet_interval
3128

32-
def get_heat(self, default):
33-
"""
34-
Get the heat setting from EEPROM
35-
"""
36-
if self._heat is None:
37-
return default
38-
return self._heat
39-
40-
def get_ignore_bad_ph_slope(self, default):
41-
"""
42-
Get the ignore bad pH slope setting from EEPROM
43-
"""
44-
if self._ignore_bad_ph_slope is None:
45-
return default
46-
return self._ignore_bad_ph_slope
47-
4829
def get_kd(self, default):
4930
"""
5031
Get the Kd value from EEPROM
@@ -77,32 +58,12 @@ def get_tank_id(self, default):
7758
return default
7859
return self._tank_id
7960

80-
def get_thermal_correction(self, default):
81-
"""
82-
Get the thermal correction value from EEPROM
83-
"""
84-
if self._thermal_correction is None:
85-
return default
86-
return float(self._thermal_correction)
87-
8861
def set_google_sheet_interval(self, value):
8962
"""
9063
Set the google sheet interval in EEPROM
9164
"""
9265
self._google_sheet_interval = value
9366

94-
def set_heat(self, value):
95-
"""
96-
Set the heat setting in EEPROM
97-
"""
98-
self._heat = value
99-
100-
def set_ignore_bad_ph_slope(self, value):
101-
"""
102-
Set the ignore bad pH slope setting in EEPROM
103-
"""
104-
self._ignore_bad_ph_slope = value
105-
10667
def set_kd(self, value):
10768
"""
10869
Set the Kd value in EEPROM

src/devices/thermal_control.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
"""
2+
A file for the ThermalControl class
3+
"""
4+
5+
import time
6+
7+
8+
class ThermalControl:
9+
"""
10+
The class for the ThermalControl
11+
"""
12+
13+
FLAT_TYPE = 0
14+
RAMP_TYPE = 1
15+
SINE_TYPE = 2
16+
17+
def __init__(self, titrator):
18+
"""
19+
The constructor function for the ThermalControl class
20+
"""
21+
self.titrator = titrator
22+
self._heat = bool(True)
23+
self._base_thermal_target = 78
24+
self._current_thermal_target = 67
25+
self._thermal_function_type = ThermalControl.FLAT_TYPE
26+
self._ramp_time_start_seconds = 0
27+
self._ramp_time_end_seconds = 0
28+
self._ramp_initial_value = 0.0
29+
self._amplitude = 0.0
30+
self._period_in_seconds = 0
31+
32+
def get_amplitude(self):
33+
"""
34+
Get the amplitude for the pH function.
35+
"""
36+
return self._amplitude
37+
38+
def get_base_thermal_target(self):
39+
"""
40+
Get the base thermal target
41+
"""
42+
return self._base_thermal_target
43+
44+
def get_current_thermal_target(self):
45+
"""
46+
Get the current thermal target
47+
"""
48+
return self._current_thermal_target
49+
50+
def get_heat(self, default):
51+
"""
52+
Get the heat setting from EEPROM
53+
"""
54+
if self._heat is None:
55+
return default
56+
return self._heat
57+
58+
def get_period_in_seconds(self):
59+
"""
60+
Get the period in seconds for the pH function.
61+
"""
62+
return self._period_in_seconds
63+
64+
def get_ramp_time_end(self):
65+
"""
66+
Get the ramp time end in seconds.
67+
"""
68+
return (
69+
self._ramp_time_end_seconds
70+
if self._thermal_function_type != ThermalControl.FLAT_TYPE
71+
else 0
72+
)
73+
74+
def get_ramp_time_start(self):
75+
"""
76+
Get the ramp time start in seconds.
77+
"""
78+
return (
79+
self._ramp_time_start_seconds
80+
if self._thermal_function_type != ThermalControl.FLAT_TYPE
81+
else 0
82+
)
83+
84+
def get_thermal_function_type(self):
85+
"""
86+
Get the current thermal function type.
87+
"""
88+
return self._thermal_function_type
89+
90+
def set_amplitude(self, amplitude):
91+
"""
92+
Set the amplitude for the pH function.
93+
"""
94+
self._amplitude = amplitude
95+
96+
def set_base_thermal_target(self, value):
97+
"""
98+
Set the base thermal target
99+
"""
100+
self._base_thermal_target = value
101+
102+
def set_current_thermal_target(self, value):
103+
"""
104+
Set the current thermal target
105+
"""
106+
self._current_thermal_target = value
107+
108+
def set_heat(self, value):
109+
"""
110+
Set the heat setting in EEPROM
111+
"""
112+
self._heat = value
113+
114+
def set_ramp_duration_hours(self, new_ph_ramp_duration):
115+
"""
116+
Set the ramp duration in hours. If the duration is greater than 0, configure ramp parameters;
117+
otherwise, set the function type to FLAT_TYPE.
118+
"""
119+
if new_ph_ramp_duration > 0:
120+
current_ramp_time = (
121+
self._ramp_time_end_seconds - self._ramp_time_start_seconds
122+
)
123+
current_ramp_time_str = f"{current_ramp_time:.3f}"
124+
new_ramp_duration_str = f"{new_ph_ramp_duration:.3f}"
125+
print(
126+
f"Change ramp time from {current_ramp_time_str} to {new_ramp_duration_str}"
127+
)
128+
129+
self._ramp_time_start_seconds = int(time.monotonic())
130+
self._ramp_time_end_seconds = self._ramp_time_start_seconds + int(
131+
new_ph_ramp_duration * 3600
132+
)
133+
134+
self._ramp_initial_value = self.titrator.thermal_probe.get_running_average()
135+
self._thermal_function_type = ThermalControl.RAMP_TYPE
136+
else:
137+
self._ramp_time_end_seconds = 0
138+
self._thermal_function_type = ThermalControl.FLAT_TYPE
139+
print("Set ramp time to 0")
140+
141+
def set_sine_amplitude_and_hours(self, amplitude, period_in_hours):
142+
"""
143+
Set the amplitude and period (in hours) for the sine wave pH function.
144+
"""
145+
if amplitude > 0 and period_in_hours > 0:
146+
self._amplitude = amplitude
147+
self._period_in_seconds = int(period_in_hours * 3600)
148+
self._thermal_function_type = ThermalControl.SINE_TYPE
149+
else:
150+
raise ValueError("Amp and period !> than 0.")
151+
152+
def set_thermal_function_type(self, function_type):
153+
"""
154+
Set the current thermal function type.
155+
"""
156+
if function_type in (
157+
ThermalControl.FLAT_TYPE,
158+
ThermalControl.RAMP_TYPE,
159+
ThermalControl.SINE_TYPE,
160+
):
161+
self._thermal_function_type = function_type
162+
else:
163+
raise ValueError("Invalid thermal function type")

src/devices/thermal_probe.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,85 @@
22
The file for the ThermalProbe class
33
"""
44

5+
import time
6+
57

68
class ThermalProbe:
79
"""
810
The class for the ThermalProbe
911
"""
1012

13+
HISTORY_SIZE = 10
14+
1115
def __init__(self, eeprom):
1216
"""
1317
The constructor function for the ThermalProbe class
1418
"""
1519
self.eeprom = eeprom
1620

17-
self.correction = self.eeprom.get_thermal_correction(0.0)
21+
self._correction = 12
22+
23+
self.history = [0.0] * self.HISTORY_SIZE
24+
self.history_index = 0
25+
self.first_time = True
26+
self.last_time = 0
27+
28+
def clear_thermal_correction(self):
29+
"""
30+
Clear the thermal correction value in EEPROM
31+
"""
32+
self._correction = 0
33+
34+
def get_raw_temperature(self):
35+
"""
36+
Simulate reading the raw temperature from a sensor.
37+
In a real implementation, this method would interface with hardware.
38+
"""
39+
# Placeholder for actual sensor reading logic
40+
return 25.0 # return thermo.temperature(RTDnominal, refResistor);
41+
42+
def get_running_average(self):
43+
"""
44+
Return the corrected running average within the range of 00.00-99.99
45+
"""
46+
temperature = self.get_uncorrected_running_average() + self._correction
47+
if temperature < 0.0:
48+
temperature = 0.0
49+
elif temperature > 99.99:
50+
temperature = 99.99
51+
return temperature
52+
53+
def get_thermal_correction(self):
54+
"""
55+
Get the thermal correction value from EEPROM
56+
"""
57+
return float(self._correction)
58+
59+
def get_uncorrected_running_average(self):
60+
"""
61+
Calculate the uncorrected running average of temperature readings.
62+
"""
63+
current_time = time.time()
64+
if (
65+
self.first_time or self.last_time + 1 <= current_time
66+
): # Check if 1 second has passed
67+
temperature = self.get_raw_temperature()
68+
if self.first_time:
69+
# Initialize the history buffer with the first temperature reading
70+
self.history = [temperature] * self.HISTORY_SIZE
71+
self.first_time = False
72+
73+
# Update the history buffer with the new temperature reading
74+
self.history_index = (self.history_index + 1) % self.HISTORY_SIZE
75+
self.history[self.history_index] = temperature
76+
self.last_time = current_time
77+
78+
# Calculate the average of the history buffer, ignoring unused slots
79+
valid_readings = self.history[: self.history_index + 1]
80+
return sum(valid_readings) / len(valid_readings)
81+
82+
def set_thermal_correction(self, value):
83+
"""
84+
Set the thermal correction value in EEPROM
85+
"""
86+
self._correction = value

src/titrator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from src.devices.ph_probe_mock import PHProbe
2020
from src.devices.pid import PID
2121
from src.devices.sd import SD
22+
from src.devices.thermal_control import ThermalControl
2223
from src.devices.thermal_probe import ThermalProbe
2324
from src.ui_state.main_menu import MainMenu
2425
from src.version import VERSION
@@ -51,6 +52,9 @@ def __init__(self):
5152
# Initialize PH Control
5253
self.ph_control = PHControl(self)
5354

55+
# Initialize Thermal Control
56+
self.thermal_control = ThermalControl(self)
57+
5458
# Initialize Thermal Probe
5559
self.thermal_probe = ThermalProbe(self.eeprom)
5660

src/ui_state/main_menu.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from src.ui_state.view_menu.view_ph_calibration import ViewPHCalibration
3636
from src.ui_state.view_menu.view_pid_constants import ViewPIDConstants
3737
from src.ui_state.view_menu.view_tank_id import ViewTankID
38+
from src.ui_state.view_menu.view_thermal import ViewThermal
3839
from src.ui_state.view_menu.view_thermal_correction import (
3940
ViewThermalCorrection,
4041
)
@@ -64,6 +65,7 @@ def __init__(self, titrator):
6465
"View pH slope",
6566
"View PID",
6667
"View tank ID",
68+
"View temp",
6769
"View temp cal",
6870
"View time",
6971
"View version",
@@ -99,6 +101,7 @@ def __init__(self, titrator):
99101
ViewPHCalibration, # View pH slope
100102
ViewPIDConstants, # View PID constants
101103
ViewTankID, # View Tank ID
104+
ViewThermal, # View Temperature
102105
ViewThermalCorrection, # View Thermal Correction
103106
ViewTime, # View Time
104107
ViewVersion, # View Version
@@ -259,6 +262,29 @@ def idle(self):
259262
self.titrator.lcd.print("".join(output), line=1)
260263

261264
self.titrator.lcd.print("", line=2)
265+
lcd = self.titrator.lcd
266+
lcd.print("Idle Line 1", line=1)
267+
268+
thermal_control = self.titrator.thermal_control
269+
temperature = self.titrator.thermal_probe.get_running_average()
270+
status = "h" if thermal_control.get_heat(True) else "c"
271+
272+
output = [" "] * 20
273+
output[0] = "T"
274+
output[1] = "=" if int(time.monotonic()) % 2 == 0 else " "
275+
276+
buffer = f"{temperature:5.2f}"
277+
output[2:7] = list(buffer[:5])
278+
279+
output[7] = " "
280+
output[8] = status
281+
output[9] = " "
282+
283+
thermal_target = thermal_control.get_current_thermal_target()
284+
buffer = f"{thermal_target:5.2f}"
285+
output[10:15] = list(buffer[:5])
286+
287+
self.titrator.lcd.print("".join(output), line=2)
262288

263289
def loop(self):
264290
"""

0 commit comments

Comments
 (0)