ihsm-impromptu/firmware/src/main.cpp
2025-05-09 23:01:59 +02:00

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;
}
}