Compare commits

...

53 Commits

Author SHA1 Message Date
demik
33f2627253 Revert "added flash switch watchdog"
This reverts commit 9152b953aa4b516238fd8f009775ccb52d42a4be because you can't enter bootloader from esp_restart() on ESP32
2025-03-20 13:24:31 +01:00
demik
9152b953aa added flash switch watchdog 2025-03-18 18:30:07 +01:00
demik
5d27f994fa added name to action idf.yml 2025-03-08 18:10:46 +01:00
demik
ef6eaab742 upgraded Changelog for v1.4.10 2025-03-07 10:04:50 +01:00
demik
75d5d49256
Update README.md
added CI badge + minot fixes
2025-03-04 00:45:58 +01:00
demik
c69cc3fc89 added Mighty Mouse driver 2025-03-04 00:12:35 +01:00
demik
fc213f398f 5.4.0 doc update 2025-03-03 13:34:56 +01:00
demik
4d1653a8a6 fix GHA path 2025-03-03 13:30:58 +01:00
demik
f35b094f37 ESP-IDF v5.4 code modifications 2025-03-03 13:25:21 +01:00
demik
2862c505a2 only start github actions when push to master/main 2025-03-03 13:22:18 +01:00
demik
c3134eef71
Create idf.yml
gibve a try at espressif/esp-idf-ci-action
2025-03-03 13:07:03 +01:00
demik
a71c9b8d2c upgraded Changelog for v1.4.9 2025-02-10 21:55:29 +01:00
demik
76aa350ffe modified clearance 2024-09-25 23:42:44 +02:00
demik
73413f0ade Back panel update: Apple IIc 2024-09-25 23:38:45 +02:00
demik
0df33e3aa0 fixed typo 2024-08-29 12:59:04 +02:00
demik
9f8d71fcea added README for Quack X esp_8_bit 2024-08-29 12:47:02 +02:00
demik
bcc7802439 added resources for esp_8_bit build 2024-08-29 11:11:14 +02:00
demik
66ee6a28fa added basic support for some PDP Rock Candy Wiimotes 2024-08-22 20:02:50 +02:00
demik
416973176e updated README for v1.4.8 2024-08-15 12:06:27 +02:00
demik
2ab47ae4db added wii remote support (buttons) 2024-08-14 23:19:39 +02:00
demik
85fcf88209 esp_hidh_dev_open() doesn't give a toss about BLE address types in our case 2024-08-10 13:12:48 +02:00
demik
bb52d6057a added Apple IIc compatibility 2024-07-03 00:01:09 +02:00
demik
9e2e5847fc added support for MacAlly Joystick (thanks tashtari@68kmla !) 2024-06-25 16:52:00 +02:00
demik
53b675040e added half click feature (on BT right click) 2024-06-18 11:54:42 +02:00
demik
78585fda10 added new FR-4 front panel 2024-06-17 22:00:57 +02:00
demik
1e4e872410 sync register probing to scheduling 2024-06-17 21:55:22 +02:00
demik
9c4dc1acb6 New FR-4 based front panel with shine-through LEDs 2024-06-08 23:19:15 +02:00
demik
bef062d1cd added complex PCB front panel 2024-06-07 23:17:22 +02:00
demik
1e142082b1 Upgraded SDK to v5.2.2 2024-06-06 20:44:51 +02:00
demik
75260870c7 fix ADB start and stop bit timings 2024-06-06 00:10:01 +02:00
demik
97a57e89ac added support for Kensington ADB Devices 2024-06-04 10:04:47 +02:00
demik
6a0e7ee968 early work to restore ADB to bluetooth functionality 2024-06-03 22:05:37 +02:00
demik
cfe3c0314a Updated ChangeLog to 1.4.5 2023-12-28 21:45:46 +01:00
demik
8dd60e2d20 Upgraded SDK to v5.1.2 2023-12-28 21:40:18 +01:00
demik
17be92fec7
Merge pull request #2 from ElectronicsArchiver/README-Rework
README Rework
2022-08-03 18:18:02 +02:00
ElectronicsArchiver
405194156c Merge branch 'README-Rework' of https://github.com/ElectronicsArchiver/quack into README-Rework 2022-08-03 12:08:08 -04:00
ElectronicsArchiver
72218018a4 Added Bigger Preview 2022-08-03 12:07:58 -04:00
トトも
6ad6c7bb4e
Adjusted Badge Style 2022-08-02 07:57:02 -04:00
ElectronicsArchiver
876920fd90 Updated Support Section 2022-07-05 06:56:52 -04:00
トトも
c211ab1f2c
Adjusted Spacing 2022-07-05 06:54:28 -04:00
ElectronicsArchiver
86f08d776b Formatted README 2022-07-05 06:51:16 -04:00
ElectronicsArchiver
0ca38e2f0a Cut Out Preview 2022-07-05 06:48:29 -04:00
demik
eeb5772664 Upgraded SDK to v4.2.3 2022-04-03 21:28:45 +02:00
demik
b4024687b4 cleaning 2022-01-04 19:26:32 +01:00
demik
4ba1b4b113 added back panel using a PCB 2022-01-04 19:23:36 +01:00
demik
f691dc34e6
fix typo 2021-12-22 23:05:08 +01:00
demik
7911d07477 provides some slight acceleration and desceleration on bluetooth mouses 2021-12-02 14:00:12 +01:00
demik
9c52c41ee2
Added support for Apple Lisa 2021-11-20 16:57:17 +01:00
demik
03a92bcda4 fixed BT LED state in case some mouse is reconnecting while the quack is scanning 2021-10-30 15:38:34 +02:00
demik
157ab85c84 Merge branch 'master' of github.com:demik/quack 2021-10-30 15:07:11 +02:00
demik
441cbe9bc7 added support for MicroSpeed MacTRAC 2.0 2021-10-30 15:06:51 +02:00
demik
1638fb5805
Added photo on README.md 2021-10-12 19:30:33 +02:00
demik
891e537a89 added PCB image 2021-10-12 19:26:54 +02:00
47 changed files with 13587 additions and 942 deletions

24
.github/workflows/idf.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: build
on:
push:
branches:
- 'master'
paths:
- 'main/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: ESP-IDF build
uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: v5.4
target: esp32
path: './'

View File

@ -1,3 +1,52 @@
## v1.4.10
- update to ESP-IDF v5.4.0
- https://github.com/espressif/esp-idf/releases/tag/v5.4
- fixed stabilities where Quack would reboot on BT disconnect
- added support for Apple Mighty Mouse (Bluetooth)
## v1.4.9
- added basic support for some PDP Rock Candy Wiimotes
- BDAs starting from the range 08:03:64
- remote doesn't reconnect correctly, have to repair each time
## v1.4.8
- added support for MacAlly Joystick
- added support for Nintendo Wiimotes
- D-pad movement working, click on B, half-click on A
- selectable movement speed on the Weemote + LED feedback
- this lay the framework to add drivers for some unsupported devices
## v1.4.7
- new feature: half click on right BT click (similar to some ADB devices)
- workarounds for v1.4.6 green led regression
## v1.4.6
- added support for ADB composite devices (Kensignton and some Joysticks)
- fixed ADB start and stop bit times (65 µs vs 70 µs, error in AN591)
- update to ESP-IDF v5.2.2
- https://github.com/espressif/esp-idf/releases/tag/v5.2.2
- fixed flash corruption after pairing too much devices (20+)
- fixed ADB host mode where the ESP boot was too fast (v5.2 regression)
## v1.4.5
- update to ESP-IDF v5.1.2
- https://github.com/espressif/esp-idf/releases/tag/v5.1.2
- big background update, lot of cleaning and rewrites
- as most patches are now mainstream, sdk patching is now minimal
- removed direct ABI bluedroid call
## v1.4.4
- update to ESP-IDF v4.2.3:
- https://github.com/espressif/esp-idf/releases/tag/v4.2.3
- mostly bluetooth related security and bug fixes
## v1.4.3
- provides some slight acceleration and desceleration on bluetooth mouses
## v1.4.2
- added support for MicroSpeed MacTRAC 2.0
- fixed a BT led state when a device reconnect while scanning
## v1.4.1
- update to ESP-IDF v4.2.2:
- https://github.com/espressif/esp-idf/releases/tag/v4.2.2
@ -6,4 +55,4 @@
## v1.4.0
- Initial firmare release
- 1.4.x series is compatible with PCB 1.4 (EDA subfolder)
- 1.4.x series is compatible with 1.3 and 1.4 PCBs (EDA subfolder)

3
EDA/.gitignore vendored
View File

@ -1,6 +1,9 @@
# tmp zip files
*.zip
# production folders
production
# KiCad backup files
*bak

View File

@ -0,0 +1,129 @@
(kicad_symbol_lib
(version 20231120)
(generator "kicad_symbol_editor")
(generator_version "8.0")
(symbol "Earth-power"
(power)
(pin_names
(offset 0)
)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "#PWR"
(at 0 -6.35 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "power_Earth"
(at 0 -3.81 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "Earth-power_0_1"
(polyline
(pts
(xy -0.635 -1.905) (xy 0.635 -1.905)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy -0.127 -2.54) (xy 0.127 -2.54)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 0 -1.27) (xy 0 0)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 1.27 -1.27) (xy -1.27 -1.27)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
)
(symbol "Earth-power_1_1"
(pin power_in line
(at 0 0 270)
(length 0) hide
(name "Earth"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
)

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": "quack_BP.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View File

@ -0,0 +1,597 @@
{
"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.12,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.15,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.508
}
},
"diff_pair_dimensions": [],
"drc_exclusions": [
"lib_footprint_issues|90013194|51095675|00000000-0000-0000-0000-000061b10486|00000000-0000-0000-0000-000000000000",
"lib_footprint_mismatch|50500000|50500000|00000000-0000-0000-0000-000061b10897|00000000-0000-0000-0000-000000000000",
"lib_footprint_mismatch|86000000|50500000|00000000-0000-0000-0000-000061b104b1|00000000-0000-0000-0000-000000000000"
],
"meta": {
"filename": "board_design_settings.json",
"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"
},
"rule_severitieslegacy_courtyards_overlap": true,
"rule_severitieslegacy_no_courtyard_defined": false,
"rules": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.025,
"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.2,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.4,
"solder_mask_to_copper_clearance": 0.005,
"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": "quack_BP.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.25,
"via_diameter": 0.8,
"via_drill": 0.4,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "Default",
"pattern": "Earth"
}
]
},
"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": "Référence"
},
"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.25,
"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": 0.0,
"text_offset_ratio": 0.08
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "Pcbnew",
"page_layout_descr_file": "",
"plot_directory": "",
"spice_adjust_passive_values": false,
"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": [
[
"1232ec36-2672-4328-a849-eb4a8f2ddaa6",
"Racine"
]
],
"text_variables": {}
}

View File

@ -0,0 +1,472 @@
(kicad_sch
(version 20231120)
(generator "eeschema")
(generator_version "8.0")
(uuid "1232ec36-2672-4328-a849-eb4a8f2ddaa6")
(paper "A4")
(title_block
(title "Quack back panel")
(date "2024-09-25")
(rev "1.1")
(company "Lostwave")
(comment 1 "https://68kmla.org")
(comment 2 "https://github.com/quack/quack")
)
(lib_symbols
(symbol "Connector:TestPoint_Small"
(pin_numbers hide)
(pin_names
(offset 0.762) hide)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "TP"
(at 0 3.81 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value" "TestPoint_Small"
(at 0 2.032 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 5.08 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 5.08 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "test point"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_keywords" "test point tp"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_fp_filters" "Pin* Test*"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "TestPoint_Small_0_1"
(circle
(center 0 0)
(radius 0.508)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
)
(symbol "TestPoint_Small_1_1"
(pin passive line
(at 0 0 90)
(length 0)
(name "1"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
(symbol "quack_BP-rescue:Earth-power"
(power)
(pin_names
(offset 0)
)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "#PWR"
(at 0 -6.35 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "power_Earth"
(at 0 -3.81 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "Earth-power_0_1"
(polyline
(pts
(xy -0.635 -1.905) (xy 0.635 -1.905)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy -0.127 -2.54) (xy 0.127 -2.54)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 0 -1.27) (xy 0 0)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 1.27 -1.27) (xy -1.27 -1.27)
)
(stroke
(width 0)
(type solid)
)
(fill
(type none)
)
)
)
(symbol "Earth-power_1_1"
(pin power_in line
(at 0 0 270)
(length 0) hide
(name "Earth"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
)
(junction
(at 76.2 69.85)
(diameter 0)
(color 0 0 0 0)
(uuid "b8bf2be8-1ddf-4427-baed-a4ea02cd13ad")
)
(wire
(pts
(xy 76.2 69.85) (xy 76.2 63.5)
)
(stroke
(width 0)
(type default)
)
(uuid "31668198-2b9f-4922-aa69-bfbabf4ccbea")
)
(wire
(pts
(xy 76.2 76.2) (xy 76.2 69.85)
)
(stroke
(width 0)
(type default)
)
(uuid "44ca78b7-df78-4257-9653-2befb9037c60")
)
(symbol
(lib_id "quack_BP-rescue:Earth-power")
(at 76.2 76.2 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(uuid "00000000-0000-0000-0000-000061b0ab0b")
(property "Reference" "#PWR0101"
(at 76.2 82.55 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "Earth"
(at 76.2 80.01 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Footprint" ""
(at 76.2 76.2 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 76.2 76.2 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 76.2 76.2 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "e290e00d-4243-4936-bdad-addda8d9fb43")
)
(instances
(project ""
(path "/1232ec36-2672-4328-a849-eb4a8f2ddaa6"
(reference "#PWR0101")
(unit 1)
)
)
)
)
(symbol
(lib_id "Connector:TestPoint_Small")
(at 76.2 63.5 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(uuid "00000000-0000-0000-0000-000061b0af58")
(property "Reference" "TP1"
(at 77.4192 62.3316 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TestPoint_Small"
(at 77.4192 64.643 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" "TestPoint:TestPoint_Pad_1.5x1.5mm"
(at 81.28 63.5 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 81.28 63.5 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 76.2 63.5 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "f1847415-b52e-4ad5-8664-458e1426d5bc")
)
(instances
(project ""
(path "/1232ec36-2672-4328-a849-eb4a8f2ddaa6"
(reference "TP1")
(unit 1)
)
)
)
)
(symbol
(lib_id "Connector:TestPoint_Small")
(at 76.2 69.85 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(uuid "00000000-0000-0000-0000-000061b0c87f")
(property "Reference" "TP2"
(at 77.4192 68.6816 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TestPoint_Small"
(at 77.4192 70.993 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" "TestPoint:TestPoint_Pad_1.5x1.5mm"
(at 81.28 69.85 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 81.28 69.85 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 76.2 69.85 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "7de150b2-ba63-4659-98b8-dba699d081f0")
)
(instances
(project ""
(path "/1232ec36-2672-4328-a849-eb4a8f2ddaa6"
(reference "TP2")
(unit 1)
)
)
)
)
(sheet_instances
(path "/"
(page "1")
)
)
)

View File

@ -0,0 +1,248 @@
update=2021 December 08, Wednesday 14:30:39
version=1
last_client=kicad
[general]
version=1
RootSch=
BoardNm=
[cvpcb]
version=1
NetIExt=net
[eeschema]
version=1
LibDir=
[eeschema/libraries]
[schematic_editor]
version=1
PageLayoutDescrFile=
PlotDirectoryName=
SubpartIdSeparator=0
SubpartFirstId=65
NetFmtName=Pcbnew
SpiceAjustPassiveValues=0
LabSize=50
ERC_TestSimilarLabels=1
[pcbnew]
version=1
PageLayoutDescrFile=
LastNetListRead=
CopperLayerCount=2
BoardThickness=1.6
AllowMicroVias=0
AllowBlindVias=0
RequireCourtyardDefinitions=0
ProhibitOverlappingCourtyards=1
MinTrackWidth=0.2
MinViaDiameter=0.4
MinViaDrill=0.3
MinMicroViaDiameter=0.2
MinMicroViaDrill=0.09999999999999999
MinHoleToHole=0.25
TrackWidth1=0.25
ViaDiameter1=0.8
ViaDrill1=0.4
dPairWidth1=0.2
dPairGap1=0.25
dPairViaGap1=0.25
SilkLineWidth=0.12
SilkTextSizeV=1
SilkTextSizeH=1
SilkTextSizeThickness=0.15
SilkTextItalic=0
SilkTextUpright=1
CopperLineWidth=0.2
CopperTextSizeV=1.5
CopperTextSizeH=1.5
CopperTextThickness=0.3
CopperTextItalic=0
CopperTextUpright=1
EdgeCutLineWidth=0.05
CourtyardLineWidth=0.05
OthersLineWidth=0.15
OthersTextSizeV=1
OthersTextSizeH=1
OthersTextSizeThickness=0.15
OthersTextItalic=0
OthersTextUpright=1
SolderMaskClearance=0
SolderMaskMinWidth=0
SolderPasteClearance=0
SolderPasteRatio=-0
[pcbnew/Layer.F.Cu]
Name=F.Cu
Type=0
Enabled=1
[pcbnew/Layer.In1.Cu]
Name=In1.Cu
Type=0
Enabled=0
[pcbnew/Layer.In2.Cu]
Name=In2.Cu
Type=0
Enabled=0
[pcbnew/Layer.In3.Cu]
Name=In3.Cu
Type=0
Enabled=0
[pcbnew/Layer.In4.Cu]
Name=In4.Cu
Type=0
Enabled=0
[pcbnew/Layer.In5.Cu]
Name=In5.Cu
Type=0
Enabled=0
[pcbnew/Layer.In6.Cu]
Name=In6.Cu
Type=0
Enabled=0
[pcbnew/Layer.In7.Cu]
Name=In7.Cu
Type=0
Enabled=0
[pcbnew/Layer.In8.Cu]
Name=In8.Cu
Type=0
Enabled=0
[pcbnew/Layer.In9.Cu]
Name=In9.Cu
Type=0
Enabled=0
[pcbnew/Layer.In10.Cu]
Name=In10.Cu
Type=0
Enabled=0
[pcbnew/Layer.In11.Cu]
Name=In11.Cu
Type=0
Enabled=0
[pcbnew/Layer.In12.Cu]
Name=In12.Cu
Type=0
Enabled=0
[pcbnew/Layer.In13.Cu]
Name=In13.Cu
Type=0
Enabled=0
[pcbnew/Layer.In14.Cu]
Name=In14.Cu
Type=0
Enabled=0
[pcbnew/Layer.In15.Cu]
Name=In15.Cu
Type=0
Enabled=0
[pcbnew/Layer.In16.Cu]
Name=In16.Cu
Type=0
Enabled=0
[pcbnew/Layer.In17.Cu]
Name=In17.Cu
Type=0
Enabled=0
[pcbnew/Layer.In18.Cu]
Name=In18.Cu
Type=0
Enabled=0
[pcbnew/Layer.In19.Cu]
Name=In19.Cu
Type=0
Enabled=0
[pcbnew/Layer.In20.Cu]
Name=In20.Cu
Type=0
Enabled=0
[pcbnew/Layer.In21.Cu]
Name=In21.Cu
Type=0
Enabled=0
[pcbnew/Layer.In22.Cu]
Name=In22.Cu
Type=0
Enabled=0
[pcbnew/Layer.In23.Cu]
Name=In23.Cu
Type=0
Enabled=0
[pcbnew/Layer.In24.Cu]
Name=In24.Cu
Type=0
Enabled=0
[pcbnew/Layer.In25.Cu]
Name=In25.Cu
Type=0
Enabled=0
[pcbnew/Layer.In26.Cu]
Name=In26.Cu
Type=0
Enabled=0
[pcbnew/Layer.In27.Cu]
Name=In27.Cu
Type=0
Enabled=0
[pcbnew/Layer.In28.Cu]
Name=In28.Cu
Type=0
Enabled=0
[pcbnew/Layer.In29.Cu]
Name=In29.Cu
Type=0
Enabled=0
[pcbnew/Layer.In30.Cu]
Name=In30.Cu
Type=0
Enabled=0
[pcbnew/Layer.B.Cu]
Name=B.Cu
Type=0
Enabled=1
[pcbnew/Layer.B.Adhes]
Enabled=0
[pcbnew/Layer.F.Adhes]
Enabled=0
[pcbnew/Layer.B.Paste]
Enabled=0
[pcbnew/Layer.F.Paste]
Enabled=0
[pcbnew/Layer.B.SilkS]
Enabled=1
[pcbnew/Layer.F.SilkS]
Enabled=1
[pcbnew/Layer.B.Mask]
Enabled=1
[pcbnew/Layer.F.Mask]
Enabled=1
[pcbnew/Layer.Dwgs.User]
Enabled=0
[pcbnew/Layer.Cmts.User]
Enabled=0
[pcbnew/Layer.Eco1.User]
Enabled=0
[pcbnew/Layer.Eco2.User]
Enabled=0
[pcbnew/Layer.Edge.Cuts]
Enabled=1
[pcbnew/Layer.Margin]
Enabled=1
[pcbnew/Layer.B.CrtYd]
Enabled=1
[pcbnew/Layer.F.CrtYd]
Enabled=1
[pcbnew/Layer.B.Fab]
Enabled=1
[pcbnew/Layer.F.Fab]
Enabled=1
[pcbnew/Layer.Rescue]
Enabled=0
[pcbnew/Netclasses]
[pcbnew/Netclasses/Default]
Name=Default
Clearance=0.2
TrackWidth=0.25
ViaDiameter=0.8
ViaDrill=0.4
uViaDiameter=0.3
uViaDrill=0.1
dPairWidth=0.2
dPairGap=0.25
dPairViaGap=0.25

BIN
EDA/Panels/Front/ADB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,201 @@
(footprint "LOGO" (version 20221018) (generator bitmap2component)
(layer "F.Cu")
(attr board_only exclude_from_pos_files exclude_from_bom)
(fp_text reference "G***" (at 0 0) (layer "F.SilkS")
(effects (font (size 1.5 1.5) (thickness 0.3)))
(tstamp bab20572-ab1f-4616-b2b2-53186f4f2c29)
)
(fp_text value "LOGO" (at 0.75 0) (layer "F.SilkS") hide
(effects (font (size 1.5 1.5) (thickness 0.3)))
(tstamp 74d306e1-5916-4e5a-bcfe-b8aff857e827)
)
(fp_poly
(pts
(xy -0.967687 0.339291)
(xy -0.863523 0.425217)
(xy -0.828276 0.464011)
(xy -0.792831 0.488322)
(xy -0.743544 0.501571)
(xy -0.666769 0.507180)
(xy -0.548862 0.508573)
(xy -0.530989 0.508620)
(xy -0.341151 0.517855)
(xy -0.194397 0.547254)
(xy -0.079715 0.600882)
(xy 0.013912 0.682801)
(xy 0.045610 0.721340)
(xy 0.078417 0.766596)
(xy 0.101268 0.809170)
(xy 0.116289 0.860518)
(xy 0.125609 0.932099)
(xy 0.131355 1.035369)
(xy 0.135654 1.181785)
(xy 0.136349 1.209947)
(xy 0.139425 1.365776)
(xy 0.139100 1.474832)
(xy 0.134440 1.546731)
(xy 0.124511 1.591091)
(xy 0.108378 1.617527)
(xy 0.095677 1.628535)
(xy 0.009875 1.662387)
(xy -0.081162 1.640472)
(xy -0.091722 1.634628)
(xy -0.114272 1.613811)
(xy -0.128849 1.575766)
(xy -0.137080 1.509794)
(xy -0.140591 1.405195)
(xy -0.141111 1.309529)
(xy -0.146892 1.125701)
(xy -0.165295 0.991346)
(xy -0.197916 0.900583)
(xy -0.246345 0.847530)
(xy -0.269376 0.835976)
(xy -0.317608 0.827890)
(xy -0.405795 0.821776)
(xy -0.517452 0.818649)
(xy -0.554464 0.818444)
(xy -0.672045 0.819513)
(xy -0.747861 0.825340)
(xy -0.796584 0.839856)
(xy -0.832886 0.866990)
(xy -0.863523 0.901227)
(xy -0.966157 0.987013)
(xy -1.081305 1.026299)
(xy -1.199489 1.024329)
(xy -1.311230 0.986344)
(xy -1.407049 0.917588)
(xy -1.477469 0.823301)
(xy -1.513010 0.708727)
(xy -1.504195 0.579108)
(xy -1.496115 0.551355)
(xy -1.430437 0.430842)
(xy -1.332604 0.346956)
(xy -1.214767 0.302121)
(xy -1.089078 0.298758)
)
(stroke (width 0.000000) (type solid)) (fill solid) (layer "F.SilkS") (tstamp c7a58463-b108-42ba-9a88-3d34cf5c1bf4))
(fp_poly
(pts
(xy 1.259897 -0.668993)
(xy 1.331506 -0.635877)
(xy 1.392588 -0.584544)
(xy 1.477977 -0.470504)
(xy 1.512294 -0.345050)
(xy 1.496646 -0.218291)
(xy 1.432143 -0.100335)
(xy 1.329326 -0.007428)
(xy 1.203830 0.045712)
(xy 1.074948 0.045922)
(xy 0.952682 -0.004896)
(xy 0.852152 -0.098214)
(xy 0.820579 -0.132945)
(xy 0.783631 -0.154070)
(xy 0.727320 -0.164935)
(xy 0.637658 -0.168887)
(xy 0.554901 -0.169333)
(xy 0.432714 -0.167543)
(xy 0.351560 -0.160029)
(xy 0.296086 -0.143577)
(xy 0.250935 -0.114973)
(xy 0.235987 -0.102580)
(xy 0.193251 -0.058852)
(xy 0.167831 -0.007829)
(xy 0.153713 0.068037)
(xy 0.146508 0.158475)
(xy 0.136575 0.261590)
(xy 0.122012 0.344227)
(xy 0.106195 0.388056)
(xy 0.053084 0.416405)
(xy -0.022119 0.421180)
(xy -0.090132 0.402187)
(xy -0.107244 0.389467)
(xy -0.125577 0.341817)
(xy -0.137288 0.247941)
(xy -0.141111 0.126881)
(xy -0.138820 0.005686)
(xy -0.128982 -0.078734)
(xy -0.107152 -0.145963)
(xy -0.068884 -0.215585)
(xy -0.060251 -0.229342)
(xy 0.010405 -0.322439)
(xy 0.092776 -0.388945)
(xy 0.197752 -0.433387)
(xy 0.336224 -0.460291)
(xy 0.519082 -0.474183)
(xy 0.529901 -0.474630)
(xy 0.664168 -0.481360)
(xy 0.752841 -0.490158)
(xy 0.806771 -0.503223)
(xy 0.836810 -0.522755)
(xy 0.848238 -0.539158)
(xy 0.913290 -0.606885)
(xy 1.017232 -0.655085)
(xy 1.143924 -0.676781)
(xy 1.167811 -0.677333)
)
(stroke (width 0.000000) (type solid)) (fill solid) (layer "F.SilkS") (tstamp 776a8a2f-41bb-4cbc-b0be-6c9ea146df53))
(fp_poly
(pts
(xy -1.047610 -1.647727)
(xy -0.985222 -1.631585)
(xy -0.932496 -1.593092)
(xy -0.888788 -1.547716)
(xy -0.794134 -1.444431)
(xy -0.484261 -1.434827)
(xy -0.346803 -1.429766)
(xy -0.252227 -1.422635)
(xy -0.186982 -1.410313)
(xy -0.137515 -1.389682)
(xy -0.090274 -1.357620)
(xy -0.066027 -1.338549)
(xy 0.033764 -1.242941)
(xy 0.097440 -1.138070)
(xy 0.131157 -1.009158)
(xy 0.141067 -0.841425)
(xy 0.141067 -0.841211)
(xy 0.139279 -0.728284)
(xy 0.131564 -0.657425)
(xy 0.114486 -0.614314)
(xy 0.084604 -0.584629)
(xy 0.078387 -0.580156)
(xy 0.022452 -0.544875)
(xy -0.016495 -0.542386)
(xy -0.067105 -0.572268)
(xy -0.075165 -0.577899)
(xy -0.108691 -0.611754)
(xy -0.129324 -0.665081)
(xy -0.141424 -0.752568)
(xy -0.145750 -0.815715)
(xy -0.156903 -0.942594)
(xy -0.179676 -1.029777)
(xy -0.223331 -1.084628)
(xy -0.297130 -1.114510)
(xy -0.410334 -1.126790)
(xy -0.539603 -1.128889)
(xy -0.668608 -1.127757)
(xy -0.752679 -1.122959)
(xy -0.803307 -1.112394)
(xy -0.831980 -1.093959)
(xy -0.846510 -1.072737)
(xy -0.911784 -1.003554)
(xy -1.013926 -0.953673)
(xy -1.134380 -0.931694)
(xy -1.151536 -0.931333)
(xy -1.285728 -0.957746)
(xy -1.399504 -1.030666)
(xy -1.479522 -1.140613)
(xy -1.493099 -1.173949)
(xy -1.517829 -1.293905)
(xy -1.498193 -1.400965)
(xy -1.430574 -1.509344)
(xy -1.398285 -1.546545)
(xy -1.337923 -1.606991)
(xy -1.284711 -1.638144)
(xy -1.215233 -1.649587)
(xy -1.143000 -1.651000)
)
(stroke (width 0.000000) (type solid)) (fill solid) (layer "F.SilkS") (tstamp 01ac52df-a17a-4784-9efb-a7bd9808d656))
)

View File

@ -0,0 +1,144 @@
(footprint "LOGO" (version 20221018) (generator bitmap2component)
(layer "F.Cu")
(attr board_only exclude_from_pos_files exclude_from_bom)
(fp_text reference "G***" (at 0 0) (layer "F.SilkS")
(effects (font (size 1.5 1.5) (thickness 0.3)))
(tstamp 7a502b98-1547-4eee-b558-16b64e9d1e25)
)
(fp_text value "LOGO" (at 0.75 0) (layer "F.SilkS") hide
(effects (font (size 1.5 1.5) (thickness 0.3)))
(tstamp fbe39f27-c970-4b01-b8f6-1e2925fe7bc3)
)
(fp_poly
(pts
(xy 0.089038 -0.896103)
(xy 0.268073 -0.863017)
(xy 0.413247 -0.790463)
(xy 0.525487 -0.676820)
(xy 0.605721 -0.520470)
(xy 0.654872 -0.319792)
(xy 0.673869 -0.073167)
(xy 0.670114 0.120058)
(xy 0.650135 0.342081)
(xy 0.612932 0.516754)
(xy 0.555002 0.651480)
(xy 0.472837 0.753660)
(xy 0.362934 0.830697)
(xy 0.324555 0.849770)
(xy 0.204989 0.884808)
(xy 0.055656 0.900154)
(xy -0.100141 0.895335)
(xy -0.239099 0.869884)
(xy -0.264590 0.861649)
(xy -0.406259 0.782930)
(xy -0.524757 0.661680)
(xy -0.607836 0.510746)
(xy -0.613201 0.496021)
(xy -0.636589 0.411856)
(xy -0.643755 0.364231)
(xy -0.372946 0.364231)
(xy -0.360659 0.388025)
(xy -0.352953 0.394965)
(xy -0.322084 0.410909)
(xy -0.286258 0.400569)
(xy -0.232872 0.357834)
(xy -0.188096 0.315009)
(xy -0.121037 0.252266)
(xy -0.069502 0.209650)
(xy -0.048321 0.197555)
(xy -0.039970 0.223805)
(xy -0.033315 0.294678)
(xy -0.029179 0.398361)
(xy -0.028223 0.486127)
(xy -0.028223 0.774698)
(xy 0.183444 0.564444)
(xy 0.270656 0.475308)
(xy 0.340409 0.399248)
(xy 0.384273 0.345753)
(xy 0.395111 0.326149)
(xy 0.376563 0.293452)
(xy 0.327799 0.234001)
(xy 0.259140 0.160320)
(xy 0.254997 0.156109)
(xy 0.114883 0.014111)
(xy 0.252494 -0.142649)
(xy 0.390105 -0.299408)
(xy 0.182248 -0.523649)
(xy -0.025609 -0.747889)
(xy -0.026916 -0.458611)
(xy -0.029282 -0.338540)
(xy -0.034640 -0.242813)
(xy -0.042117 -0.183229)
(xy -0.048321 -0.169334)
(xy -0.077689 -0.187619)
(xy -0.133288 -0.235029)
(xy -0.188096 -0.286787)
(xy -0.257730 -0.351476)
(xy -0.302379 -0.380167)
(xy -0.334645 -0.378972)
(xy -0.352953 -0.366744)
(xy -0.371526 -0.343750)
(xy -0.367478 -0.314695)
(xy -0.335198 -0.269724)
(xy -0.269073 -0.198982)
(xy -0.241401 -0.170861)
(xy -0.167556 -0.093081)
(xy -0.112431 -0.029007)
(xy -0.085755 0.009880)
(xy -0.084667 0.014111)
(xy -0.103274 0.045440)
(xy -0.152610 0.104646)
(xy -0.222943 0.180246)
(xy -0.241401 0.199082)
(xy -0.318929 0.279614)
(xy -0.360907 0.331379)
(xy -0.372946 0.364231)
(xy -0.643755 0.364231)
(xy -0.651831 0.310560)
(xy -0.660156 0.179504)
(xy -0.662791 0.006062)
(xy -0.662789 -0.014111)
(xy -0.661490 -0.171597)
(xy -0.656854 -0.286168)
(xy -0.647125 -0.371333)
(xy -0.630543 -0.440600)
(xy -0.605350 -0.507478)
(xy -0.595725 -0.529372)
(xy -0.500697 -0.689135)
(xy -0.378025 -0.802697)
(xy -0.224107 -0.871987)
(xy -0.035341 -0.898935)
)
(stroke (width 0.000000) (type solid)) (fill solid) (layer "F.SilkS") (tstamp 4334ed97-a708-4f13-92de-630863ad37c0))
(fp_poly
(pts
(xy 0.155222 0.225777)
(xy 0.203229 0.279613)
(xy 0.225572 0.321881)
(xy 0.225777 0.324555)
(xy 0.206811 0.364483)
(xy 0.160609 0.418178)
(xy 0.155222 0.423333)
(xy 0.084666 0.489616)
(xy 0.084666 0.324555)
(xy 0.084666 0.159494)
)
(stroke (width 0.000000) (type solid)) (fill solid) (layer "F.SilkS") (tstamp 29f36ae6-04c7-437b-b68f-e92a1bcc071c))
(fp_poly
(pts
(xy 0.155222 -0.395111)
(xy 0.211843 -0.329289)
(xy 0.219724 -0.275950)
(xy 0.180282 -0.221693)
(xy 0.174166 -0.216041)
(xy 0.126554 -0.179063)
(xy 0.099454 -0.180294)
(xy 0.087337 -0.225869)
(xy 0.084666 -0.315364)
(xy 0.084666 -0.461395)
)
(stroke (width 0.000000) (type solid)) (fill solid) (layer "F.SilkS") (tstamp 29155b15-cb43-451f-bd37-a9b5e52b2a57))
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
{
"board": {
"active_layer": 39,
"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": "quack_FP.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View File

@ -0,0 +1,590 @@
{
"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": 5.0,
"height": 5.0,
"width": 5.0
},
"silk_line_width": 0.12,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.15,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.0
}
},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"meta": {
"filename": "board_design_settings.json",
"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",
"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"
},
"rule_severitieslegacy_courtyards_overlap": true,
"rule_severitieslegacy_no_courtyard_defined": false,
"rules": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.025,
"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.2,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.4,
"solder_mask_to_copper_clearance": 0.005,
"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": "quack_FP.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.25,
"via_diameter": 0.8,
"via_drill": 0.4,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "Default",
"pattern": "Earth"
}
]
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"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": "Référence"
},
"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.25,
"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": 0.0,
"text_offset_ratio": 0.08
},
"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": [
[
"7534e756-9a32-481a-8435-c05e3f5b8aa6",
"Racine"
]
],
"text_variables": {}
}

View File

@ -0,0 +1,678 @@
(kicad_sch
(version 20231120)
(generator "eeschema")
(generator_version "8.0")
(uuid "7534e756-9a32-481a-8435-c05e3f5b8aa6")
(paper "A4")
(title_block
(title "Quack front panel")
(date "2024-06-08")
(rev "1.3")
(company "Lostwave")
(comment 1 "https://68kmla.org")
(comment 2 "https://github.com/demik/quack")
)
(lib_symbols
(symbol "Connector:TestPoint"
(pin_numbers hide)
(pin_names
(offset 0.762) hide)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "TP"
(at 0 6.858 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value" "TestPoint"
(at 0 5.08 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 5.08 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 5.08 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "test point"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_keywords" "test point tp"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_fp_filters" "Pin* Test*"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "TestPoint_0_1"
(circle
(center 0 3.302)
(radius 0.762)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
)
(symbol "TestPoint_1_1"
(pin passive line
(at 0 0 90)
(length 2.54)
(name "1"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
(symbol "power:Earth"
(power)
(pin_names
(offset 0)
)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "#PWR"
(at 0 -6.35 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "Earth"
(at 0 -3.81 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Power symbol creates a global label with name \"Earth\""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_keywords" "global ground gnd"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "Earth_0_1"
(polyline
(pts
(xy -0.635 -1.905) (xy 0.635 -1.905)
)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
(polyline
(pts
(xy -0.127 -2.54) (xy 0.127 -2.54)
)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 0 -1.27) (xy 0 0)
)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 1.27 -1.27) (xy -1.27 -1.27)
)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
)
(symbol "Earth_1_1"
(pin power_in line
(at 0 0 270)
(length 0) hide
(name "Earth"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
(symbol "power:PWR_FLAG"
(power)
(pin_numbers hide)
(pin_names
(offset 0) hide)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "#FLG"
(at 0 1.905 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "PWR_FLAG"
(at 0 3.81 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Special symbol for telling ERC where power comes from"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_keywords" "flag power"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "PWR_FLAG_0_0"
(pin power_out line
(at 0 0 90)
(length 0)
(name "pwr"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
(symbol "PWR_FLAG_0_1"
(polyline
(pts
(xy 0 0) (xy 0 1.27) (xy -1.016 1.905) (xy 0 2.54) (xy 1.016 1.905) (xy 0 1.27)
)
(stroke
(width 0)
(type default)
)
(fill
(type none)
)
)
)
)
)
(junction
(at 152.4 133.35)
(diameter 0)
(color 0 0 0 0)
(uuid "588cb886-3aed-4008-85f5-29bb12c27211")
)
(junction
(at 152.4 127)
(diameter 0)
(color 0 0 0 0)
(uuid "876a34c0-4b35-434c-84f9-180914ab3295")
)
(wire
(pts
(xy 152.4 127) (xy 152.4 133.35)
)
(stroke
(width 0)
(type default)
)
(uuid "1e115d4e-11b4-48e4-9fcf-b0926c0e2b01")
)
(wire
(pts
(xy 152.4 133.35) (xy 152.4 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "4863b2be-554f-463c-918b-0b9157cdb7b0")
)
(wire
(pts
(xy 152.4 114.3) (xy 152.4 127)
)
(stroke
(width 0)
(type default)
)
(uuid "921b793d-0834-4390-a002-7d0cf725ebe9")
)
(wire
(pts
(xy 139.7 127) (xy 139.7 133.35)
)
(stroke
(width 0)
(type default)
)
(uuid "d39b0495-c31a-427c-bcbc-28572ca1afed")
)
(wire
(pts
(xy 139.7 133.35) (xy 152.4 133.35)
)
(stroke
(width 0)
(type default)
)
(uuid "de926b05-6ed2-4208-a7c2-08d4c3a9424c")
)
(symbol
(lib_id "power:PWR_FLAG")
(at 139.7 127 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(uuid "00000000-0000-0000-0000-00006245f89a")
(property "Reference" "#FLG0101"
(at 139.7 125.095 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "PWR_FLAG"
(at 139.7 122.6058 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 139.7 127 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 139.7 127 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 139.7 127 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "22171ecc-326e-45cc-ac65-32dc8fa2fa71")
)
(instances
(project "quack_FP"
(path "/7534e756-9a32-481a-8435-c05e3f5b8aa6"
(reference "#FLG0101")
(unit 1)
)
)
)
)
(symbol
(lib_id "Connector:TestPoint")
(at 152.4 114.3 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(uuid "00000000-0000-0000-0000-00006245fb73")
(property "Reference" "TP2"
(at 153.8732 111.3028 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TestPoint"
(at 153.8732 113.6142 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" "TestPoint:TestPoint_Pad_1.5x1.5mm"
(at 157.48 114.3 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 157.48 114.3 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 152.4 114.3 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "3ffffe6c-cdd1-4d4e-b774-0d3665f34ad1")
)
(instances
(project "quack_FP"
(path "/7534e756-9a32-481a-8435-c05e3f5b8aa6"
(reference "TP2")
(unit 1)
)
)
)
)
(symbol
(lib_id "Connector:TestPoint")
(at 152.4 127 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(uuid "00000000-0000-0000-0000-00006246096f")
(property "Reference" "TP1"
(at 153.8732 124.0028 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TestPoint"
(at 153.8732 126.3142 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" "TestPoint:TestPoint_Pad_1.5x1.5mm"
(at 157.48 127 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 157.48 127 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 152.4 127 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "403dc0f4-6725-4462-97a0-72546d465232")
)
(instances
(project "quack_FP"
(path "/7534e756-9a32-481a-8435-c05e3f5b8aa6"
(reference "TP1")
(unit 1)
)
)
)
)
(symbol
(lib_id "power:Earth")
(at 152.4 139.7 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(uuid "00000000-0000-0000-0000-000062460a5c")
(property "Reference" "#PWR0101"
(at 152.4 146.05 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Value" "Earth"
(at 152.4 143.51 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Footprint" ""
(at 152.4 139.7 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 152.4 139.7 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 152.4 139.7 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "1"
(uuid "05f36dbb-de48-4111-b49d-9795cf897975")
)
(instances
(project "quack_FP"
(path "/7534e756-9a32-481a-8435-c05e3f5b8aa6"
(reference "#PWR0101")
(unit 1)
)
)
)
)
(sheet_instances
(path "/"
(page "1")
)
)
)

Binary file not shown.

BIN
Images/Preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

BIN
Images/board.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

165
Misc/esp_8_bit/README.md Normal file
View File

@ -0,0 +1,165 @@
# Quack X esp_8_bit
This is a crossover between two projects, that allow you to mod a Quack board to use with esp_8_bit firmware.
The result is a two players small box that can contains a few NES games and fit near your TV.
![quack+esp_8_bit](https://github.com/demik/quack/blob/master/Misc/esp_8_bit/quack%2Besp_8_bit.jpeg)
this is unsupported (outside of the quack project) and the parent project (esp_8_bit) is likely dead so this will likely not receive any updates or fixes
- parent project: [rossumur/esp_8_bit](https://github.com/rossumur/esp_8_bit)
- used patches from: [CornN64/esp_8_bit](https://github.com/CornN64/esp_8_bit)
it supports two players (one hardwired, either NES or SNES) and one Wiimote based. Unlike other builds, the hardwired connector runs at +5V (uses both level converters of the quack board), which allow the use of retro wireless adapters
schematic:
```
-----------
| |
| 25 |----------------------------------> video out
| |
| | ------------
| 2 |---| LVC 4245 |----/\/\/\/----|---> audio out
| | ------------ 1kΩ |
| | ---
| | --- 10nF
| ESP32 | |
| | v gnd
| |
| | ------------
| 4 |---| LVC 2T45 |---< NES (or SNES) controller DATA
| | ------------
| | ------------
| 12 |---| LVC 4245 |---> NES (or SNES) controller CLOCK
| 14 |---| LVC 4245 |---> NES (or SNES) controller LATCH
| | ------------
| | 5v <--> NES (or SNES) controller VCC
| | gnd <--> NES (or SNES) controller GND
| |
-----------
NES ___
DATA |o o| NC
LATCH |o o| NC
CLOCK |o o/ 5V
GND |o_/
SNES _
5V |o|
CLOCK |o|
LATCH |o|
DATA |o|
|-|
NC |o|
NC |o|
GND |o|
-
```
## Software
Since the esp_8_bit source code is old, you will need an old IDE (Arduino 1.8.14 with esp board 1.0.4)
there is a couple of patches in this directory that you will need to patch on top of [rossumur/esp_8_bit](https://github.com/rossumur/esp_8_bit)
- base.diff integrates patches from [CornN64/esp_8_bit](https://github.com/CornN64/esp_8_bit) and enable quack specific features (pinout, LEDs, DIP switches)
- controller.diff removes IR support, clean code and adds (S)NES controller support
```
> git clone https://github.com/rossumur/esp_8_bit.git
Cloning into 'esp_8_bit'...
remote: Enumerating objects: 345, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 345 (delta 5), reused 5 (delta 5), pack-reused 330 (from 1)
Receiving objects: 100% (345/345), 8.01 MiB | 8.04 MiB/s, done.
Resolving deltas: 100% (108/108), done.
> cd esp_8_bit
> patch -p1 < ../base.diff
patching file README.md
patching file esp_8_bit.ino
patching file 'src/emu.cpp'
patching file 'src/emu.h'
patching file 'src/emu_nofrendo.cpp'
patching file 'src/gui.cpp'
patching file 'src/hid_server/hid_server.cpp'
patching file 'src/video_out.h'
> patch -p1 < ../controller.diff
patching file 'src/ir_input.h'
patching file 'src/nintendo.h'
```
To avoid [this issue](https://github.com/rossumur/esp_8_bit/issues/19) you may want to change the partition layout as well. Check the solution into that issue, and also the boards.txt and quack_8_bit.csv files if needed
###
## Hardware
This mod is compatible with board revision 1.3 and 1.4
### PCB mods
hardware modifications needed
- remove Q1
- remove FB9
- remove D1
- bridge R1 or replace by 0Ω
- remove the Mini DIN-4 connector
Under the board:
- connect a wire from DE-9 pin 6 to D1 pin (+) on top of the board
- connect a wire from DE-9 pin 8 to Mini DIN-4 pin 1
![bodge](https://github.com/demik/quack/blob/master/Misc/esp_8_bit/bodge.png)
### Adapter cable
You will need a NES or SNES extension cable, one triple RCA video cable, one USB cable (charge only is OK) and one DE-9 solderable connector
RC filtering circuit will be integrated into the connector. pinout is a follow (board side)
```
_____________________
/ \
\ 1 2 3 4 5 /
\ 6 7 8 9 /
\_________________/
```
| Pin | Connection | Notes |
|---|---|---|
| 1 | Ground | power ground & RCA shields here |
| 2 | +5V | USB +5V and (S)NES +5V connector go here |
| 3 | Ground | (S)NES ground + 10nF capacitor ground |
| 4 | NC | unused |
| 5 | Clock | (S)NES controller clock |
| 6 | Video | composite video out, yellow RCA |
| 7 | Sound | one pin of the 1kΩ resistor |
| 8 | Data | (S)NES controller data |
| 9 | Latch | (S)NES controller data |
| R | Resistor | solder capacitor on the other resistor pin & white + red RCA (+) wires |
isolate everything with hot glue or better
## Usage
Nothing different than the parent project as far as emulation goes
### Switches
Quack set of dip switches allows for the following settings
- ADB Host: (S)NES controller is player A, Wiimote player B or the other way around
- Bluetooth Disable: Kill BT Operation if not needed. More stable / less noise. Use with ADB Host to ON
- Flash: flash mode (GPIO0 to GND)
### LED
PCB LEDs are usable and do have basic functionality
- green: init done, board powered up
- yellow: valid (S)NES controller input received
- red: embedded flash operation in progress

875
Misc/esp_8_bit/base.diff Normal file
View File

@ -0,0 +1,875 @@
diff --git a/README.md b/README.md
index c548654..7d33f55 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,59 @@
# **ESP_8_BIT:** Atari 8 bit computers, NES and SMS game consoles on your TV with nothing more than a ESP32 and a sense of nostalgia
-## Supports NTSC/PAL color composite video output, Bluetooth Classic or IR keyboards and joysticks; just the thing when we could all use a little distraction
+## Supports NTSC/PAL color composite video output, Bluetooth Classic keyboards and joysticks; just the thing when we could all use a little distraction
+## Supports classic NES (or SNES) controller hardwired to the ESP32. SELECT + LEFT to access file menu. SELECT + START -> reset
![ESP_8_BIT](img/esp8bit.jpg)
-**ESP_8_BIT** is designed to run on the ESP32 within the Arduino IDE framework. See it in action on [Youtube](https://www.youtube.com/watch?v=qFRkfeuTUrU). Schematic is pretty simple:
+**ESP_8_BIT** is designed to run on the ESP32 within the Arduino IDE framework. See it in action on [Youtube](https://www.youtube.com/watch?v=qFRkfeuTUrU). Schematic is simple, including LVC buffers (3.3 to 5V and vice versa):
```
-----------
| |
- | 25 |------------------> video out
+ | 25 |----------------------------------> video out
| |
- | 18 |---/\/\/\/----|---> audio out
- | | 1k |
- | | ---
- | ESP32 | --- 10nf
- | | |
- | | v gnd
+ | | ------------
+ | 2 |---| LVC 4245 |----/\/\/\/----|---> audio out
+ | | ------------ 1kΩ |
+ | | ---
+ | | --- 10nF
+ | ESP32 | |
+ | | v gnd
+ | |
+ | | ------------
+ | 4 |---| LVC 2T45 |---< NES (or SNES) controller DATA
+ | | ------------
+ | | ------------
+ | 12 |---| LVC 4245 |---> NES (or SNES) controller CLOCK
+ | 14 |---| LVC 4245 |---> NES (or SNES) controller LATCH
+ | | ------------
+ | | 5v <--> NES (or SNES) controller VCC
+ | | gnd <--> NES (or SNES) controller GND
| |
- | | 3.3v <--+-+ IR Receiver
- | | gnd <--| ) TSOP4838 etc.
- | 0 |-------------+-+ (Optional)
-----------
+
+NES ___
+ DATA |o o| NC
+ LATCH |o o| NC
+ CLOCK |o o/ 5V
+ GND |o_/
+
+SNES _
+ 5V |o|
+ CLOCK |o|
+ LATCH |o|
+ DATA |o|
+ |-|
+ NC |o|
+ NC |o|
+ GND |o|
+ -
+
```
-Audio is on pin 18 by default but can be remapped.
+Audio is on pin 2 for Quack compatibility
+
+Enable either NES or SNES controller with
+#define NES_CONTROLLER or #define SNES_CONTROLLER (not both) in nintendo.h file
Before you compile the sketch you have 2 choices:
```
@@ -181,7 +211,7 @@ You will want to add a simple rc filter to the output pin of either PWM or PDM t
## Bringing it Together
-The audio/video system uses a double buffered I2S DMA to send video data line by line to the DAC. The interrupt keeps time at the line rate (15720hz for NTSC, 15600hz for PAL). A single audio sample is fed to the LED PWM and a single line of video is converted from index color to phase/amplitude at each interrupt. The IR input pin is scanned and the various IR state machines advanced on changes.
+The audio/video system uses a double buffered I2S DMA to send video data line by line to the DAC. The interrupt keeps time at the line rate (15720hz for NTSC, 15600hz for PAL). A single audio sample is fed to the LED PWM and a single line of video is converted from index color to phase/amplitude at each interrupt. The hard wired input pin is scanned as well
This a/v pump is fed by the emulator running asynchronously producing frames of video and audio. The emulator may be running on a different core and may occasionally take longer than a frame time to produce a frame (SPI paging / FS etc). The interrupt driven pump won't care, it just keeps emitting the last frame.
@@ -195,18 +225,16 @@ How do you fit a 512k game cartridge into a device that has 384k of RAM, nearly
Short answer is you don't. When a large cart is selected it gets copied into `CrapFS`; an aptly named simple filesystem that takes over the `app1` partition at first boot. One copied `CrapFS` uses `esp_partition_mmap()` to map the part of the partition occupied by the cartridge into the data space of the CPU.
-> We are using an Audio PLL to create color composite video and a LED PWM peripheral to make the audio, a Bluetooth radio or a single GPIO pin for the joysticks and keyboard, and gpio for the IR even though there is a perfectly good peripheral for that. We are using virtual memory on a microcontroller. And it all fits on a single core. Oh how I love the ESP32.
+> We are using an Audio PLL to create color composite video and a LED PWM peripheral to make the audio, a Bluetooth radio or a single GPIO pin for the joysticks and keyboard, and gpio for the classic controller. We are using virtual memory on a microcontroller. And it all fits on a single core. Oh how I love the ESP32.
# Keyboards & Controllers
-**ESP_8_BIT** supports Bluetooth Classic/EDR keyboards and WiiMotes along with an variety of IR keyboards and joysticks.
+**ESP_8_BIT** supports Bluetooth Classic/EDR keyboards and WiiMotes along with either a classic NES or SNES controller
**On boot the software searches for new Bluetooth devices for 5 seconds**; if the device is in pairing mode the software should find it and display its name at the bottom of the screen. Some keyboards will require you to enter a "0000" to establish the connection the first time. WiiMotes should automatically pair and reconnect. WiiMote Classic controllers are also supported.
![Input Devices](img/inputdevices.jpg)
-A number of IR input devices are supported if to add a optional IR receiver (TSOP38238, TSOP4838 or equivalent) to pin 0. The IR Wireless Controllers from Atari Flashback 4 work well and have that classic Atari joystick feel. Retron IR controllers are supported as are WebTV keyboards that come in various guises: WebTV, MsnTV, Displayer, UltimateTV etc. They all should work just fine. A few places have the nice Philips variant new for $11.95 w/ free shipping (search for 'SWK-8630'). If you made a [ZorkDuino](https://hackaday.com/2014/04/30/the-zorkduino/) you will have one of these.
-
# Time to Play
If you would like to upload your own media copy them into the appropriate subfolder named for each of the emulators in the data folder. Note that the SPIFFS filesystem is fussy about filenames, keep them short, no spaces allowed. Use '[ESP32 Sketch Data Upload](https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/)' from the 'Tools' menu to copy a prepared data folder to ESP32.
diff --git a/data/atari800/atari_robot.xex b/data/atari800/atari_robot.xex
deleted file mode 100644
index b27fb91..0000000
Binary files a/data/atari800/atari_robot.xex and /dev/null differ
diff --git a/data/atari800/balls_forever.xex b/data/atari800/balls_forever.xex
deleted file mode 100644
index 7b414a0..0000000
Binary files a/data/atari800/balls_forever.xex and /dev/null differ
diff --git a/data/atari800/boink.xex b/data/atari800/boink.xex
deleted file mode 100644
index 3bad906..0000000
Binary files a/data/atari800/boink.xex and /dev/null differ
diff --git a/data/atari800/callisto.xex b/data/atari800/callisto.xex
deleted file mode 100644
index f6b42e1..0000000
Binary files a/data/atari800/callisto.xex and /dev/null differ
diff --git a/data/atari800/dos20.atr b/data/atari800/dos20.atr
deleted file mode 100644
index 2685d6d..0000000
Binary files a/data/atari800/dos20.atr and /dev/null differ
diff --git a/data/atari800/gravity_worms.atr b/data/atari800/gravity_worms.atr
deleted file mode 100755
index 645ca33..0000000
Binary files a/data/atari800/gravity_worms.atr and /dev/null differ
diff --git a/data/atari800/gtia_blast.xex b/data/atari800/gtia_blast.xex
deleted file mode 100644
index 964f0d6..0000000
Binary files a/data/atari800/gtia_blast.xex and /dev/null differ
diff --git a/data/atari800/janes_program.xex b/data/atari800/janes_program.xex
deleted file mode 100644
index 4fbbc12..0000000
Binary files a/data/atari800/janes_program.xex and /dev/null differ
diff --git a/data/atari800/maze.xex b/data/atari800/maze.xex
deleted file mode 100755
index 265191c..0000000
Binary files a/data/atari800/maze.xex and /dev/null differ
diff --git a/data/atari800/mini_zork.atr b/data/atari800/mini_zork.atr
deleted file mode 100644
index ed29c1a..0000000
Binary files a/data/atari800/mini_zork.atr and /dev/null differ
diff --git a/data/atari800/more.xex b/data/atari800/more.xex
deleted file mode 100644
index 835b1ca..0000000
Binary files a/data/atari800/more.xex and /dev/null differ
diff --git a/data/atari800/numen_rubik.atr b/data/atari800/numen_rubik.atr
deleted file mode 100644
index c198cdf..0000000
Binary files a/data/atari800/numen_rubik.atr and /dev/null differ
diff --git a/data/atari800/paperweight.xex b/data/atari800/paperweight.xex
deleted file mode 100644
index fbd30c1..0000000
Binary files a/data/atari800/paperweight.xex and /dev/null differ
diff --git a/data/atari800/raymaze_2000_ntsc.xex b/data/atari800/raymaze_2000_ntsc.xex
deleted file mode 100755
index 5d8f45f..0000000
Binary files a/data/atari800/raymaze_2000_ntsc.xex and /dev/null differ
diff --git a/data/atari800/runner_bear.xex b/data/atari800/runner_bear.xex
deleted file mode 100644
index 3fb35b3..0000000
Binary files a/data/atari800/runner_bear.xex and /dev/null differ
diff --git a/data/atari800/star_raiders_II.atr b/data/atari800/star_raiders_II.atr
deleted file mode 100644
index 05ba6a7..0000000
Binary files a/data/atari800/star_raiders_II.atr and /dev/null differ
diff --git a/data/atari800/wasteland.atr b/data/atari800/wasteland.atr
deleted file mode 100755
index 533b441..0000000
Binary files a/data/atari800/wasteland.atr and /dev/null differ
diff --git a/data/atari800/yoomp_nt.xex b/data/atari800/yoomp_nt.xex
deleted file mode 100644
index 3b20897..0000000
Binary files a/data/atari800/yoomp_nt.xex and /dev/null differ
diff --git a/data/nofrendo/sokoban.nes b/data/nofrendo/sokoban.nes
deleted file mode 100755
index 4628512..0000000
Binary files a/data/nofrendo/sokoban.nes and /dev/null differ
diff --git a/data/nofrendo/tokumaru_raycast.nes b/data/nofrendo/tokumaru_raycast.nes
deleted file mode 100644
index 7e7f9fc..0000000
Binary files a/data/nofrendo/tokumaru_raycast.nes and /dev/null differ
diff --git a/data/smsplus/baraburuu.sms b/data/smsplus/baraburuu.sms
deleted file mode 100755
index 2683e0f..0000000
Binary files a/data/smsplus/baraburuu.sms and /dev/null differ
diff --git a/data/smsplus/ftrack.gg b/data/smsplus/ftrack.gg
deleted file mode 100644
index e58a12f..0000000
Binary files a/data/smsplus/ftrack.gg and /dev/null differ
diff --git a/data/smsplus/nanowars8k.sms b/data/smsplus/nanowars8k.sms
deleted file mode 100644
index 7d7c38a..0000000
Binary files a/data/smsplus/nanowars8k.sms and /dev/null differ
diff --git a/esp_8_bit.ino b/esp_8_bit.ino
index 2256665..fa45f80 100644
--- a/esp_8_bit.ino
+++ b/esp_8_bit.ino
@@ -18,7 +18,8 @@
#include "esp_int_wdt.h"
#include "esp_spiffs.h"
-#define PERF // some stats about where we spend our time
+#include "soc/efuse_reg.h"
+#undef PERF // some stats about where we spend our time
#include "src/emu.h"
#include "src/video_out.h"
@@ -27,10 +28,10 @@
// Supports NTSC/PAL composite video, Bluetooth Classic keyboards and joysticks
// Choose one of the video standards: PAL,NTSC
-#define VIDEO_STANDARD NTSC
+#define VIDEO_STANDARD PAL
// Choose one of the following emulators: EMU_NES,EMU_SMS,EMU_ATARI
-#define EMULATOR EMU_ATARI
+#define EMULATOR EMU_NES
// Many emus work fine on a single core (S2), file system access can cause a little flickering
// #define SINGLE_CORE
@@ -42,9 +43,29 @@
// Folders will be auto-populated on first launch with a built in selection of sample media.
// Use 'ESP32 Sketch Data Upload' from the 'Tools' menu to copy a prepared data folder to ESP32
+// Quack hardware DIP switch block
+// on ESP32-PICO-D4, the following pins are used for connecting the
+// embedded flash:
+// - CLK (GPIO6)
+// - SD0 (GPIO7)
+// - SD1 (GPIO8)
+// - CMD (GPIO11)
+// - GPIO16
+// - GPIO17
+#include "driver/gpio.h"
+#define GPIO_ADB 4 // Used for NES or SNES classic controller data line
+#define GPIO_DIR 19 // Select data in or out
+#define GPIO_OE 22 // Enable 3.3V to 5V buffer
+#define GPIO_ADBSRC 32 // USed to select classic controller player A (0, pulled down) or B (1)
+#define GPIO_BTOFF 33 // Used to disable bluetooth
+#define GPIO_GREENLED 21
+#undef GPIO_BLUELED 25 // Has to be desoldered, and resistor R1 bridged (video out)
+#define GPIO_YELLOWLED 26
+#define GPIO_REDLED 27
+
// Create a new emulator, messy ifdefs ensure that only one links at a time
Emu* NewEmulator()
-{
+{
#if (EMULATOR==EMU_NES)
return NewNofrendo(VIDEO_STANDARD);
#endif
@@ -88,13 +109,16 @@ void emu_task(void* arg)
printf("emu_task %s running on core %d at %dmhz\n",
_emu->name.c_str(),xPortGetCoreID(),rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()));
emu_init();
+
+ /* Quack: enable green led after init */
+ gpio_set_level((gpio_num_t)GPIO_GREENLED, 1);
for (;;)
emu_loop();
}
esp_err_t mount_filesystem()
{
- printf("\n\n\nesp_8_bit\n\nmounting spiffs (will take ~15 seconds if formatting for the first time)....\n");
+ printf("\n\n\nesp_8_bit on Quack\n\nmounting spiffs (will take ~15 seconds if formatting for the first time)....\n");
uint32_t t = millis();
esp_vfs_spiffs_conf_t conf = {
.base_path = "",
@@ -111,11 +135,49 @@ esp_err_t mount_filesystem()
}
void setup()
-{
- rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M);
+{
+ // from CornN64 commit 4e2e5d2
+ int silicon_version = (REG_READ(EFUSE_BLK0_RDATA3_REG) >> 15) & 1;
+ if (silicon_version == 0)
+ printf("Warning this revision of the chip has an issue with the APLL and will not work properly!\n");
+
+ // setup Quack buffer, GPIOs and LEDs here
+ gpio_reset_pin((gpio_num_t)GPIO_ADBSRC);
+ gpio_set_direction((gpio_num_t)GPIO_ADBSRC, GPIO_MODE_INPUT);
+ gpio_reset_pin((gpio_num_t)GPIO_BTOFF);
+ gpio_set_direction((gpio_num_t)GPIO_BTOFF, GPIO_MODE_INPUT);
+ gpio_reset_pin((gpio_num_t)GPIO_DIR);
+ gpio_reset_pin((gpio_num_t)GPIO_OE);
+ gpio_set_direction((gpio_num_t)GPIO_DIR, GPIO_MODE_OUTPUT);
+ gpio_set_direction((gpio_num_t)GPIO_OE, GPIO_MODE_OUTPUT);
+ gpio_set_direction((gpio_num_t)GPIO_ADB, GPIO_MODE_INPUT);
+ gpio_set_level((gpio_num_t)GPIO_DIR, 0);
+ gpio_reset_pin((gpio_num_t)GPIO_GREENLED);
+ gpio_reset_pin((gpio_num_t)GPIO_YELLOWLED);
+ gpio_reset_pin((gpio_num_t)GPIO_REDLED);
+ gpio_set_direction((gpio_num_t)GPIO_GREENLED, GPIO_MODE_OUTPUT);
+ gpio_set_direction((gpio_num_t)GPIO_YELLOWLED, GPIO_MODE_OUTPUT);
+ gpio_set_direction((gpio_num_t)GPIO_REDLED, GPIO_MODE_OUTPUT);
+
+ rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M);
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
mount_filesystem(); // mount the filesystem!
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
_emu = NewEmulator(); // create the emulator!
- hid_init("emu32"); // bluetooth hid on core 1!
+
+ // Quack hardwired player A or B
+ if (gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 0)
+ printf("Hardwired controller is player 1\n");
+ else
+ printf("Hardwired controller is player 2\n");
+
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 0) {
+ printf("Bluetooth is OFF\n");
+ }
+ else {
+ printf("Bluetooth is ON\n");
+ hid_init("emu32"); // bluetooth hid on core 1!
+ }
#ifdef SINGLE_CORE
emu_init();
@@ -132,10 +194,10 @@ void perf()
if (_drawn >= _next) {
float elapsed_us = 120*1000000/(_emu->standard ? 60 : 50);
_next = _drawn + 120;
-
+
printf("frame_time:%d drawn:%d displayed:%d blit_ticks:%d->%d, isr time:%2.2f%%\n",
_frame_time/240,_drawn,_frame_counter,_blit_ticks_min,_blit_ticks_max,(_isr_us*100)/elapsed_us);
-
+
_blit_ticks_min = 0xFFFFFFFF;
_blit_ticks_max = 0;
_isr_us = 0;
@@ -147,7 +209,7 @@ void perf(){};
// this loop always runs on app_core (1).
void loop()
-{
+{
#ifdef SINGLE_CORE
emu_loop();
#else
@@ -162,9 +224,10 @@ void loop()
}
}
#endif
-
+
// update the bluetooth edr/hid stack
- hid_update();
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 1)
+ hid_update();
// Dump some stats
perf();
diff --git a/src/emu.cpp b/src/emu.cpp
index 068f6f6..f3efef4 100644
--- a/src/emu.cpp
+++ b/src/emu.cpp
@@ -21,12 +21,15 @@ using namespace std;
// Map files into memory for carts bigger than physical RAM
// Handly for NES/SMS carts
// Uses app1 as a cache with a crappy FS on top - default arduino config gives 1280k
+// if using optional quack optimised partition sheme, can go up to 1664k
#ifdef ESP_PLATFORM
#include <esp_spi_flash.h>
#include <esp_attr.h>
#include <esp_partition.h>
#include "rom/miniz.h"
+#include "driver/gpio.h"
+#define GPIO_REDLED 27
// only map 1 file at a time
spi_flash_mmap_handle_t _file_handle = 0;
@@ -114,31 +117,38 @@ public:
void reformat()
{
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
esp_partition_erase_range(_part,0,_part->size); // erase entire partition
memset(_buf,0xFF,sizeof(DIR_BLOCK_SIZE)); // only use the first 16k of first block
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
}
uint8_t* mmap(const FlashFile* file)
{
if (!file)
return 0;
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
printf("CrapFS::mmap mapping %s offset:%08X len:%d\n",file->name,file->offset,file->len);
void* data = 0;
if (esp_partition_mmap(_part, file->offset, file->len, SPI_FLASH_MMAP_DATA, (const void**)&data, &_file_handle) == 0)
{
printf("CrapFS::mmap mapped to %08X\n",data);
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
return (uint8_t*)data;
}
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
return 0;
}
// see if the file exists
FlashFile* find(const std::string& path)
{
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
for (int i = 0; i < _count; i++) {
if (_dir[i].sig == FSIG && strcmp(_dir[i].name,path.c_str()) == 0)
return _dir + i;
}
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
return NULL;
}
@@ -147,6 +157,7 @@ public:
FILE *f = fopen(path.c_str(), "rb");
if (!f)
return -1;
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
#define BUF_SIZE 4096
uint8_t* buf = new uint8_t[BUF_SIZE];
esp_err_t err;
@@ -164,6 +175,7 @@ public:
}
fclose(f);
delete buf;
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
return err;
}
@@ -173,6 +185,7 @@ public:
uint32_t start = 0x10000;
for (int i = 0; i < _count; i++) {
if (_dir[i].sig != FSIG) {
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
_dir[i].sig = FSIG;
_dir[i].offset = start;
_dir[i].len = len; //
@@ -185,6 +198,7 @@ public:
printf("CrapFS::create dir write failed %d\n",err);
return NULL;
}
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
if (copy(path,start,len))
return NULL;
return _dir+i;
diff --git a/src/emu.h b/src/emu.h
index f8d024b..c58ca3b 100644
--- a/src/emu.h
+++ b/src/emu.h
@@ -148,7 +148,7 @@ extern "C" FILE* mkfile(const char* path);
extern "C" int unpack(const char* dst_path, const uint8_t* d, int len);
void audio_write_16(const int16_t* s, int len, int channels);
-int get_hid_ir(uint8_t* dst);
+extern "C" int get_hid_nintendo(uint8_t* dst);
uint32_t generic_map(uint32_t m, const uint32_t* target);
Emu* NewAtari800(int ntsc = 1);
diff --git a/src/emu_nofrendo.cpp b/src/emu_nofrendo.cpp
index df905b2..aae1268 100644
--- a/src/emu_nofrendo.cpp
+++ b/src/emu_nofrendo.cpp
@@ -15,6 +15,9 @@
** SOFTWARE.
*/
+/* from CornN64 commit 063b08f7479beb147960c99396b09bdb0b8746b4 */
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
#include "emu.h"
#include "media.h"
@@ -24,6 +27,13 @@ extern "C" {
};
#include "math.h"
+//#define CALC_PALETTES
+//recalculated color samples from CornN64 commit a5ea467c4f39ff4a01c5639e527a88eae78080fc */
+// Choose either rossumur OG palettes or CornN64 ones below
+// OG black is a little washed on my TFT Samsung but colors are vivid
+// Second one palettes black is good but picture is too dark
+// Third is way better but still a little too dark on dark tones on my old TFT
+
using namespace std;
// https://wiki.nesdev.com/w/index.php/NTSC_video
@@ -40,7 +50,8 @@ uint32_t nes_3_phase[64] = {
0x45454500,0x423B4200,0x403B4400,0x3D3D4500,0x3B404400,0x3B424200,0x3B444000,0x3D453D00,
0x40443B00,0x42423B00,0x44403B00,0x453D3D00,0x443B4000,0x39393900,0x17171700,0x17171700,
};
-uint32_t nes_4_phase[64] = {
+
+/*uint32_t nes_4_phase[64] = {
0x2C2C2C2C,0x241D1F26,0x221D2227,0x1F1D2426,0x1D1F2624,0x1D222722,0x1D24261F,0x1F26241D,
0x2227221D,0x24261F1D,0x26241D1F,0x27221D22,0x261F1D24,0x14141414,0x14141414,0x14141414,
0x38383838,0x2C25272E,0x2A252A2F,0x27252C2E,0x25272E2C,0x252A2F2A,0x252C2E27,0x272E2C25,
@@ -49,11 +60,33 @@ uint32_t nes_4_phase[64] = {
0x373C3732,0x3A3C3533,0x3C3A3335,0x3C373237,0x3C35333A,0x2B2B2B2B,0x16161616,0x16161616,
0x45454545,0x423B3D44,0x403B4045,0x3D3B4244,0x3B3D4442,0x3B404540,0x3B42443D,0x3D44423B,
0x4045403B,0x42443D3B,0x44423B3D,0x45403B40,0x443D3B42,0x39393939,0x17171717,0x17171717,
-};
+};*/
+
+//RGB palette from http://drag.wootest.net/misc/palgen.html -> YUV -> QAM on color carrier -> 4 phases sampled
+/*uint32_t nes_4_phase[64] = {
+ 0x24242424,0x1A0E1420,0x1C0C1424,0x1B0D1523,0x15142020,0x0E1C2719,0x0D1D2717,0x0F1B2216,
+ 0x13161815,0x19181415,0x1C1A1516,0x1C1A1516,0x1B16141A,0x14141414,0x14141414,0x14141414,
+ 0x39393939,0x30151631,0x2B0D1735,0x1F122835,0x12193730,0x0A234026,0x072C411C,0x112C381C,
+ 0x1D2B291C,0x2A27181B,0x2E29161B,0x3028161E,0x321F1629,0x14141414,0x14141414,0x14141414,
+ 0x50505050,0x47241E41,0x39222940,0x30253742,0x29304D45,0x26374D3C,0x243F4D32,0x23484D29,
+ 0x2D484126,0x3A452F25,0x463E2027,0x4E341831,0x502C183C,0x1E1E1E1E,0x14141414,0x14141414,
+ 0x50505050,0x4C3D3A4A,0x463B3E49,0x413C4549,0x3F434F4B,0x3E454F48,0x3D494F43,0x3D4D4F3F,
+ 0x414E4A3D,0x464C433D,0x4B483C3F,0x4F443843,0x50413848,0x3B3B3B3B,0x14141414,0x14141414
+};*/
+uint32_t nes_4_phase[64] = {
+ 0x27272727,0x1B16191E,0x1D151921,0x1C151920,0x1A1A1F20,0x171E231C,0x171E231C,0x171C201A,
+ 0x17191A18,0x1B1A1819,0x1D1C191A,0x1D1C191A,0x1C1A191C,0x17171717,0x17171717,0x17171717,
+ 0x3A3A3A3A,0x2C1F1F2C,0x281A1E2D,0x231D282E,0x1E212F2C,0x1A263428,0x192A3423,0x1D2A3023,
+ 0x23292822,0x28261F20,0x2A281F21,0x2B271F23,0x2D241F28,0x17171717,0x17171717,0x17171717,
+ 0x50505050,0x3E2D2A3B,0x362B2F3A,0x332E373C,0x33374441,0x313A443C,0x303D4437,0x2F414332,
+ 0x34413D30,0x393E342E,0x3D3A2B2F,0x41352733,0x43312839,0x21212121,0x17171717,0x17171717,
+ 0x50505050,0x48414047,0x443F4146,0x43404447,0x44454B4A,0x43464B47,0x42484B45,0x424A4B43,
+ 0x444A4842,0x46494442,0x48474142,0x4A453F44,0x4A433F46,0x3C3C3C3C,0x17171717,0x17171717
+};
// PAL yuyv table, must be in RAM
-uint32_t _nes_yuv_4_phase_pal[] = {
+/*uint32_t _nes_yuv_4_phase_pal[] = {
0x31313131,0x2D21202B,0x2720252D,0x21212B2C,0x1D23302A,0x1B263127,0x1C293023,0x202B2D22,
0x262B2722,0x2C2B2122,0x2F2B1E23,0x31291F27,0x30251F2A,0x18181818,0x19191919,0x19191919,
0x3D3D3D3D,0x34292833,0x2F282D34,0x29283334,0x252B3732,0x232E392E,0x2431382B,0x28333429,
@@ -71,8 +104,51 @@ uint32_t _nes_yuv_4_phase_pal[] = {
0x3D423B35,0x37414136,0x323F4538,0x313C473B,0x3239463F,0x2F2F2F2F,0x1A1A1A1A,0x1A1A1A1A,
0x49494949,0x3D414845,0x43404245,0x463F3D44,0x453D3B43,0x453E3B42,0x45423B3F,0x46473E3E,
0x454A433E,0x3E48463D,0x3943483E,0x38404A42,0x39404B44,0x3E3E3E3E,0x1B1B1B1B,0x1B1B1B1B,
+};/*
+
+//RGB palette from http://drag.wootest.net/misc/palgen.html -> YUV -> QAM on color carrier -> 4 phases sampled
+/*uint32_t _nes_yuv_4_phase_pal[] = {
+ 0x1B1B1B1B,0x110B0E14,0x120A0F16,0x120B0F16,0x0F0F1515,0x0C131912,0x0C141911,0x0C121610,
+ 0x0D0F0F0E,0x11100E0F,0x12110F0F,0x12110F0F,0x120F0E11,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
+ 0x2C2C2C2C,0x20131321,0x1D0E1322,0x17111C23,0x12152420,0x0D1A291C,0x0C1E2916,0x111F2417,
+ 0x171D1C16,0x1C1B1315,0x1F1C1315,0x201C1317,0x2118131D,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
+ 0x3F3F3F3F,0x31201C2E,0x291E212D,0x2520292E,0x24283633,0x232B362D,0x212F3628,0x21333623,
+ 0x25332F22,0x2B302620,0x302C1D21,0x34271926,0x36231A2C,0x15151515,0x0D0D0D0D,0x0D0D0D0D,
+ 0x3F3F3F3F,0x39313038,0x35303137,0x33313537,0x34353B3A,0x33373B38,0x32383B35,0x323A3B33,
+ 0x343A3832,0x36393432,0x39373133,0x3A352F34,0x3B332F37,0x2E2E2E2E,0x0D0D0D0D,0x0D0D0D0D,
+
+ 0x1B1B1B1B,0x0E0B1114,0x0F0A1216,0x0F0B1216,0x150F0F15,0x19130C12,0x19140C11,0x16120C10,
+ 0x0F0F0D0E,0x0E10110F,0x0F11120F,0x0F11120F,0x0E0F1211,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
+ 0x2C2C2C2C,0x13132021,0x130E1D22,0x1C111723,0x24151220,0x291A0D1C,0x291E0C16,0x241F1117,
+ 0x1C1D1716,0x131B1C15,0x131C1F15,0x131C2017,0x1318211D,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
+ 0x3F3F3F3F,0x1C20312E,0x211E292D,0x2920252E,0x36282433,0x362B232D,0x362F2128,0x36332123,
+ 0x2F332522,0x26302B20,0x1D2C3021,0x19273426,0x1A23362C,0x15151515,0x0D0D0D0D,0x0D0D0D0D,
+ 0x3F3F3F3F,0x30313938,0x31303537,0x35313337,0x3B35343A,0x3B373338,0x3B383235,0x3B3A3233,
+ 0x383A3432,0x34393632,0x31373933,0x2F353A34,0x2F333B37,0x2E2E2E2E,0x0D0D0D0D,0x0D0D0D0D
+};*/
+
+uint32_t _nes_yuv_4_phase_pal[] = {
+ 0x26262626,0x1A10151F,0x1B0E1523,0x1B0F1622,0x16161F20,0x101C261A,0x101E2618,0x111B2117,
+ 0x14161815,0x19181516,0x1C1A1517,0x1C1A1517,0x1B16151A,0x14141414,0x14141414,0x14141414,
+ 0x3C3C3C3C,0x30181931,0x2B111933,0x21152834,0x161C3630,0x0F253E28,0x0D2C3E1F,0x152D371F,
+ 0x202B291E,0x2A281A1C,0x2E29191D,0x2F281920,0x3121192A,0x14141414,0x14141414,0x14141414,
+ 0x55555555,0x47292442,0x3B262C41,0x332A3942,0x2E354E47,0x2C3B4E3F,0x2A414D36,0x29494D2E,
+ 0x3249422B,0x3C453329,0x463F252C,0x4D371E34,0x4F2F1E3E,0x1F1F1F1F,0x14141414,0x14141414,
+ 0x55555555,0x4F42404D,0x493F424C,0x4541484C,0x4548524F,0x434A524C,0x424C5248,0x43505245,
+ 0x46514D42,0x4A4F4742,0x4E4C4144,0x52483E47,0x52453E4B,0x3F3F3F3F,0x14141414,0x14141414,
+
+ 0x26262626,0x15101A1F,0x150E1B23,0x160F1B22,0x1F161620,0x261C101A,0x261E1018,0x211B1117,
+ 0x18161415,0x15181916,0x151A1C17,0x151A1C17,0x15161B1A,0x14141414,0x14141414,0x14141414,
+ 0x3C3C3C3C,0x19183031,0x19112B33,0x28152134,0x361C1630,0x3E250F28,0x3E2C0D1F,0x372D151F,
+ 0x292B201E,0x1A282A1C,0x19292E1D,0x19282F20,0x1921312A,0x14141414,0x14141414,0x14141414,
+ 0x55555555,0x24294742,0x2C263B41,0x392A3342,0x4E352E47,0x4E3B2C3F,0x4D412A36,0x4D49292E,
+ 0x4249322B,0x33453C29,0x253F462C,0x1E374D34,0x1E2F4F3E,0x1F1F1F1F,0x14141414,0x14141414,
+ 0x55555555,0x40424F4D,0x423F494C,0x4841454C,0x5248454F,0x524A434C,0x524C4248,0x52504345,
+ 0x4D514642,0x474F4A42,0x414C4E44,0x3E485247,0x3E45524B,0x3F3F3F3F,0x14141414,0x14141414
};
+#ifdef CALC_PALETTES
+
static void make_nes_pal_uv(uint8_t *u,uint8_t *v)
{
for (int c = 0; c < 16; c++) {
@@ -152,6 +228,8 @@ static void make_nes_palette(int phases)
printf("};\n");
}
+#endif
+
uint8_t* _nofrendo_rom = 0;
extern "C"
char *osd_getromdata()
@@ -219,9 +297,11 @@ public:
virtual void gen_palettes()
{
+#ifdef CALC_PALETTES
make_nes_palette(3);
make_nes_palette(4);
make_alt_pal();
+#endif
}
virtual int info(const string& file, vector<string>& strs)
@@ -437,7 +517,11 @@ public:
return -1;
}
- nes_emulate_init(path.c_str(),width,height);
+ // from CornN64 commit 063b08f7479beb147960c99396b09bdb0b8746b4
+ if (nes_emulate_init(path.c_str(),width,height))
+ printf("NES init failed\n");
+
+ vTaskDelay(100 / portTICK_RATE_MS);
_lines = nes_emulate_frame(true); // first frame!
return 0;
}
@@ -453,7 +537,7 @@ public:
{
return _lines;
}
-
+
virtual int audio_buffer(int16_t* b, int len)
{
int n = frame_sample_count();
diff --git a/src/gui.cpp b/src/gui.cpp
index 2b2009a..db0a68a 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -17,6 +17,11 @@
#include "emu.h"
+#include "freertos/FreeRTOS.h"
+#include "driver/gpio.h"
+#define GPIO_GREENLED 21
+#define GPIO_BTOFF 33
+
using namespace std;
string get_ext(const string& s)
@@ -633,7 +638,7 @@ public:
}
_hilited = max(0,min(_hilited,c-1));
}
-
+
int count()
{
switch (_tab) {
@@ -1007,13 +1012,24 @@ void gui_update()
_gui.update_video();
uint8_t buf[64];
- int n = hid_get(buf,sizeof(buf)); // called from emulation loop
- if (n > 0)
- gui_hid(buf,n);
-
- n = get_hid_ir(buf);
+ int n;
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 1)
+ {
+ n = hid_get(buf,sizeof(buf)); // called from emulation loop
+ if (n > 0)
+ gui_hid(buf,n);
+ }
+ n = get_hid_nintendo(buf);
if (n > 0)
- gui_hid(buf,n);
+ {
+ gui_hid(buf,n);
+ // since code cleaning, we are a little too fast over here and the menu
+ // bluetooth OFF is even faster. click sound doesn't always fully play.
+ // this helps with menu flickering as well
+ // only slow down with bluetooth off otherwise it fuck the HID server badly
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 0)
+ ets_delay_us(5000);
+ }
}
void gui_key(int keycode, int pressed, int mods)
@@ -1077,6 +1093,7 @@ static void ir(const uint8_t* j, int len)
pad_key(GENERIC_DOWN,pad,81); // down
pad_key(GENERIC_RIGHT,pad,79); // right
pad_key(GENERIC_LEFT,pad,80); // left
+ pad_key(GENERIC_OTHER,pad,58); // home/gui (NES controller)
pad_key(GENERIC_RESET | GENERIC_FIRE_Z,pad,58); // home/gui
pad_key(GENERIC_FIRE | GENERIC_FIRE_C | GENERIC_FIRE_B | GENERIC_FIRE_A,pad,40); // enter (A)
_last_pad = pad;
diff --git a/src/hid_server/hid_server.cpp b/src/hid_server/hid_server.cpp
index 9672f2c..d041807 100644
--- a/src/hid_server/hid_server.cpp
+++ b/src/hid_server/hid_server.cpp
@@ -27,6 +27,10 @@ using namespace std;
#include "hci_server.h"
#include "hid_server.h"
+// for bluetooth and hardwired coexistance, Quack jumpers
+#include "driver/gpio.h"
+#define GPIO_ADBSRC 32
+
enum {
HID_SDP_PSM = 0x0001,
HID_CONTROL_PSM = 0x0011,
@@ -598,6 +602,10 @@ public:
if (d->_wii_index == -1)
{
for (int i = 0; i < 4; i++) {
+ if ((gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 0) && i == 0)
+ continue;
+ if ((gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 1) && i == 1)
+ continue;
if (wii_states[i].flags == 0) {
wii_states[i].flags = wiimote;
d->_wii_index = i;
@@ -945,7 +953,7 @@ class HIDSource {
if (d)
d->socket_changed(cb.ss.socket,cb.ss.state);
break;
-
+
default:
break;
}
diff --git a/src/video_out.h b/src/video_out.h
index 57b82e8..866ae1f 100644
--- a/src/video_out.h
+++ b/src/video_out.h
@@ -15,9 +15,22 @@
** SOFTWARE.
*/
-#define VIDEO_PIN 26
-#define AUDIO_PIN 18 // can be any pin
-#define IR_PIN 0 // TSOP4838 or equivalent on any pin if desired
+#define VIDEO_PIN 25 // just for information, not selectable due to hardware limitations
+#define AUDIO_PIN 2 // can be any pin
+
+//NES classic controller (wire colors might be different, double check!)
+// ___
+//DATA |o o| NC
+//LATCH |o o| NC
+//CLK |o o/ 5V
+//GND |o_/
+//
+//5V (red) (5V on Quack due to hardware buffers)
+//GND (white)
+#define NES_CTRL_DAT 4 // # DATA controller (A or B) (black)
+#define NES_CTRL_LATCH 14 // # LATCH (yellow)
+#define NES_CTRL_CLK 12 // # CLOCK (green)
+//On Quack, utilise the ADB buffer as RX for DATA and the Quadrature buffer as TX for clocks et al
int _pal_ = 0;
@@ -42,11 +55,13 @@ int _pal_ = 0;
#include "driver/gpio.h"
#include "driver/i2s.h"
-#ifdef IR_PIN
-#include "ir_input.h" // ir peripherals
+#ifdef NES_CTRL_LATCH
+#include "driver/gpio.h"
+extern "C" {
+#include "nintendo.h" // hard wired Nintendo controllers
+}
#endif
-
//====================================================================================================
//====================================================================================================
//
@@ -97,7 +112,7 @@ static esp_err_t start_dma(int line_width,int samples_per_cc, int ch = 1)
_dma_desc[i].buf = (uint8_t*)heap_caps_calloc(1, n, MALLOC_CAP_DMA);
if (!_dma_desc[i].buf)
return -1;
-
+
_dma_desc[i].owner = 1;
_dma_desc[i].eof = 1;
_dma_desc[i].length = n;
@@ -171,9 +186,12 @@ void video_init_hw(int line_width, int samples_per_cc)
ledcAttachPin(AUDIO_PIN, 0);
ledcWrite(0,0);
- // IR input if used
-#ifdef IR_PIN
- pinMode(IR_PIN,INPUT);
+ // Pin mappings for NES controller input
+#ifdef NES_CTRL_LATCH
+ pinMode(NES_CTRL_LATCH, GPIO_MODE_OUTPUT);
+ digitalWrite(NES_CTRL_LATCH, 0);
+ pinMode(NES_CTRL_CLK, GPIO_MODE_OUTPUT);
+ digitalWrite(NES_CTRL_CLK, 1);
#endif
}
@@ -255,12 +273,12 @@ uint32_t xthal_get_ccount() {
void audio_sample(uint8_t s);
-void ir_sample();
-
-int get_hid_ir(uint8_t* buf)
+extern "C" {
+int get_hid_nintendo(uint8_t* buf)
{
return 0;
}
+}
#endif
@@ -351,10 +369,10 @@ void video_init(int samples_per_cc, int machine, const uint32_t* palette, int nt
_hsync = usec(4.7);
_pal_ = 0;
} else {
- pal_init();
_pal_ = 1;
+ pal_init();
}
-
+
_active_lines = 240;
video_init_hw(_line_width,_samples_per_cc); // init the hardware
}
@@ -439,7 +457,7 @@ void IRAM_ATTR blit_pal(uint8_t* src, uint16_t* dst)
i += 4;
c = *((uint32_t*)(src+i));
-
+
// make 5 colors out of 4 by interpolating y: 0000 0111 1122 2223 3333
c0 = c;
c1 = c >> 8;
@@ -478,7 +496,7 @@ void IRAM_ATTR blit_pal(uint8_t* src, uint16_t* dst)
p = _palette + 64;
dst += 88;
break;
-
+
case EMU_SMS:
// 192 of 288 color clocks wide: roughly correct aspect ratio
dst += 88;
@@ -795,10 +813,6 @@ void IRAM_ATTR video_isr(volatile void* vbuf)
audio_sample(s);
//audio_sample(_sin64[_x++ & 0x3F]);
-#ifdef IR_PIN
- ir_sample();
-#endif
-
int i = _line_counter++;
uint16_t* buf = (uint16_t*)vbuf;
if (_pal_) {
@@ -810,8 +824,7 @@ void IRAM_ATTR video_isr(volatile void* vbuf)
burst(buf);
blit(_lines[i-32],buf + _active_start);
} else if (i < 304) { // post render/black 272-304
- if (i < 272) // slight optimization here, once you have 2 blanking buffers
- blanking(buf,false);
+ blanking(buf,false);
} else {
pal_sync(buf,i); // 8 lines of sync 304-312
}

View File

@ -0,0 +1,3 @@
pico32.menu.PartitionScheme.quack_8_bit=Quack+esp_8_bit (Optimised flash usage)
pico32.menu.PartitionScheme.quack_8_bit.build.partitions=quack_8_bit
pico32.menu.PartitionScheme.quack_8_bit.upload.maximum_size=1966080

BIN
Misc/esp_8_bit/bodge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,879 @@
diff --git a/src/ir_input.h b/src/ir_input.h
deleted file mode 100644
index f02bde8..0000000
--- a/src/ir_input.h
+++ /dev/null
@@ -1,672 +0,0 @@
-
-/* Copyright (c) 2010-2020, Peter Barrett
- **
- ** Permission to use, copy, modify, and/or distribute this software for
- ** any purpose with or without fee is hereby granted, provided that the
- ** above copyright notice and this permission notice appear in all copies.
- **
- ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
- ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
- ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- ** SOFTWARE.
- */
-
-#ifndef __ir_input__
-#define __ir_input__
-
-// generate events on state changes of IR pin, feed to state machines.
-// event timings have a resolution of HSYNC, timing is close enough between 15720 and 15600 to make this work
-// poll to synthesize hid events at every frame
-// i know there is a perfectly good peripheral for this in the ESP32 but this seems more fun somehow
-
-#define WEBTV_KEYBOARD
-#define RETCON_CONTROLLER
-#define FLASHBACK_CONTROLLER
-//#define APPLE_TV_CONTROLLER
-
-uint8_t _ir_last = 0;
-uint8_t _ir_count = 0;
-uint8_t _keyDown = 0;
-uint8_t _keyUp = 0;
-
-void IRAM_ATTR ir_event(uint8_t ticks, uint8_t value); // t is HSYNCH ticks, v is value
-
-inline void IRAM_ATTR ir_sample()
-{
- uint8_t ir = (GPIO.in & (1 << IR_PIN)) != 0;
- if (ir != _ir_last)
- {
- ir_event(_ir_count,_ir_last);
- _ir_count = 0;
- _ir_last = ir;
- }
- if (_ir_count != 0xFF)
- _ir_count++;
-}
-
-class IRState {
-public:
- uint8_t _state = 0;
- uint16_t _code = 0;
- uint16_t _output = 0;
- uint16_t _joy[2] = {0};
- uint16_t _joy_last[2] = {0};
- uint8_t _timer[2] = {0};
-
- void set(int i, int m, int t)
- {
- uint8_t b = 0;
- if ((m & (GENERIC_LEFT | GENERIC_RIGHT)) == (GENERIC_LEFT | GENERIC_RIGHT))
- b++; // bogus?
- if ((m & (GENERIC_UP | GENERIC_DOWN)) == (GENERIC_UP | GENERIC_DOWN))
- b++; // bogus?
- if (b) {
- printf("bogus:%04X\n",m);
- return;
- }
-
- _joy[i] = m;
- _timer[i] = t;
- }
-
- // make a fake hid event
- int get_hid(uint8_t* dst)
- {
- if (_timer[0] && !--_timer[0])
- _joy[0] = 0;
- if (_timer[1] && !--_timer[1])
- _joy[1] = 0;
- if ((_joy[0] != _joy_last[0]) || (_joy[1] != _joy_last[1])) {
- _joy_last[0] = _joy[0];
- _joy_last[1] = _joy[1];
- dst[0] = 0xA1;
- dst[1] = 0x42;
- dst[2] = _joy[0];
- dst[3] = _joy[0]>>8;
- dst[4] = _joy[1];
- dst[5] = _joy[1]>>8;
- return 6; // only sent if it changes
- }
- return 0;
- }
-};
-
-//==========================================================
-//==========================================================
-// Apple remote NEC code
-// pretty easy to adapt to any NEC remote
-
-#ifdef APPLE_TV_CONTROLLER
-
-// Silver apple remote, 7 Bit code
-// should work with both silvers and white
-#define APPLE_MENU 0x40
-#define APPLE_PLAY 0x7A
-#define APPLE_CENTER 0x3A
-#define APPLE_RIGHT 0x60
-#define APPLE_LEFT 0x10
-#define APPLE_UP 0x50
-#define APPLE_DOWN 0x30
-
-#define APPLE_RELEASE 0x20 // sent after menu and play?
-
-// generic repeat code
-#define NEC_REPEAT 0xAAAA
-
-/*
- 9ms preamble ~142
- 4.5ms 1 ~71 - start
- 2.25ms ~35 - repeat
-
- 32 bits
- 0 a 562.5µs/562.5µs 9ish wide
- 1 a 562.5µs/1.6875ms 27ish wide
-*/
-
-IRState _apple;
-int get_hid_apple(uint8_t* dst)
-{
- if (_apple._output)
- {
- if (_apple._output != NEC_REPEAT)
- _keyDown = (_apple._output >> 8) & 0x7F; // 7 bit code
- _apple._output = 0;
-
- uint16_t k = 0;
- switch (_keyDown) {
- case APPLE_UP: k = GENERIC_UP; break;
- case APPLE_DOWN: k = GENERIC_DOWN; break;
- case APPLE_LEFT: k = GENERIC_LEFT; break;
- case APPLE_RIGHT: k = GENERIC_RIGHT; break;
- case APPLE_CENTER: k = GENERIC_FIRE; break;
- case APPLE_MENU: k = GENERIC_RESET; break;
- case APPLE_PLAY: k = GENERIC_SELECT; break;
- }
- _apple.set(0,k,15); // 108ms repeat period
- }
- return _apple.get_hid(dst);
-}
-
-// NEC codes used by apple remote
-void IRAM_ATTR ir_apple(uint8_t t, uint8_t v)
-{
- if (!v) {
- if (t > 32)
- _apple._state = 0;
- } else {
- if (t < 32)
- {
- _apple._code <<= 1;
- if (t >= 12)
- _apple._code |= 1;
- if (++_apple._state == 32)
- _apple._output = _apple._code; // Apple code in bits 14-8*
- } else {
- if (t > 32 && t < 40 && !_apple._state) // Repeat 2.25ms pulse 4.5ms start
- _apple._output = NEC_REPEAT;
- _apple._state = 0;
- }
- }
-}
-
-#endif
-
-//==========================================================
-//==========================================================
-// Atari Flashback 4 wireless controllers
-
-#ifdef FLASHBACK_CONTROLLER
-
-// HSYNCH period is 44/315*455 or 63.55555..us
-// 18 bit code 1.87khz clock
-// 2.3ms zero preamble // 36
-// 0 is 0.27ms pulse // 4
-// 1 is 0.80ms pulse // 13
-
-// Keycodes...
-// Leading bit is 1 for player 1 control..
-
-#define PREAMBLE(_t) (_t >= 34 && _t <= 38)
-#define SHORT(_t) (_t >= 2 && _t <= 6)
-#define LONG(_t) (_t >= 11 && _t <= 15)
-
-// Codes come in pairs 33ms apart
-// Sequence repeats every 133ms
-// bitmap is released if no code for 10 vbls (167ms) or 0x01 (p1) / 0x0F (p2)
-// up to 12 button bits, 4 bits of csum/p1/p2 indication
-
-// called at every loop ~60Hz
-
-IRState _flashback;
-int get_hid_flashback(uint8_t* dst)
-{
- if (_flashback._output)
- {
- uint16_t m = _flashback._output >> 4; // 12 bits of buttons
- printf("F:%04X\n",m);
- uint8_t csum = _flashback._output & 0xF; // csum+1 for p1, csum-1 for p2
- uint8_t s = m + (m >> 4) + (m >> 8);
- if (((s+1) & 0xF) == csum)
- _flashback.set(0,m,15);
- else if (((s-1) & 0xF) == csum)
- _flashback.set(1,m,20);
- _flashback._output = 0;
- }
- return _flashback.get_hid(dst);
-}
-
-void IRAM_ATTR ir_flashback(uint8_t t, uint8_t v)
-{
- if (_flashback._state == 0)
- {
- if (PREAMBLE(t) && (v == 0)) // long 0, rising edge of start bit
- {
- _flashback._state = 1;
- }
- }
- else
- {
- if (v)
- {
- _flashback._code <<= 1;
- if (LONG(t))
- {
- _flashback._code |= 1;
- }
- else if (!SHORT(t))
- {
- _flashback._state = 0; // framing error
- return;
- }
-
- if (++_flashback._state == 19)
- {
- _flashback._output = _flashback._code;
- _flashback._state = 0;
- }
- }
- else
- {
- if (!SHORT(t))
- _flashback._state = 0; // Framing err
- }
- }
-}
-
-#endif
-
-//==========================================================
-//==========================================================
-// RETCON controllers
-// 75ms keyboard repeat
-// Preamble is 0.80ms low, 0.5 high
-// Low: 0.57ms=0,0.27,s=1, high 0.37
-// 16 bits
-// Preamble = 800,500/63.55555 ~ 12.6,7.87
-// LOW0 = 8.97
-// LOW1 = 4.25
-// HIGH = 5.82
-
-#ifdef RETCON_CONTROLLER
-
-// number of 63.55555 cycles per bit
-#define PREAMBLE_L(_t) (_t >= 12 && _t <= 14) // 12/13/14 preamble
-#define PREAMBLE_H(_t) (_t >= 6 && _t <= 10) // 8
-#define LOW_0(_t) (_t >= 8 && _t <= 10) // 8/9/10
-#define LOW_1(_t) (_t >= 4 && _t <= 6) // 4/5/6
-
-// map retcon to generic
-const uint16_t _jmap[] = {
- 0x0400, GENERIC_UP,
- 0x0200, GENERIC_DOWN,
- 0x0100, GENERIC_LEFT,
- 0x0080, GENERIC_RIGHT,
-
- 0x1000, GENERIC_SELECT,
- 0x0800, GENERIC_START,
-
- 0x0020, GENERIC_FIRE_X,
- 0x0040, GENERIC_FIRE_Y,
- 0x0002, GENERIC_FIRE_Z,
-
- 0x2000, GENERIC_FIRE_A,
- 0x4000, GENERIC_FIRE_B,
- 0x0008, GENERIC_FIRE_C,
-};
-
-IRState _retcon;
-int get_hid_retcon(uint8_t* dst)
-{
- if (_retcon._output)
- {
- uint16_t m = 0;
- const uint16_t* jmap = _jmap;
- int16_t i = 12;
- uint16_t k = _retcon._output;
- _retcon._output = 0;
- while (i--)
- {
- if (jmap[0] & k)
- m |= jmap[1];
- jmap += 2;
- }
- printf("R:%04X\n",m);
- _retcon.set(k >> 15,m,20);
- }
- return _retcon.get_hid(dst);
-}
-
-void IRAM_ATTR ir_retcon(uint8_t t, uint8_t v)
-{
- if (_retcon._state == 0)
- {
- if (v == 0) { // start bit
- if (PREAMBLE_L(t))
- _retcon._state = 1;
- }
- }
- else
- {
- if (!v)
- {
- _retcon._code <<= 1;
- if (LOW_1(t))
- _retcon._code |= 1;
- if (_retcon._state++ == 16)
- {
- _retcon._output = _retcon._code;
- _retcon._state = 0;
- }
- }
- }
-}
-
-#endif
-
-
-//==========================================================
-//==========================================================
-// Webtv keyboard
-#ifdef WEBTV_KEYBOARD
-
-#define BAUDB 12 // Width of uart bit in HSYNCH
-#define WT_PREAMBLE(_t) (_t >= 36 && _t <= 40) // 3.25 baud
-#define SHORTBIT(_t) (_t >= 9 && _t <= 13) // 1.5ms ish
-
-// converts webtv keyboard to common scancodes
-const uint8_t _ir2scancode[128] = {
- 0x00, // 00
- 0x00, // 02
- 0x05, // 04 B
- 0x00, // 06
- 0x00, // 08
- 0x51, // 0A Down
- 0x00, // 0C
- 0x00, // 0E
- 0x00, // 10
- 0x50, // 12 Left
- 0xE6, // 14 Right Alt
- 0x38, // 16 /
- 0xE2, // 18 Left Alt
- 0x4F, // 1A Right
- 0x2C, // 1C Space
- 0x11, // 1E N
- 0x32, // 20 #
- 0x00, // 22
- 0x22, // 24 5
- 0x41, // 26 F8
- 0x3B, // 28 F2
- 0xE4, // 2A Right Ctrl
- 0x00, // 2C
- 0x2E, // 2E =
- 0x3A, // 30 F1
- 0x4A, // 32 Home
- 0x00, // 34
- 0x2D, // 36 -
- 0xE0, // 38 Left Ctrl
- 0x35, // 3A `
- 0x42, // 3C F9
- 0x23, // 3E 6
- 0x00, // 40
- 0x00, // 42
- 0x19, // 44 V
- 0x37, // 46 .
- 0x06, // 48 C
- 0x68, // 4A F13
- 0xE5, // 4C Right Shift
- 0x36, // 4E ,
- 0x1B, // 50 X
- 0x4D, // 52 End
- 0x00, // 54
- 0x00, // 56
- 0x1D, // 58 Z
- 0x00, // 5A
- 0x28, // 5C Return
- 0x10, // 5E M
- 0x00, // 60
- 0xE7, // 62 Right GUI
- 0x09, // 64 F
- 0x0F, // 66 L
- 0x07, // 68 D
- 0x4E, // 6A PageDown
- 0x00, // 6C
- 0x0E, // 6E K
- 0x16, // 70 S
- 0x4B, // 72 PageUp
- 0x00, // 74
- 0x33, // 76 ;
- 0x04, // 78 A
- 0x00, // 7A
- 0x31, // 7C |
- 0x0D, // 7E J
- 0x00, // 80
- 0x00, // 82
- 0x17, // 84 T
- 0x40, // 86 F7
- 0x3C, // 88 F3
- 0x00, // 8A
- 0xE1, // 8C Left Shift
- 0x30, // 8E ]
- 0x39, // 90 CapsLock
- 0x00, // 92
- 0x29, // 94 Escape
- 0x2F, // 96 [
- 0x2B, // 98 Tab
- 0x00, // 9A
- 0x2A, // 9C Backspace
- 0x1C, // 9E Y
- 0x00, // A0
- 0x00, // A2
- 0x21, // A4 4
- 0x26, // A6 9
- 0x20, // A8 3
- 0x44, // AA F11
- 0x00, // AC
- 0x25, // AE 8
- 0x1F, // B0 2
- 0x00, // B2
- 0x46, // B4 PrintScreen
- 0x27, // B6 0
- 0x1E, // B8 1
- 0x45, // BA F12
- 0x43, // BC F10
- 0x24, // BE 7
- 0x00, // C0
- 0x00, // C2
- 0x0A, // C4 G
- 0x00, // C6
- 0x3D, // C8 F4
- 0x00, // CA
- 0x00, // CC
- 0x00, // CE
- 0x3E, // D0 F5
- 0x52, // D2 Up
- 0xE3, // D4 Left GUI
- 0x34, // D6 '
- 0x29, // D8 Escape
- 0x48, // DA Pause
- 0x3F, // DC F6
- 0x0B, // DE H
- 0x00, // E0
- 0x00, // E2
- 0x15, // E4 R
- 0x12, // E6 O
- 0x08, // E8 E
- 0x00, // EA
- 0x00, // EC
- 0x0C, // EE I
- 0x1A, // F0 W
- 0x00, // F2
- 0x53, // F4 Numlock
- 0x13, // F6 P
- 0x14, // F8 Q
- 0x00, // FA
- 0x00, // FC
- 0x18, // FE U
-};
-
-
-// IR Keyboard State
-uint8_t _state = 0;
-uint16_t _code = 0;
-uint8_t _wt_keys[6] = {0};
-uint8_t _wt_expire[6] = {0};
-uint8_t _wt_modifiers = 0;
-
-static
-uint8_t parity_check(uint8_t k)
-{
- uint8_t c;
- uint8_t v = k;
- for (c = 0; v; c++)
- v &= v-1;
- return (c & 1) ? k : 0;
-}
-
-// make a mask from modifier keys
-static
-uint8_t ctrl_mask(uint8_t k)
-{
- switch (k) {
- case 0x38: return KEY_MOD_LCTRL;
- case 0x8C: return KEY_MOD_LSHIFT;
- case 0x18: return KEY_MOD_LALT;
- case 0xD4: return KEY_MOD_LGUI;
- case 0x2A: return KEY_MOD_RCTRL;
- case 0x4C: return KEY_MOD_RSHIFT;
- case 0x14: return KEY_MOD_RALT;
- case 0x62: return KEY_MOD_RGUI;
- }
- return 0;
-}
-
-// update state of held keys
-// produce a hid keyboard record
-int get_hid_webtv(uint8_t* dst)
-{
- bool dirty = false;
- uint8_t k = parity_check(_keyUp);
- _keyUp = 0;
- if (k) {
- _wt_modifiers &= ~ctrl_mask(k);
- for (int i = 0; i < 6; i++) {
- if (_wt_keys[i] == k) {
- _wt_expire[i] = 1; // key will expire this frame
- break;
- }
- }
- }
-
- k = parity_check(_keyDown);
- _keyDown = 0;
- if (k) {
- _wt_modifiers |= ctrl_mask(k);
-
- // insert key into list of pressed keys
- int j = 0;
- for (int i = 0; i < 6; i++) {
- if ((_wt_keys[i] == 0) || (_wt_expire[i] == 0) || (_wt_keys[i] == k)) {
- j = i;
- break;
- }
- if (_wt_expire[i] < _wt_expire[j])
- j = i;
- }
- if (_wt_keys[j] != k) {
- _wt_keys[j] = k;
- dirty = true;
- }
- _wt_expire[j] = 8; // key will be down for ~130ms or 8 frames
- }
-
- // generate hid keyboard events if anything was pressed or changed...
- // A1 01 mods XX k k k k k k
- dst[0] = 0xA1;
- dst[1] = 0x01;
- dst[2] = _wt_modifiers;
- dst[3] = 0;
- int j = 0;
- for (int i = 0; i < 6; i++) {
- dst[4+i] = 0;
- if (_wt_expire[i]) {
- if (!--_wt_expire[i])
- dirty = true;
- }
- if (_wt_expire[i] == 0) {
- _wt_keys[i] = 0;
- } else {
- dst[4 + j++] = _ir2scancode[_wt_keys[i] >> 1];
- }
- }
- return dirty ? 10 : 0;
-}
-
-// WebTV UART like keyboard protocol
-// 3.25 bit 0 start preamble the 19 bits
-// 10 bit code for keyup, keydown, all keys released etc
-// 7 bit keycode + parity bit.
-//
-
-#define KEYDOWN 0x4A
-#define KEYUP 0x5E
-void IRAM_ATTR ir_webtv(uint8_t t, uint8_t v)
-{
- if (_state == 0)
- {
- if (WT_PREAMBLE(t) && (v == 0)) // long 0, rising edge of start bit
- _state = 1;
- }
- else if (_state == 1)
- {
- _state = (SHORTBIT(t) && (v == 1)) ? 2 : 0;
- }
- else
- {
- t += BAUDB>>1;
- uint8_t bits = _state-2;
- while ((t > BAUDB) && (bits < 16))
- {
- t -= BAUDB;
- _code = (_code << 1) | v;
- bits++;
- }
- if (bits == 16)
- {
- v = t <= BAUDB;
- uint8_t md = _code >> 8;
- _code |= v; // Low bit of code is is parity
- if (md == KEYDOWN)
- _keyDown = _code;
- else if (md == KEYUP)
- _keyUp = _code;
- _state = 0; // got one
- return;
- }
- _state = bits+2;
- }
-}
-#endif
-
-// called from interrupt
-void IRAM_ATTR ir_event(uint8_t t, uint8_t v)
-{
-#ifdef WEBTV_KEYBOARD
- ir_webtv(t,v);
-#endif
-#ifdef RETCON_CONTROLLER
- ir_retcon(t,v);
-#endif
-#ifdef APPLE_TV_CONTROLLER
- ir_apple(t,v);
-#endif
-#ifdef FLASHBACK_CONTROLLER
- ir_flashback(t,v);
-#endif
-}
-
-// called every frame from emu
-int get_hid_ir(uint8_t* dst)
-{
- int n = 0;
-#ifdef APPLE_TV_CONTROLLER
- if (n = get_hid_apple(dst))
- return n;
-#endif
-#ifdef RETCON_CONTROLLER
- if (n = get_hid_retcon(dst))
- return n;
-#endif
-#ifdef FLASHBACK_CONTROLLER
- if (n = get_hid_flashback(dst))
- return n;
-#endif
- #ifdef WEBTV_KEYBOARD
- return get_hid_webtv(dst);
- #endif
- return 0;
-}
-#endif
diff --git a/src/nintendo.h b/src/nintendo.h
new file mode 100644
index 0000000..ded6a51
--- /dev/null
+++ b/src/nintendo.h
@@ -0,0 +1,195 @@
+
+/*
+ ** Copyright (c) 2010-2020, Peter Barrett
+ ** Copyright (c) 2024, Michel Depeige
+ **
+ ** Permission to use, copy, modify, and/or distribute this software for
+ ** any purpose with or without fee is hereby granted, provided that the
+ ** above copyright notice and this permission notice appear in all copies.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
+ ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ ** SOFTWARE.
+ */
+
+#ifndef __NINTENDO_H__
+#define __NINTENDO_H__
+
+#undef NES_CONTROLLER
+#define SNES_CONTROLLER
+
+/*
+ * ^^ choose either NES or SNES above
+ *
+ * inspired from Peter's work, and mixed with code I had around + CornN64 commits:
+ * - 8bdbe1c0ff234d1721ffe48472712b58b7f3683f
+ * - 937a9567224ab981d41fef5ba0e9ddcf19758996
+ *
+ * code only uses ESP-IDF, no Arduinism
+ *
+ * ADBSRC switch set if the classic controller is player A (0, pulled down) or B (1)
+ * the yellow LED on pin 26 is used as a controller activity LED by calling esp-idf stuff directly
+ * pin 26 is DAC channel 2 but it isn't used nor enabled so it works
+ */
+
+#define GPIO_YELLOWLED 26
+#define GPIO_ADBSRC 32
+
+/* how many buttons to poll */
+#ifdef NES_CONTROLLER
+#define NC_CLOCKS 8
+#endif
+#ifdef SNES_CONTROLLER
+#define NC_CLOCKS 12
+#endif
+
+/*
+ * this is added because poking into ESP-IDF is too fast. We are not letting
+ * enough time for the controller shift register and/or the main loop in the menu
+ */
+#define NC_DELAY 8
+
+/* globals for controller memory */
+uint16_t nc_joy[2] = {0};
+uint16_t nc_joy_last[2] = {0};
+
+/* internal functions */
+static void nc_set(int i, int m) {
+ uint8_t b = 0;
+
+ /* do not store if directional pad combinaisons are impossible */
+ if ((m & (GENERIC_LEFT | GENERIC_RIGHT)) == (GENERIC_LEFT | GENERIC_RIGHT))
+ b++; // bogus?
+ if ((m & (GENERIC_UP | GENERIC_DOWN)) == (GENERIC_UP | GENERIC_DOWN))
+ b++; // bogus?
+ if (b) {
+ printf("bogus:%04X\n",m);
+ return;
+ }
+
+ nc_joy[i] = m;
+}
+
+static int get_hid_nc(uint8_t* dst) {
+ /* send only HID event if something changes */
+ if ((nc_joy[0] != nc_joy_last[0]) || (nc_joy[1] != nc_joy_last[1])) {
+ nc_joy_last[0] = nc_joy[0];
+ nc_joy_last[1] = nc_joy[1];
+ dst[0] = 0xA1;
+ dst[1] = 0x42;
+ dst[2] = nc_joy[0];
+ dst[3] = nc_joy[0] >> 8;
+ dst[4] = nc_joy[1];
+ dst[5] = nc_joy[1] >> 8;
+ return 6;
+ }
+ return 0;
+}
+
+/*************************************
+ * Classic hard wired NES controller *
+ *************************************/
+enum NES_button {
+ NES_A = 0,
+ NES_B = 1,
+ NES_SELECT = 2,
+ NES_START = 3,
+ NES_UP = 4,
+ NES_DOWN = 5,
+ NES_LEFT = 6,
+ NES_RIGHT = 7,
+};
+
+/***************************************
+ * Classic hard wired SNES controllers *
+ ***************************************/
+enum SNES_button {
+ SNES_B = 0,
+ SNES_Y = 1,
+ SNES_SELECT = 2,
+ SNES_START = 3,
+ SNES_UP = 4,
+ SNES_DOWN = 5,
+ SNES_LEFT = 6,
+ SNES_RIGHT = 7,
+ SNES_A = 8,
+ SNES_X = 9,
+ SNES_L = 10,
+ SNES_R = 11,
+};
+
+/* called every frame from emu */
+int IRAM_ATTR get_hid_nintendo(uint8_t* dst) {
+ uint16_t buttons = 0;
+ uint16_t k = 0;
+
+ gpio_set_level((gpio_num_t)NES_CTRL_LATCH, 1);
+ ets_delay_us(NC_DELAY);
+ gpio_set_level((gpio_num_t)NES_CTRL_LATCH, 0);
+ ets_delay_us(NC_DELAY);
+
+ for (int i = 0; i < NC_CLOCKS; i++) {
+ buttons |= gpio_get_level((gpio_num_t)NES_CTRL_DAT) << i;
+ gpio_set_level((gpio_num_t)NES_CTRL_CLK, 0);
+ ets_delay_us(NC_DELAY);
+ gpio_set_level((gpio_num_t)NES_CTRL_CLK, 1);
+ ets_delay_us(NC_DELAY);
+ }
+
+#ifdef SNES_CONTROLLER
+ /* on NES, menu is LEFT (directional pad) + SELECT */
+ if ((buttons & ((1<<NES_LEFT) | (1<<NES_SELECT)))==0) {
+ k |= GENERIC_OTHER;
+ }
+ else {
+ if ((buttons & (1<<NES_UP))==0) k |= GENERIC_UP;
+ if ((buttons & (1<<NES_DOWN))==0) k |= GENERIC_DOWN;
+ if ((buttons & (1<<NES_LEFT))==0) k |= GENERIC_LEFT;
+ if ((buttons & (1<<NES_RIGHT))==0) k |= GENERIC_RIGHT;
+ if ((buttons & (1<<NES_A))==0) k |= GENERIC_FIRE | GENERIC_FIRE_A;
+ if ((buttons & (1<<NES_B))==0) k |= GENERIC_FIRE_B;
+ if ((buttons & (1<<NES_START))==0) k |= GENERIC_START;
+ if ((buttons & (1<<NES_SELECT))==0) k |= GENERIC_SELECT;
+ }
+#endif
+#ifdef SNES_CONTROLLER
+ /* on SNES, menu is LEFT + RIGHT buttons */
+ if ((buttons & ((1<<SNES_L) | (1<<SNES_R)))==0) {
+ k |= GENERIC_OTHER;
+ }
+ else {
+ if ((buttons & (1 << SNES_UP)) == 0) k |= GENERIC_UP;
+ if ((buttons & (1 << SNES_DOWN)) == 0) k |= GENERIC_DOWN;
+ if ((buttons & (1 << SNES_LEFT)) == 0) k |= GENERIC_LEFT;
+ if ((buttons & (1 << SNES_RIGHT)) == 0) k |= GENERIC_RIGHT;
+ if ((buttons & (1 << SNES_A)) == 0) k |= GENERIC_FIRE | GENERIC_FIRE_A;
+ if ((buttons & (1 << SNES_B)) == 0) k |= GENERIC_FIRE_B;
+ if ((buttons & (1 << SNES_START)) == 0) k |= GENERIC_START;
+ if ((buttons & (1 << SNES_SELECT)) == 0) k |= GENERIC_SELECT;
+ }
+#endif
+
+ /* Quack hard wired player A or B */
+ if (gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 0) {
+ nc_set(0,k); // no repeat period
+ nc_set(1,0); // no repeat period
+ }
+ else {
+ nc_set(0,0); // no repeat period
+ nc_set(1,k); // no repeat period
+ }
+
+ //printf("NC:\t%04X, %X\n", buttons, k);
+ if (k && buttons)
+ gpio_set_level((gpio_num_t)GPIO_YELLOWLED, 1);
+ else
+ gpio_set_level((gpio_num_t)GPIO_YELLOWLED, 0);
+
+ return get_hid_nc(dst);
+}
+#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x90000,
app1, app, ota_1, 0xA0000,0x1B0000,
spiffs, data, spiffs, 0x250000,0x1A0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x90000
5 app1 app ota_1 0xA0000 0x1B0000
6 spiffs data spiffs 0x250000 0x1A0000

View File

@ -1,20 +1,66 @@
# Quack Mouse Converter
The Quack Mouse project is a Bluetooth & ADB Mouse "Dongle" for the Macintosh 128/512/Plus. It allow you to use most ADB and BT4+ mouses with a Macintosh 128/512/Plus though the mouse port of the Macintosh.
# Quack Mouse Converter[![Badge License]][License]&nbsp;[![Badge CI]][CI]
It include a custom PCB (EDA subfolder) with the accompanying firmware (code in main subfolder)
A dongle that allows various retro systems to use the `Apple Desktop Bus` as well as `Bluetooth 2+` mice through their `quadrature` mouse ports.
### Supported mouse conversions:
<br>
<br>
<br>
Quack support the following conversions:
- ADB mouse to Macintosh 128/512/Plus
- Bluetooth mouse to Macintosh 128/512/Plus
<div align = center>
### Unsupported mouse conversions:
[![Button Documentation]][Documentation]
[![Button Firmware]][Firmware]
[![Button PCB]][PCB]
[![Button Changelog]][Changelog]
- ADB mouse to Bluetooth LE host
This is a work in progress for now, it doesn't work with all mainstream operating systems.
<br>
<br>
### Documentation
<img src = 'Images/Preview.png' width = 500 />
More documentation is avaible on the Wiki. Please check it out: https://github.com/demik/quack/wiki
</div>
<br>
<br>
## Supported
#### Systems
<kbd>Macintosh 128 / 512 / Plus</kbd><kbd>Apple Lisa</kbd> <kbd>Apple IIc</kbd>
#### Connection Types
<kbd>Apple Desktop Bus</kbd><kbd>Bluetooth Classic 2+</kbd>
<br>
<br>
### Unsupported
- `ADB`  ➞  `Bluetooth LE`for **Non-Windows 10**
<br>
<!----------------------------------------------------------------------------->
[Documentation]: https://github.com/demik/quack/wiki
[Changelog]: ChangeLog.md
[Firmware]: main/README.md
[License]: LICENSE
[PCB]: EDA/ChangeLog.md
[CI]: https://github.com/demik/quack/actions/workflows/idf.yml
<!-------------------------------[ Badges ]----------------------------------->
[Badge License]: https://img.shields.io/badge/License-Apache_2.0-961b1f?labelColor=D22128
[Badge CI]: https://github.com/demik/quack/actions/workflows/idf.yml/badge.svg?branch=master&event=push
<!-------------------------------[ Buttons ]----------------------------------->
[Button Documentation]: https://img.shields.io/badge/Documentation-0099E5?style=for-the-badge&logoColor=white&logo=BookStack
[Button Changelog]: https://img.shields.io/badge/Changelog-21375A?style=for-the-badge&logoColor=white&logo=AzureArtifacts
[Button Firmware]: https://img.shields.io/badge/Firmware-E5426E?style=for-the-badge&logoColor=white&logo=ROS
[Button PCB]: https://img.shields.io/badge/PCB-007c40?style=for-the-badge&logoColor=white&logo=PowerApps

View File

@ -4,10 +4,12 @@ set(srcs "adb.c"
"gpio.c"
"led.c"
"main.c"
"quad.c")
"mighty.c"
"quad.c"
"wii.c")
set(include_dirs ".")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
REQUIRES esp_hid)
REQUIRES driver esp_hid nvs_flash spi_flash)

View File

@ -2,7 +2,7 @@
### Supported SDK versions:
The code is designed to be build with ESP-IDF SDK version 4.2.2. To workaround some patchs or limitations of this specific release, you need to apply the patch located in the SDK subfolder
The code is designed to be build with ESP-IDF SDK version 5.4.0. To workaround some patchs or limitations of this specific release, you need to apply the patch located in the SDK subfolder
### Unsupported mouse conversions:
@ -18,3 +18,4 @@ List of relevant files:
- m4848.h Header for Apple Hockey Puck mouse over Bluetooth emulation
- main.c Everything start here
- quad.c Quadrature related functions (mouse port)
- wii.* Wiimote driver

View File

@ -1,75 +1,13 @@
diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_le.c b/components/bt/host/bluedroid/bta/hh/bta_hh_le.c
index 6c86cb9be7..8fdf1a1dc3 100644
--- a/components/bt/host/bluedroid/bta/hh/bta_hh_le.c
+++ b/components/bt/host/bluedroid/bta/hh/bta_hh_le.c
@@ -498,7 +498,7 @@ tBTA_HH_LE_RPT *bta_hh_le_find_rpt_by_idtype(tBTA_HH_LE_RPT *p_head, UINT8 mode,
UINT8 i;
#if BTA_HH_DEBUG == TRUE
- APPL_TRACE_DEBUG("bta_hh_le_find_rpt_by_idtype: r_type: %d rpt_id: %d", r_type, rpt_id);
+ :required:("bta_hh_le_find_rpt_by_idtype: r_type: %d rpt_id: %d", r_type, rpt_id);
#endif
for (i = 0 ; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt++) {
diff --git a/components/esp_hid/include/esp_hidh.h b/components/esp_hid/include/esp_hidh.h
index b1a8264307..bedcb42b65 100644
--- a/components/esp_hid/include/esp_hidh.h
+++ b/components/esp_hid/include/esp_hidh.h
@@ -100,6 +100,7 @@ typedef union {
typedef struct {
esp_event_handler_t callback;
+ uint16_t event_stack_size;
} esp_hidh_config_t;
/**
diff --git a/components/esp_hid/src/ble_hidh.c b/components/esp_hid/src/ble_hidh.c
index 5fe54f2fa7..28cc35e295 100644
--- a/components/esp_hid/src/ble_hidh.c
+++ b/components/esp_hid/src/ble_hidh.c
@@ -617,7 +617,7 @@ esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config)
.queue_size = 5,
.task_name = "esp_ble_hidh_events",
.task_priority = uxTaskPriorityGet(NULL),
- .task_stack_size = 2048,
+ .task_stack_size = config->event_stack_size > 0 ? config->event_stack_size : 2048,
.task_core_id = tskNO_AFFINITY
};
ret = esp_event_loop_create(&event_task_args, &event_loop_handle);
diff --git a/components/esp_hid/src/bt_hidh.c b/components/esp_hid/src/bt_hidh.c
index 1a17e6aa35..6ae12958ff 100644
index 6b33beee5e..53a99af52d 100644
--- a/components/esp_hid/src/bt_hidh.c
+++ b/components/esp_hid/src/bt_hidh.c
@@ -320,7 +320,7 @@ esp_err_t esp_bt_hidh_init(const esp_hidh_config_t *config)
.queue_size = 5,
.task_name = "esp_bt_hidh_events",
.task_priority = uxTaskPriorityGet(NULL),
- .task_stack_size = 2048,
+ .task_stack_size = config->event_stack_size > 0 ? config->event_stack_size : 2048,
.task_core_id = tskNO_AFFINITY
};
esp_err_t ret = esp_event_loop_create(&event_task_args, &event_loop_handle);
@@ -386,6 +386,23 @@ void bta_hh_co_data(uint8_t handle, uint8_t *p_rpt, uint16_t len, tBTA_HH_PROTO_
ESP_LOGE(TAG, "Device Not Found: handle %u", handle);
return;
}
+
+ /*
+ * quack patch: mode is always ESP_HID_PROTOCOL_MODE_BOOT even if the device is in BOOT mode
+ * BTA_HH_PROTO_RPT_MODE from Bluedroid is 0
+ * BOOT report id is also questionable, assume than an REPORT mode with a lengh of 3 or 4 is a BOOT packet
+ */
+ if (len >= 2 + 1 && p_rpt[0] == 2 && mode == 0) {
+ esp_hidh_event_data_t p = {0};
+ p.input.dev = dev;
+ p.feature.report_id = 2;
+ p.feature.usage = ESP_HID_USAGE_MOUSE;
+ p.feature.data = p_rpt + 1;
+ p.input.length = len - 1;
+ esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_INPUT_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
+ return ;
+ }
+
report = esp_hidh_dev_get_input_report_by_id_and_proto(dev, p_rpt[0], mode ? ESP_HID_PROTOCOL_MODE_BOOT : ESP_HID_PROTOCOL_MODE_REPORT);
if (report == NULL) {
ESP_LOGE(TAG, "Report Not Found: %d mode: %s", p_rpt[0], mode ? "BOOT" : "REPORT");
@@ -668,7 +668,7 @@ static void esp_hh_cb(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param)
if (param->data_ind.len == 9 && *(param->data_ind.data) == 1) {
has_report_id = true;
_usage = ESP_HID_USAGE_KEYBOARD;
- } else if (param->data_ind.len == 4 && *(param->data_ind.data) == 2) {
+ } else if (param->data_ind.len >= 4 && *(param->data_ind.data) == 2) {
has_report_id = true;
_usage = ESP_HID_USAGE_MOUSE;
} else {

View File

@ -3,7 +3,7 @@
* quack
*
* Created by Michel DEPEIGE on 7/01/2020.
* Copyright (c) 2020 Michel DEPEIGE.
* Copyright (c) 2020-2024 Michel DEPEIGE.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Apache License, Version 2.0 (the "License");
@ -24,12 +24,13 @@
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_chip_info.h"
#include "esp_private/periph_ctrl.h"
#include "driver/rmt.h"
#include "driver/periph_ctrl.h"
#include "soc/periph_defs.h"
#include "soc/rmt_reg.h"
@ -49,15 +50,17 @@ extern QueueHandle_t q_qx, q_qy;
/* static defines */
static void adb_handle_button(bool action);
static void adb_rmt_reset(void);
static void adb_rmt_reset(void);
static bool adb_rx_isone(rmt_item32_t cell);
static bool adb_rx_isstop(rmt_item32_t cell);
static bool adb_rx_iszero(rmt_item32_t cell);
static uint16_t IRAM_ATTR adb_rx_mouse();
static void adb_rx_setup(void);
static void adb_scan_macaj(void);
static void adb_tx_as(void);
static void adb_tx_one(void);
static void adb_tx_setup(void);
static void adb_tx_stop(void);
static void adb_tx_zero(void);
/* functions */
@ -126,7 +129,7 @@ inline bool adb_is_host(void) {
void adb_probe(void) {
uint16_t register3;
ESP_LOGI(TAG, "Probing for mouse...");
ESP_LOGI(TAG, "Probing for mouse");
xTaskNotify(t_yellow, LED_SLOW, eSetValueWithOverwrite);
/* for some reason, RMT misses the first exchange sometimes. Flush the device should give it time */
@ -135,15 +138,61 @@ void adb_probe(void) {
vTaskDelay(12 / portTICK_PERIOD_MS);
while (true) {
adb_tx_cmd(ADB_MOUSE|ADB_TALK|ADB_REG3);
register3 = adb_rx_mouse();
ESP_LOGD("ADB", "Device $3 register3: %x", register3);
ESP_LOGD("ADB", "Device $3 register3: %04x", register3);
if (register3 && (register3 & ADB_H_ALL) == ADB_H_ERR)
ESP_LOGE(TAG, "Mouse failed self init test");
/*
* try to unglue composite devices (Kensington)
* the idea is to move detected devices to $9, and check $3 again
* if there is something again at $3, it may be a composite device, or the user
* plugged two devices.
* if there is nothing anymore, move back $9 to $3
*
* we will handle that further down the line by checking the handler
*/
if (register3 & ADB_H_ALL) {
ESP_LOGI(TAG, "\tMoving $3 to $9 to check for composite devices");
adb_tx_listen(ADB_MOUSE|ADB_LISTEN|ADB_REG3, (ADB_TMP<<4)|ADB_H_MOVE);
vTaskDelay(7 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "\tChecking $3 again…");
adb_tx_cmd(ADB_MOUSE|ADB_TALK|ADB_REG3);
register3 = adb_rx_mouse();
vTaskDelay(7 / portTICK_PERIOD_MS);
if (register3) {
adb_tx_cmd(ADB_TMP|ADB_TALK|ADB_REG3);
register3 = adb_rx_mouse();
ESP_LOGI(TAG, "\t… something was there, $9 handler: $%02x", register3 & ADB_H_ALL);
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
}
else {
ESP_LOGI(TAG, "\t… nothing, moving $9 back to $3");
adb_tx_listen(ADB_TMP|ADB_LISTEN|ADB_REG3, (ADB_MOUSE<<4)|ADB_H_MOVE);
}
/* restore register3 from $3 for handler detection */
vTaskDelay(7 / portTICK_PERIOD_MS);
adb_tx_cmd(ADB_MOUSE|ADB_TALK|ADB_REG3);
register3 = adb_rx_mouse();
}
/* Accept all known handlers */
if (((register3 & ADB_H_ALL) == ADB_H_C100) || ((register3 & ADB_H_ALL) == ADB_H_C200) ||
((register3 & ADB_H_ALL) == ADB_H_MTRC) || ((register3 & ADB_H_ALL) == ADB_H_KSGT) ||
((register3 & ADB_H_ALL) == ADB_H_MACJ)) {
ESP_LOGI(TAG, "… detected mouse at $3");
if ((register3 & ADB_H_ALL) == ADB_H_C100) {
ESP_LOGI(TAG, "... detected mouse at $3");
break;
}
/* try to find other misc devices */
if (!register3)
adb_scan_macaj();
vTaskDelay(500 / portTICK_PERIOD_MS);
}
@ -163,6 +212,19 @@ void adb_probe(void) {
case ADB_H_C200:
ESP_LOGD(TAG, "Mouse running at 200cpi");
break;
case ADB_H_MACJ:
ESP_LOGD(TAG, "MacAlly Joystick running at default cpi");
break;
case ADB_H_MTRC:
ESP_LOGD(TAG, "MacTRAC running at default cpi");
break;
case ADB_H_KSGT:
ESP_LOGD(TAG, "Kensington detected, switching to standard device");
adb_tx_listen(ADB_MOUSE|ADB_LISTEN|ADB_REG3, (ADB_KML1<<4)|ADB_H_MOVE);
adb_tx_listen(ADB_TMP|ADB_LISTEN|ADB_REG3, (ADB_MOUSE<<4)|ADB_H_MOVE);
adb_tx_listen(ADB_MOUSE|ADB_LISTEN|ADB_REG3, 0x6000|(ADB_MOUSE<<4)|ADB_H_C200);
ESP_LOGD(TAG, "Kensington running at 200cpi");
break;
default:
ESP_LOGE(TAG, "Mouse running with unknow handler: %x", register3 & ADB_H_ALL);
}
@ -186,16 +248,54 @@ static void adb_rmt_reset() {
adb_tx_reset();
}
/*
* MacAlly Joystick uses address $5 instead of the classic $3
* First check that it's in mouse mode emulation then switch it to $3
* We should then be able to use the base code path
*
* https://github.com/lampmerchant/tashnotes/blob/main/macintosh/adb/protocols/macally_joystick.md
*/
static void adb_scan_macaj() {
uint16_t register1;
/* the joystick will not move if we don't don't talk to register 3 first */
vTaskDelay(7 / portTICK_PERIOD_MS);
adb_tx_cmd(ADB_MACAJ|ADB_TALK|ADB_REG3);
adb_rx_mouse();
vTaskDelay(7 / portTICK_PERIOD_MS);
adb_tx_cmd(ADB_MACAJ|ADB_TALK|ADB_REG1);
register1 = adb_rx_mouse();
if (register1 == 0xAAAA) {
ESP_LOGD(TAG, "MacAlly Joystick in joystick mode detected, switching modes");
adb_tx_listen(ADB_MACAJ|ADB_LISTEN|ADB_REG1, 0xAAAA);
vTaskDelay(7 / portTICK_PERIOD_MS);
adb_tx_cmd(ADB_MACAJ|ADB_TALK|ADB_REG1);
register1 = adb_rx_mouse();
}
if (register1 == 0x5555) {
ESP_LOGD(TAG, "MacAlly Joystick in mouse mode detected, moving it to $3");
adb_tx_listen(ADB_MACAJ|ADB_LISTEN|ADB_REG3, (ADB_MOUSE<<4)|ADB_H_MOVE);
}
}
void adb_task_host(void *pvParameters) {
/* Classic Apple Mouse Protocol is 16 bits long */
uint16_t data;
uint8_t last = 0;
int8_t move = 0;
uint8_t state = ADB_S_PROBE;
uint8_t last = 0;
int8_t move = 0;
uint8_t state = ADB_S_PROBE;
/*
* wait a little for the LED tasks to start on the other core
* starting IDF 5.2.2, we need this. Looks like the init is now too fast on app core
*/
vTaskDelay(200 / portTICK_PERIOD_MS);
/* put green led to steady if BT is disabled. Otherwise BT init will do it */
if (gpio_get_level(GPIO_BTOFF) == 0)
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
ESP_LOGI(TAG, "host started on core %d", xPortGetCoreID());
/* poll the mouse like a maniac. It will answer only if there is user input */
@ -259,7 +359,7 @@ void adb_task_host(void *pvParameters) {
else
state = ADB_S_KEEP;
}
ESP_LOGD("ADB", "Check mouse presence %x", data);
ESP_LOGD("ADB", "Check mouse presence %04x", data);
}
}
}
@ -392,11 +492,15 @@ static inline void adb_rx_setup() {
}
static inline void adb_tx_as() {
/* send attention (800 µs low) + sync (70 µs high) */
/*
* send attention (800 µs low) + sync (65 µs high).
* AN591 mentions 70 µs which is a mistake, make some devices angry
*/
gpio_set_level(GPIO_ADB, 0);
ets_delay_us(800-1);
esp_rom_delay_us(800-1);
gpio_set_level(GPIO_ADB, 1);
ets_delay_us(70-1);
esp_rom_delay_us(65-1);
}
void IRAM_ATTR adb_tx_cmd(unsigned char cmd) {
@ -414,13 +518,12 @@ void IRAM_ATTR adb_tx_cmd(unsigned char cmd) {
cmd & 0x01 ? adb_tx_one() : adb_tx_zero();
/* stop bit */
adb_tx_zero();
adb_tx_stop();
adb_rx_setup();
}
void IRAM_ATTR adb_tx_data(uint16_t data) {
adb_tx_setup();
adb_tx_one();
/* send data 2 bytes (unrolled loop) */
@ -442,24 +545,27 @@ void IRAM_ATTR adb_tx_data(uint16_t data) {
data & 0x01 ? adb_tx_one() : adb_tx_zero();
/* stop bit */
adb_tx_zero();
adb_tx_stop();
adb_rx_setup();
}
void adb_tx_listen(unsigned char cmd, uint16_t data) {
adb_tx_cmd(cmd);
/* Stop to start is between 160-240µS. Go for around 160 + time for GPIO setup */
ets_delay_us(160);
/*
* Stop to start is between 160-240µS. Go for around 160 + time for GPIO setup
* values from AN591 Datasheet minus the estimated call to esp_rom_delay_us
*/
esp_rom_delay_us(160);
adb_tx_data(data);
}
static inline void adb_tx_one() {
/* values from AN591 Datasheet minus the estimated call to ets_delay_us */
gpio_set_level(GPIO_ADB, 0);
ets_delay_us(ADB_1_LOW - 1);
esp_rom_delay_us(ADB_1_LOW - 1);
gpio_set_level(GPIO_ADB, 1);
ets_delay_us(ADB_1_HIGH - 1);
esp_rom_delay_us(ADB_1_HIGH - 1);
}
void adb_tx_reset() {
@ -470,9 +576,9 @@ void adb_tx_reset() {
*/
gpio_set_level(GPIO_ADB, 0);
ets_delay_us(ADB_RESET);
esp_rom_delay_us(ADB_RESET);
gpio_set_level(GPIO_ADB, 1);
ets_delay_us(500);
esp_rom_delay_us(500);
}
static inline void adb_tx_setup() {
@ -480,10 +586,16 @@ static inline void adb_tx_setup() {
gpio_set_level(GPIO_DIR, 1);
}
static inline void adb_tx_zero() {
/* values from AN591 Datasheet minus the estimated call to ets_delay_us */
static inline void adb_tx_stop() {
gpio_set_level(GPIO_ADB, 0);
ets_delay_us(ADB_0_LOW - 1);
esp_rom_delay_us(ADB_S_LOW - 1);
gpio_set_level(GPIO_ADB, 1);
ets_delay_us(ADB_0_HIGH - 1);
esp_rom_delay_us(ADB_S_HIGH - 1);
}
static inline void adb_tx_zero() {
gpio_set_level(GPIO_ADB, 0);
esp_rom_delay_us(ADB_0_LOW - 1);
gpio_set_level(GPIO_ADB, 1);
esp_rom_delay_us(ADB_0_HIGH - 1);
}

View File

@ -43,6 +43,8 @@ void adb_tx_reset(void);
#define ADB_0_HIGH 35
#define ADB_1_LOW 35
#define ADB_1_HIGH 65
#define ADB_S_LOW 70
#define ADB_S_HIGH 30
/* Classic Apple Mouse Protocol bitmasks */
#define ADB_CMP_B1 (1<<15)
@ -55,19 +57,27 @@ void adb_tx_reset(void);
#define ADB_LISTEN 0x8
#define ADB_FLUSH 0x1
#define ADB_REG0 0x0
#define ADB_REG1 0x1
#define ADB_REG2 0x2
#define ADB_REG3 0x3
/* Device addresses */
#define ADB_MOUSE (3<<4)
#define ADB_TMP (9<<4)
#define ADB_MOUSE (3<<4) // main device
#define ADB_MACAJ (5<<4) // lost MacAlly Joystick
#define ADB_KML1 (8<<4) // disabled Kensington device
#define ADB_TMP (9<<4) // temporary device
/* Various stuff */
#define ADB_B_UP 0
#define ADB_B_DOWN 1
#define ADB_H_ALL 0xff // Handlers bitmask
#define ADB_H_ERR 0x00 // Handler 0 (Self init error)
#define ADB_H_C100 0x01 // Handler 1 (Classic @ 100cpi)
#define ADB_H_C200 0x02 // Handler 2 (Classic @ 200cpi)
#define ADB_H_MACJ 0x03 // Handler for MacAlly Joystick
#define ADB_H_MTRC 0x2f // Handler for MacTRAC 2.0
#define ADB_H_KSGT 0x32 // Handler for Kensington devices
#define ADB_H_MOVE 0xfe // Move to another address
/* Host states */

View File

@ -25,6 +25,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
@ -33,12 +34,17 @@
#include "nvs_flash.h"
#include "esp_bt.h"
#include "esp_bt_defs.h"
#if CONFIG_BT_BLE_ENABLED
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gatt_defs.h"
#endif
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_timer.h"
#include "esp_hid_common.h"
#include "esp_hidd.h"
#include "esp_hidh.h"
#include "esp_hid_gap.h"
@ -48,6 +54,8 @@
#include "gpio.h"
#include "led.h"
#include "m4848.h"
#include "mighty.h"
#include "wii.h"
/* debug tag */
static const char *TAG = "blue";
@ -72,8 +80,8 @@ static esp_hid_raw_report_map_t *blue_hid_rm_get(esp_hidh_dev_t *dev);
void blue_set_boot_protocol(esp_hidh_dev_t *dev);
static bool blue_support_boot(esp_hidh_dev_t *dev);
/* direct calls to bluedroid */
extern void BTA_HhSetProtoMode(uint8_t handle, uint8_t t_type);
/* direct calls to esp_hid_* */
void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param);
/* Device specific functions blue_d_* */
static void blue_d_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
@ -130,22 +138,21 @@ static void blue_d_connect() {
static void blue_d_disconnect(esp_hidd_event_data_t *dev) {
ESP_LOGI(TAG, "Host disconnected, reason: %s",
esp_hid_disconnect_reason_str(esp_hidd_dev_transport_get(dev->disconnect.dev), dev->disconnect.reason));
#if CONFIG_BT_BLE_ENABLED
esp_hid_ble_gap_adv_start();
#endif
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
}
static void blue_d_init() {
esp_err_t ret;
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_MOUSE, m4848_config.device_name);
ESP_ERROR_CHECK( ret );
if ((ret = esp_ble_gatts_register_callback(esp_hidd_gatts_event_handler)) != ESP_OK) {
ESP_LOGE(TAG, "GATTS register callback failed: %d", ret);
if ((ret = esp_bt_gap_register_callback(bt_gap_event_handler)) != ESP_OK) {
ESP_LOGE(TAG, "BT GAP register callback failed: %d", ret);
return;
}
ESP_ERROR_CHECK(esp_hidd_dev_init(&m4848_config, ESP_HID_TRANSPORT_BLE, blue_d_callback, &hid_dev));
ESP_ERROR_CHECK(esp_hidd_dev_init(&m4848_config, ESP_HID_TRANSPORT_BT, blue_d_callback, &hid_dev));
xTaskCreate(blue_adb2hid, "ADB2BT", 2 * 1024, NULL, tskIDLE_PRIORITY + 1, &t_adb2hid);
}
@ -158,19 +165,23 @@ static void blue_d_start()
{
ESP_LOGD(TAG, "Bluetooth stack started");
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
#if CONFIG_BT_BLE_ENABLED
esp_hid_ble_gap_adv_start();
#endif
}
/*
* Called by the ADB stack from adb_task_host on mouse activity
* Convert the 16bit ADB data to a 3 bytes HID INPUT REPORT matching the m4848
*
* The format is also BOOT compatible so don't bother to check what mode we are in
*/
void blue_adb2hid(void *pvParameters) {
uint16_t data = 0;
uint8_t buffer[3] = {0, 0, 0};
int8_t move = 0;
unsigned int tmp;
long unsigned int tmp;
ESP_LOGD(TAG, "ADB2BT started on core %d", xPortGetCoreID());
@ -222,140 +233,36 @@ void blue_adb2hid(void *pvParameters) {
}
}
/* Host specific functions blue_h_* */
void blue_h_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
{
esp_hidh_event_t event = (esp_hidh_event_t)id;
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
const uint8_t *bda = NULL;
/* union described in include/esp_hidh.h */
switch (event) {
case ESP_HIDH_OPEN_EVENT: {
esp_hidh_dev_dump(param->open.dev, stdout);
blue_h_open(param);
break;
}
case ESP_HIDH_BATTERY_EVENT: {
bda = esp_hidh_dev_bda_get(param->battery.dev);
ESP_LOGI(TAG, ESP_BD_ADDR_STR " battery: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
break;
}
case ESP_HIDH_INPUT_EVENT: {
//ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
xTaskNotify(t_yellow, LED_ONCE, eSetValueWithOverwrite);
blue_h_input(param->input.dev, param->input.data, param->input.length);
break;
}
case ESP_HIDH_FEATURE_EVENT: {
bda = esp_hidh_dev_bda_get(param->feature.dev);
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, param->feature.length);
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
break;
}
case ESP_HIDH_CLOSE_EVENT: {
blue_h_close(param);
break;
}
default:
ESP_LOGI(TAG, "Unknwown event: %d", event);
}
}
void blue_h_close(esp_hidh_event_data_t *p) {
const uint8_t *bda = NULL;
configASSERT(p != NULL);
bda = esp_hidh_dev_bda_get(p->close.dev);
ESP_LOGI(TAG, "closed connection with device " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
esp_hidh_dev_free(p->close.dev);
blue_pointers--;
if (! blue_pointers)
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
}
static void blue_handle_button(uint8_t buttons) {
static bool status = BLUE_BUTTON_N; // Keep state
buttons = buttons & (BLUE_BUTTON_1 | BLUE_BUTTON_2 | BLUE_BUTTON_3);
if (status && buttons)
return ;
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_N)
return ;
/* release button */
if (status && buttons == BLUE_BUTTON_N) {
xTaskNotify(t_click, 0, eSetValueWithOverwrite);
status = 0;
ESP_LOGD(TAG, "button released");
return ;
}
/* press button */
if (status == BLUE_BUTTON_N && buttons) {
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
status = 1;
ESP_LOGD(TAG, "button pressed");
return ;
}
}
static void blue_h_init(void) {
esp_hidh_config_t config = {
.callback = blue_h_callback,
.event_stack_size = 4096
};
ESP_ERROR_CHECK(esp_hidh_init(&config));
blue_pointers = 0;
xTaskCreatePinnedToCore(blue_scan, "blue_scan", 6 * 1024, NULL, 2, NULL, 0);
}
/*
* Bluetooth common init: init module and various stuff
* Host or Device specific inits go in blue_d_init() or blue_h_init()
* this function try to guess wich pin code to use when asked for one
* it just harass the different drivers and get a pin code from the driver
*/
void blue_init(void)
uint8_t blue_get_pin_code(esp_bd_addr_t bda, esp_bt_pin_code_t pin)
{
esp_err_t ret;
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
if (wii_is_nintendo_bda(bda)) {
wii_generate_pin(pin);
ESP_LOGD(TAG, "Nintendo device asking for pin, trying with pin " WII_PIN_STR,
WII_PIN_HEX(pin));
return 6;
}
ESP_LOGD(TAG, "Starting Bluetooth init on core %d", xPortGetCoreID());
ESP_ERROR_CHECK(ret);
/* ESP_BT_MODE_CLASSIC_BT doesn't work, it freezes esp_hidh_init */
ESP_ERROR_CHECK(esp_hid_gap_init(ESP_BT_MODE_BTDM));
ESP_ERROR_CHECK(esp_ble_gattc_register_callback(esp_hidh_gattc_event_handler));
/* let's try with default pin */
ESP_LOGI(TAG, "no match found, trying with default pin code: 1234");
pin[0] = '1';
pin[0] = '2';
pin[0] = '3';
pin[0] = '4';
esp_log_level_set("event", ESP_LOG_INFO);
/* complains about wrong data len on BOOT mode and CCONTROL */
esp_log_level_set("BT_HIDH", ESP_LOG_ERROR);
/*
* at this point, everything but bluetooth is started.
* put green steady, start blinking blue and keep scanning until a device is found
*/
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
xTaskNotify(t_blue, LED_FAST, eSetValueWithOverwrite);
if (adb_is_host())
blue_d_init();
else
blue_h_init();
return 4;
}
/************************************
* Host specific functions blue_h_* *
************************************/
/*
* data follows HID Mouse boot protocol format
* this function will decode a boot protocol frame and convert it for quadrature tasks
* data arg follows HID Mouse boot protocol format
*
* Byte | Bits | Description
* -----+------+---------------------------------------------------------------
@ -368,13 +275,26 @@ void blue_init(void)
* 3+ | 0-7 | Device specific, usually 3 is scroll wheel, usually unused (0)
*/
void blue_h_input(esp_hidh_dev_t *dev, uint8_t *data, uint16_t length) {
void blue_h_boot(uint8_t *data, uint16_t length) {
uint8_t buttons;
uint8_t i;
int8_t x, y;
buttons = data[0];
/*
* A friend of mine did this for a beer, it helps a little with high DPI mouses
* This is a precalculated table that looks like a squared arctan()
*/
const unsigned char hid2quad[] = {
0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F,
0x11, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x1A, 0x1C, 0x1D, 0x1E, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28
};
/*
* Do some checks before parsing data. We sould't get anything wrong in theory,
* but we sometimes to get shit from either mouse or bluetooth stack
@ -405,49 +325,248 @@ void blue_h_input(esp_hidh_dev_t *dev, uint8_t *data, uint16_t length) {
blue_handle_button(buttons);
/* quadrature use 7 bits x/y offsets, bluetooth BOOT uses 8 bits */
/*
* this try to help avoiding issues with high DPI mouses:
* - reduce bluetooth movement speed a little bit before 40 pixels per poll
* - go slower after 40 pixels per poll just to avoid too big jumps
*/
x = data[1];
y = data[2];
/* reduce bluetooth movement speed as well before notifying */
if (x != 0) {
if (x == 1 || x == -1) {
xQueueSendToBack(q_qx, &x, 0);
if (x < 0) {
if (x < -40)
x = ((x + 40) / 2) - 40;
else
x = -1 * hid2quad[(x * -1) - 1];
}
else {
x /= 2;
xQueueSendToBack(q_qx, &x, 0);
if (x > 40)
x = ((x - 40) / 2) + 40;
else
x = hid2quad[x - 1];
}
xQueueSendToBack(q_qx, &x, 0);
}
if (y != 0) {
if (y == 1 || y == -1) {
xQueueSendToBack(q_qy, &y, 0);
if (y < 0) {
if (y < -40)
y = ((y + 40) / 2) - 40;
else
y = -1 * hid2quad[(y * -1) - 1];
}
else {
y /= 2;
xQueueSendToBack(q_qy, &y, 0);
if (y > 40)
y = ((y - 40) / 2) + 40;
else
y = hid2quad[y - 1];
}
xQueueSendToBack(q_qy, &y, 0);
}
}
void blue_h_open(esp_hidh_event_data_t *p) {
const uint8_t *bda = NULL;
void blue_h_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
{
esp_hidh_event_t event = (esp_hidh_event_t)id;
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
const uint8_t *bda = NULL;
/* union described in include/esp_hidh.h */
switch (event) {
case ESP_HIDH_OPEN_EVENT: {
esp_hidh_dev_dump(param->open.dev, stdout);
blue_h_open(param);
break;
}
case ESP_HIDH_BATTERY_EVENT: {
bda = esp_hidh_dev_bda_get(param->battery.dev);
ESP_LOGI(TAG, ESP_BD_ADDR_STR " battery: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
break;
}
case ESP_HIDH_INPUT_EVENT: {
//ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
xTaskNotify(t_yellow, LED_ONCE, eSetValueWithOverwrite);
blue_h_input(param->input.dev, param->input.report_id, param->input.data, param->input.length);
break;
}
case ESP_HIDH_FEATURE_EVENT: {
bda = esp_hidh_dev_bda_get(param->feature.dev);
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, param->feature.length);
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
break;
}
case ESP_HIDH_CLOSE_EVENT: {
blue_h_close(param);
break;
}
default:
ESP_LOGI(TAG, "Unknwown event: %d", event);
}
}
void blue_h_close(esp_hidh_event_data_t *p) {
const uint8_t *bda = NULL;
configASSERT(p != NULL);
bda = esp_hidh_dev_bda_get(p->open.dev);
ESP_LOGI(TAG, "opened connection with " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
bda = esp_hidh_dev_bda_get(p->close.dev);
ESP_LOGI(TAG, "closed connection with device " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
esp_hidh_dev_free(p->close.dev);
/* Dump various info on console */
blue_hid_rm_get(p->open.dev);
blue_set_boot_protocol(p->open.dev);
if (blue_support_boot(p->open.dev) == false) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
ESP_LOGE(TAG, "Mouse does not support boot protocol !");
blue_pointers--;
if (! blue_pointers)
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
}
/*
* this function dispatch the input message to the correct driver
* the detection is based on Product ID + Vendor ID lists that each driver provides
* if no specific driver claims that combinaison, try with the generic boot driver
*/
void blue_h_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length) {
uint16_t vid = esp_hidh_dev_vendor_id_get(dev);
uint16_t pid = esp_hidh_dev_product_id_get(dev);
if (mighty_is_mm(vid, pid)) {
mighty_input(dev, id, data, length);
return;
}
if (wii_is_wiimote(vid, pid)) {
wii_input(dev, id, data, length);
return;
}
blue_h_boot(data, length);
}
static void blue_h_init(void) {
esp_hidh_config_t config = {
.callback = blue_h_callback,
.event_stack_size = 4096
};
ESP_ERROR_CHECK(esp_hidh_init(&config));
blue_pointers = 0;
xTaskCreatePinnedToCore(blue_scan, "blue_scan", 6 * 1024, NULL, 2, NULL, 0);
/* bluetooth stack ready at this point, initialise wii stuff there */
wii_init();
}
static void blue_handle_button(uint8_t buttons) {
static bool locked = false;
static bool releasable = true;
static bool status = BLUE_BUTTON_N; // Keep state
buttons = buttons & (BLUE_BUTTON_1 | BLUE_BUTTON_2 | BLUE_BUTTON_3);
if (status && buttons)
return ;
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_N && !locked)
return ;
if (buttons == BLUE_BUTTON_2 && locked && !releasable)
return ;
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_N && locked) {
releasable = true;
return ;
}
/* release button */
if (status && buttons == BLUE_BUTTON_N && !locked) {
xTaskNotify(t_click, 0, eSetValueWithOverwrite);
status = 0;
ESP_LOGD(TAG, "button released");
return ;
}
/* stick button on right click */
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_2 && !locked) {
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
locked = true;
releasable = false;
ESP_LOGD(TAG, "button locked");
return ;
}
/* press button (simple click) */
if (status == BLUE_BUTTON_N && buttons) {
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
locked = false;
status = 1;
ESP_LOGD(TAG, "button pressed");
return ;
}
}
/*
* Bluetooth common init: init module and various stuff
* Host or Device specific inits go in blue_d_init() or blue_h_init()
*/
void blue_init(void)
{
esp_err_t ret;
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_LOGD(TAG, "Starting Bluetooth init on core %d", xPortGetCoreID());
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_hid_gap_init(ESP_BT_MODE_CLASSIC_BT));
esp_log_level_set("event", ESP_LOG_INFO);
/* complains about wrong data len on BOOT mode and CCONTROL */
esp_log_level_set("BT_HIDH", ESP_LOG_VERBOSE);
/*
* at this point, everything but bluetooth is started.
* put green steady, start blinking blue and keep scanning until a device is found
*
* starting IDF 5.2.2, sometimes the first xTaskNotify() call may be lost. why ?
*/
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
xTaskNotify(t_blue, LED_FAST, eSetValueWithOverwrite);
if (adb_is_host())
blue_d_init();
else
blue_h_init();
}
void blue_h_open(esp_hidh_event_data_t *p) {
const uint8_t *bda = NULL;
bool generic = true;
configASSERT(p != NULL);
uint16_t vid = esp_hidh_dev_vendor_id_get(p->open.dev);
uint16_t pid = esp_hidh_dev_product_id_get(p->open.dev);
bda = esp_hidh_dev_bda_get(p->open.dev);
ESP_LOGI(TAG, "opened connection with " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
if (wii_is_wiimote(vid, pid)) {
wii_open(bda);
generic = false;
}
/* Dump various info on console for generic devices */
if (generic) {
blue_hid_rm_get(p->open.dev);
blue_set_boot_protocol(p->open.dev);
if (blue_support_boot(p->open.dev) == false) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
ESP_LOGE(TAG, "Mouse does not support boot protocol !");
}
}
blue_pointers++;
xTaskNotify(t_blue, LED_ON, eSetValueWithOverwrite);
gpio_output_enable();
xTaskNotify(t_blue, LED_ON, eSetValueWithOverwrite);
gpio_transceiver_enable();
}
/* get specific report from report map matching specified usage + type + protocol */
@ -464,7 +583,7 @@ static esp_hid_report_item_t blue_h_ri_find(esp_hidh_dev_t *d, esp_hid_usage_t u
memset(&search, 0, sizeof(esp_hid_report_item_t));
for (uint8_t i = 0; i < num_reports; i++) {
if (reports[i].protocol_mode == p && reports[i].report_type == t && reports[i].usage == u) {
if (reports[i].protocol_mode == p && reports[i].report_type == t && reports[i].usage == u) {
memcpy(&search, &reports[i], sizeof(esp_hid_report_item_t));
break;
}
@ -506,98 +625,68 @@ static esp_hid_raw_report_map_t *blue_hid_rm_get(esp_hidh_dev_t *dev) {
}
void blue_scan(void *pvParameters) {
size_t len = 0;
esp_hid_scan_result_t *mouse = NULL;
esp_hid_scan_result_t *results = NULL;
size_t len = 0;
esp_hid_scan_result_t *mouse = NULL;
esp_hid_scan_result_t *results = NULL;
esp_hid_scan_result_t *wii = NULL;
ESP_LOGI(TAG, "starting scan on core %d…", xPortGetCoreID());
esp_hid_scan(BLUE_SCAN_DURATION, &len, &results);
ESP_LOGI(TAG, "scan returned %u result(s)", len);
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
ESP_LOGI(TAG, "starting scan on core %d…", xPortGetCoreID());
esp_hid_scan(BLUE_SCAN_DURATION, &len, &results);
ESP_LOGI(TAG, "scan returned %u result(s)", len);
if (len) {
esp_hid_scan_result_t *r = results;
/* don't put the slow blink is a device reconnected while scanning */
if (blue_pointers == 0)
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
while (r) {
ESP_LOGI(TAG, "found %s %s device: " ESP_BD_ADDR_STR ", RSSI: %d, NAME: %s",
(r->transport == ESP_HID_TRANSPORT_BLE) ? "BLE" : "BT",
esp_hid_cod_major_str(r->bt.cod.major),
ESP_BD_ADDR_HEX(r->bda), r->rssi, r->name ? r->name : "");
if (len) {
esp_hid_scan_result_t *r = results;
/* search for something that looks like Bluetooth Classic mouse */
if (r->transport == ESP_HID_TRANSPORT_BT &&
strcmp("PERIPHERAL", esp_hid_cod_major_str(r->bt.cod.major)) == 0
&& (r->bt.cod.minor & ESP_HID_COD_MIN_MOUSE)) {
mouse = r;
}
r = r->next;
}
/*
* as of v1.4.5, esp_hid_gap will print detected devices in console (handle_bt_device_result())
* just look for bluetooth classic devices that may be supported
*/
while (r) {
if (r->transport == ESP_HID_TRANSPORT_BT &&
strcmp("PERIPHERAL", esp_hid_cod_major_str(r->bt.cod.major)) == 0) {
/* try to connect to the last candidate found */
if (mouse)
esp_hidh_dev_open(mouse->bda, mouse->transport, mouse->ble.addr_type);
else
ESP_LOGI(TAG, "devices found but no mouse detected");
/* look for something that look like a mouse… */
if (r->bt.cod.minor & ESP_HID_COD_MIN_MOUSE)
mouse = r;
esp_hid_scan_results_free(results);
}
/* … or a wiimote (COD minor 1 is JOYSTICK) */
if (r->bt.cod.minor == 1 && wii_is_nintendo_bda(r->bda))
wii = r;
}
r = r->next;
}
}
vTaskDelete(NULL);
/* try to connect to the last candidate found */
if (mouse)
#if CONFIG_BT_BLE_ENABLED
esp_hidh_dev_open(mouse->bda, mouse->transport, mouse->ble.addr_type);
#else
esp_hidh_dev_open(mouse->bda, mouse->transport, 0);
#endif
if (wii)
esp_hidh_dev_open(wii->bda, wii->transport, 0);
if (len && (!mouse && !wii)) {
ESP_LOGI(TAG, "%i devices found, but no mouse or wiimote detected", len);
esp_hid_scan_results_free(results);
}
if (len == 0) {
ESP_LOGI(TAG, "no new mouse or wiimote detected");
}
vTaskDelete(NULL);
}
void blue_set_boot_protocol(esp_hidh_dev_t *dev) {
configASSERT(dev != NULL);
/*
* /!\ Disclaimer /!\
* This is ugly. We are accessing directly bluedroid and need the hidden handle to do that
* Extract it from a private esp_hidh_dev_s struct and call BTA_HhSetProtoMode directly.
*/
struct decoy_dev_s {
struct esp_hidh_dev_s *next;
esp_hid_device_config_t config;
esp_hid_usage_t usage;
esp_hid_transport_t transport;
bool connected;
bool opened;
int status;
size_t reports_len;
void *reports;
void *tmp;
size_t tmp_len;
xSemaphoreHandle semaphore;
esp_err_t (*close) (esp_hidh_dev_t *dev);
esp_err_t (*report_write) (esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t len);
esp_err_t (*report_read) (esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, size_t max_length, uint8_t *value, size_t *value_len);
void (*dump) (esp_hidh_dev_t *dev, FILE *fp);
esp_bd_addr_t bda;
struct {
esp_bt_cod_t cod;
int handle;
uint8_t sub_class;
uint8_t app_id;
uint16_t attr_mask;
} bt;
TAILQ_ENTRY(esp_hidh_dev_s) devices;
};
struct decoy_dev_s *pass_that_handle;
pass_that_handle = (struct decoy_dev_s *)dev;
ESP_LOGI(TAG, "switching " ESP_BD_ADDR_STR " (%i) to protocol mode boot" ,
ESP_BD_ADDR_HEX(pass_that_handle->bda), pass_that_handle->bt.handle);
//ESP_LOG_BUFFER_HEX(TAG, dev, sizeof(struct decoy_dev_s));
/* bluedroid/bta/include/bta_hh_api.h */
BTA_HhSetProtoMode(pass_that_handle->bt.handle, 0x01);
ESP_LOGI(TAG, "switching " ESP_BD_ADDR_STR " to protocol mode boot", ESP_BD_ADDR_HEX(esp_hidh_dev_bda_get(dev)));
esp_hidh_dev_set_protocol(dev, ESP_HID_PROTOCOL_MODE_BOOT);
}
static bool blue_support_boot(esp_hidh_dev_t *dev) {

View File

@ -19,8 +19,7 @@
*
*/
#ifndef BLUE_H
#define BLUE_H
#pragma once
/* defines */
#define BLUE_SCAN_DURATION 6
@ -31,19 +30,19 @@
#define BLUE_BUTTON_1 (1 << 0)
#define BLUE_BUTTON_2 (1 << 1)
#define BLUE_BUTTON_3 (1 << 2)
#define BLUE_BUTTON_E 0xF8 /* shouldn't happen */
#define BLUE_BUTTON_E 0xF8 /* shouldn't happen */
/* prototypes */
void blue_adb2hid(void *pvParameters);
void blue_init(void);
void blue_h_input(esp_hidh_dev_t *dev, uint8_t *data, uint16_t length);
void blue_h_close(esp_hidh_event_data_t *p);
void blue_h_open(esp_hidh_event_data_t *p);
void blue_scan(void *pvParameters);
uint8_t blue_get_pin_code(esp_bd_addr_t bda, esp_bt_pin_code_t pin);
void blue_h_boot(uint8_t *data, uint16_t length);
void blue_h_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length);
void blue_h_close(esp_hidh_event_data_t *p);
void blue_h_open(esp_hidh_event_data_t *p);
void blue_scan(void *pvParameters);
void blue_set_boot_protocol(esp_hidh_dev_t *dev);
/* global variables for tasks handles */
extern TaskHandle_t t_green, t_blue, t_yellow, t_red;
#endif

View File

@ -1,19 +1,13 @@
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@ -24,8 +18,11 @@
static const char *TAG = "ESP_HID_GAP";
// uncomment to print all devices that were seen during a scan
#define GAP_DBG_PRINTF(...) //printf(__VA_ARGS__)
//static const char * gap_bt_prop_type_names[5] = {"","BDNAME","COD","RSSI","EIR"};
#define GAP_DBG_PRINTF(...) printf(__VA_ARGS__)
#if CONFIG_BT_HID_HOST_ENABLED
static const char * gap_bt_prop_type_names[5] = {"","BDNAME","COD","RSSI","EIR"};
#endif
static esp_hid_scan_result_t *bt_scan_results = NULL;
static size_t num_bt_scan_results = 0;
@ -33,11 +30,11 @@ static size_t num_bt_scan_results = 0;
static esp_hid_scan_result_t *ble_scan_results = NULL;
static size_t num_ble_scan_results = 0;
static xSemaphoreHandle bt_hidh_cb_semaphore = NULL;
static SemaphoreHandle_t bt_hidh_cb_semaphore = NULL;
#define WAIT_BT_CB() xSemaphoreTake(bt_hidh_cb_semaphore, portMAX_DELAY)
#define SEND_BT_CB() xSemaphoreGive(bt_hidh_cb_semaphore)
static xSemaphoreHandle ble_hidh_cb_semaphore = NULL;
static SemaphoreHandle_t ble_hidh_cb_semaphore = NULL;
#define WAIT_BLE_CB() xSemaphoreTake(ble_hidh_cb_semaphore, portMAX_DELAY)
#define SEND_BLE_CB() xSemaphoreGive(ble_hidh_cb_semaphore)
@ -71,6 +68,7 @@ const char *bt_gap_evt_str(uint8_t event)
return bt_gap_evt_names[event];
}
#if CONFIG_BT_BLE_ENABLED
const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
{
const char *key_str = NULL;
@ -109,6 +107,7 @@ const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
}
return key_str;
}
#endif /* CONFIG_BT_BLE_ENABLED */
void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
{
@ -123,6 +122,7 @@ void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
}
}
#if (CONFIG_BT_HID_HOST_ENABLED || CONFIG_BT_BLE_ENABLED)
static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results)
{
esp_hid_scan_result_t *r = results;
@ -134,7 +134,9 @@ static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_r
}
return NULL;
}
#endif /* (CONFIG_BT_HID_HOST_ENABLED || CONFIG_BT_BLE_ENABLED) */
#if CONFIG_BT_HID_HOST_ENABLED
static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi)
{
esp_hid_scan_result_t *r = find_scan_result(bda, bt_scan_results);
@ -186,7 +188,9 @@ static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid
bt_scan_results = r;
num_bt_scan_results++;
}
#endif
#if CONFIG_BT_BLE_ENABLED
static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi)
{
if (find_scan_result(bda, ble_scan_results)) {
@ -220,13 +224,14 @@ static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type
ble_scan_results = r;
num_ble_scan_results++;
}
#endif /* CONFIG_BT_BLE_ENABLED */
void print_uuid(esp_bt_uuid_t *uuid)
{
if (uuid->len == ESP_UUID_LEN_16) {
GAP_DBG_PRINTF("UUID16: 0x%04x", uuid->uuid.uuid16);
} else if (uuid->len == ESP_UUID_LEN_32) {
GAP_DBG_PRINTF("UUID32: 0x%08x", uuid->uuid.uuid32);
GAP_DBG_PRINTF("UUID32: 0x%08"PRIx32, uuid->uuid.uuid32);
} else if (uuid->len == ESP_UUID_LEN_128) {
GAP_DBG_PRINTF("UUID128: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", uuid->uuid.uuid128[0],
uuid->uuid.uuid128[1], uuid->uuid.uuid128[2], uuid->uuid.uuid128[3],
@ -237,7 +242,8 @@ void print_uuid(esp_bt_uuid_t *uuid)
}
}
static void handle_bt_device_result(struct disc_res_param *disc_res)
#if CONFIG_BT_HID_HOST_ENABLED
void handle_bt_device_result(struct disc_res_param *disc_res)
{
GAP_DBG_PRINTF("BT : " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(disc_res->bda));
uint32_t codv = 0;
@ -325,7 +331,9 @@ static void handle_bt_device_result(struct disc_res_param *disc_res)
add_bt_scan_result(disc_res->bda, cod, &uuid, name, name_len, rssi);
}
}
#endif
#if CONFIG_BT_BLE_ENABLED
static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst)
{
@ -371,13 +379,17 @@ static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst)
add_ble_scan_result(scan_rst->bda, scan_rst->ble_addr_type, appearance, adv_name, adv_name_len, scan_rst->rssi);
}
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_HID_HOST_ENABLED
/*
* BT GAP
* */
static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
/* quack patch */
uint8_t blue_get_pin_code(esp_bd_addr_t bda, esp_bt_pin_code_t pin);
void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch (event) {
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: {
@ -391,11 +403,38 @@ static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_para
handle_bt_device_result(&param->disc_res);
break;
}
#if (CONFIG_EXAMPLE_SSP_ENABLED)
case ESP_BT_GAP_KEY_NOTIF_EVT:
ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%d", param->key_notif.passkey);
ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%"PRIu32, param->key_notif.passkey);
break;
case ESP_BT_GAP_CFM_REQ_EVT: {
ESP_LOGI(TAG, "BT GAP CFM_REQ_EVT Please compare the numeric value: %"PRIu32, param->cfm_req.num_val);
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
break;
}
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(TAG, "BT GAP KEY_REQ_EVT Please enter passkey!");
break;
#endif
case ESP_BT_GAP_MODE_CHG_EVT:
ESP_LOGI(TAG, "BT GAP MODE_CHG_EVT mode:%d", param->mode_chg.mode);
break;
case ESP_BT_GAP_PIN_REQ_EVT: {
ESP_LOGI(TAG, "BT GAP PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
if (param->pin_req.min_16_digit) {
ESP_LOGI(TAG, "Input pin code: 0000 0000 0000 0000");
esp_bt_pin_code_t pin_code = {0};
esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
} else {
esp_bt_pin_code_t pin_code;
uint8_t pin_len;
pin_len = blue_get_pin_code(param->pin_req.bda, pin_code);
esp_bt_gap_pin_reply(param->pin_req.bda, true, pin_len, pin_code);
}
break;
}
default:
ESP_LOGV(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event));
ESP_LOGW(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event));
break;
}
}
@ -403,20 +442,19 @@ static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_para
static esp_err_t init_bt_gap(void)
{
esp_err_t ret;
#if (CONFIG_BT_SSP_ENABLED)
/* Set default parameters for Secure Simple Pairing */
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
#endif
/*
* Set default parameters for Legacy Pairing
* Use fixed pin code
* Use variable pin, input pin code when pairing
*/
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
esp_bt_pin_code_t pin_code;
pin_code[0] = '0';
pin_code[1] = '0';
pin_code[2] = '0';
pin_code[3] = '0';
esp_bt_gap_set_pin(pin_type, 4, pin_code);
esp_bt_gap_set_pin(pin_type, 0, pin_code);
if ((ret = esp_bt_gap_register_callback(bt_gap_event_handler)) != ESP_OK) {
ESP_LOGE(TAG, "esp_bt_gap_register_callback failed: %d", ret);
@ -440,7 +478,9 @@ static esp_err_t start_bt_scan(uint32_t seconds)
}
return ret;
}
#endif
#if CONFIG_BT_BLE_ENABLED
/*
* BLE GAP
* */
@ -506,13 +546,13 @@ static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_p
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // ESP_IO_CAP_OUT
// The app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
// Show the passkey number to the user to input it in the peer device.
ESP_LOGI(TAG, "BLE GAP PASSKEY_NOTIF passkey:%d", param->ble_security.key_notif.passkey);
ESP_LOGI(TAG, "BLE GAP PASSKEY_NOTIF passkey:%"PRIu32, param->ble_security.key_notif.passkey);
break;
case ESP_GAP_BLE_NC_REQ_EVT: // ESP_IO_CAP_IO
// The app will receive this event when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
// show the passkey number to the user to confirm it with the number displayed by peer device.
ESP_LOGI(TAG, "BLE GAP NC_REQ passkey:%d", param->ble_security.key_notif.passkey);
ESP_LOGI(TAG, "BLE GAP NC_REQ passkey:%"PRIu32, param->ble_security.key_notif.passkey);
esp_ble_confirm_reply(param->ble_security.key_notif.bd_addr, true);
break;
@ -662,6 +702,7 @@ esp_err_t esp_hid_ble_gap_adv_start(void)
};
return esp_ble_gap_start_advertising(&hidd_adv_params);
}
#endif /* CONFIG_BT_BLE_ENABLED */
/*
* CONTROLLER INIT
@ -671,11 +712,16 @@ static esp_err_t init_low_level(uint8_t mode)
{
esp_err_t ret;
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
#if CONFIG_IDF_TARGET_ESP32
bt_cfg.mode = mode;
#endif
#if CONFIG_BT_HID_HOST_ENABLED
if (mode & ESP_BT_MODE_CLASSIC_BT) {
bt_cfg.mode = mode;
bt_cfg.bt_max_acl_conn = 3;
bt_cfg.bt_max_sync_conn = 3;
} else {
} else
#endif
{
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
if (ret) {
ESP_LOGE(TAG, "esp_bt_controller_mem_release failed: %d", ret);
@ -705,26 +751,25 @@ static esp_err_t init_low_level(uint8_t mode)
ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", ret);
return ret;
}
#if CONFIG_BT_HID_HOST_ENABLED
if (mode & ESP_BT_MODE_CLASSIC_BT) {
ret = init_bt_gap();
if (ret) {
return ret;
}
}
#endif
#if CONFIG_BT_BLE_ENABLED
if (mode & ESP_BT_MODE_BLE) {
ret = init_ble_gap();
if (ret) {
return ret;
}
}
#endif /* CONFIG_BT_BLE_ENABLED */
return ret;
}
esp_err_t esp_hid_gap_init(uint8_t mode)
{
esp_err_t ret;
@ -771,14 +816,21 @@ esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_resul
return ESP_FAIL;
}
#if CONFIG_BT_BLE_ENABLED
if (start_ble_scan(seconds) == ESP_OK) {
if (start_bt_scan(seconds) == ESP_OK) {
WAIT_BT_CB();
}
WAIT_BLE_CB();
} else {
return ESP_FAIL;
}
#endif /* CONFIG_BT_BLE_ENABLED */
#if CONFIG_BT_HID_HOST_ENABLED
if (start_bt_scan(seconds) == ESP_OK) {
WAIT_BT_CB();
} else {
return ESP_FAIL;
}
#endif
*num_results = num_bt_scan_results + num_ble_scan_results;
*results = bt_scan_results;

View File

@ -1,31 +1,51 @@
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_HID_GAP_H_
#define _ESP_HID_GAP_H_
#define HIDH_IDLE_MODE 0x00
#define HIDH_BLE_MODE 0x01
#define HIDH_BT_MODE 0x02
#define HIDH_BTDM_MODE 0x03
#if CONFIG_BT_HID_HOST_ENABLED
#if CONFIG_BT_BLE_ENABLED
#define HID_HOST_MODE HIDH_BTDM_MODE
#else
#define HID_HOST_MODE HIDH_BT_MODE
#endif
#elif CONFIG_BT_BLE_ENABLED
#define HID_HOST_MODE HIDH_BLE_MODE
#elif CONFIG_BT_NIMBLE_ENABLED
#define HID_HOST_MODE HIDH_BLE_MODE
#else
#define HID_HOST_MODE HIDH_IDLE_MODE
#endif
#include "esp_err.h"
#include "esp_log.h"
#include "esp_bt.h"
#if !CONFIG_BT_NIMBLE_ENABLED
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#endif
#include "esp_hid_common.h"
#if CONFIG_BT_BLE_ENABLED
#include "esp_gattc_api.h"
#include "esp_gatt_defs.h"
#include "esp_gap_ble_api.h"
#include "esp_gap_bt_api.h"
#include "esp_hid_common.h"
#endif
#if CONFIG_BT_NIMBLE_ENABLED
#include "nimble/ble.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -33,13 +53,18 @@ extern "C" {
typedef struct esp_hidh_scan_result_s {
struct esp_hidh_scan_result_s *next;
#if CONFIG_BT_NIMBLE_ENABLED
uint8_t bda[6];
#else
esp_bd_addr_t bda;
#endif
esp_bd_addr_t bda;
const char *name;
int8_t rssi;
esp_hid_usage_t usage;
esp_hid_transport_t transport; //BT, BLE or USB
union {
#if !CONFIG_BT_NIMBLE_ENABLED
struct {
esp_bt_cod_t cod;
esp_bt_uuid_t uuid;
@ -48,6 +73,12 @@ typedef struct esp_hidh_scan_result_s {
esp_ble_addr_type_t addr_type;
uint16_t appearance;
} ble;
#else
struct {
uint8_t addr_type;
uint16_t appearance;
} ble;
#endif
};
} esp_hid_scan_result_t;
@ -58,8 +89,10 @@ void esp_hid_scan_results_free(esp_hid_scan_result_t *results);
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name);
esp_err_t esp_hid_ble_gap_adv_start(void);
#if !CONFIG_BT_NIMBLE_ENABLED
void print_uuid(esp_bt_uuid_t *uuid);
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
#endif
#ifdef __cplusplus
}

View File

@ -25,7 +25,6 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "gpio.h"
@ -74,10 +73,10 @@ void gpio_init(void) {
gpio_set_direction(GPIO_QY2, GPIO_MODE_OUTPUT);
}
void gpio_output_disable(void) {
void gpio_transceiver_disable(void) {
gpio_set_level(GPIO_OE, 1);
}
void gpio_output_enable(void) {
void gpio_transceiver_enable(void) {
gpio_set_level(GPIO_OE, 0);
}

View File

@ -24,8 +24,8 @@
/* prototypes */
void gpio_init(void);
void gpio_output_disable(void);
void gpio_output_enable(void);
void gpio_transceiver_disable(void);
void gpio_transceiver_enable(void);
/* GPIO pins definitions
*

View File

@ -27,7 +27,6 @@
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "led.h"
#include "gpio.h"
@ -164,7 +163,7 @@ const char *led_gpio_name(uint8_t id) {
}
void led_init(void) {
/* blink allds LEDs once */
/* blink alls LEDs once */
gpio_set_level(GPIO_GREENLED, 1);
gpio_set_level(GPIO_BLUELED, 1);
gpio_set_level(GPIO_YELLOWLED, 1);
@ -185,7 +184,7 @@ void led_init(void) {
void led_task(void *pvParameters) {
unsigned int color = (unsigned int)pvParameters;
unsigned int mode = LED_OFF;
long unsigned int mode = LED_OFF;
TickType_t wait = portMAX_DELAY;
/* start only if there is a led specified */

View File

@ -24,10 +24,12 @@
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_hidh.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_chip_info.h"
#include "esp_gap_bt_api.h"
#include "adb.h"
#include "blue.h"
@ -40,20 +42,22 @@ static const char* TAG = "quack";
void app_main(void)
{
uint32_t flash_size;
esp_flash_get_size(NULL, &flash_size);
/* Print chip information */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
ESP_LOGI(TAG, "This is %s chip with %d CPU cores, WiFi%s%s, "
"revision %d, %dMB %s flash",
"revision %d, %" PRIu32 "MB %s flash",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "",
chip_info.revision,
spi_flash_get_chip_size() / (1024 * 1024),
chip_info.revision, flash_size / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
ESP_LOGI(TAG, "Minimum free heap size: %d bytes", esp_get_minimum_free_heap_size());
ESP_LOGI(TAG, "Minimum free heap size: %" PRIu32 " bytes", esp_get_minimum_free_heap_size());
ESP_LOGI(TAG, "");
ESP_LOGI(TAG, "\\_o< \\_o< \\_o< \\_O<");
ESP_LOGI(TAG, "");
@ -70,7 +74,7 @@ void app_main(void)
if (gpio_get_level(GPIO_BTOFF) == 1)
blue_init();
else
gpio_output_enable();
gpio_transceiver_enable();
/* put LED error ON if no inputs (/BTOFF and no /ADBSRC) */
if (gpio_get_level(GPIO_BTOFF) == 0 && gpio_get_level(GPIO_ADBSRC) == 1) {

106
main/mighty.c Normal file
View File

@ -0,0 +1,106 @@
/*
* mighty.c
* mighty
*
* Created by Michel DEPEIGE on 03/03/2025.
* Copyright (c) 2025 Michel DEPEIGE.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdio.h>
#include <string.h>
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_chip_info.h"
#if !CONFIG_BT_NIMBLE_ENABLED
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#endif
#include "esp_hid_common.h"
#include "esp_hidh.h"
#include "esp_hidh_api.h"
#include "blue.h"
#include "led.h"
#include "mighty.h"
/* functions */
/*
* this function will decode a mighty frame and convert it for quadrature tasks
*
* Byte | Bits | Description
* -----+------+---------------------------------------------------------------
* 0 | 0 | Button 1 (left)
* 0 | 1 | Button 2 (right)
* 0 | 2 | Button 3 (middle)
* 0 | 3-7 | unused
* 1 | 0-7 | X Displacement
* 2 | 0-7 | Y Displacement
* 3 | 0-7 | Ball X Axis
* 4 | 0-7 | Ball Y Axis
* 5 | 0-7 | pressure sensor. Follow roughly (0)
*
* Original HID dump
* +-------------------------------------------------+
* | 05 01 09 02 a1 01 85 02 05 09 19 01 29 04 15 00 |
* | 09 01 a1 00 15 81 25 7f 09 30 09 31 75 08 95 02 |
* | 81 06 05 0c 0a 38 02 75 08 95 01 81 06 05 01 09 |
* | 38 75 08 95 01 81 06 c0 05 ff 09 c0 75 08 95 01 |
* | 81 02 05 06 09 20 85 47 15 00 25 64 75 08 95 01 |
* | b1 a2 c0 |
* +-------------------------------------------------+
*/
void mighty_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length) {
/*
* Mighty Mouse reports are exactly 6 bytes. Check for size in case of random error
* The button checks and handling follow the boot protocol
*
* Since the first 3 bytes are the same as boot, let's handle some errors here
* and call the boot decoder with a truncated report (ignore pressure and ball)
*/
if (length != 6) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
return;
}
if (data[3] || data[4]) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
}
blue_h_boot(data, 3);
}
/*
* this will tell us if the device is a supported Mighty Mouse
* PID + VID provided by the HID stack
*/
bool mighty_is_mm(uint16_t vid, uint16_t pid) {
if (vid != MIGHTY_VID)
return false;
if (pid != MIGHTY_PID)
return false;
return true;
}

33
main/mighty.h Normal file
View File

@ -0,0 +1,33 @@
/*
* wii.h
* quack
*
* Created by Michel DEPEIGE on 03/03/2025.
* Copyright (c) 2025 Michel DEPEIGE.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#pragma once
/* prototypes */
void mighty_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length);
bool mighty_is_mm(uint16_t vid, uint16_t pid);
/* defines */
#define MIGHTY_VID 0x05ac
#define MIGHTY_PID 0x030c
/* handled reports */
#define MIGHTY_REPORT_INPUT 0x02

View File

@ -28,7 +28,6 @@
#include "esp_system.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/timer.h"
#include "esp_timer.h"
#include "quad.h"
@ -40,8 +39,8 @@ esp_timer_handle_t quad_qx, quad_qy;
QueueHandle_t q_qx, q_qy;
/* static functions */
static void IRAM_ATTR quad_timer(void* arg);
/* ISR functions */
static void IRAM_ATTR quad_isr(void* arg);
/* phases */
const bool q1[] = {true, false, false, true};
@ -51,6 +50,10 @@ const bool q2[] = {true, true, false, false};
void quad_init(void) {
esp_timer_create_args_t args;
/* create quadrature queues */
q_qx = xQueueCreate(4, sizeof(int8_t));
q_qy = xQueueCreate(4, sizeof(int8_t));
/* create quadrature tasks */
xTaskCreate(quad_click, "CLICK", 1024, NULL, tskIDLE_PRIORITY + 1, &t_click);
xTaskCreate(quad_move_x, "QX", 4 * 1024, NULL, tskIDLE_PRIORITY + 1, &t_qx);
@ -66,23 +69,23 @@ void quad_init(void) {
gpio_set_level(GPIO_QY2, q2[0]);
/* create timers for quadrature phases */
args.callback = quad_timer;
args.callback = quad_isr;
args.arg = t_qx;
args.name = "quad_qx";
args.dispatch_method = ESP_TIMER_ISR;
ESP_ERROR_CHECK(esp_timer_create(&args, &quad_qx));
args.callback = quad_timer;
args.callback = quad_isr;
args.arg = t_qy;
args.name = "quad_qy";
args.dispatch_method = ESP_TIMER_ISR;
ESP_ERROR_CHECK(esp_timer_create(&args, &quad_qy));
q_qx = xQueueCreate(4, sizeof(int8_t));
q_qy = xQueueCreate(4, sizeof(int8_t));
ESP_LOGI("quad", "Quadrature tasks started on core %d", xPortGetCoreID());
}
void quad_click(void *pvParameters) {
unsigned int click = 0;
long unsigned int click = 0;
(void)pvParameters;
@ -165,7 +168,7 @@ void IRAM_ATTR quad_move_y(void *pvParameters) {
}
/* simple ISR function. Resume the task that called the oneshot timer */
static void IRAM_ATTR quad_timer(void* arg) {
static void IRAM_ATTR quad_isr(void* arg) {
BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(arg, &pxHigherPriorityTaskWoken);

BIN
main/wii.aiff Normal file

Binary file not shown.

869
main/wii.c Normal file
View File

@ -0,0 +1,869 @@
/*
* wii.c
* quack
*
* Created by Michel DEPEIGE on 11/08/2024.
* Copyright (c) 2020-2024 Michel DEPEIGE.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdio.h>
#include <string.h>
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_chip_info.h"
#if !CONFIG_BT_NIMBLE_ENABLED
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#endif
#include "esp_hid_common.h"
#include "esp_hidh.h"
#include "esp_hidh_api.h"
#include "wii.h"
#include "led.h"
#include "gpio.h"
/* defines */
#define TAG "WII"
/* globals */
TaskHandle_t t_wii_dpad;
extern TaskHandle_t t_click;
extern TaskHandle_t t_yellow, t_red;
extern QueueHandle_t q_qx, q_qy;
QueueHandle_t q_wii;
uint8_t wii_speed;
wii_accel_t wii_calibration_0g;
wii_accel_t wii_calibration_1g;
/* static defines */
static void wii_accelerometer(esp_hidh_dev_t *dev, uint8_t *data);
static void wii_change_mode(esp_hidh_dev_t *dev);
static void wii_change_speed(esp_hidh_dev_t *dev, uint16_t buttons);
static void wii_handle_button(uint16_t buttons);
static void wii_mute_state(const esp_bd_addr_t bda, bool state);
static void wii_rumble_state(const esp_bd_addr_t bda, bool state);
static void wii_speaker_state(const esp_bd_addr_t bda, bool state);
/* functions */
static void wii_accelerometer(esp_hidh_dev_t *dev, uint8_t *data) {
const uint8_t *bda = NULL;
static uint8_t p = 0; /* used for calibration pass */
uint8_t report[3] = {0};
esp_err_t ret;
float x, y, z;
static float average_x, average_y, average_z;
static uint16_t sum_x, sum_y, sum_z;
static int8_t out; /* used for quadrature messages */
wii_accel_t a = {0, 0, 0};
configASSERT(data != NULL);
configASSERT(dev != NULL);
bda = esp_hidh_dev_bda_get(dev);
if (bda == NULL)
return;
/* extract accelerator data. see https://wiibrew.org/wiki/Wiimote#Accelerometer */
a.x = (data[2] << 2) | ((data[0] >> 5) & 0x3);
a.y = (data[3] << 2) | ((data[1] >> 4) & 0x2);
a.z = (data[4] << 2) | ((data[1] >> 5) & 0x2);
/* generate acceleration values */
x = ((float)a.x - (float)wii_calibration_0g.x) / (float)wii_calibration_1g.x;
y = ((float)a.y - (float)wii_calibration_0g.y) / (float)wii_calibration_1g.y;
z = ((float)a.z - (float)wii_calibration_0g.z) / (float)wii_calibration_1g.z;
/*
* at this point it's 2AM and the values read from the test Wiimote don't match
* the calibration values. we are limited since no IR / lightbar anyway so can't
* get yaw
*
* dunno if there is something wrong in the code or the Wiimote is decalibrated
*
* so from no on, we will mangle the values to do a Arkanoid or Space Invaders
* mode and call it a day (or a night)
*
* also ignore Y and Z from now on. Sorry Y and Z
* inspiration from https://www.nxp.com/docs/en/application-note/AN3397.pdf
*/
if (p == 0) {
ESP_LOGD(TAG, "starting calibration…");
sum_x = 0;
sum_y = 0;
sum_z = 0;
}
if (p < 255) {
/*
* store MSB X, Y and Z values so the averages gets updated
* this will override values read from EEPROM but they don't seem useable
* for now (see above)
*/
/* store MSB X and Y values so the average can get calculated later */
sum_x += a.x >> 2;
sum_y += a.y >> 2;
sum_z += a.z >> 2;
p++;
/* got enough samples, let's find averages */
if (p == 255) {
average_x = ((float)((sum_x << 2) / 255) - (float)wii_calibration_0g.x);
average_x /= (float)wii_calibration_1g.x;
average_y = ((float)((sum_y << 2) / 255) - (float)wii_calibration_0g.y);
average_y /= (float)wii_calibration_1g.y;
average_z = ((float)((sum_z << 2) / 255) - (float)wii_calibration_0g.z);
average_z /= (float)wii_calibration_1g.z;
/* do a quick rumble as user notification */
wii_rumble_state(bda, true);
vTaskDelay(100 / portTICK_PERIOD_MS);
wii_rumble_state(bda, false);
report[0] = WII_REPORT_LEDS;
report[1] = WII_LED4;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
if (ret)
ESP_LOGE(TAG, "cannot set LEDs state, send error: %x", ret);
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
ESP_LOGD(TAG, "\t … X: %f, Y: %f, Z: %f", average_x, average_y, average_z);
/* set to events only after calibration */
report[0] = WII_REPORT_MODE;
report[1] = 0x00; /* events only report mode */
report[2] = 0x31; /* core buttons + accelerometer */
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 3);
if (ret)
ESP_LOGE(TAG, "cannot set mode, send error: %x", ret);
}
return;
}
/* just avoid may be used uninitialized messages */
(void)y;
(void)z;
/*
* do a mechanical filtering window, assuming the Wiimote is doing the low pass
* filtering itself since main report rate is about 30 Hz
* count idle passes to avoid drift, disregard half of what we did
* previously. this is a crap. Now I understand why Motion Plus exists
* this will move the mouse on the x axis, speed proportional to the x axis tilt
*/
if ((average_x - x) < -WII_THRESHOLD || (average_x - x) > WII_THRESHOLD) {
/* cap value to not overflow 8 bit */
out = (average_x - x) * WII_ACCELERATION * wii_speed;
if (((average_x - x) * WII_ACCELERATION * wii_speed) < -120)
out = -120;
if (((average_x - x) * WII_ACCELERATION * wii_speed) > 120)
out = 120;
}
else {
out = out >> 1;
}
//ESP_LOGD(TAG, "X %f %f :: %i", (average_x - x), (average_x - x) * WII_ACCELERATION, out);
if (out)
xQueueSendToBack(q_qx, &out, 0);
}
/*
* this will switch between the default report (buttons only) and the more advanced
* mode with accelerometer data. We don't handle others advanced modes nor extensions
*/
static void wii_change_mode(esp_hidh_dev_t *dev) {
static bool accel = 0;
const uint8_t *bda = NULL;
uint8_t report[3] = {0};
esp_err_t ret;
configASSERT(dev != NULL);
bda = esp_hidh_dev_bda_get(dev);
if (bda == NULL)
return;
accel = !accel;
ESP_LOGD(TAG, "changing mode, setting accelerometer status to %x", accel);
report[0] = WII_REPORT_MODE;
if (accel) {
report[1] = 0x04; /* continuous report mode */
report[2] = 0x31; /* core buttons + accelerometer */
}
else {
report[1] = 0x00; /* events only report mode */
report[2] = 0x30; /* core buttons only */
}
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 3);
if (ret)
ESP_LOGE(TAG, "cannot set mode, send error: %x", ret);
wii_rumble_state(bda, true);
vTaskDelay(100 / portTICK_PERIOD_MS);
wii_rumble_state(bda, false);
/* disable speed LEDs on accelerator mode (or restore in default mode) */
report[0] = WII_REPORT_LEDS;
if (accel)
report[1] = WII_LED4;
else
report[1] = wii_speed | WII_LED4;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
if (ret)
ESP_LOGE(TAG, "cannot set LEDs state, send error: %x", ret);
}
static void wii_change_speed(esp_hidh_dev_t *dev, uint16_t buttons) {
const uint8_t *bda = NULL;
uint8_t report[2] = {0};
esp_err_t ret;
configASSERT(dev != NULL);
bda = esp_hidh_dev_bda_get(dev);
if (bda == NULL)
return;
/* user facerolling the wiimote, ignore */
if ((buttons & WII_BUTTON_PLUS) && (buttons & WII_BUTTON_MINUS))
return;
if (buttons & WII_BUTTON_MINUS) {
switch (wii_speed) {
case WII_SPEED_NONE:
case WII_SPEED_LOW:
return;
case WII_SPEED_MEDIUM:
wii_speed = WII_SPEED_LOW;
break;
case WII_SPEED_HIGH:
wii_speed = WII_SPEED_MEDIUM;
default:
break;
}
}
if (buttons & WII_BUTTON_PLUS) {
switch (wii_speed) {
case WII_SPEED_NONE:
return;
case WII_SPEED_LOW:
wii_speed = WII_SPEED_MEDIUM;
break;
case WII_SPEED_MEDIUM:
wii_speed = WII_SPEED_HIGH;
break;
case WII_SPEED_HIGH:
return;
default:
break;
}
}
/* set LEDs for new speed */
report[0] = WII_REPORT_LEDS;
report[1] = wii_speed | WII_LED4;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
if (ret)
ESP_LOGE(TAG, "cannot set LEDs state, send error: %x", ret);
}
/*
* this will enable the speaker, play the boot chime and then disable the speaker
* this is blocking for about 400-600ms
*/
void wii_chime(const esp_bd_addr_t bda) {
uint8_t report[22];
esp_err_t ret;
uint8_t *pos;
/* dual note bleepbleepX2 extracted from wii.aiff */
const unsigned char chime[] = {
0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff,
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x01, 0xfc,
0xfd, 0xff, 0xfd, 0xf6, 0x09, 0x18, 0x02, 0x05, 0x17, 0x02,
0xcb, 0xd8, 0x18, 0x3c, 0x13, 0xcf, 0xc8, 0x05, 0x40, 0x29,
0xe0, 0xbe, 0xf0, 0x32, 0x34, 0xf0, 0xc1, 0xe1, 0x2a, 0x3c,
0x02, 0xc3, 0xd0, 0x19, 0x42, 0x18, 0xcf, 0xc3, 0x04, 0x40,
0x29, 0xde, 0xbb, 0xef, 0x3a, 0x3c, 0xf0, 0xb8, 0xda, 0x2b,
0x45, 0x09, 0xc1, 0xc8, 0x15, 0x40, 0x1c, 0xd7, 0xc2, 0xff,
0x43, 0x1d, 0xdc, 0xdd, 0xe2, 0x09, 0x49, 0x16, 0xb6, 0xdf,
0x39, 0x1c, 0xcb, 0xeb, 0x35, 0x0f, 0xc9, 0xf1, 0x3b, 0x0c,
0xc3, 0xf6, 0x3c, 0x05, 0xc3, 0xfd, 0x3b, 0x00, 0xc5, 0x03,
0x3b, 0xf8, 0xc2, 0x0a, 0x3e, 0xf2, 0xc0, 0x10, 0x3f, 0xed,
0xc1, 0x15, 0x3d, 0xe8, 0xc3, 0x1a, 0x3a, 0xe2, 0xc6, 0x20,
0x38, 0xdc, 0xc9, 0x25, 0x34, 0xd8, 0xcd, 0x2a, 0x30, 0xd3,
0xd1, 0x2e, 0x2c, 0xcf, 0xd5, 0x32, 0x27, 0xcb, 0xda, 0x36,
0x23, 0xc8, 0xdf, 0x39, 0x1d, 0xc5, 0xe5, 0x3c, 0x18, 0xc2,
0xea, 0x3e, 0x12, 0xc1, 0xf0, 0x3f, 0x0c, 0xbf, 0xf6, 0x40,
0x06, 0xbe, 0xfc, 0x41, 0x00, 0xbe, 0x02, 0x41, 0xfa, 0xbf,
0x08, 0x40, 0xf4, 0xbf, 0x0e, 0x3f, 0xee, 0xc1, 0x14, 0x3d,
0xe8, 0xc3, 0x1a, 0x3b, 0xe3, 0xc5, 0x1f, 0x38, 0xde, 0xc9,
0x24, 0x35, 0xd8, 0xcc, 0x29, 0x31, 0xd4, 0xd0, 0x2d, 0x2d,
0xcf, 0xd4, 0x32, 0x28, 0xcc, 0xd9, 0x35, 0x23, 0xc8, 0xde,
0x39, 0x1e, 0xc5, 0xe4, 0x3b, 0x19, 0xc3, 0xe9, 0x3d, 0x12,
0xc1, 0xef, 0x3e, 0x0d, 0xc0, 0xf5, 0x3f, 0x07, 0xbf, 0xfb,
0x40, 0x01, 0xc0, 0x01, 0x3e, 0xfb, 0xc1, 0x06, 0x3b, 0xf6,
0xc5, 0x0b, 0x37, 0xf2, 0xcc, 0x12, 0x2e, 0xf1, 0xda, 0x0b,
0x0c, 0xf3, 0x12, 0x13, 0x00, 0x09, 0x18, 0xf5, 0xc6, 0xe5,
0x25, 0x3a, 0x02, 0xc7, 0xd1, 0x17, 0x43, 0x1a, 0xd1, 0xc3,
0x01, 0x3b, 0x28, 0xe0, 0xc1, 0xf2, 0x35, 0x34, 0xf0, 0xbe,
0xde, 0x28, 0x3f, 0x06, 0xc4, 0xcd, 0x16, 0x42, 0x1a, 0xcf,
0xc0, 0x02, 0x43, 0x2f, 0xdd, 0xb8, 0xec, 0x39, 0x3e, 0xf6,
0xba, 0xd7, 0x26, 0x3f, 0x0c, 0xcc, 0xca, 0x13, 0x45, 0x0a,
0xd8, 0xdf, 0xe5, 0x1b, 0x4a, 0xfc, 0xb2, 0xf7, 0x3f, 0x07,
0xc6, 0x00, 0x37, 0xfb, 0xc6, 0x06, 0x3c, 0xf5, 0xc3, 0x0c,
0x3b, 0xef, 0xc6, 0x12, 0x38, 0xeb, 0xc9, 0x18, 0x35, 0xe3,
0xc9, 0x1f, 0x36, 0xdd, 0xc9, 0x25, 0x34, 0xd8, 0xcd, 0x29,
0x31, 0xd3, 0xd0, 0x2e, 0x2c, 0xcf, 0xd5, 0x32, 0x28, 0xcb,
0xda, 0x36, 0x23, 0xc8, 0xdf, 0x39, 0x1e, 0xc5, 0xe4, 0x3b,
0x18, 0xc2, 0xea, 0x3d, 0x12, 0xc1, 0xf0, 0x3f, 0x0d, 0xbf,
0xf5, 0x40, 0x07, 0xbe, 0xfc, 0x41, 0x00, 0xbe, 0x01, 0x41,
0xfa, 0xbf, 0x08, 0x40, 0xf4, 0xbf, 0x0d, 0x3f, 0xef, 0xc1,
0x13, 0x3d, 0xe9, 0xc3, 0x19, 0x3b, 0xe3, 0xc5, 0x1f, 0x38,
0xde, 0xc8, 0x24, 0x35, 0xd9, 0xcc, 0x29, 0x31, 0xd4, 0xd0,
0x2d, 0x2d, 0xd0, 0xd4, 0x31, 0x29, 0xcc, 0xd9, 0x35, 0x24,
0xc8, 0xde, 0x38, 0x1f, 0xc5, 0xe3, 0x3b, 0x19, 0xc3, 0xe9,
0x3d, 0x13, 0xc1, 0xef, 0x3f, 0x0d, 0xbf, 0xf4, 0x40, 0x08,
0xbe, 0xfb, 0x41, 0x01, 0xbe, 0x01, 0x40, 0xfb, 0xbf, 0x07,
0x3f, 0xf5, 0xc0, 0x0d, 0x3e, 0xef, 0xc1, 0x13, 0x3c, 0xea,
0xc5, 0x18, 0x39, 0xe5, 0xc8, 0x1b, 0x35, 0xe2, 0xcd, 0x1f,
0x2f, 0xdf, 0xd7, 0x22, 0x25, 0xe4, 0xe3, 0x11, 0x18, 0xfa,
0xef, 0x04, 0x07, 0xfe, 0xfd, 0x01, 0x00, 0xff, 0xff, 0xff,
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff
};
/*
* do the initialisation sequence for the speaker:
* - enable speaker
* - mute speaker
* - setup registers & voodoo
* - unmute speaker
*/
configASSERT(bda != NULL);
wii_speaker_state(bda, true);
wii_mute_state(bda, true);
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
memset(report, 0, 22);
report[0] = WII_REPORT_WRITE_REG;
report[1] = 0x04;
report[2] = 0xa2;
report[3] = 0x00;
report[4] = 0x09;
report[5] = 1;
report[6] = 0x01;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
if (ret)
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
memset(report, 0, 22);
report[0] = WII_REPORT_WRITE_REG;
report[1] = 0x04;
report[2] = 0xa2;
report[3] = 0x00;
report[4] = 0x01;
report[5] = 1;
report[6] = 0x08;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
if (ret)
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
memset(report, 0, 22);
report[0] = WII_REPORT_WRITE_REG;
report[1] = 0x04;
report[2] = 0xa2;
report[3] = 0x00;
report[4] = 0x01;
report[5] = 7;
report[6] = 0x00;
report[7] = 0x40; /* set PCM mode */
report[8] = 0xe0; /* set 1 kHz sample rate */
report[9] = 0x2e; /* set 1 kHz sample rate */
report[10] = 0xe0; /* increase default volume */
report[11] = 0x00;
report[12] = 0x00;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
if (ret)
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
memset(report, 0, 22);
report[0] = WII_REPORT_WRITE_REG;
report[1] = 0x04;
report[2] = 0xa2;
report[3] = 0x00;
report[4] = 0x08;
report[5] = 1;
report[6] = 0x01;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
if (ret)
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
wii_mute_state(bda, false);
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
memset(report, 0, 22);
report[0] = WII_REPORT_SPEAKER_D;
report[1] = 20 << 3; /* 20 bytes each round (20 8bits samples) */
for (pos = (uint8_t *)chime ; pos < (chime + sizeof(chime)) ; pos += 20) {
memcpy(report + 2 * sizeof(uint8_t), pos, 20);
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
if (ret)
ESP_LOGE(TAG, "cannot stream PCM data, send error: %x", ret);
/* max 1kHz 8bit PCM is at best what we can stream */
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
}
wii_speaker_state(bda, false);
}
/* this function will generate movement notifications while a button is keep pressed */
void wii_dpad(void *pvParameters) {
uint16_t buttons = 0;
uint32_t notify = 0;
int8_t x, y;
TickType_t delay = portMAX_DELAY;
(void)pvParameters;
x = 0;
y = 0;
while (true) {
if (xTaskNotifyWait(0, 0, &notify, delay) == pdTRUE) {
/* we received a status change */
buttons = (uint16_t)notify;
if (! buttons) {
delay = portMAX_DELAY;
continue;
}
delay = WII_INTERVAL / portTICK_PERIOD_MS;
}
/* ignore facerolling */
if ((buttons & WII_BUTTON_UP) && (buttons & WII_BUTTON_DOWN))
continue;
if ((buttons & WII_BUTTON_LEFT) && (buttons & WII_BUTTON_RIGHT))
continue;
if (buttons & WII_BUTTON_LEFT) {
x = -((wii_speed >> 4) * 4);
xQueueSendToBack(q_qx, &x, 0);
}
if (buttons & WII_BUTTON_RIGHT) {
x = ((wii_speed >> 4) * 4);
xQueueSendToBack(q_qx, &x, 0);
}
if (buttons & WII_BUTTON_UP) {
y = -((wii_speed >> 4) * 4);
xQueueSendToBack(q_qy, &y, 0);
}
if (buttons & WII_BUTTON_DOWN) {
y = ((wii_speed >> 4) * 4);
xQueueSendToBack(q_qy, &y, 0);
}
}
}
/*
* generate the PIN code needed for pairing. Info from https://wiibrew.org/wiki/Wiimote
* If connecting by holding down the 1+2 buttons, the PIN is the bluetooth address of
* the wiimote backwards, if connecting by pressing the "sync" button on the back of
* the wiimote, then the PIN is the bluetooth address of the host backwards.
*
* we will implement the last option
*/
uint8_t *wii_generate_pin(esp_bt_pin_code_t pin) {
uint8_t *mac;
configASSERT(pin != NULL);
mac = (uint8_t *)esp_bt_dev_get_address();
if (mac == NULL)
return NULL;
pin[0] = mac[5];
pin[1] = mac[4];
pin[2] = mac[3];
pin[3] = mac[2];
pin[4] = mac[1];
pin[5] = mac[0];
ESP_LOGV(TAG, "Bluetooth MAC: " ESP_BD_ADDR_STR ", PIN code: " WII_PIN_STR,
ESP_BD_ADDR_HEX(mac), WII_PIN_HEX(pin));
return mac;
}
/*
* similar to blue_handle_button
* this will only handle buttons that are used for clicks
* buttons that are used for movement are handled by the wii_dpad task
*/
static void wii_handle_button(uint16_t buttons) {
static bool locked = false;
static bool releasable = true;
static bool status = WII_BUTTON_NONE; // Keep state
buttons = buttons & (WII_BUTTON_A | WII_BUTTON_B);
if (status && buttons)
return ;
if (status == WII_BUTTON_NONE && buttons == WII_BUTTON_NONE && !locked)
return ;
if (buttons == WII_BUTTON_A && locked && !releasable)
return ;
if (status == WII_BUTTON_NONE && buttons == WII_BUTTON_NONE && locked) {
releasable = true;
return ;
}
/* release button */
if (status && buttons == WII_BUTTON_NONE && !locked) {
xTaskNotify(t_click, 0, eSetValueWithOverwrite);
status = 0;
ESP_LOGD(TAG, "button released");
return ;
}
/* stick button on right click */
if (status == WII_BUTTON_NONE && buttons == WII_BUTTON_A && !locked) {
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
locked = true;
releasable = false;
ESP_LOGD(TAG, "button locked");
return ;
}
/* press button (simple click) */
if (status == WII_BUTTON_NONE && buttons) {
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
locked = false;
status = 1;
ESP_LOGD(TAG, "button pressed");
return ;
}
}
void wii_init(void) {
/* put default values into calibration data */
memset(&wii_calibration_0g, 0, sizeof(wii_accel_t));
memset(&wii_calibration_1g, 0, sizeof(wii_accel_t));
xTaskCreate(wii_dpad, "WII_DPAD", 1024, NULL, tskIDLE_PRIORITY + 1, &t_wii_dpad);
}
void wii_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length) {
uint16_t buttons = 0;
static bool changed = 0;
/* do some basic sanity checks then ignore every report we cannot handle */
if (id == WII_REPORT_CORE_ONLY && length != 2) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
return ;
}
if (id == WII_REPORT_ACK && length != 4) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
return ;
}
if (id == WII_REPORT_CORE_ACCEL && length != 5) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
return ;
}
if (id == WII_REPORT_STATUS && length != 6) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
return ;
}
if (id == WII_REPORT_READ_INPUT && length != 21) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
return ;
}
if (id != WII_REPORT_ACK && id != WII_REPORT_CORE_ONLY &&
id != WII_REPORT_STATUS && id != WII_REPORT_CORE_ACCEL &&
id != WII_REPORT_READ_INPUT) {
ESP_LOGW(TAG, "Unknown report id 0x%x received (size: %i)", id, length);
return;
}
/*
* handle error codes or specific stuff from the wiimote
* we can continue to handle core buttons as usual later
*/
switch (id) {
case WII_REPORT_STATUS:
/* format is BB BB LF 00 00 VV (buttons, flags, battery) */
ESP_LOGD(TAG, "status: 0x%x%x 0x%x %i", data[0], data[1], data[2], data[5]);
break;
case WII_REPORT_READ_INPUT:
/* assume a calibration read, since it's the only read request done */
wii_show_calibration(data);
break;
case WII_REPORT_ACK:
/* format is BB BB RR EE (buttons, report number, error code) */
if (data[3]) {
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
ESP_LOGE(TAG, "error with report id 0x%x, code %x", data[2], data[3]);
}
break;
case WII_REPORT_CORE_ACCEL:
wii_accelerometer(dev, data);
break;
default:
break;
}
buttons = (data[1] << 8) | data[0];
/* changing speed is disabled in accelerometer mode */
if ((buttons & WII_BUTTON_PLUS || buttons & WII_BUTTON_MINUS) && length == 2)
wii_change_speed(dev, buttons);
/* swap modes (enable / disable accelerometer) */
if (buttons & WII_BUTTON_HOME) {
if (! changed) {
wii_change_mode(dev);
changed = true;
}
}
if (changed && !(buttons & WII_BUTTON_HOME))
changed = false;
/* do the buttons things */
wii_handle_button(buttons);
xTaskNotify(t_wii_dpad, (uint32_t)buttons, eSetValueWithOverwrite);
}
/*
* this will compare the provided BDA to a list of known Nintendo list
* return true if the provided BDA is a known prefix or fakse otherwise
*/
bool wii_is_nintendo_bda(esp_bd_addr_t bda) {
uint8_t i = 0;
/* known "Nintendo Co., Ltd." BDA prefixes as of 08/2024 */
const esp_bd_addr_t nintendo_bda_list[] = {
{0x00, 0x09, 0xbf, 0x00, 0x00, 0x00}, /* Start of Nintendo BDAs */
{0x00, 0x16, 0x56, 0x00, 0x00, 0x00},
{0x00, 0x17, 0xab, 0x00, 0x00, 0x00},
{0x00, 0x19, 0x1d, 0x00, 0x00, 0x00},
{0x00, 0x19, 0xfd, 0x00, 0x00, 0x00},
{0x00, 0x1a, 0xe9, 0x00, 0x00, 0x00},
{0x00, 0x1b, 0x7a, 0x00, 0x00, 0x00},
{0x00, 0x1b, 0xea, 0x00, 0x00, 0x00},
{0x00, 0x1c, 0xbe, 0x00, 0x00, 0x00},
{0x00, 0x1d, 0xbc, 0x00, 0x00, 0x00},
{0x00, 0x1e, 0x35, 0x00, 0x00, 0x00},
{0x00, 0x1e, 0xa9, 0x00, 0x00, 0x00},
{0x00, 0x1f, 0x32, 0x00, 0x00, 0x00},
{0x00, 0x1f, 0xc5, 0x00, 0x00, 0x00},
{0x00, 0x21, 0x47, 0x00, 0x00, 0x00},
{0x00, 0x21, 0xbd, 0x00, 0x00, 0x00},
{0x00, 0x22, 0x4c, 0x00, 0x00, 0x00},
{0x00, 0x22, 0xaa, 0x00, 0x00, 0x00},
{0x00, 0x22, 0xd7, 0x00, 0x00, 0x00},
{0x00, 0x23, 0x31, 0x00, 0x00, 0x00},
{0x00, 0x23, 0xcc, 0x00, 0x00, 0x00},
{0x00, 0x24, 0x1e, 0x00, 0x00, 0x00},
{0x00, 0x24, 0x44, 0x00, 0x00, 0x00},
{0x00, 0x24, 0xf3, 0x00, 0x00, 0x00},
{0x00, 0x25, 0xa0, 0x00, 0x00, 0x00},
{0x00, 0x26, 0x59, 0x00, 0x00, 0x00},
{0x00, 0x27, 0x09, 0x00, 0x00, 0x00},
{0x18, 0x2a, 0x7b, 0x00, 0x00, 0x00},
{0x2c, 0x10, 0xc1, 0x00, 0x00, 0x00},
{0x34, 0xaf, 0x2c, 0x00, 0x00, 0x00},
{0x40, 0xd2, 0x8a, 0x00, 0x00, 0x00},
{0x40, 0xf4, 0x07, 0x00, 0x00, 0x00},
{0x58, 0xbd, 0xa3, 0x00, 0x00, 0x00},
{0x78, 0xa2, 0xa0, 0x00, 0x00, 0x00},
{0x7c, 0xbb, 0x8a, 0x00, 0x00, 0x00},
{0x8c, 0x56, 0xc5, 0x00, 0x00, 0x00},
{0x8c, 0xcd, 0xe8, 0x00, 0x00, 0x00},
{0x9c, 0xe6, 0x35, 0x00, 0x00, 0x00},
{0xa4, 0x5c, 0x27, 0x00, 0x00, 0x00},
{0xa4, 0xc0, 0xe1, 0x00, 0x00, 0x00},
{0xb8, 0xae, 0x6e, 0x00, 0x00, 0x00},
{0xcc, 0x9e, 0x00, 0x00, 0x00, 0x00},
{0xcc, 0xfb, 0x65, 0x00, 0x00, 0x00},
{0xd8, 0x6b, 0xf7, 0x00, 0x00, 0x00},
{0xe0, 0x0c, 0x7f, 0x00, 0x00, 0x00},
{0xe0, 0xe7, 0x51, 0x00, 0x00, 0x00},
{0xe8, 0x4e, 0xce, 0x00, 0x00, 0x00}, /* End of Nintendo BDAs */
{0x08, 0x03, 0x64, 0x00, 0x00, 0x00}, /* PDP Rock Candy compatible Wiimotes */
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
while (nintendo_bda_list[i][0] || nintendo_bda_list[i][1] || nintendo_bda_list[i][2]) {
if (nintendo_bda_list[i][0] == bda[0] &&
nintendo_bda_list[i][1] == bda[1] &&
nintendo_bda_list[i][2] == bda[2]) {
return true;
}
i++;
}
return false;
}
/* this will tell us if the device is a Wiimote. PID + VID provided by the HID stack */
bool wii_is_wiimote(uint16_t vid, uint16_t pid) {
if (vid != WII_VID)
return false;
if (pid != WII_PID_OLD && pid != WII_PID_NEW)
return false;
return true;
}
/* this is called every time a Wiimote is connected */
void wii_open(const esp_bd_addr_t bda) {
uint8_t report[7] = {0};
esp_err_t ret;
configASSERT(bda != NULL);
/* enable LED4 on connect */
report[0] = WII_REPORT_LEDS;
report[1] = WII_LED4;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
if (ret)
ESP_LOGE(TAG, "cannot set LED4 state, send error: %x", ret);
/* set initial LEDs state */
wii_speed = 0;
/* stream the boot chime to the wii mote. this will also wake up the rumble */
wii_chime(bda);
wii_rumble_state(bda, true);
vTaskDelay(400 / portTICK_PERIOD_MS);
wii_speed = WII_SPEED_MEDIUM;
wii_rumble_state(bda, false);
/* do a read to fetch accelerometer calibration data from both spaces */
report[0] = WII_REPORT_READ_REG;
report[1] = 0x00; /* EEPROM space */
report[2] = 0x00; /* read from 0x000016 */
report[3] = 0x00; /* read from 0x000016 */
report[4] = 0x16; /* read from 0x000016 */
report[5] = 0x00; /* read 10 bytes */
report[6] = 0x0a; /* read 10 bytes */
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 7);
if (ret)
ESP_LOGE(TAG, "cannot read accelerometer calibration, send error: %x", ret);
report[0] = WII_REPORT_READ_REG;
report[1] = 0x00; /* EEPROM space */
report[2] = 0x00; /* read from 0x000020 */
report[3] = 0x00; /* read from 0x000020 */
report[4] = 0x20; /* read from 0x000020 */
report[5] = 0x00; /* read 10 bytes */
report[6] = 0x0a; /* read 10 bytes */
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 7);
if (ret)
ESP_LOGE(TAG, "cannot read accelerometer calibration, send error: %x", ret);
}
/* mute the speaker on the Wiimote. this will clear the rumble ! */
static void wii_mute_state(const esp_bd_addr_t bda, bool state) {
uint8_t report[2] = {0};
esp_err_t ret;
configASSERT(bda != NULL);
ESP_LOGV(TAG, "setting mute to %x", state);
report[0] = WII_REPORT_SPEAKER_M;
if (state)
report[1] = 0x04;
else
report[1] = 0x0;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
if (ret)
ESP_LOGE(TAG, "cannot set mute state, send error: %x", ret);
}
/* enable the rumble on the Wiimote. this will clear the LEDs so keep the LEDs state */
static void wii_rumble_state(const esp_bd_addr_t bda, bool state) {
uint8_t report[2] = {0};
esp_err_t ret;
if (state) {
report[0] = WII_REPORT_RUMBLE;
report[1] = 0x1 | WII_LED4;
} else {
report[0] = WII_REPORT_LEDS;
report[1] = wii_speed | WII_LED4;
}
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
if (ret)
ESP_LOGE(TAG, "cannot set rumble state, send error: %x", ret);
}
/* enable the speaker on the Wiimote. this will clear the rumble ! */
static void wii_speaker_state(const esp_bd_addr_t bda, bool state) {
uint8_t report[2] = {0};
esp_err_t ret;
configASSERT(bda != NULL);
ESP_LOGV(TAG, "setting speaker state to %x", state);
report[0] = WII_REPORT_SPEAKER_E;
if (state)
report[1] = 0x04;
else
report[1] = 0x0;
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
if (ret)
ESP_LOGE(TAG, "cannot set speaker state, send error: %x", ret);
}
/* display calibration data. do not save it for now since we are calibrating ourselves */
void wii_show_calibration(uint8_t *data) {
uint8_t i;
uint8_t checksum;
configASSERT(data != NULL);
ESP_LOGD(TAG, "EEPROM read from 0x%02x%02x", data[3], data[4]);
/* skip the header */
data += 5;
wii_calibration_0g.x = ((data[0] << 2) | ((data[3] >> 4) & 3));
wii_calibration_0g.y = ((data[1] << 2) | ((data[3] >> 2) & 3));
wii_calibration_0g.z = ((data[2] << 2) | (data[3] & 3));
wii_calibration_1g.x = (((data[4] << 2) | ((data[7] >> 4) & 3)) - wii_calibration_0g.x);
wii_calibration_1g.y = (((data[5] << 2) | ((data[7] >> 2) & 3)) - wii_calibration_0g.y);
wii_calibration_1g.z = (((data[6] << 2) | (data[7] & 3)) - wii_calibration_0g.z);
ESP_LOGD(TAG, "Wiimote calibration data: 0G (%i %i %i) 1G (%i %i %i)…",
wii_calibration_0g.x, wii_calibration_0g.y, wii_calibration_0g.z,
wii_calibration_1g.x, wii_calibration_1g.y, wii_calibration_1g.z);
checksum = 0;
for (i = 0 ; i < 9 ; i++)
checksum += data[i];
checksum += 0x55;
ESP_LOGD(TAG, "\t… checksum read: %02X, calculated: %02X", data[9], checksum);
if (data[9] != checksum)
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
}

96
main/wii.h Normal file
View File

@ -0,0 +1,96 @@
/*
* wii.h
* quack
*
* Created by Michel DEPEIGE on 11/08/2024.
* Copyright (c) 2024 Michel DEPEIGE.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#pragma once
/* prototypes */
uint8_t *wii_generate_pin(esp_bt_pin_code_t pin);
void wii_init(void);
void wii_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length);
bool wii_is_nintendo_bda(esp_bd_addr_t bda);
bool wii_is_wiimote(uint16_t vid, uint16_t pid);
void wii_open(const esp_bd_addr_t bda);
void wii_show_calibration(uint8_t *data);
/* defines */
#define WII_VID 0x057e
#define WII_PID_OLD 0x0306
#define WII_PID_NEW 0x0330
#define WII_PIN_STR "0x%02x%02x%02x%02x%02x%02x"
#define WII_PIN_HEX(pin) pin[0], pin[1], pin[2], pin[3], pin[4], pin[5]
#define WII_THRESHOLD 0.1f
#define WII_ACCELERATION -10.0f /* coonvert gravity to pixels */
#define WII_INTERVAL (1000 / 60) /* pad update interval, about 60 Hz */
#define WII_THROTTLE 20
/* core buttons. LX6 is wrong endian */
#define WII_BUTTON_NONE 0x0000
#define WII_BUTTON_LEFT 0x0001
#define WII_BUTTON_RIGHT 0x0002
#define WII_BUTTON_DOWN 0x0004
#define WII_BUTTON_UP 0x0008
#define WII_BUTTON_PLUS 0x0010
#define WII_BUTTON_TWO 0x0100
#define WII_BUTTON_ONE 0x0200
#define WII_BUTTON_B 0x0400
#define WII_BUTTON_A 0x0800
#define WII_BUTTON_MINUS 0x1000
#define WII_BUTTON_HOME 0x8000
/* LEDs + Rumble */
#define WII_LED1 0x10
#define WII_LED2 0x20
#define WII_LED3 0x40
#define WII_LED4 0x80
/* for quadrature speed feedback */
#define WII_SPEED_NONE 0x00
#define WII_SPEED_LOW WII_LED1
#define WII_SPEED_MEDIUM WII_LED2
#define WII_SPEED_HIGH WII_LED3
/* handled reports */
#define WII_REPORT_RUMBLE 0x10
#define WII_REPORT_LEDS 0x11
#define WII_REPORT_MODE 0x12
#define WII_REPORT_SPEAKER_E 0x14
#define WII_REPORT_REQUEST 0x15
#define WII_REPORT_WRITE_REG 0x16
#define WII_REPORT_READ_REG 0x17
#define WII_REPORT_SPEAKER_D 0x18
#define WII_REPORT_SPEAKER_M 0x19
#define WII_REPORT_STATUS 0x20
#define WII_REPORT_READ_INPUT 0x21
#define WII_REPORT_ACK 0x22
#define WII_REPORT_CORE_ONLY 0x30
#define WII_REPORT_CORE_ACCEL 0x31
#define WII_REPORT_INPUT WII_REPORT_ACK|WII_REPORT_CORE_ONLY
/* structures */
typedef struct wii_accel_s {
uint16_t x;
uint16_t y;
uint16_t z;
} wii_accel_t;

1884
sdkconfig

File diff suppressed because it is too large Load Diff