
厌倦了普通的纸质日历,纯数字显示的又很无聊,不知道是不是从Kindle上获取的灵感,国外小哥自制了一款“电纸书”版本的日历,一起来看看吧。
如果搭配之前做的卡西欧挂钟,效果大概是这个样子:
功能
除了基本的显示日期的功能外,它还可以:
-
显示在选定的Google日历中的前9个事件; -
显示来自OpenWeather Maps的图标以及温度和风速
该项目使用了7.5 Waveshare电子墨水屏、ESP32微控制器和LIPO电池。
采用13x18宜家Ribba像框包装。
除了微控制器的Arduino代码外,我还创建了一个脚本来从Google提取日历项。
归功于Github上的ESP32电子墨水气象站项目,在编写该项目时我从中学到了很多。
Github上该项目的代码见文末[1]
耗材:
-
Waveshare 7.5英寸E-ink屏幕(800×600)
-
LOLIN32 ESP32
-
1800 mAH锂电池
-
IKEA Ribba 13x18框架
总费用:折合人民币大概800元。
作为替代方案,Waveshare提供了带有E-Ink端口的自定义ESP32单元(可以避免接线)-但LOLIN32没有随附的电池插头,因此必须通过5V移动电源为其供电或自行接线连接到3V和GND连接器上。
步骤1 将屏幕连接到ESP32开发板
屏幕随附一根连接器电缆,可以将其直接连接到开发板上。但我发现占用了相框太多空间,决定使用自己的电线连接电路板。
接口情况:
Waveshare 7.5 ↔ LOLIN32
Vcc ↔ 3V
GND ↔ GND
DIN ↔ 14
CLK ↔13
CS ↔ 15
DC ↔ 27
RST ↔ 26
BUSY ↔25
可以下载GxEPD2库来测试连接。如果使用的是Visual Studio Code,可以通过PlatformIO获取,也可以通过Arduino的库来获取。
要使用正确的引脚来初始化显示,在库中的示例中使用以下代码:
GxEPD2_3C display(GxEPD2_750c_Z08(/*CS=*/ 15, /*DC=*/ 27, /*RST=*/ 26, /*BUSY=*/ 25));
完成示例工作(在显示屏上获取演示文本/图形)后,继续进行下一步。
步骤2 从Google日历获取事件
Google的日历难以集成,我不得不通过Google脚本进行变通,使日历条目可访问。
在scripts.google.com上创建一个新的web应用来获得访问权限,然后将下面的代码粘贴到其中。
1.转到scripts.google.com并选择新项目;
2.将下面代码粘贴到其中;
3.用一个“好”名字来保存;
4.点击“Publish”,然后选择“Anyone, even anonymous”作为安全设置;
5.在项目中根据需要复制链接"https://script.google.com/macros/s/[UNIQUE CODE]/exec"
注意: 获得链接的任何人都可以获得日历中公开的条目。但链接是唯一的且只有你自己拥有。
我也喜欢其他关于集成的想法-但目前这个就可以用了。
将带有UNIQUE CODE的URL粘贴到浏览器中来测试脚本。应该看到用分号分隔的事件列表。请确定看到了之后进行下一步操作!
相关代码如下:
<p>function doGet(e) {</p><p> var calendars = CalendarApp.getAllCalendars();</p><p>
//var cal = CalendarApp.getCalendarsByName('NAME_OF_CALENDAR')[0]; // 0 is subcalendar ID, mostly "0"
//var cal = CalendarApp.getDefaultCalendar();
var calendars = CalendarApp.getAllCalendars();
if (calendars == undefined) {
Logger.log("No data");
return ContentService.createTextOutput("no access to calendar hubba");
}</p><p> var calendars_selected = [];
for (var ii = 0; ii < calendars.length; ii++) {
if (calendars[ii].isSelected()) {
calendars_selected.push(calendars[ii]);
Logger.log(calendars[ii].getName());
}
}
Logger.log("Old: " + calendars.length + " New: " + calendars_selected.length);</p><p> const now = new Date();
var start = new Date(); start.setHours(0, 0, 0); // start at midnight
const oneday = 24*3600000; // [msec]
const stop = new Date(start.getTime() + 14 * oneday); //get appointments for the next 14 days
//var events = cal.getEvents(start, stop); //pull start/stop time
var events = mergeCalendarEvents(calendars_selected, start, stop); //pull start/stop time
var str = '';
for (var ii = 0; ii < events.length; ii++) {</p><p> var event=events[ii];
var myStatus = event.getMyStatus();
// define valid entryStatus to populate array
switch(myStatus) {
case CalendarApp.GuestStatus.OWNER:
case CalendarApp.GuestStatus.YES:
case CalendarApp.GuestStatus.NO:
case CalendarApp.GuestStatus.INVITED:
case CalendarApp.GuestStatus.MAYBE:
default:
break;
}
// Show just every entry regardless of GuestStatus to also get events from shared calendars where you haven't set up the appointment on your own
str += event.getStartTime() + ';' +
//event.isAllDayEvent() + '\t' +
//event.getPopupReminders()[0] + '\t' +
event.getTitle() +';' +
event.isAllDayEvent() + ';';
}
return ContentService.createTextOutput(str);
}</p><p>function mergeCalendarEvents(calendars, startTime, endTime) {</p><p> var params = { start:startTime, end:endTime, uniqueIds:[] };</p><p> return calendars.map(toUniqueEvents_, params)
.reduce(toSingleArray_)
.sort(byStart_);
}</p><p>function toCalendars_(id) { return CalendarApp.getCalendarById(id); }</p><p>function toUniqueEvents_ (calendar) {
return calendar.getEvents(this.start, this.end)
.filter(onlyUniqueEvents_, this.uniqueIds);
}</p><p>function onlyUniqueEvents_(event) {
var eventId = event.getId();
var uniqueEvent = this.indexOf(eventId) < 0;
if(uniqueEvent) this.push(eventId);
return uniqueEvent;
}</p><p>function toSingleArray_(a, b) { return a.concat(b) }</p><p>function byStart_(a, b) {
return a.getStartTime().getTime() - b.getStartTime().getTime();
}</p>
步骤3 配置项目
现在对ESP32开发板进行编程。
为此可以使用Visual Studio Code(带有Platform IO)或Arduino。
对于这两个平台,请下载代码[2]到自己的项目库中。
Platform IO:
1.确保已安装Platformio并作为工作区打开项目文件夹;
2.如果配置正确,PlatformIO应该自己获取所需的库。如果没有,则必须转到PlatformIO / Libraries并安装 "GxEPD2" 和 "ArduinoJson"
Arduino:
1.转到Preferences,并将"https://dl.espressif.com/dl/package_esp32_index.json" 粘贴到"Additional Boards manager URL"中;
2.转到Tools/Boards/Boards Manager(工具/主板/主板管理器)。搜索ESP32并安装开发板套件;
3.选择WEMOS LOLIN32板(或你自己购买的板型号);
4.转到库管理器并安装"GxEPD2" 和 "ArduinoJson";
从这里开始,你必须专注于调整credentials.h。
设置wifi上网ssid和密码:
<p>// Change to your WiFi credentials<br>const char* ssid1 = "[YOUR_NETWORK_SSID]"; // For your network
const char* password1 = "[YOUR_NETWORK_KEY]"; // For your network</p>
注册并获取OpenWeatherMaps.com的API:
// Use your own API key by signing up for a free developer account at <a href="https://openweathermap.org/" rel="nofollow"> https://openweathermap.org/<br>String ><br>String OWMapikey = "[YOUR OPEN WEATHERMAPS API]"; <br>
更改你的位置信息来获取当地天气 - 谷歌地图是获取纬度和经度的好伙伴:
String City = "Copenhagen"; // Your home city See: <a href="http://bulk.openweathermap.org/sample/" rel="nofollow"> <a href="http://bulk.openweathermap.org/sample/" rel="nofollow"> http://bulk.openweathermap.org/sample/
</a>
</a>
String Lattitude = "[YOUR_LATTITUDE]"; // Your lattitude - use google maps
String Longitude = "[YOUR_LONGITUDE]"; // Your longitude - use google maps
String Country = "Denmark"; // Your country
String Language = "EN"; // NOTE: Only the weather description (not used) is translated by OWM
复制Google的webapp网址,获取你的个人事件:
char calendarServer[] = "script.google.com"; <br>String calendarRequest = "https://script.google.com/macros/s/[YOUR_UNIQUE_ID]/exec"; // Write the path for your google script to fetch calendar events
剩下的部分你可以在第一次让它工作的时候继续。
现在单击compile“编译”,希望不会报任何错误。一切就绪后,使用Microusb线将LOLIN32板连接至计算机,并对板子进行编程。
如果一切顺利,约20秒后看到Waveshare面板刷新,显示接下来14天的活动以及当地天气。如果没有显示,尝试串行连接到LOLIN32板提供的COM端口(通过Windows中的设备管理器识别端口号)。可以使用类似PuTTY的程序连接到串行端口,并观察在什么地方链路断开了。
步骤4 启用电池电量测量
为避免电池完全放电,需要启用电池测量仪。
项目中的代码会读取电池的当前电压,并在屏幕上显示电池图标,显示满电量、3/4电量、1/2电量、1/4电量或电量耗尽。电量耗尽后,项目会进入永久性深度睡眠状态,直到再次充电。
如果主板是LOLIN D32,电池测量值已经内置在GPIO35引脚中-只需要在代码"uint8_t batteryPin = 35"中调整该引脚即可。
如果是普通的ESP32,则需要在电池和选定的模拟IO引脚之间插入一个分压器 - 使电池的3.7电压低于开发板能够测量的3V。
在我的设置中,我使用了30K和100K电阻器设置,并从引脚34读取。
设置起来有点复杂,但如果没有设置,在忘记给电池充电时可能会耗尽电量并损坏电池。
步骤5 组装家庭日历
现在要做的就是将日历妥妥的组装在新的IKEA像框中。Ribba像框非常适合此类IOT项目,它的屏幕和背板之间有一个很大的封闭空间。
首先将墨水屏放在像框白色垫子的最上方,上下左右调整一下直到满意为止。
把与像框一起的纸放在屏幕的顶部,但引导屏幕连接器滑过像框的一侧。用白色塑料内框将其固定-用宽的一面在屏幕上施加轻微的压力。
将墨水屏连接主板,LOLIN板和电池粘在纸上,使其在连接后自然悬挂。
接通电池后,开发板将启动并刷新屏幕。此时对墨水屏的位置进行最终调整。
关上并固定像框的后盖。考虑加一条短的USB线为像框供电(如图所示,在后盖上切一个小孔)。充电周期为2-3个月。
现在,一款独一无二电子墨水屏家庭日历就制作完成了!
搭配之前做的卡西欧挂钟,看起来效果很不错!
参考资料
ESP32电子墨水气象站项目: https://github.com/kristiantm/eink-family-calendar-esp32
[2]配置项目代码: https://github.com/kristiantm/eink-family-calendar-esp32
项目的完整代码及相关材料,可以点击阅读原文到社区下载!
DF星球译员计划
硬件军火库
点击了解详情👆
大家有什么想法,或是对文章的指正,都欢迎在下方留言!
欢迎大家点击阅读原文,与项目作者一起讨论!
文件链接失效也可以点击阅读原文到论坛获取!
往期回顾
拔掉MacBookPro,用8GB树莓派4工作一天,体验如何?
点击阅读👆


