diff --git a/FluidNC/src/Machine/MachineConfig.cpp b/FluidNC/src/Machine/MachineConfig.cpp index 170030ddb..21f461066 100644 --- a/FluidNC/src/Machine/MachineConfig.cpp +++ b/FluidNC/src/Machine/MachineConfig.cpp @@ -66,6 +66,7 @@ namespace Machine { handler.section("user_outputs", _userOutputs); handler.section("oled", _oled); + handler.section("status_outputs", _stat_out); Spindles::SpindleFactory::factory(handler, _spindles); diff --git a/FluidNC/src/Machine/MachineConfig.h b/FluidNC/src/Machine/MachineConfig.h index 9541141bc..9f33ae328 100644 --- a/FluidNC/src/Machine/MachineConfig.h +++ b/FluidNC/src/Machine/MachineConfig.h @@ -20,6 +20,7 @@ #include "../Stepper.h" #include "../Config.h" #include "../OLED.h" +#include "../Status_outputs.h" #include "Axes.h" #include "SPIBus.h" #include "I2CBus.h" @@ -74,6 +75,7 @@ namespace Machine { Start* _start = nullptr; Parking* _parking = nullptr; OLED* _oled = nullptr; + Status_Outputs* _stat_out = nullptr; Spindles::SpindleList _spindles; UartChannel* _uart_channels[MAX_N_UARTS] = { nullptr }; diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index aed6a8608..b74cceaa9 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -98,6 +98,10 @@ void setup() { config->_oled->init(); } + if (config->_stat_out) { + config->_stat_out->init(); + } + config->_stepping->init(); // Configure stepper interrupt timers plan_init(); diff --git a/FluidNC/src/Status_outputs.cpp b/FluidNC/src/Status_outputs.cpp new file mode 100644 index 000000000..2ed99b8f5 --- /dev/null +++ b/FluidNC/src/Status_outputs.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2023 - Bart Dring +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +/* + This is a class for status "Idle,Run,Hold,Alarm" pins. + + This can be used for Tower lights,etc. +*/ +#include "Status_outputs.h" +#include "Machine/MachineConfig.h" + +void Status_Outputs::init() { + if (_Idle_pin.defined()) { + _Idle_pin.setAttr(Pin::Attr::Output); + } + + if (_Run_pin.defined()) { + _Run_pin.setAttr(Pin::Attr::Output); + } + + if (_Hold_pin.defined()) { + _Hold_pin.setAttr(Pin::Attr::Output); + } + + if (_Alarm_pin.defined()) { + _Alarm_pin.setAttr(Pin::Attr::Output); + } + + log_info("Status outputs" + << " Interval:" << _interval_ms << " Idle:" << _Idle_pin.name() << " Cycle:" << _Run_pin.name() << " Hold:" << _Hold_pin.name() + << " Alarm:" << _Alarm_pin.name()); + + allChannels.registration(this); + setReportInterval(_interval_ms); +} + +void Status_Outputs::parse_report() { + if (_report.rfind("<", 0) == 0) { + parse_status_report(); + return; + } +} + +// This is how the OLED driver receives channel data +size_t Status_Outputs::write(uint8_t data) { + char c = data; + if (c == '\r') { + return 1; + } + if (c == '\n') { + parse_report(); + _report = ""; + return 1; + } + _report += c; + return 1; +} + +Channel* Status_Outputs::pollLine(char* line) { + autoReport(); + return nullptr; +} + +void Status_Outputs::parse_status_report() { + if (_report.back() == '>') { + _report.pop_back(); + } + // Now the string is a sequence of field|field|field + size_t pos = 0; + auto nextpos = _report.find_first_of("|", pos); + _state = _report.substr(pos + 1, nextpos - pos - 1); + + _Idle_pin.write(_state == "Idle"); + _Run_pin.write(_state == "Run"); + _Hold_pin.write(_state.substr(0, 4) == "Hold"); + _Alarm_pin.write(_state == "Alarm"); +} diff --git a/FluidNC/src/Status_outputs.h b/FluidNC/src/Status_outputs.h new file mode 100644 index 000000000..793624288 --- /dev/null +++ b/FluidNC/src/Status_outputs.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Config.h" +#include "Configuration/Configurable.h" +#include "Channel.h" + +typedef const uint8_t* font_t; + +class Status_Outputs : public Channel, public Configuration::Configurable { + Pin _Idle_pin; + Pin _Run_pin; + Pin _Hold_pin; + Pin _Alarm_pin; + +public: +private: + std::string _report; + std::string _state; + + int _interval_ms = 500; + + void parse_report(); + void parse_status_report(); + +public: + Status_Outputs() : Channel("status_outputs") {} + + Status_Outputs(const Status_Outputs&) = delete; + Status_Outputs(Status_Outputs&&) = delete; + Status_Outputs& operator=(const Status_Outputs&) = delete; + Status_Outputs& operator=(Status_Outputs&&) = delete; + + virtual ~Status_Outputs() = default; + + void init(); + + size_t write(uint8_t data) override; + + Channel* pollLine(char* line) override; + void flushRx() override {} + + bool lineComplete(char*, char) override { return false; } + size_t timedReadBytes(char* buffer, size_t length, TickType_t timeout) override { return 0; } + + // Configuration handlers: + void validate() override {} + void afterParse() override {}; + + void group(Configuration::HandlerBase& handler) override { + handler.item("report_interval_ms", _interval_ms, 100, 5000); + handler.item("idle_pin", _Idle_pin); + handler.item("run_pin", _Run_pin); + handler.item("hold_pin", _Hold_pin); + handler.item("alarm_pin", _Alarm_pin); + } +};