Compare commits
13 Commits
64716ea2d8
...
pcb-rev1.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 419d85209e | |||
| 0f4f72253c | |||
| 08fdb75297 | |||
| d28f0b1c0c | |||
| 9147bab5bb | |||
| 0820ec1fc8 | |||
| 7d3cdbabe4 | |||
| b5e3df1054 | |||
| 55718aa1ff | |||
| 981c75020f | |||
| b56e4f36b4 | |||
| 9ba6e698b9 | |||
| db136ed79a |
@@ -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"
|
||||
|
||||
@@ -13,13 +13,40 @@ VOLUME_CURVE = [1, 2, 4, 8, 16, 32, 63, 126, 251]
|
||||
|
||||
|
||||
class PlayerApp:
|
||||
class TagStateMachine:
|
||||
def __init__(self, parent, timer_manager):
|
||||
self.parent = parent
|
||||
self.timer_manager = timer_manager
|
||||
self.current_tag = None
|
||||
self.current_tag_time = time.ticks_ms()
|
||||
|
||||
def onTagChange(self, new_tag):
|
||||
if new_tag is not None:
|
||||
self.timer_manager.cancel(self.onTagRemoveDelay)
|
||||
if new_tag == self.current_tag:
|
||||
return
|
||||
# Change playlist on new tag
|
||||
if new_tag is not None:
|
||||
self.current_tag_time = time.ticks_ms()
|
||||
self.current_tag = new_tag
|
||||
self.parent.onNewTag(new_tag)
|
||||
else:
|
||||
self.timer_manager.schedule(time.ticks_ms() + 5000, self.onTagRemoveDelay)
|
||||
|
||||
def onTagRemoveDelay(self):
|
||||
if self.current_tag is not None:
|
||||
self.current_tag = None
|
||||
self.parent.onTagRemoved()
|
||||
|
||||
def __init__(self, deps: Dependencies):
|
||||
self.current_tag = None
|
||||
self.current_tag_time = time.ticks_ms()
|
||||
self.timer_manager = TimerManager()
|
||||
self.tag_state_machine = self.TagStateMachine(self, self.timer_manager)
|
||||
self.player = deps.mp3player(self)
|
||||
self.nfc = deps.nfcreader(self)
|
||||
self.nfc = deps.nfcreader(self.tag_state_machine)
|
||||
self.playlist_db = deps.playlistdb(self)
|
||||
self.tag_mode = self.playlist_db.getSetting('tagmode')
|
||||
self.playing_tag = None
|
||||
self.playlist = None
|
||||
self.buttons = deps.buttons(self) if deps.buttons is not None else None
|
||||
self.mp3file = None
|
||||
self.volume_pos = 3
|
||||
@@ -30,28 +57,26 @@ class PlayerApp:
|
||||
self.mp3file.close()
|
||||
self.mp3file = None
|
||||
|
||||
def onTagChange(self, new_tag):
|
||||
if new_tag is not None:
|
||||
self.timer_manager.cancel(self.onTagRemoveDelay)
|
||||
if new_tag == self.current_tag:
|
||||
return
|
||||
# Change playlist on new tag
|
||||
if new_tag is not None:
|
||||
self.current_tag_time = time.ticks_ms()
|
||||
self.current_tag = new_tag
|
||||
uid_str = b''.join('{:02x}'.format(x).encode() for x in new_tag)
|
||||
def onNewTag(self, new_tag):
|
||||
"""
|
||||
Callback (typically called by TagStateMachine) to signal that a new tag has been presented.
|
||||
"""
|
||||
uid_str = b''.join('{:02x}'.format(x).encode() for x in new_tag)
|
||||
if self.tag_mode == 'tagremains' or (self.tag_mode == 'tagstartstop' and new_tag != self.playing_tag):
|
||||
self._set_playlist(uid_str)
|
||||
else:
|
||||
self.timer_manager.schedule(time.ticks_ms() + 5000, self.onTagRemoveDelay)
|
||||
self.playing_tag = new_tag
|
||||
elif self.tag_mode == 'tagstartstop':
|
||||
print('Tag presented again, stopping playback')
|
||||
self._unset_playlist()
|
||||
self.playing_tag = None
|
||||
|
||||
def onTagRemoveDelay(self):
|
||||
if self.current_tag is not None:
|
||||
def onTagRemoved(self):
|
||||
"""
|
||||
Callback (typically called by TagStateMachine) to signal that a tag has been removed.
|
||||
"""
|
||||
if self.tag_mode == 'tagremains':
|
||||
print('Tag gone, stopping playback')
|
||||
self.current_tag = None
|
||||
if self.playlist is not None:
|
||||
pos = self.player.stop()
|
||||
if pos is not None:
|
||||
self.playlist.setPlaybackOffset(pos)
|
||||
self._unset_playlist()
|
||||
|
||||
def onButtonPressed(self, what):
|
||||
assert self.buttons is not None
|
||||
@@ -71,10 +96,18 @@ class PlayerApp:
|
||||
self._play_next()
|
||||
|
||||
def _set_playlist(self, tag: bytes):
|
||||
self._unset_playlist()
|
||||
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)
|
||||
|
||||
def _unset_playlist(self):
|
||||
if self.playlist is not None:
|
||||
pos = self.player.stop()
|
||||
if pos is not None:
|
||||
self.playlist.setPlaybackOffset(pos)
|
||||
self.playlist = None
|
||||
|
||||
def _play_next(self):
|
||||
if self.playlist is None:
|
||||
return
|
||||
@@ -82,6 +115,7 @@ class PlayerApp:
|
||||
self._play(filename)
|
||||
if filename is None:
|
||||
self.playlist = None
|
||||
self.playing_tag = None
|
||||
|
||||
def _play(self, filename: bytes | None, offset=0):
|
||||
if self.mp3file is not None:
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
|
||||
import aiorepl # type: ignore
|
||||
import asyncio
|
||||
from errno import ENOENT
|
||||
import machine
|
||||
import micropython
|
||||
import network
|
||||
import os
|
||||
import time
|
||||
from math import pi, sin, pow
|
||||
|
||||
@@ -54,35 +57,57 @@ async def rainbow(np, period=10):
|
||||
machine.mem32[0x40030000 + 0x00] = 0x10
|
||||
|
||||
|
||||
def setup_wifi():
|
||||
network.hostname("TonberryPico")
|
||||
wlan = network.WLAN(network.WLAN.IF_AP)
|
||||
wlan.config(ssid=f"TonberryPicoAP_{machine.unique_id().hex()}", security=wlan.SEC_OPEN)
|
||||
wlan.active(True)
|
||||
|
||||
|
||||
DB_PATH = '/sd/tonberry.db'
|
||||
|
||||
|
||||
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()
|
||||
|
||||
# Setup MP3 player
|
||||
with SDContext(mosi=hwconfig.SD_DI, miso=hwconfig.SD_DO, sck=hwconfig.SD_SCK, ss=hwconfig.SD_CS,
|
||||
baudrate=hwconfig.SD_CLOCKRATE), \
|
||||
BTreeFileManager('/sd/tonberry.db') as playlistdb, \
|
||||
AudioContext(hwconfig.I2S_DIN, hwconfig.I2S_DCLK, hwconfig.I2S_LRCLK) as audioctx:
|
||||
baudrate=hwconfig.SD_CLOCKRATE):
|
||||
# Temporary hack: build database from folders if no database exists
|
||||
# Can be removed once playlists can be created via API
|
||||
try:
|
||||
_ = os.stat(DB_PATH)
|
||||
except OSError as ex:
|
||||
if ex.errno == ENOENT:
|
||||
print("No playlist DB found, trying to build DB from tag dirs")
|
||||
builddb()
|
||||
|
||||
# Setup NFC
|
||||
reader = MFRC522(spi_id=hwconfig.RC522_SPIID, sck=hwconfig.RC522_SCK, miso=hwconfig.RC522_MISO,
|
||||
mosi=hwconfig.RC522_MOSI, cs=hwconfig.RC522_SS, rst=hwconfig.RC522_RST, tocard_retries=20)
|
||||
with BTreeFileManager(DB_PATH) as playlistdb, \
|
||||
AudioContext(hwconfig.I2S_DIN, hwconfig.I2S_DCLK, hwconfig.I2S_LRCLK) as audioctx:
|
||||
|
||||
# Setup app
|
||||
deps = app.Dependencies(mp3player=lambda the_app: MP3Player(audioctx, the_app),
|
||||
nfcreader=lambda the_app: Nfc(reader, the_app),
|
||||
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)
|
||||
the_app = app.PlayerApp(deps)
|
||||
# Setup NFC
|
||||
reader = MFRC522(spi_id=hwconfig.RC522_SPIID, sck=hwconfig.RC522_SCK, miso=hwconfig.RC522_MISO,
|
||||
mosi=hwconfig.RC522_MOSI, cs=hwconfig.RC522_SS, rst=hwconfig.RC522_RST, tocard_retries=20)
|
||||
|
||||
# Start
|
||||
asyncio.create_task(aiorepl.task({'timer_manager': TimerManager(),
|
||||
'app': the_app}))
|
||||
asyncio.get_event_loop().run_forever()
|
||||
# Setup app
|
||||
deps = app.Dependencies(mp3player=lambda the_app: MP3Player(audioctx, the_app),
|
||||
nfcreader=lambda the_app: Nfc(reader, the_app),
|
||||
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)
|
||||
the_app = app.PlayerApp(deps)
|
||||
|
||||
# Start
|
||||
asyncio.create_task(aiorepl.task({'timer_manager': TimerManager(),
|
||||
'app': the_app}))
|
||||
asyncio.get_event_loop().run_forever()
|
||||
|
||||
|
||||
def builddb():
|
||||
@@ -90,10 +115,11 @@ def builddb():
|
||||
For testing, build a playlist db based on the previous tag directory format.
|
||||
Can be removed once uploading files / playlist via the web api is possible.
|
||||
"""
|
||||
import os
|
||||
|
||||
os.unlink('/sd/tonberry.db')
|
||||
with BTreeFileManager('/sd/tonberry.db') as db:
|
||||
try:
|
||||
os.unlink(DB_PATH)
|
||||
except OSError:
|
||||
pass
|
||||
with BTreeFileManager(DB_PATH) as db:
|
||||
for name, type_, _, _ in os.ilistdir(b'/sd'):
|
||||
if type_ != 0x4000:
|
||||
continue
|
||||
|
||||
@@ -31,6 +31,9 @@ class BTreeDB(IPlaylistDB):
|
||||
PERSIST_NO = b'no'
|
||||
PERSIST_TRACK = b'track'
|
||||
PERSIST_OFFSET = b'offset'
|
||||
DEFAULT_SETTINGS = {
|
||||
b'tagmode': b'tagremains'
|
||||
}
|
||||
|
||||
class Playlist(IPlaylist):
|
||||
def __init__(self, parent: "BTreeDB", tag: bytes, pos: int, persist, shuffle):
|
||||
@@ -269,6 +272,11 @@ class BTreeDB(IPlaylistDB):
|
||||
self._savePlaylist(tag, entries, persist, shuffle)
|
||||
return self.getPlaylistForTag(tag)
|
||||
|
||||
def getSetting(self, key: bytes | str) -> str:
|
||||
if key is str:
|
||||
key = key.encode()
|
||||
return self.db.get(b'settings/' + key, self.DEFAULT_SETTINGS[key]).decode()
|
||||
|
||||
def validate(self, dump=False):
|
||||
"""
|
||||
Validate the structure of the playlist database.
|
||||
@@ -286,6 +294,11 @@ class BTreeDB(IPlaylistDB):
|
||||
fields = k.split(b'/')
|
||||
if len(fields) <= 1:
|
||||
fail(f'Malformed key {k!r}')
|
||||
continue
|
||||
if fields[0] == b'settings':
|
||||
val = self.db[k].decode()
|
||||
print(f'Setting {fields[1].decode()} = {val}')
|
||||
continue
|
||||
if last_tag != fields[0]:
|
||||
last_tag = fields[0]
|
||||
last_pos = None
|
||||
|
||||
@@ -38,14 +38,23 @@ class FakeMp3Player:
|
||||
def play(self, track: FakeFile, offset: int):
|
||||
self.track = track
|
||||
|
||||
def stop(self):
|
||||
self.track = None
|
||||
|
||||
|
||||
class FakeTimerManager:
|
||||
def __init__(self): pass
|
||||
def cancel(self, timer): pass
|
||||
|
||||
def schedule(self, when, what):
|
||||
what()
|
||||
|
||||
|
||||
class FakeNfcReader:
|
||||
def __init__(self): pass
|
||||
tag_callback = None
|
||||
|
||||
def __init__(self, tag_callback=None):
|
||||
FakeNfcReader.tag_callback = tag_callback
|
||||
|
||||
|
||||
class FakeButtons:
|
||||
@@ -76,6 +85,11 @@ class FakePlaylistDb:
|
||||
def getPlaylistForTag(self, tag: bytes):
|
||||
return self.FakePlaylist(self)
|
||||
|
||||
def getSetting(self, key: bytes | str):
|
||||
if key == 'tagmode':
|
||||
return 'tagremains'
|
||||
return None
|
||||
|
||||
|
||||
def fake_open(filename, mode):
|
||||
return FakeFile(filename, mode)
|
||||
@@ -100,13 +114,13 @@ def test_load_playlist_on_tag(micropythonify, faketimermanager, monkeypatch):
|
||||
fake_db = FakePlaylistDb()
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda _: FakeNfcReader(),
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
dut = app.PlayerApp(deps)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
dut.onTagChange([23, 42, 1, 2, 3])
|
||||
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'
|
||||
assert "r" in fake_mp3.track.mode
|
||||
@@ -117,13 +131,13 @@ 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 _: FakeNfcReader(),
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
dut = app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
dut.onTagChange([23, 42, 1, 2, 3])
|
||||
FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3])
|
||||
assert fake_mp3.track is not None
|
||||
assert fake_mp3.track.filename == b'track1.mp3'
|
||||
|
||||
@@ -147,14 +161,88 @@ def test_playlist_unknown_tag(micropythonify, faketimermanager, monkeypatch):
|
||||
def getPlaylistForTag(self, tag):
|
||||
return None
|
||||
|
||||
def getSetting(self, key: bytes | str):
|
||||
return None
|
||||
|
||||
fake_db = FakeNoPlaylistDb()
|
||||
fake_mp3 = FakeMp3Player()
|
||||
deps = app.Dependencies(mp3player=lambda _: fake_mp3,
|
||||
nfcreader=lambda _: FakeNfcReader(),
|
||||
nfcreader=lambda x: FakeNfcReader(x),
|
||||
buttons=lambda _: FakeButtons(),
|
||||
playlistdb=lambda _: fake_db)
|
||||
dut = app.PlayerApp(deps)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
dut.onTagChange([23, 42, 1, 2, 3])
|
||||
FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3])
|
||||
assert fake_mp3.track is None
|
||||
|
||||
|
||||
def test_tagmode_startstop(micropythonify, faketimermanager, monkeypatch):
|
||||
class MyFakePlaylistDb(FakePlaylistDb):
|
||||
def __init__(self, tracklist=[b'test/path.mp3']):
|
||||
super().__init__(tracklist)
|
||||
|
||||
def getSetting(self, key: bytes | str):
|
||||
if key == 'tagmode':
|
||||
return 'tagstartstop'
|
||||
return None
|
||||
|
||||
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)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
# Present tag to start playback
|
||||
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'
|
||||
# Removing tag should not stop playback
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
assert fake_mp3.track is not None
|
||||
assert fake_mp3.track.filename == b'test/path.mp3'
|
||||
# Presenting tag should stop playback
|
||||
FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3])
|
||||
assert fake_mp3.track is None
|
||||
# Nothing should change here
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
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_tagmode_remains(micropythonify, faketimermanager, monkeypatch):
|
||||
class MyFakePlaylistDb(FakePlaylistDb):
|
||||
def __init__(self, tracklist=[b'test/path.mp3']):
|
||||
super().__init__(tracklist)
|
||||
|
||||
def getSetting(self, key: bytes | str):
|
||||
if key == 'tagmode':
|
||||
return 'tagremains'
|
||||
return None
|
||||
|
||||
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)
|
||||
app.PlayerApp(deps)
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(builtins, 'open', fake_open)
|
||||
# Present tag to start playback
|
||||
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'
|
||||
# Remove tag to stop playback
|
||||
FakeNfcReader.tag_callback.onTagChange(None)
|
||||
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'
|
||||
|
||||
Reference in New Issue
Block a user