In this fourth part of our IoT project series, we dive into our first hardware implementation: ESP32-based LED and button control system. This project demonstrates the complete data flow from physical hardware through MQTT communication to the PyQt5 dashboard, creating a bidirectional IoT control system.
Project OverviewThis ESP32 project implements an LED and button control system with MQTT communication and real-time monitoring. It features 10 LEDs (5 red, 5 green) for visual feedback and 5 push buttons for manual input, alongside dashboard-based control. The Wokwi simulation shows a clean hardware setup:
- LEDs: Red (GPIO 2, 0, 4, 16, 17), Green (GPIO 5, 18, 19, 21, 22)
- Buttons: Connected to GPIO 12, 14, 27, 26, 25 with internal pull-ups and debouncing
- Power: Common ground, with each LED controlled by its own GPIO
The firmware uses a state-machine approach with three core components. Connection Management
handles WiFi with auto-reconnect, MQTT with authentication, and a heartbeat for monitoring. Input Processing
ensures reliable button handling with debouncing, state change detection, and active-low configuration. Output Control
manages individual LED control, synchronized visual feedback, and state persistence for consistent operation.
MQTT Communication Protocol
The system uses a structured MQTT topic hierarchy:
arduino/Led
- LED control commands from dashboardmqtt/request
- System control and status requestsmqtt/response
- Device status and health reports
Message Formats:
LED Control: "ledRED1_ON", "ledGREEN3_OFF"
Status Request: "status_request"
Status Response: "Board : ESP32 Status : Connected"
Button Events: "leds1 ON", "leds2 OFF"
System Activation Control:
bool systemActive = false;
// Activated by status request from dashboard
if (String(topic) == "mqtt/request" && message == "status_request") {
systemActive = true;
client.publish("mqtt/response", "Board : ESP32 Status : Connected");
}
Button State Management:
// Debounced button reading with state change detection
bool currentButtonState = digitalRead(buttonPins[i]);
if (currentButtonState != lastButtonState[i]) {
if (currentButtonState == LOW) { // Button pressed
String message = "leds" + String(i + 1) + " ON";
client.publish("arduino/Led", message.c_str());
// Activate both red and green LEDs
digitalWrite(redLedPins[i], HIGH);
digitalWrite(greenLedPins[i], HIGH);
}
}
PyQt5 Dashboard IntegrationThe dashboard interface provides comprehensive control through four main sections:
- MQTT Status Panel
- Button Control Section
- Switch Control Panel
- LED Status Display
Signal-Based Architecture
The Python implementation uses PyQt5's signal-slot mechanism for thread-safe UI updates:
class LED_and_Button(QObject):
# Signal definitions for thread-safe UI updates
update_button_text = pyqtSignal(str, str)
update_label_color = pyqtSignal(str, str)
update_board_status = pyqtSignal(str, str, str)
update_led_status = pyqtSignal(str, str)
Benefits of Signal-Based Design:
- Thread-safe UI updates from MQTT callbacks
- Clean separation between business logic and UI
- Scalable architecture for multiple concurrent operations
- Reliable state synchronization between hardware and interface
MQTT Message Handling
LED Control Messages:
def handle_led_message(self, topic, payload):
if payload.startswith("leds"):
parts = payload.split()
led_id, state = parts
led_num = led_id.replace("leds", "")
# Update both red and green LEDs simultaneously
red_label_name = f"label_red_led_{led_num}"
green_label_name = f"label_green_led_{led_num}"
if state == "ON":
self.update_label_color.emit(red_label_name, "red")
self.update_label_color.emit(green_label_name, "green")
Status Monitoring:
def handle_status_message(self, topic, payload):
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.update_board_status.emit(board_name, "Connected", "green")
Control Flow Analysis
- User Interaction: Switch toggle or button press in dashboard
- MQTT Publishing: Command sent to
arduino/Led
topic - ESP32 Processing: Message received and parsed
- Hardware Action: GPIO pins updated to control LEDs
- Status Feedback: Visual confirmation in dashboard interface
Hardware to Dashboard Flow
- Physical Input: Button pressed on ESP32 hardware
- State Detection: Firmware detects button state change
- MQTT Publishing: Status message sent to
arduino/Led
topic - Dashboard Processing: Message received and parsed
- UI Update: Visual elements updated to reflect hardware state
Advanced Features
Status Request System:
def request_board_status(self):
self.status_received = False
self.mqtt_client.publish(MQTT_TOPIC_MQTT_Rq, "status_request")
QTimer.singleShot(1000, self.check_status_response)
Custom Switch Implementation
def _configure_switch(self, switch, name):
switch._circle_radius = 26
switch._bg_color = "#cccccc" # Off state
switch._active_color = "#03A9F4" # On state (blue)
switch.setFixedSize(65, 30)
Deactivate and Reset System State
def deactivate(self):
print("deactivate P1")
self.mqtt_client.publish(MQTT_TOPIC_MQTT_Rq, "TurnOFF")
# Unsubscribe from MQTT topics
self.mqtt_client.unsubscribe_from_topic(MQTT_TOPIC_LED)
self.mqtt_client.unsubscribe_from_topic(MQTT_TOPIC_MQTT_Rs)
# Reset button texts and labels
for btn, _, label in self.button_map:
btn.setText("OFF")
self.reset_label_color(label)
# Reset switches and green LEDs
for switch_name, switch in self.switches.items():
switch.blockSignals(True)
switch.setChecked(False)
switch.blockSignals(False)
led_id = self.switch_to_green_led.get(switch_name)
if led_id:
label = self.green_led_map.get(led_id)
if label:
self.reset_label_color(label)
# Clear board status display
self.ui.lab_board_1.setText("Unknown")
self.ui.lab_board_status1.setText("Disconnected")
self._apply_board_status_style("red")
self.update_led_status.emit("led_boardST", "red")
ConclusionThis ESP32 LED and button control project establishes the foundation for IoT communication between physical hardware and dashboard interfaces through MQTT messaging. The bidirectional control system provides essential features like connection management, error recovery, and thread-safe UI updates.
In the next part of this series, IoT Projects Part 5: Temperature & Humidity Monitoring, we'll explore sensor data acquisition and real-time environmental monitoring with advanced data visualization and alerting capabilities.
That's all!If you have any questions or suggestions, don’t hesitate to leave a comment below.
Comments