/*
Code by lingib
Last update 15 December 2016
--------
ABOUT
--------
This "terminal" program has been designed to work with each of my
plotters that are published on http://www.instructables.com
These plotters include:
CNC Robot Plotter
CNC Hanging Plotter
CNC Drum Plotter (all software versions)
This code prevents characters getting lost through buffering and
should, with minor changes, work with any gcode receiver that
utilizes XON/XOFF handshaking.
It assumes that the gcode file that you wish to plot is in your
"processing folder". If the file is in another folder then use
"relative addressing" to locate the gcode file.
When using relative addressing precede each DOS path symbol (\)
with an escape character (\). Examples:
"filename" means the file is inside the processing folder.
".\\filename" also means the file is inside the processing folder.
"..\\filename" means the file is one level up.
"..\\folder1\\filename means file is in folder1 which is one level up.
----------
COPYRIGHT
----------
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License. If
not, see .
*/
// ---------------------------------------------------
// DEFINITIONS
// ---------------------------------------------------
// ----- create port object
import processing.serial.*; //library
Serial port; //create object from Serial class
// ----- flow control codes
final char XON = 0x11; //plotter requests more code
final char XOFF= 0x13; //stop sending code to plotter
// ----- file processing variables
String filename = ""; //file containing gcode
String keyboard = ""; //keyboard buffer
String[] command; //array of text commands
int number_commands; //number of commands
int index = 0; //array index
String message = ""; //used for instructions
// ----- flags
boolean debug_on = true;
//true: display serial port and file contents
//false:debug disabled
boolean filename_valid = false;
//true: filename has been entered
//false: filename cleared
boolean keyboard_mode = true;
//true: gcode comes from the keyboard
//false: gcode comes from a file
boolean display_menu = false;
//true: menu required
//false: menu not required
boolean plotter_paused = true;
//true: keyboard mode
//false: able to plot
boolean ok_to_send = true;
//true: send
//false: waiting for XON character from plotter
boolean finished_plotting = false;
//true: display finished plotting message
//false: display get initial filename message
// ---------------------------------------------------
// SETUP
// ---------------------------------------------------
void setup() {
// ----- create draw window
size(300, 100);
textSize(12); //set text size to 12
textAlign(CENTER);
background(#D3D3D3); //light gray
fill(0); //black text
// ----- connect to the arduino
if (debug_on) {
printArray(Serial.list()); //note the array number for your arduino port ...
}
String arduino_port = Serial.list()[8]; // ... and change the [2] to match
port = new Serial(this, arduino_port, 9600); //open 9600 baud serial connection
port.clear(); //clear send buffer
// ----- display start message
message = "enter a command ...\nor\n\"right-click\" this box to start plotting";
clear();
background(#D3D3D3);
text(message, 150, 30);
// ----- display the menu
port.write("menu\n"); //ask plotter for the menu
}
// ---------------------------------------------------
// DRAW (main loop)
// ---------------------------------------------------
void draw() {
// -----------------------------------------------------
// send keypresses to the plotter
// -----------------------------------------------------
/* left/right-clicking switches between keyboard/plotter modes */
if (mousePressed && (mouseButton == LEFT)) {
delay(500); //debounce delay
keyboard_mode = true; //send keypresses to the plotter
display_menu = true; //causes menu to be displayed
plotter_paused = true; //pause plotting
}
// -----------------------------------------------------
// send file contents to the plotter
// -----------------------------------------------------
/* left/right-clicking switches between keyboard/plotter modes */
if (mousePressed && (mouseButton == RIGHT)) {
delay(500); //debounce delay
keyboard_mode = false; //send file contents to the plotter
display_menu = true; //display menu whenever we enter keyboard mode
plotter_paused = false; //pause plotting
}
// -----------------------------------------------------
// display the menu whenever we enter the keyboard mode
// -----------------------------------------------------
if ((keyboard_mode) && (display_menu)) {
// ----- message
message = "enter a command ...\nor\n\"right-click\" this box to continue plotting";
clear();
background(#D3D3D3);
text(message, 150, 30);
// ----- request menu from the plotter
port.write("menu\n"); //use "menu\n" with Drum_Plotter_V3.ino
/*
use "M0\n" with my other plotters:
These include:
Drum_Plotter.ino
Drum_Plotter_V2.ino
Hanging_Plotter.ino
Robot_Plotter.ino
*/
println("");
display_menu = false; //one time action
}
// -----------------------------------------------------
// get initial file name
// -----------------------------------------------------
if ((keyboard_mode == false) && (!filename_valid) && (!finished_plotting)) {
// ----- message
message = "enter the \"filename\" you wish to plot\nor\n\"left-click\" this box to enter a command";
clear();
background(#D3D3D3);
text(message, 150, 30);
/* The KEY PRESSED routine intercepts your keypresses and performs this task */
}
// -----------------------------------------------------
// get next file name
// -----------------------------------------------------
if ((keyboard_mode == false) && (!filename_valid) && (finished_plotting)) {
// ----- message
message = "finished plotting ...\n\nenter another \"filename\" or\n\"left-click\" this box to enter a command";
clear();
background(#D3D3D3);
text(message, 150, 30);
/* The KEY PRESSED routine intercepts your keypresses and performs this task */
}
// -----------------------------------------------------
// plot the file
// -----------------------------------------------------
/*
left mouse-click: pauses the plotter for pen color changes
right mouse-click: resumes plotting
*/
if ((ok_to_send) && (filename_valid) && (index < number_commands) && (!plotter_paused)) {
// ----- message
message = "now plotting ....\n\n\"left-click\" this box to pause\nor enter a command";
clear();
background(#D3D3D3);
text(message, 150, 20);
// ----- send the file "line-by-line" "character-by-character"
char[] character = new char[command[index].length()]; //create a character array for the current command
for (int i = 0; i < command[index].length(); i++) { //extract the command characters into the array
character[i] = command[index].charAt(i);
}
// ----- send the command to the plotter
for (int i = 0; i < (command[index].length()); i++) {
port.write(command[index].charAt(i));
}
port.write('\n'); //complete the command
/* delay() between characters can go here if required */
// ----- generate a local XOFF rather than wait for the plotter
ok_to_send = false; //local XOFF
port.clear(); //clear transmit buffer
/* ok_to-send flag is set when SERIAL EVENT receives an XON character from the plotter */
// ----- point to the next command line
index++;
// ----- set/reset the flags
if (index >= number_commands) {
filename = "";
filename_valid = false;
index = 0;
plotter_paused = false;
finished_plotting = true;
}
}
}
// ---------------------------------------------------
// SERIAL EVENT (called whenever a character arrives)
// ---------------------------------------------------
void serialEvent (Serial port) {
char letter = port.readChar();
if (letter == XON) ok_to_send = true; //plotter ready for next command
print(letter); //echo character from arduino
}
// ---------------------------------------------------
// KEY PRESSED (called each key press)
// ---------------------------------------------------
void keyPressed() {
// ----- validate each keypress. Also prevents control characters from showing.
switch (key) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
case ' ':
case '.':
case '_':
case '\\':
case '\r':
case '\n':
{
// ----- send each keypress to the plotter
if (keyboard_mode == true) {
port.write(key); //plotter will echo each keypress
}
// ----- get filename while in plotter mode
if ((keyboard_mode == false) && (!filename_valid)) {
keyboard += key;
print(key); //local keypress echo
if ((key == '\r')||(key=='\n')) { //enter key pressed
filename = keyboard.trim(); //remove white space including \r\n
// ----- read the file
command = loadStrings(filename); //create an array of command strings
number_commands = command.length; //find number of command strings
// ----- display the file contents
if (debug_on) {
for (int i=0; i