diff --git a/CommandHandler.h b/CommandHandler.h index 9cf147b..7071875 100644 --- a/CommandHandler.h +++ b/CommandHandler.h @@ -9,20 +9,20 @@ * one. It incorperates basic parameter checking (correct number only) and * returns a CommandHandlerReturn object which indicates how each call went. * - * See README.txt for more information. - * + * See README.txt for more information. + * * Space requirements: 6 bytes + 8 per command + buffer size */ - #pragma once +#pragma once #include -#include "compileTimeCRC32.h" -#include "Microprocessor_Debugging\debugging_disable.h" +#include "compileTimeCrc32.h" +#include "debugging_disable.h" -#define COMMAND_SIZE_MAX 150 // num chars to reserve in memory for buffer -#define EEPROM_SIZE_MAX 256 // Max space used in EEPROM +#define COMMAND_SIZE_MAX 150 // num chars to reserve in memory for buffer +#define EEPROM_SIZE_MAX 256 // Max space used in EEPROM // To disable EEPROM features, set this flag: // #define EEPROM_DISABLED @@ -31,23 +31,24 @@ // Storage locations in EEPROM for commands #include #define EEPROM_STORED_COMMAND_FLAG_LOCATION 0 -#define EEPROM_STORED_COMMAND_LOCATION EEPROM_STORED_COMMAND_FLAG_LOCATION + sizeof(bool) +#define EEPROM_STORED_COMMAND_LOCATION \ + EEPROM_STORED_COMMAND_FLAG_LOCATION + sizeof(bool) #endif // Error messages enum class CommandHandlerReturn { - NO_ERROR = 0, - COMMAND_NOT_FOUND, - WRONG_NUM_OF_PARAMS, - ERROR_PARSING_COMMAND, - EMPTY_COMMAND_STRING, - NO_COMMAND_WAITING, - MALLOC_ERROR, - OUT_OF_MEM, - BUFFER_FULL, - COMMAND_TOO_LONG, - EEPROM_FULL, - UNKNOWN_ERROR + NO_ERROR = 0, + COMMAND_NOT_FOUND, + WRONG_NUM_OF_PARAMS, + ERROR_PARSING_COMMAND, + EMPTY_COMMAND_STRING, + NO_COMMAND_WAITING, + MALLOC_ERROR, + OUT_OF_MEM, + BUFFER_FULL, + COMMAND_TOO_LONG, + EEPROM_FULL, + UNKNOWN_ERROR }; ////////////////////// PARAMETER LOOKUP ////////////////////// @@ -72,237 +73,218 @@ enum class CommandHandlerReturn { // command "HELO 1 2 3.3" : // // "HELO[0x00]1[0x00]2[0x00]3.3[0x00]" -// -// The private member char* _endOfString would point to the final NULL char. +// +// The private member char* _endOfString would point to the final NULL char. // // Thus pointers can be passed to the beginning of each required parameter and // they form valid c strings for other functions to use. class ParameterLookup { + public: + // Constuctor. + // Replace spaces in commandStr with NULL + // The buffer pointed to by commandStr must be at least COMMAND_SIZE_MAX in + // length + ParameterLookup(char* commandStr) + : _theCommand(commandStr), _stringHasNULLS(false) { + CONSOLE_LOG(F("ParameterLookup::Constuctor with command: ")); + CONSOLE_LOG_LN(commandStr); -public: - - // Constuctor. - // Replace spaces in commandStr with NULL - // The buffer pointed to by commandStr must be at least COMMAND_SIZE_MAX in length - ParameterLookup(char * commandStr) : - _theCommand(commandStr), _stringHasNULLS(false) - { - CONSOLE_LOG(F("ParameterLookup::Constuctor with command: ")); - CONSOLE_LOG_LN(commandStr); - - subSpacesForNULL(); - } - - // Get parameter indexed. Parameter 0 is the command itself - // Paremeter -1 returns the entire string - // Paremeter -2 returns all the parameters - // Requesting a non-existent parameter will return a NULL ptr - const char * operator [] (int idx) const - { - - if (idx == -1) { - // The user wants the whole string. - // OK, replace all the NULLs with spaces and then return a pointer - // to the start of the string - - if (_stringHasNULLS) - restoreSpaces(); - - return _theCommand; - - } else if (idx == -2) { - // The user wants all the parameters - // Find a pointer to the first param then restore all the spaces and return it - - char* ptr = getParamPtr(1); - - restoreSpaces(); - - return ptr; - - } else { - // The user wants a particular parameter. Ensure the string si setup correctly, - // then loop through to return a pointer to the start of the appropriate param - - if (!_stringHasNULLS) - subSpacesForNULL(); - - return getParamPtr(idx); - } - } - - // Number of stored params, including the command itself - unsigned int size() const { return _size; } - - // Dump the contents of _theCommand if in debug mode - void dump() const { - - CONSOLE_LOG_LN(F("ParameterLookup::dump")); - -#ifdef DEBUGGING_ENABLED - - if (!__serial_is_ready) return; - - Serial.print(F("*** loc (")); - Serial.print((int)_theCommand, DEC); - Serial.println(F(") ***")); - - for (int i = 0; i < COMMAND_SIZE_MAX; i++) { - Serial.print(i); - Serial.print('\t'); - } - - Serial.println(""); - - for (int i = 0; i < COMMAND_SIZE_MAX; i++) { - if (isprint(_theCommand[i])) - { - Serial.print(_theCommand[i]); - } - else { - Serial.print('['); - Serial.print((int)_theCommand[i]); - Serial.print(']'); - } - Serial.print('\t'); - } - - Serial.println(F("***")); - -#endif - } - -private: - - /** - * @brief Gets a pointer to the start of a given parameter - * - * This function requires that the command `_theCommand` is - * formatted with NULLs. If it isn't, this function will format - * it. - * - * @param[in] idx The parameter index - * - * @return The parameter pointer. - */ - char * getParamPtr(int idx) { - - if (!_stringHasNULLS) - subSpacesForNULL(); - - CONSOLE_LOG(F("ParameterLookup::Looking for param ")); - CONSOLE_LOG_LN(idx); - - char * paramPtr = _theCommand; - int count = idx; + subSpacesForNULL(); + } - // `_theCommand` ends at `_endOfString`, so don't go past this - while (paramPtr < _endOfString) { + // Get parameter indexed. Parameter 0 is the command itself + // Paremeter -1 returns the entire string + // Paremeter -2 returns all the parameters + // Requesting a non-existent parameter will return a NULL ptr + const char* operator[](int idx) const { + if (idx == -1) { + // The user wants the whole string. + // OK, replace all the NULLs with spaces and then return a pointer + // to the start of the string - if (count == 0 && *paramPtr) { - // We found a non null char after passing the required - // number of nulls, so return a pointer to it + if (_stringHasNULLS) restoreSpaces(); - CONSOLE_LOG(F("ParameterLookup::Returning ptr to pos ")); - CONSOLE_LOG(paramPtr - _theCommand); - CONSOLE_LOG(F(", containing command: ")); - CONSOLE_LOG_LN(paramPtr); + return _theCommand; - return paramPtr; - } + } else if (idx == -2) { + // The user wants all the parameters + // Find a pointer to the first param then restore all the spaces and + // return it - if ('\0' == *paramPtr && // We found a null... - // ... and the previous char wasn't a null - paramPtr > _theCommand && '\0' != *(paramPtr - 1)) { - // Decrement the count, as we've passed a delimiter - count--; + char* ptr = getParamPtr(1); - CONSOLE_LOG(F("ParameterLookup::Break found at pos ")); - CONSOLE_LOG_LN(paramPtr - _theCommand); - } + restoreSpaces(); - // Next char - paramPtr++; - } + return ptr; - // We hit the end of the string. Return a NULL pointer - return 0; - } + } else { + // The user wants a particular parameter. Ensure the string si setup + // correctly, then loop through to return a pointer to the start of the + // appropriate param + if (!_stringHasNULLS) subSpacesForNULL(); - // Loop through _theCommand counting params and subbing out - // spaces or tabs for NULLs - void subSpacesForNULL() { + return getParamPtr(idx); + } + } - char * loop = _theCommand; - _size = 1; + // Number of stored params, including the command itself + unsigned int size() const { return _size; } - while (*loop) { + // Dump the contents of _theCommand if in debug mode + void dump() const { + CONSOLE_LOG_LN(F("ParameterLookup::dump")); - if (' ' == *loop || '\t' == *loop) { - - CONSOLE_LOG(F("ParameterLookup::Replacing char '")); - CONSOLE_LOG(*loop); - CONSOLE_LOG(F("' at pos ")); - CONSOLE_LOG(loop - _theCommand); - CONSOLE_LOG_LN(F(" with \\0")); - - // Replace spaces with NULL chars - *loop = '\0'; - - // If the preceeding char wasn't also a space, - // increment the param count - if (loop > _theCommand && // Don't look too far back! - '\0' != *(loop - sizeof(char))) { - _size++; - } - } - - loop++; - } - - // We looped to the last char which is a NULL. - // Leave it as a NULL and store a pointer to it - _endOfString = loop; - - _stringHasNULLS = true; - - CONSOLE_LOG(F("ParameterLookup::_theCommand ends at pos ")); - CONSOLE_LOG_LN(_endOfString); - } - - // Undo the work done by subSpacesForNULL() - void restoreSpaces() { - - char * loop = _theCommand; - - while (loop < _endOfString) { +#ifdef DEBUGGING_ENABLED - if ('\0' == *loop) { + if (!__serial_is_ready) return; - CONSOLE_LOG(F("ParameterLookup::Replacing char at pos ")); - CONSOLE_LOG(loop - _theCommand); - CONSOLE_LOG_LN(F(" with \\s")); + Serial.print(F("*** loc (")); + Serial.print((int)_theCommand, DEC); + Serial.println(F(") ***")); - // Replace NULL with space - *loop = ' '; - } + for (int i = 0; i < COMMAND_SIZE_MAX; i++) { + Serial.print(i); + Serial.print('\t'); + } - loop++; - } + Serial.println(""); - // Mark this as done - _stringHasNULLS = false; - } + for (int i = 0; i < COMMAND_SIZE_MAX; i++) { + if (isprint(_theCommand[i])) { + Serial.print(_theCommand[i]); + } else { + Serial.print('['); + Serial.print((int)_theCommand[i]); + Serial.print(']'); + } + Serial.print('\t'); + } - // Pointer to the whole command - char * _theCommand; - - // Internal params - char * _endOfString; - bool _stringHasNULLS; - unsigned int _size; + Serial.println(F("***")); +#endif + } + + private: + /** + * @brief Gets a pointer to the start of a given parameter + * + * This function requires that the command `_theCommand` is + * formatted with NULLs. If it isn't, this function will format + * it. + * + * @param[in] idx The parameter index + * + * @return The parameter pointer. + */ + char* getParamPtr(int idx) { + if (!_stringHasNULLS) subSpacesForNULL(); + + CONSOLE_LOG(F("ParameterLookup::Looking for param ")); + CONSOLE_LOG_LN(idx); + + char* paramPtr = _theCommand; + int count = idx; + + // `_theCommand` ends at `_endOfString`, so don't go past this + while (paramPtr < _endOfString) { + if (count == 0 && *paramPtr) { + // We found a non null char after passing the required + // number of nulls, so return a pointer to it + + CONSOLE_LOG(F("ParameterLookup::Returning ptr to pos ")); + CONSOLE_LOG(paramPtr - _theCommand); + CONSOLE_LOG(F(", containing command: ")); + CONSOLE_LOG_LN(paramPtr); + + return paramPtr; + } + + if ('\0' == *paramPtr && // We found a null... + // ... and the previous char wasn't a null + paramPtr > _theCommand && '\0' != *(paramPtr - 1)) { + // Decrement the count, as we've passed a delimiter + count--; + + CONSOLE_LOG(F("ParameterLookup::Break found at pos ")); + CONSOLE_LOG_LN(paramPtr - _theCommand); + } + + // Next char + paramPtr++; + } + + // We hit the end of the string. Return a NULL pointer + return 0; + } + + // Loop through _theCommand counting params and subbing out + // spaces or tabs for NULLs + void subSpacesForNULL() { + char* loop = _theCommand; + _size = 1; + + while (*loop) { + if (' ' == *loop || '\t' == *loop) { + CONSOLE_LOG(F("ParameterLookup::Replacing char '")); + CONSOLE_LOG(*loop); + CONSOLE_LOG(F("' at pos ")); + CONSOLE_LOG(loop - _theCommand); + CONSOLE_LOG_LN(F(" with \\0")); + + // Replace spaces with NULL chars + *loop = '\0'; + + // If the preceeding char wasn't also a space, + // increment the param count + if (loop > _theCommand && // Don't look too far back! + '\0' != *(loop - sizeof(char))) { + _size++; + } + } + + loop++; + } + + // We looped to the last char which is a NULL. + // Leave it as a NULL and store a pointer to it + _endOfString = loop; + + _stringHasNULLS = true; + + CONSOLE_LOG(F("ParameterLookup::_theCommand ends at pos ")); + CONSOLE_LOG_LN(_endOfString); + } + + // Undo the work done by subSpacesForNULL() + void restoreSpaces() { + char* loop = _theCommand; + + while (loop < _endOfString) { + if ('\0' == *loop) { + CONSOLE_LOG(F("ParameterLookup::Replacing char at pos ")); + CONSOLE_LOG(loop - _theCommand); + CONSOLE_LOG_LN(F(" with \\s")); + + // Replace NULL with space + *loop = ' '; + } + + loop++; + } + + // Mark this as done + _stringHasNULLS = false; + } + + // Pointer to the whole command + char* _theCommand; + + // Internal params + char* _endOfString; + bool _stringHasNULLS; + unsigned int _size; }; // Template for the functions to be called in response to a command @@ -317,588 +299,555 @@ typedef void commandFunction(const ParameterLookup& params); // invoke the nested CommandLookup object in order to get the right command // and execute it // -// This object can hold commands where is defined at compile time, e.g. -// +// This object can hold commands where is defined at compile time, +// e.g. +// // "CommandHandler<10> handler; // -// This class also contains methods for storing commands in the EEPROM in +// This class also contains methods for storing commands in the EEPROM in // order to queue a command on device startup // These can be disabled by adding the line: // #define EEPROM_DISABLED -// The user must call `executeStartupCommands()` in their code once they are ready -// for EEPROM commands to be executed +// The user must call `executeStartupCommands()` in their code once they are +// ready for EEPROM commands to be executed template -class CommandHandler -{ - - // Forward declare the CommandLookup class -private: - class CommandLookup; - -public: - - // Constuctor - // Initialise private members - CommandHandler() : - _lookupList(), // Not needed, but just to be explicit - _command_too_long(false), - _bufferFull(false), - _bufferLength(0) - { - CONSOLE_LOG_LN(F("CommandHandler::CommandHandler()")); - - // Start the input buffer empty - _inputBuffer[0] = '\0'; - } - - // Execute the waiting command - CommandHandlerReturn executeCommand() - { - - CommandHandlerReturn error = CommandHandlerReturn::NO_ERROR; - - CONSOLE_LOG_LN(F("Execute command")); - - // Return error code if no command waiting - if (!commandWaiting()) { - CONSOLE_LOG_LN(F("No command error")); - error = CommandHandlerReturn::NO_COMMAND_WAITING; - } - - // Return error code if command over-ran - if (_command_too_long) { - CONSOLE_LOG_LN(F("Overflow error")); - error = CommandHandlerReturn::COMMAND_TOO_LONG; - } - - CONSOLE_LOG(F("Command is: ")); - CONSOLE_LOG_LN(_inputBuffer); - - // Return error code if string is empty - if (_bufferLength == 0 && error == CommandHandlerReturn::NO_ERROR) - { - CONSOLE_LOG_LN(F("Empty command error")); - error = CommandHandlerReturn::EMPTY_COMMAND_STRING; - } - - // If no errors so far, continue - if (error == CommandHandlerReturn::NO_ERROR) { - - // Constuct a parameter lookup object from the command string - // This invalidates the string for future use - CONSOLE_LOG_LN(F("Creating ParameterLookup object...")); - ParameterLookup lookupObj = ParameterLookup(_inputBuffer); - - CONSOLE_LOG_LN(F("Running callStoredCommand...")); - error = _lookupList.callStoredCommand(lookupObj); - - } - - // Mark buffer as ready again - clearBuffer(); - - return error; - } - - // Register a command - // This version is deprecated because it involves storing the strings in memory - // for its calling which defeats the point of hashes! - CommandHandlerReturn registerCommand(const char* command, int num_of_parameters, - commandFunction* pointer_to_function) __attribute__((deprecated)) { - - return _lookupList.registerCommand(command, num_of_parameters, - pointer_to_function); - } - - // Register a command - // Use this version instead. To calculate the hash, use COMMANDHANDLER_HASH(cmd) - // e.g. - // registerCommand(COMMANDHANDLER_HASH("*idn"), 0, &identityFunc); - CommandHandlerReturn registerCommand(uint32_t hash, int num_of_parameters, - commandFunction* pointer_to_function) { - - return _lookupList.registerCommand(hash, num_of_parameters, - pointer_to_function); - } - - // Add a char from the serial connection to be processed and added to the queue - // Returns BUFFER_FULL if buffer is full and char wasn't added - CommandHandlerReturn addCommandChar(const char c) - { - - // Check if the buffer is already full - if (_bufferFull) { - return CommandHandlerReturn::BUFFER_FULL; - } - - // If c is a newline, mark the buffer as full - if (c == '\n') { - - CONSOLE_LOG(F("Newline received. Command: ")); - CONSOLE_LOG_LN(_inputBuffer); - - // We are already null terminated so mark the string as ready - _bufferFull = true; - - // _command_too_long will be detected by executeCommand if it is set - } - // if c is a carridge return, ignore it - else if (c == '\r') { - CONSOLE_LOG_LN(F("Ignoring a \r")); - return CommandHandlerReturn::NO_ERROR; - } - // else c is a normal char, so add it to the buffer - else { - if (_command_too_long || _bufferLength >= COMMAND_SIZE_MAX-1) - { - // Command was too long! Set the `_command_too_long` flag to chuck away all subsequent chars until next newline - CONSOLE_LOG_LN(F("ERROR: command too long!")); - - _command_too_long = true; - - return CommandHandlerReturn::COMMAND_TOO_LONG; - } - else - { - // The normal case. Add the new char to the buffer - - _inputBuffer[_bufferLength] = c; - - _bufferLength++; - - // Ensure that the buffer always contains valid c str - _inputBuffer[_bufferLength] = '\0'; - - CONSOLE_LOG(F("Char received: '")); - CONSOLE_LOG(c); - CONSOLE_LOG(F("', Buffer length: ")); - CONSOLE_LOG_LN(_bufferLength); - - return CommandHandlerReturn::NO_ERROR; - } - } - - return CommandHandlerReturn::UNKNOWN_ERROR; // We should never get here - } - - // Check to see if the handler is ready for more incoming chars - inline bool bufferFull() { return _bufferFull; } - - // Is a command waiting? - inline bool commandWaiting() { return bufferFull(); } +class CommandHandler { + // Forward declare the CommandLookup class + private: + class CommandLookup; + + public: + // Constuctor + // Initialise private members + CommandHandler() + : _lookupList(), // Not needed, but just to be explicit + _command_too_long(false), + _bufferFull(false), + _bufferLength(0) { + CONSOLE_LOG_LN(F("CommandHandler::CommandHandler()")); + + // Start the input buffer empty + _inputBuffer[0] = '\0'; + } + + // Execute the waiting command + CommandHandlerReturn executeCommand() { + CommandHandlerReturn error = CommandHandlerReturn::NO_ERROR; + + CONSOLE_LOG_LN(F("Execute command")); + + // Return error code if no command waiting + if (!commandWaiting()) { + CONSOLE_LOG_LN(F("No command error")); + error = CommandHandlerReturn::NO_COMMAND_WAITING; + } + + // Return error code if command over-ran + if (_command_too_long) { + CONSOLE_LOG_LN(F("Overflow error")); + error = CommandHandlerReturn::COMMAND_TOO_LONG; + } + + CONSOLE_LOG(F("Command is: ")); + CONSOLE_LOG_LN(_inputBuffer); + + // Return error code if string is empty + if (_bufferLength == 0 && error == CommandHandlerReturn::NO_ERROR) { + CONSOLE_LOG_LN(F("Empty command error")); + error = CommandHandlerReturn::EMPTY_COMMAND_STRING; + } + + // If no errors so far, continue + if (error == CommandHandlerReturn::NO_ERROR) { + // Constuct a parameter lookup object from the command string + // This invalidates the string for future use + CONSOLE_LOG_LN(F("Creating ParameterLookup object...")); + ParameterLookup lookupObj = ParameterLookup(_inputBuffer); + + CONSOLE_LOG_LN(F("Running callStoredCommand...")); + error = _lookupList.callStoredCommand(lookupObj); + } + + // Mark buffer as ready again + clearBuffer(); + + return error; + } + + // Register a command + // This version is deprecated because it involves storing the strings in + // memory for its calling which defeats the point of hashes! + CommandHandlerReturn registerCommand(const char* command, + int num_of_parameters, + commandFunction* pointer_to_function) + __attribute__((deprecated)) { + return _lookupList.registerCommand(command, num_of_parameters, + pointer_to_function); + } + + // Register a command + // Use this version instead. To calculate the hash, use + // COMMANDHANDLER_HASH(cmd) e.g. + // registerCommand(COMMANDHANDLER_HASH("*idn"), 0, &identityFunc); + CommandHandlerReturn registerCommand(uint32_t hash, int num_of_parameters, + commandFunction* pointer_to_function) { + return _lookupList.registerCommand(hash, num_of_parameters, + pointer_to_function); + } + + // Add a char from the serial connection to be processed and added to the + // queue Returns BUFFER_FULL if buffer is full and char wasn't added + CommandHandlerReturn addCommandChar(const char c) { + // Check if the buffer is already full + if (_bufferFull) { + return CommandHandlerReturn::BUFFER_FULL; + } + + // If c is a newline, mark the buffer as full + if (c == '\n') { + CONSOLE_LOG(F("Newline received. Command: ")); + CONSOLE_LOG_LN(_inputBuffer); + + // We are already null terminated so mark the string as ready + _bufferFull = true; + + // _command_too_long will be detected by executeCommand if it is set + } + // if c is a carridge return, ignore it + else if (c == '\r') { + CONSOLE_LOG_LN(F("Ignoring a \r")); + return CommandHandlerReturn::NO_ERROR; + } + // else c is a normal char, so add it to the buffer + else { + if (_command_too_long || _bufferLength >= COMMAND_SIZE_MAX - 1) { + // Command was too long! Set the `_command_too_long` flag to chuck away + // all subsequent chars until next newline + CONSOLE_LOG_LN(F("ERROR: command too long!")); + + _command_too_long = true; + + return CommandHandlerReturn::COMMAND_TOO_LONG; + } else { + // The normal case. Add the new char to the buffer + + _inputBuffer[_bufferLength] = c; + + _bufferLength++; + + // Ensure that the buffer always contains valid c str + _inputBuffer[_bufferLength] = '\0'; + + CONSOLE_LOG(F("Char received: '")); + CONSOLE_LOG(c); + CONSOLE_LOG(F("', Buffer length: ")); + CONSOLE_LOG_LN(_bufferLength); + + return CommandHandlerReturn::NO_ERROR; + } + } + + return CommandHandlerReturn::UNKNOWN_ERROR; // We should never get here + } + + // Check to see if the handler is ready for more incoming chars + inline bool bufferFull() { return _bufferFull; } + + // Is a command waiting? + inline bool commandWaiting() { return bufferFull(); } #ifndef EEPROM_DISABLED - // Store a command to be executed on startup in the EEPROM - // This command should not include newlines: it will be copied verbatim into the - // buffer and then executed as a normal command would be - // Multiple commands can be seperated by ';' chars - // Max length is EEPROM_SIZE_MAX - 2 (1 char to append a newline, 1 for the null term) - // Returns false on fail - CommandHandlerReturn storeStartupCommand(const String& command) - { - // Call the c str version of this command - return storeStartupCommand(command.c_str()); - } - - // Store a command to be executed on startup in the EEPROM - // This command should not include newlines: it will be copied verbatim into the - // buffer and then executed as a normal command would be - // Multiple commands can be seperated by ';' chars - // Max length is EEPROM_SIZE_MAX - 2 (1 char to append a newline, 1 for the null term) - // Returns CommandHandlerReturn to indicate error status - CommandHandlerReturn storeStartupCommand(const char* command, bool append = false) - { - int commandIdx = 0; - int eeprom_ptr = 0; - - // If we're appending to the EEPROM command, find the end of the current one - if (append) { - while (eeprom_ptr < EEPROM_SIZE_MAX - 2) { - - if (EEPROM.read(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr) == '\0') { - break; - } - - eeprom_ptr++; - } - } - - const int availableSpace = EEPROM_SIZE_MAX - 2 - eeprom_ptr; - - if (strlen(command) > availableSpace) - return CommandHandlerReturn::EEPROM_FULL; - - // Store a flag indicating that a command exists - const bool trueFlag = true; - EEPROM.update(EEPROM_STORED_COMMAND_FLAG_LOCATION, trueFlag); - - // Loop through the command and store it - while (command[commandIdx] != '\0' && eeprom_ptr < EEPROM_SIZE_MAX - 2) { - - char toBeStored; - - // Check if it's a semicolon delimiter - if (command[commandIdx] != ';') { - // Nope. Just copy it - toBeStored = command[commandIdx]; - } - else { - // It was a semicolon, so store a newline - toBeStored = '\n'; - } - - // Store the char in EEPROM - CONSOLE_LOG(F("CommandHandler::Update EEPROM (")); - CONSOLE_LOG(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr); - CONSOLE_LOG(F("): [0x")); - CONSOLE_LOG(toBeStored, HEX); - CONSOLE_LOG_LN(']'); - - EEPROM.update(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr, toBeStored); - - // Increment string pointer and eeprom pointer - commandIdx++; - eeprom_ptr += sizeof(char); - } - - // Terminate with a newline and a null - CONSOLE_LOG(F("CommandHandler::Terminate EEPROM (")); - CONSOLE_LOG(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr); - CONSOLE_LOG('&'); - CONSOLE_LOG(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr + 1); - CONSOLE_LOG_LN(')'); - - EEPROM.update(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr, '\n'); - eeprom_ptr++; - EEPROM.update(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr, '\0'); - - return CommandHandlerReturn::NO_ERROR; - } - - // Remove any startup commands from the EEPROM - bool wipeStartupCommand() - { - // There is no need to wipe the command from the EEPROM: we simply set the flag to false - - // Store a flag indicating that no command exists - const bool falseFlag = false; - - EEPROM.update(EEPROM_STORED_COMMAND_FLAG_LOCATION, falseFlag); - - return true; - } - - // Return any stored startup command by copying into buf. - // buf must point to a buffer of at least EEPROM_SIZE_MAX chars - void getStartupCommand(char * buf) - { - CONSOLE_LOG_LN(F("CommandHandler::getStartupCommand(char*)")) - - // Index of location in buffer - int bufIdx = 0; // Start at start of buffer - - // There should be a bool stored in EEPROM_STORED_COMMAND_FLAG_LOCATION if this program has run before - // It will tell us if there's a command to be read or not - // Read it as a byte though, since the memory location will be 0xFF if it has never been written to - // We only want to use it if it's exactly a bool - char fromEEPROM; - EEPROM.get(EEPROM_STORED_COMMAND_FLAG_LOCATION, fromEEPROM); - - CONSOLE_LOG(F("CommandHandler::EEPROM flag contains : [0x")); - CONSOLE_LOG(fromEEPROM, HEX); - CONSOLE_LOG_LN(']'); - - if (fromEEPROM == (char)true) { - - CONSOLE_LOG_LN(F("CommandHandler::EEPROM flag true")); - - // Index of location in EEPROM - int EEPROM_idx = EEPROM_STORED_COMMAND_LOCATION; - - // At most, go to the end of the buffer - 1, to leave space for a null terminator - while (bufIdx < EEPROM_SIZE_MAX - 1) - { - char c; - EEPROM.get(EEPROM_idx, c); - - CONSOLE_LOG(F("CommandHandler::Read from EEPROM (")); - CONSOLE_LOG(EEPROM_idx); - CONSOLE_LOG(F("): [0x")); - CONSOLE_LOG(c, HEX); - CONSOLE_LOG_LN(']'); - - if (c == '\0') { - // Found the end - - CONSOLE_LOG(F("CommandHandler::Stored command ends at ")); - CONSOLE_LOG_LN(EEPROM_idx); - - break; - } - - // Store the char - buf[bufIdx] = c; - - EEPROM_idx++; - bufIdx++; - } - } - else if (fromEEPROM == (char)false) { - CONSOLE_LOG_LN(F("CommandHandler::EEPROM flag false")); - } - else { - CONSOLE_LOG_LN(F("CommandHandler::EEPROM flag undefined")); - } - - // Null terminate - buf[bufIdx] = '\0'; - } - - // Execute any startup commands stored in the EEPROM - CommandHandlerReturn executeStartupCommands() - { - - CONSOLE_LOG_LN(F("CommandHandler::executeStartupCommands")); - - // Is a command stored (see getStoredCommand for details)? - char fromEEPROM; - EEPROM.get(EEPROM_STORED_COMMAND_FLAG_LOCATION, fromEEPROM); - - // See if the flag is anything other than "true" - if (fromEEPROM != (char)true) { - CONSOLE_LOG_LN(F("CommandHandler::executeStartupCommands: No command stored")); - return CommandHandlerReturn::NO_COMMAND_WAITING; - } - - // Command is waiting, so queue it one char at a time - // If we reach a newline, commandWaiting() will flag "true": execute the command - // If we reach a NULL, end - // If we've read COMMAND_SIZE_MAX - 2 bytes from EEPROM, stop and add a newline + NULL - - // Index of location in EEPROM - int EEPROM_idx = EEPROM_STORED_COMMAND_LOCATION; - int numCharsRead = 0; - CommandHandlerReturn result = CommandHandlerReturn::NO_ERROR; - while (true) { - - char c; - - // If we've read the max possible number of chars, stop here - if (numCharsRead >= EEPROM_SIZE_MAX) { - - CONSOLE_LOG_LN(F("CommandHandler::Stored command unterminated!")); - - // Queue a newline then stop - addCommandChar('\n'); - break; - } - - EEPROM.get(EEPROM_idx, c); - - CONSOLE_LOG(F("CommandHandler::Read from EEPROM (")); - CONSOLE_LOG(EEPROM_idx); - CONSOLE_LOG(F("): [0x")); - CONSOLE_LOG(c, HEX); - CONSOLE_LOG_LN(']'); - - if (c == '\0') { - // Found the end - - CONSOLE_LOG(F("CommandHandler::Stored command ends at ")); - CONSOLE_LOG_LN(EEPROM_idx); - - break; - } - - // If no preceding commands have failed, execute if ready - if (CommandHandlerReturn::NO_ERROR == result) { - - CONSOLE_LOG(F("CommandHandler::executeStartupCommands: Queueing ")); - CONSOLE_LOG_LN(c); - - // Queue the char - addCommandChar(c); - - // If CommandHandler is ready to execute the queued command, do so - if (commandWaiting()) { - result = executeCommand(); - } - } - - EEPROM_idx++; - numCharsRead++; - } - - return result; - - } + // Store a command to be executed on startup in the EEPROM + // This command should not include newlines: it will be copied verbatim into + // the buffer and then executed as a normal command would be Multiple commands + // can be seperated by ';' chars Max length is EEPROM_SIZE_MAX - 2 (1 char to + // append a newline, 1 for the null term) Returns false on fail + CommandHandlerReturn storeStartupCommand(const String& command) { + // Call the c str version of this command + return storeStartupCommand(command.c_str()); + } + + // Store a command to be executed on startup in the EEPROM + // This command should not include newlines: it will be copied verbatim into + // the buffer and then executed as a normal command would be Multiple commands + // can be seperated by ';' chars Max length is EEPROM_SIZE_MAX - 2 (1 char to + // append a newline, 1 for the null term) Returns CommandHandlerReturn to + // indicate error status + CommandHandlerReturn storeStartupCommand(const char* command, + bool append = false) { + int commandIdx = 0; + int eeprom_ptr = 0; + + // If we're appending to the EEPROM command, find the end of the current one + if (append) { + while (eeprom_ptr < EEPROM_SIZE_MAX - 2) { + if (EEPROM.read(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr) == '\0') { + break; + } + + eeprom_ptr++; + } + } + + const int availableSpace = EEPROM_SIZE_MAX - 2 - eeprom_ptr; + + if (strlen(command) > availableSpace) + return CommandHandlerReturn::EEPROM_FULL; + + // Store a flag indicating that a command exists + const bool trueFlag = true; + EEPROM.update(EEPROM_STORED_COMMAND_FLAG_LOCATION, trueFlag); + + // Loop through the command and store it + while (command[commandIdx] != '\0' && eeprom_ptr < EEPROM_SIZE_MAX - 2) { + char toBeStored; + + // Check if it's a semicolon delimiter + if (command[commandIdx] != ';') { + // Nope. Just copy it + toBeStored = command[commandIdx]; + } else { + // It was a semicolon, so store a newline + toBeStored = '\n'; + } + + // Store the char in EEPROM + CONSOLE_LOG(F("CommandHandler::Update EEPROM (")); + CONSOLE_LOG(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr); + CONSOLE_LOG(F("): [0x")); + CONSOLE_LOG(toBeStored, HEX); + CONSOLE_LOG_LN(']'); + + EEPROM.update(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr, toBeStored); + + // Increment string pointer and eeprom pointer + commandIdx++; + eeprom_ptr += sizeof(char); + } + + // Terminate with a newline and a null + CONSOLE_LOG(F("CommandHandler::Terminate EEPROM (")); + CONSOLE_LOG(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr); + CONSOLE_LOG('&'); + CONSOLE_LOG(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr + 1); + CONSOLE_LOG_LN(')'); + + EEPROM.update(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr, '\n'); + eeprom_ptr++; + EEPROM.update(EEPROM_STORED_COMMAND_LOCATION + eeprom_ptr, '\0'); + + return CommandHandlerReturn::NO_ERROR; + } + + // Remove any startup commands from the EEPROM + bool wipeStartupCommand() { + // There is no need to wipe the command from the EEPROM: we simply set the + // flag to false + + // Store a flag indicating that no command exists + const bool falseFlag = false; + + EEPROM.update(EEPROM_STORED_COMMAND_FLAG_LOCATION, falseFlag); + + return true; + } + + // Return any stored startup command by copying into buf. + // buf must point to a buffer of at least EEPROM_SIZE_MAX chars + void getStartupCommand(char* buf) { + CONSOLE_LOG_LN(F("CommandHandler::getStartupCommand(char*)")) + + // Index of location in buffer + int bufIdx = 0; // Start at start of buffer + + // There should be a bool stored in EEPROM_STORED_COMMAND_FLAG_LOCATION if + // this program has run before It will tell us if there's a command to be + // read or not Read it as a byte though, since the memory location will be + // 0xFF if it has never been written to We only want to use it if it's + // exactly a bool + char fromEEPROM; + EEPROM.get(EEPROM_STORED_COMMAND_FLAG_LOCATION, fromEEPROM); + + CONSOLE_LOG(F("CommandHandler::EEPROM flag contains : [0x")); + CONSOLE_LOG(fromEEPROM, HEX); + CONSOLE_LOG_LN(']'); + + if (fromEEPROM == (char)true) { + CONSOLE_LOG_LN(F("CommandHandler::EEPROM flag true")); + + // Index of location in EEPROM + int EEPROM_idx = EEPROM_STORED_COMMAND_LOCATION; + + // At most, go to the end of the buffer - 1, to leave space for a null + // terminator + while (bufIdx < EEPROM_SIZE_MAX - 1) { + char c; + EEPROM.get(EEPROM_idx, c); + + CONSOLE_LOG(F("CommandHandler::Read from EEPROM (")); + CONSOLE_LOG(EEPROM_idx); + CONSOLE_LOG(F("): [0x")); + CONSOLE_LOG(c, HEX); + CONSOLE_LOG_LN(']'); + + if (c == '\0') { + // Found the end + + CONSOLE_LOG(F("CommandHandler::Stored command ends at ")); + CONSOLE_LOG_LN(EEPROM_idx); + + break; + } + + // Store the char + buf[bufIdx] = c; + + EEPROM_idx++; + bufIdx++; + } + } else if (fromEEPROM == (char)false) { + CONSOLE_LOG_LN(F("CommandHandler::EEPROM flag false")); + } else { + CONSOLE_LOG_LN(F("CommandHandler::EEPROM flag undefined")); + } + + // Null terminate + buf[bufIdx] = '\0'; + } + + // Execute any startup commands stored in the EEPROM + CommandHandlerReturn executeStartupCommands() { + CONSOLE_LOG_LN(F("CommandHandler::executeStartupCommands")); + + // Is a command stored (see getStoredCommand for details)? + char fromEEPROM; + EEPROM.get(EEPROM_STORED_COMMAND_FLAG_LOCATION, fromEEPROM); + + // See if the flag is anything other than "true" + if (fromEEPROM != (char)true) { + CONSOLE_LOG_LN( + F("CommandHandler::executeStartupCommands: No command stored")); + return CommandHandlerReturn::NO_COMMAND_WAITING; + } + + // Command is waiting, so queue it one char at a time + // If we reach a newline, commandWaiting() will flag "true": execute the + // command If we reach a NULL, end If we've read COMMAND_SIZE_MAX - 2 bytes + // from EEPROM, stop and add a newline + NULL + + // Index of location in EEPROM + int EEPROM_idx = EEPROM_STORED_COMMAND_LOCATION; + int numCharsRead = 0; + CommandHandlerReturn result = CommandHandlerReturn::NO_ERROR; + while (true) { + char c; + + // If we've read the max possible number of chars, stop here + if (numCharsRead >= EEPROM_SIZE_MAX) { + CONSOLE_LOG_LN(F("CommandHandler::Stored command unterminated!")); + + // Queue a newline then stop + addCommandChar('\n'); + break; + } + + EEPROM.get(EEPROM_idx, c); + + CONSOLE_LOG(F("CommandHandler::Read from EEPROM (")); + CONSOLE_LOG(EEPROM_idx); + CONSOLE_LOG(F("): [0x")); + CONSOLE_LOG(c, HEX); + CONSOLE_LOG_LN(']'); + + if (c == '\0') { + // Found the end + + CONSOLE_LOG(F("CommandHandler::Stored command ends at ")); + CONSOLE_LOG_LN(EEPROM_idx); + + break; + } + + // If no preceding commands have failed, execute if ready + if (CommandHandlerReturn::NO_ERROR == result) { + CONSOLE_LOG(F("CommandHandler::executeStartupCommands: Queueing ")); + CONSOLE_LOG_LN(c); + + // Queue the char + addCommandChar(c); + + // If CommandHandler is ready to execute the queued command, do so + if (commandWaiting()) { + result = executeCommand(); + } + } + + EEPROM_idx++; + numCharsRead++; + } + + return result; + } #endif -private: - - void clearBuffer() { - // Mark buffer as ready again - _bufferFull = false; - _command_too_long = false; - _inputBuffer[0] = '\0'; - _bufferLength = 0; - } - - // An object for handling the matching of commands -> functions - CommandLookup _lookupList; - - // Flag to warn that the command handler cannot handle more incoming chars - // until the current command is processed - bool _bufferFull; - - // A buffer for receiving new commands - char _inputBuffer[COMMAND_SIZE_MAX + 1]; - unsigned int _bufferLength; - - // A flag to report that the command currently being received has overrun - bool _command_too_long; - - ////////////////////// COMMAND LOOKUP ////////////////////// - - // This class is responsible for matching strings -> commands - // It maintains a vector of hashes, associated commands and number of - // parameters required for those commands - // `callStoredCommand` performs the lookup and calls the appropriate - // command, passing through a parameter lookup object - // - // Its maximum size is determined at compile time by the `size` template argument - -private: - // Structure of the data to be stored for each command - struct dataStruct { - unsigned long hash; // Hash of the keyword (case insensitive) - int n; // Number of params this function takes - commandFunction* f; // Pointer to this function - }; - - class CommandLookup - { - public: - - CommandLookup() : - _commandsIdx(0) - {} - - // Add a new command to the list, calculating its hash at runtime (deprecated) - CommandHandlerReturn registerCommand(const char* command, int num_of_parameters, - commandFunction* pointer_to_function) { - - // Get hash of command - const long keyHash = crc32b(command); - - return registerCommand(keyHash, num_of_parameters, pointer_to_function); - } - - // Add a new command to the list - CommandHandlerReturn registerCommand(long keyHash, int num_of_parameters, - commandFunction* pointer_to_function) - { - if (_commandsIdx >= array_size) { - CONSOLE_LOG_LN(F("CommandLookup::Out of pre-allocated space")); - return CommandHandlerReturn::OUT_OF_MEM; - } - - // Set up a struct containing the number of params and a pointer to the function - dataStruct d; - - // Save params - d.hash = keyHash; - d.n = num_of_parameters; - d.f = pointer_to_function; - - // Store it in the vector - _commands[_commandsIdx++] = d; - - return CommandHandlerReturn::NO_ERROR; - } - - // Search the list of commands for the given command and execute it with the given parameter array - CommandHandlerReturn callStoredCommand(const ParameterLookup& params) - { - - CONSOLE_LOG(F("callStoredCommand with n=")); - CONSOLE_LOG_LN(params.size()); - - // Get hash of command requested - const unsigned long reqHash = crc32b(params[0]); - - int foundIdx = -1; - - // Search through vector for this hash - for (int i = 0; i < _commandsIdx; i++) { - if (reqHash == _commands[i].hash) { - foundIdx = i; - break; - } - } - - if (foundIdx == -1) { - CONSOLE_LOG_LN(F("Command not found")); - return CommandHandlerReturn::COMMAND_NOT_FOUND; - } - - const dataStruct& d = _commands[foundIdx]; - const commandFunction* f = d.f; - - CONSOLE_LOG(F("Recalled data: d.n = ")); - CONSOLE_LOG_LN(d.n); - - // Return error if wrong number of parameters - if (d.n != params.size() - 1 && d.n != -1) { - CONSOLE_LOG(F("ERROR: Expecting ")); - CONSOLE_LOG(d.n); - CONSOLE_LOG(F(" parameters but got ")); - CONSOLE_LOG_LN(params.size() - 1); - - return CommandHandlerReturn::WRONG_NUM_OF_PARAMS; - } - - CONSOLE_LOG_LN(F("Calling function...")); - f(params); - - return CommandHandlerReturn::NO_ERROR; - } - - protected: - - dataStruct _commands[array_size]; - unsigned int _commandsIdx; - - // ----------------------------- crc32b -------------------------------- - // (case insensitive) - /* This is the basic CRC-32 calculation with some optimization but no - table lookup. The the byte reversal is avoided by shifting the crc reg - right instead of left and by using a reversed 32-bit word to represent - the polynomial. - When compiled to Cyclops with GCC, this function executes in 8 + 72n - instructions, where n is the number of bytes in the input message. It - should be doable in 4 + 61n instructions. - If the inner loop is strung out (approx. 5*8 = 40 instructions), - it would take about 6 + 46n instructions. */ - static uint32_t crc32b(const char *str) { - int i, j; - uint32_t byte, crc, mask; - - i = 0; - crc = 0xFFFFFFFF; - while (str[i] != 0) { - byte = tolower(str[i]); // Get next byte. - crc = crc ^ byte; - for (j = 7; j >= 0; j--) { // Do eight times. - mask = -(crc & 1); - crc = (crc >> 1) ^ (0xEDB88320 & mask); - } - i = i + 1; - } - return ~crc; - } - - }; + private: + void clearBuffer() { + // Mark buffer as ready again + _bufferFull = false; + _command_too_long = false; + _inputBuffer[0] = '\0'; + _bufferLength = 0; + } + + // An object for handling the matching of commands -> functions + CommandLookup _lookupList; + + // Flag to warn that the command handler cannot handle more incoming chars + // until the current command is processed + bool _bufferFull; + + // A buffer for receiving new commands + char _inputBuffer[COMMAND_SIZE_MAX + 1]; + unsigned int _bufferLength; + + // A flag to report that the command currently being received has overrun + bool _command_too_long; + + ////////////////////// COMMAND LOOKUP ////////////////////// + + // This class is responsible for matching strings -> commands + // It maintains a vector of hashes, associated commands and number of + // parameters required for those commands + // `callStoredCommand` performs the lookup and calls the appropriate + // command, passing through a parameter lookup object + // + // Its maximum size is determined at compile time by the `size` template + // argument + + private: + // Structure of the data to be stored for each command + struct dataStruct { + unsigned long hash; // Hash of the keyword (case insensitive) + int n; // Number of params this function takes + commandFunction* f; // Pointer to this function + }; + + class CommandLookup { + public: + CommandLookup() : _commandsIdx(0) {} + + // Add a new command to the list, calculating its hash at runtime + // (deprecated) + CommandHandlerReturn registerCommand(const char* command, + int num_of_parameters, + commandFunction* pointer_to_function) { + // Get hash of command + const long keyHash = crc32b(command); + + return registerCommand(keyHash, num_of_parameters, pointer_to_function); + } + + // Add a new command to the list + CommandHandlerReturn registerCommand(long keyHash, int num_of_parameters, + commandFunction* pointer_to_function) { + if (_commandsIdx >= array_size) { + CONSOLE_LOG_LN(F("CommandLookup::Out of pre-allocated space")); + return CommandHandlerReturn::OUT_OF_MEM; + } + + // Set up a struct containing the number of params and a pointer to the + // function + dataStruct d; + + // Save params + d.hash = keyHash; + d.n = num_of_parameters; + d.f = pointer_to_function; + + // Store it in the vector + _commands[_commandsIdx++] = d; + + return CommandHandlerReturn::NO_ERROR; + } + + // Search the list of commands for the given command and execute it with the + // given parameter array + CommandHandlerReturn callStoredCommand(const ParameterLookup& params) { + CONSOLE_LOG(F("callStoredCommand with n=")); + CONSOLE_LOG_LN(params.size()); + + // Get hash of command requested + const unsigned long reqHash = crc32b(params[0]); + + int foundIdx = -1; + + // Search through vector for this hash + for (int i = 0; i < _commandsIdx; i++) { + if (reqHash == _commands[i].hash) { + foundIdx = i; + break; + } + } + + if (foundIdx == -1) { + CONSOLE_LOG_LN(F("Command not found")); + return CommandHandlerReturn::COMMAND_NOT_FOUND; + } + + const dataStruct& d = _commands[foundIdx]; + const commandFunction* f = d.f; + + CONSOLE_LOG(F("Recalled data: d.n = ")); + CONSOLE_LOG_LN(d.n); + + // Return error if wrong number of parameters + if (d.n != params.size() - 1 && d.n != -1) { + CONSOLE_LOG(F("ERROR: Expecting ")); + CONSOLE_LOG(d.n); + CONSOLE_LOG(F(" parameters but got ")); + CONSOLE_LOG_LN(params.size() - 1); + + return CommandHandlerReturn::WRONG_NUM_OF_PARAMS; + } + + CONSOLE_LOG_LN(F("Calling function...")); + f(params); + + return CommandHandlerReturn::NO_ERROR; + } + + protected: + dataStruct _commands[array_size]; + unsigned int _commandsIdx; + + // ----------------------------- crc32b -------------------------------- + // (case insensitive) + /* This is the basic CRC-32 calculation with some optimization but no + table lookup. The the byte reversal is avoided by shifting the crc reg + right instead of left and by using a reversed 32-bit word to represent + the polynomial. + When compiled to Cyclops with GCC, this function executes in 8 + 72n + instructions, where n is the number of bytes in the input message. It + should be doable in 4 + 61n instructions. + If the inner loop is strung out (approx. 5*8 = 40 instructions), + it would take about 6 + 46n instructions. */ + static uint32_t crc32b(const char* str) { + int i, j; + uint32_t byte, crc, mask; + + i = 0; + crc = 0xFFFFFFFF; + while (str[i] != 0) { + byte = tolower(str[i]); // Get next byte. + crc = crc ^ byte; + for (j = 7; j >= 0; j--) { // Do eight times. + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + i = i + 1; + } + return ~crc; + } + }; }; - diff --git a/compileTimeCrc32.h b/compileTimeCrc32.h index 2fdc71f..71fc2fc 100644 --- a/compileTimeCrc32.h +++ b/compileTimeCrc32.h @@ -1,102 +1,107 @@ #pragma once +#include "Arduino.h" // This file implements the COMPILE_TIME_CRC32_STR macro: -// This macro creates a uint32_t crc32 hash of an input string, +// This macro creates a uint32_t crc32 hash of an input string, // not including the null char. Importantly, it does this at compile // time, so the memory-heavy strings don't end up in // the object code -// If it's not possible to do this at compile time a cryptic +// If it's not possible to do this at compile time a cryptic // error will be thrown: // "no matching function for call to 'ct()'" // CRC32 Table (zlib polynomial) static constexpr uint32_t crc_table[256] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -}; + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL}; // A constexpr version of tolower() constexpr char tolower_const(const char c) { - return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; } -// A recursive function that calculates the case insensitive hash of the input string -template -constexpr uint32_t crc32(const char * str) -{ - return (crc32(str) >> 8) ^ crc_table[(crc32(str) ^ tolower_const(str[idx])) & 0x000000FF]; +// A recursive function that calculates the case insensitive hash of the input +// string +template +constexpr uint32_t crc32(const char* str) { + return (crc32(str) >> 8) ^ + crc_table[(crc32(str) ^ tolower_const(str[idx])) & + 0x000000FF]; } // This is the stop-recursion function -template<> -constexpr uint32_t crc32(const char * str) -{ +template <> +constexpr uint32_t crc32(const char* str) { return 0xFFFFFFFF; } // This is the call to the CRC32 chain -// Don't use this macro uness you are happy to have this hash performed at runtime (a bad idea) +// Don't use this macro uness you are happy to have this hash performed at +// runtime (a bad idea) #define DO_RUNTIME_CRC32_HASH(x) (crc32(x) ^ 0xFFFFFFFF) // This odd looking function is a cheaty way to just return the parameter V -// The point is that V is passed as a template parameter, forcing it to be evaluated at -// compile time. This allows us to throw an error if the compiler can't figure it out at -// compile time and would otherwise have stored the (large and slow) CRC code on the -// microcontroller -template -constexpr T ct() { return V; } +// The point is that V is passed as a template parameter, forcing it to be +// evaluated at compile time. This allows us to throw an error if the compiler +// can't figure it out at compile time and would otherwise have stored the +// (large and slow) CRC code on the microcontroller +template +constexpr T ct() { + return V; +} // Here we call the CRC32 function and pass the output via the template cheat -// if you get the error "no matching function for call to 'ct()'" then this is because -// the compiler can't figure out the hash at compile time. Ensure that x is a constexpr +// if you get the error "no matching function for call to 'ct()'" then this is +// because the compiler can't figure out the hash at compile time. Ensure that x +// is a constexpr #define COMMANDHANDLER_HASH(x) ct() \ No newline at end of file diff --git a/Microprocessor_Debugging/debugging_disable.h b/debugging_disable.h similarity index 100% rename from Microprocessor_Debugging/debugging_disable.h rename to debugging_disable.h diff --git a/Microprocessor_Debugging/debugging_enable.h b/debugging_enable.h similarity index 100% rename from Microprocessor_Debugging/debugging_enable.h rename to debugging_enable.h diff --git a/Microprocessor_Debugging/debugging_init.h b/debugging_init.h similarity index 100% rename from Microprocessor_Debugging/debugging_init.h rename to debugging_init.h