1

A year ago I needed a script to capture input from a serial device and send it to a web browser. (A touch sensor attached to a 3d printed Egyptian tablet in a Museum.) I had originally intended to use Perl but as that wasn't playing ball and I only had a few hours before launch I opted for Python (I'm not a python dev). I made a script that worked fine and has been for some time, with the only issue being that the script uses 100% CPU. How can I get Python to read from the serial port without using the whole CPU, while still bring responsive regardless of when the input is pressed?

My script is below:

#!/usr/bin/python

import time
import serial
import sys
from subprocess import call
import traceback

myport = 0

ser = serial.Serial()

def readkey():
        while 1:
                out = '';
                while ser.inWaiting() > 0:
                        out += ser.read(1);
                        if out != '\xf8' and out != '\xf9':
                                call(["xdotool", "key", "F8"])
                                call(["xdotool", "type", str(ord(out))])
                                call(["xdotool", "key", "F9"])
                        out = ''

def main_sys():
        global ser
        print "Opening Stela serial port"
        ser.open();
        ser.isOpen();
        print "Starting Stela subsystem"

        while 1:
                try:
                        readkey()
                        break
                except Exception as e:
                        print "caught os error".format(e)
                        time.sleep(1)
                        main_sys()


def init():
        global ser
        global myport
        while 1:
                try:
                        theport = '/dev/ttyACM'+str(myport)
                        print "Trying " + theport

                        ser = serial.Serial(
                                port=theport,
                                baudrate=115200,
                                parity=serial.PARITY_NONE,
                                stopbits=serial.STOPBITS_ONE,
                                bytesize=serial.EIGHTBITS
                        )
                        main_sys()
                        break;
                except Exception as e:
                        traceback.print_exc()
                        myport += 1
                        if myport > 5:
                                myport = 0
                        time.sleep(1)
                        init()

init()
0

2 Answers 2

2

Add a time.sleep for a short period at the end of your readKey-loop. It will let other processes run.

Also, be aware that call is blocking until the operation is finished.

Sign up to request clarification or add additional context in comments.

1 Comment

A time.sleep is a bit problematic as you might end up slowing down the application to the point were it can't keep up with the incomming data (since it seems to be an interactive use-case you'd probably be fine though). Also its considered bad practise.
1

I would try to avoid the ser.inWaiting() call. Instead I'd directly call ser.read(1). This way the call will block until more data is available. During this time the CPU will be free to do other things:

def readkey()
    out = ser.read(1);
    if out != '\xf8' and out != '\xf9':
            call(["xdotool", "key", "F8"])
            call(["xdotool", "type", str(ord(out))])
            call(["xdotool", "key", "F9"])

This should behave identically but with almost no CPU load.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.