Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement M66 Immediate mode #1330

Merged
merged 1 commit into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FluidNC/src/Error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,5 @@ const std::map<Error, const char*> ErrorNames = {
{ Error::FlowControlOutOfMemory, "Flow Control Out of Memory" },
{ Error::FlowControlStackOverflow, "Flow Control Stack Overflow" },
{ Error::ParameterAssignmentFailed, "Parameter Assignment Failed" },
{ Error::GcodeValueWordInvalid, "Gcode invalid word value" },
};
1 change: 1 addition & 0 deletions FluidNC/src/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ enum class Error : uint8_t {
FlowControlOutOfMemory = 178,
FlowControlStackOverflow = 179,
ParameterAssignmentFailed = 180,
GcodeValueWordInvalid = 181,
};

const char* errorString(Error errorNumber);
Expand Down
154 changes: 138 additions & 16 deletions FluidNC/src/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "Protocol.h" // protocol_buffer_synchronize
#include "MotionControl.h" // mc_override_ctrl_update
#include "Machine/UserOutputs.h" // setAnalogPercent
#include "Machine/UserInputs.h" // read digital/analog inputs
#include "Platform.h" // WEAK_LINK
#include "Job.h" // Job::active() and Job::channel()

Expand Down Expand Up @@ -143,6 +144,9 @@ static void gcode_comment_msg(char* comment) {
}
}

static std::optional<WaitOnInputMode> validate_wait_on_input_mode_value(uint8_t);
static Error gc_wait_on_input(bool is_digital, uint8_t input_number, WaitOnInputMode mode, float timeout);

// Edit GCode line in-place, removing whitespace and comments and
// converting to uppercase
void collapseGCode(char* line) {
Expand Down Expand Up @@ -252,16 +256,17 @@ Error gc_execute_line(char* line) {
uint32_t command_words = 0; // Tracks G and M command words. Also used for modal group violations.
uint32_t value_words = 0; // Tracks value words.

bool jogMotion = false;
bool checkMantissa = false;
bool clockwiseArc = false;
bool probeExplicit = false;
bool probeAway = false;
bool probeNoError = false;
bool syncLaser = false;
bool disableLaser = false;
bool laserIsMotion = false;
bool nonmodalG38 = false; // Used for G38.6-9
bool jogMotion = false;
bool checkMantissa = false;
bool clockwiseArc = false;
bool probeExplicit = false;
bool probeAway = false;
bool probeNoError = false;
bool syncLaser = false;
bool disableLaser = false;
bool laserIsMotion = false;
bool nonmodalG38 = false; // Used for G38.6-9
bool isWaitOnInputDigital = false;

auto n_axis = config->_axes->_numberAxis;
float coord_data[MAX_N_AXIS]; // Used by WCO-related commands
Expand Down Expand Up @@ -682,27 +687,31 @@ Error gc_execute_line(char* line) {
break;
case 62:
gc_block.modal.io_control = IoControl::DigitalOnSync;
mg_word_bit = ModalGroup::MM10;
mg_word_bit = ModalGroup::MM5;
break;
case 63:
gc_block.modal.io_control = IoControl::DigitalOffSync;
mg_word_bit = ModalGroup::MM10;
mg_word_bit = ModalGroup::MM5;
break;
case 64:
gc_block.modal.io_control = IoControl::DigitalOnImmediate;
mg_word_bit = ModalGroup::MM10;
mg_word_bit = ModalGroup::MM5;
break;
case 65:
gc_block.modal.io_control = IoControl::DigitalOffImmediate;
mg_word_bit = ModalGroup::MM10;
mg_word_bit = ModalGroup::MM5;
break;
case 66:
gc_block.modal.io_control = IoControl::WaitOnInput;
mg_word_bit = ModalGroup::MM5;
break;
case 67:
gc_block.modal.io_control = IoControl::SetAnalogSync;
mg_word_bit = ModalGroup::MM10;
mg_word_bit = ModalGroup::MM5;
break;
case 68:
gc_block.modal.io_control = IoControl::SetAnalogImmediate;
mg_word_bit = ModalGroup::MM10;
mg_word_bit = ModalGroup::MM5;
break;
default:
FAIL(Error::GcodeUnsupportedCommand); // [Unsupported M command]
Expand Down Expand Up @@ -1002,6 +1011,51 @@ Error gc_execute_line(char* line) {
clear_bitnum(value_words, GCodeWord::E);
clear_bitnum(value_words, GCodeWord::Q);
}
if ((gc_block.modal.io_control == IoControl::WaitOnInput)) {
// M66 P<digital input> L<wait mode type> Q<timeout>
// M66 E<analog input> L<wait mode type> Q<timeout>
// Exactly one of P or E must be present
if (bitnum_is_false(value_words, GCodeWord::P) && bitnum_is_false(value_words, GCodeWord::E)) {
// need at least one of P or E
FAIL(Error::GcodeValueWordMissing);
}
if (bitnum_is_true(value_words, GCodeWord::P) && bitnum_is_true(value_words, GCodeWord::E)) {
// need at most one of P or E
FAIL(Error::GcodeValueWordInvalid);
}
isWaitOnInputDigital = bitnum_is_true(value_words, GCodeWord::P);
clear_bitnum(value_words, GCodeWord::P);
clear_bitnum(value_words, GCodeWord::E);
if (bitnum_is_false(value_words, GCodeWord::L)) {
FAIL(Error::GcodeValueWordMissing);
}
clear_bitnum(value_words, GCodeWord::L);
auto const wait_mode = validate_wait_on_input_mode_value(gc_block.values.l);
if (!wait_mode) {
FAIL(Error::GcodeValueWordInvalid);
}
// Only Immediate mode is valid for analog input
if (!isWaitOnInputDigital && wait_mode != WaitOnInputMode::Immediate) {
FAIL(Error::GcodeValueWordInvalid);
}
// Q is the timeout in seconds (conditionally optional)
// - Ignored if L is 0 (Immediate).
// - Error if value 0 seconds, and L is not 0 (Immediate).
if (bitnum_is_true(value_words, GCodeWord::Q)) {
if (gc_block.values.q != 0.0) {
if (wait_mode != WaitOnInputMode::Immediate) {
// Non-immediate waits must have a non-zero timeout
FAIL(Error::GcodeValueWordInvalid);
}
}
} else {
if (wait_mode != WaitOnInputMode::Immediate) {
// Non-immediate waits must have a timeout
FAIL(Error::GcodeValueWordMissing);
}
}
clear_bitnum(value_words, GCodeWord::Q);
}
if (gc_block.modal.set_tool_number == SetToolNumber::Enable) {
if (bitnum_is_false(value_words, GCodeWord::Q)) {
FAIL(Error::GcodeValueWordMissing);
Expand Down Expand Up @@ -1656,6 +1710,29 @@ Error gc_execute_line(char* line) {
FAIL(Error::PParamMaxExceeded);
}
}
if (gc_block.modal.io_control == IoControl::WaitOnInput) {
auto const validate_input_number = [&](const float input_number) -> std::optional<uint8_t> {
if (input_number < 0) {
return std::nullopt;
}
if (isWaitOnInputDigital) {
if (input_number > MaxUserDigitalPin) {
return std::nullopt;
} else if (input_number > MaxUserAnalogPin) {
return std::nullopt;
}
}
return (uint8_t)input_number;
};
auto const maybe_input_number = validate_input_number(isWaitOnInputDigital ? gc_block.values.p : gc_block.values.e);
if (!maybe_input_number.has_value()) {
FAIL(Error::PParamMaxExceeded);
}
auto const input_number = *maybe_input_number;
auto const wait_mode = *validate_wait_on_input_mode_value(gc_block.values.l);
auto const timeout = gc_block.values.q;
gc_wait_on_input(isWaitOnInputDigital, input_number, wait_mode, timeout);
}

// [9. Override control ]: NOT SUPPORTED. Always enabled, except for parking control.
if (config->_enableParkingOverrideControl) {
Expand Down Expand Up @@ -1917,3 +1994,48 @@ void gc_exec_linef(bool sync_after, Channel& out, const char* format, ...) {
group 10 = {G98, G99} return mode canned cycles
group 13 = {G61.1, G64} path control mode (G61 is supported)
*/

static std::optional<WaitOnInputMode> validate_wait_on_input_mode_value(uint8_t value) {
switch (value) {
case 0:
return WaitOnInputMode::Immediate;
case 1:
return WaitOnInputMode::Rise;
case 2:
return WaitOnInputMode::Fall;
case 3:
return WaitOnInputMode::High;
case 4:
return WaitOnInputMode::Low;
default:
return std::nullopt;
}
}

template <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

static Error gc_wait_on_input(bool is_digital, uint8_t input_number, WaitOnInputMode mode, float timeout) {
// TODO - only Immediate read mode is supported
if (mode == WaitOnInputMode::Immediate) {
auto const result = is_digital ? config->_userInputs->readDigitalInput(input_number) :
config->_userInputs->readAnalogInput(input_number);
auto const on_ok = [&](bool result) {
log_debug("M66: " << (is_digital ? "digital" : "analog") << "_input" << input_number << " result=" << result);
set_numbered_param(5399, result ? 1.0 : 0.0);
return Error::Ok;
};
auto const on_error = [&](Error error) {
log_error("M66: " << (is_digital ? "digital" : "analog") << "_input" << input_number << " failed");
return error;
};
return std::visit(overloaded { on_ok, on_error }, result);
}

// TODO - implement rest of modes
return Error::GcodeValueWordInvalid;
}
39 changes: 27 additions & 12 deletions FluidNC/src/GCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "SpindleDatatypes.h"

#include <cstdint>
#include <optional>

typedef uint16_t gcodenum_t;

Expand All @@ -25,7 +26,9 @@ enum class Override : uint8_t {
// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).
// NOTE: Modal group values must be sequential and starting from zero.

// http://linuxcnc.org/docs/html/gcode/overview.html#gcode:modal-groups
enum class ModalGroup : uint8_t {
// Table 5. G-code Modal Groups
MG0 = 0, // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal
MG1 = 1, // [G0,G1,G2,G3,G38.2,G38.3,G38.4,G38.5,G80] Motion
MG2 = 2, // [G17,G18,G19] Plane selection
Expand All @@ -37,12 +40,14 @@ enum class ModalGroup : uint8_t {
MG8 = 8, // [G43.1,G49] Tool length offset
MG12 = 9, // [G54,G55,G56,G57,G58,G59] Coordinate system selection
MG13 = 10, // [G61] Control mode
// Table 6. M-code Modal Groups
MM4 = 11, // [M0,M1,M2,M30] Stopping
MM6 = 14, // [M6] [M61] Tool change
MM7 = 12, // [M3,M4,M5] Spindle turning
MM8 = 13, // [M7,M8,M9] Coolant control
MM9 = 14, // [M56] Override control
MM10 = 15, // [M62, M63, M64, M65, M67, M68] User Defined http://linuxcnc.org/docs/html/gcode/overview.html#_modal_groups
MM5 = 12, // [M62,M63,M64,M65,M66,M67,M68] Digital/analog output/input
MM6 = 13, // [M6] [M61] Tool change
MM7 = 14, // [M3,M4,M5] Spindle turning
MM8 = 15, // [M7,M8,M9] Coolant control
MM9 = 16, // [M56] Override control
MM10 = 17, // [M100-M199] User Defined
};

// Command actions for within execution-type modal groups (motion, stopping, non-modal). Used
Expand Down Expand Up @@ -151,15 +156,25 @@ struct CoolantState {
// Modal Group M8: Coolant control
// Modal Group M9: Override control

// Modal Group M10: User I/O control
// Modal Group M5: User I/O control
enum class IoControl : gcodenum_t {
None = 0,
DigitalOnSync = 1, // M62
DigitalOffSync = 2, // M63
DigitalOnImmediate = 3, // M64
DigitalOffImmediate = 4, // M65
SetAnalogSync = 5, // M67
SetAnalogImmediate = 6, // M68
WaitOnInput = 5, // M66
SetAnalogSync = 6, // M67
SetAnalogImmediate = 7, // M68
};

// {M66} L word value, indicates wait mode
enum class WaitOnInputMode : int8_t {
Immediate,
Rise,
Fall,
High,
Low,
};

static const int MaxUserDigitalPin = 8;
Expand Down Expand Up @@ -266,14 +281,14 @@ struct gc_modal_t {
};

struct gc_values_t {
uint8_t e; // M67
uint8_t e; // {M66,M67}
float f; // Feed
float ijk[3]; // I,J,K Axis arc offsets - only 3 are possible
uint8_t l; // G10 or canned cycles parameters
uint8_t l; // {M66,G10}, or canned cycles parameters
int32_t n; // Line number
uint32_t o; // Subroutine identifier - single-meaning word (not used by the core)
float p; // G10 or dwell parameters
float q; // M67
float p; // {M66,G10}, or dwell parameters
float q; // {M66,M67}
float r; // Arc radius
float s; // Spindle speed
uint32_t t; // Tool selection
Expand Down
5 changes: 5 additions & 0 deletions FluidNC/src/Machine/MachineConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ namespace Machine {
handler.section("parking", _parking);

handler.section("user_outputs", _userOutputs);
handler.section("user_inputs", _userInputs);

ConfigurableModuleFactory::factory(handler);
ATCs::ATCFactory::factory(handler);
Expand Down Expand Up @@ -104,6 +105,10 @@ namespace Machine {
_userOutputs = new UserOutputs();
}

if (_userInputs == nullptr) {
_userInputs = new UserInputs();
}

if (_sdCard == nullptr) {
_sdCard = new SDCard();
}
Expand Down
30 changes: 16 additions & 14 deletions FluidNC/src/Machine/MachineConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "I2CBus.h"
#include "I2SOBus.h"
#include "UserOutputs.h"
#include "UserInputs.h"
#include "Macros.h"

#include <string_view>
Expand Down Expand Up @@ -59,20 +60,21 @@ namespace Machine {
public:
MachineConfig() = default;

Axes* _axes = nullptr;
Kinematics* _kinematics = nullptr;
SPIBus* _spi = nullptr;
I2CBus* _i2c[MAX_N_I2C] = { nullptr };
I2SOBus* _i2so = nullptr;
Stepping* _stepping = nullptr;
CoolantControl* _coolant = nullptr;
Probe* _probe = nullptr;
Control* _control = nullptr;
UserOutputs* _userOutputs = nullptr;
SDCard* _sdCard = nullptr;
Macros* _macros = nullptr;
Start* _start = nullptr;
Parking* _parking = nullptr;
Axes* _axes = nullptr;
Kinematics* _kinematics = nullptr;
SPIBus* _spi = nullptr;
I2CBus* _i2c[MAX_N_I2C] = { nullptr };
I2SOBus* _i2so = nullptr;
Stepping* _stepping = nullptr;
CoolantControl* _coolant = nullptr;
Probe* _probe = nullptr;
Control* _control = nullptr;
UserOutputs* _userOutputs = nullptr;
UserInputs* _userInputs = nullptr;
SDCard* _sdCard = nullptr;
Macros* _macros = nullptr;
Start* _start = nullptr;
Parking* _parking = nullptr;

UartChannel* _uart_channels[MAX_N_UARTS] = { nullptr };
Uart* _uarts[MAX_N_UARTS] = { nullptr };
Expand Down
Loading
Loading