/*
Copyright 2023 Casey Borders
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include QMK_KEYBOARD_H
#include
#include "transactions.h"
#ifdef OLED_ENABLE
#define HEADER_SIZE 32
const char headerBase[] PROGMEM = {
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF,
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF,
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF,
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF
};
char header[HEADER_SIZE];
#define STATUS_WIDTH 15
#define STATUS_HEIGHT 32
#define STATUS_PIXELS (STATUS_WIDTH * 8)
#define STATUS_SIZE (STATUS_WIDTH * STATUS_HEIGHT)
const char logoBase[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x80, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x61, 0x61, 0x81, 0x81, 0x01, 0x01, 0xC1, 0xC1, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0xC3, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF0, 0xF0, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xF0, 0xF0, 0x00, 0x00, 0xF0, 0xF0, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xF0, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x0F, 0x0F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x06, 0x9E, 0x9E, 0x66, 0x66, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0xF8, 0xF8, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xF8, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x86, 0x86, 0x67, 0x67, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x86, 0x86, 0x00, 0x00, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7F, 0x7F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xC0, 0xC0, 0x31, 0x31, 0xC1, 0xC1, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3F, 0x3F, 0x30, 0x30, 0x3F, 0x3F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xF3, 0x7F, 0x3F, 0x7F, 0xFF, 0xFF, 0x3B, 0xF1, 0xF7, 0xFF, 0x3F, 0x39, 0x30, 0x00, 0x01, 0x01, 0x01, 0x83, 0x86, 0x0C, 0xF8, 0xF0, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xCF, 0x07, 0x1F, 0x3F, 0x3F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xDF, 0xFC, 0x7F, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0E, 0x1C, 0x18, 0x30, 0x30, 0x30, 0x60, 0x60, 0x60, 0x60, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00
};
const char workBase[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x3E, 0x3E, 0x3E, 0x3E, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x3E, 0x3E, 0x3E, 0x3E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x00, 0x00,
0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7C, 0x7C, 0x7C, 0x7C, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00,
0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x83, 0x83, 0x83, 0x83, 0x7F, 0x7F, 0x7F, 0x7F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, 0x07, 0xF8, 0xF8, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00
};
const char restBase[] PROGMEM = {
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x00, 0x00,
0x00, 0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x00,
0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7C, 0x7C, 0x7C, 0x7C, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00
};
char displayBuffer[STATUS_SIZE];
#define WORK_TIME ((uint32_t)25 * 60 * 1000)
#define REST_TIME ((uint32_t)5 * 60 * 1000)
typedef enum pomo_state {
_STOPPED,
_WORK,
_REST,
} pomo_state;
/* The master only needs to know if the timer is running or not */
bool isRunning = false;
/* The slave needs more info about the state and what's going on */
pomo_state state = _STOPPED;
uint8_t pomoCount = 0;
uint32_t pomoTimer;
uint32_t totalTime = 0;
uint32_t currTime = 0;
uint32_t lastTime = 0;
uint16_t lastProgress = 0;
const char* read_pomodoro_state(void) {
switch (state) {
case _STOPPED: return "STOPPED";
case _REST: return "REST";
case _WORK: return "WORK";
default: return "UNKNOWN";
}
}
const char* read_pomodoro_running(void) {
if (isRunning) { return "RUNNING"; }
else { return "STOPPED"; }
}
void update_display_base(void) {
switch (state) {
case _STOPPED: {
memcpy_P(displayBuffer, logoBase, STATUS_SIZE);
memset(header, 0, HEADER_SIZE);
} break;
case _WORK: {
memcpy_P(displayBuffer, workBase, STATUS_SIZE);
memcpy_P(header, headerBase, HEADER_SIZE);
} break;
case _REST: {
memcpy_P(displayBuffer, restBase, STATUS_SIZE);
memcpy_P(header, headerBase, HEADER_SIZE);
} break;
}
}
void update_cycle_display(void) {
if (state == _STOPPED) { return; }
uint8_t start = (HEADER_SIZE / 4) * (4 - pomoCount);
for (uint8_t i = start; i < HEADER_SIZE; i++) {
header[i] = ~header[i];
}
}
bool syncRunning = false;
void toggle_pomodoro(void) {
isRunning = !isRunning;
syncRunning = true;
}
void set_slave_pomodoro_running(uint8_t in_buflen, const void *in_data, uint8_t out_buflen, void *out_data) {
isRunning = *(bool*)in_data;
if (isRunning) {
/* Reset stuff and start the timer */
pomoTimer = timer_read32();
state = _WORK;
totalTime = WORK_TIME;
lastTime = 0;
lastProgress = 0;
currTime = 0;
pomoCount = 1;
update_display_base();
update_cycle_display();
} else {
state = _STOPPED;
update_display_base();
}
}
void keyboard_post_init_user(void) {
memcpy_P(displayBuffer, logoBase, STATUS_SIZE);
memset(header, 0, HEADER_SIZE);
transaction_register_rpc(SYNC_POMODORO_RUNNING, set_slave_pomodoro_running);
}
void update_pomodoro_display(void) {
oled_set_cursor(0, 0);
oled_write_raw(displayBuffer, STATUS_SIZE);
oled_set_cursor(0, STATUS_WIDTH);
oled_write_raw(header, HEADER_SIZE);
}
void update_time_display(void) {
if (currTime > lastTime) {
float ratio = (float)currTime / totalTime;
uint16_t progress = (uint16_t)(ratio * STATUS_PIXELS);
for (int i = lastProgress + 1; i <= progress; i++) {
uint16_t row = i / 8;
uint16_t bit = i % 8;
char mask = 1 << bit;
for (int j = 0; j < STATUS_HEIGHT; j++) {
uint16_t index = row * STATUS_HEIGHT + j;
if (index > STATUS_SIZE) break;
displayBuffer[index] = displayBuffer[index] ^ mask;
}
}
lastTime = currTime;
lastProgress = progress;
}
}
void housekeeping_task_user(void) {
if (is_keyboard_master()) {
if (syncRunning && transaction_rpc_send(SYNC_POMODORO_RUNNING, sizeof(bool), &isRunning)) {
syncRunning = false;
}
} else {
/* This is where the time will be updated */
if (isRunning) {
currTime = timer_elapsed32(pomoTimer);
if (currTime >= totalTime) {
if (state == _WORK) {
state = _REST;
totalTime = REST_TIME;
} else if (state == _REST) {
state = _WORK;
totalTime = WORK_TIME;
pomoCount++;
}
if (pomoCount > 4) {
isRunning = false;
state = _STOPPED;
}
pomoTimer = timer_read32();
currTime = 0;
lastTime = 0;
lastProgress = 0;
update_display_base();
update_cycle_display();
} else {
update_time_display();
}
}
}
}
#endif // OLED_ENABLE