This is an old revision of the document!


Laborator 3: Captură și transmisie de imagini prin MQTT cu ESP32-CAM

În acest laborator ne vom familiariza cu platforma de dezvoltare ESP32-CAM și vom implementa un sistem de supraveghere simplu care transmite imagini către un calculator prin protocolul MQTT.

Obiective

  1. Înțelegerea arhitecturii unui sistem IoT bazat pe MQTT (Publisher/Subscriber).
  2. Configurarea mediului de dezvoltare (PlatformIO, Mosquitto, Python).
  3. Utilizarea ESP32-CAM pentru captură foto și transmisie WiFi.
  4. Controlul dispozitivului de la distanță prin comenzi MQTT.

Arhitectura sistemului

Sistemul este compus din trei entități principale:

  1. ESP32-CAM (Publisher/Subscriber):
    • Se conectează la rețeaua WiFi.
    • Publică imagini (JPEG) pe topicul ssproject/images.
    • Ascultă comenzi pe topicul ssproject/commands.
  2. Broker MQTT (Mosquitto):
    • Intermediarul care gestionează mesajele între cameră și calculator.
    • Rulează local pe calculatorul vostru.
  3. Client Python (Subscriber/Publisher):
    • Afișează imaginile primite în timp real.
    • Trimite comenzi către cameră (START-LIVE, STOP-LIVE, CAPTURE).

Configurare mediu de lucru

Pentru a rula proiectul, trebuie să pregătim infrastructura software.

1. Brokerul MQTT (Mosquitto)

Dacă nu aveți un broker instalat, descărcați Mosquitto.

Creați în rădăcina proiectului un fișier de configurare mosquitto.conf pentru a permite conexiuni externe (de la ESP32):

mosquitto.conf
listener 1883
allow_anonymous true

Porniți brokerul folosind acest fișier de configurare:

# Opriți serviciul default dacă rulează
sudo systemctl stop mosquitto.service
 
# Porniți brokerul cu configurarea noastră
/usr/sbin/mosquitto -c mosquitto.conf -v

2. Clientul Python (Receiver)

Aplicația de vizualizare (receiver.py) necesită Python și câteva biblioteci.

# Creați mediul virtual (dacă nu există)
python3 -m venv .venv
source .venv/bin/activate
 
# Instalați dependențele
pip install paho-mqtt opencv-python numpy

3. Crearea proiectului PlatformIO (Firmware ESP32-CAM)

Vom crea un proiect PlatformIO de la zero pentru plăcuța ESP32-CAM (AI-Thinker).

3.1 Instalare PlatformIO

Instalați extensia PlatformIO IDE din Visual Studio Code (Extensions → căutați “PlatformIO IDE” → Install).

3.2 Crearea proiectului

Puteți crea proiectul din interfața PlatformIO sau din terminal:

mkdir camera && cd camera
 
# Creați un mediu virtual Python și instalați PlatformIO CLI
python3 -m venv .venv
source .venv/bin/activate
pip install platformio
 
# Inițializați proiectul
pio project init --board esp32cam

3.3 Configurarea proiectului

Înlocuiți conținutul fișierului camera/platformio.ini cu următorul:

platformio.ini
; PlatformIO Project Configuration File
 
[env]
platform = espressif32
framework = arduino
lib_deps = 
    espressif/esp32-camera@^2.0.4
    WiFi @ ^2.0.0
    knolleary/PubSubClient @ ^2.8
monitor_speed = 115200
 
[env:esp32cam]
board = esp32cam
build_flags = -D CAMERA_MODEL_AI_THINKER
              -D MQTT_MAX_PACKET_SIZE=65000

Explicații:

  • espressif/esp32-camera — biblioteca oficială pentru modulul camerei.
  • knolleary/PubSubClient — client MQTT pentru Arduino.
  • CAMERA_MODEL_AI_THINKER — definește pinii GPIO corespunzători modelului AI-Thinker.
  • MQTT_MAX_PACKET_SIZE=65000 — mărește dimensiunea maximă a unui pachet MQTT (necesar pentru trimiterea imaginilor JPEG).

3.4 Definirea pinilor GPIO pentru cameră

Creați fișierul camera/include/camera_pins.hpp cu definițiile pinilor GPIO pentru modelul AI-Thinker:

camera_pins.hpp
#if defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
 
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22
 
#else
#error "Camera model not selected"
#endif

3.5 Structura finală

După acești pași, structura proiectului ar trebui să arate astfel:

camera/
├── include/
│   └── camera_pins.hpp     # Definițiile pinilor GPIO
├── src/
│   └── main.cpp            # Codul principal (de mai jos)
├── platformio.ini          # Configurarea proiectului
└── .venv/                  # Mediul virtual Python

3.6 Compilare și upload

# Compilare
pio run
 
# Upload pe placă (asigurați-vă că aveți permisiuni pe portul serial)
sudo chmod 666 /dev/ttyACM0   # sau /dev/ttyUSB0, depinde de placă
pio run -t upload
 
# Monitor serial (pentru debug)
pio device monitor -b 115200

Codul sursă

Firmware ESP32 (''camera/src/main.cpp'')

Acesta este codul care rulează pe cameră. Analizați-l cu atenție.

main.cpp
 

Receiver Python (''receiver/receiver.py'')

Acesta este clientul care rulează pe calculator, primește imaginile și trimite comenzi.

receiver.py
import paho.mqtt.client as mqtt
import cv2
import numpy as np
import threading
 
# Configuration
BROKER = "192.168.50.239"  # TODO: Modificați cu IP-ul brokerului vostru
PORT = 1883
TOPIC_IMAGE = "ssproject/images"
TOPIC_COMMAND = "ssproject/commands"
 
# Global flags
running = True
 
# Shared frame storage (thread-safe via lock)
latest_frame = None
frame_lock = threading.Lock()
 
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("Connected to MQTT Broker!")
        client.subscribe(TOPIC_IMAGE)
    else:
        print(f"Failed to connect, return code {rc}")
 
def on_message(client, userdata, msg):
    global latest_frame
    try:
        nparr = np.frombuffer(msg.payload, np.uint8)
        img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
 
        if img is not None:
            # Store the frame — do NOT call cv2.imshow here (wrong thread!)
            with frame_lock:
                latest_frame = img
        else:
            print("Failed to decode image")
    except Exception as e:
        print(f"Error processing image: {e}")
 
def main():
    global running, latest_frame
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
 
    try:
        client.connect(BROKER, PORT, 60)
        client.loop_start()
 
        print("\n--- ESP32 Camera Controller ---")
        print("Controls:")
        print(" 's' : Take Single Picture")
        print(" 'b' : Begin Stream")
        print(" 'e' : End Stream")
        print(" 'q' : Quit")
        print("-------------------------------\n")
 
        cv2.namedWindow("ESP32-CAM Stream", cv2.WINDOW_AUTOSIZE)
 
        while running:
            # Display the latest frame if available (main thread only)
            with frame_lock:
                frame = latest_frame
 
            if frame is not None:
                cv2.imshow("ESP32-CAM Stream", frame)
 
            # Handle GUI events and keyboard input (main thread only)
            key = cv2.waitKey(30) & 0xFF
 
            if key == ord('q'):
                running = False
            elif key == ord('s'):
                print("Command: Capture")
                client.publish(TOPIC_COMMAND, "CAPTURE")
            elif key == ord('b'):
                print("Command: Start Stream")
                client.publish(TOPIC_COMMAND, "START-LIVE")
            elif key == ord('e'):
                print("Command: Stop Stream")
                client.publish(TOPIC_COMMAND, "STOP-LIVE")
 
    except KeyboardInterrupt:
        print("\nExiting...")
    except Exception as e:
        print(f"Error: {e}")
    finally:
        client.loop_stop()
        client.disconnect()
        cv2.destroyAllWindows()
 
if __name__ == "__main__":
    main()

Structura codului

Firmware (''main.cpp'')

Codul de pe cameră îndeplinește următoarele funcții:

  • setup_camera(): Inițializează senzorul OV2640.
  • callback(): Funcția apelată când se primește un mesaj pe ssproject/commands. Interpretează:
    • CAPTURE → Face o poză.
    • START-LIVE → Activează flag-ul streaming.
    • STOP-LIVE → Dezactivează flag-ul streaming.
  • loop(): Verifică starea streaming. Dacă este activ, capturează și trimite o imagine la fiecare 100ms.

Configurare Wi-Fi și MQTT

În main.cpp, trebuie să modificați următoarele constante pentru a corespunde rețelei voastre:

const char* ssid     = "NUME_RETEA";
const char* password = "PAROLA_RETEA";
const char* mqtt_server = "IP_CALCULATOR"; // Rulați `ip addr` sau `ipconfig` pentru a-l afla

Receiver (''receiver.py'')

Scriptul Python se conectează la broker și afișează imaginile folosind OpenCV. De asemenea, ascultă tastatura pentru a trimite comenzi:

  • b: Begin Stream (START-LIVE)
  • e: End Stream (STOP-LIVE)
  • s: Single Capture (CAPTURE)
  • q: Quit

Desfășurarea laboratorului

  1. Configurare rețea: Deschideți camera/src/main.cpp și setați ssid, password și mqtt_server (IP-ul PC-ului vostru).
  2. Pornire broker: Rulați comanda de pornire Mosquitto într-un terminal.
  3. Pornire receiver: Rulați python receiver.py într-un alt terminal.
  4. Upload:
    • Conectați ESP32-CAM la USB (prin programator, dacă e cazul).
    • Asigurați-vă că aveți permisiuni pe portul serial: sudo chmod 666 /dev/ttyACM0.
    • Folosiți comanda Upload din PlatformIO sau terminal: pio run -t upload.
  5. Testare:
    • După resetare, camera se va conecta la WiFi (urmăriți Serial Monitor).
    • Din fereastra receiver-ului, apăsați b pentru a porni live stream-ul.
    • Verificați latența și calitatea imaginii.

Dacă ați configurat totul corect (broker-ul este pornit, camera publică pe ssproject/images, și receiver-ul sau aplicația web este conectată la același broker), ar trebui să puteți vizualiza stream-ul și prin intermediul interfaței web a aplicației (serverul web care face subscribe la același topic MQTT). Aceasta confirmă faptul că sistemul funcționează end-to-end.

Întrebări și exerciții

  1. Ce se întâmplă dacă modificați STREAM_INTERVAL în main.cpp la o valoare mai mică (ex: 20ms)? Cum afectează asta calitatea imaginii și latența?
  2. Analizați funcția callback din main.cpp. Adăugați o comandă nouă FLASH-ON care să aprindă LED-ul flash al camerei (GPIO 4).
ss/laboratoare/03.1773680637.txt.gz · Last modified: 2026/03/16 19:03 by ciprian.popescu0411
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0