Source code for device.langmuir_hotprobe
import time
from types import SimpleNamespace
from . import base
[docs]
class LangmuirHotProbe(base.ArduinoProbeBase):
"""Arduino controller that handles heating of a langmuir probe and sweeped
measurments of its I-V characteristic.
Communication:
- serial interface to Arduino
"""
SWEEP_CMD = "q"
def __init__(self, bus, **kwargs):
"""Constructor
:param str bus: Name of serial bus where Arduino is connected,
e.g. '/dev/ttyUSB0'
"""
super().__init__(bus=bus, **kwargs)
self._last = SimpleNamespace(
power_on=False, # Main power relay (turn on once)
heating_on=False, # On/off heating
voltage_byte=0, # Heating voltage, 8bit integer
heating_power=0, # Heating power [W]
heating_timeout=None, # ... see update_frontend()
)
# ==== Inherited abstract methods ====
def _safestate(self):
self.switch_heating_off()
def _defaultstate(self):
# Turn on main power relay
self._power_on()
def _clear_defaultstate(self):
# Turn off main power relay
self._power_off()
# ==== Private commands ====
@base.base_command
def _power_on(self):
if not self._last.power_on:
self._write("r")
self._last.power_on = True
@base.base_command
def _power_off(self):
if self._last.power_on:
self._write("r")
self._last.power_on = False
@base.base_command
def _set_voltage(self, byte):
"""Set voltage, byte needs to be 0-255 (8-bit integer)."""
# Using XOR to determine which bits need to be swapped
to_swap = byte ^ self._last.voltage_byte
for i, cmd in enumerate(["a", "s", "d", "f", "g", "h", "j", "k"]):
if (to_swap & (0x80 >> i)) != 0:
self._write(cmd)
self._last.voltage_byte = byte
# ==== Commands ====
[docs]
def is_heating_on(self):
"""Status of the probe heater."""
return self._last.heating_on
[docs]
@base.base_command
def switch_heating_on(self, percent):
"""Activate heating, apply 10-100% voltage to the heater element.
:param float percent: Percentage in range 10-100.
"""
percent = float(percent)
if not (10.0 <= percent <= 100.0):
raise base.CommandError("Value percent must be between 10-100")
if self.is_heating_on():
self.switch_heating_off()
self._set_voltage(round(255 * (percent - 10) / 90))
self._write("m")
self._last.heating_on = True
[docs]
@base.base_command
def switch_heating_off(self):
"""Stop heating, no voltage is applied to the heater element."""
if self.is_heating_on():
self._write("m")
self._last.heating_on = False
[docs]
@base.base_command
def heating_power(self, device_client=None):
"""Measure heating power [W], it takes 1s (averaging)."""
self._write("i")
volts = 1.8 + self._last.voltage_byte / 255 * (20 - 1.8)
# Expected format: "Proud: 1.2 A\r\n"
line = self._readline()
while line == "":
time.sleep(0.1) # We wait for arduino to finish the measurement
line = self._readline()
amps = float(line.split(" ")[-2])
self._last.heating_power = volts * amps
if device_client:
device_client.emit("value", {
"value": self._last.heating_power,
"formatted": f"{self._last.heating_power:.1f} W",
"label": "Heating",
"min": 0, "max": 60,
"id": "heating",
})
self._last.heating_timeout = time.time() + 10 # 10 sec. timeout
return self._last.heating_power
# ==== Related to DeviceClient ====
[docs]
def update_frontend(self, device_client):
# Because heating_power() is a slow measurment (~ 1 sec.) it is better
# let user refresh it manually. The value disappears after a short
# timeout to indicate that it should be refreshed again.
if self._last.heating_timeout and self._last.heating_timeout >= time.time():
self._last.heating_timeout = None
device_client.emit("value", {
"value": 0,
"formatted": f"n/a",
"label": "Heating",
"min": 0, "max": 60,
"id": "heating",
})