├── .gitattributes
├── .gitignore
├── COPYING
├── README.md
├── arm-driver
├── README.md
├── driver.c
├── eeprom.c
├── eeprom.h
├── serial.c
└── serial.h
├── mcodes.c
├── mcodes.h
└── my_plugin
├── MCU_load
├── CMakeLists.txt
└── my_plugin.c
├── MQTT_example
└── my_plugin.c
├── Macros_bound_to_aux_inputs
├── CMakeLists.txt
└── my_plugin.c
├── Motor_power_monitor
├── CMakeLists.txt
└── my_plugin.c
├── Pause_on_SD_file_run
├── CMakeLists.txt
└── my_plugin.c
├── Persistent_tool
├── CMakeLists.txt
└── my_plugin.c
├── Probe_select
├── CMakeLists.txt
└── my_plugin.c
├── Probe_select2
├── CMakeLists.txt
└── my_plugin.c
├── README.md
├── Realtime_report_timestamp
├── CMakeLists.txt
└── my_plugin.c
├── STM32F411_blinky
└── my_plugin.c
├── Set_output_on_feed_hold
├── CMakeLists.txt
└── my_plugin.c
├── Solenoid_spindle
├── CMakeLists.txt
└── my_plugin.c
├── Stepper_enable_control
├── CMakeLists.txt
└── my_plugin.c
├── hpgl
├── CMakeLists.txt
├── README.md
├── arc.c
├── arc.h
├── charset0.c
├── clip.c
├── font173.c
├── hpgl.c
├── hpgl.h
├── htext.c
├── htext.h
├── motori.c
├── motori.h
├── scale.c
└── scale.h
└── settings
└── my_plugin.c
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto eol=lf
3 |
4 |
5 | # Custom for Visual Studio
6 | *.cs diff=csharp
7 |
8 | # Standard to msysgit
9 | *.doc diff=astextplain
10 | *.DOC diff=astextplain
11 | *.docx diff=astextplain
12 | *.DOCX diff=astextplain
13 | *.dot diff=astextplain
14 | *.DOT diff=astextplain
15 | *.pdf diff=astextplain
16 | *.PDF diff=astextplain
17 | *.rtf diff=astextplain
18 | *.RTF diff=astextplain
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows thumbnail cache files
2 | Thumbs.db
3 | ehthumbs.db
4 | ehthumbs_vista.db
5 |
6 | # Folder config file
7 | Desktop.ini
8 |
9 | # Recycle Bin used on file shares
10 | $RECYCLE.BIN/
11 |
12 | # Windows Installer files
13 | *.cab
14 | *.msi
15 | *.msm
16 | *.msp
17 |
18 | # Windows shortcuts
19 | *.lnk
20 |
21 | # Subversion folders
22 |
23 | .svn
24 |
25 | # Build folder
26 |
27 | build
28 |
29 | #
30 | # =========================
31 | # Operating System Files
32 | # =========================
33 |
34 | .vscode/settings.json
35 | .vscode/c_cpp_properties.json
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## grblHAL templates
2 |
3 | Template code to aid writing custom code for extensions such as additional IO-pins, M-codes or plugins by publishing entry points in the HAL function pointer structs.
4 |
5 | * [ioports.c](./ioports.c) - for additional digital and/or analog outputs. For enabling M62 - M68 M-codes.
6 |
7 | * [mcodes.c](./mcodes.c) - for additional M-codes, includes example code.
8 |
9 | * [my_plugin](my_plugin) folder - contains some plugin examples.
10 |
11 | ---
12 | 2021-07-29
13 |
--------------------------------------------------------------------------------
/arm-driver/README.md:
--------------------------------------------------------------------------------
1 | ## grblHAL templates, arm-driver
2 |
3 | This is a "bare bones" driver template.
4 |
5 | Function calls that are commented out and in upper case are pseudo code that needs to be replaced, possibly by reading/writing directly from/to registers.
6 |
7 | To make a functional driver MCU documentation has to be consulted and quite a bit of code added, mostly for peripheral configuration. It may also be helpful to peek at the existing drivers to see how they are implemented.
8 |
9 | Tip: I like to use the bit-band region for flexibility, and sometimes speed of execution, for GPIO pin access.
10 |
11 | ---
12 | 2019-12-29
13 |
--------------------------------------------------------------------------------
/arm-driver/driver.c:
--------------------------------------------------------------------------------
1 | /*
2 | driver.c - An embedded CNC Controller with rs274/ngc (g-code) support
3 |
4 | Template driver code for ARM processors
5 |
6 | Part of grblHAL
7 |
8 | By Terje Io, public domain
9 |
10 | */
11 |
12 | #include "eeprom.h"
13 | #include "serial.h"
14 |
15 | #include "grbl/hal.h"
16 |
17 | static bool pwmEnabled = false, IOInitDone = false;
18 | static axes_signals_t next_step_outbits;
19 | static spindle_pwm_t spindle_pwm;
20 | static delay_t delay = { .ms = 1, .callback = NULL }; // NOTE: initial ms set to 1 for "resetting" systick timer on startup
21 |
22 | // Inverts the probe pin state depending on user settings and probing cycle mode.
23 | static uint8_t probe_invert;
24 |
25 | static void spindle_set_speed (uint_fast16_t pwm_value);
26 |
27 | // Interrupt handler prototypes
28 | // Interrupt handlers needs to be registered, possibly by modifying a system specific startup file.
29 | // It is possible to relocate the interrupt dispatch table from flash to RAM and programatically attach handlers.
30 | // See the driver for SAMD21 for an example, relocation is done in the driver_init() function.
31 | // Also, if a MCU specific driver library is used this might have functions to programatically attach handlers.
32 |
33 | static void stepper_driver_isr (void);
34 | static void stepper_pulse_isr (void);
35 | static void stepper_pulse_isr_delayed (void);
36 | static void limit_isr (void);
37 | static void control_isr (void);
38 | static void systick_isr (void);
39 |
40 |
41 | // Millisecond resolution delay function
42 | // Will return immediately if a callback function is provided
43 | static void driver_delay_ms (uint32_t ms, delay_callback_ptr callback)
44 | {
45 | if(ms) {
46 | delay.ms = ms;
47 | // SYSTICK_ENABLE(); // Enable systick timer
48 | if(!(delay.callback = callback))
49 | while(delay.ms);
50 | } else {
51 | if(delay.ms) {
52 | delay.callback = NULL;
53 | delay.ms = 1;
54 | }
55 | if(callback)
56 | callback();
57 | }
58 | }
59 |
60 |
61 | // Set stepper pulse output pins.
62 | // step_outbits.value (or step_outbits.mask) are: bit0 -> X, bit1 -> Y...
63 | // Individual step bits can be accessed by step_outbits.x, step_outbits.y, ...
64 | inline static void set_step_outputs (axes_signals_t step_outbits)
65 | {
66 | step_outbits.value ^= settings.steppers.step_invert.mask;
67 |
68 | // GPIO_WRITE(STEP_PORT, step_outbits.value); // Output step bits.
69 | }
70 |
71 |
72 | // Set stepper direction ouput pins.
73 | // dir_outbits.value (or dir_outbits.mask) are: bit0 -> X, bit1 -> Y...
74 | // Individual direction bits can be accessed by dir_outbits.x, dir_outbits.y, ...
75 | inline static void set_dir_outputs (axes_signals_t dir_outbits)
76 | {
77 | dir_outbits.value ^= settings.steppers.dir_invert.mask;
78 |
79 | // GPIO_WRITE(STEP_PORT, dir_outbits.value); // Output direction bits.
80 | }
81 |
82 |
83 | // Enable steppers.
84 | // enable.value (or enable.mask) are: bit0 -> X, bit1 -> Y...
85 | // Individual enable bits can be accessed by enable.x, enable.y, ...
86 | // NOTE: if a common signal is used to enable all drivers enable.x should be used to set the signal.
87 | static void stepperEnable (axes_signals_t enable)
88 | {
89 | enable.value ^= settings.steppers.enable_invert.mask;
90 |
91 | // NOTE: many drivers are enabled by a active low signal
92 | // GPIO_WRITE(ENABLE_PORT, enable.value); // Output stepper enable bits.
93 | }
94 |
95 | // Starts stepper driver timer and forces a stepper driver interrupt callback.
96 | static void stepperWakeUp (void)
97 | {
98 | // Enable stepper drivers.
99 | stepperEnable((axes_signals_t){AXES_BITMASK});
100 |
101 | // STEPPERTIMER_LOAD(500); // Load stepper timer with a sensible timeout value allowing stepper drivers time to wake up.
102 | // NOTE: timeout should be long enough to allow the IRQ handler time to update the load
103 | // value before it fires again.
104 | // STEPPERTIMER_IRQ_ENABLE(); // Enable stepper timer IRQ.
105 | // STEPPERTIMER_START(); // Start stepper timer.
106 | }
107 |
108 | // Disables stepper driver interrupts and reset outputs.
109 | static void stepperGoIdle (bool clear_signals)
110 | {
111 | // STEPPERTIMER_IRQ_DISABLE(); // Disable stepper timer IRQ.
112 | // STEPPERTIMER_STOP(); // Stop stepper timer.
113 |
114 | if(clear_signals) {
115 | set_step_outputs((axes_signals_t){0});
116 | set_dir_outputs((axes_signals_t){0});
117 | }
118 | }
119 |
120 | // Sets up stepper driver interrupt timeout.
121 | // Called at the start of each segment.
122 | // NOTE: If a 32-bit timer is used it is advisable to limit max step time to about 2 seconds
123 | // in order to avoid excessive delays on completion of motions
124 | // NOTE: If a 16 bit timer is used it may be neccesary to adjust the timer clock frequency (prescaler)
125 | // to cover the needed range. Refer to actual drivers for code examples.
126 | static void stepperCyclesPerTick (uint32_t cycles_per_tick)
127 | {
128 | // STEPPERTIMER_LOAD(cycles_per_tick); // Set the stepper timer timeout time.
129 | }
130 |
131 |
132 | // Start a stepper pulse, no delay version
133 | // stepper_t struct is defined in grbl/stepper.h
134 | static void stepperPulseStart (stepper_t *stepper)
135 | {
136 | if(stepper->new_block) {
137 | stepper->new_block = false;
138 | set_dir_outputs(stepper->dir_outbits);
139 | }
140 |
141 | if(stepper->step_outbits.value) {
142 | set_step_outputs(stepper->step_outbits);
143 | // STEPPULSETIMER_START(); // Start step pulse timer.
144 | }
145 | }
146 |
147 |
148 | // Start a stepper pulse, delay version
149 | // stepper_t struct is defined in grbl/stepper.h
150 | static void stepperPulseStartDelayed (stepper_t *stepper)
151 | {
152 | if(stepper->new_block) {
153 | stepper->new_block = false;
154 | set_dir_outputs(stepper->dir_outbits);
155 | }
156 |
157 | if(stepper->step_outbits.value) {
158 | next_step_outbits = stepper->step_outbits; // Store out_bits
159 | // STEPPULSETIMER_START(); // Start step pulse timer.
160 | }
161 | }
162 |
163 |
164 | // Enable/disable limit pins interrupt.
165 | // NOTE: the homing parameter is indended for configuring advanced
166 | // stepper drivers for sensorless homing.
167 | static void limitsEnable (bool on, bool homing)
168 | {
169 | // if (on && settings.limits.flags.hard_enabled)
170 | // GPIO_IRQ_ENABLE(LIMITS_PORT); // Enable limit pins change interrupts.
171 | // else
172 | // GPIO_IRQ_DISABLE(LIMITS_PORT); // Disable limit pins change interrupts.
173 | }
174 |
175 | // Returns limit state as an axes_signals_t bitmap variable.
176 | // signals.value (or signals.mask) are: bit0 -> X, bit1 -> Y...
177 | // Individual signals bits can be accessed by signals.x, signals.y, ...
178 | // Each bit indicates a limit signal, where triggered is 1 and not triggered is 0.
179 | // axes_signals_t is defined in grbl/nuts_bolts.h.
180 | inline static limit_signals_t limitsGetState()
181 | {
182 | limit_signals_t signals = {0};
183 |
184 | signals.min.mask = settings.limits.invert.mask;
185 |
186 | // Read limits pins, either to a temp variable or directly to signals.vaue if no remapping is required.
187 | // Single bits may be also assigned directly by reading them via the bit band region.
188 | // signals.min.value = GPIO_READ(LIMITS_PORT);
189 |
190 | if (settings.limits.invert.mask)
191 | signals.min.value ^= settings.limits.invert.mask;
192 |
193 | return signals;
194 | }
195 |
196 | // Returns system state as a control_signals_t bitmap variable.
197 | // signals.value (or signals.mask) are: bit0 -> reset, bit1 -> feed_hold, ...
198 | // Individual enable bits can be accessed by signals.reset, signals.feed_hold, ...
199 | // Each bit indicates a control signal, where triggered is 1 and not triggered is 0.
200 | // axes_signals_t is defined in grbl/system.h.
201 | inline static control_signals_t systemGetState (void)
202 | {
203 | control_signals_t signals = {settings.control_invert.value};
204 |
205 | // Read control signal pins, either to a temp variable or directly to signals.vaue if no remapping is required.
206 | // Single bits may be also assigned directly by reading them via the bit band region.
207 | // signals.value = GPIO_READ(CONTROL_PORT);
208 |
209 | if(settings.control_invert.value)
210 | signals.value ^= settings.control_invert.value;
211 |
212 | return signals;
213 | }
214 |
215 | // Sets up the probe pin invert mask to
216 | // appropriately set the pin logic according to setting for normal-high/normal-low operation
217 | // and the probing cycle modes for toward-workpiece/away-from-workpiece.
218 | static void probeConfigure (bool is_probe_away, bool probing)
219 | {
220 | probe_invert = settings.flags.invert_probe_pin ? 0 : PROBE_PIN;
221 |
222 | if (is_probe_away)
223 | probe_invert ^= PROBE_PIN;
224 | }
225 |
226 | // Returns the probe connected and triggered pin states.
227 | probe_state_t probeGetState (void)
228 | {
229 | probe_state_t state = {
230 | .connected = On
231 | };
232 |
233 | // state.triggered = (GPIO_READ(PROBE_PORT, PROBE_PIN) ^ probe_invert) != 0;
234 |
235 | return state;
236 | }
237 |
238 | // Static spindle (off, on cw & on ccw)
239 |
240 | inline static void spindle_off (void)
241 | {
242 | // GPIO_WRITE(SPINDLE_PORT, SPINDLE_ENABLE_PIN) = settings.spindle.invert.on ? SPINDLE_ENABLE_PIN : 0;
243 | }
244 |
245 | inline static void spindle_on (void)
246 | {
247 | // GPIO_WRITE(SPINDLE_PORT, SPINDLE_ENABLE_PIN) = settings.spindle.invert.on ? 0 : SPINDLE_ENABLE_PIN);
248 | }
249 |
250 | inline static void spindle_dir (bool ccw)
251 | {
252 | // GPIO_WRITE(SPINDLE_PORT, SPINDLE_DIRECTION_PIN) = (ccw ^ settings.spindle.invert.ccw) ? SPINDLE_DIRECTION_PIN : 0);
253 | }
254 |
255 |
256 | // Start or stop spindle.
257 | static void spindleSetState (spindle_state_t state, float rpm)
258 | {
259 | if (!state.on)
260 | spindle_off();
261 | else {
262 | if(hal.driver_cap.spindle_dir)
263 | spindle_dir(state.ccw);
264 | spindle_on();
265 | }
266 | }
267 |
268 | // Variable spindle control functions
269 |
270 | // Set spindle speed.
271 | static void spindle_set_speed (uint_fast16_t pwm_value)
272 | {
273 | if (pwm_value == spindle_pwm.off_value) {
274 | if(settings.spindle.flags.pwm_action == SpindleAction_DisableWithZeroSPeed)
275 | spindle_off();
276 | pwmEnabled = false;
277 | if(spindle_pwm.always_on) {
278 | // SPINDLEPWM_TIMER_SET(spindle_pwm.off_value);
279 | // SPINDLEPWM_TIMER_START();
280 | } else {
281 | // SPINDLEPWM_TIMER_STOP();
282 | // NOTE: code may be added to ensure spindle output are at the correct level.
283 | }
284 | } else {
285 | if(!pwmEnabled) {
286 | spindle_on();
287 | pwmEnabled = true;
288 | }
289 | // SPINDLEPWM_TIMER_SET(pwm_value);
290 | // SPINDLEPWM_TIMER_START();
291 | }
292 | }
293 |
294 | #ifdef SPINDLE_PWM_DIRECT
295 |
296 | // Convert spindle speed to PWM value.
297 | static uint_fast16_t spindleGetPWM (float rpm)
298 | {
299 | return spindle_compute_pwm_value(&spindle_pwm, rpm, false);
300 | }
301 |
302 | #else
303 |
304 | // Update spindle speed.
305 | static void spindleUpdateRPM (float rpm)
306 | {
307 | spindle_set_speed(spindle_compute_pwm_value(&spindle_pwm, rpm, false));
308 | }
309 |
310 | #endif
311 |
312 | // Start or stop spindle.
313 | static void spindleSetStateVariable (spindle_state_t state, float rpm)
314 | {
315 | if (!state.on || rpm == 0.0f) {
316 | spindle_set_speed(spindle_pwm.off_value);
317 | spindle_off();
318 | } else {
319 | if(hal.driver_cap.spindle_dir)
320 | spindle_dir(state.ccw);
321 | spindle_set_speed(spindle_compute_pwm_value(&spindle_pwm, rpm, false));
322 | }
323 | }
324 |
325 | // Returns spindle state in a spindle_state_t variable.
326 | // spindle_state_t is defined in grbl/spindle_control.h
327 | static spindle_state_t spindleGetState (void)
328 | {
329 | spindle_state_t state = {0};
330 |
331 | // state.on = GPIO_READ(SPINDLE_PORT, SPINDLE_ENABLE_PIN) != 0;
332 | // if(hal.driver_cap.spindle_dir)
333 | // state.ccw = GPIO_READ((SPINDLE_PORT, SPINDLE_DIRECTION_PIN) != 0;
334 | state.value ^= settings.spindle.invert.mask;
335 | if(pwmEnabled)
336 | state.on |= pwmEnabled;
337 | state.value ^= settings.spindle.invert.mask;
338 |
339 | return state;
340 | }
341 |
342 | // end spindle code
343 |
344 | // Start/stop coolant (and mist if enabled).
345 | // coolant_state_t is defined in grbl/coolant_control.h.
346 | static void coolantSetState (coolant_state_t mode)
347 | {
348 | mode.value ^= settings.coolant_invert.mask;
349 |
350 | // GPIO_WRITE(COOLANT_PORT, FLOOD_PIN) = mode.flood; // Set flood output according to mode.flood value: 0 is off, 1 is on
351 | // GPIO_WRITE(COOLANT_PORT, MIST_PIN) = mode.mist; // Set mist output according to mode.mist value: 0 is off, 1 is on
352 | }
353 |
354 | // Returns coolant state in a coolant_state_t variable.
355 | // coolant_state_t is defined in grbl/coolant_control.h.
356 | static coolant_state_t coolantGetState (void)
357 | {
358 | coolant_state_t state = {0};
359 |
360 | // Read state back from GPIO pins
361 | // mode.flood = GPIO_READ(COOLANT_PORT, FLOOD_PIN);
362 | // mode.mist = GPIO_READ(COOLANT_PORT, MIST_PIN);
363 |
364 | state.value ^= settings.coolant_invert.mask;
365 |
366 | return state;
367 | }
368 |
369 | // Helper functions for setting/clearing/inverting individual bits atomically (uninterruptable)
370 | static void bitsSetAtomic (volatile uint_fast16_t *ptr, uint_fast16_t bits)
371 | {
372 | __disable_irq();
373 | *ptr |= bits;
374 | __enable_irq();
375 | }
376 |
377 | static uint_fast16_t bitsClearAtomic (volatile uint_fast16_t *ptr, uint_fast16_t bits)
378 | {
379 | __disable_irq();
380 | uint_fast16_t prev = *ptr;
381 | *ptr &= ~bits;
382 | __enable_irq();
383 | return prev;
384 | }
385 |
386 | static uint_fast16_t valueSetAtomic (volatile uint_fast16_t *ptr, uint_fast16_t value)
387 | {
388 | __disable_irq();
389 | uint_fast16_t prev = *ptr;
390 | *ptr = value;
391 | __enable_irq();
392 | return prev;
393 | }
394 |
395 | // Configures perhipherals when settings are initialized or changed
396 | static void settings_changed (settings_t *settings)
397 | {
398 | //
399 | hal.driver_cap.variable_spindle = spindle_precompute_pwm_values(&spindle_pwm, 120000000UL);
400 |
401 | if(IOInitDone) {
402 |
403 | stepperEnable(settings->steppers.deenergize);
404 |
405 | if(hal.driver_cap.variable_spindle) {
406 | // Set spindle PWM timer timeout value to spindle_pwm.period here
407 | hal.spindle.set_state = spindleSetStateVariable;
408 | } else
409 | hal.spindle.set_state = spindleSetState;
410 |
411 | // Stepper pulse timeout setup.
412 | // When the stepper pulse is delayed either two timers or a timer that supports multiple
413 | // compare registers is required.
414 | if(settings->steppers.pulse_delay_microseconds) {
415 | // Configure step pulse timer(s) for delayed pulse here.
416 | hal.stepper.pulse_start = stepperPulseStartDelayed;
417 | } else {
418 | // Configure step pulse timer for pulse off here.
419 | hal.stepper.pulse_start = stepperPulseStart;
420 | }
421 |
422 | /*************************
423 | * Control pins config *
424 | ************************/
425 |
426 | // Configure pullup/pulldown and interrupt type (falling/rising edge) here.
427 |
428 |
429 | /***********************
430 | * Limit pins config *
431 | ***********************/
432 |
433 | // Configure pullup/pulldown and interrupt type (falling/rising edge) here.
434 |
435 |
436 | /********************
437 | * Probe pin init *
438 | ********************/
439 |
440 | // Configure pullup/pulldown here.
441 | }
442 | }
443 |
444 | // Initializes MCU peripherals for Grbl use
445 | static bool driver_setup (settings_t *settings)
446 | {
447 | // System init
448 |
449 | // Enable peripherals to use here (if required).
450 |
451 | /******************
452 | * Stepper init *
453 | ******************/
454 |
455 | // Configure stepper driver timer here.
456 |
457 | // Configure step pulse timer here.
458 |
459 | /****************************
460 | * Software debounce init *
461 | ****************************/
462 |
463 | if(hal.driver_cap.software_debounce) {
464 | // Configure software debounce here. Additional code in GPIO IRQ handlers is required.
465 | }
466 |
467 | /***********************
468 | * Control pins init *
469 | ***********************/
470 |
471 | // Configure control pins here. Pullup/pulldown and interrupt type is set up in the settings_changed() function.
472 |
473 | /*********************
474 | * Limit pins init *
475 | *********************/
476 |
477 | // Configure limit pins here. Pullup/pulldown and interrupt type is set up in the settings_changed() function.
478 |
479 | /********************
480 | * Probe pin init *
481 | ********************/
482 |
483 | // Configure probe pin here. Pullup/pulldown is set up in the settings_changed() function.
484 |
485 | /***********************
486 | * Coolant pins init *
487 | ***********************/
488 |
489 | // Configure coolant pin(s) here.
490 |
491 | /******************
492 | * Spindle init *
493 | ******************/
494 |
495 | // Configure spindle pin(s) and spindle PWM timer here.
496 |
497 | // Set defaults
498 |
499 | IOInitDone = settings->version == 19;
500 |
501 | settings_changed(settings);
502 |
503 | hal.stepper.go_idle(true);
504 | hal.spindle.set_state((spindle_state_t){0}, 0.0f);
505 | hal.coolant.set_state((coolant_state_t){0});
506 |
507 | return IOInitDone;
508 | }
509 |
510 | // Initialize HAL pointers, setup serial comms and enable EEPROM.
511 | // NOTE: the core is not yet configured (from EEPROM data), driver_setup() will be called when done.
512 | bool driver_init (void)
513 | {
514 | // If CMSIS is used this may be a good place to initialize the system. Comment out or remove if not available or done already.
515 | SystemInit();
516 |
517 | // Set up systick timer with a 1ms period
518 | // and set SysTick IRQ to lowest priority.
519 | // NOTE: the following code assumes CMSIS is used, if not so this has to be changed.
520 | NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
521 |
522 | SysTick->LOAD = (SystemCoreClock / 1000) - 1;
523 | SysTick->VAL = 0;
524 | SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk|SysTick_CTRL_TICKINT_Msk;
525 |
526 | // end systick timer setup.
527 |
528 | // Enable EEPROM peripheral here if available.
529 |
530 | // Enable lazy stacking of FPU registers here if a FPU is available.
531 |
532 | hal.info = "my driver name"; // Typically set to MCU or board name
533 | hal.driver_version = "YYMMDD"; // Set to build date
534 | #ifdef BOARD_NAME
535 | hal.board = BOARD_NAME;
536 | #endif
537 | hal.driver_setup = driver_setup;
538 | hal.f_step_timer = SystemCoreClock; // NOTE: SystemCoreClock is a CMSIS definition, ...
539 | hal.rx_buffer_size = RX_BUFFER_SIZE;
540 | hal.delay_ms = driver_delay_ms;
541 | hal.settings_changed = settings_changed;
542 |
543 | hal.stepper.wake_up = stepperWakeUp;
544 | hal.stepper.go_idle = stepperGoIdle;
545 | hal.stepper.enable = stepperEnable;
546 | hal.stepper.cycles_per_tick = stepperCyclesPerTick;
547 | hal.stepper.pulse_start = stepperPulseStart;
548 |
549 | hal.limits.enable = limitsEnable;
550 | hal.limits.get_state = limitsGetState;
551 |
552 | hal.coolant.set_state = coolantSetState;
553 | hal.coolant.get_state = coolantGetState;
554 |
555 | hal.probe.get_state = probeGetState;
556 | hal.probe.configure = probeConfigure;
557 |
558 | hal.spindle.set_state = spindleSetState;
559 | hal.spindle.get_state = spindleGetState;
560 | #ifdef SPINDLE_PWM_DIRECT
561 | hal.spindle.get_pwm = spindleGetPWM;
562 | hal.spindle.update_pwm = spindle_set_speed;
563 | #else
564 | hal.spindle._update_rpm = spindleUpdateRPM;
565 | #endif
566 |
567 | hal.control.get_state = systemGetState;
568 |
569 | // Enable serial communication.
570 | const io_stream_t *serial_stream = serialInit(115200);
571 |
572 | memcpy(&hal.stream, serial_stream, offsetof(io_stream_t, enqueue_realtime_command));
573 |
574 | // If EEPROM is available for settings storage uncomment the following line:
575 |
576 | // eeprom_init();
577 |
578 | // end EEPROM available
579 |
580 | // Settings may also be stored in flash.
581 | // Quite a few drivers has code for that: SAMD21, SAM3X8E, ESP32, STM32F1xx and LPC1769
582 | // Check out the source for these for howto examples.
583 | // Note: many drivers has code examples for using external EEPROM, typically via I2C interface.
584 |
585 | hal.set_bits_atomic = bitsSetAtomic;
586 | hal.clear_bits_atomic = bitsClearAtomic;
587 | hal.set_value_atomic = valueSetAtomic;
588 |
589 | // Driver capabilities, used for announcing and negotiating (with the core) driver functionality.
590 | // See control_signals_t and driver_cap_t union i grbl/hal.h for available flags.
591 |
592 | #if SAFETY_DOOR_ENABLE
593 | hal.signals_cap.safety_door_ajar = On;
594 | #endif
595 |
596 | hal.driver_cap.spindle_dir = On;
597 | hal.driver_cap.variable_spindle = On;
598 | hal.driver_cap.mist_control = On;
599 | // hal.driver_cap.software_debounce = On;
600 | hal.driver_cap.step_pulse_delay = On;
601 | hal.driver_cap.amass_level = 3;
602 | hal.driver_cap.control_pull_up = On;
603 | hal.driver_cap.limits_pull_up = On;
604 | hal.driver_cap.probe_pull_up = On;
605 |
606 | my_plugin_init(); // Init any user supplied plugin (it defaults to a weak implementation).
607 |
608 | // No need to move version check before init.
609 | // Compiler will fail any signature mismatch for existing entries.
610 | return hal.version == 8;
611 | }
612 |
613 | /* interrupt handlers */
614 |
615 | // Main stepper driver.
616 | static void stepper_driver_isr (void)
617 | {
618 | // STEPPERTIMER_IRQ_CLEAR(); // Clear stepper timer interrupt.
619 | hal.stepper.interrupt_callback();
620 | }
621 |
622 | /* The Stepper Port Reset Interrupt: This interrupt handles the falling edge of the step
623 | pulse. This should always trigger before the next general stepper driver interrupt and independently
624 | finish, if stepper driver interrupts is disabled after completing a move.
625 | NOTE: Interrupt collisions between the serial and stepper interrupts can cause delays by
626 | a few microseconds, if they execute right before one another. Not a big deal, but can
627 | cause issues at high step rates if another high frequency asynchronous interrupt is
628 | added to Grbl.
629 | */
630 | // This interrupt is enabled when Grbl sets the motor port bits to execute
631 | // a step. This ISR resets the motor port after a short period (settings.pulse_microseconds)
632 | // completing one step cycle.
633 | static void stepper_pulse_isr (void)
634 | {
635 | // STEPPULSETIMER_STOP(); // Stop step pulse timer.
636 | set_step_outputs((axes_signals_t){0});
637 | }
638 |
639 | static void stepper_pulse_isr_delayed (void)
640 | {
641 | if(STEP_PULSE_ON)
642 | set_step_outputs(next_step_outbits);
643 | else {
644 | // STEPPULSETIMER_STOP(); // Stop step pulse timer.
645 | set_step_outputs((axes_signals_t){0});
646 | }
647 | }
648 |
649 | // Limit pins ISR.
650 | static void limit_isr (void)
651 | {
652 | // GPIO_IRQ_CLEAR(LIMIT_PORT);
653 | hal.limits.interrupt_callback(limitsGetState());
654 | }
655 |
656 | // Control pins ISR.
657 | static void control_isr (void)
658 | {
659 | // GPIO_IRQ_CLEAR(CONTROL_PORT);
660 | hal.control.interrupt_callback(systemGetState());
661 | }
662 |
663 | // Interrupt handler for 1 ms interval timer
664 | static void systick_isr (void)
665 | {
666 | if(delay.ms && !(--delay.ms)) {
667 | // SYSTICK_DISABLE; // Disable systick timer
668 | if(delay.callback) {
669 | delay.callback();
670 | delay.callback = NULL;
671 | }
672 | }
673 | }
674 |
--------------------------------------------------------------------------------
/arm-driver/eeprom.c:
--------------------------------------------------------------------------------
1 | /*
2 | eeprom.c - An embedded CNC Controller with rs274/ngc (g-code) support
3 |
4 | Template driver code for ARM processors
5 |
6 | Part of GrblHAL
7 |
8 | By Terje Io, public domain
9 |
10 | */
11 |
12 | #include "grbl/grbl.h"
13 |
14 | // NOTE: See driver for TM4C123 for additional code if EEPROM peripheral only support word (32-bit) access.
15 | // Also, many drivers has code for external EEPROM access, typically via I2C interface.
16 |
17 | // Read single byte from EEPROM.
18 | // addr is 0-based offset from start.
19 | static uint8_t getByte (uint32_t addr)
20 | {
21 | uint32_t data;
22 |
23 | // EEPROM_READ(addr);
24 |
25 | return data;
26 |
27 | }
28 |
29 | // Write single byte to EEPROM.
30 | // addr is 0-based offset from start.
31 | static void putByte (uint32_t addr, uint8_t new_value)
32 | {
33 | // EEPROM_WRITE(new_value);
34 | }
35 |
36 | // Read block of data from EEPROM, return true if checksum matches or no checksum used.
37 | // Checksum is stored in the byte following the last byte read from the block.
38 | static nvs_transfer_result_t readBlock (uint8_t *destination, uint32_t source, uint32_t size, bool with_checksum)
39 | {
40 | uint8_t *data = destination;
41 |
42 | for(; size > 0; size--)
43 | *data++ = getByte(source++);
44 |
45 | return with_checksum ? (calc_checksum(destination, size) == getByte(source + size) ? NVS_TransferResult_OK : NVS_TransferResult_Failed) : NVS_TransferResult_OK;
46 | }
47 |
48 | // Write block of data to EEPROM followed by an optional checksum byte.
49 | static nvs_transfer_result_t writeBlock (uint32_t destination, uint8_t *source, uint32_t size, bool with_checksum)
50 | {
51 |
52 | uint8_t *data = source;
53 | uint32_t remaining = size;
54 |
55 | for(; remaining > 0; remaining--)
56 | putByte(destination++, *data++);
57 |
58 | if(with_checksum)
59 | putByte(destination + size, calc_checksum(source, size));
60 | }
61 |
62 | void eeprom_init (void)
63 | {
64 | hal.nvs.type = NVS_EEPROM;
65 | hal.nvs.get_byte = getByte;
66 | hal.nvs.put_byte = putByte;
67 | hal.nvs.memcpy_to_nvs = writeBlock;
68 | hal.nvs.memcpy_from_nvs = readBlock;
69 | }
70 |
--------------------------------------------------------------------------------
/arm-driver/eeprom.h:
--------------------------------------------------------------------------------
1 | /*
2 | eeprom.h - An embedded CNC Controller with rs274/ngc (g-code) support
3 |
4 | Template driver code for ARM processors
5 |
6 | Part of GrblHAL
7 |
8 | By Terje Io, public domain
9 |
10 | */
11 |
12 | #ifndef __EEPROM_H__
13 | #define __EEPROM_H__
14 |
15 | void eeprom_init (void);
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/arm-driver/serial.c:
--------------------------------------------------------------------------------
1 | /*
2 | serial.c - An embedded CNC Controller with rs274/ngc (g-code) support
3 |
4 | Template driver code for ARM processors
5 |
6 | Part of grblHAL
7 |
8 | By Terje Io, public domain
9 |
10 | */
11 |
12 | #include "grbl/grbl.h"
13 |
14 | static stream_tx_buffer_t txbuffer = {0};
15 | static stream_rx_buffer_t rxbuffer = {0}, rxbackup;
16 |
17 | //
18 | // serialGetC - returns -1 if no data available
19 | //
20 | static int16_t serialGetC (void)
21 | {
22 | int16_t data;
23 | uint_fast16_t bptr = rxbuffer.tail;
24 |
25 | if(bptr == rxbuffer.head)
26 | return -1; // no data available else EOF
27 |
28 | data = rxbuffer.data[bptr++]; // Get next character, increment tmp pointer
29 | rxbuffer.tail = bptr & (RX_BUFFER_SIZE - 1); // and update pointer
30 |
31 | return data;
32 | }
33 |
34 | //
35 | // Returns number of characters in serial input buffer
36 | //
37 | uint16_t serialRxFree (void)
38 | {
39 | return (RX_BUFFER_SIZE - 1) - serialRxCount();
40 | }
41 |
42 | //
43 | // Flushes the serial input buffer.
44 | // NOTE: If the peripheral has an output FIFO that should be flushed as well.
45 | //
46 | static void serialRxFlush (void)
47 | {
48 | rxbuffer.tail = rxbuffer.head;
49 | rxbuffer.overflow = false;
50 | }
51 | //
52 | // Flushes and adds a CAN character to the serial input buffer
53 | //
54 | static void serialRxCancel (void)
55 | {
56 | serialRxFlush();
57 | rxbuffer.data[rxbuffer.head] = ASCII_CAN;
58 | rxbuffer.head = (rxbuffer.tail + 1) & (RX_BUFFER_SIZE - 1);
59 | }
60 |
61 | //
62 | // Writes a character to the serial output stream.
63 | //
64 | static bool serialPutC (const char c)
65 | {
66 | uint_fast16_t next_head;
67 |
68 | // NOTE: If buffer and transmit register are empty buffering may be bypassed.
69 | // See actual drivers for examples.
70 |
71 | next_head = (txbuffer.head + 1) & (TX_BUFFER_SIZE - 1); // Get and update head pointer
72 |
73 | while(txbuffer.tail == next_head) { // Buffer full, block until space is available...
74 | if(!hal.stream_blocking_callback())
75 | return false;
76 | }
77 |
78 | txbuffer.data[txbuffer.head] = c; // Add data to buffer
79 | txbuffer.head = next_head; // and update head pointer
80 |
81 | UART_TX_IRQ_ENABLE(); // Enable TX interrupts
82 |
83 | return true;
84 | }
85 |
86 | //
87 | // Writes a null terminated string to the serial output stream, blocks if buffer full
88 | //
89 | static void serialWriteS (const char *data)
90 | {
91 | char c, *ptr = (char *)data;
92 |
93 | while((c = *ptr++) != '\0')
94 | serialPutC(c);
95 | }
96 |
97 | // ********************************************
98 | // Optional functions, not required by the core
99 | // ********************************************
100 |
101 | // Some plugins will refuse to activate if not implemented.
102 |
103 | //
104 | // Writes a number of characters from a buffer to the serial output stream, blocks if buffer full
105 | //
106 | void serialWrite(const char *s, uint16_t length)
107 | {
108 | char *ptr = (char *)s;
109 |
110 | while(length--)
111 | serial2PutC(*ptr++);
112 | }
113 |
114 | //
115 | // Suspend or reading from the input buffer or restore backup copy of it.
116 | // Used by the manual tool change protocol.
117 | //
118 | static bool serialSuspendInput (bool suspend)
119 | {
120 | return stream_rx_suspend(&rxbuffer, suspend);
121 | }
122 |
123 | //
124 | // Returns number of characters in the input buffer.
125 | //
126 | static inline uint16_t serialRxCount (void)
127 | {
128 | uint_fast16_t head = rxbuffer.head, tail = rxbuffer.tail;
129 |
130 | return BUFCOUNT(head, tail, RX_BUFFER_SIZE);
131 | }
132 |
133 | //
134 | // Returns number of characters pending transmission.
135 | //
136 | uint16_t serialTxCount (void) {
137 |
138 | uint_fast16_t head = txbuffer.head, tail = txbuffer.tail;
139 |
140 | return BUFCOUNT(head, tail, TX_BUFFER_SIZE) /* + remaining bytes in any FIFO and/or transmit register */;
141 | }
142 |
143 | //
144 | // Flush the serial output buffer.
145 | //
146 | void serial2TxFlush (void)
147 | {
148 | // Disable TX interrupts here.
149 | // Flush caracters in any transmit FIFO too.
150 | txbuf2.tail = txbuf2.head;
151 | }
152 |
153 | //
154 | // Disable/enable reception.
155 | //
156 | static bool serialDisable (bool disable)
157 | {
158 | if(disable)
159 | // Disable RX interrupt.
160 | else
161 | // Enable RX interrupt.
162 |
163 | return true;
164 | }
165 |
166 | //
167 | // Sets the baud rate.
168 | //
169 | static bool serialSetBaudRate (uint32_t baud_rate)
170 | {
171 |
172 | return true;
173 | }
174 |
175 | // **********************
176 | // End optional functions
177 | // **********************
178 |
179 | //
180 | // Configure serial peripheral
181 | //
182 | const io_stream_t *serialInit (uint32_t baud_rate)
183 | {
184 | // Optional functions can be commented out, deleted or set to NULL if not implemented.
185 | static const io_stream_t stream = {
186 | .type = StreamType_Serial,
187 | .connected = true,
188 | .read = serialGetC,
189 | .write = serialWriteS,
190 | .write_all = serialWriteS,
191 | .get_rx_buffer_free = serialRxFree,
192 | .write_char = serialPutC,
193 | .reset_read_buffer = serialRxFlush,
194 | .cancel_read_buffer = serialRxCancel,
195 | .write_n = serialWrite, // Optional, required for Modbus plugin support.
196 | .get_rx_buffer_count = serialRxCount, // Optional, required for Modbus plugin support.
197 | .get_tx_buffer_count = serialTxCount, // Optional, required for Modbus plugin support.
198 | .reset_write_buffer = serialTxFlush, // Optional, required for Modbus plugin support.
199 | .suspend_read = serialSuspendInput, // Optional, required for manual tool change support.
200 | .disable = serialDisable, // Optional, recommended for some plugins.
201 | .set_baud_rate = serialSetBaudRate // Optional, required for Modbus and Bluetooth plugin support.
202 | };
203 |
204 | // Add code to initialize peripheral here, including enabling RX interrupts.
205 |
206 | return &stream;
207 | }
208 |
209 | static void uart_interrupt_handler (void)
210 | {
211 | uint_fast16_t bptr;
212 | int32_t data;
213 | uint32_t iflags;
214 |
215 | // iflags = UART_GET_IRQSSTATE(); // Get UART interrupt flags.
216 |
217 | if(iflags & UART_IRQ_TX) {
218 |
219 | bptr = txbuffer.tail;
220 |
221 | if(txbuffer.head != bptr) {
222 |
223 | // UART_TX_WRITE(UARTCH, txbuffer.data[bptr++]); // Put character in TXT register
224 | bptr &= (TX_BUFFER_SIZE - 1); // and update tmp tail pointer.
225 |
226 | txbuffer.tail = bptr; // Update tail pointer.
227 |
228 | if(bptr == txbuffer.head) // Disable TX interrups
229 | // UART_TX_IRQ_DISABLE(); // when TX buffer empty.
230 | }
231 | }
232 |
233 | if(iflags & (UART_IRQ_RX)) {
234 |
235 | bptr = (rxbuffer.head + 1) & (RX_BUFFER_SIZE - 1); // Get next head pointer.
236 |
237 | if(bptr == rxbuffer.tail) { // If buffer full
238 | rxbuffer.overflow = 1; // flag overflow.
239 | // UART_RX_IRQ_DISABLE(); Clear RX interrupt, may be done by a dummy read of the RX register
240 | } else {
241 | // data = UART_GET(); Read received character to data varable, clear RX interrupt if not done automatically by read.
242 | if(data == CMD_TOOL_ACK && !rxbuffer.backup) { // If tool change acknowledged
243 | stream_rx_backup(&rxbuf); // save current RX buffer
244 | hal.stream.read = serialGetC; // and restore normal input.
245 | } else if(!hal.stream.enqueue_realtime_command((char)data)) {
246 | rxbuffer.data[rxbuffer.head] = (char)data; // Add data to buffer.
247 | rxbuffer.head = bptr; // and update pointer.
248 | }
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/arm-driver/serial.h:
--------------------------------------------------------------------------------
1 | /*
2 | serial.h - An embedded CNC Controller with rs274/ngc (g-code) support
3 |
4 | Template driver code for ARM processors
5 |
6 | Part of grblHAL
7 |
8 | By Terje Io, public domain
9 |
10 | */
11 |
12 | #ifndef _HAL_SERIAL_H_
13 | #define _HAL_SERIAL_H_
14 |
15 | #include "grbl/hal.h"
16 |
17 | const io_stream_t *serialInit (uint32_t baud_rate);
18 |
19 | // If baud rate selection is not inplemented implement this signature instead:
20 | // const io_stream_t *serialInit (void);
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/mcodes.c:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | mcode.c - user defined M-codes template
4 |
5 | Part of grblHAL
6 |
7 | Copyright (c) 2019-2024 Terje Io
8 |
9 | grblHAL is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | grblHAL is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with grblHAL. If not, see .
21 |
22 | */
23 |
24 | /*
25 | * NOTE: this template is also a bare bones example for adding M100 with two parameters: P and Q
26 | */
27 |
28 | #include
29 | #include
30 |
31 | #ifdef ARDUINO
32 | #include "../grbl/hal.h"
33 | #else
34 | #include "grbl/hal.h"
35 | #endif
36 |
37 | static user_mcode_ptrs_t user_mcode;
38 |
39 | // check - check if M-code is handled here.
40 | // parameters: mcode - M-code to check for (some are predefined in user_mcode_t in grbl/gcode.h), use a cast if not.
41 | // returns: mcode if handled, UserMCode_Ignore otherwise (UserMCode_Ignore is defined in grbl/gcode.h).
42 | static user_mcode_type_t check (user_mcode_t mcode)
43 | {
44 | return mcode == UserMCode_Generic0
45 | ? UserMCode_Normal // Handled by us. Set to UserMCode_NoValueWords if there are any parameter words (letters) without an accompanying value.
46 | : (user_mcode.check ? user_mcode.check(mcode) : UserMCode_Unsupported); // If another handler present then call it or return ignore.
47 | }
48 |
49 | // validate - validate parameters
50 | // NOTE: the isnanf() checks below are not needed when check() returns UserMCode_Generic0, it has already been performed by the parser.
51 | // parameters: gc_block - pointer to parser_block_t struct (defined in grbl/gcode.h).
52 | // gc_block->words - holds a bitfield of parameter words available.
53 | // If float values are NAN (Not A Number) this means they are not available.
54 | // If integer values has all bits set to 1 this means they are not available.
55 | // returns: status_code_t enum (defined in grbl/gcode.h): Status_OK if validated ok, appropriate status from enum if not.
56 | static status_code_t validate (parser_block_t *gc_block)
57 | {
58 | status_code_t state = Status_GcodeValueWordMissing;
59 |
60 | switch(gc_block->user_mcode) {
61 |
62 | case UserMCode_Generic0:
63 | if(gc_block->words.p && !isnanf(gc_block->values.p)) // Check if P parameter value is supplied.
64 | state = Status_BadNumberFormat; // Return error if so.
65 |
66 | if(gc_block->words.q && isnanf(gc_block->values.q)) // Check if Q parameter value is supplied.
67 | state = Status_BadNumberFormat; // Return error if not.
68 |
69 | if(state != Status_BadNumberFormat && gc_block->words.q) { // Are required parameters provided?
70 | if(gc_block->values.q > 0.0f && gc_block->values.q <= 5.0f) // Yes, is Q parameter value in range (1-5)?
71 | state = Status_OK; // Yes - return ok status.
72 | else
73 | state = Status_GcodeValueOutOfRange; // No - return error status.
74 | if(gc_block->words.q) // If P parameter is present set
75 | gc_block->values.p = 1.0f; // value to 1 for execution.
76 | gc_block->words.p = gc_block->words.q = Off; // Claim parameters.
77 | gc_block->user_mcode_sync = true; // Optional: execute command synchronized
78 | }
79 | break;
80 |
81 | default:
82 | state = Status_Unhandled;
83 | break;
84 | }
85 |
86 | // If not handled by us and another handler present then call it.
87 | return state == Status_Unhandled && user_mcode.validate ? user_mcode.validate(gc_block) : state;
88 | }
89 |
90 | // execute - execute M-code
91 | // parameters: state - sys.state (bitmap, defined in system.h)
92 | // gc_block - pointer to parser_block_t struct (defined in grbl/gcode.h).
93 | // returns: -
94 | static void execute (sys_state_t state, parser_block_t *gc_block)
95 | {
96 | bool handled = true;
97 |
98 | switch(gc_block->user_mcode) {
99 |
100 | case UserMCode_Generic0:
101 | // do something: Q parameter value can be found in gc_block->values.q.
102 | // P parameter has its value in gc_block->values.p set to 1 if present, NAN if not.
103 | break;
104 |
105 | default:
106 | handled = false;
107 | break;
108 | }
109 |
110 |
111 | if(!handled && user_mcode.execute) // If not handled by us and another handler present
112 | user_mcode.execute(state, gc_block); // then call it.
113 | }
114 |
115 | // Set up HAL pointers for handling additional M-codes.
116 | // Call this function on driver setup.
117 | void mcodes_init (void)
118 | {
119 | // Save away current HAL pointers so that we can use them to keep
120 | // any chain of M-code handlers intact.
121 | memcpy(&user_mcode, &grbl.user_mcode, sizeof(user_mcode_ptrs_t));
122 |
123 | // Redirect HAL pointers to our code.
124 | grbl.user_mcode.check = check;
125 | grbl.user_mcode.validate = validate;
126 | grbl.user_mcode.execute = execute;
127 | }
128 |
--------------------------------------------------------------------------------
/mcodes.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | mcode.h - user defined M-codes template
4 |
5 | Part of grblHAL
6 |
7 | Copyright (c) 2019 Terje Io
8 |
9 | Grbl is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | Grbl is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with Grbl. If not, see .
21 |
22 | */
23 |
24 | // Set up HAL pointers for handling addtional M-codes.
25 | // Call this function on driver setup.
26 | void mcodes_init (void);
27 |
--------------------------------------------------------------------------------
/my_plugin/MCU_load/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/MCU_load/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c.c - MCU load estimator
3 |
4 | Counts number of iterations of protocol idle loop per 10ms and add count to real time report.
5 |
6 | Higher number is better. E.g. the iMXRT1062 reports > 20000 when idle -> less than 500ns per iteration.
7 |
8 | Part of grblHAL
9 |
10 | Public domain.
11 | This code is is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 | */
15 |
16 | #include
17 |
18 | #include "grbl/hal.h"
19 |
20 | static uint32_t count = 0;
21 | static bool add_report = false;
22 | static on_execute_realtime_ptr on_execute_realtime;
23 | static on_realtime_report_ptr on_realtime_report;
24 | static on_report_options_ptr on_report_options;
25 |
26 | void onExecuteRealtime (uint_fast16_t state)
27 | {
28 | static uint32_t last_ms, last_count;
29 |
30 | uint32_t ms = hal.get_elapsed_ticks();
31 |
32 | if(ms - last_ms >= 10) {
33 | last_ms = ms;
34 | count = last_count;
35 | last_count = 0;
36 | add_report = true;
37 | } else
38 | last_count++;
39 |
40 | on_execute_realtime(state);
41 | }
42 |
43 | static void onRealtimeReport (stream_write_ptr stream_write, report_tracking_flags_t report)
44 | {
45 | static char buf[20] = {0};
46 |
47 | if (add_report) {
48 |
49 | add_report = false;
50 |
51 | strcpy(buf, "|LOAD:");
52 | strcat(buf, uitoa(count));
53 |
54 | stream_write(buf);
55 | }
56 |
57 | if(on_realtime_report)
58 | on_realtime_report(stream_write, report);
59 | }
60 |
61 | static void onReportOptions (bool newopt)
62 | {
63 | on_report_options(newopt);
64 |
65 | if(!newopt)
66 | report_plugin("MCU Load", "v0.01");
67 | }
68 |
69 | void my_plugin_init (void)
70 | {
71 | on_report_options = grbl.on_report_options;
72 | grbl.on_report_options = onReportOptions;
73 |
74 | on_execute_realtime = grbl.on_execute_realtime;
75 | grbl.on_execute_realtime = onExecuteRealtime;
76 |
77 | on_realtime_report = grbl.on_realtime_report;
78 | grbl.on_realtime_report = onRealtimeReport;
79 | }
80 |
--------------------------------------------------------------------------------
/my_plugin/MQTT_example/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - some MQTT fun
3 |
4 | Part of grblHAL
5 |
6 | Public domain.
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 | */
11 |
12 | #include "driver.h"
13 |
14 | #if MQTT_ENABLE
15 |
16 | #include
17 |
18 | #include "networking/networking.h"
19 |
20 | static bool mqtt_connected = false;
21 | static const char *client_id = NULL;
22 | static coolant_set_state_ptr coolant_set_state_;
23 | static on_state_change_ptr on_state_change;
24 | static on_report_options_ptr on_report_options;
25 | static on_program_completed_ptr on_program_completed;
26 | static on_mqtt_client_connected_ptr on_client_connected;
27 | static on_mqtt_message_received_ptr on_message_received;
28 |
29 | static const char *msg_alarm = "Alarm %d! - %s";
30 | static const char *msg_job_complete = "job completed!";
31 | static const char *msg_coolant_on = "turn on water cooler!";
32 | static const char *msg_coolant_off = "turn off water cooler!";
33 |
34 | static void onStateChanged (sys_state_t state)
35 | {
36 | static sys_state_t last_state = STATE_IDLE;
37 |
38 | if(state != last_state) {
39 |
40 | last_state = state;
41 |
42 | if((state & STATE_ALARM) && mqtt_connected) {
43 |
44 | char *msg;
45 | const char *alarm;
46 | if((msg = malloc(strlen((alarm = alarms_get_description(sys.alarm)) + strlen(msg_alarm) + 3)))) {
47 | sprintf(msg, msg_alarm, sys.alarm, alarm);
48 | mqtt_publish_message("grblHALxx", msg, strlen(msg), 1, false);
49 | free(msg);
50 | }
51 | }
52 | }
53 |
54 | if(on_state_change)
55 | on_state_change(state);
56 | }
57 |
58 | void onProgramCompleted (program_flow_t program_flow, bool check_mode)
59 | {
60 | if(!check_mode && mqtt_connected)
61 | mqtt_publish_message("grblHALxx", msg_job_complete, strlen(msg_job_complete), 1, false);
62 |
63 | if(on_program_completed)
64 | on_program_completed(program_flow, check_mode);
65 | }
66 |
67 | static void onCoolantSetState (coolant_state_t state)
68 | {
69 | static coolant_state_t last_state = {0};
70 |
71 | coolant_set_state_(state);
72 |
73 | if(state.flood != last_state.flood && mqtt_connected) {
74 | if(state.flood)
75 | mqtt_publish_message("grblHALxx", msg_coolant_on, strlen(msg_coolant_on), 1, false);
76 | else
77 | mqtt_publish_message("grblHALxx", msg_coolant_off, strlen(msg_coolant_off), 1, false);
78 | }
79 |
80 | last_state = state;
81 | }
82 |
83 | static void onMQTTconnected (bool connected)
84 | {
85 | if(on_client_connected)
86 | on_client_connected(connected);
87 |
88 | if((mqtt_connected = connected)) {
89 | mqtt_subscribe_topic("grblHAL", 1, NULL);
90 | client_id = networking_get_info()->mqtt_client_id;
91 | } else
92 | client_id = NULL;
93 | }
94 |
95 | static bool onMQTTmessage (const char *topic, const void *payload, size_t payload_length)
96 | {
97 | /*
98 | hal.stream.write(topic);
99 | hal.stream.write(ASCII_EOL);
100 | hal.stream.write((char *)payload);
101 | hal.stream.write(ASCII_EOL);
102 | */
103 | if(!strcmp((char *)payload, "stop job"))
104 | grbl.enqueue_realtime_command(CMD_STOP);
105 |
106 | return on_message_received == NULL || on_message_received(topic, payload, payload_length);
107 | }
108 |
109 | static void onReportOptions (bool newopt)
110 | {
111 | on_report_options(newopt);
112 |
113 | if(!newopt)
114 | report_plugin("MQTT Demo", "v0.01");
115 | }
116 |
117 | void my_plugin_init (void)
118 | {
119 | on_report_options = grbl.on_report_options;
120 | grbl.on_report_options = onReportOptions;
121 |
122 | on_state_change = grbl.on_state_change;
123 | grbl.on_state_change = onStateChanged;
124 |
125 | coolant_set_state_ = hal.coolant.set_state;
126 | hal.coolant.set_state = onCoolantSetState;
127 |
128 | on_program_completed = grbl.on_program_completed;
129 | grbl.on_program_completed = onProgramCompleted;
130 |
131 | on_client_connected = mqtt_events.on_client_connected;
132 | mqtt_events.on_client_connected = onMQTTconnected;
133 |
134 | on_message_received = mqtt_events.on_message_received;
135 | mqtt_events.on_message_received = onMQTTmessage;
136 | }
137 |
138 | #endif
139 |
--------------------------------------------------------------------------------
/my_plugin/Macros_bound_to_aux_inputs/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Macros_bound_to_aux_inputs/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | my_plugin.c - plugin for binding macros to aux input pins
4 |
5 | Part of grblHAL
6 |
7 | Public domain.
8 | This code is is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 |
12 | Up to 4 macros can be bound to input pins by changing the N_MACROS symbol below.
13 | Each macro can be up to 127 characters long, blocks (lines) are separated by a vertical bar character: |
14 | Setting numbers $450 - $453 are for defining the macro content.
15 | Setting numbers $454 - $457 are for configuring which aux input port to assign to each macro.
16 | NOTES: If the driver does not support mapping of port numbers settings $454 - $457 will not be available.
17 | The mapped pins has to be interrupt capable and support falling interrupt mode.
18 | The controller must be in Idle mode when starting macros.
19 |
20 | Examples:
21 | $450=G0Y5|G1X0F50
22 | $451=G0x0Y0Z0
23 |
24 | Tip: use the $pins command to check the port mapping.
25 | */
26 |
27 | #define N_MACROS 2 // MAX 4
28 |
29 | #include
30 | #include
31 |
32 | #include "grbl/hal.h"
33 | #include "grbl/nvs_buffer.h"
34 | #include "grbl/nuts_bolts.h"
35 | #include "grbl/protocol.h"
36 | #include "grbl/state_machine.h"
37 | #include "grbl/platform.h"
38 | #include "grbl/task.h"
39 |
40 | #if N_MACROS > 4
41 | #undef N_MACROS
42 | #define N_MACROS 4
43 | #endif
44 |
45 | typedef struct {
46 | uint8_t port;
47 | char data[128];
48 | } macro_setting_t;
49 |
50 | typedef struct {
51 | macro_setting_t macro[N_MACROS];
52 | } macro_settings_t;
53 |
54 | static bool is_executing = false;
55 | static uint8_t n_ports;
56 | uint8_t port[N_MACROS];
57 | static char max_port[4], *command;
58 | static nvs_address_t nvs_address;
59 | static on_report_options_ptr on_report_options;
60 | static macro_settings_t plugin_settings;
61 | static stream_read_ptr stream_read;
62 | static driver_reset_ptr driver_reset;
63 |
64 | static int16_t get_macro_char (void);
65 |
66 | // Ends macro execution if currently running
67 | // and restores normal operation.
68 | static void end_macro (void)
69 | {
70 | is_executing = false;
71 | if(hal.stream.read == get_macro_char) {
72 | hal.stream.read = stream_read;
73 | report_init_fns();
74 | }
75 | }
76 |
77 | // Called on a soft reset so that normal operation can be restored.
78 | static void plugin_reset (void)
79 | {
80 | end_macro(); // End macro if currently running.
81 | driver_reset(); // Call the next reset handler in the chain.
82 | }
83 |
84 | // Macro stream input function.
85 | // Reads character by character from the macro and returns them when
86 | // requested by the foreground process.
87 | static int16_t get_macro_char (void)
88 | {
89 | static bool eol_ok = false;
90 |
91 | if(*command == '\0') { // End of macro?
92 | end_macro(); // If end reading from it
93 | return eol_ok ? SERIAL_NO_DATA : ASCII_LF; // and return a linefeed if the last character was not a linefeed.
94 | }
95 |
96 | char c = *command++; // Get next character.
97 |
98 | if((eol_ok = c == '|')) // If character is vertical bar
99 | c = ASCII_LF; // return a linefeed character.
100 |
101 | return (uint16_t)c;
102 | }
103 |
104 | // This code will be executed after each command is sent to the parser,
105 | // If an error is detected macro execution will be stopped and the status_code reported.
106 | static status_code_t trap_status_report (status_code_t status_code)
107 | {
108 | if(status_code != Status_OK) {
109 | char msg[30];
110 | sprintf(msg, "error %d in macro", (uint8_t)status_code);
111 | report_message(msg, Message_Warning);
112 | end_macro();
113 | }
114 |
115 | return status_code;
116 | }
117 |
118 | // Actual start of macro execution.
119 | static void run_macro (void *data)
120 | {
121 | if(state_get() == STATE_IDLE && hal.stream.read != get_macro_char) {
122 | stream_read = hal.stream.read; // Redirect input stream to read from the macro instead of
123 | hal.stream.read = get_macro_char; // the active stream. This ensures that input streams are not mingled.
124 | grbl.report.status_message = trap_status_report; // Add trap for status messages so we can terminate on errors.
125 | }
126 | }
127 |
128 | // On falling interrupt run macro if machine is in Idle state.
129 | // Since this function runs in an interrupt context actual start of execution
130 | // is registered as a single run task to be started from the foreground process.
131 | ISR_CODE static void execute_macro (uint8_t irq_port, bool is_high)
132 | {
133 | if(!is_high && !is_executing && state_get() == STATE_IDLE) {
134 |
135 | // Determine macro to run from port number
136 | uint_fast8_t idx = N_MACROS;
137 | do {
138 | idx--;
139 | } while(idx && port[idx] != irq_port);
140 |
141 | command = plugin_settings.macro[idx].data;
142 | if(!(*command == '\0' || *command == 0xFF)) { // If valid command
143 | is_executing = true;
144 | task_add_immediate(run_macro, NULL); // register run_macro function to be called from foreground process.
145 | }
146 | }
147 | }
148 |
149 | // Add info about our settings for $help and enumerations.
150 | // Potentially used by senders for settings UI.
151 |
152 | static const setting_group_detail_t macro_groups [] = {
153 | { Group_Root, Group_UserSettings, "Macros"}
154 | };
155 |
156 | static status_code_t set_port (setting_id_t setting, float value)
157 | {
158 | status_code_t status;
159 | uint_fast8_t idx = setting - Setting_UserDefined_4;
160 |
161 | if((status = isintf(value) ? Status_OK : Status_BadNumberFormat) == Status_OK)
162 | plugin_settings.macro[idx].port = value < 0.0f ? 255 : (uint8_t)value;
163 |
164 | return status;
165 | }
166 |
167 | static float get_port (setting_id_t setting)
168 | {
169 | uint_fast8_t idx = setting - Setting_UserDefined_4;
170 |
171 | return plugin_settings.macro[idx].port;
172 | }
173 |
174 | static const setting_detail_t macro_settings[] = {
175 | { Setting_UserDefined_0, Group_UserSettings, "Macro 1", NULL, Format_String, "x(127)", "0", "127", Setting_NonCore, &plugin_settings.macro[0].data, NULL, NULL },
176 | #if N_MACROS > 1
177 | { Setting_UserDefined_1, Group_UserSettings, "Macro 2", NULL, Format_String, "x(127)", "0", "127", Setting_NonCore, &plugin_settings.macro[1].data, NULL, NULL },
178 | #endif
179 | #if N_MACROS > 2
180 | { Setting_UserDefined_2, Group_UserSettings, "Macro 3", NULL, Format_String, "x(127)", "0", "127", Setting_NonCore, &plugin_settings.macro[2].data, NULL, NULL },
181 | #endif
182 | #if N_MACROS > 3
183 | { Setting_UserDefined_3, Group_UserSettings, "Macro 4", NULL, Format_String, "x(127)", "0", "127", Setting_NonCore, &plugin_settings.macro[3].data, NULL, NULL },
184 | #endif
185 | { Setting_UserDefined_4, Group_AuxPorts, "Macro 1 port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } },
186 | #if N_MACROS > 1
187 | { Setting_UserDefined_5, Group_AuxPorts, "Macro 2 port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } },
188 | #endif
189 | #if N_MACROS > 2
190 | { Setting_UserDefined_6, Group_AuxPorts, "Macro 3 port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } },
191 | #endif
192 | #if N_MACROS > 3
193 | { Setting_UserDefined_7, Group_AuxPorts, "Macro 4 port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } },
194 | #endif
195 | };
196 |
197 | #ifndef NO_SETTINGS_DESCRIPTIONS
198 |
199 | static const setting_descr_t macro_settings_descr[] = {
200 | { Setting_UserDefined_0, "Macro content for macro 1, separate blocks (lines) with the vertical bar character |." },
201 | #if N_MACROS > 1
202 | { Setting_UserDefined_1, "Macro content for macro 2, separate blocks (lines) with the vertical bar character |." },
203 | #endif
204 | #if N_MACROS > 2
205 | { Setting_UserDefined_2, "Macro content for macro 3, separate blocks (lines) with the vertical bar character |." },
206 | #endif
207 | #if N_MACROS > 3
208 | { Setting_UserDefined_3, "Macro content for macro 4, separate blocks (lines) with the vertical bar character |." },
209 | #endif
210 | { Setting_UserDefined_4, "Aux port number to use for the Macro 1 start pin input. Set to -1 to disable." },
211 | #if N_MACROS > 1
212 | { Setting_UserDefined_5, "Aux port number to use for the Macro 2 start pin input. Set to -1 to disable." },
213 | #endif
214 | #if N_MACROS > 2
215 | { Setting_UserDefined_6, "Aux port number to use for the Macro 3 start pin input. Set to -1 to disable." },
216 | #endif
217 | #if N_MACROS > 3
218 | { Setting_UserDefined_7, "Aux port number to use for the Macro 4 start pin input. Set to -1 to disable." },
219 | #endif
220 | };
221 |
222 | #endif
223 |
224 | // Write settings to non volatile storage (NVS).
225 | static void macro_settings_save (void)
226 | {
227 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plugin_settings, sizeof(macro_settings_t), true);
228 | }
229 |
230 | // Restore default settings and write to non volatile storage (NVS).
231 | static void macro_settings_restore (void)
232 | {
233 | uint_fast8_t idx = N_MACROS, port = n_ports > N_MACROS ? n_ports - N_MACROS : 0;
234 |
235 | // Register empty macro strings and set default port numbers if mapping is available.
236 | for(idx = 0; idx < N_MACROS; idx++) {
237 | plugin_settings.macro[idx].port = port++;
238 | *plugin_settings.macro[idx].data = '\0';
239 | };
240 |
241 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plugin_settings, sizeof(macro_settings_t), true);
242 | }
243 |
244 | // Load our settings from non volatile storage (NVS).
245 | // If load fails restore to default values.
246 | static void macro_settings_load (void)
247 | {
248 | uint_fast8_t idx = N_MACROS, n_ok = 0, n_enabled = 0;
249 | xbar_t *pin = NULL;
250 |
251 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&plugin_settings, nvs_address, sizeof(macro_settings_t), true) != NVS_TransferResult_OK)
252 | macro_settings_restore();
253 |
254 | do {
255 | idx--;
256 | if((port[idx] = plugin_settings.macro[idx].port) == 0xFF)
257 | continue;
258 | n_enabled++;
259 | if((pin = ioport_get_info(Port_Digital, Port_Input, port[idx])) && !(pin->cap.irq_mode & IRQ_Mode_Falling)) // Is port interrupt capable?
260 | port[idx] = 0xFF; // No, flag it as not claimed.
261 | else if(ioport_claim(Port_Digital, Port_Input, &port[idx], "Macro pin")) { // Try to claim the port.
262 | if(pin->cap.debounce) {
263 | gpio_in_config_t config = {
264 | .debounce = On,
265 | .pull_mode = PullMode_Up
266 | };
267 | pin->config(pin, &config, false);
268 | }
269 | } else
270 | port[idx] = 0xFF;
271 | } while(idx);
272 |
273 | // Then try to register the interrupt handler.
274 | idx = N_MACROS;
275 | do {
276 | idx--;
277 | if(port[idx] != 0xFF && hal.port.register_interrupt_handler(port[idx], IRQ_Mode_Falling, execute_macro))
278 | n_ok++;
279 | } while(idx);
280 |
281 | if(n_ok < n_enabled)
282 | task_run_on_startup(report_warning, "Macro plugin failed to claim all needed ports!");
283 | }
284 |
285 | // Add info about our plugin to the $I report.
286 | static void report_options (bool newopt)
287 | {
288 | on_report_options(newopt);
289 |
290 | if(!newopt)
291 | report_plugin("Macro plugin (PD)", "0.04);
292 | }
293 |
294 | // A call my_plugin_init will be issued automatically at startup.
295 | // There is no need to change any source code elsewhere.
296 | void my_plugin_init (void)
297 | {
298 | // Settings descriptor used by the core when interacting with this plugin.
299 | static setting_details_t setting_details = {
300 | .groups = macro_groups,
301 | .n_groups = sizeof(macro_groups) / sizeof(setting_group_detail_t),
302 | .settings = macro_settings,
303 | .n_settings = sizeof(macro_settings) / sizeof(setting_detail_t),
304 | #ifndef NO_SETTINGS_DESCRIPTIONS
305 | .descriptions = macro_settings_descr,
306 | .n_descriptions = sizeof(macro_settings_descr) / sizeof(setting_descr_t),
307 | #endif
308 | .save = macro_settings_save,
309 | .load = macro_settings_load,
310 | .restore = macro_settings_restore
311 | };
312 |
313 | // If resources available register our plugin with the core.
314 | if(ioport_can_claim_explicit() &&
315 | (n_ports = ioports_available(Port_Digital, Port_Input)) &&
316 | (nvs_address = nvs_alloc(sizeof(macro_settings_t)))) {
317 |
318 | // Used for setting value validation.
319 | strcpy(max_port, uitoa(n_ports - 1));
320 |
321 | // Register settings.
322 | settings_register(&setting_details);
323 |
324 | // Add our plugin to the $I options report.
325 | on_report_options = grbl.on_report_options;
326 | grbl.on_report_options = report_options;
327 |
328 | // Hook into the driver reset chain so we
329 | // can restore normal operation if a reset happens
330 | // when a macro is running.
331 | driver_reset = hal.driver_reset;
332 | hal.driver_reset = plugin_reset;
333 | } else
334 | task_run_on_startup(report_warning, "Macro plugin failed to initialize!");
335 | }
336 |
--------------------------------------------------------------------------------
/my_plugin/Motor_power_monitor/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Motor_power_monitor/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - plugin for monitoring motor power
3 |
4 | Part of grblHAL
5 |
6 | Public domain.
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 |
11 | On power loss alarm 17 is raised (Motor fault).
12 | On alarm cleared or soft reset a M122I command is issued to reinit Trinamic drivers if power is back on.
13 |
14 | Setting $450 is for configuring which aux input port to assign for monitoring.
15 | NOTE: If the driver does not support mapping of port number settings $450 will not be available.
16 | The mapped pin has to be interrupt capable and support change (falling and rising) interrupt mode.
17 |
18 | Tip: use the $pins command to check the port mapping.
19 | */
20 |
21 | #include
22 | #include
23 |
24 | #include "grbl/hal.h"
25 | #include "grbl/nvs_buffer.h"
26 | #include "grbl/protocol.h"
27 | #include "grbl/state_machine.h"
28 | #include "grbl/task.h"
29 |
30 | typedef enum {
31 | Power_On = 0,
32 | Power_Alarm,
33 | Power_Lost
34 | } power_state_t;
35 |
36 | typedef struct {
37 | uint8_t port;
38 | } power_settings_t;
39 |
40 | static uint8_t port, n_ports;
41 | static char max_port[4];
42 | static power_state_t power_state = Power_On;
43 | static nvs_address_t nvs_address;
44 | static power_settings_t plugin_settings;
45 | static on_report_options_ptr on_report_options;
46 |
47 | static status_code_t set_port (setting_id_t setting, float value)
48 | {
49 | if(!isintf(value))
50 | return Status_BadNumberFormat;
51 |
52 | plugin_settings.port = value < 0.0f ? 0xFF : (uint8_t)value;
53 |
54 | return Status_OK;
55 | }
56 |
57 | static float get_port (setting_id_t setting)
58 | {
59 | return plugin_settings.port >= n_ports ? -1.0f : (float) plugin_settings.port;
60 | }
61 |
62 | // Use a float (decimal) setting with getter/setter functions so -1 can be used to disable the plugin.
63 | static const setting_detail_t power_settings[] = {
64 | { Setting_UserDefined_0, Group_AuxPorts, "Power monitor port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } }
65 | };
66 |
67 | #ifndef NO_SETTINGS_DESCRIPTIONS
68 |
69 | static const setting_descr_t power_settings_descr[] = {
70 | { Setting_UserDefined_0, "Auxiliary port to use for stepper power monitoring. Set to -1 to disable." }
71 | };
72 |
73 | #endif
74 |
75 | // Write settings to non volatile storage (NVS).
76 | static void power_settings_save (void)
77 | {
78 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plugin_settings, sizeof(power_settings_t), true);
79 | }
80 |
81 | static void check_power_restored (void *data)
82 | {
83 | if(hal.port.wait_on_input(Port_Digital, port, WaitMode_Immediate, 0.0f) == 1) {
84 |
85 | power_state = Power_On;
86 |
87 | report_message("Motor power restored", Message_Info);
88 | // Reset stepper drivers
89 | if(hal.stepper.stepper_status)
90 | hal.stepper.stepper_status(true);
91 |
92 | } else
93 | task_add_delayed(check_power_restored, NULL, 250);
94 | }
95 |
96 | // Raise motor fault alarm.
97 | static void raise_power_alarm (void *data)
98 | {
99 | if(power_state == Power_Alarm) {
100 | system_raise_alarm(Alarm_MotorFault);
101 | task_add_delayed(check_power_restored, NULL, 250);
102 | }
103 |
104 | power_state = Power_Lost;
105 | }
106 |
107 | // Called when power sensing pin changes state.
108 | // Raises alarm or sets up wait for alarm reset before taking action.
109 | static void on_power_change (uint8_t port, bool state)
110 | {
111 | if(!state) {
112 | if(power_state == Power_On) {
113 | protocol_enqueue_foreground_task(raise_power_alarm, NULL);
114 | power_state = Power_Alarm;
115 | }
116 | }
117 | }
118 |
119 | // Restore default settings and write to non volatile storage (NVS).
120 | static void power_settings_restore (void)
121 | {
122 | // Find highest numbered port that supports change interrupt, or keep the current one if found.
123 | plugin_settings.port = ioport_find_free(Port_Digital, Port_Input, (pin_cap_t){ .irq_mode = IRQ_Mode_Change, .claimable = On }, "Motor supply monitor");
124 |
125 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plugin_settings, sizeof(power_settings_t), true);
126 | }
127 |
128 | // Load our settings from non volatile storage (NVS).
129 | // If load fails restore to default values.
130 | static void power_settings_load (void)
131 | {
132 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&plugin_settings, nvs_address, sizeof(power_settings_t), true) != NVS_TransferResult_OK)
133 | power_settings_restore();
134 |
135 | // Sanity check
136 | if(plugin_settings.port >= n_ports)
137 | plugin_settings.port = 0xFF;
138 |
139 | // If port is valid try claiming it, if successful register an interrupt handler.
140 | if((port = plugin_settings.port) != 0xFF) {
141 |
142 | xbar_t *portinfo = ioport_get_info(Port_Digital, Port_Input, port);
143 | if(portinfo && (portinfo->cap.irq_mode & IRQ_Mode_Change) && ioport_claim(Port_Digital, Port_Input, &port, "Motor supply monitor")) {
144 | hal.port.register_interrupt_handler(port, IRQ_Mode_Change, on_power_change);
145 | // TODO add check for power present and raise alarm if not?
146 | } else {
147 | port = 0xFF;
148 | protocol_enqueue_foreground_task(report_warning, "Motor supply monitor plugin failed to claim needed port!");
149 | }
150 | }
151 | }
152 |
153 | // Add info about our plugin to the $I report.
154 | static void onReportOptions (bool newopt)
155 | {
156 | on_report_options(newopt);
157 |
158 | if(!newopt)
159 | report_plugin("Motor supply monitor", "0.03");
160 | }
161 |
162 | // A call my_plugin_init will be issued automatically at startup.
163 | // There is no need to change any source code elsewhere.
164 | void my_plugin_init (void)
165 | {
166 | // Settings descriptor used by the core when interacting with this plugin.
167 | static setting_details_t setting_details = {
168 | .settings = power_settings,
169 | .n_settings = sizeof(power_settings) / sizeof(setting_detail_t),
170 | #ifndef NO_SETTINGS_DESCRIPTIONS
171 | .descriptions = power_settings_descr,
172 | .n_descriptions = sizeof(power_settings_descr) / sizeof(setting_descr_t),
173 | #endif
174 | .save = power_settings_save,
175 | .load = power_settings_load,
176 | .restore = power_settings_restore
177 | };
178 |
179 | // If auxiliary input available and enough free non volatile memory register our plugin with the core.
180 | if(ioport_can_claim_explicit() &&
181 | (n_ports = ioports_available(Port_Digital, Port_Input)) &&
182 | (nvs_address = nvs_alloc(sizeof(power_settings_t)))) {
183 |
184 | // Create a string of the highest port number allowed, used for $-setting input validation.
185 | strcpy(max_port, uitoa(n_ports - 1));
186 |
187 | // Register settings.
188 | settings_register(&setting_details);
189 |
190 | // Add our plugin to the $I options report.
191 | on_report_options = grbl.on_report_options;
192 | grbl.on_report_options = onReportOptions;
193 | } else
194 | protocol_enqueue_foreground_task(report_warning, "Motor supply monitor plugin failed to claim needed port!");
195 | }
196 |
--------------------------------------------------------------------------------
/my_plugin/Pause_on_SD_file_run/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Pause_on_SD_file_run/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - plugin for pausing (enter feed hold) when a SD file is run.
3 |
4 | A cycle start command has to be issued to start execution.
5 |
6 | Part of grblHAL
7 |
8 | Public domain.
9 | This code is is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | */
13 |
14 | #include "grbl/hal.h"
15 | #include "grbl/gcode.h"
16 |
17 | static on_stream_changed_ptr on_stream_changed;
18 | static on_report_options_ptr on_report_options;
19 |
20 | static void stream_changed (stream_type_t type)
21 | {
22 |
23 | if(on_stream_changed)
24 | on_stream_changed(type);
25 |
26 | if(type == StreamType_SDCard) {
27 | char m1[5];
28 | strcpy(m1, "M1");
29 | gc_execute_block(m1);
30 | }
31 | }
32 |
33 | static void report_options (bool newopt)
34 | {
35 | on_report_options(newopt);
36 |
37 | if(!newopt)
38 | report_plugin("SD Pause", "0.02");
39 | }
40 |
41 | void my_plugin_init (void)
42 | {
43 | on_stream_changed = grbl.on_stream_changed;
44 | grbl.on_stream_changed = stream_changed;
45 |
46 | on_report_options = grbl.on_report_options;
47 | grbl.on_report_options = report_options;
48 | }
49 |
--------------------------------------------------------------------------------
/my_plugin/Persistent_tool/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Persistent_tool/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - user defined plugin for keeping current number tool over reboot
3 |
4 | Set $485=1 to enable, $485=0 to disable.
5 |
6 | Part of grblHAL
7 |
8 | Public domain.
9 | This code is is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | */
13 |
14 | #include "grbl/hal.h"
15 | #include "grbl/report.h"
16 | #include "grbl/nvs_buffer.h"
17 | #include "grbl/nuts_bolts.h"
18 | #include "grbl/protocol.h"
19 |
20 | #if GRBL_BUILD < 20231210
21 | #error Persistent tool plugin requires build 20231210 or later!
22 | #endif
23 |
24 | typedef struct {
25 | bool keep_tool;
26 | tool_id_t tool_id;
27 | } plugin_settings_t;
28 |
29 | static nvs_address_t nvs_address;
30 | static plugin_settings_t my_settings;
31 | static on_report_options_ptr on_report_options;
32 | static on_tool_changed_ptr on_tool_changed;
33 | static on_parser_init_ptr on_parser_init;
34 | static const setting_detail_t user_settings[] = {
35 | { Setting_EnableToolPersistence, Group_Toolchange, "Keep tool number over reboot", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtended, &my_settings.keep_tool, NULL, NULL }
36 | };
37 |
38 | // Write settings to non volatile storage (NVS).
39 | static void plugin_settings_save (void)
40 | {
41 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&my_settings, sizeof(plugin_settings_t), true);
42 | }
43 |
44 | // Restore default settings and write to non volatile storage (NVS).
45 | static void plugin_settings_restore (void)
46 | {
47 | my_settings.keep_tool = false;
48 | my_settings.tool_id = 0;
49 |
50 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&my_settings, sizeof(plugin_settings_t), true);
51 | }
52 |
53 | // Load settings from non volatile storage (NVS).
54 | // If load fails restore to default values.
55 | static void plugin_settings_load (void)
56 | {
57 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&my_settings, nvs_address, sizeof(plugin_settings_t), true) != NVS_TransferResult_OK)
58 | plugin_settings_restore();
59 | }
60 |
61 | // Add info about our plugin to the $I report.
62 | static void onReportOptions (bool newopt)
63 | {
64 | on_report_options(newopt);
65 |
66 | if(!newopt)
67 | report_plugin("Persistent tool", "0.03");
68 | }
69 |
70 | static void onToolChanged (tool_data_t *tool)
71 | {
72 | if(on_tool_changed)
73 | on_tool_changed(tool);
74 |
75 | if(my_settings.keep_tool) {
76 | my_settings.tool_id = tool->tool_id;
77 | plugin_settings_save();
78 | }
79 | }
80 |
81 | static void onParserInit (parser_state_t *gc_state)
82 | {
83 | if(on_parser_init)
84 | on_parser_init(gc_state);
85 |
86 | if(sys.cold_start && my_settings.keep_tool) {
87 | #if N_TOOLS
88 | if(my_settings.tool_id <= N_TOOLS)
89 | gc_state->tool = &grbl.tool_table.tool[my_settings.tool_id];
90 | #else
91 | gc_state->tool->tool_id = gc_state->tool_pending = my_settings.tool_id;
92 | #endif
93 | }
94 | }
95 |
96 | // A call my_plugin_init will be issued automatically at startup.
97 | // There is no need to change any source code elsewhere.
98 | void my_plugin_init (void)
99 | {
100 | // Settings descriptor used by the core when interacting with this plugin.
101 | static setting_details_t setting_details = {
102 | .settings = user_settings,
103 | .n_settings = sizeof(user_settings) / sizeof(setting_detail_t),
104 | .save = plugin_settings_save,
105 | .load = plugin_settings_load,
106 | .restore = plugin_settings_restore
107 | };
108 |
109 | // Try to allocate space for our settings in non volatile storage (NVS).
110 | if((nvs_address = nvs_alloc(sizeof(plugin_settings_t)))) {
111 |
112 | // Add info about our plugin to the $I report.
113 | on_report_options = grbl.on_report_options;
114 | grbl.on_report_options = onReportOptions;
115 |
116 | // Subscribe to parser init event, sets current tool number to stored value on a cold start.
117 | on_parser_init = grbl.on_parser_init;
118 | grbl.on_parser_init = onParserInit;
119 |
120 | // Subscribe to tool changed event, write tool number (tool_id) to NVS when triggered.
121 | on_tool_changed = grbl.on_tool_changed;
122 | grbl.on_tool_changed = onToolChanged;
123 |
124 | // Register our settings with the core.
125 | settings_register(&setting_details);
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/my_plugin/Probe_select/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Probe_select/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - plugin template for using an auxiliary output to control a probe selection relay.
3 |
4 | Part of grblHAL
5 |
6 | Public domain.
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 |
11 | Use the $pins command to find out which output port/pin is used, it will be labeled "Probe relay".
12 | If driver supports aux port remapping a setting for selecting which port to use will be
13 | available, see below.
14 |
15 | NOTE: If no auxiliary output is available it will not install itself.
16 |
17 | M401 - switch on relay immediately.
18 | M401Q0 - set mode to switch on relay when probing @ G59.3 (default).
19 | M401Q1 - set mode to switch on relay when probing @ G59.3 while changing tool (executing M6 when $341 tool change mode is 1, 2 or 3).
20 | M401Q2 - set mode to switch on relay when probing while changing tool (executing M6).
21 | M401Q3 - set mode to always switch on relay when probing.
22 | M401Q4 - set mode to never switch on relay when probing.
23 | M401Q5 - set mode to leave relay in current state when probing.
24 | M402 - switch off relay immediately.
25 |
26 | NOTES: The symbol TOOLSETTER_RADIUS (defined in grbl/config.h, default 5.0mm) is the tolerance for checking "@ G59.3".
27 | When $341 tool change mode 1 or 2 is active it is possible to jog to/from the G59.3 position.
28 | Automatic relay switching when probing at the G59.3 position requires the machine to be homed (X and Y).
29 |
30 | Tip: Set default mode at startup by adding M401Qx to a startup script ($N0 or $N1)
31 |
32 | */
33 |
34 | #include "grbl/hal.h"
35 | #include "grbl/protocol.h"
36 |
37 | #include
38 | #include
39 |
40 | #define RELAY_DEBOUNCE 50 // ms - increase if relay is slow and/or bouncy
41 |
42 | typedef enum {
43 | ProbeMode_AtG59_3 = 0,
44 | ProbeMode_ToolChangeAtG59_3,
45 | ProbeMode_ToolChange,
46 | ProbeMode_Always,
47 | ProbeMode_Never,
48 | ProbeMode_Manual,
49 | ProbeMode_MaxValue = ProbeMode_Manual,
50 | } probe_mode_t;
51 |
52 | static uint8_t relay_port;
53 | static bool relay_on = false;
54 | static probe_mode_t probe_mode = ProbeMode_AtG59_3; // Default mode
55 | static driver_reset_ptr driver_reset;
56 | static on_probe_toolsetter_ptr on_probe_toolsetter;
57 | static user_mcode_ptrs_t user_mcode;
58 | static on_report_options_ptr on_report_options;
59 |
60 |
61 | // Later versions of grblHAL and the driver may allow configuring which aux port to use for relay control.
62 | // If possible the plugin adds a $setting and delay claiming the port until settings has been loaded.
63 | // The default setting number is Setting_UserDefined_0 ($450), this can be changed by
64 | // modifying the RELAY_PLUGIN_SETTING symbol below.
65 |
66 | #include "grbl/nvs_buffer.h"
67 |
68 | #define RELAY_PLUGIN_SETTING Setting_UserDefined_0
69 |
70 | static uint8_t n_ports;
71 | static char max_port[4];
72 |
73 | typedef struct {
74 | uint8_t port;
75 | } relay_settings_t;
76 |
77 | static nvs_address_t nvs_address;
78 | static on_report_options_ptr on_report_options;
79 | static relay_settings_t relay_settings;
80 |
81 | static user_mcode_type_t mcode_check (user_mcode_t mcode)
82 | {
83 | return mcode == (user_mcode_t)401 || mcode == (user_mcode_t)402
84 | ? UserMCode_Normal
85 | : (user_mcode.check ? user_mcode.check(mcode) : UserMCode_Unsupported);
86 | }
87 |
88 | static status_code_t mcode_validate (parser_block_t *gc_block)
89 | {
90 | status_code_t state = Status_OK;
91 |
92 | switch((uint16_t)gc_block->user_mcode) {
93 |
94 | case 401:
95 | if(gc_block->words.q) {
96 | if(isnanf(gc_block->values.q))
97 | state = Status_BadNumberFormat;
98 | else {
99 | if(!isintf(gc_block->values.q) || gc_block->values.q < 0.0f || (probe_mode_t)gc_block->values.q > ProbeMode_MaxValue)
100 | state = Status_GcodeValueOutOfRange;
101 | gc_block->words.q = Off;
102 | }
103 | }
104 | break;
105 |
106 | case 402:
107 | break;
108 |
109 | default:
110 | state = Status_Unhandled;
111 | break;
112 | }
113 |
114 | return state == Status_Unhandled && user_mcode.validate ? user_mcode.validate(gc_block) : state;
115 | }
116 |
117 | static void mcode_execute (uint_fast16_t state, parser_block_t *gc_block)
118 | {
119 | bool handled = true;
120 |
121 | if (state != STATE_CHECK_MODE)
122 | switch((uint16_t)gc_block->user_mcode) {
123 |
124 | case 401:
125 | if(gc_block->words.q)
126 | probe_mode = (probe_mode_t)gc_block->values.q;
127 | else {
128 | relay_on = true;
129 | hal.port.digital_out(relay_port, 1);
130 | hal.delay_ms(RELAY_DEBOUNCE, NULL); // Delay a bit to let any contact bounce settle.
131 | }
132 | break;
133 |
134 | case 402:
135 | relay_on = false;
136 | hal.port.digital_out(relay_port, 0);
137 | hal.delay_ms(RELAY_DEBOUNCE, NULL); // Delay a bit to let any contact bounce settle.
138 | break;
139 |
140 | default:
141 | handled = false;
142 | break;
143 | }
144 |
145 | if(!handled && user_mcode.execute)
146 | user_mcode.execute(state, gc_block);
147 | }
148 |
149 | // When called from "normal" probing tool is always NULL, when called from within
150 | // a tool change sequence (M6) then tool is a pointer to the selected tool.
151 | bool probeToolSetter (tool_data_t *tool, coord_data_t *position, bool at_g59_3, bool on)
152 | {
153 | if(on_probe_toolsetter)
154 | on_probe_toolsetter(tool, position, at_g59_3, on);
155 |
156 | if(on) switch(probe_mode) {
157 |
158 | case ProbeMode_AtG59_3:
159 | relay_on = at_g59_3;
160 | break;
161 |
162 | case ProbeMode_ToolChangeAtG59_3:
163 | relay_on = tool != NULL && at_g59_3;
164 | break;
165 |
166 | case ProbeMode_ToolChange:
167 | relay_on = tool != NULL;
168 | break;
169 |
170 | case ProbeMode_Never:
171 | relay_on = false;
172 | break;
173 |
174 | case ProbeMode_Always:
175 | relay_on = true;
176 | break;
177 |
178 | default:
179 | break;
180 |
181 | } else if(probe_mode != ProbeMode_Manual)
182 | relay_on = false;
183 |
184 | hal.port.digital_out(relay_port, relay_on);
185 | hal.delay_ms(RELAY_DEBOUNCE, NULL); // Delay a bit to let any contact bounce settle.
186 |
187 | return relay_on;
188 | }
189 |
190 | static void probe_reset (void)
191 | {
192 | driver_reset();
193 |
194 | relay_on = false;
195 | hal.port.digital_out(relay_port, false);
196 | }
197 |
198 | static status_code_t set_port (setting_id_t setting, float value)
199 | {
200 | if(!isintf(value))
201 | return Status_BadNumberFormat;
202 |
203 | relay_settings.port = value < 0.0f ? 0xFF : (uint8_t)value;
204 |
205 | return Status_OK;
206 | }
207 |
208 | static float get_port (setting_id_t setting)
209 | {
210 | return relay_settings.port >= n_ports ? -1.0f : (float) relay_settings.port;
211 | }
212 |
213 | static const setting_detail_t user_settings[] = {
214 | { RELAY_PLUGIN_SETTING, Group_AuxPorts, "Relay aux port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } }
215 | };
216 |
217 | #ifndef NO_SETTINGS_DESCRIPTIONS
218 |
219 | static const setting_descr_t relay_settings_descr[] = {
220 | { RELAY_PLUGIN_SETTING, "Aux port number to use for probe relay control. Set to -1 to disable." }
221 | };
222 |
223 | #endif
224 |
225 | // Write settings to non volatile storage (NVS).
226 | static void plugin_settings_save (void)
227 | {
228 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&relay_settings, sizeof(relay_settings_t), true);
229 | }
230 |
231 | // Restore default settings and write to non volatile storage (NVS).
232 | // Default is highest numbered free port.
233 | static void plugin_settings_restore (void)
234 | {
235 | // Find highest numbered port that supports change interrupt, or keep the current one if found.
236 | relay_settings.port = ioport_find_free(Port_Digital, Port_Output, (pin_cap_t){ .claimable = On }, "Probe relay");
237 |
238 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&relay_settings, sizeof(relay_settings_t), true);
239 | }
240 |
241 | // Load our settings from non volatile storage (NVS).
242 | // If load fails restore to default values.
243 | static void plugin_settings_load (void)
244 | {
245 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&relay_settings, nvs_address, sizeof(relay_settings_t), true) != NVS_TransferResult_OK)
246 | plugin_settings_restore();
247 |
248 | // Sanity check
249 | if(relay_settings.port >= n_ports)
250 | relay_settings.port = 0xFF;
251 |
252 | if((relay_port = relay_settings.port) != 0xFF) {
253 |
254 | if(ioport_claim(Port_Digital, Port_Output, &relay_port, "Probe relay")) {
255 |
256 | memcpy(&user_mcode, &grbl.user_mcode, sizeof(user_mcode_ptrs_t));
257 |
258 | grbl.user_mcode.check = mcode_check;
259 | grbl.user_mcode.validate = mcode_validate;
260 | grbl.user_mcode.execute = mcode_execute;
261 |
262 | driver_reset = hal.driver_reset;
263 | hal.driver_reset = probe_reset;
264 |
265 | on_probe_toolsetter = grbl.on_probe_toolsetter;
266 | grbl.on_probe_toolsetter = probeToolSetter;
267 |
268 | } else
269 | protocol_enqueue_foreground_task(report_warning, "Relay plugin: configured port number is not available");
270 | }
271 | }
272 |
273 | static void onReportOptions (bool newopt)
274 | {
275 | on_report_options(newopt);
276 |
277 | if(!newopt)
278 | report_plugin("Probe select", "0.08");
279 | }
280 |
281 | void my_plugin_init (void)
282 | {
283 | // Settings descriptor used by the core when interacting with this plugin.
284 | static setting_details_t setting_details = {
285 | .settings = user_settings,
286 | .n_settings = sizeof(user_settings) / sizeof(setting_detail_t),
287 | #ifndef NO_SETTINGS_DESCRIPTIONS
288 | .descriptions = relay_settings_descr,
289 | .n_descriptions = sizeof(relay_settings_descr) / sizeof(setting_descr_t),
290 | #endif
291 | .save = plugin_settings_save,
292 | .load = plugin_settings_load,
293 | .restore = plugin_settings_restore
294 | };
295 |
296 | if(ioport_can_claim_explicit() &&
297 | (n_ports = ioports_available(Port_Digital, Port_Output)) &&
298 | (nvs_address = nvs_alloc(sizeof(relay_settings_t)))) {
299 |
300 | strcpy(max_port, uitoa(n_ports - 1));
301 |
302 | on_report_options = grbl.on_report_options;
303 | grbl.on_report_options = onReportOptions;
304 |
305 | settings_register(&setting_details);
306 | } else
307 | protocol_enqueue_foreground_task(report_warning, "Probe select plugin failed to initialize!");
308 | }
309 |
--------------------------------------------------------------------------------
/my_plugin/Probe_select2/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Probe_select2/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - plugin template for using an auxiliary input for a second probe input.
3 | optionally a second input can be assigned for an overtravel input (by compile time option).
4 |
5 | Part of grblHAL
6 |
7 | Public domain.
8 | This code is is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 |
12 | Use the $pins command to find out which input port/pin is used, it will be labeled "Probe 2".
13 |
14 | NOTE: If no auxiliary input is available it will not install itself.
15 |
16 | M401 - switch to probe2 immediately.
17 | M401Q0 - set mode to switch to probe2 when probing @ G59.3 (default).
18 | M401Q1 - set mode to switch to probe2 when probing @ G59.3 while changing tool (executing M6 when $341 tool change mode is 1, 2 or 3).
19 | M401Q2 - set mode to switch to probe2 when probing while changing tool (executing M6).
20 | M401Q3 - set mode to always use probe2 when probing.
21 | M401Q4 - set mode to never use probe2 when probing.
22 | M401Q5 - set mode to leave probe2 in current state when probing.
23 | M402 - switch off probe2 use immediately.
24 |
25 | NOTES: The symbol TOOLSETTER_RADIUS (defined in grbl/config.h, default 5.0mm) is the tolerance for checking "@ G59.3".
26 | When $341 tool change mode 1 or 2 is active it is possible to jog to/from the G59.3 position.
27 | Automatic relay switching when probing at the G59.3 position requires the machine to be homed (X and Y).
28 |
29 | Tip: Set default mode at startup by adding M401Qx to a startup script ($N0 or $N1)
30 |
31 | */
32 |
33 | #include "grbl/hal.h"
34 | #include "grbl/protocol.h"
35 | #include "grbl/task.h"
36 |
37 | #include
38 | #include
39 |
40 | typedef enum {
41 | ProbeMode_AtG59_3 = 0,
42 | ProbeMode_ToolChangeAtG59_3,
43 | ProbeMode_ToolChange,
44 | ProbeMode_Always,
45 | ProbeMode_Never,
46 | ProbeMode_Manual,
47 | ProbeMode_MaxValue = ProbeMode_Manual,
48 | } probe_mode_t;
49 |
50 | static uint8_t probe_port;
51 | static bool use_probe2 = false;
52 | static probe_state_t probe = {
53 | .connected = On
54 | };
55 | static probe_mode_t probe_mode = ProbeMode_AtG59_3; // Default mode
56 | static driver_reset_ptr driver_reset;
57 | static probe_configure_ptr probe_configure;
58 | static probe_get_state_ptr probe_get_state;
59 | static on_probe_toolsetter_ptr on_probe_toolsetter;
60 | static user_mcode_ptrs_t user_mcode;
61 | static on_report_options_ptr on_report_options;
62 | static driver_reset_ptr driver_reset;
63 | static uint8_t overtravel_port;
64 | static control_signals_get_state_ptr control_signals_get_state;
65 |
66 | #include "grbl/nvs_buffer.h"
67 |
68 | #define PROBE_PLUGIN_SETTING Setting_UserDefined_0
69 | #define PROBE_PLUGIN_SETTING1 Setting_UserDefined_1
70 |
71 | static uint8_t n_ports;
72 | static char max_port[4];
73 |
74 | typedef struct {
75 | uint8_t probe_port;
76 | uint8_t overtravel_port;
77 | } probe2_settings_t;
78 |
79 | static nvs_address_t nvs_address;
80 | static on_report_options_ptr on_report_options;
81 | static probe2_settings_t probe2_settings;
82 |
83 | static user_mcode_type_t mcode_check (user_mcode_t mcode)
84 | {
85 | return mcode == (user_mcode_t)401 || mcode == (user_mcode_t)402
86 | ? UserMCode_Normal
87 | : (user_mcode.check ? user_mcode.check(mcode) : UserMCode_Unsupported);
88 | }
89 |
90 | static status_code_t mcode_validate (parser_block_t *gc_block)
91 | {
92 | status_code_t state = Status_OK;
93 |
94 | switch((uint16_t)gc_block->user_mcode) {
95 |
96 | case 401:
97 | if(gc_block->words.q) {
98 | if(isnanf(gc_block->values.q))
99 | state = Status_BadNumberFormat;
100 | else {
101 | if(!isintf(gc_block->values.q) || gc_block->values.q < 0.0f || (probe_mode_t)gc_block->values.q > ProbeMode_MaxValue)
102 | state = Status_GcodeValueOutOfRange;
103 | gc_block->words.q = Off;
104 | }
105 | }
106 | break;
107 |
108 | case 402:
109 | break;
110 |
111 | default:
112 | state = Status_Unhandled;
113 | break;
114 | }
115 |
116 | return state == Status_Unhandled && user_mcode.validate ? user_mcode.validate(gc_block) : state;
117 | }
118 |
119 | static void mcode_execute (uint_fast16_t state, parser_block_t *gc_block)
120 | {
121 | bool handled = true;
122 |
123 | if (state != STATE_CHECK_MODE)
124 | switch((uint16_t)gc_block->user_mcode) {
125 |
126 | case 401:
127 | if(gc_block->words.q)
128 | probe_mode = (probe_mode_t)gc_block->values.q;
129 | else
130 | use_probe2 = true;
131 | break;
132 |
133 | case 402:
134 | use_probe2 = false;
135 | break;
136 |
137 | default:
138 | handled = false;
139 | break;
140 | }
141 |
142 | if(!handled && user_mcode.execute)
143 | user_mcode.execute(state, gc_block);
144 | }
145 |
146 | // When called from "normal" probing tool is always NULL, when called from within
147 | // a tool change sequence (M6) then tool is a pointer to the selected tool.
148 | bool probeToolSetter (tool_data_t *tool, coord_data_t *position, bool at_g59_3, bool on)
149 | {
150 | if(on_probe_toolsetter)
151 | on_probe_toolsetter(tool, position, at_g59_3, on);
152 |
153 | if(on) switch(probe_mode) {
154 |
155 | case ProbeMode_AtG59_3:
156 | use_probe2 = at_g59_3;
157 | break;
158 |
159 | case ProbeMode_ToolChangeAtG59_3:
160 | use_probe2 = tool != NULL && at_g59_3;
161 | break;
162 |
163 | case ProbeMode_ToolChange:
164 | use_probe2 = tool != NULL;
165 | break;
166 |
167 | case ProbeMode_Never:
168 | use_probe2 = false;
169 | break;
170 |
171 | case ProbeMode_Always:
172 | use_probe2 = true;
173 | break;
174 |
175 | default:
176 | break;
177 |
178 | } else if(probe_mode != ProbeMode_Manual)
179 | use_probe2 = false;
180 |
181 | return use_probe2;
182 | }
183 |
184 | static control_signals_t signalsGetState (void)
185 | {
186 | control_signals_t signals = control_signals_get_state();
187 |
188 | signals.probe_overtravel = hal.port.wait_on_input(Port_Digital, overtravel_port, WaitMode_Immediate, 0.0f);
189 |
190 | return signals;
191 | }
192 |
193 | static void on_overtravel (uint8_t port, bool state)
194 | {
195 | hal.control.interrupt_callback(signalsGetState());
196 | }
197 |
198 | // Sets up the probe pin invert mask to
199 | // appropriately set the pin logic according to setting for normal-high/normal-low operation
200 | // and the probing cycle modes for toward-workpiece/away-from-workpiece.
201 | static void probeConfigure (bool is_probe_away, bool probing)
202 | {
203 | xbar_t *portinfo = hal.port.get_pin_info(Port_Digital, Port_Input, probe_port);
204 |
205 | probe.inverted = is_probe_away ? !portinfo->mode.inverted : portinfo->mode.inverted;
206 | probe.triggered = Off;
207 | probe.is_probing = probing;
208 |
209 | probe_configure(is_probe_away, probing);
210 | }
211 |
212 | // Returns the probe connected and triggered pin states.
213 | static probe_state_t probeGetState (void)
214 | {
215 | probe_state_t state = probe_get_state();
216 |
217 | if(use_probe2) {
218 | state.triggered = hal.port.wait_on_input(Port_Digital, probe_port, WaitMode_Immediate, 0.0f) ^ probe.inverted;
219 | state.connected = probe.connected;
220 | }
221 |
222 | return state;
223 | }
224 |
225 | static void probeReset (void)
226 | {
227 | driver_reset();
228 |
229 | use_probe2 = false;
230 | }
231 |
232 | static status_code_t set_port (setting_id_t setting, float value)
233 | {
234 | status_code_t status;
235 |
236 | if((status = isintf(value) ? Status_OK : Status_BadNumberFormat) == Status_OK)
237 | switch(setting) {
238 |
239 | case PROBE_PLUGIN_SETTING:
240 | probe2_settings.probe_port = value < 0.0f ? 255 : (uint8_t)value;
241 | break;
242 |
243 | case PROBE_PLUGIN_SETTING1:
244 | probe2_settings.overtravel_port = value < 0.0f ? 255 : (uint8_t)value;
245 | break;
246 |
247 | default:
248 | break;
249 | }
250 |
251 | return status;
252 | }
253 |
254 | static float get_port (setting_id_t setting)
255 | {
256 | float value = 0.0f;
257 |
258 | switch(setting) {
259 |
260 | case PROBE_PLUGIN_SETTING:
261 | value = probe2_settings.probe_port >= n_ports ? -1.0f : (float)probe2_settings.probe_port;
262 | break;
263 |
264 | case PROBE_PLUGIN_SETTING1:
265 | value = probe2_settings.overtravel_port >= n_ports ? -1.0f : (float)probe2_settings.overtravel_port;
266 | break;
267 |
268 | default:
269 | break;
270 | }
271 |
272 | return value;
273 | }
274 |
275 | // Add info about our settings for $help and enumerations.
276 | // Potentially used by senders for settings UI.
277 | static const setting_detail_t user_settings[] = {
278 | { PROBE_PLUGIN_SETTING, Group_Probing, "Probe 2 aux port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } },
279 | { PROBE_PLUGIN_SETTING1, Group_Probing, "Probe 2 overtravel aux port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } }
280 | };
281 |
282 | #ifndef NO_SETTINGS_DESCRIPTIONS
283 |
284 | static const setting_descr_t probe2_settings_descr[] = {
285 | { PROBE_PLUGIN_SETTING, "Aux port number to use for second probe input. Set to -1 to disable." },
286 | { PROBE_PLUGIN_SETTING1, "Aux port number to use for second probe overtravel input. Set to -1 to disable.\\n"
287 | "If asserted Z hard limit alarm will raised.\\n\\n"
288 | "NOTE: if input inversion is changed with $370 a hard reset is required to reconfigure the port!"
289 | }
290 | };
291 |
292 | #endif
293 |
294 | // Write settings to non volatile storage (NVS).
295 | static void plugin_settings_save (void)
296 | {
297 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&probe2_settings, sizeof(probe2_settings_t), true);
298 | }
299 |
300 | // Restore default settings and write to non volatile storage (NVS).
301 | // Default is highest numbered free port.
302 | static void plugin_settings_restore (void)
303 | {
304 | probe2_settings.overtravel_port = 0xFF;
305 |
306 | // Find highest numbered port, or keep the current one if found.
307 | probe2_settings.probe_port = ioport_find_free(Port_Digital, Port_Input, (pin_cap_t){ .claimable = On }, "Probe 2");
308 |
309 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&probe2_settings, sizeof(probe2_settings_t), true);
310 | }
311 |
312 | // Load our settings from non volatile storage (NVS).
313 | // If load fails restore to default values.
314 | static void plugin_settings_load (void)
315 | {
316 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&probe2_settings, nvs_address, sizeof(probe2_settings_t), true) != NVS_TransferResult_OK)
317 | plugin_settings_restore();
318 |
319 | // Sanity checks
320 | if(probe2_settings.probe_port >= n_ports)
321 | probe2_settings.probe_port = 0xFF;
322 | if(probe2_settings.overtravel_port >= n_ports)
323 | probe2_settings.overtravel_port = 0xFF;
324 |
325 | if((probe_port = probe2_settings.probe_port) != 0xFF) {
326 |
327 | xbar_t *portinfo = ioport_get_info(Port_Digital, Port_Input, probe_port);
328 |
329 | if(portinfo && ioport_claim(Port_Digital, Port_Input, &probe_port, "Probe 2")) {
330 |
331 | memcpy(&user_mcode, &grbl.user_mcode, sizeof(user_mcode_ptrs_t));
332 |
333 | grbl.user_mcode.check = mcode_check;
334 | grbl.user_mcode.validate = mcode_validate;
335 | grbl.user_mcode.execute = mcode_execute;
336 |
337 | driver_reset = hal.driver_reset;
338 | hal.driver_reset = probeReset;
339 |
340 | probe_configure = hal.probe.configure;
341 | hal.probe.configure = probeConfigure;
342 |
343 | probe_get_state = hal.probe.get_state;
344 | hal.probe.get_state = probeGetState;
345 |
346 | on_probe_toolsetter = grbl.on_probe_toolsetter;
347 | grbl.on_probe_toolsetter = probeToolSetter;
348 |
349 | } else
350 | task_run_on_startup(report_warning, "Probe select plugin: probe port is not available");
351 | }
352 |
353 | if((overtravel_port = probe2_settings.overtravel_port) != 0xFF) {
354 |
355 | xbar_t *portinfo = ioport_get_info(Port_Digital, Port_Input, overtravel_port);
356 | if(portinfo && (portinfo->cap.irq_mode & IRQ_Mode_Change) && ioport_claim(Port_Digital, Port_Input, &overtravel_port, "Probe 2 overtravel")) {
357 |
358 | hal.port.register_interrupt_handler(overtravel_port, portinfo->mode.inverted ? IRQ_Mode_Falling : IRQ_Mode_Rising, on_overtravel);
359 |
360 | control_signals_get_state = hal.control.get_state;
361 | hal.control.get_state = signalsGetState;
362 | } else
363 | task_run_on_startup(report_warning, "Probe select plugin: overtravel port is not available");
364 | }
365 | }
366 |
367 | static void report_options (bool newopt)
368 | {
369 | on_report_options(newopt);
370 |
371 | if(!newopt)
372 | report_plugin("Probe select 2", "0.05");
373 | }
374 |
375 | void my_plugin_init (void)
376 | {
377 | // Settings descriptor used by the core when interacting with this plugin.
378 | static setting_details_t setting_details = {
379 | .settings = user_settings,
380 | .n_settings = sizeof(user_settings) / sizeof(setting_detail_t),
381 | #ifndef NO_SETTINGS_DESCRIPTIONS
382 | .descriptions = probe2_settings_descr,
383 | .n_descriptions = sizeof(probe2_settings_descr) / sizeof(setting_descr_t),
384 | #endif
385 | .save = plugin_settings_save,
386 | .load = plugin_settings_load,
387 | .restore = plugin_settings_restore
388 | };
389 |
390 | if(ioport_can_claim_explicit() &&
391 | (n_ports = ioports_available(Port_Digital, Port_Input)) &&
392 | (nvs_address = nvs_alloc(sizeof(probe2_settings_t)))) {
393 |
394 | // Used for setting value validation
395 | strcpy(max_port, uitoa(n_ports - 1));
396 |
397 | on_report_options = grbl.on_report_options;
398 | grbl.on_report_options = report_options;
399 |
400 | settings_register(&setting_details);
401 |
402 | } else
403 | task_run_on_startup(report_warning, "Probe select plugin failed to initialize!");
404 | }
405 |
--------------------------------------------------------------------------------
/my_plugin/README.md:
--------------------------------------------------------------------------------
1 | ## grblHAL my_plugin templates and utilities
2 |
3 | To add a plugin to a build copy _my_plugin.c_ to the folder where _driver.c_ is found.
4 | Do NOT copy _CMakeLists.txt_ as this is used exclusively by the [Web Builder](http://svn.io-engineering.com:8080/) and may interfere with local builds.
5 |
6 | __Note:__ To add a plugin to the firmware for RP2040 and ESP32 builds enable the _AddMyPlugin_ option in _CMakeLists.txt_.
7 |
8 | ---
9 | 2023-08-09
10 |
--------------------------------------------------------------------------------
/my_plugin/Realtime_report_timestamp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Realtime_report_timestamp/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c.c - Real time report timestamp
3 |
4 | Part of grblHAL
5 |
6 | Public domain.
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 |
11 | Adds timestamp in |TS element to real time report, millisecond resolution.
12 |
13 | Use:
14 | M101 to reset to 0 and keep current mode.
15 | M101P0 to reset to zero and exit synchronous mode.
16 | M101P1 to reset to zero and enter synchronous mode (default).
17 | M101P2 switch to use RTC in non synchronous mode when RTC is available.
18 | M101P3 switch to use RTC in synchronous mode when RTC is available.
19 |
20 | NOTE: be sure to set the RTC before switching to RTC output.
21 |
22 | When synchrounous mode is active delay reset until buffered motions has been completed.
23 | */
24 |
25 | #include
26 | #include
27 | #include
28 |
29 | #include "grbl/hal.h"
30 |
31 | static uint32_t offset = 0;
32 | static bool mcode_sync = true, use_rtc = false;
33 | static on_realtime_report_ptr on_realtime_report;
34 | static on_report_options_ptr on_report_options;
35 | static user_mcode_ptrs_t user_mcode;
36 |
37 | static user_mcode_type_t check (user_mcode_t mcode)
38 | {
39 | return mcode == UserMCode_Generic1
40 | ? UserMCode_Normal
41 | : (user_mcode.check ? user_mcode.check(mcode) : UserMCode_Unsupported);
42 | }
43 |
44 | static status_code_t validate (parser_block_t *gc_block)
45 | {
46 | status_code_t state = Status_OK;
47 |
48 | switch(gc_block->user_mcode) {
49 |
50 | case UserMCode_Generic1:
51 | if(gc_block->words.p) {
52 | if(!isintf(gc_block->values.p))
53 | state = Status_BadNumberFormat;
54 | else {
55 | mcode_sync = (uint32_t)gc_block->values.p & 0x01;
56 | if((uint32_t)gc_block->values.p & 0x02) {
57 | struct tm time;
58 | if(!(use_rtc = hal.rtc.get_datetime != NULL && !!hal.rtc.get_datetime(&time)))
59 | state = Status_InvalidStatement;
60 | } else
61 | use_rtc = false;
62 | }
63 | gc_block->words.p = Off;
64 | }
65 | gc_block->user_mcode_sync = mcode_sync;
66 | break;
67 |
68 | default:
69 | state = Status_Unhandled;
70 | break;
71 | }
72 |
73 | return state == Status_Unhandled && user_mcode.validate ? user_mcode.validate(gc_block) : state;
74 | }
75 |
76 | static void execute (sys_state_t state, parser_block_t *gc_block) {
77 |
78 | bool handled = true;
79 |
80 | switch(gc_block->user_mcode) {
81 |
82 | case UserMCode_Generic1:
83 | offset = hal.get_elapsed_ticks();
84 | break;
85 |
86 | default:
87 | handled = false;
88 | break;
89 | }
90 |
91 |
92 | if(!handled && user_mcode.execute)
93 | user_mcode.execute(state, gc_block);
94 | }
95 |
96 | static void onRealtimeReport (stream_write_ptr stream_write, report_tracking_flags_t report)
97 | {
98 | static char buf[30] = {0};
99 |
100 | if(use_rtc) {
101 |
102 | struct tm time;
103 | if(!!hal.rtc.get_datetime(&time))
104 | sprintf(buf, "|TS:%d:%02d:%02d", time.tm_hour, time.tm_min, time.tm_sec);
105 | else
106 | *buf = '\0';
107 |
108 | } else {
109 |
110 | uint32_t ts = hal.get_elapsed_ticks() - offset;
111 | uint32_t ms, s;
112 |
113 | ms = ts % 1000;
114 | ts -= ms;
115 | s = (ts % 60000);
116 | ts -= s;
117 |
118 | sprintf(buf, "|TS:%lu:%02lu,%lu", ts / 60000, s / 1000, ms);
119 | }
120 |
121 | if(*buf)
122 | stream_write(buf);
123 |
124 | if(on_realtime_report)
125 | on_realtime_report(stream_write, report);
126 | }
127 |
128 | static void onReportOptions (bool newopt)
129 | {
130 | on_report_options(newopt);
131 |
132 | if(!newopt)
133 | report_plugin("RT timestamp", "0.04");
134 | }
135 |
136 | void my_plugin_init (void)
137 | {
138 | on_report_options = grbl.on_report_options;
139 | grbl.on_report_options = onReportOptions;
140 |
141 | on_realtime_report = grbl.on_realtime_report;
142 | grbl.on_realtime_report = onRealtimeReport;
143 |
144 | memcpy(&user_mcode, &grbl.user_mcode, sizeof(user_mcode_ptrs_t));
145 |
146 | grbl.user_mcode.check = check;
147 | grbl.user_mcode.validate = validate;
148 | grbl.user_mcode.execute = execute;
149 | }
150 |
--------------------------------------------------------------------------------
/my_plugin/STM32F411_blinky/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - user defined plugin that blinks the LED on a STM32F411 Blackpill
3 |
4 | Part of grblHAL
5 |
6 | Public domain
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 | */
11 |
12 | #include "driver.h"
13 | #include "grbl/task.h"
14 |
15 | static on_report_options_ptr on_report_options;
16 |
17 | // Add info about our plugin to the $I report.
18 | static void on_report_my_options (bool newopt)
19 | {
20 | on_report_options(newopt);
21 |
22 | if(!newopt)
23 | hal.stream.write("[PLUGIN:Blink LED v2.00]" ASCII_EOL);
24 | }
25 |
26 | static void blink_led (void *data)
27 | {
28 | static bool led_on = false;
29 |
30 | if((led_on = !led_on))
31 | GPIOC->ODR |= GPIO_PIN_13;
32 | else
33 | GPIOC->ODR &= ~GPIO_PIN_13;
34 |
35 | // Reschedule the blink LED function
36 | task_add_delayed(blink_led, NULL, 500);
37 | }
38 |
39 | void my_plugin_init (void)
40 | {
41 | // Add info about our plugin to the $I report.
42 | on_report_options = grbl.on_report_options;
43 | grbl.on_report_options = on_report_my_options;
44 |
45 | // Schedule blink LED function to be run from the grblHAL foreground process
46 | task_add_delayed(blink_led, NULL, 500); // 500 ms
47 |
48 | // Enable PC13 as output
49 | GPIO_InitTypeDef GPIO_InitStructure = {
50 | .Mode = GPIO_MODE_OUTPUT_PP,
51 | .Speed = GPIO_SPEED_FREQ_VERY_HIGH,
52 | .Pin = GPIO_PIN_13,
53 | };
54 | HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
55 | }
56 |
--------------------------------------------------------------------------------
/my_plugin/Set_output_on_feed_hold/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Set_output_on_feed_hold/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | my_plugin.c - plugin template for setting auxiliary output on feed hold
4 |
5 | Part of grblHAL
6 |
7 | Public domain.
8 | This code is is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 | */
12 |
13 | #include
14 |
15 | #include "driver.h"
16 | #include "grbl/task.h"
17 | #include "grbl/nvs_buffer.h"
18 |
19 | #define PLUGIN_SETTING Setting_UserDefined_9
20 |
21 | static uint8_t port;
22 | static uint8_t n_ports;
23 | static char max_port[4];
24 |
25 | typedef struct {
26 | uint8_t port;
27 | } plugin_settings_t;
28 |
29 | static nvs_address_t nvs_address;
30 | static plugin_settings_t plugin_settings;
31 | static on_state_change_ptr on_state_change;
32 | static on_report_options_ptr on_report_options;
33 |
34 | static void onStateChanged (sys_state_t state)
35 | {
36 | static sys_state_t last_state = STATE_IDLE;
37 |
38 | if(state != last_state) {
39 | last_state = state;
40 | hal.port.digital_out(port, state == STATE_HOLD);
41 | }
42 |
43 | if(on_state_change) // Call previous function in the chain.
44 | on_state_change(state);
45 | }
46 |
47 | static status_code_t set_port (setting_id_t setting, float value)
48 | {
49 | if(!isintf(value))
50 | return Status_BadNumberFormat;
51 |
52 | plugin_settings.port = value < 0.0f ? 0xFF : (uint8_t)value;
53 |
54 | return Status_OK;
55 | }
56 |
57 | static float get_port (setting_id_t setting)
58 | {
59 | return plugin_settings.port >= n_ports ? -1.0f : (float) plugin_settings.port;
60 | }
61 |
62 | static const setting_detail_t user_settings[] = {
63 | { PLUGIN_SETTING, Group_AuxPorts, "Feed hold aux port", NULL, Format_Decimal, "-#0", "-1", max_port, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } }
64 | };
65 |
66 | #ifndef NO_SETTINGS_DESCRIPTIONS
67 |
68 | static const setting_descr_t plugin_settings_descr[] = {
69 | { PLUGIN_SETTING, "Aux port number to use for feed hold output. Set to -1 to disable." }
70 | };
71 |
72 | #endif
73 |
74 | // Write settings to non volatile storage (NVS).
75 | static void plugin_settings_save (void)
76 | {
77 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plugin_settings, sizeof(plugin_settings_t), true);
78 | }
79 |
80 | // Restore default settings and write to non volatile storage (NVS).
81 | static void plugin_settings_restore (void)
82 | {
83 | // Find highest numbered port, or keep the current one if found.
84 | plugin_settings.port = ioport_find_free(Port_Digital, Port_Output, (pin_cap_t){ .claimable = On }, "Feed hold out");
85 |
86 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plugin_settings, sizeof(plugin_settings_t), true);
87 | }
88 |
89 | // Load our settings from non volatile storage (NVS).
90 | // If load fails restore to default values.
91 | static void plugin_settings_load (void)
92 | {
93 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&plugin_settings, nvs_address, sizeof(plugin_settings_t), true) != NVS_TransferResult_OK)
94 | plugin_settings_restore();
95 |
96 | // Sanity check
97 | if(plugin_settings.port >= n_ports)
98 | plugin_settings.port = 0xFF;
99 |
100 | if((port = plugin_settings.port) != 0xFF) {
101 | if(ioport_claim(Port_Digital, Port_Output, &port, "Feed hold out")) {
102 | on_state_change = grbl.on_state_change; // Subscribe to the state changed event by saving away the original
103 | grbl.on_state_change = onStateChanged; // function pointer and adding ours to the chain.
104 | } else
105 | task_run_on_startup(report_warning, "Feed hold plugin: configured port number is not available");
106 | }
107 | }
108 |
109 | static void onReportOptions (bool newopt)
110 | {
111 | on_report_options(newopt); // Call previous function in the chain.
112 |
113 | if(!newopt) // Add info about us to the $I report.
114 | report_plugin("PLUGIN Template 2", "v0.03");
115 | }
116 |
117 | void my_plugin_init (void)
118 | {
119 | // Settings descriptor used by the core when interacting with this plugin.
120 | static setting_details_t setting_details = {
121 | .settings = user_settings,
122 | .n_settings = sizeof(user_settings) / sizeof(setting_detail_t),
123 | #ifndef NO_SETTINGS_DESCRIPTIONS
124 | .descriptions = plugin_settings_descr,
125 | .n_descriptions = sizeof(plugin_settings_descr) / sizeof(setting_descr_t),
126 | #endif
127 | .save = plugin_settings_save,
128 | .load = plugin_settings_load,
129 | .restore = plugin_settings_restore
130 | };
131 |
132 | if(ioport_can_claim_explicit() &&
133 | (n_ports = ioports_available(Port_Digital, Port_Output)) &&
134 | (nvs_address = nvs_alloc(sizeof(plugin_settings_t)))) {
135 |
136 | strcpy(max_port, uitoa(n_ports - 1));
137 |
138 | settings_register(&setting_details);
139 |
140 | on_report_options = grbl.on_report_options; // Add our plugin to to the options report chain
141 | grbl.on_report_options = onReportOptions; // to tell the user we are active.
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/my_plugin/Solenoid_spindle/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Solenoid_spindle/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - sets PWM output to full power on spindle on, reduces it after a short delay.
3 |
4 | Part of grblHAL
5 |
6 | Public domain
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 | */
11 |
12 | #include "driver.h"
13 |
14 | #include
15 |
16 | #define SOLENOID_HOLD_DELAY 50 // ms
17 | #define SOLENOID_HOLD_FACTOR 0.25f
18 |
19 | static uint32_t power_down = 0;
20 | static spindle_ptrs_t pwm_spindle;
21 | static on_report_options_ptr on_report_options;
22 | static on_execute_realtime_ptr on_execute_realtime;
23 | static on_spindle_select_ptr on_spindle_select;
24 |
25 | static void solenoid_reduce_current (sys_state_t state)
26 | {
27 | if(power_down && hal.get_elapsed_ticks() - power_down >= SOLENOID_HOLD_DELAY)
28 | pwm_spindle.set_state(&pwm_spindle, pwm_spindle.get_state(&pwm_spindle), pwm_spindle.rpm_max * SOLENOID_HOLD_FACTOR);
29 |
30 | on_execute_realtime(state);
31 | }
32 |
33 | static void solenoid_set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
34 | {
35 | power_down = state.on && rpm > 0.0f ? hal.get_elapsed_ticks() : 0;
36 |
37 | pwm_spindle.set_state(&pwm_spindle, state, power_down ? pwm_spindle.rpm_max : rpm);
38 | }
39 |
40 | static bool solenoid_spindle_select (spindle_ptrs_t *spindle)
41 | {
42 | if(spindle->type == SpindleType_PWM) {
43 | memcpy(&pwm_spindle, spindle, sizeof(spindle_ptrs_t));
44 | spindle->set_state = solenoid_set_state;
45 | spindle->cap.laser = Off;
46 | }
47 |
48 | return on_spindle_select == NULL || on_spindle_select(spindle);
49 | }
50 |
51 | static void onReportOptions (bool newopt)
52 | {
53 | on_report_options(newopt);
54 |
55 | if(!newopt)
56 | report_plugin("Solenoid spindle", "1.04");
57 | }
58 |
59 |
60 | void my_plugin_init (void)
61 | {
62 | on_report_options = grbl.on_report_options;
63 | grbl.on_report_options = onReportOptions;
64 |
65 | on_execute_realtime = grbl.on_execute_realtime;
66 | grbl.on_execute_realtime = solenoid_reduce_current;
67 |
68 | on_spindle_select = grbl.on_spindle_select;
69 | grbl.on_spindle_select = solenoid_spindle_select;
70 | }
71 |
--------------------------------------------------------------------------------
/my_plugin/Stepper_enable_control/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(my_plugin INTERFACE)
2 |
3 | target_sources(my_plugin INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
5 | )
6 |
7 | target_include_directories(my_plugin INTERFACE ${CMAKE_CURRENT_LIST_DIR})
8 |
--------------------------------------------------------------------------------
/my_plugin/Stepper_enable_control/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - plugin for M17 & M18 (M84) Marlin style stepper enable/disable commands.
3 |
4 | Part of grblHAL
5 |
6 | Public domain.
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 |
11 | Usage:
12 | M17[X][Y][Z] - enable steppers
13 | M18[X][Y][Z][S] - disable steppers
14 | M84[X][Y][Z][S] - disable steppers
15 |
16 | If no axis words are specified all axes are enabled/disabled
17 | If no delay is specified disable is immediate, else delay is number of seconds.
18 |
19 | https://marlinfw.org/docs/gcode/M017.html
20 | https://marlinfw.org/docs/gcode/M018.html
21 | */
22 |
23 | #include
24 | #include
25 |
26 | #include "grbl/hal.h"
27 | #include "grbl/protocol.h"
28 |
29 | static bool await_disable = false;
30 | static user_mcode_ptrs_t user_mcode;
31 | static on_report_options_ptr on_report_options;
32 | static stepper_enable_ptr stepper_enable;
33 | static axes_signals_t stepper_enabled = {0};
34 |
35 | static void disable_steppers (void *data);
36 |
37 | static void stepperEnable (axes_signals_t enable, bool hold)
38 | {
39 | if(await_disable) {
40 | await_disable = false;
41 | task_delete(disable_steppers, NULL);
42 | }
43 |
44 | stepper_enabled.mask = enable.mask;
45 |
46 | stepper_enable(enable, hold);
47 | }
48 |
49 | static void disable_steppers (void *data)
50 | {
51 | await_disable = false;
52 | stepperEnable(stepper_enabled, false);
53 | }
54 |
55 | static user_mcode_type_t mcode_check (user_mcode_t mcode)
56 | {
57 | return mcode == (user_mcode_t)17 || mcode == (user_mcode_t)18 || mcode == (user_mcode_t)84
58 | ? UserMCode_NoValueWords
59 | : (user_mcode.check ? user_mcode.check(mcode) : UserMCode_Unsupported);
60 | }
61 |
62 | static status_code_t mcode_validate (parser_block_t *gc_block)
63 | {
64 | status_code_t state = Status_OK;
65 |
66 | switch((uint16_t)gc_block->user_mcode) {
67 |
68 | case 17:
69 | gc_block->words.x = gc_block->words.y = gc_block->words.z = Off;
70 | #ifdef A_AXIS
71 | gc_block->words.a = Off;
72 | #endif
73 | #ifdef B_AXIS
74 | gc_block->words.b = Off;
75 | #endif
76 | #ifdef C_AXIS
77 | gc_block->words.c = Off;
78 | #endif
79 | break;
80 |
81 | case 18:
82 | case 84:
83 | if(gc_block->words.s && isnanf(gc_block->values.s))
84 | state = Status_BadNumberFormat;
85 | gc_block->words.s = gc_block->words.x = gc_block->words.y = gc_block->words.z = Off;
86 | #ifdef A_AXIS
87 | gc_block->words.a = Off;
88 | #endif
89 | #ifdef B_AXIS
90 | gc_block->words.b = Off;
91 | #endif
92 | #ifdef C_AXIS
93 | gc_block->words.c = Off;
94 | #endif
95 | break;
96 |
97 | default:
98 | state = Status_Unhandled;
99 | break;
100 | }
101 |
102 | return state == Status_Unhandled && user_mcode.validate ? user_mcode.validate(gc_block) : state;
103 | }
104 |
105 | static void mcode_execute (uint_fast16_t state, parser_block_t *gc_block)
106 | {
107 | bool handled = true;
108 |
109 | static const parameter_words_t axis_words = {
110 | .x = On,
111 | .y = On,
112 | .z = On
113 | #ifdef A_AXIS
114 | , .a = On
115 | #endif
116 | #ifdef B_AXIS
117 | , .b = On
118 | #endif
119 | #ifdef C_AXIS
120 | , .c = On
121 | #endif
122 | };
123 |
124 | if (state != STATE_CHECK_MODE)
125 | switch((uint16_t)gc_block->user_mcode) {
126 |
127 | case 17: // stepper enable
128 | {
129 | if(gc_block->words.mask & axis_words.mask) {
130 | if(gc_block->words.x)
131 | stepper_enabled.x = On;
132 | if(gc_block->words.y)
133 | stepper_enabled.y = On;
134 | if(gc_block->words.z)
135 | stepper_enabled.z = On;
136 | #ifdef A_AXIS
137 | if(gc_block->words.a)
138 | stepper_enabled.a = On;
139 | #endif
140 | #ifdef B_AXIS
141 | if(gc_block->words.b)
142 | stepper_enabled.b = On;
143 | #endif
144 | #ifdef C_AXIS
145 | if(gc_block->words.c)
146 | stepper_enabled.c = On;
147 | #endif
148 | } else
149 | stepper_enabled.mask = AXES_BITMASK;
150 |
151 | stepperEnable(stepper_enabled, false);
152 | }
153 | break;
154 |
155 | case 18: // stepper disable
156 | case 84:
157 | {
158 | if(gc_block->words.mask & axis_words.mask) {
159 | if(gc_block->words.x)
160 | stepper_enabled.x = Off;
161 | if(gc_block->words.y)
162 | stepper_enabled.y = Off;
163 | if(gc_block->words.z)
164 | stepper_enabled.z = Off;
165 | #ifdef A_AXIS
166 | if(gc_block->words.a)
167 | stepper_enabled.a = Off;
168 | #endif
169 | #ifdef B_AXIS
170 | if(gc_block->words.b)
171 | stepper_enabled.b = Off;
172 | #endif
173 | #ifdef C_AXIS
174 | if(gc_block->words.c)
175 | stepper_enabled.c = Off;
176 | #endif
177 | } else
178 | stepper_enabled.mask = 0;
179 |
180 | if(gc_block->words.s && gc_block->values.s > 0.0f) {
181 | if(!await_disable)
182 | await_disable = task_add_delayed(disable_steppers, NULL, (uint32_t)gc_block->values.s * 1000);
183 | } else
184 | stepperEnable(stepper_enabled, false);
185 | }
186 | break;
187 |
188 | default:
189 | handled = false;
190 | break;
191 | }
192 |
193 | if(!handled && user_mcode.execute)
194 | user_mcode.execute(state, gc_block);
195 | }
196 |
197 | static void reportOptions (bool newopt)
198 | {
199 | on_report_options(newopt);
200 |
201 | if(!newopt)
202 | report_plugin("Stepper enable", "0.03");
203 | }
204 |
205 | void my_plugin_init (void)
206 | {
207 | memcpy(&user_mcode, &grbl.user_mcode, sizeof(user_mcode_ptrs_t));
208 |
209 | grbl.user_mcode.check = mcode_check;
210 | grbl.user_mcode.validate = mcode_validate;
211 | grbl.user_mcode.execute = mcode_execute;
212 |
213 | stepper_enable = hal.stepper.enable;
214 | hal.stepper.enable = stepperEnable;
215 |
216 | on_report_options = grbl.on_report_options;
217 | grbl.on_report_options = reportOptions;
218 | }
219 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(hpgl INTERFACE)
2 |
3 | target_sources(hpgl INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/motori.c
5 | ${CMAKE_CURRENT_LIST_DIR}/hpgl.c
6 | ${CMAKE_CURRENT_LIST_DIR}/arc.c
7 | ${CMAKE_CURRENT_LIST_DIR}/clip.c
8 | ${CMAKE_CURRENT_LIST_DIR}/charset0.c
9 | ${CMAKE_CURRENT_LIST_DIR}/font173.c
10 | ${CMAKE_CURRENT_LIST_DIR}/htext.c
11 | ${CMAKE_CURRENT_LIST_DIR}/scale.c
12 | )
13 |
14 | target_include_directories(hpgl INTERFACE ${CMAKE_CURRENT_LIST_DIR})
15 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/README.md:
--------------------------------------------------------------------------------
1 | ## Motöri the Plotter as a grblHAL plugin
2 |
3 | Original code by [Viacheslav Slavinsky](http://sensi.org/~svo/motori/), modified and refactored for grblHAL.
4 |
5 | This plugin adds a HPGL interpreter to grblHAL making grblHAL multilingual.
6 | No changes to the grblHAL code was needed to add this plugin!
7 |
8 | The `$HPGL` command disables the grblHAL gcode interpreter and activates the HPGL interpreter lifted from Motöri.
9 | Use `+` to exit back to normal operation.
10 |
11 | I made the plugin for my [C.ITOH CX-600 plotter](https://hackaday.io/project/183600-citoh-cx-6000-plotter-upgrade) and as an example for how the grblHAL APIs can be used.
12 |
13 | ---
14 | 2022-01-15
15 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/arc.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "hpgl.h"
4 | #include "arc.h"
5 | #include "scale.h"
6 |
7 | static int16_t arc_step;
8 | static uint_fast8_t is_wedge = 0;
9 | static float arc_stepangle, arc_xc, arc_yc, arc_a0, arc_r, arc_phi;
10 |
11 | static bool arc_cfg (user_point_t user_loc)
12 | {
13 | arc_phi = hpgl_state.numpad[2] * M_PI / 180.0f;
14 |
15 | arc_stepangle = fabsf(hpgl_state.numpad[3]);
16 | if (arc_phi < -0.0f)
17 | arc_stepangle = -arc_stepangle;
18 | arc_stepangle *= M_PI / 180.0f;
19 |
20 | arc_step = 0;
21 | arc_xc = hpgl_state.numpad[0];
22 | arc_yc = hpgl_state.numpad[1];
23 | arc_a0 = atan2f(hpgl_state.user_loc.y - arc_yc, hpgl_state.user_loc.x - arc_xc);
24 | if (arc_a0 < -0.0f)
25 | arc_a0 += 2.0f * M_PI;
26 |
27 | arc_r = hypotf(hpgl_state.user_loc.x - arc_xc, hpgl_state.user_loc.y - arc_yc);
28 |
29 | //printf_P(PSTR("AA: (%f,%f) r=%f stepangle=%f a0=%f aend=%f\n"),
30 | // arc_xc, arc_yc, arc_r, arc_stepangle, arc_a0*180/M_PI, (arc_a0+arc_phi)*180/M_PI);
31 |
32 | return arc_phi != 0.0f && arc_r != 0.0f;
33 | }
34 |
35 |
36 | bool arc_init (void)
37 | {
38 | is_wedge = 0;
39 | return arc_cfg(hpgl_state.user_loc);
40 | }
41 |
42 | bool circle_init (hpgl_point_t *target)
43 | {
44 | user_point_t d;
45 |
46 | is_wedge = 0;
47 | d.x = hpgl_state.user_loc.x + hpgl_state.numpad[0];
48 | d.y = hpgl_state.user_loc.y;
49 |
50 | hpgl_state.numpad[2] = 360.0f;
51 | hpgl_state.numpad[3] = hpgl_state.numpad[1];
52 | hpgl_state.numpad[0] = hpgl_state.user_loc.x;
53 | hpgl_state.numpad[1] = hpgl_state.user_loc.y;
54 |
55 | userscale(d, target, &hpgl_state.user_loc);
56 |
57 | return arc_cfg(d);
58 | }
59 |
60 | bool wedge_init (void)
61 | {
62 | is_wedge = 2;
63 | float r = hpgl_state.numpad[0];
64 | arc_phi = hpgl_state.numpad[1] * M_PI / 180.0f;
65 |
66 | hpgl_state.numpad[0] = hpgl_state.user_loc.x;
67 | hpgl_state.numpad[1] = hpgl_state.user_loc.y;
68 | hpgl_state.user_loc.y += r * sinf(arc_phi);
69 | hpgl_state.user_loc.x += r * cosf(arc_phi);
70 |
71 | return arc_cfg(hpgl_state.user_loc);
72 | }
73 |
74 | bool arc_next (hpgl_point_t *target)
75 | {
76 | bool cont = true;
77 | user_point_t d;
78 |
79 | if(is_wedge == 2) {
80 |
81 | is_wedge = 1;
82 | d.x = hpgl_state.numpad[0];
83 | d.y = hpgl_state.numpad[1];
84 |
85 | } else {
86 |
87 | arc_step++;
88 | float alpha = arc_step * arc_stepangle;
89 |
90 | if (fabsf(alpha) > fabsf(arc_phi)) {
91 | alpha = arc_phi;
92 | cont = false;
93 | }
94 |
95 | alpha += arc_a0;
96 | d.x = arc_xc + arc_r * cosf(alpha);
97 | d.y = arc_yc + arc_r * sinf(alpha);
98 | }
99 |
100 | if(!cont && is_wedge == 1) {
101 | cont = true;
102 | is_wedge = 3;
103 | }
104 |
105 | if(is_wedge == 3) {
106 | cont = false;
107 | d.x = hpgl_state.numpad[0];
108 | d.y = hpgl_state.numpad[1];
109 | }
110 |
111 | //printf_P(PSTR("ARC SPAN: (%6.1f %6.1f)-(%6.1f %6.1f)\n"), user_loc.x, user_loc.y, xd, yd);
112 |
113 | userscale(d, target, &hpgl_state.user_loc);
114 |
115 | return cont;//arc_step < arc_steps ? 1 : 0;
116 | }
117 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/arc.h:
--------------------------------------------------------------------------------
1 | #ifndef _ARC_H
2 | #define _ARC_H
3 |
4 | #include "hpgl.h"
5 |
6 | /// Initialize the arc based on current location and scratchpad data.
7 | /// @see numpad
8 | /// @see user_loc
9 | /// @returns 0 if the arc is degenerate (0 degrees or R=0)
10 | bool arc_init (void);
11 |
12 | bool wedge_init (void);
13 |
14 | bool circle_init (hpgl_point_t *target);
15 |
16 | /// Calculate the next chord.
17 | /// @param x next x in absolute stepper coordinates
18 | /// @param y next y in absolute stepper coordinates
19 | /// @returns 0 if this is the last chord
20 | bool arc_next (hpgl_point_t *target);
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/charset0.c:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 1992 - 1994 Heinz W. Werntges. All rights reserved.
3 | Parts Copyright (c) 1999 Martin Kroeker All rights reserved.
4 |
5 | Distributed by Free Software Foundation, Inc.
6 |
7 | This file is part of HP2xx.
8 |
9 | HP2xx is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
11 | to anyone for the consequences of using it or for whether it serves any
12 | particular purpose or works at all, unless he says so in writing. Refer
13 | to the GNU General Public License, Version 2 or later, for full details.
14 |
15 | Everyone is granted permission to copy, modify and redistribute
16 | HP2xx, but only under the conditions described in the GNU General Public
17 | License. A copy of this license is supposed to have been
18 | given to you along with HP2xx so you can know your rights and
19 | responsibilities. It should be in a file named COPYING. Among other
20 | things, the copyright notice and this notice must be preserved on all
21 | copies.
22 |
23 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24 | */
25 |
26 | /**
27 | ** This file defines a standard character set by elementary
28 | ** "draw" & "move" commands. The format is a very compact one from
29 | ** the old days where every byte was still appreciated.
30 | **
31 | ** A font or character set is an array of strings. Each character
32 | ** corresponds to one of these strings, which is addressed by its ASCII code.
33 | **
34 | ** A character is a (NULL-terminated) string of bytes. Each byte
35 | ** codes for a draw or move action according to the code below:
36 | **
37 | ** Bit: 7 6 5 4 3 2 1 0
38 | ** p x x x y y y y
39 | **
40 | ** p: Plot flag. If set, "draw to" new point, else "move to" it.
41 | ** xxx: 3-bit unsigned integer (0...7). X coordinate of new point.
42 | ** yyyy: 4-bit unsigned integer (0..15). Y coordinate of new point.
43 | **
44 | ** The baseline is y = 4 instead of y = 0, so characters with parts
45 | ** below it can be drawn properly without a need for sign bits.
46 | ** Function "code_to_ucoord()" transforms these coordinates into
47 | ** actual user coordinates.
48 | **
49 | ** Example: code for character 'L': "\032\224\324" translates to:
50 | ** moveto(1,10); drawto(1,4); drawto(5,4);
51 | **
52 | ** From the example you can conclude that the font below essentially is
53 | ** defined on a 5x7 grid:
54 | **
55 | ** 0 1 2 3 4 5 6 7
56 | ** 15 . . . . . . . . . : unused
57 | ** 14 . . . . . . . . * : always used
58 | ** 13 . . . . . . . . o : sometimes used
59 | ** 12 . . . . . . . .
60 | ** 11 . . . . . . . .
61 | ** 10 o * * * * * . .
62 | ** 9 o * * * * * . .
63 | ** 8 o * * * * * . .
64 | ** 7 o * * * * * . .
65 | ** 6 o * * * * * . .
66 | ** 5 o * * * * * . .
67 | ** 4 o * * * * * . .
68 | ** 3 o o o o o o . .
69 | ** 2 o o o o o o . .
70 | ** 1 o o o o o o . .
71 | ** 0 o o o o o o . .
72 | **/
73 |
74 |
75 | /**
76 | ** The following array of strings contains the basic character set (set 0).
77 | **
78 | ** NOTE: A nice way to add a new charset would be, e. g., to introduce a
79 | ** ``charset1[]'' as the "alternate" charset and implement the HP-GL
80 | ** commands needed for switching from one to the other.
81 | **/
82 |
83 | const char *const charset0[256] = {
84 | /* 0x00 ... 0x1f */
85 |
86 | /**
87 | ** Some control codes are valid in HPGL. These are handled elsewhere
88 | ** in a font-independent manner, so following codes are dummies:
89 | **/
90 | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
91 | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
92 |
93 | /**
94 | ** Unfortunately, some compilers do not process \xNN properly,
95 | ** so I changed all hex codes (\xNN) into octal codes (\NNN),
96 | ** thereby losing readability but gaining portability.
97 | **/
98 |
99 | /* 0x20 ... 0x2f */
100 | "",
101 | "\064\265\066\272",
102 | "\051\252\111\312",
103 | "\044\252\104\312\026\326\030\330",
104 | "\064\272\131\251\230\247\307\326\305\225",
105 | "\024\332\051\250\270\271\251\066\265\305\306\266",
106 | "\124\230\231\252\271\270\226\225\244\264\326",
107 | "\071\312",
108 | "\132\270\266\324",
109 | "\024\266\270\232",
110 | "\005\351\145\211\072\264",
111 | "\065\271\027\327",
112 | "\064\244\245\265\263\242",
113 | "\027\327",
114 | "\064\244\245\265\264",
115 | "\352",
116 |
117 | /* 0x30 ... 0x3f */
118 | /*
119 | "\025\244\304\325\331\312\252\231\225\331", ** Zero including `/' **
120 | */
121 | "\025\244\304\325\331\312\252\231\225",
122 | "\044\304\064\272\251",
123 | "\031\252\312\331\330\225\224\324",
124 | "\025\244\304\325\326\307\267\332\232",
125 | "\112\227\226\326\107\304",
126 | "\132\232\230\310\327\325\304\244\225",
127 | "\132\272\230\225\244\304\325\326\307\227",
128 | "\032\332\331\226\224",
129 | "\107\330\331\312\252\231\230\247\307\326\325\304\244\225\226\247",
130 | "\044\264\326\331\312\252\231\230\247\327",
131 | "\047\250\270\267\247\045\265\264\244\245",
132 | "\046\247\267\266\246\064\244\245\265\263\242",
133 | "\112\227\304",
134 | "\030\330\026\326",
135 | "\032\307\224",
136 | "\031\252\312\331\330\307\267\266\065\264",
137 |
138 | /* 0x40 ... 0x4f */
139 | "\103\243\224\230\252\312\331\326\305\266\267\310\330",
140 | "\024\231\252\312\331\324\026\326",
141 | "\024\232\312\331\330\307\227\024\304\325\326\307",
142 | "\125\304\244\225\231\252\312\331",
143 | "\024\232\312\331\325\304\224",
144 | "\124\224\232\332\027\307",
145 | "\024\232\332\027\307",
146 | "\131\312\252\231\225\244\304\325\327\247",
147 | "\024\232\124\332\027\327",
148 | "\024\324\064\272\032\332",
149 | "\025\244\304\325\332\232",
150 | "\024\232\027\247\324\047\332",
151 | "\032\224\324",
152 | "\024\232\270\332\324",
153 | "\024\232\324\332",
154 | "\044\225\231\252\312\331\325\304\244",
155 |
156 | /* 0x50 ... 0x5f */
157 | "\024\232\312\331\330\307\227",
158 | "\044\225\231\252\312\331\326\264\244\066\324",
159 | "\024\232\312\331\330\307\227\247\324",
160 | "\025\244\304\325\326\307\247\230\231\252\312\331",
161 | "\064\272\232\332",
162 | "\032\225\244\304\325\332",
163 | "\032\230\264\330\332",
164 | "\032\224\267\324\332",
165 | "\024\332\124\232",
166 | "\032\231\266\264\066\331\332",
167 | "\032\332\224\324",
168 | "\124\264\272\332",
169 | "\032\324",
170 | "\024\264\272\232",
171 | "\030\272\330",
172 | "\023\323",
173 |
174 | /* 0x60 ... 0x6f */
175 | "\053\310",
176 | "\124\244\225\227\250\310\304",
177 | "\024\304\325\327\310\250\052\244",
178 | "\125\304\264\245\247\270\310\327",
179 | "\112\304\244\225\227\250\310\104\324",
180 | "\026\306\327\310\250\227\225\244\324",
181 | "\064\271\312\332\047\307",
182 | "\022\262\303\310\250\227\225\244\304",
183 | "\032\224\030\270\307\304",
184 | "\072\271\050\270\264\044\304",
185 | "\072\271\050\270\263\242\222",
186 | "\024\232\104\226\310",
187 | "\052\272\264\044\304",
188 | "\024\230\027\250\267\264\067\310\327\324",
189 | "\024\230\027\250\270\307\304",
190 | "\044\225\227\250\270\307\305\264\244",
191 |
192 | /* 0x70 ... 0x7f */
193 | "\022\230\270\307\305\264\224",
194 | "\104\244\225\227\250\310\302",
195 | "\030\224\026\270\310",
196 | "\110\250\227\246\266\305\264\224",
197 | "\052\244\304\030\310",
198 | "\030\225\244\304\310",
199 | "\030\226\264\326\330",
200 | "\030\225\244\265\267\065\304\325\330",
201 | "\030\324\024\330",
202 | "\022\326\330\030\226\264",
203 | "\030\310\224\304",
204 | "\113\273\252\250\227\246\244\263\303",
205 | "\073\263",
206 | "\053\273\312\310\327\306\304\263\243",
207 | "\031\252\310\331",
208 | ""
209 | };
210 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/clip.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | // https://educatech.in/c-program-to-implement-sutherland-hodgemann-polygon-clipping-algorithm/
5 |
6 | enum {
7 | TOP = 0x1,
8 | BOTTOM = 0x2,
9 | RIGHT = 0x4,
10 | LEFT = 0x8
11 | };
12 |
13 | typedef uint_fast8_t outcode;
14 |
15 | float xwmin,xwmax,ywmin,ywmax;
16 |
17 | outcode CompOutCode(float x, float y)
18 | {
19 | outcode code = 0;
20 | if(y > ywmax)
21 | code |=TOP;
22 | else if(y < ywmin)
23 | code |= BOTTOM;
24 |
25 | if(x > xwmax)
26 | code |= RIGHT;
27 | else if(x < xwmin)
28 | code |= LEFT;
29 |
30 | return code;
31 | }
32 |
33 | void clip (float x0, float y0, float x1, float y1)
34 | {
35 | outcode outcode0,outcode1,outcodeOut;
36 | bool accept = false, done = false;
37 | outcode0 = CompOutCode(x0, y0);
38 | outcode1 = CompOutCode(x1, y1);
39 |
40 | do {
41 | if(!(outcode0 | outcode1)) {
42 | accept = true;
43 | done = true;
44 | }
45 | else if(!(done = !!(outcode0 & outcode1))) {
46 |
47 | float x, y;
48 |
49 | outcodeOut = outcode0 ? outcode0 : outcode1;
50 |
51 | if(outcodeOut & TOP) {
52 | x = x0 + (x1 - x0) * (ywmax - y0) / (y1 - y0);
53 | y = ywmax;
54 | } else if(outcodeOut & BOTTOM) {
55 | x = x0 + (x1 - x0) * (ywmin - y0) / (y1 - y0);
56 | y = ywmin;
57 | } else if(outcodeOut & RIGHT) {
58 | y = y0 + (y1 - y0) * (xwmax - x0) / (x1 - x0);
59 | x = xwmax;
60 | } else {
61 | y = y0 + (y1 - y0) * (xwmin -x0) / (x1 - x0);
62 | x = xwmin;
63 | }
64 | if(outcodeOut == outcode0) {
65 | x0 = x;
66 | y0 = y;
67 | outcode0 = CompOutCode(x0,y0);
68 | } else {
69 | x1 = x;
70 | y1 = y;
71 | outcode1 = CompOutCode(x1,y1);
72 | }
73 | }
74 | } while(!done);
75 | /*
76 | if(accept)
77 | line(x0,y0,x1,y1);
78 | outtextxy(150,20,"POLYGON AFTER CLIPPING");
79 | rectangle(xwmin,ywmin,xwmax,ywmax);
80 | */
81 | }
82 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/font173.c:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 1992 - 1994 Heinz W. Werntges. All rights reserved.
3 | Parts Copyright (c) 1999 Martin Kroeker All rights reserved.
4 |
5 | Distributed by Free Software Foundation, Inc.
6 |
7 | This file is part of HP2xx.
8 |
9 | HP2xx is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
11 | to anyone for the consequences of using it or for whether it serves any
12 | particular purpose or works at all, unless he says so in writing. Refer
13 | to the GNU General Public License, Version 2 or later, for full details.
14 |
15 | Everyone is granted permission to copy, modify and redistribute
16 | HP2xx, but only under the conditions described in the GNU General Public
17 | License. A copy of this license is supposed to have been
18 | given to you along with HP2xx so you can know your rights and
19 | responsibilities. It should be in a file named COPYING. Among other
20 | things, the copyright notice and this notice must be preserved on all
21 | copies.
22 |
23 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24 | */
25 |
26 | /**
27 | ** This file defines a standard character set by elementary
28 | ** "draw" & "move" commands. The format is a very compact one from
29 | ** the old days where every byte was still appreciated.
30 | **
31 | ** A font or character set is an array of strings. Each character
32 | ** corresponds to one of these strings, which is addressed by its ASCII code.
33 | **
34 | ** A character is a (NULL-terminated) string of bytes. Each byte
35 | ** codes for a draw or move action according to the code below:
36 | **
37 | ** Bit: 7 6 5 4 3 2 1 0
38 | ** p x x x y y y y
39 | **
40 | ** p: Plot flag. If set, "draw to" new point, else "move to" it.
41 | ** xxx: 3-bit unsigned integer (0...7). X coordinate of new point.
42 | ** yyyy: 4-bit unsigned integer (0..15). Y coordinate of new point.
43 | **
44 | ** The baseline is y = 4 instead of y = 0, so characters with parts
45 | ** below it can be drawn properly without a need for sign bits.
46 | ** Function "code_to_ucoord()" transforms these coordinates into
47 | ** actual user coordinates.
48 | **
49 | ** Example: code for character 'L': "\032\224\324" translates to:
50 | ** moveto(1,10); drawto(1,4); drawto(5,4);
51 | **
52 | ** From the example you can conclude that the font below essentially is
53 | ** defined on a 5x7 grid:
54 | **
55 | ** 0 1 2 3 4 5 6 7
56 | ** 15 . . . . . . . . . : unused
57 | ** 14 . . . . . . . . * : always used
58 | ** 13 . . . . . . . . o : sometimes used
59 | ** 12 . . . . . . . .
60 | ** 11 . . . . . . . .
61 | ** 10 o * * * * * . .
62 | ** 9 o * * * * * . .
63 | ** 8 o * * * * * . .
64 | ** 7 o * * * * * . .
65 | ** 6 o * * * * * . .
66 | ** 5 o * * * * * . .
67 | ** 4 o * * * * * . .
68 | ** 3 o o o o o o . .
69 | ** 2 o o o o o o . .
70 | ** 1 o o o o o o . .
71 | ** 0 o o o o o o . .
72 | **/
73 |
74 |
75 | /**
76 | ** The following array of strings contains the ps math font (173),
77 | ** which differs from ventura math (205) only in the sequence of
78 | ** characters in the upper half of the font
79 | **/
80 |
81 | const char *const charset173[256] = {
82 | /* 0x00 ... 0x1f */
83 |
84 | /**
85 | ** Some control codes are valid in HPGL. These are handled elsewhere
86 | ** in a font-independent manner, so following codes are dummies:
87 | **/
88 | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
89 | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
90 |
91 | /**
92 | ** Unfortunately, some compilers do not process \xNN properly,
93 | ** so I changed all hex codes (\xNN) into octal codes (\NNN),
94 | ** thereby losing readability but gaining portability.
95 | **/
96 | /* 0x20 ... 0x2f */
97 | "", /*space */
98 | "\064\265\066\272", /*exclam */
99 | "\031\264\331\047\307", /*forall */
100 | "\044\252\104\312\026\326\030\330", /*hash */
101 | "\031\271\265\225\067\227", /*exists */
102 | "\024\332\051\250\270\271\251\066\265\305\306\266", /* percent */
103 | "\124\230\231\252\271\270\226\225\244\264\326", /*ampersand */
104 | "\031\251\270\267\266\225\067\227", /*ni */
105 | "\132\270\266\324", /* opening brace */
106 | "\024\266\270\232", /* closing brace */
107 | "\005\351\145\211\072\264", /* asterisk */
108 | "\065\271\027\327", /* plus */
109 | "\064\244\245\265\263\242", /* comma */
110 | "\027\327", /* minus */
111 | "\064\244\245\265\264", /* dot */
112 | "\352", /* slash */
113 |
114 | /* 0x30 ... 0x3f */
115 | "\025\244\304\325\331\312\252\231\225",
116 | "\044\304\064\272\251",
117 | "\031\252\312\331\330\225\224\324",
118 | "\025\244\304\325\326\307\267\332\232",
119 | "\112\227\226\326\107\304",
120 | "\132\232\230\310\327\325\304\244\225",
121 | "\132\272\230\225\244\304\325\326\307\227",
122 | "\032\332\331\226\224",
123 | "\107\330\331\312\252\231\230\247\307\326\325\304\244\225\226\247",
124 | "\044\264\326\331\312\252\231\230\247\327",
125 | "\047\250\270\267\247\045\265\264\244\245",
126 | "\046\247\267\266\246\064\244\245\265\263\242",
127 | "\112\227\304",
128 | "\030\330\026\326",
129 | "\032\307\224",
130 | "\031\252\312\331\330\307\267\266\065\264",
131 |
132 | /* 0x40 ... 0x4f */
133 | "\025\325\027\327\030\251\270\310\331", /*congruent */
134 | "\024\231\252\312\331\324\026\326", /* A */
135 | "\024\232\312\331\330\307\227\024\304\325\326\307", /*B*/ "\024\332\124\232", /* Chi (X) */
136 | "\024\272\324\224", /*Delta */
137 | "\124\224\232\332\027\307", /*E*/ "\052\312\072\264\044\304\027\251\311\327\305\245\227", /*Phi */
138 | "\052\332\330\072\264\044\304", /*Gamma */
139 | "\024\232\124\332\027\327", /*H*/ "\024\324\064\272\032\332", /*I*/ "\027\304\330\331\271\270\326", /*vartheta */
140 | "\024\232\027\247\324\047\332", /*K*/ "\024\272\324", /*Lambda */
141 | "\024\232\270\332\324", /*M*/
142 | "\024\232\324\332", /*N*/
143 | "\044\225\231\252\312\331\325\304\244", /*O*/
144 | /* 0x50 ... 0x5f */
145 | "\044\252\032\332\112\304", /*Pi */
146 | "\044\225\231\252\312\331\325\304\244\027\327", /*Theta */
147 | "\024\232\312\331\330\307\227", /*Rho (P) */
148 | "\044\252\032\332\112\304", /*Sigma */
149 | "\064\272\232\332", /*T*/ "\032\231\266\264\066\331\332", /*Y*/ "\042\262\303\304\225\226\247\307", /*varsigma */
150 | "\024\244\226\231\252\312\331\326\304\324", /*Omega */
151 | "\031\232\332\331\050\246\047\307\110\306\025\224\324\325", /*Xi */
152 | "\030\250\246\306\310\330\052\312\072\264\044\304", /*Psi */
153 | "\032\332\224\324", /*Z*/ "\124\264\272\332", /*opening bracket */
154 | "\004\224\225\205\204\104\324\325\305\304\051\271\272\252\251", /*point triangle */
155 | "\024\264\272\232", /* closing bracket */
156 | "\024\324\064\270", /*bottom */
157 | "\023\323", /*underline */
158 |
159 | /* 0x60 ... 0x6f */
160 |
161 | "\034\334", /*overline */
162 | "\025\227\250\270\307\305\264\244\225\107\330\105\324", /* alpha */
163 | "\044\251\272\311\310\267\306\304\264\245", /* beta */
164 | "\027\250\267\265\304\325\024\330", /* chi */
165 | "\045\246\267\307\326\325\304\264\245\107\271\312\331", /*delta */
166 | "\127\310\250\227\246\266\046\225\244\304\325", /* epsilon */
167 | "\026\247\307\326\305\245\226\064\270", /*phi */
168 | "\027\226\303\262\243\326\327", /*gamma */
169 | "\030\247\270\310\327\322\047\245", /* eta */
170 | "067\264\304\305", /* iota */
171 | "\030\227\246\306\327\310\270\264\244", /*varphi */
172 | "\050\244\046\266\330\066\305\304\324", /* kappa */
173 | "\031\251\250\324\024\266", /* lambda */
174 | "\024\250\246\265\306\310\106\325", /* mu */
175 | "\030\226\264\326\327\310", /* nu */
176 | "\044\225\227\250\270\307\305\264\244", /*o */
177 |
178 | /* 0x70 ... 0x7f */
179 | "\044\247\104\307\027\250\267\307\330", /* pi */
180 | "\045\264\305\311\272\251\245\047\307", /* theta */
181 | "\023\246\267\307\326\325\304\264\245", /*rho */
182 | "\045\246\267\307\326\325\304\264\245\107\330", /*sigma */
183 | "\026\267\327\067\264\324", /*tau */
184 | "\030\226\264\326\330", /*v */
185 | "\050\227\225\244\265\266\065\304\325\327\310\047\307", /*var pi */
186 | "\050\227\225\244\265\266\065\304\325\327\310", /*omega */
187 | "\051\250\267\307\330\270\246\265\305\326\266\244\243\262\322\321\301", /*xi */
188 | "\027\247\265\305\327\110\264", /*psi */
189 | "\051\250\267\307\330\270\246\244\243\262\322\321\301", /*zeta */
190 | "\113\273\252\250\227\246\244\263\303", /* opening curly brace */
191 | "\073\263", /* vertical bar */
192 | "\053\273\312\310\327\306\304\263\243", /*closing curly brace */
193 | "\027\250\267\307\330", /* similar */
194 | "", /*blank */
195 |
196 | /* 0x80 ... 0x9f */
197 | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
198 | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
199 |
200 | /* 0xa0 ... 0xaf */
201 |
202 | "", /* space */
203 | "\030\251\270\311\330\070\264\044\304", /* Upsilon */
204 | "\071\312", /* prime */
205 | "\025\325\227\331", /*lessorequal */
206 | "\352", /* slash */
207 | "\026\226\247\247\266\266\245\245\226\66\266\307\326\305\266", /*infinity */
208 | "\131\332\312\270\265\244\224\225\050\310", /* math function */
209 | "\026\247\266\245\226\066\307\326\305\266\067\250\271\310\267\067\264", /*clubsuit */
210 | "\026\271\326\263\226", /* diamondsuit */
211 | "\030\252\271\312\330\265\230", /* heartsuit */
212 | "\026\271\326\305\267\245\226\067\264", /* spadesuit */
213 | "\027\327\106\327\310\046\227\250", /* leftrightarrow */
214 | "\027\327\046\227\250", /* leftarrow */
215 | "\072\264\030\272\330", /*uparrow */
216 | "\027\327\106\327\310", /*rightarrow */
217 | "\072\264\026\264\326", /* downarrow */
218 |
219 |
220 |
221 | /* 0xb0 ... 0xbf */
222 | "\050\251\272\311\310\267\250", /* degree */
223 | "\065\271\027\327\025\325", /* plusminus */
224 | "\051\252\111\312", /* two primes */
225 | "\125\225\327\231", /*greaterorequal */
226 | "\045\310\050\305", /* times */
227 | "\026\226\247\247\266\266\245\245\226\66\266\307\066\305", /* propto */
228 | "\050\271\311\305\264\244\225\226\267\306", /* partial */
229 | "\047\270\307\266\247", /* circle */
230 | "\027\327\065\266\071\272", /*divide */
231 | "\030\330\026\326\045\311", /*notequal */
232 | "\025\325\027\327\031\331", /* identity */
233 | "\026\247\266\306\327\030\251\270\310\331", /* approx */
234 | "\024\225\064\265\124\325", /* ellipsis */
235 | "\072\264", /* center part of braces and brackets */
236 | "\027\327", /* horiz line */
237 | "\027\327\046\227\250\127\330", /* leftanglearrow */
238 |
239 | /* 0xc0 ... 0xcf */
240 |
241 | "\024\245\247\130\307\305\030\324", /* aleph */
242 | "\050\231\252\272\330\132\310\305\264\244\225", /* Im */
243 | "\050\231\252\312\330\307\305\324\107\267\072\265\244\225", /* Re */
244 | "\030\227\245\243\222\224\246\247\270\310\327\325\304\264\245", /*wp */
245 | "\027\230\251\311\330\326\305\245\226\227\045\310\050\305", /* otimes */
246 | "\027\230\251\311\330\326\305\245\226\227\071\265\027\327", /*oplus */
247 | "\027\251\311\327\305\245\227\024\332", /* oslash */
248 | "\025\230\251\311\330\325", /* cap */
249 | "\031\226\245\305\326\331", /* cup */
250 | "\025\305\326\330\311\231", /* superset */
251 | "\026\306\327\310\230\024\324", /* supsetequal */
252 | "\125\245\226\230\251\331\045\311", /* not subset */
253 | "\125\245\226\230\251\331", /* subset */
254 | "\126\246\227\250\330\024\324", /*subsetequal */
255 | "\125\245\226\230\251\331\027\327", /* in */
256 | "\125\245\226\230\251\331\027\327\045\311", /* not in */
257 |
258 | /* 0xd0 ... 0xdf */
259 |
260 | "\132\224\324", /* angle */
261 | "\032\332\264\232", /* nabla */
262 | "\027\230\251\311\330\326\305\245\226\227\046\250\270\267\247\266", /* registered */
263 | "\027\230\251\311\330\326\305\245\226\227\110\270\247\266\306", /* copyright */
264 | "\032\272\052\250\070\272\311\332\330", /* TM */
265 | "\024\231\124\331\031\331", /* prod */
266 | "\026\246\264\311\331", /* root */
267 | "\066\267", /* centered dot */
268 | "\026\326\325", /* neg */
269 | "\024\267\324", /*wedge */
270 | "\030\264\330", /* vee */
271 | "\045\305\047\307\070\226\264\070\326\264", /* leftright doublearrow */
272 | "\045\325\047\327\070\226\264", /* left doublearrow */
273 | "\044\250\104\310\027\272\327", /* up doublearrow */
274 | "\025\305\027\307\070\326\264", /* right doublearrow */
275 | "\052\245\112\305\026\264\326", /* down doublearrow */
276 |
277 | /* 0xe0 ... 0xef */
278 |
279 | "\026\270\326\264\226", /* diamond */
280 | "\112\247\304", /* opening angle bracket */
281 | "\027\230\251\311\330\326\305\245\226\227\046\250\270\267\247\266", /*registered */
282 | "\027\230\251\311\330\326\305\245\226\227\110\270\247\266\306", /* copyright */
283 | "\032\272\052\250\070\272\311\332\330", /* TM */
284 | "\044\252\032\332\112\304", /*Sigma */
285 | "\112\270\264", /* top third of opening brace */
286 | "\072\264", /* center part of braces and brackets */
287 | "\072\266\304", /* lower third of opening brace */
288 | "\112\272\264", /* upper third of opening bracket */
289 | "\072\264", /* center part of braces and brackets */
290 | "\072\264\304", /* lower third of opening bracket */
291 | "\112\270\264", /* top third of opening brace */
292 | "\072\270\247\266\264", /* center part of opening curly brace */
293 | "\072\266\324", /* lower third of opening round brace */
294 | "\072\264", /* center part of braces and brackets */
295 |
296 | /* 0xf0 ... 0xff */
297 |
298 | "", /* empty */
299 | "\052\307\244", /* closing angle bracket */
300 | "\024\243\264\272\313\332", /* integral */
301 | "\112\270\264", /* top third of opening brace */
302 | "\072\264", /* center part of braces */
303 | "\072\266\224", /* lower third of closing brace */
304 | "\052\270\264", /* top third of closing brace */
305 | "\072\264", /* center part of braces */
306 | "\072\266\224", /* lower third of closing brace */
307 | "\052\272\264", /* upper third of closing bracket */
308 | "\072\264", /* center part of braces */
309 | "\072\264\244", /* lower third of closing bracket */
310 | "\052\270\264", /* top third of closing brace */
311 | "\072\270\307\266\264", /* center part of closing curly brace */
312 | "\072\266\224", /* lower third of closing brace */
313 | "" /* blank */
314 | };
315 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/hpgl.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "hpgl.h"
6 | #include "scale.h"
7 |
8 | #include "grbl/hal.h"
9 | #include "grbl/nuts_bolts.h"
10 |
11 | extern const char *const charset0[256];
12 | extern const char *const charset173[256];
13 |
14 | static const hpgl_state_t defaults = {
15 | .chord_angle = 5.0f,
16 | .pen_thickness = .3f,
17 | .plot_relative = false,
18 | .etxchar = ASCII_ETX, // ^C
19 | .term = ASCII_EOL,
20 | .pattern_type = 0,
21 | .pattern_length = 4.0f,
22 | #ifdef HPGL_A3
23 | .character_size.width = 0.187f,
24 | .character_size.height = 0.269f,
25 | #else
26 | .character_size.width = 0.285f,
27 | .character_size.height = 0.375f,
28 | #endif
29 | .use_alt_charset = false,
30 | .text_vertical = false,
31 | .charset = &charset0[0],
32 | .charset_std = &charset0[0],
33 | .charset_alt = &charset173[0],
34 | .first_error = ERR_None,
35 | .last_error = ERR_None,
36 | .errmask = 0,
37 | .alertmask = 223,
38 | .ip_pad[0] = 0,
39 | .ip_pad[1] = 0,
40 | .ip_pad[2] = MAX_X,
41 | .ip_pad[3] = MAX_Y,
42 | .sc_pad[0] = 0,
43 | .sc_pad[1] = MAX_X,
44 | .sc_pad[2] = 0,
45 | .sc_pad[3] = MAX_Y,
46 | .user_loc.x = 0.0f,
47 | .user_loc.y = 0.0f,
48 | .flags.initialized = On,
49 | .flags.ready = On
50 | };
51 |
52 | extern pen_status_t get_pen_status (void);
53 |
54 | hpgl_state_t hpgl_state;
55 | hpgl_char_ptr hpgl_char;
56 |
57 | static hpgl_command_t hpgl_char_inp (char c, hpgl_point_t *target, uint8_t *lb);
58 |
59 | static char scratchpad[SCRATCHPAD_SIZE];
60 |
61 | __attribute__((weak)) void alert_led (bool on)
62 | {
63 | }
64 |
65 | void hpgl_init (void)
66 | {
67 | // pstate = STATE_EXP1;
68 | hpgl_char = hpgl_char_inp;
69 |
70 | memcpy(&hpgl_state, &defaults, sizeof(hpgl_state_t));
71 |
72 | hpgl_set_error(hpgl_state.last_error);
73 |
74 | translate_init_sc();
75 |
76 | hpgl_state.comm.enable_dtr = stream_get_flags(hal.stream).rts_handshake;
77 | }
78 |
79 | static void hpgl_defaults (void)
80 | {
81 | memcpy(&hpgl_state, &defaults, offsetof(hpgl_state_t, ip_pad));
82 |
83 | hpgl_set_error(hpgl_state.last_error);
84 | }
85 |
86 | void hpgl_set_error (hpgl_error_t errnum)
87 | {
88 | if(errnum == ERR_None)
89 | hpgl_state.errmask = 0;
90 | else
91 | hpgl_state.errmask |= (1 << errnum);
92 |
93 | if(hpgl_state.first_error == ERR_None)
94 | hpgl_state.first_error = errnum;
95 |
96 | hpgl_state.last_error = errnum;
97 | hpgl_state.flags.error = hpgl_state.errmask != 0; // ??
98 |
99 | alert_led(!!(hpgl_state.errmask & hpgl_state.alertmask));
100 | }
101 |
102 | hpgl_error_t hpgl_get_error (void)
103 | {
104 | return hpgl_state.last_error;
105 | }
106 |
107 | bool is_numeric (char c, uint_fast8_t idx)
108 | {
109 | // TODO: add check for single decimal point in input?
110 | return idx < sizeof(scratchpad) - 2 && ((c >= '0' && c <= '9') || c == '.' || (idx == 0 && (c == '+' || c == '-')));
111 | }
112 |
113 | bool is_whitespace (char c)
114 | {
115 | return c == ' ' || c == '\n' || c == '\r' || c == 't';
116 | }
117 |
118 | bool is_command_char (char c)
119 | {
120 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
121 | }
122 |
123 | bool is_separator (char *c)
124 | {
125 | bool ok;
126 |
127 | if((ok = *c == ',' || *c == ' ' || *c == '+' || *c == '-'))
128 | *c = '\0';
129 |
130 | return ok;
131 | }
132 |
133 | bool is_terminator (char *c)
134 | {
135 | bool ok;
136 |
137 | if((ok = *c == ';'))
138 | *c = '\0';
139 |
140 | return ok || is_command_char(*c);
141 | }
142 |
143 | bool get_parameter (char *c, uint_fast8_t *idx)
144 | {
145 | if(is_numeric(*c, *idx)) {
146 | scratchpad[*idx] = *c;
147 | (*idx)++;
148 | *c = '\0';
149 | }
150 |
151 | return *idx > 0 && *c != '\0' && ((is_terminator(c)) || is_separator(c));
152 | }
153 |
154 | hpgl_command_t get_instruction (char c)
155 | {
156 | static hpgl_command_t command = 0;
157 |
158 | hpgl_command_t instruction = 0;
159 |
160 | if(is_command_char(c)) {
161 | command = command << 8 | CAPS(c);
162 | } else if(!is_whitespace(c))
163 | command = 0;
164 |
165 | if(command > 255) {
166 | instruction = command;
167 | command = 0;
168 | }
169 |
170 | return instruction;
171 | }
172 |
173 | hpgl_command_t hpgl_char_inp (char c, hpgl_point_t *target, uint8_t *lb)
174 | {
175 | static uint_fast8_t si, numpad_idx;
176 | static hpgl_command_t command = CMD_CONT;
177 |
178 | static bool is_plotting = false, is_labeling = false, is_labelterminator = false;
179 |
180 | *lb = 0;
181 | target->x = target->y = -1;
182 |
183 | if(c == ASCII_ESC) // should never arrive here...
184 | return CMD_CONT;
185 |
186 | if(is_labeling) {
187 | if((is_labeling = c != hpgl_state.etxchar)) {
188 |
189 | *lb = c;
190 |
191 | if(hpgl_state.comm.monitor_on && hpgl_state.comm.monitor_on)
192 | hal.stream.write_char(c);
193 |
194 | return command;
195 | }
196 | return command = CMD_CONT;
197 | }
198 |
199 | if(is_labelterminator) { // DT: Set text terminator
200 | hpgl_state.etxchar = c;
201 | is_labelterminator = false;
202 | return command = CMD_CONT;
203 | }
204 |
205 | if(is_whitespace(c))
206 | return CMD_CONT;
207 |
208 | char t = c;
209 | bool terminated = false;
210 | hpgl_command_t cmd = CMD_CONT;
211 |
212 | if(hpgl_state.comm.monitor_on && hpgl_state.comm.monitor_on)
213 | hal.stream.write_char(c);
214 |
215 | if(command == CMD_CONT) {
216 | t = '\0';
217 | if((command = get_instruction(c)) != CMD_CONT) {
218 |
219 | si = 0;
220 | is_plotting = command == CMD_PA || command == CMD_PD || command == CMD_PR || command == CMD_PU;
221 | is_labelterminator = command == CMD_DT;
222 |
223 | if(command == CMD_PA || command == CMD_PR)
224 | hpgl_state.plot_relative = command == CMD_PR;
225 |
226 | if((is_labeling = command == CMD_LB))
227 | cmd = CMD_LB0;
228 |
229 | numpad_idx = 4;
230 | do {
231 | hpgl_state.numpad[--numpad_idx] = 0.0f;
232 | } while(numpad_idx);
233 | }
234 | } else if(get_parameter(&c, &si)) {
235 |
236 | if((terminated = is_command_char(c)))
237 | get_instruction(c);
238 |
239 | scratchpad[si] = '\0';
240 | si = 0;
241 |
242 | if(numpad_idx < sizeof(hpgl_state.numpad)) {
243 | read_float(scratchpad, &si, &hpgl_state.numpad[numpad_idx++]);
244 | si = 0;
245 | }
246 |
247 | if(is_plotting && numpad_idx == 2)
248 | t = ';';
249 |
250 | } else if((terminated = is_command_char(c)))
251 | get_instruction(c);
252 |
253 | if(t && is_terminator(&t)) {
254 |
255 | switch(command) {
256 |
257 | case CMD_AA:
258 | cmd = CMD_AA;
259 | if (numpad_idx == 3)
260 | hpgl_state.numpad[3] = hpgl_state.chord_angle;
261 | cmd = command;
262 | break;
263 |
264 | case CMD_AR: // AR: Arc Relative - xc, yc, CentralAngle [,Degrees per step]
265 | if(numpad_idx < 3) {
266 | cmd = CMD_ERR;
267 | hpgl_set_error(ERR_WrongParams);
268 | } else {
269 | if (numpad_idx == 3)
270 | hpgl_state.numpad[3] = hpgl_state.chord_angle;
271 | user_point_t f, offset = { 0.0f, 0.0f };
272 | f.x = hpgl_state.numpad[0];
273 | f.y = hpgl_state.numpad[1];
274 | userscalerelative(f, target, &offset);
275 | hpgl_state.numpad[0] = hpgl_state.user_loc.x + offset.x;
276 | hpgl_state.numpad[1] = hpgl_state.user_loc.y + offset.y;
277 | target->x = target->y = -1;
278 | cmd = command;
279 | }
280 | break;
281 |
282 | case CMD_CA: // CA: Designate alternate character set
283 | if(numpad_idx == 0) {
284 | hpgl_state.numpad[0] = 0.0f;
285 | numpad_idx = 1;
286 | }
287 | if(numpad_idx == 1) {
288 | hpgl_state.charset_alt = hpgl_state.numpad[0] == 0.0f ? &charset0[0] : &charset173[0];
289 | if(hpgl_state.use_alt_charset)
290 | hpgl_state.charset = hpgl_state.charset_alt;
291 | } else {
292 | cmd = CMD_ERR;
293 | hpgl_set_error(ERR_UnknownCharset);
294 | }
295 | break;
296 |
297 | case CMD_CI: // CI: Circle - radius [,Degrees per step]
298 | if(numpad_idx > 0) {
299 | if(numpad_idx == 1)
300 | hpgl_state.numpad[1] = hpgl_state.chord_angle;
301 | userscale(hpgl_state.user_loc, target, &hpgl_state.user_loc);
302 | cmd = command;
303 | } else {
304 | cmd = CMD_ERR;
305 | hpgl_set_error(ERR_WrongParams);
306 | }
307 | break;
308 |
309 | case CMD_CP: // CP: Character Plot
310 | if(numpad_idx == 0) {
311 | hpgl_state.numpad[0] = NAN;
312 | hpgl_state.numpad[1] = NAN;
313 | }
314 | if(numpad_idx == 1)
315 | hpgl_state.numpad[1] = 0.0f;
316 | cmd = command;
317 | break;
318 |
319 | case CMD_CS: // CA: Designate standard character set
320 | if(numpad_idx == 0) {
321 | hpgl_state.numpad[0] = 0.0f;
322 | numpad_idx = 1;
323 | }
324 | if(numpad_idx == 1) {
325 | hpgl_state.charset_std = hpgl_state.numpad[0] == 0.0f ? &charset0[0] : &charset173[0];
326 | if(!hpgl_state.use_alt_charset)
327 | hpgl_state.charset = hpgl_state.charset_std;
328 | } else {
329 | cmd = CMD_ERR;
330 | hpgl_set_error(ERR_UnknownCharset);
331 | }
332 | break;
333 |
334 | case CMD_DF: // DF: Set default values
335 | hpgl_defaults();
336 | cmd = command;
337 | break;
338 |
339 | case CMD_DI: // DI: Absolute text direction // numpad contains sin(theta), cos(theta)
340 | if(numpad_idx == 0) {
341 | hpgl_state.numpad[0] = 1.0f;
342 | hpgl_state.numpad[1] = 0.0f;
343 | numpad_idx = 2;
344 | }
345 | if(numpad_idx >= 2) {
346 | // fabs(hpgl_state.numpad[0] + hpgl_state.numpad[1]) > 0.0004f
347 | }
348 | cmd = command;
349 | break;
350 |
351 | case CMD_DV: // DV: Vertical label direction
352 | if(numpad_idx == 1) {
353 | hpgl_state.text_vertical = truncf(hpgl_state.numpad[0]) != 0.0f;
354 | } else {
355 | cmd = CMD_ERR;
356 | hpgl_set_error(ERR_WrongParams);
357 | }
358 | break;
359 |
360 | case CMD_EA: // EA: Edge Absolute
361 | if(numpad_idx < 2) {
362 | cmd = CMD_ERR;
363 | hpgl_set_error(ERR_WrongParams);
364 | } else {
365 | user_point_t f;
366 | f.x = hpgl_state.numpad[0];
367 | f.y = hpgl_state.numpad[1];
368 | userscale(f, target, NULL);
369 | if(numpad_idx > 2)
370 | hpgl_set_error(ERR_WrongParams);
371 | cmd = command;
372 | }
373 | break;
374 |
375 | case CMD_ER: // ER: Edge Relative
376 | if(numpad_idx < 2) {
377 | cmd = CMD_ERR;
378 | hpgl_set_error(ERR_WrongParams);
379 | } else {
380 | user_point_t f;
381 | f.x = hpgl_state.user_loc.x + hpgl_state.numpad[0];
382 | f.y = hpgl_state.user_loc.y + hpgl_state.numpad[1];
383 | userscale(f, target, NULL);
384 | if(numpad_idx > 2)
385 | hpgl_set_error(ERR_WrongParams);
386 | cmd = command;
387 | }
388 | break;
389 |
390 | case CMD_ES: // ES: Extra Space
391 | if(numpad_idx == 0) {
392 | hpgl_state.numpad[0] = 0.0f;
393 | hpgl_state.numpad[1] = 0.0f;
394 | numpad_idx = 2;
395 | } else {
396 | // TODO:
397 | if(numpad_idx > 2)
398 | hpgl_set_error(ERR_WrongParams);
399 | cmd = command;
400 | }
401 | break;
402 |
403 | case CMD_EW: // EW: Edge Wedge
404 | if(numpad_idx > 0) {
405 | if(numpad_idx < 3) {
406 | cmd = CMD_ERR;
407 | hpgl_set_error(ERR_WrongParams);
408 | } else {
409 | if (numpad_idx == 3)
410 | hpgl_state.numpad[3] = hpgl_state.chord_angle;
411 | /* user_point_t f, offset = { 0.0f, 0.0f };
412 | f.x = hpgl_state.numpad[0];
413 | f.y = hpgl_state.numpad[1];
414 | userscalerelative(f, target, &offset);
415 | hpgl_state.numpad[0] = hpgl_state.user_loc.x + offset.x;
416 | hpgl_state.numpad[1] = hpgl_state.user_loc.y + offset.y;
417 | target->x = target->y = -1; */
418 | cmd = command;
419 | }
420 | }
421 | break;
422 |
423 | case CMD_IM: // IM: Input Mask
424 | if(numpad_idx > 0 && hpgl_state.numpad[0] < 256.0f) {
425 | hpgl_state.alertmask = truncf(hpgl_state.numpad[0]);
426 | alert_led(!!(hpgl_state.errmask & hpgl_state.alertmask));
427 | }
428 | break;
429 |
430 | case CMD_IN: // IN: Initialize plotter
431 | hpgl_set_error(ERR_None);
432 | cmd = command;
433 | break;
434 |
435 | case CMD_IP: // IP: Initialize plotter
436 | if(numpad_idx == 0)
437 | translate_init_ip();
438 | else if(numpad_idx == 2 || numpad_idx == 4) {
439 | bool valid;
440 | uint_fast8_t i = numpad_idx;
441 | do {
442 | i--;
443 | valid = hpgl_state.numpad[i] >= 0.0f && (int32_t)truncf(hpgl_state.numpad[i]) <= (i & 0b1 ? MAX_Y : MAX_X);
444 | } while(i && valid);
445 |
446 | if(valid) {
447 |
448 | user_point_t ip = range_P1P2();
449 |
450 | for (i = 0; i < numpad_idx; i++)
451 | hpgl_state.ip_pad[i] = (int32_t)truncf(hpgl_state.numpad[i]);
452 |
453 | if(numpad_idx == 2) {
454 | hpgl_state.ip_pad[2] = hpgl_state.ip_pad[0] + ip.x; // clip!
455 | hpgl_state.ip_pad[3] = hpgl_state.ip_pad[1] + ip.y;
456 | }
457 | }
458 | }
459 | cmd = command;
460 | break;
461 |
462 | case CMD_LB: // LB: Label character
463 | // scanning text
464 | cmd = CMD_LB;
465 | if (!(is_labeling = c != hpgl_state.etxchar)) {
466 | *lb = 0;
467 | command = CMD_CONT;
468 | } else {
469 | target->x = target->y = -1;
470 | *lb = c;
471 | }
472 | cmd = command;
473 | break;
474 |
475 | case CMD_LT: // LT: Line Type
476 | if(numpad_idx == 0) {
477 | hpgl_state.pattern_type = 0;
478 | hpgl_state.pattern_length = 4.0f;
479 | cmd = command;
480 | } else if((cmd = fabsf(hpgl_state.numpad[0]) >= 128.0f ? CMD_ERR : command) == command) {
481 | hpgl_state.numpad[0] = truncf(hpgl_state.numpad[0]);
482 | if(hpgl_state.numpad[0] <= 6.0f)
483 | hpgl_state.pattern_type = hpgl_state.numpad[0] <= 0.0f ? 0 : (uint8_t)hpgl_state.numpad[0];
484 | if(numpad_idx > 1) {
485 | if(hpgl_state.numpad[1] >= 0.0f && hpgl_state.numpad[1] < 128.0f)
486 | hpgl_state.pattern_length = hpgl_state.numpad[1];
487 | else
488 | cmd = CMD_ERR;
489 | } else
490 | hpgl_state.pattern_length = 4.0f;
491 | }
492 | if(cmd == CMD_ERR)
493 | hpgl_set_error(ERR_BadParam);
494 | break;
495 |
496 | case CMD_OA: // OA: Output Actual Position and Pen Status
497 | {
498 | hpgl_point_t position;
499 |
500 | usertohpgl(hpgl_state.user_loc, &position);
501 |
502 | hal.stream.write(uitoa(position.x));
503 | hal.stream.write(",");
504 | hal.stream.write(uitoa(position.y));
505 | hal.stream.write(",");
506 | hal.stream.write(uitoa(get_pen_status() == Pen_Down));
507 | hal.stream.write(hpgl_state.term);
508 | }
509 | break;
510 |
511 | case CMD_OE: // OE: Output Error
512 | hal.stream.write(uitoa(hpgl_state.first_error));
513 | hal.stream.write(hpgl_state.term);
514 | hpgl_state.flags.error = Off;
515 | break;
516 |
517 | case CMD_OF: // OF: Output Factors
518 | hal.stream.write("40,40");
519 | hal.stream.write(hpgl_state.term);
520 | break;
521 |
522 | case CMD_OI: // OI: Output Identification
523 | hal.stream.write(HPGL_DEVICE_IDENTIFICATION);
524 | hal.stream.write(hpgl_state.term);
525 | break;
526 |
527 | case CMD_OO: // OO: Output Options
528 | hal.stream.write("0,0,0,0,1,0,0,0"); // TODO: set second parameter to 1 if pen change is supported
529 | hal.stream.write(hpgl_state.term);
530 | break;
531 |
532 | case CMD_OP: // OP: Output Parameters P1 & P2
533 | output_P1P2();
534 | hpgl_state.flags.p1p2_changed = Off;
535 | break;
536 |
537 | case CMD_OS: // OE: Output Status
538 | hpgl_state.flags.pen_down = get_pen_status() == Pen_Down;
539 | hal.stream.write(uitoa(hpgl_state.flags.value));
540 | hal.stream.write(hpgl_state.term);
541 | hpgl_state.flags.initialized = Off;
542 | break;
543 |
544 | case CMD_OW: // OW: Output Window
545 | case CMD_OH: // OH: Output Hard-clip Limits TODO: implement clip
546 | hal.stream.write("0,0,");
547 | hal.stream.write(uitoa(MAX_X));
548 | hal.stream.write(",");
549 | hal.stream.write(uitoa(MAX_Y));
550 | hal.stream.write(hpgl_state.term);
551 | break;
552 |
553 | case CMD_PA:
554 | case CMD_PD:
555 | case CMD_PR:
556 | case CMD_PU:
557 | switch(numpad_idx) {
558 | case 0:
559 | cmd = command;
560 | command = CMD_CONT;
561 | break;
562 | case 2:
563 | {
564 | user_point_t f;
565 | f.x = hpgl_state.numpad[0];
566 | f.y = hpgl_state.numpad[1];
567 | if(hpgl_state.plot_relative)
568 | userscalerelative(f, target, &hpgl_state.user_loc);
569 | else
570 | userscale(f, target, &hpgl_state.user_loc);
571 | cmd = command;
572 | }
573 | break;
574 | default:
575 | cmd = CMD_ERR;
576 | command = CMD_CONT;
577 | hpgl_set_error(ERR_BadParam);
578 | break;
579 | }
580 | break;
581 |
582 | case CMD_PT: // PT: pen thickness
583 | cmd = command;
584 | if(numpad_idx == 0)
585 | hpgl_state.pen_thickness = 0.3f;
586 | else {
587 |
588 | if(hpgl_state.numpad[0] < 0.1f || hpgl_state.numpad[0] > 5.0f) {
589 | cmd = CMD_ERR;
590 | hpgl_set_error(ERR_BadParam);
591 | } else
592 | hpgl_state.pen_thickness = hpgl_state.numpad[0];
593 |
594 | if(cmd != CMD_ERR && numpad_idx > 1) {
595 | cmd = CMD_ERR;
596 | hpgl_set_error(ERR_WrongParams);
597 | }
598 | }
599 | break;
600 |
601 | case CMD_SA: // SA: select alternate charset
602 | hpgl_state.use_alt_charset = true;
603 | hpgl_state.charset = hpgl_state.charset_alt;
604 | break;
605 |
606 | case CMD_SC: // Scale
607 | if(numpad_idx == 0)
608 | translate_init_sc();
609 | else if(numpad_idx == 4) {
610 | for (uint_fast8_t i = 0; i < 4; i++)
611 | hpgl_state.sc_pad[i] = (int32_t)truncf(hpgl_state.numpad[i]);
612 | translate_scale();
613 | }
614 | cmd = numpad_idx == 0 || numpad_idx == 4 ? command : CMD_ERR;
615 | break;
616 |
617 | case CMD_SI: // SI: Absolute character size
618 | if(numpad_idx == 0) {
619 | hpgl_state.numpad[0] = 0.187f;
620 | hpgl_state.numpad[1] = 0.269f;
621 | numpad_idx = 2;
622 | }
623 | if((cmd = (numpad_idx == 1 ? CMD_ERR : command)) == CMD_ERR)
624 | hpgl_set_error(ERR_WrongParams);
625 | else {
626 | hpgl_state.character_size.width = hpgl_state.numpad[0];
627 | hpgl_state.character_size.height = hpgl_state.numpad[1];
628 | if(numpad_idx > 2)
629 | hpgl_set_error(ERR_WrongParams);
630 | }
631 | break;
632 |
633 | case CMD_SP: // SP: Select Pen
634 | if(numpad_idx == 0)
635 | hpgl_state.numpad[0] = 0.0f;
636 | hpgl_state.pen_thickness = 0.3; // only if pen changed...
637 | cmd = command;
638 | break;
639 |
640 | case CMD_SR: // SR: Relative character size
641 | if(numpad_idx == 0) {
642 | hpgl_state.numpad[0] = 0.75f;
643 | hpgl_state.numpad[1] = 1.5f;
644 | numpad_idx = 2;
645 | }
646 | if((cmd = (numpad_idx == 1 ? CMD_ERR : command)) == CMD_ERR)
647 | hpgl_set_error(ERR_WrongParams);
648 | else {
649 | user_point_t range = range_P1P2();
650 | hpgl_state.character_size.width = range.x * hpgl_state.numpad[0] / 100.0f;
651 | hpgl_state.character_size.height = range.y * hpgl_state.numpad[1] / 100.0f;
652 | if(numpad_idx > 2)
653 | hpgl_set_error(ERR_WrongParams);
654 | }
655 | break;
656 |
657 | case CMD_SS: // SA: select standard charset
658 | hpgl_state.use_alt_charset = false;
659 | hpgl_state.charset = hpgl_state.charset_std;
660 | break;
661 |
662 | case CMD_VS: // VS: Velocity Set
663 | cmd = numpad_idx ? command : CMD_ERR;
664 | break;
665 |
666 | default:
667 | cmd = CMD_ERR;
668 | hpgl_set_error(ERR_UnknownCommand);
669 | break;
670 | }
671 |
672 | numpad_idx = si = 0;
673 | if(terminated || !is_plotting || cmd == CMD_ERR)
674 | command = CMD_CONT;
675 | }
676 |
677 | return cmd;
678 | }
679 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/hpgl.h:
--------------------------------------------------------------------------------
1 | #ifndef _HPGL_H
2 | #define _HPGL_H
3 |
4 | #include
5 | #include
6 |
7 | //#define HPGL_DEBUG
8 |
9 | #ifndef HPGL_DEVICE_IDENTIFICATION
10 | #define HPGL_DEVICE_IDENTIFICATION "HP7574A"
11 | #endif
12 |
13 | #define SCRATCHPAD_SIZE 64
14 |
15 | #define MAX_X_A4 11040
16 | #define MAX_Y_A4 7721
17 | #define P1X_A4 603
18 | #define P1Y_A4 521
19 | #define P2X_A4 10603
20 | #define P2Y_A4 7721
21 |
22 | #define MAX_X_A3 16158
23 | #define MAX_Y_A3 11040
24 | #define P1X_A3 170
25 | #define P1Y_A3 602
26 | #define P2X_A3 15370
27 | #define P2Y_A3 10602
28 |
29 | #ifdef HPGL_A3
30 | #define MAX_X MAX_X_A3
31 | #define MAX_Y MAX_Y_A3
32 | #define P1X P1X_A3
33 | #define P1Y P1Y_A3
34 | #define P2Y P2Y_A3
35 | #define P2X P2X_A3
36 | #else
37 | #define MAX_X MAX_X_A4
38 | #define MAX_Y MAX_Y_A4
39 | #define P1X P1X_A4
40 | #define P1Y P1Y_A4
41 | #define P2Y P2Y_A4
42 | #define P2X P2X_A4
43 | #endif
44 |
45 | typedef enum {
46 | ERR_None = 0,
47 | ERR_UnknownCommand,
48 | ERR_WrongParams,
49 | ERR_BadParam,
50 | ERR_Unused1,
51 | ERR_UnknownCharset,
52 | ERR_PosOverflow,
53 | ERR_Unused2,
54 | Err_WheelsUp
55 | } hpgl_error_t;
56 |
57 | /// HPGL commands. Returned by hpgl_char() when there is data and handled by do_stuff() in motori.c
58 | /// @see do_stuff()
59 | /// @see hpgl_char()
60 | typedef enum {
61 | CMD_CONT = 0, ///< Continue, do nothing (data coming)
62 | CMD_ERR, ///< Error
63 | CMD_LB0, ///< Mark label start!!
64 | CMD_AA = 'A' << 8 | 'A', ///< AA: Arc absolute
65 | CMD_AR = 'A' << 8 | 'R', ///< AR: Arc relative
66 | CMD_AS = 'A' << 8 | 'S', ///< AS: Acceleration Select: 0 = no acceleration (nonstandard)
67 | CMD_CA = 'C' << 8 | 'A', ///< CA: Designate alternate character set
68 | CMD_CI = 'C' << 8 | 'I', ///< CI: Circle
69 | CMD_CP = 'C' << 8 | 'P', ///< CP: Character plot
70 | CMD_CS = 'C' << 8 | 'S', ///< CS: Designate standard character set
71 | CMD_DI = 'D' << 8 | 'I', ///< DI: Label direction: numpad[0]=sin(theta), numpad[1]=cos(theta)
72 | CMD_DF = 'D' << 8 | 'F', ///< DF: Set default values
73 | CMD_DT = 'D' << 8 | 'T', ///< DT: Set text terminator
74 | CMD_DV = 'D' << 8 | 'V', ///< DV: Vertical label direction
75 | CMD_EA = 'E' << 8 | 'A', ///< EA: Rectangle absolute
76 | CMD_ER = 'E' << 8 | 'R', ///< EV: Rectangle relative
77 | CMD_ES = 'E' << 8 | 'S', ///< ES: Extra Space
78 | CMD_EW = 'E' << 8 | 'W', ///< EW: Edge Wedge
79 | CMD_IN = 'I' << 8 | 'N', ///< IN: Initialize
80 | CMD_IM = 'I' << 8 | 'M', ///< IM: Input Mask
81 | CMD_IP = 'I' << 8 | 'P', ///< IP: Initialize plotter
82 | CMD_LB = 'L' << 8 | 'B', ///< LB: Label text
83 | CMD_LT = 'L' << 8 | 'T', ///< LT: Line type
84 |
85 | CMD_OA = 'O' << 8 | 'A', ///< OA: Output Actual Position and Pen Status
86 | CMD_OC = 'O' << 8 | 'C', ///< OC: Output Commanded Position and Pen Status
87 | CMD_OD = 'O' << 8 | 'D', ///< OD: Output Digitized Point and Pen Status
88 | CMD_OE = 'O' << 8 | 'E', ///< OE: Output Error
89 | CMD_OF = 'O' << 8 | 'F', ///< OF: Output Factors
90 | CMD_OH = 'O' << 8 | 'H', ///< OH: Output Hard-clip Limits
91 | CMD_OI = 'O' << 8 | 'I', ///< OI: Output Identification
92 | CMD_OO = 'O' << 8 | 'O', ///< OO: Output Options
93 |
94 | CMD_OP = 'O' << 8 | 'P', ///< OP: Output Parameters P1 & P2
95 |
96 | CMD_OS = 'O' << 8 | 'S', ///< OS: Output Status
97 | CMD_OW = 'O' << 8 | 'W', ///< OW: Output Window
98 |
99 | CMD_PA = 'P' << 8 | 'A', ///< PA: Move to returned coordinates
100 | CMD_PD = 'P' << 8 | 'D', ///< PD: Pen down
101 | CMD_PR = 'P' << 8 | 'R', ///< PR: Nove to relative position
102 | CMD_PT = 'P' << 8 | 'T', ///< PT: Pen thickness
103 | CMD_PU = 'P' << 8 | 'U', ///< PU: Pen up
104 | CMD_SA = 'S' << 8 | 'A', ///< SA: Select alternate character set
105 | CMD_SC = 'S' << 8 | 'C', ///< SC: Scale
106 | CMD_SI = 'S' << 8 | 'I', ///< SI: Absolute character size
107 | CMD_SP = 'S' << 8 | 'P', ///< SP: Select pen
108 | CMD_SR = 'S' << 8 | 'R', ///< SR: Relative character size
109 | CMD_SS = 'S' << 8 | 'S', ///< SS: Select standard character set
110 | CMD_VS = 'V' << 8 | 'S', ///< VS: Velocity Select: 0 = fastest (nonstandard)
111 | CMD_SEEK0 = 'H' << 8 | 'S' ///< Locate home position
112 | } hpgl_command_t;
113 |
114 | typedef enum {
115 | Pen_NoAction = 0,
116 | Pen_Up,
117 | Pen_Down,
118 | Pen_Timeout,
119 | Pen_Unknown = 255
120 | } pen_status_t;
121 |
122 | ///< Absolute coordinates used for stepper motion.
123 | ///< Negative means invalid.
124 | typedef int16_t hpgl_coord_t;
125 |
126 | ///< User coordinates used in input, arc calculation etc.
127 | typedef float user_coord_t;
128 |
129 | typedef struct {
130 | hpgl_coord_t x;
131 | hpgl_coord_t y;
132 | } hpgl_point_t;
133 |
134 | typedef struct {
135 | user_coord_t x;
136 | user_coord_t y;
137 | } user_point_t;
138 |
139 | typedef struct {
140 | float width;
141 | float height;
142 | } char_size_t;
143 |
144 | typedef union {
145 | uint8_t value;
146 | struct {
147 | uint8_t pen_down :1,
148 | p1p2_changed :1,
149 | point_available :1,
150 | initialized :1,
151 | ready :1,
152 | error :1,
153 | service :1,
154 | unused :1;
155 | };
156 | } hpgl_status_flags;
157 |
158 | typedef union {
159 | uint8_t value;
160 | struct {
161 | uint8_t enable_dtr :1,
162 | unused :1,
163 | monitor_mode :1,
164 | monitor_on :1,
165 | block_mode :1,
166 | unassigned :3;
167 | };
168 | } hpgl_comm_flags;
169 |
170 | typedef struct {
171 | float chord_angle;
172 | float pen_thickness;
173 | bool plot_relative;
174 | uint8_t etxchar;
175 | char term[3];
176 | uint8_t pattern_type;
177 | float pattern_length;
178 | bool use_alt_charset;
179 | bool text_vertical;
180 | char_size_t character_size;
181 | const char *const *charset;
182 | const char *const *charset_std;
183 | const char *const *charset_alt;
184 | user_point_t cr_loc;
185 | hpgl_error_t first_error;
186 | hpgl_error_t last_error;
187 | uint8_t errmask;
188 | uint8_t alertmask;
189 | float numpad[4];
190 | // The following values are not changed on a reset to default values, ip_pad must be first!
191 | int32_t ip_pad[4];
192 | int32_t sc_pad[4];
193 | int32_t esc_pad[8];
194 | user_point_t user_loc;
195 | hpgl_status_flags flags;
196 | hpgl_comm_flags comm;
197 | } hpgl_state_t;
198 |
199 | extern hpgl_state_t hpgl_state;
200 | typedef hpgl_command_t (*hpgl_char_ptr)(char c, hpgl_point_t *target, uint8_t *lb);
201 |
202 | /// Initialize the scanner.
203 | void hpgl_init();
204 |
205 | /// Handle next character from the input. When action is determined, return one of the _hpgl_command values.
206 | /// Pass target coordinates in x and y.
207 | /// @param c input char
208 | /// @param x output: destination x (returns -1 if no data)
209 | /// @param y output: destination y (returns -1 if no data)
210 | /// @param lb output: next label character (see CMD_LB)
211 | /// @returns _hpgl_command
212 | extern hpgl_char_ptr hpgl_char;
213 |
214 | hpgl_error_t hpgl_get_error (void);
215 | void hpgl_set_error (hpgl_error_t errnum);
216 |
217 | #endif
218 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/htext.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "hpgl.h"
5 | #include "scale.h"
6 | #include "htext.h"
7 |
8 | #ifdef HPGL_DEBUG
9 | #include "../grbl/hal.h"
10 | #endif
11 |
12 | user_point_t fontscale;
13 | user_point_t charorigin;
14 | user_point_t labelorigin;
15 |
16 | user_coord_t sintheta, costheta;
17 |
18 | static const char *coffs;
19 |
20 | void text_init (void)
21 | {
22 | // user_point_t ip = range_P1P2();
23 |
24 | // text_setscale(ip.x * 0.75f / 100.0f, ip.y * 1.5f / 100.0f);
25 | text_setscale(10.0f, 10.0f);
26 | text_direction(1.0f, 0.0f);
27 | }
28 |
29 | void text_setscale (float sx, float sy)
30 | {
31 | fontscale.x = sx;
32 | fontscale.y = sy;
33 |
34 | printf_P(PSTR("Text scale is (%f, %f)\n"), fontscale.x, fontscale.y);
35 | }
36 |
37 | void text_scale_cm (float cx, float cy)
38 | {
39 | user_point_t s;
40 |
41 | s.x = cx * 10.0f / 7.0f / 0.025f;
42 | s.y = cy * 10.0f / 10.0f / 0.025f;
43 |
44 | userprescale(s, &fontscale);
45 | text_setscale(fontscale.x, fontscale.y);
46 | }
47 |
48 | void text_scale_rel (float rx, float ry)
49 | {
50 | user_point_t prect = range_P1P2();
51 | float sx = rx / 100.0f * prect.x / 7.0f; // character size in plotter units
52 | float sy = ry / 100.0f * prect.y / 10.0f;
53 |
54 | text_setscale(sx, sy);
55 | }
56 |
57 | void text_direction (float cost, float sint)
58 | {
59 | sintheta = -sint;
60 | costheta = cost;
61 |
62 | printf_P(PSTR("Label rotation: sin=%f cos=%f\n"), sintheta, costheta);
63 | }
64 |
65 | static void rotate (float* x, float* y)
66 | {
67 | float xc = *x - charorigin.x;
68 | float yc = *y - charorigin.y;
69 | float xrot = xc * costheta + yc * sintheta;
70 | float yrot = -xc * sintheta + yc * costheta;
71 |
72 | *x = xrot + charorigin.x;
73 | *y = yrot + charorigin.y;
74 | }
75 |
76 | void text_beginlabel (void)
77 | {
78 | labelorigin = hpgl_state.user_loc;
79 | printf_P(PSTR("Label origin: (%f,%f)\n"), labelorigin.x, labelorigin.y);
80 | }
81 |
82 | bool text_pos (float x, float y, hpgl_point_t *target)
83 | {
84 | user_point_t d;
85 |
86 | if(isnanf(x)) {
87 | d.x = labelorigin.x;
88 | d.y = labelorigin.y - fontscale.y * 10.0f;
89 | rotate(&d.x, &d.y);
90 | userscale(d, target, &hpgl_state.user_loc);
91 | labelorigin = hpgl_state.user_loc;
92 | } else {
93 | d.x = hpgl_state.user_loc.x;
94 | d.y = hpgl_state.user_loc.y;
95 | if(x != 0.0f)
96 | d.x += fontscale.x * 5.0f * x;
97 | if(y != 0.0f)
98 | d.y += fontscale.y * 10.0f * y;
99 | rotate(&d.x, &d.y);
100 | userscale(d, target, &hpgl_state.user_loc);
101 | }
102 |
103 | charorigin = hpgl_state.user_loc;
104 |
105 | return true;
106 | }
107 |
108 | bool text_char (uint8_t c, hpgl_point_t *target, pen_status_t *pen)
109 | {
110 | user_point_t d;
111 | uint8_t encoded;
112 | static bool noadvance = false;
113 |
114 | if (c != 0) {
115 |
116 | encoded = 1;
117 | *pen = Pen_Up;
118 |
119 | switch (c) {
120 | case '\r':
121 | printf_P(PSTR("CR"));
122 | d.x = labelorigin.x;
123 | d.y = labelorigin.y;
124 | //rotate(&xd, &yd);
125 | userscale(d, target, &hpgl_state.user_loc);
126 | coffs = hpgl_state.charset[0];
127 | charorigin = hpgl_state.user_loc;
128 | noadvance = true;
129 | break;
130 | case '\n':
131 | printf_P(PSTR("LF"));
132 | d.x = hpgl_state.user_loc.x;
133 | d.y = hpgl_state.user_loc.y - fontscale.y * 10.0f;
134 | rotate(&d.x, &d.y);
135 | userscale(d, target, &hpgl_state.user_loc);
136 | coffs = hpgl_state.charset[0];
137 | charorigin = hpgl_state.user_loc;
138 | labelorigin = hpgl_state.user_loc;
139 | noadvance = true;
140 | break;
141 | default:
142 | coffs = hpgl_state.charset[c];
143 | charorigin = hpgl_state.user_loc;
144 | noadvance = false;
145 | //printf_P(PSTR("coffs=%x first=%o"), charset0[c], *charset0[c]);
146 | break;
147 | }
148 |
149 | } else {
150 |
151 | encoded = *coffs++;
152 | *pen = encoded & 0b10000000 ? Pen_Down : Pen_Up;
153 |
154 | if (encoded) {
155 | d.x = charorigin.x + fontscale.x * ((encoded >> 4) & 0b111);
156 | d.y = charorigin.y + fontscale.y * ((encoded & 0b1111) - 4);
157 | } else if (!noadvance) {
158 | d.x = charorigin.x + fontscale.x * 5.0f;
159 | d.y = charorigin.y;
160 | }
161 |
162 | #ifdef HPGL_DEBUG
163 | hal.stream.write("CH:");
164 | hal.stream.write(ftoa(d.x, 3));
165 | hal.stream.write(",");
166 | hal.stream.write(ftoa(d.y, 3));
167 | hal.stream.write(ASCII_EOL);
168 | #endif
169 |
170 | if (!noadvance) {
171 | rotate(&d.x, &d.y);
172 | userscale(d, target, &hpgl_state.user_loc);
173 | }
174 |
175 | #ifdef HPGL_DEBUG
176 | hal.stream.write("CT:");
177 | hal.stream.write(uitoa(target->x));
178 | hal.stream.write(",");
179 | hal.stream.write(uitoa(target->y));
180 | hal.stream.write(ASCII_EOL);
181 | #endif
182 |
183 | }
184 |
185 | return encoded != 0;
186 | }
187 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/htext.h:
--------------------------------------------------------------------------------
1 | #ifndef _HTEXT_H
2 | #define _HTEXT_H
3 |
4 | #include
5 | #include
6 |
7 | #include "motori.h"
8 |
9 | void text_init(void);
10 | void text_setscale(float sx, float sy);
11 | void text_scale_cm(float cx, float cy);
12 | void text_scale_rel(float rx, float ry);
13 | void text_direction(float cost, float sint);
14 |
15 | void text_beginlabel();
16 | bool text_char (uint8_t c, hpgl_point_t *target, pen_status_t *pen);
17 | bool text_pos (float x, float y, hpgl_point_t *target);
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/motori.h:
--------------------------------------------------------------------------------
1 | #ifndef _MOTORI_H
2 | #define _MOTORI_H
3 |
4 | #include "hpgl.h"
5 | #include "grbl/hal.h"
6 |
7 | #define PEN_DOWN_DELAY 20
8 | #define PEN_LIFT_DELAY 50
9 |
10 | #define GO_HOME_ON_IN // Uncomment to disable
11 |
12 | #define PSTR(s) s
13 | #define printf_P printf
14 | #define DELAY_MS(ms) hal.delay_ms(ms, NULL)
15 |
16 | /// Controls the pen position. Both servo and solenoid actuators.
17 | /// Causes immediate delay to allow for the pen to settle.
18 | ///
19 | /// @param down true if pen should be down, 0 if raised
20 | void pen_control (pen_status_t state);
21 |
22 | pen_status_t get_pen_status (void);
23 |
24 | /// Initialize plotter state. Move to home position, then reset everything, including motors and timers.
25 | /// Reset user scale and translation, raise the pen.
26 | void plotter_init();
27 |
28 | coord_data_t *get_origin (void);
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/scale.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "scale.h"
5 |
6 | #include "grbl/hal.h"
7 |
8 | static user_point_t user_scale, user_translate;
9 |
10 | void translate_init_ip (void)
11 | {
12 | hpgl_state.ip_pad[0] = 0;
13 | hpgl_state.ip_pad[1] = 0;
14 | hpgl_state.ip_pad[2] = MAX_X;
15 | hpgl_state.ip_pad[3] = MAX_Y;
16 | }
17 |
18 | void translate_init_sc (void)
19 | {
20 | user_scale.x = user_scale.y = 1.0f;
21 | user_translate.x = user_translate.y = 0.0f;
22 |
23 | hpgl_state.sc_pad[0] = 0;
24 | hpgl_state.sc_pad[1] = MAX_X;
25 | hpgl_state.sc_pad[2] = 0;
26 | hpgl_state.sc_pad[3] = MAX_Y;
27 | }
28 |
29 | // use IP and SC data to calculate the scale
30 | void translate_scale (void)
31 | {
32 | user_point_t iprange = range_P1P2();
33 |
34 | int32_t scxrange = hpgl_state.sc_pad[1] - hpgl_state.sc_pad[0]; // xmax - xmin
35 | int32_t scyrange = hpgl_state.sc_pad[3] - hpgl_state.sc_pad[2]; // ymax - ymin
36 |
37 | user_scale.x = iprange.x / (float)scxrange;
38 | user_scale.y = iprange.y / (float)scyrange;
39 | //user_xscale = ((float)scxrange)/((float)ipxrange);
40 | //user_yscale = ((float)scyrange)/((float)ipyrange);
41 | user_translate.x = -hpgl_state.sc_pad[0] * user_scale.x;
42 | user_translate.y = -hpgl_state.sc_pad[2] * user_scale.y;
43 |
44 | // printf_P(PSTR("Scale set to: (%f,%f) translate (%f,%f)"), user_xscale, user_yscale, user_translate_x, user_translate_y);
45 | }
46 |
47 | void userprescale (user_point_t abs, user_point_t *out)
48 | {
49 | out->x = abs.x / user_scale.x;
50 | out->y = abs.y / user_scale.y;
51 | }
52 |
53 | void usertohpgl (user_point_t src, hpgl_point_t *target)
54 | {
55 | target->x = (int16_t)roundf(src.x * user_scale.x);
56 | target->y = (int16_t)roundf(src.y * user_scale.y);
57 | }
58 |
59 | void userscalerelative (user_point_t src, hpgl_point_t *target, user_point_t *out)
60 | {
61 | target->x = (int16_t)roundf(out->x + src.x * user_scale.x);
62 | target->y = (int16_t)roundf(out->y + src.y * user_scale.y);
63 |
64 | if(out) {
65 | out->x = (float)target->x / user_scale.x;
66 | out->y = (float)target->y / user_scale.y;
67 | }
68 | }
69 |
70 | void userscale (user_point_t src, hpgl_point_t *target, user_point_t *out)
71 | {
72 | target->x = (int16_t)roundf(src.x * user_scale.x);
73 | target->y = (int16_t)roundf(src.y * user_scale.y);
74 |
75 | if(out) {
76 | out->x = (float)target->x / user_scale.x;
77 | out->y = (float)target->y / user_scale.y;
78 | }
79 | }
80 |
81 | user_point_t range_P1P2 (void)
82 | {
83 | user_point_t p;
84 |
85 | p.x = (float)(hpgl_state.ip_pad[2] - hpgl_state.ip_pad[0]);
86 | p.y = (float)(hpgl_state.ip_pad[3] - hpgl_state.ip_pad[1]);
87 |
88 | return p;
89 | }
90 |
91 | void output_P1P2 (void)
92 | {
93 | hal.stream.write(uitoa(hpgl_state.ip_pad[0]));
94 | hal.stream.write(",");
95 | hal.stream.write(uitoa(hpgl_state.ip_pad[1]));
96 | hal.stream.write(",");
97 | hal.stream.write(uitoa(hpgl_state.ip_pad[2]));
98 | hal.stream.write(",");
99 | hal.stream.write(uitoa(hpgl_state.ip_pad[3]));
100 | hal.stream.write(hpgl_state.term);
101 | }
102 |
--------------------------------------------------------------------------------
/my_plugin/hpgl/scale.h:
--------------------------------------------------------------------------------
1 | #ifndef _SCALE_H
2 | #define _SCALE_H
3 |
4 | #include "hpgl.h"
5 |
6 | /// Initialize translation and scale.
7 | ///
8 | /// Translation is not implemented.
9 | void translate_init_ip (void);
10 | void translate_init_sc (void);
11 |
12 | /// Use IP and SC data to calculate the scale and translation.
13 | ///
14 | /// Translation is not implemented.
15 | void translate_scale (void);
16 |
17 | /// Transform user coordinates (fx,fy) into plotter coordinates (x,y) according to
18 | /// the transform defined by IP/SC. Then do reverse transform, round and assign
19 | /// resulting values to (ox,oy).
20 | /// @param fx user coordinates x
21 | /// @param fy user coordinates y
22 | /// @param *x (output) absolute stepper x
23 | /// @param *y (output) absolute stepper y
24 | /// @param *ox (output) corrected fx
25 | /// @param *oy (output) corrected fy
26 | ///
27 | void userscale (user_point_t src, hpgl_point_t *target, user_point_t *out);
28 |
29 | void userscalerelative (user_point_t src, hpgl_point_t *target, user_point_t *out);
30 |
31 | void usertohpgl (user_point_t src, hpgl_point_t *target);
32 |
33 | /// Something that shouldn't be used
34 | void userprescale (user_point_t abs, user_point_t *out);
35 |
36 | /// Something else that should not be used
37 | user_point_t range_P1P2 (void);
38 |
39 | void output_P1P2 (void);
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/my_plugin/settings/my_plugin.c:
--------------------------------------------------------------------------------
1 | /*
2 | my_plugin.c - user defined plugin template with settings handling
3 |
4 | Part of grblHAL
5 |
6 | Public domain.
7 | This code is is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 | */
11 |
12 | /*
13 | * NOTE: this plugin does not add any other fuctionality than settings handling, attach to other HAL entry points to provide that.
14 | * See the mcode.c template or standard/template plugins for how to do this.
15 | */
16 |
17 | #include "grbl/hal.h"
18 | #include "grbl/report.h"
19 | #include "grbl/nvs_buffer.h"
20 | #include "grbl/nuts_bolts.h"
21 |
22 | typedef struct {
23 | float fvalue;
24 | uint16_t ivalue;
25 | } plugin_settings_t;
26 |
27 | static nvs_address_t nvs_address;
28 | static on_report_options_ptr on_report_options;
29 | static plugin_settings_t my_settings;
30 |
31 | static status_code_t set_my_setting1 (setting_id_t id, uint16_t value)
32 | {
33 | my_settings.ivalue = value;
34 |
35 | // do some stuff related to changes in the setting value
36 |
37 | return Status_OK;
38 | }
39 |
40 | static uint16_t get_my_setting1 (setting_id_t setting)
41 | {
42 | return my_settings.ivalue;
43 | }
44 |
45 | // Add info about our settings for $help and enumerations.
46 | // Potentially used by senders for settings UI.
47 | static const setting_group_detail_t user_groups [] = {
48 | { Group_Root, Group_UserSettings, "My settings"}
49 | };
50 |
51 | // Setting_UserDefined_0 is read and written by the core via the &my_settings.fvalue pointer.
52 | // Setting_UserDefined_1 is read and written via calls to get_my_setting1() and set_my_setting1().
53 | // - this is useful for settings that requires immediate action on a change and/or value transformation.
54 | static const setting_detail_t user_settings[] = {
55 | { Setting_UserDefined_0, Group_UserSettings, "My setting 1", NULL, Format_Decimal, "#0.0", "0", "15", Setting_NonCore, &my_settings.fvalue, NULL, NULL },
56 | { Setting_UserDefined_1, Group_UserSettings, "My setting 2", "milliseconds", Format_Int16, "####0", "50", "250", Setting_NonCoreFn, set_my_setting1, get_my_setting1, NULL }
57 | };
58 |
59 | // Write settings to non volatile storage (NVS).
60 | static void plugin_settings_save (void)
61 | {
62 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&my_settings, sizeof(plugin_settings_t), true);
63 | }
64 |
65 | // Restore default settings and write to non volatile storage (NVS).
66 | static void plugin_settings_restore (void)
67 | {
68 | my_settings.fvalue = 3.1f;
69 | my_settings.ivalue = 2;
70 |
71 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&my_settings, sizeof(plugin_settings_t), true);
72 | }
73 |
74 | // Load our settings from non volatile storage (NVS).
75 | // If load fails restore to default values.
76 | static void plugin_settings_load (void)
77 | {
78 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&my_settings, nvs_address, sizeof(plugin_settings_t), true) != NVS_TransferResult_OK)
79 | plugin_settings_restore();
80 | }
81 |
82 | // Add info about our plugin to the $I report.
83 | static void on_report_my_options (bool newopt)
84 | {
85 | on_report_options(newopt);
86 |
87 | if(!newopt)
88 | report_plugin("Settings template plugin", "1.03");
89 | }
90 |
91 | // A call my_plugin_init will be issued automatically at startup.
92 | // There is no need to change any source code elsewhere.
93 | void my_plugin_init (void)
94 | {
95 | // Settings descriptor used by the core when interacting with this plugin.
96 | static setting_details_t setting_details = {
97 | .groups = user_groups,
98 | .n_groups = sizeof(user_groups) / sizeof(setting_group_detail_t),
99 | .settings = user_settings,
100 | .n_settings = sizeof(user_settings) / sizeof(setting_detail_t),
101 | .save = plugin_settings_save,
102 | .load = plugin_settings_load,
103 | .restore = plugin_settings_restore
104 | };
105 |
106 | // Try to allocate space for our settings in non volatile storage (NVS).
107 | if((nvs_address = nvs_alloc(sizeof(plugin_settings_t)))) {
108 |
109 | // Add info about our plugin to the $I report.
110 | on_report_options = grbl.on_report_options;
111 | grbl.on_report_options = on_report_my_options;
112 |
113 | // Register our settings with the core.
114 | settings_register(&setting_details);
115 |
116 | // "Hook" into other HAL pointers here to provide functionality.
117 | }
118 | }
119 |
--------------------------------------------------------------------------------