In this eighth part of our IoT project series, we explore 6-axis motion sensing with ESP32-based MPU6050 integration. This project demonstrates accelerometer and gyroscope data acquisition, JSON-based data transmission, real-time visualization with dual plotting systems, and comprehensive motion analysis capabilities.
Project OverviewThis ESP32 project implements a comprehensive motion tracking system with MQTT communication and advanced data visualization. It features an MPU6050 6-axis IMU sensor for precise motion detection, JSON data formatting for structured communication, and real-time monitoring with dual-axis plotting. The Wokwi simulation shows a professional motion sensing setup:
- MPU6050 IMU Sensor: 6-axis motion tracking with 3-axis accelerometer and 3-axis gyroscope
- I2C Communication: SCL (GPIO 22) and SDA (GPIO 21) for digital sensor interface
- Temperature Sensing: Built-in temperature sensor for environmental monitoring
- Power Distribution: 3.3V supply for MPU6050, common ground configuration
- Data Processing: JSON formatting with structured accelerometer, gyroscope, and temperature data
The firmware uses a sensor-fusion approach with four core components. Connection Management handles WiFi with retry logic, MQTT with authentication, and automatic reconnection. Sensor Processing provides MPU6050 initialization with I2C communication, 6-axis data acquisition with calibration, and temperature measurement integration. Data Formatting implements JSON structure creation, floating-point precision control, and structured MQTT payload generation. System Activation features MQTT-based enable/disable, status reporting, and configurable update intervals.
MQTT Communication Protocol
The system uses structured MQTT topics for motion data transmission:
arduino/MPU6050
- JSON-formatted sensor data publishingmqtt/request
- System activation and status requestsmqtt/response
- Device status and health reports
Message Formats:
Motion Data: {"accelX": 0.12, "accelY": -0.05, "accelZ": 9.81, "gyroX": 1.23, "gyroY": -0.45, "gyroZ": 0.78, "temp": 23.5}
Status Response: "Board : ESP32 Status : Connected"
Status Request: "status_request"
System Control: "TurnOFF"
Key Firmware Features
MPU6050 Sensor Initialization:
#include <Wire.h>
#include <MPU6050.h>
MPU6050 mpu;
void setup() {
Wire.begin();
mpu.initialize();
if (mpu.testConnection()) {
Serial.println("MPU6050 connection successful");
mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
} else {
Serial.println("MPU6050 connection failed");
}
}
JSON Data Acquisition and Publishing:
void publishSensorData() {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
int16_t temp = mpu.getTemperature();
float accelX = ax / 16384.0; // Convert to g
float accelY = ay / 16384.0;
float accelZ = az / 16384.0;
float gyroX = gx / 131.0; // Convert to °/sec
float gyroY = gy / 131.0;
float gyroZ = gz / 131.0;
float temperature = temp / 340.0 + 36.53; // Convert to °C
String jsonPayload = "{";
jsonPayload += "\"accelX\":" + String(accelX, 2) + ",";
jsonPayload += "\"accelY\":" + String(accelY, 2) + ",";
jsonPayload += "\"accelZ\":" + String(accelZ, 2) + ",";
jsonPayload += "\"gyroX\":" + String(gyroX, 2) + ",";
jsonPayload += "\"gyroY\":" + String(gyroY, 2) + ",";
jsonPayload += "\"gyroZ\":" + String(gyroZ, 2) + ",";
jsonPayload += "\"temp\":" + String(temperature, 1);
jsonPayload += "}";
client.publish("arduino/MPU6050", jsonPayload.c_str());
}
System Activation with Data Rate Control:
void loop() {
if (systemActive) {
unsigned long now = millis();
if (now - lastMsg > 100) {
lastMsg = now;
publishSensorData();
}
}
}
PyQt5 Dashboard IntegrationThe dashboard interface provides comprehensive motion monitoring through six main sections:
- MQTT Status Panel - Connection monitoring with LED indicators
- Sensor Data Display - Real-time accelerometer, gyroscope, and temperature values
- Accelerometer Plot - PyQtGraph visualization with X/Y/Z axis curves and legend
- Gyroscope Plot - Separate angular velocity plotting with color-coded axes
- Data Buffering - Circular buffer management with configurable history length
- Error Handling - JSON parsing validation and graceful error state management
Signal-Based Architecture
Signal-slot mechanism for thread-safe updates:
class AccelerometerGyroscopeController(QObject):
accelerometer_data_changed = pyqtSignal(float, float, float)
gyroscope_data_changed = pyqtSignal(float, float, float)
temperature_changed = pyqtSignal(str)
board_status_changed = pyqtSignal(str, str, str)
plot_update_signal = pyqtSignal(float, float, float, float, float, float)
error_state_signal = pyqtSignal()
Thread-Safe Data Processing:
@pyqtSlot(float, float, float)
def update_accelerometer_ui(self, x, y, z):
if hasattr(self.ui, 'Accelx_lineEdit'):
self.ui.Accelx_lineEdit.setText(f"{x:.2f} g")
if hasattr(self.ui, 'Accely_lineEdit'):
self.ui.Accely_lineEdit.setText(f"{y:.2f} g")
if hasattr(self.ui, 'Accelz_lineEdit'):
self.ui.Accelz_lineEdit.setText(f"{z:.2f} g")
@pyqtSlot(float, float, float, float, float, float)
def update_plots_ui(self, accelX, accelY, accelZ, gyroX, gyroY, gyroZ):
self.accel_data['x'].append(accelX)
self.accel_data['y'].append(accelY)
self.accel_data['z'].append(accelZ)
if len(self.time_data) > self.max_data_points:
for axis in ['x', 'y', 'z']:
self.accel_data[axis].pop(0)
self.gyro_data[axis].pop(0)
self.time_data.pop(0)
MQTT Message Handling
JSON Data Processing with Error Handling:
def handle_mpu6050_message(self, topic, payload):
if topic == MQTT_TOPIC_MPU6050:
try:
sensor_data = json.loads(payload)
# Extract data with fallback values
accelX = sensor_data.get("accelX", 0)
accelY = sensor_data.get("accelY", 0)
accelZ = sensor_data.get("accelZ", 0)
gyroX = sensor_data.get("gyroX", 0)
gyroY = sensor_data.get("gyroY", 0)
gyroZ = sensor_data.get("gyroZ", 0)
temp = sensor_data.get("temp", "--")
self.accelerometer_data_changed.emit(accelX, accelY, accelZ)
self.gyroscope_data_changed.emit(gyroX, gyroY, gyroZ)
self.temperature_changed.emit(str(temp))
self.plot_update_signal.emit(accelX, accelY, accelZ, gyroX, gyroY, gyroZ)
except json.JSONDecodeError as e:
print(f"JSON decode error: {e}")
self.error_state_signal.emit()
Status Monitoring with Message Filtering:
def handle_status_response_message(self, topic, payload):
try:
if payload.strip() == "status_request":
return # Ignore our own status requests
if "Board :" in payload and "Status :" in payload:
board_part, status_part = payload.split("Status :")
board_name = board_part.replace("Board :", "").strip()
status = status_part.strip().lower()
if status == "connected":
self.board_status_changed.emit(board_name, "Connected", "green")
self.led_status_changed.emit("green")
self.status_received = True
except Exception as e:
print(f"Error processing status message: {e}")
Control Flow Analysis
Motion Data Flow:
- Sensor Reading: MPU6050 provides 6-axis motion data plus temperature via I2C
- Unit Conversion: Raw ADC values converted to meaningful units (g, °/sec, °C)
- JSON Formatting: Structured data packet creation with floating-point precision
- MQTT Publishing: JSON payload transmitted to
arduino/MPU6050
topic at 10Hz - Dashboard Processing: JSON parsing, validation, and real-time UI updates
Visualization Flow:
- Data Reception: JSON payload received and parsed by dashboard
- Buffer Management: New data appended to circular buffers with length control
- Plot Updates: PyQtGraph curves updated with latest data points
- Auto-Scaling: Dynamic range adjustment for optimal visualization
- Legend Management: Color-coded axis identification with professional appearance
Advanced Features
Dual-Plot Visualization System:
def init_plots(self):
self.accel_plot.setBackground(QColor(0, 0, 0, 100))
self.accel_plot.setTitle("Accelerometer Data (g)", color='w', size='14pt', bold=True)
self.accel_plot.addLegend()
self.accel_plot.showGrid(x=True, y=True)
self.accel_x_curve = self.accel_plot.plot(pen='r', name="AccelX")
self.accel_y_curve = self.accel_plot.plot(pen='g', name="AccelY")
self.accel_z_curve = self.accel_plot.plot(pen='b', name="AccelZ")
self.gyro_plot.setTitle("Gyroscope Data (°/sec)", color='w', size='14pt', bold=True)
Circular Buffer Data Management:
def __init__(self, mqtt_client, ui):
self.accel_data = {'x': [], 'y': [], 'z': []}
self.gyro_data = {'x': [], 'y': [], 'z': []}
self.time_data = []
self.max_data_points = 100 # Configurable history length
Error State Management:
@pyqtSlot()
def show_error_state_ui(self):
error_text = "ERR"
if hasattr(self.ui, 'Accelx_lineEdit'):
self.ui.Accelx_lineEdit.setText(error_text)
for field in ['Accelx_lineEdit', 'Accely_lineEdit', 'Accelz_lineEdit',
'Gyrox_lineEdit', 'Gyroy_lineEdit', 'Gyroz_lineEdit', 'temp_lineEdit']:
if hasattr(self.ui, field):
getattr(self.ui, field).setText(error_text)
Comprehensive System Deactivation:
def deactivate(self):
self.mqtt_client.publish(MQTT_TOPIC_MQTT_Rq, "TurnOFF")
self.accel_data = {'x': [], 'y': [], 'z': []}
self.gyro_data = {'x': [], 'y': [], 'z': []}
self.time_data = []
if hasattr(self, 'accel_plot'):
self.accel_plot.clear()
self.accel_plot.setTitle("Accelerometer Data (g)")
self.accel_plot.showGrid(x=True, y=True)
self.mqtt_client.unsubscribe_from_topic(MQTT_TOPIC_MPU6050)
self.mqtt_client.unsubscribe_from_topic(MQTT_TOPIC_MQTT_Rs)
ConclusionThis ESP32 motion tracking system demonstrates advanced 6-axis sensor integration with structured JSON communication, dual-axis real-time visualization, and comprehensive motion analysis capabilities. The system provides professional-grade motion sensing with accelerometer and gyroscope data fusion, temperature monitoring, and sophisticated dashboard visualization.
In the next part of this series, IoT Projects Part 9: ESP32 Gas Detection and Safety Monitoring, we'll explore environmental safety sensing with MQ-series gas sensors, threshold-based alerting, and automated safety response systems.
Comments