ESP32-CAM 视频小车(App Inventor 控制)完整方案
ESP32-CAM 视频小车(App Inventor 控制)完整方案
我会为你提供一套完整的 ESP32-CAM 视频小车解决方案,包含硬件接线、ESP32 端代码、App Inventor 应用设计,让你能快速实现视频实时传输和小车遥控。
一、整体方案思路
- ESP32-CAM 核心功能:
- 建立 WiFi 热点,供手机连接
- 启动 Web 服务器,实时传输摄像头视频流
- 接收 App 发送的控制指令(前进 / 后退 / 左转 / 右转 / 停止)
- 控制电机驱动模块(如 L298N/L293D)驱动小车
- 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. 代码上传说明
- 使用 Arduino IDE,先安装 ESP32 开发板支持
- 选择开发板:
ESP32 Wrover Module - 配置参数:
- Partition Scheme:
Huge APP (3MB No OTA/1MB SPIFFS) - Flash Mode:
QIO - Flash Frequency:
80MHz
- Partition Scheme:
- 上传代码(ESP32-CAM 需要接下载器,上传时按住 BOOT 键)
五、App Inventor 应用设计
1. App Inventor 界面布局
打开 App Inventor 2,创建新项目,按以下步骤设计界面:
(1)组件列表
表格
| 组件类型 | 组件名称 | 作用 |
|---|---|---|
| 水平布局 | HorizontalArr1 | 放置方向按键 |
| 按钮 | btnForward | 前进 |
| 按钮 | btnBackward | 后退 |
| 按钮 | btnLeft | 左转 |
| 按钮 | btnRight | 右转 |
| 按钮 | btnStop | 停止 |
| Web 浏览器 | WebViewer1 | 显示视频流 |
| 标签 | Label1 | 显示提示信息 |
(2)界面设计步骤
- 拖入
WebViewer组件,设置宽度 / 高度为全屏(如宽度 320,高度 240) - 拖入水平布局,在布局中添加 5 个按钮(前进 / 后退 / 左转 / 右转 / 停止)
- 设置按钮文本和大小,优化界面美观度
2. 组件属性配置
- WebViewer1:
- HomeURL:
http://192.168.4.1/stream(ESP32 热点默认 IP) - EnableJavaScript:
True
- HomeURL:
- 按钮:
- 设置不同的背景色(如前进绿色、停止红色)
- 设置字体大小为 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,下载并安装到手机。六、使用步骤
- 给 ESP32-CAM 小车上电,等待 WiFi 热点启动(约 5 秒)
- 打开手机 WiFi,连接
ESP32-CAR热点(密码 12345678) - 打开制作好的 App,此时 WebViewer 会显示摄像头视频流
- 点击方向按钮,即可控制小车移动
七、常见问题解决
- 视频流卡顿:降低摄像头分辨率(改为 FRAMESIZE_QVGA)
- 小车无响应:检查引脚接线、电机驱动模块供电、指令路径是否正确
- 无法连接热点:重启 ESP32-CAM,检查热点名称 / 密码是否正确
- App 无法显示视频:确认手机已连接 ESP32 热点,IP 地址是否为 192.168.4.1
总结
- 核心逻辑:ESP32-CAM 建立 WiFi 热点并启动 Web 服务器,提供视频流接口和控制接口,App Inventor 应用连接热点后,通过 WebViewer 显示视频流,通过 HTTP 请求发送控制指令。
- 关键配置:ESP32 端需正确配置摄像头参数和电机引脚,App 端需使用 ESP32 热点的默认 IP(192.168.4.1)访问视频流和控制接口。
- 避坑要点:ESP32-CAM 必须用 5V 稳压供电,上传代码时需按住 BOOT 键,视频分辨率过高会导致传输卡顿。
ESP32-CAM 视频小车(App Inventor 控制)完整方案
我会为你提供一套完整的 ESP32-CAM 视频小车解决方案,包含硬件接线、ESP32 端代码、App Inventor 应用设计,让你能快速实现视频实时传输和小车遥控。
一、整体方案思路
- ESP32-CAM 核心功能:
- 建立 WiFi 热点,供手机连接
- 启动 Web 服务器,实时传输摄像头视频流
- 接收 App 发送的控制指令(前进 / 后退 / 左转 / 右转 / 停止)
- 控制电机驱动模块(如 L298N/L293D)驱动小车
- 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. 代码上传说明
- 使用 Arduino IDE,先安装 ESP32 开发板支持
- 选择开发板:
ESP32 Wrover Module - 配置参数:
- Partition Scheme:
Huge APP (3MB No OTA/1MB SPIFFS) - Flash Mode:
QIO - Flash Frequency:
80MHz
- Partition Scheme:
- 上传代码(ESP32-CAM 需要接下载器,上传时按住 BOOT 键)
五、App Inventor 应用设计
1. App Inventor 界面布局
打开 App Inventor 2,创建新项目,按以下步骤设计界面:
(1)组件列表
表格
| 组件类型 | 组件名称 | 作用 |
|---|---|---|
| 水平布局 | HorizontalArr1 | 放置方向按键 |
| 按钮 | btnForward | 前进 |
| 按钮 | btnBackward | 后退 |
| 按钮 | btnLeft | 左转 |
| 按钮 | btnRight | 右转 |
| 按钮 | btnStop | 停止 |
| Web 浏览器 | WebViewer1 | 显示视频流 |
| 标签 | Label1 | 显示提示信息 |
(2)界面设计步骤
- 拖入
WebViewer组件,设置宽度 / 高度为全屏(如宽度 320,高度 240) - 拖入水平布局,在布局中添加 5 个按钮(前进 / 后退 / 左转 / 右转 / 停止)
- 设置按钮文本和大小,优化界面美观度
2. 组件属性配置
- WebViewer1:
- HomeURL:
http://192.168.4.1/stream(ESP32 热点默认 IP) - EnableJavaScript:
True
- HomeURL:
- 按钮:
- 设置不同的背景色(如前进绿色、停止红色)
- 设置字体大小为 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,下载并安装到手机。六、使用步骤
- 给 ESP32-CAM 小车上电,等待 WiFi 热点启动(约 5 秒)
- 打开手机 WiFi,连接
ESP32-CAR热点(密码 12345678) - 打开制作好的 App,此时 WebViewer 会显示摄像头视频流
- 点击方向按钮,即可控制小车移动
七、常见问题解决
- 视频流卡顿:降低摄像头分辨率(改为 FRAMESIZE_QVGA)
- 小车无响应:检查引脚接线、电机驱动模块供电、指令路径是否正确
- 无法连接热点:重启 ESP32-CAM,检查热点名称 / 密码是否正确
- App 无法显示视频:确认手机已连接 ESP32 热点,IP 地址是否为 192.168.4.1
总结
- 核心逻辑:ESP32-CAM 建立 WiFi 热点并启动 Web 服务器,提供视频流接口和控制接口,App Inventor 应用连接热点后,通过 WebViewer 显示视频流,通过 HTTP 请求发送控制指令。
- 关键配置:ESP32 端需正确配置摄像头参数和电机引脚,App 端需使用 ESP32 热点的默认 IP(192.168.4.1)访问视频流和控制接口。
- 避坑要点:ESP32-CAM 必须用 5V 稳压供电,上传代码时需按住 BOOT 键,视频分辨率过高会导致传输卡顿。
浙公网安备 33010602011771号