Ground Control to RPy-Rover… are you there?

In order to function, the RPy-Rover needs to receive commands from the Command Center (basically a Windows laptop) and send back the requested data (GPS, photo, status of movement, etc.)

To enable communication between the rover and the Command Centre we used Xbee radios. A multitude of models are available, from the simple Series 1 to the more complex ZigBee Series 2. The main difference between these two series, for our purpouse, is that Series 1 radios communicate basically one-to-one and the Series 2 form a mesh of radios that can be used to bounce a transmission from radio to radio until the destination radio is reached (i.e. the API mode).

For our purpouse a Series 1 would suffice, but we chose Series 2 radios in order to future proof the project, as we intend to expand in the future to a couple of collaborative rovers that can use one another to communicate with the base.

Captura de ecrã 2016-02-18, às 22.46.41
Source: SparkFun

If you want to use Xbee radios, especially the complex Series 2, we highly recommend that you read the book “Wireless Sensor Networks” by Robert Faludi, which explains everything about these small yet powerfull radios.

When we implemented the radio communication in API mode using the pyserial library the radios where able to communicate but the library generated a large number of errors (IOError: Errno 5) on the RPi and lost data bytes. After a few weeks troubleshooting we decided to use the AT mode and designed a basic control protocol.

Components

As you probably know, if you follow this blog, the Rpi-Rover is built around a Raspberry Pi (RPi) wired to components mounted on a breadboard.

The Rover’s radio is mounted on the breadboard, as is the case with most of the rover’s electronics components: IMU, QiK, serial/I2C bridges, ADC, etc.

IMG_1972

As the pin spacing on the Xbees is 2mm and breadboards sockets are spaced at 0.1″ (2.54mm), a breakout board is required in order to mount the Xbee radio on the breadboard. The breakout board comes without pins (to connect to the breadboard) and headers (to connect to the radio), so they must be purchased separately and soldered to the board.

On the Command Centre side the radio is simply connected to the laptop using an USB explorer for Xbee. This worked fine both on Mac and Windows machines.

IMG_5729

For this module we used:

  • Xbee ZigBee series 2 radios 2mW Wire Antenna (two units)
  • Sparkfun breakout module for the Xbee
  • 2x Break Away Headers
  • 2x 2mm 10pin SocketS
  • SparkFun XBee Explorer USB

Configuring the radios

Before using the radios they must be configured using the XCTU utility. This was required to instal AT firmware on both radios and configure the baud rate.

We used the default 8 bits / no parity / 1 stop bit / no flow control, but increased the speed to 115200 bps to reduce the transmission time of the photos sent back by the rover.

Wiring table

One of the radios connects to the RPi  as follows:

Xbee pin Connected to RPi pin Function
1 1 Vcc (3.3v)
2 10 Xbee Tx / RPi Rx
3 8 Xbee Rx / RPi Tx
10 6 Ground

Unused pins should be left disconnected. Pins are numberer as shown on the diagram below, which is represented with the radio’s antena facing us.

Xbee
Source: SparkFun

The RPy-Rover communication protocol

The Command centre starts a transmission by sending three letters (command) to the Rover. The Command Centre then waits for a valid answer from the Rover for 5 seconds. If it doesn’t receive a valid answer during that time the transmission is canceled.

Meanwhile the Rover listens for incoming commands. When it receives three letters it checks if it corresponds to a valid command. If it isn’t valid it discards the three letters and resumes listening. If the command is valid the Rover answers with a number followed by a CR character and the data requested by the comand. The number is the number of bytes of the data paylot it is sending in reply to the command.

In the case of a file transfer (e.g. when a photo is requested) the the data payload sent by the rover stats with the filename, followed by the CR character, followed by the file contents. The file name is transmited because it has the timestamp of the photo.

Programming

In the final project program the code for the radios is integrated into the Command Centre and the Rover’s core program. In order to explain how we used the radios without goin into the code for the other modules and Command Centre mosaic we provide the code of the proof of concept (POC) for the readios in the GitHub repository. The POC code, which we explain in this tutorial, is composed of the following two files:

  • C_CommandC_RadioPOC.py
  • R_Rover_RadioPOC.py

The POC allows the transmission of the following commands:

Command Description Rover reply
TST Test transmission OK
GPS Gert GPS position $GPRMC,053740.000,A,2503.6319,N,12136.0099,E,2.69,79.65,100106,,,A*53
(Static RMC NMEA string)
TPH Take photo PNG file
ERR Invalid command (generates error for testing purposes) (nothing)

We used PySerial on the Command Centre for serial communication with the radios and WiringPi2 on the Rover, as it includes other functionalities we require in other modules, such as I2C and SPI communication functions.

The POC on the Rover also requires the R_Camera.py library, which is part of the RPy-Rover code.

Command Centre radio POC

We start by opening the serial port at 115 kbps.

   radio = serial.Serial("COM5",115200)

Then load the list of commands available into a variable

commands =["TST", # Test (rover answers OK)
           "GPS", # Rover send GPS position
           "TPH", # Rover sends filename (date/timestamp) and photo
           "ERR"] # Generates error for testing purposes

And then print it

print("\nPlease select command:\n")
for a in range(0,len(commands)):
   print(a, " - ", commands[a])

We then go into an infinit loop waiting for user input and sending the corresponding three letter command to the rover.

The three letter command is sent using the getcommandreply(command) function, which sends the command supplied as a parameter and returns the data received from the rover. This function returns empty if no data is received within 5 seconds.

TIMEOUT = 50 # in tenths of second

def getcommandreply(command): # gets answer from rover within 5 second time-out
    cycle = 0 # timer for time-out
    bytesread = 0
    response = ""
    radio.write(command.encode(encoding='UTF-8'))
    while (cycle < TIMEOUT):
        while (radio.inWaiting() > 0): # data received from Rover
            response += radio.read(1).decode()
            bytesread += 1
            cycle -= 1 # when receiving data the timer is paused
        position = response.find(chr(10), 0) # position of CR first character, or -1 if not present
        if position >= 0: # found CR in received data
            try: # in case characters before CR are not a number
                payloadsize = int(response[0:position])
            except TypeError:
                return() # data befor CR wasn't a number - data corrupted!
            if (len(response) >= position + 1 + payloadsize): # Ok. Complete payload received
                return(response[position+1:position + 1 + payloadsize])
        # not enough data - wait for more data
        # 50 x 0.1 = 5 seconds total
        time.sleep(0.1)
        cycle += 1
    return("") # time-out

At the Command Centre the data packet received from the rover is printed on screen. For simplicity, as the POC was geared towards the radios, when a photo is received (after a TPH command) the name of the PNG file received is printed instead of displaying the actual photo. In the Command Centre Mosaic we will explain how the photo is presented on screen.

Rover radio POC

The POC program starts by oppenning the serial port at 115 kbps.

radio = wp.serialOpen("/dev/ttyAMA0",115200)

Notice that the syntax is different from what we used when opening the serial port in the Command Centre as we use WiringPi2 in the rover, as oposed to the PySerial library used bythe Command Centre.

As mentioned the rover is continually waiting for a command. Once three letters are received they are checked against the list of valid commands the corresponding data payload is sent to the Command Centre using the reply_to_cc(payload) function:

def reply_to_cc(payload): # send payload to command centre
	message = str(len(payload)) + chr(10) + payload
	print "Sending ",len(message)," bytes"
	for a in range(0,len(message)):
		wp.serialPutchar(radio, ord(message[a]))
		print a

Notice the “print a” command at the end. It was used to provide feedback on the RPi’s console as the file transmission (of photos) can easily last several minutes.

Feel free to give us feedback or ask questions, specially if you’d like to know more about this tutorial.

Previous tutorial: Creating the RPy-Rover body

First tutorial: Controlling the RPy-Rover’s motors

Back story: How we got started


Leave a comment