Since a long time ago, people have been fascinated with time. Perhaps it’s because time is the only thing money can’t buy, the only thing that cannot be stopped or the only real fortune humans have. Who knows.
Accurately measuring time has been a long time challenge and most engineers seem to enjoy building clocks – LCD or 7-segment display clocks, Nixie tube clocks, mechanical clocks … you name it and it’s probably been built already. I’ll show you today how to use an old Android phone together with the Raspberry Pi in order to build yourself a stratum-1 NTP time server (taking the clock from a GPS device).
This is a pointless exercise for most people, as simply using the existing NTP pool will give you a perfectly good clock for your router, all your computers, mobile phones and whatever devices that knows how to use NTP. But what’s the fun in that ?
Introduction to GPS and NTP
If you’ve reached this page, chances are you already know what NTP and GPS are. If you don’t, read the Wiki page on NTP and the Wiki page on GPS. Briefly put, NTP is a method of keeping proper time between networked devices. A NTP server’s precision is defined by a hierarchy of 16 levels called “strata”: 0 is the highest precision possible (atomic clock, GPS clock, etc), stratum 1 is a server taking clock from a stratum 0 device, stratum 2 is a server taking clock from a stratum 1 device and so on. Level 16 is assigned to unsynchronized devices.
My NTP server is a stratum 2 server, meaning it queries stratum 1 servers in order to get the time. It’s pretty accurate, but I wanted to have a stratum 1 server because … because.
Initially, I wanted to get time from a time signal radio broadcast. There are 6 major broadcasts: DCF77 (Germany) in Mainflingen in Hessen, MSF (Great Britain) in Anthorn, JJY (Japan), WWV (USA) in Fort Collins, WWVH (USA) on Hawaii and BPC (China) in Shangqiu. The time is broadcast slowly throughout the day, encoded in the radio signal – receivers pick up this signal and synchronize their internal clock until the next broadcast is received. This method is very good (wrist watches, wall clocks pick and home weather stations pick up this signal), but signal reception can be difficult (due to weather conditions or propagation). More so, if you’re not in the range of these radio stations … tough luck.
Atomic clocks are not really an option for home use, although somebody is trying to put an atomic watch on your wrist. That leaves us with GPS.
Seiko and Citizen have developed GPS wrist watches, but they’re no good as NTP servers. So, we’ll build our own stratum 1 NTP server with a stratum 0 taken from the GPS signal.
Project requirements
You’ll need:
– one Raspberry Pi (no matter what version), preferrably with Raspbian installed
– one USB Bluetooth adapter (something like this one, doesn’t matter as long as the Pi sees it. I’ve got one from a mini Bluetooth keyboard and it works)
– one Android phone with built-in GPS and Bluetooth capability (I used my old HTC Legend)
– GPS over BT installed
Preparing the phone
After GPS over BT is installed, open it and change the following settings:
– in Android settings > Wireless and networks > Bluetooth settings: enable Bluetooth, make the device discoverable, set the timeout to 1h
– open GPS over BT, go in Settings > Screen settings: enable “Constant screen backlight”. Close GPS over BT with Menu > Exit.
The Bluetooth symbol in the status bar should remain on (meaning Bluetooth is left enabled). If you can, this would be a good time to place the phone somewhere where GPS signal can be received (near a window maybe) and connect it to a charger.
Preparing the Raspberry Pi
All commands below assume you’re running as root, as I’m not using sudo here. You can enable the root account on your Pi by logging in as usual (with username pi and password raspberry), then running these commands:
sudo -i passwd root
You’ll be prompted for a root password, enter it twice and that’s it – you can login as root/your_password. If you’re not running as root, all commands given below should be prefixed with “sudo”.
root@pi:~# apt-get install gpsd-clients blueman
This will install Bluetooth tools (hci tools, bluez, rfcomm), a GPS daemon (gpsd) and a command line GPS client (cgps).
Checking the USB Bluetooth adapter
You can check your adapter is recognized by running “lsusb”. This should give you an output similar to:
root@pi:~# lsusb Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. Bus 001 Device 005: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
Last line is the USB Bluetooth adapter. Yours might be listed differently – it doesn’t matter, as long as it’s listed.
Connecting your Android phone to the Raspberry Pi over Bluetooth
Let’s get the MAC address of the phone’s Bluetooth adapter. Run “hcitool scan” and you should see the discoverable Bluetooth devices around you:
root@pi:~# hcitool scan Scanning ... F4:FC:32:E5:2D:C6 Legend
The MAC address and name will be those of your own phone, of course. Write down the MAC address, we’ll need it later. In case you don’t see the phone listed, make sure it’s discoverable and within Bluetooth range.
Next, we’ll set up the Bluetooth connection between the Pi and your phone. Run “bluez-simple-agent hci0 F4:FC:32:E5:2D:C6” (replacing the MAC address with your phone’s MAC address). You’ll be prompted for a password, then your phone will ask you to enter the same password again and the connection should be established:
root@pi:~# bluez-simple-agent hci0 F4:FC:32:E5:2D:C6 RequestPinCode (/org/bluez/4148/hci0/dev_F4_FC_32_E5_2D_C6) Enter PIN Code: 1234 Release New device (/org/bluez/4148/hci0/dev_F4_FC_32_E5_2D_C6)
If you get an error, make sure you’re typing the same password on both devices. You can “ping” your phone, if you wish:
root@newpi:~# l2ping -c 1 F4:FC:32:E5:2D:C6 Ping: 00:F4:6F:6F:D8:CE from F4:FC:32:E5:2D:C6 (data size 44) ... 0 bytes from F4:FC:32:E5:2D:C6 id 0 time 10.95ms 1 sent, 1 received, 0% loss
Ok, good. At this stage, we’ve established the Bluetooth connection between the phone and the Pi. Running “sdptool browse F4:FC:32:E5:2D:C6” (again, don’t forget to replace the MAC address with your own) will list the Bluetooth services provided by your phone (for example, the HTC Legend seems to have 8 services: Audio Source, Network Access Point, Voice Gateway, OBEX Object Push and so on).
Checking the Bluetooth GPS source
Go to your phone and open the GPS over BT application (make sure Bluetooth and GPS are on). In the top left corner you should see an orange indicator and “BT discoverable” underneath. Run the previous “sdptool browse F4:FC:32:E5:2D:C6” command again and you should see one extra service listed at the end:
root@pi:~# sdptool browse F4:FC:32:E5:2D:C6 Browsing F4:FC:32:E5:2D:C6 ... Service RecHandle: 0x10000 Service Class ID List: "PnP Information" (0x1200) Profile Descriptor List: "PnP Information" (0x1200) Version: 0x0102 Service Name: Audio Source Service RecHandle: 0x10001 ................................... many more lines ... Service Name: BluetoothService Service RecHandle: 0x10009 Service Class ID List: UUID 128: 00001101-0000-1000-8000-00805f9b34fb Protocol Descriptor List: "L2CAP" (0x0100) "RFCOMM" (0x0003) Channel: 20
The additional service is created by the GPS over BT application. It will take GPS data from the built-in GPS chip and pipe it as serial data encoded in NMEA over a virtual serial port (the serial port is emulated and data will be passed through Bluetooth). Take note of the channel listed in the newly added service (20 for the above example), as we’ll use it later.
Setting up the Pi to read GPS data
We need to set up the virtual serial port on the Pi. GPS data will be read from this port and parsed by the GPS daemon (gpsd). On the Pi, run this command: “rfcomm bind 0 F4:FC:32:E5:2D:C6 20”. You need to replace the MAC address with your phone’s MAC address and the channel number with the one listed previously in the new Bluetooth service.
In case your Bluetooth gets disconnected or you get errors, restart the Bluetooth service:
root@pi:~# /etc/init.d/bluetooth restart [ ok ] Stopping bluetooth: rfcomm /usr/sbin/bluetoothd. [ ok ] Starting bluetooth: bluetoothd rfcomm.
Release the RFCOMM device with “rfcomm release 0”.
Rescan the Bluetooth services with “sdptool”, note the new channel number and re-run the “rfcomm bind” command, updating the values.
At this point, GPS over BT should say have a green indicator in the top left corner and “Connected to: pi” written below.
Reading GPS data
The first check is to see GPS data is coming in NMEA format through your virtual serial port. On the Pi, run “cat /dev/rfcomm0” and you should see an output like the ones listed here. This means your Android phone is sending GPS data over Bluetooth to your Pi – which is very good.
Raw NMEA data is read on the Pi by the gpsd program. Open a new connection to your Pi (make sure rfcomm is still running) and run “gpsd -b -N -D2 -n /dev/rfcomm0” (this means: start gpsd in read-only mode, don’t go into background, debug level 2, don’t wait for clients and start polling right away). You should see the daemon starting up and no error messages.
Open a 3rd terminal and run “cgps -s”. A text window will open, showing the received GPS data in a friendlier format. If you’re not outside or near a window, you’ll see 1-2 or no satellites – make sure you have proper GPS reception, move outside or at least close to a window. This is how cgps looks like:
┌───────────────────────────────────────────┐┌─────────────────────────────────┐ │ Time: n/a ││PRN: Elev: Azim: SNR: Used: │ │ Latitude: n/a ││ 8 00 000 22 Y │ │ Longitude: n/a ││ 25 35 285 14 N │ │ Altitude: n/a ││ │ │ Speed: n/a ││ │ │ Heading: n/a ││ │ │ Climb: n/a ││ │ │ Status: NO FIX (16 secs) ││ │ │ Longitude Err: n/a ││ │ │ Latitude Err: n/a ││ │ │ Altitude Err: +/- 0 ft ││ │ │ Course Err: n/a ││ │ │ Speed Err: n/a ││ │ │ Time offset: n/a ││ │ │ Grid Square: n/a ││ │ └───────────────────────────────────────────┘└─────────────────────────────────┘
Making NTP read from your GPS
Edit your ntp.conf and add these lines:
server 127.127.28.0 minpoll 4 fudge 127.127.28.0 time1 0.183 refid NMEA server 127.127.28.1 minpoll 4 prefer fudge 127.127.28.1 refid PPS
The weird IP addresses are special loopback address which indicate NTP should read data from a shared memory segment. gpsd will update periodically these shared memory segments from the GPS source (more info here). Save the ntp.conf file, restart the NTP daemon and test it with “ntpq -p” – you should see the NMEA/PPS sources listed:
root@pi:~# ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== ... SHM(0) .NMEA. 0 l - 16 0 0.000 0.000 0.001 SHM(1) .PPS. 0 l - 16 0 0.000 0.000 0.001
Congratulations ! You’ve built an Android phone and Raspberry Pi GPS NTP server.
Now read here why NMEA sucks and why it’s not NMEA’s fault that it sucks. Unfortunately, I didn’t find any GPS output apps which use another protocol (Bluetooth GPS Output, GPS 2 Bluetooth and ShareGPS BETA also use NMEA).
Thanks for this! Is it maybe possible to do it over wifi instead of Bluetooth?
Or USB?
USB would actually be much simpler (and more reliable), as there are plenty of GPS units which report data over USB. I’ve been chasing a cheap one on eBay for a while, but no such luck.
Wifi should probably work, as long as you can get a client/server app (server running on the phone to share GPS data, client on the gpsd end).
So I ended up abandoning this project, but here is what I tried along the way:
– Gingerbread phone communicated via Bluetooth to Linux PC using the app you suggested. It got a 3D lock, but unfortunately it never produced reliable GPZDA data – let alone a 1PPS that could be used for time synchronization.
– Upgraded phone to unofficial Ice Cream Sandwich (ICS) so that I could try a Wifi GPS app and a different Bluetooth app. Unfortunately the phone never achieved GPS lock under ICS, even after running over over 24 hours. I think the phone has issues under ICS.
Here is the GPS over Wifi app that I found, in case you are interested: https://play.google.com/store/apps/details?id=net.cajax.gps2net&hl=en
Hm, looks your problem was caused by the phone itself. Was it by chance a Mediatek chip ? I’ve seen some which are very poor in regard to GPS quality.
That app is a very good find. Thanks for that, now I’ll have to revisit the project 🙂
It’s reportedly a Qualcomm Vision GPS solution.
Samsung Epic 4G Touch ? have you seen this thread ?
HTC Evo 4G (aka Supersonic). I did try flashing a GPS fix from xda-developers, as well as installing an optimized gps.conf.