Friday, October 19, 2012

Serial Line Internet Protocol (SLIP) implementation in Python

Sometime back I had a requirement to communicate with an external device which is connected to a USB port of my computer. The external device was a MSB430 sensor mote running contiki on it. My application running on the mote communicate with the computer using SLIP protocol. Therefore I needed to make my program on the PC to communicate using SLIP protocol over the USB port with the sensor mote.


Serial Line Internet Protocol (SLIP) is a very simple protocol which can be used to communicate between different devices over serial lines. It just encodes the users data byte streams before writing to the serial line and decode after reading from the serial line. I found a C language implementation of the SLIP protocol but I wanted a Python implementation. So first thing I did was searching through the Internet for a python based implementation of SLIP. But I couldn't find anything. Finally what I had to do is implement it on my own. Since there can be more people who need such a Python based implementation of SLIP protocol, I thought to put my code in the Internet.

My code consists of two source files. SLIP protocol encoding and decoding functions are defined in the ProtoSLIP.py file. Another file named as SerialComm.py wraps around those functions and provide some high-level functions which can be used by a user program to open a serial port, write data to it and read data from it using SLIP protocol. So, here we go.

Content of the ProtoSLIP.py file

1:  import termios  
2:  import serial  
3:  from collections import deque  
4:  SLIP_END = 0300          # declared in octal  
5:  SLIP_ESC = 0333    
6:  SLIP_ESC_END = 0334  
7:  SLIP_ESC_ESC = 0335  
8:  DEBUG_MAKER = 0015  
9:  MAX_MTU = 200  
10:  readBufferQueue = deque([])  
11:  #-------------------------------------------------------------------------------  
12:  # This function takes a byte list, encode it in SLIP protocol and return the encoded byte list  
13:  def encodeToSLIP(byteList):  
14:       tempSLIPBuffer = []  
15:       tempSLIPBuffer.append(SLIP_END)  
16:       for i in byteList:  
17:            if i == SLIP_END:  
18:                 tempSLIPBuffer.append(SLIP_ESC)  
19:                 tempSLIPBuffer.append(SLIP_ESC_END)  
20:            elif i == SLIP_ESC:  
21:                 tempSLIPBuffer.append(SLIP_ESC)  
22:                 tempSLIPBuffer.append(SLIP_ESC_ESC)  
23:            else:  
24:                 tempSLIPBuffer.append(i)  
25:       tempSLIPBuffer.append(SLIP_END)  
26:       return tempSLIPBuffer  
27:  #-------------------------------------------------------------------------------  
28:  #-------------------------------------------------------------------------------  
29:  # This function uses getSerialByte() function to get SLIP encoded bytes from the serial port and return a decoded byte list  
30:  def decodeFromSLIP(serialFD):  
31:       dataBuffer = []  
32:       while 1:  
33:            serialByte = getSerialByte(serialFD)  
34:            if serialByte is None:  
35:                 return -1  
36:            elif serialByte == SLIP_END:  
37:                 if len(dataBuffer) > 0:  
38:                      return dataBuffer  
39:            elif serialByte == SLIP_ESC:  
40:                 serialByte = getSerialByte(serialFD)  
41:                 if serialByte is None:  
42:                      return -1  
43:                 elif serialByte == SLIP_ESC_END:  
44:                      dataBuffer.append(SLIP_END)  
45:                 elif serialByte == SLIP_ESC_ESC:  
46:                      dataBuffer.append(SLIP_ESC)  
47:                 elif serialByte == DEBUG_MAKER:  
48:                      dataBuffer.append(DEBUG_MAKER)  
49:                 else:  
50:                      print("Protocol Error")  
51:            else:  
52:                 dataBuffer.append(serialByte)  
53:       return            
54:  #-------------------------------------------------------------------------------  
55:  #-------------------------------------------------------------------------------  
56:  # This function read byte chuncks from the serial port and return one byte at a time  
57:  def getSerialByte(serialFD):       
58:       if len(readBufferQueue) == 0:  
59:            #fetch a new data chunk from the serial port       
60:            i = 0  
61:            while len(readBufferQueue) < MAX_MTU:  
62:                 newByte = ord(serialFD.read())  
63:                 readBufferQueue.append(newByte)  
64:            newByte = readBufferQueue.popleft()  
65:            return newByte  
66:       else:  
67:            newByte = readBufferQueue.popleft()  
68:            return newByte  
69:  #-------------------------------------------------------------------------------  

Content of the SerialComm.py file

1:  import ProtoSLIP  
2:  import termios  
3:  import serial  
4:  #-------------------------------------------------------------------------------  
5:  # This function connect and configure the serial port. Then returns the file discripter  
6:  def connectToSerialPort():  
7:       serialFD = serial.Serial(port='/dev/ttyUSB0', baudrate=115200, bytesize=8, parity='N', stopbits=1, xonxoff=False, rtscts=False)  
8:       # port='/dev/ttyUSB0'- port to open  
9:       # baudrate=115200  - baud rate to communicate with the port  
10:       # bytesize=8           - size of a byte  
11:       # parity='N'           - no parity  
12:       # stopbits=1           - 1 stop bit  
13:       # xonxoff=False           - no software handshakes  
14:       # rtscts=False           - no hardware handshakes  
15:       if serialFD < 0:  
16:            print("Couldn't open serial port")  
17:            return -1  
18:       else:  
19:            print("Opened serial port")  
20:            return serialFD  
21:  #-------------------------------------------------------------------------------  
22:  #-------------------------------------------------------------------------------  
23:  # This function accept a byte array and write it to the serial port  
24:  def writeToSerialPort(serialFD, byteArray):  
25:       encodedSLIPBytes = ProtoSLIP.encodeToSLIP(byteArray)  
26:       byteString = ''.join(chr(b) for b in encodedSLIPBytes) #convert byte list to a string  
27:       serialFD.write(byteString)  
28:       return  
29:  #-------------------------------------------------------------------------------  
30:  #-------------------------------------------------------------------------------  
31:  # This function reads from the serial port and return a byte array  
32:  def readFromSerialPort(serialFD):  
33:       i = 1  
34:       byteArray = None  
35:       byteArray = ProtoSLIP.decodeFromSLIP(serialFD)  
36:       if byteArray is None:  
37:            print "readFromSerialPort(serialFD): Error"  
38:            return -1  
39:       else:  
40:            return byteArray  
41:  #-------------------------------------------------------------------------------  
42:  #-------------------------------------------------------------------------------  
43:  # This function reads from the serial port and return a byte array  
44:  def disconnectFromSerialPort(serialFD):  
45:       serialFD.close()  
46:       return  
47:  #-------------------------------------------------------------------------------  

SerialComm.py file should be imported from a user program and call the functions appropriately. I hope comments I have put in the code will make understanding of functionality of the program clear enough. Some information like the exact serial port we are opening, baud rate, parity, etc has to be edited in the code according to the requirement.

I hope my code will help someone. Cheers!



6 comments:

  1. Nice! would you mind hosting it on bitbucket or github? A lot more people would find it that way

    ReplyDelete
  2. Good idea! :) I have never hosted any code in github. I will put this as you suggested :)

    ReplyDelete
  3. Hi Ansanka,

    I put your useful code on github under public domain license and reference to this page: https://github.com/mehdix/pyslip/

    ReplyDelete
    Replies
    1. Thank you so much Mehdi :) It will be so useful to everybody now!

      Delete
  4. Hi, Someone know a c/c++ library for read/write data over serial port, i know already how to send, receive data, BUT sometime i have somes errors.
    There is a library to handle error ?
    Thanks

    ReplyDelete
  5. 1. there is a python library which is called "sliplib". it has the function encode and decode. does this do the same encoding and decoding like you did here?.
    2. reading your article i concluded that slip basically do not make any connection. on the other side it just add some values with actual data. do you confirm it ?

    ReplyDelete