From f3d5e4018b2ddb46c7e72daae7b7bfefb663e85d Mon Sep 17 00:00:00 2001 From: Taras Syvash Date: Tue, 16 Dec 2025 08:57:32 +0200 Subject: [PATCH 1/3] Add KiCad files and USB CDC logging support --- .vscode/settings.json | 5 +- kicad/.gitignore | 24 + .../watch-watchkicad_pro.kicad_pcb | 2 + .../watch-watchkicad_pro.kicad_pro | 417 + .../watch-watchkicad_pro.kicad_sch | 6810 +++++++++++++++++ .../watch-watchkicad_pro.pdf | Bin 0 -> 182374 bytes main/CMakeLists.txt | 2 +- Kconfig.projbuild => main/Kconfig.projbuild | 29 + main/dcdc_controller.c | 27 +- main/ina226_monitor.c | 13 + main/ina226_monitor.h | 11 + main/main.c | 26 +- main/uart_mux.c | 18 + main/uart_mux.h | 11 + main/usb_cdc_log.c | 98 + main/usb_cdc_log.h | 15 + main/ws2812_status.c | 61 + main/ws2812_status.h | 2 + 18 files changed, 7557 insertions(+), 14 deletions(-) create mode 100644 kicad/.gitignore create mode 100644 kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb create mode 100644 kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro create mode 100644 kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch create mode 100644 kicad/watch-watchkicad_pro/watch-watchkicad_pro.pdf rename Kconfig.projbuild => main/Kconfig.projbuild (86%) create mode 100644 main/usb_cdc_log.c create mode 100644 main/usb_cdc_log.h diff --git a/.vscode/settings.json b/.vscode/settings.json index c196f88..de6459a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,7 @@ "idf.openOcdConfigs": [ "board/esp32s3-builtin.cfg" ], - "idf.port": "/dev/tty.BLTH", + "idf.port": "/dev/tty.usbmodem14301", "idf.toolsPath": "/Users/tarassivas/.espressif", "idf.customExtraVars": { "IDF_TARGET": "esp32s3" @@ -15,5 +15,6 @@ "--background-index", "--query-driver=/Users/tarassivas/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20241119/xtensa-esp-elf/bin/xtensa-esp32-elf-gcc", "--compile-commands-dir=${workspaceFolder}/build" - ] + ], + "idf.flashType": "UART" } diff --git a/kicad/.gitignore b/kicad/.gitignore new file mode 100644 index 0000000..2606624 --- /dev/null +++ b/kicad/.gitignore @@ -0,0 +1,24 @@ +# Temporary and local KiCad artifacts +*-backups/ +*.kicad_prl +*.kicad_pro-bak +*.kicad_sch-bak +*.kicad_pcb-bak +*.sch-bak +*.pcb-bak +*.bak + +# Cache and automatically generated tables +sym-lib-table-bak +fp-lib-table-bak +*-cache.lib +*-cache.dcm + +# Generated fabrication outputs (regenerate as needed) +*.net +*.xml +*.csv +*.tsv +*.pos +*.ipr +*.rpt diff --git a/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb b/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb new file mode 100644 index 0000000..c547853 --- /dev/null +++ b/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb @@ -0,0 +1,2 @@ +(kicad_pcb (version 20241229) (generator "pcbnew") (generator_version "9.0") +) \ No newline at end of file diff --git a/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro b/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro new file mode 100644 index 0000000..35d54ee --- /dev/null +++ b/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro @@ -0,0 +1,417 @@ +{ + "board": { + "3dviewports": [], + "design_settings": { + "defaults": {}, + "diff_pair_dimensions": [], + "drc_exclusions": [], + "rules": {}, + "track_widths": [], + "via_dimensions": [] + }, + "ipc2581": { + "dist": "", + "distpn": "", + "internal_id": "", + "mfg": "", + "mpn": "" + }, + "layer_pairs": [], + "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", + "different_unit_footprint": "error", + "different_unit_net": "error", + "duplicate_reference": "error", + "duplicate_sheet_names": "error", + "endpoint_off_grid": "warning", + "extra_units": "error", + "footprint_filter": "ignore", + "footprint_link_issues": "warning", + "four_way_junction": "ignore", + "global_label_dangling": "warning", + "hier_label_mismatch": "error", + "label_dangling": "error", + "label_multiple_wires": "warning", + "lib_symbol_issues": "warning", + "lib_symbol_mismatch": "warning", + "missing_bidi_pin": "warning", + "missing_input_pin": "warning", + "missing_power_pin": "error", + "missing_unit": "warning", + "multiple_net_names": "warning", + "net_not_bus_member": "warning", + "no_connect_connected": "warning", + "no_connect_dangling": "warning", + "pin_not_connected": "error", + "pin_not_driven": "error", + "pin_to_pin": "warning", + "power_pin_not_driven": "error", + "same_local_global_label": "warning", + "similar_label_and_power": "warning", + "similar_labels": "warning", + "similar_power": "warning", + "simulation_model_issue": "ignore", + "single_global_label": "ignore", + "unannotated": "error", + "unconnected_wire_endpoint": "warning", + "unit_value_mismatch": "error", + "unresolved_variable": "error", + "wire_dangling": "error" + } + }, + "libraries": { + "pinned_footprint_libs": [], + "pinned_symbol_libs": [] + }, + "meta": { + "filename": "watch-watchkicad_pro.kicad_pro", + "version": 3 + }, + "net_settings": { + "classes": [ + { + "bus_width": 12, + "clearance": 0.2, + "diff_pair_gap": 0.25, + "diff_pair_via_gap": 0.25, + "diff_pair_width": 0.2, + "line_style": 0, + "microvia_diameter": 0.3, + "microvia_drill": 0.1, + "name": "Default", + "pcb_color": "rgba(0, 0, 0, 0.000)", + "priority": 2147483647, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.2, + "via_diameter": 0.6, + "via_drill": 0.3, + "wire_width": 6 + } + ], + "meta": { + "version": 4 + }, + "net_colors": null, + "netclass_assignments": null, + "netclass_patterns": [] + }, + "pcbnew": { + "last_paths": { + "gencad": "", + "idf": "", + "netlist": "", + "plot": "", + "pos_files": "", + "specctra_dsn": "", + "step": "", + "svg": "", + "vrml": "" + }, + "page_layout_descr_file": "" + }, + "schematic": { + "annotate_start_num": 0, + "bom_export_filename": "${PROJECTNAME}.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": false, + "label": "Qty", + "name": "${QUANTITY}", + "show": true + }, + { + "group_by": true, + "label": "Value", + "name": "Value", + "show": true + }, + { + "group_by": true, + "label": "DNP", + "name": "${DNP}", + "show": true + }, + { + "group_by": true, + "label": "Exclude from BOM", + "name": "${EXCLUDE_FROM_BOM}", + "show": true + }, + { + "group_by": true, + "label": "Exclude from Board", + "name": "${EXCLUDE_FROM_BOARD}", + "show": true + }, + { + "group_by": true, + "label": "Footprint", + "name": "Footprint", + "show": true + }, + { + "group_by": false, + "label": "Datasheet", + "name": "Datasheet", + "show": true + } + ], + "filter_string": "", + "group_symbols": true, + "include_excluded_from_bom": true, + "name": "Default Editing", + "sort_asc": true, + "sort_field": "Налаштування" + }, + "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": "", + "page_layout_descr_file": "", + "plot_directory": "", + "space_save_all_events": true, + "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": [ + [ + "e717a214-aafd-4c38-81fe-477ca8e64d07", + "Root" + ] + ], + "text_variables": {} +} diff --git a/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch b/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch new file mode 100644 index 0000000..f3943e5 --- /dev/null +++ b/kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch @@ -0,0 +1,6810 @@ +(kicad_sch + (version 20250114) + (generator "eeschema") + (generator_version "9.0") + (uuid "e717a214-aafd-4c38-81fe-477ca8e64d07") + (paper "A4") + (title_block + (title "Watch-Watch") + (date "2025-12-15") + (rev "v0.1") + (company "TComLab") + ) + (lib_symbols + (symbol "74xx:74HC4051" + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "U" + (at -5.08 11.43 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "74HC4051" + (at -7.62 -13.97 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 0 -10.16 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "http://www.ti.com/lit/ds/symlink/cd74hc4051.pdf" + (at 0 -10.16 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "8-channel analog multiplexer/demultiplexer, DIP-16/SOIC-16/TSSOP-16" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "HCMOS Multiplexer Demultiplexer Analog" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_fp_filters" "DIP*W7.62mm* SOIC*3.9x9.9mm*P1.27mm* SOIC*5.3x10.2mm*P1.27mm* TSSOP*4.4x5mm*P0.65mm*" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "74HC4051_0_1" + (rectangle + (start -5.08 10.16) + (end 7.62 -12.7) + (stroke + (width 0.254) + (type default) + ) + (fill + (type background) + ) + ) + ) + (symbol "74HC4051_1_1" + (pin passive line + (at -7.62 7.62 0) + (length 2.54) + (name "A" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "3" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at -7.62 2.54 0) + (length 2.54) + (name "S0" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "11" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at -7.62 0 0) + (length 2.54) + (name "S1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "10" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at -7.62 -2.54 0) + (length 2.54) + (name "S2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "9" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at -7.62 -7.62 0) + (length 2.54) + (name "~{E}" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "6" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin power_in line + (at 0 12.7 270) + (length 2.54) + (name "VCC" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "16" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin power_in line + (at 0 -15.24 90) + (length 2.54) + (name "GND" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "8" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin power_in line + (at 2.54 -15.24 90) + (length 2.54) + (name "VEE" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "7" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 7.62 180) + (length 2.54) + (name "A0" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "13" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 5.08 180) + (length 2.54) + (name "A1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "14" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 2.54 180) + (length 2.54) + (name "A2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "15" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 0 180) + (length 2.54) + (name "A3" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "12" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 -2.54 180) + (length 2.54) + (name "A4" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 -5.08 180) + (length 2.54) + (name "A5" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "5" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 -7.62 180) + (length 2.54) + (name "A6" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 10.16 -10.16 180) + (length 2.54) + (name "A7" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "4" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "Device:C" + (pin_numbers + (hide yes) + ) + (pin_names + (offset 0.254) + ) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "C" + (at 0.635 2.54 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "C" + (at 0.635 -2.54 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "" + (at 0.9652 -3.81 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" "Unpolarized capacitor" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "cap capacitor" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_fp_filters" "C_*" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "C_0_1" + (polyline + (pts + (xy -2.032 0.762) (xy 2.032 0.762) + ) + (stroke + (width 0.508) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy -2.032 -0.762) (xy 2.032 -0.762) + ) + (stroke + (width 0.508) + (type default) + ) + (fill + (type none) + ) + ) + ) + (symbol "C_1_1" + (pin passive line + (at 0 3.81 270) + (length 2.794) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 0 -3.81 90) + (length 2.794) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "Device:R" + (pin_numbers + (hide yes) + ) + (pin_names + (offset 0) + ) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "R" + (at 2.032 0 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "R" + (at 0 0 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at -1.778 0 90) + (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" "Resistor" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "R res resistor" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_fp_filters" "R_*" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "R_0_1" + (rectangle + (start -1.016 -2.54) + (end 1.016 2.54) + (stroke + (width 0.254) + (type default) + ) + (fill + (type none) + ) + ) + ) + (symbol "R_1_1" + (pin passive line + (at 0 3.81 270) + (length 1.27) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 0 -3.81 90) + (length 1.27) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "ESP32-S3-ZERO:ESP32-S3-ZERO" + (pin_names + (offset 1.016) + ) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "U" + (at -12.7 18.542 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + ) + (property "Value" "ESP32-S3-ZERO" + (at -12.7 -20.32 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + ) + (property "Footprint" "ESP32-S3-ZERO:MODULE_ESP32-S3-ZERO" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "MF" "Waveshare Electronics" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "MAXIMUM_PACKAGE_HEIGHT" "4.35mm" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "CREATOR" "ANA" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Price" "None" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Package" "None" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Check_prices" "https://www.snapeda.com/parts/ESP32-S3-Zero/Waveshare+Electronics/view-part/?ref=eda" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "STANDARD" "Manufacturer Recommendations" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "PARTREV" "NA" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "VERIFIER" "RODRIGO" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "SnapEDA_Link" "https://www.snapeda.com/parts/ESP32-S3-Zero/Waveshare+Electronics/view-part/?ref=snap" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "MP" "ESP32-S3-Zero" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Description_1" "ESP32-S3 Mini Development Board, Based on ESP32-S3FH4R2 Dual-Core Processor, 240MHz Running Frequency, 2.4GHz Wi-Fi & Bluetooth 5" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Availability" "Not in stock" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "MANUFACTURER" "Waveshare" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (symbol "ESP32-S3-ZERO_0_0" + (rectangle + (start -12.7 -17.78) + (end 12.7 17.78) + (stroke + (width 0.254) + (type default) + ) + (fill + (type background) + ) + ) + (pin input line + (at -17.78 7.62 0) + (length 5.08) + (name "RX" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "RX" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin output line + (at -17.78 5.08 0) + (length 5.08) + (name "TX" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "TX" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at -17.78 0 0) + (length 5.08) + (name "GP1" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "1" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at -17.78 -2.54 0) + (length 5.08) + (name "GP2" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "2" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at -17.78 -5.08 0) + (length 5.08) + (name "GP3" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "3" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at -17.78 -7.62 0) + (length 5.08) + (name "GP4" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "4" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at -17.78 -10.16 0) + (length 5.08) + (name "GP5" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "5" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin power_in line + (at 17.78 15.24 180) + (length 5.08) + (name "5V" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "5V" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin power_in line + (at 17.78 12.7 180) + (length 5.08) + (name "3V3(OUT)" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "3V3" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 7.62 180) + (length 5.08) + (name "GP6" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "6" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 5.08 180) + (length 5.08) + (name "GP7" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "7" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 2.54 180) + (length 5.08) + (name "GP8" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "8" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 0 180) + (length 5.08) + (name "GP9" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "9" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 -2.54 180) + (length 5.08) + (name "GP10" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "10" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 -5.08 180) + (length 5.08) + (name "GP11" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "11" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 -7.62 180) + (length 5.08) + (name "GP12" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "12" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin bidirectional line + (at 17.78 -10.16 180) + (length 5.08) + (name "GP13" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "13" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + (pin power_in line + (at 17.78 -15.24 180) + (length 5.08) + (name "GND" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + (number "GND" + (effects + (font + (size 1.016 1.016) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "Isolator:PC817" + (pin_names + (offset 1.016) + ) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "U" + (at -5.08 5.08 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "PC817" + (at 0 5.08 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Package_DIP:DIP-4_W7.62mm" + (at -5.08 -5.08 0) + (effects + (font + (size 1.27 1.27) + (italic yes) + ) + (justify left) + (hide yes) + ) + ) + (property "Datasheet" "http://www.soselectronic.cz/a_info/resource/d/pc817.pdf" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + (hide yes) + ) + ) + (property "Description" "DC Optocoupler, Vce 35V, CTR 50-300%, DIP-4" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "NPN DC Optocoupler" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_fp_filters" "DIP*W7.62mm*" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "PC817_0_1" + (rectangle + (start -5.08 3.81) + (end 5.08 -3.81) + (stroke + (width 0.254) + (type default) + ) + (fill + (type background) + ) + ) + (polyline + (pts + (xy -5.08 2.54) (xy -2.54 2.54) (xy -2.54 -0.635) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy -3.175 -0.635) (xy -1.905 -0.635) + ) + (stroke + (width 0.254) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy -2.54 -0.635) (xy -2.54 -2.54) (xy -5.08 -2.54) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy -2.54 -0.635) (xy -3.175 0.635) (xy -1.905 0.635) (xy -2.54 -0.635) + ) + (stroke + (width 0.254) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy -0.508 0.508) (xy 0.762 0.508) (xy 0.381 0.381) (xy 0.381 0.635) (xy 0.762 0.508) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy -0.508 -0.508) (xy 0.762 -0.508) (xy 0.381 -0.635) (xy 0.381 -0.381) (xy 0.762 -0.508) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 2.54 1.905) (xy 2.54 -1.905) (xy 2.54 -1.905) + ) + (stroke + (width 0.508) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 2.54 0.635) (xy 4.445 2.54) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 3.048 -1.651) (xy 3.556 -1.143) (xy 4.064 -2.159) (xy 3.048 -1.651) (xy 3.048 -1.651) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type outline) + ) + ) + (polyline + (pts + (xy 4.445 2.54) (xy 5.08 2.54) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 4.445 -2.54) (xy 2.54 -0.635) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type outline) + ) + ) + (polyline + (pts + (xy 4.445 -2.54) (xy 5.08 -2.54) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + ) + (symbol "PC817_1_1" + (pin passive line + (at -7.62 2.54 0) + (length 2.54) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at -7.62 -2.54 0) + (length 2.54) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 7.62 2.54 180) + (length 2.54) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "4" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin passive line + (at 7.62 -2.54 180) + (length 2.54) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "3" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "LED:WS2812B" + (pin_names + (offset 0.254) + ) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "D" + (at 5.08 5.715 0) + (effects + (font + (size 1.27 1.27) + ) + (justify right bottom) + ) + ) + (property "Value" "WS2812B" + (at 1.27 -5.715 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + ) + ) + (property "Footprint" "LED_SMD:LED_WS2812B_PLCC4_5.0x5.0mm_P3.2mm" + (at 1.27 -7.62 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Datasheet" "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" + (at 2.54 -9.525 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Description" "RGB LED with integrated controller" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "RGB LED NeoPixel addressable" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_fp_filters" "LED*WS2812*PLCC*5.0x5.0mm*P3.2mm*" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "WS2812B_0_0" + (text "RGB" + (at 2.286 -4.191 0) + (effects + (font + (size 0.762 0.762) + ) + ) + ) + ) + (symbol "WS2812B_0_1" + (polyline + (pts + (xy 1.27 -2.54) (xy 1.778 -2.54) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 1.27 -3.556) (xy 1.778 -3.556) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 2.286 -1.524) (xy 1.27 -2.54) (xy 1.27 -2.032) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 2.286 -2.54) (xy 1.27 -3.556) (xy 1.27 -3.048) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 3.683 -1.016) (xy 3.683 -3.556) (xy 3.683 -4.064) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 4.699 -1.524) (xy 2.667 -1.524) (xy 3.683 -3.556) (xy 4.699 -1.524) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 4.699 -3.556) (xy 2.667 -3.556) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (rectangle + (start 5.08 5.08) + (end -5.08 -5.08) + (stroke + (width 0.254) + (type default) + ) + (fill + (type background) + ) + ) + ) + (symbol "WS2812B_1_1" + (pin input line + (at -7.62 0 0) + (length 2.54) + (name "DIN" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "4" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin power_in line + (at 0 7.62 270) + (length 2.54) + (name "VDD" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin power_in line + (at 0 -7.62 90) + (length 2.54) + (name "VSS" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "3" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin output line + (at 7.62 0 180) + (length 2.54) + (name "DOUT" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "Sensor_Energy:INA226" + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "U" + (at -6.35 11.43 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "INA226" + (at 3.81 11.43 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "Package_SO:VSSOP-10_3x3mm_P0.5mm" + (at 20.32 -11.43 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "http://www.ti.com/lit/ds/symlink/ina226.pdf" + (at 8.89 -2.54 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "High-Side or Low-Side Measurement, Bi-Directional Current and Power Monitor (0-36V) with I2C Compatible Interface, VSSOP-10" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "ADC I2C 16-Bit Oversampling Current Shunt" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_fp_filters" "VSSOP*3x3mm*P0.5mm*" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "INA226_0_1" + (rectangle + (start 7.62 10.16) + (end -7.62 -10.16) + (stroke + (width 0.254) + (type default) + ) + (fill + (type background) + ) + ) + ) + (symbol "INA226_1_1" + (pin input line + (at -10.16 7.62 0) + (length 2.54) + (name "Vbus" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "8" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at -10.16 -2.54 0) + (length 2.54) + (name "Vin+" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "10" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at -10.16 -5.08 0) + (length 2.54) + (name "Vin-" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "9" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin power_in line + (at 0 12.7 270) + (length 2.54) + (name "VS" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "6" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin power_in line + (at 0 -12.7 90) + (length 2.54) + (name "GND" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "7" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at 10.16 7.62 180) + (length 2.54) + (name "A1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at 10.16 5.08 180) + (length 2.54) + (name "A0" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "2" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin bidirectional line + (at 10.16 0 180) + (length 2.54) + (name "SDA" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "4" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin input line + (at 10.16 -2.54 180) + (length 2.54) + (name "SCL" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "5" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + (pin open_collector line + (at 10.16 -7.62 180) + (length 2.54) + (name "~{Alert}" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "3" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "power:+3.3V" + (power) + (pin_numbers + (hide yes) + ) + (pin_names + (offset 0) + (hide yes) + ) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "#PWR" + (at 0 -3.81 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "+3.3V" + (at 0 3.556 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" "Power symbol creates a global label with name \"+3.3V\"" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "global power" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "+3.3V_0_1" + (polyline + (pts + (xy -0.762 1.27) (xy 0 2.54) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 0 2.54) (xy 0.762 1.27) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + (polyline + (pts + (xy 0 0) (xy 0 2.54) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + ) + (symbol "+3.3V_1_1" + (pin power_in line + (at 0 0 90) + (length 0) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + (symbol "power:GND" + (power) + (pin_numbers + (hide yes) + ) + (pin_names + (offset 0) + (hide yes) + ) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (property "Reference" "#PWR" + (at 0 -6.35 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "GND" + (at 0 -3.81 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"GND\" , ground" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "ki_keywords" "global power" + (at 0 0 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (symbol "GND_0_1" + (polyline + (pts + (xy 0 0) (xy 0 -1.27) (xy 1.27 -1.27) (xy 0 -2.54) (xy -1.27 -1.27) (xy 0 -1.27) + ) + (stroke + (width 0) + (type default) + ) + (fill + (type none) + ) + ) + ) + (symbol "GND_1_1" + (pin power_in line + (at 0 0 270) + (length 0) + (name "~" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (number "1" + (effects + (font + (size 1.27 1.27) + ) + ) + ) + ) + ) + (embedded_fonts no) + ) + ) + (junction + (at 181.61 78.74) + (diameter 0) + (color 0 0 0 0) + (uuid "025676d1-d598-449f-a43b-190644509707") + ) + (junction + (at 179.07 81.28) + (diameter 0) + (color 0 0 0 0) + (uuid "1dcd9958-f109-4ca4-ad2f-bb902e07f9dd") + ) + (junction + (at 68.58 69.85) + (diameter 0) + (color 0 0 0 0) + (uuid "1e83bb27-a305-4c14-a17d-2a5e06ae2c9d") + ) + (junction + (at 101.6 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "39bf179d-d71c-49a7-bba9-39c85576b620") + ) + (junction + (at 111.76 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "44006762-39c7-4640-bcd9-e56f9e382f46") + ) + (junction + (at 124.46 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "4aca34ad-ee1c-484e-bedd-41362471c3f7") + ) + (junction + (at 146.05 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "50adca50-55bc-42d4-b3d9-bd494bf74ea5") + ) + (junction + (at 134.62 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "5e27e00d-6457-4650-8a5e-73064ac79f40") + ) + (junction + (at 101.6 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "60893429-9058-4e12-addf-ee817be47139") + ) + (junction + (at 195.58 140.97) + (diameter 0) + (color 0 0 0 0) + (uuid "66556aaf-a999-4643-85eb-1239e4bd9970") + ) + (junction + (at 134.62 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "81c9f65b-9457-4bb9-92d8-ddfa66b6166a") + ) + (junction + (at 124.46 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "8337673a-2571-4f2a-93c9-da35b8acaf72") + ) + (junction + (at 156.21 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "845ce7e1-475e-4c59-bb38-1d9f378b3793") + ) + (junction + (at 90.17 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "884dd5bf-730f-4411-bf4f-a0a787ee4194") + ) + (junction + (at 78.74 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "934676c2-e910-43ee-ae69-50a2753a297a") + ) + (junction + (at 218.44 99.06) + (diameter 0) + (color 0 0 0 0) + (uuid "9b0e692a-7f0c-4074-9ef1-7bba924892f6") + ) + (junction + (at 68.58 85.09) + (diameter 0) + (color 0 0 0 0) + (uuid "9e208518-7a40-4b84-8523-e1618240ac7d") + ) + (junction + (at 198.12 140.97) + (diameter 0) + (color 0 0 0 0) + (uuid "adbfeee5-66d9-4201-8862-40f53b2d0bf8") + ) + (junction + (at 146.05 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "b51cfd0f-47a2-4b52-ba93-0eb5cd48890c") + ) + (junction + (at 78.74 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "b8ab47f4-3278-4782-aaf5-46e3754e01d3") + ) + (junction + (at 176.53 83.82) + (diameter 0) + (color 0 0 0 0) + (uuid "b9626a86-8826-4969-ad2b-d18b42cc4371") + ) + (junction + (at 111.76 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "b9c575d8-56e9-4769-a1ed-423d5503f8e0") + ) + (junction + (at 68.58 55.88) + (diameter 0) + (color 0 0 0 0) + (uuid "ba3f444b-e86a-4424-8eb2-0212eb7385eb") + ) + (junction + (at 90.17 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "be2dd4ce-6711-4f7d-b335-afe1f4f433a4") + ) + (junction + (at 88.9 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "c7c3001a-d636-4523-bb54-98b56515291f") + ) + (junction + (at 215.9 99.06) + (diameter 0) + (color 0 0 0 0) + (uuid "c8a0a632-a963-4c4d-8386-434f5ad61637") + ) + (junction + (at 168.91 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "c9299e03-15d2-44be-81d5-48a7280fc384") + ) + (junction + (at 68.58 100.33) + (diameter 0) + (color 0 0 0 0) + (uuid "db2f2a62-3a1d-4a2d-9649-04dd4f2167b7") + ) + (junction + (at 156.21 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "f0fb4dc8-dd5a-4063-8d51-3ce4b6317d2b") + ) + (junction + (at 88.9 157.48) + (diameter 0) + (color 0 0 0 0) + (uuid "f28178d0-9f05-4b70-883d-bcb65d5673ef") + ) + (junction + (at 168.91 134.62) + (diameter 0) + (color 0 0 0 0) + (uuid "f94640ea-9099-4970-a05f-4e5cef1af4c3") + ) + (wire + (pts + (xy 139.7 45.72) (xy 146.05 45.72) + ) + (stroke + (width 0) + (type default) + ) + (uuid "01087baa-4f37-4039-8c5c-bf1ad9ed260e") + ) + (wire + (pts + (xy 111.76 134.62) (xy 111.76 149.86) + ) + (stroke + (width 0) + (type default) + ) + (uuid "02cbd9ed-d25e-4ff9-bcd9-8fa3369bc184") + ) + (wire + (pts + (xy 104.14 58.42) (xy 92.71 58.42) + ) + (stroke + (width 0) + (type default) + ) + (uuid "04b3cb98-59f4-4666-bc7c-c0d7521d6b16") + ) + (wire + (pts + (xy 124.46 157.48) (xy 111.76 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "0864a91f-75dd-4260-9944-ef22aabb9705") + ) + (wire + (pts + (xy 198.12 138.43) (xy 198.12 140.97) + ) + (stroke + (width 0) + (type default) + ) + (uuid "0a6e97c0-8f17-4789-9026-43e094ead824") + ) + (wire + (pts + (xy 146.05 125.73) (xy 45.72 125.73) + ) + (stroke + (width 0) + (type default) + ) + (uuid "0d109f92-5615-418c-b916-a64842b6ac8c") + ) + (wire + (pts + (xy 179.07 134.62) (xy 168.91 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "0d3d0b1a-470b-4f77-8fa1-196853428082") + ) + (wire + (pts + (xy 88.9 35.56) (xy 78.74 35.56) + ) + (stroke + (width 0) + (type default) + ) + (uuid "0d613ea8-1ca6-4030-be7b-dc439950426a") + ) + (wire + (pts + (xy 176.53 125.73) (xy 176.53 83.82) + ) + (stroke + (width 0) + (type default) + ) + (uuid "0f5d1915-6f8e-4e98-b8b5-3c01c7db8dc8") + ) + (wire + (pts + (xy 63.5 50.8) (xy 71.12 50.8) + ) + (stroke + (width 0) + (type default) + ) + (uuid "10799bde-8725-4a0f-b659-0fb5a3a86170") + ) + (wire + (pts + (xy 78.74 134.62) (xy 88.9 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "15c17265-c729-4f2a-8c04-f0e1235d9d02") + ) + (wire + (pts + (xy 208.28 88.9) (xy 208.28 99.06) + ) + (stroke + (width 0) + (type default) + ) + (uuid "175454a1-5bb4-4616-8e04-c7c03e902882") + ) + (wire + (pts + (xy 146.05 138.43) (xy 146.05 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "186cb1a1-4bde-41c8-96c8-08ea0eb4bede") + ) + (wire + (pts + (xy 124.46 138.43) (xy 124.46 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "18971e37-a8a4-4fac-b98e-ec6dd44e7b68") + ) + (wire + (pts + (xy 104.14 40.64) (xy 100.33 40.64) + ) + (stroke + (width 0) + (type default) + ) + (uuid "191af3f0-1473-42c5-9d50-874c078c971c") + ) + (wire + (pts + (xy 153.67 146.05) (xy 161.29 146.05) + ) + (stroke + (width 0) + (type default) + ) + (uuid "19a325a2-0318-425b-be13-aafd114c8102") + ) + (wire + (pts + (xy 179.07 81.28) (xy 208.28 81.28) + ) + (stroke + (width 0) + (type default) + ) + (uuid "1b4d22e4-6da4-4010-a967-751a3215c46a") + ) + (wire + (pts + (xy 101.6 134.62) (xy 101.6 138.43) + ) + (stroke + (width 0) + (type default) + ) + (uuid "1c15e022-dfa1-4d24-bd4c-a6fb3d89aada") + ) + (wire + (pts + (xy 90.17 157.48) (xy 90.17 158.75) + ) + (stroke + (width 0) + (type default) + ) + (uuid "1c629ca3-1d08-4899-acf7-9f8e19004f67") + ) + (wire + (pts + (xy 104.14 55.88) (xy 90.17 55.88) + ) + (stroke + (width 0) + (type default) + ) + (uuid "1eba6fcb-47c4-4bd9-b1f1-11f8e9bd34f6") + ) + (wire + (pts + (xy 226.06 81.28) (xy 246.38 81.28) + ) + (stroke + (width 0) + (type default) + ) + (uuid "21ddb723-522b-4769-88f4-d6225e8e66df") + ) + (wire + (pts + (xy 168.91 157.48) (xy 156.21 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "22ac27fe-860b-4794-9438-81f6f4a5227e") + ) + (wire + (pts + (xy 168.91 134.62) (xy 156.21 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "22f9d08d-1c84-49e6-9e1b-fe04a5279762") + ) + (wire + (pts + (xy 205.74 118.11) (xy 238.76 118.11) + ) + (stroke + (width 0) + (type default) + ) + (uuid "25f4dc67-6c3e-448d-96a0-053f99acd33e") + ) + (wire + (pts + (xy 205.74 115.57) (xy 238.76 115.57) + ) + (stroke + (width 0) + (type default) + ) + (uuid "25f62168-4a63-444a-b84e-038ccd293304") + ) + (wire + (pts + (xy 187.96 120.65) (xy 181.61 120.65) + ) + (stroke + (width 0) + (type default) + ) + (uuid "26b6ff24-4e90-4d1c-aeb1-2cfaed0bcabd") + ) + (wire + (pts + (xy 168.91 153.67) (xy 168.91 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "278a49d5-3c18-4a87-9c50-5f41937b3714") + ) + (wire + (pts + (xy 63.5 85.09) (xy 68.58 85.09) + ) + (stroke + (width 0) + (type default) + ) + (uuid "27d8f3c8-23f7-4557-a05c-8651d97e1b06") + ) + (wire + (pts + (xy 187.96 123.19) (xy 179.07 123.19) + ) + (stroke + (width 0) + (type default) + ) + (uuid "2866f504-ed77-4610-bcc3-d97ac2a9134f") + ) + (wire + (pts + (xy 68.58 85.09) (xy 68.58 100.33) + ) + (stroke + (width 0) + (type default) + ) + (uuid "287715a3-666d-4591-a1e1-ed2205e95cb6") + ) + (wire + (pts + (xy 90.17 80.01) (xy 78.74 80.01) + ) + (stroke + (width 0) + (type default) + ) + (uuid "28ca0085-1f5c-4177-be69-f7210a7220b2") + ) + (wire + (pts + (xy 63.5 100.33) (xy 68.58 100.33) + ) + (stroke + (width 0) + (type default) + ) + (uuid "29aa39dd-3e40-49d7-bbff-e10745da9464") + ) + (wire + (pts + (xy 139.7 53.34) (xy 167.64 53.34) + ) + (stroke + (width 0) + (type default) + ) + (uuid "29f12bf2-01fe-4644-b7a9-856b251c515b") + ) + (wire + (pts + (xy 139.7 40.64) (xy 165.1 40.64) + ) + (stroke + (width 0) + (type default) + ) + (uuid "2c45e4cf-0804-4341-b238-9997ff28930b") + ) + (wire + (pts + (xy 181.61 78.74) (xy 208.28 78.74) + ) + (stroke + (width 0) + (type default) + ) + (uuid "2d460e39-52b4-42c5-9fc3-eef6feb4028c") + ) + (wire + (pts + (xy 165.1 83.82) (xy 176.53 83.82) + ) + (stroke + (width 0) + (type default) + ) + (uuid "2e667a15-f0ab-4b68-9ff9-5a8f36d939b9") + ) + (wire + (pts + (xy 58.42 146.05) (xy 71.12 146.05) + ) + (stroke + (width 0) + (type default) + ) + (uuid "315893c3-c7f0-409c-84cf-8661d5351ddc") + ) + (wire + (pts + (xy 156.21 134.62) (xy 156.21 149.86) + ) + (stroke + (width 0) + (type default) + ) + (uuid "33ac0ccb-f03f-454b-84cf-6be442b052b7") + ) + (wire + (pts + (xy 205.74 125.73) (xy 238.76 125.73) + ) + (stroke + (width 0) + (type default) + ) + (uuid "36c1e6d7-ec5c-4fd4-bbcb-45aaf2848d49") + ) + (wire + (pts + (xy 100.33 115.57) (xy 187.96 115.57) + ) + (stroke + (width 0) + (type default) + ) + (uuid "3a25e22b-8bb1-4882-94bc-ec4889e3c9cc") + ) + (wire + (pts + (xy 215.9 99.06) (xy 218.44 99.06) + ) + (stroke + (width 0) + (type default) + ) + (uuid "3af46651-b99d-47cd-8d0e-2ce2fd10715a") + ) + (wire + (pts + (xy 166.37 81.28) (xy 179.07 81.28) + ) + (stroke + (width 0) + (type default) + ) + (uuid "42613fa4-b0a9-4d7e-8c60-d7f3dca9b47b") + ) + (wire + (pts + (xy 45.72 146.05) (xy 50.8 146.05) + ) + (stroke + (width 0) + (type default) + ) + (uuid "480c95c4-2a9e-46fe-991d-f55daa2ad7fb") + ) + (wire + (pts + (xy 226.06 73.66) (xy 246.38 73.66) + ) + (stroke + (width 0) + (type default) + ) + (uuid "4ab9999a-5edd-4684-ac4b-cd5a2a12db2e") + ) + (wire + (pts + (xy 134.62 134.62) (xy 146.05 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "4e84766a-d6c3-4c7d-af37-466b52f3edf2") + ) + (wire + (pts + (xy 139.7 55.88) (xy 166.37 55.88) + ) + (stroke + (width 0) + (type default) + ) + (uuid "4f88049b-aca4-49dc-9c2b-ef65622e2ca8") + ) + (wire + (pts + (xy 68.58 69.85) (xy 68.58 85.09) + ) + (stroke + (width 0) + (type default) + ) + (uuid "51674df1-d203-4a70-b791-48320d8ef10c") + ) + (wire + (pts + (xy 63.5 69.85) (xy 68.58 69.85) + ) + (stroke + (width 0) + (type default) + ) + (uuid "53e2224f-eeaf-434a-a05a-d1a6ca356b1d") + ) + (wire + (pts + (xy 156.21 157.48) (xy 146.05 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "591193cb-355a-408a-9ef2-0babd2940750") + ) + (wire + (pts + (xy 132.08 146.05) (xy 138.43 146.05) + ) + (stroke + (width 0) + (type default) + ) + (uuid "5afad8f1-e352-4ac8-840c-72eecfd72450") + ) + (wire + (pts + (xy 78.74 153.67) (xy 78.74 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "5b1a052d-fc3d-49a4-b0c7-f21bd069d71e") + ) + (wire + (pts + (xy 187.96 140.97) (xy 195.58 140.97) + ) + (stroke + (width 0) + (type default) + ) + (uuid "5d1e3faf-4185-4f55-acab-e2e29e186dea") + ) + (wire + (pts + (xy 88.9 48.26) (xy 88.9 35.56) + ) + (stroke + (width 0) + (type default) + ) + (uuid "5f261417-e34e-4067-afbc-bd8f4c63feee") + ) + (wire + (pts + (xy 198.12 140.97) (xy 198.12 142.24) + ) + (stroke + (width 0) + (type default) + ) + (uuid "6140dc80-9178-424c-b041-a77da4de9cf2") + ) + (wire + (pts + (xy 104.14 53.34) (xy 88.9 53.34) + ) + (stroke + (width 0) + (type default) + ) + (uuid "6404fc7c-73cd-4f62-a301-ea7ce0c58643") + ) + (wire + (pts + (xy 90.17 157.48) (xy 101.6 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "64b00721-b2e9-489f-9546-24e17c93b99b") + ) + (wire + (pts + (xy 205.74 120.65) (xy 238.76 120.65) + ) + (stroke + (width 0) + (type default) + ) + (uuid "6691acd2-0a75-46e7-be1d-e1b61ebb9a9c") + ) + (wire + (pts + (xy 88.9 64.77) (xy 78.74 64.77) + ) + (stroke + (width 0) + (type default) + ) + (uuid "69c9e580-99c0-470f-b501-a9b823113bb6") + ) + (wire + (pts + (xy 124.46 134.62) (xy 134.62 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "6a9de8c0-a7d6-4fad-8f8b-34c0554e1132") + ) + (wire + (pts + (xy 88.9 157.48) (xy 90.17 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "6cd13438-5861-4688-9bfa-b573e9cb91a0") + ) + (wire + (pts + (xy 45.72 125.73) (xy 45.72 146.05) + ) + (stroke + (width 0) + (type default) + ) + (uuid "6fb352e9-e1e8-422c-9cad-bc1d5d4c2202") + ) + (wire + (pts + (xy 67.31 157.48) (xy 78.74 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "71f9736f-0cae-4a5c-bd01-bf5e9016ca22") + ) + (wire + (pts + (xy 166.37 55.88) (xy 166.37 81.28) + ) + (stroke + (width 0) + (type default) + ) + (uuid "73864cab-b534-4c78-9b3d-b0f812267bf3") + ) + (wire + (pts + (xy 101.6 43.18) (xy 101.6 73.66) + ) + (stroke + (width 0) + (type default) + ) + (uuid "74fafb5e-f96a-4575-b866-1e37d7356406") + ) + (wire + (pts + (xy 90.17 55.88) (xy 90.17 80.01) + ) + (stroke + (width 0) + (type default) + ) + (uuid "792ca6c0-27ba-4d4f-9804-2fb6473cbc46") + ) + (wire + (pts + (xy 179.07 123.19) (xy 179.07 81.28) + ) + (stroke + (width 0) + (type default) + ) + (uuid "7a0734c6-347b-4388-bccb-0a3a1c580f42") + ) + (wire + (pts + (xy 226.06 78.74) (xy 246.38 78.74) + ) + (stroke + (width 0) + (type default) + ) + (uuid "7b30d1bd-e8b7-4d70-bbdf-090b044dbdbe") + ) + (wire + (pts + (xy 101.6 134.62) (xy 111.76 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "7cee793c-dfd1-4e0a-b775-f18948055e3d") + ) + (wire + (pts + (xy 168.91 157.48) (xy 179.07 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "7f87b143-efd4-46cc-80f8-18461dee5921") + ) + (wire + (pts + (xy 139.7 58.42) (xy 165.1 58.42) + ) + (stroke + (width 0) + (type default) + ) + (uuid "82960223-02e5-42d9-9fe5-f67cedc355ad") + ) + (wire + (pts + (xy 181.61 120.65) (xy 181.61 78.74) + ) + (stroke + (width 0) + (type default) + ) + (uuid "8372ca62-09b3-4a05-b734-e66efe1c1762") + ) + (wire + (pts + (xy 78.74 138.43) (xy 78.74 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "84557677-67b9-4037-ac1b-b1560091fb45") + ) + (wire + (pts + (xy 68.58 55.88) (xy 68.58 69.85) + ) + (stroke + (width 0) + (type default) + ) + (uuid "84bc93df-cc41-47b2-b0c2-1644fa2d6b93") + ) + (wire + (pts + (xy 146.05 153.67) (xy 146.05 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "8ed6ae1e-f672-44cb-8851-f56591b3912a") + ) + (wire + (pts + (xy 68.58 100.33) (xy 68.58 104.14) + ) + (stroke + (width 0) + (type default) + ) + (uuid "9266ebf7-358e-4599-ad17-ccb8737a0840") + ) + (wire + (pts + (xy 146.05 157.48) (xy 134.62 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "9423ae9e-ee8a-4136-b875-910b81038dd0") + ) + (wire + (pts + (xy 104.14 48.26) (xy 88.9 48.26) + ) + (stroke + (width 0) + (type default) + ) + (uuid "95324224-6685-43ce-9533-2978aa321687") + ) + (wire + (pts + (xy 111.76 134.62) (xy 124.46 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "987a58ea-84f1-4aef-8bcb-49fc9d048055") + ) + (wire + (pts + (xy 68.58 40.64) (xy 68.58 55.88) + ) + (stroke + (width 0) + (type default) + ) + (uuid "98d1f7fa-c68d-4301-ae5d-cc69d3c68ee1") + ) + (wire + (pts + (xy 187.96 125.73) (xy 176.53 125.73) + ) + (stroke + (width 0) + (type default) + ) + (uuid "9b399f5e-32f5-4a4f-bf8e-87aa943d9d3f") + ) + (wire + (pts + (xy 67.31 149.86) (xy 67.31 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "9b945234-8735-4235-bf23-8269724a0ef5") + ) + (wire + (pts + (xy 104.14 43.18) (xy 101.6 43.18) + ) + (stroke + (width 0) + (type default) + ) + (uuid "9ba61fee-f6f5-49b6-8f2c-3c5e25b33b2b") + ) + (wire + (pts + (xy 215.9 96.52) (xy 215.9 99.06) + ) + (stroke + (width 0) + (type default) + ) + (uuid "9ca55130-e37e-40b7-a4ad-1c0fa292acd5") + ) + (wire + (pts + (xy 78.74 50.8) (xy 104.14 50.8) + ) + (stroke + (width 0) + (type default) + ) + (uuid "9eea956b-7747-40ef-a85f-007da07d1f64") + ) + (wire + (pts + (xy 86.36 146.05) (xy 93.98 146.05) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a0edf13e-13b6-4d63-809a-4b3696baa4b7") + ) + (wire + (pts + (xy 100.33 40.64) (xy 100.33 115.57) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a6880745-e083-44fd-bbb1-6efce1d64c10") + ) + (wire + (pts + (xy 187.96 130.81) (xy 187.96 140.97) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a6ab97ee-f0b0-4f35-b876-1c075a4535cd") + ) + (wire + (pts + (xy 208.28 99.06) (xy 215.9 99.06) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a6ba1fa4-7434-41de-91da-5d5e261e9f1c") + ) + (wire + (pts + (xy 168.91 138.43) (xy 168.91 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a7400c6f-0580-4260-b14b-3fa63561ef9c") + ) + (wire + (pts + (xy 134.62 157.48) (xy 124.46 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a7d0603d-687b-44f1-8616-0c3d6c1581e1") + ) + (wire + (pts + (xy 78.74 157.48) (xy 88.9 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a7f26419-fa70-4364-8905-d6297abd78e3") + ) + (wire + (pts + (xy 139.7 43.18) (xy 165.1 43.18) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a828366a-9dd4-4ac5-be02-5bce15faa59e") + ) + (wire + (pts + (xy 226.06 76.2) (xy 246.38 76.2) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a8cb47bf-139c-4a87-9299-00c74cb26e94") + ) + (wire + (pts + (xy 63.5 95.25) (xy 71.12 95.25) + ) + (stroke + (width 0) + (type default) + ) + (uuid "a972e898-9ae3-44a5-9f79-28b1e190a85e") + ) + (wire + (pts + (xy 63.5 64.77) (xy 71.12 64.77) + ) + (stroke + (width 0) + (type default) + ) + (uuid "aa497334-00e7-4091-ae28-5a2a27b5c737") + ) + (wire + (pts + (xy 90.17 134.62) (xy 101.6 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "aa8ddb02-cd01-4095-aef0-4d489aab315e") + ) + (wire + (pts + (xy 167.64 78.74) (xy 181.61 78.74) + ) + (stroke + (width 0) + (type default) + ) + (uuid "aafc1f6e-d6d6-4b33-b114-a1e4134bb50d") + ) + (wire + (pts + (xy 179.07 149.86) (xy 179.07 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "b03a2042-51bc-47df-ab4d-b2fef6a12321") + ) + (wire + (pts + (xy 92.71 95.25) (xy 78.74 95.25) + ) + (stroke + (width 0) + (type default) + ) + (uuid "b091e2c1-31b6-476a-9dbf-58c189d2918f") + ) + (wire + (pts + (xy 88.9 53.34) (xy 88.9 64.77) + ) + (stroke + (width 0) + (type default) + ) + (uuid "b1d803a4-3133-42ca-92df-1fd6bb55ac8f") + ) + (wire + (pts + (xy 205.74 123.19) (xy 238.76 123.19) + ) + (stroke + (width 0) + (type default) + ) + (uuid "b953dc1f-c9e6-4afd-a2a2-9e7406eb807c") + ) + (wire + (pts + (xy 109.22 146.05) (xy 116.84 146.05) + ) + (stroke + (width 0) + (type default) + ) + (uuid "c2e54d2b-f21f-4d26-8e6d-090be3824b4f") + ) + (wire + (pts + (xy 63.5 55.88) (xy 68.58 55.88) + ) + (stroke + (width 0) + (type default) + ) + (uuid "c3b351dd-0d39-491a-80fb-86584d8342d8") + ) + (wire + (pts + (xy 88.9 134.62) (xy 88.9 149.86) + ) + (stroke + (width 0) + (type default) + ) + (uuid "c5ebd9fc-30b3-4719-a4a2-ea025e9344ed") + ) + (wire + (pts + (xy 88.9 134.62) (xy 90.17 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "c80ff36c-f0cc-489c-adc7-fb5fb88e5f3c") + ) + (wire + (pts + (xy 63.5 80.01) (xy 71.12 80.01) + ) + (stroke + (width 0) + (type default) + ) + (uuid "ccf6024a-46c5-4a39-877b-62ab34ec0e49") + ) + (wire + (pts + (xy 156.21 134.62) (xy 146.05 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "cf0c3e53-d37b-47f9-9159-88a3d2ddfed3") + ) + (wire + (pts + (xy 146.05 45.72) (xy 146.05 125.73) + ) + (stroke + (width 0) + (type default) + ) + (uuid "cf3d06db-29aa-471a-b209-4b0de90a1b6b") + ) + (wire + (pts + (xy 92.71 58.42) (xy 92.71 95.25) + ) + (stroke + (width 0) + (type default) + ) + (uuid "d07120f3-56be-4ed2-9dde-02613eda0b7b") + ) + (wire + (pts + (xy 63.5 35.56) (xy 71.12 35.56) + ) + (stroke + (width 0) + (type default) + ) + (uuid "d10b37fc-e97e-4743-97b7-cb6647b169e2") + ) + (wire + (pts + (xy 167.64 53.34) (xy 167.64 78.74) + ) + (stroke + (width 0) + (type default) + ) + (uuid "dc05587b-dd10-41aa-9aa4-6cea171da7c4") + ) + (wire + (pts + (xy 101.6 157.48) (xy 101.6 153.67) + ) + (stroke + (width 0) + (type default) + ) + (uuid "dee4823a-52c4-4062-a285-62dc4c6d89c0") + ) + (wire + (pts + (xy 195.58 140.97) (xy 198.12 140.97) + ) + (stroke + (width 0) + (type default) + ) + (uuid "e0a7be85-1ab9-4143-aabd-09a68e6651d4") + ) + (wire + (pts + (xy 195.58 138.43) (xy 195.58 140.97) + ) + (stroke + (width 0) + (type default) + ) + (uuid "e2c2478d-3792-4799-ad1f-f654123dc5c8") + ) + (wire + (pts + (xy 124.46 153.67) (xy 124.46 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "e671db11-bfd8-47cc-a523-fe15f01a9a03") + ) + (wire + (pts + (xy 67.31 134.62) (xy 78.74 134.62) + ) + (stroke + (width 0) + (type default) + ) + (uuid "e6bb7a60-ce41-4eb1-af02-eda21bfa2683") + ) + (wire + (pts + (xy 101.6 73.66) (xy 208.28 73.66) + ) + (stroke + (width 0) + (type default) + ) + (uuid "e6bccd64-0b7b-415b-9ac2-8380628d933c") + ) + (wire + (pts + (xy 176.53 83.82) (xy 208.28 83.82) + ) + (stroke + (width 0) + (type default) + ) + (uuid "eeb2c106-23b9-47d9-9dbf-e2e8a847fab8") + ) + (wire + (pts + (xy 111.76 157.48) (xy 101.6 157.48) + ) + (stroke + (width 0) + (type default) + ) + (uuid "f1f0e90e-3ba9-4e4f-bf0b-b9a229deda6d") + ) + (wire + (pts + (xy 218.44 96.52) (xy 218.44 99.06) + ) + (stroke + (width 0) + (type default) + ) + (uuid "f27cc0a5-5680-45f8-8c1b-f150206d3068") + ) + (wire + (pts + (xy 134.62 134.62) (xy 134.62 149.86) + ) + (stroke + (width 0) + (type default) + ) + (uuid "f2f33e90-c05c-4df7-be92-7e09975c6129") + ) + (wire + (pts + (xy 226.06 83.82) (xy 246.38 83.82) + ) + (stroke + (width 0) + (type default) + ) + (uuid "f8c1274f-6d28-4a49-8f29-8f7028b3f356") + ) + (wire + (pts + (xy 63.5 40.64) (xy 68.58 40.64) + ) + (stroke + (width 0) + (type default) + ) + (uuid "faa4b204-1d0d-46d8-8b30-8c9aaf9bd0f9") + ) + (wire + (pts + (xy 165.1 58.42) (xy 165.1 83.82) + ) + (stroke + (width 0) + (type default) + ) + (uuid "fca56f26-8cfe-4331-ba55-44ba02e5182f") + ) + (label "TX3" + (at 233.68 123.19 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "12e80b7b-e0cd-4027-aa7c-f2231cb8cd8a") + ) + (label "TX0" + (at 233.68 115.57 0) + (fields_autoplaced yes) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "181b1db8-4ee5-4c72-aa95-78ae6060e7fb") + (property "TX0" "" + (at 233.68 116.84 0) + (effects + (font + (size 1.27 1.27) + (italic yes) + ) + (justify left) + ) + ) + ) + (label "RX1" + (at 241.3 76.2 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "3c8dd59d-02b0-4df9-82cf-06522775a670") + ) + (label "TX4" + (at 233.68 125.73 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "60731bd4-0079-48a7-bfab-30ee7f04ee80") + ) + (label "TX1" + (at 233.68 118.11 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "7c52be90-9114-4d17-9d39-81283cec9af0") + ) + (label "RX4" + (at 241.3 83.82 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "822a7071-b7cf-44dc-b6c5-eb476b25a773") + ) + (label "RX2" + (at 241.3 78.74 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "8bd7bc90-b9dd-4692-8bd3-f0667db00472") + ) + (label "RX3" + (at 241.3 81.28 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "9c02a286-893a-42d8-bf31-4469c39e3033") + ) + (label "TX2" + (at 233.68 120.65 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "c81ced09-563a-438d-b5a8-404439d494ac") + ) + (label "RX0" + (at 241.3 73.66 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left bottom) + ) + (uuid "f705d338-f531-45d5-9d4a-7169c5cc6c44") + ) + (symbol + (lib_id "Device:R") + (at 74.93 50.8 90) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "035d2792-c7f4-4ac1-99f1-2c7f8fe51851") + (property "Reference" "R2" + (at 74.93 44.45 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "360" + (at 74.93 46.99 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 74.93 52.578 90) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 74.93 50.8 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Resistor" + (at 74.93 50.8 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "2" + (uuid "30691e72-7922-47b7-a187-027016f0d599") + ) + (pin "1" + (uuid "8a75fba7-5fb8-42d0-a62c-b97e0b6f316f") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "R2") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "LED:WS2812B") + (at 78.74 146.05 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "0c30b024-628e-47ff-979f-12ce2e6c79b3") + (property "Reference" "D1" + (at 90.17 139.6298 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "WS2812B" + (at 90.17 142.1698 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "LED_SMD:LED_WS2812_PLCC6_5.0x5.0mm_P1.6mm" + (at 80.01 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Datasheet" "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" + (at 81.28 155.575 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Description" "RGB LED with integrated controller" + (at 78.74 146.05 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "4" + (uuid "23f67f2f-13de-4fa8-9515-b272370200f3") + ) + (pin "1" + (uuid "23322e45-8b40-4821-b29a-45f51f825595") + ) + (pin "3" + (uuid "d40992db-047a-4564-8c4a-cfa0456a4974") + ) + (pin "2" + (uuid "6fff2aed-0114-450c-8c19-d60682319736") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "D1") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:GND") + (at 68.58 104.14 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "0c5c281d-a200-46c6-a8fa-121519d9658b") + (property "Reference" "#PWR05" + (at 68.58 110.49 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "GND" + (at 68.58 109.22 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 68.58 104.14 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 68.58 104.14 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"GND\" , ground" + (at 68.58 104.14 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "16fd7b6f-da48-49cf-9ce6-8338e606855b") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR05") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "74xx:74HC4051") + (at 215.9 81.28 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "12ec3554-d23c-493d-bf12-faefd2fd1cb4") + (property "Reference" "U10" + (at 218.0433 66.04 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "74HC4051" + (at 218.0433 68.58 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "" + (at 215.9 91.44 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "http://www.ti.com/lit/ds/symlink/cd74hc4051.pdf" + (at 215.9 91.44 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "8-channel analog multiplexer/demultiplexer, DIP-16/SOIC-16/TSSOP-16" + (at 215.9 81.28 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "3" + (uuid "7c7cc8db-58bd-4f06-b946-27b3297d1dc4") + ) + (pin "11" + (uuid "0c1a86f5-61b4-4255-a8cb-1073610c5f39") + ) + (pin "10" + (uuid "8a97bd4e-6f1e-4574-99ca-1a6cfb9c3e37") + ) + (pin "9" + (uuid "803b538c-1c3e-4325-9f64-3cc182a03231") + ) + (pin "6" + (uuid "91b4ab66-c2a2-40d2-858b-969d209cd280") + ) + (pin "16" + (uuid "04f0f182-3565-4d3e-895a-07aab298f6e7") + ) + (pin "8" + (uuid "278288c6-b974-4aa1-8f3a-afadcca649ab") + ) + (pin "7" + (uuid "bb917545-e941-4df2-8627-148a5bb7f797") + ) + (pin "13" + (uuid "1b2d6dea-ad89-447d-a5a2-e8d613a496e8") + ) + (pin "14" + (uuid "b2af7590-e58d-428a-a852-2969d8530bd9") + ) + (pin "15" + (uuid "9bbfefa8-561d-46e5-ac3b-16de2eb0256f") + ) + (pin "12" + (uuid "26255be3-a05c-44c3-b1a5-29de96f29589") + ) + (pin "1" + (uuid "aae844ea-12b4-4149-98ca-d73d9a4e7b99") + ) + (pin "5" + (uuid "17850055-13fa-4e3b-a7d9-a853729ab842") + ) + (pin "2" + (uuid "b40cd49f-7c4f-40a1-847b-69883519d385") + ) + (pin "4" + (uuid "a0893078-07ad-44a9-b31f-1397c289145a") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U10") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:GND") + (at 90.17 158.75 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "1472f27e-a9b1-4b5f-95e1-486264ec958a") + (property "Reference" "#PWR06" + (at 90.17 165.1 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "GND" + (at 90.17 163.83 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 90.17 158.75 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 90.17 158.75 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"GND\" , ground" + (at 90.17 158.75 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "77e7928a-1e43-4710-b42e-e40253c3987a") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR06") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "ESP32-S3-ZERO:ESP32-S3-ZERO") + (at 121.92 48.26 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "1537ba16-7549-43af-9768-d55e391edb95") + (property "Reference" "U1" + (at 121.92 25.4 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "ESP32-S3-ZERO" + (at 121.92 27.94 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "ESP32-S3-ZERO:MODULE_ESP32-S3-ZERO" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "MF" "Waveshare Electronics" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "MAXIMUM_PACKAGE_HEIGHT" "4.35mm" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "CREATOR" "ANA" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Price" "None" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Package" "None" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Check_prices" "https://www.snapeda.com/parts/ESP32-S3-Zero/Waveshare+Electronics/view-part/?ref=eda" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "STANDARD" "Manufacturer Recommendations" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "PARTREV" "NA" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "VERIFIER" "RODRIGO" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "SnapEDA_Link" "https://www.snapeda.com/parts/ESP32-S3-Zero/Waveshare+Electronics/view-part/?ref=snap" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "MP" "ESP32-S3-Zero" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Description_1" "ESP32-S3 Mini Development Board, Based on ESP32-S3FH4R2 Dual-Core Processor, 240MHz Running Frequency, 2.4GHz Wi-Fi & Bluetooth 5" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "Availability" "Not in stock" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (property "MANUFACTURER" "Waveshare" + (at 121.92 48.26 0) + (effects + (font + (size 1.27 1.27) + ) + (justify bottom) + (hide yes) + ) + ) + (pin "9" + (uuid "82da888a-cee5-4aa9-bd63-a16bf401c59d") + ) + (pin "10" + (uuid "b1ac9d25-7780-464d-8926-f3232dc04931") + ) + (pin "11" + (uuid "b0ef804a-40f2-4961-b473-b8da32a58ba6") + ) + (pin "12" + (uuid "0f203cc5-5615-4d78-88dc-f5132b503138") + ) + (pin "13" + (uuid "5deca729-f6f1-474f-bf1f-05ed677e9290") + ) + (pin "GND" + (uuid "89aed420-fe88-4e3c-bd16-aca3c4129654") + ) + (pin "3V3" + (uuid "306f8b17-b5cb-485b-a114-b4eb48ea8dda") + ) + (pin "6" + (uuid "79cb23fc-e672-48c2-acde-2e6676cc5022") + ) + (pin "7" + (uuid "7c495e3d-b46c-4862-aa4a-a794d47098bd") + ) + (pin "8" + (uuid "8b6d2dcf-14d0-49e8-9b9a-23a0b83e6753") + ) + (pin "3" + (uuid "b549c74d-f47c-40bc-a071-5bbcaf5f351f") + ) + (pin "4" + (uuid "909cad05-79a5-4e86-b086-ac4831ff8c3e") + ) + (pin "5" + (uuid "bef72c71-5548-4ae2-a0be-ca3ac0c0ad84") + ) + (pin "5V" + (uuid "94d7bfd7-ff03-40c6-96ec-81c1645cad24") + ) + (pin "RX" + (uuid "d321baec-9b45-4a28-a2a8-77be7949c732") + ) + (pin "TX" + (uuid "1d38a0ea-afe6-45ed-b0b6-644aa74f7448") + ) + (pin "1" + (uuid "9e96a2f1-1040-488b-9d50-d32b3b3bb35f") + ) + (pin "2" + (uuid "b5bd83f6-38c2-474c-9939-fb7509bbde32") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U1") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:GND") + (at 214.63 152.4 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "18c8a714-01b1-4cd1-9dff-fe4952003b6c") + (property "Reference" "#PWR010" + (at 214.63 158.75 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "GND" + (at 214.63 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 214.63 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 214.63 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"GND\" , ground" + (at 214.63 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "c0d1fda7-18fd-4e2a-9a3f-f481f721753d") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR010") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Isolator:PC817") + (at 55.88 38.1 0) + (mirror y) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "1ba58d46-dd87-4db5-8bc9-a021a9bf679c") + (property "Reference" "U9" + (at 55.88 29.21 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "PC817" + (at 55.88 31.75 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "Package_DIP:DIP-4_W7.62mm" + (at 60.96 43.18 0) + (effects + (font + (size 1.27 1.27) + (italic yes) + ) + (justify left) + (hide yes) + ) + ) + (property "Datasheet" "http://www.soselectronic.cz/a_info/resource/d/pc817.pdf" + (at 55.88 38.1 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + (hide yes) + ) + ) + (property "Description" "DC Optocoupler, Vce 35V, CTR 50-300%, DIP-4" + (at 55.88 38.1 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "15a96f67-fa42-4673-9486-be559cefadab") + ) + (pin "2" + (uuid "0e96116c-b719-49e4-9ed1-f1cee2d6a603") + ) + (pin "4" + (uuid "a0edcaac-f000-48cc-8f14-4a10938df8c8") + ) + (pin "3" + (uuid "f9a59050-94d8-4c0c-ba76-cea41c67bb06") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U9") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:R") + (at 74.93 35.56 90) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "22e76ade-555e-4e54-a023-9d551074044d") + (property "Reference" "R1" + (at 74.93 29.21 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "360" + (at 74.93 31.75 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "Resistor_SMD:R_0805_2012Metric" + (at 74.93 37.338 90) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 74.93 35.56 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Resistor" + (at 74.93 35.56 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "d47907b5-2600-4759-b40a-d6abe018cc66") + ) + (pin "2" + (uuid "8963b22c-62a8-41d2-b327-7859e30da916") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "R1") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "74xx:74HC4051") + (at 195.58 123.19 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "2cebe236-e0d6-4fe3-96b5-fb40ab2a3c0b") + (property "Reference" "U11" + (at 197.7233 107.95 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "74HC4051" + (at 197.7233 110.49 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "" + (at 195.58 133.35 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "http://www.ti.com/lit/ds/symlink/cd74hc4051.pdf" + (at 195.58 133.35 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "8-channel analog multiplexer/demultiplexer, DIP-16/SOIC-16/TSSOP-16" + (at 195.58 123.19 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "9" + (uuid "97879531-c908-44db-9cb1-5b93c3f56cda") + ) + (pin "6" + (uuid "a40b4fcc-8267-4bd7-9f36-b95161340e2f") + ) + (pin "16" + (uuid "8bd0c952-0ec2-4192-ae1f-a7de536598ab") + ) + (pin "8" + (uuid "186a76c4-e913-4a1a-a062-cfcc31ba8487") + ) + (pin "7" + (uuid "83e72702-f4ea-4278-b801-daa619e6273d") + ) + (pin "13" + (uuid "a5ba34ef-7df5-4e2d-8661-37389fdae901") + ) + (pin "14" + (uuid "672cf7ae-198c-48b4-8222-5bafe3460e4f") + ) + (pin "15" + (uuid "c990171c-4a0f-4187-8441-21421408cd18") + ) + (pin "12" + (uuid "15d3fca7-0f0b-410f-b3f9-51b2948774f9") + ) + (pin "1" + (uuid "c456b7d4-d983-4730-95a4-abaf232ae083") + ) + (pin "5" + (uuid "76fdbe44-db39-4f35-b029-3314a0072d8c") + ) + (pin "2" + (uuid "9f0ce87d-28d0-414c-b582-61a378ed0945") + ) + (pin "4" + (uuid "83d0e13d-5cda-464b-9d00-f95647a9323b") + ) + (pin "3" + (uuid "8958b306-6c98-499e-870c-fc6b3c6cf3a6") + ) + (pin "11" + (uuid "1318fa45-2a14-4998-a322-30f0c85ebdc8") + ) + (pin "10" + (uuid "683fd50c-0a6f-4c0e-82e1-10caa3bb234f") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U11") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 88.9 153.67 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "3d7ff0f2-a668-4da2-af05-dacf33a1236f") + (property "Reference" "C2" + (at 92.71 152.3999 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 92.71 154.9399 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 89.8652 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 88.9 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 88.9 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "27f3ab45-1e6a-465a-970d-1ea8c6a2f2b7") + ) + (pin "2" + (uuid "2a003a5a-4f37-4611-926c-20ad8583aa5c") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C2") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:R") + (at 74.93 64.77 90) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "439bcb69-cb2a-4eb7-a009-3c8011e8c7b0") + (property "Reference" "R3" + (at 74.93 58.42 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "360" + (at 74.93 60.96 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 74.93 66.548 90) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 74.93 64.77 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Resistor" + (at 74.93 64.77 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "41d3a35e-96a1-4336-bca7-1aff74fe67ba") + ) + (pin "2" + (uuid "94560216-37d8-4747-9bea-c3ce1462c013") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "R3") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Isolator:PC817") + (at 55.88 53.34 0) + (mirror y) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "51a1b497-f10f-400f-adc8-6427425ade18") + (property "Reference" "U5" + (at 55.88 44.45 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "PC817" + (at 55.88 46.99 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "Package_DIP:DIP-4_W7.62mm" + (at 60.96 58.42 0) + (effects + (font + (size 1.27 1.27) + (italic yes) + ) + (justify left) + (hide yes) + ) + ) + (property "Datasheet" "http://www.soselectronic.cz/a_info/resource/d/pc817.pdf" + (at 55.88 53.34 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + (hide yes) + ) + ) + (property "Description" "DC Optocoupler, Vce 35V, CTR 50-300%, DIP-4" + (at 55.88 53.34 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "41cb6bde-2bd4-4cec-aed1-1c35b09fe551") + ) + (pin "2" + (uuid "34a4d1ae-8f5c-4513-9d36-b7c7704c4f5d") + ) + (pin "4" + (uuid "950b36e0-d78a-4d8a-ae73-884d1ab8696d") + ) + (pin "3" + (uuid "55c86c54-b179-49db-9411-1b681af4c40e") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U5") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 67.31 153.67 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "6661a57e-badc-4337-ac78-121bd37a5185") + (property "Reference" "C1" + (at 71.12 152.3999 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 71.12 154.9399 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 68.2752 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 67.31 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 67.31 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "f3325599-ca59-4135-b8f7-15fe6f1caf10") + ) + (pin "2" + (uuid "4428e479-93f9-41ff-b8c9-6a09eba8fae0") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C1") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "LED:WS2812B") + (at 124.46 146.05 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "66b22f75-f8f9-49d2-a1bf-47da28fd275a") + (property "Reference" "D3" + (at 135.89 139.6298 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "WS2812B" + (at 135.89 142.1698 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "LED_SMD:LED_WS2812_PLCC6_5.0x5.0mm_P1.6mm" + (at 125.73 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Datasheet" "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" + (at 127 155.575 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Description" "RGB LED with integrated controller" + (at 124.46 146.05 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "4" + (uuid "1370e6d0-c1f4-40a7-8b21-5f3f6e1916c7") + ) + (pin "1" + (uuid "0a95ca59-1830-403e-902d-2cc529513076") + ) + (pin "3" + (uuid "3e520e21-1102-48e4-8687-20846a359777") + ) + (pin "2" + (uuid "5b252f9e-0efa-4961-8263-9449cfd3572d") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "D3") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 111.76 153.67 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "82cc65bc-f013-444a-88c9-0901427e2e1e") + (property "Reference" "C3" + (at 115.57 152.3999 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 115.57 154.9399 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 112.7252 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 111.76 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 111.76 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "c19b4305-76f5-4cb3-ac40-c62c54868881") + ) + (pin "2" + (uuid "a2ba151b-3f98-40de-8c63-5a8233239dc4") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C3") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Isolator:PC817") + (at 55.88 82.55 0) + (mirror y) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (uuid "82d5e3f4-39c4-4679-b343-b199d87e38c8") + (property "Reference" "U7" + (at 55.88 73.66 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "PC817" + (at 55.88 76.2 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "Package_DIP:DIP-4_W7.62mm" + (at 60.96 87.63 0) + (effects + (font + (size 1.27 1.27) + (italic yes) + ) + (justify left) + (hide yes) + ) + ) + (property "Datasheet" "http://www.soselectronic.cz/a_info/resource/d/pc817.pdf" + (at 55.88 82.55 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + (hide yes) + ) + ) + (property "Description" "DC Optocoupler, Vce 35V, CTR 50-300%, DIP-4" + (at 55.88 82.55 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "fd0d644d-07c1-4709-be83-418419f9452f") + ) + (pin "2" + (uuid "05877809-ad85-4aa0-aaee-30990377863d") + ) + (pin "4" + (uuid "09492129-8e51-49a2-9582-c42f00c27de3") + ) + (pin "3" + (uuid "2ff50592-388c-4fc9-88d3-675169903a00") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U7") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "LED:WS2812B") + (at 101.6 146.05 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "83c62aa2-7e56-4c76-9d56-461cbc825c19") + (property "Reference" "D2" + (at 113.03 139.6298 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "WS2812B" + (at 113.03 142.1698 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "LED_SMD:LED_WS2812_PLCC6_5.0x5.0mm_P1.6mm" + (at 102.87 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Datasheet" "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" + (at 104.14 155.575 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Description" "RGB LED with integrated controller" + (at 101.6 146.05 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "4" + (uuid "cd836e38-e0ad-41b7-a69f-a805adbc8980") + ) + (pin "1" + (uuid "3ec74f0f-b1e3-4182-af6a-764df60b48db") + ) + (pin "3" + (uuid "241d83da-5a1a-45e8-8b0f-6bf5d4a15dea") + ) + (pin "2" + (uuid "69757822-1479-40a0-a3f6-192bf7b1d6b0") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "D2") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Isolator:PC817") + (at 55.88 67.31 0) + (mirror y) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (uuid "96506781-a5c7-45ad-bad1-1ebd983c9394") + (property "Reference" "U6" + (at 55.88 58.42 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "PC817" + (at 55.88 60.96 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "Package_DIP:DIP-4_W7.62mm" + (at 60.96 72.39 0) + (effects + (font + (size 1.27 1.27) + (italic yes) + ) + (justify left) + (hide yes) + ) + ) + (property "Datasheet" "http://www.soselectronic.cz/a_info/resource/d/pc817.pdf" + (at 55.88 67.31 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + (hide yes) + ) + ) + (property "Description" "DC Optocoupler, Vce 35V, CTR 50-300%, DIP-4" + (at 55.88 67.31 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "07406e6d-5b61-46f2-85b6-c20ac4d80be0") + ) + (pin "2" + (uuid "15a66c86-3ff8-418e-98c8-776e15e12681") + ) + (pin "4" + (uuid "0f8e34b8-5b9c-40bc-8276-dbe3b8747148") + ) + (pin "3" + (uuid "a87a7c61-3ce3-4439-9e60-136b373471e1") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U6") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 224.79 148.59 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "a4674c0d-91b2-4c26-8c28-c30f53b1feb3") + (property "Reference" "C8" + (at 228.6 147.3199 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 228.6 149.8599 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 225.7552 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 224.79 148.59 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 224.79 148.59 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "63710188-22df-424b-a5a1-2e89153ad56c") + ) + (pin "2" + (uuid "c951094d-4161-47cf-bcb2-3fd420ab3516") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C8") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 214.63 148.59 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "ab55886d-e1e8-41f0-9285-699a530c0cce") + (property "Reference" "C7" + (at 218.44 147.3199 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 218.44 149.8599 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 215.5952 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 214.63 148.59 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 214.63 148.59 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "88685949-e7bc-42e6-b319-ee3f8641b9e5") + ) + (pin "2" + (uuid "014088d1-3b63-4eaf-8491-70592107e692") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C7") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:+3.3V") + (at 215.9 68.58 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "b02ff2f1-26e1-4ddd-b20d-74f213a8dde7") + (property "Reference" "#PWR01" + (at 215.9 72.39 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "+3.3V" + (at 215.9 63.5 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 215.9 68.58 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 215.9 68.58 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"+3.3V\"" + (at 215.9 68.58 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "f87861aa-f40f-480c-b3bc-37edbf271211") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR01") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 134.62 153.67 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "b9fa0efb-a1e0-4dca-832f-44450845148f") + (property "Reference" "C4" + (at 138.43 152.3999 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 138.43 154.9399 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 135.5852 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 134.62 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 134.62 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "ad99be4a-4ac2-43fd-bbc9-863d0959e163") + ) + (pin "2" + (uuid "d2f0f265-48f7-48de-8362-c7a7b395e49a") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C4") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:+3.3V") + (at 90.17 134.62 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "bcb307fb-1dc2-4ca4-af19-998be765c3a7") + (property "Reference" "#PWR07" + (at 90.17 138.43 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "+3.3V" + (at 90.17 129.54 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 90.17 134.62 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 90.17 134.62 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"+3.3V\"" + (at 90.17 134.62 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "970ca2fd-3116-4c8d-8bff-b6c6efdcc97e") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR07") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:GND") + (at 224.79 152.4 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "c1878412-9a15-42ee-9f54-ec86c61ffded") + (property "Reference" "#PWR011" + (at 224.79 158.75 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "GND" + (at 224.79 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 224.79 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 224.79 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"GND\" , ground" + (at 224.79 152.4 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "c9e76cc5-dddc-4e49-9a02-a8cd6112cd12") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR011") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:+3.3V") + (at 195.58 110.49 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "c30d9d0f-7734-4e46-8133-7c80be5a5ab3") + (property "Reference" "#PWR02" + (at 195.58 114.3 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "+3.3V" + (at 195.58 105.41 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 195.58 110.49 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 195.58 110.49 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"+3.3V\"" + (at 195.58 110.49 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "cc09a488-61f8-4db5-b530-04b4151e887f") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR02") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:R") + (at 74.93 80.01 90) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "c7096995-2368-48e2-987b-6e223c5eca4e") + (property "Reference" "R4" + (at 74.93 73.66 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "360" + (at 74.93 76.2 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 74.93 81.788 90) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 74.93 80.01 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Resistor" + (at 74.93 80.01 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "be31fa77-15e3-4cac-9af0-c44977215d89") + ) + (pin "2" + (uuid "aa6385c6-66b8-4efa-9712-db91571e9678") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "R4") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "LED:WS2812B") + (at 168.91 146.05 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "cbf7f2f1-b58a-466f-a5d0-48443e23fe2b") + (property "Reference" "D5" + (at 180.34 139.6298 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "WS2812B" + (at 180.34 142.1698 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "LED_SMD:LED_WS2812_PLCC6_5.0x5.0mm_P1.6mm" + (at 170.18 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Datasheet" "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" + (at 171.45 155.575 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Description" "RGB LED with integrated controller" + (at 168.91 146.05 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "4" + (uuid "63b022ab-69fc-4698-9414-98814fb48b00") + ) + (pin "1" + (uuid "02c23a61-9ff4-42ab-b8a5-eddb56f32adf") + ) + (pin "3" + (uuid "082cc5c8-df3b-44ae-a6d0-36ef8f67a07b") + ) + (pin "2" + (uuid "0305b968-ee24-48d9-8dd3-f59c178b3ece") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "D5") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 179.07 153.67 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "d0cee1ca-da50-4745-a4cc-f74947b93685") + (property "Reference" "C6" + (at 182.88 152.3999 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 182.88 154.9399 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 180.0352 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 179.07 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 179.07 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "431b9153-9ed2-4241-9bde-6b3d8600ddfc") + ) + (pin "2" + (uuid "5eea22c4-fe49-48ec-a34b-7cc07f0f3f40") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C6") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Sensor_Energy:INA226") + (at 175.26 40.64 0) + (mirror y) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "d4084967-60dc-4ffb-bd68-2ac8ea74f18b") + (property "Reference" "U2" + (at 178.3781 25.4 0) + (effects + (font + (size 1.27 1.27) + ) + (justify right) + ) + ) + (property "Value" "INA226" + (at 178.3781 27.94 0) + (effects + (font + (size 1.27 1.27) + ) + (justify right) + ) + ) + (property "Footprint" "Package_SO:VSSOP-10_3x3mm_P0.5mm" + (at 154.94 52.07 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "http://www.ti.com/lit/ds/symlink/ina226.pdf" + (at 166.37 43.18 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "High-Side or Low-Side Measurement, Bi-Directional Current and Power Monitor (0-36V) with I2C Compatible Interface, VSSOP-10" + (at 175.26 40.64 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "10" + (uuid "3c4ceb74-732e-4154-8a6d-e0673f7add8c") + ) + (pin "8" + (uuid "780e1f1e-0989-43d1-bbff-2e5ee59289eb") + ) + (pin "9" + (uuid "9434de0e-d042-4da5-8775-68c6856c24bb") + ) + (pin "6" + (uuid "a012c649-b5ef-4ca5-bfb2-055f22367bcc") + ) + (pin "7" + (uuid "ba6d7911-d146-4a19-a5bf-7bc11e0753ea") + ) + (pin "1" + (uuid "ca61a598-959c-43ff-9ffd-c0228e500b3c") + ) + (pin "2" + (uuid "88637062-9288-4d67-b973-bae73a66a0ce") + ) + (pin "4" + (uuid "357cd321-7141-46ac-848e-e53ef4c51213") + ) + (pin "5" + (uuid "b186eb3c-25e6-4ee6-84c0-07e55238de13") + ) + (pin "3" + (uuid "55c5bcbe-4edf-48d1-8947-ac456f7da907") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U2") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:R") + (at 74.93 95.25 90) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "d7798c92-902f-49cb-a987-b056994f7cf8") + (property "Reference" "R5" + (at 74.93 88.9 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "360" + (at 74.93 91.44 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 74.93 97.028 90) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 74.93 95.25 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Resistor" + (at 74.93 95.25 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "2" + (uuid "3f6106ca-f167-48e0-b6eb-7581e8c8165c") + ) + (pin "1" + (uuid "3622970e-84a1-4371-9e58-75a21f11196c") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "R5") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:+3.3V") + (at 224.79 144.78 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "db1448f8-99e9-402a-83dc-a48052d95ae7") + (property "Reference" "#PWR09" + (at 224.79 148.59 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "+3.3V" + (at 224.79 139.7 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 224.79 144.78 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 224.79 144.78 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"+3.3V\"" + (at 224.79 144.78 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "d3b7c2fc-39ba-4e3c-aedf-3582a667a94a") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR09") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:GND") + (at 218.44 99.06 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "e57373b7-87da-4d16-afa0-49538aa166b6") + (property "Reference" "#PWR03" + (at 218.44 105.41 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "GND" + (at 218.44 104.14 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 218.44 99.06 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 218.44 99.06 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"GND\" , ground" + (at 218.44 99.06 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "a3a1eb6c-5b7a-464f-a7c0-5f4019aab10b") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR03") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:+3.3V") + (at 214.63 144.78 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "e9f2f369-226f-4ae5-842e-c67f3239ec9b") + (property "Reference" "#PWR08" + (at 214.63 148.59 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "+3.3V" + (at 214.63 139.7 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 214.63 144.78 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 214.63 144.78 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"+3.3V\"" + (at 214.63 144.78 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "e09fde2d-f06c-477b-9e78-fd93e13ba64e") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR08") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "power:GND") + (at 198.12 142.24 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "f17afe57-1af6-4d7f-a98d-eb2ca315db5f") + (property "Reference" "#PWR04" + (at 198.12 148.59 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Value" "GND" + (at 198.12 147.32 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 198.12 142.24 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "" + (at 198.12 142.24 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Power symbol creates a global label with name \"GND\" , ground" + (at 198.12 142.24 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "a88590e7-2d45-4b0b-9e38-bee2e83f251f") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "#PWR04") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:R") + (at 54.61 146.05 90) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "f94ca153-e77d-4e38-96e9-ac50ade84d10") + (property "Reference" "R6" + (at 54.61 139.7 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "220" + (at 54.61 142.24 90) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "" + (at 54.61 147.828 90) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 54.61 146.05 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Resistor" + (at 54.61 146.05 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "2" + (uuid "64b1030e-10ce-441c-8b30-44aacf508412") + ) + (pin "1" + (uuid "bae6d87e-5408-4ab3-a1b4-d03c0d61102c") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "R6") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Device:C") + (at 156.21 153.67 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "f97562bc-968e-47ca-a8ee-665de1aa9c58") + (property "Reference" "C5" + (at 160.02 152.3999 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Value" "100n" + (at 160.02 154.9399 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + ) + ) + (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" + (at 157.1752 157.48 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Datasheet" "~" + (at 156.21 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (property "Description" "Unpolarized capacitor" + (at 156.21 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "76b41f01-0f93-4f27-8e45-8947c0b4dc10") + ) + (pin "2" + (uuid "9444191f-f532-4873-920d-7db048c3d899") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "C5") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "Isolator:PC817") + (at 55.88 97.79 0) + (mirror y) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "fb361f5a-f4ce-4f64-8e40-97487ddcf270") + (property "Reference" "U8" + (at 55.88 88.9 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "PC817" + (at 55.88 91.44 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "Package_DIP:DIP-4_W7.62mm" + (at 60.96 102.87 0) + (effects + (font + (size 1.27 1.27) + (italic yes) + ) + (justify left) + (hide yes) + ) + ) + (property "Datasheet" "http://www.soselectronic.cz/a_info/resource/d/pc817.pdf" + (at 55.88 97.79 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left) + (hide yes) + ) + ) + (property "Description" "DC Optocoupler, Vce 35V, CTR 50-300%, DIP-4" + (at 55.88 97.79 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "1" + (uuid "4de7fb35-ec51-4fcd-a5cf-d169e7bb3896") + ) + (pin "2" + (uuid "8357aa8a-0868-4d8b-a6d3-35ab6443566f") + ) + (pin "4" + (uuid "cfebf88f-3f08-486f-9bbc-40236943d0c1") + ) + (pin "3" + (uuid "2c40128b-21d7-425a-b599-0f491432bc0a") + ) + (instances + (project "" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "U8") + (unit 1) + ) + ) + ) + ) + (symbol + (lib_id "LED:WS2812B") + (at 146.05 146.05 0) + (unit 1) + (exclude_from_sim no) + (in_bom yes) + (on_board yes) + (dnp no) + (fields_autoplaced yes) + (uuid "ff578ae1-0112-4093-afb7-94c5a520babe") + (property "Reference" "D4" + (at 157.48 139.6298 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Value" "WS2812B" + (at 157.48 142.1698 0) + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Footprint" "LED_SMD:LED_WS2812_PLCC6_5.0x5.0mm_P1.6mm" + (at 147.32 153.67 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Datasheet" "https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf" + (at 148.59 155.575 0) + (effects + (font + (size 1.27 1.27) + ) + (justify left top) + (hide yes) + ) + ) + (property "Description" "RGB LED with integrated controller" + (at 146.05 146.05 0) + (effects + (font + (size 1.27 1.27) + ) + (hide yes) + ) + ) + (pin "4" + (uuid "8a40d530-fbcf-4d83-b96f-6a19e100a15b") + ) + (pin "1" + (uuid "781f9667-907d-424d-83b4-8c7563719bc0") + ) + (pin "3" + (uuid "6da46d5d-0db2-4a3d-989d-a58f29cb8b06") + ) + (pin "2" + (uuid "97ae6cf2-acf6-41e8-9663-b21ae24b3eb4") + ) + (instances + (project "watch-watchkicad_pro" + (path "/e717a214-aafd-4c38-81fe-477ca8e64d07" + (reference "D4") + (unit 1) + ) + ) + ) + ) + (sheet_instances + (path "/" + (page "1") + ) + ) + (embedded_fonts no) +) diff --git a/kicad/watch-watchkicad_pro/watch-watchkicad_pro.pdf b/kicad/watch-watchkicad_pro/watch-watchkicad_pro.pdf new file mode 100644 index 0000000000000000000000000000000000000000..92eb3c38b1c431b11f4f2c5f048414f8dfd754c5 GIT binary patch literal 182374 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 N~LcD+CksIpvRh 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 vR*a7?FA+y|LBZo<{A~X265sGtaZK9_maEBP @@ -50,6 +56,8 @@ static uint8_t s_address = CONFIG_WATCH_INA226_ADDR; static ina226_reading_t s_last_reading; #endif +// Ініціалізує контролер INA226: налаштовує I2C, записує конфігурацію та +// калібрувальні значення, а також зберігає початковий стан вимірювань. esp_err_t ina226_monitor_init(void) { #if !CONFIG_WATCH_INA226_ENABLED @@ -103,6 +111,7 @@ esp_err_t ina226_monitor_init(void) #endif } +// Дозволяє швидко перевірити, чи був модуль INA226 успішно ініціалізований. bool ina226_monitor_ready(void) { #if CONFIG_WATCH_INA226_ENABLED @@ -112,12 +121,14 @@ bool ina226_monitor_ready(void) #endif } +// INA226 вимірює єдиний канал живлення, тому повертаємо 1. size_t ina226_monitor_channel_count(void) { return 1; } #if CONFIG_WATCH_INA226_ENABLED +// Зчитує 16-бітний регістр INA226, використовуючи транзакцію write-then-read. static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value) { uint8_t value[2]; @@ -132,6 +143,7 @@ static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value) } #endif +// Виконує одне вимірювання напруги/струму через INA226 та кешує результат. esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading) { #if !CONFIG_WATCH_INA226_ENABLED @@ -162,6 +174,7 @@ esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading) #endif } +// Повертає останній виміряний набір даних без нового звернення до шини I2C. const ina226_reading_t *ina226_monitor_get_last(void) { #if CONFIG_WATCH_INA226_ENABLED diff --git a/main/ina226_monitor.h b/main/ina226_monitor.h index a7957ba..10069c0 100644 --- a/main/ina226_monitor.h +++ b/main/ina226_monitor.h @@ -1,3 +1,9 @@ +/* + * Developed by TComLab + * Version: v0.1 + * Date: 2025-12-15 + */ + #pragma once #include @@ -11,8 +17,13 @@ typedef struct { float power_mw; } ina226_reading_t; +// Ініціалізує INA226: конфігурує I2C та калібрує вимірювач. esp_err_t ina226_monitor_init(void); +// true, якщо драйвер вже ініціалізований. bool ina226_monitor_ready(void); +// INA226 підтримує один канал, але інтерфейс залишається узагальненим. size_t ina226_monitor_channel_count(void); +// Зчитує нові дані; out_reading може бути NULL, якщо дані не потрібні. esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading); +// Повертає кеш останнього вимірювання або NULL, якщо модуль неактивний. const ina226_reading_t *ina226_monitor_get_last(void); diff --git a/main/main.c b/main/main.c index 975e89b..d3a3ea7 100644 --- a/main/main.c +++ b/main/main.c @@ -1,3 +1,9 @@ +/* + * Developed by TComLab + * Version: v0.1 + * Date: 2025-12-15 + */ + #include #include "freertos/FreeRTOS.h" @@ -7,13 +13,21 @@ #include "dcdc_controller.h" #include "ina226_monitor.h" #include "uart_mux.h" -#include "usb_cdc_cli.h" +#include "usb_cdc_log.h" #include "ws2812_status.h" static const char *TAG = "watch-watch"; void app_main(void) { + if (usb_cdc_log_init() != ESP_OK) { + ESP_LOGW(TAG, "USB CDC лог недоступний"); + } else { + ESP_LOGI(TAG, "USB CDC лог активовано"); + } + + vTaskDelay(pdMS_TO_TICKS(2000)); // Затримка для стабілізації живлення після перезавантаження + ESP_LOGI(TAG, "Запуск watch-watch systems"); if (dcdc_init() != ESP_OK) { ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера"); ws2812_status_init(); @@ -23,6 +37,10 @@ void app_main(void) if (ws2812_status_init() == ESP_OK) { ws2812_status_refresh_from_dcdc(); + esp_err_t anim_err = ws2812_status_play_bringup_animation(2, 120); + if (anim_err != ESP_OK) { + ESP_LOGW(TAG, "Анімація WS2812 недоступна: %s", esp_err_to_name(anim_err)); + } } else { ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний"); } @@ -40,11 +58,7 @@ void app_main(void) ESP_LOGW(TAG, "UART мультиплексор недоступний"); } - if (usb_cdc_cli_init() != ESP_OK) { - ESP_LOGW(TAG, "USB CDC CLI недоступний"); - } else { - ESP_LOGI(TAG, "USB CDC CLI запущено"); - } + ESP_LOGI(TAG, "Початок послідовного ввімкнення каналів з інтервалом 4 с"); const TickType_t on_time = pdMS_TO_TICKS(4000); diff --git a/main/uart_mux.c b/main/uart_mux.c index 2d5cd8a..8f7a989 100644 --- a/main/uart_mux.c +++ b/main/uart_mux.c @@ -1,3 +1,9 @@ +/* + * Developed by TComLab + * Version: v0.1 + * Date: 2025-12-15 + */ + #include "uart_mux.h" #include @@ -38,6 +44,8 @@ static bool s_initialized; static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS]; static TaskHandle_t s_watchdog_task; +// Перемикає апаратний мультиплексор на вказаний канал під захистом мьютекса, +// оновлюючи таймстемп останнього heartbeat для контролю watchdog. static esp_err_t uart_mux_select_locked(size_t channel) { if (channel >= UART_MUX_MAX_CHANNELS) { @@ -56,6 +64,8 @@ static esp_err_t uart_mux_select_locked(size_t channel) return ESP_OK; } +// Періодично опитує всі канали, щоб зчитати heartbeat та перезапускає DCDC, +// якщо канал «мовчить» довше за CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC. static void uart_mux_watchdog_task(void *arg) { const TickType_t poll_interval = pdMS_TO_TICKS(1000); @@ -94,6 +104,8 @@ static void uart_mux_watchdog_task(void *arg) #endif // CONFIG_WATCH_UART_MUX_ENABLED +// Налаштовує GPIO-вибірники, драйвер UART та створює watchdog-задачу для +// мультиплексора; повторний виклик просто повертає ESP_OK. esp_err_t uart_mux_init(void) { #if !CONFIG_WATCH_UART_MUX_ENABLED @@ -156,6 +168,7 @@ esp_err_t uart_mux_init(void) #endif } +// Повертає ознаку ініціалізації модулю UART мультиплексора. bool uart_mux_ready(void) { #if CONFIG_WATCH_UART_MUX_ENABLED @@ -165,11 +178,14 @@ bool uart_mux_ready(void) #endif } +// Кількість доступних каналів, визначених у конфігурації. size_t uart_mux_channel_count(void) { return CONFIG_WATCH_UART_MUX_CHANNELS; } +// Перемикається на канал, передає буфер даних через загальний UART та +// захищає доступ до шини мьютексом, щоб уникнути гонок між задачами. esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout) { #if !CONFIG_WATCH_UART_MUX_ENABLED @@ -199,6 +215,8 @@ esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, Tic #endif } +// Читає дані з вказаного каналу, оновлюючи heartbeat під час успішного +// зчитування, і повертає кількість байтів через out_length. esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout) { #if !CONFIG_WATCH_UART_MUX_ENABLED diff --git a/main/uart_mux.h b/main/uart_mux.h index 79bfd0d..15e2d03 100644 --- a/main/uart_mux.h +++ b/main/uart_mux.h @@ -1,3 +1,9 @@ +/* + * Developed by TComLab + * Version: v0.1 + * Date: 2025-12-15 + */ + #pragma once #include @@ -5,8 +11,13 @@ #include "freertos/FreeRTOS.h" #include "esp_err.h" +// Ініціалізує апаратний мультиплексор UART: GPIO, UART драйвер та watchdog. esp_err_t uart_mux_init(void); +// Повертає true, якщо драйвер готовий приймати виклики. bool uart_mux_ready(void); +// Кількість доступних каналів мультиплексора. size_t uart_mux_channel_count(void); +// Відправляє буфер даних на вказаний канал з тайм-аутом очікування мьютекса. esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout); +// Зчитує дані з каналу, повертаючи кількість байтів через out_length. esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout); diff --git a/main/usb_cdc_log.c b/main/usb_cdc_log.c new file mode 100644 index 0000000..fd2ad77 --- /dev/null +++ b/main/usb_cdc_log.c @@ -0,0 +1,98 @@ +#include "usb_cdc_log.h" + +#include +#include + +#include "esp_check.h" +#include "esp_log.h" +#include "tinyusb.h" +#include "tusb_cdc_acm.h" + +static const char *TAG = "usb_log"; + +static bool s_initialized; +static bool s_host_ready; +static vprintf_like_t s_prev_vprintf; + +static int usb_cdc_vprintf(const char *fmt, va_list args) +{ + if (s_host_ready) { + char buffer[256]; + va_list args_copy; + va_copy(args_copy, args); + int len = vsnprintf(buffer, sizeof(buffer), fmt, args_copy); + va_end(args_copy); + if (len > 0) { + if (len >= (int)sizeof(buffer)) { + len = sizeof(buffer) - 1; + } + if (tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (const uint8_t *)buffer, len) == ESP_OK) { + tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, 0); + } + } + return len; + } + + if (s_prev_vprintf) { + va_list args_copy; + va_copy(args_copy, args); + int ret = s_prev_vprintf(fmt, args_copy); + va_end(args_copy); + return ret; + } + return 0; +} + +static void usb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) +{ + (void)itf; + s_host_ready = event->line_state_changed_data.dtr; + if (s_host_ready) { + const char banner[] = "\r\nwatch-watch log over USB CDC\r\n"; + tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (const uint8_t *)banner, sizeof(banner) - 1); + tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, 0); + } +} + +bool usb_cdc_log_ready(void) +{ + return s_host_ready; +} + +esp_err_t usb_cdc_log_init(void) +{ + if (s_initialized) { + return ESP_OK; + } + + const tinyusb_config_t tusb_cfg = { + .device_descriptor = NULL, + .string_descriptor = NULL, + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = NULL, + .hs_configuration_descriptor = NULL, + .qualifier_descriptor = NULL, +#else + .configuration_descriptor = NULL, +#endif + }; + + ESP_RETURN_ON_ERROR(tinyusb_driver_install(&tusb_cfg), TAG, "TinyUSB init failed"); + + const tinyusb_config_cdcacm_t acm_cfg = { + .usb_dev = TINYUSB_USBDEV_0, + .cdc_port = TINYUSB_CDC_ACM_0, + .rx_unread_buf_sz = 64, + .callback_rx = NULL, + .callback_rx_wanted_char = NULL, + .callback_line_state_changed = usb_cdc_line_state_changed_callback, + .callback_line_coding_changed = NULL, + }; + ESP_RETURN_ON_ERROR(tusb_cdc_acm_init(&acm_cfg), TAG, "CDC init failed"); + + s_prev_vprintf = esp_log_set_vprintf(usb_cdc_vprintf); + s_initialized = true; + ESP_LOGI(TAG, "ESP_LOG перенаправлено в USB CDC"); + return ESP_OK; +} diff --git a/main/usb_cdc_log.h b/main/usb_cdc_log.h new file mode 100644 index 0000000..3efdac1 --- /dev/null +++ b/main/usb_cdc_log.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "esp_err.h" + +/** + * @brief Ініціалізує TinyUSB CDC та перенаправляє ESP_LOG у USB. + */ +esp_err_t usb_cdc_log_init(void); + +/** + * @brief Чи встановив хост DTR і готовий приймати лог. + */ +bool usb_cdc_log_ready(void); diff --git a/main/ws2812_status.c b/main/ws2812_status.c index 1f343a3..b78a4c6 100644 --- a/main/ws2812_status.c +++ b/main/ws2812_status.c @@ -4,6 +4,8 @@ #include "led_strip.h" #include "esp_check.h" #include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "sdkconfig.h" #ifndef CONFIG_WATCH_WS2812_LED_COUNT @@ -132,3 +134,62 @@ esp_err_t ws2812_status_refresh_from_dcdc(void) } return ws2812_status_apply(); } + +esp_err_t ws2812_status_play_bringup_animation(size_t cycles, uint32_t step_delay_ms) +{ + if (!s_strip) { + return ESP_ERR_INVALID_STATE; + } + + if (cycles == 0) { + cycles = 1; + } + if (step_delay_ms == 0) { + step_delay_ms = 150; + } + + bool saved_led_state[WS2812_STATUS_LED_COUNT]; + for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) { + saved_led_state[i] = s_led_state[i]; + } + const bool saved_error = s_error_state; + const size_t saved_active = s_active_channel; + + s_error_state = false; + s_active_channel = SIZE_MAX; + + const TickType_t delay_ticks = pdMS_TO_TICKS(step_delay_ms); + esp_err_t err = ESP_OK; + + for (size_t cycle = 0; cycle < cycles && err == ESP_OK; ++cycle) { + for (size_t led = 0; led < WS2812_STATUS_LED_COUNT; ++led) { + for (size_t idx = 0; idx < WS2812_STATUS_LED_COUNT; ++idx) { + const uint8_t g = (idx == led) ? 35 : 0; + err = led_strip_set_pixel(s_strip, idx, 0, g, 0); + if (err != ESP_OK) { + break; + } + } + if (err != ESP_OK) { + break; + } + err = led_strip_refresh(s_strip); + if (err != ESP_OK) { + break; + } + vTaskDelay(delay_ticks); + } + } + + for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) { + s_led_state[i] = saved_led_state[i]; + } + s_error_state = saved_error; + s_active_channel = saved_active; + + esp_err_t restore_err = ws2812_status_apply(); + if (err != ESP_OK) { + return err; + } + return restore_err; +} diff --git a/main/ws2812_status.h b/main/ws2812_status.h index c221f42..fc1b744 100644 --- a/main/ws2812_status.h +++ b/main/ws2812_status.h @@ -2,6 +2,7 @@ #include #include +#include #include "esp_err.h" #include "sdkconfig.h" @@ -18,3 +19,4 @@ esp_err_t ws2812_status_mark_active(size_t channel); esp_err_t ws2812_status_clear_active(void); esp_err_t ws2812_status_set_error(bool has_error); esp_err_t ws2812_status_refresh_from_dcdc(void); +esp_err_t ws2812_status_play_bringup_animation(size_t cycles, uint32_t step_delay_ms); From 89ded8b119b53c0eeb10f0076804614ff2b85a31 Mon Sep 17 00:00:00 2001 From: Taras Syvash Date: Tue, 16 Dec 2025 16:19:33 +0200 Subject: [PATCH 2/3] Improve heartbeat indication and docs --- README.md | 7 ++- main/main.c | 26 ++++---- main/uart_mux.c | 29 ++++++++- main/usb_cdc_log.c | 10 +--- main/ws2812_status.c | 139 +++++++++++++++++++++++++++++++++++++++---- main/ws2812_status.h | 2 + 6 files changed, 176 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 72a9da0..d716af8 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,12 @@ watch-watch — вбудована система на ESP32-S3 для нагл ## Світлодіоди стану - IC WS2812 підключений до GPIO 8 (один ланцюг із 5 діодів). -- Модуль `ws2812_status` синхронізує стан з DC/DC: увімкнені канали світяться зеленим, вимкнені — синім, активний у поточному циклі — яскраво-зеленим крапкою. +- Модуль `ws2812_status` показує одночасно три аспекти стану: + - **Живлення**: якщо канал вимкнений, сегмент горить тьмяно-синім; увімкнений канал — зеленим/синім залежно від зв’язку. + - **UART-зв’язок**: після кожного опитування Raspberry Pi канал, що відповів (`{"hb":2}`), стає зеленим; якщо відповідей не було, він переходить у синій. Таким чином можна одразу бачити, хто не відповідає. + - **Активний канал / опитування**: канал, який зараз опитує watchdog або проходить тестовий цикл, підсвічується яскравішим тоном; поки триває читання UART, колір м’яко блимає, щоб показати активність, але не приховати власний статус. - За критичної помилки (наприклад, DCDC не ініціалізувався) всі індикатори стають червоними. -- Колірні алгоритми можна кастомізувати у `main/ws2812_status.c`. +- Колірні алгоритми можна кастомізувати у `main/ws2812_status.c`, там же знаходиться API `ws2812_status_set_ack_state()` для відображення acknowledge. - GPIO, кількість діодів та тактову частоту RMT можна змінити через `idf.py menuconfig` (розділ *Налаштування watch-watch*). ## Моніторинг живлення (INA226) diff --git a/main/main.c b/main/main.c index d3a3ea7..f133ab0 100644 --- a/main/main.c +++ b/main/main.c @@ -17,6 +17,7 @@ #include "ws2812_status.h" static const char *TAG = "watch-watch"; +static const char HB_MESSAGE[] = "{\"hb\":1}\r\n"; void app_main(void) { @@ -37,7 +38,7 @@ void app_main(void) if (ws2812_status_init() == ESP_OK) { ws2812_status_refresh_from_dcdc(); - esp_err_t anim_err = ws2812_status_play_bringup_animation(2, 120); + esp_err_t anim_err = ws2812_status_play_bringup_animation(1, 120); if (anim_err != ESP_OK) { ESP_LOGW(TAG, "Анімація WS2812 недоступна: %s", esp_err_to_name(anim_err)); } @@ -63,17 +64,13 @@ void app_main(void) ESP_LOGI(TAG, "Початок послідовного ввімкнення каналів з інтервалом 4 с"); const TickType_t on_time = pdMS_TO_TICKS(4000); - size_t prev_channel = DCDC_CHANNEL_COUNT - 1; while (true) { for (size_t ch = 0; ch < dcdc_channel_count(); ++ch) { - if (prev_channel != ch) { - dcdc_disable(prev_channel); - ws2812_status_set_channel_state(prev_channel, false); + if (!dcdc_get_state(ch)) { + ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch); + dcdc_enable(ch); + ws2812_status_set_channel_state(ch, true); } - - ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch); - dcdc_enable(ch); - ws2812_status_set_channel_state(ch, true); ws2812_status_mark_active(ch); vTaskDelay(on_time); @@ -84,13 +81,14 @@ void app_main(void) } if (uart_mux_ready()) { - char msg[64]; - int len = snprintf(msg, sizeof(msg), "PWR %.2fV %.0fmA\r\n", - reading.voltage_v, reading.current_ma); - uart_mux_write(ch, (const uint8_t *)msg, len, pdMS_TO_TICKS(100)); + if (uart_mux_write(ch, + (const uint8_t *)HB_MESSAGE, + sizeof(HB_MESSAGE) - 1, + pdMS_TO_TICKS(100)) != ESP_OK) { + ESP_LOGW(TAG, "Не вдалося надіслати heartbeat на канал %d", (int)ch); + } } - prev_channel = ch; } } } diff --git a/main/uart_mux.c b/main/uart_mux.c index 8f7a989..cd7e825 100644 --- a/main/uart_mux.c +++ b/main/uart_mux.c @@ -7,6 +7,7 @@ #include "uart_mux.h" #include +#include #include "dcdc_controller.h" #include "driver/gpio.h" @@ -18,6 +19,7 @@ #include "freertos/semphr.h" #include "freertos/task.h" #include "sdkconfig.h" +#include "ws2812_status.h" #ifndef CONFIG_WATCH_UART_MUX_CHANNELS #define CONFIG_WATCH_UART_MUX_CHANNELS 5 @@ -43,6 +45,7 @@ static size_t s_active_channel = SIZE_MAX; static bool s_initialized; static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS]; static TaskHandle_t s_watchdog_task; +static uint8_t s_consecutive_miss[UART_MUX_MAX_CHANNELS]; // Перемикає апаратний мультиплексор на вказаний канал під захистом мьютекса, // оновлюючи таймстемп останнього heartbeat для контролю watchdog. @@ -68,7 +71,7 @@ static esp_err_t uart_mux_select_locked(size_t channel) // якщо канал «мовчить» довше за CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC. static void uart_mux_watchdog_task(void *arg) { - const TickType_t poll_interval = pdMS_TO_TICKS(1000); + const TickType_t poll_interval = pdMS_TO_TICKS(10000); const TickType_t read_timeout = pdMS_TO_TICKS(10); const int64_t timeout_us = (int64_t)CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC * 1000000LL; uint8_t buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN]; @@ -78,6 +81,7 @@ static void uart_mux_watchdog_task(void *arg) int64_t now = esp_timer_get_time(); for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) { if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(20)) == pdTRUE) { + ws2812_status_indicate_polling(ch, 2000); if (uart_mux_select_locked(ch) == ESP_OK) { int read = uart_read_bytes(CONFIG_WATCH_UART_PORT, buffer, @@ -85,16 +89,32 @@ static void uart_mux_watchdog_task(void *arg) read_timeout); if (read > 0) { s_last_heartbeat_us[ch] = now; + s_consecutive_miss[ch] = 0; + ws2812_status_set_ack_state(ch, true); + } else if (s_consecutive_miss[ch] < UINT8_MAX) { + s_consecutive_miss[ch]++; + ws2812_status_set_ack_state(ch, false); } } xSemaphoreGive(s_mutex); } + if (dcdc_get_state(ch) && s_consecutive_miss[ch] >= 3) { + ESP_LOGW(TAG, "CH%u: немає відповіді 3 рази поспіль, перезапуск живлення", (unsigned)ch); + ws2812_status_set_ack_state(ch, false); + dcdc_disable(ch); + vTaskDelay(pdMS_TO_TICKS(2000)); + dcdc_enable(ch); + s_consecutive_miss[ch] = 0; + s_last_heartbeat_us[ch] = esp_timer_get_time(); + } + if (dcdc_get_state(ch) && s_last_heartbeat_us[ch] > 0 && (now - s_last_heartbeat_us[ch]) > timeout_us) { ESP_LOGW(TAG, "Heartbeat каналу %u втрачено, перезавантаження...", (unsigned)ch); + ws2812_status_set_ack_state(ch, false); dcdc_disable(ch); - vTaskDelay(pdMS_TO_TICKS(2000)); + vTaskDelay(pdMS_TO_TICKS(3000)); dcdc_enable(ch); s_last_heartbeat_us[ch] = esp_timer_get_time(); } @@ -156,6 +176,7 @@ esp_err_t uart_mux_init(void) int64_t now = esp_timer_get_time(); for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) { s_last_heartbeat_us[ch] = now; + s_consecutive_miss[ch] = 0; } if (xTaskCreate(uart_mux_watchdog_task, "uart_mux_wd", 4096, NULL, 5, &s_watchdog_task) != pdPASS) { @@ -208,6 +229,10 @@ esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, Tic int written = uart_write_bytes(CONFIG_WATCH_UART_PORT, (const char *)data, length); if (written < 0 || (size_t)written != length) { err = ESP_FAIL; + } else { + if (uart_wait_tx_done(CONFIG_WATCH_UART_PORT, timeout) != ESP_OK) { + err = ESP_ERR_TIMEOUT; + } } } xSemaphoreGive(s_mutex); diff --git a/main/usb_cdc_log.c b/main/usb_cdc_log.c index fd2ad77..714b68e 100644 --- a/main/usb_cdc_log.c +++ b/main/usb_cdc_log.c @@ -12,7 +12,6 @@ static const char *TAG = "usb_log"; static bool s_initialized; static bool s_host_ready; -static vprintf_like_t s_prev_vprintf; static int usb_cdc_vprintf(const char *fmt, va_list args) { @@ -33,13 +32,6 @@ static int usb_cdc_vprintf(const char *fmt, va_list args) return len; } - if (s_prev_vprintf) { - va_list args_copy; - va_copy(args_copy, args); - int ret = s_prev_vprintf(fmt, args_copy); - va_end(args_copy); - return ret; - } return 0; } @@ -91,7 +83,7 @@ esp_err_t usb_cdc_log_init(void) }; ESP_RETURN_ON_ERROR(tusb_cdc_acm_init(&acm_cfg), TAG, "CDC init failed"); - s_prev_vprintf = esp_log_set_vprintf(usb_cdc_vprintf); + esp_log_set_vprintf(usb_cdc_vprintf); s_initialized = true; ESP_LOGI(TAG, "ESP_LOG перенаправлено в USB CDC"); return ESP_OK; diff --git a/main/ws2812_status.c b/main/ws2812_status.c index b78a4c6..066992e 100644 --- a/main/ws2812_status.c +++ b/main/ws2812_status.c @@ -6,6 +6,8 @@ #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/semphr.h" #include "sdkconfig.h" #ifndef CONFIG_WATCH_WS2812_LED_COUNT @@ -20,36 +22,65 @@ #define WS2812_STATUS_GPIO ((gpio_num_t)CONFIG_WATCH_WS2812_GPIO) #define WS2812_STATUS_RESOLUTION_HZ CONFIG_WATCH_WS2812_RMT_RESOLUTION +#define WS2812_POLL_BLINK_PERIOD pdMS_TO_TICKS(250) static const char *TAG = "ws2812"; static led_strip_handle_t s_strip; static bool s_led_state[WS2812_STATUS_LED_COUNT]; +static bool s_ack_state[WS2812_STATUS_LED_COUNT]; static bool s_error_state; static size_t s_active_channel = SIZE_MAX; +static bool s_polling_active[WS2812_STATUS_LED_COUNT]; +static bool s_polling_visible[WS2812_STATUS_LED_COUNT]; +static TickType_t s_polling_expire_tick[WS2812_STATUS_LED_COUNT]; +static TimerHandle_t s_polling_timers[WS2812_STATUS_LED_COUNT]; +static SemaphoreHandle_t s_ws_lock; + +static void ws2812_polling_timer_cb(TimerHandle_t timer); static esp_err_t ws2812_status_apply(void) { if (!s_strip) { return ESP_ERR_INVALID_STATE; } + if (!s_ws_lock) { + return ESP_ERR_INVALID_STATE; + } - for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) { + if (xSemaphoreTake(s_ws_lock, portMAX_DELAY) != pdTRUE) { + return ESP_ERR_TIMEOUT; + } + + esp_err_t err = ESP_OK; + for (size_t i = 0; i < WS2812_STATUS_LED_COUNT && err == ESP_OK; ++i) { uint8_t r = 0, g = 0, b = 0; if (s_error_state) { r = 40; - } else if (s_active_channel == i) { - g = 60; - } else if (s_led_state[i]) { - g = 18; + } else if (!s_led_state[i]) { + b = 5; + } else if (s_ack_state[i]) { + g = (s_active_channel == i) ? 60 : 25; } else { - b = 10; + b = (s_active_channel == i) ? 60 : 25; } - ESP_RETURN_ON_ERROR(led_strip_set_pixel(s_strip, i, r, g, b), TAG, - "led pixel set failed"); + + if (s_polling_active[i] && !s_error_state && s_led_state[i]) { + if (!s_polling_visible[i]) { + r /= 4; + g /= 4; + b /= 4; + } + } + // Swap R/G to match physical RGB order (driver outputs GRB) + err = led_strip_set_pixel(s_strip, i, g, r, b); + } + if (err == ESP_OK) { + err = led_strip_refresh(s_strip); } - return led_strip_refresh(s_strip); + xSemaphoreGive(s_ws_lock); + return err; } esp_err_t ws2812_status_init(void) @@ -77,8 +108,16 @@ esp_err_t ws2812_status_init(void) "Не вдалося створити RMT пристрій для WS2812"); ESP_RETURN_ON_ERROR(led_strip_clear(s_strip), TAG, "clear fail"); + s_ws_lock = xSemaphoreCreateMutex(); + ESP_RETURN_ON_FALSE(s_ws_lock, ESP_ERR_NO_MEM, TAG, "mutex alloc failed"); + for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) { s_led_state[i] = false; + s_ack_state[i] = false; + s_polling_active[i] = false; + s_polling_visible[i] = false; + s_polling_expire_tick[i] = 0; + s_polling_timers[i] = NULL; } s_active_channel = SIZE_MAX; s_error_state = false; @@ -86,12 +125,78 @@ esp_err_t ws2812_status_init(void) return ws2812_status_apply(); } +static void ws2812_polling_timer_cb(TimerHandle_t timer) +{ + size_t channel = (size_t)pvTimerGetTimerID(timer); + if (channel >= WS2812_STATUS_LED_COUNT) { + return; + } + TickType_t now = xTaskGetTickCount(); + if (now >= s_polling_expire_tick[channel]) { + s_polling_active[channel] = false; + s_polling_visible[channel] = false; + s_polling_expire_tick[channel] = 0; + xTimerStop(timer, 0); + } else { + s_polling_visible[channel] = !s_polling_visible[channel]; + } + ws2812_status_apply(); +} + +esp_err_t ws2812_status_indicate_polling(size_t channel, uint32_t duration_ms) +{ + if (channel >= WS2812_STATUS_LED_COUNT) { + return ESP_ERR_INVALID_ARG; + } + if (!s_strip) { + return ESP_ERR_INVALID_STATE; + } + if (duration_ms == 0) { + duration_ms = 2000; + } + + TickType_t duration_ticks = pdMS_TO_TICKS(duration_ms); + if (duration_ticks == 0) { + duration_ticks = 1; + } + + if (!s_polling_timers[channel]) { + s_polling_timers[channel] = xTimerCreate("ws2812_poll", + WS2812_POLL_BLINK_PERIOD, + pdTRUE, + (void *)channel, + ws2812_polling_timer_cb); + if (!s_polling_timers[channel]) { + return ESP_ERR_NO_MEM; + } + } + + s_polling_active[channel] = true; + s_polling_visible[channel] = true; + s_polling_expire_tick[channel] = xTaskGetTickCount() + duration_ticks; + esp_err_t err = ws2812_status_apply(); + if (err != ESP_OK) { + return err; + } + + BaseType_t res; + if (xTimerIsTimerActive(s_polling_timers[channel]) == pdTRUE) { + res = xTimerReset(s_polling_timers[channel], 0); + } else { + res = xTimerStart(s_polling_timers[channel], 0); + } + return (res == pdPASS) ? ESP_OK : ESP_FAIL; +} + esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled) { if (channel >= WS2812_STATUS_LED_COUNT) { return ESP_ERR_INVALID_ARG; } s_led_state[channel] = enabled; + if (!enabled) { + s_ack_state[channel] = false; + } return ws2812_status_apply(); } @@ -119,6 +224,15 @@ esp_err_t ws2812_status_set_error(bool has_error) return ws2812_status_apply(); } +esp_err_t ws2812_status_set_ack_state(size_t channel, bool received) +{ + if (channel >= WS2812_STATUS_LED_COUNT) { + return ESP_ERR_INVALID_ARG; + } + s_ack_state[channel] = received; + return ws2812_status_apply(); +} + esp_err_t ws2812_status_refresh_from_dcdc(void) { const size_t available_channels = dcdc_channel_count(); @@ -127,10 +241,15 @@ esp_err_t ws2812_status_refresh_from_dcdc(void) : WS2812_STATUS_LED_COUNT; for (size_t i = 0; i < count; ++i) { - s_led_state[i] = dcdc_get_state(i); + bool enabled = dcdc_get_state(i); + s_led_state[i] = enabled; + if (!enabled) { + s_ack_state[i] = false; + } } for (size_t i = count; i < WS2812_STATUS_LED_COUNT; ++i) { s_led_state[i] = false; + s_ack_state[i] = false; } return ws2812_status_apply(); } diff --git a/main/ws2812_status.h b/main/ws2812_status.h index fc1b744..fb467e9 100644 --- a/main/ws2812_status.h +++ b/main/ws2812_status.h @@ -18,5 +18,7 @@ esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled); esp_err_t ws2812_status_mark_active(size_t channel); esp_err_t ws2812_status_clear_active(void); esp_err_t ws2812_status_set_error(bool has_error); +esp_err_t ws2812_status_set_ack_state(size_t channel, bool received); +esp_err_t ws2812_status_indicate_polling(size_t channel, uint32_t duration_ms); esp_err_t ws2812_status_refresh_from_dcdc(void); esp_err_t ws2812_status_play_bringup_animation(size_t cycles, uint32_t step_delay_ms); From e9933da1a4895346c5f267dec36900f5bbc7c23a Mon Sep 17 00:00:00 2001 From: Taras Syvash Date: Fri, 19 Dec 2025 11:01:33 +0200 Subject: [PATCH 3/3] Improve watchdog monitoring and CLI --- README.md | 38 +++-- main/CMakeLists.txt | 7 +- main/main.c | 158 +++++++++++++---- main/uart_mux.c | 290 ++++++++++++++++++++++++++++--- main/uart_mux.h | 12 ++ main/usb_cdc_cli.c | 162 ++++++++++++++++-- main/usb_cdc_log.c | 26 +-- main/watch_config.c | 141 +++++++++++++++ main/watch_config.h | 18 ++ main/ws2812_status.c | 396 ++++++++++++++++++++++++------------------- main/ws2812_status.h | 7 +- 11 files changed, 970 insertions(+), 285 deletions(-) create mode 100644 main/watch_config.c create mode 100644 main/watch_config.h diff --git a/README.md b/README.md index d716af8..79c2d71 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ watch-watch — вбудована система на ESP32-S3 для нагл ## Основні можливості - **Керування каналами живлення**: 5 незалежних ліній `EN` (GPIO 2, 4, 5, 18, 19), які можна увімкнути, вимкнути або перемкнути з коду чи CLI. -- **Послідовний автотест**: у `app_main` реалізовано базову логіку — канали вмикаються по черзі з інтервалом 4 с, що дозволяє перевірити всі DC/DC. -- **Світлодіодний індикатор стану**: п’ять WS2812 (GPIO 8) показують роботу каналів — активний канал підсвічується яскраво-зеленим, увімкнені/вимкнені відображаються зеленим/синім, помилки — червоним. +- **Послідовний автотест**: у `app_main` реалізовано базову логіку — канали вмикаються по черзі з інтервалом 3 с, що дозволяє перевірити всі DC/DC без стрибків споживання. +- **Світлодіодний індикатор стану**: п’ять WS2812 (GPIO 8) світяться зеленим під час стартової затримки, після чого кожен канал сигналізує лише про дві події — відсутність VPN (два червоних блимання) та падіння APP (три жовтих блимання). - **Моніторинг навантаження**: датчики INA226 вимірюють напругу, струм та потужність кожного каналу, інформація потрапляє в лог і CLI. - **UART взаємодія з Raspberry Pi**: один UART через мультиплексор (A0/A1/A2) ділиться між п’ятьма Pi, дозволяючи надсилати службові повідомлення або обмінюватися даними. - **Нативний USB-CLI**: ESP32-S3 підключається до Raspberry Pi 5 по USB і стає CDC ACM пристроєм; командний інтерфейс дозволяє керувати каналами та дивитись стан у реальному часі. @@ -47,12 +47,12 @@ watch-watch — вбудована система на ESP32-S3 для нагл ## Світлодіоди стану - IC WS2812 підключений до GPIO 8 (один ланцюг із 5 діодів). -- Модуль `ws2812_status` показує одночасно три аспекти стану: - - **Живлення**: якщо канал вимкнений, сегмент горить тьмяно-синім; увімкнений канал — зеленим/синім залежно від зв’язку. - - **UART-зв’язок**: після кожного опитування Raspberry Pi канал, що відповів (`{"hb":2}`), стає зеленим; якщо відповідей не було, він переходить у синій. Таким чином можна одразу бачити, хто не відповідає. - - **Активний канал / опитування**: канал, який зараз опитує watchdog або проходить тестовий цикл, підсвічується яскравішим тоном; поки триває читання UART, колір м’яко блимає, щоб показати активність, але не приховати власний статус. -- За критичної помилки (наприклад, DCDC не ініціалізувався) всі індикатори стають червоними. -- Колірні алгоритми можна кастомізувати у `main/ws2812_status.c`, там же знаходиться API `ws2812_status_set_ack_state()` для відображення acknowledge. +- Після старту всі п’ять діодів світяться сталим зеленим протягом затримки `heartbeat_start_delay_sec`, щоб показати фазу ініціалізації. +- Після завершення затримки вся стрічка гасне, а кожен канал індикує лише два типи подій: + - VPN=0 — два червоних блимання по 200 мс із паузою між циклами; + - APP=0 — три жовтих блимання по 200 мс. + Якщо активні обидва попередження, вони програються послідовно (спочатку VPN, потім APP) із паузою 2 с між послідовностями. +- При критичній помилці (наприклад, DCDC не ініціалізувався) всі індикатори залишаються червоними. - GPIO, кількість діодів та тактову частоту RMT можна змінити через `idf.py menuconfig` (розділ *Налаштування watch-watch*). ## Моніторинг живлення (INA226) @@ -87,11 +87,29 @@ watch-watch — вбудована система на ESP32-S3 для нагл | `sense` | виміряти загальну напругу/струм/потужність | | `uart send ` | надіслати повідомлення у Raspberry Pi `n` | | `uart read [len]` | прочитати відповідь від Raspberry Pi `n` | -| `uart send ` | відправити текст у Raspberry Pi №n | -| `uart read [len]` | прочитати дані з цільового Pi | +| `config show` | переглянути таймінги heartbeat/DCDC | +| `config set …` | змінити та зберегти таймінги / моніторинг | +| `reset` | м’яке перезавантаження ESP32-S3 | +| `bootloader` | перезавантажити ESP32-S3 у ROM bootloader для `esptool.py` | CLI з’являється після того, як Raspberry Pi встановить DTR (наприклад, через `screen`, `picocom` або власний скрипт). +### Керування таймінгами heartbeat +Команда `config` дозволяє зберігати параметри роботи watchdog та циклу heartbeat у NVS, і вони одразу набувають чинності без перезбирання прошивки: + +1. `hb_period` — інтервал (сек) між опитуваннями/heartbeat у головному циклі. +2. `dcdc_off` — тривалість вимкнення каналу DCDC при автоматичному перезапуску (сек). +3. `hb_start` — затримка перед стартом опитування після завантаження (сек); застосовується як у головному циклі, так і у watchdog-завданні. +4. `hb_monitor` — значення `1` вмикає моніторинг heartbeat (за замовчуванням), `0` вимикає лише контроль/рестарти каналів, але тестові повідомлення heartbeat продовжують відправлятися. +5. `hb_miss` — кількість послідовних запитів без відповіді перед автоматичним перезапуском каналу (за замовчуванням 3). Значення зберігає watchdog та використовується в CLI для відображення статистики пропусків/рестартів. + +### Прошивка без натискання BOOT +1. Під’єднайтесь до CLI та виконайте команду `bootloader`. ESP32-S3 перезавантажиться у ROM bootloader, а на хості з’явиться USB-пристрій `USB JTAG/serial`. +2. На Raspberry Pi запустіть `esptool.py` або `idf.py flash` і вкажіть новий порт (`/dev/ttyACM*` або `/dev/cu.usbmodem*`). Наприклад: + `esptool.py --chip esp32s3 --port /dev/ttyACM0 --before usb_reset --after no_reset write_flash 0x0 build/watch-watch.bin` +3. Після завершення прошивки виконайте `esptool.py --after hard_reset reset` або просто перезавантажте живлення — пристрій вийде з bootloader і повернеться до нормальної роботи. +Таким чином процедура оновлення доступна без натискання кнопки BOOT і може виконуватись безпосередньо з підключеної Raspberry Pi. + ## Збірка та конфігурація 1. Встановіть ESP-IDF v5.5.1 (шлях `IDF_PATH` має вказувати на `/Users/tarassivas/esp/v5.5.1/esp-idf`). 2. Один раз виконайте `idf.py reconfigure`, щоб підвантажити залежності з `idf_component.yml`. diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index baa3e36..0d864f5 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,7 +1,8 @@ -idf_component_register(SRCS "main.c" +idf_component_register(SRCS "ws2812_status.c" "watch_config.c" "usb_cdc_cli.c" "main.c" "dcdc_controller.c" "usb_cdc_log.c" - "ws2812_status.c" + "ina226_monitor.c" "uart_mux.c" - INCLUDE_DIRS ".") + INCLUDE_DIRS "." + REQUIRES nvs_flash driver esp_timer) diff --git a/main/main.c b/main/main.c index f133ab0..4f31b11 100644 --- a/main/main.c +++ b/main/main.c @@ -5,45 +5,80 @@ */ #include +#include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" +#include "sdkconfig.h" #include "dcdc_controller.h" #include "ina226_monitor.h" #include "uart_mux.h" +#include "usb_cdc_cli.h" #include "usb_cdc_log.h" +#include "watch_config.h" #include "ws2812_status.h" static const char *TAG = "watch-watch"; -static const char HB_MESSAGE[] = "{\"hb\":1}\r\n"; +static const char HB_MESSAGE[] = "{\"cmd\":\"status\"}\r\n"; + +static int noop_vprintf(const char *fmt, va_list args) +{ + (void)fmt; + (void)args; + return 0; +} void app_main(void) { - if (usb_cdc_log_init() != ESP_OK) { - ESP_LOGW(TAG, "USB CDC лог недоступний"); + esp_log_set_vprintf(noop_vprintf); + + if (watch_config_init() != ESP_OK) { + ESP_LOGE(TAG, "Не вдалося ініціалізувати конфігурацію"); } else { - ESP_LOGI(TAG, "USB CDC лог активовано"); + ESP_LOGI(TAG, "Конфігурацію завантажено"); } - - vTaskDelay(pdMS_TO_TICKS(2000)); // Затримка для стабілізації живлення після перезавантаження + + if (usb_cdc_cli_init() == ESP_OK) { + ESP_LOGI(TAG, "USB CDC CLI активовано"); + } else { + ESP_LOGW(TAG, "Не вдалося запустити USB CLI"); + } + + bool ws_ready = false; + if (ws2812_status_init() == ESP_OK) { + ws_ready = true; + } else { + ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний"); + } + + const watch_config_t *cfg = watch_config_get(); + const uint32_t start_delay_ms = cfg->heartbeat_start_delay_sec * 1000U; + TickType_t start_delay = pdMS_TO_TICKS(start_delay_ms); + if (start_delay > 0) { + ESP_LOGI(TAG, "Очікування %u с перед стартом опитування", cfg->heartbeat_start_delay_sec); + if (ws_ready) { + ws2812_status_set_startup_hold(start_delay_ms); + } + vTaskDelay(start_delay); + if (ws_ready) { + ws2812_status_set_startup_hold(0); + } + } + ESP_LOGI(TAG, "Запуск watch-watch systems"); if (dcdc_init() != ESP_OK) { ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера"); - ws2812_status_init(); - ws2812_status_set_error(true); + if (ws_ready) { + ws2812_status_set_error(true); + } return; } - if (ws2812_status_init() == ESP_OK) { + if (ws_ready) { ws2812_status_refresh_from_dcdc(); - esp_err_t anim_err = ws2812_status_play_bringup_animation(1, 120); - if (anim_err != ESP_OK) { - ESP_LOGW(TAG, "Анімація WS2812 недоступна: %s", esp_err_to_name(anim_err)); - } - } else { - ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний"); } if (ina226_monitor_init() == ESP_OK) { @@ -59,36 +94,97 @@ void app_main(void) ESP_LOGW(TAG, "UART мультиплексор недоступний"); } - - - ESP_LOGI(TAG, "Початок послідовного ввімкнення каналів з інтервалом 4 с"); - const TickType_t on_time = pdMS_TO_TICKS(4000); + const TickType_t mux_timeout = pdMS_TO_TICKS(600); + TickType_t channel_next_hb[DCDC_CHANNEL_COUNT] = {0}; + bool channel_powered[DCDC_CHANNEL_COUNT] = {false}; + const TickType_t power_on_stagger = pdMS_TO_TICKS(3000); + ESP_LOGI(TAG, "Початок циклічного опитування всіх каналів"); while (true) { - for (size_t ch = 0; ch < dcdc_channel_count(); ++ch) { - if (!dcdc_get_state(ch)) { + cfg = watch_config_get(); + TickType_t hb_period = pdMS_TO_TICKS(cfg->heartbeat_period_sec * 1000U); + if (hb_period == 0) { + hb_period = 1; + } + TickType_t now = xTaskGetTickCount(); + size_t channels = dcdc_channel_count(); + for (size_t ch = 0; ch < channels; ++ch) { + bool powered = dcdc_get_state(ch); + if (!powered) { + channel_powered[ch] = false; ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch); - dcdc_enable(ch); - ws2812_status_set_channel_state(ch, true); + if (dcdc_enable(ch) == ESP_OK) { + powered = true; + channel_powered[ch] = true; + channel_next_hb[ch] = now + hb_period; + if (ws_ready) { + ws2812_status_set_channel_state(ch, true); + } + vTaskDelay(power_on_stagger); + } else { + ESP_LOGE(TAG, "Не вдалося ввімкнути канал %d", (int)ch); + continue; + } + } + + if (!channel_powered[ch]) { + channel_powered[ch] = true; + channel_next_hb[ch] = now + hb_period; + continue; + } + + now = xTaskGetTickCount(); + if (now < channel_next_hb[ch]) { + continue; } - ws2812_status_mark_active(ch); - vTaskDelay(on_time); ina226_reading_t reading = {0}; - if (ina226_monitor_sample(&reading) == ESP_OK) { + if (ina226_monitor_ready() && ina226_monitor_sample(&reading) == ESP_OK) { ESP_LOGI(TAG, "Живлення: %.2f В, %.1f мА, %.1f мВт", reading.voltage_v, reading.current_ma, reading.power_mw); } if (uart_mux_ready()) { - if (uart_mux_write(ch, - (const uint8_t *)HB_MESSAGE, - sizeof(HB_MESSAGE) - 1, - pdMS_TO_TICKS(100)) != ESP_OK) { - ESP_LOGW(TAG, "Не вдалося надіслати heartbeat на канал %d", (int)ch); + esp_err_t tx_err = uart_mux_write(ch, + (const uint8_t *)HB_MESSAGE, + sizeof(HB_MESSAGE) - 1, + mux_timeout); + if (tx_err != ESP_OK) { + ESP_LOGW(TAG, "Не вдалося надіслати heartbeat на канал %d: %s", + (int)ch, esp_err_to_name(tx_err)); + } else { + uint8_t rx_buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN]; + size_t received = 0; + esp_err_t rx_err = uart_mux_read(ch, + rx_buffer, + sizeof(rx_buffer), + &received, + mux_timeout); + if (rx_err == ESP_OK) { + if (received > 0) { + ESP_LOGI(TAG, "RX CH%u (%u байт після heartbeat)", (unsigned)ch, (unsigned)received); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, rx_buffer, received, ESP_LOG_INFO); + uart_mux_process_rx(ch, rx_buffer, received); + } else { + ESP_LOGW(TAG, "Канал %d не відповів даними на heartbeat", (int)ch); + uart_mux_report_miss(ch); + } + } else if (rx_err == ESP_ERR_TIMEOUT) { + ESP_LOGW(TAG, "Час очікування відповіді з каналу %d перевищено", (int)ch); + uart_mux_report_miss(ch); + } else { + ESP_LOGW(TAG, "Помилка читання відповіді CH%u: %s", + (unsigned)ch, esp_err_to_name(rx_err)); + uart_mux_report_miss(ch); + } } + } else { + ESP_LOGW(TAG, "UART мультиплексор недоступний, очікування..."); } + channel_next_hb[ch] = now + hb_period; } + + vTaskDelay(pdMS_TO_TICKS(50)); } } diff --git a/main/uart_mux.c b/main/uart_mux.c index cd7e825..a30ecb7 100644 --- a/main/uart_mux.c +++ b/main/uart_mux.c @@ -6,6 +6,7 @@ #include "uart_mux.h" +#include #include #include @@ -20,6 +21,7 @@ #include "freertos/task.h" #include "sdkconfig.h" #include "ws2812_status.h" +#include "watch_config.h" #ifndef CONFIG_WATCH_UART_MUX_CHANNELS #define CONFIG_WATCH_UART_MUX_CHANNELS 5 @@ -46,6 +48,167 @@ static bool s_initialized; static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS]; static TaskHandle_t s_watchdog_task; static uint8_t s_consecutive_miss[UART_MUX_MAX_CHANNELS]; +static bool s_watchdog_armed[UART_MUX_MAX_CHANNELS]; +static uint32_t s_total_miss_count[UART_MUX_MAX_CHANNELS]; +static uint32_t s_restart_count[UART_MUX_MAX_CHANNELS]; + +static uint8_t uart_mux_get_miss_limit_from_config(void); +static void uart_mux_restart_channel(size_t channel); +static void uart_mux_record_miss(size_t channel, uint8_t miss_limit); + +static TickType_t uart_mux_restart_delay_ticks(void) +{ + const watch_config_t *cfg = watch_config_get(); + uint32_t sec = cfg ? cfg->dcdc_restart_off_sec : 2U; + if (sec == 0) { + sec = 1; + } + return pdMS_TO_TICKS(sec * 1000U); +} + +static uint8_t uart_mux_get_miss_limit_from_config(void) +{ + const watch_config_t *cfg = watch_config_get(); + uint32_t limit = cfg ? cfg->heartbeat_miss_limit : 3U; + if (limit == 0) { + limit = 3U; + } + if (limit > UINT8_MAX) { + limit = UINT8_MAX; + } + return (uint8_t)limit; +} + +static void uart_mux_restart_channel(size_t channel) +{ + if (channel >= UART_MUX_MAX_CHANNELS) { + return; + } + ESP_LOGW(TAG, "CH%u: перезапуск живлення після відсутності відповіді", (unsigned)channel); + dcdc_disable(channel); + vTaskDelay(uart_mux_restart_delay_ticks()); + dcdc_enable(channel); + s_consecutive_miss[channel] = 0; + s_watchdog_armed[channel] = false; + if (s_restart_count[channel] < UINT32_MAX) { + ++s_restart_count[channel]; + } + s_last_heartbeat_us[channel] = esp_timer_get_time(); +} + +static void uart_mux_record_miss(size_t channel, uint8_t miss_limit) +{ + if (channel >= UART_MUX_MAX_CHANNELS) { + return; + } + if (!dcdc_get_state(channel)) { + return; + } + if (!s_watchdog_armed[channel]) { + s_watchdog_armed[channel] = true; + s_consecutive_miss[channel] = 0; + } + if (s_total_miss_count[channel] < UINT32_MAX) { + ++s_total_miss_count[channel]; + } + if (s_consecutive_miss[channel] < UINT8_MAX) { + ++s_consecutive_miss[channel]; + } + if (miss_limit == 0) { + miss_limit = 1; + } + if (s_consecutive_miss[channel] >= miss_limit) { + uart_mux_restart_channel(channel); + } +} + +static bool uart_mux_extract_numeric_field(const uint8_t *data, + size_t length, + const char *key, + int *out_value) +{ + if (!data || !key || !out_value) { + return false; + } + const size_t key_len = strlen(key); + if (key_len == 0) { + return false; + } + + for (size_t i = 0; i < length; ++i) { + if (data[i] != '"') { + continue; + } + size_t j = i + 1; + if (j + key_len >= length) { + break; + } + if (memcmp(&data[j], key, key_len) != 0) { + continue; + } + j += key_len; + if (j >= length || data[j] != '"') { + continue; + } + ++j; + while (j < length && isspace((unsigned char)data[j])) { + ++j; + } + if (j >= length || data[j] != ':') { + continue; + } + ++j; + while (j < length && isspace((unsigned char)data[j])) { + ++j; + } + bool negative = false; + if (j < length && (data[j] == '-' || data[j] == '+')) { + negative = (data[j] == '-'); + ++j; + } + bool has_digit = false; + int value = 0; + while (j < length && isdigit((unsigned char)data[j])) { + has_digit = true; + value = value * 10 + (data[j] - '0'); + ++j; + } + if (has_digit) { + *out_value = negative ? -value : value; + return true; + } + } + return false; +} + +static void uart_mux_decode_status_payload(const uint8_t *data, + size_t length, + bool *hb_ack, + bool *vpn_ok, + bool *app_ok) +{ + int value = 0; + if (hb_ack) { + bool found = uart_mux_extract_numeric_field(data, length, "hb", &value); + *hb_ack = found && value == 2; + } + if (vpn_ok) { + bool found = uart_mux_extract_numeric_field(data, length, "VPN", &value); + *vpn_ok = found && value != 0; + } + if (app_ok) { + bool found = uart_mux_extract_numeric_field(data, length, "APP", &value); + *app_ok = found && value != 0; + } +} + +// Перевіряє, чи містить буфер відповідь {"hb":2} від Raspberry Pi. +static bool uart_mux_contains_hb_ack(const uint8_t *data, size_t length) +{ + bool ack = false; + uart_mux_decode_status_payload(data, length, &ack, NULL, NULL); + return ack; +} // Перемикає апаратний мультиплексор на вказаний канал під захистом мьютекса, // оновлюючи таймстемп останнього heartbeat для контролю watchdog. @@ -72,51 +235,67 @@ static esp_err_t uart_mux_select_locked(size_t channel) static void uart_mux_watchdog_task(void *arg) { const TickType_t poll_interval = pdMS_TO_TICKS(10000); - const TickType_t read_timeout = pdMS_TO_TICKS(10); + const TickType_t read_timeout = pdMS_TO_TICKS(300); const int64_t timeout_us = (int64_t)CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC * 1000000LL; uint8_t buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN]; + const watch_config_t *cfg = watch_config_get(); + TickType_t start_delay = pdMS_TO_TICKS(cfg->heartbeat_start_delay_sec * 1000U); + if (start_delay > 0) { + vTaskDelay(start_delay); + } while (true) { vTaskDelay(poll_interval); + const watch_config_t *cfg = watch_config_get(); + if (!cfg->heartbeat_monitor_enabled) { + continue; + } + uint8_t miss_limit = uart_mux_get_miss_limit_from_config(); int64_t now = esp_timer_get_time(); for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) { + if (!dcdc_get_state(ch)) { + s_watchdog_armed[ch] = false; + s_consecutive_miss[ch] = 0; + continue; + } if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(20)) == pdTRUE) { - ws2812_status_indicate_polling(ch, 2000); if (uart_mux_select_locked(ch) == ESP_OK) { int read = uart_read_bytes(CONFIG_WATCH_UART_PORT, buffer, sizeof(buffer), read_timeout); if (read > 0) { - s_last_heartbeat_us[ch] = now; - s_consecutive_miss[ch] = 0; - ws2812_status_set_ack_state(ch, true); - } else if (s_consecutive_miss[ch] < UINT8_MAX) { - s_consecutive_miss[ch]++; - ws2812_status_set_ack_state(ch, false); + bool ack = false; + bool vpn_ok = false; + bool app_ok = false; + uart_mux_decode_status_payload(buffer, read, &ack, &vpn_ok, &app_ok); + ESP_LOGI(TAG, "UART0 RX CH%u (%d байт)%s", + (unsigned)ch, read, ack ? " [HB ACK]" : ""); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, read, ESP_LOG_INFO); + if (ack) { + s_last_heartbeat_us[ch] = now; + s_consecutive_miss[ch] = 0; + s_watchdog_armed[ch] = true; + ws2812_status_set_service_state(ch, vpn_ok, app_ok); + } else { + uart_mux_record_miss(ch, miss_limit); + } + } else { + uart_mux_record_miss(ch, miss_limit); + ESP_LOGD(TAG, "UART0 RX CH%u: немає відповіді у watchdog (%u)", + (unsigned)ch, (unsigned)s_consecutive_miss[ch]); } } xSemaphoreGive(s_mutex); } - if (dcdc_get_state(ch) && s_consecutive_miss[ch] >= 3) { - ESP_LOGW(TAG, "CH%u: немає відповіді 3 рази поспіль, перезапуск живлення", (unsigned)ch); - ws2812_status_set_ack_state(ch, false); - dcdc_disable(ch); - vTaskDelay(pdMS_TO_TICKS(2000)); - dcdc_enable(ch); - s_consecutive_miss[ch] = 0; - s_last_heartbeat_us[ch] = esp_timer_get_time(); + if (dcdc_get_state(ch) && s_watchdog_armed[ch] && s_consecutive_miss[ch] >= miss_limit) { + uart_mux_restart_channel(ch); } if (dcdc_get_state(ch) && s_last_heartbeat_us[ch] > 0 && (now - s_last_heartbeat_us[ch]) > timeout_us) { - ESP_LOGW(TAG, "Heartbeat каналу %u втрачено, перезавантаження...", (unsigned)ch); - ws2812_status_set_ack_state(ch, false); - dcdc_disable(ch); - vTaskDelay(pdMS_TO_TICKS(3000)); - dcdc_enable(ch); - s_last_heartbeat_us[ch] = esp_timer_get_time(); + uart_mux_restart_channel(ch); } } } @@ -177,6 +356,9 @@ esp_err_t uart_mux_init(void) for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) { s_last_heartbeat_us[ch] = now; s_consecutive_miss[ch] = 0; + s_watchdog_armed[ch] = false; + s_total_miss_count[ch] = 0; + s_restart_count[ch] = 0; } if (xTaskCreate(uart_mux_watchdog_task, "uart_mux_wd", 4096, NULL, 5, &s_watchdog_task) != pdPASS) { @@ -198,7 +380,6 @@ bool uart_mux_ready(void) return false; #endif } - // Кількість доступних каналів, визначених у конфігурації. size_t uart_mux_channel_count(void) { @@ -274,3 +455,66 @@ esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, siz return err; #endif } + +void uart_mux_process_rx(size_t channel, const uint8_t *data, size_t length) +{ +#if CONFIG_WATCH_UART_MUX_ENABLED + if (!s_initialized || channel >= UART_MUX_MAX_CHANNELS || !data || length == 0) { + return; + } + bool ack = false; + bool vpn_ok = false; + bool app_ok = false; + uart_mux_decode_status_payload(data, length, &ack, &vpn_ok, &app_ok); + int64_t now = esp_timer_get_time(); + if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + s_last_heartbeat_us[channel] = now; + if (ack) { + s_consecutive_miss[channel] = 0; + s_watchdog_armed[channel] = true; + } + xSemaphoreGive(s_mutex); + } + if (ack) { + ws2812_status_set_service_state(channel, vpn_ok, app_ok); + } else { + uart_mux_record_miss(channel, uart_mux_get_miss_limit_from_config()); + } +#else + (void)channel; + (void)data; + (void)length; +#endif +} + +void uart_mux_report_miss(size_t channel) +{ +#if CONFIG_WATCH_UART_MUX_ENABLED + if (!s_initialized) { + return; + } + uart_mux_record_miss(channel, uart_mux_get_miss_limit_from_config()); +#else + (void)channel; +#endif +} + +void uart_mux_get_channel_stats(size_t channel, uart_mux_channel_stats_t *out_stats) +{ + if (!out_stats) { + return; + } +#if CONFIG_WATCH_UART_MUX_ENABLED + if (!s_initialized || channel >= UART_MUX_MAX_CHANNELS) { + out_stats->missed_heartbeats = 0; + out_stats->restart_count = 0; + return; + } + out_stats->missed_heartbeats = s_total_miss_count[channel]; + out_stats->restart_count = s_restart_count[channel]; +#else + (void)channel; + out_stats->missed_heartbeats = 0; + out_stats->restart_count = 0; +#endif +} diff --git a/main/uart_mux.h b/main/uart_mux.h index 15e2d03..a73e2ac 100644 --- a/main/uart_mux.h +++ b/main/uart_mux.h @@ -7,10 +7,16 @@ #pragma once #include +#include #include "freertos/FreeRTOS.h" #include "esp_err.h" +typedef struct { + uint32_t missed_heartbeats; + uint32_t restart_count; +} uart_mux_channel_stats_t; + // Ініціалізує апаратний мультиплексор UART: GPIO, UART драйвер та watchdog. esp_err_t uart_mux_init(void); // Повертає true, якщо драйвер готовий приймати виклики. @@ -21,3 +27,9 @@ size_t uart_mux_channel_count(void); esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout); // Зчитує дані з каналу, повертаючи кількість байтів через out_length. esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout); +// Повідомляє драйверу про дані, зчитані поза watchdog-ом (для оновлення станів). +void uart_mux_process_rx(size_t channel, const uint8_t *data, size_t length); +// Повідомляє про пропущений heartbeat (використовується основним циклом). +void uart_mux_report_miss(size_t channel); +// Повертає статистику по каналу (кількість пропущених відповідей та рестартів). +void uart_mux_get_channel_stats(size_t channel, uart_mux_channel_stats_t *out_stats); diff --git a/main/usb_cdc_cli.c b/main/usb_cdc_cli.c index 9d68e84..c0dae39 100644 --- a/main/usb_cdc_cli.c +++ b/main/usb_cdc_cli.c @@ -11,6 +11,7 @@ #include "dcdc_controller.h" #include "esp_check.h" #include "esp_log.h" +#include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" @@ -18,8 +19,12 @@ #include "tinyusb.h" #include "tusb_cdc_acm.h" #include "ws2812_status.h" +#include "watch_config.h" +#include "esp_system.h" #include "ina226_monitor.h" #include "uart_mux.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/soc.h" #define CLI_LINE_MAX_LEN 128 #define CLI_QUEUE_LEN 8 @@ -45,6 +50,10 @@ static bool s_host_ready; static void usb_cli_task(void *arg); static void usb_cli_process_line(char *line); static void usb_cli_prompt(void); +static void usb_cli_enter_bootloader(void); +static void usb_cli_handle_config(char *args); +static void usb_cli_print_config(void); +static void usb_cli_handle_reset(void); static void usb_cli_write_raw(const char *data, size_t len) { @@ -112,10 +121,16 @@ static void usb_cli_print_status(void) usb_cli_printf("\r\nСтан каналів:\r\n"); for (size_t i = 0; i < dcdc_channel_count(); ++i) { dcdc_channel_t ch = (dcdc_channel_t)i; - usb_cli_printf(" - CH%u: %s (GPIO %d)\r\n", + uart_mux_channel_stats_t stats = {0}; + if (uart_mux_ready()) { + uart_mux_get_channel_stats(i, &stats); + } + usb_cli_printf(" - CH%u: %s (GPIO %d) | miss=%u restart=%u\r\n", (unsigned)i, dcdc_get_state(ch) ? "ON" : "OFF", - (int)dcdc_get_gpio(ch)); + (int)dcdc_get_gpio(ch), + (unsigned)stats.missed_heartbeats, + (unsigned)stats.restart_count); } } @@ -124,6 +139,18 @@ static void usb_cli_prompt(void) usb_cli_write_raw(PROMPT, strlen(PROMPT)); } +static void usb_cli_enter_bootloader(void) +{ + usb_cli_printf("\r\nПерехід у режим прошивки esptool...\r\n" + "Після перезавантаження з’явиться ROM-порт USB CDC/Serial.\r\n" + "Запустіть esptool.py або idf.py flash та прошийте пристрій. " + "Для виходу з bootloader виконайте 'esptool.py --after hard_reset reset' або перезавантажте живлення.\r\n"); + vTaskDelay(pdMS_TO_TICKS(100)); + tinyusb_driver_uninstall(); + REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + esp_restart(); +} + static void usb_cli_handle_switch(const char *cmd, char *args) { dcdc_channel_t channel; @@ -246,14 +273,22 @@ static void usb_cli_process_line(char *line) if (strcasecmp(cmd, "help") == 0) { usb_cli_printf( "\r\nДоступні команди:\r\n" - " help - показати довідку\r\n" - " status - стан всіх каналів\r\n" - " enable - увімкнути канал n\r\n" - " disable - вимкнути канал n\r\n" - " toggle - перемкнути канал n\r\n" - " sense [n] - показати напругу/струм/потужність (опц. канал)\r\n" - " uart send - відправити повідомлення Pi n\r\n" - " uart read [len] - прочитати дані з Pi n\r\n"); + " help - показати цю довідку\r\n" + " status - показати стан усіх каналів DCDC\r\n" + " enable - увімкнути канал n (0..4)\r\n" + " disable - вимкнути канал n\r\n" + " toggle - перемкнути канал n\r\n" + " sense - виміряти напругу/струм/потужність INA226\r\n" + " uart send - надіслати текстове повідомлення в Raspberry Pi n\r\n" + " uart read [len] - прочитати до [len] байт відповіді від Raspberry Pi n\r\n" + " config show - показати збережені таймінги heartbeat/DCDC\r\n" + " config set hb_period <сек> - змінити інтервал відправки heartbeat\r\n" + " config set dcdc_off <сек> - задати тривалість вимкнення DCDC при рестарті\r\n" + " config set hb_start <сек> - налаштувати затримку перед стартом опитування після boot\r\n" + " config set hb_monitor <0|1> - увімкнути/вимкнути контроль відповіді heartbeat\r\n" + " config set hb_miss <шт> - кількість пропусків відповіді до рестарту каналу\r\n" + " reset - м'яко перезавантажити ESP32-S3\r\n" + " bootloader - перезавантажити ESP32-S3 у ROM bootloader для esptool\r\n"); } else if (strcasecmp(cmd, "status") == 0) { usb_cli_print_status(); } else if (strcasecmp(cmd, "enable") == 0 || @@ -264,11 +299,109 @@ static void usb_cli_process_line(char *line) usb_cli_handle_sense(save_ptr); } else if (strcasecmp(cmd, "uart") == 0) { usb_cli_handle_uart(save_ptr); + } else if (strcasecmp(cmd, "config") == 0) { + usb_cli_handle_config(save_ptr); + } else if (strcasecmp(cmd, "reset") == 0) { + usb_cli_handle_reset(); + } else if (strcasecmp(cmd, "bootloader") == 0) { + usb_cli_enter_bootloader(); } else { usb_cli_printf("\r\nНевідома команда '%s'\r\n", cmd); } } +static void usb_cli_print_config(void) +{ + const watch_config_t *cfg = watch_config_get(); + usb_cli_printf( + "\r\nПоточні налаштування:\r\n" + " hb_period: %u с\r\n" + " dcdc_off: %u с\r\n" + " hb_start_delay: %u с\r\n" + " hb_monitor: %s\r\n" + " hb_miss_limit: %u зап.\r\n", + (unsigned)cfg->heartbeat_period_sec, + (unsigned)cfg->dcdc_restart_off_sec, + (unsigned)cfg->heartbeat_start_delay_sec, + cfg->heartbeat_monitor_enabled ? "on" : "off", + (unsigned)cfg->heartbeat_miss_limit); +} + +static void usb_cli_handle_config(char *args) +{ + if (!args || *args == '\0') { + usb_cli_print_config(); + return; + } + + char *ctx = NULL; + char *action = strtok_r(args, " ", &ctx); + if (!action || strcasecmp(action, "show") == 0) { + usb_cli_print_config(); + return; + } + + if (strcasecmp(action, "set") != 0) { + usb_cli_printf("\r\nВикористання: config show | config set <знач>\r\n"); + return; + } + + char *param = strtok_r(NULL, " ", &ctx); + char *value_str = strtok_r(NULL, " ", &ctx); + if (!param || !value_str) { + usb_cli_printf("\r\nВкажіть параметр і значення в секундах\r\n"); + return; + } + + uint32_t value = (uint32_t)strtoul(value_str, NULL, 10); + + watch_config_t new_cfg = *watch_config_get(); + if (strcasecmp(param, "hb_period") == 0) { + if (value == 0) { + usb_cli_printf("\r\nЗначення має бути більше нуля\r\n"); + return; + } + new_cfg.heartbeat_period_sec = value; + } else if (strcasecmp(param, "dcdc_off") == 0) { + if (value == 0) { + usb_cli_printf("\r\nЗначення має бути більше нуля\r\n"); + return; + } + new_cfg.dcdc_restart_off_sec = value; + } else if (strcasecmp(param, "hb_start") == 0 || + strcasecmp(param, "hb_start_delay") == 0) { + if (value == 0) { + usb_cli_printf("\r\nЗначення має бути більше нуля\r\n"); + return; + } + new_cfg.heartbeat_start_delay_sec = value; + } else if (strcasecmp(param, "hb_monitor") == 0) { + if (value != 0 && value != 1) { + usb_cli_printf("\r\nhb_monitor приймає 0 або 1\r\n"); + return; + } + new_cfg.heartbeat_monitor_enabled = (value != 0); + } else if (strcasecmp(param, "hb_miss") == 0 || + strcasecmp(param, "hb_miss_limit") == 0) { + if (value == 0) { + usb_cli_printf("\r\nhb_miss має бути більше нуля\r\n"); + return; + } + new_cfg.heartbeat_miss_limit = value; + } else { + usb_cli_printf("\r\nНевідомий параметр '%s'\r\n", param); + return; + } + + esp_err_t err = watch_config_save(&new_cfg); + if (err == ESP_OK) { + usb_cli_printf("\r\nПараметр оновлено\r\n"); + usb_cli_print_config(); + } else { + usb_cli_printf("\r\nПомилка збереження конфігурації: %s\r\n", esp_err_to_name(err)); + } +} + static void usb_cli_task(void *arg) { usb_cli_msg_t msg; @@ -285,14 +418,17 @@ static void usb_cli_task(void *arg) usb_cli_process_line(line); line_len = 0; } + usb_cli_write_raw("\r\n", 2); usb_cli_prompt(); } else if (ch == 0x7F || ch == '\b') { if (line_len > 0) { line_len--; + usb_cli_write_raw("\b \b", 3); } } else if (isprint((unsigned char)ch)) { if (line_len < CLI_LINE_MAX_LEN - 1) { line[line_len++] = ch; + usb_cli_write_raw(&ch, 1); } else { usb_cli_printf("\r\nРядок занадто довгий\r\n"); line_len = 0; @@ -386,3 +522,9 @@ esp_err_t usb_cdc_cli_init(void) ESP_LOGI(TAG, "USB CDC CLI ініціалізовано"); return ESP_OK; } +static void usb_cli_handle_reset(void) +{ + usb_cli_printf("\r\nПерезавантаження пристрою...\r\n"); + vTaskDelay(pdMS_TO_TICKS(100)); + esp_restart(); +} diff --git a/main/usb_cdc_log.c b/main/usb_cdc_log.c index 714b68e..1c6af23 100644 --- a/main/usb_cdc_log.c +++ b/main/usb_cdc_log.c @@ -1,6 +1,5 @@ #include "usb_cdc_log.h" -#include #include #include "esp_check.h" @@ -13,28 +12,6 @@ static const char *TAG = "usb_log"; static bool s_initialized; static bool s_host_ready; -static int usb_cdc_vprintf(const char *fmt, va_list args) -{ - if (s_host_ready) { - char buffer[256]; - va_list args_copy; - va_copy(args_copy, args); - int len = vsnprintf(buffer, sizeof(buffer), fmt, args_copy); - va_end(args_copy); - if (len > 0) { - if (len >= (int)sizeof(buffer)) { - len = sizeof(buffer) - 1; - } - if (tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (const uint8_t *)buffer, len) == ESP_OK) { - tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, 0); - } - } - return len; - } - - return 0; -} - static void usb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) { (void)itf; @@ -83,8 +60,7 @@ esp_err_t usb_cdc_log_init(void) }; ESP_RETURN_ON_ERROR(tusb_cdc_acm_init(&acm_cfg), TAG, "CDC init failed"); - esp_log_set_vprintf(usb_cdc_vprintf); s_initialized = true; - ESP_LOGI(TAG, "ESP_LOG перенаправлено в USB CDC"); + ESP_LOGI(TAG, "USB CDC лог ініціалізовано (перенаправлення ESP_LOG вимкнено)"); return ESP_OK; } diff --git a/main/watch_config.c b/main/watch_config.c new file mode 100644 index 0000000..a3d9838 --- /dev/null +++ b/main/watch_config.c @@ -0,0 +1,141 @@ +#include "watch_config.h" + +#include + +#include "esp_check.h" +#include "esp_log.h" +#include "nvs.h" +#include "nvs_flash.h" + +#define WATCH_CONFIG_NAMESPACE "watchcfg" +#define KEY_HB_PERIOD "hb_period" +#define KEY_DCDC_OFF "dcdc_off" +#define KEY_HB_START "hb_start" +#define KEY_HB_MON "hb_mon" +#define KEY_HB_MISS "hb_miss" + +#define DEFAULT_HB_PERIOD_SEC 4U +#define DEFAULT_DCDC_OFF_SEC 2U +#define DEFAULT_HB_START_SEC 2U +#define DEFAULT_HB_MISS_LIMIT 3U + +static watch_config_t s_config = { + .heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC, + .dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC, + .heartbeat_start_delay_sec = DEFAULT_HB_START_SEC, + .heartbeat_monitor_enabled = true, + .heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT, +}; +static const char *TAG = "watch_cfg"; +static bool s_initialized; + +static void watch_config_apply_defaults(void) +{ + s_config.heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC; + s_config.dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC; + s_config.heartbeat_start_delay_sec = DEFAULT_HB_START_SEC; + s_config.heartbeat_monitor_enabled = true; + s_config.heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT; +} + +static void watch_config_clamp(watch_config_t *cfg) +{ + if (!cfg->heartbeat_period_sec) { + cfg->heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC; + } + if (!cfg->dcdc_restart_off_sec) { + cfg->dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC; + } + if (!cfg->heartbeat_start_delay_sec) { + cfg->heartbeat_start_delay_sec = DEFAULT_HB_START_SEC; + } + cfg->heartbeat_monitor_enabled = cfg->heartbeat_monitor_enabled ? true : false; + if (!cfg->heartbeat_miss_limit) { + cfg->heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT; + } +} + +esp_err_t watch_config_init(void) +{ + if (s_initialized) { + return ESP_OK; + } + + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_RETURN_ON_ERROR(nvs_flash_erase(), TAG, "NVS erase failed"); + err = nvs_flash_init(); + } + ESP_RETURN_ON_ERROR(err, TAG, "NVS init failed"); + + watch_config_apply_defaults(); + + nvs_handle_t handle = 0; + err = nvs_open(WATCH_CONFIG_NAMESPACE, NVS_READWRITE, &handle); + ESP_RETURN_ON_ERROR(err, TAG, "NVS open failed"); + + uint32_t value = 0; + if (nvs_get_u32(handle, KEY_HB_PERIOD, &value) == ESP_OK && value > 0) { + s_config.heartbeat_period_sec = value; + } + if (nvs_get_u32(handle, KEY_DCDC_OFF, &value) == ESP_OK && value > 0) { + s_config.dcdc_restart_off_sec = value; + } + if (nvs_get_u32(handle, KEY_HB_START, &value) == ESP_OK && value > 0) { + s_config.heartbeat_start_delay_sec = value; + } + uint8_t hb_mon = 1; + if (nvs_get_u8(handle, KEY_HB_MON, &hb_mon) == ESP_OK) { + s_config.heartbeat_monitor_enabled = (hb_mon != 0); + } + if (nvs_get_u32(handle, KEY_HB_MISS, &value) == ESP_OK && value > 0) { + s_config.heartbeat_miss_limit = value; + } + + nvs_close(handle); + s_initialized = true; + return ESP_OK; +} + +const watch_config_t *watch_config_get(void) +{ + return &s_config; +} + +esp_err_t watch_config_save(const watch_config_t *cfg) +{ + if (!cfg) { + return ESP_ERR_INVALID_ARG; + } + + watch_config_t tmp = *cfg; + watch_config_clamp(&tmp); + + nvs_handle_t handle = 0; + ESP_RETURN_ON_ERROR(nvs_open(WATCH_CONFIG_NAMESPACE, NVS_READWRITE, &handle), + TAG, "NVS open failed"); + + esp_err_t err = nvs_set_u32(handle, KEY_HB_PERIOD, tmp.heartbeat_period_sec); + if (err == ESP_OK) { + err = nvs_set_u32(handle, KEY_DCDC_OFF, tmp.dcdc_restart_off_sec); + } + if (err == ESP_OK) { + err = nvs_set_u32(handle, KEY_HB_START, tmp.heartbeat_start_delay_sec); + } + if (err == ESP_OK) { + err = nvs_set_u8(handle, KEY_HB_MON, tmp.heartbeat_monitor_enabled ? 1 : 0); + } + if (err == ESP_OK) { + err = nvs_set_u32(handle, KEY_HB_MISS, tmp.heartbeat_miss_limit); + } + if (err == ESP_OK) { + err = nvs_commit(handle); + } + nvs_close(handle); + if (err != ESP_OK) { + return err; + } + + s_config = tmp; + return ESP_OK; +} diff --git a/main/watch_config.h b/main/watch_config.h new file mode 100644 index 0000000..0618b1b --- /dev/null +++ b/main/watch_config.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "esp_err.h" + +typedef struct { + uint32_t heartbeat_period_sec; + uint32_t dcdc_restart_off_sec; + uint32_t heartbeat_start_delay_sec; + bool heartbeat_monitor_enabled; + uint32_t heartbeat_miss_limit; +} watch_config_t; + +esp_err_t watch_config_init(void); +const watch_config_t *watch_config_get(void); +esp_err_t watch_config_save(const watch_config_t *cfg); diff --git a/main/ws2812_status.c b/main/ws2812_status.c index 066992e..fa97c5b 100644 --- a/main/ws2812_status.c +++ b/main/ws2812_status.c @@ -22,22 +22,142 @@ #define WS2812_STATUS_GPIO ((gpio_num_t)CONFIG_WATCH_WS2812_GPIO) #define WS2812_STATUS_RESOLUTION_HZ CONFIG_WATCH_WS2812_RMT_RESOLUTION -#define WS2812_POLL_BLINK_PERIOD pdMS_TO_TICKS(250) +#define WS2812_ANIM_REFRESH pdMS_TO_TICKS(100) +#define WS2812_ALERT_BLINK_PERIOD pdMS_TO_TICKS(200) +#define WS2812_ALERT_GAP_PERIOD pdMS_TO_TICKS(2000) +#define WS2812_VPN_ALERT_BLINKS 2 +#define WS2812_APP_ALERT_BLINKS 3 + +#define WS2812_VPN_SECTION_TICKS (WS2812_ALERT_BLINK_PERIOD * 2U * WS2812_VPN_ALERT_BLINKS) +#define WS2812_APP_SECTION_TICKS (WS2812_ALERT_BLINK_PERIOD * 2U * WS2812_APP_ALERT_BLINKS) static const char *TAG = "ws2812"; static led_strip_handle_t s_strip; -static bool s_led_state[WS2812_STATUS_LED_COUNT]; -static bool s_ack_state[WS2812_STATUS_LED_COUNT]; -static bool s_error_state; -static size_t s_active_channel = SIZE_MAX; -static bool s_polling_active[WS2812_STATUS_LED_COUNT]; -static bool s_polling_visible[WS2812_STATUS_LED_COUNT]; -static TickType_t s_polling_expire_tick[WS2812_STATUS_LED_COUNT]; -static TimerHandle_t s_polling_timers[WS2812_STATUS_LED_COUNT]; +static bool s_channel_enabled[WS2812_STATUS_LED_COUNT]; +static bool s_vpn_state[WS2812_STATUS_LED_COUNT]; +static bool s_app_state[WS2812_STATUS_LED_COUNT]; static SemaphoreHandle_t s_ws_lock; +static TimerHandle_t s_animation_timer; +static bool s_error_state; +static bool s_startup_hold; +static TickType_t s_startup_expire_tick; +static TickType_t s_alert_cycle_epoch; +static size_t s_active_vpn_alerts; +static size_t s_active_app_alerts; -static void ws2812_polling_timer_cb(TimerHandle_t timer); +static esp_err_t ws2812_status_apply(void); +static void ws2812_animation_timer_cb(TimerHandle_t timer); +static void ws2812_status_compute_color(size_t index, + TickType_t now_ticks, + uint8_t *r, + uint8_t *g, + uint8_t *b); +static bool ws2812_recalculate_alert_counts(void); + +static void ws2812_animation_timer_cb(TimerHandle_t timer) +{ + (void)timer; + ws2812_status_apply(); +} + +static bool ws2812_startup_hold_active(TickType_t now_ticks) +{ + if (!s_startup_hold) { + return false; + } + if (now_ticks >= s_startup_expire_tick) { + s_startup_hold = false; + return false; + } + return true; +} + +static void ws2812_status_compute_color(size_t index, + TickType_t now_ticks, + uint8_t *r, + uint8_t *g, + uint8_t *b) +{ + if (!r || !g || !b || index >= WS2812_STATUS_LED_COUNT) { + return; + } + + *r = 0; + *g = 0; + *b = 0; + + if (s_error_state) { + *r = 40; + return; + } + + if (ws2812_startup_hold_active(now_ticks)) { + *g = 40; + return; + } + + if (!s_channel_enabled[index]) { + return; + } + + const bool vpn_alert = !s_vpn_state[index]; + const bool app_alert = !s_app_state[index]; + if (!vpn_alert && !app_alert) { + return; + } + + const bool vpn_global = (s_active_vpn_alerts > 0); + const bool app_global = (s_active_app_alerts > 0); + + TickType_t cycle_ticks = 0; + if (vpn_global) { + cycle_ticks += WS2812_VPN_SECTION_TICKS + WS2812_ALERT_GAP_PERIOD; + } + if (app_global) { + cycle_ticks += WS2812_APP_SECTION_TICKS + WS2812_ALERT_GAP_PERIOD; + } + if (cycle_ticks == 0) { + return; + } + + TickType_t blink_period = WS2812_ALERT_BLINK_PERIOD ? WS2812_ALERT_BLINK_PERIOD : 1; + TickType_t cycle_pos = (now_ticks - s_alert_cycle_epoch) % cycle_ticks; + + if (vpn_global) { + if (cycle_pos < WS2812_VPN_SECTION_TICKS) { + if (vpn_alert) { + const bool on = ((cycle_pos / blink_period) % 2U) == 0U; + if (on) { + *r = 90; + } + } + return; + } + cycle_pos -= WS2812_VPN_SECTION_TICKS; + if (cycle_pos < WS2812_ALERT_GAP_PERIOD) { + return; + } + cycle_pos -= WS2812_ALERT_GAP_PERIOD; + } + + if (app_global) { + if (cycle_pos < WS2812_APP_SECTION_TICKS) { + if (app_alert) { + const bool on = ((cycle_pos / blink_period) % 2U) == 0U; + if (on) { + *r = 80; + *g = 80; + } + } + return; + } + cycle_pos -= WS2812_APP_SECTION_TICKS; + if (cycle_pos < WS2812_ALERT_GAP_PERIOD) { + return; + } + } +} static esp_err_t ws2812_status_apply(void) { @@ -47,32 +167,15 @@ static esp_err_t ws2812_status_apply(void) if (!s_ws_lock) { return ESP_ERR_INVALID_STATE; } - if (xSemaphoreTake(s_ws_lock, portMAX_DELAY) != pdTRUE) { return ESP_ERR_TIMEOUT; } + const TickType_t now_ticks = xTaskGetTickCount(); esp_err_t err = ESP_OK; for (size_t i = 0; i < WS2812_STATUS_LED_COUNT && err == ESP_OK; ++i) { uint8_t r = 0, g = 0, b = 0; - if (s_error_state) { - r = 40; - } else if (!s_led_state[i]) { - b = 5; - } else if (s_ack_state[i]) { - g = (s_active_channel == i) ? 60 : 25; - } else { - b = (s_active_channel == i) ? 60 : 25; - } - - if (s_polling_active[i] && !s_error_state && s_led_state[i]) { - if (!s_polling_visible[i]) { - r /= 4; - g /= 4; - b /= 4; - } - } - // Swap R/G to match physical RGB order (driver outputs GRB) + ws2812_status_compute_color(i, now_ticks, &r, &g, &b); err = led_strip_set_pixel(s_strip, i, g, r, b); } if (err == ESP_OK) { @@ -95,7 +198,6 @@ esp_err_t ws2812_status_init(void) .led_model = LED_MODEL_WS2812, .flags.invert_out = false, }; - led_strip_rmt_config_t rmt_cfg = { .clk_src = RMT_CLK_SRC_DEFAULT, .resolution_hz = WS2812_STATUS_RESOLUTION_HZ, @@ -111,204 +213,142 @@ esp_err_t ws2812_status_init(void) s_ws_lock = xSemaphoreCreateMutex(); ESP_RETURN_ON_FALSE(s_ws_lock, ESP_ERR_NO_MEM, TAG, "mutex alloc failed"); + const TickType_t now = xTaskGetTickCount(); for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) { - s_led_state[i] = false; - s_ack_state[i] = false; - s_polling_active[i] = false; - s_polling_visible[i] = false; - s_polling_expire_tick[i] = 0; - s_polling_timers[i] = NULL; + s_channel_enabled[i] = false; + s_vpn_state[i] = true; + s_app_state[i] = true; } - s_active_channel = SIZE_MAX; s_error_state = false; + s_startup_hold = false; + s_startup_expire_tick = 0; + s_alert_cycle_epoch = now; + (void)ws2812_recalculate_alert_counts(); + if (!s_animation_timer) { + s_animation_timer = xTimerCreate("ws2812_anim", + WS2812_ANIM_REFRESH, + pdTRUE, + NULL, + ws2812_animation_timer_cb); + ESP_RETURN_ON_FALSE(s_animation_timer, ESP_ERR_NO_MEM, TAG, "anim timer alloc failed"); + ESP_RETURN_ON_FALSE(xTimerStart(s_animation_timer, 0) == pdPASS, + ESP_FAIL, + TAG, + "anim timer start failed"); + } + + (void)ws2812_recalculate_alert_counts(); return ws2812_status_apply(); } -static void ws2812_polling_timer_cb(TimerHandle_t timer) -{ - size_t channel = (size_t)pvTimerGetTimerID(timer); - if (channel >= WS2812_STATUS_LED_COUNT) { - return; - } - TickType_t now = xTaskGetTickCount(); - if (now >= s_polling_expire_tick[channel]) { - s_polling_active[channel] = false; - s_polling_visible[channel] = false; - s_polling_expire_tick[channel] = 0; - xTimerStop(timer, 0); - } else { - s_polling_visible[channel] = !s_polling_visible[channel]; - } - ws2812_status_apply(); -} - -esp_err_t ws2812_status_indicate_polling(size_t channel, uint32_t duration_ms) -{ - if (channel >= WS2812_STATUS_LED_COUNT) { - return ESP_ERR_INVALID_ARG; - } - if (!s_strip) { - return ESP_ERR_INVALID_STATE; - } - if (duration_ms == 0) { - duration_ms = 2000; - } - - TickType_t duration_ticks = pdMS_TO_TICKS(duration_ms); - if (duration_ticks == 0) { - duration_ticks = 1; - } - - if (!s_polling_timers[channel]) { - s_polling_timers[channel] = xTimerCreate("ws2812_poll", - WS2812_POLL_BLINK_PERIOD, - pdTRUE, - (void *)channel, - ws2812_polling_timer_cb); - if (!s_polling_timers[channel]) { - return ESP_ERR_NO_MEM; - } - } - - s_polling_active[channel] = true; - s_polling_visible[channel] = true; - s_polling_expire_tick[channel] = xTaskGetTickCount() + duration_ticks; - esp_err_t err = ws2812_status_apply(); - if (err != ESP_OK) { - return err; - } - - BaseType_t res; - if (xTimerIsTimerActive(s_polling_timers[channel]) == pdTRUE) { - res = xTimerReset(s_polling_timers[channel], 0); - } else { - res = xTimerStart(s_polling_timers[channel], 0); - } - return (res == pdPASS) ? ESP_OK : ESP_FAIL; -} - esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled) { if (channel >= WS2812_STATUS_LED_COUNT) { return ESP_ERR_INVALID_ARG; } - s_led_state[channel] = enabled; + if (!s_strip) { + return ESP_ERR_INVALID_STATE; + } + s_channel_enabled[channel] = enabled; if (!enabled) { - s_ack_state[channel] = false; + s_vpn_state[channel] = true; + s_app_state[channel] = true; } return ws2812_status_apply(); } -esp_err_t ws2812_status_mark_active(size_t channel) -{ - if (channel >= WS2812_STATUS_LED_COUNT) { - return ESP_ERR_INVALID_ARG; - } - s_active_channel = channel; - return ws2812_status_apply(); -} - -esp_err_t ws2812_status_clear_active(void) -{ - s_active_channel = SIZE_MAX; - return ws2812_status_apply(); -} - esp_err_t ws2812_status_set_error(bool has_error) { - s_error_state = has_error; - if (has_error) { - s_active_channel = SIZE_MAX; + if (!s_strip) { + return ESP_ERR_INVALID_STATE; } + s_error_state = has_error; return ws2812_status_apply(); } -esp_err_t ws2812_status_set_ack_state(size_t channel, bool received) +esp_err_t ws2812_status_set_service_state(size_t channel, bool vpn_ok, bool app_ok) { if (channel >= WS2812_STATUS_LED_COUNT) { return ESP_ERR_INVALID_ARG; } - s_ack_state[channel] = received; + if (!s_strip) { + return ESP_ERR_INVALID_STATE; + } + bool changed = false; + if (s_vpn_state[channel] != vpn_ok) { + s_vpn_state[channel] = vpn_ok; + changed = true; + } + if (s_app_state[channel] != app_ok) { + s_app_state[channel] = app_ok; + changed = true; + } + if (changed) { + (void)ws2812_recalculate_alert_counts(); + s_alert_cycle_epoch = xTaskGetTickCount(); + } return ws2812_status_apply(); } esp_err_t ws2812_status_refresh_from_dcdc(void) { + if (!s_strip) { + return ESP_ERR_INVALID_STATE; + } const size_t available_channels = dcdc_channel_count(); const size_t count = available_channels < WS2812_STATUS_LED_COUNT ? available_channels : WS2812_STATUS_LED_COUNT; - for (size_t i = 0; i < count; ++i) { - bool enabled = dcdc_get_state(i); - s_led_state[i] = enabled; - if (!enabled) { - s_ack_state[i] = false; - } + s_channel_enabled[i] = dcdc_get_state(i); } for (size_t i = count; i < WS2812_STATUS_LED_COUNT; ++i) { - s_led_state[i] = false; - s_ack_state[i] = false; + s_channel_enabled[i] = false; + s_vpn_state[i] = true; + s_app_state[i] = true; } + (void)ws2812_recalculate_alert_counts(); + s_alert_cycle_epoch = xTaskGetTickCount(); return ws2812_status_apply(); } -esp_err_t ws2812_status_play_bringup_animation(size_t cycles, uint32_t step_delay_ms) +esp_err_t ws2812_status_set_startup_hold(uint32_t duration_ms) { if (!s_strip) { return ESP_ERR_INVALID_STATE; } - - if (cycles == 0) { - cycles = 1; - } - if (step_delay_ms == 0) { - step_delay_ms = 150; + if (duration_ms == 0) { + s_startup_hold = false; + s_startup_expire_tick = 0; + return ws2812_status_apply(); } - bool saved_led_state[WS2812_STATUS_LED_COUNT]; + TickType_t duration_ticks = pdMS_TO_TICKS(duration_ms); + if (duration_ticks == 0) { + duration_ticks = 1; + } + s_startup_hold = true; + s_startup_expire_tick = xTaskGetTickCount() + duration_ticks; + return ws2812_status_apply(); +} +static bool ws2812_recalculate_alert_counts(void) +{ + size_t vpn = 0; + size_t app = 0; for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) { - saved_led_state[i] = s_led_state[i]; - } - const bool saved_error = s_error_state; - const size_t saved_active = s_active_channel; - - s_error_state = false; - s_active_channel = SIZE_MAX; - - const TickType_t delay_ticks = pdMS_TO_TICKS(step_delay_ms); - esp_err_t err = ESP_OK; - - for (size_t cycle = 0; cycle < cycles && err == ESP_OK; ++cycle) { - for (size_t led = 0; led < WS2812_STATUS_LED_COUNT; ++led) { - for (size_t idx = 0; idx < WS2812_STATUS_LED_COUNT; ++idx) { - const uint8_t g = (idx == led) ? 35 : 0; - err = led_strip_set_pixel(s_strip, idx, 0, g, 0); - if (err != ESP_OK) { - break; - } - } - if (err != ESP_OK) { - break; - } - err = led_strip_refresh(s_strip); - if (err != ESP_OK) { - break; - } - vTaskDelay(delay_ticks); + if (!s_channel_enabled[i]) { + continue; + } + if (!s_vpn_state[i]) { + ++vpn; + } + if (!s_app_state[i]) { + ++app; } } - - for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) { - s_led_state[i] = saved_led_state[i]; - } - s_error_state = saved_error; - s_active_channel = saved_active; - - esp_err_t restore_err = ws2812_status_apply(); - if (err != ESP_OK) { - return err; - } - return restore_err; + const bool changed = (vpn != s_active_vpn_alerts) || (app != s_active_app_alerts); + s_active_vpn_alerts = vpn; + s_active_app_alerts = app; + return changed; } diff --git a/main/ws2812_status.h b/main/ws2812_status.h index fb467e9..f6b42ce 100644 --- a/main/ws2812_status.h +++ b/main/ws2812_status.h @@ -15,10 +15,7 @@ esp_err_t ws2812_status_init(void); esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled); -esp_err_t ws2812_status_mark_active(size_t channel); -esp_err_t ws2812_status_clear_active(void); esp_err_t ws2812_status_set_error(bool has_error); -esp_err_t ws2812_status_set_ack_state(size_t channel, bool received); -esp_err_t ws2812_status_indicate_polling(size_t channel, uint32_t duration_ms); +esp_err_t ws2812_status_set_service_state(size_t channel, bool vpn_ok, bool app_ok); +esp_err_t ws2812_status_set_startup_hold(uint32_t duration_ms); esp_err_t ws2812_status_refresh_from_dcdc(void); -esp_err_t ws2812_status_play_bringup_animation(size_t cycles, uint32_t step_delay_ms);