Create Custom Arduino Library
Help the user interface with Arduino sensors or peripherals from MATLAB by creating
a custom arduino library that bridges MATLAB and Arduino C++ code.
When to Use
- User wants to read sensors not natively supported (DHT11, DHT22, IMU, air quality)
- User wants to control peripherals not natively supported (OLED displays, motor drivers, NeoPixels)
- User asks "How do I use [sensor/actuator/peripheral] from MATLAB with Arduino?"
- User wants to wrap an existing Arduino C++ library for MATLAB access
- User mentions
LibraryBase, arduinoioaddons, or custom addon creation
When NOT to Use
- Peripheral is already supported natively (use
listArduinoLibraries to check)
- User needs to discover or connect to an Arduino board (see
matlab-connect-arduino)
- User wants generic I2C/SPI communication (built-in)
- User wants to deploy standalone Arduino code (no MATLAB connection)
- User wants Simulink Arduino deployment (different workflow)
- User wants to fix Arduino hardware setup screens
Workflow
MANDATORY GATE — do not skip, but keep it fast:
Before creating a custom addon, you MUST run Steps 0 and 1 below. These are quick
checks (one MATLAB command + one sentence about File Exchange), not deep research.
Report findings in 1-2 lines and move on. If native support or a community addon
exists, stop. Otherwise proceed to Step 2 immediately.
STEP 0: Check Native Support (ALWAYS DO FIRST)
listArduinoLibraries
If the peripheral is in the list, tell the user and STOP. Do NOT proceed to Step 2
or beyond. Do NOT generate custom library code. Common built-in:
I2C, SPI, Serial, Servo, RotaryEncoder, Ultrasonic, ShiftRegister
STEP 1: Search File Exchange (MathWorks contributions only)
Search for existing add-ons contributed by the MathWorks MATLAB Hardware Team:
- Search:
site:mathworks.com/matlabcentral/fileexchange Arduino <peripheral>
- Look for packages with
+arduinoioaddons folder structure
- Only use results authored by MathWorks — filter by author "MathWorks Hardware Team"
or "MathWorks". Do NOT recommend community contributions from unknown third parties.
If a MathWorks-contributed add-on is found, guide the user through installation:
- Download/extract the addon package
addpath the folder containing +arduinoioaddons
- Install third-party Arduino libraries via
arduinoio.customLibrary.downloadLibrary
- Verify with
listArduinoLibraries
STOP here if a MathWorks add-on is found — do NOT proceed to Step 2 or beyond.
STEP 2: Gather Required Inputs
Before creating a custom library, the user MUST provide:
- Custom library name (e.g.,
MyDHT22)
- Third-party Arduino library name (if wrapping one; e.g.,
DHT22, U8g2)
- Target output directory for the MATLAB add-on files
Do NOT proceed to any further steps until all required inputs are provided. If any are
missing, ask the user explicitly and wait for their response.
STEP 3: Install Third-Party Arduino Library
% R2025a+ — use downloadLibrary (preferred)
arduinoio.customLibrary.downloadLibrary("<ArduinoLibraryName>")
% Accepts: library name, name@version, GitHub URL, or local .zip path
% Examples:
arduinoio.customLibrary.downloadLibrary("DHT22")
arduinoio.customLibrary.downloadLibrary("U8g2")
arduinoio.customLibrary.downloadLibrary("https://github.com/olikraus/u8g2")
Verify installation:
dir(fullfile(arduinoio.CLIRoot, 'user', 'libraries'))
For R2024a-R2024b (no downloadLibrary): manually extract the library zip into
fullfile(arduinoio.CLIRoot, 'user', 'libraries', '<LibName>').
For pre-R2024a (no CLIRoot): use arduinoio.IDERoot instead of arduinoio.CLIRoot.
STEP 4: Scaffold the Add-On
% R2025a+ — use createLibraryTemplate (preferred)
arduinoio.customLibrary.createLibraryTemplate("<CustomLibraryName>")
This creates the folder structure in the current working directory (pwd) and adds the
path automatically. After calling it, the generated files are at:
<pwd>/+arduinoioaddons/+<CustomLibraryName>Folder/<CustomLibraryName>.m
<pwd>/+arduinoioaddons/+<CustomLibraryName>Folder/src/<CustomLibraryName>.h
Do NOT search other locations — the files are always in pwd.
Naming: createLibraryTemplate("X") creates +XFolder/X.m — it appends "Folder"
to the package name automatically. The generated setup() has placeholder comments —
clear the body (no .begin() calls). The generated loop() is unused and can be deleted.
For pre-R2025a: create the folder structure manually:
<targetDir>/+arduinoioaddons/+<FolderName>/<ClassName>.m
<targetDir>/+arduinoioaddons/+<FolderName>/src/<HeaderName>.h
Then register:
addpath('<targetDir>');
savepath;
STEP 5: Configure the MATLAB Class
Update the generated .m file. See "MATLAB Class Property Rules" below for exact values.
Add minimal comments to the generated .m file for user understanding:
- A file-level comment block stating the class purpose and the peripheral it wraps
- Brief comments on non-obvious property values (e.g., why
DependentLibraries is empty)
- A one-line comment above each public method explaining what it does
STEP 6: Write the C++ Header
Update the generated .h file. See "C++ Header Rules" below and read
references/cpp-patterns.md for templates.
Add minimal comments to the generated .h file for user understanding:
- A file-level comment stating the addon name and peripheral it supports
- A one-line comment above each command ID
#define explaining the command
- A brief comment in
commandHandler for each case describing what it does
STEP 7: Verify
listArduinoLibraries % Must show your library name
a = arduino("<port>", "<board>", "Libraries", "<FolderName>/<ClassName>");
obj = addon(a, "<FolderName>/<ClassName>", <args>);
MATLAB Class Property Rules
| Property | Format | Example | Notes |
|---|
LibraryName | 'FolderName/ClassName' | 'Adafruit/DHT22' | Must match namespace |
DependentLibraries | Cell of MATLAB addon names | {} or {'I2C'} | See decision table below |
LibraryHeaderFiles | '<ArduinoLibFolder>/<header.h>' | 'DHT/DHT.h' | Folder name in CLIRoot + header filename |
CppHeaderFile | fullfile(arduinoio.FilePath(mfilename('fullpath')), 'src', '<Name>.h') | — | Always this pattern |
CppClassName | Must match C++ class name | 'DHT22Base' | Exact match required |
DependentLibraries Decision Table
| Scenario | Value | Reason |
|---|
| GPIO-only peripheral (DHT, NeoPixel) | {} | No MATLAB bus dependency |
| I2C device using SW_I2C (displays, most sensors) | {} | Software I2C bypasses MATLAB's bus |
| I2C device delegating to MATLAB's I2C infrastructure | {'I2C'} | MATLAB manages Wire init and pins |
| SPI device | {} | MATLAB does not own SPI bus |
| Uses another custom addon | {'OtherAddon/Name'} | Chain dependency |
LibraryHeaderFiles Format
Format: '<FolderNameInCLIRoot>/<HeaderFilename>'
% GOOD — folder name matches what's in CLIRoot/user/libraries/
LibraryHeaderFiles = {'DHT/DHT.h'}
LibraryHeaderFiles = {'U8g2/U8g2lib.h'}
LibraryHeaderFiles = {'Adafruit_Motor_Shield_V2_Library/Adafruit_MotorShield.h'}
% BAD — just filename without folder
LibraryHeaderFiles = {'DHT.h'}
% BAD — subdirectory path (NEVER include src/ even if the file physically lives there)
LibraryHeaderFiles = {'U8g2/src/U8g2lib.h'}
% BAD — Arduino library name (Wire is not in CLIRoot/user/libraries)
LibraryHeaderFiles = {'Wire/Wire.h'}
The Arduino build system automatically searches the src/ subdirectory within
the library folder, so NEVER include src/ in this path. This is the most
common mistake — even though headers like U8g2lib.h physically reside in
<LibFolder>/src/, you must write 'U8g2/U8g2lib.h' NOT 'U8g2/src/U8g2lib.h'.
C++ Header Rules
Read references/cpp-patterns.md for full templates. Critical rules:
setup() MUST NOT initialize hardware — calling .begin(), Wire.begin(),
SPI.begin() etc. in setup() causes "Internal error: The initialization of the
server code is incorrect." Simple variable assignments (e.g., cursorRow = 0) are safe.
- Use lazy initialization — init hardware on first command via a dedicated INIT command
sendResponseMsg must send >= 1 byte — sendResponseMsg(cmdID, 0, 0) crashes ARM boards (UNO R4, ESP32)
- NEVER
#include <Wire.h> in your addon header — causes I2C bus conflict with MATLAB.
Your code must never contain this line. Third-party libraries may include it internally
(that is fine — you don't control their code), but YOUR .h file must not.
#include <SPI.h> is safe — MATLAB does not own SPI
- Use angle brackets for third-party headers:
#include <U8g2lib.h>
- Use constructor initializer list for member objects, not in-class initialization
I2C Bus Conflict — Read references/bus-conflicts.md
MATLAB owns the hardware I2C bus (Wire). If your peripheral uses I2C:
- Preferred: Use software I2C constructors (e.g.,
U8G2_..._SW_I2C) with DependentLibraries = {}
- Do NOT call
Wire.begin() directly
- Do NOT
#include <Wire.h> in your own addon header
If the third-party library requires hardware I2C (e.g., SGP30, motor shields): set
DependentLibraries = {'I2C'} and let MATLAB manage the bus. The third-party library
may include Wire.h internally — this is safe because MATLAB has already initialized Wire.
Your C++ code must NOT call Wire.begin() since MATLAB already did.
Common Mistakes
| Mistake | Why It's Wrong | Correct Approach |
|---|
DependentLibraries = {'Wire'} | Wire is not in listArduinoLibraries | Use {} or {'I2C'} |
Third-party lib in +arduinoioaddons/src/ | Wrong location; build can't find it | arduinoio.CLIRoot/user/libraries/ |
LibraryHeaderFiles = {'U8g2lib.h'} | Missing folder prefix | {'U8g2/U8g2lib.h'} |
U8G2_..._HW_I2C constructor | Conflicts with MATLAB's Wire management | Use _SW_I2C constructor |
.begin() calls in setup() | Server handshake fails | Init hardware via command; variable assignments are OK |
sendResponseMsg(cmdID, 0, 0) | Crashes ARM boards (nullptr) | Send >= 1 byte always |
#include <Wire.h> in your addon header | Conflicts with MATLAB I2C server | Omit; use SW_I2C or DependentLibraries = {'I2C'} |
| Installing via Arduino IDE Library Manager | MATLAB uses its own CLIRoot path | Use downloadLibrary() |
Not calling addpath after creating addon | listArduinoLibraries won't find it | addpath + savepath |
Conventions
- Always check native support first, File Exchange second, create last
- Always use
downloadLibrary (R2025a+) or manual copy to CLIRoot for third-party libs
- Always verify with
listArduinoLibraries after registration
- Never call
.begin() or hardware init in setup() — use lazy init via dedicated command
- Always send >= 1 byte in
sendResponseMsg
- Never include
<Wire.h> in your own code — use software I2C or depend on {'I2C'}
- Never put third-party Arduino libraries inside
+arduinoioaddons
- Prefer
createLibraryTemplate (R2025a+) over manual folder creation
Fallback: When Automated Steps Fail or Take Too Long
If listArduinoLibraries hangs, File Exchange search takes too long, downloadLibrary
fails, or createLibraryTemplate fails — fall back to the pre-R2025a manual approach:
- Install library manually: Download the third-party Arduino library .zip and extract
it into
fullfile(arduinoio.CLIRoot, 'user', 'libraries', '<LibName>').
- Create folder structure manually:
<targetDir>/+arduinoioaddons/+<FolderName>/<ClassName>.m
<targetDir>/+arduinoioaddons/+<FolderName>/src/<HeaderName>.h
- Register manually:
addpath('<targetDir>'); savepath;
- Continue from Step 5 (configure MATLAB class and C++ header as normal).
Do not retry failing commands repeatedly. Switch to manual creation and proceed.
Troubleshooting
When errors occur during deployment or usage, read the appropriate reference:
- Compilation or "source not found" errors → read
references/common-errors.md
- Runtime errors ("unable to receive data", "server code incorrect") → read
references/common-errors.md
- Bus conflicts (I2C/SPI/UART) → read
references/bus-conflicts.md
- C++ code patterns (lazy init, response format, includes) → read
references/cpp-patterns.md
Copyright 2026 The MathWorks, Inc.