Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Arduino & Microcontrollers

Arduino Library Writing: Create Your Own Custom Class and Functions

Arduino Library Writing: Create Your Own Custom Class and Functions

March 11, 2026 /Posted byJayesh Jain / 0

Every time you call dht.readTemperature() or servo.write(90), you are using someone’s custom Arduino library. Libraries are the backbone of the Arduino ecosystem — they encapsulate complex hardware interaction into clean, reusable interfaces that let any developer use sophisticated functionality with just a few lines of code.

Writing your own Arduino library is a skill that transforms you from a library consumer into a library author. Whether you want to package your sensor driver for reuse across projects, share your work with the community, or simply write better-organized code, understanding Arduino library structure and C++ class design is an essential step forward.

This guide takes you from zero to a fully functional, publishable Arduino library — with a real-world example of a temperature sensor abstraction class.

Table of Contents

  1. Arduino Library Structure and File Organization
  2. Writing the Header File (.h)
  3. Writing the Implementation File (.cpp)
  4. keywords.txt and library.properties
  5. Writing Example Sketches
  6. Advanced Techniques: Callbacks, Templates, and Inheritance
  7. Testing and Publishing Your Library
  8. Frequently Asked Questions

Arduino Library Structure and File Organization

An Arduino library is a directory with a specific structure that the Arduino IDE and PlatformIO recognize. The minimum required structure is:

MyLibrary/
├── src/
│   ├── MyLibrary.h        # Header file (class declaration)
│   └── MyLibrary.cpp      # Implementation file (method definitions)
├── examples/
│   └── BasicExample/
│       └── BasicExample.ino
├── keywords.txt            # Syntax highlighting definitions
└── library.properties      # Library metadata

The src/ directory is the modern convention (Arduino IDE 1.5+). Older libraries placed .h and .cpp files directly in the root. Both work, but the src/ layout is preferred for new libraries as it separates source from metadata.

The library directory name must exactly match the library name in library.properties and the header file name. Consistency here prevents confusing IDE errors.

To create a library manually, create this directory structure inside your Arduino libraries folder (typically ~/Arduino/libraries/ on Linux/Mac or Documents/Arduino/libraries/ on Windows). Both Arduino IDE and PlatformIO will discover it automatically.

Recommended: Arduino Uno R3 Beginners Kit — Test your custom library development on the most common Arduino platform with the largest community of library users to learn from.

Writing the Header File (.h)

The header file is the public interface of your library — it declares what your class can do without revealing how it does it. A well-designed header file should be readable and self-explanatory even without the .cpp implementation.

We’ll build a SimpleSensor library that abstracts a generic analog temperature sensor like the LM35:

// src/SimpleSensor.h
#ifndef SIMPLESENSOR_H
#define SIMPLESENSOR_H

// Required Arduino includes
#include <Arduino.h>

// Library version (can be read by user code)
#define SIMPLESENSOR_VERSION "1.0.0"

// Temperature units enum for clean API
enum TempUnit {
    CELSIUS,
    FAHRENHEIT,
    KELVIN
};

class SimpleSensor {

public:
    // Constructor: accepts the analog pin number
    SimpleSensor(uint8_t pin);
    
    // Initialize the sensor (call in setup())
    bool begin();
    
    // Read temperature in the specified unit
    float readTemperature(TempUnit unit = CELSIUS);
    
    // Get the raw ADC reading (0-1023)
    int readRaw();
    
    // Set number of samples to average (default: 1)
    void setSamples(uint8_t samples);
    
    // Set reference voltage for ADC calculations (default: 5.0V)
    void setReferenceVoltage(float vref);
    
    // Check if sensor is responding (basic sanity check)
    bool isConnected();

private:
    uint8_t _pin;           // The analog pin connected to sensor
    uint8_t _samples;       // Number of readings to average
    float   _vref;          // ADC reference voltage
    bool    _initialized;   // Has begin() been called?
    
    // Private helper: convert ADC value to Celsius
    float _adcToCelsius(int adcValue);
    
    // Private helper: take multiple readings and average
    float _averagedReading();
};

#endif  // SIMPLESENSOR_H

Several important points about this header:

  • Include guards: #ifndef SIMPLESENSOR_H / #define SIMPLESENSOR_H / #endif prevent the header from being included multiple times in the same compilation unit, which would cause “redefinition” errors.
  • Arduino.h: Must be included because Arduino types (uint8_t, byte, etc.) and functions (analogRead, pinMode) are defined here. Without it, your library won’t compile.
  • Private vs Public: Public methods are the user-facing API. Private methods and variables are implementation details the user doesn’t need to see or interact with.
  • Default parameters: readTemperature(TempUnit unit = CELSIUS) gives users a clean default while preserving flexibility.
  • Naming conventions: Private members prefixed with underscore (_pin, _vref) is a common Arduino library convention that makes them visually distinct.
Recommended: LM35 Temperature Sensors — The ideal sensor to build your first custom library around. Simple analog output, linear response, and direct Celsius conversion make it perfect for a library learning project.

Writing the Implementation File (.cpp)

The .cpp file contains the actual implementations of all the methods declared in the header. Every method definition uses the ClassName:: scope resolution operator:

// src/SimpleSensor.cpp
#include "SimpleSensor.h"

// Constructor: initialize member variables
SimpleSensor::SimpleSensor(uint8_t pin) {
    _pin = pin;
    _samples = 1;        // Default: single reading
    _vref = 5.0;         // Default: 5V reference (Uno, Mega)
    _initialized = false;
}

// begin(): set up the pin and validate
bool SimpleSensor::begin() {
    // Analog pins are inputs by default, but set explicitly
    pinMode(_pin, INPUT);
    _initialized = true;
    
    // Quick sanity check: ADC value shouldn't be 0 or max if sensor connected
    int rawVal = analogRead(_pin);
    return (rawVal > 0 && rawVal < 1023);
}

// readTemperature(): the main public method
float SimpleSensor::readTemperature(TempUnit unit) {
    if (!_initialized) return -999.0;  // Error: begin() not called
    
    float celsius = _averagedReading();
    
    switch (unit) {
        case FAHRENHEIT:
            return (celsius * 9.0 / 5.0) + 32.0;
        case KELVIN:
            return celsius + 273.15;
        case CELSIUS:
        default:
            return celsius;
    }
}

// readRaw(): return raw ADC value
int SimpleSensor::readRaw() {
    return analogRead(_pin);
}

// setSamples(): configure averaging
void SimpleSensor::setSamples(uint8_t samples) {
    // Clamp between 1 and 64 to avoid blocking too long
    _samples = constrain(samples, 1, 64);
}

// setReferenceVoltage(): for 3.3V systems or external AREF
void SimpleSensor::setReferenceVoltage(float vref) {
    _vref = vref;
}

// isConnected(): basic check if sensor is responding
bool SimpleSensor::isConnected() {
    int raw = analogRead(_pin);
    // A floating pin reads erratically; a connected LM35 reads in range
    return (raw > 10 && raw < 1010);
}

// PRIVATE: convert ADC reading to Celsius for LM35
// LM35: 10mV per degree Celsius
// Celsius = (ADC * Vref / 1024) / 0.01
float SimpleSensor::_adcToCelsius(int adcValue) {
    float voltage = (adcValue * _vref) / 1024.0;
    return voltage / 0.01;  // LM35: 10mV per degree
}

// PRIVATE: take multiple readings and return average
float SimpleSensor::_averagedReading() {
    long sum = 0;
    for (uint8_t i = 0; i < _samples; i++) {
        sum += analogRead(_pin);
        if (_samples > 1) delay(2);  // Small delay between readings
    }
    return _adcToCelsius(sum / _samples);
}

Notice how the private helper methods _adcToCelsius() and _averagedReading() are not in the user-facing API but do the mathematical heavy lifting. This keeps the public interface clean while hiding complexity.

Recommended: Arduino Nano Every with Headers — A compact, modern Arduino for deploying custom library-based projects. The ATmega4809 offers improved peripherals and is well-suited for standalone sensor node applications.

keywords.txt and library.properties

library.properties — this file tells the Arduino IDE (and PlatformIO) everything it needs to know about your library:

name=SimpleSensor
version=1.0.0
author=Your Name <[email protected]>
maintainer=Your Name <[email protected]>
sentence=A simple analog temperature sensor library for Arduino.
paragraph=Supports LM35 and similar analog sensors with averaging, unit conversion, and configurable reference voltage.
category=Sensors
url=https://github.com/yourusername/SimpleSensor
architectures=*
depends=

The architectures=* means the library works on all Arduino platforms. If your library is AVR-specific, use architectures=avr. The depends field lists other libraries yours requires.

keywords.txt — provides syntax highlighting in Arduino IDE. The format is: KEYWORD TAB TYPE

#######################################
# Syntax Coloring Map for SimpleSensor
#######################################

# Datatypes (orange)
SimpleSensorttKEYWORD1
TempUnitttKEYWORD1

# Methods and functions (brown/orange)
begintttKEYWORD2
readTemperaturettKEYWORD2
readRawtttKEYWORD2
setSamplestttKEYWORD2
setReferenceVoltagetKEYWORD2
isConnectedttKEYWORD2

# Constants (teal)
CELSIUStttLITERAL1
FAHRENHEITttLITERAL1
KELVINtttLITERAL1

Writing Example Sketches

Example sketches are how users learn to use your library. Put them in the examples/ directory — they appear in Arduino IDE under File → Examples → SimpleSensor:

// examples/BasicReading/BasicReading.ino
#include <SimpleSensor.h>

// Create sensor on analog pin A0
SimpleSensor tempSensor(A0);

void setup() {
    Serial.begin(9600);
    
    // Average 8 readings for smoother output
    tempSensor.setSamples(8);
    
    if (tempSensor.begin()) {
        Serial.println("SimpleSensor initialized successfully!");
    } else {
        Serial.println("Warning: Sensor may not be connected.");
    }
}

void loop() {
    float tempC = tempSensor.readTemperature(CELSIUS);
    float tempF = tempSensor.readTemperature(FAHRENHEIT);
    
    Serial.print("Temperature: ");
    Serial.print(tempC, 1);
    Serial.print(" C / ");
    Serial.print(tempF, 1);
    Serial.println(" F");
    
    delay(2000);
}

Good examples show the most common use case first (basic reading), then provide more complex examples in separate sketch directories (averaging, unit switching, error handling). Each example should be complete and self-contained.

Recommended: Arduino Starter Kit with 170 Pages Project Book — Deep-dive into Arduino concepts and best practices that will make you a stronger library author. The project book covers C++ fundamentals applied to hardware.

Advanced Techniques: Callbacks, Templates, and Inheritance

Callbacks for Event-Driven Libraries

For libraries that need to notify user code when something happens (threshold crossed, data received, etc.), function pointer callbacks are the standard approach:

// In header
typedef void (*AlertCallback)(float temperature);

class SimpleSensor {
public:
    // Register a callback for temperature alerts
    void setAlertCallback(AlertCallback callback, float threshold);
    
    // Call in loop() to check threshold and trigger callback
    void update();

private:
    AlertCallback _callback;
    float _threshold;
};

// User code:
void onHotAlert(float temp) {
    Serial.print("ALERT! Temperature: "); Serial.println(temp);
}

sensor.setAlertCallback(onHotAlert, 40.0);  // Alert above 40°C

Template Classes for Generic Sensors

If your library needs to support different data types or sensor configurations, C++ templates allow generics without performance overhead:

// Generic ring buffer useful in many sensor libraries
template <typename T, uint8_t SIZE>
class RingBuffer {
public:
    void push(T value) {
        _buf[_head] = value;
        _head = (_head + 1) % SIZE;
        if (_count < SIZE) _count++;
    }
    
    T average() {
        T sum = 0;
        for (uint8_t i = 0; i < _count; i++) sum += _buf[i];
        return _count ? sum / _count : 0;
    }

private:
    T _buf[SIZE];
    uint8_t _head = 0;
    uint8_t _count = 0;
};

// Use:
RingBuffer<float, 16> tempHistory;  // 16-element float ring buffer

Inheritance for Sensor Families

If you’re building a family of related libraries (e.g., multiple temperature sensor types), use inheritance to share common code:

// Base class with common interface
class TemperatureSensor {
public:
    virtual float readTemperature() = 0;  // Pure virtual - must override
    virtual bool begin() = 0;
    
    // Common utility - same for all subclasses
    float readFahrenheit() {
        return (readTemperature() * 9.0 / 5.0) + 32.0;
    }
};

// Concrete implementations
class LM35Sensor : public TemperatureSensor { /* ... */ };
class DHT11Sensor : public TemperatureSensor { /* ... */ };
class DS18B20Sensor : public TemperatureSensor { /* ... */ };

Testing and Publishing Your Library

Testing Your Library

Before sharing your library, test it thoroughly:

  1. Compile test: Open each example sketch and verify it compiles without warnings on all target boards
  2. Hardware test: Upload and test with real hardware — the sensor, the Arduino, and any other components your library supports
  3. Edge cases: Test with incorrect pin numbers, unconnected sensors, extreme values. Make sure your library fails gracefully (returns error values, not garbage or crashes)
  4. PlatformIO: If you want PlatformIO users, test your library with a simple platformio.ini project

Publishing to Arduino Library Manager

  1. Create a public GitHub repository for your library
  2. Create a release tag (e.g., v1.0.0) on GitHub
  3. Submit a pull request to the arduino/library-registry GitHub repository, adding your library’s GitHub URL to the list
  4. The Arduino team reviews submissions (usually within a few days)
  5. Once approved, your library appears in Arduino IDE’s Library Manager for the entire global community

Publishing to PlatformIO Registry

  1. Install PlatformIO Core: pip install platformio
  2. Login: pio account login
  3. Publish from your library directory: pio pkg publish
Recommended: Arduino Tiny Machine Learning Kit — Once you’re comfortable writing basic Arduino libraries, this kit challenges you to write libraries for ML inference — the next frontier in embedded library development.

Frequently Asked Questions

Do I need to know C++ to write Arduino libraries?

A basic understanding of C++ classes, constructors, and member functions is required. However, the Arduino library API keeps C++ usage relatively simple — you don’t need templates, inheritance, or advanced metaprogramming for most libraries. If you know how to write Arduino sketches confidently, you are 70% of the way to writing a basic library. The main new concepts are: class declaration in a .h file, method definition in a .cpp file using the ClassName:: prefix, and the difference between public and private members.

How do I access hardware (SPI, I2C, Serial) from inside a library?

Include the relevant header in your library’s .h or .cpp file and use the global hardware objects. For I2C use #include <Wire.h> and call Wire.begin(), Wire.requestFrom(), etc. For SPI use #include <SPI.h>. For Serial, just use Serial.print() directly. It is good practice to call Wire.begin() or SPI.begin() in your library’s begin() method, since multiple libraries calling these in setup() is harmless — they are designed to handle multiple calls.

Can my library use global variables?

Avoid global variables in libraries wherever possible. Use class member variables instead. Global variables in libraries can conflict with user code or other libraries that use the same variable names. If you absolutely need a global (e.g., for an ISR that must be accessed outside the class), prefix it with your library name to minimize conflicts and document it clearly: volatile bool SimpleSensor_irqFired = false;

How do I debug library code? I can’t easily use Serial.print inside a library.

You can use Serial.print inside library code — it works fine. However, it’s best practice to make debug output conditional on a compile-time flag, so users can disable it to save flash space. Define a debug macro at the top of your header: #define SIMPLESENSOR_DEBUG 0. Then in .cpp: if (SIMPLESENSOR_DEBUG) { Serial.println("Debug info"); }. Advanced libraries use #ifdef SIMPLESENSOR_DEBUG preprocessor guards so the debug code compiles to nothing when disabled.

What is the maximum amount of code a library can have?

There is no practical limit to library size — the Arduino IDE compiles everything together. However, all the code in all your included libraries (plus your sketch) must fit in the target microcontroller’s flash memory. If your library is too large for small boards like the Uno (32KB flash), document this requirement and suggest larger boards like the Mega (256KB) or Nano 33 family. Conditional compilation (#ifdef __AVR_ATmega2560__) can enable/disable features based on the target board.

Take your Arduino skills to the professional level with quality hardware from Zbotic.in’s Arduino & Microcontrollers store — browse our complete selection of Arduino boards, sensors, and accessories, all with fast delivery across India.

Tags: Arduino C++, Arduino library, Arduino programming, arduino tutorial, custom Arduino class, embedded c++
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Raspberry Pi Docker Setup: Run...
blog raspberry pi docker setup run containers on arm linux 594969
blog arduino i2c tutorial wire library scanner multiple devices 594971
Arduino I2C Tutorial: Wire Lib...

Related posts

Svg%3E
Read more

Arduino Batch Programming: Flash Multiple Boards Quickly

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Based Radar System with Ultrasonic Sensor

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Automatic Plant Monitor: Sunlight, Moisture, Temperature

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Lie Detector: GSR Sensor Polygraph Project

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Metal Detector: Build a Treasure Finder

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now