Scenario:
I'm in the midst of building a cheap CNC project. Currently i have GRBL 0.9 on an funduino uno, and 3x easydriver stepper controllers.
What i'm attempting to do, is use a sainsmart arduino mega 2560 r3 as a secondary terminal to access grbl functions.
My thinking was that i could connect the uno to the PC via USB, and connect the mega to the UNO via RX/TX pins
Where i seem to be having difficulty:
I was hoping that i could use software serial to "spy" on the uno's serial activity with the PC, and split off pertinent information such as axis positions etc, and print that to the LCD.
I was also hoping to have a menu on the LCD with basic functions, such as pause/resume, manual jogging on each axis for positioning, homing, software restarting grbl, and sending the unlock command.
The menu part i have figured out, and receiving on the RX line appears to work partially.
However, when i try this setup, GRBL stops responding on the PC connection (GRBL Control).
I also seem to be having another issue, that may be related to the first one, whenever i stream the serial data to the LCD, there are quite a bit of garbage characters being displayed.
My questions are thus:
-
Is it possible to use software serial to "spy" on the GRBL serial communications (as i read somewhere the USB and the RX/TX lines on the uno share the same UART) without interrupting, or otherwise causing issues with PC-USB -> Arduino communication
-
Is it possible to interject commands from the second arduino to the first one without causing issues.
i.e. is there some sort of interrupt i need to wait for within the serial communication before injecting my own commands from the LCD menu? -
Has someone already done this? If i'm reinventing the wheel, does someone know of a working example of this setup that i could peruse and learn from.
Hardware Information Links:
EasyDriver 4.4 Stepper Motor Breakout Modules
LCD Keypad Shield
Funduino Uno R3
Sainsmart Mega2560 R3
Current testing codes:
Software serial to LCD test (Garbage output to screen, GRBL stops responding)
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#define rxPin 50 // receive
#define txPin 48 // transmit
SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
void setup(){
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// define pin modes for tx, rx, led pins:
pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
mySerial.begin(115200);
//mySerial.print("LCD Display...");
lcd.print("Initializing...");
delay(800);
lcd.clear();
lcd.print("Ready");
}
void loop()
{
// when characters arrive over the serial port...
if (mySerial.available()) {
// wait a bit for the entire message to arrive
delay(100);
// clear the screen
lcd.clear();
// read all the available characters
int index = 0;
while (mySerial.available() > 0) {
// display each character to the LCD
if(index < 16){
lcd.setCursor(index, 0);
}else{
if(index > 34){
index = 0;
lcd.setCursor(index, 0);
}else{
lcd.setCursor(index-16, 1);
}
}
lcd.write(mySerial.read());
delay(100);
index++;
}
}
}
LCD Menu test (still needs loads of work )
#include <LiquidCrystal.h>
//Software serial disabled until i figure it all out
//#include <SoftwareSerial.h>
//#define rxPin 50 // receive
//#define txPin 48 // transmit
//SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);
// Initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
//States for the menu.
int currentMenuItem = 0;
int lastState = 0;
void setup() {
// define pin modes for tx, rx, led pins:
// pinMode(rxPin, INPUT);
// pinMode(txPin, OUTPUT);
// mySerial.begin(115200);
//Set the characters and column numbers.
lcd.begin(16, 2);
//Print default title.
clearPrintTitle();
}
void loop() {
//Call the main menu.
mainMenu();
}
void mainMenu() {
//State = 0 every loop cycle.
int state = 0;
//Refresh the button pressed.
int x = analogRead (0);
//Set the Row 0, Col 0 position.
lcd.setCursor(0,0);
//Check analog values from LCD Keypad Shield
if (x < 100) {
//Right
} else if (x < 200) {
//Up
state = 1;
} else if (x < 400){
//Down
state = 2;
} else if (x < 600){
//Left
} else if (x < 800){
//Select
state = 3;
}
//If we are out of bounds on the menu then reset it.
if (currentMenuItem < 0 || currentMenuItem > 5) {
currentMenuItem = 0;
}
//If we have changed Index, saves re-draws.
if (state != lastState) {
if (state == 1) {
//If Up
currentMenuItem = currentMenuItem - 1;
displayMenu(currentMenuItem);
} else if (state == 2) {
//If Down
currentMenuItem = currentMenuItem + 1;
displayMenu(currentMenuItem);
} else if (state == 3) {
//If Selected
selectMenu(currentMenuItem);
}
//Save the last State to compare.
lastState = state;
}
//Small delay
delay(5);
}
//Display Menu Option based on Index.
void displayMenu(int x) {
switch (x) {
case 1:
clearPrintTitle();
lcd.print ("-> Reset GRBL");
break;
case 2:
clearPrintTitle();
lcd.print ("-> Zero Position");
break;
case 3:
clearPrintTitle();
lcd.print ("-> Home ");
break;
case 4:
clearPrintTitle();
lcd.print ("-> Pause Job");
break;
case 5:
clearPrintTitle();
lcd.print ("-> Resume Job");
break;
}
}
//Print a basic header on Row 1.
void clearPrintTitle() {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Sinful Media CNC ");
lcd.setCursor(0,1);
}
//Show the selection on Screen.
void selectMenu(int x) {
switch (x) {
case 1:
clearPrintTitle();
lcd.print ("GRBL Resetting...");
delay(2500);
clearPrintTitle();
displayMenu(currentMenuItem);
//Send command to GRBL to software reset
break;
case 2:
clearPrintTitle();
lcd.print ("Zeroing POS...");
delay(2500);
clearPrintTitle();
displayMenu(currentMenuItem);
//Send command to GRBL to zero position
break;
case 3:
clearPrintTitle();
lcd.print ("Homing...");
delay(2500);
clearPrintTitle();
displayMenu(currentMenuItem);
//Send command to grbl to go home
break;
case 4:
clearPrintTitle();
lcd.print ("Pausing...");
delay(2500);
clearPrintTitle();
displayMenu(currentMenuItem);
//Send command to GRBL to Pause Job Queue
break;
case 5:
clearPrintTitle();
lcd.print ("Resuming...");
delay(2500);
clearPrintTitle();
displayMenu(currentMenuItem);
//Send command to GRBL to Resume Job Queue
break;
}
}