Skip to content

Commit

Permalink
Plasma spindle (#1449)
Browse files Browse the repository at this point in the history
* Initial commit

* Adding arc detect event

* WIP

* Revert "WIP"

This reverts commit 692214a.

* Conditionally send an alarm instead of a plain event

* Send AbortCycle alarm only if the plasma arc is on

* Tested and removed output_pin

* Add arc_ok pre-check

---------

Co-authored-by: Mitch Bradley <[email protected]>
  • Loading branch information
bdring and MitchBradley authored Mar 8, 2025
1 parent 5b577be commit 22c09f4
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 0 deletions.
122 changes: 122 additions & 0 deletions FluidNC/src/Spindles/PlasmaSpindle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "PlasmaSpindle.h"

#include "../System.h" // sys.abort

/*
PlasmaSpindle:
output_pin: gpio.13
enable_pin: gpio.14
arc_ok_pin: 'gpio.33:low'
arc_wait_ms: 1200
tool_num: 0
speed_map: 0=0.00% 1=100.00%
off_on_alarm: true
atc:
m6_macro:
Ideas:
- Maybe arc_wait_ms disables that feature
*/

namespace Spindles {

void PlasmaSpindle::init() {
if (_arc_ok_pin.defined()) {
_arcOkEventPin = new ArcOkEventPin("ArcOK", _arc_ok_pin, this);
_arcOkEventPin->init();
}

_arc_on = false;

_enable_pin.setAttr(Pin::Attr::Output);
_arc_ok_pin.setAttr(Pin::Attr::Input);

if (_speeds.size() == 0) {
// The default speed map for an On/Off spindle is off - 0% -
// for speed 0 and on - 100% - for any nonzero speedl
// In other words there is a step transition right at 0.
linearSpeeds(1, 100.0f);
}
setupSpeeds(1);
init_atc();
config_message();
}

// prints the startup message of the spindle config
void PlasmaSpindle ::config_message() {
log_info(name() << " Ena:" << _enable_pin.name() << " Arc OK:" << _arc_ok_pin.name() << atc_info());
}

void PlasmaSpindle::setState(SpindleState state, SpindleSpeed speed) {
if (sys.abort) {
return; // Block during abort.
}

// We always use mapSpeed() with the unmodified input speed so it sets
// sys.spindle_speed correctly.
uint32_t dev_speed = speed; // no mapping
if (state == SpindleState::Disable) {
_arc_on = false;
set_enable(false);

} else {
// check arc OK is not on before starting
if (_arcOkEventPin->get()) {
log_error(name() << " arc_ok active before starting plasma");
mc_critical(ExecAlarm::SpindleControl);
return;
}

if (!wait_for_arc_ok()) {
return;
}
_arc_on = true;
set_enable(true);
}
}

bool IRAM_ATTR PlasmaSpindle::wait_for_arc_ok() {
uint32_t wait_until_ms = millis() + _max_arc_wait;
while (millis() < wait_until_ms) {
if (_arcOkEventPin->get()) {
_arc_on = true;
return true;
}
protocol_execute_realtime();
delay_ms(1);
}
_arc_on = false;
gc_state.modal.spindle = SpindleState::Disable;
mc_critical(ExecAlarm::SpindleControl);
log_error(name() << " failed to get arc OK signal");
return false; // failed to get arc_ok
}

void IRAM_ATTR PlasmaSpindle::set_output(uint32_t dev_speed) {}

void IRAM_ATTR PlasmaSpindle::setSpeedfromISR(uint32_t dev_speed) {}

void IRAM_ATTR PlasmaSpindle::set_enable(bool enable) {
if (_disable_with_zero_speed && sys.spindle_speed == 0) {
enable = false;
}

_enable_pin.synchronousWrite(enable);
}

void PlasmaSpindle::set_direction(bool Clockwise) {}

void PlasmaSpindle::deinit() {
stop();
_enable_pin.setAttr(Pin::Attr::Input);
_arc_ok_pin.setAttr(Pin::Attr::Input);
}

// Configuration registration
namespace {
SpindleFactory::InstanceBuilder<PlasmaSpindle> registration("PlasmaSpindle");
}
}
110 changes: 110 additions & 0 deletions FluidNC/src/Spindles/PlasmaSpindle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) 2020 - Bart Dring
// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file.

#pragma once

/*
Experimental Plasma Spindle
*/

#include "Spindle.h"
#include "esp32-hal.h" // millis()
#include "../MotionControl.h" // mc_critical

namespace Spindles {
// This is for an on/off spindle all RPMs above 0 are on
class PlasmaSpindle : public Spindle {
private:
class ArcOkEventPin : public EventPin {
private:
bool _value = false;
Pin* _pin = nullptr;
PlasmaSpindle* _parent;

public:
ArcOkEventPin(const char* legend, Pin& pin, PlasmaSpindle* parent) : EventPin(nullptr, legend), _pin(&pin), _parent(parent) {}

void init() {
if (_pin->undefined()) {
return;
}
_value = _pin->read();
_pin->report(_legend);
_pin->setAttr(Pin::Attr::Input);
_pin->registerEvent(static_cast<EventPin*>(this));
update(_pin->read());
}
void update(bool state) { _value = state; }

// Differs from the base class version by sending the event on either edge
void trigger(bool active) override {
update(active);
if (!active && _parent->_arc_on) {
_parent->_arc_on = false;
send_alarm(ExecAlarm::AbortCycle);
}
}

bool get() { return _value; }
};

private:
ArcOkEventPin* _arcOkEventPin;

protected:
// This includes all items except direction_pin. direction_pin applies
// to most but not all of OnOff's derived classes. Derived classes that
// do not support direction_pin can invoke OnOff::groupCommon() instead
// of OnOff::group()
void groupCommon(Configuration::HandlerBase& handler) {
handler.item("enable_pin", _enable_pin);
handler.item("arc_ok_pin", _arc_ok_pin);
handler.item("arc_wait_ms", _max_arc_wait, 0, 3000);
Spindle::group(handler);
}

public:
PlasmaSpindle(const char* name) : Spindle(name) {}

PlasmaSpindle(const PlasmaSpindle&) = delete;
PlasmaSpindle(PlasmaSpindle&&) = delete;
PlasmaSpindle& operator=(const PlasmaSpindle&) = delete;
PlasmaSpindle& operator=(PlasmaSpindle&&) = delete;

void init() override;

void setSpeedfromISR(uint32_t dev_speed) override;
void setState(SpindleState state, SpindleSpeed speed) override;
void config_message() override;

// Methods introduced by this base clase
virtual void set_direction(bool Clockwise);
virtual void set_enable(bool enable);

bool wait_for_arc_ok();

// Configuration handlers:
void validate() override { Spindle::validate(); }

void group(Configuration::HandlerBase& handler) override { groupCommon(handler); }

virtual ~PlasmaSpindle() {}

protected:
Pin _enable_pin;
Pin _arc_ok_pin;

uint32_t _max_arc_wait = 1000;

// TO DO. These are not used in the class
// _disable_with_zero_speed forces a disable when speed is 0
bool _disable_with_zero_speed = false;
// _zero_speed_with_disable forces speed to 0 when disabled
bool _zero_speed_with_disable = false;

bool _arc_on = false;
bool use_delay_settings() const override { return false; }
virtual void set_output(uint32_t speed);
virtual void deinit();
};
}

0 comments on commit 22c09f4

Please sign in to comment.