From c1b2a911151afbe31333cc56c757e4341d2585b7 Mon Sep 17 00:00:00 2001 From: Rob Giseburt Date: Wed, 18 Jan 2017 15:48:31 -0600 Subject: [PATCH] More work on Marlin-compatibility (M104,M140,M109,M190 implemented) --- g2core/canonical_machine.h | 8 +- g2core/config.cpp | 2 +- g2core/controller.cpp | 4 + g2core/gcode_parser.cpp | 56 +++++++++--- g2core/marlin_compatibility.cpp | 151 ++++++++++++++++++++++++++++---- g2core/marlin_compatibility.h | 3 + 6 files changed, 190 insertions(+), 34 deletions(-) diff --git a/g2core/canonical_machine.h b/g2core/canonical_machine.h index 3d15e730..b89ef5b0 100644 --- a/g2core/canonical_machine.h +++ b/g2core/canonical_machine.h @@ -176,7 +176,13 @@ typedef enum { // these are in order to optimized CASE NEXT_ACTION_SUSPEND_ORIGIN_OFFSETS, // G92.2 NEXT_ACTION_RESUME_ORIGIN_OFFSETS, // G92.3 NEXT_ACTION_JSON_COMMAND_SYNC, // M100 - NEXT_ACTION_JSON_WAIT // M101 + NEXT_ACTION_JSON_COMMAND_ASYNC, // M100.1 + NEXT_ACTION_JSON_WAIT, // M101 + +#if MARLIN_COMPAT_ENABLED == true + NEXT_ACTION_MARLIN_SET_EXTRUDER_TEMP, // 104, 109 + NEXT_ACTION_MARLIN_SET_BED_TEMP, // 140, 190 +#endif } cmNextAction; typedef enum { // G Modal Group 1 diff --git a/g2core/config.cpp b/g2core/config.cpp index 9548d65e..5e3574be 100644 --- a/g2core/config.cpp +++ b/g2core/config.cpp @@ -725,7 +725,7 @@ nvObj_t *nv_add_conditional_message(const char *string) // conditionally add void nv_print_list(stat_t status, uint8_t text_flags, uint8_t json_flags) { - if (js.json_mode == JSON_MODE) { + if ((js.json_mode == JSON_MODE) || (js.json_mode == MARLIN_COMM_MODE)) { json_print_list(status, json_flags); } else { text_print_list(status, text_flags); diff --git a/g2core/controller.cpp b/g2core/controller.cpp index 505fec4c..044ab720 100755 --- a/g2core/controller.cpp +++ b/g2core/controller.cpp @@ -171,6 +171,10 @@ static void _controller_HSM() DISPATCH(cm_jogging_cycle_callback()); // jog cycle operation DISPATCH(cm_deferred_write_callback()); // persist G10 changes when not in machining cycle +#if MARLIN_COMPAT_ENABLED == true + DISPATCH(marlin_callback()); // handle marlin stuff - may return EAGAIN, must be after planner_callback! +#endif + //----- command readers and parsers --------------------------------------------------// DISPATCH(_sync_to_planner()); // ensure there is at least one free buffer in planning queue diff --git a/g2core/gcode_parser.cpp b/g2core/gcode_parser.cpp index 3a89d071..ace88476 100644 --- a/g2core/gcode_parser.cpp +++ b/g2core/gcode_parser.cpp @@ -76,6 +76,8 @@ typedef struct GCodeInputValue { // Gcode inputs - meaning depends on context #if MARLIN_COMPAT_ENABLED == true bool marlin_temp_requested; // M105 temperature report request (Marlin-only) bool marlin_position_requested; // M114 position report request (Marlin-only) + + bool marlin_wait_for_temp; // M140 or M190 - wait for temperature (Marlin-only) #endif } GCodeValue_t; @@ -700,6 +702,12 @@ stat_t _parse_gcode_block(char *buf, char *active_comment) #warning MARLIN_COMPAT_ENABLED case 105: SET_NON_MODAL (marlin_temp_requested, true); case 114: SET_NON_MODAL (marlin_position_requested, true); + + case 140: gv.marlin_wait_for_temp = true; // NO break! + case 104: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_SET_EXTRUDER_TEMP); + + case 190: gv.marlin_wait_for_temp = true; // NO break! + case 109: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_SET_BED_TEMP); #endif // MARLIN_COMPAT_ENABLED default: status = STAT_MCODE_COMMAND_UNSUPPORTED; @@ -782,6 +790,40 @@ stat_t _execute_gcode_block(char *active_comment) cm_set_model_linenum(gv.linenum); EXEC_FUNC(cm_set_feed_rate_mode, feed_rate_mode); // G93, G94 EXEC_FUNC(cm_set_feed_rate, F_word); // F + +#if MARLIN_COMPAT_ENABLED == true + // Handle Marlin specifics + // must be before we deal with S_word and P_word, since it MIGHT use those + if (gf.marlin_temp_requested) { // M105 + js.json_mode = MARLIN_COMM_MODE; // we use M105 to know when to switch + ritorno(marlin_request_temperature_report()); + } + if (gf.marlin_position_requested) { // M114 + js.json_mode = MARLIN_COMM_MODE; // we use M105 to know when to switch + ritorno(marlin_request_position_report()); + } + switch (gv.next_action) { + case NEXT_ACTION_MARLIN_SET_EXTRUDER_TEMP: { // M104 or M140 + float temp = 0; + if (gf.S_word) { temp = gv.S_word; } + if (gf.P_word) { temp = gv.P_word; } // we treat them the same, for now + status = marlin_set_temperature(cm.gm.tool_select, temp, gv.marlin_wait_for_temp); + return status; + break; + } + case NEXT_ACTION_MARLIN_SET_BED_TEMP: { // M109 or M190 + float temp = 0; + if (gf.S_word) { temp = gv.S_word; } + if (gf.P_word) { temp = gv.P_word; } // we treat them the same, for now + + // Note: tool is zero-based, so to gt he3 (the bed) we pass tool as 2 + status = marlin_set_temperature(2, temp, gv.marlin_wait_for_temp); + return status; + break; + } + } +#endif // MARLIN_COMPAT_ENABLED + EXEC_FUNC(cm_set_spindle_speed, S_word); // S if (gf.sso_control) { // spindle speed override ritorno(cm_sso_control(gv.P_word, gf.P_word)); @@ -883,20 +925,6 @@ stat_t _execute_gcode_block(char *active_comment) } } -#if MARLIN_COMPAT_ENABLED == true - // Handle Marlin specifics - if (gf.marlin_temp_requested) { // spindle speed override - js.json_mode = MARLIN_COMM_MODE; // we use M105 to know when to switch - ritorno(marlin_request_temperature_report()); - } - if (gf.marlin_position_requested) { // spindle speed override - js.json_mode = MARLIN_COMM_MODE; // we use M105 to know when to switch - ritorno(marlin_request_position_report()); - } -#endif // MARLIN_COMPAT_ENABLED - - - // do the program stops and ends : M0, M1, M2, M30, M60 if (gf.program_flow == true) { if (gv.program_flow == PROGRAM_STOP) { diff --git a/g2core/marlin_compatibility.cpp b/g2core/marlin_compatibility.cpp index 156fdf1f..039a3aea 100644 --- a/g2core/marlin_compatibility.cpp +++ b/g2core/marlin_compatibility.cpp @@ -29,6 +29,8 @@ #include "util.h" #include "xio.h" // for char definitions #include "json_parser.h" +#include "planner.h" +#include "MotateTimers.h" // for char definitions // Structures used enum STK500 { @@ -51,6 +53,24 @@ enum STK500 { bool temperature_requested = false; bool position_requested = false; +// State machine to handle marlin temperature controls +enum class MarlinSetTempState { + Idle = 0, + SettingTemperature, + StartingUpdates, + StartingWait, + StoppingUpdates, + SettingTemperatureNoWait +}; +MarlinSetTempState set_temp_state; // record the state for the temperature-control pseudo-cycle +// These next parameters for the next temperature-control pseudo-cycle are only needed until the calls are queued. +float next_temperature; // as it says +uint8_t next_temperature_tool; // this is a g2core (1-based) tool + +// Information about if we are to be dumping periodic temperature updates +bool temperature_updates_requested = false; +Motate::Timeout temperature_update_timeout; + // local helper functions and macros /* @@ -263,27 +283,122 @@ void marlin_response(const stat_t status, char *buf) xio_writeline(buffer); } -//stat_t gc_get_gc(nvObj_t *nv) -//{ -// ritorno(nv_copy_string(nv, cs.saved_buf)); -// nv->valuetype = TYPE_STRING; -// return (STAT_OK); -//} -// -//stat_t gc_run_gc(nvObj_t *nv) -//{ -// return(gcode_parser(*nv->stringp)); -//} +/* + * _marlin_start_temperature_updates()/_marlin_end_temperature_updates() - + * calls from commands in the buffer to manage temperature_updates_requested + */ +void _marlin_start_temperature_updates(float* vect, bool* flag) { + temperature_updates_requested = true; + temperature_update_timeout.set(1); // immediately +} -/*********************************************************************************** - * TEXT MODE SUPPORT - * Functions to print variables from the cfgArray table - ***********************************************************************************/ +void _marlin_end_temperature_updates(float* vect, bool* flag) { + temperature_updates_requested = false; +} -#ifdef __TEXT_MODE +/* + * _queue_next_temperature_comands() - returns true if it finished + */ +bool _queue_next_temperature_commands() +{ + if (MarlinSetTempState::Idle != set_temp_state) { + if (mp_planner_is_full()) { + return false; + } -// no text mode functions here. Move along + char buffer[128]; + char *str = buffer; -#endif // __TEXT_MODE + if ((MarlinSetTempState::SettingTemperature == set_temp_state) || + (MarlinSetTempState::SettingTemperatureNoWait == set_temp_state)) + { + str += sprintf(str, "{he%dst:%.2f}", next_temperature_tool, next_temperature); + cm_json_command(buffer); + + if (MarlinSetTempState::SettingTemperatureNoWait == set_temp_state) { + set_temp_state = MarlinSetTempState::Idle; + return true; + } + + set_temp_state = MarlinSetTempState::StartingUpdates; + if (mp_planner_is_full()) { + return false; + } + } + + if (MarlinSetTempState::StartingUpdates == set_temp_state) { + mp_queue_command(_marlin_start_temperature_updates, nullptr, nullptr); + + set_temp_state = MarlinSetTempState::StartingWait; + if (mp_planner_is_full()) { + return false; + } + } + + if (MarlinSetTempState::StartingWait == set_temp_state) { + str = buffer; + str += sprintf(str, "{he%dat:t}", next_temperature_tool); + cm_json_wait(buffer); + + set_temp_state = MarlinSetTempState::StoppingUpdates; + if (mp_planner_is_full()) { + return false; + } + } + + if (MarlinSetTempState::StoppingUpdates == set_temp_state) { + mp_queue_command(_marlin_end_temperature_updates, nullptr, nullptr); + + set_temp_state = MarlinSetTempState::Idle; + } + } + + return true; +} + +/* + * marlin_callback() - called by controller dispatcher - return STAT_EAGAIN if it failed + */ +stat_t marlin_callback() +{ + if (temperature_updates_requested && (temperature_update_timeout.isPast())) { + char buffer[128]; + char *str = buffer; + + _report_temperatures(str); + + *str++ = '\n'; + *str++ = 0; + + temperature_update_timeout.set(1000); // every second + + xio_writeline(buffer); + } // temperature updates + + if (!_queue_next_temperature_commands()) { + return STAT_EAGAIN; + } + + return STAT_OK; +} + + +stat_t marlin_set_temperature(uint8_t tool, float temperature, bool wait) { + if (MarlinSetTempState::Idle != set_temp_state) { + return (STAT_BUFFER_FULL_FATAL); // this shouldn't happen + } + if ((tool < 0) || (tool > 3)) { + return STAT_INPUT_VALUE_RANGE_ERROR; + } + + set_temp_state = wait ? MarlinSetTempState::SettingTemperature : MarlinSetTempState::SettingTemperatureNoWait; + next_temperature = temperature; + + // Note the conversion from a "marlin" tool (zero-base) to a g2core tool (1-based) + next_temperature_tool = tool + 1; + + _queue_next_temperature_commands(); // we can ignore the return + return (STAT_OK); +} #endif // MARLIN_COMPAT_ENABLED == true diff --git a/g2core/marlin_compatibility.h b/g2core/marlin_compatibility.h index b062a7b4..7ad35fc4 100644 --- a/g2core/marlin_compatibility.h +++ b/g2core/marlin_compatibility.h @@ -33,7 +33,10 @@ bool marlin_handle_fake_stk500(char *str); stat_t marlin_request_temperature_report(); // M105 stat_t marlin_request_position_report(); // M114 +stat_t marlin_set_temperature(uint8_t tool, float temperature, bool wait); // M104, M109, M140, M190 void marlin_response(const stat_t status, char *buf); +stat_t marlin_callback(); + #endif // End of include guard: MARLIN_COMPAT_H_ONCE