Multiplatform Python Function to communicate 9-bit based RS-485 network

 When I was working on one of my projects on RaspberryPI I met some difficulties of using 9-bit serial communication on Linux. The problem is that in POSIX based platforms there are no MARK and SPACE parity configuration bits like in Windows. However, there is workaround. It is possible to access CMSPAR flag (control mode flag for MARK/SPACE parity) using termios module for Python. And then we are able to control MARK/SPACE by changing PARODD flag. Excellent description of problem is given here http://www.lothosoft.ch/thomas/libmip/markspaceparity.php.
    In code below I communicate using 9-bit based serial communication protocol. Code could be used both in Windows and Linux environment. 1 byte 0x55 is just for "line activation" or preamble used to clean up (discharch) any static charges on the line. Then the first "address" byte which is used to address dedicated external device, while others will just ignore all message. Address byte should have MARK parity to let external hardware to detect it as a "address" byte. All other bytes have SPACE parity and are actual data. At the and of message i usually put CRC8 control byte to eliminate any mistakes during communication.

    1. # for python version 2.7
    2.    
    3. import os
    4. import platform
    5. import serial
    6.  
    7. from time import sleep
    8. if(platform.system() == 'Windows'):
    9.     Wind_os = True
    10. else:
    11.     Wind_os = False
    12.     import termios  
    13.     if (sys.hexversion < 0x020100f0):  
    14.         import TERMIOS  
    15.     else:  
    16.         TERMIOS = termios
    17.  
    18. class Ser_Comm:
    19.     def __init__(self):
    20.         self.baudrate = "19200"
    21.         self.portname = "COM1"
    22.         self.ser_handle = False
    23.         if(not(Wind_os)): #for Linux
    24.            self.portname = "/dev/" + self.portname
    25.         self.init_serial()
    26.         print "message sent to serial"
    27.  
    28.     def init_serial(self,*args):
    29.         confs = "port %s, rate %s" % (self.portname,self.baudrate)  
    30.         try:
    31.             ser_handle = serial.Serial(self.portname,self.baudrate ,parity = serial.PARITY_NONE,timeout=0,rtscts=0)
    32.         except Exception as e:
    33.             self.close_serial()
    34.             print("Serial initialization failed: %s. Reason: %s" % (confs,e))
    35.         #simple com_array function call with crc8 at the end
    36.         addr_byte = 0x24
    37.         s_str = [ 0x02,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x42]      
    38.         self.com_array(s_str)        
    39.  
    40.     def close_serial(self):
    41.         if(self.ser_handle):
    42.           self.ser_handle.close()
    43.           self.ser_handle = False
    44.              
    45.     def com_array(self,array):
    46.       #if(len(array) > 1):
    47.       if(self.ser_handle):
    48.             self.ser_handle.write(chr(0x55))
    49.             CMSPAR = 0x40000000 #manual definition of  CMSPAR flag
    50.             if(Wind_os):
    51.                 self.close_serial()
    52.                 self.ser_handle = serial.Serial(self.portname, \
    53.                 self.baudrate,parity = serial.PARITY_MARK,timeout=0,rtscts=0)
    54.             else:
    55.                 iflag,oflag,cflag,lflag,ispeed,ospeed,cc = termios.tcgetattr(self.ser_handle)
    56.                 cflag |= TERMIOS.PARENB | CMSPAR | TERMIOS.PARODD # to select MARK parity
    57.                 termios.tcsetattr(ser_handle, termios.TCSANOW, [iflag,oflag,cflag,lflag,ispeed,ospeed,cc])
    58.             self.ser_handle.write(chr(addr_byte))
    59.          
    60.             if(Wind_os):  
    61.                 self.close_serial()
    62.                 self.ser_handle = serial.Serial(self.portname, \
    63.                 self.baudrate,parity = serial.PARITY_SPACE,timeout=0,rtscts=0)
    64.             else:
    65.                 cflag |= termios.PARENB | CMSPAR # To select SPACE parity
    66.                 cflag &= ~termios.PARODD
    67.                 termios.tcsetattr(self.ser_handle, termios.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
    68.             for hch in(array):  
    69.                 self.ser_handle.write(chr(hch))
    70.                 sleep(0.002)
    71.    
    72. app=Ser_Comm()
    Hosted by uCoz