Cleanup, Refactoring, Silkscreen update

- Code refactor, still far from great but a bit less crappy
- Unify behavior - Mouse Zoom feature was made toggle and
  moved from ALT to Right ALT + Right CTRL to avoid interfering
  with OS
- Added optional screensaver "Pong" mode, prevents sleep and it's fun
- Updated README
- Added more visible pin1 marking on PCB for digital isolator
- Marked pins to solder with a star *
- Added checksum and version format support for flash config
This commit is contained in:
Hrvoje Cavrak 2024-01-21 01:45:22 +01:00
parent c21d5b14ea
commit 15da60bd95
35 changed files with 1462 additions and 1001 deletions

View File

@ -73,17 +73,23 @@ This will make the corresponding Pico board enter the bootloader upgrade mode an
## Misc features
#### Mouse slowdown
### Mouse slowdown
Ever tried to move that YT video slider to a specific position but your mouse moves too jumpy and suddenly you are moving your hand super-carefully like you're 5 and playing "Operation" all over again?
**Holding right ALT** while moving the mouse will slow it down considerably, enabling you to get the finer precision work done and still have your mouse moving quickly otherwise.
**Press right CTRL + right ALT** to toggle a slow-mouse mode. The mouse pointer will slow down considerably, enabling you to get the finer precision work done and still have your mouse moving normally by quickly pressing the same keys again.
#### Switch Lock
### Switch Lock
If you want to lock yourself to one screen, use ```RIGHT CTRL + L```.
This will make sure you won't accidentally leave your current screen. To turn off, press the same key combo again.
### Screensaver
Supposedly built in to prevent computer from entering standby, but truth be told - it is just fun to watch. Off by default, will make your mouse pointer bounce around the screen like a Pong ball. When enabled, it activates after a period of inactivity defined in user config header and automatically switches off as soon as you send any output towards that screen.
![Image](img/screensaver.gif)
## Hardware
[The circuit](schematics/DeskHop.pdf) is based on two Raspberry Pi Pico boards, chosen because they are cheap (4.10 € / pc), can be hand soldered and most suppliers have them in stock.
@ -152,22 +158,23 @@ The standard process to do that is using isopropyl alcohol and an old toothbrush
## Usage guide
#### Keyboard shortcuts
### Keyboard shortcuts
_Firmware upgrade_
- ```Right Shift + F12 + Left Shift + A``` - put board A in FW upgrade mode
- ```Right Shift + F12 + Left Shift + B``` - put board B in FW upgrade mode
_Usage_
- ```Right ALT``` - mouse slows down while it's pressed
- ```Right CTRL + Right ALT``` - Toggle slower mouse mode
- ```Right CTRL + L``` - Lock/Unlock mouse desktop switching
- ```Caps Lock``` - Switch between outputs
_Config_
- ```Right Shift + F12 + D``` - remove flash config
- ```Right Shift + F12 + Y``` - save screen switch offset
- ```Right Shift + F12 + S``` - turn on/off screensaver option
#### Switch cursor height calibration
### Switch cursor height calibration
This step is not required, but it can be handy if your screens are not perfectly aligned or differ in size. The objective is to have the mouse pointer come out at exactly the same height.
@ -177,11 +184,11 @@ Just park your mouse on the LARGER screen at the height of the smaller/lower scr
Repeat for the bottom border (if it's above the larger screen's border). This will get saved to flash and it should keep this calibration value from now on.
#### Other configuration
### Other configuration
Mouse speed can now be configured per output screen and per axis. If you have multiple displays under Linux, your X speed is probably too fast, so you need to configure it in user_config.h and rebuild. In the future, this will be configurable without having to do that.
#### Functional verification
### Functional verification
When you connect a new USB peripheral, the board will flash the led twice, and instruct the other board to do the same. This way you can test if USB and outgoing communication works for each board.
@ -239,7 +246,7 @@ There are several software alternatives you can use if that works in your partic
- Not tested with a wide variety of devices, I don't know how it will work with your hardware. There is a reasonable chance things might not work out-of-the-box.
- Advanced keyboards (with knobs, extra buttons or sliders) will probably face issues where this additional hardware doesn't work.
- Super-modern mice with 300 buttons might see some buttons not work as expected.
- NOTE: Both computers need to be connected and powered on for this to work (as each board gets powered by the computer it plugs into).
- NOTE: Both computers need to be connected and provide power to the USB for this to work (as each board gets powered by the computer it plugs into). Many desktops and laptops will provide power even when shut down nowadays. If you need to run with one board fully disconnected, you should be able to use a USB hub to plug both keyboard and mouse to a single port.
## Progress
@ -252,6 +259,7 @@ Planned features:
- ~~Support for USB hubs and single-sided operation~~
- Configurable screens (partially)
- ~~Permament configuration stored in flash~~
- Better support for keyboards with knobs and mice with mickeys
- Unified firmware for both Picos
- ... and more!

Binary file not shown.

Binary file not shown.

BIN
img/screensaver.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

@ -210,11 +210,11 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 5fd3f62a-d98c-4462-b185-53ee62a95831)
)
(fp_text user "GP12" (at -13.2 13.97 45) (layer "F.SilkS")
(fp_text user " GP12*" (at -13.2 13.97 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 654bded7-dc3d-4110-b5c8-9933d7968d40)
)
(fp_text user "GND" (at -12.8 19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at -12.8 19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 7a76e090-db64-49ef-9c3a-8fddc8374f01)
)
@ -234,19 +234,19 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 9af6d6e3-843a-4bca-90bd-2c3b9f316a84)
)
(fp_text user "GP15" (at -13.054 24.13 45) (layer "F.SilkS")
(fp_text user " GP15*" (at -13.054 24.13 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 9da49d49-7de1-42a4-91fb-7720c8535bf6)
)
(fp_text user "GND" (at 12.8 19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at 12.8 19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 9ec3ebbf-2742-4ec9-9764-07554be8b7fa)
)
(fp_text user "GND" (at -12.8 -6.35 45) (layer "F.SilkS")
(fp_text user " GND*" (at -12.8 -6.35 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp a268ddcc-2c85-484d-bb30-2c57a598aa04)
)
(fp_text user "GP13" (at -13.054 16.51 45) (layer "F.SilkS")
(fp_text user " GP13*" (at -13.054 16.51 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp a57e760a-aacf-4400-923a-ac969c8333f6)
)
@ -286,11 +286,11 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp c5d36558-3396-4928-b429-c319485ac106)
)
(fp_text user "VBUS" (at 13.3 -24.2 45) (layer "F.SilkS")
(fp_text user " VBUS*" (at 13.3 -24.2 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp c5dbf120-2592-4aa5-ba0e-17d002b6126a)
)
(fp_text user "GND" (at -12.8 -19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at -12.8 -19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp ced39d21-201b-4efd-a857-444103e82d38)
)
@ -306,11 +306,11 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp e7db89ba-8097-461c-922f-efe95c69b626)
)
(fp_text user "3V3" (at 12.9 -13.9 45) (layer "F.SilkS")
(fp_text user " 3V3*" (at 12.9 -13.9 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp eba83c67-b501-49f2-b261-9eabd4774e8b)
)
(fp_text user "GND" (at 12.8 -19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at 12.8 -19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp ebec5c26-3696-4fb8-bce4-7240f2a1628b)
)
@ -318,7 +318,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp ee08acb9-4187-403c-977d-30480b9f54e8)
)
(fp_text user "GP14" (at -13.1 21.59 45) (layer "F.SilkS")
(fp_text user " GP14*" (at -13.1 21.59 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp f416675b-3513-4287-a967-d510ec90166f)
)
@ -600,7 +600,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 04258d86-db8d-4f2f-83cf-9be5258ae56e)
)
(fp_text user "GND" (at 12.8 -19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at 12.8 -19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 06d9de45-f0bc-4bab-bbd3-4d7fd75dc0de)
)
@ -608,7 +608,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 080eb970-3605-4dea-8a86-d0f5e211360c)
)
(fp_text user "GP15" (at -13.054 24.13 45) (layer "F.SilkS")
(fp_text user " GP15*" (at -13.054 24.13 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 0eb03dac-5681-444f-b662-bd902e4a78d6)
)
@ -616,7 +616,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 244993d6-b401-400c-9c81-52250a283542)
)
(fp_text user "VBUS" (at 13.3 -24.2 45) (layer "F.SilkS")
(fp_text user " VBUS*" (at 13.3 -24.2 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 29420bbe-3d5e-48c4-8f10-f791ece49ffd)
)
@ -640,7 +640,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 42ae899d-cb96-4cf6-b779-72e6029879ca)
)
(fp_text user "GND" (at -12.8 19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at -12.8 19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 4dba1d28-fd16-4cfc-a36d-02e78edede5b)
)
@ -652,11 +652,11 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 5ae03de5-12f1-445f-a4d7-0534f3b50b2c)
)
(fp_text user "GND" (at -12.8 -19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at -12.8 -19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 5e0af269-21d7-4b63-b85d-a427da8e3c7e)
)
(fp_text user "GP17" (at 13.054 21.59 45) (layer "F.SilkS")
(fp_text user " GP17*" (at 13.054 21.59 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 60192e7d-24f9-415f-972e-4eab77a5b5e7)
)
@ -664,7 +664,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 69e44965-b51f-4579-a420-34c32d41c682)
)
(fp_text user "GND" (at -12.8 6.35 45) (layer "F.SilkS")
(fp_text user " GND*" (at -12.8 6.35 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 6a08809b-e049-49ec-a689-fecdb644c04e)
)
@ -672,7 +672,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 6eac192f-b906-44eb-85a6-d39b2e567c1d)
)
(fp_text user "GP14" (at -13.1 21.59 45) (layer "F.SilkS")
(fp_text user " GP14*" (at -13.1 21.59 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 704211ed-8e53-4fc9-8c55-04f6d1e97075)
)
@ -700,15 +700,15 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 924973f6-cb24-4d5e-9b4f-944cd7b8d3c2)
)
(fp_text user "3V3" (at 12.9 -13.9 45) (layer "F.SilkS")
(fp_text user " 3V3*" (at 12.9 -13.9 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 9916d5ae-1ea5-4b41-aa70-6a7f9b6c33e5)
)
(fp_text user "GP16" (at 13.054 24.13 45) (layer "F.SilkS")
(fp_text user " GP16*" (at 13.054 24.13 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp 99bb43bd-b729-4948-9bb2-e8ae89878b0c)
)
(fp_text user "GND" (at -12.8 -6.35 45) (layer "F.SilkS")
(fp_text user " GND*" (at -12.8 -6.35 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp bae5cebf-f67e-458f-bc62-d2ede84b1ad3)
)
@ -732,7 +732,7 @@
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp dcfd0f66-777c-43b2-b1d9-87dd102c9f3f)
)
(fp_text user "GND" (at 12.8 19.05 45) (layer "F.SilkS")
(fp_text user " GND*" (at 12.8 19.05 45) (layer "F.SilkS")
(effects (font (size 0.8 0.8) (thickness 0.15)))
(tstamp e1a37e64-c2ff-4550-aad7-7eee1cfd9296)
)
@ -1054,7 +1054,7 @@
(property "ki_keywords" "cap capacitor")
(path "/3c58f69e-1b44-4a9e-8696-820df6d6d00b")
(attr smd)
(fp_text reference "C2" (at 3.5429 0.0254) (layer "F.SilkS")
(fp_text reference "C2" (at 0.134809 1.830335) (layer "F.SilkS")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 06cb8b2c-1271-446d-a93d-130a270fe442)
)
@ -1602,7 +1602,7 @@
(property "ki_keywords" "cap capacitor")
(path "/fb534856-14b0-4379-bd15-7952c4f76624")
(attr smd)
(fp_text reference "C1" (at 3.556 0.0508) (layer "F.SilkS")
(fp_text reference "C1" (at -0.099564 -1.727201) (layer "F.SilkS")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 2a770326-88ad-4947-9ace-cdc0b6ff3bb8)
)
@ -5352,6 +5352,8 @@
)
(stroke (width 0) (type solid)) (fill solid) (layer "F.SilkS") (tstamp c5383451-e798-427e-8631-6dd983e845c0))
(gr_circle (center 64.048728 81.041328) (end 64.455128 81.041328)
(stroke (width 0.12) (type solid)) (fill solid) (layer "F.SilkS") (tstamp d88e0c1e-780a-4330-9e02-0d8f43cbcef0))
(gr_poly
(pts
(xy 71.346351 110.357574)
@ -6069,6 +6071,9 @@
(gr_text "DeskHop - Keyboard/Mouse Switch\n\nby Hrvoje Cavrak, 12/2023\n\nhttps://github.com/hrvach" (at 64.9732 96.266) (layer "B.SilkS") (tstamp e86cc18b-e914-42f8-adc9-6ea2bbb55ee5)
(effects (font (size 1.1 1.1) (thickness 0.15)) (justify left bottom mirror))
)
(gr_text "*" (at 60.4774 76.708) (layer "F.SilkS") (tstamp 0c6253e3-41c8-4ad8-a815-57aa39b7d021)
(effects (font (size 1 1) (thickness 0.15)) (justify left bottom))
)
(gr_text "PC 1" (at 83.9216 46.7868) (layer "F.SilkS") (tstamp 179b2c6b-34d3-4f8d-9920-6b082e4f9180)
(effects (font (size 0.8 0.8) (thickness 0.15)) (justify left bottom))
)
@ -6084,6 +6089,9 @@
(gr_text "DATA" (at 63.0428 99.9744 45) (layer "F.SilkS") (tstamp 6a8636ed-7847-4f79-be68-902fb28046b3)
(effects (font (size 0.8 0.8) (thickness 0.15)))
)
(gr_text "*" (at 72.9488 76.708) (layer "F.SilkS") (tstamp 7bf68895-0aeb-4dda-aa66-265c186f64a3)
(effects (font (size 1 1) (thickness 0.15)) (justify left bottom))
)
(gr_text "A" (at 68.834 53.34) (layer "F.SilkS") (tstamp cb5b8470-d35e-410c-b050-6e152f7e6a4c)
(effects (font (size 2 2) (thickness 0.15)) (justify left bottom))
)

View File

@ -1,6 +1,6 @@
{
"board": {
"active_layer": 0,
"active_layer": 37,
"active_layer_preset": "All Layers",
"auto_track_width": true,
"hidden_netclasses": [],

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:16+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Copper,L2,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:16*
%MOMM*%
%LPD*%
G01*

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:17+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Soldermask,Bot*%
%TF.FilePolarity,Negative*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:17*
%MOMM*%
%LPD*%
G01*

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:17+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Paste,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:17*
%MOMM*%
%LPD*%
G01*

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:17+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Legend,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:17*
%MOMM*%
%LPD*%
G01*

View File

@ -1,11 +1,11 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:17+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Profile,NP*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:17*
%MOMM*%
%LPD*%
G01*

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:44+01:00*%
%TF.CreationDate,2024-01-21T01:35:16+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Copper,L1,Top*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:44*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:16*
%MOMM*%
%LPD*%
G01*

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:17+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Soldermask,Top*%
%TF.FilePolarity,Negative*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:17*
%MOMM*%
%LPD*%
G01*

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:17+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Paste,Top*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:17*
%MOMM*%
%LPD*%
G01*

View File

@ -1,19 +1,20 @@
%TF.GenerationSoftware,KiCad,Pcbnew,7.0.9*%
%TF.CreationDate,2023-12-24T22:12:45+01:00*%
%TF.CreationDate,2024-01-21T01:35:17+01:00*%
%TF.ProjectId,DeskHop,4465736b-486f-4702-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Legend,Top*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 7.0.9) date 2023-12-24 22:12:45*
G04 Created by KiCad (PCBNEW 7.0.9) date 2024-01-21 01:35:17*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10C,0.000000*%
%ADD11C,0.120000*%
%ADD12C,0.150000*%
%ADD12C,0.466400*%
%ADD13C,0.150000*%
G04 APERTURE END LIST*
D10*
G36*
@ -2924,6 +2925,13 @@ X72829522Y-109711596D01*
X72833573Y-109711461D01*
X72839898Y-109711622D01*
G37*
D12*
X64281928Y-81041328D02*
G75*
G03*
X64281928Y-81041328I-233200J0D01*
G01*
D10*
G36*
X71346351Y-110357574D02*
G01*
@ -3631,7 +3639,17 @@ X73688095Y-110998032D01*
X73692147Y-110997865D01*
X73698471Y-110998026D01*
G37*
D12*
D13*
X60957036Y-75577819D02*
X60957036Y-75815914D01*
X60718941Y-75720676D02*
X60957036Y-75815914D01*
X60957036Y-75815914D02*
X61195131Y-75720676D01*
X60814179Y-76006390D02*
X60957036Y-75815914D01*
X60957036Y-75815914D02*
X61099893Y-76006390D01*
X84210760Y-46681095D02*
X84210760Y-45881095D01*
X84210760Y-45881095D02*
@ -3870,6 +3888,16 @@ X63851198Y-99678364D02*
X63474074Y-98924117D01*
X63474074Y-98924117D02*
X64228321Y-99301241D01*
X73428436Y-75577819D02*
X73428436Y-75815914D01*
X73190341Y-75720676D02*
X73428436Y-75815914D01*
X73428436Y-75815914D02*
X73666531Y-75720676D01*
X73285579Y-76006390D02*
X73428436Y-75815914D01*
X73428436Y-75815914D02*
X73571293Y-76006390D01*
X69313636Y-52516009D02*
X70266017Y-52516009D01*
X69123160Y-53087438D02*
@ -4978,6 +5006,16 @@ X73046679Y-81767186D02*
X73046679Y-82413683D01*
X73046679Y-82413683D02*
X73396865Y-82063497D01*
X73154428Y-81174563D02*
X73289115Y-81309250D01*
X73100554Y-81390062D02*
X73289115Y-81309250D01*
X73289115Y-81309250D02*
X73369928Y-81120688D01*
X73316053Y-81497811D02*
X73289115Y-81309250D01*
X73289115Y-81309250D02*
X73477677Y-81336187D01*
X71830435Y-88032431D02*
X71749623Y-88059368D01*
X71749623Y-88059368D02*
@ -5040,6 +5078,16 @@ X73419741Y-87466746D02*
X73365867Y-87574495D01*
X73365867Y-87574495D02*
X73231180Y-87709182D01*
X73311992Y-86496999D02*
X73446679Y-86631686D01*
X73258117Y-86712498D02*
X73446679Y-86631686D01*
X73446679Y-86631686D02*
X73527491Y-86443124D01*
X73473616Y-86820248D02*
X73446679Y-86631686D01*
X73446679Y-86631686D02*
X73635241Y-86658624D01*
X71857372Y-44825494D02*
X71776560Y-44852431D01*
X71776560Y-44852431D02*
@ -5510,6 +5558,16 @@ X73300428Y-92465934D02*
X73219616Y-92492871D01*
X73219616Y-92492871D02*
X73165741Y-92492871D01*
X73300428Y-91334563D02*
X73435115Y-91469250D01*
X73246554Y-91550062D02*
X73435115Y-91469250D01*
X73435115Y-91469250D02*
X73515928Y-91280688D01*
X73462053Y-91657811D02*
X73435115Y-91469250D01*
X73435115Y-91469250D02*
X73623677Y-91496187D01*
X97430435Y-88032431D02*
X97349623Y-88059368D01*
X97349623Y-88059368D02*
@ -5572,6 +5630,16 @@ X99019741Y-87466746D02*
X98965867Y-87574495D01*
X98965867Y-87574495D02*
X98831180Y-87709182D01*
X98911992Y-86496999D02*
X99046679Y-86631686D01*
X98858117Y-86712498D02*
X99046679Y-86631686D01*
X99046679Y-86631686D02*
X99127491Y-86443124D01*
X99073616Y-86820248D02*
X99046679Y-86631686D01*
X99046679Y-86631686D02*
X99235241Y-86658624D01*
X71830435Y-62632431D02*
X71749623Y-62659368D01*
X71749623Y-62659368D02*
@ -5634,6 +5702,16 @@ X73419741Y-62066746D02*
X73365867Y-62174495D01*
X73365867Y-62174495D02*
X73231180Y-62309182D01*
X73311992Y-61096999D02*
X73446679Y-61231686D01*
X73258117Y-61312498D02*
X73446679Y-61231686D01*
X73446679Y-61231686D02*
X73527491Y-61043124D01*
X73473616Y-61420248D02*
X73446679Y-61231686D01*
X73446679Y-61231686D02*
X73635241Y-61258624D01*
X71333998Y-85734868D02*
X71253185Y-85761805D01*
X71253185Y-85761805D02*
@ -5722,6 +5800,16 @@ X73273491Y-84872871D02*
X73192679Y-84899808D01*
X73192679Y-84899808D02*
X73138804Y-84899808D01*
X73300428Y-83714563D02*
X73435115Y-83849250D01*
X73246554Y-83930062D02*
X73435115Y-83849250D01*
X73435115Y-83849250D02*
X73515928Y-83660688D01*
X73462053Y-84037811D02*
X73435115Y-83849250D01*
X73435115Y-83849250D02*
X73623677Y-83876187D01*
X97441998Y-65424868D02*
X97361185Y-65451805D01*
X97361185Y-65451805D02*
@ -6574,6 +6662,16 @@ X99102211Y-43556780D02*
X99236898Y-43422093D01*
X99236898Y-43422093D02*
X99344648Y-43368218D01*
X99640959Y-43018032D02*
X99775646Y-43152719D01*
X99587085Y-43233531D02*
X99775646Y-43152719D01*
X99775646Y-43152719D02*
X99856459Y-42964157D01*
X99802584Y-43341280D02*
X99775646Y-43152719D01*
X99775646Y-43152719D02*
X99964208Y-43179656D01*
X71830435Y-49932431D02*
X71749623Y-49959368D01*
X71749623Y-49959368D02*
@ -6636,6 +6734,16 @@ X73419741Y-49366746D02*
X73365867Y-49474495D01*
X73365867Y-49474495D02*
X73231180Y-49609182D01*
X73311992Y-48396999D02*
X73446679Y-48531686D01*
X73258117Y-48612498D02*
X73446679Y-48531686D01*
X73446679Y-48531686D02*
X73527491Y-48343124D01*
X73473616Y-48720248D02*
X73446679Y-48531686D01*
X73446679Y-48531686D02*
X73635241Y-48558624D01*
X71757372Y-47355494D02*
X71676560Y-47382431D01*
X71676560Y-47382431D02*
@ -6930,6 +7038,16 @@ X98904242Y-54786120D02*
X98823430Y-54813057D01*
X98823430Y-54813057D02*
X98769555Y-54813057D01*
X98931179Y-53627812D02*
X99065866Y-53762499D01*
X98877305Y-53843311D02*
X99065866Y-53762499D01*
X99065866Y-53762499D02*
X99146679Y-53573937D01*
X99092804Y-53951060D02*
X99065866Y-53762499D01*
X99065866Y-53762499D02*
X99254428Y-53789436D01*
X97430435Y-49932431D02*
X97349623Y-49959368D01*
X97349623Y-49959368D02*
@ -6992,6 +7110,16 @@ X99019741Y-49366746D02*
X98965867Y-49474495D01*
X98965867Y-49474495D02*
X98831180Y-49609182D01*
X98911992Y-48396999D02*
X99046679Y-48531686D01*
X98858117Y-48612498D02*
X99046679Y-48531686D01*
X99046679Y-48531686D02*
X99127491Y-48343124D01*
X99073616Y-48720248D02*
X99046679Y-48531686D01*
X99046679Y-48531686D02*
X99235241Y-48558624D01*
X98182651Y-70777710D02*
X97724716Y-70696898D01*
X97859403Y-71100959D02*
@ -7106,6 +7234,16 @@ X72715680Y-89279436D02*
X72985054Y-89818184D01*
X72985054Y-89818184D02*
X73335240Y-89467998D01*
X73254428Y-88794563D02*
X73389115Y-88929250D01*
X73200554Y-89010062D02*
X73389115Y-88929250D01*
X73389115Y-88929250D02*
X73469928Y-88740688D01*
X73416053Y-89117811D02*
X73389115Y-88929250D01*
X73389115Y-88929250D02*
X73577677Y-88956187D01*
X97441998Y-93354868D02*
X97361185Y-93381805D01*
X97361185Y-93381805D02*
@ -7460,6 +7598,16 @@ X62561264Y-49350995D02*
X62507390Y-49458744D01*
X62507390Y-49458744D02*
X62372703Y-49593431D01*
X62453515Y-48381248D02*
X62588202Y-48515935D01*
X62399640Y-48596747D02*
X62588202Y-48515935D01*
X62588202Y-48515935D02*
X62669014Y-48327373D01*
X62615139Y-48704497D02*
X62588202Y-48515935D01*
X62588202Y-48515935D02*
X62776764Y-48542873D01*
X43329333Y-96536353D02*
X43291237Y-96574449D01*
X43291237Y-96574449D02*
@ -7592,6 +7740,16 @@ X36841951Y-92450183D02*
X36761139Y-92477120D01*
X36761139Y-92477120D02*
X36707264Y-92477120D01*
X36841951Y-91318812D02*
X36976638Y-91453499D01*
X36788077Y-91534311D02*
X36976638Y-91453499D01*
X36976638Y-91453499D02*
X37057451Y-91264937D01*
X37003576Y-91642060D02*
X36976638Y-91453499D01*
X36976638Y-91453499D02*
X37165200Y-91480436D01*
X60983521Y-85719117D02*
X60902708Y-85746054D01*
X60902708Y-85746054D02*
@ -7812,6 +7970,16 @@ X62643734Y-43541029D02*
X62778421Y-43406342D01*
X62778421Y-43406342D02*
X62886171Y-43352467D01*
X63182482Y-43002281D02*
X63317169Y-43136968D01*
X63128608Y-43217780D02*
X63317169Y-43136968D01*
X63317169Y-43136968D02*
X63397982Y-42948406D01*
X63344107Y-43325529D02*
X63317169Y-43136968D01*
X63317169Y-43136968D02*
X63505731Y-43163905D01*
X61724174Y-70761959D02*
X61266239Y-70681147D01*
X61400926Y-71085208D02*
@ -8284,6 +8452,16 @@ X36961264Y-87450995D02*
X36907390Y-87558744D01*
X36907390Y-87558744D02*
X36772703Y-87693431D01*
X36853515Y-86481248D02*
X36988202Y-86615935D01*
X36799640Y-86696747D02*
X36988202Y-86615935D01*
X36988202Y-86615935D02*
X37069014Y-86427373D01*
X37015139Y-86804497D02*
X36988202Y-86615935D01*
X36988202Y-86615935D02*
X37176764Y-86642873D01*
X35398895Y-65129743D02*
X35318083Y-65156680D01*
X35318083Y-65156680D02*
@ -8552,6 +8730,16 @@ X36961264Y-49350995D02*
X36907390Y-49458744D01*
X36907390Y-49458744D02*
X36772703Y-49593431D01*
X36853515Y-48381248D02*
X36988202Y-48515935D01*
X36799640Y-48596747D02*
X36988202Y-48515935D01*
X36988202Y-48515935D02*
X37069014Y-48327373D01*
X37015139Y-48704497D02*
X36988202Y-48515935D01*
X36988202Y-48515935D02*
X37176764Y-48542873D01*
X60983521Y-90799117D02*
X60902708Y-90826054D01*
X60902708Y-90826054D02*
@ -8618,6 +8806,16 @@ X62276516Y-89452247D02*
X62653640Y-89075123D01*
X62653640Y-89075123D02*
X62976889Y-89883245D01*
X62949951Y-88778812D02*
X63084638Y-88913499D01*
X62896077Y-88994311D02*
X63084638Y-88913499D01*
X63084638Y-88913499D02*
X63165451Y-88724937D01*
X63111576Y-89102060D02*
X63084638Y-88913499D01*
X63084638Y-88913499D02*
X63273200Y-88940436D01*
X35398895Y-57509743D02*
X35318083Y-57536680D01*
X35318083Y-57536680D02*
@ -8738,6 +8936,16 @@ X36961264Y-74750995D02*
X36907390Y-74858744D01*
X36907390Y-74858744D02*
X36772703Y-74993431D01*
X36853515Y-73781248D02*
X36988202Y-73915935D01*
X36799640Y-73996747D02*
X36988202Y-73915935D01*
X36988202Y-73915935D02*
X37069014Y-73727373D01*
X37015139Y-74104497D02*
X36988202Y-73915935D01*
X36988202Y-73915935D02*
X37176764Y-73942873D01*
X35398895Y-70209743D02*
X35318083Y-70236680D01*
X35318083Y-70236680D02*
@ -8920,6 +9128,16 @@ X36257203Y-89263685D02*
X36526577Y-89802433D01*
X36526577Y-89802433D02*
X36876763Y-89452247D01*
X36795951Y-88778812D02*
X36930638Y-88913499D01*
X36742077Y-88994311D02*
X36930638Y-88913499D01*
X36930638Y-88913499D02*
X37011451Y-88724937D01*
X36957576Y-89102060D02*
X36930638Y-88913499D01*
X36930638Y-88913499D02*
X37119200Y-88940436D01*
X61233707Y-57929178D02*
X61503081Y-57659804D01*
X61341457Y-58144677D02*
@ -9406,6 +9624,16 @@ X62445765Y-54770369D02*
X62364953Y-54797306D01*
X62364953Y-54797306D02*
X62311078Y-54797306D01*
X62472702Y-53612061D02*
X62607389Y-53746748D01*
X62418828Y-53827560D02*
X62607389Y-53746748D01*
X62607389Y-53746748D02*
X62688202Y-53558186D01*
X62634327Y-53935309D02*
X62607389Y-53746748D01*
X62607389Y-53746748D02*
X62795951Y-53773685D01*
X60983521Y-93339117D02*
X60902708Y-93366054D01*
X60902708Y-93366054D02*
@ -9510,6 +9738,16 @@ X62599765Y-92153871D02*
X62599765Y-92207746D01*
X62599765Y-92207746D02*
X62626702Y-92288558D01*
X62949951Y-91318812D02*
X63084638Y-91453499D01*
X62896077Y-91534311D02*
X63084638Y-91453499D01*
X63084638Y-91453499D02*
X63165451Y-91264937D01*
X63111576Y-91642060D02*
X63084638Y-91453499D01*
X63084638Y-91453499D02*
X63273200Y-91480436D01*
X35371958Y-62616680D02*
X35291146Y-62643617D01*
X35291146Y-62643617D02*
@ -9572,6 +9810,16 @@ X36961264Y-62050995D02*
X36907390Y-62158744D01*
X36907390Y-62158744D02*
X36772703Y-62293431D01*
X36853515Y-61081248D02*
X36988202Y-61215935D01*
X36799640Y-61296747D02*
X36988202Y-61215935D01*
X36988202Y-61215935D02*
X37069014Y-61027373D01*
X37015139Y-61404497D02*
X36988202Y-61215935D01*
X36988202Y-61215935D02*
X37176764Y-61242873D01*
X60983521Y-73019117D02*
X60902708Y-73046054D01*
X60902708Y-73046054D02*
@ -9998,6 +10246,16 @@ X62561264Y-87450995D02*
X62507390Y-87558744D01*
X62507390Y-87558744D02*
X62372703Y-87693431D01*
X62453515Y-86481248D02*
X62588202Y-86615935D01*
X62399640Y-86696747D02*
X62588202Y-86615935D01*
X62588202Y-86615935D02*
X62669014Y-86427373D01*
X62615139Y-86804497D02*
X62588202Y-86615935D01*
X62588202Y-86615935D02*
X62776764Y-86642873D01*
X35398895Y-54969743D02*
X35318083Y-54996680D01*
X35318083Y-54996680D02*
@ -10558,56 +10816,56 @@ X62896076Y-58691309D02*
X62842202Y-58691309D01*
X62842202Y-58691309D02*
X62761389Y-58718247D01*
X73980424Y-75262645D02*
X73932805Y-75310265D01*
X73932805Y-75310265D02*
X73789948Y-75357884D01*
X73789948Y-75357884D02*
X73694710Y-75357884D01*
X73694710Y-75357884D02*
X73551853Y-75310265D01*
X73551853Y-75310265D02*
X73456615Y-75215026D01*
X73456615Y-75215026D02*
X73408996Y-75119788D01*
X73408996Y-75119788D02*
X73361377Y-74929312D01*
X73361377Y-74929312D02*
X73361377Y-74786455D01*
X73361377Y-74786455D02*
X73408996Y-74595979D01*
X73408996Y-74595979D02*
X73456615Y-74500741D01*
X73456615Y-74500741D02*
X73551853Y-74405503D01*
X73551853Y-74405503D02*
X73694710Y-74357884D01*
X73694710Y-74357884D02*
X73789948Y-74357884D01*
X73789948Y-74357884D02*
X73932805Y-74405503D01*
X73932805Y-74405503D02*
X73980424Y-74453122D01*
X74361377Y-74453122D02*
X74408996Y-74405503D01*
X74408996Y-74405503D02*
X74504234Y-74357884D01*
X74504234Y-74357884D02*
X74742329Y-74357884D01*
X74742329Y-74357884D02*
X74837567Y-74405503D01*
X74837567Y-74405503D02*
X74885186Y-74453122D01*
X74885186Y-74453122D02*
X74932805Y-74548360D01*
X74932805Y-74548360D02*
X74932805Y-74643598D01*
X74932805Y-74643598D02*
X74885186Y-74786455D01*
X74885186Y-74786455D02*
X74313758Y-75357884D01*
X74313758Y-75357884D02*
X74932805Y-75357884D01*
X70572333Y-77067580D02*
X70524714Y-77115200D01*
X70524714Y-77115200D02*
X70381857Y-77162819D01*
X70381857Y-77162819D02*
X70286619Y-77162819D01*
X70286619Y-77162819D02*
X70143762Y-77115200D01*
X70143762Y-77115200D02*
X70048524Y-77019961D01*
X70048524Y-77019961D02*
X70000905Y-76924723D01*
X70000905Y-76924723D02*
X69953286Y-76734247D01*
X69953286Y-76734247D02*
X69953286Y-76591390D01*
X69953286Y-76591390D02*
X70000905Y-76400914D01*
X70000905Y-76400914D02*
X70048524Y-76305676D01*
X70048524Y-76305676D02*
X70143762Y-76210438D01*
X70143762Y-76210438D02*
X70286619Y-76162819D01*
X70286619Y-76162819D02*
X70381857Y-76162819D01*
X70381857Y-76162819D02*
X70524714Y-76210438D01*
X70524714Y-76210438D02*
X70572333Y-76258057D01*
X70953286Y-76258057D02*
X71000905Y-76210438D01*
X71000905Y-76210438D02*
X71096143Y-76162819D01*
X71096143Y-76162819D02*
X71334238Y-76162819D01*
X71334238Y-76162819D02*
X71429476Y-76210438D01*
X71429476Y-76210438D02*
X71477095Y-76258057D01*
X71477095Y-76258057D02*
X71524714Y-76353295D01*
X71524714Y-76353295D02*
X71524714Y-76448533D01*
X71524714Y-76448533D02*
X71477095Y-76591390D01*
X71477095Y-76591390D02*
X70905667Y-77162819D01*
X70905667Y-77162819D02*
X71524714Y-77162819D01*
X49044266Y-97498819D02*
X49044266Y-98213104D01*
X49044266Y-98213104D02*
@ -10734,46 +10992,46 @@ X61350089Y-97271266D02*
X60778661Y-97842695D01*
X60778661Y-97842695D02*
X61397708Y-97842695D01*
X60287369Y-75238779D02*
X60239750Y-75286399D01*
X60239750Y-75286399D02*
X60096893Y-75334018D01*
X60096893Y-75334018D02*
X60001655Y-75334018D01*
X60001655Y-75334018D02*
X59858798Y-75286399D01*
X59858798Y-75286399D02*
X59763560Y-75191160D01*
X59763560Y-75191160D02*
X59715941Y-75095922D01*
X59715941Y-75095922D02*
X59668322Y-74905446D01*
X59668322Y-74905446D02*
X59668322Y-74762589D01*
X59668322Y-74762589D02*
X59715941Y-74572113D01*
X59715941Y-74572113D02*
X59763560Y-74476875D01*
X59763560Y-74476875D02*
X59858798Y-74381637D01*
X59858798Y-74381637D02*
X60001655Y-74334018D01*
X60001655Y-74334018D02*
X60096893Y-74334018D01*
X60096893Y-74334018D02*
X60239750Y-74381637D01*
X60239750Y-74381637D02*
X60287369Y-74429256D01*
X61239750Y-75334018D02*
X60668322Y-75334018D01*
X60954036Y-75334018D02*
X60954036Y-74334018D01*
X60954036Y-74334018D02*
X60858798Y-74476875D01*
X60858798Y-74476875D02*
X60763560Y-74572113D01*
X60763560Y-74572113D02*
X60668322Y-74619732D01*
X63942933Y-77016780D02*
X63895314Y-77064400D01*
X63895314Y-77064400D02*
X63752457Y-77112019D01*
X63752457Y-77112019D02*
X63657219Y-77112019D01*
X63657219Y-77112019D02*
X63514362Y-77064400D01*
X63514362Y-77064400D02*
X63419124Y-76969161D01*
X63419124Y-76969161D02*
X63371505Y-76873923D01*
X63371505Y-76873923D02*
X63323886Y-76683447D01*
X63323886Y-76683447D02*
X63323886Y-76540590D01*
X63323886Y-76540590D02*
X63371505Y-76350114D01*
X63371505Y-76350114D02*
X63419124Y-76254876D01*
X63419124Y-76254876D02*
X63514362Y-76159638D01*
X63514362Y-76159638D02*
X63657219Y-76112019D01*
X63657219Y-76112019D02*
X63752457Y-76112019D01*
X63752457Y-76112019D02*
X63895314Y-76159638D01*
X63895314Y-76159638D02*
X63942933Y-76207257D01*
X64895314Y-77112019D02*
X64323886Y-77112019D01*
X64609600Y-77112019D02*
X64609600Y-76112019D01*
X64609600Y-76112019D02*
X64514362Y-76254876D01*
X64514362Y-76254876D02*
X64419124Y-76350114D01*
X64419124Y-76350114D02*
X64323886Y-76397733D01*
D11*
%TO.C,U1*%
X74844000Y-43334000D02*

View File

@ -1,7 +1,7 @@
M48
; DRILL file {KiCad 7.0.9} date Sunday, December 24, 2023 at 10:12:50PM
; DRILL file {KiCad 7.0.9} date Sunday, January 21, 2024 at 01:35:19AM
; FORMAT={-:-/ absolute / inch / decimal}
; #@! TF.CreationDate,2023-12-24T22:12:50+01:00
; #@! TF.CreationDate,2024-01-21T01:35:19+01:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.9
; #@! TF.FileFunction,NonPlated,1,2,NPTH
FMAT,2

View File

@ -1,7 +1,7 @@
M48
; DRILL file {KiCad 7.0.9} date Sunday, December 24, 2023 at 10:12:50PM
; DRILL file {KiCad 7.0.9} date Sunday, January 21, 2024 at 01:35:19AM
; FORMAT={-:-/ absolute / inch / decimal}
; #@! TF.CreationDate,2023-12-24T22:12:50+01:00
; #@! TF.CreationDate,2024-01-21T01:35:19+01:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,7.0.9
; #@! TF.FileFunction,Plated,1,2,PTH
FMAT,2

View File

@ -5,7 +5,7 @@
"Application": "Pcbnew",
"Version": "7.0.9"
},
"CreationDate": "2023-12-24T22:12:45+01:00"
"CreationDate": "2024-01-21T01:35:17+01:00"
},
"GeneralSpecs": {
"ProjectId": {

View File

@ -2,10 +2,11 @@
/* Default configuration */
const config_t default_config = {
.magic_header = 0x0B00B1E5,
.output[ACTIVE_OUTPUT_A] =
.magic_header = 0xB00B1E5,
.version = CURRENT_CONFIG_VERSION,
.output[OUTPUT_A] =
{
.number = ACTIVE_OUTPUT_A,
.number = OUTPUT_A,
.speed_x = MOUSE_SPEED_A_FACTOR_X,
.speed_y = MOUSE_SPEED_A_FACTOR_Y,
.border = {
@ -15,9 +16,9 @@ const config_t default_config = {
.screen_count = 1,
.screen_index = 0,
},
.output[ACTIVE_OUTPUT_B] =
.output[OUTPUT_B] =
{
.number = ACTIVE_OUTPUT_B,
.number = OUTPUT_B,
.speed_x = MOUSE_SPEED_B_FACTOR_X,
.speed_y = MOUSE_SPEED_B_FACTOR_Y,
.border = {
@ -27,4 +28,5 @@ const config_t default_config = {
.screen_count = 1,
.screen_index = 0,
},
.screensaver_enabled = SCREENSAVER_ENABLED,
};

View File

@ -21,77 +21,77 @@
* ============ Hotkey Handler Routines ============ *
* =================================================== */
void output_toggle_hotkey_handler(device_state_t* state) {
/* This is the main hotkey for switching outputs */
void output_toggle_hotkey_handler(device_t *state) {
/* If switching explicitly disabled, return immediately */
if (state->switch_lock)
return;
state->active_output ^= 1;
switch_output(state->active_output);
switch_output(state, state->active_output);
};
/* This key combo records switch y top coordinate for different-size monitors */
void screen_border_hotkey_handler(device_state_t* state) {
void screen_border_hotkey_handler(device_t *state) {
border_size_t *border = &state->config.output[state->active_output].border;
/* To deal away with 2 different keys, if we're above half, it's the top coord and vice versa */
/* To avoid having 2 different keys, if we're above half, it's the top coord */
if (state->mouse_y > (MAX_SCREEN_COORD / 2))
border->bottom = state->mouse_y;
else
border->top = state->mouse_y;
send_packet((uint8_t*)border, SYNC_BORDERS_MSG, sizeof(border_size_t));
save_config();
send_packet((uint8_t *)border, SYNC_BORDERS_MSG, sizeof(border_size_t));
save_config(state);
};
/* This key combo puts board A in firmware upgrade mode */
void fw_upgrade_hotkey_handler_A(device_state_t* state) {
void fw_upgrade_hotkey_handler_A(device_t *state) {
reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0);
};
/* This key combo puts board B in firmware upgrade mode */
void fw_upgrade_hotkey_handler_B(device_state_t* state) {
void fw_upgrade_hotkey_handler_B(device_t *state) {
send_value(ENABLE, FIRMWARE_UPGRADE_MSG);
};
/* This key combo prevents mouse from switching outputs */
void switchlock_hotkey_handler(device_state_t* state) {
void switchlock_hotkey_handler(device_t *state) {
state->switch_lock ^= 1;
send_value(state->switch_lock, SWITCH_LOCK_MSG);
}
/* When pressed, erases stored config in flash and loads defaults */
void wipe_config_hotkey_handler(device_state_t* state) {
/* When pressed, erases stored config in flash and loads defaults on both boards */
void wipe_config_hotkey_handler(device_t *state) {
wipe_config();
load_config();
load_config(state);
send_value(ENABLE, WIPE_CONFIG_MSG);
}
void mouse_zoom_hotkey_handler(device_state_t* state) {
if (state->mouse_zoom)
return;
void screensaver_hotkey_handler(device_t *state) {
state->config.screensaver_enabled ^= 1;
send_value(state->config.screensaver_enabled, SCREENSAVER_MSG);
}
send_value(ENABLE, MOUSE_ZOOM_MSG);
state->mouse_zoom = true;
};
void all_keys_released_handler(device_state_t* state) {
if (state->mouse_zoom) {
state->mouse_zoom = false;
send_value(DISABLE, MOUSE_ZOOM_MSG);
}
/* When pressed, toggles the current mouse zoom mode state */
void mouse_zoom_hotkey_handler(device_t *state) {
state->mouse_zoom ^= 1;
send_value(state->mouse_zoom, MOUSE_ZOOM_MSG);
};
/**==================================================== *
* ========== UART Message Handling Routines ======== *
* ==================================================== */
void handle_keyboard_uart_msg(uart_packet_t* packet, device_state_t* state) {
queue_kbd_report((hid_keyboard_report_t*)packet->data, state);
/* Function handles received keypresses from the other board */
void handle_keyboard_uart_msg(uart_packet_t *packet, device_t *state) {
queue_kbd_report((hid_keyboard_report_t *)packet->data, state);
state->last_activity[BOARD_ROLE] = time_us_64();
}
void handle_mouse_abs_uart_msg(uart_packet_t* packet, device_state_t* state) {
hid_abs_mouse_report_t* mouse_report = (hid_abs_mouse_report_t*)packet->data;
/* Function handles received mouse moves from the other board */
void handle_mouse_abs_uart_msg(uart_packet_t *packet, device_t *state) {
mouse_abs_report_t *mouse_report = (mouse_abs_report_t *)packet->data;
queue_mouse_report(mouse_report, state);
state->mouse_x = mouse_report->x;
@ -100,52 +100,69 @@ void handle_mouse_abs_uart_msg(uart_packet_t* packet, device_state_t* state) {
state->last_activity[BOARD_ROLE] = time_us_64();
}
void handle_output_select_msg(uart_packet_t* packet, device_state_t* state) {
/* Function handles request to switch output */
void handle_output_select_msg(uart_packet_t *packet, device_t *state) {
state->active_output = packet->data[0];
if (state->tud_connected)
stop_pressing_any_keys(&global_state);
release_all_keys(state);
restore_leds(state);
}
/* On firmware upgrade message, reboot into the BOOTSEL fw upgrade mode */
void handle_fw_upgrade_msg(void) {
void handle_fw_upgrade_msg(uart_packet_t *packet, device_t *state) {
reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0);
}
void handle_mouse_zoom_msg(uart_packet_t* packet, device_state_t* state) {
/* Comply with request to turn mouse zoom mode on/off */
void handle_mouse_zoom_msg(uart_packet_t *packet, device_t *state) {
state->mouse_zoom = packet->data[0];
}
void handle_set_report_msg(uart_packet_t* packet, device_state_t* state) {
/* Only board B sends LED state through this message type */
/* Process request to update keyboard LEDs */
void handle_set_report_msg(uart_packet_t *packet, device_t *state) {
state->keyboard_leds[BOARD_ROLE] = packet->data[0];
restore_leds(state);
}
void handle_switch_lock_msg(uart_packet_t* packet, device_state_t* state) {
/* Process request to block mouse from switching, update internal state */
void handle_switch_lock_msg(uart_packet_t *packet, device_t *state) {
state->switch_lock = packet->data[0];
}
/* Handle border syncing message that lets the other device know about monitor height offset */
void handle_sync_borders_msg(uart_packet_t* packet, device_state_t* state) {
void handle_sync_borders_msg(uart_packet_t *packet, device_t *state) {
border_size_t *border = &state->config.output[state->active_output].border;
memcpy(border, packet->data, sizeof(border_size_t));
save_config();
save_config(state);
}
/* When this message is received, flash the locally attached LED to verify serial comms */
void handle_flash_led_msg(uart_packet_t* packet, device_state_t* state) {
void handle_flash_led_msg(uart_packet_t *packet, device_t *state) {
blink_led(state);
}
/* When this message is received, wipe the local flash config */
void handle_wipe_config_msg(uart_packet_t *packet, device_t *state) {
wipe_config();
load_config(state);
}
void handle_screensaver_msg(uart_packet_t *packet, device_t *state) {
state->config.screensaver_enabled = packet->data[0];
}
/**==================================================== *
* ============== Output Switch Routines ============ *
* ==================================================== */
/* Update output variable, set LED on/off and notify the other board so they are in sync. */
void switch_output(uint8_t new_output) {
global_state.active_output = new_output;
restore_leds(&global_state);
void switch_output(device_t *state, uint8_t new_output) {
state->active_output = new_output;
restore_leds(state);
send_value(new_output, OUTPUT_SELECT_MSG);
/* If we were holding a key down and drag the mouse to another screen, the key gets stuck.
Changing outputs = no more keypresses on the previous system. */
stop_pressing_any_keys(&global_state);
release_all_keys(state);
}

View File

@ -19,7 +19,6 @@
*/
#include "main.h"
#include "hid_parser.h"
#define IS_BLOCK_END (collection.start == collection.end)
#define MAX_BUTTONS 16
@ -57,7 +56,7 @@ int32_t to_signed(globals_t *data) {
/* Given a value struct with size and offset in bits,
find and return a value from the HID report */
int32_t get_report_value(uint8_t* report, report_val_t *val) {
int32_t get_report_value(uint8_t *report, report_val_t *val) {
/* Calculate the bit offset within the byte */
uint8_t offset_in_bits = val->offset % 8;
@ -92,119 +91,135 @@ int32_t get_report_value(uint8_t* report, report_val_t *val) {
return result;
}
/* This method is far from a generalized HID descriptor parsing, but should work
* well enough to find the basic values we care about to move the mouse around.
* Your descriptor for a mouse with 2 wheels and 264 buttons might not parse correctly.
**/
uint8_t parse_report_descriptor(mouse_t *mouse, uint8_t arr_count,
uint8_t const *report, uint16_t desc_len) {
/* Get these elements and store them in the proper place in the mouse struct
* For example, to match wheel, we want collection usage to be HID_USAGE_DESKTOP_MOUSE, page to be HID_USAGE_PAGE_DESKTOP,
* usage to be HID_USAGE_DESKTOP_WHEEL, then if all of that is matched we store the value to mouse->wheel */
const usage_map_t usage_map[] = {
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_BUTTON, HID_USAGE_DESKTOP_POINTER, &mouse->buttons},
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_X, &mouse->move_x},
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_Y, &mouse->move_y},
{HID_USAGE_DESKTOP_MOUSE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_WHEEL, &mouse->wheel},
};
/* Some variables used for keeping tabs on parsing */
uint8_t usage_count = 0;
uint8_t g_usage = 0;
uint32_t offset_in_bits = 0;
uint8_t usages[64] = {0};
uint8_t* p_usage = usages;
collection_t collection = {0};
/* as tag is 4 bits, there can be 16 different tags in global header type */
globals_t globals[16] = {0};
for (int len = desc_len; len > 0; len--) {
header_t header = *(header_t *)report++;
uint32_t data = get_descriptor_value(report, header.size);
switch (header.type) {
case RI_TYPE_MAIN:
// Keep count of collections, starts and ends
collection.start += (header.tag == RI_MAIN_COLLECTION);
collection.end += (header.tag == RI_MAIN_COLLECTION_END);
if (header.tag == RI_MAIN_INPUT) {
for (int i = 0; i < globals[RI_GLOBAL_REPORT_COUNT].val; i++) {
/* If we don't have as many usages as elements, the usage for the previous
element applies */
if (i && i >= usage_count ) {
*(p_usage + i) = *(p_usage + usage_count - 1);
void update_usage(parser_state_t *parser, int i) {
/* If we don't have as many usages as elements, the usage for the previous element applies */
if (i && i >= parser->usage_count) {
*(parser->p_usage + i) = *(parser->p_usage + parser->usage_count - 1);
}
}
const usage_map_t *map = usage_map;
void find_and_store_element(parser_state_t *parser, int map_len, int i) {
usage_map_t *map = &parser->map[0];
/* Only focus on the items we care about (buttons, x and y, wheels, etc) */
for (int j=0; j<sizeof(usage_map)/sizeof(usage_map[0]); j++, map++) {
for (int j = 0; j < map_len; j++, map++) {
/* Filter based on usage criteria */
if (map->report_usage == g_usage &&
map->usage_page == globals[RI_GLOBAL_USAGE_PAGE].val &&
map->usage == *(p_usage + i)) {
if (map->report_usage == parser->global_usage
&& map->usage_page == parser->globals[RI_GLOBAL_USAGE_PAGE].val
&& map->usage == *(parser->p_usage + i)) {
/* Buttons are the ones that appear multiple times, will handle them properly
For now, let's just aggregate the length and combine them into one :) */
/* Buttons are the ones that appear multiple times, aggregate for now */
if (map->element->size) {
map->element->size++;
continue;
}
/* Store the found element's attributes */
map->element->offset = offset_in_bits;
map->element->size = globals[RI_GLOBAL_REPORT_SIZE].val;
map->element->min = to_signed(&globals[RI_GLOBAL_LOGICAL_MIN]);
map->element->max = to_signed(&globals[RI_GLOBAL_LOGICAL_MAX]);
map->element->offset = parser->offset_in_bits;
map->element->size = parser->globals[RI_GLOBAL_REPORT_SIZE].val;
map->element->min = to_signed(&parser->globals[RI_GLOBAL_LOGICAL_MIN]);
map->element->max = to_signed(&parser->globals[RI_GLOBAL_LOGICAL_MAX]);
}
};
/* Iterate <count> times and increase offset by <size> amount, moving by <count> x <size> bits */
offset_in_bits += globals[RI_GLOBAL_REPORT_SIZE].val;
}
/* Advance the usage array pointer by global report count and reset the count variable */
p_usage += globals[RI_GLOBAL_REPORT_COUNT].val;
usage_count = 0;
}
break;
}
case RI_TYPE_GLOBAL:
/* There are just 16 possible tags, store any one that comes along to an array instead of doing
switch and 16 cases */
globals[header.tag].val = data;
globals[header.tag].hdr = header;
void handle_global_item(parser_state_t *parser, header_t *header, uint32_t data, mouse_t *mouse) {
/* There are just 16 possible tags, store any one that comes along to an array
instead of doing switch and 16 cases */
parser->globals[header->tag].val = data;
parser->globals[header->tag].hdr = *header;
if (header.tag == RI_GLOBAL_REPORT_ID) {
if (header->tag == RI_GLOBAL_REPORT_ID) {
/* Important to track, if report IDs are used reports are preceded/offset by a 1-byte ID value */
if(g_usage == HID_USAGE_DESKTOP_MOUSE)
if (parser->global_usage == HID_USAGE_DESKTOP_MOUSE)
mouse->report_id = data;
mouse->uses_report_id = true;
}
}
void handle_local_item(parser_state_t *parser, header_t *header, uint32_t data) {
if (header->tag == RI_LOCAL_USAGE) {
/* If we are not within a collection, the usage tag applies to the entire section */
if (parser->collection.start == parser->collection.end) {
parser->global_usage = data;
} else {
*(parser->p_usage + parser->usage_count++) = data;
}
}
}
void handle_main_item(parser_state_t *parser, header_t *header, int map_len) {
/* Update Collection */
parser->collection.start += (header->tag == RI_MAIN_COLLECTION);
parser->collection.end += (header->tag == RI_MAIN_COLLECTION_END);
if (header->tag == RI_MAIN_INPUT) {
for (int i = 0; i < parser->globals[RI_GLOBAL_REPORT_COUNT].val; i++) {
update_usage(parser, i);
find_and_store_element(parser, map_len, i);
/* Iterate <count> times and increase offset by <size> amount, moving by <count> x <size> bits */
parser->offset_in_bits += parser->globals[RI_GLOBAL_REPORT_SIZE].val;
}
/* Advance the usage array pointer by global report count and reset the count variable */
parser->p_usage += parser->globals[RI_GLOBAL_REPORT_COUNT].val;
parser->usage_count = 0;
}
}
/* This method is sub-optimal and far from a generalized HID descriptor parsing, but should
* hopefully work well enough to find the basic values we care about to move the mouse around.
* Your descriptor for a mouse with 2 wheels and 264 buttons might not parse correctly.
**/
uint8_t parse_report_descriptor(mouse_t *mouse, uint8_t arr_count, uint8_t const *report, uint16_t desc_len) {
usage_map_t usage_map[] = {
{.report_usage = HID_USAGE_DESKTOP_MOUSE,
.usage_page = HID_USAGE_PAGE_BUTTON,
.usage = HID_USAGE_DESKTOP_POINTER,
.element = &mouse->buttons},
{.report_usage = HID_USAGE_DESKTOP_MOUSE,
.usage_page = HID_USAGE_PAGE_DESKTOP,
.usage = HID_USAGE_DESKTOP_X,
.element = &mouse->move_x},
{.report_usage = HID_USAGE_DESKTOP_MOUSE,
.usage_page = HID_USAGE_PAGE_DESKTOP,
.usage = HID_USAGE_DESKTOP_Y,
.element = &mouse->move_y},
{.report_usage = HID_USAGE_DESKTOP_MOUSE,
.usage_page = HID_USAGE_PAGE_DESKTOP,
.usage = HID_USAGE_DESKTOP_WHEEL,
.element = &mouse->wheel},
};
parser_state_t parser = {0};
parser.p_usage = parser.usages;
parser.map = usage_map;
while (desc_len > 0) {
header_t header = *(header_t *)report++;
uint32_t data = get_descriptor_value(report, header.size);
switch (header.type) {
case RI_TYPE_MAIN:
handle_main_item(&parser, &header, ARRAY_SIZE(usage_map));
break;
case RI_TYPE_GLOBAL:
handle_global_item(&parser, &header, data, mouse);
break;
case RI_TYPE_LOCAL:
if (header.tag == RI_LOCAL_USAGE) {
/* If we are not within a collection, the usage tag applies to the entire section */
if (IS_BLOCK_END)
g_usage = data;
else
*(p_usage + usage_count++) = data;
}
handle_local_item(&parser, &header, data);
break;
}
/* If header specified some non-zero length data, move by that much to get to the new byte
we should interpret as a header element */
/* Move to the next position and decrement size by header length + data length */
report += header.size;
len -= header.size;
desc_len -= header.size + 1;
}
return 0;
}

View File

@ -18,6 +18,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "main.h"
#define MAX_REPORTS 32
/* Counts how many collection starts and ends we've seen, when they equalize
@ -61,15 +64,14 @@ typedef struct {
/* Defines information about HID report format for the mouse. */
typedef struct {
report_val_t buttons;
report_val_t move_x;
report_val_t move_y;
report_val_t wheel;
bool uses_report_id;
uint8_t report_id;
uint8_t protocol;
bool uses_report_id;
} mouse_t;
/* For each element type we're interested in there is an entry
@ -79,5 +81,18 @@ typedef struct {
uint8_t report_usage;
uint8_t usage_page;
uint8_t usage;
report_val_t* element;
report_val_t *element;
} usage_map_t;
typedef struct {
uint8_t usage_count;
uint8_t global_usage;
uint32_t offset_in_bits;
uint8_t usages[64];
uint8_t *p_usage;
collection_t collection;
usage_map_t *map;
globals_t globals[16]; /* as tag is 4 bits, there can be 16 different tags in global header type */
} parser_state_t;

View File

@ -18,7 +18,7 @@
#include "main.h"
/* ==================================================== *
* Hotkeys to trigger actions via the keyboard
* Hotkeys to trigger actions via the keyboard.
* ==================================================== */
hotkey_combo_t hotkeys[] = {
@ -26,13 +26,15 @@ hotkey_combo_t hotkeys[] = {
{.modifier = 0,
.keys = {HOTKEY_TOGGLE},
.key_count = 1,
.pass_to_os = false,
.action_handler = &output_toggle_hotkey_handler},
/* Holding down right ALT slows the mouse down */
{.modifier = KEYBOARD_MODIFIER_RIGHTALT,
/* Pressing right ALT + right CTRL toggles the slow mouse mode */
{.modifier = KEYBOARD_MODIFIER_RIGHTALT | KEYBOARD_MODIFIER_RIGHTCTRL,
.keys = {},
.key_count = 0,
.pass_to_os = true,
.acknowledge = true,
.action_handler = &mouse_zoom_hotkey_handler},
/* Switch lock */
@ -49,6 +51,13 @@ hotkey_combo_t hotkeys[] = {
.acknowledge = true,
.action_handler = &wipe_config_hotkey_handler},
/* Toggle screensaver function */
{.modifier = KEYBOARD_MODIFIER_RIGHTSHIFT,
.keys = {HID_KEY_F12, HID_KEY_S},
.key_count = 2,
.acknowledge = true,
.action_handler = &screensaver_hotkey_handler},
/* Record switch y coordinate */
{.modifier = KEYBOARD_MODIFIER_RIGHTSHIFT,
.keys = {HID_KEY_F12, HID_KEY_Y},
@ -70,11 +79,53 @@ hotkey_combo_t hotkeys[] = {
.acknowledge = true,
.action_handler = &fw_upgrade_hotkey_handler_B}};
/* ============================================================ *
* Detect if any hotkeys were pressed
* ============================================================ */
/* Tries to find if the keyboard report contains key, returns true/false */
bool key_in_report(uint8_t key, const hid_keyboard_report_t *report) {
for (int j = 0; j < KEYS_IN_USB_REPORT; j++) {
if (key == report->keycode[j]) {
return true;
}
}
return false;
}
/* Check if the current report matches a specific hotkey passed on */
bool check_specific_hotkey(hotkey_combo_t keypress, const hid_keyboard_report_t *report) {
/* We expect all modifiers specified to be detected in the report */
if (keypress.modifier != (report->modifier & keypress.modifier))
return false;
for (int n = 0; n < keypress.key_count; n++) {
if (!key_in_report(keypress.keys[n], report)) {
return false;
}
}
/* Getting here means all of the keys were found. */
return true;
}
/* Go through the list of hotkeys, check if any of them match. */
hotkey_combo_t *check_all_hotkeys(hid_keyboard_report_t *report, device_t *state) {
for (int n = 0; n < ARRAY_SIZE(hotkeys); n++) {
if (check_specific_hotkey(hotkeys[n], report)) {
return &hotkeys[n];
}
}
return NULL;
}
/* ==================================================== *
* Keyboard Queue Section
* ==================================================== */
void process_kbd_queue_task(device_state_t* state) {
void process_kbd_queue_task(device_t *state) {
hid_keyboard_report_t report;
/* If we're not connected, we have nowhere to send reports to. */
@ -93,7 +144,7 @@ void process_kbd_queue_task(device_state_t* state) {
queue_try_remove(&state->kbd_queue, &report);
}
void queue_kbd_report(hid_keyboard_report_t* report, device_state_t* state) {
void queue_kbd_report(hid_keyboard_report_t *report, device_t *state) {
/* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */
if (!state->tud_connected)
return;
@ -101,18 +152,18 @@ void queue_kbd_report(hid_keyboard_report_t* report, device_state_t* state) {
queue_try_add(&state->kbd_queue, report);
}
void stop_pressing_any_keys(device_state_t* state) {
void release_all_keys(device_t *state) {
static hid_keyboard_report_t no_keys_pressed_report = {0, 0, {0}};
queue_try_add(&state->kbd_queue, &no_keys_pressed_report);
}
/* If keys need to go locally, queue packet to kbd queue, else send them through UART */
void send_key(hid_keyboard_report_t* report, device_state_t* state) {
if (state->active_output == BOARD_ROLE) {
void send_key(hid_keyboard_report_t *report, device_t *state) {
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
queue_kbd_report(report, state);
state->last_activity[BOARD_ROLE] = time_us_64();
} else {
send_packet((uint8_t*)report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH);
send_packet((uint8_t *)report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH);
}
}
@ -120,49 +171,26 @@ void send_key(hid_keyboard_report_t* report, device_state_t* state) {
* Parse and interpret the keys pressed on the keyboard
* ==================================================== */
bool no_keys_are_pressed(const hid_keyboard_report_t* report) {
if (report->modifier != 0)
return false;
for (int n = 0; n < KEYS_IN_USB_REPORT; n++) {
if (report->keycode[n] != 0)
return false;
}
return true;
}
hotkey_combo_t* check_hotkeys(hid_keyboard_report_t* report, int length, device_state_t* state) {
/* Go through the list of hotkeys, check if any are pressed, then execute their handler */
for (int n = 0; n < sizeof(hotkeys) / sizeof(hotkeys[0]); n++) {
if (is_key_pressed(hotkeys[n], report)) {
return &hotkeys[n];
}
}
return NULL;
}
void process_keyboard_report(uint8_t* raw_report, int length, device_state_t* state) {
hid_keyboard_report_t* keyboard_report = (hid_keyboard_report_t*)raw_report;
hotkey_combo_t* hotkey = NULL;
void process_keyboard_report(uint8_t *raw_report, int length, device_t *state) {
hid_keyboard_report_t *keyboard_report = (hid_keyboard_report_t *)raw_report;
hotkey_combo_t *hotkey = NULL;
if (length < KBD_REPORT_LENGTH)
return;
/* If no keys are pressed anymore, take care of checking and deactivating stuff */
if(no_keys_are_pressed(keyboard_report))
all_keys_released_handler(state);
else
/* Check if it was a hotkey, makes sense only if a key is pressed */
hotkey = check_hotkeys(keyboard_report, length, state);
/* Check if any hotkey was pressed */
hotkey = check_all_hotkeys(keyboard_report, state);
/* ... and take appropriate action */
if(hotkey != NULL) {
if (hotkey != NULL) {
/* Provide visual feedback we received the action */
if (hotkey->acknowledge)
blink_led(state);
/* Execute the corresponding handler */
hotkey->action_handler(state);
/* And pass the key to the output PC if configured to do so. */
if (!hotkey->pass_to_os)
return;
}
@ -170,31 +198,3 @@ void process_keyboard_report(uint8_t* raw_report, int length, device_state_t* st
/* This method will decide if the key gets queued locally or sent through UART */
send_key(keyboard_report, state);
}
/* ============================================================ *
* Check if a specific key combination is present in the report
* ============================================================ */
bool is_key_pressed(hotkey_combo_t keypress, const hid_keyboard_report_t* report) {
int matches = 0;
/* We expect all modifiers specified to be detected in the report */
if (keypress.modifier != (report->modifier & keypress.modifier))
return false;
for (int n = 0; n < keypress.key_count; n++) {
for (int j = 0; j < KEYS_IN_USB_REPORT; j++) {
if (keypress.keys[n] == report->keycode[j]) {
matches++;
break;
}
}
/* If any of the keys are not found, we can bail out early. */
if (matches < n + 1) {
return false;
}
}
/* Getting here means all of the keys were found. */
return true;
}

View File

@ -21,35 +21,40 @@
* ========== Update pico and keyboard LEDs ========== *
* ==================================================== */
void set_keyboard_leds(uint8_t requested_led_state, device_state_t* state) {
void set_keyboard_leds(uint8_t requested_led_state, device_t *state) {
static uint8_t new_led_value;
new_led_value = requested_led_state;
if (state->keyboard_connected) {
tuh_hid_set_report(state->kbd_dev_addr, state->kbd_instance, 0, HID_REPORT_TYPE_OUTPUT,
&new_led_value, sizeof(uint8_t));
tuh_hid_set_report(state->kbd_dev_addr,
state->kbd_instance,
0,
HID_REPORT_TYPE_OUTPUT,
&new_led_value,
sizeof(uint8_t));
}
}
void restore_leds(device_state_t* state) {
void restore_leds(device_t *state) {
/* Light up on-board LED if current board is active output */
state->onboard_led_state = (state->active_output == BOARD_ROLE);
gpio_put(GPIO_LED_PIN, state->onboard_led_state);
/* Light up appropriate keyboard leds (if it's connected locally) */
if (state->keyboard_connected) {
uint8_t leds = state->keyboard_leds[state->active_output];
set_keyboard_leds(leds, state);
}
}
void blink_led(device_state_t* state) {
void blink_led(device_t *state) {
/* Since LEDs might be ON previously, we go OFF, ON, OFF, ON, OFF */
state->blinks_left = 5;
state->last_led_change = time_us_32();
}
void led_blinking_task(device_state_t* state) {
/* 80 ms off, 80 ms on */
const int blink_interval_us = 80000;
void led_blinking_task(device_t *state) {
const int blink_interval_us = 80000; /* 80 ms off, 80 ms on */
static uint8_t leds;
/* If there is no more blinking to be done, exit immediately */

View File

@ -18,7 +18,8 @@
#include "main.h"
/********* Global Variable **********/
device_state_t global_state = {0};
device_t global_state = {0};
device_t *device = &global_state;
/**================================================== *
* ============== Main Program Loops ============== *
@ -29,25 +30,24 @@ void main(void) {
sleep_ms(10);
// Initial board setup
initial_setup();
initial_setup(device);
// Initial state, A is the default output
switch_output(ACTIVE_OUTPUT_A);
switch_output(device, OUTPUT_A);
while (true) {
// USB device task, needs to run as often as possible
tud_task();
// Verify core1 is still running and if so, reset watchdog timer
kick_watchdog();
kick_watchdog(device);
// Check if there were any keypresses and send them
process_kbd_queue_task(&global_state);
process_kbd_queue_task(device);
// Check if there were any mouse movements and send them
process_mouse_queue_task(&global_state);
process_mouse_queue_task(device);
}
}
void core1_main() {
@ -55,17 +55,20 @@ void core1_main() {
while (true) {
// Update the timestamp, so core0 can figure out if we're dead
global_state.core1_last_loop_pass = time_us_64();
device->core1_last_loop_pass = time_us_64();
// USB host task, needs to run as often as possible
if (tuh_inited())
tuh_task();
// Receives data over serial from the other board
receive_char(&in_packet, &global_state);
receive_char(&in_packet, device);
// Check if LED needs blinking
led_blinking_task(&global_state);
led_blinking_task(device);
// Mouse screensaver task
screensaver_task(device);
}
}

View File

@ -17,43 +17,44 @@
#pragma once
#include "pico/stdlib.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "hardware/watchdog.h"
#include "hid_parser.h"
#include "pico/bootrom.h"
#include "pico/multicore.h"
#include "pico/stdlib.h"
#include "pico/util/queue.h"
#include "pio_usb.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "user_config.h"
#include <hardware/flash.h>
#include <hardware/sync.h>
#include <hardware/watchdog.h>
#include <pico/bootrom.h>
#include <pico/multicore.h>
#include <pico/stdlib.h>
#include <pico/util/queue.h>
/********* Misc definitions for better readability **********/
#define PICO_A 0
#define PICO_B 1
#define ACTIVE_OUTPUT_A 0
#define ACTIVE_OUTPUT_B 1
#define OUTPUT_A 0
#define OUTPUT_B 1
#define ENABLE 1
#define DISABLE 0
#define DIRECTION_X 0
#define DIRECTION_Y 1
#define MAX_REPORT_ITEMS 16
#define MOUSE_BOOT_REPORT_LEN 4
#define NUM_SCREENS 2 // Will be more in the future
#define MOUSE_ZOOM_SCALING_FACTOR 2
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define CURRENT_BOARD_IS_ACTIVE_OUTPUT (global_state.active_output == BOARD_ROLE)
/********* Pinout definitions **********/
#define PIO_USB_DP_PIN 14 // D+ is pin 14, D- is pin 15
@ -100,6 +101,8 @@ enum packet_type_e {
SWITCH_LOCK_MSG = 7,
SYNC_BORDERS_MSG = 8,
FLASH_LED_MSG = 9,
SCREENSAVER_MSG = 10,
WIPE_CONFIG_MSG = 11,
};
/*
@ -138,10 +141,13 @@ typedef struct {
#define MOUSE_REPORT_LENGTH 7
/********* Screen **********/
#define MIN_SCREEN_COORD 0
#define MAX_SCREEN_COORD 32767
/********* Configuration storage definitions **********/
#define CURRENT_CONFIG_VERSION 2
typedef struct {
int top; // When jumping from a smaller to a bigger screen, go to THIS top height
int bottom; // When jumping from a smaller to a bigger screen, go to THIS bottom
@ -150,7 +156,7 @@ typedef struct {
/* Define output parameters */
typedef struct {
int number; // Number of this output (e.g. ACTIVE_OUTPUT_A = 0 etc)
int number; // Number of this output (e.g. OUTPUT_A = 0 etc)
int screen_count; // How many monitors per output (e.g. Output A is Windows with 3 monitors)
int screen_index; // Current active screen
int speed_x; // Mouse speed per output, in direction X
@ -161,8 +167,11 @@ typedef struct {
/* Data structure defining how configuration is stored */
typedef struct {
uint32_t magic_header;
uint32_t version;
uint8_t force_mouse_boot_mode;
output_t output[NUM_SCREENS];
uint8_t screensaver_enabled;
// Keep checksum at the end of the struct
uint32_t checksum;
} config_t;
@ -175,6 +184,11 @@ extern config_t ADDR_CONFIG[];
typedef void (*action_handler_t)();
typedef struct { // Maps message type -> message handler function
enum packet_type_e type;
action_handler_t handler;
} uart_handler_t;
typedef struct {
uint8_t modifier; // Which modifier is pressed
uint8_t keys[6]; // Which keys need to be pressed
@ -190,7 +204,7 @@ typedef struct TU_ATTR_PACKED {
int16_t y;
int8_t wheel;
int8_t pan;
} hid_abs_mouse_report_t;
} mouse_abs_report_t;
typedef enum { IDLE, READING_PACKET, PROCESSING_PACKET } receiver_state_t;
@ -201,7 +215,6 @@ typedef struct {
uint8_t keyboard_leds[NUM_SCREENS]; // State of keyboard LEDs (index 0 = A, index 1 = B)
uint64_t last_activity[NUM_SCREENS]; // Timestamp of the last input activity (-||-)
receiver_state_t receiver_state; // Storing the state for the simple receiver state machine
uint64_t core1_last_loop_pass; // Timestamp of last core1 loop execution
uint8_t active_output; // Currently selected output (0 = A, 1 = B)
@ -209,7 +222,6 @@ typedef struct {
int16_t mouse_y;
config_t config; // Device configuration, loaded from flash or defaults used
mouse_t mouse_dev; // Mouse device specifics, e.g. stores locations for keys in report
queue_t kbd_queue; // Queue that stores keyboard reports
queue_t mouse_queue; // Queue that stores mouse reports
@ -228,82 +240,82 @@ typedef struct {
int32_t blinks_left; // How many blink transitions are left
int32_t last_led_change; // Timestamp of the last time led state transitioned
} device_state_t;
} device_t;
/********* Setup **********/
void initial_setup(void);
void initial_setup(device_t *);
void serial_init(void);
void core1_main(void);
/********* Keyboard **********/
bool is_key_pressed(hotkey_combo_t, const hid_keyboard_report_t*);
void process_keyboard_report(uint8_t*, int, device_state_t*);
void stop_pressing_any_keys(device_state_t*);
void queue_kbd_report(hid_keyboard_report_t*, device_state_t*);
void process_kbd_queue_task(device_state_t*);
void send_key(hid_keyboard_report_t*, device_state_t*);
bool check_specific_hotkey(hotkey_combo_t, const hid_keyboard_report_t *);
void process_keyboard_report(uint8_t *, int, device_t *);
void release_all_keys(device_t *);
void queue_kbd_report(hid_keyboard_report_t *, device_t *);
void process_kbd_queue_task(device_t *);
void send_key(hid_keyboard_report_t *, device_t *);
/********* Mouse **********/
bool tud_hid_abs_mouse_report(uint8_t report_id,
uint8_t buttons,
int16_t x,
int16_t y,
int8_t vertical,
int8_t horizontal);
bool tud_hid_abs_mouse_report(
uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal);
void process_mouse_report(uint8_t*, int, device_state_t*);
uint8_t parse_report_descriptor(mouse_t* mouse,
uint8_t arr_count,
uint8_t const* desc_report,
uint16_t desc_len);
int32_t get_report_value(uint8_t* report, report_val_t* val);
void process_mouse_queue_task(device_state_t*);
void queue_mouse_report(hid_abs_mouse_report_t*, device_state_t*);
void send_mouse(hid_abs_mouse_report_t*, device_state_t*);
void process_mouse_report(uint8_t *, int, device_t *);
uint8_t
parse_report_descriptor(mouse_t *mouse, uint8_t arr_count, uint8_t const *desc_report, uint16_t desc_len);
int32_t get_report_value(uint8_t *report, report_val_t *val);
void process_mouse_queue_task(device_t *);
void queue_mouse_report(mouse_abs_report_t *, device_t *);
void output_mouse_report(mouse_abs_report_t *, device_t *);
/********* UART **********/
void receive_char(uart_packet_t*, device_state_t*);
void send_packet(const uint8_t*, enum packet_type_e, int);
void receive_char(uart_packet_t *, device_t *);
void send_packet(const uint8_t *, enum packet_type_e, int);
void send_value(const uint8_t, enum packet_type_e);
/********* LEDs **********/
void restore_leds(device_state_t*);
void blink_led(device_state_t*);
void led_blinking_task(device_state_t*);
void restore_leds(device_t *);
void blink_led(device_t *);
void led_blinking_task(device_t *);
/********* Checksum **********/
uint8_t calc_checksum(const uint8_t*, int);
bool verify_checksum(const uart_packet_t*);
uint8_t calc_checksum(const uint8_t *, int);
bool verify_checksum(const uart_packet_t *);
/********* Watchdog **********/
void kick_watchdog(void);
void kick_watchdog(device_t *);
/********* Configuration **********/
void load_config(void);
void save_config(void);
void load_config(device_t *);
void save_config(device_t *);
void wipe_config(void);
/********* Misc **********/
void screensaver_task(device_t *);
/********* Handlers **********/
void output_toggle_hotkey_handler(device_state_t*);
void screen_border_hotkey_handler(device_state_t*);
void fw_upgrade_hotkey_handler_A(device_state_t*);
void fw_upgrade_hotkey_handler_B(device_state_t*);
void mouse_zoom_hotkey_handler(device_state_t*);
void all_keys_released_handler(device_state_t*);
void switchlock_hotkey_handler(device_state_t*);
void wipe_config_hotkey_handler(device_state_t*);
void output_toggle_hotkey_handler(device_t *);
void screen_border_hotkey_handler(device_t *);
void fw_upgrade_hotkey_handler_A(device_t *);
void fw_upgrade_hotkey_handler_B(device_t *);
void mouse_zoom_hotkey_handler(device_t *);
void all_keys_released_handler(device_t *);
void switchlock_hotkey_handler(device_t *);
void wipe_config_hotkey_handler(device_t *);
void screensaver_hotkey_handler(device_t *);
void handle_keyboard_uart_msg(uart_packet_t*, device_state_t*);
void handle_mouse_abs_uart_msg(uart_packet_t*, device_state_t*);
void handle_output_select_msg(uart_packet_t*, device_state_t*);
void handle_mouse_zoom_msg(uart_packet_t*, device_state_t*);
void handle_set_report_msg(uart_packet_t*, device_state_t*);
void handle_switch_lock_msg(uart_packet_t*, device_state_t*);
void handle_sync_borders_msg(uart_packet_t*, device_state_t*);
void handle_flash_led_msg(uart_packet_t*, device_state_t*);
void handle_fw_upgrade_msg(void);
void handle_keyboard_uart_msg(uart_packet_t *, device_t *);
void handle_mouse_abs_uart_msg(uart_packet_t *, device_t *);
void handle_output_select_msg(uart_packet_t *, device_t *);
void handle_mouse_zoom_msg(uart_packet_t *, device_t *);
void handle_set_report_msg(uart_packet_t *, device_t *);
void handle_switch_lock_msg(uart_packet_t *, device_t *);
void handle_sync_borders_msg(uart_packet_t *, device_t *);
void handle_flash_led_msg(uart_packet_t *, device_t *);
void handle_fw_upgrade_msg(uart_packet_t *, device_t *);
void handle_wipe_config_msg(uart_packet_t *, device_t *);
void handle_screensaver_msg(uart_packet_t *, device_t *);
void switch_output(uint8_t);
void switch_output(device_t *, uint8_t);
/********* Global variables (don't judge) **********/
extern device_state_t global_state;
extern device_t global_state;

View File

@ -17,45 +17,40 @@
#include "main.h"
int get_mouse_offset(int32_t movement, const int direction) {
int offset = 0;
output_t *active_output =
&global_state.config.output[global_state.active_output];
if (direction == DIRECTION_X)
offset = movement * active_output->speed_x;
else
offset = movement * active_output->speed_y;
/* Holding a special hotkey enables mouse to slow down as much as possible
when you need that extra precision */
if (global_state.mouse_zoom)
offset = offset >> 2;
return offset;
}
void keep_cursor_on_screen(int16_t *position, const int32_t *movement,
const int direction) {
int16_t offset = get_mouse_offset(*movement, direction);
/* Move mouse coordinate 'position' by 'offset', but don't fall off the screen */
int32_t move_and_keep_on_screen(int position, int offset) {
/* Lowest we can go is 0 */
if (*position + offset < 0)
*position = 0;
if (position + offset < MIN_SCREEN_COORD)
return MIN_SCREEN_COORD;
/* Highest we can go is MAX_SCREEN_COORD */
else if (*position + offset > MAX_SCREEN_COORD)
*position = MAX_SCREEN_COORD;
else if (position + offset > MAX_SCREEN_COORD)
return MAX_SCREEN_COORD;
/* We're still on screen, all good */
else
*position += offset;
return position + offset;
}
/* If mouse needs to go locally, queue packet to mouse queue, else send them
* through UART */
void send_mouse(hid_abs_mouse_report_t *report, device_state_t *state) {
if (state->active_output == BOARD_ROLE) {
void update_mouse_position(device_t *state, mouse_values_t *values) {
output_t *current = &state->config.output[state->active_output];
uint8_t reduce_speed = 0;
/* Check if we are configured to move slowly */
if (state->mouse_zoom)
reduce_speed = MOUSE_ZOOM_SCALING_FACTOR;
/* Calculate movement */
int offset_x = values->move_x * (current->speed_x >> reduce_speed);
int offset_y = values->move_y * (current->speed_y >> reduce_speed);
/* Update movement */
state->mouse_x = move_and_keep_on_screen(state->mouse_x, offset_x);
state->mouse_y = move_and_keep_on_screen(state->mouse_y, offset_y);
}
/* If we are active output, queue packet to mouse queue, else send them through UART */
void output_mouse_report(mouse_abs_report_t *report, device_t *state) {
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
queue_mouse_report(report, state);
state->last_activity[BOARD_ROLE] = time_us_64();
} else {
@ -63,7 +58,11 @@ void send_mouse(hid_abs_mouse_report_t *report, device_state_t *state) {
}
}
int16_t scale_y_coord(output_t *from, output_t *to, device_state_t *state) {
/* Calculate and return Y coordinate when moving from screen out_from to screen out_to */
int16_t scale_y_coordinate(int screen_from, int screen_to, device_t *state) {
output_t *from = &state->config.output[screen_from];
output_t *to = &state->config.output[screen_to];
int size_to = to->border.bottom - to->border.top;
int size_from = from->border.bottom - from->border.top;
@ -71,123 +70,108 @@ int16_t scale_y_coord(output_t *from, output_t *to, device_state_t *state) {
if (size_from == size_to)
return state->mouse_y;
/* Moving from bigger ==> smaller screen */
if (size_from < size_to) {
/* Moving from smaller ==> bigger screen
y_a = top + (((bottom - top) * y_b) / HEIGHT) */
if (size_from > size_to) {
return to->border.top + ((size_to * state->mouse_y) / MAX_SCREEN_COORD);
}
/* Moving from bigger ==> smaller screen
y_b = ((y_a - top) * HEIGHT) / (bottom - top) */
if (state->mouse_y < from->border.top)
return 0;
return MIN_SCREEN_COORD;
if (state->mouse_y > from->border.bottom)
return MAX_SCREEN_COORD;
/* y_b = ((y_a - top) * HEIGHT) / (bottom - top) */
return ((state->mouse_y - from->border.top) * MAX_SCREEN_COORD) /
size_from;
}
/* Moving from smaller ==> bigger screen,
y_a = top + (((bottom - top) * y_b) / HEIGHT) */
return to->border.top + ((size_to * state->mouse_y) / MAX_SCREEN_COORD);
return ((state->mouse_y - from->border.top) * MAX_SCREEN_COORD) / size_from;
}
void check_mouse_switch(const mouse_values_t *values, device_state_t *state) {
hid_abs_mouse_report_t report = {.y = 0, .x = MAX_SCREEN_COORD};
void switch_screen(device_t *state, int new_x, int output_from, int output_to) {
mouse_abs_report_t hidden_pointer = {.y = MIN_SCREEN_COORD, .x = MAX_SCREEN_COORD};
output_mouse_report(&hidden_pointer, state);
switch_output(state, output_to);
state->mouse_x = (output_to == OUTPUT_A) ? MIN_SCREEN_COORD : MAX_SCREEN_COORD;
state->mouse_y = scale_y_coordinate(output_from, output_to, state);
}
void check_screen_switch(const mouse_values_t *values, device_t *state) {
int new_x = state->mouse_x + values->move_x;
/* No switching allowed if explicitly disabled */
if (state->switch_lock)
return;
/* End of screen left switches screen A->B */
bool jump_from_A_to_B =
(state->mouse_x + values->move_x < -MOUSE_JUMP_THRESHOLD &&
state->active_output == ACTIVE_OUTPUT_A);
/* End of screen right switches screen B->A */
bool jump_from_B_to_A = (state->mouse_x + values->move_x >
MAX_SCREEN_COORD + MOUSE_JUMP_THRESHOLD &&
state->active_output == ACTIVE_OUTPUT_B);
if (jump_from_A_to_B || jump_from_B_to_A) {
/* Hide mouse pointer in the upper right corner on the system we are
switching FROM If the mouse is locally attached to the current board or
notify other board if not */
send_mouse(&report, state);
if (jump_from_A_to_B) {
switch_output(ACTIVE_OUTPUT_B);
state->mouse_x = MAX_SCREEN_COORD;
state->mouse_y = scale_y_coord(&state->config.output[ACTIVE_OUTPUT_A],
&state->config.output[ACTIVE_OUTPUT_B],
state);
} else {
switch_output(ACTIVE_OUTPUT_A);
state->mouse_x = 0;
state->mouse_y = scale_y_coord(&state->config.output[ACTIVE_OUTPUT_B],
&state->config.output[ACTIVE_OUTPUT_A],
state);
/* End of screen left switches screen A->B TODO: make configurable */
if (new_x < MIN_SCREEN_COORD - JUMP_THRESHOLD && state->active_output == OUTPUT_A) {
switch_screen(state, new_x, OUTPUT_A, OUTPUT_B);
}
/* End of screen right switches screen B->A TODO: make configurable */
else if (new_x > MAX_SCREEN_COORD + JUMP_THRESHOLD && state->active_output == OUTPUT_B) {
switch_screen(state, new_x, OUTPUT_B, OUTPUT_A);
}
}
void extract_values_report_protocol(uint8_t *report, device_state_t *state,
mouse_values_t *values) {
/* If Report ID is used, the report is prefixed by the report ID so we have to
* move by 1 byte */
if (state->mouse_dev.uses_report_id) {
/* Move past the ID to parse the report */
report++;
}
values->move_x = get_report_value(report, &state->mouse_dev.move_x);
values->move_y = get_report_value(report, &state->mouse_dev.move_y);
values->wheel = get_report_value(report, &state->mouse_dev.wheel);
values->buttons = get_report_value(report, &state->mouse_dev.buttons);
}
void extract_values_boot_protocol(uint8_t *report, device_state_t *state,
mouse_values_t *values) {
hid_mouse_report_t *mouse_report = (hid_mouse_report_t *)report;
void extract_report_values(uint8_t *raw_report, device_t *state, mouse_values_t *values) {
/* Interpret values depending on the current protocol used. */
if (state->mouse_dev.protocol == HID_PROTOCOL_BOOT) {
hid_mouse_report_t *mouse_report = (hid_mouse_report_t *)raw_report;
values->move_x = mouse_report->x;
values->move_y = mouse_report->y;
values->wheel = mouse_report->wheel;
values->buttons = mouse_report->buttons;
return;
}
/* If HID Report ID is used, the report is prefixed by the report ID so we have to move by 1 byte */
if (state->mouse_dev.uses_report_id)
raw_report++;
values->move_x = get_report_value(raw_report, &state->mouse_dev.move_x);
values->move_y = get_report_value(raw_report, &state->mouse_dev.move_y);
values->wheel = get_report_value(raw_report, &state->mouse_dev.wheel);
values->buttons = get_report_value(raw_report, &state->mouse_dev.buttons);
}
void process_mouse_report(uint8_t *raw_report, int len, device_state_t *state) {
mouse_values_t values = {0};
/* Interpret values depending on the current protocol used */
if (state->mouse_dev.protocol == HID_PROTOCOL_BOOT)
extract_values_boot_protocol(raw_report, state, &values);
else
extract_values_report_protocol(raw_report, state, &values);
/* We need to enforce the cursor doesn't go off-screen, that would be bad. */
keep_cursor_on_screen(&state->mouse_x, &values.move_x, DIRECTION_X);
keep_cursor_on_screen(&state->mouse_y, &values.move_y, DIRECTION_Y);
hid_abs_mouse_report_t abs_mouse_report = {.buttons = values.buttons,
mouse_abs_report_t create_mouse_report(device_t *state, mouse_values_t *values) {
mouse_abs_report_t abs_mouse_report = {.buttons = values->buttons,
.x = state->mouse_x,
.y = state->mouse_y,
.wheel = values.wheel,
.wheel = values->wheel,
.pan = 0};
return abs_mouse_report;
}
void process_mouse_report(uint8_t *raw_report, int len, device_t *state) {
mouse_values_t values = {0};
/* Interpret the mouse HID report, extract and save values we need. */
extract_report_values(raw_report, state, &values);
/* Calculate and update mouse pointer movement. */
update_mouse_position(state, &values);
/* Create the report for the output PC based on the updated values */
mouse_abs_report_t report = create_mouse_report(state, &values);
/* Move the mouse, depending where the output is supposed to go */
send_mouse(&abs_mouse_report, state);
output_mouse_report(&report, state);
/* We use the mouse to switch outputs, the logic is in check_mouse_switch() */
check_mouse_switch(&values, state);
/* We use the mouse to switch outputs, the logic is in check_screen_switch() */
check_screen_switch(&values, state);
}
/* ==================================================== *
* Mouse Queue Section
* ==================================================== */
void process_mouse_queue_task(device_state_t *state) {
hid_abs_mouse_report_t report = {0};
void process_mouse_queue_task(device_t *state) {
mouse_abs_report_t report = {0};
/* We need to be connected to the host to send messages */
if (!state->tud_connected)
@ -202,18 +186,16 @@ void process_mouse_queue_task(device_state_t *state) {
tud_remote_wakeup();
/* ... try sending it to the host, if it's successful */
bool succeeded =
tud_hid_abs_mouse_report(REPORT_ID_MOUSE, report.buttons, report.x,
report.y, report.wheel, report.pan);
bool succeeded = tud_hid_abs_mouse_report(
REPORT_ID_MOUSE, report.buttons, report.x, report.y, report.wheel, report.pan);
/* ... then we can remove it from the queue */
if (succeeded)
queue_try_remove(&state->mouse_queue, &report);
}
void queue_mouse_report(hid_abs_mouse_report_t *report, device_state_t *state) {
/* It wouldn't be fun to queue up a bunch of messages and then dump them all
* on host */
void queue_mouse_report(mouse_abs_report_t *report, device_t *state) {
/* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */
if (!state->tud_connected)
return;

View File

@ -65,12 +65,12 @@ void pio_usb_host_config(void) {
* Perform initial board/usb setup
* ================================================== */
void initial_setup(void) {
void initial_setup(device_t *state) {
/* PIO USB requires a clock multiple of 12 MHz, setting to 120 MHz */
set_sys_clock_khz(120000, true);
/* Search the persistent storage sector in flash for valid config or use defaults */
load_config();
load_config(state);
/* Init and enable the on-board LED GPIO as output */
gpio_init(GPIO_LED_PIN);
@ -80,8 +80,8 @@ void initial_setup(void) {
serial_init();
/* Initialize keyboard and mouse queues */
queue_init(&global_state.kbd_queue, sizeof(hid_keyboard_report_t), KBD_QUEUE_LENGTH);
queue_init(&global_state.mouse_queue, sizeof(hid_abs_mouse_report_t), MOUSE_QUEUE_LENGTH);
queue_init(&state->kbd_queue, sizeof(hid_keyboard_report_t), KBD_QUEUE_LENGTH);
queue_init(&state->mouse_queue, sizeof(mouse_abs_report_t), MOUSE_QUEUE_LENGTH);
/* Setup RP2040 Core 1 */
multicore_reset_core1();
@ -94,7 +94,7 @@ void initial_setup(void) {
pio_usb_host_config();
/* Update the core1 initial pass timestamp before enabling the watchdog */
global_state.core1_last_loop_pass = time_us_64();
state->core1_last_loop_pass = time_us_64();
/* Setup the watchdog so we reboot and recover from a crash */
watchdog_enable(WATCHDOG_TIMEOUT, WATCHDOG_PAUSE_ON_DEBUG);

View File

@ -27,7 +27,7 @@
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
//--------------------------------------------------------------------
@ -51,23 +51,24 @@
// defined by board.mk
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#error CFG_TUSB_MCU must be defined
#endif
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_DEVICE_RHPORT_NUM
#define BOARD_DEVICE_RHPORT_NUM 0
#define BOARD_DEVICE_RHPORT_NUM 0
#endif
// RHPort max operational speed can defined by board.mk
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
#ifndef BOARD_DEVICE_RHPORT_SPEED
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
#else
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
#endif
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX \
|| CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 \
|| CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
#else
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
#endif
#endif
// Device mode with rhport and speed defined by board.mk
@ -89,7 +90,7 @@
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
#endif
//--------------------------------------------------------------------
@ -121,12 +122,12 @@
// max device support (excluding hub device)
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
#define CFG_TUH_HID 4
#define CFG_TUH_HID 3 * CFG_TUH_DEVICE_MAX
#define CFG_TUH_HID_EPIN_BUFSIZE 64
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
#ifdef __cplusplus
}
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@ -21,7 +21,7 @@
* =============== Sending Packets ================ *
* ================================================== */
void send_packet(const uint8_t* data, enum packet_type_e packet_type, int length) {
void send_packet(const uint8_t *data, enum packet_type_e packet_type, int length) {
uint8_t raw_packet[RAW_PACKET_LENGTH] = {[0] = START1,
[1] = START2,
[2] = packet_type,
@ -31,60 +31,42 @@ void send_packet(const uint8_t* data, enum packet_type_e packet_type, int length
if (length > 0)
memcpy(&raw_packet[START_LENGTH + TYPE_LENGTH], data, length);
/* Packets are short, fixed length, high speed and there is no flow control to block this */
uart_write_blocking(SERIAL_UART, raw_packet, RAW_PACKET_LENGTH);
}
void send_value(const uint8_t value, enum packet_type_e packet_type) {
const uint8_t data[8] = {[0] = value};
send_packet((uint8_t*)&data, packet_type, 1);
const uint8_t data = value;
send_packet(&data, packet_type, sizeof(uint8_t));
}
/**================================================== *
* =============== Parsing Packets ================ *
* ================================================== */
void process_packet(uart_packet_t* packet, device_state_t* state) {
if (!verify_checksum(packet)) {
const uart_handler_t uart_handler[] = {
{.type = KEYBOARD_REPORT_MSG, .handler = handle_keyboard_uart_msg},
{.type = MOUSE_REPORT_MSG, .handler = handle_mouse_abs_uart_msg},
{.type = OUTPUT_SELECT_MSG, .handler = handle_output_select_msg},
{.type = FIRMWARE_UPGRADE_MSG, .handler = handle_fw_upgrade_msg},
{.type = MOUSE_ZOOM_MSG, .handler = handle_mouse_zoom_msg},
{.type = KBD_SET_REPORT_MSG, .handler = handle_set_report_msg},
{.type = SWITCH_LOCK_MSG, .handler = handle_switch_lock_msg},
{.type = SYNC_BORDERS_MSG, .handler = handle_sync_borders_msg},
{.type = FLASH_LED_MSG, .handler = handle_flash_led_msg},
{.type = SCREENSAVER_MSG, .handler = handle_screensaver_msg},
{.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg},
};
void process_packet(uart_packet_t *packet, device_t *state) {
if (!verify_checksum(packet))
return;
for (int i = 0; i < ARRAY_SIZE(uart_handler); i++) {
if (uart_handler[i].type == packet->type) {
uart_handler[i].handler(packet, state);
return;
}
switch (packet->type) {
case KEYBOARD_REPORT_MSG:
handle_keyboard_uart_msg(packet, state);
break;
case MOUSE_REPORT_MSG:
handle_mouse_abs_uart_msg(packet, state);
break;
case OUTPUT_SELECT_MSG:
handle_output_select_msg(packet, state);
break;
case FIRMWARE_UPGRADE_MSG:
handle_fw_upgrade_msg();
break;
case MOUSE_ZOOM_MSG:
handle_mouse_zoom_msg(packet, state);
break;
case KBD_SET_REPORT_MSG:
handle_set_report_msg(packet, state);
break;
case SWITCH_LOCK_MSG:
handle_switch_lock_msg(packet, state);
break;
case SYNC_BORDERS_MSG:
handle_sync_borders_msg(packet, state);
break;
case FLASH_LED_MSG:
handle_flash_led_msg(packet, state);
break;
}
}
@ -92,41 +74,59 @@ void process_packet(uart_packet_t* packet, device_state_t* state) {
* ============== Receiving Packets =============== *
* ================================================== */
void receive_char(uart_packet_t* packet, device_state_t* state) {
uint8_t* raw_packet = (uint8_t*)packet;
/* We are in IDLE state until we detect the packet start (0xAA 0x55) */
void handle_idle_state(uint8_t *raw_packet, device_t *state) {
if (!uart_is_readable(SERIAL_UART)) {
return;
}
raw_packet[0] = raw_packet[1]; /* Remember the previous byte received */
raw_packet[1] = uart_getc(SERIAL_UART); /* Try to match packet start */
/* If we found 0xAA 0x55, we're in sync and can move on to read/process the packet */
if (raw_packet[0] == START1 && raw_packet[1] == START2) {
state->receiver_state = READING_PACKET;
}
}
/* Read a character off the line until we reach fixed packet length */
void handle_reading_state(uint8_t *raw_packet, device_t *state, int *count) {
if (!uart_is_readable(SERIAL_UART)) {
return;
}
/* Read and store the incoming byte */
raw_packet[(*count)++] = uart_getc(SERIAL_UART);
/* Check if a complete packet is received */
if (*count >= PACKET_LENGTH) {
state->receiver_state = PROCESSING_PACKET;
}
}
/* Process that packet, restart counters and state machine to have it back to IDLE */
void handle_processing_state(uart_packet_t *packet, device_t *state, int *count) {
process_packet(packet, state);
state->receiver_state = IDLE;
*count = 0;
}
/* Very simple state machine to receive and process packets over serial */
void receive_char(uart_packet_t *packet, device_t *state) {
uint8_t *raw_packet = (uint8_t *)packet;
static int count = 0;
switch (state->receiver_state) {
case IDLE:
if (uart_is_readable(SERIAL_UART)) {
raw_packet[0] = raw_packet[1]; /* Remember the previous byte received */
raw_packet[1] = uart_getc(SERIAL_UART); /* ... and try to match packet start */
/* If we found 0xAA 0x55, we're in sync and can move on to read/process the packet
*/
if (raw_packet[0] == START1 && raw_packet[1] == START2) {
state->receiver_state = READING_PACKET;
}
}
handle_idle_state(raw_packet, state);
break;
case READING_PACKET:
if (uart_is_readable(SERIAL_UART)) {
raw_packet[count++] = uart_getc(SERIAL_UART);
/* Check if a complete packet is received */
if (count >= PACKET_LENGTH) {
state->receiver_state = PROCESSING_PACKET;
}
}
handle_reading_state(raw_packet, state, &count);
break;
case PROCESSING_PACKET:
process_packet(packet, state);
/* Cleanup and return to IDLE when done */
count = 0;
state->receiver_state = IDLE;
handle_processing_state(packet, state, &count);
break;
}
}

View File

@ -27,8 +27,8 @@
uint16_t tud_hid_get_report_cb(uint8_t instance,
uint8_t report_id,
hid_report_type_t report_type,
uint8_t* buffer,
uint16_t reqlen) {
uint8_t *buffer,
uint16_t request_len) {
return 0;
}
@ -42,15 +42,14 @@ uint16_t tud_hid_get_report_cb(uint8_t instance,
void tud_hid_set_report_cb(uint8_t instance,
uint8_t report_id,
hid_report_type_t report_type,
uint8_t const* buffer,
uint8_t const *buffer,
uint16_t bufsize) {
if (report_id == REPORT_ID_KEYBOARD && bufsize == 1 && report_type == HID_REPORT_TYPE_OUTPUT) {
/**
* If we are using caps lock LED to indicate the chosen output, we will
* override whatever is sent through the SetReport message.
*/
if (report_id != REPORT_ID_KEYBOARD || bufsize != 1 || report_type != HID_REPORT_TYPE_OUTPUT)
return;
uint8_t leds = buffer[0];
/* If we are using caps lock LED to indicate the chosen output, that has priority */
if (KBD_LED_AS_INDICATOR) {
leds = leds & 0xFD; /* 1111 1101 (Clear Caps Lock bit) */
@ -60,13 +59,12 @@ void tud_hid_set_report_cb(uint8_t instance,
global_state.keyboard_leds[global_state.active_output] = leds;
/* If we are board without the keyboard hooked up directly, we need to send this information
to the other one since that one has the keyboard connected to it (and LEDs you can turn on :)) */
/* If the board doesn't have the keyboard hooked up directly, we need to relay this information
to the other one that has (and LEDs you can turn on). */
if (global_state.keyboard_connected)
restore_leds(&global_state);
else
send_value(leds, KBD_SET_REPORT_MSG);
}
}
/* Invoked when device is mounted */
@ -85,6 +83,7 @@ void tud_umount_cb(void) {
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
switch (itf_protocol) {
case HID_ITF_PROTOCOL_KEYBOARD:
global_state.keyboard_connected = false;
@ -99,15 +98,12 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
}
}
void tuh_hid_mount_cb(uint8_t dev_addr,
uint8_t instance,
uint8_t const* desc_report,
uint16_t desc_len) {
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
switch (itf_protocol) {
case HID_ITF_PROTOCOL_KEYBOARD:
/* Keeping this is needed for setting leds from device set_report callback */
/* Keeping this is required for setting leds from device set_report callback */
global_state.kbd_dev_addr = dev_addr;
global_state.kbd_instance = instance;
global_state.keyboard_connected = true;
@ -130,26 +126,21 @@ void tuh_hid_mount_cb(uint8_t dev_addr,
/* Also signal the other board to flash LED, to enable easy verification if serial works */
send_value(ENABLE, FLASH_LED_MSG);
/* Kick off the report querying */
tuh_hid_receive_report(dev_addr, instance);
}
/* Invoked when received report from device via interrupt endpoint */
void tuh_hid_report_received_cb(uint8_t dev_addr,
uint8_t instance,
uint8_t const* report,
uint16_t len) {
(void)len;
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
switch (itf_protocol) {
case HID_ITF_PROTOCOL_KEYBOARD:
process_keyboard_report((uint8_t*)report, len, &global_state);
process_keyboard_report((uint8_t *)report, len, &global_state);
break;
case HID_ITF_PROTOCOL_MOUSE:
process_mouse_report((uint8_t*)report, len, &global_state);
process_mouse_report((uint8_t *)report, len, &global_state);
break;
}
@ -158,9 +149,7 @@ void tuh_hid_report_received_cb(uint8_t dev_addr,
}
/* Set protocol in a callback. If we were called, command succeeded. We're only
doing this for the mouse anyway, so we can only be called about the mouse */
doing this for the mouse for now, so we can only be called about the mouse */
void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {
(void) dev_addr;
(void) idx;
global_state.mouse_dev.protocol = protocol;
}

View File

@ -23,8 +23,8 @@
*
*/
#include "main.h"
#include "usb_descriptors.h"
#include "main.h"
#include "tusb.h"
//--------------------------------------------------------------------+
@ -51,8 +51,8 @@ tusb_desc_device_t const desc_device = {.bLength = sizeof(tusb_desc_device_t),
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const* tud_descriptor_device_cb(void) {
return (uint8_t const*)&desc_device;
uint8_t const *tud_descriptor_device_cb(void) {
return (uint8_t const *)&desc_device;
}
//--------------------------------------------------------------------+
@ -65,7 +65,7 @@ uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(RE
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const* tud_hid_descriptor_report_cb(uint8_t instance) {
uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
(void)instance;
return desc_hid_report;
}
@ -77,17 +77,12 @@ bool tud_hid_n_abs_mouse_report(uint8_t instance,
int16_t y,
int8_t vertical,
int8_t horizontal) {
hid_abs_mouse_report_t report = {
.buttons = buttons, .x = x, .y = y, .wheel = vertical, .pan = horizontal};
mouse_abs_report_t report = {.buttons = buttons, .x = x, .y = y, .wheel = vertical, .pan = horizontal};
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
}
bool tud_hid_abs_mouse_report(uint8_t report_id,
uint8_t buttons,
int16_t x,
int16_t y,
int8_t vertical,
int8_t horizontal) {
bool tud_hid_abs_mouse_report(
uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
}
@ -103,12 +98,7 @@ enum { ITF_NUM_HID, ITF_NUM_TOTAL };
uint8_t const desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1,
ITF_NUM_TOTAL,
0,
CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 500),
// Interface number, string index, protocol, report descriptor len, EP In address, size &
// polling interval
@ -129,8 +119,7 @@ uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based
// on speed
tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
tusb_desc_device_qualifier_t const desc_device_qualifier = {.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
@ -147,15 +136,15 @@ tusb_desc_device_qualifier_t const desc_device_qualifier = {
// complete. device_qualifier descriptor describes information about a high-speed capable device
// that would change if the device were operating at the other speed. If not highspeed capable stall
// this request.
uint8_t const* tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const*)&desc_device_qualifier;
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const *)&desc_device_qualifier;
}
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to
// complete Configuration descriptor in the other speed e.g if high speed then this is for full
// speed and vice versa
uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
// other speed config is basically configuration with type = OHER_SPEED_CONFIG
@ -171,7 +160,7 @@ uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
// This example use the same configuration for both high and full speed mode
@ -191,7 +180,7 @@ enum {
};
// array of pointer to string descriptors
char const* string_desc_arr[] = {
char const *string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"Hrvoje Cavrak", // 1: Manufacturer
"DeskHop Switch", // 2: Product
@ -203,7 +192,7 @@ static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to
// complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void)langid;
uint8_t chr_count;
@ -218,7 +207,7 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
return NULL;
const char* str = string_desc_arr[index];
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);

View File

@ -30,16 +30,14 @@ enum
REPORT_ID_KEYBOARD = 1,
REPORT_ID_MOUSE,
REPORT_ID_CONSUMER_CONTROL,
REPORT_ID_GAMEPAD,
REPORT_ID_COUNT
};
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
/* Report ID */\
__VA_ARGS__ \
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
@ -48,15 +46,18 @@ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
HID_USAGE_MAX ( 5 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX ( 1 ) ,\
\
/* Left, Right, Middle, Backward, Forward buttons */ \
HID_REPORT_COUNT( 5 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
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 ) ,\
\
/* X, Y absolute position [0, 32767] */ \
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
@ -65,6 +66,7 @@ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
HID_REPORT_SIZE ( 16 ) ,\
HID_REPORT_COUNT ( 2 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
\
/* Vertical wheel scroll [-127, 127] */ \
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
HID_LOGICAL_MIN ( 0x81 ) ,\
@ -73,6 +75,7 @@ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
\
/* Horizontal wheel scroll [-127, 127] */ \
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
HID_LOGICAL_MIN ( 0x81 ), \
@ -83,4 +86,80 @@ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
HID_COLLECTION_END , \
HID_COLLECTION_END \
/* Generated report */
/* */
/* 0x05, 0x01, Usage Page (Desktop), */
/* 0x09, 0x06, Usage (Keyboard), */
/* 0xA1, 0x01, Collection (Application), */
/* 0x85, 0x01, Report ID (1), */
/* 0x05, 0x07, Usage Page (Keyboard), */
/* 0x19, 0xE0, Usage Minimum (KB Leftcontrol), */
/* 0x29, 0xE7, Usage Maximum (KB Right GUI), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x25, 0x01, Logical Maximum (1), */
/* 0x95, 0x08, Report Count (8), */
/* 0x75, 0x01, Report Size (1), */
/* 0x81, 0x02, Input (Variable), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x01, Input (Constant), */
/* 0x05, 0x08, Usage Page (LED), */
/* 0x19, 0x01, Usage Minimum (01h), */
/* 0x29, 0x05, Usage Maximum (05h), */
/* 0x95, 0x05, Report Count (5), */
/* 0x75, 0x01, Report Size (1), */
/* 0x91, 0x02, Output (Variable), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x03, Report Size (3), */
/* 0x91, 0x01, Output (Constant), */
/* 0x05, 0x07, Usage Page (Keyboard), */
/* 0x19, 0x00, Usage Minimum (None), */
/* 0x2A, 0xFF, 0x00, Usage Maximum (FFh), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x26, 0xFF, 0x00, Logical Maximum (255), */
/* 0x95, 0x06, Report Count (6), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x00, Input, */
/* 0xC0, End Collection, */
/* 0x05, 0x01, Usage Page (Desktop), */
/* 0x09, 0x02, Usage (Mouse), */
/* 0xA1, 0x01, Collection (Application), */
/* 0x85, 0x02, Report ID (2), */
/* 0x09, 0x01, Usage (Pointer), */
/* 0xA1, 0x00, Collection (Physical), */
/* 0x05, 0x09, Usage Page (Button), */
/* 0x19, 0x01, Usage Minimum (01h), */
/* 0x29, 0x05, Usage Maximum (05h), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x25, 0x01, Logical Maximum (1), */
/* 0x95, 0x05, Report Count (5), */
/* 0x75, 0x01, Report Size (1), */
/* 0x81, 0x02, Input (Variable), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x03, Report Size (3), */
/* 0x81, 0x01, Input (Constant), */
/* 0x05, 0x01, Usage Page (Desktop), */
/* 0x09, 0x30, Usage (X), */
/* 0x09, 0x31, Usage (Y), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x26, 0xFF, 0x7F, Logical Maximum (32767), */
/* 0x75, 0x10, Report Size (16), */
/* 0x95, 0x02, Report Count (2), */
/* 0x81, 0x02, Input (Variable), */
/* 0x09, 0x38, Usage (Wheel), */
/* 0x15, 0x81, Logical Minimum (-127), */
/* 0x25, 0x7F, Logical Maximum (127), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x06, Input (Variable, Relative), */
/* 0x05, 0x0C, Usage Page (Consumer), */
/* 0x0A, 0x38, 0x02, Usage (AC Pan), */
/* 0x15, 0x81, Logical Minimum (-127), */
/* 0x25, 0x7F, Logical Maximum (127), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x06, Input (Variable, Relative), */
/* 0xC0, End Collection, */
/* 0xC0 End Collection */
#endif /* USB_DESCRIPTORS_H_ */

View File

@ -36,7 +36,7 @@
* MOUSE_SPEED_A_FACTOR_X: [1-128], mouse moves at this speed in X direction
* MOUSE_SPEED_A_FACTOR_Y: [1-128], mouse moves at this speed in Y direction
*
* MOUSE_JUMP_THRESHOLD: [0-32768], sets the "force" you need to use to drag the
* JUMP_THRESHOLD: [0-32768], sets the "force" you need to use to drag the
* mouse to another screen, 0 meaning no force needed at all, and ~500 some force
* needed, ~1000 no accidental jumps, you need to really mean it.
*
@ -52,5 +52,22 @@
#define MOUSE_SPEED_B_FACTOR_X 16
#define MOUSE_SPEED_B_FACTOR_Y 16
#define MOUSE_JUMP_THRESHOLD 0
#define JUMP_THRESHOLD 0
/**================================================== *
* ============== Screensaver Config ============== *
* ==================================================
*
* Defines how long does an output need to be idle for screensaver to kick in.
* With this function, after being left idle for a certain amount of time (defined below),
* mouse cursor starts moving around like a bouncy-ball in pong. No clicking, of course.
* Move mouse on that active output to stop.
*
* SCREENSAVER_ENABLED: [0 or 1] 0 means screensaver is disabled, 1 means it is enabled.
* SCREENSAVER_TIME_SEC: time in seconds
*
* */
#define SCREENSAVER_ENABLED 0
#define SCREENSAVER_TIME_SEC 240

View File

@ -21,7 +21,7 @@
* ============== Checksum Functions ============== *
* ================================================== */
uint8_t calc_checksum(const uint8_t* data, int length) {
uint8_t calc_checksum(const uint8_t *data, int length) {
uint8_t checksum = 0;
for (int i = 0; i < length; i++) {
@ -31,7 +31,7 @@ uint8_t calc_checksum(const uint8_t* data, int length) {
return checksum;
}
bool verify_checksum(const uart_packet_t* packet) {
bool verify_checksum(const uart_packet_t *packet) {
uint8_t checksum = calc_checksum(packet->data, PACKET_DATA_LENGTH);
return checksum == packet->checksum;
}
@ -40,11 +40,11 @@ bool verify_checksum(const uart_packet_t* packet) {
* ============== Watchdog Functions ============== *
* ================================================== */
void kick_watchdog(void) {
void kick_watchdog(device_t *state) {
/* Read the timer AFTER duplicating the core1 timestamp,
so it doesn't get updated in the meantime. */
uint64_t core1_last_loop_pass = global_state.core1_last_loop_pass;
uint64_t core1_last_loop_pass = state->core1_last_loop_pass;
uint64_t current_time = time_us_64();
/* If core1 stops updating the timestamp, we'll stop kicking the watchog and reboot */
@ -62,24 +62,85 @@ void wipe_config(void) {
restore_interrupts(ints);
}
void load_config(void) {
config_t* config = ADDR_CONFIG_BASE_ADDR;
void load_config(device_t *state) {
const config_t *config = ADDR_CONFIG_BASE_ADDR;
config_t *running_config = &state->config;
/* If no config is detected, copy default values to our struct. TODO checksum */
if (config->magic_header != 0x0B00B1E5) {
config = (config_t*)&default_config;
}
/* Load the flash config first, including the checksum */
memcpy(running_config, config, sizeof(config_t));
memcpy(&global_state.config, config, sizeof(config_t));
/* Calculate and update checksum, size without checksum */
uint8_t checksum = calc_checksum((uint8_t *)running_config, sizeof(config_t) - sizeof(uint32_t));
/* We expect a certain byte to start the config header */
bool magic_header_fail = (running_config->magic_header != 0xB00B1E5);
/* We expect the checksum to match */
bool checksum_fail = (running_config->checksum != checksum);
/* We expect the config version to match exactly, to avoid erroneous values */
bool version_fail = (running_config->version != CURRENT_CONFIG_VERSION);
/* On any condition failing, we fall back to default config */
if (magic_header_fail || checksum_fail || version_fail)
memcpy(running_config, &default_config, sizeof(config_t));
}
void save_config(void) {
void save_config(device_t *state) {
uint8_t buf[FLASH_PAGE_SIZE];
memcpy(buf, &global_state.config, sizeof(config_t));
uint8_t *raw_config = (uint8_t *)&state->config;
/* Calculate and update checksum, size without checksum */
uint8_t checksum = calc_checksum(raw_config, sizeof(config_t) - sizeof(uint32_t));
state->config.checksum = checksum;
/* Copy the config to buffer and wipe the old one */
memcpy(buf, raw_config, sizeof(config_t));
wipe_config();
/* Disable interrupts, then write the flash page and re-enable */
uint32_t ints = save_and_disable_interrupts();
flash_range_program(PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE, buf, FLASH_PAGE_SIZE);
restore_interrupts(ints);
}
/* Have something fun and entertaining when idle */
void screensaver_task(device_t *state) {
const uint64_t idle_timeout_us = SCREENSAVER_TIME_SEC * 1000000;
const int mouse_move_delay = 5000;
static mouse_abs_report_t report = {.x = 0, .y = 0};
static int last_pointer_move = 0;
/* "Randomly" chosen initial values */
static int dx = 20;
static int dy = 25;
/* If we're not enabled, nothing to do here. */
if (!state->config.screensaver_enabled)
return;
/* We are enabled, but idle time still too small to activate. */
if (time_us_64() - state->last_activity[BOARD_ROLE] < idle_timeout_us)
return;
/* We're active! Now check if it's time to move the cursor yet. */
if ((time_us_32()) - last_pointer_move < mouse_move_delay)
return;
/* Check if we are bouncing off the walls and reverse direction in that case. */
if (report.x + dx < MIN_SCREEN_COORD || report.x + dx > MAX_SCREEN_COORD)
dx = -dx;
if (report.y + dy < MIN_SCREEN_COORD || report.y + dy > MAX_SCREEN_COORD)
dy = -dy;
report.x += dx;
report.y += dy;
/* Move mouse pointer */
queue_mouse_report(&report, state);
/* Update timer of the last pointer move */
last_pointer_move = time_us_32();
}