Arrays and Strings - Programming Arduino Getting Started with Sketches (2012)

Programming Arduino Getting Started with Sketches (2012)

5
Arrays and Strings

After reading Chapter 4, you have a reasonable appreciation as to how to structure your sketches to make your life easier. If there is one thing that a good programmer likes, it’s an easy life. Now our attention is going to turn to the data that you use in your sketches.

The book Algorithms + Data Structures = Programs by Niklaus Wirth has been around for a good while now, but still manages to capture the essences of computer science and programming in particular. I can strongly recommend it to anyone who finds themselves bitten by the programming bug. It also captures the idea that to write a good program, you need to think about both the algorithm (what you do) and the structure of the data you use.

You have looked at loops, if statements, and what is called the “algorithmic” side of programming an Arduino; you are now going to turn to how you structure your data.

Arrays

Arrays are a way of containing a list of values. The variables that you have met so far have contained only a single value, usually an int. By contrast, an array contains a list of values, and you can access any one of those values by its position in the list.

C, in common with the majority of programming languages, begins its index positions at 0 rather than 1. This means that the first element is actually element zero.

To illustrate the use of arrays, we could create an example application that repeatedly flashes “SOS” in Morse code using the Arduino board’s built-in LED.

Morse code used to be a vital method of communication in the 19th and 20th centuries. Because of its coding of letters as a series of long and short dots, Morse code can be sent over telegraph wires, over a radio link, and using signaling lights. The letters “SOS” (an acronym for “save our souls”) is still recognized as an international signal of distress.

The letter “S” is represented as three short flashes (dots) and the letter “O” by three long flashes (dashes). You are going to use an array of ints to hold the duration of each flash that you are going to make. You can then use a forloop to step through each of the items in the array, making a flash of the appropriate duration.

First let’s have a look at how you are going to create an array of ints containing the durations.

Image

You indicate that a variable contains an array by placing [] after the variable name.

In this case, you are going to set the values for the durations at the time that you create the array. The syntax for doing this is to use curly braces and then values each separated by commas. Don’t forget the semicolon on the end of the line.

You can access any given element of the array using the square bracket notation. So, if you want to get the first element of the array, you can write the following:

Image

To illustrate this, let’s create an array and then print out all its values to the Serial Monitor:

Image

Image

Upload the sketch to your board and then open the Serial Monitor. If all is well, you will see something like Figure 5-1.

This is quite neat, because if you wanted to add more durations to the array, all you would need to do is add them to the list inside the curly braces and change “9” in the for loop to the new size of the array.

Image

Figure 5-1 The Serial Monitor Showing the Output of Sketch 5-01

You have to be a little careful with arrays, because the compiler will not try and stop you from accessing elements of data that are beyond the end of the array. This is because the array is really a pointer to an address in memory, as shown in Figure 5-2.

Programs keep their data, both ordinary variables and arrays, in memory. Computer memory is arranged much more rigidly than the human kind of memory. It is easiest to think of the memory in an Arduino as a collection of pigeonholes. When you define an array of nine elements, for example, the next available nine pigeonholes are reserved for its use and the variable is said to point at the first pigeonhole or element of the array.

Going back to our point about access being allowed beyond the bounds of your array, if you decided to access durations[10], then you would still Memory get back an int, but the value of this int could be anything. This is in itself fairly harmless, except that if you accidentally get a value outside of the array, you are likely to get confusing results in your sketch.

Image

Figure 5-2 Arrays and Pointers

However, what is far worse is if you try changing a value outside of the size of the array. For instance, if you were to include something like the following in your program, the results could simply make your sketch break:

Image

The pigeonhole durations[10] may be in use as some completely different variable. So always make sure that you do not go outside of the size of the array. If your sketch starts behaving strangely, then check for this kind of problem.

Morse Code SOS Using Arrays

Sketch 5-02 shows how you can use an array to make your emergency signal of SOS:

Image

An obvious advantage of this approach is that it is very easy to change the message by simply altering the durations array. In sketch 5-05, you will take the use of arrays a stage further to make a more general-purpose Morse code flasher.

String Arrays

In the programming world, the word string has nothing to do with long thin stuff that you tie knots in. A string is a sequence of characters. It’s the way you can get your Arduino to deal with text. For example, the sketch 5-03 will repeatedly send the text “Hello” to the Serial Monitor one time per second:

Image

String Literals

String literals are enclosed in double quotation marks. They are literal in the sense that the string is a constant, rather like the int 123.

As you would expect, you can put strings in a variable. There is also an advanced string library, but for now you will use standard C strings, such as the one in sketch 5-03.

In C, a string literal is actually an array of the type char. The type char is a bit like int in that it is a number, but that number is between 0 and 127 and represents one character. The character may be a letter of the alphabet, a number, a punctuation mark, or a special character such as a tab or a line feed. These number codes for letters use a standard called ASCII; Some of the most commonly used ASCII codes are shown in Table 5-1.

Image

Table 5-1 Common ASCII Codes

The string literal “Hello” is actually an array of characters, as shown in Figure 5-3.

Note that the string literal has a special null character at the end. This character is used to indicate the end of the string.

String Variables

As you would expect, string variables are very similar to array variables, except that there is a useful shorthand method for defining their initial value.

Image

This defines an array of characters and initializes it to the word “Hello.” It will also add a final null value (ASCII 0) to mark the end of the string.

Image

Figure 5-3 The String Literal “Hello”

Although the preceding example is most consistent with what you know about writing arrays, it would be more common to write the following:

Image

This is equivalent, and the * indicates a pointer. The idea is that name points to the first char element of the char array. That is the memory location that contains the letter H.

You can rewrite sketch 5-03 to use a variable as well as a string constant, as follows:

Image

A Morse Code Translator

Let’s put together what you have learned about arrays and strings to build a more complex sketch that will accept any message from the Serial Monitor and flash it out on the built-in LED.

The letters in Morse code are shown in Table 5-2.

Some of the rules of Morse code are that a dash is three times as long as a dot, the time between each dash or dot is equal to the duration of a dot, the space between two letters is the same length as a dash, and the space between two words is the same duration as seven dots.

For this project, we will not worry about punctuation, although it would be an interesting exercise for you to try adding this to the sketch. For a full list of all the Morse characters, see en.wikipedia.org/wiki/Morse_code.

Image

Table 5-2 Morse Code Letters

Data

You are going to build this example a step at a time, starting with the data structure that you are going to use to represent the codes.

It is important to understand that there is no one solution to this problem. Different programmers will come up with different ways to solve it. So, it is a mistake to think to yourself, “I would never have come up with that.” Well, no, quite possibly you would come up with something different and better. Everyone thinks in different ways, and this solution happens to be the one that first popped into the author’s head.

Representing the data is all about finding a way of expressing Table 5-2 in C. In fact, you are going to split the data into two tables: one for the letters, and one for the numbers. The data structure for the letters is as follows:

Image

What you have here is an array of string literals. So, because a string literal is actually an array of char, what you actually have here is an array of arrays—something that is perfectly legal and really quite useful.

This means that to find Morse for A, you would access letters[0], which would give you the string .-. This approach is not terribly efficient, because you are using a whole byte (eight bits) of memory to represent a dash or a dot, which could be represented in a bit. However, you can easily justify this approach by saying that the total number of bytes is still only about 90 and we do have 2K to play with. Equally importantly, it makes the code easy to understand.

Numbers use the same approach:

Image

Globals and Setup

You need to define a couple of global variables: one for the delay period for a dot, and one to define which pin the LED is attached to:

Image

The setup function is pretty simple; you just need to set the ledPin as an output and set up the serial port:

Image

The loop function

You are now going to start on the real processing work in the loop function. The algorithm for this function is as follows:

• If there is a character to read from USB:

• If it’s a letter, flash it using the letters array

• If it’s a number, flash it using the numbers array

• If it’s a space, flash four times the dot delay

That’s all. You should not think too far ahead. This algorithm represents what you want to do, or what your intention is, and this style of programming is called programming by intention.

If you write this algorithm in C, it will look like this:

Image

There are a few things here that need explaining. First, there is Serial.available(). To understand this, you first need to know a little about how an Arduino communicates with your computer over USB. Figure 5-4 summarizes this process.

In the situation where the computer is sending data from the Serial Monitor to the Arduino board, then the USB is converted from the USB signal levels and protocol to something that the microcontroller on the Arduino board can use. This conversion happens in a special-purpose chip on the Arduino board. The data is then received by a part of the microcontroller called the Universal Asynchronous Receiver/Transmitter (UART). The UART places the data it receives into a buffer. The buffer is a special area of memory (128 bytes) that can hold data that is removed as soon as it is read.

Image

Figure 5-4 Serial communication with an Arduino

This communication happens regardless of what your sketch is doing. So, even though you may be merrily flashing LEDs, data will still arrive in the buffer and sit there until you are ready to read it. You can think of the buffer as being a bit like an e-mail inbox.

The way that you check to see whether you “have mail” is to use the function Serial.available(). This function returns the number of bytes of data in the buffer that are waiting for you to read. If there are no messages waiting to be read, then the function returns 0. This is why the if statement checks to see that there are more than zero bytes available to read, and if they are, then the first thing that the statement does is read the next available char, using the function Serial.read(). This function gets assigned to the local variable ch.

Next is another if to decide what kind of thing it is that you want to flash:

Image

At first, this might seem a bit strange. You are using <= and >= to compare characters. You can do that because each character is actually represented by a number (its ASCII code). So, if the code for the character is somewhere between a and z (97 and 122), then you know that the character that has come from the computer is a lowercase letter. You then call a function that you have not written yet called flashSequence, to which you will pass a string of dots and dashes; for example, to flash a, you would pass it .- as its argument.

You are devolving responsibility to this function for actually doing the flashing. You are not trying to do it inside the loop. This lets us keep the code easy to read.

Here is the C that determines the string of dashes and dots that you need to send to the flashSequence function:

Image

Once again, this looks a little strange. The function appears to be subtracting one character from another. This is actually a perfectly reasonable thing to do, because the function is actually subtracting the ASCII values.

Remember that you are storing the codes for the letters in an array. So the first element of the array contains a string of dashes and dots for the letter A, the second element includes the dots and dashes for B, and so on. So you need to find the right position in the array for the letter that you have just fetched from the buffer. The position for any lowercase letter will be the character code for the letter minus the character code for a. So, for example, a - a is actually 97 - 97 = 0. Similarly, c - a is actually 99 - 97 = 2. So, in the following statement, if ch is the letter c, then the bit inside the square brackets would evaluate to 2, and you would get element 2 from the array, which is -.-:

What this section has just described is concerned with lowercase letters. You also have to deal with uppercase letters and numbers. These are both handled in a similar manner.

The flashSequence Function

We have assumed a function called flashSequence and made use of it, but now you need to write it. We have planned for it to take a string containing a series of dashes and dots and to make the necessary flashes with the correct timings.

Thinking about the algorithm for doing this, you can break it into the following steps:

• For each element of the string of dashes and dots (such as .-.-)

• flash that dot or dash Using the concept of programming by intention, let’s keep the function as simple as that.

The Morse codes are not the same length for all letters, so you need to loop around the string until you encounter the end marker, \0. You also need a counter variable called i that starts at 0 and is incremented as the processing looks at each dot and dash:

Image

Again, you delegate the actual job of flashing an individual dot or dash to a new method called flashDotOrDash, which actually turns the LED on and off. Finally, when the program has flashed the dots and dashes, it needs to pause for three dots worth of delay. Note the helpful use of a comment.

The flashDotOrDash Function

The last function in your chain of functions is the one that actually does the work of turning the LED on and off. As its argument, the function has a single character that is either a dot (.) or a dash (-).

All the function needs to do is turn the LED on and delay for the duration of a dot if it’s a dot and three times the duration of a dot if it’s a dash, then turn the LED off again. Finally it needs to delay for the period of a dot, to give the gap between flashes.

Image

Putting It All Together

Putting all this together, the full listing is shown in sketch 5-05. Upload it to your Arduino board and try it out. Remember that to use it, you need to open the Serial Monitor and type some text into the area at the top and click Send. You should then see that text being flashed as Morse code.

Image

Image

Image

This sketch includes a loop function that is called automatically and repeatedly calls a flashSequence function that you wrote, which itself repeatedly calls a flashDotOrDash function that you wrote, which calls digitalWrite and delay functions that are provided by Arduino!

This is how your sketches should look. Breaking things up into functions makes it much easier to get your code working and makes it easier when you return to it after a period of not using it.

Conclusion

In addition to looking at strings and arrays in this chapter, you have also built this more complex Morse translator that I hope will also reinforce the importance of building your code with functions.

In the next chapter, you learn about input and output, by which we mean input and output of analog and digital signals from the Arduino.