A modern, robust communication protocol for the age of AI
An enhanced Arduino library for CAN Bus communication with built-in publish/subscribe protocol.
Based on the excellent arduino-CAN library by Sandeep Mistry
β¨ All original arduino-CAN features - Full compatibility with existing CAN applications
π₯ NEW: Pub/Sub Protocol - Complete publish/subscribe messaging system:
- Broker-Client Architecture - Central broker manages topic subscriptions and message routing
- Topic-Based Messaging - Publish and subscribe to named topics
- Direct Messaging - Send messages directly to the broker or specific clients
- Automatic Client ID Assignment - Plug-and-play client connection (sequential IDs: 1, 2, 3, ...)
- β‘ Persistent ID Assignment - Clients with serial numbers always get the same ID across reconnections
- π Automatic Subscription Restoration - Clients automatically restore subscriptions after power cycles
- Serial Number Registration - Register clients using MAC addresses, chip IDs, or custom identifiers
- πΎ Flash Memory Storage - Client ID mappings, subscriptions, and topic names survive power cycles (ESP32 NVS / Arduino EEPROM)
- Extended Frame Support - Handles messages longer than 8 bytes automatically
- Callback-Based API - Event-driven message handling
- Multiple Examples - Ready-to-use broker, client, and sensor node examples
- Microchip MCP2515 based boards/shields
- Espressif ESP32's built-in SJA1000 compatible CAN controller with an external 3.3V CAN transceiver
| Microchip MCP2515 | Arduino |
|---|---|
| VCC | 5V |
| GND | GND |
| SCK | SCK |
| SO | MISO |
| SI | MOSI |
| CS | 10 |
| INT | 2 |
CS and INT pins can be changed by using CAN.setPins(cs, irq). INT pin is optional, it is only needed for receive callback mode. If INT pin is used, it must be interrupt capable via attachInterrupt(...).
NOTE: Logic level converters must be used for boards which operate at 3.3V.
Requires an external 3.3V CAN transceiver, such as a TI SN65HVD230.
| CAN transceiver | ESP32 |
|---|---|
| 3V3 | 3V3 |
| GND | GND |
| CTX | 5 |
| CRX | 4 |
CTX and CRX pins can be changed by using CAN.setPins(rx, tx); but make sure these are the CAN PINs on the board. Not all pins can be CAN.
- Choose
Sketch->Include Library->Manage Libraries... - Type
CANinto the search box. - Click the row to select the library.
- Click the
Installbutton to install the library.
cd ~/Documents/Arduino/libraries/
git clone https://github.com/sandeepmistry/arduino-CAN CAN#include <SuperCANBus.h>
CANPubSubBroker broker(CAN);
void setup() {
Serial.begin(115200);
CAN.begin(500E3);
broker.begin();
broker.onPublish([](uint16_t hash, const String& topic, const String& msg) {
Serial.print("Message on ");
Serial.print(topic);
Serial.print(": ");
Serial.println(msg);
});
Serial.println("Broker ready!");
}
void loop() {
broker.loop();
}#include <SuperCANBus.h>
CANPubSubClient client(CAN);
void setup() {
Serial.begin(115200);
CAN.begin(500E3);
client.onMessage([](uint16_t hash, const String& topic, const String& msg) {
Serial.print("Received [");
Serial.print(topic);
Serial.print("]: ");
Serial.println(msg);
});
if (client.begin()) {
client.subscribe("sensors/temperature");
Serial.println("Connected!");
}
}
void loop() {
client.loop();
// Publish every 5 seconds
static unsigned long lastPublish = 0;
if (millis() - lastPublish > 5000) {
client.publish("sensors/temperature", "25.5");
lastPublish = millis();
}
}#include <SuperCANBus.h>
CANPubSubClient client(CAN);
String SERIAL_NUMBER = "ESP32_ABC123"; // Or use MAC/chip ID
void setup() {
Serial.begin(115200);
CAN.begin(500E3);
// Connect with serial number - same ID every time!
if (client.begin(SERIAL_NUMBER)) {
Serial.print("Connected with persistent ID: ");
Serial.println(client.getClientId(), DEC);
client.subscribe("sensors/temperature");
}
}
void loop() {
client.loop();
}- API.md - Original CAN bus API documentation
- PUBSUB_PROTOCOL.md - Complete pub/sub protocol documentation
- PUBSUB_API.md - Pub/Sub API reference
- SERIAL_NUMBER_MANAGEMENT.md - Client ID persistence with serial numbers
- FLASH_STORAGE.md - Flash memory storage guide
- GETTING_STARTED.md - Quick start tutorial
- ARCHITECTURE.md - System architecture
- QUICK_REFERENCE.md - Quick reference cheat sheet
See the arduino-CAN examples for basic CAN usage.
- Broker - Pub/Sub broker with serial interface
- Client - Interactive pub/sub client
- BrokerWithSerial - Broker with serial number management and flash storage
- ClientWithSerial - Client with persistent ID using serial number
- SubscriptionRestore - β Demo of automatic subscription restoration (recommended starting point!)
- StorageTest - Flash storage testing and verification
- Complete - Combined broker/client example (compile-time selectable)
- SensorNode - Real-world sensor node with periodic publishing
The killer feature! Clients with serial numbers get automatic subscription restoration:
// First boot
client.begin("ESP32_ABC123");
client.subscribe("sensors/temp");
client.subscribe("alerts/critical");
// Broker stores everything in flash β
// After power cycle or reset...
client.begin("ESP32_ABC123");
// β¨ Subscriptions automatically restored!
// β¨ Same client ID!
// β¨ Ready to receive messages immediately!
// NO manual subscribe() calls needed!- Automatic client ID assignment (sequential: 1, 2, 3, ... displayed in decimal)
- β‘ Persistent ID management - Serial number-based client registration
- πΎ Flash memory storage - Mappings, subscriptions, and topic names survive power loss and resets
- π Subscription restoration - Automatically restores subscriptions when clients reconnect WITHOUT ANY CODE CHANGES
- Topic name preservation - Full topic names stored and restored (not just hashes)
- Topic subscription management with topic name storage
- Message routing to subscribers
- Direct messaging support (supports extended frames for long messages)
- Client connection tracking
- Broadcast messaging
- Client registration/unregistration API
- Query clients by serial number or ID
- Automatic connection with ID request
- β‘ Persistent ID registration using serial numbers (MAC, chip ID, custom)
- π Automatic subscription restoration - Subscriptions restored after power cycle ZERO CODE CHANGES NEEDED
- Reconnection-friendly - Same ID and subscriptions every time
- Topic name preservation - Full topic names maintained across reboots
- Subscribe/unsubscribe to topics
- Publish messages to topics (supports extended frames for long messages)
- Send direct messages to broker (supports extended frames for long messages)
- Automatic ping/pong for connection monitoring with configurable timeouts
- Event callbacks for all message types
- Topic name tracking for readable display
SUBSCRIBE/UNSUBSCRIBE- Topic subscription managementPUBLISH- Publish message to topicTOPIC_DATA- Receive subscribed topic dataDIRECT_MSG- Direct node-to-broker communicationPING/PONG- Connection monitoringACK- Message acknowledgment
The pub/sub protocol can be configured by modifying constants in CANPubSub.h:
#define MAX_SUBSCRIPTIONS 20 // Max unique topics
#define MAX_SUBSCRIBERS_PER_TOPIC 10 // Max subscribers per topic
#define MAX_CLIENT_TOPICS 10 // Max topics per client- Broker starts and listens for client connections (broker is always ID 0)
- Client connects by requesting an ID from the broker
- Broker assigns a unique ID (starting from 1: first client gets 1, second gets 2, etc.)
- Client subscribes to topics by sending topic hashes
- Any client publishes a message to a topic
- Broker routes the message to all subscribers
- Clients receive messages via callbacks
- Client sends ID request with serial number (e.g., "ESP32_ABC123")
- Broker checks if serial number is already registered
- If YES β Returns existing ID from flash memory
- If NO β Assigns new ID (starting from 1) and stores mapping to flash
- Client receives same ID every time it reconnects
- Broker automatically restores all previous subscriptions for that client
- Client receives subscription notifications with topic names
- Client is fully restored with same ID and all subscriptions intact
Topics are hashed to 16-bit values for efficient CAN bus transmission. Extended frames are automatically used for messages longer than 8 bytes.
- Getting Started Guide
- Quick Reference
- Pub/Sub API
- Pub/Sub Protocol
- Serial Number Management
- Ping/Pong Connection Monitoring
- Peer-to-Peer Messaging
- Extended Frames
- Flash Storage
- Architecture
| Feature | Traditional CAN | Pub/Sub Protocol |
|---|---|---|
| Message routing | Manual ID management | Automatic topic-based |
| Client IDs | Hard-coded, manual assignment | Dynamic or persistent (serial-based) |
| Reconnection | May conflict with IDs | Same ID guaranteed with serial number |
| Storage | No persistence | Flash memory for ID mappings |
| Scalability | Fixed IDs, complex wiring | Dynamic subscription |
| Flexibility | Hard-coded destinations | Runtime topic subscription |
| Setup complexity | High | Low |
| Code reusability | Low | High |
This library is fully backward compatible with arduino-CAN. To use the pub/sub features:
- Replace
#include <CAN.h>with#include <SuperCANBus.h> - Add pub/sub broker or client code
- All existing CAN code continues to work unchanged
This library is licensed under the MIT Licence.
Based on arduino-CAN by Sandeep Mistry.
Pub/Sub protocol extensions by Juan Pablo Risso.