Now, we'll combine the previous two parts of the lab to demonstrate communication in both directions between Java and the Arduino.
At each loop iteration, the Arduino program TX a message, then checks if there is one to RX. If there is, it updates the LED accordingly.
The java program also loops; at each iteration, it checks to see if there is a message to RX and prints it out. It then prompts the user for a command to TX.
However, that since both programs are only executing one command at a time, they cannot RX and TX simultaneously. This means that some messages will be missed.
Note: in java, you can create threads to essentially run parallel processes, but that is outside the scope of this lab.
In the steps below, text highlighted in yellow denote values you will need to change to update your setup.
Take the following code and place in a file named SimpleSerialTXRX.java.
/*
* Adapted from Yu Hin Hau's code
* https://billwaa.wordpress.com/2012/05/18/engineering-arduino-to-java-communication-2/
*
* Simple example of Java and Arduino Communication
* Sends and receives messages using the serial bus.
* If sent message is: 0 -> LED off, 1 -> LED on, 2 -> LED blink once.
*/
import java.util.Scanner;
import jssc.SerialPort;
import jssc.SerialPortList;
import jssc.SerialPortException;
public class SimpleSerialTXRX
{
/**** SETTINGS -- change to match your setup! *******/
// change the following to match the serial port you use for the Arduino
final static String SERIAL_PORT_ADDRESS = "/dev/tty.usbmodem1421";
// change to match the baud rate you're using from Arduino
final static int BAUD_RATE = 19200;
/**** END SETTINGS *********************************/
//Declare Special Symbol Used in Serial Data Stream from Arduino
final static String start_char = "@";
final static String end_char = "#";
final static String sep_char = ":";
private static SerialPort serialPort;
private static Scanner reader;
public static void main(String[] args)
{
//Initialize Port
serialPort = new SerialPort( SERIAL_PORT_ADDRESS );
//Create Scanner to read from Console
reader = new Scanner(System.in);
try
{
System.out.println( "about to open port" );
//Open Serial Port and set Parameters
serialPort.openPort();
serialPort.setParams( BAUD_RATE, 8, 1, 0);
// begin main loop
while ( true )
{
// try to receive a message from the serial port
readFromArduino();
// wait for the user to enter a command, then send across the serial port
readFromConsole();
}
}
catch (SerialPortException ex)
{
System.out.println(ex);
}
}
/**
* RX (receive) data from the serial port; print it out.
**/
public static void readFromArduino()
{
byte[] buffer;
try
{
System.out.println( "reading from arduino" );
//Filter out bad data from Arduino initialization
buffer = serialPort.readBytes(200);
//Retrieve data from Arduino -- read 100 bytes
buffer = serialPort.readBytes(100);
//Convert bytes into String
String dataStream = new String(buffer);
//Isolate Data Stream using symbols defined earlier
dataStream = dataStream.substring(dataStream.indexOf(start_char)+1);
dataStream = dataStream.substring(0,dataStream.indexOf(end_char)-1);
//Retrieve data sent by Arduino in form of Strings
String[] data = dataStream.split(sep_char);
//Display obtained data
for(int i = 0; i < data.length; i++)
System.out.println(data[i]);
}
catch (SerialPortException ex)
{
System.out.println(ex);
}
}
/**
* Prompt and wait for the user to enter a command.
* TX (transmit) that command through the serial port.
**/
public static void readFromConsole()
{
System.out.print("Operation (0 = Off / 1 = On / 2 = Blink / q = Quit ): ");
try
{
// if there is something to read from the command line
if(reader.hasNext())
{
/* Remember that through Serial, we are sending bytes through,
* therefore, we must convert the char input we got
* into byte form. In another word, the value reprsented in
* the system encoding, aka ASCII code. 0 is 48, 1 is 49,
* 2 is 50... luckily, we can type cast in Java. Mess around
* with this program to see the different ASCII representation
* for each of your character input!
*/
// read input from the command line
String input = reader.next();
// get the first character (ignore the rest)
char firstChar = input.charAt( 0 );
// // convert it to an array of chars
// char inputArray[] = input.toCharArray();
// if it's a 'q', quit
if ( firstChar == 'q')
{
// close the port
serialPort.closePort();
// end the program
System.exit( 0 );
}
else
{
// convert to byte by casting
// and send the data across the port
serialPort.writeByte((byte)firstChar);
System.out.println((byte)firstChar + " <= sent!");
}
}
}
catch (SerialPortException ex)
{
System.out.println(ex);
}
}
}
Follow the same steps from the previous part to update the settings and compile your code:
Update the values of the constants to match your setup. Notice the settings area at the top of the class file.
The SERIAL_PORT_ADDRESS should match the port you select in the Arduino application.
The BAUD_RATE must match that of the Arduino sketch (you should not have to change this).
Compile your code. This class relies on the JSSC (Java Simple Serial Connector) library, which is included in the Arduino environment (versions 1.5.6+). Therefore, in order to compile your java class, you'll need to include this library in the class path.
First, determine the path of the library; it is actually stored as a JAR within your Arduino application. To see the actual file, right click your Arduino application in the Finder and select Show Package Contents. Within the resulting directory structure, navigate to Contents -> Resources -> Java. Within this directory, you should see a file name jssc****.jar, where the **** indicates the version number. Right-click that file and select Get Info. In the resulting window, look for the path to the file (next to "Where").
You will need to use this path along with the -cp flag when compiling your java program. For example, using the above path, I would compile my code with the command:
javac -cp /Applications/Arduino1.6.0.app/Contents/Resources/Java/jssc-2.8.0.jar *.java
Confirm that your code compiled -- you should see a SimpleSerialRX.class class file.
Copy and upload the following Arduino sketch. It transmits (TX) messages to the Serial bus once per loop iteration. The message that is sent is simply the number of the loop iteration. It then checks for a message from the Serial port; if it does receive (RX) one, it changes the status of the onboard LED accordingly. (0 -> LED off, 1 -> LED on, 2 -> LED blink once)
/*
* Adapted from Yu Hin Hau's code
* https://billwaa.wordpress.com/2012/05/18/engineering-arduino-to-java-communication-2/
*
* Simple example of Java and Arduino Communication
* Sends and receives messages using the serial bus.
* If received message is: 0 -> LED off, 1 -> LED on, 2 -> LED blink once.
*/
// settings
int serialBaudRate = 19200;
int LED = 13;
//Define special symbols
char start_char = '@';
char end_char = '#';
char sep_char = ':';
// tx, rx data
String temp; // temp memory for transmitting data
int messageNum = 0; // sent with number of loop iteration
char operation = 0; // to hold received data
void setup()
{
// for communication
Serial.begin(serialBaudRate);
// for LED output
pinMode(LED, OUTPUT);
}
/**
* At each loop iteration, send/transmit (tx) a message with the next number.
* Then, see if there is a message to receive (rx) and store it to use as the next operation.
**/
void loop()
{
// send one message
transmitNextMessage();
// try to receive a message
if ( receiveMessage() )
{
// if so, use it to control the LED
doOp();
}
}
/**
* Send a message with the next number.
**/
void transmitNextMessage()
{
// begin the message, includes sending a special start character
startStream();
// main message
writeStream(messageNum++);
// end the message, includes sending a special end character
endStream();
}
/**
* See if there is a message to receive. If so, store it in
* operation variable.
**/
boolean receiveMessage()
{
// check if there is a message
boolean msgWaiting = (Serial.available() > 0) ? true : false;
// if there is one, read it and store in operation
if ( msgWaiting )
operation = Serial.read();
// return true if we got a message
return msgWaiting;
}
/*Remember that the data we sent
* from the Java client are bytes casted as chars;
* therefore, the values that get sent
* through are ASCII numerical codes.
* We use this data to convert / compare it back with
* a char type value.
* 0 -> LED off, 1 -> LED on, 2 -> LED blink once
*/
void doOp()
{
//LED Off
if(operation == '0')
digitalWrite(LED, LOW);
//LED On
else if(operation == '1')
digitalWrite(LED, HIGH);
//LED Blink
else if(operation == '2')
{
digitalWrite(LED, HIGH);
delay(500);
digitalWrite(LED, LOW);
delay(500);
}
}
//Methods to Convert Everything to String
//Then Send out through Serial Port
void startStream()
{
Serial.write(start_char);
Serial.flush();
}
void endStream()
{
Serial.write(end_char);
Serial.flush();
}
void sepStream()
{
Serial.write(sep_char);
Serial.flush();
}
void writeStream(int data)
{
temp = String(data);
byte charBuf[temp.length()];
temp.getBytes(charBuf,temp.length()+1);
Serial.write(charBuf,temp.length());
Serial.flush();
sepStream();
}
void writeStream(long data)
{
temp = String(data);
byte charBuf[temp.length()];
temp.getBytes(charBuf,temp.length()+1);
Serial.write(charBuf,temp.length());
Serial.flush();
sepStream();
}
void writeStream(char string[])
{
Serial.write(string);
Serial.flush();
sepStream();
}
As with the previous parts of the lab:
Notice the BAUD_RATE value matches the constant from the Java file -- it is important that they agree on the communication rate!
Upload your sketch to the Arduino.
Do not open the Serial monitor, as it will interfere with the java program's ability to access the bus.
Now, it's time to test out sending and receiving messages. Remember that the Java program relies on the JSSC library. Therefore, you need to again include the JAR in the classpath; in addition, you will need to include the directory with the class file of your java program. Here, we assume the current directory, denoted by the dot (.), and append it to the path by using the OS-dependent path separator ( : for *nix systems, including Mac OS X).
Run your java program with the java command and -cp flag, as in
java -cp /Applications/Arduino1.6.0.app/Contents/Resources/Java/jssc-2.8.0.jar:. SimpleSerialTXRX
You should see a numbers printed out -- this is a message being received from the Arduino.
You will then be prompted to send a message as indicated.
Because some messages are being missed, you will notice:
You do not see every message printed out the Java side. Because of the speed at which messages are being sent from the Arduino and the time spent waiting for the user to enter a subsequent command, the Java program does not RX all messages. They do not get stored on the bus.
Similarly, you may find that your onboard LED does not always respond. Again, because the Arduino program is spending some time in TX mode, it may not RX a message from the Java program.