From b5d9c9d6660f2cce7d7e5232ec6959305e3d1a64 Mon Sep 17 00:00:00 2001 From: Quinn Date: Wed, 7 Aug 2024 08:57:07 -0400 Subject: [PATCH] Refactored to have a pure virtual bas class and added telnet capability --- BluetoothSerialMessage.h | 31 +++---- Message.h | 187 +++++++++++++++++++++++++++++++++++++ SerialMessage.h | 194 +++++---------------------------------- TelnetMessage.h | 138 ++++++++++++++++++++++++++++ USBMessage.h | 30 +++--- 5 files changed, 379 insertions(+), 201 deletions(-) create mode 100644 Message.h create mode 100644 TelnetMessage.h diff --git a/BluetoothSerialMessage.h b/BluetoothSerialMessage.h index 966bb9a..cd148d3 100644 --- a/BluetoothSerialMessage.h +++ b/BluetoothSerialMessage.h @@ -2,18 +2,19 @@ #include "SerialMessage.h" #include -template -class BluetoothSerialMessage : public SerialMessage{ +template +class BluetoothSerialMessage : public SerialMessage{ public: /** * @brief Construct a new Bluetooth Serial Message object */ BluetoothSerialMessage(BluetoothSerial *serial); - + + void Init(uint32_t baudRate) override{this->Init();} /** * @brief Initialize the BluetoothSerialMessage object */ - void Init(uint32_t baudRate = 115200) override; + void Init(); /** * @brief prints the args array to the serial monitor @@ -28,32 +29,30 @@ class BluetoothSerialMessage : public SerialMessage{ uint32_t dataAvailable() override; BluetoothSerial *serial; - - }; -template -char BluetoothSerialMessage::getChar(){ +template +char BluetoothSerialMessage::getChar(){ return serial->read(); } -template -uint32_t BluetoothSerialMessage::dataAvailable(){ +template +uint32_t BluetoothSerialMessage::dataAvailable(){ return serial->available(); } -template -BluetoothSerialMessage::BluetoothSerialMessage(BluetoothSerial *serial){ +template +BluetoothSerialMessage::BluetoothSerialMessage(BluetoothSerial *serial){ this->serial = serial; } -template -void BluetoothSerialMessage::Init(uint32_t baudRate){ +template +void BluetoothSerialMessage::Init(){ serial->begin("MiniBot"); } -template -void BluetoothSerialMessage::PrintArgs(){ +template +void BluetoothSerialMessage::PrintArgs(){ serial->print("Current number of args: "); serial->println(this->populatedArgs); for (int i = 0; i < this->populatedArgs; i++) { diff --git a/Message.h b/Message.h new file mode 100644 index 0000000..6ec143a --- /dev/null +++ b/Message.h @@ -0,0 +1,187 @@ +/** + * @file Message.h + * @brief This file contains the Message base class which is can be inherited to + * parse serial messages over any type of protocol + * @version 1.0.0 + * @author Quinn Henthorne. Contact: quinn.henthorne@gmail.com +*/ + +#pragma once + +#include + +template +class Message{ + public: + + /** + * @brief Initialize the Message object + */ + virtual void Init(uint32_t baudRate); + + /** + * @brief Prints the args array to the serial monitor + */ + virtual void PrintArgs() = 0; + + /** + * @brief Update the Message object and parse any data that's available + */ + void Update(); + + /** + * @brief Returns true if there is new data available + * @return true if there is new data available + */ + bool IsNewData(); + + /** + * @brief Clears the new data flag + */ + void ClearNewData(); + + /** + * @brief Return a pointer to the args array + * @return a pointer to the args array + */ + int32_t * GetArgs(); + + /** + * @brief Returns the number of args that have been populated for the current message + * @return the number of args that have been populated for the current message + */ + uint32_t GetArgsLength(); + + /** + * @brief Returns the number of args that have been populated for the current message + * @return the number of args that have been populated for the current message + */ + uint32_t GetPopulatedArgs(); + + protected: + enum SerialState : uint8_t{ + IDLE, + NEW_DATA, + DATA_RECIEVED, + RECIEVE_IN_PROGRESS + }; + Message() = default; + /** + * @brief reads the serial data and stores it in the data array + * @return the next character in the serial buffer + */ + virtual char getChar() = 0; + + /** + * @brief returns the number of bytes available in the serial buffer + * @return the number of bytes available in the serial buffer + */ + virtual uint32_t dataAvailable() = 0; + + /** + * @brief Takes in any available serial data and reads it into the buffer if our start character is hit. + * Also marks what state the Message object is in + */ + void readSerial(); + void parseData(); + + SerialState state{IDLE}; + char data[SERIAL_BUFFER_SIZE]; // an array to store the received data + char temp_data[SERIAL_BUFFER_SIZE]; // an array that will be used with strtok() + uint32_t ndx{0}; + uint32_t populatedArgs{0}; // the number of args that have been populated for the current message + int32_t args[MAX_ARGS]; + const char startMarker = '!'; + const char endMarker = ';'; +}; + +template +void Init(uint32_t baudRate){ + Serial.println("You called Message::Init! This should NEVER happen!"); +} + +template +void Message::readSerial(){ + char c; + + // read the incoming serial data: + while (this->dataAvailable() > 0 && this->state != SerialState::DATA_RECIEVED) { + // get the neext character in the serial buffer + c = this->getChar(); + // only execute this if the startMarker has been received + // if the incoming character is the endMarker clean up and set the flags + if (this->state == SerialState::RECIEVE_IN_PROGRESS) { + if (c == endMarker) { + data[ndx] = '\0'; // terminate the string + ndx = 0; + this->state = SerialState::DATA_RECIEVED; + } + // if the incoming character is not the endMarker + else { + // add it to the data array + data[ndx] = c; + ndx++; // increment the data array index + // if the index is greater than the maximum data array size, + // keep overwriting the last element until the endMarker is received. + if (ndx >= SERIAL_BUFFER_SIZE) { + ndx = SERIAL_BUFFER_SIZE - 1; + } + } + } + // if the incoming character is the startMarker, set the recvInProgress flag + else if (c == startMarker) { + this->state = SerialState::RECIEVE_IN_PROGRESS; + } + } +} + +template +void Message::parseData() { // split the data into its parts + this->populatedArgs = 0; // reset the populated args counter + char * indx; // this is used by strtok() as an index + int i = 0; + indx = strtok(temp_data, ","); // get the first part - the string + while(indx != NULL){ + this->args[i] = atoi(indx); + populatedArgs++; + i++; + indx = strtok(NULL, ","); // this continues where the previous call left off + } +} + +template +void Message::Update(){ + readSerial(); + if (this->state == SerialState::DATA_RECIEVED) { + strcpy(temp_data, data); + // this temporary copy is necessary to protect the original data + // because strtok() used in parseData() replaces the commas with \0 + parseData(); + this->state = SerialState::NEW_DATA; + } +} + +template +bool Message::IsNewData(){ + return this->state == SerialState::NEW_DATA; +} + +template +void Message::ClearNewData(){ + this->state = SerialState::IDLE; +} + +template +int32_t * Message::GetArgs(){ + return args; +} + +template +uint32_t Message::GetArgsLength(){ + return MAX_ARGS; +} + +template +uint32_t Message::GetPopulatedArgs(){ + return populatedArgs; +} \ No newline at end of file diff --git a/SerialMessage.h b/SerialMessage.h index 82b253c..99a96ba 100644 --- a/SerialMessage.h +++ b/SerialMessage.h @@ -6,16 +6,15 @@ * @author Quinn Henthorne. Contact: quinn.henthorne@gmail.com */ -#ifndef SERIALMESSAGE_H -#define SERIALMESSAGE_H +#pragma once -#include "Arduino.h" +#include -template -class SerialMessage{ +#include "Message.h" + +template +class SerialMessage : public Message{ public: - // @warning Never use this to construct a SerialMessage object - SerialMessage() = delete; /** * @brief Construct a new Serial Message object */ @@ -24,202 +23,57 @@ class SerialMessage{ /** * @brief Initialize the SerialMessage object */ - virtual void Init(unsigned int baud_rate = 115200); - - /** - * @brief Update the SerialMessage object and parse any data that's available - */ - void Update(); - - /** - * @brief Returns true if there is new data available - * @return true if there is new data available - */ - bool IsNewData(); - - /** - * @brief Clears the new data flag - */ - virtual void ClearNewData(); - - /** - * @brief Return a pointer to the args array - * @return a pointer to the args array - */ - int32_t * GetArgs(); - - /** - * @brief Returns the number of args that have been populated for the current message - * @return the number of args that have been populated for the current message - */ - uint32_t GetArgsLength(); - - /** - * @brief Returns the number of args that have been populated for the current message - * @return the number of args that have been populated for the current message - */ - uint32_t GetPopulatedArgs(); + void Init(unsigned int baudRate) override; /** * @brief Prints the args array to the serial monitor */ - virtual void PrintArgs(); + void PrintArgs() override; protected: - enum SerialState : uint8_t{ - IDLE, - NEW_DATA, - DATA_RECIEVED, - RECIEVE_IN_PROGRESS - }; - virtual void readSerial(); - virtual void parseData(); /** * @brief reads the serial data and stores it in the data array * @return the next character in the serial buffer */ - virtual char getChar(); + char getChar() override; /** * @brief returns the number of bytes available in the serial buffer * @return the number of bytes available in the serial buffer */ - virtual uint32_t dataAvailable(); - - SerialState state{IDLE}; - char data[byteSize]; // an array to store the received data - char temp_data[byteSize]; // an array that will be used with strtok() - uint32_t ndx{0}; - static constexpr uint16_t args_length{30}; - uint32_t populatedArgs{0}; // the number of args that have been populated for the current message - int32_t args[args_length]; - const char startMarker = '!'; - const char endMarker = ';'; + uint32_t dataAvailable() override; private: HardwareSerial *serial{nullptr}; }; -template -char SerialMessage::getChar(){ +template +char SerialMessage::getChar(){ return this->serial->read(); } -template -uint32_t SerialMessage::dataAvailable(){ +template +uint32_t SerialMessage::dataAvailable(){ return this->serial->available(); } -template -SerialMessage::SerialMessage(HardwareSerial *serial) : +template +SerialMessage::SerialMessage(HardwareSerial *serial) : serial(serial){} -template -void SerialMessage::Init(unsigned int baud_rate){ - this->serial->begin(baud_rate); +template +void SerialMessage::Init(uint32_t baudRate){ + this->serial->begin(baudRate); } -template -void SerialMessage::readSerial(){ - char c; - - // read the incoming serial data: - while (this->dataAvailable() > 0 && this->state != SerialState::DATA_RECIEVED) { - // get the neext character in the serial buffer - c = this->getChar(); - // only execute this if the startMarker has been received - // if the incoming character is the endMarker clean up and set the flags - if (this->state == SerialState::RECIEVE_IN_PROGRESS) { - if (c == endMarker) { - data[ndx] = '\0'; // terminate the string - ndx = 0; - this->state = SerialState::DATA_RECIEVED; - } - // if the incoming character is not the endMarker - else { - // add it to the data array - data[ndx] = c; - ndx++; // increment the data array index - // if the index is greater than the maximum data array size, - // keep overwriting the last element until the endMarker is received. - if (ndx >= byteSize) { - ndx = byteSize - 1; - } - } - } - // if the incoming character is the startMarker, set the recvInProgress flag - else if (c == startMarker) { - this->state = SerialState::RECIEVE_IN_PROGRESS; - } - } -} - -template -void SerialMessage::parseData() { // split the data into its parts - this->populatedArgs = 0; // reset the populated args counter - char * indx; // this is used by strtok() as an index - int i = 0; - indx = strtok(temp_data, ","); // get the first part - the string - while(indx != NULL){ - this->args[i] = atoi(indx); - populatedArgs++; - i++; - indx = strtok(NULL, ","); // this continues where the previous call left off - } -} - -template -void SerialMessage::Update(){ - readSerial(); - if (this->state == SerialState::DATA_RECIEVED) { - // for debug only: - // Serial.print("Received:"); - // Serial.print(data); - // Serial.println(":End"); - strcpy(temp_data, data); - // this temporary copy is necessary to protect the original data - // because strtok() used in parseData() replaces the commas with \0 - parseData(); - //PrintArgs(); - this->state = SerialState::NEW_DATA; - } -} - -template -bool SerialMessage::IsNewData(){ - return this->state == SerialState::NEW_DATA; -} - -template -void SerialMessage::ClearNewData(){ - this->state = SerialState::IDLE; -} - -template -int32_t * SerialMessage::GetArgs(){ - return args; -} - -template -uint32_t SerialMessage::GetArgsLength(){ - return args_length; -} - -template -uint32_t SerialMessage::GetPopulatedArgs(){ - return populatedArgs; -} - -template -void SerialMessage::PrintArgs(){ +template +void SerialMessage::PrintArgs(){ return; this->serial->print("Current number of args: "); this->serial->println(this->populatedArgs); - for (int i = 0; i < populatedArgs; i++) { - this->serial->print(args[i]); + for (int i = 0; i < this->populatedArgs; i++) { + this->serial->print(this->args[i]); this->serial->print(" "); } this->serial->println(); -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/TelnetMessage.h b/TelnetMessage.h new file mode 100644 index 0000000..961453f --- /dev/null +++ b/TelnetMessage.h @@ -0,0 +1,138 @@ +#pragma once + +#include + +#include "SerialMessage.h" +#include "WiFi.h" +#include "GlobalPrint.h" + +template +class TelnetMessage : public Message{ + public: + /** + * @brief Construct a new Bluetooth Serial Message object + */ + TelnetMessage(ESPTelnet *telnet) : + telnet(telnet){} + + void Init(uint32_t baudRate) override{this->Init();} + /** + * @brief Initialize the TelnetMessage object + * @pre The WiFi object must have a state of WL_CONNECTED + */ + void Init(); + + /** + * @brief prints the args array to the telnet monitor + */ + void PrintArgs() override; + + void SetString(const char * data); + + void SetOnInputRecieved(void (*callback)(String data)); + private: + /** + * @brief reads the telnet data and stores it in the data array + */ + char getChar() override; + + /** + * @brief returns the amount of data available + */ + uint32_t dataAvailable() override; + + static void onConnectCallback(String ip); + static void onConnectionAttemptCallback(String ip); + static void onReconnectCallback(String ip); + static void onDisconnectCallback(String ip); + + ESPTelnet *telnet; + + const char * incomingData = nullptr; + uint32_t incomingDataLength{0}; + uint32_t incomingDataCharIndex{0}; +}; + +template +void TelnetMessage::onConnectCallback(String ip) { + GlobalPrint::Print("- Telnet: "); + GlobalPrint::Print(ip); + GlobalPrint::Println(" connected"); +} + +template +void TelnetMessage::onConnectionAttemptCallback(String ip) { + GlobalPrint::Print("- Telnet: "); + GlobalPrint::Print(ip); + GlobalPrint::Println(" failed to connect"); +} + +template +void TelnetMessage::onReconnectCallback(String ip) { + GlobalPrint::Print("- Telnet: "); + GlobalPrint::Print(ip); + GlobalPrint::Println(" reconnected"); +} + +template +void TelnetMessage::onDisconnectCallback(String ip) { + GlobalPrint::Print("- Telnet: "); + GlobalPrint::Print(ip); + GlobalPrint::Println(" disconnected"); +} + +template +void TelnetMessage::SetOnInputRecieved(void (*callback)(String data)){ + this->telnet->onInputReceived(callback); +} + +template +char TelnetMessage::getChar(){ + // Stop us from reading any more data if there's nothing more to read + if(this->incomingData == nullptr || this->incomingDataLength == 0){ + return this->endMarker; + } + + char output = this->incomingData[this->incomingDataCharIndex]; + this->incomingDataCharIndex++; + // GlobalPrint::Println("Char Idx:" + String(this->incomingDataCharIndex) + ", Data Length:" + String(this->incomingDataLength) + ", Char:" + output); + return output; +} + +template +void TelnetMessage::SetString(const char * data){ + this->incomingDataLength = strlen(data); + this->incomingDataCharIndex = 0; + this->incomingData = data; + this->Update(); +} + +template +uint32_t TelnetMessage::dataAvailable(){ + if(this->incomingData == nullptr){ + return 0; + } + return this->incomingDataLength - this->incomingDataCharIndex; +} + +template +void TelnetMessage::Init(){ + telnet->begin(23); + // set all of our callbacks + telnet->onConnect(TelnetMessage::onConnectCallback); + telnet->onConnectionAttempt(TelnetMessage::onConnectionAttemptCallback); + telnet->onReconnect(TelnetMessage::onReconnectCallback); + telnet->onDisconnect(TelnetMessage::onDisconnectCallback); +} + +template +void TelnetMessage::PrintArgs(){ + telnet->print("Current number of args: "); + telnet->println(this->populatedArgs); + for (int i = 0; i < this->populatedArgs; i++) { + telnet->print(this->args[i]); + telnet->print(" "); + } + telnet->println(); +} + diff --git a/USBMessage.h b/USBMessage.h index 9acabd3..eee066c 100644 --- a/USBMessage.h +++ b/USBMessage.h @@ -2,14 +2,14 @@ #include "SerialMessage.h" #include -template -class USBMessage : public SerialMessage{ +template +class USBMessage : public Message{ public: /** - * @brief Construct a new Bluetooth Serial Message object + * @brief Construct a new USB Serial Message object */ USBMessage(USBCDC *serial); - void Init(uint32_t baudRate = 115200) override; + void Init(uint32_t baudRate) override; /** * @brief prints the args array to the serial monitor */ @@ -23,26 +23,26 @@ class USBMessage : public SerialMessage{ }; -template -char USBMessage::getChar(){ +template +USBMessage::USBMessage(USBCDC *USBSerial) : serial(USBSerial){} + +template +char USBMessage::getChar(){ return serial->read(); } -template -uint32_t USBMessage::dataAvailable(){ +template +uint32_t USBMessage::dataAvailable(){ return serial->available(); } -template -USBMessage::USBMessage(USBCDC *serial) : serial(serial){} - -template -void USBMessage::Init(uint32_t baudRate){ +template +void USBMessage::Init(uint32_t baudRate){ serial->begin(); } -template -void USBMessage::PrintArgs(){ +template +void USBMessage::PrintArgs(){ serial->print("Current number of args: "); serial->println(this->populatedArgs); for (int i = 0; i < this->populatedArgs; i++) {