Skip to main content

Overview

Kinematrix implements a triple include system - a sophisticated three-tier architecture that provides different ways to include modules depending on your use case. This system gives you precise control over compilation behavior and memory usage.

The Three Tiers

Production

KinematrixModules.h
Headers + Implementations
For production builds with full compilation

Development Helper

KinematrixModulesHelper.h
Headers + Implementations
Alternative enable prefix for development

Header-Only

KinematrixModulesNoDef.h
Headers only (no .cpp)
For API exploration without compilation

Architecture Details

Tier 1: Production (KinematrixModules.h)

Includes both .h header files and .cpp implementation files.
KinematrixModules.h
#ifdef ENABLE_MODULE_I2C_SCANNER
#include "../lib/modules/communication/wired/i2c/i2c-scanner.h"
#include "../lib/modules/communication/wired/i2c/i2c-scanner.cpp"
#endif

#ifdef ENABLE_MODULE_PID_CONTROLLER
#include "../lib/modules/control/PIDController.h"
#include "../lib/modules/control/PIDController.cpp"
#endif

#ifdef ENABLE_MODULE_WIFI_HANDLER_V2
#include "../lib/modules/wifi/wifi-handler-v2.h"
#include "../lib/modules/wifi/wifi-handler-v2.cpp"
#endif
Use when: You need full functionality compiled into your application (most common case). Enable prefix: ENABLE_MODULE_* or ENABLE_SENSOR_* Statistics:
  • KinematrixModules.h: 88 modules, 97 .cpp includes ✅
  • KinematrixSensor.h: 78 .cpp includes ✅

Tier 2: Development Helper (KinematrixModulesHelper.h)

Identical structure to production tier, but uses different enable prefix.
KinematrixModulesHelper.h
#ifdef ENABLE_MODULE_HELPER_I2C_SCANNER
#include "../lib/modules/communication/wired/i2c/i2c-scanner.h"
#include "../lib/modules/communication/wired/i2c/i2c-scanner.cpp"
#endif

#ifdef ENABLE_MODULE_HELPER_PID_CONTROLLER
#include "../lib/modules/control/PIDController.h"
#include "../lib/modules/control/PIDController.cpp"
#endif

#ifdef ENABLE_MODULE_HELPER_WIFI_HANDLER_V2
#include "../lib/modules/wifi/wifi-handler-v2.h"
#include "../lib/modules/wifi/wifi-handler-v2.cpp"
#endif
Use when: You need separate enable flags for helper/utility functions distinct from production modules. Enable prefix: ENABLE_MODULE_HELPER_* or ENABLE_SENSOR_HELPER_* Statistics:
  • KinematrixModulesHelper.h: 88 modules, 97 .cpp includes ✅
  • KinematrixSensorHelper.h: 78 .cpp includes ✅

Tier 3: Header-Only (KinematrixModulesNoDef.h)

Includes only .h header files - no implementations compiled.
KinematrixModulesNoDef.h
#ifdef ENABLE_MODULE_NODEF_I2C_SCANNER
#include "../lib/modules/communication/wired/i2c/i2c-scanner.h"
// No .cpp file - just the header!
#endif

#ifdef ENABLE_MODULE_NODEF_PID_CONTROLLER
#include "../lib/modules/control/PIDController.h"
// No .cpp file - declarations only
#endif

#ifdef ENABLE_MODULE_NODEF_WIFI_HANDLER_V2
#include "../lib/modules/wifi/wifi-handler-v2.h"
// No .cpp file - API visible but not compiled
#endif
Use when:
  • Exploring APIs during development
  • Creating mock implementations
  • Reducing compilation time for IntelliSense/autocomplete
  • Documentation generation
Enable prefix: ENABLE_MODULE_NODEF_* or ENABLE_SENSOR_NODEF_* Statistics:
  • KinematrixModulesNoDef.h: 88 modules, 0 .cpp includes ✅
  • KinematrixSensorNoDef.h: 72 .h includes, 0 .cpp includes ✅
NoDef headers provide declarations only. Attempting to use functionality without compiling the .cpp will result in linker errors.

Master Include File

The main Kinematrix.h orchestrates all three tiers:
Kinematrix.h
#pragma once

#pragma message("[INFO]: compiling Kinematrix started")

#include "Arduino.h"

#include "KinematrixDevelopment.h"

#include "KinematrixModules.h"          // Production modules
#include "KinematrixModulesHelper.h"    // Helper modules
#include "KinematrixModulesNoDef.h"     // Header-only modules

#include "KinematrixSensor.h"           // Production sensors
#include "KinematrixSensorHelper.h"     // Helper sensors
#include "KinematrixSensorNoDef.h"      // Header-only sensors

#include "KinematrixAddons.h"

#pragma message("[INFO]: compiling Kinematrix done")

Usage Patterns

Pattern 1: Standard Production Build

Most common use case - compile everything you need:
// Define required modules
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_BME680_V2
#define ENABLE_MODULE_WIFI_HANDLER_V2
#define ENABLE_MODULE_PID_CONTROLLER

// Include main header
#include "Kinematrix.h"

void setup() {
    // All enabled modules fully compiled and available
    SensorModuleV2 sensors;
    BME680SensV2 env;
    PIDController pid;
}

Pattern 2: Development with Helpers

Separate development utilities from production code:
// Production modules
#define ENABLE_MODULE_WIFI_HANDLER_V2
#define ENABLE_MODULE_MQTT_MANAGER

// Development helpers (can be disabled for production)
#define ENABLE_MODULE_HELPER_SERIAL_DEBUGGER_V2
#define ENABLE_MODULE_HELPER_I2C_SCANNER

#include "Kinematrix.h"

void setup() {
    // Production code
    WiFiHandlerV2 wifi;
    MQTTManager mqtt;
    
    // Helper utilities for debugging
    #ifdef ENABLE_MODULE_HELPER_SERIAL_DEBUGGER_V2
    SerialDebuggerV2 debug;
    debug.enable();
    #endif
}

Pattern 3: API Exploration (NoDef)

Explore APIs without compiling implementations:
// Just exploring the API - no compilation overhead
#define ENABLE_MODULE_NODEF_FIREBASE_RTDB_V3
#define ENABLE_MODULE_NODEF_FUZZY_MAMDANI
#define ENABLE_MODULE_NODEF_KNN

#include "KinematrixModulesNoDef.h"  // Headers only!

// IntelliSense/autocomplete works, but linking will fail
// Good for exploring class interfaces during planning

Pattern 4: Selective Inclusion

Manually control which headers to include:
// Don't use main header at all
// #include "Kinematrix.h"  // Skipped

// Manually include exactly what you need
#include "lib/modules/control/PID.h"
#include "lib/modules/control/PID.cpp"

#include "lib/sensors/SensorModuleV2/SensorModule/SensorModuleV2.h"
#include "lib/sensors/SensorModuleV2/SensorModule/SensorModuleV2.cpp"

void setup() {
    // Only PID and SensorModuleV2 available
    // Maximum control, maximum manual effort
}

Include Pattern Examples

Communication Modules

// PRODUCTION PATTERN (.h + .cpp)
#ifdef ENABLE_MODULE_I2C_SCANNER
#include "../lib/modules/communication/wired/i2c/i2c-scanner.h"
#include "../lib/modules/communication/wired/i2c/i2c-scanner.cpp"
#endif

// HELPER PATTERN (.h + .cpp)
#ifdef ENABLE_MODULE_HELPER_I2C_SCANNER
#include "../lib/modules/communication/wired/i2c/i2c-scanner.h"
#include "../lib/modules/communication/wired/i2c/i2c-scanner.cpp"
#endif

// HEADER-ONLY PATTERN (.h only)
#ifdef ENABLE_MODULE_NODEF_I2C_SCANNER
#include "../lib/modules/communication/wired/i2c/i2c-scanner.h"
#endif

Complex Modules (Multiple Files)

Some modules include multiple dependency files:
// Modbus module with multiple components
#ifdef ENABLE_MODULE_MODBUS
#include "../lib/modules/communication/wired/modbus/modbus.h"
#include "../lib/modules/communication/wired/modbus/modbusDevice.h"
#include "../lib/modules/communication/wired/modbus/modbusRegBank.h"
#include "../lib/modules/communication/wired/modbus/modbusSlave.h"
#include "../lib/modules/communication/wired/modbus/modbusDevice.cpp"
#include "../lib/modules/communication/wired/modbus/modbusRegBank.cpp"
#include "../lib/modules/communication/wired/modbus/modbusSlave.cpp"
#endif

Filter Modules (Dynamic Type)

Dynamic type filters with specialized implementations:
#ifdef ENABLE_MODULE_DYNAMIC_TYPE_MOVING_AVERAGE_FILTER
#include "../lib/modules/filter/DynamicTypeMovingAverageFilter.h"
#include "../lib/modules/filter/DynamicTypeMovingAverageFilter.cpp"
#endif

#ifdef ENABLE_MODULE_DYNAMIC_TYPE_KALMAN_FILTER
#include "../lib/modules/filter/DynamicTypeKalmanFilter.h"
#include "../lib/modules/filter/DynamicTypeKalmanFilter.cpp"
#endif

Architecture Consistency

The triple include system maintains perfect consistency across all files:

Module Include Files

KinematrixModules.h
88 modules, 97 .cpp includes (production)
KinematrixModulesHelper.h
88 modules, 97 .cpp includes (helper)
KinematrixModulesNoDef.h
88 modules, 0 .cpp includes (header-only)
All three files maintain the same module list with different prefixes.

Benefits of Triple Include System

Separation of Concerns

Production code, development helpers, and API exploration are cleanly separated with different enable flags.

Faster Development

Use NoDef headers for IntelliSense without compilation overhead during API exploration.

Flexible Builds

Mix and match production, helper, and NoDef modules in the same project as needed.

Easy Debugging

Enable helper modules during development, disable for production builds without code changes.

Recent Improvements

The system was recently audited and perfected:
1

Added Missing Modules

✅ Added SERIAL_CERIA module to all 3 module files
✅ Added STANDARD_SCALER and TRAIN_TEST_SPLIT to Helper & NoDef files
2

Fixed Filter Includes

✅ Added all 6 missing DynamicType filter .cpp includes:
  • DynamicTypeMovingAverageFilter.cpp
  • DynamicTypeExponentialMovingAverageFilter.cpp
  • DynamicTypeHighPassFilter.cpp
  • DynamicTypeMedianFilter.cpp
  • DynamicTypeComplementaryFilter.cpp
  • DynamicTypeBandStopFilter.cpp
3

Achieved Perfect Consistency

✅ All files now follow correct patterns:
  • Production: .h + .cpp
  • Helper: .h + .cpp
  • NoDef: .h only

Compilation Messages

Each tier provides compilation feedback:
[INFO]: compiling Kinematrix started
[INFO]: Including production modules...
[INFO]: Including helper modules...
[INFO]: Including header-only modules...
[INFO]: compiling Kinematrix done

Best Practices

Always use ENABLE_MODULE_* (not HELPER or NODEF) for production releases to ensure all implementations are compiled.
Enable ENABLE_MODULE_HELPER_SERIAL_DEBUGGER_V2 during development, easily disable for production by removing helper defines.
NoDef is perfect for IntelliSense and API browsing, but will cause linker errors if you try to use the functions.
Don’t enable both ENABLE_MODULE_PID and ENABLE_MODULE_NODEF_PID - this causes conflicts. Choose one tier per module.

Advanced Usage

Conditional Development Features

// Production modules always enabled
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_MODULE_WIFI_HANDLER_V2

// Debug helpers only in debug builds
#ifdef DEBUG_BUILD
#define ENABLE_MODULE_HELPER_SERIAL_DEBUGGER_V2
#define ENABLE_MODULE_HELPER_I2C_SCANNER
#endif

#include "Kinematrix.h"

void setup() {
    // Production code always runs
    SensorModuleV2 sensors;
    WiFiHandlerV2 wifi;
    
    // Debug helpers only in debug builds
    #ifdef ENABLE_MODULE_HELPER_SERIAL_DEBUGGER_V2
    SerialDebuggerV2 debug;
    debug.verbose();
    #endif
}

Mock Testing with NoDef

// In test environment - use NoDef for mocking
#define ENABLE_MODULE_NODEF_MQTT_MANAGER
#include "KinematrixModulesNoDef.h"

// Provide your own mock implementation
class MockMQTTManager : public MQTTManager {
public:
    bool connect() override { return true; }  // Always succeeds
    void publish(const char* topic, const char* msg) override {
        Serial.printf("Mock publish: %s = %s\n", topic, msg);
    }
};

void test() {
    MockMQTTManager mqtt;  // Uses your mock, not compiled implementation
}

See Also

Build docs developers (and LLMs) love