This week in Physical Computing we dissected how our Arduino and computer talk to one another. They speak by sending bytes of data to one another, one bit at a time; there are 8 bits in a byte.
But in order to communicate successfully the following is needed:
An electrical agreement that consists of three lines.
1) A transmit line (Tx) from the Arduino (attached to the computer’s receive line Rx)
2) A receive line (Rx) for for the Arduino (attached to the computer’s transmit line Tx)
3) A common ground (GND)
Each device needs to operate at the same voltage. If the Arduino is sending out at 5V, then computer needs to receive at 5V.
The speed at which the data is sent and read. For a typical baud rate of 9600, for example, the Arduino will send bit of data every 1/9600 second and the computer will listen every 1/9600 of a second. Both devices are keeping time separately (they do not share a common clock--hence the asynchronous label), but they are sending and listening for data at an agreed upon rate. Writing Serial.begin (9600) in the setup() function of the Arduino IDE sets this rate.
The devices also have to agree on the logic for interpreting pulses of information. For example, they can agree that HIGH = 1 and LOW = 0 (true logic) or HIGH = 0 and LOW = 1 (inverted logic).
With these agreements in place, an additional agreement is what those pulses as a group mean. In other words, how should they be represented: “When two devices are communicating serially, they can’t read each other’s program logic. They can only read what the other device has said. Because of this, you need to make sure that the programs on both sides are using the same communications protocol. This is the highest level of agreement in serial communications: both sides need to agree on what the bytes being sent mean, and in what order they are being sent.”
So let’s talk about serial communications protocols:
Only one byte of data can be sent at a time over a serial port. The question becomes how are the values interpreted?
Using Serial.write() in our Arduino IDE sends the binary value of the sensor reading. For the value, 255, the binary form is 1111 1111, one byte of information (8 bits). When we use Serial.write() we’re using a binary protocol, “because there’s no other meaning to each byte’s value other than the number itself.” The Serial Monitor, however, automatically shows the ASCII representation of the value being read, and each value is represented as a letter, number or special character. ("When the Serial Monitor receives a byte, it and assumes it should show you the ASCII character corresponding to that byte’s value.") There are 255 ASCII codes--see this lookup table. A sensor value of 255 shows nothing in the Serial Monitor because it’s actually nbsp (non-breaking space).
So what if you have a sensor value higher than 255? Anything higher cannot be sent as a byte. 256 in binary is 100000000 and needs 9 bits.
Also, what if you have multiple sensors returning values? How can you tell which number is from each sensor?
AND, there are only 255 ASCII codes in the lookup table…what to do?!
One answer to all questions is to use a different protocol: Serial.print() and Serial.println(). This breaks down the value into separate characters and represent each one individually, again using ASCII. If we use an ASCII lookup table, the individual numbers in 256 would have a decimal representation of 50 (for 2), 53 (for 5), and 54 (for 6).
Not only can we represent individual numbers, but we can also represent letters and punctuation marks with ASCII codes. Since we can represent other characters, we can use those to separate values from multiple sensors AND tell the receiving computer when the message starts and when it ends. Cool, huh?
When we use the protocol Serial.println() to display values in the Serial Monitor, we see a list that looks something like this:
and ACTUALLY, there are two bytes of data (two characters) that we don’t see with ASCII-encoded decimal representations of 13 and 10. These are carriage return and new line, respectively, and tell the display to wrap the next value to the next line. These are control characters and don’t print in the monitor; they are invisible. So when using Serial.println(), 256 in decimal form is the string 50 53 54 13 10 behind the scenes. Five separate bytes of data sent as a string.
Working with multiple sensors and serial data:
As mentioned above, using the Serial.print() protocol encodes values in ASCII form but also gives us the ability to represent other characters in ASCII form, too--like a comma or other punctuation marks to separate data and keep it in order. (Remember also that Serial.println() includes a carriage return and next line control characters--another option for organizing data.)
Here's example code for printing sensor data into the Serial Monitor from the first three Analog Pins on the Arduino.
But what if the sending is happening faster than the data can be read? Another option is to set up a process whereby the sender only transmits data when it is requested by the receiver--a call and response or handshake method. Here's my modified code from the lab to request values from my three sensors in this fashion.