None W01E_Arduino_Resource

Arduino Basics

What is "Arduino?"

Arduino is a "platform" designed for rapid development of mechatronic systems, or systems that involve hardware, software, and/or electronics. Mechatronics is also commonly called "physical computing" in the current conversational lexicon.

In this course, we will use the Arduino Mega board for laboratory exercises, and the Pololu Zumo 32u4 for projects. Both of these physical devices can be programmed to interact with their environments using the Arduino programming environment, which is compatible with Mac, Windows, and Linux.

The Arduino website and the Zumo Resource Guide are very well put together, and contain much more information than this document does. This notebook is only intended as a "quick and dirty" guide to get you started, and external resources are linked where appropriate.

But just to make sure everyone is on the same page, here's how "programming an Arduino" works:

  1. Decide what you want the physical device (Arduino or Zumo) to do. Usually, this will be something like: "Measure X, then output Y." Robots and control systems interact with their environments, and the "pins" on an Arduino or Zumo's processor are how we both read information from the outside world and give information back to the outside world.

  2. Using your computer, write some code called a "sketch" that will tell the physical device (Arduino or Zumo) what to do. Part of this code, contained in a special function called "void loop()," runs endlessly, so the microprocessor can react to a moment's notice to changes in its environment (in our case, inputs you or a system give it).

  3. Compile that human-readable code into a format the microprocessor can understand, and upload the instructions onto the physical processor (Arduino or Zumo). After you do this, your computer is no longer needed for the processor to operate!! With that said, we often do use serial communication through a USB port to help us see what the Arduino/Zumo is doing at any moment.

Remember: the Arduino will only do exactly what you tell it to do... nothing more, nothing less!

Arduino the language

Many people say that they know how to program "in Arduino," but Arduino isn't really its own language. It is a subset of the C++ programming language, which is very powerful and ubiquitous in robotics, automation, and controls because it is fast and flexible. So when you're learning to program in Arduino, you're developing skills that will transfer to more difficult programming languages.

Programming Environment

You can install The Arduino programming environment for Mac, Windows or OSX on your personal computer. All of the lab computers already have it installed.

image.png

The picture above shows what the Arduino environment looks like. You'll use this to program your Zumo robot as well as the Arduinos in the lab space. For an introduction to the Arduino Integrated Development Environment (IDE), click here. The basics of the workflow are as follows:

  1. Write your "sketch" in the window. Save often, and in a place where you'll find your work later
  2. Click the checkmark button (far left) to compile or "verify" your code. Any errors will show up in the black window.
  3. Select your board type using the "Tools" menu, which for this course will either be "Arduino Mega 2560" (Lab) or "Pololu A Star 32u4" (Projects). For a description of how to install drivers and support for the Zumo robot, See the Project 1 Assignment page.
  4. Select the proper serial port from the "Tools" menu so that your computer will be able to talk to your board. On the lab machines, which have an old-style serial port, this will usually be something like "COM5," but it varies. It will not be "COM1" or "COM3."
  5. Click the "Upload" button (right arrow) to upload your code to your board.
  6. If the sketch you wrote provides feedback through the serial port using "Serial.print()" commands, open either the serial monitor (magnifying glass) button or the serial plotter (Tools->Serial Plotter) to visualize feedback from your board. Make sure you slect the correct baud rate on the serial monitor/plotter. It must match what you wrote in your call to Serial.begin(). For this course, it is best to use the fastest available baud rate, or 115200 bits per second.

Program Structure

An Arduino program is a bit different than a MATLAB script you'd write to plot data, simulate a dynamic system, etc. because an Arduino program is primarily designed to interact with the outside world. As such, it must do certain things repeatedly (like measure voltages, send signals to a motor amplifier, etc.). This section gives you some bare-bones basics, but this guide from Arduino is much more complete.

There are three basic parts to most common Arduino "sketches."

  1. Declaration/Initialization (the very top of the code)
  2. The "void setup()" function, which runs only once when the microprocessor first boots.
  3. The "void loop()" function, which runs repeatedly after the setup() function has been called, as long as the Arduino is powered on, and regardless of whether the Arduino is connected to a computer.

At the top of the code, a programmer delcares variables, and may give them initial values. For instance, if I know that I'll need to use a variable called "output voltage" in the void loop() function, and I know I'll need that variable to be global in scope, I'll declare it at the top of the code:

float output_voltage;//just a declaration

This allocates memory for the variable, and tells Arduino that it will be a floating-point number, but does not put a number in that memory yet-- I'll have to do that later. If instead I want to give my variable some default value, I can do that as well:

float output_voltage = 0.0;//a declaration and initialization

The top of the code is also where you will tell the compiler if you wish to use libraries in your code. Also note that every line in Arduino should be terminated with a semicolon! Failure to do this will result in a compiler error.


Any code that needs to run only one time will go in the void setup() loop. This includes beginning a serial connection with your computer using:

Serial.begin(115200);

or declaring certain arduino digital pins as outputs or inputs:

pinMode(13,OUTPUT);
pinMode(2,INPUT);

The void loop() function is where the "meat" of your code goes. If you were to describe what your program does to someone, you'd be describing what you put in the void loop() function. This is where, over and over, the processor will read inputs from its environment (digital signals and analog voltages), make calculations, and write analog and/or digital values back to its environment.

Variable Data Types

All of the variables an Arduino processor works with are stored in memory as binary information. These days, it is fairly common knowledge that "stored as binary information" means "stored as ones and zeros," or stored as a series of low and high voltages. However, there are multiple ways to represent a number in binary. Which way you should choose, or which "data type" you should assign for your variable, depends on, among other things:

  1. How large the number might be
  2. Whether it might need to have fractional values (a decimal point)

This notebook doesn't contain a technical description of how various data types work, but a good description is easy to find. In fact, Arduino's website has specific help pages for each data type. In this course, we will primarily use:

  • int for whole numbers that can be negative or positive
  • unsigned long for very large, always-positive whole numbers (like the number of microseconds that have elapsed since power-on)
  • bool for representing things that can only be either true or false
  • float for representing numbers that likely need a decimal place

It is also possible that in Project 2 of this course, you will want to use an array of values. Arrays in arduino are a lot like arrays in MATLAB, with some important differences:

  1. Arrays in Arduino are fixed-size and must be declared before use. In MATLAB, a variable can "grow" in size during the execution of a program because memory in MATLAB is dynamically allocated.
  2. Arrays in Arduino are zero-indexed, which means that to access the first value in an array, you call my_variable[0] and NOT my_variable[1].
  3. Arrays in Arduino are defined in curly braces {} and are indexed in square brackets []. In MATLAB, arrays are defined in square brackets and indexed in parentheses.

More detail about arrays in Arduino is available here.

Variable Scope

If you will need to use a variable over and over, and need its value to be stored between successive executions of void loop(), it needs to be global in scope. Any variable declared above void setup() and void loop() is global. An example is shown below for a program that increments a variable every time void loop() runs, and prints the value to the serial monitor:

int myCount = 0;

void setup(){
    Serial.begin(115200);
}

void loop(){
    myCount = myCount+1;
    Serial.print(myCount);
    Serial.println();//prints a new line for readability
}

Notice that each time loop() runs, it already needs to "know" what the value of myCount is.

If, on the other hand, you do not need a variable to be "persistent" between executions of void loop() (or any other function, for that matter), or if you just need a "counter" variable for a "for" loop that can be "thrown away" after you're done with it, you can declare variables inside of a function. An example of this is shown below for a program that reads an analog voltage value from pin A0 and prints it to the monitor:

int inPin = 0;//which analog pin will we read from?
//this is global just so it isn't repetitively allocated each time.
void setup(){
    Serial.begin(115200);
}

void loop(){
    int myPotVal = analogRead(inPin);//local because we read a new value each time.
    //note that it doesn't HAVE to be local... could have been global without much trouble.
    Serial.print(myPotVal);
    Serial.println();
}

Pitfall warning! Overwriting Global Variables with Local Ones! One common error that new programmers make is declaring a variable at the top of a sketch (global), but then ALSO delcaring it inside void loop() by accident. When you do this, your program will default to the use of the local variable, not the global one. Here's an example of this mistake, in a simple program that is supposed to print the number "6" but instead prints the number "4:"

int myNumber = 5;
void setup(){
    Serial.begin(115200);
}
void loop(){
    int myNumber = 3;
    int result = myNumber+1;
    Serial.print(result);
    Serial.println();

Remember that any local variable is only "visible" to the program from within the function or flow control statement (for/if/while) in which it was declared!

There are some other variable scoping issues that may come up during the course, including the #DEFINE concept but these are the big ones.

Functions

Some of the programs you'll write in this course can get pretty complex. So, it might be a good idea to learn to write your own functions, which will clean up your void loop() considerably, allowing you to see at a glance what the program is doing. Arduino's official reference guide has a great resource for learning to write your own functions here.

While the link above gives a great overview of functions, one thing that I'd like to mention is that for certain special tasks, it may be a good idea to write functions that don't return any value at all (and thus should be called void myFunctionName() since "void" is the anti-datatype), and just operate on global variables. This isn't a "good programming practice," but for what we'll be doing in this class, it can be very useful for cleaning up your code. Let's look at two examples of a program that has to print several values to the serial monitor at the end. The first does not use a function:

int baseNum = 10;
int loopCount = 0;
int num1;
int num2;
int num3;

void setup(){
    Serial.begin(115200);
}
void loop(){
    //calculate my three numbers
    num1 = baseNum*1+loopCount;
    num2 = baseNum*2+loopCount;
    num3 = baseNum*3+loopCount;
    //print to the serial port for debugging
    Serial.print(num1);
    Serial.print("\t");//prints a tab to space out the numbers
    Serial.print(num2);
    Serial.print("\t");//prints a tab to space out the numbers
    Serial.print(num3);
    Serial.println();//prints a newline to separate individual runs through "loop."
    //increment the loop counter
    loopCount = loopCount+1;
}

Notice that this program has a lot of repetitive code in it, and that it's not the easiest to read. It's a little "busy" (not really, but you can imagine that some programs will get very busy).

Let's now look at a program that does the same thing, but with two user-defined functions. Each one performs a specific function, and is "called" inside void loop() to keep the main part of the program clean and easy to read.

int baseNum = 10;
int loopCount = 0;
int num1;
int num2;
int num3;

void setup(){
    Serial.begin(115200);
}
void loop(){
    //calculate my three numbers
    calcNumbers();
    //print to the serial port for debugging
    printData();
    //increment the loop counter
    loopCount = loopCount+1;
}

void calcNumbers(){
    num1 = baseNum*1+loopCount;
    num2 = baseNum*2+loopCount;
    num3 = baseNum*3+loopCount;
}

void printData(){
    //print to the serial port for debugging
    Serial.print(num1);
    Serial.print("\t");//prints a tab to space out the numbers
    Serial.print(num2);
    Serial.print("\t");//prints a tab to space out the numbers
    Serial.print(num3);
    Serial.println();//prints a newline to separate individual runs through "loop."
}

Now notice that the void loop() function, where you'd start if you wanted to know what the program actually does, is only a couple of lines long, making it very easy to read. All of the functions operate on global variables, and none of them returns anything... but they do clean up the code. Doing this also makes it easy to debug your code, because you can debug one function at a time.

Flow Control

Flow control statements we will commonly use in this course are listed in the following subsections. The Arduino pages for each type of statement will give you a concise introduction.

"If" Statements

see here

"While" Loops

see here

"For" Loops

see here

Typecasting (Changing variable types)

Typecasting is basically changing the datatype of a variable. This is very useful, because many functions in the Arduino language require that you pass them a certain datatype. The documentation page that explains typecasting for Arduino is here. For example, the analogWrite() function requires that you pass the analog value you'd like to write to a pin as an integer between 0 and 255. Let's say your code produced that number as a float. You could typecast that float before sending it to the analogWrite() command, as shown:

float myVal = (float)analogRead(0)*5.0/1023.0*255.0;
    analogWrite(6,(int) 1.0);

Notice that I also typecast the analogRead() command to make sure that the number I was working with in the calculation was a float, offering me the best possible precision in the calculation. In the end, the value I get is rounded, but that's OK.

Common Functions

All of the functions available in the Arduino language are located on the reference page, but here are some of the most important ones we'll use in this course. Pay particular attention to what kind of arguments they take, what they return, and in what units they operate. Do not assume. In the case of digital read and write commands, note the particulars of how the pin should be configured in the void setup() function.

analogRead()

analogWrite()

digitalRead()

digitalWrite()

abs()

constrain()

map()

Serial.print()

(There is more detail in this notebook here-Commands)).

millis()

micros()

(There is more detail about using millis and micros in this notebook here-and-micros())).

Arduino the Device

In general, Arduino-compatible devices are 8-bit microprocessors (although more advanced architectures are gaining traction) surrounded by a few supporting devices on a single Printed Circuit Board (PCB) such a USB connectors/converters, voltage regulators, an oscillator that tells the processor how often to make decisions (clock speed), and connectors so that you can interface the device with the outside world.

Arduino-compatible devices are actually faster than the computer my family had in our house when I was a kid! But they're very small, and can be "embedded" into a control system for portability and reliability. Unlike a traditional PC, tablet, or phone, Arduinos generally do not run an operating system, so they don't get interrupted by emails, automatic updates, or other disruptions that could prevent them from doing their job in a control system.

In this course, lab exercises will require use of the Arduino Mega board, shown below. This board is located under the green circuit board on your ME480 workstation.

Project exercises will use the Pololu Zumo 32u4, shown below: image.png

Each one of these devices has its own quirks, but each is easily programmed using the Arduino environment. For instructions on getting started with the Zumo, see the project one notebook or this page.

Troubleshooting Your Code

As with most of the topics in this guide, arduino has its own comprehensive resource for troubleshooting that you should read. The information in this section is not complete, because there is no "magic pill" or recipe for debugging that will always work. However, these tidbits of (hopefully good) advice are distilled from several years of teaching this course with Arduino.

Compiler Errors

Compiler errors keep the code from being translated to code that the Arduino can understand. Usually, these are caused by typos or omissions, and the Arduino IDE is pretty good at telling you what the problem is. These errors will be reported in the black window in red text when you click either the "verify" or the "upload" button. Before asking for help from your classmates or an instructor, make sure you complete the following steps:

  1. read the compiler error, and determine where and what Arduino thinks the problem is (line numbers are usually given)
  2. Attempt to fix the problem yourself if you understand what the compiler error means
  3. If you do not understand what the compiler error means, and it does not show up in the "common compiler errors" section below, Google part or all of the error message. You're probably not the only one to have this error!

Seeing Red: Common Compiler Errors

Some suggestions for each are given below, but using Google to help troubleshoot these errors is a good idea.

  • (Variable_Name) was not declared in this scope. This error usually has to do with a variable you either forgot to declare or a variable that is local that should be global.

  • Expected ; before (something). If you get this error, you probably have a missing semicolon, parenthesis, or bracket somewhere above the line indicated by the error.

  • Port not found If this error occurs, and your Arduino is plugged in, be sure the correct port is selected in the Arduino menu. If it is, you may have a bad cable, or there may be a short circuit preventing your Arduino from booting up.

  • out of sync error This is usually related to the Arduino's ability to talk to the computer. Is the right board type selected? Is the Arduino healthy and powered properly?

Run-Time Errors: "My code doesn't do what it is supposed to!"

Run-time errors are much more difficult to track down. If your code compiles and uploads properly, but does not do what it is supposed to do, you have a run-time error. Tracking these down is difficult and takes a lot of practice to do quickly, but if you follow a methodical process, you will save yourself a lot of wasted time. The cardinal rule of finding runtime errors, also called debugging, is:

  • do not simply change things for the sake of "seeing what will happen" or because you think "maybe this will fix it." Be methodical in your search for the error.

Arbitrarily changing lines of code, especially more than one at a time, will definitely make your life harder. If you wrote a program, you should know exactly what each line is supposed to do. What this means is that it should be possible to follow a methodical process, working through each thing that the code is supposed to do and testing it (often by printing a particular variable to the Serial monitor), until you find the problem. Here are some general principles that should help you both avoid bugs and find them quickly when they do pop up.

Debugging Strategies

Comment your code.

Although it is true that once you become fluent in a programming language, you should be able to decipher each line in your program and know what it was intended to do. Still, sometimes we forget, or do something silly, so it's best to comment each important line in plain English so that you remember what you were thinking when you wrote it.

Use Serial.print() to learn what is happening at various places in your program.

We can't simple ask the Arduino in English to tell us what it is "thinking." But with the serial monitor, we can have it show us what each key variable is at each key moment in our code. If we're using "if" statements, or if the program structure is complex, it is usually a good idea to make sure the Arduino is running all of the lines of code you think it should be by using Serial.print("I'm Here!"); type statements in all key loops and/or flow control statements

PRO TIP: The advice above is great for finding problems, but it really slows down your code and turns the serial monitor into a mess. Use this method wisely, and delete the extra print statements when your code is running properly!

Functionalize your code.

As explained in the section on functions above, you can organize your code by creating user-defined functions. It's best if you can test these functions independently of the rest of your code. For instance, if you're worried that you might be messing up a particular calculation, having that calculation functionalized will allow you to test that piece of your code by itself. See below.

Create simplified versions of your code that do only one thing

Sometimes, you might now know exactly where the problem is. To isolate it, you can save a few versions of your code, each of which performs a simplified version of what you want the entire code to do. If your code is functionalized, this is actually fairly easy-- you can just comment out the lines in void loop() that call functions other than the one you're trying to test.

Temporarily hard-code values to check inputs and outputs

It is easy to accidentally pass an argument to a function that it inappropriate, and will cause weird behavior. For example we use the analogWrite() function pretty extensively in this course. Looking at the documentation for this function, we see that the input "value" must be an int between 0 and 255. Passing this function a float, or passing it a value outside the range [0,255] will cause strange behavior that is very hard to detect.

The strategy of hard-coding values will help you separate what might be a hardware problem from problems with your code. For instance, if you are supposed to send a value of "255" to the analogWrite command when a potentiometer knob is turned all the way up, but this doesn't seem to correspond with the behavior you're seeing, actually send 255 to the function to determine whether the problem disappears.

Use a multimeter or oscilloscope to make sure that input and output values are what they're supposed to be.

Debugging the hardware attached to your arduino is a whole different topic, but consider for a moment that your code requires a "high" voltage level on pin 5 to execute. Are you sure that when you flip a switch, you're getting a "high" voltage on pin 5? You can use a Serial.print command to check whether the Arduino believes the input voltage to be high, but it is even better to corroborate or refute its belief using a multimeter or oscilloscope!!

Common Bugs

There are countless ways to make a mistake in programming, but there are a couple of common "traps" that are so prevalent that they're worth calling out specifically here:

You used the wrong datatype for a variable

This one happens a lot. Many functions in Arduino require a certain datatype to work properly, and if you're trying to do floating point math (math with decimal places), and you mix integers into your calculations, you could end up in trouble. For example, the two serial print lines below

Serial.print

**You passed an argument to a function that is too big, the wrong sign, or the wrong datatype**

**You overwrote a variable**

This happens a lot when you accidentally declare a variable global, but then re-declare a local version of it within a function. An example of this is shown in the "variable scope" section above. It's also easy to overwrite a variable if you miss an equals sign in a logical statement. here's an example:

```C++
//let us know if a is one.
if(a=1){
    Serial.println("a is one.");
}
else{
    Serial.println("a is not one");
}

No matter what you set "a" to earlier in your code, you will always see "a is one" on the serial monitor. Why? Because the code is missing an equals sign!! The line a=1 assigns the value "1" to the variable "a." That's not what we wanted... we wanted to test if a was equal to one. If we assign the value 1 to a every loop, right before we print, and "1" is interpreted by Arduino as "true" (which it is), of course we're always going to see that a is one!!

What we needed to write was:

if(a==1){...

because logical tests require a double equals sign.

Something critical happens in a flow control statement, but your code never enters that statement

Look at the example above once more, and ask yourself what would happen if you only told the Arduino to drive a motor if it was in the "else" part of that if statement. It would never get there, so we'd never be able to drive the motor! In hindsight, we can see very easily what the problem is. But if you're writing code at 3AM the night before an assignment is due (which I don't recommend), it's easy to miss that pesky equals sign. How would you find that error? The key is to use Serial.print() commands in all key places of your code. Make the Arduino tell you where it is going in the code, and how often, and what all key variables are when you get there.

You are doing math improperly with mixed variable types

Be careful about mixing datatypes when your code is performing calculations. If you'd like a float to come out of a calculation, make sure everything in the calculation is a float or is typecast as a float. This is explained fairly well here.

Using the Serial Monitor and Serial.Print() Commands

This course is going to require you to collect data from the serial monitor (see this video for more information). Doing this is relatively easy, but you should follow the following general format when printing serial data to the monitor:

  1. Use a single Serial.println() statement to create a new line after all data has been printed. This will allow you to easily load the data into Jupyter/MATLAB.
  2. Separate variables with a call to Serial.print("\t"); to insert a tab in between variables

Here is an example of a printData() function that has all of the right pieces:

void printData(){
    Serial.print(t);//print time
    Serial.print("\t");//separate with a tab
    Serial.print(var1);//print first variable of interest
    Serial.print("\t");//separate with a tab
    Serial.print(var2);//print second variable of interest
    Serial.print("\t");//separate with a tab
    Serial.print(var3);//print third variable of interest
    Serial.println();
}

This function prints three numbers separated by tabs, but you can print more! Just remember that lots of printing will slow down you overall "loop time," or the time it takes the Arduino to complete one pass through void loop(). Since most of the control systems we'll be implementing in Arduino will be designed assuming that the Arduino operates infinitely fast (continuous-time math), we want to keep the loop time as small as possible. If you keep track of a "dt" or loop time in your code, you can always check this! Loop times of around 100 Hz are good.

Using millis() and micros()

To use the data we get from Arduino for validating dynamic models, or for calculating approximate integrals and derivatives inside of our Arduino code, we need to keep track of time. To do this, we can use either millis, which gives us the milliseconds elapsed since the Arduino was powered up, or micros, which gives us this value in microseconds. Which should you use? Probably micros if you're calculating numerical derivatives (for velocities, etc.) because its precision is better, but probably millis if you need to keep track of a long period of time. Microseconds will "overflow" or roll over to 0 much more quickly than millis.

Tips

  • use the unsigned long data type to hold values from millis() or micros(). Do NOT use int.
  • when calculating time differences or loop times, don't convert to seconds until AFTER finding the time difference in either milliseconds or microseconds.

Here is an example of a program that calculates the loop time in microseconds, and then converts this to a $\Delta t$ in seconds:

unsigned long microsNow;//what is the time right now?
unsigned long microsLast;//what was the time last time I checked?
unsigned long dtMicros;//loop time in microseconds
float dt;//loop time in seconds, suitable for calculating derivatives.

void setup(){
    Serial.begin(115200);
    microsLast = micros();
    delay(1);//delay for one millisecond
    microsNow = micros();//initialize both of these. BE CAREFUL that you never get a dT of 0!!!
}

void loop(){
    //check the time
    microsNow = micros();
    //compare to old time;
    dtMicros = microsNow-microsLast;
    //convert to seconds
    dt = (float)dtMicros/1000000000.0;
    //store "old time" right now before doing anything else! But make sure we don't need "microsNow" anywhere else
    microsLast = micros;
    //do something useful. read a value from A0 I guess
    int someVal = analogRead(0);
    //print micros, dt, and value
    Serial.print(microsNow);
    Serial.print("\t");
    Serial.print(dt);
    Serial.print("\t");
    Serial.print(someVal);
    Serial.println();
}

Arduino Libraries

The Zumo robot projects will require the use of the Zumo library. See the Zumo intro for more details, but in general, a library is a set of new, specialized functions that you can use if you "include" the library in your code. For information about how to include and install libraries, see the official Arduino page about libraries.

Libraries in arduino are collections of code that add functionality to your Arduino program. They allow you to perform complex tasks with simple code, because more complex code lives "under the hood" inside the library. If a library for Arduino is installed on your computer, then you can "include" the library in your code in order to access its functions and variables.

Libraries in Arduino are usually organized into code structures called "classes" that describe "objects" you can declare and then use in your program, each of which has some useful functionality. In ME352 and ME240, you used the MATLAB programming environment, and you likely used "functions" to perform certain tasks (plot() in MATLAB is a function!). Like functions, organizing code into classes that describe objects can simplify your main program. However, when compared to functions, classes are a more modern, sophisticated concept common in modern programming languages.

So what is a class? It is a piece of code that describes an object. What is an object? It is mostly what it sounds like. For example, a person could be conceived as an object. That person can perform functions (eat, drink, sleep) but that person also has properties (hair color, gender identity, age). An object in code, like an "object" such as a person, can be considered as a collection of properties (sometimes called "member variables") and functions (sometimes called "methods"). If we had a "person" library in Arduino, we might want to declare the existence of several "people" in our code. That would go at the top of our program, and look like this:

//tell Arduino we want to use the Person library
#include <Person.h>
//declare people (like variables).
Person Sam;
Person Jess;

Let's say we initialized these peoples' properties appropriately. Sam and Jess are both instances of the Class (an instance of a class is an object) Person. Each person can have different properties, and calling their respective functions can be done independently. For instance, we can tell Sam and Jess each to eat independently in our code, and we can also access their individual properties to help us make decisions about what they should do. That might lead to a void loop() function that looked something like this pseudocode:

void loop(){
    if(Sam.haircolor=="grey"){
        Sam.eat(more_vegetables);//tell sam to eat more vegetables to slow aging
    }
    if(Jess.age>40){
        Jess.eat(banana);
    }
}

So in our code, we are accessing the functions that define a person (in this case, eat), but we also have the option of querying (and sometimes setting) the properties of each person. Many Arduino libraries, including the Zumo library, are set up this way.

For a more concrete example, let's talk about the "timer" functionality of the ME480FSMTimerCounter library, which you will use in this notebook's challenge. A timer as an object has functions (e.g. update the timer), but it also has properties (e.g. elapsed time since activated). When we use the TimerCounter library, we can create a "timer object" that has its own elapsed time, and that can be updated to tell us when our preset amount of elapsed time has passed.

The following code "includes" the TimerCounter library (assuming it is installed on your system), and sets up two timers that are activated by the Zumo's "button A." It updates the timers in each pass of the void loop() function, and prints the status bit of each timer to the serial monitor.

You can try this code out yourself on your Zumo, or on your ME480 lab workstation (be sure to read the comments to know how to modify the code for use on the lab workstation).

//tell Arduino to load the library.
#include <ME480FSM.h>

//declare a variable for each timer. 
FSMTimer timer1(500);//this timer's status bit goes true after being activated for 500ms
FSMTimer timer2(1000);//this timer's status bit goes true after being activated for 1000ms

//declare the pin number for the button I'll use to activate the timer.
//for Zumo, it is pin 12 for button A. For ME480 workstation, it is pin 29 for BTN 1.
int btnA_pin = 14;

void setup(){
    //set the input button's pin as an INPUT_PULLUP type.
    pinMode(btnA_pin,INPUT_PULLUP);//see note above where btnA_pin is defined.
    Serial.begin(115200);//start a serial monitor
}


void loop(){
    //store the state of the button in a local variable.
    bool btnA_state = digitalRead(btnA_pin); //true if not pressed, false if pressed (must be this way based on wiring of these buttons).
    //now update each timer. Both are activated by holding button A.
    //note that we "not" the button reading, so that the timer is activated when the button is HELD.
    timer1.update(!btnA_state);
    timer2.update(!btnA_state);

    //Print each timer's status bit, as well as elapsed time, to the serial monitor so we can see what this does!
    Serial.print("Timer1 elapsed:\t");
    Serial.print(timer1.elapsed);//elapsed is a variable that each timer "object" keeps track of.
    Serial.print("\t Timer1 status: \t");
    Serial.print(timer1.TMR);
    Serial.print("\t\t");
    Serial.print("Timer2 elapsed:\t");
    Serial.print(timer2.elapsed);//elapsed is a variable that each timer "object" keeps track of.
    Serial.print("\t Timer2 status: \t");
    Serial.print(timer2.TMR);
    Serial.println();

}

If you try this code, hold down Button A (zumo) or user BTN 1 (workstation) and watch what happens on the serial monitor. The timers each activate when button A is held down, and each respective timer's status bit goes true after the timer has remained active for the preset time.

Now that we have a general sense of what an Arduino library is, we need to understand how to install one. We cannot use a library in Arduino without first making sure it is installed.

Installing an Arduino Library from a zip file

when you open the Arduino programming environment, you can install a library by going to the "Sketch$\rightarrow$Include Library$\rightarrow$Add .ZIP Library" option from the menu.

By clicking this link, you can download a file called "ME480FSM-main.zip." This library contains classes that implement timers and a rising edge counter. To install the ME480FSM library on your machine's Arduino installation, download this file, rename it to ME480FSM.zip, and then add this Library to your Arduino installation using the instructions above.

Once you have done this, you should find the TimerCounter Library in the list of libraries under "Sketch$\rightarrow$Include Library." Click this library in the menu to automatically add the appropriate #include <ME480FSM.h> statement to the top of any sketch in which you wish to use the ME480FSM library.

Reading a Library's Documentation

Whenever we come across a library that we think might be helpful for us as we design and implement a controller, we have to learn to use its particular classes and functions! While it's always nice when we can consult an "expert" who knows everything there is to know about a library, it's much more common that we'll find libraries on our own that we need to teach ourselves about.

Luckily, libraries are usually accompanied by documentation that is designed so that programmers can teach themselves how to use the library's functionality. For ME480, the libraries you'll be using are all accompanied by web-based documentation. As an example, see the ME480FSM library's documentation here.

Looking at the landing page for the library, we see an overview of what the library is for, along with a navigation bar that lets us explore the library's details. See the screenshot below.

image.png

At the top, you will see a button called "Classes," which explains what classes (objects) the library provides for your use. Digging in, let's look at the page for the FSMTimer class. On that page, you will see a list of all of the methods (functions) in that class, as shown below.

image-3.png

The page for each class usually contains a description of what it is used for, along with a list (with variable types) of all member functions and attributes (properties) that can be accessed if you declare an instance of this class. Essentially, these pages should tell you everything you need to know in order to use the library in your own code.

The methodology we used to explore the ME480FSM library will work for any Arduino library!

In [ ]: