Studio 6: Streaming Data from Arduino to Java
- Authoring Java tools
- Optional additional stuff
- Finish up
- Key Concepts
Click here to access the repository for this studio.
Up to this point everything we have sent from the Arduino to the desktop PC has been displayed on the PC screen using the Arduino IDE’s Serial Monitor. No more! The topic of today’s studio is receiving data in a Java program.
By the end of this studio you should know:
- how to receive data from a serial port in a Java program, and
- (optionally) how to send and receive data types that are larger than a single byte.
Authoring Java tools
The first task for Arduino to Java communication is to have a Java program receive data sent by the Arduino. We will start simple.
You may want to refer to the JavaDoc for the jSSC
library, particularly the
SerialPort class methods.
- You will be modifying the
SerialCommclass that you created in last week’s studio. Open up the code you wrote last week and copy it into the repository for this studio.
Make sure to give it a
static void main() method, if it doesn’t already
have one. This is where your Java program will start.
This program will receive data from a serial port (your Arduino) and print it out. In order to do that, you will use additional features of the
SerialPort class that you were introduced to last week.
You should author two new methods (in addition to
main) in your
available()method should return a
trueif there is at least one byte available to be read from the serial port. The
SerialPortis helpful here.
readByte()method should read a single byte from the serial port and return a
bytethat contains the value read. The
readBytes(int byteCount)method of
SerialPortis helpful here, but watch out for its return type (an array of bytes,
byte, rather than a single
mainroutine, first create a
Next, continuously check if a byte is
available()and read it if there is. Have your program infinitely do this.
Now, take the data you read and find a way to print it to the Java console. Note the return type of
readByte(). Though in our case it represents a
char, it is not. This is a design choice. You will have to cast it to print it properly.
Using any of your previous Arduino sketches that send data to the Serial Monitor (e.g.,
heartbeat.inomight be a good choice), exercise your Java program and ensure that you get the same text output on the Java console that you previously were getting when using the Serial Monitor built into the Arduino IDE.
Experiment by starting the Java program first, then the Arduino. You can do this by downloading the Arduino program, running it and testing via the Serial Monitor, closing the Serial Monitor, and pulling the plug on the USB cable to the Arduino. Start up the Java program, then plug in the Arduino, which will cause it to start again (the sketch is retained in memory that is not lost when the power is removed).
After you’ve tried that, change the startup order. What happens when? Why?
A debugging version
Recall that in the previous studio, there was a debug option that could be used to examine outgoing bytes from Java to the Arduino. We wish to replicate this behavior for bytes going from the Arduino to Java.
readByte() method you made earlier so that it conditionally (based upon the value of the
debug instance variable) prints incoming bytes to the console in hex. When
true, you will need to convert to hex before you print: for every byte that comes from the Arduino, the debugging output should be 6 characters: the string “[0x”, 2 characters that represent the data byte in hexadecimal, and the character ‘]’ (e.g., if the byte in the data stream is
5f, the string sent to the console should be “
[0x5f]” (or “
[0x5F]” if you prefer). You can use
String.format("%02x", byte value) to format a
byte as a two-character
String in hexadecimal.
It should continue to return the value of the byte that was input from the serial port.
You should now be able to view the data in hex as it goes through the
readByte() method and be able to view the “true” ASCII value in the Java console.
Show off your working code to an instructor or TA (using the debug mode to show the byte values).
Optional additional stuff
If you have gotten this far and it’s near the end of studio time, consider today a success and jump ahead to Finishing up. If you have time, there are some additional things you can play with that will also be useful in the upcoming assignments.
All of the data we’ve been transmitting so far has been ASCII characters. While it is very possible to communicate entirely using ASCII characters, it is very inefficient. In other words, you could convert your
3to the ASCII character for
0x33) and convert it back upon receipt, but that just seems silly. However, that’s what happens when you use
Serial.print(), or, in general, any “print” method: the program converts whatever you want to print from it’s internal representation into a printable representation, like an ASCII character, then sends that to an output stream.
If you actually want to send a
0x33, over a stream, you use
Serial.write(). From the documentation for
Serial.write(), it’s clear that it can only send one byte at a time: it can only physically send numbers between
0xff. Anything more than that is truncated:
0x012d) is sent as
Make a new Arduino (in
Arduino/studio6/datatypetest) sketch that first
Serial.print()s the numbers
360in sequence. What are the results when you use your Java program to view the output? What about Serial Monitor? Use a list of ASCII characters (available on the internet or via the command
man 7 asciiin terminal) to understand the output if it’s confusing, and remember that an Arduino sketch needs to
Serial.begin(9600)before it can use any of the serial commands.
The Arduino is unable to
Serial.write()numbers larger than
255. This is a serious limitation for those of you who enjoy counting.
Let’s investigate how to send larger (more than one byte) data types. An integer (an
int) in Arduino C is a two-byte type. By bitshifting appropriately, you can send each byte of this number sequentially. For example, you can send the number
0x21. Alter this Arduino sketch to send the two bytes in this way, first the most significant byte (
0x4din the example), then the least significant byte.
mainroutine to combine the two received bytes and store the result in a Java integer. You will have to bitshift. Remember, Java’s integers are 4 bytes, so there is plenty of space for a two byte number to fit1.
Output the received integers on the console, and compare that with what you see from
readByte’s debug output.
Extend the Arduino C code to send an Arduino
long intof 4 bytes (the return value from
millis()would be a good choice). On the Java side, pull the same trick you did for the two-byte values above, just extend it to 4 bytes. How well does that work?
- Commit your code and verify in your web browser that it is all there.
- Check out with a TA.
Changes to repo structure:
- Serial communication
- Sending bytes from Arduino to Java (
- Sending bytes from Arduino to Java (
- Java Input
- Reading serial bytes
- Serial.print vs Serial.write
- conversion and communication efficiency
- Multi-byte communication
- byte significance i.e. most significant byte
- Serial.print vs Serial.write
This size difference actually has a large impact when dealing with negative numbers, which, as in all systems, are stored in two’s complement. Since a number’s negativity is entirely dependent on its first bit, naively sending the Arduino
intbytes to Java will not correctly send a negative number.
The solution is sign extension. Bitshifting right in Java (and on signed values in C) automatically copies the highest bit in all the extended places (
0xff00 >> 16 == 0xffff,
0x0d00 >> 8 == 0x00d0). This preserves the value of the number, though that exact proof is left as an excercise for the reader. ↩