diff --git a/Grbl_Esp32/src/Eeprom.cpp b/Grbl_Esp32/src/Eeprom.cpp index 6654289a..d88d9bc2 100644 --- a/Grbl_Esp32/src/Eeprom.cpp +++ b/Grbl_Esp32/src/Eeprom.cpp @@ -24,14 +24,29 @@ void memcpy_to_eeprom_with_checksum(unsigned int destination, const char* source unsigned char checksum = 0; for (; size > 0; size--) { unsigned char data = static_cast(*source++); - checksum = (checksum << 1) | (checksum >> 7); + // Note: This checksum calculation is broken as described below. + checksum = (checksum << 1) || (checksum >> 7); checksum += data; - EEPROM.write(destination++, data); + EEPROM.write(destination++, *(source++)); } EEPROM.write(destination, checksum); EEPROM.commit(); } +int memcpy_from_eeprom_with_old_checksum(char* destination, unsigned int source, unsigned int size) { + unsigned char data, checksum = 0; + for (; size > 0; size--) { + data = EEPROM.read(source++); + // Note: This checksum calculation is broken - the || should be just | - + // thus making the checksum very weak. + // We leave it as-is so we can read old data after a firmware upgrade. + // The new storage format uses the tagged NVS mechanism, avoiding this bug. + checksum = (checksum << 1) || (checksum >> 7); + checksum += data; + *(destination++) = data; + } + return (checksum == EEPROM.read(source)); +} int memcpy_from_eeprom_with_checksum(char* destination, unsigned int source, unsigned int size) { unsigned char data, checksum = 0; for (; size > 0; size--) { diff --git a/Grbl_Esp32/src/Eeprom.h b/Grbl_Esp32/src/Eeprom.h index a9c1d3ab..0a69ec3e 100644 --- a/Grbl_Esp32/src/Eeprom.h +++ b/Grbl_Esp32/src/Eeprom.h @@ -26,3 +26,4 @@ //void eeprom_put_char(unsigned int addr, unsigned char new_value); void memcpy_to_eeprom_with_checksum(unsigned int destination, const char* source, unsigned int size); int memcpy_from_eeprom_with_checksum(char* destination, unsigned int source, unsigned int size); +int memcpy_from_eeprom_with_old_checksum(char* destination, unsigned int source, unsigned int size); diff --git a/Grbl_Esp32/src/Error.cpp b/Grbl_Esp32/src/Error.cpp index 8f232e99..e7fa5de6 100644 --- a/Grbl_Esp32/src/Error.cpp +++ b/Grbl_Esp32/src/Error.cpp @@ -40,6 +40,7 @@ std::map ErrorCodes = { { Error::TravelExceeded, "Max travel exceeded during jog" }, { Error::InvalidJogCommand, "Invalid jog command" }, { Error::SettingDisabledLaser, "Laser mode requires PWM output" }, + { Error::HomingNoCycles, "No Homing/Cycle defined in settings" }, { Error::GcodeUnsupportedCommand, "Unsupported GCode command" }, { Error::GcodeModalGroupViolation, "Gcode modal group violation" }, { Error::GcodeUndefinedFeedRate, "Gcode undefined feed rate" }, diff --git a/Grbl_Esp32/src/Error.h b/Grbl_Esp32/src/Error.h index da7d04d2..cfc2b379 100644 --- a/Grbl_Esp32/src/Error.h +++ b/Grbl_Esp32/src/Error.h @@ -43,6 +43,7 @@ enum class Error : uint8_t { TravelExceeded = 15, InvalidJogCommand = 16, SettingDisabledLaser = 17, + HomingNoCycles = 18, GcodeUnsupportedCommand = 20, GcodeModalGroupViolation = 21, GcodeUndefinedFeedRate = 22, diff --git a/Grbl_Esp32/src/GCode.cpp b/Grbl_Esp32/src/GCode.cpp index 55343fe9..8031c95e 100644 --- a/Grbl_Esp32/src/GCode.cpp +++ b/Grbl_Esp32/src/GCode.cpp @@ -37,28 +37,11 @@ parser_block_t gc_block; #define FAIL(status) return (status); void gc_init() { - // First thing we do here is iterate through the coord systems and read them all, so that - // we get all our coord system errors here, and not while we're busy: - float coord_system[MAX_N_AXIS]; - - // g54 - g59 is 6 coordinate systems, plus 2 for G28 and G30 reference positions - bool reported_error = false; - const int MAX_COORD_SYSTEMS = 8; - for (uint8_t i = 0; i < MAX_COORD_SYSTEMS; ++i) { - if (!(settings_read_coord_data(i, coord_system))) { - if (!reported_error) { - reported_error = true; - report_status_message(Error::SettingReadFail, CLIENT_SERIAL); - } - } - } - // Reset parser state: memset(&gc_state, 0, sizeof(parser_state_t)); // Load default G54 coordinate system. - if (!(settings_read_coord_data(gc_state.modal.coord_select, gc_state.coord_system))) { - report_status_message(Error::SettingReadFail, CLIENT_SERIAL); - } + gc_state.modal.coord_select = CoordIndex::G54; + coords[gc_state.modal.coord_select]->get(gc_state.coord_system); } // Sets g-code parser position in mm. Input in steps. Called by the system abort and hard @@ -149,7 +132,7 @@ Error gc_execute_line(char* line, uint8_t client) { memcpy(&gc_block.modal, &gc_state.modal, sizeof(gc_modal_t)); // Copy current modes AxisCommand axis_command = AxisCommand::None; uint8_t axis_0, axis_1, axis_linear; - uint8_t coord_select = 0; // Tracks G10 P coordinate selection for execution + CoordIndex coord_select = CoordIndex::G54; // Tracks G10 P coordinate selection for execution // Initialize bitflag tracking variables for axis indices compatible operations. uint8_t axis_words = 0; // XYZ tracking uint8_t ijk_words = 0; // IJK tracking @@ -158,6 +141,8 @@ Error gc_execute_line(char* line, uint8_t client) { uint32_t value_words = 0; // Tracks value words. uint8_t gc_parser_flags = GCParserNone; auto n_axis = number_axis->get(); + float coord_data[MAX_N_AXIS]; // Used by WCO-related commands + uint8_t pValue; // Integer value of P word // Determine if the line is a jogging motion or a normal g-code block. if (line[0] == '$') { // NOTE: `$J=` already parsed when passed to this function. @@ -409,15 +394,30 @@ Error gc_execute_line(char* line, uint8_t client) { mg_word_bit = ModalGroup::MG8; break; case 54: - case 55: - case 56: - case 57: - case 58: - case 59: - // NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.) - gc_block.modal.coord_select = int_value - 54; // Shift to array indexing. + gc_block.modal.coord_select = CoordIndex::G54; mg_word_bit = ModalGroup::MG12; break; + case 55: + gc_block.modal.coord_select = CoordIndex::G55; + mg_word_bit = ModalGroup::MG12; + break; + case 56: + gc_block.modal.coord_select = CoordIndex::G56; + mg_word_bit = ModalGroup::MG12; + break; + case 57: + gc_block.modal.coord_select = CoordIndex::G57; + mg_word_bit = ModalGroup::MG12; + break; + case 58: + gc_block.modal.coord_select = CoordIndex::G58; + mg_word_bit = ModalGroup::MG12; + break; + case 59: + gc_block.modal.coord_select = CoordIndex::G59; + mg_word_bit = ModalGroup::MG12; + break; + // NOTE: G59.x are not supported. case 61: if (mantissa != 0) { FAIL(Error::GcodeUnsupportedCommand); // [G61.1 not supported] @@ -879,13 +879,13 @@ Error gc_execute_line(char* line, uint8_t client) { float block_coord_system[MAX_N_AXIS]; memcpy(block_coord_system, gc_state.coord_system, sizeof(gc_state.coord_system)); if (bit_istrue(command_words, bit(ModalGroup::MG12))) { // Check if called in block - if (gc_block.modal.coord_select > N_COORDINATE_SYSTEM) { + // This error probably cannot happen because preceding code sets + // gc_block.modal.coord_select only to specific supported values + if (gc_block.modal.coord_select >= CoordIndex::NWCSystems) { FAIL(Error::GcodeUnsupportedCoordSys); // [Greater than N sys] } if (gc_state.modal.coord_select != gc_block.modal.coord_select) { - if (!(settings_read_coord_data(gc_block.modal.coord_select, block_coord_system))) { - FAIL(Error::SettingReadFail); - } + coords[gc_block.modal.coord_select]->get(block_coord_system); } } // [16. Set path control mode ]: N/A. Only G61. G61.1 and G64 NOT SUPPORTED. @@ -907,10 +907,6 @@ Error gc_execute_line(char* line, uint8_t client) { if (bit_isfalse(value_words, (bit(GCodeWord::P) | bit(GCodeWord::L)))) { FAIL(Error::GcodeValueWordMissing); // [P/L word missing] } - coord_select = trunc(gc_block.values.p); // Convert p value to int. - if (coord_select > N_COORDINATE_SYSTEM) { - FAIL(Error::GcodeUnsupportedCoordSys); // [Greater than N sys] - } if (gc_block.values.l != 20) { if (gc_block.values.l == 2) { if (bit_istrue(value_words, bit(GCodeWord::R))) { @@ -920,18 +916,21 @@ Error gc_execute_line(char* line, uint8_t client) { FAIL(Error::GcodeUnsupportedCommand); // [Unsupported L] } } - bit_false(value_words, (bit(GCodeWord::L) | bit(GCodeWord::P))); - // Determine coordinate system to change and try to load from EEPROM. - if (coord_select > 0) { - coord_select--; // Adjust P1-P6 index to EEPROM coordinate data indexing. + // Select the coordinate system based on the P word + pValue = trunc(gc_block.values.p); // Convert p value to integer + if (pValue > 0) { + // P1 means G54, P2 means G55, etc. + coord_select = static_cast(pValue - 1 + CoordIndex::G54); } else { - coord_select = gc_block.modal.coord_select; // Index P0 as the active coordinate system + // P0 means use currently-selected system + coord_select = gc_block.modal.coord_select; } - // NOTE: Store parameter data in IJK values. By rule, they are not in use with this command. - // FIXME: Instead of IJK, we'd better use: float vector[MAX_N_AXIS]; // [DG] - if (!settings_read_coord_data(coord_select, gc_block.values.ijk)) { - FAIL(Error::SettingReadFail); // [EEPROM read fail] + if (coord_select >= CoordIndex::NWCSystems) { + FAIL(Error::GcodeUnsupportedCoordSys); // [Greater than N sys] } + bit_false(value_words, (bit(GCodeWord::L) | bit(GCodeWord::P))); + coords[coord_select]->get(coord_data); + // Pre-calculate the coordinate data changes. for (idx = 0; idx < n_axis; idx++) { // Axes indices are consistent, so loop may be used. // Update axes defined only in block. Always in machine coordinates. Can change non-active system. @@ -939,13 +938,13 @@ Error gc_execute_line(char* line, uint8_t client) { if (gc_block.values.l == 20) { // L20: Update coordinate system axis at current position (with modifiers) with programmed value // WPos = MPos - WCS - G92 - TLO -> WCS = MPos - G92 - TLO - WPos - gc_block.values.ijk[idx] = gc_state.position[idx] - gc_state.coord_offset[idx] - gc_block.values.xyz[idx]; + coord_data[idx] = gc_state.position[idx] - gc_state.coord_offset[idx] - gc_block.values.xyz[idx]; if (idx == TOOL_LENGTH_OFFSET_AXIS) { - gc_block.values.ijk[idx] -= gc_state.tool_length_offset; + coord_data[idx] -= gc_state.tool_length_offset; } } else { // L2: Update coordinate system axis to programmed value. - gc_block.values.ijk[idx] = gc_block.values.xyz[idx]; + coord_data[idx] = gc_block.values.xyz[idx]; } } // Else, keep current stored value. } @@ -1003,37 +1002,16 @@ Error gc_execute_line(char* line, uint8_t client) { case NonModal::GoHome1: // G30 // [G28/30 Errors]: Cutter compensation is enabled. // Retreive G28/30 go-home position data (in machine coordinates) from EEPROM - // NOTE: Store parameter data in IJK values. By rule, they are not in use with this command. if (gc_block.non_modal_command == NonModal::GoHome0) { -#ifdef USE_KINEMATICS_FOO - // read the data into a temporary array, so we can apply kinematics - float G28_Pos[MAX_N_AXIS]; - if (!settings_read_coord_data(SETTING_INDEX_G28, G28_Pos)) { - FAIL(Error::SettingReadFail); - } - grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "G28 Cartesian: %3.3f %3.3f %3.3f", G28_Pos[0], G28_Pos[1], G28_Pos[2]); - inverse_kinematics(G28_Pos); // change G28_Pos from cartesian to angles - grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "G28 Angle: %3.3f %3.3f %3.3f", G28_Pos[0], G28_Pos[1], G28_Pos[2]); - memcpy(gc_block.values.ijk, G28_Pos, sizeof(G28_Pos)); - memcpy(gc_state.position, G28_Pos, sizeof(G28_Pos)); // update position for next move reference - mc_line_kins(G28_Pos, pl_data , gc_state.position); - -#else - if (!settings_read_coord_data(SETTING_INDEX_G28, gc_block.values.ijk)) { - FAIL(Error::SettingReadFail); - } -#endif - + coords[CoordIndex::G28]->get(coord_data); } else { // == NonModal::GoHome1 - if (!settings_read_coord_data(SETTING_INDEX_G30, gc_block.values.ijk)) { - FAIL(Error::SettingReadFail); - } + coords[CoordIndex::G30]->get(coord_data); } if (axis_words) { // Move only the axes specified in secondary move. for (idx = 0; idx < n_axis; idx++) { if (!(axis_words & bit(idx))) { - gc_block.values.ijk[idx] = gc_state.position[idx]; + coord_data[idx] = gc_state.position[idx]; } } } else { @@ -1470,7 +1448,7 @@ Error gc_execute_line(char* line, uint8_t client) { // [15. Coordinate system selection ]: if (gc_state.modal.coord_select != gc_block.modal.coord_select) { gc_state.modal.coord_select = gc_block.modal.coord_select; - memcpy(gc_state.coord_system, block_coord_system, MAX_N_AXIS * sizeof(float)); + memcpy(gc_state.coord_system, block_coord_system, sizeof(gc_state.coord_system)); system_flag_wco_change(); } // [16. Set path control mode ]: G61.1/G64 NOT SUPPORTED @@ -1481,10 +1459,10 @@ Error gc_execute_line(char* line, uint8_t client) { // [19. Go to predefined position, Set G10, or Set axis offsets ]: switch (gc_block.non_modal_command) { case NonModal::SetCoordinateData: - settings_write_coord_data(coord_select, gc_block.values.ijk); + coords[coord_select]->set(coord_data); // Update system coordinate system if currently active. if (gc_state.modal.coord_select == coord_select) { - memcpy(gc_state.coord_system, gc_block.values.ijk, MAX_N_AXIS * sizeof(float)); + memcpy(gc_state.coord_system, coord_data, sizeof(gc_state.coord_system)); system_flag_wco_change(); } break; @@ -1496,14 +1474,14 @@ Error gc_execute_line(char* line, uint8_t client) { if (axis_command != AxisCommand::None) { mc_line(gc_block.values.xyz, pl_data); // kinematics kinematics not used for homing righ now } - mc_line_kins(gc_block.values.ijk, pl_data, gc_state.position); - memcpy(gc_state.position, gc_block.values.ijk, MAX_N_AXIS * sizeof(float)); + mc_line(coord_data, pl_data); + memcpy(gc_state.position, coord_data, sizeof(gc_state.position)); break; case NonModal::SetHome0: - settings_write_coord_data(SETTING_INDEX_G28, gc_state.position); + coords[CoordIndex::G28]->set(gc_state.position); break; case NonModal::SetHome1: - settings_write_coord_data(SETTING_INDEX_G30, gc_state.position); + coords[CoordIndex::G30]->set(gc_state.position); break; case NonModal::SetCoordinateOffset: memcpy(gc_state.coord_offset, gc_block.values.xyz, sizeof(gc_block.values.xyz)); @@ -1603,7 +1581,7 @@ Error gc_execute_line(char* line, uint8_t client) { gc_state.modal.distance = Distance::Absolute; gc_state.modal.feed_rate = FeedRate::UnitsPerMin; // gc_state.modal.cutter_comp = CutterComp::Disable; // Not supported. - gc_state.modal.coord_select = 0; // G54 + gc_state.modal.coord_select = CoordIndex::G54; gc_state.modal.spindle = SpindleState::Disable; gc_state.modal.coolant = {}; #ifdef ENABLE_PARKING_OVERRIDE_CONTROL @@ -1621,9 +1599,7 @@ Error gc_execute_line(char* line, uint8_t client) { #endif // Execute coordinate change and spindle/coolant stop. if (sys.state != State::CheckMode) { - if (!(settings_read_coord_data(gc_state.modal.coord_select, gc_state.coord_system))) { - FAIL(Error::SettingReadFail); - } + coords[gc_state.modal.coord_select]->get(gc_state.coord_system); system_flag_wco_change(); // Set to refresh immediately just in case something altered. spindle->set_state(SpindleState::Disable, 0); coolant_off(); diff --git a/Grbl_Esp32/src/GCode.h b/Grbl_Esp32/src/GCode.h index d272a096..1f594bfe 100644 --- a/Grbl_Esp32/src/GCode.h +++ b/Grbl_Esp32/src/GCode.h @@ -251,7 +251,7 @@ typedef struct { Plane plane_select; // {G17,G18,G19} // CutterCompensation cutter_comp; // {G40} NOTE: Don't track. Only default supported. ToolLengthOffset tool_length; // {G43.1,G49} - uint8_t coord_select; // {G54,G55,G56,G57,G58,G59} + CoordIndex coord_select; // {G54,G55,G56,G57,G58,G59} // uint8_t control; // {G61} NOTE: Don't track. Only default supported. ProgramFlow program_flow; // {M0,M1,M2,M30} CoolantState coolant; // {M7,M8,M9} @@ -264,7 +264,7 @@ typedef struct { typedef struct { uint8_t e; // M67 float f; // Feed - float ijk[MAX_N_AXIS]; // I,J,K Axis arc offsets + float ijk[3]; // I,J,K Axis arc offsets - only 3 are possible uint8_t l; // G10 or canned cycles parameters int32_t n; // Line number float p; // G10 or dwell parameters diff --git a/Grbl_Esp32/src/Grbl.h b/Grbl_Esp32/src/Grbl.h index ad62d9b3..571aae60 100644 --- a/Grbl_Esp32/src/Grbl.h +++ b/Grbl_Esp32/src/Grbl.h @@ -23,7 +23,7 @@ // Grbl versioning system const char* const GRBL_VERSION = "1.3a"; -const char* const GRBL_VERSION_BUILD = "20200924"; +const char* const GRBL_VERSION_BUILD = "20200929"; //#include #include diff --git a/Grbl_Esp32/src/MotionControl.cpp b/Grbl_Esp32/src/MotionControl.cpp index 5aa0e820..8ee2ad07 100644 --- a/Grbl_Esp32/src/MotionControl.cpp +++ b/Grbl_Esp32/src/MotionControl.cpp @@ -264,7 +264,7 @@ static bool axis_is_squared(uint8_t axis_mask) { // NOTE: There should be no motions in the buffer and Grbl must be in an idle state before // executing the homing cycle. This prevents incorrect buffered plans after homing. void mc_homing_cycle(uint8_t cycle_mask) { - bool no_cycle_defined = true; // this will catch if no homing cycle has been defined + bool no_cycles_defined = true; #ifdef USE_CUSTOM_HOMING if (user_defined_homing()) { return; @@ -317,7 +317,7 @@ void mc_homing_cycle(uint8_t cycle_mask) { for (int cycle = 0; cycle < MAX_N_AXIS; cycle++) { auto homing_mask = homing_cycle[cycle]->get(); if (homing_mask) { // if there are some axes in this cycle - no_cycle_defined = false; + no_cycles_defined = false; if (!axis_is_squared(homing_mask)) { limits_go_home(homing_mask); // Homing cycle 0 } else { @@ -333,8 +333,8 @@ void mc_homing_cycle(uint8_t cycle_mask) { } } } - if (no_cycle_defined) { - + if (no_cycles_defined) { + report_status_message(Error::HomingNoCycles, CLIENT_ALL); } } protocol_execute_realtime(); // Check for reset and set system abort. diff --git a/Grbl_Esp32/src/ProcessSettings.cpp b/Grbl_Esp32/src/ProcessSettings.cpp index 7a052381..ddae1b22 100644 --- a/Grbl_Esp32/src/ProcessSettings.cpp +++ b/Grbl_Esp32/src/ProcessSettings.cpp @@ -56,11 +56,8 @@ void settings_restore(uint8_t restore_flag) { } } if (restore_flag & SETTINGS_RESTORE_PARAMETERS) { - uint8_t idx; - float coord_data[MAX_N_AXIS]; - memset(&coord_data, 0, sizeof(coord_data)); - for (idx = 0; idx <= SETTING_INDEX_NCOORD; idx++) { - settings_write_coord_data(idx, coord_data); + for (auto idx = CoordIndex::Begin; idx < CoordIndex::End; ++idx) { + coords[idx]->setDefault(); } } if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) { @@ -559,8 +556,10 @@ Error system_execute_line(char* line, WebUI::ESPResponseStream* out, WebUI::Auth // non-empty string - [ESPxxx]yyy or $xxx=yyy return do_command_or_setting(key, value, auth_level, out); } + Error system_execute_line(char* line, uint8_t client, WebUI::AuthenticationLevel auth_level) { - return system_execute_line(line, new WebUI::ESPResponseStream(client, true), auth_level); + WebUI::ESPResponseStream stream(client, true); + return system_execute_line(line, &stream, auth_level); } void system_execute_startup(char* line) { diff --git a/Grbl_Esp32/src/Report.cpp b/Grbl_Esp32/src/Report.cpp index e93b578a..e307b05d 100644 --- a/Grbl_Esp32/src/Report.cpp +++ b/Grbl_Esp32/src/Report.cpp @@ -166,23 +166,24 @@ void grbl_notifyf(const char* title, const char* format, ...) { } } +static const int coordStringLen = 20; +static const int axesStringLen = coordStringLen * MAX_N_AXIS; + // formats axis values into a string and returns that string in rpt -// NOTE: rpt should have at least size: 20 * MAX_N_AXIS +// NOTE: rpt should have at least size: axesStringLen static void report_util_axis_values(float* axis_value, char* rpt) { uint8_t idx; - char axisVal[20]; + char axisVal[coordStringLen]; float unit_conv = 1.0; // unit conversion multiplier..default is mm + const char* format = "%4.3f"; // Default - report mm to 3 decimal places rpt[0] = '\0'; if (report_inches->get()) { unit_conv = 1.0 / MM_PER_INCH; + format = "%4.4f"; // Report inches to 4 decimal places } auto n_axis = number_axis->get(); for (idx = 0; idx < n_axis; idx++) { - if (report_inches->get()) { - snprintf(axisVal, 19, "%4.4f", axis_value[idx] * unit_conv); // Report inches to 4 decimals - } else { - snprintf(axisVal, 19, "%4.3f", axis_value[idx] * unit_conv); // Report mm to 3 decimals - } + snprintf(axisVal, coordStringLen-1, format, axis_value[idx] * unit_conv); strcat(rpt, axisVal); if (idx < (number_axis->get() - 1)) { strcat(rpt, ","); @@ -190,6 +191,27 @@ static void report_util_axis_values(float* axis_value, char* rpt) { } } +// This version returns the axis values as a String +static String report_util_axis_values(const float* axis_value) { + String rpt = ""; + uint8_t idx; + char axisVal[coordStringLen]; + float unit_conv = 1.0; // unit conversion multiplier..default is mm + int decimals = 3; // Default - report mm to 3 decimal places + if (report_inches->get()) { + unit_conv = 1.0 / MM_PER_INCH; + decimals = 4; // Report inches to 4 decimal places + } + auto n_axis = number_axis->get(); + for (idx = 0; idx < n_axis; idx++) { + rpt += String(axis_value[idx] * unit_conv, decimals); + if (idx < (number_axis->get() - 1)) { + rpt += ","; + } + } + return rpt; +} + // Handles the primary confirmation protocol response for streaming interfaces and human-feedback. // For every incoming line, this method responds with an 'ok' for a successful command or an // 'error:' to indicate some error event with the line or some critical system error during @@ -284,8 +306,8 @@ void report_grbl_help(uint8_t client) { void report_probe_parameters(uint8_t client) { // Report in terms of machine position. float print_position[MAX_N_AXIS]; - char probe_rpt[(MAX_N_AXIS * 20 + 13 + 6 + 1)]; // the probe report we are building here - char temp[MAX_N_AXIS * 20]; + char probe_rpt[(axesStringLen + 13 + 6 + 1)]; // the probe report we are building here + char temp[axesStringLen]; strcpy(probe_rpt, "[PRB:"); // initialize the string with the first characters // get the machine position and put them into a string and append to the probe report system_convert_array_steps_to_mpos(print_position, sys_probe_position); @@ -299,46 +321,27 @@ void report_probe_parameters(uint8_t client) { // Prints Grbl NGC parameters (coordinate offsets, probing) void report_ngc_parameters(uint8_t client) { - float coord_data[MAX_N_AXIS]; - uint8_t coord_select; - char temp[MAX_N_AXIS * 20]; - char ngc_rpt[((8 + (MAX_N_AXIS * 20)) * SETTING_INDEX_NCOORD + 4 + MAX_N_AXIS * 20 + 8 + 2 * 20)]; - ngc_rpt[0] = '\0'; - for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) { - if (!(settings_read_coord_data(coord_select, coord_data))) { - report_status_message(Error::SettingReadFail, CLIENT_SERIAL); - return; - } - strcat(ngc_rpt, "[G"); - switch (coord_select) { - case 6: - strcat(ngc_rpt, "28"); - break; - case 7: - strcat(ngc_rpt, "30"); - break; - default: - sprintf(temp, "%d", coord_select + 54); - strcat(ngc_rpt, temp); - break; // G54-G59 - } - strcat(ngc_rpt, ":"); - report_util_axis_values(coord_data, temp); - strcat(ngc_rpt, temp); - strcat(ngc_rpt, "]\r\n"); + String ngc_rpt = ""; + + // Print persistent offsets G54 - G59, G28, and G30 + for (auto coord_select = CoordIndex::Begin; coord_select < CoordIndex::End; ++coord_select) { + ngc_rpt += "["; + ngc_rpt += coords[coord_select]->getName(); + ngc_rpt += ":"; + ngc_rpt += report_util_axis_values(coords[coord_select]->get()); + ngc_rpt += "]\r\n"; } - strcat(ngc_rpt, "[G92:"); // Print G92,G92.1 which are not persistent in memory - report_util_axis_values(gc_state.coord_offset, temp); - strcat(ngc_rpt, temp); - strcat(ngc_rpt, "]\r\n"); - strcat(ngc_rpt, "[TLO:"); // Print tool length offset value + ngc_rpt += "[G92:"; // Print non-persistent G92,G92.1 + ngc_rpt += report_util_axis_values(gc_state.coord_offset); + ngc_rpt += "]\r\n"; + ngc_rpt += "[TLO:"; // Print tool length offset + float tlo = gc_state.tool_length_offset; if (report_inches->get()) { - snprintf(temp, 20, "%4.3f]\r\n", gc_state.tool_length_offset * INCH_PER_MM); - } else { - snprintf(temp, 20, "%4.3f]\r\n", gc_state.tool_length_offset); + tlo *= INCH_PER_MM; } - strcat(ngc_rpt, temp); - grbl_send(client, ngc_rpt); + ngc_rpt += String(tlo, 3);; + ngc_rpt += "]\r\n"; + grbl_send(client, ngc_rpt.c_str()); report_probe_parameters(client); } diff --git a/Grbl_Esp32/src/Settings.cpp b/Grbl_Esp32/src/Settings.cpp index 701037f6..f1be2536 100644 --- a/Grbl_Esp32/src/Settings.cpp +++ b/Grbl_Esp32/src/Settings.cpp @@ -653,3 +653,32 @@ Error GrblCommand::action(char* value, WebUI::AuthenticationLevel auth_level, We } return _action((const char*)value, auth_level, out); }; +Coordinates* coords[CoordIndex::End]; + +bool Coordinates::load() { + size_t len; + switch (nvs_get_blob(Setting::_handle, _name, _currentValue, &len)) { + case ESP_OK: + return true; + case ESP_ERR_NVS_INVALID_LENGTH: + // This could happen if the stored value is longer than the buffer. + // That is highly unlikely since we always store MAX_N_AXIS coordinates. + // It would indicate that we have decreased MAX_N_AXIS since the + // value was stored. We don't flag it as an error, but rather + // accept the initial coordinates and ignore the residue. + // We could issue a warning message if we were so inclined. + return true; + case ESP_ERR_NVS_INVALID_NAME: + case ESP_ERR_NVS_INVALID_HANDLE: + default: + return false; + } +}; + +void Coordinates::set(float value[MAX_N_AXIS]) { + memcpy(&_currentValue, value, sizeof(_currentValue)); +#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE + protocol_buffer_synchronize(); +#endif + nvs_set_blob(Setting::_handle, _name, _currentValue, sizeof(_currentValue)); +} diff --git a/Grbl_Esp32/src/Settings.h b/Grbl_Esp32/src/Settings.h index 233e8571..5b91c80e 100644 --- a/Grbl_Esp32/src/Settings.h +++ b/Grbl_Esp32/src/Settings.h @@ -76,7 +76,6 @@ public: class Setting : public Word { private: protected: - static nvs_handle _handle; // group_t _group; axis_t _axis = NO_AXIS; Setting* link; // linked list of setting objects @@ -85,6 +84,7 @@ protected: const char* _keyName; public: + static nvs_handle _handle; static void init(); static Setting* List; Setting* next() { return link; } @@ -206,6 +206,28 @@ public: int32_t get() { return _currentValue; } }; +class Coordinates { +private: + float _currentValue[MAX_N_AXIS]; + const char* _name; +public: + Coordinates(const char* name) : _name(name) {} + + const char* getName() { return _name; } + bool load(); + void setDefault() { + float zeros[MAX_N_AXIS] = { 0.0, }; + set(zeros); + }; + // Copy the value to an array + void get(float* value) { memcpy(value, _currentValue, sizeof(_currentValue)); } + // Return a pointer to the array + const float* get() { return _currentValue; } + void set(float *value); +}; + +extern Coordinates* coords[CoordIndex::End]; + class FloatSetting : public Setting { private: float _defaultValue; diff --git a/Grbl_Esp32/src/SettingsDefinitions.cpp b/Grbl_Esp32/src/SettingsDefinitions.cpp index afe3f280..a0b05895 100644 --- a/Grbl_Esp32/src/SettingsDefinitions.cpp +++ b/Grbl_Esp32/src/SettingsDefinitions.cpp @@ -205,9 +205,38 @@ static const char* makeGrblName(int axisNum, int base) { return strcpy(retval, buf); } +void make_coordinate(CoordIndex index, const char* name) { + float coord_data[MAX_N_AXIS] = { 0.0 }; + auto coord = new Coordinates(name); + coords[index] = coord; + if (!coord->load()) { + grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Propagating %s data to NVS format", coord->getName()); + // If coord->load() returns false it means that no entry + // was present in non-volatile storage. In that case we + // first look for an old-format entry in the EEPROM section. + // If an entry is present some number of float values at + // the beginning of coord_data will be overwritten with + // the EEPROM data, and the rest will remain at 0.0. + // If no old-format entry is present, all will remain 0.0 + // Regardless, we create a new entry with that data. + (void)old_settings_read_coord_data(index, coord_data); + coords[index]->set(coord_data); + } +} void make_settings() { Setting::init(); + // Propagate old coordinate system data to the new format if necessary. + // G54 - G59 work coordinate systems, G28, G30 reference positions, etc + make_coordinate(CoordIndex::G54, "G54"); + make_coordinate(CoordIndex::G55, "G55"); + make_coordinate(CoordIndex::G56, "G56"); + make_coordinate(CoordIndex::G57, "G57"); + make_coordinate(CoordIndex::G58, "G58"); + make_coordinate(CoordIndex::G59, "G59"); + make_coordinate(CoordIndex::G28, "G28"); + make_coordinate(CoordIndex::G30, "G30"); + // number_axis = new IntSetting(EXTENDED, WG, NULL, "NumberAxis", N_AXIS, 0, 6, NULL, true); number_axis = new FakeSetting(N_AXIS); diff --git a/Grbl_Esp32/src/SettingsStorage.cpp b/Grbl_Esp32/src/SettingsStorage.cpp index b45f040c..5c17d8ba 100644 --- a/Grbl_Esp32/src/SettingsStorage.cpp +++ b/Grbl_Esp32/src/SettingsStorage.cpp @@ -25,26 +25,21 @@ #include "Grbl.h" // Read selected coordinate data from EEPROM. Updates pointed coord_data value. -uint8_t settings_read_coord_data(uint8_t coord_select, float* coord_data) { - uint32_t addr = coord_select * (sizeof(float) * MAX_N_AXIS + 1) + EEPROM_ADDR_PARAMETERS; - if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float) * MAX_N_AXIS))) { +// This is now a compatibility routine that is used to propagate coordinate data +// in the old EEPROM format to the new tagged NVS format. +bool old_settings_read_coord_data(uint8_t coord_select, float* coord_data) { + uint32_t addr = coord_select * (sizeof(float) * N_AXIS + 1) + EEPROM_ADDR_PARAMETERS; + if (!(memcpy_from_eeprom_with_old_checksum((char*)coord_data, addr, sizeof(float) * N_AXIS)) && + !(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float) * MAX_N_AXIS))) { // Reset with default zero vector clear_vector_float(coord_data); - settings_write_coord_data(coord_select, coord_data); + // The old code used to rewrite the zeroed data but now that is done + // elsewhere, in a different format. return false; } return true; } -// Method to store coord data parameters into EEPROM -void settings_write_coord_data(uint8_t coord_select, float* coord_data) { -#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE - protocol_buffer_synchronize(); -#endif - uint32_t addr = coord_select * (sizeof(float) * MAX_N_AXIS + 1) + EEPROM_ADDR_PARAMETERS; - memcpy_to_eeprom_with_checksum(addr, (char*)coord_data, sizeof(float) * MAX_N_AXIS); -} - // Method to store build info into EEPROM // NOTE: This function can only be called in IDLE state. void settings_store_build_info(const char* line) { @@ -73,3 +68,9 @@ uint8_t get_step_pin_mask(uint8_t axis_idx) { uint8_t get_direction_pin_mask(uint8_t axis_idx) { return bit(axis_idx); } + +// Allow iteration over CoordIndex values +CoordIndex& operator ++ (CoordIndex& i) { + i = static_cast(static_cast(i) + 1); + return i; +} diff --git a/Grbl_Esp32/src/SettingsStorage.h b/Grbl_Esp32/src/SettingsStorage.h index 02566d15..4df9870a 100644 --- a/Grbl_Esp32/src/SettingsStorage.h +++ b/Grbl_Esp32/src/SettingsStorage.h @@ -48,14 +48,6 @@ const int EEPROM_SIZE = 1024U; const int EEPROM_ADDR_PARAMETERS = 512U; const int EEPROM_ADDR_BUILD_INFO = 942U; -// Define EEPROM address indexing for coordinate parameters -const int N_COORDINATE_SYSTEM = 6; // Number of supported work coordinate systems (from index 1) -const int SETTING_INDEX_NCOORD = N_COORDINATE_SYSTEM + 1; // Total number of system stored (from index 0) -// NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59) -const int SETTING_INDEX_G28 = N_COORDINATE_SYSTEM; // Home position 1 -const int SETTING_INDEX_G30 = N_COORDINATE_SYSTEM + 1; // Home position 2 -// const int SETTING_INDEX_G92 = N_COORDINATE_SYSTEM+2; // Coordinate offset (G92.2,G92.3 not supported) - // Initialize the configuration subsystem (load settings from EEPROM) void settings_init(); void settings_restore(uint8_t restore_flag); @@ -64,14 +56,37 @@ void write_global_settings(); uint8_t settings_read_build_info(char* line); void settings_store_build_info(const char* line); -// Writes selected coordinate data to EEPROM -void settings_write_coord_data(uint8_t coord_select, float* coord_data); - // Reads selected coordinate data from EEPROM -uint8_t settings_read_coord_data(uint8_t coord_select, float* coord_data); +bool old_settings_read_coord_data(uint8_t coord_select, float* coord_data); // Returns the step pin mask according to Grbl's internal axis numbering uint8_t get_step_pin_mask(uint8_t i); // Returns the direction pin mask according to Grbl's internal axis numbering uint8_t get_direction_pin_mask(uint8_t i); + +// Various places in the code access saved coordinate system data +// by a small integer index according to the values below. +enum CoordIndex : uint8_t{ + Begin = 0, + G54 = Begin, + G55, + G56, + G57, + G58, + G59, + // To support 9 work coordinate systems it would be necessary to define + // the following 3 and modify GCode.cpp to support G59.1, G59.2, G59.3 + // G59_1, + // G59_2, + // G59_3, + NWCSystems, + G28 = NWCSystems, + G30, + // G92_2, + // G92_3, + End, +}; +// Allow iteration over CoordIndex values +CoordIndex& operator ++ (CoordIndex& i); +