Проект на Python за управление на ESP8266: Контрол на релета и сензори чрез бутони и WEB интерфейс

Проект на Python за управление на ESP8266 Контрол на релета и сензори чрез бутони и WEB интерфейс

ТОЗИ КОД Е НАПИСАН СПЕЦИАЛНО ЗА УПРАВЛЕНИЕ НА УСТОРЙСТВОТО ОТ СТАТИЯТА НИ: Проект с ESP8266 D1 Mini: Контрол на четири релета чрез бутони и WEB интерфейс и изчитане на сензори

Този проект демонстрира как можем да управляваме ESP8266 D1 Mini, оборудван с четири релета, чрез графичен интерфейс, създаден с Python и библиотеки за GUI, както и чрез бутони и уеб интерфейс. В допълнение към контрола на релетата, проектът чете данни от DHT11 сензор за температура и влажност и аналоговя вход.

Кодът за управлението на ESP8266 е написан на C++, а графичният интерфейс за управление и мониторинг на устройството е реализиран с помощта на Python, като основната комуникация се извършва чрез HTTP заявки.

Подготовка на средата за работа с Python

Преди да разгледаме кода, нека създадем и настроим виртуална среда на Python, в която ще работим по проекта. Използването на виртуална среда помага за управлението на библиотеките и версиите им, без да засяга глобалната инсталация на Python на системата.

1. Създаване на виртуална среда

Въвеждането на виртуална среда е ключова стъпка при работа с Python, тъй като тя изолира проектните зависимости. За този проект ще създадем виртуална среда, която наричаме my_env. Изпълнете следната команда:

python3 -m venv my_env

Това създава папка my_env, в която се намират всички необходими изпълними файлове и библиотеки за Python.

2. Активиране на виртуалната среда

След като сме създали виртуалната среда, трябва да я активираме. Активирането зависи от операционната система:

Linux/MacOS:

source my_env/bin/activate

Windows:

my_env\Scripts\activate
Python my_env среда

След активиране ще видите, че името на средата (в случая my_env) се появява в началото на командния ред.

3. Деактивиране на виртуалната среда

Когато приключите с работата по проекта, можете да деактивирате виртуалната среда с командата:

deactivate

4. Инсталиране на библиотеки

След като средата е активирана, можем да инсталираме необходимите библиотеки. Ако искаме да видим инсталираните библиотеки в текущата среда, изпълняваме:

python3 -m pip freeze

За да експортираме списъка с библиотеките във файл, използваме:

python3 -m pip freeze >> req.txt

Това ще създаде файл req.txt, който можем да използваме за възстановяване на същата конфигурация на библиотеките в бъдеще с командата:

python3 -m pip install -r req.txt

Тази команда ще инсталира всички библиотеки които се намират в req.txt файла. Тези библиотеки се записват с предишната команда, след като сте изпробвали кода.

Библиотеките за този пример може са инсталирате с горе-посочения метод след като ги свалите файла от тук: req.txt

Нашата програма за управление на ЕСП8266 с Python GUI

import sys
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QFrame)
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QFont
import requests

# Импортираме библиотеката за комуникация с ESP8266
class DeviceReader:
    def __init__(self, device_ip):
        self.device_ip = device_ip

    def _get_data(self, endpoint):
        url = f'http://{self.device_ip}/{endpoint}'
        try:
            response = requests.get(url)
            response.raise_for_status()
            return response.text
        except requests.exceptions.RequestException as e:
            print(f"Error fetching data from {url}: {e}")
            return None

    def read_analog(self):
        data = self._get_data('readAnalog')
        try:
            return int(data.strip())
        except (ValueError, TypeError):
            return None

    def read_buttons(self):
        data = self._get_data('readButtons')
        if data:
            buttons = {}
            for line in data.splitlines():
                if line.startswith("Button"):
                    parts = line.split(":")
                    if len(parts) == 2:
                        button_num = int(parts[0].split()[1])
                        counter = int(parts[1].strip())
                        buttons[f"Button {button_num}"] = counter
            return buttons
        return None

    def read_relays(self):
        data = self._get_data('readRelays')
        if data:
            relays = {}
            for line in data.splitlines():
                if line.startswith("Relay"):
                    parts = line.split(":")
                    if len(parts) == 2:
                        relay_num = int(parts[0].split()[1])
                        status = parts[1].strip()
                        relays[f"Relay {relay_num}"] = status
            return relays
        return None

    def read_dht(self):
        data = self._get_data('readDHT')
        if data:
            dht_data = {}
            for line in data.splitlines():
                if "Temperature" in line:
                    temp_str = line.split(":")[1].strip().split()[0]
                    dht_data["Temperature"] = float(temp_str)
                elif "Humidity" in line:
                    hum_str = line.split(":")[1].strip().split()[0]
                    dht_data["Humidity"] = float(hum_str)
            return dht_data
        return None

    def toggle_relay(self, relay_num):
        """Изпраща заявка за превключване на състоянието на реле"""
        endpoint = f'toggleRelay{relay_num}'
        return self._get_data(endpoint)

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        # Настройки за ESP8266
        self.device_reader = DeviceReader("192.168.1.132") # Заместете с IP на вашия ESP8266

        # UI Инициализация
        self.setWindowTitle("ESP8266 Control Interface")
        self.setGeometry(100, 100, 600, 400)

        layout = QVBoxLayout()

        # Показване на температура и влажност от DHT
        self.dht_frame = QFrame(self)
        self.dht_frame.setFrameShape(QFrame.Box)
        layout.addWidget(self.dht_frame)

        self.dht_layout = QVBoxLayout(self.dht_frame)
        self.temp_label = QLabel("Temperature: -- °C", self)
        self.hum_label = QLabel("Humidity: -- %", self)
        self.temp_label.setFont(QFont("Arial", 16))
        self.hum_label.setFont(QFont("Arial", 16))

        self.dht_layout.addWidget(self.temp_label)
        self.dht_layout.addWidget(self.hum_label)

        # Показване на състоянието на релеите
        self.relay_frame = QFrame(self)
        self.relay_frame.setFrameShape(QFrame.Box)
        layout.addWidget(self.relay_frame)

        self.relay_layout = QVBoxLayout(self.relay_frame)
        self.relay_status_labels = []
        self.relay_buttons = []

        for i in range(1, 5):
            relay_label = QLabel(f"Relay {i}: --", self)
            relay_button = QPushButton(f"Toggle Relay {i}", self)
            relay_button.clicked.connect(lambda checked, i=i: self.toggle_relay(i))

            self.relay_status_labels.append(relay_label)
            self.relay_buttons.append(relay_button)

            self.relay_layout.addWidget(relay_label)
            self.relay_layout.addWidget(relay_button)

        # Показване на аналогова стойност
        self.analog_label = QLabel("Analog Value: --", self)
        self.analog_label.setFont(QFont("Arial", 16))
        layout.addWidget(self.analog_label)

        # Настройка на таймер за обновяване на данните
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_data)
        self.timer.start(2000)  # Обновяване на всяка 2 секунди

        self.setLayout(layout)

    def update_data(self):
        # Обновяване на данни от сензорите
        dht_data = self.device_reader.read_dht()
        if dht_data:
            self.temp_label.setText(f"Temperature: {dht_data['Temperature']} °C")
            self.hum_label.setText(f"Humidity: {dht_data['Humidity']} %")

        # Обновяване на състоянието на релеите
        relays = self.device_reader.read_relays()
        if relays:
            for i in range(1, 5):
                status = relays.get(f"Relay {i}", "Unknown")
                self.relay_status_labels[i - 1].setText(f"Relay {i}: {status}")

        # Обновяване на аналогова стойност
        analog_value = self.device_reader.read_analog()
        if analog_value is not None:
            self.analog_label.setText(f"Analog Value: {analog_value}")

    def toggle_relay(self, relay_num):
        """Превключване на състоянието на реле"""
        result = self.device_reader.toggle_relay(relay_num)
        if result:
            print(f"Relay {relay_num} toggled successfully")
        self.update_data()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

Обяснение на Python GUI кода

Програмата, която създадохме за управлението на ESP8266, използва библиотеката PySide6 за графичния интерфейс (GUI) и библиотеката requests за изпращане на HTTP заявки към ESP8266.

Нека разгледаме основния код на Python:

import sys
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QFrame)
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QFont
import requests

Тук първо импортираме основните модули на PySide6, които ще използваме за изграждане на потребителския интерфейс. Импортираме също библиотеката requests, която позволява лесно изпращане на HTTP заявки към ESP8266.

Клас за комуникация с ESP8266

class DeviceReader:
    def __init__(self, device_ip):
        self.device_ip = device_ip

    def _get_data(self, endpoint):
        url = f'http://{self.device_ip}/{endpoint}'
        try:
            response = requests.get(url)
            response.raise_for_status()
            return response.text
        except requests.exceptions.RequestException as e:
            print(f"Error fetching data from {url}: {e}")
            return None

Този клас представлява основната връзка с ESP8266, като използва IP адреса на устройството и различни крайни точки (endpoints), за да получава данни. Например, чрез _get_data() функцията, изпращаме GET заявка към ESP8266, за да получим данни от специфична крайна точка.

Функции за четене на сензорни данни

    def read_analog(self):
        data = self._get_data('readAnalog')
        try:
            return int(data.strip())
        except (ValueError, TypeError):
            return None

    def read_buttons(self):
        data = self._get_data('readButtons')
        if data:
            buttons = {}
            for line in data.splitlines():
                if line.startswith("Button"):
                    parts = line.split(":")
                    if len(parts) == 2:
                        button_num = int(parts[0].split()[1])
                        counter = int(parts[1].strip())
                        buttons[f"Button {button_num}"] = counter
            return buttons
        return None

Тези функции използват _get_data() метода, за да извлекат данни за аналоговия вход и състоянието на бутоните от ESP8266. Данните се парсират и връщат в подходящ формат.

Графичен потребителски интерфейс (GUI)

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.device_reader = DeviceReader("192.168.100.132")

        # UI Инициализация
        self.setWindowTitle("ESP8266 Control Interface")
        self.setGeometry(100, 100, 600, 400)

        layout = QVBoxLayout()

        # Показване на температура и влажност от DHT
        self.dht_frame = QFrame(self)
        self.dht_frame.setFrameShape(QFrame.Box)
        layout.addWidget(self.dht_frame)

        self.dht_layout = QVBoxLayout(self.dht_frame)
        self.temp_label = QLabel("Temperature: -- °C", self)
        self.hum_label = QLabel("Humidity: -- %", self)
        self.temp_label.setFont(QFont("Arial", 16))
        self.hum_label.setFont(QFont("Arial", 16))

        self.dht_layout.addWidget(self.temp_label)
        self.dht_layout.addWidget(self.hum_label)

        # Показване на състоянието на релеите
        self.relay_frame = QFrame(self)
        self.relay_frame.setFrameShape(QFrame.Box)
        layout.addWidget(self.relay_frame)

        self.relay_layout = QVBoxLayout(self.relay_frame)
        self.relay_status_labels = []
        self.relay_buttons = []

        for i in range(1, 5):
            relay_label = QLabel(f"Relay {i}: --", self)
            relay_button = QPushButton(f"Toggle Relay {i}", self)
            relay_button.clicked.connect(lambda checked, i=i: self.toggle_relay(i))

            self.relay_status_labels.append(relay_label)
            self.relay_buttons.append(relay_button)

            self.relay_layout.addWidget(relay_label)
            self.relay_layout.addWidget(relay_button)

В този клас създаваме основния прозорец на приложението, където добавяме различни елементи за визуализиране на данните и контрол на релетата. Температурата и влажността от DHT11 се показват чрез два QLabel елемента. За всяко реле създаваме бутон за превключване на състоянието му, както и етикет, показващ текущото състояние.

Превключване на релетата и обновяване на данните

    def toggle_relay(self, relay_num):
        result = self.device_reader.toggle_relay(relay_num)
        if result:
            print(f"Relay {relay_num} toggled successfully")
        self.update_data()

    def update_data(self):
        dht_data = self.device_reader.read_dht()
        if dht_data:
            self.temp_label.setText(f"Temperature: {dht_data['Temperature']} °C")
            self.hum_label.setText(f"Humidity: {dht_data['Humidity']} %")

        relays = self.device_reader.read_relays()
        if relays:
            for i in range(1, 5):
                status = relays.get(f"Relay {i}", "Unknown")
                self.relay_status_labels[i - 1].setText(f"Relay {i}: {status}")

        analog_value = self.device_reader.read_analog()
        if analog_value is not None:
            self.analog_label.setText(f"Analog Value: {analog_value}")

Методът toggle_relay() изпраща команда за превключване на реле и обновява състоянието на всички релета, както и на сензорите. update_data() методът се извиква периодично чрез таймер, за да се обновяват показваните стойности в GUI.

Стартиране на програмата

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
  • if __name__ == "__main__": Този ред гарантира, че програмата ще стартира само ако файлът се изпълнява директно, а не когато е импортиран като модул в друг скрипт.
  • app = QApplication(sys.argv) Създава се инстанция на QApplication, която управлява цикъла на събитията и обработва действията на потребителя. Аргументът sys.argv дава възможност за предаване на параметри от командния ред.
  • window = MainWindow() Тук се създава основният прозорец на приложението, който съдържа графичния интерфейс.
  • window.show() Този метод показва прозореца на екрана, правейки интерфейса достъпен за потребителя.
  • sys.exit(app.exec()) Започва цикъла на събитията на приложението. След като прозорецът бъде затворен, приложението приключва, и sys.exit() гарантира коректно излизане от програмата.

Видео презентация на кода:

Проект на Python за управление на ESP8266 Контрол на релета и сензори чрез бутони и WEB интерфейс RaspberryPI LCD

Заключение

Този Python проект с графичен интерфейс предоставя удобно средство за контрол на ESP8266 и неговите релета, като същевременно чете данни от различни сензори. Интерфейсът е интуитивен и се обновява автоматично на всеки няколко секунди, за да показва актуални данни от устройството.