jamx
jamx

Reputation: 11

python serial 100% cpu

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()

Upvotes: 1

Views: 2006

Answers (2)

Stefan
Stefan

Reputation: 4317

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.

Upvotes: 1

Kiwi
Kiwi

Reputation: 2816

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.

Upvotes: 2

Related Questions