Jesh Kundem
Jesh Kundem

Reputation: 974

Small Example for pyserial using Threading

Can anyone please give me a small and simple example on how to use threading with pyserial communication. I am googling for over three days and I am still illeterate and I dont even have a working piece of code which integrate both of them

Basically I am aiming to use threading in this scenario:

Have a serial communication continuously go on in the back ground to attain certain value (say A) from an MCU.

Stop attaining value A - then attain value B...and start continuously attaining value A again.

You can find some basic code here.

import threading
import time
import sys
import serial
import os
import time

def Task1(ser):

    while 1:

        print "Inside Thread 1"
        ser.write('\x5A\x03\x02\x02\x02\x09') # Byte ArrayTo Control a MicroProcessing Unit
        b = ser.read(7)
        print b.encode('hex')
        print "Thread 1 still going on"
        time.sleep(1)


def Task2(ser):

    print "Inside Thread 2"
    print "I stopped Task 1 to start and execute Thread 2"
    ser.write('x5A\x03\x02\x08\x02\x0F')
    c = ser.read(7)
    print c.encode('hex')
    print "Thread 2 complete"


def Main():
    ser = serial.Serial(3, 11520)
    t1 = threading.Thread(target = Task1, args=[ser])
    t2 = threading.Thread(target = Task2, args=[ser])
    print "Starting Thread 1"
    t1.start()
    print "Starting Thread 2"
    t2.start()

    print "=== exiting ==="

    ser.close()

if __name__ == '__main__':

    Main()

Upvotes: 1

Views: 22228

Answers (1)

jez
jez

Reputation: 15349

There's no factual basis for the claim by Task2:

print "I stopped Task 1 to start and execute Thread 2"

Your implementation starts one thread then immediately starts the other without stopping the first. This means that the ser.read and ser.write commands could interfere with each other.

The simplest thing you could do to address this is to introduce variables that allow communication:

import sys
import os
import time
import threading

thread_flag = None


def Report(s):
    print s
    sys.stdout.flush() # helps to ensure messages from different threads appear in the right order

def Stop():
    global thread_flag
    thread_flag = 'stop'

def Task1(ser):

    Report("Inside Thread 1")
    global thread_flag
    thread_flag = 'go'

    while True:

        Report("Thread 1 waiting for permission to read")
        while thread_flag != 'go': time.sleep( 0.001 )

        while thread_flag == 'go':
            Report("Thread 1 is reading")
            #ser.write('\x5A\x03\x02\x02\x02\x09') # Byte ArrayTo Control a MicroProcessing Unit
            #b = ser.read(7)
            #Report(b.encode('hex'))
            time.sleep(1)

        if thread_flag == 'stop': break
        else: thread_flag = 'paused'   # signals that the inner loop is done

    Report("Thread 1 complete")


def Task2(ser):

    Report("Inside Thread 2")

    global thread_flag
    thread_flag = 'pause' # signals Task1 to pause
    while thread_flag != 'paused': time.sleep(0.001) # waits for Task1 inner loop to exit
    Report("I stopped Task 1 to start and execute Thread 2")

    #ser.write('x5A\x03\x02\x08\x02\x0F')
    #c = ser.read(7)
    #Report(c.encode('hex'))

    thread_flag = 'go' # signals Thread 1 to resume
    Report("Thread 2 complete")


def Main():
    ser = None # serial.Serial(3, 11520)
    t1 = threading.Thread(target = Task1, args=[ser])
    t2 = threading.Thread(target = Task2, args=[ser])
    Report("Starting Thread 1")
    t1.start()

    time.sleep(3)
    Report("Starting Thread 2")
    t2.start()


if __name__ == '__main__':

    Main()

That approach uses a global variable, which is often frowned upon. You could instead make Task1 and Task2 methods of an object self that carries both self.ser and self.thread_flag as attributes.

For any inter-thread communication more complex than this, you need to investigate locks, mutexes and semaphores (e.g. threading.Lock)

Upvotes: 0

Related Questions