269 lines
6.5 KiB
C++
269 lines
6.5 KiB
C++
#include <Arduino.h>
|
|
#include <base64.h>
|
|
#include <qrcode.h>
|
|
#include <soc/rtc_wdt.h>
|
|
#include <esp_adc_cal.h>
|
|
#include <time.h>
|
|
#include <esp_pm.h>
|
|
#include <esp_system.h>
|
|
#include <esp_task_wdt.h>
|
|
#include <mbedtls/md.h>
|
|
#include <SPIFFS.h>
|
|
#include <StreamString.h>
|
|
#include <time.h>
|
|
#include <OneButton.h>
|
|
|
|
#include "main.h"
|
|
|
|
size_t qr_w = 0;
|
|
uint16_t qr_img[128*128];
|
|
|
|
int qr_version = 4;
|
|
int qr_scale = 3;
|
|
|
|
OneButton bt_a, bt_b;
|
|
|
|
void long_press_a(void);
|
|
void click_a(void);
|
|
void click_b(void);
|
|
void update_display(void);
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
/* the thing just kept spuriously triggering. */
|
|
esp_task_wdt_deinit();
|
|
rtc_wdt_protect_off();
|
|
rtc_wdt_disable();
|
|
disableCore0WDT();
|
|
disableCore1WDT();
|
|
|
|
ledcSetup(backlightChannel, 12000, 8);
|
|
ledcAttachPin(TFT_BL, backlightChannel);
|
|
ledcWrite(backlightChannel, 255);
|
|
tft.init();
|
|
tft.fillScreen(TFT_BLACK);
|
|
|
|
bt_a.setup(0);
|
|
bt_b.setup(35);
|
|
|
|
struct timeval now = {1746823581};
|
|
settimeofday(&now, nullptr);
|
|
|
|
SPIFFS.begin(true);
|
|
|
|
tft.fillScreen(TFT_RED);
|
|
|
|
QRCode qrcode;
|
|
uint8_t *qr_buf = new uint8_t[qrcode_getBufferSize(qr_version)];
|
|
qrcode_initText(&qrcode, qr_buf, qr_version, ECC_LOW, "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example");
|
|
qr_w = qrcode.size * qr_scale;
|
|
|
|
for (int y=0; y<qrcode.size; y++) {
|
|
for (int sy=0; sy<qr_scale; sy++) {
|
|
for(int x=0; x<qrcode.size; x++) {
|
|
for (int sx=0; sx<qr_scale; sx++) {
|
|
qr_img[x*qr_scale + sx +qr_w*(y*qr_scale + sy)] = qrcode_getModule(&qrcode, x, y) ? TFT_BLACK : TFT_WHITE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete qr_buf;
|
|
|
|
tft.fillScreen(TFT_WHITE);
|
|
tft.startWrite();
|
|
tft.pushImage((TFT_WIDTH - qr_w)/2, (TFT_HEIGHT - qr_w)/2, qr_w, qr_w, (uint16_t*)qr_img);
|
|
tft.endWrite();
|
|
|
|
bt_a.attachClick(click_a);
|
|
bt_a.attachLongPressStart(long_press_a);
|
|
bt_b.attachClick(click_b);
|
|
|
|
Serial.println("Boot");
|
|
}
|
|
|
|
enum {
|
|
ST_INIT = 0,
|
|
ST_STANDBY,
|
|
ST_SET_TIME,
|
|
} ui_state = ST_INIT;
|
|
int cursor_pos = 0;
|
|
bool display_valid = true;
|
|
|
|
uint64_t last_refresh = 0;
|
|
void loop() {
|
|
bt_a.tick();
|
|
bt_b.tick();
|
|
|
|
uint64_t now = millis();
|
|
if (now - last_refresh > 500) {
|
|
display_valid = false;
|
|
last_refresh = now;
|
|
}
|
|
|
|
if (!display_valid) {
|
|
update_display();
|
|
}
|
|
}
|
|
|
|
void update_display() {
|
|
if (ui_state == ST_INIT) {
|
|
Serial.println("ST_INIT");
|
|
|
|
} else if (ui_state == ST_STANDBY) {
|
|
Serial.println("ST_STANDBY");
|
|
tft.fillScreen(TFT_BLACK);
|
|
|
|
} else if (ui_state == ST_SET_TIME) {
|
|
Serial.println("ST_SET_TIME");
|
|
tft.fillScreen(TFT_BLACK);
|
|
tft.setCursor(10, 10);
|
|
|
|
time_t tt = time(nullptr);
|
|
struct tm *t = localtime(&tt);
|
|
|
|
char buf[128];
|
|
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
|
|
t->tm_year + 1900,
|
|
t->tm_mon + 1,
|
|
t->tm_mday,
|
|
t->tm_hour,
|
|
t->tm_min,
|
|
t->tm_sec);
|
|
String msg(buf);
|
|
|
|
int adjusted_cursor = cursor_pos + 2;
|
|
adjusted_cursor += cursor_pos / 2;
|
|
|
|
if (cursor_pos < 10) {
|
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
|
tft.print(msg.substring(0, adjusted_cursor));
|
|
|
|
tft.setTextColor(TFT_BLACK, TFT_WHITE);
|
|
tft.print(msg[adjusted_cursor]);
|
|
|
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
|
tft.print(msg.substring(adjusted_cursor+1, msg.length()));
|
|
|
|
} else {
|
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
|
tft.print(msg.substring(0, adjusted_cursor));
|
|
|
|
tft.setTextColor(TFT_BLACK, TFT_WHITE);
|
|
tft.print(msg.substring(adjusted_cursor, msg.length()));
|
|
}
|
|
|
|
} else {
|
|
tft.fillScreen(TFT_RED);
|
|
}
|
|
display_valid = true;
|
|
}
|
|
|
|
void long_press_a() {
|
|
Serial.println("Long A");
|
|
if (ui_state == ST_SET_TIME) {
|
|
ui_state = ST_STANDBY;
|
|
display_valid = false;
|
|
|
|
} else if (ui_state == ST_STANDBY) {
|
|
ui_state = ST_SET_TIME;
|
|
display_valid = false;
|
|
}
|
|
}
|
|
|
|
void click_a() {
|
|
Serial.println("Click A");
|
|
if (ui_state == ST_INIT) {
|
|
ui_state = ST_SET_TIME;
|
|
display_valid = false;
|
|
|
|
} else if (ui_state == ST_SET_TIME) {
|
|
cursor_pos ++;
|
|
if (cursor_pos == 11) {
|
|
ui_state = ST_STANDBY;
|
|
cursor_pos = 0;
|
|
}
|
|
display_valid = false;
|
|
}
|
|
}
|
|
|
|
bool is_leap_year(int year) {
|
|
if (year % 4 == 0) {
|
|
if (year % 100 == 0) {
|
|
if (year % 400 == 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int month_length(int year, int month) {
|
|
switch (month) { /* note: month is 0-indexed */
|
|
case 1: return is_leap_year(year) ? 29 : 28;
|
|
case 3: return 30;
|
|
case 5: return 30;
|
|
case 8: return 30;
|
|
case 10: return 30;
|
|
default: return 31;
|
|
}
|
|
}
|
|
|
|
void click_b() {
|
|
Serial.println("Click B");
|
|
if (ui_state == ST_SET_TIME) {
|
|
|
|
time_t tt = time(nullptr);
|
|
struct tm *t = localtime(&tt);
|
|
struct tm mod = *t;
|
|
|
|
int adj = (cursor_pos%2 == 0) ? 10 : 1;
|
|
switch (cursor_pos/2) {
|
|
case 0:
|
|
mod.tm_year += adj;
|
|
if (mod.tm_year >= 2100 - 1900)
|
|
mod.tm_year = 2000 - 1900;
|
|
break;
|
|
case 1:
|
|
mod.tm_mon += adj;
|
|
if (mod.tm_mon >= 12)
|
|
mod.tm_mon = 0;
|
|
break;
|
|
case 2:
|
|
mod.tm_mday += adj;
|
|
if (mod.tm_mday > month_length(mod.tm_year, mod.tm_mon))
|
|
mod.tm_mday = 1;
|
|
break;
|
|
case 3:
|
|
mod.tm_hour += adj;
|
|
if (mod.tm_hour >= 24)
|
|
mod.tm_hour = 0;
|
|
break;
|
|
case 4:
|
|
mod.tm_min += adj;
|
|
if (mod.tm_min >= 60)
|
|
mod.tm_min = 0;
|
|
break;
|
|
case 5:
|
|
mod.tm_sec = 0;
|
|
break;
|
|
}
|
|
|
|
struct timeval new_tv = {
|
|
.tv_sec = mktime(&mod),
|
|
};
|
|
Serial.print("Old time ");
|
|
Serial.print(tt);
|
|
Serial.print(" new time ");
|
|
Serial.print(new_tv.tv_sec);
|
|
Serial.print(" cursor ");
|
|
Serial.print(cursor_pos);
|
|
Serial.print(" adj ");
|
|
Serial.println(adj);
|
|
settimeofday(&new_tv, nullptr);
|
|
display_valid = false;
|
|
}
|
|
}
|
|
|