Contents
u-blox MAX-M10S And The UBX Protocol
As part of my university's rocketry team, I have been working on a system to find and transmit our rocket's location after it lands. The GNSS module I went with was the u-blox MAX-M10S from Pi Hut, hence this article.
This is also the first article on Micropedia that is NOT about an STM32 microcontroller and is instead about an external peripheral module, marking a new milestone in the website's journey!
Foreword
If you're here, you probably bought one of these GNSS (Global Navigation Satellite System) receivers. Thus, you probably know what a GNSS module is - it gets its location through communication with satellites alongside some maths, exactly like your phone does when you open Google Maps!
I won't go into the technical details of how GNSS works, there are plenty of good beginners guides on YouTube and the broader internet, here I am focusing specifically on how to set up and configure the u-blox MAX-M10S at the register-level, by sending VALSET/VALGET/VALDEL messages via the UBX protocol, as outlined in the datasheets.
What Is UBX?
UBX is a proprietary messaging protocol for ublox devices. It's the alternative to NMEA, which is more 'standard' since it's a standard protocol that's intended to be able to work on any GNSS device.
Although NMEA is better if you value code portability, UBX is more lightweight and gets parsed faster, which is what I was looking for in my project before deciding to write this article.
I will write an NMEA article as well, have a look as it may be out by now.
Message Naming
You'll frequenly see strings that look like mumbo-jumbo throughout this article - they're actually the message naming conventions set by u-blox.
They look like this: <protocol name>-<class name>-<message name>, for example UBX-NAV-PVT.
These messages can also be separated by specific fields found inside them (like microcontroller registers, such as the STM32's UART CR1 register having the field UE inside it), and you will see that with a dot (.) placed between the message and field, such as UBX-MON-VER.swVersion.
There is also a final message standard which uses message versions. It's a way of denoting between two types of the same message (at least how I understood it so far). E.g. you have the UBX-MGA-GPS message which can be represented in two different ways, each having its own message:
UBX-MGA-GPS-EPHUBX-MGA-GPS-ALM
Configuration Messages
Configuration messages have a specific format: CFG-<group>-<item>, such as this message: CFG-NAVSPG-DYNMODEL.
Hopefully you will no longer worry about how to interpret these weird-looking strings and instead understand what they are!
Satellite Fields
Many UBX messages have information on specific satellites.
The relevant fields are:
gnssIdcontains the ID of which GNSS the satellite belongs to.svIdcontains the number of the satellite in its GNSS system (SV stands for Space Vehicle).
GLONASS satellites can be tracked before they're identified. In that case, their svId is reported as 255.
In the case multiple messages arrive from the same satellite a separate field is used:
sigIdidentifies the unique signal in the case multiple different signals are received from one satellite. Note it's only valid if paired with agnssId.
Here are the satellite numbers, only the 3rd column from the right includes useful id fields, the rightmost column seems to be for the NMEA protocol from what I understood:
The ublox interface descriptor document contains tables with satellite identifiers and their respective gnssId/svId fields, the document has a few different tables for it and one even splits across 2 pages, so adding screenshots was awkward.
Look out for table 2 in the document as that's the UBX one specifically.
More On sigId
As mentioned above, sigId is a field which identifies the specific signal is being referenced from the same satellite.
Again, the ublox interface descriptor document has a table containing the meaning of each gnssId/sigId combination - it's in table 4.
Message Types
There are 11 message types outlined by ublox, and I find most of them to be rather self-explanatory. Here they are regardless, with a short summary:
- input: only ever input into the receiver and never output
- output: only ever output from the receiver at no set interval
- input/output: previous two combined
- periodic: output at regular intervals
- periodic/polled: output at regular intervals, can be polled
- command: commands to the receiver, input-only
- get: gets receiver configuration info
- set: sets receiver configuration
- get/set: previous two combined
- polled: non-periodic, poll-only messages
- poll request: poll request
UBX Protocol
Time for the meat of the article.
The UBX protocol is a proprietary protocol designed by ublox, and any ublox receiver can communicate through it.
The 2 main benefits are that it's compact, making use of every bit transferred and thus being efficient, and it's also checksum protected, meaning errors are less likely to slip through.
Side note - the checksum is also a low overhead, it's only O(n) complexity so it scales well with longer packets.
UBX Packet Layout
Here's the structure of a basic UBX packet:
| 0xB5 | 0x62 | Class | ID | Length | Payload | CK_A | CK_B |
|---|---|---|---|---|---|---|---|
| 1 byte | 1 byte | 1 byte | 1 byte | 2 bytes | N bytes | 1 byte | 1 byte |
Preamble
Every packet starts with the exact same 2-byte preamble: 0xB5 0x62.
Generally, the preamble's purpose is to synchronise the controllers and signal the start of a packet. However, in the case of ublox controllers, since they can communicate with both UBX and NMEA protocols, the preamble also serves to identify that the incoming data is a UBX packet.
Class
UBX messages are modular - they have a:
- Class ID to identify the general message class
- Message ID to identify the specific message within the class
This 1-byte field specifies the class.
ID
1-byte field that specifies that message ID of the packet.
Length
This is the length IN BYTES of the impending payload section.
Since the payload doesn't have a fixed length (labelled 'N' on the table above), this is how the receiver knows the length of the data.
Payload
This is the actual data. Note it must be equal in length to the value specified by the Length field.
CK_A / CK_B
These are the 2 bytes for the aforementioned checksum.
Check the dedicated checksum section - it's short but exists for the sake of indexing it on the contents tab of the article.
Data Types
The UBX protocol defines several data types and uses symbols to denote them throughout documentation. Here they are:
- U1/U2/U4: unsigned 8/16/32-bit integer
- I1/I2/I4: signed 8/16/32-bit integer, two's complement
- X1: 8-bit field
- X2/X4: 16/32-bit field, little-endian
- R4/R8: IEEE 754 single/double precision floating-point value
- CH: ASCII character
- Un: unsigned bitfield value, n-bits wide
- In: signed bitfield value, n-bits wide, two's complement
- Sn: signed bitfield value, n-bits wide, MSB is the sign, the other bits are the magnitude
Miscellaneous
Here are some final, rapid-fire things from the datasheet which don't need their own section but are probably worth mentioning.
Units
Where needed, units are defined.
If possible, standard units are used (e.g. 'm' for meters, 's' for seconds), and time is represented through years, months, days, hours, days, minutes, and seconds.
Scales
Sometimes you need to represent one unit as another.
In that case, a scale is used.
When the scale is 1, there is no scale applied. Otherwise, using an example, if meters are expressed as centimeters, the scale is 0.01.
Repeated Fields
Some message types have repetition as part of their structure.
There are 2 types of repetition:
- One field repeated a certain number of times
- A group of fields is repeated a certain number of times
The former shows the data type and the repetition count in square braces.
For example, U1[5] means "unsigned 1-byte integer array of size 5".
The latter has 4 sub-types of repetition:
- Variable-by-field: a group is repeated an amount of times specified by an earlier field
- Constant group: a group is repeated a constant amount of times, predefined by the message definition
- Optional group: a group repeated 0 or 1 times, included when the payload is large enough to contain it, and its presence is inferred by the message length
- Variable-by-size: a group is repeated as many times as the remainder of the max payload size allows
Checksum
As mentioned earlier, this is a 2-byte field on every UBX packet.
The checksum is calculated using the following parts of the packet as input:
- Class
- ID
- Length
- Payload
The formula for calculating these is actually the same as what the TCP protocol uses - that's the internet protocol your device has used countless times just by navigating to this page alone!
uint8_t CK_A = 0;
uint8_t CK_B = 0;
for (uint8_t i = 0; i < N; i++)
{
CK_A = CK_A + buffer[i]
CK_B = CK_B + CK_A
}
Index: - CK_A and CK_B: checksum values - buffer: data over which the checksum is calculated - N: length of buffer
'Outlier' Messages
Acknowledgement
There are 2 message types related to this: UBX-ACK-ACK and UBX-ACK-NAK.
When configuration messages are sent to the receiver, it'll reply with one of the two to confirm if the config has been set or not.
Some messages from other classes also use this ACK/NAK mechanism. They are defined in the message explanations.
Polling
Sometimes you want to get info from the receiver.
In that case, you send the required message (the one you want to poll for) to the receiver, however with an empty payload.
The receiver returns the same message but with a populated payload containing the data you need.
Available Messages
This is fully defined in the "u-blox M10 SPG 5.10 - Interface Description" document.
There are too many messages to outline here, and most of it would be copy-pasting an already good resources.
To see all messages you can use, look at section 3.8 - UBX message overview.
Sources
ublox MAX-M10S - Integration Manual u-blox M10 SPG 5.10 - Interface Description