diff --git a/TODO b/TODO index 5020464..38a3c67 100644 --- a/TODO +++ b/TODO @@ -1,16 +1 @@ ---- main.c --- -Battery icon -Check when to read battery - ---- UI --- -123456mm >A -123456mm >B - _ -XXXXXXXX | | -XXXXXXXX | | -XXXXXXXX |_| - -Battery Image - 24x48 px -2px White Border -2px Black Border -2px horizontal segments per 5% +Battery icon - how to calc percentage? diff --git a/code/main.c b/code/main.c index 2342166..fe43234 100644 --- a/code/main.c +++ b/code/main.c @@ -40,6 +40,13 @@ #define OLED_X_SIZE 128 #define OLED_Y_SIZE 64 +#define OLED_BATTERY_ICON_X1 (OLED_X_SIZE - (CHAR_SIZE + 1) * 2 * 2) +#define OLED_BATTERY_ICON_X2 (OLED_X_SIZE - 1) +#define OLED_BATTERY_ICON_Y1 2 +#define OLED_BATTERY_ICON_Y2 ((OLED_Y_SIZE / 8) - 1) +#define OLED_BATTERY_ICON_SIZE 2 +#define OLED_BATTERY_ICON_PERCENT 5 + // Time(ms) to keep the display on before sleep #define DISPLAY_DELAY 3000 @@ -262,6 +269,9 @@ void display_send_cmd(uint8_t cmd); // OLED send data helper void display_send_data(const uint8_t *data, uint16_t buflen); +// OLED print area helper +void display_set_area(uint8_t start_x, uint8_t end_x, uint8_t start_y, uint8_t end_y); + // OLED init void display_init(); @@ -609,6 +619,60 @@ void display_send_data(const uint8_t *data, uint16_t buflen) } } +void display_set_area(uint8_t start_x, uint8_t end_x, uint8_t start_y, uint8_t end_y) +{ + uint8_t cmd_list[6] = + { + 0x21, // Set Column Address + 0x00, // Start Address + 0x00, // End Address + 0x22, // Set Page Address + 0x00, // Start Page + 0x00 // End Page + }; + uint8_t i; + + if (start_x >= OLED_X_SIZE) + { + start_x = OLED_X_SIZE - 1; + } + + if (end_x >= OLED_X_SIZE) + { + end_x = OLED_X_SIZE - 1; + } + + if (end_x < start_x) + { + end_x = start_x; + } + + if (start_y >= (OLED_Y_SIZE / 8)) + { + start_y = (OLED_Y_SIZE / 8) - 1; + } + + if (end_y >= (OLED_Y_SIZE / 8)) + { + end_y = (OLED_Y_SIZE / 8) - 1; + } + + if (end_y < start_y) + { + end_y = start_y; + } + + cmd[1] = start_x; + cmd[2] = end_x; + cmd[4] = start_y; + cmd[5] = end_y; + + for (i = 0; i < sizeof(cmd_list); ++i) + { + display_send_cmd(cmd_list[i]); + } +} + void display_init() { uint16_t i; @@ -722,15 +786,6 @@ void get_symbol16(uint8_t index, uint8_t *out) void print_symbol(uint8_t symbol_idx, uint8_t x, uint8_t y, uint8_t invert) { uint8_t unfolded_symbol[CHAR_SIZE * 4]; - uint8_t cmd_list[6] = - { - 0x21, // Set Column Address - 0x00, // Start Address - 0x00, // End Address - 0x22, // Set Page Address - 0x00, // Start Page 0 - 0x00 // End Page - }; uint8_t i; get_symbol16(symbol_idx, unfolded_symbol); @@ -750,16 +805,7 @@ void print_symbol(uint8_t symbol_idx, uint8_t x, uint8_t y, uint8_t invert) x = OLED_X_SIZE - CHAR_SIZE * 2; } - cmd_list[1] = x; - cmd_list[2] = x + CHAR_SIZE * 2 - 1; - cmd_list[4] = y * 2; - cmd_list[5] = y * 2 + 1; - - for (i = 0; i < sizeof(cmd_list); ++i) - { - display_send_cmd(cmd_list[i]); - } - + display_set_area(x, x + CHAR_SIZE * 2 - 1, y * 2, y * 2 + 1); display_send_data(unfolded_symbol, sizeof(unfolded_symbol)); } @@ -894,10 +940,7 @@ void print_clear_menu_area() // End Y - Depends On OLED Size cmd_list[5] = (OLED_Y_SIZE / 8) - 1; - for (i = 0; i < sizeof(cmd_list); ++i) - { - display_send_cmd(cmd_list[i]); - } + display_set_area(0, OLED_BATTERY_ICON_X1 - 1, 2, (OLED_Y_SIZE / 8) - 1); for (i = 0; i < sizeof(data); ++i) { @@ -962,6 +1005,106 @@ void print_option(uint8_t option) void print_battery_icon() { + static uint8_t old_percent = 0xFF; + uint8_t buf[16]; + uint8_t i; + uint8_t y; + uint8_t percent; + uint8_t threshold; + uint8_t bits; + uint8_t mask; + + for (i = 0; i < sizeof(buf), ++i) + { + buf[i] = 0xFF; + } + +// Buffer safety +#if ((OLED_BATTERY_ICON_Y2 - OLED_BATTERY_ICON_Y1 + 1) * OLED_BATTERY_ICON_SIZE > sizeof(buf)) +#error "THIS Y OVERFLOWS" +#endif + + // Draw left/right borders only once + if (0xFF == old_percent) + { + // Draw left border + display_set_area(OLED_BATTERY_ICON_X1, OLED_BATTERY_ICON_X1 + OLED_BATTERY_ICON_SIZE - 1, + OLED_BATTERY_ICON_Y1, OLED_BATTERY_ICON_Y2); + display_send_data(buf, (OLED_BATTERY_ICON_Y2 - OLED_BATTERY_ICON_Y1 + 1) * OLED_BATTERY_ICON_SIZE); + + // Draw right border + display_set_area(OLED_BATTERY_ICON_X2 - 1, OLED_BATTERY_ICON_X2, + OLED_BATTERY_ICON_Y1, OLED_BATTERY_ICON_Y2); + display_send_data(buf, (OLED_BATTERY_ICON_Y2 - OLED_BATTERY_ICON_Y1 + 1) * OLED_BATTERY_ICON_SIZE); + } + + // TODO: Calc battery percent + percent = 50; + + // Do not draw if not needed + if ((old_percent / OLED_BATTERY_ICON_PERCENT) == (percent / OLED_BATTERY_ICON_PERCENT)) + { + old_percent = percent; + return; + } + + // Set bitmask + bits = 0x00; + for (i = 0; i < OLED_BATTERY_ICON_SIZE; ++i) + { + bits <<= 1; + bits += 1; + } + + // Threshold is artificially bigger to not draw over border + // On lower border it underflows + threshold = 100 - ((OLED_BATTERY_ICON_PERCENT + 1) / 2) + OLED_BATTERY_ICON_PERCENT * 2; + + for (y = OLED_BATTERY_ICON_Y1, y <= OLED_BATTERY_ICON_Y2; ++y) + { + mask = 0x00; + + for (i = 0; i < 8 / OLED_BATTERY_ICON_SIZE; ++i) + { + mask <<= OLED_BATTERY_ICON_SIZE; + if (percent > threshold) + { + mask |= bits; + } + + threshold -= OLED_BATTERY_ICON_PERCENT; + } + + // Add top border + if (0 == y) + { + mask |= (bits << (8 - OLED_BATTERY_ICON_SIZE)); + } + + // Add bottom border + if (OLED_BATTERY_ICON_Y2 == y) + { + mask |= bits; + } + + // Fill buffer + for (i = 0; i < sizeof(buf); ++i) + { + buf[i] = mask; + } + + // Diplay percent + display_set_area(OLED_BATTERY_ICON_X1 + OLED_BATTERY_ICON_SIZE * 2, + OLED_BATTERY_ICON_X2 - OLED_BATTERY_ICON_SIZE * 2, + y, y); + display_send_data(buf, OLED_BATTERY_ICON_X2 - OLED_BATTERY_ICON_X1 - + OLED_BATTERY_ICON_SIZE * 4 + 1); + } + +// Buffer safety +#if ((OLED_BATTERY_ICON_X2 - OLED_BATTERY_ICON_X1 - OLED_BATTERY_ICON_SIZE * 4 + 1) > sizeof(buf)) +#error "THIS X OVERFLOWS" +#endif }