Ortho keyboard with Rust firmware.
Keyboard created with CadQuery.
The firmware is under GNU GPL 3.0. The hardware is under CERN-OHL-S. See the respective directories for this.
Ortho keyboard with rust firmware
While OnHold lets you change behavior if pressed or held, and chording lets you act as a different key if more are held at the same time, combos act when you press keys in series.
Chroding: Press J
and K
at the same time to produce W
.
Combo: Press J
. Release J
. Press K
within time_limit
and produce W
.
So chording is pressing at the same time, and combos are pressing in the right order in short succession.
KeyType{
DoubleTap(Key, u8, Key),
// ...
}
If pressed once it works as normal key. If pressed and held, it works as a normal key.
If you double tap the key (press**press) within the time limit (u8
) then it acts as key2.
How tap-tap&hold should be handled I'm not fully sure.
Perhaps there are several types hidden here.
KeyType {
TapHold, // Press-Release-Press&Hold
DoubleTap, // Press-Release-Press-Release
DoubleTapHold, // Press-Release-Press-Release-Press&Hold
}
There are a lot of possibilities. Might be feature creep.
I want to be able to press two keys "at the same time" and make them behave like a different key.
Eg. I press J
, I get J
. I press k
I get k
. But if I press JK
in "quick succsession" I want them to trigger as Enter
Having OnHold and DoubleTap is all nice and dandy. But I should not have to choose between them! I should be able to have both on the same key.
I should be able to say
(
KeyCode(A),
DoubleTap(KeyCode(Esc), 100ms),
OnHold(KeyCode(Shift), 100ms),
TapHold(Function(...), 100ms, 100ms),
DoubleTapHold(LayerMo(4), 100ms, 100ms, 50ms)
And that is ONE key. I should be able to do all the things on one and the same key without there being any problems. So this key for example would write A
when pressed normally. If pressed twice within 100ms it would produce Esc
. If held for 100ms it would produce Shift
. You get far by just allowing this.
But I want more. TapHold
would be press-release-press-hold, and that would execute a Function
here.
DoubleTapHold
is the same thing, but press-release-press-release-press-hold. And that would move us to to layer+4.
This allows one key to suddenly become 5 keys.
On top of this we have Chording
and Combo
that needs to be handled somehow. But all types should be allowed, always.
The current way to check what keys are pressed is done like this
fn debounced_scan(&mut self) -> State {
let s1 = self.scan();
delay_ms(20);
let s2 = self.scan();
State::intersect(s1, s2)
}
we scan the matrix two time, do an intersection of the two states, and then say that the intersection is the state. This is a basic and working way of doing it, but it makes it harder to implement things like OnHold
.
A different approach to the scanning is to "tick" the state. Lets say we have a mut State
instead. Something like
struct Pos {
row:u8,
col: u8
}
struct State {
pressed: Vec<(Pos, u8)>, // Position of key, and how many ticks it has been pressed
leds: u8,
impl State {
fn update(&self, scan: Vec<Pos>) {
scan.iter().map(self::tickOrAdd)
self.pressed.iter().onlyKeepPosPresentInScan()
}
}
So the state keeps a Vec
(or Set
or w/e) about which keys are pressed and for how many ticks they have been pressed. For each scan we do, we send in the list of pressed keys to State and lets the state update the pressed keys.
If a Pos is not present in pressed
but it is in scan
, it gets added to pressed
with 0 ticks.
If a Pos is already present, increase the tick by one.
If the Pos is present in pressed
but not in scan
, remove the the Pos.
After each scan
we can then poll the State
and ask for all Pos with a higher tick than X. We can then ask Layout
what key we should register based on how many ticks that Pos has. Layout can then decide if it should give us N or M based on the number of ticks.
This can make OnHold real easy. Just check what key we are based on ticks.
The scan()
method then become something like
fn scan(mut &self) {
let scan_result = self.scan();
self.state.update(scan_result);
delay(5);
}
And then when we have the state we can do something like let pressed_keys: Vec<u8> = state.getKeys()
which returns the pressed keys -- based on the seen state and layout.
In places like
waddle/firmware/waddle/src/state.rs
Lines 96 to 103 in e7d38b8
As the stack is quite limited it would be good to rewrite this part (and others like it) to remove the recursion. Do a loop instead.
If I press A
, I want to send the letter A
.
If I hold A
for X time I want the system to send Shift
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.