Chapter 16. Using, Modifying, and Creating Libraries

16.0. Introduction

Libraries add functionality to the Arduino environment. They extend the commands available to provide capabilities not available in the core Arduino language. Libraries provide a way to add features that can be accessed from any of your sketches once you have installed the library.

The Arduino software distribution includes built-in libraries that cover common tasks. These libraries are discussed in Recipe 16.1.

Libraries are also a good way for people to share code that may be useful to others. Many third-party libraries provide specialized capabilities; these can be downloaded from the Arduino Playground and other sites. Libraries are often written to simplify the use of a particular piece of hardware. Many of the devices covered in earlier chapters use libraries to make it easier to connect to the devices.

Libraries can also provide a friendly wrapper around complex code to make it easier to use. An example is the Wire library distributed with Arduino, which hides much of the complexity of low-level hardware communications (see Chapter 13).

This chapter explains how to use and modify libraries. It also gives examples of how to create your own libraries.

16.1. Using the Built-in Libraries

Problem

You want to use the libraries provided with the Arduino distribution in your sketch.

Solution

This recipe shows you how to use Arduino library functionality in your sketch.

To see the list of available libraries from the IDE menu, click SketchImport Library. A list will drop down showing all the available libraries. The first dozen or so are the libraries distributed with Arduino. A horizontal line separates that list from the libraries that you download and install yourself.

Clicking on a library will add that library to the current sketch, by adding the following line to the top of the sketch:

#include <nameOfTheLibrarySelected.h>

This results in the functions within the library becoming available to use in your sketch.

Note

The Arduino IDE updates its list of available libraries only when the IDE is first started on your computer. If you install a library after the IDE is running, you need to close the IDE and restart for that new library to be recognized.

The Arduino libraries are documented in the reference at http://arduino.cc/en/Reference/Libraries and each library includes example sketches demonstrating their use. Chapter 1 has details on how to navigate to the examples in the IDE.

The libraries that are included with Arduino as of version 0022 are:

EEPROM

Used to store and read information in memory that is preserved when power is removed; see Chapter 18

Ethernet

Used to communicate with the Arduino Ethernet shield; see Chapter 15

Firmata

A protocol used to simplify serial communication and control of the board

LiquidCrystal

For controlling compatible LCD displays; see Chapter 11

Matrix

Helps manage a matrix of LEDs; see Chapter 7

SD

Supports reading and writing files to an SD card using external hardware

Servo

Used to control servo motors; see Chapter 8

SoftwareSerial

Enables additional serial ports

SPI

Used for Ethernet and SPI hardware; see Chapter 13

Sprite

Enables the use of sprites with an LED matrix

Stepper

For working with stepper motors; see Chapter 8

Wire

Works with I2C devices attached to the Arduino; see Chapter 13

Discussion

Libraries that work with specific hardware within the Arduino controller chip only work on predefined pins. The Wire and SPI libraries are examples of this kind of library. Libraries that allow user selection of pins usually have this specified in setup; Servo, LiquidCrystal, and Stepper are examples of this kind of library. See the library documentation for specific information on how to configure the library.

Including a library adds the library code to your sketch behind the scenes. This means the size of your sketch, as reported at the end of the compilation process, will increase, but the Arduino build process is smart enough to only include the code your sketch is actually using from the library, so you don’t have to worry about the memory overhead for methods that are not being used. Therefore, you also don’t have to worry about unused functions reducing the amount of code you can put into your sketch.

See Also

The Arduino reference for libraries: http://arduino.cc/en/Reference/Libraries

16.2. Installing Third-Party Libraries

Problem

You want to use a library created for use with Arduino but not in the standard distribution.

Solution

Download the library. It will often be a .zip file. Unzip it and you will have a folder that has the same title as the name of the library. This folder needs to be put inside a folder called libraries inside your Arduino document folder. To find the Arduino document folder, launch Arduino and choose Sketch→Show Sketch Folder. Go up to the parent directory of the folder that appears, and you’ll be in the Arduino document folder. If no libraries folder exists, create one and put the library inside it.

If the Arduino IDE is running, quit the program and restart it. The IDE scans this folder to find libraries only when it is launched. If you now go to the menu SketchImport Library, at the bottom, below the gray line and the word Contributed, you should see the library you have added.

If the libraries provide example sketches, you can view these from the IDE menu; click FileExamples, and the libraries examples will be under the libraries name in a section between the general examples and the Arduino distributed library example listing.

Discussion

A large number of libraries are provided by third parties. Many are of very high quality, are actively maintained, and provide good documentation and example sketches. The Arduino Playground is a good place to look for libraries: http://www.arduino.cc/playground/.

Look for libraries that have clear documentation and examples. Check out the Arduino forums to see if there are any threads (discussion topics) that discuss the library. Libraries that were designed to be used with early Arduino releases may have problems when used with the latest Arduino version, so you may need to read through a lot of material (some threads for popular libraries contain hundreds of posts) to find information on using an older library with the latest Arduino release.

If the library examples do not appear in the Examples menu or you get a message saying “Library not found” when you try to use the library, check that the libraries folder is in the correct place with the name spelled correctly. A library folder named <LibraryName> (where <LibraryName> is the name for the library) must contain a file named <LibraryName>.h with the same spelling and capitalization. Check that additional files needed by the library are in the folder.

16.3. Modifying a Library

Problem

You want to change the behavior of an existing library, perhaps to extend its capability. For example, the TimeAlarms library in Chapter 12 only supports six alarms and you need more (see Recipe 12.5).

Solution

The Time and TimeAlarms libraries are described in Chapter 12, so refer to Recipe 12.5 to familiarize yourself with the standard functionality. The libraries can be downloaded from the website for this book, or from http://www.arduino.cc/playground/uploads/Code/Time.zip (this download includes both libraries).

Once you have the Time and TimeAlarms libraries installed, compile and upload the following sketch, which will attempt to create seven alarms—one more than the libraries support. Each Alarm task simply prints its task number:

/*
multiple_alarms sketch
 has more timer repeats than the library supports out of the box -
 you will need to edit the header file to enable more than 6 alarms
 */

#include <Time.h>
#include <TimeAlarms.h>

int currentSeconds = 0;

void setup()
{
  Serial.begin(9600);

  // create 7 alarm tasks
  Alarm.timerRepeat(1, repeatTask1);
  Alarm.timerRepeat(2, repeatTask2);
  Alarm.timerRepeat(3, repeatTask3);
  Alarm.timerRepeat(4, repeatTask4);
  Alarm.timerRepeat(5, repeatTask5);
  Alarm.timerRepeat(6, repeatTask6);
  Alarm.timerRepeat(7, repeatTask7);  //7th timer repeat
}


void repeatTask1()
{
  Serial.print("task 1  ");
}

void repeatTask2()
{
  Serial.print("task 2  ");
}
void repeatTask3()
{
  Serial.print("task 3  ");
}

void repeatTask4()
{
  Serial.print("task 4  ");
}

void repeatTask5()
{
  Serial.print("task 5  ");
}

void repeatTask6()
{
  Serial.print("task 6  ");
}

void repeatTask7()
{
  Serial.print("task 7  ");
}

void  loop()
{
  if(second() != currentSeconds)
  {
   // print the time for each new second
   // the task numbers will be printed when the alarm for that task is triggered
    Serial.println();
    Serial.print(second());
    Serial.print("->");
    currentSeconds = second();
    Alarm.delay(1); //Alarm.delay must be called to service the alarms
  }
}

Open the Serial Monitor and watch the output being printed. After nine seconds of output, you should see this:

1->task 1
2->task 1  task 2
3->task 1  task 3
4->task 1  task 2  task 4
5->task 1  task 5
6->task 1  task 2  task 3  task 6
7->task 1
8->task 1  task 2  task 4
9->task 1  task 3

The task scheduled for seven seconds did not trigger because the library only provides six timer “objects” that you can use.

You can increase this by modifying the library. Go to the libraries folder in your Arduino Documents folder.

Note

You can locate the directory containing the sketchbook folder by clicking on the menu item FilePreferences in the IDE. A dialog box will open, showing the sketchbook location.

If you have installed the Time and TimeAlarms libraries (both libraries are in the file you downloaded), navigate to the LibrariesTimeAlarms folder. Open the TimeAlarms.h header file (for more details about header files, see Recipe 16.4). You can edit the file with any text editor; for example, Notepad on Windows or TextEdit on a Mac.

You should see the following at the top of the TimeAlarms.h file:

#ifndef TimeAlarms_h
#define TimeAlarms_h

#include <inttypes.h>
#include "Time.h"
#define dtNBR_ALARMS 6

The maximum number of alarms is specified by the value defined for dtNbr_ALARMS.

Change:

   #define dtNBR_ALARMS 6

to:

  #define dtNMBR_ALARMS 7

and save the file.

Upload the sketch to your Arduino again, and this time the serial output should read:

1->task 1
2->task 1  task 2
3->task 1  task 3
4->task 1  task 2  task 4
5->task 1  task 5
6->task 1  task 2  task 3  task 6
7->task 1  task 7
8->task 1  task 2  task 4
9->task 1  task 3

You can see that task 7 now activates after seven seconds.

Discussion

Capabilities offered by a library are a trade-off between the resources used by the library and the resources available to the rest of your sketch, and it is often possible to change these capabilities if required. For example, you may need to decrease the amount of memory used for a serial library so that other code in the sketch has more RAM. Or you may need to increase the memory usage by a library for your application. The library writer generally creates the library to meet typical scenarios, but if your application needs capabilities not catered to by the library writer, you may be able to modify the library to accommodate them.

In this example, the TimeAlarms library allocates room (in RAM) for six alarms. Each of these consumes around a dozen bytes and the space is reserved even if only a few are used. The number of alarms is set in the library header file (the header is a file named TimeAlarms.h in the TimeAlarms folder). Here are the first few lines of TimeAlarms.h:

#ifndef TimeAlarms_h
#define TimeAlarms_h

#include <inttypes.h>

#include "Time.h"

#define dtNBR_ALARMS 6

In the TimeAlarms library, the maximum number of alarms is set using a #define statement. Because you changed it and saved the header file when you recompiled the sketch to upload it, it uses the new upper limit.

Sometimes constants are used to define characteristics such as the clock speed of the board, and when used with a board that runs at a different speed, you will get unexpected results. Editing this value in the header file to the correct one for the board you are using will fix this problem.

If you edit the header file and the library stops working, you can always download the library again and replace the whole library to return to the original state.

See Also

Recipe 16.4 has more details on how you can add functionality to libraries.

16.4. Creating Your Own Library

Problem

You want to create your own library. Libraries are a convenient way to reuse code across multiple sketches and are a good way to share with other users.

Solution

A library is a collection of methods and variables that are combined in a format that enables users to access functions and variables in a standardized way.

Most Arduino libraries are written as a class. If you are familiar with C++ or Java, you will be familiar with classes. However, you can create a library without using a class, and this recipe shows you how.

This recipe explains how you can transform the sketch from Recipe 7.1 to move the BlinkLED function into a library.

See Recipe 7.1 for the wiring diagram and an explanation of the circuit. Here is the sketch that will be the starting point for the library:

/*
 * blinkLibTest
 */

const int firstLedPin  = 3;        // choose the pin for each of the LEDs
const int secondLedPin = 5;
const int thirdLedPin  = 6;

void setup()
{
  pinMode(firstLedPin, OUTPUT);     // declare LED pins as output
  pinMode(secondLedPin, OUTPUT);    // declare LED pins as output
  pinMode(thirdLedPin, OUTPUT);     // declare LED pins as output
}

void loop()
{
  // flash each of the LEDs for 1000 milliseconds (1 second)
  blinkLED(firstLedPin, 1000);
  blinkLED(secondLedPin, 1000);
  blinkLED(thirdLedPin, 1000);
}

The blinkLED function from Recipe 7.1 should be removed from the sketch and moved into a separate file named blinkLED.cpp (see the Discussion for more details about .cpp files):

/* blinkLED.cpp
 * simple library to light an LED for a duration given in milliseconds
 */
#include <WProgram.h> // Arduino includes
#include "blinkLED.h"

// blink the LED on the given pin for the duration in milliseconds
void blinkLED(int pin, int duration)
{
  digitalWrite(pin, HIGH);     // turn LED on
  delay(duration);
  digitalWrite(pin, LOW);      // turn LED off
  delay(duration);
}

Create the blinkLED.h header file as follows:

/*
 * blinkLED.h
 * Library header file for BlinkLED library
 */

void blinkLED(int pin, int duration);  // function prototype

Discussion

The library will be named blinkLED and will be located in the libraries folder (see Recipe 16.2).

The blinkLED function from Recipe 7.1 is moved out of the sketch and into a library file named blinkLED.cpp (the .cpp extension stands for “C plus plus” and contains the executable code).

Note

The terms functions and methods are used in Arduino library documentation to refer to blocks of code such as blinkLED. The term method was introduced to refer to the functional blocks in a class. Both terms refer to the named functional blocks that are made accessible by a library.

The blinkLED.cpp file contains a blinkLED function that is identical to the code from Recipe 7.1 with the following two lines added at the top:

#include <WProgram.h> // Arduino includes
#include "blinkLED.h"

The #include <WProgram.h> line is needed by a library that uses any Arduino functions or constants. Without this, the compiler will report errors for all the Arduino functions used in your sketch.

The next line, #include "blinkLED.h", contains the function definitions (also known as prototypes) for your library. The Arduino build process creates prototypes for all the functions within a sketch automatically when a sketch is compiled—but it does not create any prototypes for library code, so if you make a library, you must create a header with these prototypes. It is this header file that is added to a sketch when you import a library from the IDE (see Recipe 16.1).

Note

Every library must have a file that declares the names of the functions to be exposed. This file is called a header file (also known as an include file) and has the form <LibraryName>.h (where <LibraryName> is the name for your library). In this example, the header file is named blinkLED.h and is in the same folder as blinkLED.cpp.

The header file for this library is simple. It declares the one function:

void blinkLED(int pin, int duration);  // function prototype

This looks similar to the function definition in the blinkLED.cpp file:

void blinkLED(int pin, int duration)

The difference is subtle but vital. The header file prototype contains a trailing semicolon. This tells the compiler that this is just a declaration of the form for the function but not the code. The source file, blinkLED.cpp, does not contain the trailing semicolon and this informs the compiler that this is the actual source code for the function.

Note

Libraries can have more than one header file and more than one implementation file. But there must be at least one header and that must match the library name. It is this file that is included at the top of the sketch when you import a library.

A good book on C++ can provide more details on using header and .cpp files to create code modules. This recipe’s See Also section lists some popular choices.

With the blinkLED.cpp and blinkLED.h files in the correct place within the libraries folder, close the IDE and reopen it.

Note

The Arduino IDE updates its list of available libraries only when the IDE is first started on your computer. If you create a library after the IDE is running, you need to close the IDE and restart for that library to be recognized.

Upload the blinkLibTest sketch and you should see the three LEDs blinking.

It’s easy to add additional functionality to the library. For example, you can add some constant values for common delays so that users of your libraries can use the descriptive constants instead of millisecond values.

Add the three lines with constant values to your header file as follows:

// constants for duration
const int BLINK_SHORT = 250;
const int BLINK_MEDIUM = 500;
const int BLINK_LONG = 1000;

void blinkLED(int pin, int duration);  // function prototype

Change the code in loop as follows and upload the sketch to see the different blink rates:

void loop()
{
  blinkLED(firstLedPin, BLINK_SHORT);
  blinkLED(secondLedPin, BLINK_MEDIUM);
  blinkLED(thirdLedPin, BLINK_LONG);
}

Note

You need to close and restart the IDE when you first add the library to the libraries folder, but not after subsequent changes to the library. Libraries included in Arduino release 0017 and later are recompiled each time the sketch is compiled. Arduino releases earlier than 0017 required the deletion of the library object files to make the library recompile and for changes to be included.

New functions can be easily added. This example adds a function that continues blinking for the number of times given by the sketch. Here is the loop code:

void loop()
{
  blinkLED(firstLedPin,BLINK_SHORT,5 );     // blink 5 times
  blinkLED(secondLedPin,BLINK_MEDIUM,3 );   // blink 3 times
  blinkLED(thirdLedPin, BLINK_LONG);        // blink once
}

To add this functionality to the library, add the prototype to blinkLED.h as follows:

/*
 * BlinkLED.h
 * Header file for BlinkLED library
 */

const int BLINK_SHORT = 250;
const int BLINK_MEDIUM = 500;
const int BLINK_LONG = 1000;

void blinkLED(int pin, int duration);
// new function for repeat count
void blinkLED(int pin, int duration, int repeats);

Add the function into blinkLED.cpp:

/*
 * BlinkLED.cpp
 */

#include <WProgram.h> // Arduino includes
#include "BlinkLED.h"

// blink the LED on the given pin for the duration in milliseconds
void blinkLED(int pin, int duration)
{
  digitalWrite(pin, HIGH);     // turn LED on
  delay(duration);
  digitalWrite(pin, LOW);      // turn LED off
  delay(duration);
}

/* function to repeat blinking
void blinkLED(int pin, int duration, int repeats)
{
  while(repeats)
  {
     blinkLED(pin, duration);
     repeats = repeats -1;
  }
}

You can create a keywords.txt file if you want to add syntax highlighting (coloring the keywords used in your library when viewing a sketch in the IDE). This is a text file that contains the name of the keyword and the keyword type—each type uses a different color. The keyword and type must be separated by a tab (not a space). For example, save the following file as keywords.txt in the blinkLED folder:

#######################################
# Methods and Functions (KEYWORD2)
#######################################
blinkLED  KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
BLINK_SHORT    LITERAL1
BLINK_MEDIUM   LITERAL1
BLINK_LONG     LITERAL1

See Also

“Writing a Library for Arduino” reference document: http://www.arduino.cc/en/Hacking/LibraryTutorial

Also see the following books on C++:

  • Practical C++ Programming by Steve Oualline (O’Reilly)

  • C++ Primer Plus by Stephen Prata (Sams)

  • C++ Primer by Stanley B. Lippman, Josée Lajoie, and Barbara E. Moo (Addison-Wesley Professional)

16.5. Creating a Library That Uses Other Libraries

Problem

You want to create a library that uses functionality from one or more existing libraries. For example, you want to create a library to aid debugging that sends print output to a second Arduino board using the Wire library.

Solution

This recipe uses the Wire library discussed in Chapter 13 and the core Arduino print functionality to create a new library that sends printed output to another Arduino board connected using I2C. The connections and code are covered in Recipe 13.9. This recipe describes how that code can be converted into a library.

Create a folder named i2cDebug in the libraries directory (see Recipe 16.4 for details on the file structure for a library). Create a file named i2cDebug.h with the following code:

/*
 * i2cDebug.h
 */

 #include <WProgram.h>
 #include <Print.h>   // the Arduino print class

class I2CDebugClass : public Print
{
  private:
    int I2CAddress;
    byte count;
    void write(byte c);
  public:
    I2CDebugClass();
    boolean begin(int id);
};

extern I2CDebugClass i2cDebug;   // the i2c debug object

Create a file named i2cDebug.cpp in the i2cDebug folder as follows:

/*
 * i2cDebug.cpp
 */

 #include <i2cDebug.h>

 #include <Wire.h>    // the Arduino I2C library


 I2CDebugClass::I2CDebugClass()
 {
 }

 boolean I2CDebugClass::begin(int id)
 {
    I2CAddress = id;  // save the slave's address
    Wire.begin(); // join I2C bus (address optional for master)
    return true;
 }

  void I2CDebugClass::write(byte c)
  {
     if( count == 0)
     {
      // here if the first char in the transmission
       Wire.beginTransmission(I2CAddress); // transmit to device
     }
     Wire.send(c);
     //  if the I2C buffer is full or an end of line is reached, send the data
     // BUFFER_LENGTH is defined in the Wire library
     if(++count >= BUFFER_LENGTH || c == '
') 
     {
         // send data if buffer full or newline character
         Wire.endTransmission();
         count = 0;
     }
  }

I2CDebugClass i2cDebug;   // Create an I2C debug object

Load this example sketch into the IDE:

/*
 * i2cDebug
 * example sketch for i2cDebug library
 */

#include <Wire.h>    // the Arduino I2C library
#include <i2cDebug.h>

const int address = 4;    //the address to be used by the communicating devices
const int sensorPin = 0;  // select the analog input pin for the sensor
int val;                  // variable to store the sensor value


void setup()
{
   Serial.begin(9600);
   i2cDebug.begin(address);
}

void loop()
{
   // read the voltage on the pot(val ranges from 0 to 1023)
   val = analogRead(sensorPin);        
   Serial.println(val);
   i2cDebug.println(val);
}

Remember that you need to restart the IDE after creating the library folder. See Recipe 16.4 for more detail on creating a library.

Upload the slave I2C sketch onto another Arduino board as described in Recipe 13.9, and you should see the output from the Arduino board running your library displayed on the second board.

Discussion

To include another library, use its include statement in your code as you would in a sketch. It is sensible to include information about any additional libraries that your library needs in documentation if you make it available for others to use, especially if it requires a library that is not distributed with Arduino.

This recipe provides an example of how to use a class when creating a library. A class is a programming technique for grouping functions and variables together. The following references provide an introduction to classes:

  • Programming Interactivity by Joshua Noble (O’Reilly)

  • C++ Primer by Stanley B. Lippman, Josée Lajoie, and Barbara E. Moo (Addison-Wesley Professional)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.97.219