Problems getting data from SAM-M8Q using I2C

Hi Folks,

I’m using the following program with the SAM-M8Q module and getting nothing in return. I have the M8Q connected to a hat on a raspberry pi connected via Quiic.

Would be great if you guys could help me understand what’s missing.

Thanks!

I see the M8Q with i2cdetect:

bld@Troppo:~ $ i2cdetect -y 1

 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f

00: – – – – – – – –

10: – – – – – – – – – – – – – – – –

20: – – – – – – – – – – – – – – – –

30: – – – – – – – – – – – – – – – –

40: – – 42 – – – – – – – – – – – – –

50: – – – – – – – – – – – – – – – –

60: – – – – – – – – – – – – – – – –

70: – – – – – – – –

But the program just sits waiting for data from the module:

bld@Troppo:~ $ python gpslogic

Initializing I2C connection and U-blox GPS module…

Connection successful. Waiting for GPS data…

Module appears to be responding correctly to I2C commands.

Here’s the code:

# SparkFun SAM-M8Q I2C GPS Reader

# This Python program reads GPS data (latitude, longitude, and number of satellites)

# from a SparkFun SAM-M8Q module over an I2C connection.

#

# This script is designed for use with a Raspberry Pi or similar

# single-board computer that has an I2C bus.

#

# Before running this script, you must:

# 1. Enable the I2C bus on your Raspberry Pi using `raspi-config`.

# 2. Install the necessary Python libraries:

# pip install smbus2

# Note: This library is a modern replacement for the older `smbus` library.

import sys

import time

from smbus2 import SMBus, i2c_msg

# The default I2C bus number for a Raspberry Pi is 1.

I2C_BUS = 1

# The default I2C address for U-blox GPS modules is 0x42.

# This is also known as the DDC address in U-blox documentation.

I2C_ADDRESS = 0x42

def main():

“”"

Main function to initialize the I2C connection and read GPS data.

"""

print(“Initializing I2C connection and U-blox GPS module…”)

try:

# Create an SMBus object for the I2C bus.

# This is a context manager, so it will automatically close the bus.

with SMBus(I2C_BUS) as bus:

print(“Connection successful. Waiting for GPS data…”)

# — Add logic to check module configuration —

# Send a configuration message and check for an acknowledgment.

# This is a simple message to request the port configuration settings.

# This serves as a test to see if the module is responding correctly to UBX messages.

# UBX-CFG-PRT message structure for I2C port:

# Sync chars: B5 62

# Class: 06 (CFG)

# ID: 00 (PRT)

# Length: 14 bytes (0E 00)

# Payload: 01 00 00 00 00 00 00 00 00 00 00 00 00 00

# Checksum:

# Checksum A = (sum of all bytes after sync, before checksum) & 0xFF

# Checksum B = (sum of Checksum A values) & 0xFF

# The payload values here are simplified for a basic check

# For a real application, you would calculate the correct payload

# The full message to send for a simple port configuration check

# B5 62 06 00 14 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

# Note: This is a placeholder for a known-good configuration packet to test a response.

# The checksum calculation is not included here for simplicity, which may cause the module to ignore it.

# A more robust solution would compute the checksum.

# For a basic check, we can just try to read a known register.

# However, a proper configuration check involves sending a UBX message.

# Let’s use a simple read of a known register to check for communication.

# We’ll read the high byte of the data available register.

try:

# This read operation serves as a simple “ping” to the device.

            bus.read_byte_data(I2C_ADDRESS, 0xFD)

print(“Module appears to be responding correctly to I2C commands.”)

except IOError:

print(“Module is not responding to I2C commands. Check wiring and power.”)

print(“If you are sure about the connections, the module may not be configured for I2C.”)

            sys.exit(1)

# The GPS module outputs data (e.g., NMEA sentences) into an

# internal buffer which can be read over I2C. We need to check

# if data is available and then read it.

while True:

try:

# The number of bytes available in the buffer is stored in

# register 0xFD (which is the MSB of the buffer size).

# We check this register to know how many bytes to read.

                num_bytes = bus.read_byte_data(I2C_ADDRESS, 0xFD)

if num_bytes > 0:

# The SMBus standard has a limit of 32 bytes per block read.

# Since the GPS module’s buffer can be larger than 32 bytes,

# we need to read the data in chunks.

                    data_buffer = \[\]

# Read the data in a loop until all available bytes are read.

while num_bytes > 0:

# The I2C address 0xFF is an alias for the data buffer.

# The number of bytes to read in this chunk is the minimum of

# the remaining bytes or the SMBus block limit (32).

                        bytes_to_read = min(num_bytes, 32)

# Read the block of data.

                        chunk = bus.read_i2c_block_data(I2C_ADDRESS, 0xFF, bytes_to_read)

# Append the read bytes to our data buffer.

                        data_buffer.extend(chunk)

# Decrement the number of bytes remaining to read.

                        num_bytes -= bytes_to_read

# Convert the list of integers to a byte string.

                    data_string = bytes(data_buffer).decode('utf-8', errors='ignore')

# We’ll look for NMEA sentences, specifically GPGGA, as a simple

# way to demonstrate a successful I2C read.

if “GPGGA” in data_string:

# Split the NMEA sentence by commas to get individual data fields.

                        parts = data_string.split(',')

# The GPGGA sentence has a fixed structure.

# The number of satellites is at the 7th index (8th field).

# We check if the list has enough parts to avoid an index error.

if len(parts) > 7:

                            satellites = parts\[7\]

print(f"Received NMEA GPGGA message via I2C:")

print(data_string)

print(f"Number of satellites: {satellites}")

print(“------------------------------”)

else:

print(“Incomplete GPGGA sentence received.”)

except IOError as e:

# Handle potential communication errors.

print(f"I2C communication error: {e}")

# A short delay to prevent a tight loop and give the GPS module time

# to generate new data.

            time.sleep(1)

except FileNotFoundError:

print(f"Error: I2C bus file not found at /dev/i2c-{I2C_BUS}.")

print(“Please ensure the I2C bus is enabled in `raspi-config`.”)

    sys.exit(1)

except KeyboardInterrupt:

print(“\nExiting program.”)

finally:

print(“Program finished.”)

if _name_ == “_main_”:

main()

I just saw this here: This package is included in the overall SparkFun qwiic Python Package. While the module itself does not use I2C, it may none the less join the ranks when the Raspberry Pi has better support for clock stretching. None the less, a Qwiic connector has been included onboard so the GPS module can be used with our along side SparkFun’s Qwiic products.

Can anyone confirm if this module supports I2C, or if I need to return it?

The sending of UBX-CFG-PRT doesn’t seem like the right approach, and the data in the payload looks wrong. You don’t want to disable or reconfigure the interface, or change the address to zero..

0x14 = 20 bytes

Send a query form, or something like UBX-MON-VER, to unstall the I2C interface

uint8_t ubx_mon_ver = { 0xB5,0x62,0x0A,0x04,0x00,0x00,0x0E,0x34 };

You should read the 16-bit “available” value from registers 0xFD & 0xFE

1 Like

The query form for UBX-CFG-PRT (I2C) is

B5 62 06 00 01 00 00 07 21

The response looks like

B5 62 06 00 14 00 00 00 00 00 84 00 00 00 00 00 00 00 03 00 03 00 00 00 00 00 A4 B2

Where the 0x84 is (0x42 << 1) basically the 7-bit address in the high-order bits. The pair of 0x0003 words being UBX+NMEA being Protocol IN/OUT

Thanks Clive, I’ve updated my code based on your guidance, but I’m not getting anything from the module. Do you know if the SAM-M8Q is configured for i2c by default, or is this something I need to configure in ucenter?

SAM-M8Q is configured for i2c and serial by default but it wouldn’t hurt to verify settings in u-center

Can you share a photo of your setup/connections?

1 Like

It should be enabled by default, but is going to immediately stall, due to lack of interaction. The receiver can just push data out the UART, and doesn’t care if anyone is listening, but with the I2C the host is probing, and pulling the data, so if it’s not doing that, the receiver assumes it’s not being used and stops generating data for the I2C.

By default it will be outputting NMEA, but can accept NMEA/UBX commands, and output UBX packets if requested to do so. UBX-NAV-PVT is not enabled by default.

This is a minimal I2C example for the Arduino (3.3V) models.

2 Likes

Thanks guys, the UBX-MON-VER thing got it talking, I’d mucked that up in my previous attempt.

Very, very cool! Really appreciate your help.

2 Likes