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.

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