Skip to content

Adding custom Tools and Modules to µCNC

Paciente8159 edited this page May 2, 2022 · 40 revisions

Jump to section

Adding custom modules to µCNC

µCNC has implemented a module system that allows the user to perform custom actions that get executed in an event/delegate fashion style similar to what is done with C#. Multiple callbacks functions can be attached to the same event. These modules can be quite useful and perform several things like adding custom custom gcodes to perform actions, or modifying IO states if a given condition is verified. µCNC already has a few useful modules like PID controller, Encoder module, TMC drivers support and custom G/M code support.

µCNC existing events/delegates

Without having to modify core code inside µCNC it is possible to listen to several already existing events. Here is a list of current events:

Event name Enable option Description
gcode_parse ENABLE_PARSER_MODULES Fires when a gcode command was not captured/understood by the core parser is trying to parse the code
gcode_exec ENABLE_PARSER_MODULES Fires when a gcode command that was not captured/understood by the core parser is going to execute code
cnc_reset ENABLE_MAIN_LOOP_MODULES Fires when µCNC resets
rtc_tick ENABLE_PARSER_MODULES Fires every millisecond. This code runs inside the RTC interrupt. Do not run long routines here. This is for time critical (periodic) tasks.
cnc_dotasks ENABLE_PARSER_MODULES Fires on the main loop running. Any repeating task should be hooked here
cnc_stop ENABLE_PARSER_MODULES Fires when a halt/stop condition is triggered
itp_reset_rt_position ENABLE_MAIN_LOOP_MODULES Fires when the internal systems reset the machine position
settings_change ENABLE_SETTINGS_MODULES Fires when a $ setting is changed
send_pins_states ENABLE_PROTOCOL_MODULES Fires when $P command is printing the pins states
input_change ENABLE_IO_MODULES Fires when a generic input pin changes state
probe_enable ENABLE_MOTION_MODULES Fires when the probe is enabled (used by bltouch module)
probe_disable ENABLE_MOTION_MODULES Fires when the probe is disabled (used by bltouch module)

Each of these events exposes a delegate and and event that have the following naming convention:

<event_name>_delegate -> type of a function pointer that defines the function prototype to be called by the event handler <event_name>_event -> pointer to the event that contains all the subscribers that are going to be called by the event handler mod_<event_name>_hook -> the event handler that is called inside the core code and calls and executes every subscriber callback

For example cnc_dotasks exposes cnc_dotasks_delegate and cnc_dotasks_event that will enable to create an attach a listener that gets called when the event executed inside the core code. This is done by the call to mod_cnc_dotasks_hook inside cnc_dotasks main loop function.

Most of these events have no input or output arguments. That translate to a void (*fp) (void). The exceptions are:

gcode_parse

Input args:

type Description
unsigned char This is the GCode word being parsed (example 'G')
uint8_t This is the GCode word argument converted to uint8_t format (example 98)
uint8_t The parser current error code
float This is the actual GCode word argument parsed as float. (example -1.2568)
parser_state_t * The current parser state (struct)
parser_words_t * The current parser words argument values (struct)
parser_cmd_explicit_t * The current parser GCode command active words/groups

Returns:

uint8_t - Returns the error. This can inform the handler if the custom parser successfully intercepted and parsed the command, if while parsing found an error, or ignores it and passes to the next custom parser listening to the event.

gcode_exec

Input args:

type Description
parser_state_t * The current parser state (struct)
parser_words_t * The current parser words argument values (struct)
parser_cmd_explicit_t * The current parser GCode command active words/groups

Returns:

uint8_t - Returns the error. This can inform the handler if the custom parser successfully intercepted and parsed the command, if while parsing found an error, or ignores it and passes to the next custom parser listening to the event.

itp_reset_rt_position

Input args:

type Description
float * The new origin that will be fixed as the machine new coordinates

Returns:

nothing

Creating a new event listener

Creating a new event listener is easy. All current modules are inside the src/modules directory but it's not mandatory. It's just a matter of having them organized.

Let's look at an example in the Arduino IDE environment by creating and attaching and event listener to cnc_dotasks.

Add a new file .c to uCNC directory (same has uCNC.ino) and paste this code

//include cnc.h relative path
#include "src/cnc.h"

//preprocessor check if module is enabled
#ifdef ENABLE_MAIN_LOOP_MODULES

//custom function declaration
void my_custom_code(void);
//create a listener of type cnc_dotasks
CREATE_LISTENER(cnc_dotasks_delegate, my_custom_code);
//custom code implementation
void my_custom_code(void)
{
    // do something
}

#endif

This created the event listener that is of type cnc_dotasks. The only thing left to do is to attach it so that it gets called when cnc_dotasks is fired.

In the uCNC.ino it's just a matter of adding the listener to the event like this:

Open uCNC.ino

#include "src/cnc.h"

int main(void)
{
    //initializes all systems
    cnc_init();
    // add the listener to cnc_dotasks
    ADD_LISTENER(cnc_dotasks_delegate, my_custom_code, cnc_dotasks_event);
    for (;;)
    {
        cnc_run();
    }
}

That's it. Your custom function will run inside the main loop.