Monday, September 2, 2013

Go Concurrency and Python, Part 1 - the GPS

I wanted to understand more about where / how concurrency is used in Go, so I'd know when it's appropriate to use in the rover to prevent blocking calls from stalling out important processing tasks.

First, I found this video that pretty much lays out some good ideas ("patterns") for some handy uses. It moves along at a good pace, so watching the whole thing is probably recommended:


So it does exactly what I want; goroutines can encapsulate potentially blocking calls, can have timeouts, and let the caller proceed until a place / time where the results of the call might be more useful, for example, if several things need to be initiated in one chunk, then the results gathered a short time later.

The next bit I wanted to test was access to the GPIO functions on the Beaglebone Black. Since I'm not at a point where I totally get how device tree overlays would be exposed in Go, I could use the Adafruit python library. The fact that the python calls are blocking is of little interest, since I could call them from a Go routine and keep on chugging away in the main processing loop while they execute.


opkg update && opkg install python-pip python-setuptools python-smbus

After a few hundred lines of console output I get only one error, which I ignore...

pkg_run_script: package "bonescript" postinst script returned status 1.

 Hmmm. Big deal. Let's move on... and run the recommended test:


python -c "import Adafruit_BBIO.GPIO as GPIO; print GPIO"

<module 'Adafruit_BBIO.GPIO' from '/usr/lib/python2.7/site-packages/Adafruit_BBIO/GPIO.so'>

Yay! Looks ok. I'll worry about the bonescript error ... later.

One of the things I'm interested in doing is listening to one of the UARTs for the serial input from the GPS module. Originally this was supposed to be a shield (it's the Adafruit Ultimate GPS Logger), but going for the 'simpler is better' mantra I decided to just create a fake set of sockets on a PCB as a shield carrier. Should work.

The UART part of the tutorial requires the python serial module pyserial... so let's install that:

 pip install pyserial


And we get:

Downloading/unpacking pyserial
  Downloading pyserial-2.6.tar.gz (116kB): 116kB downloaded
  Running setup.py egg_info for package pyserial
    running egg_info
    
    warning: no files found matching 'examples/miniterm.py'
    warning: no files found matching 'test/test_io_lib.py'
Installing collected packages: pyserial
  Running setup.py install for pyserial
    running install
    changing mode of build/scripts-2.7/miniterm.py from 644 to 755
    /usr/bin/python /tmp/tmpnwLW2Q.py
    removing /tmp/tmpnwLW2Q.py
    
    warning: no files found matching 'examples/miniterm.py'
    warning: no files found matching 'test/test_io_lib.py'
    changing mode of /usr/bin/miniterm.py to 755
Successfully installed pyserial
Cleaning up...




Hmmm... more minor errors, but reports of success. Huh. Well, let's keep going... and set up the two loopback wires between two UARTS, and launch minicom in two different windows. Success! One point, if you are typing the commands instead of copy/pasting the lines, isn't obvious - the device is ttyO1, not tty01 (it's a captial O, not a zero). Weird.

 Anyway... the GPS Shield can be powered from +5v and it has a 3.3v regulator on it, so the serial lines are at 3.3v levels - safe for the Beaglebone. After some futzing I had it displaying the serial GPS fix as seen on /dev/ttyO1:

minicom -b 9600 -D /dev/ttyO1

However the terminal emulation part of minicom really botches the output. Let's just dump the port, a hint from this post helps...


root@beaglebone:/sys/kernel/debug# stty -F /dev/ttyO1 raw
root@beaglebone:/sys/kernel/debug# stty -F /dev/ttyO1 9600
root@beaglebone:/sys/kernel/debug# cat /dev/ttyO1
$GPGGA,165449.087,,,,,0,0,,M,,M,,*4C
$GPRMC,165449.087,V,,,,,0.00,0.00,020913,,,N*40
$GPGGA,165450.087,,,,,0,0,,,M,,M,,*44
$GPRMC,165450.087,V,,,,,0.00,0.00,020913,,,N*48
$GPGGA,165451.087,,,,,0,0,,,M,,M,,*45
$GPRMC,165451.087,V,,,,,0.00,0.00,020913,,,N*49
$GPGGA,165452.087,,,,,0,0,,,M,,M,,*46
$GPRMC,165452.087,V,,,,,0.00,0.00,020913,,,N*4A
$GPGGA,165453.087,,,,,0,0,,,M,,M,,*47
$GPRMC,165453.087,V,,,,,0.00,0.00,020913,,,N*4B


Ok, so we know at a device level we are getting good serial input to the port from the GPS. Nice. Let's see if we can consume that in some code.



root@beaglebone:~/pytests# cat uart.py
import Adafruit_BBIO.UART as UART
import serial

UART.setup("UART1")

ser = serial.Serial(port = "/dev/ttyO1", baudrate=9600, timeout=5)
ser.close()
ser.open()

c = 0
print "Starting read loop..."

while ( c < 10) and (ser.isOpen()):
print ser.read(64),
c += 1




And running it we get what you'd expect, a nice easy to read display of GPS strings:


root@beaglebone:~/pytests# python uart.py
return 1 lookup_uart_by_nameStarting read loop...
173321.087,,,,,0,0,,,M,,M,,*42
$GPRMC,173321.087,V,,,,,0.00,0.0 0,020913,,,N*4E
$GPGGA,173322.087,,,,,0,0,,,M,,M,,*41
$GPRMC,1 73322.087,V,,,,,0.00,0.00,020913,,,N*4D
$GPGGA,173323.087,,,,,0 ,0,,,M,,M,,*40
$GPRMC,173323.087,V,,,,,0.00,0.00,020913,,,N*4C
$GPGGA,173324.087,,,,,0,0,,,M,,M,,*47
$GPRMC,173324.087,V,,,,, 0.00,0.00,020913,,,N*4B
$GPGGA,173325.087,,,,,0,0,,,M,,M,,*46
$GPRMC,173325.087,V,,,,,0.00,0.00,020913,,,N*4A
$GPGGA,173326.0 87,,,,,0,0,,,M,,M,,*45
$GPRMC,173326.087,V,,,,,0.00,0.00,020913 ,,,N*49
$GPGGA,173327.087,,,,,0,0,,,M,,M,,*44
$GPRMC,173327.08 7,V,,,,,0.00,0.00,020913,,,N*48
$GPGGA,173328.087,,,,,0,0,,,M,,


There are some obvious next steps here, like when we get GPS data that starts in the middle of string, or when we hit the 64 character limit and truncate a line for no reason. There are plenty of open source GPS string parsers out there, so it's really up to you as to how to implement.

Since I know how I'm going to use the data (and what data to throw away) I know I'll want a few things from the GPS, like coordinates, but I want them in a certain format for later use.

For the purposes of this test I think I'll just work on some basic parsing, since what I really want to test is if I can return valid data strings from python back to a Go calling routine.



No comments:

Post a Comment