P2FA/c/src/p2fa.c
2024-11-27 13:15:59 -05:00

332 lines
9.4 KiB
C

//
// Created by steven on 11/14/2024.
//
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#include <gpiod.h>
#include <string.h>
#include "../libs/go_p2fa.h"
#include "../libs/e-Paper/EPD_2in13_V4.h"
#include "../libs/GUI/GUI_Paint.h"
#include "../libs/GUI/GUI_BMPfile.h"
#include "../libs/Config/Debug.h"
#define GPIO_CHIP "/dev/gpiochip0"
#define DEBOUNCE_TIME_MS 50
// GPIO Pins
#define BUTTON_UP 13
#define BUTTON_DOWN 19
#define BUTTON_LEFT 12
#define BUTTON_RIGHT 16
#define BUTTON_SELECT 27
#define BUTTON_ALT 22
#define BUTTON_BACK 6
#define BUTTON_POWER 20
typedef struct {
int gpio_pin;
int key_code;
} ButtonMap;
ButtonMap button_map[] = {
{BUTTON_UP, 'u'},
{BUTTON_DOWN, 'd'},
{BUTTON_LEFT, 'l'},
{BUTTON_RIGHT, 'r'},
{BUTTON_SELECT, 's'},
{BUTTON_ALT, 'a'},
{BUTTON_BACK, 'b'},
{BUTTON_POWER, 'p'},
};
#define NUM_BUTTONS 8
struct gpiod_chip *chip;
struct gpiod_line_bulk lines;
struct gpiod_line *line_pointers[NUM_BUTTONS];
int last_button_state[NUM_BUTTONS] = {0};
struct timespec last_press_time[NUM_BUTTONS];
void closeP2FA() {
EPD_2in13_V4_Init();
EPD_2in13_V4_Clear();
EPD_2in13_V4_Sleep();
DEV_Delay_ms(2000); // Important, at least 2s
DEV_Module_Exit();
for (int i = 0; i < NUM_BUTTONS; i++) {
gpiod_line_release(line_pointers[i]);
}
gpiod_chip_close(chip);
}
void Handler(int sigNum) {
Debug("Caught signal %d\n", sigNum);
closeP2FA();
exit(0);
}
int computeWidth(char* text, sFONT *font) {
return font->Width * strlen(text);
}
int readButtons() {
for (int i = 0; i < NUM_BUTTONS; i++) {
int value = gpiod_line_get_value(line_pointers[i]);
if (value < 0) {
perror("Failed to read GPIO value");
continue;
}
// Debounce logic
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
long elapsed_ms = (now.tv_sec - last_press_time[i].tv_sec) * 1000 +
(now.tv_nsec - last_press_time[i].tv_nsec) / 1000000;
if (value == 1 && last_button_state[i] == 0 && elapsed_ms > DEBOUNCE_TIME_MS) {
last_button_state[i] = 1;
last_press_time[i] = now;
return button_map[i].key_code;
}
if (value == 0) {
last_button_state[i] = 0;
}
}
return -1; // No button press detected
}
int getBtn() {
while (1) {
int key = readButtons();
if (key != -1) {
return key;
}
// Add a short delay to reduce CPU usage
DEV_Delay_ms(10);
}
}
//TODO Put display to sleep after so many refreshes
void code(UWORD *blackImage, char* confName) {
//int ch;
while(1) {
// ch = getch();
// if (ch != ERR) {
// if (ch == 'q') break;
// Debug("Key Pressed: %c\n", ch);
// }
Paint_NewImage(blackImage, EPD_2in13_V4_WIDTH, EPD_2in13_V4_HEIGHT, 90, WHITE);
EPD_2in13_V4_Init();
Paint_SelectImage(blackImage);
Paint_Clear(WHITE);
GoString confStr = {confName, strlen(confName)};
const char *code = getCode(confStr);
int codeLength = strlen(code);
if (codeLength <= 6) {
Paint_DrawString(5, 20, code, &Font65, WHITE, BLACK);
} else if (codeLength > 8) {
Paint_DrawString(5, 20, code, &Font40, WHITE, BLACK);
} else if (codeLength > 6) {
Paint_DrawString(5, 20, code, &Font56, WHITE, BLACK);
}
Paint_DrawString(8, 5, confName, &Font12, WHITE, BLACK);
Paint_DrawRectangle(10, 114, 240, 94, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
EPD_2in13_V4_Display_Base(blackImage);
Paint_NewImage(blackImage, EPD_2in13_V4_WIDTH, EPD_2in13_V4_HEIGHT, 90, WHITE);
Paint_SelectImage(blackImage);
clock_t startTime;
clock_t endTime;
int elapsedTime;
int progBarCurrentIndex = 11;
int progBarEndIndex;
int lastI = 30 * 1000;
for (int i = getTimeRemainingMS(30);; i = getTimeRemainingMS(30)) {
//ch = getch();
//if (ch != ERR) {
// if (ch == 'q') return;
//}
if (i > lastI) break;
startTime = clock();
double percentLeft = (double) i / (double) (30 * 1000);
progBarEndIndex = 228 - (int) (228 * percentLeft);
Paint_ClearWindows(progBarCurrentIndex, 113, progBarEndIndex, 95, WHITE);
int line = 15;
while (progBarCurrentIndex < progBarEndIndex) {
Paint_DrawLine(progBarCurrentIndex, 113, progBarCurrentIndex, 95, BLACK, DOT_PIXEL_1X1,
LINE_STYLE_SOLID);
++progBarCurrentIndex;
++line;
if (line == 21) line = 15;
}
EPD_2in13_V4_Display_Partial(blackImage);
endTime = clock();
elapsedTime = (int) ((endTime - startTime) / CLOCKS_PER_SEC);
if (elapsedTime < 1000) {
DEV_Delay_ms(1000 - (elapsedTime * 1000));
}
lastI = i;
}
}
}
void config(UWORD *blackImage, char* configName) {
GoString goConfName = { configName, strlen(configName) };
struct getConfigLines_return configLinesReturn = getConfigLines(goConfName);
int length = configLinesReturn.r1;
char** configLines = configLinesReturn.r0;
if (configLines == NULL || length == -1) {
Debug("Config not found\n");
return;
}
Paint_NewImage(blackImage, EPD_2in13_V4_WIDTH, EPD_2in13_V4_HEIGHT, 90, WHITE);
EPD_2in13_V4_Init();
Paint_SelectImage(blackImage);
Paint_Clear(WHITE);
for (int i = 0; i < length; i++) {
Paint_DrawString(5, 5 + (i * 16), configLines[i], &Font12, WHITE, BLACK);
}
EPD_2in13_V4_Display(blackImage);
//int ch = getch();
}
void drawTotpHome(UWORD *blackImage, char** configNames, int *length) {
Paint_NewImage(blackImage, EPD_2in13_V4_WIDTH, EPD_2in13_V4_HEIGHT, 0, WHITE);
Paint_SelectImage(blackImage);
Paint_Clear(WHITE);
for (int j = 0; j < *length; j++) {
if (configNames[j] == NULL) {
Debug("Null Config: %d\n", j);
}
Paint_DrawString(5, 5 + j * 20, configNames[j], &Font12, WHITE, BLACK);
}
EPD_2in13_V4_Display_Base(blackImage);
Paint_NewImage(blackImage, EPD_2in13_V4_WIDTH, EPD_2in13_V4_HEIGHT, 0, WHITE);
Paint_SelectImage(blackImage);
}
void totpHome(UWORD *blackImage) {
struct getConfigNames_return configNamesReturn = getConfigNames();
int length = configNamesReturn.r1;
char** configNames = configNamesReturn.r0;
if (configNames == NULL || length == 0) {
Debug("No configs found\n");
return;
}
drawTotpHome(blackImage, configNames, &length);
int i = 0;
Paint_DrawString(115, 5 + (i * 20), "<", &Font12, WHITE, BLACK);
EPD_2in13_V4_Display_Partial(blackImage);
int ch;
while (1) {
//timeout(-1);
ch = 'q'; //getch();
if (!ch) {
switch(ch) {
// case KEY_UP:
// if (i > 0) i--;
// Paint_ClearWindows(115, 5 + (i + 1) * 20, 122, 17 + (i + 1) * 20, WHITE);
// break;
// case KEY_DOWN:
// if (i < length - 1) i++;
// Paint_ClearWindows(115, 5 + (i - 1) * 20, 122, 17 + (i - 1) * 20, WHITE);
// break;
case '\n':
code(blackImage, configNames[i]);
drawTotpHome(blackImage, configNames, &length);
break;
case 'c':
config(blackImage, configNames[i]);
drawTotpHome(blackImage, configNames, &length);
break;
case 'q':
return;
}
}
Paint_DrawString(115, 5 + (i * 20), "<", &Font12, WHITE, BLACK);
EPD_2in13_V4_Display_Partial(blackImage);
}
freeCStringArray(configNames, length);
DEV_Delay_ms(2000);
}
int drawLoop() {
if (DEV_Module_Init() != 0) {
return -1;
}
EPD_2in13_V4_Init();
EPD_2in13_V4_Clear();
UBYTE *blackImage;
UWORD ImageSize = ((EPD_2in13_V4_WIDTH % 8 == 0) ? (EPD_2in13_V4_WIDTH / 8) : (EPD_2in13_V4_WIDTH / 8 + 1)) * EPD_2in13_V4_HEIGHT;
if ((blackImage = (UBYTE *) malloc(ImageSize)) == NULL) {
Debug("Failed to apply for black memory...\n");
return -1;
}
totpHome(blackImage);
return 0;
}
int main() {
signal(SIGINT, Handler);
signal(SIGTERM, Handler);
loadConfigs();
// Begin Init GPIO
chip = gpiod_chip_open(GPIO_CHIP);
if (!chip) {
printf("Failed to open GPIO chip\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < NUM_BUTTONS; i++) {
line_pointers[i] = gpiod_chip_get_line(chip, button_map[i].gpio_pin);
if (!line_pointers[i]) {
printf("Failed to get GPIO line\n");
exit(EXIT_FAILURE);
}
if (gpiod_line_request_input(line_pointers[i], "gpio_button") < 0) {
printf("Failed to request input for GPIO line\n");
gpiod_chip_close(chip);
exit(EXIT_FAILURE);
}
gpiod_line_bulk_init(&lines, NUM_BUTTONS, line_pointers);
}
// End Init GPIO
printf("Monitoring buttons. Press Ctrl+C to exit.\n");
while (1) {
printf("Button: %c\n", (char)getBtn());
}
//drawLoop();
closeP2FA();
return 0;
}