🛠图片取模制作

  • 🔧制作软件:Image2Lcd
  • 🌿图片尺寸要求:200X200像素点

esp32 墨水屏_数据库

  • 📍图片素材获取源: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;
}

esp32 墨水屏_#define_02

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

esp32 墨水屏_数据_03

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

  • 🔖运行成功后,会自动生成:.bin文件。
  • esp32 墨水屏_#include_05

  • 🌿将生成的.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系统文件上传说明

  • ✨首次上传程序时,需要注意,删除全部内容:

esp32 墨水屏_esp32 墨水屏_06

  • 🔱上传LittleFS系统文件

esp32 墨水屏_数据库_07

  • ✨读取数据时间不固定,推荐将单片机的看门狗定时器关掉,对于esp32默认是开启的,如果不关掉和设置更长时间。如果读取时间超时了,就到会导致复位。

esp32 墨水屏_数据_08

🍭电子墨水屏注意事项

  • 🔱支持局刷的屏幕,注意使用的时候不能一直用局刷对屏幕进行刷新,需要在做几次局刷之后,对屏幕进行一次全刷清屏。否则会造成屏幕显示效果异常。
  • 🔱注意屏幕不能长时间上电,在屏幕不刷新的时候,要将屏幕设置成睡眠模式,或者进行断电处理。否则屏幕长时间保持高电压状态,会损坏膜片,无法修复。
  • 🔱使用墨水屏的时候,建议刷新时间间隔至少是180s, 并且至少每24小时做一次刷新,如果长期不使用墨水屏的话,要将墨水屏刷白存放。(具体储存环境需求参考数据手册)
  • 🔱屏幕进入睡眠模式之后,会忽略发送的图片数据,只有重新初始化才能正常刷新。

📘电子墨水屏显示内容说明

  • 🌿对于黑白款的电子墨水屏,内容支持全刷和局刷。在使用过程中如果需要切换不同的刷新方式时,需要先停止使用SPI总线(SPI.endTransaction()),推荐在刷完显示内容后,调用epd.Sleep()之后,停与屏幕的SPI通讯。再次刷新屏幕的时候,再进行一次初始化。
  • 🌿对于图片内容显示,仅限黑白像素图片,1.54"墨水屏200X200像素的图片大小5KB,刷新非常慢,刷新率需要根据电子屏特性要求,设置刷新时间,请参考上面的注意事项。

⛳图片更新方式

  • 🌿通过Arduino IDE 1.8.x.x版本,只需选择上传littleFS进行文件上传即可,但是还是需要在当前工程目录下有data目录,才可更新显示内容素材。

esp32 墨水屏_esp32 墨水屏_09


esp32 墨水屏_数据_10

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

📝主程序代码:

/**
 *  @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 = black
  epd.DisplayFrame();
  epd.ClearFrameMemory(0xff);   // bit set = white, bit reset = black
  epd.DisplayFrame();

  delay(2000);
 paint.SetRotate(ROTATE_0);//屏幕旋转 指定显示区域
 paint.Clear(UNCOLORED);//白色区域内容显示

  epd.SetFrameMemory(gImage_bird);//gImage_rp / gImage_bird
  epd.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 = black
  epd.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 = black
  epd.DisplayFrame();
  epd.ClearFrameMemory(0xFF);   // bit set = white, bit reset = black
  epd.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();

}