【DigiKey“智造万物,快乐不停”创意大赛】M5Paper 超低功耗素雅网络相册
本帖最后由 HonestQiao 于 2023-12-13 00:24 编辑<p>这篇分享,是结合M5Paper的墨水屏,以及其核心ESP32的低功耗模式,实现一个超低功耗的电子相册。</p>
<p>其核心功能如下:</p>
<ul>
<li>通过网络更新图片</li>
<li>使用石墨屏显示</li>
<li>使用深度睡眠模式降低功耗,并定时唤醒以便更新图片</li>
<li>使用Python构建图片服务器,进行图片的灰度转换</li>
</ul>
<p>下面分享具体的实现过程。</p>
<p> </p>
<p><strong>一、ESP32低功耗了解</strong></p>
<p>ESP32的工作模式,分为以下几种:</p>
<p></p>
<p>关机当然是功耗最低的了,但是需要人工启动,或者靠外部设备启动。</p>
<p>下面是各种常见模式的运行电路/外设和功耗:</p>
<p>1. 正常工作模式:</p>
<p> </p>
<p> </p>
<p>2. 轻度睡眠模式:</p>
<p> </p>
<p>3. 深度睡眠模式:</p>
<p> </p>
<p>显而易见,不关机的情况下,深度睡眠,就是功耗最低的模式了。</p>
<p>在休眠模式下,ESP32进入省电模式,将所有数据保存在 RAM 中。此时,任何不必要的外围设备的电源都会被切断,而RAM被供电来保留其数据。</p>
<p>外围设备都断电了,那显示屏怎么办,还亮不亮? 还好M5Paper是墨水屏,只要显示了,断电,不怕的,继续显示。</p>
<p> </p>
<p><strong>二、在Arduino控制ESP32深度休眠</strong></p>
<p>在Arduino中, 要控制ESP32深度休眠,非常简便,通过以下的调用即可完成:</p>
<pre>
<code class="language-cpp">#define uS_TO_S_FACTOR 1000000ULL /* 微秒到秒 */
#define TIME_TO_SLEEP 15 /* 岁杪时间(s) */
// 定义深度休眠的时长
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// 进入深度休眠状态
esp_deep_sleep_start();</code></pre>
<p>通过上述代码,就能轻松控制了。</p>
<p> </p>
<p>另外,要想在睡眠状态,还能保存变量数据,是的唤醒后继续生效,需要使用显现的方式定义变量:</p>
<pre>
<code class="language-cpp">RTC_DATA_ATTR int bootCount = 0;/* 启动次数计数 */</code></pre>
<p>RTC_DATA_ATTR定义了这个变量,在ESP32休眠的时候,保存到RTC 内存中。</p>
<p> </p>
<p><strong>三、M5Paper从网络获取图片显示</strong></p>
<p>M5Paper的EPD库,提供了专门的方法,来显示网络图片,具体调用如下:</p>
<pre>
<code class="language-cpp">// 创建画布
canvas.createCanvas(540, 960);
// 从网络获取并绘制图片
Serial.println("Fetch and draw the picture");
canvas.drawJpgUrl( "http://192.168.1.15:18080/album/get_img/refresh");
canvas.pushCanvas(0, 0, UPDATE_MODE_GC16);</code></pre>
<p>网址 <a href="http://192.168.1.15:18080/album/get_img/refresh" target="_blank">http://192.168.1.15:18080/album/get_img/refresh</a> 是我本地使用Python写的一个简单图片服务器,专门给M5Paper提供灰度图片。</p>
<p>因为M5Paper只能显示灰度图片,所以使用Python服务来进行转换,这样子可以提高从网络获取数据的速度,并提高显示的效率。</p>
<p> </p>
<p><strong>四、Python图片服务器</strong></p>
<p>上面给M5Paper提供的图片服务器,使用Python语言编写,并使用了 flask 来建立WEB服务,使用 Pillow 来提供图片处理,使用 numpy 来进行图片到手绘风格的转换。</p>
<p>尝试过,直接使用Pillow把彩色图片给灰度化的话,界面上会有很多元素留下灰色的点,效果不好。</p>
<p>经过一番研究,可以用 numpy 辅助处理,把彩色图片转换到手绘风格,并去掉一些无关的点,效果很好。</p>
<p>最终的具体代码如下:</p>
<pre>
<code class="language-cpp">#!/usr/bin/env python
# coding=utf-8
from flask import Flask,Response
from flask import render_template
import os
from PIL import Image
import numpy as np
img_index = 0
img_max = 10
img_depth = 10.
app = Flask(__name__)
def pad_image(image, target_size):
"""
:param image: input image
:param target_size: a tuple (num,num)
:return: new image
"""
iw, ih = image.size# 原始图像的尺寸
w, h = target_size# 目标图像的尺寸
print("original size: ",(iw,ih))
print("new size: ", (w, h))
scale = min(w / iw, h / ih)# 转换的最小比例
# 保证长或宽,至少一个符合目标图像的尺寸 0.5保证四舍五入
nw = int(iw * scale+0.5)
nh = int(ih * scale+0.5)
print("now nums are: ", (nw, nh))
image = image.resize((nw, nh), Image.Resampling.BICUBIC)# 更改图像尺寸,双立法插值效果很好
#image.show()
new_image = Image.new('RGB', target_size, (255, 255, 255))# 生成黑色图像
# // 为整数除法,计算图像的位置
new_image.paste(image, ((w - nw) // 2, (h - nh) // 2))# 将图像填充为中间图像,两侧为黑色的样式
#new_image.show()
return new_image
def image_convert_hand(img_orig):
# 图像的手绘
"""黑白风格
边界的位置比较重
相同或相近色彩趋近于白色
咯有光源效果
"""
# 读取彩色图片并转化为np数组
a = np.array(img_orig.convert('L')).astype('float')
depth = img_depth
grad = np.gradient(a)
grad_x, grad_y = grad
grad_x = grad_x*depth/100
grad_y = grad_y*depth/100
A = np.sqrt(grad_x**2 + grad_y**2 + 1.)
uni_x = grad_x/A
uni_y = grad_y/A
uni_z = 1./A
vec_el = np.pi/2.2
vec_ez = np.pi/4.
dx = np.cos(vec_el)*np.cos(vec_ez)
dy = np.cos(vec_el)*np.sin(vec_ez)
dz = np.sin(vec_el)
b = 255*(dx*uni_x + dy*uni_y + dz*uni_z)
b = b.clip(0, 255)
im = Image.fromarray(b.astype('uint8'))
# 保存转化后的图片
return im
@app.route('/')
def index():
resp = Response("<img src='/album/get_img/show'>", mimetype="text/html")
return resp
@app.route('/album/get_img/<action>')
def get_image(action):
global img_index, img_max
img_path = 'imgs_out/{}.jpg'.format(img_index)
if not os.path.exists(img_path):
img_path_src = 'imgs/{}.webp'.format(img_index)
image = Image.open(img_path_src)
image = image_convert_hand(image)
size = (540, 960)
newImage = pad_image(image, size)
newImage.save(img_path)
img_stream = open(img_path, "br")
resp = Response(img_stream, mimetype="image/jpeg")
resp.headers.add('content-length', str(os.path.getsize(img_path)))
if action == 'refresh':
img_index = img_index + 1
if img_index >= img_max:
img_index = 0
return resp
if __name__ == '__main__':
app.run(debug=True, port=18080, host='0.0.0.0')</code></pre>
<p> </p>
<p>在上述代码中:</p>
<ul>
<li>image_convert_hand() 用于把图片转换为手绘风格</li>
<li>pad_image()用于缩放图片到540x960,并适当的进行补白</li>
<li>
<p>get_image() 用于提供最新的图片</p>
</li>
<li>
<p>查看当前的图片: http://ip:18080//album/get_img/show</p>
</li>
<li>
<p>查看当前的图片并指向下一章: http://ip:18080//album/get_img/refresh</p>
</li>
</ul>
<p>代码中使用 img_max 一共定义了最大10张图片,需要提前准备好原始图片。</p>
<p> </p>
<p>要运行上述代码,需要文件目录使用如下的结果:</p>
<p> </p>
<p>其中:</p>
<ul>
<li>album_server.py: 为Python图片服务器代码</li>
<li>imgs:为原始图片目录,因为我是从网络下载的图片,所以都是webp图片</li>
<li>imgs_out:为处理后的图片缓存起来备用,提供下一次访问的速度,删除后,访问时,会自动重新生成</li>
</ul>
<p>运行上述代码后,就可以打开浏览器访问: http://ip:18080/,就可以查看最新执行的图片了,下面为原始图片和实际处理后的图片的对比:</p>
<p></p>
<p> </p>
<p>因为M5Paper的墨水屏,只能显示灰度图片,所以我命名为素雅网络相册</p>
<p> </p>
<p><strong>五、M5Paper 超低功耗素雅电子相册</strong></p>
<p>经过上面的步骤,完成了各个部分的功能,将所有功能整合起来,完整的代码如下:</p>
<pre>
<code class="language-cpp">#include <M5EPD.h>
#include <WiFi.h>
#define uS_TO_S_FACTOR 1000000ULL /* 微秒到秒 */
#define TIME_TO_SLEEP 15 /* 岁杪时间(s) */
RTC_DATA_ATTR int bootCount = 0;/* 启动次数计数 */
M5EPD_Canvas canvas(&M5.EPD);
void setup() {
// M5初始化
M5.begin();
M5.RTC.begin();
Serial.println("System booting...");
// 启动次数计数
++bootCount;
Serial.println("Boot number: " + String(bootCount));
// 配置休眠时长
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds");
// 连接到WiFi
WiFi.begin("OpenBSD", "********");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("OK");
// 设置竖屏
M5.EPD.SetRotation(90);
// 首次启动显示应用名称
if (bootCount == 1) {
Serial.println("M5Paper Album");
Serial.println("Author: HonestQiao");
// 首次启动时清屏
M5.EPD.Clear(true);
// 创建画布
canvas.createCanvas(300, 600);
// 显示文本
canvas.setTextSize(12);
canvas.drawString("M5Paper", 10, 0);
canvas.drawString("Album", 10, 100);
canvas.setTextSize(2);
canvas.drawString("Author: HonestQiao", 0, 400);
canvas.pushCanvas(120, 300, UPDATE_MODE_GL16);
delay(3000);
canvas.deleteCanvas();
}
// 创建画布
canvas.createCanvas(540, 960);
// 从网络获取并绘制图片
Serial.println("Fetch and draw the picture");
canvas.drawJpgUrl( "http://192.168.1.15:18080/album/get_img/refresh");
canvas.pushCanvas(0, 0, UPDATE_MODE_GC16);
delay(1000);
// 即将进入睡眠
Serial.println("Going to sleep now");
delay(100);
// 进入睡眠
esp_deep_sleep_start();
}
void loop() {
}
</code></pre>
<p> </p>
<p>因为各个部分,在前面已经做过介绍了,所以这里就不对代码继续说明了。</p>
<p> </p>
<p><strong>六、运行效果</strong></p>
<p>运行的效果,就直接上视频,更直观:</p>
<p>95c6e68d611b5b36ec32bb60ce566ffe<br />
</p>
<p>从上面的视频效果可以看出,虽然显示的是灰度图片,但是显示出来的效果,还是非常不错的。</p>
<p> </p>
<p>如果使用串口工具监听串口输出,也可以看到对应的信息:</p>
<p></p>
<p> </p>
<p><strong>七、总结 </strong></p>
<p>感觉这次选择的M5Paper非常的合适,用起来很舒服。</p>
<p>石墨屏与ESP32结合,能够玩出很多普通屏幕所不能玩出的花样。</p>
<p>就像这次 超低功耗素雅网络相册,如果用普通的TFT屏,就没法做到,还非得是墨水屏才好用。</p>
<p> </p>
<p><strong>八、鸣谢</strong></p>
<p>在这次分享的项目的探究过程中,参考了不少资料,下面是部分还记得出处资料:</p>
<ul>
<li><a href="https://www.yiboard.com/thread-1847-1-1.html">详解ESP32深度睡眠模式及其唤醒源 - 乐鑫ESP32</a>(www.yiboard.com/thread-1847-1-1.html)</li>
<li><a href="https://blog.csdn.net/qq_67171848/article/details/127043203">ESP32 低功耗模式_esp32低功耗-CSDN博客</a>(blog.csdn.net/qq_67171848/article/details/127043203)</li>
<li><a href="https://blog.51cto.com/u_15284384/3051452">【IoT】ESP32 Arduino 超低功耗模式 Deep-sleep_51CTO博客</a>(blog.51cto.com/u_15284384/3051452)</li>
<li><a href="https://www.jianshu.com/p/6f044e035511">玩转 ESP32 + Arduino (十七) deepsleep深睡眠模式 - 简书(</a>www.jianshu.com/p/6f044e035511)</li>
<li><a href="https://blog.csdn.net/qq_35189715/article/details/94594562">Python彩色图片转手绘风格-CSDN博客</a>(blog.csdn.net/qq_35189715/article/details/94594562)</li>
<li><a href="https://blog.csdn.net/JZJZ73/article/details/108471960">python PIL 填充图片 更改到规定尺寸_pil pad 到固定大小-CSDN博客</a>(blog.csdn.net/JZJZ73/article/details/108471960)</li>
</ul>
<p>对上述资料的作者,以及没有列出但学习过的资料的作者,都表示深深的感谢!!!</p>
<p> </p>
<p>这个处理成手绘风格的图片操作非常NB呀,楼主有没有考虑打包成一个exe的软件发布一下,用于黑白打印机效果很好。<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan88.gif" width="59" /></p>
sipower 发表于 2023-12-16 15:15
这个处理成手绘风格的图片操作非常NB呀,楼主有没有考虑打包成一个exe的软件发布一下,用于黑白打印机效果 ...
<p>可以考虑呀。</p>
<p> </p>
<p>封装一下,然后选择彩色图片,就能预览黑白手绘风格,然后调用打印机打印。</p>
<p>点赞点赞</p>
页:
[1]