Saturday, January 23, 2016

The Lost GPS Episode

This is an old tale, nearly lost to the shifting sands of time due to a peculiar practice in modern households known as "spring cleaning".  You might remember in an earlier episode when I confessed to a penchant for keeping notes on random scraps of paper, reserving the immense power of the computer for the final preparation, configuration, and publishing of said notes.  But my methods have a weakness.  Random scraps of paper notes strewn about bear a striking resemblance to garbage -- to the untrained eye.  And spring cleaning caught me by surprise this year, delayed as it was by a few months, thanks in no small part to the ferocious and lasting winter we'd so recently suffered through.  And so it came to pass that most of this tale now resides in a landfill somewhere, lost and forgotten.  I will however attempt to recreate the juicy bits from the #zipit IRC logs at mozzwald.com.

But first, a little background.  The humble beginnings of this sadly delinquent story first came about more than a year ago when I took a zipit on the road for a trip to Boston, intending to investigate it's utility as a travel laptop, alarm clock, and gps.  It worked quite well as an alarm clock, and well enough as a laptop, but my zipitized build of pspmaps made it truly a poor man's gps.  Emphasis on poor.  I'd already fixed up pspmaps to work with the latest google API -- now with JSON -- so I upgraded it further with some sorely missing features which you can read about here.  Then, as chance would have it, the clever folks on the zipit IRC channel got to chatting about some tiny serial gps modules.  So I did some searches and discovered the new breed of gps modules were much smaller than what I'd worked with in the past, which makes sense if you think about it.  And now you can get them packaged with the antenna built right onboard.  Nice!  Eventually I got motivated and purchased one for myself -- a tiny usb serial gps module (with built in antenna) to use in conjunction with pspmaps on the zipit.
 Here it is.  GlobalSat USB dongle Model No ND-105C.  Zipit included for size comparison.

The GlobalSat gps arrived in early May, last spring.  It's even smaller in real life than I imagined from the pictures.  And it's designed to spit out NMEA sentences once a second or so at 4800bps over usb serial, most likely to an android phone or a tablet.  But it's also exactly what I required to convince pspmaps to track the location of the zipit. So the first order of business was to get it connected it to my laptop and make sure it really worked.  The GlobalSat has a blinking red LED on one side to indicate that it has power and is getting fixes.  Red LED, check. Hold it right side up near the window and the LED blinks, check 2.  Next step, set the serial baud rate and see what's cooking.

stty -a /dev/ttyUSB0
stty -F /dev/ttyUSB0 4800
cat </dev/ttyUSB0


Yay!  NMEA sentences.  It works.  Hit Ctrl-C to make them stop.

So now we want to try it on the zipit.  You'll need an adapter to get usb out of the rear connector on the zipit.  If you can solder the tiny pins then you can make your own cable from the Hirose connector like the one shown in the picture above, or you can get a cool zipit breakout board from mozzwald while they're still available.  Once it's connected you have to load the kernel module and toggle the zipit usb  into hostmode.  If you have the latest openwrt jffs with mozzwald's zippity beep extras then you should be ok.  I borrowed the kernel and some nice scripts like /usr/local/bin/usbtog from the jffs image and loaded them on my openwrt SD card so I'd have room to build things.  Navigate to the gmenu2x terminals screen and open up a bash command prompt. 

Load the prolific usb serial module for this gps.

insmod pl2303

Toggle usb to device mode and then back to host mode.

usbtog d
usbtog h


Make sure the usb serial device was created.

ls /dev/ttyUSB*

Look around to see what's up.

lsusb

    Bus 001 Device 001: ID 1d6b:0001
    Bus 001 Device 003: ID 067b:2303


dmesg


    pxa27x-udc pxa27x-udc: USB reset
    USB Serial support registered for pl2303
    usbcore: registered new interface driver pl2303
    pl2303: Prolific PL2303 USB to serial adaptor driver
    pxa27x-udc pxa27x-udc: USB reset
    usb 1-2: new full-speed USB device number 2 using pxa27x-ohci
    usb 1-2: New USB device found, idVendor=067b, idProduct=2303
    usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
    usb 1-2: Product: USB-Serial Controller D
    usb 1-2: Manufacturer: Prolific Technology Inc.
    pl2303 1-2:1.0: pl2303 converter detected
    usb 1-2: pl2303 converter now attached to ttyUSB0


Now fetch a copy of picocom and run it to watch the NMEA messages scroll by.  If you're near a window you should be able to recognize the position fix messages.

picocom -b 4800 /dev/ttyUSB0

Press ctrl-a crtl-x to return to the console.  Once you've verified the gps actually works on the zipit you can get started installing some software to make better use of it.  Apparently gpsd is the defacto standard gps driver on linux.  It sounds really nice.  Supposedly it sets itself up between you and the raw NMEA messages and smooths over the wrinkles while making the data available to multiple multiplexed clients, even remote ones.  Cool.  You'll want to grab the specially patched version of gpsd for the zipit and install it with opkg.  Here's some additional reading on gpsd clients if you're curious.

When I'm testing things I usually open a second tty on the zipit with alt-right, load a smaller font like iz2slat and launch gpsd from there so I can see what it's doing and so I can talk to it from the original tty, accessible via alt-left.  I discovered early on that gpsd only seems to run from a real zipit tty.  Normally I ssh into the zipit to test things but that didn't work for this.  I also discovered that you must bring up the loopback adapter so you can talk to gpsd.

ifconfig lo up
gpsd -N -n -D 3 -G /dev/ttyUSB0

Now you can telnet into port 2947 on the zipit (from the zipit or your laptop) and send the command to make gpsd talk.

telnet localhost 2947
?WATCH={"enable":true,"json":true}

It should start jabbering away.  Use the IP address of your zipit instead of localhost if you want to try this from your laptop.

You can also run a special version of gpsd sample client program cgps, that I christened zgps, with all the extra whitespace squeezed out so that it fits on the tiny zipit screen.  This will print live fix information in a more human readable format than the raw JSON data you looked at earlier.  The modified zgps.c source code is available if you'd like to see what I did.
The zgps client shows you what the gps sees.

I also used another gpsd example client program gpspipe to make a nifty little speedometer script for the zipit that you can play with.  If you want to duct tape the zipit to your bicycle and still be able to actually read it while riding, then you'll need to find the nice large ter-132b.psf terminus console font somewhere, possibly in a full PC linux OS, and install it on your zipit.
The zipit is not currently moving at top speed.

And finally, the goal of this adventure, a special version of pspmaps patched to work with gpsd is available.  If you don't already have it, install pspmaps 2.4.2.  Then rename the pspmaps executable (just in case) and replace it with the gpsd patched version.  When you run it, there should now be a selection in the menu to enable the gps tracking.  The updated pspmaps source code is in my pspmaps github project.  Normally, I'd cap this part of the story off with a short shaky video of pspmaps tracking my progress along a local road somewhere.  It really did work, I swear, but unfortunately I haven't made that video yet.  Sorry.

So how did we get here?  The full story is faithfully recorded in the irclogs from early May to the middle of June.  Search for the word gps from user deeice if you want all the details.  I'll try to cover the highlights. 

When the gsp arrived, my first priority was to make sure it actually worked with the zipit.  That took some trial and error with the kernel modules and the usb host/data mode toggling business, but eventually I arrived at the recipe listed above and could see the gps talking and reporting NMEA location fixes in picocom.  The next step was gpsd.  The default openwrt-zipit package is the old and crusty version 3.6 gpsd, so I checked to see what version was in the current upstream openwrt packages.  I think that's how we settled on version 3.10.   Or maybe that was the version available on the zipit Arch linux.  I was mixing it up at the time, working on both openwrt and Arch, so who knows.  Anyhow, I got the newer gpsd compiled and running on the zipit and then got busy hacking the cgps code into a multithreaded demo that I might eventually shoehorn into pspmaps.  That took much longer than it should have because none of the gpsd client programs running on the zipit would talk to gpsd until I finally realized the loopback interface wasn't up.  Darn it!  Once I solved that issue, the rudimentary pspmaps to gpsd interface was coded up and working in no time.  And then we discovered the performance issues.

It turns out gpsd is littered with floating point math!  Ewww!  Also it does't appear to use blocking IO to read the NMEA sentences from the serial port.  Both of these issues were confirmed by scanning through the gpsd mailing list log file archives.  Apparently they've made various attempts to fix things over several releases but were not entirely successful, most likely because the zipit and other seriously underpowered devices are not in their target market.  So I set about trying to fix it because the enormous cpu drain of gpsd was clobbering the pspmaps performance. 
CPU is pegged at 80%.  Yikes!

For some reason gpsd wants to calculate its own "more accurate" location fixes from the satellite position data, using loads of floating point math, each and every time a character is received from the gps.  Or at least it might as well be.  The mailing list logs pointed to a NOFLOATS_ENABLE configure option that might alleviate some of the pain, but the option seems to have disappeared in the most recent 3.14 versions of gpsd, so libgpsd 3.10 version was selected and built with the NOFLOATS_ENABLE setting.  That made things marginally better.  Next I fixed the IO select call to block, but it still used too much cpu.  So I was forced to go with a simple hack to usleep for a fraction of a second  whenever we receive snippet of data from the gps.  That chunks the data up into larger clusters and gives the cpu some well deserved rest periods in between.  Pspmaps only looks at the fix data once a second anyhow.  That put the performance in the not great, but acceptable range.  I was looking for less than 5% cpu usage and we were getting something more like 20.

So I checked out some alternatives to gpsd.  There's an ancient version of gpsd hacked to use fixed point math for the maemo called minigpsd that looked at least slightly promising.  But further investigation showed the api was incompatible and insufficient, and even worse it made use of the enormous bloated gconf library for it's tiny config file.  WTF?  Even the gnome crowd seems to have abandoned gconf as a mistake.  I also looked at some interesting arduino code like tinygps.  But then I found what I wanted in the simple raw NMEA parser minmea.  It doesn't multiplex the gps data (as far as I know), but it does support fixed point math and my simple test program showed it only uses about 6% of the cpu (probably half of that is printing out what it's doing) so it's a winner.  According to the github pspmaps archive, I've already hooked this into the pspmaps code base along with the gpsd code, but I couldn't actually remember running any tests on pspmaps with minmea.  So I ran some tests and it really does work pretty much as I promised.  Yay.  I'm not sure how robust my code is if for example the gps is disconnected and reconnected later.  So I still need to do some follow up work here...

Also, I don't seem to have the faster gpsd installed on my zipit.  At least it doesn't act any faster than that screenshot above.  Phooey!  Gotta poke around on the zipit and see what happened to that.

I also need to tinker with using /etc/init.d to launch gpsd for better hot plugging.
And I need to locate (or make) the Arch executables for zgps and/or pspmaps.

The todo list goes on and on.  Time to find some random scraps of paper and get this down in writing...


Update January 25 2016:

Ok, I found the faster gpsd executable (with a tenth of a second usleep before the select call, and perhaps a few other unknown tweaks that I thought might work).

Significantly less cpu used by the patched gpsd.

Now I just need to figure out exactly how I created it so we can get it into a real ipk package.  Until then you can copy this one to /usr/sbin/gpsd and replace the one installed by the gpsd ipk.

gpsd-wrt-with-usleep.tgz


Update 2:

Actually, that might just be all there is to this story.  The gps was acting flaky recently, and now it won't talk at all.  I think the flimsy micro usb connector got busted somehow.  Oh well.

Update 3:

Nope.  I'm not ready to end this yet, so I ordered a cheap serial gps from sparkfun to keep the hope alive.  It's as simple as it gets for a serial gps in order to work around my limitations.  In theory, I should be able to connect the three wires (gnd, 3.3V, and tx) to the power, ground, and ttyS1 rx line on the zipit expansion port.  I already have 3 of the appropriate connectors with which to learn how to solder properly (or get someone skilled to do it).  The best part of this plan is that it should work with both iz2s and the current openwrt and arch zipit distributions.  At least, that's the plan.

Hmm, perhaps while I'm waiting for the package I'll try and use my serial to ttl converter to test that rx pin.  I know I've managed to get a clip onto one or 2 of those tiny pins in the past.

 See that?  I got 3 clips on the zipit connector for some gpio tests, way back when.


Update 4 - January 30 2016:

While waiting for the sparkfun gps to arrive, I figured I might as well check and make sure the whole expansion connector serial port idea works.  Supposedly pin 7 on the connector is labeled btrxd, and was originally intended as the receive line for a bluetooth serial port, which the kernel sees as ttyS1.  The bluetooth transmit and flow control lines are only available on pads inside the zipit and not exposed on the expansion adapter, but that's ok for my purposes.  Somebody at Zipit Wireless must've been thinking of me when they did that.  Anyhow, I compiled picocom on my iz2s zipit and created the ttyS1 serial device.

mknod /dev/ttyS1 c 4 65

Then I connected some clips to pins 7 btrxd and 22 gnd on the zipit connector and clipped the other ends to tx and gnd on a nokia ca-42 data cable that was hanging off a usb slot on my laptop.

Here's the wiring harness.  Ignore that green screw terminal dB9 adapter on the top right.

I then cranked up picocom at both ends and sent a friendly message to the zipit.

It's gonna work.


Update 5 - February 21 2016:

The axim cable arrived a while back but I was busy dog sitting and didn't do much with it for quite a while.
Little Edison probably thinks it's a chew toy, just for him.

Once there was no longer danger of interference from small doggies demanding attention, I got busy with the cable.

 Here's the axim cable with the lid removed.

It's well constructed for my plans.  The connecter is easy to disassemble with two screws and some small clips on the sides near the business end.  There's only 4 wires through the cable, but they're connected to about 13 pins.  In the picture you can can see some extra wires used to tie different blocks of pins together.  Probably all go to ground on the axim.

I've pulled a fistful of pins, all tied to the red wire.

I used my needle nose pliers to yank the blocks of pins, and then chopped off all but one pin from each wire with a toe nail clipper.  Hope it still works for my toes...  Then I re-inserted 3 of the pins back into the proper slots required to connect the gps to the zipit bluetooth serial pins.  My notes say I connected the green wire to pin 22 ground, black to pin 17 +3.3V, and white to pin 7 btrxd, reserving the red wire for "in case of emergency..."
The reassembled cable has just 3 pins, right where I want them.

The operation was something of a challenge because I only have 2 hands and can no longer see all that well up close.  Fortunately I discovered an old kids toy festooned with magnifiers and doubled up the 2 biggest lenses so I could see the pins and their slots.

Eventually the sparkfun gps arrived as well, you can see it in that last picture of the cable.  I will get it connected, but meanwhile, interest in my lego CAD programs has been rekindled briefly, so I'm going with that for now.


Update 6 - September 24 2016:

I did eventually return to this topic and finish the job.  You can read the final chapter in the zipit gps saga here.

No comments:

Post a Comment