How it works
UBX uses an 8-bit Fletcher algorithm over the message body. The checksum covers the class byte, ID byte, 2-byte little-endian length, and the payload. The sync bytes (0xB5 0x62) are explicitly excluded.
CK_A = 0, CK_B = 0
for each byte B in [CLASS, ID, LEN_LO, LEN_HI, PAYLOAD...]:
CK_A = (CK_A + B) & 0xFF
CK_B = (CK_B + CK_A) & 0xFF
The full message frame is:
0xB5 0x62 CLASS ID LEN_LO LEN_HI [PAYLOAD...] CK_A CK_B
Example — CFG-MSG (0x06 0x01) to enable GGA on UART1 at 1Hz:
B5 62 06 01 08 00 F0 00 00 00 00 00 00 01 → CK_A=FB, CK_B=49
Full: B5 62 06 01 08 00 F0 00 00 00 00 00 00 01 FB 49
C implementation
void ubx_checksum(const uint8_t *buf, size_t len,
uint8_t *ck_a, uint8_t *ck_b) {
/* buf points to CLASS byte (after the two sync bytes) */
/* len = 4 + payload_length (class + id + 2 length bytes + payload) */
*ck_a = 0;
*ck_b = 0;
for (size_t i = 0; i < len; i++) {
*ck_a += buf[i];
*ck_b += *ck_a;
}
}
Call it as:
uint8_t msg[] = {0x06, 0x01, 0x08, 0x00,
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
uint8_t ck_a, ck_b;
ubx_checksum(msg, sizeof(msg), &ck_a, &ck_b);
/* ck_a = 0xFB, ck_b = 0x49 */
Common mistakes
Including sync bytes in the checksum. The 0xB5 and 0x62 sync chars are frame delimiters, not message content. Starting the checksum from byte 0 of the buffer instead of byte 2 is the most common bug.
Little-endian length field. The length is a 16-bit value in little-endian order. A 8-byte payload → 0x08 0x00, not 0x00 0x08.
Off-by-one on payload length. The length field encodes only the payload size. It does not include class, ID, length bytes, or checksum bytes. Getting this wrong shifts the checksum window by 4 bytes.
ACK/NAK handling. After sending a CFG message, the receiver responds with ACK-ACK (0x05 0x01) or ACK-NAK (0x05 0x00). Always parse the response before sending the next command. Sending CFG messages faster than the receiver can process them causes silently dropped configuration.
Not persisting config. CFG messages only update RAM by default. Send CFG-CFG (0x06 0x09) with saveMask = 0xFFFF to persist to flash/battery-backed RAM, otherwise your configuration resets on power cycle.
Practical u-blox tips
To switch the receiver to output NMEA GGA + RMC only, and disable all other NMEA sentences, send CFG-MSG for each sentence type (F0 00 through F0 08) with the rate byte set to 0 or 1 for the target port.
For polling: send a poll request (class + ID + length 0x00 0x00, no payload) and the receiver replies with the current configuration. Useful for reading out current port settings via CFG-PRT (0x06 0x00) before changing them.