Compare commits
23 Commits
542ff578bc
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| b0f8bcc9da | |||
| 103859120c | |||
| 403517fdb4 | |||
| 11eb8713bf | |||
| d903c2bf52 | |||
| f8f76674ce | |||
| 3cc5a0d2bd | |||
| 3be585ecbf | |||
| 5b1a945ac3 | |||
| f4e050d413 | |||
| 776db785c4 | |||
|
|
ef0ec47d92 | ||
| 0ed9d6bb57 | |||
| 4771a77686 | |||
| eef165b6de | |||
| 1ace878488 | |||
| a30d59605d | |||
| 2ee3615dcd | |||
| db0f7daa4c | |||
| 6809a37d9d | |||
| 0ca09ed498 | |||
| 07aed02fe7 | |||
| aef0ffd5a1 |
@@ -15,6 +15,7 @@
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"prototype_zone_fills": false,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
@@ -53,6 +54,7 @@
|
||||
"zone_display_mode": 1
|
||||
},
|
||||
"git": {
|
||||
"integration_disabled": false,
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
"ssh_key": ""
|
||||
@@ -105,6 +107,7 @@
|
||||
"filter_text": "",
|
||||
"group_by_constraint": false,
|
||||
"group_by_netclass": false,
|
||||
"show_time_domain_details": false,
|
||||
"show_unconnected_nets": false,
|
||||
"show_zero_pad_nets": false,
|
||||
"sort_ascending": true,
|
||||
@@ -115,6 +118,7 @@
|
||||
"files": []
|
||||
},
|
||||
"schematic": {
|
||||
"hierarchy_collapsed": [],
|
||||
"selection_filter": {
|
||||
"graphics": true,
|
||||
"images": true,
|
||||
@@ -122,6 +126,7 @@
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pins": true,
|
||||
"ruleAreas": true,
|
||||
"symbols": true,
|
||||
"text": true,
|
||||
"wires": true
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_barcodes": false,
|
||||
"apply_defaults_to_fp_dimensions": false,
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
@@ -82,6 +84,7 @@
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_field_mismatch": "warning",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "ignore",
|
||||
"hole_clearance": "error",
|
||||
@@ -99,6 +102,7 @@
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"missing_tuning_profile": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
@@ -118,9 +122,12 @@
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_not_centered_on_via": "ignore",
|
||||
"track_on_post_machined_layer": "error",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"tuning_profile_track_geometries": "ignore",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
@@ -235,17 +242,28 @@
|
||||
"zones_allow_external_fillets": false
|
||||
},
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
"mpn": "",
|
||||
"sch_revision": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
@@ -494,13 +512,14 @@
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
@@ -683,6 +702,7 @@
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
@@ -721,7 +741,14 @@
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "MPPT.kicad_sch",
|
||||
"name": "MPPT",
|
||||
"uuid": "00000000-0000-0000-0000-000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
@@ -729,5 +756,11 @@
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"prototype_zone_fills": false,
|
||||
"ratsnest_display_mode": 0,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
@@ -54,6 +55,7 @@
|
||||
"zone_display_mode": 1
|
||||
},
|
||||
"git": {
|
||||
"integration_disabled": false,
|
||||
"repo_password": "",
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
@@ -113,6 +115,7 @@
|
||||
"filter_text": "",
|
||||
"group_by_constraint": false,
|
||||
"group_by_netclass": false,
|
||||
"show_time_domain_details": false,
|
||||
"show_unconnected_nets": false,
|
||||
"show_zero_pad_nets": false,
|
||||
"sort_ascending": true,
|
||||
@@ -123,6 +126,7 @@
|
||||
"files": []
|
||||
},
|
||||
"schematic": {
|
||||
"hierarchy_collapsed": [],
|
||||
"selection_filter": {
|
||||
"graphics": true,
|
||||
"images": true,
|
||||
@@ -130,6 +134,7 @@
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pins": true,
|
||||
"ruleAreas": true,
|
||||
"symbols": true,
|
||||
"text": true,
|
||||
"wires": true
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_barcodes": false,
|
||||
"apply_defaults_to_fp_dimensions": false,
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
@@ -78,6 +80,7 @@
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_field_mismatch": "warning",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "warning",
|
||||
"hole_clearance": "error",
|
||||
@@ -96,6 +99,7 @@
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"missing_tuning_profile": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
@@ -115,9 +119,12 @@
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_not_centered_on_via": "ignore",
|
||||
"track_on_post_machined_layer": "error",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"tuning_profile_track_geometries": "ignore",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
@@ -242,17 +249,28 @@
|
||||
"zones_use_no_outline": true
|
||||
},
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
"mpn": "",
|
||||
"sch_revision": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
@@ -443,11 +461,14 @@
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "ignore",
|
||||
"extra_units": "error",
|
||||
"field_name_whitespace": "warning",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"global_label_dangling": "warning",
|
||||
"ground_pin_not_ground": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"isolated_pin_label": "warning",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
@@ -470,6 +491,7 @@
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "warning",
|
||||
"stacked_pin_name": "warning",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"undefined_netclass": "error",
|
||||
@@ -502,6 +524,7 @@
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 1.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 6
|
||||
@@ -520,6 +543,7 @@
|
||||
"priority": 0,
|
||||
"schematic_color": "rgb(255, 4, 6)",
|
||||
"track_width": 1.0,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 12
|
||||
@@ -538,6 +562,7 @@
|
||||
"priority": 1,
|
||||
"schematic_color": "rgb(255, 153, 0)",
|
||||
"track_width": 0.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 12
|
||||
@@ -556,6 +581,7 @@
|
||||
"priority": 2,
|
||||
"schematic_color": "rgb(81, 255, 3)",
|
||||
"track_width": 1.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 12
|
||||
@@ -574,6 +600,7 @@
|
||||
"priority": 3,
|
||||
"schematic_color": "rgb(130, 130, 130)",
|
||||
"track_width": 1.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 12
|
||||
@@ -592,13 +619,14 @@
|
||||
"priority": 4,
|
||||
"schematic_color": "rgb(0, 0, 0)",
|
||||
"track_width": 0.5,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 12
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
@@ -1077,6 +1105,10 @@
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"annotation": {
|
||||
"method": 0,
|
||||
"sort_order": 0
|
||||
},
|
||||
"bom_export_filename": "PlantCtrlESP32.csv",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
@@ -1256,6 +1288,7 @@
|
||||
"sort_asc": true,
|
||||
"sort_field": "LCSC_PART_NUMBER"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
@@ -1263,6 +1296,7 @@
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"hop_over_size_choice": 0,
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
@@ -1295,6 +1329,7 @@
|
||||
},
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "/tmp/",
|
||||
"reuse_designators": true,
|
||||
"space_save_all_events": true,
|
||||
"spice_adjust_passive_values": false,
|
||||
"spice_current_sheet_as_root": false,
|
||||
@@ -1304,7 +1339,16 @@
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "PlantCtrlESP32.kicad_sch",
|
||||
"name": "PlantCtrlESP32",
|
||||
"uuid": "00000000-0000-0000-0000-000000000000"
|
||||
}
|
||||
],
|
||||
"used_designators": "",
|
||||
"variants": []
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
@@ -1312,5 +1356,11 @@
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"prototype_zone_fills": false,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
@@ -53,6 +54,7 @@
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
"integration_disabled": false,
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
"ssh_key": ""
|
||||
@@ -105,6 +107,7 @@
|
||||
"filter_text": "",
|
||||
"group_by_constraint": false,
|
||||
"group_by_netclass": false,
|
||||
"show_time_domain_details": false,
|
||||
"show_unconnected_nets": false,
|
||||
"show_zero_pad_nets": false,
|
||||
"sort_ascending": true,
|
||||
@@ -115,6 +118,7 @@
|
||||
"files": []
|
||||
},
|
||||
"schematic": {
|
||||
"hierarchy_collapsed": [],
|
||||
"selection_filter": {
|
||||
"graphics": true,
|
||||
"images": true,
|
||||
@@ -122,6 +126,7 @@
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pins": true,
|
||||
"ruleAreas": true,
|
||||
"symbols": true,
|
||||
"text": true,
|
||||
"wires": true
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_barcodes": false,
|
||||
"apply_defaults_to_fp_dimensions": false,
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
@@ -82,6 +84,7 @@
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_field_mismatch": "warning",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "warning",
|
||||
"hole_clearance": "error",
|
||||
@@ -99,6 +102,7 @@
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"missing_tuning_profile": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
@@ -118,9 +122,12 @@
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_not_centered_on_via": "ignore",
|
||||
"track_on_post_machined_layer": "error",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"tuning_profile_track_geometries": "ignore",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
@@ -236,17 +243,28 @@
|
||||
"zones_allow_external_fillets": false
|
||||
},
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
"mpn": "",
|
||||
"sch_revision": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
@@ -495,13 +513,14 @@
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
@@ -629,6 +648,7 @@
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
@@ -667,7 +687,14 @@
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "PumpOutput.kicad_sch",
|
||||
"name": "PumpOutput",
|
||||
"uuid": "00000000-0000-0000-0000-000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
@@ -675,5 +702,11 @@
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"prototype_zone_fills": false,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
@@ -54,6 +55,7 @@
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
"integration_disabled": false,
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
"ssh_key": ""
|
||||
@@ -106,6 +108,7 @@
|
||||
"filter_text": "",
|
||||
"group_by_constraint": false,
|
||||
"group_by_netclass": false,
|
||||
"show_time_domain_details": false,
|
||||
"show_unconnected_nets": false,
|
||||
"show_zero_pad_nets": false,
|
||||
"sort_ascending": true,
|
||||
@@ -116,6 +119,7 @@
|
||||
"files": []
|
||||
},
|
||||
"schematic": {
|
||||
"hierarchy_collapsed": [],
|
||||
"selection_filter": {
|
||||
"graphics": true,
|
||||
"images": true,
|
||||
@@ -123,6 +127,7 @@
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pins": true,
|
||||
"ruleAreas": true,
|
||||
"symbols": true,
|
||||
"text": true,
|
||||
"wires": true
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_barcodes": false,
|
||||
"apply_defaults_to_fp_dimensions": false,
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
@@ -77,6 +79,7 @@
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "warning",
|
||||
"footprint_symbol_field_mismatch": "warning",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "warning",
|
||||
"hole_clearance": "error",
|
||||
@@ -94,6 +97,7 @@
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "warning",
|
||||
"missing_footprint": "warning",
|
||||
"missing_tuning_profile": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "warning",
|
||||
@@ -113,9 +117,12 @@
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_not_centered_on_via": "ignore",
|
||||
"track_on_post_machined_layer": "error",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"tuning_profile_track_geometries": "ignore",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
@@ -227,17 +234,28 @@
|
||||
"zones_allow_external_fillets": false
|
||||
},
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
"mpn": "",
|
||||
"sch_revision": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
@@ -486,13 +504,14 @@
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
@@ -861,6 +880,7 @@
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
@@ -899,7 +919,14 @@
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "sensor.kicad_sch",
|
||||
"name": "sensor",
|
||||
"uuid": "00000000-0000-0000-0000-000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
@@ -907,5 +934,11 @@
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"board": {
|
||||
"active_layer": 0,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": true,
|
||||
"auto_track_width": false,
|
||||
"hidden_netclasses": [],
|
||||
"hidden_nets": [],
|
||||
"high_contrast_mode": 0,
|
||||
@@ -15,6 +15,7 @@
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"prototype_zone_fills": false,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
@@ -54,6 +55,7 @@
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
"integration_disabled": false,
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
"ssh_key": ""
|
||||
@@ -63,8 +65,30 @@
|
||||
"version": 5
|
||||
},
|
||||
"net_inspector_panel": {
|
||||
"col_hidden": [],
|
||||
"col_order": [],
|
||||
"col_hidden": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"col_order": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
],
|
||||
"col_widths": [],
|
||||
"custom_group_rules": [],
|
||||
"expanded_rows": [],
|
||||
@@ -73,6 +97,7 @@
|
||||
"filter_text": "",
|
||||
"group_by_constraint": false,
|
||||
"group_by_netclass": false,
|
||||
"show_time_domain_details": false,
|
||||
"show_unconnected_nets": false,
|
||||
"show_zero_pad_nets": false,
|
||||
"sort_ascending": true,
|
||||
@@ -83,6 +108,7 @@
|
||||
"files": []
|
||||
},
|
||||
"schematic": {
|
||||
"hierarchy_collapsed": [],
|
||||
"selection_filter": {
|
||||
"graphics": true,
|
||||
"images": true,
|
||||
@@ -90,6 +116,7 @@
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pins": true,
|
||||
"ruleAreas": true,
|
||||
"symbols": true,
|
||||
"text": true,
|
||||
"wires": true
|
||||
|
||||
@@ -2,25 +2,262 @@
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {},
|
||||
"diff_pair_dimensions": [],
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_barcodes": false,
|
||||
"apply_defaults_to_fp_dimensions": false,
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
"board_outline_line_width": 0.05,
|
||||
"copper_line_width": 0.2,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.05,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": true,
|
||||
"text_position": 0,
|
||||
"units_format": 0
|
||||
},
|
||||
"fab_line_width": 0.1,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.1,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 0.8,
|
||||
"height": 1.27,
|
||||
"width": 2.54
|
||||
},
|
||||
"silk_line_width": 0.1,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.1,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"min_clearance": 0.5
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [
|
||||
{
|
||||
"gap": 0.0,
|
||||
"via_gap": 0.0,
|
||||
"width": 0.0
|
||||
}
|
||||
],
|
||||
"drc_exclusions": [],
|
||||
"rules": {},
|
||||
"track_widths": [],
|
||||
"via_dimensions": []
|
||||
"meta": {
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"creepage": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_field_mismatch": "warning",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "ignore",
|
||||
"hole_clearance": "error",
|
||||
"hole_to_hole": "warning",
|
||||
"holes_co_located": "warning",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"missing_tuning_profile": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "error",
|
||||
"padstack": "warning",
|
||||
"pth_inside_courtyard": "error",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "ignore",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_on_edge_cuts": "error",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_not_centered_on_via": "ignore",
|
||||
"track_on_post_machined_layer": "error",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"tuning_profile_track_geometries": "ignore",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.0,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.5,
|
||||
"min_groove_width": 0.0,
|
||||
"min_hole_clearance": 0.25,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.2,
|
||||
"min_microvia_drill": 0.1,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.8,
|
||||
"min_text_thickness": 0.08,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.2,
|
||||
"min_via_annular_width": 0.1,
|
||||
"min_via_diameter": 0.5,
|
||||
"solder_mask_to_copper_clearance": 0.005,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_onpthpad": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_onsmdpad": true,
|
||||
"td_ontrackend": false,
|
||||
"td_onvia": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [
|
||||
0.0,
|
||||
0.2,
|
||||
0.5,
|
||||
1.0,
|
||||
2.0,
|
||||
5.0
|
||||
],
|
||||
"tuning_pattern_settings": {
|
||||
"diff_pair_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 1.0
|
||||
},
|
||||
"diff_pair_skew_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"single_track_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
}
|
||||
},
|
||||
"via_dimensions": [
|
||||
{
|
||||
"diameter": 0.0,
|
||||
"drill": 0.0
|
||||
}
|
||||
],
|
||||
"zones_allow_external_fillets": false
|
||||
},
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
"mpn": "",
|
||||
"sch_revision": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
@@ -210,11 +447,14 @@
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"field_name_whitespace": "warning",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"global_label_dangling": "warning",
|
||||
"ground_pin_not_ground": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"isolated_pin_label": "warning",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
@@ -237,6 +477,7 @@
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "ignore",
|
||||
"stacked_pin_name": "warning",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"undefined_netclass": "error",
|
||||
@@ -269,13 +510,14 @@
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
@@ -297,6 +539,10 @@
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"annotation": {
|
||||
"method": 0,
|
||||
"sort_order": 0
|
||||
},
|
||||
"bom_export_filename": "${PROJECTNAME}.csv",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
@@ -359,15 +605,298 @@
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Actuator/Cap Color",
|
||||
"name": "Actuator/Cap Color",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Attrition Qty",
|
||||
"name": "Attrition Qty",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Capacitance",
|
||||
"name": "Capacitance",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Category",
|
||||
"name": "Category",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Circuit",
|
||||
"name": "Circuit",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Class",
|
||||
"name": "Class",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Contact Current",
|
||||
"name": "Contact Current",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Diode Configuration",
|
||||
"name": "Diode Configuration",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Field5",
|
||||
"name": "Field5",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Forward Voltage (Vf@If)",
|
||||
"name": "Forward Voltage (Vf@If)",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Insulation Resistance",
|
||||
"name": "Insulation Resistance",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "LCSC",
|
||||
"name": "LCSC",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "LCSC_PART_NUMBER",
|
||||
"name": "LCSC_PART_NUMBER",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Manufacturer",
|
||||
"name": "Manufacturer",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Mechanical Life",
|
||||
"name": "Mechanical Life",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Minimum Qty",
|
||||
"name": "Minimum Qty",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Mounting Style",
|
||||
"name": "Mounting Style",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Operating Force",
|
||||
"name": "Operating Force",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Operating Temperature",
|
||||
"name": "Operating Temperature",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Operating Temperature Range",
|
||||
"name": "Operating Temperature Range",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Overload Voltage (Max)",
|
||||
"name": "Overload Voltage (Max)",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Part",
|
||||
"name": "Part",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Pin Style",
|
||||
"name": "Pin Style",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Power(Watts)",
|
||||
"name": "Power(Watts)",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Price",
|
||||
"name": "Price",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Process",
|
||||
"name": "Process",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Rectified Current",
|
||||
"name": "Rectified Current",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Resistance",
|
||||
"name": "Resistance",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reverse Leakage Current",
|
||||
"name": "Reverse Leakage Current",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reverse Voltage (Vr)",
|
||||
"name": "Reverse Voltage (Vr)",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Sim.Device",
|
||||
"name": "Sim.Device",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Sim.Pins",
|
||||
"name": "Sim.Pins",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Sim.Type",
|
||||
"name": "Sim.Type",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Stock",
|
||||
"name": "Stock",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Strike Gundam",
|
||||
"name": "Strike Gundam",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Switch Height",
|
||||
"name": "Switch Height",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Switch Length",
|
||||
"name": "Switch Length",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Switch Width",
|
||||
"name": "Switch Width",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Temperature Coefficient",
|
||||
"name": "Temperature Coefficient",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Tolerance",
|
||||
"name": "Tolerance",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Type",
|
||||
"name": "Type",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Voltage Rated",
|
||||
"name": "Voltage Rated",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Voltage Rating (Dc)",
|
||||
"name": "Voltage Rating (Dc)",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "With Lamp",
|
||||
"name": "With Lamp",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Actuator Style",
|
||||
"name": "Actuator Style",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Description",
|
||||
"name": "Description",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "#",
|
||||
"name": "${ITEM_NUMBER}",
|
||||
"show": false
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"include_excluded_from_bom": true,
|
||||
"name": "Default Editing",
|
||||
"name": "",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
@@ -375,6 +904,7 @@
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"hop_over_size_choice": 0,
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
@@ -398,6 +928,7 @@
|
||||
"net_format_name": "",
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"reuse_designators": true,
|
||||
"space_save_all_events": true,
|
||||
"spice_current_sheet_as_root": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
@@ -406,13 +937,28 @@
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "bms.kicad_sch",
|
||||
"name": "bms",
|
||||
"uuid": "7972d0e7-2611-420d-b298-ef8307db6186"
|
||||
}
|
||||
],
|
||||
"used_designators": "",
|
||||
"variants": []
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"7972d0e7-2611-420d-b298-ef8307db6186",
|
||||
"Root"
|
||||
"bms"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1
Hardware/open-bms/bms/fabrication-toolkit-options.json
Normal file
1
Hardware/open-bms/bms/fabrication-toolkit-options.json
Normal file
@@ -0,0 +1 @@
|
||||
{"ARCHIVE_NAME": "", "EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false, "OPEN BROWSER": true, "NO_BACKUP_OPT": false}
|
||||
@@ -0,0 +1,337 @@
|
||||
(footprint "AMASS_XT30UPB+DATA-M_1x02_P5.0mm_Vertical"
|
||||
(version 20240108)
|
||||
(generator "pcbnew")
|
||||
(generator_version "8.0")
|
||||
(layer "F.Cu")
|
||||
(descr "Connector XT30 Vertical PCB Male, https://www.tme.eu/en/Document/4acc913878197f8c2e30d4b8cdc47230/XT30UPB%20SPEC.pdf")
|
||||
(tags "RC Connector XT30")
|
||||
(property "Reference" "REF**"
|
||||
(at 2.5 -4 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "f7510d54-dcb1-4c3b-b842-cd250a98370c")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "AMASS_XT30UPB+DATA-M_1x02_P5.0mm_Vertical"
|
||||
(at 2.5 4 0)
|
||||
(layer "F.Fab")
|
||||
(uuid "c5a8a60c-4ea1-4401-a30c-34d36be61c07")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Footprint" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "8fb27306-c085-4316-b554-4ba9be794054")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "74b71861-05d2-4229-8e81-25952aaaef7e")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "a1d16ecc-7e64-48c4-b772-a9255380960d")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(attr through_hole)
|
||||
(fp_line
|
||||
(start -2.71 -1.41)
|
||||
(end -2.71 1.41)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "e96c6ad2-9ca6-4df1-b35b-76e090d7ff4e")
|
||||
)
|
||||
(fp_line
|
||||
(start -2.71 -1.41)
|
||||
(end -1.01 -2.71)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "0784d204-0a48-4a2b-8085-50e1ff7a1493")
|
||||
)
|
||||
(fp_line
|
||||
(start -2.71 1.41)
|
||||
(end -1.01 2.71)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "db750970-e424-4a8e-a882-20a90baabffc")
|
||||
)
|
||||
(fp_line
|
||||
(start -1.01 -2.71)
|
||||
(end 7.71 -2.71)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "9f23420e-db87-438c-a708-676a0616966e")
|
||||
)
|
||||
(fp_line
|
||||
(start -1.01 2.71)
|
||||
(end 7.71 2.71)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "2ba574e3-9de5-4d7d-9777-e19e6fa702e7")
|
||||
)
|
||||
(fp_line
|
||||
(start 7.71 -2.71)
|
||||
(end 7.71 2.71)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "2018cc6b-6115-4763-8b7c-54b60affbda7")
|
||||
)
|
||||
(fp_rect
|
||||
(start -6.3 -2.71)
|
||||
(end 7.71 2.7)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(fill none)
|
||||
(layer "F.SilkS")
|
||||
(uuid "11aac399-c862-4b67-9828-087abeea5b1b")
|
||||
)
|
||||
(fp_line
|
||||
(start -3.1 -1.8)
|
||||
(end -3.1 1.8)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "06ae69d8-1372-4524-8b1a-27a2f062f1c5")
|
||||
)
|
||||
(fp_line
|
||||
(start -3.1 -1.8)
|
||||
(end -1.4 -3.1)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "ae869a92-c688-4f2b-82ca-0578106a035a")
|
||||
)
|
||||
(fp_line
|
||||
(start -3.1 1.8)
|
||||
(end -1.4 3.1)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "cac6d927-6ba1-4095-825b-f94ee0d7abe9")
|
||||
)
|
||||
(fp_line
|
||||
(start -1.4 -3.1)
|
||||
(end 8.1 -3.1)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "fb4fa373-5492-4717-a9fe-7b69f4c53ba0")
|
||||
)
|
||||
(fp_line
|
||||
(start -1.4 3.1)
|
||||
(end 8.1 3.1)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "70296c77-546d-44a2-b5d3-e6dc58cf713b")
|
||||
)
|
||||
(fp_line
|
||||
(start 8.1 -3.1)
|
||||
(end 8.1 3.1)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "64764a09-de32-4f35-b54a-17e44810370f")
|
||||
)
|
||||
(fp_line
|
||||
(start -2.6 -1.3)
|
||||
(end -2.6 1.3)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "8d7ee7cb-5dda-453f-aa9a-6420c87f1b8e")
|
||||
)
|
||||
(fp_line
|
||||
(start -2.6 -1.3)
|
||||
(end -0.9 -2.6)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "2fa3ad90-36bb-4374-95ed-e44e50c7e385")
|
||||
)
|
||||
(fp_line
|
||||
(start -2.6 1.3)
|
||||
(end -0.9 2.6)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "2e4f8556-ffc2-4791-91da-e68c3513337e")
|
||||
)
|
||||
(fp_line
|
||||
(start -0.9 -2.6)
|
||||
(end 7.6 -2.6)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "cb9cd8af-1997-41db-b9fe-8982960ac6db")
|
||||
)
|
||||
(fp_line
|
||||
(start -0.9 2.6)
|
||||
(end 7.6 2.6)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "ea8a6c02-e974-4677-a854-a0891c323245")
|
||||
)
|
||||
(fp_line
|
||||
(start 7.6 -2.6)
|
||||
(end 7.6 2.6)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.Fab")
|
||||
(uuid "c66f305c-0d56-4591-bc02-23252ad20321")
|
||||
)
|
||||
(fp_text user "-"
|
||||
(at -4 0 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "c119570a-6846-48dc-9422-0b5665ab2df6")
|
||||
(effects
|
||||
(font
|
||||
(size 1.5 1.5)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fp_text user "+"
|
||||
(at 9 0 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "d6ab678c-47f7-47e0-869b-ef3b9dbd1ba9")
|
||||
(effects
|
||||
(font
|
||||
(size 1.5 1.5)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fp_text user "${REFERENCE}"
|
||||
(at 2.5 0 0)
|
||||
(layer "F.Fab")
|
||||
(uuid "a70efd12-1491-4664-ae98-5b2b7f52a502")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(pad "1" thru_hole rect
|
||||
(at 0 0)
|
||||
(size 3 3)
|
||||
(drill 1.8)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "3a0f3b23-814b-4df9-a02c-3d9fed9e23c9")
|
||||
)
|
||||
(pad "2" thru_hole circle
|
||||
(at 5 0)
|
||||
(size 3 3)
|
||||
(drill 1.8)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "d897d74a-a13b-47cf-9806-eb8a75fe8d08")
|
||||
)
|
||||
(pad "3" thru_hole circle
|
||||
(at -3.9 -1)
|
||||
(size 1.524 1.524)
|
||||
(drill 1)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "02a8c3fc-d75c-47a4-a907-f9191ff19e2c")
|
||||
)
|
||||
(pad "4" thru_hole circle
|
||||
(at -3.9 1)
|
||||
(size 1.524 1.524)
|
||||
(drill 1)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "2000b5b8-f7c9-40a8-9010-65c16d2aefce")
|
||||
)
|
||||
(model "${KICAD8_3DMODEL_DIR}/Connector_AMASS.3dshapes/AMASS_XT30UPB-M_1x02_P5.0mm_Vertical.wrl"
|
||||
(offset
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
4
Hardware/open-bms/bms/fp-lib-table
Normal file
4
Hardware/open-bms/bms/fp-lib-table
Normal file
@@ -0,0 +1,4 @@
|
||||
(fp_lib_table
|
||||
(version 7)
|
||||
(lib (name "amass") (type "KiCad") (uri "${KIPRJMOD}/footprints/amass") (options "") (descr ""))
|
||||
)
|
||||
@@ -256,12 +256,12 @@ async fn main(spawner: Spawner) {
|
||||
|
||||
// Improve CAN robustness for longer cables:
|
||||
// 1. Enable Automatic Bus-Off Management (ABOM)
|
||||
// 2. Disable Automatic Retransmission (NART) as we send regular measurements anyway
|
||||
// 2. Enable Automatic Retransmission (NART) to recover from transient errors
|
||||
// 3. Enable Receive FIFO Overwrite Mode (RFLM = 0, default)
|
||||
// 4. Increase Resync Jump Width (SJW) if possible by patching BTIMR
|
||||
hal::pac::CAN1.ctlr().modify(|w| {
|
||||
w.set_abom(false);
|
||||
w.set_nart(true);
|
||||
w.set_nart(false);
|
||||
});
|
||||
|
||||
// SJW is bits 24-25 of BTIMR. HAL sets it to 0 (SJW=1).
|
||||
@@ -415,7 +415,7 @@ async fn can_task(
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
let mut msg: heapless::String<128> = heapless::String::new();
|
||||
let _ = write!(&mut msg, "rx err {:?}", err);
|
||||
let _ = write!(&mut msg, "rx err {:?} \r\n", err);
|
||||
log(msg);
|
||||
}
|
||||
}
|
||||
@@ -429,14 +429,25 @@ async fn can_task(
|
||||
}
|
||||
}
|
||||
|
||||
// Check CAN error status register for bus-off condition
|
||||
if hal::pac::CAN1.errsr().read().boff() {
|
||||
blink_error_loop(info, warn, 3, 3).await; // Bus-off error
|
||||
}
|
||||
|
||||
|
||||
while let Ok(mut frame) = CAN_TX_CH.try_receive() {
|
||||
match can.transmit(&mut frame) {
|
||||
Ok(..) => {
|
||||
Ok(_ok) => {
|
||||
let status = hal::pac::CAN1.errsr().read();
|
||||
|
||||
// Check CAN error status register for bus-off condition
|
||||
if status.boff() || status.ewgf() || status.epvf() {
|
||||
let mut msg: heapless::String<128> = heapless::String::new();
|
||||
let _ = write!(&mut msg, "canbus status {} {} {} \r\n", status.boff(), status.ewgf(), status.epvf());
|
||||
log(msg);
|
||||
for _ in 0..2 {
|
||||
warn.set_high();
|
||||
Timer::after_millis(100).await;
|
||||
warn.set_low();
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(nb::Error::WouldBlock) => {
|
||||
for _ in 0..2 {
|
||||
@@ -446,7 +457,7 @@ async fn can_task(
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
let mut msg: heapless::String<128> = heapless::String::new();
|
||||
let _ = write!(&mut msg, "canbus out buffer full");
|
||||
let _ = write!(&mut msg, "canbus out buffer full \r\n");
|
||||
log(msg);
|
||||
}
|
||||
Err(nb::Error::Other(err)) => {
|
||||
@@ -457,7 +468,7 @@ async fn can_task(
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
let mut msg: heapless::String<128> = heapless::String::new();
|
||||
let _ = write!(&mut msg, "tx err {:?}", err);
|
||||
let _ = write!(&mut msg, "tx err {:?} \r\n", err);
|
||||
log(msg);
|
||||
}
|
||||
}
|
||||
@@ -516,7 +527,7 @@ async fn worker(
|
||||
loop {
|
||||
let mut total_pulses: u32 = 0;
|
||||
|
||||
for _ in 0..AVG_WINDOWS {
|
||||
for _window in 0..AVG_WINDOWS {
|
||||
// Count rising edges of Q in a 100 ms window
|
||||
let start = Instant::now();
|
||||
let mut pulses: u32 = 0;
|
||||
@@ -582,13 +593,16 @@ async fn worker(
|
||||
log(msg);
|
||||
|
||||
let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap();
|
||||
let delay_ms = moisture_id.as_raw() as u64 % 50;
|
||||
Timer::after(Duration::from_millis(delay_ms)).await;
|
||||
CAN_TX_CH.send(moisture).await;
|
||||
|
||||
// Send firmware build timestamp after each measurement so the controller
|
||||
// always has up-to-date build info without requiring an identify request.
|
||||
if let Some(build_frame) = CanFrame::new(firmware_build_id, &FIRMWARE_BUILD_MINUTES.to_be_bytes()) {
|
||||
CAN_TX_CH.send(build_frame).await;
|
||||
}
|
||||
let firmware = CanFrame::new(firmware_build_id, &FIRMWARE_BUILD_MINUTES.to_be_bytes()).unwrap();
|
||||
let delay_ms = firmware_build_id.as_raw() as u64 % 50;
|
||||
Timer::after(Duration::from_millis(delay_ms)).await;
|
||||
CAN_TX_CH.send(firmware).await;
|
||||
|
||||
// Wait for the other slot to measure, plus gaps to ensure no overlap
|
||||
// After A finishes measuring: wait 50ms (gap) + 400ms (B measures) + 50ms (gap) = 500ms
|
||||
|
||||
396
Software/MainBoard/rust/Cargo.lock
generated
396
Software/MainBoard/rust/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -101,6 +101,7 @@ chrono-tz = { version = "0.10.4", default-features = false, features = ["filter-
|
||||
heapless = { version = "0.7.17", features = ["serde"] } # stay in sync with mcutie version
|
||||
static_cell = "2.1.1"
|
||||
portable-atomic = "1.11.1"
|
||||
critical-section = "1"
|
||||
crc = "3.3.0"
|
||||
bytemuck = { version = "1.24.0", features = ["derive", "min_const_generics", "pod_saturating", "extern_crate_alloc"] }
|
||||
deranged = "0.5.5"
|
||||
|
||||
3
Software/MainBoard/rust/TODO
Normal file
3
Software/MainBoard/rust/TODO
Normal file
@@ -0,0 +1,3 @@
|
||||
One Wire does not seem to work.
|
||||
Flow Sensor does not seem to work.
|
||||
PlantProfiles with a dry out phase needs to be implemented + Memory for this
|
||||
@@ -49,5 +49,8 @@ fn linker_be_nice() {
|
||||
|
||||
fn main() {
|
||||
linker_be_nice();
|
||||
// Non-existent path causes Cargo to always re-run this script,
|
||||
// keeping VERGEN_BUILD_TIMESTAMP fresh on every build.
|
||||
println!("cargo:rerun-if-changed=ALWAYS_REBUILD_SENTINEL");
|
||||
let _ = EmitBuilder::builder().all_git().all_build().emit();
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ pub struct PlantConfig {
|
||||
pub min_pump_current_ma: u16,
|
||||
pub max_pump_current_ma: u16,
|
||||
pub ignore_current_error: bool,
|
||||
pub fertilizer_s: u16,
|
||||
pub fertilizer_cooldown_min: u16,
|
||||
}
|
||||
|
||||
impl Default for PlantConfig {
|
||||
@@ -150,6 +152,8 @@ impl Default for PlantConfig {
|
||||
min_pump_current_ma: 10,
|
||||
max_pump_current_ma: 3000,
|
||||
ignore_current_error: true,
|
||||
fertilizer_s: 0,
|
||||
fertilizer_cooldown_min: 1440, // 1 day default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,9 +316,12 @@ impl From<sntpc::Error> for FatError {
|
||||
impl From<BmsProtocolError> for FatError {
|
||||
fn from(value: BmsProtocolError) -> Self {
|
||||
match value {
|
||||
BmsProtocolError::I2cCommunicationError => FatError::String {
|
||||
BmsProtocolError::I2cCommunicationError =>FatError::String {
|
||||
error: "I2C communication error".to_string(),
|
||||
},
|
||||
BmsProtocolError::ChecksumError => FatError::String {
|
||||
error: "BMS checksum error".to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut CONSECUTIVE_WATERING_PLANT: [u32; PLANT_COUNT] = [0; PLANT_COUNT];
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut LAST_FERTILIZER_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut LOW_VOLTAGE_DETECTED: i8 = 0;
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut RESTART_TO_CONF: i8 = 0;
|
||||
@@ -342,6 +344,14 @@ impl Esp<'_> {
|
||||
LAST_WATERING_TIMESTAMP[plant] = time.timestamp_millis();
|
||||
}
|
||||
}
|
||||
pub(crate) fn last_fertilizer_time(&self, plant: usize) -> i64 {
|
||||
unsafe { LAST_FERTILIZER_TIMESTAMP[plant] }
|
||||
}
|
||||
pub(crate) fn store_last_fertilizer_time(&mut self, plant: usize, time: DateTime<Utc>) {
|
||||
unsafe {
|
||||
LAST_FERTILIZER_TIMESTAMP[plant] = time.timestamp_millis();
|
||||
}
|
||||
}
|
||||
pub(crate) fn set_low_voltage_in_cycle(&mut self) {
|
||||
unsafe {
|
||||
LOW_VOLTAGE_DETECTED = 1;
|
||||
|
||||
@@ -164,6 +164,7 @@ pub trait BoardInteraction<'a> {
|
||||
async fn get_mptt_voltage(&mut self) -> FatResult<Voltage>;
|
||||
async fn get_mptt_current(&mut self) -> FatResult<Current>;
|
||||
async fn can_power(&mut self, state: bool) -> FatResult<()>;
|
||||
async fn fertilizer_pump(&mut self, enable: bool) -> FatResult<()>;
|
||||
|
||||
async fn backup_config(&mut self, config: &PlantControllerConfig) -> FatResult<()>;
|
||||
async fn read_backup(&mut self) -> FatResult<PlantControllerConfig>;
|
||||
@@ -175,12 +176,7 @@ pub trait BoardInteraction<'a> {
|
||||
}
|
||||
|
||||
/// Return the last known firmware build timestamps per sensor, set during detect_sensors.
|
||||
fn get_sensor_build_minutes(
|
||||
&self,
|
||||
) -> (
|
||||
[Option<u32>; PLANT_COUNT],
|
||||
[Option<u32>; PLANT_COUNT],
|
||||
) {
|
||||
fn get_sensor_build_minutes(&self) -> ([Option<u32>; PLANT_COUNT], [Option<u32>; PLANT_COUNT]) {
|
||||
([None; PLANT_COUNT], [None; PLANT_COUNT])
|
||||
}
|
||||
|
||||
@@ -294,7 +290,8 @@ impl PlantHal {
|
||||
error: format!("Could not init wifi: {:?}", e),
|
||||
})?;
|
||||
|
||||
let pcnt_module = Pcnt::new(peripherals.PCNT);
|
||||
let mut pcnt_module = Pcnt::new(peripherals.PCNT);
|
||||
pcnt_module.set_interrupt_handler(water::flow_interrupt_handler);
|
||||
|
||||
let free_pins = FreePeripherals {
|
||||
gpio0: peripherals.GPIO0,
|
||||
|
||||
@@ -161,9 +161,11 @@ pub(crate) async fn create_v4(
|
||||
info!("Start v4");
|
||||
let mut awake = Output::new(peripherals.gpio21, Level::High, OutputConfig::default());
|
||||
awake.set_high();
|
||||
info!("v4: gpio21 awake ok");
|
||||
|
||||
let mut general_fault = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default());
|
||||
general_fault.set_low();
|
||||
info!("v4: gpio23 general_fault ok");
|
||||
|
||||
let twai_config = Some(TwaiConfiguration::new(
|
||||
peripherals.twai,
|
||||
@@ -172,17 +174,24 @@ pub(crate) async fn create_v4(
|
||||
TWAI_BAUDRATE,
|
||||
TwaiMode::Normal,
|
||||
));
|
||||
info!("v4: twai config ok");
|
||||
|
||||
let extra1 = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio6 extra1 ok");
|
||||
let extra2 = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio15 extra2 ok");
|
||||
|
||||
let one_wire_pin = Flex::new(peripherals.gpio18);
|
||||
info!("v4: gpio18 one_wire ok");
|
||||
let tank_power_pin = Output::new(peripherals.gpio11, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio11 tank_power ok");
|
||||
let flow_sensor_pin = Input::new(
|
||||
peripherals.gpio4,
|
||||
InputConfig::default().with_pull(Pull::Up),
|
||||
);
|
||||
info!("v4: gpio4 flow_sensor ok");
|
||||
|
||||
info!("v4: creating tank sensor");
|
||||
let tank_sensor = TankSensor::create(
|
||||
one_wire_pin,
|
||||
peripherals.adc1,
|
||||
@@ -191,12 +200,17 @@ pub(crate) async fn create_v4(
|
||||
flow_sensor_pin,
|
||||
peripherals.pcnt1,
|
||||
)?;
|
||||
info!("v4: tank sensor ok");
|
||||
|
||||
let can_power = Output::new(peripherals.gpio22, Level::Low, OutputConfig::default());
|
||||
info!("v4: gpio22 can_power ok");
|
||||
|
||||
let solar_is_day = Input::new(peripherals.gpio7, InputConfig::default());
|
||||
info!("v4: gpio7 solar_is_day ok");
|
||||
let light = Output::new(peripherals.gpio10, Level::Low, Default::default());
|
||||
info!("v4: gpio10 light ok");
|
||||
let charge_indicator = Output::new(peripherals.gpio3, Level::Low, Default::default());
|
||||
info!("v4: gpio3 charge_indicator ok");
|
||||
|
||||
info!("Start pump expander");
|
||||
let pump_device = I2cDevice::new(I2C_DRIVER.get().await);
|
||||
@@ -391,7 +405,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||
|
||||
let mut moistures = Moistures::default();
|
||||
let _ = wait_for_can_measurements(&mut twai, &mut moistures)
|
||||
.with_timeout(Duration::from_millis(1000))
|
||||
.with_timeout(Duration::from_millis(5000))
|
||||
.await;
|
||||
Ok(moistures)
|
||||
})
|
||||
@@ -484,6 +498,15 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fertilizer_pump(&mut self, enable: bool) -> FatResult<()> {
|
||||
if enable {
|
||||
self.extra2.set_high();
|
||||
} else {
|
||||
self.extra2.set_low();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn backup_config(&mut self, controller_config: &PlantControllerConfig) -> FatResult<()> {
|
||||
let mut buffer: [u8; 4096 - BACKUP_HEADER_MAX_SIZE] = [0; 4096 - BACKUP_HEADER_MAX_SIZE];
|
||||
let length = postcard::to_slice(controller_config, &mut buffer)?.len();
|
||||
@@ -643,9 +666,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sensor_build_minutes(
|
||||
&self,
|
||||
) -> ([Option<u32>; PLANT_COUNT], [Option<u32>; PLANT_COUNT]) {
|
||||
fn get_sensor_build_minutes(&self) -> ([Option<u32>; PLANT_COUNT], [Option<u32>; PLANT_COUNT]) {
|
||||
(self.sensor_a_build_minutes, self.sensor_b_build_minutes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,20 @@ use esp_hal::pcnt::channel::EdgeMode::{Hold, Increment};
|
||||
use esp_hal::pcnt::unit::Unit;
|
||||
use esp_hal::peripherals::GPIO5;
|
||||
use esp_hal::Async;
|
||||
use esp_println::println;
|
||||
use log::info;
|
||||
use onewire::{ds18b20, Device, DeviceSearch, OneWire, DS18B20};
|
||||
use portable_atomic::{AtomicUsize, Ordering};
|
||||
|
||||
unsafe impl Send for TankSensor<'_> {}
|
||||
|
||||
static FLOW_OVERFLOW_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub struct TankSensor<'a> {
|
||||
one_wire_bus: OneWire<Flex<'a>>,
|
||||
tank_channel: Adc<'a, ADC1<'a>, Async>,
|
||||
tank_power: Output<'a>,
|
||||
tank_pin: AdcPin<GPIO5<'a>, ADC1<'a>, AdcCalLine<ADC1<'a>>>,
|
||||
flow_counter: Unit<'a, 1>,
|
||||
flow_unit: Unit<'static, 1>,
|
||||
}
|
||||
|
||||
impl<'a> TankSensor<'a> {
|
||||
@@ -30,7 +33,7 @@ impl<'a> TankSensor<'a> {
|
||||
gpio5: GPIO5<'a>,
|
||||
tank_power: Output<'a>,
|
||||
flow_sensor: Input,
|
||||
pcnt1: Unit<'a, 1>,
|
||||
pcnt1: Unit<'static, 1>,
|
||||
) -> Result<TankSensor<'a>, FatError> {
|
||||
one_wire_pin.apply_output_config(
|
||||
&OutputConfig::default()
|
||||
@@ -41,47 +44,76 @@ impl<'a> TankSensor<'a> {
|
||||
one_wire_pin.set_high();
|
||||
one_wire_pin.set_input_enable(true);
|
||||
one_wire_pin.set_output_enable(true);
|
||||
info!("tank: one_wire pin config ok");
|
||||
|
||||
let mut adc1_config = AdcConfig::new();
|
||||
info!("tank: adc config created");
|
||||
let tank_pin =
|
||||
adc1_config.enable_pin_with_cal::<_, AdcCalLine<_>>(gpio5, Attenuation::_11dB);
|
||||
info!("tank: adc pin cal ok");
|
||||
let tank_channel = Adc::new(adc1, adc1_config).into_async();
|
||||
info!("tank: adc channel ok");
|
||||
|
||||
let one_wire_bus = OneWire::new(one_wire_pin, false);
|
||||
info!("tank: one_wire bus ok");
|
||||
|
||||
pcnt1.set_high_limit(Some(i16::MAX))?;
|
||||
info!("tank: pcnt high limit ok");
|
||||
// Reject pulses shorter than ~12.8 µs (1023 APB cycles @ 80 MHz) to suppress EMI noise
|
||||
// on the sensor cable. Real flow pulses are in the millisecond range.
|
||||
pcnt1.set_filter(Some(1023)).unwrap();
|
||||
|
||||
let ch0 = &pcnt1.channel0;
|
||||
ch0.set_edge_signal(flow_sensor.peripheral_input());
|
||||
info!("tank: pcnt edge signal ok");
|
||||
ch0.set_input_mode(Hold, Increment);
|
||||
ch0.set_ctrl_mode(Keep, Keep);
|
||||
info!("tank: pcnt input/ctrl mode ok");
|
||||
|
||||
pcnt1.listen();
|
||||
info!("tank: pcnt listen ok");
|
||||
|
||||
Ok(TankSensor {
|
||||
one_wire_bus,
|
||||
tank_channel,
|
||||
tank_power,
|
||||
tank_pin,
|
||||
flow_counter: pcnt1,
|
||||
flow_unit: pcnt1,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset_flow_meter(&mut self) {
|
||||
self.flow_counter.pause();
|
||||
self.flow_counter.clear();
|
||||
// Pause, clear counter, clear any pending interrupt, then reset the overflow counter —
|
||||
// all inside a single critical section to prevent a race where the interrupt fires
|
||||
// between the overflow reset and the pause.
|
||||
critical_section::with(|_| {
|
||||
self.flow_unit.pause();
|
||||
self.flow_unit.clear();
|
||||
self.flow_unit.reset_interrupt();
|
||||
FLOW_OVERFLOW_COUNTER.store(0, Ordering::SeqCst);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn start_flow_meter(&mut self) {
|
||||
self.flow_counter.resume();
|
||||
}
|
||||
|
||||
pub fn get_flow_meter_value(&mut self) -> i16 {
|
||||
self.flow_counter.value()
|
||||
self.flow_unit.resume();
|
||||
}
|
||||
|
||||
pub fn stop_flow_meter(&mut self) -> i16 {
|
||||
self.flow_counter.pause();
|
||||
self.get_flow_meter_value()
|
||||
critical_section::with(|_| {
|
||||
let val = self.flow_unit.value();
|
||||
self.flow_unit.pause();
|
||||
val
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_full_flow_count(&self) -> u32 {
|
||||
// Read both values inside a single critical section so an overflow interrupt cannot
|
||||
// fire between the two reads and produce an inconsistent result.
|
||||
critical_section::with(|_| {
|
||||
let overflowed = FLOW_OVERFLOW_COUNTER.load(Ordering::SeqCst) as u32;
|
||||
let current = self.flow_unit.value() as u32;
|
||||
overflowed * (i16::MAX as u32 + 1) + current
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn water_temperature_c(&mut self) -> Result<f32, FatError> {
|
||||
@@ -90,9 +122,9 @@ impl<'a> TankSensor<'a> {
|
||||
let mut delay = Delay::new();
|
||||
|
||||
let presence = self.one_wire_bus.reset(&mut delay)?;
|
||||
println!("OneWire: reset presence pulse = {}", presence);
|
||||
info!("OneWire: reset presence pulse = {}", presence);
|
||||
if !presence {
|
||||
println!("OneWire: no device responded to reset — check pull-up resistor and wiring");
|
||||
info!("OneWire: no device responded to reset — check pull-up resistor and wiring");
|
||||
}
|
||||
|
||||
let mut search = DeviceSearch::new();
|
||||
@@ -100,7 +132,7 @@ impl<'a> TankSensor<'a> {
|
||||
let mut devices_found = 0u8;
|
||||
while let Some(device) = self.one_wire_bus.search_next(&mut search, &mut delay)? {
|
||||
devices_found += 1;
|
||||
println!(
|
||||
info!(
|
||||
"OneWire: found device #{} family=0x{:02X} addr={:02X?}",
|
||||
devices_found, device.address[0], device.address
|
||||
);
|
||||
@@ -108,16 +140,16 @@ impl<'a> TankSensor<'a> {
|
||||
water_temp_sensor = Some(device);
|
||||
break;
|
||||
} else {
|
||||
println!("OneWire: skipping device — not a DS18B20 (family 0x{:02X} != 0x{:02X})", device.address[0], ds18b20::FAMILY_CODE);
|
||||
info!("OneWire: skipping device — not a DS18B20 (family 0x{:02X} != 0x{:02X})", device.address[0], ds18b20::FAMILY_CODE);
|
||||
}
|
||||
}
|
||||
if devices_found == 0 {
|
||||
println!("OneWire: search found zero devices on the bus");
|
||||
info!("OneWire: search found zero devices on the bus");
|
||||
}
|
||||
|
||||
match water_temp_sensor {
|
||||
Some(device) => {
|
||||
println!("Found one wire device: {:?}", device);
|
||||
info!("Found one wire device: {:?}", device);
|
||||
let mut water_temp_sensor = DS18B20::new(device)?;
|
||||
|
||||
let water_temp: Result<f32, FatError> = loop {
|
||||
@@ -126,11 +158,11 @@ impl<'a> TankSensor<'a> {
|
||||
.await;
|
||||
match &temp {
|
||||
Ok(res) => {
|
||||
println!("Water temp is {}", res);
|
||||
info!("Water temp is {}", res);
|
||||
break temp;
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Could not get water temp {} attempt {}", err, attempt)
|
||||
info!("Could not get water temp {} attempt {}", err, attempt)
|
||||
}
|
||||
}
|
||||
if attempt == 5 {
|
||||
@@ -178,3 +210,15 @@ impl<'a> TankSensor<'a> {
|
||||
Ok(median_mv / 1000.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[esp_hal::handler]
|
||||
pub fn flow_interrupt_handler() {
|
||||
use esp_hal::peripherals::PCNT;
|
||||
let pcnt = PCNT::regs();
|
||||
if pcnt.int_raw().read().cnt_thr_event_u(1).bit() {
|
||||
if pcnt.u_status(1).read().h_lim().bit() {
|
||||
FLOW_OVERFLOW_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
pcnt.int_clr().write(|w| w.cnt_thr_event_u(1).set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex as BlockingMutex;
|
||||
use log::{LevelFilter, Log, Metadata, Record};
|
||||
|
||||
const MAX_LIVE_LOG_ENTRIES: usize = 64;
|
||||
const MAX_LIVE_LOG_ENTRIES: usize = 128;
|
||||
|
||||
struct LiveLogBuffer {
|
||||
entries: Vec<(u64, String)>,
|
||||
|
||||
@@ -311,6 +311,10 @@ pub enum LogMessage {
|
||||
PumpOpenLoopCurrent,
|
||||
#[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")]
|
||||
PumpMissingSensorCurrent,
|
||||
#[strum(
|
||||
serialize = "Fertilizer applied for ${number_a}s on plant ${number_b} (last application ${txt_short} minutes ago)"
|
||||
)]
|
||||
FertilizerApplied,
|
||||
#[strum(serialize = "MPPT Current sensor could not be reached")]
|
||||
MPPTError,
|
||||
#[strum(
|
||||
|
||||
@@ -123,6 +123,8 @@ struct PumpInfo {
|
||||
max_current_ma: u16,
|
||||
min_current_ma: u16,
|
||||
error: String,
|
||||
flow_raw: u32,
|
||||
flow_ml: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -132,8 +134,9 @@ pub struct PumpResult {
|
||||
min_current_ma: u16,
|
||||
error: String,
|
||||
flow_value_ml: f32,
|
||||
flow_value_count: i16,
|
||||
flow_value_count: u32,
|
||||
pump_time_s: u16,
|
||||
overcurrent_ma: Option<u16>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
@@ -239,7 +242,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||
println!("starting webserver");
|
||||
|
||||
let _ = http_server(reboot_now.clone(), stack);
|
||||
spawner.spawn(http_server(reboot_now.clone(), stack)?);
|
||||
wait_infinity(board, WaitType::MissingConfig, reboot_now.clone(), UTC).await;
|
||||
}
|
||||
|
||||
@@ -384,7 +387,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
|
||||
let moisture = board.board_hal.measure_moisture_hz().await?;
|
||||
|
||||
let plantstate: [PlantState; PLANT_COUNT] = [
|
||||
let mut plantstate: [PlantState; PLANT_COUNT] = [
|
||||
PlantState::interpret_raw_values(moisture, 0, &mut board).await,
|
||||
PlantState::interpret_raw_values(moisture, 1, &mut board).await,
|
||||
PlantState::interpret_raw_values(moisture, 2, &mut board).await,
|
||||
@@ -408,6 +411,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
&& !water_frozen;
|
||||
if pump_required {
|
||||
log(LogMessage::EnableMain, dry_run as u32, 0, "", "");
|
||||
let mut overcurrent_results: [Option<u16>; PLANT_COUNT] = [None; PLANT_COUNT];
|
||||
for (plant_id, (state, plant_config)) in plantstate
|
||||
.iter()
|
||||
.zip(&board.board_hal.get_config().plants.clone())
|
||||
@@ -454,12 +458,15 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
0,
|
||||
0,
|
||||
String::new(),
|
||||
0,
|
||||
0.0,
|
||||
)
|
||||
.await;
|
||||
|
||||
let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run).await;
|
||||
match result {
|
||||
Ok(state) => {
|
||||
overcurrent_results[plant_id] = state.overcurrent_ma;
|
||||
pump_info(
|
||||
&mut board,
|
||||
plant_id,
|
||||
@@ -469,6 +476,8 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
state.max_current_ma,
|
||||
state.min_current_ma,
|
||||
state.error,
|
||||
state.flow_value_count,
|
||||
state.flow_value_ml,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -482,6 +491,8 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
0,
|
||||
0,
|
||||
format!("{err:?}"),
|
||||
0,
|
||||
0.0,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -497,6 +508,16 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
.store_consecutive_pump_count(plant_id, 0);
|
||||
}
|
||||
}
|
||||
for (plant_id, overcurrent) in overcurrent_results.iter().enumerate() {
|
||||
if let Some(current_ma) = *overcurrent {
|
||||
plantstate[plant_id].pump.overcurrent_error = Some(current_ma);
|
||||
}
|
||||
}
|
||||
publish_plant_states(&mut board, &timezone_time.clone(), &plantstate)
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
error!("Error publishing plant states after pumping {e}");
|
||||
});
|
||||
} else {
|
||||
// Pump corrosion protection: pulses each pump once a week for 2s around midday.
|
||||
let last_check_day = board
|
||||
@@ -709,19 +730,58 @@ pub async fn do_secure_pump(
|
||||
let steps_in_50ms = plant_config.pump_time_s as usize * 20;
|
||||
|
||||
let mut current_collector = vec![0_u16; steps_in_50ms];
|
||||
let mut flow_collector = vec![0_i16; steps_in_50ms];
|
||||
let mut flow_collector = vec![0_u32; steps_in_50ms];
|
||||
let mut error = String::new();
|
||||
let mut first_error = true;
|
||||
let mut pump_time_ms: u32 = 0;
|
||||
let mut overcurrent_ma: Option<u16> = None;
|
||||
|
||||
if !dry_run {
|
||||
// Run fertilizer pump first if configured and not in cooldown
|
||||
if plant_config.fertilizer_s > 0 {
|
||||
let current_time = board.board_hal.get_time().await;
|
||||
let last_fertilizer = board.board_hal.get_esp().last_fertilizer_time(plant_id);
|
||||
let elapsed_minutes = (current_time.timestamp() - last_fertilizer) / 60;
|
||||
|
||||
if elapsed_minutes >= plant_config.fertilizer_cooldown_min as i64 {
|
||||
info!(
|
||||
"Starting fertilizer pump for {} seconds (last fertilizer was {} minutes ago)",
|
||||
plant_config.fertilizer_s, elapsed_minutes
|
||||
);
|
||||
log(
|
||||
LogMessage::FertilizerApplied,
|
||||
plant_config.fertilizer_s as u32,
|
||||
(plant_id + 1) as u32,
|
||||
&elapsed_minutes.to_string(),
|
||||
"",
|
||||
);
|
||||
board.board_hal.fertilizer_pump(true).await?;
|
||||
Timer::after_millis(plant_config.fertilizer_s as u64 * 1000).await;
|
||||
board.board_hal.fertilizer_pump(false).await?;
|
||||
info!("Fertilizer pump stopped");
|
||||
|
||||
// Store the current time as last fertilizer time
|
||||
board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.store_last_fertilizer_time(plant_id, current_time);
|
||||
} else {
|
||||
let remaining_minutes =
|
||||
plant_config.fertilizer_cooldown_min as i64 - elapsed_minutes;
|
||||
info!(
|
||||
"Skipping fertilizer (cooldown: {} minutes remaining)",
|
||||
remaining_minutes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
board.board_hal.get_tank_sensor()?.reset_flow_meter();
|
||||
board.board_hal.get_tank_sensor()?.start_flow_meter();
|
||||
board.board_hal.pump(plant_id, true).await?;
|
||||
|
||||
for step in 0..steps_in_50ms {
|
||||
let step_start = Instant::now();
|
||||
let flow_value = board.board_hal.get_tank_sensor()?.get_flow_meter_value();
|
||||
let flow_value = board.board_hal.get_tank_sensor()?.get_full_flow_count();
|
||||
flow_collector[step] = flow_value;
|
||||
let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
||||
|
||||
@@ -752,6 +812,7 @@ pub async fn do_secure_pump(
|
||||
plant_config.max_pump_current_ma.to_string().as_str(),
|
||||
step.to_string().as_str(),
|
||||
);
|
||||
overcurrent_ma = Some(current_ma);
|
||||
error = err_msg;
|
||||
} else if high_current && first_error {
|
||||
let err_msg = format!("OverCurrent: {}mA", current_ma);
|
||||
@@ -762,6 +823,7 @@ pub async fn do_secure_pump(
|
||||
plant_config.max_pump_current_ma.to_string().as_str(),
|
||||
step.to_string().as_str(),
|
||||
);
|
||||
overcurrent_ma = Some(current_ma);
|
||||
board.board_hal.general_fault(true).await;
|
||||
board.board_hal.fault(plant_id, true).await?;
|
||||
if !plant_config.ignore_current_error {
|
||||
@@ -831,7 +893,7 @@ pub async fn do_secure_pump(
|
||||
pump_time_ms = 1337;
|
||||
}
|
||||
board.board_hal.get_tank_sensor()?.stop_flow_meter();
|
||||
let final_flow_value = board.board_hal.get_tank_sensor()?.get_flow_meter_value();
|
||||
let final_flow_value = board.board_hal.get_tank_sensor()?.get_full_flow_count();
|
||||
let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
||||
info!("Final flow value is {final_flow_value} with {flow_value_ml} ml");
|
||||
current_collector.sort();
|
||||
@@ -843,6 +905,7 @@ pub async fn do_secure_pump(
|
||||
flow_value_count: final_flow_value,
|
||||
pump_time_s: (pump_time_ms / 1000) as u16,
|
||||
error,
|
||||
overcurrent_ma,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -997,6 +1060,8 @@ async fn pump_info(
|
||||
max_current_ma: u16,
|
||||
min_current_ma: u16,
|
||||
error: String,
|
||||
flow_raw: u32,
|
||||
flow_ml: f32,
|
||||
) {
|
||||
let pump_info = PumpInfo {
|
||||
enabled: pump_active,
|
||||
@@ -1005,6 +1070,8 @@ async fn pump_info(
|
||||
max_current_ma,
|
||||
min_current_ma,
|
||||
error,
|
||||
flow_raw,
|
||||
flow_ml,
|
||||
};
|
||||
let pump_topic = format!("/pump{}", plant_id + 1);
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ impl<'t, T: Deref<Target = str> + 't, L: Publishable + 't, const S: usize>
|
||||
username: self.username,
|
||||
password: self.password,
|
||||
subscriptions: self.subscriptions,
|
||||
keep_alive
|
||||
keep_alive,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ use chrono::{DateTime, TimeDelta, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 7500.; // 60kHz (500Hz margin)
|
||||
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels
|
||||
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 70000.; // 70kHz
|
||||
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 400.; // this is really, really dry, think like cactus levels
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub enum MoistureSensorError {
|
||||
@@ -51,16 +51,27 @@ pub enum PumpError {
|
||||
failed_attempts: usize,
|
||||
max_allowed_failures: usize,
|
||||
},
|
||||
OverCurrent {
|
||||
current_ma: u16,
|
||||
max_allowed_ma: u16,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct PumpState {
|
||||
consecutive_pump_count: u32,
|
||||
previous_pump: Option<DateTime<Utc>>,
|
||||
pub overcurrent_error: Option<u16>,
|
||||
}
|
||||
|
||||
impl PumpState {
|
||||
fn is_err(&self, plant_config: &PlantConfig) -> Option<PumpError> {
|
||||
if let Some(current_ma) = self.overcurrent_error {
|
||||
return Some(PumpError::OverCurrent {
|
||||
current_ma,
|
||||
max_allowed_ma: plant_config.max_pump_current_ma,
|
||||
});
|
||||
}
|
||||
if self.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 {
|
||||
Some(PumpError::PumpNotWorking {
|
||||
failed_attempts: self.consecutive_pump_count as usize,
|
||||
@@ -89,6 +100,8 @@ pub struct PlantState {
|
||||
pub sensor_a_firmware_build_minutes: Option<u32>,
|
||||
/// Last known firmware build timestamp for sensor B.
|
||||
pub sensor_b_firmware_build_minutes: Option<u32>,
|
||||
/// Last time fertilizer was applied (Unix timestamp in seconds).
|
||||
pub last_fertilizer_time: i64,
|
||||
}
|
||||
|
||||
fn map_range_moisture(
|
||||
@@ -162,6 +175,7 @@ impl PlantState {
|
||||
|
||||
let previous_pump = board.board_hal.get_esp().last_pump_time(plant_id);
|
||||
let consecutive_pump_count = board.board_hal.get_esp().consecutive_pump_count(plant_id);
|
||||
let last_fertilizer_time = board.board_hal.get_esp().last_fertilizer_time(plant_id);
|
||||
let (a_builds, b_builds) = board.board_hal.get_sensor_build_minutes();
|
||||
let state = Self {
|
||||
sensor_a,
|
||||
@@ -169,9 +183,11 @@ impl PlantState {
|
||||
pump: PumpState {
|
||||
consecutive_pump_count,
|
||||
previous_pump,
|
||||
overcurrent_error: None,
|
||||
},
|
||||
sensor_a_firmware_build_minutes: a_builds[plant_id],
|
||||
sensor_b_firmware_build_minutes: b_builds[plant_id],
|
||||
last_fertilizer_time,
|
||||
};
|
||||
if state.is_err() {
|
||||
let _ = board.board_hal.fault(plant_id, true).await;
|
||||
@@ -296,6 +312,7 @@ impl PlantState {
|
||||
},
|
||||
sensor_a_firmware_build_minutes: self.sensor_a_firmware_build_minutes,
|
||||
sensor_b_firmware_build_minutes: self.sensor_b_firmware_build_minutes,
|
||||
last_fertilizer_time: self.last_fertilizer_time,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,4 +345,6 @@ pub struct PlantInfo<'a> {
|
||||
sensor_a_firmware_build_minutes: Option<u32>,
|
||||
/// firmware build timestamp of sensor B (minutes since Unix epoch); None if unknown
|
||||
sensor_b_firmware_build_minutes: Option<u32>,
|
||||
/// last time when fertilizer was applied
|
||||
last_fertilizer_time: i64,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::fat_error::{FatError, FatResult};
|
||||
use crate::hal::savegame_manager::SAVEGAME_SLOT_SIZE;
|
||||
use crate::webserver::read_up_to_bytes_from_request;
|
||||
use crate::BOARD_ACCESS;
|
||||
use alloc::borrow::ToOwned;
|
||||
@@ -46,7 +47,7 @@ pub(crate) async fn backup_config<T, const N: usize>(
|
||||
where
|
||||
T: Read + Write,
|
||||
{
|
||||
let input = read_up_to_bytes_from_request(conn, Some(4096)).await?;
|
||||
let input = read_up_to_bytes_from_request(conn, Some(SAVEGAME_SLOT_SIZE)).await?;
|
||||
info!("Read input with length {}", input.len());
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
let config_to_backup = serde_json::from_slice(&input)?;
|
||||
|
||||
@@ -17,8 +17,8 @@ use crate::webserver::get_log::{get_live_log, get_log};
|
||||
use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index};
|
||||
use crate::webserver::ota::ota_operations;
|
||||
use crate::webserver::post_json::{
|
||||
board_test, can_power, detect_sensors, night_lamp_test, pump_test, set_config, wifi_scan,
|
||||
write_time,
|
||||
board_test, can_power, detect_sensors, fertilizer_pump_test, night_lamp_test, pump_test,
|
||||
set_config, wifi_scan, write_time,
|
||||
};
|
||||
use crate::{bail, BOARD_ACCESS};
|
||||
use alloc::borrow::ToOwned;
|
||||
@@ -116,6 +116,7 @@ impl Handler for HTTPRequestRouter {
|
||||
"/pumptest" => Some(pump_test(conn).await),
|
||||
"/can_power" => Some(can_power(conn).await),
|
||||
"/lamptest" => Some(night_lamp_test(conn).await),
|
||||
"/fertilizerpumptest" => Some(fertilizer_pump_test(conn).await),
|
||||
"/boardtest" => Some(board_test().await),
|
||||
"/detect_sensors" => Some(detect_sensors(conn).await),
|
||||
"/reboot" => {
|
||||
|
||||
@@ -102,6 +102,19 @@ where
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) async fn fertilizer_pump_test<T, const N: usize>(
|
||||
_request: &mut Connection<'_, T, N>,
|
||||
) -> FatResult<Option<String>>
|
||||
where
|
||||
T: Read + Write,
|
||||
{
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.fertilizer_pump(true).await?;
|
||||
embassy_time::Timer::after_millis(1000).await;
|
||||
board.board_hal.fertilizer_pump(false).await?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) async fn write_time<T, const N: usize>(
|
||||
request: &mut Connection<'_, T, N>,
|
||||
) -> FatResult<Option<String>>
|
||||
|
||||
@@ -128,6 +128,8 @@ export interface PlantConfig {
|
||||
min_moisture: number,
|
||||
pump_time_s: number,
|
||||
pump_cooldown_min: number,
|
||||
fertilizer_s: number,
|
||||
fertilizer_cooldown_min: number,
|
||||
pump_hour_start: number,
|
||||
pump_hour_end: number,
|
||||
pump_limit_ml: number,
|
||||
|
||||
@@ -22,3 +22,8 @@
|
||||
<div class="boardkey">Pump corrosion protection (weekly)</div>
|
||||
<input type="checkbox" id="hardware_pump_corrosion_protection">
|
||||
</div>
|
||||
|
||||
<div class="subtitle">Fertilizer Pump:</div>
|
||||
<div class="flexcontainer">
|
||||
<button class="subtitle" id="fertilizer_pump_test">Test Fertilizer Pump</button>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ export class HardwareConfigView {
|
||||
private readonly hardware_board_value: HTMLSelectElement;
|
||||
private readonly hardware_battery_value: HTMLSelectElement;
|
||||
private readonly hardware_pump_corrosion_protection: HTMLInputElement;
|
||||
private readonly fertilizer_pump_test: HTMLButtonElement;
|
||||
constructor(controller:Controller){
|
||||
(document.getElementById("hardwareview") as HTMLElement).innerHTML = require('./hardware.html') as string;
|
||||
|
||||
@@ -33,6 +34,11 @@ export class HardwareConfigView {
|
||||
|
||||
this.hardware_pump_corrosion_protection = document.getElementById("hardware_pump_corrosion_protection") as HTMLInputElement;
|
||||
this.hardware_pump_corrosion_protection.onchange = controller.configChanged
|
||||
|
||||
this.fertilizer_pump_test = document.getElementById("fertilizer_pump_test") as HTMLButtonElement;
|
||||
this.fertilizer_pump_test.onclick = () => {
|
||||
controller.testFertilizerPump();
|
||||
}
|
||||
}
|
||||
|
||||
setConfig(hardware: BoardHardware) {
|
||||
|
||||
@@ -304,6 +304,12 @@ export class Controller {
|
||||
})
|
||||
}
|
||||
|
||||
testFertilizerPump() {
|
||||
fetch(PUBLIC_URL + "/fertilizerpumptest", {
|
||||
method: "POST"
|
||||
})
|
||||
}
|
||||
|
||||
testPlant(plantId: number) {
|
||||
let counter = 0
|
||||
let limit = 30
|
||||
|
||||
@@ -78,6 +78,16 @@
|
||||
<input class="plantvalue" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600"
|
||||
placeholder="30">
|
||||
</div>
|
||||
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
|
||||
<div class="plantkey">Fertilizer (s):</div>
|
||||
<input class="plantvalue" id="plant_${plantId}_fertilizer_s" type="number" min="0" max="60"
|
||||
placeholder="0">
|
||||
</div>
|
||||
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
|
||||
<div class="plantkey">Fertilizer Cooldown (m):</div>
|
||||
<input class="plantvalue" id="plant_${plantId}_fertilizer_cooldown_min" type="number" min="0" max="20160"
|
||||
placeholder="1440">
|
||||
</div>
|
||||
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
|
||||
<div class="plantkey">"Pump Hour Start":</div>
|
||||
<select class="plantvalue" id="plant_${plantId}_pump_hour_start">10</select>
|
||||
|
||||
@@ -79,6 +79,8 @@ export class PlantView {
|
||||
private readonly minMoisture: HTMLInputElement;
|
||||
private readonly pumpTimeS: HTMLInputElement;
|
||||
private readonly pumpCooldown: HTMLInputElement;
|
||||
private readonly fertilizerS: HTMLInputElement;
|
||||
private readonly fertilizerCooldownMin: HTMLInputElement;
|
||||
private readonly pumpHourStart: HTMLSelectElement;
|
||||
private readonly pumpHourEnd: HTMLSelectElement;
|
||||
private readonly sensorAInstalled: HTMLInputElement;
|
||||
@@ -180,6 +182,16 @@ export class PlantView {
|
||||
controller.configChanged()
|
||||
}
|
||||
|
||||
this.fertilizerS = document.getElementById("plant_" + plantId + "_fertilizer_s") as HTMLInputElement;
|
||||
this.fertilizerS.onchange = function () {
|
||||
controller.configChanged()
|
||||
}
|
||||
|
||||
this.fertilizerCooldownMin = document.getElementById("plant_" + plantId + "_fertilizer_cooldown_min") as HTMLInputElement;
|
||||
this.fertilizerCooldownMin.onchange = function () {
|
||||
controller.configChanged()
|
||||
}
|
||||
|
||||
this.pumpHourStart = document.getElementById("plant_" + plantId + "_pump_hour_start") as HTMLSelectElement;
|
||||
this.pumpHourStart.onchange = function () {
|
||||
controller.configChanged()
|
||||
@@ -328,6 +340,8 @@ export class PlantView {
|
||||
this.minMoisture.value = plantConfig.min_moisture?.toString() || "";
|
||||
this.pumpTimeS.value = plantConfig.pump_time_s.toString();
|
||||
this.pumpCooldown.value = plantConfig.pump_cooldown_min.toString();
|
||||
this.fertilizerS.value = plantConfig.fertilizer_s?.toString() || "0";
|
||||
this.fertilizerCooldownMin.value = plantConfig.fertilizer_cooldown_min?.toString() || "1440";
|
||||
this.pumpHourStart.value = plantConfig.pump_hour_start.toString();
|
||||
this.pumpHourEnd.value = plantConfig.pump_hour_end.toString();
|
||||
this.sensorBInstalled.checked = plantConfig.sensor_b;
|
||||
@@ -355,6 +369,8 @@ export class PlantView {
|
||||
pump_time_s: this.pumpTimeS.valueAsNumber,
|
||||
pump_limit_ml: 5000,
|
||||
pump_cooldown_min: this.pumpCooldown.valueAsNumber,
|
||||
fertilizer_s: this.fertilizerS.valueAsNumber || 0,
|
||||
fertilizer_cooldown_min: this.fertilizerCooldownMin.valueAsNumber || 1440,
|
||||
pump_hour_start: +this.pumpHourStart.value,
|
||||
pump_hour_end: +this.pumpHourEnd.value,
|
||||
sensor_b: this.sensorBInstalled.checked,
|
||||
|
||||
@@ -3,39 +3,55 @@ title: "BatteryManagement"
|
||||
date: 2025-01-27
|
||||
draft: false
|
||||
description: "a description"
|
||||
tags: ["battery", "bq34z100"]
|
||||
tags: ["battery", "bms"]
|
||||
---
|
||||
# Battery Management Module
|
||||
The project contains an additional companion board (Fuel Gauge), with a bq34z100 battery management IC.
|
||||
|
||||
It allows to track the health and charge for an external battery and is supposed to be soldered directly to the battery.
|
||||
The MainBoard contains a connector for power, and additionally a two-pin I2C bus to communicate with the Battery Management module.
|
||||
The PlantCtrl system uses an external **Battery Management System (BMS)** board that connects to the MainBoard. This module monitors battery voltage, current, and health metrics and communicates with the ESP32-C6 via I2C.
|
||||
|
||||
<!-- TODO: Add photo of the new modular Battery Management board -->
|
||||
|
||||
# Setup
|
||||
{{< alert >}}
|
||||
A protected Battery is required. There is only a very simplistic output voltage adjustment for the MPPT system and no charge termination. It is expected that the battery itself protects against overcharging and deep discharges!
|
||||
The open-bms is a custom battery management board designed for this project. It uses a CH32V203 microcontroller to handle battery monitoring and protection. The older bq34z100-based battery management board is deprecated and located in the `__Legay_Unused` folder.
|
||||
{{< /alert >}}
|
||||
* BatteryManagement is purely optional, but recommended for solar power.
|
||||
* If available it will be used for an extended low power deep sleep in case of critical charge.
|
||||
* If available it will also be used, to reduce the nightlight, if the charge drops to a predefined level, so the nightlight cannot drain to much battery
|
||||
* If available, all relevant battery metrics will be published via mqtt
|
||||
|
||||
Currently the setup requires a custom Ev2400 flasher and the properitary windows software from texas instruments.
|
||||
{{< alert >}}
|
||||
Before soldering to the battery
|
||||
{{< /alert >}}
|
||||
1. The voltage devider high side must be bridged, while being connected to the computer and being supplied with around 4.2 V from the battery solder leads.
|
||||
2. Then the data/register for low voltage flash write protection should be set to 0V, as else with the voltage divider and no further configuration, the IC will refuse all write requests.
|
||||
3. After this the supplied golden image can be used, it will setup the battery for 6Ah and a 4S lifepo. Different values can be adjusted after this to the users liking.
|
||||
## Hardware
|
||||
|
||||
The Battery Management Board features:
|
||||
* CH32V203 RISC-V microcontroller for battery monitoring
|
||||
* I2C interface for communication with the MainBoard
|
||||
* Battery voltage and current sensing
|
||||
|
||||
{{< alert >}}
|
||||
The main board, does not care or process any of the charge discharge limits that can be set. Ensure that the battery can supply enough current as well as accept a 2.4A charging current from the MPPT system.
|
||||
The open-bms board does not use the bq34z100 fuel gauge IC. That component was used in an older legacy design now located in the `__Legay_Unused` folder.
|
||||
{{< /alert >}}
|
||||
|
||||
The golden image sets the statups led up, to be in blinky mode. one very long interval means, that the battery is pretty much full. A few very short flashes mean that the battery is nearly empty. No light means, that the battery is in discharge protection and shut down.
|
||||
## Integration with MainBoard
|
||||
|
||||
If the red error led lights, something is wrong with the battery. This can be abnormal voltages or a very low health state.
|
||||
The battery management board:
|
||||
* Connects to the MainBoard via a two-pin I2C bus
|
||||
* Provides power connection to the battery
|
||||
* Reports battery metrics via MQTT (if configured)
|
||||
|
||||
# Todo?
|
||||
If the battery reports that no discharging should occure, report this and then shutdown without using pumps
|
||||
## Usage
|
||||
|
||||
* If available, the system will use battery metrics for deep sleep management when charge is critical
|
||||
* The nightlight can be automatically disabled if battery level drops below a predefined threshold
|
||||
* All battery metrics are published via MQTT when configured
|
||||
* The system includes safety mechanisms to prevent overcharging and deep discharges through the battery's built-in protection circuitry
|
||||
|
||||
## Safety Notes
|
||||
|
||||
{{< alert >}}
|
||||
The system requires a battery with built-in protection circuitry. The MPPT system does not include charge termination or overcharge protection - the battery itself must provide these safety features.
|
||||
{{< /alert >}}
|
||||
|
||||
The CH32V203-based BMS monitors battery health and provides status information but does not control the charge/discharge limits. Ensure your battery can handle the maximum charging current from the MPPT system (up to 2.4A).
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Connect Battery:** Connect your protected battery to the BMS board
|
||||
2. **_connect MainBoard:** Connect the Battery Management Board to the MainBoard via the I2C bus connector
|
||||
3. **Power On:** Power on the system and verify communication via MQTT
|
||||
|
||||
## Status Indicators
|
||||
|
||||
The BMS board includes status LEDs, they behave like every normal powerbank (1-5 lights, animted if charging)
|
||||
@@ -65,13 +65,9 @@ Software and Hardware may fail: It is your responsibility to ensure that a stuck
|
||||
{{< /alert >}}
|
||||
|
||||
|
||||
# Todo
|
||||
## Flow Sensor
|
||||
There is a input for a flow sensor, currently it is not used as the software is missing.
|
||||
* Allow monitoring if pumps are actually moving water
|
||||
* Allow to set limits for how much ml are allowed additinally to the current time limit per watering run
|
||||
|
||||
|
||||
|
||||
Currently it cannot be set how two sensor should be interpreted and they are only averaged. More complex functions would be nice here, eg. allowing a user settable interpolation (0.8*a+0.2*b)/2 and Min(a,b) as well as max(a,b)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ tags: ["esp32", "hardware"]
|
||||
<img src="pcb_back.png" class="grid-w50" />
|
||||
{{< /gallery >}}
|
||||
|
||||
<!-- TODO: Add new screenshots of the modular PCB setup -->
|
||||
|
||||
{{< gitea server="https://git.mannheim.ccc.de/" repo="C3MA/PlantCtrl" >}}
|
||||
|
||||
## Modular Design
|
||||
@@ -27,17 +25,25 @@ The system now consists of a **MainBoard** which acts as the controller and seve
|
||||
* **Fully Open Source:** Designed in KiCad
|
||||
|
||||
## Available Modules
|
||||
* **MPPT Charger:** Efficient solar charging for batteries.
|
||||
* **Pump Driver:** High-current outputs for pumps and valves.
|
||||
* **Sensor Interface:** Support for multiple moisture sensors.
|
||||
* **Light Controller:** For LED nightlights or growth lights.
|
||||
* **MPPT Charger:** Efficient solar charging for batteries using CN3795.
|
||||
* **Pump Driver:** High-current outputs (up to 3A) for pumps and valves.
|
||||
* **Sensor Module:** CAN bus-based moisture sensors using CH32V203 microcontroller.
|
||||
* **Battery Management:** External BMS board with CH32V203 for battery monitoring.
|
||||
* **Light Controller:** For LED nightlights or growth lights using AP63200.
|
||||
|
||||
## Sensor Module (CAN bus)
|
||||
The standard sensor module features its own **CH32V203 RISC-V microcontroller**, which handles the measurement of soil moisture and communicates the results back to the MainBoard via the CAN bus.
|
||||
|
||||
* **Capacity:** Supports up to 16 sensors (typically 8 plants with an A and B sensor each).
|
||||
* **Reliability:** Digital communication via CAN bus ensures data integrity even over longer cable runs and in electrically noisy environments.
|
||||
* **Addressing:** The A sensor is always used; the B sensor is optional and suggested for larger planters to provide a better average of the soil moisture.
|
||||
|
||||
## Capabilities
|
||||
* **Moisture Sensors:** Supports multiple capacitive or resistive sensors via expansion modules.
|
||||
* **Moisture Sensors:** Supports multiple capacitive or resistive sensors via CAN bus-based Sensor Modules.
|
||||
* **Pumps/Valves:** Support for multiple independent watering zones.
|
||||
* **Power:**
|
||||
* Solar powered with MPPT
|
||||
* Battery powered with optional Battery Management (Fuel Gauge)
|
||||
* Battery powered with optional Battery Management System (BMS)
|
||||
* Can also be used with a standard power supply (7-24V)
|
||||
* **Efficient Power:** Use of high-efficiency DC-DC converters for 3.3V and peripherals.
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@ description: "a description"
|
||||
tags: ["firmeware", "upload"]
|
||||
---
|
||||
# From Source
|
||||
|
||||
The PlantCtrl firmware is written in Rust for the ESP32-C6 RISC-V microcontroller.
|
||||
|
||||
## Preconditions
|
||||
* **Rust:** Current version of `rustup`.
|
||||
* **ESP32 Toolchain:** `espup` installed and configured.
|
||||
* **ESP32 Toolchain:** `espup` installed and configured for ESP32-C6.
|
||||
* **espflash:** Installed via `cargo install espflash`.
|
||||
* **Node.js:** `npm` installed (for the web interface).
|
||||
|
||||
@@ -37,10 +40,8 @@ You can use the provided bash scripts to automate the build and flash process:
|
||||
You can also update the firmware wirelessly if the system is already running and connected to your network.
|
||||
|
||||
1. Generate the OTA binary:
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
2. The binary will be at `target/riscv32imac-unknown-none-elf/release/plant-ctrl2`.
|
||||
**`./image.sh`**
|
||||
2. The binary will be `image.bin`.
|
||||
3. Open the PlantCtrl web interface in your browser.
|
||||
4. Navigate to the **OTA** section.
|
||||
5. Upload the `plant-ctrl2` file.
|
||||
|
||||
@@ -6,23 +6,26 @@ description: "a description"
|
||||
tags: ["mqtt", "esp"]
|
||||
---
|
||||
# MQTT
|
||||
A configured MQTT server will receive statistical and status data from the controller.
|
||||
|
||||
The PlantCtrl firmware publishes comprehensive status and telemetry data via MQTT when configured. The system uses the **mcutie** crate for Home Assistant integration and standard MQTT topics.
|
||||
|
||||
### Topics
|
||||
|
||||
| Topic | Example | Description |
|
||||
|-------|---------|-------------|
|
||||
| `firmware/address` | `192.168.1.2` | IP address in station mode |
|
||||
| `firmware/state` | `VersionInfo { ... }` | Debug information about the current firmware and OTA slots |
|
||||
| `firmware/state` | `{...}` | Debug information about the current firmware and OTA slots |
|
||||
| `firmware/last_online` | `2025-01-22T08:56:46.664+01:00` | Last time the board was online |
|
||||
| `state` | `online` | Current state of the controller |
|
||||
| `mppt` | `{"current_ma":1200,"voltage_ma":18500}` | MPPT charging metrics |
|
||||
| `battery` | `{"Info":{"voltage_milli_volt":12860,"average_current_milli_ampere":-16,...}}` | Battery health and charge data |
|
||||
| `water` | `{"enough_water":true,"warn_level":false,"left_ml":1337,...}` | Water tank status |
|
||||
| `plant{1-8}` | `{"sensor_a":...,"sensor_b":...,"mode":"TargetMoisture",...}` | Detailed status for each plant slot |
|
||||
| `pump{1-8}` | `{"enabled":true,"pump_ineffective":false,...}` | Metrics for the last pump activity |
|
||||
| `mppt` | `{"current_ma":1200,"voltage_ma":18500}` | MPPT charging metrics (current and voltage from solar panel) |
|
||||
| `battery` | `{"Info":{"voltage_milli_volt":12860,"state_of_charge":95,...}}` | Battery health and charge data from the BMS |
|
||||
| `water` | `{"enough_water":true,"warn_level":false,"left_ml":1337,...}` | Water tank status (level, temperature, frozen detection) |
|
||||
| `plant{1-8}` | `{"sensor_a":...,"sensor_b":...,"mode":"TargetMoisture",...}` | Detailed status for each plant slot including moisture sensors |
|
||||
| `pump{1-8}` | `{"enabled":true,"median_current_ma":500,...}` | Metrics for each pump output |
|
||||
| `light` | `{"enabled":true,"active":true,...}` | Night light status |
|
||||
| `deepsleep` | `night 1h` | Why and how long the ESP will sleep |
|
||||
| `deepsleep` | `night 1h` | Reason and duration of deep sleep |
|
||||
|
||||
Note: The batteries `average_current_milli_ampere` field uses a placeholder value (1337) and should be updated with actual current sensor readings when available.
|
||||
|
||||
### Data Structures
|
||||
|
||||
@@ -39,14 +42,15 @@ Contains a debug dump of the `VersionInfo` struct:
|
||||
- `voltage_ma`: Solar panel voltage in mV
|
||||
|
||||
#### Battery (`battery`)
|
||||
Can be `"Unknown"` or an `Info` object:
|
||||
- `voltage_milli_volt`: Battery voltage
|
||||
- `average_current_milli_ampere`: Current draw/charge
|
||||
- `design_milli_ampere_hour`: Battery capacity
|
||||
- `remaining_milli_ampere_hour`: Remaining capacity
|
||||
Can be `"Unknown"` or an `Info` object. The battery data comes from a custom BMS (Battery Management System) board that uses the CH32V203 microcontroller with I2C communication.
|
||||
|
||||
- `voltage_milli_volt`: Battery voltage in millivolts
|
||||
- `average_current_milli_ampere`: Current draw/charge in milliamperes (placeholder: 1337)
|
||||
- `design_milli_ampere_hour`: Battery design capacity in milliampere-hours
|
||||
- `remaining_milli_ampere_hour`: Remaining capacity in milliampere-hours
|
||||
- `state_of_charge`: Charge percentage (0-100)
|
||||
- `state_of_health`: Health percentage (0-100)
|
||||
- `temperature`: Temperature in degrees Celsius
|
||||
- `state_of_health`: Health percentage (0-100) based onLifetime capacity vs design capacity
|
||||
- `temperature`: Battery temperature in degrees Celsius
|
||||
|
||||
#### Water (`water`)
|
||||
- `enough_water`: Boolean, true if level is above empty threshold
|
||||
|
||||
@@ -6,9 +6,9 @@ description: "How to compile the project"
|
||||
tags: ["clone", "compile"]
|
||||
---
|
||||
# Preconditions:
|
||||
* **Rust:** `rustup` installed.
|
||||
* **ESP32 Toolchain:** `espup` installed.
|
||||
* **Build Utilities:** `ldproxy` and `espflash` installed.
|
||||
* **Rust:** `rustup` installed with the Rust toolchain.
|
||||
* **ESP32 Toolchain:** `espup` installed for ESP32 support.
|
||||
* **Build Utilities:** `ldproxy` and `espflash` installed via cargo.
|
||||
* **Node.js:** `npm` installed (for the web interface).
|
||||
|
||||
# Cloning the Repository
|
||||
@@ -19,24 +19,16 @@ cd PlantCtrl/Software/MainBoard/rust
|
||||
```
|
||||
|
||||
# Toolchain Setup
|
||||
1. **Install Rust:** If not already done, visit [rustup.rs](https://rustup.rs/).
|
||||
2. **Install ldproxy:**
|
||||
|
||||
The project uses Rust with ESP32-C6 support. The toolchain setup involves installing the necessary components:
|
||||
|
||||
1. **Rust Toolchain:**
|
||||
```bash
|
||||
cargo install ldproxy
|
||||
```
|
||||
3. **Install espup:**
|
||||
```bash
|
||||
cargo install espup
|
||||
```
|
||||
4. **Install ESP toolchain:**
|
||||
```bash
|
||||
espup install
|
||||
```
|
||||
5. **Install espflash:**
|
||||
```bash
|
||||
cargo install espflash
|
||||
rustup toolchain install stable
|
||||
rustup default stable
|
||||
```
|
||||
|
||||
|
||||
# Building the Web Interface
|
||||
The configuration website is built using TypeScript and Webpack, then embedded into the Rust binary.
|
||||
```bash
|
||||
@@ -46,14 +38,7 @@ npx webpack
|
||||
cd ..
|
||||
```
|
||||
|
||||
# Compiling the Firmware
|
||||
Build the project using Cargo:
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
The resulting binary will be located in `target/riscv32imac-unknown-none-elf/release/plant-ctrl2`.
|
||||
|
||||
# Using Build Scripts
|
||||
# Compiling the Firmware using Build Scripts
|
||||
To simplify the process, several bash scripts are provided in the `Software/MainBoard/rust` directory:
|
||||
|
||||
* **`image_build.sh`**: Automatically builds the web interface, compiles the Rust firmware in release mode, and creates a flashable `image.bin`.
|
||||
|
||||
Reference in New Issue
Block a user