// // Created by steven on 11/14/2024. // #include #include #include #include #include #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; }