Merge pull request #642 from tobycm/main

Add toby's hackpad
This commit is contained in:
Taran Mittal 2025-10-06 15:17:49 +11:00 committed by GitHub
commit 6a03969394
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 663468 additions and 0 deletions

View file

@ -0,0 +1,66 @@
---
runme:
id: 01JN0WNC339XBG3HPCGNCHPFSG
version: v3
---
# Toby's Hackpad
simple hackpad to teach me how to make schematics, design PCBs, and write firmware for the RP2040.
thank you deeply to @dhylands for mentoring me through this process. i learned so much small tips for the encoders, i2c, switches, neopixels, etc.
## CAD Model:
![case](./assets/case.png)
![case top cover](./assets/top.png)
speedran the CAD model in Onshape. i love onshape.
## PCB
pcb made in kicad, very nice software that works great with linux too! learned how to make my own symbols for an external display!!!
Schematic:
![schematic](./assets/schematic.png)
PCB:
![pcb!](./assets/pcb.png)
## Assembled preview
![assembled](./assets/assembled.png)
![assembled](./assets/assembled_no_top.png)
![assembled](./assets/assembled_no_top_top_view.png)
## Firmware Overview
using kmk for the firmware. primitive at the moment but planning to make extensions through the spare gpio pins in the future.
- the rotary encoder changes volume!
- VIA support soon!
## BOM:
| Quantity | Part |
|----------|--------------------------------------|
| 1 | PinHeader_1x16_P2.54mm_Vertical |
| 12 | LED_SK6812MINI_PLCC4 |
| 10 | SW_Cherry_MX |
| 1 | C-100nF-0603 |
| 1 | MCP23017_SO |
| 5 | C-1uF-0603 |
| 2 | R-4.7K-0603 |
| 1 | XIAO-RP2040-DIP |
| 1 | RotaryEncoder_Alps_EC11E-Switch |
please refer to [bom.csv](./bom.csv) for the full list of components.
## Smol fact
i joined acon speedrunning session on the last day before the deadline and it was pretty fun to listen to someone yapping while i work on my hackpad lol.

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

View file

@ -0,0 +1,10 @@
Quantity,Part
1,PinHeader_1x16_P2.54mm_Vertical
12,LED_SK6812MINI_PLCC4
10,SW_Cherry_MX
1,C-100nF-0603
1,MCP23017_SO
5,C-1uF-0603
2,R-4.7K-0603
1,XIAO-RP2040-DIP
1,RotaryEncoder_Alps_EC11E-Switch
1 Quantity Part
2 1 PinHeader_1x16_P2.54mm_Vertical
3 12 LED_SK6812MINI_PLCC4
4 10 SW_Cherry_MX
5 1 C-100nF-0603
6 1 MCP23017_SO
7 5 C-1uF-0603
8 2 R-4.7K-0603
9 1 XIAO-RP2040-DIP
10 1 RotaryEncoder_Alps_EC11E-Switch

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,54 @@
print("toby's lil hackpad!")
# Basic imports for OrpheusPad
import board
import busio
from adafruit_mcp230xx.mcp23017 import MCP23017
from kmk.extensions.RGB import RGB
from kmk.keys import KC
from kmk.kmk_keyboard import KMKKeyboard
from kmk.modules.encoder import EncoderHandler
from kmk.scanners.mcp import KMKScannerMCP230XX # MCP23017 scanner for KMK
# Initialize I2C with correct SDA (GPIO6) and SCL (GPIO7)
i2c = busio.I2C(board.GP7, board.GP6) # SDA = GPIO6, SCL = GPIO7
# Initialize MCP23017
mcp = MCP23017(i2c)
# Initialize Keyboard
keyboard = KMKKeyboard()
# Configure MCP23017 scanner for KMK
keyboard.matrix = KMKScannerMCP230XX(
i2c=i2c,
mcp230xx_cls=MCP23017,
columns=5, # Adjust as needed
rows=2, # Adjust as needed
column_pins=[0, 1, 2, 3, 4], # MCP23017 GPIOs for columns (GPA0-GPA4)
row_pins=[5, 6, 7, 14,
15], # MCP23017 GPIOs for rows (GPA5-GPA7, GPB6-GPB7)
)
# RGB Configuration
rgb = RGB(pixel_pin=board.GP0, num_pixels=12) # LED on GPIO0
keyboard.extensions.append(rgb)
# Encoder Configuration
encoder_handler = EncoderHandler()
encoder_handler.pins = (
(board.GP2, board.GP1, board.GP3), # Encoder #1
)
encoder_handler.map = [
((KC.UP, KC.DOWN), ),
]
keyboard.modules.append(encoder_handler)
# Define Keymap
keyboard.keymap = [[
KC.A, KC.B, KC.C, KC.D, KC.E, KC.F, KC.G, KC.H, KC.I, KC.J
]]
if __name__ == '__main__':
keyboard.go()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,83 @@
{
"board": {
"active_layer": 31,
"active_layer_preset": "All Layers",
"auto_track_width": true,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
0,
1,
2,
3,
4,
5,
8,
9,
10,
11,
12,
13,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
32,
33,
34,
35,
36,
39,
40
],
"visible_layers": "fffffff_ffffffff",
"zone_display_mode": 0
},
"git": {
"repo_password": "",
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "toby-hackpad.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View file

@ -0,0 +1,584 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.762,
"height": 1.524,
"width": 1.524
},
"silk_line_width": 0.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_near_hole": "error",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.1,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.1,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"simulation_model_issue": "ignore",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "toby-hackpad.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "./",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"name": "Grouped By Value",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"a0ac5c88-1c78-4be4-bce9-7e4d45d376cd",
"Root"
]
],
"text_variables": {}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -0,0 +1,54 @@
print("toby's lil hackpad!")
# Basic imports for OrpheusPad
import board
import busio
from adafruit_mcp230xx.mcp23017 import MCP23017
from kmk.extensions.RGB import RGB
from kmk.keys import KC
from kmk.kmk_keyboard import KMKKeyboard
from kmk.modules.encoder import EncoderHandler
from kmk.scanners.mcp import KMKScannerMCP230XX # MCP23017 scanner for KMK
# Initialize I2C with correct SDA (GPIO6) and SCL (GPIO7)
i2c = busio.I2C(board.GP7, board.GP6) # SDA = GPIO6, SCL = GPIO7
# Initialize MCP23017
mcp = MCP23017(i2c)
# Initialize Keyboard
keyboard = KMKKeyboard()
# Configure MCP23017 scanner for KMK
keyboard.matrix = KMKScannerMCP230XX(
i2c=i2c,
mcp230xx_cls=MCP23017,
columns=5, # Adjust as needed
rows=2, # Adjust as needed
column_pins=[0, 1, 2, 3, 4], # MCP23017 GPIOs for columns (GPA0-GPA4)
row_pins=[5, 6, 7, 14,
15], # MCP23017 GPIOs for rows (GPA5-GPA7, GPB6-GPB7)
)
# RGB Configuration
rgb = RGB(pixel_pin=board.GP0, num_pixels=12) # LED on GPIO0
keyboard.extensions.append(rgb)
# Encoder Configuration
encoder_handler = EncoderHandler()
encoder_handler.pins = (
(board.GP2, board.GP1, board.GP3), # Encoder #1
)
encoder_handler.map = [
((KC.UP, KC.DOWN), ),
]
keyboard.modules.append(encoder_handler)
# Define Keymap
keyboard.keymap = [[
KC.A, KC.B, KC.C, KC.D, KC.E, KC.F, KC.G, KC.H, KC.I, KC.J
]]
if __name__ == '__main__':
keyboard.go()

File diff suppressed because it is too large Load diff