Skip to main content

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

1

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
2

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
3

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:
#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.
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
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
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

1

Explore More Sensors

Kinematrix supports 40+ sensors. Try adding:
  • DHT22 for additional temperature monitoring
  • INA219 for power consumption measurement
  • GPS for location tracking
2

Add Advanced Features

Enhance your project with:
  • Real-time filtering (Kalman, Moving Average)
  • Alert system for threshold monitoring
  • Auto-tuning PID parameters
3

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

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

Build docs developers (and LLMs) love