这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【M5PaperESP32E-Ink】网页传图投屏到M5PAPER墨水屏

共2条 1/1 1 跳转至

【M5PaperESP32E-Ink】网页传图投屏到M5PAPER墨水屏

助工
2026-05-03 11:29:30     打赏

M5Paper 搭载 ESP32 主控与电子墨水屏,凭借低功耗、断电不刷新的特点,非常适合静态图片、文字信息常驻显示。本次基于 UiFlow2 ,采用 MicroPython 开发,搭建简易网页上传服务,实现电脑/手机网页端上传图片,投屏到 M5Paper 墨水屏。

一、硬件介绍

使用的硬件为M5Paper ESP32 E-Ink Development Kit V1.1,是M5Stack推出的触控墨水屏开发板。主控芯片是 ESP32,搭载一块4.7英寸的触摸电子墨水屏,分辨率960×540。

相关参数:

  • SOC:ESP32-D0WDQ6-V3@双核处理器,主频 240MHz

  • 墨水屏:型号:ED047TC1,540 x 960@4.7",灰度 : 16 级

  • 按键:拨轮开关 *1 ,复位按键 *1

  • RTC:BM8563

  • 温湿度传感器:SHT30

二、开发环境

使用M5Stack官方开发平台——UiFlow2,支持图形化积木拖拽编程与 MicroPython 编程,内置 M5Paper 屏幕、外设等底层驱动,无需复杂底层适配,上手门槛低。

1777778343711261.jpg

官方提供了M5Burner固件烧录工具, 通过M5Burner可以很方便的烧录 UiFlow 固件,并在烧录时一同写入 Wi-Fi 等配置信息。

1777778406712263.png

三、功能实现

UiFlow2 已完整封装墨水屏显示、网络服务等底层驱动,只需熟悉相关 API 参数即可快速开发。

此前做过 ESP32 摄像头图传项目,实现的是将摄像头画面上传至网页显示;本次要实现的功能刚好相反:从网页上传图片,由 ESP32 接收并显示在墨水屏上。

关键代码如下:

内嵌网页服务

ESP32 搭建本地 Web 服务,以设备 IP 为访问地址,内置图片上传页面,支持局域网内手机、电脑浏览器访问选图上传。

def html_page(status="就绪"):
    return '''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>M5Paper传图工具</title>
<style>
body { font-family: sans-serif; padding: 20px; max-width: 500px; margin: 0 auto; }
input[type=file] { width: 100%; font-size: 16px; margin: 10px 0; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
input[type=submit] { width: 100%; height: 50px; font-size: 18px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
input[type=submit]:hover { background: #0056b3; }
p { color: #333; background: #f5f5f5; padding: 10px; border-radius: 4px; word-break: break-all; }
</style>
</head>
<body>
<h2>M5Paper传图工具</h2>
<form method="POST" action="/" enctype="multipart/form-data">
<input type="file" name="file" accept="image/*">
<input type="submit" value="展示到M5Paper">
</form>
<p>状态: ''' + status + '''</p>
</body>
</html>'''

HTTP 图片接收

采用流式分片接收网页上传的图片数据,避免一次性加载大图片导致 ESP32 内存溢出、程序崩溃。

def handle_client(client):
    global current_status
    try:
        client.settimeout(30.0)
        
        # ---------- 读取 HTTP 头 ----------
        header_data = b''
        while b'\r\n\r\n' not in header_data:
            chunk = client.recv(1024)
            if not chunk:
                break
            header_data += chunk
        
        if b'\r\n\r\n' not in header_data:
            send_response(client, html_page("请求错误"))
            client.close()
            return
        
        header_end = header_data.find(b'\r\n\r\n')
        headers = header_data[:header_end].decode('utf-8', 'ignore')
        
        # 解析 Content-Length 和 Boundary
        content_length = 0
        boundary = None
        for line in headers.split('\r\n'):
            if line.lower().startswith('content-length:'):
                content_length = int(line.split(':')[1].strip())
            if line.lower().startswith('content-type:'):
                if 'boundary=' in line:
                    boundary = '--' + line.split('boundary=')[1].strip()
        
        # GET 请求:只返回网页,不刷新屏幕
        if not headers.startswith('POST'):
            send_response(client, html_page(current_status))
            client.close()
            return
        
        # ---------- 流式接收写入临时文件 ----------
        body_remaining = header_data[header_end + 4:]
        temp_path = '/flash/upload_tmp.png'
        
        with open(temp_path, 'wb') as f:
            if body_remaining:
                f.write(body_remaining)
                received = len(body_remaining)
            else:
                received = 0
            
            while received < content_length:
                to_read = min(2048, content_length - received)
                chunk = client.recv(to_read)
                if not chunk:
                    break
                f.write(chunk)
                received += len(chunk)
        
        print('共接收:', received, '字节')
        
        # ---------- 从临时文件提取纯图片数据 ----------
        if boundary:
            with open(temp_path, 'rb') as f:
                file_data = f.read()
            
            b_enc = boundary.encode()
            pos1 = file_data.find(b_enc)
            if pos1 != -1:
                hdr_end = file_data.find(b'\r\n\r\n', pos1)
                if hdr_end != -1:
                    file_start = hdr_end + 4
                    pos2 = file_data.find(b_enc, file_start)
                    if pos2 != -1:
                        img_data = file_data[file_start:pos2].rstrip(b'\r\n')
                        
                        with open('/flash/upload.png', 'wb') as f:
                            f.write(img_data)
                        
                        print('图片已保存:', len(img_data), '字节')
                        current_status = "上传成功, " + str(len(img_data)) + " 字节"
                        # 只有成功才刷新屏幕显示图片
                        show_image()
                    else:
                        current_status = "数据格式错误"
                else:
                    current_status = "文件头缺失"
            else:
                current_status = "分隔符缺失"
        else:
            current_status = "无分隔符信息"
        
        try:
            os.remove(temp_path)
        except:
            pass
        
        send_response(client, html_page(current_status))
        
    except Exception as e:
        print('客户端错误:', e)
        current_status = "错误: " + str(e)
        try:
            send_response(client, html_page(current_status))
        except:
            pass
    finally:
        client.close()

墨水屏图片显示

对接收完成的图片数据进行解析,将图片展示在 M5Paper 电子墨水屏上。

def show_image():
    global current_status
    try:
        Widgets.fillScreen(0xFFFFFF)
        img = Widgets.Image("/flash/upload.png", 0, 0)
        img.setVisible(True)
        M5.update()
        current_status = "图片已显示"
        print('图片已显示')
    except Exception as e:
        print('显示错误:', e)
        current_status = "显示失败"

设备初始化配置

开机自动连接 WiFi,启动 Web 服务,屏幕同步显示设备 IP,方便访问传图页面。

def setup():
    global server_socket, label_ip
    
    M5.begin()
    Widgets.fillScreen(0xFFFFFF)
    
    # 初始化画面(此时不刷新,等最后统一刷)
    Widgets.Label("M5Paper 传图工具", 120, 40, 1.0, 0x000000, 0xFFFFFF, Widgets.FONTS.AlibabaPuHuiTiCN24)
    label_ip = Widgets.Label("正在连接WiFi...", 100, 120, 1.0, 0x000000, 0xFFFFFF, Widgets.FONTS.AlibabaPuHuiTiCN24)
    
    ip = connect_wifi()
    if ip:
        label_ip.setText("IP地址: " + ip)
        Widgets.Label("等待上传图片...", 100, 180, 1.0, 0x000000, 0xFFFFFF, Widgets.FONTS.AlibabaPuHuiTiCN24)
    else:
        label_ip.setText("WiFi连接失败")
        M5.update()  # 失败时刷一次,让用户看到
        return
    
    # 初始化完成,统一刷新一次
    M5.update()
    
    try:
        addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(addr)
        server_socket.listen(3)
        server_socket.setblocking(False)
        print('HTTP服务器已启动, 端口80')
    except Exception as e:
        print('服务器错误:', e)
        label_ip.setText("服务器错误")
        M5.update()

四、效果展示

使用局域网内手机、电脑浏览器访问 M5Paper 的 IP,选择本地图片上传,M5Paper 接收并显示在墨水屏上,无需数据线,局域网内随时随地一键传图。

1777778706580485.jpg






关键词: M5Paper     E-Ink     墨水屏    

院士
2026-05-05 15:52:29     打赏
2楼

谢谢分享,效果不错哟。


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]