mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-01 02:21:46 +02:00
Changed control pins based on the ISR_WIP branch
Added user defined pins
This commit is contained in:
@@ -167,15 +167,15 @@ bool user_defined_homing(AxisMask cycle_mask) {
|
||||
}
|
||||
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
|
||||
// Exit routines: No time to run protocol_execute_realtime() in this loop.
|
||||
if (sys_rt_exec_state.bit.safetyDoor || sys_rt_exec_state.bit.reset || cycle_stop) {
|
||||
if (rtSafetyDoor || rtReset || cycle_stop) {
|
||||
ExecState rt_exec_state;
|
||||
rt_exec_state.value = sys_rt_exec_state.value;
|
||||
// Homing failure condition: Reset issued during cycle.
|
||||
if (rt_exec_state.bit.reset) {
|
||||
if (rtReset) {
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailReset;
|
||||
}
|
||||
// Homing failure condition: Safety door was opened.
|
||||
if (rt_exec_state.bit.safetyDoor) {
|
||||
if (rtSafetyDoor) {
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailDoor;
|
||||
}
|
||||
// Homing failure condition: Limit switch still engaged after pull-off motion
|
||||
|
@@ -23,22 +23,12 @@
|
||||
|
||||
#include "Grbl.h"
|
||||
#include "Control.h"
|
||||
|
||||
// TODO: the plumbing to communicate with System.cpp is not yet done.
|
||||
// These variables are placeholders.
|
||||
bool rtSafetyDoor;
|
||||
bool rtReset;
|
||||
bool rtFeedHold;
|
||||
bool rtCycleStart;
|
||||
bool rtButtonMacro0;
|
||||
bool rtButtonMacro1;
|
||||
bool rtButtonMacro2;
|
||||
bool rtButtonMacro3;
|
||||
#include "System.h"
|
||||
|
||||
Control::Control() :
|
||||
_safetyDoor(0, &rtSafetyDoor, "Door", 'D'), _reset(1, &rtReset, "Reset", 'R'), _feedHold(2, &rtFeedHold, "FeedHold", 'H'),
|
||||
_cycleStart(3, &rtCycleStart, "CycleStart", 'S'), _macro0(4, &rtButtonMacro0, "Macro 0", '0'),
|
||||
_macro1(5, &rtButtonMacro1, "Macro 1", '1'), _macro2(6, &rtButtonMacro2, "Macro 2", '2'), _macro3(7, &rtButtonMacro3, "Macro 3", '3') {}
|
||||
_safetyDoor(0, rtSafetyDoor, "Door", 'D'), _reset(1, rtReset, "Reset", 'R'), _feedHold(2, rtFeedHold, "FeedHold", 'H'),
|
||||
_cycleStart(3, rtCycleStart, "CycleStart", 'S'), _macro0(4, rtButtonMacro0, "Macro 0", '0'), _macro1(5, rtButtonMacro1, "Macro 1", '1'),
|
||||
_macro2(6, rtButtonMacro2, "Macro 2", '2'), _macro3(7, rtButtonMacro3, "Macro 3", '3') {}
|
||||
|
||||
void Control::init() {
|
||||
_safetyDoor.init();
|
||||
@@ -63,12 +53,22 @@ void Control::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("macro2", _macro2._pin);
|
||||
handler.handle("macro3", _macro3._pin);
|
||||
}
|
||||
#if 0
|
||||
|
||||
void Control::report(char* status, bool& pinReportStarted) {
|
||||
_safetyDoor.report(status, pinReportStarted);
|
||||
_reset.report(status, pinReportStarted);
|
||||
_feedHold.report(status, pinReportStarted);
|
||||
_cycleStart.report(status, pinReportStarted);
|
||||
_macro0.report(status, pinReportStarted);
|
||||
_macro1.report(status, pinReportStarted);
|
||||
_macro2.report(status, pinReportStarted);
|
||||
_macro3.report(status, pinReportStarted);
|
||||
}
|
||||
|
||||
// Returns if safety door is ajar(T) or closed(F), based on pin state.
|
||||
bool system_check_safety_door_ajar() {
|
||||
bool Control::system_check_safety_door_ajar() {
|
||||
// If a safety door pin is not defined, this will return false
|
||||
// because that is the default for the value field, which will
|
||||
// never be changed for an undefined pin.
|
||||
return _safetyDoor.get();
|
||||
}
|
||||
#endif
|
||||
|
@@ -30,6 +30,8 @@
|
||||
|
||||
class Control : public Configuration::Configurable {
|
||||
private:
|
||||
// TODO: Should we not just put this in an array so we can enumerate it easily?
|
||||
|
||||
ControlPin _safetyDoor;
|
||||
ControlPin _reset;
|
||||
ControlPin _feedHold;
|
||||
@@ -49,5 +51,8 @@ public:
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
|
||||
bool system_check_safety_door_ajar();
|
||||
void report(char* status, bool& pinReportStarted);
|
||||
|
||||
~Control() = default;
|
||||
};
|
||||
|
@@ -8,10 +8,7 @@
|
||||
void IRAM_ATTR ControlPin::handleISR() {
|
||||
bool pinState = _pin.read();
|
||||
_value = pinState;
|
||||
|
||||
if (_rtVariable) {
|
||||
*_rtVariable = pinState;
|
||||
}
|
||||
_rtVariable = pinState;
|
||||
}
|
||||
|
||||
void ControlPin::init() {
|
||||
@@ -25,3 +22,19 @@ void ControlPin::init() {
|
||||
_pin.attachInterrupt<ControlPin, &ControlPin::handleISR>(this, CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlPin::report(char* status, bool& pinReportStarted) {
|
||||
if (!_pin.undefined()) {
|
||||
if (!pinReportStarted) {
|
||||
strcat(status, "|Pn:");
|
||||
pinReportStarted = true;
|
||||
}
|
||||
size_t pos = strlen(status);
|
||||
status[pos] = letter();
|
||||
status[pos + 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
ControlPin::~ControlPin() {
|
||||
_pin.detachInterrupt();
|
||||
}
|
||||
|
@@ -9,20 +9,26 @@ private:
|
||||
bool _invertBitNum;
|
||||
bool _value;
|
||||
const char _letter;
|
||||
volatile bool* _rtVariable;
|
||||
volatile bool& _rtVariable;
|
||||
const char* _legend;
|
||||
|
||||
void IRAM_ATTR handleISR();
|
||||
|
||||
public:
|
||||
ControlPin(uint8_t bitNum, volatile bool* rtVariable, const char* legend, char letter) :
|
||||
_invertBitNum(bitNum), _value(false), _letter(letter), _rtVariable(rtVariable), _legend(legend) {}
|
||||
ControlPin(uint8_t bitNum, volatile bool& rtVariable, const char* legend, char letter) :
|
||||
_invertBitNum(bitNum), _value(false), _letter(letter), _rtVariable(rtVariable), _legend(legend) {
|
||||
_rtVariable = _value;
|
||||
}
|
||||
|
||||
Pin _pin;
|
||||
|
||||
void init();
|
||||
bool get() { return _value; }
|
||||
const char* legend() { return _legend; }
|
||||
// char invertBitNum() { return _invertBitNum; }
|
||||
char letter() { return _letter; }
|
||||
void init();
|
||||
|
||||
void report(char* status, bool& pinReportStarted);
|
||||
|
||||
~ControlPin();
|
||||
};
|
||||
|
@@ -2,29 +2,6 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
// System executor bit map. Used internally by realtime protocol as realtime command flags,
|
||||
// which notifies the main program to execute the specified realtime command asynchronously.
|
||||
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
|
||||
// flags are always false, so the realtime protocol only needs to check for a non-zero value to
|
||||
// know when there is a realtime command to execute.
|
||||
struct ExecStateBits {
|
||||
uint8_t statusReport : 1;
|
||||
uint8_t cycleStart : 1;
|
||||
uint8_t cycleStop : 1; // Unused, per cycle_stop variable
|
||||
uint8_t feedHold : 1;
|
||||
uint8_t reset : 1;
|
||||
uint8_t safetyDoor : 1;
|
||||
uint8_t motionCancel : 1;
|
||||
uint8_t sleep : 1;
|
||||
};
|
||||
|
||||
union ExecState {
|
||||
uint8_t value;
|
||||
ExecStateBits bit;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ExecStateBits) == sizeof(uint8_t), "ExecStateBits is not an uint8");
|
||||
|
||||
// Override bit maps. Realtime bitflags to control feed, rapid, spindle, and coolant overrides.
|
||||
// Spindle/coolant and feed/rapids are separated into two controlling flag variables.
|
||||
|
||||
|
@@ -1561,8 +1561,8 @@ Error gc_execute_line(char* line, uint8_t client) {
|
||||
case ProgramFlow::Paused:
|
||||
protocol_buffer_synchronize(); // Sync and finish all remaining buffered motions before moving on.
|
||||
if (sys.state != State::CheckMode) {
|
||||
sys_rt_exec_state.bit.feedHold = true; // Use feed hold for program pause.
|
||||
protocol_execute_realtime(); // Execute suspend.
|
||||
rtFeedHold = true; // Use feed hold for program pause.
|
||||
protocol_execute_realtime(); // Execute suspend.
|
||||
}
|
||||
break;
|
||||
case ProgramFlow::CompletedM2:
|
||||
|
@@ -66,7 +66,8 @@ void grbl_init() {
|
||||
config->_axes->init();
|
||||
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Initializing system...");
|
||||
system_ini(); // Configure pinout pins and pin-change interrupt (Renamed due to conflict with esp32 files)
|
||||
config->_control->init();
|
||||
init_output_pins(); // Configure pinout pins and pin-change interrupt (Renamed due to conflict with esp32 files)
|
||||
memset(sys_position, 0, sizeof(sys_position)); // Clear machine position.
|
||||
|
||||
machine_init(); // user supplied function for special initialization
|
||||
@@ -126,10 +127,16 @@ static void reset_variables() {
|
||||
memset(sys_probe_position, 0, sizeof(sys_probe_position)); // Clear probe position.
|
||||
|
||||
sys_probe_state = ProbeState::Off;
|
||||
sys_rt_exec_state.value = 0;
|
||||
rtStatusReport = false;
|
||||
rtCycleStart = false;
|
||||
rtFeedHold = false;
|
||||
rtReset = false;
|
||||
rtSafetyDoor = false;
|
||||
rtMotionCancel = false;
|
||||
rtSleep = false;
|
||||
rtCycleStop = false;
|
||||
sys_rt_exec_accessory_override.value = 0;
|
||||
sys_rt_exec_alarm = ExecAlarm::None;
|
||||
cycle_stop = false;
|
||||
sys_rt_f_override = FeedOverride::Default;
|
||||
sys_rt_r_override = RapidOverride::Default;
|
||||
sys_rt_s_override = SpindleSpeedOverride::Default;
|
||||
|
@@ -63,6 +63,7 @@ const char* const GRBL_VERSION_BUILD = "20210326";
|
||||
#include "Settings.h"
|
||||
#include "SettingsDefinitions.h"
|
||||
#include "WebUI/WebSettings.h"
|
||||
#include "ControlPin.h"
|
||||
|
||||
#include "UserOutput.h"
|
||||
|
||||
|
@@ -221,15 +221,13 @@ void limits_go_home(uint8_t cycle_mask, uint n_locate_cycles) {
|
||||
}
|
||||
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
|
||||
// Exit routines: No time to run protocol_execute_realtime() in this loop.
|
||||
if (sys_rt_exec_state.bit.safetyDoor || sys_rt_exec_state.bit.reset || cycle_stop) {
|
||||
ExecState rt_exec_state;
|
||||
rt_exec_state.value = sys_rt_exec_state.value;
|
||||
if (rtSafetyDoor || rtReset || rtCycleStop) {
|
||||
// Homing failure condition: Reset issued during cycle.
|
||||
if (rt_exec_state.bit.reset) {
|
||||
if (rtReset) {
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailReset;
|
||||
}
|
||||
// Homing failure condition: Safety door was opened.
|
||||
if (rt_exec_state.bit.safetyDoor) {
|
||||
if (rtSafetyDoor) {
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailDoor;
|
||||
}
|
||||
// Homing failure condition: Limit switch still engaged after pull-off motion
|
||||
@@ -237,7 +235,7 @@ void limits_go_home(uint8_t cycle_mask, uint n_locate_cycles) {
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailPulloff;
|
||||
}
|
||||
// Homing failure condition: Limit switch not found during approach.
|
||||
if (approach && cycle_stop) {
|
||||
if (approach && rtCycleStop) {
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailApproach;
|
||||
}
|
||||
|
||||
@@ -249,7 +247,7 @@ void limits_go_home(uint8_t cycle_mask, uint n_locate_cycles) {
|
||||
return;
|
||||
} else {
|
||||
// Pull-off motion complete. Disable CYCLE_STOP from executing.
|
||||
cycle_stop = false;
|
||||
rtCycleStop = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -386,7 +384,7 @@ void limits_soft_check(float* target) {
|
||||
// workspace volume so just come to a controlled stop so position is not lost. When complete
|
||||
// enter alarm mode.
|
||||
if (sys.state == State::Cycle) {
|
||||
sys_rt_exec_state.bit.feedHold = true;
|
||||
rtFeedHold = true;
|
||||
do {
|
||||
protocol_execute_realtime();
|
||||
if (sys.abort) {
|
||||
|
@@ -344,6 +344,19 @@ void SPIBus::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("mosi", _sck);
|
||||
}
|
||||
|
||||
void UserOutputs::validate() const {}
|
||||
|
||||
void UserOutputs::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("analog0", _analogOutput[0]);
|
||||
handler.handle("analog1", _analogOutput[1]);
|
||||
handler.handle("analog2", _analogOutput[2]);
|
||||
handler.handle("analog3", _analogOutput[3]);
|
||||
handler.handle("digital0", _digitalOutput[0]);
|
||||
handler.handle("digital1", _digitalOutput[1]);
|
||||
handler.handle("digital2", _digitalOutput[2]);
|
||||
handler.handle("digital3", _digitalOutput[3]);
|
||||
}
|
||||
|
||||
void MachineConfig::validate() const {}
|
||||
|
||||
void MachineConfig::handle(Configuration::HandlerBase& handler) {
|
||||
@@ -357,7 +370,9 @@ void MachineConfig::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("board", _board);
|
||||
handler.handle("name", _name);
|
||||
handler.handle("idle_time", _idleTime);
|
||||
// TODO: Consider putting these under a gcode: hierarchy level
|
||||
handler.handle("user_outputs", _userOutputs);
|
||||
|
||||
// TODO: Consider putting these under a gcode: hierarchy level? Or motion control?
|
||||
handler.handle("laser_mode", _laserMode);
|
||||
handler.handle("arc_tolerance", _arcTolerance);
|
||||
handler.handle("junction_deviation", _junctionDeviation);
|
||||
@@ -380,6 +395,15 @@ void MachineConfig::afterParse() {
|
||||
log_info("Probe config missing; building default probe");
|
||||
_probe = new Probe();
|
||||
}
|
||||
|
||||
if (_userOutputs == nullptr) {
|
||||
_userOutputs = new UserOutputs();
|
||||
}
|
||||
|
||||
if (_control == nullptr) {
|
||||
log_info("Control config missing; building default");
|
||||
_control = new Control();
|
||||
}
|
||||
}
|
||||
|
||||
size_t MachineConfig::readFile(const char* filename, char*& buffer) {
|
||||
|
@@ -202,6 +202,19 @@ public:
|
||||
~SPIBus() = default;
|
||||
};
|
||||
|
||||
class UserOutputs : public Configuration::Configurable {
|
||||
public:
|
||||
UserOutputs() = default;
|
||||
|
||||
Pin _analogOutput[4];
|
||||
Pin _digitalOutput[4];
|
||||
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
|
||||
~UserOutputs() = default;
|
||||
};
|
||||
|
||||
class WifiConfig : public Configuration::Configurable {
|
||||
public:
|
||||
WifiConfig() = default;
|
||||
@@ -351,13 +364,14 @@ class MachineConfig : public Configuration::Configurable {
|
||||
public:
|
||||
MachineConfig() = default;
|
||||
|
||||
Axes* _axes = nullptr;
|
||||
SPIBus* _spi = nullptr;
|
||||
I2SOBus* _i2so = nullptr;
|
||||
CoolantControl* _coolant = nullptr;
|
||||
Probe* _probe = nullptr;
|
||||
Communications* _comms = nullptr;
|
||||
Control* _control = nullptr;
|
||||
Axes* _axes = nullptr;
|
||||
SPIBus* _spi = nullptr;
|
||||
I2SOBus* _i2so = nullptr;
|
||||
CoolantControl* _coolant = nullptr;
|
||||
Probe* _probe = nullptr;
|
||||
Communications* _comms = nullptr;
|
||||
Control* _control = nullptr;
|
||||
UserOutputs* _userOutputs = nullptr;
|
||||
|
||||
int _pulseMicroSeconds = 3;
|
||||
int _directionDelayMilliSeconds = 0;
|
||||
|
@@ -450,7 +450,7 @@ GCUpdatePos mc_probe_cycle(float* target, plan_line_data_t* pl_data, uint8_t par
|
||||
// Activate the probing state monitor in the stepper module.
|
||||
sys_probe_state = ProbeState::Active;
|
||||
// Perform probing cycle. Wait here until probe is triggered or motion completes.
|
||||
sys_rt_exec_state.bit.cycleStart = true;
|
||||
rtCycleStart = true;
|
||||
do {
|
||||
protocol_execute_realtime();
|
||||
if (sys.abort) {
|
||||
@@ -534,8 +534,8 @@ void mc_override_ctrl_update(Override override_state) {
|
||||
void mc_reset() {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Debug, "mc_reset()");
|
||||
// Only this function can set the system reset. Helps prevent multiple kill calls.
|
||||
if (!sys_rt_exec_state.bit.reset) {
|
||||
sys_rt_exec_state.bit.reset = true;
|
||||
if (!rtReset) {
|
||||
rtReset = true;
|
||||
// Kill spindle and coolant.
|
||||
spindle->stop();
|
||||
|
||||
|
@@ -28,6 +28,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
// TODO: ENABLE_CONTROL_SW_DEBOUNCE should end up here with a shared task.
|
||||
|
||||
// #define PIN_DEBUG // Pin debugging. WILL spam you with a lot of data!
|
||||
|
||||
// Forward declarations:
|
||||
|
@@ -64,7 +64,7 @@ void Probe::state_monitor() {
|
||||
if (get_state() ^ _isProbeAway) {
|
||||
sys_probe_state = ProbeState::Off;
|
||||
memcpy(sys_probe_position, sys_position, sizeof(sys_position));
|
||||
sys_rt_exec_state.bit.motionCancel = true;
|
||||
rtMotionCancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,8 @@
|
||||
#include <map>
|
||||
#include "Regex.h"
|
||||
|
||||
#include "MachineConfig.h"
|
||||
|
||||
// WG Readable and writable as guest
|
||||
// WU Readable and writable as user and admin
|
||||
// WA Readable as user and admin, writable as admin
|
||||
@@ -222,7 +224,7 @@ Error toggle_check_mode(const char* value, WebUI::AuthenticationLevel auth_level
|
||||
Error disable_alarm_lock(const char* value, WebUI::AuthenticationLevel auth_level, WebUI::ESPResponseStream* out) {
|
||||
if (sys.state == State::Alarm) {
|
||||
// Block if safety door is ajar.
|
||||
if (system_check_safety_door_ajar()) {
|
||||
if (config->_control->system_check_safety_door_ajar()) {
|
||||
return Error::CheckDoor;
|
||||
}
|
||||
report_feedback_message(Message::AlarmUnlock);
|
||||
@@ -239,7 +241,7 @@ Error home(int cycle) {
|
||||
if (homingAxes()) {
|
||||
return Error::SettingDisabled;
|
||||
}
|
||||
if (system_check_safety_door_ajar()) {
|
||||
if (config->_control->system_check_safety_door_ajar()) {
|
||||
return Error::CheckDoor; // Block if safety door is ajar.
|
||||
}
|
||||
sys.state = State::Homing; // Set system state variable
|
||||
@@ -291,7 +293,7 @@ Error home_c(const char* value, WebUI::AuthenticationLevel auth_level, WebUI::ES
|
||||
return home(bit(C_AXIS));
|
||||
}
|
||||
Error sleep_grbl(const char* value, WebUI::AuthenticationLevel auth_level, WebUI::ESPResponseStream* out) {
|
||||
sys_rt_exec_state.bit.sleep = true;
|
||||
rtSleep = true;
|
||||
return Error::Ok;
|
||||
}
|
||||
Error get_report_build_info(const char* value, WebUI::AuthenticationLevel auth_level, WebUI::ESPResponseStream* out) {
|
||||
|
@@ -126,8 +126,8 @@ void protocol_main_loop() {
|
||||
} else {
|
||||
// Check if the safety door is open.
|
||||
sys.state = State::Idle;
|
||||
if (system_check_safety_door_ajar()) {
|
||||
sys_rt_exec_state.bit.safetyDoor = true;
|
||||
if (config->_control->system_check_safety_door_ajar()) {
|
||||
rtSafetyDoor = true;
|
||||
protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state.
|
||||
}
|
||||
// All systems go!
|
||||
@@ -224,8 +224,8 @@ void protocol_buffer_synchronize() {
|
||||
// is finished, single commands), a command that needs to wait for the motions in the buffer to
|
||||
// execute calls a buffer sync, or the planner buffer is full and ready to go.
|
||||
void protocol_auto_cycle_start() {
|
||||
if (plan_get_current_block() != NULL) { // Check if there are any blocks in the buffer.
|
||||
sys_rt_exec_state.bit.cycleStart = true; // If so, execute them!
|
||||
if (plan_get_current_block() != NULL) { // Check if there are any blocks in the buffer.
|
||||
rtCycleStart = true; // If so, execute them!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,176 +250,279 @@ void protocol_execute_realtime() {
|
||||
// Executes run-time commands, when required. This function primarily operates as Grbl's state
|
||||
// machine and controls the various real-time features Grbl has to offer.
|
||||
// NOTE: Do not alter this unless you know exactly what you are doing!
|
||||
void protocol_exec_rt_system() {
|
||||
ExecAlarm alarm = sys_rt_exec_alarm; // Temp variable to avoid calling volatile multiple times.
|
||||
if (alarm != ExecAlarm::None) { // Enter only if an alarm is pending
|
||||
static void protocol_do_alarm() {
|
||||
switch (sys_rt_exec_alarm) {
|
||||
case ExecAlarm::None:
|
||||
return;
|
||||
// System alarm. Everything has shutdown by something that has gone severely wrong. Report
|
||||
// the source of the error to the user. If critical, Grbl disables by entering an infinite
|
||||
// loop until system reset/abort.
|
||||
sys.state = State::Alarm; // Set system alarm state
|
||||
report_alarm_message(alarm);
|
||||
// Halt everything upon a critical event flag. Currently hard and soft limits flag this.
|
||||
if ((alarm == ExecAlarm::HardLimit) || (alarm == ExecAlarm::SoftLimit)) {
|
||||
case ExecAlarm::HardLimit:
|
||||
case ExecAlarm::SoftLimit:
|
||||
sys.state = State::Alarm; // Set system alarm state
|
||||
report_alarm_message(sys_rt_exec_alarm);
|
||||
report_feedback_message(Message::CriticalEvent);
|
||||
sys_rt_exec_state.bit.reset = false; // Disable any existing reset
|
||||
rtReset = false; // Disable any existing reset
|
||||
do {
|
||||
// Block everything, except reset and status reports, until user issues reset or power
|
||||
// cycles. Hard limits typically occur while unattended or not paying attention. Gives
|
||||
// the user and a GUI time to do what is needed before resetting, like killing the
|
||||
// incoming stream. The same could be said about soft limits. While the position is not
|
||||
// lost, continued streaming could cause a serious crash if by chance it gets executed.
|
||||
} while (!sys_rt_exec_state.bit.reset);
|
||||
}
|
||||
sys_rt_exec_alarm = ExecAlarm::None;
|
||||
} while (!rtReset);
|
||||
break;
|
||||
default:
|
||||
sys.state = State::Alarm; // Set system alarm state
|
||||
report_alarm_message(sys_rt_exec_alarm);
|
||||
break;
|
||||
}
|
||||
ExecState rt_exec_state;
|
||||
rt_exec_state.value = sys_rt_exec_state.value; // Copy volatile sys_rt_exec_state.
|
||||
if (rt_exec_state.value != 0 || cycle_stop) { // Test if any bits are on
|
||||
// Execute system abort.
|
||||
if (rt_exec_state.bit.reset) {
|
||||
sys.abort = true; // Only place this is set true.
|
||||
return; // Nothing else to do but exit.
|
||||
}
|
||||
// Execute and serial print status
|
||||
if (rt_exec_state.bit.statusReport) {
|
||||
report_realtime_status(CLIENT_ALL);
|
||||
sys_rt_exec_state.bit.statusReport = false;
|
||||
}
|
||||
// NOTE: Once hold is initiated, the system immediately enters a suspend state to block all
|
||||
// main program processes until either reset or resumed. This ensures a hold completes safely.
|
||||
if (rt_exec_state.bit.motionCancel || rt_exec_state.bit.feedHold || rt_exec_state.bit.safetyDoor || rt_exec_state.bit.sleep) {
|
||||
// State check for allowable states for hold methods.
|
||||
if (!(sys.state == State::Alarm || sys.state == State::CheckMode)) {
|
||||
// If in CYCLE or JOG states, immediately initiate a motion HOLD.
|
||||
if (sys.state == State::Cycle || sys.state == State::Jog) {
|
||||
if (!(sys.suspend.bit.motionCancel || sys.suspend.bit.jogCancel)) { // Block, if already holding.
|
||||
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
|
||||
sys.step_control = {};
|
||||
sys.step_control.executeHold = true; // Initiate suspend state with active flag.
|
||||
if (sys.state == State::Jog) { // Jog cancelled upon any hold event, except for sleeping.
|
||||
if (!rt_exec_state.bit.sleep) {
|
||||
sys.suspend.bit.jogCancel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
|
||||
if (sys.state == State::Idle) {
|
||||
sys.suspend.value = 0;
|
||||
sys.suspend.bit.holdComplete = true;
|
||||
}
|
||||
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
|
||||
// to halt and cancel the remainder of the motion.
|
||||
if (rt_exec_state.bit.motionCancel) {
|
||||
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
|
||||
// to hold the CYCLE. Motion cancel is valid for a single planner block motion only, while jog cancel
|
||||
// will handle and clear multiple planner block motions.
|
||||
if (sys.state != State::Jog) {
|
||||
sys.suspend.bit.motionCancel = true; // NOTE: State is State::Cycle.
|
||||
}
|
||||
sys_rt_exec_state.bit.motionCancel = false;
|
||||
}
|
||||
// Execute a feed hold with deceleration, if required. Then, suspend system.
|
||||
if (rt_exec_state.bit.feedHold) {
|
||||
// Block SAFETY_DOOR, JOG, and SLEEP states from changing to HOLD state.
|
||||
if (!(sys.state == State::SafetyDoor || sys.state == State::Jog || sys.state == State::Sleep)) {
|
||||
sys.state = State::Hold;
|
||||
}
|
||||
sys_rt_exec_state.bit.feedHold = false;
|
||||
}
|
||||
// Execute a safety door stop with a feed hold and disable spindle/coolant.
|
||||
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
|
||||
// devices (spindle/coolant), and blocks resuming until switch is re-engaged.
|
||||
if (rt_exec_state.bit.safetyDoor) {
|
||||
report_feedback_message(Message::SafetyDoorAjar);
|
||||
// If jogging, block safety door methods until jog cancel is complete. Just flag that it happened.
|
||||
if (!(sys.suspend.bit.jogCancel)) {
|
||||
// Check if the safety re-opened during a restore parking motion only. Ignore if
|
||||
// already retracting, parked or in sleep state.
|
||||
if (sys.state == State::SafetyDoor) {
|
||||
if (sys.suspend.bit.initiateRestore) { // Actively restoring
|
||||
sys_rt_exec_alarm = ExecAlarm::None;
|
||||
}
|
||||
|
||||
static void protocol_start_holding() {
|
||||
if (!(sys.suspend.bit.motionCancel || sys.suspend.bit.jogCancel)) { // Block, if already holding.
|
||||
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
|
||||
sys.step_control = {};
|
||||
sys.step_control.executeHold = true; // Initiate suspend state with active flag.
|
||||
}
|
||||
}
|
||||
|
||||
static void protocol_cancel_jogging() {
|
||||
if (!sys.suspend.bit.motionCancel) {
|
||||
sys.suspend.bit.jogCancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void protocol_hold_complete() {
|
||||
sys.suspend.value = 0;
|
||||
sys.suspend.bit.holdComplete = true;
|
||||
}
|
||||
|
||||
static void protocol_do_motion_cancel() {
|
||||
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
|
||||
// to halt and cancel the remainder of the motion.
|
||||
rtMotionCancel = false;
|
||||
rtCycleStart = false; // Cancel any pending start
|
||||
|
||||
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may have been initiated
|
||||
// beforehand. Motion cancel affects only a single planner block motion, while jog cancel
|
||||
// will handle and clear multiple planner block motions.
|
||||
switch (sys.state) {
|
||||
case State::Alarm:
|
||||
case State::CheckMode:
|
||||
return; // Do not set motionCancel
|
||||
|
||||
case State::Idle:
|
||||
protocol_hold_complete();
|
||||
break;
|
||||
|
||||
case State::Cycle:
|
||||
protocol_start_holding();
|
||||
break;
|
||||
|
||||
case State::Jog:
|
||||
protocol_start_holding();
|
||||
protocol_cancel_jogging();
|
||||
// When jogging, we do not set motionCancel, hence return not break
|
||||
return;
|
||||
|
||||
case State::Sleep:
|
||||
case State::Hold:
|
||||
case State::Homing:
|
||||
case State::SafetyDoor:
|
||||
break;
|
||||
}
|
||||
sys.suspend.bit.motionCancel = true;
|
||||
}
|
||||
|
||||
static void protocol_do_feedhold() {
|
||||
// Execute a feed hold with deceleration, if required. Then, suspend system.
|
||||
rtFeedHold = false;
|
||||
rtCycleStart = false; // Cancel any pending start
|
||||
switch (sys.state) {
|
||||
case State::Alarm:
|
||||
case State::CheckMode:
|
||||
case State::SafetyDoor:
|
||||
case State::Sleep:
|
||||
return; // Do not change the state to Hold
|
||||
|
||||
case State::Hold:
|
||||
case State::Homing:
|
||||
break;
|
||||
|
||||
case State::Idle:
|
||||
protocol_hold_complete();
|
||||
break;
|
||||
|
||||
case State::Cycle:
|
||||
protocol_start_holding();
|
||||
break;
|
||||
|
||||
case State::Jog:
|
||||
protocol_start_holding();
|
||||
protocol_cancel_jogging();
|
||||
return; // Do not change the state to Hold
|
||||
}
|
||||
sys.state = State::Hold;
|
||||
}
|
||||
|
||||
static void protocol_do_safety_door() {
|
||||
// Execute a safety door stop with a feed hold and disable spindle/coolant.
|
||||
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
|
||||
// devices (spindle/coolant), and blocks resuming until switch is re-engaged.
|
||||
|
||||
rtCycleStart = false; // Cancel any pending start
|
||||
report_feedback_message(Message::SafetyDoorAjar);
|
||||
switch (sys.state) {
|
||||
case State::Alarm:
|
||||
case State::CheckMode:
|
||||
case State::Sleep:
|
||||
rtSafetyDoor = false;
|
||||
return; // Do not change the state to SafetyDoor
|
||||
|
||||
case State::Hold:
|
||||
break;
|
||||
case State::Homing:
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailDoor;
|
||||
break;
|
||||
case State::SafetyDoor:
|
||||
if (!sys.suspend.bit.jogCancel && sys.suspend.bit.initiateRestore) { // Actively restoring
|
||||
#ifdef PARKING_ENABLE
|
||||
// Set hold and reset appropriate control flags to restart parking sequence.
|
||||
if (sys.step_control.executeSysMotion) {
|
||||
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
|
||||
sys.step_control = {};
|
||||
sys.step_control.executeHold = true;
|
||||
sys.step_control.executeSysMotion = true;
|
||||
sys.suspend.bit.holdComplete = false;
|
||||
} // else NO_MOTION is active.
|
||||
// Set hold and reset appropriate control flags to restart parking sequence.
|
||||
if (sys.step_control.executeSysMotion) {
|
||||
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
|
||||
sys.step_control = {};
|
||||
sys.step_control.executeHold = true;
|
||||
sys.step_control.executeSysMotion = true;
|
||||
sys.suspend.bit.holdComplete = false;
|
||||
} // else NO_MOTION is active.
|
||||
#endif
|
||||
sys.suspend.bit.retractComplete = false;
|
||||
sys.suspend.bit.initiateRestore = false;
|
||||
sys.suspend.bit.restoreComplete = false;
|
||||
sys.suspend.bit.restartRetract = true;
|
||||
}
|
||||
}
|
||||
if (sys.state != State::Sleep) {
|
||||
sys.state = State::SafetyDoor;
|
||||
}
|
||||
sys_rt_exec_state.bit.safetyDoor = false;
|
||||
}
|
||||
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
|
||||
// are executed if the door switch closes and the state returns to HOLD.
|
||||
sys.suspend.bit.safetyDoorAjar = true;
|
||||
sys.suspend.bit.retractComplete = false;
|
||||
sys.suspend.bit.initiateRestore = false;
|
||||
sys.suspend.bit.restoreComplete = false;
|
||||
sys.suspend.bit.restartRetract = true;
|
||||
}
|
||||
break;
|
||||
case State::Idle:
|
||||
protocol_hold_complete();
|
||||
break;
|
||||
case State::Cycle:
|
||||
protocol_start_holding();
|
||||
break;
|
||||
case State::Jog:
|
||||
protocol_start_holding();
|
||||
protocol_cancel_jogging();
|
||||
break;
|
||||
}
|
||||
if (!sys.suspend.bit.jogCancel) {
|
||||
// If jogging, leave the safety door event pending until the jog cancel completes
|
||||
rtSafetyDoor = false;
|
||||
sys.state = State::SafetyDoor;
|
||||
}
|
||||
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
|
||||
// are executed if the door switch closes and the state returns to HOLD.
|
||||
sys.suspend.bit.safetyDoorAjar = true;
|
||||
}
|
||||
|
||||
static void protocol_do_sleep() {
|
||||
rtSleep = false;
|
||||
switch (sys.state) {
|
||||
case State::Alarm:
|
||||
sys.suspend.bit.retractComplete = true;
|
||||
sys.suspend.bit.holdComplete = true;
|
||||
break;
|
||||
|
||||
case State::Idle:
|
||||
protocol_hold_complete();
|
||||
break;
|
||||
|
||||
case State::Cycle:
|
||||
case State::Jog:
|
||||
protocol_start_holding();
|
||||
// Unlike other hold events, sleep does not set jogCancel
|
||||
break;
|
||||
|
||||
case State::CheckMode:
|
||||
case State::Sleep:
|
||||
case State::Hold:
|
||||
case State::Homing:
|
||||
case State::SafetyDoor:
|
||||
break;
|
||||
}
|
||||
sys.state = State::Sleep;
|
||||
}
|
||||
|
||||
static void protocol_do_initiate_cycle() {
|
||||
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
|
||||
sys.step_control = {}; // Restore step control to normal operation
|
||||
if (plan_get_current_block() && !sys.suspend.bit.motionCancel) {
|
||||
sys.suspend.value = 0; // Break suspend state.
|
||||
sys.state = State::Cycle;
|
||||
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
|
||||
st_wake_up();
|
||||
} else { // Otherwise, do nothing. Set and resume IDLE state.
|
||||
sys.suspend.value = 0; // Break suspend state.
|
||||
sys.state = State::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
// The handlers for rtFeedHold, rtMotionCancel, and rtsDafetyDoor clear rtCycleStart to
|
||||
// ensure that auto-cycle-start does not resume a hold without explicit user input.
|
||||
static void protocol_do_cycle_start() {
|
||||
rtCycleStart = false;
|
||||
|
||||
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
|
||||
|
||||
// Resume door state when parking motion has retracted and door has been closed.
|
||||
switch (sys.state) {
|
||||
case State::SafetyDoor:
|
||||
if (!sys.suspend.bit.safetyDoorAjar) {
|
||||
if (sys.suspend.bit.restoreComplete) {
|
||||
sys.state = State::Idle; // Set to IDLE to immediately resume the cycle.
|
||||
} else if (sys.suspend.bit.retractComplete) {
|
||||
// Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
|
||||
// NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
|
||||
// the retraction execution is complete, which implies the initial feed hold is not active. To
|
||||
// restore normal operation, the restore procedures must be initiated by the following flag. Once,
|
||||
// they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
|
||||
sys.suspend.bit.initiateRestore = true;
|
||||
}
|
||||
}
|
||||
if (rt_exec_state.bit.sleep) {
|
||||
if (sys.state == State::Alarm) {
|
||||
sys.suspend.bit.retractComplete = true;
|
||||
sys.suspend.bit.holdComplete = true;
|
||||
}
|
||||
sys.state = State::Sleep;
|
||||
sys_rt_exec_state.bit.sleep = false;
|
||||
}
|
||||
}
|
||||
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
|
||||
if (rt_exec_state.bit.cycleStart) {
|
||||
// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
|
||||
// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
|
||||
if (!(rt_exec_state.bit.feedHold || rt_exec_state.bit.motionCancel || rt_exec_state.bit.safetyDoor)) {
|
||||
// Resume door state when parking motion has retracted and door has been closed.
|
||||
if (sys.state == State::SafetyDoor && !(sys.suspend.bit.safetyDoorAjar)) {
|
||||
if (sys.suspend.bit.restoreComplete) {
|
||||
sys.state = State::Idle; // Set to IDLE to immediately resume the cycle.
|
||||
} else if (sys.suspend.bit.retractComplete) {
|
||||
// Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
|
||||
// NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
|
||||
// the retraction execution is complete, which implies the initial feed hold is not active. To
|
||||
// restore normal operation, the restore procedures must be initiated by the following flag. Once,
|
||||
// they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
|
||||
sys.suspend.bit.initiateRestore = true;
|
||||
}
|
||||
}
|
||||
// Cycle start only when IDLE or when a hold is complete and ready to resume.
|
||||
if (sys.state == State::Idle || (sys.state == State::Hold && sys.suspend.bit.holdComplete)) {
|
||||
if (sys.state == State::Hold && sys.spindle_stop_ovr.value) {
|
||||
sys.spindle_stop_ovr.bit.restoreCycle = true; // Set to restore in suspend routine and cycle start after.
|
||||
} else {
|
||||
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
|
||||
sys.step_control = {}; // Restore step control to normal operation
|
||||
if (plan_get_current_block() && !sys.suspend.bit.motionCancel) {
|
||||
sys.suspend.value = 0; // Break suspend state.
|
||||
sys.state = State::Cycle;
|
||||
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
|
||||
st_wake_up();
|
||||
} else { // Otherwise, do nothing. Set and resume IDLE state.
|
||||
sys.suspend.value = 0; // Break suspend state.
|
||||
sys.state = State::Idle;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State::Idle:
|
||||
protocol_do_initiate_cycle();
|
||||
break;
|
||||
case State::Hold:
|
||||
// Cycle start only when IDLE or when a hold is complete and ready to resume.
|
||||
if (sys.suspend.bit.holdComplete) {
|
||||
if (sys.spindle_stop_ovr.value) {
|
||||
sys.spindle_stop_ovr.bit.restoreCycle = true; // Set to restore in suspend routine and cycle start after.
|
||||
} else {
|
||||
protocol_do_initiate_cycle();
|
||||
}
|
||||
}
|
||||
sys_rt_exec_state.bit.cycleStart = false;
|
||||
}
|
||||
if (cycle_stop) {
|
||||
break;
|
||||
case State::Alarm:
|
||||
case State::CheckMode:
|
||||
case State::Sleep:
|
||||
case State::Cycle:
|
||||
case State::Homing:
|
||||
case State::Jog:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void protocol_do_cycle_stop() {
|
||||
rtCycleStop = false;
|
||||
|
||||
switch (sys.state) {
|
||||
case State::Hold:
|
||||
case State::SafetyDoor:
|
||||
case State::Sleep:
|
||||
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
|
||||
// realtime command execution in the main program, ensuring that the planner re-plans safely.
|
||||
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
|
||||
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
|
||||
// NOTE: cycle_stop is set by the stepper subsystem when a cycle or feed hold completes.
|
||||
if ((sys.state == State::Hold || sys.state == State::SafetyDoor || sys.state == State::Sleep) && !(sys.soft_limit) &&
|
||||
!(sys.suspend.bit.jogCancel)) {
|
||||
// NOTE: rtCycleStop is set by the stepper subsystem when a cycle or feed hold completes.
|
||||
if (!sys.soft_limit && !sys.suspend.bit.jogCancel) {
|
||||
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
|
||||
// has issued a resume command or reset.
|
||||
plan_cycle_reinitialize();
|
||||
@@ -428,28 +531,37 @@ void protocol_exec_rt_system() {
|
||||
}
|
||||
sys.step_control.executeHold = false;
|
||||
sys.step_control.executeSysMotion = false;
|
||||
} else {
|
||||
// Motion complete. Includes CYCLE/JOG/HOMING states and jog cancel/motion cancel/soft limit events.
|
||||
// NOTE: Motion and jog cancel both immediately return to idle after the hold completes.
|
||||
if (sys.suspend.bit.jogCancel) { // For jog cancel, flush buffers and sync positions.
|
||||
sys.step_control = {};
|
||||
plan_reset();
|
||||
st_reset();
|
||||
gc_sync_position();
|
||||
plan_sync_position();
|
||||
}
|
||||
if (sys.suspend.bit.safetyDoorAjar) { // Only occurs when safety door opens during jog.
|
||||
sys.suspend.bit.jogCancel = false;
|
||||
sys.suspend.bit.holdComplete = true;
|
||||
sys.state = State::SafetyDoor;
|
||||
} else {
|
||||
sys.suspend.value = 0;
|
||||
sys.state = State::Idle;
|
||||
}
|
||||
break;
|
||||
}
|
||||
cycle_stop = false;
|
||||
}
|
||||
// Fall through
|
||||
case State::Alarm:
|
||||
case State::CheckMode:
|
||||
case State::Idle:
|
||||
case State::Cycle:
|
||||
case State::Homing:
|
||||
case State::Jog:
|
||||
// Motion complete. Includes CYCLE/JOG/HOMING states and jog cancel/motion cancel/soft limit events.
|
||||
// NOTE: Motion and jog cancel both immediately return to idle after the hold completes.
|
||||
if (sys.suspend.bit.jogCancel) { // For jog cancel, flush buffers and sync positions.
|
||||
sys.step_control = {};
|
||||
plan_reset();
|
||||
st_reset();
|
||||
gc_sync_position();
|
||||
plan_sync_position();
|
||||
}
|
||||
if (sys.suspend.bit.safetyDoorAjar) { // Only occurs when safety door opens during jog.
|
||||
sys.suspend.bit.jogCancel = false;
|
||||
sys.suspend.bit.holdComplete = true;
|
||||
sys.state = State::SafetyDoor;
|
||||
} else {
|
||||
sys.suspend.value = 0;
|
||||
sys.state = State::Idle;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void protocol_execute_overrides() {
|
||||
// Execute overrides.
|
||||
if ((sys_rt_f_override != sys.f_override) || (sys_rt_r_override != sys.r_override)) {
|
||||
sys.f_override = sys_rt_f_override;
|
||||
@@ -464,7 +576,7 @@ void protocol_exec_rt_system() {
|
||||
sys.step_control.updateSpindleRpm = true;
|
||||
sys.spindle_speed_ovr = sys_rt_s_override;
|
||||
sys.report_ovr_counter = 0; // Set to report change immediately
|
||||
// If spinlde is on, tell it the rpm has been overridden
|
||||
// If spindle is on, tell it the RPM has been overridden
|
||||
if (gc_state.modal.spindle != SpindleState::Disable) {
|
||||
spindle->set_rpm(gc_state.spindle_speed);
|
||||
}
|
||||
@@ -487,44 +599,366 @@ void protocol_exec_rt_system() {
|
||||
// run state can be determined by checking the parser state.
|
||||
if (sys_rt_exec_accessory_override.bit.coolantFloodOvrToggle) {
|
||||
sys_rt_exec_accessory_override.bit.coolantFloodOvrToggle = false;
|
||||
if (config->_coolant->hasFlood()) {
|
||||
if (sys.state == State::Idle || sys.state == State::Cycle || sys.state == State::Hold) {
|
||||
gc_state.modal.coolant.Flood = !gc_state.modal.coolant.Flood;
|
||||
config->_coolant->set_state(gc_state.modal.coolant); // Report counter set in coolant_set_state().
|
||||
}
|
||||
#ifdef COOLANT_FLOOD_PIN
|
||||
if (sys.state == State::Idle || sys.state == State::Cycle || sys.state == State::Hold) {
|
||||
gc_state.modal.coolant.Flood = !gc_state.modal.coolant.Flood;
|
||||
coolant_set_state(gc_state.modal.coolant); // Report counter set in coolant_set_state().
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (sys_rt_exec_accessory_override.bit.coolantMistOvrToggle) {
|
||||
sys_rt_exec_accessory_override.bit.coolantMistOvrToggle = false;
|
||||
if (config->_coolant->hasMist()) {
|
||||
if (sys.state == State::Idle || sys.state == State::Cycle || sys.state == State::Hold) {
|
||||
gc_state.modal.coolant.Mist = !gc_state.modal.coolant.Mist;
|
||||
config->_coolant->set_state(gc_state.modal.coolant); // Report counter set in coolant_set_state().
|
||||
}
|
||||
#ifdef COOLANT_MIST_PIN
|
||||
if (sys.state == State::Idle || sys.state == State::Cycle || sys.state == State::Hold) {
|
||||
gc_state.modal.coolant.Mist = !gc_state.modal.coolant.Mist;
|
||||
coolant_set_state(gc_state.modal.coolant); // Report counter set in coolant_set_state().
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_exec_rt_system() {
|
||||
protocol_do_alarm(); // If there is a hard or soft limit, this will block until rtReset is set
|
||||
|
||||
if (rtReset) {
|
||||
if (sys.state == State::Homing) {
|
||||
sys_rt_exec_alarm = ExecAlarm::HomingFailReset;
|
||||
}
|
||||
// Execute system abort.
|
||||
sys.abort = true; // Only place this is set true.
|
||||
return; // Nothing else to do but exit.
|
||||
}
|
||||
|
||||
if (rtStatusReport) {
|
||||
rtStatusReport = false;
|
||||
report_realtime_status(CLIENT_ALL);
|
||||
}
|
||||
|
||||
if (rtMotionCancel) {
|
||||
protocol_do_motion_cancel();
|
||||
}
|
||||
|
||||
if (rtFeedHold) {
|
||||
protocol_do_feedhold();
|
||||
}
|
||||
|
||||
if (rtSafetyDoor) {
|
||||
protocol_do_safety_door();
|
||||
}
|
||||
|
||||
if (rtSleep) {
|
||||
protocol_do_sleep();
|
||||
}
|
||||
|
||||
if (rtCycleStart) {
|
||||
protocol_do_cycle_start();
|
||||
}
|
||||
|
||||
if (rtCycleStop) {
|
||||
protocol_do_cycle_stop();
|
||||
}
|
||||
|
||||
protocol_execute_overrides();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (sys_rt_exec_debug) {
|
||||
report_realtime_debug();
|
||||
sys_rt_exec_debug = false;
|
||||
report_realtime_debug();
|
||||
}
|
||||
#endif
|
||||
// Reload step segment buffer
|
||||
switch (sys.state) {
|
||||
case State::Alarm:
|
||||
case State::CheckMode:
|
||||
case State::Idle:
|
||||
case State::Sleep:
|
||||
break;
|
||||
case State::Cycle:
|
||||
case State::Hold:
|
||||
case State::SafetyDoor:
|
||||
case State::Homing:
|
||||
case State::Sleep:
|
||||
case State::Jog:
|
||||
st_prep_buffer();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//void protocol_exec_rt_system() {
|
||||
// ExecAlarm alarm = sys_rt_exec_alarm; // Temp variable to avoid calling volatile multiple times.
|
||||
// if (alarm != ExecAlarm::None) { // Enter only if an alarm is pending
|
||||
// // System alarm. Everything has shutdown by something that has gone severely wrong. Report
|
||||
// // the source of the error to the user. If critical, Grbl disables by entering an infinite
|
||||
// // loop until system reset/abort.
|
||||
// sys.state = State::Alarm; // Set system alarm state
|
||||
// report_alarm_message(alarm);
|
||||
// // Halt everything upon a critical event flag. Currently hard and soft limits flag this.
|
||||
// if ((alarm == ExecAlarm::HardLimit) || (alarm == ExecAlarm::SoftLimit)) {
|
||||
// report_feedback_message(Message::CriticalEvent);
|
||||
// rtReset = false; // Disable any existing reset
|
||||
// do {
|
||||
// // Block everything, except reset and status reports, until user issues reset or power
|
||||
// // cycles. Hard limits typically occur while unattended or not paying attention. Gives
|
||||
// // the user and a GUI time to do what is needed before resetting, like killing the
|
||||
// // incoming stream. The same could be said about soft limits. While the position is not
|
||||
// // lost, continued streaming could cause a serious crash if by chance it gets executed.
|
||||
// } while (!sys_rt_exec_state.bit.reset);
|
||||
// }
|
||||
// sys_rt_exec_alarm = ExecAlarm::None;
|
||||
// }
|
||||
// ExecState rt_exec_state;
|
||||
// rt_exec_state.value = sys_rt_exec_state.value; // Copy volatile sys_rt_exec_state.
|
||||
// if (rt_exec_state.value != 0 || cycle_stop) { // Test if any bits are on
|
||||
// // Execute system abort.
|
||||
// if (rt_exec_state.bit.reset) {
|
||||
// sys.abort = true; // Only place this is set true.
|
||||
// return; // Nothing else to do but exit.
|
||||
// }
|
||||
// // Execute and serial print status
|
||||
// if (rt_exec_state.bit.statusReport) {
|
||||
// report_realtime_status(CLIENT_ALL);
|
||||
// sys_rt_exec_state.bit.statusReport = false;
|
||||
// }
|
||||
// // NOTE: Once hold is initiated, the system immediately enters a suspend state to block all
|
||||
// // main program processes until either reset or resumed. This ensures a hold completes safely.
|
||||
// if (rt_exec_state.bit.motionCancel || rt_exec_state.bit.feedHold || rt_exec_state.bit.safetyDoor || rt_exec_state.bit.sleep) {
|
||||
// // State check for allowable states for hold methods.
|
||||
// if (!(sys.state == State::Alarm || sys.state == State::CheckMode)) {
|
||||
// // If in CYCLE or JOG states, immediately initiate a motion HOLD.
|
||||
// if (sys.state == State::Cycle || sys.state == State::Jog) {
|
||||
// if (!(sys.suspend.bit.motionCancel || sys.suspend.bit.jogCancel)) { // Block, if already holding.
|
||||
// st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
|
||||
// sys.step_control = {};
|
||||
// sys.step_control.executeHold = true; // Initiate suspend state with active flag.
|
||||
// if (sys.state == State::Jog) { // Jog cancelled upon any hold event, except for sleeping.
|
||||
// if (!rt_exec_state.bit.sleep) {
|
||||
// sys.suspend.bit.jogCancel = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
|
||||
// if (sys.state == State::Idle) {
|
||||
// sys.suspend.value = 0;
|
||||
// sys.suspend.bit.holdComplete = true;
|
||||
// }
|
||||
// // Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
|
||||
// // to halt and cancel the remainder of the motion.
|
||||
// if (rt_exec_state.bit.motionCancel) {
|
||||
// // MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
|
||||
// // to hold the CYCLE. Motion cancel is valid for a single planner block motion only, while jog cancel
|
||||
// // will handle and clear multiple planner block motions.
|
||||
// if (sys.state != State::Jog) {
|
||||
// sys.suspend.bit.motionCancel = true; // NOTE: State is State::Cycle.
|
||||
// }
|
||||
// sys_rt_exec_state.bit.motionCancel = false;
|
||||
// }
|
||||
// // Execute a feed hold with deceleration, if required. Then, suspend system.
|
||||
// if (rt_exec_state.bit.feedHold) {
|
||||
// // Block SAFETY_DOOR, JOG, and SLEEP states from changing to HOLD state.
|
||||
// if (!(sys.state == State::SafetyDoor || sys.state == State::Jog || sys.state == State::Sleep)) {
|
||||
// sys.state = State::Hold;
|
||||
// }
|
||||
// sys_rt_exec_state.bit.feedHold = false;
|
||||
// }
|
||||
// // Execute a safety door stop with a feed hold and disable spindle/coolant.
|
||||
// // NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
|
||||
// // devices (spindle/coolant), and blocks resuming until switch is re-engaged.
|
||||
// if (rt_exec_state.bit.safetyDoor) {
|
||||
// report_feedback_message(Message::SafetyDoorAjar);
|
||||
// // If jogging, block safety door methods until jog cancel is complete. Just flag that it happened.
|
||||
// if (!(sys.suspend.bit.jogCancel)) {
|
||||
// // Check if the safety re-opened during a restore parking motion only. Ignore if
|
||||
// // already retracting, parked or in sleep state.
|
||||
// if (sys.state == State::SafetyDoor) {
|
||||
// if (sys.suspend.bit.initiateRestore) { // Actively restoring
|
||||
//#ifdef PARKING_ENABLE
|
||||
// // Set hold and reset appropriate control flags to restart parking sequence.
|
||||
// if (sys.step_control.executeSysMotion) {
|
||||
// st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
|
||||
// sys.step_control = {};
|
||||
// sys.step_control.executeHold = true;
|
||||
// sys.step_control.executeSysMotion = true;
|
||||
// sys.suspend.bit.holdComplete = false;
|
||||
// } // else NO_MOTION is active.
|
||||
//#endif
|
||||
// sys.suspend.bit.retractComplete = false;
|
||||
// sys.suspend.bit.initiateRestore = false;
|
||||
// sys.suspend.bit.restoreComplete = false;
|
||||
// sys.suspend.bit.restartRetract = true;
|
||||
// }
|
||||
// }
|
||||
// if (sys.state != State::Sleep) {
|
||||
// sys.state = State::SafetyDoor;
|
||||
// }
|
||||
// sys_rt_exec_state.bit.safetyDoor = false;
|
||||
// }
|
||||
// // NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
|
||||
// // are executed if the door switch closes and the state returns to HOLD.
|
||||
// sys.suspend.bit.safetyDoorAjar = true;
|
||||
// }
|
||||
// }
|
||||
// if (rt_exec_state.bit.sleep) {
|
||||
// if (sys.state == State::Alarm) {
|
||||
// sys.suspend.bit.retractComplete = true;
|
||||
// sys.suspend.bit.holdComplete = true;
|
||||
// }
|
||||
// sys.state = State::Sleep;
|
||||
// sys_rt_exec_state.bit.sleep = false;
|
||||
// }
|
||||
// }
|
||||
// // Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
|
||||
// if (rt_exec_state.bit.cycleStart) {
|
||||
// // Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
|
||||
// // Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
|
||||
// if (!(rt_exec_state.bit.feedHold || rt_exec_state.bit.motionCancel || rt_exec_state.bit.safetyDoor)) {
|
||||
// // Resume door state when parking motion has retracted and door has been closed.
|
||||
// if (sys.state == State::SafetyDoor && !(sys.suspend.bit.safetyDoorAjar)) {
|
||||
// if (sys.suspend.bit.restoreComplete) {
|
||||
// sys.state = State::Idle; // Set to IDLE to immediately resume the cycle.
|
||||
// } else if (sys.suspend.bit.retractComplete) {
|
||||
// // Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
|
||||
// // NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
|
||||
// // the retraction execution is complete, which implies the initial feed hold is not active. To
|
||||
// // restore normal operation, the restore procedures must be initiated by the following flag. Once,
|
||||
// // they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
|
||||
// sys.suspend.bit.initiateRestore = true;
|
||||
// }
|
||||
// }
|
||||
// // Cycle start only when IDLE or when a hold is complete and ready to resume.
|
||||
// if (sys.state == State::Idle || (sys.state == State::Hold && sys.suspend.bit.holdComplete)) {
|
||||
// if (sys.state == State::Hold && sys.spindle_stop_ovr.value) {
|
||||
// sys.spindle_stop_ovr.bit.restoreCycle = true; // Set to restore in suspend routine and cycle start after.
|
||||
// } else {
|
||||
// // Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
|
||||
// sys.step_control = {}; // Restore step control to normal operation
|
||||
// if (plan_get_current_block() && !sys.suspend.bit.motionCancel) {
|
||||
// sys.suspend.value = 0; // Break suspend state.
|
||||
// sys.state = State::Cycle;
|
||||
// st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
|
||||
// st_wake_up();
|
||||
// } else { // Otherwise, do nothing. Set and resume IDLE state.
|
||||
// sys.suspend.value = 0; // Break suspend state.
|
||||
// sys.state = State::Idle;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// sys_rt_exec_state.bit.cycleStart = false;
|
||||
// }
|
||||
// if (cycle_stop) {
|
||||
// // Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
|
||||
// // realtime command execution in the main program, ensuring that the planner re-plans safely.
|
||||
// // NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
|
||||
// // cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
|
||||
// // NOTE: cycle_stop is set by the stepper subsystem when a cycle or feed hold completes.
|
||||
// if ((sys.state == State::Hold || sys.state == State::SafetyDoor || sys.state == State::Sleep) && !(sys.soft_limit) &&
|
||||
// !(sys.suspend.bit.jogCancel)) {
|
||||
// // Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
|
||||
// // has issued a resume command or reset.
|
||||
// plan_cycle_reinitialize();
|
||||
// if (sys.step_control.executeHold) {
|
||||
// sys.suspend.bit.holdComplete = true;
|
||||
// }
|
||||
// sys.step_control.executeHold = false;
|
||||
// sys.step_control.executeSysMotion = false;
|
||||
// } else {
|
||||
// // Motion complete. Includes CYCLE/JOG/HOMING states and jog cancel/motion cancel/soft limit events.
|
||||
// // NOTE: Motion and jog cancel both immediately return to idle after the hold completes.
|
||||
// if (sys.suspend.bit.jogCancel) { // For jog cancel, flush buffers and sync positions.
|
||||
// sys.step_control = {};
|
||||
// plan_reset();
|
||||
// st_reset();
|
||||
// gc_sync_position();
|
||||
// plan_sync_position();
|
||||
// }
|
||||
// if (sys.suspend.bit.safetyDoorAjar) { // Only occurs when safety door opens during jog.
|
||||
// sys.suspend.bit.jogCancel = false;
|
||||
// sys.suspend.bit.holdComplete = true;
|
||||
// sys.state = State::SafetyDoor;
|
||||
// } else {
|
||||
// sys.suspend.value = 0;
|
||||
// sys.state = State::Idle;
|
||||
// }
|
||||
// }
|
||||
// cycle_stop = false;
|
||||
// }
|
||||
// }
|
||||
// // Execute overrides.
|
||||
// if ((sys_rt_f_override != sys.f_override) || (sys_rt_r_override != sys.r_override)) {
|
||||
// sys.f_override = sys_rt_f_override;
|
||||
// sys.r_override = sys_rt_r_override;
|
||||
// sys.report_ovr_counter = 0; // Set to report change immediately
|
||||
// plan_update_velocity_profile_parameters();
|
||||
// plan_cycle_reinitialize();
|
||||
// }
|
||||
//
|
||||
// // NOTE: Unlike motion overrides, spindle overrides do not require a planner reinitialization.
|
||||
// if (sys_rt_s_override != sys.spindle_speed_ovr) {
|
||||
// sys.step_control.updateSpindleRpm = true;
|
||||
// sys.spindle_speed_ovr = sys_rt_s_override;
|
||||
// sys.report_ovr_counter = 0; // Set to report change immediately
|
||||
// // If spinlde is on, tell it the rpm has been overridden
|
||||
// if (gc_state.modal.spindle != SpindleState::Disable) {
|
||||
// spindle->set_rpm(gc_state.spindle_speed);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (sys_rt_exec_accessory_override.bit.spindleOvrStop) {
|
||||
// sys_rt_exec_accessory_override.bit.spindleOvrStop = false;
|
||||
// // Spindle stop override allowed only while in HOLD state.
|
||||
// // NOTE: Report counters are set in spindle_set_state() when spindle stop is executed.
|
||||
// if (sys.state == State::Hold) {
|
||||
// if (sys.spindle_stop_ovr.value == 0) {
|
||||
// sys.spindle_stop_ovr.bit.initiate = true;
|
||||
// } else if (sys.spindle_stop_ovr.bit.enabled) {
|
||||
// sys.spindle_stop_ovr.bit.restore = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // NOTE: Since coolant state always performs a planner sync whenever it changes, the current
|
||||
// // run state can be determined by checking the parser state.
|
||||
// if (sys_rt_exec_accessory_override.bit.coolantFloodOvrToggle) {
|
||||
// sys_rt_exec_accessory_override.bit.coolantFloodOvrToggle = false;
|
||||
// if (config->_coolant->hasFlood()) {
|
||||
// if (sys.state == State::Idle || sys.state == State::Cycle || sys.state == State::Hold) {
|
||||
// gc_state.modal.coolant.Flood = !gc_state.modal.coolant.Flood;
|
||||
// config->_coolant->set_state(gc_state.modal.coolant); // Report counter set in coolant_set_state().
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (sys_rt_exec_accessory_override.bit.coolantMistOvrToggle) {
|
||||
// sys_rt_exec_accessory_override.bit.coolantMistOvrToggle = false;
|
||||
// if (config->_coolant->hasMist()) {
|
||||
// if (sys.state == State::Idle || sys.state == State::Cycle || sys.state == State::Hold) {
|
||||
// gc_state.modal.coolant.Mist = !gc_state.modal.coolant.Mist;
|
||||
// config->_coolant->set_state(gc_state.modal.coolant); // Report counter set in coolant_set_state().
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//#ifdef DEBUG
|
||||
// if (sys_rt_exec_debug) {
|
||||
// report_realtime_debug();
|
||||
// sys_rt_exec_debug = false;
|
||||
// }
|
||||
//#endif
|
||||
// // Reload step segment buffer
|
||||
// switch (sys.state) {
|
||||
// case State::Cycle:
|
||||
// case State::Hold:
|
||||
// case State::SafetyDoor:
|
||||
// case State::Homing:
|
||||
// case State::Sleep:
|
||||
// case State::Jog:
|
||||
// st_prep_buffer();
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
// Handles Grbl system suspend procedures, such as feed hold, safety door, and parking motion.
|
||||
// The system will enter this loop, create local variables for suspend tasks, and return to
|
||||
// whatever function that invoked the suspend, such that Grbl resumes normal operation.
|
||||
@@ -648,7 +1082,7 @@ static void protocol_exec_rt_suspend() {
|
||||
}
|
||||
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume.
|
||||
if (sys.state == State::SafetyDoor) {
|
||||
if (!(system_check_safety_door_ajar())) {
|
||||
if (!config->_control->system_check_safety_door_ajar()) {
|
||||
sys.suspend.bit.safetyDoorAjar = false; // Reset door ajar flag to denote ready to resume.
|
||||
}
|
||||
}
|
||||
@@ -702,8 +1136,8 @@ static void protocol_exec_rt_suspend() {
|
||||
}
|
||||
#endif
|
||||
if (!sys.suspend.bit.restartRetract) {
|
||||
sys.suspend.bit.restoreComplete = true;
|
||||
sys_rt_exec_state.bit.cycleStart = true; // Set to resume program.
|
||||
sys.suspend.bit.restoreComplete = true;
|
||||
rtCycleStart = true; // Set to resume program.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -732,7 +1166,7 @@ static void protocol_exec_rt_suspend() {
|
||||
}
|
||||
}
|
||||
if (sys.spindle_stop_ovr.bit.restoreCycle) {
|
||||
sys_rt_exec_state.bit.cycleStart = true; // Set to resume program.
|
||||
rtCycleStart = true; // Set to resume program.
|
||||
}
|
||||
sys.spindle_stop_ovr.value = 0; // Clear stop override state
|
||||
}
|
||||
|
@@ -578,6 +578,16 @@ void report_echo_line_received(char* line, uint8_t client) {
|
||||
// float wco = returns the work coordinate offset
|
||||
// bool wpos = true for work position compensation
|
||||
|
||||
void addPinReport(char* status, char pinLetter, bool& pinReportStarted) {
|
||||
if (!pinReportStarted) {
|
||||
strcat(status, "|Pn:");
|
||||
pinReportStarted = true;
|
||||
}
|
||||
size_t pos = strlen(status);
|
||||
status[pos] = pinLetter;
|
||||
status[pos + 1] = '\0';
|
||||
}
|
||||
|
||||
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram
|
||||
// and the actual location of the CNC machine. Users may change the following function to their
|
||||
// specific needs, but the desired real-time data report must be as short as possible. This is
|
||||
@@ -643,62 +653,23 @@ void report_realtime_status(uint8_t client) {
|
||||
strcat(status, temp);
|
||||
#endif
|
||||
#ifdef REPORT_FIELD_PIN_STATE
|
||||
AxisMask lim_pin_state = limits_get_state();
|
||||
ControlPins ctrl_pin_state = system_control_get_state();
|
||||
bool prb_pin_state = config->_probe->get_state();
|
||||
if (lim_pin_state || ctrl_pin_state.value || prb_pin_state) {
|
||||
strcat(status, "|Pn:");
|
||||
if (prb_pin_state) {
|
||||
strcat(status, "P");
|
||||
}
|
||||
if (lim_pin_state) {
|
||||
auto n_axis = config->_axes->_numberAxis;
|
||||
if (n_axis >= 1 && bit_istrue(lim_pin_state, bit(X_AXIS))) {
|
||||
strcat(status, "X");
|
||||
}
|
||||
if (n_axis >= 2 && bit_istrue(lim_pin_state, bit(Y_AXIS))) {
|
||||
strcat(status, "Y");
|
||||
}
|
||||
if (n_axis >= 3 && bit_istrue(lim_pin_state, bit(Z_AXIS))) {
|
||||
strcat(status, "Z");
|
||||
}
|
||||
if (n_axis >= 4 && bit_istrue(lim_pin_state, bit(A_AXIS))) {
|
||||
strcat(status, "A");
|
||||
}
|
||||
if (n_axis >= 5 && bit_istrue(lim_pin_state, bit(B_AXIS))) {
|
||||
strcat(status, "B");
|
||||
}
|
||||
if (n_axis >= 6 && bit_istrue(lim_pin_state, bit(C_AXIS))) {
|
||||
strcat(status, "C");
|
||||
}
|
||||
}
|
||||
if (ctrl_pin_state.value) {
|
||||
if (ctrl_pin_state.bit.safetyDoor) {
|
||||
strcat(status, "D");
|
||||
}
|
||||
if (ctrl_pin_state.bit.reset) {
|
||||
strcat(status, "R");
|
||||
}
|
||||
if (ctrl_pin_state.bit.feedHold) {
|
||||
strcat(status, "H");
|
||||
}
|
||||
if (ctrl_pin_state.bit.cycleStart) {
|
||||
strcat(status, "S");
|
||||
}
|
||||
if (ctrl_pin_state.bit.macro0) {
|
||||
strcat(status, "0");
|
||||
}
|
||||
if (ctrl_pin_state.bit.macro1) {
|
||||
strcat(status, "1");
|
||||
}
|
||||
if (ctrl_pin_state.bit.macro2) {
|
||||
strcat(status, "2");
|
||||
}
|
||||
if (ctrl_pin_state.bit.macro3) {
|
||||
strcat(status, "3");
|
||||
AxisMask lim_pin_state = limits_get_state();
|
||||
bool prb_pin_state = config->_probe->get_state();
|
||||
bool pinReportStarted = false;
|
||||
if (prb_pin_state) {
|
||||
addPinReport(status, 'P', pinReportStarted);
|
||||
}
|
||||
if (lim_pin_state) {
|
||||
auto n_axis = config->_axes->_numberAxis;
|
||||
for (int i = 0; i < n_axis; i++) {
|
||||
if (bit_istrue(lim_pin_state, bit(i))) {
|
||||
addPinReport(status, "XYZABC"[i], pinReportStarted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config->_control->report(status, pinReportStarted);
|
||||
|
||||
#endif
|
||||
#ifdef REPORT_FIELD_WORK_COORD_OFFSET
|
||||
if (sys.report_wco_counter > 0) {
|
||||
|
@@ -232,17 +232,17 @@ void execute_realtime_command(Cmd command, uint8_t client) {
|
||||
report_realtime_status(client); // direct call instead of setting flag
|
||||
break;
|
||||
case Cmd::CycleStart:
|
||||
sys_rt_exec_state.bit.cycleStart = true;
|
||||
rtCycleStart = true;
|
||||
break;
|
||||
case Cmd::FeedHold:
|
||||
sys_rt_exec_state.bit.feedHold = true;
|
||||
rtFeedHold = true;
|
||||
break;
|
||||
case Cmd::SafetyDoor:
|
||||
sys_rt_exec_state.bit.safetyDoor = true;
|
||||
rtSafetyDoor = true;
|
||||
break;
|
||||
case Cmd::JogCancel:
|
||||
if (sys.state == State::Jog) { // Block all other states from invoking motion cancel.
|
||||
sys_rt_exec_state.bit.motionCancel = true;
|
||||
rtMotionCancel = true;
|
||||
}
|
||||
break;
|
||||
case Cmd::DebugReport:
|
||||
|
@@ -295,7 +295,7 @@ static void stepper_pulse_func() {
|
||||
spindle->set_rpm(0);
|
||||
}
|
||||
}
|
||||
cycle_stop = true;
|
||||
rtCycleStop = true;
|
||||
return; // Nothing to do but exit.
|
||||
}
|
||||
}
|
||||
|
@@ -29,10 +29,20 @@ system_t sys;
|
||||
int32_t sys_position[MAX_N_AXIS]; // Real-time machine (aka home) position vector in steps.
|
||||
int32_t sys_probe_position[MAX_N_AXIS]; // Last probe position in machine coordinates and steps.
|
||||
volatile ProbeState sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
|
||||
volatile ExecState sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
|
||||
volatile ExecAlarm sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
|
||||
volatile ExecAlarm sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
|
||||
volatile ExecAccessory sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
|
||||
volatile bool cycle_stop; // For state transitions, instead of bitflag
|
||||
volatile bool rtStatusReport;
|
||||
volatile bool rtCycleStart;
|
||||
volatile bool rtFeedHold;
|
||||
volatile bool rtReset;
|
||||
volatile bool rtSafetyDoor;
|
||||
volatile bool rtMotionCancel;
|
||||
volatile bool rtSleep;
|
||||
volatile bool rtCycleStop; // For state transitions, instead of bitflag
|
||||
volatile bool rtButtonMacro0;
|
||||
volatile bool rtButtonMacro1;
|
||||
volatile bool rtButtonMacro2;
|
||||
volatile bool rtButtonMacro3;
|
||||
volatile void* sys_pl_data_inflight; // holds a plan_line_data_t while cartesian_to_motors has taken ownership of a line motion
|
||||
#ifdef DEBUG
|
||||
volatile bool sys_rt_exec_debug;
|
||||
@@ -47,164 +57,24 @@ UserOutput::DigitalOutput* myDigitalOutputs[MaxUserDigitalPin];
|
||||
xQueueHandle control_sw_queue; // used by control switch debouncing
|
||||
bool debouncing = false; // debouncing in process
|
||||
|
||||
void system_ini() { // Renamed from system_init() due to conflict with esp32 files
|
||||
// setup control inputs
|
||||
|
||||
if (ControlSafetyDoorPin->get().defined()) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Door switch on pin %s", ControlSafetyDoorPin->getStringValue());
|
||||
auto pin = ControlSafetyDoorPin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
if (ControlResetPin->get().defined()) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Reset switch on pin %s", ControlResetPin->getStringValue());
|
||||
auto pin = ControlResetPin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
if (ControlFeedHoldPin->get().defined()) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Hold switch on pin %s", ControlFeedHoldPin->getStringValue());
|
||||
auto pin = ControlFeedHoldPin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
if (ControlCycleStartPin->get().defined()) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Start switch on pin %s", ControlCycleStartPin->getStringValue());
|
||||
auto pin = ControlCycleStartPin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
if (MacroButton0Pin->get().defined()) {
|
||||
auto pin = MacroButton0Pin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
if (MacroButton1Pin->get().defined()) {
|
||||
auto pin = MacroButton1Pin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
if (MacroButton2Pin->get().defined()) {
|
||||
auto pin = MacroButton2Pin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
if (MacroButton3Pin->get().defined()) {
|
||||
auto pin = MacroButton3Pin->get();
|
||||
auto attr = Pin::Attr::Input | Pin::Attr::ISR;
|
||||
if (pin.capabilities().has(Pins::PinCapabilities::PullUp)) {
|
||||
attr = attr | Pin::Attr::PullUp;
|
||||
}
|
||||
pin.setAttr(attr);
|
||||
pin.attachInterrupt(isr_control_inputs, CHANGE);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CONTROL_SW_DEBOUNCE
|
||||
// setup task used for debouncing
|
||||
control_sw_queue = xQueueCreate(10, sizeof(int));
|
||||
xTaskCreate(controlCheckTask,
|
||||
"controlCheckTask",
|
||||
3096,
|
||||
NULL,
|
||||
5, // priority
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
void init_output_pins() {
|
||||
//customize pin definition if needed
|
||||
#if (GRBL_SPI_SS != -1) || (GRBL_SPI_MISO != -1) || (GRBL_SPI_MOSI != -1) || (GRBL_SPI_SCK != -1)
|
||||
SPI.begin(GRBL_SPI_SCK, GRBL_SPI_MISO, GRBL_SPI_MOSI, GRBL_SPI_SS);
|
||||
#endif
|
||||
|
||||
auto userOutputs = config->_userOutputs;
|
||||
|
||||
// Setup M62,M63,M64,M65 pins
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
myDigitalOutputs[i] = new UserOutput::DigitalOutput(i, UserDigitalPin[i]->get());
|
||||
myDigitalOutputs[i] = new UserOutput::DigitalOutput(i, userOutputs->_digitalOutput[i]);
|
||||
}
|
||||
|
||||
// Setup M67 Pins
|
||||
myAnalogOutputs[0] = new UserOutput::AnalogOutput(0, UserAnalogPin[0]->get(), USER_ANALOG_PIN_0_FREQ);
|
||||
myAnalogOutputs[1] = new UserOutput::AnalogOutput(1, UserAnalogPin[1]->get(), USER_ANALOG_PIN_1_FREQ);
|
||||
myAnalogOutputs[2] = new UserOutput::AnalogOutput(2, UserAnalogPin[2]->get(), USER_ANALOG_PIN_2_FREQ);
|
||||
myAnalogOutputs[3] = new UserOutput::AnalogOutput(3, UserAnalogPin[3]->get(), USER_ANALOG_PIN_3_FREQ);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CONTROL_SW_DEBOUNCE
|
||||
// this is the debounce task
|
||||
void controlCheckTask(void* pvParameters) {
|
||||
while (true) {
|
||||
std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // read fence for settings and other state
|
||||
int evt;
|
||||
xQueueReceive(control_sw_queue, &evt, portMAX_DELAY); // block until receive queue
|
||||
vTaskDelay(CONTROL_SW_DEBOUNCE_PERIOD); // delay a while
|
||||
ControlPins pins = system_control_get_state();
|
||||
if (pins.value) {
|
||||
system_exec_control_pin(pins);
|
||||
}
|
||||
debouncing = false;
|
||||
|
||||
static UBaseType_t uxHighWaterMark = 0;
|
||||
# ifdef DEBUG_TASK_STACK
|
||||
reportTaskStackSize(uxHighWaterMark);
|
||||
# endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void IRAM_ATTR isr_control_inputs(void*) {
|
||||
#ifdef ENABLE_CONTROL_SW_DEBOUNCE
|
||||
// we will start a task that will recheck the switches after a small delay
|
||||
int evt;
|
||||
if (!debouncing) { // prevent resending until debounce is done
|
||||
debouncing = true;
|
||||
xQueueSendFromISR(control_sw_queue, &evt, NULL);
|
||||
}
|
||||
#else
|
||||
ControlPins pins = system_control_get_state();
|
||||
system_exec_control_pin(pins);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns if safety door is ajar(T) or closed(F), based on pin state.
|
||||
uint8_t system_check_safety_door_ajar() {
|
||||
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
|
||||
return system_control_get_state().bit.safetyDoor;
|
||||
#else
|
||||
return false; // Input pin not enabled, so just return that it's closed.
|
||||
#endif
|
||||
myAnalogOutputs[0] = new UserOutput::AnalogOutput(0, userOutputs->_analogOutput[0], USER_ANALOG_PIN_0_FREQ);
|
||||
myAnalogOutputs[1] = new UserOutput::AnalogOutput(1, userOutputs->_analogOutput[1], USER_ANALOG_PIN_1_FREQ);
|
||||
myAnalogOutputs[2] = new UserOutput::AnalogOutput(2, userOutputs->_analogOutput[2], USER_ANALOG_PIN_2_FREQ);
|
||||
myAnalogOutputs[3] = new UserOutput::AnalogOutput(3, userOutputs->_analogOutput[3], USER_ANALOG_PIN_3_FREQ);
|
||||
}
|
||||
|
||||
void system_flag_wco_change() {
|
||||
@@ -236,85 +106,6 @@ float* system_get_mpos() {
|
||||
return position;
|
||||
};
|
||||
|
||||
// Returns control pin state as a uint8 bitfield. Each bit indicates the input pin state, where
|
||||
// triggered is 1 and not triggered is 0. Invert mask is applied. Bitfield organization is
|
||||
// defined by the ControlPin in System.h.
|
||||
ControlPins system_control_get_state() {
|
||||
ControlPins defined_pins;
|
||||
defined_pins.value = 0;
|
||||
|
||||
ControlPins pin_states;
|
||||
pin_states.value = 0;
|
||||
|
||||
defined_pins.bit.safetyDoor = ControlSafetyDoorPin->get().defined();
|
||||
if (ControlSafetyDoorPin->get().read()) {
|
||||
pin_states.bit.safetyDoor = true;
|
||||
}
|
||||
|
||||
defined_pins.bit.reset = ControlResetPin->get().defined();
|
||||
if (ControlResetPin->get().read()) {
|
||||
pin_states.bit.reset = true;
|
||||
}
|
||||
|
||||
defined_pins.bit.feedHold = ControlFeedHoldPin->get().defined();
|
||||
if (ControlFeedHoldPin->get().read()) {
|
||||
pin_states.bit.feedHold = true;
|
||||
}
|
||||
|
||||
defined_pins.bit.cycleStart = ControlCycleStartPin->get().defined();
|
||||
if (ControlCycleStartPin->get().read()) {
|
||||
pin_states.bit.cycleStart = true;
|
||||
}
|
||||
|
||||
defined_pins.bit.macro0 = MacroButton0Pin->get().defined();
|
||||
if (MacroButton0Pin->get().read()) {
|
||||
pin_states.bit.macro0 = true;
|
||||
}
|
||||
|
||||
defined_pins.bit.macro1 = MacroButton1Pin->get().defined();
|
||||
if (MacroButton1Pin->get().read()) {
|
||||
pin_states.bit.macro1 = true;
|
||||
}
|
||||
|
||||
defined_pins.bit.macro2 = MacroButton2Pin->get().defined();
|
||||
if (MacroButton2Pin->get().read()) {
|
||||
pin_states.bit.macro2 = true;
|
||||
}
|
||||
|
||||
defined_pins.bit.macro3 = MacroButton3Pin->get().defined();
|
||||
if (MacroButton3Pin->get().read()) {
|
||||
pin_states.bit.macro3 = true;
|
||||
}
|
||||
|
||||
#ifdef INVERT_CONTROL_PIN_MASK
|
||||
pin_states.value ^= (INVERT_CONTROL_PIN_MASK & defined_pins.value);
|
||||
#endif
|
||||
return pin_states;
|
||||
}
|
||||
|
||||
// TODO: make this work with Control.cpp
|
||||
// execute the function of the control pin
|
||||
void system_exec_control_pin(ControlPins pins) {
|
||||
if (pins.bit.reset) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Reset via control pin");
|
||||
mc_reset();
|
||||
} else if (pins.bit.cycleStart) {
|
||||
sys_rt_exec_state.bit.cycleStart = true;
|
||||
} else if (pins.bit.feedHold) {
|
||||
sys_rt_exec_state.bit.feedHold = true;
|
||||
} else if (pins.bit.safetyDoor) {
|
||||
sys_rt_exec_state.bit.safetyDoor = true;
|
||||
} else if (pins.bit.macro0) {
|
||||
user_defined_macro(0); // function must be implemented by user
|
||||
} else if (pins.bit.macro1) {
|
||||
user_defined_macro(1); // function must be implemented by user
|
||||
} else if (pins.bit.macro2) {
|
||||
user_defined_macro(2); // function must be implemented by user
|
||||
} else if (pins.bit.macro3) {
|
||||
user_defined_macro(3); // function must be implemented by user
|
||||
}
|
||||
}
|
||||
|
||||
void sys_digital_all_off() {
|
||||
for (uint8_t io_num = 0; io_num < MaxUserDigitalPin; io_num++) {
|
||||
myDigitalOutputs[io_num]->set_level(LOW);
|
||||
|
@@ -114,48 +114,34 @@ typedef struct {
|
||||
} system_t;
|
||||
extern system_t sys;
|
||||
|
||||
// Control pin states
|
||||
struct ControlPinBits {
|
||||
uint8_t safetyDoor : 1;
|
||||
uint8_t reset : 1;
|
||||
uint8_t feedHold : 1;
|
||||
uint8_t cycleStart : 1;
|
||||
uint8_t macro0 : 1;
|
||||
uint8_t macro1 : 1;
|
||||
uint8_t macro2 : 1;
|
||||
uint8_t macro3 : 1;
|
||||
};
|
||||
union ControlPins {
|
||||
uint8_t value;
|
||||
ControlPinBits bit;
|
||||
};
|
||||
|
||||
// NOTE: These position variables may need to be declared as volatiles, if problems arise.
|
||||
extern int32_t sys_position[MAX_N_AXIS]; // Real-time machine (aka home) position vector in steps.
|
||||
extern int32_t sys_probe_position[MAX_N_AXIS]; // Last probe position in machine coordinates and steps.
|
||||
|
||||
extern volatile ProbeState sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
|
||||
extern volatile ExecState sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
|
||||
extern volatile ExecAlarm sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
|
||||
extern volatile ExecAccessory sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
|
||||
extern volatile Percent sys_rt_f_override; // Feed override value in percent
|
||||
extern volatile Percent sys_rt_r_override; // Rapid feed override value in percent
|
||||
extern volatile Percent sys_rt_s_override; // Spindle override value in percent
|
||||
extern volatile bool cycle_stop;
|
||||
extern volatile bool rtStatusReport;
|
||||
extern volatile bool rtCycleStart;
|
||||
extern volatile bool rtFeedHold;
|
||||
extern volatile bool rtReset;
|
||||
extern volatile bool rtSafetyDoor;
|
||||
extern volatile bool rtMotionCancel;
|
||||
extern volatile bool rtSleep;
|
||||
extern volatile bool rtCycleStop;
|
||||
extern volatile bool rtButtonMacro0;
|
||||
extern volatile bool rtButtonMacro1;
|
||||
extern volatile bool rtButtonMacro2;
|
||||
extern volatile bool rtButtonMacro3;
|
||||
extern volatile void* sys_pl_data_inflight; // holds a plan_line_data_t while cartesian_to_motors has taken ownership of a line motion
|
||||
#ifdef DEBUG
|
||||
extern volatile bool sys_rt_exec_debug;
|
||||
#endif
|
||||
|
||||
void system_ini(); // Renamed from system_init() due to conflict with esp32 files
|
||||
|
||||
// Returns bitfield of control pin states, organized by CONTROL_PIN_INDEX. (1=triggered, 0=not triggered).
|
||||
ControlPins system_control_get_state();
|
||||
|
||||
// Returns if safety door is ajar(T) or closed(F), based on pin state.
|
||||
uint8_t system_check_safety_door_ajar();
|
||||
|
||||
void isr_control_inputs(void*);
|
||||
void init_output_pins(); // Renamed from system_init() due to conflict with esp32 files
|
||||
|
||||
// Execute the startup script lines stored in non-volatile storage upon initialization
|
||||
void system_execute_startup(char* line);
|
||||
@@ -172,10 +158,6 @@ float system_convert_axis_steps_to_mpos(int32_t* steps, uint8_t idx);
|
||||
void system_convert_array_steps_to_mpos(float* position, int32_t* steps);
|
||||
float* system_get_mpos();
|
||||
|
||||
// A task that runs after a control switch interrupt for debouncing.
|
||||
void controlCheckTask(void* pvParameters);
|
||||
void system_exec_control_pin(ControlPins pins);
|
||||
|
||||
bool sys_set_digital(uint8_t io_num, bool turnOn);
|
||||
void sys_digital_all_off();
|
||||
bool sys_set_analog(uint8_t io_num, float percent);
|
||||
|
Reference in New Issue
Block a user