Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions keyboards/keychron/common/keychron_raw_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "keychron_common.h"
#include "keychron_raw_hid.h"
#include "raw_hid.h"
#include "usb_descriptor.h"
#include "version.h"
#include "language.h"
#ifdef FACTORY_TEST_ENABLE
Expand All @@ -34,6 +35,8 @@
#endif
#if defined(LK_WIRELESS_ENABLE) || defined(KC_BLUETOOTH_ENABLE)
# include "wireless.h"
# include "transport.h"
# include "battery.h"
# ifdef LK_WIRELESS_ENABLE
# include "lkbt51.h"
# else
Expand Down Expand Up @@ -72,6 +75,9 @@ void get_support_feature(uint8_t *data) {
;

data[2] = (FEATURE_QUICK_START | FEATURE_NKRO) >> 8;
# if defined(LK_WIRELESS_ENABLE) || defined(KC_BLUETOOTH_ENABLE)
data[2] |= (FEATURE_BATTERY >> 8);
# endif
}

# ifdef ANANLOG_MATRIX
Expand Down Expand Up @@ -128,6 +134,24 @@ void kc_raw_hid_send(uint8_t src, uint8_t *data, uint8_t len) {
# endif
}

# if defined(LK_WIRELESS_ENABLE) || defined(KC_BLUETOOTH_ENABLE)
/* Proactively push the battery state to the host over the wireless link.
* The host only needs to READ the dongle's raw HID interface; this works even
* if the host->keyboard (downlink) raw HID path is not bridged by the dongle. */
void kc_battery_push(void) {
uint8_t data[RAW_EPSIZE] = {0};
uint16_t bat_voltage = battery_get_voltage();
data[0] = KC_GET_BATTERY;
data[1] = battery_get_percentage();
data[2] = bat_voltage & 0xFF;
data[3] = (bat_voltage >> 8) & 0xFF;
data[4] = battery_get_charging_state();
data[5] = get_transport();
data[6] = KC_BATTERY_MODEL_ID;
kc_raw_hid_send(RAW_HID_SRC_WIRELESS, data, RAW_EPSIZE);
}
# endif

bool kc_raw_hid_rx(uint8_t src, uint8_t *data, uint8_t length) {
# if defined(ANANLOG_MATRIX) && defined(VIA_ENABLE)
if (src == RAW_HID_SRC_USB && data[0] == id_get_keyboard_value && data[1] == id_switch_matrix_state) {
Expand Down Expand Up @@ -167,6 +191,18 @@ bool kc_raw_hid_rx(uint8_t src, uint8_t *data, uint8_t length) {
data[1] = get_highest_layer(default_layer_state);
break;

# if defined(LK_WIRELESS_ENABLE) || defined(KC_BLUETOOTH_ENABLE)
case KC_GET_BATTERY: {
uint16_t bat_voltage = battery_get_voltage();
data[1] = battery_get_percentage();
data[2] = bat_voltage & 0xFF;
data[3] = (bat_voltage >> 8) & 0xFF;
data[4] = battery_get_charging_state();
data[5] = get_transport();
data[6] = KC_BATTERY_MODEL_ID;
} break;
# endif

case KC_MISC_CMD_GROUP:
switch (data[1]) {
case MISC_GET_PROTOCOL_VER:
Expand Down
13 changes: 13 additions & 0 deletions keyboards/keychron/common/keychron_raw_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum {
KC_GET_FIRMWARE_VERSION = 0xA1,
KC_GET_SUPPORT_FEATURE = 0xA2,
KC_GET_DEFAULT_LAYER = 0xA3,
KC_GET_BATTERY = 0xA4,
KC_MISC_CMD_GROUP = 0xA7,
KC_KEYCHRON_RGB = 0xA8,
KC_ANALOG_MATRIX = 0xA9,
Expand All @@ -45,6 +46,7 @@ enum {
// Byte 1
FEATURE_QUICK_START = 0x01U << 8,
FEATURE_NKRO = 0x01U << 9,
FEATURE_BATTERY = 0x01U << 10,
};

enum {
Expand Down Expand Up @@ -86,3 +88,14 @@ enum {
};

void kc_raw_hid_send(uint8_t src, uint8_t *data, uint8_t len);

// Optional model identifier carried in KC_GET_BATTERY reports (data[6]).
// Define per-keyboard in config.h so every translation unit agrees on it.
// 0 means "unspecified / legacy firmware".
#ifndef KC_BATTERY_MODEL_ID
# define KC_BATTERY_MODEL_ID 0
#endif

#if defined(LK_WIRELESS_ENABLE) || defined(KC_BLUETOOTH_ENABLE)
void kc_battery_push(void);
#endif
22 changes: 22 additions & 0 deletions keyboards/keychron/common/wireless/battery.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ uint8_t battery_get_percentage(void) {
return 0;
}

uint8_t battery_get_charging_state(void) {
#if defined(BAT_CHARGING_PIN)
if (usb_power_connected()) {
if (gpio_read_pin(BAT_CHARGING_PIN) == BAT_CHARGING_LEVEL)
return BAT_CHARGING;
else
return BAT_FULL_CHARGED;
}
#endif
return BAT_NOT_CHARGING;
}

bool battery_is_empty(void) {
return bat_empty > BATTERY_EMPTY_COUNT;
}
Expand Down Expand Up @@ -197,7 +209,10 @@ void battery_timer_reset(void) {

void battery_task(void) {
uint32_t t = rtc_timer_elapsed_ms(bat_monitor_timer_buffer);
bool monitor_voltage = false;

if ((get_transport() & TRANSPORT_WIRELESS) && (wireless_get_state() == WT_CONNECTED || battery_power_on_sample())) {
monitor_voltage = true;
#if defined(BAT_CHARGING_PIN)
if (usb_power_connected() && t > VOLTAGE_MEASURE_INTERVAL) {
if (gpio_read_pin(BAT_CHARGING_PIN) == BAT_CHARGING_LEVEL)
Expand All @@ -206,7 +221,14 @@ void battery_task(void) {
lkbt51_update_bat_state(BAT_FULL_CHARGED);
}
#endif
} else if (get_transport() == TRANSPORT_USB && usb_power_connected()) {
/* Cable mode: still poll the wireless module for cell voltage (read-only).
* Charging state already comes from usb_power + BAT_CHARGING_PIN; this
* keeps percentage/mV in sync for host tools over USB raw HID. */
monitor_voltage = true;
}

if (monitor_voltage) {
if ((battery_power_on_sample()
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
&& !indicator_is_enabled()
Expand Down
1 change: 1 addition & 0 deletions keyboards/keychron/common/wireless/battery.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ void battery_calculate_voltage(bool vol_src_bt, uint16_t value);
void battery_set_voltage(uint16_t value);
uint16_t battery_get_voltage(void);
uint8_t battery_get_percentage(void);
uint8_t battery_get_charging_state(void);
bool battery_is_empty(void);
bool battery_is_critical_low(void);
bool battery_power_on_sample(void);
Expand Down
5 changes: 5 additions & 0 deletions keyboards/keychron/k10_he/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@

#include "eeconfig_kb.h"

/* Model identifier reported in KC_GET_BATTERY (data[6]) so the host can show
* the keyboard model even over the 2.4 GHz dongle. Defined here (config.h) so
* every translation unit, including the shared keychron_raw_hid.c, sees it. */
#define KC_BATTERY_MODEL_ID 1

/* External EEPROM Configuration*/
#define I2C_DRIVER I2CD3
#define I2C1_SCL_PIN A8
Expand Down
20 changes: 20 additions & 0 deletions keyboards/keychron/k10_he/k10_he.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@
*/

#include "keychron.h"
#ifdef LK_WIRELESS_ENABLE
# include "keychron_raw_hid.h"
#endif

#ifndef POWER_ON_LED_DURATION
# define POWER_ON_LED_DURATION 3000
#endif

static uint32_t power_on_indicator_timer;

#ifdef LK_WIRELESS_ENABLE
# ifndef BATTERY_PUSH_INTERVAL
# define BATTERY_PUSH_INTERVAL 2000
# endif
static uint32_t battery_push_timer = 0;
#endif

#ifdef DIP_SWITCH_ENABLE
bool dip_switch_update_kb(uint8_t index, bool active) {
if (index == 0) {
Expand Down Expand Up @@ -89,6 +99,16 @@ void keychron_task_kb(void) {
#endif
}
}

#ifdef LK_WIRELESS_ENABLE
/* Push the battery state to the host over the wireless link (PUSH model). */
if (timer_elapsed32(battery_push_timer) > BATTERY_PUSH_INTERVAL) {
battery_push_timer = timer_read32();
if ((get_transport() & TRANSPORT_WIRELESS) && wireless_get_state() == WT_CONNECTED) {
kc_battery_push();
}
}
#endif
}

#ifdef LK_WIRELESS_ENABLE
Expand Down