Skip to content

Commit

Permalink
Add config property to display central battery level
Browse files Browse the repository at this point in the history
  • Loading branch information
englmaxi committed Jun 16, 2024
1 parent fd77edc commit c249ed8
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 56 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Dongle Display

This module repository provides a ZMK shield that replaces the built-in status screen with a custom screen designed for 128x64-pixel OLED displays.
As it shows only the peripheral battery levels, it is recommended to be used on dongles only.

## Usage

Expand Down Expand Up @@ -49,6 +48,14 @@ For setup examples, refer to the shields in my [`zmk-config`](https://github.com
- output status
- peripheral battery levels

## Configuration

To also display the battery level of the dongle/central device, use the following configuration property:

```ini
CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY=y
```

## Demo
![output](https://github.com/englmaxi/zmk-config/assets/43675074/8d268f23-1a4f-44c3-817e-c36dc96a1f8b)
![mods](https://github.com/englmaxi/zmk-config/assets/43675074/af9ec3f5-8f61-4629-abed-14ba0047f0bd)
Expand Down
4 changes: 4 additions & 0 deletions boards/shields/dongle_display/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ config ZMK_DISPLAY_STATUS_SCREEN_CUSTOM
select ZMK_WPM
imply ZMK_HID_INDICATORS

config ZMK_DONGLE_DISPLAY_DONGLE_BATTERY
bool "Show also the battery level of the dongle"
depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL)

choice ZMK_DISPLAY_WORK_QUEUE
default ZMK_DISPLAY_WORK_QUEUE_DEDICATED
endchoice
Expand Down
6 changes: 3 additions & 3 deletions boards/shields/dongle_display/custom_status_screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

static struct zmk_widget_output_status output_status_widget;
static struct zmk_widget_layer_status layer_status_widget;
static struct zmk_widget_peripheral_battery_status peripheral_battery_status_widget;
static struct zmk_widget_dongle_battery_status dongle_battery_status_widget;
static struct zmk_widget_modifiers modifiers_widget;
static struct zmk_widget_bongo_cat bongo_cat_widget;

Expand Down Expand Up @@ -56,8 +56,8 @@ lv_obj_t *zmk_display_status_screen() {
// lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), LV_ALIGN_BOTTOM_LEFT, 2, -18);
lv_obj_align_to(zmk_widget_layer_status_obj(&layer_status_widget), zmk_widget_bongo_cat_obj(&bongo_cat_widget), LV_ALIGN_BOTTOM_LEFT, 0, 5);

zmk_widget_peripheral_battery_status_init(&peripheral_battery_status_widget, screen);
lv_obj_align(zmk_widget_peripheral_battery_status_obj(&peripheral_battery_status_widget), LV_ALIGN_TOP_RIGHT, 0, 0);
zmk_widget_dongle_battery_status_init(&dongle_battery_status_widget, screen);
lv_obj_align(zmk_widget_dongle_battery_status_obj(&dongle_battery_status_widget), LV_ALIGN_TOP_RIGHT, 0, 0);

return screen;
}
108 changes: 74 additions & 34 deletions boards/shields/dongle_display/widgets/battery_status.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,58 +10,69 @@
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

#include <zmk/display.h>
#include <zmk/display/widgets/battery_status.h>
#include <zmk/usb.h>
#include <zmk/battery.h>
#include <zmk/ble.h>
#include <zmk/display.h>
#include <zmk/events/battery_state_changed.h>
#include <zmk/events/usb_conn_state_changed.h>
#include <zmk/event_manager.h>
#include <zmk/events/battery_state_changed.h>
#include <zmk/usb.h>

#include "battery_status.h"

#if IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY)
#define SOURCE_OFFSET 1
#else
#define SOURCE_OFFSET 0
#endif

static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets);

struct peripheral_battery_state {
struct battery_state {
uint8_t source;
uint8_t level;
bool usb_present;
};

static lv_color_t battery_image_buffer[ZMK_SPLIT_BLE_PERIPHERAL_COUNT][5 * 8];
static lv_color_t battery_image_buffer[ZMK_SPLIT_BLE_PERIPHERAL_COUNT + SOURCE_OFFSET][5 * 8];

static void draw_battery(lv_obj_t *canvas, uint8_t level) {
static void draw_battery(lv_obj_t *canvas, uint8_t level, bool usb_present) {
lv_canvas_fill_bg(canvas, lv_color_black(), LV_OPA_COVER);

lv_draw_rect_dsc_t rect_fill_dsc;
lv_draw_rect_dsc_init(&rect_fill_dsc);
rect_fill_dsc.bg_color = lv_color_white();

if (usb_present) {
rect_fill_dsc.bg_opa = LV_OPA_TRANSP;
rect_fill_dsc.border_color = lv_color_white();
rect_fill_dsc.border_width = 1;
}

lv_canvas_set_px(canvas, 0, 0, lv_color_white());
lv_canvas_set_px(canvas, 4, 0, lv_color_white());

if (level > 90) {
// full
} else if (level > 70) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 1, &rect_fill_dsc);
} else if (level > 50) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 2, &rect_fill_dsc);
} else if (level > 30) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 3, &rect_fill_dsc);
} else if (level > 10) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 4, &rect_fill_dsc);
} else {
if (level <= 10 || usb_present) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 5, &rect_fill_dsc);
} else if (level <= 30) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 4, &rect_fill_dsc);
} else if (level <= 50) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 3, &rect_fill_dsc);
} else if (level <= 70) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 2, &rect_fill_dsc);
} else if (level <= 90) {
lv_canvas_draw_rect(canvas, 1, 2, 3, 1, &rect_fill_dsc);
}
}

static void set_battery_symbol(lv_obj_t *widget, struct peripheral_battery_state state) {
static void set_battery_symbol(lv_obj_t *widget, struct battery_state state) {
LOG_DBG("source: %d, level: %d, usb: %d", state.source, state.level, state.usb_present);
lv_obj_t *symbol = lv_obj_get_child(widget, state.source * 2);
lv_obj_t *label = lv_obj_get_child(widget, state.source * 2 + 1);

draw_battery(symbol, state.level);
draw_battery(symbol, state.level, state.usb_present);
lv_label_set_text_fmt(label, "%4u%%", state.level);

if (state.level > 0) {
if (state.level > 0 || state.usb_present) {
lv_obj_clear_flag(symbol, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(label, LV_OBJ_FLAG_HIDDEN);
} else {
Expand All @@ -70,30 +81,59 @@ static void set_battery_symbol(lv_obj_t *widget, struct peripheral_battery_state
}
}

void battery_status_update_cb(struct peripheral_battery_state state) {
struct zmk_widget_battery_status *widget;
void battery_status_update_cb(struct battery_state state) {
struct zmk_widget_dongle_battery_status *widget;
SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj, state); }
}

static struct peripheral_battery_state battery_status_get_state(const zmk_event_t *eh) {
static struct battery_state peripheral_battery_status_get_state(const zmk_event_t *eh) {
const struct zmk_peripheral_battery_state_changed *ev = as_zmk_peripheral_battery_state_changed(eh);
return (struct peripheral_battery_state){
.source = ev->source,
return (struct battery_state){
.source = ev->source + SOURCE_OFFSET,
.level = ev->state_of_charge,
};
}

ZMK_DISPLAY_WIDGET_LISTENER(widget_battery_status, struct peripheral_battery_state,
static struct battery_state central_battery_status_get_state(const zmk_event_t *eh) {
const struct zmk_battery_state_changed *ev = as_zmk_battery_state_changed(eh);
return (struct battery_state) {
.source = 0,
.level = (ev != NULL) ? ev->state_of_charge : zmk_battery_state_of_charge(),
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
.usb_present = zmk_usb_is_powered(),
#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */
};
}

static struct battery_state battery_status_get_state(const zmk_event_t *eh) {
if (as_zmk_peripheral_battery_state_changed(eh) != NULL) {
return peripheral_battery_status_get_state(eh);
} else {
return central_battery_status_get_state(eh);
}
}

ZMK_DISPLAY_WIDGET_LISTENER(widget_dongle_battery_status, struct battery_state,
battery_status_update_cb, battery_status_get_state)

ZMK_SUBSCRIPTION(widget_battery_status, zmk_peripheral_battery_state_changed);
ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_peripheral_battery_state_changed);

#if IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY)
#if !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)

int zmk_widget_peripheral_battery_status_init(struct zmk_widget_peripheral_battery_status *widget, lv_obj_t *parent) {
ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_battery_state_changed);
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_usb_conn_state_changed);
#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
#endif /* IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY) */

int zmk_widget_dongle_battery_status_init(struct zmk_widget_dongle_battery_status *widget, lv_obj_t *parent) {
widget->obj = lv_obj_create(parent);

lv_obj_set_size(widget->obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);

for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT + SOURCE_OFFSET; i++) {
lv_obj_t *image_canvas = lv_canvas_create(widget->obj);
lv_obj_t *battery_label = lv_label_create(widget->obj);

Expand All @@ -108,11 +148,11 @@ int zmk_widget_peripheral_battery_status_init(struct zmk_widget_peripheral_batte

sys_slist_append(&widgets, &widget->node);

widget_battery_status_init();
widget_dongle_battery_status_init();

return 0;
}

lv_obj_t *zmk_widget_peripheral_battery_status_obj(struct zmk_widget_peripheral_battery_status *widget) {
lv_obj_t *zmk_widget_dongle_battery_status_obj(struct zmk_widget_dongle_battery_status *widget) {
return widget->obj;
}
36 changes: 18 additions & 18 deletions boards/shields/dongle_display/widgets/battery_status.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <lvgl.h>
#include <zephyr/kernel.h>

struct zmk_widget_peripheral_battery_status {
sys_snode_t node;
lv_obj_t *obj;
};

int zmk_widget_peripheral_battery_status_init(struct zmk_widget_peripheral_battery_status *widget, lv_obj_t *parent);
lv_obj_t *zmk_widget_peripheral_battery_status_obj(struct zmk_widget_peripheral_battery_status *widget);
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <lvgl.h>
#include <zephyr/kernel.h>

struct zmk_widget_dongle_battery_status {
sys_snode_t node;
lv_obj_t *obj;
};

int zmk_widget_dongle_battery_status_init(struct zmk_widget_dongle_battery_status *widget, lv_obj_t *parent);
lv_obj_t *zmk_widget_dongle_battery_status_obj(struct zmk_widget_dongle_battery_status *widget);

0 comments on commit c249ed8

Please sign in to comment.