Compare commits

...

18 Commits

Author SHA1 Message Date
hueso
49181dadde Moonrise face
Some checks failed
Build / build (push) Has been cancelled
Build / build-simulator (push) Has been cancelled
2025-05-16 19:02:44 -03:00
hueso
8abf6f4f5b Fixed buffer overflows in sunrise_sunset_face
Some checks failed
Build / build (push) Has been cancelled
Build / build-simulator (push) Has been cancelled
GitHub Pages / gh-pages (push) Has been cancelled
2025-05-15 11:59:21 -03:00
Joey Castillo
8c39d42824 Merge branch 'main' of github.com:joeycastillo/Sensor-Watch 2025-05-06 23:57:45 -04:00
Joey Castillo
56f2c93783 sensor watch pro test procedure 2025-05-06 23:56:19 -04:00
Wesley Ellis
14e64caac9
Merge pull request #512 from tahnok/schematic-readme
Add schematic info to README
2025-04-03 08:58:30 -04:00
Wesley Ellis
cc6a0c363e Add links to schematics and gerbers for different models 2025-04-01 09:55:41 -04:00
Wesley Ellis
845caa30fb Add link to schematics and gerbers to README 2025-04-01 09:55:41 -04:00
Joey Castillo
0ef5e5700e Sensor Watch Pro 2025-03-08 17:42:54 -05:00
Jeremy O'Brien
e8f31beb70
Smallchess face (#272)
* smallchess face

* use correct game-state modifying board move function

* make show last work after undo

* use SCL_Game->ply instead of board[ply_byte]

* beep when cpu is done computing a move

* increase engine strength to ply 3

* match ply type and use the local variable where available

* fix warnings

* add doc to smallchess face

* smallchess: fix compile warnings

* smallchess: move smallchesslib.h to movement/lib
2024-09-17 22:36:34 -04:00
joeycastillo
c2103d9eaa silence warnings in butterfly_game_face 2024-09-17 22:27:43 -04:00
joeycastillo
88338dc0ba silence warnings in wareki_face 2024-09-17 22:19:22 -04:00
Hugo Chargois
e8ba597131
Add Butterfly game face (#338) 2024-09-17 22:04:00 -04:00
kbc-yam
52c3d5b796
add wareki_face for japanese user (#351) 2024-09-17 22:00:44 -04:00
MarkBlyth
7af5626147
Add min/max temperature watch face (#335) 2024-09-17 21:54:33 -04:00
Austoria
b2d313e0e7
Metronome Complication (#303)
* Metronome Complication

A simple metronome complication that allows user to set BPM, toggle sound, and set counts per measure.

* silence warnings in metronome_face

* avoid mode button in metronome settings, other tweaks

---------

Co-authored-by: joeycastillo <joeycastillo@utexas.edu>
2024-09-17 21:46:20 -04:00
jokomo24
0f5defe789
Face for tracking the menstrual cycle (#250)
Authored-by: jokomo <jokomo@parallels-ubuntu18.04>
2024-09-17 20:55:50 -04:00
joeycastillo
3634460a02 silence warning in beeps_face 2024-09-17 20:46:00 -04:00
Jose Castillo
fc2f9c5130
add accelerometer interrupt counter (#452) 2024-09-17 20:38:09 -04:00
71 changed files with 267747 additions and 12 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
{
"board": {
"active_layer": 31,
"active_layer_preset": "",
"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
},
"ratsnest_display_mode": 0,
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": true,
"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,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
32,
33,
34,
35,
36
],
"visible_layers": "00290aa_80000007",
"zone_display_mode": 0
},
"git": {
"repo_password": "",
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "OSO-SWAT-C1-rounded.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View File

@ -0,0 +1,692 @@
{
"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.9,
"height": 0.9,
"width": 0.9
},
"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": {
"45_degree_only": false,
"min_clearance": 1e-06
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [
"clearance|6549579|23514621|0dac23a8-f43b-4e30-9a10-b757851281ba|192908ec-7c4c-4ab1-a565-bc709c96b57f",
"clearance|7950000|22690000|821d722e-241b-43f8-bcb8-12290d54f4d9|192908ec-7c4c-4ab1-a565-bc709c96b57f",
"clearance|7970521|23400521|e003e765-2ee5-400f-abaa-d669dc68a0c6|192908ec-7c4c-4ab1-a565-bc709c96b57f",
"clearance|8581179|22000000|3dde5b5d-55b0-42ee-abfb-5429896be16c|192908ec-7c4c-4ab1-a565-bc709c96b57f",
"clearance|8581179|22002512|3cab235c-f7da-46c2-8b4d-9d21002f2b49|192908ec-7c4c-4ab1-a565-bc709c96b57f",
"items_not_allowed|6130279|23400521|0dac23a8-f43b-4e30-9a10-b757851281ba|00000000-0000-0000-0000-000000000000",
"items_not_allowed|7050400|23400521|e003e765-2ee5-400f-abaa-d669dc68a0c6|00000000-0000-0000-0000-000000000000",
"items_not_allowed|7950000|23035000|821d722e-241b-43f8-bcb8-12290d54f4d9|00000000-0000-0000-0000-000000000000",
"items_not_allowed|8640000|22000000|3cab235c-f7da-46c2-8b4d-9d21002f2b49|00000000-0000-0000-0000-000000000000",
"items_not_allowed|8780000|22000000|3dde5b5d-55b0-42ee-abfb-5429896be16c|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": "warning",
"copper_sliver": "warning",
"courtyards_overlap": "warning",
"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": "error",
"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": "error",
"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": "warning",
"starved_thermal": "warning",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.005,
"min_clearance": 0.0889,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.0889,
"min_hole_clearance": 0.0889,
"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.2,
"min_track_width": 0.0889,
"min_via_annular_width": 0.125,
"min_via_diameter": 0.45,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0,
0.0762,
0.0889,
0.1016,
0.127,
0.1524,
0.1778,
0.2032,
0.254,
0.3048,
0.55
],
"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": [
{
"diameter": 0.0,
"drill": 0.0
},
{
"diameter": 0.5588,
"drill": 0.3048
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"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": "error",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"simulation_model_issue": "error",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "OSO-SWAT-C1-rounded.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.0889,
"diff_pair_gap": 0.0889,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.0889,
"line_style": 0,
"microvia_diameter": 0.45,
"microvia_drill": 0.2,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.0889,
"via_diameter": 0.45,
"via_drill": 0.2,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.0889,
"diff_pair_gap": 0.0889,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.0889,
"line_style": 0,
"microvia_diameter": 0.45,
"microvia_drill": 0.2,
"name": "gnd",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2032,
"via_diameter": 0.45,
"via_drill": 0.2,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.0889,
"diff_pair_gap": 0.0889,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.0889,
"line_style": 0,
"microvia_diameter": 0.45,
"microvia_drill": 0.2,
"name": "power",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2032,
"via_diameter": 0.45,
"via_drill": 0.2,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "gnd",
"pattern": "GND"
},
{
"netclass": "power",
"pattern": "VCC"
}
]
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "./OSO-SWAT-C1-06",
"pos_files": "",
"specctra_dsn": "",
"step": "OSO-SWAT-C1.step",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "OSO-SWAT-C1-05.csv",
"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": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
}
],
"filter_string": "",
"group_symbols": true,
"name": "",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"ngspice": {
"fix_include_paths": true,
"fix_passive_vals": false,
"meta": {
"version": 0
},
"model_mode": 0,
"workbook_filename": ""
},
"page_layout_descr_file": "empty.kicad_wks",
"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": [
[
"3048f5e8-34bb-46da-8fa5-8c5aad2586f2",
"Root"
]
],
"text_variables": {}
}

View File

@ -0,0 +1 @@
(version 1)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
{
"board": {
"active_layer": 0,
"active_layer_preset": "",
"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
},
"ratsnest_display_mode": 0,
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": true,
"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,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
32,
33,
34,
35,
36
],
"visible_layers": "0015055_80000001",
"zone_display_mode": 0
},
"git": {
"repo_password": "",
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "OSO-SWAT-C1.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View File

@ -0,0 +1,681 @@
{
"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.9,
"height": 0.9,
"width": 0.9
},
"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": {
"45_degree_only": false,
"min_clearance": 1e-06
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"filename": "board_design_settings.json",
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "warning",
"copper_sliver": "warning",
"courtyards_overlap": "warning",
"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": "error",
"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": "error",
"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": "warning",
"starved_thermal": "warning",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.005,
"min_clearance": 0.0889,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.0889,
"min_hole_clearance": 0.0889,
"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.2,
"min_track_width": 0.0889,
"min_via_annular_width": 0.125,
"min_via_diameter": 0.45,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0,
0.0762,
0.0889,
0.1016,
0.127,
0.1524,
0.1778,
0.2032,
0.254,
0.3048,
0.55
],
"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": [
{
"diameter": 0.0,
"drill": 0.0
},
{
"diameter": 0.5588,
"drill": 0.3048
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"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": "error",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"simulation_model_issue": "error",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "OSO-SWAT-C1.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.0889,
"diff_pair_gap": 0.0889,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.0889,
"line_style": 0,
"microvia_diameter": 0.45,
"microvia_drill": 0.2,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.0889,
"via_diameter": 0.45,
"via_drill": 0.2,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.0889,
"diff_pair_gap": 0.0889,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.0889,
"line_style": 0,
"microvia_diameter": 0.45,
"microvia_drill": 0.2,
"name": "gnd",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2032,
"via_diameter": 0.45,
"via_drill": 0.2,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.0889,
"diff_pair_gap": 0.0889,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.0889,
"line_style": 0,
"microvia_diameter": 0.45,
"microvia_drill": 0.2,
"name": "power",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2032,
"via_diameter": 0.45,
"via_drill": 0.2,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "gnd",
"pattern": "GND"
},
{
"netclass": "power",
"pattern": "VCC"
}
]
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "./OSO-SWAT-C1-06",
"pos_files": "./temp/",
"specctra_dsn": "",
"step": "OSO-SWAT-C1.step",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "OSO-SWAT-C1-05-bottom.csv",
"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": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
}
],
"filter_string": "",
"group_symbols": true,
"name": "",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"ngspice": {
"fix_include_paths": true,
"fix_passive_vals": false,
"meta": {
"version": 0
},
"model_mode": 0,
"workbook_filename": ""
},
"page_layout_descr_file": "empty.kicad_wks",
"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": [
[
"3048f5e8-34bb-46da-8fa5-8c5aad2586f2",
"Root"
]
],
"text_variables": {}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
(footprint "ELT3KN"
(version 20240108)
(generator "pcbnew")
(generator_version "8.0")
(layer "F.Cu")
(property "Reference" "REF**"
(at 0 -2.5 0)
(unlocked yes)
(layer "F.SilkS")
(uuid "a169e63e-29cc-4564-a621-b8211c147892")
(effects
(font
(size 1 1)
(thickness 0.2)
(bold yes)
)
)
)
(property "Value" "ELT3KN"
(at 0 2.5 0)
(unlocked yes)
(layer "F.Fab")
(uuid "12f117ff-fc54-4392-9702-20053568a437")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Footprint" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "4696eff2-7885-4247-8d7f-ea328aeb61b3")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "38e32ed5-5bed-49cc-a302-cc80b63f7eca")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "bbe09b54-7873-4c59-90a5-708f27e5af76")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(attr smd)
(fp_arc
(start -1.495202 -0.697761)
(mid -0.886339 -1.391726)
(end 0 -1.65)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "2b5cd3f8-b18c-4557-94e3-1779ca1608f1")
)
(fp_arc
(start 0 -1.65)
(mid 0.886339 -1.391727)
(end 1.495202 -0.697761)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "d473595f-e35a-4857-8449-1d9f3feacdc9")
)
(fp_arc
(start 0 1.65)
(mid -0.886339 1.391727)
(end -1.495202 0.697761)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "d37347f0-9221-4e4b-a029-a04f185819ec")
)
(fp_arc
(start 1.495202 0.697761)
(mid 0.886339 1.391726)
(end 0 1.65)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "f12bf60f-5b3d-45b5-82f7-79448488ae21")
)
(fp_circle
(center 0 0)
(end 1.65 0)
(stroke
(width 0.1)
(type default)
)
(fill none)
(layer "F.Fab")
(uuid "3ab1094b-f4c1-4436-87a3-fa43bddd0a8d")
)
(fp_text user "${REFERENCE}"
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(uuid "0739cf1b-8f40-4d32-91d0-496ba0ec98b8")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(pad "1" smd roundrect
(at -2.15 0)
(size 1.4 1.05)
(layers "F.Cu" "F.Paste" "F.Mask")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "e742aada-aafd-47f3-8447-3fbf9c4a7579")
)
(pad "2" smd roundrect
(at 2.15 0)
(size 1.4 1.05)
(layers "F.Cu" "F.Paste" "F.Mask")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "fa457dd1-8427-41f4-83ec-ab02d543d005")
)
)

View File

@ -0,0 +1,47 @@
(footprint "FH19C9S05SH10" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(descr "<b>FH19C-9S-0.5SH(10)-1</b><br>\n")
(fp_text reference "REF**" (at 0 -1.425) (layer "F.SilkS")
(effects (font (size 1.1684 1.1684) (thickness 0.1016)))
(tstamp f001c7f4-3bac-4901-bfec-55e6729b7a2d)
)
(fp_text value ">VALUE" (at 0 -1.425) (layer "F.Fab")
(effects (font (size 1.1684 1.1684) (thickness 0.1016)))
(tstamp 561aeca9-49f1-4f98-93e2-6788ec46bd30)
)
(fp_line (start -2.75 -2.25) (end -3.25 -2.25) (layer "F.SilkS") (width 0.1) (tstamp 1eac27e8-d706-4fd3-9d7e-f44c3bceecb7))
(fp_line (start -3.25 0.75) (end 3.25 0.75) (layer "F.SilkS") (width 0.1) (tstamp 2954b329-e121-4506-a75a-9fd0ad6fb160))
(fp_line (start -3.25 -2.25) (end -3.25 -0.75) (layer "F.SilkS") (width 0.1) (tstamp 707c871c-221c-430b-8a6b-9066bbe6a8b7))
(fp_line (start 3.25 -2.25) (end 3.25 -0.75) (layer "F.SilkS") (width 0.1) (tstamp c93c51a4-9fbb-472e-8b27-c730f1646c88))
(fp_line (start 2.75 -2.25) (end 3.25 -2.25) (layer "F.SilkS") (width 0.1) (tstamp f62c7b3a-070f-4127-95d5-3042f710b35c))
(fp_arc (start -2.457 -2.572) (mid -2.407 -2.622) (end -2.357 -2.572) (layer "F.SilkS") (width 0.2) (tstamp 99032ce3-83f4-4d78-bc12-cfc846be0db6))
(fp_arc (start -2.357 -2.572) (mid -2.407 -2.522) (end -2.457 -2.572) (layer "F.SilkS") (width 0.2) (tstamp cc85bc5e-d12e-4c30-b330-62e6d8cd831e))
(fp_arc (start -2.457 -2.572) (mid -2.407 -2.622) (end -2.357 -2.572) (layer "F.SilkS") (width 0.2) (tstamp e306135f-93e3-4c31-83e0-8c60a6608eec))
(fp_line (start 3.25 0.75) (end -3.25 0.75) (layer "F.Fab") (width 0.2) (tstamp 4524f199-9890-4fbd-90ec-2bd6babdbf89))
(fp_line (start 3.25 -2.25) (end 3.25 0.75) (layer "F.Fab") (width 0.2) (tstamp 855f1cc0-31ef-4190-b476-18e3b5158686))
(fp_line (start -3.25 0.75) (end -3.25 -2.25) (layer "F.Fab") (width 0.2) (tstamp a7aeadd1-064c-4dec-839a-d3cb8b0a8559))
(fp_line (start -3.25 -2.25) (end 3.25 -2.25) (layer "F.Fab") (width 0.2) (tstamp b29496a7-f051-4b0c-a792-56f5b1d5ae06))
(pad "1" smd rect (at -2 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp acda55bd-5be5-40b1-a960-d198db4ec672))
(pad "2" smd rect (at -1.5 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 45b21120-703c-4698-9c56-6564d6e1bf81))
(pad "3" smd rect (at -1 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 8b754c17-ed29-4271-a8ee-1331ac5995ef))
(pad "4" smd rect (at -0.5 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 9ccadf67-4eb3-4873-b5dd-5cc11b45344d))
(pad "5" smd rect (at 0 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp bda542d0-ecb7-417f-8c5a-7bba95d4eba5))
(pad "6" smd rect (at 0.5 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 8e3054c6-288a-41ce-abc0-f8e853b26354))
(pad "7" smd rect (at 1 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 1f1c1390-fe89-4a71-a866-c4e1f01372f9))
(pad "8" smd rect (at 1.5 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 83315760-4e59-4aea-8656-92a90fbb73b7))
(pad "9" smd rect (at 2 -2.5 90) (size 0.8 0.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp f1470e81-8bfa-4c7b-9a4f-907119981553))
(pad "MP1" smd rect (at -3 0 90) (size 0.8 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 5d56d96c-0e59-4d80-b122-61ae0456f87e))
(pad "MP2" smd rect (at 3 0 90) (size 0.8 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 65badac9-bbc1-4323-b96e-20b366a5c013))
)

View File

@ -0,0 +1,22 @@
(footprint "FIDUCIAL_1MM" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at 0 0) (layer "F.SilkS")
(effects (font (size 1.27 1.27) (thickness 0.15)))
(tstamp 084b231a-a841-4989-ba78-c80f52a1d530)
)
(fp_text value "" (at 0 0) (layer "F.Fab")
(effects (font (size 1.27 1.27) (thickness 0.15)))
(tstamp 5023af4a-f69e-458d-abff-77e97f0c49aa)
)
(fp_arc (start -0.75 0) (mid -0.53033 -0.53033) (end 0 -0.75) (layer "F.Mask") (width 0.5) (tstamp 243424d9-367c-4c55-9ce2-5c7f6b71c486))
(fp_arc (start 0 0.75) (mid -0.53033 0.53033) (end -0.75 0) (layer "F.Mask") (width 0.5) (tstamp 38a261de-e7ad-4359-9a4e-e8325d947b3c))
(fp_arc (start 0.75 0) (mid 0.53033 0.53033) (end 0 0.75) (layer "F.Mask") (width 0.5) (tstamp 3e5e4d91-af7b-467f-93be-7757d59f4acd))
(fp_arc (start 0 -0.75) (mid 0.53033 -0.53033) (end 0.75 0) (layer "F.Mask") (width 0.5) (tstamp cba0161e-94ca-4df5-83f4-b38d910a40d8))
(fp_arc (start 0 -0.75) (mid 0.53033 -0.53033) (end 0.75 0) (layer "F.CrtYd") (width 0.5) (tstamp 24375f2d-9f3c-47f3-bf1b-d9abc9459487))
(fp_arc (start 0.75 0) (mid 0.53033 0.53033) (end 0 0.75) (layer "F.CrtYd") (width 0.5) (tstamp 61d35d35-c7e3-45c7-a1f6-4c560b28917a))
(fp_arc (start 0 0.75) (mid -0.53033 0.53033) (end -0.75 0) (layer "F.CrtYd") (width 0.5) (tstamp b245f192-3aeb-4a3c-8f5f-5b2ac372cfb6))
(fp_arc (start -0.75 0) (mid -0.53033 -0.53033) (end 0 -0.75) (layer "F.CrtYd") (width 0.5) (tstamp f012c0d9-2bab-4b5b-8327-ed4e32f900bb))
(pad "1" smd roundrect (at 0 0) (size 1 1) (layers "F.Cu" "F.Mask") (roundrect_rratio 0.5)
(solder_mask_margin 0.0635) (tstamp ea46b6d0-c0be-44d4-aa82-47c56ffdb259))
)

View File

@ -0,0 +1,26 @@
(footprint "LED_QBLP655" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at 0.011609 -1.462631) (layer "F.SilkS")
(effects (font (size 0.682286 0.682286) (thickness 0.14977)))
(tstamp 441c2b28-158a-4d68-a991-31f4c6a03554)
)
(fp_text value ">VALUE" (at -0.054931 1.580818) (layer "F.Fab")
(effects (font (size 0.677435 0.677435) (thickness 0.148705)))
(tstamp 2f361345-03db-4c21-a99d-0b9cfab62aee)
)
(fp_circle (center -3.1 -0.5) (end -3 -0.5) (layer "F.SilkS") (width 0.2) (fill none) (tstamp 0ba68dd4-e618-4da0-b7e7-599817b38c08))
(fp_line (start -1.6 -0.6) (end 1.6 -0.6) (layer "F.Fab") (width 0.127) (tstamp 3629e8ab-0a05-463c-9cb0-81d2e314e044))
(fp_line (start -1.6 0.6) (end -1.6 -0.6) (layer "F.Fab") (width 0.127) (tstamp 379a3f23-154c-4da8-9d91-714d85fa6b82))
(fp_line (start 1.6 -0.6) (end 1.6 0.6) (layer "F.Fab") (width 0.127) (tstamp 78db06e4-b90e-45d6-90ee-f31a25c04c7d))
(fp_line (start 1.6 0.6) (end -1.6 0.6) (layer "F.Fab") (width 0.127) (tstamp de669889-6aee-41d8-9167-24b5b7cd2f7a))
(fp_circle (center -3.1 -0.5) (end -3 -0.5) (layer "F.Fab") (width 0.2) (fill none) (tstamp a3f31f15-16ea-4248-8fe2-4f1f6cb6fef0))
(pad "1" smd rect (at -1.5 -0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp c0b010da-7837-47db-be16-93e46d77ee03))
(pad "2" smd rect (at 1.5 -0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 0491e5ac-92c6-4da4-a40f-13768f4f5f7c))
(pad "3" smd rect (at -1.5 0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp d96a3b29-ffbb-4252-a695-34388cf2cce8))
(pad "4" smd rect (at 1.5 0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp a7b3dfda-cc06-4a25-8ceb-15247a6b386b))
)

View File

@ -0,0 +1,24 @@
(footprint "LED_QBLP655_RGB" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at 0.011609 -1.462631) (layer "F.SilkS")
(effects (font (size 0.682286 0.682286) (thickness 0.14977)))
(tstamp 441c2b28-158a-4d68-a991-31f4c6a03554)
)
(fp_text value ">VALUE" (at -0.054931 1.580818) (layer "F.Fab")
(effects (font (size 0.677435 0.677435) (thickness 0.148705)))
(tstamp 2f361345-03db-4c21-a99d-0b9cfab62aee)
)
(fp_line (start -1.6 -0.6) (end 1.6 -0.6) (layer "F.Fab") (width 0.127) (tstamp 3629e8ab-0a05-463c-9cb0-81d2e314e044))
(fp_line (start -1.6 0.6) (end -1.6 -0.6) (layer "F.Fab") (width 0.127) (tstamp 379a3f23-154c-4da8-9d91-714d85fa6b82))
(fp_line (start 1.6 -0.6) (end 1.6 0.6) (layer "F.Fab") (width 0.127) (tstamp 78db06e4-b90e-45d6-90ee-f31a25c04c7d))
(fp_line (start 1.6 0.6) (end -1.6 0.6) (layer "F.Fab") (width 0.127) (tstamp de669889-6aee-41d8-9167-24b5b7cd2f7a))
(pad "1" smd rect (at 1.5 -0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 0491e5ac-92c6-4da4-a40f-13768f4f5f7c))
(pad "2" smd rect (at -1.5 -0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp c0b010da-7837-47db-be16-93e46d77ee03))
(pad "3" smd rect (at -1.5 0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp d96a3b29-ffbb-4252-a695-34388cf2cce8))
(pad "4" smd rect (at 1.5 0.4) (size 1.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp a7b3dfda-cc06-4a25-8ceb-15247a6b386b))
)

View File

@ -0,0 +1,15 @@
(footprint "MICROBUILDER_TESTPOINT_ROUND_1.5MM" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at 1.143 0.127) (layer "F.SilkS")
(effects (font (size 0.666496 0.666496) (thickness 0.146304)) (justify left bottom))
(tstamp 3249263f-d424-465a-883e-c9fbfca1c498)
)
(fp_text value ">VALUE" (at 1.143 0.635) (layer "F.Fab")
(effects (font (size 0.36576 0.36576) (thickness 0.04064)) (justify left bottom))
(tstamp ffe18277-47dd-48a9-ae75-8daae098595b)
)
(fp_circle (center 0 0) (end 1 0) (layer "F.SilkS") (width 0.2032) (fill none) (tstamp 10aeb60f-0687-4840-aeb5-a5e914b89db2))
(pad "P$1" smd roundrect (at 0 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (roundrect_rratio 0.5)
(solder_mask_margin 0.0635) (tstamp 278b13e7-2a43-4fc5-a675-ea6ed2efdd4b))
)

View File

@ -0,0 +1,38 @@
(footprint "MICROBUILDER__0805MP" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(descr "<b>0805 MicroPitch</b>")
(fp_text reference "REF**" (at -1.5875 -0.9525) (layer "F.SilkS")
(effects (font (size 0.666496 0.666496) (thickness 0.146304)) (justify left bottom))
(tstamp 6d480cb3-ccbc-4f91-b215-36d9b6b90e39)
)
(fp_text value ">VALUE" (at -1.5875 1.27) (layer "F.Fab")
(effects (font (size 0.36576 0.36576) (thickness 0.04064)) (justify left bottom))
(tstamp 6bb67786-fe79-4530-a8f0-ca53290823df)
)
(fp_poly (pts
(xy -0.1999 0.5001)
(xy 0.1999 0.5001)
(xy 0.1999 -0.5001)
(xy -0.1999 -0.5001)
) (layer "F.Adhes") (width 0) (fill solid) (tstamp 9e351c4d-f895-49c5-ba3f-cecde3dc6e44))
(fp_line (start 0 -0.508) (end 0 0.508) (layer "F.SilkS") (width 0.2032) (tstamp 629c7bfb-b888-4932-aba0-6916a21e7f67))
(fp_line (start -0.51 0.535) (end 0.51 0.535) (layer "F.Fab") (width 0.1016) (tstamp 22396a4f-3f2f-4a10-9672-ef964a1e15ec))
(fp_line (start -0.51 -0.535) (end 0.51 -0.535) (layer "F.Fab") (width 0.1016) (tstamp 90cacfdf-b07f-4151-ac89-23042124f640))
(fp_poly (pts
(xy 0.4064 0.65)
(xy 1 0.65)
(xy 1 -0.65)
(xy 0.4064 -0.65)
) (layer "F.Fab") (width 0) (fill solid) (tstamp 0138f6cb-7190-4a12-af47-1e018fd27b3c))
(fp_poly (pts
(xy -1 0.65)
(xy -0.4168 0.65)
(xy -0.4168 -0.65)
(xy -1 -0.65)
) (layer "F.Fab") (width 0) (fill solid) (tstamp 637ecbcd-a78f-4c8a-8dcf-472bde2de83b))
(pad "1" smd rect (at -1.016 0) (size 1.2 1.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp bd23358d-4a17-4f52-b69d-122e91d97c44))
(pad "2" smd rect (at 1.016 0) (size 1.2 1.3) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 165d5fb4-7aca-4db2-a5bd-306d43842183))
)

View File

@ -0,0 +1,274 @@
(footprint "QFN64_9X9MC_MCH" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at 0 -5.715) (layer "F.SilkS")
(effects (font (size 0.499872 0.499872) (thickness 0.109728)))
(tstamp 3e871d9c-2a1f-438a-ba83-60e5f8d0fa51)
)
(fp_text value ">VALUE" (at 0 5.715) (layer "F.Fab")
(effects (font (size 0.499872 0.499872) (thickness 0.109728)))
(tstamp f2dcc10c-546f-4bd9-be1f-368ae783a117)
)
(fp_poly (pts
(xy 2.1606 0.662)
(xy 0.8366 0.662)
(xy 0.8366 -0.662)
(xy 2.1606 -0.662)
) (layer "F.Paste") (width 0) (fill solid) (tstamp 2ec0ad3d-3957-4524-a3b2-3e97e3345ecf))
(fp_poly (pts
(xy 2.1606 -0.8366)
(xy 0.8366 -0.8366)
(xy 0.8366 -2.1606)
(xy 2.1606 -2.1606)
) (layer "F.Paste") (width 0) (fill solid) (tstamp 42a0d4d0-fc9b-4730-8110-13a84ab3bcb0))
(fp_poly (pts
(xy 0.662 0.662)
(xy -0.662 0.662)
(xy -0.662 -0.662)
(xy 0.662 -0.662)
) (layer "F.Paste") (width 0) (fill solid) (tstamp 4fbb659c-e71a-4c9b-9e8a-6da964a65cd4))
(fp_poly (pts
(xy 2.1606 2.1606)
(xy 0.8366 2.1606)
(xy 0.8366 0.8366)
(xy 2.1606 0.8366)
) (layer "F.Paste") (width 0) (fill solid) (tstamp 655728ed-daf4-477f-8a3b-b5624d1a73df))
(fp_poly (pts
(xy -0.8366 2.1606)
(xy -2.1606 2.1606)
(xy -2.1606 0.8366)
(xy -0.8366 0.8366)
) (layer "F.Paste") (width 0) (fill solid) (tstamp 6566a81c-4902-448f-a6df-8bc32a765ffa))
(fp_poly (pts
(xy -0.8366 -0.8366)
(xy -2.1606 -0.8366)
(xy -2.1606 -2.1606)
(xy -0.8366 -2.1606)
) (layer "F.Paste") (width 0) (fill solid) (tstamp 99ce666e-158b-4f82-b57f-184f0eedda5c))
(fp_poly (pts
(xy 0.662 -0.8366)
(xy -0.662 -0.8366)
(xy -0.662 -2.1606)
(xy 0.662 -2.1606)
) (layer "F.Paste") (width 0) (fill solid) (tstamp ddf6c3a2-a910-45ea-844b-a8db2b458e91))
(fp_poly (pts
(xy -0.8366 0.662)
(xy -2.1606 0.662)
(xy -2.1606 -0.662)
(xy -0.8366 -0.662)
) (layer "F.Paste") (width 0) (fill solid) (tstamp e3f7e99a-4f59-4e18-a5cb-6900c4b08cae))
(fp_poly (pts
(xy 0.662 2.1606)
(xy -0.662 2.1606)
(xy -0.662 0.8366)
(xy 0.662 0.8366)
) (layer "F.Paste") (width 0) (fill solid) (tstamp f934416f-45a1-43f5-b5e1-fba1dddf1aa4))
(fp_line (start 4.2164 4.6228) (end 4.6228 4.6228) (layer "F.SilkS") (width 0.1524) (tstamp 2015e2d6-2795-4247-a207-acb4cf8886e6))
(fp_line (start -4.2164 -4.6228) (end -4.6228 -4.6228) (layer "F.SilkS") (width 0.1524) (tstamp 2252c90a-7ebf-4d2d-a791-89982b21117b))
(fp_line (start 4.6228 -4.6228) (end 4.2164 -4.6228) (layer "F.SilkS") (width 0.1524) (tstamp 2c7437bb-66d1-4b20-ab25-e4022916701f))
(fp_line (start -4.6228 4.6228) (end -4.2164 4.6228) (layer "F.SilkS") (width 0.1524) (tstamp 32c6b15e-f755-475e-b73e-6c7fffdfa53a))
(fp_line (start -4.6228 -4.6228) (end -4.6228 -4.2164) (layer "F.SilkS") (width 0.1524) (tstamp 450f1cc5-825a-4891-8a52-c72891a373f0))
(fp_line (start 4.6228 4.6228) (end 4.6228 4.2164) (layer "F.SilkS") (width 0.1524) (tstamp 82e659cf-ebcc-4fcd-a243-d4894e4c531e))
(fp_line (start 4.6228 -4.2164) (end 4.6228 -4.6228) (layer "F.SilkS") (width 0.1524) (tstamp 8caa9a3a-8461-464c-9a5d-58f28cc73fdb))
(fp_line (start -4.6228 4.2164) (end -4.6228 4.6228) (layer "F.SilkS") (width 0.1524) (tstamp 9d03b0af-f67a-4b75-88d5-3fdc2fb53147))
(fp_circle (center -5.207 -5.207) (end -5.08 -5.207) (layer "F.SilkS") (width 0.254) (fill none) (tstamp d8471eec-d281-4956-8871-df001c4b9d0e))
(fp_line (start 4.4958 -2.5908) (end 4.4958 -2.8956) (layer "F.Fab") (width 0.1524) (tstamp 049458dd-1cf1-4608-bc61-728d12d853ec))
(fp_line (start -4.4958 -3.9116) (end -4.4958 -3.6068) (layer "F.Fab") (width 0.1524) (tstamp 05d31c0e-dc59-4cfd-8b11-9750f532f89d))
(fp_line (start 4.4958 -0.1016) (end 4.4958 -0.4064) (layer "F.Fab") (width 0.1524) (tstamp 0c83150d-4515-4bc6-947c-a18bb0f583b5))
(fp_line (start -3.9116 4.4958) (end -3.6068 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 10420e88-4a28-4643-b21a-9465d0057a73))
(fp_line (start 4.4958 0.4064) (end 4.4958 0.1016) (layer "F.Fab") (width 0.1524) (tstamp 10e57a60-62de-407e-b340-5e48d8af51a1))
(fp_line (start -4.4958 -2.8956) (end -4.4958 -2.5908) (layer "F.Fab") (width 0.1524) (tstamp 15822844-ae2b-47a9-8d24-dd3b08c9541d))
(fp_line (start 4.4958 4.4958) (end 4.4958 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 189f4344-d732-4381-8d0f-4a31c3fa9319))
(fp_line (start 4.4958 -3.0988) (end 4.4958 -3.4036) (layer "F.Fab") (width 0.1524) (tstamp 1da5d536-ffc3-40be-9afa-f932c35159bf))
(fp_line (start -0.6096 -4.4958) (end -0.9144 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 1e512696-3bdf-43ca-8ca3-64344bcd8de6))
(fp_line (start -4.4958 -3.4036) (end -4.4958 -3.0988) (layer "F.Fab") (width 0.1524) (tstamp 1efbd3c4-f6bc-449a-85e1-e54dc088704f))
(fp_line (start -1.6002 -4.4958) (end -1.905 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 1fd0b84b-3048-4a24-b2ab-7960a733598e))
(fp_line (start 4.4958 1.397) (end 4.4958 1.0922) (layer "F.Fab") (width 0.1524) (tstamp 256bd16a-a961-4e7c-bc57-c47218be0e9b))
(fp_line (start 0.1016 4.4958) (end 0.4064 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 2610c8d8-02ed-46ba-b8ce-b15585c89028))
(fp_line (start -0.1016 -4.4958) (end -0.4064 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 2b9a3dd0-e1a1-405a-ac7d-c3e3d878560b))
(fp_line (start -4.4958 0.6096) (end -4.4958 0.9144) (layer "F.Fab") (width 0.1524) (tstamp 3132d241-d7f6-4a55-9503-29f1062b517f))
(fp_line (start 0.6096 4.4958) (end 0.9144 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 31daf2d4-3957-4ce7-98d3-a043649dd97e))
(fp_line (start -4.4958 3.0988) (end -4.4958 3.4036) (layer "F.Fab") (width 0.1524) (tstamp 337e4bb2-4de6-409d-8061-54d04950842d))
(fp_line (start 4.4958 -4.4958) (end -4.4958 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 4115dd93-f2eb-4a67-8dbd-532f41b7a69f))
(fp_line (start -4.4958 -0.9144) (end -4.4958 -0.6096) (layer "F.Fab") (width 0.1524) (tstamp 453e6bba-48de-4666-a7eb-66bef0e2b02c))
(fp_line (start 3.9116 -4.4958) (end 3.6068 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 4cda505e-c23d-4504-823e-c4f0c3948a9d))
(fp_line (start 4.4958 -1.0922) (end 4.4958 -1.397) (layer "F.Fab") (width 0.1524) (tstamp 503e0041-3118-4b51-83f1-b9c14f77f4b1))
(fp_line (start -4.4958 -3.2258) (end -3.2258 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 54b28547-cb8b-489a-a9df-6ad15177b2e9))
(fp_line (start -3.0988 -4.4958) (end -3.4036 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 5918fd35-f23c-42ff-9fba-9c5a9ac207d3))
(fp_line (start 4.4958 3.4036) (end 4.4958 3.0988) (layer "F.Fab") (width 0.1524) (tstamp 5f822173-84ee-42a7-a838-54e1cfc17adf))
(fp_line (start 3.6068 4.4958) (end 3.9116 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 632e8422-a140-4238-81b2-8829c7485b2e))
(fp_line (start -4.4958 -2.413) (end -4.4958 -2.1082) (layer "F.Fab") (width 0.1524) (tstamp 67448bdd-3f5c-4a5e-8105-da8836876044))
(fp_line (start 2.1082 4.4958) (end 2.413 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 69ab593b-1948-43ac-9d69-727c88e9b459))
(fp_line (start -2.8956 4.4958) (end -2.5908 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 6dc5284f-c6ff-453d-b163-9f63ed7892ad))
(fp_line (start -1.905 4.4958) (end -1.6002 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 6df1e081-1245-47a6-86a6-0c79f1f9db56))
(fp_line (start 1.6002 4.4958) (end 1.905 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 709494b8-7fc2-496a-8451-57d7441e8505))
(fp_line (start -4.4958 3.6068) (end -4.4958 3.9116) (layer "F.Fab") (width 0.1524) (tstamp 7212588c-17f7-4956-b77b-065540b4ee49))
(fp_line (start 2.8956 -4.4958) (end 2.5908 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 7ce7324c-91ea-47c6-b7c2-bd1b2f2a14c2))
(fp_line (start -3.4036 4.4958) (end -3.0988 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 7fb70e9b-db47-465c-b7f5-3f144c3fe511))
(fp_line (start 4.4958 -2.1082) (end 4.4958 -2.413) (layer "F.Fab") (width 0.1524) (tstamp 832b785c-4536-49cf-b6de-cedea7d015a6))
(fp_line (start 2.5908 4.4958) (end 2.8956 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 838b54da-8d76-4af0-a533-f421e7be02c2))
(fp_line (start -4.4958 2.5908) (end -4.4958 2.8956) (layer "F.Fab") (width 0.1524) (tstamp 8c3c449f-a035-419f-9950-338baab558f6))
(fp_line (start 4.4958 -1.6002) (end 4.4958 -1.905) (layer "F.Fab") (width 0.1524) (tstamp 8e588935-ced6-458a-a187-91e74c238ac8))
(fp_line (start -1.397 4.4958) (end -1.0922 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 8fe96cfe-a951-426f-80b8-9a4ee6265385))
(fp_line (start -4.4958 1.0922) (end -4.4958 1.397) (layer "F.Fab") (width 0.1524) (tstamp 8ff9eeba-c353-40da-b499-b20afa11f353))
(fp_line (start -4.4958 -4.4958) (end -4.4958 4.4958) (layer "F.Fab") (width 0.1524) (tstamp 93722526-b9c8-4550-8cdd-369ecb315477))
(fp_line (start -1.0922 -4.4958) (end -1.397 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 9a7cda4c-e257-4759-beca-0a008c33f019))
(fp_line (start 0.9144 -4.4958) (end 0.6096 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp 9c8602d4-958b-412c-8bb3-0d75d4a4147b))
(fp_line (start 4.4958 2.8956) (end 4.4958 2.5908) (layer "F.Fab") (width 0.1524) (tstamp ab68518b-68f5-4fed-903c-3d10b7662e9c))
(fp_line (start -4.4958 -0.4064) (end -4.4958 -0.1016) (layer "F.Fab") (width 0.1524) (tstamp ae845f43-7bd0-4d9b-b6ce-ea24c3f78b67))
(fp_line (start 4.4958 2.413) (end 4.4958 2.1082) (layer "F.Fab") (width 0.1524) (tstamp ae9df4c2-28e5-48f0-aff2-79a0c1a8a15a))
(fp_line (start -0.4064 4.4958) (end -0.1016 4.4958) (layer "F.Fab") (width 0.1524) (tstamp b27f2059-e221-491a-8338-8399fdb6602a))
(fp_line (start 1.905 -4.4958) (end 1.6002 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp b30b8721-3fe1-41c3-8198-1a4ccb7ceb74))
(fp_line (start -4.4958 4.4958) (end 4.4958 4.4958) (layer "F.Fab") (width 0.1524) (tstamp b476f917-8b6e-4132-b869-771909a051c6))
(fp_line (start 1.397 -4.4958) (end 1.0922 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp b5382627-0c47-465b-972a-a305e2e8c255))
(fp_line (start -4.4958 0.1016) (end -4.4958 0.4064) (layer "F.Fab") (width 0.1524) (tstamp b6e57a1a-77bc-4011-8ab7-3ca5980c960f))
(fp_line (start 4.4958 1.905) (end 4.4958 1.6002) (layer "F.Fab") (width 0.1524) (tstamp b8b1c0a5-6e37-467f-b8f0-bf600730b7aa))
(fp_line (start 2.413 -4.4958) (end 2.1082 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp b8c56df8-3039-424a-9681-56118c58b690))
(fp_line (start 1.0922 4.4958) (end 1.397 4.4958) (layer "F.Fab") (width 0.1524) (tstamp bb394421-3605-4756-9508-929d6758f654))
(fp_line (start -3.6068 -4.4958) (end -3.9116 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp be1b6c3d-7e5b-4a40-908c-b936697a3a15))
(fp_line (start 4.4958 -0.6096) (end 4.4958 -0.9144) (layer "F.Fab") (width 0.1524) (tstamp c1182e1e-de77-4a90-a154-0b526180f4ec))
(fp_line (start -4.4958 2.1082) (end -4.4958 2.413) (layer "F.Fab") (width 0.1524) (tstamp c1bc3314-d8be-4ff1-a3c3-ed1c9b2d7f54))
(fp_line (start -2.1082 -4.4958) (end -2.413 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp c2f4caf6-9472-4b57-8792-997c7297ad6f))
(fp_line (start 3.4036 -4.4958) (end 3.0988 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp c580dbd6-b23d-4473-a656-cd0cbd690fa4))
(fp_line (start 4.4958 0.9144) (end 4.4958 0.6096) (layer "F.Fab") (width 0.1524) (tstamp c8e7357a-ffca-40b2-9f1f-0e667dd778c7))
(fp_line (start 4.4958 -3.6068) (end 4.4958 -3.9116) (layer "F.Fab") (width 0.1524) (tstamp c968c46a-836c-481d-9a49-06172946f81e))
(fp_line (start -4.4958 -1.905) (end -4.4958 -1.6002) (layer "F.Fab") (width 0.1524) (tstamp cd74684a-5938-4604-a11c-848de541a8a6))
(fp_line (start 0.4064 -4.4958) (end 0.1016 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp d06a81a3-464e-4192-865d-76347fe63188))
(fp_line (start -4.4958 1.6002) (end -4.4958 1.905) (layer "F.Fab") (width 0.1524) (tstamp e08784ed-8b2a-49ee-bef9-7b2d3857e5ba))
(fp_line (start 4.4958 3.9116) (end 4.4958 3.6068) (layer "F.Fab") (width 0.1524) (tstamp e40896aa-4f74-4b18-add9-342665b200b4))
(fp_line (start -0.9144 4.4958) (end -0.6096 4.4958) (layer "F.Fab") (width 0.1524) (tstamp e9b70cd9-c935-4ef0-8469-421eb4fd1bac))
(fp_line (start -4.4958 -1.397) (end -4.4958 -1.0922) (layer "F.Fab") (width 0.1524) (tstamp eb56c15a-02df-4139-84ab-d8a1651203fa))
(fp_line (start 3.0988 4.4958) (end 3.4036 4.4958) (layer "F.Fab") (width 0.1524) (tstamp f58471ed-0cee-4f6a-b2e5-1373fa172f0d))
(fp_line (start -2.5908 -4.4958) (end -2.8956 -4.4958) (layer "F.Fab") (width 0.1524) (tstamp f9ac7fe7-3a4d-4488-bc27-2d7e673008b2))
(fp_line (start -2.413 4.4958) (end -2.1082 4.4958) (layer "F.Fab") (width 0.1524) (tstamp f9cb66b4-6171-4d5f-b302-fa11feb7e25e))
(pad "1" smd rect (at -4.3942 -3.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 0962937a-7e17-49c2-843a-309b9e705166))
(pad "2" smd rect (at -4.3942 -3.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp a2fedbd9-479b-4398-baab-86589de39528))
(pad "3" smd rect (at -4.3942 -2.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp e98112c7-b41a-4ab2-8cb2-235538dcf529))
(pad "4" smd rect (at -4.3942 -2.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 01c8af07-90ff-4b3d-b404-db01135cbcbd))
(pad "5" smd rect (at -4.3942 -1.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 36cd9aaf-ae25-480f-827b-e14d53a73530))
(pad "6" smd rect (at -4.3942 -1.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 89a4dae4-94d7-4e16-b389-021d58b92093))
(pad "7" smd rect (at -4.3942 -0.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp e79aeb81-b0e9-42dc-9659-8a0783bf4de2))
(pad "8" smd rect (at -4.3942 -0.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 7c204068-d0aa-459b-b4fa-71e8935dd9dd))
(pad "9" smd rect (at -4.3942 0.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 1c34ed7b-f29c-48ab-a67f-7e34e10696d9))
(pad "10" smd rect (at -4.3942 0.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 4b62ffb5-f26f-42ce-b28d-6d417a8eb453))
(pad "11" smd rect (at -4.3942 1.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp b93c79ac-77e5-4fb0-abe6-0f662be65153))
(pad "12" smd rect (at -4.3942 1.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp a82deada-b3dc-43d8-9675-ea9e287e99f4))
(pad "13" smd rect (at -4.3942 2.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp f89abcc5-9507-4994-b7ec-08e99b565c34))
(pad "14" smd rect (at -4.3942 2.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 3b07801d-fbc1-4f67-a403-def8cd01c41d))
(pad "15" smd rect (at -4.3942 3.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp fc1fc9cb-2099-4de4-8cdd-d83af9dd5f90))
(pad "16" smd rect (at -4.3942 3.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp bc1d3611-8bc5-4413-b9ae-8a04bfaa0730))
(pad "17" smd rect (at -3.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp eae353fe-5b10-44d1-aee2-527fb697339e))
(pad "18" smd rect (at -3.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 0be2d7ef-74f7-4c54-9c11-567b325a3550))
(pad "19" smd rect (at -2.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 94af1e4e-830f-47d9-b4af-1d2f77f0e846))
(pad "20" smd rect (at -2.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 0c875dfd-527c-4ba5-84fd-4d171caf74bb))
(pad "21" smd rect (at -1.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp f8327bac-2bfe-4f25-b58b-a76fe59386c6))
(pad "22" smd rect (at -1.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 39a31cfe-a89d-4f7b-b480-a9458bbbaeec))
(pad "23" smd rect (at -0.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp e1e0568d-368c-488e-902b-8d4d2ca00d90))
(pad "24" smd rect (at -0.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp b4c8b1b6-b104-424e-b3ae-e10a63946100))
(pad "25" smd rect (at 0.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 43bd0070-dd63-4c69-be69-c4ea652b922a))
(pad "26" smd rect (at 0.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 4a1cf31f-d5f4-4210-ac17-84340c530d59))
(pad "27" smd rect (at 1.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp a6955bbd-8e2f-47ac-9d2d-1dc038277299))
(pad "28" smd rect (at 1.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 7ffcf02c-15fa-481f-81f2-bd642d7f79cb))
(pad "29" smd rect (at 2.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 41a0665e-4d1a-4fa6-90c8-82faf5e06664))
(pad "30" smd rect (at 2.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp cc003555-3729-46c6-8945-cbd09db502b4))
(pad "31" smd rect (at 3.25 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 5c77f960-ade6-4efb-9c4b-dcf559c7eac2))
(pad "32" smd rect (at 3.75 4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp d89499ae-4014-4f85-949d-db2745a5c1be))
(pad "33" smd rect (at 4.3942 3.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp a15c3c43-a66d-4557-9dd1-9291285217ad))
(pad "34" smd rect (at 4.3942 3.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 5654db23-3f90-4ce8-879c-12ebe3c1b2f7))
(pad "35" smd rect (at 4.3942 2.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp cf36d037-47b9-4211-a0ba-1b21c826386c))
(pad "36" smd rect (at 4.3942 2.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 7446c48e-88d2-43f3-9107-02dc8c28c890))
(pad "37" smd rect (at 4.3942 1.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp dc190b06-ebe1-480f-9f26-814b886ca30e))
(pad "38" smd rect (at 4.3942 1.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp f31d87cd-a520-48c6-bd59-16ce7a0c8a4c))
(pad "39" smd rect (at 4.3942 0.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 3fa7895a-6817-41c8-b122-323e15af05f5))
(pad "40" smd rect (at 4.3942 0.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 0af777e9-db83-44e5-82b3-4d0cc1118c82))
(pad "41" smd rect (at 4.3942 -0.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp cc198b2a-7c6d-448a-ba27-a1a291727e63))
(pad "42" smd rect (at 4.3942 -0.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 4f4f7230-a137-4430-aeca-4b9793e7ef29))
(pad "43" smd rect (at 4.3942 -1.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 09e93bbc-81b5-4780-a105-25986af4e57c))
(pad "44" smd rect (at 4.3942 -1.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 80626780-24b0-434f-a133-1859aa0ffc80))
(pad "45" smd rect (at 4.3942 -2.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp e213e53b-8e52-48ca-828a-5f55b9135f2a))
(pad "46" smd rect (at 4.3942 -2.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 4c7b79ca-8f05-473a-80e6-3bebce2e229f))
(pad "47" smd rect (at 4.3942 -3.25 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 2cab5ab7-2cc3-4048-816f-20e4725e4987))
(pad "48" smd rect (at 4.3942 -3.75 270) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 8441a91d-f48e-4251-bcaf-dc7094507898))
(pad "49" smd rect (at 3.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 3c1b33ef-acb9-4266-b8c6-bb013e5dae2c))
(pad "50" smd rect (at 3.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp a8a245a0-1165-437d-87fe-7eee7997aee7))
(pad "51" smd rect (at 2.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 1e99a129-c646-417d-b055-3f37123cf8ef))
(pad "52" smd rect (at 2.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 82864969-1aa8-49df-9eac-4458700e6ca7))
(pad "53" smd rect (at 1.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 4d35c705-5a51-4806-a126-12c937c6b1e7))
(pad "54" smd rect (at 1.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp d491f121-2845-455d-8541-44fa2ec6c594))
(pad "55" smd rect (at 0.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 2186d0aa-6d37-46d1-8257-2c34c61fca1c))
(pad "56" smd rect (at 0.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 732ba6d0-6193-4c48-8070-6779e2d08aac))
(pad "57" smd rect (at -0.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp f7f108f5-a5d2-4a71-bd92-4b7a64cd67ef))
(pad "58" smd rect (at -0.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 5a9c3c03-bc6d-4d54-97af-ccd4b0ec62bd))
(pad "59" smd rect (at -1.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 02619e26-a84a-4ced-aa63-185f88279688))
(pad "60" smd rect (at -1.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 92a99c1b-1f77-4822-9ef0-404ea7ef9b34))
(pad "61" smd rect (at -2.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 90707762-bc1e-4e07-b417-1728eca9f661))
(pad "62" smd rect (at -2.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 3db20676-41a3-4cd5-aaaa-fb7aac0e3ef9))
(pad "63" smd rect (at -3.25 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 2a66cae8-a800-4958-8ae7-e7d7f25b2674))
(pad "64" smd rect (at -3.75 -4.3942 180) (size 0.254 0.8128) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 70110040-44aa-4caf-9824-f55720183967))
(pad "65" smd rect (at 0 0) (size 4.572 4.572) (layers "F.Cu" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 1a1fa73c-dc68-468e-8e77-2f7147d220c5))
)

View File

@ -0,0 +1,29 @@
(footprint "SOD-323F" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at -1.8 -0.9) (layer "F.SilkS")
(effects (font (size 0.747776 0.747776) (thickness 0.065024)) (justify left bottom))
(tstamp 8d130de6-21ac-49e1-b622-a95fb15425d4)
)
(fp_text value ">VALUE" (at -2.1 1.7) (layer "F.Fab")
(effects (font (size 0.747776 0.747776) (thickness 0.065024)) (justify left bottom))
(tstamp f5198c37-c2af-4b4a-ace7-b957cdea8352)
)
(fp_line (start 0.4 -0.6) (end 0.4 0.6) (layer "F.SilkS") (width 0.127) (tstamp 0165360a-2e66-4636-a3c0-be1ba6b7146b))
(fp_line (start 0.4 0.6) (end 0.3 0.6) (layer "F.SilkS") (width 0.127) (tstamp 31d6649e-2cb0-4b56-9f6f-94a0dc543d5a))
(fp_line (start 0.85 -0.65) (end 0.85 0.65) (layer "F.SilkS") (width 0.127) (tstamp 320884e5-1885-40b5-8b79-b48357d2fa56))
(fp_line (start 0.3 0.6) (end 0.3 -0.6) (layer "F.SilkS") (width 0.127) (tstamp 3a4ae6db-a2ad-4e01-be88-bd3d813a8e29))
(fp_line (start -0.85 0.65) (end -0.85 -0.65) (layer "F.SilkS") (width 0.127) (tstamp 4fef4a0d-c874-4fe4-9bb4-3b8d64e9347b))
(fp_line (start 0.85 0.65) (end -0.85 0.65) (layer "F.SilkS") (width 0.127) (tstamp 7ebd6805-bd0d-4272-b0ea-a17483d80ca5))
(fp_line (start -0.85 -0.65) (end 0.85 -0.65) (layer "F.SilkS") (width 0.127) (tstamp cb3724d9-81a8-487f-80a1-8ccce53d6d55))
(fp_line (start -0.9 -0.2) (end -1.2 -0.2) (layer "F.Fab") (width 0.127) (tstamp 9290e464-7c10-4d28-b961-53ead5d00f7c))
(fp_line (start 1.2 0.2) (end 0.9 0.2) (layer "F.Fab") (width 0.127) (tstamp 9a951f19-776d-408e-9dd7-550047b0e2a6))
(fp_line (start -1.2 0.2) (end -0.9 0.2) (layer "F.Fab") (width 0.127) (tstamp c213b2e9-ebc1-49b9-ad72-5803127305a5))
(fp_line (start 0.9 -0.2) (end 1.2 -0.2) (layer "F.Fab") (width 0.127) (tstamp c31a6350-0001-4420-90f8-12019bfccaf7))
(fp_line (start 1.2 -0.2) (end 1.2 0.2) (layer "F.Fab") (width 0.127) (tstamp d0e73230-a9c2-4595-9b95-ba35aa96b070))
(fp_line (start -1.2 -0.2) (end -1.2 0.2) (layer "F.Fab") (width 0.127) (tstamp d91a4af6-4bb8-467f-b69b-1a906bc211a7))
(pad "A" smd rect (at -1 0) (size 1 0.8) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 72b76f37-b628-4712-9ff6-021da3f7a013))
(pad "C" smd rect (at 1 0) (size 1 0.8) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp cb8dd863-14d8-4470-ad3e-b81fad315442))
)

View File

@ -0,0 +1,34 @@
(footprint "SOT65P210X110-5N" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at -1.8 -1.4) (layer "F.SilkS")
(effects (font (size 0.747776 0.747776) (thickness 0.065024)) (justify left bottom))
(tstamp 457cb9de-fc22-4a85-9c82-7438f85d137d)
)
(fp_text value ">VALUE" (at -1.8 1.4) (layer "F.Fab")
(effects (font (size 0.747776 0.747776) (thickness 0.065024)) (justify left top))
(tstamp 674ecd7e-5cc8-4469-9690-04223952bf82)
)
(fp_line (start -0.625 1.17) (end 0.625 1.17) (layer "F.SilkS") (width 0.127) (tstamp 2f22abf8-16bd-42ad-a0ee-757b9dc94627))
(fp_line (start 0.625 -1.17) (end -0.625 -1.17) (layer "F.SilkS") (width 0.127) (tstamp 7fba8434-1fa5-492e-93c3-f5210db0af84))
(fp_circle (center -2.1 -0.9) (end -2 -0.9) (layer "F.SilkS") (width 0.2) (fill none) (tstamp 687a4ff9-95c3-4e33-9daa-47acc0e40e1e))
(fp_line (start -1.805 -1.2625) (end -1.805 1.2625) (layer "F.CrtYd") (width 0.05) (tstamp 17863368-ebf9-4b76-8a27-2431df2a120c))
(fp_line (start 1.805 -1.2625) (end -1.805 -1.2625) (layer "F.CrtYd") (width 0.05) (tstamp 62cf88fb-d482-4b99-9b0f-281e43055f8b))
(fp_line (start -1.805 1.2625) (end 1.805 1.2625) (layer "F.CrtYd") (width 0.05) (tstamp b5135559-37a4-492d-a6dc-8ab982428252))
(fp_line (start 1.805 1.2625) (end 1.805 -1.2625) (layer "F.CrtYd") (width 0.05) (tstamp dcf4a5aa-5c00-4b2e-ba11-24374e17c340))
(fp_line (start 0.625 1.0125) (end 0.625 -1.0125) (layer "F.Fab") (width 0.127) (tstamp 0d92b703-ef1a-45ad-8945-076840a3419d))
(fp_line (start 0.625 -1.0125) (end -0.625 -1.0125) (layer "F.Fab") (width 0.127) (tstamp 3b31ae4c-5100-4114-b54f-c515c85e31ef))
(fp_line (start -0.625 -1.0125) (end -0.625 1.0125) (layer "F.Fab") (width 0.127) (tstamp 4479abbf-71f9-42d6-b915-553dfa74ac7a))
(fp_line (start -0.625 1.0125) (end 0.625 1.0125) (layer "F.Fab") (width 0.127) (tstamp a7b3fdba-c9c6-4541-aa58-e7d5af1159f7))
(fp_circle (center -2.1 -0.9) (end -2 -0.9) (layer "F.Fab") (width 0.2) (fill none) (tstamp 4b3d66f1-3431-45ab-8164-ecd0988102d2))
(pad "1" smd rect (at -0.97 -0.65) (size 1.17 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 79539752-66ac-4cde-9791-7e50fc9c08e9))
(pad "2" smd rect (at -0.97 0) (size 1.17 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 412a873e-1c99-4a4b-86e1-a676f2285a25))
(pad "3" smd rect (at -0.97 0.65) (size 1.17 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp d56e9cb4-6e4a-4ec0-a67f-7fd53cd17c95))
(pad "4" smd rect (at 0.97 0.65) (size 1.17 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp bed9dd9c-f127-43c3-a252-0194a4ef1905))
(pad "5" smd rect (at 0.97 -0.65) (size 1.17 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 468863fa-6e22-4082-9c4f-d4c63b16d5f2))
)

View File

@ -0,0 +1,282 @@
(footprint "SW_EVP-BB1AAB000"
(version 20240108)
(generator "pcbnew")
(generator_version "8.0")
(layer "F.Cu")
(property "Reference" "REF**"
(at -2.628409 -1.50195 0)
(layer "F.SilkS")
(uuid "5bfc1104-d8e6-463d-96bf-3b297940d17a")
(effects
(font
(size 1.169909 1.169909)
(thickness 0.101731)
)
(justify left bottom)
)
)
(property "Value" ">VALUE"
(at -2.629818 3.005518 0)
(layer "F.Fab")
(uuid "0d6e3660-78d4-4c9d-aa40-0c2181937185")
(effects
(font
(size 1.170545 1.170545)
(thickness 0.101786)
)
(justify left bottom)
)
)
(property "Footprint" ""
(at 0 0 0)
(layer "F.Fab")
(hide yes)
(uuid "adf19691-fa3b-4d7a-abc1-3f78748c21cf")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(layer "F.Fab")
(hide yes)
(uuid "e644642e-886c-44ac-b86f-d6c911d62221")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(layer "F.Fab")
(hide yes)
(uuid "36f34cd2-251b-4e5d-9062-61208ddf5d26")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(fp_line
(start -1.325 -0.375)
(end -1.325 0.375)
(stroke
(width 0.465)
(type solid)
)
(layer "F.Paste")
(uuid "4d800587-4919-426d-b1ba-7b44b3e9466f")
)
(fp_line
(start 1.325 -0.375)
(end 1.325 0.375)
(stroke
(width 0.465)
(type solid)
)
(layer "F.Paste")
(uuid "ce64bb60-7755-4c83-8701-9a2cef3a1c7b")
)
(fp_poly
(pts
(xy -1.095 -0.145) (xy -1.555 -0.145) (xy -1.555 -0.605) (xy -1.095 -0.605)
)
(stroke
(width 0)
(type solid)
)
(fill solid)
(layer "F.Paste")
(uuid "6db398f8-2729-450b-aef1-8242c2cd2e0f")
)
(fp_poly
(pts
(xy -1.095 0.605) (xy -1.555 0.605) (xy -1.555 0.145) (xy -1.095 0.145)
)
(stroke
(width 0)
(type solid)
)
(fill solid)
(layer "F.Paste")
(uuid "84da75db-45c9-4044-8d1e-347621238750")
)
(fp_poly
(pts
(xy 1.555 -0.145) (xy 1.095 -0.145) (xy 1.095 -0.605) (xy 1.555 -0.605)
)
(stroke
(width 0)
(type solid)
)
(fill solid)
(layer "F.Paste")
(uuid "e8fb6dd3-e158-4257-9e15-870695c296b7")
)
(fp_poly
(pts
(xy 1.555 0.605) (xy 1.095 0.605) (xy 1.095 0.145) (xy 1.555 0.145)
)
(stroke
(width 0)
(type solid)
)
(fill solid)
(layer "F.Paste")
(uuid "85bec7d7-877b-406c-bad8-abaa99fcbe23")
)
(fp_line
(start -1.3 -0.9635)
(end 1.3 -0.9635)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "daeab9ff-4ca6-4d3e-8486-53bc65dc2c70")
)
(fp_line
(start -1.3 0.9635)
(end 1.3 0.9635)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "2a0673e1-384a-4091-aad2-73c6b1e7babd")
)
(fp_line
(start -1.325 -0.375)
(end -1.325 0.375)
(stroke
(width 0.55)
(type solid)
)
(layer "F.Mask")
(uuid "573428fa-1f2a-49c8-b999-dc918c489ec0")
)
(fp_line
(start 1.325 -0.375)
(end 1.325 0.375)
(stroke
(width 0.55)
(type solid)
)
(layer "F.Mask")
(uuid "3f0f8722-1483-4aa6-bcae-dcff8ecba59b")
)
(fp_line
(start -1.85 -1.05)
(end 1.85 -1.05)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "d6daa52c-633b-4f9e-9611-999747c83ff7")
)
(fp_line
(start -1.85 1.05)
(end -1.85 -1.05)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "b1dcf4ab-520c-4ad1-8398-87f8dd8fec29")
)
(fp_line
(start 1.85 -1.05)
(end 1.85 1.05)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "4939d836-3fdc-47f9-b885-f87382603570")
)
(fp_line
(start 1.85 1.05)
(end -1.85 1.05)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "e4f415ce-fdbb-4c24-b2a1-e501eae24f60")
)
(fp_line
(start -1.3 -0.8)
(end 1.3 -0.8)
(stroke
(width 0.127)
(type solid)
)
(layer "F.Fab")
(uuid "8e13a383-d573-4545-b914-42a75e606b8c")
)
(fp_line
(start -1.3 0.8)
(end -1.3 -0.8)
(stroke
(width 0.127)
(type solid)
)
(layer "F.Fab")
(uuid "1e090053-4c0c-424f-8664-54986066ef27")
)
(fp_line
(start 1.3 -0.8)
(end 1.3 0.8)
(stroke
(width 0.127)
(type solid)
)
(layer "F.Fab")
(uuid "df66859c-c21f-4048-8e27-8d86d5f411f0")
)
(fp_line
(start 1.3 0.8)
(end -1.3 0.8)
(stroke
(width 0.127)
(type solid)
)
(layer "F.Fab")
(uuid "31b618ce-8704-4bd2-948f-25d290b97ca7")
)
(pad "A1" smd rect
(at -1.325 -0.375)
(size 0.55 0.55)
(layers "F.Cu" "F.Mask")
(solder_mask_margin 0.0635)
(uuid "efeb691d-5593-439a-a931-ffe8a985f2c4")
)
(pad "A2" smd rect
(at -1.325 0.375)
(size 0.55 0.55)
(layers "F.Cu" "F.Mask")
(solder_mask_margin 0.0635)
(uuid "4ceaa3e3-568f-42dd-8491-a773d8add0f9")
)
(pad "B1" smd rect
(at 1.325 -0.375)
(size 0.55 0.55)
(layers "F.Cu" "F.Mask")
(solder_mask_margin 0.0635)
(uuid "5bdc0a88-b8c1-4c56-b2c0-8737e7eeb5ed")
)
(pad "B2" smd rect
(at 1.325 0.375)
(size 0.55 0.55)
(layers "F.Cu" "F.Mask")
(solder_mask_margin 0.0635)
(uuid "fb158033-514b-4126-84ba-b82cd884d9a2")
)
)

View File

@ -0,0 +1,32 @@
(footprint "XTAL3215" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(fp_text reference "REF**" (at -2.3 2.2) (layer "F.SilkS")
(effects (font (size 0.93472 0.93472) (thickness 0.08128)) (justify left bottom))
(tstamp a9c6df82-9b3d-430b-ac3d-773dba21edac)
)
(fp_text value ">VALUE" (at -2.6 -1.2) (layer "F.Fab")
(effects (font (size 0.93472 0.93472) (thickness 0.08128)) (justify left bottom))
(tstamp 2d0c10c9-abbe-4218-b4a2-a2a06a7ca92f)
)
(fp_line (start -1.6 0.4172) (end -1.6 -0.4764) (layer "F.SilkS") (width 0.127) (tstamp 078a622d-2e6e-4da6-9441-6376052c47eb))
(fp_line (start -1.1 -0.4) (end 1 -0.4) (layer "F.SilkS") (width 0.127) (tstamp 16000523-6636-4a7c-90fe-2f09fce78d5e))
(fp_line (start -1.3 0.2) (end -1.3 -0.1) (layer "F.SilkS") (width 0.127) (tstamp 46b2dcb4-bf9c-401f-993d-249e79f10a6f))
(fp_line (start 1 0.4) (end -1 0.4) (layer "F.SilkS") (width 0.127) (tstamp 519e3fa1-ccf8-482c-80ef-bd5f407c1c5f))
(fp_line (start 1.3172 0.7) (end -1.3172 0.7) (layer "F.SilkS") (width 0.127) (tstamp 93307323-bd88-4ddc-ab8a-4f2d823906f4))
(fp_line (start -1.3764 -0.7) (end 1.2838 -0.7) (layer "F.SilkS") (width 0.127) (tstamp 96d8ac11-ef54-4b37-87dc-dbd4ca0b1cb4))
(fp_line (start 1.6 -0.3838) (end 1.6 0.4172) (layer "F.SilkS") (width 0.127) (tstamp bec9dc30-b160-4d84-ad5b-772b0009be76))
(fp_line (start 1.3 -0.1) (end 1.3 0.1) (layer "F.SilkS") (width 0.127) (tstamp f4ad8862-e14a-4b15-bbdf-fb0b7a3f55f3))
(fp_arc (start -1.3172 0.7) (mid -1.51717 0.61717) (end -1.6 0.4172) (layer "F.SilkS") (width 0.127) (tstamp 0c48587a-6c32-4781-9323-526f5fdc2492))
(fp_arc (start 1.3 0.1) (mid 1.212131 0.312132) (end 0.999999 0.399999) (layer "F.SilkS") (width 0.127) (tstamp 11535cde-6a05-4f6a-a42d-7840292384eb))
(fp_arc (start -1 0.4) (mid -1.191422 0.362132) (end -1.300001 0.199999) (layer "F.SilkS") (width 0.127) (tstamp 1d879171-52ed-4187-9c41-72a9e6c6b120))
(fp_arc (start -1.6 -0.4764) (mid -1.534509 -0.634509) (end -1.3764 -0.7) (layer "F.SilkS") (width 0.127) (tstamp 285901dd-7be7-4fde-bb96-976224872c4a))
(fp_arc (start -1.3 -0.1) (mid -1.262161 -0.291441) (end -1.099999 -0.399999) (layer "F.SilkS") (width 0.127) (tstamp 2eacfb6a-d7d1-4bde-b6bc-6096465bc8c1))
(fp_arc (start 1.2838 -0.7) (mid 1.507387 -0.607387) (end 1.6 -0.3838) (layer "F.SilkS") (width 0.127) (tstamp 499a51d8-52e8-4eb8-aeac-96a1be49503b))
(fp_arc (start 1.6 0.4172) (mid 1.51717 0.61717) (end 1.3172 0.7) (layer "F.SilkS") (width 0.127) (tstamp ab55d0d5-009c-4663-a9bc-dfc975cfe4f8))
(fp_arc (start 1 -0.4) (mid 1.212103 -0.312103) (end 1.3 -0.1) (layer "F.SilkS") (width 0.127) (tstamp e095b566-4c68-4a1b-a41d-59524889820a))
(pad "P$1" smd rect (at 1.2 0) (size 1.1 1.9) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 4ee07c5d-9f78-4558-bf26-a2f36b7ff744))
(pad "P$2" smd rect (at -1.2 0 180) (size 1.1 1.9) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 4d20cca2-e97b-4688-a784-b838b6ce3b76))
)

View File

@ -0,0 +1,38 @@
(footprint "_0402MP" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(descr "<b>0402 MicroPitch<p>")
(fp_text reference "REF**" (at -0.635 -0.4763) (layer "F.SilkS")
(effects (font (size 0.499872 0.499872) (thickness 0.109728)) (justify left bottom))
(tstamp 66f54e50-362a-4456-a671-e8b742aabfe7)
)
(fp_text value ">VALUE" (at -0.635 0.7938) (layer "F.Fab")
(effects (font (size 0.36576 0.36576) (thickness 0.04064)) (justify left bottom))
(tstamp 7205e05a-db5d-40d5-aa8d-0965b4d6a0a3)
)
(fp_poly (pts
(xy -0.1 0.2)
(xy 0.1 0.2)
(xy 0.1 -0.2)
(xy -0.1 -0.2)
) (layer "F.Adhes") (width 0) (fill solid) (tstamp 151adedc-3095-4283-bc8b-67b6c834501c))
(fp_line (start 0 -0.127) (end 0 0.127) (layer "F.SilkS") (width 0.2032) (tstamp a31d0c78-4bd5-4839-be72-14af5f3f165d))
(fp_line (start -0.245 -0.174) (end 0.245 -0.174) (layer "F.Fab") (width 0.1016) (tstamp ddaa21a2-edd4-401c-ac76-6f183cf5be3d))
(fp_line (start 0.245 0.174) (end -0.245 0.174) (layer "F.Fab") (width 0.1016) (tstamp ec8c18e2-500a-4935-81c1-76a24c731e63))
(fp_poly (pts
(xy -0.5 0.25)
(xy -0.254 0.25)
(xy -0.254 -0.25)
(xy -0.5 -0.25)
) (layer "F.Fab") (width 0) (fill solid) (tstamp 335257f6-f886-460f-b838-e2d8232128c8))
(fp_poly (pts
(xy 0.2588 0.25)
(xy 0.5 0.25)
(xy 0.5 -0.25)
(xy 0.2588 -0.25)
) (layer "F.Fab") (width 0) (fill solid) (tstamp 3d7cd32b-2bab-4b7c-a3da-482a6d596be7))
(pad "1" smd rect (at -0.508 0) (size 0.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp c609a9e6-5647-44af-a743-b732a2a0c37d))
(pad "2" smd rect (at 0.508 0) (size 0.5 0.5) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 1249fd1b-9f93-42c3-bb42-dae4056aa043))
)

View File

@ -0,0 +1,38 @@
(footprint "_0603MP" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(descr "<b>0603 MicroPitch</b>")
(fp_text reference "REF**" (at -0.9525 -0.635) (layer "F.SilkS")
(effects (font (size 0.666496 0.666496) (thickness 0.146304)) (justify left bottom))
(tstamp a1f3b960-720c-4050-8c7b-e4fb81f7c447)
)
(fp_text value ">VALUE" (at -0.9525 0.9525) (layer "F.Fab")
(effects (font (size 0.36576 0.36576) (thickness 0.04064)) (justify left bottom))
(tstamp f7454cc9-a5be-454b-b5bb-decc69423e09)
)
(fp_poly (pts
(xy -0.1999 0.25)
(xy 0.1999 0.25)
(xy 0.1999 -0.25)
(xy -0.1999 -0.25)
) (layer "F.Adhes") (width 0) (fill solid) (tstamp 23d6168b-6030-48a2-96be-111de678cbf9))
(fp_line (start 0 -0.254) (end 0 0.254) (layer "F.SilkS") (width 0.2032) (tstamp ebb6e458-b901-41c0-b269-52de840ade47))
(fp_line (start -0.432 0.306) (end 0.432 0.306) (layer "F.Fab") (width 0.1016) (tstamp 0ff59386-9f8f-4bc5-871b-4804e001e34f))
(fp_line (start 0.432 -0.306) (end -0.432 -0.306) (layer "F.Fab") (width 0.1016) (tstamp c1b693b3-0c6a-47f3-bd85-e73de386676a))
(fp_poly (pts
(xy 0.4318 0.4)
(xy 0.8 0.4)
(xy 0.8 -0.4)
(xy 0.4318 -0.4)
) (layer "F.Fab") (width 0) (fill solid) (tstamp 98d6e8c0-7c85-4056-b142-5a49d5f78f6a))
(fp_poly (pts
(xy -0.8 0.4)
(xy -0.4318 0.4)
(xy -0.4318 -0.4)
(xy -0.8 -0.4)
) (layer "F.Fab") (width 0) (fill solid) (tstamp c67e1e1d-f03c-4cd2-855a-6f77ed9840d4))
(pad "1" smd rect (at -0.762 0) (size 0.8 0.8) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp 261178c6-668c-4d25-9316-e97dc5e52e2d))
(pad "2" smd rect (at 0.762 0) (size 0.8 0.8) (layers "F.Cu" "F.Paste" "F.Mask")
(solder_mask_margin 0.0635) (tstamp aece925f-4ef2-4a3b-a64c-72600b766d22))
)

View File

@ -0,0 +1,4 @@
Default True 2.0 3
gnd True 2.0 3
power True 2.0 3
True True False

View File

@ -0,0 +1,42 @@
(footprint "OSO_SWD_2x3" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(attr smd)
(fp_text reference "REF**" (at 0 -2.032 unlocked) (layer "F.SilkS") hide
(effects (font (size 1 1) (thickness 0.15)))
(tstamp cc97765e-03dd-40c8-bc09-9942f8485d23)
)
(fp_text value "OSO_SWD_2x3" (at 0 -4.572 unlocked) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 3723f1be-f632-4091-a098-c9348aec63f6)
)
(fp_text user "C" (at -0.508 -0.762 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp 88931c8d-237f-4e79-b244-24354b5507cc)
)
(fp_text user "+" (at -0.508 1.778 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp b2d594d3-dac9-41e7-b515-19ad22df4cd2)
)
(fp_text user "D" (at 2.032 1.778 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp c41801cb-4840-4a63-9b83-a0b86093de8d)
)
(fp_text user "-" (at 2.032 -0.762 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp d116e34b-7d96-49fa-980e-61f5adbb5cc2)
)
(fp_text user "~{R}" (at -0.508 -3.302 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp e48e1604-d489-45a4-9587-152a042c354a)
)
(fp_text user "${REFERENCE}" (at 0 4.318 unlocked) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 3cf253e6-b3d8-4b70-990a-3a9df456d233)
)
(pad "1" smd circle (at 1.27 2.54) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp c93a3c51-b06c-47bc-b2c8-dcd43fd3bce4))
(pad "2" smd circle (at 1.27 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 657a0b8d-9c0a-4efd-920e-1742e80dbd77))
(pad "3" smd circle (at -1.27 2.54) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 110879b7-d820-400e-83c2-2ba83b15a32d))
(pad "4" smd circle (at -1.27 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 0da5021b-2960-4fb1-abd4-df913ada6e37))
(pad "5" smd circle (at -1.27 -2.54) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 5e6b9662-455f-4d0d-8334-3f95b2cc20e1))
)

View File

@ -0,0 +1,42 @@
(footprint "OSO_SWD_Linear" (version 20211014) (generator pcbnew)
(layer "F.Cu")
(tedit 0)
(attr smd)
(fp_text reference "REF**" (at 0 -2.032 unlocked) (layer "F.SilkS") hide
(effects (font (size 1 1) (thickness 0.15)))
(tstamp cc97765e-03dd-40c8-bc09-9942f8485d23)
)
(fp_text value "OSO_SWD_Linear" (at 0 -1.5 unlocked) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 3723f1be-f632-4091-a098-c9348aec63f6)
)
(fp_text user "SWC" (at 2.54 1.524 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp 88931c8d-237f-4e79-b244-24354b5507cc)
)
(fp_text user "3V3" (at 0 1.524 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp b2d594d3-dac9-41e7-b515-19ad22df4cd2)
)
(fp_text user "SWD" (at -5.08 1.524 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp c41801cb-4840-4a63-9b83-a0b86093de8d)
)
(fp_text user "GND" (at -2.54 1.524 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp d116e34b-7d96-49fa-980e-61f5adbb5cc2)
)
(fp_text user "~{RST}" (at 5.08 1.524 unlocked) (layer "F.SilkS")
(effects (font (size 0.5 0.5) (thickness 0.1)))
(tstamp e48e1604-d489-45a4-9587-152a042c354a)
)
(fp_text user "${REFERENCE}" (at 0 3.048 unlocked) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 3cf253e6-b3d8-4b70-990a-3a9df456d233)
)
(pad "1" smd circle (at -5.08 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp c93a3c51-b06c-47bc-b2c8-dcd43fd3bce4))
(pad "2" smd circle (at -2.54 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 657a0b8d-9c0a-4efd-920e-1742e80dbd77))
(pad "3" smd circle (at 0 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 110879b7-d820-400e-83c2-2ba83b15a32d))
(pad "4" smd circle (at 2.54 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 0da5021b-2960-4fb1-abd4-df913ada6e37))
(pad "5" smd circle (at 5.08 0) (size 1.5 1.5) (layers "F.Cu" "F.Mask") (tstamp 5e6b9662-455f-4d0d-8334-3f95b2cc20e1))
)

View File

@ -0,0 +1,44 @@
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
(symbol "OSO_SWD" (in_bom yes) (on_board yes)
(property "Reference" "J" (id 0) (at 0 -7.62 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "OSO_SWD" (id 1) (at 0 7.62 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "" (id 2) (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "" (id 3) (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "OSO_SWD_0_1"
(rectangle (start -5.08 6.35) (end 5.08 -6.35)
(stroke (width 0) (type default) (color 0 0 0 0))
(fill (type background))
)
)
(symbol "OSO_SWD_1_1"
(pin bidirectional line (at -7.62 5.08 0) (length 2.54)
(name "SWDIO" (effects (font (size 1.27 1.27))))
(number "1" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at -7.62 2.54 0) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "2" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at -7.62 0 0) (length 2.54)
(name "VCC" (effects (font (size 1.27 1.27))))
(number "3" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -7.62 -2.54 0) (length 2.54)
(name "SWCLK" (effects (font (size 1.27 1.27))))
(number "4" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -7.62 -5.08 0) (length 2.54)
(name "~{RESET}" (effects (font (size 1.27 1.27))))
(number "5" (effects (font (size 1.27 1.27))))
)
)
)
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
(kicad_wks (version 20210606) (generator pl_editor)
(setup (textsize 1.5 1.5)(linewidth 0.15)(textlinewidth 0.15)
(left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))
(line (name "segm1:Line") (start 0 0) (end 0 0))
)

View File

@ -0,0 +1,4 @@
(fp_lib_table
(lib (name "OSO-SWAT-A1-05")(type "KiCad")(uri "$(KIPRJMOD)/OSO-SWAT-C1.pretty")(options "")(descr ""))
(lib (name "OSO-SWD")(type "KiCad")(uri "${KIPRJMOD}/OSO-SWD.pretty")(options "")(descr ""))
)

View File

@ -0,0 +1,4 @@
(sym_lib_table
(lib (name "OSO-SWAT-A1-05-eagle-import")(type "KiCad")(uri "${KIPRJMOD}/OSO-SWAT-A1-05-eagle-import.kicad_sym")(options "")(descr ""))
(lib (name "OSO-SWD")(type "KiCad")(uri "${KIPRJMOD}/OSO_SWD.kicad_sym")(options "")(descr ""))
)

View File

@ -74,6 +74,17 @@ python3 -m http.server -d build-sim
Finally, visit [watch.html](http://localhost:8000/watch.html) to see your work.
Hardware Schematics and PCBs
----------------------------
| Name | Color | Schematic | Gerbers |
| ---- | ----- | --------- | ------- |
| Sensorwatch Lite | RED | [PCB/Main Boards/OSO-SWAT-B1](PCB/Main%20Boards/OSO-SWAT-B1) | [OSO-SWAT-B1-03](PCB/Main%20Boards/OSO-SWAT-B1/OSO-SWAT-B1-03.zip) |
| Sensorwatch | GREEN | [OSO-SWAT-A1-05](PCB/Main%20Boards/OSO-SWAT-A1/OSO-SWAT-A1-05.sch) (Eagle format) | ? |
| Sensorwatch Pro | TBD | TBD | TBD |
License
-------
Different components of the project are licensed differently, see [LICENSE.md](https://github.com/joeycastillo/Sensor-Watch/blob/main/LICENSE.md).

View File

@ -0,0 +1,420 @@
#include <stdio.h>
#include <string.h>
#include "watch.h"
#include "spiflash.h"
bool has_ticked = false;
extern struct io_descriptor *uart_io;
// array of lcd pins from pins.h
const uint8_t lcd_pins[] = {
SLCD26, // SEG23
SLCD25, // SEG22
SLCD24, // SEG21
SLCD23, // SEG20
SLCD22, // SEG19
SLCD21, // SEG18
SLCD20, // SEG17
SLCD19, // SEG16
SLCD18, // SEG15
SLCD17, // SEG14
SLCD16, // SEG13
SLCD15, // SEG12
SLCD14, // SEG11
SLCD13, // SEG10
SLCD12, // SEG9
SLCD11, // SEG8
SLCD10, // SEG7
SLCD9, // SEG6
SLCD8, // SEG5
SLCD7, // SEG4
SLCD6, // SEG3
SLCD5, // SEG2
SLCD4, // SEG1
SLCD3, // SEG0
SLCD2, // COM2
SLCD1, // COM1
SLCD0, // COM0
};
void cb_tick(void);
void cb_tick(void) {
has_ticked = true;
watch_rtc_disable_periodic_callback(8);
}
void pass_if(bool passed);
void pass_if(bool passed) {
if (passed) {
watch_set_led_green();
delay_ms(100);
watch_set_led_off();
} else {
watch_set_led_red();
delay_ms(100);
watch_set_led_off();
}
}
void app_init(void) {
}
void app_wake_from_backup(void) {
}
static void enable_irda_uart() {
gpio_set_pin_direction(IR_ENABLE, GPIO_DIRECTION_OUT);
gpio_set_pin_level(IR_ENABLE, false);
SERCOM_USART_CTRLA_Type ctrla;
SERCOM_USART_CTRLB_Type ctrlb;
ctrla.reg = SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE(1);
ctrlb.reg = SERCOM_USART_CTRLB_CHSIZE(0) | SERCOM_USART_CTRLB_ENC;
MCLK->APBCMASK.reg |= MCLK_APBCMASK_SERCOM0;
GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN(0) | GCLK_PCHCTRL_CHEN;
while (0 == (GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE].reg & GCLK_PCHCTRL_CHEN));
usart_sync_init(&USART_0, SERCOM0, (void *)NULL);
SERCOM0->USART.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE;
gpio_set_pin_direction(IRSENSE, GPIO_DIRECTION_IN);
gpio_set_pin_function(IRSENSE, PINMUX_PA04D_SERCOM0_PAD0);
ctrla.reg |= SERCOM_USART_CTRLA_RXPO(0);
ctrlb.reg |= SERCOM_USART_CTRLB_RXEN;
SERCOM0->USART.CTRLA.reg = ctrla.reg;
SERCOM0->USART.CTRLB.reg = ctrlb.reg;
if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) {
uint64_t br = 65536 - ((65536 * 16.0f * 600) / 8000000);
SERCOM0->USART.BAUD.reg = (uint16_t)br;
} else {
uint64_t br = 65536 - ((65536 * 16.0f * 600) / 4000000);
SERCOM0->USART.BAUD.reg = (uint16_t)br;
}
SERCOM0->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
usart_sync_enable(&USART_0);
usart_sync_get_io_descriptor(&USART_0, &uart_io);
}
void app_setup(void) {
// Set up tick for RTC test
watch_rtc_register_periodic_callback(cb_tick, 8);
// Set up UART for communication with tester
enable_irda_uart();
// Set up LED pins
watch_enable_leds();
watch_enable_buzzer();
// Set up buttons with pull-down resistors
gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
gpio_set_pin_direction(BTN_LIGHT, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(BTN_LIGHT, GPIO_PULL_DOWN);
gpio_set_pin_direction(BTN_MODE, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(BTN_MODE, GPIO_PULL_DOWN);
// Set up ADC for thermistor and light sensor tests
watch_enable_adc();
watch_enable_analog_input(TEMPSENSE);
// Pin A0 is the thermistor enable pin
gpio_set_pin_direction(TS_ENABLE, GPIO_DIRECTION_OUT);
}
void app_prepare_for_standby(void) {
}
void app_wake_from_standby(void) {
}
static bool test_i2c(void) {
watch_enable_i2c();
uint16_t device_id = watch_i2c_read8(0x48, 0x0F);
printf("%d\n", device_id);
return device_id == 0x75;
}
static bool test_spi(void) {
gpio_set_pin_level(A3, true);
gpio_set_pin_direction(A3, GPIO_DIRECTION_OUT);
watch_enable_spi();
delay_ms(10);
watch_set_pin_level(A3, false);
delay_ms(10);
uint8_t read_status_response[3] = {0};
bool ok = spi_flash_read_command(0x9F, read_status_response, 3);
watch_set_pin_level(A3, true);
printf("%d %d %d\n", read_status_response[0], read_status_response[1], read_status_response[2]);
return (read_status_response[0] == 0xC8 && read_status_response[1] == 0x40 && read_status_response[2] == 0x13);
}
bool app_loop(void) {
uint8_t buf[5] = {0};
watch_storage_read(10, 0, buf, 4);
printf("%s\n", (const char *)buf);
if (strcmp((const char *)buf, "BEEP") == 0) {
watch_set_led_yellow();
watch_buzzer_play_note(BUZZER_NOTE_C5, 150);
watch_buzzer_play_note(BUZZER_NOTE_REST, 25);
watch_buzzer_play_note(BUZZER_NOTE_E5, 150);
watch_buzzer_play_note(BUZZER_NOTE_REST, 25);
watch_buzzer_play_note(BUZZER_NOTE_G5, 150);
watch_buzzer_play_note(BUZZER_NOTE_REST, 25);
watch_buzzer_play_note(BUZZER_NOTE_C6, 150);
watch_storage_erase(10);
delay_ms(10);
watch_storage_write(10, 0, (const char *)"9PIN", 4);
watch_storage_sync();
watch_storage_read(10, 0, buf, 4);
delay_ms(10);
if(strcmp((const char *)buf, (const char *)"9PIN") == 0) {
watch_set_led_off();
while(1);
}
}
if (strcmp((const char *)buf, "9PIN") == 0) {
bool i2c_passed = test_i2c();
bool spi_passed = test_spi();
if (i2c_passed && spi_passed) {
watch_storage_erase(10);
delay_ms(10);
watch_storage_write(10, 0, (const char *)"PASS", 4);
watch_storage_sync();
watch_storage_read(10, 0, buf, 4);
delay_ms(10);
if(strcmp((const char *)buf, (const char *)"PASS") == 0) {
gpio_set_pin_direction(A0, GPIO_DIRECTION_OUT);
gpio_set_pin_level(A0, true);
}
} else if (i2c_passed) {
// SPI failed, RED indicator
watch_set_led_color_rgb(128, 0, 0);
} else if (spi_passed) {
// I2C failed, BLUE indicator
watch_set_led_color_rgb(0, 0, 128);
} else {
// both failed, PURPLE indicator
watch_set_led_color_rgb(64, 0, 128);
}
while(1);
}
if(strcmp((const char *)buf, (const char *)"PASS") == 0) {
watch_set_led_green();
while(1);
}
char char_received = watch_uart_getc();
if (char_received) {
switch (char_received) {
// - [X] RTC
case 'R':
pass_if(has_ticked);
break;
// - [X] LCD pin continuity
case 'O':
// Set all LCD pins high
for (int i = 0; i < 27; i++) {
gpio_set_pin_function(lcd_pins[i], GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(lcd_pins[i], GPIO_DIRECTION_OUT);
gpio_set_pin_level(lcd_pins[i], true);
}
// It is the tester's responsibility to check that the pins are high
break;
case 'P':
// Set all LCD pins low
for (int i = 0; i < 27; i++) {
gpio_set_pin_function(lcd_pins[i], GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(lcd_pins[i], GPIO_DIRECTION_OUT);
gpio_set_pin_level(lcd_pins[i], false);
}
// It is the tester's responsibility to check that the pins are low
break;
// - [X] LCD pin bridging
case 'Q':
{
bool passed = true;
// Pull all LCD pins up
for (int i = 0; i < 27; i++) {
gpio_set_pin_function(lcd_pins[i], GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(lcd_pins[i], GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(lcd_pins[i], GPIO_PULL_UP);
}
// SEG23 is adjacent to the green LED.
// setting the LED green drives GREEN low.
watch_set_led_green();
if (!gpio_get_pin_level(SLCD26)) {
// If SEG23 is low, then it must be bridged to the green pin
pass_if(false);
}
// SEG13 is adjacent to the blue LED.
// setting the LED blue drives BLUE low.
watch_set_led_color_rgb(0, 0, 255);
if (!gpio_get_pin_level(SLCD16)) {
// If SEG13 is low, then it must be bridged to the blue pin
pass_if(false);
}
// SEG12 is adjacent to the red LED.
// setting the LED red drives RED low.
watch_set_led_red();
if (!gpio_get_pin_level(SLCD15)) {
// If SEG12 is low, then it must be bridged to the red pin
pass_if(false);
}
watch_set_led_off();
// After this, all LCD pins are adjacent. Test if each pin is bridged to the previous one.
for (int i = 1; i < 27; i++) {
gpio_set_pin_direction(lcd_pins[i - 1], GPIO_DIRECTION_OUT);
gpio_set_pin_level(lcd_pins[i - 1], false);
if (!gpio_get_pin_level(lcd_pins[i])) {
passed = false;
break;
}
gpio_set_pin_direction(lcd_pins[i - 1], GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(lcd_pins[i - 1], GPIO_PULL_UP);
}
// Special cases:
// SLCD0 neighbors VCC
gpio_set_pin_direction(SLCD0, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(SLCD0, GPIO_PULL_DOWN);
if (gpio_get_pin_level(SLCD0)) {
passed = false;
}
// SLCD18 neighbors VCC
gpio_set_pin_direction(SLCD18, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(SLCD18, GPIO_PULL_DOWN);
if (gpio_get_pin_level(SLCD18)) {
passed = false;
}
// SLCD26 neighbors USB_N
gpio_set_pin_direction(GPIO(GPIO_PORTA, 24), GPIO_DIRECTION_OUT);
gpio_set_pin_level(GPIO(GPIO_PORTA, 24), true);
gpio_set_pin_direction(SLCD26, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(SLCD26, GPIO_PULL_DOWN);
// if SLCD26 is high, then it is bridged to USB_N
if (gpio_get_pin_level(SLCD26)) {
passed = false;
}
// SLCD11 neighbors VLCD
watch_enable_display();
delay_ms(50);
gpio_set_pin_function(SLCD11, GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(SLCD11, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(SLCD11, GPIO_PULL_DOWN);
if (gpio_get_pin_level(SLCD11)) {
passed = false;
}
for (int i = 0; i < 27; i++) {
gpio_set_pin_function(lcd_pins[i], GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(lcd_pins[i], GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(lcd_pins[i], GPIO_PULL_OFF);
}
pass_if(passed);
}
break;
// - [X] Thermistor high
case 'U':
// Set TS_ENABLE high and read the value of TEMPSENSE via the ADC.
// Pass if the value is near VCC.
gpio_set_pin_level(TS_ENABLE, true);
pass_if(watch_get_analog_pin_level(TEMPSENSE) > 65000);
break;
// - [X] Thermistor low
case 'T':
{
// Set TS_ENABLE low and read the value of TEMPSENSE via the ADC.
// Pass if the value is within the realm of reasonable temperatures.
// 15000 is a few minutes in the freezer, 45000 is holding it a few feet above a stovetop
gpio_set_pin_level(TS_ENABLE, false);
uint16_t value = watch_get_analog_pin_level(TEMPSENSE);
pass_if(value < 45000 && value > 15000);
}
break;
// - [X] VLCD low
case 'V':
watch_enable_display();
SLCD->CTRLA.bit.ENABLE = 0;
while(SLCD->SYNCBUSY.bit.ENABLE);
SLCD->CTRLC.bit.CTST = 0x0;
SLCD->CTRLA.bit.ENABLE = 1;
while(SLCD->SYNCBUSY.bit.ENABLE);
break;
// - [X] VLCD high
case 'W':
watch_enable_display();
SLCD->CTRLA.bit.ENABLE = 0;
while(SLCD->SYNCBUSY.bit.ENABLE);
SLCD->CTRLC.bit.CTST = 0xD;
SLCD->CTRLA.bit.ENABLE = 1;
while(SLCD->SYNCBUSY.bit.ENABLE);
break;
/// TODO: LED
case 'r':
watch_set_led_color_rgb(255, 0, 0);
delay_ms(100);
watch_set_led_color_rgb(0, 0, 0);
// It is the tester's responsibility to check the LED color.
break;
case 'g':
watch_set_led_color_rgb(0, 255, 0);
delay_ms(100);
watch_set_led_color_rgb(0, 0, 0);
// It is the tester's responsibility to check the LED color.
break;
case 'b':
watch_set_led_color_rgb(0, 0, 255);
delay_ms(100);
watch_set_led_color_rgb(0, 0, 0);
// It is the tester's responsibility to check the LED color.
break;
// - [X] Buttons
case 'B':
// Pass if all three buttons are low
pass_if(!gpio_get_pin_level(BTN_ALARM) && !gpio_get_pin_level(BTN_LIGHT) && !gpio_get_pin_level(BTN_MODE));
break;
case 'L':
// pass if BTN_LIGHT is high and the other two are low
pass_if(gpio_get_pin_level(BTN_LIGHT) && !gpio_get_pin_level(BTN_ALARM) && !gpio_get_pin_level(BTN_MODE));
break;
case 'A':
// pass if BTN_ALARM is high and the other two are low
pass_if(gpio_get_pin_level(BTN_ALARM) && !gpio_get_pin_level(BTN_LIGHT) && !gpio_get_pin_level(BTN_MODE));
break;
case 'M':
// pass if BTN_MODE is high and the other two are low
pass_if(gpio_get_pin_level(BTN_MODE) && !gpio_get_pin_level(BTN_ALARM) && !gpio_get_pin_level(BTN_LIGHT));
break;
// - [X] File system
case 'F':
watch_storage_erase(10);
delay_ms(10);
watch_storage_write(10, 0, (const char *)"BEEP", 4);
watch_storage_sync();
watch_storage_read(10, 0, buf, 4);
delay_ms(10);
// No need to do anything here; comparison with 'beep' happens at next loop invocation.
break;
}
}
return false;
}

View File

@ -0,0 +1,10 @@
TOP = ../../..
include $(TOP)/make.mk
INCLUDES += \
-I../
SRCS += \
../app.c
include $(TOP)/rules.mk

View File

@ -12,6 +12,14 @@
#define BTN_MODE GPIO(GPIO_PORTA, 31)
#define WATCH_BTN_MODE_EIC_CHANNEL 11
// Temperature Sensor
#define TS_ENABLE GPIO(GPIO_PORTB, 23)
#define TEMPSENSE GPIO(GPIO_PORTA, 3)
// Light Sensor
#define IR_ENABLE GPIO(GPIO_PORTB, 22)
#define IRSENSE GPIO(GPIO_PORTA, 4)
// Buzzer
#define BUZZER GPIO(GPIO_PORTA, 27)
#define WATCH_BUZZER_TCC_PINMUX PINMUX_PA27F_TCC0_WO5

View File

@ -223,7 +223,7 @@ ifndef COLOR
$(error Set the COLOR variable to RED, BLUE, or GREEN depending on what board you have.)
endif
COLOR_VALID := $(filter $(COLOR),RED BLUE GREEN)
COLOR_VALID := $(filter $(COLOR),RED BLUE GREEN PRO)
ifeq ($(COLOR_VALID),)
$(error COLOR must be RED, BLUE, or GREEN)

View File

@ -0,0 +1,304 @@
// Compute times of moonrise and moonset at a specified latitude and longitude.
//
// This software minimizes computational work by performing the full calculation
// of the lunar position three times, at the beginning, middle, and end of the
// period of interest. Three point interpolation is used to predict the
// position for each hour, and the arithmetic mean is used to predict the
// half-hour positions.
//
// The full computational burden is negligible on modern computers, but the
// algorithm is effective and still useful for small embedded systems.
//
// This software was originally adapted to javascript by Stephen R. Schmitt
// from a BASIC program from the 'Astronomical Computing' column of Sky &
// Telescope, April 1989, page 78.
//
// Subsequently adapted from Stephen R. Schmitt's javascript to c++ for the
// Arduino by Cyrus Rahman.
//
// Subsequently adapted from Cyrus Rahman's Arduino C++ to C for the Sensor
// Watch by hueso, this work is subject to Stephen Schmitt's copyright:
//
// Copyright 2007 Stephen R. Schmitt
// Subsequent work Copyright 2020 Cyrus Rahman
// You may use or modify this source code in any way you find useful, provided
// that you agree that the author(s) have no warranty, obligations or liability.
// You must determine the suitability of this source code for your use.
//
// Redistributions of this source code must retain this copyright notice.
#include "moonrise.h"
#include <math.h>
#define K1 15 * (M_PI / 180) * 1.0027379
// Determine the nearest moon rise or set event previous, and the nearest
// moon rise or set event subsequent, to the specified time in seconds since the
// Unix epoch (January 1, 1970) and at the specified latitude and longitude in
// degrees.
//
// We look for events from MR_WINDOW/2 hours in the past to MR_WINDOW/2 hours
// in the future.
MoonRise MoonRise_calculate(double latitude, double longitude, uint32_t t) {
MoonRise self = {};
skyCoordinates moonPosition[3];
double offsetDays;
self.queryTime = t;
offsetDays = julianDate(t) - 2451545L; // Days since Jan 1, 2000, 1200UTC.
// Begin testing (MR_WINDOW / 2) hours before requested time.
// offsetDays -= (double)MR_WINDOW / (2 * 24) ;
// Calculate coordinates at start, middle, and end of search period.
for (int i = 0; i < 3; i++) {
moonPosition[i] = moon(offsetDays + i * (double)MR_WINDOW / (2 * 24));
}
// If the RA wraps around during this period, unwrap it to keep the
// sequence smooth for interpolation.
if (moonPosition[1].RA <= moonPosition[0].RA)
moonPosition[1].RA += 2 * M_PI;
if (moonPosition[2].RA <= moonPosition[1].RA)
moonPosition[2].RA += 2 * M_PI;
// Initialize interpolation array.
skyCoordinates mpWindow[3];
mpWindow[0].RA = moonPosition[0].RA;
mpWindow[0].declination = moonPosition[0].declination;
mpWindow[0].distance = moonPosition[0].distance;
for (int k = 0; k < MR_WINDOW; k++) { // Check each interval of search period
float ph = (float)(k + 1) / MR_WINDOW;
mpWindow[2].RA = interpolate(moonPosition[0].RA, moonPosition[1].RA,
moonPosition[2].RA, ph);
mpWindow[2].declination =
interpolate(moonPosition[0].declination, moonPosition[1].declination,
moonPosition[2].declination, ph);
mpWindow[2].distance = moonPosition[2].distance;
// Look for moonrise/set events during this interval.
{
double ha[3], VHz[3];
double lSideTime;
// Get (local_sidereal_time - MR_WINDOW / 2) hours in radians.
lSideTime = localSiderealTime(offsetDays, longitude) * 2 * M_PI / 360;
// Calculate Hour Angle.
ha[0] = lSideTime - mpWindow[0].RA + k * K1;
ha[2] = lSideTime - mpWindow[2].RA + k * K1 + K1;
// Hour Angle and declination at half hour.
ha[1] = (ha[2] + ha[0]) / 2;
mpWindow[1].declination =
(mpWindow[2].declination + mpWindow[0].declination) / 2;
double s = sin(M_PI / 180 * latitude);
double c = cos(M_PI / 180 * latitude);
// refraction + semidiameter at horizon + distance correction
double z = cos(M_PI / 180 * (90.567 - 41.685 / mpWindow[0].distance));
VHz[0] = s * sin(mpWindow[0].declination) +
c * cos(mpWindow[0].declination) * cos(ha[0]) - z;
VHz[2] = s * sin(mpWindow[2].declination) +
c * cos(mpWindow[2].declination) * cos(ha[2]) - z;
if (signbit(VHz[0]) == signbit(VHz[2]))
goto noevent; // No event this hour.
VHz[1] = s * sin(mpWindow[1].declination) +
c * cos(mpWindow[1].declination) * cos(ha[1]) - z;
double a, b, d, e, time;
a = 2 * VHz[2] - 4 * VHz[1] + 2 * VHz[0];
b = 4 * VHz[1] - 3 * VHz[0] - VHz[2];
d = b * b - 4 * a * VHz[0];
if (d < 0)
goto noevent; // No event this hour.
d = sqrt(d);
e = (-b + d) / (2 * a);
if ((e < 0) || (e > 1))
e = (-b - d) / (2 * a);
time = k + e + 1 / 120; // Time since k=0 of event (in hours).
// The time we started searching + the time from the start of the search
// to the event is the time of the event.
uint32_t eventTime;
eventTime = self.queryTime + (time) * 60 * 60;
double hz, nz, dz, az;
hz = ha[0] + e * (ha[2] - ha[0]); // Azimuth of the moon at the event.
nz = -cos(mpWindow[1].declination) * sin(hz);
dz = c * sin(mpWindow[1].declination) -
s * cos(mpWindow[1].declination) * cos(hz);
az = atan2(nz, dz) / (M_PI / 180);
if (az < 0)
az += 360;
// If there is no previously recorded event of this type, save this event.
//
// If this event is previous to queryTime, and is the nearest event to
// queryTime of events of its type previous to queryType, save this event,
// replacing the previously recorded event of its type. Events subsequent
// to queryTime are treated similarly, although since events are tested in
// chronological order no replacements will occur as successive events
// will be further from queryTime.
//
// If this event is subsequent to queryTime and there is an event of its
// type previous to queryTime, then there is an event of the other type
// between the two events of this event's type. If the event of the other
// type is previous to queryTime, then it is the nearest event to
// queryTime that is previous to queryTime. In this case save the current
// event, replacing the previously recorded event of its type. Otherwise
// discard the current event.
//
if ((VHz[0] < 0) && (VHz[2] > 0)) {
if (!self.hasRise ||
((self.riseTime < self.queryTime) == (eventTime < self.queryTime) &&
(self.riseTime - self.queryTime) > (eventTime - self.queryTime)) ||
((self.riseTime < self.queryTime) != (eventTime < self.queryTime) &&
(self.hasSet && (self.riseTime < self.queryTime) ==
(self.setTime < self.queryTime)))) {
self.riseTime = eventTime;
self.riseAz = az;
self.hasRise = true;
}
}
if ((VHz[0] > 0) && (VHz[2] < 0)) {
if (!self.hasSet ||
((self.setTime < self.queryTime) == (eventTime < self.queryTime) &&
(self.setTime - self.queryTime) > (eventTime - self.queryTime)) ||
((self.setTime < self.queryTime) != (eventTime < self.queryTime) &&
(self.hasRise && (self.setTime < self.queryTime) ==
(self.riseTime < self.queryTime)))) {
self.setTime = eventTime;
self.setAz = az;
self.hasSet = true;
}
}
noevent:
// There are obscure cases in the polar regions that require extra logic.
if (!self.hasRise && !self.hasSet)
self.isVisible = !signbit(VHz[2]);
else if (self.hasRise && !self.hasSet)
self.isVisible = (self.queryTime > self.riseTime);
else if (!self.hasRise && self.hasSet)
self.isVisible = (self.queryTime < self.setTime);
else
self.isVisible =
((self.riseTime < self.setTime && self.riseTime < self.queryTime &&
self.setTime > self.queryTime) ||
(self.riseTime > self.setTime && (self.riseTime < self.queryTime ||
self.setTime > self.queryTime)));
}
if (self.hasSet && self.hasRise)
break;
mpWindow[0] = mpWindow[2]; // Advance to next interval.
}
return self;
}
// Moon position using fundamental arguments
// (Van Flandern & Pulkkinen, 1979)
skyCoordinates moon(double dayOffset) {
double l = 0.606434 + 0.03660110129 * dayOffset;
double m = 0.374897 + 0.03629164709 * dayOffset;
double f = 0.259091 + 0.03674819520 * dayOffset;
double d = 0.827362 + 0.03386319198 * dayOffset;
double n = 0.347343 - 0.00014709391 * dayOffset;
double g = 0.993126 + 0.00273777850 * dayOffset;
l = 2 * M_PI * (l - floor(l));
m = 2 * M_PI * (m - floor(m));
f = 2 * M_PI * (f - floor(f));
d = 2 * M_PI * (d - floor(d));
n = 2 * M_PI * (n - floor(n));
g = 2 * M_PI * (g - floor(g));
double v, u, w;
v = 0.39558 * sin(f + n)
+ 0.08200 * sin(f)
+ 0.03257 * sin(m - f - n)
+ 0.01092 * sin(m + f + n)
+ 0.00666 * sin(m - f)
- 0.00644 * sin(m + f - 2*d + n)
- 0.00331 * sin(f - 2*d + n)
- 0.00304 * sin(f - 2*d)
- 0.00240 * sin(m - f - 2*d - n)
+ 0.00226 * sin(m + f)
- 0.00108 * sin(m + f - 2*d)
- 0.00079 * sin(f - n)
+ 0.00078 * sin(f + 2*d + n);
u = 1
- 0.10828 * cos(m)
- 0.01880 * cos(m - 2*d)
- 0.01479 * cos(2*d)
+ 0.00181 * cos(2*m - 2*d)
- 0.00147 * cos(2*m)
- 0.00105 * cos(2*d - g)
- 0.00075 * cos(m - 2*d + g);
w = 0.10478 * sin(m)
- 0.04105 * sin(2*f + 2*n)
- 0.02130 * sin(m - 2*d)
- 0.01779 * sin(2*f + n)
+ 0.01774 * sin(n)
+ 0.00987 * sin(2*d)
- 0.00338 * sin(m - 2*f - 2*n)
- 0.00309 * sin(g)
- 0.00190 * sin(2*f)
- 0.00144 * sin(m + n)
- 0.00144 * sin(m - 2*f - n)
- 0.00113 * sin(m + 2*f + 2*n)
- 0.00094 * sin(m - 2*d + g)
- 0.00092 * sin(2*m - 2*d);
double s;
skyCoordinates sc;
s = w / sqrt(u - v*v);
sc.RA = l + atan(s / sqrt(1 - s*s)); // Right ascension
s = v / sqrt(u);
sc.declination = atan(s / sqrt(1 - s*s)); // Declination
sc.distance = 60.40974 * sqrt(u); // Distance
return(sc);
}
// 3-point interpolation
double interpolate(double f0, double f1, double f2, double p) {
double a = f1 - f0;
double b = f2 - f1 - a;
return(f0 + p * (2*a + b * (2*p - 1)));
}
// Determine Julian date from Unix time.
// Provides marginally accurate results with Arduino 4-byte double.
double julianDate(uint32_t t) {
return (t / 86400.0L + 2440587.5);
}
// Local Sidereal Time
// Provides local sidereal time in degrees, requires longitude in degrees
// and time in fractional Julian days since Jan 1, 2000, 1200UTC (e.g. the
// Julian date - 2451545).
// cf. USNO Astronomical Almanac and
// https://astronomy.stackexchange.com/questions/24859/local-sidereal-time
double localSiderealTime(double offsetDays, double longitude) {
double lSideTime = (15.0L * (6.697374558L + 0.06570982441908L * offsetDays +
remainder(offsetDays, 1) * 24 + 12 +
0.000026 * (offsetDays / 36525) * (offsetDays / 36525))
+ longitude) / 360;
lSideTime -= floor(lSideTime);
lSideTime *= 360; // Convert to degrees.
return(lSideTime);
}

View File

@ -0,0 +1,43 @@
#ifndef MoonRise_h
#define MoonRise_h
#include <stdint.h>
#include <stdbool.h>
// Size of event search window in hours.
// Events further away from the search time than MR_WINDOW/2 will not be
// found. At higher latitudes the moon rise/set intervals become larger, so if
// you want to find the nearest events this will need to increase. Larger
// windows will increase interpolation error. Useful values are probably from
// 12 - 48 but will depend upon your application.
#define MR_WINDOW 48 // Even integer
typedef struct {
double RA; // Right ascension
double declination; // Declination
double distance; // Distance
} skyCoordinates;
typedef struct {
uint32_t queryTime;
uint32_t riseTime;
uint32_t setTime;
float riseAz;
float setAz;
bool hasRise;
bool hasSet;
bool isVisible;
} MoonRise;
MoonRise MoonRise_calculate(double latitude, double longitude, uint32_t t);
// private:
void testMoonRiseSet(MoonRise *self, int i, double offsetDays, double latitude,
double longitude, skyCoordinates *mp);
skyCoordinates moon(double dayOffset);
double interpolate(double f0, double f1, double f2, double p);
double julianDate(uint32_t t);
double localSiderealTime(double offsetDays, double longitude);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,8 @@ INCLUDES += \
-I../lib/vsop87/ \
-I../lib/astrolib/ \
-I../lib/morsecalc/ \
-I../lib/smallchesslib/ \
-I../lib/moonrise/ \
# If you add any other source files you wish to compile, add them after ../app.c
# Note that you will need to add a backslash at the end of any line you wish to continue, i.e.
@ -39,6 +41,7 @@ SRCS += \
../lib/TOTP/TOTP.c \
../lib/base32/base32.c \
../lib/sunriset/sunriset.c \
../lib/moonrise/moonrise.c \
../lib/vsop87/vsop87a_milli.c \
../lib/astrolib/astrolib.c \
../lib/morsecalc/calc.c \
@ -119,6 +122,7 @@ SRCS += \
../watch_faces/complication/toss_up_face.c \
../watch_faces/complication/geomancy_face.c \
../watch_faces/clock/simple_clock_bin_led_face.c \
../watch_faces/complication/menstrual_cycle_face.c \
../watch_faces/complication/flashlight_face.c \
../watch_faces/clock/decimal_time_face.c \
../watch_faces/clock/wyoscan_face.c \
@ -129,7 +133,10 @@ SRCS += \
../watch_faces/complication/couch_to_5k_face.c \
../watch_faces/clock/minute_repeater_decimal_face.c \
../watch_faces/complication/tuning_tones_face.c \
../watch_faces/sensor/minmax_face.c \
../watch_faces/complication/kitchen_conversions_face.c \
../watch_faces/complication/butterfly_game_face.c \
../watch_faces/complication/wareki_face.c \
../watch_faces/complication/wordle_face.c \
../watch_faces/complication/endless_runner_face.c \
../watch_faces/complication/periodic_face.c \
@ -141,6 +148,10 @@ SRCS += \
../watch_faces/complication/simple_calculator_face.c \
../watch_faces/sensor/alarm_thermometer_face.c \
../watch_faces/demo/beeps_face.c \
../watch_faces/sensor/accel_interrupt_count_face.c \
../watch_faces/complication/metronome_face.c \
../watch_faces/complication/smallchess_face.c \
../watch_faces/complication/moonrise_face.c \
# New watch faces go above this line.
# Leave this line at the bottom of the file; it has all the targets for making your project.

View File

@ -94,6 +94,7 @@
#include "geomancy_face.h"
#include "dual_timer_face.h"
#include "simple_clock_bin_led_face.h"
#include "menstrual_cycle_face.h"
#include "flashlight_face.h"
#include "decimal_time_face.h"
#include "wyoscan_face.h"
@ -104,7 +105,10 @@
#include "couch_to_5k_face.h"
#include "minute_repeater_decimal_face.h"
#include "tuning_tones_face.h"
#include "minmax_face.h"
#include "kitchen_conversions_face.h"
#include "butterfly_game_face.h"
#include "wareki_face.h"
#include "wordle_face.h"
#include "endless_runner_face.h"
#include "periodic_face.h"
@ -116,6 +120,10 @@
#include "simple_calculator_face.h"
#include "alarm_thermometer_face.h"
#include "beeps_face.h"
#include "accel_interrupt_count_face.h"
#include "metronome_face.h"
#include "smallchess_face.h"
#include "moonrise_face.h"
// New includes go above this line.
#endif // MOVEMENT_FACES_H_

View File

@ -0,0 +1,467 @@
/*
* MIT License
*
* Copyright (c) 2023 Hugo Chargois
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Emulator only: need time() to seed the random number generator
#if __EMSCRIPTEN__
#include <time.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "butterfly_game_face.h"
static char butterfly_shapes[][3] = {
"[]", "][", "25", "52", "9e", "e9", "6a", "a6", "3E", "E3", "00", "HH", "88"
};
static int8_t single_beep[] = {BUZZER_NOTE_A7, 4, 0};
static int8_t round_win_melody[] = {
BUZZER_NOTE_C6, 4,
BUZZER_NOTE_E6, 4,
BUZZER_NOTE_G6, 4,
BUZZER_NOTE_C7, 12,
0};
static int8_t round_lose_melody[] = {
BUZZER_NOTE_E6, 4,
BUZZER_NOTE_F6, 4,
BUZZER_NOTE_D6SHARP_E6FLAT, 4,
BUZZER_NOTE_C6, 12,
0};
static int8_t game_win_melody[] = {
BUZZER_NOTE_G6, 4,
BUZZER_NOTE_A6, 4,
BUZZER_NOTE_B6, 4,
BUZZER_NOTE_C7, 12,
BUZZER_NOTE_D7, 4,
BUZZER_NOTE_E7, 4,
BUZZER_NOTE_D7, 4,
BUZZER_NOTE_C7, 12,
BUZZER_NOTE_B6, 4,
BUZZER_NOTE_C7, 4,
BUZZER_NOTE_D7, 4,
BUZZER_NOTE_G7, 24,
0};
#define NUM_SHAPES (sizeof(butterfly_shapes) / sizeof(butterfly_shapes[0]))
#define POS_LEFT 4
#define POS_CENTER 6
#define POS_RIGHT 8
#define TICK_FREQ 8
#define TICKS_PER_SHAPE 8
#define PLAYER_1 0
#define PLAYER_2 1
// returns a random integer r with 0 <= r < max
static inline uint8_t _get_rand(uint8_t max) {
#if __EMSCRIPTEN__
return rand() % max;
#else
return arc4random_uniform(max);
#endif
}
/*
* The game is built with a simple state machine where each state is called a
* "screen". Each screen can draw on the display and handles events, including
* the "activate" event, which is repurposed and sent whenever we move from one
* screen to another via the _transition_to function. Basically it's a mini
* movement inside movement.
*/
typedef bool (*screen_fn_t)(movement_event_t, butterfly_game_state_t*);
static screen_fn_t cur_screen_fn;
static bool _transition_to(screen_fn_t sf, butterfly_game_state_t *state) {
movement_event_t ev = {EVENT_ACTIVATE, 0};
cur_screen_fn = sf;
return sf(ev, state);
}
static uint8_t _pick_wrong_shape(butterfly_game_state_t *state, bool skip_wrong_shape) {
if (!skip_wrong_shape) {
// easy case, we only need to skip over 1 shape: the correct shape
uint8_t r = _get_rand(NUM_SHAPES-1);
if (r >= state->correct_shape) {
r++;
}
return r;
} else {
// a bit more complex, we need to skip over 2 shapes: the correct one
// and the current wrong one
uint8_t r = _get_rand(NUM_SHAPES-2);
uint8_t i1, i2; // the 2 indices to skip over, with i1 < i2
if (state->correct_shape < state->current_shape) {
i1 = state->correct_shape;
i2 = state->current_shape;
} else {
i1 = state->current_shape;
i2 = state->correct_shape;
}
if (r >= i1) {
r++;
}
if (r >= i2) {
r++;
}
return r;
}
}
static void _display_shape(uint8_t shape, uint8_t pos) {
watch_display_string(butterfly_shapes[shape], pos);
}
static void _display_scores(butterfly_game_state_t *state) {
char buf[] = " ";
buf[0] = '0' + state->score_p1;
watch_display_string(buf, 0);
buf[0] = '0' + state->score_p2;
watch_display_string(buf, 3);
}
static void _play_sound(butterfly_game_state_t *state, int8_t *seq) {
if (state->sound) watch_buzzer_play_sequence(seq, NULL);
}
static bool _round_start_screen(movement_event_t event, butterfly_game_state_t *state);
static bool _reset_screen(movement_event_t event, butterfly_game_state_t *state);
static bool _game_win_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
state->ctr = 4 * TICK_FREQ;
watch_clear_display();
if (state->score_p1 >= state->goal_score) {
watch_display_string("pl1 wins", 0);
} else {
watch_display_string("pl2 wins", 0);
}
_play_sound(state, game_win_melody);
break;
case EVENT_TICK:
state->ctr--;
if (state->ctr == 0) {
return _transition_to(_reset_screen, state);
}
break;
}
return true;
}
static bool _round_win_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
state->ctr = TICK_FREQ;
if (state->round_winner == PLAYER_1) {
state->score_p1++;
} else {
state->score_p2++;
}
watch_clear_display();
_display_scores(state);
_display_shape(state->correct_shape, state->round_winner == PLAYER_1 ? POS_LEFT : POS_RIGHT);
_play_sound(state, round_win_melody);
break;
case EVENT_TICK:
state->ctr--;
if (state->ctr == 0) {
if (state->score_p1 >= state->goal_score || state->score_p2 >= state->goal_score) {
return _transition_to(_game_win_screen, state);
}
return _transition_to(_round_start_screen, state);
}
break;
}
return true;
}
static bool _round_lose_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
state->ctr = TICK_FREQ;
if (state->round_winner == PLAYER_1) {
if (state->score_p2 > 0) state->score_p2--;
} else {
if (state->score_p1 > 0) state->score_p1--;
}
_display_shape(state->correct_shape, POS_CENTER);
_play_sound(state, round_lose_melody);
break;
case EVENT_TICK:
if (--state->ctr == 0) {
return _transition_to(_round_start_screen, state);
}
_display_shape(state->ctr%2 ? state->correct_shape : state->current_shape, POS_CENTER);
break;
}
return true;
}
static bool _correct_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
_display_shape(state->correct_shape, POS_CENTER);
_play_sound(state, single_beep);
break;
case EVENT_LIGHT_BUTTON_DOWN:
state->round_winner = PLAYER_1;
return _transition_to(_round_win_screen, state);
case EVENT_ALARM_BUTTON_DOWN:
state->round_winner = PLAYER_2;
return _transition_to(_round_win_screen, state);
}
return true;
}
static bool _wrong_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
state->ctr = TICKS_PER_SHAPE;
state->current_shape = _pick_wrong_shape(state, true);
_display_shape(state->current_shape, POS_CENTER);
_play_sound(state, single_beep);
break;
case EVENT_TICK:
if (--state->ctr == 0) {
if (--state->show_correct_shape_after == 0) {
return _transition_to(_correct_shape_screen, state);
}
return _transition_to(_wrong_shape_screen, state);
}
break;
case EVENT_LIGHT_BUTTON_DOWN:
state->round_winner = PLAYER_2;
return _transition_to(_round_lose_screen, state);
case EVENT_ALARM_BUTTON_DOWN:
state->round_winner = PLAYER_1;
return _transition_to(_round_lose_screen, state);
}
return true;
}
static bool _first_wrong_shape_screen(movement_event_t event, butterfly_game_state_t *state) {
// the first of the wrong shape screens is a bit different than the next
// ones, for 2 reasons:
// * we can pick any shape except one (the correct shape); whereas in the
// subsequent wrong shape screens, we also must not pick the same wrong
// shape as the last
// * we don't act on the light/alarm button events; they would normally be
// a fail in a wrong shape screen, but in this case it may just be that
// the 2 players acknowledge the picked shape (in the previous screen) in
// quick succession, and we don't want the second player to immediately
// fail.
switch (event.event_type) {
case EVENT_ACTIVATE:
state->ctr = TICKS_PER_SHAPE;
state->current_shape = _pick_wrong_shape(state, false);
_display_shape(state->current_shape, POS_CENTER);
_play_sound(state, single_beep);
break;
case EVENT_TICK:
if (--state->ctr == 0) {
return _transition_to(_wrong_shape_screen, state);
}
break;
}
return true;
}
static bool _round_start_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
state->correct_shape = _get_rand(NUM_SHAPES);
state->show_correct_shape_after = _get_rand(10) + 1;
watch_display_string(" - -", 0);
_display_scores(state);
_display_shape(state->correct_shape, POS_CENTER);
break;
case EVENT_LIGHT_BUTTON_DOWN:
case EVENT_ALARM_BUTTON_DOWN:
watch_display_string(" ", 4);
return _transition_to(_first_wrong_shape_screen, state);
}
return true;
}
static bool _goal_select_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
watch_clear_display();
state->goal_score = 6;
break;
case EVENT_LIGHT_BUTTON_DOWN:
return _transition_to(_round_start_screen, state);
case EVENT_ALARM_BUTTON_DOWN:
state->goal_score += 3;
if (state->goal_score > 9) state->goal_score = 3;
break;
}
char buf[] = "GOaL ";
buf[5] = '0' + state->goal_score;
watch_display_string(buf, 4);
return true;
}
static bool _reset_screen(movement_event_t event, butterfly_game_state_t *state) {
(void) event;
state->score_p1 = 0;
state->score_p2 = 0;
return _transition_to(_goal_select_screen, state);
}
static bool _continue_select_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
watch_clear_display();
// no game in progress, start a new game
if (state->score_p1 == 0 && state->score_p2 == 0) {
return _transition_to(_goal_select_screen, state);
}
state->cont = false;
break;
case EVENT_LIGHT_BUTTON_DOWN:
if (state->cont) {
return _transition_to(_round_start_screen, state);
}
return _transition_to(_reset_screen, state);
case EVENT_ALARM_BUTTON_DOWN:
state->cont = !state->cont;
break;
}
if (state->cont) {
watch_display_string("Cont y", 4);
} else {
watch_display_string("Cont n", 4);
}
return true;
}
static bool _sound_select_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
watch_clear_display();
break;
case EVENT_LIGHT_BUTTON_DOWN:
return _transition_to(_continue_select_screen, state);
case EVENT_ALARM_BUTTON_DOWN:
state->sound = !state->sound;
break;
}
if (state->sound) {
watch_display_string("snd y", 5);
} else {
watch_display_string("snd n", 5);
}
return true;
}
static bool _splash_screen(movement_event_t event, butterfly_game_state_t *state) {
switch (event.event_type) {
case EVENT_ACTIVATE:
state->ctr = TICK_FREQ;
watch_clear_display();
watch_display_string("Btrfly", 4);
break;
case EVENT_LIGHT_BUTTON_DOWN:
case EVENT_ALARM_BUTTON_DOWN:
return _transition_to(_sound_select_screen, state);
case EVENT_TICK:
if (--state->ctr == 0) {
return _transition_to(_sound_select_screen, state);
}
break;
}
return true;
}
void butterfly_game_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(butterfly_game_state_t));
memset(*context_ptr, 0, sizeof(butterfly_game_state_t));
// Do any one-time tasks in here; the inside of this conditional happens only at boot.
}
// Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
#if __EMSCRIPTEN__
// simulator only: seed the random number generator
time_t t;
srand((unsigned) time(&t));
#endif
}
void butterfly_game_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
movement_request_tick_frequency(TICK_FREQ);
}
bool butterfly_game_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
butterfly_game_state_t *state = (butterfly_game_state_t *)context;
switch (event.event_type) {
case EVENT_ACTIVATE:
return _transition_to(_splash_screen, state);
case EVENT_TICK:
case EVENT_LIGHT_BUTTON_DOWN:
case EVENT_ALARM_BUTTON_DOWN:
return (*cur_screen_fn)(event, state);
case EVENT_TIMEOUT:
movement_move_to_face(0);
return true;
default:
return movement_default_loop_handler(event, settings);
}
}
void butterfly_game_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
// handle any cleanup before your watch face goes off-screen.
}

View File

@ -0,0 +1,125 @@
/*
* MIT License
*
* Copyright (c) 2023 Hugo Chargois
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BUTTERFLY_GAME_FACE_H_
#define BUTTERFLY_GAME_FACE_H_
#include "movement.h"
/*
* BUTTERFLY
*
* A GAME OF SHAPE RECOGNITION AND QUICK REFLEXES FOR 2 PLAYERS
*
* Setup
* =====
*
* The game is played by 2 players, each using a distinct button:
* - player 1 plays with the LIGHT (upper left) button
* - player 2 plays with the ALARM (lower right) button
*
* To play, both players need a firm grip on the watch. A suggested method is to
* face each other, remove the watch from the wrist, and position it sideways
* between you. Hold one side of the strap in your preferred hand (right or
* left) and use your thumb to play.
*
* Start of the game
* =================
*
* After the splash screen (BtrFly) is shown, the game proceeds through a couple
* configuration screens. Use ALARM to cycle through the possible values, and
* LIGHT to validate and move to the next screen.
*
* The configuration options are:
*
* - snd y/n Toggle sound effects on or off
* - goal 3/6/9 Choose to play a game of 3, 6 or 9 points
* - cont y/n Decide to continue an unfinished game or start a new one
* (this option appears only if a game is in progress)
*
* Rules
* =====
*
* Prior to each round, a symmetrical shape composed of 2 characters is shown in
* the center of the screen. This shape, representing a butterfly's wings, is
* randomly chosen from a set of a dozen or so possible shapes. For example:
*
* ][
*
* Memorize this shape! Your objective in the round will be to "catch" this
* "butterfly" by pressing your button before your opponent does.
*
* Once you believe you've memorized the shape, press your button. The round
* officially begins as soon as either player presses their button.
*
* Various "butterflies" will then appear on the screen, one after the other.
* The fastest player to press their button when the correct butterfly is shown
* wins the round. However, if a player presses their button when an incorrect
* butterfly is shown, they immediately lose the round.
*
* Scoring
* =======
*
* The scores are displayed at the top of the screen at all times.
*
* When a round is won by a player, their score increases by one. When a round
* is lost by a player, their score decreases by one; unless they have a score
* of 0, in which case it remains unchanged.
*
* The game ends when a player reaches the set point goal (3, 6 or 9 points).
*
*/
typedef struct {
bool cont : 1; // continue
bool sound : 1;
uint8_t goal_score : 4;
// a generic ctr used by multiple states to display themselves for multiple frames
uint8_t ctr : 6;
uint8_t correct_shape : 5;
uint8_t current_shape : 5;
uint8_t show_correct_shape_after : 5;
uint8_t round_winner : 1;
uint8_t score_p1 : 5;
uint8_t score_p2 : 5;
} butterfly_game_state_t;
void butterfly_game_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void butterfly_game_face_activate(movement_settings_t *settings, void *context);
bool butterfly_game_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void butterfly_game_face_resign(movement_settings_t *settings, void *context);
#define butterfly_game_face ((const watch_face_t){ \
butterfly_game_face_setup, \
butterfly_game_face_activate, \
butterfly_game_face_loop, \
butterfly_game_face_resign, \
NULL, \
})
#endif // BUTTERFLY_GAME_FACE_H_

View File

@ -0,0 +1,472 @@
/*
* MIT License
*
* Copyright (c) 2023 Joseph Borne Komosa | @jokomo24
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
* Menstrual Cycle Face
*
* Background:
*
* I discovered the Casio F-91W through my partner, appreciated the retro aesthetic of the watch,
* and got one for myself. Soon afterward I discovered the Sensor Watch project and ordered two boards!
* I introduced the Sensor Watch to my partner who inquired whether she could track her menstrual cycle.
* So I decided to implement a menstrual cycle watch face that also calculates the peak fertility window
* using The Calendar Method. While this information may be useful when attempting to achieve or avoid
* pregnancy, it is important to understand that these are rough estimates at best.
*
* How to use:
*
* 1. To begin tracking, go to 'Last Period' page and toggle the alarm button to the number of days since
* the last, most recent, period and hold the alarm button to enter. This will perform the following actions:
* - Store the corresponding date as the 'first' period in order to calculate the total_days_tracked.
* - Turn on the Signal Indicator to signify that tracking has been activated.
* - Deactivate this page and instead show the ticking animation.
* - Adjust the days left in the 'Period in <num> Days' page accordingly.
* - Activate the 'Period Is Here' page and no longer display 'NA'. To prevent accidental user entry,
* the page will display the ticking animation until ten days have passed since the date of the last
* period entered.
* - Activate the 'Peak Fertility' page to begin showing the estimated window,
* as well as display the Alarm Indicator, on this page and on the main 'Period in <num> Days' page,
* whenever the current date falls within the Peak Fertility Window.
*
* 2. Toggle and enter 'y' in the 'Period Is Here' page on the day of every sequential period afterward.
* DO NOT FORGET TO DO SO!
* - If forgotten, the data will become inaccurate and tracking will need to be reset! -> (FIXME, allow one to enter a 'missed' period using the 'Last Period' page).
* This will perform the following actions:
* - Calculate this completed cycle's length and reevaluate the shortest and longest cycle variables.
* - Increment total_cycles by one.
* - Recalculate and save the average cycle for 'Average Cycle' page.
*/
#include <stdlib.h>
#include <string.h>
#include "menstrual_cycle_face.h"
#include "watch.h"
#include "watch_utility.h"
#define TYPICAL_AVG_CYC 28
#define SECONDS_PER_DAY 86400
#define MENSTRUAL_CYCLE_FACE_NUM_PAGES (6)
enum {
period_in_num_days,
average_cycle,
peak_fertility_window,
period_is_here,
first_period,
reset,
} page_titles_e;
const char menstrual_cycle_face_titles[MENSTRUAL_CYCLE_FACE_NUM_PAGES][11] = {
"Prin day", // Period In <num> Days: Estimated days till the next period occurs
"Av cycle ", // Average Cycle: The average number of days estimated per cycle
"Peak Fert ", // Peak Fertility Window: The first and last day of month (displayed top & bottom right, respectively, once tracking) for the estimated window of fertility
"Prishere ", // Period Is Here: Toggle and enter 'y' on the day the actual period occurs to improve Avg and Fert estimations
"Last Per ", // Last Period: Enter the number of days since the last period to begin tracking from that corresponding date by storing it as the 'first'
" Reset ", // Reset: Toggle and enter 'y' to reset tracking data
};
/* Beep function */
static inline void beep(movement_settings_t *settings) {
if (settings->bit.button_should_sound)
watch_buzzer_play_note(BUZZER_NOTE_E8, 75);
}
// Calculate the total number of days for which menstrual cycle tracking has been active
static inline uint32_t total_days_tracked(menstrual_cycle_state_t *state) {
// If tracking has not yet been activated, return 0
if (!(state->dates.reg))
return 0;
// Otherwise, set the start date to the first day of the first tracked cycle
watch_date_time date_time_start;
date_time_start.unit.second = 0;
date_time_start.unit.minute = 0;
date_time_start.unit.hour = 0;
date_time_start.unit.day = state->dates.bit.first_day;
date_time_start.unit.month = state->dates.bit.first_month;
date_time_start.unit.year = state->dates.bit.first_year;
// Get the current date and time
watch_date_time date_time_now = watch_rtc_get_date_time();
// Convert the start date and current date to Unix time
uint32_t unix_start = watch_utility_date_time_to_unix_time(date_time_start, state->utc_offset);
uint32_t unix_now = watch_utility_date_time_to_unix_time(date_time_now, state->utc_offset);
// Calculate the total number of days and return it
return (unix_now - unix_start) / SECONDS_PER_DAY;
}
// Calculate the number of days until the next menstrual period
static inline int8_t days_till_period(menstrual_cycle_state_t *state) {
// Calculate the number of days left until the next period based on the average cycle length and the number of cycles tracked
int8_t days_left = (state->cycles.bit.average_cycle * (state->cycles.bit.total_cycles + 1)) - total_days_tracked(state);
// If the result is negative, return 0 (i.e., the period is expected to start today or has already started)
return (days_left < 0) ? 0 : days_left;
}
static inline void reset_tracking(menstrual_cycle_state_t *state) {
state->dates.bit.first_day = 0;
state->dates.bit.first_month = 0;
state->dates.bit.first_year = 0;
state->dates.bit.prev_day = 0;
state->dates.bit.prev_month = 0;
state->dates.bit.prev_year = 0;
state->cycles.bit.shortest_cycle = TYPICAL_AVG_CYC;
state->cycles.bit.longest_cycle = TYPICAL_AVG_CYC;
state->cycles.bit.average_cycle = TYPICAL_AVG_CYC;
state->cycles.bit.total_cycles = 0;
state->dates.bit.reserved = 0;
state->cycles.bit.reserved = 0;
watch_store_backup_data(state->dates.reg, state->backup_register_dt);
watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
}
/*
Fertility Window based on "The Calendar Method"
Source: https://www.womenshealth.gov/pregnancy/you-get-pregnant/trying-conceive
The Calendar Method has several steps:
Step 1: Track the menstrual cycle for 812 months. One cycle is from the first day of one
period until the first day of the next period. The average cycle is 28 days, but
it may be as short as 24 days or as long as 38 days.
Step 2: Subtract 18 from the number of days in the shortest menstrual cycle.
Step 3: Subtract 11 from the number of days in the longest menstrual cycle.
Step 4: Using a calendar, mark down the start of the next period (using previous instead). Count ahead by the number
of days calculated in step 2. This is when peak fertility begins. Peak fertility ends
at the number of days calculated in step 3.
NOTE: Right now, the fertility window face displays its estimated window as soon as tracking is activated, although
it is important to keep in mind that The Calendar Method states that peak accuracy of the window will be
reached only after at least 8 months of tracking the menstrual cycle (can make it so that it only displays
after total_days_tracked >= 8 months...but the info is interesting and should already be taken with the understanding that,
in general, it is a rough estimation at best).
*/
typedef enum Fertile_Window {first_day, last_day} fertile_window;
// Calculate the predicted starting or ending day of peak fertility
static inline uint32_t get_day_pk_fert(menstrual_cycle_state_t *state, fertile_window which_day) {
// Get the date of the previous period
watch_date_time date_prev_period;
date_prev_period.unit.second = 0;
date_prev_period.unit.minute = 0;
date_prev_period.unit.hour = 0;
date_prev_period.unit.day = state->dates.bit.prev_day;
date_prev_period.unit.month = state->dates.bit.prev_month;
date_prev_period.unit.year = state->dates.bit.prev_year;
// Convert the previous period date to Unix time
uint32_t unix_prev_period = watch_utility_date_time_to_unix_time(date_prev_period, state->utc_offset);
// Calculate the Unix time of the predicted peak fertility day based on the length of the shortest/longest cycle
uint32_t unix_pk_date;
switch(which_day) {
case first_day:
unix_pk_date = unix_prev_period + ((state->cycles.bit.shortest_cycle - 18) * SECONDS_PER_DAY);
break;
case last_day:
unix_pk_date = unix_prev_period + ((state->cycles.bit.longest_cycle - 11) * SECONDS_PER_DAY);
break;
}
// Convert the Unix time of the predicted peak fertility day to a date/time and return the day of the month
return watch_utility_date_time_from_unix_time(unix_pk_date, state->utc_offset).unit.day;
}
// Determine if today falls within the predicted peak fertility window
static inline bool inside_fert_window(menstrual_cycle_state_t *state) {
// If tracking has not yet been activated, return false
if (!(state->dates.reg))
return false;
// Get the current date/time
watch_date_time date_time_now = watch_rtc_get_date_time();
// Check if the current day falls between the first and last predicted peak fertility days
if (get_day_pk_fert(state, first_day) > get_day_pk_fert(state, last_day)) { // We are crossing over the end of the month
if (date_time_now.unit.day >= get_day_pk_fert(state, first_day) ||
date_time_now.unit.day <= get_day_pk_fert(state, last_day))
return true;
}
else if (date_time_now.unit.day >= get_day_pk_fert(state, first_day) &&
date_time_now.unit.day <= get_day_pk_fert(state, last_day))
return true;
// If the current day does not fall within the predicted peak fertility window, return false
return false;
}
// Update the shortest and longest menstrual cycles based on the previous menstrual cycle
static inline void update_shortest_longest_cycle(menstrual_cycle_state_t *state) {
// Get the date of the previous menstrual cycle
watch_date_time date_prev_period;
date_prev_period.unit.second = 0;
date_prev_period.unit.minute = 0;
date_prev_period.unit.hour = 0;
date_prev_period.unit.day = state->dates.bit.prev_day;
date_prev_period.unit.month = state->dates.bit.prev_month;
date_prev_period.unit.year = state->dates.bit.prev_year;
// Convert the date of the previous menstrual cycle to UNIX time
uint32_t unix_prev_period = watch_utility_date_time_to_unix_time(date_prev_period, state->utc_offset);
// Calculate the length of the current menstrual cycle
uint8_t cycle_length = total_days_tracked(state) - (unix_prev_period / SECONDS_PER_DAY);
// Update the shortest or longest cycle length if necessary
if (cycle_length < state->cycles.bit.shortest_cycle)
state->cycles.bit.shortest_cycle = cycle_length;
else if (cycle_length > state->cycles.bit.longest_cycle)
state->cycles.bit.longest_cycle = cycle_length;
}
void menstrual_cycle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) watch_face_index;
(void) settings;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(menstrual_cycle_state_t));
memset(*context_ptr, 0, sizeof(menstrual_cycle_state_t));
menstrual_cycle_state_t *state = ((menstrual_cycle_state_t *)*context_ptr);
state->dates.bit.first_day = 0;
state->dates.bit.first_month = 0;
state->dates.bit.first_year = 0;
state->dates.bit.prev_day = 0;
state->dates.bit.prev_month = 0;
state->dates.bit.prev_year = 0;
state->cycles.bit.shortest_cycle = TYPICAL_AVG_CYC;
state->cycles.bit.longest_cycle = TYPICAL_AVG_CYC;
state->cycles.bit.average_cycle = TYPICAL_AVG_CYC;
state->cycles.bit.total_cycles = 0;
state->dates.bit.reserved = 0;
state->cycles.bit.reserved = 0;
state->backup_register_dt = 0;
state->backup_register_cy = 0;
}
menstrual_cycle_state_t *state = ((menstrual_cycle_state_t *)*context_ptr);
if (!(state->backup_register_dt && state->backup_register_cy)) {
state->backup_register_dt = movement_claim_backup_register();
state->backup_register_cy = movement_claim_backup_register();
if (state->backup_register_dt && state->backup_register_cy) {
watch_store_backup_data(state->dates.reg, state->backup_register_dt);
watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
}
}
else {
state->dates.reg = watch_get_backup_data(state->backup_register_dt);
state->cycles.reg = watch_get_backup_data(state->backup_register_cy);
}
}
void menstrual_cycle_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
menstrual_cycle_state_t *state = (menstrual_cycle_state_t *)context;
state->period_today = 0;
state->current_page = 0;
state->reset_tracking = 0;
state->utc_offset = movement_timezone_offsets[settings->bit.time_zone] * 60;
movement_request_tick_frequency(4); // we need to manually blink some pixels
}
bool menstrual_cycle_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
menstrual_cycle_state_t *state = (menstrual_cycle_state_t *)context;
watch_date_time date_period;
uint8_t current_page = state->current_page;
uint8_t first_day_fert;
uint8_t last_day_fert;
uint32_t unix_now;
uint32_t unix_prev_period;
switch (event.event_type) {
case EVENT_TICK:
case EVENT_ACTIVATE:
// Do nothing; handled below.
break;
case EVENT_MODE_BUTTON_UP:
movement_move_to_next_face();
return false;
case EVENT_LIGHT_BUTTON_DOWN:
current_page = (current_page + 1) % MENSTRUAL_CYCLE_FACE_NUM_PAGES;
state->current_page = current_page;
state->days_prev_period = 0;
watch_clear_indicator(WATCH_INDICATOR_BELL);
if (watch_tick_animation_is_running())
watch_stop_tick_animation();
break;
case EVENT_ALARM_LONG_PRESS:
switch (current_page) {
case period_in_num_days:
break;
case average_cycle:
break;
case peak_fertility_window:
break;
case period_is_here:
if (state->period_today && total_days_tracked(state)) {
// Calculate before updating date of last period
update_shortest_longest_cycle(state);
// Update the date of last period after calulating the, now previous, cycle length
date_period = watch_rtc_get_date_time();
state->dates.bit.prev_day = date_period.unit.day;
state->dates.bit.prev_month = date_period.unit.month;
state->dates.bit.prev_year = date_period.unit.year;
// Calculate new cycle average
state->cycles.bit.total_cycles += 1;
state->cycles.bit.average_cycle = total_days_tracked(state) / state->cycles.bit.total_cycles;
// Store the new data
watch_store_backup_data(state->dates.reg, state->backup_register_dt);
watch_store_backup_data(state->cycles.reg, state->backup_register_cy);
state->period_today = !(state->period_today);
beep(settings);
}
break;
case first_period:
// If tracking has not yet been activated
if (!(state->dates.reg)) {
unix_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), state->utc_offset);
unix_prev_period = unix_now - (state->days_prev_period * SECONDS_PER_DAY);
date_period = watch_utility_date_time_from_unix_time(unix_prev_period, state->utc_offset);
state->dates.bit.first_day = date_period.unit.day;
state->dates.bit.first_month = date_period.unit.month;
state->dates.bit.first_year = date_period.unit.year;
state->dates.bit.prev_day = date_period.unit.day;
state->dates.bit.prev_month = date_period.unit.month;
state->dates.bit.prev_year = date_period.unit.year;
watch_store_backup_data(state->dates.reg, state->backup_register_dt);
beep(settings);
}
break;
case reset:
if (state->reset_tracking) {
reset_tracking(state);
state->reset_tracking = !(state->reset_tracking);
beep(settings);
}
break;
}
break;
case EVENT_ALARM_BUTTON_UP:
switch (current_page) {
case period_in_num_days:
break;
case average_cycle:
break;
case peak_fertility_window:
break;
case period_is_here:
if (total_days_tracked(state))
state->period_today = !(state->period_today);
break;
case first_period:
if (!(state->dates.reg))
state->days_prev_period = (state->days_prev_period > 99) ? 0 : state->days_prev_period + 1; // Cycle through pages to quickly reset to 0
break;
case reset:
state->reset_tracking = !(state->reset_tracking);
break;
}
break;
case EVENT_TIMEOUT:
movement_move_to_face(0);
break;
default:
return movement_default_loop_handler(event, settings);
}
watch_display_string((char *)menstrual_cycle_face_titles[current_page], 0);
if (state->dates.reg)
watch_set_indicator(WATCH_INDICATOR_SIGNAL); // signal that we are now in a tracking state
char buf[13];
switch (current_page) {
case period_in_num_days:
sprintf(buf, "%2d", days_till_period(state));
if (inside_fert_window(state))
watch_set_indicator(WATCH_INDICATOR_BELL);
watch_display_string(buf, 4);
break;
case average_cycle:
sprintf(buf, "%2d", state->cycles.bit.average_cycle);
watch_display_string(buf, 2);
break;
case peak_fertility_window:
if (event.subsecond % 5 && state->dates.reg) { // blink active for 3 quarter-seconds
first_day_fert = get_day_pk_fert(state, first_day);
last_day_fert = get_day_pk_fert(state, last_day);
sprintf(buf, "Fr%2d To %2d", first_day_fert, last_day_fert); // From: first day | To: last day
if (inside_fert_window(state))
watch_set_indicator(WATCH_INDICATOR_BELL);
watch_display_string(buf, 0);
}
break;
case period_is_here:
if (event.subsecond % 5) { // blink active for 3 quarter-seconds
if (!(state->dates.reg))
watch_display_string("NA", 8); // Not Applicable: Do not allow period entry until tracking is activated...
else if (state->period_today)
watch_display_string("y", 9);
else
watch_display_string("n", 9);
}
break;
case first_period:
if (state->dates.reg) {
if (!watch_tick_animation_is_running())
watch_start_tick_animation(500); // Tracking activated
}
else if (event.subsecond % 5) { // blink active for 3 quarter-seconds
sprintf(buf, "%2d", state->days_prev_period);
watch_display_string(buf, 8);
}
break;
case reset:
// blink active for 3 quarter-seconds
if (event.subsecond % 5 && state->reset_tracking)
watch_display_string("y", 9);
else if (event.subsecond % 5)
watch_display_string("n", 9);
break;
}
return true;
}
void menstrual_cycle_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}

View File

@ -0,0 +1,80 @@
/*
* MIT License
*
* Copyright (c) 2023 Joseph Borne Komosa | @jokomo24
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef MENSTRUAL_CYCLE_FACE_H_
#define MENSTRUAL_CYCLE_FACE_H_
#include "movement.h"
typedef struct {
// Store the date of the 'first' and the total cycles since to calulate and store the average menstrual cycle.
// Store the date of the previous, most recent, period to calculate the cycle length.
// Store the shortest and longest cycle to calculate the fertility window for The Calender Method.
// NOTE: Not thrilled about using two registers, but could not find a way to perform The Calender Method
// without requiring both the 'first' and 'prev' dates.
union {
struct {
uint8_t first_day : 5;
uint8_t first_month : 4;
uint8_t first_year : 6; // 0-63 (representing 2020-2083)
uint8_t prev_day : 5;
uint8_t prev_month : 4;
uint8_t prev_year : 6; // 0-63 (representing 2020-2083)
uint8_t reserved : 2; // left over bit space
} bit;
uint32_t reg; // Tracking's been activated if > 0
} dates;
union {
struct {
uint8_t shortest_cycle : 6; // For step 2 of The Calender Method
uint8_t longest_cycle : 6; // For step 3 of The Calender Method
uint8_t average_cycle : 6; // The average menstrual cycle lasts 28 days, but normal cycles can vary from 21 to 35 days
uint16_t total_cycles : 11; // The total cycles (periods) entered since the start of tracking
uint8_t reserved : 3; // left over bit space
} bit;
uint32_t reg;
} cycles;
uint8_t backup_register_dt;
uint8_t backup_register_cy;
uint8_t current_page;
uint8_t days_prev_period;
int32_t utc_offset;
bool period_today;
bool reset_tracking;
} menstrual_cycle_state_t;
void menstrual_cycle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void menstrual_cycle_face_activate(movement_settings_t *settings, void *context);
bool menstrual_cycle_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void menstrual_cycle_face_resign(movement_settings_t *settings, void *context);
#define menstrual_cycle_face ((const watch_face_t){ \
menstrual_cycle_face_setup, \
menstrual_cycle_face_activate, \
menstrual_cycle_face_loop, \
menstrual_cycle_face_resign, \
NULL, \
})
#endif // MENSTRUAL_CYCLE_FACE_H_

View File

@ -0,0 +1,263 @@
/*
* MIT License
*
* Copyright (c) 2023 Austin Teets
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "metronome_face.h"
#include "watch.h"
static const int8_t _sound_seq_start[] = {BUZZER_NOTE_C8, 2, 0};
static const int8_t _sound_seq_beat[] = {BUZZER_NOTE_C6, 2, 0};
void metronome_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(metronome_state_t));
memset(*context_ptr, 0, sizeof(metronome_state_t));
}
}
void metronome_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
metronome_state_t *state = (metronome_state_t *)context;
movement_request_tick_frequency(2);
if (state->bpm == 0) {
state->count = 4;
state->bpm = 120;
state->soundOn = true;
}
state->mode = metWait;
state->correction = 0;
state->setCur = hundred;
}
static void _metronome_face_update_lcd(metronome_state_t *state) {
char buf[11];
if (state->soundOn) {
watch_set_indicator(WATCH_INDICATOR_BELL);
} else {
watch_clear_indicator(WATCH_INDICATOR_BELL);
}
sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp");
watch_display_string(buf, 0);
}
static void _metronome_start_stop(metronome_state_t *state) {
if (state->mode != metRun) {
movement_request_tick_frequency(64);
state->mode = metRun;
watch_clear_display();
double ticks = 3840.0 / (double)state->bpm;
state->tick = (int) ticks;
state->curTick = (int) ticks;
state->halfBeat = (int)(state->tick/2);
state->curCorrection = ticks - state->tick;
state->correction = ticks - state->tick;
state->curBeat = 1;
} else {
state->mode = metWait;
movement_request_tick_frequency(2);
_metronome_face_update_lcd(state);
}
}
static void _metronome_tick_beat(metronome_state_t *state) {
char buf[11];
if (state->soundOn) {
if (state->curBeat == 1) {
watch_buzzer_play_sequence((int8_t *)_sound_seq_start, NULL);
} else {
watch_buzzer_play_sequence((int8_t *)_sound_seq_beat, NULL);
}
}
sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp");
watch_display_string(buf, 0);
}
static void _metronome_event_tick(uint8_t subsecond, metronome_state_t *state) {
(void) subsecond;
if (state->curCorrection >= 1) {
state->curCorrection -= 1;
state->curTick -= 1;
}
int diff = state->curTick - state->tick;
if(diff == 0) {
_metronome_tick_beat(state);
state->curTick = 0;
state->curCorrection += state->correction;
if (state->curBeat < state->count ) {
state->curBeat += 1;
} else {
state->curBeat = 1;
}
} else {
if (state->curTick == state->halfBeat) {
watch_clear_display();
}
state->curTick += 1;
}
}
static void _metronome_setting_tick(uint8_t subsecond, metronome_state_t *state) {
char buf[13];
sprintf(buf, "MN %d %03d%s", state->count, state->bpm, "bp");
if (subsecond%2 == 0) {
switch (state->setCur) {
case hundred:
buf[5] = ' ';
break;
case ten:
buf[6] = ' ';
break;
case one:
buf[7] = ' ';
break;
case count:
buf[3] = ' ';
break;
case alarm:
break;
}
}
if (state->setCur == alarm) {
sprintf(buf, "MN 8eep%s", state->soundOn ? "On" : " -");
}
if (state->soundOn) {
watch_set_indicator(WATCH_INDICATOR_BELL);
} else {
watch_clear_indicator(WATCH_INDICATOR_BELL);
}
watch_display_string(buf, 0);
}
static void _metronome_update_setting(metronome_state_t *state) {
char buf[13];
switch (state->setCur) {
case hundred:
if (state->bpm < 100) {
state->bpm += 100;
} else {
state->bpm -= 100;
}
break;
case ten:
if ((state->bpm / 10) % 10 < 9) {
state->bpm += 10;
} else {
state->bpm -= 90;
}
break;
case one:
if (state->bpm%10 < 9) {
state->bpm += 1;
} else {
state->bpm -= 9;
}
break;
case count:
if (state->count < 9) {
state->count += 1;
} else {
state->count = 2;
}
break;
case alarm:
state->soundOn = !state->soundOn;
break;
}
sprintf(buf, "MN %d %03d%s", state->count % 10, state->bpm, "bp");
if (state->setCur == alarm) {
sprintf(buf, "MN 8eep%s", state->soundOn ? "On" : " -");
}
if (state->soundOn) {
watch_set_indicator(WATCH_INDICATOR_BELL);
} else {
watch_clear_indicator(WATCH_INDICATOR_BELL);
}
watch_display_string(buf, 0);
}
bool metronome_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
metronome_state_t *state = (metronome_state_t *)context;
switch (event.event_type) {
case EVENT_ACTIVATE:
_metronome_face_update_lcd(state);
break;
case EVENT_TICK:
if (state->mode == metRun){
_metronome_event_tick(event.subsecond, state);
} else if (state->mode == setMenu) {
_metronome_setting_tick(event.subsecond, state);
}
break;
case EVENT_ALARM_BUTTON_UP:
if (state->mode == setMenu) {
_metronome_update_setting(state);
} else {
_metronome_start_stop(state);
}
break;
case EVENT_LIGHT_BUTTON_DOWN:
if (state->mode == setMenu) {
if (state->setCur < alarm) {
state->setCur += 1;
} else {
state->setCur = hundred;
}
}
break;
case EVENT_ALARM_LONG_PRESS:
if (state->mode != metRun && state->mode != setMenu) {
movement_request_tick_frequency(2);
state->mode = setMenu;
_metronome_face_update_lcd(state);
} else if (state->mode == setMenu) {
state->mode = metWait;
_metronome_face_update_lcd(state);
}
break;
case EVENT_MODE_BUTTON_UP:
movement_move_to_next_face();
break;
case EVENT_TIMEOUT:
if (state->mode != metRun) {
movement_move_to_face(0);
}
break;
case EVENT_LOW_ENERGY_UPDATE:
break;
default:
return movement_default_loop_handler(event, settings);
}
return true;
}
void metronome_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}

View File

@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (c) 2023 Austin Teets
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef METRONOME_FACE_H_
#define METRONOME_FACE_H_
#include "movement.h"
/*
* A Metronome watch complication
* Allows the user to set the BPM, counts per measure, beep sound on/off
* Screen flashes on on the beat and off on the half beat (1/8th note)
* Beep will sound high for downbeat and low for subsequent beats in measure
* USE:
* Press Alarm to start/stop metronome_face
* Hold Alarm to enter settings menu
* Short Light press will move through options
* Short Alarm press will increment/toggle options
* Long alarm press will exit options
*/
typedef enum {
metWait,
metRun,
setMenu
} metronome_mode_t;
typedef enum {
hundred,
ten,
one,
count,
alarm
} setting_cursor_t;
typedef struct {
// Anything you need to keep track of, put it here!
uint8_t bpm;
double correction;
double curCorrection;
int count;
int tick;
int curTick;
int curBeat;
int halfBeat;
metronome_mode_t mode : 3;
setting_cursor_t setCur : 4;
bool soundOn;
} metronome_state_t;
void metronome_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void metronome_face_activate(movement_settings_t *settings, void *context);
bool metronome_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void metronome_face_resign(movement_settings_t *settings, void *context);
#define metronome_face ((const watch_face_t){ \
metronome_face_setup, \
metronome_face_activate, \
metronome_face_loop, \
metronome_face_resign, \
NULL, \
})
#endif // METRONOME_FACE_H_

View File

@ -0,0 +1,384 @@
/*
* MIT License
*
* Copyright (c) 2025 hueso
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "moonrise_face.h"
#include "sunrise_sunset_face.h"
#include "watch.h"
#include "watch_utility.h"
#include "moonrise.h"
#if __EMSCRIPTEN__
#include <emscripten.h>
#endif
static const uint8_t _location_count = sizeof(longLatPresets) / sizeof(long_lat_presets_t);
static void _moonrise_face_update(movement_settings_t *settings, moonrise_state_t *state) {
char buf[11];
movement_location_t movement_location;
if (state->longLatToUse == 0 || _location_count <= 1)
movement_location = (movement_location_t) watch_get_backup_data(1);
else{
movement_location.bit.latitude = longLatPresets[state->longLatToUse].latitude;
movement_location.bit.longitude = longLatPresets[state->longLatToUse].longitude;
}
if (movement_location.reg == 0) {
watch_clear_colon();
watch_display_string("Mz no Loc", 0);
return;
}
watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
watch_date_time scratch_time; // scratchpad, contains different values at different times
scratch_time.reg = date_time.reg;
double lat = (double)movement_location.bit.latitude / 100.0;
double lon = (double)movement_location.bit.longitude / 100.0;
uint32_t t = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60);
MoonRise mr = MoonRise_calculate(lat, lon, t);
if(mr.isVisible)
watch_set_indicator(WATCH_INDICATOR_LAP);
else
watch_clear_indicator(WATCH_INDICATOR_LAP);
if ( (state->rise_index == 0 && !mr.hasRise) ||
(state->rise_index == 1 && !mr.hasSet) ) {
watch_clear_colon();
watch_clear_indicator(WATCH_INDICATOR_PM);
watch_clear_indicator(WATCH_INDICATOR_24H);
snprintf(buf, sizeof(buf), "%s%2d none ", state->rise_index ? "M_" : "M~", scratch_time.unit.day);
watch_display_string(buf, 0);
return;
}
watch_set_colon();
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero)
watch_set_indicator(WATCH_INDICATOR_24H);
if(state->rise_index == 0)
scratch_time = watch_utility_date_time_from_unix_time(mr.riseTime, movement_timezone_offsets[settings->bit.time_zone] * 60);
else
scratch_time = watch_utility_date_time_from_unix_time(mr.setTime, movement_timezone_offsets[settings->bit.time_zone] * 60);
state->rise_set_expires.reg = scratch_time.reg;
bool set_leading_zero = false;
if (!settings->bit.clock_mode_24h)
if (watch_utility_convert_to_12_hour(&scratch_time))
watch_set_indicator(WATCH_INDICATOR_PM);
else
watch_clear_indicator(WATCH_INDICATOR_PM);
else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) {
set_leading_zero = true;
}
snprintf(buf, sizeof(buf), "%s%2d%2d%02d%2s", state->rise_index ? "M_" : "M~", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute,longLatPresets[state->longLatToUse].name);
watch_display_string(buf, 0);
if (set_leading_zero)
watch_display_string("0", 4);
return;
}
static int16_t _moonrise_face_latlon_from_struct(moonrise_lat_lon_settings_t val) {
int16_t retval = (val.sign ? -1 : 1) *
(
val.hundreds * 10000 +
val.tens * 1000 +
val.ones * 100 +
val.tenths * 10 +
val.hundredths
);
return retval;
}
static moonrise_lat_lon_settings_t _moonrise_face_struct_from_latlon(int16_t val) {
moonrise_lat_lon_settings_t retval;
retval.sign = val < 0;
val = abs(val);
retval.hundredths = val % 10;
val /= 10;
retval.tenths = val % 10;
val /= 10;
retval.ones = val % 10;
val /= 10;
retval.tens = val % 10;
val /= 10;
retval.hundreds = val % 10;
return retval;
}
static void _moonrise_face_update_location_register(moonrise_state_t *state) {
if (state->location_changed) {
movement_location_t movement_location;
int16_t lat = _moonrise_face_latlon_from_struct(state->working_latitude);
int16_t lon = _moonrise_face_latlon_from_struct(state->working_longitude);
movement_location.bit.latitude = lat;
movement_location.bit.longitude = lon;
watch_store_backup_data(movement_location.reg, 1);
state->location_changed = false;
}
}
static void _moonrise_face_update_settings_display(movement_event_t event, moonrise_state_t *state) {
char buf[12];
switch (state->page) {
case 0:
return;
case 1:
snprintf(buf, sizeof(buf), "LA %c %04d", state->working_latitude.sign ? '-' : '+', abs(_moonrise_face_latlon_from_struct(state->working_latitude)));
break;
case 2:
snprintf(buf, sizeof(buf), "LO %c%05d", state->working_longitude.sign ? '-' : '+', abs(_moonrise_face_latlon_from_struct(state->working_longitude)));
break;
}
if (event.subsecond % 2) {
buf[state->active_digit + 4] = ' ';
}
watch_display_string(buf, 0);
}
static void _moonrise_face_advance_digit(moonrise_state_t *state) {
state->location_changed = true;
switch (state->page) {
case 1: // latitude
switch (state->active_digit) {
case 0:
state->working_latitude.sign++;
break;
case 1:
// we skip this digit
break;
case 2:
state->working_latitude.tens = (state->working_latitude.tens + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) {
// prevent latitude from going over ±90.
// TODO: perform these checks when advancing the digit?
state->working_latitude.ones = 0;
state->working_latitude.tenths = 0;
state->working_latitude.hundredths = 0;
}
break;
case 3:
state->working_latitude.ones = (state->working_latitude.ones + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) state->working_latitude.ones = 0;
break;
case 4:
state->working_latitude.tenths = (state->working_latitude.tenths + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) state->working_latitude.tenths = 0;
break;
case 5:
state->working_latitude.hundredths = (state->working_latitude.hundredths + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_latitude)) > 9000) state->working_latitude.hundredths = 0;
break;
}
break;
case 2: // longitude
switch (state->active_digit) {
case 0:
state->working_longitude.sign++;
break;
case 1:
state->working_longitude.hundreds = (state->working_longitude.hundreds + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) {
// prevent longitude from going over ±180
state->working_longitude.tens = 8;
state->working_longitude.ones = 0;
state->working_longitude.tenths = 0;
state->working_longitude.hundredths = 0;
}
break;
case 2:
state->working_longitude.tens = (state->working_longitude.tens + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.tens = 0;
break;
case 3:
state->working_longitude.ones = (state->working_longitude.ones + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.ones = 0;
break;
case 4:
state->working_longitude.tenths = (state->working_longitude.tenths + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.tenths = 0;
break;
case 5:
state->working_longitude.hundredths = (state->working_longitude.hundredths + 1) % 10;
if (abs(_moonrise_face_latlon_from_struct(state->working_longitude)) > 18000) state->working_longitude.hundredths = 0;
break;
}
break;
}
}
void moonrise_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(moonrise_state_t));
memset(*context_ptr, 0, sizeof(moonrise_state_t));
}
}
void moonrise_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
#if __EMSCRIPTEN__
int16_t browser_lat = EM_ASM_INT({
return lat;
});
int16_t browser_lon = EM_ASM_INT({
return lon;
});
if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
movement_location_t browser_loc;
browser_loc.bit.latitude = browser_lat;
browser_loc.bit.longitude = browser_lon;
watch_store_backup_data(browser_loc.reg, 1);
}
#endif
moonrise_state_t *state = (moonrise_state_t *)context;
movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
state->working_latitude = _moonrise_face_struct_from_latlon(movement_location.bit.latitude);
state->working_longitude = _moonrise_face_struct_from_latlon(movement_location.bit.longitude);
}
bool moonrise_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
moonrise_state_t *state = (moonrise_state_t *)context;
switch (event.event_type) {
case EVENT_ACTIVATE:
_moonrise_face_update(settings, state);
break;
case EVENT_LOW_ENERGY_UPDATE:
case EVENT_TICK:
if (state->page == 0) {
// if entering low energy mode, start tick animation
if (event.event_type == EVENT_LOW_ENERGY_UPDATE && !watch_tick_animation_is_running()) watch_start_tick_animation(1000);
// check if we need to update the display
watch_date_time date_time = watch_rtc_get_date_time();
if (date_time.reg >= state->rise_set_expires.reg) {
// and on the off chance that this happened before EVENT_TIMEOUT snapped us back to rise/set 0, go back now
state->rise_index = 0;
_moonrise_face_update(settings, state);
}
} else {
_moonrise_face_update_settings_display(event, state);
}
break;
case EVENT_LIGHT_BUTTON_DOWN:
if (state->page) {
state->active_digit++;
if (state->page == 1 && state->active_digit == 1) state->active_digit++; // max latitude is +- 90, no hundreds place
if (state->active_digit > 5) {
state->active_digit = 0;
state->page = (state->page + 1) % 3;
_moonrise_face_update_location_register(state);
}
_moonrise_face_update_settings_display(event, context);
} else if (_location_count <= 1) {
movement_illuminate_led();
}
if (state->page == 0) {
movement_request_tick_frequency(1);
_moonrise_face_update(settings, state);
}
break;
case EVENT_LIGHT_LONG_PRESS:
if (_location_count <= 1) break;
else if (!state->page) movement_illuminate_led();
break;
case EVENT_LIGHT_BUTTON_UP:
if (state->page == 0 && _location_count > 1) {
state->longLatToUse = (state->longLatToUse + 1) % _location_count;
_moonrise_face_update(settings, state);
}
break;
case EVENT_ALARM_BUTTON_UP:
if (state->page) {
_moonrise_face_advance_digit(state);
_moonrise_face_update_settings_display(event, context);
} else {
state->rise_index = (state->rise_index + 1) % 2;
_moonrise_face_update(settings, state);
}
break;
case EVENT_ALARM_LONG_PRESS:
if (state->page == 0) {
if (state->longLatToUse != 0) {
state->longLatToUse = 0;
_moonrise_face_update(settings, state);
break;
}
state->page++;
state->active_digit = 0;
watch_clear_display();
movement_request_tick_frequency(4);
_moonrise_face_update_settings_display(event, context);
}
else {
state->active_digit = 0;
state->page = 0;
_moonrise_face_update_location_register(state);
_moonrise_face_update(settings, state);
}
break;
case EVENT_TIMEOUT:
if (watch_get_backup_data(1) == 0) {
// if no location set, return home
movement_move_to_face(0);
} else if (state->page || state->rise_index) {
// otherwise on timeout, exit settings mode and return to the next sunrise or sunset
state->page = 0;
state->rise_index = 0;
movement_request_tick_frequency(1);
_moonrise_face_update(settings, state);
}
break;
default:
return movement_default_loop_handler(event, settings);
}
return true;
}
void moonrise_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
moonrise_state_t *state = (moonrise_state_t *)context;
state->page = 0;
state->active_digit = 0;
state->rise_index = 0;
_moonrise_face_update_location_register(state);
}

View File

@ -0,0 +1,90 @@
/*
* MIT License
*
* Copyright (c) 2022 Joey Castillo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef MOONRISE_FACE_H_
#define MOONRISE_FACE_H_
/*
* SUNRISE & SUNSET FACE
*
* The Sunrise/Sunset face is designed to display the next sunrise or sunset
* for a given location. It also functions as an interface for setting the
* location register, which other watch faces can use for various purposes.
*
* Refer to the wiki for usage instructions:
* https://www.sensorwatch.net/docs/watchfaces/complication/#sunrisesunset
*/
#include "movement.h"
typedef struct {
uint8_t sign: 1; // 0-1
uint8_t hundreds: 1; // 0-1, ignored for latitude
uint8_t tens: 4; // 0-9 (must wrap at 10)
uint8_t ones: 4; // 0-9 (must wrap at 10)
uint8_t tenths: 4; // 0-9 (must wrap at 10)
uint8_t hundredths: 4; // 0-9 (must wrap at 10)
} moonrise_lat_lon_settings_t;
typedef struct {
uint8_t page;
uint8_t rise_index;
uint8_t active_digit;
bool location_changed;
watch_date_time rise_set_expires;
moonrise_lat_lon_settings_t working_latitude;
moonrise_lat_lon_settings_t working_longitude;
uint8_t longLatToUse;
} moonrise_state_t;
void moonrise_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void moonrise_face_activate(movement_settings_t *settings, void *context);
bool moonrise_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void moonrise_face_resign(movement_settings_t *settings, void *context);
#define moonrise_face ((const watch_face_t){ \
moonrise_face_setup, \
moonrise_face_activate, \
moonrise_face_loop, \
moonrise_face_resign, \
NULL, \
})
/*
typedef struct {
char name[2];
int16_t latitude;
int16_t longitude;
} long_lat_presets_t;
static const long_lat_presets_t longLatPresets[] =
{
{ .name = " "}, // Default, the long and lat get replaced by what's set in the watch
// { .name = "Ny", .latitude = 4072, .longitude = -7401 }, // New York City, NY
// { .name = "LA", .latitude = 3405, .longitude = -11824 }, // Los Angeles, CA
// { .name = "dE", .latitude = 4221, .longitude = -8305 }, // Detroit, MI
};
*/
#endif // MOONRISE_FACE_H_

View File

@ -0,0 +1,504 @@
/*
* MIT License
*
* Copyright (c) 2023 Jeremy O'Brien
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "smallchesslib.h"
#include "smallchess_face.h"
#include "watch.h"
#define PIECE_LIST_END_MARKER 0xff
int8_t cpu_done_beep[] = {BUZZER_NOTE_C5, 5, BUZZER_NOTE_C6, 5, BUZZER_NOTE_C7, 5, 0};
static void smallchess_init_board(smallchess_face_state_t *state) {
SCL_gameInit((SCL_Game *)state->game, 0);
memset(state->moveable_pieces, 0xff, sizeof(state->moveable_pieces));
memset(state->moveable_dests, 0xff, sizeof(state->moveable_dests));
}
void smallchess_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(smallchess_face_state_t));
memset(*context_ptr, 0, sizeof(smallchess_face_state_t));
/* now alloc/init the game board */
smallchess_face_state_t *state = (smallchess_face_state_t *)*context_ptr;
state->game = malloc(sizeof(SCL_Game));
smallchess_init_board(*context_ptr);
}
}
void smallchess_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}
static void _smallchess_calc_moveable_pieces(smallchess_face_state_t *state) {
int moveable_pieces_idx = 0;
SCL_Game *game = (SCL_Game *)state->game;
for (int i = 0; i < SCL_BOARD_SQUARES; ++i) {
if (game->board[i] != '.' &&
SCL_pieceIsWhite(game->board[i]) == SCL_boardWhitesTurn(game->board)) {
SCL_SquareSet moveable_pieces = SCL_SQUARE_SET_EMPTY;
SCL_boardGetMoves(game->board, i, moveable_pieces);
if (SCL_squareSetSize(moveable_pieces) != 0) {
state->moveable_pieces[moveable_pieces_idx] = i;
moveable_pieces_idx++;
}
}
}
state->moveable_pieces[moveable_pieces_idx] = PIECE_LIST_END_MARKER;
state->moveable_pieces_idx = 0;
}
static void _smallchess_make_ai_move(smallchess_face_state_t *state) {
char ai_from_str[3] = {0};
char ai_to_str[3] = {0};
uint8_t rep_from, rep_to;
char ai_prom;
watch_clear_display();
watch_start_character_blink('C', 100);
SCL_gameGetRepetiotionMove(state->game, &rep_from, &rep_to);
#ifndef __EMSCRIPTEN__
hri_oscctrl_write_OSC16MCTRL_FSEL_bf(OSCCTRL, OSCCTRL_OSC16MCTRL_FSEL_16_Val);
#endif
SCL_getAIMove(state->game, 3, 0, 0, SCL_boardEvaluateStatic, NULL, 0, rep_from, rep_to, &state->ai_from_square, &state->ai_to_square, &ai_prom);
#ifndef __EMSCRIPTEN__
hri_oscctrl_write_OSC16MCTRL_FSEL_bf(OSCCTRL, OSCCTRL_OSC16MCTRL_FSEL_4_Val);
#endif
SCL_gameMakeMove(state->game, state->ai_from_square, state->ai_to_square, ai_prom);
watch_stop_blink();
watch_buzzer_play_sequence(cpu_done_beep, NULL);
/* cache the move as a string for SHOW_CPU_MOVE state */
SCL_squareToString(state->ai_from_square, ai_from_str);
SCL_squareToString(state->ai_to_square, ai_to_str);
snprintf(state->last_move_str, sizeof(state->last_move_str), " %s-%s", ai_from_str, ai_to_str);
/* now cache the list of legal pieces we can move */
_smallchess_calc_moveable_pieces(state);
}
static char _smallchess_make_lowercase(char c) {
if (c < 0x61)
return c + 0x20;
return c;
}
static void _smallchess_get_endgame_string(smallchess_face_state_t *state, char *buf, uint8_t len) {
uint8_t endgame_state = ((SCL_Game *)state->game)->state;
uint16_t ply = ((SCL_Game *)state->game)->ply;
switch (endgame_state) {
case SCL_GAME_STATE_WHITE_WIN:
snprintf(buf, len, "Wh%2dm&ate ", ply);
break;
case SCL_GAME_STATE_BLACK_WIN:
snprintf(buf, len, "bL%2dm&ate ", ply);
break;
case SCL_GAME_STATE_DRAW:
case SCL_GAME_STATE_DRAW_STALEMATE:
case SCL_GAME_STATE_DRAW_REPETITION:
case SCL_GAME_STATE_DRAW_50:
case SCL_GAME_STATE_DRAW_DEAD:
snprintf(buf, len, " %2d Drauu", ply);
break;
default:
snprintf(buf, len, " %2d Error", ply);
break;
}
}
static void _smallchess_face_update_lcd(smallchess_face_state_t *state) {
uint8_t start_square;
uint8_t end_square;
char start_coord[3] = {0};
char end_coord[3] = {0};
char buf[14] = {0};
uint16_t ply = ((SCL_Game *)state->game)->ply;
switch (state->state) {
case SMALLCHESS_MENU_RESUME:
snprintf(buf, sizeof(buf), "SC%2dResume", ply);
break;
case SMALLCHESS_MENU_UNDO:
snprintf(buf, sizeof(buf), "SC%2d Undo ", ply);
break;
case SMALLCHESS_MENU_SHOW_LAST_MOVE:
snprintf(buf, sizeof(buf), "SC%2dShLast", ply);
break;
case SMALLCHESS_MENU_NEW_WHITE:
snprintf(buf, sizeof(buf), "Wh%2dStart ", ply);
break;
case SMALLCHESS_MENU_NEW_BLACK:
snprintf(buf, sizeof(buf), "bL%2dStart ", ply);
break;
case SMALLCHESS_SHOW_CPU_MOVE:
case SMALLCHESS_SHOW_LAST_MOVE:
snprintf(buf,
sizeof(buf),
"%c %2d%s",
_smallchess_make_lowercase(((SCL_Game *)state->game)->board[state->ai_to_square]),
ply,
state->last_move_str);
break;
case SMALLCHESS_SELECT_PIECE:
if (((SCL_Game *)state->game)->state != SCL_GAME_STATE_PLAYING) {
_smallchess_get_endgame_string(state, buf, sizeof(buf));
break;
}
start_square = state->moveable_pieces[state->moveable_pieces_idx];
SCL_squareToString(start_square, start_coord);
snprintf(buf,
sizeof(buf),
"%c %2d %s- ",
_smallchess_make_lowercase(((SCL_Game *)state->game)->board[start_square]),
ply + 1,
start_coord);
break;
case SMALLCHESS_SELECT_DEST:
start_square = state->moveable_pieces[state->moveable_pieces_idx];
SCL_squareToString(start_square, start_coord);
end_square = state->moveable_dests[state->moveable_dests_idx];
SCL_squareToString(end_square, end_coord);
snprintf(buf,
sizeof(buf),
"%c %2d %s-%s",
_smallchess_make_lowercase(((SCL_Game *)state->game)->board[start_square]),
ply + 1,
start_coord,
end_coord);
break;
default:
break;
}
watch_display_string(buf, 0);
}
static void _smallchess_select_main_menu_subitem(smallchess_face_state_t *state) {
char from_str[3] = {0};
char to_str[3] = {0};
char prom;
switch (state->state) {
case SMALLCHESS_MENU_RESUME:
state->state = SMALLCHESS_SELECT_PIECE;
break;
case SMALLCHESS_MENU_UNDO:
/* undo twice to undo the CPU's move and our move */
SCL_gameUndoMove((SCL_Game *)state->game);
SCL_gameUndoMove((SCL_Game *)state->game);
/* and re-calculate the moveable pieces for this new state */
_smallchess_calc_moveable_pieces(state);
break;
case SMALLCHESS_MENU_NEW_WHITE:
SCL_gameInit((SCL_Game *)state->game, 0);
_smallchess_calc_moveable_pieces(state);
state->state = SMALLCHESS_SELECT_PIECE;
break;
case SMALLCHESS_MENU_NEW_BLACK:
SCL_gameInit((SCL_Game *)state->game, 0);
/* force a move since black is playing */
_smallchess_make_ai_move(state);
state->state = SMALLCHESS_SHOW_CPU_MOVE;
break;
case SMALLCHESS_MENU_SHOW_LAST_MOVE:
/* fetch the move */
SCL_recordGetMove(((SCL_Game *)state->game)->record, ((SCL_Game *)state->game)->ply - 1, &state->ai_from_square, &state->ai_to_square, &prom);
SCL_squareToString(state->ai_from_square, from_str);
SCL_squareToString(state->ai_to_square, to_str);
snprintf(state->last_move_str, sizeof(state->last_move_str), " %s-%s", from_str, to_str);
state->state = SMALLCHESS_SHOW_LAST_MOVE;
break;
default:
break;
}
}
static void _smallchess_handle_select_piece_button_event(smallchess_face_state_t *state, movement_event_t event) {
SCL_SquareSet moveable_dests = SCL_SQUARE_SET_EMPTY;
/* back to main menu on any event when game ends */
if (((SCL_Game *)state->game)->state != SCL_GAME_STATE_PLAYING) {
state->state = SMALLCHESS_MENU_RESUME;
return;
}
switch (event.event_type) {
case EVENT_ALARM_BUTTON_UP:
// check for no moves possible state (shouldn't happen but this will prevent weirdness)
if (state->moveable_pieces[0] == PIECE_LIST_END_MARKER) {
return;
}
state->moveable_pieces_idx += 1;
if (state->moveable_pieces_idx >= NUM_ELEMENTS(state->moveable_pieces)) {
state->moveable_pieces_idx = 0;
}
if (state->moveable_pieces[state->moveable_pieces_idx] == PIECE_LIST_END_MARKER) {
state->moveable_pieces_idx = 0;
}
break;
case EVENT_LIGHT_BUTTON_UP:
// check for no moves possible state (shouldn't happen but this will prevent weirdness)
if (state->moveable_pieces[0] == PIECE_LIST_END_MARKER) {
return;
}
/* handle wrap around */
if (state->moveable_pieces_idx == 0) {
for (unsigned int i = 0; i < NUM_ELEMENTS(state->moveable_pieces); i++) {
if (state->moveable_pieces[i] == 0xff) {
state->moveable_pieces_idx = i - 1;
break;
}
}
} else {
state->moveable_pieces_idx -= 1;
}
break;
case EVENT_LIGHT_LONG_PRESS:
if (((SCL_Game *)state->game)->ply == 0) {
state->state = SMALLCHESS_MENU_NEW_WHITE;
} else {
state->state = SMALLCHESS_MENU_RESUME;
}
break;
case EVENT_ALARM_LONG_PRESS:
/* pre-calculate the possible moves this piece can make */
SCL_boardGetMoves(((SCL_Game *)state->game)->board, state->moveable_pieces[state->moveable_pieces_idx], moveable_dests);
state->moveable_dests_idx = 0;
SCL_SQUARE_SET_ITERATE_BEGIN(moveable_dests)
state->moveable_dests[state->moveable_dests_idx] = iteratedSquare;
state->moveable_dests_idx++;
SCL_SQUARE_SET_ITERATE_END
state->moveable_dests[state->moveable_dests_idx] = PIECE_LIST_END_MARKER;
state->moveable_dests_idx = 0;
state->state = SMALLCHESS_SELECT_DEST;
default:
break;
}
}
static void _smallchess_handle_select_dest_button_event(smallchess_face_state_t *state, movement_event_t event) {
switch (event.event_type) {
case EVENT_ALARM_BUTTON_UP:
// check for no moves possible state (shouldn't happen but this will prevent weirdness)
if (state->moveable_dests[0] == PIECE_LIST_END_MARKER) {
return;
}
state->moveable_dests_idx += 1;
if (state->moveable_dests_idx >= (sizeof(state->moveable_dests) / sizeof(state->moveable_dests[0]))) {
state->moveable_dests_idx = 0;
}
if (state->moveable_dests[state->moveable_dests_idx] == PIECE_LIST_END_MARKER) {
state->moveable_dests_idx = 0;
}
break;
case EVENT_LIGHT_BUTTON_UP:
// check for no moves possible state (shouldn't happen but this will prevent weirdness)
if (state->moveable_dests[0] == PIECE_LIST_END_MARKER) {
return;
}
/* handle wrap around */
if (state->moveable_dests_idx == 0) {
for (unsigned int i = 0; i < NUM_ELEMENTS(state->moveable_dests); i++) {
if (state->moveable_dests[i] == 0xff) {
state->moveable_dests_idx = i - 1;
break;
}
}
} else {
state->moveable_dests_idx -= 1;
}
break;
case EVENT_LIGHT_LONG_PRESS:
state->state = SMALLCHESS_SELECT_PIECE;
break;
case EVENT_ALARM_LONG_PRESS:
SCL_gameMakeMove((SCL_Game *)state->game, state->moveable_pieces[state->moveable_pieces_idx], state->moveable_dests[state->moveable_dests_idx], 'q');
/* if the player didn't win or draw here, calculate a move */
if (((SCL_Game *)state->game)->state == SCL_GAME_STATE_PLAYING) {
_smallchess_make_ai_move(state);
state->state = SMALLCHESS_SHOW_CPU_MOVE;
} else {
/* player ended the game through mate or draw; jump to select piece screen to show state */
state->state = SMALLCHESS_SELECT_PIECE;
}
break;
default:
break;
}
}
/* this just waits until any button is hit */
static void _smallchess_handle_show_cpu_move_button_event(smallchess_face_state_t *state, movement_event_t event) {
switch (event.event_type) {
case EVENT_ALARM_BUTTON_UP:
case EVENT_LIGHT_BUTTON_UP:
case EVENT_ALARM_LONG_PRESS:
case EVENT_LIGHT_LONG_PRESS:
state->state = SMALLCHESS_SELECT_PIECE;
break;
default:
break;
}
}
static void _smallchess_handle_show_last_move_button_event(smallchess_face_state_t *state, movement_event_t event) {
switch (event.event_type) {
case EVENT_ALARM_BUTTON_UP:
case EVENT_LIGHT_BUTTON_UP:
case EVENT_ALARM_LONG_PRESS:
case EVENT_LIGHT_LONG_PRESS:
state->state = SMALLCHESS_MENU_SHOW_LAST_MOVE;
break;
default:
break;
}
}
static void _smallchess_handle_playing_button_event(smallchess_face_state_t *state, movement_event_t event) {
if (state->state == SMALLCHESS_SELECT_PIECE) {
_smallchess_handle_select_piece_button_event(state, event);
} else if (state->state == SMALLCHESS_SELECT_DEST) {
_smallchess_handle_select_dest_button_event(state, event);
} else if (state->state == SMALLCHESS_SHOW_CPU_MOVE) {
_smallchess_handle_show_cpu_move_button_event(state, event);
} else if (state->state == SMALLCHESS_SHOW_LAST_MOVE) {
_smallchess_handle_show_last_move_button_event(state, event);
}
}
static void _smallchess_handle_main_menu_button_event(smallchess_face_state_t *state, movement_event_t event) {
uint16_t ply = ((SCL_Game *)state->game)->ply;
switch (event.event_type) {
case EVENT_ALARM_BUTTON_UP:
/* no game started; only offer start white/start black */
if (ply == 0) {
if (state->state == SMALLCHESS_MENU_NEW_WHITE) {
state->state = SMALLCHESS_MENU_NEW_BLACK;
} else {
state->state = SMALLCHESS_MENU_NEW_WHITE;
}
} else {
state->state++;
if (state->state >= SMALLCHESS_PLAYING_SPLIT) {
state->state = SMALLCHESS_MENU_RESUME;
}
}
break;
case EVENT_LIGHT_BUTTON_UP:
/* no game started; only offer start white/start black */
if (ply == 0) {
if (state->state == SMALLCHESS_MENU_NEW_BLACK) {
state->state = SMALLCHESS_MENU_NEW_WHITE;
} else {
state->state = SMALLCHESS_MENU_NEW_BLACK;
}
} else {
if (state->state == SMALLCHESS_MENU_RESUME) {
state->state = SMALLCHESS_PLAYING_SPLIT - 1;
} else {
state->state--;
}
}
break;
case EVENT_ALARM_LONG_PRESS:
_smallchess_select_main_menu_subitem(state);
break;
default:
break;
}
}
static void _smallchess_handle_button_event(smallchess_face_state_t *state, movement_event_t event) {
if (state->state < SMALLCHESS_PLAYING_SPLIT) {
/* in main menu */
_smallchess_handle_main_menu_button_event(state, event);
} else if (state->state > SMALLCHESS_PLAYING_SPLIT) {
/* in piece selection */
_smallchess_handle_playing_button_event(state, event);
}
}
bool smallchess_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
(void) settings;
smallchess_face_state_t *state = (smallchess_face_state_t *)context;
switch (event.event_type) {
case EVENT_ACTIVATE:
if (((SCL_Game *)state->game)->ply == 0) {
state->state = SMALLCHESS_MENU_NEW_WHITE;
} else {
state->state = SMALLCHESS_MENU_RESUME;
}
_smallchess_face_update_lcd(state);
break;
case EVENT_LIGHT_BUTTON_UP:
case EVENT_LIGHT_LONG_PRESS:
case EVENT_ALARM_BUTTON_UP:
case EVENT_ALARM_LONG_PRESS:
_smallchess_handle_button_event(state, event);
_smallchess_face_update_lcd(state);
break;
case EVENT_TICK:
break;
case EVENT_TIMEOUT:
break;
case EVENT_LIGHT_BUTTON_DOWN:
break;
default:
movement_default_loop_handler(event, settings);
break;
}
return true;
}
void smallchess_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
watch_set_led_off();
}

View File

@ -0,0 +1,90 @@
/*
* MIT License
*
* Copyright (c) 2023 Jeremy O'Brien
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef SMALLCHESS_FACE_H_
#define SMALLCHESS_FACE_H_
#include "movement.h"
/*
* Chess watchface
*
* Implements a (very) simple chess engine.
* Uses smallchesslib for the engine: https://codeberg.org/drummyfish/smallchesslib
*
* When moving a piece, only valid pieces and moves are presented.
*
* Interaction is done through a simple menu/submenu system:
* - Light button: navigate backwards through the current menu
* - Alarm button: navigate forwards through the current menu
* - Light button (long press): navigate up to the parent menu
* - Alarm button (long press): select the current item or submenu
*/
enum smallchess_state {
/* main menu */
SMALLCHESS_MENU_RESUME,
SMALLCHESS_MENU_SHOW_LAST_MOVE,
SMALLCHESS_MENU_UNDO,
SMALLCHESS_MENU_NEW_WHITE,
SMALLCHESS_MENU_NEW_BLACK,
SMALLCHESS_PLAYING_SPLIT,
/* playing game submenu */
SMALLCHESS_SHOW_LAST_MOVE,
SMALLCHESS_SHOW_CPU_MOVE,
SMALLCHESS_SELECT_PIECE,
SMALLCHESS_SELECT_DEST,
};
#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]))
#define SMALLCHESS_NUM_PIECES 16 // number of pieces each player has
typedef struct {
void *game;
enum smallchess_state state;
uint8_t moveable_pieces[SMALLCHESS_NUM_PIECES + 1];
uint8_t moveable_pieces_idx;
uint8_t moveable_dests[29]; // this magic number represents the maximum number of moves a piece can make (queen in center of board)
// plus one for the end list marker
uint8_t moveable_dests_idx;
char last_move_str[7];
uint8_t ai_from_square, ai_to_square;
} smallchess_face_state_t;
void smallchess_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void smallchess_face_activate(movement_settings_t *settings, void *context);
bool smallchess_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void smallchess_face_resign(movement_settings_t *settings, void *context);
#define smallchess_face ((const watch_face_t){ \
smallchess_face_setup, \
smallchess_face_activate, \
smallchess_face_loop, \
smallchess_face_resign, \
NULL, \
})
#endif // SMALLCHESS_FACE_H_

View File

@ -45,7 +45,7 @@ static void _sunrise_sunset_set_expiration(sunrise_sunset_state_t *state, watch_
}
static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_sunset_state_t *state) {
char buf[14];
char buf[11];
double rise, set, minutes, seconds;
bool show_next_match = false;
movement_location_t movement_location;
@ -87,7 +87,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
watch_clear_colon();
watch_clear_indicator(WATCH_INDICATOR_PM);
watch_clear_indicator(WATCH_INDICATOR_24H);
sprintf(buf, "%s%2d none ", (result == 1) ? "SE" : "rI", scratch_time.unit.day);
snprintf(buf, sizeof(buf), "%s%2d none ", (result == 1) ? "SE" : "rI", scratch_time.unit.day);
watch_display_string(buf, 0);
return;
}
@ -120,7 +120,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
} else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) {
set_leading_zero = true;
}
sprintf(buf, "rI%2d%2d%02d%s", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute,longLatPresets[state->longLatToUse].name);
snprintf(buf, sizeof(buf), "rI%2d%2d%02d%s", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute,longLatPresets[state->longLatToUse].name);
watch_display_string(buf, 0);
if (set_leading_zero)
watch_display_string("0", 4);
@ -152,7 +152,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
} else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) {
set_leading_zero = true;
}
sprintf(buf, "SE%2d%2d%02d%s", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute, longLatPresets[state->longLatToUse].name);
snprintf(buf, sizeof(buf), "SE%2d%2d%02d%s", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute, longLatPresets[state->longLatToUse].name);
watch_display_string(buf, 0);
if (set_leading_zero)
watch_display_string("0", 4);
@ -212,16 +212,16 @@ static void _sunrise_sunset_face_update_location_register(sunrise_sunset_state_t
}
static void _sunrise_sunset_face_update_settings_display(movement_event_t event, sunrise_sunset_state_t *state) {
char buf[12];
char buf[11];
switch (state->page) {
case 0:
return;
case 1:
sprintf(buf, "LA %c %04d", state->working_latitude.sign ? '-' : '+', abs(_sunrise_sunset_face_latlon_from_struct(state->working_latitude)));
snprintf(buf, sizeof(buf), "LA %c %04d", state->working_latitude.sign ? '-' : '+', abs(_sunrise_sunset_face_latlon_from_struct(state->working_latitude)));
break;
case 2:
sprintf(buf, "LO %c%05d", state->working_longitude.sign ? '-' : '+', abs(_sunrise_sunset_face_latlon_from_struct(state->working_longitude)));
snprintf(buf, sizeof(buf), "LO %c%05d", state->working_longitude.sign ? '-' : '+', abs(_sunrise_sunset_face_latlon_from_struct(state->working_longitude)));
break;
}
if (event.subsecond % 2) {

View File

@ -0,0 +1,234 @@
#include <stdlib.h>
#include <string.h>
#include "wareki_face.h"
#include "filesystem.h"
#include "watch_utility.h"
//Long press status flag
static bool _alarm_button_press;
static bool _light_button_press;
void wareki_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) watch_face_index;
//printf("wareki_setup() \n");
(void) settings;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(wareki_state_t));
memset(*context_ptr, 0, sizeof(wareki_state_t));
// Do any one-time tasks in here; the inside of this conditional happens only at boot.
//debug code
// watch_date_time datetime = watch_rtc_get_date_time();
// datetime.unit.year = 2022 - WATCH_RTC_REFERENCE_YEAR;
// datetime.unit.month = 12;
// datetime.unit.day = 31;
// datetime.unit.hour = 23;
// datetime.unit.minute= 59;
// datetime.unit.second= 30;
// watch_rtc_set_date_time(datetime);
// settings->bit.clock_mode_24h = true; //24時間表記
// settings->bit.to_interval = 1;//0=60sec 1=2m 2=5m 3=30m
// watch_store_backup_data(settings->reg, 0);
}
}
// splash view
static void draw_wareki_splash(wareki_state_t *state) {
(void) state;
char buf[11];
watch_clear_colon();
sprintf(buf, "%s","wa ------");
watch_display_string(buf, 0);
}
//draw year and Japanese wareki
static void draw_year_and_wareki(wareki_state_t *state) {
char buf[27];
if(state->disp_year < REIWA_GANNEN){
//Heisei
sprintf(buf, " h%2d%4d ", (int)state->disp_year - HEISEI_GANNEN + 1, (int)state->disp_year);
}
else{
//Reiwa
sprintf(buf, " r%2d%4d ", (int)state->disp_year - REIWA_GANNEN + 1 , (int)state->disp_year);
}
watch_display_string(buf, 0);
}
void wareki_activate(movement_settings_t *settings, void *context) {
//printf("wareki_activate() \n");
(void) settings;
wareki_state_t *state = (wareki_state_t *)context;
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
state->active = false;
_alarm_button_press = false;
_light_button_press = false;
state->real_year = watch_rtc_get_date_time().unit.year + WATCH_RTC_REFERENCE_YEAR;
state->start_year = state->real_year;
state->disp_year = state->real_year;
movement_request_tick_frequency(1);
}
void addYear(wareki_state_t* state,int count){
state->disp_year = state->disp_year + count;
if(state->disp_year > REIWA_LIMIT ){
state->disp_year = REIWA_LIMIT;
}
else{
//watch_buzzer_play_note(BUZZER_NOTE_C8, 30);
}
}
void subYear(wareki_state_t* state,int count){
state->disp_year = state->disp_year - count;
if(state->disp_year < 1989 ){
state->disp_year = 1989;
}
else{
//watch_buzzer_play_note(BUZZER_NOTE_C7, 30);
}
}
bool wareki_loop(movement_event_t event, movement_settings_t *settings, void *context) {
wareki_state_t *state = (wareki_state_t *)context;
state->real_year = watch_rtc_get_date_time().unit.year + WATCH_RTC_REFERENCE_YEAR;
if( state->real_year != state->start_year ){
state->start_year = state->real_year;
state->disp_year = state->real_year;
}
switch (event.event_type) {
case EVENT_ACTIVATE:
draw_wareki_splash(state);
break;
case EVENT_MODE_BUTTON_UP:
movement_move_to_next_face();
break;
case EVENT_LOW_ENERGY_UPDATE:
case EVENT_TICK:
//printf("tick %d\n",state->disp_year );
if (_alarm_button_press && watch_get_pin_level(BTN_ALARM)){
//printf("ALARM ON\n");
}
else{
//printf("ALARM OFF\n");
_alarm_button_press = false;
}
if (_light_button_press && watch_get_pin_level(BTN_LIGHT)){
//printf("LIGHT ON\n");
}
else{
//printf("LIGHT OFF\n");
_light_button_press = false;
}
if (_alarm_button_press) {
addYear(state,1);
}
if (_light_button_press) {
subYear(state,1);
}
draw_year_and_wareki(state);
break;
case EVENT_LIGHT_BUTTON_DOWN:
//printf("LIGHT DOWN\n");
subYear(state,1);
break;
case EVENT_LIGHT_LONG_PRESS:
//printf("LIGHTPRESS \n");
_light_button_press = true;
movement_request_tick_frequency(8);
break;
case EVENT_LIGHT_LONG_UP:
//printf("LIGHTPRESS UP\n");
_light_button_press = false;
movement_request_tick_frequency(4);
break;
case EVENT_LIGHT_BUTTON_UP:
//printf("LIGHT UP\n");
_light_button_press = false;
movement_request_tick_frequency(4);
break;
case EVENT_ALARM_BUTTON_DOWN:
//printf("ALARM DOWN\n");
addYear(state,1);
break;
case EVENT_ALARM_LONG_PRESS:
//printf("LONGPRESS \n");
_alarm_button_press = true;
movement_request_tick_frequency(8);
break;
case EVENT_ALARM_LONG_UP:
//printf("LONGPRESS UP\n");
_alarm_button_press = false;
movement_request_tick_frequency(4);
break;
case EVENT_ALARM_BUTTON_UP:
//printf("ALARM UP\n");
movement_request_tick_frequency(4);
break;
case EVENT_TIMEOUT:
//printf("time out ! \n");
movement_move_to_face(0);
break;
//case EVENT_LOW_ENERGY_UPDATE:
// If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
// Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
// You should also consider starting the tick animation, to show the wearer that this is sleep mode:
// watch_start_tick_animation(500);
//break;
default:
// Movement's default loop handler will step in for any cases you don't handle above:
// * EVENT_LIGHT_BUTTON_DOWN lights the LED
// * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
// * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
// You can override any of these behaviors by adding a case for these events to this switch statement.
return movement_default_loop_handler(event, settings);
}
return true;
}
void wareki_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}

View File

@ -0,0 +1,34 @@
#ifndef WAREKI_FACE_H_
#define WAREKI_FACE_H_
#include "movement.h"
#define REIWA_LIMIT 2018 + 31
#define REIWA_GANNEN 2019
#define HEISEI_GANNEN 1989
typedef struct {
bool active;
uint32_t disp_year; //Current displayed year
uint32_t start_year; //Year when this screen was launched
uint32_t real_year; //The actual current year
} wareki_state_t;
void wareki_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void wareki_activate(movement_settings_t *settings, void *context);
bool wareki_loop(movement_event_t event, movement_settings_t *settings, void *context);
void wareki_resign(movement_settings_t *settings, void *context);
void addYear(wareki_state_t* state,int count);
void subYear(wareki_state_t* state,int count);
#define wareki_face ((const watch_face_t){ \
wareki_setup, \
wareki_activate, \
wareki_loop, \
wareki_resign, \
NULL, \
})
#endif // WAREKI_FACE_H_

View File

@ -38,7 +38,7 @@ void beeps_face_setup(movement_settings_t *settings, uint8_t watch_face_index, v
void beeps_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
beeps_state_t *state = (beeps_state_t *)context;
(void) context;
}
static void _beep_face_update_lcd(beeps_state_t *state) {

View File

@ -94,6 +94,7 @@ static void _lis2dw_logging_face_update_display(movement_settings_t *settings, l
watch_display_string(buf, 0);
if (set_leading_zero)
watch_display_string("0", 4);
printf("%s\n", buf);
}
static void _lis2dw_logging_face_log_data(lis2dw_logger_state_t *logger_state) {
@ -142,7 +143,7 @@ void lis2dw_logging_face_activate(movement_settings_t *settings, void *context)
logger_state->display_index = 0;
logger_state->log_ticks = 0;
watch_enable_digital_input(A0);
watch_enable_digital_input(A4);
}
bool lis2dw_logging_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
@ -196,7 +197,7 @@ bool lis2dw_logging_face_loop(movement_event_t event, movement_settings_t *setti
void lis2dw_logging_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
watch_disable_digital_input(A0);
watch_disable_digital_input(A4);
}
bool lis2dw_logging_face_wants_background_task(movement_settings_t *settings, void *context) {

View File

@ -0,0 +1,162 @@
/*
* MIT License
*
* Copyright (c) 2022 Joey Castillo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "accel_interrupt_count_face.h"
#include "lis2dw.h"
#include "watch.h"
// hacky hacky!
uint32_t *ptr_to_count = 0;
void accel_interrupt_handler(void);
void accel_interrupt_handler(void) {
(*ptr_to_count)++;
}
static void _accel_interrupt_count_face_update_display(accel_interrupt_count_state_t *state) {
char buf[11];
if (state->running) {
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
} else {
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
}
// "AC"celerometer "IN"terrupts
snprintf(buf, 11, "AC1N%6ld", state->count);
watch_display_string(buf, 0);
printf("%s\n", buf);
}
static void _accel_interrupt_count_face_configure_threshold(uint8_t threshold) {
lis2dw_configure_wakeup_int1(threshold, false, true);
}
void accel_interrupt_count_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(accel_interrupt_count_state_t));
memset(*context_ptr, 0, sizeof(accel_interrupt_count_state_t));
ptr_to_count = &((accel_interrupt_count_state_t *)*context_ptr)->count;
watch_enable_i2c();
lis2dw_begin();
lis2dw_set_low_power_mode(LIS2DW_LP_MODE_2); // lowest power 14-bit mode, 25 Hz is 3.5 µA @ 1.8V w/ low noise, 3µA without
lis2dw_set_low_noise_mode(true); // consumes a little more power
lis2dw_set_range(LIS2DW_CTRL6_VAL_RANGE_4G);
lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ); // is this enough?
// threshold is 1/64th of full scale, so for a FS of ±4G this is 1.25G
((accel_interrupt_count_state_t *)*context_ptr)->threshold = 10;
_accel_interrupt_count_face_configure_threshold(((accel_interrupt_count_state_t *)*context_ptr)->threshold);
}
}
void accel_interrupt_count_face_activate(movement_settings_t *settings, void *context) {
accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)context;
// never in settings mode at the start
state->is_setting = false;
// force LE interval to never sleep
settings->bit.le_interval = 0;
}
bool accel_interrupt_count_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)context;
if (state->is_setting) {
switch (event.event_type) {
case EVENT_LIGHT_BUTTON_DOWN:
state->new_threshold = (state->new_threshold + 1) % 64;
// fall through
case EVENT_TICK:
{
char buf[11];
snprintf(buf, 11, "TH %4d ", state->new_threshold);
watch_display_string(buf, 0);
printf("%s\n", buf);
}
break;
case EVENT_ALARM_BUTTON_UP:
lis2dw_configure_wakeup_int1(state->threshold, false, true);
state->threshold = state->new_threshold;
state->is_setting = false;
break;
default:
movement_default_loop_handler(event, settings);
break;
}
} else {
switch (event.event_type) {
case EVENT_LIGHT_BUTTON_DOWN:
movement_illuminate_led();
// if stopped, reset the count
if (!state->running) {
state->count = 0;
}
_accel_interrupt_count_face_update_display(state);
break;
case EVENT_ALARM_BUTTON_UP:
if (state->running) {
state->running = false;
watch_register_interrupt_callback(A4, NULL, INTERRUPT_TRIGGER_RISING);
} else {
state->running = true;
watch_register_interrupt_callback(A4, accel_interrupt_handler, INTERRUPT_TRIGGER_RISING);
}
_accel_interrupt_count_face_update_display(state);
break;
case EVENT_ACTIVATE:
case EVENT_TICK:
_accel_interrupt_count_face_update_display(state);
break;
case EVENT_ALARM_LONG_PRESS:
if (!state->running) {
state->new_threshold = state->threshold;
state->is_setting = true;
}
return false;
default:
movement_default_loop_handler(event, settings);
break;
}
}
return true;
}
void accel_interrupt_count_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}
bool accel_interrupt_count_face_wants_background_task(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
return false;
}

View File

@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) 2022 Joey Castillo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
/*
* Accelerometer Interrupt Counter
*
* This is an experimental watch face for counting the number of interrupts that
* the Sensor Watch Motion acceleromoeter board fires. I expect it will be removed
* once we integrate accelerometer functionality more deeply into Movement.
*/
#include "movement.h"
#include "watch.h"
typedef struct {
uint32_t count;
uint8_t new_threshold;
uint8_t threshold;
bool running;
bool is_setting;
} accel_interrupt_count_state_t;
void accel_interrupt_count_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void accel_interrupt_count_face_activate(movement_settings_t *settings, void *context);
bool accel_interrupt_count_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void accel_interrupt_count_face_resign(movement_settings_t *settings, void *context);
bool accel_interrupt_count_face_wants_background_task(movement_settings_t *settings, void *context);
#define accel_interrupt_count_face ((const watch_face_t){ \
accel_interrupt_count_face_setup, \
accel_interrupt_count_face_activate, \
accel_interrupt_count_face_loop, \
accel_interrupt_count_face_resign, \
accel_interrupt_count_face_wants_background_task, \
})

View File

@ -0,0 +1,154 @@
/*
* MIT License
*
* Copyright (c) 2023 Mark Blyth
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "minmax_face.h"
#include "thermistor_driver.h"
#include "watch.h"
static float _get_displayed_temperature_c(minmax_state_t *state){
float min_temp = 1000;
float max_temp = -1000;
for(int i = 0; i < LOGGING_DATA_POINTS; i++){
if(state->hourly_maxs[i] > max_temp){
max_temp = state->hourly_maxs[i];
}
if(state->hourly_mins[i] < min_temp){
min_temp = state->hourly_mins[i];
}
}
if(state->show_min) return min_temp;
return max_temp;
}
static void _minmax_face_log_data(minmax_state_t *logger_state) {
thermistor_driver_enable();
size_t pos = (size_t) watch_rtc_get_date_time().unit.hour;
float temp_c = thermistor_driver_get_temperature();
// If no data yet, initialise with current temperature
if(!logger_state->have_logged){
logger_state->have_logged = true;
for(int i=0; i<LOGGING_DATA_POINTS; i++){
logger_state->hourly_mins[i] = temp_c;
logger_state->hourly_maxs[i] = temp_c;
}
}
// On new hour, update lists to current temperature
else if(watch_rtc_get_date_time().unit.minute < 2){
logger_state->hourly_mins[pos] = temp_c;
logger_state->hourly_maxs[pos] = temp_c;
}
// Log hourly highs and lows
else if(logger_state->hourly_mins[pos] > temp_c){
logger_state->hourly_mins[pos] = temp_c;
}
else if(logger_state->hourly_maxs[pos] < temp_c){
logger_state->hourly_maxs[pos] = temp_c;
}
thermistor_driver_disable();
}
static void _minmax_face_update_display(float temperature_c, bool in_fahrenheit) {
char buf[14];
if (in_fahrenheit) {
sprintf(buf, "%4.0f#F", temperature_c * 1.8 + 32.0);
} else {
sprintf(buf, "%4.0f#C", temperature_c);
}
watch_display_string(buf, 4);
}
void minmax_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(minmax_state_t));
memset(*context_ptr, 0, sizeof(minmax_state_t));
}
}
void minmax_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
minmax_state_t *state = (minmax_state_t *)context;
state->show_min = true;
watch_display_string("MN", 0); // Start with minimum temp
}
bool minmax_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
minmax_state_t *state = (minmax_state_t *)context;
float temp_c;
switch (event.event_type) {
case EVENT_ACTIVATE:
temp_c = _get_displayed_temperature_c(state);
_minmax_face_update_display(temp_c, settings->bit.use_imperial_units);
break;
case EVENT_LIGHT_LONG_PRESS:
settings->bit.use_imperial_units = !settings->bit.use_imperial_units;
temp_c = _get_displayed_temperature_c(state);
_minmax_face_update_display(temp_c, settings->bit.use_imperial_units);
break;
case EVENT_ALARM_BUTTON_UP:
state->show_min = !state->show_min;
if(state->show_min){
watch_display_string("MN", 0);
} else {
watch_display_string("MX", 0);
}
temp_c = _get_displayed_temperature_c(state);
_minmax_face_update_display(temp_c, settings->bit.use_imperial_units);
break;
case EVENT_TIMEOUT:
movement_move_to_face(0);
break;
case EVENT_BACKGROUND_TASK:
_minmax_face_log_data(state);
break;
default:
return movement_default_loop_handler(event, settings);
}
return true;
}
void minmax_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}
bool minmax_face_wants_background_task(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
// this will get called at the top of each minute; always request bg task
return true;
}

View File

@ -0,0 +1,69 @@
/*
* MIT License
*
* Copyright (c) 2023 Mark Blyth
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef MINMAX_FACE_H_
#define MINMAX_FACE_H_
#include "movement.h"
#include "watch.h"
#define LOGGING_DATA_POINTS (24)
/*
* Log for the min. and max. temperature over the last 24h.
*
* Temperature is logged once a minute, every minute. Results are
* stored, noting the highest and lowest temperatures observed within
* any given hour. The watch face then displays the minimum or maximum
* temperature recorded over the last 24h.
*
* A long press of the light button changes units between Celsius and
* Fahrenheit. Pressing the alarm button switches between displaying the
* minimum and maximum observed temperatures. If no buttons are pressed,
* the watch face will eventually time out and return home.
*/
typedef struct {
bool show_min;
bool have_logged;
float hourly_mins[LOGGING_DATA_POINTS];
float hourly_maxs[LOGGING_DATA_POINTS];
} minmax_state_t;
void minmax_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void minmax_face_activate(movement_settings_t *settings, void *context);
bool minmax_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void minmax_face_resign(movement_settings_t *settings, void *context);
bool minmax_face_wants_background_task(movement_settings_t *settings, void *context);
#define minmax_face ((const watch_face_t){ \
minmax_face_setup, \
minmax_face_activate, \
minmax_face_loop, \
minmax_face_resign, \
minmax_face_wants_background_task, \
})
#endif // MINMAX_FACE_H_

View File

@ -97,6 +97,16 @@ void watch_enable_analog_input(const uint8_t pin) {
case A4:
gpio_set_pin_function(pin, PINMUX_PB00B_ADC_AIN8);
break;
#ifdef TEMPSENSE
case TEMPSENSE:
gpio_set_pin_function(pin, PINMUX_PA03B_ADC_AIN1);
break;
#endif
#ifdef IRSENSE
case IRSENSE:
gpio_set_pin_function(pin, PINMUX_PA04B_ADC_AIN4);
break;
#endif
default:
return;
}
@ -114,7 +124,15 @@ uint16_t watch_get_analog_pin_level(const uint8_t pin) {
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN11_Val);
case A4:
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN8_Val);
default:
#ifdef TEMPSENSE
case TEMPSENSE:
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN1_Val);
#endif
#ifdef IRSENSE
case IRSENSE:
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN4_Val);
#endif
default:
return 0;
}
}