diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fc558d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +examples/*/build + diff --git a/check.sh b/check.sh new file mode 100755 index 0000000..e01c721 --- /dev/null +++ b/check.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# +# check.sh +# Script to verify example builds +######################################################################## +set -e +#set -o verbose +#set -o xtrace + +# Base_Test +cd examples/Base_Test +make clean +make +make clean + +# Mixed_Parser +cd ../Mixed_Parser +make clean +make +make clean + +# Multiple_Parsers +cd ../Multiple_Parsers +make clean +make +make clean + +# NMEA_Test +cd ../NMEA_Test +make clean +make +make clean + +# RTCM_Test +cd ../RTCM_Test +make clean +make +make clean + +# SBF_in_SPARTN_Test +cd ../SBF_in_SPARTN_Test +make clean +make +make clean + +# SBF_Test +cd ../SBF_Test +make clean +make +make clean + +# SPARTN_Test +cd ../SPARTN_Test +make clean +make +make clean + +# UBLOX_Test +cd ../UBLOX_Test +make clean +make +make clean + +# Unicore_Binary_Test +cd ../Unicore_Binary_Test +make clean +make +make clean + +# Unicore_Hash_Test +cd ../Unicore_Hash_Test +make clean +make +make clean + +# User_Parser_Test +cd ../User_Parser +make clean +make +make clean + +# Return to origin directory +cd ../.. diff --git a/examples/Base_Test/Base_Test.ino b/examples/Base_Test/Base_Test.ino index a92eb90..2127a7b 100644 --- a/examples/Base_Test/Base_Test.ino +++ b/examples/Base_Test/Base_Test.ino @@ -4,7 +4,10 @@ License: MIT. Please see LICENSE.md for more details */ -#include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "No_Parser.h" + +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" //---------------------------------------- // Constants @@ -17,17 +20,6 @@ const uint8_t rawDataStream[] = #define RAW_DATA_BYTES sizeof(rawDataStream) -// Forward routine declarations -bool noParserPreamble(SEMP_PARSE_STATE *parse, uint8_t data); - -// Build the description for the No parser -SEMP_PARSER_DESCRIPTION noParserDescription = -{ - "No parser", // parserName - noParserPreamble, // preamble - 0, // scratchPadBytes -}; - // Build the table listing all of the parsers SEMP_PARSER_DESCRIPTION * parserTable[] = { @@ -42,10 +34,13 @@ const int parserCount = sizeof(parserTable) / sizeof(parserTable[0]); uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - void setup() { uint8_t * buffer; @@ -138,19 +133,17 @@ void setup() Serial.printf("All done\r\n"); } +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } -// Check for the preamble -bool noParserPreamble(SEMP_PARSE_STATE *parse, uint8_t data) -{ - // Preamble not found - return false; -} - +//---------------------------------------- // 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; @@ -171,63 +164,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -// Display the contents of a buffer -void dumpBuffer(uint8_t *buffer, uint16_t length) -{ - int bytes; - uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/Base_Test/No_Parser.cpp b/examples/Base_Test/No_Parser.cpp new file mode 100644 index 0000000..851812e --- /dev/null +++ b/examples/Base_Test/No_Parser.cpp @@ -0,0 +1,74 @@ +/* + No_Parser.cpp + SparkFun base test example sketch, verify it builds + + License: MIT. Please see LICENSE.md for more details + + Implement the "No_Parser" +*/ + +#include "No_Parser.h" + +//------------------------------------------------------------------------------ +// No_Parser routines +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the No_Parser recgonizes the input and false +// if another parser should be used +//---------------------------------------- +bool noParserPreamble(SEMP_PARSE_STATE *parse, uint8_t data) +{ + if (data == 2) + { + parse->eomCallback(parse, parse->type); + parse->state = sempFirstByte; + return true; + } + // Preamble not found + return false; +} + +//---------------------------------------- +// Translate the state into a zero terminated state name string +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- +const char * noParserGetStateName(const SEMP_PARSE_STATE *parse) +{ + return nullptr; +} + +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Describe the parser +//---------------------------------------- +SEMP_PARSER_DESCRIPTION noParserDescription = +{ + "No parser", // parserName + noParserPreamble, // preamble + 0, // scratchPadBytes + 0, // payloadOffset +}; diff --git a/examples/Base_Test/No_Parser.h b/examples/Base_Test/No_Parser.h new file mode 100644 index 0000000..35b973b --- /dev/null +++ b/examples/Base_Test/No_Parser.h @@ -0,0 +1,21 @@ +/* + No_Parser.h + + License: MIT. Please see LICENSE.md for more details + + Declare the public data structures and routines for the "No_Parser" +*/ + +#ifndef __NO_PARSER_H__ +#define __NO_PARSER_H__ + +#include //http://librarymanager/All#SparkFun_Extensible_Message_Parser + +//---------------------------------------- +// Constants +//---------------------------------------- + +// No parser externals +extern SEMP_PARSER_DESCRIPTION noParserDescription; + +#endif // __NO_PARSER_H__ diff --git a/examples/Base_Test/makefile b/examples/Base_Test/makefile new file mode 100644 index 0000000..d380094 --- /dev/null +++ b/examples/Base_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=Base_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/Common/dumpBuffer.ino b/examples/Common/dumpBuffer.ino new file mode 100644 index 0000000..85a7ea5 --- /dev/null +++ b/examples/Common/dumpBuffer.ino @@ -0,0 +1,61 @@ +/* + dumpBuffer.ino + + Display the contents of a buffer in hexadecimal and ASCII + + License: MIT. Please see LICENSE.md for more details +*/ + +//---------------------------------------- +// Display the contents of a buffer in hexadecimal and ASCII +// +// Inputs: +// buffer: Address of a buffer containing the data to display +// length: Number of bytes of data to display +//---------------------------------------- +void dumpBuffer(const uint8_t *buffer, size_t length) +{ + int bytes; + const uint8_t *end; + int index; + size_t offset; + + end = &buffer[length]; + offset = 0; + while (buffer < end) + { + // Determine the number of bytes to display on the line + bytes = end - buffer; + if (bytes > (16 - (offset & 0xf))) + bytes = 16 - (offset & 0xf); + + // Display the offset + Serial.printf("0x%08lx: ", offset); + + // Skip leading bytes + for (index = 0; index < (offset & 0xf); index++) + Serial.printf(" "); + + // Display the data bytes + for (index = 0; index < bytes; index++) + Serial.printf("%02x ", buffer[index]); + + // Separate the data bytes from the ASCII + for (; index < (16 - (offset & 0xf)); index++) + Serial.printf(" "); + Serial.printf(" "); + + // Skip leading bytes + for (index = 0; index < (offset & 0xf); index++) + Serial.printf(" "); + + // Display the ASCII values + for (index = 0; index < bytes; index++) + Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); + Serial.printf("\r\n"); + + // Set the next line of data + buffer += bytes; + offset += bytes; + } +} diff --git a/examples/Common/reportFatalError.ino b/examples/Common/reportFatalError.ino new file mode 100644 index 0000000..e0c6aba --- /dev/null +++ b/examples/Common/reportFatalError.ino @@ -0,0 +1,24 @@ +/* + reportFatalError.ino + + Display a the HALTED message every 15 seconds + + License: MIT. Please see LICENSE.md for more details +*/ + +//---------------------------------------- +// 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) + { + Serial.print("HALTED: "); + Serial.print(errorMsg); + Serial.println(); + sleep(15); + } +} diff --git a/examples/Mixed_Parser/Mixed_Parser.ino b/examples/Mixed_Parser/Mixed_Parser.ino index a688934..44cf8af 100644 --- a/examples/Mixed_Parser/Mixed_Parser.ino +++ b/examples/Mixed_Parser/Mixed_Parser.ino @@ -4,11 +4,20 @@ This example demonstrates how to create a mixed parser - here we parse u-blox UBX and NMEA using a single mixed parser + A mixed parser lists multiple parsers in the parserTable. This + configuration works when the data stream contains a mix of complete + messages. For cases where protocol2 messages are embedded in the + payload of protocol1, use the design of the SBF_in_SPARTN example and + invoke the protocol2 parser in the processMessage routine. + License: MIT. Please see LICENSE.md for more details */ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -122,17 +131,17 @@ typedef struct _DataStream const uint8_t *data; } DataStream; -#define DATA_STREAM_INIT(x) {sizeof(x), &x[0]} +#define DATA_STREAM_INIT(x, extraBytes) {sizeof(x) - extraBytes, &x[0]} const DataStream dataStream[] = { - DATA_STREAM_INIT(nmea_1), - DATA_STREAM_INIT(ublox_1), - DATA_STREAM_INIT(nmea_2), - DATA_STREAM_INIT(ublox_2), - DATA_STREAM_INIT(nmea_3), - DATA_STREAM_INIT(ublox_3), - DATA_STREAM_INIT(nmea_4), - DATA_STREAM_INIT(ublox_4) + DATA_STREAM_INIT(nmea_1, 1), + DATA_STREAM_INIT(ublox_1, 0), + DATA_STREAM_INIT(nmea_2, 1), + DATA_STREAM_INIT(ublox_2, 0), + DATA_STREAM_INIT(nmea_3, 1), + DATA_STREAM_INIT(ublox_3, 0), + DATA_STREAM_INIT(nmea_4, 1), + DATA_STREAM_INIT(ublox_4, 0) }; #define DATA_STREAM_ENTRIES (sizeof(dataStream) / sizeof(dataStream[0])) @@ -149,11 +158,13 @@ int dataIndex; uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { int rawDataBytes; @@ -197,13 +208,17 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } +//---------------------------------------- // 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; @@ -243,63 +258,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/Mixed_Parser/makefile b/examples/Mixed_Parser/makefile new file mode 100644 index 0000000..b7b7220 --- /dev/null +++ b/examples/Mixed_Parser/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=Mixed_Parser +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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/Multiple_Parsers/Multiple_Parsers.ino b/examples/Multiple_Parsers/Multiple_Parsers.ino index 29fa589..ae530dc 100644 --- a/examples/Multiple_Parsers/Multiple_Parsers.ino +++ b/examples/Multiple_Parsers/Multiple_Parsers.ino @@ -9,6 +9,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -110,17 +113,17 @@ typedef struct _DataStream const uint8_t *data; } DataStream; -#define DATA_STREAM_INIT(x) {sizeof(x), &x[0]} +#define DATA_STREAM_INIT(x, extraBytes) {sizeof(x) - extraBytes, &x[0]} const DataStream dataStream[] = { - DATA_STREAM_INIT(nmea_1), - DATA_STREAM_INIT(ublox_1), - DATA_STREAM_INIT(nmea_2), - DATA_STREAM_INIT(ublox_2), - DATA_STREAM_INIT(nmea_3), - DATA_STREAM_INIT(ublox_3), - DATA_STREAM_INIT(nmea_4), - DATA_STREAM_INIT(ublox_4) + DATA_STREAM_INIT(nmea_1, 1), + DATA_STREAM_INIT(ublox_1, 0), + DATA_STREAM_INIT(nmea_2, 1), + DATA_STREAM_INIT(ublox_2, 0), + DATA_STREAM_INIT(nmea_3, 1), + DATA_STREAM_INIT(ublox_3, 0), + DATA_STREAM_INIT(nmea_4, 1), + DATA_STREAM_INIT(ublox_4, 0) }; #define DATA_STREAM_ENTRIES (sizeof(dataStream) / sizeof(dataStream[0])) @@ -150,11 +153,13 @@ uint32_t dataOffset; SEMP_PARSE_STATE *nmeaParser; SEMP_PARSE_STATE *ubloxParser; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { size_t bufferLength; @@ -214,13 +219,17 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } +//---------------------------------------- // Call back from within parser, for end of message // Process a complete message incoming from parser +//---------------------------------------- void nmeaSentence(SEMP_PARSE_STATE *parse, uint16_t type) { uint32_t byteIndex; @@ -273,63 +282,3 @@ void ubloxMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/Multiple_Parsers/makefile b/examples/Multiple_Parsers/makefile new file mode 100644 index 0000000..71941fa --- /dev/null +++ b/examples/Multiple_Parsers/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=Multiple_Parsers +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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/NMEA_Test/NMEA_Test.ino b/examples/NMEA_Test/NMEA_Test.ino index 9a581bd..68bb5b6 100644 --- a/examples/NMEA_Test/NMEA_Test.ino +++ b/examples/NMEA_Test/NMEA_Test.ino @@ -8,6 +8,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -59,10 +62,19 @@ const uint8_t rawDataStream[] = // 1 2 3 4 5 6 7 8 //2345678901234567890123456789012345678901234567890123456789012345678901234567890 "$GPRMC,210230,A,3855.4487,N,09446.0071,W,0.0,076.2,130495,003.8,E+*69\r\n" // 981 + + // Test different line endings with valid NMEA sentences + // 1 2 3 4 5 6 7 8 + //2345678901234567890123456789012345678901234567890123456789012345678901234567890 + "$GPGGA,210230,3855.4487,N,09446.0071,W,1,07,1.1,370.5,M,-29.5,M,,*7A" + "$GPGSV,2,1,08,02,74,042,45,04,18,190,36,07,67,279,42,12,29,323,36*77\n" + "$GPGSV,2,2,08,15,30,050,47,19,09,158,,26,12,281,40,27,38,173,41*7B\n\r" + "$GPRMC,210230,A,3855.4487,N,09446.0071,W,0.0,076.2,130495,003.8,E*69\r" + "$GPGGA,210230,3855.4487,N,09446.0071,W,1,07,1.1,370.5,M,-29.5,M,,*7A\r\n" }; // Number of bytes in the rawDataStream -#define RAW_DATA_BYTES sizeof(rawDataStream) +#define RAW_DATA_BYTES (sizeof(rawDataStream) - 1) // Account for the largest NMEA sentence + CR + LF + zero termination #define BUFFER_LENGTH 68 + 1 + 1 + 1 @@ -74,11 +86,13 @@ const uint8_t rawDataStream[] = uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { delay(1000); @@ -96,6 +110,9 @@ void setup() if (!parse) reportFatalError("Failed to initialize the parser"); + // Add the callback for invalid data + sempSetInvalidDataCallback(parse, invalidData); + // Obtain a raw data stream from somewhere Serial.printf("Raw data stream: %d bytes\r\n", RAW_DATA_BYTES); @@ -111,13 +128,17 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } +//---------------------------------------- // 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; @@ -130,7 +151,7 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) // Display the raw message Serial.println(); - Serial.printf("Valid NMEA Sentence: %s, %d bytes at 0x%08x (%d)\r\n", + Serial.printf("Valid NMEA Sentence: %s, %d bytes at 0x%08x (%d)\r\n", sempNmeaGetSentenceName(parse), parse->length, offset, offset); dumpBuffer(parse->buffer, parse->length); @@ -143,62 +164,12 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) } } -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) +//---------------------------------------- +// Callback from within the parser when invalid data is identified +//---------------------------------------- +void invalidData(const uint8_t * buffer, size_t length) { - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } + // Display the invalid data + Serial.printf("Invalid data:\r\n"); + dumpBuffer(buffer, length); } diff --git a/examples/NMEA_Test/makefile b/examples/NMEA_Test/makefile new file mode 100644 index 0000000..d8e78fa --- /dev/null +++ b/examples/NMEA_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=NMEA_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/RTCM_Test/RTCM_Test.ino b/examples/RTCM_Test/RTCM_Test.ino index 94c6368..7784fd1 100644 --- a/examples/RTCM_Test/RTCM_Test.ino +++ b/examples/RTCM_Test/RTCM_Test.ino @@ -8,6 +8,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -70,11 +73,13 @@ const uint8_t rawDataStream[] = uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { delay(1000); @@ -109,13 +114,17 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } +//---------------------------------------- // 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; @@ -145,63 +154,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08x: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/RTCM_Test/makefile b/examples/RTCM_Test/makefile new file mode 100644 index 0000000..3cbd6ce --- /dev/null +++ b/examples/RTCM_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=RTCM_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/SBF_Test/SBF_Test.ino b/examples/SBF_Test/SBF_Test.ino index 4cf0e7b..a2fada2 100644 --- a/examples/SBF_Test/SBF_Test.ino +++ b/examples/SBF_Test/SBF_Test.ino @@ -8,6 +8,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -69,11 +72,13 @@ const uint8_t rawDataStream[] = uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { delay(1000); @@ -106,14 +111,18 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { // Nothing to do here... } +//---------------------------------------- // 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) { uint32_t offset; @@ -149,63 +158,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint32_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/SBF_Test/makefile b/examples/SBF_Test/makefile new file mode 100644 index 0000000..8b987a1 --- /dev/null +++ b/examples/SBF_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=SBF_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/SBF_in_SPARTN_Test/SBF_in_SPARTN_Test.ino b/examples/SBF_in_SPARTN_Test/SBF_in_SPARTN_Test.ino index 7936743..dd709f5 100644 --- a/examples/SBF_in_SPARTN_Test/SBF_in_SPARTN_Test.ino +++ b/examples/SBF_in_SPARTN_Test/SBF_in_SPARTN_Test.ino @@ -3,14 +3,23 @@ The Septentrio mosaic-X5 can output raw L-Band (LBandBeam1) data, interspersed with SBF messages - This example demonstrates how to use two parsers to separate the SBF from the L-Band stream - and extract SPARTN from the remaining raw L-Band + A dual parser is required when two protocols are mixed in the raw data + stream. At least one of the protocols must have contigious elements + within the raw data stream. The elements may be messages, packets or + tag-length-value tuples so that the invalid data is easily identified. + + This example uses two parsers to separate SBF from SPARTN in the raw + L-Band data stream. The invalid data callback is specified for the + outer layer SBF parser and calls the SPARTN parser with the invalid data. License: MIT. Please see LICENSE.md for more details */ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -55,7 +64,11 @@ const uint8_t rawDataStream[] = // SBF Block 4007 (PVTGeodetic) - 0x24, 0x40, 0xC4, 0x86, 0xA7, 0x4F, 0x60, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, + 0x24, 0x40, // Preamble + 0xC4, 0x86, // CRC + 0xA7, 0x4F, // ID + 0x60, 0x00, // Length + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x5F, 0xA0, 0x12, 0xC2, 0x00, 0x00, 0x00, 0x20, 0x5F, 0xA0, 0x12, 0xC2, 0x00, 0x00, 0x00, 0x20, 0x5F, 0xA0, 0x12, 0xC2, 0xF9, 0x02, 0x95, 0xD0, 0xF9, 0x02, 0x95, 0xD0, 0xF9, 0x02, 0x95, 0xD0, 0xF9, 0x02, 0x95, 0xD0, 0xF9, 0x02, 0x95, 0xD0, 0x00, 0x00, 0x00, 0x20, @@ -100,11 +113,13 @@ uint32_t dataOffset1; SEMP_PARSE_STATE *parse1; SEMP_PARSE_STATE *parse2; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { size_t bufferLength; @@ -128,7 +143,7 @@ void setup() // Add the callback for invalid SBF data, // to allow it to be passed to the SPARTN parser - sempSbfSetInvalidDataCallback(parse1, invalidSbfData); + sempSetInvalidDataCallback(parse1, invalidSbfData); bufferLength = sempGetBufferLength(parserTable2, parserCount2, BUFFER_LENGTH); uint8_t * buffer2 = (uint8_t *)malloc(bufferLength); @@ -159,26 +174,28 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { // Nothing to do here... } +//---------------------------------------- // Callback from within the SBF parser when invalid data is identified // The data is passed on to the SPARTN parser -void invalidSbfData(SEMP_PARSE_STATE *parse) +//---------------------------------------- +void invalidSbfData(const uint8_t * buffer, size_t length) { // Data is not SBF, so pass it to the SPARTN parser - for (uint32_t dataOffset = 0; dataOffset < parse->length; dataOffset++) - { - // Update the SPARTN parser state based on the non-SBF byte - sempParseNextByte(parse2, parse->buffer[dataOffset]); - } + sempParseNextBytes(parse2, buffer, length); } +//---------------------------------------- // Call back from within parser, for end of message // Process a complete message incoming from parser +//---------------------------------------- void processSbfMessage(SEMP_PARSE_STATE *parse, uint16_t type) { uint32_t offset; @@ -205,8 +222,10 @@ void processSbfMessage(SEMP_PARSE_STATE *parse, uint16_t type) } } +//---------------------------------------- // Call back from within parser, for end of message // Process a complete message incoming from parser +//---------------------------------------- void processSpartnMessage(SEMP_PARSE_STATE *parse, uint16_t type) { static bool displayOnce = true; @@ -227,63 +246,3 @@ void processSpartnMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint32_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/SBF_in_SPARTN_Test/makefile b/examples/SBF_in_SPARTN_Test/makefile new file mode 100644 index 0000000..df40e05 --- /dev/null +++ b/examples/SBF_in_SPARTN_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=SBF_in_SPARTN_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/SPARTN_Test/SPARTN_Test.ino b/examples/SPARTN_Test/SPARTN_Test.ino index 0b36142..1cafba9 100644 --- a/examples/SPARTN_Test/SPARTN_Test.ino +++ b/examples/SPARTN_Test/SPARTN_Test.ino @@ -8,6 +8,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -127,11 +130,13 @@ const uint8_t rawDataStream[] = uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { delay(1000); @@ -164,14 +169,18 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { // Nothing to do here... } +//---------------------------------------- // 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; @@ -204,63 +213,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint32_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/SPARTN_Test/makefile b/examples/SPARTN_Test/makefile new file mode 100644 index 0000000..d68fd90 --- /dev/null +++ b/examples/SPARTN_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=SPARTN_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/UBLOX_Test/UBLOX_Test.ino b/examples/UBLOX_Test/UBLOX_Test.ino index f2c1beb..eeafb9a 100644 --- a/examples/UBLOX_Test/UBLOX_Test.ino +++ b/examples/UBLOX_Test/UBLOX_Test.ino @@ -8,6 +8,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -120,11 +123,13 @@ const uint8_t rawDataStream[] = uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { delay(1000); @@ -157,13 +162,17 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } +//---------------------------------------- // 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; @@ -198,63 +207,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) Serial.printf("Longitude: %ld\r\n", sempUbloxGetI4(parse, 24)); } } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/UBLOX_Test/makefile b/examples/UBLOX_Test/makefile new file mode 100644 index 0000000..1662bcf --- /dev/null +++ b/examples/UBLOX_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=UBLOX_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/Unicore_Binary_Test/Unicore_Binary_Test.ino b/examples/Unicore_Binary_Test/Unicore_Binary_Test.ino index 80d0c3d..bcdda92 100644 --- a/examples/Unicore_Binary_Test/Unicore_Binary_Test.ino +++ b/examples/Unicore_Binary_Test/Unicore_Binary_Test.ino @@ -8,6 +8,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -117,11 +120,13 @@ const uint8_t rawDataStream[] = uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { delay(1000); @@ -154,13 +159,17 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } +//---------------------------------------- // 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; @@ -181,62 +190,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempPrintParserConfiguration(parse, &Serial); } } - -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/Unicore_Binary_Test/makefile b/examples/Unicore_Binary_Test/makefile new file mode 100644 index 0000000..9bada13 --- /dev/null +++ b/examples/Unicore_Binary_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# 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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/Unicore_Hash_Test/Unicore_Hash_Test.ino b/examples/Unicore_Hash_Test/Unicore_Hash_Test.ino index f57dbd0..cd2a9a7 100644 --- a/examples/Unicore_Hash_Test/Unicore_Hash_Test.ino +++ b/examples/Unicore_Hash_Test/Unicore_Hash_Test.ino @@ -8,6 +8,9 @@ #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + //---------------------------------------- // Constants //---------------------------------------- @@ -63,7 +66,7 @@ const uint8_t rawDataStream[] = }; // Number of bytes in the rawDataStream -#define RAW_DATA_BYTES sizeof(rawDataStream) +#define RAW_DATA_BYTES (sizeof(rawDataStream) - 1) // Account for the largest Unicore hash (#) sentence + zero termination #define BUFFER_LENGTH 144 + 1 @@ -75,11 +78,13 @@ const uint8_t rawDataStream[] = uint32_t dataOffset; SEMP_PARSE_STATE *parse; +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + //---------------------------------------- -// Test routine +// Application entry point used to initialize the system //---------------------------------------- - -// Initialize the system void setup() { delay(1000); @@ -113,15 +118,19 @@ void setup() Serial.printf("All done\r\n"); } -// Main loop processing after system is initialized +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- void loop() { } +//---------------------------------------- // Handle the bad checksum calculation // See Unicore NebulasIV, High Precision Products, HIGH PRECISION // COMMANDS AND LOGS Manual // Section 7.3, page 112 +//---------------------------------------- bool badUnicoreHashChecksum(SEMP_PARSE_STATE *parse) { int alternateChecksum; @@ -147,8 +156,10 @@ bool badUnicoreHashChecksum(SEMP_PARSE_STATE *parse) return badChecksum; } +//---------------------------------------- // 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) { uint32_t offset; @@ -164,63 +175,3 @@ void processMessage(SEMP_PARSE_STATE *parse, uint16_t type) sempUnicoreHashGetSentenceName(parse), parse->length, offset, offset); dumpBuffer(parse->buffer, parse->length); } - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/Unicore_Hash_Test/makefile b/examples/Unicore_Hash_Test/makefile new file mode 100644 index 0000000..aef6b0e --- /dev/null +++ b/examples/Unicore_Hash_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=Unicore_Hash_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/examples/User_Parser/User_Parser.ino b/examples/User_Parser/User_Parser.ino deleted file mode 100644 index 39a0735..0000000 --- a/examples/User_Parser/User_Parser.ino +++ /dev/null @@ -1,313 +0,0 @@ -/* - SparkFun user parser example sketch - - This example demonstrates how to write your own parser - - with its own state machine and support methods - - License: MIT. Please see LICENSE.md for more details -*/ - -#include //http://librarymanager/All#SparkFun_Extensible_Message_Parser - -//---------------------------------------- -// Types -//---------------------------------------- - -typedef struct _USER_SCRATCH_PAD -{ - uint32_t messageNumber; // Count the valid messages -} USER_SCRATCH_PAD; - -//---------------------------------------- -// User Parser -// -// The routines below are specified in the reverse order of execution -// to eliminate the need for forward references and possibly exposing -// them via a header file to the application. -//---------------------------------------- - -// Check for a number -bool userFindNumber(SEMP_PARSE_STATE *parse, uint8_t data) -{ - USER_SCRATCH_PAD *scratchPad = (USER_SCRATCH_PAD *)parse->scratchPad; - if ((data < '0') || (data > '9')) - return sempFirstByte(parse, data); - - // Account for the valid message - scratchPad->messageNumber += 1; - - // Pass the valid message to the end-of-message handler - parse->eomCallback(parse, parse->type); - - // Start searching for a preamble byte - parse->state = sempFirstByte; - return true; -} - -// Check for the second preamble byte -bool userSecondPreambleByte(SEMP_PARSE_STATE *parse, uint8_t data) -{ - USER_SCRATCH_PAD *scratchPad = (USER_SCRATCH_PAD *)parse->scratchPad; - - // Validate the second preamble byte - if (data != 'B') - { - // Print the error - sempPrintf(parse->printDebug, - "USER_Parser: Bad second preamble byte after message %d", - scratchPad->messageNumber); - - // Start searching for a preamble byte - return sempFirstByte(parse, data); - } - - // Valid preamble, search for a number - parse->state = userFindNumber; - return true; -} - -// Check for the first preamble byte -bool userPreamble(SEMP_PARSE_STATE *parse, uint8_t data) -{ - // Validate the first preamble byte - if (data != 'A') - // Start searching for a preamble byte - return false; - - // Valid preamble byte, search for the next preamble byte - parse->state = userSecondPreambleByte; - return true; -} - -// Translates state value into an string, returns nullptr if not found -const char * userParserStateName(const SEMP_PARSE_STATE *parse) -{ - if (parse->state == userPreamble) - return "userPreamble"; - if (parse->state == userSecondPreambleByte) - return "userSecondPreambleByte"; - if (parse->state == userFindNumber) - return "userFindNumber"; - return nullptr; -} - -// Return the message number value -uint32_t userParserGetMessageNumber(const SEMP_PARSE_STATE *parse) -{ - USER_SCRATCH_PAD *scratchPad = (USER_SCRATCH_PAD *)parse->scratchPad; - return scratchPad->messageNumber; -} - -//---------------------------------------- -// Constants -//---------------------------------------- - -// Provide some valid and invalid NMEA sentences -const uint8_t rawDataStream[] = -{ - // Valid messages - "AB1" // 0 - - // Invalid data, must skip over - "ab2" // 3 - - // Valid messages - "AB3" // 6 - - // Invalid character in the message header - "Ab4" // 9 - - // Sentence too long by a single byte - "ABC5" // 12 - - // Valid messages - "AB6" // 16 -}; - -// Number of bytes in the rawDataStream -#define RAW_DATA_BYTES sizeof(rawDataStream) - -// Account for the largest message -#define BUFFER_LENGTH 3 - -SEMP_PARSER_DESCRIPTION userParserDescription = -{ - "User parser", // parserName - userPreamble, // preamble - sizeof(USER_SCRATCH_PAD), -}; - -SEMP_PARSER_DESCRIPTION * userParserTable[] = -{ - &userParserDescription -}; -const int userParserCount = sizeof(userParserTable) / sizeof(userParserTable[0]); - -//---------------------------------------- -// Locals -//---------------------------------------- - -uint32_t dataOffset; -SEMP_PARSE_STATE *parse; - -//---------------------------------------- -// Test routine -//---------------------------------------- - -// Initialize the system -void setup() -{ - int dataIndex; - - delay(1000); - - Serial.begin(115200); - Serial.println(); - Serial.println("User_Parser example sketch"); - Serial.println(); - - // Initialize the parser - size_t bufferLength = sempGetBufferLength(userParserTable, - userParserCount, - BUFFER_LENGTH); - uint8_t * buffer = (uint8_t *)malloc(bufferLength); - parse = sempBeginParser("User_Parser", userParserTable, userParserCount, - buffer, bufferLength, userMessage); - if (!parse) - reportFatalError("Failed to initialize the user parser"); - - // Obtain a raw data stream from somewhere - Serial.printf("Raw data stream: %d bytes\r\n", RAW_DATA_BYTES); - - // The raw data stream is passed to the parser one byte at a time - sempEnableDebugOutput(parse); - for (dataOffset = 0; dataOffset < RAW_DATA_BYTES; dataOffset++) - { - uint8_t data; - const char * endState; - const char * startState; - - // Get the parse state before entering the parser to enable - // printing of the parser transition - startState = getParseStateName(parse); - - // Update the parser state based on the incoming byte - data = rawDataStream[dataOffset]; - sempParseNextByte(parse, data); - - // Print the parser transition - endState = getParseStateName(parse); - Serial.printf("0x%02x (%c), state: (%p) %s --> %s (%p)\r\n", - rawDataStream[dataOffset], - ((data >= ' ') && (data < 0x7f)) ? data : '.', - startState, startState, endState, endState); - } - - // Done parsing the data - sempStopParser(&parse); - free(buffer); - Serial.printf("All done\r\n"); -} - -// Main loop processing after system is initialized -void loop() -{ -} - -// Call back from within parser, for end of message -// Process a complete message incoming from parser -void userMessage(SEMP_PARSE_STATE *parse, uint16_t type) -{ - static bool displayOnce = true; - uint32_t offset; - - // Display the raw message - Serial.println(); - offset = dataOffset + 1 - parse->length; - Serial.printf("Valid Message %d, %d bytes at 0x%08x (%d)\r\n", - userParserGetMessageNumber(parse), parse->length, offset, offset); - dumpBuffer(parse->buffer, parse->length); - - // Display the parser state - if (displayOnce) - { - displayOnce = false; - Serial.println(); - sempPrintParserConfiguration(parse, &Serial); - } -} - -// Translate the state value into an ASCII state name -const char *getParseStateName(SEMP_PARSE_STATE *parse) -{ - const char *name; - - do - { - name = userParserStateName(parse); - if (name) - break; - name = sempGetStateName(parse); - } while (0); - return name; -} - -// Display the contents of a buffer -void dumpBuffer(const uint8_t *buffer, uint16_t length) -{ - int bytes; - const uint8_t *end; - int index; - uint16_t offset; - - end = &buffer[length]; - offset = 0; - while (buffer < end) - { - // Determine the number of bytes to display on the line - bytes = end - buffer; - if (bytes > (16 - (offset & 0xf))) - bytes = 16 - (offset & 0xf); - - // Display the offset - Serial.printf("0x%08lx: ", offset); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the data bytes - for (index = 0; index < bytes; index++) - Serial.printf("%02x ", buffer[index]); - - // Separate the data bytes from the ASCII - for (; index < (16 - (offset & 0xf)); index++) - Serial.printf(" "); - Serial.printf(" "); - - // Skip leading bytes - for (index = 0; index < (offset & 0xf); index++) - Serial.printf(" "); - - // Display the ASCII values - for (index = 0; index < bytes; index++) - Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); - Serial.printf("\r\n"); - - // Set the next line of data - buffer += bytes; - offset += bytes; - } -} - -// Print the error message every 15 seconds -void reportFatalError(const char *errorMsg) -{ - while (1) - { - Serial.print("HALTED: "); - Serial.print(errorMsg); - Serial.println(); - sleep(15); - } -} diff --git a/examples/User_Parser_Test/User_Parser.cpp b/examples/User_Parser_Test/User_Parser.cpp new file mode 100644 index 0000000..388e597 --- /dev/null +++ b/examples/User_Parser_Test/User_Parser.cpp @@ -0,0 +1,141 @@ +/* + User_Parser.ino + + License: MIT. Please see LICENSE.md for more details + + Implement the User_Parser +*/ + +#include "User_Parser.h" + +//---------------------------------------- +// Types +//---------------------------------------- + +typedef struct _USER_SCRATCH_PAD +{ + uint32_t messageNumber; // Count the valid messages +} USER_SCRATCH_PAD; + +//------------------------------------------------------------------------------ +// User Parser +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Check for a number +//---------------------------------------- +bool userFindNumber(SEMP_PARSE_STATE *parse, uint8_t data) +{ + USER_SCRATCH_PAD *scratchPad = (USER_SCRATCH_PAD *)parse->scratchPad; + if ((data < '0') || (data > '9')) + return sempFirstByte(parse, data); + + // Account for the valid message + scratchPad->messageNumber += 1; + + // Pass the valid message to the end-of-message handler + parse->eomCallback(parse, parse->type); + + // Start searching for a preamble byte + parse->state = sempFirstByte; + return true; +} + +//---------------------------------------- +// Check for the second preamble byte +//---------------------------------------- +bool userSecondPreambleByte(SEMP_PARSE_STATE *parse, uint8_t data) +{ + USER_SCRATCH_PAD *scratchPad = (USER_SCRATCH_PAD *)parse->scratchPad; + + // Validate the second preamble byte + if (data != 'B') + { + // Print the error + sempPrintf(parse->printDebug, + "USER_Parser: Bad second preamble byte after message %d", + scratchPad->messageNumber); + + // Start searching for a preamble byte + return sempFirstByte(parse, data); + } + + // Valid preamble, search for a number + parse->state = userFindNumber; + return true; +} + +//---------------------------------------- +// Check for the first preamble byte +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the User_Parser recgonizes the input and false +// if another parser should be used +//---------------------------------------- +bool userPreamble(SEMP_PARSE_STATE *parse, uint8_t data) +{ + // Validate the first preamble byte + if (data != 'A') + // Start searching for a preamble byte + return false; + + // Valid preamble byte, search for the next preamble byte + parse->state = userSecondPreambleByte; + return true; +} + +//---------------------------------------- +// Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- +const char * userParserGetStateName(const SEMP_PARSE_STATE *parse) +{ + if (parse->state == userPreamble) + return "userPreamble"; + if (parse->state == userSecondPreambleByte) + return "userSecondPreambleByte"; + if (parse->state == userFindNumber) + return "userFindNumber"; + return sempGetStateName(parse); +} + +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Describe the parser +//---------------------------------------- +SEMP_PARSER_DESCRIPTION userParserDescription = +{ + "User parser", // parserName + userPreamble, // preamble + sizeof(USER_SCRATCH_PAD), // scratchPadBytes + 0, // payloadOffset +}; + +//---------------------------------------- +// Get the message number +//---------------------------------------- +uint32_t userParserGetMessageNumber(const SEMP_PARSE_STATE *parse) +{ + USER_SCRATCH_PAD *scratchPad = (USER_SCRATCH_PAD *)parse->scratchPad; + return scratchPad->messageNumber; +} diff --git a/examples/User_Parser_Test/User_Parser.h b/examples/User_Parser_Test/User_Parser.h new file mode 100644 index 0000000..e929d21 --- /dev/null +++ b/examples/User_Parser_Test/User_Parser.h @@ -0,0 +1,41 @@ +/* + User_Parser.h + + License: MIT. Please see LICENSE.md for more details + + Declare the public data structures and routines for the User_Parser +*/ + +#ifndef __USER_PARSER_H__ +#define __USER_PARSER_H__ + +#include //http://librarymanager/All#SparkFun_Extensible_Message_Parser + +//---------------------------------------- +// Constants +//---------------------------------------- + +extern SEMP_PARSER_DESCRIPTION userParserDescription; + +//---------------------------------------- +// User_Parser specific API +//---------------------------------------- + +// Get the message number +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs: +// Returns the message number value +uint32_t userParserGetMessageNumber(const SEMP_PARSE_STATE *parse); + +// Translates state value into an string, returns nullptr if not found +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs: +// Returns a zero-terminated state name string +//---------------------------------------- +const char * userParserGetStateName(const SEMP_PARSE_STATE *parse); + +#endif // __USER_PARSER_H__ diff --git a/examples/User_Parser_Test/User_Parser_Test.ino b/examples/User_Parser_Test/User_Parser_Test.ino new file mode 100644 index 0000000..0a1c8f3 --- /dev/null +++ b/examples/User_Parser_Test/User_Parser_Test.ino @@ -0,0 +1,151 @@ +/* + SparkFun user parser example sketch + + This example demonstrates how to write your own parser - + with its own state machine and support methods + + License: MIT. Please see LICENSE.md for more details +*/ + +#include "User_Parser.h" + +#include "../Common/dumpBuffer.ino" +#include "../Common/reportFatalError.ino" + +//---------------------------------------- +// Constants +//---------------------------------------- + +// Provide some valid and invalid NMEA sentences +const uint8_t rawDataStream[] = +{ + // Valid messages + "AB1" // 0 + + // Invalid data, must skip over + "ab2" // 3 + + // Valid messages + "AB3" // 6 + + // Invalid character in the message header + "Ab4" // 9 + + // Sentence too long by a single byte + "ABC5" // 12 + + // Valid messages + "AB6" // 16 +}; + +// Number of bytes in the rawDataStream +#define RAW_DATA_BYTES (sizeof(rawDataStream) - 1) + +// Account for the largest message +#define BUFFER_LENGTH 3 + +SEMP_PARSER_DESCRIPTION * userParserTable[] = +{ + &userParserDescription +}; +const int userParserCount = sizeof(userParserTable) / sizeof(userParserTable[0]); + +//---------------------------------------- +// Locals +//---------------------------------------- + +uint32_t dataOffset; +SEMP_PARSE_STATE *parse; + +//------------------------------------------------------------------------------ +// Test routines +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Application entry point used to initialize the system +//---------------------------------------- +void setup() +{ + int dataIndex; + + delay(1000); + + Serial.begin(115200); + Serial.println(); + Serial.println("User_Parser example sketch"); + Serial.println(); + + // Initialize the parser + size_t bufferLength = sempGetBufferLength(userParserTable, + userParserCount, + BUFFER_LENGTH); + uint8_t * buffer = (uint8_t *)malloc(bufferLength); + parse = sempBeginParser("User_Parser", userParserTable, userParserCount, + buffer, bufferLength, userMessage); + if (!parse) + reportFatalError("Failed to initialize the user parser"); + + // Obtain a raw data stream from somewhere + Serial.printf("Raw data stream: %d bytes\r\n", RAW_DATA_BYTES); + + // The raw data stream is passed to the parser one byte at a time + sempEnableDebugOutput(parse); + for (dataOffset = 0; dataOffset < RAW_DATA_BYTES; dataOffset++) + { + uint8_t data; + const char * endState; + const char * startState; + + // Get the parse state before entering the parser to enable + // printing of the parser transition + startState = userParserGetStateName(parse); + + // Update the parser state based on the incoming byte + data = rawDataStream[dataOffset]; + sempParseNextByte(parse, data); + + // Print the parser transition + endState = userParserGetStateName(parse); + Serial.printf("0x%02x (%c), state: (%p) %s --> %s (%p)\r\n", + rawDataStream[dataOffset], + ((data >= ' ') && (data < 0x7f)) ? data : '.', + startState, startState, endState, endState); + } + + // Done parsing the data + sempStopParser(&parse); + free(buffer); + Serial.printf("All done\r\n"); +} + +//---------------------------------------- +// Main loop processing, repeatedly called after system is initialized by setup +//---------------------------------------- +void loop() +{ +} + +//---------------------------------------- +// Call back from within parser, for end of message +// Process a complete message incoming from parser +//---------------------------------------- +void userMessage(SEMP_PARSE_STATE *parse, uint16_t type) +{ + static bool displayOnce = true; + uint32_t offset; + + // Display the raw message + Serial.println(); + offset = dataOffset + 1 - parse->length; + Serial.printf("Valid Message %d, %d bytes at 0x%08x (%d)\r\n", + userParserGetMessageNumber(parse), parse->length, offset, offset); + dumpBuffer(parse->buffer, parse->length); + + // Display the parser state + if (displayOnce) + { + displayOnce = false; + Serial.println(); + sempPrintParserConfiguration(parse, &Serial); + } +} diff --git a/examples/User_Parser_Test/makefile b/examples/User_Parser_Test/makefile new file mode 100644 index 0000000..6b4ff73 --- /dev/null +++ b/examples/User_Parser_Test/makefile @@ -0,0 +1,161 @@ +###################################################################### +# makefile +# +# Builds the example sketch +###################################################################### + +.ONESHELL: +SHELL=/bin/bash + +########## +# Source files +########## + +SKETCH=User_Parser_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\ +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 +DIR_LISTING=dir +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/ +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 +COPY=cp +DELETE=rm -Rf +DIR_LISTING=ls +TERMINAL_APP=minicom + +TERMINAL_PORT=/dev/ttyUSB0 +TERMINAL_PARAMS=-b 115200 -8 < /dev/tty + +endif + +#--------- +# OS Independent +#--------- + +# Files +BIN_FILE=$(BIN_PATH)$(SKETCH).ino.bin + +########## +# Buid all the sources - must be first +########## + +EXECUTABLES += $(BIN_FILE) + +.PHONY: all + +all: $(EXECUTABLES) + +######## +# Build the Firmware +########## + +#DEBUG_LEVEL=debug +DEBUG_LEVEL=none + +$(BIN_FILE): $(SKETCH).ino *.ino makefile + $(CLEAR) + echo "----------------------------------------------------------------------" + $(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 firmware +########## + +.PHONY: upload + +upload: $(BIN_FILE) + 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_16MB.bin \ + 0xe000 $(BOOT_LOADER_PATH)boot_app0.bin \ + 0x10000 $< + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +########## +# Open the terminal (tty) +########## + +.PHONY: terminal + +terminal: + $(TERMINAL_APP) -D $(TERMINAL_PORT) $(TERMINAL_PARAMS) + +######## +# Clean the build directory +########## + +.PHONY: clean + +clean: + rm -Rf *.o *.a $(BUILD_PATH) diff --git a/src/Parse_NMEA.cpp b/src/Parse_NMEA.cpp index f0e1cfb..30b149e 100644 --- a/src/Parse_NMEA.cpp +++ b/src/Parse_NMEA.cpp @@ -34,9 +34,14 @@ typedef struct _SEMP_NMEA_VALUES uint8_t sentenceNameLength; // Length of the sentence name } SEMP_NMEA_VALUES; -//---------------------------------------- +//------------------------------------------------------------------------------ // NMEA parse routines -//---------------------------------------- +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ // // NMEA Sentence @@ -50,29 +55,44 @@ typedef struct _SEMP_NMEA_VALUES // |<-------- Checksum -------->| // +//---------------------------------------- // Validate the checksum -void sempNmeaValidateChecksum(SEMP_PARSE_STATE *parse) +//---------------------------------------- +bool sempNmeaValidateChecksum(SEMP_PARSE_STATE *parse, size_t bytesToIgnore) { int checksum; + size_t length; SEMP_NMEA_VALUES *scratchPad = (SEMP_NMEA_VALUES *)parse->scratchPad; + // Ignore the CR and LF at the end of the buffer + length = parse->length - bytesToIgnore; + // Convert the checksum characters into binary - checksum = sempAsciiToNibble(parse->buffer[parse->length - 2]) << 4; - checksum |= sempAsciiToNibble(parse->buffer[parse->length - 1]); + checksum = sempAsciiToNibble(parse->buffer[length - 2]) << 4; + checksum |= sempAsciiToNibble(parse->buffer[length - 1]); // Validate the checksum - if ((checksum == parse->crc) || (parse->badCrc && (!parse->badCrc(parse)))) + if (((checksum == parse->crc) || (parse->badCrc && (!parse->badCrc(parse)))) + && ((length + 2) <= parse->bufferLength)) { // Always add the carriage return and line feed - parse->buffer[parse->length++] = '\r'; - parse->buffer[parse->length++] = '\n'; + parse->buffer[length++] = '\r'; + parse->buffer[length++] = '\n'; // Zero terminate the NMEA sentence, don't count this in the length - parse->buffer[parse->length] = 0; + parse->buffer[length] = 0; // Process this NMEA sentence + parse->length = length; parse->eomCallback(parse, parse->type); // Pass parser array index + return true; } + + // Display the error + if ((length + 2) > parse->bufferLength) + sempPrintf(parse->printDebug, + "ERROR SEMP %s: NMEA buffer is too small, increase >= %d", + parse->parserName, length + 2); else // Display the checksum error sempPrintf(parse->printDebug, @@ -80,66 +100,59 @@ void sempNmeaValidateChecksum(SEMP_PARSE_STATE *parse) "received 0x%c%c, computed: 0x%02x", parse->parserName, scratchPad->sentenceName, - parse->length, parse->length, - parse->buffer[parse->length - 2], - parse->buffer[parse->length - 1], + length, length, + parse->buffer[length - 2], + parse->buffer[length - 1], parse->crc); + + // The data character is in the buffer, remove it because the caller + // passes it to sempFirstByte. + parse->length -= 1; + sempInvalidDataCallback(parse); + return false; } +//---------------------------------------- // Read the linefeed +//---------------------------------------- bool sempNmeaLineFeed(SEMP_PARSE_STATE *parse, uint8_t data) { - // Don't add the current character to the length - parse->length -= 1; - + // Validate the checksum and pass the sentence to the upper layer // Process the LF - if (data == '\n') + if (sempNmeaValidateChecksum(parse, 2) && (data == '\n')) { - // Pass the sentence to the upper layer - sempNmeaValidateChecksum(parse); - // Start searching for a preamble byte parse->state = sempFirstByte; return true; } - // Pass the sentence to the upper layer - sempNmeaValidateChecksum(parse); - // Start searching for a preamble byte return sempFirstByte(parse, data); } +//---------------------------------------- // Read the remaining carriage return +//---------------------------------------- bool sempNmeaCarriageReturn(SEMP_PARSE_STATE *parse, uint8_t data) { - // Don't add the current character to the length - parse->length -= 1; - + // Validate the checksum and pass the sentence to the upper layer // Process the CR - if (data == '\r') + if (sempNmeaValidateChecksum(parse, 2) && (data == '\r')) { - // Pass the sentence to the upper layer - sempNmeaValidateChecksum(parse); - // Start searching for a preamble byte parse->state = sempFirstByte; return true; } - // Pass the sentence to the upper layer - sempNmeaValidateChecksum(parse); - // Start searching for a preamble byte return sempFirstByte(parse, data); } +//---------------------------------------- // Read the line termination +//---------------------------------------- bool sempNmeaLineTermination(SEMP_PARSE_STATE *parse, uint8_t data) { - // Don't add the current character to the length - parse->length -= 1; - // Process the line termination if (data == '\r') { @@ -152,14 +165,16 @@ bool sempNmeaLineTermination(SEMP_PARSE_STATE *parse, uint8_t data) return true; } - // Pass the sentence to the upper layer - sempNmeaValidateChecksum(parse); + // Validate the checksum and pass the sentence to the upper layer + sempNmeaValidateChecksum(parse, 1); // Start searching for a preamble byte return sempFirstByte(parse, data); } +//---------------------------------------- // Read the second checksum byte +//---------------------------------------- bool sempNmeaChecksumByte2(SEMP_PARSE_STATE *parse, uint8_t data) { // Validate the checksum character @@ -174,11 +189,18 @@ bool sempNmeaChecksumByte2(SEMP_PARSE_STATE *parse, uint8_t data) "SEMP %s: NMEA invalid second checksum character", parse->parserName); + // The data character is in the buffer, remove it and pass the + // remaining data to the invalid data handler + parse->length -= 1; + sempInvalidDataCallback(parse); + // Start searching for a preamble byte return sempFirstByte(parse, data); } +//---------------------------------------- // Read the first checksum byte +//---------------------------------------- bool sempNmeaChecksumByte1(SEMP_PARSE_STATE *parse, uint8_t data) { // Validate the checksum character @@ -193,11 +215,18 @@ bool sempNmeaChecksumByte1(SEMP_PARSE_STATE *parse, uint8_t data) "SEMP %s: NMEA invalid first checksum character", parse->parserName); + // The data character is in the buffer, remove it and pass the + // remaining data to the invalid data handler + parse->length -= 1; + sempInvalidDataCallback(parse); + // Start searching for a preamble byte return sempFirstByte(parse, data); } +//---------------------------------------- // Read the sentence data +//---------------------------------------- bool sempNmeaFindAsterisk(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_NMEA_VALUES *scratchPad = (SEMP_NMEA_VALUES *)parse->scratchPad; @@ -219,6 +248,11 @@ bool sempNmeaFindAsterisk(SEMP_PARSE_STATE *parse, uint8_t data) parse->parserName, scratchPad->sentenceName); + // The data character is in the buffer, remove it and + // pass the remaining data to the invalid data handler + parse->length -= 1; + sempInvalidDataCallback(parse); + // Start searching for a preamble byte return sempFirstByte(parse, data); } @@ -233,6 +267,11 @@ bool sempNmeaFindAsterisk(SEMP_PARSE_STATE *parse, uint8_t data) parse->parserName, parse->bufferLength); + // The data character is in the buffer, remove it and pass the + // remaining data to the invalid data handler + parse->length -= 1; + sempInvalidDataCallback(parse); + // Start searching for a preamble byte return sempFirstByte(parse, data); } @@ -240,7 +279,9 @@ bool sempNmeaFindAsterisk(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the sentence name +//---------------------------------------- bool sempNmeaFindFirstComma(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_NMEA_VALUES *scratchPad = (SEMP_NMEA_VALUES *)parse->scratchPad; @@ -254,6 +295,11 @@ bool sempNmeaFindFirstComma(SEMP_PARSE_STATE *parse, uint8_t data) sempPrintf(parse->printDebug, "SEMP %s: NMEA invalid sentence name character 0x%02x", parse->parserName, data); + + // The data character is in the buffer, remove it and pass the + // remaining data to the invalid data handler + parse->length -= 1; + sempInvalidDataCallback(parse); return sempFirstByte(parse, data); } @@ -264,6 +310,11 @@ bool sempNmeaFindFirstComma(SEMP_PARSE_STATE *parse, uint8_t data) "SEMP %s: NMEA sentence name > %ld characters", parse->parserName, sizeof(scratchPad->sentenceName) - 1); + + // The data character is in the buffer, remove it and pass the + // remaining data to the invalid data handler + parse->length -= 1; + sempInvalidDataCallback(parse); return sempFirstByte(parse, data); } @@ -279,7 +330,17 @@ bool sempNmeaFindFirstComma(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the NMEA parser recgonizes the input and false +// if another parser should be used +//---------------------------------------- bool sempNmeaPreamble(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_NMEA_VALUES *scratchPad = (SEMP_NMEA_VALUES *)parse->scratchPad; @@ -290,7 +351,15 @@ bool sempNmeaPreamble(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- const char * sempNmeaGetStateName(const SEMP_PARSE_STATE *parse) { if (parse->state == sempNmeaPreamble) @@ -312,17 +381,38 @@ const char * sempNmeaGetStateName(const SEMP_PARSE_STATE *parse) return nullptr; } -// Return the NMEA sentence name as a string -const char * sempNmeaGetSentenceName(const SEMP_PARSE_STATE *parse) -{ - SEMP_NMEA_VALUES *scratchPad = (SEMP_NMEA_VALUES *)parse->scratchPad; - return (const char *)scratchPad->sentenceName; -} +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ +//---------------------------------------- // Describe the parser +//---------------------------------------- SEMP_PARSER_DESCRIPTION sempNmeaParserDescription = { "NMEA parser", // parserName sempNmeaPreamble, // preamble sizeof(SEMP_NMEA_VALUES), // scratchPadBytes + 0, // payloadOffset }; + +//---------------------------------------- +// Abort NMEA parsing on a non-printable char +//---------------------------------------- +void sempNmeaAbortOnNonPrintable(SEMP_PARSE_STATE *parse, bool abort) +{ + if (parse) + parse->nmeaAbortOnNonPrintable = abort; +} + +//---------------------------------------- +// Return the NMEA sentence name as a string +//---------------------------------------- +const char * sempNmeaGetSentenceName(const SEMP_PARSE_STATE *parse) +{ + SEMP_NMEA_VALUES *scratchPad = (SEMP_NMEA_VALUES *)parse->scratchPad; + return (const char *)scratchPad->sentenceName; +} diff --git a/src/Parse_RTCM.cpp b/src/Parse_RTCM.cpp index d8592f8..ef70186 100644 --- a/src/Parse_RTCM.cpp +++ b/src/Parse_RTCM.cpp @@ -31,8 +31,21 @@ typedef struct _SEMP_RTCM_VALUES //---------------------------------------- // Support routines +// +// The parser support routines are placed before the parser routines to eliminate +// forward declarations. //---------------------------------------- +//---------------------------------------- +// Compute the CRC one byte at a time +// +// Inputs: +// crc: Current CRC value +// data: Byte to encode into the CRC value +// +// Outputs: +// Returns the updated CRC value +//---------------------------------------- uint32_t sempRtcmComputeCrc24q(SEMP_PARSE_STATE *parse, uint8_t data) { uint32_t crc = parse->crc; @@ -40,9 +53,14 @@ uint32_t sempRtcmComputeCrc24q(SEMP_PARSE_STATE *parse, uint8_t data) return crc & 0x00ffffff; } -//---------------------------------------- +//------------------------------------------------------------------------------ // RTCM parse routines -//---------------------------------------- +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ // // RTCM Standard 10403.2 - Chapter 4, Transport Layer @@ -58,7 +76,9 @@ uint32_t sempRtcmComputeCrc24q(SEMP_PARSE_STATE *parse, uint8_t data) // |<------------------------ CRC -------------------------->| // +//---------------------------------------- // Read the CRC +//---------------------------------------- bool sempRtcmReadCrc(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_RTCM_VALUES *scratchPad = (SEMP_RTCM_VALUES *)parse->scratchPad; @@ -101,7 +121,9 @@ bool sempRtcmReadCrc(SEMP_PARSE_STATE *parse, uint8_t data) return false; } +//---------------------------------------- // Read the rest of the message +//---------------------------------------- bool sempRtcmReadData(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_RTCM_VALUES *scratchPad = (SEMP_RTCM_VALUES *)parse->scratchPad; @@ -119,7 +141,9 @@ bool sempRtcmReadData(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the lower 4 bits of the message number +//---------------------------------------- bool sempRtcmReadMessage2(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_RTCM_VALUES *scratchPad = (SEMP_RTCM_VALUES *)parse->scratchPad; @@ -130,7 +154,9 @@ bool sempRtcmReadMessage2(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the upper 8 bits of the message number +//---------------------------------------- bool sempRtcmReadMessage1(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_RTCM_VALUES *scratchPad = (SEMP_RTCM_VALUES *)parse->scratchPad; @@ -141,7 +167,9 @@ bool sempRtcmReadMessage1(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the lower 8 bits of the length +//---------------------------------------- bool sempRtcmReadLength2(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_RTCM_VALUES *scratchPad = (SEMP_RTCM_VALUES *)parse->scratchPad; @@ -167,7 +195,9 @@ bool sempRtcmReadLength2(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the upper two bits of the length +//---------------------------------------- bool sempRtcmReadLength1(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_RTCM_VALUES *scratchPad = (SEMP_RTCM_VALUES *)parse->scratchPad; @@ -183,7 +213,17 @@ bool sempRtcmReadLength1(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the RTCM parser recgonizes the input and false +// if another parser should be used +//---------------------------------------- bool sempRtcmPreamble(SEMP_PARSE_STATE *parse, uint8_t data) { if (data == 0xd3) @@ -199,7 +239,15 @@ bool sempRtcmPreamble(SEMP_PARSE_STATE *parse, uint8_t data) return false; } +//---------------------------------------- // Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- const char * sempRtcmGetStateName(const SEMP_PARSE_STATE *parse) { if (parse->state == sempRtcmPreamble) @@ -219,21 +267,54 @@ const char * sempRtcmGetStateName(const SEMP_PARSE_STATE *parse) return nullptr; } +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Describe the parser +//---------------------------------------- +SEMP_PARSER_DESCRIPTION sempRtcmParserDescription = +{ + "RTCM parser", // parserName + sempRtcmPreamble, // preamble + sizeof(SEMP_RTCM_VALUES), // scratchPadBytes + 0, // payloadOffset +}; + +//---------------------------------------- // Get the message number +//---------------------------------------- uint16_t sempRtcmGetMessageNumber(const SEMP_PARSE_STATE *parse) { SEMP_RTCM_VALUES *scratchPad = (SEMP_RTCM_VALUES *)parse->scratchPad; return scratchPad->message; } -// Get unsigned integer with width bits, starting at bit start -uint64_t sempRtcmGetUnsignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, uint16_t width) +//---------------------------------------- +// Get signed integer with width bits, starting at bit start +//---------------------------------------- +int64_t sempRtcmGetSignedBits(const SEMP_PARSE_STATE *parse, size_t start, size_t width) { uint8_t *ptr = parse->buffer; ptr += 3; // Skip the preamble and length bytes - uint64_t result = 0; - uint16_t count = 0; + union + { + uint64_t unsigned64; + int64_t signed64; + } result; + + result.unsigned64 = 0; + + uint64_t twosComplement = 0xFFFFFFFFFFFFFFFF; + + bool isNegative; + + size_t count = 0; uint8_t bitMask = 0x80; // Skip whole bytes (8 bits) @@ -253,15 +334,18 @@ uint64_t sempRtcmGetUnsignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, } } + isNegative = *ptr & bitMask; // Record the first bit - indicates in the number is negative + // We have reached the start bit and ptr is pointing at the correct byte // Now extract width bits, incrementing ptr and shifting bitMask as we go while (count < (start + width)) { - if (*ptr & bitMask) // Is the bit set? - result |= 1; // Set the corresponding bit in result + if (*ptr & bitMask) // Is the bit set? + result.unsigned64 |= 1; // Set the corresponding bit in result - bitMask >>= 1; // Shift the bit mask - count++; // Increment the count + bitMask >>= 1; // Shift the bit mask + count++; // Increment the count + twosComplement <<= 1; // Shift the two's complement mask (clear LSB) if (bitMask == 0) // Have we counted 8 bits? { @@ -270,31 +354,26 @@ uint64_t sempRtcmGetUnsignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, } if (count < (start + width)) // Do we need to shift result? - result <<= 1; // Shift the result + result.unsigned64 <<= 1; // Shift the result } - return result; + // Handle negative number + if (isNegative) + result.unsigned64 |= twosComplement; // OR in the two's complement mask + + return result.signed64; } -// Get signed integer with width bits, starting at bit start -int64_t sempRtcmGetSignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, uint16_t width) +//---------------------------------------- +// Get unsigned integer with width bits, starting at bit start +//---------------------------------------- +uint64_t sempRtcmGetUnsignedBits(const SEMP_PARSE_STATE *parse, size_t start, size_t width) { uint8_t *ptr = parse->buffer; ptr += 3; // Skip the preamble and length bytes - union - { - uint64_t unsigned64; - int64_t signed64; - } result; - - result.unsigned64 = 0; - - uint64_t twosComplement = 0xFFFFFFFFFFFFFFFF; - - bool isNegative; - - uint16_t count = 0; + uint64_t result = 0; + size_t count = 0; uint8_t bitMask = 0x80; // Skip whole bytes (8 bits) @@ -314,18 +393,15 @@ int64_t sempRtcmGetSignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, uin } } - isNegative = *ptr & bitMask; // Record the first bit - indicates in the number is negative - // We have reached the start bit and ptr is pointing at the correct byte // Now extract width bits, incrementing ptr and shifting bitMask as we go while (count < (start + width)) { - if (*ptr & bitMask) // Is the bit set? - result.unsigned64 |= 1; // Set the corresponding bit in result + if (*ptr & bitMask) // Is the bit set? + result |= 1; // Set the corresponding bit in result - bitMask >>= 1; // Shift the bit mask - count++; // Increment the count - twosComplement <<= 1; // Shift the two's complement mask (clear LSB) + bitMask >>= 1; // Shift the bit mask + count++; // Increment the count if (bitMask == 0) // Have we counted 8 bits? { @@ -334,20 +410,8 @@ int64_t sempRtcmGetSignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, uin } if (count < (start + width)) // Do we need to shift result? - result.unsigned64 <<= 1; // Shift the result + result <<= 1; // Shift the result } - // Handle negative number - if (isNegative) - result.unsigned64 |= twosComplement; // OR in the two's complement mask - - return result.signed64; + return result; } - -// Describe the parser -SEMP_PARSER_DESCRIPTION sempRtcmParserDescription = -{ - "RTCM parser", // parserName - sempRtcmPreamble, // preamble - sizeof(SEMP_RTCM_VALUES), // scratchPadBytes -}; diff --git a/src/Parse_SBF.cpp b/src/Parse_SBF.cpp index ae60b9c..c11fdfc 100644 --- a/src/Parse_SBF.cpp +++ b/src/Parse_SBF.cpp @@ -30,14 +30,19 @@ typedef struct _SEMP_SBF_VALUES uint8_t sbfIDrev = 0; uint16_t length; uint16_t bytesRemaining; - // Invalid data callback routine (parser-specific) - SEMP_INVALID_DATA_CALLBACK invalidDataCallback = (SEMP_INVALID_DATA_CALLBACK)nullptr; } SEMP_SBF_VALUES; -//---------------------------------------- +//------------------------------------------------------------------------------ // SBF parse routines -//---------------------------------------- +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ +//---------------------------------------- +//---------------------------------------- bool sempSbfReadBytes(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; @@ -63,8 +68,7 @@ bool sempSbfReadBytes(SEMP_PARSE_STATE *parse, uint8_t data) scratchPad->sbfID, parse->length, parse->length); - if (scratchPad->invalidDataCallback) - scratchPad->invalidDataCallback(parse); + sempInvalidDataCallback(parse); } return false; @@ -73,7 +77,9 @@ bool sempSbfReadBytes(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for Length MSB +//---------------------------------------- bool sempSbfLengthMSB(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; @@ -100,14 +106,14 @@ bool sempSbfLengthMSB(SEMP_PARSE_STATE *parse, uint8_t data) parse->parserName, parse->length, parse->length); - if (scratchPad->invalidDataCallback) - scratchPad->invalidDataCallback(parse); - + sempInvalidDataCallback(parse); parse->state = sempFirstByte; return false; } +//---------------------------------------- // Check for Length LSB +//---------------------------------------- bool sempSbfLengthLSB(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; @@ -120,7 +126,9 @@ bool sempSbfLengthLSB(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for ID byte 2 +//---------------------------------------- bool sempSbfID2(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; @@ -135,7 +143,9 @@ bool sempSbfID2(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for ID byte 1 +//---------------------------------------- bool sempSbfID1(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; @@ -148,7 +158,9 @@ bool sempSbfID1(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for CRC byte 2 +//---------------------------------------- bool sempSbfCRC2(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; @@ -160,7 +172,9 @@ bool sempSbfCRC2(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for CRC byte 1 +//---------------------------------------- bool sempSbfCRC1(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; @@ -171,7 +185,9 @@ bool sempSbfCRC1(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for preamble 2 +//---------------------------------------- bool sempSbfPreamble2(SEMP_PARSE_STATE *parse, uint8_t data) { if (data == 0x40) // @ @@ -179,6 +195,7 @@ bool sempSbfPreamble2(SEMP_PARSE_STATE *parse, uint8_t data) parse->state = sempSbfCRC1; return true; } + // else sempPrintf(parse->printDebug, "SEMP %s: SBF, 0x%04x (%d) bytes, invalid preamble2", @@ -186,14 +203,22 @@ bool sempSbfPreamble2(SEMP_PARSE_STATE *parse, uint8_t data) parse->length, parse->length); SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; - if (scratchPad->invalidDataCallback) - scratchPad->invalidDataCallback(parse); - + sempInvalidDataCallback(parse); parse->state = sempFirstByte; return false; } +//---------------------------------------- // Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the SBF parser recgonizes the input and false +// if another parser should be used +//---------------------------------------- bool sempSbfPreamble(SEMP_PARSE_STATE *parse, uint8_t data) { if (data == 0x24) // $ - same as NMEA @@ -201,14 +226,18 @@ bool sempSbfPreamble(SEMP_PARSE_STATE *parse, uint8_t data) parse->state = sempSbfPreamble2; return true; } - // else - SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; - if (scratchPad->invalidDataCallback) - scratchPad->invalidDataCallback(parse); return false; } +//---------------------------------------- // Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- const char * sempSbfGetStateName(const SEMP_PARSE_STATE *parse) { if (parse->state == sempSbfPreamble) @@ -232,141 +261,77 @@ const char * sempSbfGetStateName(const SEMP_PARSE_STATE *parse) return nullptr; } -// Set the invalid data callback -void sempSbfSetInvalidDataCallback(const SEMP_PARSE_STATE *parse, SEMP_INVALID_DATA_CALLBACK invalidDataCallback) +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Describe the parser +//---------------------------------------- +SEMP_PARSER_DESCRIPTION sempSbfParserDescription = { - SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; - scratchPad->invalidDataCallback = invalidDataCallback; -} + "SBF parser", // parserName + sempSbfPreamble, // preamble + sizeof(SEMP_SBF_VALUES), // scratchPadBytes + 0, // payloadOffset +}; +//---------------------------------------- // Get the Block Number +//---------------------------------------- uint16_t sempSbfGetBlockNumber(const SEMP_PARSE_STATE *parse) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; return scratchPad->sbfID; } +//---------------------------------------- // Get the Block Revision +//---------------------------------------- uint8_t sempSbfGetBlockRevision(const SEMP_PARSE_STATE *parse) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; return scratchPad->sbfIDrev; } -// Get data -uint8_t sempSbfGetU1(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - return parse->buffer[offset]; -} -uint16_t sempSbfGetU2(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - uint16_t data = parse->buffer[offset]; - data |= ((uint16_t)parse->buffer[offset + 1]) << 8; - return data; -} -uint32_t sempSbfGetU4(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - uint32_t data = 0; - for (uint16_t i = 0; i < sizeof(data); i++) - data |= ((uint32_t)parse->buffer[offset + i]) << (8 * i); - return data; -} -uint64_t sempSbfGetU8(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - uint64_t data = 0; - for (uint16_t i = 0; i < sizeof(data); i++) - data |= ((uint64_t)parse->buffer[offset + i]) << (8 * i); - return data; -} -int8_t sempSbfGetI1(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint8_t unsignedN; - int8_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempSbfGetU1(parse, offset); - return unsignedSignedN.signedN; -} -int16_t sempSbfGetI2(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint16_t unsignedN; - int16_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempSbfGetU2(parse, offset); - return unsignedSignedN.signedN; -} -int32_t sempSbfGetI4(const SEMP_PARSE_STATE *parse, uint16_t offset) +//---------------------------------------- +//---------------------------------------- +const uint8_t *sempSbfGetEncapsulatedPayload(const SEMP_PARSE_STATE *parse) { - union { - uint32_t unsignedN; - int32_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempSbfGetU4(parse, offset); - return unsignedSignedN.signedN; + return (const uint8_t *)(sempSbfGetString(parse, 20)); } -int64_t sempSbfGetI8(const SEMP_PARSE_STATE *parse, uint16_t offset) + +//---------------------------------------- +//---------------------------------------- +uint16_t sempSbfGetEncapsulatedPayloadLength(const SEMP_PARSE_STATE *parse) { - union { - uint64_t unsignedN; - int64_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempSbfGetU8(parse, offset); - return unsignedSignedN.signedN; + return sempSbfGetU2(parse, 16); } +//---------------------------------------- // Get the ID value +//---------------------------------------- uint16_t sempSbfGetId(const SEMP_PARSE_STATE *parse) { SEMP_SBF_VALUES * scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; return scratchPad->sbfID; } -float sempSbfGetF4(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint32_t unsignedN; - float flt; - } unsignedFloat; - unsignedFloat.unsignedN = sempSbfGetU4(parse, offset); - return unsignedFloat.flt; -} -double sempSbfGetF8(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint64_t unsignedN; - double flt; - } unsignedFloat; - unsignedFloat.unsignedN = sempSbfGetU8(parse, offset); - return unsignedFloat.flt; -} -const char *sempSbfGetString(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - return (const char *)(&parse->buffer[offset]); -} +//---------------------------------------- +//---------------------------------------- bool sempSbfIsEncapsulatedNMEA(const SEMP_PARSE_STATE *parse) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; return ((scratchPad->sbfID == 4097) && (parse->buffer[14] == 4)); } + +//---------------------------------------- +//---------------------------------------- bool sempSbfIsEncapsulatedRTCMv3(const SEMP_PARSE_STATE *parse) { SEMP_SBF_VALUES *scratchPad = (SEMP_SBF_VALUES *)parse->scratchPad; return ((scratchPad->sbfID == 4097) && (parse->buffer[14] == 2)); } -uint16_t sempSbfGetEncapsulatedPayloadLength(const SEMP_PARSE_STATE *parse) -{ - return sempSbfGetU2(parse, 16); -} -const uint8_t *sempSbfGetEncapsulatedPayload(const SEMP_PARSE_STATE *parse) -{ - return (const uint8_t *)(sempSbfGetString(parse, 20)); -} - -// Describe the parser -SEMP_PARSER_DESCRIPTION sempSbfParserDescription = -{ - "SBF parser", // parserName - sempSbfPreamble, // preamble - sizeof(SEMP_SBF_VALUES), // scratchPadBytes -}; diff --git a/src/Parse_SPARTN.cpp b/src/Parse_SPARTN.cpp index ed9a84f..189a954 100644 --- a/src/Parse_SPARTN.cpp +++ b/src/Parse_SPARTN.cpp @@ -39,10 +39,17 @@ typedef struct _SEMP_SPARTN_VALUES uint16_t embeddedApplicationLengthBytes; } SEMP_SPARTN_VALUES; -//---------------------------------------- +//------------------------------------------------------------------------------ // SPARTN parse routines -//---------------------------------------- +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ +//---------------------------------------- +//---------------------------------------- bool sempSpartnReadTF018(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; @@ -116,6 +123,8 @@ bool sempSpartnReadTF018(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- +//---------------------------------------- bool sempSpartnReadTF017(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; @@ -130,6 +139,8 @@ bool sempSpartnReadTF017(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- +//---------------------------------------- bool sempSpartnReadTF016(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; @@ -152,6 +163,8 @@ bool sempSpartnReadTF016(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- +//---------------------------------------- bool sempSpartnReadTF009(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; @@ -198,6 +211,8 @@ bool sempSpartnReadTF009(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- +//---------------------------------------- bool sempSpartnReadTF007(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; @@ -217,6 +232,8 @@ bool sempSpartnReadTF007(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- +//---------------------------------------- bool sempSpartnReadTF002TF006(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; @@ -286,7 +303,17 @@ bool sempSpartnReadTF002TF006(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the SPARTN parser recgonizes the input and false +// if another parser should be used +//---------------------------------------- bool sempSpartnPreamble(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; @@ -300,7 +327,15 @@ bool sempSpartnPreamble(SEMP_PARSE_STATE *parse, uint8_t data) return false; } +//---------------------------------------- // Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- const char * sempSpartnGetStateName(const SEMP_PARSE_STATE *parse) { if (parse->state == sempSpartnPreamble) @@ -320,24 +355,38 @@ const char * sempSpartnGetStateName(const SEMP_PARSE_STATE *parse) return nullptr; } -// Get the message number -uint8_t sempSpartnGetMessageType(const SEMP_PARSE_STATE *parse) +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Describe the parser +//---------------------------------------- +SEMP_PARSER_DESCRIPTION sempSpartnParserDescription = { - SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; - return scratchPad->messageType; -} + "SPARTN parser", // parserName + sempSpartnPreamble, // preamble + sizeof(SEMP_SPARTN_VALUES), // scratchPadBytes + 0, // payloadOffset +}; +//---------------------------------------- // Get the message subtype number +//---------------------------------------- uint8_t sempSpartnGetMessageSubType(const SEMP_PARSE_STATE *parse) { SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; return scratchPad->messageSubtype; } -// Describe the parser -SEMP_PARSER_DESCRIPTION sempSpartnParserDescription = +//---------------------------------------- +// Get the message number +//---------------------------------------- +uint8_t sempSpartnGetMessageType(const SEMP_PARSE_STATE *parse) { - "SPARTN parser", // parserName - sempSpartnPreamble, // preamble - sizeof(SEMP_SPARTN_VALUES), // scratchPadBytes -}; + SEMP_SPARTN_VALUES *scratchPad = (SEMP_SPARTN_VALUES *)parse->scratchPad; + return scratchPad->messageType; +} diff --git a/src/Parse_UBLOX.cpp b/src/Parse_UBLOX.cpp index fd8f255..ccd1980 100644 --- a/src/Parse_UBLOX.cpp +++ b/src/Parse_UBLOX.cpp @@ -38,9 +38,14 @@ typedef struct _SEMP_UBLOX_VALUES uint8_t ck_b; // U-blox checksum byte 2 } SEMP_UBLOX_VALUES; -//---------------------------------------- +//------------------------------------------------------------------------------ // U-BLOX parse routines -//---------------------------------------- +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ // // U-BLOX Message @@ -63,7 +68,9 @@ typedef struct _SEMP_UBLOX_VALUES // CK_B += CK_A // +//---------------------------------------- // Read the CK_B byte +//---------------------------------------- bool sempUbloxCkB(SEMP_PARSE_STATE *parse, uint8_t data) { bool badChecksum; @@ -89,14 +96,18 @@ bool sempUbloxCkB(SEMP_PARSE_STATE *parse, uint8_t data) return false; } +//---------------------------------------- // Read the CK_A byte +//---------------------------------------- bool sempUbloxCkA(SEMP_PARSE_STATE *parse, uint8_t data) { parse->state = sempUbloxCkB; return true; } +//---------------------------------------- // Read the payload +//---------------------------------------- bool sempUbloxPayload(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; @@ -112,7 +123,9 @@ bool sempUbloxPayload(SEMP_PARSE_STATE *parse, uint8_t data) return sempUbloxCkA(parse, data); } +//---------------------------------------- // Read the second length byte +//---------------------------------------- bool sempUbloxLength2(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; @@ -139,7 +152,9 @@ bool sempUbloxLength2(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the first length byte +//---------------------------------------- bool sempUbloxLength1(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; @@ -154,7 +169,9 @@ bool sempUbloxLength1(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the ID byte +//---------------------------------------- bool sempUbloxId(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; @@ -168,7 +185,9 @@ bool sempUbloxId(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the class byte +//---------------------------------------- bool sempUbloxClass(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; @@ -182,7 +201,9 @@ bool sempUbloxClass(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the second sync byte +//---------------------------------------- bool sempUbloxSync2(SEMP_PARSE_STATE *parse, uint8_t data) { // Verify the sync 2 byte @@ -201,7 +222,17 @@ bool sempUbloxSync2(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the UBLOX parser recgonizes the input and false +// if another parser should be used +//---------------------------------------- bool sempUbloxPreamble(SEMP_PARSE_STATE *parse, uint8_t data) { if (data != 0xb5) @@ -210,7 +241,15 @@ bool sempUbloxPreamble(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- const char * sempUbloxGetStateName(const SEMP_PARSE_STATE *parse) { if (parse->state == sempUbloxPreamble) @@ -234,124 +273,58 @@ const char * sempUbloxGetStateName(const SEMP_PARSE_STATE *parse) return nullptr; } -// Get the message number: |- Class (8 bits) -||- ID (8 bits) -| -uint16_t sempUbloxGetMessageNumber(const SEMP_PARSE_STATE *parse) +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Describe the parser +//---------------------------------------- +SEMP_PARSER_DESCRIPTION sempUbloxParserDescription = { - SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; - uint16_t message = ((uint16_t)scratchPad->messageClass) << 8; - message |= (uint16_t)scratchPad->messageId; - return message; -} + "U-Blox parser", // parserName + sempUbloxPreamble, // preamble + sizeof(SEMP_UBLOX_VALUES), // scratchPadBytes + SEMP_UBLOX_PAYLOAD_OFFSET, // payloadOffset +}; +//---------------------------------------- // Get the message Class +//---------------------------------------- uint8_t sempUbloxGetMessageClass(const SEMP_PARSE_STATE *parse) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; return scratchPad->messageClass; } +//---------------------------------------- // Get the message ID +//---------------------------------------- uint8_t sempUbloxGetMessageId(const SEMP_PARSE_STATE *parse) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; return scratchPad->messageId; } -// Get the Payload Length -uint16_t sempUbloxGetPayloadLength(const SEMP_PARSE_STATE *parse) +//---------------------------------------- +// Get the message number: |- Class (8 bits) -||- ID (8 bits) -| +//---------------------------------------- +uint16_t sempUbloxGetMessageNumber(const SEMP_PARSE_STATE *parse) { SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; - return scratchPad->payloadLength; + uint16_t message = ((uint16_t)scratchPad->messageClass) << 8; + message |= (uint16_t)scratchPad->messageId; + return message; } -// Get data -uint8_t sempUbloxGetU1(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - return parse->buffer[offset + SEMP_UBLOX_PAYLOAD_OFFSET]; -} -uint16_t sempUbloxGetU2(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - uint16_t data = parse->buffer[offset + SEMP_UBLOX_PAYLOAD_OFFSET]; - data |= ((uint16_t)parse->buffer[offset + SEMP_UBLOX_PAYLOAD_OFFSET + 1]) << 8; - return data; -} -uint32_t sempUbloxGetU4(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - uint32_t data = 0; - for (uint16_t i = 0; i < sizeof(data); i++) - data |= ((uint32_t)parse->buffer[offset + SEMP_UBLOX_PAYLOAD_OFFSET + i]) << (8 * i); - return data; -} -uint64_t sempUbloxGetU8(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - uint64_t data = 0; - for (uint16_t i = 0; i < sizeof(data); i++) - data |= ((uint64_t)parse->buffer[offset + SEMP_UBLOX_PAYLOAD_OFFSET + i]) << (8 * i); - return data; -} -int8_t sempUbloxGetI1(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint8_t unsignedN; - int8_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempUbloxGetU1(parse, offset); - return unsignedSignedN.signedN; -} -int16_t sempUbloxGetI2(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint16_t unsignedN; - int16_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempUbloxGetU2(parse, offset); - return unsignedSignedN.signedN; -} -int32_t sempUbloxGetI4(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint32_t unsignedN; - int32_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempUbloxGetU4(parse, offset); - return unsignedSignedN.signedN; -} -int64_t sempUbloxGetI8(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint64_t unsignedN; - int64_t signedN; - } unsignedSignedN; - unsignedSignedN.unsignedN = sempUbloxGetU8(parse, offset); - return unsignedSignedN.signedN; -} -float sempUbloxGetR4(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint32_t unsignedN; - float flt; - } unsignedFloat; - unsignedFloat.unsignedN = sempUbloxGetU4(parse, offset); - return unsignedFloat.flt; -} -double sempUbloxGetR8(const SEMP_PARSE_STATE *parse, uint16_t offset) -{ - union { - uint64_t unsignedN; - double flt; - } unsignedFloat; - unsignedFloat.unsignedN = sempUbloxGetU8(parse, offset); - return unsignedFloat.flt; -} -const char *sempUbloxGetString(const SEMP_PARSE_STATE *parse, uint16_t offset) +//---------------------------------------- +// Get the Payload Length +//---------------------------------------- +size_t sempUbloxGetPayloadLength(const SEMP_PARSE_STATE *parse) { - return (const char *)(&parse->buffer[offset]); + SEMP_UBLOX_VALUES *scratchPad = (SEMP_UBLOX_VALUES *)parse->scratchPad; + return scratchPad->payloadLength; } - -// Describe the parser -SEMP_PARSER_DESCRIPTION sempUbloxParserDescription = -{ - "U-Blox parser", // parserName - sempUbloxPreamble, // preamble - sizeof(SEMP_UBLOX_VALUES), // scratchPadBytes -}; diff --git a/src/Parse_Unicore_Binary.cpp b/src/Parse_Unicore_Binary.cpp index 2f51245..afbe285 100644 --- a/src/Parse_Unicore_Binary.cpp +++ b/src/Parse_Unicore_Binary.cpp @@ -27,11 +27,16 @@ typedef struct _SEMP_UNICORE_BINARY_VALUES uint16_t bytesRemaining; // Bytes remaining in RTCM CRC calculation } SEMP_UNICORE_BINARY_VALUES; -//---------------------------------------- +//------------------------------------------------------------------------------ // Support routines -//---------------------------------------- +// +// The parser support routines are placed before the parser routines to eliminate +// forward declarations. +//------------------------------------------------------------------------------ +//---------------------------------------- // Compute the CRC for the Unicore data +//---------------------------------------- uint32_t sempUnicoreBinaryComputeCrc(SEMP_PARSE_STATE *parse, uint8_t data) { uint32_t crc; @@ -41,36 +46,14 @@ uint32_t sempUnicoreBinaryComputeCrc(SEMP_PARSE_STATE *parse, uint8_t data) return crc; } -// Print the Unicore message header -void sempUnicoreBinaryPrintHeader(SEMP_PARSE_STATE *parse) -{ - SEMP_UNICORE_HEADER * header; - - Print *print = parse->printError; - if (print) - { - sempPrintln(print, "Unicore Message Header"); - header = (SEMP_UNICORE_HEADER *)parse->buffer; - sempPrintf(print, " 0x%02x: Sync A", header->syncA); - sempPrintf(print, " 0x%02x: Sync B", header->syncB); - sempPrintf(print, " 0x%02x: Sync C", header->syncC); - sempPrintf(print, " %3d%%: CPU Idle Time", header->cpuIdlePercent); - sempPrintf(print, " %5d: Message ID", header->messageId); - sempPrintf(print, " %5d: Message Length (bytes)", header->messageLength); - sempPrintf(print, " %3d: Reference Time", header->referenceTime); - sempPrintf(print, " 0x%02x: Time Status", header->timeStatus); - sempPrintf(print, " %5d: Week Number", header->weekNumber); - sempPrintf(print, "%10d: Seconds of Week", header->secondsOfWeek); - sempPrintf(print, "0x%08x: RESERVED", header->RESERVED); - sempPrintf(print, " %3d: Release Version", header->releasedVersion); - sempPrintf(print, " %3d: Leap Seconds", header->leapSeconds); - sempPrintf(print, " %5d: Output Delay (mSec)", header->outputDelayMSec); - } -} - -//---------------------------------------- +//------------------------------------------------------------------------------ // Unicore parse routines -//---------------------------------------- +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ // // Unicore Binary Response @@ -86,7 +69,9 @@ void sempUnicoreBinaryPrintHeader(SEMP_PARSE_STATE *parse) // |<------------------------ CRC --------------->| // +//---------------------------------------- // Read the CRC +//---------------------------------------- bool sempUnicoreBinaryReadCrc(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UNICORE_BINARY_VALUES *scratchPad = (SEMP_UNICORE_BINARY_VALUES *)parse->scratchPad; @@ -118,7 +103,9 @@ bool sempUnicoreBinaryReadCrc(SEMP_PARSE_STATE *parse, uint8_t data) return false; } +//---------------------------------------- // Read the message data +//---------------------------------------- bool sempUnicoreBinaryReadData(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UNICORE_BINARY_VALUES *scratchPad = (SEMP_UNICORE_BINARY_VALUES *)parse->scratchPad; @@ -134,6 +121,7 @@ bool sempUnicoreBinaryReadData(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Header // // Bytes Field Description @@ -152,6 +140,7 @@ bool sempUnicoreBinaryReadData(SEMP_PARSE_STATE *parse, uint8_t data) // 2 Output delay time, ms // // Read the header +//---------------------------------------- bool sempUnicoreBinaryReadHeader(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UNICORE_BINARY_VALUES *scratchPad = (SEMP_UNICORE_BINARY_VALUES *)parse->scratchPad; @@ -172,7 +161,9 @@ bool sempUnicoreBinaryReadHeader(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the third sync byte +//---------------------------------------- bool sempUnicoreBinaryBinarySync3(SEMP_PARSE_STATE *parse, uint8_t data) { // Verify sync byte 3 @@ -185,7 +176,9 @@ bool sempUnicoreBinaryBinarySync3(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the second sync byte +//---------------------------------------- bool sempUnicoreBinaryBinarySync2(SEMP_PARSE_STATE *parse, uint8_t data) { // Verify sync byte 2 @@ -198,7 +191,17 @@ bool sempUnicoreBinaryBinarySync2(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the Unicore Binary parser recgonizes the input and +// false if another parser should be used +//---------------------------------------- bool sempUnicoreBinaryPreamble(SEMP_PARSE_STATE *parse, uint8_t data) { // Determine if this is the beginning of a Unicore message @@ -213,7 +216,15 @@ bool sempUnicoreBinaryPreamble(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- const char * sempUnicoreBinaryGetStateName(const SEMP_PARSE_STATE *parse) { if (parse->state == sempUnicoreBinaryPreamble) @@ -231,10 +242,49 @@ const char * sempUnicoreBinaryGetStateName(const SEMP_PARSE_STATE *parse) return nullptr; } +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ + +//---------------------------------------- // Describe the parser +//---------------------------------------- SEMP_PARSER_DESCRIPTION sempUnicoreBinaryParserDescription = { "Unicore binary parser", // parserName sempUnicoreBinaryPreamble, // preamble sizeof(SEMP_UNICORE_BINARY_VALUES), // scratchPadBytes + 0, // payloadOffset }; + +//---------------------------------------- +// Print the Unicore message header +//---------------------------------------- +void sempUnicoreBinaryPrintHeader(SEMP_PARSE_STATE *parse) +{ + SEMP_UNICORE_HEADER * header; + + Print *print = parse->printError; + if (print) + { + sempPrintln(print, "Unicore Message Header"); + header = (SEMP_UNICORE_HEADER *)parse->buffer; + sempPrintf(print, " 0x%02x: Sync A", header->syncA); + sempPrintf(print, " 0x%02x: Sync B", header->syncB); + sempPrintf(print, " 0x%02x: Sync C", header->syncC); + sempPrintf(print, " %3d%%: CPU Idle Time", header->cpuIdlePercent); + sempPrintf(print, " %5d: Message ID", header->messageId); + sempPrintf(print, " %5d: Message Length (bytes)", header->messageLength); + sempPrintf(print, " %3d: Reference Time", header->referenceTime); + sempPrintf(print, " 0x%02x: Time Status", header->timeStatus); + sempPrintf(print, " %5d: Week Number", header->weekNumber); + sempPrintf(print, "%10d: Seconds of Week", header->secondsOfWeek); + sempPrintf(print, "0x%08x: RESERVED", header->RESERVED); + sempPrintf(print, " %3d: Release Version", header->releasedVersion); + sempPrintf(print, " %3d: Leap Seconds", header->leapSeconds); + sempPrintf(print, " %5d: Output Delay (mSec)", header->outputDelayMSec); + } +} diff --git a/src/Parse_Unicore_Hash.cpp b/src/Parse_Unicore_Hash.cpp index 92d87e1..8ca4f8d 100644 --- a/src/Parse_Unicore_Hash.cpp +++ b/src/Parse_Unicore_Hash.cpp @@ -39,9 +39,14 @@ typedef struct _SEMP_UNICORE_HASH_VALUES uint8_t sentenceNameLength; // Length of the sentence name } SEMP_UNICORE_HASH_VALUES; -//---------------------------------------- +//------------------------------------------------------------------------------ // Unicore hash (#) parse routines -//---------------------------------------- +// +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. The public +// data structures and routines are listed at the end of the file. +//------------------------------------------------------------------------------ // // Unicore Hash (#) Sentence @@ -55,10 +60,12 @@ typedef struct _SEMP_UNICORE_HASH_VALUES // |<-------- Checksum -------->| // +//---------------------------------------- // Compute the CRC for a sentence containing an 8 byte CRC value // One such example is the full version message // #VERSION,97,GPS,FINE,2282,248561000,0,0,18,676;UM980,R4.10Build7923,HRPT00-S10C-P,2310415000001-MD22B1224962616,ff3bac96f31f9bdd,2022/09/28*7432d4ed // CRC is calculated without the # or * characters +//---------------------------------------- void sempUnicoreHashValidatCrc(SEMP_PARSE_STATE *parse) { SEMP_UNICORE_HASH_VALUES *scratchPad = (SEMP_UNICORE_HASH_VALUES *)parse->scratchPad; @@ -121,7 +128,9 @@ void sempUnicoreHashValidatCrc(SEMP_PARSE_STATE *parse) parse->eomCallback(parse, parse->type); // Pass parser array index } +//---------------------------------------- // Validate the checksum +//---------------------------------------- void sempUnicoreHashValidateChecksum(SEMP_PARSE_STATE *parse) { uint32_t checksum; @@ -165,7 +174,9 @@ void sempUnicoreHashValidateChecksum(SEMP_PARSE_STATE *parse) parse->crc); } +//---------------------------------------- // Read the linefeed +//---------------------------------------- bool sempUnicoreHashLineFeed(SEMP_PARSE_STATE *parse, uint8_t data) { // Don't add the current character to the length @@ -189,7 +200,9 @@ bool sempUnicoreHashLineFeed(SEMP_PARSE_STATE *parse, uint8_t data) return sempFirstByte(parse, data); } +//---------------------------------------- // Read the remaining carriage return +//---------------------------------------- bool sempUnicoreHashCarriageReturn(SEMP_PARSE_STATE *parse, uint8_t data) { // Don't add the current character to the length @@ -213,7 +226,9 @@ bool sempUnicoreHashCarriageReturn(SEMP_PARSE_STATE *parse, uint8_t data) return sempFirstByte(parse, data); } +//---------------------------------------- // Read the line termination +//---------------------------------------- bool sempUnicoreHashLineTermination(SEMP_PARSE_STATE *parse, uint8_t data) { // Don't add the current character to the length @@ -238,7 +253,9 @@ bool sempUnicoreHashLineTermination(SEMP_PARSE_STATE *parse, uint8_t data) return sempFirstByte(parse, data); } +//---------------------------------------- // Read the checksum bytes +//---------------------------------------- bool sempUnicoreHashChecksumByte(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UNICORE_HASH_VALUES *scratchPad = (SEMP_UNICORE_HASH_VALUES *)parse->scratchPad; @@ -265,7 +282,9 @@ bool sempUnicoreHashChecksumByte(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the sentence data +//---------------------------------------- bool sempUnicoreHashFindAsterisk(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UNICORE_HASH_VALUES *scratchPad = (SEMP_UNICORE_HASH_VALUES *)parse->scratchPad; @@ -310,7 +329,9 @@ bool sempUnicoreHashFindAsterisk(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Read the sentence name +//---------------------------------------- bool sempUnicoreHashFindFirstComma(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UNICORE_HASH_VALUES *scratchPad = (SEMP_UNICORE_HASH_VALUES *)parse->scratchPad; @@ -354,7 +375,17 @@ bool sempUnicoreHashFindFirstComma(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Check for the preamble +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if the Unicore Hash parser recgonizes the input and +// false if another parser should be used +//---------------------------------------- bool sempUnicoreHashPreamble(SEMP_PARSE_STATE *parse, uint8_t data) { SEMP_UNICORE_HASH_VALUES *scratchPad = (SEMP_UNICORE_HASH_VALUES *)parse->scratchPad; @@ -365,7 +396,15 @@ bool sempUnicoreHashPreamble(SEMP_PARSE_STATE *parse, uint8_t data) return true; } +//---------------------------------------- // Translates state value into an string, returns nullptr if not found +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs +// Returns the address of the zero terminated state name string +//---------------------------------------- const char * sempUnicoreHashGetStateName(const SEMP_PARSE_STATE *parse) { if (parse->state == sempUnicoreHashPreamble) @@ -385,17 +424,38 @@ const char * sempUnicoreHashGetStateName(const SEMP_PARSE_STATE *parse) return nullptr; } -// Return the Unicore hash (#) sentence name as a string -const char * sempUnicoreHashGetSentenceName(const SEMP_PARSE_STATE *parse) -{ - SEMP_UNICORE_HASH_VALUES *scratchPad = (SEMP_UNICORE_HASH_VALUES *)parse->scratchPad; - return (const char *)scratchPad->sentenceName; -} +//------------------------------------------------------------------------------ +// Public data and routines +// +// The following data structures and routines are listed in the .h file and are +// exposed to the SEMP routine and application layer. +//------------------------------------------------------------------------------ +//---------------------------------------- // Describe the parser +//---------------------------------------- SEMP_PARSER_DESCRIPTION sempUnicoreHashParserDescription = { "Unicore hash parser", // parserName sempUnicoreHashPreamble, // preamble sizeof(SEMP_UNICORE_HASH_VALUES), // scratchPadBytes + 0, // payloadOffset }; + +//---------------------------------------- +// Abort Unicore hash parsing on a non-printable char +//---------------------------------------- +void sempUnicoreHashAbortOnNonPrintable(SEMP_PARSE_STATE *parse, bool abort) +{ + if (parse) + parse->unicoreHashAbortOnNonPrintable = abort; +} + +//---------------------------------------- +// Return the Unicore hash (#) sentence name as a string +//---------------------------------------- +const char * sempUnicoreHashGetSentenceName(const SEMP_PARSE_STATE *parse) +{ + SEMP_UNICORE_HASH_VALUES *scratchPad = (SEMP_UNICORE_HASH_VALUES *)parse->scratchPad; + return (const char *)scratchPad->sentenceName; +} diff --git a/src/SparkFun_Extensible_Message_Parser.cpp b/src/SparkFun_Extensible_Message_Parser.cpp index ad76882..72c770a 100644 --- a/src/SparkFun_Extensible_Message_Parser.cpp +++ b/src/SparkFun_Extensible_Message_Parser.cpp @@ -25,10 +25,14 @@ License: MIT. Please see LICENSE.md for more details // Align x to multiples of 8: 0->0; 1->8; 8->8; 9->16 #define SEMP_ALIGN(x) ((x + SEMP_ALIGNMENT_MASK) & (~SEMP_ALIGNMENT_MASK)) -//---------------------------------------- -// Support routines -//---------------------------------------- +//------------------------------------------------------------------------------ +// SEMP private support routines +// +// The support routines are placed before the parser routines to eliminate +// forward declarations. +//------------------------------------------------------------------------------ +//---------------------------------------- // Compute the scratch pad length // Inputs: // parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses @@ -36,6 +40,7 @@ License: MIT. Please see LICENSE.md for more details // // Outputs: // Returns the number of bytes needed for the scratch pad +//---------------------------------------- size_t sempGetScratchPadLength(SEMP_PARSER_DESCRIPTION **parserTable, uint16_t parserCount) { @@ -52,6 +57,7 @@ size_t sempGetScratchPadLength(SEMP_PARSER_DESCRIPTION **parserTable, return SEMP_ALIGN(length); } +//---------------------------------------- // Locate the parse structure // Inputs: // scratchPadBytes: Desired size of the scratch pad in bytes @@ -61,6 +67,7 @@ size_t sempGetScratchPadLength(SEMP_PARSER_DESCRIPTION **parserTable, // // Outputs: // Returns the number of bytes needed for the scratch pad +//---------------------------------------- SEMP_PARSE_STATE * sempLocateParseStructure(uint16_t scratchPadBytes, uint8_t *buffer, size_t bufferLength, @@ -118,7 +125,15 @@ SEMP_PARSE_STATE * sempLocateParseStructure(uint16_t scratchPadBytes, return parse; } -// Convert nibble to ASCII +//------------------------------------------------------------------------------ +// SparkFun Extensible Message Parser API routines +// +// These routines are called by parsers and applications +//------------------------------------------------------------------------------ + +//---------------------------------------- +// Convert an ASCII character (0-9, A-F, or a-f) into a 4-bit binary value +//---------------------------------------- int sempAsciiToNibble(int data) { // Convert the value to lower case @@ -130,129 +145,9 @@ int sempAsciiToNibble(int data) return -1; } -// Translate the type value into an ASCII type name -const char * sempGetTypeName(SEMP_PARSE_STATE *parse, uint16_t type) -{ - const char *name = "Unknown parser"; - - if (parse) - { - if (type < parse->parserCount) - name = parse->parsers[type]->parserName; - else if (type == parse->parserCount) - name = "SEMP scanning for preamble"; - } - return name; -} - -// Print the parser's configuration -void sempPrintParserConfiguration(SEMP_PARSE_STATE *parse, Print *print) -{ - if (print && parse) - { - sempPrintln(print, "SparkFun Extensible Message Parser"); - sempPrintf(print, " parserName: %p (%s)", parse->parserName, parse->parserName); - sempPrintf(print, " parsers: %p", (void *)parse->parsers); - sempPrintf(print, " parserCount: %d", parse->parserCount); - sempPrintf(print, " printError: %p", parse->printError); - sempPrintf(print, " printDebug: %p", parse->printDebug); - sempPrintf(print, " verboseDebug: %d", parse->verboseDebug); - sempPrintf(print, " nmeaAbortOnNonPrintable: %d", parse->nmeaAbortOnNonPrintable); - sempPrintf(print, " unicoreHashAbortOnNonPrintable: %d", parse->unicoreHashAbortOnNonPrintable); - sempPrintf(print, " Scratch Pad: %p (%ld bytes)", - (void *)parse->scratchPad, - parse->scratchPad ? (parse->buffer - (uint8_t *)parse->scratchPad) - : 0); - sempPrintf(print, " computeCrc: %p", (void *)parse->computeCrc); - sempPrintf(print, " crc: 0x%08x", parse->crc); - sempPrintf(print, " State: %p%s", (void *)parse->state, - (parse->state == sempFirstByte) ? " (sempFirstByte)" : ""); - sempPrintf(print, " EomCallback: %p", (void *)parse->eomCallback); - sempPrintf(print, " Buffer: %p (%d bytes)", - (void *)parse->buffer, parse->bufferLength); - sempPrintf(print, " length: %d message bytes", parse->length); - sempPrintf(print, " type: %d (%s)", parse->type, sempGetTypeName(parse, parse->type)); - } -} - -// Format and print a line of text -void sempPrintf(Print *print, const char *format, ...) -{ - if (print) - { - // https://stackoverflow.com/questions/42131753/wrapper-for-printf - va_list args; - va_start(args, format); - va_list args2; - - va_copy(args2, args); - char buf[vsnprintf(nullptr, 0, format, args) + sizeof("\r\n")]; - - vsnprintf(buf, sizeof buf, format, args2); - - // Add CR+LF - buf[sizeof(buf) - 3] = '\r'; - buf[sizeof(buf) - 2] = '\n'; - buf[sizeof(buf) - 1] = '\0'; - - print->write(buf, strlen(buf)); - - va_end(args); - va_end(args2); - } -} - -// Print a line of error text -void sempPrintln(Print *print, const char *string) -{ - if (print) - print->println(string); -} - -// Translates state value into an ASCII state name -const char * sempGetStateName(const SEMP_PARSE_STATE *parse) -{ - if (parse && (parse->state == sempFirstByte)) - return "sempFirstByte"; - return "Unknown state"; -} - -// Disable debug output -void sempDisableDebugOutput(SEMP_PARSE_STATE *parse) -{ - if (parse) - parse->printDebug = nullptr; -} - -// Enable debug output -void sempEnableDebugOutput(SEMP_PARSE_STATE *parse, Print *print, bool verbose) -{ - if (parse) - { - parse->printDebug = print; - parse->verboseDebug = verbose; - } -} - -// Disable error output -void sempDisableErrorOutput(SEMP_PARSE_STATE *parse) -{ - if (parse) - parse->printError = nullptr; -} - -// Enable error output -void sempEnableErrorOutput(SEMP_PARSE_STATE *parse, Print *print) -{ - if (parse) - parse->printError = print; -} - -//---------------------------------------- -// Parse routines //---------------------------------------- - // Initialize the parser +//---------------------------------------- SEMP_PARSE_STATE *sempBeginParser( const char *parserTableName, SEMP_PARSER_DESCRIPTION **parserTable, @@ -334,7 +229,27 @@ SEMP_PARSE_STATE *sempBeginParser( return parse; } +//---------------------------------------- +// Disable debug output +//---------------------------------------- +void sempDisableDebugOutput(SEMP_PARSE_STATE *parse) +{ + if (parse) + parse->printDebug = nullptr; +} + +//---------------------------------------- +// Disable error output +//---------------------------------------- +void sempDisableErrorOutput(SEMP_PARSE_STATE *parse) +{ + if (parse) + parse->printError = nullptr; +} + +//---------------------------------------- // Wait for the first byte in the data stream +//---------------------------------------- bool sempFirstByte(SEMP_PARSE_STATE *parse, uint8_t data) { int index; @@ -364,23 +279,20 @@ bool sempFirstByte(SEMP_PARSE_STATE *parse, uint8_t data) } } - // Preamble byte not found, continue searching for a preamble byte + // Preamble not found, pass this data to another parser if requested + if (parse->invalidData) + parse->invalidData(parse->buffer, parse->length); + + // Continue searching for a preamble byte parse->state = sempFirstByte; } return false; } +//---------------------------------------- // Compute the necessary buffer length in bytes to support the scratch pad -// and parse buffer lengths. -// Inputs: -// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses -// parserCount: Number of entries in the parseTable -// parseBufferBytes: Desired size of the parse buffer in bytes -// printDebug: Device to output any debug messages, may be nullptr -// -// Outputs: -// Returns the number of bytes needed for the buffer that contains -// the SEMP parser state, a scratch pad area and the parse buffer +// and parse buffer lengths +//---------------------------------------- size_t sempGetBufferLength(SEMP_PARSER_DESCRIPTION **parserTable, uint16_t parserCount, size_t parserBufferBytes, @@ -411,7 +323,284 @@ size_t sempGetBufferLength(SEMP_PARSER_DESCRIPTION **parserTable, return length; } +//---------------------------------------- +// Get a 32-bit floating point value +//---------------------------------------- +float sempGetF4(const SEMP_PARSE_STATE *parse, uint16_t offset) +{ + float value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 32-bit floating point value +//---------------------------------------- +float sempGetF4NoOffset(const SEMP_PARSE_STATE *parse, uint16_t offset) +{ + float value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 64-bit floating point (double) value +//---------------------------------------- +double sempGetF8(const SEMP_PARSE_STATE *parse, size_t offset) +{ + double value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 64-bit floating point (double) value +//---------------------------------------- +double sempGetF8NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + double value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get an 8-bit integer value +//---------------------------------------- +int8_t sempGetI1(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int8_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + value = *(int8_t *)&parse->buffer[offset + parserDescription->payloadOffset]; + return value; +} + +//---------------------------------------- +// Get an 8-bit integer value +//---------------------------------------- +int8_t sempGetI1NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int8_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + value = *(int8_t *)&parse->buffer[offset]; + return value; +} + +//---------------------------------------- +// Get a 16-bit integer value +//---------------------------------------- +int16_t sempGetI2(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int16_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 16-bit integer value +//---------------------------------------- +int16_t sempGetI2NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int16_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 32-bit integer value +//---------------------------------------- +int32_t sempGetI4(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int32_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 32-bit integer value +//---------------------------------------- +int32_t sempGetI4NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int32_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 64-bit integer value +//---------------------------------------- +int64_t sempGetI8(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int64_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 64-bit integer value +//---------------------------------------- +int64_t sempGetI8NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + int64_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Translates state value into an ASCII state name +//---------------------------------------- +const char * sempGetStateName(const SEMP_PARSE_STATE *parse) +{ + if (parse && (parse->state == sempFirstByte)) + return "sempFirstByte"; + return "Unknown state"; +} + +//---------------------------------------- +// Get a zero terminated string address +//---------------------------------------- +const char * sempGetString(const SEMP_PARSE_STATE *parse, size_t offset) +{ + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + return (const char *)&parse->buffer[offset + parserDescription->payloadOffset]; +} + +//---------------------------------------- +// Get a zero terminated string address +//---------------------------------------- +const char * sempGetStringNoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + return (const char *)&parse->buffer[offset]; +} + +//---------------------------------------- +// Translate the type value into an ASCII type name +//---------------------------------------- +const char * sempGetTypeName(SEMP_PARSE_STATE *parse, uint16_t type) +{ + const char *name = "Unknown parser"; + + if (parse) + { + if (type < parse->parserCount) + name = parse->parsers[type]->parserName; + else if (type == parse->parserCount) + name = "SEMP scanning for preamble"; + } + return name; +} + +//---------------------------------------- +// Get an 8-bit integer value +//---------------------------------------- +uint8_t sempGetU1(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint8_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + value = *(uint8_t *)&parse->buffer[offset + parserDescription->payloadOffset]; + return value; +} + +//---------------------------------------- +// Get an 8-bit integer value +//---------------------------------------- +uint8_t sempGetU1NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint8_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + value = *(uint8_t *)&parse->buffer[offset]; + return value; +} + +//---------------------------------------- +// Get a 16-bit integer value +//---------------------------------------- +uint16_t sempGetU2(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint16_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 16-bit integer value +//---------------------------------------- +uint16_t sempGetU2NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint16_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 32-bit integer value +//---------------------------------------- +uint32_t sempGetU4(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint32_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 32-bit integer value +//---------------------------------------- +uint32_t sempGetU4NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint32_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 64-bit integer value +//---------------------------------------- +uint64_t sempGetU8(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint64_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset + parserDescription->payloadOffset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Get a 64-bit integer value +//---------------------------------------- +uint64_t sempGetU8NoOffset(const SEMP_PARSE_STATE *parse, size_t offset) +{ + uint64_t value; + SEMP_PARSER_DESCRIPTION *parserDescription = parse->parsers[parse->type]; + memcpy(&value, &parse->buffer[offset], sizeof(value)); + return value; +} + +//---------------------------------------- +// Perform the invalid data callback +//---------------------------------------- +void sempInvalidDataCallback(SEMP_PARSE_STATE *parse) +{ + if (parse->invalidData) + parse->invalidData(parse->buffer, parse->length); + parse->state = sempFirstByte; +} + +//---------------------------------------- // Parse the next byte +//---------------------------------------- void sempParseNextByte(SEMP_PARSE_STATE *parse, uint8_t data) { if (parse) @@ -424,6 +613,10 @@ void sempParseNextByte(SEMP_PARSE_STATE *parse, uint8_t data) parse->parserName, parse->bufferLength); + // Pass this data to another parser if requested + if (parse->invalidData) + parse->invalidData(parse->buffer, parse->length); + // Start searching for a preamble byte sempFirstByte(parse, data); return; @@ -441,18 +634,59 @@ void sempParseNextByte(SEMP_PARSE_STATE *parse, uint8_t data) } } +//---------------------------------------- // Parse the next bytes -void sempParseNextBytes(SEMP_PARSE_STATE *parse, uint8_t *data, uint16_t len) +//---------------------------------------- +void sempParseNextBytes(SEMP_PARSE_STATE *parse, const uint8_t *data, size_t len) { - uint8_t *ptr = data; - for (uint16_t i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) + sempParseNextByte(parse, *data++); +} + +//---------------------------------------- +// Set the invalid data callback +//---------------------------------------- +void sempSetInvalidDataCallback(SEMP_PARSE_STATE *parse, + SEMP_INVALID_DATA_CALLBACK invalidDataCallback) +{ + parse->invalidData = invalidDataCallback; +} + +//---------------------------------------- +// Print the parser's configuration +//---------------------------------------- +void sempPrintParserConfiguration(SEMP_PARSE_STATE *parse, Print *print) +{ + if (print && parse) { - sempParseNextByte(parse, *ptr); - ptr++; + sempPrintln(print, "SparkFun Extensible Message Parser"); + sempPrintf(print, " parserName: %p (%s)", parse->parserName, parse->parserName); + sempPrintf(print, " parsers: %p", (void *)parse->parsers); + sempPrintf(print, " parserCount: %d", parse->parserCount); + sempPrintf(print, " printError: %p", parse->printError); + sempPrintf(print, " printDebug: %p", parse->printDebug); + sempPrintf(print, " verboseDebug: %d", parse->verboseDebug); + sempPrintf(print, " nmeaAbortOnNonPrintable: %d", parse->nmeaAbortOnNonPrintable); + sempPrintf(print, " unicoreHashAbortOnNonPrintable: %d", parse->unicoreHashAbortOnNonPrintable); + sempPrintf(print, " Scratch Pad: %p (%ld bytes)", + (void *)parse->scratchPad, + parse->scratchPad ? (parse->buffer - (uint8_t *)parse->scratchPad) + : 0); + sempPrintf(print, " computeCrc: %p", (void *)parse->computeCrc); + sempPrintf(print, " crc: 0x%08x", parse->crc); + sempPrintf(print, " State: %p%s", (void *)parse->state, + (parse->state == sempFirstByte) ? " (sempFirstByte)" : ""); + sempPrintf(print, " EomCallback: %p", (void *)parse->eomCallback); + sempPrintf(print, " Buffer: %p (%d bytes)", + (void *)parse->buffer, parse->bufferLength); + sempPrintf(print, " length: %d message bytes", parse->length); + sempPrintf(print, " type: %d (%s)", parse->type, sempGetTypeName(parse, parse->type)); } } +//---------------------------------------- // Shutdown the parser +//---------------------------------------- void sempStopParser(SEMP_PARSE_STATE **parse) { // Prevent further references to the structure @@ -460,20 +694,65 @@ void sempStopParser(SEMP_PARSE_STATE **parse) *parse = nullptr; } +//------------------------------------------------------------------------------ +// V1 routines to eliminate +//------------------------------------------------------------------------------ + //---------------------------------------- -// Parser specific routines +// Enable debug output //---------------------------------------- - -// Abort NMEA parsing on a non-printable char -void sempNmeaAbortOnNonPrintable(SEMP_PARSE_STATE *parse, bool abort) +void sempEnableDebugOutput(SEMP_PARSE_STATE *parse, Print *print, bool verbose) { if (parse) - parse->nmeaAbortOnNonPrintable = abort; + { + parse->printDebug = print; + parse->verboseDebug = verbose; + } } -// Abort Unicore hash parsing on a non-printable char -void sempUnicoreHashAbortOnNonPrintable(SEMP_PARSE_STATE *parse, bool abort) +//---------------------------------------- +// Enable error output +//---------------------------------------- +void sempEnableErrorOutput(SEMP_PARSE_STATE *parse, Print *print) { if (parse) - parse->unicoreHashAbortOnNonPrintable = abort; + parse->printError = print; +} + +//---------------------------------------- +// Format and print a line of text +//---------------------------------------- +void sempPrintf(Print *print, const char *format, ...) +{ + if (print) + { + // https://stackoverflow.com/questions/42131753/wrapper-for-printf + va_list args; + va_start(args, format); + va_list args2; + + va_copy(args2, args); + char buf[vsnprintf(nullptr, 0, format, args) + sizeof("\r\n")]; + + vsnprintf(buf, sizeof buf, format, args2); + + // Add CR+LF + buf[sizeof(buf) - 3] = '\r'; + buf[sizeof(buf) - 2] = '\n'; + buf[sizeof(buf) - 1] = '\0'; + + print->write(buf, strlen(buf)); + + va_end(args); + va_end(args2); + } +} + +//---------------------------------------- +// Print a line of error text +//---------------------------------------- +void sempPrintln(Print *print, const char *string) +{ + if (print) + print->println(string); } diff --git a/src/SparkFun_Extensible_Message_Parser.h b/src/SparkFun_Extensible_Message_Parser.h index 33ff527..d9c53f6 100644 --- a/src/SparkFun_Extensible_Message_Parser.h +++ b/src/SparkFun_Extensible_Message_Parser.h @@ -76,14 +76,16 @@ typedef uint32_t (*SEMP_COMPUTE_CRC)(P_SEMP_PARSE_STATE parse, uint8_t dataByte) typedef void (*SEMP_EOM_CALLBACK)(P_SEMP_PARSE_STATE parse, uint16_t type); // Invalid data callback +// // Inputs: -// parse: Address of a SEMP_PARSE_STATE structure +// buffer: Address of a buffer containing the invalid data +// length: Number of bytes in the buffer // // Normally this routine pointer is set to nullptr. When this routine is // specified, it is called when ever the parser detects an invalid data // stream. All invalid data is passed to this routine and the parser // goes back to scanning for the first preamble byte. -typedef void (*SEMP_INVALID_DATA_CALLBACK)(P_SEMP_PARSE_STATE parse); +typedef void (*SEMP_INVALID_DATA_CALLBACK)(const uint8_t * buffer, size_t length); // Parse routine // @@ -101,7 +103,8 @@ typedef const struct _SEMP_PARSER_DESCRIPTION { const char * parserName; // Name of the parser SEMP_PARSE_ROUTINE preamble; // Routine to handle the preamble - size_t scratchPadBytes; // Required scratch pad size + size_t scratchPadBytes; // Required scratch pad size + size_t payloadOffset; // Offset to the first byte of the payload } SEMP_PARSER_DESCRIPTION; // Maintain the operating state of one or more parsers processing a raw @@ -113,6 +116,7 @@ typedef struct _SEMP_PARSE_STATE SEMP_EOM_CALLBACK eomCallback; // End of message callback routine SEMP_BAD_CRC_CALLBACK badCrc; // Bad CRC callback routine SEMP_COMPUTE_CRC computeCrc; // Routine to compute the CRC when set + SEMP_INVALID_DATA_CALLBACK invalidData; // Invalid data callback const char *parserName; // Name of parser table void *scratchPad; // Parser scratchpad area Print *printError; // Class to use for error output @@ -120,9 +124,9 @@ typedef struct _SEMP_PARSE_STATE bool verboseDebug; // Verbose debug output (default: false) uint32_t crc; // RTCM computed CRC uint8_t *buffer; // Buffer containing the message - uint32_t bufferLength; // Length of the buffer in bytes + size_t bufferLength; // Length of the buffer in bytes uint16_t parserCount; // Number of parsers - uint16_t length; // Message length including line termination + size_t length; // Message length including line termination uint16_t type; // Active parser type, a value of // parserCount means searching for preamble bool nmeaAbortOnNonPrintable; // Abort NMEA parsing on the arrival of a non-printable char @@ -154,18 +158,12 @@ typedef struct _SEMP_UNICORE_HEADER uint16_t outputDelayMSec; // Output delay time, ms } SEMP_UNICORE_HEADER; -//---------------------------------------- -// Support routines -//---------------------------------------- - -int sempAsciiToNibble(int data); - -//---------------------------------------- -// Public routines - Called by the application +//------------------------------------------------------------------------------ +// SparkFun Extensible Message Parser API routines - Called by the applications // -// The public routines are used to locate and release the parse data -// structure and to pass data bytes to the parser. -//---------------------------------------- +// The general API routines are used to locate and release the parse data +// structure, pass data bytes to the parser and control output. +//------------------------------------------------------------------------------ // Initialize the parser // @@ -243,37 +241,6 @@ void sempDisableDebugOutput(SEMP_PARSE_STATE *parse); // parse: Address of a SEMP_PARSE_STATE structure void sempDisableErrorOutput(SEMP_PARSE_STATE *parse); -// Enable debug output -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// print: Address of a Print object to use for output -void sempEnableDebugOutput(SEMP_PARSE_STATE *parse, Print *print = &Serial, bool verbose = false); - -// Enable error output -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// print: Address of a Print object to use for output -void sempEnableErrorOutput(SEMP_PARSE_STATE *parse, Print *print = &Serial); - -// Only parsers should call the routine sempFirstByte when an unexpected -// byte is found in the data stream. Parsers will also set the state -// value to sempFirstByte after successfully parsing a message. The -// sempFirstByte routine calls each of the parsers' preamble routine to -// determine if the parser recognizes the data byte as the preamble for -// a message. The first parser to acknowledge the preamble byte by -// returning true is the parser that gets called for the following data. -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse -// -// Outputs: -// Returns true if a parser was found to process this data and false -// when none of the parsers recgonize the input data -bool sempFirstByte(SEMP_PARSE_STATE *parse, uint8_t data); - // Compute the necessary buffer length in bytes to support the scratch pad // and parse buffer lengths. // @@ -325,56 +292,301 @@ void sempParseNextByte(SEMP_PARSE_STATE *parse, uint8_t data); // data: Address of a buffr containing the next data bytes in the // stream of data to parse // len: Number of data bytes to parse -void sempParseNextBytes(SEMP_PARSE_STATE *parse, uint8_t *data, uint16_t len); +void sempParseNextBytes(SEMP_PARSE_STATE *parse, + const uint8_t *data, + size_t len); -// Print the contents of the parser data structure +// Set the invalid data callback +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE * structure +// invalidDataCallback: Routine to call to handle invalid data +void sempSetInvalidDataCallback(SEMP_PARSE_STATE *parse, + SEMP_INVALID_DATA_CALLBACK invalidDataCallback); + +// The routine sempStopParser frees the parse data structure and sets +// the pointer value to nullptr to prevent future references to the +// structure. +// +// Inputs: +// parse: Address of a SEMP_PARSE_STATE * structure +void sempStopParser(SEMP_PARSE_STATE **parse); + +//------------------------------------------------------------------------------ +// Payload access routines - Called by the applications +// +// The payload access routines are used by the application to extract +// values from the payload. +//------------------------------------------------------------------------------ + +// Get a 32-bit floating point value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the floating point value +float sempGetF4(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get a 32-bit floating point value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the floating point value +float sempGetF4NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get a 64-bit floating point (double) value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the floating point value +double sempGetF8(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get a 64-bit floating point (double) value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the floating point value +double sempGetF8NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 8-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int8_t sempGetI1(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 8-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int8_t sempGetI1NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 16-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int16_t sempGetI2(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 16-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int16_t sempGetI2NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 32-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int32_t sempGetI4(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 32-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int32_t sempGetI4NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 64-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int64_t sempGetI8(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 64-bit integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the integer value +int64_t sempGetI8NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get a zero terminated string address +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the address of the string +const char * sempGetString(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get a zero terminated string address +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the address of the string +const char * sempGetStringNoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 8-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint8_t sempGetU1(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 8-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint8_t sempGetU1NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 16-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint16_t sempGetU2(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 16-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint16_t sempGetU2NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 32-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint32_t sempGetU4(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 32-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint32_t sempGetU4NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 64-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint64_t sempGetU8(const SEMP_PARSE_STATE *parse, size_t offset); + +// Get an 64-bit unsigned integer value +// Inputs: +// parseTable: Address of an array of SEMP_PARSER_DESCRIPTION addresses +// offset: Offsets from the packetNumber of entries in the parseTable +// +// Outputs: +// Returns the unsigned integer value +uint64_t sempGetU8NoOffset(const SEMP_PARSE_STATE *parse, size_t offset); + +//------------------------------------------------------------------------------ +// SparkFun Extensible Message Parser API routines - Called by parsers +// +// These API routines should only be called by parsers when processing +// the incoming data stream +//------------------------------------------------------------------------------ + +// Only parsers should call the routine sempFirstByte when an unexpected +// byte is found in the data stream. Parsers will also set the state +// value to sempFirstByte after successfully parsing a message. The +// sempFirstByte routine calls each of the parsers' preamble routine to +// determine if the parser recognizes the data byte as the preamble for +// a message. The first parser to acknowledge the preamble byte by +// returning true is the parser that gets called for the following data. // // Inputs: // parse: Address of a SEMP_PARSE_STATE structure -// print: Address of a Print object to use for output -void sempPrintParserConfiguration(SEMP_PARSE_STATE *parse, Print *print = &Serial); +// data: First data byte in the stream of data to parse +// +// Outputs: +// Returns true if a parser was found to process this data and false +// when none of the parsers recgonize the input data +bool sempFirstByte(SEMP_PARSE_STATE *parse, uint8_t data); -// Format and print a line of text +// Perform the invalid data callback // // Inputs: -// print: Address of a Print object to use for output -// format: Address of a zero terminated string of format characters -// ...: Parameters needed for the format -void sempPrintf(Print *print, const char *format, ...); +// parse: Address of a SEMP_PARSE_STATE structure +void sempInvalidDataCallback(SEMP_PARSE_STATE *parse); -// Print a line of text +//------------------------------------------------------------------------------ +// Print support routines - Called by parsers and some applications +// +// The print support routines are used to output debug and error messages. +//------------------------------------------------------------------------------ + +// Convert an ASCII character (0-9, A-F, or a-f) into a 4-bit binary value // // Inputs: -// print: Address of a Print object to use for output -// string: Address of a zero terminated string of characters to output -void sempPrintln(Print *print, const char *string = ""); +// data: An ASCII character (0-9, A-F, or a-f) +// +// Outputs: +// If successful, returns the 4-bit binary value matching the character +// or -1 upon failure for invalid characters +int sempAsciiToNibble(int data); -// The routine sempStopParser frees the parse data structure and sets -// the pointer value to nullptr to prevent future references to the -// structure. +// Print the contents of the parser data structure // // Inputs: -// parse: Address of a SEMP_PARSE_STATE * structure -void sempStopParser(SEMP_PARSE_STATE **parse); +// parse: Address of a SEMP_PARSE_STATE structure +// print: Address of a Print object to use for output +void sempPrintParserConfiguration(SEMP_PARSE_STATE *parse, Print *print = &Serial); -//---------------------------------------- -// Parsers -//---------------------------------------- +//------------------------------------------------------------------------------ +// Parser notes +//------------------------------------------------------------------------------ -// The parser routines within a parser module are typically placed in -// reverse order within the module. This lets the routine declaration -// proceed the routine use and eliminates the need for forward declaration. -// Removing the forward declaration helps reduce the exposure of the -// routines to the application layer. As such only the preamble routine -// should need to be listed below. +// The parser routines are placed in reverse order to define the routine before +// its use and eliminate forward declarations. Removing the forward declaration +// helps reduce the exposure of the routines to the application layer. Typically +// only the parser description is made public. -//---------------------------------------- +//------------------------------------------------------------------------------ // NMEA -//---------------------------------------- +//------------------------------------------------------------------------------ // Length of the sentence name array #define SEMP_NMEA_SENTENCE_NAME_BYTES 16 +// Data structure to list in the parserTable passed to sempBeginParser extern SEMP_PARSER_DESCRIPTION sempNmeaParserDescription; // Abort NMEA on a non-printable char @@ -402,44 +614,24 @@ const char * sempNmeaGetSentenceName(const SEMP_PARSE_STATE *parse); // Returns the address of a zero terminated state name string const char * sempNmeaGetStateName(const SEMP_PARSE_STATE *parse); -// Start the parser by processing the first byte of data -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse -// -// Outputs: -// Returns true if the NMEA parser recgonizes the input and false -// another parser should be used -bool sempNmeaPreamble(SEMP_PARSE_STATE *parse, uint8_t data); - -//---------------------------------------- +//------------------------------------------------------------------------------ // RTCM -//---------------------------------------- +//------------------------------------------------------------------------------ +// Data structure to list in the parserTable passed to sempBeginParser extern SEMP_PARSER_DESCRIPTION sempRtcmParserDescription; // RTCM parse routines uint16_t sempRtcmGetMessageNumber(const SEMP_PARSE_STATE *parse); -int64_t sempRtcmGetSignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, uint16_t width); +int64_t sempRtcmGetSignedBits(const SEMP_PARSE_STATE *parse, size_t start, size_t width); const char * sempRtcmGetStateName(const SEMP_PARSE_STATE *parse); -uint64_t sempRtcmGetUnsignedBits(const SEMP_PARSE_STATE *parse, uint16_t start, uint16_t width); - -// Start the parser by processing the first byte of data -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse -// -// Outputs: -// Returns true if the RTCM parser recgonizes the input and false -// another parser should be used -bool sempRtcmPreamble(SEMP_PARSE_STATE *parse, uint8_t data); +uint64_t sempRtcmGetUnsignedBits(const SEMP_PARSE_STATE *parse, size_t start, size_t width); -//---------------------------------------- +//------------------------------------------------------------------------------ // SBF -//---------------------------------------- +//------------------------------------------------------------------------------ +// Data structure to list in the parserTable passed to sempBeginParser extern SEMP_PARSER_DESCRIPTION sempSbfParserDescription; // SBF parse routines @@ -447,12 +639,6 @@ uint16_t sempSbfGetBlockNumber(const SEMP_PARSE_STATE *parse); uint8_t sempSbfGetBlockRevision(const SEMP_PARSE_STATE *parse); const uint8_t *sempSbfGetEncapsulatedPayload(const SEMP_PARSE_STATE *parse); uint16_t sempSbfGetEncapsulatedPayloadLength(const SEMP_PARSE_STATE *parse); -float sempSbfGetF4(const SEMP_PARSE_STATE *parse, uint16_t offset); -double sempSbfGetF8(const SEMP_PARSE_STATE *parse, uint16_t offset); -int8_t sempSbfGetI1(const SEMP_PARSE_STATE *parse, uint16_t offset); -int16_t sempSbfGetI2(const SEMP_PARSE_STATE *parse, uint16_t offset); -int32_t sempSbfGetI4(const SEMP_PARSE_STATE *parse, uint16_t offset); -int64_t sempSbfGetI8(const SEMP_PARSE_STATE *parse, uint16_t offset); // Get the ID value // @@ -463,41 +649,29 @@ int64_t sempSbfGetI8(const SEMP_PARSE_STATE *parse, uint16_t offset); // Returns the ID value uint16_t sempSbfGetId(const SEMP_PARSE_STATE *parse); const char * sempSbfGetStateName(const SEMP_PARSE_STATE *parse); -const char *sempSbfGetString(const SEMP_PARSE_STATE *parse, uint16_t offset); -uint8_t sempSbfGetU1(const SEMP_PARSE_STATE *parse, uint16_t offset); -uint16_t sempSbfGetU2(const SEMP_PARSE_STATE *parse, uint16_t offset); -uint32_t sempSbfGetU4(const SEMP_PARSE_STATE *parse, uint16_t offset); -uint64_t sempSbfGetU8(const SEMP_PARSE_STATE *parse, uint16_t offset); bool sempSbfIsEncapsulatedNMEA(const SEMP_PARSE_STATE *parse); bool sempSbfIsEncapsulatedRTCMv3(const SEMP_PARSE_STATE *parse); -// Start the parser by processing the first byte of data -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse -// -// Outputs: -// Returns true if the SBF parser recgonizes the input and false -// another parser should be used -bool sempSbfPreamble(SEMP_PARSE_STATE *parse, uint8_t data); -void sempSbfSetInvalidDataCallback(const SEMP_PARSE_STATE *parse, SEMP_INVALID_DATA_CALLBACK invalidDataCallback); - -//---------------------------------------- +// Deprecated duplicate routines +#define sempSbfGetF4 sempGetF4 +#define sempSbfGetF8 sempGetF8 +#define sempSbfGetI1 sempGetI1 +#define sempSbfGetI2 sempGetI2 +#define sempSbfGetI4 sempGetI4 +#define sempSbfGetI8 sempGetI8 +#define sempSbfGetString sempGetString +#define sempSbfGetU1 sempGetU1 +#define sempSbfGetU2 sempGetU2 +#define sempSbfGetU4 sempGetU4 +#define sempSbfGetU8 sempGetU8 + +//------------------------------------------------------------------------------ // SPARTN -//---------------------------------------- +//------------------------------------------------------------------------------ +// Data structure to list in the parserTable passed to sempBeginParser extern SEMP_PARSER_DESCRIPTION sempSpartnParserDescription; -// Get the message number -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// -// Outputs: -// Returns the message type number -uint8_t sempSpartnGetMessageType(const SEMP_PARSE_STATE *parse); - // Get the message subtype number // // Inputs: @@ -507,94 +681,66 @@ uint8_t sempSpartnGetMessageType(const SEMP_PARSE_STATE *parse); // Returns the message subtype number uint8_t sempSpartnGetMessageSubType(const SEMP_PARSE_STATE *parse); -// Translates state value into an string, returns nullptr if not found +// Get the message number // // Inputs: // parse: Address of a SEMP_PARSE_STATE structure // // Outputs: -// Returns a zero terminated string of characters -const char * sempSpartnGetStateName(const SEMP_PARSE_STATE *parse); +// Returns the message type number +uint8_t sempSpartnGetMessageType(const SEMP_PARSE_STATE *parse); -// Start the parser by processing the first byte of data +// Translates state value into an string, returns nullptr if not found // // Inputs: // parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse // // Outputs: -// Returns true if the SPARTN parser recgonizes the input and false -// another parser should be used -bool sempSpartnPreamble(SEMP_PARSE_STATE *parse, uint8_t data); +// Returns a zero terminated string of characters +const char * sempSpartnGetStateName(const SEMP_PARSE_STATE *parse); -//---------------------------------------- +//------------------------------------------------------------------------------ // u-blox -//---------------------------------------- +//------------------------------------------------------------------------------ +// Data structure to list in the parserTable passed to sempBeginParser extern SEMP_PARSER_DESCRIPTION sempUbloxParserDescription; -// Get the 8-bit integer from the offset -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// offset: Offset from the message header -// -// Outputs: -// Returns the integer value -int8_t sempUbloxGetI1(const SEMP_PARSE_STATE *parse, uint16_t offset); -int16_t sempUbloxGetI2(const SEMP_PARSE_STATE *parse, uint16_t offset); -int32_t sempUbloxGetI4(const SEMP_PARSE_STATE *parse, uint16_t offset); -int64_t sempUbloxGetI8(const SEMP_PARSE_STATE *parse, uint16_t offset); uint8_t sempUbloxGetMessageClass(const SEMP_PARSE_STATE *parse); uint8_t sempUbloxGetMessageId(const SEMP_PARSE_STATE *parse); uint16_t sempUbloxGetMessageNumber(const SEMP_PARSE_STATE *parse); // |- Class (8 bits) -||- ID (8 bits) -| -uint16_t sempUbloxGetPayloadLength(const SEMP_PARSE_STATE *parse); -float sempUbloxGetR4(const SEMP_PARSE_STATE *parse, uint16_t offset); -double sempUbloxGetR8(const SEMP_PARSE_STATE *parse, uint16_t offset); +size_t sempUbloxGetPayloadLength(const SEMP_PARSE_STATE *parse); const char * sempUbloxGetStateName(const SEMP_PARSE_STATE *parse); -const char *sempUbloxGetString(const SEMP_PARSE_STATE *parse, uint16_t offset); -uint8_t sempUbloxGetU1(const SEMP_PARSE_STATE *parse, uint16_t offset); // offset is the Payload offset -uint16_t sempUbloxGetU2(const SEMP_PARSE_STATE *parse, uint16_t offset); -uint32_t sempUbloxGetU4(const SEMP_PARSE_STATE *parse, uint16_t offset); -uint64_t sempUbloxGetU8(const SEMP_PARSE_STATE *parse, uint16_t offset); -// Start the parser by processing the first byte of data -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse -// -// Outputs: -// Returns true if the UBLOX parser recgonizes the input and false -// another parser should be used -bool sempUbloxPreamble(SEMP_PARSE_STATE *parse, uint8_t data); - -//---------------------------------------- +// Deprecated duplicate routines +#define sempUbloxGetI1 sempGetI1 +#define sempUbloxGetI2 sempGetI2 +#define sempUbloxGetI4 sempGetI4 +#define sempUbloxGetI8 sempGetI8 +#define sempUbloxGetR4 sempGetF4 +#define sempUbloxGetR8 sempGetF8 +#define sempUbloxGetString sempGetString +#define sempUbloxGetU1 sempGetU1 +#define sempUbloxGetU2 sempGetU2 +#define sempUbloxGetU4 sempGetU4 +#define sempUbloxGetU8 sempGetU8 + +//------------------------------------------------------------------------------ // Unicore Binary -//---------------------------------------- +//------------------------------------------------------------------------------ +// Data structure to list in the parserTable passed to sempBeginParser extern SEMP_PARSER_DESCRIPTION sempUnicoreBinaryParserDescription; // Unicore binary parse routines const char * sempUnicoreBinaryGetStateName(const SEMP_PARSE_STATE *parse); - -// Start the parser by processing the first byte of data -// -// Inputs: -// parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse -// -// Outputs: -// Returns true if the Unicore Binary parser recgonizes the input and -// false another parser should be used -bool sempUnicoreBinaryPreamble(SEMP_PARSE_STATE *parse, uint8_t data); - void sempUnicoreBinaryPrintHeader(SEMP_PARSE_STATE *parse); -//---------------------------------------- +//------------------------------------------------------------------------------ // Unicore Hash (#) -//---------------------------------------- +//------------------------------------------------------------------------------ +// Data structure to list in the parserTable passed to sempBeginParser extern SEMP_PARSER_DESCRIPTION sempUnicoreHashParserDescription; // Abort Unicore hash on a non-printable char @@ -605,20 +751,49 @@ extern SEMP_PARSER_DESCRIPTION sempUnicoreHashParserDescription; void sempUnicoreHashAbortOnNonPrintable(SEMP_PARSE_STATE *parse, bool abort = true); // Unicore hash (#) parse routines +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// +// Outputs: +// Returns the address of a zero terminated sentence name string const char * sempUnicoreHashGetSentenceName(const SEMP_PARSE_STATE *parse); + const char * sempUnicoreHashGetStateName(const SEMP_PARSE_STATE *parse); -// Start the parser by processing the first byte of data +//------------------------------------------------------------------------------ +// V1 routines to be eliminated +//------------------------------------------------------------------------------ + +// Enable debug output // // Inputs: // parse: Address of a SEMP_PARSE_STATE structure -// data: First data byte in the stream of data to parse +// print: Address of a Print object to use for output +void sempEnableDebugOutput(SEMP_PARSE_STATE *parse, + Print *print = &Serial, + bool verbose = false); + +// Enable error output // -// Outputs: -// Returns true if the Unicore Hash parser recgonizes the input and -// false another parser should be used -bool sempUnicoreHashPreamble(SEMP_PARSE_STATE *parse, uint8_t data); +// Inputs: +// parse: Address of a SEMP_PARSE_STATE structure +// print: Address of a Print object to use for output +void sempEnableErrorOutput(SEMP_PARSE_STATE *parse, + Print *print = &Serial); -void sempUnicoreHashPrintHeader(SEMP_PARSE_STATE *parse); +// Format and print a line of text +// +// Inputs: +// print: Address of a Print object to use for output +// format: Address of a zero terminated string of format characters +// ...: Parameters needed for the format +void sempPrintf(Print *print, const char *format, ...); + +// Print a line of text +// +// Inputs: +// print: Address of a Print object to use for output +// string: Address of a zero terminated string of characters to output +void sempPrintln(Print *print, const char *string = ""); #endif // __SPARKFUN_EXTENSIBLE_MESSAGE_PARSER_H__ diff --git a/src/semp_crc_sbf.h b/src/semp_crc_sbf.h index 9ff8b77..75a295a 100644 --- a/src/semp_crc_sbf.h +++ b/src/semp_crc_sbf.h @@ -59,10 +59,20 @@ static const uint16_t semp_ccitt_crc_table[256] = { 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; +//---------------------------------------- +// Compute the CRC one byte at a time +// +// Inputs: +// crc: Current CRC value +// data: Byte to encode into the CRC value +// +// Outputs: +// Returns the updated CRC value +//---------------------------------------- uint16_t semp_ccitt_crc_update(uint16_t crc, const uint8_t data) { uint8_t tbl_idx = ((crc >> 8) ^ data) & 0xff; - + crc = (semp_ccitt_crc_table[tbl_idx]) ^ (crc << 8); return crc; diff --git a/src/semp_crc_spartn.h b/src/semp_crc_spartn.h index 8feda43..1fb56cb 100644 --- a/src/semp_crc_spartn.h +++ b/src/semp_crc_spartn.h @@ -183,6 +183,16 @@ const uint32_t semp_u32Crc32Table[] = { // Support for SPARTN parsing // Mostly stolen from https://github.com/u-blox/ubxlib/blob/master/common/spartn/src/u_spartn_crc.c +//---------------------------------------- +// Compute the CRC value +// +// Inputs: +// pU8Msg: Address of the beginning of the message +// size: Number of bytes in the message +// +// Outputs: +// Returns the CRC value associated with the message +//---------------------------------------- uint8_t semp_uSpartnCrc4(const uint8_t *pU8Msg, size_t size) { // Initialize local variables @@ -200,6 +210,16 @@ uint8_t semp_uSpartnCrc4(const uint8_t *pU8Msg, size_t size) return u8Remainder; } +//---------------------------------------- +// Compute the CRC value +// +// Inputs: +// pU8Msg: Address of the beginning of the message +// size: Number of bytes in the message +// +// Outputs: +// Returns the CRC value associated with the message +//---------------------------------------- uint8_t semp_uSpartnCrc8(const uint8_t *pU8Msg, size_t size) { // Initialize local variables @@ -217,6 +237,16 @@ uint8_t semp_uSpartnCrc8(const uint8_t *pU8Msg, size_t size) return u8Remainder; } +//---------------------------------------- +// Compute the CRC value +// +// Inputs: +// pU8Msg: Address of the beginning of the message +// size: Number of bytes in the message +// +// Outputs: +// Returns the CRC value associated with the message +//---------------------------------------- uint16_t semp_uSpartnCrc16(const uint8_t *pU8Msg, size_t size) { // Initialize local variables @@ -235,6 +265,16 @@ uint16_t semp_uSpartnCrc16(const uint8_t *pU8Msg, size_t size) return u16Remainder; } +//---------------------------------------- +// Compute the CRC value +// +// Inputs: +// pU8Msg: Address of the beginning of the message +// size: Number of bytes in the message +// +// Outputs: +// Returns the CRC value associated with the message +//---------------------------------------- uint32_t semp_uSpartnCrc24(const uint8_t *pU8Msg, size_t size) { // Initialize local variables @@ -254,6 +294,16 @@ uint32_t semp_uSpartnCrc24(const uint8_t *pU8Msg, size_t size) return u32Remainder; } +//---------------------------------------- +// Compute the CRC value +// +// Inputs: +// pU8Msg: Address of the beginning of the message +// size: Number of bytes in the message +// +// Outputs: +// Returns the CRC value associated with the message +//---------------------------------------- uint32_t semp_uSpartnCrc32(const uint8_t *pU8Msg, size_t size) { // Initialize local variables