Compare commits
305 Commits
theAlexes/
...
moonrise
Author | SHA1 | Date | |
---|---|---|---|
|
49181dadde | ||
|
8abf6f4f5b | ||
|
8c39d42824 | ||
|
56f2c93783 | ||
|
14e64caac9 | ||
|
cc6a0c363e | ||
|
845caa30fb | ||
|
0ef5e5700e | ||
|
e8f31beb70 | ||
|
c2103d9eaa | ||
|
88338dc0ba | ||
|
e8ba597131 | ||
|
52c3d5b796 | ||
|
7af5626147 | ||
|
b2d313e0e7 | ||
|
0f5defe789 | ||
|
3634460a02 | ||
|
fc2f9c5130 | ||
|
bf4d461d8c | ||
|
fe9a0a693d | ||
|
30267dfc0c | ||
|
a9d503b807 | ||
|
683032219e | ||
|
ea5efb4d82 | ||
|
f2479bee4f | ||
|
831aadddea | ||
|
5926e81d25 | ||
|
035fff52da | ||
|
c9cbb82163 | ||
|
3c86a42aa8 | ||
|
1462d18376 | ||
|
be969c4deb | ||
|
673f90b724 | ||
|
44b543421d | ||
|
0cc28b9811 | ||
|
9d1410780c | ||
|
0d16329574 | ||
|
f6f427ca7d | ||
|
22c25f5c23 | ||
|
a539bd7da7 | ||
|
7a4424b2d4 | ||
|
e4e0b611f3 | ||
|
6b7ea8bbaa | ||
|
531bcfd285 | ||
|
137906e14a | ||
|
337864eb54 | ||
|
0f6f79a85a | ||
|
c2723ebc8e | ||
|
ac5bf8cfce | ||
|
608199268e | ||
|
c02ec307ff | ||
|
002f5bc242 | ||
|
5ae88e438d | ||
|
5a8a49a8c7 | ||
![]() |
52f9500666 | ||
|
bfadb81e82 | ||
|
6d1d4f0594 | ||
|
1f6153809b | ||
|
941a6ec523 | ||
|
99586f8442 | ||
|
b9dbc4ed21 | ||
|
543788b622 | ||
|
7de53f9fa0 | ||
|
ce249abde6 | ||
|
c805cdc7df | ||
|
9e1e692511 | ||
|
50cff54833 | ||
|
97ad12d939 | ||
|
e2ec468754 | ||
|
6db0a62bbf | ||
|
93b9ca6341 | ||
|
27ab799e85 | ||
|
c89316b3ec | ||
|
fab8c94428 | ||
|
c102a10165 | ||
|
4cb00ebb4e | ||
|
fea60e615c | ||
|
07a2a49e72 | ||
|
852c3aad27 | ||
|
02e66309d4 | ||
|
ddaf3a8324 | ||
|
80dc8a247f | ||
|
a79bb46d39 | ||
|
3462d51071 | ||
|
48fd4ee903 | ||
|
b69cd117f9 | ||
|
fd526ed401 | ||
|
b37be89bae | ||
|
014ef32576 | ||
|
c0a72acb7c | ||
|
cd4b285c6e | ||
|
4d6a9345f2 | ||
|
118c07a3b6 | ||
|
faec45ce24 | ||
|
2e46aa0e2c | ||
|
c74ed78d72 | ||
|
e4a5121303 | ||
|
28b14d3665 | ||
|
324942009e | ||
|
30363d408e | ||
|
c027b247b2 | ||
|
503fcd6ebc | ||
|
6ec6476d0f | ||
|
07d2bc91a5 | ||
|
2d7aaceff7 | ||
|
6f3f09c5ba | ||
|
defd01f9f0 | ||
|
abc0bedbde | ||
|
ed3c4d3c30 | ||
|
e2870eb7af | ||
|
73a975d0d9 | ||
|
4dedcb3a6d | ||
|
dd719183cf | ||
|
5435bc7f34 | ||
|
d1c19166a1 | ||
|
c43820e75d | ||
|
41df6c113f | ||
|
b364a6cfab | ||
|
8205abe5be | ||
|
255ea97cc4 | ||
|
4b8bd61408 | ||
|
c87e814140 | ||
|
733318c036 | ||
|
d98f749f3b | ||
|
fdff6f581a | ||
|
1b887aea2b | ||
|
b58d6c0a2e | ||
|
8342fef84f | ||
|
0d16d126cd | ||
|
5149e7e1dd | ||
|
1d2fb20e99 | ||
|
099f78443e | ||
|
7f38f8e416 | ||
|
1a1560b59d | ||
|
0d58f0d77d | ||
|
af6f6002ba | ||
|
a0ffd0ca7f | ||
|
7ceb682675 | ||
|
cb57ef237d | ||
|
ce31db3712 | ||
|
1868f8446a | ||
|
57ca74b253 | ||
|
935ede9fda | ||
|
4257b71562 | ||
|
67c1089fb2 | ||
|
a0111fbe24 | ||
|
2a10402d19 | ||
|
4bb4bc85fa | ||
|
1e76022146 | ||
|
1675af6449 | ||
|
6dd46b46b1 | ||
|
10eda8b208 | ||
|
580f8bf8ee | ||
|
02f6a3256c | ||
|
3e327eb7fd | ||
|
cef0d8836a | ||
|
8ea779874f | ||
|
0c86be4a40 | ||
|
676f50d194 | ||
|
3a24ede3de | ||
|
ee53e83ae7 | ||
|
6bf22edbdc | ||
|
2e878e146c | ||
|
d4bd10ba5e | ||
|
a67076f437 | ||
|
23c422b27a | ||
|
a2e5417de9 | ||
|
b774900ae6 | ||
|
696f7f12ec | ||
|
6268ce4381 | ||
|
12b1432aae | ||
|
6f7693e880 | ||
|
20946e88f4 | ||
|
70426751f8 | ||
|
c75a21196f | ||
|
b607b6f7a9 | ||
|
e13d42b5b5 | ||
|
28db77f90c | ||
|
18154deef4 | ||
|
fa0cdef45b | ||
|
879c48ce4d | ||
|
db4097bf84 | ||
|
dea05663b0 | ||
|
663cd725f8 | ||
|
c8ca0d3619 | ||
|
95ca374c98 | ||
|
657ff724d0 | ||
|
42dc15117e | ||
|
a715265af6 | ||
|
9c093f9540 | ||
|
c7413322a5 | ||
|
c8a87d3b7c | ||
|
dd04443413 | ||
|
cae5d8a33f | ||
|
2a194dfa69 | ||
|
77fb6202c9 | ||
|
fe259ee526 | ||
|
01940449b6 | ||
|
d5a8c57c82 | ||
|
c6f2bff75e | ||
|
9640f452cd | ||
|
f02e73c1c4 | ||
|
a4fc048f94 | ||
|
e2c5babb2c | ||
|
dcd4d12c0a | ||
|
20b4a32835 | ||
|
e9837ff0cb | ||
|
2cdfa2d3b3 | ||
|
9861da84c3 | ||
|
09576807eb | ||
|
f85a7f2c78 | ||
|
1da9d0aefe | ||
|
0ad5fa4733 | ||
|
44bdc8503e | ||
|
ca1da33b82 | ||
|
7000d08ba5 | ||
|
598e876186 | ||
|
2824a62908 | ||
|
7a15ed3591 | ||
|
51176344dc | ||
|
84f0db0654 | ||
|
6ae5dfef70 | ||
|
aebea960c0 | ||
|
e50390b673 | ||
|
74421c7e65 | ||
|
fa2907e098 | ||
|
db165bec30 | ||
|
ccf99a9727 | ||
|
bae8c65825 | ||
|
4c546b14dc | ||
|
149911e4ad | ||
|
e732afbec6 | ||
|
278520bfaf | ||
|
7bbac4cd80 | ||
|
027e42dc58 | ||
|
e297b3013e | ||
|
607946ed2e | ||
|
f7d1b8f9f3 | ||
|
7f2ac61375 | ||
|
73c3ba3ae7 | ||
|
36117ca207 | ||
|
9727dac3c3 | ||
|
947e299494 | ||
|
4a4fce428e | ||
|
df2dac5a07 | ||
|
7d5aaf60ca | ||
|
4375ca37e0 | ||
|
a9e6b82f00 | ||
|
6e26c01de0 | ||
|
cb90a1980f | ||
|
42c14c2e5b | ||
|
c8702d346e | ||
|
8aa1a3f8fa | ||
|
222c60b072 | ||
|
5f1a651732 | ||
|
a13c8c35b8 | ||
|
9ee319c888 | ||
|
a3d23ab45e | ||
|
a7c0fb7dfd | ||
|
2afc2c6721 | ||
|
5ec3dca9e4 | ||
|
ac5fb40730 | ||
|
7f6a9e5c9b | ||
|
b923d50652 | ||
|
5f5d86353f | ||
|
3eaf807590 | ||
|
d98d14d236 | ||
|
89a2af92ef | ||
|
226cda748c | ||
|
96fdf64c4c | ||
|
7a2ecad334 | ||
|
af0f8d2732 | ||
|
53f11cbd1e | ||
|
fa35b8bb77 | ||
|
9794f86430 | ||
![]() |
2ce07f9539 | ||
![]() |
e3d67af604 | ||
|
77ea1b8965 | ||
|
439843f56b | ||
|
45beca19e8 | ||
|
524098b925 | ||
|
d2b5ed7155 | ||
|
1e29fe85e5 | ||
|
c22123961f | ||
|
69f25f1016 | ||
|
0e70adf4d4 | ||
|
10701f3d50 | ||
|
7e5c34773a | ||
|
3f850d79c8 | ||
|
df38c262b8 | ||
|
40b71400c5 | ||
|
7eb725a5f6 | ||
|
f633b7634b | ||
|
4763568900 | ||
|
30195a2619 | ||
|
2164593484 | ||
|
c15b01cd4c | ||
|
57e9f11a0c | ||
|
82ed355aba | ||
|
56f76b8c6a | ||
![]() |
f3c28ede96 | ||
|
43e94ca0f2 | ||
|
b147ac0c67 | ||
|
739ad64cc1 | ||
|
a831ed3336 |
@ -1,6 +1,4 @@
|
||||
FROM ubuntu:22.10
|
||||
|
||||
# TODO: install emscripten (https://emscripten.org/docs/getting_started/downloads.html)
|
||||
FROM ubuntu:24.04
|
||||
|
||||
# TODO: Clean this up once buildkit is supported gracefully in devcontainers
|
||||
# https://github.com/microsoft/vscode-remote-release/issues/1409
|
||||
@ -27,7 +25,9 @@ RUN apt-get update \
|
||||
# ca certs need to be available for fetching git submodules
|
||||
ca-certificates \
|
||||
# python is used to convert binaries to uf2 files
|
||||
python3 python-is-python3
|
||||
python3 python-is-python3 \
|
||||
# emscripten for building simulator
|
||||
emscripten
|
||||
|
||||
# Download and verify both x86-64 and aarch64 toolchains. This is unfortunate and
|
||||
# slows down the build, but it's a clean-ish option until buildkit can be used.
|
||||
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
run: make
|
||||
working-directory: 'movement/make'
|
||||
- name: Upload UF2
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: watch.uf2
|
||||
path: movement/make/build/watch.uf2
|
||||
@ -52,7 +52,7 @@ jobs:
|
||||
cp watch.html index.html
|
||||
tar -czf simulator.tar.gz index.html watch.wasm watch.js
|
||||
- name: Upload simulator build
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: simulator.tar.gz
|
||||
path: movement/make/build-sim/simulator.tar.gz
|
||||
|
34
PCB/Main Boards/OSO-SWAT-C1/ELT-3KN004B.wrl
Normal file
34
PCB/Main Boards/OSO-SWAT-C1/ELT-3KN004B.wrl
Normal file
File diff suppressed because one or more lines are too long
12383
PCB/Main Boards/OSO-SWAT-C1/EVP-BB4A9B000--3DModel-STEP-56544.STEP
Normal file
12383
PCB/Main Boards/OSO-SWAT-C1/EVP-BB4A9B000--3DModel-STEP-56544.STEP
Normal file
File diff suppressed because it is too large
Load Diff
8784
PCB/Main Boards/OSO-SWAT-C1/FH19C-9S-0.5SH.stp
Normal file
8784
PCB/Main Boards/OSO-SWAT-C1/FH19C-9S-0.5SH.stp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
2373
PCB/Main Boards/OSO-SWAT-C1/NX3215SA.step
Normal file
2373
PCB/Main Boards/OSO-SWAT-C1/NX3215SA.step
Normal file
File diff suppressed because it is too large
Load Diff
6938
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-A1-05-eagle-import.kicad_sym
Normal file
6938
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-A1-05-eagle-import.kicad_sym
Normal file
File diff suppressed because it is too large
Load Diff
BIN
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-06.zip
Normal file
BIN
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-06.zip
Normal file
Binary file not shown.
93996
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-rounded.kicad_pcb
Normal file
93996
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-rounded.kicad_pcb
Normal file
File diff suppressed because it is too large
Load Diff
83
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-rounded.kicad_prl
Normal file
83
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-rounded.kicad_prl
Normal 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": []
|
||||
}
|
||||
}
|
692
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-rounded.kicad_pro
Normal file
692
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1-rounded.kicad_pro
Normal 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": {}
|
||||
}
|
1
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_dru
Normal file
1
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_dru
Normal file
@ -0,0 +1 @@
|
||||
(version 1)
|
90383
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_pcb
Normal file
90383
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_pcb
Normal file
File diff suppressed because it is too large
Load Diff
83
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_prl
Normal file
83
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_prl
Normal 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": []
|
||||
}
|
||||
}
|
681
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_pro
Normal file
681
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_pro
Normal 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": {}
|
||||
}
|
15111
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_sch
Normal file
15111
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
154
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.pretty/ELT3KN.kicad_mod
Normal file
154
PCB/Main Boards/OSO-SWAT-C1/OSO-SWAT-C1.pretty/ELT3KN.kicad_mod
Normal 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")
|
||||
)
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -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")
|
||||
)
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
@ -0,0 +1,4 @@
|
||||
Default True 2.0 3
|
||||
gnd True 2.0 3
|
||||
power True 2.0 3
|
||||
True True False
|
@ -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))
|
||||
)
|
@ -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))
|
||||
)
|
44
PCB/Main Boards/OSO-SWAT-C1/OSO_SWD.kicad_sym
Normal file
44
PCB/Main Boards/OSO-SWAT-C1/OSO_SWD.kicad_sym
Normal 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))))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
10807
PCB/Main Boards/OSO-SWAT-C1/QBLP655_QBLP655R_BI_TRI.step
Normal file
10807
PCB/Main Boards/OSO-SWAT-C1/QBLP655_QBLP655R_BI_TRI.step
Normal file
File diff suppressed because it is too large
Load Diff
2612
PCB/Main Boards/OSO-SWAT-C1/contact_adjusted_angle.step
Normal file
2612
PCB/Main Boards/OSO-SWAT-C1/contact_adjusted_angle.step
Normal file
File diff suppressed because it is too large
Load Diff
5
PCB/Main Boards/OSO-SWAT-C1/empty.kicad_wks
Normal file
5
PCB/Main Boards/OSO-SWAT-C1/empty.kicad_wks
Normal 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))
|
||||
)
|
4
PCB/Main Boards/OSO-SWAT-C1/fp-lib-table
Normal file
4
PCB/Main Boards/OSO-SWAT-C1/fp-lib-table
Normal 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 ""))
|
||||
)
|
4
PCB/Main Boards/OSO-SWAT-C1/sym-lib-table
Normal file
4
PCB/Main Boards/OSO-SWAT-C1/sym-lib-table
Normal 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 ""))
|
||||
)
|
11
README.md
11
README.md
@ -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).
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
const int8_t UTC_OFFSET = 4; // set to your current UTC offset to see correct beats time
|
||||
const uint8_t BEAT_REFRESH_FREQUENCY = 8;
|
||||
@ -203,7 +204,6 @@ void set_time_mode_handle_primary_button(void) {
|
||||
|
||||
void set_time_mode_handle_secondary_button(void) {
|
||||
watch_date_time date_time = watch_rtc_get_date_time();
|
||||
const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};
|
||||
|
||||
switch (application_state.page) {
|
||||
case 0: // hour
|
||||
@ -224,13 +224,10 @@ void set_time_mode_handle_secondary_button(void) {
|
||||
break;
|
||||
case 5: // day
|
||||
date_time.unit.day = date_time.unit.day + 1;
|
||||
// can't set to the 29th on a leap year. if it's february 29, set to 11:59 on the 28th.
|
||||
// and it should roll over.
|
||||
if (date_time.unit.day > days_in_month[date_time.unit.month - 1]) {
|
||||
date_time.unit.day = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (date_time.unit.day > days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR))
|
||||
date_time.unit.day = 1;
|
||||
watch_rtc_set_date_time(date_time);
|
||||
}
|
||||
|
||||
|
64
apps/pro-rainbow-test/app.c
Normal file
64
apps/pro-rainbow-test/app.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "watch.h"
|
||||
|
||||
void app_init(void) {
|
||||
}
|
||||
|
||||
void app_wake_from_backup(void) {
|
||||
}
|
||||
|
||||
void app_setup(void) {
|
||||
watch_enable_leds();
|
||||
}
|
||||
|
||||
void app_prepare_for_standby(void) {
|
||||
}
|
||||
|
||||
void app_wake_from_standby(void) {
|
||||
}
|
||||
|
||||
bool app_loop(void) {
|
||||
static uint8_t red = 0;
|
||||
static uint8_t green = 0;
|
||||
static uint8_t blue = 255;
|
||||
static uint8_t phase = 0;
|
||||
|
||||
switch (phase) {
|
||||
case 0:
|
||||
red++;
|
||||
if (red == 255) phase = 1;
|
||||
break;
|
||||
case 1:
|
||||
green++;
|
||||
if (green == 255) phase = 2;
|
||||
break;
|
||||
case 2:
|
||||
red--;
|
||||
if (red == 0) phase = 3;
|
||||
break;
|
||||
case 3:
|
||||
blue++;
|
||||
if (blue == 255) phase = 4;
|
||||
break;
|
||||
case 4:
|
||||
green--;
|
||||
if (green == 0) phase = 5;
|
||||
break;
|
||||
case 5:
|
||||
red++;
|
||||
if (red == 255) phase = 6;
|
||||
break;
|
||||
case 6:
|
||||
blue--;
|
||||
if (blue == 0) {
|
||||
phase = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
watch_set_led_color_rgb(red, green, blue);
|
||||
delay_ms(2);
|
||||
|
||||
return false;
|
||||
}
|
10
apps/pro-rainbow-test/make/Makefile
Executable file
10
apps/pro-rainbow-test/make/Makefile
Executable file
@ -0,0 +1,10 @@
|
||||
TOP = ../../..
|
||||
include $(TOP)/make.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I../
|
||||
|
||||
SRCS += \
|
||||
../app.c
|
||||
|
||||
include $(TOP)/rules.mk
|
420
apps/sensor-watch-pro-test/app.c
Normal file
420
apps/sensor-watch-pro-test/app.c
Normal 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;
|
||||
}
|
10
apps/sensor-watch-pro-test/make/Makefile
Executable file
10
apps/sensor-watch-pro-test/make/Makefile
Executable file
@ -0,0 +1,10 @@
|
||||
TOP = ../../..
|
||||
include $(TOP)/make.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I../
|
||||
|
||||
SRCS += \
|
||||
../app.c
|
||||
|
||||
include $(TOP)/rules.mk
|
@ -120,4 +120,8 @@
|
||||
#define D0 GPIO(GPIO_PORTB, 3)
|
||||
#define D1 GPIO(GPIO_PORTB, 0)
|
||||
|
||||
// interrupt mapping
|
||||
#define EXT_IRQ_AMOUNT 6
|
||||
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {5, PIN_PB05}, {7, PIN_PA07},
|
||||
|
||||
#endif // PINS_H_INCLUDED
|
||||
|
@ -81,4 +81,8 @@
|
||||
#define D0 GPIO(GPIO_PORTB, 3)
|
||||
#define D1 GPIO(GPIO_PORTB, 0)
|
||||
|
||||
// interrupt mapping
|
||||
#define EXT_IRQ_AMOUNT 6
|
||||
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {6, PIN_PA22}, {7, PIN_PA23},
|
||||
|
||||
#endif // PINS_H_INCLUDED
|
||||
|
@ -81,4 +81,8 @@
|
||||
#define D0 GPIO(GPIO_PORTB, 3)
|
||||
#define D1 GPIO(GPIO_PORTB, 0)
|
||||
|
||||
// interrupt mapping
|
||||
#define EXT_IRQ_AMOUNT 6
|
||||
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {6, PIN_PA22}, {7, PIN_PA23},
|
||||
|
||||
#endif // PINS_H_INCLUDED
|
||||
|
@ -81,4 +81,8 @@
|
||||
#define D0 GPIO(GPIO_PORTB, 3)
|
||||
#define D1 GPIO(GPIO_PORTB, 0)
|
||||
|
||||
// interrupt mapping
|
||||
#define EXT_IRQ_AMOUNT 6
|
||||
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {6, PIN_PA22}, {7, PIN_PA23},
|
||||
|
||||
#endif // PINS_H_INCLUDED
|
||||
|
130
boards/OSO-SWAT-C1-00/pins.h
Normal file
130
boards/OSO-SWAT-C1-00/pins.h
Normal file
@ -0,0 +1,130 @@
|
||||
#ifndef PINS_H_INCLUDED
|
||||
#define PINS_H_INCLUDED
|
||||
|
||||
// Detects if we are on USB power.
|
||||
#define VBUS_DET GPIO(GPIO_PORTB, 5)
|
||||
|
||||
// Buttons
|
||||
#define BTN_ALARM GPIO(GPIO_PORTA, 2)
|
||||
#define WATCH_BTN_ALARM_EIC_CHANNEL 2
|
||||
#define BTN_LIGHT GPIO(GPIO_PORTA, 30)
|
||||
#define WATCH_BTN_LIGHT_EIC_CHANNEL 10
|
||||
#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
|
||||
#define WATCH_BUZZER_TCC_CHANNEL 1
|
||||
|
||||
// LEDs
|
||||
#define WATCH_INVERT_LED_POLARITY
|
||||
|
||||
#define RED GPIO(GPIO_PORTA, 12)
|
||||
#define WATCH_RED_TCC_CHANNEL 2
|
||||
#define WATCH_RED_TCC_PINMUX PINMUX_PA12F_TCC0_WO6
|
||||
|
||||
#define BLUE GPIO(GPIO_PORTA, 13)
|
||||
#define WATCH_BLUE_TCC_CHANNEL 3
|
||||
#define WATCH_BLUE_TCC_PINMUX PINMUX_PA13F_TCC0_WO7
|
||||
|
||||
#define GREEN GPIO(GPIO_PORTA, 22)
|
||||
#define WATCH_GREEN_TCC_CHANNEL 0
|
||||
#define WATCH_GREEN_TCC_PINMUX PINMUX_PA22F_TCC0_WO4
|
||||
|
||||
// Segment LCD
|
||||
#define SLCD0 GPIO(GPIO_PORTB, 6)
|
||||
#define SLCD1 GPIO(GPIO_PORTB, 7)
|
||||
#define SLCD2 GPIO(GPIO_PORTB, 8)
|
||||
#define SLCD3 GPIO(GPIO_PORTB, 9)
|
||||
#define SLCD4 GPIO(GPIO_PORTA, 5)
|
||||
#define SLCD5 GPIO(GPIO_PORTA, 6)
|
||||
#define SLCD6 GPIO(GPIO_PORTA, 7)
|
||||
#define SLCD7 GPIO(GPIO_PORTA, 8)
|
||||
#define SLCD8 GPIO(GPIO_PORTA, 9)
|
||||
#define SLCD9 GPIO(GPIO_PORTA, 10)
|
||||
#define SLCD10 GPIO(GPIO_PORTA, 11)
|
||||
#define SLCD11 GPIO(GPIO_PORTB, 11)
|
||||
#define SLCD12 GPIO(GPIO_PORTB, 12)
|
||||
#define SLCD13 GPIO(GPIO_PORTB, 13)
|
||||
#define SLCD14 GPIO(GPIO_PORTB, 14)
|
||||
#define SLCD15 GPIO(GPIO_PORTB, 15)
|
||||
#define SLCD16 GPIO(GPIO_PORTA, 14)
|
||||
#define SLCD17 GPIO(GPIO_PORTA, 15)
|
||||
#define SLCD18 GPIO(GPIO_PORTA, 16)
|
||||
#define SLCD19 GPIO(GPIO_PORTA, 17)
|
||||
#define SLCD20 GPIO(GPIO_PORTA, 18)
|
||||
#define SLCD21 GPIO(GPIO_PORTA, 19)
|
||||
#define SLCD22 GPIO(GPIO_PORTB, 16)
|
||||
#define SLCD23 GPIO(GPIO_PORTB, 17)
|
||||
#define SLCD24 GPIO(GPIO_PORTA, 20)
|
||||
#define SLCD25 GPIO(GPIO_PORTA, 21)
|
||||
#define SLCD26 GPIO(GPIO_PORTA, 23)
|
||||
// This board uses a slightly different pin mapping from the standard watch, and it's not enough to
|
||||
// just declare the pins. We also have to set the LCD Pin Enable register with the SLCD pins we're
|
||||
// using. These numbers are not port/pin numbers, but the "SLCD/LP[x]" numbers in the pinmux table.
|
||||
// If not defined in pins.h, the LCD driver will fall back to the pin mapping in hpl_slcd_config.h.
|
||||
// LPENL is for pins SLCD/LP[0..31].
|
||||
#define CONF_SLCD_LPENL (\
|
||||
(uint32_t)1 << 0 | \
|
||||
(uint32_t)1 << 1 | \
|
||||
(uint32_t)1 << 2 | \
|
||||
(uint32_t)1 << 3 | \
|
||||
(uint32_t)1 << 5 | \
|
||||
(uint32_t)1 << 6 | \
|
||||
(uint32_t)1 << 7 | \
|
||||
(uint32_t)1 << 11 | \
|
||||
(uint32_t)1 << 12 | \
|
||||
(uint32_t)1 << 13 | \
|
||||
(uint32_t)1 << 14 | \
|
||||
(uint32_t)1 << 21 | \
|
||||
(uint32_t)1 << 22 | \
|
||||
(uint32_t)1 << 23 | \
|
||||
(uint32_t)1 << 24 | \
|
||||
(uint32_t)1 << 25 | \
|
||||
(uint32_t)1 << 30 | \
|
||||
(uint32_t)1 << 31 | 0)
|
||||
// LPENH is for pins SLCD/LP[32..51], where bit 0 represents pin 32.
|
||||
#define CONF_SLCD_LPENH (\
|
||||
(uint32_t)1 << (32 - 32) | \
|
||||
(uint32_t)1 << (33 - 32) | \
|
||||
(uint32_t)1 << (34 - 32) | \
|
||||
(uint32_t)1 << (35 - 32) | \
|
||||
(uint32_t)1 << (42 - 32) | \
|
||||
(uint32_t)1 << (43 - 32) | \
|
||||
(uint32_t)1 << (48 - 32) | \
|
||||
(uint32_t)1 << (49 - 32) | \
|
||||
(uint32_t)1 << (51 - 32) | 0)
|
||||
|
||||
|
||||
// 9-pin connector
|
||||
#define A0 GPIO(GPIO_PORTB, 4)
|
||||
#define WATCH_A0_EIC_CHANNEL 4
|
||||
#define A1 GPIO(GPIO_PORTB, 1)
|
||||
#define WATCH_A1_EIC_CHANNEL 1
|
||||
#define A2 GPIO(GPIO_PORTB, 2)
|
||||
#define WATCH_A2_EIC_CHANNEL 2
|
||||
#define A3 GPIO(GPIO_PORTB, 3)
|
||||
#define WATCH_A3_EIC_CHANNEL 3
|
||||
#define A4 GPIO(GPIO_PORTB, 0)
|
||||
#define WATCH_A4_EIC_CHANNEL 0
|
||||
#define SDA GPIO(GPIO_PORTB, 30)
|
||||
#define SCL GPIO(GPIO_PORTB, 31)
|
||||
|
||||
// aliases for as A3/A4; these were mentioned as D0/D1 in early documentation.
|
||||
#define D0 GPIO(GPIO_PORTB, 3)
|
||||
#define D1 GPIO(GPIO_PORTB, 0)
|
||||
|
||||
// interrupt mapping
|
||||
#define EXT_IRQ_AMOUNT 6
|
||||
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {10, PIN_PA30}, {11, PIN_PA31},
|
||||
|
||||
#endif // PINS_H_INCLUDED
|
22
make.mk
22
make.mk
@ -6,7 +6,19 @@ BUILD = ./build-sim
|
||||
endif
|
||||
BIN = watch
|
||||
|
||||
ifndef BOARD
|
||||
ifndef COLOR
|
||||
$(error Set the COLOR variable to RED, BLUE, GREEN or PRO depending on what board you have.)
|
||||
endif
|
||||
|
||||
COLOR_VALID := $(filter $(COLOR),RED BLUE GREEN PRO)
|
||||
|
||||
ifeq ($(COLOR_VALID),)
|
||||
$(error COLOR must be RED, BLUE, GREEN or PRO)
|
||||
endif
|
||||
|
||||
ifeq ($(COLOR), PRO)
|
||||
override BOARD = OSO-SWAT-C1-00
|
||||
else
|
||||
override BOARD = OSO-SWAT-A1-05
|
||||
endif
|
||||
|
||||
@ -154,7 +166,6 @@ SRCS += \
|
||||
$(TOP)/watch-library/shared/driver/lis2dw.c \
|
||||
$(TOP)/watch-library/shared/driver/opt3001.c \
|
||||
$(TOP)/watch-library/shared/driver/spiflash.c \
|
||||
$(TOP)/watch-library/shared/watch/watch_private_buzzer.c \
|
||||
$(TOP)/watch-library/shared/watch/watch_private_display.c \
|
||||
$(TOP)/watch-library/shared/watch/watch_utility.c \
|
||||
|
||||
@ -199,7 +210,6 @@ SRCS += \
|
||||
$(TOP)/watch-library/simulator/watch/watch.c \
|
||||
$(TOP)/watch-library/shared/driver/thermistor_driver.c \
|
||||
$(TOP)/watch-library/shared/driver/opt3001.c \
|
||||
$(TOP)/watch-library/shared/watch/watch_private_buzzer.c \
|
||||
$(TOP)/watch-library/shared/watch/watch_private_display.c \
|
||||
$(TOP)/watch-library/shared/watch/watch_utility.c \
|
||||
|
||||
@ -213,6 +223,12 @@ 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 PRO)
|
||||
|
||||
ifeq ($(COLOR_VALID),)
|
||||
$(error COLOR must be RED, BLUE, or GREEN)
|
||||
endif
|
||||
|
||||
ifeq ($(COLOR), BLUE)
|
||||
CFLAGS += -DWATCH_IS_BLUE_BOARD
|
||||
endif
|
||||
|
@ -120,13 +120,29 @@ bool filesystem_init(void) {
|
||||
printf("Ignore that error! Formatting filesystem...\r\n");
|
||||
err = lfs_format(&lfs, &cfg);
|
||||
if (err < 0) return false;
|
||||
err = lfs_mount(&lfs, &cfg) == LFS_ERR_OK;
|
||||
err = lfs_mount(&lfs, &cfg);
|
||||
printf("Filesystem mounted with %ld bytes free.\r\n", filesystem_get_free_space());
|
||||
}
|
||||
|
||||
return err == LFS_ERR_OK;
|
||||
}
|
||||
|
||||
int _filesystem_format(void);
|
||||
int _filesystem_format(void) {
|
||||
int err = lfs_unmount(&lfs);
|
||||
if (err < 0) {
|
||||
printf("Couldn't unmount - continuing to format, but you should reboot afterwards!\r\n");
|
||||
}
|
||||
|
||||
err = lfs_format(&lfs, &cfg);
|
||||
if (err < 0) return err;
|
||||
|
||||
err = lfs_mount(&lfs, &cfg);
|
||||
if (err < 0) return err;
|
||||
printf("Filesystem re-mounted with %ld bytes free.\r\n", filesystem_get_free_space());
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool filesystem_file_exists(char *filename) {
|
||||
info.type = 0;
|
||||
lfs_stat(&lfs, filename, &info);
|
||||
@ -251,6 +267,16 @@ int filesystem_cmd_rm(int argc, char *argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int filesystem_cmd_format(int argc, char *argv[]) {
|
||||
(void) argc;
|
||||
if(strcmp(argv[1], "YES") == 0) {
|
||||
return _filesystem_format();
|
||||
}
|
||||
printf("usage: format YES\r\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int filesystem_cmd_echo(int argc, char *argv[]) {
|
||||
(void) argc;
|
||||
|
||||
@ -279,4 +305,3 @@ int filesystem_cmd_echo(int argc, char *argv[]) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,7 @@ int filesystem_cmd_ls(int argc, char *argv[]);
|
||||
int filesystem_cmd_cat(int argc, char *argv[]);
|
||||
int filesystem_cmd_df(int argc, char *argv[]);
|
||||
int filesystem_cmd_rm(int argc, char *argv[]);
|
||||
int filesystem_cmd_format(int argc, char *argv[]);
|
||||
int filesystem_cmd_echo(int argc, char *argv[]);
|
||||
|
||||
#endif // FILESYSTEM_H_
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <inttypes.h>
|
||||
#include "time.h"
|
||||
|
||||
typedef enum {
|
||||
typedef enum __attribute__ ((__packed__)) {
|
||||
SHA1,
|
||||
SHA224,
|
||||
SHA256,
|
||||
|
304
movement/lib/moonrise/moonrise.c
Normal file
304
movement/lib/moonrise/moonrise.c
Normal 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);
|
||||
}
|
||||
|
43
movement/lib/moonrise/moonrise.h
Normal file
43
movement/lib/moonrise/moonrise.h
Normal 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
|
3704
movement/lib/smallchesslib/smallchesslib.h
Normal file
3704
movement/lib/smallchesslib/smallchesslib.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 \
|
||||
@ -52,6 +55,7 @@ SRCS += \
|
||||
../shell.c \
|
||||
../shell_cmd_list.c \
|
||||
../watch_faces/clock/simple_clock_face.c \
|
||||
../watch_faces/clock/close_enough_clock_face.c \
|
||||
../watch_faces/clock/clock_face.c \
|
||||
../watch_faces/clock/world_clock_face.c \
|
||||
../watch_faces/clock/beats_face.c \
|
||||
@ -118,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 \
|
||||
@ -128,7 +133,25 @@ 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 \
|
||||
../watch_faces/complication/deadline_face.c \
|
||||
../watch_faces/complication/higher_lower_game_face.c \
|
||||
../watch_faces/clock/french_revolutionary_face.c \
|
||||
../watch_faces/clock/minimal_clock_face.c \
|
||||
../watch_faces/complication/simon_face.c \
|
||||
../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.
|
||||
|
@ -201,7 +201,7 @@ static void _movement_handle_scheduled_tasks(void) {
|
||||
|
||||
for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
|
||||
if (scheduled_tasks[i].reg) {
|
||||
if (scheduled_tasks[i].reg == date_time.reg) {
|
||||
if (scheduled_tasks[i].reg <= date_time.reg) {
|
||||
scheduled_tasks[i].reg = 0;
|
||||
movement_event_t background_event = { EVENT_BACKGROUND_TASK, 0 };
|
||||
watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]);
|
||||
@ -239,14 +239,24 @@ void movement_request_tick_frequency(uint8_t freq) {
|
||||
}
|
||||
|
||||
void movement_illuminate_led(void) {
|
||||
if (movement_state.settings.bit.led_duration) {
|
||||
if (movement_state.settings.bit.led_duration != 0b111) {
|
||||
watch_set_led_color(movement_state.settings.bit.led_red_color ? (0xF | movement_state.settings.bit.led_red_color << 4) : 0,
|
||||
movement_state.settings.bit.led_green_color ? (0xF | movement_state.settings.bit.led_green_color << 4) : 0);
|
||||
movement_state.light_ticks = (movement_state.settings.bit.led_duration * 2 - 1) * 128;
|
||||
if (movement_state.settings.bit.led_duration == 0) {
|
||||
movement_state.light_ticks = 1;
|
||||
} else {
|
||||
movement_state.light_ticks = (movement_state.settings.bit.led_duration * 2 - 1) * 128;
|
||||
}
|
||||
_movement_enable_fast_tick_if_needed();
|
||||
}
|
||||
}
|
||||
|
||||
static void _movement_led_off(void) {
|
||||
watch_set_led_off();
|
||||
movement_state.light_ticks = -1;
|
||||
_movement_disable_fast_tick_if_possible();
|
||||
}
|
||||
|
||||
bool movement_default_loop_handler(movement_event_t event, movement_settings_t *settings) {
|
||||
(void)settings;
|
||||
|
||||
@ -257,6 +267,11 @@ bool movement_default_loop_handler(movement_event_t event, movement_settings_t *
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
movement_illuminate_led();
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
if (movement_state.settings.bit.led_duration == 0) {
|
||||
_movement_led_off();
|
||||
}
|
||||
break;
|
||||
case EVENT_MODE_LONG_PRESS:
|
||||
if (MOVEMENT_SECONDARY_FACE_INDEX && movement_state.current_face_idx == 0) {
|
||||
movement_move_to_face(MOVEMENT_SECONDARY_FACE_INDEX);
|
||||
@ -319,15 +334,23 @@ void movement_request_wake() {
|
||||
_movement_reset_inactivity_countdown();
|
||||
}
|
||||
|
||||
void end_buzzing() {
|
||||
static void end_buzzing() {
|
||||
movement_state.is_buzzing = false;
|
||||
}
|
||||
|
||||
void end_buzzing_and_disable_buzzer(void) {
|
||||
static void end_buzzing_and_disable_buzzer(void) {
|
||||
end_buzzing();
|
||||
watch_disable_buzzer();
|
||||
}
|
||||
|
||||
static void set_initial_clock_mode(void) {
|
||||
#ifdef CLOCK_FACE_24H_ONLY
|
||||
movement_state.settings.bit.clock_mode_24h = true;
|
||||
#else
|
||||
movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void movement_play_signal(void) {
|
||||
void *maybe_disable_buzzer = end_buzzing_and_disable_buzzer;
|
||||
if (watch_is_buzzer_or_led_enabled()) {
|
||||
@ -376,14 +399,14 @@ void app_init(void) {
|
||||
#endif
|
||||
|
||||
memset(&movement_state, 0, sizeof(movement_state));
|
||||
|
||||
movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
|
||||
set_initial_clock_mode();
|
||||
movement_state.settings.bit.led_red_color = MOVEMENT_DEFAULT_RED_COLOR;
|
||||
movement_state.settings.bit.led_green_color = MOVEMENT_DEFAULT_GREEN_COLOR;
|
||||
movement_state.settings.bit.button_should_sound = MOVEMENT_DEFAULT_BUTTON_SOUND;
|
||||
movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL;
|
||||
movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL;
|
||||
movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION;
|
||||
|
||||
movement_state.light_ticks = -1;
|
||||
movement_state.alarm_ticks = -1;
|
||||
movement_state.next_available_backup_register = 4;
|
||||
@ -503,9 +526,7 @@ bool app_loop(void) {
|
||||
if (watch_get_pin_level(BTN_LIGHT)) {
|
||||
movement_state.light_ticks = 1;
|
||||
} else {
|
||||
watch_set_led_off();
|
||||
movement_state.light_ticks = -1;
|
||||
_movement_disable_fast_tick_if_possible();
|
||||
_movement_led_off();
|
||||
}
|
||||
}
|
||||
|
||||
@ -543,6 +564,17 @@ bool app_loop(void) {
|
||||
event.subsecond = movement_state.subsecond;
|
||||
// the first trip through the loop overrides the can_sleep state
|
||||
can_sleep = wf->loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_face_idx]);
|
||||
|
||||
// Keep light on if user is still interacting with the watch.
|
||||
if (movement_state.light_ticks > 0) {
|
||||
switch (event.event_type) {
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
case EVENT_MODE_BUTTON_DOWN:
|
||||
case EVENT_ALARM_BUTTON_DOWN:
|
||||
movement_illuminate_led();
|
||||
}
|
||||
}
|
||||
|
||||
event.event_type = EVENT_NONE;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ typedef union {
|
||||
uint8_t to_interval : 2; // an inactivity interval for asking the active face to resign.
|
||||
bool to_always : 1; // if true, always time out from the active face to face 0. otherwise only faces that time out will resign (the default).
|
||||
uint8_t le_interval : 3; // 0 to disable low energy mode, or an inactivity interval for going into low energy mode.
|
||||
uint8_t led_duration : 2; // how many seconds to shine the LED for (x2), or 0 to disable it.
|
||||
uint8_t led_duration : 3; // how many seconds to shine the LED for (x2), 0 to shine only while the button is depressed, or all bits set to disable the LED altogether.
|
||||
uint8_t led_red_color : 4; // for general purpose illumination, the red LED value (0-15)
|
||||
uint8_t led_green_color : 4; // for general purpose illumination, the green LED value (0-15)
|
||||
uint8_t time_zone : 6; // an integer representing an index in the time zone table.
|
||||
@ -60,9 +60,10 @@ typedef union {
|
||||
// time-oriented complication like a sunrise/sunset timer, and a simple locale preference could tell an
|
||||
// altimeter to display feet or meters as easily as it tells a thermometer to display degrees in F or C.
|
||||
bool clock_mode_24h : 1; // indicates whether clock should use 12 or 24 hour mode.
|
||||
bool clock_24h_leading_zero : 1; // indicates whether clock should leading zero to indicate 24 hour mode.
|
||||
bool use_imperial_units : 1; // indicates whether to use metric units (the default) or imperial.
|
||||
bool alarm_enabled : 1; // indicates whether there is at least one alarm enabled.
|
||||
uint8_t reserved : 6; // room for more preferences if needed.
|
||||
uint8_t reserved : 5; // room for more preferences if needed.
|
||||
} bit;
|
||||
uint32_t reg;
|
||||
} movement_settings_t;
|
||||
|
@ -67,21 +67,139 @@ int8_t signal_tune[] = {
|
||||
};
|
||||
#endif // SIGNAL_TUNE_MARIO_THEME
|
||||
|
||||
#ifdef SIGNAL_TUNE_MGS_CODEC
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_REST, 6,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
BUZZER_NOTE_G5SHARP_A5FLAT, 1,
|
||||
BUZZER_NOTE_C6, 1,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_MGS_CODEC
|
||||
|
||||
#ifdef SIGNAL_TUNE_KIM_POSSIBLE
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_G7, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_G4, 3,
|
||||
BUZZER_NOTE_G4, 2,
|
||||
BUZZER_NOTE_REST, 5,
|
||||
BUZZER_NOTE_G7, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_G4, 3,
|
||||
BUZZER_NOTE_G4, 2,
|
||||
BUZZER_NOTE_REST, 5,
|
||||
BUZZER_NOTE_A7SHARP_B7FLAT, 6,
|
||||
BUZZER_NOTE_REST, 2,
|
||||
BUZZER_NOTE_G7, 6,
|
||||
BUZZER_NOTE_G4, 2,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_KIM_POSSIBLE
|
||||
|
||||
#ifdef SIGNAL_TUNE_POWER_RANGERS
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_D8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_D8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_C8, 6,
|
||||
BUZZER_NOTE_REST, 2,
|
||||
BUZZER_NOTE_D8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_F8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_D8, 6,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_POWER_RANGERS
|
||||
|
||||
#ifdef SIGNAL_TUNE_LAYLA
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_A6, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_C7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_D7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_D7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_C7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_D7, 20,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_LAYLA
|
||||
|
||||
#ifdef SIGNAL_TUNE_HARRY_POTTER_SHORT
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_B5, 12,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_E6, 12,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_G6, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F6SHARP_G6FLAT, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_E6, 16,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_B6, 8,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_A6, 24,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F6SHARP_G6FLAT, 24,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_HARRY_POTTER_SHORT
|
||||
|
||||
#ifdef SIGNAL_TUNE_HARRY_POTTER_LONG
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_B5, 12,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_E6, 12,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_G6, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F6SHARP_G6FLAT, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_E6, 16,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_B6, 8,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_A6, 24,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F6SHARP_G6FLAT, 24,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
|
||||
BUZZER_NOTE_E6, 12,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_G6, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F6SHARP_G6FLAT, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_D6SHARP_E6FLAT, 16,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F6, 8,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_B5, 24,
|
||||
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_HARRY_POTTER_LONG
|
||||
|
||||
#endif // MOVEMENT_CUSTOM_SIGNAL_TUNES_H_
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define MOVEMENT_FACES_H_
|
||||
|
||||
#include "simple_clock_face.h"
|
||||
#include "close_enough_clock_face.h"
|
||||
#include "clock_face.h"
|
||||
#include "world_clock_face.h"
|
||||
#include "preferences_face.h"
|
||||
@ -93,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"
|
||||
@ -103,7 +105,25 @@
|
||||
#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"
|
||||
#include "deadline_face.h"
|
||||
#include "higher_lower_game_face.h"
|
||||
#include "french_revolutionary_face.h"
|
||||
#include "minimal_clock_face.h"
|
||||
#include "simon_face.h"
|
||||
#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_
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
@ -57,7 +58,7 @@ static char *prv_skip_whitespace(char *c) {
|
||||
if (*c == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (!isspace((int) *c) != 0) {
|
||||
if ((!isspace((int) *c)) != 0) {
|
||||
return c;
|
||||
}
|
||||
c++;
|
||||
|
@ -85,6 +85,13 @@ shell_command_t g_shell_commands[] = {
|
||||
.max_args = 1,
|
||||
.cb = filesystem_cmd_rm,
|
||||
},
|
||||
{
|
||||
.name = "format",
|
||||
.help = "usage: format YES",
|
||||
.min_args = 1,
|
||||
.max_args = 1,
|
||||
.cb = filesystem_cmd_format,
|
||||
},
|
||||
{
|
||||
.name = "echo",
|
||||
.help = "usage: echo TEXT {>,>>} FILE",
|
||||
@ -156,4 +163,3 @@ static int stress_cmd(int argc, char *argv[]) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -42,10 +42,6 @@
|
||||
#define CLOCK_FACE_LOW_BATTERY_VOLTAGE_THRESHOLD 2200
|
||||
#endif
|
||||
|
||||
#ifndef CLOCK_FACE_24H_ONLY
|
||||
#define CLOCK_FACE_24H_ONLY 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
watch_date_time previous;
|
||||
@ -57,8 +53,15 @@ typedef struct {
|
||||
} clock_state_t;
|
||||
|
||||
static bool clock_is_in_24h_mode(movement_settings_t *settings) {
|
||||
if (CLOCK_FACE_24H_ONLY) { return true; }
|
||||
#ifdef CLOCK_FACE_24H_ONLY
|
||||
return true;
|
||||
#else
|
||||
return settings->bit.clock_mode_24h;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool clock_should_set_leading_zero(movement_settings_t *settings) {
|
||||
return clock_is_in_24h_mode(settings) && settings->bit.clock_24h_leading_zero;
|
||||
}
|
||||
|
||||
static void clock_indicate(WatchIndicatorSegment indicator, bool on) {
|
||||
@ -70,11 +73,11 @@ static void clock_indicate(WatchIndicatorSegment indicator, bool on) {
|
||||
}
|
||||
|
||||
static void clock_indicate_alarm(movement_settings_t *settings) {
|
||||
clock_indicate(WATCH_INDICATOR_BELL, settings->bit.alarm_enabled);
|
||||
clock_indicate(WATCH_INDICATOR_SIGNAL, settings->bit.alarm_enabled);
|
||||
}
|
||||
|
||||
static void clock_indicate_time_signal(clock_state_t *clock) {
|
||||
clock_indicate(WATCH_INDICATOR_SIGNAL, clock->time_signal_enabled);
|
||||
clock_indicate(WATCH_INDICATOR_BELL, clock->time_signal_enabled);
|
||||
}
|
||||
|
||||
static void clock_indicate_24h(movement_settings_t *settings) {
|
||||
@ -125,13 +128,13 @@ static void clock_toggle_time_signal(clock_state_t *clock) {
|
||||
clock_indicate_time_signal(clock);
|
||||
}
|
||||
|
||||
static void clock_display_all(watch_date_time date_time) {
|
||||
static void clock_display_all(watch_date_time date_time, bool leading_zero) {
|
||||
char buf[10 + 1];
|
||||
|
||||
snprintf(
|
||||
buf,
|
||||
sizeof(buf),
|
||||
"%s%2d%2d%02d%02d",
|
||||
leading_zero? "%s%02d%02d%02d%02d" : "%s%2d%2d%02d%02d",
|
||||
watch_utility_get_weekday(date_time),
|
||||
date_time.unit.day,
|
||||
date_time.unit.hour,
|
||||
@ -181,7 +184,7 @@ static void clock_display_clock(movement_settings_t *settings, clock_state_t *cl
|
||||
clock_indicate_pm(settings, current);
|
||||
current = clock_24h_to_12h(current);
|
||||
}
|
||||
clock_display_all(current);
|
||||
clock_display_all(current, clock_should_set_leading_zero(settings));
|
||||
}
|
||||
}
|
||||
|
||||
|
233
movement/watch_faces/clock/close_enough_clock_face.c
Normal file
233
movement/watch_faces/clock/close_enough_clock_face.c
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Ruben Nic
|
||||
*
|
||||
* 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 "close_enough_clock_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
const char *words[12] = {
|
||||
" ",
|
||||
" 5",
|
||||
"10",
|
||||
"15",
|
||||
"20",
|
||||
"25",
|
||||
"30",
|
||||
"35",
|
||||
"40",
|
||||
"45",
|
||||
"50",
|
||||
"55",
|
||||
};
|
||||
|
||||
static const char *past_word = " P";
|
||||
static const char *to_word = " 2";
|
||||
static const char *oclock_word = "OC";
|
||||
|
||||
// sets when in the five minute period we switch
|
||||
// from "X past HH" to "X to HH+1"
|
||||
static const int hour_switch_index = 8;
|
||||
|
||||
static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) {
|
||||
state->alarm_enabled = settings_alarm_enabled;
|
||||
if (state->alarm_enabled) {
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
} else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
};
|
||||
}
|
||||
|
||||
void close_enough_clock_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(close_enough_clock_state_t));
|
||||
}
|
||||
}
|
||||
|
||||
void close_enough_clock_face_activate(movement_settings_t *settings, void *context) {
|
||||
close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
|
||||
|
||||
if (watch_tick_animation_is_running()) {
|
||||
watch_stop_tick_animation();
|
||||
}
|
||||
|
||||
if (settings->bit.clock_mode_24h) {
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
}
|
||||
|
||||
// show alarm indicator if there is an active alarm
|
||||
_update_alarm_indicator(settings->bit.alarm_enabled, state);
|
||||
|
||||
// this ensures that none of the five_minute_periods will match, so we always rerender when the face activates
|
||||
state->prev_five_minute_period = -1;
|
||||
state->prev_min_checked = -1;
|
||||
}
|
||||
|
||||
bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
|
||||
|
||||
char buf[11];
|
||||
watch_date_time date_time;
|
||||
bool show_next_hour = false;
|
||||
int prev_five_minute_period;
|
||||
int prev_min_checked;
|
||||
int close_enough_hour;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
date_time = watch_rtc_get_date_time();
|
||||
prev_five_minute_period = state->prev_five_minute_period;
|
||||
prev_min_checked = state->prev_min_checked;
|
||||
|
||||
// check the battery voltage once a day...
|
||||
if (date_time.unit.day != state->last_battery_check) {
|
||||
state->last_battery_check = date_time.unit.day;
|
||||
watch_enable_adc();
|
||||
uint16_t voltage = watch_get_vcc_voltage();
|
||||
watch_disable_adc();
|
||||
// 2.2 volts will happen when the battery has maybe 5-10% remaining?
|
||||
// we can refine this later.
|
||||
state->battery_low = (voltage < 2200);
|
||||
}
|
||||
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) {
|
||||
watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
}
|
||||
|
||||
// same minute, skip update
|
||||
if (date_time.unit.minute == prev_min_checked) {
|
||||
break;
|
||||
} else {
|
||||
state->prev_min_checked = date_time.unit.minute;
|
||||
}
|
||||
|
||||
int five_minute_period = (date_time.unit.minute / 5) % 12;
|
||||
|
||||
// If we are 60% to the next 5 interval, move up to the next period
|
||||
if (fmodf(date_time.unit.minute / 5.0f, 1.0f) > 0.5f) {
|
||||
// If we are on the last 5 interval and moving to the next period we need to display the next hour because we are wrapping around
|
||||
if (five_minute_period == 11) {
|
||||
show_next_hour = true;
|
||||
}
|
||||
|
||||
five_minute_period = (five_minute_period + 1) % 12;
|
||||
}
|
||||
|
||||
// same five_minute_period, skip update
|
||||
if (five_minute_period == prev_five_minute_period) {
|
||||
break;
|
||||
}
|
||||
|
||||
// we don't want to modify date_time.unit.hour just in case other watch faces use it
|
||||
close_enough_hour = date_time.unit.hour;
|
||||
|
||||
// move from "MM(mins) P HH" to "MM(mins) 2 HH+1"
|
||||
if (five_minute_period >= hour_switch_index || show_next_hour) {
|
||||
close_enough_hour = (close_enough_hour + 1) % 24;
|
||||
}
|
||||
|
||||
if (!settings->bit.clock_mode_24h) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (close_enough_hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
|
||||
close_enough_hour %= 12;
|
||||
if (close_enough_hour == 0) {
|
||||
close_enough_hour = 12;
|
||||
}
|
||||
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) {
|
||||
date_time.unit.hour = 12;
|
||||
}
|
||||
}
|
||||
|
||||
char first_word[3];
|
||||
char second_word[3];
|
||||
char third_word[3];
|
||||
if (five_minute_period == 0) { // "HH OC",
|
||||
sprintf(first_word, "%2d", close_enough_hour);
|
||||
strncpy(second_word, words[five_minute_period], 3);
|
||||
strncpy(third_word, oclock_word, 3);
|
||||
} else {
|
||||
int words_length = sizeof(words) / sizeof(words[0]);
|
||||
|
||||
strncpy(
|
||||
first_word,
|
||||
five_minute_period >= hour_switch_index ?
|
||||
words[words_length - five_minute_period] :
|
||||
words[five_minute_period],
|
||||
3
|
||||
);
|
||||
strncpy(
|
||||
second_word,
|
||||
five_minute_period >= hour_switch_index ?
|
||||
to_word : past_word,
|
||||
3
|
||||
);
|
||||
sprintf(third_word, "%2d", close_enough_hour);
|
||||
}
|
||||
|
||||
sprintf(
|
||||
buf,
|
||||
"%s%2d%s%s%s",
|
||||
watch_utility_get_weekday(date_time),
|
||||
date_time.unit.day,
|
||||
first_word,
|
||||
second_word,
|
||||
third_word
|
||||
);
|
||||
|
||||
watch_display_string(buf, 0);
|
||||
state->prev_five_minute_period = five_minute_period;
|
||||
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != settings->bit.alarm_enabled) {
|
||||
_update_alarm_indicator(settings->bit.alarm_enabled, state);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void close_enough_clock_face_resign(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
}
|
62
movement/watch_faces/clock/close_enough_clock_face.h
Normal file
62
movement/watch_faces/clock/close_enough_clock_face.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Ruben Nic
|
||||
*
|
||||
* 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 CLOSE_ENOUGH_CLOCK_FACE_H_
|
||||
#define CLOSE_ENOUGH_CLOCK_FACE_H_
|
||||
|
||||
/*
|
||||
* CLOSE ENOUGH CLOCK FACE
|
||||
*
|
||||
* Displays the current time; but only in periods of 5.
|
||||
* Just in the in the formats of:
|
||||
* - "10 past 5"
|
||||
* - "15 to 7"
|
||||
* - "6 o'clock"
|
||||
*
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef struct {
|
||||
int prev_five_minute_period;
|
||||
int prev_min_checked;
|
||||
uint8_t last_battery_check;
|
||||
bool battery_low;
|
||||
bool alarm_enabled;
|
||||
} close_enough_clock_state_t;
|
||||
|
||||
void close_enough_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
|
||||
void close_enough_clock_face_activate(movement_settings_t *settings, void *context);
|
||||
bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
void close_enough_clock_face_resign(movement_settings_t *settings, void *context);
|
||||
|
||||
#define close_enough_clock_face ((const watch_face_t){ \
|
||||
close_enough_clock_face_setup, \
|
||||
close_enough_clock_face_activate, \
|
||||
close_enough_clock_face_loop, \
|
||||
close_enough_clock_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // CLOSE_ENOUGH_CLOCK_FACE_H_
|
245
movement/watch_faces/clock/french_revolutionary_face.c
Normal file
245
movement/watch_faces/clock/french_revolutionary_face.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 CarpeNoctem
|
||||
*
|
||||
* 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 "french_revolutionary_face.h"
|
||||
|
||||
void french_revolutionary_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(french_revolutionary_state_t));
|
||||
memset(*context_ptr, 0, sizeof(french_revolutionary_state_t));
|
||||
// Do any one-time tasks in here; the inside of this conditional happens only at boot.
|
||||
french_revolutionary_state_t *state = (french_revolutionary_state_t *)*context_ptr;
|
||||
state->use_am_pm = false;
|
||||
state->show_seconds = true;
|
||||
state->display_type = 0;
|
||||
state->colon_set_after_splash = false;
|
||||
}
|
||||
// Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
|
||||
}
|
||||
|
||||
void french_revolutionary_face_activate(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
|
||||
|
||||
// Handle any tasks related to your watch face coming on screen.
|
||||
state->colon_set_after_splash = false;
|
||||
}
|
||||
|
||||
bool french_revolutionary_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
|
||||
|
||||
char buf[11];
|
||||
watch_date_time date_time;
|
||||
fr_decimal_time decimal_time;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
// Initial UI - Show a quick "splash screen"
|
||||
watch_clear_display();
|
||||
watch_display_string("FR dECimL", 0);
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
|
||||
date_time = watch_rtc_get_date_time();
|
||||
|
||||
decimal_time = get_decimal_time(&date_time);
|
||||
|
||||
set_display_buffer(buf, state, &decimal_time, &date_time);
|
||||
|
||||
// If we're in low-energy mode, don't write out the seconds. Also start the LE tick animation if it's not already going.
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
buf[8] = ' ';
|
||||
buf[9] = ' ';
|
||||
if (!watch_tick_animation_is_running()) { watch_start_tick_animation(500); }
|
||||
}
|
||||
|
||||
// Update the display with our decimal time
|
||||
watch_display_string(buf, 0);
|
||||
|
||||
// Oh, and a one-off to set the colon after the "splash screen"
|
||||
if (!state->colon_set_after_splash) {
|
||||
watch_set_colon();
|
||||
state->colon_set_after_splash = true;
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->display_type += 1 ; // cycle through the display types
|
||||
if (state->display_type > 2) { state->display_type = 0; } // but return to 0 after 2
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
// I originally had chiming on the decimal-hour enabled, and this would enable/disable that chime, just like on
|
||||
// the simple clock and decimal time faces. But because decimal seconds don't always line up with normal seconds,
|
||||
// I assume the (decimal-)hourly chime could sometimes be missed. Additionally, I need this button for other purposes,
|
||||
// now that I added seconds on/off toggle and upper normal-time with the ability to toggle that between 12/24hr format.
|
||||
state->show_seconds = !state->show_seconds;
|
||||
if (!state->show_seconds) { watch_display_string(" ", 8); }
|
||||
else { watch_display_string("--", 8); }
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
// In case anyone really wants that upper time in 12-hour format. I thought about using the global setting (settings->bit.clock_mode_24h)
|
||||
// for this preference, but thought someone who prefers 12-hour format normally, might prefer 24hr when compared to a 10hr decimal day,
|
||||
// so this is separate for now.
|
||||
state->use_am_pm = !state->use_am_pm;
|
||||
if (state->use_am_pm) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
date_time = watch_rtc_get_date_time();
|
||||
if (date_time.unit.hour < 12) { watch_clear_indicator(WATCH_INDICATOR_PM); }
|
||||
else { watch_set_indicator(WATCH_INDICATOR_PM); }
|
||||
} else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
}
|
||||
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 if the watch can enter standby mode. Generally speaking, you should always return true.
|
||||
// Exceptions:
|
||||
// * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
|
||||
// * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
|
||||
// Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
|
||||
// movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
|
||||
return true;
|
||||
}
|
||||
|
||||
void french_revolutionary_face_resign(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
|
||||
// handle any cleanup before your watch face goes off-screen.
|
||||
}
|
||||
|
||||
// Calculate decimal time from normal (24hr) time
|
||||
fr_decimal_time get_decimal_time(watch_date_time *date_time) {
|
||||
uint32_t current_24hr_secs, current_decimal_seconds;
|
||||
fr_decimal_time decimal_time;
|
||||
// Current 24-hr time in seconds (There are 86400 of these in a day.)
|
||||
current_24hr_secs = date_time->unit.hour * 3600 + date_time->unit.minute * 60 + date_time->unit.second;
|
||||
|
||||
// Current Decimal Time in seconds. There are 100000 seconds in a 10-hr decimal-time day.
|
||||
// current_decimal_seconds = current_24hr_seconds * 100000 / 86400, or = current_24_seconds * 1000 / 864;
|
||||
// By chopping the extra zeros off the end, we can use uint32 instead of uint64.
|
||||
current_decimal_seconds = current_24hr_secs * 1000 / 864;
|
||||
|
||||
decimal_time.hour = current_decimal_seconds / 10000;
|
||||
// Remove the hours from total seconds and keep the remainder for below.
|
||||
current_decimal_seconds = current_decimal_seconds - decimal_time.hour * 10000;
|
||||
|
||||
decimal_time.minute = current_decimal_seconds / 100;
|
||||
// Remove the minutes from total seconds and keep the remaining seconds
|
||||
// Note: I think I used an extra seconds variable here because sprintf or movement weren't liking a uint32...
|
||||
decimal_time.second = current_decimal_seconds - decimal_time.minute * 100;
|
||||
return decimal_time;
|
||||
}
|
||||
|
||||
// Fills in the display buffer, depending on the currently-selected display option (and sub-options):
|
||||
// - Decimal-time only
|
||||
// - Decimal-time with date in top-right
|
||||
// - Decimal-time with normal time in the top (minutes first, then hours, due to display limitations)
|
||||
// TODO: There is some power-saving stuff that simple clock does here around not redrawing characters that haven't changed, but we're not doing that here.
|
||||
// I'll try to add that optimization could be added in a future commit.
|
||||
void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time *date_time) {
|
||||
switch (state->display_type) {
|
||||
// Decimal time only
|
||||
case 0:
|
||||
// Originally I had the day slot set to "FR" (French Revolutionary time), but my brain kept thinking "Friday" whenever I saw it,
|
||||
// so I changed it to dT (Decimal Time) to avoid that confusion. Apologies to anyone who has the other decimal_time face and this one
|
||||
// installed concurrently. Maybe the splash screen will help a little.
|
||||
sprintf( buf, "dT %2d%02d%02d", decimal_time->hour, decimal_time->minute, decimal_time->second );
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
break;
|
||||
// Decimal time and date
|
||||
case 1:
|
||||
sprintf( buf, "dT%2d%2d%02d%02d", date_time->unit.day, decimal_time->hour, decimal_time->minute, decimal_time->second );
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
break;
|
||||
// Decimal time on bottom, normal time above
|
||||
case 2:
|
||||
if (state->use_am_pm) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
if (date_time->unit.hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
date_time->unit.hour %= 12;
|
||||
if (date_time->unit.hour == 0) date_time->unit.hour = 12;
|
||||
} else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
}
|
||||
// Note, the date digits don't display a leading zero well, so we don't use it.
|
||||
sprintf( buf, "%02d%2d%2d%02d%02d", date_time->unit.minute, date_time->unit.hour, decimal_time->hour, decimal_time->minute, decimal_time->second );
|
||||
|
||||
// Make the second character of the Day area more readable
|
||||
buf[1] = fix_character_one(buf[1]);
|
||||
break;
|
||||
}
|
||||
// Finally, if show_seconds is disabled, trim those off.
|
||||
if (!state->show_seconds) {
|
||||
buf[8] = ' ';
|
||||
buf[9] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// Sadly, the second character of the Day field cannot show all numbers, so we make some replacements.
|
||||
// See https://www.sensorwatch.net/docs/wig/display/#limitations-of-the-weekday-digits
|
||||
char fix_character_one(char digit) {
|
||||
char return_char = digit; // We don't need to update this for 0, 1, 3, 7 and 8.
|
||||
switch(digit) {
|
||||
case '2':
|
||||
// Roman numeral / tally representation of 2
|
||||
return_char = '|'; // Thanks, Joey, for already having this in the character set.
|
||||
break;
|
||||
case '4':
|
||||
// Looks almost like a 4 - just missing the top-left segment.
|
||||
// 0b01000110
|
||||
return_char = '&'; // Slight hack - I want 0b01000110, but 0b01000100 is already in the character set and will do, since B and C segments are linked in this position.
|
||||
break;
|
||||
case '5':
|
||||
return_char = 'F'; // F for Five
|
||||
break;
|
||||
case '6':
|
||||
return_char = 'E'; // Looks almost like a 6 - just missing the bottom-right segment. Not super happy with it, but liked it best of the options I tried.
|
||||
break;
|
||||
case '9':
|
||||
return_char = 'N'; // N for Nine
|
||||
break;
|
||||
}
|
||||
return return_char;
|
||||
}
|
84
movement/watch_faces/clock/french_revolutionary_face.h
Normal file
84
movement/watch_faces/clock/french_revolutionary_face.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 CarpeNoctem
|
||||
*
|
||||
* 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 FRENCH_REVOLUTIONARY_FACE_H_
|
||||
#define FRENCH_REVOLUTIONARY_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* French Revolutionary Decimal Time
|
||||
*
|
||||
* Similar to the Decimal Time face, but with the day divided into ten hours instead of twenty four.
|
||||
* Each hour is divided into one hundred minutes, and those minutes are divided into 100 seconds.
|
||||
* I came across this one the Svalbard watch site here: https://svalbard.watch/pages/about_decimal_time.html
|
||||
* More info here as well: https://en.wikipedia.org/wiki/Decimal_time
|
||||
*
|
||||
* By default, the face just displays the current decimal time. Pressing the alarm button will toggle through other display options:
|
||||
* 1) Just decimal time (with dT indicator at top)
|
||||
* 2) Decimal time, with dT indicator and date above.
|
||||
* 3) Decimal time, with 24-hr time above (where Day and Date would normally be displayed), BUT minutes first then hours.
|
||||
* Sadly, the first character of the date area only goes up to 3 (see https://www.sensorwatch.net/docs/wig/display/#the-day-digits)
|
||||
* I was going to begrudgindly leave this display option out when I realized that, but thought it would be better to have this backwards
|
||||
* representation of the "normal" time than not at all.
|
||||
*
|
||||
* A long-press of the light button will toggle the upper time between 12-hr AM/PM and 24-hr mode. I thought of reading the main setting for this,
|
||||
* but thought that a person could normally prefer 12hr time, but next to a 10hr day want to see the normal time in the 24hr format.
|
||||
*
|
||||
* A long-press of the alarm button will toggle the seconds off and on.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
bool use_am_pm; // Use 12-hr AM/PM for upper display instead of 24-hr? (Default is 24-hr)
|
||||
bool show_seconds;
|
||||
bool colon_set_after_splash;
|
||||
uint8_t display_type : 2;
|
||||
} french_revolutionary_state_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t second : 8; // 0-99
|
||||
uint8_t minute : 8; // 0-99
|
||||
uint8_t hour : 5; // 0-10
|
||||
} fr_decimal_time;
|
||||
|
||||
void french_revolutionary_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
|
||||
void french_revolutionary_face_activate(movement_settings_t *settings, void *context);
|
||||
bool french_revolutionary_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
void french_revolutionary_face_resign(movement_settings_t *settings, void *context);
|
||||
char fix_character_one(char digit);
|
||||
fr_decimal_time get_decimal_time(watch_date_time *date_time);
|
||||
void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time *date_time);
|
||||
|
||||
|
||||
#define french_revolutionary_face ((const watch_face_t){ \
|
||||
french_revolutionary_face_setup, \
|
||||
french_revolutionary_face_activate, \
|
||||
french_revolutionary_face_loop, \
|
||||
french_revolutionary_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // FRENCH_REVOLUTIONARY_FACE_H_
|
||||
|
117
movement/watch_faces/clock/minimal_clock_face.c
Normal file
117
movement/watch_faces/clock/minimal_clock_face.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Dennisman219
|
||||
*
|
||||
* 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 "minimal_clock_face.h"
|
||||
|
||||
static void _minimal_clock_face_update_display(movement_settings_t *settings) {
|
||||
watch_date_time date_time = watch_rtc_get_date_time();
|
||||
char buffer[11];
|
||||
|
||||
if (!settings->bit.clock_mode_24h) {
|
||||
date_time.unit.hour %= 12;
|
||||
sprintf(buffer, "%2d%02d ", date_time.unit.hour, date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buffer, "%02d%02d ", date_time.unit.hour, date_time.unit.minute);
|
||||
}
|
||||
|
||||
watch_display_string(buffer, 4);
|
||||
}
|
||||
|
||||
void minimal_clock_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(minimal_clock_state_t));
|
||||
memset(*context_ptr, 0, sizeof(minimal_clock_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.
|
||||
}
|
||||
|
||||
void minimal_clock_face_activate(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
// Handle any tasks related to your watch face coming on screen.
|
||||
watch_set_colon();
|
||||
}
|
||||
|
||||
bool minimal_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
(void) context;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
// Show your initial UI here.
|
||||
_minimal_clock_face_update_display(settings);
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
// If needed, update your display here.
|
||||
_minimal_clock_face_update_display(settings);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
// You can use the Light button for your own purposes. Note that by default, Movement will also
|
||||
// illuminate the LED in response to EVENT_LIGHT_BUTTON_DOWN; to suppress that behavior, add an
|
||||
// empty case for EVENT_LIGHT_BUTTON_DOWN.
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
// Just in case you have need for another button.
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
// Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
|
||||
// you may uncomment this line to move back to the first watch face in the list:
|
||||
// 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);
|
||||
_minimal_clock_face_update_display(settings);
|
||||
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 if the watch can enter standby mode. Generally speaking, you should always return true.
|
||||
// Exceptions:
|
||||
// * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
|
||||
// * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
|
||||
// Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
|
||||
// movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
|
||||
return true;
|
||||
}
|
||||
|
||||
void minimal_clock_face_resign(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
|
||||
// handle any cleanup before your watch face goes off-screen.
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Joey Castillo
|
||||
* Copyright (c) 2023 Dennisman219
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -21,15 +21,37 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "driver_init.h"
|
||||
|
||||
uint16_t sequence_length(int8_t *sequence) {
|
||||
uint16_t result = 0;
|
||||
#ifndef MINIMAL_CLOCK_FACE_H_
|
||||
#define MINIMAL_CLOCK_FACE_H_
|
||||
|
||||
while (*sequence != 0){
|
||||
result += *(sequence + 1);
|
||||
sequence += 2;
|
||||
}
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* MINIMAL CLOCK FACE
|
||||
*
|
||||
* A minimal clock face that just shows hours and minutes.
|
||||
* There is nothing to configure. The face follows the 12h/24h setting
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
// Anything you need to keep track of, put it here!
|
||||
uint8_t unused;
|
||||
} minimal_clock_state_t;
|
||||
|
||||
void minimal_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
|
||||
void minimal_clock_face_activate(movement_settings_t *settings, void *context);
|
||||
bool minimal_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
void minimal_clock_face_resign(movement_settings_t *settings, void *context);
|
||||
|
||||
#define minimal_clock_face ((const watch_face_t){ \
|
||||
minimal_clock_face_setup, \
|
||||
minimal_clock_face_activate, \
|
||||
minimal_clock_face_loop, \
|
||||
minimal_clock_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // MINIMAL_CLOCK_FACE_H_
|
||||
|
||||
return result;
|
||||
}
|
@ -68,7 +68,7 @@ void repetition_minute_face_activate(movement_settings_t *settings, void *contex
|
||||
|
||||
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
|
||||
|
||||
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
@ -112,6 +112,7 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
bool set_leading_zero = false;
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before seconds is the same, don't waste cycles setting those segments.
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
|
||||
@ -132,6 +133,8 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
} else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) {
|
||||
set_leading_zero = true;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
@ -142,6 +145,8 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
if (set_leading_zero)
|
||||
watch_display_string("0", 4);
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state);
|
||||
break;
|
||||
|
@ -60,7 +60,7 @@ void simple_clock_bin_led_face_activate(movement_settings_t *settings, void *con
|
||||
|
||||
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
|
||||
|
||||
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
@ -138,6 +138,7 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
bool set_leading_zero = false;
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before seconds is the same, don't waste cycles setting those segments.
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
|
||||
@ -158,6 +159,8 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
} else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) {
|
||||
set_leading_zero = true;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
@ -168,6 +171,8 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
if (set_leading_zero)
|
||||
watch_display_string("0", 4);
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state);
|
||||
}
|
||||
|
@ -51,7 +51,11 @@ void simple_clock_face_activate(movement_settings_t *settings, void *context) {
|
||||
|
||||
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
|
||||
|
||||
#ifdef CLOCK_FACE_24H_ONLY
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
#else
|
||||
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
#endif
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
@ -95,6 +99,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
bool set_leading_zero = false;
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before seconds is the same, don't waste cycles setting those segments.
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
|
||||
@ -106,6 +111,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting
|
||||
sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
|
||||
} else {
|
||||
// other stuff changed; let's do it all.
|
||||
#ifndef CLOCK_FACE_24H_ONLY
|
||||
if (!settings->bit.clock_mode_24h) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (date_time.unit.hour < 12) {
|
||||
@ -116,6 +122,12 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (settings->bit.clock_mode_24h && settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) {
|
||||
set_leading_zero = true;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_tick_animation_is_running()) watch_start_tick_animation(500);
|
||||
@ -125,6 +137,10 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
|
||||
if (set_leading_zero)
|
||||
watch_display_string("0", 4);
|
||||
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state);
|
||||
break;
|
||||
|
@ -50,7 +50,7 @@ void weeknumber_clock_face_activate(movement_settings_t *settings, void *context
|
||||
|
||||
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
|
||||
|
||||
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
@ -94,6 +94,7 @@ bool weeknumber_clock_face_loop(movement_event_t event, movement_settings_t *set
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
bool set_leading_zero = false;
|
||||
if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before minutes is the same.
|
||||
pos = 6;
|
||||
@ -109,6 +110,8 @@ bool weeknumber_clock_face_loop(movement_event_t event, movement_settings_t *set
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
} else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) {
|
||||
set_leading_zero = true;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
@ -119,6 +122,8 @@ bool weeknumber_clock_face_loop(movement_event_t event, movement_settings_t *set
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
if (set_leading_zero)
|
||||
watch_display_string("0", 4);
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state);
|
||||
break;
|
||||
|
@ -174,7 +174,7 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings,
|
||||
if (refresh_face) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
watch_set_colon();
|
||||
if (settings->bit.clock_mode_24h)
|
||||
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero)
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
state->previous_date_time = REFRESH_TIME;
|
||||
@ -188,6 +188,7 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings,
|
||||
previous_date_time = state->previous_date_time;
|
||||
state->previous_date_time = date_time.reg;
|
||||
|
||||
bool set_leading_zero = false;
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
/* Everything before seconds is the same, don't waste cycles setting those segments. */
|
||||
pos = 8;
|
||||
@ -208,7 +209,9 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings,
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0)
|
||||
date_time.unit.hour = 12;
|
||||
}
|
||||
} else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) {
|
||||
set_leading_zero = true;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
@ -230,6 +233,8 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings,
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
if (set_leading_zero)
|
||||
watch_display_string("0", 4);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->current_zone = find_selected_zone(state, FORWARD);
|
||||
|
@ -60,7 +60,7 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se
|
||||
watch_date_time date_time;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
watch_set_colon();
|
||||
state->previous_date_time = 0xFFFFFFFF;
|
||||
// fall through
|
||||
@ -72,6 +72,7 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se
|
||||
previous_date_time = state->previous_date_time;
|
||||
state->previous_date_time = date_time.reg;
|
||||
|
||||
bool set_leading_zero = false;
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before seconds is the same, don't waste cycles setting those segments.
|
||||
pos = 8;
|
||||
@ -91,6 +92,8 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
} else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) {
|
||||
set_leading_zero = true;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
@ -112,6 +115,8 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
if (set_leading_zero)
|
||||
watch_display_string("0", 4);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
movement_request_tick_frequency(4);
|
||||
|
@ -293,6 +293,7 @@ static void _activity_update_logging_screen(movement_settings_t *settings, activ
|
||||
}
|
||||
// Briefly, show time without seconds
|
||||
else {
|
||||
bool set_leading_zero = false;
|
||||
watch_clear_indicator(WATCH_INDICATOR_LAP);
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
uint8_t hour = now.unit.hour;
|
||||
@ -304,14 +305,18 @@ static void _activity_update_logging_screen(movement_settings_t *settings, activ
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
hour %= 12;
|
||||
if (hour == 0) hour = 12;
|
||||
}
|
||||
else {
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
} else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
if (!settings->bit.clock_24h_leading_zero)
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
else if (hour < 10)
|
||||
set_leading_zero = true;
|
||||
}
|
||||
sprintf(activity_buf, "%2d%02d ", hour, now.unit.minute);
|
||||
watch_set_colon();
|
||||
watch_display_string(activity_buf, 4);
|
||||
if (set_leading_zero)
|
||||
watch_display_string("0", 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,7 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state
|
||||
i = state->alarm[state->alarm_idx].day + 1;
|
||||
}
|
||||
//handle am/pm for hour display
|
||||
bool set_leading_zero = false;
|
||||
uint8_t h = state->alarm[state->alarm_idx].hour;
|
||||
if (!settings->bit.clock_mode_24h) {
|
||||
if (h >= 12) {
|
||||
@ -81,8 +82,17 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
if (h == 0) h = 12;
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
if (settings->bit.clock_24h_leading_zero) {
|
||||
if (h < 10) {
|
||||
set_leading_zero = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
sprintf(buf, "%c%c%2d%2d%02d ",
|
||||
|
||||
sprintf(buf, set_leading_zero? "%c%c%2d%02d%02d " : "%c%c%2d%2d%02d ",
|
||||
_dow_strings[i][0], _dow_strings[i][1],
|
||||
(state->alarm_idx + 1),
|
||||
h,
|
||||
|
467
movement/watch_faces/complication/butterfly_game_face.c
Normal file
467
movement/watch_faces/complication/butterfly_game_face.c
Normal 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.
|
||||
}
|
||||
|
125
movement/watch_faces/complication/butterfly_game_face.h
Normal file
125
movement/watch_faces/complication/butterfly_game_face.h
Normal 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_
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Joseph Bryant
|
||||
* Copyright (c) 2023 Konrad Rieck
|
||||
* Copyright (c) 2022 Wesley Ellis
|
||||
*
|
||||
@ -68,17 +69,30 @@ static inline void button_beep(movement_settings_t *settings) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
|
||||
}
|
||||
|
||||
static void start(countdown_state_t *state, movement_settings_t *settings) {
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
static void schedule_countdown(countdown_state_t *state, movement_settings_t *settings) {
|
||||
|
||||
state->mode = cd_running;
|
||||
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
|
||||
state->target_ts = watch_utility_offset_timestamp(state->now_ts, state->hours, state->minutes, state->seconds);
|
||||
// Calculate the new state->now_ts but don't update it until we've updated the target -
|
||||
// avoid possible race where the old target is compared to the new time and immediately triggers
|
||||
uint32_t new_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), get_tz_offset(settings));
|
||||
state->target_ts = watch_utility_offset_timestamp(new_now, state->hours, state->minutes, state->seconds);
|
||||
state->now_ts = new_now;
|
||||
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings));
|
||||
movement_schedule_background_task(target_dt);
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
|
||||
}
|
||||
|
||||
static void auto_repeat(countdown_state_t *state, movement_settings_t *settings) {
|
||||
movement_play_alarm();
|
||||
load_countdown(state);
|
||||
schedule_countdown(state, settings);
|
||||
}
|
||||
|
||||
static void start(countdown_state_t *state, movement_settings_t *settings) {
|
||||
state->mode = cd_running;
|
||||
schedule_countdown(state, settings);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void draw(countdown_state_t *state, uint8_t subsecond) {
|
||||
char buf[16];
|
||||
|
||||
@ -87,7 +101,10 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
|
||||
|
||||
switch (state->mode) {
|
||||
case cd_running:
|
||||
delta = state->target_ts - state->now_ts;
|
||||
if (state->target_ts <= state->now_ts)
|
||||
delta = 0;
|
||||
else
|
||||
delta = state->target_ts - state->now_ts;
|
||||
result = div(delta, 60);
|
||||
state->seconds = result.rem;
|
||||
result = div(result.quot, 60);
|
||||
@ -97,6 +114,7 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
|
||||
break;
|
||||
case cd_reset:
|
||||
case cd_paused:
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
sprintf(buf, "CD %2d%02d%02d", state->hours, state->minutes, state->seconds);
|
||||
break;
|
||||
case cd_setting:
|
||||
@ -123,14 +141,13 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
|
||||
|
||||
static void pause(countdown_state_t *state) {
|
||||
state->mode = cd_paused;
|
||||
movement_cancel_background_task();
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
movement_cancel_background_task_for_face(state->watch_face_index);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
|
||||
static void reset(countdown_state_t *state) {
|
||||
state->mode = cd_reset;
|
||||
movement_cancel_background_task();
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
movement_cancel_background_task_for_face(state->watch_face_index);
|
||||
load_countdown(state);
|
||||
}
|
||||
|
||||
@ -139,6 +156,15 @@ static void ring(countdown_state_t *state) {
|
||||
reset(state);
|
||||
}
|
||||
|
||||
static void times_up(movement_settings_t *settings, countdown_state_t *state) {
|
||||
if(state->repeat) {
|
||||
auto_repeat(state, settings);
|
||||
}
|
||||
else {
|
||||
ring(state);
|
||||
}
|
||||
}
|
||||
|
||||
static void settings_increment(countdown_state_t *state) {
|
||||
switch(state->selection) {
|
||||
case 0:
|
||||
@ -167,6 +193,7 @@ void countdown_face_setup(movement_settings_t *settings, uint8_t watch_face_inde
|
||||
memset(*context_ptr, 0, sizeof(countdown_state_t));
|
||||
state->minutes = DEFAULT_MINUTES;
|
||||
state->mode = cd_reset;
|
||||
state->watch_face_index = watch_face_index;
|
||||
store_countdown(state);
|
||||
}
|
||||
}
|
||||
@ -177,9 +204,11 @@ void countdown_face_activate(movement_settings_t *settings, void *context) {
|
||||
if(state->mode == cd_running) {
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
watch_set_colon();
|
||||
if(state->repeat)
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
|
||||
movement_request_tick_frequency(1);
|
||||
quick_ticks_running = false;
|
||||
@ -249,6 +278,7 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
|
||||
// Only start the timer if we have a valid time.
|
||||
start(state, settings);
|
||||
button_beep(settings);
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
break;
|
||||
case cd_setting:
|
||||
@ -258,9 +288,19 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
|
||||
draw(state, event.subsecond);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
if (state->mode == cd_setting) {
|
||||
quick_ticks_running = true;
|
||||
movement_request_tick_frequency(8);
|
||||
switch(state->mode) {
|
||||
case cd_setting:
|
||||
quick_ticks_running = true;
|
||||
movement_request_tick_frequency(8);
|
||||
break;
|
||||
default:
|
||||
// Toggle auto-repeat
|
||||
button_beep(settings);
|
||||
state->repeat = !state->repeat;
|
||||
if(state->repeat)
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
@ -282,7 +322,7 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
|
||||
abort_quick_ticks(state);
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
ring(state);
|
||||
times_up(settings, state);
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
abort_quick_ticks(state);
|
||||
|
@ -62,6 +62,8 @@ typedef struct {
|
||||
uint8_t set_seconds;
|
||||
uint8_t selection;
|
||||
countdown_mode_t mode;
|
||||
bool repeat;
|
||||
uint8_t watch_face_index;
|
||||
} countdown_state_t;
|
||||
|
||||
|
||||
|
@ -26,8 +26,7 @@
|
||||
#include <string.h>
|
||||
#include "day_one_face.h"
|
||||
#include "watch.h"
|
||||
|
||||
static const uint8_t days_in_month[12] = {31, 29, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};
|
||||
#include "watch_utility.h"
|
||||
|
||||
static uint32_t _day_one_face_juliandaynum(uint16_t year, uint16_t month, uint16_t day) {
|
||||
// from here: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation
|
||||
@ -66,13 +65,12 @@ static void _day_one_face_increment(day_one_state_t *state) {
|
||||
break;
|
||||
case PAGE_DAY:
|
||||
state->birth_day = state->birth_day + 1;
|
||||
if (state->birth_day == 0 || state->birth_day > days_in_month[state->birth_month - 1]) {
|
||||
state->birth_day = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (state->birth_day == 0 || state->birth_day > days_in_month(state->birth_month, state->birth_year))
|
||||
state->birth_day = 1;
|
||||
}
|
||||
|
||||
void day_one_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
|
||||
|
649
movement/watch_faces/complication/deadline_face.c
Normal file
649
movement/watch_faces/complication/deadline_face.c
Normal file
@ -0,0 +1,649 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023-2024 Konrad Rieck
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* # Deadline Face
|
||||
*
|
||||
* This is a watch face for tracking deadlines. It draws inspiration from
|
||||
* other watch faces of the project but focuses on keeping track of
|
||||
* deadlines. You can enter and monitor up to four different deadlines by
|
||||
* providing their respective date and time. The face has two modes:
|
||||
* *running mode* and *settings mode*.
|
||||
*
|
||||
* ## Running Mode
|
||||
*
|
||||
* When the watch face is activated, it defaults to running mode. The top
|
||||
* right corner shows the current deadline number, and the main display
|
||||
* presents the time left until the deadline. The format of the display
|
||||
* varies depending on the remaining time.
|
||||
*
|
||||
* - When less than a day is left, the display shows the remaining hours,
|
||||
* minutes, and seconds in the form `HH:MM:SS`.
|
||||
*
|
||||
* - When less than a month is left, the display shows the remaining days
|
||||
* and hours in the form `DD:HH` with the unit `dy` for days.
|
||||
*
|
||||
* - When less than a year is left, the display shows the remaining months
|
||||
* and days in the form `MM:DD` with the unit `mo` for months.
|
||||
*
|
||||
* - When more than a year is left, the years and months are displayed in
|
||||
* the form `YY:MM` with the unit `yr` for years.
|
||||
*
|
||||
* - When a deadline has passed in the last 24 hours, the display shows
|
||||
* `over` to indicate that the deadline has just recently been reached.
|
||||
*
|
||||
* - When no deadline is set for a particular slot, or if a deadline has
|
||||
* already passed by more than 24 hours, `--:--` is displayed.
|
||||
*
|
||||
* The user can navigate in running mode using the following buttons:
|
||||
*
|
||||
* - The *alarm button* moves the next deadline. There are currently four
|
||||
* slots available for deadlines. When the last slot has been reached,
|
||||
* pressing the button moves to the first slot.
|
||||
*
|
||||
* - A *long press* on the *alarm button* activates settings mode and
|
||||
* enables configuring the currently selected deadline.
|
||||
*
|
||||
* - A *long press* on the *light button* activates a deadline alarm. The
|
||||
* bell icon is displayed, and the alarm will ring upon reaching any of
|
||||
* the deadlines set. It is important to note that the watch will not
|
||||
* enter low-energy sleep mode while the alarm is enabled.
|
||||
*
|
||||
*
|
||||
* ## Settings Mode
|
||||
*
|
||||
* In settings mode, the currently selected slot for a deadline can be
|
||||
* configured by providing the date and the time. Like running mode, the
|
||||
* top right corner of the display indicates the current deadline number.
|
||||
* The main display shows the date and, on the next page, the time to be
|
||||
* configured.
|
||||
*
|
||||
* The user can use the following buttons in settings mode.
|
||||
*
|
||||
* - The *light button* navigates through the different date and time
|
||||
* settings, going from year, month, day, hour, to minute. The selected
|
||||
* position is blinking.
|
||||
*
|
||||
* - A *long press* on the light button resets the date and time to the next
|
||||
* day at midnight. This is the default deadline.
|
||||
*
|
||||
* - The *alarm button* increments the currently selected position. A *long
|
||||
* press* on the *alarm button* changes the value faster.
|
||||
*
|
||||
* - The *mode button* exists setting mode and returns to *running mode*.
|
||||
* Here the selected deadline slot can be changed.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "deadline_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
#define SETTINGS_NUM (5)
|
||||
const char settings_titles[SETTINGS_NUM][3] = { "YR", "MO", "DA", "HR", "M1" };
|
||||
|
||||
/* Local functions */
|
||||
static void _running_init(movement_settings_t *settings, deadline_state_t *state);
|
||||
static bool _running_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
static void _running_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state);
|
||||
static void _setting_init(movement_settings_t *settings, deadline_state_t *state);
|
||||
static bool _setting_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
static void _setting_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state, watch_date_time date);
|
||||
|
||||
/* Utility functions */
|
||||
static void _background_alarm_play(movement_settings_t *settings, deadline_state_t *state);
|
||||
static void _background_alarm_schedule(movement_settings_t *settings, deadline_state_t *state);
|
||||
static void _background_alarm_cancel(movement_settings_t *settings, deadline_state_t *state);
|
||||
static void _increment_date(movement_settings_t *settings, deadline_state_t *state, watch_date_time date_time);
|
||||
static inline int32_t _get_tz_offset(movement_settings_t *settings);
|
||||
static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state);
|
||||
static inline bool _is_leap(int16_t y);
|
||||
static inline int _days_in_month(int16_t mpnth, int16_t y);
|
||||
static inline unsigned int _mod(int a, int b);
|
||||
static inline void _beep_button(movement_settings_t *settings);
|
||||
static inline void _beep_enable(movement_settings_t *settings);
|
||||
static inline void _beep_disable(movement_settings_t *settings);
|
||||
static inline void _reset_deadline(movement_settings_t *settings, deadline_state_t *state);
|
||||
|
||||
/* Check for leap year */
|
||||
static inline bool _is_leap(int16_t y)
|
||||
{
|
||||
y += 1900;
|
||||
return !(y % 4) && ((y % 100) || !(y % 400));
|
||||
}
|
||||
|
||||
/* Modulo function */
|
||||
static inline unsigned int _mod(int a, int b)
|
||||
{
|
||||
int r = a % b;
|
||||
return r < 0 ? r + b : r;
|
||||
}
|
||||
|
||||
/* Return days in month */
|
||||
static inline int _days_in_month(int16_t month, int16_t year)
|
||||
{
|
||||
uint8_t days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
month = _mod(month - 1, 12);
|
||||
|
||||
if (month == 1 && _is_leap(year)) {
|
||||
return days[month] + 1;
|
||||
} else {
|
||||
return days[month];
|
||||
}
|
||||
}
|
||||
|
||||
/* Return time zone offset */
|
||||
static inline int32_t _get_tz_offset(movement_settings_t *settings)
|
||||
{
|
||||
return movement_timezone_offsets[settings->bit.time_zone] * 60;
|
||||
}
|
||||
|
||||
/* Beep for a button press*/
|
||||
static inline void _beep_button(movement_settings_t *settings)
|
||||
{
|
||||
// Play a beep as confirmation for a button press (if applicable)
|
||||
if (!settings->bit.button_should_sound)
|
||||
return;
|
||||
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
|
||||
}
|
||||
|
||||
/* Beep for entering settings */
|
||||
static inline void _beep_enable(movement_settings_t *settings)
|
||||
{
|
||||
if (!settings->bit.button_should_sound)
|
||||
return;
|
||||
|
||||
watch_buzzer_play_note(BUZZER_NOTE_G7, 50);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
|
||||
}
|
||||
|
||||
/* Beep for leaving settings */
|
||||
static inline void _beep_disable(movement_settings_t *settings)
|
||||
{
|
||||
if (!settings->bit.button_should_sound)
|
||||
return;
|
||||
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_G7, 75);
|
||||
}
|
||||
|
||||
/* Change tick frequency */
|
||||
static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state)
|
||||
{
|
||||
if (state->tick_freq != freq) {
|
||||
movement_request_tick_frequency(freq);
|
||||
state->tick_freq = freq;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine index of closest deadline */
|
||||
static uint8_t _closest_deadline(movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings));
|
||||
uint32_t min_ts = UINT32_MAX;
|
||||
uint8_t min_index = 0;
|
||||
|
||||
for (uint8_t i = 0; i < DEADLINE_FACE_DATES; i++) {
|
||||
if (state->deadlines[i] < now_ts || state->deadlines[i] > min_ts)
|
||||
continue;
|
||||
min_ts = state->deadlines[i];
|
||||
min_index = i;
|
||||
}
|
||||
|
||||
return min_index;
|
||||
}
|
||||
|
||||
/* Play background alarm */
|
||||
static void _background_alarm_play(movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
(void) settings;
|
||||
|
||||
/* Use the default alarm from movement and move to foreground */
|
||||
if (state->alarm_enabled) {
|
||||
movement_play_alarm();
|
||||
movement_move_to_face(state->face_idx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule background alarm */
|
||||
static void _background_alarm_schedule(movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
/* We simply re-use the scheduling in the background task */
|
||||
deadline_face_wants_background_task(settings, state);
|
||||
}
|
||||
|
||||
/* Cancel background alarm */
|
||||
static void _background_alarm_cancel(movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
(void) settings;
|
||||
|
||||
movement_cancel_background_task_for_face(state->face_idx);
|
||||
}
|
||||
|
||||
/* Reset deadline to tomorrow */
|
||||
static inline void _reset_deadline(movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
/* Get current time and reset hours/minutes/seconds */
|
||||
watch_date_time date_time = watch_rtc_get_date_time();
|
||||
date_time.unit.second = 0;
|
||||
date_time.unit.minute = 0;
|
||||
date_time.unit.hour = 0;
|
||||
|
||||
/* Add 24 hours to obtain first second of tomorrow */
|
||||
uint32_t ts = watch_utility_date_time_to_unix_time(date_time, _get_tz_offset(settings));
|
||||
ts += 24 * 60 * 60;
|
||||
|
||||
state->deadlines[state->current_index] = ts;
|
||||
}
|
||||
|
||||
/* Increment date in settings mode. Function taken from `set_time_face.c` */
|
||||
static void _increment_date(movement_settings_t *settings, deadline_state_t *state, watch_date_time date_time)
|
||||
{
|
||||
const uint8_t days_in_month[12] = { 31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31 };
|
||||
|
||||
switch (state->current_page) {
|
||||
case 0:
|
||||
/* Only 10 years covered. Fix this sometime next decade */
|
||||
date_time.unit.year = ((date_time.unit.year % 10) + 1);
|
||||
break;
|
||||
case 1:
|
||||
date_time.unit.month = (date_time.unit.month % 12) + 1;
|
||||
break;
|
||||
case 2:
|
||||
date_time.unit.day = date_time.unit.day + 1;
|
||||
|
||||
/* Check for leap years */
|
||||
int8_t days = days_in_month[date_time.unit.month - 1];
|
||||
if (date_time.unit.month == 2 && _is_leap(date_time.unit.year))
|
||||
days++;
|
||||
|
||||
if (date_time.unit.day > days)
|
||||
date_time.unit.day = 1;
|
||||
break;
|
||||
case 3:
|
||||
date_time.unit.hour = (date_time.unit.hour + 1) % 24;
|
||||
break;
|
||||
case 4:
|
||||
date_time.unit.minute = (date_time.unit.minute + 1) % 60;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t ts = watch_utility_date_time_to_unix_time(date_time, _get_tz_offset(settings));
|
||||
state->deadlines[state->current_index] = ts;
|
||||
}
|
||||
|
||||
/* Update display in running mode */
|
||||
static void _running_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
(void) event;
|
||||
(void) settings;
|
||||
|
||||
/* Seconds, minutes, hours, days, months, years */
|
||||
int16_t unit[] = { 0, 0, 0, 0, 0, 0 };
|
||||
uint8_t i, range[] = { 60, 60, 24, 30, 12, 0 };
|
||||
char buf[16];
|
||||
|
||||
/* Display indicators */
|
||||
if (state->alarm_enabled)
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings));
|
||||
|
||||
/* Deadline expired */
|
||||
if (state->deadlines[state->current_index] < now_ts) {
|
||||
if (state->deadlines[state->current_index] + 24 * 60 * 60 > now_ts)
|
||||
sprintf(buf, "DL%2dOVER ", state->current_index + 1);
|
||||
else
|
||||
sprintf(buf, "DL%2d---- ", state->current_index + 1);
|
||||
|
||||
//watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_display_string(buf, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get date time structs */
|
||||
watch_date_time deadline = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], _get_tz_offset(settings)
|
||||
);
|
||||
|
||||
/* Calculate naive difference of dates */
|
||||
unit[0] = deadline.unit.second - now.unit.second;
|
||||
unit[1] = deadline.unit.minute - now.unit.minute;
|
||||
unit[2] = deadline.unit.hour - now.unit.hour;
|
||||
unit[3] = deadline.unit.day - now.unit.day;
|
||||
unit[4] = deadline.unit.month - now.unit.month;
|
||||
unit[5] = deadline.unit.year - now.unit.year;
|
||||
|
||||
/* Correct errors of naive difference */
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (unit[i] < 0) {
|
||||
/* Correct remaining units */
|
||||
if (i == 3)
|
||||
unit[i] += _days_in_month(deadline.unit.month - 1, deadline.unit.year);
|
||||
else
|
||||
unit[i] += range[i];
|
||||
|
||||
/* Carry over change to next unit if non-zero */
|
||||
if (i < 5 && unit[i + 1] != 0)
|
||||
unit[i + 1] -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set range */
|
||||
i = state->current_index + 1;
|
||||
if (unit[5] > 0) {
|
||||
/* years:months */
|
||||
sprintf(buf, "DL%2d%02d%02dYR", i, unit[5] % 100, unit[4] % 12);
|
||||
} else if (unit[4] > 0) {
|
||||
/* months:days */
|
||||
sprintf(buf, "DL%2d%02d%02dMO", i, (unit[5] * 12 + unit[4]) % 100, unit[3] % 32);
|
||||
} else if (unit[3] > 0) {
|
||||
/* days:hours */
|
||||
sprintf(buf, "DL%2d%02d%02ddY", i, unit[3] % 32, unit[2] % 24);
|
||||
} else {
|
||||
/* hours:minutes:seconds */
|
||||
sprintf(buf, "DL%2d%02d%02d%02d", i, unit[2] % 24, unit[1] % 60, unit[0] % 60);
|
||||
}
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
|
||||
/* Init running mode */
|
||||
static void _running_init(movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
(void) settings;
|
||||
(void) state;
|
||||
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_set_colon();
|
||||
|
||||
/* Ensure 1Hz updates only */
|
||||
_change_tick_freq(1, state);
|
||||
}
|
||||
|
||||
/* Loop of running mode */
|
||||
static bool _running_loop(movement_event_t event, movement_settings_t *settings, void *context)
|
||||
{
|
||||
deadline_state_t *state = (deadline_state_t *) context;
|
||||
|
||||
if (event.event_type != EVENT_BACKGROUND_TASK)
|
||||
_running_display(event, settings, state);
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
_beep_button(settings);
|
||||
state->current_index = (state->current_index + 1) % DEADLINE_FACE_DATES;
|
||||
_running_display(event, settings, state);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
_beep_enable(settings);
|
||||
_setting_init(settings, state);
|
||||
state->mode = DEADLINE_FACE_SETTING;
|
||||
break;
|
||||
case EVENT_MODE_BUTTON_UP:
|
||||
movement_move_to_next_face();
|
||||
return false;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
_beep_button(settings);
|
||||
state->alarm_enabled = !state->alarm_enabled;
|
||||
if (state->alarm_enabled) {
|
||||
_background_alarm_schedule(settings, context);
|
||||
} else {
|
||||
_background_alarm_cancel(settings, context);
|
||||
}
|
||||
_running_display(event, settings, state);
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
_background_alarm_play(settings, state);
|
||||
break;
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Update display in settings mode */
|
||||
static void _setting_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state, watch_date_time date_time)
|
||||
{
|
||||
char buf[11];
|
||||
|
||||
int i = state->current_index + 1;
|
||||
if (state->current_page > 2) {
|
||||
watch_set_colon();
|
||||
if (settings->bit.clock_mode_24h) {
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
sprintf(buf, "%s%2d%2d%02d ", settings_titles[state->current_page], i, date_time.unit.hour, date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buf, "%s%2d%2d%02d ", settings_titles[state->current_page], i, (date_time.unit.hour % 12) ? (date_time.unit.hour % 12) : 12,
|
||||
date_time.unit.minute);
|
||||
if (date_time.unit.hour < 12)
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
else
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
} else {
|
||||
watch_clear_colon();
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
sprintf(buf, "%s%2d%2d%02d%02d", settings_titles[state->current_page], i, date_time.unit.year + 20, date_time.unit.month, date_time.unit.day);
|
||||
}
|
||||
|
||||
/* Blink up the parameter we are setting */
|
||||
if (event.subsecond % 2) {
|
||||
switch (state->current_page) {
|
||||
case 0:
|
||||
case 3:
|
||||
buf[4] = buf[5] = ' ';
|
||||
break;
|
||||
case 1:
|
||||
case 4:
|
||||
buf[6] = buf[7] = ' ';
|
||||
break;
|
||||
case 2:
|
||||
buf[8] = buf[9] = ' ';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
|
||||
/* Init setting mode */
|
||||
static void _setting_init(movement_settings_t *settings, deadline_state_t *state)
|
||||
{
|
||||
state->current_page = 0;
|
||||
|
||||
/* Init fresh deadline to next day */
|
||||
if (state->deadlines[state->current_index] == 0) {
|
||||
_reset_deadline(settings, state);
|
||||
}
|
||||
|
||||
/* Ensure 1Hz updates only */
|
||||
_change_tick_freq(1, state);
|
||||
}
|
||||
|
||||
/* Loop of setting mode */
|
||||
static bool _setting_loop(movement_event_t event, movement_settings_t *settings, void *context)
|
||||
{
|
||||
deadline_state_t *state = (deadline_state_t *) context;
|
||||
watch_date_time date_time;
|
||||
date_time = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], _get_tz_offset(settings));
|
||||
|
||||
if (event.event_type != EVENT_BACKGROUND_TASK)
|
||||
_setting_display(event, settings, state, date_time);
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_TICK:
|
||||
if (state->tick_freq == 8) {
|
||||
if (watch_get_pin_level(BTN_ALARM)) {
|
||||
_increment_date(settings, state, date_time);
|
||||
_setting_display(event, settings, state, date_time);
|
||||
} else {
|
||||
_change_tick_freq(4, state);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
_change_tick_freq(8, state);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_UP:
|
||||
_change_tick_freq(4, state);
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
_beep_button(settings);
|
||||
_reset_deadline(settings, state);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
state->current_page = (state->current_page + 1) % SETTINGS_NUM;
|
||||
_setting_display(event, settings, state, date_time);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
_change_tick_freq(4, state);
|
||||
_increment_date(settings, state, date_time);
|
||||
_setting_display(event, settings, state, date_time);
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
_beep_button(settings);
|
||||
_background_alarm_schedule(settings, context);
|
||||
_change_tick_freq(1, state);
|
||||
state->mode = DEADLINE_FACE_RUNNING;
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_MODE_BUTTON_UP:
|
||||
_beep_disable(settings);
|
||||
_background_alarm_schedule(settings, context);
|
||||
_running_init(settings, state);
|
||||
_running_display(event, settings, state);
|
||||
state->mode = DEADLINE_FACE_RUNNING;
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
_background_alarm_play(settings, state);
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Setup face */
|
||||
void deadline_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr)
|
||||
{
|
||||
(void) settings;
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr != NULL)
|
||||
return; /* Skip setup if context available */
|
||||
|
||||
/* Allocate state */
|
||||
*context_ptr = malloc(sizeof(deadline_state_t));
|
||||
memset(*context_ptr, 0, sizeof(deadline_state_t));
|
||||
|
||||
/* Store face index for background tasks */
|
||||
deadline_state_t *state = (deadline_state_t *) *context_ptr;
|
||||
state->face_idx = watch_face_index;
|
||||
}
|
||||
|
||||
/* Activate face */
|
||||
void deadline_face_activate(movement_settings_t *settings, void *context)
|
||||
{
|
||||
(void) settings;
|
||||
deadline_state_t *state = (deadline_state_t *) context;
|
||||
|
||||
/* Set display options */
|
||||
_running_init(settings, state);
|
||||
state->mode = DEADLINE_FACE_RUNNING;
|
||||
state->current_index = _closest_deadline(settings, state);
|
||||
}
|
||||
|
||||
/* Loop face */
|
||||
bool deadline_face_loop(movement_event_t event, movement_settings_t *settings, void *context)
|
||||
{
|
||||
(void) settings;
|
||||
deadline_state_t *state = (deadline_state_t *) context;
|
||||
switch (state->mode) {
|
||||
case DEADLINE_FACE_SETTING:
|
||||
_setting_loop(event, settings, context);
|
||||
break;
|
||||
default:
|
||||
case DEADLINE_FACE_RUNNING:
|
||||
_running_loop(event, settings, context);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Resign face */
|
||||
void deadline_face_resign(movement_settings_t *settings, void *context)
|
||||
{
|
||||
(void) settings;
|
||||
(void) context;
|
||||
}
|
||||
|
||||
/* Want background task */
|
||||
bool deadline_face_wants_background_task(movement_settings_t *settings, void *context)
|
||||
{
|
||||
deadline_state_t *state = (deadline_state_t *) context;
|
||||
|
||||
if (!state->alarm_enabled)
|
||||
return false;
|
||||
|
||||
/* Determine closest deadline */
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings));
|
||||
uint32_t next_ts = state->deadlines[_closest_deadline(settings, state)];
|
||||
|
||||
/* No active deadline */
|
||||
if (next_ts < now_ts)
|
||||
return false;
|
||||
|
||||
/* No deadline within next 60 seconds */
|
||||
if (next_ts >= now_ts + 60)
|
||||
return false;
|
||||
|
||||
/* Deadline within next minute. Let's set up an alarm */
|
||||
watch_date_time next = watch_utility_date_time_from_unix_time(next_ts, _get_tz_offset(settings));
|
||||
movement_request_wake();
|
||||
movement_schedule_background_task_for_face(state->face_idx, next);
|
||||
return false;
|
||||
}
|
65
movement/watch_faces/complication/deadline_face.h
Normal file
65
movement/watch_faces/complication/deadline_face.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023-2024 Konrad Rieck
|
||||
*
|
||||
* 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 DEADLINE_FACE_H_
|
||||
#define DEADLINE_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/* Modes of face */
|
||||
typedef enum {
|
||||
DEADLINE_FACE_RUNNING = 0,
|
||||
DEADLINE_FACE_SETTING
|
||||
} deadline_mode_t;
|
||||
|
||||
/* Number of deadline dates */
|
||||
#define DEADLINE_FACE_DATES (4)
|
||||
|
||||
/* Deadline configuration */
|
||||
typedef struct {
|
||||
deadline_mode_t mode:1;
|
||||
uint8_t current_page:3;
|
||||
uint8_t current_index:2;
|
||||
uint8_t alarm_enabled:1;
|
||||
uint8_t tick_freq;
|
||||
uint8_t face_idx;
|
||||
uint32_t deadlines[DEADLINE_FACE_DATES];
|
||||
} deadline_state_t;
|
||||
|
||||
void deadline_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr);
|
||||
void deadline_face_activate(movement_settings_t *settings, void *context);
|
||||
bool deadline_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
void deadline_face_resign(movement_settings_t *settings, void *context);
|
||||
bool deadline_face_wants_background_task(movement_settings_t *settings, void *context);
|
||||
|
||||
#define deadline_face ((const watch_face_t){ \
|
||||
deadline_face_setup, \
|
||||
deadline_face_activate, \
|
||||
deadline_face_loop, \
|
||||
deadline_face_resign, \
|
||||
deadline_face_wants_background_task \
|
||||
})
|
||||
|
||||
#endif // DEADLINE_FACE_H_
|
617
movement/watch_faces/complication/endless_runner_face.c
Normal file
617
movement/watch_faces/complication/endless_runner_face.c
Normal file
@ -0,0 +1,617 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 <David Volovskiy>
|
||||
*
|
||||
* 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 "endless_runner_face.h"
|
||||
|
||||
typedef enum {
|
||||
JUMPING_FINAL_FRAME = 0,
|
||||
NOT_JUMPING,
|
||||
JUMPING_START,
|
||||
} RunnerJumpState;
|
||||
|
||||
typedef enum {
|
||||
SCREEN_TITLE = 0,
|
||||
SCREEN_PLAYING,
|
||||
SCREEN_LOSE,
|
||||
SCREEN_TIME,
|
||||
SCREEN_COUNT
|
||||
} RunnerCurrScreen;
|
||||
|
||||
typedef enum {
|
||||
DIFF_BABY = 0, // FREQ_SLOW FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES_EASY frames
|
||||
DIFF_EASY, // FREQ FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES_EASY frames
|
||||
DIFF_NORM, // FREQ FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES frames
|
||||
DIFF_HARD, // FREQ FPS; MIN_ZEROES_HARD 0's min; jump is JUMP_FRAMES frames
|
||||
DIFF_FUEL, // Mode where the top-right displays the amoount of fuel that you can be above the ground for, dodging obstacles. When on the ground, your fuel recharges.
|
||||
DIFF_FUEL_1, // Same as DIFF_FUEL, but if your fuel is 0, then you won't recharge
|
||||
DIFF_COUNT
|
||||
} RunnerDifficulty;
|
||||
|
||||
#define NUM_GRID 12 // This the length that the obstacle track can be on
|
||||
#define FREQ 8 // Frequency request for the game
|
||||
#define FREQ_SLOW 4 // Frequency request for baby mode
|
||||
#define JUMP_FRAMES 2 // Wait this many frames on difficulties above EASY before coming down from the jump button pressed
|
||||
#define JUMP_FRAMES_EASY 3 // Wait this many frames on difficulties at or below EASY before coming down from the jump button pressed
|
||||
#define MIN_ZEROES 4 // At minimum, we'll have this many spaces between obstacles
|
||||
#define MIN_ZEROES_HARD 3 // At minimum, we'll have this many spaces between obstacles on hard mode
|
||||
#define MAX_HI_SCORE 9999 // Max hi score to store and display on the title screen.
|
||||
#define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39
|
||||
#define JUMP_FRAMES_FUEL 30 // The max fuel that fuel that the fuel mode game will hold
|
||||
#define JUMP_FRAMES_FUEL_RECHARGE 3 // How much fuel each frame on the ground adds
|
||||
#define MAX_DISP_SCORE_FUEL 9 // Since the fuel mode displays the score in the weekday slot, two digits will display wonky data
|
||||
|
||||
typedef struct {
|
||||
uint32_t obst_pattern;
|
||||
uint16_t obst_indx : 8;
|
||||
uint16_t jump_state : 5;
|
||||
uint16_t sec_before_moves : 3;
|
||||
uint16_t curr_score : 10;
|
||||
uint16_t curr_screen : 4;
|
||||
bool loc_2_on;
|
||||
bool loc_3_on;
|
||||
bool success_jump;
|
||||
bool fuel_mode;
|
||||
uint8_t fuel;
|
||||
} game_state_t;
|
||||
|
||||
static game_state_t game_state;
|
||||
static const uint8_t _num_bits_obst_pattern = sizeof(game_state.obst_pattern) * 8;
|
||||
|
||||
static void print_binary(uint32_t value, int bits) {
|
||||
#if __EMSCRIPTEN__
|
||||
for (int i = bits - 1; i >= 0; i--) {
|
||||
// Print each bit
|
||||
printf("%lu", (value >> i) & 1);
|
||||
// Optional: add a space every 4 bits for readability
|
||||
if (i % 4 == 0 && i != 0) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
#else
|
||||
(void) value;
|
||||
(void) bits;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32_t get_random(uint32_t max) {
|
||||
#if __EMSCRIPTEN__
|
||||
return rand() % max;
|
||||
#else
|
||||
return arc4random_uniform(max);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t get_random_nonzero(uint32_t max) {
|
||||
uint32_t random;
|
||||
do
|
||||
{
|
||||
random = get_random(max);
|
||||
} while (random == 0);
|
||||
return random;
|
||||
}
|
||||
|
||||
static uint32_t get_random_kinda_nonzero(uint32_t max) {
|
||||
// Returns a number that's between 1 and max, unless max is 0 or 1, then it returns 0 to max.
|
||||
if (max == 0) return 0;
|
||||
else if (max == 1) return get_random(max);
|
||||
return get_random_nonzero(max);
|
||||
}
|
||||
|
||||
static uint32_t get_random_fuel(uint32_t prev_val) {
|
||||
static uint8_t prev_rand_subset = 0;
|
||||
uint32_t rand;
|
||||
uint8_t max_ones, subset;
|
||||
uint32_t rand_legal = 0;
|
||||
prev_val = prev_val & ~0xFFFF;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
subset = 0;
|
||||
max_ones = 8;
|
||||
if (prev_rand_subset > 4)
|
||||
max_ones -= prev_rand_subset;
|
||||
rand = get_random_kinda_nonzero(max_ones);
|
||||
if (rand > 5 && prev_rand_subset) rand = 5; // The gap of one or two is awkward
|
||||
for (uint32_t j = 0; j < rand; j++) {
|
||||
subset |= (1 << j);
|
||||
}
|
||||
if (prev_rand_subset >= 7)
|
||||
subset = subset << 1;
|
||||
subset &= 0xFF;
|
||||
rand_legal |= subset << (8 * i);
|
||||
prev_rand_subset = rand;
|
||||
}
|
||||
|
||||
rand_legal = prev_val | rand_legal;
|
||||
print_binary(rand_legal, 32);
|
||||
return rand_legal;
|
||||
}
|
||||
|
||||
static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) {
|
||||
/** @brief A legal random number starts with the previous number (which should be the 12 bits on the screen).
|
||||
* @param prev_val The previous value to tack onto. The return will have its first NUM_GRID MSBs be the same as prev_val, and the rest be new
|
||||
* @param difficulty To dictate how spread apart the obsticles must be
|
||||
* @return the new random value, where it's first NUM_GRID MSBs are the same as prev_val
|
||||
*/
|
||||
uint8_t min_zeros = (difficulty == DIFF_HARD) ? MIN_ZEROES_HARD : MIN_ZEROES;
|
||||
uint32_t max = (1 << (_num_bits_obst_pattern - NUM_GRID)) - 1;
|
||||
uint32_t rand = get_random_nonzero(max);
|
||||
uint32_t rand_legal = 0;
|
||||
prev_val = prev_val & ~max;
|
||||
|
||||
for (int i = (NUM_GRID + 1); i <= _num_bits_obst_pattern; i++) {
|
||||
uint32_t mask = 1 << (_num_bits_obst_pattern - i);
|
||||
bool msb = (rand & mask) >> (_num_bits_obst_pattern - i);
|
||||
if (msb) {
|
||||
rand_legal = rand_legal << min_zeros;
|
||||
i+=min_zeros;
|
||||
}
|
||||
rand_legal |= msb;
|
||||
rand_legal = rand_legal << 1;
|
||||
}
|
||||
|
||||
rand_legal = rand_legal & max;
|
||||
for (int i = 0; i <= min_zeros; i++) {
|
||||
if (prev_val & (1 << (i + _num_bits_obst_pattern - NUM_GRID))){
|
||||
rand_legal = rand_legal >> (min_zeros - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rand_legal = prev_val | rand_legal;
|
||||
print_binary(rand_legal, 32);
|
||||
return rand_legal;
|
||||
}
|
||||
|
||||
static void display_ball(bool jumping) {
|
||||
if (!jumping) {
|
||||
watch_set_pixel(0, 21);
|
||||
watch_set_pixel(1, 21);
|
||||
watch_set_pixel(0, 20);
|
||||
watch_set_pixel(1, 20);
|
||||
watch_clear_pixel(1, 17);
|
||||
watch_clear_pixel(2, 20);
|
||||
watch_clear_pixel(2, 21);
|
||||
}
|
||||
else {
|
||||
watch_clear_pixel(0, 21);
|
||||
watch_clear_pixel(1, 21);
|
||||
watch_clear_pixel(0, 20);
|
||||
watch_set_pixel(1, 20);
|
||||
watch_set_pixel(1, 17);
|
||||
watch_set_pixel(2, 20);
|
||||
watch_set_pixel(2, 21);
|
||||
}
|
||||
}
|
||||
|
||||
static void display_score(uint8_t score) {
|
||||
char buf[3];
|
||||
if (game_state.fuel_mode) {
|
||||
score %= (MAX_DISP_SCORE_FUEL + 1);
|
||||
sprintf(buf, "%1d", score);
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
else {
|
||||
score %= (MAX_DISP_SCORE + 1);
|
||||
sprintf(buf, "%2d", score);
|
||||
watch_display_string(buf, 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_to_score(endless_runner_state_t *state) {
|
||||
if (game_state.curr_score <= MAX_HI_SCORE) {
|
||||
game_state.curr_score++;
|
||||
if (game_state.curr_score > state -> hi_score)
|
||||
state -> hi_score = game_state.curr_score;
|
||||
}
|
||||
game_state.success_jump = true;
|
||||
display_score(game_state.curr_score);
|
||||
}
|
||||
|
||||
static void display_fuel(uint8_t subsecond, uint8_t difficulty) {
|
||||
char buf[4];
|
||||
if (difficulty == DIFF_FUEL_1 && game_state.fuel == 0 && subsecond % (FREQ/2) == 0) {
|
||||
watch_display_string(" ", 2); // Blink the 0 fuel to show it cannot be refilled.
|
||||
return;
|
||||
}
|
||||
sprintf(buf, "%2d", game_state.fuel);
|
||||
watch_display_string(buf, 2);
|
||||
}
|
||||
|
||||
static void check_and_reset_hi_score(endless_runner_state_t *state) {
|
||||
// Resets the hi score at the beginning of each month.
|
||||
watch_date_time date_time = watch_rtc_get_date_time();
|
||||
if ((state -> year_last_hi_score != date_time.unit.year) ||
|
||||
(state -> month_last_hi_score != date_time.unit.month))
|
||||
{
|
||||
// The high score resets itself every new month.
|
||||
state -> hi_score = 0;
|
||||
state -> year_last_hi_score = date_time.unit.year;
|
||||
state -> month_last_hi_score = date_time.unit.month;
|
||||
}
|
||||
}
|
||||
|
||||
static void display_difficulty(uint16_t difficulty) {
|
||||
switch (difficulty)
|
||||
{
|
||||
case DIFF_BABY:
|
||||
watch_display_string(" b", 2);
|
||||
break;
|
||||
case DIFF_EASY:
|
||||
watch_display_string(" E", 2);
|
||||
break;
|
||||
case DIFF_HARD:
|
||||
watch_display_string(" H", 2);
|
||||
break;
|
||||
case DIFF_FUEL:
|
||||
watch_display_string(" F", 2);
|
||||
break;
|
||||
case DIFF_FUEL_1:
|
||||
watch_display_string("1F", 2);
|
||||
break;
|
||||
case DIFF_NORM:
|
||||
default:
|
||||
watch_display_string(" N", 2);
|
||||
break;
|
||||
}
|
||||
game_state.fuel_mode = difficulty >= DIFF_FUEL && difficulty <= DIFF_FUEL_1;
|
||||
}
|
||||
|
||||
static void change_difficulty(endless_runner_state_t *state) {
|
||||
state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT;
|
||||
display_difficulty(state -> difficulty);
|
||||
if (state -> soundOn) {
|
||||
if (state -> difficulty == 0) watch_buzzer_play_note(BUZZER_NOTE_B4, 30);
|
||||
else watch_buzzer_play_note(BUZZER_NOTE_C5, 30);
|
||||
}
|
||||
}
|
||||
|
||||
static void toggle_sound(endless_runner_state_t *state) {
|
||||
state -> soundOn = !state -> soundOn;
|
||||
if (state -> soundOn){
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C5, 30);
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
}
|
||||
else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
}
|
||||
}
|
||||
|
||||
static void display_title(endless_runner_state_t *state) {
|
||||
uint16_t hi_score = state -> hi_score;
|
||||
uint8_t difficulty = state -> difficulty;
|
||||
bool sound_on = state -> soundOn;
|
||||
game_state.curr_screen = SCREEN_TITLE;
|
||||
memset(&game_state, 0, sizeof(game_state));
|
||||
game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay.
|
||||
if (sound_on) game_state.sec_before_moves--; // Start chime is about 1 second
|
||||
watch_set_colon();
|
||||
if (hi_score > MAX_HI_SCORE) {
|
||||
watch_display_string("ER HS --", 0);
|
||||
}
|
||||
else {
|
||||
char buf[14];
|
||||
sprintf(buf, "ER HS%4d", hi_score);
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
display_difficulty(difficulty);
|
||||
}
|
||||
|
||||
static void display_time(watch_date_time date_time, bool clock_mode_24h) {
|
||||
static watch_date_time previous_date_time;
|
||||
char buf[6 + 1];
|
||||
|
||||
// If the hour needs updating or it's the first time displaying the time
|
||||
if ((game_state.curr_screen != SCREEN_TIME) || (date_time.unit.hour != previous_date_time.unit.hour)) {
|
||||
uint8_t hour = date_time.unit.hour;
|
||||
game_state.curr_screen = SCREEN_TIME;
|
||||
|
||||
if (clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
else {
|
||||
if (hour >= 12) watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
hour %= 12;
|
||||
if (hour == 0) hour = 12;
|
||||
}
|
||||
watch_set_colon();
|
||||
sprintf( buf, "%2d%02d ", hour, date_time.unit.minute);
|
||||
watch_display_string(buf, 4);
|
||||
}
|
||||
// If both digits of the minute need updating
|
||||
else if ((date_time.unit.minute / 10) != (previous_date_time.unit.minute / 10)) {
|
||||
sprintf( buf, "%02d ", date_time.unit.minute);
|
||||
watch_display_string(buf, 6);
|
||||
}
|
||||
// If only the ones-place of the minute needs updating.
|
||||
else if (date_time.unit.minute != previous_date_time.unit.minute) {
|
||||
sprintf( buf, "%d ", date_time.unit.minute % 10);
|
||||
watch_display_string(buf, 7);
|
||||
}
|
||||
previous_date_time.reg = date_time.reg;
|
||||
}
|
||||
|
||||
static void begin_playing(endless_runner_state_t *state) {
|
||||
uint8_t difficulty = state -> difficulty;
|
||||
game_state.curr_screen = SCREEN_PLAYING;
|
||||
watch_clear_colon();
|
||||
movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ);
|
||||
if (game_state.fuel_mode) {
|
||||
watch_display_string(" ", 0);
|
||||
game_state.obst_pattern = get_random_fuel(0);
|
||||
if ((16 * JUMP_FRAMES_FUEL_RECHARGE) < JUMP_FRAMES_FUEL) // 16 frames of zeros at the start of a level
|
||||
game_state.fuel = JUMP_FRAMES_FUEL - (16 * JUMP_FRAMES_FUEL_RECHARGE); // Have it below its max to show it counting up when starting.
|
||||
if (game_state.fuel < JUMP_FRAMES_FUEL_RECHARGE) game_state.fuel = JUMP_FRAMES_FUEL_RECHARGE;
|
||||
}
|
||||
else {
|
||||
watch_display_string(" ", 2);
|
||||
game_state.obst_pattern = get_random_legal(0, difficulty);
|
||||
}
|
||||
game_state.jump_state = NOT_JUMPING;
|
||||
display_ball(game_state.jump_state != NOT_JUMPING);
|
||||
display_score( game_state.curr_score);
|
||||
if (state -> soundOn){
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C5, 200);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_E5, 200);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_G5, 200);
|
||||
}
|
||||
}
|
||||
|
||||
static void display_lose_screen(endless_runner_state_t *state) {
|
||||
game_state.curr_screen = SCREEN_LOSE;
|
||||
game_state.curr_score = 0;
|
||||
watch_display_string(" LOSE ", 0);
|
||||
if (state -> soundOn)
|
||||
watch_buzzer_play_note(BUZZER_NOTE_A1, 600);
|
||||
else
|
||||
delay_ms(600);
|
||||
}
|
||||
|
||||
static void display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t *state) {
|
||||
static bool prev_obst_pos_two = 0;
|
||||
switch (grid_loc)
|
||||
{
|
||||
case 2:
|
||||
game_state.loc_2_on = obstacle;
|
||||
if (obstacle)
|
||||
watch_set_pixel(0, 20);
|
||||
else if (game_state.jump_state != NOT_JUMPING) {
|
||||
watch_clear_pixel(0, 20);
|
||||
if (game_state.fuel_mode && prev_obst_pos_two)
|
||||
add_to_score(state);
|
||||
}
|
||||
prev_obst_pos_two = obstacle;
|
||||
break;
|
||||
case 3:
|
||||
game_state.loc_3_on = obstacle;
|
||||
if (obstacle)
|
||||
watch_set_pixel(1, 21);
|
||||
else if (game_state.jump_state != NOT_JUMPING)
|
||||
watch_clear_pixel(1, 21);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (!game_state.fuel_mode && obstacle) // If an obstacle is here, it means the ball cleared it
|
||||
add_to_score(state);
|
||||
//fall through
|
||||
case 0:
|
||||
case 5:
|
||||
if (obstacle)
|
||||
watch_set_pixel(0, 18 + grid_loc);
|
||||
else
|
||||
watch_clear_pixel(0, 18 + grid_loc);
|
||||
break;
|
||||
case 4:
|
||||
if (obstacle)
|
||||
watch_set_pixel(1, 22);
|
||||
else
|
||||
watch_clear_pixel(1, 22);
|
||||
break;
|
||||
case 6:
|
||||
if (obstacle)
|
||||
watch_set_pixel(1, 0);
|
||||
else
|
||||
watch_clear_pixel(1, 0);
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
if (obstacle)
|
||||
watch_set_pixel(0, grid_loc - 6);
|
||||
else
|
||||
watch_clear_pixel(0, grid_loc - 6);
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
if (obstacle)
|
||||
watch_set_pixel(0, grid_loc - 5);
|
||||
else
|
||||
watch_clear_pixel(0, grid_loc - 5);
|
||||
break;
|
||||
case 11:
|
||||
if (obstacle)
|
||||
watch_set_pixel(1, 6);
|
||||
else
|
||||
watch_clear_pixel(1, 6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_jumping(endless_runner_state_t *state) {
|
||||
game_state.jump_state = NOT_JUMPING;
|
||||
display_ball(game_state.jump_state != NOT_JUMPING);
|
||||
if (state -> soundOn){
|
||||
if (game_state.success_jump)
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C5, 60);
|
||||
else
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C3, 60);
|
||||
}
|
||||
game_state.success_jump = false;
|
||||
}
|
||||
|
||||
static void display_obstacles(endless_runner_state_t *state) {
|
||||
for (int i = 0; i < NUM_GRID; i++) {
|
||||
// Use a bitmask to isolate each bit and shift it to the least significant position
|
||||
uint32_t mask = 1 << ((_num_bits_obst_pattern - 1) - i);
|
||||
bool obstacle = (game_state.obst_pattern & mask) >> ((_num_bits_obst_pattern - 1) - i);
|
||||
display_obstacle(obstacle, i, state);
|
||||
}
|
||||
game_state.obst_pattern = game_state.obst_pattern << 1;
|
||||
game_state.obst_indx++;
|
||||
if (game_state.fuel_mode) {
|
||||
if (game_state.obst_indx >= (_num_bits_obst_pattern / 2)) {
|
||||
game_state.obst_indx = 0;
|
||||
game_state.obst_pattern = get_random_fuel(game_state.obst_pattern);
|
||||
}
|
||||
}
|
||||
else if (game_state.obst_indx >= _num_bits_obst_pattern - NUM_GRID) {
|
||||
game_state.obst_indx = 0;
|
||||
game_state.obst_pattern = get_random_legal(game_state.obst_pattern, state -> difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_game(endless_runner_state_t *state, uint8_t subsecond) {
|
||||
uint8_t curr_jump_frame = 0;
|
||||
if (game_state.sec_before_moves != 0) {
|
||||
if (subsecond == 0) --game_state.sec_before_moves;
|
||||
return;
|
||||
}
|
||||
display_obstacles(state);
|
||||
switch (game_state.jump_state)
|
||||
{
|
||||
case NOT_JUMPING:
|
||||
if (game_state.fuel_mode) {
|
||||
for (int i = 0; i < JUMP_FRAMES_FUEL_RECHARGE; i++)
|
||||
{
|
||||
if(game_state.fuel >= JUMP_FRAMES_FUEL || (state -> difficulty == DIFF_FUEL_1 && !game_state.fuel))
|
||||
break;
|
||||
game_state.fuel++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JUMPING_FINAL_FRAME:
|
||||
stop_jumping(state);
|
||||
break;
|
||||
default:
|
||||
if (game_state.fuel_mode) {
|
||||
if (!game_state.fuel)
|
||||
game_state.jump_state = JUMPING_FINAL_FRAME;
|
||||
else
|
||||
game_state.fuel--;
|
||||
if (!watch_get_pin_level(BTN_ALARM) && !watch_get_pin_level(BTN_LIGHT)) stop_jumping(state);
|
||||
}
|
||||
else {
|
||||
curr_jump_frame = game_state.jump_state - NOT_JUMPING;
|
||||
if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES))
|
||||
game_state.jump_state = JUMPING_FINAL_FRAME;
|
||||
else
|
||||
game_state.jump_state++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) {
|
||||
delay_ms(200); // To show the player jumping onto the obstacle before displaying the lose screen.
|
||||
display_lose_screen(state);
|
||||
}
|
||||
else if (game_state.fuel_mode)
|
||||
display_fuel(subsecond, state -> difficulty);
|
||||
}
|
||||
|
||||
void endless_runner_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(endless_runner_state_t));
|
||||
memset(*context_ptr, 0, sizeof(endless_runner_state_t));
|
||||
endless_runner_state_t *state = (endless_runner_state_t *)*context_ptr;
|
||||
state->difficulty = DIFF_NORM;
|
||||
}
|
||||
}
|
||||
|
||||
void endless_runner_face_activate(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
}
|
||||
|
||||
bool endless_runner_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
endless_runner_state_t *state = (endless_runner_state_t *)context;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
check_and_reset_hi_score(state);
|
||||
if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
display_title(state);
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
switch (game_state.curr_screen)
|
||||
{
|
||||
case SCREEN_TITLE:
|
||||
case SCREEN_LOSE:
|
||||
break;
|
||||
default:
|
||||
update_game(state, event.subsecond);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
if (game_state.curr_screen == SCREEN_TITLE)
|
||||
begin_playing(state);
|
||||
else if (game_state.curr_screen == SCREEN_LOSE)
|
||||
display_title(state);
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
if (game_state.curr_screen == SCREEN_TITLE)
|
||||
change_difficulty(state);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
case EVENT_ALARM_BUTTON_DOWN:
|
||||
if (game_state.curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){
|
||||
if (game_state.fuel_mode && !game_state.fuel) break;
|
||||
game_state.jump_state = JUMPING_START;
|
||||
display_ball(game_state.jump_state != NOT_JUMPING);
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
if (game_state.curr_screen != SCREEN_PLAYING)
|
||||
toggle_sound(state);
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
if (game_state.curr_screen != SCREEN_TITLE)
|
||||
display_title(state);
|
||||
break;
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
display_time(watch_rtc_get_date_time(), settings->bit.clock_mode_24h);
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void endless_runner_face_resign(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
}
|
||||
|
62
movement/watch_faces/complication/endless_runner_face.h
Normal file
62
movement/watch_faces/complication/endless_runner_face.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 <David Volovskiy>
|
||||
*
|
||||
* 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 ENDLESS_RUNNER_FACE_H_
|
||||
#define ENDLESS_RUNNER_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
ENDLESS_RUNNER face
|
||||
|
||||
This is a basic endless-runner, like the [Chrome Dino game](https://en.wikipedia.org/wiki/Dinosaur_Game).
|
||||
On the title screen, you can select a difficulty by long-pressing LIGHT or toggle sound by long-pressing ALARM.
|
||||
LED or ALARM are used to jump.
|
||||
High-score is displayed on the top-right on the title screen. During a game, the current score is displayed.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint16_t hi_score : 10;
|
||||
uint8_t difficulty : 3;
|
||||
uint8_t month_last_hi_score : 4;
|
||||
uint8_t year_last_hi_score : 6;
|
||||
uint8_t soundOn : 1;
|
||||
/* 24 bits, likely aligned to 32 bits = 4 bytes */
|
||||
} endless_runner_state_t;
|
||||
|
||||
void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
|
||||
void endless_runner_face_activate(movement_settings_t *settings, void *context);
|
||||
bool endless_runner_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
void endless_runner_face_resign(movement_settings_t *settings, void *context);
|
||||
|
||||
#define endless_runner_face ((const watch_face_t){ \
|
||||
endless_runner_face_setup, \
|
||||
endless_runner_face_activate, \
|
||||
endless_runner_face_loop, \
|
||||
endless_runner_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // ENDLESS_RUNNER_FACE_H_
|
||||
|
396
movement/watch_faces/complication/higher_lower_game_face.c
Executable file
396
movement/watch_faces/complication/higher_lower_game_face.c
Executable file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Chris Ellis
|
||||
*
|
||||
* 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 "higher_lower_game_face.h"
|
||||
#include "watch_private_display.h"
|
||||
|
||||
#define TITLE_TEXT "Hi-Lo"
|
||||
#define GAME_BOARD_SIZE 6
|
||||
#define MAX_BOARDS 40
|
||||
#define GUESSES_PER_SCREEN 5
|
||||
#define WIN_SCORE (MAX_BOARDS * GUESSES_PER_SCREEN)
|
||||
#define STATUS_DISPLAY_START 0
|
||||
#define BOARD_SCORE_DISPLAY_START 2
|
||||
#define BOARD_DISPLAY_START 4
|
||||
#define BOARD_DISPLAY_END 9
|
||||
#define MIN_CARD_VALUE 2
|
||||
#define MAX_CARD_VALUE 14
|
||||
#define CARD_RANK_COUNT (MAX_CARD_VALUE - MIN_CARD_VALUE + 1)
|
||||
#define CARD_SUIT_COUNT 4
|
||||
#define DECK_SIZE (CARD_SUIT_COUNT * CARD_RANK_COUNT)
|
||||
#define FLIP_BOARD_DIRECTION false
|
||||
|
||||
typedef struct card_t {
|
||||
uint8_t value;
|
||||
bool revealed;
|
||||
} card_t;
|
||||
|
||||
typedef enum {
|
||||
A, B, C, D, E, F, G
|
||||
} segment_t;
|
||||
|
||||
typedef enum {
|
||||
HL_GUESS_EQUAL,
|
||||
HL_GUESS_HIGHER,
|
||||
HL_GUESS_LOWER
|
||||
} guess_t;
|
||||
|
||||
typedef enum {
|
||||
HL_GS_TITLE_SCREEN,
|
||||
HL_GS_GUESSING,
|
||||
HL_GS_WIN,
|
||||
HL_GS_LOSE,
|
||||
HL_GS_SHOW_SCORE,
|
||||
} game_state_t;
|
||||
|
||||
static game_state_t game_state = HL_GS_TITLE_SCREEN;
|
||||
static card_t game_board[GAME_BOARD_SIZE] = {0};
|
||||
static uint8_t guess_position = 0;
|
||||
static uint8_t score = 0;
|
||||
static uint8_t completed_board_count = 0;
|
||||
static uint8_t deck[DECK_SIZE] = {0};
|
||||
static uint8_t current_card = 0;
|
||||
|
||||
static uint8_t generate_random_number(uint8_t num_values) {
|
||||
// Emulator: use rand. Hardware: use arc4random.
|
||||
#if __EMSCRIPTEN__
|
||||
return rand() % num_values;
|
||||
#else
|
||||
return arc4random_uniform(num_values);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void stack_deck(void) {
|
||||
for (size_t i = 0; i < CARD_RANK_COUNT; i++) {
|
||||
for (size_t j = 0; j < CARD_SUIT_COUNT; j++)
|
||||
deck[(i * CARD_SUIT_COUNT) + j] = MIN_CARD_VALUE + i;
|
||||
}
|
||||
}
|
||||
|
||||
static void shuffle_deck(void) {
|
||||
// Randomize shuffle with Fisher Yates
|
||||
size_t i;
|
||||
size_t j;
|
||||
uint8_t tmp;
|
||||
|
||||
for (i = DECK_SIZE - 1; i > 0; i--) {
|
||||
j = generate_random_number(0xFF) % (i + 1);
|
||||
tmp = deck[j];
|
||||
deck[j] = deck[i];
|
||||
deck[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_deck(void) {
|
||||
current_card = 0;
|
||||
stack_deck();
|
||||
shuffle_deck();
|
||||
}
|
||||
|
||||
static uint8_t get_next_card(void) {
|
||||
if (current_card >= DECK_SIZE)
|
||||
reset_deck();
|
||||
return deck[current_card++];
|
||||
}
|
||||
|
||||
static void reset_board(bool first_round) {
|
||||
// First card is random on the first board, and carried over from the last position on subsequent boards
|
||||
const uint8_t first_card_value = first_round
|
||||
? get_next_card()
|
||||
: game_board[GAME_BOARD_SIZE - 1].value;
|
||||
|
||||
game_board[0].value = first_card_value;
|
||||
game_board[0].revealed = true; // Always reveal first card
|
||||
|
||||
// Fill remainder of board
|
||||
for (size_t i = 1; i < GAME_BOARD_SIZE; ++i) {
|
||||
game_board[i] = (card_t) {
|
||||
.value = get_next_card(),
|
||||
.revealed = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static void init_game(void) {
|
||||
watch_clear_display();
|
||||
watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);
|
||||
watch_display_string("GA", STATUS_DISPLAY_START);
|
||||
reset_deck();
|
||||
reset_board(true);
|
||||
score = 0;
|
||||
completed_board_count = 0;
|
||||
guess_position = 1;
|
||||
}
|
||||
|
||||
static void set_segment_at_position(segment_t segment, uint8_t position) {
|
||||
const uint64_t position_segment_data = (Segment_Map[position] >> (8 * (uint8_t) segment)) & 0xFF;
|
||||
const uint8_t com_pin = position_segment_data >> 6;
|
||||
const uint8_t seg = position_segment_data & 0x3F;
|
||||
watch_set_pixel(com_pin, seg);
|
||||
}
|
||||
|
||||
static void render_board_position(size_t board_position) {
|
||||
const size_t display_position = FLIP_BOARD_DIRECTION
|
||||
? BOARD_DISPLAY_START + board_position
|
||||
: BOARD_DISPLAY_END - board_position;
|
||||
const bool revealed = game_board[board_position].revealed;
|
||||
|
||||
//// Current position indicator spot
|
||||
//if (board_position == guess_position) {
|
||||
// watch_display_character('-', display_position);
|
||||
// return;
|
||||
//}
|
||||
|
||||
if (!revealed) {
|
||||
// Higher or lower indicator (currently just an empty space)
|
||||
watch_display_character(' ', display_position);
|
||||
//set_segment_at_position(F, display_position);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t value = game_board[board_position].value;
|
||||
switch (value) {
|
||||
case 14: // A (≡)
|
||||
watch_display_character(' ', display_position);
|
||||
set_segment_at_position(A, display_position);
|
||||
set_segment_at_position(D, display_position);
|
||||
set_segment_at_position(G, display_position);
|
||||
break;
|
||||
case 13: // K (=)
|
||||
watch_display_character(' ', display_position);
|
||||
set_segment_at_position(A, display_position);
|
||||
set_segment_at_position(D, display_position);
|
||||
break;
|
||||
case 12: // Q (-)
|
||||
watch_display_character('-', display_position);
|
||||
break;
|
||||
default: {
|
||||
const char display_char = (value - MIN_CARD_VALUE) + '0';
|
||||
watch_display_character(display_char, display_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void render_board(void) {
|
||||
for (size_t i = 0; i < GAME_BOARD_SIZE; ++i) {
|
||||
render_board_position(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_board_count(void) {
|
||||
// Render completed boards (screens)
|
||||
char buf[3] = {0};
|
||||
snprintf(buf, sizeof(buf), "%2hhu", completed_board_count);
|
||||
watch_display_string(buf, BOARD_SCORE_DISPLAY_START);
|
||||
}
|
||||
|
||||
static void render_final_score(void) {
|
||||
watch_display_string("SC", STATUS_DISPLAY_START);
|
||||
char buf[7] = {0};
|
||||
const uint8_t complete_boards = score / GUESSES_PER_SCREEN;
|
||||
snprintf(buf, sizeof(buf), "%2hu %03hu", complete_boards, score);
|
||||
watch_set_colon();
|
||||
watch_display_string(buf, BOARD_DISPLAY_START);
|
||||
}
|
||||
|
||||
static guess_t get_answer(void) {
|
||||
if (guess_position < 1 || guess_position > GAME_BOARD_SIZE)
|
||||
return HL_GUESS_EQUAL; // Maybe add an error state, shouldn't ever hit this.
|
||||
|
||||
game_board[guess_position].revealed = true;
|
||||
const uint8_t previous_value = game_board[guess_position - 1].value;
|
||||
const uint8_t current_value = game_board[guess_position].value;
|
||||
|
||||
if (current_value > previous_value)
|
||||
return HL_GUESS_HIGHER;
|
||||
else if (current_value < previous_value)
|
||||
return HL_GUESS_LOWER;
|
||||
else
|
||||
return HL_GUESS_EQUAL;
|
||||
}
|
||||
|
||||
static void do_game_loop(guess_t user_guess) {
|
||||
switch (game_state) {
|
||||
case HL_GS_TITLE_SCREEN:
|
||||
init_game();
|
||||
render_board();
|
||||
render_board_count();
|
||||
game_state = HL_GS_GUESSING;
|
||||
break;
|
||||
case HL_GS_GUESSING: {
|
||||
const guess_t answer = get_answer();
|
||||
|
||||
// Render answer indicator
|
||||
switch (answer) {
|
||||
case HL_GUESS_EQUAL:
|
||||
watch_display_string("==", STATUS_DISPLAY_START);
|
||||
break;
|
||||
case HL_GUESS_HIGHER:
|
||||
watch_display_string("HI", STATUS_DISPLAY_START);
|
||||
break;
|
||||
case HL_GUESS_LOWER:
|
||||
watch_display_string("LO", STATUS_DISPLAY_START);
|
||||
break;
|
||||
}
|
||||
|
||||
// Scoring
|
||||
if (answer == user_guess) {
|
||||
score++;
|
||||
} else if (answer == HL_GUESS_EQUAL) {
|
||||
// No score for two consecutive identical cards
|
||||
} else {
|
||||
// Incorrect guess, game over
|
||||
watch_display_string("GO", STATUS_DISPLAY_START);
|
||||
game_board[guess_position].revealed = true;
|
||||
render_board_position(guess_position);
|
||||
game_state = HL_GS_LOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (score >= WIN_SCORE) {
|
||||
// Win, perhaps some kind of animation sequence?
|
||||
watch_display_string("WI", STATUS_DISPLAY_START);
|
||||
watch_display_string(" ", BOARD_SCORE_DISPLAY_START);
|
||||
watch_display_string("------", BOARD_DISPLAY_START);
|
||||
game_state = HL_GS_WIN;
|
||||
return;
|
||||
}
|
||||
|
||||
// Next guess position
|
||||
const bool final_board_guess = guess_position == GAME_BOARD_SIZE - 1;
|
||||
if (final_board_guess) {
|
||||
// Seed new board
|
||||
completed_board_count++;
|
||||
render_board_count();
|
||||
guess_position = 1;
|
||||
reset_board(false);
|
||||
render_board();
|
||||
} else {
|
||||
guess_position++;
|
||||
render_board_position(guess_position - 1);
|
||||
render_board_position(guess_position);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HL_GS_WIN:
|
||||
case HL_GS_LOSE:
|
||||
// Show score screen on button press from either state
|
||||
watch_clear_display();
|
||||
render_final_score();
|
||||
game_state = HL_GS_SHOW_SCORE;
|
||||
break;
|
||||
case HL_GS_SHOW_SCORE:
|
||||
watch_clear_display();
|
||||
watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);
|
||||
watch_display_string("GA", STATUS_DISPLAY_START);
|
||||
game_state = HL_GS_TITLE_SCREEN;
|
||||
break;
|
||||
default:
|
||||
watch_display_string("ERROR", BOARD_DISPLAY_START);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void light_button_handler(void) {
|
||||
do_game_loop(HL_GUESS_HIGHER);
|
||||
}
|
||||
|
||||
static void alarm_button_handler(void) {
|
||||
do_game_loop(HL_GUESS_LOWER);
|
||||
}
|
||||
|
||||
void higher_lower_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(higher_lower_game_face_state_t));
|
||||
memset(*context_ptr, 0, sizeof(higher_lower_game_face_state_t));
|
||||
// Do any one-time tasks in here; the inside of this conditional happens only at boot.
|
||||
memset(game_board, 0, sizeof(game_board));
|
||||
}
|
||||
// Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
|
||||
}
|
||||
|
||||
void higher_lower_game_face_activate(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context;
|
||||
(void) state;
|
||||
// Handle any tasks related to your watch face coming on screen.
|
||||
game_state = HL_GS_TITLE_SCREEN;
|
||||
}
|
||||
|
||||
bool higher_lower_game_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context;
|
||||
(void) state;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
// Show your initial UI here.
|
||||
watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START);
|
||||
watch_display_string("GA", STATUS_DISPLAY_START);
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
// If needed, update your display here.
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
light_button_handler();
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
// Don't trigger light
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
alarm_button_handler();
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
// Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
|
||||
// you may uncomment this line to move back to the first watch face in the list:
|
||||
// movement_move_to_face(0);
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
|
||||
// return true if the watch can enter standby mode. Generally speaking, you should always return true.
|
||||
// Exceptions:
|
||||
// * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
|
||||
// * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
|
||||
// Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
|
||||
// movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
|
||||
return true;
|
||||
}
|
||||
|
||||
void higher_lower_game_face_resign(movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
(void) context;
|
||||
|
||||
// handle any cleanup before your watch face goes off-screen.
|
||||
}
|
106
movement/watch_faces/complication/higher_lower_game_face.h
Executable file
106
movement/watch_faces/complication/higher_lower_game_face.h
Executable file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Chris Ellis
|
||||
*
|
||||
* 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 HIGHER_LOWER_GAME_FACE_H_
|
||||
#define HIGHER_LOWER_GAME_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* Higher-Lower game face
|
||||
* ======================
|
||||
*
|
||||
* A game face based on the "higher-lower" card game where the objective is to correctly guess if the next card will
|
||||
* be higher or lower than the last revealed cards.
|
||||
*
|
||||
* Game Flow:
|
||||
* - When the face is selected, the "Hi-Lo" "Title" screen will be displayed, and the status indicator will display "GA" for game
|
||||
* - Pressing `ALARM` or `LIGHT` will start the game and proceed to the "Guessing" screen
|
||||
* - The first card will be revealed and the player must now make a guess
|
||||
* - A player can guess `Higher` by pressing the `LIGHT` button, and `Lower` by pressing the `ALARM` button
|
||||
* - The status indicator will show the result of the guess: HI (Higher), LO (Lower), or == (Equal)
|
||||
* - There are five guesses to make on each game screen, once the end of the screen is reached, a new screen
|
||||
* will be started, with the last revealed card carried over
|
||||
* - The number of completed screens is displayed in the top right (see Scoring)
|
||||
* - If the player has guessed correctly, the score is updated and play continues (see Scoring)
|
||||
* - If the player has guessed incorrectly, the status will change to GO (Game Over)
|
||||
* - The current card will be revealed
|
||||
* - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen
|
||||
* - If the game is won, the status indicator will display "WI" and the "Win" screen will be displayed
|
||||
* - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen
|
||||
* - The status indicator will change to "SC" when the final score is displayed
|
||||
* - The number of completed game screens will be displayed on using the first two digits
|
||||
* - The number of correct guesses will be displayed using the final three digits
|
||||
* - E.g. "13: 063" represents 13 completed screens, with 63 correct guesses
|
||||
* - Pressing `ALARM` or `LIGHT` while on the "Score" screen will transition to back to the "Title" screen
|
||||
*
|
||||
* Scoring:
|
||||
* - If the player guesses correctly (HI/LO) a point is gained
|
||||
* - If the player guesses incorrectly the game ends
|
||||
* - Unless the revealed card is equal (==) to the last card, in which case play continues, but no point is gained
|
||||
* - If the player completes 40 screens full of cards, the game ends and a win screen is displayed
|
||||
*
|
||||
* Misc:
|
||||
* The face tries to remain true to the spirit of using "cards"; to cope with the display limitations I've arrived at
|
||||
* the following mapping of card values to screen display, but am open to better suggestions:
|
||||
*
|
||||
* Thanks to voloved for adding deck shuffling and drawing!
|
||||
*
|
||||
* | Cards | |
|
||||
* |---------|--------------------------|
|
||||
* | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A|
|
||||
* | Display |0|1|2|3|4|5|6|7|8 |9|-|=|≡|
|
||||
*
|
||||
* A previous alternative can be found in the git history:
|
||||
* | Cards | |
|
||||
* |---------|--------------------------|
|
||||
* | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A|
|
||||
* | Display |2|3|4|5|6|7|8|9| 0|-|=|≡|H|
|
||||
*
|
||||
*
|
||||
* Future Ideas:
|
||||
* - Add sounds
|
||||
* - Save/Display high score
|
||||
* - Add a "Win" animation
|
||||
* - Consider using lap indicator for larger score limit
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
// Anything you need to keep track of, put it here!
|
||||
} higher_lower_game_face_state_t;
|
||||
|
||||
void higher_lower_game_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
|
||||
void higher_lower_game_face_activate(movement_settings_t *settings, void *context);
|
||||
bool higher_lower_game_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
void higher_lower_game_face_resign(movement_settings_t *settings, void *context);
|
||||
|
||||
#define higher_lower_game_face ((const watch_face_t){ \
|
||||
higher_lower_game_face_setup, \
|
||||
higher_lower_game_face_activate, \
|
||||
higher_lower_game_face_loop, \
|
||||
higher_lower_game_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // HIGHER_LOWER_GAME_FACE_H_
|
472
movement/watch_faces/complication/menstrual_cycle_face.c
Normal file
472
movement/watch_faces/complication/menstrual_cycle_face.c
Normal 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 8–12 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;
|
||||
}
|
80
movement/watch_faces/complication/menstrual_cycle_face.h
Normal file
80
movement/watch_faces/complication/menstrual_cycle_face.h
Normal 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_
|
263
movement/watch_faces/complication/metronome_face.c
Normal file
263
movement/watch_faces/complication/metronome_face.c
Normal 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;
|
||||
}
|
||||
|
86
movement/watch_faces/complication/metronome_face.h
Normal file
86
movement/watch_faces/complication/metronome_face.h
Normal 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_
|
||||
|
384
movement/watch_faces/complication/moonrise_face.c
Normal file
384
movement/watch_faces/complication/moonrise_face.c
Normal 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);
|
||||
}
|
90
movement/watch_faces/complication/moonrise_face.h
Normal file
90
movement/watch_faces/complication/moonrise_face.h
Normal 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_
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user