From b79e6ae097d6cc0c0e66b2159a649e59319bddac Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Fri, 20 Feb 2026 10:33:28 +0000 Subject: [PATCH 1/5] Add Unicore MODE example --- examples/Unicore_MODE_Test/Arduino_Boards.h | 70 +++ examples/Unicore_MODE_Test/ESP32.h | 56 +++ examples/Unicore_MODE_Test/Parser_Tests.ino | 201 ++++++++ examples/Unicore_MODE_Test/SAMD21.h | 116 +++++ examples/Unicore_MODE_Test/SAMD51.h | 86 ++++ .../Unicore_MODE_Test/Unicore_MODE_Test.ino | 64 +++ examples/Unicore_MODE_Test/makefile | 454 ++++++++++++++++++ 7 files changed, 1047 insertions(+) create mode 100644 examples/Unicore_MODE_Test/Arduino_Boards.h create mode 100644 examples/Unicore_MODE_Test/ESP32.h create mode 100644 examples/Unicore_MODE_Test/Parser_Tests.ino create mode 100644 examples/Unicore_MODE_Test/SAMD21.h create mode 100644 examples/Unicore_MODE_Test/SAMD51.h create mode 100644 examples/Unicore_MODE_Test/Unicore_MODE_Test.ino create mode 100644 examples/Unicore_MODE_Test/makefile diff --git a/examples/Unicore_MODE_Test/Arduino_Boards.h b/examples/Unicore_MODE_Test/Arduino_Boards.h new file mode 100644 index 0000000..ec7bfc2 --- /dev/null +++ b/examples/Unicore_MODE_Test/Arduino_Boards.h @@ -0,0 +1,70 @@ +/* + Arduino_Boards.h + + Constants and routines for Arduino platforms + + License: MIT. Please see LICENSE.md for more details +*/ + +#if defined(ARDUINO_ARCH_AVR) +// ARDUINO_AVR_UNO + +//---------------------------------------- +// Chip specific routines +//---------------------------------------- + +void initUart() +{ + Serial.begin(115200); + + // Delay to let the hardware initialize + delay(1000); +} + +//---------------------------------------- +// Touch the watch dog timer to prevent a reboot +//---------------------------------------- +#define petWDT() + +//---------------------------------------- +// Output a buffer of data +//---------------------------------------- +void output(uint8_t * buffer, size_t length) +{ + size_t bytesWritten; + + if (Serial) + { + while (length) + { + // Wait until space is available in the FIFO + while (Serial.availableForWrite() == 0); + + // Output the character + bytesWritten = Serial.write(buffer, length); + buffer += bytesWritten; + length -= bytesWritten; + } + } +} + +//---------------------------------------- +// Delay for a while +// +// Inputs: +// seconds: The number of seconds to delay +//---------------------------------------- +void sleep(int seconds) +{ + int count; + + // Determine how many 100mSec intervals are in the specified seconds + count = seconds * 10; + while (count-- > 0) + { + petWDT(); + delay(100); + } +} + +#endif // ARDUINO_ARCH_AVR diff --git a/examples/Unicore_MODE_Test/ESP32.h b/examples/Unicore_MODE_Test/ESP32.h new file mode 100644 index 0000000..df1d6fd --- /dev/null +++ b/examples/Unicore_MODE_Test/ESP32.h @@ -0,0 +1,56 @@ +/* + ESP32.h + + Constants and routines for ESP32 platforms + + License: MIT. Please see LICENSE.md for more details +*/ + +#if defined(ESP32) + +//---------------------------------------- +// Constants +//---------------------------------------- + +const int baudRate = 115200; + +//---------------------------------------- +// Chip specific routines +//---------------------------------------- + +void initUart() +{ + Serial.begin(baudRate); + + // Delay to let the hardware initialize + delay(1000); +} + +//---------------------------------------- +// Output a buffer of data +//---------------------------------------- +void output(uint8_t * buffer, size_t length) +{ + size_t bytesWritten; + + if (Serial) + { + while (length) + { + // Wait until space is available in the FIFO + while (Serial.availableForWrite() == 0); + + // Output the character + bytesWritten = Serial.write(buffer, length); + buffer += bytesWritten; + length -= bytesWritten; + } + } +} + +//---------------------------------------- +// Touch the watch dog timer to prevent a reboot +//---------------------------------------- +#define petWDT() + +#endif // ESP32 diff --git a/examples/Unicore_MODE_Test/Parser_Tests.ino b/examples/Unicore_MODE_Test/Parser_Tests.ino new file mode 100644 index 0000000..c12a090 --- /dev/null +++ b/examples/Unicore_MODE_Test/Parser_Tests.ino @@ -0,0 +1,201 @@ +/* + SparkFun Unicore binary test example sketch + + This example demonstrates how to parse a Unicore Binary data stream + + License: MIT. Please see LICENSE.md for more details +*/ + +#include //http://librarymanager/All#SparkFun_Extensible_Message_Parser + +//---------------------------------------- +// Constants +//---------------------------------------- + +// parserTable index values +#define UM980_NMEA_PARSER_INDEX 0 +#define UM980_UNICORE_HASH_PARSER_INDEX 1 +#define UM980_UNICORE_BINARY_PARSER_INDEX 2 + +// Build the table listing all of the parsers +const SEMP_PARSER_DESCRIPTION * parserTable[] = +{ + &sempNmeaParserDescription, + &sempUnicoreHashParserDescription, + &sempUnicoreBinaryParserDescription, +}; +const int parserCount = sizeof(parserTable) / sizeof(parserTable[0]); + +const uint8_t rawDataStream[] = +{ + // This is the UM980's response to the "MODE\r\n" command + // Note that the $ is _included_ in the checksum, + // causing badNmeaChecksum to be called + // "$command,MODE,response: OK*5D\r\n" + // "#MODE,97,GPS,FINE,2406,465982000,17548,0,18,962;MODE ROVER SURVEY,*1B\r\n" + 0x24,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2C,0x4D,0x4F,0x44,0x45,0x2C,0x72,0x65, + 0x73,0x70,0x6F,0x6E,0x73,0x65,0x3A,0x20,0x4F,0x4B,0x2A,0x35,0x44,0x0D,0x0A, + 0x23,0x4D,0x4F,0x44,0x45,0x2C,0x39,0x37,0x2C,0x47,0x50,0x53,0x2C,0x46,0x49,0x4E, + 0x45,0x2C,0x32,0x34,0x30,0x36,0x2C,0x34,0x36,0x35,0x39,0x38,0x32,0x30,0x30,0x30, + 0x2C,0x31,0x37,0x35,0x34,0x38,0x2C,0x30,0x2C,0x31,0x38,0x2C,0x39,0x36,0x32,0x3B, + 0x4D,0x4F,0x44,0x45,0x20,0x52,0x4F,0x56,0x45,0x52,0x20,0x53,0x55,0x52,0x56,0x45, + 0x59,0x2C,0x2A,0x31,0x42,0x0D,0x0A, + + // This is the response to "BESTNAVB 1.00\r\n" + // "$command,BESTNAVB 1.00,response: OK*7A\r\n" + 0x24,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2C,0x42,0x45,0x53,0x54,0x4E,0x41,0x56, + 0x42,0x20,0x31,0x2E,0x30,0x30,0x2C,0x72,0x65,0x73,0x70,0x6F,0x6E,0x73,0x65,0x3A, + 0x20,0x4F,0x4B,0x2A,0x37,0x41,0x0D,0x0A, + + // This is a valid periodic RECTIMEB message + 0xAA,0x44,0xB5,0x61,0x66,0x00,0x2C,0x00,0x00,0xA0,0x66,0x09,0xE8,0x5D,0xC6,0x1B, + 0x8C,0x44,0x00,0x00,0x00,0x12,0x13,0x00,0x00,0x00,0x00,0x00,0x17,0x77,0xC4,0x19, + 0x0C,0x0F,0x31,0x3F,0x7F,0x38,0xFC,0xC2,0xF5,0x4A,0x3C,0x3E,0x00,0x00,0x00,0x00, + 0x00,0x00,0x32,0xC0,0xEA,0x07,0x00,0x00,0x02,0x14,0x09,0x1A,0x58,0x1B,0x00,0x00, + 0x01,0x00,0x00,0x00,0x63,0xA7,0x47,0xDB, +}; + +// Number of bytes in the rawDataStream +#define RAW_DATA_BYTES (sizeof(rawDataStream)) + +//---------------------------------------- +// Locals +//---------------------------------------- + +// Account for the largest Unicore messages +uint8_t buffer[3095]; +uint32_t dataOffset; +SEMP_PARSE_STATE *parse; + +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + +// Alternate checksum for NMEA parser needed during setup +// **************************************************** +// * Based on the Unicore_GNSS_Arduino_Library v2.0.0 * +// **************************************************** +bool badNmeaChecksum(SEMP_PARSE_STATE *parse) +{ + int alternateChecksum; + bool badChecksum; + int checksum; + + // Check if this is a NMEA parser: NMEA or Unicore Hash + if (parse->type >= UM980_UNICORE_BINARY_PARSER_INDEX) + return false; + + // Older UM980 firmware during setup is improperly adding the '$' + // into the checksum calculation. Convert the received checksum + // characters into binary. + // ************************************************************ + // * Please see Issue #91 * + // * With SEMP >= v2.0.0 * + // * For NMEA: the last two bytes are CR LF, not the checksum * + // * For Unicore Hash: the last two bytes are the checksum * + // ************************************************************ + checksum = sempAsciiToNibble(parse->buffer[parse->length - 1]); + checksum |= sempAsciiToNibble(parse->buffer[parse->length - 2]) << 4; + + // Determine if the checksum also includes the '$' or '#' + alternateChecksum = parse->crc ^ (parse->type ? '#' : '$'); + badChecksum = (alternateChecksum != checksum); + + // Display bad checksums + if (!badChecksum) + { + sempPrintString(output, "Unicore Lib: Message improperly includes "); + sempPrintChar(output, parse->buffer[0]); + sempPrintStringLn(output, " in checksum"); + sempDumpBuffer(output, parse->buffer, parse->length); + } + return badChecksum; +} + +//---------------------------------------- +// Run the parser tests +//---------------------------------------- +void parserTests() +{ + size_t bufferLength; + + // Verify the buffer size + bufferLength = sempGetBufferLength(parserTable, parserCount); + if (sizeof(buffer) < bufferLength) + { + sempPrintString(output, "Set buffer size to >= "); + sempPrintDecimalI32Ln(output, bufferLength); + reportFatalError("Fix the buffer size!"); + } + + // Initialize the parser - with badNmeaChecksum + parse = sempBeginParser("Unicore_Test", parserTable, parserCount, buffer, + bufferLength, processMessage, output, output, + badNmeaChecksum); + if (!parse) + reportFatalError("Failed to initialize the parser"); + + // Obtain a raw data stream from somewhere + sempPrintString(output, "Raw data stream: "); + sempPrintDecimalI32(output, RAW_DATA_BYTES); + sempPrintStringLn(output, " bytes"); + + // The raw data stream is passed to the parser one byte at a time + sempDebugOutputEnable(parse, output); + for (dataOffset = 0; dataOffset < RAW_DATA_BYTES; dataOffset++) + // Update the parser state based on the incoming byte + sempParseNextByte(parse, rawDataStream[dataOffset]); + + // Done parsing the data + sempStopParser(&parse); +} + +//---------------------------------------- +// Call back from within parser, for end of message +// Process a complete message incoming from parser +//---------------------------------------- +void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) +{ + static bool displayOnce = true; + uint32_t offset; + + // Display the raw message + sempPrintLn(output); + offset = dataOffset + 1 - parse->length; + sempPrintString(output, "Valid Unicore message: "); + sempPrintHex0x04x(output, parse->length); + sempPrintString(output, " ("); + sempPrintDecimalI32(output, parse->length); + sempPrintString(output, ") bytes at "); + sempPrintHex0x08x(output, offset); + sempPrintString(output, " ("); + sempPrintDecimalI32(output, offset); + sempPrintCharLn(output, ')'); + sempDumpBuffer(output, parse->buffer, parse->length); + + // Display the parser state + if (displayOnce) + { + displayOnce = false; + sempPrintLn(output); + sempUnicoreBinaryPrintHeader(parse); + sempPrintLn(output); + sempPrintParserConfiguration(parse, output); + } +} + +//---------------------------------------- +// Print the error message every 15 seconds +// +// Inputs: +// errorMsg: Zero-terminated error message string to output every 15 seconds +//---------------------------------------- +void reportFatalError(const char *errorMsg) +{ + while (1) + { + sempPrintString(output, "HALTED: "); + sempPrintStringLn(output, errorMsg); + sleep(15); + } +} diff --git a/examples/Unicore_MODE_Test/SAMD21.h b/examples/Unicore_MODE_Test/SAMD21.h new file mode 100644 index 0000000..496c5a1 --- /dev/null +++ b/examples/Unicore_MODE_Test/SAMD21.h @@ -0,0 +1,116 @@ +/* + SAMD21.h + + Constants and routines for SAMD platforms + + License: MIT. Please see LICENSE.md for more details +*/ + +#if defined(ARDUINO_SAMD_ZERO) + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// Includes +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +#include "SAMDTimerInterrupt.h" //http://librarymanager/All#SAMD_TimerInterrupt v1.10.1 by Koi Hang +#include //https://github.com/javos65/WDTZero + +//---------------------------------------- +// Constants +//---------------------------------------- + +const int baudRate = 115200; +const int pinCTS = 30; +const int pinRTS = 38; + +//---------------------------------------- +// Locals +//---------------------------------------- + +WDTZero myWatchDog; + +//---------------------------------------- +// Chip specific routines +//---------------------------------------- +void initUart() +{ + // Start watchdog timer + myWatchDog.setup(WDT_HARDCYCLE2S); // Initialize WDT with 2s timeout + + // Start the serial port + Serial.begin(baudRate); + + // Flow control + pinMode(pinCTS, INPUT_PULLUP); + pinMode(pinRTS, OUTPUT); + digitalWrite(pinRTS, 1); + + // Delay to let the hardware initialize + delay(1000); + + // Wait for USB serial to become available + while (Serial == false); +} + +//---------------------------------------- +// Touch the watch dog timer to prevent a reboot +//---------------------------------------- +void petWDT() +{ + static uint32_t lastPet; + + // Petting the dog takes a long time (~4.5ms on SAMD21) so it's only + // done just before the 2 seccond limit + if ((millis() - lastPet) > 1800) + { + lastPet = millis(); + + // This takes 4-5ms to complete + myWatchDog.clear(); + } +} + +//---------------------------------------- +// Output a buffer of data +//---------------------------------------- +void output(uint8_t * buffer, size_t length) +{ + size_t bytesWritten; + + if (Serial) + { + while (length) + { + // Wait until space is available in the FIFO + while (Serial.availableForWrite() == 0) + petWDT(); + + // Output the character + bytesWritten = Serial.write(buffer, length); + buffer += bytesWritten; + length -= bytesWritten; + petWDT(); + } + } +} + +//---------------------------------------- +// Delay for a while +// +// Inputs: +// seconds: The number of seconds to delay +//---------------------------------------- +void sleep(int seconds) +{ + int count; + + // Determine how many 100mSec intervals are in the specified seconds + count = seconds * 10; + while (count-- > 0) + { + petWDT(); + delay(100); + } +} + +#endif // ARDUINO_SAMD_ZERO diff --git a/examples/Unicore_MODE_Test/SAMD51.h b/examples/Unicore_MODE_Test/SAMD51.h new file mode 100644 index 0000000..9564be2 --- /dev/null +++ b/examples/Unicore_MODE_Test/SAMD51.h @@ -0,0 +1,86 @@ +/* + SAMD51.h + + Constants and routines for SAMD51 platforms + + License: MIT. Please see LICENSE.md for more details +*/ + +#if defined(ARDUINO_SAMD51_THING_PLUS) || defined(ARDUINO_SAMD51_MICROMOD) + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// Includes +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +//---------------------------------------- +// Constants +//---------------------------------------- + +const int baudRate = 115200; + +//---------------------------------------- +// Chip specific routines +//---------------------------------------- +void initUart() +{ + // Start watchdog timer +// myWatchDog.setup(WDT_HARDCYCLE2S); // Initialize WDT with 2s timeout + + // Start the serial port + Serial.begin(baudRate); + + // Delay to let the hardware initialize + delay(1000); + + // Wait for USB serial to become available + while (Serial == false); +} + +//---------------------------------------- +// Touch the watch dog timer to prevent a reboot +//---------------------------------------- +#define petWDT() + +//---------------------------------------- +// Output a buffer of data +//---------------------------------------- +void output(uint8_t * buffer, size_t length) +{ + size_t bytesWritten; + + if (Serial) + { + while (length) + { + // Wait until space is available in the FIFO + while (Serial.availableForWrite() == 0) + petWDT(); + + // Output the character + bytesWritten = Serial.write(buffer, length); + buffer += bytesWritten; + length -= bytesWritten; + } + } +} + +//---------------------------------------- +// Delay for a while +// +// Inputs: +// seconds: The number of seconds to delay +//---------------------------------------- +void sleep(int seconds) +{ + int count; + + // Determine how many 100mSec intervals are in the specified seconds + count = seconds * 10; + while (count-- > 0) + { + petWDT(); + delay(100); + } +} + +#endif // ARDUINO_SAMD51_THING_PLUS diff --git a/examples/Unicore_MODE_Test/Unicore_MODE_Test.ino b/examples/Unicore_MODE_Test/Unicore_MODE_Test.ino new file mode 100644 index 0000000..52b997a --- /dev/null +++ b/examples/Unicore_MODE_Test/Unicore_MODE_Test.ino @@ -0,0 +1,64 @@ +/* + SparkFun Unicore binary test example sketch + + This example demonstrates how to parse a Unicore Binary data stream + + License: MIT. Please see LICENSE.md for more details +*/ + +#include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "Arduino_Boards.h" +#include "ESP32.h" +#include "SAMD21.h" +#include "SAMD51.h" + +//---------------------------------------- +// Locals +//---------------------------------------- + +bool runTests; + +//---------------------------------------- +// Application entry point used to initialize the system +//---------------------------------------- +void setup() +{ + initUart(); + sempPrintLn(output); + sempPrintStringLn(output, "Unicore_Test example sketch"); + sempPrintLn(output); + + // Run the tests + runTests = true; +} + +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- +void loop() +{ + // Keep the system running + petWDT(); + + // Determine if a character was input + if (Serial) + { + if (Serial.available()) + { + Serial.read(); + + // If so, run the tests again + runTests = true; + } + } + + // Run the tests when requested + if (runTests) + { + runTests = false; + + // Run the tests + parserTests(); + sempPrintStringLn(output, "All done"); + } +} diff --git a/examples/Unicore_MODE_Test/makefile b/examples/Unicore_MODE_Test/makefile new file mode 100644 index 0000000..b80f2ea --- /dev/null +++ b/examples/Unicore_MODE_Test/makefile @@ -0,0 +1,454 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=Unicore_Binary_Test +ESP32_CHIP=esp32 +PARTITION_FILE_NAME=RTKEverywhere + +########## +# OS specific paths +########## + +ifeq ($(OS),Windows_NT) +#--------- +# Windows NT generic paths +#--------- + +USER_DIRECTORY_PATH=C:\Users\$(USERNAME)\ +EXAMPLE_PATH=..\ + +ARDUINO_PATH=$(USER_DIRECTORY_PATH)Documents\Arduino\ +ARDUINO_LIBRARY_PATH=$(ARDUINO_PATH)libraries\ +BUILD_PATH=build\ +ESP32_BIN_PATH=$BUILD_PATH)esp32.esp32.$(ESP32_CHIP)\ +BOOT_LOADER_PATH=..\..\binaries\ +ESPTOOL_PATH=$(USER_DIRECTORY_PATH)\.arduino15\packages\esp32\tools\esptool_py\4.5.1\ +HOME_BOARD_PATH=$(USER_DIRECTORY_PATH)AppData\Local\Arduino15\packages\esp32\ +READ_MAP_FILE_PATH= + +# Windows NT utilities +ARDUINO_CLI=~/Arduino/arduino-cli +CLEAR=cls +COPY=copy +DELETE=rmdir /s +TERMINAL_APP= + +TERMINAL_PORT=COM3 +TERMINAL_PARAMS= + +else +#--------- +# Linux generic paths +#--------- + +USER_DIRECTORY_PATH=~/ +EXAMPLE_PATH=../ + +ARDUINO_PATH=$(USER_DIRECTORY_PATH)Arduino/ +ARDUINO_LIBRARY_PATH=$(ARDUINO_PATH)libraries/ +BUILD_PATH=build/ +ESP32_BIN_PATH=$(BUILD_PATH)esp32.esp32.$(ESP32_CHIP)/ +BOOT_LOADER_PATH=~/SparkFun/SparkFun_RTK_Firmware_Uploader/RTK_Firmware_Uploader/resource/ +ESP_IDF_PATH=$(HOME_BOARD_PATH)tools/esp32-arduino-libs/ +ESPTOOL_PATH=$(USER_DIRECTORY_PATH)Arduino/hardware/espressif/esp32/tools/esptool/ +HOME_BOARD_PATH=$(USER_DIRECTORY_PATH).arduino15/packages/esp32/ +READ_MAP_FILE_PATH=$(USER_DIRECTORY_PATH)SparkFun/rc/RTK/Firmware/Tools/ + +# Linux utilities +ARDUINO_CLI=$(USER_DIRECTORY_PATH)bin/arduino-cli +CLEAR=clear +DELETE=rm -Rf + +TERMINAL_APP=minicom +TERMINAL_PARAMS=-8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +ESP32_BIN_FILE=$(ESP32_BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(ESP32_BIN_FILE) +EXECUTABLES += samd21_LoRaSerial +EXECUTABLES += samd51_thing_plus +EXECUTABLES += micromod_samd51 +#EXECUTABLES += arduino_uno_r3 + +.PHONY: all + +all: $(EXECUTABLES) + +########## +# Open the terminal (tty) +########## + +BAUDRATE=115200 + +.PHONY: terminal + +terminal: + if [ -a "/dev/ttyUSB0" ]; then + export TERMINAL_PORT=/dev/ttyUSB0 + else + if [ -a "/dev/ttyUSB1" ]; then + export TERMINAL_PORT=/dev/ttyUSB1 + else + if [ -a "/dev/ttyUSB2" ]; then + export TERMINAL_PORT=/dev/ttyUSB2 + else + if [ -a "/dev/ttyUSB3" ]; then + export TERMINAL_PORT=/dev/ttyUSB3 + else + if [ -a "/dev/ttyACM0" ]; then + export TERMINAL_PORT=/dev/ttyACM0 + else + if [ -a "/dev/ttyACM1" ]; then + export TERMINAL_PORT=/dev/ttyACM1 + else + if [ -a "/dev/ttyACM2" ]; then + export TERMINAL_PORT=/dev/ttyACM2 + else + if [ -a "/dev/ttyACM3" ]; then + export TERMINAL_PORT=/dev/ttyACM3 + fi + fi + fi + fi + fi + fi + fi + fi + echo $$TERMINAL_PORT + $(TERMINAL_APP) -D $$TERMINAL_PORT -b $(BAUDRATE) $(TERMINAL_PARAMS) + +########## +# Build the ESP32 Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(ESP32_BIN_FILE): $(SKETCH).ino *.h *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + echo $(SKETCH) - ESP32 + $(ARDUINO_CLI) \ + compile \ + --fqbn "esp32:esp32:$(ESP32_CHIP)":DebugLevel=$(DEBUG_LEVEL) \ + $< \ + --warnings default \ + --build-property menu.PSRAM.enabled=Enabled \ + --build-property build.partitions=$(PARTITION_FILE_NAME) \ + --build-property build.flash_freq=80m \ + --build-property build.flash_mode=dio \ + --build-property build.flash_size=4MB \ + --build-property upload.speed=921600 \ + --export-binaries + +########## +# Upload the ESP32 firmware +########## + +.PHONY: upload + +upload: $(ESP32_BIN_FILE) + if [ -a "/dev/ttyUSB0" ]; then + export TERMINAL_PORT=/dev/ttyUSB0 + else + if [ -a "/dev/ttyUSB1" ]; then + export TERMINAL_PORT=/dev/ttyUSB1 + else + if [ -a "/dev/ttyUSB2" ]; then + export TERMINAL_PORT=/dev/ttyUSB2 + else + if [ -a "/dev/ttyUSB3" ]; then + export TERMINAL_PORT=/dev/ttyUSB3 + else + if [ -a "/dev/ttyACM0" ]; then + export TERMINAL_PORT=/dev/ttyACM0 + else + if [ -a "/dev/ttyACM1" ]; then + export TERMINAL_PORT=/dev/ttyACM1 + else + if [ -a "/dev/ttyACM2" ]; then + export TERMINAL_PORT=/dev/ttyACM2 + else + if [ -a "/dev/ttyACM3" ]; then + export TERMINAL_PORT=/dev/ttyACM3 + fi + fi + fi + fi + fi + fi + fi + fi + echo $$TERMINAL_PORT + python3 $(ESPTOOL_PATH)esptool.py \ + --chip esp32 \ + --port $$TERMINAL_PORT \ + --baud 921600 \ + --before default_reset \ + --after hard_reset \ + write_flash \ + --flash_mode dio \ + --flash_freq 80m \ + --flash_size detect \ + --compress \ + 0x1000 $(BOOT_LOADER_PATH)RTK_Surveyor.ino.bootloader.bin \ + 0x8000 $(BOOT_LOADER_PATH)RTK_Surveyor_Partitions_4MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $$TERMINAL_PORT -b $(BAUDRATE) $(TERMINAL_PARAMS) + +########## +# Build the SAMD21 Firmware +########## + +samd21_LoRaSerial: $(SKETCH).ino *.h *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + echo $(SKETCH) - $@ + $(ARDUINO_CLI) \ + compile \ + $< \ + --fqbn SparkFun:samd:LoRaSerial + +########## +# Upload the SAMD21 firmware +########## + +samd21_LoRaSerial_upload: $(SKETCH).ino samd21_LoRaSerial + if [ -a "/dev/ttyUSB0" ]; then + export TERMINAL_PORT=/dev/ttyUSB0 + else + if [ -a "/dev/ttyUSB1" ]; then + export TERMINAL_PORT=/dev/ttyUSB1 + else + if [ -a "/dev/ttyUSB2" ]; then + export TERMINAL_PORT=/dev/ttyUSB2 + else + if [ -a "/dev/ttyUSB3" ]; then + export TERMINAL_PORT=/dev/ttyUSB3 + else + if [ -a "/dev/ttyACM0" ]; then + export TERMINAL_PORT=/dev/ttyACM0 + else + if [ -a "/dev/ttyACM1" ]; then + export TERMINAL_PORT=/dev/ttyACM1 + else + if [ -a "/dev/ttyACM2" ]; then + export TERMINAL_PORT=/dev/ttyACM2 + else + if [ -a "/dev/ttyACM3" ]; then + export TERMINAL_PORT=/dev/ttyACM3 + fi + fi + fi + fi + fi + fi + fi + fi + echo $$TERMINAL_PORT + $(ARDUINO_CLI) \ + upload $< \ + -p $$TERMINAL_PORT \ + -b SparkFun:samd:LoRaSerial + $(TERMINAL_APP) -D $$TERMINAL_PORT -b $(BAUDRATE) $(TERMINAL_PARAMS) + +########## +# Build the SAMD51 Firmware +########## + +samd51_thing_plus: $(SKETCH).ino *.h *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + echo $(SKETCH) - $@ + $(ARDUINO_CLI) \ + compile \ + $< \ + --fqbn SparkFun:samd:samd51_thing_plus + +########## +# Upload the SAMD51 firmware +########## + +samd51_thing_plus_upload: $(SKETCH).ino samd51_thing_plus + if [ -a "/dev/ttyUSB0" ]; then + export TERMINAL_PORT=/dev/ttyUSB0 + else + if [ -a "/dev/ttyUSB1" ]; then + export TERMINAL_PORT=/dev/ttyUSB1 + else + if [ -a "/dev/ttyUSB2" ]; then + export TERMINAL_PORT=/dev/ttyUSB2 + else + if [ -a "/dev/ttyUSB3" ]; then + export TERMINAL_PORT=/dev/ttyUSB3 + else + if [ -a "/dev/ttyACM0" ]; then + export TERMINAL_PORT=/dev/ttyACM0 + else + if [ -a "/dev/ttyACM1" ]; then + export TERMINAL_PORT=/dev/ttyACM1 + else + if [ -a "/dev/ttyACM2" ]; then + export TERMINAL_PORT=/dev/ttyACM2 + else + if [ -a "/dev/ttyACM3" ]; then + export TERMINAL_PORT=/dev/ttyACM3 + fi + fi + fi + fi + fi + fi + fi + fi + echo $$TERMINAL_PORT + $(ARDUINO_CLI) \ + upload $< \ + -p $$TERMINAL_PORT \ + -b SparkFun:samd:samd51_thing_plus + $(TERMINAL_APP) -D $$TERMINAL_PORT -b $(BAUDRATE) $(TERMINAL_PARAMS) + +########## +# Build the MicroMod SAMD51 Firmware +########## + +micromod_samd51: $(SKETCH).ino *.h *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + echo $(SKETCH) - $@ + $(ARDUINO_CLI) \ + compile \ + $< \ + --fqbn SparkFun:samd:micromod_samd51 + +########## +# Upload the MicroMod SAMD51 firmware +########## + +micromod_samd51_upload: $(SKETCH).ino micromod_samd51 + if [ -a "/dev/ttyUSB0" ]; then + export TERMINAL_PORT=/dev/ttyUSB0 + else + if [ -a "/dev/ttyUSB1" ]; then + export TERMINAL_PORT=/dev/ttyUSB1 + else + if [ -a "/dev/ttyUSB2" ]; then + export TERMINAL_PORT=/dev/ttyUSB2 + else + if [ -a "/dev/ttyUSB3" ]; then + export TERMINAL_PORT=/dev/ttyUSB3 + else + if [ -a "/dev/ttyACM0" ]; then + export TERMINAL_PORT=/dev/ttyACM0 + else + if [ -a "/dev/ttyACM1" ]; then + export TERMINAL_PORT=/dev/ttyACM1 + else + if [ -a "/dev/ttyACM2" ]; then + export TERMINAL_PORT=/dev/ttyACM2 + else + if [ -a "/dev/ttyACM3" ]; then + export TERMINAL_PORT=/dev/ttyACM3 + fi + fi + fi + fi + fi + fi + fi + fi + echo $$TERMINAL_PORT + $(ARDUINO_CLI) \ + upload $< \ + -p $$TERMINAL_PORT \ + -b SparkFun:samd:micromod_samd51 + $(TERMINAL_APP) -D $$TERMINAL_PORT -b $(BAUDRATE) $(TERMINAL_PARAMS) + +########## +# Build the Arduino Uno R3 firmware +########## + +arduino_uno_r3: $(SKETCH).ino *.h *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + echo $(SKETCH) - $@ + $(ARDUINO_CLI) \ + compile \ + $< \ + --fqbn arduino:avr:uno + +########## +# Upload the Arduino Uno R3 firmware +########## + +arduino_uno_r3_upload: $(SKETCH).ino arduino_uno_r3 + if [ -a "/dev/ttyUSB0" ]; then + export TERMINAL_PORT=/dev/ttyUSB0 + else + if [ -a "/dev/ttyUSB1" ]; then + export TERMINAL_PORT=/dev/ttyUSB1 + else + if [ -a "/dev/ttyUSB2" ]; then + export TERMINAL_PORT=/dev/ttyUSB2 + else + if [ -a "/dev/ttyUSB3" ]; then + export TERMINAL_PORT=/dev/ttyUSB3 + else + if [ -a "/dev/ttyACM0" ]; then + export TERMINAL_PORT=/dev/ttyACM0 + else + if [ -a "/dev/ttyACM1" ]; then + export TERMINAL_PORT=/dev/ttyACM1 + else + if [ -a "/dev/ttyACM2" ]; then + export TERMINAL_PORT=/dev/ttyACM2 + else + if [ -a "/dev/ttyACM3" ]; then + export TERMINAL_PORT=/dev/ttyACM3 + fi + fi + fi + fi + fi + fi + fi + fi + echo $$TERMINAL_PORT + $(ARDUINO_CLI) \ + upload \ + -p $$TERMINAL_PORT \ + --fqbn arduino:avr:uno \ + --verbose \ + . + $(TERMINAL_APP) -D $$TERMINAL_PORT -b $(BAUDRATE) $(TERMINAL_PARAMS) + +########## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) From 3a4573172fa2eee0aa1ca0020208ff58762a0015 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sat, 21 Feb 2026 08:17:21 +0000 Subject: [PATCH 2/5] Correct SKETCH name in makefile --- examples/Unicore_MODE_Test/makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Unicore_MODE_Test/makefile b/examples/Unicore_MODE_Test/makefile index b80f2ea..990d7d1 100644 --- a/examples/Unicore_MODE_Test/makefile +++ b/examples/Unicore_MODE_Test/makefile @@ -11,7 +11,7 @@ SHELL=/bin/bash # Source files ########## -SKETCH=Unicore_Binary_Test +SKETCH=Unicore_MODE_Test ESP32_CHIP=esp32 PARTITION_FILE_NAME=RTKEverywhere From 32ae06befb5fc9f0a123e886fcaba4a67fb0a566 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sat, 21 Feb 2026 08:17:51 +0000 Subject: [PATCH 3/5] Update comments --- examples/Unicore_MODE_Test/Unicore_MODE_Test.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/Unicore_MODE_Test/Unicore_MODE_Test.ino b/examples/Unicore_MODE_Test/Unicore_MODE_Test.ino index 52b997a..ed92354 100644 --- a/examples/Unicore_MODE_Test/Unicore_MODE_Test.ino +++ b/examples/Unicore_MODE_Test/Unicore_MODE_Test.ino @@ -1,7 +1,8 @@ /* - SparkFun Unicore binary test example sketch + SparkFun Unicore MODE test example sketch - This example demonstrates how to parse a Unicore Binary data stream + This example demonstrates how to parse a Unicore command response + e.g. MODE License: MIT. Please see LICENSE.md for more details */ From 6c1b09ec8b387bf0e1e0620005c316fd947a40c6 Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sat, 21 Feb 2026 08:18:34 +0000 Subject: [PATCH 4/5] Add Hash test with 8-byte checksum --- examples/Unicore_MODE_Test/Parser_Tests.ino | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/Unicore_MODE_Test/Parser_Tests.ino b/examples/Unicore_MODE_Test/Parser_Tests.ino index c12a090..8f53591 100644 --- a/examples/Unicore_MODE_Test/Parser_Tests.ino +++ b/examples/Unicore_MODE_Test/Parser_Tests.ino @@ -47,6 +47,19 @@ const uint8_t rawDataStream[] = 0x42,0x20,0x31,0x2E,0x30,0x30,0x2C,0x72,0x65,0x73,0x70,0x6F,0x6E,0x73,0x65,0x3A, 0x20,0x4F,0x4B,0x2A,0x37,0x41,0x0D,0x0A, + // Valid Unicore hash (#) sentence with an 8 byte checksum + // "#VERSION,40,GPS,UNKNOWN,1,1000,0,0,18,15;UM980,R4.10Build7923,HRPT00-S10C-P, + // 2310415000001-MD22B1225023842,ff3b1e9611b3b07b,2022/09/28*b164c965\r\n" + 0x23,0x56,0x45,0x52,0x53,0x49,0x4F,0x4E,0x2C,0x34,0x30,0x2C,0x47,0x50,0x53,0x2C, + 0x55,0x4E,0x4B,0x4E,0x4F,0x57,0x4E,0x2C,0x31,0x2C,0x31,0x30,0x30,0x30,0x2C,0x30, + 0x2C,0x30,0x2C,0x31,0x38,0x2C,0x31,0x35,0x3B,0x55,0x4D,0x39,0x38,0x30,0x2C,0x52, + 0x34,0x2E,0x31,0x30,0x42,0x75,0x69,0x6C,0x64,0x37,0x39,0x32,0x33,0x2C,0x48,0x52, + 0x50,0x54,0x30,0x30,0x2D,0x53,0x31,0x30,0x43,0x2D,0x50,0x2C,0x32,0x33,0x31,0x30, + 0x34,0x31,0x35,0x30,0x30,0x30,0x30,0x30,0x31,0x2D,0x4D,0x44,0x32,0x32,0x42,0x31, + 0x32,0x32,0x35,0x30,0x32,0x33,0x38,0x34,0x32,0x2C,0x66,0x66,0x33,0x62,0x31,0x65, + 0x39,0x36,0x31,0x31,0x62,0x33,0x62,0x30,0x37,0x62,0x2C,0x32,0x30,0x32,0x32,0x2F, + 0x30,0x39,0x2F,0x32,0x38,0x2A,0x62,0x31,0x36,0x34,0x63,0x39,0x36,0x35,0x0D,0x0A, + // This is a valid periodic RECTIMEB message 0xAA,0x44,0xB5,0x61,0x66,0x00,0x2C,0x00,0x00,0xA0,0x66,0x09,0xE8,0x5D,0xC6,0x1B, 0x8C,0x44,0x00,0x00,0x00,0x12,0x13,0x00,0x00,0x00,0x00,0x00,0x17,0x77,0xC4,0x19, From cb7bd06a578f2c804214281d46d3324728cb3d8d Mon Sep 17 00:00:00 2001 From: Paul Clark Date: Sat, 21 Feb 2026 10:47:56 +0000 Subject: [PATCH 5/5] Update example to use combined text and data --- examples/Unicore_MODE_Test/Parser_Tests.ino | 90 +++++++++++---------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/examples/Unicore_MODE_Test/Parser_Tests.ino b/examples/Unicore_MODE_Test/Parser_Tests.ino index 8f53591..6ee0a89 100644 --- a/examples/Unicore_MODE_Test/Parser_Tests.ino +++ b/examples/Unicore_MODE_Test/Parser_Tests.ino @@ -26,40 +26,30 @@ const SEMP_PARSER_DESCRIPTION * parserTable[] = }; const int parserCount = sizeof(parserTable) / sizeof(parserTable[0]); -const uint8_t rawDataStream[] = +const uint8_t modeResponse[] = { // This is the UM980's response to the "MODE\r\n" command // Note that the $ is _included_ in the checksum, // causing badNmeaChecksum to be called - // "$command,MODE,response: OK*5D\r\n" - // "#MODE,97,GPS,FINE,2406,465982000,17548,0,18,962;MODE ROVER SURVEY,*1B\r\n" - 0x24,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2C,0x4D,0x4F,0x44,0x45,0x2C,0x72,0x65, - 0x73,0x70,0x6F,0x6E,0x73,0x65,0x3A,0x20,0x4F,0x4B,0x2A,0x35,0x44,0x0D,0x0A, - 0x23,0x4D,0x4F,0x44,0x45,0x2C,0x39,0x37,0x2C,0x47,0x50,0x53,0x2C,0x46,0x49,0x4E, - 0x45,0x2C,0x32,0x34,0x30,0x36,0x2C,0x34,0x36,0x35,0x39,0x38,0x32,0x30,0x30,0x30, - 0x2C,0x31,0x37,0x35,0x34,0x38,0x2C,0x30,0x2C,0x31,0x38,0x2C,0x39,0x36,0x32,0x3B, - 0x4D,0x4F,0x44,0x45,0x20,0x52,0x4F,0x56,0x45,0x52,0x20,0x53,0x55,0x52,0x56,0x45, - 0x59,0x2C,0x2A,0x31,0x42,0x0D,0x0A, + "$command,MODE,response: OK*5D\r\n" + "#MODE,97,GPS,FINE,2406,465982000,17548,0,18,962;MODE ROVER SURVEY,*1B\r\n" +}; +const uint8_t bestNavResponse[] = +{ // This is the response to "BESTNAVB 1.00\r\n" - // "$command,BESTNAVB 1.00,response: OK*7A\r\n" - 0x24,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2C,0x42,0x45,0x53,0x54,0x4E,0x41,0x56, - 0x42,0x20,0x31,0x2E,0x30,0x30,0x2C,0x72,0x65,0x73,0x70,0x6F,0x6E,0x73,0x65,0x3A, - 0x20,0x4F,0x4B,0x2A,0x37,0x41,0x0D,0x0A, + "$command,BESTNAVB 1.00,response: OK*7A\r\n" +}; +const uint8_t unicoreHash8ByteCrc[] = +{ // Valid Unicore hash (#) sentence with an 8 byte checksum - // "#VERSION,40,GPS,UNKNOWN,1,1000,0,0,18,15;UM980,R4.10Build7923,HRPT00-S10C-P, - // 2310415000001-MD22B1225023842,ff3b1e9611b3b07b,2022/09/28*b164c965\r\n" - 0x23,0x56,0x45,0x52,0x53,0x49,0x4F,0x4E,0x2C,0x34,0x30,0x2C,0x47,0x50,0x53,0x2C, - 0x55,0x4E,0x4B,0x4E,0x4F,0x57,0x4E,0x2C,0x31,0x2C,0x31,0x30,0x30,0x30,0x2C,0x30, - 0x2C,0x30,0x2C,0x31,0x38,0x2C,0x31,0x35,0x3B,0x55,0x4D,0x39,0x38,0x30,0x2C,0x52, - 0x34,0x2E,0x31,0x30,0x42,0x75,0x69,0x6C,0x64,0x37,0x39,0x32,0x33,0x2C,0x48,0x52, - 0x50,0x54,0x30,0x30,0x2D,0x53,0x31,0x30,0x43,0x2D,0x50,0x2C,0x32,0x33,0x31,0x30, - 0x34,0x31,0x35,0x30,0x30,0x30,0x30,0x30,0x31,0x2D,0x4D,0x44,0x32,0x32,0x42,0x31, - 0x32,0x32,0x35,0x30,0x32,0x33,0x38,0x34,0x32,0x2C,0x66,0x66,0x33,0x62,0x31,0x65, - 0x39,0x36,0x31,0x31,0x62,0x33,0x62,0x30,0x37,0x62,0x2C,0x32,0x30,0x32,0x32,0x2F, - 0x30,0x39,0x2F,0x32,0x38,0x2A,0x62,0x31,0x36,0x34,0x63,0x39,0x36,0x35,0x0D,0x0A, + "#VERSION,40,GPS,UNKNOWN,1,1000,0,0,18,15;UM980,R4.10Build7923,HRPT00-S10C-P," + "2310415000001-MD22B1225023842,ff3b1e9611b3b07b,2022/09/28*b164c965\r\n" +}; +const uint8_t unicoreRecTime[] = +{ // This is a valid periodic RECTIMEB message 0xAA,0x44,0xB5,0x61,0x66,0x00,0x2C,0x00,0x00,0xA0,0x66,0x09,0xE8,0x5D,0xC6,0x1B, 0x8C,0x44,0x00,0x00,0x00,0x12,0x13,0x00,0x00,0x00,0x00,0x00,0x17,0x77,0xC4,0x19, @@ -68,8 +58,22 @@ const uint8_t rawDataStream[] = 0x01,0x00,0x00,0x00,0x63,0xA7,0x47,0xDB, }; -// Number of bytes in the rawDataStream -#define RAW_DATA_BYTES (sizeof(rawDataStream)) +typedef struct _DataStream +{ + size_t length; + const uint8_t *data; +} DataStream; + +#define DATA_STREAM_INIT(x, extraBytes) {sizeof(x) - extraBytes, &x[0]} +const DataStream dataStream[] = +{ + DATA_STREAM_INIT(modeResponse, 1), + DATA_STREAM_INIT(bestNavResponse, 1), + DATA_STREAM_INIT(unicoreHash8ByteCrc, 1), + DATA_STREAM_INIT(unicoreRecTime, 0), +}; + +#define DATA_STREAM_ENTRIES (sizeof(dataStream) / sizeof(dataStream[0])) //---------------------------------------- // Locals @@ -77,6 +81,8 @@ const uint8_t rawDataStream[] = // Account for the largest Unicore messages uint8_t buffer[3095]; +int byteOffset; +int dataIndex; uint32_t dataOffset; SEMP_PARSE_STATE *parse; @@ -85,9 +91,7 @@ SEMP_PARSE_STATE *parse; //------------------------------------------------------------------------------ // Alternate checksum for NMEA parser needed during setup -// **************************************************** -// * Based on the Unicore_GNSS_Arduino_Library v2.0.0 * -// **************************************************** +// Based on the Unicore_GNSS_Arduino_Library v2.0.0 bool badNmeaChecksum(SEMP_PARSE_STATE *parse) { int alternateChecksum; @@ -101,12 +105,6 @@ bool badNmeaChecksum(SEMP_PARSE_STATE *parse) // Older UM980 firmware during setup is improperly adding the '$' // into the checksum calculation. Convert the received checksum // characters into binary. - // ************************************************************ - // * Please see Issue #91 * - // * With SEMP >= v2.0.0 * - // * For NMEA: the last two bytes are CR LF, not the checksum * - // * For Unicore Hash: the last two bytes are the checksum * - // ************************************************************ checksum = sempAsciiToNibble(parse->buffer[parse->length - 1]); checksum |= sempAsciiToNibble(parse->buffer[parse->length - 2]) << 4; @@ -117,7 +115,7 @@ bool badNmeaChecksum(SEMP_PARSE_STATE *parse) // Display bad checksums if (!badChecksum) { - sempPrintString(output, "Unicore Lib: Message improperly includes "); + sempPrintString(output, "Unicore: Message improperly includes "); sempPrintChar(output, parse->buffer[0]); sempPrintStringLn(output, " in checksum"); sempDumpBuffer(output, parse->buffer, parse->length); @@ -131,6 +129,7 @@ bool badNmeaChecksum(SEMP_PARSE_STATE *parse) void parserTests() { size_t bufferLength; + int rawDataBytes; // Verify the buffer size bufferLength = sempGetBufferLength(parserTable, parserCount); @@ -149,15 +148,24 @@ void parserTests() reportFatalError("Failed to initialize the parser"); // Obtain a raw data stream from somewhere + rawDataBytes = 0; + for (dataIndex = 0; dataIndex < DATA_STREAM_ENTRIES; dataIndex++) + rawDataBytes += dataStream[dataIndex].length; sempPrintString(output, "Raw data stream: "); - sempPrintDecimalI32(output, RAW_DATA_BYTES); + sempPrintDecimalI32(output,rawDataBytes); sempPrintStringLn(output, " bytes"); // The raw data stream is passed to the parser one byte at a time sempDebugOutputEnable(parse, output); - for (dataOffset = 0; dataOffset < RAW_DATA_BYTES; dataOffset++) - // Update the parser state based on the incoming byte - sempParseNextByte(parse, rawDataStream[dataOffset]); + for (dataIndex = 0; dataIndex < DATA_STREAM_ENTRIES; dataIndex++) + { + for (byteOffset = 0; byteOffset < dataStream[dataIndex].length; byteOffset++) + { + // Update the parser state based on the incoming byte + sempParseNextByte(parse, dataStream[dataIndex].data[byteOffset]); + dataOffset += 1; + } + } // Done parsing the data sempStopParser(&parse);