My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

meatpack.cpp 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * MeatPack G-code Compression
  24. *
  25. * Algorithm & Implementation: Scott Mudge - mail@scottmudge.com
  26. * Date: Dec. 2020
  27. *
  28. * Character Frequencies from ~30 MB of comment-stripped gcode:
  29. * '1' -> 4451136 '4' -> 1353273 '\n' -> 1087683 '-' -> 90242
  30. * '0' -> 4253577 '9' -> 1352147 'G' -> 1075806 'Z' -> 34109
  31. * ' ' -> 3053297 '3' -> 1262929 'X' -> 975742 'M' -> 11879
  32. * '.' -> 3035310 '5' -> 1189871 'E' -> 965275 'S' -> 9910
  33. * '2' -> 1523296 '6' -> 1127900 'Y' -> 965274
  34. * '8' -> 1366812 '7' -> 1112908 'F' -> 99416
  35. *
  36. * When space is omitted the letter 'E' is used in its place
  37. */
  38. #include "../inc/MarlinConfig.h"
  39. #if ENABLED(MEATPACK)
  40. #include "meatpack.h"
  41. MeatPack meatpack;
  42. #define MeatPack_ProtocolVersion "PV01"
  43. //#define MP_DEBUG
  44. #define DEBUG_OUT ENABLED(MP_DEBUG)
  45. #include "../core/debug_out.h"
  46. bool MeatPack::cmd_is_next = false; // A command is pending
  47. uint8_t MeatPack::state = 0; // Configuration state OFF
  48. uint8_t MeatPack::second_char = 0; // The unpacked 2nd character from an out-of-sequence packed pair
  49. uint8_t MeatPack::cmd_count = 0, // Counts how many command bytes are received (need 2)
  50. MeatPack::full_char_count = 0, // Counts how many full-width characters are to be received
  51. MeatPack::char_out_count = 0; // Stores number of characters to be read out.
  52. uint8_t MeatPack::char_out_buf[2]; // Output buffer for caching up to 2 characters
  53. // The 15 most-common characters used in G-code, ~90-95% of all G-code uses these characters
  54. // Stored in SRAM for performance.
  55. uint8_t meatPackLookupTable[16] = {
  56. '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  57. '.', ' ', '\n', 'G', 'X',
  58. '\0' // Unused. 0b1111 indicates a literal character
  59. };
  60. TERN_(MP_DEBUG, uint8_t chars_decoded = 0); // Log the first 64 bytes after each reset
  61. void MeatPack::reset_state() {
  62. state = 0;
  63. cmd_is_next = false;
  64. second_char = 0;
  65. cmd_count = full_char_count = char_out_count = 0;
  66. TERN_(MP_DEBUG, chars_decoded = 0);
  67. }
  68. /**
  69. * Unpack one or two characters from a packed byte into a buffer.
  70. * Return flags indicating whether any literal bytes follow.
  71. */
  72. uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) {
  73. uint8_t out = 0;
  74. // If lower nybble is 1111, the higher nybble is unused, and next char is full.
  75. if ((pk & kFirstNotPacked) == kFirstNotPacked)
  76. out = kFirstCharIsLiteral;
  77. else {
  78. const uint8_t chr = pk & 0x0F;
  79. chars_out[0] = meatPackLookupTable[chr]; // Set the first char
  80. }
  81. // Check if upper nybble is 1111... if so, we don't need the second char.
  82. if ((pk & kSecondNotPacked) == kSecondNotPacked)
  83. out |= kSecondCharIsLiteral;
  84. else {
  85. const uint8_t chr = (pk >> 4) & 0x0F;
  86. chars_out[1] = meatPackLookupTable[chr]; // Set the second char
  87. }
  88. return out;
  89. }
  90. /**
  91. * Interpret a single (non-command) character
  92. * according to the current MeatPack state.
  93. */
  94. void MeatPack::handle_rx_char_inner(const uint8_t c) {
  95. if (TEST(state, MPConfig_Bit_Active)) { // Is MeatPack active?
  96. if (!full_char_count) { // No literal characters to fetch?
  97. uint8_t buf[2] = { 0, 0 };
  98. const uint8_t res = unpack_chars(c, buf); // Decode the byte into one or two characters.
  99. if (res & kFirstCharIsLiteral) { // The 1st character couldn't be packed.
  100. ++full_char_count; // So the next stream byte is a full character.
  101. if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. Another stream byte is a full character.
  102. else second_char = buf[1]; // Retain the unpacked second character.
  103. }
  104. else {
  105. handle_output_char(buf[0]); // Send the unpacked first character out.
  106. if (buf[0] != '\n') { // After a newline the next char won't be set
  107. if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. The next stream byte is a full character.
  108. else handle_output_char(buf[1]); // Send the unpacked second character out.
  109. }
  110. }
  111. }
  112. else {
  113. handle_output_char(c); // Pass through the character that couldn't be packed...
  114. if (second_char) {
  115. handle_output_char(second_char); // ...and send an unpacked 2nd character, if set.
  116. second_char = 0;
  117. }
  118. --full_char_count; // One literal character was consumed
  119. }
  120. }
  121. else // Packing not enabled, just copy character to output
  122. handle_output_char(c);
  123. }
  124. /**
  125. * Buffer a single output character which will be picked up in
  126. * GCodeQueue::get_serial_commands via calls to get_result_char
  127. */
  128. void MeatPack::handle_output_char(const uint8_t c) {
  129. char_out_buf[char_out_count++] = c;
  130. #if ENABLED(MP_DEBUG)
  131. if (chars_decoded < 1024) {
  132. ++chars_decoded;
  133. DEBUG_ECHOLNPAIR("RB: ", AS_CHAR(c));
  134. }
  135. #endif
  136. }
  137. /**
  138. * Process a MeatPack command byte to update the state.
  139. * Report the new state to serial.
  140. */
  141. void MeatPack::handle_command(const MeatPack_Command c) {
  142. switch (c) {
  143. case MPCommand_QueryConfig: break;
  144. case MPCommand_EnablePacking: SBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] ENA REC"); break;
  145. case MPCommand_DisablePacking: CBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] DIS REC"); break;
  146. case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break;
  147. case MPCommand_EnableNoSpaces:
  148. SBI(state, MPConfig_Bit_NoSpaces);
  149. meatPackLookupTable[kSpaceCharIdx] = kSpaceCharReplace; DEBUG_ECHOLNPGM("[MPDBG] ENA NSP"); break;
  150. case MPCommand_DisableNoSpaces:
  151. CBI(state, MPConfig_Bit_NoSpaces);
  152. meatPackLookupTable[kSpaceCharIdx] = ' '; DEBUG_ECHOLNPGM("[MPDBG] DIS NSP"); break;
  153. default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC");
  154. }
  155. report_state();
  156. }
  157. void MeatPack::report_state() {
  158. // NOTE: if any configuration vars are added below, the outgoing sync text for host plugin
  159. // should not contain the "PV' substring, as this is used to indicate protocol version
  160. SERIAL_ECHOPGM("[MP] ");
  161. SERIAL_ECHOPGM(MeatPack_ProtocolVersion " ");
  162. serialprint_onoff(TEST(state, MPConfig_Bit_Active));
  163. serialprintPGM(TEST(state, MPConfig_Bit_NoSpaces) ? PSTR(" NSP\n") : PSTR(" ESP\n"));
  164. }
  165. /**
  166. * Interpret a single character received from serial
  167. * according to the current meatpack state.
  168. */
  169. void MeatPack::handle_rx_char(const uint8_t c, const serial_index_t serial_ind) {
  170. if (c == kCommandByte) { // A command (0xFF) byte?
  171. if (cmd_count) { // In fact, two in a row?
  172. cmd_is_next = true; // Then a MeatPack command follows
  173. cmd_count = 0;
  174. }
  175. else
  176. ++cmd_count; // cmd_count = 1 // One command byte received so far...
  177. return;
  178. }
  179. if (cmd_is_next) { // Were two command bytes received?
  180. PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));
  181. handle_command((MeatPack_Command)c); // Then the byte is a MeatPack command
  182. cmd_is_next = false;
  183. return;
  184. }
  185. if (cmd_count) { // Only a single 0xFF was received
  186. handle_rx_char_inner(kCommandByte); // A single 0xFF is passed on literally so it can be interpreted as kFirstNotPacked|kSecondNotPacked
  187. cmd_count = 0;
  188. }
  189. handle_rx_char_inner(c); // Other characters are passed on for MeatPack decoding
  190. }
  191. uint8_t MeatPack::get_result_char(char* const __restrict out) {
  192. uint8_t res = 0;
  193. if (char_out_count) {
  194. res = char_out_count;
  195. char_out_count = 0;
  196. for (uint8_t i = 0; i < res; ++i)
  197. out[i] = (char)char_out_buf[i];
  198. }
  199. return res;
  200. }
  201. #endif // MEATPACK