Tag Archives: BerrygGPS

Live Asset Tracking with the OzzMaker SARA-R5 LTE-M GPS + 10DOF and Traccar

Ever wanted to know where your caravan, boat, trailer or ute is — right now, from your phone — without paying a monthly subscription to a tracking company?

In this guide we will build a complete, self-hosted live asset tracker using:

  • An OzzMaker SARA-R5 LTE-M GPS + 10DOF board on a Raspberry Pi Zero
  • Traccar, a free and open-source GPS tracking server
  • A free “Always Free” VM on Oracle Cloud to host Traccar
  • A PPP data connection over LTE-M to upload positions
  • Geofencing, so you get an alert the moment your asset leaves (or enters) an area you define

Why Traccar?

Traccar Main page

Traccar is a mature, open-source GPS tracking platform used by everyone from hobbyists to commercial fleets — and self-hosting it means you get all of its features for free, with your data staying on your own server. Highlights include:

  • Live tracking — watch all your devices move on a map in real time, from any browser or the free Traccar Manager app (iOS/Android).
  • Geofencing — draw virtual boundaries (circles or polygons) on the map and have Traccar watch them for you. We’ll set this up in Part 7.
  • Alerts & notifications — get notified by email, web popup or push notification when a device exits a geofence, exceeds a speed limit, goes offline, moves after being stationary, or triggers an ignition/motion event.
  • Route replay — scrub back through history and replay exactly where an asset travelled on any day, complete with speed at every point. Great for reviewing a trip, or working out where the trailer went last Tuesday.
  • Reports — built-in trip, stop, distance, and event summary reports over any date range, exportable to Excel.
  • Multiple devices — one server can track as many devices as you like. Build a second tracker for the boat, add the family cars with the free Traccar Client phone app — all on the same map.
  • Users & sharing — create extra accounts with access to only some devices, handy if you want to share the caravan’s location with family without handing over admin access.
  • Full REST API — everything the web interface can do can be scripted, so you can integrate tracking data into your own projects.

We’ll be using live tracking, geofencing, alerts and route replay in this guide — but it’s worth exploring the rest once you’re up and running.

How it all fits together

The SARA-R5 module does two jobs at once. Its integrated M8 GNSS receiver streams NMEA position data to the Pi, while the LTE-M modem provides an internet connection via PPP. A small Python script reads the GPS position and sends it to your Traccar server every few seconds using Traccar’s simple HTTP-based OsmAnd protocol.

GPS Tracker

  1.  NMEA data is sent from GPS to the Pi
  2. A python script sends the location data to the Traccar host on the internet via PPP
  3. The SARA-R5 establishes a PPP connection, giving the Pi internet access

Continue reading Live Asset Tracking with the OzzMaker SARA-R5 LTE-M GPS + 10DOF and Traccar →

Accessing GPS via I2C on a BerryGPS-IMU

The BerryGPS-IMU uses a  CAM-M8C U-Blox GPS module, this GPS module includes a DDC interface which is fully I2C compatible.

This guide will show how to read NMEA sentences from the GPS module via I2C, using a Raspberry Pi. This leaves the serial interface on the Raspberry Pi free for other uses. You can also use a QWIIC connector to connect the BerryGPS-IMU to the Raspberry Pi

We will create a virtual node where we will send the NMEA sentences, we will then configure GPSD to read this virtual node.

 

Caveat: There is a well know I2C clock stretching bug on the Raspberry Pi which will be encountered when trying to communicate with the  uBlox module via native I2C.  This results with random characters appearing in the retrieved data.
We will cover two methods of how to get around this;

Method 1 – Include a lot of checks to ignore NMEA sentences with corrupt data.
The overall amount of NMEA sentences which will be corrupt is very small(20 out of 1,000), which still makes this method very usable.

Method 2 – Using bit banging to overcome the clock stretching bug. This will result in zero errors, but requires I2C to be disabled on the Raspberry Pi.

 

I2C Jumpers

By default, the GPS module on the BerryGPS-IMU is not connected to the I2C bus.  This can be fixed by placing a solder blob on the jumpers JP11 and JP10 on the back of the PCB.

BerryGPS-IMU I2C GPS

 

Method 1 – Using native I2C

Enable I2C on your Raspberry Pi and set the speed to 400Khz.

pi@raspberrypi ~ $ sudo nano /boot/config.txt

Near the bottom, add the following line

dtparam=i2c_arm=on,i2c_arm_baudrate=400000

Now reboot.

You can confirm if you see the GPS module by using the below command.

pi@raspberrypi ~ $ sudo i2cdetect -y 1

 

Here is the output when a BerryGPS-IMU is connected. 42 is the GPS module

pi@raspberrypi ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: — — — — — — — — — — — — —
10: — — — — — — — — — — — — 1c — — —
20: — — — — — — — — — — — — — — — —
30: — — — — — — — — — — — — — — — —
40: — — 42 — — — — — — — — — — — — —
50: — — — — — — — — — — — — — — — —
60: — — — — — — — — — — 6a — — — — —
70: — — — — — — — 77

 

Create the python script which will read the data via I2C from the GPS module

pi@raspberrypi ~ $ nano i2c-gps.py

Copy in the below code;

#! /usr/bin/python
import time
import smbus
import signal
import sys
BUS = None
address = 0x42
gpsReadInterval = 0.03
def connectBus():
    global BUS
    BUS = smbus.SMBus(1)
def parseResponse(gpsLine):
  if(gpsLine.count(36) == 1):                           # Check #1, make sure '$' doesnt appear twice
    if len(gpsLine) < 84:                               # Check #2, 83 is maximun NMEA sentenace length.
        CharError = 0;
        for c in gpsLine:                               # Check #3, Make sure that only readiable ASCII charaters and Carriage Return are seen.
            if (c < 32 or c > 122) and  c != 13:
                CharError+=1
        if (CharError == 0):#    Only proceed if there are no errors.
            gpsChars = ''.join(chr(c) for c in gpsLine)
            if (gpsChars.find('txbuf') == -1):          # Check #4, skip txbuff allocation error
                gpsStr, chkSum = gpsChars.split('*',2)  # Check #5 only split twice to avoid unpack error
                gpsComponents = gpsStr.split(',')
                chkVal = 0
                for ch in gpsStr[1:]: # Remove the $ and do a manual checksum on the rest of the NMEA sentence
                     chkVal ^= ord(ch)
                if (chkVal == int(chkSum, 16)): # Compare the calculated checksum with the one in the NMEA sentence
                     print gpsChars
def handle_ctrl_c(signal, frame):
        sys.exit(130)
#This will capture exit when using Ctrl-C
signal.signal(signal.SIGINT, handle_ctrl_c)
def readGPS():
    c = None
    response = []
    try:
        while True: # Newline, or bad char.
            c = BUS.read_byte(address)
            if c == 255:
                return False
            elif c == 10:
                break
            else:
                response.append(c)
        parseResponse(response)
    except IOError:
        connectBus()
    except Exception,e:
        print e
connectBus()
while True:
    readGPS()
    time.sleep(gpsReadInterval)

 

You can test the script with python i2c-gps.py. If you have a GPS fix, you will get output similar to below.

pi@raspberrypi ~ $ python i2c-gps.py
$GNRMC,071423.00,A,3254.18201,S,15243.27916,E,0.252,,110721,,,A*72
$GNVTG,,T,,M,0.252,N,0.466,K,A*3C
$GNGGA,071423.00,3254.18201,S,15243.27916,E,1,10,1.01,29.0,M,22.6,M,,*63
$GNGSA,A,3,30,14,07,17,13,19,15,,,,,,1.66,1.01,1.31*17
$GNGSA,A,3,73,74,72,,,,,,,,,,1.66,1.01,1.31*1C
$GPGSV,3,1,12,01,21,124,,06,13,009,20,07,11,048,21,13,47,286,34*70
$GPGSV,3,2,12,14,54,143,27,15,25,253,31,17,84,132,28,19,70,328,31*7D
$GPGSV,3,3,12,21,09,139,,24,08,225,18,28,,,29,30,49,052,26*45
$GLGSV,3,1,10,65,14,243,16,71,00,330,,72,15,287,21,73,39,099,22*65
$GLGSV,3,2,10,74,53,176,19,75,16,227,,80,00,070,,83,24,149,*64

 

Create a virtual node, this is where we will send the NMEA sentences from the GPS module to.

pi@raspberrypi ~ $ mknod /tmp/gps p

 

Now run the python script and redirect the output to the virtual node we just created.
We will also use stdbuf so the output from the script is sent to the virtual node one line at a time. Without this, the output is buffered and only sent to the virtual node when the buffer is full.

pi@raspberrypi ~ $ stdbuf -oL python i2c-gps.py > /tmp/gps
Configure GPSD to use the virtual buffer

No you can configure GPSD to point to the virtual buffer.

pi@raspberrypi ~ $ sudo nano /etc/default/gpsd

Look for
DEVICES=””
and change it to
DEVICES=”/tmp/gps”

Restart GPSD so the new settings take effect.

pi@raspberrypi ~ $ sudo systemctl restart gpsd.socket

 

You can now start using your GPS module with your Raspberry Pi

Method 2 – Bit Bang I2C

Confirm that you do not have I2C enabled. There should be no i2c devices under /dev/

pi@raspberrypi ~ $ ls /dev/i2c*
ls: cannot access ‘/dev/i2c*’: No such file or directory

 

If you do have I2C enabled, the above command will return a file under the /dev/ directory.
You can disable I2C in /boot/config.txt

pi@raspberrypi ~ $ sudo nano /boot/config.txt

 

Look for the below line and comment it out by adding a “#” in front.

dtparam=i2c_arm=on

Now reboot.

Create the python script which will read the data by bit banging I2C from the GPS module

pi@raspberrypi ~ $ nano i2c-gps.py

 

Copy in the below code;

import time
import signal
import sys
import pigpio
address = 0x42
gpsReadInterval = 0.03
SDA=2
SCL=3
pi = pigpio.pi()
pi.set_pull_up_down(SDA, pigpio.PUD_UP)
pi.set_pull_up_down(SCL, pigpio.PUD_UP)
pi.bb_i2c_open(SDA,SCL,100000)
def handle_ctrl_c(signal, frame):
        pi.bb_i2c_close(SDA)
        pi.stop()
        sys.exit(130)
#This will capture exit when using Ctrl-C
signal.signal(signal.SIGINT, handle_ctrl_c)
def readGPS():
    c = None
    response = []
    while True: # Newline, or bad char.
        a=pi.bb_i2c_zip(SDA, [4, address, 2, 6, 1])  # Bit bang I2C read. 2 = Start, 6 = read, 1= How many bytes to read
        c = ord(a[1])
        if c == 255:
            return False
        elif c == 10:
            break
        else:
            response.append(c)
    gpsChars = ''.join(chr(c) for c in response)  #Convert list to string
    print gpsChars
while True:
    readGPS()
    time.sleep(gpsReadInterval)

 

Create a virtual node, this is where we will send the NMEA sentences from the GPS module to

pi@raspberrypi ~ $ mknod /tmp/gps p

 

Now run the python script and redirect the output to the virtual node we just created.
We will also use stdbuf so the output from the script is sent to the virtual node one line at a time. Without this, the output is buffered and only sent to the virtual node when the buffer is full.

pi@raspberrypi ~ $ stdbuf -oL python i2c-gps.py > /tmp/gps
Configure GPSD to use the virtual buffer

No you can configure GPSD to point to the virtual buffer

pi@raspberrypi ~ $ sudo nano /etc/default/gpsd

Look for
DEVICES=””
and change it to
DEVICES=”/tmp/gps”

Restart GPSD so the new settings take effect.

pi@raspberrypi ~ $ sudo systemctl restart gpsd.socket

 

You can now start using your GPS module with your Raspberry Pi