ESP32-CAM 视频小车(App Inventor 控制)完整方案

ESP32-CAM 视频小车(App Inventor 控制)完整方案

 
我会为你提供一套完整的 ESP32-CAM 视频小车解决方案,包含硬件接线、ESP32 端代码、App Inventor 应用设计,让你能快速实现视频实时传输和小车遥控。
 

一、整体方案思路

 
  1. ESP32-CAM 核心功能:
    • 建立 WiFi 热点,供手机连接
    • 启动 Web 服务器,实时传输摄像头视频流
    • 接收 App 发送的控制指令(前进 / 后退 / 左转 / 右转 / 停止)
    • 控制电机驱动模块(如 L298N/L293D)驱动小车
     
  2. App Inventor 应用功能:
    • 连接 ESP32-CAM 的 WiFi 热点
    • 显示摄像头实时视频流
    • 发送控制指令到 ESP32-CAM
    • 简单的 UI 设计(方向按键、视频显示区域)
     
 

二、硬件准备

 
表格
 
组件数量说明
ESP32-CAM 模块 1 带 OV2640 摄像头
电机驱动模块 1 L298N 或 L293D
直流减速电机 2 小车驱动
四轮小车底盘 1 含轮胎、支架
锂电池(7.4V/12V) 1 供电(需 5V 稳压给 ESP32)
杜邦线 若干 接线使用
5V 稳压模块 1 给 ESP32-CAM 供电
 

三、硬件接线

 

1. ESP32-CAM 与电机驱动模块接线

 
表格
 
ESP32-CAM 引脚L298N 引脚功能
GPIO13 IN1 左电机正转
GPIO12 IN2 左电机反转
GPIO14 IN3 右电机正转
GPIO2 IN4 右电机反转
5V 12V 电源(需稳压)
GND GND 共地
 

2. 供电说明

 
  • 电机驱动模块接 7.4V/12V 锂电池
  • ESP32-CAM 必须接 5V 稳压电源(直接接锂电池会烧毁)
  • 所有模块的 GND 必须共地
 

四、ESP32-CAM 端代码

 

1. 代码功能

 
  • 创建 WiFi 热点(可自定义名称和密码)
  • 启动 HTTP 服务器,提供视频流和控制接口
  • 响应 App 发送的控制指令,驱动电机
 
cpp
 
运行
 
 
 
 
#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>

// ================== 配置参数 ==================
// WiFi 热点配置
const char* ssid = "ESP32-CAR";    // 热点名称
const char* password = "12345678"; // 热点密码

// 电机引脚定义
#define IN1 13
#define IN2 12
#define IN3 14
#define IN4 2

// 摄像头配置(ESP32-CAM 默认配置)
#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
// ==============================================

WebServer server(80); // 创建 Web 服务器,端口 80

// 电机控制函数
void stopCar() {
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
}

void forward() {
  stopCar();
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}

void backward() {
  stopCar();
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}

void left() {
  stopCar();
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}

void right() {
  stopCar();
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}

// 视频流传输函数
String getContentType(String filename) {
  if (filename.endsWith(".html")) return "text/html";
  else if (filename.endsWith(".jpg")) return "image/jpeg";
  return "text/plain";
}

bool handleFileRead(String path) {
  if (path.endsWith("/")) path += "index.html";
  String contentType = getContentType(path);
  String pathWithGz = path + ".gz";
  if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) {
    if (SPIFFS.exists(pathWithGz)) path += ".gz";
    File file = SPIFFS.open(path, "r");
    size_t sent = server.streamFile(file, contentType);
    file.close();
    return true;
  }
  return false;
}

// 摄像头初始化
void initCamera() {
  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_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  
  // 分辨率配置(降低分辨率提升传输速度)
  if (psramFound()) {
    config.frame_size = FRAMESIZE_VGA; // VGA (640x480)
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_QVGA; // QVGA (320x240)
    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 with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }
}

// 视频流处理
void handleStream() {
  WiFiClient client = server.client();
  camera_fb_t * fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
    return;
  }
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: image/jpeg");
  client.print("Content-Length: ");
  client.println(fb->len);
  client.println();
  client.write(fb->buf, fb->len);
  client.println();
  esp_camera_fb_return(fb);
}

// 初始化服务器路由
void initServer() {
  // 视频流接口
  server.on("/stream", HTTP_GET, handleStream);
  
  // 控制指令接口
  server.on("/forward", HTTP_GET, []() {
    forward();
    server.send(200, "text/plain", "forward");
  });
  
  server.on("/backward", HTTP_GET, []() {
    backward();
    server.send(200, "text/plain", "backward");
  });
  
  server.on("/left", HTTP_GET, []() {
    left();
    server.send(200, "text/plain", "left");
  });
  
  server.on("/right", HTTP_GET, []() {
    right();
    server.send(200, "text/plain", "right");
  });
  
  server.on("/stop", HTTP_GET, []() {
    stopCar();
    server.send(200, "text/plain", "stop");
  });
  
  // 根目录返回控制页面(可选)
  server.on("/", HTTP_GET, []() {
    server.send(200, "text/html", 
      "<html><body>"
      "<h1>ESP32-CAR Control</h1>"
      "<img src='/stream' width='640' height='480'><br>"
      "<button onclick='fetch(\"/forward\")'>Forward</button>"
      "<button onclick='fetch(\"/backward\")'>Backward</button>"
      "<button onclick='fetch(\"/left\")'>Left</button>"
      "<button onclick='fetch(\"/right\")'>Right</button>"
      "<button onclick='fetch(\"/stop\")'>Stop</button>"
      "</body></html>"
    );
  });
  
  server.onNotFound([]() {
    if (!handleFileRead(server.uri())) {
      server.send(404, "text/plain", "404 Not Found");
    }
  });
  
  server.begin();
  Serial.println("Web server started");
}

void setup() {
  Serial.begin(115200);
  
  // 初始化电机引脚
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  stopCar(); // 初始状态停止
  
  // 初始化摄像头
  initCamera();
  
  // 创建 WiFi 热点
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP); // 热点 IP 通常是 192.168.4.1
  
  // 初始化 Web 服务器
  initServer();
}

void loop() {
  server.handleClient(); // 处理客户端请求
  delay(10);
}
 
 

2. 代码上传说明

 
  1. 使用 Arduino IDE,先安装 ESP32 开发板支持
  2. 选择开发板:ESP32 Wrover Module
  3. 配置参数:
    • Partition Scheme: Huge APP (3MB No OTA/1MB SPIFFS)
    • Flash Mode: QIO
    • Flash Frequency: 80MHz
     
  4. 上传代码(ESP32-CAM 需要接下载器,上传时按住 BOOT 键)
 

五、App Inventor 应用设计

 

1. App Inventor 界面布局

 
打开 App Inventor 2,创建新项目,按以下步骤设计界面:
 

(1)组件列表

 
表格
 
组件类型组件名称作用
水平布局 HorizontalArr1 放置方向按键
按钮 btnForward 前进
按钮 btnBackward 后退
按钮 btnLeft 左转
按钮 btnRight 右转
按钮 btnStop 停止
Web 浏览器 WebViewer1 显示视频流
标签 Label1 显示提示信息
 

(2)界面设计步骤

 
  1. 拖入 WebViewer 组件,设置宽度 / 高度为全屏(如宽度 320,高度 240)
  2. 拖入水平布局,在布局中添加 5 个按钮(前进 / 后退 / 左转 / 右转 / 停止)
  3. 设置按钮文本和大小,优化界面美观度
 

2. 组件属性配置

 
  • WebViewer1:
    • HomeURL: http://192.168.4.1/stream(ESP32 热点默认 IP)
    • EnableJavaScript: True
     
  • 按钮:
    • 设置不同的背景色(如前进绿色、停止红色)
    • 设置字体大小为 16sp
     
 

3. 逻辑代码(块编程)

 

(1)初始化

 
plaintext
 
 
当屏幕初始化时
  设置 WebViewer1.URL 为 "http://192.168.4.1/stream"
  WebViewer1.刷新页面
 
 

(2)按钮点击事件(以前进为例)

 
plaintext
 
 
当 btnForward 被点击时
  调用 发送指令 过程 ("/forward")
 
 

(3)通用指令发送过程

 
创建一个自定义过程 发送指令,参数为 指令路径
 
plaintext
 
 
过程 发送指令 (指令路径)
  初始化 url 为 "http://192.168.4.1" + 指令路径
  调用 Web.Get 方法
    url: url
    成功时:
      设置 Label1.文本 为 "指令发送成功:" + 指令路径
    失败时:
      设置 Label1.文本 为 "指令发送失败,请检查连接"
 
 

(4)其他按钮

 
为后退(/backward)、左转(/left)、右转(/right)、停止(/stop)按钮绑定相同的逻辑,仅修改指令路径。
 

4. 导出 APK

 
设计完成后,点击 构建生成 APK,下载并安装到手机。
 

六、使用步骤

 
  1. 给 ESP32-CAM 小车上电,等待 WiFi 热点启动(约 5 秒)
  2. 打开手机 WiFi,连接 ESP32-CAR 热点(密码 12345678)
  3. 打开制作好的 App,此时 WebViewer 会显示摄像头视频流
  4. 点击方向按钮,即可控制小车移动
 

七、常见问题解决

 
  1. 视频流卡顿:降低摄像头分辨率(改为 FRAMESIZE_QVGA)
  2. 小车无响应:检查引脚接线、电机驱动模块供电、指令路径是否正确
  3. 无法连接热点:重启 ESP32-CAM,检查热点名称 / 密码是否正确
  4. App 无法显示视频:确认手机已连接 ESP32 热点,IP 地址是否为 192.168.4.1
 

总结

 
  1. 核心逻辑:ESP32-CAM 建立 WiFi 热点并启动 Web 服务器,提供视频流接口和控制接口,App Inventor 应用连接热点后,通过 WebViewer 显示视频流,通过 HTTP 请求发送控制指令。
  2. 关键配置:ESP32 端需正确配置摄像头参数和电机引脚,App 端需使用 ESP32 热点的默认 IP(192.168.4.1)访问视频流和控制接口。
  3. 避坑要点:ESP32-CAM 必须用 5V 稳压供电,上传代码时需按住 BOOT 键,视频分辨率过高会导致传输卡顿。

ESP32-CAM 视频小车(App Inventor 控制)完整方案

 
我会为你提供一套完整的 ESP32-CAM 视频小车解决方案,包含硬件接线、ESP32 端代码、App Inventor 应用设计,让你能快速实现视频实时传输和小车遥控。
 

一、整体方案思路

 
  1. ESP32-CAM 核心功能
    • 建立 WiFi 热点,供手机连接
    • 启动 Web 服务器,实时传输摄像头视频流
    • 接收 App 发送的控制指令(前进 / 后退 / 左转 / 右转 / 停止)
    • 控制电机驱动模块(如 L298N/L293D)驱动小车
     
  2. App Inventor 应用功能
    • 连接 ESP32-CAM 的 WiFi 热点
    • 显示摄像头实时视频流
    • 发送控制指令到 ESP32-CAM
    • 简单的 UI 设计(方向按键、视频显示区域)
     
 

二、硬件准备

 
表格
 
组件数量说明
ESP32-CAM 模块 1 带 OV2640 摄像头
电机驱动模块 1 L298N 或 L293D
直流减速电机 2 小车驱动
四轮小车底盘 1 含轮胎、支架
锂电池(7.4V/12V) 1 供电(需 5V 稳压给 ESP32)
杜邦线 若干 接线使用
5V 稳压模块 1 给 ESP32-CAM 供电
 

三、硬件接线

 

1. ESP32-CAM 与电机驱动模块接线

 
表格
 
ESP32-CAM 引脚L298N 引脚功能
GPIO13 IN1 左电机正转
GPIO12 IN2 左电机反转
GPIO14 IN3 右电机正转
GPIO2 IN4 右电机反转
5V 12V 电源(需稳压)
GND GND 共地
 

2. 供电说明

 
  • 电机驱动模块接 7.4V/12V 锂电池
  • ESP32-CAM 必须接 5V 稳压电源(直接接锂电池会烧毁)
  • 所有模块的 GND 必须共地
 

四、ESP32-CAM 端代码

 

1. 代码功能

 
  • 创建 WiFi 热点(可自定义名称和密码)
  • 启动 HTTP 服务器,提供视频流和控制接口
  • 响应 App 发送的控制指令,驱动电机
 
cpp
 
运行
 
 
 
 
#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>

// ================== 配置参数 ==================
// WiFi 热点配置
const char* ssid = "ESP32-CAR";    // 热点名称
const char* password = "12345678"; // 热点密码

// 电机引脚定义
#define IN1 13
#define IN2 12
#define IN3 14
#define IN4 2

// 摄像头配置(ESP32-CAM 默认配置)
#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
// ==============================================

WebServer server(80); // 创建 Web 服务器,端口 80

// 电机控制函数
void stopCar() {
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
}

void forward() {
  stopCar();
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}

void backward() {
  stopCar();
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}

void left() {
  stopCar();
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
}

void right() {
  stopCar();
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
}

// 视频流传输函数
String getContentType(String filename) {
  if (filename.endsWith(".html")) return "text/html";
  else if (filename.endsWith(".jpg")) return "image/jpeg";
  return "text/plain";
}

bool handleFileRead(String path) {
  if (path.endsWith("/")) path += "index.html";
  String contentType = getContentType(path);
  String pathWithGz = path + ".gz";
  if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) {
    if (SPIFFS.exists(pathWithGz)) path += ".gz";
    File file = SPIFFS.open(path, "r");
    size_t sent = server.streamFile(file, contentType);
    file.close();
    return true;
  }
  return false;
}

// 摄像头初始化
void initCamera() {
  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_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  
  // 分辨率配置(降低分辨率提升传输速度)
  if (psramFound()) {
    config.frame_size = FRAMESIZE_VGA; // VGA (640x480)
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_QVGA; // QVGA (320x240)
    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 with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }
}

// 视频流处理
void handleStream() {
  WiFiClient client = server.client();
  camera_fb_t * fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
    return;
  }
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: image/jpeg");
  client.print("Content-Length: ");
  client.println(fb->len);
  client.println();
  client.write(fb->buf, fb->len);
  client.println();
  esp_camera_fb_return(fb);
}

// 初始化服务器路由
void initServer() {
  // 视频流接口
  server.on("/stream", HTTP_GET, handleStream);
  
  // 控制指令接口
  server.on("/forward", HTTP_GET, []() {
    forward();
    server.send(200, "text/plain", "forward");
  });
  
  server.on("/backward", HTTP_GET, []() {
    backward();
    server.send(200, "text/plain", "backward");
  });
  
  server.on("/left", HTTP_GET, []() {
    left();
    server.send(200, "text/plain", "left");
  });
  
  server.on("/right", HTTP_GET, []() {
    right();
    server.send(200, "text/plain", "right");
  });
  
  server.on("/stop", HTTP_GET, []() {
    stopCar();
    server.send(200, "text/plain", "stop");
  });
  
  // 根目录返回控制页面(可选)
  server.on("/", HTTP_GET, []() {
    server.send(200, "text/html", 
      "<html><body>"
      "<h1>ESP32-CAR Control</h1>"
      "<img src='/stream' width='640' height='480'><br>"
      "<button onclick='fetch(\"/forward\")'>Forward</button>"
      "<button onclick='fetch(\"/backward\")'>Backward</button>"
      "<button onclick='fetch(\"/left\")'>Left</button>"
      "<button onclick='fetch(\"/right\")'>Right</button>"
      "<button onclick='fetch(\"/stop\")'>Stop</button>"
      "</body></html>"
    );
  });
  
  server.onNotFound([]() {
    if (!handleFileRead(server.uri())) {
      server.send(404, "text/plain", "404 Not Found");
    }
  });
  
  server.begin();
  Serial.println("Web server started");
}

void setup() {
  Serial.begin(115200);
  
  // 初始化电机引脚
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  stopCar(); // 初始状态停止
  
  // 初始化摄像头
  initCamera();
  
  // 创建 WiFi 热点
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP); // 热点 IP 通常是 192.168.4.1
  
  // 初始化 Web 服务器
  initServer();
}

void loop() {
  server.handleClient(); // 处理客户端请求
  delay(10);
}
 
 

2. 代码上传说明

 
  1. 使用 Arduino IDE,先安装 ESP32 开发板支持
  2. 选择开发板:ESP32 Wrover Module
  3. 配置参数:
    • Partition Scheme: Huge APP (3MB No OTA/1MB SPIFFS)
    • Flash Mode: QIO
    • Flash Frequency: 80MHz
     
  4. 上传代码(ESP32-CAM 需要接下载器,上传时按住 BOOT 键)
 

五、App Inventor 应用设计

 

1. App Inventor 界面布局

 
打开 App Inventor 2,创建新项目,按以下步骤设计界面:
 

(1)组件列表

 
表格
 
组件类型组件名称作用
水平布局 HorizontalArr1 放置方向按键
按钮 btnForward 前进
按钮 btnBackward 后退
按钮 btnLeft 左转
按钮 btnRight 右转
按钮 btnStop 停止
Web 浏览器 WebViewer1 显示视频流
标签 Label1 显示提示信息
 

(2)界面设计步骤

 
  1. 拖入 WebViewer 组件,设置宽度 / 高度为全屏(如宽度 320,高度 240)
  2. 拖入水平布局,在布局中添加 5 个按钮(前进 / 后退 / 左转 / 右转 / 停止)
  3. 设置按钮文本和大小,优化界面美观度
 

2. 组件属性配置

 
  • WebViewer1
    • HomeURL: http://192.168.4.1/stream(ESP32 热点默认 IP)
    • EnableJavaScript: True
     
  • 按钮
    • 设置不同的背景色(如前进绿色、停止红色)
    • 设置字体大小为 16sp
     
 

3. 逻辑代码(块编程)

 

(1)初始化

 
plaintext
 
 
当屏幕初始化时
  设置 WebViewer1.URL 为 "http://192.168.4.1/stream"
  WebViewer1.刷新页面
 
 

(2)按钮点击事件(以前进为例)

 
plaintext
 
 
当 btnForward 被点击时
  调用 发送指令 过程 ("/forward")
 
 

(3)通用指令发送过程

 
创建一个自定义过程 发送指令,参数为 指令路径
 
plaintext
 
 
过程 发送指令 (指令路径)
  初始化 url 为 "http://192.168.4.1" + 指令路径
  调用 Web.Get 方法
    url: url
    成功时:
      设置 Label1.文本 为 "指令发送成功:" + 指令路径
    失败时:
      设置 Label1.文本 为 "指令发送失败,请检查连接"
 
 

(4)其他按钮

 
为后退(/backward)、左转(/left)、右转(/right)、停止(/stop)按钮绑定相同的逻辑,仅修改指令路径。
 

4. 导出 APK

 
设计完成后,点击 构建生成 APK,下载并安装到手机。
 

六、使用步骤

 
  1. 给 ESP32-CAM 小车上电,等待 WiFi 热点启动(约 5 秒)
  2. 打开手机 WiFi,连接 ESP32-CAR 热点(密码 12345678)
  3. 打开制作好的 App,此时 WebViewer 会显示摄像头视频流
  4. 点击方向按钮,即可控制小车移动
 

七、常见问题解决

 
  1. 视频流卡顿:降低摄像头分辨率(改为 FRAMESIZE_QVGA)
  2. 小车无响应:检查引脚接线、电机驱动模块供电、指令路径是否正确
  3. 无法连接热点:重启 ESP32-CAM,检查热点名称 / 密码是否正确
  4. App 无法显示视频:确认手机已连接 ESP32 热点,IP 地址是否为 192.168.4.1
 

总结

 
  1. 核心逻辑:ESP32-CAM 建立 WiFi 热点并启动 Web 服务器,提供视频流接口和控制接口,App Inventor 应用连接热点后,通过 WebViewer 显示视频流,通过 HTTP 请求发送控制指令。
  2. 关键配置:ESP32 端需正确配置摄像头参数和电机引脚,App 端需使用 ESP32 热点的默认 IP(192.168.4.1)访问视频流和控制接口。
  3. 避坑要点:ESP32-CAM 必须用 5V 稳压供电,上传代码时需按住 BOOT 键,视频分辨率过高会导致传输卡顿。
posted @ 2026-03-13 16:08  多多和羊羊  阅读(4)  评论(0)    收藏  举报