Arduin ESP32+epaper(电子墨水屏)时钟相册制作教程
Arduin ESP32 +epaper(电子墨水屏)时钟相册制作教程
- 🔖epaper(电子墨水屏)采用的是:合宙1.54“ 电子墨水屏(e-paper)
- 📍相关篇《Arduino框架下ESP32/ESP8266+合宙1.54“ 电子墨水屏(e-paper)驱动显示示例》
- 🌿电子屏幕相关资料:
https://www.waveshare.net/wiki/1.54inch_e-Paper_Module - 🪓图片取模教程和工具介绍:https://www.waveshare.net/wiki/Image2Lcd%E5%9B%BE%E7%89%87%E5%8F%96%E6%A8%A1%E6%AD%A5%E9%AA%A4
- 📌同类型的,微雪品牌的相关资料参考(提供了包含约50种规格的电子墨水屏(e-paper)):
https://www.waveshare.net/wiki/1.54inch_e-Paper_Module - 🔱个人采用的是esp32测试,esp8266应该也可以的。
🔖有关数据管理和更新,可以参考以下内容:
- 📌通过网页实现图片数据管理,可以参考《Arduino ESP32当作FTP服务器 访问SD卡文件》
- 📌《Arduino ESP32 Web服务器SD卡文件管理》


🛠图片取模制作
- 🔧制作软件:
Image2Lcd - 🌿图片尺寸要求:200X200像素点

- 📍图片素材获取源:
https://icons8.com/icons/set/raspberry-pi - 📍图片素材获取源2:
https://fontawesome.com/icons?d=gallery&p=2
🍭字模数据转BIN文件
- 🔨需要借助c代码,使用C编译器指令或
VSCode将字模数组转bin文件。200X200像素的图片去取模转BIN文件后的大小都是5KB。
#include <stdio.h>
const unsigned char gImage_yun[5000] = {图片字符数组}
int main()
{size_t size = sizeof(gImage_yun[0]);size_t count = sizeof(gImage_yun) / size;FILE *fp = fopen("gImage_yun.bin", "wb");if (fp == NULL){printf("Failed to open file.\n");return 1;}size_t elements_written = fwrite(gImage_yun, size, count, fp);if (elements_written != count){printf("Failed to write data to file.\n");fclose(fp);return 1;}fclose(fp);printf("Data written successfully.\n");return 0;
}

- 🔖如何配合
CodeRuner插件使用: 

-
🔖如果不使用该插件的话,可以在终端命令行中输入
gcc .\Font.c命令按回车,然后执行.\a.exe命令回车。(前提是安装了C编译器并且添加到了系统环境变量中)

-
🔖运行成功后,会自动生成:
.bin文件。

-
🌿将生成的
.BIN文件拷贝到工程项目的data文件夹内。如果使用SD卡配合文件系统也是可以的。
📘LittleFS文件系统配置
- ⚡Arduino平台的所有的文件系统(SPIFFS/LittleFS/FatFS文件系统),仅针对ESP32,仅支持Arduino IDE
1.8.x.x版本下使用,Arduino IDE 2.x.x不支持!!!(ESP8266支持Arduino IDE 2.x.x版本) - 📍esp32文件系统上传插件下载地址:
https://github.com/lorol/arduino-esp32fs-plugin/releases - 📍ESP8266 littlefs文件系统下载地址:
https://github.com/earlephilhower/arduino-littlefs-upload/releases
📗LittleFS系统文件上传说明
- ✨首次上传程序时,需要注意,删除全部内容:

- 🔱上传LittleFS系统文件




🍭电子墨水屏注意事项
- 🔱支持局刷的屏幕,注意使用的时候不能一直用局刷对屏幕进行刷新,需要在做几次局刷之后,对屏幕进行一次全刷清屏。否则会造成屏幕显示效果异常。
- 🔱注意屏幕不能长时间上电,在屏幕不刷新的时候,要将屏幕设置成睡眠模式,或者进行断电处理。否则屏幕长时间保持高电压状态,会损坏膜片,无法修复。
- 🔱使用墨水屏的时候,建议刷新时间间隔至少是180s, 并且至少每24小时做一次刷新,如果长期不使用墨水屏的话,要将墨水屏刷白存放。(具体储存环境需求参考数据手册)
- 🔱屏幕进入睡眠模式之后,会忽略发送的图片数据,只有重新初始化才能正常刷新。
📘电子墨水屏显示内容说明
- 🌿对于黑白款的电子墨水屏,内容支持全刷和局刷。在使用过程中如果需要切换不同的刷新方式时,需要先停止使用SPI总线(
SPI.endTransaction()),推荐在刷完显示内容后,调用epd.Sleep()之后,停与屏幕的SPI通讯。再次刷新屏幕的时候,再进行一次初始化。
⛳图片更新方式
- 🌿通过Arduino IDE
1.8.x.x版本,只需选择上传littleFS进行文件上传即可,但是还是需要在当前工程目录下有data目录,才可更新显示内容素材。


- 图片数据文件上传后,进行复位,打开串口,如果文件系统初始化成功,读取到文件会打印出来:

📝主程序代码:
/*** @filename : epd1in54-demo.ino* @brief : 1.54inch e-paper display demo* @author : Yehui from Waveshare** Copyright (C) Waveshare September 5 2017** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documnetation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furished to do so, subject to the following conditions:** The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.** ESP32引脚定义:* BUSY -> 13 || RES -> 12 || DC -> 14 || CS -> 27 || SCL -> 18 || SDA -> 23 ||*黑字:COLORED 白色:UNCOLORED支持局刷的屏幕,注意使用的时候不能一直用局刷对屏幕进行刷新,需要在做几次局刷之后,对屏幕进行一次全刷清屏。否则会造成屏幕显示效果异常。
注意屏幕不能长时间上电,在屏幕不刷新的时候,要将屏幕设置成睡眠模式,或者进行断电处理。否则屏幕长时间保持高电压状态,会损坏膜片,无法修复。
使用墨水屏的时候,建议刷新时间间隔至少是180s, 并且至少每24小时做一次刷新,如果长期不使用墨水屏的话,要将墨水屏刷白存放。(具体储存环境需求参考数据手册)
屏幕进入睡眠模式之后,会忽略发送的图片数据,只有重新初始化才能正常刷新。*/#include <SPI.h>
#include "epd1in54.h"
#include "epdpaint.h"
#include "imagedata.h"#include <WiFi.h> // esp32开发板自带库
#include <time.h>
#include <sys/time.h>
#include <Ticker.h>
#include <LittleFS.h> // 如果使用LittleFS
#include "FS.h"
#include "esp_task_wdt.h"
//#include "RTClib.h" //by Adafruit库来实现软件RTC时间
#define TZ 8 // (utc+) TZ in hours
#define DST_MN 0 // use 60mn for summer time in some countries
#define TZ_MN ((TZ)*60)
#define TZ_SEC ((TZ)*3600)
#define daylightOffset_sec 3600#define LED_PIN 2
#define COLORED 0
#define UNCOLORED 1#define bitmapSize 5000#define PIC_TIME 75#define Conter_MAX 10 //图片最多显示数量const char* ssid = "MERCURY_D268G";//填写wifi信息
const char* password = "pba5ayzk";
uint8_t Conter=0;
String pic1[10];/*** Due to RAM not enough in Arduino UNO, a frame buffer is not allowed.* In this case, a smaller image buffer is allocated and you have to * update a partial display several times.* 1 byte = 8 pixels, therefore you have to set 8*N pixels at a time.*/
unsigned char image[1024];
Paint paint(image, 0, 0); // width should be the multiple of 8
Epd epd;Ticker Refresh; //图片切换
Ticker UPDATE; //时间显示
Ticker blinker;//unsigned long time_start_ms;
//unsigned long time_now_s;
const char * weekday[] = { "7", "1","2", "3","4", "5", "6"};
uint8_t blinkerPace = 1; //seconds
static bool TOGGLE = false;
//bool Refresh_flag = true;void listDir(fs::FS &fs, const char * dirname, uint8_t levels);
void Picture_Refresh();
void UP_Date_Data();void blink() {digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}void setup() {// put your setup code here, to run once:Serial.begin(9600);Serial.println();pinMode(LED_PIN, OUTPUT);WiFi.mode(WIFI_STA);WiFi.begin(ssid, password);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.println("Connecting to WiFi...");}// rtc.begin(DateTime(F(__DATE__), F(__TIME__)));//不联网,使用编译时间// rtc.adjust(DateTime(2024,3, 9, 23, 12, 10));Serial.println("Connected to WiFi");if (!LittleFS.begin()) { // LittleFS初始化Serial.println("LittleFS Mount Failed");} else {Serial.println("LittleFS Mounted Successfully");}configTime(TZ_SEC, daylightOffset_sec , "ntp3.aliyun.com", "ntp.ntsc.ac.cn", "1.cn.pool.ntp.org");//asia.pool.ntp.org//rtc.begin(DateTime(F(__DATE__), F(__TIME__)));Refresh.attach(PIC_TIME, Picture_Refresh); // 每隔60秒执行一次blinker.attach(blinkerPace, blink);//lut_partial_update if (epd.Init(lut_full_update) != 0) {Serial.print("e-Paper init failed");return;}epd.ClearFrameMemory(0xFF); // bit set = white, bit reset = blackepd.DisplayFrame();epd.ClearFrameMemory(0xff); // bit set = white, bit reset = blackepd.DisplayFrame();delay(2000);paint.SetRotate(ROTATE_0);//屏幕旋转 指定显示区域paint.Clear(UNCOLORED);//白色区域内容显示epd.SetFrameMemory(gImage_bird);//gImage_rp / gImage_birdepd.DisplayFrame();epd.SetFrameMemory(gImage_bird);epd.DisplayFrame();epd.Sleep();SPI.endTransaction();delay(2000);UPDATE.once(5,UP_Date_Data); //时间更新一次// epd.Reset();Serial.println("Display complete");listDir(LittleFS, "/", 1);// 禁用硬件看门狗esp_task_wdt_init(10 * 1000 * 1000,false); // 参数为0表示不启用看门狗}void loop() {// put your main code here, to run repeatedly:
}void listDir(fs::FS &fs, const char * dirname, uint8_t levels){Serial.printf("Listing directory: %s\r\n", dirname);File root = fs.open(dirname);if(!root){Serial.println("- failed to open directory");return;}if(!root.isDirectory()){Serial.println(" - not a directory");return;}File file = root.openNextFile();while(file){if(file.isDirectory()){Serial.print(" DIR : ");Serial.println(file.name());if(levels){listDir(fs, file.path(), levels -1);}} else {Serial.print(" FILE: ");pic1[Conter]=file.name();Serial.print( pic1[Conter].c_str());Serial.print("\tSIZE: ");Serial.println(file.size());Conter++;}file = root.openNextFile();Conter %=Conter_MAX;}
}void Picture_Refresh()
{// 读取文件内容到缓冲区static uint8_t buffer_imge[bitmapSize]; // 确保这里定义的缓冲区足够大以容纳字模数据static uint8_t CNT=0;File bitmapFile;String pic_name = "/"+ pic1[CNT++];Serial.println(pic_name.c_str());//打印当前要显示图片的名称bitmapFile = LittleFS.open(pic_name, "r");CNT %= Conter;if (!bitmapFile) {Serial.println("Failed to open bitmap file for reading!");return;}else
{
//Serial.print("Reading" );
uint16_t len = bitmapFile.size();
// uint32_t start = millis();while(len){size_t toRead = len;if(toRead > bitmapSize){toRead = bitmapSize;}bitmapFile.read(buffer_imge, toRead);len -= toRead;}// uint32_t end = millis() - start;
// Serial.printf("5000 bytes Reading in %u ms\r\n", end);epd.ClearFrameMemory(0xff); //刷白epd.DisplayFrame();epd.ClearFrameMemory(0xff); // bit set = white, bit reset = blackepd.DisplayFrame();delay(800);paint.SetRotate(ROTATE_0);epd.SetFrameMemory(buffer_imge);epd.DisplayFrame();epd.SetFrameMemory(buffer_imge);epd.DisplayFrame();epd.Sleep();SPI.endTransaction();}UPDATE.once(60,UP_Date_Data);//调用时间更新
}void UP_Date_Data()
{char buff[32];time_t now = time(nullptr);struct tm* timeinfo;timeinfo = localtime(&now); // 转换为本地时间strftime(buff, sizeof(buff), "%c", timeinfo);Serial.println(buff);Serial.printf(PSTR("20%02d-%02d-%02d %02d:%02d:%02d\r\n"),\(timeinfo->tm_year)%100,timeinfo->tm_mon+1,timeinfo->tm_mday,\timeinfo->tm_hour,timeinfo->tm_min,timeinfo->tm_sec);String Date_data =String(timeinfo->tm_mon+1)+"-"+String(timeinfo->tm_mday) +" "+String(timeinfo->tm_hour)+":"+String(timeinfo->tm_min)+" T"+weekday[timeinfo->tm_wday];String Time_data = String(timeinfo->tm_hour)+":"+String(timeinfo->tm_min);//"20"+String((timeinfo->tm_year)%100)+"-"+ +":"+String(timeinfo->tm_sec);Serial.println(Date_data);Serial.println(Time_data);delay(800);epd.ClearFrameMemory(0xFF); // bit set = white, bit reset = blackepd.DisplayFrame();epd.ClearFrameMemory(0xFF); // bit set = white, bit reset = blackepd.DisplayFrame();paint.SetRotate(ROTATE_0);paint.SetWidth(200);paint.SetHeight(26);paint.Clear(UNCOLORED);//白底paint.DrawStringAt(0, 3,Date_data.c_str(), &Font24, COLORED);//UNCOLORED:白色epd.SetFrameMemory(paint.GetImage(), 0, 174, paint.GetWidth(), paint.GetHeight()); epd.DisplayFrame();epd.SetFrameMemory(paint.GetImage(), 0, 174, paint.GetWidth(), paint.GetHeight()); epd.DisplayFrame();epd.Sleep();}
📘工程源码:
链接: https://pan.baidu.com/s/1jd7jHhtjmozOxTA38kuGsw?pwd=dngy
提取码: dngy
- 🔖此文章仅作为个人学习探索知识的总结,不作为他人或引用者的理论依据,由于学识所限,难免会出现错误或纰漏,欢迎大家指正。
相关文章:
Arduin ESP32+epaper(电子墨水屏)时钟相册制作教程
Arduin ESP32 epaper(电子墨水屏)时钟相册制作教程 🔖epaper(电子墨水屏)采用的是:合宙1.54“ 电子墨水屏(e-paper)📍相关篇《Arduino框架下ESP32/ESP8266合宙1.54“ 电子墨水屏(e-paper)驱动显…...
Django模型层(附带test环境)
Django模型层(附带test环境) 目录 Django模型层(附带test环境)连接数据库Django ORM在models.py中建表允许为空指定默认值数据库迁移命令 开启测试环境建表语句补充(更改默认表名)数据的增加时间数据的时区 多表数据的增加一对多多对多 数据的删除修改数据查询数据查询所有数据…...
(AliyunAIACP17)知识点:神经网络(深度学习)分析
摘要: 案,详细阐述了神经网络的实现步骤,并提供了相应的代码示例。此外,文章还涵盖了神经网络中的技巧与实践、性能优化与测试,以及常见问题与解答。最后,对神经网络在深度学习中的应用前景进行了展望。 …...
基于 HBase Phoenix 构建实时数仓(1)—— Hadoop HA 安装部署
目录 一、主机规划 二、环境准备 1. 启动 NTP 时钟同步 2. 修改 hosts 文件 3. 配置所有主机间 ssh 免密 4. 修改用户可打开文件数与进程数(可选) 三、安装 JDK 四、安装部署 Zookeeper 集群 1. 解压、配置环境变量 2. 创建配置文件 3. 创建新…...
XS2185:八通道PSE控制器产品
八通道PSE控制器产品-XS2185 芯片特性 八通道PSE 支持标准PD供电 支持非标PD供电 每个端口功率最大30W 12位端口电流监测 12位电源电压监测 支持直流负载断开检测 支持LED供电状态指示 支持过流保护 支持短路保护 Sifos基本测试通过 32-PIN…...
Selenium WebDriver API 中涉及的一些常用方法和类
Selenium WebDriver API 是 Selenium 提供的一组方法和类,用于控制浏览器和操作 Web 元素。这些 API 提供了丰富的功能,包括但不限于: 1. **查找元素**:通过不同的定位方式(如ID、Class Name、XPath等)在页…...
OJ_复数集合
题干 C实现 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <queue> #include <string> using namespace std;struct Complex {int re;int im;//构造函数Complex(int _re, int _im) {//注意参数名字必须不同re _re;im _im;} };//结构体不支…...
【学一点RISC-V】ACLINT(高级核心本地中断控制器)文档
RISCV架构 ACLINT文档 ACLINT原文档:https://github.com/riscv/riscv-aclint/blob/main/riscv-aclint.adoc 在这里进行了翻译以及校对,仅供参考,不正确的地方欢迎指出 1、介绍 【此 RISC-V ACLINT 规范定义了一组内存映射设备,这…...
grafana table合并查询
注:本文基于Grafana v9.2.8编写 1 问题 默认情况下table展示的是一个查询返回的多个field,但是我想要的数据在不同的metric上,比如我需要显示某个pod的读写IO,但是读和写这两个指标存在于两个不同的metirc,需要分别查…...
编程笔记 html5cssjs 007 文章排版 颜真卿《述张长史笔法十二意》
编程笔记 html5&css&js 007 文章排版 颜真卿《述张长史笔法十二意》 一、代码二、解释 这段代码定义了一个古文展示页面的结构和样式,同时本文内容也是书法爱好者的珍贵资料。 一、代码 <!DOCTYPE html> <html lang"zh-CN"> <hea…...
Yolov8模型用torch_pruning剪枝
目录 🚀🚀🚀订阅专栏,更新及时查看不迷路🚀🚀🚀 原理 遍历所有分组 高级剪枝器 🚀🚀🚀订阅专栏,更新及时查看不迷路🚀🚀…...
C++字符串操作【超详细】
零.前言 本文将重点围绕C的字符串来展开描述。 其中,对于C/C中字符串的一些区别也做出了回答,并对于C的(string库)进行了讲解,最后我们给出字符串的不同表达形式。 开发环境: VS2022 一.字符串常量跟字…...
Ps:画笔工具
画笔工具 Brush Tool是 Photoshop 中最常用的工具,可广泛地用于绘画与修饰工作之中。 快捷键:B ◆ ◆ ◆ 常用操作方法与技巧 1、熟练掌握画笔工具的操作对于使用其他工具也非常有益,因为 Photoshop 中许多与笔刷相关的工具有类似的选项和操…...
【鸿蒙 HarmonyOS 4.0】弹性布局(Flex)
一、介绍 弹性布局(Flex)提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。容器默认存在主轴与交叉轴,子元素默认沿主轴排列,子元素在主轴方向的尺寸称为主轴尺寸,在交叉轴方向的尺寸称为交叉轴尺寸…...
Java 客户端向服务端上传文件(TCP通信)
一、实验内容 编写一个客户端向服务端上传文件的程序,要求使用TCP通信的的知识,完成将本地机器输入的路径下的文件上传到D盘中名称为upload的文件夹中。并把客户端的IP地址加上count标识作为上传后文件的文件名,即IP(count&#…...
问题:前端获取long型数值精度丢失,后面几位都为0
文章目录 问题分析解决 问题 通过接口获取到的数据和 Postman 获取到的数据不一样,仔细看 data 的第17位之后 分析 该字段类型是long类型问题:前端接收到数据后,发现精度丢失,当返回的结果超过17位的时候,后面的全…...
Day26:安全开发-PHP应用模版引用Smarty渲染MVC模型数据联动RCE安全
目录 新闻列表 自写模版引用 Smarty模版引用 代码RCE安全测试 思维导图 PHP知识点: 功能:新闻列表,会员中心,资源下载,留言版,后台模块,模版引用,框架开发等 技术:输…...
LVS集群(Linux Virtual server)介绍----及LVS的NAT模式部署(一)
群集的含义 ●Cluster,集群、群集由多台主机构成,但对外只表现为一个整体,只提供访问入口(域名或IP地址),相当于一台大型计算机 问题: 互联网应用中,随着站点对硬件性能、响应速度、服务稳定性、数据可靠…...
海外媒体宣发套餐如何利用3种方式洞察市场-华媒舍
在当今数字化时代,媒体宣发成为了企业推广产品和品牌的重要手段之一。其中,7FT媒体宣发套餐是一种常用而有效的宣传方式。本文将介绍这种媒体宣发套餐,以及如何利用它来洞察市场。 一、关键概念 在深入讨论7FT媒体宣发套餐之前,让…...
开发知识点-Apache Struts2框架
Apache Struts2 介绍S2-001S2CVE-2023-22530 介绍 Apache Struts2是一个基于MVC(模型-视图-控制器)设计模式的Web应用程序框架,它是Apache旗下的一个开源项目,并且是Struts1的下一代产品。Struts2是在Struts1和WebWork的技术基础…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
