Skip to content

Commit

Permalink
Add UserInputs and basic structure for M66 in GCode
Browse files Browse the repository at this point in the history
  • Loading branch information
dymk committed Sep 24, 2024
1 parent 86d131b commit 66227f2
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 22 deletions.
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
102 changes: 102 additions & 0 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 @@ -696,6 +700,10 @@ Error gc_execute_line(char* line) {
gc_block.modal.io_control = IoControl::DigitalOffImmediate;
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::MM5;
Expand Down Expand Up @@ -1002,6 +1010,42 @@ 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);
}
if (bitnum_is_false(value_words, GCodeWord::L)) {
FAIL(Error::GcodeValueWordMissing);
}
auto const wait_mode = validate_wait_on_input_mode_value(gc_block.values.l);
if (!wait_mode) {
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);
}
}
}
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 +1700,30 @@ Error gc_execute_line(char* line) {
FAIL(Error::PParamMaxExceeded);
}
}
if (gc_block.modal.io_control == IoControl::WaitOnInput) {
bool const is_digital = bitnum_is_true(value_words, GCodeWord::P); // E implies analog
auto const validate_input_number = [&](const float input_number) -> std::optional<uint8_t> {
if (input_number < 0) {
return std::nullopt;
}
if (is_digital) {
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(is_digital ? 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(is_digital, 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 +1985,37 @@ 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) {
if (mode == WaitOnInputMode::Immediate) {
auto const result = is_digital ? config->_userInputs->readDigitalInput(input_number) :
config->_userInputs->readAnalogInput(input_number);
return std::visit(overloaded { [&](bool result) { return Error::Ok; }, [&](Error error) { return error; } }, result);
}

return Error::GcodeValueWordInvalid;
}
25 changes: 18 additions & 7 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 Down Expand Up @@ -155,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 @@ -270,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);

ModuleFactory::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
58 changes: 58 additions & 0 deletions FluidNC/src/Machine/UserInputs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) 2024 - Dylan Knutson
// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file.

#include "UserInputs.h"

namespace Machine {
UserInputs::UserInputs() {}
UserInputs::~UserInputs() {}

void UserInputs::group(Configuration::HandlerBase& handler) {
char item_name[64];
for (size_t i = 0; i < MaxUserAnalogPin; i++) {
snprintf(item_name, sizeof(item_name), "analog%d_pin", i);
handler.item(item_name, _analogInput[i]);
}
for (size_t i = 0; i < MaxUserDigitalPin; i++) {
snprintf(item_name, sizeof(item_name), "digital%d_pin", i);
handler.item(item_name, _digitalInput[i]);
}
}

void UserInputs::init() {
for (auto& input : _digitalInput) {
if (input.defined()) {
input.setAttr(Pin::Attr::Input);
}
}
for (auto& input : _analogInput) {
if (input.defined()) {
input.setAttr(Pin::Attr::Input);
}
}
}

UserInputs::ReadInputResult UserInputs::readDigitalInput(uint8_t input_number) {
if (input_number >= MaxUserDigitalPin) {
return Error::PParamMaxExceeded;
}
auto& pin = _digitalInput[input_number];
if (!pin.defined()) {
return Error::InvalidValue;
}
return pin.read();
}

UserInputs::ReadInputResult UserInputs::readAnalogInput(uint8_t input_number) {
// TODO - analog pins are read the same as digital.
if (input_number >= MaxUserAnalogPin) {
return Error::PParamMaxExceeded;
}
auto& pin = _analogInput[input_number];
if (!pin.defined()) {
return Error::InvalidValue;
}
return pin.read();
}

} // namespace Machine
34 changes: 34 additions & 0 deletions FluidNC/src/Machine/UserInputs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2024 - Dylan Knutson
// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file.

#pragma once

#include "../Configuration/Configurable.h"
#include "../GCode.h"

#include <variant>
#include <array>

namespace Machine {

class UserInputs : public Configuration::Configurable {
std::array<Pin, MaxUserDigitalPin> _digitalInput;

// TODO - analog pins are read the same as digital. The Pin
// API should either be extended to support analog reads, or
// a new AnalogPin class should be created.
std::array<Pin, MaxUserAnalogPin> _analogInput;

public:
UserInputs();
virtual ~UserInputs();

void init();
void group(Configuration::HandlerBase& handler) override;

using ReadInputResult = std::variant<bool, Error>;
ReadInputResult readDigitalInput(uint8_t input_number);
ReadInputResult readAnalogInput(uint8_t input_number);
};

} // namespace Machine
2 changes: 1 addition & 1 deletion FluidNC/src/Machine/UserOutputs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ namespace Machine {
bool setDigital(size_t io_num, bool isOn);
bool setAnalogPercent(size_t io_num, float percent);

~UserOutputs();
virtual ~UserOutputs();
};
}
2 changes: 2 additions & 0 deletions FluidNC/src/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ void setup() {

config->_userOutputs->init();

config->_userInputs->init();

config->_axes->init();

config->_control->init();
Expand Down

0 comments on commit 66227f2

Please sign in to comment.