The class model¶
CONTRACT · IMPLEMENTATION · STANDALONE · BORDERLINE CASES
MeshCore's 196 classes fall into three kinds: classes that lay down what
another part may expect, classes that implement such an agreement, and classes
that stand on their own. This chapter describes that three-way split, states
what a contract is and is not, and walks through the 119 classes of the shared
tree one by one. The 77 from variants/ appear as a summary at the end.
[!NOTE] Source. This page was verified against the firmware itself:
MeshCorev1.16.0, commit03b6ef4, 28 July 2026 — every class in the tables was checked for file and line number insrc/,examples/andvariants/.
What a contract is¶
A contract is a class that exists solely to lay down what another part may
expect. It holds no working code, only the list of what an implementation must
be able to do, plus sometimes a default answer for when the hardware cannot do
something. In C++ you recognise it by its virtual methods, the mandatory ones
ending in = 0.
Three properties make something a contract:
- It describes, it does not do.
Radiodoes not know how to drive an SX1262; it lays down that there must be something that sends bytes. - The user knows only the contract. Packet handling holds a
Radio*and has no idea which chip hangs off it. - Implementations are interchangeable. Every class that implements the contract can replace any other without the user changing.
The logical side of this story — which agreements exist and what they promise — is in Contracts. Here it is about the classes that carry them.
What is not a contract¶
A base class that holds shared code is not a contract but a common parent.
ESP32Board is such a case: it implements the board contract and offers
code the derived board classes inherit. It therefore sits in group 2, not
group 1.
The distinction is not always sharp. BridgeBase and RadioLibWrapper are
both an implementation and a parent: they implement AbstractBridge and
mesh::Radio respectively, and there are classes hanging under them in turn.
Anyone reading the three-way split as a hard partition runs into trouble with
those two. They sit in group 2 because they implement a contract; that they
also have children of their own changes nothing about that property.
Standalone is everything that is not a contract and implements none
either: classes that do one thing and on which nothing depends. ClientACL
manages the access list, RegionMap converts region codes, Packet is a data
object. They are not replaceable because there is nothing they would have to
replace.
An instructive case is CustomSX1262. It sits in group 3, not group 2. The
class inherits from RadioLib's SX1262 and implements no MeshCore contract;
it is CustomSX1262Wrapper that does, via RadioLibWrapper. That explains
why there are two classes per radio chip: one that adapts the chip driver, one
that pours the result into the MeshCore contract. See Radio
realisation.
The distribution¶
The shared tree counts 119 classes: 14 interface classes, 50 implementation classes and 55 standalone classes.
| Group | Count | Characteristic |
|---|---|---|
| 1 — interface classes | 14 | Virtual methods only, no working code |
| 2 — implementation classes | 50 | Inherits from a class in group 1 |
| 3 — standalone | 55 | No contract, implements none either |
Group 1 — interface classes (14)¶
| Class | Location |
|---|---|
DataStoreHost |
examples/companion_radio/DataStore.h r.8 |
MillisecondClock |
src/Dispatcher.h r.14 |
Radio |
src/Dispatcher.h r.22 |
PacketManager |
src/Dispatcher.h r.85 |
MeshTables |
src/Mesh.h r.16 |
MainBoard |
src/MeshCore.h r.45 |
RTCClock |
src/MeshCore.h r.80 |
RNG |
src/Utils.h r.9 |
AbstractBridge |
src/helpers/AbstractBridge.h r.5 |
BaseSerialInterface |
src/helpers/BaseSerialInterface.h r.7 |
CommonCLICallbacks |
src/helpers/CommonCLI.h r.68 |
SensorManager |
src/helpers/SensorManager.h r.12 |
LocationProvider |
src/helpers/sensors/LocationProvider.h r.6 |
DisplayDriver |
src/helpers/ui/DisplayDriver.h r.6 |
Two things stand out in this list.
SensorManager and LocationProvider are not in src/ but in
src/helpers/. That is not a mistake: they are contracts that only became
necessary once sensors arrived, and they have not been moved to the core.
CommonCLICallbacks and DataStoreHost reverse the dependency. They are
defined by the layer below but implemented by the application above — MyMesh
in examples/simple_repeater/ implements CommonCLICallbacks so that the
control code in src/helpers/CommonCLI.cpp can call something without knowing
which application is running. The lower layer calls the higher one without
knowing it.
Group 2 — implementation classes (50)¶
| Class | Contract | Location | Inherits from |
|---|---|---|---|
ESP32Board |
Board | src/helpers/ESP32Board.h r.18 |
mesh::MainBoard |
MeshadventurerBoard |
Board | src/helpers/MeshadventurerBoard.h r.20 |
ESP32Board |
NRF52Board |
Board | src/helpers/NRF52Board.h r.27 |
mesh::MainBoard |
NRF52BoardDCDC |
Board | src/helpers/NRF52Board.h r.74 |
NRF52Board |
STM32Board |
Board | src/helpers/stm32/STM32Board.h r.6 |
mesh::MainBoard |
TBeamBoard |
Board | src/helpers/esp32/TBeamBoard.h r.91 |
ESP32Board |
BridgeBase |
Bridge | src/helpers/bridges/BridgeBase.h r.21 |
AbstractBridge |
ESPNowBridge |
Bridge | src/helpers/bridges/ESPNowBridge.h r.42 |
BridgeBase |
RS232Bridge |
Bridge | src/helpers/bridges/RS232Bridge.h r.47 |
BridgeBase |
SimpleMeshTables |
Seen table | src/helpers/SimpleMeshTables.h r.11 |
mesh::MeshTables |
LocalIdentity |
Identity | src/Identity.h r.54 |
Identity |
AutoDiscoverRTCClock |
Clock | src/helpers/AutoDiscoverRTCClock.h r.7 |
mesh::RTCClock |
ESP32RTCClock |
Clock | src/helpers/ESP32Board.h r.160 |
mesh::RTCClock |
VolatileRTCClock |
Clock | src/helpers/ArduinoHelpers.h r.6 |
mesh::RTCClock |
ArduinoSerialInterface |
Interface | src/helpers/ArduinoSerialInterface.h r.6 |
BaseSerialInterface |
SerialBLEInterface |
Interface | src/helpers/esp32/SerialBLEInterface.h r.9 |
BaseSerialInterface, BLESecurityCallbacks, BLEServerCallbacks, BLECharacteristicCallbacks |
SerialBLEInterface |
Interface | src/helpers/nrf52/SerialBLEInterface.h r.10 |
BaseSerialInterface |
SerialWifiInterface |
Interface | src/helpers/esp32/SerialWifiInterface.h r.6 |
BaseSerialInterface |
MicroNMEALocationProvider |
Location | src/helpers/sensors/MicroNMEALocationProvider.h r.36 |
LocationProvider |
RAK12500LocationProvider |
Location | src/helpers/sensors/EnvironmentSensorManager.cpp r.177 |
LocationProvider |
BaseChatMesh |
Mesh | src/helpers/BaseChatMesh.h r.59 |
mesh::Mesh |
MyMesh |
Mesh | examples/simple_secure_chat/main.cpp r.73 |
BaseChatMesh, ContactVisitor |
MyMesh |
Mesh | examples/simple_repeater/MyMesh.h r.83 |
mesh::Mesh, CommonCLICallbacks |
MyMesh |
Mesh | examples/simple_room_server/MyMesh.h r.91 |
mesh::Mesh, CommonCLICallbacks |
MyMesh |
Mesh | examples/simple_sensor/main.cpp r.8 |
SensorMesh |
MyMesh |
Mesh | examples/companion_radio/MyMesh.h r.87 |
BaseChatMesh, DataStoreHost |
SensorMesh |
Mesh | examples/simple_sensor/SensorMesh.h r.49 |
mesh::Mesh, CommonCLICallbacks |
ArduinoMillis |
Millisecond clock | src/helpers/ArduinoHelpers.h r.22 |
mesh::MillisecondClock |
StaticPoolPacketManager |
Packet pool | src/helpers/StaticPoolPacketManager.h r.21 |
mesh::PacketManager |
CustomLLCC68Wrapper |
Radio | src/helpers/radiolib/CustomLLCC68Wrapper.h r.7 |
RadioLibWrapper |
CustomLR1110Wrapper |
Radio | src/helpers/radiolib/CustomLR1110Wrapper.h r.7 |
RadioLibWrapper |
CustomSTM32WLxWrapper |
Radio | src/helpers/radiolib/CustomSTM32WLxWrapper.h r.8 |
RadioLibWrapper |
CustomSX1262Wrapper |
Radio | src/helpers/radiolib/CustomSX1262Wrapper.h r.11 |
RadioLibWrapper |
CustomSX1268Wrapper |
Radio | src/helpers/radiolib/CustomSX1268Wrapper.h r.11 |
RadioLibWrapper |
CustomSX1276Wrapper |
Radio | src/helpers/radiolib/CustomSX1276Wrapper.h r.10 |
RadioLibWrapper |
ESPNOWRadio |
Radio | src/helpers/esp32/ESPNOWRadio.h r.5 |
mesh::Radio |
RadioLibWrapper |
Radio | src/helpers/radiolib/RadioLibWrappers.h r.6 |
mesh::Radio |
E213Display |
Display | src/helpers/ui/E213Display.h r.12 |
DisplayDriver |
E290Display |
Display | src/helpers/ui/E290Display.h r.12 |
DisplayDriver |
GxEPDDisplay |
Display | src/helpers/ui/GxEPDDisplay.h r.19 |
DisplayDriver |
LGFXDisplay |
Display | src/helpers/ui/LGFXDisplay.h r.12 |
DisplayDriver |
NullDisplayDriver |
Display | src/helpers/ui/NullDisplayDriver.h r.5 |
DisplayDriver |
SSD1306Display |
Display | src/helpers/ui/SSD1306Display.h r.18 |
DisplayDriver |
ST7735Display |
Display | src/helpers/ui/ST7735Display.h r.10 |
DisplayDriver |
ST7789Display |
Display | src/helpers/ui/ST7789Display.h r.9 |
DisplayDriver |
ST7789LCDDisplay |
Display | src/helpers/ui/ST7789LCDDisplay.h r.10 |
DisplayDriver |
U8g2Display |
Display | src/helpers/ui/U8g2Display.h r.19 |
DisplayDriver |
EnvironmentSensorManager |
Sensor management | src/helpers/sensors/EnvironmentSensorManager.h r.7 |
SensorManager |
RadioNoiseListener |
Entropy | src/helpers/radiolib/RadioLibWrappers.h r.74 |
mesh::RNG |
StdRNG |
Entropy | src/helpers/ArduinoHelpers.h r.27 |
mesh::RNG |
[!NOTE] The Inherits from column gives the base classes as they appear in the declaration, without the access level. One exception is worth mentioning:
NRF52BoardDCDCinheritsvirtual public NRF52Board. That virtual inheritance is needed because thirty board classes invariants/hang under it and reachNRF52Boardalong two paths; withoutvirtualeach of those boards would get two copies of the base class.
SerialBLEInterface and MyMesh appear more than once. That is not an error
in the table: they are different classes with the same name, in different
files, and each build compiles exactly one of them. SerialBLEInterface
exists twice — one for ESP32, one for nRF52 — and MyMesh five times, one per
application that needs one.
Group 3 — standalone (55)¶
| Class | Location |
|---|---|
AbstractUITask |
examples/companion_radio/AbstractUITask.h r.25 |
DataStore |
examples/companion_radio/DataStore.h r.16 |
SplashScreen |
examples/companion_radio/ui-new/UITask.cpp r.34 |
HomeScreen |
examples/companion_radio/ui-new/UITask.cpp r.86 |
MsgPreviewScreen |
examples/companion_radio/ui-new/UITask.cpp r.466 |
UITask |
examples/companion_radio/ui-new/UITask.h r.25 |
Button |
examples/companion_radio/ui-orig/Button.h r.12 |
UITask |
examples/companion_radio/ui-orig/UITask.h r.17 |
ScrollingStatusBar |
examples/companion_radio/ui-tiny/ScrollingStatusBar.h r.18 |
SplashScreen |
examples/companion_radio/ui-tiny/UITask.cpp r.34 |
HomeScreen |
examples/companion_radio/ui-tiny/UITask.cpp r.90 |
UITask |
examples/companion_radio/ui-tiny/UITask.h r.28 |
KissModem |
examples/kiss_modem/KissModem.h r.100 |
RateLimiter |
examples/simple_repeater/RateLimiter.h r.5 |
UITask |
examples/simple_repeater/UITask.h r.6 |
UITask |
examples/simple_room_server/UITask.h r.6 |
TimeSeriesData |
examples/simple_sensor/TimeSeriesData.h r.11 |
UITask |
examples/simple_sensor/UITask.h r.6 |
Dispatcher |
src/Dispatcher.h r.116 |
Identity |
src/Identity.h r.11 |
GroupChannel |
src/Mesh.h r.7 |
Mesh |
src/Mesh.h r.26 |
Packet |
src/Packet.h r.42 |
Utils |
src/Utils.h r.19 |
AdvertDataBuilder |
src/helpers/AdvertDataHelpers.h r.19 |
AdvertDataParser |
src/helpers/AdvertDataHelpers.h r.43 |
AdvertTimeHelper |
src/helpers/AdvertDataHelpers.h r.68 |
ContactVisitor |
src/helpers/BaseChatMesh.h r.23 |
ContactsIterator |
src/helpers/BaseChatMesh.h r.30 |
ClientACL |
src/helpers/ClientACL.h r.40 |
CommonCLI |
src/helpers/CommonCLI.h r.117 |
IdentityStore |
src/helpers/IdentityStore.h r.14 |
RTC_RX8130CE |
src/helpers/RTC_RX8130CE.h r.9 |
RefCountedDigitalPin |
src/helpers/RefCountedDigitalPin.h r.5 |
BufStream |
src/helpers/RegionMap.cpp r.7 |
RegionMap |
src/helpers/RegionMap.h r.23 |
PacketQueue |
src/helpers/StaticPoolPacketManager.h r.5 |
StatsFormatHelper |
src/helpers/StatsFormatHelper.h r.5 |
TransportKeyStore |
src/helpers/TransportKeyStore.h r.16 |
StrHelper |
src/helpers/TxtDataHelpers.h r.12 |
CustomLLCC68 |
src/helpers/radiolib/CustomLLCC68.h r.8 |
CustomLR1110 |
src/helpers/radiolib/CustomLR1110.h r.6 |
CustomSTM32WLx |
src/helpers/radiolib/CustomSTM32WLx.h r.8 |
CustomSX1262 |
src/helpers/radiolib/CustomSX1262.h r.8 |
CustomSX1268 |
src/helpers/radiolib/CustomSX1268.h r.8 |
CustomSX1276 |
src/helpers/radiolib/CustomSX1276.h r.11 |
LPPReader |
src/helpers/sensors/LPPDataHelpers.h r.66 |
LPPWriter |
src/helpers/sensors/LPPDataHelpers.h r.175 |
GenericVibration |
src/helpers/ui/GenericVibration.h r.21 |
MomentaryButton |
src/helpers/ui/MomentaryButton.h r.11 |
String |
src/helpers/ui/OLEDDisplay.h r.50 |
OLEDDisplay |
src/helpers/ui/OLEDDisplay.h r.159 |
OLEDDisplay |
src/helpers/ui/OLEDDisplay.h r.161 |
ST7789Spi |
src/helpers/ui/ST7789Spi.h r.96 |
UIScreen |
src/helpers/ui/UIScreen.h r.17 |
[!NOTE]
OLEDDisplayappears twice insrc/helpers/ui/OLEDDisplay.h, on lines 159 and 161, behind an#if— one version inherits fromStream.Stringon line 50 in that same file is a forward reference from vendored code. Neither is MeshCore design but code adopted from ThingPulse; see The source tree.
The 77 from variants/¶
variants/ counts 77 class declarations under 73 unique names — four names
occur in more than one variant directory. They are not written out one by one,
because they all do the same thing: implement a contract with the pin
assignment of one board.
| Contract being implemented | Classes |
|---|---|
| Board | 65 |
| Sensor management | 7 |
| Display | 3 |
| Entropy | 2 |
The 65 board classes all implement the same contract in the same way. Four of
them are worth naming separately, because they are the only RP2040 board
classes: that family is the only one without a shared board class in
src/helpers/.
| RP2040 board class | Location |
|---|---|
RAK11310Board |
variants/rak11310/RAK11310Board.h r.15 |
PicoWBoard |
variants/rpi_picow/PicoWBoard.h r.11 |
WaveshareBoard |
variants/waveshare_rp2040_lora/WaveshareBoard.h r.27 |
XiaoRP2040Board |
variants/xiao_rp2040/XiaoRP2040Board.h r.25 |
All other board classes inherit from a shared parent — 30 from
NRF52BoardDCDC, 23 from ESP32Board, 3 from NRF52Board, 3 from
STM32Board, plus a few from HeltecV3Board and TBeamBoard. These four
inherit from mesh::MainBoard directly and therefore write out for themselves
what the other 61 get from their parent.
Recomputing¶
The counts in this chapter come from tools/design-overview.py:
python3 tools/design-overview.py /path/to/MeshCore --classes
The script counts every line of the form class Name { … or
class Name : base { … with the brace on the same line. struct does not
count, and neither do forward declarations without a body.
Sources¶
- MeshCore
03b6ef4—src/Dispatcher.h - MeshCore
03b6ef4—src/MeshCore.h - MeshCore
03b6ef4—src/helpers/ - MeshCore
03b6ef4—src/helpers/NRF52Board.h - MeshCore
03b6ef4—variants/
Translated from Dutch by Anthropic Claude