ESP 32 camera 小车
一、ESP32‑CAM 端完整代码(Arduino)
cpp
运行
#include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>
// 电机驱动引脚(L298N / L9110)
#define IN1 12
#define IN2 13
#define IN3 14
#define IN4 15
WebServer server(80);
// WIFI 设置
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
// 摄像头引脚(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
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;
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
esp_camera_init(&config);
}
void carForward() {
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
server.send(200, "text/plain", "forward");
}
void carBackward() {
digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH);
server.send(200, "text/plain", "backward");
}
void carLeft() {
digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
server.send(200, "text/plain", "left");
}
void carRight() {
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH);
server.send(200, "text/plain", "right");
}
void carStop() {
digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
server.send(200, "text/plain", "stop");
}
void stream() {
WiFiClient client = server.client();
String header = "HTTP/1.1 200 OK\r\nContent-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n";
client.print(header);
while (client.connected()) {
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) break;
client.print("--frame\r\n");
client.print("Content-Type: image/jpeg\r\nContent-Length: ");
client.print(fb->len);
client.print("\r\n\r\n");
client.write(fb->buf, fb->len);
esp_camera_fb_return(fb);
delay(30);
}
}
void setup() {
Serial.begin(115200);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
carStop();
initCamera();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi 已连接");
Serial.println("IP 地址: ");
Serial.println(WiFi.localIP());
server.on("/forward", carForward);
server.on("/backward", carBackward);
server.on("/left", carLeft);
server.on("/right", carRight);
server.on("/stop", carStop);
server.on("/stream", stream);
server.begin();
}
void loop() {
server.handleClient();
}
二、安卓 APP 完整源码(Android Studio)
1. AndroidManifest.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.esp32camcar">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ESP32CamCar">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2. activity_main.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/iv_stream"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="fitCenter"
android:background="#000"/>
<EditText
android:id="@+id/et_ip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="ESP32 IP"
android:text="192.168.4.1"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:layout_marginTop="20dp">
<Button
android:id="@+id/btn_forward"
android:layout_width="100dp"
android:layout_height="60dp"
android:text="前进"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_left"
android:layout_width="100dp"
android:layout_height="60dp"
android:text="左转"/>
<Button
android:id="@+id/btn_stop"
android:layout_width="100dp"
android:layout_height="60dp"
android:text="停止"/>
<Button
android:id="@+id/btn_right"
android:layout_width="100dp"
android:layout_height="60dp"
android:text="右转"/>
</LinearLayout>
<Button
android:id="@+id/btn_backward"
android:layout_width="100dp"
android:layout_height="60dp"
android:text="后退"/>
</LinearLayout>
</LinearLayout>
3. MainActivity.java
java
运行
package com.example.esp32camcar;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private ImageView iv_stream;
private EditText et_ip;
private Handler handler = new Handler(Looper.getMainLooper());
private final String PATH_FORWARD = "/forward";
private final String PATH_BACKWARD = "/backward";
private final String PATH_LEFT = "/left";
private final String PATH_RIGHT = "/right";
private final String PATH_STOP = "/stop";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_stream = findViewById(R.id.iv_stream);
et_ip = findViewById(R.id.et_ip);
Button btn_forward = findViewById(R.id.btn_forward);
Button btn_backward = findViewById(R.id.btn_backward);
Button btn_left = findViewById(R.id.btn_left);
Button btn_right = findViewById(R.id.btn_right);
Button btn_stop = findViewById(R.id.btn_stop);
startStream();
btn_forward.setOnClickListener(v -> sendCmd(PATH_FORWARD));
btn_backward.setOnClickListener(v -> sendCmd(PATH_BACKWARD));
btn_left.setOnClickListener(v -> sendCmd(PATH_LEFT));
btn_right.setOnClickListener(v -> sendCmd(PATH_RIGHT));
btn_stop.setOnClickListener(v -> sendCmd(PATH_STOP));
}
private void sendCmd(final String path) {
new Thread(() -> {
try {
String ip = et_ip.getText().toString().trim();
URL url = new URL("http://" + ip + path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(1000);
conn.getResponseCode();
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
private void startStream() {
new Thread(() -> {
while (true) {
try {
String ip = et_ip.getText().toString().trim();
URL url = new URL("http://" + ip + "/stream");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
handler.post(() -> iv_stream.setImageBitmap(bitmap));
is.close();
conn.disconnect();
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}
}).start();
}
}
三、使用步骤
- ESP32 代码里改 WiFi 名称和密码。
- 上传后看串口监视器,拿到 ESP32 IP。
- 安卓 APP 里输入这个 IP。
- 即可实时视频 + 遥控小车。
浙公网安备 33010602011771号