Overview
This guide will walk you through building a complete IoT environmental monitoring system that:
Reads temperature, humidity, pressure, and gas data from a BME680 sensor
Applies PID control to maintain target temperature
Sends data to an MQTT broker every 5 seconds
Displays real-time data on serial monitor
This example demonstrates Kinematrix’s modular approach by combining sensor, control, and communication modules with minimal code.
Hardware Requirements
Microcontroller ESP32 DevKit v1 (or any ESP32 board)
Sensor BME680 environmental sensor (I2C)
Actuator Heating element + MOSFET (optional, for PID control)
Wiring Diagram
BME680 Sensor → ESP32
VCC → 3.3V
GND → GND
SCL → GPIO 22 (I2C SCL)
SDA → GPIO 21 (I2C SDA)
Heating Element (Optional)
MOSFET Gate → GPIO 25 (PWM)
MOSFET Source → GND
Heater → MOSFET Drain + 12V
Project Setup
Create New Project
Create a new PlatformIO project or Arduino sketch: # PlatformIO
pio project init --board esp32doit-devkit-v1
# Or create a new sketch in Arduino IDE
Configure platformio.ini
For PlatformIO users, add this configuration: [env:esp32dev]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
lib_deps =
bblanchon/ArduinoJson@^7.0.0
adafruit/Adafruit Unified Sensor@^1.1.0
adafruit/Adafruit BME680 Library@^2.0.0
knolleary/PubSubClient@^2.8.0
monitor_speed = 115200
Install Dependencies
For Arduino IDE users, install these libraries via Library Manager:
ArduinoJson (v7+)
Adafruit BME680 Library
Adafruit Unified Sensor
PubSubClient
Complete Code Example
Here’s the complete working example:
// Enable required Kinematrix modules
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_BME680_V2
#define ENABLE_MODULE_WIFI_HANDLER_V2
#define ENABLE_MODULE_MQTT_MANAGER
#define ENABLE_MODULE_PID_CONTROLLER
#include "Kinematrix.h"
// WiFi credentials
const char * WIFI_SSID = "YourWiFiSSID" ;
const char * WIFI_PASSWORD = "YourWiFiPassword" ;
// MQTT broker configuration
const char * MQTT_BROKER = "mqtt.example.com" ; // or "192.168.1.100"
const int MQTT_PORT = 1883 ;
const char * MQTT_TOPIC_TEMP = "sensors/temperature" ;
const char * MQTT_TOPIC_HUMIDITY = "sensors/humidity" ;
const char * MQTT_TOPIC_PRESSURE = "sensors/pressure" ;
// PID control settings
const float TARGET_TEMP = 25.0 ; // Target temperature in °C
const int HEATER_PIN = 25 ; // PWM output pin for heater control
// Create module instances
SensorModuleV2 sensors;
BME680SensV2 environmental;
WiFiHandlerV2 wifiHandler;
MQTTManager mqtt;
PIDController pid ( 2.0 , 0.1 , 0.5 , 0.1 , 0 , 255 ); // Kp, Ki, Kd, dt, min, max
// Timing variables
unsigned long lastMQTTPublish = 0 ;
const unsigned long MQTT_INTERVAL = 5000 ; // Publish every 5 seconds
void setup () {
Serial . begin ( 115200 );
delay ( 1000 );
Serial . println ( " \n === Kinematrix IoT Environmental Monitor ===" );
Serial . println ( "Version: 0.0.28 Beta \n " );
// Initialize heater control pin
pinMode (HEATER_PIN, OUTPUT);
// Step 1: Initialize BME680 sensor
Serial . println ( "[1/4] Initializing BME680 sensor..." );
JsonDocument doc;
environmental . setDocumentValue ( & doc);
environmental . setDocument ( "bme680" );
environmental . setUpdateInterval ( 1000 ); // Update every second
if ( environmental . init ()) {
Serial . println ( " ✓ BME680 initialized successfully" );
} else {
Serial . println ( " ✗ BME680 initialization failed!" );
Serial . println ( " Check wiring and I2C address" );
while ( 1 ) delay ( 100 ); // Halt on sensor error
}
// Add sensor to sensor manager
sensors . addSensor ( "env" , & environmental);
sensors . init ();
// Step 2: Connect to WiFi
Serial . println ( " \n [2/4] Connecting to WiFi..." );
wifiHandler . begin (WIFI_SSID, WIFI_PASSWORD);
wifiHandler . setAutoReconnect ( true );
while ( ! wifiHandler . isConnected ()) {
delay ( 500 );
Serial . print ( "." );
}
Serial . println ( " \n ✓ WiFi connected" );
Serial . print ( " IP Address: " );
Serial . println ( WiFi . localIP ());
// Step 3: Connect to MQTT broker
Serial . println ( " \n [3/4] Connecting to MQTT broker..." );
mqtt . setServer (MQTT_BROKER, MQTT_PORT);
mqtt . setKeepAlive ( 60 );
if ( mqtt . connect ( "KinematrixDevice" )) {
Serial . println ( " ✓ MQTT connected" );
} else {
Serial . println ( " ⚠ MQTT connection failed (will retry)" );
}
// Step 4: Initialize PID controller
Serial . println ( " \n [4/4] Initializing PID controller..." );
pid . setSetPoint (TARGET_TEMP);
pid . calculateOptimalIntegralLimit ();
Serial . print ( " Target Temperature: " );
Serial . print (TARGET_TEMP);
Serial . println ( "°C" );
Serial . print ( " PID Parameters - Kp: " );
Serial . print ( pid . getKp ());
Serial . print ( ", Ki: " );
Serial . print ( pid . getKi ());
Serial . print ( ", Kd: " );
Serial . println ( pid . getKd ());
Serial . println ( " \n === System Ready ===" );
Serial . println ( "Monitoring started... \n " );
}
void loop () {
// Update WiFi connection
wifiHandler . update ();
// Update MQTT connection
if ( ! mqtt . connected ()) {
mqtt . reconnect ();
}
mqtt . loop ();
// Update all sensors
sensors . update ();
// Read current sensor values
float temperature = environmental . getFloatValue ( "temperature" );
float humidity = environmental . getFloatValue ( "humidity" );
float pressure = environmental . getFloatValue ( "pressure" );
float gas = environmental . getFloatValue ( "gas" );
// PID temperature control
float heaterOutput = pid . compute (temperature);
analogWrite (HEATER_PIN, ( int )heaterOutput);
// Display real-time data on serial monitor
Serial . print ( "Temp: " );
Serial . print (temperature, 2 );
Serial . print ( "°C | Humidity: " );
Serial . print (humidity, 2 );
Serial . print ( "% | Pressure: " );
Serial . print (pressure, 2 );
Serial . print ( " hPa | Gas: " );
Serial . print (gas, 0 );
Serial . print ( " Ω | Heater: " );
Serial . print ((heaterOutput / 255.0 ) * 100 , 0 );
Serial . println ( "%" );
// Publish to MQTT every 5 seconds
unsigned long currentTime = millis ();
if (currentTime - lastMQTTPublish >= MQTT_INTERVAL) {
lastMQTTPublish = currentTime;
if ( mqtt . connected ()) {
mqtt . publish (MQTT_TOPIC_TEMP, String (temperature, 2 ));
mqtt . publish (MQTT_TOPIC_HUMIDITY, String (humidity, 2 ));
mqtt . publish (MQTT_TOPIC_PRESSURE, String (pressure, 2 ));
Serial . println ( "→ Published to MQTT" );
}
}
delay ( 1000 ); // Update every second
}
Code Breakdown
Let’s understand what each section does:
Module Selection (Lines 1-5)
#define ENABLE_SENSOR_MODULE_V2
#define ENABLE_SENSOR_BME680_V2
#define ENABLE_MODULE_WIFI_HANDLER_V2
#define ENABLE_MODULE_MQTT_MANAGER
#define ENABLE_MODULE_PID_CONTROLLER
#include "Kinematrix.h"
This enables only the modules we need:
SensorModuleV2 : Modern sensor framework with type-safe access
BME680SensV2 : Environmental sensor driver
WiFiHandlerV2 : Advanced WiFi management with auto-reconnect
MQTTManager : MQTT client with automatic reconnection
PIDController : PID control algorithm with auto-tuning
Kinematrix’s conditional compilation means unused modules won’t be compiled, saving memory.
Sensor Initialization (Lines 52-66)
JsonDocument doc;
environmental . setDocumentValue ( & doc);
environmental . setDocument ( "bme680" );
environmental . setUpdateInterval ( 1000 );
if ( environmental . init ()) {
Serial . println ( "✓ BME680 initialized successfully" );
}
sensors . addSensor ( "env" , & environmental);
sensors . init ();
SensorModuleV2 uses JSON documents for data storage:
Each sensor stores its values in a JSON structure
Values are accessed by key (e.g., “temperature”, “humidity”)
SensorModuleV2 manages multiple sensors with a unified interface
WiFi Connection (Lines 68-79)
wifiHandler . begin (WIFI_SSID, WIFI_PASSWORD);
wifiHandler . setAutoReconnect ( true );
while ( ! wifiHandler . isConnected ()) {
delay ( 500 );
Serial . print ( "." );
}
WiFiHandlerV2 provides:
Automatic reconnection on connection loss
Connection status monitoring
Event callbacks for connect/disconnect
mqtt . setServer (MQTT_BROKER, MQTT_PORT);
mqtt . setKeepAlive ( 60 );
if ( mqtt . connect ( "KinematrixDevice" )) {
Serial . println ( "✓ MQTT connected" );
}
MQTTManager wraps PubSubClient with:
Automatic reconnection logic
Simplified publish/subscribe API
Connection state management
PID Controller (Lines 92-104)
PIDController pid ( 2.0 , 0.1 , 0.5 , 0.1 , 0 , 255 );
pid . setSetPoint (TARGET_TEMP);
pid . calculateOptimalIntegralLimit ();
// In loop:
float heaterOutput = pid . compute (temperature);
analogWrite (HEATER_PIN, ( int )heaterOutput);
PID controller maintains target temperature:
Kp=2.0 : Proportional gain
Ki=0.1 : Integral gain (eliminates steady-state error)
Kd=0.5 : Derivative gain (reduces overshoot)
dt=0.1 : Time step in seconds
Output range : 0-255 (PWM for heater control)
Expected Output
When you run this code, you should see:
=== Kinematrix IoT Environmental Monitor ===
Version: 0.0.28 Beta
[1/4] Initializing BME680 sensor...
✓ BME680 initialized successfully
[2/4] Connecting to WiFi...
.......
✓ WiFi connected
IP Address: 192.168.1.42
[3/4] Connecting to MQTT broker...
✓ MQTT connected
[4/4] Initializing PID controller...
Target Temperature: 25.0°C
PID Parameters - Kp: 2.00, Ki: 0.10, Kd: 0.50
=== System Ready ===
Monitoring started...
Temp: 24.32°C | Humidity: 45.67% | Pressure: 1013.25 hPa | Gas: 45678 Ω | Heater: 15%
Temp: 24.45°C | Humidity: 45.71% | Pressure: 1013.26 hPa | Gas: 45692 Ω | Heater: 12%
Temp: 24.58°C | Humidity: 45.68% | Pressure: 1013.24 hPa | Gas: 45701 Ω | Heater: 9%
→ Published to MQTT
Temp: 24.71°C | Humidity: 45.65% | Pressure: 1013.23 hPa | Gas: 45715 Ω | Heater: 6%
Temp: 24.83°C | Humidity: 45.62% | Pressure: 1013.22 hPa | Gas: 45728 Ω | Heater: 3%
→ Published to MQTT
...
Customization Ideas
Add Data Logging Enable the SD card module to log data locally: #define ENABLE_MODULE_SD_CARD_MODULE_ESP32
Add Display Show real-time data on an OLED screen: #define ENABLE_MODULE_OLED_MENU
Firebase Integration Send data to Firebase Realtime Database: #define ENABLE_MODULE_FIREBASE_RTDB_V3
Calibration Add interactive sensor calibration: #define ENABLE_SENSOR_CALIBRATION_V2
Next Steps
Explore More Sensors
Kinematrix supports 40+ sensors. Try adding:
DHT22 for additional temperature monitoring
INA219 for power consumption measurement
GPS for location tracking
Add Advanced Features
Enhance your project with:
Real-time filtering (Kalman, Moving Average)
Alert system for threshold monitoring
Auto-tuning PID parameters
Study Examples
Browse the 254+ examples in the example/ directory: # Sensor examples
example/sensors/SensorModuleV2/
# Control algorithm examples
example/modules/control/
# Communication examples
example/modules/communication/
Troubleshooting
BME680 initialization failed
Possible causes :
Incorrect I2C wiring (check SDA/SCL connections)
Wrong I2C address (BME680 can be 0x76 or 0x77)
Power supply issue (ensure 3.3V, not 5V)
Solution :// Try scanning I2C bus
#define ENABLE_MODULE_I2C_SCANNER
I2CScanner scanner;
scanner . scan (); // Will print found devices
Solution :
Verify SSID and password
Check WiFi signal strength
Try increasing connection timeout:
wifiHandler . setTimeout ( 30000 ); // 30 seconds
Solution :
Verify broker address and port
Check if broker requires authentication:
mqtt . setCredentials ( "username" , "password" );
Test broker connectivity with another MQTT client
Solution :
Verify heater wiring and MOSFET connections
Tune PID parameters for your system:
// Try auto-tuning
pid . autoTune (ZIEGLER_NICHOLS_2);
Learn More
Architecture Guide Understand Kinematrix’s modular design and compilation system
API Reference Complete API documentation for all modules