DeskHop v0.68

- Wrote a "proper" user manual
  - Added screensaver shortcuts
  - Modified descriptor from 5+3 buttons to 8 buttons
  - Added OSHW cert to docs
  - Bumped bootsel detection to debug version
  - Tidied up CMakeList
This commit is contained in:
Hrvoje Cavrak 2024-11-25 21:06:20 +01:00
parent 1bbee41e3f
commit 4a3ca5c83a
10 changed files with 154 additions and 49 deletions

View File

@ -1,29 +1,42 @@
cmake_minimum_required(VERSION 3.6) cmake_minimum_required(VERSION 3.6)
## Version Configuration
set(VERSION_MAJOR 00) set(VERSION_MAJOR 00)
set(VERSION_MINOR 160) set(VERSION_MINOR 161)
## Release Type Selection
option(DH_DEBUG "Build a debug version" OFF)
## Hardware Configuration
set(DP_PIN_DEFAULT 14 CACHE STRING "Default USB D+ Pin Number")
set(PIO_USE_TINYUSB 1 CACHE STRING "Make TinyUSB Manage the PIO USB Port")
set(PICO_BOARD "pico")
## Pico SDK Configuration
set(PICO_SDK_FETCH_FROM_GIT off) set(PICO_SDK_FETCH_FROM_GIT off)
set(PICO_BOARD=pico)
set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/pico-sdk) set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/pico-sdk)
set(SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src)
include(pico_sdk_import.cmake) include(pico_sdk_import.cmake)
set(CMAKE_C_FLAGS "-Ofast -Wall -mcpu=cortex-m0plus -mtune=cortex-m0plus -fstack-usage")
set(PICO_COPY_TO_RAM 1)
## Project Setup
project(deskhop_project C CXX ASM) project(deskhop_project C CXX ASM)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(PICO_COPY_TO_RAM 1)
## Compiler Flags
set(CMAKE_C_FLAGS "-Ofast -Wall -mcpu=cortex-m0plus -mtune=cortex-m0plus -fstack-usage")
## Initialize Pico SDK
pico_sdk_init() pico_sdk_init()
## PIO USB Library Setup
set(PICO_PIO_USB_DIR ${CMAKE_CURRENT_LIST_DIR}/Pico-PIO-USB) set(PICO_PIO_USB_DIR ${CMAKE_CURRENT_LIST_DIR}/Pico-PIO-USB)
add_library(Pico-PIO-USB STATIC add_library(Pico-PIO-USB STATIC
${PICO_PIO_USB_DIR}/src/pio_usb.c ${PICO_PIO_USB_DIR}/src/pio_usb.c
${PICO_PIO_USB_DIR}/src/pio_usb_host.c ${PICO_PIO_USB_DIR}/src/pio_usb_host.c
${PICO_PIO_USB_DIR}/src/usb_crc.c ${PICO_PIO_USB_DIR}/src/usb_crc.c
) )
pico_generate_pio_header(Pico-PIO-USB ${PICO_PIO_USB_DIR}/src/usb_tx.pio) pico_generate_pio_header(Pico-PIO-USB ${PICO_PIO_USB_DIR}/src/usb_tx.pio)
pico_generate_pio_header(Pico-PIO-USB ${PICO_PIO_USB_DIR}/src/usb_rx.pio) pico_generate_pio_header(Pico-PIO-USB ${PICO_PIO_USB_DIR}/src/usb_rx.pio)
@ -36,33 +49,36 @@ target_link_libraries(Pico-PIO-USB PRIVATE
) )
target_include_directories(Pico-PIO-USB PRIVATE ${PICO_PIO_USB_DIR}) target_include_directories(Pico-PIO-USB PRIVATE ${PICO_PIO_USB_DIR})
## Source Files
set(COMMON_SOURCES set(COMMON_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c ${SRC_DIR}/usb_descriptors.c
${CMAKE_CURRENT_LIST_DIR}/src/defaults.c ${SRC_DIR}/defaults.c
${CMAKE_CURRENT_LIST_DIR}/src/constants.c ${SRC_DIR}/constants.c
${CMAKE_CURRENT_LIST_DIR}/src/protocol.c ${SRC_DIR}/protocol.c
${CMAKE_CURRENT_LIST_DIR}/src/hid_parser.c ${SRC_DIR}/hid_parser.c
${CMAKE_CURRENT_LIST_DIR}/src/hid_report.c ${SRC_DIR}/hid_report.c
${CMAKE_CURRENT_LIST_DIR}/src/utils.c ${SRC_DIR}/utils.c
${CMAKE_CURRENT_LIST_DIR}/src/handlers.c ${SRC_DIR}/handlers.c
${CMAKE_CURRENT_LIST_DIR}/src/setup.c ${SRC_DIR}/setup.c
${CMAKE_CURRENT_LIST_DIR}/src/keyboard.c ${SRC_DIR}/keyboard.c
${CMAKE_CURRENT_LIST_DIR}/src/mouse.c ${SRC_DIR}/mouse.c
${CMAKE_CURRENT_LIST_DIR}/src/tasks.c ${SRC_DIR}/tasks.c
${CMAKE_CURRENT_LIST_DIR}/src/led.c ${SRC_DIR}/led.c
${CMAKE_CURRENT_LIST_DIR}/src/uart.c ${SRC_DIR}/uart.c
${CMAKE_CURRENT_LIST_DIR}/src/usb.c ${SRC_DIR}/usb.c
${CMAKE_CURRENT_LIST_DIR}/src/main.c ${SRC_DIR}/main.c
${CMAKE_CURRENT_LIST_DIR}/src/ramdisk.c ${SRC_DIR}/ramdisk.c
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c ${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c ${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
) )
## Include Directories
set(COMMON_INCLUDES set(COMMON_INCLUDES
${CMAKE_CURRENT_LIST_DIR}/src/include ${SRC_DIR}/include
${PICO_PIO_USB_DIR}/src ${PICO_PIO_USB_DIR}/src
) )
## Library Dependencies
set(COMMON_LINK_LIBRARIES set(COMMON_LINK_LIBRARIES
pico_stdlib pico_stdlib
hardware_flash hardware_flash
@ -76,12 +92,14 @@ set(COMMON_LINK_LIBRARIES
pico_multicore pico_multicore
Pico-PIO-USB Pico-PIO-USB
) )
set(binary deskhop) set(binary deskhop)
## Disk Image Configuration
# This assembles disk.S, then updates the elf section in post-build
# With the disk FAT image binary in /disk/disk.img
set(DISK_ASM "${CMAKE_CURRENT_LIST_DIR}/disk/disk.S") set(DISK_ASM "${CMAKE_CURRENT_LIST_DIR}/disk/disk.S")
set(DISK_BIN "${CMAKE_CURRENT_LIST_DIR}/disk/disk.img") set(DISK_BIN "${CMAKE_CURRENT_LIST_DIR}/disk/disk.img")
set_property(SOURCE ${DISK_ASM} APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp") set_property(SOURCE ${DISK_ASM} APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp")
add_executable(${binary} ${DISK_ASM}) add_executable(${binary} ${DISK_ASM})
@ -89,21 +107,28 @@ add_executable(${binary} ${DISK_ASM})
target_sources(${binary} PUBLIC ${COMMON_SOURCES}) target_sources(${binary} PUBLIC ${COMMON_SOURCES})
target_compile_definitions(${binary} target_compile_definitions(${binary}
PRIVATE PRIVATE
PIO_USB_USE_TINYUSB=1 PIO_USB_USE_TINYUSB=${PIO_USE_TINYUSB}
PIO_USB_DP_PIN_DEFAULT=14 PIO_USB_DP_PIN_DEFAULT=${DP_PIN_DEFAULT}
# Uncomment to enable debug uart:
# DH_DEBUG=1
__disk_file_path__="${DISK_BIN}" __disk_file_path__="${DISK_BIN}"
) )
## Support building a debug version
if (DH_DEBUG)
add_definitions(-DDH_DEBUG)
endif()
target_include_directories(${binary} PUBLIC ${COMMON_INCLUDES}) target_include_directories(${binary} PUBLIC ${COMMON_INCLUDES})
target_link_libraries(${binary} PUBLIC ${COMMON_LINK_LIBRARIES}) target_link_libraries(${binary} PUBLIC ${COMMON_LINK_LIBRARIES})
## Configure Pico Library
pico_enable_stdio_usb(${binary} 0) pico_enable_stdio_usb(${binary} 0)
pico_enable_stdio_uart(${binary} 0) pico_enable_stdio_uart(${binary} 0)
pico_set_linker_script(${binary} ${CMAKE_SOURCE_DIR}/memory_map.ld)
## Build other file formats as well
pico_add_extra_outputs(${binary}) pico_add_extra_outputs(${binary})
## Post-Build Commands
add_custom_command( add_custom_command(
TARGET ${binary} POST_BUILD TARGET ${binary} POST_BUILD
COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/crc32.py ${binary}.bin ${binary}.crc ${VERSION_MAJOR}${VERSION_MINOR} COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/crc32.py ${binary}.bin ${binary}.crc ${VERSION_MAJOR}${VERSION_MINOR}
@ -113,9 +138,9 @@ add_custom_command(
COMMENT "Update CRC32 section to match the actual binary" COMMENT "Update CRC32 section to match the actual binary"
) )
## Linker Options
target_link_options(${binary} PRIVATE target_link_options(${binary} PRIVATE
-Xlinker -Xlinker
--print-memory-usage --print-memory-usage
) )
pico_set_linker_script(${binary} ${CMAKE_SOURCE_DIR}/memory_map.ld)

View File

@ -19,6 +19,9 @@ All I wanted was a way to use a keyboard shortcut to quickly switch outputs, pai
- Full Galvanic isolation between your outputs - Full Galvanic isolation between your outputs
- Works with Linux, macOS and Windows - Works with Linux, macOS and Windows
![Image](img/oshw.svg){width=240}
[Proudly certified](https://certification.oshwa.org/de000149.html) by the Open Source Hardware Association (oshwa.org)
------ ------
## How it works ## How it works
@ -50,6 +53,9 @@ It also remembers the LED state for each computer, so you can pick up exactly ho
![Image](img/demo-typing.gif) ![Image](img/demo-typing.gif)
## User Manual
To help you set up, configure and program the board available [read the manual provided here](user-manual.pdf).
## How to build ## How to build
To avoid version mismatch and reported path issues when building, as well as to save you from having to download a large SDK, the project now bundles minimal pico sdk and tinyusb. To avoid version mismatch and reported path issues when building, as well as to save you from having to download a large SDK, the project now bundles minimal pico sdk and tinyusb.
@ -208,19 +214,20 @@ The standard process to do that is using isopropyl alcohol and an old toothbrush
## Usage guide ## Usage guide
### Keyboard shortcuts - (fw versions 0.64 +) ### Keyboard shortcuts - (fw versions 0.68+)
_Config_ _Config_
- ~~```Left Shift```~~ ```Left Ctrl + Right Shift + C + O``` - enter config mode - ~~```Left Shift```~~ ```Left Ctrl + Right Shift + C + O``` - enter config mode
- ```Right Shift + F12 + D``` - remove flash config - ```Right Shift + F12 + D``` - remove flash config
- ```Right Shift + F12 + Y``` - save screen switch offset - ```Right Shift + F12 + Y``` - save screen switch offset
- ```Right Shift + F12 + S``` - turn on/off screensaver option
_Usage_ _Usage_
- ```Right CTRL + Right ALT``` - Toggle slower mouse mode - ```Right CTRL + Right ALT``` - Toggle slower mouse mode
- ```Right CTRL + K``` - Lock/Unlock mouse desktop switching - ```Right CTRL + K``` - Lock/Unlock mouse desktop switching
- ```Right CTRL + L``` - Lock both outputs at once (set output OS before, see shortcuts below) - ```Right CTRL + L``` - Lock both outputs at once (set output OS before, see shortcuts below)
- ~~```Left Shift```~~ ```Left Ctrl + Right Shift + G``` - Toggle gaming mode (lock to screen, act as standard mouse) - ~~```Left Shift```~~ ```Left CTRL + Right Shift + G``` - Toggle gaming mode (lock to screen, act as standard mouse)
- ```Left CTRL + Right Shift + S``` - Enable screensaver
- ```Left CTRL + Right Shift + X``` - Disable screensaver
- ```Left CTRL + Caps Lock``` - Switch between outputs - ```Left CTRL + Caps Lock``` - Switch between outputs
Note: some keyboards don't send both shifts at the same time properly, that's why the shortcut was changed - to work for everyone. Apologies for the confusion. Note: some keyboards don't send both shifts at the same time properly, that's why the shortcut was changed - to work for everyone. Apologies for the confusion.
@ -316,6 +323,7 @@ This still doesn't guarantee anything, but I believe it makes a reasonable set o
*I'm not selling anything, this is just a personal, non-commercial hobby project.* *I'm not selling anything, this is just a personal, non-commercial hobby project.*
[UPDATE] It seems you can order it in QTY of 1 (for either PCB, assembled PCB or a fully assembled device) from [Elecrow if you follow this link](https://www.elecrow.com/deskhop-fast-desktop-switching.html) [UPDATE] It seems you can order it in QTY of 1 (for either PCB, assembled PCB or a fully assembled device) from [Elecrow if you follow this link](https://www.elecrow.com/deskhop-fast-desktop-switching.html)
As reported by users, your **board will arrive blank** and you have to write the firmware yourself.
[UPDATE2] - I never asked Elecrow for anything, but a couple of days ago they offered to sponsor the project with a small budget that will be used for future board prototyping. Since my goal is to create a better board with more than 2 outputs etc, I believe prototyping services might be beneficial to the project. [UPDATE2] - I never asked Elecrow for anything, but a couple of days ago they offered to sponsor the project with a small budget that will be used for future board prototyping. Since my goal is to create a better board with more than 2 outputs etc, I believe prototyping services might be beneficial to the project.

35
img/oshw.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -39,6 +39,12 @@ void _get_border_position(device_t *state, border_size_t *border) {
border->top = state->pointer_y; border->top = state->pointer_y;
} }
void _screensaver_set(device_t *state, bool value) {
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT)
state->config.output[BOARD_ROLE].screensaver.mode = value;
else
send_value(value, SCREENSAVER_MSG);
};
/* This key combo records switch y top coordinate for different-size monitors */ /* This key combo records switch y top coordinate for different-size monitors */
void screen_border_hotkey_handler(device_t *state, hid_keyboard_report_t *report) { void screen_border_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
@ -115,6 +121,15 @@ void mouse_zoom_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
send_value(state->mouse_zoom, MOUSE_ZOOM_MSG); send_value(state->mouse_zoom, MOUSE_ZOOM_MSG);
}; };
/* When pressed, enables the screensaver on active output */
void enable_screensaver_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
_screensaver_set(state, true);
}
/* When pressed, disables the screensaver on active output */
void disable_screensaver_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
_screensaver_set(state, false);
}
/* Put the device into a special configuration mode */ /* Put the device into a special configuration mode */
void config_enable_hotkey_handler(device_t *state, hid_keyboard_report_t *report) { void config_enable_hotkey_handler(device_t *state, hid_keyboard_report_t *report) {
@ -209,6 +224,11 @@ void handle_wipe_config_msg(uart_packet_t *packet, device_t *state) {
load_config(state); load_config(state);
} }
/* Update screensaver state after received message */
void handle_screensaver_msg(uart_packet_t *packet, device_t *state) {
state->config.output[BOARD_ROLE].screensaver.mode = packet->data[0];
}
/* Process consumer control message */ /* Process consumer control message */
void handle_consumer_control_msg(uart_packet_t *packet, device_t *state) { void handle_consumer_control_msg(uart_packet_t *packet, device_t *state) {
queue_cc_packet(packet->data, state); queue_cc_packet(packet->data, state);
@ -254,9 +274,9 @@ void handle_api_msgs(uart_packet_t *packet, device_t *state) {
memcpy(ptr, &packet->data[1], map->len); memcpy(ptr, &packet->data[1], map->len);
} }
else if (packet->type == GET_VAL_MSG) { else if (packet->type == GET_VAL_MSG) {
uart_packet_t response = {.type=GET_VAL_MSG, .data={[0] = value_idx}}; uart_packet_t response = {.type=GET_VAL_MSG, .data={[0] = value_idx}};
memcpy(&response.data[1], ptr, map->len); memcpy(&response.data[1], ptr, map->len);
queue_cfg_packet(&response, state); queue_cfg_packet(&response, state);
} }
/* With each GET/SET message, we reset the configuration mode timeout */ /* With each GET/SET message, we reset the configuration mode timeout */

View File

@ -123,6 +123,7 @@ enum packet_type_e {
SYNC_BORDERS_MSG = 8, SYNC_BORDERS_MSG = 8,
FLASH_LED_MSG = 9, FLASH_LED_MSG = 9,
WIPE_CONFIG_MSG = 10, WIPE_CONFIG_MSG = 10,
SCREENSAVER_MSG = 11,
HEARTBEAT_MSG = 12, HEARTBEAT_MSG = 12,
GAMING_MODE_MSG = 13, GAMING_MODE_MSG = 13,
CONSUMER_CONTROL_MSG = 14, CONSUMER_CONTROL_MSG = 14,
@ -540,6 +541,8 @@ void screenlock_hotkey_handler(device_t *, hid_keyboard_report_t *);
void output_config_hotkey_handler(device_t *, hid_keyboard_report_t *); void output_config_hotkey_handler(device_t *, hid_keyboard_report_t *);
void wipe_config_hotkey_handler(device_t *, hid_keyboard_report_t *); void wipe_config_hotkey_handler(device_t *, hid_keyboard_report_t *);
void config_enable_hotkey_handler(device_t *, hid_keyboard_report_t *); void config_enable_hotkey_handler(device_t *, hid_keyboard_report_t *);
void enable_screensaver_hotkey_handler(device_t *, hid_keyboard_report_t *);
void disable_screensaver_hotkey_handler(device_t *, hid_keyboard_report_t *);
void handle_keyboard_uart_msg(uart_packet_t *, device_t *); void handle_keyboard_uart_msg(uart_packet_t *, device_t *);
void handle_mouse_abs_uart_msg(uart_packet_t *, device_t *); void handle_mouse_abs_uart_msg(uart_packet_t *, device_t *);
@ -563,6 +566,7 @@ void handle_proxy_msg(uart_packet_t *, device_t *);
void handle_api_msgs(uart_packet_t *, device_t *); void handle_api_msgs(uart_packet_t *, device_t *);
void handle_api_read_all_msg(uart_packet_t *, device_t *); void handle_api_read_all_msg(uart_packet_t *, device_t *);
void handle_toggle_gaming_msg(uart_packet_t *, device_t *); void handle_toggle_gaming_msg(uart_packet_t *, device_t *);
void handle_screensaver_msg(uart_packet_t *, device_t *);
void switch_output(device_t *, uint8_t); void switch_output(device_t *, uint8_t);

View File

@ -70,15 +70,11 @@
HID_LOGICAL_MIN ( 0 ) ,\ HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX ( 1 ) ,\ HID_LOGICAL_MAX ( 1 ) ,\
\ \
/* Left, Right, Middle, Backward, Forward buttons */ \ /* Left, Right, Mid, Back, Forward buttons + 3 extra */ \
HID_REPORT_COUNT( 5 ) ,\ HID_REPORT_COUNT( 8 ) ,\
HID_REPORT_SIZE ( 1 ) ,\ HID_REPORT_SIZE ( 1 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
\ \
/* 3 bit padding */ \
HID_REPORT_COUNT( 1 ) ,\
HID_REPORT_SIZE ( 3 ) ,\
HID_INPUT ( HID_CONSTANT ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
\ \
/* X, Y position [MOUSE_MIN, 32767] */ \ /* X, Y position [MOUSE_MIN, 32767] */ \

View File

@ -58,6 +58,20 @@ hotkey_combo_t hotkeys[] = {
.acknowledge = true, .acknowledge = true,
.action_handler = &toggle_gaming_mode_handler}, .action_handler = &toggle_gaming_mode_handler},
/* Enable screensaver for active output */
{.modifier = KEYBOARD_MODIFIER_LEFTCTRL | KEYBOARD_MODIFIER_RIGHTSHIFT,
.keys = {HID_KEY_S},
.key_count = 1,
.acknowledge = true,
.action_handler = &enable_screensaver_hotkey_handler},
/* Disable screensaver for active output */
{.modifier = KEYBOARD_MODIFIER_LEFTCTRL | KEYBOARD_MODIFIER_RIGHTSHIFT,
.keys = {HID_KEY_X},
.key_count = 1,
.acknowledge = true,
.action_handler = &disable_screensaver_hotkey_handler},
/* Erase stored config */ /* Erase stored config */
{.modifier = KEYBOARD_MODIFIER_RIGHTSHIFT, {.modifier = KEYBOARD_MODIFIER_RIGHTSHIFT,
.keys = {HID_KEY_F12, HID_KEY_D}, .keys = {HID_KEY_F12, HID_KEY_D},

View File

@ -133,9 +133,11 @@ void heartbeat_output_task(device_t *state) {
blink_led(state); blink_led(state);
} }
#ifdef DH_DEBUG
/* Holding the button invokes bootsel firmware upgrade */ /* Holding the button invokes bootsel firmware upgrade */
if (is_bootsel_pressed()) if (is_bootsel_pressed())
reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0); reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0);
#endif
uart_packet_t packet = { uart_packet_t packet = {
.type = HEARTBEAT_MSG, .type = HEARTBEAT_MSG,

View File

@ -78,6 +78,7 @@ const uart_handler_t uart_handler[] = {
{.type = FLASH_LED_MSG, .handler = handle_flash_led_msg}, {.type = FLASH_LED_MSG, .handler = handle_flash_led_msg},
{.type = GAMING_MODE_MSG, .handler = handle_toggle_gaming_msg}, {.type = GAMING_MODE_MSG, .handler = handle_toggle_gaming_msg},
{.type = CONSUMER_CONTROL_MSG, .handler = handle_consumer_control_msg}, {.type = CONSUMER_CONTROL_MSG, .handler = handle_consumer_control_msg},
{.type = SCREENSAVER_MSG, .handler = handle_screensaver_msg},
/* Config */ /* Config */
{.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg}, {.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg},

BIN
user-manual.pdf Normal file

Binary file not shown.