pocket-hid: port OLED menu from reform incl battery status, some refactoring

This commit is contained in:
Lukas F. Hartmann 2024-04-12 23:36:30 +02:00
parent d12064b469
commit ee66d3c149
No known key found for this signature in database
GPG Key ID: 376511EB67AD7BAF
7 changed files with 683 additions and 222 deletions

View File

@ -17,6 +17,8 @@ target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
${CMAKE_CURRENT_SOURCE_DIR}/src/oled.c
${CMAKE_CURRENT_SOURCE_DIR}/src/remote.c
${CMAKE_CURRENT_SOURCE_DIR}/src/menu.c
)
# Example include

View File

@ -0,0 +1,22 @@
/*
MNT Reform 2.0 Keyboard Firmware
See keyboard.c for Copyright
SPDX-License-Identifier: MIT
*/
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
#define PREF_HID_FW_REV "PREFHID20240412"
void reset_keyboard_state(void);
void led_set(uint32_t rgb);
void led_task(uint32_t rgb);
void led_mod_hue(int d);
void led_mod_brightness(int d);
void led_set_brightness(int b);
void led_mod_saturation(int d);
void led_cycle_hue();
#endif

View File

@ -1,27 +1,8 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
SPDX-License-Identifier: MIT
Copyright (c) 2019 Ha Thach (tinyusb.org)
Copyright (c) 2021-2024 MNT Research GmbH (mntre.com)
*/
#include <stdlib.h>
#include <stdio.h>
@ -39,6 +20,9 @@
#include "usb_descriptors.h"
#include "oled.h"
#include "menu.h"
#include "remote.h"
#include "keyboard.h"
#include "ws2812.pio.h"
@ -86,12 +70,6 @@ static int pressed_keys = 0;
static volatile uint32_t led_value = 0;
void hid_task(void);
void led_set(uint32_t rgb);
void led_task(uint32_t rgb);
void led_mod_hue(int d);
void led_mod_brightness(int d);
void led_set_brightness(int b);
void led_cycle_hue();
int process_keyboard(uint8_t* resulting_scancodes);
#define UART_ID uart1
@ -100,75 +78,6 @@ int process_keyboard(uint8_t* resulting_scancodes);
#define STOP_BITS 1
#define PARITY UART_PARITY_NONE
static char uart_rx_line[128];
static int uart_rx_i=0;
void insert_bat_icon(char* str, int x, float v) {
char icon = 0;
if (v>=80) {
icon = 8;
} else if (v>=60) {
icon = 6;
} else if (v>=40) {
icon = 4;
} else if (v>=20) {
icon = 2;
} else {
icon = 0;
}
str[x] = 4*32+icon;
str[x+1] = 4*32+icon+1;
}
void on_uart_rx() {
while (uart_is_readable(UART_ID)) {
char c = uart_getc(UART_ID);
if (uart_rx_i<127) {
uart_rx_line[uart_rx_i++] = c;
uart_rx_line[uart_rx_i] = 0;
} else {
uart_rx_i = 0;
}
if (c == '\n') {
// TODO hack
if (uart_rx_i>6) {
gfx_clear();
//gfx_poke_str(0, 3, uart_rx_line);
// cut off after 4 digits
uart_rx_line[4] = 0;
float percentage = ((float)atoi(uart_rx_line))/(float)10.0;
char batinfo[32];
sprintf(batinfo, " %.1f%%", (double)percentage);
insert_bat_icon(batinfo, 0, percentage);
gfx_poke_str(7, 1, batinfo);
gfx_flush();
}
uart_rx_i = 0;
}
}
}
void anim_hello(void) {
gfx_clear();
gfx_on();
for (int y=0; y<3; y++) {
for (int x=0; x<12; x++) {
gfx_poke((uint8_t)(x+4),(uint8_t)(y+1),(uint8_t)((5+y)*32+x));
}
gfx_flush();
}
for (uint8_t y=0; y<0xff; y++) {
gfx_contrast(y);
sleep_ms(0);
}
for (uint8_t y=0; y<0xff; y++) {
gfx_contrast(0xff-y);
sleep_ms(0);
}
}
/*------------- MAIN -------------*/
int main(void)
{
@ -191,40 +100,28 @@ int main(void)
gpio_init(PIN_COL1);
gpio_set_dir(PIN_COL1, true);
//gpio_pull_up(PIN_COL1);
gpio_init(PIN_COL2);
gpio_set_dir(PIN_COL2, true);
//gpio_pull_up(PIN_COL2);
gpio_init(PIN_COL3);
gpio_set_dir(PIN_COL3, true);
//gpio_pull_up(PIN_COL3);
gpio_init(PIN_COL4);
gpio_set_dir(PIN_COL4, true);
//gpio_pull_up(PIN_COL4);
gpio_init(PIN_COL5);
gpio_set_dir(PIN_COL5, true);
//gpio_pull_up(PIN_COL5);
gpio_init(PIN_COL6);
gpio_set_dir(PIN_COL6, true);
//gpio_pull_up(PIN_COL6);
gpio_init(PIN_COL7);
gpio_set_dir(PIN_COL7, true);
//gpio_pull_up(PIN_COL7);
gpio_init(PIN_COL8);
gpio_set_dir(PIN_COL8, true);
//gpio_pull_up(PIN_COL8);
gpio_init(PIN_COL9);
gpio_set_dir(PIN_COL9, true);
//gpio_pull_up(PIN_COL9);
gpio_init(PIN_COL10);
gpio_set_dir(PIN_COL10, true);
//gpio_pull_up(PIN_COL10);
gpio_init(PIN_COL11);
gpio_set_dir(PIN_COL11, true);
//gpio_pull_up(PIN_COL11);
gpio_init(PIN_COL12);
gpio_set_dir(PIN_COL12, true);
//gpio_pull_up(PIN_COL12);
gpio_init(PIN_ROW1);
gpio_set_dir(PIN_ROW1, false);
@ -267,11 +164,12 @@ int main(void)
led_set_brightness(0x0);
gfx_init(false);
anim_hello();
//anim_hello();
irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
irq_set_exclusive_handler(UART_IRQ, remote_on_uart_rx);
irq_set_enabled(UART_IRQ, true);
uart_set_irq_enables(UART_ID, true, false); // bool rx_has_data, bool tx_needs_data
// bool rx_has_data, bool tx_needs_data
uart_set_irq_enables(UART_ID, true, false);
while (1) {
pressed_keys = process_keyboard(pressed_scancodes);
@ -351,31 +249,38 @@ bool tud_hid_trackball_report(uint8_t report_id,
uint8_t matrix_debounce[KBD_COLS*KBD_ROWS];
uint8_t matrix_state[KBD_COLS*KBD_ROWS];
int active_meta_mode = 0;
uint8_t last_meta_key = 0;
uint8_t* active_matrix = matrix;
bool media_toggle = 0;
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;
// enter the menu
void enter_meta_mode(void) {
active_meta_mode = 1;
reset_and_render_menu();
if (!remote_get_power_state()) {
led_set_brightness(10);
}
}
void remote_turn_off_som() {
uart_puts(UART_ID, "\r\n0p\r\n");
led_set_brightness(0);
soc_power_on = 0;
void exit_meta_mode(void) {
active_meta_mode = 0;
if (!remote_get_power_state()) {
led_set_brightness(0);
}
}
void remote_wake_som() {
uart_puts(UART_ID, "\r\n1w\r\n");
anim_hello();
void reset_keyboard_state(void) {
for (int i = 0; i < KBD_COLS*KBD_ROWS; i++) {
matrix_debounce[i] = 0;
matrix_state[i] = 0;
}
last_meta_key = 0;
reset_menu();
}
int process_keyboard(uint8_t* resulting_scancodes) {
@ -450,17 +355,10 @@ int process_keyboard(uint8_t* resulting_scancodes) {
if (debounced_pressed) {
total_pressed++;
// Is circle key pressed?
// FIXME HACK
if (keycode == KEY_ENTER && fn_key && !command_sent) {
if (!soc_power_on) {
remote_turn_on_som();
command_sent = 1;
//anim_hello();
} else {
remote_turn_off_som();
command_sent = 1;
// hyper + enter? open OLED menu
if (keycode == KEY_ENTER && fn_key) {
if (!active_meta_mode && !last_meta_key) {
enter_meta_mode();
}
} else if (keycode == KEY_F12) {
remote_wake_som();
@ -472,19 +370,19 @@ int process_keyboard(uint8_t* resulting_scancodes) {
// not holding the same key?
if (last_meta_key != keycode) {
// hyper/circle/menu functions
int stay_meta = 0; //execute_meta_function(keycode);
int stay_meta = execute_meta_function(keycode);
// don't repeat action while key is held down
last_meta_key = keycode;
// exit meta mode
if (!stay_meta) {
active_meta_mode = 0;
exit_meta_mode();
}
// after wake-up from sleep mode, skip further keymap processing
if (stay_meta == 2) {
//reset_keyboard_state();
//enter_meta_mode();
reset_keyboard_state();
enter_meta_mode();
return 0;
}
}
@ -499,7 +397,6 @@ int process_keyboard(uint8_t* resulting_scancodes) {
} else {
// key not pressed
if (keycode == KEY_COMPOSE) {
command_sent = 0;
fn_key = 0;
active_matrix = matrix;
}
@ -518,75 +415,84 @@ int prev_buttons = 0;
int scroll_toggle = 0;
int prev_num_keys = 0;
int8_t tb_nx = 0;
int8_t tb_ny = 0;
int tb_btn1 = 0;
int tb_btn2 = 0;
int tb_btn3 = 0;
int tb_btn4 = 0;
// returns motion yes/no
static int poll_trackball()
{
tb_btn3 = matrix_state[KBD_COLS*5+3]>0;
tb_btn1 = matrix_state[KBD_COLS*5+4]>0;
tb_btn2 = matrix_state[KBD_COLS*5+7]>0;
tb_btn4 = matrix_state[KBD_COLS*5+8]>0;
uint8_t buf[] = {0x7f, 0x00, 0x00, 0x00};
buf[0] = 0x02;
i2c_write_blocking_until(i2c0, ADDR_SENSOR, buf, 1, true, make_timeout_time_ms(2));
i2c_read_blocking_until(i2c0, ADDR_SENSOR, buf, 1, false, make_timeout_time_ms(2));
if (buf[0] & 0xf0) {
buf[0] = 0x03;
i2c_write_blocking_until(i2c0, ADDR_SENSOR, buf, 1, true, make_timeout_time_ms(2));
i2c_read_blocking_until(i2c0, ADDR_SENSOR, buf, 2, false, make_timeout_time_ms(2));
tb_nx = (int8_t)buf[0];
tb_ny = (int8_t)buf[1];
// HACK hue/value wheel
if (matrix_state[KBD_COLS*4+0]) {
if (tb_ny) {
// shift held? saturation
if (matrix_state[KBD_COLS*3+0]) {
led_mod_saturation(tb_ny);
} else {
led_mod_brightness(tb_ny);
}
}
if (tb_nx) {
led_mod_hue(tb_nx);
}
}
return 1;
}
return 0;
}
static void send_hid_report(uint8_t report_id)
{
// skip if hid is not ready yet
if ( !tud_hid_ready() ) return;
if (!tud_hid_ready()) return;
switch (report_id) {
case REPORT_ID_KEYBOARD:
{
//int num_keys = process_keyboard(pressed_scancodes);
//if (num_keys > 0) {
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, pressed_scancodes);
//} else {
// send empty key report if previously has key pressed
//if (prev_num_keys) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
//}
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, pressed_scancodes);
prev_num_keys = pressed_keys;
}
break;
case REPORT_ID_MOUSE:
{
uint8_t buf[] = {0x7f, 0x00, 0x00, 0x00};
buf[0] = 0x02;
i2c_write_blocking_until(i2c0, ADDR_SENSOR, buf, 1, true, make_timeout_time_ms(2));
i2c_read_blocking_until(i2c0, ADDR_SENSOR, buf, 1, false, make_timeout_time_ms(2));
//i2c_write_blocking(i2c0, ADDR_SENSOR, buf, 1, true);
//i2c_read_blocking(i2c0, ADDR_SENSOR, buf, 1, false);
int btn3 = matrix_state[KBD_COLS*5+3]>0;
int btn1 = matrix_state[KBD_COLS*5+4]>0;
int btn2 = matrix_state[KBD_COLS*5+7]>0;
int btn4 = matrix_state[KBD_COLS*5+8]>0;
int buttons = btn1 | (btn2<<1) | (btn4<<2);
if (buf[0] & 0xf0) {
buf[0] = 0x03;
//i2c_write_blocking(i2c0, ADDR_SENSOR, buf, 1, true);
//i2c_read_blocking(i2c0, ADDR_SENSOR, buf, 2, false);
i2c_write_blocking_until(i2c0, ADDR_SENSOR, buf, 1, true, make_timeout_time_ms(2));
i2c_read_blocking_until(i2c0, ADDR_SENSOR, buf, 2, false, make_timeout_time_ms(2));
int8_t nx = (int8_t)buf[0];
int8_t ny = (int8_t)buf[1];
int buttons = tb_btn1 | (tb_btn2<<1) | (tb_btn4<<2);
int motion = poll_trackball();
if (motion) {
// no button, right + down, no scroll pan
if (btn3 || scroll_toggle) {
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, 0, 0, 2*ny, -2*nx);
if (tb_btn3 || scroll_toggle) {
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, 0, 0, 2*tb_ny, -2*tb_nx);
} else {
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, -2*nx, -2*ny, 0, 0);
}
// HACK hue/value wheel
if (matrix_state[KBD_COLS*4+0]) {
if (ny) {
led_mod_brightness(ny);
}
if (nx) {
led_mod_hue(nx);
}
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, -2*tb_nx, -2*tb_ny, 0, 0);
}
} else {
//if (buttons != prev_buttons) {
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, 0, 0, 0, 0);
//}
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, 0, 0, 0, 0);
}
prev_buttons = buttons;
@ -616,29 +522,7 @@ static void send_hid_report(uint8_t report_id)
case REPORT_ID_GAMEPAD:
{
// use to avoid send multiple consecutive zero report for keyboard
/*static bool has_gamepad_key = false;
hid_gamepad_report_t report =
{
.x = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0,
.hat = 0, .buttons = 0
};
if ( btn )
{
report.hat = GAMEPAD_HAT_UP;
report.buttons = GAMEPAD_BUTTON_A;
tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
has_gamepad_key = true;
}else
{
report.hat = GAMEPAD_HAT_CENTERED;
report.buttons = 0;
if (has_gamepad_key) tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
has_gamepad_key = false;
}*/
// TODO: later
}
break;
@ -687,6 +571,9 @@ void led_bitmap(uint8_t row, const uint8_t* row_rgb) {
}
}
int hid_task_counter = 0;
// 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)
@ -695,11 +582,21 @@ void hid_task(void)
const uint32_t interval_ms = 10;
static uint32_t start_ms = 0;
if ( board_millis() - start_ms < interval_ms) return; // not enough time
if (board_millis() - start_ms < interval_ms) {
// not enough time passed
return;
}
start_ms += interval_ms;
// allow trackball backlight control even if there's no USB yet
if (hid_task_counter%10 == 0) {
if (tud_suspended() && remote_get_power_state()) {
poll_trackball();
}
}
// Remote wakeup
if ( tud_suspended() && pressed_keycodes[0] )
if (tud_suspended() && pressed_keys > 0)
{
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
@ -708,9 +605,15 @@ void hid_task(void)
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
send_hid_report(REPORT_ID_KEYBOARD);
}
hid_task_counter++;
if (hid_task_counter%100 == 0) {
refresh_menu_page();
}
}
int led_brightness = 0;
int led_saturation = 255;
int led_hue = 127;
typedef struct RgbColor
@ -780,7 +683,7 @@ void led_set_hsv() {
HsvColor hsv;
RgbColor rgb;
hsv.h = (unsigned char)led_hue;
hsv.s = 0xff;
hsv.s = (unsigned char)led_saturation;
hsv.v = (unsigned char)led_brightness;
rgb = HsvToRgb(hsv);
@ -801,6 +704,17 @@ void led_mod_hue(int d) {
led_set_hsv();
}
void led_mod_saturation(int d) {
led_saturation+=d;
if (led_saturation>0xff) led_saturation = 0xff;
if (led_saturation<0) led_saturation = 0;
led_set_hsv();
}
void led_set_saturation(int s) {
led_saturation = s;
}
void led_set_brightness(int b) {
led_brightness = b;
led_set_hsv();
@ -812,7 +726,6 @@ void led_cycle_hue() {
led_set_hsv();
}
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
@ -928,7 +841,8 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
led_task(pixel_grb);
}
else if (cmd == strnstr(cmd, CMD_LOGO, 4)) {
anim_hello();
// FIXME
//anim_hello();
}
else if (cmd == strnstr(cmd, CMD_OLED_BRITE, 4)) {
uint8_t val = (uint8_t)atoi((const char*)&buffer[4]);
@ -959,5 +873,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

@ -0,0 +1,222 @@
/*
MNT Reform 2.0 Keyboard Firmware
See keyboard.c for Copyright
SPDX-License-Identifier: MIT
*/
//#include "backlight.h"
//#include "keyboard.h"
#include "menu.h"
#include "oled.h"
#include "remote.h"
#include "usb_hid_keys.h"
#include "keyboard.h"
#include "pico/stdlib.h"
//#include "scancodes.h"
int current_menu_y = 0;
int current_scroll_y = 0;
int current_menu_page = 0;
int8_t logo_timeout_ticks = 0;
#ifdef KBD_MODE_STANDALONE
#define MENU_NUM_ITEMS 5
const MenuItem menu_items[] = {
{ "Exit Menu ESC", KEY_ESC },
{ "Key Backlight- F1", KEY_F1 },
{ "Key Backlight+ F2", KEY_F2 },
{ "System Status s", KEY_S },
{ "USB Flashing Mode x", KEY_X },
};
#else
#define MENU_NUM_ITEMS 7
const MenuItem menu_items[] = {
{ "Exit Menu ESC", KEY_ESC },
{ "Power On 1", KEY_1 },
{ "Power Off 0", KEY_0 },
{ "Reset r", KEY_R },
{ "Battery Status b", KEY_B },
{ "Wake SPC", KEY_SPACE },
{ "System Status s", KEY_S },
// Only needed for debugging.
// The keyboard will go to sleep when turning off
// main system power.
{ "KBD Power-Off p", KEY_P },
};
#endif
void reset_menu() {
current_scroll_y = 0;
current_menu_y = 0;
current_menu_page = MENU_PAGE_NONE;
gfx_clear();
gfx_flush();
}
void reset_and_render_menu() {
reset_menu();
render_menu(current_scroll_y);
}
void render_menu(int y) {
gfx_clear();
gfx_invert_row((uint8_t)(current_menu_y-y));
for (int i=0; i<MENU_NUM_ITEMS; i++) {
gfx_poke_cstr(0,(uint8_t)(i-y),menu_items[i].title);
}
gfx_on();
gfx_flush();
}
// automatically refresh the current menu page if needed
void refresh_menu_page() {
if (current_menu_page == MENU_PAGE_BATTERY_STATUS) {
remote_get_voltages();
} else if (current_menu_page == MENU_PAGE_MNT_LOGO && --logo_timeout_ticks <= 0) {
reset_menu();
}
}
int execute_menu_function(int y) {
current_menu_page = MENU_PAGE_NONE;
if (y>=0 && y<MENU_NUM_ITEMS) {
current_menu_page = MENU_PAGE_OTHER;
return execute_meta_function(menu_items[y].keycode);
}
return execute_meta_function(KEY_ESC);
}
// returns 1 for navigation function (stay in meta mode), 0 for terminal function
int execute_meta_function(int keycode) {
if (keycode == KEY_0) {
// TODO: are you sure?
anim_goodbye();
remote_turn_off_som();
//keyboard_power_off();
reset_keyboard_state();
// Directly enter menu again
return 2;
}
else if (keycode == KEY_1) {
//kbd_brightness_init();
if (remote_turn_on_som()) {
anim_hello();
}
return 0;
}
else if (keycode == KEY_R) {
// TODO: are you sure?
//remote_reset_som();
}
else if (keycode == KEY_SPACE) {
remote_wake_som();
}
else if (keycode == KEY_B) {
current_menu_page = MENU_PAGE_BATTERY_STATUS;
remote_get_voltages();
return 0;
}
else if (keycode == KEY_S) {
remote_get_status();
return 0;
}
else if (keycode == KEY_F1) {
//kbd_brightness_dec();
return 1;
}
else if (keycode == KEY_F2) {
//kbd_brightness_inc();
return 1;
}
else if (keycode == KEY_UP) {
current_menu_y--;
if (current_menu_y<0) current_menu_y = 0;
if (current_menu_y<=current_scroll_y) current_scroll_y--;
if (current_scroll_y<0) current_scroll_y = 0;
render_menu(current_scroll_y);
return 1;
}
else if (keycode == KEY_DOWN) {
current_menu_y++;
if (current_menu_y>=MENU_NUM_ITEMS) current_menu_y = MENU_NUM_ITEMS-1;
if (current_menu_y>=current_scroll_y+3) current_scroll_y++;
render_menu(current_scroll_y);
return 1;
}
else if (keycode == KEY_ENTER) {
return execute_menu_function(current_menu_y);
}
else if (keycode == KEY_ESC) {
gfx_clear();
gfx_flush();
}
/*else if (keycode == KEY_X) {
gfx_clear();
gfx_poke_str(1, 1, "Entered firmware");
gfx_poke_str(1, 2, "update mode.");
gfx_on();
gfx_flush();
jump_to_bootloader();
}*/
else if (keycode == KEY_L) {
anim_hello();
return 0;
}
gfx_clear();
gfx_flush();
return 0;
}
void anim_hello(void) {
current_menu_page = MENU_PAGE_MNT_LOGO;
logo_timeout_ticks = 10; // ~30sec
gfx_clear();
//kbd_brightness_set(0);
gfx_on();
for (uint8_t y=0; y<3; y++) {
for (uint8_t x=0; x<12; x++) {
gfx_poke(x+4,y+1,(uint8_t)((5+y)*32+x));
}
gfx_flush();
}
for (uint8_t y=0; y<0xff; y++) {
/*if ((y % 32) == 0) {
kbd_brightness_inc();
}*/
gfx_contrast(y);
sleep_ms(0);
}
for (uint8_t y=0; y<0xff; y++) {
/*if ((y % 64) == 0) {
kbd_brightness_dec();
}*/
gfx_contrast(0xff-y);
sleep_ms(0);
}
}
void anim_goodbye(void) {
gfx_clear();
gfx_on();
for (uint8_t y=0; y<3; y++) {
for (uint8_t x=0; x<12; x++) {
gfx_poke(x+4,y+1,(uint8_t)((5+y)*32+x));
}
}
for (uint8_t y=0; y<3; y++) {
for (uint8_t x=0; x<12; x++) {
gfx_poke(x+4,y+1,' ');
}
gfx_flush();
}
//int16_t brt = kbd_brightness_get();
/*while (brt--) {
kbd_brightness_dec();
Delay_MS(64);
}*/
gfx_off();
}

View File

@ -0,0 +1,29 @@
/*
MNT Reform 2.0 Keyboard Firmware
See keyboard.c for Copyright
SPDX-License-Identifier: MIT
*/
#ifndef _MENU_H_
#define _MENU_H_
typedef struct MenuItem {
const char* title;
int keycode;
} MenuItem;
#define MENU_PAGE_NONE 0
#define MENU_PAGE_OTHER 1
#define MENU_PAGE_BATTERY_STATUS 2
#define MENU_PAGE_MNT_LOGO 3
void reset_and_render_menu(void);
void reset_menu(void);
void render_menu(int y);
void refresh_menu_page(void);
int execute_menu_function(int y);
int execute_meta_function(int keycode);
void anim_hello(void);
void anim_goodbye(void);
#endif

View File

@ -0,0 +1,242 @@
/*
MNT Reform 2.0 Keyboard Firmware
See keyboard.c for Copyright
SPDX-License-Identifier: MIT
*/
#include <stdlib.h>
#include "remote.h"
#include "oled.h"
#include "keyboard.h"
#include "bsp/board_api.h"
#include "pico/stdlib.h"
#define UART_ID uart1
// received by uart
#define RESPONSE_MAX 128
static char response[RESPONSE_MAX];
static int uart_rx_i = 0;
static int uart_print_response = 0;
static int uart_response_complete = 0;
uint8_t term_x = 0;
uint8_t term_y = 0;
double voltages[8];
int alert_low_battery = 0;
int alert_blink = 0;
uint8_t remote_som_power_expected_state = 0;
int command_sent = 0;
int soc_power_on = 0; // fixme
void insert_bat_icon(char* str, int x, double v) {
char icon = 0;
if (v>=3.3) {
icon = 8;
} else if (v>=3.1) {
icon = 6;
} else if (v>=3.0) {
icon = 4;
} else if (v>=2.9) {
icon = 2;
} else {
icon = 0;
}
str[x] = 4*32+icon;
str[x+1] = 4*32+icon+1;
}
int remote_turn_on_som() {
uart_puts(UART_ID, "1p\r\n");
//led_set_brightness(10);
soc_power_on = 1;
return 1;
}
int remote_turn_off_som() {
uart_puts(UART_ID, "0p\r\n");
//led_set_brightness(0);
soc_power_on = 0;
return 1;
}
int remote_wake_som() {
uart_puts(UART_ID, "1w\r\n");
//anim_hello();
return 1;
}
int remote_get_power_state() {
return soc_power_on;
}
void remote_on_uart_rx() {
while (uart_is_readable(UART_ID)) {
char c = uart_getc(UART_ID);
if (uart_rx_i < RESPONSE_MAX-1) {
response[uart_rx_i++] = c;
response[uart_rx_i] = 0;
} else {
uart_rx_i = 0;
}
uint8_t poke_chr = c;
if (c == '\n') {
// TODO hack
/*if (uart_rx_i>6) {
gfx_clear();
//gfx_poke_str(0, 3, uart_rx_line);
// cut off after 4 digits
uart_rx_line[4] = 0;
float percentage = ((float)atoi(uart_rx_line))/(float)10.0;
char batinfo[32];
sprintf(batinfo, " %.1f%%", (double)percentage);
insert_bat_icon(batinfo, 0, percentage);
gfx_poke_str(7, 1, batinfo);
gfx_flush();
}*/
uart_rx_i = 0;
poke_chr=' ';
}
if (c!='\r') {
if (uart_print_response) {
gfx_poke(term_x,term_y,poke_chr);
gfx_poke(term_x+1,term_y,' ');
term_x++;
if (term_x>=20) {
term_x=0;
term_y++;
if (term_y>=3) {
term_y=0;
}
}
}
} else {
uart_response_complete = 1;
}
}
}
int remote_try_command(const char* cmd, int print_response) {
int ok = 1;
memset(response, 0, RESPONSE_MAX);
uart_rx_i = 0;
uart_response_complete = 0;
uart_print_response = print_response;
if (print_response) {
term_x = 0;
term_y = 0;
}
uart_puts(UART_ID, cmd);
int timeout = 0;
while (!uart_response_complete) {
sleep_ms(10);
timeout++;
if (timeout > 10) {
gfx_poke(0,0,'T');
gfx_flush();
ok = 0;
break;
}
}
if (print_response) {
gfx_flush();
};
return ok;
}
int remote_get_status(void) {
gfx_clear();
gfx_poke_cstr(0, 2, "MNT Pocket Reform HID");
gfx_poke_cstr(0, 3, PREF_HID_FW_REV);
gfx_on();
gfx_flush();
int ok = remote_try_command("s\r", 1);
return ok;
}
int remote_get_voltages(void) {
term_x = 0;
term_y = 0;
double bat_volts = 0;
double bat_amps = 0;
char bat_gauge[5] = {0,0,0,0,0};
int ok = remote_try_command("c\r", 0);
if (!ok) return ok;
// lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???% P1
// | | | | | | | | | | | | |
// 0 3 6 9 12 15 18 21 24| | | |
// 26 33 39 44
// |
// `- can be a minus
double sum_volts = 0;
for (int i=0; i<8; i++) {
voltages[i] = ((double)((response[i*3]-'0')*10 + (response[i*3+1]-'0')))/10.0;
if (voltages[i]<0) voltages[i]=0;
if (voltages[i]>=10) voltages[i]=9.9;
sum_volts += voltages[i];
}
int amps_offset = 3*8+2;
// cut off string
response[amps_offset+5]=0;
bat_amps = ((double)atoi(&response[amps_offset]))/1000.0;
int volts_offset = amps_offset+5+2;
response[volts_offset+5]=0;
bat_volts = ((double)atoi(&response[volts_offset]))/1000.0;
int gauge_offset = volts_offset+5+1;
strncpy(bat_gauge, &response[gauge_offset], 4);
const char* power_str = " ";
int syspower_offset = gauge_offset+5;
char power_digit = response[syspower_offset+1];
if (power_digit == '1') {
power_str = " On";
soc_power_on = 1;
} else if (power_digit == '0') {
power_str = "Off";
soc_power_on = 0;
}
// plot
gfx_clear();
char str[32];
sprintf(str,"[] %.1f %s",voltages[0],bat_gauge);
insert_bat_icon(str,0,voltages[0]);
gfx_poke_str(0,0,str);
sprintf(str,"[] %.1f %s",voltages[1],power_str);
insert_bat_icon(str,0,voltages[1]);
gfx_poke_str(0,1,str);
if (bat_amps>=0) {
sprintf(str," %2.3fA",bat_amps);
} else {
sprintf(str," %2.2fA",bat_amps);
}
gfx_poke_str(0,2,str);
sprintf(str," %2.2fV",bat_volts);
gfx_poke_str(0,3,str);
gfx_flush();
return ok;
}

View File

@ -0,0 +1,30 @@
/*
MNT Reform 2.0 Keyboard Firmware
See keyboard.c for Copyright
SPDX-License-Identifier: MIT
*/
#ifndef _REMOTE_H_
#define _REMOTE_H_
void insert_bat_icon(char* str, int x, double v);
void empty_serial(void);
int remote_receive_string(int print);
int remote_try_wakeup(void);
int remote_try_command(const char* cmd, int print_response);
int remote_get_voltages(void);
int remote_check_for_low_battery(void);
int remote_get_status(void);
int remote_turn_on_som(void);
int remote_turn_off_som(void);
int remote_reset_som(void);
int remote_wake_som(void);
int remote_report_voltages(void);
int remote_enable_som_uart(void);
int remote_disable_som_uart(void);
void remote_process_alerts(void);
void remote_init(void);
void remote_on_uart_rx(void);
int remote_get_power_state(void);
#endif