2

So I'm trying to communicate with with a zigbee module connected to a serial port by using AT (sometimes refered as Hayes) commands.

My problem is I can't figure out how to correctly read its answers with the pyserial module of Python (I'm using Python 2.7 on an embedded device).

Sometime the script manage to perfectly read the module's responses and sometimes it just returns a suite of 'A' characters.

Here is a sample output :

Opening serial /dev/ttyS2 ... /dev/ttyS2 is open...

Enter command or 'exit' : AT

------------- Response -------------

AT OK

Enter command or 'exit' : ATI

------------- Response ------------- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAaAaaaAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Enter command or 'exit' :

I really don't understand what's happening here but I think it's probably because I use the pyserial module incorrectly. I've already tried to use the inWaiting() command to read only the bytes contained in the input buffer but this function always returns zero and the readline() command don't seems to work either so I just read packets of 1024 bytes.

Here's the code I've written so far :

import serial, time, os

def sendAtCommand(command):

    # responseArray = []
    # i = 0

    try:
        # print('Number of bytes waiting in input buffer before write : '+str(ser.inWaiting())) -> always zero...

        if ser.inWaiting():
            # flush input buffer, discarding all its contents
            print('flushing input buffer ...')
            ser.flushInput()
            # flush output buffer, aborting current output
            # ser.flushOutput()

        try:
            ser.write(command.encode('ascii')+'\r')
            #ser.write(command+'\r')
            #time.sleep(5)
        except SerialTimeoutException as e:
            print(repr(e))

        response = ser.read(size=1024)
        # print('Number of bytes waiting in input buffer after write : '+str(ser.inWaiting())) -> always zero
        # while ser.inWaiting():
            # print('Reading response ... '+str(i))
            # responseArray.append(ser.readline(eol='\r\n'))
            # i += 1
            # time.sleep(0.2)

        print('------------------------------------')
        print('------------- Response -------------')
        print('------------------------------------')
        print(response)
        # for line in responseArray:
            # print(line)           
        print('------------------------------------')

        time.sleep(1)

    except KeyboardInterrupt as e:
        print('Closing serial '+port+' before interrupting ...')
        ser.close()
        exit()

VERSION = '0.02'
firstStart = True

port = '/dev/ttyS2'
baudrate = 19200
bytesize = 8
parity = 'N'
stopbits = 1
timeout = 1
xonxoff = False
rtscts = False
dsrdtr = False
write_timeout = None
inter_byte_timeout = None

try:
    os.system('cls' if os.name == 'nt' else 'clear')
    print('')
    print('PySerial version : '+serial.VERSION)
    firstStart = False

    print('')
    print('Opening serial '+port+' ...')
    ser = serial.Serial(port, baudrate, bytesize, parity, stopbits, timeout,
    xonxoff, rtscts, write_timeout, dsrdtr, inter_byte_timeout)
except ValueError as e:
    print(repr(e))
except SerialException as e:
    print(repr(e))

if ser.isOpen():
    print(ser.name + ' is open...')
    print('')

while True:

    try:
        cmd = raw_input('Enter command or \'exit\' : ')

        if cmd == 'exit':
            ser.close()
            exit()
        else:
            sendAtCommand(cmd)
    except KeyboardInterrupt as e:
        print('Closing serial '+port+' before interrupting ...')
        ser.close()
        exit()
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Sir Robin
  • 182
  • 2
  • 14
  • Have you tried controlling the device using a serial port terminal program like minicom (or screen) to send commands by you typing them directly, and directly show the zignee device responses? This will help work out whether the problem is your program, the serial port setup, or the device (or a combination). – DisappointedByUnaccountableMod Nov 18 '16 at 17:58
  • For more detail see e.g. http://unix.stackexchange.com/questions/22545/how-to-connect-to-a-serial-port-as-simple-as-using-ssh – DisappointedByUnaccountableMod Nov 18 '16 at 18:13
  • 1
    Looks like a bitrate and/or stopbit-missmatch. – too honest for this site Nov 19 '16 at 17:05
  • Default XBee baudrate is 9600. – tomlogic Nov 21 '16 at 04:24
  • First, thank you all for your answers :) @Barny : yes, I already tried this using miniterm.py, the responses are pretty much the same tho. - Olaf & tomlogic : the script running on my board (a Linkit 7688 based on OpenWrt Linux distro) uses the default factory settings of the zigbee device (a Telegesis ETRX357) for its pyserial settings -> 19200bps, Data bits - 8, Parity - none, Stop bits - 1, Flow Control – none. I will try to set other settings myself today on both devices to be sure they're running on the same configuration. – Sir Robin Nov 21 '16 at 08:52
  • If you can't get it to respond correctly to ATI in miniterm then the problem clearly isn't only how you are using pyserial in your code, and probably is nothing to do with pyserial. I'd say you should persist with miniterm-based diagnosis first. – DisappointedByUnaccountableMod Nov 21 '16 at 10:40

1 Answers1

1

Ok so first, thank you all for your answers. Finally I've figured out how to resolve this issue.

I don't know why but the problem was that I powered the telegesis zigbee board by using it's UART vcc pin (connected to the Linkit 7688 3v3 pin).

Now, I just connect the GND, Tx and Rx of both board and use an external source for power and it works perfectly. Please refer to the picture below if you have the same issue with the same material.

Don't use the VCC pin boxed in red, you must power the board using an external power source through the x2 socket.

(Sorry all for the poor quality of my english)

Telegesis  ETRX357-DVK development kit

EDIT :

Ah and now I don't read random packets of 1024 bytes anymore, I use a separate thread for this as you can see below :

class readSerial(Thread):
    """This thread waits for data in the serial input buffer"""
    """This is a new subclass of the Thread class (cf. Python 2.4+ threading module"""    

    def __init__(self, serialObject):
        """Overrides the __init__(self [,args]) method to add additional arguments"""
        Thread.__init__(self)
        self.serialObject = serialObject
        self.daemon = True
        self.start()

    def run(self):

        ser = self.serialObject

        data = ''

        ser.reset_input_buffer()
        ser.reset_output_buffer() 

        while True:
            try:
                if ser.in_waiting > 0:
                    data = ser.read(ser.in_waiting)

                    print('msg received : ')
                    print('')
                    print(data.decode('ascii'))

                if 'UCAST' in data.decode('ascii'):
                    zigbeeRequest(data.decode('ascii')[26:], ser)

                    ser.reset_input_buffer()

                    data = ''
                if 'BCAST' in data.decode('ascii'):
                    zigbeeRequest(data.decode('ascii')[26:], ser)

                    ser.reset_input_buffer()

                    data = ''

                time.sleep(1)                    
            except Exception as e:
                print(repr(e))

Then just put

readThread = readSerial(ser)

in your main programm after you have opened your serial connection.

I hope this can be useful for someone even if I know well it isn't perfect programming (don't catch exceptions like I do...)

Sir Robin
  • 182
  • 2
  • 14