Skip to content
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

drivers/mks_g_series.py many updates: Python 3 only, NAK support, com… #70

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 62 additions & 30 deletions PyExpLabSys/drivers/mks_g_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,27 @@
import time
import logging
import serial
from PyExpLabSys.common.supported_versions import python2_and_3

LOGGER = logging.getLogger(__name__)
LOGGER.addHandler(logging.NullHandler())
python2_and_3(__file__)

NAK_TABLE = {
'01': 'Checksum error',
'10': 'Syntax error',
'11': 'Data length error',
'12': 'Invalid data',
'13': 'Invalid operating mode',
'14': 'Invalid action',
'15': 'Invalid gas',
'16': 'Invalid control mode',
'17': 'Invalid command',
'24': 'Calibration error',
'25': 'Flow too large',
'27': 'Too many gases in gas table',
'28': 'Flow cal error, valve not open',
'98': 'Internal device error',
'99': 'Internal device error',
}


class MksGSeries:
Expand All @@ -20,46 +36,57 @@ def __init__(self, port='/dev/ttyUSB0'):
self.ser.bytesize = serial.EIGHTBITS
self.ser.stopbits = serial.STOPBITS_ONE

def checksum(self, command, reply=False):
def checksum(self, command):
""" Calculate checksum of command """
if not reply:
com_string = '@' + command
else:
com_string = command
total = 0
for i in range(0, len(com_string)):
total = total + ord(com_string[i])
return (hex(total)[-2:]).upper()
com_string = '@' + command
checksum = sum([ord(i) for i in com_string])
# The following hack deviates from the manual, but has proven to work on two different setups
if 'NAK' in com_string:
checksum += 9
return hex(checksum)[-2:].upper()

def comm(self, command, addr):
""" Implements communication protocol """
# TODO: add retries on comm errors
com_string = str(addr).zfill(3) + command + ';'
checksum = self.checksum(com_string)
com_string = '@@@@' + com_string + checksum
com_string = '@@@' + com_string + checksum
com_string = com_string.encode('ascii')
self.ser.write(com_string)
time.sleep(0.1)
reply = self.ser.read(self.ser.inWaiting())
try:
reply = reply.decode('ascii')
except UnicodeDecodeError:
reply = reply.decode('ascii', 'ignore')
reply = reply.strip('\x00')
reply = '@' + reply

raw_reply = self.ser.read(self.ser.inWaiting())
# In case that noise adds zeros on either side:
reply = raw_reply.strip(b'\x00')
reply = reply.decode('ascii')
if len(reply) == 0:
LOGGER.warning('No such device')
else:
if reply[-3:] == self.checksum(reply[1:-3], reply=True):
reply = reply[6:-3] # Cut away master address and checksum
return ''
# Response structure: '@@@000' + 'ACK' + content + ';' + 2-char-checksum
noise_check_passed = reply.find(';') == len(reply) - 3
ack = reply[6:9]
content = reply[9:-3]
crc = reply[-2:]
if noise_check_passed:
KennethNielsen marked this conversation as resolved.
Show resolved Hide resolved
# Calculate checksum (the first @ is added again during calc)
if crc == self.checksum(reply[1:-2]):
if ack == 'ACK':
return content
elif ack == 'NAK':
message = NAK_TABLE[content]
print('NAK error {} in command: {}'.format(content, message))
LOGGER.warning(
'NAK error {} in command: {}'.format(content, message)
)
return ''
else:
LOGGER.warning('Unexpected response: {}'.format(repr(raw_reply)))
return ''
else:
LOGGER.error('Checksum error in reply')
reply = ''
if reply[1:4] == 'ACK':
reply = reply[4:-3]
else:
LOGGER.warning('Error in command')
return reply
return ''
else:
LOGGER.warning('Response seemingly too noisy: {}'.format(repr(raw_reply)))
return ''

def read_full_scale_range(self, addr):
""" Read back the current full scale range from the instrument """
Expand Down Expand Up @@ -132,9 +159,14 @@ def read_serial_number(self, addr=254):
command = 'SN?'
return self.comm(command, addr)

def read_internal_temperature(self, addr=254):
""" Return the internal temperature in degrees Celcius """
command = 'TA?'
return self.comm(command, addr)


if __name__ == '__main__':
MKS = MksGSeries()
print(MKS.read_serial_number(1))
print(MKS.read_full_scale_range(1))
# print(MKS.set_device_address(254,005))
# print(MKS.set_device_address(254, 005))