0

I'm having trouble reading the response from a RS232 OBD2 interface via pySerial. The code successfully enters the data, as I can see from a direct parallel terminal screen, but fails to read and print the response, regardless of the response.

Right now the code is not capable of printing the response in neither versions of Python. The code looks something like this :

from serial import * # I also tried using /from serial import Serial
import time
ser = Serial("/dev/rfcomm1", 38400, timeout=1)
#print ('Starting up, formatting responses')
#ser.write("ATZ\r"),
#ser.write("ATSP0\r"),
#ser.write("ATS1\r"),
#ser.write("ATL1\r"),
#ser.write("ATH1\r"),
#ser.write("ATF1\r")
#time.sleep(1)
#print ('We have lift-off !')
if ser.inWaiting() > 0:
    ser.flushInput()
#ser.timeout = 1.
time.sleep(1)
#print (raw_data)
ser.write("AT RV\r") #The response should be something like 13.5V, but nothing
ser.timeout = 1.
msg = ser.read(size=1024)
print msg
ser.close()

I left only the AT RV command because while I'm working on it I sent the text formatting commands to ease the job. Right now when I send it it just gives me a blank line (although the terminal which is running on the same machine displays the desired output)

There are no errors in the code, and the commands go through and are responded to by the interface, and I can see that in another live term, but nothing appears when running the Python code. What should I do ?

Codin Moldovanu
  • 90
  • 2
  • 10

2 Answers2

4

You should read after writing, not before.

# before writing anything, ensure there is nothing in the buffer
if ser.inWaiting() > 0:
    ser.flushInput()

# set the timeout to something reasonable, e.g., 1 s
ser.timeout = 1.

# send the commands:
ser.write("ATZ\r")
# ...

# read the response, guess a length that is more than the message
msg = ser.read(1024)
print msg

# send more commands
# read more responses
# ...

The point here is that there is no way to know when the response has been received. This code waits for one second after each command sent, unless more than 1024 bytes arrive during that time. There are more clever algorithms, but let's try with this one, first.

If you want to do something more complicated with the serial line, have a look at the pexpect module.


Some thoughts debugging python serial problems

Serial communication problems are sometimes a bit sticky to solve. pySerial is a reliable library, but as different platforms have different types of serial API, there are a lot of details. Things have not become any easier by the removal of physical serial ports, as the USB converters bring an extra layer into the game. Bluetooth converters are even worse.

The best way to debug the physical layer is to have some monitor hardware with two serial ports tapped into the serial lines. This kind of sniffer helps to isolate the problem to either end of the connection. Unfortunately, such sniffers are very rarely at hand when needed.

The next best thing is to short the RD and TD (RXD, TXD) pins of the serial line. This way all data will be echoed. If the data is received as sent, the physical connection is good. One thing to take care is handshaking. If you do not know what you are doing, disable all flow control (xon/xoff, rts/cts, dtr/dsr. pySerial disables these all if otherwise instructed.

In the case of the question above the physical connection is ok, as another piece of software demonstrates that the data is sent and understood by the other device. (Seeing that something is sent does not prove anything, as that information does not go through the physical layer, but seeing something produced by another device is received proves that the physical connection is ok.)

Now we know the data comes into the operating system, but pySerial does not see it. Or then our code is still somehow bad (no, it shouldn't, but...)

Let us suspect own own code and try someone else's code. This can be run from command prompt:

python -m serial.tools.miniterm /dev/rfcomm1 38400

Now we have a terminal which can be used to manually send/receive data form the other party. If the behaviour can be repeated (sends ok, data is received into the system, but not shown on the terminal) with this, then the problem is probably not in our code.

The next step then is to try:

sudo python -m serial.tools.miniterm /dev/rfcomm1 38400

In principle access right problems lead to situations where we can receive but not send. But it does not harm to test this, because odd rights cause odd problems.

pySerialhas a handy function readline which should read one line at a time from the serial line. This is often what is wanted. However, in this specific case the lines seem to end with \r instead of \n. The same may be repeated elsewhere in code, so with special data special care is needed. (The simple "read with timeout" is safe but slow in this sense.) This is discussed in: pySerial 2.6: specify end-of-line in readline()

The same issue plagues all terminal programs. For the pySerial miniterm, see its documentation (command-line option --cr).

If there are timeouts, they can and should be made longer for debugging purposes. A one-second timeout may be changed into a ten-second timeout to make sure the other device has ample time to answer.

Community
  • 1
  • 1
DrV
  • 22,637
  • 7
  • 60
  • 72
  • I tried adding the flush part, set the timeout to 1s and moved the read command after writing, but all I get is a blank line with no answer. – Codin Moldovanu Jun 23 '14 at 06:55
  • Try to use a serial terminal program to issue the commands manually and to see if you can receive the answers through the line at all. The problem may be elsewhere. Also, you should edit the new code you have written to your question. Then it is easier to see if there is something wrong. – DrV Jun 23 '14 at 07:21
  • The interface recieves the commands sent by the code, and responds to them (I can see this in a live terminal) but the code effectively fails to read, it only returns a blank space. – Codin Moldovanu Jun 23 '14 at 08:15
  • If your live terminal is a different device physically connected in parallel with the serial line, it does not necessarily mean the serial communication reaches the operating system. It just shows that the OBD2 device is answering correctly. This is why it would be useful to try to use a terminal program on the same computer where you are running your python script. If that one works, then the problem is in the script. (And, please show the newest script. There may still be something to fix. Serial comms is sometimes quite complicated.) – DrV Jun 23 '14 at 08:18
  • I'm running the live terminal on the same device as the code, that's why I said that I can see the commands being issued. I'll post the newest script as soon as I get home. – Codin Moldovanu Jun 23 '14 at 10:02
  • There is a slight chance that the live terminal is the problem. Depending on the terminal software, operating system, etc. the terminal may interfere with the serial communication. (I have no experience on such programs on Raspberry, so this is just a guess based on some earlier experience.) BTW, pyserial comes with a terminal software, so you could try that manually, as well. See: http://pyserial.sourceforge.net/examples.html – DrV Jun 23 '14 at 13:11
  • I updated the code in the first post, and right now I get a blank line with nothing written in it. Again, the desired output is being shown in a live terminal which is running on the same machine, I also tried running the script without the live terminal and still nothing. – Codin Moldovanu Jun 24 '14 at 15:18
0

I had exactly the same problem, through Python 2 IDLE no results displayed on the IDLE screen, but the results were redirected to picocom active at the terminal. I needed to capture results because my goal is to read incoming SMSs. The following code solved my problem, I do not know the reason yet, ongoing analysis.

import time
import serial

modem1 = serial.Serial("/dev/ttyUSB3",baudrate=115200,timeout=0,rtscts=0,xonxoff=0)
def sendat1(cmd):
    if cmd == 'res' : modem1.write('Z'); return
    if cmd == 'out' : modem1.write(chr(26)); return
    modem1.write('AT+'+cmd+'\r')
    time.sleep(3)
    obu = str(modem1.inWaiting())
    msg = modem1.read(32798)
    print(obu+':\n'+msg)
    return

try:
    if modem1.inWaiting()>0: modem1.flushInput()
    sendat1('res')
    sendat1('CMGF=1')
    sendat1('CMGL')
    sendat1('out')
finally:
    modem1.close()
techraf
  • 64,883
  • 27
  • 193
  • 198
Oreste
  • 1
  • 2