15 Commits

Author SHA1 Message Date
Jerry
21dc3ff831 1.1.3 2025-10-03 11:14:19 +08:00
Jerry
6e3d9cd06d 1.1.3 2025-10-03 11:08:10 +08:00
Jerry
0865a9492a 1.1.3 2025-10-03 11:04:55 +08:00
Jerry
d085750073 1.1.2 2025-09-29 18:39:59 +08:00
Jerry
64e5980d3d 1.1.1 2025-09-21 22:32:06 +08:00
Jerry
f631e8632a 1.1.0 2025-09-18 12:20:10 +08:00
Jerry
d1511d7035 1.1.0 2025-09-18 12:10:10 +08:00
Jerry
2a65240c5a 1.1.0 2025-09-18 11:49:19 +08:00
Jerry
e58ad6aa75 Fix memory leak by gzip 2025-09-05 08:20:33 +08:00
JADE Solution
dabe760a81 Merge pull request #17 from fengtao918/main
修复闰年计算逻辑错误
2025-09-01 23:19:33 +08:00
fengtao
102b1b507f 修复闰年计算逻辑错误 2025-09-01 22:32:02 +08:00
Jerry
3cbdda3c60 1.0.27 rebuild 2025-07-05 12:44:48 +08:00
Jerry
a8a60e8286 1.0.27 2025-07-02 19:38:05 +08:00
Jerry
bf390dada2 1.0.26 2025-06-29 08:12:04 +08:00
Jerry
5856a84b8d 1.0.25 2025-04-17 09:45:06 +08:00
19 changed files with 482 additions and 215 deletions

View File

@@ -2,6 +2,7 @@
墨水屏日历采用三色4.2寸墨水屏,展示基本月历信息,支持农历、公共假期、倒计日、天气(实时天气、每日天气)展示。<br>
项目以低难度、低成本的方式,方便爱好者实现属于自己的低功耗月历。<br>
<img src="./assets/img/sample.jpg" width="60%"><br>
<img src="./assets/img/sample2.jpg" width="60%"><br>
Bilibili连接https://www.bilibili.com/video/BV1wHDhYoE3G/<br>
注:固件仅供个人下载免费使用,禁止商用。
@@ -28,8 +29,11 @@ Bilibili连接https://www.bilibili.com/video/BV1wHDhYoE3G/<br>
* 其他
* 按钮->(PIN_14, GND)
* LED->22(板载)
* 电池ADC->32
2. 三色墨水屏排线插入时注意针脚方向,屏幕排线和驱动板排线1号针脚均是悬空,注意对齐。
3. 电池接口需要是ph2.0,且注意正负极(开发板上有标注),如果电池的正负极反了,可以用镊子调整电池插头。
1. 可选电池电压检测电路需要通过两个相同的大电阻建议500K以上串联通过分压进行检测电池电压。如图
<img src="./assets/img/battery_adc.png" width="30%"><br>
4. 烧录固件<br>
使用ESP32的烧录工具flash_download_tool烧录固件. [Flash Download Tools](https://www.espressif.com/en/support/download/other-tools?keys=flash+download+tools)
1. 选择烧录的文件和烧录地址bootloader.bin与partitions.bin烧录过一次后就不需要重复烧录了
@@ -44,8 +48,8 @@ Bilibili连接https://www.bilibili.com/video/BV1wHDhYoE3G/<br>
## Button Operation Guide:
1. **单点**
如果处在休眠中,唤醒系统,并强制刷新月历。
如果处在运行中,强制刷新日历。
如果处在休眠中,唤醒系统,并刷新月历。
如果处在运行中,强制刷新/切换日历。
如果处在配置中,不做任何处理。
2. **双击**
系统正常运行中,双击进入系统配置。(并强制停止WIFI相关操作,如获取天气。)
@@ -79,6 +83,12 @@ Bilibili连接https://www.bilibili.com/video/BV1wHDhYoE3G/<br>
* 日期Tag:<br>
1. 输入格式yyyyMMddxyyyy为年每年显示设为0000MM为月份每月显示设为00dd为日期x为tag的图标a:书签b金钱c笑脸d警告。例如00000015b每年每月15日旁边显示$符号00000312a每年3月12日显示书签符号。
2. 最多可以设置三个tag中间以分号隔开。例如00000015b;00000312a
* 课程表:<br>
输入格式,分多段,每段用“;”隔开。
1. 课程数段。"000"三位数字分别代表上午、下午、晚上课程数不可省略。如“430”代表上午4课下午三课晚上无课。
2. 每日课程(多段)。首位为星期数,后接当日的课程,以“,”分隔。如:“二,数学,语文,英语,体育,音乐,德法,”,表示周二的课程为数学、语文...等。
3. 样例: 430;一,语文,数学,体育,美术,科学,综合;二,数学,语文,英语,体育,音乐,德法;三,数学,英语,科学,语文,体育,书法,音乐;四,语文,数学,信息,劳动,德法,体育;五,英语,语文,数学,美术,心理,体育;
4. 注意:分隔用的“,”“;”,必须为英文符号,不可用中文符号。
* 保存配置后,系统自动重启。
3. Update. OTA升级
此项需要在浏览器内完成,通过ip地址访问配置页面,然后进入Update,选择固件文件后上传,等待。刷新完成后,页面会有成功提示。
@@ -97,7 +107,7 @@ A: 观察22针脚的LED是否点亮有闪烁说明固件已经刷入如果
3. Q: 可以支持哪种屏?<br>
A: 仅4.2寸三色屏(目前仅支持黑白红,将来支持黑白黄)。
4. Q: 我使用的屏应该烧录哪个固件?<br>
A: 从经验上预估SES的拆机屏选z21比较新一些的选z98如果是非常老的屏选z15。实在不行的话三个固件都刷一遍试试。<br>
A: 从经验上预估,大部分比较新一些的选z98SES的拆机屏选z21如果是非常老的屏选z15。实在不行的话三个固件都刷一遍试试。<br>
明确的丝印对应如下:<br>
|丝印|固件|
|-----|-----|
@@ -108,13 +118,29 @@ A: 从经验上预估SES的拆机屏选z21比较新一些的选z98
A: 1. 检查USB线连接是否正常。 <br>2. app的串口下拉框里是否检测到COM口。 <br>3. 把其他串口工具关闭防止占用COM口。
6. Q: 刷新固件的过程中提示报错。<br>
A: 1. 换根质量较好的或短一些的USB线或换个USB口插入。 <br>2. 可以将刷新的波特率降低一点如选择速率低一点的115200。
7. Q: 刷新固件后,需要重新配置吗?
7. Q: 刷新固件后,需要重新配置吗?<br>
A: 仅刷新app固件后配置是保留的所以无需重新配置。如果刷新了分区表partition.bin会将esp的nvs区刷新这时候需要重新配置。
8. Q: 填入和风天气的API Key和位置ID后没有成功获取天气信息。
A: 3月1日后注册的和风天气账户有API Host限制请下载1.0.25以后版本在配置时配置API Host信息。
8. Q: 填入和风天气的API Key和位置ID后没有成功获取天气信息。<br>
A: 2025年3月1日后注册的和风天气账户有API Host限制请下载1.0.25以后版本在配置时配置API Host信息。
8. Q: 如何切换日历和课程表显示?<br>
A: 在系统运行状态下(状态灯常亮),单击按键,稍等即可切换课程表的展示。
## Releases
### 1.0.25 (未测试)
### 1.1.3
* Fix: some bugs.
### 1.1.2
* Refine: 功耗
### 1.1.1
* New Featue: 增加对电池的测量和显示。
### 1.1.0
* New Feature: 课程表。
* Refine: 对针脚统一配置。
* Fix: some bugs.
### 1.0.27
* Fix: 闰月错误。bug from nongli lib
### 1.0.26
* Refine: 显示当前为第**周。国际标准ISO 8601以周一作为每周起始日
### 1.0.25
* Fix: 和风天气api变更需要输入API Host在配置页面增加配置项
### 1.0.24
* Refine: 配置页面location约束改为64长度允许输入经纬度设置天气位置e.g. 116.41,39.92

BIN
assets/img/battery_adc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
assets/img/sample2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

View File

@@ -1,39 +0,0 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -8,6 +8,8 @@
// !!!preferences key限制15字符
#define PREF_SI_CAL_DATE "SI_CAL_DATE" // 屏幕当前显示的日期
#define PREF_SI_WEEK_1ST "SI_WEEK_1ST" // 每周第一天0: 周日默认1:周一
#define PREF_SI_TYPE "SI_TYPE" // 屏幕显示类型
#define PREF_QWEATHER_HOST "QWEATHER_HOST" // QWEATHER HOST
#define PREF_QWEATHER_KEY "QWEATHER_KEY" // QWEATHER KEY/TOKEN
#define PREF_QWEATHER_TYPE "QWEATHER_TYPE" // 0: 每日天气1: 实时天气
@@ -15,6 +17,7 @@
#define PREF_CD_DAY_DATE "CD_DAY_DATE" // 倒计日
#define PREF_CD_DAY_LABLE "CD_DAY_LABLE" // 倒计日名称
#define PREF_TAG_DAYS "TAG_DAYS" // tag day
#define PREF_STUDY_SCHEDULE "STUDY_SCH" // 课程表
// 假期信息tm年假期日(int8),假期日(int8)...
#define PREF_HOLIDAY "HOLIDAY"

6
include/battery.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef __BATTERY_H__
#define __BATTERY_H__
int readBatteryVoltage();
#endif

View File

@@ -1,10 +1,6 @@
#ifndef ___LED_H__
#define ___LED_H__
#include <Arduino.h>
#define PIN_LED GPIO_NUM_22
void led_init();
void led_fast();
void led_slow();

View File

@@ -10,4 +10,6 @@ void si_weather();
int si_screen_status();
void si_screen();
void print_status();
void print_status();
void si_warning(const char* str);

27
include/wiring.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef __WIRING_H__
#define __WIRING_H__
#define LOLIN32_LITE
#ifdef LOLIN32_LITE
// Output PIN
#define SPI_MOSI GPIO_NUM_23
#define SPI_MISO GPIO_NUM_19 // Reserved
#define SPI_SCK GPIO_NUM_18
#define SPI_CS GPIO_NUM_5
#define SPI_DC GPIO_NUM_17
#define SPI_RST GPIO_NUM_16
#define SPI_BUSY GPIO_NUM_4
// I2C
#define I2C_SDA GPIO_NUM_21
#define I2C_SCL GPIO_NUM_22
// Other PIN
#define KEY_M GPIO_NUM_14 // 注意由于此按键负责唤醒因此需要选择支持RTC唤醒的PIN脚。
#define PIN_LED_R GPIO_NUM_22
#define PIN_ADC GPIO_NUM_32 // ADC
#endif
#endif

View File

@@ -1,46 +0,0 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -12,6 +12,8 @@
description = Ink Screen Calendar
default_envs =
z21
z15
z98
[common]
framework = arduino
@@ -23,6 +25,7 @@ lib_deps =
mathertel/OneButton@^2.6.1
https://github.com/JADE-Jerry/nongli.git
https://github.com/tignioj/ArduinoUZlib
https://github.com/Wh1teRabbitHU/RX8010SJ
[env:z98]
build_type = release
@@ -33,8 +36,7 @@ lib_deps = ${common.lib_deps}
upload_speed = 460800
monitor_speed = 115200
board_build.partitions = min_spiffs.csv
build_flags =
-D SI_DRIVER=98
build_flags = -D SI_DRIVER=98
[env:z21]
build_type = release
@@ -45,9 +47,7 @@ lib_deps = ${common.lib_deps}
upload_speed = 460800
monitor_speed = 115200
board_build.partitions = min_spiffs.csv
build_flags =
-D SI_DRIVER=21
;extra_scripts = post:extra_script.py
build_flags = -D SI_DRIVER=21
[env:z15]
build_type = release
@@ -58,8 +58,8 @@ lib_deps = ${common.lib_deps}
upload_speed = 460800
monitor_speed = 115200
board_build.partitions = min_spiffs.csv
build_flags =
-D SI_DRIVER=15
build_flags = -D SI_DRIVER=15
[env:esp32c3]
platform = espressif32
@@ -70,6 +70,6 @@ lib_deps = ${common.lib_deps}
upload_speed = 460800
monitor_speed = 115200
board_build.partitions = min_spiffs.csv
build_flags =
-DARDUINO_USB_MODE=1 ;开启USB Slave 功能
-DARDUINO_USB_CDC_ON_BOOT=1 ;开启CDC 下载功能宏
build_flags = -D SI_DRIVER=98
; -DARDUINO_USB_MODE=1
; -DARDUINO_USB_CDC_ON_BOOT=1

View File

@@ -8,7 +8,6 @@
#include <ArduinoUZlib.h> // 解压gzip
struct Weather {
String time;
int8_t temp;
@@ -106,9 +105,10 @@ private:
if (isGzip) {
// gzip解压缩
uint8_t* outBuf = NULL;
size_t outLen = 0;
uint32_t outLen = 0;
ArduinoUZlib::decompress((uint8_t*)s.c_str(), (uint32_t)s.length(), outBuf, outLen);
error = deserializeJson(doc, (char*)outBuf, outLen);
free(outBuf);
} else {
error = deserializeJson(doc, s);
}

35
src/battery.cpp Normal file
View File

@@ -0,0 +1,35 @@
/*
电压 (V) 近似剩余电量 状态说明
4.20 100% 刚刚充满,充电器断开
4.10 ~90% 电量非常充足
4.00 ~80% 电量充足
3.90 ~60% 中等电量
3.80 ~50% 中等电量(接近标称电压)
3.75 ~40% 电量偏低
3.70 ~30% 标称电压点,但电量已不多
3.65 ~20% 低电量
3.50 ~10% 极低电量,应立即充电
3.30 0% 放电截止电压,继续放电将损坏电池
充电截止电压 4.2
放电截止电压 3.3
*/
#include "driver/adc.h"
#include "esp_adc_cal.h"
/**
* 获取电池电压mV
*/
int readBatteryVoltage() {
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_11db, ADC_WIDTH_BIT_12, 1100, &adc_chars);
static const adc1_channel_t channel = ADC1_CHANNEL_4; // GPIO32
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(channel, ADC_ATTEN_11db);
int adc_val = adc1_get_raw(channel);
int voltage = esp_adc_cal_raw_to_voltage(adc_val, &adc_chars);
voltage *= 2;
return voltage;
}

View File

@@ -1,13 +1,14 @@
#include "led.h"
#define PIN_LED GPIO_NUM_22
#include <Arduino.h>
#include "wiring.h"
TaskHandle_t LED_HANDLER;
int8_t BLINK_TYPE;
void led_init()
{
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_LED_R, OUTPUT);
}
void task_led(void *param)
@@ -17,42 +18,42 @@ void task_led(void *param)
switch(BLINK_TYPE)
{
case 0:
digitalWrite(PIN_LED, HIGH); // Off
digitalWrite(PIN_LED_R, HIGH); // Off
vTaskDelay(pdMS_TO_TICKS(1000));
break;
case 1:
digitalWrite(PIN_LED, LOW); // On
digitalWrite(PIN_LED_R, LOW); // On
vTaskDelay(pdMS_TO_TICKS(1000));
break;
case 2:
digitalWrite(PIN_LED, LOW); // On
digitalWrite(PIN_LED_R, LOW); // On
vTaskDelay(pdMS_TO_TICKS(1000));
digitalWrite(PIN_LED, HIGH); // Off
digitalWrite(PIN_LED_R, HIGH); // Off
vTaskDelay(pdMS_TO_TICKS(1000));
break;
case 3:
digitalWrite(PIN_LED, LOW); // On
digitalWrite(PIN_LED_R, LOW); // On
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(PIN_LED, HIGH); // Off
digitalWrite(PIN_LED_R, HIGH); // Off
vTaskDelay(pdMS_TO_TICKS(200));
break;
case 4:
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(PIN_LED, LOW); // On
digitalWrite(PIN_LED_R, LOW); // On
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(PIN_LED, HIGH); // Off
digitalWrite(PIN_LED_R, HIGH); // Off
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(PIN_LED, LOW); // On
digitalWrite(PIN_LED_R, LOW); // On
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(PIN_LED, HIGH); // Off
digitalWrite(PIN_LED_R, HIGH); // Off
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(PIN_LED, LOW); // On
digitalWrite(PIN_LED_R, LOW); // On
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(PIN_LED, HIGH); // Off
digitalWrite(PIN_LED_R, HIGH); // Off
vTaskDelay(pdMS_TO_TICKS(1000));
break;
default:
digitalWrite(PIN_LED, HIGH); // Off
digitalWrite(PIN_LED_R, HIGH); // Off
vTaskDelay(pdMS_TO_TICKS(1000));
}
}

View File

@@ -3,7 +3,11 @@
#include <WiFiManager.h>
#include "OneButton.h"
#include "esp_sleep.h"
#include <wiring.h>
#include "battery.h"
#include "led.h"
#include "_sntp.h"
@@ -13,8 +17,8 @@
#include "version.h"
#define PIN_BUTTON GPIO_NUM_14 // 注意由于此按键负责唤醒因此需要选择支持RTC唤醒的PIN脚。
OneButton button(PIN_BUTTON, true);
#include "OneButton.h"
OneButton button(KEY_M, true);
void IRAM_ATTR checkTicks() {
button.tick();
@@ -31,6 +35,7 @@ WiFiManagerParameter para_cd_day_label("cd_day_label", "倒数日4字以内
WiFiManagerParameter para_cd_day_date("cd_day_date", "日期yyyyMMdd", "", 8, "pattern='\\d{8}'"); // 城市code
WiFiManagerParameter para_tag_days("tag_days", "日期TagyyyyMMddx详见README", "", 30); // 日期Tag
WiFiManagerParameter para_si_week_1st("si_week_1st", "每周起始0:周日1:周一)", "0", 2, "pattern='\\[0-1]{1}'"); // 每周第一天
WiFiManagerParameter para_study_schedule("study_schedule", "课程表", "0", 4000, "pattern='\\[0-9]{3}[;]$'"); // 每周第一天
void print_wakeup_reason() {
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
@@ -39,8 +44,22 @@ void print_wakeup_reason() {
Serial.println("Wakeup caused by external signal using RTC_IO");
break;
case ESP_SLEEP_WAKEUP_EXT1:
{
Serial.println("Wakeup caused by external signal using RTC_CNTL");
uint64_t status = esp_sleep_get_ext1_wakeup_status();
if (status == 0) {
Serial.println(" *None of the configured pins woke us up");
} else {
Serial.print(" *Wakeup pin mask: ");
Serial.printf("0x%016llX\r\n", status);
for (int i = 0; i < 64; i++) {
if ((status >> i) & 0x1) {
Serial.printf(" - GPIO%d\r\n", i);
}
}
}
break;
}
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Wakeup caused by timer");
break;
@@ -51,7 +70,7 @@ void print_wakeup_reason() {
Serial.println("Wakeup caused by ULP program");
break;
default:
Serial.printf("Wakeup was not caused by deep sleep.\n");
Serial.printf("Wakeup was not caused by deep sleep.\r\n");
}
}
@@ -70,7 +89,7 @@ void setup() {
Serial.begin(115200);
Serial.println(".");
print_wakeup_reason();
Serial.println("\r\n\r\n\r\n");
Serial.println("\r\n\r\n");
delay(10);
button.setClickMs(300);
@@ -79,7 +98,7 @@ void setup() {
button.attachDoubleClick(buttonDoubleClick, &button);
// button.attachMultiClick()
button.attachLongPressStop(buttonLongPressStop, &button);
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON), checkTicks, CHANGE);
attachInterrupt(digitalPinToInterrupt(KEY_M), checkTicks, CHANGE);
Serial.printf("***********************\r\n");
Serial.printf(" J-Calendar\r\n");
@@ -88,8 +107,24 @@ void setup() {
Serial.printf("Copyright © 2022-2025 JADE Software Co., Ltd. All Rights Reserved.\r\n\r\n");
led_init();
led_fast();
led_on();
delay(1000);
int voltage = readBatteryVoltage();
Serial.printf("Battery: %d mV\r\n", voltage);
if(voltage < 1000) {
Serial.println("[WARN]无电池。");
} else if(voltage < 3000) {
Serial.println("[WARN]电量低于3v系统休眠。");
go_sleep();
} else if (voltage < 3300) {
// 低于3.3v,电池电量用尽,屏幕给警告,然后关机。
Serial.println("[WARN]电量低于3.3v,警告并系统休眠。");
si_warning("电量不足,请充电!");
go_sleep();
}
Serial.println("Wm begin...");
led_fast();
wm.setHostname("J-Calendar");
wm.setEnableConfigPortal(false);
wm.setConnectTimeout(10);
@@ -158,10 +193,10 @@ void loop() {
// 未在配置状态,且屏幕刷新完成,进入休眠
if (!wm.getConfigPortalActive() && si_screen_status() > 0) {
if(_wifi_flag) {
if (_wifi_flag) {
go_sleep();
}
if(!_wifi_flag && millis() - _wifi_failed_millis > 10 * 1000) { // 如果wifi连接不成功等待10秒休眠
if (!_wifi_flag && millis() - _wifi_failed_millis > 10 * 1000) { // 如果wifi连接不成功等待10秒休眠
go_sleep();
}
}
@@ -181,6 +216,11 @@ void buttonClick(void* oneButton) {
Serial.println("In config status.");
} else {
Serial.println("Refresh screen manually.");
Preferences pref;
pref.begin(PREF_NAMESPACE);
int _si_type = pref.getInt(PREF_SI_TYPE);
pref.putInt(PREF_SI_TYPE, _si_type == 0 ? 1 : 0);
pref.end();
si_screen();
}
}
@@ -196,6 +236,7 @@ void saveParamsCallback() {
pref.putString(PREF_CD_DAY_DATE, para_cd_day_date.getValue());
pref.putString(PREF_TAG_DAYS, para_tag_days.getValue());
pref.putString(PREF_SI_WEEK_1ST, strcmp(para_si_week_1st.getValue(), "1") == 0 ? "1" : "0");
pref.putString(PREF_STUDY_SCHEDULE, para_study_schedule.getValue());
pref.end();
Serial.println("Params saved.");
@@ -232,6 +273,7 @@ void buttonDoubleClick(void* oneButton) {
String cddDate = pref.getString(PREF_CD_DAY_DATE);
String tagDays = pref.getString(PREF_TAG_DAYS);
String week1st = pref.getString(PREF_SI_WEEK_1ST, "0");
String studySchedule = pref.getString(PREF_STUDY_SCHEDULE);
pref.end();
para_qweather_host.setValue(qHost.c_str(), 64);
@@ -242,6 +284,7 @@ void buttonDoubleClick(void* oneButton) {
para_cd_day_date.setValue(cddDate.c_str(), 8);
para_tag_days.setValue(tagDays.c_str(), 30);
para_si_week_1st.setValue(week1st.c_str(), 1);
para_study_schedule.setValue(studySchedule.c_str(), 4000);
wm.setTitle("J-Calendar");
wm.addParameter(&para_si_week_1st);
@@ -252,6 +295,7 @@ void buttonDoubleClick(void* oneButton) {
wm.addParameter(&para_cd_day_label);
wm.addParameter(&para_cd_day_date);
wm.addParameter(&para_tag_days);
wm.addParameter(&para_study_schedule);
// std::vector<const char *> menu = {"wifi","wifinoscan","info","param","custom","close","sep","erase","update","restart","exit"};
std::vector<const char*> menu = { "wifi","param","update","sep","info","restart","exit" };
wm.setMenu(menu); // custom menu, pass vector
@@ -268,7 +312,6 @@ void buttonDoubleClick(void* oneButton) {
_idle_millis = millis();
}
// 重置系统,并重启
void buttonLongPressStop(void* oneButton) {
Serial.println("Button long press.");
@@ -286,71 +329,62 @@ void buttonLongPressStop(void* oneButton) {
#define TIMEOUT_TO_SLEEP 10 // seconds
time_t blankTime = 0;
void go_sleep() {
// 设置唤醒时间为下个偶数整点。
time_t now = time(NULL);
struct tm tmNow = { 0 };
// Serial.printf("Now: %ld -- %s\n", now, ctime(&now));
localtime_r(&now, &tmNow); // 时间戳转化为本地时间结构
uint64_t p;
// 根据配置情况来刷新如果未配置qweather信息则24小时刷新否则每2小时刷新
Preferences pref;
pref.begin(PREF_NAMESPACE);
String _qweather_key = pref.getString(PREF_QWEATHER_KEY, "");
pref.end();
time_t now;
time(&now);
struct tm local;
localtime_r(&now, &local);
if (_qweather_key.length() == 0 || weather_type() == 0) { // 没有配置天气或者使用按日天气,则第二天刷新。
Serial.println("Sleep to next day.");
now += 3600 * 24;
localtime_r(&now, &tmNow); // 将新时间转成tm
// Serial.printf("Set1: %ld -- %s\n", now, ctime(&now));
struct tm tmNew = { 0 };
tmNew.tm_year = tmNow.tm_year;
tmNew.tm_mon = tmNow.tm_mon; // 月份从0开始
tmNew.tm_mday = tmNow.tm_mday; // 日期
tmNew.tm_hour = 0; // 小时
tmNew.tm_min = 0; // 分钟
tmNew.tm_sec = 10; // 秒, 防止离线时出现时间误差所以延后10s
time_t set = mktime(&tmNew);
p = (uint64_t)(set - time(NULL));
Serial.printf("Sleep time: %ld seconds\n", p);
} else {
if (tmNow.tm_hour % 2 == 0) { // 将时间推后两个小时,偶整点刷新。
now += 7200;
} else {
now += 3600;
// Sleep to next day
int secondsToNextDay = (24 - local.tm_hour) * 3600 - local.tm_min * 60 - local.tm_sec;
if (secondsToNextDay <= 0) {
secondsToNextDay += 24 * 3600;
}
localtime_r(&now, &tmNow); // 将新时间转成tm
// Serial.printf("Set1: %ld -- %s\n", now, ctime(&now));
struct tm tmNew = { 0 };
tmNew.tm_year = tmNow.tm_year;
tmNew.tm_mon = tmNow.tm_mon; // 月份从0开始
tmNew.tm_mday = tmNow.tm_mday; // 日期
tmNew.tm_hour = tmNow.tm_hour; // 小时
tmNew.tm_min = 0; // 分钟
tmNew.tm_sec = 10; // 秒, 防止离线时出现时间误差所以延后10s
time_t set = mktime(&tmNew);
p = (uint64_t)(set - time(NULL));
Serial.printf("Sleep time: %ld seconds\n", p);
Serial.printf("Seconds to next day: %d seconds.\n", secondsToNextDay);
p = (uint64_t)(secondsToNextDay);
} else {
// Sleep to next even hour.
int secondsToNextHour = (60 - local.tm_min) * 60 - local.tm_sec;
if (secondsToNextHour <= 0) {
secondsToNextHour += 3600;
}
if ((local.tm_hour % 2) == 0) { // 如果是奇数点则多睡1小时
secondsToNextHour += 3600;
}
Serial.printf("Seconds to next even hour: %d seconds.\n", secondsToNextHour);
p = (uint64_t)(secondsToNextHour);
}
p += 10; // 额外增加10秒避免过早唤醒
esp_sleep_enable_timer_wakeup(p * (uint64_t)uS_TO_S_FACTOR);
esp_sleep_enable_ext0_wakeup(PIN_BUTTON, 0);
esp_sleep_enable_ext0_wakeup(KEY_M, LOW);
// 省电考虑关闭RTC外设和存储器
// esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); // RTC IO, sensors and ULP, 注意由于需要按键唤醒所以不能关闭否则会导致RTC_IO唤醒失败
// esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); // RTC IO, sensors and ULP, 注意由于需要按键唤醒所以不能关闭否则会导致RTC_IO唤醒(ext0)失败
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF); //
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF);
gpio_deep_sleep_hold_dis(); // 解除所有引脚的保持状态
// 省电考虑重置gpio平均每针脚能省8ua。
gpio_reset_pin(PIN_LED); // 减小deep-sleep电流
gpio_reset_pin(GPIO_NUM_5); // 减小deep-sleep电流
gpio_reset_pin(GPIO_NUM_17); // 减小deep-sleep电流
gpio_reset_pin(GPIO_NUM_16); // 减小deep-sleep电流
gpio_reset_pin(GPIO_NUM_4); // 减小deep-sleep电流
gpio_reset_pin(PIN_LED_R); // 减小deep-sleep电流
gpio_reset_pin(SPI_CS); // 减小deep-sleep电流
gpio_reset_pin(SPI_DC); // 减小deep-sleep电流
gpio_reset_pin(SPI_RST); // 减小deep-sleep电流
gpio_reset_pin(SPI_BUSY); // 减小deep-sleep电流
gpio_reset_pin(SPI_MOSI); // 减小deep-sleep电流
gpio_reset_pin(SPI_MISO); // 减小deep-sleep电流
gpio_reset_pin(SPI_SCK); // 减小deep-sleep电流
gpio_reset_pin(PIN_ADC); // 减小deep-sleep电流
gpio_reset_pin(I2C_SDA); // 减小deep-sleep电流
gpio_reset_pin(I2C_SCL); // 减小deep-sleep电流
delay(10);
Serial.println("Deep sleep...");

View File

@@ -1,10 +1,14 @@
#include "screen_ink.h"
#include <weather.h>
#include <API.hpp>
#include "holiday.h"
#include "nongli.h"
#include "battery.h"
int voltage;
#include <_preference.h>
#include <U8g2_for_Adafruit_GFX.h>
@@ -14,10 +18,10 @@
#include "font.h"
#define ROTATION 0
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, GxEPD2_DRIVER_CLASS::HEIGHT> display(GxEPD2_DRIVER_CLASS(/*CS=D8*/ 5, /*DC=D3*/ 17, /*RST=D4*/ 16, /*BUSY=D2*/ 4));
#include "wiring.h"
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, GxEPD2_DRIVER_CLASS::HEIGHT> display(GxEPD2_DRIVER_CLASS(/*CS=D8*/ SPI_CS, /*DC=D3*/ SPI_DC, /*RST=D4*/ SPI_RST, /*BUSY=D2*/ SPI_BUSY));
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
#define FONT_TEXT u8g2_font_wqy16_t_gb2312 // 224825bytes最大字库天气描述中“霾”只有此字库中有
#define FONT_SUB u8g2_font_wqy12_t_gb2312 // 次要字体u8g2最小字体
const String week_str[] = { "", "", "", "", "", "", "" };
@@ -41,6 +45,10 @@ const int jrLength = 11;
const int jrDate[] = { 101, 214, 308, 312, 501, 504, 601, 701, 801, 910, 1001, 1224, 1225 };
const String jrText[] = { "元旦", "情人节", "妇女节", "植树节", "劳动节", "青年节", "儿童节", "建党节", "建军节", "教师节", "国庆节", "平安夜", "圣诞节" };
int _si_type = 0;
String _study_schedule;
struct tm tmInfo = { 0 }; // 日历显示用的时间
struct
@@ -67,6 +75,11 @@ struct
int16_t cdDayX;
int16_t cdDayY;
int16_t statusX;
int16_t statusY;
int16_t statusW;
int16_t statusH;
int16_t weatherX;
int16_t weatherY;
int16_t weatherW;
@@ -98,9 +111,9 @@ void init_cal_layout_size() {
calLayout.weekX = 10;
calLayout.weekY = calLayout.topH - 5;
calLayout.lunarYearX = calLayout.topX + calLayout.topW;
calLayout.lunarYearX = calLayout.topX + calLayout.topW + 15;
calLayout.lunarYearY = calLayout.yearY / 2;
calLayout.lunarDayX = calLayout.topX + calLayout.topW;
calLayout.lunarDayX = calLayout.topX + calLayout.topW + 15;
calLayout.lunarDayY = calLayout.yearY;
calLayout.cdDayX = 0;
@@ -111,6 +124,11 @@ void init_cal_layout_size() {
calLayout.tW = 60;
calLayout.tH = calLayout.topH / 2;
calLayout.statusX = 300;
calLayout.statusY = 0;
calLayout.statusW = display.width() - calLayout.weatherX;
calLayout.statusH = 14;
calLayout.weatherX = 300;
calLayout.weatherY = calLayout.topY;
calLayout.weatherW = display.width() - calLayout.weatherX;
@@ -129,7 +147,7 @@ void init_cal_layout_size() {
calLayout.dayH = 44;
}
void draw_cal_layout() {
void draw_cal_header() {
uint16_t color;
u8g2Fonts.setFont(FONT_TEXT);
@@ -139,19 +157,19 @@ void draw_cal_layout() {
int16_t daysMagin = 4;
for (int i = 0; i < 7; i++) {
if((i + _week_1st) % 7 == 0 || (i + _week_1st) % 7 == 6) {
if ((i + _week_1st) % 7 == 0 || (i + _week_1st) % 7 == 6) {
color = GxEPD_RED;
} else {
color = GxEPD_BLACK;
}
// header background
if(i == 0) {
display.fillRect(0, calLayout.headerY, (display.width() - 7 * calLayout.dayW)/2, calLayout.headerH, color);
} else if(i == 6) {
display.fillRect((display.width() + 7 * calLayout.dayW)/2, calLayout.headerY, (display.width() - 7 * calLayout.dayW)/2, calLayout.headerH, color);
if (i == 0) {
display.fillRect(0, calLayout.headerY, (display.width() - 7 * calLayout.dayW) / 2, calLayout.headerH, color);
} else if (i == 6) {
display.fillRect((display.width() + 7 * calLayout.dayW) / 2, calLayout.headerY, (display.width() - 7 * calLayout.dayW) / 2, calLayout.headerH, color);
}
display.fillRect((display.width() - 7 * calLayout.dayW)/2 + i * calLayout.dayW, calLayout.headerY, calLayout.dayW, calLayout.headerH, color);
display.fillRect((display.width() - 7 * calLayout.dayW) / 2 + i * calLayout.dayW, calLayout.headerY, calLayout.dayW, calLayout.headerH, color);
// header text
u8g2Fonts.drawUTF8(calLayout.headerX + daysMagin + (calLayout.dayW - u8g2Fonts.getUTF8Width(week_str[i].c_str())) / 2 + i * calLayout.dayW, calLayout.headerY + calLayout.headerH - 3, week_str[(i + _week_1st) % 7].c_str());
}
@@ -196,14 +214,15 @@ void draw_cal_year(bool partial) {
calLayout.lunarYearX = u8g2Fonts.getCursorX() + 15;
calLayout.lunarDayX = u8g2Fonts.getCursorX() + 15;
// 星期几
// 第几周
u8g2Fonts.setFont(FONT_TEXT);
u8g2Fonts.setFontMode(1);
u8g2Fonts.setFontDirection(0);
u8g2Fonts.setForegroundColor(todayColor);
u8g2Fonts.setCursor(calLayout.weekX, calLayout.weekY);
u8g2Fonts.print("星期" + week_str[tmInfo.tm_wday]);
char week_num[8];
strftime(week_num, sizeof(week_num), "%V", &tmInfo); // 国际标准ISO 8601以周一作为每周起始日
u8g2Fonts.printf("第%s周", week_num);
calLayout.cdDayX = u8g2Fonts.getCursorX(); // update cd day X;
// 今日农历年份e.g. 乙巳年 蛇
@@ -246,7 +265,9 @@ void draw_cal_days(bool partial) {
totalDays = 31;
}
if (monthNum == 2) {
if ((tmInfo.tm_year + 1900) == 0 && (tmInfo.tm_year + 1900) % 100 != 0) {
if ((tmInfo.tm_year + 1900) % 4 == 0
&& (tmInfo.tm_year + 1900) % 100 != 0
|| (tmInfo.tm_year + 1900) % 400 == 0) {
totalDays = 29; // 闰二月
} else {
totalDays = 28; // 二月
@@ -288,7 +309,7 @@ void draw_cal_days(bool partial) {
}
pref.end();
if(_holiday.year != tmInfo.tm_year + 1900 || _holiday.month != tmInfo.tm_mon + 1) {
if (_holiday.year != tmInfo.tm_year + 1900 || _holiday.month != tmInfo.tm_mon + 1) {
_holiday = {};
}
@@ -310,7 +331,7 @@ void draw_cal_days(bool partial) {
} else {
color = GxEPD_BLACK;
}
if (tmInfo.tm_year + 1900 == _holiday.year && tmInfo.tm_mon + 1 == _holiday.month) {
uint8_t holidayIndex = 0;
for (; holidayIndex < _holiday.length; holidayIndex++) {
@@ -416,8 +437,8 @@ void draw_cal_days(bool partial) {
// 今日农历年份e.g. 乙巳年 蛇
// 如果农历月份小于公历月份,那么说明是上一年
int tg = nl_tg(tmInfo.tm_year + 1900 - (lunarMon > (tmInfo.tm_mon + 1)? 1:0));
int dz = nl_dz(tmInfo.tm_year + 1900 - (lunarMon > (tmInfo.tm_mon + 1)? 1:0));;
int tg = nl_tg(tmInfo.tm_year + 1900 - (lunarMon > (tmInfo.tm_mon + 1) ? 1 : 0));
int dz = nl_dz(tmInfo.tm_year + 1900 - (lunarMon > (tmInfo.tm_mon + 1) ? 1 : 0));;
todayLunarYear = String(nl_tg_text[tg]) + String(nl_dz_text[dz]) + "" + String(nl_sx_text[dz]);
// 今日农历日期
@@ -568,6 +589,7 @@ void draw_cd_day(String label, String date) {
}
}
void draw_special_day() {
String str = "Special Days!!!";
@@ -795,6 +817,154 @@ void draw_err(bool partial) {
}
}
void draw_status(bool partial) {
if (partial) {
display.setPartialWindow(calLayout.statusX, calLayout.statusY, calLayout.statusW, calLayout.statusH);
display.firstPage();
display.fillScreen(GxEPD_WHITE);
}
u8g2Fonts.setFontMode(1);
u8g2Fonts.setFontDirection(0);
u8g2Fonts.setBackgroundColor(GxEPD_WHITE);
u8g2Fonts.setForegroundColor(GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_siji_t_6x10);
// 电池icon
String iconStr = "";
if(voltage >= 4100) { // 满电
iconStr = "\ue24b";
} else if (voltage >= 3900) { // 多电
iconStr = "\ue249";
} else if (voltage >= 3700) { // 中电量
iconStr = "\ue247";
} else if (voltage >= 3500) { // 低电量
iconStr = "\ue245";
} else { // 空
iconStr = "\ue242";
}
u8g2Fonts.drawUTF8(400 - 12 - 4, 10, iconStr.c_str());
if (partial) {
display.nextPage();
}
}
void drawStudySchedule() {
int i = _study_schedule.substring(0, 3).toInt();
Serial.printf("%d\r\n", i);
int morningClassCount = i / 100;
int eveningClassCount = (i % 100) / 10;
int nightClassCount = i % 10;
int segmentCount = (morningClassCount > 0 ? 1 : 0) + (eveningClassCount > 0 ? 1 : 0) + (nightClassCount > 0 ? 1 : 0);
String daysClassStr[7];
int pos_begin = 4;
int daysCount = 0;
do {
int pos_end = _study_schedule.indexOf(";", pos_begin);
if (pos_end < 0) break;
String str = _study_schedule.substring(pos_begin, pos_end);
str.trim();
daysClassStr[daysCount++] = str;
// Serial.printf("%d : %d : %s\r\n", daysCount, pos_end, str.c_str());
pos_begin = pos_end + 1;
} while (true);
// 计算Cell尺寸
int cellHeight = (display.height() - calLayout.topH - calLayout.headerH - segmentCount * 2) / (morningClassCount + eveningClassCount + nightClassCount);
int cellWidth = display.width() / daysCount;
int marginLeft = (display.width() - cellWidth * daysCount) / 2;
// cell line
display.drawFastHLine(0, calLayout.topH, display.width(), GxEPD_RED);
display.drawFastHLine(0, calLayout.topH - 1, display.width(), GxEPD_RED);
display.drawFastHLine(0, calLayout.topH + calLayout.headerH - 1, display.width(), GxEPD_RED);
display.drawFastHLine(0, calLayout.topH + calLayout.headerH, display.width(), GxEPD_RED);
int segmentIndex = 0;
uint16_t max_y = calLayout.topH + calLayout.headerH; // 水平分割线
for (int x = 1; x <= morningClassCount; x++) {
max_y += cellHeight;
display.drawFastHLine(0, max_y, display.width(), GxEPD_BLACK);
}
if (eveningClassCount > 0) {
max_y += 2;
display.drawFastHLine(0, max_y, display.width(), GxEPD_BLACK);
}
for (int x = 1; x <= eveningClassCount; x++) {
max_y += cellHeight;
display.drawFastHLine(0, max_y, display.width(), GxEPD_BLACK);
}
if (nightClassCount > 0) {
max_y += 2;
display.drawFastHLine(0, max_y, display.width(), GxEPD_BLACK);
}
for (int x = 1; x <= nightClassCount; x++) {
max_y += cellHeight;
display.drawFastHLine(0, max_y, display.width(), GxEPD_BLACK);
}
for (int x = 1; x < daysCount; x++) { // 垂直分割线
display.drawFastVLine(cellWidth * x, calLayout.headerH + calLayout.topH, max_y - calLayout.headerH - calLayout.topH, GxEPD_BLACK);
}
for (int x = 0; x < daysCount; x++) {
// Serial.println(daysClassStr[x].c_str());
int pos_begin = 0;
int yStep = 0;
int segmentIndex = 0;
uint16_t fontColor = GxEPD_BLACK;
do {
int pos_end = daysClassStr[x].indexOf(",", pos_begin);
String ss = daysClassStr[x].substring(pos_begin, pos_end);
ss.trim();
// Serial.printf("%s\r\n", ss.c_str());
if (yStep == 0) {
if (week_str[tmInfo.tm_wday] == ss) {
display.fillRect(cellWidth * x + marginLeft, calLayout.topH, cellWidth, calLayout.headerH, GxEPD_RED);
fontColor = GxEPD_RED;
u8g2Fonts.setBackgroundColor(GxEPD_RED);
u8g2Fonts.setForegroundColor(GxEPD_WHITE);
} else {
fontColor = GxEPD_BLACK;
u8g2Fonts.setBackgroundColor(GxEPD_WHITE);
u8g2Fonts.setForegroundColor(fontColor);
}
u8g2Fonts.setFont(FONT_TEXT);
u8g2Fonts.drawUTF8(cellWidth * x + marginLeft + (cellWidth - u8g2Fonts.getUTF8Width(ss.c_str())) / 2, calLayout.topH + calLayout.headerH - 4, ss.c_str());
} else if (yStep > 0) {
if (yStep == morningClassCount + 1 || yStep == (morningClassCount + eveningClassCount + 1)) {
segmentIndex++;
}
u8g2Fonts.setBackgroundColor(GxEPD_WHITE);
u8g2Fonts.setForegroundColor(fontColor);
int fontHeight = 12;
if (cellHeight < 18) {
u8g2Fonts.setFont(FONT_SUB);
fontHeight = 12;
} else {
u8g2Fonts.setFont(FONT_TEXT);
fontHeight = 16;
}
u8g2Fonts.drawUTF8(cellWidth * x + marginLeft + (cellWidth - u8g2Fonts.getUTF8Width(ss.c_str())) / 2, calLayout.topH + calLayout.headerH + cellHeight * yStep - 1 - (cellHeight - 1 - fontHeight) / 2 + 2 * segmentIndex, ss.c_str());
}
if (pos_end < 0) break;
pos_begin = pos_end + 1;
yStep++;
} while (true);
}
// 显示当前周几, 农历年月不显示
todayLunarYear = "";
todayLunarDay = "";
uint16_t color = (tmInfo.tm_wday == 0 || tmInfo.tm_wday == 6) ? GxEPD_RED : GxEPD_BLACK;
u8g2Fonts.setBackgroundColor(GxEPD_WHITE);
u8g2Fonts.setForegroundColor(GxEPD_BLACK);
u8g2Fonts.setFont(FONT_TEXT);
u8g2Fonts.drawUTF8(calLayout.lunarDayX, calLayout.lunarDayY, ("星期" + week_str[tmInfo.tm_wday]).c_str());
}
///////////// Calendar //////////////
/**
* 处理日历信息
@@ -809,6 +979,10 @@ void si_calendar() {
_cd_day_date = pref.getString(PREF_CD_DAY_DATE);
_tag_days_str = pref.getString(PREF_TAG_DAYS);
_week_1st = pref.getString(PREF_SI_WEEK_1ST, "0").toInt();
_study_schedule = pref.getString(PREF_STUDY_SCHEDULE);
_si_type = pref.getInt(PREF_SI_TYPE);
if (_study_schedule.isEmpty()) _si_type = 0;
pref.end();
time_t now = 0;
@@ -836,8 +1010,7 @@ void si_calendar() {
time_t set = mktime(&tmInfo);
timeval tv;
tv.tv_sec = set;
settimeofday(&tv, nullptr);
isSetOK = true;
isSetOK = (settimeofday(&tv, nullptr) == 0);
Serial.println("WARN: Set system time by api time.");
} else {
Serial.println("ERR: Fail to format api time.");
@@ -874,27 +1047,38 @@ int si_calendar_status() {
void task_screen(void* param) {
Serial.println("[Task] screen update begin...");
voltage = readBatteryVoltage();
delay(100);
display.init(115200); // 串口使能 初始化完全刷新使能 复位时间 ret上拉使能
display.setRotation(ROTATION); // 设置屏幕旋转1和3是横向 0和2是纵向
u8g2Fonts.begin(display);
init_cal_layout_size();
display.setFullWindow();
display.firstPage();
display.fillScreen(GxEPD_WHITE);
draw_cal_layout();
draw_cal_days(false);
draw_cal_year(false);
do {
if (_si_type == 1) {
drawStudySchedule();
} else {
draw_cal_days(false);
draw_cal_header();
}
// 倒计日
draw_cd_day(_cd_day_label, _cd_day_date);
draw_cal_year(false);
if (weather_status() == 1) {
draw_weather(false);
} else if (weather_status() == 2) {
draw_err(false);
}
// 倒计日
draw_cd_day(_cd_day_label, _cd_day_date);
display.display();
if (weather_status() == 1) {
draw_weather(false);
}
if (voltage > 1000 && voltage < 4300) {
draw_status(false);
}
} while (display.nextPage());
int32_t _calendar_date = (tmInfo.tm_year + 1900) * 10000 + (tmInfo.tm_mon + 1) * 100 + tmInfo.tm_mday;
@@ -904,6 +1088,7 @@ void task_screen(void* param) {
pref.end();
display.powerOff(); // !!!important!!!, 关闭屏幕否则会多0.5ma的空载电流(全屏刷新的话会自动关闭,局部刷新必须手动关闭)
display.hibernate();
Serial.println("[Task] screen update end...");
_screen_status = 1;
@@ -935,4 +1120,41 @@ void print_status() {
Serial.printf("Weather: %d\n", weather_status());
Serial.printf("Calendar: %d\n", si_calendar_status());
Serial.printf("Screen: %d\n", si_screen_status());
}
void si_warning(const char* str) {
Serial.println("Screen warning...");
display.init(115200); // 串口使能 初始化完全刷新使能 复位时间 ret上拉使能
display.setRotation(ROTATION); // 设置屏幕旋转1和3是横向 0和2是纵向
u8g2Fonts.begin(display);
display.setFullWindow();
display.fillScreen(GxEPD_WHITE);
do {
u8g2Fonts.setFontMode(1);
u8g2Fonts.setFontDirection(0);
u8g2Fonts.setBackgroundColor(GxEPD_WHITE);
u8g2Fonts.setForegroundColor(GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_open_iconic_all_4x_t);
int space = 8;
int w = u8g2Fonts.getUTF8Width("\u0118") + space;
u8g2Fonts.setFont(FONT_TEXT);
w += u8g2Fonts.getUTF8Width(str);
u8g2Fonts.setForegroundColor(GxEPD_RED);
u8g2Fonts.setFont(u8g2_font_open_iconic_all_4x_t);
u8g2Fonts.setCursor((display.width() - w) / 2, (display.height() + 32) / 2);
u8g2Fonts.print("\u0118");
u8g2Fonts.setForegroundColor(GxEPD_BLACK);
u8g2Fonts.setCursor(u8g2Fonts.getCursorX() + space, u8g2Fonts.getCursorY() - 5);
u8g2Fonts.setFont(FONT_TEXT);
u8g2Fonts.print(str);
} while (display.nextPage());
display.powerOff();
display.hibernate();
}