From 42aeb537fd9293be6bba6be1ce3c2edd52e89274 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 20 Mar 2024 09:01:04 +0000 Subject: [PATCH 1/6] Refactor LineBuffer --- Sming/Core/Data/Buffer/LineBuffer.cpp | 49 ++++++++++++++++++++++++ Sming/Core/Data/Buffer/LineBuffer.h | 55 +++++++++------------------ 2 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 Sming/Core/Data/Buffer/LineBuffer.cpp diff --git a/Sming/Core/Data/Buffer/LineBuffer.cpp b/Sming/Core/Data/Buffer/LineBuffer.cpp new file mode 100644 index 0000000000..eab88d015f --- /dev/null +++ b/Sming/Core/Data/Buffer/LineBuffer.cpp @@ -0,0 +1,49 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * LineBuffer.h - support for buffering/editing a line of text + * + * author mikee47 Feb 2019 + * + ****/ + +#include "LineBuffer.h" + +char LineBufferBase::addChar(char c) +{ + if(c == '\n' || c == '\r') { + return '\n'; + } + + if(c >= 0x20 && c < 0x7f && length < (size - 1)) { + buffer[length++] = c; + buffer[length] = '\0'; + return c; + } + + return '\0'; +} + +bool LineBufferBase::backspace() +{ + if(length == 0) { + return false; + } + --length; + buffer[length] = '\0'; + return true; +} + +bool LineBufferBase::startsWith(const char* text) const +{ + auto len = strlen(text); + return memcmp(buffer, text, len) == 0; +} + +bool LineBufferBase::contains(const char* text) const +{ + return strstr(buffer, text) != nullptr; +} diff --git a/Sming/Core/Data/Buffer/LineBuffer.h b/Sming/Core/Data/Buffer/LineBuffer.h index e23186b483..202b2691d3 100644 --- a/Sming/Core/Data/Buffer/LineBuffer.h +++ b/Sming/Core/Data/Buffer/LineBuffer.h @@ -19,9 +19,13 @@ * @brief Class to enable buffering of a single line of text, with simple editing * @note We define this as a template class for simplicity, no need for separate buffer memory management */ -template class LineBuffer +class LineBufferBase { public: + LineBufferBase(char* buffer, uint16_t size) : buffer(buffer), size(size) + { + } + /** * @brief Add a character to the buffer * @retval char Character added to buffer, '\0' if ignored, '\n' if line is complete @@ -73,43 +77,22 @@ template class LineBuffer bool backspace(); private: - char buffer[BUFSIZE] = {'\0'}; ///< The text buffer - uint16_t length = 0; ///< Number of characters stored + char* buffer; + uint16_t size; + uint16_t length{0}; ///< Number of characters stored }; -template char LineBuffer::addChar(char c) -{ - if(c == '\n' || c == '\r') { - return '\n'; - } - - if(c >= 0x20 && c < 0x7f && length < (BUFSIZE - 1)) { - buffer[length++] = c; - buffer[length] = '\0'; - return c; - } - - return '\0'; -} - -template bool LineBuffer::backspace() +/** + * @brief Class to enable buffering of a single line of text, with simple editing + * @note We define this as a template class for simplicity, no need for separate buffer memory management + */ +template class LineBuffer : public LineBufferBase { - if(length == 0) { - return false; - } else { - --length; - buffer[length] = '\0'; - return true; +public: + LineBuffer() : LineBufferBase(buffer, BUFSIZE) + { } -} -template bool LineBuffer::startsWith(const char* text) const -{ - auto len = strlen(text); - return memcmp(buffer, text, len) == 0; -} - -template bool LineBuffer::contains(const char* text) const -{ - return strstr(buffer, text) != nullptr; -} +private: + char buffer[BUFSIZE]{}; +}; From f7adf5bca2850071db9c1c9b34dd635be531f73b Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 20 Mar 2024 16:05:21 +0000 Subject: [PATCH 2/6] Add `bool`, `String` operators, `printTo` method --- Sming/Core/Data/Buffer/LineBuffer.h | 21 +++++++++++++++++++ .../src/CommandProcessing/Handler.cpp | 3 +-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sming/Core/Data/Buffer/LineBuffer.h b/Sming/Core/Data/Buffer/LineBuffer.h index 202b2691d3..cd6f4ff35e 100644 --- a/Sming/Core/Data/Buffer/LineBuffer.h +++ b/Sming/Core/Data/Buffer/LineBuffer.h @@ -14,6 +14,8 @@ #include #include +#include +#include /** * @brief Class to enable buffering of a single line of text, with simple editing @@ -40,6 +42,20 @@ class LineBufferBase length = 0; } + explicit operator bool() const + { + return length != 0; + } + + /** + * @brief Copy buffer contents into a String + * @retval String + */ + explicit operator String() const + { + return String(buffer, length); + } + /** * @brief Get the text, nul-terminated */ @@ -76,6 +92,11 @@ class LineBufferBase */ bool backspace(); + size_t printTo(Print& p) const + { + return p.write(buffer, length); + } + private: char* buffer; uint16_t size; diff --git a/Sming/Libraries/CommandProcessing/src/CommandProcessing/Handler.cpp b/Sming/Libraries/CommandProcessing/src/CommandProcessing/Handler.cpp index 03bec19a4e..60ec754bfd 100644 --- a/Sming/Libraries/CommandProcessing/src/CommandProcessing/Handler.cpp +++ b/Sming/Libraries/CommandProcessing/src/CommandProcessing/Handler.cpp @@ -29,9 +29,8 @@ size_t Handler::process(char recvChar) output.print(getCommandPrompt()); } } else if(recvChar == getCommandEOL()) { - String command(commandBuf.getBuffer(), commandBuf.getLength()); + processCommandLine(String(commandBuf)); commandBuf.clear(); - processCommandLine(command); } else if(recvChar == '\b' || recvChar == 0x7f) { if(commandBuf.backspace()) { output.print(_F("\b \b")); From 07e3b6d056e731d5c905ab099103ecb656d23559 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Wed, 20 Mar 2024 16:11:20 +0000 Subject: [PATCH 3/6] Add `process`, `processKey` methods --- Sming/Core/Data/Buffer/LineBuffer.cpp | 58 +++++++++++++++++++++++++++ Sming/Core/Data/Buffer/LineBuffer.h | 34 ++++++++++++++-- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/Sming/Core/Data/Buffer/LineBuffer.cpp b/Sming/Core/Data/Buffer/LineBuffer.cpp index eab88d015f..2f4d14ab75 100644 --- a/Sming/Core/Data/Buffer/LineBuffer.cpp +++ b/Sming/Core/Data/Buffer/LineBuffer.cpp @@ -12,6 +12,64 @@ #include "LineBuffer.h" +LineBufferBase::Action LineBufferBase::process(Stream& input, ReadWriteStream& output) +{ + int c; + while((c = input.read()) >= 0) { + auto action = processKey(c, &output); + if(action == Action::clear || action == Action::submit) { + return action; + } + } + return Action::none; +} + +LineBufferBase::Action LineBufferBase::processKey(char key, ReadWriteStream* output) +{ + auto prevKey = previousKey; + previousKey = key; + + switch(key) { + case '\x1b': // ESC -> delete current commandLine + clear(); + if(output) { + output->println(); + } + return Action::clear; + + case '\b': // delete (backspace) + case '\x7f': // xterm ctrl-? + if(!backspace()) { + return Action::none; + } + if(output) { + output->print("\b \b"); + } + return Action::backspace; + + case '\r': + case '\n': + // For "\r\n" or "\n\r" sequence ignore second key + if(prevKey != key && (prevKey == '\r' || prevKey == '\n')) { + previousKey = '\0'; + return Action::none; + } + if(output) { + output->println(); + } + return Action::submit; + + default: + if(!addChar(key)) { + return Action::none; + } + if(output) { + output->print(key); + } + return Action::echo; + } +} + char LineBufferBase::addChar(char c) { if(c == '\n' || c == '\r') { diff --git a/Sming/Core/Data/Buffer/LineBuffer.h b/Sming/Core/Data/Buffer/LineBuffer.h index cd6f4ff35e..0295edc41f 100644 --- a/Sming/Core/Data/Buffer/LineBuffer.h +++ b/Sming/Core/Data/Buffer/LineBuffer.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include /** * @brief Class to enable buffering of a single line of text, with simple editing @@ -28,6 +28,33 @@ class LineBufferBase { } + /** + * @brief Returned from `processKey` method directing caller + */ + enum class Action { + none, ///< Do nothing, ignore the key + clear, ///< Line is cleared: typically perform a carriage return + echo, ///< Key should be echoed + backspace, ///< Perform backspace edit, e.g. output "\b \b" + submit, ///< User hit return, process line and clear it + }; + + /** + * @brief Process all available data from `input` + * @param input Source of keystrokes + * @param output The output stream (e.g. Serial) for echoing + * @retval Action: none, clear or submit + */ + Action process(Stream& input, ReadWriteStream& output); + + /** + * @brief Process a keypress in a consistent manner for console editing + * @param key The keypress value + * @param output The output stream (e.g. Serial) for echoing, if required + * @retval Action + */ + Action processKey(char key, ReadWriteStream* output = nullptr); + /** * @brief Add a character to the buffer * @retval char Character added to buffer, '\0' if ignored, '\n' if line is complete @@ -53,7 +80,7 @@ class LineBufferBase */ explicit operator String() const { - return String(buffer, length); + return length ? String(buffer, length) : nullptr; } /** @@ -100,7 +127,8 @@ class LineBufferBase private: char* buffer; uint16_t size; - uint16_t length{0}; ///< Number of characters stored + uint16_t length{0}; ///< Number of characters stored + char previousKey{'\0'}; ///< For processing CR/LF }; /** From c9cd9290b6bdb986226174a6b8fbd96783161f81 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 12 Mar 2024 08:02:07 +0000 Subject: [PATCH 4/6] Use LineBuffer for LiveDebug --- samples/LiveDebug/app/application.cpp | 44 +++++++++------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/samples/LiveDebug/app/application.cpp b/samples/LiveDebug/app/application.cpp index 6b7a5bd780..0adca0af28 100644 --- a/samples/LiveDebug/app/application.cpp +++ b/samples/LiveDebug/app/application.cpp @@ -96,9 +96,7 @@ void showPrompt() void onDataReceived(Stream& source, char arrivedChar, unsigned short availableCharsCount) { - static unsigned commandLength; - const unsigned MAX_COMMAND_LENGTH = 16; - static char commandBuffer[MAX_COMMAND_LENGTH + 1]; + static LineBuffer commandBuffer; // Error detection unsigned status = Serial.getStatus(); @@ -118,38 +116,24 @@ void onDataReceived(Stream& source, char arrivedChar, unsigned short availableCh } // Discard what is likely to be garbage Serial.clear(SERIAL_RX_ONLY); - commandLength = 0; + commandBuffer.clear(); showPrompt(); return; } - int c; - while((c = Serial.read()) >= 0) { - switch(c) { - case '\b': // delete (backspace) - case 0x7f: // xterm ctrl-? - if(commandLength > 0) { - --commandLength; - Serial.print(_F("\b \b")); - } - break; - case '\r': - case '\n': - if(commandLength > 0) { - Serial.println(); - String cmd(commandBuffer, commandLength); - commandLength = 0; - Serial.clear(SERIAL_RX_ONLY); - handleCommand(cmd); - } - showPrompt(); - break; - default: - if(c >= 0x20 && c <= 0x7f && commandLength < MAX_COMMAND_LENGTH) { - commandBuffer[commandLength++] = c; - Serial.print(char(c)); - } + switch(commandBuffer.process(source, Serial)) { + case commandBuffer.Action::clear: + showPrompt(); + break; + case commandBuffer.Action::submit: { + if(commandBuffer) { + handleCommand(String(commandBuffer)); + commandBuffer.clear(); } + showPrompt(); + } + default: + break; } } From d751b681e53b08273df8746b15071aae7c4a8f00 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 19 Mar 2024 21:04:30 +0000 Subject: [PATCH 5/6] Basic_Ota --- samples/Basic_Ota/app/application.cpp | 170 ++++++++++++++++---------- 1 file changed, 108 insertions(+), 62 deletions(-) diff --git a/samples/Basic_Ota/app/application.cpp b/samples/Basic_Ota/app/application.cpp index f9aa5f7153..51770d421c 100644 --- a/samples/Basic_Ota/app/application.cpp +++ b/samples/Basic_Ota/app/application.cpp @@ -3,6 +3,7 @@ #include #include #include +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -10,9 +11,12 @@ #define WIFI_PWD "PleaseEnterPass" #endif +namespace +{ std::unique_ptr otaUpdater; Storage::Partition spiffsPartition; OtaUpgrader ota; +LineBuffer<16> commandBuffer; Storage::Partition findSpiffsPartition(Storage::Partition appPart) { @@ -122,76 +126,114 @@ void showInfo() << " @ 0x" << String(after.address(), HEX) << endl; } -void serialCallBack(Stream& stream, char arrivedChar, unsigned short availableCharsCount) +void showPrompt() +{ + Serial << _F("OTA> ") << commandBuffer; +} + +void handleCommand(const String& str) { - int pos = stream.indexOf('\n'); - if(pos > -1) { - char str[pos + 1]; - for(int i = 0; i < pos + 1; i++) { - str[i] = stream.read(); - if(str[i] == '\r' || str[i] == '\n') { - str[i] = '\0'; + if(F("connect") == str) { + Serial << _F("Connecting to '") << WIFI_SSID << "'..." << endl; + WifiStation.config(WIFI_SSID, WIFI_PWD); + WifiStation.enable(true); + WifiStation.connect(); + return; + } + + if(F("ip") == str) { + Serial << "ip: " << WifiStation.getIP() << ", mac: " << WifiStation.getMacAddress() << endl; + return; + } + + if(F("ota") == str) { + doUpgrade(); + return; + } + + if(F("switch") == str) { + doSwitch(); + return; + } + + if(F("restart") == str) { + System.restart(); + return; + } + + if(F("ls") == str) { + Directory dir; + if(dir.open()) { + while(dir.next()) { + Serial << " " << dir.stat().name << endl; } } + Serial << _F("filecount ") << dir.count() << endl; + return; + } - if(F("connect") == str) { - // connect to wifi - WifiStation.config(WIFI_SSID, WIFI_PWD); - WifiStation.enable(true); - WifiStation.connect(); - } else if(F("ip") == str) { - Serial << "ip: " << WifiStation.getIP() << ", mac: " << WifiStation.getMacAddress() << endl; - } else if(F("ota") == str) { - doUpgrade(); - } else if(F("switch") == str) { - doSwitch(); - } else if(F("restart") == str) { - System.restart(); - } else if(F("ls") == str) { - Directory dir; - if(dir.open()) { - while(dir.next()) { - Serial << " " << dir.stat().name << endl; - } - } - Serial << _F("filecount ") << dir.count() << endl; - } else if(F("cat") == str) { - Directory dir; - if(dir.open() && dir.next()) { - auto filename = dir.stat().name.c_str(); - Serial << "dumping file " << filename << ": " << endl; - // We don't know how big the is, so streaming it is safest - FileStream fs; - fs.open(filename); - Serial.copyFrom(&fs); - Serial.println(); - } else { - Serial.println(F("Empty spiffs!")); - } - } else if(F("info") == str) { - showInfo(); - } else if(F("help") == str) { - Serial.print(_F("\r\n" - "available commands:\r\n" - " help - display this message\r\n" - " ip - show current ip address\r\n" - " connect - connect to wifi\r\n" - " restart - restart the device\r\n" - " switch - switch to the other rom and reboot\r\n" - " ota - perform ota update, switch rom and reboot\r\n" - " info - show device info\r\n")); - - if(spiffsPartition) { - Serial.print(_F(" ls - list files in spiffs\r\n" - " cat - show first file in spiffs\r\n")); - } + if(F("cat") == str) { + Directory dir; + if(dir.open() && dir.next()) { + auto filename = dir.stat().name.c_str(); + Serial << "dumping file " << filename << ": " << endl; + // We don't know how big the is, so streaming it is safest + FileStream fs; + fs.open(filename); + Serial.copyFrom(&fs); Serial.println(); } else { - Serial.println("unknown command"); + Serial.println(F("Empty spiffs!")); + } + return; + } + + if(F("info") == str) { + showInfo(); + return; + } + + if(F("help") == str) { + Serial.print(_F("\r\n" + "available commands:\r\n" + " help - display this message\r\n" + " ip - show current ip address\r\n" + " connect - connect to wifi\r\n" + " restart - restart the device\r\n" + " switch - switch to the other rom and reboot\r\n" + " ota - perform ota update, switch rom and reboot\r\n" + " info - show device info\r\n")); + + if(spiffsPartition) { + Serial.print(_F(" ls - list files in spiffs\r\n" + " cat - show first file in spiffs\r\n")); } + Serial.println(); + return; } + + Serial << _F("unknown command: ") << str << endl; } +void serialCallBack(Stream& stream, char arrivedChar, unsigned short availableCharsCount) +{ + switch(commandBuffer.process(stream, Serial)) { + case commandBuffer.Action::submit: + if(commandBuffer) { + handleCommand(String(commandBuffer)); + commandBuffer.clear(); + } + showPrompt(); + break; + case commandBuffer.Action::clear: + showPrompt(); + break; + default:; + } +} + +} // namespace + void init() { Serial.begin(SERIAL_BAUD_RATE); // 115200 by default @@ -207,10 +249,14 @@ void init() } WifiAccessPoint.enable(false); + WifiEvents.onStationGotIP([](IpAddress ip, IpAddress netmask, IpAddress gateway) { showPrompt(); }); - Serial << _F("\r\nCurrently running ") << partition.name() << " @ 0x" << String(partition.address(), HEX) << '.' + Serial << endl + << _F("Currently running ") << partition.name() << " @ 0x" << String(partition.address(), HEX) << '.' << endl + << _F("Type 'help' and press enter for instructions.") << endl << endl; - Serial << _F("Type 'help' and press enter for instructions.") << endl << endl; + + showPrompt(); Serial.onDataReceived(serialCallBack); } From f40bf5f2354c1b607352c433d53db9a37aa5cdc4 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 19 Mar 2024 21:15:52 +0000 Subject: [PATCH 6/6] FlashIP --- Sming/Libraries/FlashIP | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sming/Libraries/FlashIP b/Sming/Libraries/FlashIP index 5f6d552d7d..820d08aab8 160000 --- a/Sming/Libraries/FlashIP +++ b/Sming/Libraries/FlashIP @@ -1 +1 @@ -Subproject commit 5f6d552d7d18f80bbbce7cd891fdce9026802953 +Subproject commit 820d08aab817dde262d5d5f9fb453dca88b374e9