// SPDX-License-Identifier: Apache-2.0 // Copyright (c) 2012-2024 Scott Penrose and WII5 Buoy contributors // // This file is part of WII5 Buoy firmware. // See LICENSE for full terms. /** * @file WII5Sh3dConsole.h * @brief Console abstraction: routes printf/log to one or more serial streams. */ #ifndef WII5Sh3dConsole_h #define WII5Sh3dConsole_h #include #include #include #include // TODO Default serial port? #define SerialConsole Serial // Enable debug flush, if you want regular flushing for crashes etc #define CONSOLE_DEBUG // #define CONSOLE_DEBUG_FLUSH #define CONSOLE_STREAMS 2 // TODO - Can we allocate more if needed? // Enable to allow Strings in the print lines instead of F() and "" // #define CONSOLE_USE_STRINGS // Default levels #define LOG_NONE -1 #define LOG_FATAL 0 #define LOG_ERROR 1 #define LOG_WARN 2 #define LOG_INFO 3 #define LOG_DEBUG 4 #define LOG_ALL 5 #define CONSOLE_DIRECTION_NONE 0 #define CONSOLE_DIRECTION_INPUT 1 #define CONSOLE_DIRECTION_OUTPUT 2 #define CONSOLE_DIRECTION_BOTH 3 // Default sizex - overloaded in begin #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) #define CONSOLE_BUFFER_SIZE 100 #define CONSOLE_COMMAND_SIZE 25 #define VSNPRINTF vsnprintf_P #elif defined(CORE_TEENSY) #define CONSOLE_BUFFER_SIZE 200 #define CONSOLE_COMMAND_SIZE 75 #define VSNPRINTF vsnprintf #else #define CONSOLE_BUFFER_SIZE 100 #define CONSOLE_COMMAND_SIZE 25 #define VSNPRINTF vsnprintf_P #endif #define CONSOLE_TYPE_NMEA_ID 1 #define CONSOLE_TYPE_NMEA_C0 '$' #define CONSOLE_TYPE_NMEA_NAME "NMEA" #define CONSOLE_TYPE_CSV_ID 2 #define CONSOLE_TYPE_CSV_C0 '@' #define CONSOLE_TYPE_CSV_NAME "CSV" #define CONSOLE_TYPE_CSV_LEN 10 #define CONSOLE_TYPE_BIN_ID 3 #define CONSOLE_TYPE_BIN_C0 '!' #define CONSOLE_TYPE_BIN_NAME "Binary" #define CONSOLE_TYPE_BINDATA_ID 4 #define CONSOLE_TYPE_BINDATA_NAME "BinaryData" #define CONSOLE_TYPE_IGNORE_ID 97 #define CONSOLE_TYPE_DESTROY_ID 98 #define CONSOLE_TYPE_OTHER_ID 99 #define CONSOLE_TYPE_OTHER_C0 '' #define CONSOLE_TYPE_OTHER_NAME "Standard" // NOTE: Make sure all of these are wrapped in F() for prints #define CONSOLE_PRE_INFO "# INFO: " #define CONSOLE_PRE_WARN "# WARN: " #define CONSOLE_PRE_ERROR "# ERROR: " #define CONSOLE_PRE_DEBUG "# DEBUG: " // Consider better name #define CONSOLE_PRE_FATAL "# IMPORTANT/NOTSTORED: " #define CONSOLE_POST_TIME " - " typedef void (*CallbackFunction) (uint8_t l, char *buf); // 10 seconds. Really 1 is enough, but slow type debug @ commands maybe #define CONSOLE_TIMEOUT 30000 /** * @brief Console abstraction over multiple Stream objects. * * Routes formatted output (printf, log) and parses incoming command frames * across one or more Stream sources. Recognises three frame syntaxes: * - NMEA ($-prefixed, CRC-checked) * - CSV (@-prefixed, comma-separated) * - Binary (!-prefixed, length+CRC framed) * * Most code in this firmware logs through `console` rather than calling * `Serial.print` directly so that output goes wherever the board is * configured for. */ class WII5Sh3dConsole { public: /** @brief One-time bring-up. Allocates internal print + command buffers. */ void begin(uint16_t bufSize = CONSOLE_BUFFER_SIZE, uint16_t cmdSize = CONSOLE_COMMAND_SIZE); /** @brief One-time bring-up using caller-supplied buffers. */ void begin(char* buf, uint16_t bufSize, char* cmdBuf, uint16_t cmdSize); /** @brief Add a Stream source to the console (typically a Serial port). */ bool add(Stream *s, uint8_t direction = CONSOLE_DIRECTION_BOTH); /** @brief Remove a previously-added Stream. */ bool remove (Stream *s); /** @brief Enable Binary frame parsing (caller must provide a buffer). */ bool enableBinary(); /** @brief Disable Binary frame parsing. */ bool disableBinary(); /** @brief Set the buffer used to receive Binary frames. */ void setBinaryBuffer(char *buf, uint16_t len); /** @brief Discard any in-flight command. */ void clearCommand(); /** @brief Remove the registered logging callback. */ void clearCallback(); /** @brief Register a callback invoked on log output. */ void setCallbackFunction(CallbackFunction f); /** @brief Use the default storage-callback (writes to the SD log). */ void setCallbackStorage(); /** @brief Only invoke the callback for messages at this level or above. */ void setCallbackLevel(uint8_t l); /** @brief Current callback log-level threshold. */ uint8_t getCallbackLevel(); /** @brief Manually fire the callback for a given level. */ void doCallback(uint8_t l); /** @brief Set the console log-level threshold. */ void setLevel (uint8_t in); /** @brief Current console log-level threshold. */ int8_t getLevel() { return level; } /** @brief Tick: read from streams, parse frames, run timeouts. */ void loop(); /** @brief Flush all output streams. */ void flush(); /** @brief True once a complete command frame has been received. */ bool available(); /** @brief First byte of the most recent "X;" standard-style command. */ byte getCommand() {return cmd;} /** @brief Numeric argument from the most recent "X;" command. */ uint32_t getVal() {return val;} /** @brief Raw command-buffer pointer. */ char* getBuffer() { return cmdBuffer; } /** @brief Feed a byte into the standard-style command parser. */ void processCmd(char in); /** @brief Finalize parsing of a CSV (@-prefixed) command frame. */ void processCsv(); /** @brief Number of CSV fields parsed in the current frame. */ uint8_t getCsvCount() {return csv_count;} /** @brief CSV field `n` interpreted as uint32. */ uint32_t getCsvVal(uint8_t n) {return csv_uint32[n];} /** @brief CSV field `n` as the original string. */ char* getCsvBuffer(uint8_t n) { return csv_str[n]; } /** @brief Enable date-time prefix on log output. */ void setDateTime() {displayDateTime = true;} /** @brief Disable date-time prefix on log output. */ void clearDateTime() {displayDateTime = false;} /** @brief Print a formatted date-time prefix (current time if t==0). */ void printDateTime(uint32_t t = 0); /** @brief Print a string to all output streams. */ void print(char *out); /** @brief printf to a single stream rather than all of them. */ void sprintf(Stream *s, const __FlashStringHelper *out, ... ); /** @brief printf to all output streams (RAM format string). */ void printf(char* out, ...); /** @brief printf to all output streams (PROGMEM format string). */ void printf(const __FlashStringHelper *out, ... ); #ifdef CONSOLE_USE_STRINGS void printf(String& out, ...); #endif /** @brief Emit the level-prefix string ("# INFO: " etc) for a level. */ void printPrefix(uint8_t level); /** @brief Log a message at level `l` (RAM format string). */ void log(uint8_t l, char* out, ...); /** @brief Log a message at level `l` (PROGMEM format string). */ void log(uint8_t l, const __FlashStringHelper *out, ... ); #ifdef CONSOLE_USE_STRINGS void log(uint8_t l, String& out, ...); #endif // Special handlers // void logReduceNumStr(uint8_t l, uint16 num, const __FlashStringHelper *out, ... ); // void logReduceNum(uint8_t l, uint16 num); void safeLog(uint8_t l, char* out, ...); void safeLog(uint8_t l, const __FlashStringHelper *out, ... ); #ifdef CONSOLE_USE_STRINGS void safeLog(uint8_t l, String& out, ...); #endif void nmeaSend(const __FlashStringHelper *out, ... ); void nmeaCRCSend(); void printNewLine(); char *printBuffer; // [CONSOLE_BUFFER_SIZE]; uint16_t bufferMax; bool availableBin() { return binAvailable; } char* getBinBuffer() { return binBuffer; } uint16_t getBinLength() { return binReceiveCount; } void printBinary(char* buf, uint16_t len); void printBits(void* buf, uint16_t len); WII5Sh3dConsole() { level = LOG_ALL; callback_level = LOG_NONE; } void setEcho(bool in); bool getEcho(); protected: bool echo; void internalErrorNotEnoughMemory(); void internalErrorNoBufferStream(); bool check(); // Timeouts for new lines for safety elapsedMillis waitInput; CallbackFunction callback; int8_t callback_level; bool callback_default; // Maximum number of streams? Stream* streams[CONSOLE_STREAMS]; uint8_t streamsDirection[CONSOLE_STREAMS]; uint8_t streamCount = 0; // Level of debugging ? int8_t level; bool displayDateTime; // TODO Storage? // func callback for storage ? // Command data uint8_t cmdType; uint8_t cmdCount; byte cmd; uint32_t val; // Big buffers - only created at start // Noite - moved public temporary - char *printBuffer; // [CONSOLE_BUFFER_SIZE]; char *cmdBuffer; // [CONSOLE_COMMAND_SIZE]; char *csv_str[CONSOLE_TYPE_CSV_LEN]; uint32_t csv_uint32[CONSOLE_TYPE_CSV_LEN]; uint8_t csv_count; // How many CSV fields do we have so far uint8_t csv_last; // What location in the buffer is it // Binary Data external buffers, counts and status bool binAvailable; char *binBuffer; uint16_t binMax; bool binEnable; uint16_t binReceiveCount; uint16_t binReceiveExpect; uint16_t binReceiveCRC; // Actual allocated memory // Note - moved public temporary - uint16_t bufferMax; uint16_t consoleMax; uint16_t i; }; // TODO Better name? extern WII5Sh3dConsole console; #endif