From cb8b9b83f77c1b73fe336460a177eb61b719c3da Mon Sep 17 00:00:00 2001 From: George Fu Date: Wed, 15 Apr 2020 15:12:17 +0800 Subject: [PATCH 01/14] Support DEBUG option --- ESPWebDAV.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/ESPWebDAV.h b/ESPWebDAV.h index d64835a..5f02950 100644 --- a/ESPWebDAV.h +++ b/ESPWebDAV.h @@ -1,12 +1,17 @@ #include #include -// debugging -// #define DBG_PRINT(...) { Serial.print(__VA_ARGS__); } -// #define DBG_PRINTLN(...) { Serial.println(__VA_ARGS__); } -// production -#define DBG_PRINT(...) { } -#define DBG_PRINTLN(...) { } +#define DEBUG + +#ifdef DEBUG + #define DBG_INIT(...) { Serial.begin(__VA_ARGS__); } + #define DBG_PRINT(...) { Serial.print(__VA_ARGS__); } + #define DBG_PRINTLN(...) { Serial.println(__VA_ARGS__); } +#else + #define DBG_INIT(...) {} + #define DBG_PRINT(...) {} + #define DBG_PRINTLN(...) {} +#endif // constants for WebServer #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) From afd000d4bec1a0e1136463e52487d03e6cae2779 Mon Sep 17 00:00:00 2001 From: George Fu Date: Wed, 15 Apr 2020 15:13:32 +0800 Subject: [PATCH 02/14] Add default sketch --- ESPWebDAV.ino | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 ESPWebDAV.ino diff --git a/ESPWebDAV.ino b/ESPWebDAV.ino new file mode 100644 index 0000000..3449487 --- /dev/null +++ b/ESPWebDAV.ino @@ -0,0 +1,174 @@ +// Using the WebDAV server with Rigidbot 3D printer. +// Printer controller is a variation of Rambo running Marlin firmware + +#include "ESP8266WiFi.h" +#include "ESPWebDAV.h" + +// LED is connected to GPIO2 on this board +#define INIT_LED {pinMode(2, OUTPUT);} +#define LED_ON {digitalWrite(2, LOW);} +#define LED_OFF {digitalWrite(2, HIGH);} + +#define HOSTNAME "FYSETC" +#define SERVER_PORT 80 +#define SPI_BLOCKOUT_PERIOD 20000UL + +#define SD_CS 4 +#define MISO 12 +#define MOSI 13 +#define SCLK 14 +#define CS_SENSE 5 + + +const char *ssid = "507-asus"; +const char *password = "!Umv870q"; + +ESPWebDAV dav; +String statusMessage; +bool initFailed = false; + +volatile long spiBlockoutTime = 0; +bool weHaveBus = false; + + + +// ------------------------ +void setup() { +// ------------------------ + // ----- GPIO ------- + // Detect when other master uses SPI bus + pinMode(CS_SENSE, INPUT); + attachInterrupt(CS_SENSE, []() { + if(!weHaveBus) + spiBlockoutTime = millis() + SPI_BLOCKOUT_PERIOD; + }, FALLING); + + DBG_INIT(115200); + DBG_PRINTLN(""); + INIT_LED; + blink(); + + // wait for other master to assert SPI bus first + delay(SPI_BLOCKOUT_PERIOD); + + // ----- WIFI ------- + // Set hostname first + WiFi.hostname(HOSTNAME); + // Reduce startup surge current + WiFi.setAutoConnect(false); + WiFi.mode(WIFI_STA); + WiFi.setPhyMode(WIFI_PHY_MODE_11N); + WiFi.begin(ssid, password); + + // Wait for connection + while(WiFi.status() != WL_CONNECTED) { + blink(); + DBG_PRINT("."); + } + + DBG_PRINTLN(""); + DBG_PRINT("Connected to "); DBG_PRINTLN(ssid); + DBG_PRINT("IP address: "); DBG_PRINTLN(WiFi.localIP()); + DBG_PRINT("RSSI: "); DBG_PRINTLN(WiFi.RSSI()); + DBG_PRINT("Mode: "); DBG_PRINTLN(WiFi.getPhyMode()); + + + // ----- SD Card and Server ------- + // Check to see if other master is using the SPI bus + while(millis() < spiBlockoutTime) + blink(); + + takeBusControl(); + + // start the SD DAV server + if(!dav.init(SD_CS, SPI_FULL_SPEED, SERVER_PORT)) { + statusMessage = "Failed to initialize SD Card"; + DBG_PRINT("ERROR: "); DBG_PRINTLN(statusMessage); + // indicate error on LED + errorBlink(); + initFailed = true; + } + else + blink(); + + relenquishBusControl(); + DBG_PRINTLN("WebDAV server started"); +} + + + +// ------------------------ +void loop() { +// ------------------------ + if(millis() < spiBlockoutTime) + blink(); + + // do it only if there is a need to read FS + if(dav.isClientWaiting()) { + if(initFailed) + return dav.rejectClient(statusMessage); + + // has other master been using the bus in last few seconds + if(millis() < spiBlockoutTime) + return dav.rejectClient("Marlin is reading from SD card"); + + // a client is waiting and FS is ready and other SPI master is not using the bus + takeBusControl(); + dav.handleClient(); + relenquishBusControl(); + } +} + + + +// ------------------------ +void takeBusControl() { +// ------------------------ + weHaveBus = true; + LED_ON; + pinMode(MISO, SPECIAL); + pinMode(MOSI, SPECIAL); + pinMode(SCLK, SPECIAL); + pinMode(SD_CS, OUTPUT); +} + + + +// ------------------------ +void relenquishBusControl() { +// ------------------------ + pinMode(MISO, INPUT); + pinMode(MOSI, INPUT); + pinMode(SCLK, INPUT); + pinMode(SD_CS, INPUT); + LED_OFF; + weHaveBus = false; +} + + + + +// ------------------------ +void blink() { +// ------------------------ + LED_ON; + delay(100); + LED_OFF; + delay(400); +} + + + +// ------------------------ +void errorBlink() { +// ------------------------ + for(int i = 0; i < 100; i++) { + LED_ON; + delay(50); + LED_OFF; + delay(50); + } +} + + + From 6f24fb351e737f634173fa739efcee6417281e16 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 21 Apr 2020 11:18:53 +0800 Subject: [PATCH 03/14] support setting config with serial --- ESPWebDAV.cpp | 11 ++ ESPWebDAV.h | 2 + ESPWebDAV.ino | 355 ++++++++++++++++++++++++++++++++++++++++++-------- config.cpp | 51 ++++++++ config.h | 34 +++++ macros.h | 11 ++ parser.cpp | 200 ++++++++++++++++++++++++++++ parser.h | 140 ++++++++++++++++++++ serial.cpp | 36 +++++ serial.h | 98 ++++++++++++++ 10 files changed, 881 insertions(+), 57 deletions(-) create mode 100644 config.cpp create mode 100644 config.h create mode 100644 macros.h create mode 100644 parser.cpp create mode 100644 parser.h create mode 100644 serial.cpp create mode 100644 serial.h diff --git a/ESPWebDAV.cpp b/ESPWebDAV.cpp index 3205680..7db9aa9 100644 --- a/ESPWebDAV.cpp +++ b/ESPWebDAV.cpp @@ -24,7 +24,18 @@ bool ESPWebDAV::init(int chipSelectPin, SPISettings spiSettings, int serverPort) return sd.begin(chipSelectPin, spiSettings); } +// ------------------------ +bool ESPWebDAV::initSD(int chipSelectPin, SPISettings spiSettings) { + // initialize the SD card + return sd.begin(chipSelectPin, spiSettings); +} +// ------------------------ +bool ESPWebDAV::startServer() { +// ------------------------ + // start the wifi server + server->begin(); +} // ------------------------ void ESPWebDAV::handleNotFound() { diff --git a/ESPWebDAV.h b/ESPWebDAV.h index 5f02950..983c4cc 100644 --- a/ESPWebDAV.h +++ b/ESPWebDAV.h @@ -25,6 +25,8 @@ enum DepthType { DEPTH_NONE, DEPTH_CHILD, DEPTH_ALL }; class ESPWebDAV { public: bool init(int chipSelectPin, SPISettings spiSettings, int serverPort); + bool initSD(int chipSelectPin, SPISettings spiSettings); + bool startServer(); bool isClientWaiting(); void handleClient(String blank = ""); void rejectClient(String rejectMessage); diff --git a/ESPWebDAV.ino b/ESPWebDAV.ino index 3449487..76352dc 100644 --- a/ESPWebDAV.ino +++ b/ESPWebDAV.ino @@ -3,6 +3,9 @@ #include "ESP8266WiFi.h" #include "ESPWebDAV.h" +#include "serial.h" +#include "parser.h" +#include "Config.h" // LED is connected to GPIO2 on this board #define INIT_LED {pinMode(2, OUTPUT);} @@ -12,6 +15,7 @@ #define HOSTNAME "FYSETC" #define SERVER_PORT 80 #define SPI_BLOCKOUT_PERIOD 20000UL +#define WIFI_CONNECT_TIMEOUT 30000UL #define SD_CS 4 #define MISO 12 @@ -19,22 +23,76 @@ #define SCLK 14 #define CS_SENSE 5 - -const char *ssid = "507-asus"; -const char *password = "!Umv870q"; - ESPWebDAV dav; String statusMessage; bool initFailed = false; volatile long spiBlockoutTime = 0; bool weHaveBus = false; +bool wifiConnected = false; + +bool wifiConnect(const char*ssid,const char*password) { + if(ssid == NULL || password==NULL) { + SERIAL_ECHOLN("Please set the wifi ssid and password first"); + } + + // + wifiConnected = false; + + // Set hostname first + WiFi.hostname(HOSTNAME); + // Reduce startup surge current + WiFi.setAutoConnect(false); + WiFi.mode(WIFI_STA); + WiFi.setPhyMode(WIFI_PHY_MODE_11N); + WiFi.begin(ssid, password); + + // Wait for connection + unsigned long timeout = millis(); + while(WiFi.status() != WL_CONNECTED) { + blink(); + DBG_PRINT("."); + if(millis() > timeout + WIFI_CONNECT_TIMEOUT) + return false; + } + + DBG_PRINTLN(""); + DBG_PRINT("Connected to "); DBG_PRINTLN(ssid); + DBG_PRINT("IP address: "); DBG_PRINTLN(WiFi.localIP()); + DBG_PRINT("RSSI: "); DBG_PRINTLN(WiFi.RSSI()); + DBG_PRINT("Mode: "); DBG_PRINTLN(WiFi.getPhyMode()); + + wifiConnected = true; + + config.save(ssid,password); + + return true; +} - +void startDAVServer() { + // Check to see if other master is using the SPI bus + while(millis() < spiBlockoutTime) + blink(); + + takeBusControl(); + + // start the SD DAV server + if(!dav.init(SD_CS, SPI_FULL_SPEED, SERVER_PORT)) { + statusMessage = "Failed to initialize SD Card"; + DBG_PRINT("ERROR: "); DBG_PRINTLN(statusMessage); + // indicate error on LED + errorBlink(); + initFailed = true; + } + else + blink(); + + relenquishBusControl(); + DBG_PRINTLN("WebDAV server started"); +} // ------------------------ void setup() { -// ------------------------ // ----- GPIO ------- // Detect when other master uses SPI bus pinMode(CS_SENSE, INPUT); @@ -52,50 +110,203 @@ void setup() { delay(SPI_BLOCKOUT_PERIOD); // ----- WIFI ------- - // Set hostname first - WiFi.hostname(HOSTNAME); - // Reduce startup surge current - WiFi.setAutoConnect(false); - WiFi.mode(WIFI_STA); - WiFi.setPhyMode(WIFI_PHY_MODE_11N); - WiFi.begin(ssid, password); - - // Wait for connection - while(WiFi.status() != WL_CONNECTED) { - blink(); - DBG_PRINT("."); - } + if(config.load() == 1) { // Connected before + if(wifiConnect(config.ssid(), config.password())) { + startDAVServer(); + } + else { + SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); + } + } + else { + SERIAL_ECHOLN("Please set the wifi ssid with M50 and password with M51 , and start connection with M52"); + } +} - DBG_PRINTLN(""); - DBG_PRINT("Connected to "); DBG_PRINTLN(ssid); - DBG_PRINT("IP address: "); DBG_PRINTLN(WiFi.localIP()); - DBG_PRINT("RSSI: "); DBG_PRINTLN(WiFi.RSSI()); - DBG_PRINT("Mode: "); DBG_PRINTLN(WiFi.getPhyMode()); +#define MAX_CMD_SIZE 96 +#define BUFSIZE 4 + +/** + * GCode Command Queue + * A simple ring buffer of BUFSIZE command strings. + * + * Commands are copied into this buffer by the command injectors + * (immediate, serial, sd card) and they are processed sequentially by + * the main loop. The process_next_command function parses the next + * command and hands off execution to individual handler functions. + */ +uint8_t commands_in_queue = 0, // Count of commands in the queue + cmd_queue_index_r = 0, // Ring buffer read (out) position + cmd_queue_index_w = 0; // Ring buffer write (in) position + +uint32_t command_sd_pos[BUFSIZE]; +volatile uint32_t current_command_sd_pos; + +char command_queue[BUFSIZE][MAX_CMD_SIZE]; + +static bool send_ok[BUFSIZE]; + +// Number of characters read in the current line of serial input +static int serial_count; // = 0; + +/** + * Once a new command is in the ring buffer, call this to commit it + */ +inline void _commit_command(bool say_ok) { + send_ok[cmd_queue_index_w] = say_ok; + if (++cmd_queue_index_w >= BUFSIZE) cmd_queue_index_w = 0; + commands_in_queue++; +} +/** + * Copy a command from RAM into the main command buffer. + * Return true if the command was successfully added. + * Return false for a full buffer, or if the 'command' is a comment. + */ +inline bool _enqueuecommand(const char* cmd, bool say_ok=false) { + if (*cmd == ';' || commands_in_queue >= BUFSIZE) return false; + strcpy(command_queue[cmd_queue_index_w], cmd); + _commit_command(say_ok); + return true; +} + +/** + * Get all commands waiting on the serial port and queue them. + * Exit when the buffer is full or when no more characters are + * left on the serial port. + */ +void get_serial_commands() { + static char serial_line_buffer[MAX_CMD_SIZE]; + static bool serial_comment_mode = false; + + /** + * Loop while serial characters are incoming and the queue is not full + */ + int c; + while (commands_in_queue < BUFSIZE && (c = Serial.read()) >= 0) { + + char serial_char = c; + + /** + * If the character ends the line + */ + if (serial_char == '\n' || serial_char == '\r') { + + serial_comment_mode = false; // end of line == end of comment + + // Skip empty lines and comments + if (!serial_count) { continue; } + + serial_line_buffer[serial_count] = 0; // Terminate string + serial_count = 0; // Reset buffer + + char* command = serial_line_buffer; + + while (*command == ' ') command++; // Skip leading spaces + + // Add the command to the queue + _enqueuecommand(serial_line_buffer, true); + } + else if (serial_count >= MAX_CMD_SIZE - 1) { + // Keep fetching, but ignore normal characters beyond the max length + // The command will be injected when EOL is reached + } + else if (serial_char == '\\') { // Handle escapes + if ((c = MYSERIAL0.read()) >= 0 && !serial_comment_mode) // if we have one more character, copy it over + serial_line_buffer[serial_count++] = (char)c; + // otherwise do nothing + } + else { // it's not a newline, carriage return or escape char + if (serial_char == ';') serial_comment_mode = true; + if (!serial_comment_mode) serial_line_buffer[serial_count++] = serial_char; + } + } // queue has space, serial has data +} - // ----- SD Card and Server ------- - // Check to see if other master is using the SPI bus - while(millis() < spiBlockoutTime) - blink(); - - takeBusControl(); - - // start the SD DAV server - if(!dav.init(SD_CS, SPI_FULL_SPEED, SERVER_PORT)) { - statusMessage = "Failed to initialize SD Card"; - DBG_PRINT("ERROR: "); DBG_PRINTLN(statusMessage); - // indicate error on LED - errorBlink(); - initFailed = true; - } - else - blink(); +/** + * M50: Set the Wifi ssid + */ +inline void gcode_M50() { + for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; + config.ssid(parser.string_arg); + SERIAL_ECHO(config.ssid()); +} + +/** + * M50: Set the Wifi password + */ +inline void gcode_M51() { + for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; + config.password(parser.string_arg); + SERIAL_ECHO(config.password()); +} + +/** + * M52: Connect the wifi + */ +inline void gcode_M52() { + if(wifiConnect(config.ssid(), config.password())) { + startDAVServer(); + } + else { + SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); + } +} + +/** + * M53: Check wifi status + */ +inline void gcode_M53() { + if(WiFi.status() != WL_CONNECTED) { + SERIAL_ECHOLN("Wifi not connected"); + SERIAL_ECHOLN("Please set the wifi ssid with M50 and password with M51 , and start connection with M52"); + } + else { + DBG_PRINTLN(""); + DBG_PRINT("Connected to "); SERIAL_ECHOLN(WiFi.SSID()); + DBG_PRINT("IP address: "); DBG_PRINTLN(WiFi.localIP()); + DBG_PRINT("RSSI: "); DBG_PRINTLN(WiFi.RSSI()); + DBG_PRINT("Mode: "); DBG_PRINTLN(WiFi.getPhyMode()); + } +} - relenquishBusControl(); - DBG_PRINTLN("WebDAV server started"); +/** + * Process the parsed command and dispatch it to its handler + */ +void process_parsed_command() { + //SERIAL_ECHOLNPAIR("command_letter:", parser.command_letter); + //SERIAL_ECHOLNPAIR("codenum:", parser.codenum); + + // Handle a known G, M, or T + switch (parser.command_letter) { + case 'G': switch (parser.codenum) { + default: parser.unknown_command_error(); + } + break; + + case 'M': switch (parser.codenum) { + case 50: gcode_M50(); break; + case 51: gcode_M51(); break; + case 52: gcode_M52(); break; + case 53: gcode_M53(); break; + default: parser.unknown_command_error(); + } + break; + + default: parser.unknown_command_error(); + } + + SERIAL_ECHOLN("ok"); } +void process_next_command() { + char * const current_command = command_queue[cmd_queue_index_r]; + current_command_sd_pos = command_sd_pos[cmd_queue_index_r]; + // Parse the next command in the queue + parser.parse(current_command); + process_parsed_command(); +} // ------------------------ void loop() { @@ -104,19 +315,37 @@ void loop() { blink(); // do it only if there is a need to read FS - if(dav.isClientWaiting()) { - if(initFailed) - return dav.rejectClient(statusMessage); - - // has other master been using the bus in last few seconds - if(millis() < spiBlockoutTime) - return dav.rejectClient("Marlin is reading from SD card"); - - // a client is waiting and FS is ready and other SPI master is not using the bus - takeBusControl(); - dav.handleClient(); - relenquishBusControl(); + if(wifiConnected) { + if(dav.isClientWaiting()) { + if(initFailed) + return dav.rejectClient(statusMessage); + + // has other master been using the bus in last few seconds + if(millis() < spiBlockoutTime) + return dav.rejectClient("Marlin is reading from SD card"); + + // a client is waiting and FS is ready and other SPI master is not using the bus + takeBusControl(); + dav.handleClient(); + relenquishBusControl(); + } } + + // Get the serial input + if (commands_in_queue < BUFSIZE) get_serial_commands(); + + if (commands_in_queue) { + process_next_command(); + + // The queue may be reset by a command handler or by code invoked by idle() within a handler + if (commands_in_queue) { + --commands_in_queue; + if (++cmd_queue_index_r >= BUFSIZE) cmd_queue_index_r = 0; + } + } + + // + statusBlink(); } @@ -170,5 +399,17 @@ void errorBlink() { } } - - +void statusBlink() { + static unsigned long time = 0; + if(millis() > time + 1000 ) { + if(wifiConnected) { + LED_ON; + delay(50); + LED_OFF; + } + else { + LED_OFF; + } + time = millis(); + } +} diff --git a/config.cpp b/config.cpp new file mode 100644 index 0000000..56e728b --- /dev/null +++ b/config.cpp @@ -0,0 +1,51 @@ +#include +#include "config.h" + +// ------------------------ +unsigned char Config::load() { + EEPROM.begin(EEPROM_SIZE); + uint8_t *p = (uint8_t*)(&data); + for (int i = 0; i < sizeof(data); i++) + { + *(p + i) = EEPROM.read(i); + } + EEPROM.commit(); + + return data.flag; +} + +char* Config::ssid() { + return data.ssid; +} + +void Config::ssid(char* ssid) { + if(ssid == NULL) return; + strncpy(data.ssid,ssid,WIFI_SSID_LEN); +} + +char* Config::password() { + return data.psw; +} + +void Config::password(char* password) { + if(password == NULL) return; + strncpy(data.psw,password,WIFI_PASSWD_LEN); +} + +void Config::save(const char*ssid,const char*password) { + if(ssid ==NULL || password==NULL) + return; + + EEPROM.begin(EEPROM_SIZE); + data.flag = 1; + strncpy(data.ssid, ssid, WIFI_SSID_LEN); + strncpy(data.psw, password, WIFI_PASSWD_LEN); + uint8_t *p = (uint8_t*)(&data); + for (int i = 0; i < sizeof(data); i++) + { + EEPROM.write(i, *(p + i)); + } + EEPROM.commit(); +} + +Config config; diff --git a/config.h b/config.h new file mode 100644 index 0000000..12593a9 --- /dev/null +++ b/config.h @@ -0,0 +1,34 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include +#include + +#define WIFI_SSID_LEN 32 +#define WIFI_PASSWD_LEN 64 + +#define EEPROM_SIZE 512 + +typedef struct config_type +{ + unsigned char flag; // Was saved before? + char ssid[32]; + char psw[64]; +}CONFIG_TYPE; + +class Config { +public: + unsigned char load(); + char* ssid(); + void ssid(char* ssid); + char* password(); + void password(char* password); + void save(const char*ssid,const char*password); + +protected: + CONFIG_TYPE data; +}; + +extern Config config; + +#endif diff --git a/macros.h b/macros.h new file mode 100644 index 0000000..1d7d6bb --- /dev/null +++ b/macros.h @@ -0,0 +1,11 @@ +typedef uint32_t millis_t; + +#define WITHIN(V,L,H) ((V) >= (L) && (V) <= (H)) +#define NUMERIC(a) WITHIN(a, '0', '9') +#define DECIMAL(a) (NUMERIC(a) || a == '.') +#define NUMERIC_SIGNED(a) (NUMERIC(a) || (a) == '-' || (a) == '+') +#define DECIMAL_SIGNED(a) (DECIMAL(a) || (a) == '-' || (a) == '+') + +#define constrain(value, arg_min, arg_max) ((value) < (arg_min) ? (arg_min) :((value) > (arg_max) ? (arg_max) : (value))) + +#define FORCE_INLINE __attribute__((always_inline)) inline diff --git a/parser.cpp b/parser.cpp new file mode 100644 index 0000000..e17ca6e --- /dev/null +++ b/parser.cpp @@ -0,0 +1,200 @@ +/** + * Base on Malrin https://github.com/MarlinFirmware/Marlin + * parser.cpp - Parser for a GCode line, providing a parameter interface. + */ +#include "serial.h" +#include "parser.h" + +// Must be declared for allocation and to satisfy the linker +// Zero values need no initialization. +char *GCodeParser::command_ptr, + *GCodeParser::string_arg, + *GCodeParser::value_ptr; +char GCodeParser::command_letter; +int GCodeParser::codenum; + +char *GCodeParser::command_args; // start of parameters + +// Create a global instance of the GCode parser singleton +GCodeParser parser; + +/** + * Clear all code-seen (and value pointers) + * + * Since each param is set/cleared on seen codes, + * this may be optimized by commenting out ZERO(param) + */ +void GCodeParser::reset() { + string_arg = NULL; // No whole line argument + command_letter = '?'; // No command letter + codenum = 0; // No command code +} + +// Populate all fields by parsing a single line of GCode +// 58 bytes of SRAM are used to speed up seen/value +void GCodeParser::parse(char *p) { + + reset(); // No codes to report + + // Skip spaces + while (*p == ' ') ++p; + + // Skip N[-0-9] if included in the command line + if (*p == 'N' && NUMERIC_SIGNED(p[1])) { + p += 2; // skip N[-0-9] + while (NUMERIC(*p)) ++p; // skip [0-9]* + while (*p == ' ') ++p; // skip [ ]* + } + + // *p now points to the current command, which should be G, M, or T + command_ptr = p; + + // Get the command letter, which must be G, M, or T + const char letter = *p++; + + // Nullify asterisk and trailing whitespace + char *starpos = strchr(p, '*'); + if (starpos) { + --starpos; // * + while (*starpos == ' ') --starpos; // spaces... + starpos[1] = '\0'; + } + + // Bail if the letter is not G, M, or T + switch (letter) { case 'G': case 'M': case 'T': break; default: return; } + + // Skip spaces to get the numeric part + while (*p == ' ') p++; + + // Bail if there's no command code number + if (!NUMERIC(*p)) return; + + // Save the command letter at this point + // A '?' signifies an unknown command + command_letter = letter; + + // Get the code number - integer digits only + codenum = 0; + do { + codenum *= 10, codenum += *p++ - '0'; + } while (NUMERIC(*p)); + + // Skip all spaces to get to the first argument, or nul + while (*p == ' ') p++; + + // The command parameters (if any) start here, for sure! + + // Only use string_arg for these M codes + if (letter == 'M') switch (codenum) { case 50: case 51: case 23: case 28: case 30: case 117: case 118: case 928: string_arg = p; return; default: break; } + + #if defined(DEBUG_GCODE_PARSER) + const bool debug = codenum == 800; + #endif + + /** + * Find all parameters, set flags and pointers for fast parsing + * + * Most codes ignore 'string_arg', but those that want a string will get the right pointer. + * The following loop assigns the first "parameter" having no numeric value to 'string_arg'. + * This allows M0/M1 with expire time to work: "M0 S5 You Win!" + * For 'M118' you must use 'E1' and 'A1' rather than just 'E' or 'A' + */ + string_arg = NULL; + while (const char code = *p++) { // Get the next parameter. A NUL ends the loop + + // Special handling for M32 [P] !/path/to/file.g# + // The path must be the last parameter + if (code == '!' && letter == 'M' && codenum == 32) { + string_arg = p; // Name starts after '!' + char * const lb = strchr(p, '#'); // Already seen '#' as SD char (to pause buffering) + if (lb) *lb = '\0'; // Safe to mark the end of the filename + return; + } + + // Arguments MUST be uppercase for fast GCode parsing + #define PARAM_TEST true + + if (PARAM_TEST) { + + while (*p == ' ') p++; // Skip spaces between parameters & values + + const bool has_num = valid_float(p); + + #if defined(DEBUG_GCODE_PARSER) + if (debug) { + SERIAL_ECHOPAIR("Got letter ", code); + SERIAL_ECHOPAIR(" at index ", (int)(p - command_ptr - 1)); + if (has_num) SERIAL_ECHOPGM(" (has_num)"); + } + #endif + + if (!has_num && !string_arg) { // No value? First time, keep as string_arg + string_arg = p - 1; + #if defined(DEBUG_GCODE_PARSER) + if (debug) SERIAL_ECHOPAIR(" string_arg: ", hex_address((void*)string_arg)); // DEBUG + #endif + } + + #if defined(DEBUG_GCODE_PARSER) + if (debug) SERIAL_EOL(); + #endif + } + else if (!string_arg) { // Not A-Z? First time, keep as the string_arg + string_arg = p - 1; + #if defined(DEBUG_GCODE_PARSER) + if (debug) SERIAL_ECHOPAIR(" string_arg: ", hex_address((void*)string_arg)); // DEBUG + #endif + } + + if (!WITHIN(*p, 'A', 'Z')) { // Another parameter right away? + while (*p && DECIMAL_SIGNED(*p)) p++; // Skip over the value section of a parameter + while (*p == ' ') p++; // Skip over all spaces + } + } +} + +void GCodeParser::unknown_command_error() { + SERIAL_ECHO_START(); + SERIAL_ECHOPAIR("Unknown:", command_ptr); + SERIAL_CHAR('"'); + SERIAL_EOL(); +} + +#if defined(DEBUG_GCODE_PARSER) + + void GCodeParser::debug() { + SERIAL_ECHOPAIR("Command: ", command_ptr); + SERIAL_ECHOPAIR(" (", command_letter); + SERIAL_ECHO(codenum); + SERIAL_ECHOLNPGM(")"); + SERIAL_ECHOPAIR(" args: \"", command_args); + SERIAL_CHAR('"'); + if (string_arg) { + SERIAL_ECHOPGM(" string: \""); + SERIAL_ECHO(string_arg); + SERIAL_CHAR('"'); + } + SERIAL_ECHOPGM("\n\n"); + for (char c = 'A'; c <= 'Z'; ++c) { + if (seen(c)) { + SERIAL_ECHOPAIR("Code '", c); SERIAL_ECHOPGM("':"); + if (has_value()) { + SERIAL_ECHOPAIR("\n float: ", value_float()); + SERIAL_ECHOPAIR("\n long: ", value_long()); + SERIAL_ECHOPAIR("\n ulong: ", value_ulong()); + SERIAL_ECHOPAIR("\n millis: ", value_millis()); + SERIAL_ECHOPAIR("\n sec-ms: ", value_millis_from_seconds()); + SERIAL_ECHOPAIR("\n int: ", value_int()); + SERIAL_ECHOPAIR("\n ushort: ", value_ushort()); + SERIAL_ECHOPAIR("\n byte: ", (int)value_byte()); + SERIAL_ECHOPAIR("\n bool: ", (int)value_bool()); + SERIAL_ECHOPAIR("\n linear: ", value_linear_units()); + } + else + SERIAL_ECHOPGM(" (no value)"); + SERIAL_ECHOPGM("\n\n"); + } + } + } + +#endif // DEBUG_GCODE_PARSER diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..684cca2 --- /dev/null +++ b/parser.h @@ -0,0 +1,140 @@ +/** + * Base on Malrin https://github.com/MarlinFirmware/Marlin + * parser.h - Parser for a GCode line, providing a parameter interface. + * Codes like M149 control the way the GCode parser behaves, + * so settings for these codes are located in this class. + */ + +#ifndef _PARSER_H_ +#define _PARSER_H_ + +//#include "enum.h" +#include +#include "macros.h" + +#define strtof strtod + +/** + * GCode parser + * + * - Parse a single gcode line for its letter, code, subcode, and parameters + * - Provide accessors for parameters: + * - Parameter exists + * - Parameter has value + * - Parameter value in different units and types + */ +class GCodeParser { + +private: + static char *value_ptr; // Set by seen, used to fetch the value + static char *command_args; // Args start here, for slow scan + +public: + + // Command line state + static char *command_ptr, // The command, so it can be echoed + *string_arg; // string of command line + + static char command_letter; // G, M, or T + static int codenum; // 123 + + #if defined(DEBUG_GCODE_PARSER) + static void debug(); + #endif + + GCodeParser() { + } + + // Reset is done before parsing + static void reset(); + + #define LETTER_BIT(N) ((N) - 'A') + + FORCE_INLINE static bool valid_signless(const char * const p) { + return NUMERIC(p[0]) || (p[0] == '.' && NUMERIC(p[1])); // .?[0-9] + } + + FORCE_INLINE static bool valid_float(const char * const p) { + return valid_signless(p) || ((p[0] == '-' || p[0] == '+') && valid_signless(&p[1])); // [-+]?.?[0-9] + } + + + // Code is found in the string. If not found, value_ptr is unchanged. + // This allows "if (seen('A')||seen('B'))" to use the last-found value. + static bool seen(const char c) { + char *p = strchr(command_args, c); + const bool b = !!p; + if (b) value_ptr = valid_float(&p[1]) ? &p[1] : (char*)NULL; + return b; + } + + static bool seen_any() { return *command_args == '\0'; } + + #define SEEN_TEST(L) !!strchr(command_args, L) + + // Seen any axis parameter + static bool seen_axis() { + return SEEN_TEST('X') || SEEN_TEST('Y') || SEEN_TEST('Z') || SEEN_TEST('E'); + } + + // Populate all fields by parsing a single line of GCode + // This uses 54 bytes of SRAM to speed up seen/value + static void parse(char * p); + + // The code value pointer was set + FORCE_INLINE static bool has_value() { return value_ptr != NULL; } + + // Seen a parameter with a value + inline static bool seenval(const char c) { return seen(c) && has_value(); } + + // Float removes 'E' to prevent scientific notation interpretation + inline static float value_float() { + if (value_ptr) { + char *e = value_ptr; + for (;;) { + const char c = *e; + if (c == '\0' || c == ' ') break; + if (c == 'E' || c == 'e') { + *e = '\0'; + const float ret = strtof(value_ptr, NULL); + *e = c; + return ret; + } + ++e; + } + return strtof(value_ptr, NULL); + } + return 0; + } + + // Code value as a long or ulong + inline static int32_t value_long() { return value_ptr ? strtol(value_ptr, NULL, 10) : 0L; } + inline static uint32_t value_ulong() { return value_ptr ? strtoul(value_ptr, NULL, 10) : 0UL; } + + // Code value for use as time + FORCE_INLINE static millis_t value_millis() { return value_ulong(); } + FORCE_INLINE static millis_t value_millis_from_seconds() { return value_float() * 1000UL; } + + // Reduce to fewer bits + FORCE_INLINE static int16_t value_int() { return (int16_t)value_long(); } + FORCE_INLINE static uint16_t value_ushort() { return (uint16_t)value_long(); } + inline static uint8_t value_byte() { return (uint8_t)constrain(value_long(), 0, 255); } + + // Bool is true with no value or non-zero + inline static bool value_bool() { return !has_value() || !!value_byte(); } + + void unknown_command_error(); + + // Provide simple value accessors with default option + FORCE_INLINE static float floatval(const char c, const float dval=0.0) { return seenval(c) ? value_float() : dval; } + FORCE_INLINE static bool boolval(const char c, const bool dval=false) { return seenval(c) ? value_bool() : (seen(c) ? true : dval); } + FORCE_INLINE static uint8_t byteval(const char c, const uint8_t dval=0) { return seenval(c) ? value_byte() : dval; } + FORCE_INLINE static int16_t intval(const char c, const int16_t dval=0) { return seenval(c) ? value_int() : dval; } + FORCE_INLINE static uint16_t ushortval(const char c, const uint16_t dval=0) { return seenval(c) ? value_ushort() : dval; } + FORCE_INLINE static int32_t longval(const char c, const int32_t dval=0) { return seenval(c) ? value_long() : dval; } + FORCE_INLINE static uint32_t ulongval(const char c, const uint32_t dval=0) { return seenval(c) ? value_ulong() : dval; } +}; + +extern GCodeParser parser; + +#endif // _PARSER_H_ diff --git a/serial.cpp b/serial.cpp new file mode 100644 index 0000000..791e8fa --- /dev/null +++ b/serial.cpp @@ -0,0 +1,36 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "serial.h" + +const char errormagic[] PROGMEM = "Error:"; +const char echomagic[] PROGMEM = "echo:"; + +void serial_echopair_PGM(const char* s_P, const char *v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_PGM(const char* s_P, char v) { serialprintPGM(s_P); SERIAL_CHAR(v); } +void serial_echopair_PGM(const char* s_P, int v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_PGM(const char* s_P, long v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_PGM(const char* s_P, float v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_PGM(const char* s_P, double v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_PGM(const char* s_P, unsigned long v) { serialprintPGM(s_P); SERIAL_ECHO(v); } + +void serial_spaces(uint8_t count) { while (count--) SERIAL_CHAR(' '); } diff --git a/serial.h b/serial.h new file mode 100644 index 0000000..f05c069 --- /dev/null +++ b/serial.h @@ -0,0 +1,98 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __SERIAL_H__ +#define __SERIAL_H__ + +#include +#include "macros.h" + +#define MYSERIAL0 Serial + +extern const char echomagic[] PROGMEM; +extern const char errormagic[] PROGMEM; + +#define SERIAL_CHAR(x) ((void)MYSERIAL0.write(x)) +#define SERIAL_EOL() SERIAL_CHAR('\n') + +#define SERIAL_PRINT(x,b) MYSERIAL0.print(x,b) +#define SERIAL_PRINTLN(x,b) MYSERIAL0.println(x,b) +#define SERIAL_PRINTF(args...) MYSERIAL0.printf(args) + +#define SERIAL_FLUSH() MYSERIAL0.flush() + +#define SERIAL_PROTOCOLCHAR(x) SERIAL_CHAR(x) +#define SERIAL_PROTOCOL(x) MYSERIAL0.print(x) +#define SERIAL_PROTOCOL_F(x,y) MYSERIAL0.print(x,y) +#define SERIAL_PROTOCOLPGM(x) serialprintPGM(PSTR(x)) +#define SERIAL_PROTOCOLLN(x) do{ MYSERIAL0.print(x); SERIAL_EOL(); }while(0) +#define SERIAL_PROTOCOLLNPGM(x) serialprintPGM(PSTR(x "\n")) +#define SERIAL_PROTOCOLPAIR(name, value) serial_echopair_PGM(PSTR(name),(value)) +#define SERIAL_PROTOCOLLNPAIR(name, value) do{ SERIAL_PROTOCOLPAIR(name, value); SERIAL_EOL(); }while(0) + +#define SERIAL_ECHO_START() serialprintPGM(echomagic) +#define SERIAL_ECHO(x) SERIAL_PROTOCOL(x) +#define SERIAL_ECHOPGM(x) SERIAL_PROTOCOLPGM(x) +#define SERIAL_ECHOLN(x) SERIAL_PROTOCOLLN(x) +#define SERIAL_ECHOLNPGM(x) SERIAL_PROTOCOLLNPGM(x) +#define SERIAL_ECHOPAIR(pre,value) SERIAL_PROTOCOLPAIR(pre, value) +#define SERIAL_ECHOLNPAIR(pre,value) SERIAL_PROTOCOLLNPAIR(pre, value) +#define SERIAL_ECHO_F(x,y) SERIAL_PROTOCOL_F(x,y) + +#define SERIAL_ERROR_START() serialprintPGM(errormagic) +#define SERIAL_ERROR(x) SERIAL_PROTOCOL(x) +#define SERIAL_ERRORPGM(x) SERIAL_PROTOCOLPGM(x) +#define SERIAL_ERRORLN(x) SERIAL_PROTOCOLLN(x) +#define SERIAL_ERRORLNPGM(x) SERIAL_PROTOCOLLNPGM(x) + +// These macros compensate for float imprecision +#define SERIAL_PROTOCOLPAIR_F(pre, value) SERIAL_PROTOCOLPAIR(pre, FIXFLOAT(value)) +#define SERIAL_PROTOCOLLNPAIR_F(pre, value) SERIAL_PROTOCOLLNPAIR(pre, FIXFLOAT(value)) +#define SERIAL_ECHOPAIR_F(pre,value) SERIAL_ECHOPAIR(pre, FIXFLOAT(value)) +#define SERIAL_ECHOLNPAIR_F(pre, value) SERIAL_ECHOLNPAIR(pre, FIXFLOAT(value)) + +// +// Functions for serial printing from PROGMEM. (Saves loads of SRAM.) +// +FORCE_INLINE void serialprintPGM(const char* str) { + while (char ch = pgm_read_byte(str++)) SERIAL_CHAR(ch); +} + +void serial_echopair_PGM(const char* s_P, const char *v); +void serial_echopair_PGM(const char* s_P, char v); +void serial_echopair_PGM(const char* s_P, int v); +void serial_echopair_PGM(const char* s_P, long v); +void serial_echopair_PGM(const char* s_P, float v); +void serial_echopair_PGM(const char* s_P, double v); +void serial_echopair_PGM(const char* s_P, unsigned int v); +void serial_echopair_PGM(const char* s_P, unsigned long v); +FORCE_INLINE void serial_echopair_PGM(const char* s_P, uint8_t v) { serial_echopair_PGM(s_P, (int)v); } +FORCE_INLINE void serial_echopair_PGM(const char* s_P, uint16_t v) { serial_echopair_PGM(s_P, (int)v); } +FORCE_INLINE void serial_echopair_PGM(const char* s_P, bool v) { serial_echopair_PGM(s_P, (int)v); } +FORCE_INLINE void serial_echopair_PGM(const char* s_P, void *v) { serial_echopair_PGM(s_P, (unsigned long)v); } + +void serial_spaces(uint8_t count); +#define SERIAL_ECHO_SP(C) serial_spaces(C) +#define SERIAL_ERROR_SP(C) serial_spaces(C) +#define SERIAL_PROTOCOL_SP(C) serial_spaces(C) + +#endif // __SERIAL_H__ From 0cea8d6ba9b14b17b0ea3b872962eb13907aaeab Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 21 Apr 2020 14:37:59 +0800 Subject: [PATCH 04/14] rearrange the code --- ESPWebDAV.cpp | 1 + ESPWebDAV.h | 7 +- ESPWebDAV.ino | 365 +++----------------------------------------------- config.cpp | 14 ++ config.h | 1 + gcode.cpp | 181 +++++++++++++++++++++++++ gcode.h | 53 ++++++++ network.cpp | 141 +++++++++++++++++++ network.h | 37 +++++ parser.h | 1 + serial.h | 2 + 11 files changed, 449 insertions(+), 354 deletions(-) create mode 100644 gcode.cpp create mode 100644 gcode.h create mode 100644 network.cpp create mode 100644 network.h diff --git a/ESPWebDAV.cpp b/ESPWebDAV.cpp index 7db9aa9..63666d9 100644 --- a/ESPWebDAV.cpp +++ b/ESPWebDAV.cpp @@ -566,3 +566,4 @@ void ESPWebDAV::handleDelete(ResourceType resource) { send("200 OK", NULL, ""); } +ESPWebDAV dav; diff --git a/ESPWebDAV.h b/ESPWebDAV.h index 983c4cc..89e06a3 100644 --- a/ESPWebDAV.h +++ b/ESPWebDAV.h @@ -4,11 +4,9 @@ #define DEBUG #ifdef DEBUG - #define DBG_INIT(...) { Serial.begin(__VA_ARGS__); } #define DBG_PRINT(...) { Serial.print(__VA_ARGS__); } #define DBG_PRINTLN(...) { Serial.println(__VA_ARGS__); } #else - #define DBG_INIT(...) {} #define DBG_PRINT(...) {} #define DBG_PRINTLN(...) {} #endif @@ -83,7 +81,4 @@ class ESPWebDAV { int _contentLength; }; - - - - +extern ESPWebDAV dav; diff --git a/ESPWebDAV.ino b/ESPWebDAV.ino index 76352dc..48b30ef 100644 --- a/ESPWebDAV.ino +++ b/ESPWebDAV.ino @@ -1,120 +1,29 @@ // Using the WebDAV server with Rigidbot 3D printer. // Printer controller is a variation of Rambo running Marlin firmware -#include "ESP8266WiFi.h" -#include "ESPWebDAV.h" #include "serial.h" #include "parser.h" -#include "Config.h" +#include "config.h" +#include "network.h" +#include "gcode.h" // LED is connected to GPIO2 on this board #define INIT_LED {pinMode(2, OUTPUT);} #define LED_ON {digitalWrite(2, LOW);} #define LED_OFF {digitalWrite(2, HIGH);} -#define HOSTNAME "FYSETC" -#define SERVER_PORT 80 -#define SPI_BLOCKOUT_PERIOD 20000UL -#define WIFI_CONNECT_TIMEOUT 30000UL - -#define SD_CS 4 -#define MISO 12 -#define MOSI 13 -#define SCLK 14 -#define CS_SENSE 5 - -ESPWebDAV dav; -String statusMessage; -bool initFailed = false; - -volatile long spiBlockoutTime = 0; -bool weHaveBus = false; -bool wifiConnected = false; - -bool wifiConnect(const char*ssid,const char*password) { - if(ssid == NULL || password==NULL) { - SERIAL_ECHOLN("Please set the wifi ssid and password first"); - } - - // - wifiConnected = false; - - // Set hostname first - WiFi.hostname(HOSTNAME); - // Reduce startup surge current - WiFi.setAutoConnect(false); - WiFi.mode(WIFI_STA); - WiFi.setPhyMode(WIFI_PHY_MODE_11N); - WiFi.begin(ssid, password); - - // Wait for connection - unsigned long timeout = millis(); - while(WiFi.status() != WL_CONNECTED) { - blink(); - DBG_PRINT("."); - if(millis() > timeout + WIFI_CONNECT_TIMEOUT) - return false; - } - - DBG_PRINTLN(""); - DBG_PRINT("Connected to "); DBG_PRINTLN(ssid); - DBG_PRINT("IP address: "); DBG_PRINTLN(WiFi.localIP()); - DBG_PRINT("RSSI: "); DBG_PRINTLN(WiFi.RSSI()); - DBG_PRINT("Mode: "); DBG_PRINTLN(WiFi.getPhyMode()); - - wifiConnected = true; - - config.save(ssid,password); - - return true; -} - -void startDAVServer() { - // Check to see if other master is using the SPI bus - while(millis() < spiBlockoutTime) - blink(); - - takeBusControl(); - - // start the SD DAV server - if(!dav.init(SD_CS, SPI_FULL_SPEED, SERVER_PORT)) { - statusMessage = "Failed to initialize SD Card"; - DBG_PRINT("ERROR: "); DBG_PRINTLN(statusMessage); - // indicate error on LED - errorBlink(); - initFailed = true; - } - else - blink(); - - relenquishBusControl(); - DBG_PRINTLN("WebDAV server started"); -} - // ------------------------ void setup() { - // ----- GPIO ------- - // Detect when other master uses SPI bus - pinMode(CS_SENSE, INPUT); - attachInterrupt(CS_SENSE, []() { - if(!weHaveBus) - spiBlockoutTime = millis() + SPI_BLOCKOUT_PERIOD; - }, FALLING); - - DBG_INIT(115200); - DBG_PRINTLN(""); + SERIAL_INIT(115200); INIT_LED; blink(); - // wait for other master to assert SPI bus first - delay(SPI_BLOCKOUT_PERIOD); + network.setup(); // ----- WIFI ------- if(config.load() == 1) { // Connected before - if(wifiConnect(config.ssid(), config.password())) { - startDAVServer(); - } - else { + if(!network.start()) { + SERIAL_ECHOLN(""); SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); } } @@ -123,260 +32,18 @@ void setup() { } } -#define MAX_CMD_SIZE 96 -#define BUFSIZE 4 - -/** - * GCode Command Queue - * A simple ring buffer of BUFSIZE command strings. - * - * Commands are copied into this buffer by the command injectors - * (immediate, serial, sd card) and they are processed sequentially by - * the main loop. The process_next_command function parses the next - * command and hands off execution to individual handler functions. - */ -uint8_t commands_in_queue = 0, // Count of commands in the queue - cmd_queue_index_r = 0, // Ring buffer read (out) position - cmd_queue_index_w = 0; // Ring buffer write (in) position - -uint32_t command_sd_pos[BUFSIZE]; -volatile uint32_t current_command_sd_pos; - -char command_queue[BUFSIZE][MAX_CMD_SIZE]; - -static bool send_ok[BUFSIZE]; - -// Number of characters read in the current line of serial input -static int serial_count; // = 0; - -/** - * Once a new command is in the ring buffer, call this to commit it - */ -inline void _commit_command(bool say_ok) { - send_ok[cmd_queue_index_w] = say_ok; - if (++cmd_queue_index_w >= BUFSIZE) cmd_queue_index_w = 0; - commands_in_queue++; -} - -/** - * Copy a command from RAM into the main command buffer. - * Return true if the command was successfully added. - * Return false for a full buffer, or if the 'command' is a comment. - */ -inline bool _enqueuecommand(const char* cmd, bool say_ok=false) { - if (*cmd == ';' || commands_in_queue >= BUFSIZE) return false; - strcpy(command_queue[cmd_queue_index_w], cmd); - _commit_command(say_ok); - return true; -} - -/** - * Get all commands waiting on the serial port and queue them. - * Exit when the buffer is full or when no more characters are - * left on the serial port. - */ -void get_serial_commands() { - static char serial_line_buffer[MAX_CMD_SIZE]; - static bool serial_comment_mode = false; - - /** - * Loop while serial characters are incoming and the queue is not full - */ - int c; - while (commands_in_queue < BUFSIZE && (c = Serial.read()) >= 0) { - - char serial_char = c; - - /** - * If the character ends the line - */ - if (serial_char == '\n' || serial_char == '\r') { - - serial_comment_mode = false; // end of line == end of comment - - // Skip empty lines and comments - if (!serial_count) { continue; } - - serial_line_buffer[serial_count] = 0; // Terminate string - serial_count = 0; // Reset buffer - - char* command = serial_line_buffer; - - while (*command == ' ') command++; // Skip leading spaces - - // Add the command to the queue - _enqueuecommand(serial_line_buffer, true); - } - else if (serial_count >= MAX_CMD_SIZE - 1) { - // Keep fetching, but ignore normal characters beyond the max length - // The command will be injected when EOL is reached - } - else if (serial_char == '\\') { // Handle escapes - if ((c = MYSERIAL0.read()) >= 0 && !serial_comment_mode) // if we have one more character, copy it over - serial_line_buffer[serial_count++] = (char)c; - // otherwise do nothing - } - else { // it's not a newline, carriage return or escape char - if (serial_char == ';') serial_comment_mode = true; - if (!serial_comment_mode) serial_line_buffer[serial_count++] = serial_char; - } - } // queue has space, serial has data -} - -/** - * M50: Set the Wifi ssid - */ -inline void gcode_M50() { - for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; - config.ssid(parser.string_arg); - SERIAL_ECHO(config.ssid()); -} - -/** - * M50: Set the Wifi password - */ -inline void gcode_M51() { - for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; - config.password(parser.string_arg); - SERIAL_ECHO(config.password()); -} - -/** - * M52: Connect the wifi - */ -inline void gcode_M52() { - if(wifiConnect(config.ssid(), config.password())) { - startDAVServer(); - } - else { - SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); - } -} - -/** - * M53: Check wifi status - */ -inline void gcode_M53() { - if(WiFi.status() != WL_CONNECTED) { - SERIAL_ECHOLN("Wifi not connected"); - SERIAL_ECHOLN("Please set the wifi ssid with M50 and password with M51 , and start connection with M52"); - } - else { - DBG_PRINTLN(""); - DBG_PRINT("Connected to "); SERIAL_ECHOLN(WiFi.SSID()); - DBG_PRINT("IP address: "); DBG_PRINTLN(WiFi.localIP()); - DBG_PRINT("RSSI: "); DBG_PRINTLN(WiFi.RSSI()); - DBG_PRINT("Mode: "); DBG_PRINTLN(WiFi.getPhyMode()); - } -} - -/** - * Process the parsed command and dispatch it to its handler - */ -void process_parsed_command() { - //SERIAL_ECHOLNPAIR("command_letter:", parser.command_letter); - //SERIAL_ECHOLNPAIR("codenum:", parser.codenum); - - // Handle a known G, M, or T - switch (parser.command_letter) { - case 'G': switch (parser.codenum) { - default: parser.unknown_command_error(); - } - break; - - case 'M': switch (parser.codenum) { - case 50: gcode_M50(); break; - case 51: gcode_M51(); break; - case 52: gcode_M52(); break; - case 53: gcode_M53(); break; - default: parser.unknown_command_error(); - } - break; - - default: parser.unknown_command_error(); - } - - SERIAL_ECHOLN("ok"); -} - -void process_next_command() { - char * const current_command = command_queue[cmd_queue_index_r]; - current_command_sd_pos = command_sd_pos[cmd_queue_index_r]; - - // Parse the next command in the queue - parser.parse(current_command); - process_parsed_command(); -} - // ------------------------ void loop() { -// ------------------------ - if(millis() < spiBlockoutTime) - blink(); + // handle the request + network.handle(); - // do it only if there is a need to read FS - if(wifiConnected) { - if(dav.isClientWaiting()) { - if(initFailed) - return dav.rejectClient(statusMessage); - - // has other master been using the bus in last few seconds - if(millis() < spiBlockoutTime) - return dav.rejectClient("Marlin is reading from SD card"); - - // a client is waiting and FS is ready and other SPI master is not using the bus - takeBusControl(); - dav.handleClient(); - relenquishBusControl(); - } - } + // Handle gcode + gcode.Handle(); - // Get the serial input - if (commands_in_queue < BUFSIZE) get_serial_commands(); - - if (commands_in_queue) { - process_next_command(); - - // The queue may be reset by a command handler or by code invoked by idle() within a handler - if (commands_in_queue) { - --commands_in_queue; - if (++cmd_queue_index_r >= BUFSIZE) cmd_queue_index_r = 0; - } - } - - // + // blink statusBlink(); } - - -// ------------------------ -void takeBusControl() { -// ------------------------ - weHaveBus = true; - LED_ON; - pinMode(MISO, SPECIAL); - pinMode(MOSI, SPECIAL); - pinMode(SCLK, SPECIAL); - pinMode(SD_CS, OUTPUT); -} - - - -// ------------------------ -void relenquishBusControl() { -// ------------------------ - pinMode(MISO, INPUT); - pinMode(MOSI, INPUT); - pinMode(SCLK, INPUT); - pinMode(SD_CS, INPUT); - LED_OFF; - weHaveBus = false; -} - - - - // ------------------------ void blink() { // ------------------------ @@ -386,8 +53,6 @@ void blink() { delay(400); } - - // ------------------------ void errorBlink() { // ------------------------ @@ -402,7 +67,7 @@ void errorBlink() { void statusBlink() { static unsigned long time = 0; if(millis() > time + 1000 ) { - if(wifiConnected) { + if(network.isConnected()) { LED_ON; delay(50); LED_OFF; @@ -412,4 +77,8 @@ void statusBlink() { } time = millis(); } + + // SPI bus not ready + //if(millis() < spiBlockoutTime) + // blink(); } diff --git a/config.cpp b/config.cpp index 56e728b..f7cb147 100644 --- a/config.cpp +++ b/config.cpp @@ -48,4 +48,18 @@ void Config::save(const char*ssid,const char*password) { EEPROM.commit(); } +void Config::save() { + if(data.ssid == NULL || data.psw == NULL) + return; + + EEPROM.begin(EEPROM_SIZE); + data.flag = 1; + uint8_t *p = (uint8_t*)(&data); + for (int i = 0; i < sizeof(data); i++) + { + EEPROM.write(i, *(p + i)); + } + EEPROM.commit(); +} + Config config; diff --git a/config.h b/config.h index 12593a9..e82ca04 100644 --- a/config.h +++ b/config.h @@ -24,6 +24,7 @@ class Config { char* password(); void password(char* password); void save(const char*ssid,const char*password); + void save(); protected: CONFIG_TYPE data; diff --git a/gcode.cpp b/gcode.cpp new file mode 100644 index 0000000..36b644a --- /dev/null +++ b/gcode.cpp @@ -0,0 +1,181 @@ +#include "gcode.h" +#include "config.h" +#include "parser.h" +#include "network.h" +#include "serial.h" +#include + +Gcode gcode; + +void Gcode::Handle() { + // Get the serial input + if (commands_in_queue < BUFSIZE) get_serial_commands(); + + if (commands_in_queue) { + process_next_command(); + + // The queue may be reset by a command handler or by code invoked by idle() within a handler + if (commands_in_queue) { + --commands_in_queue; + if (++cmd_queue_index_r >= BUFSIZE) cmd_queue_index_r = 0; + } + } +} + +/** + * Once a new command is in the ring buffer, call this to commit it + */ +void Gcode::_commit_command(bool say_ok) { + send_ok[cmd_queue_index_w] = say_ok; + if (++cmd_queue_index_w >= BUFSIZE) cmd_queue_index_w = 0; + commands_in_queue++; +} + +/** + * Copy a command from RAM into the main command buffer. + * Return true if the command was successfully added. + * Return false for a full buffer, or if the 'command' is a comment. + */ +bool Gcode::_enqueuecommand(const char* cmd, bool say_ok) { + if (*cmd == ';' || commands_in_queue >= BUFSIZE) return false; + strcpy(command_queue[cmd_queue_index_w], cmd); + _commit_command(say_ok); + return true; +} + +/** + * Get all commands waiting on the serial port and queue them. + * Exit when the buffer is full or when no more characters are + * left on the serial port. + */ +void Gcode::get_serial_commands() { + static char serial_line_buffer[MAX_CMD_SIZE]; + static bool serial_comment_mode = false; + + /** + * Loop while serial characters are incoming and the queue is not full + */ + int c; + while (commands_in_queue < BUFSIZE && (c = Serial.read()) >= 0) { + + char serial_char = c; + + /** + * If the character ends the line + */ + if (serial_char == '\n' || serial_char == '\r') { + + serial_comment_mode = false; // end of line == end of comment + + // Skip empty lines and comments + if (!serial_count) { continue; } + + serial_line_buffer[serial_count] = 0; // Terminate string + serial_count = 0; // Reset buffer + + char* command = serial_line_buffer; + + while (*command == ' ') command++; // Skip leading spaces + + // Add the command to the queue + _enqueuecommand(serial_line_buffer, true); + } + else if (serial_count >= MAX_CMD_SIZE - 1) { + // Keep fetching, but ignore normal characters beyond the max length + // The command will be injected when EOL is reached + } + else if (serial_char == '\\') { // Handle escapes + if ((c = MYSERIAL0.read()) >= 0 && !serial_comment_mode) // if we have one more character, copy it over + serial_line_buffer[serial_count++] = (char)c; + // otherwise do nothing + } + else { // it's not a newline, carriage return or escape char + if (serial_char == ';') serial_comment_mode = true; + if (!serial_comment_mode) serial_line_buffer[serial_count++] = serial_char; + } + } // queue has space, serial has data +} + +/** + * M50: Set the Wifi ssid + */ +void Gcode::gcode_M50() { + for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; + config.ssid(parser.string_arg); + SERIAL_ECHO(config.ssid()); +} + +/** + * M50: Set the Wifi password + */ +void Gcode::gcode_M51() { + for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; + config.password(parser.string_arg); + SERIAL_ECHO(config.password()); +} + +/** + * M52: Connect the wifi + */ +void Gcode::gcode_M52() { + if(!network.start()) { + SERIAL_ECHOLN(""); + SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); + } +} + +/** + * M53: Check wifi status + */ +void Gcode::gcode_M53() { + if(WiFi.status() != WL_CONNECTED) { + SERIAL_ECHOLN("Wifi not connected"); + SERIAL_ECHOLN("Please set the wifi ssid with M50 and password with M51 , and start connection with M52"); + } + else { + SERIAL_ECHOLN(""); + SERIAL_ECHO("Connected to "); SERIAL_ECHOLN(WiFi.SSID()); + SERIAL_ECHO("IP address: "); SERIAL_ECHOLN(WiFi.localIP()); + SERIAL_ECHO("RSSI: "); SERIAL_ECHOLN(WiFi.RSSI()); + SERIAL_ECHO("Mode: "); SERIAL_ECHOLN(WiFi.getPhyMode()); + SERIAL_ECHO("Asscess to SD at the Run prompt : \\\\"); SERIAL_ECHO(WiFi.localIP());SERIAL_ECHOLN("\\DavWWWRoot"); + } +} + +/** + * Process the parsed command and dispatch it to its handler + */ +void Gcode::process_parsed_command() { + //SERIAL_ECHOLNPAIR("command_letter:", parser.command_letter); + //SERIAL_ECHOLNPAIR("codenum:", parser.codenum); + + // Handle a known G, M, or T + switch (parser.command_letter) { + case 'G': switch (parser.codenum) { + default: parser.unknown_command_error(); + } + break; + + case 'M': switch (parser.codenum) { + case 50: gcode_M50(); break; + case 51: gcode_M51(); break; + case 52: gcode_M52(); break; + case 53: gcode_M53(); break; + default: parser.unknown_command_error(); + } + break; + + default: parser.unknown_command_error(); + } + + SERIAL_ECHOLN("ok"); +} + +void Gcode::process_next_command() { + char * const current_command = command_queue[cmd_queue_index_r]; + current_command_sd_pos = command_sd_pos[cmd_queue_index_r]; + + // Parse the next command in the queue + parser.parse(current_command); + process_parsed_command(); +} diff --git a/gcode.h b/gcode.h new file mode 100644 index 0000000..dcf04a6 --- /dev/null +++ b/gcode.h @@ -0,0 +1,53 @@ +#ifndef _GCODE_H_ +#define _GCODE_H_ + +#define MAX_CMD_SIZE 96 +#define BUFSIZE 4 + +/** + * GCode + * + * - Handle gcode + */ +class Gcode { +public: + void Handle(); + +private: + void _commit_command(bool say_ok); + bool _enqueuecommand(const char* cmd, bool say_ok=false); + void get_serial_commands(); + void gcode_M50(); + void gcode_M51(); + void gcode_M52(); + void gcode_M53(); + void process_parsed_command(); + void process_next_command(); + + /** + * GCode Command Queue + * A simple ring buffer of BUFSIZE command strings. + * + * Commands are copied into this buffer by the command injectors + * (immediate, serial, sd card) and they are processed sequentially by + * the main loop. The process_next_command function parses the next + * command and hands off execution to individual handler functions. + */ + unsigned char commands_in_queue = 0, // Count of commands in the queue + cmd_queue_index_r = 0, // Ring buffer read (out) position + cmd_queue_index_w = 0; // Ring buffer write (in) position + + unsigned long command_sd_pos[BUFSIZE]; + volatile unsigned long current_command_sd_pos; + + char command_queue[BUFSIZE][MAX_CMD_SIZE]; + + bool send_ok[BUFSIZE]; + + // Number of characters read in the current line of serial input + int serial_count; // = 0; +}; + +extern Gcode gcode; + +#endif diff --git a/network.cpp b/network.cpp new file mode 100644 index 0000000..99b1b94 --- /dev/null +++ b/network.cpp @@ -0,0 +1,141 @@ +#include "network.h" +#include "serial.h" +#include "config.h" +#include "ESP8266WiFi.h" +#include "ESPWebDAV.h" + +volatile long Network::_spiBlockoutTime = 0; +bool Network::_weHaveBus = false; + +void Network::setup() { + // ----- GPIO ------- + // Detect when other master uses SPI bus + pinMode(CS_SENSE, INPUT); + attachInterrupt(CS_SENSE, []() { + if(!_weHaveBus) + _spiBlockoutTime = millis() + SPI_BLOCKOUT_PERIOD; + }, FALLING); + + // wait for other master to assert SPI bus first + delay(SPI_BLOCKOUT_PERIOD); +} + +bool Network::start() { + wifiConnected = false; + + // Set hostname first + WiFi.hostname(HOSTNAME); + // Reduce startup surge current + WiFi.setAutoConnect(false); + WiFi.mode(WIFI_STA); + WiFi.setPhyMode(WIFI_PHY_MODE_11N); + WiFi.begin(config.ssid(), config.password()); + + // Wait for connection + unsigned int timeout = 0; + while(WiFi.status() != WL_CONNECTED) { + //blink(); + SERIAL_ECHO("."); + timeout++; + if(timeout++ > WIFI_CONNECT_TIMEOUT/100) + return false; + else + delay(100); + } + + SERIAL_ECHOLN(""); + SERIAL_ECHO("Connected to "); SERIAL_ECHOLN(config.ssid()); + SERIAL_ECHO("IP address: "); SERIAL_ECHOLN(WiFi.localIP()); + SERIAL_ECHO("RSSI: "); SERIAL_ECHOLN(WiFi.RSSI()); + SERIAL_ECHO("Mode: "); SERIAL_ECHOLN(WiFi.getPhyMode()); + SERIAL_ECHO("Asscess to SD at the Run prompt : \\\\"); SERIAL_ECHO(WiFi.localIP());SERIAL_ECHOLN("\\DavWWWRoot"); + + wifiConnected = true; + + config.save(); + + startDAVServer(); + + return true; +} + +void Network::startDAVServer() { + // Check to see if other master is using the SPI bus + while(millis() < _spiBlockoutTime) { + //blink(); + } + + takeBusControl(); + + // start the SD DAV server + if(!dav.init(SD_CS, SPI_FULL_SPEED, SERVER_PORT)) { + DBG_PRINT("ERROR: "); DBG_PRINTLN("Failed to initialize SD Card"); + // indicate error on LED + //errorBlink(); + initFailed = true; + } + else { + //blink(); + } + + relenquishBusControl(); + DBG_PRINTLN("WebDAV server started"); +} + +bool Network::isConnected() { + return wifiConnected; +} + +// a client is waiting and FS is ready and other SPI master is not using the bus +bool Network::ready() { + if(!isConnected()) return false; + + // do it only if there is a need to read FS + if(!dav.isClientWaiting()) return false; + + if(initFailed) { + dav.rejectClient("Failed to initialize SD Card"); + return false; + } + + // has other master been using the bus in last few seconds + if(millis() < _spiBlockoutTime) { + dav.rejectClient("Marlin is reading from SD card"); + return false; + } + + return true; +} + +void Network::handle() { + if(network.ready()) { + takeBusControl(); + dav.handleClient(); + relenquishBusControl(); + } +} + +// ------------------------ +void Network::takeBusControl() { +// ------------------------ + _weHaveBus = true; + //LED_ON; + pinMode(MISO_PIN, SPECIAL); + pinMode(MOSI_PIN, SPECIAL); + pinMode(SCLK_PIN, SPECIAL); + pinMode(SD_CS, OUTPUT); +} + +// ------------------------ +void Network::relenquishBusControl() { +// ------------------------ + pinMode(MISO_PIN, INPUT); + pinMode(MOSI_PIN, INPUT); + pinMode(SCLK_PIN, INPUT); + pinMode(SD_CS, INPUT); + //LED_OFF; + _weHaveBus = false; +} + + +Network network; diff --git a/network.h b/network.h new file mode 100644 index 0000000..3db8398 --- /dev/null +++ b/network.h @@ -0,0 +1,37 @@ +#ifndef _NETWORK_H_ +#define _NETWORK_H_ + +#define HOSTNAME "FYSETC" +#define SERVER_PORT 80 + +#define SD_CS 4 +#define MISO_PIN 12 +#define MOSI_PIN 13 +#define SCLK_PIN 14 +#define CS_SENSE 5 + +#define SPI_BLOCKOUT_PERIOD 20000UL +#define WIFI_CONNECT_TIMEOUT 30000UL + +class Network { +public: + Network() { initFailed = false;} + static void setup(); + static void takeBusControl(); + static void relenquishBusControl(); + bool start(); + void startDAVServer(); + bool isConnected(); + void handle(); + bool ready(); + +private: + bool wifiConnected; + bool initFailed; + static volatile long _spiBlockoutTime; + static bool _weHaveBus; +}; + +extern Network network; + +#endif diff --git a/parser.h b/parser.h index 684cca2..1f3f99d 100644 --- a/parser.h +++ b/parser.h @@ -10,6 +10,7 @@ //#include "enum.h" #include +#include #include "macros.h" #define strtof strtod diff --git a/serial.h b/serial.h index f05c069..4c276a2 100644 --- a/serial.h +++ b/serial.h @@ -28,6 +28,8 @@ #define MYSERIAL0 Serial +#define SERIAL_INIT(...) { Serial.begin(__VA_ARGS__); } + extern const char echomagic[] PROGMEM; extern const char errormagic[] PROGMEM; From 8d9dea5f3eff7bce861da9c97ae6bac85ac8e139 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 21 Apr 2020 17:26:40 +0800 Subject: [PATCH 05/14] Update serial info --- ESPWebDAV.ino | 12 ++++++++++-- gcode.cpp | 16 ++++++++++++---- network.cpp | 6 ++++-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ESPWebDAV.ino b/ESPWebDAV.ino index 48b30ef..41c92fd 100644 --- a/ESPWebDAV.ino +++ b/ESPWebDAV.ino @@ -23,12 +23,20 @@ void setup() { // ----- WIFI ------- if(config.load() == 1) { // Connected before if(!network.start()) { - SERIAL_ECHOLN(""); SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); + SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); + SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); + SERIAL_ECHOLN("- M52: Start to connect the wifi"); + SERIAL_ECHOLN("- M53: Check the connection status"); } } else { - SERIAL_ECHOLN("Please set the wifi ssid with M50 and password with M51 , and start connection with M52"); + SERIAL_ECHOLN("Welcome to FYSETC: www.fysetc.com"); + SERIAL_ECHOLN("Please set the wifi config first"); + SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); + SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); + SERIAL_ECHOLN("- M52: Start to connect the wifi"); + SERIAL_ECHOLN("- M53: Check the connection status"); } } diff --git a/gcode.cpp b/gcode.cpp index 36b644a..a5f0314 100644 --- a/gcode.cpp +++ b/gcode.cpp @@ -102,7 +102,8 @@ void Gcode::get_serial_commands() { void Gcode::gcode_M50() { for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; config.ssid(parser.string_arg); - SERIAL_ECHO(config.ssid()); + SERIAL_ECHO("ssid:"); + SERIAL_ECHOLN(config.ssid()); } /** @@ -111,7 +112,8 @@ void Gcode::gcode_M50() { void Gcode::gcode_M51() { for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; config.password(parser.string_arg); - SERIAL_ECHO(config.password()); + SERIAL_ECHO("password:"); + SERIAL_ECHOLN(config.password()); } /** @@ -119,8 +121,11 @@ void Gcode::gcode_M51() { */ void Gcode::gcode_M52() { if(!network.start()) { - SERIAL_ECHOLN(""); SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); + SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); + SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); + SERIAL_ECHOLN("- M52: Start to connect the wifi"); + SERIAL_ECHOLN("- M53: Check the connection status"); } } @@ -130,7 +135,10 @@ void Gcode::gcode_M52() { void Gcode::gcode_M53() { if(WiFi.status() != WL_CONNECTED) { SERIAL_ECHOLN("Wifi not connected"); - SERIAL_ECHOLN("Please set the wifi ssid with M50 and password with M51 , and start connection with M52"); + SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); + SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); + SERIAL_ECHOLN("- M52: Start to connect the wifi"); + SERIAL_ECHOLN("- M53: Check the connection status"); } else { SERIAL_ECHOLN(""); diff --git a/network.cpp b/network.cpp index 99b1b94..75654b2 100644 --- a/network.cpp +++ b/network.cpp @@ -37,8 +37,10 @@ bool Network::start() { //blink(); SERIAL_ECHO("."); timeout++; - if(timeout++ > WIFI_CONNECT_TIMEOUT/100) + if(timeout++ > WIFI_CONNECT_TIMEOUT/100) { + SERIAL_ECHOLN(""); return false; + } else delay(100); } @@ -79,7 +81,7 @@ void Network::startDAVServer() { } relenquishBusControl(); - DBG_PRINTLN("WebDAV server started"); + DBG_PRINTLN("FYSETC WebDAV server started"); } bool Network::isConnected() { From 31115f6f705f07e8483d7284f7704829ca71c0c9 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 23 Jun 2020 16:52:25 +0800 Subject: [PATCH 06/14] Support ini file to setup wifi network --- ESPWebDAV.ino | 5 +-- config.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++- config.h | 1 + gcode.cpp | 2 +- network.cpp | 63 ++++++++---------------------------- network.h | 14 +------- pins.h | 10 ++++++ sdControl.cpp | 48 +++++++++++++++++++++++++++ sdControl.h | 21 ++++++++++++ 9 files changed, 187 insertions(+), 67 deletions(-) create mode 100644 pins.h create mode 100644 sdControl.cpp create mode 100644 sdControl.h diff --git a/ESPWebDAV.ino b/ESPWebDAV.ino index 41c92fd..bfbe76d 100644 --- a/ESPWebDAV.ino +++ b/ESPWebDAV.ino @@ -6,6 +6,7 @@ #include "config.h" #include "network.h" #include "gcode.h" +#include "sdControl.h" // LED is connected to GPIO2 on this board #define INIT_LED {pinMode(2, OUTPUT);} @@ -18,12 +19,12 @@ void setup() { INIT_LED; blink(); - network.setup(); + sdcontrol.setup(); // ----- WIFI ------- if(config.load() == 1) { // Connected before if(!network.start()) { - SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); + SERIAL_ECHOLN("Connect fail, please check your INI file or set the wifi config and connect again"); SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); SERIAL_ECHOLN("- M52: Start to connect the wifi"); diff --git a/config.cpp b/config.cpp index f7cb147..cc2df90 100644 --- a/config.cpp +++ b/config.cpp @@ -1,8 +1,96 @@ +#include +#include +#include #include +#include "pins.h" #include "config.h" +#include "serial.h" +#include "sdControl.h" + +int Config::loadSD() { + SdFat sdfat; + + SERIAL_ECHOLN("Going to load config from INI file"); + + if(!sdcontrol.canWeTakeBus()) { + SERIAL_ECHOLN("Marlin is controling the bus"); + return -1; + } + sdcontrol.takeBusControl(); + + if(!sdfat.begin(SD_CS, SPI_FULL_SPEED)) { + SERIAL_ECHOLN("Initial SD failed"); + sdcontrol.relinquishBusControl(); + return -2; + } + + File file = sdfat.open("SETUP.INI", FILE_READ); + if (!file) { + SERIAL_ECHOLN("Open INI file failed"); + sdcontrol.relinquishBusControl(); + return -3; + } + + // Get SSID and PASSWORD from file + int rst = 0,step = 0; + String buffer,sKEY,sValue; + while (file.available()) { // check for EOF + buffer = file.readStringUntil('\n'); + if(buffer.length() == 0) continue; // Empty line + buffer.replace(" ", ""); // Delete all blank + buffer.replace("\r", ""); // Delete all CR + int iS = buffer.indexOf('='); // Get the seperator + if(iS < 0) continue; // Bad line + sKEY = buffer.substring(0,iS); + sValue = buffer.substring(iS+1); + if(sKEY == "SSID") { + SERIAL_ECHOLN("INI file : SSID found"); + if(sValue.length()>0) { + memset(data.ssid,'\0',WIFI_SSID_LEN); + sValue.toCharArray(data.ssid,WIFI_SSID_LEN); + step++; + } + else { + rst = -4; + goto FAIL; + } + } + else if(sKEY == "PASSWORD") { + SERIAL_ECHOLN("INI file : PASSWORD found"); + if(sValue.length()>0) { + memset(data.psw,'\0',WIFI_PASSWD_LEN); + sValue.toCharArray(data.psw,WIFI_PASSWD_LEN); + step++; + } + else { + rst = -5; + goto FAIL; + } + } + else continue; // Bad line + } + if(step != 2) { // We miss ssid or password + //memset(data,) // TODO: do we need to empty the data? + SERIAL_ECHOLN("Please check your SSDI or PASSWORD in ini file"); + rst = -6; + goto FAIL; + } + + FAIL: + file.close(); + sdcontrol.relinquishBusControl(); + return rst; +} -// ------------------------ unsigned char Config::load() { + // Try to get the config from ini file + if(0 == loadSD()) + { + return 1; // Return as connected before + } + + SERIAL_ECHOLN("Going to load config from EEPROM"); + EEPROM.begin(EEPROM_SIZE); uint8_t *p = (uint8_t*)(&data); for (int i = 0; i < sizeof(data); i++) diff --git a/config.h b/config.h index e82ca04..ab6f5a8 100644 --- a/config.h +++ b/config.h @@ -18,6 +18,7 @@ typedef struct config_type class Config { public: + int loadSD(); unsigned char load(); char* ssid(); void ssid(char* ssid); diff --git a/gcode.cpp b/gcode.cpp index a5f0314..18bd4e4 100644 --- a/gcode.cpp +++ b/gcode.cpp @@ -121,7 +121,7 @@ void Gcode::gcode_M51() { */ void Gcode::gcode_M52() { if(!network.start()) { - SERIAL_ECHOLN("Connect fail, please set the wifi config and connect again"); + SERIAL_ECHOLN("Connect fail, please check your INI file or set the wifi config and connect again"); SERIAL_ECHOLN("- M50: Set the wifi ssid , 'M50 ssid-name'"); SERIAL_ECHOLN("- M51: Set the wifi password , 'M51 password'"); SERIAL_ECHOLN("- M52: Start to connect the wifi"); diff --git a/network.cpp b/network.cpp index 75654b2..616b6a3 100644 --- a/network.cpp +++ b/network.cpp @@ -1,24 +1,10 @@ #include "network.h" #include "serial.h" #include "config.h" +#include "pins.h" #include "ESP8266WiFi.h" #include "ESPWebDAV.h" - -volatile long Network::_spiBlockoutTime = 0; -bool Network::_weHaveBus = false; - -void Network::setup() { - // ----- GPIO ------- - // Detect when other master uses SPI bus - pinMode(CS_SENSE, INPUT); - attachInterrupt(CS_SENSE, []() { - if(!_weHaveBus) - _spiBlockoutTime = millis() + SPI_BLOCKOUT_PERIOD; - }, FALLING); - - // wait for other master to assert SPI bus first - delay(SPI_BLOCKOUT_PERIOD); -} +#include "sdControl.h" bool Network::start() { wifiConnected = false; @@ -56,18 +42,17 @@ bool Network::start() { config.save(); - startDAVServer(); + SERIAL_ECHOLN("Going to start DAV server"); + if(startDAVServer() < 0) return false; return true; } -void Network::startDAVServer() { - // Check to see if other master is using the SPI bus - while(millis() < _spiBlockoutTime) { - //blink(); +int Network::startDAVServer() { + if(!sdcontrol.canWeTakeBus()) { + return -1; } - - takeBusControl(); + sdcontrol.takeBusControl(); // start the SD DAV server if(!dav.init(SD_CS, SPI_FULL_SPEED, SERVER_PORT)) { @@ -80,8 +65,9 @@ void Network::startDAVServer() { //blink(); } - relenquishBusControl(); + sdcontrol.relinquishBusControl(); DBG_PRINTLN("FYSETC WebDAV server started"); + return 0; } bool Network::isConnected() { @@ -101,7 +87,7 @@ bool Network::ready() { } // has other master been using the bus in last few seconds - if(millis() < _spiBlockoutTime) { + if(!sdcontrol.canWeTakeBus()) { dav.rejectClient("Marlin is reading from SD card"); return false; } @@ -111,33 +97,10 @@ bool Network::ready() { void Network::handle() { if(network.ready()) { - takeBusControl(); + sdcontrol.takeBusControl(); dav.handleClient(); - relenquishBusControl(); + sdcontrol.relinquishBusControl(); } } -// ------------------------ -void Network::takeBusControl() { -// ------------------------ - _weHaveBus = true; - //LED_ON; - pinMode(MISO_PIN, SPECIAL); - pinMode(MOSI_PIN, SPECIAL); - pinMode(SCLK_PIN, SPECIAL); - pinMode(SD_CS, OUTPUT); -} - -// ------------------------ -void Network::relenquishBusControl() { -// ------------------------ - pinMode(MISO_PIN, INPUT); - pinMode(MOSI_PIN, INPUT); - pinMode(SCLK_PIN, INPUT); - pinMode(SD_CS, INPUT); - //LED_OFF; - _weHaveBus = false; -} - - Network network; diff --git a/network.h b/network.h index 3db8398..b583886 100644 --- a/network.h +++ b/network.h @@ -4,23 +4,13 @@ #define HOSTNAME "FYSETC" #define SERVER_PORT 80 -#define SD_CS 4 -#define MISO_PIN 12 -#define MOSI_PIN 13 -#define SCLK_PIN 14 -#define CS_SENSE 5 - -#define SPI_BLOCKOUT_PERIOD 20000UL #define WIFI_CONNECT_TIMEOUT 30000UL class Network { public: Network() { initFailed = false;} - static void setup(); - static void takeBusControl(); - static void relenquishBusControl(); bool start(); - void startDAVServer(); + int startDAVServer(); bool isConnected(); void handle(); bool ready(); @@ -28,8 +18,6 @@ class Network { private: bool wifiConnected; bool initFailed; - static volatile long _spiBlockoutTime; - static bool _weHaveBus; }; extern Network network; diff --git a/pins.h b/pins.h new file mode 100644 index 0000000..46997b3 --- /dev/null +++ b/pins.h @@ -0,0 +1,10 @@ +#ifndef _SD_H_ +#define _SD_H_ + +#define SD_CS 4 +#define MISO_PIN 12 +#define MOSI_PIN 13 +#define SCLK_PIN 14 +#define CS_SENSE 5 + +#endif \ No newline at end of file diff --git a/sdControl.cpp b/sdControl.cpp new file mode 100644 index 0000000..c061a9b --- /dev/null +++ b/sdControl.cpp @@ -0,0 +1,48 @@ +#include +#include "sdControl.h" +#include "pins.h" + +volatile long SDControl::_spiBlockoutTime = 0; +bool SDControl::_weTookBus = false; + +void SDControl::setup() { + // ----- GPIO ------- + // Detect when other master uses SPI bus + pinMode(CS_SENSE, INPUT); + attachInterrupt(CS_SENSE, []() { + if(!_weTookBus) + _spiBlockoutTime = millis() + SPI_BLOCKOUT_PERIOD; + }, FALLING); + + // wait for other master to assert SPI bus first + delay(SPI_BLOCKOUT_PERIOD); +} + +// ------------------------ +void SDControl::takeBusControl() { +// ------------------------ + _weTookBus = true; + //LED_ON; + pinMode(MISO_PIN, SPECIAL); + pinMode(MOSI_PIN, SPECIAL); + pinMode(SCLK_PIN, SPECIAL); + pinMode(SD_CS, OUTPUT); +} + +// ------------------------ +void SDControl::relinquishBusControl() { +// ------------------------ + pinMode(MISO_PIN, INPUT); + pinMode(MOSI_PIN, INPUT); + pinMode(SCLK_PIN, INPUT); + pinMode(SD_CS, INPUT); + //LED_OFF; + _weTookBus = false; +} + +bool SDControl::canWeTakeBus() { + if(millis() < _spiBlockoutTime) { + return false; + } + return true; +} diff --git a/sdControl.h b/sdControl.h new file mode 100644 index 0000000..3e02416 --- /dev/null +++ b/sdControl.h @@ -0,0 +1,21 @@ +#ifndef _SD_CONTROL_H_ +#define _SD_CONTROL_H_ + +#define SPI_BLOCKOUT_PERIOD 20000UL + +class SDControl { +public: + SDControl() { } + static void setup(); + static void takeBusControl(); + static void relinquishBusControl(); + static bool canWeTakeBus(); + +private: + static volatile long _spiBlockoutTime; + static bool _weTookBus; +}; + +extern SDControl sdcontrol; + +#endif From 1e40d7c9aa35abea7978a49a774a31fdafbb2c0a Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 23 Jun 2020 16:57:47 +0800 Subject: [PATCH 07/14] Add example ini file --- ini/SETUP.INI | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 ini/SETUP.INI diff --git a/ini/SETUP.INI b/ini/SETUP.INI new file mode 100644 index 0000000..3a48cd8 --- /dev/null +++ b/ini/SETUP.INI @@ -0,0 +1,2 @@ +SSID=xxxx +PASSWORD=xxxx \ No newline at end of file From e8771e3fd7ff5ed040e234a6135003b9c172734c Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 23 Jun 2020 17:19:29 +0800 Subject: [PATCH 08/14] Update README --- README.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 537e338..487a7de 100644 --- a/README.md +++ b/README.md @@ -17,23 +17,34 @@ GCode can be directly uploaded from the slicer (Cura) to this remote drive, ther ## Dependencies: 1. [ESP8266 Arduino Core version 2.4](https://github.com/esp8266/Arduino) 2. [SdFat library](https://github.com/greiman/SdFat) - + ## Use: -Compile and upload the program to an ESP8266 module. ESP12-E was used for development and testing. -Connect the SPI bus lines to SD card. +### Compile and upload + +Compile and upload the program to an ESP8266 module. + +### Config + +Turn the option button to ```USB2UART``` and connect the module to your computer with USB cable. And use the following command to set or check the network status , you can also see our video [here](https://www.youtube.com/watch?v=YAFAK-jPcOs). + + M50: Set the wifi ssid , 'M50 ssid-name' + M51: Set the wifi password , 'M51 password' + M52: Start to connect the wifi + M53: Check the connection status +### Access + +#### windows + +To access the drive from Windows, type ```\\ip\DavWWWRoot``` at the Run prompt, this will show in serial output as our [video](https://www.youtube.com/watch?v=YAFAK-jPcOs) shows. + +Or use Map Network Drive menu in Windows Explorer. -ESP Module|SD Card ----|--- -GPIO13|MOSI -GPIO12|MISO -GPIO14|SCK -GPIO4|CS -GPIO5|CS Sense +##### MAC -The card should be formatted for Fat16 or Fat32 +Just need to use ```http://192.168.0.x``` in access network drive option -To access the drive from Windows, type ```\\esp_hostname_or_ip\DavWWWRoot``` at the Run prompt, or use Map Network Drive menu in Windows Explorer. +*note: The card should be formatted for Fat16 or Fat32* ## References Marlin Firmware - [http://marlinfw.org/](http://marlinfw.org/) From 1ac7da52600b9c6b2089bbe9e8c54ebf9848a197 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 23 Jun 2020 18:00:13 +0800 Subject: [PATCH 09/14] Update README --- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 487a7de..a3029db 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,61 @@ GCode can be directly uploaded from the slicer (Cura) to this remote drive, ther ## Use: ### Compile and upload -Compile and upload the program to an ESP8266 module. +#### Compile + +If you don't want to update the firmware. You don't need to do this. Compile and upload the program to an ESP8266 module. + +- Open the project + + Download this project and open it with [arduino](https://www.arduino.cc/) software. + +- Add board manager link + + Add boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json` to File->Preferences board manager, Documentation: https://arduino-esp8266.readthedocs.io/en/2.7.1/ + +- Select board + + Select Tools->boards->Generic ESP8285 Module. + +- Click the Arduino compile button + +#### Upload + +1. Pulg in the USB cable to your computer. +2. Press and hold the module FLSH +3. Connect the USB cable to the module +4. Release the module FLSH button +5. Click the Arduino upload button ### Config -Turn the option button to ```USB2UART``` and connect the module to your computer with USB cable. And use the following command to set or check the network status , you can also see our video [here](https://www.youtube.com/watch?v=YAFAK-jPcOs). +First you can see our video [here](https://www.youtube.com/watch?v=YAFAK-jPcOs). You have two ways to config the module. + +*note: The card should be formatted for Fat16 or Fat32* + +#### Option 1: INI file + +You can edit the example ```SETUP.INI``` file in ```ini``` folder, change the SSID and PASSWORD value. And then copy ```SETUP.INI``` file to your root SD card. Then insert it to the module. + +1. Turn the module option button to ```USB2UART``` +2. Open a COM software in your computer +3. Connect the module to your computer with USB cable +4. Open the software COM port + +you can see the module IP and other information. + +*note: if you miss the serial output, you can click the ```RST``` button in the module.* + +#### Option 2 : Command + +Insert your sdcard to the module. + +1. Turn the module option button to ```USB2UART``` +2. Open a COM software in your computer +3. Connect the module to your computer with USB cable +4. Open the software COM port + +And use the following command to connect the network or check the network status M50: Set the wifi ssid , 'M50 ssid-name' M51: Set the wifi password , 'M51 password' @@ -40,12 +90,10 @@ To access the drive from Windows, type ```\\ip\DavWWWRoot``` at the Run prompt, Or use Map Network Drive menu in Windows Explorer. -##### MAC +#### MAC Just need to use ```http://192.168.0.x``` in access network drive option -*note: The card should be formatted for Fat16 or Fat32* - ## References Marlin Firmware - [http://marlinfw.org/](http://marlinfw.org/) From e042b8f6c33d29faa78a7df2720aadd504bec2bc Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 8 Sep 2020 15:08:44 +0800 Subject: [PATCH 10/14] Generate ip.gcode after wifi connected --- config.cpp | 39 +++++++++++++++++++++++++++++++++++++++ config.h | 3 ++- network.cpp | 10 ++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/config.cpp b/config.cpp index cc2df90..19512b3 100644 --- a/config.cpp +++ b/config.cpp @@ -99,6 +99,10 @@ unsigned char Config::load() { } EEPROM.commit(); + if(data.flag) { + SERIAL_ECHOLN("Going to use the old config to connect the network"); + } + SERIAL_ECHOLN("We didn't connect the network before"); return data.flag; } @@ -150,4 +154,39 @@ void Config::save() { EEPROM.commit(); } +// Save to ip address to sdcard +int Config::save_ip(const char *ip) { + SdFat sdfat; + + SERIAL_ECHOLN("Going to save config to ip.gcode file"); + + if(!sdcontrol.canWeTakeBus()) { + SERIAL_ECHOLN("Marlin is controling the bus"); + return -1; + } + sdcontrol.takeBusControl(); + + if(!sdfat.begin(SD_CS, SPI_FULL_SPEED)) { + SERIAL_ECHOLN("Initial SD failed"); + sdcontrol.relinquishBusControl(); + return -2; + } + + // Remove the old file + sdfat.remove("ip.gcode"); + + File file = sdfat.open("ip.gcode", FILE_WRITE); + if (!file) { + SERIAL_ECHOLN("Open ip file failed"); + sdcontrol.relinquishBusControl(); + return -3; + } + + // Get SSID and PASSWORD from file + char buf[21] = "M117 "; + strncat(buf,ip,15); + file.write(buf, 21); + file.close(); +} + Config config; diff --git a/config.h b/config.h index ab6f5a8..0101ad7 100644 --- a/config.h +++ b/config.h @@ -26,7 +26,8 @@ class Config { void password(char* password); void save(const char*ssid,const char*password); void save(); - + int save_ip(const char *ip); + protected: CONFIG_TYPE data; }; diff --git a/network.cpp b/network.cpp index 616b6a3..f531884 100644 --- a/network.cpp +++ b/network.cpp @@ -6,6 +6,14 @@ #include "ESPWebDAV.h" #include "sdControl.h" +String IpAddress2String(const IPAddress& ipAddress) +{ + return String(ipAddress[0]) + String(".") +\ + String(ipAddress[1]) + String(".") +\ + String(ipAddress[2]) + String(".") +\ + String(ipAddress[3]) ; +} + bool Network::start() { wifiConnected = false; @@ -41,6 +49,8 @@ bool Network::start() { wifiConnected = true; config.save(); + String sIp = IpAddress2String(WiFi.localIP()); + config.save_ip(sIp.c_str()); SERIAL_ECHOLN("Going to start DAV server"); if(startDAVServer() < 0) return false; From 44302e8b7e382b8e2d127be058166d4c378c831c Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 8 Sep 2020 17:02:27 +0800 Subject: [PATCH 11/14] If connect fail, LEO always on --- ESPWebDAV.ino | 7 +++++-- network.cpp | 7 +++++++ network.h | 4 +++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ESPWebDAV.ino b/ESPWebDAV.ino index bfbe76d..885de86 100644 --- a/ESPWebDAV.ino +++ b/ESPWebDAV.ino @@ -76,13 +76,16 @@ void errorBlink() { void statusBlink() { static unsigned long time = 0; if(millis() > time + 1000 ) { - if(network.isConnected()) { + if(network.isConnecting()) { + LED_OFF; + } + else if(network.isConnected()) { LED_ON; delay(50); LED_OFF; } else { - LED_OFF; + LED_ON; } time = millis(); } diff --git a/network.cpp b/network.cpp index f531884..6db1083 100644 --- a/network.cpp +++ b/network.cpp @@ -16,6 +16,7 @@ String IpAddress2String(const IPAddress& ipAddress) bool Network::start() { wifiConnected = false; + wifiConnecting = true; // Set hostname first WiFi.hostname(HOSTNAME); @@ -33,6 +34,7 @@ bool Network::start() { timeout++; if(timeout++ > WIFI_CONNECT_TIMEOUT/100) { SERIAL_ECHOLN(""); + wifiConnecting = false; return false; } else @@ -54,6 +56,7 @@ bool Network::start() { SERIAL_ECHOLN("Going to start DAV server"); if(startDAVServer() < 0) return false; + wifiConnecting = false; return true; } @@ -84,6 +87,10 @@ bool Network::isConnected() { return wifiConnected; } +bool Network::isConnecting() { + return wifiConnecting; +} + // a client is waiting and FS is ready and other SPI master is not using the bus bool Network::ready() { if(!isConnected()) return false; diff --git a/network.h b/network.h index b583886..1757897 100644 --- a/network.h +++ b/network.h @@ -8,15 +8,17 @@ class Network { public: - Network() { initFailed = false;} + Network() { initFailed = false;wifiConnecting = true;} bool start(); int startDAVServer(); bool isConnected(); + bool isConnecting(); void handle(); bool ready(); private: bool wifiConnected; + bool wifiConnecting; bool initFailed; }; From 4ad597e13c5791a9dd8fdb2b036e14d7fcad88ce Mon Sep 17 00:00:00 2001 From: George Fu Date: Mon, 28 Dec 2020 17:40:23 +0800 Subject: [PATCH 12/14] Support SSID with blank in config file --- config.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/config.cpp b/config.cpp index 19512b3..24abee8 100644 --- a/config.cpp +++ b/config.cpp @@ -37,7 +37,6 @@ int Config::loadSD() { while (file.available()) { // check for EOF buffer = file.readStringUntil('\n'); if(buffer.length() == 0) continue; // Empty line - buffer.replace(" ", ""); // Delete all blank buffer.replace("\r", ""); // Delete all CR int iS = buffer.indexOf('='); // Get the seperator if(iS < 0) continue; // Bad line From 6f08c0fcb8e4d36f26a29af7cb55b88cdf6cf86a Mon Sep 17 00:00:00 2001 From: George Fu Date: Fri, 29 Jan 2021 09:22:08 +0800 Subject: [PATCH 13/14] Fix SSID or password blank issue --- gcode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcode.cpp b/gcode.cpp index 18bd4e4..77faffb 100644 --- a/gcode.cpp +++ b/gcode.cpp @@ -100,7 +100,7 @@ void Gcode::get_serial_commands() { * M50: Set the Wifi ssid */ void Gcode::gcode_M50() { - for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; + for (char *fn = parser.string_arg; *fn; ++fn); config.ssid(parser.string_arg); SERIAL_ECHO("ssid:"); SERIAL_ECHOLN(config.ssid()); From ccf220909dc5a060d635c921144789d8b226bb56 Mon Sep 17 00:00:00 2001 From: George Fu Date: Thu, 15 Sep 2022 16:29:19 +0800 Subject: [PATCH 14/14] Update README --- README.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a3029db..b9d2d0d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # WebDAV Server and a 3D Printer + This project is a WiFi WebDAV server using ESP8266 SoC. It maintains the filesystem on an SD card. Supports the basic WebDav operations - *PROPFIND*, *GET*, *PUT*, *DELETE*, *MKCOL*, *MOVE* etc. @@ -11,15 +12,15 @@ I am using this setup as a networked drive for 3D Printer running Marlin. Follow GCode can be directly uploaded from the slicer (Cura) to this remote drive, thereby simplifying the workflow. - ![Printer Hookup Diagram](PrinterHookup2.jpg) ## Dependencies: + 1. [ESP8266 Arduino Core version 2.4](https://github.com/esp8266/Arduino) 2. [SdFat library](https://github.com/greiman/SdFat) - ## Use: + ### Compile and upload #### Compile @@ -27,26 +28,27 @@ GCode can be directly uploaded from the slicer (Cura) to this remote drive, ther If you don't want to update the firmware. You don't need to do this. Compile and upload the program to an ESP8266 module. - Open the project - + Download this project and open it with [arduino](https://www.arduino.cc/) software. - Add board manager link - + Add boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json` to File->Preferences board manager, Documentation: https://arduino-esp8266.readthedocs.io/en/2.7.1/ - Select board - + Select Tools->boards->Generic ESP8285 Module. - Click the Arduino compile button #### Upload -1. Pulg in the USB cable to your computer. -2. Press and hold the module FLSH -3. Connect the USB cable to the module -4. Release the module FLSH button -5. Click the Arduino upload button +1. Pulg in the USB cable to your computer +2. Diag the switch on the module to `USB2UART` +3. Press and hold the module FLSH +4. Connect the USB cable to the module +5. Release the module FLSH button +6. Click the Arduino upload button ### Config @@ -82,6 +84,7 @@ And use the following command to connect the network or check the network status M51: Set the wifi password , 'M51 password' M52: Start to connect the wifi M53: Check the connection status + ### Access #### windows @@ -95,6 +98,7 @@ Or use Map Network Drive menu in Windows Explorer. Just need to use ```http://192.168.0.x``` in access network drive option ## References + Marlin Firmware - [http://marlinfw.org/](http://marlinfw.org/) Cura Slicer - [https://ultimaker.com/en/products/ultimaker-cura-software](https://ultimaker.com/en/products/ultimaker-cura-software) @@ -102,6 +106,3 @@ Cura Slicer - [https://ultimaker.com/en/products/ultimaker-cura-software](https: 3D Printer LCD and SD Card Interface - [http://reprap.org/wiki/RepRapDiscount_Full_Graphic_Smart_Controller](http://reprap.org/wiki/RepRapDiscount_Full_Graphic_Smart_Controller) LCD Schematics - [http://reprap.org/mediawiki/images/7/79/LCD_connect_SCHDOC.pdf](http://reprap.org/mediawiki/images/7/79/LCD_connect_SCHDOC.pdf) - - -