diff options
author | David Crocker <dcrocker@eschertech.com> | 2017-11-09 18:15:55 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2017-11-09 18:16:08 +0300 |
commit | 2d79e7afdd4a155c71d376de593fcb0731ce8911 (patch) | |
tree | 62fb99906c96755aed19ab9f1e489d569d103490 /src | |
parent | 90d307da50d49f19e1f746cacc9c938022cc8c7e (diff) |
Towards 1.20beta7
String parameters can now be quoted in all G- and M-commands
Added partial support for standstill current reduction (M917)
M0 and M1 no longe turns anything off or run any macros when in
simulation mode
Diffstat (limited to 'src')
-rw-r--r-- | src/Duet/Webserver.cpp | 76 | ||||
-rw-r--r-- | src/DuetNG/DuetWiFi/Network.cpp | 25 | ||||
-rw-r--r-- | src/DuetNG/DuetWiFi/Network.h | 4 | ||||
-rw-r--r-- | src/DuetNG/DuetWiFi/WifiFirmwareUploader.cpp | 2 | ||||
-rw-r--r-- | src/DuetNG/FirmwareUpdater.cpp | 2 | ||||
-rw-r--r-- | src/DuetNG/FtpResponder.cpp | 49 | ||||
-rw-r--r-- | src/DuetNG/HttpResponder.cpp | 41 | ||||
-rw-r--r-- | src/DuetNG/HttpResponder.h | 1 | ||||
-rw-r--r-- | src/DuetNG/NetworkResponder.h | 2 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer.cpp | 187 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer.h | 10 | ||||
-rw-r--r-- | src/GCodes/GCodes.cpp | 71 | ||||
-rw-r--r-- | src/GCodes/GCodes.h | 1 | ||||
-rw-r--r-- | src/GCodes/GCodes2.cpp | 279 | ||||
-rw-r--r-- | src/Libraries/General/StringRef.h | 12 | ||||
-rw-r--r-- | src/Movement/Kinematics/ScaraKinematics.cpp | 22 | ||||
-rw-r--r-- | src/Platform.cpp | 56 | ||||
-rw-r--r-- | src/Platform.h | 9 | ||||
-rw-r--r-- | src/RepRap.cpp | 38 | ||||
-rw-r--r-- | src/RepRap.h | 2 | ||||
-rw-r--r-- | src/Storage/MassStorage.cpp | 28 | ||||
-rw-r--r-- | src/Version.h | 4 |
22 files changed, 516 insertions, 405 deletions
diff --git a/src/Duet/Webserver.cpp b/src/Duet/Webserver.cpp index 0351e85a..a2ba0f9c 100644 --- a/src/Duet/Webserver.cpp +++ b/src/Duet/Webserver.cpp @@ -378,7 +378,7 @@ bool ProtocolInterpreter::StartUpload(FileStore *file, const char *fileName) if (file != nullptr) { fileBeingUploaded.Set(file); - strncpy(filenameBeingUploaded, fileName, ARRAY_SIZE(filenameBeingUploaded)); + SafeStrncpy(filenameBeingUploaded, fileName, ARRAY_SIZE(filenameBeingUploaded)); filenameBeingUploaded[ARRAY_UPB(filenameBeingUploaded)] = 0; uploadState = uploadOK; @@ -617,7 +617,7 @@ void Webserver::HttpInterpreter::DoFastUpload() // Grab a copy of the filename and finish this upload char filename[FILENAME_LENGTH]; - strncpy(filename, filenameBeingUploaded, FILENAME_LENGTH); + SafeStrncpy(filename, filenameBeingUploaded, FILENAME_LENGTH); FinishUpload(postFileLength); // Update the file timestamp if it was specified before @@ -825,7 +825,7 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command) { const char *configPath = platform->GetMassStorage()->CombineName(platform->GetSysDir(), platform->GetConfigFile()); char fileName[FILENAME_LENGTH]; - strncpy(fileName, configPath, FILENAME_LENGTH); + SafeStrncpy(fileName, configPath, FILENAME_LENGTH); SendFile(fileName, false); return; @@ -978,7 +978,7 @@ void Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff } else if (StringEquals(request, "delete") && GetKeyValue("name") != nullptr) { - bool ok = platform->GetMassStorage()->Delete(FS_PREFIX, GetKeyValue("name")); + const bool ok = platform->GetMassStorage()->Delete(FS_PREFIX, GetKeyValue("name")); response->printf("{\"err\":%d}", (ok) ? 0 : 1); } else if (StringEquals(request, "filelist") && GetKeyValue("dir") != nullptr) @@ -1011,7 +1011,7 @@ void Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff if (nameVal != nullptr) { // Regular rr_fileinfo?name=xxx call - strncpy(filenameBeingProcessed, nameVal, ARRAY_SIZE(filenameBeingProcessed)); + SafeStrncpy(filenameBeingProcessed, nameVal, ARRAY_SIZE(filenameBeingProcessed)); filenameBeingProcessed[ARRAY_UPB(filenameBeingProcessed)] = 0; } else @@ -1031,17 +1031,22 @@ void Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff bool success = false; if (oldVal != nullptr && newVal != nullptr) { - success = platform->GetMassStorage()->Rename(oldVal, newVal); + MassStorage * const ms = platform->GetMassStorage(); + if (StringEquals(GetKeyValue("deleteexisting"), "yes") && ms->FileExists(oldVal) && ms->FileExists(newVal)) + { + ms->Delete(nullptr, newVal, true); + } + success = ms->Rename(oldVal, newVal); } response->printf("{\"err\":%d}", (success) ? 0 : 1); } else if (StringEquals(request, "mkdir")) { - const char* dirVal = GetKeyValue("dir"); + const char* const dirVal = GetKeyValue("dir"); bool success = false; if (dirVal != nullptr) { - success = (platform->GetMassStorage()->MakeDirectory(dirVal)); + success = platform->GetMassStorage()->MakeDirectory(dirVal); } response->printf("{\"err\":%d}", (success) ? 0 : 1); } @@ -2121,7 +2126,7 @@ void Webserver::FtpInterpreter::ProcessLine() if (filename[0] != '/') { const char *temp = platform->GetMassStorage()->CombineName(currentDir, filename); - strncpy(filename, temp, FILENAME_LENGTH); + SafeStrncpy(filename, temp, FILENAME_LENGTH); filename[FILENAME_LENGTH - 1] = 0; } @@ -2138,7 +2143,7 @@ void Webserver::FtpInterpreter::ProcessLine() { // Copy origin path to temp oldFilename and read new path char oldFilename[FILENAME_LENGTH]; - strncpy(oldFilename, filename, FILENAME_LENGTH); + SafeStrncpy(oldFilename, filename, FILENAME_LENGTH); oldFilename[FILENAME_LENGTH - 1] = 0; ReadFilename(4); @@ -2387,41 +2392,42 @@ void Webserver::FtpInterpreter::ChangeDirectory(const char *newDirectory) /* Prepare the new directory path */ if (newDirectory[0] == '/') // absolute path { - strncpy(combinedPath, newDirectory, FILENAME_LENGTH); + SafeStrncpy(combinedPath, newDirectory, FILENAME_LENGTH); combinedPath[FILENAME_LENGTH - 1] = 0; } - else // relative path + else if (StringEquals(newDirectory, ".")) { - if (StringEquals(newDirectory, "..")) // go up + SafeStrncpy(combinedPath, currentDir, ARRAY_SIZE(combinedPath)); + } + else if (StringEquals(newDirectory, "..")) // go up + { + if (StringEquals(currentDir, "/")) { - if (StringEquals(currentDir, "/")) - { - // we're already at the root, so we can't go up any more - SendReply(550, "Failed to change directory."); - return; - } - else + // we're already at the root, so we can't go up any more + SendReply(550, "Failed to change directory."); + return; + } + else + { + SafeStrncpy(combinedPath, currentDir, FILENAME_LENGTH); + for(int i=strlen(combinedPath) -2; i>=0; i--) { - strncpy(combinedPath, currentDir, FILENAME_LENGTH); - for(int i=strlen(combinedPath) -2; i>=0; i--) + if (combinedPath[i] == '/') { - if (combinedPath[i] == '/') - { - combinedPath[i +1] = 0; - break; - } + combinedPath[i +1] = 0; + break; } } } - else // go to child directory + } + else // go to child directory + { + SafeStrncpy(combinedPath, currentDir, FILENAME_LENGTH); + if (strlen(currentDir) > 1) { - strncpy(combinedPath, currentDir, FILENAME_LENGTH); - if (strlen(currentDir) > 1) - { - strncat(combinedPath, "/", FILENAME_LENGTH - strlen(combinedPath) - 1); - } - strncat(combinedPath, newDirectory, FILENAME_LENGTH - strlen(combinedPath) - 1); + SafeStrncat(combinedPath, "/", FILENAME_LENGTH); } + SafeStrncat(combinedPath, newDirectory, FILENAME_LENGTH); } /* Make sure the new path does not end with a '/', because FatFs won't see the directory otherwise */ @@ -2433,7 +2439,7 @@ void Webserver::FtpInterpreter::ChangeDirectory(const char *newDirectory) /* Verify path and change it */ if (platform->GetMassStorage()->DirectoryExists(combinedPath)) { - strncpy(currentDir, combinedPath, FILENAME_LENGTH); + SafeStrncpy(currentDir, combinedPath, FILENAME_LENGTH); SendReply(250, "Directory successfully changed."); } else diff --git a/src/DuetNG/DuetWiFi/Network.cpp b/src/DuetNG/DuetWiFi/Network.cpp index f4c9a2d2..c776fc7c 100644 --- a/src/DuetNG/DuetWiFi/Network.cpp +++ b/src/DuetNG/DuetWiFi/Network.cpp @@ -103,7 +103,7 @@ static inline void DisableEspInterrupt() // WiFi interface class Network::Network(Platform& p) : platform(p), nextResponderToPoll(nullptr), uploader(nullptr), currentSocket(0), ftpDataPort(0), - state(NetworkState::disabled), requestedMode(WiFiState::disabled), currentMode(WiFiState::disabled), activated(false), + state(NetworkState::disabled), requestedMode(WiFiState::disabled), currentMode(WiFiState::disabled), activated(false), serialRunning(false), espStatusChanged(false), spiTxUnderruns(0), spiRxOverruns(0) { for (size_t i = 0; i < NumProtocols; ++i) @@ -358,7 +358,7 @@ void Network::Start() delay(50); // Release the reset on the ESP8266 - digitalWrite(EspResetPin, HIGH); + StartWiFi(); // Give it time to sample GPIO0 and GPIO15 // GPIO0 has to be held high for sufficient time: @@ -1380,6 +1380,15 @@ void Network::SpiInterrupt() } } +// Start the ESP +void Network::StartWiFi() +{ + digitalWrite(EspResetPin, HIGH); + ConfigurePin(g_APinDescription[APINS_UART1]); // connect the pins to UART1 + Serial1.begin(115200); // initialise the UART, to receive debug info + serialRunning = true; +} + // Reset the ESP8266 and leave held in reset void Network::ResetWiFi() { @@ -1387,6 +1396,12 @@ void Network::ResetWiFi() pinMode(APIN_UART1_TXD, INPUT_PULLUP); // just enable pullups on TxD and RxD pins for now to avoid floating pins pinMode(APIN_UART1_RXD, INPUT_PULLUP); currentMode = WiFiState::disabled; + + if (serialRunning) + { + Serial1.end(); + serialRunning = false; + } } // Reset the ESP8266 to take commands from the UART or from external input. The caller must wait for the reset to complete after calling this. @@ -1397,6 +1412,12 @@ void Network::ResetWiFi() // 0 0 1 SD card boot (not used in on Duet) void Network::ResetWiFiForUpload(bool external) { + if (serialRunning) + { + Serial1.end(); + serialRunning = false; + } + // Make sure the ESP8266 is in the reset state pinMode(EspResetPin, OUTPUT_LOW); diff --git a/src/DuetNG/DuetWiFi/Network.h b/src/DuetNG/DuetWiFi/Network.h index fa89cb0b..c2081895 100644 --- a/src/DuetNG/DuetWiFi/Network.h +++ b/src/DuetNG/DuetWiFi/Network.h @@ -74,8 +74,9 @@ public: // The remaining functions are specific to the WiFi version WifiFirmwareUploader& GetWifiUploader() { return *uploader; } + void StartWiFi(); void ResetWiFi(); - static void ResetWiFiForUpload(bool external); + void ResetWiFiForUpload(bool external); const char *GetWiFiServerVersion() const { return wiFiServerVersion; } @@ -146,6 +147,7 @@ private: WiFiState requestedMode; WiFiState currentMode; bool activated; + bool serialRunning; volatile bool espStatusChanged; uint8_t ipAddress[4]; diff --git a/src/DuetNG/DuetWiFi/WifiFirmwareUploader.cpp b/src/DuetNG/DuetWiFi/WifiFirmwareUploader.cpp index 8a30af16..e2adb76b 100644 --- a/src/DuetNG/DuetWiFi/WifiFirmwareUploader.cpp +++ b/src/DuetNG/DuetWiFi/WifiFirmwareUploader.cpp @@ -586,7 +586,7 @@ void WifiFirmwareUploader::Spin() } uploadPort.begin(baud); uploadPort.setInterruptPriority(1); // we are going to move data at seriously high speeds - Network::ResetWiFiForUpload(false); + reprap.GetNetwork().ResetWiFiForUpload(false); lastAttemptTime = lastResetTime = millis(); state = UploadState::connecting; } diff --git a/src/DuetNG/FirmwareUpdater.cpp b/src/DuetNG/FirmwareUpdater.cpp index 3ed7bf74..1f3b2384 100644 --- a/src/DuetNG/FirmwareUpdater.cpp +++ b/src/DuetNG/FirmwareUpdater.cpp @@ -62,7 +62,7 @@ namespace FirmwareUpdater switch(module) { case WifiExternalFirmwareModule: - Network::ResetWiFiForUpload(true); + reprap.GetNetwork().ResetWiFiForUpload(true); break; case WifiFirmwareModule: diff --git a/src/DuetNG/FtpResponder.cpp b/src/DuetNG/FtpResponder.cpp index f849370b..5c96b30f 100644 --- a/src/DuetNG/FtpResponder.cpp +++ b/src/DuetNG/FtpResponder.cpp @@ -819,38 +819,39 @@ void FtpResponder::ChangeDirectory(const char *newDirectory) { SafeStrncpy(combinedPath, newDirectory, ARRAY_SIZE(combinedPath)); } - else // Relative path + else if (StringEquals(newDirectory, ".")) { - if (StringEquals(newDirectory, "..")) // Go up + SafeStrncpy(combinedPath, currentDirectory, ARRAY_SIZE(combinedPath)); + } + else if (StringEquals(newDirectory, "..")) // Go up + { + // Check if we're already at the root directory + if (StringEquals(currentDirectory, "/")) { - // Check if we're already at the root directory - if (StringEquals(currentDirectory, "/")) - { - outBuf->copy("550 Failed to change directory.\r\n"); - Commit(responderState); - return; - } + outBuf->copy("550 Failed to change directory.\r\n"); + Commit(responderState); + return; + } - // No - find the parent directory - SafeStrncpy(combinedPath, currentDirectory, ARRAY_SIZE(combinedPath)); - for(int i = strlen(combinedPath) - 2; i >= 0; i--) + // No - find the parent directory + SafeStrncpy(combinedPath, currentDirectory, ARRAY_SIZE(combinedPath)); + for(int i = strlen(combinedPath) - 2; i >= 0; i--) + { + if (combinedPath[i] == '/') { - if (combinedPath[i] == '/') - { - combinedPath[i + 1] = 0; - break; - } + combinedPath[i + 1] = 0; + break; } } - else // Go to child directory + } + else // Go to child directory + { + SafeStrncpy(combinedPath, currentDirectory, ARRAY_SIZE(combinedPath)); + if (!StringEndsWith(combinedPath, "/") && strlen(combinedPath) > 1) { - SafeStrncpy(combinedPath, currentDirectory, ARRAY_SIZE(combinedPath)); - if (!StringEndsWith(combinedPath, "/") && strlen(combinedPath) > 1) - { - SafeStrncat(combinedPath, "/", ARRAY_SIZE(combinedPath)); - } - SafeStrncat(combinedPath, newDirectory, ARRAY_SIZE(combinedPath)); + SafeStrncat(combinedPath, "/", ARRAY_SIZE(combinedPath)); } + SafeStrncat(combinedPath, newDirectory, ARRAY_SIZE(combinedPath)); } // Make sure the new path does not end with a slash, else FatFs won't be able to see the directory diff --git a/src/DuetNG/HttpResponder.cpp b/src/DuetNG/HttpResponder.cpp index d48e57d8..df11cbfb 100644 --- a/src/DuetNG/HttpResponder.cpp +++ b/src/DuetNG/HttpResponder.cpp @@ -67,21 +67,15 @@ bool HttpResponder::Spin() case ResponderState::reading: { bool readSomething = false; - for (;;) + char c; + while (skt->ReadChar(c)) { - char c; - if (skt->ReadChar(c)) + if (CharFromClient(c)) { - if (CharFromClient(c)) - { - return true; - } - readSomething = true; - } - else - { - break; + timer = millis(); // restart the timeout + return true; } + readSomething = true; } // Here when we were not able to read a character but we didn't receive a finished message @@ -522,7 +516,7 @@ bool HttpResponder::GetJsonResponse(const char* request, OutputBuffer *&response } else if (StringEquals(request, "delete") && GetKeyValue("name") != nullptr) { - bool ok = GetPlatform().GetMassStorage()->Delete(FS_PREFIX, GetKeyValue("name")); + const bool ok = GetPlatform().GetMassStorage()->Delete(FS_PREFIX, GetKeyValue("name")); response->printf("{\"err\":%d}", (ok) ? 0 : 1); } else if (StringEquals(request, "filelist") && GetKeyValue("dir") != nullptr) @@ -565,17 +559,22 @@ bool HttpResponder::GetJsonResponse(const char* request, OutputBuffer *&response bool success = false; if (oldVal != nullptr && newVal != nullptr) { - success = GetPlatform().GetMassStorage()->Rename(oldVal, newVal); + MassStorage * const ms = GetPlatform().GetMassStorage(); + if (StringEquals(GetKeyValue("deleteexisting"), "yes") && ms->FileExists(oldVal) && ms->FileExists(newVal)) + { + ms->Delete(nullptr, newVal, true); + } + success = ms->Rename(oldVal, newVal); } response->printf("{\"err\":%d}", (success) ? 0 : 1); } else if (StringEquals(request, "mkdir")) { - const char* dirVal = GetKeyValue("dir"); + const char* const dirVal = GetKeyValue("dir"); bool success = false; if (dirVal != nullptr) { - success = (GetPlatform().GetMassStorage()->MakeDirectory(dirVal)); + success = GetPlatform().GetMassStorage()->MakeDirectory(dirVal); } response->printf("{\"err\":%d}", (success) ? 0 : 1); } @@ -1173,6 +1172,16 @@ void HttpResponder::CancelUpload() NetworkResponder::CancelUpload(); } +// This overrides the version in class NetworkResponder +void HttpResponder::SendData() +{ + NetworkResponder::SendData(); + if (responderState == ResponderState::reading) + { + timer = millis(); // restart the timer + } +} + void HttpResponder::Diagnostics(MessageType mt) const { GetPlatform().MessageF(mt, " HTTP(%d)", (int)responderState); diff --git a/src/DuetNG/HttpResponder.h b/src/DuetNG/HttpResponder.h index a11f561a..e1e84416 100644 --- a/src/DuetNG/HttpResponder.h +++ b/src/DuetNG/HttpResponder.h @@ -28,6 +28,7 @@ public: protected: void ConnectionLost() override; void CancelUpload() override; + void SendData() override; private: static const size_t MaxHttpSessions = 8; // maximum number of simultaneous HTTP sessions diff --git a/src/DuetNG/NetworkResponder.h b/src/DuetNG/NetworkResponder.h index 6e4ace6c..92187380 100644 --- a/src/DuetNG/NetworkResponder.h +++ b/src/DuetNG/NetworkResponder.h @@ -46,7 +46,7 @@ protected: // States machine control. Not all derived classes use all states. enum class ResponderState { - free, // ready to be allocated + free = 0, // ready to be allocated reading, // ready to receive data sending, // sending data uploading, // uploading a file to SD card diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp index 666731b5..c416d93e 100644 --- a/src/GCodes/GCodeBuffer.cpp +++ b/src/GCodes/GCodeBuffer.cpp @@ -523,67 +523,53 @@ const void GCodeBuffer::GetLongArray(long l[], size_t& returnedLength) } } -// Get a string after a G Code letter found by a call to Seen(). -// It will be the whole of the rest of the GCode string, so strings should always be the last parameter. -// Use the other overload of GetString to get strings that may not be the last parameter, or may be quoted. -const char* GCodeBuffer::GetString() +// Get and copy a quoted string returning true if successful +bool GCodeBuffer::GetQuotedString(const StringRef& str) { + str.Clear(); if (readPointer >= 0) { - commandEnd = gcodeLineEnd; // the string is the remainder of the line of gcode - const char* const result = &gcodeBuffer[readPointer + 1]; - readPointer = -1; - return result; + ++readPointer; // skip the character that introduced the string + return gcodeBuffer[readPointer] == '"' && InternalGetQuotedString(str); } INTERNAL_ERROR; - return ""; + return false; } -// Get and copy a quoted string returning true if successful -bool GCodeBuffer::GetQuotedString(const StringRef& str) +// Given that the current character is double-quote, fetch the quoted string +bool GCodeBuffer::InternalGetQuotedString(const StringRef& str) { - str.Clear(); - if (readPointer >= 0) + ++readPointer; + for (;;) { - ++readPointer; // skip the character that introduced the string - if (gcodeBuffer[readPointer] == '"') + char c = gcodeBuffer[readPointer++]; + if (c < ' ') + { + return false; + } + if (c == '"') { - ++readPointer; - for (;;) + if (gcodeBuffer[readPointer++] != '"') { - char c = gcodeBuffer[readPointer++]; - if (c < ' ') - { - return false; - } - if (c == '"') - { - if (gcodeBuffer[readPointer++] != '"') - { - return true; - } - } - else if (c == '\'') - { - if (isalpha(gcodeBuffer[readPointer])) - { - // Single quote before an alphabetic character forces that character to lower case - c = tolower(gcodeBuffer[readPointer++]); - } - else if (gcodeBuffer[readPointer] == c) - { - // Two single quotes are used to represent one - ++readPointer; - } - } - str.cat(c); + return true; } } - return false; + else if (c == '\'') + { + if (isalpha(gcodeBuffer[readPointer])) + { + // Single quote before an alphabetic character forces that character to lower case + c = tolower(gcodeBuffer[readPointer++]); + } + else if (gcodeBuffer[readPointer] == c) + { + // Two single quotes are used to represent one + ++readPointer; + } + } + str.cat(c); } - - INTERNAL_ERROR; return false; } @@ -592,54 +578,51 @@ bool GCodeBuffer::GetPossiblyQuotedString(const StringRef& str) { if (readPointer >= 0) { - if (gcodeBuffer[readPointer + 1] == '"') - { - return GetQuotedString(str); - } - - commandEnd = gcodeLineEnd; // the string is the remainder of the line of gcode - str.Clear(); - for (;;) - { - ++readPointer; - const char c = gcodeBuffer[readPointer]; - if (c < ' ') - { - break; - } - str.cat(c); - } - str.StripTrailingSpaces(); - return !str.IsEmpty(); + ++readPointer; + return InternalGetPossiblyQuotedString(str); } INTERNAL_ERROR; return false; } -// This returns a pointer to the end of the buffer where a string starts. +// Get and copy a string which may or may not be quoted, starting at readPointer +bool GCodeBuffer::InternalGetPossiblyQuotedString(const StringRef& str) +{ + str.Clear(); + if (gcodeBuffer[readPointer] == '"') + { + return InternalGetQuotedString(str); + } + + commandEnd = gcodeLineEnd; // the string is the remainder of the line of gcode + for (;;) + { + const char c = gcodeBuffer[readPointer++]; + if (c < ' ') + { + break; + } + str.cat(c); + } + str.StripTrailingSpaces(); + return !str.IsEmpty(); +} + +// This returns a string comprising the rest of the line, excluding any comment // It is provided for legacy use, in particular in the M23 // command that sets the name of a file to be printed. In // preference use GetString() which requires the string to have // been preceded by a tag letter. -// If no string was provided, it produces an error message if the string was not optional, and returns nullptr. -const char* GCodeBuffer::GetUnprecedentedString(bool optional) +bool GCodeBuffer::GetUnprecedentedString(const StringRef& str) { - commandEnd = gcodeLineEnd; // the string is the remainder of the line - size_t i; + readPointer = parameterStart; char c; - for (i = parameterStart; i < commandEnd && ((c = gcodeBuffer[i]) == ' ' || c == '\t'); ++i) { } - - if (i == commandEnd) + while ((unsigned int)readPointer < commandEnd && ((c = gcodeBuffer[readPointer]) == ' ' || c == '\t')) { - if (!optional) - { - reprap.GetPlatform().MessageF(ErrorMessage, "%c%d: String expected but not seen.\n", commandLetter, commandNumber); - } - return nullptr; + ++readPointer; // skip leading spaces } - - return &gcodeBuffer[i]; + return InternalGetPossiblyQuotedString(str); } // Get an int32 after a G Code letter @@ -802,6 +785,44 @@ bool GCodeBuffer::GetIPAddress(uint32_t& ip) return ok; } +// Get a MAX address sextet after a key letter +bool GCodeBuffer::GetMacAddress(uint8_t mac[6]) +{ + if (readPointer < 0) + { + INTERNAL_ERROR; + return false; + } + + const char* p = &gcodeBuffer[readPointer + 1]; + unsigned int n = 0; + for (;;) + { + char *pp; + const unsigned long v = strtoul(p, &pp, 16); + if (pp == p || v > 255) + { + readPointer = -1; + return false; + } + mac[n] = (uint8_t)v; + ++n; + p = pp; + if (*p != ':') + { + break; + } + if (n == 6) + { + readPointer = -1; + return false; + } + ++p; + } + readPointer = -1; + return n == 6; +} + // Get the original machine state before we pushed anything GCodeMachineState& GCodeBuffer::OriginalMachineState() const { @@ -882,4 +903,10 @@ void GCodeBuffer::MessageAcknowledged(bool cancelled) } } +// Return true if we can queue gcodes from this source +bool GCodeBuffer::CanQueueCodes() const +{ + return queueCodes || machineState->doingFileMacro; // return true if we queue commands form this source or we are executing a macro +} + // End diff --git a/src/GCodes/GCodeBuffer.h b/src/GCodes/GCodeBuffer.h index f8a71b61..0846a007 100644 --- a/src/GCodes/GCodeBuffer.h +++ b/src/GCodes/GCodeBuffer.h @@ -35,8 +35,8 @@ public: uint32_t GetUIValue(); // Get an unsigned integer value bool GetIPAddress(uint8_t ip[4]); // Get an IP address quad after a key letter bool GetIPAddress(uint32_t& ip); // Get an IP address quad after a key letter - const char* GetUnprecedentedString(bool optional = false); // Get a string with no preceding key letter - const char* GetString(); // Get an unquoted string after a key letter + bool GetMacAddress(uint8_t mac[6]); // Get a MAX address sextet after a key letter + bool GetUnprecedentedString(const StringRef& str); // Get a string with no preceding key letter bool GetQuotedString(const StringRef& str); // Get and copy a quoted string bool GetPossiblyQuotedString(const StringRef& str); // Get and copy a string which may or may not be quoted const void GetFloatArray(float a[], size_t& length, bool doPad) __attribute__((hot)); // Get a :-separated list of floats after a key letter @@ -71,7 +71,7 @@ public: void SetState(GCodeState newState); void AdvanceState(); const char *GetIdentity() const { return identity; } - const bool CanQueueCodes() const { return queueCodes; } + bool CanQueueCodes() const; void MessageAcknowledged(bool cancelled); FilePosition GetFilePosition(size_t bytesCached) const; // Get the file position at the start of the current command bool IsWritingBinary() const; // returns true if writing binary @@ -102,6 +102,10 @@ private: void StoreAndAddToChecksum(char c); bool LineFinished(); // Deal with receiving end-of-line and return true if we have a command void DecodeCommand(); + bool InternalGetQuotedString(const StringRef& str) + pre (gcodeBuffer[readPointer] == '"'; str.IsEmpty()); + bool InternalGetPossiblyQuotedString(const StringRef& str) + pre (readPointer >= 0); GCodeMachineState *machineState; // Machine state for this gcode source char gcodeBuffer[GCODE_LENGTH]; // The G Code diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 72a10788..dfb93a62 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -630,26 +630,29 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, StringRef& reply) case GCodeState::stopping: // MO after executing stop.g if present case GCodeState::sleeping: // M1 after executing sleep.g if present - // Deselect the active tool and turn off all heaters, unless parameter Hn was used with n > 0 - if (!gb.Seen('H') || gb.GetIValue() <= 0) + if (simulationMode == 0) { - Tool* tool = reprap.GetCurrentTool(); - if (tool != nullptr) + // Deselect the active tool and turn off all heaters, unless parameter Hn was used with n > 0 + if (!gb.Seen('H') || gb.GetIValue() <= 0) { - reprap.StandbyTool(tool->Number()); + Tool* tool = reprap.GetCurrentTool(); + if (tool != nullptr) + { + reprap.StandbyTool(tool->Number()); + } + reprap.GetHeat().SwitchOffAll(); } - reprap.GetHeat().SwitchOffAll(); - } - // chrishamm 2014-18-10: Although RRP says M0 is supposed to turn off all drives and heaters, - // I think M1 is sufficient for this purpose. Leave M0 for a normal reset. - if (gb.GetState() == GCodeState::sleeping) - { - DisableDrives(); - } - else - { - platform.SetDriversIdle(); + // chrishamm 2014-18-10: Although RRP says M0 is supposed to turn off all drives and heaters, + // I think M1 is sufficient for this purpose. Leave M0 for a normal reset. + if (gb.GetState() == GCodeState::sleeping) + { + DisableDrives(); + } + else + { + platform.SetDriversIdle(); + } } gb.SetState(GCodeState::normal); break; @@ -3376,42 +3379,6 @@ void GCodes::DisableDrives() SetAllAxesNotHomed(); } -void GCodes::SetMACAddress(GCodeBuffer& gb) -{ - uint8_t mac[6]; - const char* ipString = gb.GetString(); - uint8_t sp = 0; - uint8_t spp = 0; - uint8_t ipp = 0; - while (ipString[sp]) - { - if (ipString[sp] == ':') - { - mac[ipp] = strtoul(&ipString[spp], nullptr, 16); - ipp++; - if (ipp > 5) - { - break; - } - sp++; - spp = sp; - } - else - { - sp++; - } - } - if (ipp == 5) - { - mac[ipp] = strtoul(&ipString[spp], nullptr, 16); - platform.SetMACAddress(mac); - } - else - { - platform.MessageF(ErrorMessage, "Dud MAC address: %s\n", gb.Buffer()); - } -} - bool GCodes::ChangeMicrostepping(size_t drive, int microsteps, int mode) const { bool dummy; diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index f3ce9b91..e44f7756 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -246,7 +246,6 @@ private: bool Push(GCodeBuffer& gb); // Push feedrate etc on the stack void Pop(GCodeBuffer& gb); // Pop feedrate etc void DisableDrives(); // Turn the motors off - void SetMACAddress(GCodeBuffer& gb); // Deals with an M540 void HandleReply(GCodeBuffer& gb, bool error, const char *reply); // Handle G-Code replies void HandleReply(GCodeBuffer& gb, bool error, OutputBuffer *reply); bool OpenFileToWrite(GCodeBuffer& gb, const char* directory, const char* fileName, const FilePosition size, const bool binaryWrite, const uint32_t fileCRC32); diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index 4d1bfec9..ff398b1a 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -294,7 +294,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } { const bool wasPaused = isPaused; // isPaused gets cleared by CancelPrint - StopPrint(&gb == fileGCode); // if this is normal end-of-print commanded by the file, deleted the resurrect.g file + StopPrint(&gb == fileGCode); // if this is normal end-of-print commanded by the file, delete the resurrect.g file // If we are cancelling a paused print with M0 and we are homed and cancel.g exists then run it and do nothing else if (wasPaused && code == 0 && AllAxesAreHomed() && DoFileMacro(gb, CANCEL_G, false)) @@ -443,11 +443,19 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { OutputBuffer *fileResponse; const int sparam = (gb.Seen('S')) ? gb.GetIValue() : 0; - const char* dir = (gb.Seen('P')) ? gb.GetString() : platform.GetGCodeDir(); + String<FILENAME_LENGTH> dir; + if (gb.Seen('P')) + { + gb.GetPossiblyQuotedString(dir.GetRef()); + } + else + { + dir.GetRef().copy(platform.GetGCodeDir()); + } if (sparam == 2) { - fileResponse = reprap.GetFilesResponse(dir, true); // Send the file list in JSON format + fileResponse = reprap.GetFilesResponse(dir.Pointer(), true); // Send the file list in JSON format fileResponse->cat('\n'); } else @@ -469,7 +477,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) bool encapsulateList = ((&gb != serialGCode && &gb != telnetGCode) || platform.Emulating() != marlin); FileInfo fileInfo; - if (platform.GetMassStorage()->FindFirst(dir, fileInfo)) + if (platform.GetMassStorage()->FindFirst(dir.Pointer(), fileInfo)) { // iterate through all entries and append each file name do { @@ -537,12 +545,12 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) return false; } { - const char* filename = gb.GetUnprecedentedString(); - if (filename != nullptr) + String<FILENAME_LENGTH> filename; + if (gb.GetUnprecedentedString(filename.GetRef())) { - if (QueueFileToPrint(filename, reply)) + if (QueueFileToPrint(filename.Pointer(), reply)) { - reprap.GetPrintMonitor().StartingPrint(filename); + reprap.GetPrintMonitor().StartingPrint(filename.Pointer()); if (platform.Emulating() == marlin && (&gb == serialGCode || &gb == telnetGCode)) { reply.copy("File opened\nFile selected"); @@ -550,7 +558,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) else { // Command came from web interface or PanelDue, or not emulating Marlin, so send a nicer response - reply.printf("File %s selected for printing", filename); + reply.printf("File %s selected for printing", filename.Pointer()); } if (code == 32) @@ -563,6 +571,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) result = GCodeResult::error; } } + else + { + reply.copy("Filename expected"); + result = GCodeResult::error; + } } break; @@ -697,20 +710,25 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 28: // Write to file { - const char* const str = gb.GetUnprecedentedString(); - if (str != nullptr) + String<FILENAME_LENGTH> filename; + if (gb.GetUnprecedentedString(filename.GetRef())) { - const bool ok = OpenFileToWrite(gb, platform.GetGCodeDir(), str, 0, false, 0); + const bool ok = OpenFileToWrite(gb, platform.GetGCodeDir(), filename.Pointer(), 0, false, 0); if (ok) { - reply.printf("Writing to file: %s", str); + reply.printf("Writing to file: %s", filename.Pointer()); } else { - reply.printf("Can't open file %s for writing.", str); + reply.printf("Can't open file %s for writing.", filename.Pointer()); result = GCodeResult::error; } } + else + { + reply.copy("Filename expected"); + result = GCodeResult::error; + } } break; @@ -720,10 +738,15 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 30: // Delete file { - const char *filename = gb.GetUnprecedentedString(); - if (filename != nullptr) + String<FILENAME_LENGTH> filename; + if (gb.GetUnprecedentedString(filename.GetRef())) { - platform.GetMassStorage()->Delete(platform.GetGCodeDir(), filename, false);; + platform.GetMassStorage()->Delete(platform.GetGCodeDir(), filename.Pointer(), false); + } + else + { + reply.copy("Filename expected"); + result = GCodeResult::error; } } break; @@ -736,9 +759,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) return false; } { - const char* filename = gb.GetUnprecedentedString(true); // get filename, or nullptr if none provided + String<FILENAME_LENGTH> filename; + const bool gotFilename = gb.GetUnprecedentedString(filename.GetRef()); OutputBuffer *fileInfoResponse; - bool done = reprap.GetPrintMonitor().GetFileInfoResponse(filename, fileInfoResponse); + const bool done = reprap.GetPrintMonitor().GetFileInfoResponse((gotFilename) ? filename.Pointer() : nullptr, fileInfoResponse); if (done) { fileInfoResponse->cat('\n'); @@ -835,15 +859,23 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (fileBeingHashed == nullptr) { // See if we can open the file and start hashing - const char* filename = gb.GetUnprecedentedString(true); - if (StartHash(filename)) + String<FILENAME_LENGTH> filename; + if (gb.GetUnprecedentedString(filename.GetRef())) { - // Hashing is now in progress... - result = GCodeResult::notFinished; + if (StartHash(filename.Pointer())) + { + // Hashing is now in progress... + result = GCodeResult::notFinished; + } + else + { + reply.printf("Cannot open file: %s", filename.Pointer()); + result = GCodeResult::error; + } } else { - reply.printf("Cannot open file: %s", filename); + reply.copy("Filename expected"); result = GCodeResult::error; } } @@ -973,7 +1005,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 98: // Call Macro/Subprogram if (gb.Seen('P')) { - DoFileMacro(gb, gb.GetString(), true); + String<FILENAME_LENGTH> filename; + gb.GetPossiblyQuotedString(filename.GetRef()); + DoFileMacro(gb, filename.Pointer(), true); } break; @@ -1254,8 +1288,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 117: // Display message { - const char *msg = gb.GetUnprecedentedString(true); - reprap.SetMessage((msg == nullptr) ? "" : msg); + String<FILENAME_LENGTH> msg; + gb.GetUnprecedentedString(msg.GetRef()); + reprap.SetMessage(msg.Pointer()); } break; @@ -1301,72 +1336,16 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } break; - case 140: // Set bed temperature - { - Heat& heat = reprap.GetHeat(); - int8_t bedHeater; - if (gb.Seen('H')) - { - bedHeater = gb.GetIValue(); - if (bedHeater < 0) - { - // Make sure we stay within reasonable boundaries... - bedHeater = -1; - } - else if (bedHeater >= (int)Heaters) - { - reply.printf("Invalid heater number '%d'", bedHeater); - result = GCodeResult::error; - break; - } - heat.SetBedHeater(bedHeater); - platform.UpdateConfiguredHeaters(); - - if (bedHeater < 0) - { - // Stop here if the hot bed has been disabled - break; - } - } - else - { - bedHeater = heat.GetBedHeater(); - if (bedHeater < 0) - { - reply.copy("Hot bed is not present"); - result = GCodeResult::error; - break; - } - } - - if (gb.Seen('S')) - { - const float temperature = gb.GetFValue(); - if (temperature < NEARLY_ABS_ZERO) - { - heat.SwitchOff(bedHeater); - } - else - { - heat.SetActiveTemperature(bedHeater, temperature); - heat.Activate(bedHeater); - } - } - if (gb.Seen('R')) - { - heat.SetStandbyTemperature(bedHeater, gb.GetFValue()); - } - } - break; - + case 140: // Bed temperature case 141: // Chamber temperature { Heat& heat = reprap.GetHeat(); bool seen = false; + + // See if the heater number is being set if (gb.Seen('H')) { seen = true; - int heater = gb.GetIValue(); if (heater < 0) { @@ -1374,23 +1353,35 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } else if (heater >= (int)Heaters) { - reply.printf("Bad heater number '%d'", heater); + reply.printf("Invalid heater number '%d'", heater); result = GCodeResult::error; break; } - else + + if (code == 141) { heat.SetChamberHeater(heater); - platform.UpdateConfiguredHeaters(); } + else + { + heat.SetBedHeater(heater); + } + platform.UpdateConfiguredHeaters(); } + const int8_t currentHeater = (code == 141) ? heat.GetChamberHeater() : heat.GetBedHeater(); + const char* heaterName = (code == 141) ? "chamber" : "bed"; + + // Active temperature if (gb.Seen('S')) { seen = true; - - const int8_t currentHeater = heat.GetChamberHeater(); - if (currentHeater != -1) + if (currentHeater < 0) + { + reply.printf("No %s heater has been configured", heaterName); + result = GCodeResult::error; + } + else { const float temperature = gb.GetFValue(); if (temperature < NEARLY_ABS_ZERO) @@ -1403,23 +1394,32 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) heat.Activate(currentHeater); } } - else + } + + if (gb.Seen('R')) + { + seen = true; + if (currentHeater < 0) { - reply.copy("No chamber heater has been configured"); + reply.printf("No %s heater has been configured", heaterName); result = GCodeResult::error; } + else + { + heat.SetStandbyTemperature(currentHeater, gb.GetFValue()); + } } if (!seen) { - const int8_t currentHeater = reprap.GetHeat().GetChamberHeater(); - if (currentHeater != -1) + if (currentHeater < 0) { - reply.printf("Chamber heater %d is currently at %.1f" DEGREE_SYMBOL "C", currentHeater, (double)reprap.GetHeat().GetTemperature(currentHeater)); + reply.printf("No %s heater has been configured", heaterName); } else { - reply.copy("No chamber heater has been configured"); + reply.printf("%c%s heater %d is currently at %.1f" DEGREE_SYMBOL "C", + toupper(heaterName[0]), heaterName + 1, currentHeater, (double)reprap.GetHeat().GetTemperature(currentHeater)); } } } @@ -2360,12 +2360,21 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 540: // Set/report MAC address if (gb.Seen('P')) { - SetMACAddress(gb); + uint8_t mac[6]; + if (gb.GetMacAddress(mac)) + { + platform.SetMACAddress(mac); + } + else + { + reply.copy("Bad MAC address"); + result = GCodeResult::error; + } } else { - const byte* mac = platform.MACAddress(); - reply.printf("MAC: %x:%x:%x:%x:%x:%x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + const uint8_t *mac = platform.MACAddress(); + reply.printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } break; @@ -2620,17 +2629,25 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) folder = platform.GetWebDir(); defaultFile = INDEX_PAGE_FILE; } - const char* filename = (gb.Seen('P') ? gb.GetString() : defaultFile); + String<FILENAME_LENGTH> filename; + if (gb.Seen('P')) + { + gb.GetPossiblyQuotedString(filename.GetRef()); + } + else + { + filename.GetRef().copy(defaultFile); + } const FilePosition size = (gb.Seen('S') ? (FilePosition)gb.GetIValue() : 0); const uint32_t crc32 = (gb.Seen('C') ? gb.GetUIValue() : 0); - const bool ok = OpenFileToWrite(gb, folder, filename, size, true, crc32); + const bool ok = OpenFileToWrite(gb, folder, filename.Pointer(), size, true, crc32); if (ok) { - reply.printf("Writing to file: %s", filename); + reply.printf("Writing to file: %s", filename.Pointer()); } else { - reply.printf("Can't open file %s for writing.", filename); + reply.printf("Can't open file %s for writing.", filename.Pointer()); result = GCodeResult::error; } } @@ -3926,7 +3943,8 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 752: // Start 3D scan if (gb.Seen('P')) { - const char *file = gb.GetString(); + String<FILENAME_LENGTH> file; + gb.GetPossiblyQuotedString(file.GetRef()); if (gb.Seen('S')) { const int sParam = gb.GetIValue(); @@ -3934,7 +3952,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { if (reprap.GetScanner().IsRegistered()) { - result = GetGCodeResultFromFinished(reprap.GetScanner().StartScan(file, sParam)); + result = GetGCodeResultFromFinished(reprap.GetScanner().StartScan(file.Pointer(), sParam)); } else { @@ -4066,8 +4084,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) seen = true; // Set date - const char * const dateString = gb.GetString(); - if (strptime(dateString, "%Y-%m-%d", &timeInfo) == nullptr) + String<12> dateString; + gb.GetPossiblyQuotedString(dateString.GetRef()); + if (strptime(dateString.Pointer(), "%Y-%m-%d", &timeInfo) == nullptr) { reply.copy("Invalid date format"); result = GCodeResult::error; @@ -4080,8 +4099,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) seen = true; // Set time - const char * const timeString = gb.GetString(); - if (strptime(timeString, "%H:%M:%S", &timeInfo) == nullptr) + String<12> timeString; + gb.GetPossiblyQuotedString(timeString.GetRef()); + if (strptime(timeString.Pointer(), "%H:%M:%S", &timeInfo) == nullptr) { reply.copy("Invalid time format"); result = GCodeResult::error; @@ -4112,14 +4132,17 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 906: // Set/report Motor currents case 913: // Set/report motor current percent - // Note that we no longer wait for movement to stop. This is so that we can use these commands in the M911 power fail script. +#if HAS_SMART_DRIVERS + case 917: // Set/report standstill motor current percentage +#endif + // Note that we no longer wait for movement to stop. This is so that we can use these commands (in particular, M913) in the M911 power fail script. { bool seen = false; for (size_t axis = 0; axis < numTotalAxes; axis++) { if (gb.Seen(axisLetters[axis])) { - platform.SetMotorCurrent(axis, gb.GetFValue(), code == 913); + platform.SetMotorCurrent(axis, gb.GetFValue(), code); seen = true; } } @@ -4132,7 +4155,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) // 2014-09-29 DC42: we no longer insist that the user supplies values for all possible extruder drives for (size_t e = 0; e < eCount; e++) { - platform.SetMotorCurrent(numTotalAxes + e, eVals[e], code == 913); + platform.SetMotorCurrent(numTotalAxes + e, eVals[e], code); } seen = true; } @@ -4149,15 +4172,20 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (!seen) { - reply.copy((code == 913) ? "Motor current % of normal - " : "Motor current (mA) - "); + reply.copy( (code == 913) ? "Motor current % of normal - " +#if HAS_SMART_DRIVERS + : (code == 917) ? "Motor standstill current % of normal - " +#endif + : "Motor current (mA) - " + ); for (size_t axis = 0; axis < numTotalAxes; ++axis) { - reply.catf("%c:%d, ", axisLetters[axis], (int)platform.GetMotorCurrent(axis, code == 913)); + reply.catf("%c:%d, ", axisLetters[axis], (int)platform.GetMotorCurrent(axis, code)); } reply.cat("E"); for (size_t extruder = 0; extruder < numExtruders; extruder++) { - reply.catf(":%d", (int)platform.GetMotorCurrent(extruder + numTotalAxes, code == 913)); + reply.catf(":%d", (int)platform.GetMotorCurrent(extruder + numTotalAxes, code)); } if (code == 906) { @@ -4372,9 +4400,20 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (result != GCodeResult::notFinished) { reprap.EmergencyStop(); // this disables heaters and drives - Duet WiFi pre-production boards need drives disabled here - uint16_t reason = (gb.Seen('P') && StringStartsWith(gb.GetString(), "ERASE")) - ? (uint16_t)SoftwareResetReason::erase - : (uint16_t)SoftwareResetReason::user; + bool doErase; + if (gb.Seen('P')) + { + String<8> eraseString; + gb.GetPossiblyQuotedString(eraseString.GetRef()); + doErase = StringStartsWith(eraseString.Pointer(), "ERASE"); + } + else + { + doErase = false; + } + const uint16_t reason = (doErase) + ? (uint16_t)SoftwareResetReason::erase + : (uint16_t)SoftwareResetReason::user; platform.SoftwareReset(reason); // doesn't return } break; diff --git a/src/Libraries/General/StringRef.h b/src/Libraries/General/StringRef.h index 09ddaa24..4bfe4dba 100644 --- a/src/Libraries/General/StringRef.h +++ b/src/Libraries/General/StringRef.h @@ -12,7 +12,8 @@ #include <cstdarg> // for va_args #include <cstring> // for strlen -#undef printf +// Need to declare strnlen here because it isn't ISO standard +size_t strnlen(const char *s, size_t n); // Class to describe a string buffer, including its length. This saves passing buffer lengths around everywhere. class StringRef @@ -25,6 +26,7 @@ public: size_t Length() const { return len; } size_t strlen() const; + bool IsEmpty() const { return p[0] == 0; } char *Pointer() { return p; } const char *Pointer() const { return p; } @@ -43,7 +45,6 @@ public: size_t StripTrailingSpaces() const; size_t Prepend(const char *src) const; - bool IsEmpty() const { return p[0] == 0; } }; // Class to describe a string which we can get a StringRef reference to @@ -54,8 +55,13 @@ public: StringRef GetRef() { return StringRef(storage, Len + 1); } const char *c_str() const { return storage; } - size_t strlen() const { return ::strlen(storage); } + size_t strlen() const { return strnlen(storage, Len); } bool IsEmpty() const { return storage[0] == 0; } + const char *Pointer() const { return storage; } + char& operator[](size_t index) { return storage[index]; } + char operator[](size_t index) const { return storage[index]; } + + void Clear() const { storage[0] = 0; } private: char storage[Len + 1]; diff --git a/src/Movement/Kinematics/ScaraKinematics.cpp b/src/Movement/Kinematics/ScaraKinematics.cpp index 2bfbc5b3..ace7160d 100644 --- a/src/Movement/Kinematics/ScaraKinematics.cpp +++ b/src/Movement/Kinematics/ScaraKinematics.cpp @@ -124,8 +124,8 @@ bool ScaraKinematics::CartesianToMotorSteps(const float machinePos[], const floa //debugPrintf("psi = %.2f, theta = %.2f\n", psi * RadiansToDegrees, theta * RadiansToDegrees); motorPos[X_AXIS] = lrintf(theta * RadiansToDegrees * stepsPerMm[X_AXIS]); - motorPos[Y_AXIS] = lrintf((psi * RadiansToDegrees * stepsPerMm[Y_AXIS]) - (crosstalk[0] * motorPos[X_AXIS])); - motorPos[Z_AXIS] = lrintf((machinePos[Z_AXIS] * stepsPerMm[Z_AXIS]) - (motorPos[X_AXIS] * crosstalk[1]) - (motorPos[Y_AXIS] * crosstalk[2])); + motorPos[Y_AXIS] = lrintf((psi - (crosstalk[0] * theta)) * RadiansToDegrees * stepsPerMm[Y_AXIS]); + motorPos[Z_AXIS] = lrintf((machinePos[Z_AXIS] - (crosstalk[1] * theta) - (crosstalk[2] * psi)) * stepsPerMm[Z_AXIS]); // Transform any additional axes linearly for (size_t axis = XYZ_AXES; axis < numVisibleAxes; ++axis) @@ -139,14 +139,14 @@ bool ScaraKinematics::CartesianToMotorSteps(const float machinePos[], const floa // For Scara, the X and Y components of stepsPerMm are actually steps per degree angle. void ScaraKinematics::MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const { - const float psi = ((float)motorPos[X_AXIS]/stepsPerMm[X_AXIS]) * DegreesToRadians; - const float theta = (((float)motorPos[Y_AXIS] + ((float)motorPos[X_AXIS] * crosstalk[0]))/stepsPerMm[Y_AXIS]) * DegreesToRadians; + const float theta = ((float)motorPos[X_AXIS]/stepsPerMm[X_AXIS]) * DegreesToRadians; + const float psi = (((float)motorPos[Y_AXIS]/stepsPerMm[Y_AXIS]) * DegreesToRadians) + (crosstalk[0] * theta); - machinePos[X_AXIS] = (cosf(psi) * proximalArmLength + cosf(psi + theta) * distalArmLength) - xOffset; - machinePos[Y_AXIS] = (sinf(psi) * proximalArmLength + sinf(psi + theta) * distalArmLength) - yOffset; + machinePos[X_AXIS] = (cosf(theta) * proximalArmLength + cosf(psi + theta) * distalArmLength) - xOffset; + machinePos[Y_AXIS] = (sinf(theta) * proximalArmLength + sinf(psi + theta) * distalArmLength) - yOffset; // On some machines (e.g. Helios), the X and/or Y arm motors also affect the Z height - machinePos[Z_AXIS] = ((float)motorPos[Z_AXIS] + ((float)motorPos[X_AXIS] * crosstalk[1]) + ((float)motorPos[Y_AXIS] * crosstalk[2]))/stepsPerMm[Z_AXIS]; + machinePos[Z_AXIS] = ((float)motorPos[Z_AXIS]/stepsPerMm[Z_AXIS]) + (crosstalk[1] * theta) + (crosstalk[2] * psi); // Convert any additional axes linearly for (size_t drive = XYZ_AXES; drive < numVisibleAxes; ++drive) @@ -370,7 +370,9 @@ const char* ScaraKinematics::GetHomingFileName(AxesBitmap toBeHomed, AxesBitmap // Return true if the entire homing move should be terminated, false if only the motor associated with the endstop switch should be stopped. bool ScaraKinematics::QueryTerminateHomingMove(size_t axis) const { - return false; + // If crosstalk causes the axis mkotor concerned to affect other axes then must terminate the entire move + return (axis == X_AXIS && (crosstalk[0] != 0.0 || crosstalk[1] != 0.0)) + || (axis == Y_AXIS && crosstalk[2] != 0.0); } // This function is called from the step ISR when an endstop switch is triggered during homing after stopping just one motor or all motors. @@ -411,7 +413,9 @@ void ScaraKinematics::Recalc() distalArmLengthSquared = fsquare(distalArmLength); twoPd = proximalArmLength * distalArmLength * 2.0f; - minRadius = (proximalArmLength + distalArmLength * min<float>(cosf(psiLimits[0] * DegreesToRadians), cosf(psiLimits[1] * DegreesToRadians))) * 1.005; + minRadius = sqrtf(proximalArmLengthSquared + distalArmLengthSquared + - twoPd * max<float>(cosf(psiLimits[0] * DegreesToRadians), cosf(psiLimits[1] * DegreesToRadians))) * 1.005; + if (psiLimits[0] < 0.0 && psiLimits[1] > 0.0) { // Zero distal arm angle is reachable diff --git a/src/Platform.cpp b/src/Platform.cpp index 133b2964..130fa306 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -498,6 +498,9 @@ void Platform::Init() motorCurrents[drive] = 0.0; motorCurrentFraction[drive] = 1.0; +#if HAS_SMART_DRIVERS + motorStandstillCurrentFraction[drive] = 1.0; +#endif driverState[drive] = DriverStatus::disabled; // Enable pullup resistors on endstop inputs here if necessary. @@ -937,7 +940,7 @@ bool Platform::CheckFirmwareUpdatePrerequisites(StringRef& reply) bool ok = firmwareFile->Read(reinterpret_cast<char*>(&firstDword), sizeof(firstDword)) == (int)sizeof(firstDword); firmwareFile->Close(); if (!ok || firstDword != -#if (SAM4S || SAM4E) +#if SAM4E || SAM4S IRAM_ADDR + IRAM_SIZE #else IRAM1_ADDR + IRAM1_SIZE @@ -984,7 +987,7 @@ void Platform::UpdateFirmware() uint32_t data32[IFLASH_PAGE_SIZE/4]; char* const data = reinterpret_cast<char *>(data32); -#if (SAM4S || SAM4E) +#if SAM4E || SAM4S // The EWP command is not supported for non-8KByte sectors in the SAM4 series. // So we have to unlock and erase the complete 64Kb sector first. flash_unlock(IAP_FLASH_START, IAP_FLASH_END, nullptr, nullptr); @@ -1126,7 +1129,7 @@ void Platform::UpdateFirmware() static const char filename[] = "0:/sys/" IAP_FIRMWARE_FILE; const uint32_t topOfStack = *reinterpret_cast<uint32_t *>(IAP_FLASH_START); if (topOfStack + sizeof(filename) <= -#if (SAM4S || SAM4E) +#if SAM4E || SAM4S IRAM_ADDR + IRAM_SIZE #else IRAM1_ADDR + IRAM1_SIZE @@ -2578,37 +2581,48 @@ void Platform::SetDriversIdle() } // Set the current for a drive. Current is in mA. -void Platform::SetDriverCurrent(size_t driver, float currentOrPercent, bool isPercent) +void Platform::SetDriverCurrent(size_t driver, float currentOrPercent, int code) { if (driver < DRIVES) { - if (isPercent) - { - motorCurrentFraction[driver] = 0.01 * currentOrPercent; - } - else + switch (code) { + case 906: motorCurrents[driver] = currentOrPercent; + break; + + case 913: + motorCurrentFraction[driver] = 0.01 * currentOrPercent; + break; + +#if HAS_SMART_DRIVERS + case 917: + motorStandstillCurrentFraction[driver] = 0.01 * currentOrPercent; + break; +#endif + default: + break; } + UpdateMotorCurrent(driver); } } // Set the current for all drivers on an axis or extruder. Current is in mA. -void Platform::SetMotorCurrent(size_t drive, float currentOrPercent, bool isPercent) +void Platform::SetMotorCurrent(size_t drive, float currentOrPercent, int code) { const size_t numAxes = reprap.GetGCodes().GetTotalAxes(); if (drive < numAxes) { for (size_t i = 0; i < axisDrivers[drive].numDrivers; ++i) { - SetDriverCurrent(axisDrivers[drive].driverNumbers[i], currentOrPercent, isPercent); + SetDriverCurrent(axisDrivers[drive].driverNumbers[i], currentOrPercent, code); } } else if (drive < DRIVES) { - SetDriverCurrent(extruderDrivers[drive - numAxes], currentOrPercent, isPercent); + SetDriverCurrent(extruderDrivers[drive - numAxes], currentOrPercent, code); } } @@ -2680,7 +2694,7 @@ void Platform::UpdateMotorCurrent(size_t driver) // Get the configured motor current for a drive. // Currently we don't allow multiple motors on a single axis to have different currents, so we can just return the current for the first one. -float Platform::GetMotorCurrent(size_t drive, bool isPercent) const +float Platform::GetMotorCurrent(size_t drive, int code) const { if (drive < DRIVES) { @@ -2688,7 +2702,21 @@ float Platform::GetMotorCurrent(size_t drive, bool isPercent) const const uint8_t driver = (drive < numAxes) ? axisDrivers[drive].driverNumbers[0] : extruderDrivers[drive - numAxes]; if (driver < DRIVES) { - return (isPercent) ? motorCurrentFraction[driver] * 100.0 : motorCurrents[driver]; + switch (code) + { + case 906: + return motorCurrents[driver]; + + case 913: + return motorCurrentFraction[driver] * 100.0; + +#if HAS_SMART_DRIVERS + case 917: + return motorStandstillCurrentFraction[driver] * 100.0; +#endif + default: + break; + } } } return 0.0; diff --git a/src/Platform.h b/src/Platform.h index 436ccf90..84c44edb 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -403,8 +403,8 @@ public: void DisableDrive(size_t drive); void DisableAllDrives(); void SetDriversIdle(); - void SetMotorCurrent(size_t drive, float current, bool isPercent); - float GetMotorCurrent(size_t drive, bool isPercent) const; + void SetMotorCurrent(size_t drive, float current, int code); + float GetMotorCurrent(size_t drive, int code) const; void SetIdleCurrentFactor(float f); float GetIdleCurrentFactor() const { return idleCurrentFactor; } @@ -686,7 +686,7 @@ private: // DRIVES - void SetDriverCurrent(size_t driver, float current, bool isPercent); + void SetDriverCurrent(size_t driver, float current, int code); void UpdateMotorCurrent(size_t driver); void SetDriverDirection(uint8_t driver, bool direction) pre(driver < DRIVES); @@ -706,6 +706,9 @@ private: float pressureAdvance[MaxExtruders]; float motorCurrents[DRIVES]; // the normal motor current for each stepper driver float motorCurrentFraction[DRIVES]; // the percentages of normal motor current that each driver is set to +#if HAS_SMART_DRIVERS + float motorStandstillCurrentFraction[DRIVES]; +#endif AxisDriversConfig axisDrivers[MaxAxes]; // the driver numbers assigned to each axis uint8_t extruderDrivers[MaxExtruders]; // the driver number assigned to each extruder uint32_t driveDriverBits[2 * DRIVES]; // the bitmap of driver port bits for each axis or extruder, followed by the raw versions diff --git a/src/RepRap.cpp b/src/RepRap.cpp index 2e2d8279..a7033656 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -310,11 +310,11 @@ void RepRap::SetDebug(Module m, bool enable) { if (enable) { - debug |= (1 << m); + debug |= (1u << m); } else { - debug &= ~(1 << m); + debug &= ~(1u << m); } PrintDebug(); } @@ -326,30 +326,24 @@ void RepRap::SetDebug(bool enable) void RepRap::PrintDebug() { - if (debug != 0) + platform->Message(GenericMessage, "Debugging enabled for modules:"); + for (size_t i = 0; i < numModules; i++) { - platform->Message(GenericMessage, "Debugging enabled for modules:"); - for (size_t i = 0; i < numModules; i++) + if ((debug & (1u << i)) != 0) { - if ((debug & (1 << i)) != 0) - { - platform->MessageF(GenericMessage, " %s(%u)", moduleName[i], i); - } - } - platform->Message(GenericMessage, "\nDebugging disabled for modules:"); - for (size_t i = 0; i < numModules; i++) - { - if ((debug & (1 << i)) == 0) - { - platform->MessageF(GenericMessage, " %s(%u)", moduleName[i], i); - } + platform->MessageF(GenericMessage, " %s(%u)", moduleName[i], i); } - platform->Message(GenericMessage, "\n"); } - else + + platform->Message(GenericMessage, "\nDebugging disabled for modules:"); + for (size_t i = 0; i < numModules; i++) { - platform->Message(GenericMessage, "Debugging disabled\n"); + if ((debug & (1u << i)) == 0) + { + platform->MessageF(GenericMessage, " %s(%u)", moduleName[i], i); + } } + platform->Message(GenericMessage, "\n"); } // Add a tool. @@ -629,9 +623,9 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) ch = '['; for (size_t axis = 0; axis < numAxes; axis++) { - // Coordinates may be NaNs, for example when delta or SCARA homing fails. Replace any NaNs by 999.9 to prevent JSON parsing errors. + // Coordinates may be NaNs, for example when delta or SCARA homing fails. Replace any NaNs or infinities by 9999.9 to prevent JSON parsing errors. const float coord = liveCoordinates[axis]; - response->catf("%c%.3f", ch, (double)((std::isnan(coord) || std::isinf(coord)) ? 999.9 : coord)); + response->catf("%c%.3f", ch, (double)((std::isnan(coord) || std::isinf(coord)) ? 9999.9 : coord)); ch = ','; } } diff --git a/src/RepRap.h b/src/RepRap.h index c84d1416..8d77df7c 100644 --- a/src/RepRap.h +++ b/src/RepRap.h @@ -145,7 +145,7 @@ private: uint32_t fastLoop, slowLoop; uint32_t lastTime; - uint16_t debug; + uint32_t debug; bool stopped; bool active; bool resetting; diff --git a/src/Storage/MassStorage.cpp b/src/Storage/MassStorage.cpp index 19f9d9a0..8d29e8e8 100644 --- a/src/Storage/MassStorage.cpp +++ b/src/Storage/MassStorage.cpp @@ -86,7 +86,7 @@ FileWriteBuffer *MassStorage::AllocateWriteBuffer() return nullptr; } - FileWriteBuffer *buffer = freeWriteBuffers; + FileWriteBuffer * const buffer = freeWriteBuffers; freeWriteBuffers = buffer->Next(); buffer->SetNext(nullptr); return buffer; @@ -224,9 +224,9 @@ const char* MassStorage::GetMonthName(const uint8_t month) // Delete a file or directory bool MassStorage::Delete(const char* directory, const char* fileName, bool silent) { - const char* location = (directory != nullptr) - ? platform->GetMassStorage()->CombineName(directory, fileName) - : fileName; + const char* const location = (directory != nullptr) + ? platform->GetMassStorage()->CombineName(directory, fileName) + : fileName; if (f_unlink(location) != FR_OK) { if (!silent) @@ -241,7 +241,7 @@ bool MassStorage::Delete(const char* directory, const char* fileName, bool silen // Create a new directory bool MassStorage::MakeDirectory(const char *parentDir, const char *dirName) { - const char* location = platform->GetMassStorage()->CombineName(parentDir, dirName); + const char* const location = platform->GetMassStorage()->CombineName(parentDir, dirName); if (f_mkdir(location) != FR_OK) { platform->MessageF(ErrorMessage, "Failed to create directory %s\n", location); @@ -288,9 +288,9 @@ bool MassStorage::FileExists(const char *file) const bool MassStorage::FileExists(const char *directory, const char *fileName) const { - const char *location = (directory != nullptr) - ? platform->GetMassStorage()->CombineName(directory, fileName) - : fileName; + const char * const location = (directory != nullptr) + ? platform->GetMassStorage()->CombineName(directory, fileName) + : fileName; return FileExists(location); } @@ -310,9 +310,9 @@ bool MassStorage::DirectoryExists(const char* directory, const char* subDirector // Return the last modified time of a file, or zero if failure time_t MassStorage::GetLastModifiedTime(const char* directory, const char *fileName) const { - const char *location = (directory != nullptr) - ? platform->GetMassStorage()->CombineName(directory, fileName) - : fileName; + const char * const location = (directory != nullptr) + ? platform->GetMassStorage()->CombineName(directory, fileName) + : fileName; FILINFO fil; fil.lfname = nullptr; if (f_stat(location, &fil) == FR_OK) @@ -324,9 +324,9 @@ time_t MassStorage::GetLastModifiedTime(const char* directory, const char *fileN bool MassStorage::SetLastModifiedTime(const char* directory, const char *fileName, time_t time) { - const char *location = (directory != nullptr) - ? platform->GetMassStorage()->CombineName(directory, fileName) - : fileName; + const char * const location = (directory != nullptr) + ? platform->GetMassStorage()->CombineName(directory, fileName) + : fileName; const struct tm * const timeInfo = gmtime(&time); FILINFO fno; fno.fdate = (WORD)(((timeInfo->tm_year - 80) * 512U) | (timeInfo->tm_mon + 1) * 32U | timeInfo->tm_mday); diff --git a/src/Version.h b/src/Version.h index 0f667117..bb93cca3 100644 --- a/src/Version.h +++ b/src/Version.h @@ -9,11 +9,11 @@ #define SRC_VERSION_H_ #ifndef VERSION -# define VERSION "1.20beta6" +# define VERSION "1.20beta6+1" #endif #ifndef DATE -# define DATE "2017-11-03" +# define DATE "2017-11-09" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman" |