A command-based messaging library for Arduino that enables structured communication between microcontrollers and PC host applications over serial, Bluetooth, or TCP/IP.
The protocol is simple — <cmdId>,<arg1>,<arg2>,...; — but the library handles typed arguments, binary encoding, callback dispatch, send/receive queues, connection management, and auto-reconnect for you.
CmdMessenger includes first-class host-side clients for three languages. Pick the one that fits your project.
| Language | Location | Status | Transports |
|---|---|---|---|
| C# / .NET | extras/CSharp/ |
✅ Stable | Serial, TCP, Bluetooth (Windows) |
| Python | extras/Python/ |
🚧 Alpha | Serial, TCP |
| TypeScript / Node.js | extras/TypeScript/ |
🚧 Initial | Serial, TCP |
All three clients share the same protocol, the same queue strategies, and the same test scenarios — a sketch that works with the C# client will work unchanged with the Python or TypeScript client.
Arduino Library Manager (recommended):
Search for "CmdMessenger" in Sketch → Include Library → Manage Libraries.
PlatformIO:
lib_deps = CmdMessenger#include <CmdMessenger.h>
enum { kSetLed, kStatus };
CmdMessenger cmdMessenger = CmdMessenger(Serial);
void onSetLed() {
bool ledState = cmdMessenger.readBoolArg();
digitalWrite(LED_BUILTIN, ledState ? HIGH : LOW);
cmdMessenger.sendCmd(kStatus, ledState);
}
void setup() {
Serial.begin(115200);
cmdMessenger.attach(kSetLed, onSetLed);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
cmdMessenger.feedinSerialData();
}Send 0,1; from the Serial Monitor → LED on, board replies 1,1;.
Requires .NET 8 SDK or later. Works on Windows, Linux and macOS.
using CommandMessenger;
using CommandMessenger.Transport.Serial;
var transport = new SerialTransport
{
CurrentSerialSettings = { PortName = "COM3", BaudRate = 115200, DtrEnable = true }
};
var messenger = new CmdMessenger(transport);
messenger.Connect();
messenger.Attach(1 /* kStatus */, cmd =>
Console.WriteLine("LED is now " + (cmd.ReadBoolArg() ? "on" : "off")));
// Send command 0 (kSetLed) with argument true, wait for ACK command 1
var ack = await messenger.SendCommandAsync(new SendCommand(0, true, 1, 500));Run the paired samples:
cd extras/CSharp/Samples/SendAndReceive
dotnet runThe sample projects are in extras/CSharp/Samples/ — numbered 1–10 matching the Arduino examples.
UI thread marshalling — if calling from a WinForms/WPF/MAUI UI thread, set:
messenger.SynchronizationContext = SynchronizationContext.Current;This routes all callbacks to the UI thread automatically.
Requires Python 3.9+. Works on Windows, Linux and macOS.
cd extras/Python
build.bat # Windows
bash build.sh # macOS / LinuxThis creates a .venv, installs dependencies, and installs py-cmdmessenger in editable mode.
from cmd_messenger import CmdMessenger, SendCommand, BoardType
from cmd_messenger.transport.serial import SerialTransport, SerialSettings
settings = SerialSettings(port_name="COM3", baud_rate=115200)
transport = SerialTransport(settings)
with CmdMessenger(transport, board_type=BoardType.BIT_16) as messenger:
messenger.connect()
def on_status(cmd):
print("LED is now", cmd.read_bool_arg())
messenger.attach(1, on_status)
messenger.send_command(SendCommand(0, True)) # kSetLed, true
import time; time.sleep(0.5)See extras/Python/ for the full library and console + web-UI samples.
Requires Node.js 22+.
cd extras/TypeScript
npm ci
npm run buildimport { BoardType, CmdMessenger, SendCommand } from 'cmd-messenger';
import { SerialTransport, SerialSettings } from 'cmd-messenger/transport/serial';
const transport = new SerialTransport(new SerialSettings({ portName: 'COM3', baudRate: 115200 }));
const messenger = new CmdMessenger(transport, BoardType.Bit16);
await messenger.connect();
messenger.attach(1 /* kStatus */, cmd =>
console.log('LED is now', cmd.readBoolArg()));
await messenger.sendCommand(new SendCommand(0, true)); // kSetLed, trueSee extras/TypeScript/ for the full library.
- All primary types — bool, int16, int32, float, double, char, string, binary
- Plain text or binary encoding — human-readable or efficient, your choice
- Callback-driven —
attach(cmdId, handler)for each command ID sendArg()family — send arguments without field separator for custom packet formats- Configurable delimiters — field separator (
,) and command separator (;) are settable - Multiple transports — Serial USB, Bluetooth (HC-05/HC-06), TCP over WiFi/Ethernet
- Any
Streaminterface — works withSerial,SoftwareSerial,WiFiClient, BLE UART, etc.
| Platform | Notes |
|---|---|
| Arduino AVR (Uno, Mega, Nano, Pro Micro…) | Full support |
| Arduino ARM (Due, Zero) | Full support |
| ESP8266 / ESP32 | Full support; use RtsEnable = true on the C# side for auto-reset |
| Teensy 3.x / 4.x | Full support |
| Digispark (attiny85) | Supported; requires Energia or Digispark core |
Any Stream-compatible board |
Should work |
Requires Arduino IDE 1.5+ or PlatformIO.
| Sketch | Demonstrates | C# | Python | TypeScript |
|---|---|---|---|---|
| Receive | Receive commands | ✅ | ✅ | ✅ |
| SendAndReceive | Bidirectional | ✅ | ✅ | ✅ |
| SendAndReceiveArguments | Typed arguments | ✅ | ✅ | ✅ |
| SendAndReceiveBinaryArguments | Binary encoding | ✅ | ✅ | ✅ |
| DataLogging | Live charting | ✅ | ✅ | — |
| ArduinoController | GUI + queued commands | ✅ | ✅ | — |
| TemperatureControl | PID + live chart | ✅ | ✅ | — |
| SimpleWatchdog | Auto-reconnect | ✅ | ✅ | ✅ |
| ConsoleShell | Serial shell (no PC app) | — | — | — |
| SendWithoutSeparator | Custom packet format | — | — | — |
| Symptom | Fix |
|---|---|
| Cannot connect | Verify COM port and baud rate. Test with the ConsoleShell sketch + Serial Monitor. |
| Callbacks not firing | Enable logging on the C# side; check DtrEnable = true for boards that need DTR reset. |
| ESP32 / ESP8266 not resetting | Set both DtrEnable = true and RtsEnable = true on the C# / Python side. |
| PlatformIO install fails | Ensure your lib_deps uses CmdMessenger (no extra dependencies are required). |
| Pro Micro (32u4) | Native USB — set DtrEnable = true. |
| Version | Author |
|---|---|
| Messenger (original) | Thomas Ouellet Fredericks |
| CmdMessenger v1 | Neil Dudman |
| CmdMessenger v2 | Dreamcat4 |
| CmdMessenger v3–v4 | Thijs Elenbaas & Valeriy Kucherenko |
| CmdMessenger v5 | Thijs Elenbaas |
MIT — Copyright © 2013–2026 Thijs Elenbaas.
Changelog
New host clients:
- [Python] First-class
py-cmdmessengerlibrary — full parity with C# (serial + TCP, all queue strategies, connection manager, watchdog, auto-reconnect,JsonSerialConnectionStorer) - [TypeScript] First-class Node.js client — serial + TCP, all queue strategies, connection manager,
JsonSerialConnectionStorer
C# — async core rewrite:
SendCommandAsync(cmd)/await messenger.SendCommandAsync(...)— proper async ACK wait viaTaskCompletionSourceper pending ACK; no busy-spin, no receive-queue suspensionAsyncWorkerreplaced withasync Taskdrain loop +CancellationTokenSourceEventWaiterreplaced withSemaphoreSlim-based async waiterSystem.Threading.Channelsused for queue backing
C# — modernisation:
- Target framework:
netstandard2.0(covers .NET Framework 4.6.1+, .NET Core 2+, .NET 5–11, Linux, macOS) System.Windows.Formsdependency removed from core libraryControlToInvokeOn(WinForms-only) replaced withSynchronizationContext— works with WinForms, WPF, MAUI, console, any platform- All project files migrated from old verbose MSBuild format to SDK-style
- Console samples target
net8.0; WinForms samples targetnet8.0-windows BinaryFormatterin storers replaced with JSON (JsonSerialConnectionStorer)ScottPlot 5replacesZedGraphin charting samples- Callback exceptions now isolated — unhandled exceptions fire
CallbackExceptionevent instead of crashing the receive pump ReadCharArg()added toReceivedCommandRead(string format)format-string reader added (e.g.cmd.Read("ifs"))IEnumerable<string>added toReceivedCommand(foreach over raw arguments)RtsEnableadded toSerialSettings(ESP32/ESP8266 auto-reset)SerialConnectionStorernow persists to JSON;BinaryFormatterremoved- 247 xUnit unit tests (was 98)
Arduino firmware:
comms->print(cmdId, DEC)— fixes compile error on Digispark/Energia non-standard coresCMDMESSENGER_MAXCALLBACKSused consistently throughout
Bug fixes:
- [C#/#22] Commands arriving during ACK wait were silently dropped — fixed; non-ACK commands always queued
- [C#/#29]
ObjectDisposedExceptionon sleep/wake / hot-unplug — fixed via proper disposal order - [C#/#30] Receive queue not signalled after suspend/resume — fixed
Metadata:
library.propertiesversion aligned withlibrary.json- Spurious
Adafruit MAX31855 librarydependency removed fromlibrary.json
- [Arduino]
sendArg(),sendSciArg(),sendBinArg()— send arguments without field separator - [Arduino]
unescape()applied to string reads (fixes escaped delimiter handling) - [Arduino] Bounds-checked
readBinArg<T>()withArgOkvalidation - [Arduino] Configurable
CMDMESSENGER_MAXCALLBACKSwith compile-time zero-callback support - [Arduino] ESP8266/ESP32 type fixes
- [Arduino]
LastArgLengthtracking in argument parser - Fix:
startCommandinitialization - Fix:
comms->printused consistently (no directSerial.print) - Fix: LICENSE.md reformatted for GitHub detection
- [Arduino] Additional autoConnect sample
- [.NET] Full threading redesign
- [.NET] AutoConnect & watchdog functionality
- [.NET] Tested Linux compatibility
- [.NET] Visual Basic samples
- [.NET] Native Bluetooth support (Windows only)
- [Arduino] Bugfix: ~1 in 1000 commands failed with multiple binary parameters
- [Arduino] Bugfix: binary send of non-number compile error
- [Arduino] Feature: send command without argument
- [Arduino] Feature: scientific notation for full float range
- [.NET] Unit tests, performance improvements
- [Arduino] Console shell sample, minor performance improvement
- [Arduino] Bug-fix in receiving binary values
- [.NET] 100× faster communication, single-core support
- [Arduino] Speed improvements for Teensy
- [All] Clean transport layer interface (Bluetooth, ZigBee, Web)
- [.NET] Adaptive throttling, smart queuing
- Multi-argument commands, type arguments, binary data, escaping, acknowledgements
- Arduino IDE 022, configurable separators, Base-64 encoding, Serial Monitor debugging
