More Marlin compatibility work

This commit is contained in:
Rob Giseburt
2017-01-18 12:44:12 -06:00
parent c2158b9662
commit 56b9991a16
12 changed files with 405 additions and 4 deletions
+2 -1
View File
@@ -195,7 +195,8 @@ typedef uint16_t index_t; // use this if there are > 255 indexed o
typedef enum {
TEXT_MODE = 0, // sticky text mode
JSON_MODE, // sticky JSON mode
AUTO_MODE // auto-configure communications mode
AUTO_MODE, // auto-configure communications mode
MARLIN_COMM_MODE, // sticky marlin-compatibility mode (if compiled in)
} commMode;
typedef enum {
+29 -1
View File
@@ -48,6 +48,10 @@
#include "MotatePower.h"
#if MARLIN_COMPAT_ENABLED == true
#include "marlin_compatibility.h"
#endif
/***********************************************************************************
**** STRUCTURE ALLOCATIONS *********************************************************
***********************************************************************************/
@@ -221,7 +225,15 @@ static void _dispatch_kernel(const devflags_t flags)
// It's possible to let some stuff through, but that's not happening yet.
return;
}
#if MARLIN_COMPAT_ENABLED == true
if (marlin_handle_fake_stk500(cs.bufp)) {
cs.comm_mode = MARLIN_COMM_MODE;
js.json_mode = MARLIN_COMM_MODE;
return;
}
#endif
while ((*cs.bufp == SPC) || (*cs.bufp == TAB)) { // position past any leading whitespace
cs.bufp++;
}
@@ -262,6 +274,12 @@ static void _dispatch_kernel(const devflags_t flags)
cs.comm_request_mode = TEXT_MODE; // mode of this command
text_response(gcode_parser(cs.bufp), cs.saved_buf);
}
#endif
#if MARLIN_COMPAT_ENABLED == true
else if (js.json_mode == MARLIN_COMM_MODE) { // handle marlin-specific protocol gcode
cs.comm_request_mode = MARLIN_COMM_MODE; // mode of this command
marlin_response(gcode_parser(cs.bufp), cs.saved_buf);
}
#endif
else { // anything else is interpreted as Gcode
cs.comm_request_mode = JSON_MODE; // mode of this command
@@ -272,6 +290,16 @@ static void _dispatch_kernel(const devflags_t flags)
nv_copy_string(nv, cs.bufp); // copy the Gcode line
nv->valuetype = TYPE_STRING;
status = gcode_parser(cs.bufp);
#if MARLIN_COMPAT_ENABLED == true
if (js.json_mode == MARLIN_COMM_MODE) { // in case a marlin-specific M-code was found
cs.comm_request_mode = MARLIN_COMM_MODE; // mode of this command
// We ae switching to marlin_comm_mode, kill status reports and queue reports
sr.status_report_verbosity = SR_OFF;
qr.queue_report_verbosity = QR_OFF;
marlin_response(gcode_parser(cs.bufp), cs.saved_buf);
return;
}
#endif
nv_print_list(status, TEXT_NO_PRINT, JSON_RESPONSE_FORMAT);
sr_request_status_report(SR_REQUEST_TIMED); // generate incremental status report to show any changes
}
+32
View File
@@ -30,6 +30,7 @@
#if MARLIN_COMPAT_ENABLED == true
#include "marlin_compatibility.h"
#include "json_parser.h" // so we can switch js.comm_mode on a marlin M-code
#endif
// Structures used by Gcode parser
@@ -71,6 +72,11 @@ typedef struct GCodeInputValue { // Gcode inputs - meaning depends on context
bool mfo_control; // M50 feedrate override control
bool mto_control; // M50.1 traverse override control
bool sso_control; // M51 spindle speed override control
#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)
#endif
} GCodeValue_t;
typedef struct GCodeFlags { // Gcode input flags
@@ -110,6 +116,11 @@ typedef struct GCodeFlags { // Gcode input flags
bool mfo_control;
bool mto_control;
bool sso_control;
#if MARLIN_COMPAT_ENABLED == true
bool marlin_temp_requested;
bool marlin_position_requested;
#endif
} GCodeFlag_t;
typedef struct GCodeParser {
@@ -684,6 +695,13 @@ stat_t _parse_gcode_block(char *buf, char *active_comment)
case 51: SET_MODAL (MODAL_GROUP_M9, sso_control, true);
case 100: SET_NON_MODAL (next_action, NEXT_ACTION_JSON_COMMAND_SYNC);
case 101: SET_NON_MODAL (next_action, NEXT_ACTION_JSON_WAIT);
#if MARLIN_COMPAT_ENABLED == true
#warning MARLIN_COMPAT_ENABLED
case 105: SET_NON_MODAL (marlin_temp_requested, true);
case 114: SET_NON_MODAL (marlin_position_requested, true);
#endif // MARLIN_COMPAT_ENABLED
default: status = STAT_MCODE_COMMAND_UNSUPPORTED;
}
break;
@@ -865,6 +883,20 @@ 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) {
+200
View File
@@ -28,8 +28,28 @@
#include "canonical_machine.h"
#include "util.h"
#include "xio.h" // for char definitions
#include "json_parser.h"
// Structures used
enum STK500 {
// Success
STATUS_CMD_OK = 0x00,
// Warnings
STATUS_CMD_TOUT = 0x80,
STATUS_RDY_BSY_TOUT = 0x81,
STATUS_SET_PARAM_MISSING = 0x82,
// Errors
STATUS_CMD_FAILED = 0xC0,
STATUS_CKSUM_ERROR = 0xC1,
STATUS_CMD_UNKNOWN = 0xC9,
};
// Local variables
bool temperature_requested = false;
bool position_requested = false;
// local helper functions and macros
@@ -37,6 +57,7 @@
* marlin_verify_checksum() - check to see if we have a line number (cheaply) and a valid checksum
*/
stat_t marlin_verify_checksum(char *str)
{
if (*str != 'N') { return STAT_OK; } // we only check if we have a line number
@@ -56,6 +77,58 @@ stat_t marlin_verify_checksum(char *str)
return STAT_OK;
}
void _marlin_fake_stk500_response(char *resp, uint16_t length)
{
char *str = resp;
str[2] = (length >> 8) & 0xFF;
str[3] = (length) & 0xFF;
uint8_t crc = 0;
for (uint16_t i = length + 5; i>0; i--) {
crc ^= *resp++;
}
*resp = crc;
xio_write(str, length + 6);
}
/*
* marlin_handle_fake_stk500() - returns true if it handled something (IOW, don't futher process the line)
*/
bool marlin_handle_fake_stk500(char *str)
{
char *resp = str;
if (*str != 0x1B) { return false; }
// we handle only a handful of messages ... poorly
// for example: this is where we should validate the checksum, but we are going to not for now.
str += 1 + 1 + 2 + 1; // 1 for 0x1B, 1 for sequence, 2 for length, 1 for 0x0E
char c = *str++;
if ((c == 0x01) || // CMD_SIGN_ON
(c == 0x10) || // CMD_ENTER_PROGMODE_ISP
(c == 0x11) // CMD_LEAVE_PROGMODE_ISP
)
{
*str = STATUS_CMD_OK;
_marlin_fake_stk500_response(resp, 2);
if (c == 0x11) { // CMD_LEAVE_PROGMODE_ISP
xio_exit_fake_bootloader();
}
return true;
}
*str = STATUS_CMD_UNKNOWN;
_marlin_fake_stk500_response(resp, 2);
return true;
}
/***********************************************************************************
@@ -63,6 +136,133 @@ stat_t marlin_verify_checksum(char *str)
* Functions to get and set variables from the cfgArray table
***********************************************************************************/
stat_t marlin_request_temperature_report() // M105
{
uint8_t tool = cm.gm.tool_select;
if (tool > 1) {
return STAT_INPUT_VALUE_RANGE_ERROR;
}
temperature_requested = true;
return STAT_OK;
}
stat_t marlin_request_position_report() // M114
{
position_requested = true;
return STAT_OK;
}
nvObj_t *_get_spcific_nv(const char *key) {
nvObj_t *nv = nv_reset_nv_list(); // returns first object in the body
strncpy(nv->token, key, TOKEN_LEN);
// validate and post-process the token
if ((nv->index = nv_get_index((const char *)"", nv->token)) == NO_MATCH) { // get index or fail it
// since we JUST provided the keys, this should never happen
return nullptr;
}
strcpy(nv->group, cfgArray[nv->index].group); // capture the group string if there is one
nv_get(nv);
return nv;
}
void _report_temperatures(char *(&str)) {
// Tool 0 is extruder 1
uint8_t tool = cm.gm.tool_select;
nvObj_t *nv = nullptr;
if (tool == 0) {
nv = _get_spcific_nv("he1t");
} else if (tool == 1) {
nv = _get_spcific_nv("he2t");
} else {
return; // we have no way of reporting errors here, ATM
}
if (!nv) { return; }
str += sprintf(str, " T:%.2f", (float)nv->value);
if (tool == 0) {
nv = _get_spcific_nv("he1st");
} else if (tool == 1) {
nv = _get_spcific_nv("he2st");
} else {
return; // we have no way of reporting errors here, ATM
}
if (!nv) { return; }
str += sprintf(str, " /%.2f", (float)nv->value);
nv = _get_spcific_nv("he3t");
if (!nv) { return; }
str += sprintf(str, " B:%.2f", (float)nv->value);
nv = _get_spcific_nv("he3st");
if (!nv) { return; }
str += sprintf(str, " /%.2f", (float)nv->value);
}
void _report_position(char *(&str)) {
//
// Tool 0 is extruder 1
uint8_t tool = cm.gm.tool_select;
str += sprintf(str, " X:%.2f", cm_get_work_position(ACTIVE_MODEL, 0));
str += sprintf(str, " Y:%.2f", cm_get_work_position(ACTIVE_MODEL, 1));
str += sprintf(str, " Z:%.2f", cm_get_work_position(ACTIVE_MODEL, 2));
if (tool == 0) {
str += sprintf(str, " E:%.2f", cm_get_work_position(ACTIVE_MODEL, 3));
} else if (tool == 1) {
str += sprintf(str, " E:%.2f", cm_get_work_position(ACTIVE_MODEL, 4));
}
}
/*
* marlin_response() - marlin mirror if text_response()
*/
void marlin_response(const stat_t status, char *buf)
{
char buffer[128];
char *str = buffer;
if ((status == STAT_OK) || (status == STAT_EAGAIN) || (status == STAT_NOOP)) {
strncpy(str, "ok", 2);
str += 2;
if (temperature_requested) {
_report_temperatures(str);
}
if (position_requested) {
_report_position(str);
}
// nvObj_t *nv = nv_body+1;
//
// if (nv_get_type(nv) == NV_TYPE_MESSAGE) {
// p += sprintf(p, (char *)*nv->stringp);
// }
// sprintf(p, "\n");
} else {
// str += sprintf(p, "echo:M%d %s", (int)status, get_status_message(status), buf);
str += sprintf(str, "Error:[%d] %s", (int)status, get_status_message(status));
}
*str++ = '\n';
*str++ = 0;
// reset requests
temperature_requested = false;
position_requested = false;
xio_writeline(buffer);
}
//stat_t gc_get_gc(nvObj_t *nv)
//{
// ritorno(nv_copy_string(nv, cs.saved_buf));
+6
View File
@@ -29,5 +29,11 @@
*/
stat_t marlin_verify_checksum(char *str);
bool marlin_handle_fake_stk500(char *str);
stat_t marlin_request_temperature_report(); // M105
stat_t marlin_request_position_report(); // M114
void marlin_response(const stat_t status, char *buf);
#endif // End of include guard: MARLIN_COMPAT_H_ONCE
+2 -1
View File
@@ -111,7 +111,8 @@ void rpt_print_loading_configs_message(void)
void rpt_print_system_ready_message(void)
{
_startup_helper(STAT_OK, "SYSTEM READY");
#warning DEAL WITH STARTUP MESSAGE
//_startup_helper(STAT_OK, "SYSTEM READY");
if (cs.comm_mode == TEXT_MODE) { text_response(STAT_OK, (char *)"");}// prompt
}
@@ -57,8 +57,10 @@
// Communications and reporting settings
#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode
#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects
#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE
#define JSON_VERBOSITY JV_LINENUM // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
@@ -60,6 +60,7 @@
#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode
#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects
#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE
#define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
@@ -57,8 +57,10 @@
// Communications and reporting settings
#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode
#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects
#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE
#define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
@@ -57,6 +57,7 @@
// Communications and reporting settings
#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode
#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects
+123
View File
@@ -173,6 +173,10 @@ struct xioDeviceWrapperBase { // C++ base class for device primit
virtual int16_t write(const char *buffer, int16_t len) { return -1; };
virtual char *readline(devflags_t limit_flags, uint16_t &size) { return nullptr; };
#if MARLIN_COMPAT_ENABLED == true
virtual void exitFakeBootloaderMode() {};
#endif
};
// Here we create the xio_t class, which has convenience methods to handle cross-device actions as a whole.
@@ -397,6 +401,14 @@ struct xio_t {
return (NULL);
};
#if MARLIN_COMPAT_ENABLED == true
void exitFakeBootloaderMode() {
for (int8_t i = 0; i < _dev_count; ++i) {
DeviceWrappers[i]->exitFakeBootloaderMode();
}
};
#endif
uint16_t magic_end;
};
@@ -447,6 +459,32 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> {
bool _last_returned_a_control = false;
#if MARLIN_COMPAT_ENABLED == true
enum class STK500V2_State {
Done, // not in the faked stk500v2 bootloader
Timeout, // timeout period, waiting for a start character
Start, // waiting for 0x1B
Sequence, // waiting for sequence byte
Length_0, // waiting for MSB of length
Length_1, // waiting for LSB of length
Header_End,// waiting for 0x0E
Data, // waiting for more data
Checksum // waiting for checksum byte
};
STK500V2_State _stk_parser_state;
uint16_t _stk_packet_data_length;
Motate::Timeout _stk_timeout;
void startFakeBootloaderMode() {
_stk_parser_state = STK500V2_State::Timeout;
_stk_timeout.set(2000); // two seconds
}
void exitFakeBootloaderMode() {
_stk_parser_state = STK500V2_State::Done;
}
#endif
LineRXBuffer(owner_type owner) : parent_type{owner} {};
void init() {
@@ -610,6 +648,70 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> {
return false;
}
#if MARLIN_COMPAT_ENABLED == true
// it's possible something will try to talk stk500v2 to us.
// See https://github.com/synthetos/g2/wiki/Marlin-Compatibility#stk500v2
if (_stk_parser_state >= STK500V2_State::Timeout)
{
if (_stk_parser_state == STK500V2_State::Timeout) {
if (_stk_timeout.isPast()) {
_stk_parser_state = STK500V2_State::Done;
// start over, outside of stk500v2 mode
continue;
}
// if we got something before the timeout, then we're in stk500v2 mode
// we'll look at what we got and maybe exit anyway
_stk_parser_state = STK500V2_State::Start;
}
if (_stk_parser_state == STK500V2_State::Start) {
if (c == 0x1B) {
_stk_parser_state = STK500V2_State::Sequence;
// this is the start of this "line" and we can "read" (skip) everything up to here.
_read_offset = _scan_offset;
_line_start_offset = _scan_offset;
}
else if ((c == '{') || (c == '\n') || (c == '\r') || (c == 'G') || (c == 'M')) {
// jump out of bootloader mode
_stk_parser_state = STK500V2_State::Done;
continue;
}
} else if (_stk_parser_state == STK500V2_State::Sequence) {
// we ignore the sequence
_stk_parser_state = STK500V2_State::Length_0;
} else if (_stk_parser_state == STK500V2_State::Length_0) {
_stk_packet_data_length = c << 8;
_stk_parser_state = STK500V2_State::Length_1;
} else if (_stk_parser_state == STK500V2_State::Length_1) {
_stk_packet_data_length |= c;
_stk_parser_state = STK500V2_State::Header_End;
} else if (_stk_parser_state == STK500V2_State::Header_End) {
if (c == 0x0E) {
_stk_parser_state = STK500V2_State::Data;
} else {
// end-of-header marker was corrupt, start over
_stk_packet_data_length = 0;
_stk_parser_state = STK500V2_State::Start;
}
} else if (_stk_parser_state == STK500V2_State::Data) {
// we don't read the data here, just return it
if (--_stk_packet_data_length == 0) {
_stk_parser_state = STK500V2_State::Checksum;
}
} else if (_stk_parser_state == STK500V2_State::Checksum) {
// we do NOT check the checksum, since if it's corrupt, we
// need to reply, and we can't reply here.
// at this point, we at least have a complete packet
// we will use the "control" return machanism to handle this
// since controls don't have to be \r\n-terminated
is_control = true;
}
}
else
#endif
// Look for line endings
if (c == '\r' || c == '\n') {
if (!_at_start_of_line) { // We only mark ends_line for the first end-line char, and if
@@ -1003,6 +1105,10 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re
setAsConnectedAndReady();
#if MARLIN_COMPAT_ENABLED == true
_rx_buffer.startFakeBootloaderMode();
#endif
if (isAlwaysDataAndCtrl()) {
// Case 1 (ignoring others)
setActive();
@@ -1087,6 +1193,13 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re
} // flags & DEV_IS_CONNECTED
}
};
#if MARLIN_COMPAT_ENABLED == true
void exitFakeBootloaderMode() override {
_rx_buffer.exitFakeBootloaderMode();
};
#endif
};
@@ -1289,6 +1402,16 @@ void xio_flush_to_command() {
return xio.flushToCommand();
}
#if MARLIN_COMPAT_ENABLED == true
/*
* xio_end_fake_bootloader() - end the fake bootloader mode
*/
void xio_exit_fake_bootloader() {
return xio.exitFakeBootloaderMode();
}
#endif
/***********************************************************************************
* newlib-nano support functions
* Here we wire printf to xio
+5 -1
View File
@@ -49,8 +49,9 @@
#define XIO_H_ONCE
//#include "g2core.h" // not required if used in g2core project
#include "config.h" // required for nvObj typedef
#include "config.h" // required for nvObj typedef
#include "canonical_machine.h" // needed for cm_has_hold()
#include "settings.h" // needed for MARLIN_COMPAT_ENABLED
/**** Defines, Macros, and Assorted Parameters ****/
@@ -122,6 +123,9 @@ char *xio_readline(devflags_t &flags, uint16_t &size);
int16_t xio_writeline(const char *buffer, bool only_to_muted = false);
bool xio_connected();
void xio_flush_to_command();
#if MARLIN_COMPAT_ENABLED == true
void xio_exit_fake_bootloader();
#endif
stat_t xio_set_spi(nvObj_t *nv);