ESP32-CAM ArduinoIDE开发系列文章目录
第一篇:ESP32-CAM高性价比WIFI图传方案快速入门教程
第二篇:ESP32-CAM第一个无线点灯程序
第三篇:ESP32-CAM智能网关的设计与实现
前言
daodanjishui物联网核心原创技术之ESP32 Arduino IDE开发之嵌入式网页服务器架设、http请求收发与解析、单片机IO口读写操作和AJAX技术整合组成:ESP32-CAM实现嵌入式无线监控智能网关。
一、ESP32-CAM无线监控智能网关是什么?
在第二篇的项目中:ESP32-CAM、ESP8266、WIFI、蓝牙、摄像头设备实现嵌入式服务器点灯,我演示了ESP32作为一个物联网服务器响应无线点灯请求,用浏览器提交表单的方式点亮了ESP32-cam模块上集成的LED闪光灯照明从而实现了一个嵌入式服务器能实现的功能,不了解的读者可以去看看第二篇。
在该第三篇文章中,我将会在官方程序的基础上再次增加功能,也就是在第二篇的基础上增加功能,程序开发是一个循序渐进的过程。
增加的功能如下:
(1)在单片机服务器的网页里面嵌入了AJAX技术,能通过点击按钮cmd异步提交表单请求从而部分刷新页面,不用担心整个网页被刷新,这个功能是开发网页遥控器必备!做网页遥控智能小车也是必备的。如下图箭头所示:
(2)在服务器响应了一次浏览器客户端按钮的请求之后会有一个异步刷新的响应,比如发送了一次off的操作之后,服务器会返回一个红色的“off ok”到反馈信息栏目的后面,绝对不会发生网页的跳转,这是智能网关人机交互和环境参数采集显示的必备功能,还有一个查询指令的功能,输入check,点击发送可以查询LED的运行状态,这个功能给查询单片机的运行状态开了一个头。如下图所示:
(3)在服务器的主页加入两个网页跳转的链接,“快捷开灯”和“快捷视频”,点击第一个链接能跳转到另一个网页并点亮ESP32的灯,点击第二个链接可以看到ESP32摄像头模块拍摄的视频。这就是在服务器主页实现网页的跳转技术和视频流传输技术,制作广告网关和智能监控摄像头一定不能错过的功能。如下图所示:
边开灯边拍摄的视频图像如下图所示:
(4)将服务器的IP地址显示在客户端的浏览器上面,这个功能相信很多买家都想实现却没有实现吧?虽然我这个代码是靠串口打印把IP地址输入到浏览器才能访问到服务器的主页,但是下一期项目我会加入浏览器配网和域名解析的功能,输入固定的域名就能登录服务器的主页了,到那个时候才发现这个功能是多么地重要。涉及到的技术点不是三言两语能表达的!IP会随着网络变化而变化,不是静态网页的。如下图所示:
观看daodanjishui拍摄的优酷视频地址是:https://v.youku.com/v_show/id_XNTE3ODY4MjYwMA==.html
直接观看
二、开发过程
1.基于官方代码的修改
这个版本的源码有两种方式提交get和post请求,一种是异步提交;一种是刷新网页提交直接获取服务器响应,但是刷新提交表单会跳转网页,还赠送了一个快捷方式提交表单的方法,还有一个查询灯状态check指令的功能。
写这个程序花了我两天的时间,难点还是网页转为字符串,网页写太大了会让单片机内存溢出,所以网页的注释我也删除了,就是为了减少空间。
还有一种方法是把html网页,教程:
https://blog.csdn.net/qq_27114397/article/details/89643232
- 首先通过 gzip 将 HTML 文件压缩为 .gz 文件
- 使用 filetoarray 工具将 .gz 文件转为头文件
- 在 ESP32 程序中将头文件中的数组发送出去
官方代码的代码就是这样搞得,不过我觉得太麻烦了,就直接将网址转为字符串了,所以大小受限制,用了字符串,我还要考虑将String的html字符串转化为字符数组,这样才能传进函数发出去。下面就是这部分代码:
int length=index_html.length();
char buf[length];
strcpy(buf, index_html.c_str());
return httpd_resp_send(req, buf,sizeof(buf)/sizeof(buf[0]));
另外一个问题就是解析原始网页的问题,我当时一直想从官方源码入手还原压缩成为16进制的网页数据的源码,后面还原成乱码,后面还是直接运行官方源码,直接从浏览器登录主页按F12保存下来的网址,最后终于看到了网页的源码,当时不知道多开心。
得到源码之后发现,发送get请求的代码不明显,看起来像是脚本,乱起八糟的,果断放弃了,代码量实在太多了。但是参考了源码的get请求参数的型号和个数,终于通过浏览器测试出get请求的格式,成功发送出get请求。
发送get请求之后又遇到一个问题,没有办法让服务器给客户端一个响应,因为源码没有做这个事情,也就是说你让服务器点灯了,但是能不能点成功,还不知道!后面我翻看发送get请求系列代码的时候看到:httpd_resp_set_type(req, “text/html”);
httpd_resp_set_hdr(req, “Content-Encoding”, “gzip”);我就能猜出服务器是怎么响应客户端并且返回客户端信息的。这个逻辑终于被我捋通了。现在我查询灯的亮灭情况的逻辑也写出来了,不过也简单,用固定语法查询pin4的高低电平就知道灯的亮灭情况了,高电平是亮。
当时异步提交请求出了很严重的问题,根本没有办法响应,后面我不知道是什么问题,都快要崩溃放弃了,而且我也只做了这个请求的测试,但是用的浏览器打开网页文件来测试的,网址写死的。一放到服务器运行,就不能发送请求,我调试了很久,后面沦落到胡乱改各种参数,甚至认为请求网址的参数搞错了,走了很多弯路。后来实在忍不住参考了别人网友那种非异步提交请求的方式,这种方式我原来是不屑一顾的,因为难度太小了,硬着头皮加入了这个请求提交方式之后,发现果然这个方式有效,请求网页放到服务器能提交请求了,虽然是刷新形式的提交,网页会跳转不方便,起码是点亮灯了。后来我想了想,既然有成功的可能,干嘛不再试一下呢?于是我又在浏览器按下F12再抓取一次服务器主页的源码,才恍然大悟,浏览器的调试窗口显示function checkCmd(cmd)函数的定义不存在,我当时就慌了,知道有戏了。仔细一看,这个函数定义的时候,我在后面写了注释,这个js区域写的注释,在转化为字符串又转换为字符数组再还原输出到浏览器上就变味了,所以我果断删除了主页的所有js区域里面的注释,果然成功了,高兴极了,虽然这个源码卖不了不多少钱,不过也是花了两天的生命去调试的。
后期代码的升级和维护就水到渠成了,因为我对源码的逻辑都了解了,虽然很多函数找不到源码的定义,但是根据入口参数和函数的名称也猜出大概的功能了。造车轮不是我这年龄段要做的事情,做出项目才是王道。再说了java和c语言的套接字函数我都用得很多了,这个arduino库函数的套接字函数看不看源码也无所谓了。
官方网页代码如下(示例):
<!DOCTYPE html>
<!-- saved from url=(0019)http://172.17.14.2/ -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ESP32 OV2460</title>
<style>
body {
font-family: Arial,Helvetica,sans-serif;
background: #181818;
color: #EFEFEF;
font-size: 16px
}
h2 {
font-size: 18px
}
section.main {
display: flex
}
#menu,section.main {
flex-direction: column
}
#menu {
display: none;
flex-wrap: nowrap;
min-width: 340px;
background: #363636;
padding: 8px;
border-radius: 4px;
margin-top: -10px;
margin-right: 10px;
}
#content {
display: flex;
flex-wrap: wrap;
align-items: stretch
}
figure {
padding: 0px;
margin: 0;
-webkit-margin-before: 0;
margin-block-start: 0;
-webkit-margin-after: 0;
margin-block-end: 0;
-webkit-margin-start: 0;
margin-inline-start: 0;
-webkit-margin-end: 0;
margin-inline-end: 0
}
figure img {
display: block;
width: 100%;
height: auto;
border-radius: 4px;
margin-top: 8px;
}
@media (min-width: 800px) and (orientation:landscape) {
#content {
display:flex;
flex-wrap: nowrap;
align-items: stretch
}
figure img {
display: block;
max-width: 100%;
max-height: calc(100vh - 40px);
width: auto;
height: auto
}
figure {
padding: 0 0 0 0px;
margin: 0;
-webkit-margin-before: 0;
margin-block-start: 0;
-webkit-margin-after: 0;
margin-block-end: 0;
-webkit-margin-start: 0;
margin-inline-start: 0;
-webkit-margin-end: 0;
margin-inline-end: 0
}
}
section#buttons {
display: flex;
flex-wrap: nowrap;
justify-content: space-between
}
#nav-toggle {
cursor: pointer;
display: block
}
#nav-toggle-cb {
outline: 0;
opacity: 0;
width: 0;
height: 0
}
#nav-toggle-cb:checked+#menu {
display: flex
}
.input-group {
display: flex;
flex-wrap: nowrap;
line-height: 22px;
margin: 5px 0
}
.input-group>label {
display: inline-block;
padding-right: 10px;
min-width: 47%
}
.input-group input,.input-group select {
flex-grow: 1
}
.range-max,.range-min {
display: inline-block;
padding: 0 5px
}
button {
display: block;
margin: 5px;
padding: 0 12px;
border: 0;
line-height: 28px;
cursor: pointer;
color: #fff;
background: #ff3034;
border-radius: 5px;
font-size: 16px;
outline: 0
}
button:hover {
background: #ff494d
}
button:active {
background: #f21c21
}
button.disabled {
cursor: default;
background: #a0a0a0
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
height: 22px;
background: #363636;
cursor: pointer;
margin: 0
}
input[type=range]:focus {
outline: 0
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 2px;
cursor: pointer;
background: #EFEFEF;
border-radius: 0;
border: 0 solid #EFEFEF
}
input[type=range]::-webkit-slider-thumb {
border: 1px solid rgba(0,0,30,0);
height: 22px;
width: 22px;
border-radius: 50px;
background: #ff3034;
cursor: pointer;
-webkit-appearance: none;
margin-top: -11.5px
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #EFEFEF
}
input[type=range]::-moz-range-track {
width: 100%;
height: 2px;
cursor: pointer;
background: #EFEFEF;
border-radius: 0;
border: 0 solid #EFEFEF
}
input[type=range]::-moz-range-thumb {
border: 1px solid rgba(0,0,30,0);
height: 22px;
width: 22px;
border-radius: 50px;
background: #ff3034;
cursor: pointer
}
input[type=range]::-ms-track {
width: 100%;
height: 2px;
cursor: pointer;
background: 0 0;
border-color: transparent;
color: transparent
}
input[type=range]::-ms-fill-lower {
background: #EFEFEF;
border: 0 solid #EFEFEF;
border-radius: 0
}
input[type=range]::-ms-fill-upper {
background: #EFEFEF;
border: 0 solid #EFEFEF;
border-radius: 0
}
input[type=range]::-ms-thumb {
border: 1px solid rgba(0,0,30,0);
height: 22px;
width: 22px;
border-radius: 50px;
background: #ff3034;
cursor: pointer;
height: 2px
}
input[type=range]:focus::-ms-fill-lower {
background: #EFEFEF
}
input[type=range]:focus::-ms-fill-upper {
background: #363636
}
.switch {
display: block;
position: relative;
line-height: 22px;
font-size: 16px;
height: 22px
}
.switch input {
outline: 0;
opacity: 0;
width: 0;
height: 0
}
.slider {
width: 50px;
height: 22px;
border-radius: 22px;
cursor: pointer;
background-color: grey
}
.slider,.slider:before {
display: inline-block;
transition: .4s
}
.slider:before {
position: relative;
content: "";
border-radius: 50%;
height: 16px;
width: 16px;
left: 4px;
top: 3px;
background-color: #fff
}
input:checked+.slider {
background-color: #ff3034
}
input:checked+.slider:before {
-webkit-transform: translateX(26px);
transform: translateX(26px)
}
select {
border: 1px solid #363636;
font-size: 14px;
height: 22px;
outline: 0;
border-radius: 5px
}
.image-container {
position: relative;
min-width: 160px
}
.close {
position: absolute;
right: 5px;
top: 5px;
background: #ff3034;
width: 16px;
height: 16px;
border-radius: 100px;
color: #fff;
text-align: center;
line-height: 18px;
cursor: pointer
}
.hidden {
display: none
}
</style>
</head>
<body>
<section class="main">
<div id="logo">
<label for="nav-toggle-cb" id="nav-toggle">☰ Toggle OV2640 settings</label>
</div>
<div id="content">
<div id="sidebar">
<input type="checkbox" id="nav-toggle-cb" checked="checked">
<nav id="menu">
<div class="input-group" id="framesize-group">
<label for="framesize">Resolution</label>
<select id="framesize" class="default-action">
<option value="10">UXGA(1600x1200)</option>
<option value="9">SXGA(1280x1024)</option>
<option value="8">XGA(1024x768)</option>
<option value="7">SVGA(800x600)</option>
<option value="6">VGA(640x480)</option>
<option value="5" selected="selected">CIF(400x296)</option>
<option value="4">QVGA(320x240)</option>
<option value="3">HQVGA(240x176)</option>
<option value="0">QQVGA(160x120)</option>
</select>
</div>
<div class="input-group" id="quality-group">
<label for="quality">Quality</label>
<div class="range-min">10</div>
<input type="range" id="quality" min="10" max="63" value="10" class="default-action">
<div class="range-max">63</div>
</div>
<div class="input-group" id="brightness-group">
<label for="brightness">Brightness</label>
<div class="range-min">-2</div>
<input type="range" id="brightness" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="contrast-group">
<label for="contrast">Contrast</label>
<div class="range-min">-2</div>
<input type="range" id="contrast" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="saturation-group">
<label for="saturation">Saturation</label>
<div class="range-min">-2</div>
<input type="range" id="saturation" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="special_effect-group">
<label for="special_effect">Special Effect</label>
<select id="special_effect" class="default-action">
<option value="0" selected="selected">No Effect</option>
<option value="1">Negative</option>
<option value="2">Grayscale</option>
<option value="3">Red Tint</option>
<option value="4">Green Tint</option>
<option value="5">Blue Tint</option>
<option value="6">Sepia</option>
</select>
</div>
<div class="input-group" id="awb-group">
<label for="awb">AWB</label>
<div class="switch">
<input id="awb" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="awb"></label>
</div>
</div>
<div class="input-group" id="awb_gain-group">
<label for="awb_gain">AWB Gain</label>
<div class="switch">
<input id="awb_gain" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="awb_gain"></label>
</div>
</div>
<div class="input-group" id="wb_mode-group">
<label for="wb_mode">WB Mode</label>
<select id="wb_mode" class="default-action">
<option value="0" selected="selected">Auto</option>
<option value="1">Sunny</option>
<option value="2">Cloudy</option>
<option value="3">Office</option>
<option value="4">Home</option>
</select>
</div>
<div class="input-group" id="aec-group">
<label for="aec">AEC SENSOR</label>
<div class="switch">
<input id="aec" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="aec"></label>
</div>
</div>
<div class="input-group" id="aec2-group">
<label for="aec2">AEC DSP</label>
<div class="switch">
<input id="aec2" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="aec2"></label>
</div>
</div>
<div class="input-group" id="ae_level-group">
<label for="ae_level">AE Level</label>
<div class="range-min">-2</div>
<input type="range" id="ae_level" min="-2" max="2" value="0" class="default-action">
<div class="range-max">2</div>
</div>
<div class="input-group" id="aec_value-group">
<label for="aec_value">Exposure</label>
<div class="range-min">0</div>
<input type="range" id="aec_value" min="0" max="1200" value="204" class="default-action">
<div class="range-max">1200</div>
</div>
<div class="input-group" id="agc-group">
<label for="agc">AGC</label>
<div class="switch">
<input id="agc" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="agc"></label>
</div>
</div>
<div class="input-group hidden" id="agc_gain-group">
<label for="agc_gain">Gain</label>
<div class="range-min">1x</div>
<input type="range" id="agc_gain" min="0" max="30" value="5" class="default-action">
<div class="range-max">31x</div>
</div>
<div class="input-group" id="gainceiling-group">
<label for="gainceiling">Gain Ceiling</label>
<div class="range-min">2x</div>
<input type="range" id="gainceiling" min="0" max="6" value="0" class="default-action">
<div class="range-max">128x</div>
</div>
<div class="input-group" id="bpc-group">
<label for="bpc">BPC</label>
<div class="switch">
<input id="bpc" type="checkbox" class="default-action">
<label class="slider" for="bpc"></label>
</div>
</div>
<div class="input-group" id="wpc-group">
<label for="wpc">WPC</label>
<div class="switch">
<input id="wpc" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="wpc"></label>
</div>
</div>
<div class="input-group" id="raw_gma-group">
<label for="raw_gma">Raw GMA</label>
<div class="switch">
<input id="raw_gma" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="raw_gma"></label>
</div>
</div>
<div class="input-group" id="lenc-group">
<label for="lenc">Lens Correction</label>
<div class="switch">
<input id="lenc" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="lenc"></label>
</div>
</div>
<div class="input-group" id="hmirror-group">
<label for="hmirror">H-Mirror</label>
<div class="switch">
<input id="hmirror" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="hmirror"></label>
</div>
</div>
<div class="input-group" id="vflip-group">
<label for="vflip">V-Flip</label>
<div class="switch">
<input id="vflip" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="vflip"></label>
</div>
</div>
<div class="input-group" id="dcw-group">
<label for="dcw">DCW (Downsize EN)</label>
<div class="switch">
<input id="dcw" type="checkbox" class="default-action" checked="checked">
<label class="slider" for="dcw"></label>
</div>
</div>
<div class="input-group" id="colorbar-group">
<label for="colorbar">Color Bar</label>
<div class="switch">
<input id="colorbar" type="checkbox" class="default-action">
<label class="slider" for="colorbar"></label>
</div>
</div>
<div class="input-group" id="face_detect-group">
<label for="face_detect">Face Detection</label>
<div class="switch">
<input id="face_detect" type="checkbox" class="default-action">
<label class="slider" for="face_detect"></label>
</div>
</div>
<div class="input-group" id="face_recognize-group">
<label for="face_recognize">Face Recognition</label>
<div class="switch">
<input id="face_recognize" type="checkbox" class="default-action">
<label class="slider" for="face_recognize"></label>
</div>
</div>
<section id="buttons">
<button id="get-still">Get Still</button>
<button id="toggle-stream">Start Stream</button>
<button id="face_enroll" class="disabled" disabled="disabled">Enroll Face</button>
</section>
</nav>
</div>
<figure>
<div id="stream-container" class="image-container hidden">
<div class="close" id="close-stream">×</div>
<img id="stream" src="http://172.17.14.2/">
</div>
</figure>
</div>
</section>
<script>
document.addEventListener('DOMContentLoaded', function (event) {
var baseHost = document.location.origin
var streamUrl = baseHost + ':81'
const hide = el => {
el.classList.add('hidden')
}
const show = el => {
el.classList.remove('hidden')
}
const disable = el => {
el.classList.add('disabled')
el.disabled = true
}
const enable = el => {
el.classList.remove('disabled')
el.disabled = false
}
const updateValue = (el, value, updateRemote) => {
updateRemote = updateRemote == null ? true : updateRemote
let initialValue
if (el.type === 'checkbox') {
initialValue = el.checked
value = !!value
el.checked = value
} else {
initialValue = el.value
el.value = value
}
if (updateRemote && initialValue !== value) {
updateConfig(el);
} else if(!updateRemote){
if(el.id === "aec"){
value ? hide(exposure) : show(exposure)
} else if(el.id === "agc"){
if (value) {
show(gainCeiling)
hide(agcGain)
} else {
hide(gainCeiling)
show(agcGain)
}
} else if(el.id === "awb_gain"){
value ? show(wb) : hide(wb)
} else if(el.id === "face_recognize"){
value ? enable(enrollButton) : disable(enrollButton)
}
}
}
function updateConfig (el) {
let value
switch (el.type) {
case 'checkbox':
value = el.checked ? 1 : 0
break
case 'range':
case 'select-one':
value = el.value
break
case 'button':
case 'submit':
value = '1'
break
default:
return
}
const query = `${baseHost}/control?var=${el.id}&val=${value}`
fetch(query)
.then(response => {
console.log(`request to ${query} finished, status: ${response.status}`)
})
}
document
.querySelectorAll('.close')
.forEach(el => {
el.onclick = () => {
hide(el.parentNode)
}
})
// read initial values
fetch(`${baseHost}/status`)
.then(function (response) {
return response.json()
})
.then(function (state) {
document
.querySelectorAll('.default-action')
.forEach(el => {
updateValue(el, state[el.id], false)
})
})
const view = document.getElementById('stream')
const viewContainer = document.getElementById('stream-container')
const stillButton = document.getElementById('get-still')
const streamButton = document.getElementById('toggle-stream')
const enrollButton = document.getElementById('face_enroll')
const closeButton = document.getElementById('close-stream')
const stopStream = () => {
window.stop();
streamButton.innerHTML = 'Start Stream'
}
const startStream = () => {
view.src = `${streamUrl}/stream`
show(viewContainer)
streamButton.innerHTML = 'Stop Stream'
}
// Attach actions to buttons
stillButton.onclick = () => {
stopStream()
view.src = `${baseHost}/capture?_cb=${Date.now()}`
show(viewContainer)
}
closeButton.onclick = () => {
stopStream()
hide(viewContainer)
}
streamButton.onclick = () => {
const streamEnabled = streamButton.innerHTML === 'Stop Stream'
if (streamEnabled) {
stopStream()
} else {
startStream()
}
}
enrollButton.onclick = () => {
updateConfig(enrollButton)
}
// Attach default on change action
document
.querySelectorAll('.default-action')
.forEach(el => {
el.onchange = () => updateConfig(el)
})
// Custom actions
// Gain
const agc = document.getElementById('agc')
const agcGain = document.getElementById('agc_gain-group')
const gainCeiling = document.getElementById('gainceiling-group')
agc.onchange = () => {
updateConfig(agc)
if (agc.checked) {
show(gainCeiling)
hide(agcGain)
} else {
hide(gainCeiling)
show(agcGain)
}
}
// Exposure
const aec = document.getElementById('aec')
const exposure = document.getElementById('aec_value-group')
aec.onchange = () => {
updateConfig(aec)
aec.checked ? hide(exposure) : show(exposure)
}
// AWB
const awb = document.getElementById('awb_gain')
const wb = document.getElementById('wb_mode-group')
awb.onchange = () => {
updateConfig(awb)
awb.checked ? show(wb) : hide(wb)
}
// Detection and framesize
const detect = document.getElementById('face_detect')
const recognize = document.getElementById('face_recognize')
const framesize = document.getElementById('framesize')
framesize.onchange = () => {
updateConfig(framesize)
if (framesize.value > 5) {
updateValue(detect, false)
updateValue(recognize, false)
}
}
detect.onchange = () => {
if (framesize.value > 5) {
alert("Please select CIF or lower resolution before enabling this feature!");
updateValue(detect, false)
return;
}
updateConfig(detect)
if (!detect.checked) {
disable(enrollButton)
updateValue(recognize, false)
}
}
recognize.onchange = () => {
if (framesize.value > 5) {
alert("Please select CIF or lower resolution before enabling this feature!");
updateValue(recognize, false)
return;
}
updateConfig(recognize)
if (recognize.checked) {
enable(enrollButton)
updateValue(detect, true)
} else {
disable(enrollButton)
}
}
})
</script>
</body></html>
data = pd.read_csv(
'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())
2.服务器读取get请求
代码如下(示例):
String var = String(variable);//获取get请求的id
int val = atoi(value);//获取get请求的值
//在这里打印出传输的指令
Serial.printf("var=%s\n",var);
Serial.printf("val=%d\n",val);
sensor_t * s = esp_camera_sensor_get();
int res = 0;
if(!strcmp(variable, "framesize")) {
if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
}
else if(!strcmp(variable, "quality")){
res = s->set_quality(s, val);//这里是根据id和value设置好,关键
}else if(!strcmp(variable, "on")){//如果接收的字符串是"on"
controlLamp(true);//开灯
httpd_resp_set_type(req, "text/html");//我加入
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, "on ok", 5);
}else if(!strcmp(variable, "off")){
controlLamp(false);//关灯
httpd_resp_set_type(req, "text/html");//我加入
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, "off ok", 6);
}else if(!strcmp(variable, "check")){//查询灯的状态
int LAMP_flag= digitalRead(LAMP_PIN);//读取引脚的状态,并返回HIGH 或LOW
httpd_resp_set_type(req, "text/html");//我加入
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
if(LAMP_flag==HIGH){
return httpd_resp_send(req, "LAMP is on", 10);
}else if(LAMP_flag==LOW){
return httpd_resp_send(req, "LAMP is off", 11);
}
}
3.运行与调试
界面启动如下:
查看视频如下
总结
这次项目也是尝试让ESP32成为智能网关的一次尝试,界面很清爽简单,但是功能却不简单,每一步逻辑都是我精心设计的,虽然这网关只能同时响应一个客户端的请求,但是要改变也只是几行代码的事情,虽然这款单片机目前使用用户不多,大多数是国外的程序开发者使用。国内关于这个模块的资料也不多,靠大家慢慢去玩转才能激发这款功能强大的模块真正的用武之地,再次强调一下,这个项目不是ESP32-CAM模块的评测,是实打实的开源项目。下一期项目会加入手机APP与ESP32交互或者JSP服务与ESP32交互的项目,敬请期待。
代码下载地址:https://gf.bilibili.com/item/detail/1107716114
直接跳转过去