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()