开源硬件
Arduino
客制化键盘
Arduino_寄存器
二进制运算
寄存器+二进制运算
LCD-逐字显示
密码依次录入
等待输入
WiFi Duck(无线击键注入攻击平台)
WiFi Duc-New
WiFi Duc-Old
蓝牙无线烧录
ESP8266
ESP-NOW
ESP8266看门狗
ESP8266-休眠模式
ESP01/01S使用说明
WIFI_SD
ESP8266-Web服务器
ESP8266-WIFI自动认证
ESP32
ESP32 ADC2
ESP32_PWM
ESP32_CAM
ESP32 小坦克
ESP32_限电保护
Arduino IDE 添加 ESP32
ESP32-iPhone BLE攻击
STM32
STM32F103-虚拟键盘
STC
STC8G1K08(A)
树莓派-触摸屏
Arduino IDE
Arduino_自制库
Arduino库收集
常见排序算法
冒泡排序
选择排序
插入排序
希尔排序
归并排序
快速排序
计数排序
预处理
millis(运行时长)
Arduino IDE 2.X-修改数据位置
Mixly
Mixly安装教程
Mixly 模块介绍
Mixly-添加ESP32CAM支持
Mixly-库定制工具
模块
4G模块连接物联网
GPS模块
语音模块(JQ8900)
安信可VB语音识别
28BYJ-48(5V步进)
FreeRTOS
FreeRTOS-多任务基础
FreeRTOS-任务共享全局变量
FreeRTOS-多核多任务
FreeRTOS-MUTEX
FreeRTOS-常规程序改多任务
FreeRTOS-定时器
LaserGRBL(激光雕刻)
LaserGRBL-GRBL
GRBL-CNC Shield v4
MicroPython
Scratch
Wokwi(在线仿真)
html转无符号数组
待做开源项目
本文档使用 MrDoc 发布
-
+
首页
ESP32 小坦克
# 环境准备 基于`ESP32`来制作,使用`Arduino IDE`来编程 附加开发板网址:`https://dl.espressif.com/dl/package_esp32_index.json` 自行更新到最新`ESP32`库 使用的硬件为`ESP32_CAM`,故以这款主控介绍  > 相关文件:/共享盘/创客/DIY/esp32小坦克 # CameraWebServer 因为该项目是基于官方的`CameraWebServer`更改制作的 所以先测试`CameraWebServer`是否可用 相关可查看[ESP32_CAM](http://nas.918178.xyz:8090/archives/esp32cam) # 小坦克程序修改 大致思路是: 使用新的`html`页面替换掉原有的,在这个网页上可以显示查看摄像头,并且有一些案件可供操作,按下按键会回传信息,进行简单的比对来确认按下了那个按键,来执行对应功能 ## 摄像头可设置版 ### 准备 html 在 `camera_index.h` 文件中就包含`ov2640`和`ov3660`两种不同摄像头的`web`源码,这个是已经转码后的文件,我们这边直接弃用,更改可读性更高的`html`文件  按键 按下时会回传指令,就是根据这个指令中带的`var`参数(绿色)进行判断是什么被按下  > 红色部分这些为配置摄像头的,不能随意改动,是根据`id`进行返回指令的 > 除了几个基本控制,其他的都是摄像头相关的 滑动块 在拖动松手后会回传指令,可以设置滑块的范围(min/max)、默认值(value)以及步进(step),调用指令(this.value)  电脑端和手机端显示效果基本一致  根据需求修改好后,全部复制 #### 添加自定义的 html 页面 `追加` ``` static const char PROGMEM INDEX_HTML[] = R"rawliteral( //这里替换为 刚复制的 html 文件内容 )rawliteral"; ``` #### 修改 web页 原: ``` static esp_err_t index_handler(httpd_req_t *req){ httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); sensor_t * s = esp_camera_sensor_get(); if (s->id.PID == OV3660_PID) { return httpd_resp_send(req, (const char *)index_ov3660_html_gz, index_ov3660_html_gz_len); } return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len); } ``` `修改`为: ``` static esp_err_t index_handler(httpd_req_t *req){ httpd_resp_set_type(req, "text/html"); // httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); // sensor_t * s = esp_camera_sensor_get(); // if (s->id.PID == OV3660_PID) { // return httpd_resp_send(req, (const char *)index_ov3660_html_gz, index_ov3660_html_gz_len); // } // return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len); return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML)); //禁用之前显示的,直接调用我们刚添加的 html } ``` ### 添加 web 控制返回控制 #### app_httpd.h 原: ``` if(!strcmp(variable, "framesize")) { ... } else if(!strcmp(variable, "quality"))... ... else { res = -1; } ``` 在最后 `else` 前面`追加`上我们相关的控制判断 ``` //传入的var第一个为判断条件,第二个为数值 //浮动块 舵机的 else if(!strcmp(variable, "servo")) { if (val > 180) val = 180; else if (val < 0) val = 0; // 舵机2端口连接着通道8,分辨率10(0~1023) ledcWrite(8,servo_pwm(val)); delay(50); } //按键 else if(!strcmp(variable, "control")) { if (val==1) { Serial.println("前进"); ledcWrite(12,speed); ledcWrite(13,0); digitalWrite(15,HIGH); ledcWrite(14,0); } else if (val==2){ ... } ... } ``` 只举了简单两个不同控件的例子,具体根据实际带入 > 注意,舵机还需要定义下面的函数才能使用,具体查看[ESP32_PWM](http://nas.918178.xyz:8090/archives/esp32pwm#toc-head-2) 额外`追加`部分 ``` #include <esp32-hal-ledc.h> //引用ledc的PWM库 int speed = 255; //给一个初始速度,防止未设置速度就点击移动 int led = 20; //led的初始亮度 boolean led_zt = 0; //这个是配合控制led总开状态 //舵机度数换算,没这个函数舵机无法使用 int servo_pwm(int ds){ const float servo_min = 25.6; const float servo_max = 128; return (int)(((servo_max - servo_min)/180)*ds+servo_min); } ``` #### .ino 主文件 `追加` ``` //舵机 ledcAttachPin(2, 8); //绑定2号引脚到低速通道(0~15通道) ledcSetup(8, 50, 10); //设置通道频率50(20ms周期),分辨率10(0~1023) // 计算,舵机为20ms一周期,0.5ms~2.5ms分别对应0~180° // MIN 0.5ms:0.5ms/(20ms/[分辨率,如10分辨率为1023]) // MAX 2.5ms:2.5ms/(20ms/[分辨率,如10分辨率为1023]) // (MAX-MIN)/180*度数+MIN //LED ledcAttachPin(4, 4); //绑定4号引脚到通道(0~15通道) ledcSetup(4, 5000, 8); //设置通道4的频率和分辨率(0~20,10为1023,8为255) //电机 ledcAttachPin(12, 12); //电机A1 ledcSetup(12, 5000, 8); ledcAttachPin(13, 13); //电机A2 ledcSetup(13, 5000, 8); // ledcAttachPin(15, 15); //15使用PWM有问题,原因未知,故禁用 // ledcSetup(15, 5000, 8); //如果使用L293D这种一个PWM控制速度,一个高低电平控制方向就可以实现PWM控速 pinMode(15, OUTPUT); //电机B1,使用高低电平控制 ledcAttachPin(14, 14); //电机B2 ledcSetup(14, 5000, 8); ``` ### 其他修改 #### app_httpd.h `禁用` ``` //禁用前面代码为以下的1 httpd_resp_set_type(req, "image/jpeg"); httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg"); // httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //禁用前面代码为以下的2 res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); if(res != ESP_OK){ return res; } // httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //两条这个都禁用 // free(net_boxes->score); // p+=sprintf(p, "\"sharpness\":%d,", s->status.sharpness); //有视频连接时输出延迟和帧数,实际使用不需要,就禁用了,方便查看其他触发情况 // Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps), %u+%u+%u+%u=%u %s%d\n", // (uint32_t)(_jpg_buf_len), // (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time, // avg_frame_time, 1000.0 / avg_frame_time, // (uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time, // (detected)?"DETECTED ":"", face_id // ); ``` ### .ino 主文件 `修改` ``` //禁用其他板子的定义,启用 AI 板子的定义 //#define CAMERA_MODEL_WROVER_KIT // Has PSRAM //#define CAMERA_MODEL_ESP_EYE // Has PSRAM //#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM //#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM //#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM //#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM #define CAMERA_MODEL_AI_THINKER // Has PSRAM //#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM ``` 设置为搜索不到热点就放出热点 原: ``` WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); ``` 修改为: ``` //头部定义 //AP const char *apssid = "ESP8266"; const char *appassword = "12345678"; IPAddress apIP(192, 168, 233, 1); IPAddress gateway(192, 168, 233, 1); IPAddress subnet(255, 255, 255, 0); //STA,这个默认有的 const char* ssid = "WIFI233"; const char* password = "12356789"; //void setup() 中定义 // int sj = 0; //初始化时间 WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { digitalWrite(ST_LED,LOW); delay(60); digitalWrite(ST_LED,HIGH); delay(500); sj ++; if (sj > 30) break;//等待一定时间进行连接热点 Serial.print("."); } if(!WiFi.localIP()){ //没有连接到WIFI,启用AP模式 WiFi.mode(WIFI_AP); WiFi.disconnect(); delay(100); WiFi.softAPConfig(apIP, gateway, subnet); WiFi.softAP(apssid, appassword); Serial.println("WiFi connected"); } ``` ## 基本版 基本版 和 带摄像设置版 类似,阉割掉摄像头设置,只保留基础的摄像显示和坦克控制功能 这里使用的是变量追加的形式,相较于直接写入html,这种方式在`运行过程中`可以通过代码`更改页面`  ### 准备 html 一样,可以先准备正常的 html,这样的方便测试 ``` <meta charset=\"utf-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\"> <title>ESP32小坦克</title> <script>var xhttp = new XMLHttpRequest();</script> <script>function getsend(arg) { xhttp.open('GET', arg +'?' + new Date().getTime(), true); xhttp.send() } </script> <p align=center><IMG id ='httpweb' SRC='http://恭喜你发现我了' style='width:300px; transform:rotate(180deg);'></p><br/><br/> <script>var url= window.location.hostname;document.getElementById('httpweb').src ='http://'+url+':81/stream';</script> <p align=center> <button style=background-color:#6fdb0a;width:90px;height:80px onmousedown=getsend('go') onmouseup=getsend('stop') ontouchstart=getsend('go') ontouchend=getsend('stop') ><b>前进</b></button> </p> <p align=center> <button style=background-color:#6fdb0a;width:90px;height:80px; onmousedown=getsend('left') onmouseup=getsend('stop') ontouchstart=getsend('left') ontouchend=getsend('stop')><b>左转</b></button> <button style=background-color:#ce2406;width:90px;height:80px onmousedown=getsend('stop') onmouseup=getsend('stop')><b>停止</b></button> <button style=background-color:#6fdb0a;width:90px;height:80px onmousedown=getsend('right') onmouseup=getsend('stop') ontouchstart=getsend('right') ontouchend=getsend('stop')><b>右转</b></button> </p> <p align=center><button style=background-color:#6fdb0a;width:90px;height:80px onmousedown=getsend('back') onmouseup=getsend('stop') ontouchstart=getsend('back') ontouchend=getsend('stop') ><b>后退</b></button></p> <p align=center> <button style=background-color:#f59212;width:140px;height:40px onmousedown=getsend('ledon')><b>打开 LED</b></button> <button style=background-color:#ffa60057;width:140px;height:40px onmousedown=getsend('ledoff')><b>关闭 LED</b></button> </p> ```  使用浏览器查看效果 > `getsend(arg) { xhttp.open('GET', arg +'?' + new Date().getTime(), true); xhttp.send() }` > 按键按下会调用这个函数,返回指令 ### 转换 html 测试完后,需要转换成代码`追加`形式 追加格式很简单,每一段开头加上`page += "`,末尾加上`";` 可以使用`正则表达式`搜索`\n`(即换行符),来替换成`";\npage += "`  注意,首尾处可能不会生成,所以需要手动补充  ### 加入 html `修改 app_httpd.cpp`,`static esp_err_t index_handler(httpd_req_t *req){`下的内容 ``` static esp_err_t index_handler(httpd_req_t *req){ httpd_resp_set_type(req, "text/html"); String page = ""; //以下填写刚转换的html内容,transform:rotate(180deg) 为旋转图像角度 return httpd_resp_send(req, &page[0], strlen(&page[0])); } ``` ### 添加返回指令 `app_httpd.cpp`在`void startCameraServer(){`中进行`追加` ``` //每个指令对应一个函数 httpd_uri_t go_uri = { //调用函数 .uri = "/go", //返回的地址(同按键一样) .method = HTTP_GET, .handler = go_handler, //需执行的函数 .user_ctx = NULL }; ``` ### 添加指令触发 `app_httpd.cpp`在`if (httpd_start(&camera_httpd, &config) == ESP_OK) {`中进行`追加` ``` //每个指令函数对应一个触发 httpd_register_uri_handler(camera_httpd, &go_uri); //修改为上个函数名 ``` ### 添加指令执行函数 每个指令都对应一个执行函数,每个函数都需要进行编写执行内容 ``` //函数名为 退回指令 中书写的 需执行函数名 static esp_err_t go_handler(httpd_req_t *req){ //在这里定义需要执行的指令 Serial.println("指令A"); httpd_resp_set_type(req, "text/html"); return httpd_resp_send(req, "OK", 2); } ``` > 注意,如果使用PWM或是舵机,参考 带摄像头设置 中的介绍 ## 按键多无法解析  同时定义多个按键时,可能出现无法使用(默认最大`8`) 在`esp_http_server.h`中  通过修改这个参数来增加可用按键数
造物者W
2022年4月25日 21:29
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码