From 6f6c1842ce804d5a95aaa5348cdfe536201b754f Mon Sep 17 00:00:00 2001 From: Luc <8822552+luc-github@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:34:19 +0800 Subject: [PATCH] Add TFT Simulator tool to test ESP board with ESP3D without printer o cnc --- .gitignore | 1 + tools/fw_simulator/esp3d_common.py | 31 +++ tools/fw_simulator/fw_simulator.py | 64 +++++ tools/fw_simulator/grbl.py | 231 ++++++++++++++++++ tools/fw_simulator/marlin.py | 360 +++++++++++++++++++++++++++++ tools/fw_simulator/repetier.py | 360 +++++++++++++++++++++++++++++ tools/fw_simulator/smoothieware.py | 360 +++++++++++++++++++++++++++++ 7 files changed, 1407 insertions(+) create mode 100644 tools/fw_simulator/esp3d_common.py create mode 100644 tools/fw_simulator/fw_simulator.py create mode 100644 tools/fw_simulator/grbl.py create mode 100644 tools/fw_simulator/marlin.py create mode 100644 tools/fw_simulator/repetier.py create mode 100644 tools/fw_simulator/smoothieware.py diff --git a/.gitignore b/.gitignore index 635fc82f..07e8293d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ embedded/node_modules .vscode/launch.json .vscode/arduino.json esp3d/myconfig.h +__pycache__ \ No newline at end of file diff --git a/tools/fw_simulator/esp3d_common.py b/tools/fw_simulator/esp3d_common.py new file mode 100644 index 00000000..55034d5a --- /dev/null +++ b/tools/fw_simulator/esp3d_common.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +#Common ESP3D snippets +import time + +class bcolors: + COL_PURPLE = '\033[95m' + COL_BLUE = '\033[94m' + COL_CYAN = '\033[96m' + COL_GREEN = '\033[92m' + COL_ORANGE = '\033[93m' + COL_RED = '\033[91m' + END_COL = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + +def current_milli_time(): + return round(time.time() * 1000) + +def wait(durationms, ser): + nowtime = current_milli_time() + while (current_milli_time() < nowtime + durationms): + if ser.in_waiting: + line = ser.readline().decode('utf-8').strip() + print(bcolors.COL_BLUE+line+bcolors.END_COL) + + +def send_echo(ser, msg): + ser.write((msg + "\n").encode('utf-8')) + ser.flush() + print(bcolors.END_COL + msg + bcolors.END_COL) \ No newline at end of file diff --git a/tools/fw_simulator/fw_simulator.py b/tools/fw_simulator/fw_simulator.py new file mode 100644 index 00000000..efe4a30c --- /dev/null +++ b/tools/fw_simulator/fw_simulator.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +import sys +import serial +import serial.tools.list_ports +import esp3d_common as common +import marlin +import grbl +import repetier +import smoothieware + +def main(): + if len(sys.argv) < 2: + print("Please use one of the follwowing FW: marlin, repetier, smoothieware or grbl.") + return + + fw_name = sys.argv[1].lower() + + if fw_name == "marlin": + fw = marlin + elif fw_name == "repetier": + fw = repetier + elif fw_name == "smoothieware": + fw = smoothieware + elif fw_name == "grbl": + fw = grbl + else: + print("Firmware not supported : {}".format(fw_name)) + return + ports = serial.tools.list_ports.comports() + portTFT = "" + print(common.bcolors.COL_GREEN+"Serial ports detected: "+common.bcolors.END_COL) + for port, desc, hwid in sorted(ports): + print(common.bcolors.COL_GREEN+" - {}: {} ".format(port, desc)+common.bcolors.END_COL) + desc.capitalize() + if (desc.find("SERIAL") != -1): + portTFT = port + print(common.bcolors.COL_GREEN + + "Found " + portTFT + " for ESP3D"+common.bcolors.END_COL) + break + print(common.bcolors.COL_GREEN+"Open port " + str(port)+common.bcolors.END_COL) + if (portTFT == ""): + print(common.bcolors.COL_RED+"No serial port found"+common.bcolors.END_COL) + exit(0) + ser = serial.Serial(portTFT, 115200) + print(common.bcolors.COL_GREEN+"Now Simulating: " + fw_name + common.bcolors.END_COL) + starttime = common.current_milli_time() + # loop forever, just unplug the port to stop the program or do ctrl-c + while True: + try: + if ser.in_waiting: + line = ser.readline().decode('utf-8').strip() + print(common.bcolors.COL_BLUE+line+common.bcolors.END_COL) + #ignore log lines from TFT + if not line.startswith("["): + response = fw.processLine(line, ser) + if (response != ""): + common.send_echo(ser, response) + except KeyboardInterrupt: + print(common.bcolors.COL_GREEN+"End of program"+common.bcolors.END_COL) + exit(0) + + +# call main function +main() diff --git a/tools/fw_simulator/grbl.py b/tools/fw_simulator/grbl.py new file mode 100644 index 00000000..211eddff --- /dev/null +++ b/tools/fw_simulator/grbl.py @@ -0,0 +1,231 @@ +#!/usr/bin/python +# Marlin GCODE parser / responder + +import time +import re +import random +import esp3d_common as common +positions = { + "X": 0.0, + "Y": 0.0, + "Z": 0.0, + "A": 0.0, + "B": 0.0, + "C": 0.0 +} + +modes = { + "absolute": True +} + +report_counter = 0 + + +def wait(durationms, ser): + nowtime = common.current_milli_time() + while (common.current_milli_time() < nowtime + durationms): + if ser.in_waiting: + line = ser.readline().decode('utf-8').strip() + print(common.bcolors.COL_PURPLE+line+common.bcolors.END_COL) + + +def ok(line): + if (not line.startswith("N")): + return "ok (" + line + ")" + N = re.findall(r'N\d*', line) + if (len(N) > 0): + return "ok " + N[0][1:] + + +# build the response for the busy response, +# simulating the delay of the busy response +def send_busy(ser, nb): + v = nb + while (v > 0): + # FIXME: the message is not this one on grbl + # common.send_echo(ser, "echo:busy: processing") + wait(1000, ser) + v = v - 1 + +# G0/G1 response + + +def G0_G1_response(cmd, line, ser): + global positions + X_val = "" + Y_val = "" + Z_val = "" + A_val = "" + B_val = "" + C_val = "" + # extract X + X = re.findall(r'X[+]*[-]*\d+[\.]*\d*', cmd) + if (len(X) > 0): + X_val = X[0][1:] + # extract Y + Y = re.findall(r'Y[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Y) > 0): + Y_val = Y[0][1:] + # extract Z + Z = re.findall(r'Z[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Z) > 0): + Z_val = Z[0][1:] + # extract A + A = re.findall(r'A[+]*[-]*\d+[\.]*\d*', cmd) + if (len(A) > 0): + A_val = A[0][1:] + # extract B + B = re.findall(r'B[+]*[-]*\d+[\.]*\d*', cmd) + if (len(B) > 0): + B_val = B[0][1:] + # extract C + C = re.findall(r'C[+]*[-]*\d+[\.]*\d*', cmd) + if (len(C) > 0): + C_val = C[0][1:] + if (modes["absolute"]): + if (X_val != ""): + positions["X"] = float(X_val) + if (Y_val != ""): + positions["Y"] = float(Y_val) + if (Z_val != ""): + positions["Z"] = float(Z_val) + if (A_val!= ""): + positions["A"] = float(A_val) + if (B_val!= ""): + positions["B"] = float(B_val) + if (C_val!= ""): + positions["C"] = float(C_val) + return ok(line) + else: + if (X_val != ""): + positions["X"] += float(X_val) + if (Y_val != ""): + positions["Y"] += float(Y_val) + if (Z_val != ""): + positions["Z"] += float(Z_val) + if (A_val!= ""): + positions["A"] += float(A_val) + if (B_val!= ""): + positions["B"] += float(B_val) + if (C_val!= ""): + positions["C"] += float(C_val) + return ok(line) + +# $H response + +def Home_response(cmd, line, ser): + global positions + send_busy(ser, 3) + if (cmd.find("X") != -1): + positions["X"] = 0.00 + if (cmd.find("Y") != -1): + positions["Y"] = 0.00 + if (cmd.find("Z") != -1): + positions["Z"] = 0.00 + if (cmd.find("A")!= -1): + positions["A"] = 0.00 + if (cmd.find("B")!= -1): + positions["B"] = 0.00 + if (cmd.find("C")!= -1): + positions["C"] = 0.00 + if (cmd == "G28"): + positions["X"] = 0.00 + positions["Y"] = 0.00 + positions["Z"] = 0.00 + positions["A"] = 0.00 + positiones["B"] = 0.00 + positions["C"] = 0.00 + return ok(line) + + +# Absolute mode + + +def G90_response(cmd, line, ser): + global modes + modes["absolute"] = True + return ok(line) + +# Relative mode + + +def G91_response(cmd, line, ser): + global modes + modes["absolute"] = False + return ok(line) + +def Jog_response(cmd, line, ser): + if (cmd.find("G91")!= -1): + G91_response(cmd, line, ser) + elif (cmd.find("G90")!= -1): + G90_response(cmd, line, ser) + cmd = cmd.replace("G90", "") + cmd = cmd.replace("G91", "") + cmd = cmd.replace("G21", "") + cmd = cmd.strip() + return G0_G1_response(cmd, line, ser) + +# status response +# "\n" +# "\n" +# "\n" +def status_response(cmd, line, ser): + global positions + global report_counter + wpco = "" + ov = "" + fs = "|FS:0,0" + astate = "" + pn = "" + status = "Idle" + report_counter += 1 + if report_counter == 11: + report_counter = 1 + if report_counter == 1: + wpco = "|WCO:0.000,0.000,0.000,1.000,1.000,1.000" + if report_counter == 2: + #FIXME: use variable to report the override values + ov = "|Ov:100,100,100" + pn = "|Pn:XYZ" + if report_counter >= 3: + astate = "|A:S" + pn = "|Pn:P" + + position = "|MPos:" + "{:.3f}".format(positions["X"]) + "," + "{:.3f}".format( + positions["Y"]) + "," + "{:.3f}".format(positions["Z"]) + "," + "{:.3f}".format(positions["A"]) + "," + "{:.3f}".format(positions["B"]) + "," + "{:.3f}".format(positions["C"]) + return "<" + status + position + fs + wpco + ov + astate + pn + ">\n" + + +# List of supported methods +methods = [ + {"str": "G0", "fn": G0_G1_response}, + {"str": "G1", "fn": G0_G1_response}, + {"str": "$H", "fn": Home_response}, + {"str": "$J=", "fn": Jog_response}, + {"str": "G90", "fn": G90_response}, + {"str": "G91", "fn": G91_response}, + {"str": "?", "fn": status_response}, +] + + +# Process a line of GCODE +def processLine(line, ser): + time.sleep(0.01) + cmd = line + if (line.startswith("N")): + p = line.find(' ') + cmd = line[p+1:] + p = cmd.rfind('*') + cmd = cmd[:p] + global methods + for method in methods: + if cmd.startswith(method["str"]): + return method["fn"](cmd, line, ser) + if line.startswith("M") or line.startswith("G") or line.startswith("N") or line.startswith("$"): + return ok(line) + if line.find("[esp") != -1 or line.find("[0;") != -1 or line.find("[1;") != -1: + return "" + if line.startswith("ESP-ROM") or line.startswith("Build:") or line.startswith("SPIWP:") or line.startswith("mode:")or line.startswith("load:") or line.startswith("entry "): + return "" + #FIXME: this is not grbl response if the command is not recognized + return "echo:Unknown command: \"" + line + "\"\nok" diff --git a/tools/fw_simulator/marlin.py b/tools/fw_simulator/marlin.py new file mode 100644 index 00000000..984686e4 --- /dev/null +++ b/tools/fw_simulator/marlin.py @@ -0,0 +1,360 @@ +#!/usr/bin/python +# Marlin GCODE parser / responder + +import time +import re +import random +import esp3d_common as common +positions = { + "X": 0.0, + "Y": 0.0, + "Z": 0.0 +} +temperatures = { + "E0": { + "value": 0.0, + "target": 0.0, + "lastTime": -1, + "heatspeed": 0.6, + "coolspeed": 0.8, + "variation": 0.5 + }, + "B": { + "value": 0.0, + "target": 0.0, + "lastTime": -1, + "heatspeed": 0.2, + "coolspeed": 0.8, + "variation": 0.5 + } +} +modes = { + "absolute": True +} + +stop_heating = False + + +def wait(durationms, ser): + global stop_heating + nowtime = common.current_milli_time() + while (common.current_milli_time() < nowtime + durationms): + if ser.in_waiting: + line = ser.readline().decode('utf-8').strip() + if line=="M108": + stop_heating = True + print(common.bcolors.COL_PURPLE+line+common.bcolors.END_COL) + + +def ok(line): + if (not line.startswith("N")): + return "ok (" + line + ")" + N = re.findall(r'N\d*', line) + if (len(N) > 0): + return "ok " + N[0][1:] + +# Update the temperatures according context +def updateTemperatures(entry, timestp): + global temperatures + roomtemp = 20.0 + v = random.random()*5 + target = temperatures[entry]["target"] + if target == 0: + target = roomtemp + if (temperatures[entry]["value"] == 0): + temperatures[entry]["value"] = roomtemp + v / 2 + if (temperatures[entry]["lastTime"] == -1): + temperatures[entry]["lastTime"] = timestp + if temperatures[entry]["value"] + 5 < target: + temperatures[entry]["value"] = temperatures[entry]["value"] + \ + (temperatures[entry]["heatspeed"] * + (timestp - temperatures[entry]["lastTime"])) / 1000 + elif temperatures[entry]["value"] - 5 > target: + temperatures[entry]["value"] = temperatures[entry]["value"] - \ + (temperatures[entry]["coolspeed"] * + (timestp - temperatures[entry]["lastTime"])) / 1000 + elif target - 2 < temperatures[entry]["value"] and temperatures[entry]["value"] < target + 2: + temperatures[entry]["value"] = target + \ + temperatures[entry]["variation"] * (random.random() - 0.5) + elif temperatures[entry]["value"] < target: + temperatures[entry]["value"] = temperatures[entry]["value"] + \ + ((temperatures[entry]["heatspeed"]/3) * + (timestp - temperatures[entry]["lastTime"])) / 1000 + else: + temperatures[entry]["value"] = temperatures[entry]["value"] - \ + ((temperatures[entry]["coolspeed"]/3) * + (timestp - temperatures[entry]["lastTime"])) / 1000 + + temperatures[entry]["lastTime"] = timestp + +# build the response for the temperature +def generateTemperatureResponse(withok): + global temperatures + response = " " + if (withok): + response = "ok " + response += "T:" + "{:.2f}".format(temperatures["E0"]["value"]) + " /" + "{:.2f}".format(temperatures["E0"]["target"]) + " B:" + "{:.2f}".format( + temperatures["B"]["value"]) + " /" + "{:.2f}".format(temperatures["B"]["target"]) + " @:127 B@:0" + return response + +# build the response for the busy response, +# simulating the delay of the busy response +def send_busy(ser, nb): + v = nb + while (v > 0): + common.send_echo(ser, "echo:busy: processing") + wait(1000, ser) + v = v - 1 + +# G0/G1 response +def G0_G1_response(cmd,line,ser): + global positions + X_val = "" + Y_val = "" + Z_val = "" + # extract X + X = re.findall(r'X[+]*[-]*\d+[\.]*\d*', cmd) + if (len(X) > 0): + X_val = X[0][1:] + # extract Y + Y = re.findall(r'Y[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Y) > 0): + Y_val = Y[0][1:] + # extract Z + Z = re.findall(r'Z[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Z) > 0): + Z_val = Z[0][1:] + if (modes["absolute"]): + if (X_val != ""): + positions["X"] = float(X_val) + if (Y_val != ""): + positions["Y"] = float(Y_val) + if (Z_val != ""): + positions["Z"] = float(Z_val) + return ok(line) + else: + if (X_val != ""): + positions["X"] += float(X_val) + if (Y_val != ""): + positions["Y"] += float(Y_val) + if (Z_val != ""): + positions["Z"] += float(Z_val) + return ok(line) + +# G28 response +def G28_response(cmd,line,ser): + global positions + send_busy(ser, 3) + if (cmd.find("X") != -1): + positions["X"] = 0.00 + if (cmd.find("Y") != -1): + positions["Y"] = 0.00 + if (cmd.find("Z") != -1): + positions["Z"] = 0.00 + if (cmd == "G28"): + positions["X"] = 0.00 + positions["Y"] = 0.00 + positions["Z"] = 0.00 + return ok(line) + +# G29 V4 response +def G29_V4_response(cmd,line,ser): + common.send_echo(ser, " G29 Auto Bed Leveling") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 50.000 Z: 0.000") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 50.000 Z: 0.016") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 50.000 Z: -0.013") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 50.000 Z: -0.051") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 133.000 Z: -0.005") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 133.000 Z: -0.041") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 133.000 Z: -0.031") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 133.000 Z: -0.036") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 216.000 Z: -0.050") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 216.000 Z: 0.055") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 216.000 Z: 0.051") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 216.000 Z: 0.026") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 299.000 Z: -0.018") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 299.000 Z: -0.064") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 299.000 Z: -0.036") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 299.000 Z: -0.046") + send_busy(ser, 3) + common.send_echo(ser, "Bilinear Leveling Grid:") + common.send_echo(ser, " 0 1 2 3") + common.send_echo(ser, " 0 +0.0000 +0.0162 -0.0125 -0.0512") + common.send_echo(ser, " 1 -0.0363 -0.0313 -0.0412 -0.0050") + common.send_echo(ser, " 2 -0.0500 +0.0550 +0.0512 +0.0262") + common.send_echo(ser, " 3 -0.0463 -0.0363 -0.0638 -0.0175") + return ok(line) + +# Absolute mode +def G90_response(cmd,line,ser): + global modes + modes["absolute"] = True + return ok(line) + +# Relative mode +def G91_response(cmd,line,ser): + global modes + modes["absolute"] = False + return ok(line) + +# M104 extruder control not waiting +def M104_response(cmd,line,ser): + global temperatures + targettemp = re.findall(r'S\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["E0"]["target"] = float(targettemp[0][1:]) + return ok(line) + +# M105 temperatures query +def M105_response(cmd,line,ser): + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(True) + return val + +# M106 fan control +def M106_response(cmd,line,ser): + return line+"\nok" + +# M107 fan stop +def M107_response(cmd,line,ser): + if (len(val) > 0): + return "M106 P" + val[0][1:] + " S0\nok" + else: + return "M106 P0 S0\nok" + +# M109 extruder control waiting +def M109_response(cmd,line,ser): + global temperatures + global stop_heating + targettemp = re.findall(r'[SR]\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + stop_heating = False + temperatures["E0"]["target"] = float(targettemp[0][1:]) + target = 20.0 + if (temperatures["E0"]["target"] != 0): + target = temperatures["E0"]["target"] + while ( temperatures["E0"]["value"] < target-2 or temperatures["E0"]["value"] > target+2): + send_busy(ser, 1) + if stop_heating: + stop_heating = False + temperatures["E0"]["target"] = 0.0 + return ok(line) + "\nok" + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + ser.flush() + wait(1000, ser) + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + return ok(line) + +# M114 Positions query +def M114_response(cmd,line,ser): + global positions + val = "X:" + "{:.2f}".format(positions["X"]) + " Y:" + "{:.2f}".format( + positions["Y"]) + " Z:" + "{:.2f}".format(positions["Z"])+" E:0.00 Count X:0 Y:0 Z:0\nok" + return val + +# M140 bed control not waiting +def M140_response(cmd,line,ser): + global temperatures + targettemp = re.findall(r'S\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["B"]["target"] = float(targettemp[0][1:]) + return ok(line) + +# M190 bed control waiting +def M190_response(cmd,line,ser): + global temperatures + global stop_heating + targettemp = re.findall(r'[SR]\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["B"]["target"] = float(targettemp[0][1:]) + target = 20.0 + if (temperatures["B"]["target"] != 0): + target = temperatures["B"]["target"] + stop_heating = False + while (temperatures["B"]["value"] < target-2 or temperatures["B"]["value"] > target+2): + send_busy(ser, 1) + if stop_heating: + stop_heating = False + temperatures["B"]["target"] = 0.0 + return ok(line) + "\nok" + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + ser.flush() + wait(1000, ser) + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + return ok(line) + +# M220 response +def M220_response(cmd,line,ser): + val = re.findall(r'S\d+', cmd) + if (len(val) > 0): + F_R = val[0][1:] + return "FR:"+F_R+"%\nok" + return ok(line) + +# List of supported methods +methods = [ + {"str": "G0", "fn": G0_G1_response}, + {"str": "G1", "fn": G0_G1_response}, + {"str": "G28", "fn": G28_response}, + {"str": "G29 V4", "fn": G29_V4_response}, + {"str": "G90", "fn": G90_response}, + {"str": "G91", "fn": G91_response}, + {"str": "M104", "fn": M104_response}, + {"str": "M105", "fn": M105_response}, + {"str": "M106", "fn": M106_response}, + {"str": "M107", "fn": M107_response}, + {"str": "M109", "fn": M109_response}, + {"str": "M114", "fn": M114_response}, + {"str": "M140", "fn": M140_response}, + {"str": "M190", "fn": M190_response}, + {"str": "M220", "fn": M220_response}, +] + + +# Process a line of GCODE +def processLine(line,ser): + time.sleep(0.01) + cmd = line + if (line.startswith("N")): + p = line.find(' ') + cmd = line[p+1:] + p = cmd.rfind('*') + cmd = cmd[:p] + global methods + for method in methods: + if cmd.startswith(method["str"]): + return method["fn"](cmd,line,ser) + if line.startswith("M") or line.startswith("G") or line.startswith("N"): + return ok(line) + if line.find("[esp")!=-1 or line.find("[0;")!=-1 or line.find("[1;")!=-1: + return "" + return "echo:Unknown command: \"" + line + "\"\nok" diff --git a/tools/fw_simulator/repetier.py b/tools/fw_simulator/repetier.py new file mode 100644 index 00000000..984686e4 --- /dev/null +++ b/tools/fw_simulator/repetier.py @@ -0,0 +1,360 @@ +#!/usr/bin/python +# Marlin GCODE parser / responder + +import time +import re +import random +import esp3d_common as common +positions = { + "X": 0.0, + "Y": 0.0, + "Z": 0.0 +} +temperatures = { + "E0": { + "value": 0.0, + "target": 0.0, + "lastTime": -1, + "heatspeed": 0.6, + "coolspeed": 0.8, + "variation": 0.5 + }, + "B": { + "value": 0.0, + "target": 0.0, + "lastTime": -1, + "heatspeed": 0.2, + "coolspeed": 0.8, + "variation": 0.5 + } +} +modes = { + "absolute": True +} + +stop_heating = False + + +def wait(durationms, ser): + global stop_heating + nowtime = common.current_milli_time() + while (common.current_milli_time() < nowtime + durationms): + if ser.in_waiting: + line = ser.readline().decode('utf-8').strip() + if line=="M108": + stop_heating = True + print(common.bcolors.COL_PURPLE+line+common.bcolors.END_COL) + + +def ok(line): + if (not line.startswith("N")): + return "ok (" + line + ")" + N = re.findall(r'N\d*', line) + if (len(N) > 0): + return "ok " + N[0][1:] + +# Update the temperatures according context +def updateTemperatures(entry, timestp): + global temperatures + roomtemp = 20.0 + v = random.random()*5 + target = temperatures[entry]["target"] + if target == 0: + target = roomtemp + if (temperatures[entry]["value"] == 0): + temperatures[entry]["value"] = roomtemp + v / 2 + if (temperatures[entry]["lastTime"] == -1): + temperatures[entry]["lastTime"] = timestp + if temperatures[entry]["value"] + 5 < target: + temperatures[entry]["value"] = temperatures[entry]["value"] + \ + (temperatures[entry]["heatspeed"] * + (timestp - temperatures[entry]["lastTime"])) / 1000 + elif temperatures[entry]["value"] - 5 > target: + temperatures[entry]["value"] = temperatures[entry]["value"] - \ + (temperatures[entry]["coolspeed"] * + (timestp - temperatures[entry]["lastTime"])) / 1000 + elif target - 2 < temperatures[entry]["value"] and temperatures[entry]["value"] < target + 2: + temperatures[entry]["value"] = target + \ + temperatures[entry]["variation"] * (random.random() - 0.5) + elif temperatures[entry]["value"] < target: + temperatures[entry]["value"] = temperatures[entry]["value"] + \ + ((temperatures[entry]["heatspeed"]/3) * + (timestp - temperatures[entry]["lastTime"])) / 1000 + else: + temperatures[entry]["value"] = temperatures[entry]["value"] - \ + ((temperatures[entry]["coolspeed"]/3) * + (timestp - temperatures[entry]["lastTime"])) / 1000 + + temperatures[entry]["lastTime"] = timestp + +# build the response for the temperature +def generateTemperatureResponse(withok): + global temperatures + response = " " + if (withok): + response = "ok " + response += "T:" + "{:.2f}".format(temperatures["E0"]["value"]) + " /" + "{:.2f}".format(temperatures["E0"]["target"]) + " B:" + "{:.2f}".format( + temperatures["B"]["value"]) + " /" + "{:.2f}".format(temperatures["B"]["target"]) + " @:127 B@:0" + return response + +# build the response for the busy response, +# simulating the delay of the busy response +def send_busy(ser, nb): + v = nb + while (v > 0): + common.send_echo(ser, "echo:busy: processing") + wait(1000, ser) + v = v - 1 + +# G0/G1 response +def G0_G1_response(cmd,line,ser): + global positions + X_val = "" + Y_val = "" + Z_val = "" + # extract X + X = re.findall(r'X[+]*[-]*\d+[\.]*\d*', cmd) + if (len(X) > 0): + X_val = X[0][1:] + # extract Y + Y = re.findall(r'Y[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Y) > 0): + Y_val = Y[0][1:] + # extract Z + Z = re.findall(r'Z[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Z) > 0): + Z_val = Z[0][1:] + if (modes["absolute"]): + if (X_val != ""): + positions["X"] = float(X_val) + if (Y_val != ""): + positions["Y"] = float(Y_val) + if (Z_val != ""): + positions["Z"] = float(Z_val) + return ok(line) + else: + if (X_val != ""): + positions["X"] += float(X_val) + if (Y_val != ""): + positions["Y"] += float(Y_val) + if (Z_val != ""): + positions["Z"] += float(Z_val) + return ok(line) + +# G28 response +def G28_response(cmd,line,ser): + global positions + send_busy(ser, 3) + if (cmd.find("X") != -1): + positions["X"] = 0.00 + if (cmd.find("Y") != -1): + positions["Y"] = 0.00 + if (cmd.find("Z") != -1): + positions["Z"] = 0.00 + if (cmd == "G28"): + positions["X"] = 0.00 + positions["Y"] = 0.00 + positions["Z"] = 0.00 + return ok(line) + +# G29 V4 response +def G29_V4_response(cmd,line,ser): + common.send_echo(ser, " G29 Auto Bed Leveling") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 50.000 Z: 0.000") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 50.000 Z: 0.016") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 50.000 Z: -0.013") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 50.000 Z: -0.051") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 133.000 Z: -0.005") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 133.000 Z: -0.041") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 133.000 Z: -0.031") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 133.000 Z: -0.036") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 216.000 Z: -0.050") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 216.000 Z: 0.055") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 216.000 Z: 0.051") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 216.000 Z: 0.026") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 299.000 Z: -0.018") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 299.000 Z: -0.064") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 299.000 Z: -0.036") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 299.000 Z: -0.046") + send_busy(ser, 3) + common.send_echo(ser, "Bilinear Leveling Grid:") + common.send_echo(ser, " 0 1 2 3") + common.send_echo(ser, " 0 +0.0000 +0.0162 -0.0125 -0.0512") + common.send_echo(ser, " 1 -0.0363 -0.0313 -0.0412 -0.0050") + common.send_echo(ser, " 2 -0.0500 +0.0550 +0.0512 +0.0262") + common.send_echo(ser, " 3 -0.0463 -0.0363 -0.0638 -0.0175") + return ok(line) + +# Absolute mode +def G90_response(cmd,line,ser): + global modes + modes["absolute"] = True + return ok(line) + +# Relative mode +def G91_response(cmd,line,ser): + global modes + modes["absolute"] = False + return ok(line) + +# M104 extruder control not waiting +def M104_response(cmd,line,ser): + global temperatures + targettemp = re.findall(r'S\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["E0"]["target"] = float(targettemp[0][1:]) + return ok(line) + +# M105 temperatures query +def M105_response(cmd,line,ser): + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(True) + return val + +# M106 fan control +def M106_response(cmd,line,ser): + return line+"\nok" + +# M107 fan stop +def M107_response(cmd,line,ser): + if (len(val) > 0): + return "M106 P" + val[0][1:] + " S0\nok" + else: + return "M106 P0 S0\nok" + +# M109 extruder control waiting +def M109_response(cmd,line,ser): + global temperatures + global stop_heating + targettemp = re.findall(r'[SR]\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + stop_heating = False + temperatures["E0"]["target"] = float(targettemp[0][1:]) + target = 20.0 + if (temperatures["E0"]["target"] != 0): + target = temperatures["E0"]["target"] + while ( temperatures["E0"]["value"] < target-2 or temperatures["E0"]["value"] > target+2): + send_busy(ser, 1) + if stop_heating: + stop_heating = False + temperatures["E0"]["target"] = 0.0 + return ok(line) + "\nok" + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + ser.flush() + wait(1000, ser) + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + return ok(line) + +# M114 Positions query +def M114_response(cmd,line,ser): + global positions + val = "X:" + "{:.2f}".format(positions["X"]) + " Y:" + "{:.2f}".format( + positions["Y"]) + " Z:" + "{:.2f}".format(positions["Z"])+" E:0.00 Count X:0 Y:0 Z:0\nok" + return val + +# M140 bed control not waiting +def M140_response(cmd,line,ser): + global temperatures + targettemp = re.findall(r'S\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["B"]["target"] = float(targettemp[0][1:]) + return ok(line) + +# M190 bed control waiting +def M190_response(cmd,line,ser): + global temperatures + global stop_heating + targettemp = re.findall(r'[SR]\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["B"]["target"] = float(targettemp[0][1:]) + target = 20.0 + if (temperatures["B"]["target"] != 0): + target = temperatures["B"]["target"] + stop_heating = False + while (temperatures["B"]["value"] < target-2 or temperatures["B"]["value"] > target+2): + send_busy(ser, 1) + if stop_heating: + stop_heating = False + temperatures["B"]["target"] = 0.0 + return ok(line) + "\nok" + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + ser.flush() + wait(1000, ser) + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + return ok(line) + +# M220 response +def M220_response(cmd,line,ser): + val = re.findall(r'S\d+', cmd) + if (len(val) > 0): + F_R = val[0][1:] + return "FR:"+F_R+"%\nok" + return ok(line) + +# List of supported methods +methods = [ + {"str": "G0", "fn": G0_G1_response}, + {"str": "G1", "fn": G0_G1_response}, + {"str": "G28", "fn": G28_response}, + {"str": "G29 V4", "fn": G29_V4_response}, + {"str": "G90", "fn": G90_response}, + {"str": "G91", "fn": G91_response}, + {"str": "M104", "fn": M104_response}, + {"str": "M105", "fn": M105_response}, + {"str": "M106", "fn": M106_response}, + {"str": "M107", "fn": M107_response}, + {"str": "M109", "fn": M109_response}, + {"str": "M114", "fn": M114_response}, + {"str": "M140", "fn": M140_response}, + {"str": "M190", "fn": M190_response}, + {"str": "M220", "fn": M220_response}, +] + + +# Process a line of GCODE +def processLine(line,ser): + time.sleep(0.01) + cmd = line + if (line.startswith("N")): + p = line.find(' ') + cmd = line[p+1:] + p = cmd.rfind('*') + cmd = cmd[:p] + global methods + for method in methods: + if cmd.startswith(method["str"]): + return method["fn"](cmd,line,ser) + if line.startswith("M") or line.startswith("G") or line.startswith("N"): + return ok(line) + if line.find("[esp")!=-1 or line.find("[0;")!=-1 or line.find("[1;")!=-1: + return "" + return "echo:Unknown command: \"" + line + "\"\nok" diff --git a/tools/fw_simulator/smoothieware.py b/tools/fw_simulator/smoothieware.py new file mode 100644 index 00000000..984686e4 --- /dev/null +++ b/tools/fw_simulator/smoothieware.py @@ -0,0 +1,360 @@ +#!/usr/bin/python +# Marlin GCODE parser / responder + +import time +import re +import random +import esp3d_common as common +positions = { + "X": 0.0, + "Y": 0.0, + "Z": 0.0 +} +temperatures = { + "E0": { + "value": 0.0, + "target": 0.0, + "lastTime": -1, + "heatspeed": 0.6, + "coolspeed": 0.8, + "variation": 0.5 + }, + "B": { + "value": 0.0, + "target": 0.0, + "lastTime": -1, + "heatspeed": 0.2, + "coolspeed": 0.8, + "variation": 0.5 + } +} +modes = { + "absolute": True +} + +stop_heating = False + + +def wait(durationms, ser): + global stop_heating + nowtime = common.current_milli_time() + while (common.current_milli_time() < nowtime + durationms): + if ser.in_waiting: + line = ser.readline().decode('utf-8').strip() + if line=="M108": + stop_heating = True + print(common.bcolors.COL_PURPLE+line+common.bcolors.END_COL) + + +def ok(line): + if (not line.startswith("N")): + return "ok (" + line + ")" + N = re.findall(r'N\d*', line) + if (len(N) > 0): + return "ok " + N[0][1:] + +# Update the temperatures according context +def updateTemperatures(entry, timestp): + global temperatures + roomtemp = 20.0 + v = random.random()*5 + target = temperatures[entry]["target"] + if target == 0: + target = roomtemp + if (temperatures[entry]["value"] == 0): + temperatures[entry]["value"] = roomtemp + v / 2 + if (temperatures[entry]["lastTime"] == -1): + temperatures[entry]["lastTime"] = timestp + if temperatures[entry]["value"] + 5 < target: + temperatures[entry]["value"] = temperatures[entry]["value"] + \ + (temperatures[entry]["heatspeed"] * + (timestp - temperatures[entry]["lastTime"])) / 1000 + elif temperatures[entry]["value"] - 5 > target: + temperatures[entry]["value"] = temperatures[entry]["value"] - \ + (temperatures[entry]["coolspeed"] * + (timestp - temperatures[entry]["lastTime"])) / 1000 + elif target - 2 < temperatures[entry]["value"] and temperatures[entry]["value"] < target + 2: + temperatures[entry]["value"] = target + \ + temperatures[entry]["variation"] * (random.random() - 0.5) + elif temperatures[entry]["value"] < target: + temperatures[entry]["value"] = temperatures[entry]["value"] + \ + ((temperatures[entry]["heatspeed"]/3) * + (timestp - temperatures[entry]["lastTime"])) / 1000 + else: + temperatures[entry]["value"] = temperatures[entry]["value"] - \ + ((temperatures[entry]["coolspeed"]/3) * + (timestp - temperatures[entry]["lastTime"])) / 1000 + + temperatures[entry]["lastTime"] = timestp + +# build the response for the temperature +def generateTemperatureResponse(withok): + global temperatures + response = " " + if (withok): + response = "ok " + response += "T:" + "{:.2f}".format(temperatures["E0"]["value"]) + " /" + "{:.2f}".format(temperatures["E0"]["target"]) + " B:" + "{:.2f}".format( + temperatures["B"]["value"]) + " /" + "{:.2f}".format(temperatures["B"]["target"]) + " @:127 B@:0" + return response + +# build the response for the busy response, +# simulating the delay of the busy response +def send_busy(ser, nb): + v = nb + while (v > 0): + common.send_echo(ser, "echo:busy: processing") + wait(1000, ser) + v = v - 1 + +# G0/G1 response +def G0_G1_response(cmd,line,ser): + global positions + X_val = "" + Y_val = "" + Z_val = "" + # extract X + X = re.findall(r'X[+]*[-]*\d+[\.]*\d*', cmd) + if (len(X) > 0): + X_val = X[0][1:] + # extract Y + Y = re.findall(r'Y[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Y) > 0): + Y_val = Y[0][1:] + # extract Z + Z = re.findall(r'Z[+]*[-]*\d+[\.]*\d*', cmd) + if (len(Z) > 0): + Z_val = Z[0][1:] + if (modes["absolute"]): + if (X_val != ""): + positions["X"] = float(X_val) + if (Y_val != ""): + positions["Y"] = float(Y_val) + if (Z_val != ""): + positions["Z"] = float(Z_val) + return ok(line) + else: + if (X_val != ""): + positions["X"] += float(X_val) + if (Y_val != ""): + positions["Y"] += float(Y_val) + if (Z_val != ""): + positions["Z"] += float(Z_val) + return ok(line) + +# G28 response +def G28_response(cmd,line,ser): + global positions + send_busy(ser, 3) + if (cmd.find("X") != -1): + positions["X"] = 0.00 + if (cmd.find("Y") != -1): + positions["Y"] = 0.00 + if (cmd.find("Z") != -1): + positions["Z"] = 0.00 + if (cmd == "G28"): + positions["X"] = 0.00 + positions["Y"] = 0.00 + positions["Z"] = 0.00 + return ok(line) + +# G29 V4 response +def G29_V4_response(cmd,line,ser): + common.send_echo(ser, " G29 Auto Bed Leveling") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 50.000 Z: 0.000") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 50.000 Z: 0.016") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 50.000 Z: -0.013") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 50.000 Z: -0.051") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 133.000 Z: -0.005") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 133.000 Z: -0.041") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 133.000 Z: -0.031") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 133.000 Z: -0.036") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 216.000 Z: -0.050") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 216.000 Z: 0.055") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 216.000 Z: 0.051") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 216.000 Z: 0.026") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 299.000 Y: 299.000 Z: -0.018") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 216.000 Y: 299.000 Z: -0.064") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 133.000 Y: 299.000 Z: -0.036") + send_busy(ser, 3) + common.send_echo(ser, "Bed X: 50.000 Y: 299.000 Z: -0.046") + send_busy(ser, 3) + common.send_echo(ser, "Bilinear Leveling Grid:") + common.send_echo(ser, " 0 1 2 3") + common.send_echo(ser, " 0 +0.0000 +0.0162 -0.0125 -0.0512") + common.send_echo(ser, " 1 -0.0363 -0.0313 -0.0412 -0.0050") + common.send_echo(ser, " 2 -0.0500 +0.0550 +0.0512 +0.0262") + common.send_echo(ser, " 3 -0.0463 -0.0363 -0.0638 -0.0175") + return ok(line) + +# Absolute mode +def G90_response(cmd,line,ser): + global modes + modes["absolute"] = True + return ok(line) + +# Relative mode +def G91_response(cmd,line,ser): + global modes + modes["absolute"] = False + return ok(line) + +# M104 extruder control not waiting +def M104_response(cmd,line,ser): + global temperatures + targettemp = re.findall(r'S\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["E0"]["target"] = float(targettemp[0][1:]) + return ok(line) + +# M105 temperatures query +def M105_response(cmd,line,ser): + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(True) + return val + +# M106 fan control +def M106_response(cmd,line,ser): + return line+"\nok" + +# M107 fan stop +def M107_response(cmd,line,ser): + if (len(val) > 0): + return "M106 P" + val[0][1:] + " S0\nok" + else: + return "M106 P0 S0\nok" + +# M109 extruder control waiting +def M109_response(cmd,line,ser): + global temperatures + global stop_heating + targettemp = re.findall(r'[SR]\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + stop_heating = False + temperatures["E0"]["target"] = float(targettemp[0][1:]) + target = 20.0 + if (temperatures["E0"]["target"] != 0): + target = temperatures["E0"]["target"] + while ( temperatures["E0"]["value"] < target-2 or temperatures["E0"]["value"] > target+2): + send_busy(ser, 1) + if stop_heating: + stop_heating = False + temperatures["E0"]["target"] = 0.0 + return ok(line) + "\nok" + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + ser.flush() + wait(1000, ser) + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + return ok(line) + +# M114 Positions query +def M114_response(cmd,line,ser): + global positions + val = "X:" + "{:.2f}".format(positions["X"]) + " Y:" + "{:.2f}".format( + positions["Y"]) + " Z:" + "{:.2f}".format(positions["Z"])+" E:0.00 Count X:0 Y:0 Z:0\nok" + return val + +# M140 bed control not waiting +def M140_response(cmd,line,ser): + global temperatures + targettemp = re.findall(r'S\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["B"]["target"] = float(targettemp[0][1:]) + return ok(line) + +# M190 bed control waiting +def M190_response(cmd,line,ser): + global temperatures + global stop_heating + targettemp = re.findall(r'[SR]\d+[\.]*\d*', cmd) + if (len(targettemp) > 0): + temperatures["B"]["target"] = float(targettemp[0][1:]) + target = 20.0 + if (temperatures["B"]["target"] != 0): + target = temperatures["B"]["target"] + stop_heating = False + while (temperatures["B"]["value"] < target-2 or temperatures["B"]["value"] > target+2): + send_busy(ser, 1) + if stop_heating: + stop_heating = False + temperatures["B"]["target"] = 0.0 + return ok(line) + "\nok" + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + ser.flush() + wait(1000, ser) + updateTemperatures("E0", common.current_milli_time()) + updateTemperatures("B", common.current_milli_time()) + val = generateTemperatureResponse(False) + common.send_echo(ser, val) + return ok(line) + +# M220 response +def M220_response(cmd,line,ser): + val = re.findall(r'S\d+', cmd) + if (len(val) > 0): + F_R = val[0][1:] + return "FR:"+F_R+"%\nok" + return ok(line) + +# List of supported methods +methods = [ + {"str": "G0", "fn": G0_G1_response}, + {"str": "G1", "fn": G0_G1_response}, + {"str": "G28", "fn": G28_response}, + {"str": "G29 V4", "fn": G29_V4_response}, + {"str": "G90", "fn": G90_response}, + {"str": "G91", "fn": G91_response}, + {"str": "M104", "fn": M104_response}, + {"str": "M105", "fn": M105_response}, + {"str": "M106", "fn": M106_response}, + {"str": "M107", "fn": M107_response}, + {"str": "M109", "fn": M109_response}, + {"str": "M114", "fn": M114_response}, + {"str": "M140", "fn": M140_response}, + {"str": "M190", "fn": M190_response}, + {"str": "M220", "fn": M220_response}, +] + + +# Process a line of GCODE +def processLine(line,ser): + time.sleep(0.01) + cmd = line + if (line.startswith("N")): + p = line.find(' ') + cmd = line[p+1:] + p = cmd.rfind('*') + cmd = cmd[:p] + global methods + for method in methods: + if cmd.startswith(method["str"]): + return method["fn"](cmd,line,ser) + if line.startswith("M") or line.startswith("G") or line.startswith("N"): + return ok(line) + if line.find("[esp")!=-1 or line.find("[0;")!=-1 or line.find("[1;")!=-1: + return "" + return "echo:Unknown command: \"" + line + "\"\nok"