Learning Goals 5 min
Cluster H's capstone: take one of your Cluster A-G sensor / motor wrappers and turn it into a polished, distributable Arduino library. By the end of this lesson you will:
- Pick a wrapper worth packaging (SmoothedSensor, Motor class, RangeFinder, etc.).
- Refactor into a proper library structure with API polish (defaults, doxygen, const-correctness).
- Optionally publish to GitHub and submit to the Library Registry.
Warm-Up 10 min
Look back at your reusable code so far:
- Motor (L03-08, L03-41) — wraps L298N IN1/IN2/EN signals into
setSpeed(int). - SmoothedSensor (L03-41) — analog sensor with running-average smoothing.
- RangeFinder (L03-43) — HC-SR04 wrapper returning cm.
- PID (L04-24) — generic PID controller.
- Blinker (L03-42, L04-41) — non-blocking blink.
- ShiftReg (L03-21) — 74HC595 helper.
Pick one. Today we polish it end-to-end and (optionally) ship.
New Concept · From wrapper to library 25 min
The polish pass
- Split into .h + .cpp (or .h only if header-only is fine).
- Apply API design principles from L04-42: defaults, const, doxygen, private internals.
- Add 2-3 examples showing common uses.
- Write README with quick start.
- Test on multiple boards: UNO, Nano, ESP32. Use #ifdef for board differences.
- Version it 0.1.0 until you're confident → 1.0.0 when stable.
- License: pick MIT or similar.
- Optionally publish via the L04-43 flow.
Example: turn Motor into a library
Start with the L03-09 Motor struct + functions. Convert to a class library.
Motor.h
#ifndef MOTOR_H
#define MOTOR_H
#include <Arduino.h>
/**
* @brief L298N-driven DC motor with signed PWM speed.
*
* Each instance handles one motor channel of an L298N H-bridge.
* Connect IN1, IN2, and ENA (or IN3/IN4/ENB) to digital pins.
* Positive duty = forward; negative = reverse; zero = brake.
*/
class Motor {
public:
/**
* @param in1 Direction pin 1.
* @param in2 Direction pin 2.
* @param en PWM-capable enable pin.
*/
Motor(int in1, int in2, int en);
/// Configure pins as OUTPUT. Call in setup().
void begin();
/// Set signed duty cycle: -255..+255.
void setSpeed(int duty);
/// Stop with both IN pins LOW (active brake).
void brake();
/// Disconnect motor (coast). Requires PWM pin to support 0.
void coast();
/// Last commanded signed duty.
int getSpeed() const { return lastDuty_; }
private:
int in1_, in2_, en_;
int lastDuty_;
};
#endifMotor.cpp
#include "Motor.h"
Motor::Motor(int in1, int in2, int en)
: in1_(in1), in2_(in2), en_(en), lastDuty_(0) {}
void Motor::begin() {
pinMode(in1_, OUTPUT);
pinMode(in2_, OUTPUT);
pinMode(en_, OUTPUT);
brake();
}
void Motor::setSpeed(int duty) {
duty = constrain(duty, -255, 255);
if (duty > 0) {
digitalWrite(in1_, HIGH);
digitalWrite(in2_, LOW);
analogWrite(en_, duty);
} else if (duty < 0) {
digitalWrite(in1_, LOW);
digitalWrite(in2_, HIGH);
analogWrite(en_, -duty);
} else {
brake();
}
lastDuty_ = duty;
}
void Motor::brake() {
digitalWrite(in1_, LOW);
digitalWrite(in2_, LOW);
analogWrite(en_, 0);
lastDuty_ = 0;
}
void Motor::coast() {
analogWrite(en_, 0);
lastDuty_ = 0;
}Example sketch
#include <Motor.h>
Motor leftWheel(4, 7, 5);
Motor rightWheel(8, 9, 6);
void setup() {
leftWheel.begin();
rightWheel.begin();
}
void loop() {
leftWheel.setSpeed(200);
rightWheel.setSpeed(200);
delay(2000);
leftWheel.setSpeed(-200);
rightWheel.setSpeed(-200);
delay(2000);
}Cross-board support
void Motor::setSpeed(int duty) {
// ...
#if defined(ESP32) || defined(ESP8266)
// ESP boards use a different PWM resolution by default (10-bit)
duty = (duty * 1023) / 255;
#endif
// ...
}Or call analogWriteResolution(8) in begin() to standardise.
Worked Example · Ship one of your libraries 30 min
Pick
Pick the most polished of your wrappers. SmoothedSensor is a great choice — small, useful, no hardware-specific.
Refactor
Apply all of L04-42's principles. Split files. Add defaults. Add doxygen. Add 2 examples.
Test
Compile + run on UNO. Compile + run on ESP. Note any divergence; fix or document.
Document
README with quickstart. License. Optional: HTML docs from Doxygen.
Publish (optional)
Push to GitHub. Tag 1.0.0. Submit PR to Library Registry.
Celebrate
If you submitted: your code is now installable by anyone in the world. Real software shipping.
Cluster H done. Final cluster (exam prep) starts tomorrow.
Try It Yourself 15 min
Goal: Make a second library from a different wrapper. Apply the same polish pass.
Goal: Combine two of your libraries into one (e.g. Motor + RangeFinder + a tiny "Chassis" helper). The kind of higher-level "robot car" library you'd find on the Arduino store.
Goal: Submit a library to the Arduino Library Registry. Get it actually listed. Watch the install count climb.
Mini-Challenge · Portfolio piece 10 min
Take one polished library you've built. Use it in a different sketch that solves a real problem. Take a video / write-up describing:
- What problem does the library solve?
- Quick usage example.
- What you'd add for v2.
This is a portfolio piece for engineering applications and showcases real authorship.
Recap 5 min
Library = struct/class wrapper + polished API + examples + README + license + Git tag + PR to registry. Once shipped, others install with one click. Cluster H done. Final cluster: Arduino Certification Exam preparation.
- Driver library
- A reusable wrapper for a hardware component (sensor, motor, display). Hides protocol / wiring details behind a clean API.
- Polish pass
- The conversion from "works in my sketch" to "ready for everyone's sketches": defaults, docs, examples, license.
- Header-only library
- Library with everything in .h files (no .cpp). Simpler for small utilities; means definitions inline.
- Multi-architecture support
- Library that works on UNO, ESP, Nano 33 BLE. Use
#ifdeffor board-specific bits. - Portfolio piece
- A published project you can show to others — university applications, internships, engineering portfolios. Open source libraries are excellent portfolio fodder.
Homework 5 min
- Polish one library. Optionally publish.
- Read ahead to ARD-L04-45 (Exam Format and Domains). Cluster I — final stretch.