【FireBeetle 2 ESP32-C5】API 连接与本地图像识别
本文介绍了 FireBeetle 2 ESP32-C5 开发套件基于网络 HTTP 协议 API 实现本地图像识别的项目设计。
项目介绍
准备工作:包括开发板固件烧录、模型下载、Python 库部署等;
流程图:包括开发板代码和网页服务器代码对应的流程图;
工程代码:包括开发板板端执行代码、网页服务器端代码等;
工程测试:包括程序运行、图片上传、识别结果查看等;
准备工作
下载 YOLOv8n.pt 模型,或程序自动从 GitHub 网站下载;
开发板烧录 MicroPython 固件;
电脑安装 Thonny IDE 软件,用来调试板端程序;
电脑安装 Python 软件,并部署所需库函数,如 opencv-python 等,便于模型识别与调用。
详见:【LattePanda Mu 开发套件】AI 视觉应用开发——物体识别- Makelog(造物记) .
流程图
网页服务器代码流程图

ESP32-C5 代码流程图

工程代码
包括 ESP32-C5 代码、网页服务器执行代码两部分。
ESP32-C5 代码
打开 Thonny IDE 软件,新建 api_object_recognition.py 工程文件,并添加如下代码
# ------ ESP32-C5 one-shot send local JPG to YOLO server ----------
import network
import urequests
import sys
# ---------- Wi-Fi ----------
SSID = "xxx"
PWD = "xxx"
SERVER = "http://192.168.31.101:5000/detect"
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.connect(SSID, PWD)
while not sta.isconnected():
pass
print("Wi-Fi ok:", sta.ifconfig()[0])
# ---------- send target picture ----------
LOCAL_JPG = "desktop.jpg"
with open(LOCAL_JPG, "rb") as f:
jpg = f.read()
boundary = "123456"
hdr = f"--{boundary}\r\n"
hdr += f'Content-Disposition: form-data; name="image"; filename="{LOCAL_JPG}"\r\n'
hdr += "Content-Type: image/jpeg\r\n\r\n"
end = f"\r\n--{boundary}--\r\n"
body = hdr.encode() + jpg + end.encode()
try:
rsp = urequests.post(SERVER,
data=body,
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"})
data = rsp.json()
print("objects:", data["objects"])
print("result file:", data["result_file"])
except Exception as e:
print("err:", e)
# ---------- finish ----------
print("done – program ends")
sys.exit()
保存代码;
网页服务器代码
在模型文件上级目录创建 yolo_http_server.py 文件,添加如下代码
"""
YOLOv8 HTTP inference server
POST /detect → multipart JPEG upload
GET /last → shows last annotated image in browser
"""
import os
import uuid
import time
from pathlib import Path
from flask import Flask, request, jsonify, send_file
import cv2
from ultralytics import YOLO
app = Flask(__name__)
# ---------- config -------------------------------------------------
MODEL_PATH = "models/yolov8n.pt" # will auto-download first run
STORAGE_DIR = Path("img") # same folder you used before
STORAGE_DIR.mkdir(exist_ok=True)
ALLOWED_EXT = {".jpg", ".jpeg", ".png"}
# ---------- warm-up model once -------------------------------------
print("Loading YOLOv8 model …")
model = YOLO(MODEL_PATH)
print("Model ready.")
# ---------- helpers ------------------------------------------------
def save_upload(multipart_file):
"""store original file and return Path object"""
ext = Path(multipart_file.filename).suffix.lower()
if ext not in ALLOWED_EXT:
raise ValueError("Only .jpg .jpeg .png allowed")
name = f"{int(time.time()*1000)}_{uuid.uuid4().hex[:8]}{ext}"
path = STORAGE_DIR / name
multipart_file.save(path)
return path
def infer_and_annotate(image_path: Path):
"""run YOLOv8 + plot; return (results_list, annotated_cv2_image)"""
img_bgr = cv2.imread(str(image_path))
if img_bgr is None:
raise RuntimeError("Cannot decode image")
results = model(img_bgr) # ultralytics engine
annotated_bgr = results[0].plot() # boxes + labels drawn
# build serialisable list
objects = []
for box in results[0].boxes:
cls = int(box.cls)
conf = float(box.conf)
xyxy = box.xyxy[0].tolist() # [x1,y1,x2,y2]
objects.append({
"label": model.names[cls],
"conf": round(conf, 3),
"box": [int(x) for x in xyxy]
})
return objects, annotated_bgr
# ---------- routes -------------------------------------------------
@app.route("/detect", methods=["POST"])
def detect():
if "image" not in request.files:
return jsonify(error="missing 'image' multipart field"), 400
try:
original_path = save_upload(request.files["image"])
objects, annotated = infer_and_annotate(original_path)
# save annotated image side-by-side
annotated_name = original_path.stem + "_yolo.jpg"
annotated_path = STORAGE_DIR / annotated_name
cv2.imwrite(str(annotated_path), annotated)
return jsonify(
objects=objects,
result_file=annotated_name,
original_file=original_path.name
)
except Exception as e:
return jsonify(error=str(e)), 500
@app.route("/last")
def last_image():
"""convenience: show most recent _yolo.jpg in browser"""
files = sorted(STORAGE_DIR.glob("*_yolo.jpg"), key=os.path.getmtime, reverse=True)
if not files:
return "No annotated image yet", 404
return send_file(files[0], mimetype="image/jpeg")
@app.route("/")
def root():
return ("YOLOv8 inference server is running.
"
"POST to /detect (multipart JPEG)
"
"GET /last (newest annotated image)")
# ---------- entry --------------------------------------------------
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, threaded=True)
保存代码;
工程测试
命令行终端进入网页服务器代码所在文件夹;
终端执行 python yolo_http_server.py 指令;
打开终端提示的网址 http://192.168.31.109:5000/ 显示界面如下
表明模型加载成功且正在运行;
运行 Thonny IDE 并连接开发板,将目标图片 desktop.jpg 传输至板端根目录;
运行 api_object_recognition.py 程序,终端输出 WiFi 连接和物体识别结果;
打开网页 http://192.168.31.109:5000/last 查看图片识别结果;
总结
本文介绍了 FireBeetle 2 ESP32-C5 开发 HTTP 协议 API 实现本地图像识别的项目设计,包括网页服务器工程代码、板端执行代码、工程测试流程、识别结果查看等,为相关产品的开发设计和快速应用提供了参考。
舞动的枫叶2025.10.03
很棒的作品!ESP32-C5支持蓝牙和WiFi6无线通信,可以蓝牙连接手机,手机发送图片,由ESP32-C5作为转发器,通过WiFi上传图片到网页服务器完成识别~