# -*- coding: utf-8 -*-
# (c) Copyright 2020 Sensirion AG, Switzerland
from __future__ import absolute_import, division, print_function
from datetime import datetime
from sensirion_shdlc_driver import ShdlcDeviceBase, ShdlcFirmwareUpdate
from sensirion_shdlc_driver.types import FirmwareVersion, HardwareVersion, \
ProtocolVersion, Version
from .definitions import Sfc5xxxValveInputSource
from .types import Sfc5xxxCalibrationConditions, Sfc5xxxReadBufferResponse
from .units import Sfc5xxxUnitPrefix, Sfc5xxxUnit, Sfc5xxxUnitTimeBase, \
Sfc5xxxMediumUnit
from .device_errors import SFC5XXX_DEVICE_ERROR_LIST
from .firmware_image import Sfc5xxxFirmwareImage
from .commands import \
Sfc5xxxCmdActivateCalibration, \
Sfc5xxxCmdDeviceReset, \
Sfc5xxxCmdFactoryReset, \
Sfc5xxxCmdGetArticleCode, \
Sfc5xxxCmdGetBaudrate, \
Sfc5xxxCmdGetCalibrationFullscale, \
Sfc5xxxCmdGetCalibrationGasDescription, \
Sfc5xxxCmdGetCalibrationGasId, \
Sfc5xxxCmdGetCalibrationGasUnit, \
Sfc5xxxCmdGetCalibrationInitialConditions, \
Sfc5xxxCmdGetCalibrationRecalibrationConditions, \
Sfc5xxxCmdGetCalibrationThermalConductivityReference, \
Sfc5xxxCmdGetCalibrationValidity, \
Sfc5xxxCmdGetCurrentFullscale, \
Sfc5xxxCmdGetCurrentGasDescription, \
Sfc5xxxCmdGetCurrentGasId, \
Sfc5xxxCmdGetCurrentGasUnit, \
Sfc5xxxCmdGetCurrentInitialCalibrationConditions, \
Sfc5xxxCmdGetCurrentRecalibrationConditions, \
Sfc5xxxCmdGetCurrentThermalConductivityReference, \
Sfc5xxxCmdGetGasTemperatureCompensationEnable, \
Sfc5xxxCmdGetInletGasTemperatureForCompensation, \
Sfc5xxxCmdGetInletPressureForGainCorrection, \
Sfc5xxxCmdGetNumberOfCalibrations, \
Sfc5xxxCmdGetPressureDependentGainEnable, \
Sfc5xxxCmdGetProductName, \
Sfc5xxxCmdGetProductSubtype, \
Sfc5xxxCmdGetProductType, \
Sfc5xxxCmdGetReplyDelay, \
Sfc5xxxCmdGetSerialNumber, \
Sfc5xxxCmdGetSetpoint, \
Sfc5xxxCmdGetSetpointPersist, \
Sfc5xxxCmdGetSlaveAddress, \
Sfc5xxxCmdGetUserControllerGain, \
Sfc5xxxCmdGetUserDefinedFullscale, \
Sfc5xxxCmdGetUserDefinedMediumUnit, \
Sfc5xxxCmdGetUserDefinedMediumUnitWithoutWildcards, \
Sfc5xxxCmdGetUserDefinedValveValue, \
Sfc5xxxCmdGetValveInputSource, \
Sfc5xxxCmdGetVersion, \
Sfc5xxxCmdMeasureRawFlow, \
Sfc5xxxCmdMeasureRawThermalConductivity, \
Sfc5xxxCmdMeasureRawThermalConductivityWithClosedValve, \
Sfc5xxxCmdMeasureTemperature, \
Sfc5xxxCmdReadDeviceStatus, \
Sfc5xxxCmdReadMeasuredValue, \
Sfc5xxxCmdReadMeasuredValueBuffer, \
Sfc5xxxCmdReadUserMemory, \
Sfc5xxxCmdSetBaudrate, \
Sfc5xxxCmdSetGasTemperatureCompensationEnable, \
Sfc5xxxCmdSetInletGasTemperatureForCompensation, \
Sfc5xxxCmdSetInletPressureForGainCorrection, \
Sfc5xxxCmdSetPressureDependentGainEnable, \
Sfc5xxxCmdSetReplyDelay, \
Sfc5xxxCmdSetSetpoint, \
Sfc5xxxCmdSetSetpointAndReadMeasuredValue, \
Sfc5xxxCmdSetSetpointPersist, \
Sfc5xxxCmdSetSlaveAddress, \
Sfc5xxxCmdSetUserControllerGain, \
Sfc5xxxCmdSetUserDefinedMediumUnit, \
Sfc5xxxCmdSetUserDefinedValveValue, \
Sfc5xxxCmdSetValveInputSource, \
Sfc5xxxCmdWriteUserMemory
import logging
log = logging.getLogger(__name__)
[docs]class Sfc5xxxShdlcDevice(ShdlcDeviceBase):
"""
Sfc5xxx device.
This is a low-level driver which just provides all SHDLC commands as Python
methods. Typically, calling a method sends one SHDLC request to the device
and interprets its response. There is no higher level functionality
available, please look for other drivers if you need a higher level
interface.
There is no (or very few) caching functionality in this driver. For example
if you call :func:`get_serial_number` 100 times, it will send the command
100 times over the SHDLC interface to the device. This makes the driver
(nearly) stateless.
"""
[docs] def __init__(self, connection, slave_address):
"""
Create an Sfc5xxx device instance on an SHDLC connection.
.. note:: This constructor does not communicate with the device, so
it's possible to instantiate an object even if the device is
not connected or powered yet.
:param ~sensirion_shdlc_driver.connection.ShdlcConnection connection:
The connection used for the communication.
:param byte slave_address:
The address of the device. The default address of the SFC5xxx is 0.
"""
super(Sfc5xxxShdlcDevice, self).__init__(connection, slave_address)
self._register_device_errors(SFC5XXX_DEVICE_ERROR_LIST)
[docs] def get_product_type(self, as_int=False):
"""
Get the product type. The product type (sometimes also called "device
type") can be used to detect what kind of SHDLC product is connected.
:param bool as_int: If ``True``, the product type is returned as an
integer, otherwise as a string of hexadecimal
digits (default).
:return: The product type as an integer or string of hexadecimal
digits.
:rtype: string/int
"""
product_type = self.execute(Sfc5xxxCmdGetProductType())
if as_int:
product_type = int(product_type, 16)
return product_type
[docs] def get_product_subtype(self):
"""
Get the product subtype. Some product types exist in multiple slightly
different variants, this command allows to determine the exact variant
of the connected device. Sometimes this is called "device subtype".
.. note:: This command is not supported by every product type.
:return: The product subtype as a byte (the interpretation depends on
the connected product type).
:rtype: byte
"""
return self.execute(Sfc5xxxCmdGetProductSubtype())
[docs] def get_product_name(self):
"""
Get the product name of the device.
.. note:: This command is not supported by every product type.
:return: The product name as an ASCII string.
:rtype: string
"""
return self.execute(Sfc5xxxCmdGetProductName())
[docs] def get_article_code(self):
"""
Get the article code of the device.
.. note:: This command is not supported by every product type.
:return: The article code as an ASCII string.
:rtype: string
"""
return self.execute(Sfc5xxxCmdGetArticleCode())
[docs] def get_serial_number(self):
"""
Get the serial number of the device.
:return: The serial number as an ASCII string.
:rtype: string
"""
return self.execute(Sfc5xxxCmdGetSerialNumber())
[docs] def get_version(self):
"""
Get the version of the device firmware, hardware and SHDLC protocol.
:return: The device version as a Version object.
:rtype: Version
"""
fw_maj, fw_min, fw_dbg, hw_maj, hw_min, pc_maj, pc_min = \
self.execute(Sfc5xxxCmdGetVersion())
return Version(
firmware=FirmwareVersion(major=fw_maj, minor=fw_min, debug=fw_dbg),
hardware=HardwareVersion(major=hw_maj, minor=hw_min),
protocol=ProtocolVersion(major=pc_maj, minor=pc_min)
)
[docs] def read_device_status(self, clear=True, as_exception=False):
"""
Read and optionally clear the device status and the last error. The
status and error code interpretation is documented in the device
interface specification.
:param bool clear:
If ``True``, the status flags on the device get cleared.
:param bool as_exception:
If ``True``, the last error is returned as an
:py:class:`~sensirion_shdlc_driver.errors.ShdlcDeviceError`
object instead of a byte.
:return: The device status as a 32-bit unsigned integer containing all
status flags, and the last error which occurred on the device.
If ``as_exception`` is ``True``, it's returned as an
:py:class:`~sensirion_shdlc_driver.errors.ShdlcDeviceError`
object or ``None``, otherwise as a byte.
:rtype: int, byte/ShdlcDeviceError/None
"""
state, error = self.execute(Sfc5xxxCmdReadDeviceStatus(clear=clear))
if as_exception:
error = self._get_device_error(error)
return state, error
[docs] def get_slave_address(self):
"""
Get the SHDLC slave address of the device.
.. note:: See also the property
:py:attr:`~sensirion_shdlc_driver.device.ShdlcDevice.slave_address`
which returns the device's slave address without sending a
command. This method really sends a command to the device,
even though the slave address is actually already known by
this object.
:return: The slave address of the device.
:rtype: byte
"""
return self.execute(Sfc5xxxCmdGetSlaveAddress())
[docs] def set_slave_address(self, slave_address, update_driver=True):
"""
Set the SHDLC slave address of the device.
.. note:: The slave address is stored in non-volatile memory of the
device and thus persists after a device reset. So the next
time connecting to the device, you have to use the new
address.
.. warning:: When changing the address of a slave, make sure there
isn't already a slave with that address on the same bus!
In that case you would get communication issues which can
only be fixed by disconnecting one of the slaves.
:param byte slave_address:
The new slave address [0..254]. The address 255 is reserved for
broadcasts.
:param bool update_driver:
If ``True``, the property
:py:attr:`~sensirion_shdlc_driver.device.ShdlcDevice.slave_address`
of this object is also updated with the new address. This is
needed to allow further communication with the device, as its
address has changed.
"""
self.execute(Sfc5xxxCmdSetSlaveAddress(slave_address))
if update_driver:
self._slave_address = slave_address
[docs] def get_baudrate(self):
"""
Get the SHDLC baudrate of the device.
.. note:: This method really sends a command to the device, even though
the baudrate is already known by the used
:py:class:`~sensirion_shdlc_driver.port.ShdlcPort` object.
:return: The baudrate of the device [bit/s].
:rtype: int
"""
return self.execute(Sfc5xxxCmdGetBaudrate())
[docs] def set_baudrate(self, baudrate, update_driver=True):
"""
Set the SHDLC baudrate of the device.
.. note:: The baudrate is stored in non-volatile memory of the
device and thus persists after a device reset. So the next
time connecting to the device, you have to use the new
baudrate.
.. warning:: If you pass ``True`` to the argument ``update_driver``,
the baudrate of the underlaying
:py:class:`~sensirion_shdlc_driver.port.ShdlcPort` object
is changed. As the baudrate applies to the whole bus (with
all its slaves), you might no longer be able to
communicate with other slaves. Generally you should change
the baudrate of all slaves consecutively, and only set
``update_driver`` to ``True`` the last time.
:param int baudrate:
The new baudrate. See device documentation for a list of supported
baudrates. Many devices support the baudrates 9600, 19200 and
115200.
:param bool update_driver:
If true, the baudrate of the
:py:class:`~sensirion_shdlc_driver.port.ShdlcPort` object is also
updated with the baudrate. This is needed to allow further
communication with the device, as its baudrate has changed.
"""
self.execute(Sfc5xxxCmdSetBaudrate(baudrate))
if update_driver:
self._connection.port.bitrate = baudrate
[docs] def get_reply_delay(self):
"""
Get the SHDLC reply delay of the device.
See
:py:meth:`~sensirion_shdlc_driver.device.ShdlcDevice.set_reply_delay()`
for details.
:return: The reply delay of the device [μs].
:rtype: byte
"""
return self.execute(Sfc5xxxCmdGetReplyDelay())
[docs] def set_reply_delay(self, reply_delay_us):
"""
Set the SHDLC reply delay of the device.
The reply delay allows to increase the minimum response time of the
slave to a given value in Microseconds. This is needed for RS485
masters which require some time to switch from sending to receiving.
If the slave starts sending the response while the master is still
driving the bus lines, a conflict on the bus occurs and communication
fails. If you use such a slow RS485 master, you can increase the reply
delay of all slaves to avoid this issue.
:param byte reply_delay_us: The new reply delay [μs].
"""
# Due to a bug in some firmware versions, reject values 0 or 1 here
if reply_delay_us < 2:
raise ValueError("Reply delay must be >= 2us!")
self.execute(Sfc5xxxCmdSetReplyDelay(reply_delay_us))
[docs] def device_reset(self):
"""
Execute a device reset (reboot firmware, similar to power cycle).
"""
self.execute(Sfc5xxxCmdDeviceReset())
[docs] def factory_reset(self):
"""
Perform a factory reset (restore the off-the-shelf factory
configuration).
.. warning:: This resets any configuration done after leaving the
factory! Keep in mind that this command might also change
communication parameters (i.e. baudrate and slave address)
and thus you might have to adjust the driver's parameters
to allow further communication with the device.
"""
self.execute(Sfc5xxxCmdFactoryReset())
[docs] def get_setpoint(self, scaling):
"""
Get the current flow setpoint.
:param ~sensirion_shdlc_sfc5xxx.definitions.Sfc5xxxScaling scaling:
Defines with which scale resp. unit the setpoint should be
returned.
:return:
The current setpoint with the specified scaling.
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetSetpoint(int(scaling)))
[docs] def set_setpoint(self, setpoint, scaling):
"""
Set the flow setpoint which is used by the flow controller as reference
input.
:param float setpoint:
The new setpoint with the specified scaling.
:param ~sensirion_shdlc_sfc5xxx.definitions.Sfc5xxxScaling scaling:
Defines with which scale resp. unit the setpoint is passed.
"""
self.execute(Sfc5xxxCmdSetSetpoint(int(scaling), setpoint))
[docs] def get_setpoint_persist(self):
"""
Get the setpoint persist configuration.
:return:
Whether the setpoint should persist after a device reset or not.
:rtype:
bool
"""
return self.execute(Sfc5xxxCmdGetSetpointPersist())
[docs] def set_setpoint_persist(self, persist):
"""
Set the setpoint persist configuration.
Allows to define if a setpoint should persist after a device reset
(soft or hardreset) or if it should be set to zero. Default is
``False``, i.e. the setpoint is set to zero after a device reset.
:param bool persist:
Whether the setpoint should persist after a reset or not.
"""
self.execute(Sfc5xxxCmdSetSetpointPersist(persist))
[docs] def set_setpoint_and_read_measured_value(self, setpoint, scaling):
"""
Set the flow setpoint and return the last measured flow value in
one command.
This is the same as calling
:py:meth:`~sensirion_shdlc_sfc5xxx.device.Sfc5xxxShdlcDevice.set_setpoint`
and
:py:meth:`~sensirion_shdlc_sfc5xxx.device.Sfc5xxxShdlcDevice.read_measured_value`,
but it's more efficient since only one SHDLC command is executed.
:param float setpoint:
The new setpoint with the specified scaling.
:param ~sensirion_shdlc_sfc5xxx.definitions.Sfc5xxxScaling scaling:
Defines with which scale resp. unit the setpoint is passed and the
measured flow is returned.
:return:
The last measured flow with the specified scaling.
:rtype:
float
"""
return self.execute(Sfc5xxxCmdSetSetpointAndReadMeasuredValue(
int(scaling), setpoint))
[docs] def read_measured_value(self, scaling):
"""
Read the last measured flow value.
:param ~sensirion_shdlc_sfc5xxx.definitions.Sfc5xxxScaling scaling:
Defines with which scale resp. unit the measured flow should be
returned.
:return:
The last measured flow with the specified scaling.
:rtype:
float
"""
return self.execute(Sfc5xxxCmdReadMeasuredValue(int(scaling)))
[docs] def read_measured_value_buffer(self, scaling, max_reads=100):
"""
Read the measured flow value buffer.
The MFC has an internal ring buffer in which the measured flow values
are automatically stored in a regular interval. The size of the buffer
is between 85 and 256 (depends on the specific device configuration).
This method sends "read buffer" commands to the device until the whole
buffer was read out (since the SHDLC frame length is limited, it's not
possible to read out the whole buffer with a single command). The
received values will automatically be removed from the buffer in the
device, so you will only get the values received since the last call
to this method.
.. warning:: If you call this method too rarely, the buffer in the
device will overrun, i.e. the oldest values are lost. You
should check for lost values by reading the property
:py:attr:`~sensirion_shdlc_sfc5xxx.types.Sfc5xxxReadBufferResponse.lost_values`
of the returned object. This method does not raise an
exception if there are values lost since depending on the
use-case, this might be expected behavior.
.. note:: If the buffer gets filled faster than you read it out, it's
not possible to read out the whole buffer. In that case, the
property
:py:attr:`~sensirion_shdlc_sfc5xxx.types.Sfc5xxxReadBufferResponse.remaining_values`
of the returned object will hold a value greater than zero.
You might want to check this property to be sure the whole
buffer was read out. However, often that's not needed since
sooner or later you will also get a buffer overrun in this
situation.
:param ~sensirion_shdlc_sfc5xxx.definitions.Sfc5xxxScaling scaling:
Defines with which scale resp. unit the measured flow values should
be returned.
:param int max_reads:
The maximum count of read commands which should be sent until the
read operation will be aborted. This abort condition is needed to
avoid an infinite loop if the buffer gets filled faster than it
can be read out.
:return:
An object containing the buffered flow values and some metadata.
See
:py:class:`~sensirion_shdlc_sfc5xxx.types.Sfc5xxxReadBufferResponse`
for details.
:rtype:
:py:class:`~sensirion_shdlc_sfc5xxx.types.Sfc5xxxReadBufferResponse`
"""
read_count = 0
total_lost_values = 0
total_measured_values = []
for i in range(0, max_reads):
lost_values, remaining_values, sampling_time, measured_values = \
self.execute(Sfc5xxxCmdReadMeasuredValueBuffer(int(scaling)))
read_count += 1
total_lost_values += lost_values
total_measured_values += measured_values
if remaining_values == 0:
break
return Sfc5xxxReadBufferResponse(
scaling, read_count, total_lost_values, remaining_values,
sampling_time, total_measured_values)
[docs] def get_user_defined_valve_value(self):
"""
Get the current user defined valve value.
:return:
The current user defined valve value [0..1]. Zero means
"fully closed", 1.0 means "fully open".
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetUserDefinedValveValue())
[docs] def set_user_defined_valve_value(self, value):
"""
Set the user defined valve value.
.. note:: Writing the user defined valve value has no effect as long as
the valve input source is not set to
:py:attr:`~sensirion_shdlc_sfc5xxx.definitions.Sfc5xxxValveInputSource.USER_DEFINED`.
So you'll also have to call
:py:meth:`~sensirion_shdlc_sfc5xxx.device.Sfc5xxxShdlcDevice.set_valve_input_source()`
to change the valve input source.
:param float value:
The new user defined valve value as a normalized value in the range
[0..1]. Zero means "fully closed", 1.0 means "fully open".
"""
self.execute(Sfc5xxxCmdSetUserDefinedValveValue(value))
[docs] def get_user_defined_medium_unit(self, substitute_wildcards=False):
"""
Get the currently configured user defined medium unit.
:param bool substitute_wildcards:
By default, the user defined medium unit is set to "undefined"
values, which means to fall back to the calibration unit. With this
parameter you can choose whether you want to get the actually
configured user defined unit (which then might be undefined), or
whether the calibration unit should be returned instead in that
case. Default is ``False``, i.e. you will get the actual
configuration without falling back to the calibration unit.
:return:
The current user defined medium unit.
:rtype:
~sensirion_shdlc_sfc5xxx.units.Sfc5xxxMediumUnit
"""
cmd = Sfc5xxxCmdGetUserDefinedMediumUnitWithoutWildcards() \
if substitute_wildcards else Sfc5xxxCmdGetUserDefinedMediumUnit()
raw_prefix, raw_unit, raw_timebase = self.execute(cmd)
return Sfc5xxxMediumUnit(
prefix=Sfc5xxxUnitPrefix.from_int(raw_prefix),
unit=Sfc5xxxUnit.from_int(raw_unit),
timebase=Sfc5xxxUnitTimeBase.from_int(raw_timebase),
)
[docs] def set_user_defined_medium_unit(self, unit):
"""
Set the user defined medium unit.
This is the unit which will be used when reading or writing flow values
like setpoint or measured flow with the scaling
:py:attr:`~sensirion_shdlc_sfc5xxx.definitions.Sfc5xxxScaling.USER_DEFINED`.
This allows to always work with the same flow unit, no matter what's
the actual calibration unit of the MFC.
.. note:: This setting is stored permanently in the device, i.e. it
persists after a device reset.
:param ~sensirion_shdlc_sfc5xxx.units.Sfc5xxxMediumUnit unit:
The user defined medium unit to set.
"""
self.execute(Sfc5xxxCmdSetUserDefinedMediumUnit(
prefix=unit.prefix.value,
unit=unit.unit.value,
timebase=unit.timebase.value,
))
[docs] def get_user_defined_fullscale(self):
"""
Get the fullscale flow in the currently set user defined unit.
:return:
The fullscale flow in the current user defined unit.
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetUserDefinedFullscale())
[docs] def get_user_controller_gain(self):
"""
Get the current user controller gain.
:return:
The current user controller gain (normalized value, default 1.0).
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetUserControllerGain())
[docs] def set_user_controller_gain(self, gain):
"""
Set the current user controller gain.
This allows to adjust the speed of the flow controller by changing its
overall gain.
.. note:: This value is stored permanently in the device, i.e. it
persists after a device reset.
:param float gain:
The new gain to set (normalized value, default 1.0).
"""
self.execute(Sfc5xxxCmdSetUserControllerGain(gain))
[docs] def get_pressure_dependent_gain_enable(self):
"""
Get the state of the pressure dependent gain setting.
:return:
Whether the pressure dependent gain is enabled or not.
:rtype:
bool
"""
return self.execute(Sfc5xxxCmdGetPressureDependentGainEnable())
[docs] def set_pressure_dependent_gain_enable(self, enable):
"""
Set the state of the pressure dependent gain setting.
If the pressure dependent gain is enabled, you'll have to set the
actual inlet pressure with the method
:py:meth:`~sensirion_shdlc_sfc5xxx.device.Sfc5xxxShdlcDevice.set_inlet_pressure_for_gain_correction()`.
.. note:: This value is stored permanently in the device, i.e. it
persists after a device reset.
:param bool enable:
Whether the pressure dependent gain should be enabled or not.
"""
self.execute(Sfc5xxxCmdSetPressureDependentGainEnable(enable))
[docs] def get_inlet_pressure_for_gain_correction(self):
"""
Get the currently configured inlet pressure as used for the pressure
dependent controller gain.
:return:
The currently configured inlet pressure [bar].
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetInletPressureForGainCorrection())
[docs] def set_inlet_pressure_for_gain_correction(self, pressure):
"""
Set the inlet pressure used for the pressure dependent controller gain.
.. note:: This value is stored permanently in the device, i.e. it
persists after a device reset.
:param float pressure:
The new pressure [bar] to set.
"""
self.execute(Sfc5xxxCmdSetInletPressureForGainCorrection(pressure))
[docs] def get_gas_temperature_compensation_enable(self):
"""
Get the state of the gas temperature compensation setting.
:return:
Whether the gas temperature compensation is enabled or not.
:rtype:
bool
"""
return self.execute(Sfc5xxxCmdGetGasTemperatureCompensationEnable())
[docs] def set_gas_temperature_compensation_enable(self, enable):
"""
Set the state of the gas temperature compensation setting.
If the gas temperature compensation is enabled, you'll have to set the
actual inlet gas temperature with the method
:py:meth:`~sensirion_shdlc_sfc5xxx.device.Sfc5xxxShdlcDevice.set_inlet_gas_temperature_for_compensation()`.
.. note:: This value is stored permanently in the device, i.e. it
persists after a device reset.
:param bool enable:
Whether the gas temperature compensation should be enabled or not.
"""
self.execute(Sfc5xxxCmdSetGasTemperatureCompensationEnable(enable))
[docs] def get_inlet_gas_temperature_for_compensation(self):
"""
Get the currently configured inlet gas temperature as used for the
gas temperature compensation.
:return:
The currently configured inlet gas temperature [°C].
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetInletGasTemperatureForCompensation())
[docs] def set_inlet_gas_temperature_for_compensation(self, temperature):
"""
Set the inlet gas temperature used for the gas temperature
compensation.
.. note:: This value is stored permanently in the device, i.e. it
persists after a device reset.
:param float temperature:
The new temperature [°C] to set.
"""
self.execute(
Sfc5xxxCmdSetInletGasTemperatureForCompensation(temperature))
[docs] def measure_raw_flow(self):
"""
Measure the raw flow ticks.
:return:
Measured raw flow ticks.
:rtype:
int
"""
return self.execute(Sfc5xxxCmdMeasureRawFlow())
[docs] def measure_temperature(self):
"""
Measure the sensor temperature.
:return:
Measured temperature [°C].
:rtype:
float
"""
return self.execute(Sfc5xxxCmdMeasureTemperature())
[docs] def measure_raw_thermal_conductivity(self, close_valve=True):
"""
Measure the raw thermal conductivity.
:param bool close_valve:
If ``True`` (default), the valve will be forcibly closed for
500ms before measuring the thermal conductivity. This is needed
to guarantee a correct and stable measurement. If you are sure that
the valve is closed anyway, you could set this to ``False`` to
speed up the measurement.
:return:
Measured raw thermal conductivity ticks.
:rtype:
int
"""
cmd = Sfc5xxxCmdMeasureRawThermalConductivityWithClosedValve() \
if close_valve else Sfc5xxxCmdMeasureRawThermalConductivity()
return self.execute(cmd)
[docs] def get_number_of_calibrations(self):
"""
Get the number of possible calibrations. Each calibration can either
contain a valid calibration or not. Use
:py:meth:`~sensirion_shdlc_sfc5xxx.device.Sfc5xxxDevice.get_calibration_validity`
to determine which indices contain a valid calibration.
:return:
The number of calibrations the device memory can hold.
:rtype:
int
"""
return self.execute(Sfc5xxxCmdGetNumberOfCalibrations())
[docs] def get_calibration_validity(self, index):
"""
Check whether there exists a valid calibration block at a specific
index or not.
:param int index:
The index of the calibration to check.
:return:
Whether at the specified index exists a valid calibration block or
not.
:rtype:
bool
"""
return self.execute(Sfc5xxxCmdGetCalibrationValidity(index))
[docs] def get_calibration_gas_description(self, index):
"""
Get the gas description of a specific calibration index.
:param int index:
The index of the calibration to read from.
:return:
Gas description string.
:rtype:
str
"""
return self.execute(Sfc5xxxCmdGetCalibrationGasDescription(index))
[docs] def get_calibration_gas_id(self, index):
"""
Get the gas ID of a specific calibration index.
:param int index:
The index of the calibration to read from.
:return:
Gas ID.
:rtype:
int
"""
return self.execute(Sfc5xxxCmdGetCalibrationGasId(index))
[docs] def get_calibration_gas_unit(self, index):
"""
Get the gas unit of a specific calibration index.
:param int index:
The index of the calibration to read from.
:return:
Gas unit.
:rtype:
~sensirion_shdlc_sfc5xxx.units.Sfc5xxxMediumUnit
"""
raw_prefix, raw_unit, raw_timebase = \
self.execute(Sfc5xxxCmdGetCalibrationGasUnit(index))
return Sfc5xxxMediumUnit(
prefix=Sfc5xxxUnitPrefix.from_int(raw_prefix),
unit=Sfc5xxxUnit.from_int(raw_unit),
timebase=Sfc5xxxUnitTimeBase.from_int(raw_timebase),
)
[docs] def get_calibration_fullscale(self, index):
"""
Get the fullscale flow of a specific calibration index.
:param int index:
The index of the calibration to read from.
:return:
Fullscale flow in the unit of the calibration.
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetCalibrationFullscale(index))
[docs] def get_calibration_initial_conditions(self, index):
"""
Get the initial calibration conditions of a specific calibration index.
:param int index:
The index of the calibration to read from.
:return:
Initial calibration conditions.
:rtype:
~sensirion_shdlc_sfc5xxx.types.Sfc5xxxCalibrationConditions
"""
company, operator, year, month, day, hour, minute, temperature, \
inlet_pressure, differential_pressure, is_real_gas_calibration, \
accuracy_setpoint, accuracy_fullscale = self.execute(
Sfc5xxxCmdGetCalibrationInitialConditions(index))
return Sfc5xxxCalibrationConditions(
company=company,
operator=operator,
datetime=datetime(year, month, day, hour, minute),
temperature=temperature,
inlet_pressure=inlet_pressure,
differential_pressure=differential_pressure,
is_real_gas_calibration=is_real_gas_calibration,
accuracy_setpoint=accuracy_setpoint,
accuracy_fullscale=accuracy_fullscale,
)
[docs] def get_calibration_recalibration_conditions(self, index):
"""
Get the recalibration conditions of a specific calibration index.
:param int index:
The index of the calibration to read from.
:return:
Recalibration conditions.
:rtype:
~sensirion_shdlc_sfc5xxx.types.Sfc5xxxCalibrationConditions
"""
company, operator, year, month, day, hour, minute, temperature, \
inlet_pressure, differential_pressure, is_real_gas_calibration, \
accuracy_setpoint, accuracy_fullscale = self.execute(
Sfc5xxxCmdGetCalibrationRecalibrationConditions(index))
return Sfc5xxxCalibrationConditions(
company=company,
operator=operator,
datetime=datetime(year, month, day, hour, minute),
temperature=temperature,
inlet_pressure=inlet_pressure,
differential_pressure=differential_pressure,
is_real_gas_calibration=is_real_gas_calibration,
accuracy_setpoint=accuracy_setpoint,
accuracy_fullscale=accuracy_fullscale,
)
[docs] def get_calibration_thermal_conductivity_reference(self, index):
"""
Get the thermal conductivity reference value of a specific
calibration index.
:param int index:
The index of the calibration to read from.
:return:
Thermal conductivity reference value ticks.
:rtype:
int
"""
return self.execute(
Sfc5xxxCmdGetCalibrationThermalConductivityReference(index))
[docs] def get_current_gas_description(self):
"""
Get the gas description of the currently active calibration.
:return:
Gas description string.
:rtype:
str
"""
return self.execute(Sfc5xxxCmdGetCurrentGasDescription())
[docs] def get_current_gas_id(self):
"""
Get the gas ID of the currently active calibration.
:return:
Gas ID.
:rtype:
int
"""
return self.execute(Sfc5xxxCmdGetCurrentGasId())
[docs] def get_current_gas_unit(self):
"""
Get the gas unit of the currently active calibration.
:return:
Gas unit.
:rtype:
~sensirion_shdlc_sfc5xxx.units.Sfc5xxxMediumUnit
"""
raw_prefix, raw_unit, raw_timebase = \
self.execute(Sfc5xxxCmdGetCurrentGasUnit())
return Sfc5xxxMediumUnit(
prefix=Sfc5xxxUnitPrefix.from_int(raw_prefix),
unit=Sfc5xxxUnit.from_int(raw_unit),
timebase=Sfc5xxxUnitTimeBase.from_int(raw_timebase),
)
[docs] def get_current_fullscale(self):
"""
Get the fullscale flow of the currently active calibration.
:return:
Fullscale flow in the unit of the calibration.
:rtype:
float
"""
return self.execute(Sfc5xxxCmdGetCurrentFullscale())
[docs] def get_current_initial_calibration_conditions(self):
"""
Get the initial calibration conditions of the currently active
calibration.
:return:
Initial calibration conditions.
:rtype:
~sensirion_shdlc_sfc5xxx.types.Sfc5xxxCalibrationConditions
"""
company, operator, year, month, day, hour, minute, temperature, \
inlet_pressure, differential_pressure, is_real_gas_calibration, \
accuracy_setpoint, accuracy_fullscale = self.execute(
Sfc5xxxCmdGetCurrentInitialCalibrationConditions())
return Sfc5xxxCalibrationConditions(
company=company,
operator=operator,
datetime=datetime(year, month, day, hour, minute),
temperature=temperature,
inlet_pressure=inlet_pressure,
differential_pressure=differential_pressure,
is_real_gas_calibration=is_real_gas_calibration,
accuracy_setpoint=accuracy_setpoint,
accuracy_fullscale=accuracy_fullscale,
)
[docs] def get_current_recalibration_conditions(self):
"""
Get the recalibration conditions of the currently active calibration.
:return:
Recalibration conditions.
:rtype:
~sensirion_shdlc_sfc5xxx.types.Sfc5xxxCalibrationConditions
"""
company, operator, year, month, day, hour, minute, temperature, \
inlet_pressure, differential_pressure, is_real_gas_calibration, \
accuracy_setpoint, accuracy_fullscale = self.execute(
Sfc5xxxCmdGetCurrentRecalibrationConditions())
return Sfc5xxxCalibrationConditions(
company=company,
operator=operator,
datetime=datetime(year, month, day, hour, minute),
temperature=temperature,
inlet_pressure=inlet_pressure,
differential_pressure=differential_pressure,
is_real_gas_calibration=is_real_gas_calibration,
accuracy_setpoint=accuracy_setpoint,
accuracy_fullscale=accuracy_fullscale,
)
[docs] def get_current_thermal_conductivity_reference(self):
"""
Get the thermal conductivity reference value of the currently active
calibration.
:return:
Thermal conductivity reference value ticks.
:rtype:
int
"""
return self.execute(Sfc5xxxCmdGetCurrentThermalConductivityReference())
[docs] def activate_calibration(self, index):
"""
Activate a specific gas calibration block and run the flow controller.
:param int index:
The index of the calibration to activate.
"""
self.execute(Sfc5xxxCmdActivateCalibration(index))
[docs] def read_user_memory(self, address, length):
"""
Read data from the user memory.
:param int address:
The address to read from.
:param int length:
Number of bytes to read.
:return:
The read data.
:rtype:
bytes
"""
return self.execute(Sfc5xxxCmdReadUserMemory(address, length))
[docs] def write_user_memory(self, address, data):
"""
Write data to the user memory.
:param int address:
The address to write to.
:param bytes-like data:
Data to write.
"""
self.execute(Sfc5xxxCmdWriteUserMemory(address, len(data), data))
[docs] def update_firmware(self, image, emergency=False, status_callback=None,
progress_callback=None):
"""Update the firmware on the device.
This method allows you to download a new firmware (provided as a
\\*.hex file) to the device. A device reset is performed after the
firmware update.
.. note:: This can take several minutes, don't abort it! If aborted,
the device stays in the bootloader and you need to restart
the update with ``emergency=True`` to recover.
.. warning:: If the Sfc5xxx is connected through RS485
together with other SHDLC devices on the same bus, make
sure that no other device has the slave address 0 and
baudrate 115200! These connection settings are used by
the bootloader and thus two devices would respond to the
bootloader commands. If an update fails because of this,
you will have to disconnect the other device with address
0 and do an emergency update to recover this device.
:param image:
The image to flash, either as a
:py::class:`~sensirion_shdlc_sfc5xxx.firmware_image.Sfc5xxxFirmwareImage`
object, a file-like object, or the filename (``str``) to the
\\*.hex file.
:param bool emergency:
Must be set to ``True`` if the device is already in bootloader
mode, ``False`` otherwise.
:param callable status_callback:
Optional callback for status report, taking a string as parameter.
:param callable progress_callback:
Optional callback for progress report, taking a float as parameter
(progress in percent).
:raises ~sensirion_shdlc_driver.errors.ShdlcFirmwareImageIncompatibilityError:
If the image is not compatible with the connected device.
:raises Exception:
On other errors.
""" # noqa: E501
if not isinstance(image, Sfc5xxxFirmwareImage):
image = Sfc5xxxFirmwareImage(image)
update = ShdlcFirmwareUpdate(self, image,
status_callback=status_callback,
progress_callback=progress_callback)
update.execute(emergency=emergency)