1

I am connecting my Ubuntu box to a wireless readout setup over Bluetooth. I wrote a Python script to send the serial information through /dev/rfcomm0. The script connects fine and works for a few minutes, but then Python will start using 100% CPU and the messages stop flowing through.

I can open rfcomm0 in a serial terminal and communicate through it by hand just fine. When I open it through a terminal it seems to work indefinitely. Also, I can swap the Bluetooth receiver for a USB cable, and change the port to /dev/ttyUSB0, and I don't get any problems over time.

It seems either I'm doing something wrong with rfcomm0 or PySerial doesn't handle it well.

Here's the script:

import psutil
import serial
import string
import time

sampleTime = 1
numSamples = 5
lastTemp = 0

TEMP_CHAR = 't'
USAGE_CHAR = 'u'
SENSOR_NAME = 'TC0D'

gauges = serial.Serial()
gauges.port = '/dev/rfcomm0'
gauges.baudrate = 9600
gauges.parity = 'N'
gauges.writeTimeout = 0
gauges.open()

print("Connected to " + gauges.portstr)

filename = '/sys/bus/platform/devices/applesmc.768/temp2_input'

def parseSensorsOutputLinux(output):
    return int(round(float(output) / 1000))

while(1):
    usage = psutil.cpu_percent(interval=sampleTime)
    gauges.write(USAGE_CHAR)
    gauges.write(chr(int(usage))) #write the first byte
    #print("Wrote usage: " + str(int(usage)))

    sensorFile = open(filename)
    temp = parseSensorsOutputLinux(sensorFile.read())
    gauges.write(TEMP_CHAR)
    gauges.write(chr(temp))
    #print("Wrote temp: " + str(temp))

Any thoughts?

Thanks.

EDIT: Here is the revised code, using Python-BlueZ instead of PySerial:

import psutil
import serial
import string
import time
import bluetooth

sampleTime = 1
numSamples = 5
lastTemp = 0

TEMP_CHAR = 't'
USAGE_CHAR = 'u'
SENSOR_NAME = 'TC0D'

#gauges = serial.Serial()
#gauges.port = '/dev/rfcomm0'
#gauges.baudrate = 9600
#gauges.parity = 'N'
#gauges.writeTimeout = 0
#gauges.open()

gaugeSocket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
gaugeSocket.connect(('00:06:66:42:22:96', 1))

filename = '/sys/bus/platform/devices/applesmc.768/temp2_input'

def parseSensorsOutputLinux(output):
    return int(round(float(output) / 1000))

while(1):
    usage = psutil.cpu_percent(interval=sampleTime)
    #gauges.write(USAGE_CHAR)
    gaugeSocket.send(USAGE_CHAR)
    #gauges.write(chr(int(usage))) #write the first byte
    gaugeSocket.send(chr(int(usage)))
    #print("Wrote usage: " + str(int(usage)))

    sensorFile = open(filename)
    temp = parseSensorsOutputLinux(sensorFile.read())
    #gauges.write(TEMP_CHAR)
    gaugeSocket.send(TEMP_CHAR)
    #gauges.write(chr(temp))
    gaugeSocket.send(chr(temp))
    #print("Wrote temp: " + str(temp))

It seems either Ubuntu must be closing /dev/rfcomm0 after a certain time or my Bluetooth receiver is messing things up. Even when the BluetoothError arises, the "connected" light on the receiver stays illuminated, and it is not until I power-cycle to receiver that I can reconnect.

I'm not sure how to approach this problem. It's odd that the connection would work fine for a few minutes (seemingly a random amount of time) and then seize up.

In case it helps, the Bluetooth receiver is a BlueSmirf Silver from Sparkfun. Do I need to be trying to maintain the connection from the receiver end or something?

5
  • 1
    This doesn't directly answer your question, but have you considered using a BluetoothSocket(RFCOMM) from the python-bluez library and treat it like the network protocol it is, rather than going through a virtual /dev/rfcommX serial device? I found the socket interface a bit more reliable when I was last dealing with an RFCOMM based protocol, and it removes the need for root access to configure the serial device ahead of time. There is some sample code here: homepages.ius.edu/rwisman/C490/html/PythonandBluetooth.htm Mar 19, 2012 at 5:07
  • Using python-bluez allows the connection to persist indefinitely. Perhaps it was a problem with Bluetooth timing out... I'd still be interested in figuring out the Serial problems, though. EDIT: Actually, the connection still fails after a few minutes. Python doesn't hog the CPU, though. Here's the error: Traceback (most recent call last): File "gauges-daemon.py", line 49, in <module> gaugeSocket.send(TEMP_CHAR) File "<string>", line 5, in send bluetooth.btcommon.BluetoothError: (11, 'Resource temporarily unavailable')
    – Travis G.
    Mar 19, 2012 at 5:34
  • Perhaps this error condition is sending pyserial into a tight loop. You might have some luck running the program under python -mtrace -t program.py and see if you can trigger the error. This produces a lot of output, so you'd probably want to redirect stderr to a log file. Mar 19, 2012 at 6:44
  • I don't think there's a tight loop problem. Here's a few snippets from the mtrace output: Normally, --- modulename: bluez, funcname: send <string>(2): <string>(3): gauges-daemon.py(51): gaugeSocket.send(chr(temp)) --- modulename: bluez, funcname: send <string>(2): <string>(3): gauges-daemon.py(39): usage = psutil.cpu_percent(interval=sampleTime) , whereas on the very last call before failure, --- modulename: bluez, funcname: send <string>(2): <string>(3): <string>(4): <string>(5): --- modulename: trace, funcname: _unsettrace trace.py(80): sys.settrace(None)
    – Travis G.
    Mar 19, 2012 at 7:08
  • I was thinking more about your PySerial version: that's the one you said was eating 100% CPU. I'd expect the traceback would give enough information about the problem in the socket version. Mar 19, 2012 at 7:22

1 Answer 1

2

It turns out the Bluetooth buffer on the PC was overflowing because I was never calling recv() in the Python script.

The Bluetooth accessory was sending characters to the PC, which the script ignored and which inevitably led to a buffer overflow. This overflow apparently causes the rfcomm channel to lock up until the buffer is read.

Here is the working script, for future reference:

import psutil
import serial
import string
import time
import bluetooth

sampleTime = 1
numSamples = 5
lastTemp = 0

TEMP_CHAR = 't'
USAGE_CHAR = 'u'
SENSOR_NAME = 'TC0D'

filename = '/sys/bus/platform/devices/applesmc.768/temp2_input'


def parseSensorsOutputLinux(output):
    return int(round(float(output) / 1000))

def connect():
    while(True):    
        try:
            gaugeSocket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
            gaugeSocket.connect(('00:06:66:42:22:96', 1))
            break;
        except bluetooth.btcommon.BluetoothError as error:
            gaugeSocket.close()
            print "Could not connect: ", error, "; Retrying in 10s..."
            time.sleep(10)
    return gaugeSocket;

gaugeSocket = connect()
while(True):
    usage = psutil.cpu_percent(interval=sampleTime)
    sensorFile = open(filename)
    temp = parseSensorsOutputLinux(sensorFile.read())
    try:
        gaugeSocket.send(USAGE_CHAR)
        gaugeSocket.send(chr(int(usage)))
        #print("Wrote usage: " + str(int(usage)))

        gaugeSocket.send(TEMP_CHAR)
        gaugeSocket.send(chr(temp))
        print gaugeSocket.recv(1024)
        #print("Wrote temp: " + str(temp))
    except bluetooth.btcommon.BluetoothError as error:
        print "Caught BluetoothError: ", error
        time.sleep(5)
        gaugeSocket = connect()
        pass

gaugeSocket.close()

The key change is in the addition of the line

gaugeSocket.recv(1024)

which cleans the buffer of the "okay" messages sent back from the remote device.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .