ESP32CAM向本地服务器传输图片使用base64编码进行传输

首先是ESP32CAM有WiFi模块,通过WiFi与本机连接同一个ip地址完成拍摄图片后上传本地服务器并保存

ESP32CAM在VSCODE使用platformio开发

项目具体步骤为

1ESP32CAM的代码烧录

2本地服务器的启动

3重启ESP32CAM完成与本地服务器连接,在串口工具可以查看连接状态

由于在ESP32CAM上进行了开闪光灯的操作,如不需要请自行注释

ESP32CAM

#include <Arduino.h>

#include <WiFi.h>

#include "esp_camera.h"

#include <Base64.h>  // 引入 Base64 库



const char *ssid = "";  //替换为实际的WiFi名称

const char *password = ""; // 替换为实际的WiFi密码

const IPAddress serverIP(192, 168, 3, 235); //替换为你的服务器的 IP 地址

uint16_t serverPort = 8080;         // 替换为你的设置的服务器端口号



#define maxcache 1436



WiFiClient client; // 声明一个客户端对象,用于与服务器进行连接



// 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



#define LED_PIN 4  // ESP32-CAM的LED引脚,默认GPIO 4



static camera_config_t camera_config = {

    .pin_pwdn = PWDN_GPIO_NUM,

    .pin_reset = RESET_GPIO_NUM,

    .pin_xclk = XCLK_GPIO_NUM,

    .pin_sscb_sda = SIOD_GPIO_NUM,

    .pin_sscb_scl = SIOC_GPIO_NUM,



    .pin_d7 = Y9_GPIO_NUM,

    .pin_d6 = Y8_GPIO_NUM,

    .pin_d5 = Y7_GPIO_NUM,

    .pin_d4 = Y6_GPIO_NUM,

    .pin_d3 = Y5_GPIO_NUM,

    .pin_d2 = Y4_GPIO_NUM,

    .pin_d1 = Y3_GPIO_NUM,

    .pin_d0 = Y2_GPIO_NUM,

    .pin_vsync = VSYNC_GPIO_NUM,

    .pin_href = HREF_GPIO_NUM,

    .pin_pclk = PCLK_GPIO_NUM,



    .xclk_freq_hz = 20000000,

    .ledc_timer = LEDC_TIMER_0,

    .ledc_channel = LEDC_CHANNEL_0,



    .pixel_format = PIXFORMAT_JPEG,

    .frame_size = FRAMESIZE_VGA,

    .jpeg_quality = 12,

    .fb_count = 1,

};



// Wi-Fi 初始化

void wifi_init() {

    WiFi.mode(WIFI_STA);

    WiFi.setSleep(false); // 关闭 STA 模式下 Wi-Fi 休眠,提高响应速度

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {

        delay(500);

        Serial.print(".");

    }

    Serial.println("WiFi Connected!");

    Serial.print("IP Address:");

    Serial.println(WiFi.localIP());

}



// 摄像头初始化

esp_err_t camera_init() {

   

    esp_err_t err = esp_camera_init(&camera_config);

    if (err != ESP_OK) {

        Serial.println("Camera Init Failed");

        return err;

    }

   

    sensor_t * s = esp_camera_sensor_get();

    Serial.println("Camera Init OK!");

    return ESP_OK;

}

// 打开LED

void led_off() {

    digitalWrite(LED_PIN, LOW);  // 打开LED(LOW通常是打开LED)

}



// 关闭LED

void led_on() {

    digitalWrite(LED_PIN, HIGH);  // 关闭LED

}




void setup() {

    Serial.begin(115200);

    while (!Serial);  // 等待串口连接

    Serial.println("Starting...");

    wifi_init();

    camera_init();

    pinMode(LED_PIN, OUTPUT);  // 设置LED引脚为输出模式

    Serial.println("Testing LED...");

}  



// 发送图片到服务器

void send_image_to_server(camera_fb_t * fb) {

    client.print("Frame Begin"); // 一张图片的起始标志



    // 将图片数据分段发送

    String encodedData = base64::encode(fb->buf, fb->len);

    int leng = encodedData.length();

    int timess = leng / maxcache;

    int extra = leng % maxcache;

    for (int j = 0; j < timess; j++) {

        client.write(encodedData.c_str() + j * maxcache, maxcache);

    }

    client.write(encodedData.c_str() + timess * maxcache, extra);

    client.print("Frame Over");      // 一张图片的结束标志



    Serial.print("This Frame Length: ");

    Serial.print(fb->len);

    Serial.println(". Success To Send Image For TCP!");



    esp_camera_fb_return(fb);  // 释放帧缓冲

}



void loop() {

    static unsigned long lastMillis = 0;

    unsigned long currentMillis = millis();



    // 每隔 10 秒拍照并发送

    if (currentMillis - lastMillis >= 10000) { // 10000 毫秒即 10 秒

        lastMillis = currentMillis; // 更新最后发送时间



        Serial.println("Trying to capture and send image...");

       

       

        if (client.connect(serverIP, serverPort)) { // 连接服务器          

            led_on();  // 打开 LED(亮灯)

            delay(500);  // 等待500ms,确保LED亮起

            camera_fb_t * fb = esp_camera_fb_get();  // 获取摄像头的帧数据

            if (!fb) {

                Serial.println("Camera Capture Failed");

                led_off();  // 关闭 LED(熄灯)

                return;

            }

            else {

                send_image_to_server(fb);

                led_off();  // 关闭 LED(熄灯)

            }

           

        }

        else {

            Serial.println("Connection to server failed, trying again...");

            led_off();  // 关闭 LED(熄灯)

            client.stop(); // 发送完成后关闭连接

           

       

        }

       

    }



    delay(100);  // 稍微延迟,减少 CPU 占用

}

python搭建服务器

import base64
import socket
import threading
import time
import numpy as np
import cv2
import os
from datetime import datetime

begin_data = b'Frame Begin'
end_data = b'Frame Over'

# 图片保存目录
SAVE_DIR = "received_images"

# 确保保存目录存在
if not os.path.exists(SAVE_DIR):
    os.makedirs(SAVE_DIR)

# 接收数据
# ESP32发送一张照片的流程
# 先发送Frame Begin 表示开始发送图片 然后将图片数据分包发送 每次发送1430 余数最后发送
# 完毕后发送结束标志 Frame Over 表示一张图片发送完毕
# 1430 来自ESP32cam发送的一个包大小为1430 接收到数据 data格式为b''
def handle_sock(sock, addr):
    temp_data = b''
    t1 = int(round(time.time() * 1000))
    while True:
        data = sock.recv(1436)
        # 如果这一帧数据包的开头是 b'Frame Begin' 则是一张图片的开始
        if data[0:len(begin_data)] == begin_data:
            # 将这一帧数据包的开始标志信息(b'Frame Begin')清除   因为他不属于图片数据
            data = data[len(begin_data):len(data)]
            # 判断这一帧数据流是不是最后一个帧 最后一针数据的结尾时b'Frame Over'
            while data[-len(end_data):] != end_data:
                temp_data = temp_data + data  # 不是结束的包 讲数据添加进temp_data
                data = sock.recv(1436)  # 继续接受数据 直到接受的数据包包含b'Frame Over' 表示是这张图片的最后一针
            # 判断为最后一个包 将数据去除 结束标志信息 b'Frame Over'
            temp_data = temp_data + data[0:(len(data) - len(end_data))]  # 将多余的(\r\nFrame Over)去掉 其他放入temp_data
            # 将接收到的 Base64 编码的数据解码
            base64_data = temp_data.decode('utf-8')
            img_data = base64.b64decode(base64_data)
            # 显示图片
            receive_data = np.frombuffer(img_data, dtype='uint8')  # 将获取到的字符流数据转换成1维数组
            r_img = cv2.imdecode(receive_data, cv2.IMREAD_COLOR)  # 将数组解码成图像

            # 计算帧率
            t2 = int(round(time.time() * 1000))
            fps = 1000 // (t2 - t1)
            cv2.putText(r_img, "FPS" + str(fps), (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)

            # # 显示接收到图片
            # if r_img is not None:
            #     cv2.imshow('server_frame', r_img)
            #     if cv2.waitKey(10) & 0xFF == ord('q'):
            #         break
            # t1 = t2
            # print("接收到的数据包大小:" + str(len(temp_data)))  # 显示该张照片数据大小

            # 保存图片
            save_image(r_img,addr)
            temp_data = b''  # 清空数据 便于下一章照片使用


def save_image(img, addr):
    # 使用当前时间戳作为文件名,确保文件名唯一
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    # 生成文件名,可以根据客户端的 IP 或者端口生成
    filename = os.path.join(SAVE_DIR, f"image_{timestamp}_{addr[0]}_{addr[1]}.jpg")

    # 保存图片
    cv2.imwrite(filename, img)
    print(f"图片已保存为: {filename}")

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('192.168.3.235', 8080)) # 替换为你的本机ip地址以及你想要设置的端口号
server.listen(5)
CONNECTION_LIST = []

# 主线程循环接收客户端连接
while True:
    sock, addr = server.accept()
    CONNECTION_LIST.append(sock)
    print('Connect--{}'.format(addr))
    # 连接成功后开一个线程用于处理客户端
    client_thread = threading.Thread(target=handle_sock, args=(sock, addr))
    client_thread.start()

### 使用ESP32-CAM通过MQTT协议进行视频流传输 要实现ESP32-CAM通过MQTT协议发送视频流,可以采用以下方法。首先需要配置ESP32-CAM作为摄像头设备捕获图像数据,并将其转换为适合通过网络传输的格式(如JPEG)。接着利用MQTT客户端库将这些帧发布到指定的主题上。 #### 配置环境 为了完成此功能,需安装必要的库文件并设置开发环境。具体来说,在Arduino IDE中添加支持ESP32硬件平台的支持包以及PubSubClient MQTT库[^1]。 ```cpp #include <WiFi.h> #include <PubSubClient.h> // WiFi 和 MQTT服务器参数 const char* ssid = "your_SSID"; const char* password = "your_PASSWORD"; const char* mqtt_server = "broker_address"; WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void reconnect() { // 循环直到重新连接成功 while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("ESP32Cam")) { Serial.println("connected"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } ``` 上述代码片段实现了Wi-Fi连接初始化与MQTT重连机制的功能。 #### 图像捕捉与处理 接下来定义函数来获取来自ESP32-CAM传感器的数据并将它们编码成JPEG格式以便于后续操作: ```cpp #include "esp_camera.h" camera_fb_t *frameBuffer; bool captureImage(String &imageString){ frameBuffer = esp_camera_fb_get(); if(!frameBuffer){ Serial.println("Camera capture failed."); return false; } imageString = base64::encode(frameBuffer->buf, frameBuffer->len); esp_camera_fb_return(frameBuffer); return true; } ``` 这里调用了`esp_camera_fb_get()`函数从摄像机缓冲区读取最新的一帧画面,并使用Base64编码方式对其进行序列化处理以适应MQTT消息体的要求。 #### 发布视频帧至MQTT主题 最后一步是在循环里定期抓拍新照片并通过已建立好的MQTT通道广播出去: ```cpp void loop(){ String encodedImage; if(captureImage(encodedImage)){ client.publish("/cam",encodedImage.c_str()); } delay(1000); // 控制刷新频率 } ``` 以上即完成了整个流程的设计思路说明及其对应的部分源码展示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值