/* Copyright (c) 2019 Graham Payne<br> *
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
A Mach4 CNC Pendant.
It uses the HID capabilities of the Arduino DUE to send key strokes
to the machine via the PC, over a USB cable.
Keystrokes are intercepted by the built in keyboard plug-in in Mach4.
Keystrokes are configured in the plug-in config screen.
Inputs are then setup to pass the keystrokes to the screen script.
Some coding is required in the screen editor in Mach4's PLC script to tell the keystrokes what to do.
The original versionn I made utilised hardware buttons, then a keypad, 2 toggle switches for Enable & Spindle,
a rotary encoder and a 4 line LCD screen.
This was done on a Arduino Loenardo (HID capable), used every pin !
Another version had just a touch screen for all functions including jogging. This needed more pins so a DUE was used.
The final version had a touch screen for all buttons but a rotary encoder for jogging,
I felt it dangerous to have jogging on the touch screen.
This is the final version.
Board Arduino DUE.
Programming port for programming, Native USB port for connection to Mach4.
This program requires the UTFT libraries and keyboard library.
It is assumed that the display module is connected to an
appropriate shield or that you know how to change the pin
numbers in the setup.
*/
#include <memorysaver.h>
#include "Keyboard.h"
#include "Rotary.h"
#include <URTouch.h>
#include <URTouchCD.h>
#include <UTFT.h>
Rotary r = Rotary(20, 21); // make an instance of the rotary encoder
UTFT myGLCD (ILI9486,38,39,40,41);
URTouch myTouch( 6, 5, 4, 3, 2); // Initialize touchscreen
extern uint8_t BigFont[]; // Declare which fonts we will be using
int Selected_Axis = 1; //X=1, Y=2, Z=3, A=4 from cycle of button
bool Machine_Enabled = LOW;
bool Spindle_Enabled = LOW;
//float Xpos = 0;
//float Ypos = 0;
//float Zpos = 0;
//float Apos = 0;
//int JogStepCounter = 1;
//float JogRate = 0;</p><p>void setup() {<br> r.begin(true); // initialise the rotary encoder
void setup() {
Serial.begin(9600);
Serial.println (myGLCD.getDisplayXSize());
myGLCD.InitLCD(0);
myGLCD.setFont(BigFont);
myGLCD.setBackColor(0, 0, 0);
myGLCD.clrScr();
myTouch.setPrecision(PREC_MEDIUM);
myTouch.InitTouch(0);
Send_Key(KEY_F7); // turn spindle off
delay(100);
Send_Key(KEY_F9);
drawButtons();
AxisButtonOn();
}
// portrait
// turn machine off
// LIGHT UP THE X BUTTON
// set background to black
// JOG_STEP_PRESSED(); // set jog step to 0.01 and screen it
//***********************************************************
// turn on the enable button
//***********************************************************
void enable_on() {
myGLCD.setColor(255, 0, 0); //red
myGLCD.fillRoundRect (1, 1, 155, 55);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (1, 1, 155, 55);
myGLCD.setBackColor(255, 0, 0); //red
myGLCD.setColor(0, 0, 0); //black
myGLCD.print("ENABLED", 25, 21);
}
//***********************************************************
// turn off the enable button
//***********************************************************
void enable_off() {
myGLCD.setColor(0, 255, 0); //green
myGLCD.fillRoundRect (1, 1, 155, 55);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (1, 1, 155, 55);
myGLCD.setBackColor(0, 255, 0); //green
myGLCD.setColor(0, 0, 0); //black
myGLCD.print("IDLE", 46, 21);
}
//***********************************************************
// turn on the spindle button
//***********************************************************
void spindle_on() {
myGLCD.setColor(255, 0, 0); //red
myGLCD.fillRoundRect (165, 1, 318, 55);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (165, 1, 318, 55);
myGLCD.setBackColor(255, 0, 0); //red
myGLCD.setColor(0, 0, 0); //black
myGLCD.print("SP ON", 200, 21);
}
//***********************************************************
// turn off the spindle button
//***********************************************************
void spindle_off() {
myGLCD.setColor(0, 255, 0); //green
myGLCD.fillRoundRect (165, 1, 318, 55);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (165, 1, 318, 55);
myGLCD.setBackColor(0, 255, 0); //green
myGLCD.setColor(0, 0, 0); //black
myGLCD.print("SP OFF", 190, 21);
}
//***********************************************************
// from the selected axis highlight the associated button
//***********************************************************
void AxisButtonOn() {
switch (Selected_Axis) {
case 1:
myGLCD.setColor(255, 0, 0); //red
myGLCD.fillRoundRect (1, 195, 73, 250);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (1, 195, 73, 250);
myGLCD.setBackColor(255, 0, 0); //red
myGLCD.setColor(255, 255, 255); //white
myGLCD.print("X", 30, 215);
break;
case 2:
myGLCD.setColor(255, 0, 0);
myGLCD.fillRoundRect (83, 195, 155, 250);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRoundRect (83, 195, 155, 250);
myGLCD.setBackColor(255, 0, 0);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Y", 113, 215);
break;
case 3:
myGLCD.setColor(255, 0, 0);
myGLCD.fillRoundRect (165, 195, 237, 250);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRoundRect (165, 195, 237, 250);
myGLCD.setBackColor(255, 0, 0);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Z", 194, 215);
break;
case 4:
myGLCD.setColor(255, 0, 0);
myGLCD.fillRoundRect (247, 195, 319, 250);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRoundRect (247, 195, 319, 250);
myGLCD.setBackColor(255, 0, 0);
myGLCD.setColor(255, 255, 255);
myGLCD.print("A", 277, 215);
break;
}
}
//***********************************************************
// turn all the axis buttons off
//***********************************************************
void AxisButtonsOff() {
myGLCD.setColor(0, 0, 255); //blue
myGLCD.fillRoundRect (1, 195, 73, 250);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (1, 195, 73, 250);
myGLCD.setBackColor(0, 0, 255); //red
myGLCD.setColor(255, 255, 255); //white
myGLCD.print("X", 30, 215);
myGLCD.setColor(0, 0, 255);
myGLCD.fillRoundRect (83, 195, 155, 250);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRoundRect (83, 195, 155, 250);
myGLCD.setBackColor(0, 0, 255);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Y", 113, 215);
myGLCD.setColor(0, 0, 255);
myGLCD.fillRoundRect (165, 195, 237, 250);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRoundRect (165, 195, 237, 250);
myGLCD.setBackColor(0, 0, 255);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Z", 194, 215);
myGLCD.setColor(0, 0, 255);
myGLCD.fillRoundRect (247, 195, 319, 250);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRoundRect (247, 195, 319, 250);
myGLCD.setBackColor(0, 0, 255);
myGLCD.setColor(255, 255, 255);
myGLCD.print("A", 277, 215);
}
//***********************************************************
// send keystroke function
//***********************************************************
void Send_Key(char n) {
Keyboard.begin();
Keyboard.press(n);
delay(100);
Keyboard.releaseAll();
}
/* Coords
enable = 1, 1, 155, 55
spindle = 165, 1, 318, 55
start = 1, 65, 103, 120
hold = 113, 65, 206, 120
stop = 216, 65, 319, 120
home = 1, 130, 103, 185
goto 0 = 113, 130, 206,185
reset = 216, 130, 319,185
X = 1, 195, 73, 250
Y = 83, 195,155, 250
Z = 165, 195, 237, 250
A = 247, 195, 319, 250
jog step = 0, 260, 155, 315
jog step display box = 165, 260, 319, 315
display box X = 40, 325, 250, 362
display box Y = 40, 364, 250, 401
display box Z = 40, 403, 250, 440
display box A = 40, 442, 250, 479
*/
//***********************************************************
// draw the buttons on the screen
//***********************************************************
void drawButtons() {
enable_off(); // Draw the Enable button
spindle_off(); // Draw the Spindle button
// Draw the START button
myGLCD.setColor(0, 255, 0); //green button
myGLCD.fillRoundRect (1, 65, 103, 120);
myGLCD.setColor(255, 255, 255); //white border
myGLCD.drawRoundRect (1, 65, 103, 120);
myGLCD.setBackColor(0, 255, 0); //green text background
myGLCD.setColor(0, 0, 0); //black text
myGLCD.print("START", 13, 85);
// Draw the HOLD button
myGLCD.setColor(255, 255, 0); //yellow
myGLCD.fillRoundRect (113, 65, 206, 120);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRoundRect (113, 65, 206, 120);
myGLCD.setBackColor(255, 255, 0); //yellow
myGLCD.setColor(0, 0, 0); //black
myGLCD.print("HOLD", 128, 85);
// Draw the STOP button
myGLCD.setColor(255, 0, 0); //red
myGLCD.fillRoundRect (216, 65, 319, 120);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (216, 65, 319, 120);
myGLCD.setBackColor(255, 0, 0); //red
myGLCD.setColor(255, 255, 255); //white
myGLCD.print("STOP", 234, 85);
// Draw the HOME button
myGLCD.setColor(128, 239, 239); //viiolet
myGLCD.fillRoundRect (1, 130, 103, 185);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (1, 130, 103, 185);
myGLCD.setBackColor(128, 239, 239); //violet
myGLCD.setColor(255, 255, 255); //white
myGLCD.print("HOME", 23, 150);
// Draw the GOTO 0 button
myGLCD.setColor(230, 230, 230); //silver
myGLCD.fillRoundRect (113, 130, 206, 185);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (113, 130, 206, 185);
myGLCD.setBackColor(230, 230, 230); //silver
myGLCD.setColor(0, 0, 0); //black
myGLCD.print("GOTO", 127, 140);
myGLCD.print("0", 150, 160);
// Draw the RESET button
myGLCD.setColor(255, 0, 0); //red
myGLCD.fillRoundRect (216, 130, 319, 185);
myGLCD.setColor(255, 255, 255); //white
myGLCD.drawRoundRect (216, 130, 319, 185);
myGLCD.setBackColor(255, 0, 0); //red
myGLCD.setColor(255, 255, 255); //white
myGLCD.print("RESET", 225, 150);
AxisButtonsOff(); // draw axis selections X, Y, Z & A off
//draw Cycle Jog Step Rate Button
// myGLCD.setColor(255,255,0); //red
// myGLCD.fillRoundRect (0, 260, 155, 315);
// myGLCD.setColor(255, 255, 255); //white
// myGLCD.drawRoundRect (0, 260, 155, 315);
// myGLCD.setBackColor(255,255,0); //red
// myGLCD.setColor(0, 0, 0); //white
// myGLCD.print("JOG RATE", 10, 280);
// //draw Cycle Jog Step Rate display box
// myGLCD.setColor(255,255,255); //white
// myGLCD.drawRoundRect (165, 260, 319, 315);</p><p>//draw AXIS display boxes
// myGLCD.setColor(255,255,255); //white
// myGLCD.drawRoundRect (40, 325, 250, 362);
// myGLCD.drawRoundRect (40, 364, 250, 401);
// myGLCD.drawRoundRect (40, 403, 250, 440);
// myGLCD.drawRoundRect (40, 442, 250, 479);
// print axis coordinate place holders
// myGLCD.setBackColor(0, 0, 0); //red
// myGLCD.setColor(255, 255, 255); //white
// myGLCD.print("X:", 1, 335);
// myGLCD.print("Y:", 1, 374);
// myGLCD.print("Z:", 1, 413);
// myGLCD.print("A:", 1, 452);
// myGLCD.print(String(Xpos) + " ", 50, 335);
// myGLCD.print(String(Ypos) + " ", 50, 374);
// myGLCD.print(String(Zpos) + " ", 50, 413);
// myGLCD.print(String(Apos) + " ", 50, 452);
}
//***********************************************************
// flash the button border when pressed in red
//***********************************************************
void FlashBorder(int x1, int y1, int x2, int y2) { // Draw a red frame while a button is touched
myGLCD.setColor(255, 0, 0); // red
myGLCD.drawRoundRect (x1, y1, x2, y2);
while (myTouch.dataAvailable()) //while pressed
myTouch.read();
myGLCD.setColor(255, 255, 255); // back to white
myGLCD.drawRoundRect (x1, y1, x2, y2);
}
//***********************************************************
// toggle the enable button
//***********************************************************
void ENABLED_PRESSED() {
FlashBorder(1, 1, 155, 55);
if (Machine_Enabled) {
Machine_Enabled = LOW; //turn enable OFF
Send_Key(KEY_F9);
Spindle_Enabled = LOW; //spindle turns off (if on) when enabled off.
enable_off(); //enable button green
} else if (!Machine_Enabled) { //or turn enable on
Machine_Enabled = HIGH;
Send_Key(KEY_F10);
enable_on(); //enable button red
}
}
//***********************************************************
// toggle the spindle button
//***********************************************************
void SPINDLE_PRESSED() {
FlashBorder(165, 1, 318, 55);
if (Spindle_Enabled) {
Spindle_Enabled = LOW;
Send_Key(KEY_F7);
spindle_off();
} else if (!Spindle_Enabled && Machine_Enabled) { //only turn on if machine is enabled
Spindle_Enabled = HIGH;
Send_Key(KEY_F8);
spindle_on();
}
}
//***********************************************************
// Start button pressed
//***********************************************************
void START_PRESSED() {
FlashBorder(1, 65, 103, 120); // flash border
Send_Key(KEY_F6); // send the keystroke
}
//***********************************************************
// Hold button pressed
//***********************************************************
void HOLD_PRESSED() {
FlashBorder(113, 65, 206, 120);
Send_Key(KEY_F5);
}
//***********************************************************
// Stop button pressed
//***********************************************************
void STOP_PRESSED() {
FlashBorder(216, 65, 318, 120);
Send_Key(KEY_F4);
}
//***********************************************************
// Home button pressed
//***********************************************************
void HOME_PRESSED() {
FlashBorder(1, 130, 103, 185);
Send_Key(KEY_F1);
}
//***********************************************************
// Go to 0 button pressed
//***********************************************************
void GOTO_0_PRESSED() {
FlashBorder(113, 130, 206, 185);
Send_Key(KEY_F2);
}
//***********************************************************
// Reset button pressed
//***********************************************************
void RESET_PRESSED() {
FlashBorder(216, 130, 318, 185);
Send_Key(KEY_F3);
}
//***********************************************************
// X axis button pressed
//***********************************************************
void X_PRESSED() {
FlashBorder(1, 195, 73, 250);
Selected_Axis = 1;
AxisButtonsOff();
AxisButtonOn();
}
//***********************************************************
// Y axix button pressed
//***********************************************************
void Y_PRESSED() {
FlashBorder(83, 195, 155, 250);
Selected_Axis = 2;
AxisButtonsOff();
AxisButtonOn();
}
//***********************************************************
// Z axis button pressed
//***********************************************************
void Z_PRESSED() {
FlashBorder(165, 195, 237, 250);
Selected_Axis = 3;
AxisButtonsOff();
AxisButtonOn();
}
//***********************************************************
// A axis button pressed
//***********************************************************
void A_PRESSED() {
FlashBorder(247, 195, 319, 250);
Selected_Axis = 4;
AxisButtonsOff();
AxisButtonOn();
}
//***********************************************************
// Cycle thru Jog Rates
//***********************************************************
//void JOG_STEP_PRESSED() {
// FlashBorder(0, 260, 155, 315);//239, 260, 319, 315
// JogStepCounter ++; //inc counter
// if (JogStepCounter >= 11) JogStepCounter = 1; // no more than 10
// Keyboard.begin();
// switch (JogStepCounter) {
// case 1:
// myGLCD.print("0.01", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('1');
// JogRate = 0.01; // jog rate set at 0.01
// break;
// case 2:
// myGLCD.print("0.1 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('2');
// JogRate = 0.1; // jog rate set at 0.1
// break;
// case 3:
// myGLCD.print("1 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('3');
// JogRate = 1; // jog rate set at 1
// break;
// case 4:
// myGLCD.print("5 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('4');
// JogRate = 5; // jog rate set at 5
// break;
// case 5:
// myGLCD.print("10 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('5');
// JogRate = 10; // jog rate set at 10
// break;
// case 6:
// myGLCD.print("20 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('6');
// JogRate = 20; // jog rate set at 20
// break;
// case 7:
// myGLCD.print("50 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('7');
// JogRate = 50; // jog rate set at 50
// break;
// case 8:
// myGLCD.print("100 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('8');
// JogRate = 100; // jog rate set at 100
// break;
// case 9:
// myGLCD.print("150 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('9');
// JogRate = 150; // jog rate set at 150
// break;
// case 10:
// myGLCD.print("200 ", 185, 280);
// Keyboard.press(KEY_LEFT_CTRL);
// Keyboard.press('0');
// JogRate = 200; // jog rate set at 200
// break;
// }
// delay(100);
// Keyboard.releaseAll();
// }
//***********************************************************
// Jog positive direction
//***********************************************************
void JOG_PLUS() {
Keyboard.begin();
switch (Selected_Axis) { // jog by selected axis
case 1: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('x');
// Xpos = Xpos + JogRate;
// myGLCD.print(String(Xpos) + " ", 50, 335); //display it
break;
case 2: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('y');
// Ypos = Ypos + JogRate;
// myGLCD.print(String(Ypos) + " ", 50, 374);
break;
case 3: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('z');
// Zpos = Zpos + JogRate;
// myGLCD.print(String(Zpos) + " ", 50, 413);
break;
case 4: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('a');
// Apos = Apos + JogRate;
// myGLCD.print(String(Apos) + " ", 50, 452);
break;
}
delay(100);
Keyboard.releaseAll();
}
//***********************************************************
// Jog negative direction
//***********************************************************
void JOG_MINUS() {
Keyboard.begin();
switch (Selected_Axis) {
case 1: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('s');
// Xpos = Xpos - JogRate;
// myGLCD.print(String(Xpos) + " ", 50, 335);
break;
case 2: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('d');
// Ypos = Ypos - JogRate;
// myGLCD.print(String(Ypos) + " ", 50, 374);
break;
case 3: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('w');
// Zpos = Zpos - JogRate;
// myGLCD.print(String(Zpos) + " ", 50, 413);
break;
case 4: Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('e');
// Apos = Apos - JogRate;
// myGLCD.print(String(Apos) + " ", 50, 452);
break;
}
delay(100);
Keyboard.releaseAll();
}
//***********************************************************
// check rotary encoder for movement
//***********************************************************
void Check_Encoder() {
unsigned char result = r.process();
if (result) {
if (result == DIR_CW) {
JOG_PLUS();
}
else if (result == DIR_CCW) {
JOG_MINUS();
}
else if (result == DIR_NONE) {
// do nothing
}
}
}
//***********************************************************
// check screen for touch
//***********************************************************
void Check_Screen() {
if (myTouch.dataAvailable()) {
myTouch.read();
int x = myTouch.getX();
int y = myTouch.getY();
if ((y >= 1) && (y <= 54)) {
if ((x >= 2) && (x <= 154)) { // ENABLE
ENABLED_PRESSED();
} else if ((x >= 166) && (x <= 317)) { //SPINDLE
SPINDLE_PRESSED();
}
}
if ((y >= 66) && (y <= 119)) {
if ((x >= 2) && (x <= 102)) { // start
START_PRESSED();
} else if ((x >= 114) && (x <= 205)) { // hold
HOLD_PRESSED();
} else if ((x >= 217) && (x <= 317)) { // stop
STOP_PRESSED();
}
}
if ((y >= 131) && (y <= 184)) {
if ((x >= 2) && (x <= 102)) { // home
HOME_PRESSED();
} else if ((x >= 114) && (x <= 205)) { // goto 0
GOTO_0_PRESSED();
} else if ((x >= 217) && (x <= 317)) { // reset
RESET_PRESSED();
}
}
if ((y >= 196) && (y <= 249)) { // 4th row
if ((x >= 2) && (x <= 72)) { // X
X_PRESSED();
} else if ((x >= 84) && (x <= 154)) { // Y
Y_PRESSED();
} else if ((x >= 166) && (x <= 236)) { // Z
Z_PRESSED();
} else if ((x >= 248) && (x <= 318)) { // A
A_PRESSED();
}
}
// if ((y >= 261) && (y <= 314)) { // JOG STEP
// if ((x >= 1) && (x <= 154)) {
// JOG_STEP_PRESSED();
// }
// }
myGLCD.setBackColor(0, 0, 0);
myGLCD.setColor(255, 255, 255);
} // data available
}
void loop() {
Check_Encoder();
Check_Screen();
}