From bb69fa14ff12e4bac071eaa19a81414db0b31081 Mon Sep 17 00:00:00 2001 From: ellensp Date: Mon, 18 Jan 2021 01:00:15 +1300 Subject: [PATCH 01/19] Initial Commit --- Marlin/Configuration.h | 3 + Marlin/src/feature/meatpack.cpp | 370 ++++++++++++++++++++++++++++++++ Marlin/src/feature/meatpack.h | 65 ++++++ Marlin/src/gcode/host/M115.cpp | 3 + Marlin/src/gcode/queue.cpp | 161 ++++++++------ 5 files changed, 531 insertions(+), 71 deletions(-) create mode 100644 Marlin/src/feature/meatpack.cpp create mode 100644 Marlin/src/feature/meatpack.h diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 3f9b6174c2fd..604160cf27dd 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -123,6 +123,9 @@ */ #define BAUDRATE 250000 +// Enable g-code compression (see https://github.com/scottmudge/OctoPrint-MeatPack) +#define MEATPACK + // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp new file mode 100644 index 000000000000..9f31a3fdc695 --- /dev/null +++ b/Marlin/src/feature/meatpack.cpp @@ -0,0 +1,370 @@ +/* +* MeatPack G-Code Compression +* +* Algorithm & Implementation: Scott Mudge - mail@scottmudge.com +* Date: Dec. 2020 +*/ + +#include "../inc/MarlinConfig.h" + +#if ENABLED(MEATPACK) + +#include "meatpack.h" + +//#include "language.h" +//#include "Marlin.h" + +//#define MP_DEBUG + +// Utility definitions +#define MeatPack_CommandByte 0b11111111 +#define MeatPack_NextPackedFirst 0b00000001 +#define MeatPack_NextPackedSecond 0b00000010 + +#define MeatPack_SpaceCharIdx 11U +#define MeatPack_SpaceCharReplace 'E' + +/* + Character Frequencies from ~30 MB of comment-stripped gcode: + '1' -> 4451136 + '0' -> 4253577 + ' ' -> 3053297 + '.' -> 3035310 + '2' -> 1523296 + '8' -> 1366812 + '4' -> 1353273 + '9' -> 1352147 + '3' -> 1262929 + '5' -> 1189871 + '6' -> 1127900 + '7' -> 1112908 + '\n' -> 1087683 + 'G' -> 1075806 + 'X' -> 975742 + 'E' -> 965275 + 'Y' -> 965274 + 'F' -> 99416 + '-' -> 90242 + 'Z' -> 34109 + 'M' -> 11879 + 'S' -> 9910 + If spaces are omitted, we add 'E' +*/ + +// Note: +// I've tried both a switch/case method and a lookup table. The disassembly is exactly the same after compilation, byte-to-byte. +// Thus, performance is identical. +#define USE_LOOKUP_TABLE + +#ifdef USE_LOOKUP_TABLE +// The 15 most-common characters used in G-code, ~90-95% of all g-code uses these characters +// NOT storing this with PROGMEM, given how frequently this table will be accessed. +uint8_t MeatPackLookupTbl[16] = { + '0', // 0000 + '1', // 0001 + '2', // 0010 + '3', // 0011 + '4', // 0100 + '5', // 0101 + '6', // 0110 + '7', // 0111 + '8', // 1000 + '9', // 1001 + '.', // 1010 + ' ', // 1011 + '\n', // 1100 + 'G', // 1101 + 'X', // 1110 + '\0' // never used, 0b1111 is used to indicate next 8-bits is a full character +}; +#else +inline uint8_t get_char(register uint8_t in) { + switch (in) { + case 0b0000: + return '0'; + break; + case 0b0001: + return '1'; + break; + case 0b0010: + return '2'; + break; + case 0b0011: + return '3'; + break; + case 0b0100: + return '4'; + break; + case 0b0101: + return '5'; + break; + case 0b0110: + return '6'; + break; + case 0b0111: + return '7'; + break; + case 0b1000: + return '8'; + break; + case 0b1001: + return '9'; + break; + case 0b1010: + return '.'; + break; + case 0b1011: + return ' '; + break; + case 0b1100: + return '\n'; + break; + case 0b1101: + return 'G'; + break; + case 0b1110: + return 'X'; + break; + } + return 0; +} +#endif + + +// State variables +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +enum MeatPack_ConfigStateFlags { + MPConfig_None = 0, + MPConfig_Active = (1 << 0), + MPConfig_NoSpaces = (1 << 1) +}; + +uint8_t mp_config = MPConfig_None; // Configuration state +uint8_t mp_cmd_active = 0; // Is a command is pending +uint8_t mp_char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs +uint8_t mp_cmd_count = 0; // Counts how many command bytes are received (need 2) +uint8_t mp_full_char_queue = 0; // Counts how many full-width characters are to be received +uint8_t mp_char_out_buf[2]; // Output buffer for caching up to 2 characters +uint8_t mp_char_out_count = 0; // Stores number of characters to be read out. + +// #DEBUGGING +#ifdef MP_DEBUG +uint32_t mp_chars_decoded = 0; +#endif + +void FORCE_INLINE mp_handle_output_char(const uint8_t c) { + mp_char_out_buf[mp_char_out_count++] = c; + +#ifdef MP_DEBUG + if (mp_chars_decoded < 64) { + ++mp_chars_decoded; + SERIAL_ECHOPGM("Rec Byte: "); + MYSERIAL.print("0x"); + MYSERIAL.print((uint8_t)c, HEX); + SERIAL_ECHOLNPGM(""); + } +#endif +} + +// Storing +// packed = ((low & 0xF) << 4) | (high & 0xF); + +// Unpacking +// low = (packed >> 4) & 0xF; +// high = (packed & 0xF); + +//========================================================================== +uint8_t FORCE_INLINE mp_unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) { + register uint8_t out = 0; + +#ifdef USE_LOOKUP_TABLE + // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. + if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; + else chars_out[0] = MeatPackLookupTbl[(pk & 0xF)]; // Assign lower char + + // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. + if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; + else chars_out[1] = MeatPackLookupTbl[((pk >> 4) & 0xf)]; // Assign upper char +#else + // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. + if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; + else chars_out[0] = get_char(pk & 0xF); // Assign lower char + + // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. + if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; + else chars_out[1] = get_char((pk >> 4) & 0xf); // Assign upper char +#endif + + return out; +} + +//============================================================================== +void FORCE_INLINE mp_reset_state() { + SERIAL_ECHOLNPGM("MP Reset"); + mp_char_out_count = 0; + mp_cmd_active = MPCommand_None; + mp_config = MPConfig_None; + mp_char_buf = 0; + mp_cmd_count = 0; + mp_cmd_active = 0; + mp_full_char_queue = 0; + +#ifdef MP_DEBUG + mp_chars_decoded = 0; +#endif +} + +//========================================================================== +void FORCE_INLINE mp_handle_rx_char_inner(const uint8_t c) { + + // Packing enabled, handle character and re-arrange them appropriately. + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + if (mp_config & MPConfig_Active) { + if (mp_full_char_queue > 0) { + mp_handle_output_char(c); + if (mp_char_buf > 0) { + mp_handle_output_char(mp_char_buf); + mp_char_buf = 0; + } + --mp_full_char_queue; + } + else { + uint8_t buf[2] = { 0,0 }; + register const uint8_t res = mp_unpack_chars(c, buf); + + if (res & MeatPack_NextPackedFirst) { + ++mp_full_char_queue; + if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; + else mp_char_buf = buf[1]; + } + else { + mp_handle_output_char(buf[0]); + if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; + else mp_handle_output_char(buf[1]); + } + } + } + else // Packing not enabled, just copy character to output + mp_handle_output_char(c); +} + +//========================================================================== +void FORCE_INLINE mp_echo_config_state() { + SERIAL_ECHOPGM("[MP] "); + + // Echo current state + if (mp_config & MPConfig_Active) + SERIAL_ECHOPGM("ON"); + else + SERIAL_ECHOPGM("OFF"); + + if (mp_config & MPConfig_NoSpaces) + SERIAL_ECHOPGM("NSP"); // [N]o [SP]aces + else + SERIAL_ECHOPGM("ESP"); // [E]nabled [SP]aces + + SERIAL_ECHOLNPGM(""); + + // Validate config vars + MeatPackLookupTbl[MeatPack_SpaceCharIdx] = ((mp_config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '); +} + +//========================================================================== +void FORCE_INLINE mp_handle_cmd(const MeatPack_Command c) { + switch (c) { + case MPCommand_EnablePacking: { + mp_config |= MPConfig_Active; +#ifdef MP_DEBUG + SERIAL_ECHOLNPGM("[MPDBG] ENABL REC"); +#endif + } break; + case MPCommand_DisablePacking: { + mp_config &= ~(MPConfig_Active); +#ifdef MP_DEBUG + SERIAL_ECHOLNPGM("[MPDBG] DISBL REC"); +#endif + } break; + case MPCommand_TogglePacking: { + mp_config ^= MPConfig_Active; +#ifdef MP_DEBUG + SERIAL_ECHOLNPGM("[MPDBG] TGL REC"); +#endif + } break; + case MPCommand_ResetAll: { + mp_reset_state(); +#ifdef MP_DEBUG + SERIAL_ECHOLNPGM("[MPDBG] RESET REC"); +#endif + } break; + case MPCommand_EnableNoSpaces: { + mp_config |= MPConfig_NoSpaces; +#ifdef MP_DEBUG + SERIAL_ECHOLNPGM("[MPDBG] ENABL NSP"); +#endif + } break; + case MPCommand_DisableNoSpaces: { + mp_config &= ~(MPConfig_NoSpaces); +#ifdef MP_DEBUG + SERIAL_ECHOLNPGM("[MPDBG] DISBL NSP"); +#endif + } break; + default: { +#ifdef MP_DEBUG + SERIAL_ECHOLN("[MPDBG] UNK CMD REC"); +#endif + } + case MPCommand_QueryConfig: + break; + } + + mp_echo_config_state(); +} + +//========================================================================== +void mp_handle_rx_char(const uint8_t c) { + + // Check for commit complete + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + if (c == (uint8_t)(MeatPack_CommandByte)) { + if (mp_cmd_count > 0) { + mp_cmd_active = 1; + mp_cmd_count = 0; + } + else + ++mp_cmd_count; + return; + } + + if (mp_cmd_active > 0) { + mp_handle_cmd((MeatPack_Command)c); + mp_cmd_active = 0; + return; + } + + if (mp_cmd_count > 0) { + mp_handle_rx_char_inner((uint8_t)(MeatPack_CommandByte)); + mp_cmd_count = 0; + } + + mp_handle_rx_char_inner(c); +} + +//========================================================================== +uint8_t mp_get_result_char(char* const __restrict out) { + if (mp_char_out_count > 0) { + const uint8_t res = mp_char_out_count; + for (register uint8_t i = 0; i < mp_char_out_count; ++i) + out[i] = (char)mp_char_out_buf[i]; + mp_char_out_count = 0; + return res; + } + return 0; +} + +//============================================================================== +void mp_trigger_cmd(const MeatPack_Command cmd) +{ + mp_handle_cmd(cmd); +} + +#endif // MEATPACK diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h new file mode 100644 index 000000000000..2720584aab70 --- /dev/null +++ b/Marlin/src/feature/meatpack.h @@ -0,0 +1,65 @@ +/* +* MeatPack G-Code Compression +* +* Algorithm & Implementation: Scott Mudge - mail@scottmudge.com +* Date: Dec. 2020 +* +* Specifically optimized for 3D printing G-Code, this is a zero-cost data compression method +* which packs ~180-190% more data into the same amount of bytes going to the CNC controller. +* As a majority of G-Code can be represented by a restricted alphabet, I performed histogram +* analysis on a wide variety of 3D printing gcode samples, and found ~93% of all gcode could +* be represented by the same 15-character alphabet. +* +* This allowed me to design a system of packing 2 8-bit characters into a single byte, assuming +* they fall within this limited 15-character alphabet. Using a 4-bit lookup table, these 8-bit +* characters can be represented by a 4-bit index. +* +* Combined with some logic to allow commingling of full-width characters outside of this 15- +* character alphabet (at the cost of an extra 8-bits per full-width character), and by stripping +* out unnecessary comments, the end result is gcode which is roughly half the original size. +* +* Why did I do this? I noticed micro-stuttering and other data-bottleneck issues while printing +* objects with high curvature, especially at high speeds. There is also the issue of the limited +* baud rate provided by Prusa's Atmega2560-based boards, over the USB serial connection. So soft- +* ware like OctoPrint would also suffer this same micro-stuttering and poor print quality issue. +* +*/ +//#include +#include "../inc/MarlinConfig.h" + +#if ENABLED(MEATPACK) + +#define MeatPack_SecondNotPacked 0b11110000 +#define MeatPack_FirstNotPacked 0b00001111 + +// These are commands sent to MeatPack to control its behavior. +// They are sent by first sending 2x MeatPack_CommandByte (0xFF) in sequence, +// followed by one of the command bytes below. +// Provided that 0xFF is an exceedingly rare character that is virtually never +// present in g-code naturally, it is safe to assume 2 in sequence should never +// happen naturally, and so it is used as a signal here. +// +// 0xFF *IS* used in "packed" g-code (used to denote that the next 2 characters are +// full-width), however 2 in a row will never occur, as the next 2 bytes will always +// some non-0xFF character. +enum MeatPack_Command { + MPCommand_None = 0U, + MPCommand_TogglePacking = 253U, + MPCommand_EnablePacking = 251U, + MPCommand_DisablePacking = 250U, + MPCommand_ResetAll = 249U, + MPCommand_QueryConfig = 248U, + MPCommand_EnableNoSpaces = 247U, + MPCommand_DisableNoSpaces = 246U +}; + +// Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences, +// and will control state internally. +extern void mp_handle_rx_char(const uint8_t c); + +// After passing in rx'd char using above method, call this to get characters out. Can return +// from 0 to 2 characters at once. +// @param out [in] Output pointer for unpacked/processed data. +// @return Number of characters returned. Range from 0 to 2. +extern uint8_t mp_get_result_char(char* const __restrict out); +#endif // MEATPACK diff --git a/Marlin/src/gcode/host/M115.cpp b/Marlin/src/gcode/host/M115.cpp index 63511b606d16..1b088e7d34b9 100644 --- a/Marlin/src/gcode/host/M115.cpp +++ b/Marlin/src/gcode/host/M115.cpp @@ -141,6 +141,9 @@ void GcodeSuite::M115() { // CHAMBER_TEMPERATURE (M141, M191) cap_line(PSTR("CHAMBER_TEMPERATURE"), ENABLED(HAS_HEATED_CHAMBER)); + // MEATPACK Compresson + cap_line(PSTR("MEATPACK"), ENABLED(MEATPACK)); + // Machine Geometry #if ENABLED(M115_GEOMETRY_REPORT) const xyz_pos_t dmin = { X_MIN_POS, Y_MIN_POS, Z_MIN_POS }, diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index 98fe91db4074..9df765dc79d1 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -34,6 +34,9 @@ GCodeQueue queue; #include "../module/planner.h" #include "../module/temperature.h" #include "../MarlinCore.h" +#if ENABLED(MEATPACK) + #include "../feature/meatpack.h" +#endif #if ENABLED(PRINTER_EVENT_LEDS) #include "../feature/leds/printer_event_leds.h" @@ -467,100 +470,116 @@ void GCodeQueue::get_serial_commands() { while (length < BUFSIZE && serial_data_available()) { LOOP_L_N(i, NUM_SERIAL) { - const int c = read_serial(i); - if (c < 0) continue; + #if ENABLED(MEATPACK) + // MeatPack Changes + const int rec = read_serial(i); + if (rec < 0) continue; - const char serial_char = c; + mp_handle_rx_char((uint8_t)rec); + char c_res[2] = {0, 0}; + const uint8_t char_count = mp_get_result_char(c_res); - if (ISEOL(serial_char)) { + for (uint8_t char_index = 0; char_index < char_count; ++char_index){ + const int c = c_res[char_index]; + #else + const int c = read_serial(i); + #endif + if (c < 0) continue; - // Reset our state, continue if the line was empty - if (process_line_done(serial_input_state[i], serial_line_buffer[i], serial_count[i])) - continue; + const char serial_char = c; - char* command = serial_line_buffer[i]; + if (ISEOL(serial_char)) { - while (*command == ' ') command++; // Skip leading spaces - char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line + // Reset our state, continue if the line was empty + if (process_line_done(serial_input_state[i], serial_line_buffer[i], serial_count[i])) + continue; - if (npos) { + char* command = serial_line_buffer[i]; - const bool M110 = !!strstr_P(command, PSTR("M110")); + while (*command == ' ') command++; // Skip leading spaces + char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line - if (M110) { - char* n2pos = strchr(command + 4, 'N'); - if (n2pos) npos = n2pos; - } + if (npos) { - const long gcode_N = strtol(npos + 1, nullptr, 10); + const bool M110 = !!strstr_P(command, PSTR("M110")); - if (gcode_N != last_N[i] + 1 && !M110) - return gcode_line_error(PSTR(STR_ERR_LINE_NO), i); + if (M110) { + char* n2pos = strchr(command + 4, 'N'); + if (n2pos) npos = n2pos; + } - char *apos = strrchr(command, '*'); - if (apos) { - uint8_t checksum = 0, count = uint8_t(apos - command); - while (count) checksum ^= command[--count]; - if (strtol(apos + 1, nullptr, 10) != checksum) - return gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), i); - } - else - return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); + const long gcode_N = strtol(npos + 1, nullptr, 10); - last_N[i] = gcode_N; - } - #if ENABLED(SDSUPPORT) - // Pronterface "M29" and "M29 " has no line number - else if (card.flag.saving && !is_M29(command)) - return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); - #endif + if (gcode_N != last_N[i] + 1 && !M110) + return gcode_line_error(PSTR(STR_ERR_LINE_NO), i); - // - // Movement commands give an alert when the machine is stopped - // - - if (IsStopped()) { - char* gpos = strchr(command, 'G'); - if (gpos) { - switch (strtol(gpos + 1, nullptr, 10)) { - case 0: case 1: - #if ENABLED(ARC_SUPPORT) - case 2: case 3: - #endif - #if ENABLED(BEZIER_CURVE_SUPPORT) - case 5: - #endif - PORT_REDIRECT(i); // Reply to the serial port that sent the command - SERIAL_ECHOLNPGM(STR_ERR_STOPPED); - LCD_MESSAGEPGM(MSG_STOPPED); - break; + char *apos = strrchr(command, '*'); + if (apos) { + uint8_t checksum = 0, count = uint8_t(apos - command); + while (count) checksum ^= command[--count]; + if (strtol(apos + 1, nullptr, 10) != checksum) + return gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), i); + } + else + return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); + + last_N[i] = gcode_N; + } + #if ENABLED(SDSUPPORT) + // Pronterface "M29" and "M29 " has no line number + else if (card.flag.saving && !is_M29(command)) + return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); + #endif + + // + // Movement commands give an alert when the machine is stopped + // + + if (IsStopped()) { + char* gpos = strchr(command, 'G'); + if (gpos) { + switch (strtol(gpos + 1, nullptr, 10)) { + case 0: case 1: + #if ENABLED(ARC_SUPPORT) + case 2: case 3: + #endif + #if ENABLED(BEZIER_CURVE_SUPPORT) + case 5: + #endif + PORT_REDIRECT(i); // Reply to the serial port that sent the command + SERIAL_ECHOLNPGM(STR_ERR_STOPPED); + LCD_MESSAGEPGM(MSG_STOPPED); + break; + } + } } - } - } - #if DISABLED(EMERGENCY_PARSER) - // Process critical commands early + #if DISABLED(EMERGENCY_PARSER) + // Process critical commands early if (command[0] == 'M') switch (command[3]) { case '8': if (command[2] == '0' && command[1] == '1') { wait_for_heatup = false; TERN_(HAS_LCD_MENU, wait_for_user = false); } break; case '2': if (command[2] == '1' && command[1] == '1') kill(M112_KILL_STR, nullptr, true); break; case '0': if (command[1] == '4' && command[2] == '1') quickstop_stepper(); break; } - #endif + #endif - #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0 - last_command_time = ms; - #endif + #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0 + last_command_time = ms; + #endif - // Add the command to the queue - _enqueue(serial_line_buffer[i], true - #if HAS_MULTI_SERIAL - , i - #endif - ); - } - else - process_stream_char(serial_char, serial_input_state[i], serial_line_buffer[i], serial_count[i]); + // Add the command to the queue + _enqueue(serial_line_buffer[i], true + #if HAS_MULTI_SERIAL + , i + #endif + ); + } + else + process_stream_char(serial_char, serial_input_state[i], serial_line_buffer[i], serial_count[i]); +#if ENABLED(MEATPACK) + } +#endif } // for NUM_SERIAL } // queue has space, serial has data } From 87b7ba215098a2b2b8fbff03d4b1619170834f16 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 18 Jan 2021 22:30:15 -0600 Subject: [PATCH 02/19] Clean up, add test --- Marlin/Configuration.h | 4 +- Marlin/src/feature/meatpack.cpp | 484 +++++++++++++++----------------- Marlin/src/feature/meatpack.h | 130 +++++---- Marlin/src/gcode/queue.cpp | 176 ++++++------ buildroot/tests/FYSETC_S6-tests | 1 + 5 files changed, 398 insertions(+), 397 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 604160cf27dd..ab7961d515f4 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -123,8 +123,8 @@ */ #define BAUDRATE 250000 -// Enable g-code compression (see https://github.com/scottmudge/OctoPrint-MeatPack) -#define MEATPACK +// Enable G-code compression (see https://github.com/scottmudge/OctoPrint-MeatPack) +//#define MEATPACK // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 9f31a3fdc695..7270ebc8dbee 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -1,9 +1,31 @@ -/* -* MeatPack G-Code Compression -* -* Algorithm & Implementation: Scott Mudge - mail@scottmudge.com -* Date: Dec. 2020 -*/ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 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 . + * + */ + +/** + * MeatPack G-Code Compression + * + * Algorithm & Implementation: Scott Mudge - mail@scottmudge.com + * Date: Dec. 2020 + */ #include "../inc/MarlinConfig.h" @@ -15,40 +37,42 @@ //#include "Marlin.h" //#define MP_DEBUG +#define DEBUG_OUT ENABLED(MP_DEBUG) +#include "../core/debug_out.h" // Utility definitions -#define MeatPack_CommandByte 0b11111111 -#define MeatPack_NextPackedFirst 0b00000001 -#define MeatPack_NextPackedSecond 0b00000010 +#define MeatPack_CommandByte 0b11111111 +#define MeatPack_NextPackedFirst 0b00000001 +#define MeatPack_NextPackedSecond 0b00000010 #define MeatPack_SpaceCharIdx 11U #define MeatPack_SpaceCharReplace 'E' /* - Character Frequencies from ~30 MB of comment-stripped gcode: - '1' -> 4451136 - '0' -> 4253577 - ' ' -> 3053297 - '.' -> 3035310 - '2' -> 1523296 - '8' -> 1366812 - '4' -> 1353273 - '9' -> 1352147 - '3' -> 1262929 - '5' -> 1189871 - '6' -> 1127900 - '7' -> 1112908 - '\n' -> 1087683 - 'G' -> 1075806 - 'X' -> 975742 - 'E' -> 965275 - 'Y' -> 965274 - 'F' -> 99416 - '-' -> 90242 - 'Z' -> 34109 - 'M' -> 11879 - 'S' -> 9910 - If spaces are omitted, we add 'E' + Character Frequencies from ~30 MB of comment-stripped gcode: + '1' -> 4451136 + '0' -> 4253577 + ' ' -> 3053297 + '.' -> 3035310 + '2' -> 1523296 + '8' -> 1366812 + '4' -> 1353273 + '9' -> 1352147 + '3' -> 1262929 + '5' -> 1189871 + '6' -> 1127900 + '7' -> 1112908 + '\n' -> 1087683 + 'G' -> 1075806 + 'X' -> 975742 + 'E' -> 965275 + 'Y' -> 965274 + 'F' -> 99416 + '-' -> 90242 + 'Z' -> 34109 + 'M' -> 11879 + 'S' -> 9910 + If spaces are omitted, we add 'E' */ // Note: @@ -57,86 +81,48 @@ #define USE_LOOKUP_TABLE #ifdef USE_LOOKUP_TABLE -// The 15 most-common characters used in G-code, ~90-95% of all g-code uses these characters -// NOT storing this with PROGMEM, given how frequently this table will be accessed. -uint8_t MeatPackLookupTbl[16] = { - '0', // 0000 - '1', // 0001 - '2', // 0010 - '3', // 0011 - '4', // 0100 - '5', // 0101 - '6', // 0110 - '7', // 0111 - '8', // 1000 - '9', // 1001 - '.', // 1010 - ' ', // 1011 - '\n', // 1100 - 'G', // 1101 - 'X', // 1110 + // The 15 most-common characters used in G-code, ~90-95% of all G-code uses these characters + // NOT storing this with PROGMEM, given how frequently this table will be accessed. + constexpr uint8_t MeatPackLookupTbl[16] = { + '0', // 0000 + '1', // 0001 + '2', // 0010 + '3', // 0011 + '4', // 0100 + '5', // 0101 + '6', // 0110 + '7', // 0111 + '8', // 1000 + '9', // 1001 + '.', // 1010 + ' ', // 1011 + '\n',// 1100 + 'G', // 1101 + 'X', // 1110 '\0' // never used, 0b1111 is used to indicate next 8-bits is a full character -}; + }; + #else -inline uint8_t get_char(register uint8_t in) { - switch (in) { - case 0b0000: - return '0'; - break; - case 0b0001: - return '1'; - break; - case 0b0010: - return '2'; - break; - case 0b0011: - return '3'; - break; - case 0b0100: - return '4'; - break; - case 0b0101: - return '5'; - break; - case 0b0110: - return '6'; - break; - case 0b0111: - return '7'; - break; - case 0b1000: - return '8'; - break; - case 0b1001: - return '9'; - break; - case 0b1010: - return '.'; - break; - case 0b1011: - return ' '; - break; - case 0b1100: - return '\n'; - break; - case 0b1101: - return 'G'; - break; - case 0b1110: - return 'X'; - break; - } - return 0; -} -#endif + inline uint8_t get_char(register uint8_t in) { + switch (in) { + case 0b0000 ... 0b1001: return '0' + in; + case 0b1010: return '.'; + case 0b1011: return ' '; + case 0b1100: return '\n'; + case 0b1101: return 'G'; + case 0b1110: return 'X'; + } + return 0; + } +#endif // State variables // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - enum MeatPack_ConfigStateFlags { - MPConfig_None = 0, - MPConfig_Active = (1 << 0), - MPConfig_NoSpaces = (1 << 1) + MPConfig_None = 0, + MPConfig_Active = (1 << 0), + MPConfig_NoSpaces = (1 << 1) }; uint8_t mp_config = MPConfig_None; // Configuration state @@ -147,23 +133,23 @@ uint8_t mp_full_char_queue = 0; // Counts how many full-width characters are uint8_t mp_char_out_buf[2]; // Output buffer for caching up to 2 characters uint8_t mp_char_out_count = 0; // Stores number of characters to be read out. -// #DEBUGGING +// DEBUGGING #ifdef MP_DEBUG -uint32_t mp_chars_decoded = 0; + uint32_t mp_chars_decoded = 0; #endif void FORCE_INLINE mp_handle_output_char(const uint8_t c) { - mp_char_out_buf[mp_char_out_count++] = c; + mp_char_out_buf[mp_char_out_count++] = c; -#ifdef MP_DEBUG + #ifdef MP_DEBUG if (mp_chars_decoded < 64) { - ++mp_chars_decoded; - SERIAL_ECHOPGM("Rec Byte: "); - MYSERIAL.print("0x"); - MYSERIAL.print((uint8_t)c, HEX); - SERIAL_ECHOLNPGM(""); + ++mp_chars_decoded; + DEBUG_ECHOPGM("Rec Byte: "); + MYSERIAL.print("0x"); + MYSERIAL.print((uint8_t)c, HEX); + DEBUG_EOL(); } -#endif + #endif } // Storing @@ -174,197 +160,191 @@ void FORCE_INLINE mp_handle_output_char(const uint8_t c) { // high = (packed & 0xF); //========================================================================== + uint8_t FORCE_INLINE mp_unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) { - register uint8_t out = 0; + register uint8_t out = 0; + + #ifdef USE_LOOKUP_TABLE -#ifdef USE_LOOKUP_TABLE // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; else chars_out[0] = MeatPackLookupTbl[(pk & 0xF)]; // Assign lower char // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; - else chars_out[1] = MeatPackLookupTbl[((pk >> 4) & 0xf)]; // Assign upper char -#else + else chars_out[1] = MeatPackLookupTbl[((pk >> 4) & 0xF)]; // Assign upper char + + #else // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; else chars_out[0] = get_char(pk & 0xF); // Assign lower char // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; - else chars_out[1] = get_char((pk >> 4) & 0xf); // Assign upper char -#endif + else chars_out[1] = get_char((pk >> 4) & 0xF); // Assign upper char - return out; + #endif + + return out; } //============================================================================== -void FORCE_INLINE mp_reset_state() { - SERIAL_ECHOLNPGM("MP Reset"); - mp_char_out_count = 0; - mp_cmd_active = MPCommand_None; - mp_config = MPConfig_None; - mp_char_buf = 0; - mp_cmd_count = 0; - mp_cmd_active = 0; - mp_full_char_queue = 0; -#ifdef MP_DEBUG +void FORCE_INLINE mp_reset_state() { + SERIAL_ECHOLNPGM("MP Reset"); + mp_char_out_count = 0; + mp_cmd_active = MPCommand_None; + mp_config = MPConfig_None; + mp_char_buf = 0; + mp_cmd_count = 0; + mp_cmd_active = 0; + mp_full_char_queue = 0; + + #ifdef MP_DEBUG mp_chars_decoded = 0; -#endif + #endif } //========================================================================== + void FORCE_INLINE mp_handle_rx_char_inner(const uint8_t c) { - // Packing enabled, handle character and re-arrange them appropriately. - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (mp_config & MPConfig_Active) { - if (mp_full_char_queue > 0) { - mp_handle_output_char(c); - if (mp_char_buf > 0) { - mp_handle_output_char(mp_char_buf); - mp_char_buf = 0; - } - --mp_full_char_queue; - } - else { - uint8_t buf[2] = { 0,0 }; - register const uint8_t res = mp_unpack_chars(c, buf); - - if (res & MeatPack_NextPackedFirst) { - ++mp_full_char_queue; - if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; - else mp_char_buf = buf[1]; - } - else { - mp_handle_output_char(buf[0]); - if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; - else mp_handle_output_char(buf[1]); - } + // Packing enabled, handle character and re-arrange them appropriately. + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + if (mp_config & MPConfig_Active) { + if (mp_full_char_queue > 0) { + mp_handle_output_char(c); + if (mp_char_buf > 0) { + mp_handle_output_char(mp_char_buf); + mp_char_buf = 0; } + --mp_full_char_queue; } - else // Packing not enabled, just copy character to output - mp_handle_output_char(c); + else { + uint8_t buf[2] = { 0,0 }; + register const uint8_t res = mp_unpack_chars(c, buf); + + if (res & MeatPack_NextPackedFirst) { + ++mp_full_char_queue; + if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; + else mp_char_buf = buf[1]; + } + else { + mp_handle_output_char(buf[0]); + if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; + else mp_handle_output_char(buf[1]); + } + } + } + else // Packing not enabled, just copy character to output + mp_handle_output_char(c); } //========================================================================== + void FORCE_INLINE mp_echo_config_state() { - SERIAL_ECHOPGM("[MP] "); + SERIAL_ECHOPGM("[MP] "); - // Echo current state - if (mp_config & MPConfig_Active) - SERIAL_ECHOPGM("ON"); - else - SERIAL_ECHOPGM("OFF"); + // Echo current state + serialprint_onoff(mp_config & MPConfig_Active); - if (mp_config & MPConfig_NoSpaces) - SERIAL_ECHOPGM("NSP"); // [N]o [SP]aces - else - SERIAL_ECHOPGM("ESP"); // [E]nabled [SP]aces + SERIAL_CHAR(' '); + + if (mp_config & MPConfig_NoSpaces) + SERIAL_ECHOPGM("NSP"); // [N]o [SP]aces + else + SERIAL_ECHOPGM("ESP"); // [E]nabled [SP]aces - SERIAL_ECHOLNPGM(""); + SERIAL_EOL(); - // Validate config vars - MeatPackLookupTbl[MeatPack_SpaceCharIdx] = ((mp_config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '); + // Validate config vars + MeatPackLookupTbl[MeatPack_SpaceCharIdx] = ((mp_config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '); } //========================================================================== -void FORCE_INLINE mp_handle_cmd(const MeatPack_Command c) { - switch (c) { - case MPCommand_EnablePacking: { - mp_config |= MPConfig_Active; -#ifdef MP_DEBUG - SERIAL_ECHOLNPGM("[MPDBG] ENABL REC"); -#endif - } break; - case MPCommand_DisablePacking: { - mp_config &= ~(MPConfig_Active); -#ifdef MP_DEBUG - SERIAL_ECHOLNPGM("[MPDBG] DISBL REC"); -#endif - } break; - case MPCommand_TogglePacking: { - mp_config ^= MPConfig_Active; -#ifdef MP_DEBUG - SERIAL_ECHOLNPGM("[MPDBG] TGL REC"); -#endif - } break; - case MPCommand_ResetAll: { - mp_reset_state(); -#ifdef MP_DEBUG - SERIAL_ECHOLNPGM("[MPDBG] RESET REC"); -#endif - } break; - case MPCommand_EnableNoSpaces: { - mp_config |= MPConfig_NoSpaces; -#ifdef MP_DEBUG - SERIAL_ECHOLNPGM("[MPDBG] ENABL NSP"); -#endif - } break; - case MPCommand_DisableNoSpaces: { - mp_config &= ~(MPConfig_NoSpaces); -#ifdef MP_DEBUG - SERIAL_ECHOLNPGM("[MPDBG] DISBL NSP"); -#endif - } break; - default: { -#ifdef MP_DEBUG - SERIAL_ECHOLN("[MPDBG] UNK CMD REC"); -#endif - } - case MPCommand_QueryConfig: - break; - } - mp_echo_config_state(); +void FORCE_INLINE mp_handle_cmd(const MeatPack_Command c) { + switch (c) { + case MPCommand_EnablePacking: + mp_config |= MPConfig_Active; + DEBUG_ECHOLNPGM("[MPDBG] ENABL REC"); + break; + case MPCommand_DisablePacking: + mp_config &= ~(MPConfig_Active); + DEBUG_ECHOLNPGM("[MPDBG] DISBL REC"); + break; + case MPCommand_TogglePacking: + mp_config ^= MPConfig_Active; + DEBUG_ECHOLNPGM("[MPDBG] TGL REC"); + break; + case MPCommand_ResetAll: + mp_reset_state(); + DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); + break; + case MPCommand_EnableNoSpaces: + mp_config |= MPConfig_NoSpaces; + DEBUG_ECHOLNPGM("[MPDBG] ENABL NSP"); + break; + case MPCommand_DisableNoSpaces: + mp_config &= ~(MPConfig_NoSpaces); + DEBUG_ECHOLNPGM("[MPDBG] DISBL NSP"); + break; + default: + DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC"); + + case MPCommand_QueryConfig: break; + } + + mp_echo_config_state(); } //========================================================================== + void mp_handle_rx_char(const uint8_t c) { - // Check for commit complete - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (c == (uint8_t)(MeatPack_CommandByte)) { - if (mp_cmd_count > 0) { - mp_cmd_active = 1; - mp_cmd_count = 0; - } - else - ++mp_cmd_count; - return; + // Check for commit complete + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + if (c == (uint8_t)(MeatPack_CommandByte)) { + if (mp_cmd_count > 0) { + mp_cmd_active = 1; + mp_cmd_count = 0; } + else + ++mp_cmd_count; + return; + } - if (mp_cmd_active > 0) { - mp_handle_cmd((MeatPack_Command)c); - mp_cmd_active = 0; - return; - } + if (mp_cmd_active > 0) { + mp_handle_cmd((MeatPack_Command)c); + mp_cmd_active = 0; + return; + } - if (mp_cmd_count > 0) { - mp_handle_rx_char_inner((uint8_t)(MeatPack_CommandByte)); - mp_cmd_count = 0; - } + if (mp_cmd_count > 0) { + mp_handle_rx_char_inner((uint8_t)(MeatPack_CommandByte)); + mp_cmd_count = 0; + } - mp_handle_rx_char_inner(c); + mp_handle_rx_char_inner(c); } //========================================================================== + uint8_t mp_get_result_char(char* const __restrict out) { - if (mp_char_out_count > 0) { - const uint8_t res = mp_char_out_count; - for (register uint8_t i = 0; i < mp_char_out_count; ++i) - out[i] = (char)mp_char_out_buf[i]; - mp_char_out_count = 0; - return res; - } - return 0; + if (mp_char_out_count > 0) { + const uint8_t res = mp_char_out_count; + for (register uint8_t i = 0; i < mp_char_out_count; ++i) + out[i] = (char)mp_char_out_buf[i]; + mp_char_out_count = 0; + return res; + } + return 0; } //============================================================================== -void mp_trigger_cmd(const MeatPack_Command cmd) -{ - mp_handle_cmd(cmd); + +void mp_trigger_cmd(const MeatPack_Command cmd) { + mp_handle_cmd(cmd); } #endif // MEATPACK diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index 2720584aab70..011e972cac39 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -1,65 +1,89 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 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 . + * + */ + /* -* MeatPack G-Code Compression -* -* Algorithm & Implementation: Scott Mudge - mail@scottmudge.com -* Date: Dec. 2020 -* -* Specifically optimized for 3D printing G-Code, this is a zero-cost data compression method -* which packs ~180-190% more data into the same amount of bytes going to the CNC controller. -* As a majority of G-Code can be represented by a restricted alphabet, I performed histogram -* analysis on a wide variety of 3D printing gcode samples, and found ~93% of all gcode could -* be represented by the same 15-character alphabet. -* -* This allowed me to design a system of packing 2 8-bit characters into a single byte, assuming -* they fall within this limited 15-character alphabet. Using a 4-bit lookup table, these 8-bit -* characters can be represented by a 4-bit index. -* -* Combined with some logic to allow commingling of full-width characters outside of this 15- -* character alphabet (at the cost of an extra 8-bits per full-width character), and by stripping -* out unnecessary comments, the end result is gcode which is roughly half the original size. -* -* Why did I do this? I noticed micro-stuttering and other data-bottleneck issues while printing -* objects with high curvature, especially at high speeds. There is also the issue of the limited -* baud rate provided by Prusa's Atmega2560-based boards, over the USB serial connection. So soft- -* ware like OctoPrint would also suffer this same micro-stuttering and poor print quality issue. -* -*/ -//#include -#include "../inc/MarlinConfig.h" + * MeatPack G-Code Compression + * + * Algorithm & Implementation: Scott Mudge - mail@scottmudge.com + * Date: Dec. 2020 + * + * Specifically optimized for 3D printing G-Code, this is a zero-cost data compression method + * which packs ~180-190% more data into the same amount of bytes going to the CNC controller. + * As a majority of G-Code can be represented by a restricted alphabet, I performed histogram + * analysis on a wide variety of 3D printing gcode samples, and found ~93% of all gcode could + * be represented by the same 15-character alphabet. + * + * This allowed me to design a system of packing 2 8-bit characters into a single byte, assuming + * they fall within this limited 15-character alphabet. Using a 4-bit lookup table, these 8-bit + * characters can be represented by a 4-bit index. + * + * Combined with some logic to allow commingling of full-width characters outside of this 15- + * character alphabet (at the cost of an extra 8-bits per full-width character), and by stripping + * out unnecessary comments, the end result is gcode which is roughly half the original size. + * + * Why did I do this? I noticed micro-stuttering and other data-bottleneck issues while printing + * objects with high curvature, especially at high speeds. There is also the issue of the limited + * baud rate provided by Prusa's Atmega2560-based boards, over the USB serial connection. So soft- + * ware like OctoPrint would also suffer this same micro-stuttering and poor print quality issue. + * + */ +#pragma once -#if ENABLED(MEATPACK) +#include -#define MeatPack_SecondNotPacked 0b11110000 -#define MeatPack_FirstNotPacked 0b00001111 +#define MeatPack_SecondNotPacked 0b11110000 +#define MeatPack_FirstNotPacked 0b00001111 -// These are commands sent to MeatPack to control its behavior. -// They are sent by first sending 2x MeatPack_CommandByte (0xFF) in sequence, -// followed by one of the command bytes below. -// Provided that 0xFF is an exceedingly rare character that is virtually never -// present in g-code naturally, it is safe to assume 2 in sequence should never -// happen naturally, and so it is used as a signal here. -// -// 0xFF *IS* used in "packed" g-code (used to denote that the next 2 characters are -// full-width), however 2 in a row will never occur, as the next 2 bytes will always -// some non-0xFF character. +/** + * Commands sent to MeatPack to control its behavior. + * They are sent by first sending 2x MeatPack_CommandByte (0xFF) in sequence, + * followed by one of the command bytes below. + * Provided that 0xFF is an exceedingly rare character that is virtually never + * present in G-code naturally, it is safe to assume 2 in sequence should never + * happen naturally, and so it is used as a signal here. + * + * 0xFF *IS* used in "packed" G-code (used to denote that the next 2 characters are + * full-width), however 2 in a row will never occur, as the next 2 bytes will always + * some non-0xFF character. + */ enum MeatPack_Command { - MPCommand_None = 0U, - MPCommand_TogglePacking = 253U, - MPCommand_EnablePacking = 251U, - MPCommand_DisablePacking = 250U, - MPCommand_ResetAll = 249U, - MPCommand_QueryConfig = 248U, - MPCommand_EnableNoSpaces = 247U, - MPCommand_DisableNoSpaces = 246U + MPCommand_None = 0U, + MPCommand_TogglePacking = 253U, + MPCommand_EnablePacking = 251U, + MPCommand_DisablePacking = 250U, + MPCommand_ResetAll = 249U, + MPCommand_QueryConfig = 248U, + MPCommand_EnableNoSpaces = 247U, + MPCommand_DisableNoSpaces = 246U }; // Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences, // and will control state internally. extern void mp_handle_rx_char(const uint8_t c); -// After passing in rx'd char using above method, call this to get characters out. Can return -// from 0 to 2 characters at once. -// @param out [in] Output pointer for unpacked/processed data. -// @return Number of characters returned. Range from 0 to 2. +/** + * After passing in rx'd char using above method, call this to get characters out. + * Can return from 0 to 2 characters at once. + * @param out [in] Output pointer for unpacked/processed data. + * @return Number of characters returned. Range from 0 to 2. + */ extern uint8_t mp_get_result_char(char* const __restrict out); -#endif // MEATPACK diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index 9df765dc79d1..adfe4335c33a 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -34,9 +34,6 @@ GCodeQueue queue; #include "../module/planner.h" #include "../module/temperature.h" #include "../MarlinCore.h" -#if ENABLED(MEATPACK) - #include "../feature/meatpack.h" -#endif #if ENABLED(PRINTER_EVENT_LEDS) #include "../feature/leds/printer_event_leds.h" @@ -50,6 +47,10 @@ GCodeQueue queue; #include "../feature/binary_stream.h" #endif +#if ENABLED(MEATPACK) + #include "../feature/meatpack.h" +#endif + #if ENABLED(POWER_LOSS_RECOVERY) #include "../feature/powerloss.h" #endif @@ -470,117 +471,112 @@ void GCodeQueue::get_serial_commands() { while (length < BUFSIZE && serial_data_available()) { LOOP_L_N(i, NUM_SERIAL) { - #if ENABLED(MEATPACK) - // MeatPack Changes - const int rec = read_serial(i); - if (rec < 0) continue; + const int c = read_serial(i); + if (c < 0) continue; - mp_handle_rx_char((uint8_t)rec); - char c_res[2] = {0, 0}; + #if ENABLED(MEATPACK) + mp_handle_rx_char(uint8_t(c)); + char c_res[2] = { 0, 0 }; const uint8_t char_count = mp_get_result_char(c_res); - - for (uint8_t char_index = 0; char_index < char_count; ++char_index){ - const int c = c_res[char_index]; #else - const int c = read_serial(i); + constexpr uint8_t char_count = 1; #endif - if (c < 0) continue; - const char serial_char = c; + LOOP_L_N(char_index, char_count) { + const char serial_char = TERN(MEATPACK, c_res[char_index], c); - if (ISEOL(serial_char)) { + if (ISEOL(serial_char)) { - // Reset our state, continue if the line was empty - if (process_line_done(serial_input_state[i], serial_line_buffer[i], serial_count[i])) - continue; + // Reset our state, continue if the line was empty + if (process_line_done(serial_input_state[i], serial_line_buffer[i], serial_count[i])) + continue; - char* command = serial_line_buffer[i]; + char* command = serial_line_buffer[i]; - while (*command == ' ') command++; // Skip leading spaces - char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line + while (*command == ' ') command++; // Skip leading spaces + char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line - if (npos) { + if (npos) { - const bool M110 = !!strstr_P(command, PSTR("M110")); - - if (M110) { - char* n2pos = strchr(command + 4, 'N'); - if (n2pos) npos = n2pos; - } + const bool M110 = !!strstr_P(command, PSTR("M110")); - const long gcode_N = strtol(npos + 1, nullptr, 10); + if (M110) { + char* n2pos = strchr(command + 4, 'N'); + if (n2pos) npos = n2pos; + } - if (gcode_N != last_N[i] + 1 && !M110) - return gcode_line_error(PSTR(STR_ERR_LINE_NO), i); + const long gcode_N = strtol(npos + 1, nullptr, 10); - char *apos = strrchr(command, '*'); - if (apos) { - uint8_t checksum = 0, count = uint8_t(apos - command); - while (count) checksum ^= command[--count]; - if (strtol(apos + 1, nullptr, 10) != checksum) - return gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), i); - } - else - return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); + if (gcode_N != last_N[i] + 1 && !M110) + return gcode_line_error(PSTR(STR_ERR_LINE_NO), i); - last_N[i] = gcode_N; + char *apos = strrchr(command, '*'); + if (apos) { + uint8_t checksum = 0, count = uint8_t(apos - command); + while (count) checksum ^= command[--count]; + if (strtol(apos + 1, nullptr, 10) != checksum) + return gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), i); } - #if ENABLED(SDSUPPORT) - // Pronterface "M29" and "M29 " has no line number - else if (card.flag.saving && !is_M29(command)) - return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); - #endif + else + return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); - // - // Movement commands give an alert when the machine is stopped - // - - if (IsStopped()) { - char* gpos = strchr(command, 'G'); - if (gpos) { - switch (strtol(gpos + 1, nullptr, 10)) { - case 0: case 1: - #if ENABLED(ARC_SUPPORT) - case 2: case 3: - #endif - #if ENABLED(BEZIER_CURVE_SUPPORT) - case 5: - #endif - PORT_REDIRECT(i); // Reply to the serial port that sent the command - SERIAL_ECHOLNPGM(STR_ERR_STOPPED); - LCD_MESSAGEPGM(MSG_STOPPED); - break; - } + last_N[i] = gcode_N; + } + #if ENABLED(SDSUPPORT) + // Pronterface "M29" and "M29 " has no line number + else if (card.flag.saving && !is_M29(command)) + return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), i); + #endif + + // + // Movement commands give an alert when the machine is stopped + // + + if (IsStopped()) { + char* gpos = strchr(command, 'G'); + if (gpos) { + switch (strtol(gpos + 1, nullptr, 10)) { + case 0: case 1: + #if ENABLED(ARC_SUPPORT) + case 2: case 3: + #endif + #if ENABLED(BEZIER_CURVE_SUPPORT) + case 5: + #endif + PORT_REDIRECT(i); // Reply to the serial port that sent the command + SERIAL_ECHOLNPGM(STR_ERR_STOPPED); + LCD_MESSAGEPGM(MSG_STOPPED); + break; } } - - #if DISABLED(EMERGENCY_PARSER) - // Process critical commands early - if (command[0] == 'M') switch (command[3]) { - case '8': if (command[2] == '0' && command[1] == '1') { wait_for_heatup = false; TERN_(HAS_LCD_MENU, wait_for_user = false); } break; - case '2': if (command[2] == '1' && command[1] == '1') kill(M112_KILL_STR, nullptr, true); break; - case '0': if (command[1] == '4' && command[2] == '1') quickstop_stepper(); break; } - #endif - #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0 - last_command_time = ms; - #endif + #if DISABLED(EMERGENCY_PARSER) + // Process critical commands early + if (command[0] == 'M') switch (command[3]) { + case '8': if (command[2] == '0' && command[1] == '1') { wait_for_heatup = false; TERN_(HAS_LCD_MENU, wait_for_user = false); } break; + case '2': if (command[2] == '1' && command[1] == '1') kill(M112_KILL_STR, nullptr, true); break; + case '0': if (command[1] == '4' && command[2] == '1') quickstop_stepper(); break; + } + #endif - // Add the command to the queue - _enqueue(serial_line_buffer[i], true - #if HAS_MULTI_SERIAL - , i - #endif - ); - } - else - process_stream_char(serial_char, serial_input_state[i], serial_line_buffer[i], serial_count[i]); + #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0 + last_command_time = ms; + #endif -#if ENABLED(MEATPACK) + // Add the command to the queue + _enqueue(serial_line_buffer[i], true + #if HAS_MULTI_SERIAL + , i + #endif + ); } -#endif - } // for NUM_SERIAL + else + process_stream_char(serial_char, serial_input_state[i], serial_line_buffer[i], serial_count[i]); + + } // char_count loop + + } // NUM_SERIAL loop } // queue has space, serial has data } diff --git a/buildroot/tests/FYSETC_S6-tests b/buildroot/tests/FYSETC_S6-tests index 18951ebb79b4..c7f7a16bbdbf 100755 --- a/buildroot/tests/FYSETC_S6-tests +++ b/buildroot/tests/FYSETC_S6-tests @@ -9,6 +9,7 @@ set -e # Build examples restore_configs use_example_configs FYSETC/S6 +opt_enable MEATPACK opt_set Y_DRIVER_TYPE TMC2209 opt_set Z_DRIVER_TYPE TMC2130 exec_test $1 $2 "FYSETC S6 Example" "$3" From 6e7d0b43f163bfcd4bfe7cc991680efd03d24877 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 18 Jan 2021 22:41:20 -0600 Subject: [PATCH 03/19] sp --- Marlin/src/feature/meatpack.cpp | 41 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 7270ebc8dbee..a9694e2d9412 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -41,7 +41,7 @@ #include "../core/debug_out.h" // Utility definitions -#define MeatPack_CommandByte 0b11111111 +#define MeatPack_CommandByte 0b11111111 #define MeatPack_NextPackedFirst 0b00000001 #define MeatPack_NextPackedSecond 0b00000010 @@ -83,7 +83,7 @@ #ifdef USE_LOOKUP_TABLE // The 15 most-common characters used in G-code, ~90-95% of all G-code uses these characters // NOT storing this with PROGMEM, given how frequently this table will be accessed. - constexpr uint8_t MeatPackLookupTbl[16] = { + uint8_t MeatPackLookupTbl[16] = { '0', // 0000 '1', // 0001 '2', // 0010 @@ -103,16 +103,17 @@ }; #else - inline uint8_t get_char(register uint8_t in) { - switch (in) { - case 0b0000 ... 0b1001: return '0' + in; - case 0b1010: return '.'; - case 0b1011: return ' '; - case 0b1100: return '\n'; - case 0b1101: return 'G'; - case 0b1110: return 'X'; - } - return 0; + + inline uint8_t get_char(register const uint8_t in) { + switch (in) { + case 0b0000 ... 0b1001: return '0' + in; + case 0b1010: return '.'; + case 0b1011: return ' '; + case 0b1100: return '\n'; + case 0b1101: return 'G'; + case 0b1110: return 'X'; + } + return 0; } #endif @@ -168,11 +169,11 @@ uint8_t FORCE_INLINE mp_unpack_chars(const uint8_t pk, uint8_t* __restrict const // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; - else chars_out[0] = MeatPackLookupTbl[(pk & 0xF)]; // Assign lower char + else chars_out[0] = MeatPackLookupTbl[pk & 0xF]; // Assign lower char // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; - else chars_out[1] = MeatPackLookupTbl[((pk >> 4) & 0xF)]; // Assign upper char + else chars_out[1] = MeatPackLookupTbl[(pk >> 4) & 0xF]; // Assign upper char #else // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. @@ -214,10 +215,10 @@ void FORCE_INLINE mp_handle_rx_char_inner(const uint8_t c) { if (mp_config & MPConfig_Active) { if (mp_full_char_queue > 0) { mp_handle_output_char(c); - if (mp_char_buf > 0) { - mp_handle_output_char(mp_char_buf); - mp_char_buf = 0; - } + if (mp_char_buf > 0) { + mp_handle_output_char(mp_char_buf); + mp_char_buf = 0; + } --mp_full_char_queue; } else { @@ -304,7 +305,7 @@ void mp_handle_rx_char(const uint8_t c) { // Check for commit complete // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (c == (uint8_t)(MeatPack_CommandByte)) { + if (c == uint8_t(MeatPack_CommandByte)) { if (mp_cmd_count > 0) { mp_cmd_active = 1; mp_cmd_count = 0; @@ -321,7 +322,7 @@ void mp_handle_rx_char(const uint8_t c) { } if (mp_cmd_count > 0) { - mp_handle_rx_char_inner((uint8_t)(MeatPack_CommandByte)); + mp_handle_rx_char_inner(uint8_t(MeatPack_CommandByte)); mp_cmd_count = 0; } From 50519d01a319f9ed13244158f798580dc7b6ea49 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 18 Jan 2021 22:43:40 -0600 Subject: [PATCH 04/19] etc. --- Marlin/src/feature/meatpack.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index a9694e2d9412..a629aace51b5 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -33,9 +33,6 @@ #include "meatpack.h" -//#include "language.h" -//#include "Marlin.h" - //#define MP_DEBUG #define DEBUG_OUT ENABLED(MP_DEBUG) #include "../core/debug_out.h" @@ -121,13 +118,13 @@ // State variables // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - enum MeatPack_ConfigStateFlags { - MPConfig_None = 0, - MPConfig_Active = (1 << 0), - MPConfig_NoSpaces = (1 << 1) + MPConfig_None = 0, + MPConfig_Active = _BV(0), + MPConfig_NoSpaces = _BV(1) }; uint8_t mp_config = MPConfig_None; // Configuration state -uint8_t mp_cmd_active = 0; // Is a command is pending +uint8_t mp_cmd_active = 0; // A command is pending uint8_t mp_char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs uint8_t mp_cmd_count = 0; // Counts how many command bytes are received (need 2) uint8_t mp_full_char_queue = 0; // Counts how many full-width characters are to be received From 50292007642f09604fa4ab83c9a9c9ee26bd10ef Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 18 Jan 2021 23:10:50 -0600 Subject: [PATCH 05/19] Wrap as a singleton --- Marlin/src/feature/meatpack.cpp | 207 +++++++++++++------------------- Marlin/src/feature/meatpack.h | 49 ++++++-- Marlin/src/gcode/queue.cpp | 4 +- 3 files changed, 123 insertions(+), 137 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index a629aace51b5..9a01b3657d94 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -33,6 +33,8 @@ #include "meatpack.h" +MeatPack meatpack; + //#define MP_DEBUG #define DEBUG_OUT ENABLED(MP_DEBUG) #include "../core/debug_out.h" @@ -117,36 +119,32 @@ // State variables // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -enum MeatPack_ConfigStateFlags { - MPConfig_None = 0, - MPConfig_Active = _BV(0), - MPConfig_NoSpaces = _BV(1) -}; - -uint8_t mp_config = MPConfig_None; // Configuration state -uint8_t mp_cmd_active = 0; // A command is pending -uint8_t mp_char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs -uint8_t mp_cmd_count = 0; // Counts how many command bytes are received (need 2) -uint8_t mp_full_char_queue = 0; // Counts how many full-width characters are to be received -uint8_t mp_char_out_buf[2]; // Output buffer for caching up to 2 characters -uint8_t mp_char_out_count = 0; // Stores number of characters to be read out. + +uint8_t MeatPack::config = MPConfig_None; // Configuration state +uint8_t MeatPack::cmd_active = 0; // A command is pending +uint8_t MeatPack::char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs +uint8_t MeatPack::cmd_count = 0; // Counts how many command bytes are received (need 2) +uint8_t MeatPack::full_char_queue = 0; // Counts how many full-width characters are to be received +uint8_t MeatPack::char_out_buf[2]; // Output buffer for caching up to 2 characters +uint8_t MeatPack::char_out_count = 0; // Stores number of characters to be read out. // DEBUGGING #ifdef MP_DEBUG - uint32_t mp_chars_decoded = 0; + uint32_t MeatPack::chars_decoded = 0; #endif -void FORCE_INLINE mp_handle_output_char(const uint8_t c) { - mp_char_out_buf[mp_char_out_count++] = c; +void MeatPack::reset_state() { + SERIAL_ECHOLNPGM("MP Reset"); + char_out_count = 0; + cmd_active = MPCommand_None; + config = MPConfig_None; + char_buf = 0; + cmd_count = 0; + cmd_active = 0; + full_char_queue = 0; #ifdef MP_DEBUG - if (mp_chars_decoded < 64) { - ++mp_chars_decoded; - DEBUG_ECHOPGM("Rec Byte: "); - MYSERIAL.print("0x"); - MYSERIAL.print((uint8_t)c, HEX); - DEBUG_EOL(); - } + chars_decoded = 0; #endif } @@ -157,9 +155,7 @@ void FORCE_INLINE mp_handle_output_char(const uint8_t c) { // low = (packed >> 4) & 0xF; // high = (packed & 0xF); -//========================================================================== - -uint8_t FORCE_INLINE mp_unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) { +uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) { register uint8_t out = 0; #ifdef USE_LOOKUP_TABLE @@ -186,69 +182,62 @@ uint8_t FORCE_INLINE mp_unpack_chars(const uint8_t pk, uint8_t* __restrict const return out; } -//============================================================================== - -void FORCE_INLINE mp_reset_state() { - SERIAL_ECHOLNPGM("MP Reset"); - mp_char_out_count = 0; - mp_cmd_active = MPCommand_None; - mp_config = MPConfig_None; - mp_char_buf = 0; - mp_cmd_count = 0; - mp_cmd_active = 0; - mp_full_char_queue = 0; +void MeatPack::handle_output_char(const uint8_t c) { + char_out_buf[char_out_count++] = c; #ifdef MP_DEBUG - mp_chars_decoded = 0; + if (chars_decoded < 64) { + ++chars_decoded; + DEBUG_ECHOPGM("Rec Byte: "); + MYSERIAL.print("0x"); + MYSERIAL.print((uint8_t)c, HEX); + DEBUG_EOL(); + } #endif } -//========================================================================== - -void FORCE_INLINE mp_handle_rx_char_inner(const uint8_t c) { +void MeatPack::handle_rx_char_inner(const uint8_t c) { // Packing enabled, handle character and re-arrange them appropriately. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (mp_config & MPConfig_Active) { - if (mp_full_char_queue > 0) { - mp_handle_output_char(c); - if (mp_char_buf > 0) { - mp_handle_output_char(mp_char_buf); - mp_char_buf = 0; + if (config & MPConfig_Active) { + if (full_char_queue > 0) { + handle_output_char(c); + if (char_buf > 0) { + handle_output_char(char_buf); + char_buf = 0; } - --mp_full_char_queue; + --full_char_queue; } else { - uint8_t buf[2] = { 0,0 }; - register const uint8_t res = mp_unpack_chars(c, buf); + uint8_t buf[2] = { 0, 0 }; + register const uint8_t res = unpack_chars(c, buf); if (res & MeatPack_NextPackedFirst) { - ++mp_full_char_queue; - if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; - else mp_char_buf = buf[1]; + ++full_char_queue; + if (res & MeatPack_NextPackedSecond) ++full_char_queue; + else char_buf = buf[1]; } else { - mp_handle_output_char(buf[0]); - if (res & MeatPack_NextPackedSecond) ++mp_full_char_queue; - else mp_handle_output_char(buf[1]); + handle_output_char(buf[0]); + if (res & MeatPack_NextPackedSecond) ++full_char_queue; + else handle_output_char(buf[1]); } } } else // Packing not enabled, just copy character to output - mp_handle_output_char(c); + handle_output_char(c); } -//========================================================================== - -void FORCE_INLINE mp_echo_config_state() { +void MeatPack::echo_config_state() { SERIAL_ECHOPGM("[MP] "); // Echo current state - serialprint_onoff(mp_config & MPConfig_Active); + serialprint_onoff(config & MPConfig_Active); SERIAL_CHAR(' '); - if (mp_config & MPConfig_NoSpaces) + if (config & MPConfig_NoSpaces) SERIAL_ECHOPGM("NSP"); // [N]o [SP]aces else SERIAL_ECHOPGM("ESP"); // [E]nabled [SP]aces @@ -256,93 +245,61 @@ void FORCE_INLINE mp_echo_config_state() { SERIAL_EOL(); // Validate config vars - MeatPackLookupTbl[MeatPack_SpaceCharIdx] = ((mp_config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '); + MeatPackLookupTbl[MeatPack_SpaceCharIdx] = ((config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '); } -//========================================================================== - -void FORCE_INLINE mp_handle_cmd(const MeatPack_Command c) { +void MeatPack::handle_cmd(const MeatPack_Command c) { switch (c) { - case MPCommand_EnablePacking: - mp_config |= MPConfig_Active; - DEBUG_ECHOLNPGM("[MPDBG] ENABL REC"); - break; - case MPCommand_DisablePacking: - mp_config &= ~(MPConfig_Active); - DEBUG_ECHOLNPGM("[MPDBG] DISBL REC"); - break; - case MPCommand_TogglePacking: - mp_config ^= MPConfig_Active; - DEBUG_ECHOLNPGM("[MPDBG] TGL REC"); - break; - case MPCommand_ResetAll: - mp_reset_state(); - DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); - break; - case MPCommand_EnableNoSpaces: - mp_config |= MPConfig_NoSpaces; - DEBUG_ECHOLNPGM("[MPDBG] ENABL NSP"); - break; - case MPCommand_DisableNoSpaces: - mp_config &= ~(MPConfig_NoSpaces); - DEBUG_ECHOLNPGM("[MPDBG] DISBL NSP"); - break; - default: - DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC"); - + case MPCommand_EnablePacking: config |= MPConfig_Active; DEBUG_ECHOLNPGM("[MPDBG] ENABL REC"); break; + case MPCommand_DisablePacking: config &= ~(MPConfig_Active); DEBUG_ECHOLNPGM("[MPDBG] DISBL REC"); break; + case MPCommand_TogglePacking: config ^= MPConfig_Active; DEBUG_ECHOLNPGM("[MPDBG] TGL REC"); break; + case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break; + case MPCommand_EnableNoSpaces: config |= MPConfig_NoSpaces; DEBUG_ECHOLNPGM("[MPDBG] ENABL NSP"); break; + case MPCommand_DisableNoSpaces: config &= ~(MPConfig_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] DISBL NSP"); break; + default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC"); case MPCommand_QueryConfig: break; } - - mp_echo_config_state(); + echo_config_state(); } -//========================================================================== - -void mp_handle_rx_char(const uint8_t c) { - +void MeatPack::handle_rx_char(const uint8_t c) { // Check for commit complete // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (c == uint8_t(MeatPack_CommandByte)) { - if (mp_cmd_count > 0) { - mp_cmd_active = 1; - mp_cmd_count = 0; + if (cmd_count > 0) { + cmd_active = 1; + cmd_count = 0; } else - ++mp_cmd_count; + ++cmd_count; return; } - if (mp_cmd_active > 0) { - mp_handle_cmd((MeatPack_Command)c); - mp_cmd_active = 0; + if (cmd_active > 0) { + handle_cmd((MeatPack_Command)c); + cmd_active = 0; return; } - if (mp_cmd_count > 0) { - mp_handle_rx_char_inner(uint8_t(MeatPack_CommandByte)); - mp_cmd_count = 0; + if (cmd_count > 0) { + handle_rx_char_inner(uint8_t(MeatPack_CommandByte)); + cmd_count = 0; } - mp_handle_rx_char_inner(c); + handle_rx_char_inner(c); } -//========================================================================== - -uint8_t mp_get_result_char(char* const __restrict out) { - if (mp_char_out_count > 0) { - const uint8_t res = mp_char_out_count; - for (register uint8_t i = 0; i < mp_char_out_count; ++i) - out[i] = (char)mp_char_out_buf[i]; - mp_char_out_count = 0; - return res; +uint8_t MeatPack::get_result_char(char* const __restrict out) { + uint8_t res = 0; + if (char_out_count) { + res = char_out_count; + char_out_count = 0; + for (register uint8_t i = 0; i < res; ++i) + out[i] = (char)char_out_buf[i]; } - return 0; + return res; } -//============================================================================== - -void mp_trigger_cmd(const MeatPack_Command cmd) { - mp_handle_cmd(cmd); -} +void MeatPack::trigger_cmd(const MeatPack_Command cmd) { handle_cmd(cmd); } #endif // MEATPACK diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index 011e972cac39..93215dab0642 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -76,14 +76,43 @@ enum MeatPack_Command { MPCommand_DisableNoSpaces = 246U }; -// Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences, -// and will control state internally. -extern void mp_handle_rx_char(const uint8_t c); +enum MeatPack_ConfigStateFlags : uint8_t { + MPConfig_None = 0, + MPConfig_Active = _BV(0), + MPConfig_NoSpaces = _BV(1) +}; -/** - * After passing in rx'd char using above method, call this to get characters out. - * Can return from 0 to 2 characters at once. - * @param out [in] Output pointer for unpacked/processed data. - * @return Number of characters returned. Range from 0 to 2. - */ -extern uint8_t mp_get_result_char(char* const __restrict out); +class MeatPack { +protected: + static uint8_t config; // Configuration state + static uint8_t cmd_active; // A command is pending + static uint8_t char_buf; // Buffers a character if dealing with out-of-sequence pairs + static uint8_t cmd_count; // Counts how many command bytes are received (need 2) + static uint8_t full_char_queue; // Counts how many full-width characters are to be received + static uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters + static uint8_t char_out_count; // Stores number of characters to be read out. + +public: + // Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences, + // and will control state internally. + static void handle_rx_char(const uint8_t c); + + /** + * After passing in rx'd char using above method, call this to get characters out. + * Can return from 0 to 2 characters at once. + * @param out [in] Output pointer for unpacked/processed data. + * @return Number of characters returned. Range from 0 to 2. + */ + static uint8_t get_result_char(char* const __restrict out); + +private: + static void reset_state(); + static void echo_config_state(); + static uint8_t unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out); + static void handle_cmd(const MeatPack_Command c); + static void trigger_cmd(const MeatPack_Command cmd); + static void handle_output_char(const uint8_t c); + static void handle_rx_char_inner(const uint8_t c); +}; + +extern MeatPack meatpack; diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index adfe4335c33a..4e7f30733f4d 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -475,9 +475,9 @@ void GCodeQueue::get_serial_commands() { if (c < 0) continue; #if ENABLED(MEATPACK) - mp_handle_rx_char(uint8_t(c)); + meatpack.handle_rx_char(uint8_t(c)); char c_res[2] = { 0, 0 }; - const uint8_t char_count = mp_get_result_char(c_res); + const uint8_t char_count = meatpack.get_result_char(c_res); #else constexpr uint8_t char_count = 1; #endif From e614ed3de009e4c9e41875a5d4e104a6e07da9b6 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 18 Jan 2021 23:47:31 -0600 Subject: [PATCH 06/19] use a bool --- Marlin/src/feature/meatpack.cpp | 62 +++++++++++++++------------------ Marlin/src/feature/meatpack.h | 6 ++-- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 9a01b3657d94..a8045a33ec7e 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -120,32 +120,28 @@ MeatPack meatpack; // State variables // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool MeatPack::cmd_is_next = false; // A command is pending uint8_t MeatPack::config = MPConfig_None; // Configuration state -uint8_t MeatPack::cmd_active = 0; // A command is pending uint8_t MeatPack::char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs uint8_t MeatPack::cmd_count = 0; // Counts how many command bytes are received (need 2) -uint8_t MeatPack::full_char_queue = 0; // Counts how many full-width characters are to be received +uint8_t MeatPack::full_char_count = 0; // Counts how many full-width characters are to be received uint8_t MeatPack::char_out_buf[2]; // Output buffer for caching up to 2 characters uint8_t MeatPack::char_out_count = 0; // Stores number of characters to be read out. // DEBUGGING -#ifdef MP_DEBUG - uint32_t MeatPack::chars_decoded = 0; -#endif +TERN_(MP_DEBUG, uint32_t chars_decoded = 0); void MeatPack::reset_state() { SERIAL_ECHOLNPGM("MP Reset"); - char_out_count = 0; - cmd_active = MPCommand_None; + + cmd_is_next = false; config = MPConfig_None; char_buf = 0; cmd_count = 0; - cmd_active = 0; - full_char_queue = 0; + full_char_count = 0; + char_out_count = 0; - #ifdef MP_DEBUG - chars_decoded = 0; - #endif + TERN_(MP_DEBUG, chars_decoded = 0); } // Storing @@ -160,20 +156,20 @@ uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars #ifdef USE_LOOKUP_TABLE - // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. + // If lower nybble is 1111, the higher nybble is unused, and next char is full. if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; else chars_out[0] = MeatPackLookupTbl[pk & 0xF]; // Assign lower char - // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. + // Check if upper nybble is 1111... if so, we don't need the second char. if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; else chars_out[1] = MeatPackLookupTbl[(pk >> 4) & 0xF]; // Assign upper char #else - // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. + // If lower nybble is 1111, the higher nybble is unused, and next char is full. if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; else chars_out[0] = get_char(pk & 0xF); // Assign lower char - // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. + // Check if upper nybble is 1111... if so, we don't need the second char. if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; else chars_out[1] = get_char((pk >> 4) & 0xF); // Assign upper char @@ -185,7 +181,7 @@ uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars void MeatPack::handle_output_char(const uint8_t c) { char_out_buf[char_out_count++] = c; - #ifdef MP_DEBUG + #if ENABLED(MP_DEBUG) if (chars_decoded < 64) { ++chars_decoded; DEBUG_ECHOPGM("Rec Byte: "); @@ -201,26 +197,26 @@ void MeatPack::handle_rx_char_inner(const uint8_t c) { // Packing enabled, handle character and re-arrange them appropriately. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (config & MPConfig_Active) { - if (full_char_queue > 0) { + if (full_char_count) { handle_output_char(c); - if (char_buf > 0) { + if (char_buf) { handle_output_char(char_buf); - char_buf = 0; + char_buf = 0; } - --full_char_queue; + --full_char_count; } else { uint8_t buf[2] = { 0, 0 }; register const uint8_t res = unpack_chars(c, buf); if (res & MeatPack_NextPackedFirst) { - ++full_char_queue; - if (res & MeatPack_NextPackedSecond) ++full_char_queue; + ++full_char_count; + if (res & MeatPack_NextPackedSecond) ++full_char_count; else char_buf = buf[1]; } else { handle_output_char(buf[0]); - if (res & MeatPack_NextPackedSecond) ++full_char_queue; + if (res & MeatPack_NextPackedSecond) ++full_char_count; else handle_output_char(buf[1]); } } @@ -245,7 +241,7 @@ void MeatPack::echo_config_state() { SERIAL_EOL(); // Validate config vars - MeatPackLookupTbl[MeatPack_SpaceCharIdx] = ((config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '); + MeatPackLookupTbl[MeatPack_SpaceCharIdx] = (config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; } void MeatPack::handle_cmd(const MeatPack_Command c) { @@ -266,24 +262,24 @@ void MeatPack::handle_rx_char(const uint8_t c) { // Check for commit complete // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (c == uint8_t(MeatPack_CommandByte)) { - if (cmd_count > 0) { - cmd_active = 1; + if (!cmd_count) + ++cmd_count; // cmd_count = 1 + else { cmd_count = 0; + cmd_is_next = true; } - else - ++cmd_count; return; } - if (cmd_active > 0) { + if (cmd_is_next) { + cmd_is_next = false; handle_cmd((MeatPack_Command)c); - cmd_active = 0; return; } - if (cmd_count > 0) { - handle_rx_char_inner(uint8_t(MeatPack_CommandByte)); + if (cmd_count) { cmd_count = 0; + handle_rx_char_inner(uint8_t(MeatPack_CommandByte)); } handle_rx_char_inner(c); diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index 93215dab0642..842f38919025 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -83,12 +83,12 @@ enum MeatPack_ConfigStateFlags : uint8_t { }; class MeatPack { -protected: +private: + static bool cmd_is_next; // A command is pending static uint8_t config; // Configuration state - static uint8_t cmd_active; // A command is pending static uint8_t char_buf; // Buffers a character if dealing with out-of-sequence pairs static uint8_t cmd_count; // Counts how many command bytes are received (need 2) - static uint8_t full_char_queue; // Counts how many full-width characters are to be received + static uint8_t full_char_count; // Counts how many full-width characters are to be received static uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters static uint8_t char_out_count; // Stores number of characters to be read out. From db24247f2619c33511b5cc229584abfc39166d16 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 18 Jan 2021 23:50:23 -0600 Subject: [PATCH 07/19] Move the option --- Marlin/Configuration.h | 3 --- Marlin/Configuration_adv.h | 2 ++ Marlin/src/feature/meatpack.cpp | 2 +- Marlin/src/feature/meatpack.h | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index ab7961d515f4..3f9b6174c2fd 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -123,9 +123,6 @@ */ #define BAUDRATE 250000 -// Enable G-code compression (see https://github.com/scottmudge/OctoPrint-MeatPack) -//#define MEATPACK - // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 0e2545700dc3..36075d3854be 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -3295,6 +3295,8 @@ //#define GCODE_QUOTED_STRINGS // Support for quoted string parameters #endif +//#define MEATPACK // Support for MeatPack G-code compression (https://github.com/scottmudge/OctoPrint-MeatPack) + //#define GCODE_CASE_INSENSITIVE // Accept G-code sent to the firmware in lowercase //#define REPETIER_GCODE_M360 // Add commands originally from Repetier FW diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index a8045a33ec7e..134227f0cc36 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -21,7 +21,7 @@ */ /** - * MeatPack G-Code Compression + * MeatPack G-code Compression * * Algorithm & Implementation: Scott Mudge - mail@scottmudge.com * Date: Dec. 2020 diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index 842f38919025..2828610ac6ba 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -21,7 +21,7 @@ */ /* - * MeatPack G-Code Compression + * MeatPack G-code Compression * * Algorithm & Implementation: Scott Mudge - mail@scottmudge.com * Date: Dec. 2020 From 600be742dc69cd4c14b3b81aba7aa9de0dd9e98f Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 19 Jan 2021 00:01:30 -0600 Subject: [PATCH 08/19] assign first time --- Marlin/src/feature/meatpack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 134227f0cc36..86e54441a497 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -157,7 +157,7 @@ uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars #ifdef USE_LOOKUP_TABLE // If lower nybble is 1111, the higher nybble is unused, and next char is full. - if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; + if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out = MeatPack_NextPackedFirst; else chars_out[0] = MeatPackLookupTbl[pk & 0xF]; // Assign lower char // Check if upper nybble is 1111... if so, we don't need the second char. @@ -166,7 +166,7 @@ uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars #else // If lower nybble is 1111, the higher nybble is unused, and next char is full. - if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out |= MeatPack_NextPackedFirst; + if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out = MeatPack_NextPackedFirst; else chars_out[0] = get_char(pk & 0xF); // Assign lower char // Check if upper nybble is 1111... if so, we don't need the second char. From ea5ee8daade825345a44c81b763ccc6eb164dd4b Mon Sep 17 00:00:00 2001 From: ellensp Date: Tue, 19 Jan 2021 19:10:29 +1300 Subject: [PATCH 09/19] add MEATPACK src filter --- platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio.ini b/platformio.ini index 24209ba1cf4d..4a134b70d384 100644 --- a/platformio.ini +++ b/platformio.ini @@ -93,6 +93,7 @@ default_src_filter = + - - + - - - + - - - - - - @@ -308,6 +309,7 @@ PCA9632 = src_filter=+ PRINTER_EVENT_LEDS = src_filter=+ TEMP_STAT_LEDS = src_filter=+ MAX7219_DEBUG = src_filter=+ + +MEATPACK = src_filter=+ MIXING_EXTRUDER = src_filter=+ + HAS_PRUSA_MMU1 = src_filter=+ HAS_PRUSA_MMU2 = src_filter=+ + From 2e2c6967effb088c24e2633e995d35aa6239a02d Mon Sep 17 00:00:00 2001 From: ellensp Date: Tue, 19 Jan 2021 19:13:09 +1300 Subject: [PATCH 10/19] reorder MEATPACK in default_src_filter --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 4a134b70d384..15ad888436a5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -92,8 +92,8 @@ default_src_filter = + - - + - - - - - - + - - - - - - From d5b557c0ae92f4cc94d7d3bb14cfa9cb52cff9f2 Mon Sep 17 00:00:00 2001 From: ellensp Date: Tue, 19 Jan 2021 19:35:44 +1300 Subject: [PATCH 11/19] add sanity check to stop MEATPACK and BINARY_FILE_TRANSFER --- Marlin/src/inc/SanityCheck.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 8da31336e83f..f54664d0aae1 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -3291,6 +3291,14 @@ static_assert( _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2) #endif #endif + +/** + * Sanity Check for MEATPACK and BINARY_FILE_TRANSFER Features + */ +#if BOTH(MEATPACK, BINARY_FILE_TRANSFER) + #error "Either enable MEATPACK or enable BINARY_FILE_TRANSFER." +#endif + /** * Sanity check for valid stepper driver types */ From 5af112a8667397aef1cdd8c5cf54a888d6b57e51 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Fri, 22 Jan 2021 17:04:04 -0600 Subject: [PATCH 12/19] Update meatpack.h --- Marlin/src/feature/meatpack.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index 2828610ac6ba..1ab3e9725ce1 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -65,15 +65,15 @@ * full-width), however 2 in a row will never occur, as the next 2 bytes will always * some non-0xFF character. */ -enum MeatPack_Command { - MPCommand_None = 0U, - MPCommand_TogglePacking = 253U, - MPCommand_EnablePacking = 251U, - MPCommand_DisablePacking = 250U, - MPCommand_ResetAll = 249U, - MPCommand_QueryConfig = 248U, - MPCommand_EnableNoSpaces = 247U, - MPCommand_DisableNoSpaces = 246U +enum MeatPack_Command : uint8_t { + MPCommand_None = 0, + MPCommand_TogglePacking = 0xFD, + MPCommand_EnablePacking = 0xFB, + MPCommand_DisablePacking = 0xFA, + MPCommand_ResetAll = 0xF9, + MPCommand_QueryConfig = 0xF8, + MPCommand_EnableNoSpaces = 0xF7, + MPCommand_DisableNoSpaces = 0xF6 }; enum MeatPack_ConfigStateFlags : uint8_t { From 5ecedb629482fed483ff13cd994a7ec31bb327bb Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Fri, 22 Jan 2021 17:41:25 -0600 Subject: [PATCH 13/19] tweaks --- Marlin/src/feature/meatpack.cpp | 28 ++++++++++++++-------------- Marlin/src/feature/meatpack.h | 7 +++---- platformio.ini | 4 ++-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 86e54441a497..08280cdc59a9 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -121,7 +121,7 @@ MeatPack meatpack; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool MeatPack::cmd_is_next = false; // A command is pending -uint8_t MeatPack::config = MPConfig_None; // Configuration state +uint8_t MeatPack::config = 0; // Configuration state OFF uint8_t MeatPack::char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs uint8_t MeatPack::cmd_count = 0; // Counts how many command bytes are received (need 2) uint8_t MeatPack::full_char_count = 0; // Counts how many full-width characters are to be received @@ -135,7 +135,7 @@ void MeatPack::reset_state() { SERIAL_ECHOLNPGM("MP Reset"); cmd_is_next = false; - config = MPConfig_None; + config = 0; char_buf = 0; cmd_count = 0; full_char_count = 0; @@ -154,7 +154,7 @@ void MeatPack::reset_state() { uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) { register uint8_t out = 0; - #ifdef USE_LOOKUP_TABLE + #if ENABLED(USE_LOOKUP_TABLE) // If lower nybble is 1111, the higher nybble is unused, and next char is full. if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out = MeatPack_NextPackedFirst; @@ -196,7 +196,7 @@ void MeatPack::handle_rx_char_inner(const uint8_t c) { // Packing enabled, handle character and re-arrange them appropriately. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (config & MPConfig_Active) { + if (TEST(config, MPConfig_Bit_Active)) { if (full_char_count) { handle_output_char(c); if (char_buf) { @@ -229,11 +229,11 @@ void MeatPack::echo_config_state() { SERIAL_ECHOPGM("[MP] "); // Echo current state - serialprint_onoff(config & MPConfig_Active); + serialprint_onoff(TEST(config, MPConfig_Bit_Active)); SERIAL_CHAR(' '); - if (config & MPConfig_NoSpaces) + if (TEST(config, MPConfig_Bit_NoSpaces)) SERIAL_ECHOPGM("NSP"); // [N]o [SP]aces else SERIAL_ECHOPGM("ESP"); // [E]nabled [SP]aces @@ -241,18 +241,18 @@ void MeatPack::echo_config_state() { SERIAL_EOL(); // Validate config vars - MeatPackLookupTbl[MeatPack_SpaceCharIdx] = (config & MPConfig_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; + MeatPackLookupTbl[MeatPack_SpaceCharIdx] = TEST(config, MPConfig_Bit_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; } void MeatPack::handle_cmd(const MeatPack_Command c) { switch (c) { - case MPCommand_EnablePacking: config |= MPConfig_Active; DEBUG_ECHOLNPGM("[MPDBG] ENABL REC"); break; - case MPCommand_DisablePacking: config &= ~(MPConfig_Active); DEBUG_ECHOLNPGM("[MPDBG] DISBL REC"); break; - case MPCommand_TogglePacking: config ^= MPConfig_Active; DEBUG_ECHOLNPGM("[MPDBG] TGL REC"); break; - case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break; - case MPCommand_EnableNoSpaces: config |= MPConfig_NoSpaces; DEBUG_ECHOLNPGM("[MPDBG] ENABL NSP"); break; - case MPCommand_DisableNoSpaces: config &= ~(MPConfig_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] DISBL NSP"); break; - default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC"); + case MPCommand_EnablePacking: SBI(config, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] ENA REC"); break; + case MPCommand_DisablePacking: CBI(config, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] DIS REC"); break; + case MPCommand_TogglePacking: TBI(config, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] TGL REC"); break; + case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break; + case MPCommand_EnableNoSpaces: SBI(config, MPConfig_Bit_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] ENA NSP"); break; + case MPCommand_DisableNoSpaces: CBI(config, MPConfig_Bit_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] DIS NSP"); break; + default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC"); case MPCommand_QueryConfig: break; } echo_config_state(); diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index 1ab3e9725ce1..ffbab5e850c9 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -76,10 +76,9 @@ enum MeatPack_Command : uint8_t { MPCommand_DisableNoSpaces = 0xF6 }; -enum MeatPack_ConfigStateFlags : uint8_t { - MPConfig_None = 0, - MPConfig_Active = _BV(0), - MPConfig_NoSpaces = _BV(1) +enum MeatPack_ConfigStateBits : uint8_t { + MPConfig_Bit_Active = 0, + MPConfig_Bit_NoSpaces = 1 }; class MeatPack { diff --git a/platformio.ini b/platformio.ini index 30dc2d062831..8523e5a712b1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -406,7 +406,7 @@ BEZIER_CURVE_SUPPORT = src_filter=+ + HAS_BED_PROBE = src_filter=+ + + + IS_SCARA = src_filter=+ -HAS_SERVOS = src_filter=+ + +HAS_SERVOS = src_filter=+ + MORGAN_SCARA = src_filter=+ HAS_MICROSTEPS = src_filter=+ (ESP3D_)?WIFISUPPORT = AsyncTCP, ESP Async WebServer @@ -708,7 +708,7 @@ extra_scripts = ${common.extra_scripts} src_filter = ${common.default_src_filter} + + lib_deps = ${common.lib_deps} Servo -custom_marlin.USES_LIQUIDCRYSTAL = LiquidCrystal@1.0.0 +custom_marlin.USES_LIQUIDCRYSTAL = LiquidCrystal@1.0.7 custom_marlin.NEOPIXEL_LED = Adafruit NeoPixel=https://github.com/p3p/Adafruit_NeoPixel/archive/1.5.0.zip build_flags = ${common.build_flags} -DU8G_HAL_LINKS -IMarlin/src/HAL/LPC1768/include -IMarlin/src/HAL/LPC1768/u8g # debug options for backtrace From c9f036bb878e31fc3f0ace4a6007f6c6503462bf Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Fri, 22 Jan 2021 17:57:15 -0600 Subject: [PATCH 14/19] lq version --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 8523e5a712b1..11248d76509c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -708,7 +708,7 @@ extra_scripts = ${common.extra_scripts} src_filter = ${common.default_src_filter} + + lib_deps = ${common.lib_deps} Servo -custom_marlin.USES_LIQUIDCRYSTAL = LiquidCrystal@1.0.7 +custom_marlin.USES_LIQUIDCRYSTAL = LiquidCrystal~1.0.7 custom_marlin.NEOPIXEL_LED = Adafruit NeoPixel=https://github.com/p3p/Adafruit_NeoPixel/archive/1.5.0.zip build_flags = ${common.build_flags} -DU8G_HAL_LINKS -IMarlin/src/HAL/LPC1768/include -IMarlin/src/HAL/LPC1768/u8g # debug options for backtrace From b972a6b2fbc2ee089268e817949c2bc8f3a72d91 Mon Sep 17 00:00:00 2001 From: ellensp Date: Sat, 23 Jan 2021 20:30:10 +1300 Subject: [PATCH 15/19] Refactoring for whitespace omission and using 'E' instead of ' ' in lookup table. --- Marlin/src/feature/meatpack.cpp | 54 ++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 08280cdc59a9..76df6124892d 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -47,6 +47,8 @@ MeatPack meatpack; #define MeatPack_SpaceCharIdx 11U #define MeatPack_SpaceCharReplace 'E' +#define MeatPack_ProtocolVersion "PV01" + /* Character Frequencies from ~30 MB of comment-stripped gcode: '1' -> 4451136 @@ -79,6 +81,17 @@ MeatPack meatpack; // Thus, performance is identical. #define USE_LOOKUP_TABLE +// State variables +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +bool MeatPack::cmd_is_next = false; // A command is pending +uint8_t MeatPack::config = 0; // Configuration state OFF +uint8_t MeatPack::char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs +uint8_t MeatPack::cmd_count = 0; // Counts how many command bytes are received (need 2) +uint8_t MeatPack::full_char_count = 0; // Counts how many full-width characters are to be received +uint8_t MeatPack::char_out_buf[2]; // Output buffer for caching up to 2 characters +uint8_t MeatPack::char_out_count = 0; // Stores number of characters to be read out. + #ifdef USE_LOOKUP_TABLE // The 15 most-common characters used in G-code, ~90-95% of all G-code uses these characters // NOT storing this with PROGMEM, given how frequently this table will be accessed. @@ -107,7 +120,7 @@ MeatPack meatpack; switch (in) { case 0b0000 ... 0b1001: return '0' + in; case 0b1010: return '.'; - case 0b1011: return ' '; + case 0b1011: return (config & MPConfig_Bit_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; case 0b1100: return '\n'; case 0b1101: return 'G'; case 0b1110: return 'X'; @@ -117,23 +130,10 @@ MeatPack meatpack; #endif -// State variables -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool MeatPack::cmd_is_next = false; // A command is pending -uint8_t MeatPack::config = 0; // Configuration state OFF -uint8_t MeatPack::char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs -uint8_t MeatPack::cmd_count = 0; // Counts how many command bytes are received (need 2) -uint8_t MeatPack::full_char_count = 0; // Counts how many full-width characters are to be received -uint8_t MeatPack::char_out_buf[2]; // Output buffer for caching up to 2 characters -uint8_t MeatPack::char_out_count = 0; // Stores number of characters to be read out. - // DEBUGGING TERN_(MP_DEBUG, uint32_t chars_decoded = 0); void MeatPack::reset_state() { - SERIAL_ECHOLNPGM("MP Reset"); - cmd_is_next = false; config = 0; char_buf = 0; @@ -142,6 +142,11 @@ void MeatPack::reset_state() { char_out_count = 0; TERN_(MP_DEBUG, chars_decoded = 0); + + #ifdef MP_DEBUG + chars_decoded = 0; + SERIAL_ECHOLNPGM("MP Reset"); + #endif } // Storing @@ -226,22 +231,29 @@ void MeatPack::handle_rx_char_inner(const uint8_t c) { } void MeatPack::echo_config_state() { - SERIAL_ECHOPGM("[MP] "); + SERIAL_ECHOPGM(" [MP] "); // Add space at idx 0 just in case first character is dropped due to timing/sync issues. - // Echo current state - serialprint_onoff(TEST(config, MPConfig_Bit_Active)); + // NOTE: if any configuration vars are added below, the outgoing sync text for host plugin + // should not contain the "PV' substring, as this is used to indicate protocol version + SERIAL_ECHOPGM(MeatPack_ProtocolVersion); - SERIAL_CHAR(' '); + // Echo current state + if (TEST(config, MPConfig_Bit_Active)) + SERIAL_ECHOPGM(" ON"); + else + SERIAL_ECHOPGM(" OFF"); if (TEST(config, MPConfig_Bit_NoSpaces)) - SERIAL_ECHOPGM("NSP"); // [N]o [SP]aces + SERIAL_ECHOPGM(" NSP"); // [N]o [SP]aces else - SERIAL_ECHOPGM("ESP"); // [E]nabled [SP]aces + SERIAL_ECHOPGM(" ESP"); // [E]nabled [SP]aces SERIAL_EOL(); // Validate config vars - MeatPackLookupTbl[MeatPack_SpaceCharIdx] = TEST(config, MPConfig_Bit_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; + #ifdef USE_LOOKUP_TABLE + MeatPackLookupTbl[MeatPack_SpaceCharIdx] = TEST(config, MPConfig_Bit_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; + #endif } void MeatPack::handle_cmd(const MeatPack_Command c) { From 721e693fba34156dc1ff7f21d8139f68df417c03 Mon Sep 17 00:00:00 2001 From: ellensp Date: Sat, 23 Jan 2021 20:39:49 +1300 Subject: [PATCH 16/19] debugging. --- Marlin/src/feature/meatpack.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 76df6124892d..345f68070996 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -187,11 +187,10 @@ void MeatPack::handle_output_char(const uint8_t c) { char_out_buf[char_out_count++] = c; #if ENABLED(MP_DEBUG) - if (chars_decoded < 64) { + if (chars_decoded < 1024) { ++chars_decoded; - DEBUG_ECHOPGM("Rec Byte: "); - MYSERIAL.print("0x"); - MYSERIAL.print((uint8_t)c, HEX); + DEBUG_ECHOPGM("RB: "); + MYSERIAL.print((char)c); DEBUG_EOL(); } #endif @@ -221,8 +220,10 @@ void MeatPack::handle_rx_char_inner(const uint8_t c) { } else { handle_output_char(buf[0]); - if (res & MeatPack_NextPackedSecond) ++full_char_count; - else handle_output_char(buf[1]); + if (buf[0] != '\n') { + if (res & MeatPack_NextPackedSecond) ++full_char_count; + else handle_output_char(buf[1]); + } } } } From cfecf8737ec3531e8e5857833510588ed74ccbdb Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 24 Jan 2021 00:27:41 -0600 Subject: [PATCH 17/19] Clean up and document --- Marlin/src/feature/meatpack.cpp | 351 ++++++++++++++------------------ Marlin/src/feature/meatpack.h | 33 +-- 2 files changed, 168 insertions(+), 216 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 345f68070996..9d3c0ece3283 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -25,6 +25,21 @@ * * Algorithm & Implementation: Scott Mudge - mail@scottmudge.com * Date: Dec. 2020 + * + * Character Frequencies from ~30 MB of comment-stripped gcode: + * '1' -> 4451136 '7' -> 1112908 + * '0' -> 4253577 '\n' -> 1087683 + * ' ' -> 3053297 'G' -> 1075806 + * '.' -> 3035310 'X' -> 975742 + * '2' -> 1523296 'E' -> 965275 + * '8' -> 1366812 'Y' -> 965274 + * '4' -> 1353273 'F' -> 99416 + * '9' -> 1352147 '-' -> 90242 + * '3' -> 1262929 'Z' -> 34109 + * '5' -> 1189871 'M' -> 11879 + * '6' -> 1127900 'S' -> 9910 + * + * When space is omitted the letter 'E' is used in its place */ #include "../inc/MarlinConfig.h" @@ -32,157 +47,129 @@ #if ENABLED(MEATPACK) #include "meatpack.h" - MeatPack meatpack; +#define MeatPack_ProtocolVersion "PV01" +//#define MEATPACK_LOOKUP_TABLE //#define MP_DEBUG + #define DEBUG_OUT ENABLED(MP_DEBUG) #include "../core/debug_out.h" -// Utility definitions -#define MeatPack_CommandByte 0b11111111 -#define MeatPack_NextPackedFirst 0b00000001 -#define MeatPack_NextPackedSecond 0b00000010 - -#define MeatPack_SpaceCharIdx 11U -#define MeatPack_SpaceCharReplace 'E' - -#define MeatPack_ProtocolVersion "PV01" - -/* - Character Frequencies from ~30 MB of comment-stripped gcode: - '1' -> 4451136 - '0' -> 4253577 - ' ' -> 3053297 - '.' -> 3035310 - '2' -> 1523296 - '8' -> 1366812 - '4' -> 1353273 - '9' -> 1352147 - '3' -> 1262929 - '5' -> 1189871 - '6' -> 1127900 - '7' -> 1112908 - '\n' -> 1087683 - 'G' -> 1075806 - 'X' -> 975742 - 'E' -> 965275 - 'Y' -> 965274 - 'F' -> 99416 - '-' -> 90242 - 'Z' -> 34109 - 'M' -> 11879 - 'S' -> 9910 - If spaces are omitted, we add 'E' -*/ - -// Note: -// I've tried both a switch/case method and a lookup table. The disassembly is exactly the same after compilation, byte-to-byte. -// Thus, performance is identical. -#define USE_LOOKUP_TABLE - -// State variables -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +TERN_(MP_DEBUG, uint8_t chars_decoded = 0); // Log the first 64 bytes after each reset bool MeatPack::cmd_is_next = false; // A command is pending -uint8_t MeatPack::config = 0; // Configuration state OFF -uint8_t MeatPack::char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs -uint8_t MeatPack::cmd_count = 0; // Counts how many command bytes are received (need 2) -uint8_t MeatPack::full_char_count = 0; // Counts how many full-width characters are to be received +uint8_t MeatPack::state = 0; // Configuration state OFF +uint8_t MeatPack::second_char = 0; // The unpacked 2nd character from an out-of-sequence packed pair +uint8_t MeatPack::cmd_count = 0, // Counts how many command bytes are received (need 2) + MeatPack::full_char_count = 0, // Counts how many full-width characters are to be received + MeatPack::char_out_count = 0; // Stores number of characters to be read out. uint8_t MeatPack::char_out_buf[2]; // Output buffer for caching up to 2 characters -uint8_t MeatPack::char_out_count = 0; // Stores number of characters to be read out. -#ifdef USE_LOOKUP_TABLE +#if ENABLED(MEATPACK_LOOKUP_TABLE) // The 15 most-common characters used in G-code, ~90-95% of all G-code uses these characters - // NOT storing this with PROGMEM, given how frequently this table will be accessed. - uint8_t MeatPackLookupTbl[16] = { - '0', // 0000 - '1', // 0001 - '2', // 0010 - '3', // 0011 - '4', // 0100 - '5', // 0101 - '6', // 0110 - '7', // 0111 - '8', // 1000 - '9', // 1001 - '.', // 1010 - ' ', // 1011 - '\n',// 1100 - 'G', // 1101 - 'X', // 1110 - '\0' // never used, 0b1111 is used to indicate next 8-bits is a full character + // Stored in SRAM for performance. + static const uint8_t meatPackLookupTable[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '.', ' ', '\n', 'G', 'X', + '\0' // Unused. 0b1111 indicates a literal character }; +#endif -#else +uint8_t MeatPack::unpacked_char(register const uint8_t in) { + #if ENABLED(MEATPACK_LOOKUP_TABLE) - inline uint8_t get_char(register const uint8_t in) { - switch (in) { - case 0b0000 ... 0b1001: return '0' + in; - case 0b1010: return '.'; - case 0b1011: return (config & MPConfig_Bit_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; - case 0b1100: return '\n'; - case 0b1101: return 'G'; - case 0b1110: return 'X'; - } - return 0; - } + return meatPackLookupTable[in]; -#endif + #else -// DEBUGGING -TERN_(MP_DEBUG, uint32_t chars_decoded = 0); + switch (in) { + case 0b0000 ... 0b1001: return '0' + in; + case 0b1010: return '.'; + case 0b1011: return (state & MPConfig_Bit_NoSpaces) ? kSpaceCharReplace : ' '; + case 0b1100: return '\n'; + case 0b1101: return 'G'; + case 0b1110: return 'X'; + } + return 0; + + #endif +} void MeatPack::reset_state() { + state = 0; cmd_is_next = false; - config = 0; - char_buf = 0; - cmd_count = 0; - full_char_count = 0; - char_out_count = 0; - + second_char = 0; + cmd_count = full_char_count = char_out_count = 0; TERN_(MP_DEBUG, chars_decoded = 0); - - #ifdef MP_DEBUG - chars_decoded = 0; - SERIAL_ECHOLNPGM("MP Reset"); - #endif + report_state(); } -// Storing -// packed = ((low & 0xF) << 4) | (high & 0xF); - -// Unpacking -// low = (packed >> 4) & 0xF; -// high = (packed & 0xF); - +/** + * Unpack one or two characters from a packed byte into a buffer. + * Return flags indicating whether any literal bytes follow. + */ uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) { - register uint8_t out = 0; - - #if ENABLED(USE_LOOKUP_TABLE) - - // If lower nybble is 1111, the higher nybble is unused, and next char is full. - if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out = MeatPack_NextPackedFirst; - else chars_out[0] = MeatPackLookupTbl[pk & 0xF]; // Assign lower char - - // Check if upper nybble is 1111... if so, we don't need the second char. - if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; - else chars_out[1] = MeatPackLookupTbl[(pk >> 4) & 0xF]; // Assign upper char - - #else - // If lower nybble is 1111, the higher nybble is unused, and next char is full. - if ((pk & MeatPack_FirstNotPacked) == MeatPack_FirstNotPacked) out = MeatPack_NextPackedFirst; - else chars_out[0] = get_char(pk & 0xF); // Assign lower char - - // Check if upper nybble is 1111... if so, we don't need the second char. - if ((pk & MeatPack_SecondNotPacked) == MeatPack_SecondNotPacked) out |= MeatPack_NextPackedSecond; - else chars_out[1] = get_char((pk >> 4) & 0xF); // Assign upper char + uint8_t out = 0; + + // If lower nybble is 1111, the higher nybble is unused, and next char is full. + if ((pk & kFirstNotPacked) == kFirstNotPacked) + out = kFirstCharIsLiteral; + else { + const uint8_t chr = pk & 0x0F; + chars_out[0] = unpacked_char(chr); // Set the first char + } - #endif + // Check if upper nybble is 1111... if so, we don't need the second char. + if ((pk & kSecondNotPacked) == kSecondNotPacked) + out |= kSecondCharIsLiteral; + else { + const uint8_t chr = (pk >> 4) & 0x0F; + chars_out[1] = unpacked_char(chr); // Set the second char + } return out; } +/** + * Interpret a single (non-command) character + * according to the current MeatPack state. + */ +void MeatPack::handle_rx_char_inner(const uint8_t c) { + if (TEST(state, MPConfig_Bit_Active)) { // Is MeatPack active? + if (!full_char_count) { // No literal characters to fetch? + uint8_t buf[2] = { 0, 0 }; + register const uint8_t res = unpack_chars(c, buf); // Decode the byte into one or two characters. + if (res & kFirstCharIsLiteral) { // The 1st character couldn't be packed. + ++full_char_count; // So the next stream byte is a full character. + if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. Another stream byte is a full character. + else second_char = buf[1]; // Retain the unpacked second character. + } + else { + handle_output_char(buf[0]); // Send the unpacked first character out. + if (buf[0] != '\n') { // After a newline the next char won't be set + if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. The next stream byte is a full character. + else handle_output_char(buf[1]); // Send the unpacked second character out. + } + } + } + else { + handle_output_char(c); // Pass through the character that couldn't be packed... + if (second_char) { + handle_output_char(second_char); // ...and send an unpacked 2nd character, if set. + second_char = 0; + } + --full_char_count; // One literal character was consumed + } + } + else // Packing not enabled, just copy character to output + handle_output_char(c); +} + +/** + * Buffer a single output character which will be picked up in + * GCodeQueue::get_serial_commands via calls to get_result_char + */ void MeatPack::handle_output_char(const uint8_t c) { char_out_buf[char_out_count++] = c; @@ -196,106 +183,66 @@ void MeatPack::handle_output_char(const uint8_t c) { #endif } -void MeatPack::handle_rx_char_inner(const uint8_t c) { - - // Packing enabled, handle character and re-arrange them appropriately. - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (TEST(config, MPConfig_Bit_Active)) { - if (full_char_count) { - handle_output_char(c); - if (char_buf) { - handle_output_char(char_buf); - char_buf = 0; - } - --full_char_count; - } - else { - uint8_t buf[2] = { 0, 0 }; - register const uint8_t res = unpack_chars(c, buf); - - if (res & MeatPack_NextPackedFirst) { - ++full_char_count; - if (res & MeatPack_NextPackedSecond) ++full_char_count; - else char_buf = buf[1]; - } - else { - handle_output_char(buf[0]); - if (buf[0] != '\n') { - if (res & MeatPack_NextPackedSecond) ++full_char_count; - else handle_output_char(buf[1]); - } - } - } +/** + * Process a MeatPack command byte to update the state. + * Report the new state to serial. + */ +void MeatPack::handle_command(const MeatPack_Command c) { + switch (c) { + case MPCommand_EnablePacking: SBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] ENA REC"); break; + case MPCommand_DisablePacking: CBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] DIS REC"); break; + case MPCommand_TogglePacking: TBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] TGL REC"); break; + case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break; + case MPCommand_EnableNoSpaces: SBI(state, MPConfig_Bit_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] ENA NSP"); + TERN_(USE_LOOKUP_TABLE, MeatPackLookupTbl[kSpaceCharIdx] = kSpaceCharReplace); + break; + case MPCommand_DisableNoSpaces: CBI(state, MPConfig_Bit_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] DIS NSP"); + TERN_(USE_LOOKUP_TABLE, MeatPackLookupTbl[kSpaceCharIdx] = ' '); + break; + default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC"); + case MPCommand_QueryConfig: break; } - else // Packing not enabled, just copy character to output - handle_output_char(c); + report_state(); } -void MeatPack::echo_config_state() { - SERIAL_ECHOPGM(" [MP] "); // Add space at idx 0 just in case first character is dropped due to timing/sync issues. - +void MeatPack::report_state() { // NOTE: if any configuration vars are added below, the outgoing sync text for host plugin // should not contain the "PV' substring, as this is used to indicate protocol version + SERIAL_ECHOPGM("[MP] "); SERIAL_ECHOPGM(MeatPack_ProtocolVersion); - - // Echo current state - if (TEST(config, MPConfig_Bit_Active)) - SERIAL_ECHOPGM(" ON"); - else - SERIAL_ECHOPGM(" OFF"); - - if (TEST(config, MPConfig_Bit_NoSpaces)) - SERIAL_ECHOPGM(" NSP"); // [N]o [SP]aces - else - SERIAL_ECHOPGM(" ESP"); // [E]nabled [SP]aces - + serialprint_onoff(TEST(state, MPConfig_Bit_Active)); + SERIAL_CHAR(' '); + serialprintPGM(TEST(state, MPConfig_Bit_NoSpaces) ? PSTR("NSP") : PSTR("ESP")); SERIAL_EOL(); - - // Validate config vars - #ifdef USE_LOOKUP_TABLE - MeatPackLookupTbl[MeatPack_SpaceCharIdx] = TEST(config, MPConfig_Bit_NoSpaces) ? MeatPack_SpaceCharReplace : ' '; - #endif -} - -void MeatPack::handle_cmd(const MeatPack_Command c) { - switch (c) { - case MPCommand_EnablePacking: SBI(config, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] ENA REC"); break; - case MPCommand_DisablePacking: CBI(config, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] DIS REC"); break; - case MPCommand_TogglePacking: TBI(config, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] TGL REC"); break; - case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break; - case MPCommand_EnableNoSpaces: SBI(config, MPConfig_Bit_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] ENA NSP"); break; - case MPCommand_DisableNoSpaces: CBI(config, MPConfig_Bit_NoSpaces); DEBUG_ECHOLNPGM("[MPDBG] DIS NSP"); break; - default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC"); - case MPCommand_QueryConfig: break; - } - echo_config_state(); } +/** + * Interpret a single character received from serial + * according to the current meatpack state. + */ void MeatPack::handle_rx_char(const uint8_t c) { - // Check for commit complete - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (c == uint8_t(MeatPack_CommandByte)) { - if (!cmd_count) - ++cmd_count; // cmd_count = 1 - else { + if (c == kCommandByte) { // A command (0xFF) byte? + if (cmd_count) { // In fact, two in a row? + cmd_is_next = true; // Then a MeatPack command follows cmd_count = 0; - cmd_is_next = true; } + else + ++cmd_count; // cmd_count = 1 // One command byte received so far... return; } - if (cmd_is_next) { + if (cmd_is_next) { // Were two command bytes received? + handle_command((MeatPack_Command)c); // Then the byte is a MeatPack command cmd_is_next = false; - handle_cmd((MeatPack_Command)c); return; } - if (cmd_count) { + if (cmd_count) { // Only a single 0xFF was received + handle_rx_char_inner(kCommandByte); // A single 0xFF is passed on literally so it can be interpreted as kFirstNotPacked|kSecondNotPacked cmd_count = 0; - handle_rx_char_inner(uint8_t(MeatPack_CommandByte)); } - handle_rx_char_inner(c); + handle_rx_char_inner(c); // Other characters are passed on for MeatPack decoding } uint8_t MeatPack::get_result_char(char* const __restrict out) { @@ -309,6 +256,4 @@ uint8_t MeatPack::get_result_char(char* const __restrict out) { return res; } -void MeatPack::trigger_cmd(const MeatPack_Command cmd) { handle_cmd(cmd); } - #endif // MEATPACK diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index ffbab5e850c9..b89f87844f4e 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -50,9 +50,6 @@ #include -#define MeatPack_SecondNotPacked 0b11110000 -#define MeatPack_FirstNotPacked 0b00001111 - /** * Commands sent to MeatPack to control its behavior. * They are sent by first sending 2x MeatPack_CommandByte (0xFF) in sequence, @@ -83,15 +80,26 @@ enum MeatPack_ConfigStateBits : uint8_t { class MeatPack { private: + friend class GCodeQueue; + + // Utility definitions + static const uint8_t kCommandByte = 0b11111111, + kFirstNotPacked = 0b00001111, + kSecondNotPacked = 0b11110000, + kFirstCharIsLiteral = 0b00000001, + kSecondCharIsLiteral = 0b00000010; + + static const uint8_t kSpaceCharIdx = 11; + static const char kSpaceCharReplace = 'E'; + static bool cmd_is_next; // A command is pending - static uint8_t config; // Configuration state - static uint8_t char_buf; // Buffers a character if dealing with out-of-sequence pairs - static uint8_t cmd_count; // Counts how many command bytes are received (need 2) - static uint8_t full_char_count; // Counts how many full-width characters are to be received + static uint8_t state; // Configuration state + static uint8_t second_char; // Buffers a character if dealing with out-of-sequence pairs + static uint8_t cmd_count, // Counter of command bytes received (need 2) + full_char_count, // Counter for full-width characters to be received + char_out_count; // Stores number of characters to be read out. static uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters - static uint8_t char_out_count; // Stores number of characters to be read out. -public: // Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences, // and will control state internally. static void handle_rx_char(const uint8_t c); @@ -104,12 +112,11 @@ class MeatPack { */ static uint8_t get_result_char(char* const __restrict out); -private: static void reset_state(); - static void echo_config_state(); + static void report_state(); + static uint8_t unpacked_char(register const uint8_t in); static uint8_t unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out); - static void handle_cmd(const MeatPack_Command c); - static void trigger_cmd(const MeatPack_Command cmd); + static void handle_command(const MeatPack_Command c); static void handle_output_char(const uint8_t c); static void handle_rx_char_inner(const uint8_t c); }; From b0a98383ec7bf1e2b22b9915a2770f89a0542da4 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 24 Jan 2021 00:31:16 -0600 Subject: [PATCH 18/19] etc. --- Marlin/src/feature/meatpack.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 9d3c0ece3283..0d9c8009bc1e 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -27,19 +27,14 @@ * Date: Dec. 2020 * * Character Frequencies from ~30 MB of comment-stripped gcode: - * '1' -> 4451136 '7' -> 1112908 - * '0' -> 4253577 '\n' -> 1087683 - * ' ' -> 3053297 'G' -> 1075806 - * '.' -> 3035310 'X' -> 975742 - * '2' -> 1523296 'E' -> 965275 - * '8' -> 1366812 'Y' -> 965274 - * '4' -> 1353273 'F' -> 99416 - * '9' -> 1352147 '-' -> 90242 - * '3' -> 1262929 'Z' -> 34109 - * '5' -> 1189871 'M' -> 11879 - * '6' -> 1127900 'S' -> 9910 + * '1' -> 4451136 '4' -> 1353273 '\n' -> 1087683 '-' -> 90242 + * '0' -> 4253577 '9' -> 1352147 'G' -> 1075806 'Z' -> 34109 + * ' ' -> 3053297 '3' -> 1262929 'X' -> 975742 'M' -> 11879 + * '.' -> 3035310 '5' -> 1189871 'E' -> 965275 'S' -> 9910 + * '2' -> 1523296 '6' -> 1127900 'Y' -> 965274 + * '8' -> 1366812 '7' -> 1112908 'F' -> 99416 * - * When space is omitted the letter 'E' is used in its place + * When space is omitted the letter 'E' is used in its place */ #include "../inc/MarlinConfig.h" From f4eae488499c978395deb6588fbaa4a90da4a1ad Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 24 Jan 2021 00:33:09 -0600 Subject: [PATCH 19/19] etc. --- Marlin/src/feature/meatpack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 0d9c8009bc1e..ea14b44c46de 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -51,8 +51,6 @@ MeatPack meatpack; #define DEBUG_OUT ENABLED(MP_DEBUG) #include "../core/debug_out.h" -TERN_(MP_DEBUG, uint8_t chars_decoded = 0); // Log the first 64 bytes after each reset - bool MeatPack::cmd_is_next = false; // A command is pending uint8_t MeatPack::state = 0; // Configuration state OFF uint8_t MeatPack::second_char = 0; // The unpacked 2nd character from an out-of-sequence packed pair @@ -91,6 +89,8 @@ uint8_t MeatPack::unpacked_char(register const uint8_t in) { #endif } +TERN_(MP_DEBUG, uint8_t chars_decoded = 0); // Log the first 64 bytes after each reset + void MeatPack::reset_state() { state = 0; cmd_is_next = false;