智能设备 - 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 服务器运行画面

猜你喜欢

转载自blog.csdn.net/m0_50614038/article/details/129495668