Compare commits
8 Commits
28d93fbc5d
...
869a92d998
| Author | SHA1 | Date | |
|---|---|---|---|
| 869a92d998 | |||
| 696f7b956c | |||
| 0bb1b2758a | |||
| 419d85209e | |||
| 0f4f72253c | |||
| 08fdb75297 | |||
| d28f0b1c0c | |||
| 9147bab5bb |
@@ -5,18 +5,18 @@
|
||||
(condition "A.Type == 'pad' && (B.Type == 'text' || B.Type == 'graphic')"))
|
||||
|
||||
(rule "drill hole size (mechanical)"
|
||||
(constraint hole_size (min 0.3mm) (max 6.3mm)))
|
||||
(constraint hole_size (min 0.2mm) (max 6.3mm)))
|
||||
|
||||
(rule "Minimum Via Hole Size"
|
||||
(constraint hole_size (min 0.3mm))
|
||||
(constraint hole_size (min 0.2mm))
|
||||
(condition "A.Type == 'via'"))
|
||||
|
||||
(rule "Minimum Via Diameter"
|
||||
(constraint via_diameter (min 20mil))
|
||||
(constraint via_diameter (min 0.35mm))
|
||||
(condition "A.Type == 'via'"))
|
||||
|
||||
(rule "PTH Hole Size"
|
||||
(constraint hole_size (min 12mil) (max 6.35mm))
|
||||
(constraint hole_size (min 0.2mm) (max 6.35mm))
|
||||
(condition "A.isPlated()"))
|
||||
|
||||
(rule "Minimum Non-plated Hole Size"
|
||||
@@ -24,5 +24,5 @@
|
||||
(condition "A.Type == 'pad' && !A.isPlated()"))
|
||||
|
||||
(rule "Pad to Track clearance"
|
||||
(constraint clearance (min 0.2mm))
|
||||
(condition "A.isPlated() && A.Type != 'Via' && B.Type == 'track'"))
|
||||
(constraint clearance (min 0.1mm))
|
||||
(condition "A.isPlated() && A.Type != 'Via' && B.Type == 'track'"))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,17 +18,17 @@
|
||||
"zones": 0.6
|
||||
},
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
"dimensions": false,
|
||||
"footprints": false,
|
||||
"graphics": true,
|
||||
"keepouts": true,
|
||||
"keepouts": false,
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pads": true,
|
||||
"otherItems": false,
|
||||
"pads": false,
|
||||
"text": true,
|
||||
"tracks": true,
|
||||
"vias": true,
|
||||
"zones": true
|
||||
"zones": false
|
||||
},
|
||||
"visible_items": [
|
||||
"vias",
|
||||
@@ -51,7 +51,7 @@
|
||||
"conflict_shadows",
|
||||
"shapes"
|
||||
],
|
||||
"visible_layers": "00000000_00000000_0fffffff_ffffffff",
|
||||
"visible_layers": "00000000_00000000_0ffffff7_ffffffff",
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 3.2,
|
||||
"height": 3.2,
|
||||
"width": 3.2
|
||||
"drill": 0.8,
|
||||
"height": 1.6,
|
||||
"width": 1.6
|
||||
},
|
||||
"silk_line_width": 0.1,
|
||||
"silk_text_italic": false,
|
||||
@@ -60,35 +60,11 @@
|
||||
],
|
||||
"drc_exclusions": [
|
||||
[
|
||||
"items_not_allowed|117348000|131000000|4c513f4f-0c90-4a64-b2d1-bf0189c761db|00000000-0000-0000-0000-000000000000",
|
||||
"lib_footprint_mismatch|148587019|59126370|4f17c230-1bc1-45e3-8816-9cf9d13e3a5c|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"items_not_allowed|117348000|131000000|fea7aba8-73c4-4553-8cc8-83472c47a83b|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"items_not_allowed|122420000|136340000|8ffc6a0d-806e-4b99-922f-2baead2bd98b|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"items_not_allowed|148270000|136340000|804b9d74-91fa-4f75-bc54-8f55b801562f|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"items_not_allowed|148720706|135916802|f2c1e20e-a272-42d8-a551-3e9074b96921|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"nonmirrored_text_on_back_layer|210811000|131073000|e87ca627-5327-4f7e-ac1a-c1eadd662c0a|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"silk_edge_clearance|115549999|139536698|483993e4-aad9-46a1-bcd0-7f427e76aa64|cd433b4c-40ac-48cc-b03e-5acf3357d88b",
|
||||
""
|
||||
],
|
||||
[
|
||||
"silk_overlap|115550000|131000000|cd433b4c-40ac-48cc-b03e-5acf3357d88b|fea7aba8-73c4-4553-8cc8-83472c47a83b",
|
||||
"nonmirrored_text_on_back_layer|213233000|81790000|e87ca627-5327-4f7e-ac1a-c1eadd662c0a|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
]
|
||||
],
|
||||
@@ -157,22 +133,22 @@
|
||||
},
|
||||
"rules": {
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.2032,
|
||||
"min_clearance": 0.15,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.2,
|
||||
"min_groove_width": 0.0,
|
||||
"min_hole_clearance": 0.35,
|
||||
"min_hole_to_hole": 0.45,
|
||||
"min_hole_to_hole": 0.2,
|
||||
"min_microvia_diameter": 0.2,
|
||||
"min_microvia_drill": 0.1,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 1.0,
|
||||
"min_text_thickness": 0.15,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.2032,
|
||||
"min_through_hole_diameter": 0.2,
|
||||
"min_track_width": 0.15,
|
||||
"min_via_annular_width": 0.15,
|
||||
"min_via_diameter": 0.5,
|
||||
"min_via_diameter": 0.35,
|
||||
"solder_mask_to_copper_clearance": 0.005,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
|
||||
@@ -4,7 +4,127 @@
|
||||
(generator_version "9.0")
|
||||
(uuid "7226ca0f-138a-46b4-89f3-dc32dfb1f9f7")
|
||||
(paper "A4")
|
||||
(title_block
|
||||
(title "Tonberry Pico")
|
||||
(date "2025-11-08")
|
||||
(rev "1.2")
|
||||
(comment 1 "Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>")
|
||||
(comment 2 "SPDX-License-Identifier: MIT")
|
||||
)
|
||||
(lib_symbols
|
||||
(symbol "Connector_Generic:Conn_01x01"
|
||||
(pin_names
|
||||
(offset 1.016)
|
||||
(hide yes)
|
||||
)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(property "Reference" "J"
|
||||
(at 0 2.54 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "Conn_01x01"
|
||||
(at 0 -2.54 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" ""
|
||||
(at 0 0 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" "~"
|
||||
(at 0 0 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" "Generic connector, single row, 01x01, script generated (kicad-library-utils/schlib/autogen/connector/)"
|
||||
(at 0 0 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "ki_keywords" "connector"
|
||||
(at 0 0 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "ki_fp_filters" "Connector*:*_1x??_*"
|
||||
(at 0 0 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(symbol "Conn_01x01_1_1"
|
||||
(rectangle
|
||||
(start -1.27 1.27)
|
||||
(end 1.27 -1.27)
|
||||
(stroke
|
||||
(width 0.254)
|
||||
(type default)
|
||||
)
|
||||
(fill
|
||||
(type background)
|
||||
)
|
||||
)
|
||||
(rectangle
|
||||
(start -1.27 0.127)
|
||||
(end 0 -0.127)
|
||||
(stroke
|
||||
(width 0.1524)
|
||||
(type default)
|
||||
)
|
||||
(fill
|
||||
(type none)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at -5.08 0 0)
|
||||
(length 3.81)
|
||||
(name "Pin_1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "1"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(embedded_fonts no)
|
||||
)
|
||||
(symbol "Connector_Generic:Conn_01x03"
|
||||
(pin_names
|
||||
(offset 1.016)
|
||||
@@ -176,7 +296,7 @@
|
||||
)
|
||||
(embedded_fonts no)
|
||||
)
|
||||
(symbol "Connector_Generic:Conn_01x05"
|
||||
(symbol "Connector_Generic:Conn_01x06"
|
||||
(pin_names
|
||||
(offset 1.016)
|
||||
(hide yes)
|
||||
@@ -192,8 +312,8 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "Conn_01x05"
|
||||
(at 0 -7.62 0)
|
||||
(property "Value" "Conn_01x06"
|
||||
(at 0 -10.16 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -218,7 +338,7 @@
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" "Generic connector, single row, 01x05, script generated (kicad-library-utils/schlib/autogen/connector/)"
|
||||
(property "Description" "Generic connector, single row, 01x06, script generated (kicad-library-utils/schlib/autogen/connector/)"
|
||||
(at 0 0 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -245,10 +365,10 @@
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(symbol "Conn_01x05_1_1"
|
||||
(symbol "Conn_01x06_1_1"
|
||||
(rectangle
|
||||
(start -1.27 6.35)
|
||||
(end 1.27 -6.35)
|
||||
(end 1.27 -8.89)
|
||||
(stroke
|
||||
(width 0.254)
|
||||
(type default)
|
||||
@@ -312,6 +432,17 @@
|
||||
(type none)
|
||||
)
|
||||
)
|
||||
(rectangle
|
||||
(start -1.27 -7.493)
|
||||
(end 0 -7.747)
|
||||
(stroke
|
||||
(width 0.1524)
|
||||
(type default)
|
||||
)
|
||||
(fill
|
||||
(type none)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at -5.08 5.08 0)
|
||||
(length 3.81)
|
||||
@@ -402,6 +533,24 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(pin passive line
|
||||
(at -5.08 -7.62 0)
|
||||
(length 3.81)
|
||||
(name "Pin_6"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(number "6"
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(embedded_fonts no)
|
||||
)
|
||||
@@ -3809,7 +3958,7 @@
|
||||
)
|
||||
(rectangle
|
||||
(start 242.57 60.96)
|
||||
(end 283.21 154.94)
|
||||
(end 283.21 163.83)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
@@ -3859,16 +4008,6 @@
|
||||
)
|
||||
(uuid "466265fb-ac3b-4fe8-8af4-502fa85087f9")
|
||||
)
|
||||
(text "Pin changes for revision 1 (compared to breadboard):\n- RC522_RST to GPIO14 instead of GPIO9\n- RC522_IRQ to GPIO15 instead of GPIO14\n- I2S_SD to GPIO9 instead of NC\n- I2S_LRCLK to GPIO6 instead of GPIO7\n- I2S_DCLK to GPIO7 instead of GPIO6"
|
||||
(exclude_from_sim no)
|
||||
(at 256.032 174.244 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
(uuid "6843afc4-f352-41ed-b1f0-21b2625ea765")
|
||||
)
|
||||
(text "Sparkfun board has pullup\nfor mono mode by default"
|
||||
(exclude_from_sim no)
|
||||
(at 70.104 69.85 0)
|
||||
@@ -3899,6 +4038,16 @@
|
||||
)
|
||||
(uuid "dcabf837-4155-4957-820b-e0a63b5947c8")
|
||||
)
|
||||
(text "Place GND through-hole pad in line with +5V side of R2\nto allow user to select different gain for MAX98357A"
|
||||
(exclude_from_sim no)
|
||||
(at 40.894 32.766 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
(uuid "dcca3a95-1162-4543-8f05-573beea44e38")
|
||||
)
|
||||
(text "Battery power"
|
||||
(exclude_from_sim no)
|
||||
(at 21.59 111.76 0)
|
||||
@@ -3927,6 +4076,12 @@
|
||||
(color 0 0 0 0)
|
||||
(uuid "7b8323c0-6656-4c4e-b086-636ff2b9c6b7")
|
||||
)
|
||||
(junction
|
||||
(at 276.86 148.59)
|
||||
(diameter 0)
|
||||
(color 0 0 0 0)
|
||||
(uuid "9a1919a6-5883-47bb-82e5-3e3ecca52cb1")
|
||||
)
|
||||
(junction
|
||||
(at 55.88 45.72)
|
||||
(diameter 0)
|
||||
@@ -4005,10 +4160,6 @@
|
||||
(at 132.08 41.91)
|
||||
(uuid "327eca18-19ed-4df2-b4a4-b6473b4637be")
|
||||
)
|
||||
(no_connect
|
||||
(at 177.8 59.69)
|
||||
(uuid "476000a3-9e7f-4cbf-9a8d-0e319caba0cd")
|
||||
)
|
||||
(no_connect
|
||||
(at 177.8 69.85)
|
||||
(uuid "50c74a0a-6abe-49d5-b374-0fde9fb9680b")
|
||||
@@ -4289,6 +4440,16 @@
|
||||
)
|
||||
(uuid "31e9b0f8-7cda-4a52-89a1-0e733bdf35fb")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 276.86 148.59) (xy 276.86 151.13)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "382ff189-be47-4469-ba80-056083381655")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 254 138.43) (xy 265.43 138.43)
|
||||
@@ -4389,6 +4550,16 @@
|
||||
)
|
||||
(uuid "48ccc495-741c-4b2b-9585-fde090268ab2")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 275.59 148.59) (xy 276.86 148.59)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "4c57d923-2d70-4cfe-aa54-f3da164b16e8")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 276.86 128.27) (xy 276.86 138.43)
|
||||
@@ -4409,6 +4580,16 @@
|
||||
)
|
||||
(uuid "4f930ab5-0db4-4b67-a7e7-7fda8dc25972")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 254 87.63) (xy 266.7 87.63)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "50622824-c775-4a38-abd2-79f7254111d1")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 85.09 170.18) (xy 72.39 170.18)
|
||||
@@ -4421,7 +4602,7 @@
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 276.86 138.43) (xy 276.86 139.7)
|
||||
(xy 276.86 138.43) (xy 276.86 148.59)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
@@ -4599,6 +4780,16 @@
|
||||
)
|
||||
(uuid "79bb88bf-f554-4e83-bbcb-698a1df2a819")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 36.83 46.99) (xy 36.83 49.53)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "7ac8879d-9fc7-40b2-b550-c83532147c07")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 55.88 45.72) (xy 55.88 44.45)
|
||||
@@ -4809,6 +5000,16 @@
|
||||
)
|
||||
(uuid "9c334529-8798-4c90-875a-da73ae41ede1")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 177.8 59.69) (xy 182.88 59.69)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "9d9ed882-a31b-445f-833c-142b7ba4e54b")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 49.53 162.56) (xy 49.53 190.5)
|
||||
@@ -5169,6 +5370,16 @@
|
||||
)
|
||||
(uuid "ccf59af6-59fd-48d7-a33f-49c9aeb1ece9")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 254 148.59) (xy 265.43 148.59)
|
||||
)
|
||||
(stroke
|
||||
(width 0)
|
||||
(type default)
|
||||
)
|
||||
(uuid "cdc6967b-fa8e-4d0e-8256-ef9790895e47")
|
||||
)
|
||||
(wire
|
||||
(pts
|
||||
(xy 52.07 55.88) (xy 52.07 69.85)
|
||||
@@ -5713,6 +5924,28 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(global_label "BUTTON4"
|
||||
(shape output)
|
||||
(at 254 87.63 180)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify right)
|
||||
)
|
||||
(uuid "21ff2bc3-e911-4f23-a993-f08a650905a9")
|
||||
(property "Intersheetrefs" "${INTERSHEET_REFS}"
|
||||
(at 242.6086 87.63 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify right)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
)
|
||||
(global_label "BUTTON1"
|
||||
(shape output)
|
||||
(at 254 80.01 180)
|
||||
@@ -5735,6 +5968,28 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(global_label "BUTTON4"
|
||||
(shape input)
|
||||
(at 182.88 59.69 0)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
)
|
||||
(uuid "2736ef81-384c-4324-a05c-697c55fa029e")
|
||||
(property "Intersheetrefs" "${INTERSHEET_REFS}"
|
||||
(at 194.2714 59.69 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify left)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
)
|
||||
(global_label "BUTTON2"
|
||||
(shape output)
|
||||
(at 254 128.27 180)
|
||||
@@ -5779,6 +6034,28 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(global_label "BUTTON4"
|
||||
(shape output)
|
||||
(at 254 148.59 180)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify right)
|
||||
)
|
||||
(uuid "47d67395-d5c6-4e13-8fd7-2f3ddfb71aa2")
|
||||
(property "Intersheetrefs" "${INTERSHEET_REFS}"
|
||||
(at 242.6086 148.59 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify right)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
)
|
||||
(global_label "POWER_BUTTON"
|
||||
(shape input)
|
||||
(at 83.82 157.48 0)
|
||||
@@ -6115,7 +6392,7 @@
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Connector_PinSocket_2.54mm:PinSocket_1x03_P2.54mm_Vertical"
|
||||
(property "Footprint" "Connector_JST:JST_XH_B3B-XH-A_1x03_P2.50mm_Vertical"
|
||||
(at 207.01 49.53 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -6160,6 +6437,72 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "power:GND")
|
||||
(at 36.83 49.53 0)
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(dnp no)
|
||||
(uuid "0e3366a9-cc30-4574-9a85-1f934576d050")
|
||||
(property "Reference" "#PWR018"
|
||||
(at 36.83 55.88 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Value" "GND"
|
||||
(at 38.862 53.34 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify right)
|
||||
)
|
||||
)
|
||||
(property "Footprint" ""
|
||||
(at 36.83 49.53 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 36.83 49.53 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" "Power symbol creates a global label with name \"GND\" , ground"
|
||||
(at 36.83 49.53 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(pin "1"
|
||||
(uuid "4411b33e-7a34-433c-a726-528c62666fb1")
|
||||
)
|
||||
(instances
|
||||
(project "tonberry-pico"
|
||||
(path "/7226ca0f-138a-46b4-89f3-dc32dfb1f9f7"
|
||||
(reference "#PWR018")
|
||||
(unit 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Jumper:Jumper_2_Bridged")
|
||||
(at 93.98 69.85 0)
|
||||
@@ -6814,7 +7157,7 @@
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Package_TO_SOT_THT:TO-92_Inline"
|
||||
(property "Footprint" "Package_TO_SOT_THT:TO-92_Inline_Wide"
|
||||
(at 58.42 172.085 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -6935,8 +7278,8 @@
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(dnp no)
|
||||
(on_board no)
|
||||
(dnp yes)
|
||||
(fields_autoplaced yes)
|
||||
(uuid "6107dfa8-3a58-4296-84b3-7c3671cb0432")
|
||||
(property "Reference" "SW3"
|
||||
@@ -7003,8 +7346,8 @@
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(dnp no)
|
||||
(on_board no)
|
||||
(dnp yes)
|
||||
(uuid "623caba9-5088-4f71-b179-4289ab57e20f")
|
||||
(property "Reference" "SW4"
|
||||
(at 270.51 96.52 0)
|
||||
@@ -7090,7 +7433,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical"
|
||||
(property "Footprint" "PCM_JLCPCB:SW_TS-1088-AR02016"
|
||||
(at 266.7 20.32 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -7586,8 +7929,8 @@
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(dnp no)
|
||||
(on_board no)
|
||||
(dnp yes)
|
||||
(uuid "79f78486-f390-4ddb-b9f4-4a3a2c11d855")
|
||||
(property "Reference" "SW1"
|
||||
(at 270.51 110.49 0)
|
||||
@@ -7653,8 +7996,8 @@
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board yes)
|
||||
(dnp no)
|
||||
(on_board no)
|
||||
(dnp yes)
|
||||
(fields_autoplaced yes)
|
||||
(uuid "7cf33776-e3ad-49e6-8da4-da9b4c4012c2")
|
||||
(property "Reference" "SW2"
|
||||
@@ -7787,7 +8130,7 @@
|
||||
)
|
||||
(symbol
|
||||
(lib_id "power:GND")
|
||||
(at 276.86 139.7 0)
|
||||
(at 276.86 151.13 0)
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
@@ -7796,7 +8139,7 @@
|
||||
(fields_autoplaced yes)
|
||||
(uuid "7f17a4cc-3063-4ad7-9820-0d414285324e")
|
||||
(property "Reference" "#PWR013"
|
||||
(at 276.86 146.05 0)
|
||||
(at 276.86 157.48 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -7805,7 +8148,7 @@
|
||||
)
|
||||
)
|
||||
(property "Value" "GND"
|
||||
(at 276.86 144.78 0)
|
||||
(at 276.86 156.21 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -7813,7 +8156,7 @@
|
||||
)
|
||||
)
|
||||
(property "Footprint" ""
|
||||
(at 276.86 139.7 0)
|
||||
(at 276.86 151.13 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -7822,7 +8165,7 @@
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 276.86 139.7 0)
|
||||
(at 276.86 151.13 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -7831,7 +8174,7 @@
|
||||
)
|
||||
)
|
||||
(property "Description" "Power symbol creates a global label with name \"GND\" , ground"
|
||||
(at 276.86 139.7 0)
|
||||
(at 276.86 151.13 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -8009,7 +8352,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Modules:AZ_Delivery_RC522"
|
||||
(property "Footprint" "Connector_PinSocket_2.54mm:PinSocket_1x08_P2.54mm_Vertical"
|
||||
(at 74.93 90.17 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -8164,7 +8507,7 @@
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Package_TO_SOT_THT:TO-92_Inline"
|
||||
(property "Footprint" "Package_TO_SOT_THT:TO-92_Inline_Wide"
|
||||
(at 46.99 159.385 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -8720,6 +9063,73 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Connector_Generic:Conn_01x01")
|
||||
(at 36.83 41.91 90)
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom no)
|
||||
(on_board yes)
|
||||
(dnp no)
|
||||
(fields_autoplaced yes)
|
||||
(uuid "d3bcb0bc-df35-41fa-87ea-2781bf254e68")
|
||||
(property "Reference" "J8"
|
||||
(at 39.37 40.6399 90)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify right)
|
||||
)
|
||||
)
|
||||
(property "Value" "Conn_01x01"
|
||||
(at 39.37 43.1799 90)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify right)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Connector_Wire:SolderWire-0.25sqmm_1x01_D0.65mm_OD1.7mm"
|
||||
(at 36.83 41.91 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" "~"
|
||||
(at 36.83 41.91 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" "Generic connector, single row, 01x01, script generated (kicad-library-utils/schlib/autogen/connector/)"
|
||||
(at 36.83 41.91 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(pin "1"
|
||||
(uuid "516d40fb-50f8-4c22-aeaf-d075175736e6")
|
||||
)
|
||||
(instances
|
||||
(project ""
|
||||
(path "/7226ca0f-138a-46b4-89f3-dc32dfb1f9f7"
|
||||
(reference "J8")
|
||||
(unit 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Connector_Generic:Conn_01x07")
|
||||
(at 73.66 49.53 0)
|
||||
@@ -9427,6 +9837,74 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Switch:SW_Push")
|
||||
(at 270.51 148.59 0)
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
(in_bom yes)
|
||||
(on_board no)
|
||||
(dnp yes)
|
||||
(fields_autoplaced yes)
|
||||
(uuid "fb95655b-d8ba-47f5-91f3-286848a1e70f")
|
||||
(property "Reference" "SW6"
|
||||
(at 270.51 140.97 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "SW_Push"
|
||||
(at 270.51 143.51 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Button_Switch_THT:Push_E-Switch_KS01Q01"
|
||||
(at 270.51 143.51 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" "~"
|
||||
(at 270.51 143.51 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" "Push button switch, generic, two pins"
|
||||
(at 270.51 148.59 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(pin "1"
|
||||
(uuid "6694cff6-47a5-4135-b781-b53e5eb0d46c")
|
||||
)
|
||||
(pin "2"
|
||||
(uuid "aa2c7820-7193-44e1-aac3-be02505e4c11")
|
||||
)
|
||||
(instances
|
||||
(project "tonberry-pico"
|
||||
(path "/7226ca0f-138a-46b4-89f3-dc32dfb1f9f7"
|
||||
(reference "SW6")
|
||||
(unit 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Connector_Generic:Conn_01x03")
|
||||
(at 115.57 31.75 90)
|
||||
@@ -9572,7 +10050,7 @@
|
||||
)
|
||||
)
|
||||
(symbol
|
||||
(lib_id "Connector_Generic:Conn_01x05")
|
||||
(lib_id "Connector_Generic:Conn_01x06")
|
||||
(at 271.78 80.01 0)
|
||||
(unit 1)
|
||||
(exclude_from_sim no)
|
||||
@@ -9582,7 +10060,7 @@
|
||||
(fields_autoplaced yes)
|
||||
(uuid "fccc1f29-a663-4617-8514-4cf1edd994cd")
|
||||
(property "Reference" "J7"
|
||||
(at 274.32 78.7399 0)
|
||||
(at 274.32 80.0099 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -9591,7 +10069,7 @@
|
||||
)
|
||||
)
|
||||
(property "Value" "Buttons"
|
||||
(at 274.32 81.2799 0)
|
||||
(at 274.32 82.5499 0)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@@ -9599,7 +10077,7 @@
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "Connector_PinSocket_2.54mm:PinSocket_1x05_P2.54mm_Vertical"
|
||||
(property "Footprint" "Connector_JST:JST_XH_B6B-XH-A_1x06_P2.50mm_Vertical"
|
||||
(at 271.78 80.01 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -9617,7 +10095,7 @@
|
||||
(hide yes)
|
||||
)
|
||||
)
|
||||
(property "Description" "Generic connector, single row, 01x05, script generated (kicad-library-utils/schlib/autogen/connector/)"
|
||||
(property "Description" "Generic connector, single row, 01x06, script generated (kicad-library-utils/schlib/autogen/connector/)"
|
||||
(at 271.78 80.01 0)
|
||||
(effects
|
||||
(font
|
||||
@@ -9641,6 +10119,9 @@
|
||||
(pin "1"
|
||||
(uuid "46257db4-53cd-407e-8cb7-103df4c3a13d")
|
||||
)
|
||||
(pin "6"
|
||||
(uuid "cabc7efd-b7fb-49c1-8a3e-beca01096a1a")
|
||||
)
|
||||
(instances
|
||||
(project "tonberry-pico"
|
||||
(path "/7226ca0f-138a-46b4-89f3-dc32dfb1f9f7"
|
||||
|
||||
@@ -6,7 +6,7 @@ import time
|
||||
from utils import TimerManager
|
||||
|
||||
|
||||
Dependencies = namedtuple('Dependencies', ('mp3player', 'nfcreader', 'buttons', 'playlistdb'))
|
||||
Dependencies = namedtuple('Dependencies', ('mp3player', 'nfcreader', 'buttons', 'playlistdb', 'hwconfig', 'leds'))
|
||||
|
||||
# Should be ~ 6dB steps
|
||||
VOLUME_CURVE = [1, 2, 4, 8, 16, 32, 63, 126, 251]
|
||||
@@ -44,6 +44,8 @@ class PlayerApp:
|
||||
self.player = deps.mp3player(self)
|
||||
self.nfc = deps.nfcreader(self.tag_state_machine)
|
||||
self.playlist_db = deps.playlistdb(self)
|
||||
self.hwconfig = deps.hwconfig(self)
|
||||
self.leds = deps.leds(self)
|
||||
self.tag_mode = self.playlist_db.getSetting('tagmode')
|
||||
self.playing_tag = None
|
||||
self.playlist = None
|
||||
@@ -51,6 +53,7 @@ class PlayerApp:
|
||||
self.mp3file = None
|
||||
self.volume_pos = 3
|
||||
self.player.set_volume(VOLUME_CURVE[self.volume_pos])
|
||||
self._onIdle()
|
||||
|
||||
def __del__(self):
|
||||
if self.mp3file is not None:
|
||||
@@ -95,8 +98,18 @@ class PlayerApp:
|
||||
self.mp3file = None
|
||||
self._play_next()
|
||||
|
||||
def onIdleTimeout(self):
|
||||
if self.hwconfig.get_on_battery():
|
||||
self.hwconfig.power_off()
|
||||
else:
|
||||
# Check again in a minute
|
||||
self.timer_manager.schedule(time.ticks_ms() + 60*1000, self.onIdleTimeout)
|
||||
|
||||
def _set_playlist(self, tag: bytes):
|
||||
self._unset_playlist()
|
||||
if self.playlist is not None:
|
||||
pos = self.player.stop()
|
||||
if pos is not None:
|
||||
self.playlist.setPlaybackOffset(pos)
|
||||
self.playlist = self.playlist_db.getPlaylistForTag(tag)
|
||||
self._play(self.playlist.getCurrentPath() if self.playlist is not None else None,
|
||||
self.playlist.getPlaybackOffset() if self.playlist is not None else 0)
|
||||
@@ -104,6 +117,7 @@ class PlayerApp:
|
||||
def _unset_playlist(self):
|
||||
if self.playlist is not None:
|
||||
pos = self.player.stop()
|
||||
self._onIdle()
|
||||
if pos is not None:
|
||||
self.playlist.setPlaybackOffset(pos)
|
||||
self.playlist = None
|
||||
@@ -122,7 +136,17 @@ class PlayerApp:
|
||||
self.player.stop()
|
||||
self.mp3file.close()
|
||||
self.mp3file = None
|
||||
self._onIdle()
|
||||
if filename is not None:
|
||||
print(f'Playing {filename!r}')
|
||||
self.mp3file = open(filename, 'rb')
|
||||
self.player.play(self.mp3file, offset)
|
||||
self._onActive()
|
||||
|
||||
def _onIdle(self):
|
||||
self.timer_manager.schedule(time.ticks_ms() + 60*1000, self.onIdleTimeout)
|
||||
self.leds.set_state(self.leds.IDLE)
|
||||
|
||||
def _onActive(self):
|
||||
self.timer_manager.cancel(self.onIdleTimeout)
|
||||
self.leds.set_state(self.leds.PLAYING)
|
||||
|
||||
@@ -39,6 +39,7 @@ BUTTON_POWER = Pin.board.GP21
|
||||
# Power
|
||||
POWER_EN = Pin.board.GP22
|
||||
VBAT_ADC = Pin.board.GP26
|
||||
VBUS_DET = Pin.board.WL_GPIO2
|
||||
|
||||
|
||||
def board_init():
|
||||
@@ -61,3 +62,13 @@ def get_battery_voltage():
|
||||
adc = machine.ADC(VBAT_ADC) # create ADC object on ADC pin
|
||||
battv = adc.read_u16()/65535.0*3.3*2
|
||||
return battv
|
||||
|
||||
|
||||
def power_off():
|
||||
POWER_EN.init(mode=Pin.OUT)
|
||||
POWER_EN.value(0)
|
||||
|
||||
|
||||
def get_on_battery():
|
||||
vbus = VBUS_DET.value()
|
||||
return not vbus
|
||||
|
||||
@@ -47,3 +47,12 @@ def board_init():
|
||||
def get_battery_voltage():
|
||||
# Not supported on breadboard
|
||||
return None
|
||||
|
||||
|
||||
def power_off():
|
||||
# Not supported on breadboard
|
||||
pass
|
||||
|
||||
|
||||
def get_on_battery():
|
||||
return False
|
||||
|
||||
@@ -9,7 +9,6 @@ import micropython
|
||||
import network
|
||||
import os
|
||||
import time
|
||||
from math import pi, sin, pow
|
||||
|
||||
# Own modules
|
||||
import app
|
||||
@@ -18,7 +17,7 @@ from mfrc522 import MFRC522
|
||||
from mp3player import MP3Player
|
||||
from nfc import Nfc
|
||||
from rp2_neopixel import NeoPixel
|
||||
from utils import BTreeFileManager, Buttons, SDContext, TimerManager
|
||||
from utils import BTreeFileManager, Buttons, SDContext, TimerManager, LedManager
|
||||
|
||||
try:
|
||||
import hwconfig
|
||||
@@ -31,28 +30,6 @@ micropython.alloc_emergency_exception_buf(100)
|
||||
# Machine setup
|
||||
hwconfig.board_init()
|
||||
|
||||
|
||||
async def rainbow(np, period=10):
|
||||
def gamma(value, X=2.2):
|
||||
return min(max(int(brightness * pow(value / 255.0, X) * 255.0 + 0.5), 0), 255)
|
||||
|
||||
brightness = 0.05
|
||||
count = 0.0
|
||||
leds = len(np)
|
||||
while True:
|
||||
for i in range(leds):
|
||||
ofs = (count + i) % leds
|
||||
np[i] = (gamma((sin(ofs / leds * 2 * pi) + 1) * 127),
|
||||
gamma((sin(ofs / leds * 2 * pi + 2/3*pi) + 1) * 127),
|
||||
gamma((sin(ofs / leds * 2 * pi + 4/3*pi) + 1) * 127))
|
||||
count += 0.02 * leds
|
||||
before = time.ticks_ms()
|
||||
await np.async_write()
|
||||
now = time.ticks_ms()
|
||||
if before + 20 > now:
|
||||
await asyncio.sleep_ms(20 - (now - before))
|
||||
|
||||
|
||||
# high prio for proc 1
|
||||
machine.mem32[0x40030000 + 0x00] = 0x10
|
||||
|
||||
@@ -71,7 +48,6 @@ def run():
|
||||
asyncio.new_event_loop()
|
||||
# Setup LEDs
|
||||
np = NeoPixel(hwconfig.LED_DIN, hwconfig.LED_COUNT, sm=1)
|
||||
asyncio.create_task(rainbow(np))
|
||||
|
||||
# Wifi with default config
|
||||
setup_wifi()
|
||||
@@ -101,7 +77,9 @@ def run():
|
||||
buttons=lambda the_app: Buttons(the_app, pin_volup=hwconfig.BUTTON_VOLUP,
|
||||
pin_voldown=hwconfig.BUTTON_VOLDOWN,
|
||||
pin_next=hwconfig.BUTTON_NEXT),
|
||||
playlistdb=lambda _: playlistdb)
|
||||
playlistdb=lambda _: playlistdb,
|
||||
hwconfig=lambda _: hwconfig,
|
||||
leds=lambda _: LedManager(np))
|
||||
the_app = app.PlayerApp(deps)
|
||||
|
||||
# Start
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
from utils.buttons import Buttons
|
||||
from utils.leds import LedManager
|
||||
from utils.mbrpartition import MBRPartition
|
||||
from utils.pinindex import get_pin_index
|
||||
from utils.playlistdb import BTreeDB, BTreeFileManager
|
||||
from utils.sdcontext import SDContext
|
||||
from utils.timer import TimerManager
|
||||
|
||||
__all__ = ["BTreeDB", "BTreeFileManager", "Buttons", "get_pin_index", "MBRPartition", "SDContext", "TimerManager"]
|
||||
__all__ = ["BTreeDB", "BTreeFileManager", "Buttons", "get_pin_index", "LedManager", "MBRPartition", "SDContext",
|
||||
"TimerManager"]
|
||||
|
||||
58
software/src/utils/leds.py
Normal file
58
software/src/utils/leds.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
import asyncio
|
||||
from math import sin, pi
|
||||
from micropython import const
|
||||
import time
|
||||
|
||||
|
||||
class LedManager:
|
||||
IDLE = const(0)
|
||||
PLAYING = const(1)
|
||||
|
||||
def __init__(self, np):
|
||||
self.led_state = LedManager.IDLE
|
||||
self.np = np
|
||||
self.brightness = 0.1
|
||||
self.leds = len(self.np)
|
||||
asyncio.create_task(self.run())
|
||||
|
||||
def set_state(self, state):
|
||||
assert state in [LedManager.IDLE, LedManager.PLAYING]
|
||||
self.led_state = state
|
||||
|
||||
def _gamma(self, value, X=2.2):
|
||||
result = min(max(int(self.brightness * pow(value / 255.0, X) * 255.0 + 0.5), 0), 255)
|
||||
if value > 0:
|
||||
result = max(1, result)
|
||||
return result
|
||||
|
||||
def _rainbow(self, time):
|
||||
for i in range(self.leds):
|
||||
ofs = (time * self.leds + i) % self.leds
|
||||
self.np[i] = (self._gamma((sin(ofs / self.leds * 2 * pi) + 1) * 127),
|
||||
self._gamma((sin(ofs / self.leds * 2 * pi + 2/3*pi) + 1) * 127),
|
||||
self._gamma((sin(ofs / self.leds * 2 * pi + 4/3*pi) + 1) * 127))
|
||||
|
||||
def _pulse(self, time, color, speed):
|
||||
scaled_sin = max(1, abs(sin(time / speed * 2 * pi)) * 255)
|
||||
val = (self._gamma(color[0]*scaled_sin),
|
||||
self._gamma(color[1]*scaled_sin),
|
||||
self._gamma(color[2]*scaled_sin))
|
||||
for i in range(self.leds):
|
||||
self.np[i] = val
|
||||
|
||||
async def run(self):
|
||||
time_ = 0.0
|
||||
while True:
|
||||
if self.led_state == LedManager.IDLE:
|
||||
self._pulse(time_, (0, 1, 0), 3)
|
||||
elif self.led_state == LedManager.PLAYING:
|
||||
self._rainbow(time_)
|
||||
time_ += 0.02
|
||||
before = time.ticks_ms()
|
||||
await self.np.async_write()
|
||||
now = time.ticks_ms()
|
||||
if before + 20 > now:
|
||||
await asyncio.sleep_ms(20 - (now - before))
|
||||
@@ -1,2 +1,5 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
def const(x):
|
||||
return x
|
||||
|
||||
@@ -43,11 +43,21 @@ class FakeMp3Player:
|
||||
|
||||
|
||||
class FakeTimerManager:
|
||||
def __init__(self): pass
|
||||
def cancel(self, timer): pass
|
||||
def __init__(self):
|
||||
self.queued = []
|
||||
|
||||
def cancel(self, timer):
|
||||
self.queued = [(elem[0], elem[1], True) if elem[1] == timer else elem for elem in self.queued]
|
||||
|
||||
def schedule(self, when, what):
|
||||
what()
|
||||
self.queued.append((when, what, False))
|
||||
|
||||
def testing_run_queued(self):
|
||||
queued = self.queued
|
||||
self.queued = []
|
||||
for when, what, canceled in queued:
|
||||
if not canceled:
|
||||
what()
|
||||
|
||||
|
||||
class FakeNfcReader:
|
||||
@@ -91,32 +101,62 @@ class FakePlaylistDb:
|
||||
return None
|
||||
|
||||
|
||||
class FakeHwconfig:
|
||||
def __init__(self):
|
||||
self.powered = True
|
||||
self.on_battery = False
|
||||
|
||||
def power_off(self):
|
||||
self.powered = False
|
||||
|
||||
def get_on_battery(self):
|
||||
return self.on_battery
|
||||
|
||||
|
||||
class FakeLeds:
|
||||
IDLE = 0
|
||||
PLAYING = 1
|
||||
|
||||
def __init__(self):
|
||||
self.state = None
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
|
||||
|
||||
def fake_open(filename, mode):
|
||||
return FakeFile(filename, mode)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def faketimermanager(monkeypatch):
|
||||
monkeypatch.setattr(utils.timer.TimerManager, '_instance', FakeTimerManager())
|
||||
fake_timer_manager = FakeTimerManager()
|
||||
monkeypatch.setattr(utils.timer.TimerManager, '_instance', fake_timer_manager)
|
||||
yield fake_timer_manager
|
||||
|
||||
|
||||
def _makedeps(mp3player=FakeMp3Player, nfcreader=FakeNfcReader, buttons=FakeButtons,
|
||||
playlistdb=FakePlaylistDb, hwconfig=FakeHwconfig, leds=FakeLeds):
|
||||
return app.Dependencies(mp3player=lambda _: mp3player() if callable(mp3player) else mp3player,
|
||||
nfcreader=lambda x: nfcreader(x) if callable(nfcreader) else nfcreader,
|
||||
buttons=lambda _: buttons() if callable(buttons) else buttons,
|
||||
playlistdb=lambda _: playlistdb() if callable(playlistdb) else playlistdb,
|
||||
hwconfig=lambda _: hwconfig() if callable(hwconfig) else hwconfig,
|
||||
leds=lambda _: leds() if callable(leds) else leds)
|
||||
|
||||
|
||||
def test_construct_app(micropythonify, faketimermanager):
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda _: FakeNfcReader(),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: FakePlaylistDb())
|
||||
_ = app.PlayerApp(deps)
|
||||
deps = _makedeps(mp3player=fake_mp3)
|
||||
dut = app.PlayerApp(deps)
|
||||
fake_mp3 = dut.player
|
||||
assert fake_mp3.volume is not None
|
||||
|
||||
|
||||
def test_load_playlist_on_tag(micropythonify, faketimermanager, monkeypatch):
|
||||
fake_db = FakePlaylistDb()
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
@@ -130,10 +170,7 @@ def test_load_playlist_on_tag(micropythonify, faketimermanager, monkeypatch):
|
||||
def test_playlist_seq(micropythonify, faketimermanager, monkeypatch):
|
||||
fake_db = FakePlaylistDb([b'track1.mp3', b'track2.mp3', b'track3.mp3'])
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db)
|
||||
dut = app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
@@ -166,10 +203,7 @@ def test_playlist_unknown_tag(micropythonify, faketimermanager, monkeypatch):
|
||||
|
||||
fake_db = FakeNoPlaylistDb()
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
@@ -189,10 +223,7 @@ def test_tagmode_startstop(micropythonify, faketimermanager, monkeypatch):
|
||||
|
||||
fake_db = MyFakePlaylistDb()
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
@@ -202,6 +233,7 @@ def test_tagmode_startstop(micropythonify, faketimermanager, monkeypatch):
|
||||
assert fake_mp3.track.filename == b'test/path.mp3'
|
||||
# Removing tag should not stop playback
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
faketimermanager.testing_run_queued()
|
||||
assert fake_mp3.track is not None
|
||||
assert fake_mp3.track.filename == b'test/path.mp3'
|
||||
# Presenting tag should stop playback
|
||||
@@ -209,6 +241,7 @@ def test_tagmode_startstop(micropythonify, faketimermanager, monkeypatch):
|
||||
assert fake_mp3.track is None
|
||||
# Nothing should change here
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
faketimermanager.testing_run_queued()
|
||||
assert fake_mp3.track is None
|
||||
# Presenting tag again should start playback again
|
||||
FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3])
|
||||
@@ -228,10 +261,7 @@ def test_tagmode_remains(micropythonify, faketimermanager, monkeypatch):
|
||||
|
||||
fake_db = MyFakePlaylistDb()
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
@@ -241,8 +271,52 @@ def test_tagmode_remains(micropythonify, faketimermanager, monkeypatch):
|
||||
assert fake_mp3.track.filename == b'test/path.mp3'
|
||||
# Remove tag to stop playback
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
faketimermanager.testing_run_queued()
|
||||
assert fake_mp3.track is None
|
||||
# Presenting tag again should start playback again
|
||||
FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3])
|
||||
assert fake_mp3.track is not None
|
||||
assert fake_mp3.track.filename == b'test/path.mp3'
|
||||
|
||||
|
||||
def test_led_state(micropythonify, faketimermanager, monkeypatch):
|
||||
fake_leds = FakeLeds()
|
||||
deps = _makedeps(leds=fake_leds)
|
||||
app.PlayerApp(deps)
|
||||
assert fake_leds.state == FakeLeds.IDLE
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3])
|
||||
assert fake_leds.state == FakeLeds.PLAYING
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
faketimermanager.testing_run_queued()
|
||||
assert fake_leds.state == FakeLeds.IDLE
|
||||
|
||||
|
||||
def test_idle_shutdown_after_start(micropythonify, faketimermanager, monkeypatch):
|
||||
fake_hwconfig = FakeHwconfig()
|
||||
fake_hwconfig.on_battery = True
|
||||
deps = _makedeps(hwconfig=fake_hwconfig)
|
||||
app.PlayerApp(deps)
|
||||
assert fake_hwconfig.powered
|
||||
faketimermanager.testing_run_queued()
|
||||
assert not fake_hwconfig.powered
|
||||
|
||||
|
||||
def test_idle_shutdown_after_playback(micropythonify, faketimermanager, monkeypatch):
|
||||
fake_hwconfig = FakeHwconfig()
|
||||
fake_hwconfig.on_battery = True
|
||||
deps = _makedeps(hwconfig=fake_hwconfig)
|
||||
app.PlayerApp(deps)
|
||||
assert fake_hwconfig.powered
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3])
|
||||
faketimermanager.testing_run_queued()
|
||||
assert fake_hwconfig.powered
|
||||
# Stop playback
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
faketimermanager.testing_run_queued()
|
||||
# Elapse idle timer
|
||||
faketimermanager.testing_run_queued()
|
||||
assert not fake_hwconfig.powered
|
||||
|
||||
Reference in New Issue
Block a user