pocket-hid: add backlight control commands etc

This commit is contained in:
Lukas F. Hartmann 2023-02-21 13:01:56 +01:00
parent 6270add22f
commit 5c381dbd63
5 changed files with 317 additions and 53 deletions

View File

@ -0,0 +1,4 @@
#!/bin/sh
gcc -O2 -o kbdgfx ./kbdgfx.c -lm

View File

@ -0,0 +1,128 @@
/*
kbdgfx.c -- Demo for drawing realtime graphics to the MNT Reform Keyboard
Copyright 2022 MNT Research GmbH (https://mntre.com)
License: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <unistd.h>
#define ROWS 6
#define COLS 12
#define BUFSZ (6+COLS*3)
#define FBUFSZ COLS*ROWS*3
// our unpacked, wasteful framebuffer (3 bytes per pixel)
uint8_t fb[FBUFSZ];
// the buffer we send over HID
uint8_t buf[BUFSZ];
void draw_sine(float t, uint8_t* dst, uint8_t r, uint8_t g, uint8_t b, float brite) {
for (int x=0; x<COLS; x++) {
float fy = 2.5 + sin(t + ((float)x/12.0 * 3.141))*2;
for (int y=0; y<ROWS; y++) {
float d = 1.0 / (10.0 * (abs((float)y - fy)));
if (d > 1) d = 1;
dst[3*(y*COLS + x)] |= (uint8_t)(r * d * brite);
dst[3*(y*COLS + x)+1] |= (uint8_t)(g * d * brite);
dst[3*(y*COLS + x)+2] |= (uint8_t)(b * d * brite);
}
}
}
void draw_box(uint8_t* dst, uint8_t r, uint8_t g, uint8_t b, float brite) {
for (int x=0; x<COLS; x++) {
for (int y=0; y<ROWS; y++) {
dst[3*(y*COLS + x)] |= (uint8_t)(r * brite);
dst[3*(y*COLS + x)+1] |= (uint8_t)(g * brite);
dst[3*(y*COLS + x)+2] |= (uint8_t)(b * brite);
}
}
}
int main(int argc, char** argv) {
// just a counter
uint32_t t = 0;
if (argc < 2) {
printf("Usage: sudo kbdgfx /dev/hidraw0 [bitmap.raw]\n");
exit(1);
}
// loop forever
while (1) {
// start with the command
buf[0] = 'x';
buf[1] = 'X';
buf[2] = 'R';
buf[3] = 'G';
buf[4] = 'B';
// clear
memset(fb, 0, FBUFSZ);
if (argc == 3) {
// read bitmap from file
FILE* bmf = fopen(argv[2],"r");
if (!bmf) {
printf("Couldn't open bitmap %s!\n", argv[2]);
exit(2);
}
int res = fread(fb, FBUFSZ, 1, bmf);
fclose(bmf);
if (res<1) {
printf("Couldn't read bitmap or wrong size.\n", argv[2]);
exit(3);
}
} else {
// graphics demo
// paint
//draw_sine((float)t*0.03, fb);
if (t>500) {
draw_sine((float)t*0.025+1.0, fb, 0xff, 0x00, 0x00, fmaxf(0.0, fminf(1.0, (700-((float)t/1.6))/200.0)));
draw_sine((float)t*0.025, fb, 0x00, 0x00, 0xff, fmaxf(0.0, fminf(1.0, (700-((float)t/1.5))/200.0)));
draw_sine((float)t*0.025-1.0, fb, 0x00, 0xff, 0x00, fmaxf(0.0, fminf(1.0, (700-((float)t/1.2))/200.0)));
if (t>700) {
draw_box(fb, 0xff, 0xff, 0xff, fmaxf(0.0, fminf(1.0, (((float)t/1.5)-500.0)/200.0)));
}
} else {
draw_sine((float)t*0.025+1.0, fb, 0xff, 0x00, 0x00, fmaxf(0.0, fminf(1.0, (((float)t)-100)/200.0)));
draw_sine((float)t*0.025, fb, 0x00, 0x00, 0xff, fmaxf(0.0, fminf(1.0, (((float)t/1.2)-200)/200.0)));
draw_sine((float)t*0.025-1.0, fb, 0x00, 0xff, 0x00, fmaxf(0.0, fminf(1.0, (((float)t/1.1)-300)/200.0)));
}
}
// send our buffer to the keyboard, one line at a time
for (int row = 0; row < 6; row++) {
FILE* f = fopen(argv[1],"w");
if (!f) {
printf("Couldn't open %s. Try sudo.\n", argv[1]);
exit(1);
}
buf[5] = row;
memcpy(buf+6, fb+row*(COLS*3), COLS*3);
fwrite(buf, BUFSZ, 1, f);
fclose(f);
}
// if we're in bitmap file mode, exit now
if (argc == 3) exit(0);
// ~50 FPS
usleep(100*20);
// ~2 FPS
//usleep(1000*500);
t++;
}
}

View File

@ -141,7 +141,7 @@ void on_uart_rx() {
char batinfo[32];
sprintf(batinfo, " %.1f%%", (double)percentage);
insert_bat_icon(batinfo, 0, percentage);
gfx_poke_str(0, 0, batinfo);
gfx_poke_str(7, 1, batinfo);
gfx_flush();
}
uart_rx_i = 0;
@ -255,7 +255,7 @@ int main(void)
ws2812_program_init(pio, sm, offset, PIN_LEDS, 800000, false);
led_set_brightness(0x80);
led_set_brightness(0x0);
gfx_init(false);
anim_hello();
@ -350,6 +350,19 @@ bool fn_key = 0; // Am I holding FN?
bool circle = 0; // Am I holding circle?
int command_sent = 0;
int soc_power_on = 0; // fixme
void remote_turn_on_som() {
uart_puts(UART_ID, "\r\n1p\r\n");
led_set_brightness(10);
soc_power_on = 1;
}
void remote_turn_off_som() {
uart_puts(UART_ID, "\r\n0p\r\n");
led_set_brightness(0);
soc_power_on = 0;
}
int process_keyboard(uint8_t* resulting_scancodes) {
// how many keys are pressed this round
@ -415,26 +428,18 @@ int process_keyboard(uint8_t* resulting_scancodes) {
// Is circle key pressed?
// FIXME HACK
if (keycode == KEY_COMPOSE &&
( matrix_state[1]
|| matrix_state[10]
) && !command_sent) {
if (matrix_state[1]) {
// 1
//remote_turn_som_power_on();
uart_puts(UART_ID, "\r\n1p\r\n");
led_set_brightness(10);
if (keycode == KEY_POWER && !command_sent) {
if (!soc_power_on) {
remote_turn_on_som();
command_sent = 1;
//anim_hello();
} else if (matrix_state[10]) {
//remote_turn_som_power_off();
led_set_brightness(0);
uart_puts(UART_ID, "\r\n0p\r\n");
} else {
remote_turn_off_som();
command_sent = 1;
}
} else if (keycode == HID_KEY_EXECUTE) {
} else if (keycode == KEY_COMPOSE) {
fn_key = 1;
//active_matrix = matrix_fn;
active_matrix = matrix_fn;
} else {
if (active_meta_mode) {
// not holding the same key?
@ -468,25 +473,8 @@ int process_keyboard(uint8_t* resulting_scancodes) {
// key not pressed
if (keycode == KEY_COMPOSE) {
command_sent = 0;
}
else if (keycode == HID_KEY_EXECUTE) {
fn_key = 0;
if (media_toggle) {
//active_matrix = matrix_fn_toggled;
} else {
//active_matrix = matrix;
}
} else if (keycode == KEY_SYSRQ) {
if (fn_key && circle) {
if (!media_toggle) {
media_toggle = 1;
//active_matrix = matrix_fn_toggled;
} else {
media_toggle = 0;
//active_matrix = matrix_fn;
}
}
circle = 0;
active_matrix = matrix;
}
}
}
@ -652,6 +640,35 @@ void led_task(uint32_t color) {
}
}
uint8_t led_rgb_buf[12*3*6];
void led_bitmap(uint8_t row, uint8_t* row_rgb) {
// row = 5 -> commit
if (row > 5) return;
uint8_t* store = &led_rgb_buf[row*3*12];
int offset = 0;
for (int x=0; x<3*12; x++) {
if (row == 5 && x == 0*3) offset = 3*3;
if (row == 5 && x == 2*3) offset = 7*3;
store[x] = row_rgb[x+offset];
if (row == 5 && x == 4*3) break;
}
if (row == 5) {
for (int y=0; y<6; y++) {
int w = 12;
if (y==5) w = 4;
uint8_t* bitmap = &led_rgb_buf[y*3*12];
for (int x=0; x<w; x++) {
uint32_t pixel_grb = (bitmap[1]<<16u) | (bitmap[2]<<8u) | bitmap[0];
pio_sm_put_blocking(pio0, 0, pixel_grb << 8u);
bitmap+=3;
}
}
}
}
// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
// tud_hid_report_complete_cb() is used to send the next report after previous one is complete
void hid_task(void)
@ -678,7 +695,7 @@ void hid_task(void)
}
int led_brightness = 0;
int led_hue = 0;
int led_hue = 127;
typedef struct RgbColor
{
@ -811,6 +828,17 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
return 0;
}
// hid commands are all 4-letter, so they fit in a 32 bit integer
#define cmd(_s) (*(uint32_t *)(_s))
#define CMD_TEXT_FRAME cmd("OLED") // fill the screen with a single wall of text
#define CMD_OLED_CLEAR cmd("WCLR") // clear the oled display
#define CMD_OLED_BITMAP cmd("WBIT") // (u16 offset, u8 bytes...) write raw bytes into the oled framebuffer
#define CMD_POWER_OFF cmd("PWR0") // turn off power rails
#define CMD_BACKLIGHT cmd("LITE") // keyboard backlight level
#define CMD_RGB_BACKLIGHT cmd("LRGB") // keyboard backlight rgb
#define CMD_RGB_BITMAP cmd("XRGB") // push rgb backlight bitmap
#define CMD_LOGO cmd("LOGO") // play logo animation
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
@ -818,7 +846,78 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
(void) instance;
(void) buffer;
if (report_type == HID_REPORT_TYPE_OUTPUT)
char repinfo[64];
//sprintf(repinfo, "Rep: %d/%d ", report_type, report_id);
//gfx_poke_str(1, 1, repinfo);
//sprintf(repinfo, "Len: %d ", bufsize);
//gfx_poke_str(1, 2, repinfo);
/*if (buffer) {
sprintf(repinfo, "%02x %02x %02x %02x %02x %02x", buffer[0], buffer[1], buffer[2],
buffer[3], buffer[4], buffer[5]);
gfx_poke_str(0, 0, repinfo);
}
gfx_flush();*/
//return;
if (bufsize < 5) return;
// FIXME
if (report_type == 2) {
// Big Reform style
if (report_id == 'x') {
const uint32_t command = (buffer[3]<<24u)|(buffer[2]<<16u)|(buffer[1]<<8u)|buffer[0];
if (command == CMD_TEXT_FRAME) {
gfx_clear();
gfx_on();
int c = 4;
for (uint8_t y=0; y<4; y++) {
for (uint8_t x=0; x<21; x++) {
if (buffer[c] == '\n') {
c++;
x = 0;
y++;
} else if (x < 21 && y < 4) {
gfx_poke(x, y, buffer[c++]);
}
if (c>=bufsize) break;
}
}
gfx_flush();
}
else if (command == CMD_POWER_OFF) {
//reset_menu();
//anim_goodbye();
remote_turn_off_som();
//keyboard_power_off();
//reset_keyboard_state();
}
else if (command == CMD_OLED_CLEAR) {
gfx_clear();
gfx_flush();
}
else if (command == CMD_OLED_BITMAP) {
matrix_render_direct((uint8_t*)buffer+4);
}
else if (command == CMD_RGB_BITMAP) {
// row, data (12 * 3 rgb bytes)
led_bitmap(buffer[4], (uint8_t*)buffer+5);
}
else if (command == CMD_RGB_BACKLIGHT) {
uint32_t pixel_grb = (buffer[5]<<16u) | (buffer[6]<<8u) | buffer[4];
led_task(pixel_grb);
}
else if (command == CMD_LOGO) {
anim_hello();
}
}
}
/*if (report_type == HID_REPORT_TYPE_OUTPUT)
{
// Set keyboard LED e.g Capslock, Numlock etc...
if (report_id == REPORT_ID_KEYBOARD)
@ -828,5 +927,5 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
//uint8_t const kbd_leds = buffer[0];
}
}
}*/
}

View File

@ -92,29 +92,62 @@ uint8_t matrix[KBD_MATRIX_SZ] = {
};
// When holding down HYPER
/*uint8_t matrix_fn[KBD_MATRIX_SZ] = {
uint8_t matrix_fn[KBD_MATRIX_SZ] = {
// Media keys on Hyper + F7-F12
KEY_ESC,
KEY_GRAVE,
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6,
HID_KEYBOARD_SC_MEDIA_BACKWARD,
HID_KEYBOARD_SC_MEDIA_PLAY,
HID_KEYBOARD_SC_MEDIA_FORWARD,
HID_KEYBOARD_SC_MEDIA_MUTE,
HID_KEYBOARD_SC_MEDIA_VOLUME_DOWN,
HID_KEYBOARD_SC_MEDIA_VOLUME_UP,
KEY_CIRCLE,
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_DELETE,
KEY_TAB,
KEY_F11,
KEY_F12,
KEY_F13,
KEY_R,
KEY_T,
KEY_Y,
KEY_U,
KEY_I,
KEY_LEFTBRACE,
KEY_RIGHTBRACE,
KEY_SEMICOLON,
MATRIX_DEFAULT_ROW_2,
MATRIX_DEFAULT_ROW_3,
MATRIX_DEFAULT_ROW_4,
MATRIX_DEFAULT_ROW_5,
MATRIX_DEFAULT_ROW_6
};*/
KEY_LEFTSHIFT,
KEY_Z,
KEY_X,
KEY_C,
KEY_V,
KEY_B,
KEY_N,
KEY_M,
KEY_COMMA,
KEY_DOT,
KEY_PAGEUP,
KEY_RIGHTSHIFT,
KEY_COMPOSE,
KEY_RIGHTMETA,
KEY_RIGHTALT,
KEY_BACKSLASH,
KEY_EQUAL,
KEY_POWER,
KEY_POWER,
KEY_MINUS,
KEY_SLASH,
KEY_HOME,
KEY_PAGEDOWN,
KEY_END
};
// Second layer (toggled by HYPER+CIRCLE)
/*uint8_t matrix_fn_toggled[KBD_MATRIX_SZ] = {

View File

@ -97,7 +97,7 @@
#define CFG_TUD_VENDOR 0
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE 16
#define CFG_TUD_HID_EP_BUFSIZE 64
#ifdef __cplusplus
}