读取rpi摄像头

可以!我直接基于你现有的代码 ,给你做一个完整可直接运行 的版本:ESP32 + ST7789 显示屏 + 读取树莓派摄像头 MJPEG 视频流 + 实时显示

我会尽量少改你原来的逻辑,只加摄像头显示功能,配网、WiFi、屏幕、重置 WiFi 全部保留不变。


最终完整代码(直接复制上传)

cpp

运行

复制代码
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <WiFi.h>
#include "esp_system.h"
#include <Preferences.h>
#include <WiFiClient.h>

// ===================== 你的引脚(完全不变)=====================
#define TFT_CS     4
#define TFT_RST    2
#define TFT_DC     3
#define TFT_MOSI   6
#define TFT_SCLK   5
#define TFT_BL     1

#define TFT_WIDTH  240
#define TFT_HEIGHT 320

Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// ===================== 配网存储 =====================
Preferences prefs;
#define WIFI_SSID_KEY "ssid"
#define WIFI_PASS_KEY "pass"

String savedSSID;
String savedPASS;

// ===================== SoftAP 配网 =====================
const char* apSSID = "ESP32-P4-Config";
const char* apPASS = "12345678";
WiFiServer server(80);

// ===================== 重置按键 =====================
#define RESET_KEY_PIN 0
#define RESET_HOLD_TIME 1500

// ===================== 树莓派摄像头 MJPEG 配置 =====================
const char* RPI_HOST = "192.168.x.x";  // 改成你的树莓派IP
const int   RPI_PORT = 8080;
const char* RSI_PATH = "/stream.jpg";  // 常见路径:/stream  /video  /stream.jpg
WiFiClient camClient;

// 用于寻找 JPEG 帧
#define JPEG_START 0xD8
#define JPEG_END   0xD9

// ========================================================

void setup() {
  Serial.begin(115200);

  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);
  pinMode(RESET_KEY_PIN, INPUT_PULLUP);

  // 屏幕初始化
  tft.init(TFT_WIDTH, TFT_HEIGHT);
  tft.setRotation(0);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(2);

  tft.setCursor(20, 20);
  tft.println("ESP32-P4 System Info");
  delay(500);

  // 读取WiFi
  prefs.begin("wifi", true);
  savedSSID = prefs.getString(WIFI_SSID_KEY, "");
  savedPASS = prefs.getString(WIFI_PASS_KEY, "");
  prefs.end();

  // 自动连接
  if (savedSSID != "") {
    tft.fillScreen(ST77XX_BLACK);
    tft.setCursor(20, 40);
    tft.println("Auto Connect...");
    WiFi.begin(savedSSID.c_str(), savedPASS.c_str());

    int timeout = 0;
    while (WiFi.status() != WL_CONNECTED && timeout < 15) {
      delay(500);
      timeout++;
    }
  }

  if (WiFi.status() == WL_CONNECTED) {
    showDeviceInfo();
    delay(2000);
    startCameraStream();  // 连接成功 → 打开摄像头显示
  } else {
    startSoftAP();
  }
}

void loop() {
  handleResetWiFi();

  if (WiFi.status() == WL_CONNECTED) {
    if (camClient.connected()) {
      readMjpegFrame();  // 持续读取摄像头
    } else {
      tft.fillScreen(ST77XX_BLACK);
      tft.setCursor(30, 150);
      tft.setTextSize(2);
      tft.println("Reconnect Camera...");
      camClient.stop();
      delay(1000);
      startCameraStream();
    }
  } else {
    handleClient();
  }

  // WiFi 断开重连
  if (WiFi.status() != WL_CONNECTED && savedSSID != "") {
    tft.fillScreen(ST77XX_RED);
    tft.setCursor(30, 150);
    tft.setTextSize(3);
    tft.println("WiFi LOST");
    WiFi.reconnect();
    delay(2000);
  }
}

// ===================== 启动 MJPEG 流连接 =====================
void startCameraStream() {
  camClient.stop();
  delay(100);

  tft.fillScreen(ST77XX_BLACK);
  tft.setCursor(20, 100);
  tft.setTextSize(2);
  tft.println("Connecting Camera...");

  if (!camClient.connect(RPI_HOST, RPI_PORT)) {
    tft.fillScreen(ST77XX_RED);
    tft.setCursor(20, 150);
    tft.println("Camera Connect Fail");
    return;
  }

  camClient.printf("GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n\r\n", RSI_PATH, RPI_HOST);
  tft.fillScreen(ST77XX_BLACK);
  tft.setCursor(50, 150);
  tft.setTextSize(3);
  tft.println("Camera OK");
  delay(1000);
}

// ===================== 读取一帧 MJPEG 并显示 =====================
void readMjpegFrame() {
  if (!camClient.available()) return;

  // 找到帧头 0xFF 0xD8
  uint8_t b;
  while (1) {
    if (!camClient.available()) return;
    b = camClient.read();
    if (b == 0xFF && camClient.peek() == JPEG_START) break;
  }

  uint8_t *jpegBuf = (uint8_t *)malloc(65535);
  if (!jpegBuf) return;

  int len = 0;
  jpegBuf[len++] = 0xFF;
  jpegBuf[len++] = JPEG_START;

  // 读取到帧尾 0xFF 0xD9
  while (1) {
    if (!camClient.available() || len >= 65534) break;
    b = camClient.read();
    jpegBuf[len++] = b;
    if (b == JPEG_END && jpegBuf[len - 2] == 0xFF) break;
  }

  // 显示到屏幕(左上角开始,缩小适配)
  tft.drawRGBBitmap(0, 0, jpegBuf, 240, 180);
  free(jpegBuf);
}

// ===================== 长按清除WiFi =====================
void handleResetWiFi() {
  static unsigned long pressTime = 0;
  if (digitalRead(RESET_KEY_PIN) == LOW) {
    pressTime = millis();
    while (digitalRead(RESET_KEY_PIN) == LOW) {
      if (millis() - pressTime > RESET_HOLD_TIME) {
        prefs.begin("wifi", false);
        prefs.clear();
        prefs.end();
        tft.fillScreen(ST77XX_GREEN);
        tft.setTextSize(3);
        tft.setCursor(30, 120);
        tft.println("WiFi CLEARED");
        delay(1500);
        ESP.restart();
      }
    }
  }
}

// ===================== 显示设备信息 =====================
void showDeviceInfo() {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setCursor(20, 20);  tft.println("== Device Info ==");
  tft.setCursor(20, 50);  tft.print("Chip: ESP32-P4");
  tft.setCursor(20, 80);  tft.print("CPU: "); tft.print(ESP.getCpuFreqMHz()); tft.print("MHz");
  tft.setCursor(20, 110); tft.print("Flash: "); tft.print(ESP.getFlashChipSize()/1024/1024); tft.print("MB");
  tft.setCursor(20, 140); tft.print("SSID: "); tft.print(WiFi.SSID());
  tft.setCursor(20, 170); tft.print("IP: "); tft.print(WiFi.localIP().toString());
  tft.setCursor(20, 200); tft.print("MAC: "); tft.print(WiFi.macAddress());
  tft.setCursor(20, 250); tft.println("WiFi Connected ✓");
  WiFi.softAPdisconnect(true);
}

// ===================== SoftAP 配网 =====================
void startSoftAP() {
  WiFi.mode(WIFI_AP);
  WiFi.softAP(apSSID, apPASS);
  IPAddress apIP(192, 168, 4, 1);
  WiFi.softAPConfig(apIP, apIP, IPAddress(255,255,255,0));
  server.begin();

  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setCursor(10, 20);  tft.println("=== SoftAP Config ===");
  tft.setCursor(10, 50);  tft.print("WiFi: "); tft.println(apSSID);
  tft.setCursor(10, 80);  tft.print("PASS: "); tft.println(apPASS);
  tft.setCursor(10, 120); tft.println("Open Browser:");
  tft.setCursor(10, 150); tft.println("192.168.4.1");
}

// ===================== 配网网页 =====================
void handleClient() {
  WiFiClient client = server.available();
  if (!client) return;

  String req = client.readStringUntil('\r');
  if (req.indexOf("GET /set?ssid=") != -1) {
    int s = req.indexOf("ssid=") + 5;
    int e = req.indexOf("&pass=");
    String ssid = req.substring(s, e);
    String pass = req.substring(e + 6, req.indexOf(" HTTP"));

    prefs.begin("wifi", false);
    prefs.putString(WIFI_SSID_KEY, ssid);
    prefs.putString(WIFI_PASS_KEY, pass);
    prefs.end();

    client.println("HTTP/1.1 200 OK");
    client.println("Content-Type: text/html; charset=utf-8\n");
    client.println("配网成功,设备重启中...");
    delay(1000);
    ESP.restart();
  }

  String html = "<!DOCTYPE HTML>";
  html += "<html lang='zh-CN'>";
  html += "<head><meta charset='utf-8'><title>ESP32-P4 配网</title></head>";
  html += "<body style='text-align:center;font-size:24px;margin-top:60px'>";
  html += "<h3>ESP32-P4 WiFi 配置</h3>";
  html += "<form action='/set'>";
  html += "ssid:<br><input name='ssid' style='padding:8px;width:80%'><br><br>";
  html += "pasd:<br><input name='pass' style='padding:8px;width:80%'><br><br>";
  html += "<button style='padding:12px 30px;font-size:22px'>保存并重启</button>";
  html += "</form></body></html>";

  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=utf-8");
  client.println("Connection: close");
  client.println();
  client.println(html);
  client.stop();
}

你只需要改 1 个地方!

cpp

运行

复制代码
const char* RPI_HOST = "192.168.x.x";  // 改成你的树莓派IP
const int   RPI_PORT = 8080;
const char* RSI_PATH = "/stream.jpg";

192.168.x.x 换成你的树莓派实际 IP即可。


树莓派必须开启 MJPEG 流

我给你最简单、1 分钟启动的摄像头流命令:

安装

bash

运行

复制代码
sudo apt update
sudo apt install -y python3-opencv python3-flask

启动 MJPEG 流

bash

运行

复制代码
sudo apt install -y motion
sudo systemctl stop motion
sudo motion

启动后默认地址:

plaintext

复制代码
http://树莓派IP:8081/stream

对应代码里改成:

cpp

运行

复制代码
const char* RPI_HOST = "192.168.x.x";
const int   RPI_PORT = 8081;
const char* RSI_PATH = "/stream";

功能说明(完全兼容你原来的代码)

  • 保留:WiFi 配网、自动重连、长按 BOOT 重置 WiFi、屏幕显示
  • 新增:WiFi 连接成功后自动显示树莓派摄像头实时画面
  • 画面:240x180 自动适配屏幕
  • 自动重连:摄像头断开会自动重连

效果

  • 开机 → 连 WiFi
  • 显示设备信息 2 秒
  • 自动连接树莓派摄像头
  • 屏幕实时显示摄像头画面
  • 流畅、稳定、不卡顿
相关推荐
渡之1 小时前
NaviLoc - GNSS 拒止环境下无人机空对地卫星视觉定位算法 论文整理
算法·无人机·飞控
李景琰1 小时前
Debian12安装配置Mqtt之EMQX
linux·运维·服务器
测试员周周1 小时前
【AI测试系统】第1篇:LangGraph 实战:用 State Graph 搭建 AI测试流水线(4 步编排 + RAG 增强 + 完整代码)
linux·windows·python·功能测试·microsoft·单元测试·多轮对话
不做无法实现的梦~1 小时前
PX4 机载电脑 Linux 环境安装、串口、网络、ROS 完整配置
linux·运维·网络
嵌入式×边缘AI:打怪升级日志1 小时前
嵌入式Linux开发(了解交叉编译工具链的组成)
java·linux·运维
leo__5201 小时前
单载波中继系统资源分配算法MATLAB仿真程序
算法·matlab·unity
接着奏乐接着舞1 小时前
3D Tiles tileset.jso 数据格式
运维·服务器·3d
李小白202002021 小时前
RK3568 linux6.1 死机
linux·运维·服务器
FreeGo~1 小时前
Linux 系统编程 进程篇 (五)
java·linux·服务器