智能设备 - ESP32-CAM上网、拍照、上传图片与状态显示
本项目的智能装备采用 ESP32-CAM,需要提供上网、拍照、上传图片与状态显示等功能,开发语言讲采用 MicroPython,因为上网、拍照与状态显示等三项功能 ESP32-CAM 可以单独完成,而上传图片需要事先架设好 Web 服务器作为接收的服务器,所以放在最后再来说明。ESP32-CAM 提供一个拍摄图片的网页 API,可以让用户调用,藉以获取摄像头的拍摄结果,让 ESP32-CAM 支援
智能设备 - ESP32-CAM
本项目的智能装备采用 ESP32-CAM,需要提供上网、拍照、上传图片与状态显示等功能,开发语言讲采用 MicroPython,因为上网、拍照与状态显示等三项功能 ESP32-CAM 可以单独完成,而上传图片需要事先架设好 Web 服务器作为接收的服务器,所以放在最后再来说明。
设备状态
简单的将 ESP32-CAM 区分成三个状态:初始、就绪、忙碌,而将这些状态透过红色 LED 来呈现,而分别是 300ms 闪烁,恒亮、以及 100ms 闪烁这三个频率来呈现,而为了避免灯号控制功能与主程序之间有冲突,所以我们用计时器中断触发的方式来进行。
原始代码
from machine import Timer, Pin
(ST_INIT, ST_READY, ST_BUSY) = (300, 0, 100)
def toggle_led(led_pin):
led_pin.value(not led_pin.value())
def led_blink_timed(timer, led_pin, state):
if state == 'READY':
timer.deinit()
led_pin.value(ST_READY)
elif state == 'INIT':
timer.init(period=ST_INIT, mode=Timer.PERIODIC, callback=lambda t: toggle_led(led_pin))
elif state == 'BUSY':
timer.init(period=ST_BUSY, mode=Timer.PERIODIC, callback=lambda t: toggle_led(led_pin))
else:
print('not define yet')
# 声明引脚 D2 作为LED的引脚
led_pin = Pin(33, Pin.OUT)
timer = Timer(1) # 创建定时器对象
# 定时器触发
led_blink_timed(timer, led_pin, state='INIT')
接著把这些代码添加到各个操作中,已确保用户可以从灯号来得知目前 ESP32-CAM 的运作状态。
ESP32-CAM 上网
首先需有网络存取点,才能让 ESP32-CAM 连上后上网,并且进行时间同步,以确保智能设备能与其他设备的时间都是同步的,参考代码如下。
原始代码
# enable station interface and connect to WiFi access point
import time, network, ntptime
def connectWiFi():
wlan = network.WLAN(network.STA_IF)
if wlan.isconnected():
wlan.disconnect()
wlan.active(True)
wlan.connect('your-ssid', 'your-password')
while not wlan.isconnected():
pass
print('network config: ', wlan.ifconfig())
ntptime.NTP_DELTA = ntptime.NTP_DELTA - 8*60*60 # UTC+8
ntptime.settime()
print("同步后本地时间:%s" %str(time.localtime()))
led_blink_timed(timer, led_pin, state='INIT')
connectWiFi()
下图将上述代码透过 Thonny 开发工具写到 ESP32-CAM 智能设备中,并存为 main.py ,预设开机后自行运行该文件,需要记住 ESP32-CAM 连上 Wi-Fi 后的 IP 位址,因为后续代码需要透过 IP 来ˊ取得图片。
图 1. 在 ESP32-CAM 建立 main.py 档
ESP32-CAM 拍照
ESP32-CAM 提供一个拍摄图片的网页 API,可以让用户调用,藉以获取摄像头的拍摄结果,让 ESP32-CAM 支援网页功能需要事先安装 microdot 包,可以透过在主机输入以下命令来进行安装,详细介绍可以参阅前面章节。
# 安装 mip
mpremote connect /dev/cu.usbserial-14110 mip install mip
# 从 github 安装
mpremote connect /dev/cu.usbserial-14110 mip install https://raw.githubusercontent.com/miguelgrinberg/microdot/main/src/microdot.py
将上述代码透过 Thonny 开发工具写到 ESP32-CAM 智能设备中,将会建立一个拍照 API - /image_feed。
原始代码
import time
from microdot import Microdot
import camera
app = Microdot()
@app.route('/image_feed')
def image_feed(request):
led_blink_timed(timer, led_pin, state='BUSY')
while not camera.init(0, format=camera.JPEG, fb_location=camera.PSRAM):
time.sleep(1)
frame = camera.capture()
camera.deinit()
led_blink_timed(timer, led_pin, state='READY')
return frame, 200, {'Content-Type': 'image/jpeg'}
if __name__ == '__main__':
led_blink_timed(timer, led_pin, state='READY')
app.run(debug=True) # 因为 Web 服务器属于阻断式服务,如果写在下方将无法运行
图 2. 在 ESP32-CAM 建立拍照 API
在本机建立一个初步的图形用户介面观看拍照 API是否可以正常运作。主要的部份是够过 img 标签来读取 ESP32-CAM 的图片 <img src="http://192.168.137.160:5000/image_feed" id="uPyImage">
,使用按键 button 来进行实时的拍照 <button value="picture" onClick="imageRefresh()">
。
原始代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>石头计算图形用户介面</title>
<meta name="robots" content="index,follow">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>石头计算图形用户介面</h1>
<div class="container">
<button value="picture" onClick="imageRefresh()">ESP32-CAM的照片</button>
<img src="http://192.168.137.160:5000/image_feed" id="uPyImage">
</div>
<script language="javascript">
var uPyImage = document.getElementById("uPyImage");
function imageRefresh(){
imageSrc = uPyImage.src
if (imageSrc.lastIndexOf('?')>0)
imageSrc = imageSrc.substr(0,imageSrc.lastIndexOf('?'));
uPyImage.src = imageSrc + "?t=" + new Date().getTime();
console.log('refresh URL - ' + uPyImage.src);
}
</script>
</body>
</html>
下图为本机的图形用户介面调用 ESP32-CAM 拍照 API的结果。
图 3. 使用本机的图形用户介面调用 ESP32-CAM 拍照 API
ESP32-CAM 上传图片
要从 ESP32-CAM 上传图片到 WEB 服务器,1. ESP32-CAM 与 WEB 服务器必须连上相同的子网;2. 用户透过图形用户介面发出请求,让3. ESP32-CAM 上传图片到4. WEB 服务器并储存起来,接著5. WEB 服务器回传响应结果,6. ESP32-CAM 也响应结果给图形用户介面,画面如下图所示。
图 4. 透过图形用户介面请求 ESP32-CAM 上传照片到 WEB 服务器
在图形用户介面中新增一个按键用来要求传送照片
请求 ESP32-CAM 上传主要的代码如下
function sendImage(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Typical action to be performed when the document is ready:
document.getElementById("ESP32Response").innerHTML = xhttp.responseText;
}
};
// 要求 ESP32-CAM 传送照片,所以以下位址为 ESP32-CAM 的位址
xhttp.open("GET", "http://192.168.137.160:5000/send_image", true);
xhttp.send();
}
图 5. 图形用户介面请求 ESP32-CAM 上传代码说明
图 6. 图形用户介面的传送图片按钮
ESP32-CAM 上传图片的 API 为 /send_image,首先先进行拍照,接著就调用 urequests 包的 post 方法,将照片传给 WEB 服务器,urequests 包在安装 mip 包的时候会一并进行安装,所以不用另外安装。将 WEB 服务器的响应结果以 JSON 格式回传给图形用户介面。
ESP32-CAM 上传图片代码
# 上传照片
@app.route('/send_image')
def send_image(request):
url = 'http://192.168.137.200:5000/esp32_im'
while not camera.init(0, format=camera.JPEG, fb_location=camera.PSRAM):
time.sleep(1)
frame = camera.capture()
camera.deinit()
r = requests.post(url, headers = {'content-type': 'image/jpeg'}, data = frame)
print(r.json())
return r.json(), 200, {'Content-Type': 'application/json'}
Web 服务器是以 Flask 网页框架撰写而成,而在 ESP32-CAM 使用的 microdot 包其实也是以 Flask 为范本制作,这样可以有效的降低在不同平台的学习成本。建立一个 /esp32_im 的 Web Service API 接口,用来接收 ESP32-CAM 所传来的图片,并将之写为 esp32-im-received.jpg 文件,储存在 Web 服务器的文件系统中,并回传响应信息。
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/esp32_im", methods=["POST","GET"])
def process_image():
imageData = request.get_data(parse_form_data=False)
file = open("esp32-im-received.jpg", "wb")
file.write(imageData)
print(request.content_length)
return jsonify({'msg': 'IMAGE writed'})
if __name__ == "__main__":
app.run(host='0.0.0.0',debug=True,)
下图是 Web 服务器运行画面,可以看到有接收到来自 ESP32-CAM 的 POST 上传照片请求。
图 7. Web 服务器运行画面
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)