GUI #70
18
README.md
18
README.md
|
@ -67,7 +67,21 @@ On a Windows machine install [st.com](https://st.com) DfuSe USB device firmware
|
||||||
openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg -c "program target/thumbv7em-none-eabihf/release/thermostat verify reset;exit"
|
openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg -c "program target/thumbv7em-none-eabihf/release/thermostat verify reset;exit"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Network
|
## GUI Usage
|
||||||
|
|
||||||
|
A GUI has been developed for easy configuration and plotting of key parameters.
|
||||||
|
|
||||||
|
The Python GUI program is located at pytec/tecQT.py
|
||||||
|
|
||||||
|
The GUI is developed based on the Python library pyqtgraph. The environment needed to run the GUI is configured automatically by running:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
nix develop
|
||||||
|
|||||||
|
```
|
||||||
|
|
||||||
|
The GUI program assumes the default IP and port of 192.168.1.26 23 is used. If a different IP or port is used, the IP and port setting should be changed in the GUI code.
|
||||||
|
|
||||||
|
## Command Line Usage
|
||||||
|
|
||||||
### Connecting
|
### Connecting
|
||||||
|
|
||||||
|
@ -178,7 +192,7 @@ postfilter rate can be tuned with the `postfilter` command.
|
||||||
- Connect TEC module device 1 to TEC1- and TEC1+.
|
- Connect TEC module device 1 to TEC1- and TEC1+.
|
||||||
- The GND pin is for shielding not for sinking TEC module currents.
|
- The GND pin is for shielding not for sinking TEC module currents.
|
||||||
|
|
||||||
When using a TEC module with the Thermostat, the Thermostat expects the thermal load (where the thermistor is located) to heat up with a positive software current set point, and cool down with a negative current set point.
|
When using a TEC module with the Thermostat, the Thermostat expects the thermal load (where the thermistor is located) to cool down with a positive software current set point, and heat up with a negative current set point.
|
||||||
|
|
||||||
Testing heat flow direction with a low set current is recommended before installation of the TEC module.
|
Testing heat flow direction with a low set current is recommended before installation of the TEC module.
|
||||||
|
|
||||||
|
|
|
@ -71,8 +71,13 @@
|
||||||
rustPlatform.rust.cargo
|
rustPlatform.rust.cargo
|
||||||
openocd dfu-util
|
openocd dfu-util
|
||||||
] ++ (with python3Packages; [
|
] ++ (with python3Packages; [
|
||||||
numpy matplotlib
|
numpy matplotlib pyqtgraph
|
||||||
]);
|
]);
|
||||||
|
shellHook=
|
||||||
|
''
|
||||||
|
export QT_PLUGIN_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.dev.qtPluginPrefix}
|
||||||
|
export QML2_IMPORT_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.dev.qtQmlPrefix}
|
||||||
sb10q
commented
Better solution: package tecQT (making a separate flake output) and use wrapQtApp Better solution: package tecQT (making a separate flake output) and use wrapQtApp
|
|||||||
|
'';
|
||||||
};
|
};
|
||||||
defaultPackage.x86_64-linux = thermostat;
|
defaultPackage.x86_64-linux = thermostat;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@ class PIDAutotuneState(Enum):
|
||||||
STATE_RELAY_STEP_DOWN = 'relay step down'
|
STATE_RELAY_STEP_DOWN = 'relay step down'
|
||||||
STATE_SUCCEEDED = 'succeeded'
|
STATE_SUCCEEDED = 'succeeded'
|
||||||
STATE_FAILED = 'failed'
|
STATE_FAILED = 'failed'
|
||||||
|
STATE_READY = 'ready'
|
||||||
|
|
||||||
|
|
||||||
class PIDAutotune:
|
class PIDAutotune:
|
||||||
|
@ -56,6 +57,20 @@ class PIDAutotune:
|
||||||
self._Ku = 0
|
self._Ku = 0
|
||||||
self._Pu = 0
|
self._Pu = 0
|
||||||
|
|
||||||
|
def setParam(self, target, step, noiseband, sampletime, lookback):
|
||||||
|
self._setpoint = target
|
||||||
|
self._outputstep = step
|
||||||
|
self._out_max = step
|
||||||
|
self._out_min = -step
|
||||||
|
self._noiseband = noiseband
|
||||||
|
self._inputs = deque(maxlen=round(lookback / sampletime))
|
||||||
|
|
||||||
|
def setReady(self):
|
||||||
|
self._state = PIDAutotuneState.STATE_READY
|
||||||
|
|
||||||
|
def setOff(self):
|
||||||
|
self._state = PIDAutotuneState.STATE_OFF
|
||||||
|
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Get the current state."""
|
"""Get the current state."""
|
||||||
return self._state
|
return self._state
|
||||||
|
@ -81,6 +96,13 @@ class PIDAutotune:
|
||||||
kd = divisors[2] * self._Ku * self._Pu
|
kd = divisors[2] * self._Ku * self._Pu
|
||||||
return PIDAutotune.PIDParams(kp, ki, kd)
|
return PIDAutotune.PIDParams(kp, ki, kd)
|
||||||
|
|
||||||
|
def get_tec_pid (self):
|
||||||
|
divisors = self._tuning_rules["tyreus-luyben"]
|
||||||
|
kp = self._Ku * divisors[0]
|
||||||
|
ki = divisors[1] * self._Ku / self._Pu
|
||||||
|
kd = divisors[2] * self._Ku * self._Pu
|
||||||
|
return kp, ki, kd
|
||||||
|
|
||||||
def run(self, input_val, time_input):
|
def run(self, input_val, time_input):
|
||||||
"""To autotune a system, this method must be called periodically.
|
"""To autotune a system, this method must be called periodically.
|
||||||
|
|
||||||
|
@ -95,7 +117,8 @@ class PIDAutotune:
|
||||||
|
|
||||||
if (self._state == PIDAutotuneState.STATE_OFF
|
if (self._state == PIDAutotuneState.STATE_OFF
|
||||||
or self._state == PIDAutotuneState.STATE_SUCCEEDED
|
or self._state == PIDAutotuneState.STATE_SUCCEEDED
|
||||||
or self._state == PIDAutotuneState.STATE_FAILED):
|
or self._state == PIDAutotuneState.STATE_FAILED
|
||||||
|
or self._state == PIDAutotuneState.STATE_READY):
|
||||||
self._state = PIDAutotuneState.STATE_RELAY_STEP_UP
|
self._state = PIDAutotuneState.STATE_RELAY_STEP_UP
|
||||||
|
|
||||||
self._last_run_timestamp = now
|
self._last_run_timestamp = now
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Client:
|
||||||
return line
|
return line
|
||||||
|
|
||||||
def _command(self, *command):
|
def _command(self, *command):
|
||||||
self._socket.sendall((" ".join(command) + "\n").encode('utf-8'))
|
self._socket.sendall(((" ".join(command)).strip() + "\n").encode('utf-8'))
|
||||||
|
|
||||||
line = self._read_line()
|
line = self._read_line()
|
||||||
response = json.loads(line)
|
response = json.loads(line)
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
|
import pyqtgraph.parametertree.parameterTypes as pTypes
|
||||||
|
from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType
|
||||||
|
import numpy as np
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from pytec.client import Client
|
||||||
|
from enum import Enum
|
||||||
|
from autotune import PIDAutotune, PIDAutotuneState
|
||||||
|
|
||||||
|
rec_len = 1000
|
||||||
|
refresh_period = 20
|
||||||
|
|
||||||
|
TECparams = [ [
|
||||||
sb10q
commented
tec_params, PEP8 and consistency with the two variables above tec_params, PEP8 and consistency with the two variables above
|
|||||||
|
{'tag': 'report', 'type': 'parent', 'children': [
|
||||||
|
{'tag': 'pid_engaged', 'type': 'bool', 'value': False},
|
||||||
|
]},
|
||||||
|
{'tag': 'pwm', 'type': 'parent', 'children': [
|
||||||
|
{'tag': 'max_i_pos', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'max_i_neg', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'max_v', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'i_set', 'type': 'float', 'value': 0},
|
||||||
|
]},
|
||||||
|
{'tag': 'pid', 'type': 'parent', 'children': [
|
||||||
|
{'tag': 'kp', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'ki', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'kd', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'output_min', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'output_max', 'type': 'float', 'value': 0},
|
||||||
|
]},
|
||||||
|
{'tag': 's-h', 'type': 'parent', 'children': [
|
||||||
|
{'tag': 't0', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'r0', 'type': 'float', 'value': 0},
|
||||||
|
{'tag': 'b', 'type': 'float', 'value': 0},
|
||||||
|
]},
|
||||||
|
{'tag': 'PIDtarget', 'type': 'parent', 'children': [
|
||||||
|
{'tag': 'target', 'type': 'float', 'value': 0},
|
||||||
|
]},
|
||||||
|
] for _ in range(2)]
|
||||||
|
|
||||||
|
GUIparams = [[
|
||||||
|
{'name': 'Disable Output', 'type': 'action', 'tip': 'Disable Output'},
|
||||||
|
{'name': 'Constant Current', 'type': 'group', 'children': [
|
||||||
|
{'name': 'Set Current', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (-3, 3), 'siPrefix': True, 'suffix': 'A'},
|
||||||
|
]},
|
||||||
|
{'name': 'Temperature PID', 'type': 'bool', 'value': False, 'children': [
|
||||||
|
{'name': 'Set Temperature', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-273, 300), 'siPrefix': True, 'suffix': 'C'},
|
||||||
|
]},
|
||||||
|
{'name': 'Output Config', 'expanded': False, 'type': 'group', 'children': [
|
||||||
|
{'name': 'Max Current', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 3), 'siPrefix': True, 'suffix': 'A'},
|
||||||
|
{'name': 'Max Voltage', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 5), 'siPrefix': True, 'suffix': 'V'},
|
||||||
|
]},
|
||||||
|
{'name': 'Thermistor Config', 'expanded': False, 'type': 'group', 'children': [
|
||||||
|
{'name': 'T0', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-100, 100), 'siPrefix': True, 'suffix': 'C'},
|
||||||
|
{'name': 'R0', 'type': 'float', 'value': 10000, 'step': 1, 'siPrefix': True, 'suffix': 'Ohm'},
|
||||||
|
{'name': 'Beta', 'type': 'float', 'value': 3950, 'step': 1},
|
||||||
|
]},
|
||||||
|
{'name': 'PID Config', 'expanded': False, 'type': 'group', 'children': [
|
||||||
|
{'name': 'kP', 'type': 'float', 'value': 0, 'step': 0.1},
|
||||||
|
{'name': 'kI', 'type': 'float', 'value': 0, 'step': 0.1},
|
||||||
|
{'name': 'kD', 'type': 'float', 'value': 0, 'step': 0.1},
|
||||||
|
{'name': 'PID Auto Tune', 'expanded': False, 'type': 'group', 'children': [
|
||||||
|
{'name': 'Target Temperature', 'type': 'float', 'value': 20, 'step': 0.1, 'siPrefix': True, 'suffix': 'C'},
|
||||||
|
{'name': 'Test Current', 'type': 'float', 'value': 1, 'step': 0.1, 'siPrefix': True, 'suffix': 'A'},
|
||||||
|
{'name': 'Temperature Swing', 'type': 'float', 'value': 1.5, 'step': 0.1, 'siPrefix': True, 'suffix': 'C'},
|
||||||
|
{'name': 'Run', 'type': 'action', 'tip': 'Run'},
|
||||||
|
]},
|
||||||
|
]},
|
||||||
|
{'name': 'Save to flash', 'type': 'action', 'tip': 'Save to flash'},
|
||||||
|
] for _ in range(2)]
|
||||||
|
|
||||||
|
autoTuner = [PIDAutotune(20, 1, 1, 1.5, refresh_period / 1000),
|
||||||
|
PIDAutotune(20, 1, 1, 1.5, refresh_period / 1000)]
|
||||||
|
|
||||||
|
## If anything changes in the tree, print a message
|
||||||
|
def change(param, changes, ch):
|
||||||
|
print("tree changes:")
|
||||||
|
for param, change, data in changes:
|
||||||
|
path = paramList[ch].childPath(param)
|
||||||
|
if path is not None:
|
||||||
|
childName = '.'.join(path)
|
||||||
|
else:
|
||||||
|
childName = param.name()
|
||||||
|
print(' parameter: %s'% childName)
|
||||||
|
print(' change: %s'% change)
|
||||||
|
print(' data: %s'% str(data))
|
||||||
|
print(' ----------')
|
||||||
|
|
||||||
|
if (childName == 'Disable Output'):
|
||||||
|
tec.set_param('pwm', ch, 'i_set', 0)
|
||||||
|
paramList[ch].child('Constant Current').child('Set Current').setValue(0)
|
||||||
|
paramList[ch].child('Temperature PID').setValue(False)
|
||||||
|
autoTuner[ch].setOff()
|
||||||
|
|
||||||
|
if (childName == 'Temperature PID'):
|
||||||
|
if (data):
|
||||||
|
tec.set_param("pwm", ch, "pid")
|
||||||
|
else:
|
||||||
|
tec.set_param('pwm', ch, 'i_set', paramList[ch].child('Constant Current').child('Set Current').value())
|
||||||
|
|
||||||
|
if (childName == 'Constant Current.Set Current'):
|
||||||
|
tec.set_param('pwm', ch, 'i_set', data)
|
||||||
|
paramList[ch].child('Temperature PID').setValue(False)
|
||||||
|
|
||||||
|
if (childName == 'Temperature PID.Set Temperature'):
|
||||||
|
tec.set_param('pid', ch, 'target', data)
|
||||||
|
|
||||||
|
if (childName == 'Output Config.Max Current'):
|
||||||
|
tec.set_param('pwm', ch, 'max_i_pos', data)
|
||||||
|
tec.set_param('pwm', ch, 'max_i_neg', data)
|
||||||
|
tec.set_param('pid', ch, 'output_min', -data)
|
||||||
|
tec.set_param('pid', ch, 'output_max', data)
|
||||||
|
|
||||||
|
if (childName == 'Output Config.Max Voltage'):
|
||||||
|
tec.set_param('pwm', ch, 'max_v', data)
|
||||||
|
|
||||||
|
if (childName == 'Thermistor Config.T0'):
|
||||||
|
tec.set_param('s-h', ch, 't0', data)
|
||||||
|
|
||||||
|
if (childName == 'Thermistor Config.R0'):
|
||||||
|
tec.set_param('s-h', ch, 'r0', data)
|
||||||
|
|
||||||
|
if (childName == 'Thermistor Config.Beta'):
|
||||||
|
tec.set_param('s-h', ch, 'b', data)
|
||||||
|
|
||||||
|
if (childName == 'PID Config.kP'):
|
||||||
|
tec.set_param('pid', ch, 'kp', data)
|
||||||
|
|
||||||
|
if (childName == 'PID Config.kI'):
|
||||||
|
tec.set_param('pid', ch, 'ki', data)
|
||||||
|
|
||||||
|
if (childName == 'PID Config.kD'):
|
||||||
|
tec.set_param('pid', ch, 'kd', data)
|
||||||
|
|
||||||
|
if (childName == 'PID Config.PID Auto Tune.Run'):
|
||||||
|
autoTuner[ch].setParam(paramList[ch].child('PID Config').child('PID Auto Tune').child('Target Temperature').value(),
|
||||||
|
paramList[ch].child('PID Config').child('PID Auto Tune').child('Test Current').value(),
|
||||||
|
paramList[ch].child('PID Config').child('PID Auto Tune').child('Temperature Swing').value(),
|
||||||
|
refresh_period / 1000,
|
||||||
|
1)
|
||||||
|
autoTuner[ch].setReady()
|
||||||
|
paramList[ch].child('Temperature PID').setValue(False)
|
||||||
|
|
||||||
|
if (childName == 'Save to flash'):
|
||||||
|
tec.save_config()
|
||||||
|
|
||||||
|
def change0(param, changes):
|
||||||
|
change(param, changes, 0)
|
||||||
|
|
||||||
|
def change1(param, changes):
|
||||||
|
change(param, changes, 1)
|
||||||
|
|
||||||
|
class Curves:
|
||||||
|
def __init__(self, legend: str, key: str, channel: int, color: str, buffer_len: int, period: int):
|
||||||
|
self.curveItem = pg.PlotCurveItem(pen=({'color': color, 'width': 1}))
|
||||||
|
self.legendStr = legend
|
||||||
|
self.keyStr = key
|
||||||
|
self.channel = channel
|
||||||
|
self.data_buf = np.zeros(buffer_len)
|
||||||
|
self.time_stamp = np.zeros(buffer_len)
|
||||||
|
self.buffLen = buffer_len
|
||||||
|
self.period = period
|
||||||
|
|
||||||
|
def update(self, tec_data, cnt):
|
||||||
|
if cnt == 0:
|
||||||
|
np.copyto(self.data_buf, np.full(self.buffLen, tec_data[self.channel][self.keyStr]))
|
||||||
|
else:
|
||||||
|
self.data_buf[:-1] = self.data_buf[1:]
|
||||||
|
self.data_buf[-1] = tec_data[self.channel][self.keyStr]
|
||||||
|
self.time_stamp[:-1] = self.time_stamp[1:]
|
||||||
|
self.time_stamp[-1] = cnt * self.period / 1000
|
||||||
|
self.curveItem.setData(x = self.time_stamp, y = self.data_buf)
|
||||||
|
|
||||||
|
class Graph:
|
||||||
|
def __init__(self, parent: pg.LayoutWidget, title: str, row: int, col: int, curves: list[Curves]):
|
||||||
|
self.plotItem = pg.PlotWidget(title=title)
|
||||||
|
self.legendItem = pg.LegendItem(offset=(75, 30), brush=(50,50,200,150))
|
||||||
|
self.legendItem.setParentItem(self.plotItem.getPlotItem())
|
||||||
|
parent.addWidget(self.plotItem, row, col)
|
||||||
|
self.curves = curves
|
||||||
|
for curve in self.curves:
|
||||||
|
self.plotItem.addItem(curve.curveItem)
|
||||||
|
self.legendItem.addItem(curve.curveItem, curve.legendStr)
|
||||||
|
|
||||||
|
def update(self, tec_data, cnt):
|
||||||
|
for curve in self.curves:
|
||||||
|
curve.update(tec_data, cnt)
|
||||||
|
self.plotItem.setRange(xRange=[(cnt - self.curves[0].buffLen) * self.curves[0].period / 1000, cnt * self.curves[0].period / 1000])
|
||||||
|
|
||||||
|
def TECsync():
|
||||||
|
global TECparams
|
||||||
|
for channel in range(2):
|
||||||
|
for parents in TECparams[channel]:
|
||||||
|
if parents['tag'] == 'report':
|
||||||
|
for data in tec.report_mode():
|
||||||
|
for children in parents['children']:
|
||||||
|
print(data)
|
||||||
|
children['value'] = data[channel][children['tag']]
|
||||||
|
if quit:
|
||||||
|
break
|
||||||
|
if parents['tag'] == 'pwm':
|
||||||
|
for children in parents['children']:
|
||||||
|
children['value'] = tec.get_pwm()[channel][children['tag']]['value']
|
||||||
|
if parents['tag'] == 'pid':
|
||||||
|
for children in parents['children']:
|
||||||
|
children['value'] = tec.get_pid()[channel]['parameters'][children['tag']]
|
||||||
|
if parents['tag'] == 's-h':
|
||||||
|
for children in parents['children']:
|
||||||
|
children['value'] = tec.get_steinhart_hart()[channel]['params'][children['tag']]
|
||||||
|
if parents['tag'] == 'PIDtarget':
|
||||||
|
for children in parents['children']:
|
||||||
|
children['value'] = tec.get_pid()[channel]['target']
|
||||||
|
|
||||||
|
def refreshTreeParam(tempTree:dict, channel:int) -> dict:
|
||||||
|
tempTree['children']['Constant Current']['children']['Set Current']['value'] = TECparams[channel][1]['children'][3]['value']
|
||||||
|
tempTree['children']['Temperature PID']['value'] = TECparams[channel][0]['children'][0]['value']
|
||||||
|
tempTree['children']['Temperature PID']['children']['Set Temperature']['value'] = TECparams[channel][4]['children'][0]['value']
|
||||||
|
tempTree['children']['Output Config']['children']['Max Current']['value'] = TECparams[channel][1]['children'][0]['value']
|
||||||
|
tempTree['children']['Output Config']['children']['Max Voltage']['value'] = TECparams[channel][1]['children'][2]['value']
|
||||||
|
tempTree['children']['Thermistor Config']['children']['T0']['value'] = TECparams[channel][3]['children'][0]['value'] - 273.15
|
||||||
|
tempTree['children']['Thermistor Config']['children']['R0']['value'] = TECparams[channel][3]['children'][1]['value']
|
||||||
|
tempTree['children']['Thermistor Config']['children']['Beta']['value'] = TECparams[channel][3]['children'][2]['value']
|
||||||
|
tempTree['children']['PID Config']['children']['kP']['value'] = TECparams[channel][2]['children'][0]['value']
|
||||||
|
tempTree['children']['PID Config']['children']['kI']['value'] = TECparams[channel][2]['children'][1]['value']
|
||||||
|
tempTree['children']['PID Config']['children']['kD']['value'] = TECparams[channel][2]['children'][2]['value']
|
||||||
|
return tempTree
|
||||||
|
|
||||||
|
cnt = 0
|
||||||
|
def updateData():
|
||||||
sb10q
commented
update_data, see PEP8 update_data, see PEP8
|
|||||||
|
global cnt
|
||||||
|
for data in tec.report_mode():
|
||||||
|
|
||||||
|
ch0tempGraph.update(data, cnt)
|
||||||
|
ch1tempGraph.update(data, cnt)
|
||||||
|
ch0currentGraph.update(data, cnt)
|
||||||
|
ch1currentGraph.update(data, cnt)
|
||||||
|
|
||||||
|
for channel in range (2):
|
||||||
|
if (autoTuner[channel].state() == PIDAutotuneState.STATE_READY or
|
||||||
|
autoTuner[channel].state() == PIDAutotuneState.STATE_RELAY_STEP_UP or
|
||||||
|
autoTuner[channel].state() == PIDAutotuneState.STATE_RELAY_STEP_DOWN):
|
||||||
|
autoTuner[channel].run(data[channel]['temperature'], data[channel]['time'])
|
||||||
|
tec.set_param('pwm', channel, 'i_set', autoTuner[channel].output())
|
||||||
|
elif (autoTuner[channel].state() == PIDAutotuneState.STATE_SUCCEEDED):
|
||||||
|
kp, ki, kd = autoTuner[channel].get_tec_pid()
|
||||||
|
autoTuner[channel].setOff()
|
||||||
|
paramList[channel].child('PID Config').child('kP').setValue(kp)
|
||||||
|
paramList[channel].child('PID Config').child('kI').setValue(ki)
|
||||||
|
paramList[channel].child('PID Config').child('kD').setValue(kd)
|
||||||
|
tec.set_param('pid', channel, 'kp', kp)
|
||||||
|
tec.set_param('pid', channel, 'ki', ki)
|
||||||
|
tec.set_param('pid', channel, 'kd', kd)
|
||||||
|
elif (autoTuner[channel].state() == PIDAutotuneState.STATE_FAILED):
|
||||||
|
tec.set_param('pwm', channel, 'i_set', 0)
|
||||||
|
autoTuner[channel].setOff()
|
||||||
|
|
||||||
|
if quit:
|
||||||
|
break
|
||||||
|
cnt += 1
|
||||||
sb10q
commented
Should there be a feature to reset the plots and/or set a maximum number of samples to store? Should there be a feature to reset the plots and/or set a maximum number of samples to store?
|
|||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tec = Client(host="192.168.1.26", port=23, timeout=None)
|
||||||
sb10q
commented
argparse, IP address settings argparse, IP address settings
|
|||||||
|
|
||||||
|
app = pg.mkQApp()
|
||||||
|
pg.setConfigOptions(antialias=True)
|
||||||
|
mw = QtGui.QMainWindow()
|
||||||
|
mw.setWindowTitle('Thermostat Control Panel')
|
||||||
|
mw.resize(1920,1200)
|
||||||
|
cw = QtGui.QWidget()
|
||||||
|
mw.setCentralWidget(cw)
|
||||||
|
l = QtGui.QVBoxLayout()
|
||||||
|
layout = pg.LayoutWidget()
|
||||||
|
l.addWidget(layout)
|
||||||
|
cw.setLayout(l)
|
||||||
|
|
||||||
|
## Create tree of Parameter objects
|
||||||
|
paramList = [Parameter.create(name='GUIparams', type='group', children=GUIparams[0]),
|
||||||
|
Parameter.create(name='GUIparams', type='group', children=GUIparams[1])]
|
||||||
|
|
||||||
|
ch0Tree = ParameterTree()
|
||||||
|
ch0Tree.setParameters(paramList[0], showTop=False)
|
||||||
|
ch1Tree = ParameterTree()
|
||||||
|
ch1Tree.setParameters(paramList[1], showTop=False)
|
||||||
|
|
||||||
|
TECsync()
|
||||||
|
paramList[0].restoreState(refreshTreeParam(paramList[0].saveState(), 0))
|
||||||
|
paramList[1].restoreState(refreshTreeParam(paramList[1].saveState(), 1))
|
||||||
|
|
||||||
|
paramList[0].sigTreeStateChanged.connect(change0)
|
||||||
sb10q
commented
Make Make ``channel`` the first parameter, use ``functools.partial``, and remove ``change0/change1``
|
|||||||
|
paramList[1].sigTreeStateChanged.connect(change1)
|
||||||
|
|
||||||
|
layout.addWidget(ch0Tree, 1, 1, 1, 1)
|
||||||
|
layout.addWidget(ch1Tree, 2, 1, 1, 1)
|
||||||
|
|
||||||
|
ch0tempGraph = Graph(layout, 'Channel 0 Temperature', 1, 2, [Curves('Feedback', 'temperature', 0, 'r', rec_len, refresh_period)])
|
||||||
|
ch1tempGraph = Graph(layout, 'Channel 1 Temperature', 2, 2, [Curves('Feedback', 'temperature', 1, 'r', rec_len, refresh_period)])
|
||||||
|
ch0currentGraph = Graph(layout, 'Channel 0 Current', 1, 3, [Curves('Feedback', 'tec_i', 0, 'r', rec_len, refresh_period),
|
||||||
|
Curves('Setpoint', 'i_set', 0, 'g', rec_len, refresh_period)])
|
||||||
|
ch1currentGraph = Graph(layout, 'Channel 1 Current', 2, 3, [Curves('Feedback', 'tec_i', 1, 'r', rec_len, refresh_period),
|
||||||
|
Curves('Setpoint', 'i_set', 1, 'g', rec_len, refresh_period)])
|
||||||
|
|
||||||
|
t = QtCore.QTimer()
|
||||||
|
t.timeout.connect(updateData)
|
||||||
|
t.start(refresh_period)
|
||||||
|
|
||||||
|
mw.show()
|
||||||
|
|
||||||
|
pg.exec()
|
|
@ -113,7 +113,12 @@ impl Channels {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_i(&mut self, channel: usize) -> ElectricCurrent {
|
pub fn get_i(&mut self, channel: usize) -> ElectricCurrent {
|
||||||
let center_point = self.get_center(channel);
|
let center_point = match channel.into() {
|
||||||
|
0 => self.channel0.vref_meas,
|
||||||
|
1 => self.channel1.vref_meas,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
// let center_point = self.get_center(channel);
|
||||||
sb10q
commented
Why? Why?
If the get_center function is broken then it should be fixed. In any case this commented-out line should be removed.
|
|||||||
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
|
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
|
||||||
let voltage = self.get_dac(channel);
|
let voltage = self.get_dac(channel);
|
||||||
let i_tec = (voltage - center_point) / (10.0 * r_sense);
|
let i_tec = (voltage - center_point) / (10.0 * r_sense);
|
||||||
|
|
Loading…
Reference in New Issue
You could make it a
nix shell