* added cmd line script to read out and set flow control (red-y series by Voegtlin)
This commit is contained in:
parent
6dc67edaaf
commit
50b6bf6622
210
src/tools/flow_control.py
Executable file
210
src/tools/flow_control.py
Executable file
@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
import optparse
|
||||||
|
import os, sys
|
||||||
|
import serial
|
||||||
|
import numpy as N
|
||||||
|
import struct
|
||||||
|
import binascii
|
||||||
|
import time
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
"""
|
||||||
|
6.2.2 CRC Generation
|
||||||
|
====================
|
||||||
|
The Cyclical Redundancy Checking (CRC) field is two bytes, containing a 16–bit binary value. The CRC value is calculated by the
|
||||||
|
transmitting device, which appends the CRC to the message. The device that receives recalculates a CRC during receipt of the
|
||||||
|
message, and compares the calculated value to the actual value it received in the CRC field. If the two values are not equal, an error
|
||||||
|
results.
|
||||||
|
|
||||||
|
The CRC is started by first preloading a 16–bit register to all 1’s. Then a process begins of applying successive 8–bit bytes of the
|
||||||
|
message to the current contents of the register. Only the eight bits of data in each character are used for generating the CRC. Start
|
||||||
|
and stop bits and the parity bit, do not apply to the CRC.
|
||||||
|
During generation of the CRC, each 8–bit character is exclusive ORed with the register contents. Then the result is shifted in the
|
||||||
|
direction of the least significant bit (LSB), with a zero filled into the most significant bit (MSB) position. The LSB is extracted and
|
||||||
|
examined. If the LSB was a 1, the register is then exclusive ORed with a preset, fixed value. If the LSB was a 0, no exclusive OR takes
|
||||||
|
place.
|
||||||
|
|
||||||
|
This process is repeated until eight shifts have been performed. After the last (eighth) shift, the next 8–bit character is exclusive ORed
|
||||||
|
with the register’s current value, and the process repeats for eight more shifts as described above. The final content of the register,
|
||||||
|
after all the characters of the message have been applied, is the CRC value.
|
||||||
|
A procedure for generating a CRC is:
|
||||||
|
|
||||||
|
1. Load a 16–bit register with FFFF hex (all 1’s). Call this the CRC register.
|
||||||
|
2. Exclusive OR the first 8–bit byte of the message with the low–order byte of the 16–bit CRC register, putting the result in the
|
||||||
|
CRC register.
|
||||||
|
3. Shift the CRC register one bit to the right (toward the LSB), zero–filling the MSB. Extract and examine the LSB.
|
||||||
|
4. (If the LSB was 0): Repeat Step 3 (another shift).
|
||||||
|
(If the LSB was 1): Exclusive OR the CRC register with the polynomial value 0xA001 (1010 0000 0000 0001).
|
||||||
|
5. Repeat Steps 3 and 4 until 8 shifts have been performed. When this is done, a complete 8–bit byte will have been
|
||||||
|
processed.
|
||||||
|
6. Repeat Steps 2 through 5 for the next 8–bit byte of the message. Continue doing this until all bytes have been processed.
|
||||||
|
7. The final content of the CRC register is the CRC value.
|
||||||
|
8. When the CRC is placed into the message, its upper and lower bytes must be swapped as described below.
|
||||||
|
|
||||||
|
Placing the CRC into the Message
|
||||||
|
When the 16–bit CRC (two 8–bit bytes) is transmitted in the message, the low-order byte will be transmitted first, followed by the high-
|
||||||
|
order byte.
|
||||||
|
"""
|
||||||
|
|
||||||
|
crc_test = "\x0d\x01\x00\x62\x00\x33"
|
||||||
|
crc_expected = 0xddd
|
||||||
|
|
||||||
|
|
||||||
|
def crc(message):
|
||||||
|
crc = 0xffff # step 1
|
||||||
|
for byte in message:
|
||||||
|
if type(byte) == type(str):
|
||||||
|
crc ^= ord(byte) # step 2: xor with lower byte of crc
|
||||||
|
else:
|
||||||
|
crc ^= byte # step 2: xor with lower byte of crc
|
||||||
|
for i in range(8): # step 5: repeat 2-4 until 8 shifts were performed
|
||||||
|
if (crc & 0x1) == 0: # step 4: check LSB
|
||||||
|
mask = 0x0
|
||||||
|
else:
|
||||||
|
mask = 0xa001
|
||||||
|
crc >>= 1 # step 3: shift one bit to the right
|
||||||
|
crc ^= mask
|
||||||
|
if DEBUG: print "Message:", [i for i in message], " CRC:", hex(crc)
|
||||||
|
return chr(crc & 0xff) + chr(
|
||||||
|
(crc >> 8) & 0xff) # return lower byte, upper byte (shift to the right, return last byte) i
|
||||||
|
|
||||||
|
|
||||||
|
class Flow:
|
||||||
|
def __init__(self, device="/dev/ttyUSB0"):
|
||||||
|
self.s = serial.Serial(port=device, timeout=0.02)
|
||||||
|
self.s.readline()
|
||||||
|
self.s.timeout = 0.2
|
||||||
|
self.address = bytearray([0x3])
|
||||||
|
self.rcommands = {
|
||||||
|
"flow": [bytearray([0x3, 0x0, 0x0, 0x0, 0x2]), ">f"],
|
||||||
|
"temperature": [bytearray([0x3, 0x0, 0x2, 0x0, 0x2]), ">f"],
|
||||||
|
"setflow": [bytearray([0x3, 0x0, 0x6, 0x0, 0x2]), ">f"],
|
||||||
|
"total": [bytearray([0x3, 0x0, 0x8, 0x0, 0x2]), ">f"],
|
||||||
|
"alarm": [bytearray([0x3, 0x0, 0xc, 0x0, 0x1]), ">H"],
|
||||||
|
"hwerror": [bytearray([0x3, 0x0, 0xd, 0x0, 0x1]), ">H"],
|
||||||
|
"unit_flow": [bytearray([0x3, 0x0, 0x16, 0x0, 0x4]), ">8s"],
|
||||||
|
"medium": [bytearray([0x3, 0x00, 0x1a, 0x0, 0x4]), ">8s"],
|
||||||
|
"device": [bytearray([0x3, 0x00, 0x23, 0x0, 0x4]), ">8s"],
|
||||||
|
"unit_total": [bytearray([0x3, 0x40, 0x48, 0x0, 0x4]), ">8s"],
|
||||||
|
}
|
||||||
|
|
||||||
|
self.wcommands = {
|
||||||
|
"flow": [bytearray([0x06, 0x0, 0x6]), ">f"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def send_data(self, data_and_type):
|
||||||
|
data, type = data_and_type
|
||||||
|
if DEBUG: print "send", data_and_type, "data:", binascii.hexlify(data), "type:", type, self.address
|
||||||
|
msg = self.address + data
|
||||||
|
msg_crc = msg + crc(msg)
|
||||||
|
if DEBUG: print "send:", repr(str(msg_crc))
|
||||||
|
nbytes_sent = self.s.write(str(msg_crc))
|
||||||
|
if DEBUG: print "send:", self.s
|
||||||
|
if DEBUG: print "Sent bytes:", nbytes_sent
|
||||||
|
time.sleep(0.1) # this is necessary, otherwise no data can be received
|
||||||
|
|
||||||
|
return msg_crc
|
||||||
|
|
||||||
|
def recv_data(self):
|
||||||
|
"""
|
||||||
|
Reading Data
|
||||||
|
============
|
||||||
|
|
||||||
|
Function: 0x3
|
||||||
|
Format of the resulting string
|
||||||
|
|
||||||
|
Addr Func NoBytes Byte1...N CRCHi CRCLo
|
||||||
|
|
||||||
|
"""
|
||||||
|
message = bytearray(self.s.read(3))
|
||||||
|
if DEBUG: print "recv_data: header:", binascii.hexlify(message)
|
||||||
|
if message == "":
|
||||||
|
return
|
||||||
|
addr, func, nbytes = message
|
||||||
|
if func == 0x03:
|
||||||
|
data = self.s.read(nbytes)
|
||||||
|
if DEBUG: print "recv_data: data: %i %s" % (nbytes, binascii.hexlify(data))
|
||||||
|
message += data
|
||||||
|
crc_data = self.s.read(2)
|
||||||
|
if DEBUG: print "recv_data: crc", repr(str(crc_data))
|
||||||
|
if crc(message) != crc_data:
|
||||||
|
print "mismatch", crc(message), crc_data
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_var(self, named_type="flow"):
|
||||||
|
cmd,fmt = self.rcommands[named_type]
|
||||||
|
if DEBUG: print "get_var", cmd,fmt
|
||||||
|
self.send_data([cmd,fmt])
|
||||||
|
d = self.recv_data()
|
||||||
|
if d:
|
||||||
|
d = struct.unpack(fmt, d)[0]
|
||||||
|
return d
|
||||||
|
# msg = self.recv_data()
|
||||||
|
# print str(msg)
|
||||||
|
|
||||||
|
# float struct.unpack('>f', "4byte string")
|
||||||
|
# ushort struct.unpack('>H', "2byte string")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def current_flow(self):
|
||||||
|
# start register 0x0000
|
||||||
|
# 2 bytes
|
||||||
|
cmd = self.address + bytearray([0x3, 0x0, 0x0, 0x0, 0x2])
|
||||||
|
return cmd + crc(cmd)
|
||||||
|
|
||||||
|
def current_temperature(self):
|
||||||
|
# start register 0x0002
|
||||||
|
#
|
||||||
|
#
|
||||||
|
cmd = self.address + bytearray([0x3, 0x0, 0x2, 0x0, 0x2])
|
||||||
|
return cmd + crc(cmd)
|
||||||
|
|
||||||
|
def set_flow(self, flow):
|
||||||
|
"""
|
||||||
|
write multiple registers: 0x10
|
||||||
|
startHi
|
||||||
|
startLo
|
||||||
|
numregHi
|
||||||
|
numregLo
|
||||||
|
numbytes
|
||||||
|
"""
|
||||||
|
_flow = struct.pack(">f", flow)
|
||||||
|
# cmd = address + bytearray([0x10,0x0,0x6, 0x0, 0x2, 0x2]) + _flow
|
||||||
|
cmd1 = self.address + bytearray([0x06, 0x0, 0x6]) + _flow
|
||||||
|
self.s.write(cmd1 + crc(cmd1))
|
||||||
|
time.sleep(0.01)
|
||||||
|
# print self.s.readline()
|
||||||
|
# self.recv_data()
|
||||||
|
|
||||||
|
def cmd(self, data):
|
||||||
|
cmds = self.address + data
|
||||||
|
for i in cmds:
|
||||||
|
print hex(ord(i)),
|
||||||
|
return cmds + crc(cmds)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
conf = os.path.expanduser('~/.flow')
|
||||||
|
if os.path.exists(conf):
|
||||||
|
dev = open(conf).readlines()[0].strip()
|
||||||
|
redy = Flow(device=dev)
|
||||||
|
print "Device: %6s" % (redy.get_var("device"))
|
||||||
|
print "Medium: %6s" % (redy.get_var("medium"))
|
||||||
|
print "Current Flow: %6.2f" % (redy.get_var("flow"))
|
||||||
|
print "Current Set Flow: %6.2f" % (redy.get_var("setflow"))
|
||||||
|
print "Flow Unit: %6s" % (redy.get_var("unit_flow"))
|
||||||
|
print "Total Gas: %6.2f" % (redy.get_var("total"))
|
||||||
|
print "Total Unit: %6s" % (redy.get_var("unit_total"))
|
||||||
|
print "Current Temperature: %6.2f C" % (redy.get_var("temperature"))
|
||||||
|
print "Alarm: %8i" % (redy.get_var("alarm"))
|
||||||
|
print "HW Error: %8i" % (redy.get_var("hwerror"))
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
try:
|
||||||
|
float(sys.argv[1])
|
||||||
|
except:
|
||||||
|
raise SyntaxError, "usage: %s <flow> # to set flow or empty to get current flow\nfirst line in ~/.flow defines serial tty, default: /dev/ttyUSB0" % (sys.argv[0])
|
||||||
|
print "Set flow: %6.2f"%(float(sys.argv[1]))
|
||||||
|
redy.set_flow(float(sys.argv[1]))
|
Loading…
Reference in New Issue
Block a user