3994浏览
查看: 3994|回复: 3

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

[复制链接]
本帖最后由 HonestQiao 于 2024-1-9 00:36 编辑

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

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

目前版本的效果:用树莓派PICO做一个电子墨水屏老黄历图13

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

三、硬件:
1. 树莓派Pico
用树莓派PICO做一个电子墨水屏老黄历图1

2. Pervasive Displays墨水屏+转接板
用树莓派PICO做一个电子墨水屏老黄历图2
用树莓派PICO做一个电子墨水屏老黄历图3


3. 实物连接
用树莓派PICO做一个电子墨水屏老黄历图4

四、寻找老黄历数据和模版
讲过多番查找,最终使用 https://www.ibazi.cn/huangli 的信息,做为老黄历的数据和呈现模版。
用树莓派PICO做一个电子墨水屏老黄历图5

五、驱动EPD屏幕
树莓派Pico可以在Arduino IDE环境开发,而Pervasive Displays又为 EPD提供了Arduino环境的Lib,所以就直接使用Arduino IDE开发了。
安装PDLS_EXT3_Basic_GLobal即可:
用树莓派PICO做一个电子墨水屏老黄历图6

然后参考实例,了解具体的用法:
用树莓派PICO做一个电子墨水屏老黄历图7


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

参考其自带的Terminal8x12e.h,复制了一份Terminal8x16e.h。
再访问 https://www.23bei.com/tool/216.html 生成所需显示内容的字形数据:
用树莓派PICO做一个电子墨水屏老黄历图8

上面的数据,拷贝到上述字形数据文件中,替换原有的一部分:
用树莓派PICO做一个电子墨水屏老黄历图9

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


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

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

最终,得到一系列坐标定义:
  1. 顶部:
  2. 方块:
  3. 5,4 83,64 淡红
  4. 字:
  5. 95,11 今天 黑色
  6. 95,35 农历 淡红
  7. 95,54 葵卯年 黑色
  8. 37,25 7 白色
  9. 线框:灰色
  10. x=70,170
  11. y=77,96,158
  12. 0,77,w=240-1,h=158-77 黑色
  13. 70,77,w=100,h=158-77 黑色
  14. 中间两边:
  15. 圆:
  16. 37,110,r=10   红色
  17. 204,110.r=10  灰色
  18. 字:
  19. 37,110 宜   白色
  20. 204,110 忌  白色
  21. 37,82 无节日 黑色
  22. 204,82 无节气 黑色
  23. 字:
  24. 37,126 祭祀 黑色
  25. 204,126 畋猎 黑色
  26. 37,141 嫁娶 黑色
  27. 204,141 求医 黑色
  28. 中间中间:
  29. 圆:
  30. 117,120,r=30 灰色
  31. 117,120,r=20 暗红
  32. 117,120,r=20 灰色
  33. 117,120,r=10 白色
  34. 线:
  35. 117,120-5 - 117,120+5 红色
  36. 字:
  37. 70,77 喜 白色
  38. 170,77 贵 白色
  39. 70,158 财 白色
  40. 170,158 生 白色
  41. 块:
  42. 70,77-76,83 喜 暗红
  43. 170,77-176,83 贵 红色
  44. 70,158-76,164 财 淡红
  45. 170,158-176,164 生 灰色
  46. 字:
  47. 80,77 西北 黑色
  48. 180,77 东北 黑色
  49. 80,158 正东 黑色
  50. 180,158 东南 黑色
  51. 下中:
  52. 0,158 - 0,181, w=240,h=181-158 灰色
  53. 0,181 - 0,210, w=240,h=210-181 灰色
  54. 0,210 - 0,270, w=240, h=270-181 灰色
  55. 70,181, 170,210, w=100,h=270-181 灰色
  56. 70,254, 170,210, w=100,h=270-210 灰色
  57. 字:
  58. 54,166 年 红色
  59. 99,166 月 红色
  60. 146,166 日 红色
  61. 9,185   冲 红色
  62. 102,185 煞 红色
  63. 176,185 天干 红色
  64. 14,213 今日胎神 红色
  65. 98,213 今日吉时
  66. 182.213 今日八字
  67. 74,257 十二神 红色
  68. 124,257 二八宿 红色
复制代码


八、代码编写
上一步得到了坐标数据,下面就是实际的代码编写了:
  1. // Screen
  2. #include "PDLS_EXT3_Basic_Global.h"
  3. // SDK
  4. // #include <Arduino.h>
  5. #include "hV_HAL_Peripherals.h"
  6. // Include application, user and local libraries
  7. // #include <SPI.h>
  8. // Configuration
  9. #include "hV_Configuration.h"
  10. // Set parameters
  11. // Define structures and classes
  12. // Define variables and constants
  13. Screen_EPD_EXT3 myScreen(eScreen_EPD_EXT3_370, boardRaspberryPiPico_RP2040);
  14. // Prototypes
  15. // Utilities
  16. ///
  17. /// @brief Wait with countdown
  18. /// @param second duration, s
  19. ///
  20. void wait(uint8_t second)
  21. {
  22.     for (uint8_t i = second; i > 0; i--)
  23.     {
  24.         Serial.print(formatString(" > %i  \r", i));
  25.         delay(1000);
  26.     }
  27.     Serial.print("         \r");
  28. }
  29. // Functions
  30. ///
  31. /// @brief DisplayLHL
  32. ///
  33. void DisplayLHL()
  34. {
  35.     myScreen.setOrientation(2);
  36.     myScreen.setPenSolid(true);
  37.     myScreen.dRectangle(5, 4, 83-5, 64-4, myColours.lightRed);
  38.     myScreen.selectFont(Font_Terminal16x24);
  39.     myScreen.gText(37, 25, "7", myColours.white);
  40.     myScreen.selectFont(Font_Terminal8x12);
  41.     myScreen.gText(95, 11, "Today, 2024-1, Sunday", myColours.black);
  42.     myScreen.gText(95, 35, "NongLi 11-26", myColours.red);
  43.     myScreen.gText(95, 54, "Tu Yi-Chou Geng-Wu", myColours.black);
  44.     myScreen.setPenSolid(false);
  45.     myScreen.dRectangle(0, 77, 240-1, 158-77, myColours.grey);
  46.     myScreen.dRectangle(70, 77, 170-70, 158-77, myColours.grey);
  47.     myScreen.setPenSolid(true);
  48.     myScreen.circle(37, 110, 10, myColours.red);
  49.     myScreen.circle(204, 110, 10, myColours.grey);
  50.     myScreen.gText(37-4, 110-4, "Y", myColours.white);
  51.     myScreen.gText(204-4, 110-4, "N", myColours.white);
  52.     myScreen.gText(37-20, 82, "No Jie", myColours.black);
  53.     myScreen.gText(204-20, 82, "No Qi", myColours.black);
  54.     myScreen.gText(37-20-8, 126, "Ji-Shi", myColours.black);
  55.     myScreen.gText(204-20-8, 126, "Lie-Shou", myColours.black);
  56.     myScreen.gText(37-20-8, 141, "Jia-Qu", myColours.black);
  57.     myScreen.gText(204-20-8, 141, "Qiu-Yi", myColours.black);
  58.     myScreen.circle(117, 120, 30, myColours.grey);
  59.     myScreen.circle(117, 120, 25, myColours.darkRed);
  60.     myScreen.circle(117, 120, 20, myColours.grey);
  61.     myScreen.circle(117, 120, 10, myColours.white);
  62.     myScreen.circle(117, 120, 2, myColours.darkRed);
  63.     myScreen.dLine(117, 120-10, 0, 10, myColours.black);
  64.     myScreen.dLine(117, 120, 0, 10, myColours.red);
  65.     myScreen.dRectangle(70, 77, 16, 16, myColours.red);
  66.     myScreen.dRectangle(170-16, 77, 16, 16, myColours.darkRed);
  67.     myScreen.dRectangle(70, 158-16, 16, 16, myColours.lightRed);
  68.     myScreen.dRectangle(170-16, 158-16, 16, 16, myColours.grey);
  69.     myScreen.gText(70+4, 77+4, "J", myColours.white);
  70.     myScreen.gText(170-16+4, 77+4, "E", myColours.white);
  71.     myScreen.gText(70+4, 158-16+4, "P", myColours.black);
  72.     myScreen.gText(170-16+4, 158-16+4, "L", myColours.black);
  73.     myScreen.gText(80+8, 77+2, "WN", myColours.black);
  74.     myScreen.gText(180-8-32-8, 77+2, "EN", myColours.black);
  75.     myScreen.gText(80+8, 158-8-4, "CE", myColours.black);
  76.     myScreen.gText(180-8-32-8, 158-8-4, "ES", myColours.black);
  77.     myScreen.setPenSolid(false);
  78.     myScreen.dRectangle(0, 158, 240-1, 181-158, myColours.grey);
  79.     myScreen.dRectangle(0, 181, 240-1, 210-181, myColours.grey);
  80.     myScreen.dRectangle(0, 210, 240-1, 270-210, myColours.grey);
  81.     myScreen.dRectangle(70, 181, 170-70, 270-181, myColours.grey);
  82.     myScreen.dRectangle(70, 254, 170-70, 270-254, myColours.grey);
  83.     myScreen.setPenSolid(true);
  84.     myScreen.gText(54, 166, "Y:", myColours.red);
  85.     myScreen.gText(99, 166, "M:", myColours.red);
  86.     myScreen.gText(146, 166, "D:", myColours.red);
  87.     myScreen.gText(9, 185, "Chong:", myColours.red);
  88.     myScreen.gText(102, 185, "Sha:", myColours.red);
  89.     myScreen.gText(176, 185, "Tian:", myColours.red);
  90.     myScreen.gText(14-8, 213, "Today-B:", myColours.red);
  91.     myScreen.gText(98-16, 213, "Today-T:", myColours.red);
  92.     myScreen.gText(182-8, 213, "Today-8:", myColours.red);
  93.     myScreen.selectFont(Font_Terminal6x8);
  94.     myScreen.gText(74, 257, "12:", myColours.red);
  95.     myScreen.gText(124, 257, "28:", myColours.red);
  96.     myScreen.selectFont(Font_Terminal8x16);
  97.     myScreen.gText(76, 329, "LMNOPQ", myColours.red);
  98.     myScreen.flush();
  99. }
  100. // Add setup code
  101. ///
  102. /// @brief Setup
  103. ///
  104. void setup()
  105. {
  106.     // Start
  107.     Serial.begin(115200);
  108.     delay(2000);
  109.     Serial.println("begin... ");
  110.     myScreen.begin();
  111.     Serial.println(formatString("%s %ix%i", myScreen.WhoAmI().c_str(), myScreen.screenSizeX(), myScreen.screenSizeY()));
  112.     Serial.println("Colours... ");
  113.     myScreen.clear();
  114.     DisplayLHL();
  115.     wait(8);
  116.     Serial.println("=== ");
  117.     Serial.println();
  118. }
  119. // Add loop code
  120. ///
  121. /// @brief Loop, empty
  122. ///
  123. void loop()
  124. {
  125.     delay(1000);
  126. }
复制代码


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

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

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

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

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


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

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

1. json配置文件如下:
  1. {
  2.     "name": "laohuangli",
  3.     "orientation": 2,
  4.     "version": 1.0,
  5.     "offset" : [0, 0],
  6.     "data_demo" :[
  7.         {"type": "comment", "value":"这是一行注释"},
  8.         {"type": "setOrientation", "value":2},
  9.         {"type": "setOffset", "x":0, "y":0},
  10.         {"type": "setPenSolid", "value":true},
  11.         {"type": "selectFont", "value":"16x24"},
  12.         {"type": "setFontSolid", "value":true},
  13.         {"type": "gText",      "x":37, "y":25, "text":"7", "color":"white"},
  14.         {"type": "point",       "x":5, "y":4, "color":"white"},
  15.         {"type": "line",       "x":5, "y":4, "x1":5, "y1":4, "color":"white"},
  16.         {"type": "rectangle",       "x":5, "y":4, "x1":5, "y1":4, "color":"white"},
  17.         {"type": "circle",       "x":5, "y":4, "r":10, "color":"white"},
  18.         {"type": "triangle",       "x":5, "y":4, "x1":5, "y1":4, "x2":5, "y2":4, "color":"white"},
  19.         {"type": "dLine",       "x":5, "y":4, "w":78, "h":60, "color":"white"},
  20.         {"type": "dRectangle", "x":5, "y":4, "w":78, "h":60, "color":"white"}
  21.     ],
  22.     "data" :[
  23.         {"type":"comment", "value":"设置方向"},
  24.         {"type": "setOrientation", "value":2},
  25.         {"type":"comment", "value":"设置笔触"},
  26.         {"type": "setPenSolid", "value":true},
  27.         {"type": "setFontSolid", "value":false},
  28.         {"type":"comment", "value":"标题"},
  29.         {"type": "selectFont", "value":"8x16"},
  30.         {"type": "gText", "x":96, "y":5, "text":"老黄历", "color":"red"},
  31.         {"type": "dLine", "x":0, "y":30, "w":240, "h":0, "color":"grey"},
  32.         {"type": "setOffset", "x":0, "y":35},
  33.         {"type":"comment", "value":"当日日期"},
  34.         {"type": "dRectangle", "x":5, "y":4, "w":78, "h":70, "color":"lightRed"},
  35.         {"type": "selectFont", "value":"16x24"},
  36.         {"type": "gText",      "x":37, "y":30, "text":"8", "color":"white"},
  37.         {"type":"comment", "value":"当日日历信息"},
  38.         {"type": "selectFont", "value":"8x16"},
  39.         {"type": "gText",      "x":95, "y":10, "text":"2024年1月 星期一", "color":"black"},
  40.         {"type": "gText",      "x":95, "y":25, "text":"农历十一月廿七", "color":"black"},
  41.         {"type": "gText",      "x":95, "y":40, "text":"癸卯年 兔年", "color":"black"},
  42.         {"type": "gText",      "x":95, "y":55, "text":"乙丑月 辛未日", "color":"black"},
  43.         {"type": "setOffset", "x":0, "y":50},
  44.         {"type":"comment", "value":"中间方框"},
  45.         {"type": "setPenSolid", "value":false},
  46.         {"type": "dRectangle", "x":0, "y":77, "w":239, "h":81, "color":"grey"},
  47.         {"type": "dRectangle", "x":70, "y":77, "w":100, "h":81, "color":"grey"},
  48.         {"type":"comment", "value":"中间方框内两边圆形"},
  49.         {"type": "setPenSolid", "value":true},
  50.         {"type": "circle", "x":37, "y":110, "r":10, "color":"red"},
  51.         {"type": "circle", "x":204, "y":110, "r":10, "color":"grey"},
  52.         {"type": "selectFont", "value":"8x16"},
  53.         {"type": "gText", "x":31, "y":103, "text":"宜", "color":"white"},
  54.         {"type": "gText", "x":198, "y":103, "text":"忌", "color":"black"},
  55.         {"type":"comment", "value":"中间方框内上部文字"},
  56.         {"type": "gText", "x":17, "y":82, "text":"无节日", "color":"black"},
  57.         {"type": "gText", "x":184, "y":82, "text":"无节气", "color":"black"},
  58.         {"type":"comment", "value":"中间方框内下部文字"},
  59.         {"type": "gText", "x":9, "y":126, "text":"祭祀 解除", "color":"black"},
  60.         {"type": "gText", "x":176, "y":126, "text":"祈福 冠带", "color":"black"},
  61.         {"type": "gText", "x":9, "y":141, "text":"破屋坏垣", "color":"black"},
  62.         {"type": "gText", "x":176, "y":141, "text":"嫁娶 进人口", "color":"black"},
  63.         {"type":"comment", "value":"中间方框内中部圆盘"},
  64.         {"type": "circle", "x":117, "y":120, "r":30, "color":"grey"},
  65.         {"type": "circle", "x":117, "y":120, "r":25, "color":"darkRed"},
  66.         {"type": "circle", "x":117, "y":120, "r":20, "color":"grey"},
  67.         {"type": "circle", "x":117, "y":120, "r":10, "color":"white"},
  68.         {"type":"comment", "value":"中间方框内中部圆盘内部指南针"},
  69.         {"type": "circle", "x":117, "y":120, "r":2, "color":"darkRed"},
  70.         {"type": "dLine", "x":117, "y":110, "w":0, "h":10, "color":"black"},
  71.         {"type": "dLine", "x":117, "y":120, "w":0, "h":10, "color":"red"},
  72.         {"type":"comment", "value":"中间方框内中部圆盘四边方框和文字"},
  73.         {"type": "dRectangle", "x":70, "y":77, "w":16, "h":16, "color":"red"},
  74.         {"type": "dRectangle", "x":154, "y":77, "w":16, "h":16, "color":"darkRed"},
  75.         {"type": "dRectangle", "x":70, "y":142, "w":16, "h":16, "color":"lightRed"},
  76.         {"type": "dRectangle", "x":154, "y":142, "w":16, "h":16, "color":"grey"},
  77.         {"type": "gText", "x":72, "y":77, "text":"喜", "color":"white"},
  78.         {"type": "gText", "x":156, "y":77, "text":"贵", "color":"white"},
  79.         {"type": "gText", "x":72, "y":142, "text":"财", "color":"black"},
  80.         {"type": "gText", "x":156, "y":142, "text":"生", "color":"black"},
  81.         {"type":"comment", "value":"中间方框内中部圆盘四边方框旁边文字"},
  82.         {"type": "gText", "x":88, "y":77, "text":"西南", "color":"black"},
  83.         {"type": "gText", "x":130, "y":77, "text":"东北", "color":"black"},
  84.         {"type": "gText", "x":88, "y":142, "text":"正东", "color":"black"},
  85.         {"type": "gText", "x":130, "y":142, "text":"东南", "color":"black"},
  86.         {"type": "setOffset", "x":0, "y":65},
  87.         {"type":"comment", "value":"下部方框"},
  88.         {"type": "setPenSolid", "value":false},
  89.         {"type": "dRectangle", "x":0, "y":158, "w":239, "h":23, "color":"grey"},
  90.         {"type": "dRectangle", "x":0, "y":181, "w":239, "h":49, "color":"grey"},
  91.         {"type": "dRectangle", "x":0, "y":230, "w":239, "h":90, "color":"grey"},
  92.         {"type": "dRectangle", "x":70, "y":181, "w":100, "h":139, "color":"grey"},
  93.         {"type": "dRectangle", "x":70, "y":284, "w":100, "h":36, "color":"grey"},
  94.         {"type":"comment", "value":"下部方框文字:红色"},
  95.         {"type":"comment", "value":"下部方框顶部文字"},
  96.         {"type": "gText", "x":54, "y":162, "text":"年", "color":"red"},
  97.         {"type": "gText", "x":99, "y":162, "text":"月", "color":"red"},
  98.         {"type": "gText", "x":146, "y":162, "text":"日", "color":"red"},
  99.         {"type":"comment", "value":"下部方框中间文字"},
  100.         {"type": "gText", "x":9, "y":185, "text":"冲", "color":"red"},
  101.         {"type": "gText", "x":9, "y":205, "text":"肖", "color":"red"},
  102.         {"type": "gText", "x":95, "y":200, "text":"煞", "color":"red"},
  103.         {"type": "gText", "x":176, "y":185, "text":"天干", "color":"red"},
  104.         {"type": "gText", "x":176, "y":205, "text":"地支", "color":"red"},
  105.         {"type":"comment", "value":"下部方框下部文字"},
  106.         {"type": "gText", "x":6, "y":233, "text":"今日胎神", "color":"red"},
  107.         {"type": "gText", "x":82, "y":233, "text":"今日吉时", "color":"red"},
  108.         {"type": "gText", "x":174, "y":233, "text":"今日八字", "color":"red"},
  109.         {"type":"comment", "value":"下部方框下部中间文字"},
  110.         {"type": "gText", "x":74, "y":287, "text":"十二神", "color":"red"},
  111.         {"type": "gText", "x":124, "y":287, "text":"二八宿", "color":"red"},
  112.         {"type":"comment", "value":"下部方框文字:黑色"},
  113.         {"type":"comment", "value":"下部方框顶部文字"},
  114.         {"type": "gText", "x":70, "y":162, "text":"金箔", "color":"black"},
  115.         {"type": "gText", "x":115, "y":162, "text":"海中", "color":"black"},
  116.         {"type": "gText", "x":162, "y":162, "text":"路旁", "color":"black"},
  117.         {"type":"comment", "value":"下部方框中间文字"},
  118.         {"type": "gText", "x":24, "y":190, "text":"羊日冲牛", "color":"black"},
  119.         {"type": "gText", "x":24, "y":210, "text":"乙丑", "color":"black"},
  120.         {"type": "gText", "x":120, "y":200, "text":"煞西", "color":"black"},
  121.         {"type": "gText", "x":201, "y":190, "text":"辛金", "color":"black"},
  122.         {"type": "gText", "x":201, "y":210, "text":"未土", "color":"black"},
  123.         {"type":"comment", "value":"下部方框下部文字"},
  124.         {"type": "gText", "x":6, "y":253, "text":"厨灶厕外", "color":"black"},
  125.         {"type": "gText", "x":6, "y":273, "text":"西南", "color":"black"},
  126.         {"type": "gText", "x":82, "y":253, "text":"庚寅辛卯癸巳", "color":"black"},
  127.         {"type": "gText", "x":82, "y":269, "text":"丙申戊戌己亥 ", "color":"black"},
  128.         {"type": "gText", "x":174, "y":253, "text":"癸卯乙丑", "color":"black"},
  129.         {"type": "gText", "x":174, "y":273, "text":"辛未戊子", "color":"black"},
  130.         {"type":"comment", "value":"下部方框下部中间文字"},
  131.         {"type": "gText", "x":95, "y":301, "text":"破", "color":"black"},
  132.         {"type": "gText", "x":145, "y":301, "text":"张", "color":"black"}
  133.     ]
  134. }
复制代码


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

2.  json配置文件,通过web提供
在这里,使用了python提供简单web服务:
  1. python -m http.server 9080
复制代码

3. arduino中PicoW的代码:
  1. // #include <WiFi.h>
  2. // #include <HTTPClient.h>
  3. // Screen
  4. #include "PDLS_EXT3_Basic_Global.h"
  5. // SDK
  6. // #include <Arduino.h>
  7. #include "hV_HAL_Peripherals.h"
  8. // Include application, user and local libraries
  9. // #include <SPI.h>
  10. // Configuration
  11. #include "hV_Configuration.h"
  12. #include <Arduino.h>
  13. #include <WiFi.h>
  14. #include <HTTPClient.h>
  15. #include <ArduinoJson.h>
  16. // Define structures and classes
  17. // Define variables and constants
  18. Screen_EPD_EXT3 myScreen(eScreen_EPD_EXT3_370, boardRaspberryPiPico_RP2040);
  19. bool status = false;
  20. // Prototypes
  21. // Utilities
  22. ///
  23. /// @brief Wait with countdown
  24. /// @param second duration, s
  25. ///
  26. void wait(uint8_t second) {
  27.   for (uint8_t i = second; i > 0; i--) {
  28.     Serial.print(formatString(" > %i  \r", i));
  29.     delay(1000);
  30.   }
  31.   Serial.print("         \r");
  32. }
  33. #ifndef STASSID
  34. #define STASSID "********"
  35. #define STAPSK "********"
  36. #endif
  37. const char *ssid = STASSID;
  38. const char *pass = STAPSK;
  39. WiFiMulti WiFiMulti;
  40. uint8_t get_font(String font_name) {
  41.   if (font_name == "6x8") {
  42.     return Font_Terminal6x8;
  43.   } else if (font_name == "8x12") {
  44.     return Font_Terminal8x12;
  45.   } else if (font_name == "8x16") {
  46.     return Font_Terminal8x16;
  47.   } else if (font_name == "12x16") {
  48.     return Font_Terminal12x16;
  49.   } else if (font_name == "16x24") {
  50.     return Font_Terminal16x24;
  51.   } else {
  52.     Serial.println(formatString("font_name is invalid: %s", font_name));
  53.     return Font_Terminal6x8;
  54.   }
  55. }
  56. uint16_t get_color(String color_name) {
  57.   if (color_name == "black") {
  58.     return myColours.black;
  59.   } else if (color_name == "white") {
  60.     return myColours.white;
  61.   } else if (color_name == "grey") {
  62.     return myColours.grey;
  63.   } else if (color_name == "red") {
  64.     return myColours.red;
  65.   } else if (color_name == "lightRed") {
  66.     return myColours.lightRed;
  67.   } else if (color_name == "darkRed") {
  68.     return myColours.darkRed;
  69.   } else {
  70.     Serial.println(formatString("color_name is invalid: %s", color_name));
  71.     return myColours.black;
  72.   }
  73. }
  74. void request_json_and_display() {
  75.   // wait for WiFi connection
  76.   if ((WiFiMulti.run() == WL_CONNECTED)) {
  77.     HTTPClient http;
  78.     Serial.print("[HTTP] begin...\n");
  79.     if (http.begin("http://192.168.1.15:9080/laohuangli.json")) {  // HTTP
  80.       Serial.print("[HTTP] GET...\n");
  81.       // start connection and send HTTP header
  82.       int httpCode = http.GET();
  83.       // httpCode will be negative on error
  84.       if (httpCode > 0) {
  85.         // HTTP header has been send and Server response header has been handled
  86.         Serial.printf("[HTTP] GET... code: %d\n", httpCode);
  87.         // file found at server
  88.         if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
  89.           String payload = http.getString();
  90.           Serial.println(payload);
  91.           // Allocate the JSON document
  92.           JsonDocument doc;
  93.           // Parse JSON object
  94.           DeserializationError error = deserializeJson(doc, payload);
  95.           if (error) {
  96.             Serial.print(F("deserializeJson() failed: "));
  97.             Serial.println(error.f_str());
  98.             return;
  99.           } else {
  100.             // 解析数据
  101.             int x_offset = 0;
  102.             int y_offset = 0;
  103.             int orientation = 2;
  104.             
  105.             x_offset = doc["offset"][0].as<long>();
  106.             y_offset = doc["offset"][1].as<long>();
  107.             orientation = doc["orientation"].as<long>();
  108.             Serial.println(F("Response:"));
  109.             Serial.println(formatString("name: %s", doc["name"].as<const char *>()));
  110.             Serial.println(formatString("orientation: %d", orientation));
  111.             Serial.println(formatString("offset: (%d,%d)", x_offset, y_offset));
  112.             Serial.println(formatString("version: %0.2f", doc["version"].as<float>()));
  113.             Serial.println(formatString("data size: %d", doc["data"].size()));
  114.             // 设置默认方向
  115.             myScreen.setOrientation(orientation);
  116.             for (int i = 0; i < doc["data"].size(); i++) {
  117.               String type = doc["data"][i]["type"];
  118.               if (type == "comment") {
  119.                 // 注释
  120.                 String comment = doc["data"][i]["value"];
  121.                 Serial.println(formatString("comment: %s", comment.c_str()));
  122.               } else if (type == "setOrientation") {
  123.                 // 设置方向
  124.                 int value = doc["data"][i]["value"].as<long>();
  125.                 Serial.println(formatString("setOrientation: %d", value));
  126.                 myScreen.setOrientation(value);
  127.               } else if (type == "setOffset") {
  128.                 // 设置方向
  129.                 x_offset = doc["data"][i]["x"].as<long>();
  130.                 y_offset = doc["data"][i]["y"].as<long>();
  131.                 Serial.println(formatString("setOffset: (x, y)=(%d, %d)", x_offset, y_offset));
  132.               } else if (type == "setPenSolid") {
  133.                 // 设置落笔抬笔
  134.                 int value = doc["data"][i]["value"].as<bool>();
  135.                 Serial.println(formatString("setPenSolid: %d", value));
  136.                 myScreen.setPenSolid(value);
  137.               } else if (type == "selectFont") {
  138.                 // 设置字体
  139.                 String font_name = doc["data"][i]["value"];
  140.                 Serial.println(formatString("selectFont: %s", font_name.c_str()));
  141.                 uint8_t font = get_font(font_name);
  142.                 myScreen.selectFont(font);
  143.               } else if (type == "setFontSolid") {
  144.                 // 设置字体落笔抬笔
  145.                 int value = doc["data"][i]["value"].as<bool>();
  146.                 Serial.println(formatString("setFontSolid: %d", value));
  147.                 myScreen.setFontSolid(value);
  148.               } else if (type == "gText") {
  149.                 // 显示文本
  150.                 int x = doc["data"][i]["x"].as<long>();
  151.                 int y = doc["data"][i]["y"].as<long>();
  152.                 String text = doc["data"][i]["text"];
  153.                 String color_name = doc["data"][i]["color"];
  154.                 Serial.println(formatString("gText: (x,y)=(%d,%d) text=%s color=%s", x, y, text.c_str(), color_name.c_str()));
  155.                 uint16_t color = get_color(color_name);
  156.                 myScreen.gText(x + x_offset, y + y_offset, text, color);
  157.               } else if (type == "point") {
  158.                 // 画点
  159.                 int x = doc["data"][i]["x"].as<long>();
  160.                 int y = doc["data"][i]["y"].as<long>();
  161.                 String color_name = doc["data"][i]["color"];
  162.                 Serial.println(formatString("point: (x,y)=(%d,%d) color=%s", x, y, color_name.c_str()));
  163.                 uint16_t color = get_color(color_name);
  164.                 myScreen.point(x + x_offset, y + y_offset, color);
  165.               } else if (type == "line") {
  166.                 // 画线
  167.                 int x = doc["data"][i]["x"].as<long>();
  168.                 int y = doc["data"][i]["y"].as<long>();
  169.                 int x1 = doc["data"][i]["x1"].as<long>();
  170.                 int y1 = doc["data"][i]["y1"].as<long>();
  171.                 String color_name = doc["data"][i]["color"];
  172.                 Serial.println(formatString("line: (x,y)=(%d,%d) (x1,y1)=(%d,%d) color=%s", x, y, x1, y1, color_name.c_str()));
  173.                 uint16_t color = get_color(color_name);
  174.                 myScreen.line(x + x_offset, y + y_offset, x1+ x_offset, y1 + y_offset, color);
  175.               } else if (type == "rectangle") {
  176.                 // 画矩形
  177.                 int x = doc["data"][i]["x"].as<long>();
  178.                 int y = doc["data"][i]["y"].as<long>();
  179.                 int x1 = doc["data"][i]["x1"].as<long>();
  180.                 int y1 = doc["data"][i]["y1"].as<long>();
  181.                 String color_name = doc["data"][i]["color"];
  182.                 Serial.println(formatString("rectangle: (x,y)=(%d,%d) (x1,y1)=(%d,%d) color=%s", x, y, x1, y1, color_name.c_str()));
  183.                 uint16_t color = get_color(color_name);
  184.                 myScreen.rectangle(x + x_offset, y + y_offset, x1 + x_offset, y1 + y_offset, color);
  185.               } else if (type == "circle") {
  186.                 // 画圆
  187.                 int x = doc["data"][i]["x"].as<long>();
  188.                 int y = doc["data"][i]["y"].as<long>();
  189.                 int r = doc["data"][i]["r"].as<long>();
  190.                 String color_name = doc["data"][i]["color"];
  191.                 Serial.println(formatString("circle: (x,y)=(%d,%d) r=%d color=%s", x, y, r, color_name.c_str()));
  192.                 uint16_t color = get_color(color_name);
  193.                 myScreen.circle(x + x_offset, y + y_offset, r, color);
  194.               } else if (type == "triangle") {
  195.                 // 画三角形
  196.                 int x = doc["data"][i]["x"].as<long>();
  197.                 int y = doc["data"][i]["y"].as<long>();
  198.                 int x1 = doc["data"][i]["x1"].as<long>();
  199.                 int y1 = doc["data"][i]["y1"].as<long>();
  200.                 int x2 = doc["data"][i]["x2"].as<long>();
  201.                 int y2 = doc["data"][i]["y2"].as<long>();
  202.                 String color_name = doc["data"][i]["color"];
  203.                 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()));
  204.                 uint16_t color = get_color(color_name);
  205.                 myScreen.triangle(x + x_offset, y + y_offset, x1 + x_offset, y1 + y_offset, x2 + x_offset, y2 + y_offset, color);
  206.               } else if (type == "dLine") {
  207.                 // 画线(向量)
  208.                 int x = doc["data"][i]["x"].as<long>();
  209.                 int y = doc["data"][i]["y"].as<long>();
  210.                 int w = doc["data"][i]["w"].as<long>();
  211.                 int h = doc["data"][i]["h"].as<long>();
  212.                 String color_name = doc["data"][i]["color"];
  213.                 Serial.println(formatString("dLine: (x,y)=(%d,%d) (w,h)=(%d,%d) color=%s", x, y, w, h, color_name.c_str()));
  214.                 uint16_t color = get_color(color_name);
  215.                 myScreen.dLine(x + x_offset, y + y_offset, w, h, color);
  216.               } else if (type == "dRectangle") {
  217.                 // 画矩形(向量)
  218.                 int x = doc["data"][i]["x"].as<long>();
  219.                 int y = doc["data"][i]["y"].as<long>();
  220.                 int w = doc["data"][i]["w"].as<long>();
  221.                 int h = doc["data"][i]["h"].as<long>();
  222.                 String color_name = doc["data"][i]["color"];
  223.                 Serial.println(formatString("dRectangle: (x,y)=(%d,%d) (w,h)=(%d,%d) color=%s", x, y, w, h, color_name.c_str()));
  224.                 uint16_t color = get_color(color_name);
  225.                 myScreen.dRectangle(x + x_offset, y + y_offset, w, h, color);
  226.               } else {
  227.                 Serial.println(formatString("type: %s", type));
  228.               }
  229.             }
  230.             myScreen.flush();
  231.           }
  232.         }
  233.       } else {
  234.         Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  235.       }
  236.       http.end();
  237.     } else {
  238.       Serial.println("[HTTP} Unable to connect");
  239.     }
  240.   }
  241. }
  242. void setup() {
  243.   pinMode(LED_BUILTIN, OUTPUT);
  244.   Serial.begin(115200);
  245.   // Serial.setDebugOutput(true);
  246.   Serial.println();
  247.   Serial.println();
  248.   Serial.println();
  249.   for (uint8_t t = 4; t > 0; t--) {
  250.     Serial.printf("[SETUP] WAIT %d...\n", t);
  251.     Serial.flush();
  252.     delay(1000);
  253.   }
  254.   status = true;
  255.   digitalWrite(LED_BUILTIN, status);
  256.   WiFiMulti.addAP(ssid, pass);
  257.   Serial.print("WiFi is connecting: ");
  258.   while ((WiFiMulti.run() != WL_CONNECTED)) {
  259.     Serial.print(".");
  260.     Serial.flush();
  261.     delay(100);
  262.     digitalWrite(LED_BUILTIN, status);
  263.     status = !status;
  264.   }
  265.   Serial.println(" OK!");
  266.   status = false;
  267.   digitalWrite(LED_BUILTIN, status);
  268.   Serial.println("begin... ");
  269.   myScreen.begin();
  270.   Serial.println(formatString("%s %ix%i", myScreen.WhoAmI().c_str(), myScreen.screenSizeX(), myScreen.screenSizeY()));
  271.   Serial.println("request json and display... ");
  272.   status = true;
  273.   digitalWrite(LED_BUILTIN, status);
  274.   myScreen.clear();
  275.   request_json_and_display();
  276.   status = false;
  277.   digitalWrite(LED_BUILTIN, status);
  278.   Serial.println("=== ");
  279.   Serial.println();
  280. }
  281. void loop() {
  282.   if (BOOTSEL) {
  283.     status = true;
  284.     digitalWrite(LED_BUILTIN, status);
  285.     Serial.printf("\a\aYou pressed BOOTSEL!\n");
  286.     // Wait for BOOTSEL to be released
  287.     while (BOOTSEL) {
  288.       delay(1);
  289.     }
  290.     status = false;
  291.     digitalWrite(LED_BUILTIN, status);
  292.     delay(500);
  293.     status = true;
  294.     digitalWrite(LED_BUILTIN, status);
  295.     myScreen.clear();
  296.     request_json_and_display();
  297.     status = false;
  298.     digitalWrite(LED_BUILTIN, status);
  299.   }
  300.   delay(1000);
  301. }
复制代码


4. 最终效果:
用树莓派PICO做一个电子墨水屏老黄历图14

如果觉得显示的效果不好,例如位置不对等,可以在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

乔帮主的文章太赞了。虽然还没有接触墨水屏,看到这篇文章,我都想跃跃欲试了。既高大上又有难度,很有挑战的意义。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail