Assignment 4: Dot Dot Dot, Dash Dash Dash, Dot Dot Dot
Click here to access the respository for this assignment.
The idea
In this assignment you’ll use the combined power of Java and Arduino’s communications. Java will send data to the Arduino, and the Arduino will process those bytes and display their contents in Morse code. Morse code is an example of a substitution cipher because individual letters are replaced by a specific, fixed Morse code.
By the end of this assignment you should have a better understanding of how data streams work and how they can be used. In addition, you will practice: iterating through arrays, working with ASCII characters, and considering the logic required to properly implement non-blocking (i.e., delta) timing.
The background
The Java side of the communication
We will be using the Java SerialComm
class, which you created during the
last studio. If you don’t yet have a functionally complete SerialComm
class (that supports debug
of outgoing bytes), you will
want to finish that first.
The Arduino
Serial stream
The primary functions for interacting with Arduino’s incoming Serial
data
are Serial.available()
and
Serial.read()
. The Serial
object operations are also built on top of
Stream
objects.
Serial.available()
returns the number of bytes currently available to the
Serial.read()
function. Serial.read()
reads the next byte from the
stream.
Additional Functions
You will also use morseEncode(char symbol)
, which has already been written
for you. morseEncode()
will return a String
that contains data
corresponding to the Morse code for a given symbol. Since Morse code
doesn’t have codes for lowercase letters, you will author a toUpper()
function that will convert lowercase characters to uppercase. This will
enable the morseEncode()
function (which already calls toUpper()
, meaning
you don’t have to edit morseEncode()
) to translate the alphanumeric
characters inputted to morse code. toUpper()
should have the following
parameters:
// Argument: Any character
// Return Value: Either:
// 1) If the character is a letter,
// the upper case equivalent.
// 2) If the character is not a letter,
// the original value.
char toUpper(char c) {
...
}
The easiest way to complete toUpper()
is to use the properties of ASCII values. If you look closely at the ASCII Table you’ll notice that a lower case letter can be converted to an upper case letter by changing just one bit, so you may want to review bitwise operations. Since the letters are numeric and in lexicographic order they can also be compared via the normal relational operators, like:
char c;
...
// Does this character come "at or after"
// the character for 0? (The character 1 would
/ come after 0, etc.)
if(c>='0') {
Using these properties of ASCII (being able to do comparisons, lexicographic ordering, and being able to manipulate bits) you can complete toUpper()
with just a few lines of code.
Including files
There are three files in the MorseCoder/
directory:
MorseCoder.ino
: This contains a nearly empty sketch. You will complete all your Arduino work here.MorseCodes.h
: This is called a header file (hence the “.h” extension) and contains things that need to read at the top (or head) of a source file to define features used later in the file. TheMorseCoder.ino
Arduino file “includes” it to have access to the function defined inMorseCodes.cpp
. A header file and corresponding#include
are used somewhat like theimport
statement in Java. Read through the comments which describe the function(s) you may need to use for this assignment.MorseCodes.cpp
: This contains a Morse code table and code that uses it. You do not need to modify it. Although you don’t need to understand the exact details of howmorseEncode()
works to be able to use it, take a few minutes and read through it. You should notice that it uses the properties of ASCII in a few ways.
Header files are an important part of code organization in C. They’re usually paired with a source file (Often with a “.c”, “.cc”, or “.cpp” extension). This separation helps for several reasons: it speeds up compile time, keeps code organized, and it can prevent errors due to order of declarations. Most importantly, the header is usually used to specify a public interface to functions whereas the corresponding source file provides private declarations. Often when you just need to use a function and don’t care about how it works you can simply refer to the header file.
Often a library of related functionality is contained in one source file (.cpp file) that may be used in many different projects. The MorseCodes files are a library that can be used to encode and decode Morse code.
The Arduino Assignment
Getting the Arduino to recognize input from the Serial Monitor
-
Start by making the sketch in
MorseCoder.ino
read in characters from the Serial Monitor and echo them back, as in the studio. - Complete the
toUpper()
function and test it. Use it to ensure all the echoed characters are in upper case. For example, if the following is entered:Don't shout at me!
It would be echoed back as:
DON'T SHOUT AT ME!
-
Use the
morseEncode()
function to echo back the Morse code of the characters instead of the characters themselves.morseEncode()
will use yourtoUpper()
function, so there’s no need for you to calltoUpper()
yourself. Read the comments inMorseCodes.h
for details of how you need to callmorseEncode()
. - Update your code to blink an LED for the given Morse code. You will
have to process each character of the Morse code separately. For example,
the
morseEncode()
function will return a String. You will have to loop through the returned string, retrieve each character, and use the character to turn on an LED for the appropriate amount of time. You can access an individual letter by using array-like notation. For example, this would print each letter in a string one-at-a-time:
String words = "Hello World!";
for(int i=0;i<words.length();i++) {
Serial.print(words[i]);
}
- The timing of Morse code should be as follows:
- you chose the duration of 1 unit; 500 ms is a good choice
- a dot,
.
, turns the LED on for 1 unit - a dash,
-
, turns the LED on for 3 units - a space ensures the LED is off for a total of 7 units
- between the dots and dashes of a single symbol’s code, turn the LED off
for 1 unit. For example, the code for
A
is.-
. The LED needs to be off for 1 unit between when it is on for the.
and then on again for the-
. - between symbols the LED should be off for 3 units. For example, if coding
two letters, like
AB
you would have to blink the code forA
, which is._
, and the code forB
, which is-...
. The LED should be off for three units of time between the final symbol ofA
and the start ofB
to indicate the end of a letter. - when not displaying a dot or a dash the LED should be turned off.
Here are two visualizations showing the full encoding of OH HELLO
:
Time: 1234567890123456789012345678901234567890123456789012345678901234567890123 Message: O---------- H---- H---- E L-------- L-------- O---------- Signal: HHHLHHHLHHHLLLHLHLHLLLLLLLHLHLHLLLHLLLHLHHHLHLHLLLHLHHHLHLHLLLHHHLHHHLHHH ^ ^ ^ ^ ^ dash | dot | | symbol space word space letter space
Character | Complete Morse Code | Each Symbol | LED On Time | LED Off Time |
---|---|---|---|---|
O | --- |
- (1st - ) |
3 | 1 |
” | - (2nd - ) |
3 | 1 | |
” | - (3rd - ) |
3 | 1 | |
(between letter) | 2 (total of 3 including above) | |||
H | .... |
. (1st) |
1 | 1 |
” | . (2nd) |
1 | 1 | |
” | . (3rd) |
1 | 1 | |
(between letter) | 2 (total of 3 including above) | |||
(space) |
4 (total of 7 including above) | |||
H | ... |
. (1st) |
1 | 1 |
” | . (2nd) |
1 | 1 | |
” | . (3rd) |
1 | 1 | |
(between letter) | 2 (total of 3 including above) | |||
E | . |
. |
1 | 1 |
(between letter) | 2 (total of 3 including above) | |||
L | .-.. |
. |
1 | 1 |
” | - |
3 | 1 | |
” | . |
1 | 1 | |
” | . |
1 | 1 | |
(between letter) | 2 (total of 3 including above) | |||
L | .-.. |
. |
1 | 1 |
” | - |
3 | 1 | |
” | . |
1 | 1 | |
” | . |
1 | 1 | |
(between letter) | 2 (total of 3 including above) | |||
O | --- |
- (1st - ) |
3 | 1 |
” | - (2nd - ) |
3 | 1 | |
” | - (3rd - ) |
3 | 1 |
Although you may initially use delay()
timing to get going with the assignment, when you’re done you should only be using delta timing (i.e., NO delay()
allowed).
The PC Assignment
Your final task is to author a Java program that performs very close to the same function as the Serial Monitor.
- Complete the
PCToMorseCode
insrc/assignment5
. It will be somewhat like yourHexTX
from Studio 5 with a few minor changes:- It will read an entire line at a time.
- It will send send each character to serial port.
- The
debug
inSerialComm
should be set totrue
so that you can see the bytes as they go into and out of the Java program (to/from the Arduino).
Demo
The following video shows a brief demo of the expected timing:
- The pane on the right shows the text being entered on the console by the user
- The horizontal band shows the Morse code being typed out. The “|” indicates the time. The red one is the current time.
- Immediately above the | is the current symbol being sent. A dot takes a single time period and a dash takes three time periods (so a dash is over three |s). Note the time between dots/dashes of a single letter (one |), between different letters of the same word (a total of three |s), and between different words (a total of seven |s).
- Above the dot/dash indicators is the specific letter being sent.
- The light is in the lower right corner. Note that for a dash it is lit for three |s and for a dot it is lit for a single |.
The check-in
Verify that you have completed the following files:
- `MorseCoder/`
- `MorseCoder.ino`
- `MorseCodes.cpp`
- `MorseCodes.h`
- `communication/`
- `SerialComm.java`
- `PCToMorseCode.java`
-
Commit all your code. Do not ask to be checked out by a TA until after you have made certain that your work is committed. Failing to do this may result in you losing points on your assignment.
-
Follow the checklist below to see if you have everything done before demo your assignment to a TA.
- Your sketch is able to read input from Java or the Serial Monitor
Serial.read()
should only be invoked when data isavailable()
- LED flashes properly (dot and dash are distinguishable)
- Timing between letters and between words is correct
- No
delay()
is used in your work - Your Java program reads lines of characters and sends them through the serial port.
- Your Java program uses
debug
to display the outgoing data - All of your files are committed
-
Check out with a TA.
The rubric
- 100pts: Did the demoed assignment work?
toUpper()
meets requirements (10pts)- Proper Morse code. Dashes differ from dots, blinking the correct patterns. (15 pts)
- Proper timing between dots/dashes, between symbols, and between words (15 pts)
- Arduino code uses delta timing (No
delay()
s) (15 pts) - Arduino code is well-organized, variables are appropriately named, and comments are used to aid understanding (10 pts)
- Java code reads from the console and sends data to the Arduino (25 pts)
- The
SerialComm
class has an appropriatewriteByte()
method (10 pts)