-
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
Open
YannickDieter
wants to merge
20
commits into
master
Choose a base branch
from
flowmeter
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Flowmeter #220
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
dc81de9
Improvement on steering the valve in bronkhorst_ELflow
AntonioT7 a7ac683
parent df1180ba5c40dd45a9ea336bba19e1c4e8c0375c
AntonioT7 5a4b139
Julabo FP50: update
a30cfbf
WIP: improve bronkhorst flowmeter implementation
ef13821
WIP: add a link to the commads document
0bb3236
WIP: adding commands
a42bfcd
WIP: delete the functions of get/set control mode written by thirono
1df7aae
WIP: bring back the functions from thirono for a while
14db023
brokhorst valve control and measure N2 flow is ready
AntonioT7 bdd1a4f
corrected some comments
AntonioT7 31d2013
FIX: fix set_setpoint
SilasM2001 c28940f
ENH: fix added get_valve_output
SilasM2001 3e4a833
Added basic example
SilasM2001 f2a834c
fixing codestyle
SilasM2001 254ab29
fixing codestyle
SilasM2001 7aa512c
MAINT: Change memorized binds to dict to facilitate accessing
matthias-schuessler babed48
FIX: Fix a bug where searching for a device is stopped if a different…
matthias-schuessler b7cb27e
MAINT: Readability changes in accordance with changes requested in MR…
matthias-schuessler ec75e92
MAINT: Codestyle
matthias-schuessler f57e199
ADD: Error if value > 32000 and added comments
SilasM2001 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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,102 @@ | |
|
||
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 | ||
and here: https://www.bronkhorst.com/getmedia/257147fc-7f5d-4628-9a47-0533cf68ac08/917099-Manual-EL-FLOW-Select.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)}!") | ||
|
||
if value > 32000: | ||
raise ValueError(f"The valid range is 0 to 32000, the set value is {value}!. Setting setpoint to 32000") | ||
value = 32000 | ||
|
||
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 = int(ret[11:], 16) # read from the 11th digits to translate what point is set | ||
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) | ||
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) != 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) | ||
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) | ||
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 ((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) | ||
answer = int(ret[11:], 16) # read from the 11th digits to translate what mode is on | ||
return answer | ||
|
||
def get_flow(self): | ||
"""This should give the flow in l/min | ||
""" | ||
# first get the max capacity in capacity unit | ||
# the max capacity depends on e.g the fluid, which can be changed | ||
# Capacity unit is a command, which returns the used unit in hex | ||
self._intf.write(self.CMDS['get_capacity']) | ||
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) | ||
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) | ||
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 | ||
cap_100 = struct.unpack('!f', bytes.fromhex(ret[11:]))[0] # read from the 11th digits to translate what the capacity is | ||
|
||
# now measure the flow | ||
self._intf.write(self.CMDS['get_measure_flow']) | ||
ret1 = self.read() | ||
answer = int(ret1[11:], 16) # convert reply from hex to integer | ||
|
||
val = answer / 32000 * cap_100 | ||
return val |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.