Created
November 19, 2011 10:23
-
-
Save pklaus/1378695 to your computer and use it in GitHub Desktop.
Python Code to communicate with the 6 channel Pfeiffer Vacuum TPG256A MaxiGauge pressure gauge controller via a serial connection
This file contains hidden or 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
#!/usr/bin/env python | |
### Load the module: | |
from PfeifferVacuum import MaxiGauge, MaxiGaugeError | |
import time | |
import sys | |
### Initialize an instance of the MaxiGauge controller with | |
### the handle of the serial terminal it is connected to | |
mg = MaxiGauge('/dev/ttyUSB1') | |
### Read out the pressure gauges | |
while True: | |
startTime = time.time() | |
try: | |
ps = mg.pressures() | |
except MaxiGaugeError, mge: | |
print mge | |
continue | |
line = "" | |
for sensor in ps: | |
#print sensor values | |
if sensor.status in [0,1,2]: # if normal, over or under range | |
line += str(sensor.pressure) | |
line += ", " | |
print line[0:-2] # omit the last comma and space | |
sys.stdout.flush() | |
# do this every second | |
endTime = time.time()-startTime | |
time.sleep(max([0.0, 1.0-endTime])) |
This file contains hidden or 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
#!/usr/bin/env python | |
### Load the module: | |
from PfeifferVacuum import MaxiGauge | |
### Initialize an instance of the MaxiGauge controller with | |
### the handle of the serial terminal it is connected to | |
mg = MaxiGauge('/dev/ttyUSB0') | |
### Run the self check (not needed) | |
print(mg.checkDevice()) | |
### Set device characteristics (here: change the display contrast) | |
print("Set the display contrast to: %d" % mg.displayContrast(10)) | |
### Read out the pressure gauges | |
print(mg.pressures()) | |
### Display the value of the pressure gauges for 20 repeated read outs | |
for i in range(20): | |
ps = mg.pressures() | |
print "Sensor 1: %4e mbar" % ps[0].pressure + "Sensor 6: %4e mbar" % ps[5].pressure |
This file contains hidden or 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
#!/usr/bin/env python | |
# -*- encoding: UTF8 -*- | |
# Author: Philipp Klaus, philipp.l.klaus AT web.de | |
# This file is part of PfeifferVacuum.py. | |
# | |
# PfeifferVacuum.py is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# PfeifferVacuum.py is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with PfeifferVacuum.py. If not, see <http://www.gnu.org/licenses/>. | |
### This module depends on PySerial, a cross platform Python module | |
### to leverage the communication with the serial port. | |
### http://pyserial.sourceforge.net/pyserial.html#installation | |
### If you have `pip` installed on your computer, getting PySerial is as easy as | |
### pip install pyserial | |
import serial | |
class MaxiGauge (object): | |
def __init__(self, serialPort, baud=9600, debug=False): | |
self.debug=debug | |
try: | |
self.connection = serial.Serial(serialPort, baudrate=baud, timeout=0.2) | |
except serial.serialutil.SerialException as se: | |
raise MaxiGaugeError(se) | |
#self.send(C['ETX']) ### We might reset the connection first, but it doesn't really matter: | |
def checkDevice(self): | |
message = "The Display Contrast is currently set to %d (out of 20).\n" % self.displayContrast() | |
message += "Keys since MaxiGauge was switched on: %s (out of 1,2,3,4,5).\n" % ", ".join( map (str, self.pressedKeys()) ) | |
return message | |
def pressedKeys(self): | |
keys = int(self.send('TKB',1)[0]) | |
pressedKeys = [] | |
for i in [4,3,2,1,0]: # It's got 5 keys | |
if keys/2**i == 1: | |
pressedKeys.append(i+1) | |
keys = keys%2**i | |
pressedKeys.reverse() | |
return pressedKeys | |
def displayContrast(self,newContrast=-1): | |
if newContrast == -1: return int(self.send('DCC',1)[0]) | |
else: return int(self.send('DCC,%d' % (newContrast,) ,1)[0]) | |
def pressures(self): | |
return [self.pressure(i+1) for i in range(6)] | |
def pressure(self, sensor): | |
if sensor < 1 or sensor >6: raise MaxiGaugeError('Sensor can only be between 1 and 6. You choose ' + str(sensor)) | |
reading = self.send('PR%d' % sensor, 1) ## reading will have the form x,x.xxxEsx <CR><LF> (see p.88) | |
try: | |
r = reading[0].split(',') | |
status = int(r[0]) | |
pressure = float(r[-1]) | |
except: | |
raise MaxiGaugeError("Problem interpreting the returned line:\n%s" % reading) | |
return PressureReading(sensor, status, pressure) | |
def debugMessage(self, message): | |
if self.debug: print(repr(message)) | |
def send(self, mnemonic, numEnquiries = 0): | |
self.connection.flushInput() | |
self.write(mnemonic+LINE_TERMINATION) | |
#if mnemonic != C['ETX']: self.read() | |
#self.read() | |
self.getACQorNAK() | |
response = [] | |
for i in range(numEnquiries): | |
self.enquire() | |
response.append(self.read()) | |
return response | |
def write(self,what): | |
self.debugMessage(what) | |
self.connection.write(what) | |
def enquire(self): | |
self.write(C['ENQ']) | |
def read(self): | |
data = "" | |
while True: | |
x = self.connection.read() | |
self.debugMessage(x) | |
data += x | |
if len(data)>1 and data[-2:]==LINE_TERMINATION: | |
break | |
return data[:-len(LINE_TERMINATION)] | |
def getACQorNAK(self): | |
returncode = self.connection.readline() | |
self.debugMessage(returncode) | |
## The following is usually expected but our MaxiGauge controller sometimes forgets this parameter... That seems to be a bug with the DCC command. | |
#if len(returncode)<3: raise MaxiGaugeError('Only received a line termination from MaxiGauge. Was expecting ACQ or NAK.') | |
if len(returncode)<3: self.debugMessage('Only received a line termination from MaxiGauge. Was expecting ACQ or NAK.') | |
if len(returncode)>2 and returncode[-3] == C['NAK']: | |
self.enquire() | |
returnedError = self.read() | |
error = str(returnedError).split(',' , 1) | |
print repr(error) | |
errmsg = { 'System Error': ERR_CODES[0][int(error[0])] , 'Gauge Error': ERR_CODES[1][int(error[1])] } | |
raise MaxiGaugeNAK(errmsg) | |
#if len(returncode)>2 and returncode[-3] != C['ACQ']: raise MaxiGaugeError('Expecting ACQ or NAK from MaxiGauge but neither were sent.') | |
if len(returncode)>2 and returncode[-3] != C['ACQ']: self.debugMessage('Expecting ACQ or NAK from MaxiGauge but neither were sent.') | |
# if no exception raised so far, the interface is just fine: | |
return returncode[:-(len(LINE_TERMINATION)+1)] | |
def disconnect(self): | |
#self.send(C['ETX']) | |
if hasattr(self, 'connection') and self.connection: self.connection.close() | |
def __del__(self): | |
self.disconnect() | |
class PressureReading(object): | |
def __init__(self, id, status, pressure): | |
if int(id) not in range(1,7): raise MaxiGaugeError('Pressure Gauge ID must be between 1-6') | |
self.id = int(id) | |
if int(status) not in PRESSURE_READING_STATUS.keys(): raise MaxiGaugeError('The Pressure Status must be in the range %s' % PRESSURE_READING_STATUS.keys()) | |
self.status = int(status) | |
self.pressure = float(pressure) | |
def statusMsg(self): | |
return PRESSURE_READING_STATUS[self.status] | |
def __repr__(self): | |
return "Gauge #%d: Status %d (%s), Pressure: %f mbar\n" % (self.id, self.status, self.statusMsg(), self.pressure) | |
### ------ now we define the exceptions that could occur ------ | |
class MaxiGaugeError(Exception): | |
pass | |
class MaxiGaugeNAK(MaxiGaugeError): | |
pass | |
### ------- Control Symbols as defined on p. 81 of the english | |
### manual for the Pfeiffer Vacuum TPG256A ----------- | |
C = { | |
'ETX': "\x03", # End of Text (Ctrl-C) Reset the interface | |
'CR': "\x0D", # Carriage Return Go to the beginning of line | |
'LF': "\x0A", # Line Feed Advance by one line | |
'ENQ': "\x05", # Enquiry Request for data transmission | |
'ACQ': "\x06", # Acknowledge Positive report signal | |
'NAK': "\x15", # Negative Acknowledge Negative report signal | |
'ESC': "\x1b", # Escape | |
} | |
LINE_TERMINATION=C['CR']+C['LF'] # CR, LF and CRLF are all possible (p.82) | |
### Mnemonics as defined on p. 85 | |
M = [ | |
'BAU', # Baud rate Baud rate 95 | |
'CAx', # Calibration factor Sensor x Calibration factor sensor x (1 ... 6) 92 | |
'CID', # Measurement point names Measurement point names 88 | |
'DCB', # Display control Bargraph Bargraph 89 | |
'DCC', # Display control Contrast Display control contrast 90 | |
'DCD', # Display control Digits Display digits 88 | |
'DCS', # Display control Screensave Display control screensave 90 | |
'DGS', # Degas Degas 93 | |
'ERR', # Error Status Error status 97 | |
'FIL', # Filter time constant Filter time constant 92 | |
'FSR', # Full scale range of linear sensors Full scale range of linear sensors 93 | |
'LOC', # Parameter setup lock Parameter setup lock 91 | |
'NAD', # Node (device) address for RS485 Node (device) address for RS485 96 | |
'OFC', # Offset correction Offset correction 93 | |
'OFC', # Offset correction Offset correction 93 | |
'PNR', # Program number Program number 98 | |
'PRx', # Status, Pressure sensor x (1 ... 6) Status, Pressure sensor x (1 ... 6) 88 | |
'PUC', # Underrange Ctrl Underrange control 91 | |
'RSX', # Interface Interface 94 | |
'SAV', # Save default Save default 94 | |
'SCx', # Sensor control Sensor control 87 | |
'SEN', # Sensor on/off Sensor on/off 86 | |
'SPx', # Set Point Control Source for Relay xThreshold value setting, Allocation 90 | |
'SPS', # Set Point Status A,B,C,D,E,F Set point status 91 | |
'TAI', # Test program A/D Identify Test A/D converter identification inputs 100 | |
'TAS', # Test program A/D Sensor Test A/D converter measurement value inputs 100 | |
'TDI', # Display test Display test 98 | |
'TEE', # EEPROM test EEPROM test 100 | |
'TEP', # EPROM test EPROM test 99 | |
'TID', # Sensor identification Sensor identification 101 | |
'TKB', # Keyboard test Keyboard test 99 | |
'TRA', # RAM test RAM test 99 | |
'UNI', # Unit of measurement (Display) Unit of measurement (pressure) 89 | |
'WDT', # Watchdog and System Error Control Watchdog and system error control 101 | |
] | |
### Error codes as defined on p. 97 | |
ERR_CODES = [ | |
{ | |
0: 'No error', | |
1: 'Watchdog has responded', | |
2: 'Task fail error', | |
4: 'IDCX idle error', | |
8: 'Stack overflow error', | |
16: 'EPROM error', | |
32: 'RAM error', | |
64: 'EEPROM error', | |
128: 'Key error', | |
4096: 'Syntax error', | |
8192: 'Inadmissible parameter', | |
16384: 'No hardware', | |
32768: 'Fatal error' | |
} , | |
{ | |
0: 'No error', | |
1: 'Sensor 1: Measurement error', | |
2: 'Sensor 2: Measurement error', | |
4: 'Sensor 3: Measurement error', | |
8: 'Sensor 4: Measurement error', | |
16: 'Sensor 5: Measurement error', | |
32: 'Sensor 6: Measurement error', | |
512: 'Sensor 1: Identification error', | |
1024: 'Sensor 2: Identification error', | |
2048: 'Sensor 3: Identification error', | |
4096: 'Sensor 4: Identification error', | |
8192: 'Sensor 5: Identification error', | |
16384: 'Sensor 6: Identification error', | |
} | |
] | |
### pressure status as defined on p.88 | |
PRESSURE_READING_STATUS = { | |
0: 'Measurement data okay', | |
1: 'Underrange', | |
2: 'Overrange', | |
3: 'Sensor error', | |
4: 'Sensor off', | |
5: 'No sensor', | |
6: 'Identification error' | |
} |
This file contains hidden or 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
#!/usr/bin/env python | |
### Load the module: | |
from PfeifferVacuum import MaxiGauge, MaxiGaugeError | |
import time | |
import sys | |
### Initialize an instance of the MaxiGauge controller with | |
### the handle of the serial terminal it is connected to | |
mg = MaxiGauge('/dev/ttyUSB1') | |
logfile = file('measurement-data.txt', 'a') | |
### Read out the pressure gauges | |
while True: | |
startTime = time.time() | |
try: | |
ps = mg.pressures() | |
except MaxiGaugeError, mge: | |
print mge | |
continue | |
line = "" | |
for sensor in ps: | |
#print sensor | |
if sensor.status in [0,1,2]: | |
line += "%.3E" % sensor.pressure | |
line += ", " | |
print line[0:-2] # omit the last comma and space | |
sys.stdout.flush() | |
logfile.write(line+'\n') | |
logfile.flush() | |
# do this every second | |
endTime = time.time()-startTime | |
time.sleep(max([0.0, 1. - endTime])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment