-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flowmeter #220
base: master
Are you sure you want to change the base?
Flowmeter #220
Changes from 15 commits
dc81de9
a7ac683
5a4b139
a30cfbf
ef13821
0bb3236
a42bfcd
1df7aae
14db023
bdd1a4f
31d2013
c28940f
3e4a833
f2a834c
254ab29
7aa512c
babed48
b7cb27e
ec75e92
f57e199
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
|
||
import logging | ||
import struct | ||
import time | ||
|
||
from basil.HL.RegisterHardwareLayer import HardwareLayer | ||
|
||
|
@@ -16,160 +17,100 @@ | |
|
||
class Bronkhorst_ELFLOW(HardwareLayer): | ||
''' Bronkhorst ELFLOW | ||
Manual can be found here: | ||
https://www.bronkhorst.com/getmedia/77a1438f-e547-4a79-95ad-53e81fd38a97/917027-Manual-RS232-interface.pdf | ||
''' | ||
|
||
CMDS = { | ||
'get_measure_flow': ':06800401210120', | ||
'get_capacity': ':068004014D014D', | ||
'get_control_mode': ':06800401040104', | ||
'set_control_mode': ':0580010104', | ||
'set_setpoint': ':0680010121', | ||
'get_setpoint': ':06800401210121', | ||
'get_valve': ':06800472417241', | ||
} | ||
|
||
def __init__(self, intf, conf): | ||
self.debug = 0 | ||
self.node = "80" | ||
super(Bronkhorst_ELFLOW, self).__init__(intf, conf) | ||
self.pre_time = time.time() | ||
|
||
def init(self): | ||
super(Bronkhorst_ELFLOW, self).init() | ||
|
||
def write(self, cmd): | ||
cmd_s = "" | ||
for c in cmd: | ||
cmd_s = cmd_s + "%02X" % c | ||
cmd_s = ":%02X%s%s" % (len(cmd) + 1, self.node, cmd_s) | ||
if self.debug != 0: | ||
logger.debug("ELFLOW.write() %s" % str(cmd_s)) | ||
self._intf.write(cmd_s) | ||
if time.time() - self.pre_time < 1.0: | ||
time.sleep(1.0) | ||
self._intf.write(str(cmd)) | ||
self.pre_time = time.time() | ||
|
||
def read(self): | ||
ret_s = self._intf.read() | ||
if self.debug != 0: | ||
logger.debug("ELFLOW.read() %s" % str(ret_s)) | ||
if len(ret_s) < 5 or ret_s[0] != ":" or ret_s[3:5] != self.node: | ||
logger.debug("ELFLOW.read() format error ret=%s" % str(ret_s)) | ||
return [] | ||
ret_len = int(ret_s[1:3]) | ||
if ret_len * 2 != len(ret_s[3:-2]): | ||
logger.debug("ELFLOW.read() data lenth error ret=%s" % str(ret_s)) | ||
return [] | ||
ret = [] | ||
for i in range(ret_len - 1): | ||
ret.append(int(ret_s[5 + 2 * i:5 + 2 * (i + 1)], 16)) | ||
return ret | ||
ret = self._intf.read() | ||
if len(ret) < 2 or ret[-2:] != "\r\n": | ||
logger.warning("read() termination error") | ||
return ret.strip() | ||
|
||
def get_valve_output(self): | ||
self._intf.write(self.CMDS['get_valve']) | ||
ret = int(self.read()[11:], 16) | ||
return ret * 61.7 / 10345949 # converts int in percentage | ||
|
||
def set_setpoint(self, value): | ||
cmd = [1, 1, 0x21, (value >> 8) & 0xFF, value & 0xFF] | ||
self.write(cmd) | ||
"""value range from 0 - 32000 | ||
""" | ||
|
||
if not isinstance(value, int): | ||
raise ValueError(f"Given value has to be of type integer, is {type(value)}!") | ||
|
||
hex_val = hex(value)[2:] # [2:] to remove the 0x from the beginning of the hex number | ||
command = f"{self.CMDS['set_setpoint']}" + f"{hex_val.zfill(4)}" # hex should have at least 4 digits | ||
self._intf.write(command) | ||
ret = self.read() | ||
if len(ret) != 3: | ||
logger.debug("ELFLOW.set_setpoint() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 0 and ret[1] == 0 and ret[2] == 5: | ||
return 0 | ||
else: | ||
logger.debug("ELFLOW.set_setpoint() ret error ret=%s" % str(ret)) | ||
return -1 | ||
return ret | ||
|
||
def get_setpoint(self): | ||
cmd = [4, 1, 0x21, 1, 0x21] | ||
self.write(cmd) | ||
self._intf.write(self.CMDS['get_setpoint']) | ||
ret = self.read() | ||
if len(ret) != 5: | ||
logger.debug("ELFLOW.set_setpoint() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 2 and ret[1] == cmd[1] and ret[2] == cmd[2]: | ||
return ((ret[3] << 8) & 0xFF00) | (ret[4] & 0xFF) | ||
else: | ||
logger.debug("ELFLOW.set_setpoint() ret error ret=%s" % str(ret)) | ||
return -1 | ||
|
||
def set_control_mode(self, value): | ||
answer_in_hex = ret[11:] # read from the 11th digits to translate what point is set | ||
answer = int(answer_in_hex, 16) | ||
return answer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this might even be less "confusing" if you do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is changed! |
||
|
||
def set_mode(self, value): | ||
""" 0 setpoint source RS232 | ||
3 valve close | ||
4 freeze valuve out | ||
4 freeze valve out | ||
8 valve fully open | ||
20 valve steering (valve=setpoint)""" | ||
cmd = [1, 1, 4, value & 0xFF] | ||
self.write(cmd) | ||
ret = self.read() | ||
if len(ret) != 3: | ||
logger.debug("ELFLOW.set_setpoint() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 0 and ret[1] == 0 and ret[2] == 4: | ||
return 0 | ||
else: | ||
logger.debug("ELFLOW.set_setpoint() ret error ret=%s" % str(ret)) | ||
return -1 | ||
|
||
def get_control_mode(self): | ||
cmd = [4, 1, 1, 1, 4] | ||
self.write(cmd) | ||
20 valve steering """ | ||
hex_val = hex(value)[2:] # [2:] to remove the 0x from the beginning of the hex number | ||
command = f"{self.CMDS['set_control_mode']}" + f"{hex_val.zfill(2)}" # hex should have at least two digits | ||
self._intf.write(command) | ||
ret = self.read() | ||
if len(ret) != 4: | ||
logger.debug("ELFLOW.set_setpoint() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 2 and ret[1] == cmd[1] and ret[2] == cmd[2]: | ||
return ret[3] | ||
else: | ||
logger.debug("ELFLOW.set_setpoint() ret error ret=%s" % str(ret)) | ||
return -1 | ||
|
||
def set_valve_output(self, value): | ||
cmd = [1, 114, 0x41, (value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF] | ||
self.write(cmd) | ||
ret = self.read() | ||
if len(ret) != 3: | ||
logger.debug("ELFLOW.set_valve_output() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 0 and ret[1] == 0 and ret[2] == 7: | ||
return 0 | ||
else: | ||
logger.debug("ELFLOW.set_valve_output() ret error ret=%s" % str(ret)) | ||
return -1 | ||
return ret | ||
|
||
def get_valve_output(self): | ||
cmd = [4, 114, 0x41, 114, 0x41] | ||
self.write(cmd) | ||
ret = self.read() | ||
if len(ret) != 7: | ||
logger.debug("ELFLOW.set_setpoint() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 2 and ret[1] == cmd[1] and ret[2] == cmd[2]: | ||
return ((ret[3] << 24) & 0xFF000000) | ((ret[4] << 16) & 0xFF0000) | ((ret[5] << 8) & 0xFF00) | (ret[6] & 0xFF) | ||
else: | ||
logger.debug("ELFLOW.get_valve_output() ret error ret=%s" % str(ret)) | ||
return -1 | ||
|
||
def set_controller_speed(self, value): | ||
value = struct.unpack('<I', struct.pack('<f', value))[0] | ||
cmd = [1, 114, 0x40 + 30, (value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF] | ||
self.write(cmd) | ||
ret = self.read() | ||
if len(ret) != 3: | ||
logger.debug("ELFLOW.set_controller_speed() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 0 and ret[1] == 0 and ret[2] == 7: | ||
return 0 | ||
else: | ||
logger.debug("ELFLOW.set_controller_speed() ret error ret=%s" % str(ret)) | ||
return -1 | ||
|
||
def get_controller_speed(self): | ||
cmd = [4, 114, 0x41, 114, 0x40 + 30] | ||
self.write(cmd) | ||
def get_mode(self): | ||
self._intf.write(self.CMDS['get_control_mode']) | ||
ret = self.read() | ||
if len(ret) != 7: | ||
logger.debug("ELFLOW.set_setpoint() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 2 and ret[1] == cmd[1] and ret[2] == cmd[2]: | ||
return struct.unpack('!f', chr(ret[3]) + chr(ret[4]) + chr(ret[5]) + chr(ret[6]))[0] | ||
else: | ||
logger.debug("ELFLOW.get_valve_output() ret error ret=%s" % str(ret)) | ||
return -1 | ||
|
||
def get_measure(self): | ||
cmd = [4, 1, 0x21, 1, 0x20] | ||
self.write(cmd) | ||
answer_in_hex = ret[11:] # read from the 11th digits to translate what mode is on | ||
answer = int(answer_in_hex, 16) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above |
||
return answer | ||
|
||
def get_flow(self): | ||
"""This should give the flow in l/min | ||
""" | ||
|
||
# first get the max capacity in % | ||
self._intf.write(self.CMDS['get_capacity']) | ||
ret = self.read() | ||
if len(ret) != 5: | ||
logger.debug("ELFLOW.set_setpoint() data lenth error ret=%s" % str(ret)) | ||
return -1 | ||
elif ret[0] == 2 and ret[1] == cmd[1] and ret[2] == cmd[2]: | ||
return ((ret[3] << 8) & 0xFF00) | (ret[4] & 0xFF) | ||
else: | ||
logger.debug("ELFLOW.get_valve_output() ret error ret=%s" % str(ret)) | ||
return -1 | ||
answer_in_hex = ret[11:] # read from the 11th digits to translate what the capacity is | ||
cap_100 = struct.unpack('!f', bytes.fromhex(answer_in_hex))[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The capacity100 command gives the maximum capacity in the selected capacity unit (This should be L/min). However one can check the unit by using the capacityunit command and by converting the responded hex code into a string. This then should give the unit. |
||
|
||
# now measure the flow | ||
self._intf.write(self.CMDS['get_measure_flow']) | ||
ret1 = self.read() | ||
answer_in_hex = ret1[11:] | ||
answer = int(answer_in_hex, 16) | ||
|
||
val = answer / 32000 * cap_100 | ||
return val |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# | ||
# ------------------------------------------------------------ | ||
# Copyright (c) All rights reserved | ||
# SiLab, Institute of Physics, University of Bonn | ||
# ------------------------------------------------------------ | ||
# | ||
|
||
''' This script shows how to use EFLOW | ||
''' | ||
|
||
from basil.dut import Dut | ||
|
||
dut = Dut('bronkhorstELFLOW_pyserial.yaml') | ||
dut.init() | ||
|
||
# setting set point | ||
dut["hot_n2"].set_mode(0) | ||
dut["hot_n2"].set_setpoint(10000) | ||
dut["hot_n2"].set_mode(0) | ||
print("setpoint", dut["hot_n2"].get_setpoint()) | ||
|
||
# controlling valve | ||
dut["hot_n2"].set_mode(20) | ||
|
||
# measuring flow rate | ||
# print("Flow",dut["hot_n2"].get_flow()) | ||
|
||
# Measuring of valve opening in % | ||
valve = dut["hot_n2"].get_valve_output() | ||
print("Valve opened in %", valve) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the range exactly 32000 or 32767 aka 2^15? The command seems to allow 2^16 actually? Iirc, there is a large range that does not influence the effective the setting, is it this one? Not sure if we should print a warning or info or ignore it, since the user should not care? @YannickDieter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I heard @SilasM2001, you know more about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worked with the new version in the flowmeter branch. I can not commend on the previous code. The manual says setpoint has a range of 0 to 32000, with 32000 being 100%. In my experience the range works fine. The module testing setup of Matthias uses a large range (1000-25000 or so, if i recall correctly) and i did not observe a large range that does not influence the effective setting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just wanted to say again: The manual says setpoint has a range from 0 to 32000, 32000 being 100%. I dont know why the old command allowed values up to 32767.