#include "WiFi.h" // WiFi连接库
#include "AsyncUDP.h" // 异步UDP通信库
#include "camera_pins.h" // 相机引脚定义头文件
#include "esp_camera.h" // ESP32相机驱动库
#include <EEPROM.h> // 非易失性存储库
#include <WiFiUdp.h> // UDP通信库
#include <esp_system.h> // 系统功能库
// 网络配置常量
#define max_packet_byte 1399 // UDP单包最大字节数(考虑MTU限制)
#define LED_PIN 4 // LED指示灯引脚(GPIO4)
// 网络凭据
const char* ssid = "360WiFi-CEFF77"; // WiFi网络名称(SSID)
const char* password = "qwer1234"; // WiFi密码
uint16_t serverPort = 1234; // 目标服务器端口号
uint8_t cam_flag = 0; // 相机控制标志(0=无操作,1=初始化并拍照,2=拍照,3=关闭相机)
// UDP对象
WiFiUDP RUdp; // 用于接收UDP数据包的对象
unsigned int localUdpPort = 2333; // 本地UDP监听端口
AsyncUDP udp; // 用于异步发送UDP数据的对象
// 相机相关变量
camera_fb_t *fb = NULL; // 相机帧缓冲区指针
uint8_t onePacket[max_packet_byte+1]; // UDP数据包缓冲区(额外1字节用于包索引)
bool UDPconnected = true; // UDP连接状态标志
// 网络配置变量
IPAddress staticIP; // 设备静态IP地址
IPAddress gateway; // 网关地址
IPAddress subnet; // 子网掩码
IPAddress dns; // DNS服务器地址
IPAddress serverIP; // 目标服务器IP地址
// 重试机制配置
const int MAX_CAM_INIT_RETRY = 5; // 相机初始化最大重试次数
int cam_init_retry_count = 0; // 当前相机初始化重试计数
const int MAX_FB_GET_RETRY = 3; // 获取帧缓冲区最大重试次数
// EEPROM存储地址定义
#define EEPROM_SIZE 24 // EEPROM分配大小(字节)
#define STATIC_IP_FLAG_ADDR 0 // 静态IP标志位地址(1字节)
#define STATIC_IP_ADDR 1 // 静态IP存储地址(4字节)
#define GATEWAY_ADDR 5 // 网关地址存储地址(4字节)
#define SUBNET_ADDR 9 // 子网掩码存储地址(4字节)
#define DNS_ADDR 13 // DNS服务器存储地址(4字节)
#define SERVER_IP_ADDR 17 // 目标服务器IP存储地址(4字节)
/**
* @brief 发送图像数据包
* @param img 图像数据指针
* @param length 数据长度
* @param index 包索引(0-254=正常包, 0xFF=最后一个包)
*/
void sendImgAray(uint8_t * img, uint16_t lenth, uint8_t index) {
memset(onePacket, 0x00, max_packet_byte+1); // 清空数据包缓冲区
memcpy(onePacket, img, lenth); // 复制图像数据到发送缓冲区
onePacket[lenth] = index; // 在数据末尾添加包索引
// 如果UDP连接正常,发送数据包
if (udp.connected()) {
udp.write(onePacket, lenth+1); // 发送数据包(数据+索引)
}
}
/**
* @brief 初始化相机
* @return 初始化是否成功(true/false)
*/
bool startCamera() {
// 先尝试反初始化相机(确保干净状态)
if(esp_camera_deinit() != ESP_OK) {
Serial.println("Camera deinit failed");
}
// 相机配置结构体
camera_config_t config;
// LED控制器配置
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;
// I2C引脚配置
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; // XCLK频率20MHz
config.frame_size = FRAMESIZE_SVGA; // 分辨率800x600
config.pixel_format = PIXFORMAT_JPEG; // 像素格式JPEG
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; // 抓取模式(缓冲区空时抓取)
config.fb_location = CAMERA_FB_IN_PSRAM; // 帧缓冲区位置(PSRAM)
config.jpeg_quality = 18; // JPEG质量(0-63, 数值越小质量越高)
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\n", err);
return false;
} else {
Serial.println("Camera init success");
// 获取相机传感器并调整参数
sensor_t * s = esp_camera_sensor_get();
// 图像质量调整
s->set_brightness(s, 1); // 亮度(+2 to -2)
s->set_contrast(s, 0); // 对比度(+2 to -2)
s->set_saturation(s, 2); // 饱和度(+2 to -2)
s->set_special_effect(s, 0); // 特效(0-6, 0=无特效)
s->set_whitebal(s, 1); // 自动白平衡(0=禁用, 1=启用)
s->set_awb_gain(s, 1); // AWB增益(0=禁用, 1=启用)
s->set_wb_mode(s, 0); // 白平衡模式(0=自动,1-4=预设)
s->set_exposure_ctrl(s, 1); // 曝光控制(0=禁用, 1=启用)
s->set_aec2(s, 1); // AEC2(0=禁用, 1=启用)
s->set_ae_level(s, 0); // AE水平(-2 to 2)
s->set_aec_value(s, 350); // 曝光值(0-1200)
s->set_gain_ctrl(s, 1); // 增益控制(0=禁用, 1=启用)
s->set_agc_gain(s, 0); // AGC增益(0-30)
s->set_gainceiling(s, (gainceiling_t)0); // 增益上限(0-6)
s->set_bpc(s, 1); // 黑点校准(0=禁用, 1=启用)
s->set_wpc(s, 1); // 白点校准(0=禁用, 1=启用)
s->set_raw_gma(s, 1); // RAW伽马(0=禁用, 1=启用)
s->set_lenc(s, 1); // 镜头校准(0=禁用, 1=启用)
s->set_hmirror(s, 1); // 水平镜像(0=禁用, 1=启用)
s->set_vflip(s, 0); // 垂直翻转(0=禁用, 1=启用)
s->set_dcw(s, 1); // DCW(下采样)(0=禁用, 1=启用)
s->set_colorbar(s, 0); // 颜色条(0=禁用, 1=启用)
// 重置重试计数器
cam_init_retry_count = 0;
return true;
}
}
/**
* @brief LED闪烁控制
* @param pin_num LED引脚
* @param dark_time 熄灭时间(ms)
* @param light_time 点亮时间(ms)
*/
void blink(int pin_num, int dark_time, int light_time) {
delay(dark_time); // 等待熄灭时间
digitalWrite(pin_num, HIGH); // 点亮LED
delay(light_time); // 等待点亮时间
digitalWrite(pin_num, LOW); // 熄灭LED
}
/**
* @brief 从EEPROM加载网络配置
*/
void loadNetworkConfig() {
// 检查是否设置了静态IP标志
uint8_t useStaticIP = EEPROM.read(STATIC_IP_FLAG_ADDR);
if(useStaticIP == 1) {
// 从EEPROM读取静态IP配置(4字节)
staticIP = IPAddress(
EEPROM.read(STATIC_IP_ADDR),
EEPROM.read(STATIC_IP_ADDR+1),
EEPROM.read(STATIC_IP_ADDR+2),
EEPROM.read(STATIC_IP_ADDR+3));
// 读取网关地址(4字节)
gateway = IPAddress(
EEPROM.read(GATEWAY_ADDR),
EEPROM.read(GATEWAY_ADDR+1),
EEPROM.read(GATEWAY_ADDR+2),
EEPROM.read(GATEWAY_ADDR+3));
// 读取子网掩码(4字节)
subnet = IPAddress(
EEPROM.read(SUBNET_ADDR),
EEPROM.read(SUBNET_ADDR+1),
EEPROM.read(SUBNET_ADDR+2),
EEPROM.read(SUBNET_ADDR+3));
// 读取DNS服务器地址(4字节)
dns = IPAddress(
EEPROM.read(DNS_ADDR),
EEPROM.read(DNS_ADDR+1),
EEPROM.read(DNS_ADDR+2),
EEPROM.read(DNS_ADDR+3));
// 打印加载的配置
Serial.println("Loaded static IP configuration from EEPROM:");
Serial.print("Static IP: "); Serial.println(staticIP);
Serial.print("Gateway: "); Serial.println(gateway);
Serial.print("Subnet: "); Serial.println(subnet);
Serial.print("DNS: "); Serial.println(dns);
} else {
Serial.println("Using DHCP for IP configuration");
}
// 加载目标服务器IP地址(4字节)
serverIP = IPAddress(
EEPROM.read(SERVER_IP_ADDR),
EEPROM.read(SERVER_IP_ADDR+1),
EEPROM.read(SERVER_IP_ADDR+2),
EEPROM.read(SERVER_IP_ADDR+3));
Serial.print("Server IP: "); Serial.println(serverIP);
}
/**
* @brief 保存静态IP配置到EEPROM
* @param ip 静态IP地址
* @param gw 网关地址
* @param sn 子网掩码
* @param d DNS服务器地址
*/
void saveStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn, IPAddress d) {
EEPROM.write(STATIC_IP_FLAG_ADDR, 1); // 设置静态IP标志
// 保存IP地址(4字节)
EEPROM.write(STATIC_IP_ADDR, ip[0]);
EEPROM.write(STATIC_IP_ADDR+1, ip[1]);
EEPROM.write(STATIC_IP_ADDR+2, ip[2]);
EEPROM.write(STATIC_IP_ADDR+3, ip[3]);
// 保存网关地址(4字节)
EEPROM.write(GATEWAY_ADDR, gw[0]);
EEPROM.write(GATEWAY_ADDR+1, gw[1]);
EEPROM.write(GATEWAY_ADDR+2, gw[2]);
EEPROM.write(GATEWAY_ADDR+3, gw[3]);
// 保存子网掩码(4字节)
EEPROM.write(SUBNET_ADDR, sn[0]);
EEPROM.write(SUBNET_ADDR+1, sn[1]);
EEPROM.write(SUBNET_ADDR+2, sn[2]);
EEPROM.write(SUBNET_ADDR+3, sn[3]);
// 保存DNS服务器地址(4字节)
EEPROM.write(DNS_ADDR, d[0]);
EEPROM.write(DNS_ADDR+1, d[1]);
EEPROM.write(DNS_ADDR+2, d[2]);
EEPROM.write(DNS_ADDR+3, d[3]);
EEPROM.commit(); // 提交写入操作
Serial.println("Static IP configuration saved to EEPROM");
}
/**
* @brief 保存目标服务器IP到EEPROM
* @param ip 目标服务器IP地址
*/
void saveServerIP(IPAddress ip) {
// 保存服务器IP地址(4字节)
EEPROM.write(SERVER_IP_ADDR, ip[0]);
EEPROM.write(SERVER_IP_ADDR+1, ip[1]);
EEPROM.write(SERVER_IP_ADDR+2, ip[2]);
EEPROM.write(SERVER_IP_ADDR+3, ip[3]);
EEPROM.commit(); // 提交写入操作
Serial.println("Server IP saved to EEPROM");
}
/**
* @brief 设置使用DHCP模式
*/
void useDHCP() {
EEPROM.write(STATIC_IP_FLAG_ADDR, 0); // 清除静态IP标志
EEPROM.commit(); // 提交写入操作
Serial.println("Switched to DHCP mode");
}
/**
* @brief 解析IP配置命令
* @param command 串口接收到的命令字符串
*/
void parseIPCommand(String command) {
// SETIP命令格式: SETIP <ip> <gateway> <subnet> <dns>
if(command.startsWith("SETIP ")) {
int firstSpace = command.indexOf(' ');
String ips = command.substring(firstSpace + 1); // 提取IP参数字符串
// 解析IP地址(4部分)
int ipParts[4];
int index = 0;
int lastPos = 0;
for(int i = 0; i < 4; i++) {
int dotPos = ips.indexOf('.', lastPos);
if(dotPos == -1 && i < 3) break;
if(i == 3) dotPos = ips.indexOf(' ', lastPos);
ipParts[i] = ips.substring(lastPos, dotPos).toInt();
lastPos = dotPos + 1;
}
// 创建IPAddress对象
IPAddress newIP(ipParts[0], ipParts[1], ipParts[2], ipParts[3]);
// 解析网关地址
lastPos = ips.indexOf(' ', lastPos) + 1;
for(int i = 0; i < 4; i++) {
int dotPos = ips.indexOf(i < 3 ? '.' : ' ', lastPos);
ipParts[i] = ips.substring(lastPos, dotPos).toInt();
lastPos = dotPos + 1;
}
IPAddress newGateway(ipParts[0], ipParts[1], ipParts[2], ipParts[3]);
// 解析子网掩码
lastPos = ips.indexOf(' ', lastPos) + 1;
for(int i = 0; i < 4; i++) {
int dotPos = ips.indexOf(i < 3 ? '.' : ' ', lastPos);
ipParts[i] = ips.substring(lastPos, dotPos).toInt();
lastPos = dotPos + 1;
}
IPAddress newSubnet(ipParts[0], ipParts[1], ipParts[2], ipParts[3]);
// 解析DNS服务器地址
lastPos = ips.indexOf(' ', lastPos) + 1;
for(int i = 0; i < 4; i++) {
int dotPos = (i < 3) ? ips.indexOf('.', lastPos) : ips.length();
ipParts[i] = ips.substring(lastPos, dotPos).toInt();
lastPos = dotPos + 1;
}
IPAddress newDNS(ipParts[0], ipParts[1], ipParts[2], ipParts[3]);
// 保存配置
saveStaticIPConfig(newIP, newGateway, newSubnet, newDNS);
Serial.println("Static IP configured. Restart to apply changes.");
}
// DHCP命令
else if(command.equals("DHCP")) {
useDHCP(); // 设置为DHCP模式
Serial.println("DHCP mode set. Restart to apply changes.");
}
}
/**
* @brief Arduino初始化函数
*/
void setup() {
Serial.begin(115200); // 初始化串口通信(115200波特率)
EEPROM.begin(EEPROM_SIZE); // 初始化EEPROM
// 初始化LED引脚
pinMode(LED_PIN, OUTPUT);
blink(LED_PIN, 0, 200); // 开机LED闪烁(200ms)
// 加载网络配置
loadNetworkConfig();
// 带重试机制的相机初始化
bool cam_ok = false;
while(cam_init_retry_count < MAX_CAM_INIT_RETRY && !cam_ok) {
cam_ok = startCamera();
if(!cam_ok) {
Serial.printf("Camera init retry %d/%d\n",
cam_init_retry_count+1, MAX_CAM_INIT_RETRY);
cam_init_retry_count++;
blink(LED_PIN, 200, 200); // 错误状态闪烁
delay(1000); // 重试间隔
}
}
// 相机初始化失败处理
if(!cam_ok) {
Serial.println("All camera init attempts failed. Rebooting...");
for(int i=0; i<5; i++) {
blink(LED_PIN, 100, 100); // 快速闪烁(错误指示)
}
delay(3000);
esp_restart(); // 重启设备
}
// 设置WiFi模式为STA(客户端)
WiFi.mode(WIFI_STA);
// 配置静态IP或使用DHCP
uint8_t useStaticIP = EEPROM.read(STATIC_IP_FLAG_ADDR);
if(useStaticIP == 1) {
if(!WiFi.config(staticIP, gateway, subnet, dns)) {
Serial.println("Static IP configuration failed! Switching to DHCP...");
useDHCP(); // 静态IP配置失败时切换为DHCP
} else {
Serial.println("Static IP configured");
}
}
// 连接WiFi
Serial.print("Connecting to WiFi...");
WiFi.begin(ssid, password);
// 等待WiFi连接(20秒超时)
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startTime < 20000) {
delay(500);
Serial.print(".");
blink(LED_PIN, 100, 100); // 连接中闪烁
}
// WiFi连接失败处理
if(WiFi.status() != WL_CONNECTED){
Serial.println("\nWiFi connection failed");
while (1) {
blink(LED_PIN, 500, 500); // 慢速闪烁(连接失败)
}
}
// WiFi连接成功
Serial.println("\nWiFi Connected");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// 成功连接指示(快速闪烁3次)
for(int i=0; i<3; i++){
blink(LED_PIN, 100, 100);
}
// 连接到目标服务器
udp.connect(serverIP, serverPort); // 异步UDP连接
RUdp.begin(localUdpPort); // 启动UDP监听
Serial.print("UDP connected to: ");
Serial.print(serverIP);
Serial.print(":");
Serial.println(serverPort);
}
/**
* @brief 从串口读取并处理IP命令
*/
void get_ip_from_serial() {
if(Serial.available()) {
String req = Serial.readStringUntil('\n'); // 读取整行
req.trim(); // 去除首尾空白
// 检查是否是IP配置命令(SETIP或DHCP)
if(req.startsWith("SETIP") || req.equals("DHCP")) {
parseIPCommand(req);
return;
}
// 检查是否是服务器IP设置命令(SERVERIP)
if(req.startsWith("SERVERIP ")) {
req = req.substring(9); // 去除命令前缀
int parts[4];
int index = 0;
int lastPos = 0;
// 解析IP地址(4部分)
for(int i = 0; i < 4; i++) {
int dotPos = req.indexOf('.', lastPos);
if(dotPos == -1 && i < 3) break;
if(i == 3) dotPos = req.length();
parts[i] = req.substring(lastPos, dotPos).toInt();
lastPos = dotPos + 1;
}
// 创建IPAddress对象并保存
IPAddress newServerIP(parts[0], parts[1], parts[2], parts[3]);
saveServerIP(newServerIP);
udp.connect(newServerIP, serverPort); // 重新连接到新服务器
Serial.print("Server IP set to: ");
Serial.println(newServerIP);
return;
}
// 旧版服务器IP设置逻辑(兼容性)
int len = req.length();
char words[len + 1];
req.toCharArray(words, len + 1);
int start_ = 0;
uint8_t nums[4]; // 存储IP的四个部分
uint8_t count = 0; // 当前解析的段数
// 解析IP地址
for (int i = 0; i < len; i++) {
if (words[i] == '.') {
// 提取点分隔的部分
char word[i - start_ + 1];
for (int j = start_; j < i; j++) {
word[j - start_] = words[j];
}
word[i - start_] = '\0';
nums[count] = atoi(word); // 转换为整数
count++;
start_ = i + 1;
}
if (count > 2) {
break;
}
}
// 处理最后一段
char word[4];
for (int j = start_; j < (start_+3); j++) {
word[j - start_] = words[j];
}
word[3] = '\0';
nums[3] = atoi(word);
// 创建IP地址并连接
IPAddress ip(nums[0], nums[1], nums[2], nums[3]);
udp.connect(ip, serverPort);
saveServerIP(ip); // 保存到EEPROM
// 打印连接信息
char _msg[50];
sprintf(_msg, "Connected to %d.%d.%d.%d", nums[0], nums[1], nums[2], nums[3]);
Serial.println(_msg);
UDPconnected = true;
}
}
/**
* @brief 处理相机命令(拍照和发送)
*/
void handleCameraCommand() {
uint8_t retry_count = 0;
bool success = false;
//digitalWrite(LED_PIN,HIGH);
// 带重试机制的帧获取
while(retry_count < MAX_FB_GET_RETRY && !success) {
fb = esp_camera_fb_get(); // 获取帧缓冲区
if(fb) {
success = true;
} else {
Serial.printf("Frame buffer get failed, retry %d/%d\n",
retry_count+1, MAX_FB_GET_RETRY);
retry_count++;
delay(100); // 重试间隔
}
}
// 帧获取失败处理
if(!success) {
Serial.println("All frame buffer get attempts failed");
// 释放相机资源
//digitalWrite(LED_PIN,LOW);
esp_camera_deinit();
delay(1000);
// 尝试重新初始化相机(最多3次)
bool cam_reinit = false;
int reinit_retry = 0;
while(reinit_retry < 3 && !cam_reinit) {
cam_reinit = startCamera();
if(!cam_reinit) {
Serial.printf("Camera reinit attempt %d/3 failed\n", reinit_retry+1);
reinit_retry++;
delay(1000);
}
}
// 相机重新初始化失败处理
if(!cam_reinit) {
Serial.println("Camera reinit failed after frame get failure. Rebooting...");
for(int i=0; i<10; i++) {
blink(LED_PIN, 100, 100); // 快速闪烁(即将重启)
}
delay(1000);
esp_restart(); // 重启设备
}
blink(LED_PIN, 200, 1000); // 长闪烁(恢复成功)
cam_flag = 0; // 重置相机标志
return;
}
// 图片处理
uint8_t *P_temp = fb->buf; // 保存原始指针位置
size_t pic_length = fb->len; // 获取图片字节数
size_t pic_pack_quantity = pic_length / max_packet_byte; // 完整包数量
size_t remine_byte = pic_length % max_packet_byte; // 剩余字节数
// 发送完整包
for (int j = 0; j < pic_pack_quantity; j++) {
sendImgAray(fb->buf, max_packet_byte, j); // 发送一个完整包
fb->buf += max_packet_byte; // 移动指针到下一个位置
delay(50); // 包间延迟(给接收方处理时间)
}
// 发送最后一个包(剩余数据)
sendImgAray(fb->buf, remine_byte, 0xFF); // 0xFF表示最后一个包
fb->buf = P_temp; // 恢复原始指针位置
// 释放帧缓冲区
esp_camera_fb_return(fb);
fb = NULL;
}
/**
* @brief Arduino主循环
*/
void loop() {
// UDP连接状态检查
if(!udp.connected()) {
// 尝试重新连接(每5秒一次)
static unsigned long lastReconnectAttempt = 0;
if (millis() - lastReconnectAttempt > 5000) {
Serial.println("UDP not connected. Attempting to reconnect...");
udp.connect(serverIP, serverPort); // 尝试重新连接
lastReconnectAttempt = millis(); // 更新最后尝试时间
}
// 慢速闪烁表示未连接
blink(LED_PIN, 900, 100);
} else {
// 检查串口输入(配置命令)
get_ip_from_serial();
// 检查UDP数据包
int packetSize = RUdp.parsePacket(); // 获取数据包大小
if (packetSize) { // 如果有数据可用
char buf[packetSize+1]; // 创建缓冲区
int len = RUdp.read(buf, packetSize); // 读取数据
buf[len] = '\0'; // 添加字符串结束符
// 打印接收信息
Serial.print("Received packet size: ");
Serial.print(packetSize);
Serial.print(", content: ");
Serial.println(buf[0]); // 打印第一个字符(命令)
Serial.print("From IP: ");
Serial.println(RUdp.remoteIP());
Serial.print("From Port: ");
Serial.println(RUdp.remotePort());
// 命令解析
if(buf[0] == '1') { // 开启相机并拍照
Serial.println("Received '1', starting camera");
cam_flag = 1;
} else if(buf[0] == '2') { // 拍照(相机已开启)
Serial.println("Received '2', capturing frame");
cam_flag = 2;
} else if(buf[0] == '3') { // 关闭相机
Serial.println("Received '3', deinitializing camera");
cam_flag = 3;
} else if(buf[0] == '4') { // 保留命令
Serial.println("Received '4'");
cam_flag = 4;
} else if(buf[0] == 'R') { // 重启设备
Serial.println("Received 'R', rebooting...");
esp_restart();
}
}
// 处理相机命令
if(cam_flag == 1) {
// 初始化相机并拍照
if(!startCamera()) {
Serial.println("Failed to start camera for command '1'");
blink(LED_PIN, 200, 1000); // 错误闪烁
} else {
delay(100);
handleCameraCommand(); // 拍照并发送
}
cam_flag = 0; // 重置标志
} else if(cam_flag == 2) {
// 拍照(相机已初始化)
handleCameraCommand(); // 拍照并发送
cam_flag = 0; // 重置标志
} else if(cam_flag == 3) {
// 关闭相机
esp_camera_deinit();
Serial.println("Camera deinitialized");
cam_flag = 0; // 重置标志
} else if(cam_flag == 4) {
// 保留功能
cam_flag = 0; // 重置标志
}
// 空闲状态处理(每2秒)
static unsigned long lastBlink = 0;
if (millis() - lastBlink > 1500) {
// 可在此添加心跳或状态报告
lastBlink = millis();
}
}
}
这段代码我要如何减少丢包现象,同时服务器端的代码要如何修改
最新发布