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 Library Manager but also from GitHub. 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.
You want to use the libraries provided with the Arduino distribution in your sketch.
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 Sketch→Include 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 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.
The Arduino IDE updates its list of available libraries only when the IDE is first started on your computer. If you manually install a library after the IDE is running, you need to close the IDE and restart for that new library to be recognized. If you install a library through the Library Manager, you won’t need to restart the IDE.
The Arduino libraries are documented in the Arduino Reference 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 1.8.10 are:
Supports Adafruit’s Circuit Playground board, which includes many sensors and outputs for quick and easy prototyping.
This is used by the now-discontinued Arduino Yun, Yun Shield, and TRE. It allows communication between the Linux system and microcontroller on those boards.
This is used by the now-discontinued Arduino Esplora board, an all-in-one board that includes sensors, joystick, LEDs, buzzers, and other inputs and outputs.
Used to store and read information in memory that is preserved when power is removed; see Chapter 18.
Used to communicate with the Arduino Ethernet shield, compatible modules such as the Adafruit Ethernet FeatherWing, or for use with the Arduino Ethernet board; see Chapter 15.
A protocol used to simplify serial communication and control of the board.
Supports the now-discontinued Arduino GSM shield, which connects Arduino to cellular data networks.
Allows certain boards, such as the Arduino Leonardo and SAMD-based boards, to function as a mouse or keyboard. You won’t use this library directly, but the Keyboard and Mouse libraries depend on it.
Allows certain boards, such as the Arduino Leonardo and SAMD-based boards, to function as a USB keyboard.
For controlling compatible LCD displays; see Chapter 11.
Allows certain boards, such as the Arduino Leonardo and SAMD-based boards, to function as a USB mouse.
Supports operation of the now-discontinued Arduino Robot’s control board.
Supports operation of the now-discontinued Arduino Robot’s infrared remote.
Supports operation of the now-discontinued Arduino Robot’s motor board.
Supports reading and writing files to an SD card using external hardware.
Used to control servo motors; see Chapter 8.
Enables additional serial ports.
This is used by the now-discontinued Arduino Yun to enable WebSocket-based communications.
Used for Ethernet and SPI hardware; see Chapter 13.
For working with stepper motors; see Chapter 8.
Connects Arduino to Temboo, a platform for connecting to APIs, databases, and code utilities.
A library to support the now-discontinued Arduino LCD screen. Third-party boards and modules are available along with custom libraries.
Supports the now-discontinued Arduino WiFi shield, which has been replaced by Arduino boards with built-in WiFi as well as third-party boards and modules.
Works with I2C devices attached to the Arduino; see Chapter 13.
The following two libraries can be found in releases prior to Arduino 1.0 but are no longer included with the Arduino distribution:
Helps manage a matrix of LEDs; see Chapter 7.
Enables the use of sprites with an LED matrix.
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 that 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.
Libraries included with Arduino (and many contributed libraries) include example sketches that show how to use the library. They are accessed from the File→Examples menu.
First, check the Library Manager to see if the library is available. Select Tools →Manage Libraries and search for the library you are looking for (or for the name of a component; you can often find libraries that are designed for specific components). Hover over its entry in the list of results, and click Install (see Figure 16-1). It will be ready to use right away.
If the library is not available in the Library Manager, you will need to download the library. If the library is available on GitHub, check the README carefully. In most cases, you should be able to download it from the Releases tab just above the list of files or click the Clone or Download button and choose Download Zip. If the library is made available some other way, 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, open Preferences (Arduino→Preferences on macOS; File→Preferences on Windows or Linux) and note the sketchbook location. Navigate to that directory in a filesystem browser (such as Windows Explorer or macOS Finder) or at the terminal. If no libraries folder exists, create one and put the folder you unzipped inside it.
If the Arduino IDE is still running, quit and restart it. The IDE scans this folder to find libraries only when it is launched. If you now go to the menu Sketch→Import 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 File→Examples, and the libraries examples will be under the libraries name in a section between the general examples and the Arduino distributed library example listing.
A large number of libraries are provided by third parties. Many are very high quality, are actively maintained, and provide good documentation and example sketches. Arduino Libraries has a great, regularly updated list of available libraries. The Arduino Playground, although no longer accepting updates, is also a good place to look for libraries.
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.
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).
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 installed using the Library Manager. If you have trouble finding the Time library, try searching the Library Manager for “timekeeping.”
Once you have the Time and TimeAlarms libraries installed, compile and upload the following sketch on an AVR-based board, which will attempt to create seven alarms—one more than the libraries support (on AVR that is; ARM and ESP8266 boards support more). 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 <TimeLib.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
.
(
"task 1 "
);
}
void
repeatTask2
()
{
Serial
.
(
"task 2 "
);
}
void
repeatTask3
()
{
Serial
.
(
"task 3 "
);
}
void
repeatTask4
()
{
Serial
.
(
"task 4 "
);
}
void
repeatTask5
()
{
Serial
.
(
"task 5 "
);
}
void
repeatTask6
()
{
Serial
.
(
"task 6 "
);
}
void
repeatTask7
()
{
Serial
.
(
"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
.
(
second
());
Serial
.
(
"->"
);
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.
You can locate the directory containing the sketchbook folder by clicking the menu item File→Preferences (on Windows or Linux) or Arduino→Preferences (on macOS) 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 Libraries\TimeAlarms 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 <Arduino.h>
#include "TimeLib.h"
#if defined(__AVR__)
#define dtNBR_ALARMS 6
// max is 255
#else
#define dtNBR_ALARMS 12
// assume non-AVR has more memory
#endif
The maximum number of alarms is specified by the value defined for dtNbr_ALARMS
.
Change:
#define dtNBR_ALARMS 6
to:
#define dtNBR_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.
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).
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 define statements (or 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.
Recipe 16.4 has more details on how you can add functionality to libraries.
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. The library will contain the blinkLED
function from that recipe. Here is the sketch that will be used to test the library:
/*
* blinkLibTest
*/
#include "blinkLED.h"
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 1,000 ms (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 "Arduino.h"
// use: Wprogram.h for Arduino versions prior to 1.0
#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
);
}
Most library authors are programmers who use their favorite programming editor, but you can use any plain-text editor to create these files.
Create the blinkLED.h header file as follows:
/*
* blinkLED.h
* Library header file for BlinkLED library
*/
#include "Arduino.h"
void
blinkLED
(
int
pin
,
int
duration
);
// function prototype
The library will be named “blinkLED” and will be located in the libraries folder (see Recipe 16.2); create a subdirectory named blinkLED in the libraries folder and move blinkLED.h and blinkLED.cpp into it. Next, create a subdirectory of that folder called examples and a subdirectory under that folder called blinkLibTest. Next, put the contents of the sketch shown earlier into a file named examples/blinkLibTest/blinkLibTest.ino.
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).
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 "Arduino.h"
// Arduino include
#include "blinkLED.h"
The #include "Arduino.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.
Arduino.h was added in Release 1.0 and replaces WProgram.h. If you are compiling sketches using earlier releases, you can use the following conditional include to bring in the correct version:
#if ARDUINO >= 100
#include "Arduino.h
// for 1.0 and later
#else
#include "WProgram.h"
// for earlier releases
#endif
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).
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.
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, blinkLED.h, and blinkLibTest.ino files in the correct place within the libraries folder, close the IDE and reopen it. The directory structure should look like this:
libraries/ └── blinkLED/ ├── blinkLED.cpp ├── blinkLED.h └── examples/ └── blinkLibTest/ └── blinkLibTest.ino
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. Although you need to close and restart the IDE when you first add the library to the libraries folder, you do not need to do so after subsequent changes to the library.
Click File→Examples (Examples from Custom Libraries)→blinkLED→blinkLibTest to open the example sketch. 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, traditionally put just before the function prototype, 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
)
;
}
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 */
#
include "Arduino.h"
// constants for duration
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 * simple library to light an LED for a duration given in milliseconds */
#
include "Arduino.h"
#
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 (you’ll need to quit and restart the IDE when you add or modify a keywords.txt file):
####################################### # Methods and Functions (KEYWORD2) ####################################### blinkLED KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### BLINK_SHORT LITERAL1 BLINK_MEDIUM LITERAL1 BLINK_LONG LITERAL1
See Recipe 16.5 for more examples of writing a library.
This “Writing a Library for Arduino” reference document
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)
This recipe uses the functions described in Recipe 13.6 to communicate with a Wii nunchuck using the Wire library.
Create a folder named Nunchuck in the libraries directory (see Recipe 16.4 for details on the file structure for a library). Create a file named Nunchuck.h with the following code:
/*
* Nunchuck.h
* Arduino library to interface with wii Nunchuck
*/
#ifndef Nunchuck_included
#define Nunchuck_included
// identities for each field provided by the wii nunchuck
enum
nunchuckItems
{
wii_joyX
,
wii_joyY
,
wii_accelX
,
wii_accelY
,
wii_accelZ
,
wii_btnC
,
wii_btnZ
,
wii_ItemCount
};
// uses pins adjacent to I2C pins as power & ground for Nunchuck
void
nunchuckSetPowerpins
();
// initialize the I2C interface for the nunchuck
void
nunchuckInit
();
// Request data from the nunchuck
void
nunchuckRequest
();
// Receive data back from the nunchuck,
// returns true if read successful, else false
bool
nunchuckRead
();
// Encode data to format that most wiimote drivers accept
char
nunchuckDecode
(
uint8_t
x
);
// return the value for the given item
int
nunchuckGetValue
(
int
item
);
#endif
Create a file named Nunchuck.cpp in the Nunchuck folder as follows:
/*
* Nunchuck.cpp
* Arduino library to interface with wii Nunchuck
*/
#include "Arduino.h"
// Arduino defines
#include "Wire.h"
// Wire (I2C) defines
#include "Nunchuck.h"
// Defines for this library
// Constants for Uno board (use 19 and 18 for mega)
const
int
vccPin
=
A3
;
// +v and gnd provided through these pins
const
int
gndPin
=
A2
;
const
int
dataLength
=
6
;
// number of bytes to request
static
byte
rawData
[
dataLength
];
// array to store nunchuck data
// uses pins adjacent to I2C pins as power & ground for Nunchuck
void
nunchuckSetPowerpins
()
{
pinMode
(
gndPin
,
OUTPUT
);
// set power pins to the correct state
pinMode
(
vccPin
,
OUTPUT
);
digitalWrite
(
gndPin
,
LOW
);
digitalWrite
(
vccPin
,
HIGH
);
delay
(
100
);
// wait for power to stabilize
}
// initialize the I2C interface for the nunchuck
void
nunchuckInit
()
{
Wire
.
begin
();
// join i2c bus as master
Wire
.
beginTransmission
(
0x52
);
// transmit to device 0x52
Wire
.
write
((
byte
)
0x40
);
// sends memory address
Wire
.
write
((
byte
)
0x00
);
// sends sent a zero.
Wire
.
endTransmission
();
// stop transmitting
}
// Request data from the nunchuck
void
nunchuckRequest
()
{
Wire
.
beginTransmission
(
0x52
);
// transmit to device 0x52
Wire
.
write
((
byte
)
0x00
);
// sends one byte
Wire
.
endTransmission
();
// stop transmitting
}
// Receive data back from the nunchuck,
// returns true if read successful, else false
bool
nunchuckRead
()
{
byte
cnt
=
0
;
Wire
.
requestFrom
(
0x52
,
dataLength
);
// request data from nunchuck
while
(
Wire
.
available
())
{
byte
x
=
Wire
.
read
();
rawData
[
cnt
]
=
nunchuckDecode
(
x
);
cnt
++
;
}
nunchuckRequest
();
// send request for next data payload
if
(
cnt
>=
dataLength
)
return
true
;
// success if all 6 bytes received
else
return
false
;
// failure
}
// Encode data to format that most wiimote drivers accept
char
nunchuckDecode
(
byte
x
)
{
return
(
x
^
0x17
)
+
0x17
;
}
// return the value for the given item
int
nunchuckGetValue
(
int
item
)
{
if
(
item
<=
wii_accelZ
)
return
(
int
)
rawData
[
item
];
else
if
(
item
==
wii_btnZ
)
return
bitRead
(
rawData
[
5
],
0
)
?
0
:
1
;
else
if
(
item
==
wii_btnC
)
return
bitRead
(
rawData
[
5
],
1
)
?
0
:
1
;
}
Connect the nunchuck as shown in Recipe 13.6 but use the following sketch to test the library (if Arduino was running while you created the previous two files, quit and restart it so it will see the new library). If you’d like, you can create this as the file WiichuckSerial.ino in the folder examples/WiichuckSerial underneath the Nunchuck library folder to make it available as an example program:
/*
* WiichuckSerial
*
* Uses Nunchuck library to send sensor values to serial port
*/
#include <Wire.h>
#include "Nunchuck.h"
void
setup
()
{
Serial
.
begin
(
9600
);
nunchuckSetPowerpins
();
nunchuckInit
();
// send the initialization handshake
nunchuckRead
();
// ignore the first time
delay
(
50
);
}
void
loop
()
{
nunchuckRead
();
Serial
.
(
"H,"
);
// header
for
(
int
i
=
0
;
i
<
5
;
i
++
)
// print values of accelerometers and buttons
{
Serial
.
(
nunchuckGetValue
(
wii_accelX
+
i
),
DEC
);
Serial
.
write
(
','
);
}
Serial
.
println
();
delay
(
20
);
// the time in milliseconds between sends
}
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.
The major difference between the library code and the sketch from Recipe 13.6 is the addition of the Nunchuck.h header file that contains the function prototypes (Arduino sketch code silently creates prototypes for you, unlike Arduino libraries, which require explicit prototypes).
Here is another example of creating a library; this one uses a C++ class to encapsulate the library functions. A class is a programming technique for grouping functions and variables together and is commonly used for most Arduino libraries.
This library can be used as a debugging aid by sending print output to a second Arduino board using the Wire library. This is particularly useful when the hardware serial port is not available and software serial solutions are not appropriate due to the timing delays they introduce. Here the core Arduino print functionality is used to create a new library that sends printed output to I2C. The connections and code are covered in Recipe 13.5. The following description shows 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
*/
#ifndef i2cDebug_included
#define i2cDebug_included
#include <Arduino.h>
#include <Print.h>
// the Arduino print class
class
i2cDebugClass
:
public
{
private
:
int
i2cAddress
;
byte
count
;
size_t
write
(
byte
c
);
public
:
i2cDebugClass
();
bool
begin
(
int
id
);
};
extern
i2cDebugClass
i2cDebug
;
// the i2c debug object
#endif
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
()
{
}
bool
i2cDebugClass
::
begin
(
int
id
)
{
i2cAddress
=
id
;
// save the slave's address
Wire
.
begin
();
// join I2C bus (address optional for master)
return
true
;
}
size_t
i2cDebugClass
::
write
(
byte
c
)
{
if
(
count
==
0
)
{
// here if the first char in the transmission
Wire
.
beginTransmission
(
i2cAddress
);
// transmit to device
}
Wire
.
write
(
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
==
'\n'
)
{
// send data if buffer full or newline character
Wire
.
endTransmission
();
count
=
0
;
}
return
1
;
// one character written
}
i2cDebugClass
i2cDebug
;
// Create an I2C debug object
The write
method returns size_t
, a value that enables the print function to return the number of characters printed. This is new in Arduino 1.0—earlier versions did not return a value from write
or print
. If you have a library that is based on Stream or Print, then you will need to change the return type to size_t
.
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 details on creating a library.
Upload the slave I2C sketch onto another Arduino board and wire up the boards as described in Recipe 13.5, and you should see the output from the Arduino board running your library displayed on the second board.
The following references provide an introduction to classes if C++ classes are new to you:
Programming Interactivity by Joshua Noble (O’Reilly)
C++ Primer by Stanley B. Lippman, Josée Lajoie, and Barbara E. Moo (Addison-Wesley Professional)
Most libraries should only require the change of a few lines to work under Arduino 1.0. For example, any one or more of these header file includes:
#include "wiring.h"
#include "WProgram.h"
#include "WConstants.h"
#include "pins_arduino.h"
should be changed to a single include of:
#include "Arduino.h"
The filenames may be enclosed in either angle brackets or quotes.
Older libraries that don’t compile under Arduino 1.0 will usually generate one or more of these error messages:
source
file
:
error
:
wiring
.
h
:
No
such
file
or
directory
source
file
:
error
:
WProgram
.
h
:
No
such
file
or
directory
source
file
:
error
:
WConstants
.
h
:
No
such
file
or
directory
source
file
:
error
:
pins_arduino
.
h
:
No
such
file
or
directory
source file
is the full path of the library file that needs to be updated. There will be a list of other errors following this due to the indicated file not being found in the 1.0 release, but these should disappear after you have replaced the old header names with Arduino.h. The definitions in these files are now included in Arduino.h and the solution is to replace includes for all of the preceding files with a single include for Arduino.h.
If you want to run current versions of Arduino alongside earlier versions, you can use a conditional define (see Recipe 17.6):
#if ARDUINO >= 100
#include "Arduino.h"
#else
// These are the filenames that are used in the original version of library
#include "wiring.h"
#include "pins_arduino.h"
#endif
Third-party libraries that use serial, Ethernet, or other functionality that has changed syntax in Arduino 1.0 may require additional code changes.