esp32cam -> 服务器 | 手机 -> 服务器 直接服务器传输图片

news2025/4/17 23:57:38

服务器先下载python

一、Python环境搭建(CentOS/Ubuntu通用)

一条一条执行

安装基础依赖

# CentOS

sudo yum install gcc openssl-devel bzip2-devel libffi-devel zlib-devel

# Ubuntu

sudo apt update && sudo apt install build-essential libssl-dev libffi-dev zlib1g-dev

下载并编译Python 3.7+

wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz

tar xvf Python-3.7.12.tgz

cd Python-3.7.12

./configure --enable-optimizations

make -j 2 # 根据CPU核心数调整

sudo make altinstall # 保留系统默认Python

验证安装

python3.7 -V # 应显示Python 3.7.12

需要配置的环境:

# 创建目录并设置权限
mkdir -p images
chmod 777 images  #一定要确保images的777 因为它原理是esp32上传图片到images的文件夹

#由用户访问5000端口时反回最后一张图片给他

# 安装依赖
pip install flask

# 运行服务(后台运行)
nohup python app.py > server.log 2>&1 &

也可以直接 python3 cam_server.py一次性调用

 使用步骤:

使用步骤:

  1. 服务器端:

bash

复制

# 创建目录并设置权限
mkdir -p images
chmod 777 images

# 安装依赖
pip install flask

# 运行服务(后台运行)
nohup python app.py > server.log 2>&1 &
  1. Arduino端:

  • 使用PlatformIO或Arduino IDE上传代码

  • 打开串口监视器查看连接状态

  1. 手机访问:

复制

http://159.75.100.98:5000

关键修正点说明:

  1. 服务器端:

  • 使用send_from_directory函数前必须从flask导入

  • 简化了图片接收逻辑,直接读取原始数据

  • 添加时间戳防止浏览器缓存

  • 设置严格的文件夹权限

  1. Arduino端:

  • 优化多部分格式的构建方式

  • 确保分三次发送完整请求

  • 增加更详细的错误提示

如果仍遇到问题,请按以下步骤排查:

  1. 检查服务器5000端口是否开放

  2. 查看服务器日志:tail -f server.log

  3. 在服务器测试图片上传:

bash

复制

curl -X POST http://localhost:5000/upload -F "image=@test.jpg"
  1. 检查images目录权限:

bash

复制

ls -ld images

arduino代码适配AI Thinker ESP32-CAM板子

#include <WiFi.h>
#include <HTTPClient.h>
#include "esp_camera.h"

const char* ssid = "Redmi K70";
const char* password = "ss20051512";
const char* serverIP = "159.75.100.98";
const int serverPort = 5000;

// 摄像头引脚配置(保持原样)
#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

void setup() {
  Serial.begin(115200);
  
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size = FRAMESIZE_UXGA;
  config.pixel_format = PIXFORMAT_JPEG;  // for streaming
  //config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition
  config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_PSRAM;
  config.jpeg_quality = 12;
  config.fb_count = 1;
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed: 0x%x", err);
    return;
  }

  WiFi.begin(ssid, password);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi Connected");
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    camera_fb_t *fb = esp_camera_fb_get();
    if(!fb || fb->len == 0) {
      Serial.println("Capture Failed");
      return;
    }

    HTTPClient http;
    String url = "http://" + String(serverIP) + ":" + String(serverPort) + "/upload";
    http.begin(url);
    
    // 直接发送JPEG二进制数据
    http.addHeader("Content-Type", "image/jpeg");
    http.addHeader("Content-Length", String(fb->len));
    
    int httpCode = http.POST(fb->buf, fb->len);
    
    if(httpCode == HTTP_CODE_OK) {
      Serial.printf("Image Sent. Size: %dB\n", fb->len);
    } else {
      Serial.printf("Error Code: %d\n", httpCode);
    }
    
    http.end();
    esp_camera_fb_return(fb);
  }
  delay(3000); // 适当缩短延时
}

服务器代码:

from flask import Flask, request, render_template_string, send_from_directory
import os
import time

app = Flask(__name__)
UPLOAD_FOLDER = './images'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

# 强制设置权限
os.chmod(UPLOAD_FOLDER, 0o777)

HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
    <title>实时监控</title>
    <meta http-equiv="refresh" content="3">
    <style>
        img { max-width: 100%; height: auto; border: 2px solid #333; }
    </style>
</head>
<body>
    <h1>ESP32-CAM 实时画面</h1>
    <img src="/latest.jpg?t={timestamp}">
</body>
</html>
"""

@app.route('/')
def index():
    return render_template_string(HTML_TEMPLATE.replace("{timestamp}", str(time.time())))

@app.route('/upload', methods=['POST'])
def upload():
    try:
        # 直接读取二进制数据
        raw_data = request.get_data()
        if len(raw_data) < 100:  # 简单验证数据有效性
            return "Invalid image data", 400
            
        timestamp = str(int(time.time()))
        filename = f"{timestamp}.jpg"
        filepath = os.path.join(UPLOAD_FOLDER, filename)
        
        with open(filepath, 'wb') as f:
            f.write(raw_data)
        
        # 更新符号链接
        latest_path = os.path.join(UPLOAD_FOLDER, 'latest.jpg')
        if os.path.lexists(latest_path):
            os.remove(latest_path)
        os.symlink(filename, latest_path)
        
        return f"Received {len(raw_data)} bytes", 200
        
    except Exception as e:
        return f"Server Error: {str(e)}", 500

@app.route('/latest.jpg')
def serve_latest():
    try:
        return send_from_directory(UPLOAD_FOLDER, 'latest.jpg', mimetype='image/jpeg')
    except:
        return "Image not available", 404

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2331627.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

豆浆机语音提示芯片方案:基于可远程在线更换语音的WT2003H-16S芯片

随着智能家居概念的普及&#xff0c;消费者对家电产品的智能化、便捷性提出了更高要求。豆浆机作为厨房常用电器&#xff0c;其操作便捷性和用户体验直接影响市场竞争力。传统豆浆机多依赖指示灯或简单蜂鸣器提示用户操作状态&#xff0c;信息传递单一且无法满足个性化需求。 在…

解密工业控制柜:认识关键硬件(PLC)

前言 作为一名视觉开发工程师&#xff0c;我们不仅要做到做好自己的工作&#xff0c;我们更需要在工业现场学习更多知识&#xff0c;最近网上流传很多&#xff0c;“教会徒弟&#xff0c;饿死师傅”&#xff1b;在自动化行业中&#xff0c;在项目下来很忙的时候&#xff0c;我们…

【嵌入式系统设计师】知识点:第11 章 嵌入式系统设计案例分析

提示:“软考通关秘籍” 专栏围绕软考展开,全面涵盖了如嵌入式系统设计师、数据库系统工程师、信息系统管理工程师等多个软考方向的知识点。从计算机体系结构、存储系统等基础知识,到程序语言概述、算法、数据库技术(包括关系数据库、非关系型数据库、SQL 语言、数据仓库等)…

【深度解析】SkyWalking 10.2.0版本安全优化与性能提升实战指南

前言 Apache SkyWalking 作为云原生可观测性领域的佼佼者&#xff0c;在微服务架构监控中扮演着至关重要的角色。然而&#xff0c;官方版本在安全性、镜像体积和功能扩展方面仍有优化空间。本文将分享一套完整的 SkyWalking 10.2.0 版本优化方案&#xff0c;从安全漏洞修复到镜…

面向大模型的开发框架LangChain

这篇文章会带给你 如何使用 LangChain&#xff1a;一套在大模型能力上封装的工具框架如何用几行代码实现一个复杂的 AI 应用面向大模型的流程开发的过程抽象 文章目录 这篇文章会带给你写在前面LangChain 的核心组件文档&#xff08;以 Python 版为例&#xff09;模型 I/O 封装…

Easysearch VS Opensearch 数据写入与存储性能对比

本文记录 Easysearch 和 Opensearch 数据写入和数据存储方面的性能对比。 准备 压测工具&#xff1a;INFINI Loadgen 对比版本&#xff1a; Easysearch 1.11.1&#xff08;lucene 8.11.4&#xff09;Opensearch 2.19.1&#xff08;lucene 9.12.1&#xff09; 节点 JVM 配置…

【Proteus仿真】【32单片机-A009】矩阵按键系统设计

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 联系作者 一、主要功能 1、按键值与LCD显示 2、矩阵按键 二、使用步骤 系统运行后&#xff0c;LCD1602显示当前的按键值&#xff1b; 当按下不同按键后显示屏更新对应的按键值。 三、硬件资…

用一个实际例子快速理解MCP应用的工作步骤

已经有很多的文章介绍MCP server&#xff0c;MCP Client工作原理&#xff0c;这里不做太多介绍。但是很多介绍都只是侧重介绍概念&#xff0c;实际的工作原理理解起来对初学者还是不太友好。本文以一个智能旅游咨询系统为例&#xff0c;详细说明在利用 Model Context Protocol&…

TCP 和 UDP 可以使用同一个端口吗?

TCP 和 UDP 可以使用同一个端口吗&#xff1f; 前言 在深入探讨 TCP 和 UDP 是否可以使用同一个端口之前&#xff0c;我们首先需要理解网络通信的基本原理。网络通信是一个复杂的过程&#xff0c;涉及到多个层次的协议和机制。在 OSI 模型中&#xff0c;传输层是负责端到端数…

探索原生JS的力量:自定义实现类似于React的useState功能

1.写在前面 本方案特别适合希望在历史遗留的原生JavaScript项目中实现简单轻量级数据驱动机制的开发者。无需引入任何框架或第三方库&#xff0c;即可按照此方法封装出类似于React中useState的功能&#xff0c;轻松为项目添加状态管理能力&#xff0c;既保持了项目的轻量性&am…

HBuilder运行uni-app程序报错【Error: listen EACCES: permission denied 0.0.0.0:5173】

一、错误提示&#xff1a; 当使用HBuilder运行uni-app项目的时候提示了如下错误❌ 15:11:03.089 项目 project 开始编译 15:11:04.404 请注意运行模式下&#xff0c;因日志输出、sourcemap 以及未压缩源码等原因&#xff0c;性能和包体积&#xff0c;均不及发行模式。 15:11:04…

VMware Fusion Pro 13 for Mac虚拟机

VMware Fusion Pro 13 for Mac虚拟机 文章目录 VMware Fusion Pro 13 for Mac虚拟机一、介绍二、效果下载 一、介绍 VMware Fusion Pro for Mac&#xff0c;是一款mac虚拟机软件&#xff0c;跟Parallels Desktop一样&#xff0c;都可以让你的 Mac 同时运行一个或多个不同的操作…

7.第二阶段x64游戏实战-string类

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;7.第二阶段x64游戏实战-分析人物属性 string类是字符串类&#xff0c;在计算机中…

【debug莫名其妙跑飞了】

现象&#xff1a;就是在初始化汇编里跑飞了&#xff0c;也可能运行起来时钟不对 原因&#xff1a;调试器调试程序时会执行reset复位&#xff0c;reset没有正确执行。 细节决定成败&#xff0c;事出反常必有妖&#xff0c;忽略的小卡拉米最后能玩死你啊

基础知识补充篇:什么是DAPP前端连接中的provider

专栏:区块链入门到放弃查看目录-CSDN博客文章浏览阅读352次。为了方便查看将本专栏的所有内容列出目录,按照顺序查看即可。后续也会在此规划一下后续内容,因此如果遇到不能点击的,代表还没有更新。声明:文中所出观点大多数源于笔者多年开发经验所总结,如果你想要知道区块…

openssl源码分析之加密模式(modes)

openssl实现分组加密模式&#xff08;例如AES128-CBC的CBC部分&#xff09;的模块名字叫做modes&#xff0c;源代码位于 https://gitee.com/gh_mirrors/openssl/tree/master/crypto/modes 博主又打不开github了TT&#xff0c;只能找个gitee镜像 头文件是modes.h。 该模块目前…

【PVR】《Palm Vein Recognition and Large-scale Research based on Deep Learning》

邬晓毅. 基于深度学习的掌静脉识别及规模化研究[D]. 四川:电子科技大学,2024. 文章目录 1、背景2、相关工作3、创新点和贡献4、方法和实验4.1、知识介绍4.2、基于自适应损失函数的掌静脉识别算法研究4.3、退化图像的掌静脉识别鲁棒性提升研究4.4、掌静脉识别系统规模化 5、总结…

PyQt学习记录

PyQt学习记录 要在界面上 创建一个控件&#xff0c;就需要在程序代码中 创建 这个 控件对应类 地一个 实例对象。 在Qt系统中&#xff0c;控件&#xff08;widget&#xff09;是 层层嵌套 的&#xff0c;除了最顶层的控件&#xff0c;其他的控件都有父控件。 几个函数 函数mo…

Linux 学习笔记(5)路径知识详解:绝对路径、相对路径与特殊路径符(期末、期中复习必备)

前言 一、相对路径与绝对路径 1、概念阐述 2、实际示例 二、特殊路径符 1.特殊路径符介绍 2.应用场景 三、总结 四、结语 前言 在 Linux 系统的学习过程中&#xff0c;路径的概念至关重要&#xff0c;它是我们在文件系统中定位文件和目录的关键。今天&#xff0c;我们就…

Trae + LangGPT 生成结构化 Prompt

Trae LangGPT 生成结构化 Prompt 0. 引言1. 安装 Trae2. 克隆 LangGPT3. Trae 和 LangGPT 联动4. 集成到 Dify 中 0. 引言 Github 上 LangGPT 这个项目&#xff0c;主要向我们介绍了写结构化Prompt的一些方法和示例&#xff0c;我们怎么直接使用这个项目&#xff0c;辅助我们…