terjeio / ioSender

A GCode Sender for Grbl and grblHAL written in C# (Windows only).
BSD 3-Clause "New" or "Revised" License
224 stars 67 forks source link

REQ: Ability to map an MCode to a macro or other function in IOSender #269

Closed 5ocworkshop closed 1 year ago

5ocworkshop commented 1 year ago

Was discussing with Andrew over on the PrintNC Discord how it might be helpful to be able to trigger a macro in IOSender (or some other external program or function) from an MCODE in a gcode file.

In my personal use case, I have a vacuum router that switches my vac feed betwen my chop saw, bench and CNC machine. It is currently triggered via an MQTT message which is generated when I manually push the power button on a "smart" outlet. GIven that my laptop is on the network already and has wireless, if I could make a call to something to send the MQTT message when a job starts, due to the presence of an MCODE from my post processor, I could have the vacuum selectively triggered from within jobs.

Likewise, the ability of the Jogger that Expatria has designed to trigger macros wihtin IOSender would extend the functionality of the handheld jogger.

Is this something you think is worthwhile and could be considered?

terjeio commented 1 year ago

Likewise, the ability of the Jogger that Expatria has designed to trigger macros wihtin IOSender

The macros are stored in the controller, not in ioSender (unless it is a custom version of ioSender).

Is this something you think is worthwhile and could be considered?

IMO it would be better to connect a ESP32, ESP8266 or a RP2040W board (or similar) to the controller (via I2C?) and send the messages from that. And/or add the MQTT protocol to the network stack for networking capable boards?

terjeio commented 1 year ago

Here is a teaser plugin for grblHAL on networking capable boards. Adding mcodes for sending messages from ioSender is easy.

/*
  my_plugin.c - some MQTT fun

  Part of grblHAL

  Public domain.
*/

#ifdef ARDUINO
#include "../../driver.h"
#else
#include "driver.h"
#endif

#include <string.h>

#include "../networking/networking.h"

static bool mqtt_connected = false;
static coolant_set_state_ptr coolant_set_state_;
static on_state_change_ptr on_state_change;
static on_report_options_ptr on_report_options;
static on_program_completed_ptr on_program_completed;
static on_mqtt_client_connected_ptr on_client_connected;
static on_mqtt_message_received_ptr on_message_received;

static const char *msg_alarm = "ouch, machine went into alarm state!";
static const char *msg_job_complete = "job completed!";
static const char *msg_coolant_on = "turn on water cooler!";
static const char *msg_coolant_off = "turn off water cooler!";

static void onStateChanged (sys_state_t state)
{
    static sys_state_t last_state = STATE_IDLE;

    if(state != last_state) {

        last_state = state;

        if((state & STATE_ALARM) && mqtt_connected)
            mqtt_publish_message("grblHALxx", msg_alarm, strlen(msg_alarm), 1, false);
    }

    if(on_state_change)
        on_state_change(state);
}

void onProgramCompleted (program_flow_t program_flow, bool check_mode)
{
    if(!check_mode && mqtt_connected)
        mqtt_publish_message("grblHALxx", msg_job_complete, strlen(msg_job_complete), 1, false);

    if(on_program_completed)
        on_program_completed(program_flow, check_mode);
}

static void onCoolantSetState (coolant_state_t state)
{
    static coolant_state_t last_state = {0};

    coolant_set_state_(state);

    if(state.flood != last_state.flood && mqtt_connected) {
        if(state.flood)
            mqtt_publish_message("grblHALxx", msg_coolant_on, strlen(msg_coolant_on), 1, false);
        else
            mqtt_publish_message("grblHALxx", msg_coolant_off, strlen(msg_coolant_off), 1, false);
    }

    last_state = state;
}

void onMQTTconnected (bool connected)
{
    if((mqtt_connected = connected))
        mqtt_subscribe_topic("grblHAL", 1, NULL);

    if(on_client_connected)
        on_client_connected(connected);
}

bool onMQTTmessage (const char *topic, const void *payload, size_t payload_length)
{
    hal.stream.write(topic);
    hal.stream.write(ASCII_EOL);
    hal.stream.write((char *)payload);
    hal.stream.write(ASCII_EOL);

    if(!strcmp((char *)payload, "stop job"))
        grbl.enqueue_realtime_command(CMD_STOP);

    return on_message_received == NULL || on_message_received(topic, payload, payload_length);
}

static void onReportOptions (bool newopt)
{
    on_report_options(newopt);

    if(!newopt)
        hal.stream.write("[PLUGIN:MQTT Demo v0.01]" ASCII_EOL);
}

void my_plugin_init (void)
{
    on_report_options = grbl.on_report_options;
    grbl.on_report_options = onReportOptions;

    on_state_change = grbl.on_state_change;
    grbl.on_state_change = onStateChanged;

    coolant_set_state_ = hal.coolant.set_state;
    hal.coolant.set_state = onCoolantSetState;

    on_program_completed = grbl.on_program_completed;
    grbl.on_program_completed = onProgramCompleted;

    on_client_connected = mqtt_events.on_client_connected;
    mqtt_events.on_client_connected = onMQTTconnected;

    on_message_received = mqtt_events.on_message_received;
    mqtt_events.on_message_received = onMQTTmessage;
}