HonestQiao 发表于 2024-1-7 21:31:04

用树莓派PICO做一个电子墨水屏老黄历

本帖最后由 HonestQiao 于 2024-1-9 00:36 编辑

近期刚好上手了一块 Pervasive Displays 的电子墨水屏,显示的效果非常好,结合手头的树莓派Pico,制作了一款老黄历。

一、项目介绍:
这个项目,使用树莓派Pico做为主控,使用Pervasive Displays的红白黑三色电子墨水屏进行显示,并使用采集的老黄历数据,进行老黄历的呈现。

目前版本的效果:

二、项目设计:
要实现这个项目,需要依次实现以下:
1. 寻找老黄历数据和模版
2. 驱动EPD屏幕
3. 在EPD屏幕上显示中文
4. 老黄历界面设计
5. 代码编写
6. 整体优化

三、硬件:
1. 树莓派Pico


2. Pervasive Displays墨水屏+转接板




3. 实物连接


四、寻找老黄历数据和模版
讲过多番查找,最终使用 https://www.ibazi.cn/huangli 的信息,做为老黄历的数据和呈现模版。


五、驱动EPD屏幕
树莓派Pico可以在Arduino IDE环境开发,而Pervasive Displays又为 EPD提供了Arduino环境的Lib,所以就直接使用Arduino IDE开发了。
安装PDLS_EXT3_Basic_GLobal即可:


然后参考实例,了解具体的用法:



六. 在EPD屏幕上显示中文
PDLS_EXT3_Basic_GLobal默认只提供了英文字符字库,没有中文字库的接口。
经过相关官方了解,只有商业用户,才提供支持用户字库的版本,非商业用户只能自己研究。
经过一番探究,确定PDLS_EXT3_Basic_GLobal中显示字符,就使用的画点。
既然画点,那就好办了,弄一个汉字的点阵数据就可以。

参考其自带的Terminal8x12e.h,复制了一份Terminal8x16e.h。
再访问 https://www.23bei.com/tool/216.html 生成所需显示内容的字形数据:


上面的数据,拷贝到上述字形数据文件中,替换原有的一部分:


再代码中,使用如下代码即可显示:
myScreen.selectFont(Font_Terminal8x16);
    myScreen.gText(76, 329, "LMNOPQ", myColours.red);


当然,这只是一个简单的替换显示。
复杂一点的,还可以做一个转换函数,方便快捷进行调用。

七. 在EPD屏幕上呈现老黄历界面
要想在EPD上面,显示老黄历界面,是最麻烦的一步。
我的具体做法如下:
1. 在图形软件中,新建一个和屏幕像素同等大小的图像
2. 然后截图老黄历界面并调整到实际大小
3. 在界面上拉线,获取每个位置的坐标


最终,得到一系列坐标定义:
顶部:
方块:
5,4 83,64 淡红
字:
95,11 今天 黑色
95,35 农历 淡红
95,54 葵卯年 黑色

37,25 7 白色

线框:灰色
x=70,170
y=77,96,158
0,77,w=240-1,h=158-77 黑色
70,77,w=100,h=158-77 黑色


中间两边:
圆:
37,110,r=10   红色
204,110.r=10灰色

字:
37,110 宜   白色
204,110 忌白色

37,82 无节日 黑色
204,82 无节气 黑色

字:
37,126 祭祀 黑色
204,126 畋猎 黑色
37,141 嫁娶 黑色
204,141 求医 黑色

中间中间:
圆:
117,120,r=30 灰色
117,120,r=20 暗红
117,120,r=20 灰色
117,120,r=10 白色

线:
117,120-5 - 117,120+5 红色

字:
70,77 喜 白色
170,77 贵 白色
70,158 财 白色
170,158 生 白色

块:
70,77-76,83 喜 暗红
170,77-176,83 贵 红色
70,158-76,164 财 淡红
170,158-176,164 生 灰色

字:
80,77 西北 黑色
180,77 东北 黑色
80,158 正东 黑色
180,158 东南 黑色



下中:

0,158 - 0,181, w=240,h=181-158 灰色
0,181 - 0,210, w=240,h=210-181 灰色
0,210 - 0,270, w=240, h=270-181 灰色
70,181, 170,210, w=100,h=270-181 灰色
70,254, 170,210, w=100,h=270-210 灰色

字:
54,166 年 红色
99,166 月 红色
146,166 日 红色

9,185   冲 红色
102,185 煞 红色
176,185 天干 红色

14,213 今日胎神 红色
98,213 今日吉时
182.213 今日八字

74,257 十二神 红色
124,257 二八宿 红色

八、代码编写
上一步得到了坐标数据,下面就是实际的代码编写了:
// Screen
#include "PDLS_EXT3_Basic_Global.h"

// SDK
// #include <Arduino.h>
#include "hV_HAL_Peripherals.h"

// Include application, user and local libraries
// #include <SPI.h>

// Configuration
#include "hV_Configuration.h"

// Set parameters

// Define structures and classes

// Define variables and constants
Screen_EPD_EXT3 myScreen(eScreen_EPD_EXT3_370, boardRaspberryPiPico_RP2040);

// Prototypes

// Utilities
///
/// @brief Wait with countdown
/// @param second duration, s
///
void wait(uint8_t second)
{
    for (uint8_t i = second; i > 0; i--)
    {
      Serial.print(formatString(" > %i\r", i));
      delay(1000);
    }
    Serial.print("         \r");
}

// Functions
///
/// @brief DisplayLHL
///
void DisplayLHL()
{
    myScreen.setOrientation(2);

    myScreen.setPenSolid(true);
    myScreen.dRectangle(5, 4, 83-5, 64-4, myColours.lightRed);

    myScreen.selectFont(Font_Terminal16x24);
    myScreen.gText(37, 25, "7", myColours.white);

    myScreen.selectFont(Font_Terminal8x12);
    myScreen.gText(95, 11, "Today, 2024-1, Sunday", myColours.black);
    myScreen.gText(95, 35, "NongLi 11-26", myColours.red);
    myScreen.gText(95, 54, "Tu Yi-Chou Geng-Wu", myColours.black);

    myScreen.setPenSolid(false);
    myScreen.dRectangle(0, 77, 240-1, 158-77, myColours.grey);
    myScreen.dRectangle(70, 77, 170-70, 158-77, myColours.grey);

    myScreen.setPenSolid(true);
    myScreen.circle(37, 110, 10, myColours.red);
    myScreen.circle(204, 110, 10, myColours.grey);

    myScreen.gText(37-4, 110-4, "Y", myColours.white);
    myScreen.gText(204-4, 110-4, "N", myColours.white);

    myScreen.gText(37-20, 82, "No Jie", myColours.black);
    myScreen.gText(204-20, 82, "No Qi", myColours.black);

    myScreen.gText(37-20-8, 126, "Ji-Shi", myColours.black);
    myScreen.gText(204-20-8, 126, "Lie-Shou", myColours.black);
    myScreen.gText(37-20-8, 141, "Jia-Qu", myColours.black);
    myScreen.gText(204-20-8, 141, "Qiu-Yi", myColours.black);

    myScreen.circle(117, 120, 30, myColours.grey);
    myScreen.circle(117, 120, 25, myColours.darkRed);
    myScreen.circle(117, 120, 20, myColours.grey);
    myScreen.circle(117, 120, 10, myColours.white);

    myScreen.circle(117, 120, 2, myColours.darkRed);
    myScreen.dLine(117, 120-10, 0, 10, myColours.black);
    myScreen.dLine(117, 120, 0, 10, myColours.red);

    myScreen.dRectangle(70, 77, 16, 16, myColours.red);
    myScreen.dRectangle(170-16, 77, 16, 16, myColours.darkRed);
    myScreen.dRectangle(70, 158-16, 16, 16, myColours.lightRed);
    myScreen.dRectangle(170-16, 158-16, 16, 16, myColours.grey);

    myScreen.gText(70+4, 77+4, "J", myColours.white);
    myScreen.gText(170-16+4, 77+4, "E", myColours.white);
    myScreen.gText(70+4, 158-16+4, "P", myColours.black);
    myScreen.gText(170-16+4, 158-16+4, "L", myColours.black);

    myScreen.gText(80+8, 77+2, "WN", myColours.black);
    myScreen.gText(180-8-32-8, 77+2, "EN", myColours.black);
    myScreen.gText(80+8, 158-8-4, "CE", myColours.black);
    myScreen.gText(180-8-32-8, 158-8-4, "ES", myColours.black);


    myScreen.setPenSolid(false);
    myScreen.dRectangle(0, 158, 240-1, 181-158, myColours.grey);
    myScreen.dRectangle(0, 181, 240-1, 210-181, myColours.grey);
    myScreen.dRectangle(0, 210, 240-1, 270-210, myColours.grey);
    myScreen.dRectangle(70, 181, 170-70, 270-181, myColours.grey);
    myScreen.dRectangle(70, 254, 170-70, 270-254, myColours.grey);

    myScreen.setPenSolid(true);
    myScreen.gText(54, 166, "Y:", myColours.red);
    myScreen.gText(99, 166, "M:", myColours.red);
    myScreen.gText(146, 166, "D:", myColours.red);

    myScreen.gText(9, 185, "Chong:", myColours.red);
    myScreen.gText(102, 185, "Sha:", myColours.red);
    myScreen.gText(176, 185, "Tian:", myColours.red);

    myScreen.gText(14-8, 213, "Today-B:", myColours.red);
    myScreen.gText(98-16, 213, "Today-T:", myColours.red);
    myScreen.gText(182-8, 213, "Today-8:", myColours.red);

    myScreen.selectFont(Font_Terminal6x8);
    myScreen.gText(74, 257, "12:", myColours.red);
    myScreen.gText(124, 257, "28:", myColours.red);


    myScreen.selectFont(Font_Terminal8x16);
    myScreen.gText(76, 329, "LMNOPQ", myColours.red);

    myScreen.flush();
}

// Add setup code
///
/// @brief Setup
///
void setup()
{
    // Start
    Serial.begin(115200);
    delay(2000);

    Serial.println("begin... ");
    myScreen.begin();
    Serial.println(formatString("%s %ix%i", myScreen.WhoAmI().c_str(), myScreen.screenSizeX(), myScreen.screenSizeY()));

    Serial.println("Colours... ");
    myScreen.clear();
    DisplayLHL();
    wait(8);

    Serial.println("=== ");
    Serial.println();
}

// Add loop code
///
/// @brief Loop, empty
///
void loop()
{
    delay(1000);
}


上述代码,就是使用了画线、画圆、画方块、画方框、显示文字、设置字体等调用,来进行具体的实现。

九、实现效果
上面的代码编译执行后,效果如下:





因为最近比较忙,显示的还只是英文字符,还没有完全换为中文字符。
另外,老黄历的数据是采集的,不能公开,所以数据还在整合中,将尽快整合完成。


十、整体优化
之前的版本,还是显示的英文字符,而且内容,都还是在代码里面写死的。
每一次修改,都需要重新编译,然后下载,然后启动运行,耗时耗力不好调整。

那么这一版本,就做了整体优化。
具体的优化细节如下:
1. 要显示的信息,提取为json文件,要改变现实效果,只需要修改json配置即可
2. json配置文件,通过web提供
3. PicoW添加了联网读取解析json的功能,并根据json的数据,进行实际的显示绘制操作
4. PicoW添加了按BOOTSEL按键,重新读取json的功能

1. json配置文件如下:
{
    "name": "laohuangli",
    "orientation": 2,
    "version": 1.0,
    "offset" : ,
    "data_demo" :[
      {"type": "comment", "value":"这是一行注释"},
      {"type": "setOrientation", "value":2},
      {"type": "setOffset", "x":0, "y":0},
      {"type": "setPenSolid", "value":true},

      {"type": "selectFont", "value":"16x24"},
      {"type": "setFontSolid", "value":true},
      {"type": "gText",      "x":37, "y":25, "text":"7", "color":"white"},

      {"type": "point",       "x":5, "y":4, "color":"white"},
      {"type": "line",       "x":5, "y":4, "x1":5, "y1":4, "color":"white"},
      {"type": "rectangle",       "x":5, "y":4, "x1":5, "y1":4, "color":"white"},
      {"type": "circle",       "x":5, "y":4, "r":10, "color":"white"},
      {"type": "triangle",       "x":5, "y":4, "x1":5, "y1":4, "x2":5, "y2":4, "color":"white"},

      {"type": "dLine",       "x":5, "y":4, "w":78, "h":60, "color":"white"},
      {"type": "dRectangle", "x":5, "y":4, "w":78, "h":60, "color":"white"}
    ],
    "data" :[
      {"type":"comment", "value":"设置方向"},
      {"type": "setOrientation", "value":2},

      {"type":"comment", "value":"设置笔触"},
      {"type": "setPenSolid", "value":true},
      {"type": "setFontSolid", "value":false},

      {"type":"comment", "value":"标题"},
      {"type": "selectFont", "value":"8x16"},
      {"type": "gText", "x":96, "y":5, "text":"老黄历", "color":"red"},

      {"type": "dLine", "x":0, "y":30, "w":240, "h":0, "color":"grey"},

      {"type": "setOffset", "x":0, "y":35},

      {"type":"comment", "value":"当日日期"},
      {"type": "dRectangle", "x":5, "y":4, "w":78, "h":70, "color":"lightRed"},
      {"type": "selectFont", "value":"16x24"},
      {"type": "gText",      "x":37, "y":30, "text":"8", "color":"white"},

      {"type":"comment", "value":"当日日历信息"},
      {"type": "selectFont", "value":"8x16"},
      {"type": "gText",      "x":95, "y":10, "text":"2024年1月 星期一", "color":"black"},
      {"type": "gText",      "x":95, "y":25, "text":"农历十一月廿七", "color":"black"},
      {"type": "gText",      "x":95, "y":40, "text":"癸卯年 兔年", "color":"black"},
      {"type": "gText",      "x":95, "y":55, "text":"乙丑月 辛未日", "color":"black"},

      {"type": "setOffset", "x":0, "y":50},

      {"type":"comment", "value":"中间方框"},
      {"type": "setPenSolid", "value":false},
      {"type": "dRectangle", "x":0, "y":77, "w":239, "h":81, "color":"grey"},
      {"type": "dRectangle", "x":70, "y":77, "w":100, "h":81, "color":"grey"},

      {"type":"comment", "value":"中间方框内两边圆形"},
      {"type": "setPenSolid", "value":true},
      {"type": "circle", "x":37, "y":110, "r":10, "color":"red"},
      {"type": "circle", "x":204, "y":110, "r":10, "color":"grey"},
      {"type": "selectFont", "value":"8x16"},
      {"type": "gText", "x":31, "y":103, "text":"宜", "color":"white"},
      {"type": "gText", "x":198, "y":103, "text":"忌", "color":"black"},

      {"type":"comment", "value":"中间方框内上部文字"},
      {"type": "gText", "x":17, "y":82, "text":"无节日", "color":"black"},
      {"type": "gText", "x":184, "y":82, "text":"无节气", "color":"black"},

      {"type":"comment", "value":"中间方框内下部文字"},
      {"type": "gText", "x":9, "y":126, "text":"祭祀 解除", "color":"black"},
      {"type": "gText", "x":176, "y":126, "text":"祈福 冠带", "color":"black"},
      {"type": "gText", "x":9, "y":141, "text":"破屋坏垣", "color":"black"},
      {"type": "gText", "x":176, "y":141, "text":"嫁娶 进人口", "color":"black"},


      {"type":"comment", "value":"中间方框内中部圆盘"},
      {"type": "circle", "x":117, "y":120, "r":30, "color":"grey"},
      {"type": "circle", "x":117, "y":120, "r":25, "color":"darkRed"},
      {"type": "circle", "x":117, "y":120, "r":20, "color":"grey"},
      {"type": "circle", "x":117, "y":120, "r":10, "color":"white"},

      {"type":"comment", "value":"中间方框内中部圆盘内部指南针"},
      {"type": "circle", "x":117, "y":120, "r":2, "color":"darkRed"},
      {"type": "dLine", "x":117, "y":110, "w":0, "h":10, "color":"black"},
      {"type": "dLine", "x":117, "y":120, "w":0, "h":10, "color":"red"},

      {"type":"comment", "value":"中间方框内中部圆盘四边方框和文字"},
      {"type": "dRectangle", "x":70, "y":77, "w":16, "h":16, "color":"red"},
      {"type": "dRectangle", "x":154, "y":77, "w":16, "h":16, "color":"darkRed"},
      {"type": "dRectangle", "x":70, "y":142, "w":16, "h":16, "color":"lightRed"},
      {"type": "dRectangle", "x":154, "y":142, "w":16, "h":16, "color":"grey"},
      {"type": "gText", "x":72, "y":77, "text":"喜", "color":"white"},
      {"type": "gText", "x":156, "y":77, "text":"贵", "color":"white"},
      {"type": "gText", "x":72, "y":142, "text":"财", "color":"black"},
      {"type": "gText", "x":156, "y":142, "text":"生", "color":"black"},

      {"type":"comment", "value":"中间方框内中部圆盘四边方框旁边文字"},
      {"type": "gText", "x":88, "y":77, "text":"西南", "color":"black"},
      {"type": "gText", "x":130, "y":77, "text":"东北", "color":"black"},
      {"type": "gText", "x":88, "y":142, "text":"正东", "color":"black"},
      {"type": "gText", "x":130, "y":142, "text":"东南", "color":"black"},

      {"type": "setOffset", "x":0, "y":65},

      {"type":"comment", "value":"下部方框"},
      {"type": "setPenSolid", "value":false},
      {"type": "dRectangle", "x":0, "y":158, "w":239, "h":23, "color":"grey"},
      {"type": "dRectangle", "x":0, "y":181, "w":239, "h":49, "color":"grey"},
      {"type": "dRectangle", "x":0, "y":230, "w":239, "h":90, "color":"grey"},
      {"type": "dRectangle", "x":70, "y":181, "w":100, "h":139, "color":"grey"},
      {"type": "dRectangle", "x":70, "y":284, "w":100, "h":36, "color":"grey"},

      {"type":"comment", "value":"下部方框文字:红色"},
      {"type":"comment", "value":"下部方框顶部文字"},
      {"type": "gText", "x":54, "y":162, "text":"年", "color":"red"},
      {"type": "gText", "x":99, "y":162, "text":"月", "color":"red"},
      {"type": "gText", "x":146, "y":162, "text":"日", "color":"red"},

      {"type":"comment", "value":"下部方框中间文字"},
      {"type": "gText", "x":9, "y":185, "text":"冲", "color":"red"},
      {"type": "gText", "x":9, "y":205, "text":"肖", "color":"red"},
      {"type": "gText", "x":95, "y":200, "text":"煞", "color":"red"},
      {"type": "gText", "x":176, "y":185, "text":"天干", "color":"red"},
      {"type": "gText", "x":176, "y":205, "text":"地支", "color":"red"},

      {"type":"comment", "value":"下部方框下部文字"},
      {"type": "gText", "x":6, "y":233, "text":"今日胎神", "color":"red"},
      {"type": "gText", "x":82, "y":233, "text":"今日吉时", "color":"red"},
      {"type": "gText", "x":174, "y":233, "text":"今日八字", "color":"red"},

      {"type":"comment", "value":"下部方框下部中间文字"},
      {"type": "gText", "x":74, "y":287, "text":"十二神", "color":"red"},
      {"type": "gText", "x":124, "y":287, "text":"二八宿", "color":"red"},

      {"type":"comment", "value":"下部方框文字:黑色"},
      {"type":"comment", "value":"下部方框顶部文字"},
      {"type": "gText", "x":70, "y":162, "text":"金箔", "color":"black"},
      {"type": "gText", "x":115, "y":162, "text":"海中", "color":"black"},
      {"type": "gText", "x":162, "y":162, "text":"路旁", "color":"black"},

      {"type":"comment", "value":"下部方框中间文字"},
      {"type": "gText", "x":24, "y":190, "text":"羊日冲牛", "color":"black"},
      {"type": "gText", "x":24, "y":210, "text":"乙丑", "color":"black"},
      {"type": "gText", "x":120, "y":200, "text":"煞西", "color":"black"},
      {"type": "gText", "x":201, "y":190, "text":"辛金", "color":"black"},
      {"type": "gText", "x":201, "y":210, "text":"未土", "color":"black"},

      {"type":"comment", "value":"下部方框下部文字"},
      {"type": "gText", "x":6, "y":253, "text":"厨灶厕外", "color":"black"},
      {"type": "gText", "x":6, "y":273, "text":"西南", "color":"black"},

      {"type": "gText", "x":82, "y":253, "text":"庚寅辛卯癸巳", "color":"black"},
      {"type": "gText", "x":82, "y":269, "text":"丙申戊戌己亥 ", "color":"black"},

      {"type": "gText", "x":174, "y":253, "text":"癸卯乙丑", "color":"black"},
      {"type": "gText", "x":174, "y":273, "text":"辛未戊子", "color":"black"},

      {"type":"comment", "value":"下部方框下部中间文字"},
      {"type": "gText", "x":95, "y":301, "text":"破", "color":"black"},
      {"type": "gText", "x":145, "y":301, "text":"张", "color":"black"}
    ]
}

在上述文件中,定义了在老黄历界面中,所有需要的调用操作,data_demo中给出了所有可用的定义。
上面这个json文件,也可以用程序来自动生成,这样子每次访问网址的时候,可以根据当前日期,自动选择对应日期的数据输出json给PicoW使用。

2.json配置文件,通过web提供
在这里,使用了python提供简单web服务:
python -m http.server 9080
3. arduino中PicoW的代码:
// #include <WiFi.h>
// #include <HTTPClient.h>

// Screen
#include "PDLS_EXT3_Basic_Global.h"

// SDK
// #include <Arduino.h>
#include "hV_HAL_Peripherals.h"

// Include application, user and local libraries
// #include <SPI.h>

// Configuration
#include "hV_Configuration.h"

#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// Define structures and classes

// Define variables and constants
Screen_EPD_EXT3 myScreen(eScreen_EPD_EXT3_370, boardRaspberryPiPico_RP2040);

bool status = false;

// Prototypes

// Utilities
///
/// @brief Wait with countdown
/// @param second duration, s
///
void wait(uint8_t second) {
for (uint8_t i = second; i > 0; i--) {
    Serial.print(formatString(" > %i\r", i));
    delay(1000);
}
Serial.print("         \r");
}

#ifndef STASSID
#define STASSID "********"
#define STAPSK "********"
#endif

const char *ssid = STASSID;
const char *pass = STAPSK;

WiFiMulti WiFiMulti;

uint8_t get_font(String font_name) {
if (font_name == "6x8") {
    return Font_Terminal6x8;
} else if (font_name == "8x12") {
    return Font_Terminal8x12;
} else if (font_name == "8x16") {
    return Font_Terminal8x16;
} else if (font_name == "12x16") {
    return Font_Terminal12x16;
} else if (font_name == "16x24") {
    return Font_Terminal16x24;
} else {
    Serial.println(formatString("font_name is invalid: %s", font_name));
    return Font_Terminal6x8;
}
}

uint16_t get_color(String color_name) {
if (color_name == "black") {
    return myColours.black;
} else if (color_name == "white") {
    return myColours.white;
} else if (color_name == "grey") {
    return myColours.grey;
} else if (color_name == "red") {
    return myColours.red;
} else if (color_name == "lightRed") {
    return myColours.lightRed;
} else if (color_name == "darkRed") {
    return myColours.darkRed;
} else {
    Serial.println(formatString("color_name is invalid: %s", color_name));
    return myColours.black;
}
}

void request_json_and_display() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {

    HTTPClient http;

    Serial.print(" begin...\n");
    if (http.begin("http://192.168.1.15:9080/laohuangli.json")) {// HTTP
      Serial.print(" GET...\n");
      // start connection and send HTTP header
      int httpCode = http.GET();

      // httpCode will be negative on error
      if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      Serial.printf(" GET... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = http.getString();
          Serial.println(payload);

          // Allocate the JSON document
          JsonDocument doc;

          // Parse JSON object
          DeserializationError error = deserializeJson(doc, payload);
          if (error) {
            Serial.print(F("deserializeJson() failed: "));
            Serial.println(error.f_str());
            return;
          } else {
            // 解析数据
            int x_offset = 0;
            int y_offset = 0;
            int orientation = 2;
            
            x_offset = doc["offset"].as<long>();
            y_offset = doc["offset"].as<long>();
            orientation = doc["orientation"].as<long>();

            Serial.println(F("Response:"));
            Serial.println(formatString("name: %s", doc["name"].as<const char *>()));
            Serial.println(formatString("orientation: %d", orientation));
            Serial.println(formatString("offset: (%d,%d)", x_offset, y_offset));
            Serial.println(formatString("version: %0.2f", doc["version"].as<float>()));
            Serial.println(formatString("data size: %d", doc["data"].size()));

            // 设置默认方向
            myScreen.setOrientation(orientation);

            for (int i = 0; i < doc["data"].size(); i++) {
            String type = doc["data"]["type"];
            if (type == "comment") {
                // 注释
                String comment = doc["data"]["value"];
                Serial.println(formatString("comment: %s", comment.c_str()));
            } else if (type == "setOrientation") {
                // 设置方向
                int value = doc["data"]["value"].as<long>();
                Serial.println(formatString("setOrientation: %d", value));
                myScreen.setOrientation(value);
            } else if (type == "setOffset") {
                // 设置方向
                x_offset = doc["data"]["x"].as<long>();
                y_offset = doc["data"]["y"].as<long>();
                Serial.println(formatString("setOffset: (x, y)=(%d, %d)", x_offset, y_offset));
            } else if (type == "setPenSolid") {
                // 设置落笔抬笔
                int value = doc["data"]["value"].as<bool>();
                Serial.println(formatString("setPenSolid: %d", value));
                myScreen.setPenSolid(value);
            } else if (type == "selectFont") {
                // 设置字体
                String font_name = doc["data"]["value"];
                Serial.println(formatString("selectFont: %s", font_name.c_str()));
                uint8_t font = get_font(font_name);
                myScreen.selectFont(font);
            } else if (type == "setFontSolid") {
                // 设置字体落笔抬笔
                int value = doc["data"]["value"].as<bool>();
                Serial.println(formatString("setFontSolid: %d", value));
                myScreen.setFontSolid(value);
            } else if (type == "gText") {
                // 显示文本
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                String text = doc["data"]["text"];
                String color_name = doc["data"]["color"];
                Serial.println(formatString("gText: (x,y)=(%d,%d) text=%s color=%s", x, y, text.c_str(), color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.gText(x + x_offset, y + y_offset, text, color);
            } else if (type == "point") {
                // 画点
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                String color_name = doc["data"]["color"];
                Serial.println(formatString("point: (x,y)=(%d,%d) color=%s", x, y, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.point(x + x_offset, y + y_offset, color);
            } else if (type == "line") {
                // 画线
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                int x1 = doc["data"]["x1"].as<long>();
                int y1 = doc["data"]["y1"].as<long>();
                String color_name = doc["data"]["color"];
                Serial.println(formatString("line: (x,y)=(%d,%d) (x1,y1)=(%d,%d) color=%s", x, y, x1, y1, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.line(x + x_offset, y + y_offset, x1+ x_offset, y1 + y_offset, color);
            } else if (type == "rectangle") {
                // 画矩形
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                int x1 = doc["data"]["x1"].as<long>();
                int y1 = doc["data"]["y1"].as<long>();
                String color_name = doc["data"]["color"];
                Serial.println(formatString("rectangle: (x,y)=(%d,%d) (x1,y1)=(%d,%d) color=%s", x, y, x1, y1, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.rectangle(x + x_offset, y + y_offset, x1 + x_offset, y1 + y_offset, color);
            } else if (type == "circle") {
                // 画圆
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                int r = doc["data"]["r"].as<long>();
                String color_name = doc["data"]["color"];
                Serial.println(formatString("circle: (x,y)=(%d,%d) r=%d color=%s", x, y, r, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.circle(x + x_offset, y + y_offset, r, color);
            } else if (type == "triangle") {
                // 画三角形
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                int x1 = doc["data"]["x1"].as<long>();
                int y1 = doc["data"]["y1"].as<long>();
                int x2 = doc["data"]["x2"].as<long>();
                int y2 = doc["data"]["y2"].as<long>();
                String color_name = doc["data"]["color"];
                Serial.println(formatString("triangle: (x,y)=(%d,%d) (x1,y1)=(%d,%d) (x2,y2)=(%d,%d) color=%s", x, y, x1, y1, x2, y2, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.triangle(x + x_offset, y + y_offset, x1 + x_offset, y1 + y_offset, x2 + x_offset, y2 + y_offset, color);
            } else if (type == "dLine") {
                // 画线(向量)
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                int w = doc["data"]["w"].as<long>();
                int h = doc["data"]["h"].as<long>();
                String color_name = doc["data"]["color"];
                Serial.println(formatString("dLine: (x,y)=(%d,%d) (w,h)=(%d,%d) color=%s", x, y, w, h, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.dLine(x + x_offset, y + y_offset, w, h, color);
            } else if (type == "dRectangle") {
                // 画矩形(向量)
                int x = doc["data"]["x"].as<long>();
                int y = doc["data"]["y"].as<long>();
                int w = doc["data"]["w"].as<long>();
                int h = doc["data"]["h"].as<long>();
                String color_name = doc["data"]["color"];
                Serial.println(formatString("dRectangle: (x,y)=(%d,%d) (w,h)=(%d,%d) color=%s", x, y, w, h, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.dRectangle(x + x_offset, y + y_offset, w, h, color);
            } else {
                Serial.println(formatString("type: %s", type));
            }
            }
            myScreen.flush();
          }
      }
      } else {
      Serial.printf(" GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
      }

      http.end();
    } else {
      Serial.println("[HTTP} Unable to connect");
    }
}
}

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
// Serial.setDebugOutput(true);

Serial.println();
Serial.println();
Serial.println();

for (uint8_t t = 4; t > 0; t--) {
    Serial.printf(" WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
}

status = true;
digitalWrite(LED_BUILTIN, status);
WiFiMulti.addAP(ssid, pass);
Serial.print("WiFi is connecting: ");
while ((WiFiMulti.run() != WL_CONNECTED)) {
    Serial.print(".");
    Serial.flush();
    delay(100);
    digitalWrite(LED_BUILTIN, status);
    status = !status;
}
Serial.println(" OK!");
status = false;
digitalWrite(LED_BUILTIN, status);

Serial.println("begin... ");
myScreen.begin();
Serial.println(formatString("%s %ix%i", myScreen.WhoAmI().c_str(), myScreen.screenSizeX(), myScreen.screenSizeY()));

Serial.println("request json and display... ");
status = true;
digitalWrite(LED_BUILTIN, status);
myScreen.clear();
request_json_and_display();
status = false;
digitalWrite(LED_BUILTIN, status);

Serial.println("=== ");
Serial.println();
}

void loop() {
if (BOOTSEL) {
    status = true;
    digitalWrite(LED_BUILTIN, status);

    Serial.printf("\a\aYou pressed BOOTSEL!\n");
    // Wait for BOOTSEL to be released
    while (BOOTSEL) {
      delay(1);
    }
    status = false;
    digitalWrite(LED_BUILTIN, status);
    delay(500);

    status = true;
    digitalWrite(LED_BUILTIN, status);
    myScreen.clear();
    request_json_and_display();
    status = false;
    digitalWrite(LED_BUILTIN, status);
}
delay(1000);
}


4. 最终效果:


如果觉得显示的效果不好,例如位置不对等,可以在json配置文件中修改,然后按一下BOOTSEL按键,就能自动调用最新版本,然后刷新显示了。

十一、后续优化
后续的工作,就要简单多了,主要是如下的工作:
1. 睡眠模式:显示完成后,直接进入睡眠模式,定时唤醒。因为使用的是电子墨水屏,不供电也能继续显示,所以显示完,就可以睡眠了。
2. python服务端提供每日老黄历json文件




aramy 发表于 2024-1-8 10:09:28

赞!跟着乔老师买了墨水屏,等到了就跟着学习玩啦!

HonestQiao 发表于 2024-1-8 22:37:58

aramy 发表于 2024-1-8 10:09
赞!跟着乔老师买了墨水屏,等到了就跟着学习玩啦!

好好学习,天天向上!

_深蓝_ 发表于 2024-1-9 08:45:53

乔帮主的文章太赞了。虽然还没有接触墨水屏,看到这篇文章,我都想跃跃欲试了。既高大上又有难度,很有挑战的意义。
页: [1]
查看完整版本: 用树莓派PICO做一个电子墨水屏老黄历