1. Mengapa Membuat Library Arduino?
Membuat Arduino Library memungkinkan kita membungkus kode yang kompleks ke dalam antarmuka (API) yang mudah digunakan. Library yang baik membuat proyek lebih modular, reusable, dan mudah di-maintain.
Keuntungan Library yang Baik
- â Copy-paste kode ke setiap proyek
- â Sulit sharing ke orang lain
- â Tidak ada dependency management
- â No syntax highlighting
- â Reusable di banyak proyek
- â Install via Library Manager
- â Auto dependency resolution
- â Syntax highlighting otomatis
2. Struktur Library Arduino
Arduino mengharuskan struktur folder yang spesifik. Ada dua gaya: flat layout (sederhana) dan src layout (recommended untuk library besar).
MySensorLibrary/
âââ src/ â Source files
â âââ MySensorLibrary.h â Header utama (wajib)
â âââ MySensorLibrary.cpp â Implementation
â âââ utils.h â Helper header
âââ examples/ â Contoh penggunaan
â âââ BasicRead/
â â âââ BasicRead.ino
â âââ AdvancedConfig/
â âââ AdvancedConfig.ino
âââ keywords.txt â Syntax highlighting
âââ library.properties â Metadata library
âââ LICENSE â Lisensi (MIT recommended)
âââ README.md â Dokumentasi
MySensorLibrary/
âââ src/
â âââ MySensorLibrary.h â Main header (include semua)
â âââ MySensorLibrary.cpp â Core implementation
â âââ sensor_driver.h â Sensor-specific header
â âââ sensor_driver.cpp â Sensor-specific impl
â âââ utils/
â â âââ crc.h
â â âââ crc.cpp
â âââ config.h â Konfigurasi library
âââ examples/
â âââ SimpleRead/
â â âââ SimpleRead.ino
â âââ MultipleSensors/
â â âââ MultipleSensors.ino
â âââ Calibration/
â âââ Calibration.ino
âââ keywords.txt
âââ library.properties
âââ LICENSE
âââ README.md
3. Header File (.h) yang Benar
Header file adalah "wajah" library yang dilihat user. Pastikan menggunakan include guards atau #pragma once untuk mencegah double inclusion.
/**
* @file MySensorLibrary.h
* @brief Library untuk membaca sensor suhu DHT dan BMP
*
* Library ini menyediakan API sederhana untuk membaca
* sensor suhu dan kelembaban dari DHT22 dan BMP280.
*
* @author BeebaneLabs
* @version 1.0.0
* @date 2026-06-29
* @license MIT
*/
#ifndef MY_SENSOR_LIBRARY_H
#define MY_SENSOR_LIBRARY_H
#include <Arduino.h>
#include <Wire.h>
// Version info
#define MY_SENSOR_LIB_VERSION "1.0.0"
#define MY_SENSOR_LIB_VERSION_MAJOR 1
#define MY_SENSOR_LIB_VERSION_MINOR 0
#define MY_SENSOR_LIB_VERSION_PATCH 0
// Sensor types yang didukung
enum SensorType {
SENSOR_DHT22 = 0,
SENSOR_BMP280,
SENSOR_SHT31,
SENSOR_COUNT
};
// Struct untuk data sensor
struct SensorData {
float temperature; // °C
float humidity; // %RH (hanya untuk sensor dengan humidity)
float pressure; // hPa (hanya untuk sensor dengan tekanan)
float altitude; // meter (dihitung dari pressure)
bool isValid; // true jika data valid
unsigned long timestamp; // millis() saat pembacaan
};
// Callback type untuk data baru
typedef void (*DataCallback)(SensorData data);
/**
* @class MySensor
* @brief Class utama untuk interaksi dengan sensor
*
* Contoh penggunaan:
* @code
* MySensor sensor;
* sensor.begin(SENSOR_DHT22, 4);
* SensorData data = sensor.read();
* Serial.println(data.temperature);
* @endcode
*/
class MySensor {
public:
/**
* @brief Constructor default
*/
MySensor();
/**
* @brief Destructor â cleanup resources
*/
~MySensor();
/**
* @brief Inisialisasi sensor
* @param type Tipe sensor (SENSOR_DHT22, SENSOR_BMP280, dll)
* @param pin GPIO pin (untuk DHT) atau I2C address (untuk I2C sensor)
* @param wire Pointer ke TwoWire (default: &Wire)
* @return true jika inisialisasi berhasil
*/
bool begin(SensorType type, uint8_t pin, TwoWire *wire = &Wire);
/**
* @brief Baca data sensor
* @return SensorData struct berisi pembacaan
*/
SensorData read();
/**
* @brief Baca hanya suhu
* @return Suhu dalam °C, atau NAN jika error
*/
float readTemperature();
/**
* @brief Baca hanya kelembaban
* @return Kelembaban dalam %RH, atau NAN jika error
*/
float readHumidity();
/**
* @brief Set callback saat data baru tersedia
* @param callback Fungsi yang dipanggil saat data ready
*/
void onDataReady(DataCallback callback);
/**
* @brief Mulai pembacaan periodik di background
* @param intervalMs Interval pembacaan dalam milidetik
*/
void startAutoRead(uint32_t intervalMs = 2000);
/**
* @brief Stop pembacaan periodik
*/
void stopAutoRead();
/**
* @brief Set offset kalibrasi
* @param tempOffset Offset suhu dalam °C
* @param humidOffset Offset kelembaban dalam %RH
*/
void setCalibration(float tempOffset, float humidOffset);
/**
* @brief Cek apakah sensor terhubung
* @return true jika sensor responsif
*/
bool isConnected();
/**
* @brief Dapatkan informasi sensor
* @return String berisi info sensor
*/
String getInfo();
/**
* @brief Dapatkan library version
* @return Versi library dalam format "x.y.z"
*/
static const char* getVersion() { return MY_SENSOR_LIB_VERSION; }
private:
SensorType _type;
uint8_t _pin;
TwoWire *_wire;
bool _initialized;
float _tempOffset;
float _humidOffset;
DataCallback _callback;
SensorData _lastData;
// Private methods
bool _initDHT22();
bool _initBMP280();
SensorData _readDHT22();
SensorData _readBMP280();
float _calculateAltitude(float pressure);
};
#endif // MY_SENSOR_LIBRARY_H
Gunakan #ifndef/#define/#endif (include guards) untuk kompatibilitas lintas compiler. #pragma once lebih praktis tapi tidak dijamin didukung semua compiler (meskipun hampir semua compiler modern mendukungnya). Untuk Arduino library, include guards lebih aman.
4. Class Design & OOP
Arduino library yang baik menggunakan prinsip OOP: encapsulation (private members), abstraction (public API sederhana), dan clean interface.
Prinsip Class Design
| Prinsip | Praktik | Contoh |
|---|---|---|
| Encapsulation | Private members dengan _ prefix | uint8_t _pin; |
| Constructor | Inisialisasi semua member | MySensor() : _initialized(false) {} |
| begin() pattern | Pisahkan konstruksi dari inisialisasi | bool begin(type, pin); |
| Error handling | Return bool/nullptr untuk error | if (!sensor.begin()) return; |
| Default params | Parameter opsional dengan default | begin(type, pin, &Wire); |
| Const correctness | Tandai method yang tidak modify | float readTemp() const; |
Global objects di Arduino diinisialisasi sebelum setup() â sebelum hardware siap. Gunakan begin() pattern: constructor hanya set nilai default, begin() yang inisialisasi hardware (GPIO, I2C, SPI).
5. Implementation File (.cpp)
/**
* @file MySensorLibrary.cpp
* @brief Implementation dari MySensor class
*/
#include "MySensorLibrary.h"
// BMP280 I2C address
#define BMP280_ADDR 0x76
// ============ Constructor & Destructor ============
MySensor::MySensor()
: _type(SENSOR_DHT22)
, _pin(0)
, _wire(&Wire)
, _initialized(false)
, _tempOffset(0.0)
, _humidOffset(0.0)
, _callback(nullptr)
{
memset(&_lastData, 0, sizeof(SensorData));
}
MySensor::~MySensor() {
stopAutoRead();
}
// ============ Public Methods ============
bool MySensor::begin(SensorType type, uint8_t pin, TwoWire *wire) {
_type = type;
_pin = pin;
_wire = wire ? wire : &Wire;
switch (_type) {
case SENSOR_DHT22:
_initialized = _initDHT22();
break;
case SENSOR_BMP280:
_wire->begin();
_initialized = _initBMP280();
break;
default:
Serial.println("[MySensor] Unknown sensor type!");
_initialized = false;
break;
}
if (_initialized) {
Serial.printf("[MySensor] Initialized: type=%d, pin=%d\n", _type, _pin);
}
return _initialized;
}
SensorData MySensor::read() {
if (!_initialized) {
_lastData.isValid = false;
return _lastData;
}
switch (_type) {
case SENSOR_DHT22:
_lastData = _readDHT22();
break;
case SENSOR_BMP280:
_lastData = _readBMP280();
break;
default:
_lastData.isValid = false;
break;
}
// Apply calibration
if (_lastData.isValid) {
_lastData.temperature += _tempOffset;
_lastData.humidity += _humidOffset;
_lastData.timestamp = millis();
}
// Trigger callback
if (_callback && _lastData.isValid) {
_callback(_lastData);
}
return _lastData;
}
float MySensor::readTemperature() {
SensorData data = read();
return data.isValid ? data.temperature : NAN;
}
float MySensor::readHumidity() {
SensorData data = read();
return data.isValid ? data.humidity : NAN;
}
void MySensor::onDataReady(DataCallback callback) {
_callback = callback;
}
void MySensor::setCalibration(float tempOffset, float humidOffset) {
_tempOffset = tempOffset;
_humidOffset = humidOffset;
}
bool MySensor::isConnected() {
if (_type == SENSOR_BMP280) {
_wire->beginTransmission(BMP280_ADDR);
return (_wire->endTransmission() == 0);
}
return _initialized;
}
String MySensor::getInfo() {
String info = "MySensor v" + String(MY_SENSOR_LIB_VERSION);
info += " | Type: " + String(_type);
info += " | Pin: " + String(_pin);
info += " | Init: " + String(_initialized ? "YES" : "NO");
return info;
}
// ============ Private Methods ============
bool MySensor::_initDHT22() {
// DHT22 library handles init internally
pinMode(_pin, INPUT_PULLUP);
delay(2000); // DHT22 warm-up time
return true;
}
bool MySensor::_initBMP280() {
_wire->beginTransmission(BMP280_ADDR);
if (_wire->endTransmission() != 0) return false;
// Read chip ID
_wire->beginTransmission(BMP280_ADDR);
_wire->write(0xD0); // Chip ID register
_wire->endTransmission();
_wire->requestFrom(BMP280_ADDR, (uint8_t)1);
uint8_t chipId = _wire->read();
if (chipId != 0x58) {
Serial.printf("[MySensor] BMP280 ID mismatch: 0x%02X\n", chipId);
return false;
}
// Configure: oversampling x16, normal mode
_wire->beginTransmission(BMP280_ADDR);
_wire->write(0xF4); // ctrl_meas
_wire->write(0xB7);
_wire->endTransmission();
return true;
}
SensorData MySensor::_readDHT22() {
SensorData data;
// Simplified DHT22 read â in real code, use DHT library
data.temperature = 25.0; // Placeholder
data.humidity = 60.0;
data.pressure = 0;
data.altitude = 0;
data.isValid = true;
return data;
}
SensorData MySensor::_readBMP280() {
SensorData data = {0};
uint8_t buf[6];
_wire->beginTransmission(BMP280_ADDR);
_wire->write(0xF7); // Start register
_wire->endTransmission();
_wire->requestFrom(BMP280_ADDR, (uint8_t)6);
for (int i = 0; i < 6; i++) {
buf[i] = _wire->read();
}
// Parse raw data
int32_t rawPress = ((int32_t)buf[0] << 12) | ((int32_t)buf[1] << 4) | (buf[2] >> 4);
int32_t rawTemp = ((int32_t)buf[3] << 12) | ((int32_t)buf[4] << 4) | (buf[5] >> 4);
// Simplified calculation â real code needs calibration
data.temperature = (float)rawTemp / 100.0;
data.pressure = (float)rawPress / 256.0 / 100.0;
data.altitude = _calculateAltitude(data.pressure);
data.humidity = 0;
data.isValid = true;
return data;
}
float MySensor::_calculateAltitude(float pressure) {
// Barometric formula
return 44330.0 * (1.0 - pow(pressure / 1013.25, 0.1903));
}
6. keywords.txt: Syntax Highlighting
File keywords.txt memberitahu Arduino IDE cara me-highlight keyword library di editor. Format: keyword diikuti TAB lalu tipe KEYWORD1/KEYWORD2/LITERAL1.
#######################################
# Syntax Coloring Map untuk MySensorLibrary
#######################################
#######################################
# Class & Datatypes (KEYWORD1 â cyan/ungu)
#######################################
MySensor KEYWORD1
SensorData KEYWORD1
SensorType KEYWORD1
DataCallback KEYWORD1
#######################################
# Methods & Functions (KEYWORD2 â orange)
#######################################
begin KEYWORD2
read KEYWORD2
readTemperature KEYWORD2
readHumidity KEYWORD2
onDataReady KEYWORD2
startAutoRead KEYWORD2
stopAutoRead KEYWORD2
setCalibration KEYWORD2
isConnected KEYWORD2
getInfo KEYWORD2
getVersion KEYWORD2
#######################################
# Constants (LITERAL1 â hijau)
#######################################
SENSOR_DHT22 LITERAL1
SENSOR_BMP280 LITERAL1
SENSOR_SHT31 LITERAL1
MY_SENSOR_LIB_VERSION LITERAL1
Arduino IDE membutuhkan TAB character (bukan spasi) antara keyword dan tipe. Jika syntax highlighting tidak bekerja, kemungkinan besar kamu menggunakan spasi. Gunakan text editor yang bisa menunjukkan whitespace characters.
7. library.properties: Metadata
File library.properties berisi metadata yang digunakan Arduino Library Manager untuk menampilkan informasi library.
name=MySensorLibrary
version=1.0.0
author=BeebaneLabs <beebane@example.com>
maintainer=BeebaneLabs <beebane@example.com>
sentence=Universal sensor library for DHT22, BMP280, SHT31.
paragraph=Read temperature, humidity, and pressure from popular I2C sensors. Supports auto-calibration, callback-based reading, and multi-sensor configuration.
category=Sensors
url=https://github.com/Beebane25/MySensorLibrary
architectures=*
depends=Wire
includes=MySensorLibrary.h
depends=Adafruit BMP280 Library
Field yang Wajib
| Field | Wajib? | Deskripsi |
|---|---|---|
| name | â | Nama library (tanpa spasi untuk Library Manager) |
| version | â | SemVer (x.y.z) |
| author | â | Nama dan email |
| maintainer | â | Nama dan email maintainer |
| sentence | â | Satu kalimat deskripsi (max 90 char) |
| paragraph | â | Deskripsi lebih panjang |
| category | â | Communication, Display, Sensors, Signal Input/Output, dll |
| url | â | URL repository GitHub |
| architectures | â | * untuk semua, esp32, avr, dll |
8. Examples yang Baik
Contoh yang baik adalah dokumen terpenting dari sebuah library. Letakkan di folder examples/ dengan nama folder = nama .ino file.
/**
* BasicRead.ino
* Contoh paling sederhana membaca sensor
*
* Wiring:
* DHT22 VCC â 3.3V
* DHT22 DATA â GPIO 4 (10kΊ pull-up)
* DHT22 GND â GND
*/
#include <MySensorLibrary.h>
MySensor sensor;
void setup() {
Serial.begin(115200);
Serial.println("=== MySensor Basic Read ===");
// Inisialisasi sensor DHT22 pada pin 4
if (!sensor.begin(SENSOR_DHT22, 4)) {
Serial.println("ERROR: Sensor tidak terdeteksi!");
while (1) {
delay(1000); // Stop di sini
}
}
Serial.println("Sensor OK!");
Serial.println(sensor.getInfo());
}
void loop() {
// Baca semua data
SensorData data = sensor.read();
if (data.isValid) {
Serial.printf("Suhu: %.1f°C | Humid: %.1f%%\n",
data.temperature, data.humidity);
} else {
Serial.println("Pembacaan gagal!");
}
delay(2000);
}
9. Publish ke Arduino Library Manager
Langkah Publish
- Buat repository GitHub dengan nama yang sesuai
namedi library.properties - Push semua file ke repository
- Buat tag release dengan versi yang sesuai:
git tag 1.0.0 && git push --tags - Register di Arduino Library Registry: Buka
https://github.com/arduino/library-registry - Submit issue dengan format:
https://github.com/username/repo - Tunggu Arduino Bot melakukan review otomatis
- Fix issues yang ditemukan bot (jika ada)
- Library akan tersedia di Arduino Library Manager setelah disetujui
Arduino Bot akan mengecek: library.properties valid, ada minimal 1 example, name unik, dan architectures terisi. Gunakan arduino-lint untuk mengecek library secara lokal sebelum submit: arduino-lint --library-manager .