Learning Goals 5 min
You've used dozens of Arduino libraries: Servo, Wire, Adafruit_SSD1306, ArduinoBLE, PubSubClient. Now you write your own. By the end of this lesson you will:
- Identify the four files of a polished Arduino library:
.h,.cpp,keywords.txt,library.properties. - Understand the folder layout for a library that installs cleanly in Arduino IDE.
- Write your first "hello library" that wraps the L03-42 Blinker class into a proper library.
Warm-Up 10 min
Open any installed library's folder (e.g. ~/Documents/Arduino/libraries/Servo/). You'll see:
Servo.h— the public interface (declarations).Servo.cpp(or src/Servo.cpp) — the implementations.keywords.txt— IDE syntax-highlighting hints.library.properties— name, version, dependencies, etc.examples/— sample sketches users can open from Examples menu.README.md— docs.
That's the canonical structure. Match it and your library looks professional.
New Concept · The four files 25 min
1. The header (Blinker.h)
Declares the class, includes guards, public API.
#ifndef BLINKER_H
#define BLINKER_H
#include <Arduino.h>
class Blinker {
public:
Blinker(int pin, unsigned long onMs, unsigned long offMs);
void begin();
void update();
void setRate(unsigned long onMs, unsigned long offMs);
void pause();
void resume();
bool isOn() const;
private:
int pin_;
unsigned long onMs_, offMs_;
unsigned long lastToggle_;
bool state_;
bool paused_;
};
#endifInclude guards (#ifndef ... #define ... #endif) prevent double-inclusion errors when the header gets pulled in twice.
2. The implementation (Blinker.cpp)
#include "Blinker.h"
Blinker::Blinker(int pin, unsigned long onMs, unsigned long offMs)
: pin_(pin), onMs_(onMs), offMs_(offMs),
lastToggle_(0), state_(false), paused_(false) {}
void Blinker::begin() {
pinMode(pin_, OUTPUT);
digitalWrite(pin_, LOW);
state_ = false;
lastToggle_ = millis();
}
void Blinker::update() {
if (paused_) return;
unsigned long now = millis();
unsigned long wait = state_ ? onMs_ : offMs_;
if (now - lastToggle_ >= wait) {
lastToggle_ = now;
state_ = !state_;
digitalWrite(pin_, state_ ? HIGH : LOW);
}
}
void Blinker::setRate(unsigned long onMs, unsigned long offMs) {
onMs_ = onMs; offMs_ = offMs;
}
void Blinker::pause() { paused_ = true; }
void Blinker::resume() { paused_ = false; lastToggle_ = millis(); }
bool Blinker::isOn() const { return state_; }3. keywords.txt
Tells the IDE which words to colour-highlight:
# tab-separated; ONE tab between columns Blinker KEYWORD1 begin KEYWORD2 update KEYWORD2 setRate KEYWORD2 pause KEYWORD2 resume KEYWORD2 isOn KEYWORD2
KEYWORD1 = class names (orange in IDE). KEYWORD2 = methods (brown). LITERAL1 = constants. Critical: tabs, not spaces. Spaces break parsing silently.
4. library.properties
name=Blinker version=1.0.0 author=Your Name <you@example.com> maintainer=Your Name <you@example.com> sentence=Non-blocking LED blinker for Arduino. paragraph=Wraps the millis() blink-without-delay pattern into a reusable class. Supports asymmetric on/off times, pause/resume, and live rate changes. category=Signal Input/Output url=https://github.com/yourname/Blinker architectures=* includes=Blinker.h
Required fields: name, version, author, maintainer, sentence, paragraph, category, url. The IDE shows this metadata when users hover the library in Library Manager.
Folder layout
Blinker/
├── library.properties
├── keywords.txt
├── README.md
├── src/
│ ├── Blinker.h
│ └── Blinker.cpp
└── examples/
└── HelloBlink/
└── HelloBlink.ino
The src/ folder is standard for newer libraries. Older libraries put the .h/.cpp in the root — still works.
Examples folder
A minimal examples/HelloBlink/HelloBlink.ino:
#include <Blinker.h>
Blinker led(LED_BUILTIN, 200, 1800);
void setup() {
led.begin();
}
void loop() {
led.update();
}Users find this under File → Examples → Blinker → HelloBlink after installing the library.
Worked Example · Wrap Blinker as a real library 25 min
Step 1 — make the folder
Create ~/Documents/Arduino/libraries/Blinker/.
Step 2 — write the four files
Use the templates from §3. library.properties, keywords.txt, src/Blinker.h, src/Blinker.cpp.
Step 3 — add an example
examples/HelloBlink/HelloBlink.ino as above.
Step 4 — write a README
# Blinker
Non-blocking LED blink for Arduino. Multiple LEDs at different rates from one sketch.
## Usage
\`\`\`cpp
#include <Blinker.h>
Blinker led(LED_BUILTIN, 200, 1800);
void setup() { led.begin(); }
void loop() { led.update(); }
\`\`\`
See \`examples/HelloBlink\` for more.
Step 5 — restart the IDE
Open Arduino IDE. Sketch → Include Library → Blinker. Now #include <Blinker.h> works in any sketch on your machine.
Step 6 — verify keyword highlighting
Open a sketch, type Blinker led(...). Blinker should colour orange. led.update() should colour brown. If not — check keywords.txt is tabs, not spaces.
Basic 5 min
Goal: Add a second example: examples/MultiBlink/MultiBlink.ino showing 3 LEDs at 3 rates.
Challenge 1 5 min
Goal: Convert another reusable class from your library (Motor, SmoothedSensor, Stepper helper, etc.) into a properly-structured library. Same folder layout.
Challenge 2 5 min
Goal: Add a Doxygen-style comment header to each public method. /// comments before each declaration. Generates polished IDE tooltips and HTML docs.
Challenge 3 · Fix the broken library 10 min
A classmate packaged an active-buzzer driver called Beeper. It will not compile, the example is missing from the menu, and no words are highlighted. There are four mistakes.
Beeper/
├── library.properties
├── keywords.txt
├── src/
│ ├── Beeper.h
│ └── Beeper.cpp
└── examples/
└── BeepDemo.ino
src/Beeper.h:
#ifndef BEEPER_H
#define BEEPER_H
class Beeper {
public:
Beeper(int pin);
void begin();
void beep(unsigned long ms);
private:
int pin_;
};
#endifsrc/Beeper.cpp:
#include "Beeper.h"
Beeper::Beeper(int pin) : pin_(pin) {}
void begin() {
pinMode(pin_, OUTPUT);
}
void Beeper::beep(unsigned long ms) {
digitalWrite(pin_, HIGH);
delay(ms);
digitalWrite(pin_, LOW);
}keywords.txt (the gaps were typed with the space bar):
Beeper KEYWORD1 begin KEYWORD2 beep KEYWORD2
Find each mistake, say what goes wrong, and fix it. Then install the library and run the example on a buzzer.
It works if File → Examples → Beeper → BeepDemo appears, compiles, and beeps the buzzer. Beeper should show in orange.
Recap 5 min
A library = .h + .cpp + keywords.txt + library.properties + examples + README. Standard folder layout. Drop in libraries/ and the IDE picks it up. Tomorrow we design the public API — what to expose, what to hide.
- Library
- A reusable bundle of Arduino code that can be included with
#include. - Header file (.h)
- Public declarations. Class definitions, function signatures, constants.
- Implementation file (.cpp)
- Bodies of the functions / methods declared in the header.
- Include guards
#ifndef NAME_H ... #define NAME_H ... #endifwrapping header content. Prevents double-inclusion errors.- keywords.txt
- Tab-separated file listing identifiers and their highlight category. Colors the IDE.
- library.properties
- Metadata: name, version, author, description, etc. Used by the Library Manager.
- examples/ folder
- Sample sketches. Each in its own subdirectory. Shows in File → Examples → <library>.
- src/ folder
- Modern convention for code files. Alternative to .h/.cpp in the library root.
Extra Mission 5 min
Part 1 — Design a library someone else would want
Think of a part you have wired many times: a button, a buzzer, an ultrasonic sensor, a servo. On paper, design a library that would save another student time with it.
Your design must include:
- The library name, checked so it does not clash with a common one.
- The class name and every public method, with one line on what each does.
- The names of two examples and what each shows.
- The
keywords.txtentries.
Part 2 — Make it
Build it as a proper four-file library with the folder layout from the New Concept. Include at least one example that compiles and runs on your circuit.
Bring back next class: your zipped library folder, a screenshot of your example in File → Examples, and a photo of it running.