ahtn / keyplus Goto Github PK
View Code? Open in Web Editor NEWAn easy to use, wired and wireless modular keyboard firmware
License: MIT License
An easy to use, wired and wireless modular keyboard firmware
License: MIT License
Also, when I try to install pyside, it gives errors:
-- Could NOT find LibXslt (missing: LIBXSLT_LIBRARIES LIBXSLT_INCLUDE_DIR) (Required is at least version "1.1.19")
Qt QTGUI library not found.
Qt QTXML library not found.
Qt QTCORE library not found.
CMake Error at ApiExtractor/CMakeLists.txt:82 (qt4_add_resources):
Unknown CMake command "qt4_add_resources".
Something things that I would like the matrix scanner configuration to support:
Currently the xmega doesn't use the nRF24L01+ IRQ pin. The built in packet FIFO of the nRF24L01+ can only store 3 packets at a time, so it's possible that packets will be dropped if we do not process them fast enough. This didn't use to be an issue, but after some other changes to the RF protocol I'm now noticing dropped packets when the xmega is acting as an RF receiver.
Also, with the IRQ pin it will be possible to better utilize sleep modes for improved power efficiency when acting as a wireless transmit.
Having an API to send and receive arbitrary data between the master and slave devices would allow for new features were sending bulk data between devices is needed.
Pretty much the title. Support WS2812B RGB LEDs that are commonly used for underglow. One common use is to indicate the active layer, so this relates somewhat to #12.
I connected a device that was sending invalid packets to the nRF receiver, and this broke keyboard input after the receiver seemed to try parse the packets as keyboard packets. The RF packet parser needs to be made more robust as these packets where not even a valid size for keyboard input. Also, it seems that the parser would attempt to scan these packets even when no devices had been paired, which should not be possible when the pairing algorithm is working correctly.
To detect and debug issues of this nature, it would be good to write a packet fuzzer that can send malformed/random packets to the receiver at different rates.
Many system need to perform task when a timer expiers. Currently most of the system that use this now are each running their own tasks that are checking if their timers have expired. Generalizing the system would allow them to share code and the memory used to track timers.
It would make it easier to use interrupt timers instead of relying on polling to check if the tasks should be triggered. The interrupt system would only need to be set for the timer that is closest to expiring. When that timer expires, the interrupt would be called, the appropriate task run, and the interrupt would be reset to the next timer. This would benefit a future bluetooth implementation as it would allow the code to sleep much more than the current system.
Currently when building the firmware it can only include settings defined in the source code. It would be much more convinent to build the firmware with a layout file "pre-loaded" into the target hex file. This should easily be done with a python script.
PyQt5 has better support on MacOS and the newest versions of python3. Originally I choose to use pyside because it is basically equivalent to PyQt5 just with LGPL instead of GPL. However, the license difference is not an issue for the host software. Since PyQt5 is easier to support it makes sense to switch to it.
Before switching, need to check everything is working correctly on Linux and Windows under PyQt5.
When a device is powerd on and detects USB voltage on its VBUS check pin, it needs to decide whether it has connection to a USB host, or if it only has I2C connections to other devices. This is important because only the device with the host connection should enable its NRF24 module. If multiple nRF24 modules are active simultaneously, they will interfer with each others auto-ack functionality.
When the device only has one USB port, this should not be too difficult, as it can leave it's USB data lines active at all times and only run the initialization code after a IRQ, but continue operating as normal.
However, if the DUAL_USB
feature is used, then that implementation cannot be used, since the USB data lines need to be switched to I2C. So in this case, the device should active an I2C connection on each of its ports in turn and ping for other devices. In this way, it can deduce that a port is I2C. Whenever a device, finds out that it has a USB port, it should ping the other devices, so that they know that all their other ports can be switched to I2C.
Handling USB requests is handled differently on each platform. Need to implement a framework so that they can all share the same implementation.
Implement proper unicode support for USB string descriptors. Currently, the code assumes strings stored in the device configuration are assumed to be ASCII. However, according to the USB spec, strings should be encoded in UTF-16LE.
The layout generator will also need to correctly encode and decode strings these strings when generating/parsing the device settings.
The master device should be able to provide status information for connected slave devices:
I'm really confused about how to do this, at the top of the example file it says "Generated on date
on time
", is there some program I run to generate this?
The USB vendor IN endpoint is unbuffered. This can cause issuse when the host sends commands and expects a response but the device overwrites the pending message on the IN endpoint. The code should either lock the endpoint when it is in use by a command, or the vendor IN messages need to be buffered.
Currently the code has indicator LED's added in the HID descriptor, but it has no way to use them yet. It would also be good if the indicator LED's can work with user define layers.
I would like indicator LED's to be easily configurable from the yaml config file. The pins on the controller would be allocated in a similar way as the row and column pins of the matrix. That is the controller would map a list of LED pins LED0, LED1, ...
to a dedicated list of LED hardware pins on the controller C0, C1, ...
based on the number of LED's describe in the config file.
The user would then define roles to the LEDx pins in the config file something like this for each device:
keyboard_device:
# ...
num_leds: 4
led0: caps_lock
led1: num_lock
led2: kana
led3: layer2
# ...
The current implementation of modkeys only supports left or right modifiers but not both.
To allow both kinds of modifiers at once, we would need to use extended keycodes (EKC). An extended keycode for modkeys would look like this:
[0x00] -> KC_MODKEY
[0x02] -> 8-bit modifier mask
[0x03] -> 8-bit HID keycode
And the modkey handler would need to be extended appropriately.
Since modkeys account for the majority off key presses when typing, I'd like to build this implementation on top of the current one, as using EKC's have a little bit of extra overhead in terms of code size and speed.
The nRF24 has an internal FIFO for storing up to 3 packets. Currently when sending a matrix packet, the code doesn't check if the FIFO is full. So if RF packets are not transmitted fast enough (could easily happen if there is other RF interference), then packets will be dropped.
Instead the code should check if the FIFO is full, and if it is, it should use a separate FIFO on the microcontroller to hold packets and add them to the nRF24 FIFO when space becomes available.
Another potential issue that may arise from buffering, is the receiver may try to apply several matrix packets at a time. In the worst case, this could cause keys to be missed (it applies a key down and key up at the same time), so it might be beneficial for the receiver to read one matrix packet at a time and send any USB keycodes that are generated before moving to the next packet.
[sudo] password for :
Traceback (most recent call last):
File "uniflash.py", line 372, in <module>
detach_kernel_drivers(boot_dev, [0, 1])
File "uniflash.py", line 33, in detach_kernel_drivers
if dev.is_kernel_driver_active(i):
AttributeError: 'NoneType' object has no attribute 'is_kernel_driver_active'
Currently the code only use a fixed RF channel for the nRF24 radio. To improve RF performance in noisy environments it would be benefical to implement some form of channel hoping. The implementation would have need to be compatible with the unifying protocol.
Frequency hoping benefits the mouse more than the keyboard, and it adds a certain amount of overhead that is not present when using a fixed channel approach. So, ideally we should support both approaches.
Unlike Linux and OS X, on Windows it is always necessary to have a driver to communicate with a USB device, so the uniflash.py
script probably won't work on Windows without a driver for the Unifying receiver.
To get the script to work, it might be as simple as installing the Logitech drivers for the Unifying receiver. If that doesn't work, it might be necessary to use zadig to apply a generic libusb driver to communicate with the Unifying receiver.
Currently the debounce settings are hard coded at compile time.
List of things that should probably be configurable:
From section 3.1.16 of the I2C spec:
If the data line (SDA) is stuck LOW, the master should send nine clock pulses. The device
that held the bus LOW should release it sometime within those nine clocks. If not, then
use the HW reset or cycle power to clear the bus.
The efm8 series of 8051 microprocessors by Silicon Labs are probably the cheapest, smallest and simplest FS USB capable micoprocessors on the market. Some highlights include:
The efm8 series would be best suited for USB only keyboards, but could also support I2C split and maybe nRF24 receiver mode. Battery mode would probably not be implemented for efm8.
According to Silcon Lab's documentation, their USB SDK can be compiled with SDCC. So it should be quite easy to add support.
I can't use version 0.3 because my keyplus mini boards have version 0.2.2, and I don't have an avr programmer to update it. The keyplus app doesn't detect any devices. No error messages are shown.
Just a simple command to enable/disable packet monitoring for debug purposes. This can already be done using usb_print
but it needs to be built in at compile time.
The current implementation of mouse movement keycodes is just a place holder. Need to come up with a good implementation for mouse acceleration and add the corresponding settings to the config file.
Advantages of nRF52840:
Instead of running keyplus on the keyboard microcontroller itself, keyplus could be run on the host OS and remap keycodes in software. This would allow the code to be used with keyboards that do not have support for custom firmware, and it could potentially make debugging keycode handlers easier than on microcontrollers which have poorer debug support. It would also make the output of unicode characters easier. Once a reliable way of reading/writing keycodes to the host OS is found, it is not too difficult to implement either.
A long time ago I had a working demo of this which used libxdo
to generate keycodes and read the keyboard input from /dev/hidraw
. It worked reasonably well, but it was running in user space so it occasionally had latency issues. Also, it only worked in X, since it relied on libxdo
to generate output. So a lower level approach, like a kernel driver would probably be needed to make this work well. Right now I can't find the relevant commit to this code in my old repository.
This project would not be super serious and would mostly be a fun way to learn about Linux driver development, more than it would be to make something useful.
> sudo make BOARD=keyplus_mini program-boot
python3 ../host-software/keyplus-cli program \
\
--new-id 0 \
--layout "../layouts/basic_split_test.yaml" \
--rf "../layouts/test_rf_config.yaml" \
--fw-hex "build/keyplus_mini-atxmega32a4u/xmega_keyplus-keyplus_mini-atxmega32a4u.hex" \
-M 0x7600 0x7800 0x0800 \
-o ""build/keyplus_mini-atxmega32a4u/xmega_keyplus-keyplus_mini-atxmega32a4u-basic_split_test.hex"" \
-F chip_name="ATxmega32A4U" \
-F scan_method=fast_row_col \
# ./scripts/flash_hex.sh "build/keyplus_mini-atxmega32a4u/xmega_keyplus-keyplus_mini-atxmega32a4u-basic_split_test.hex" atxmega32a4u
python3 ../host-software/keyplus-cli bootloader -p ATxmega32A4U | sleep 1.5
Error: Couldn't find any matching devices.
python3 ./xusb-boot/scripts/bin/xusbboot-cli -f "build/keyplus_mini-atxmega32a4u/xmega_keyplus-keyplus_mini-atxmega32a4u-basic_split_test.hex" -mcu atxmega32a4u
Traceback (most recent call last):
File "./xusb-boot/scripts/bin/xusbboot-cli", line 183, in <module>
if (dev.vendor_id, dev.product_id) in xusbboot.DEFAULT_USB_IDS:
AttributeError: module 'xusbboot' has no attribute 'DEFAULT_USB_IDS'
Makefile:227: recipe for target 'program-boot' failed
make: *** [program-boot] Error 1
OS X has not always exposed the interface number of HID devices. The current master branch of HIDAPI has a workaround for this, but the current homebrew formula builds from a really old commit in the HIDAPI repo (0.8.0-rc1from 2013).
It looks like there is a proper fix here which requires patching this line.
To make things easier for OS X users, a static build needs to be made for OS X which bundles a recent/patched version of the HIDAPI library.
ruamel.yaml
should make this easier.Current for each board define, it is required to provided the explicit matrix scan mapping for each device in the layout file:
scan_mode:
mode: col_row # options: col_row, pins, none
rows: 4
cols: 6
# maps how keys are physically wired, to how they appear visually
matrix_map: [
r2c2, r2c1, r2c0, r2c3, r2c4, r2c5,
r3c2, r3c1, r3c0, r3c3, r3c4, r3c5,
r1c2, r1c1, r1c0, r1c3, r1c4, r1c5,
r0c2, r0c1, r0c0, r0c3, r0c4, r0c5,
]
However, if the keyboard is using a PCB with a fix layout, it would be easier if the user could load a preset matrix map for their board, with one line:
presets: <BOARD_NAME>
It would be easiest to bundle the preset configurations with the loader. If this list of presets is updated frequently, then it would be convinent to have a command in the loader to download the latest version.
When generating the layout data for a target board that only supports USB and no wireless/wired split connectivity, then the layout flasher should only include the layout for the target board.
The config file format should support using different language modes. The idea is to add a default language field like default_lang: 'de'
, which would then select the default keymap lookup table from the python script for the corresponding language. So with that set, the user would be able to just type ö
into their keymap, and it would look for ö
in the keymap table and generate the corresponding key(s) when generating the layout file, adding AltGr, Shift, etc. as necessary.
It would be good to support changing the language map on a per key and per layer basis as well.
In your blog post, you mentioned that the firmware can be updated wirelessly, and that that's important so that it's possible to "apply security updates for the wireless protocol". That begs two questions.
Recently I rewrote most of the python API for controlling keyboards, but have yet to integrate most of the features into the layout flasher. There's a lot of code left over from the old system that needs to be removed.
Also, the GUI code was hacked together as I added features so the code has become quite messy and needs to be cleaned up. Should try to aim to break it up into separate files and make it reusable in a GUI editor in the future.
The GUI tool should be able to flash nRF24LU1+ devices, and should ideally handle:
nrf24lu1p-512-bootloader
raw HID bootloaderUsing Python 3.5.2 and trying to run the uniflash info command, I get the failure at line 206. I can comment it out and keep moving, but I think I need the results of that to know whether to use the 16 or 32 hex file.
I'm configuring my keyplus mini firmware, but I don't know what the keycodes are. There should be a list of them.
The flasher should display a warning if a hex file is not intended for the current mcu/board. Since there isn't really a way to add metadata to hex files, it will be necessary to either use the file nameor by extending the hex file format. Extending the hex file format is probably the more robust solution, but the extra data might need to be removed to flash the file with external tools.
If you accidentally load a bad hex file, you can force the device to enter bootloader mode by following these steps:
It would be convenient if you could enter any unicode character into a layout file, and then if the character is not supported by the current keyboard layout / language map, then the software automatically generate a key sequence to input the character using the current OS supported input method.
Since each OS has its own method of inputting unicode characters, there's probably a need to track the current OS in the firmware. Then unicode character macro could use the appropriate method to input the character. This value could be switched with keycodes or in the flasher (this value could also be used to swap modifier keys on MacOS).
For, reference here's how unicode characters can be input on different OS's:
Ctrl+Shift+u
and then type in the unicode code pointNot too difficult since most of the modules should already be using and initialization functions. This will avoid the host disconnecting and reconnecting our USB port.
Add an addition mode to the GUI tool to pair mice.
The ATmega32u4 does not have many significant advantages compared to other architectues, however it is the most popular controller currently being used in hobbiest keyboard projects thanks to the Teensy and Arduino Pro Micro dev boards in addition to having lots of firmware support.
It might be possible to update the bootloader without needing an external programmer. There's a program BootJacker that can run from application space, find the spm
instruction used by the current bootloader, and use it to write to flash. However, if the device has certain lock bits set, this hack won't work. Source code.
If the device doesn't have lock bits protecting the bootloader section, then it should be possible to replace the bootloader. I've corrupted pro micro's before by writing a flash files that overlapped with the bootloader section, so I think this hack might work for them. If this hack can work reliably, then an Arduino script could be used to replace the pro micro bootloader with a better one.
Right now keyplus is using the VID:0x6666 which is USB-IF official VID for prototype products. To avoid conflicts with other USB devices in the field, keyplus should have VID/PID for core devices (keyplus mini controller, xusb-boot, nrf24lu1p-512-bootloader, keyplus nRF24LU1+ dongle, generic xmega keyplus device). Buying a VID from USB-IF is expensive, but their are some free/cheap options for open source/small projects:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.