Compare commits
	
		
			23 Commits
		
	
	
		
			Release-1.
			...
			641412ecc8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 641412ecc8 | |||
| 55e4fdb7d3 | |||
| 0e352881d3 | |||
| 030f010832 | |||
| a2b7a1b10e | |||
| 10c098ad33 | |||
| 3c3feeb8b5 | |||
| 893a57707d | |||
| 790261b6b7 | |||
| 0b6c432c70 | |||
| c88b532978 | |||
| 27b8cb0166 | |||
| 56872ea856 | |||
| d3506fd479 | |||
| ba90e98565 | |||
| 7b2c853b0d | |||
| ed8f65b92e | |||
| 69e5249a9d | |||
| b9f3b39684 | |||
| 63f8c34d97 | |||
| d99216815a | |||
| e6b5c0e3f0 | |||
| 9aa266792d | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1 @@
 | 
			
		||||
.pio
 | 
			
		||||
.vscode/.browse.c_cpp.db*
 | 
			
		||||
.vscode/c_cpp_properties.json
 | 
			
		||||
.vscode/launch.json
 | 
			
		||||
.vscode/ipch
 | 
			
		||||
Bestellung.xlsx
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Documentation/Datasheets/E220-900T22D_UserManual_EN_v1.0.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Documentation/Datasheets/E220-900T22D_UserManual_EN_v1.0.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								Hardware/#auto_saved_files#
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Hardware/#auto_saved_files#
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
E:\Dark Emergency Timer\Hardware\DE-Timer\_autosave-DE-Timer.kicad_sch
 | 
			
		||||
							
								
								
									
										31
									
								
								Hardware/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Hardware/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
# For PCBs designed using KiCad: http://www.kicad-pcb.org/
 | 
			
		||||
# Format documentation: http://kicad-pcb.org/help/file-formats/
 | 
			
		||||
 | 
			
		||||
# Temporary files
 | 
			
		||||
*.000
 | 
			
		||||
*.bak
 | 
			
		||||
*.bck
 | 
			
		||||
*.kicad_pcb-bak
 | 
			
		||||
*.kicad_sch-bak
 | 
			
		||||
*.kicad_prl
 | 
			
		||||
*.sch-bak
 | 
			
		||||
*~
 | 
			
		||||
_autosave-*
 | 
			
		||||
*.tmp
 | 
			
		||||
*-save.pro
 | 
			
		||||
*-save.kicad_pcb
 | 
			
		||||
fp-info-cache
 | 
			
		||||
 | 
			
		||||
# Netlist files (exported from Eeschema)
 | 
			
		||||
*.net
 | 
			
		||||
 | 
			
		||||
# Autorouter files (exported from Pcbnew)
 | 
			
		||||
*.dsn
 | 
			
		||||
*.ses
 | 
			
		||||
 | 
			
		||||
# Exported BOM files
 | 
			
		||||
*.xml
 | 
			
		||||
*.csv
 | 
			
		||||
 | 
			
		||||
gerber/
 | 
			
		||||
*-backups/
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Hardware/BOM DE-Timer HW Rev.1.0.xlsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Hardware/BOM DE-Timer HW Rev.1.0.xlsx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										18457
									
								
								Hardware/DE-Timer.kicad_pcb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18457
									
								
								Hardware/DE-Timer.kicad_pcb
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										437
									
								
								Hardware/DE-Timer.kicad_pro
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										437
									
								
								Hardware/DE-Timer.kicad_pro
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,437 @@
 | 
			
		||||
{
 | 
			
		||||
  "board": {
 | 
			
		||||
    "design_settings": {
 | 
			
		||||
      "defaults": {
 | 
			
		||||
        "board_outline_line_width": 0.09999999999999999,
 | 
			
		||||
        "copper_line_width": 0.19999999999999998,
 | 
			
		||||
        "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.049999999999999996,
 | 
			
		||||
        "dimension_precision": 4,
 | 
			
		||||
        "dimension_units": 3,
 | 
			
		||||
        "dimensions": {
 | 
			
		||||
          "arrow_length": 1270000,
 | 
			
		||||
          "extension_offset": 500000,
 | 
			
		||||
          "keep_text_aligned": true,
 | 
			
		||||
          "suppress_zeroes": false,
 | 
			
		||||
          "text_position": 0,
 | 
			
		||||
          "units_format": 1
 | 
			
		||||
        },
 | 
			
		||||
        "fab_line_width": 0.09999999999999999,
 | 
			
		||||
        "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.15,
 | 
			
		||||
        "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.762,
 | 
			
		||||
          "height": 1.524,
 | 
			
		||||
          "width": 1.524
 | 
			
		||||
        },
 | 
			
		||||
        "silk_line_width": 0.15,
 | 
			
		||||
        "silk_text_italic": false,
 | 
			
		||||
        "silk_text_size_h": 1.0,
 | 
			
		||||
        "silk_text_size_v": 1.0,
 | 
			
		||||
        "silk_text_thickness": 0.15,
 | 
			
		||||
        "silk_text_upright": false,
 | 
			
		||||
        "zones": {
 | 
			
		||||
          "45_degree_only": false,
 | 
			
		||||
          "min_clearance": 0.127
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "diff_pair_dimensions": [
 | 
			
		||||
        {
 | 
			
		||||
          "gap": 0.0,
 | 
			
		||||
          "via_gap": 0.0,
 | 
			
		||||
          "width": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "drc_exclusions": [],
 | 
			
		||||
      "meta": {
 | 
			
		||||
        "version": 2
 | 
			
		||||
      },
 | 
			
		||||
      "rule_severities": {
 | 
			
		||||
        "annular_width": "error",
 | 
			
		||||
        "clearance": "error",
 | 
			
		||||
        "copper_edge_clearance": "error",
 | 
			
		||||
        "courtyards_overlap": "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_type_mismatch": "error",
 | 
			
		||||
        "hole_clearance": "error",
 | 
			
		||||
        "hole_near_hole": "error",
 | 
			
		||||
        "invalid_outline": "error",
 | 
			
		||||
        "item_on_disabled_layer": "error",
 | 
			
		||||
        "items_not_allowed": "error",
 | 
			
		||||
        "length_out_of_range": "error",
 | 
			
		||||
        "malformed_courtyard": "error",
 | 
			
		||||
        "microvia_drill_out_of_range": "error",
 | 
			
		||||
        "missing_courtyard": "ignore",
 | 
			
		||||
        "missing_footprint": "warning",
 | 
			
		||||
        "net_conflict": "warning",
 | 
			
		||||
        "npth_inside_courtyard": "ignore",
 | 
			
		||||
        "padstack": "error",
 | 
			
		||||
        "pth_inside_courtyard": "ignore",
 | 
			
		||||
        "shorting_items": "error",
 | 
			
		||||
        "silk_over_copper": "warning",
 | 
			
		||||
        "silk_overlap": "warning",
 | 
			
		||||
        "skew_out_of_range": "error",
 | 
			
		||||
        "through_hole_pad_without_hole": "error",
 | 
			
		||||
        "too_many_vias": "error",
 | 
			
		||||
        "track_dangling": "warning",
 | 
			
		||||
        "track_width": "error",
 | 
			
		||||
        "tracks_crossing": "error",
 | 
			
		||||
        "unconnected_items": "error",
 | 
			
		||||
        "unresolved_variable": "error",
 | 
			
		||||
        "via_dangling": "warning",
 | 
			
		||||
        "zone_has_empty_net": "error",
 | 
			
		||||
        "zones_intersect": "error"
 | 
			
		||||
      },
 | 
			
		||||
      "rules": {
 | 
			
		||||
        "allow_blind_buried_vias": false,
 | 
			
		||||
        "allow_microvias": false,
 | 
			
		||||
        "max_error": 0.005,
 | 
			
		||||
        "min_clearance": 0.0,
 | 
			
		||||
        "min_copper_edge_clearance": 0.0,
 | 
			
		||||
        "min_hole_clearance": 0.25,
 | 
			
		||||
        "min_hole_to_hole": 0.25,
 | 
			
		||||
        "min_microvia_diameter": 0.19999999999999998,
 | 
			
		||||
        "min_microvia_drill": 0.09999999999999999,
 | 
			
		||||
        "min_silk_clearance": 0.0,
 | 
			
		||||
        "min_through_hole_diameter": 0.3,
 | 
			
		||||
        "min_track_width": 0.19999999999999998,
 | 
			
		||||
        "min_via_annular_width": 0.049999999999999996,
 | 
			
		||||
        "min_via_diameter": 0.39999999999999997,
 | 
			
		||||
        "solder_mask_clearance": 0.0,
 | 
			
		||||
        "solder_mask_min_width": 0.0,
 | 
			
		||||
        "use_height_for_length_calcs": true
 | 
			
		||||
      },
 | 
			
		||||
      "track_widths": [
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.635,
 | 
			
		||||
        1.27,
 | 
			
		||||
        2.54,
 | 
			
		||||
        5.08
 | 
			
		||||
      ],
 | 
			
		||||
      "via_dimensions": [
 | 
			
		||||
        {
 | 
			
		||||
          "diameter": 0.0,
 | 
			
		||||
          "drill": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "zones_allow_external_fillets": false,
 | 
			
		||||
      "zones_use_no_outline": true
 | 
			
		||||
    },
 | 
			
		||||
    "layer_presets": []
 | 
			
		||||
  },
 | 
			
		||||
  "boards": [],
 | 
			
		||||
  "cvpcb": {
 | 
			
		||||
    "equivalence_files": []
 | 
			
		||||
  },
 | 
			
		||||
  "erc": {
 | 
			
		||||
    "erc_exclusions": [],
 | 
			
		||||
    "meta": {
 | 
			
		||||
      "version": 0
 | 
			
		||||
    },
 | 
			
		||||
    "pin_map": [
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        2,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        1,
 | 
			
		||||
        2,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        1,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        1,
 | 
			
		||||
        0,
 | 
			
		||||
        2,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        2
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2,
 | 
			
		||||
        2
 | 
			
		||||
      ]
 | 
			
		||||
    ],
 | 
			
		||||
    "rule_severities": {
 | 
			
		||||
      "bus_definition_conflict": "error",
 | 
			
		||||
      "bus_entry_needed": "error",
 | 
			
		||||
      "bus_label_syntax": "error",
 | 
			
		||||
      "bus_to_bus_conflict": "error",
 | 
			
		||||
      "bus_to_net_conflict": "error",
 | 
			
		||||
      "different_unit_footprint": "error",
 | 
			
		||||
      "different_unit_net": "error",
 | 
			
		||||
      "duplicate_reference": "error",
 | 
			
		||||
      "duplicate_sheet_names": "error",
 | 
			
		||||
      "extra_units": "error",
 | 
			
		||||
      "global_label_dangling": "warning",
 | 
			
		||||
      "hier_label_mismatch": "error",
 | 
			
		||||
      "label_dangling": "error",
 | 
			
		||||
      "lib_symbol_issues": "warning",
 | 
			
		||||
      "multiple_net_names": "warning",
 | 
			
		||||
      "net_not_bus_member": "warning",
 | 
			
		||||
      "no_connect_connected": "warning",
 | 
			
		||||
      "no_connect_dangling": "warning",
 | 
			
		||||
      "pin_not_connected": "error",
 | 
			
		||||
      "pin_not_driven": "error",
 | 
			
		||||
      "pin_to_pin": "warning",
 | 
			
		||||
      "power_pin_not_driven": "error",
 | 
			
		||||
      "similar_labels": "warning",
 | 
			
		||||
      "unannotated": "error",
 | 
			
		||||
      "unit_value_mismatch": "error",
 | 
			
		||||
      "unresolved_variable": "error",
 | 
			
		||||
      "wire_dangling": "error"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "libraries": {
 | 
			
		||||
    "pinned_footprint_libs": [],
 | 
			
		||||
    "pinned_symbol_libs": []
 | 
			
		||||
  },
 | 
			
		||||
  "meta": {
 | 
			
		||||
    "filename": "DE-Timer.kicad_pro",
 | 
			
		||||
    "version": 1
 | 
			
		||||
  },
 | 
			
		||||
  "net_settings": {
 | 
			
		||||
    "classes": [
 | 
			
		||||
      {
 | 
			
		||||
        "bus_width": 12.0,
 | 
			
		||||
        "clearance": 0.1,
 | 
			
		||||
        "diff_pair_gap": 0.25,
 | 
			
		||||
        "diff_pair_via_gap": 0.25,
 | 
			
		||||
        "diff_pair_width": 0.2,
 | 
			
		||||
        "line_style": 0,
 | 
			
		||||
        "microvia_diameter": 0.3,
 | 
			
		||||
        "microvia_drill": 0.1,
 | 
			
		||||
        "name": "Default",
 | 
			
		||||
        "pcb_color": "rgba(0, 0, 0, 0.000)",
 | 
			
		||||
        "schematic_color": "rgba(0, 0, 0, 0.000)",
 | 
			
		||||
        "track_width": 0.25,
 | 
			
		||||
        "via_diameter": 0.8,
 | 
			
		||||
        "via_drill": 0.4,
 | 
			
		||||
        "wire_width": 6.0
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "meta": {
 | 
			
		||||
      "version": 2
 | 
			
		||||
    },
 | 
			
		||||
    "net_colors": null
 | 
			
		||||
  },
 | 
			
		||||
  "pcbnew": {
 | 
			
		||||
    "last_paths": {
 | 
			
		||||
      "gencad": "",
 | 
			
		||||
      "idf": "",
 | 
			
		||||
      "netlist": "",
 | 
			
		||||
      "specctra_dsn": "",
 | 
			
		||||
      "step": "",
 | 
			
		||||
      "vrml": ""
 | 
			
		||||
    },
 | 
			
		||||
    "page_layout_descr_file": ""
 | 
			
		||||
  },
 | 
			
		||||
  "schematic": {
 | 
			
		||||
    "annotate_start_num": 0,
 | 
			
		||||
    "drawing": {
 | 
			
		||||
      "default_line_thickness": 6.0,
 | 
			
		||||
      "default_text_size": 50.0,
 | 
			
		||||
      "field_names": [],
 | 
			
		||||
      "intersheets_ref_own_page": false,
 | 
			
		||||
      "intersheets_ref_prefix": "",
 | 
			
		||||
      "intersheets_ref_short": false,
 | 
			
		||||
      "intersheets_ref_show": false,
 | 
			
		||||
      "intersheets_ref_suffix": "",
 | 
			
		||||
      "junction_size_choice": 3,
 | 
			
		||||
      "label_size_ratio": 0.375,
 | 
			
		||||
      "pin_symbol_size": 25.0,
 | 
			
		||||
      "text_offset_ratio": 0.15
 | 
			
		||||
    },
 | 
			
		||||
    "legacy_lib_dir": "",
 | 
			
		||||
    "legacy_lib_list": [],
 | 
			
		||||
    "meta": {
 | 
			
		||||
      "version": 1
 | 
			
		||||
    },
 | 
			
		||||
    "net_format_name": "",
 | 
			
		||||
    "ngspice": {
 | 
			
		||||
      "fix_include_paths": true,
 | 
			
		||||
      "fix_passive_vals": false,
 | 
			
		||||
      "meta": {
 | 
			
		||||
        "version": 0
 | 
			
		||||
      },
 | 
			
		||||
      "model_mode": 0,
 | 
			
		||||
      "workbook_filename": ""
 | 
			
		||||
    },
 | 
			
		||||
    "page_layout_descr_file": "",
 | 
			
		||||
    "plot_directory": "",
 | 
			
		||||
    "spice_adjust_passive_values": false,
 | 
			
		||||
    "spice_external_command": "spice \"%I\"",
 | 
			
		||||
    "subpart_first_id": 65,
 | 
			
		||||
    "subpart_id_separator": 0
 | 
			
		||||
  },
 | 
			
		||||
  "sheets": [
 | 
			
		||||
    [
 | 
			
		||||
      "1a6d2848-e78e-49fe-8978-e1890f07836f",
 | 
			
		||||
      ""
 | 
			
		||||
    ]
 | 
			
		||||
  ],
 | 
			
		||||
  "text_variables": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3171
									
								
								Hardware/DE-Timer.kicad_sch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3171
									
								
								Hardware/DE-Timer.kicad_sch
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										90
									
								
								Hardware/Ebyte_RF.kicad_sym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								Hardware/Ebyte_RF.kicad_sym
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
 | 
			
		||||
  (symbol "E220-900T22D" (in_bom yes) (on_board yes)
 | 
			
		||||
    (property "Reference" "U" (id 0) (at 7.62 1.27 0)
 | 
			
		||||
      (effects (font (size 1.27 1.27)))
 | 
			
		||||
    )
 | 
			
		||||
    (property "Value" "E220-900T22D" (id 1) (at 13.97 -1.27 0)
 | 
			
		||||
      (effects (font (size 1.27 1.27)))
 | 
			
		||||
    )
 | 
			
		||||
    (property "Footprint" "" (id 2) (at 0 0 0)
 | 
			
		||||
      (effects (font (size 1.27 1.27)) hide)
 | 
			
		||||
    )
 | 
			
		||||
    (property "Datasheet" "" (id 3) (at 0 0 0)
 | 
			
		||||
      (effects (font (size 1.27 1.27)) hide)
 | 
			
		||||
    )
 | 
			
		||||
    (symbol "E220-900T22D_0_1"
 | 
			
		||||
      (rectangle (start -5.08 -2.54) (end 12.7 -20.32)
 | 
			
		||||
        (stroke (width 0) (type default) (color 0 0 0 0))
 | 
			
		||||
        (fill (type background))
 | 
			
		||||
      )
 | 
			
		||||
      (polyline
 | 
			
		||||
        (pts
 | 
			
		||||
          (xy 15.24 -15.24)
 | 
			
		||||
          (xy 13.97 -13.97)
 | 
			
		||||
        )
 | 
			
		||||
        (stroke (width 0) (type default) (color 0 0 0 0))
 | 
			
		||||
        (fill (type none))
 | 
			
		||||
      )
 | 
			
		||||
      (polyline
 | 
			
		||||
        (pts
 | 
			
		||||
          (xy 15.24 -15.24)
 | 
			
		||||
          (xy 15.24 -13.97)
 | 
			
		||||
        )
 | 
			
		||||
        (stroke (width 0) (type default) (color 0 0 0 0))
 | 
			
		||||
        (fill (type none))
 | 
			
		||||
      )
 | 
			
		||||
      (polyline
 | 
			
		||||
        (pts
 | 
			
		||||
          (xy 12.7 -17.78)
 | 
			
		||||
          (xy 15.24 -17.78)
 | 
			
		||||
          (xy 15.24 -15.24)
 | 
			
		||||
          (xy 16.51 -13.97)
 | 
			
		||||
        )
 | 
			
		||||
        (stroke (width 0) (type default) (color 0 0 0 0))
 | 
			
		||||
        (fill (type none))
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
    (symbol "E220-900T22D_1_1"
 | 
			
		||||
      (pin input line (at -7.62 -6.35 0) (length 2.54)
 | 
			
		||||
        (name "M0" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "1" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin unspecified line (at 10.16 -22.86 90) (length 2.54)
 | 
			
		||||
        (name "mount" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "10" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin input line (at -7.62 -8.89 0) (length 2.54)
 | 
			
		||||
        (name "M1" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "2" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin input line (at -7.62 -11.43 0) (length 2.54)
 | 
			
		||||
        (name "RXD" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "3" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin output line (at -7.62 -13.97 0) (length 2.54)
 | 
			
		||||
        (name "TXD" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "4" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin output line (at -7.62 -16.51 0) (length 2.54)
 | 
			
		||||
        (name "AUX" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "5" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin power_in line (at 3.81 0 270) (length 2.54)
 | 
			
		||||
        (name "VCC" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "6" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin power_in line (at 1.27 -22.86 90) (length 2.54)
 | 
			
		||||
        (name "GND" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "7" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin unspecified line (at 5.08 -22.86 90) (length 2.54)
 | 
			
		||||
        (name "mount" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "8" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
      (pin unspecified line (at 7.62 -22.86 90) (length 2.54)
 | 
			
		||||
        (name "mount" (effects (font (size 1.27 1.27))))
 | 
			
		||||
        (number "9" (effects (font (size 1.27 1.27))))
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										25
									
								
								Hardware/Ebyte_RF.pretty/E220-900T22D.kicad_mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Hardware/Ebyte_RF.pretty/E220-900T22D.kicad_mod
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
(footprint "E220-900T22D" (version 20211014) (generator pcbnew)
 | 
			
		||||
  (layer "F.Cu")
 | 
			
		||||
  (tedit 62E17D77)
 | 
			
		||||
  (attr through_hole)
 | 
			
		||||
  (fp_text reference "REF**" (at 0 -12.93 unlocked) (layer "F.SilkS")
 | 
			
		||||
    (effects (font (size 1 1) (thickness 0.15)))
 | 
			
		||||
    (tstamp 4c9fdea7-ba0c-45cc-8f66-240980c37d5c)
 | 
			
		||||
  )
 | 
			
		||||
  (fp_text value "E220-900T22D" (at 0 -11.43 unlocked) (layer "F.Fab")
 | 
			
		||||
    (effects (font (size 1 1) (thickness 0.15)))
 | 
			
		||||
    (tstamp c58960d9-4cac-4036-ad2e-1aef26946dae)
 | 
			
		||||
  )
 | 
			
		||||
  (fp_rect (start 18 2) (end 30.5 8.2) (layer "F.SilkS") (width 0.12) (fill none) (tstamp 477892a1-722e-4cda-bb6c-fcdb8ba5f93e))
 | 
			
		||||
  (fp_rect (start -18 -10.5) (end 18 10.5) (layer "F.SilkS") (width 0.12) (fill none) (tstamp 563c12e4-8f8c-446c-a11f-94f5aa93b994))
 | 
			
		||||
  (pad "1" thru_hole oval (at -16.5 7.62) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp cbc539d2-6a10-4052-9b7a-f10326dcac67))
 | 
			
		||||
  (pad "2" thru_hole oval (at -16.5 5.08) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 936e2ca6-11ae-4f42-9128-52bb329f3d21))
 | 
			
		||||
  (pad "3" thru_hole oval (at -16.5 2.54) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 0c30a4be-5679-499f-8c5b-5f3024f9d6cf))
 | 
			
		||||
  (pad "4" thru_hole oval (at -16.5 0) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 2f3deced-880d-4075-a81b-95c62da5b94d))
 | 
			
		||||
  (pad "5" thru_hole oval (at -16.5 -2.54) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 7e08f2a4-63d6-468b-bd8b-ec607077e023))
 | 
			
		||||
  (pad "6" thru_hole oval (at -16.5 -5.08) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp a06e8e78-f567-42e6-b645-013b1073ca31))
 | 
			
		||||
  (pad "7" thru_hole oval (at -16.5 -7.62) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 53c85970-3e21-4fae-a84f-721cfc0513b5))
 | 
			
		||||
  (pad "8" thru_hole oval (at 15 -1.92) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 065b9982-55f2-4822-977e-07e8a06e7b35))
 | 
			
		||||
  (pad "9" thru_hole oval (at 15 -4.46) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 70fb572d-d5ec-41e7-9482-63d4578b4f47))
 | 
			
		||||
  (pad "10" thru_hole oval (at 15 -7) (size 2.54 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 0f31f11f-c374-4640-b9a4-07bbdba8d354))
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										3
									
								
								Hardware/fp-lib-table
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Hardware/fp-lib-table
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(fp_lib_table
 | 
			
		||||
  (lib (name "Ebyte_RF")(type "KiCad")(uri "${KIPRJMOD}/Ebyte_RF.pretty")(options "")(descr ""))
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										3
									
								
								Hardware/sym-lib-table
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Hardware/sym-lib-table
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(sym_lib_table
 | 
			
		||||
  (lib (name "Ebyte_RF")(type "KiCad")(uri "${KIPRJMOD}/Ebyte_RF.kicad_sym")(options "")(descr ""))
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										7
									
								
								Software/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Software/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
data/
 | 
			
		||||
.pio
 | 
			
		||||
.vscode/.browse.c_cpp.db*
 | 
			
		||||
.vscode/c_cpp_properties.json
 | 
			
		||||
.vscode/launch.json
 | 
			
		||||
.vscode/ipch
 | 
			
		||||
wifi_credentials.ini
 | 
			
		||||
@@ -3,5 +3,8 @@
 | 
			
		||||
    // for the documentation about the extensions.json format
 | 
			
		||||
    "recommendations": [
 | 
			
		||||
        "platformio.platformio-ide"
 | 
			
		||||
    ],
 | 
			
		||||
    "unwantedRecommendations": [
 | 
			
		||||
        "ms-vscode.cpptools-extension-pack"
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								Software/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								Software/README.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										8
									
								
								Software/git_rev_macro.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Software/git_rev_macro.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
revision = (
 | 
			
		||||
    subprocess.check_output(["git", "rev-parse", "--short=10", "HEAD"])
 | 
			
		||||
    .strip()
 | 
			
		||||
    .decode("utf-8")
 | 
			
		||||
)
 | 
			
		||||
print("-DGIT_REV='\"%s\"'" % revision)
 | 
			
		||||
							
								
								
									
										2
									
								
								Software/lib/ESP Async WebServer/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								Software/lib/ESP Async WebServer/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
.vscode
 | 
			
		||||
.DS_Store
 | 
			
		||||
							
								
								
									
										1
									
								
								Software/lib/ESP Async WebServer/.piopm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Software/lib/ESP Async WebServer/.piopm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{"type": "library", "name": "ESP Async WebServer", "version": "1.2.3", "spec": {"owner": "me-no-dev", "id": 306, "name": "ESP Async WebServer", "requirements": null, "uri": null}}
 | 
			
		||||
							
								
								
									
										46
									
								
								Software/lib/ESP Async WebServer/.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Software/lib/ESP Async WebServer/.travis.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
sudo: false
 | 
			
		||||
 | 
			
		||||
language: python
 | 
			
		||||
 | 
			
		||||
os:
 | 
			
		||||
  - linux
 | 
			
		||||
 | 
			
		||||
git:
 | 
			
		||||
  depth: false
 | 
			
		||||
 | 
			
		||||
stages:
 | 
			
		||||
  - build
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  include:
 | 
			
		||||
 | 
			
		||||
    - name: "Build Arduino ESP32"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32
 | 
			
		||||
 | 
			
		||||
    - name: "Build Arduino ESP8266"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp8266
 | 
			
		||||
 | 
			
		||||
    - name: "Build Platformio ESP32"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32 1 1
 | 
			
		||||
 | 
			
		||||
    - name: "Build Platformio ESP8266"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp8266 1 1
 | 
			
		||||
 | 
			
		||||
notifications:
 | 
			
		||||
  email:
 | 
			
		||||
    on_success: change
 | 
			
		||||
    on_failure: change
 | 
			
		||||
  webhooks:
 | 
			
		||||
    urls:
 | 
			
		||||
      - https://webhooks.gitter.im/e/60e65d0c78ea0a920347
 | 
			
		||||
    on_success: change  # options: [always|never|change] default: always
 | 
			
		||||
    on_failure: always  # options: [always|never|change] default: always
 | 
			
		||||
    on_start: never     # options: [always|never|change] default: always
 | 
			
		||||
							
								
								
									
										17
									
								
								Software/lib/ESP Async WebServer/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Software/lib/ESP Async WebServer/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
set(COMPONENT_SRCDIRS
 | 
			
		||||
    "src"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(COMPONENT_ADD_INCLUDEDIRS
 | 
			
		||||
    "src"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(COMPONENT_REQUIRES
 | 
			
		||||
    "arduino-esp32"
 | 
			
		||||
    "AsyncTCP"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
register_component()
 | 
			
		||||
 | 
			
		||||
target_compile_definitions(${COMPONENT_TARGET} PUBLIC -DESP32)
 | 
			
		||||
target_compile_options(${COMPONENT_TARGET} PRIVATE -fno-rtti)
 | 
			
		||||
							
								
								
									
										1486
									
								
								Software/lib/ESP Async WebServer/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1486
									
								
								Software/lib/ESP Async WebServer/README.md
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								Software/lib/ESP Async WebServer/_config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Software/lib/ESP Async WebServer/_config.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
theme: jekyll-theme-cayman
 | 
			
		||||
							
								
								
									
										3
									
								
								Software/lib/ESP Async WebServer/component.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Software/lib/ESP Async WebServer/component.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
COMPONENT_ADD_INCLUDEDIRS := src
 | 
			
		||||
COMPONENT_SRCDIRS := src
 | 
			
		||||
CXXFLAGS += -fno-rtti
 | 
			
		||||
							
								
								
									
										3
									
								
								Software/lib/ESP Async WebServer/keywords.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								Software/lib/ESP Async WebServer/keywords.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
JsonArray	KEYWORD1
 | 
			
		||||
add	KEYWORD2
 | 
			
		||||
createArray	KEYWORD3
 | 
			
		||||
							
								
								
									
										33
									
								
								Software/lib/ESP Async WebServer/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Software/lib/ESP Async WebServer/library.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
{
 | 
			
		||||
  "name":"ESP Async WebServer",
 | 
			
		||||
  "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32",
 | 
			
		||||
  "keywords":"http,async,websocket,webserver",
 | 
			
		||||
  "authors":
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Hristo Gochkov",
 | 
			
		||||
    "maintainer": true
 | 
			
		||||
  },
 | 
			
		||||
  "repository":
 | 
			
		||||
  {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git"
 | 
			
		||||
  },
 | 
			
		||||
  "version": "1.2.3",
 | 
			
		||||
  "license": "LGPL-3.0",
 | 
			
		||||
  "frameworks": "arduino",
 | 
			
		||||
  "platforms": ["espressif8266", "espressif32"],
 | 
			
		||||
  "dependencies": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "ESPAsyncTCP",
 | 
			
		||||
      "platforms": "espressif8266"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "AsyncTCP",
 | 
			
		||||
      "platforms": "espressif32"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Hash",
 | 
			
		||||
      "platforms": "espressif8266"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								Software/lib/ESP Async WebServer/library.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Software/lib/ESP Async WebServer/library.properties
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
name=ESP Async WebServer
 | 
			
		||||
version=1.2.3
 | 
			
		||||
author=Me-No-Dev
 | 
			
		||||
maintainer=Me-No-Dev
 | 
			
		||||
sentence=Async Web Server for ESP8266 and ESP31B
 | 
			
		||||
paragraph=Async Web Server for ESP8266 and ESP31B
 | 
			
		||||
category=Other
 | 
			
		||||
url=https://github.com/me-no-dev/ESPAsyncWebServer
 | 
			
		||||
architectures=*
 | 
			
		||||
							
								
								
									
										363
									
								
								Software/lib/ESP Async WebServer/src/AsyncEventSource.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								Software/lib/ESP Async WebServer/src/AsyncEventSource.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,363 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#include "Arduino.h"
 | 
			
		||||
#include "AsyncEventSource.h"
 | 
			
		||||
 | 
			
		||||
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
 | 
			
		||||
  String ev = "";
 | 
			
		||||
 | 
			
		||||
  if(reconnect){
 | 
			
		||||
    ev += "retry: ";
 | 
			
		||||
    ev += String(reconnect);
 | 
			
		||||
    ev += "\r\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(id){
 | 
			
		||||
    ev += "id: ";
 | 
			
		||||
    ev += String(id);
 | 
			
		||||
    ev += "\r\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(event != NULL){
 | 
			
		||||
    ev += "event: ";
 | 
			
		||||
    ev += String(event);
 | 
			
		||||
    ev += "\r\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(message != NULL){
 | 
			
		||||
    size_t messageLen = strlen(message);
 | 
			
		||||
    char * lineStart = (char *)message;
 | 
			
		||||
    char * lineEnd;
 | 
			
		||||
    do {
 | 
			
		||||
      char * nextN = strchr(lineStart, '\n');
 | 
			
		||||
      char * nextR = strchr(lineStart, '\r');
 | 
			
		||||
      if(nextN == NULL && nextR == NULL){
 | 
			
		||||
        size_t llen = ((char *)message + messageLen) - lineStart;
 | 
			
		||||
        char * ldata = (char *)malloc(llen+1);
 | 
			
		||||
        if(ldata != NULL){
 | 
			
		||||
          memcpy(ldata, lineStart, llen);
 | 
			
		||||
          ldata[llen] = 0;
 | 
			
		||||
          ev += "data: ";
 | 
			
		||||
          ev += ldata;
 | 
			
		||||
          ev += "\r\n\r\n";
 | 
			
		||||
          free(ldata);
 | 
			
		||||
        }
 | 
			
		||||
        lineStart = (char *)message + messageLen;
 | 
			
		||||
      } else {
 | 
			
		||||
        char * nextLine = NULL;
 | 
			
		||||
        if(nextN != NULL && nextR != NULL){
 | 
			
		||||
          if(nextR < nextN){
 | 
			
		||||
            lineEnd = nextR;
 | 
			
		||||
            if(nextN == (nextR + 1))
 | 
			
		||||
              nextLine = nextN + 1;
 | 
			
		||||
            else
 | 
			
		||||
              nextLine = nextR + 1;
 | 
			
		||||
          } else {
 | 
			
		||||
            lineEnd = nextN;
 | 
			
		||||
            if(nextR == (nextN + 1))
 | 
			
		||||
              nextLine = nextR + 1;
 | 
			
		||||
            else
 | 
			
		||||
              nextLine = nextN + 1;
 | 
			
		||||
          }
 | 
			
		||||
        } else if(nextN != NULL){
 | 
			
		||||
          lineEnd = nextN;
 | 
			
		||||
          nextLine = nextN + 1;
 | 
			
		||||
        } else {
 | 
			
		||||
          lineEnd = nextR;
 | 
			
		||||
          nextLine = nextR + 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        size_t llen = lineEnd - lineStart;
 | 
			
		||||
        char * ldata = (char *)malloc(llen+1);
 | 
			
		||||
        if(ldata != NULL){
 | 
			
		||||
          memcpy(ldata, lineStart, llen);
 | 
			
		||||
          ldata[llen] = 0;
 | 
			
		||||
          ev += "data: ";
 | 
			
		||||
          ev += ldata;
 | 
			
		||||
          ev += "\r\n";
 | 
			
		||||
          free(ldata);
 | 
			
		||||
        }
 | 
			
		||||
        lineStart = nextLine;
 | 
			
		||||
        if(lineStart == ((char *)message + messageLen))
 | 
			
		||||
          ev += "\r\n";
 | 
			
		||||
      }
 | 
			
		||||
    } while(lineStart < ((char *)message + messageLen));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len)
 | 
			
		||||
: _data(nullptr), _len(len), _sent(0), _acked(0)
 | 
			
		||||
{
 | 
			
		||||
  _data = (uint8_t*)malloc(_len+1);
 | 
			
		||||
  if(_data == nullptr){
 | 
			
		||||
    _len = 0;
 | 
			
		||||
  } else {
 | 
			
		||||
    memcpy(_data, data, len);
 | 
			
		||||
    _data[_len] = 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceMessage::~AsyncEventSourceMessage() {
 | 
			
		||||
     if(_data != NULL)
 | 
			
		||||
        free(_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
 | 
			
		||||
  // If the whole message is now acked...
 | 
			
		||||
  if(_acked + len > _len){
 | 
			
		||||
     // Return the number of extra bytes acked (they will be carried on to the next message)
 | 
			
		||||
     const size_t extra = _acked + len - _len;
 | 
			
		||||
     _acked = _len;
 | 
			
		||||
     return extra;
 | 
			
		||||
  }
 | 
			
		||||
  // Return that no extra bytes left.
 | 
			
		||||
  _acked += len;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSourceMessage::send(AsyncClient *client) {
 | 
			
		||||
  const size_t len = _len - _sent;
 | 
			
		||||
  if(client->space() < len){
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  size_t sent = client->add((const char *)_data, len);
 | 
			
		||||
  if(client->canSend())
 | 
			
		||||
    client->send();
 | 
			
		||||
  _sent += sent;
 | 
			
		||||
  return sent; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server)
 | 
			
		||||
: _messageQueue(LinkedList<AsyncEventSourceMessage *>([](AsyncEventSourceMessage *m){ delete  m; }))
 | 
			
		||||
{
 | 
			
		||||
  _client = request->client();
 | 
			
		||||
  _server = server;
 | 
			
		||||
  _lastId = 0;
 | 
			
		||||
  if(request->hasHeader("Last-Event-ID"))
 | 
			
		||||
    _lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str());
 | 
			
		||||
    
 | 
			
		||||
  _client->setRxTimeout(0);
 | 
			
		||||
  _client->onError(NULL, NULL);
 | 
			
		||||
  _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
 | 
			
		||||
  _client->onPoll([](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onPoll(); }, this);
 | 
			
		||||
  _client->onData(NULL, NULL);
 | 
			
		||||
  _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this);
 | 
			
		||||
  _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this);
 | 
			
		||||
 | 
			
		||||
  _server->_addClient(this);
 | 
			
		||||
  delete request;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceClient::~AsyncEventSourceClient(){
 | 
			
		||||
   _messageQueue.free();
 | 
			
		||||
  close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
 | 
			
		||||
  if(dataMessage == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
  if(!connected()){
 | 
			
		||||
    delete dataMessage;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _messageQueue.add(dataMessage);
 | 
			
		||||
 | 
			
		||||
  _runQueue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
 | 
			
		||||
  while(len && !_messageQueue.isEmpty()){
 | 
			
		||||
    len = _messageQueue.front()->ack(len, time);
 | 
			
		||||
    if(_messageQueue.front()->finished())
 | 
			
		||||
      _messageQueue.remove(_messageQueue.front());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _runQueue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onPoll(){
 | 
			
		||||
  if(!_messageQueue.isEmpty()){
 | 
			
		||||
    _runQueue();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
 | 
			
		||||
  _client->close(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onDisconnect(){
 | 
			
		||||
  _client = NULL;
 | 
			
		||||
  _server->_handleDisconnect(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::close(){
 | 
			
		||||
  if(_client != NULL)
 | 
			
		||||
    _client->close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::write(const char * message, size_t len){
 | 
			
		||||
  _queueMessage(new AsyncEventSourceMessage(message, len));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
 | 
			
		||||
  String ev = generateEventMessage(message, event, id, reconnect);
 | 
			
		||||
  _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_runQueue(){
 | 
			
		||||
  while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
 | 
			
		||||
    _messageQueue.remove(_messageQueue.front());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
 | 
			
		||||
  {
 | 
			
		||||
    if(!(*i)->sent())
 | 
			
		||||
      (*i)->send(_client);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Handler
 | 
			
		||||
 | 
			
		||||
AsyncEventSource::AsyncEventSource(const String& url)
 | 
			
		||||
  : _url(url)
 | 
			
		||||
  , _clients(LinkedList<AsyncEventSourceClient *>([](AsyncEventSourceClient *c){ delete c; }))
 | 
			
		||||
  , _connectcb(NULL)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
AsyncEventSource::~AsyncEventSource(){
 | 
			
		||||
  close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::onConnect(ArEventHandlerFunction cb){
 | 
			
		||||
  _connectcb = cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
 | 
			
		||||
  /*char * temp = (char *)malloc(2054);
 | 
			
		||||
  if(temp != NULL){
 | 
			
		||||
    memset(temp+1,' ',2048);
 | 
			
		||||
    temp[0] = ':';
 | 
			
		||||
    temp[2049] = '\r';
 | 
			
		||||
    temp[2050] = '\n';
 | 
			
		||||
    temp[2051] = '\r';
 | 
			
		||||
    temp[2052] = '\n';
 | 
			
		||||
    temp[2053] = 0;
 | 
			
		||||
    client->write((const char *)temp, 2053);
 | 
			
		||||
    free(temp);
 | 
			
		||||
  }*/
 | 
			
		||||
  
 | 
			
		||||
  _clients.add(client);
 | 
			
		||||
  if(_connectcb)
 | 
			
		||||
    _connectcb(client);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
 | 
			
		||||
  _clients.remove(client);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::close(){
 | 
			
		||||
  for(const auto &c: _clients){
 | 
			
		||||
    if(c->connected())
 | 
			
		||||
      c->close();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pmb fix
 | 
			
		||||
size_t AsyncEventSource::avgPacketsWaiting() const {
 | 
			
		||||
  if(_clients.isEmpty())
 | 
			
		||||
    return 0;
 | 
			
		||||
  
 | 
			
		||||
  size_t    aql=0;
 | 
			
		||||
  uint32_t  nConnectedClients=0;
 | 
			
		||||
  
 | 
			
		||||
  for(const auto &c: _clients){
 | 
			
		||||
    if(c->connected()) {
 | 
			
		||||
      aql+=c->packetsWaiting();
 | 
			
		||||
      ++nConnectedClients;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
//  return aql / nConnectedClients;
 | 
			
		||||
  return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  String ev = generateEventMessage(message, event, id, reconnect);
 | 
			
		||||
  for(const auto &c: _clients){
 | 
			
		||||
    if(c->connected()) {
 | 
			
		||||
      c->write(ev.c_str(), ev.length());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSource::count() const {
 | 
			
		||||
  return _clients.count_if([](AsyncEventSourceClient *c){
 | 
			
		||||
    return c->connected();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
 | 
			
		||||
  if(request->method() != HTTP_GET || !request->url().equals(_url)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  request->addInterestingHeader("Last-Event-ID");
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
 | 
			
		||||
  if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
 | 
			
		||||
    return request->requestAuthentication();
 | 
			
		||||
  request->send(new AsyncEventSourceResponse(this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Response
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
 | 
			
		||||
  _server = server;
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _contentType = "text/event-stream";
 | 
			
		||||
  _sendContentLength = false;
 | 
			
		||||
  addHeader("Cache-Control", "no-cache");
 | 
			
		||||
  addHeader("Connection","keep-alive");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
 | 
			
		||||
  String out = _assembleHead(request->version());
 | 
			
		||||
  request->client()->write(out.c_str(), _headLength);
 | 
			
		||||
  _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){
 | 
			
		||||
  if(len){
 | 
			
		||||
    new AsyncEventSourceClient(request, _server);
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										116
									
								
								Software/lib/ESP Async WebServer/src/AsyncEventSource.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								Software/lib/ESP Async WebServer/src/AsyncEventSource.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ASYNCEVENTSOURCE_H_
 | 
			
		||||
#define ASYNCEVENTSOURCE_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
class AsyncEventSource;
 | 
			
		||||
class AsyncEventSourceResponse;
 | 
			
		||||
class AsyncEventSourceClient;
 | 
			
		||||
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
 | 
			
		||||
 | 
			
		||||
class AsyncEventSourceMessage {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t * _data; 
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    size_t _sent;
 | 
			
		||||
    //size_t _ack;
 | 
			
		||||
    size_t _acked; 
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncEventSourceMessage(const char * data, size_t len);
 | 
			
		||||
    ~AsyncEventSourceMessage();
 | 
			
		||||
    size_t ack(size_t len, uint32_t time __attribute__((unused)));
 | 
			
		||||
    size_t send(AsyncClient *client);
 | 
			
		||||
    bool finished(){ return _acked == _len; }
 | 
			
		||||
    bool sent() { return _sent == _len; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncEventSourceClient {
 | 
			
		||||
  private:
 | 
			
		||||
    AsyncClient *_client;
 | 
			
		||||
    AsyncEventSource *_server;
 | 
			
		||||
    uint32_t _lastId;
 | 
			
		||||
    LinkedList<AsyncEventSourceMessage *> _messageQueue;
 | 
			
		||||
    void _queueMessage(AsyncEventSourceMessage *dataMessage);
 | 
			
		||||
    void _runQueue();
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server);
 | 
			
		||||
    ~AsyncEventSourceClient();
 | 
			
		||||
 | 
			
		||||
    AsyncClient* client(){ return _client; }
 | 
			
		||||
    void close();
 | 
			
		||||
    void write(const char * message, size_t len);
 | 
			
		||||
    void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
 | 
			
		||||
    bool connected() const { return (_client != NULL) && _client->connected(); }
 | 
			
		||||
    uint32_t lastId() const { return _lastId; }
 | 
			
		||||
    size_t  packetsWaiting() const { return _messageQueue.length(); }
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    void _onAck(size_t len, uint32_t time);
 | 
			
		||||
    void _onPoll(); 
 | 
			
		||||
    void _onTimeout(uint32_t time);
 | 
			
		||||
    void _onDisconnect();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncEventSource: public AsyncWebHandler {
 | 
			
		||||
  private:
 | 
			
		||||
    String _url;
 | 
			
		||||
    LinkedList<AsyncEventSourceClient *> _clients;
 | 
			
		||||
    ArEventHandlerFunction _connectcb;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncEventSource(const String& url);
 | 
			
		||||
    ~AsyncEventSource();
 | 
			
		||||
 | 
			
		||||
    const char * url() const { return _url.c_str(); }
 | 
			
		||||
    void close();
 | 
			
		||||
    void onConnect(ArEventHandlerFunction cb);
 | 
			
		||||
    void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
 | 
			
		||||
    size_t count() const; //number clinets connected
 | 
			
		||||
    size_t  avgPacketsWaiting() const;
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    void _addClient(AsyncEventSourceClient * client);
 | 
			
		||||
    void _handleDisconnect(AsyncEventSourceClient * client);
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncEventSourceResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _content;
 | 
			
		||||
    AsyncEventSource *_server;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncEventSourceResponse(AsyncEventSource *server);
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCEVENTSOURCE_H_ */
 | 
			
		||||
							
								
								
									
										252
									
								
								Software/lib/ESP Async WebServer/src/AsyncJson.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								Software/lib/ESP Async WebServer/src/AsyncJson.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,252 @@
 | 
			
		||||
// AsyncJson.h
 | 
			
		||||
/*
 | 
			
		||||
  Async Response to use with ArduinoJson and AsyncWebServer
 | 
			
		||||
  Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
 | 
			
		||||
 | 
			
		||||
  Example of callback in use
 | 
			
		||||
 | 
			
		||||
   server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
 | 
			
		||||
 | 
			
		||||
    AsyncJsonResponse * response = new AsyncJsonResponse();
 | 
			
		||||
    JsonObject& root = response->getRoot();
 | 
			
		||||
    root["key1"] = "key number one";
 | 
			
		||||
    JsonObject& nested = root.createNestedObject("nested");
 | 
			
		||||
    nested["key1"] = "key number one";
 | 
			
		||||
 | 
			
		||||
    response->setLength();
 | 
			
		||||
    request->send(response);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  --------------------
 | 
			
		||||
 | 
			
		||||
  Async Request to use with ArduinoJson and AsyncWebServer
 | 
			
		||||
  Written by Arsène von Wyss (avonwyss)
 | 
			
		||||
 | 
			
		||||
  Example
 | 
			
		||||
 | 
			
		||||
  AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
 | 
			
		||||
  handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
 | 
			
		||||
    JsonObject& jsonObj = json.as<JsonObject>();
 | 
			
		||||
    // ...
 | 
			
		||||
  });
 | 
			
		||||
  server.addHandler(handler);
 | 
			
		||||
  
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ASYNC_JSON_H_
 | 
			
		||||
#define ASYNC_JSON_H_
 | 
			
		||||
#include <ArduinoJson.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include <Print.h>
 | 
			
		||||
 | 
			
		||||
#if ARDUINOJSON_VERSION_MAJOR == 5
 | 
			
		||||
  #define ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
#else
 | 
			
		||||
  #define DYNAMIC_JSON_DOCUMENT_SIZE  1024
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
constexpr const char* JSON_MIMETYPE = "application/json";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Json Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class ChunkPrint : public Print {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t* _destination;
 | 
			
		||||
    size_t _to_skip;
 | 
			
		||||
    size_t _to_write;
 | 
			
		||||
    size_t _pos;
 | 
			
		||||
  public:
 | 
			
		||||
    ChunkPrint(uint8_t* destination, size_t from, size_t len)
 | 
			
		||||
      : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
 | 
			
		||||
    virtual ~ChunkPrint(){}
 | 
			
		||||
    size_t write(uint8_t c){
 | 
			
		||||
      if (_to_skip > 0) {
 | 
			
		||||
        _to_skip--;
 | 
			
		||||
        return 1;
 | 
			
		||||
      } else if (_to_write > 0) {
 | 
			
		||||
        _to_write--;
 | 
			
		||||
        _destination[_pos++] = c;
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    size_t write(const uint8_t *buffer, size_t size)
 | 
			
		||||
    {
 | 
			
		||||
      return this->Print::write(buffer, size);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncJsonResponse: public AsyncAbstractResponse {
 | 
			
		||||
  protected:
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
    DynamicJsonBuffer _jsonBuffer;
 | 
			
		||||
#else
 | 
			
		||||
    DynamicJsonDocument _jsonBuffer;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    JsonVariant _root;
 | 
			
		||||
    bool _isValid;
 | 
			
		||||
 | 
			
		||||
  public:    
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
    AsyncJsonResponse(bool isArray=false): _isValid{false} {
 | 
			
		||||
      _code = 200;
 | 
			
		||||
      _contentType = JSON_MIMETYPE;
 | 
			
		||||
      if(isArray)
 | 
			
		||||
        _root = _jsonBuffer.createArray();
 | 
			
		||||
      else
 | 
			
		||||
        _root = _jsonBuffer.createObject();
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
 | 
			
		||||
      _code = 200;
 | 
			
		||||
      _contentType = JSON_MIMETYPE;
 | 
			
		||||
      if(isArray)
 | 
			
		||||
        _root = _jsonBuffer.createNestedArray();
 | 
			
		||||
      else
 | 
			
		||||
        _root = _jsonBuffer.createNestedObject();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ~AsyncJsonResponse() {}
 | 
			
		||||
    JsonVariant & getRoot() { return _root; }
 | 
			
		||||
    bool _sourceValid() const { return _isValid; }
 | 
			
		||||
    size_t setLength() {
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY      
 | 
			
		||||
      _contentLength = _root.measureLength();
 | 
			
		||||
#else
 | 
			
		||||
      _contentLength = measureJson(_root);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
      if (_contentLength) { _isValid = true; }
 | 
			
		||||
      return _contentLength;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   size_t getSize() { return _jsonBuffer.size(); }
 | 
			
		||||
 | 
			
		||||
    size_t _fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
      ChunkPrint dest(data, _sentLength, len);
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY      
 | 
			
		||||
      _root.printTo( dest ) ;
 | 
			
		||||
#else
 | 
			
		||||
      serializeJson(_root, dest);
 | 
			
		||||
#endif
 | 
			
		||||
      return len;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PrettyAsyncJsonResponse: public AsyncJsonResponse {	
 | 
			
		||||
public:
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
	PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {}
 | 
			
		||||
#else
 | 
			
		||||
	PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
 | 
			
		||||
#endif
 | 
			
		||||
	size_t setLength () {
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
		_contentLength = _root.measurePrettyLength ();
 | 
			
		||||
#else
 | 
			
		||||
		_contentLength = measureJsonPretty(_root);
 | 
			
		||||
#endif
 | 
			
		||||
		if (_contentLength) {_isValid = true;}
 | 
			
		||||
		return _contentLength;
 | 
			
		||||
	}
 | 
			
		||||
	size_t _fillBuffer (uint8_t *data, size_t len) {
 | 
			
		||||
		ChunkPrint dest (data, _sentLength, len);
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
		_root.prettyPrintTo (dest);
 | 
			
		||||
#else
 | 
			
		||||
		serializeJsonPretty(_root, dest);
 | 
			
		||||
#endif
 | 
			
		||||
		return len;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request, JsonVariant &json)> ArJsonRequestHandlerFunction;
 | 
			
		||||
 | 
			
		||||
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
 | 
			
		||||
private:
 | 
			
		||||
protected:
 | 
			
		||||
  const String _uri;
 | 
			
		||||
  WebRequestMethodComposite _method;
 | 
			
		||||
  ArJsonRequestHandlerFunction _onRequest;
 | 
			
		||||
  size_t _contentLength;
 | 
			
		||||
#ifndef ARDUINOJSON_5_COMPATIBILITY   
 | 
			
		||||
  const size_t maxJsonBufferSize;
 | 
			
		||||
#endif
 | 
			
		||||
  size_t _maxContentLength;
 | 
			
		||||
public:
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY      
 | 
			
		||||
  AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) 
 | 
			
		||||
  : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
 | 
			
		||||
#else
 | 
			
		||||
  AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE) 
 | 
			
		||||
  : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
 | 
			
		||||
#endif
 | 
			
		||||
  
 | 
			
		||||
  void setMethod(WebRequestMethodComposite method){ _method = method; }
 | 
			
		||||
  void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
 | 
			
		||||
  void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
 | 
			
		||||
 | 
			
		||||
  virtual bool canHandle(AsyncWebServerRequest *request) override final{
 | 
			
		||||
    if(!_onRequest)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if(!(_method & request->method()))
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) )
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    request->addInterestingHeader("ANY");
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  virtual void handleRequest(AsyncWebServerRequest *request) override final {
 | 
			
		||||
    if(_onRequest) {
 | 
			
		||||
      if (request->_tempObject != NULL) {
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY    
 | 
			
		||||
        DynamicJsonBuffer jsonBuffer;
 | 
			
		||||
        JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
 | 
			
		||||
        if (json.success()) {
 | 
			
		||||
#else
 | 
			
		||||
        DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
 | 
			
		||||
        DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
 | 
			
		||||
        if(!error) {
 | 
			
		||||
          JsonVariant json = jsonBuffer.as<JsonVariant>();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
          _onRequest(request, json);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      request->send(_contentLength > _maxContentLength ? 413 : 400);
 | 
			
		||||
    } else {
 | 
			
		||||
      request->send(500);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
 | 
			
		||||
  }
 | 
			
		||||
  virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
 | 
			
		||||
    if (_onRequest) {
 | 
			
		||||
      _contentLength = total;
 | 
			
		||||
      if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
 | 
			
		||||
        request->_tempObject = malloc(total);
 | 
			
		||||
      }
 | 
			
		||||
      if (request->_tempObject != NULL) {
 | 
			
		||||
        memcpy((uint8_t*)(request->_tempObject) + index, data, len);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1299
									
								
								Software/lib/ESP Async WebServer/src/AsyncWebSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1299
									
								
								Software/lib/ESP Async WebServer/src/AsyncWebSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										350
									
								
								Software/lib/ESP Async WebServer/src/AsyncWebSocket.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								Software/lib/ESP Async WebServer/src/AsyncWebSocket.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,350 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ASYNCWEBSOCKET_H_
 | 
			
		||||
#define ASYNCWEBSOCKET_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#define WS_MAX_QUEUED_MESSAGES 32
 | 
			
		||||
#else
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#define WS_MAX_QUEUED_MESSAGES 8
 | 
			
		||||
#endif
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
#include "AsyncWebSynchronization.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ESP8266
 | 
			
		||||
#include <Hash.h>
 | 
			
		||||
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
 | 
			
		||||
#include <../src/Hash.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#define DEFAULT_MAX_WS_CLIENTS 8
 | 
			
		||||
#else
 | 
			
		||||
#define DEFAULT_MAX_WS_CLIENTS 4
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocket;
 | 
			
		||||
class AsyncWebSocketResponse;
 | 
			
		||||
class AsyncWebSocketClient;
 | 
			
		||||
class AsyncWebSocketControl;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    /** Message type as defined by enum AwsFrameType.
 | 
			
		||||
     * Note: Applications will only see WS_TEXT and WS_BINARY.
 | 
			
		||||
     * All other types are handled by the library. */
 | 
			
		||||
    uint8_t message_opcode;
 | 
			
		||||
    /** Frame number of a fragmented message. */
 | 
			
		||||
    uint32_t num;
 | 
			
		||||
    /** Is this the last frame in a fragmented message ?*/
 | 
			
		||||
    uint8_t final;
 | 
			
		||||
    /** Is this frame masked? */
 | 
			
		||||
    uint8_t masked;
 | 
			
		||||
    /** Message type as defined by enum AwsFrameType.
 | 
			
		||||
     * This value is the same as message_opcode for non-fragmented
 | 
			
		||||
     * messages, but may also be WS_CONTINUATION in a fragmented message. */
 | 
			
		||||
    uint8_t opcode;
 | 
			
		||||
    /** Length of the current frame.
 | 
			
		||||
     * This equals the total length of the message if num == 0 && final == true */
 | 
			
		||||
    uint64_t len;
 | 
			
		||||
    /** Mask key */
 | 
			
		||||
    uint8_t mask[4];
 | 
			
		||||
    /** Offset of the data inside the current frame. */
 | 
			
		||||
    uint64_t index;
 | 
			
		||||
} AwsFrameInfo;
 | 
			
		||||
 | 
			
		||||
typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
 | 
			
		||||
typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType;
 | 
			
		||||
typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
 | 
			
		||||
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketMessageBuffer {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t * _data;
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    bool _lock; 
 | 
			
		||||
    uint32_t _count;  
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocketMessageBuffer();
 | 
			
		||||
    AsyncWebSocketMessageBuffer(size_t size);
 | 
			
		||||
    AsyncWebSocketMessageBuffer(uint8_t * data, size_t size); 
 | 
			
		||||
    AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &); 
 | 
			
		||||
    AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&); 
 | 
			
		||||
    ~AsyncWebSocketMessageBuffer(); 
 | 
			
		||||
    void operator ++(int i) { _count++; }
 | 
			
		||||
    void operator --(int i) {  if (_count > 0) { _count--; } ;  }
 | 
			
		||||
    bool reserve(size_t size);
 | 
			
		||||
    void lock() { _lock = true; }
 | 
			
		||||
    void unlock() { _lock = false; }
 | 
			
		||||
    uint8_t * get() { return _data; }
 | 
			
		||||
    size_t length() { return _len; }
 | 
			
		||||
    uint32_t count() { return _count; }
 | 
			
		||||
    bool canDelete() { return (!_count && !_lock); } 
 | 
			
		||||
 | 
			
		||||
    friend AsyncWebSocket; 
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketMessage {
 | 
			
		||||
  protected:
 | 
			
		||||
    uint8_t _opcode;
 | 
			
		||||
    bool _mask;
 | 
			
		||||
    AwsMessageStatus _status;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){}
 | 
			
		||||
    virtual ~AsyncWebSocketMessage(){}
 | 
			
		||||
    virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){}
 | 
			
		||||
    virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; }
 | 
			
		||||
    virtual bool finished(){ return _status != WS_MSG_SENDING; }
 | 
			
		||||
    virtual bool betweenFrames() const { return false; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage {
 | 
			
		||||
  private:
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    size_t _sent;
 | 
			
		||||
    size_t _ack;
 | 
			
		||||
    size_t _acked;
 | 
			
		||||
    uint8_t * _data;
 | 
			
		||||
public:
 | 
			
		||||
    AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false);
 | 
			
		||||
    AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false);
 | 
			
		||||
    virtual ~AsyncWebSocketBasicMessage() override;
 | 
			
		||||
    virtual bool betweenFrames() const override { return _acked == _ack; }
 | 
			
		||||
    virtual void ack(size_t len, uint32_t time) override ;
 | 
			
		||||
    virtual size_t send(AsyncClient *client) override ;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t * _data;
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    size_t _sent;
 | 
			
		||||
    size_t _ack;
 | 
			
		||||
    size_t _acked;
 | 
			
		||||
    AsyncWebSocketMessageBuffer * _WSbuffer; 
 | 
			
		||||
public:
 | 
			
		||||
    AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false); 
 | 
			
		||||
    virtual ~AsyncWebSocketMultiMessage() override;
 | 
			
		||||
    virtual bool betweenFrames() const override { return _acked == _ack; }
 | 
			
		||||
    virtual void ack(size_t len, uint32_t time) override ;
 | 
			
		||||
    virtual size_t send(AsyncClient *client) override ;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketClient {
 | 
			
		||||
  private:
 | 
			
		||||
    AsyncClient *_client;
 | 
			
		||||
    AsyncWebSocket *_server;
 | 
			
		||||
    uint32_t _clientId;
 | 
			
		||||
    AwsClientStatus _status;
 | 
			
		||||
 | 
			
		||||
    LinkedList<AsyncWebSocketControl *> _controlQueue;
 | 
			
		||||
    LinkedList<AsyncWebSocketMessage *> _messageQueue;
 | 
			
		||||
 | 
			
		||||
    uint8_t _pstate;
 | 
			
		||||
    AwsFrameInfo _pinfo;
 | 
			
		||||
 | 
			
		||||
    uint32_t _lastMessageTime;
 | 
			
		||||
    uint32_t _keepAlivePeriod;
 | 
			
		||||
 | 
			
		||||
    void _queueMessage(AsyncWebSocketMessage *dataMessage);
 | 
			
		||||
    void _queueControl(AsyncWebSocketControl *controlMessage);
 | 
			
		||||
    void _runQueue();
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    void *_tempObject;
 | 
			
		||||
 | 
			
		||||
    AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server);
 | 
			
		||||
    ~AsyncWebSocketClient();
 | 
			
		||||
 | 
			
		||||
    //client id increments for the given server
 | 
			
		||||
    uint32_t id(){ return _clientId; }
 | 
			
		||||
    AwsClientStatus status(){ return _status; }
 | 
			
		||||
    AsyncClient* client(){ return _client; }
 | 
			
		||||
    AsyncWebSocket *server(){ return _server; }
 | 
			
		||||
    AwsFrameInfo const &pinfo() const { return _pinfo; }
 | 
			
		||||
 | 
			
		||||
    IPAddress remoteIP();
 | 
			
		||||
    uint16_t  remotePort();
 | 
			
		||||
 | 
			
		||||
    //control frames
 | 
			
		||||
    void close(uint16_t code=0, const char * message=NULL);
 | 
			
		||||
    void ping(uint8_t *data=NULL, size_t len=0);
 | 
			
		||||
 | 
			
		||||
    //set auto-ping period in seconds. disabled if zero (default)
 | 
			
		||||
    void keepAlivePeriod(uint16_t seconds){
 | 
			
		||||
      _keepAlivePeriod = seconds * 1000;
 | 
			
		||||
    }
 | 
			
		||||
    uint16_t keepAlivePeriod(){
 | 
			
		||||
      return (uint16_t)(_keepAlivePeriod / 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //data packets
 | 
			
		||||
    void message(AsyncWebSocketMessage *message){ _queueMessage(message); }
 | 
			
		||||
    bool queueIsFull();
 | 
			
		||||
 | 
			
		||||
    size_t printf(const char *format, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
#ifndef ESP32
 | 
			
		||||
    size_t printf_P(PGM_P formatP, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
#endif
 | 
			
		||||
    void text(const char * message, size_t len);
 | 
			
		||||
    void text(const char * message);
 | 
			
		||||
    void text(uint8_t * message, size_t len);
 | 
			
		||||
    void text(char * message);
 | 
			
		||||
    void text(const String &message);
 | 
			
		||||
    void text(const __FlashStringHelper *data);
 | 
			
		||||
    void text(AsyncWebSocketMessageBuffer *buffer); 
 | 
			
		||||
 | 
			
		||||
    void binary(const char * message, size_t len);
 | 
			
		||||
    void binary(const char * message);
 | 
			
		||||
    void binary(uint8_t * message, size_t len);
 | 
			
		||||
    void binary(char * message);
 | 
			
		||||
    void binary(const String &message);
 | 
			
		||||
    void binary(const __FlashStringHelper *data, size_t len);
 | 
			
		||||
    void binary(AsyncWebSocketMessageBuffer *buffer); 
 | 
			
		||||
 | 
			
		||||
    bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; }
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    void _onAck(size_t len, uint32_t time);
 | 
			
		||||
    void _onError(int8_t);
 | 
			
		||||
    void _onPoll();
 | 
			
		||||
    void _onTimeout(uint32_t time);
 | 
			
		||||
    void _onDisconnect();
 | 
			
		||||
    void _onData(void *pbuf, size_t plen);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler;
 | 
			
		||||
 | 
			
		||||
//WebServer Handler implementation that plays the role of a socket server
 | 
			
		||||
class AsyncWebSocket: public AsyncWebHandler {
 | 
			
		||||
  public:
 | 
			
		||||
    typedef LinkedList<AsyncWebSocketClient *> AsyncWebSocketClientLinkedList;
 | 
			
		||||
  private:
 | 
			
		||||
    String _url;
 | 
			
		||||
    AsyncWebSocketClientLinkedList _clients;
 | 
			
		||||
    uint32_t _cNextId;
 | 
			
		||||
    AwsEventHandler _eventHandler;
 | 
			
		||||
    bool _enabled;
 | 
			
		||||
    AsyncWebLock _lock;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocket(const String& url);
 | 
			
		||||
    ~AsyncWebSocket();
 | 
			
		||||
    const char * url() const { return _url.c_str(); }
 | 
			
		||||
    void enable(bool e){ _enabled = e; }
 | 
			
		||||
    bool enabled() const { return _enabled; }
 | 
			
		||||
    bool availableForWriteAll();
 | 
			
		||||
    bool availableForWrite(uint32_t id);
 | 
			
		||||
 | 
			
		||||
    size_t count() const;
 | 
			
		||||
    AsyncWebSocketClient * client(uint32_t id);
 | 
			
		||||
    bool hasClient(uint32_t id){ return client(id) != NULL; }
 | 
			
		||||
 | 
			
		||||
    void close(uint32_t id, uint16_t code=0, const char * message=NULL);
 | 
			
		||||
    void closeAll(uint16_t code=0, const char * message=NULL);
 | 
			
		||||
    void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
 | 
			
		||||
 | 
			
		||||
    void ping(uint32_t id, uint8_t *data=NULL, size_t len=0);
 | 
			
		||||
    void pingAll(uint8_t *data=NULL, size_t len=0); //  done
 | 
			
		||||
 | 
			
		||||
    void text(uint32_t id, const char * message, size_t len);
 | 
			
		||||
    void text(uint32_t id, const char * message);
 | 
			
		||||
    void text(uint32_t id, uint8_t * message, size_t len);
 | 
			
		||||
    void text(uint32_t id, char * message);
 | 
			
		||||
    void text(uint32_t id, const String &message);
 | 
			
		||||
    void text(uint32_t id, const __FlashStringHelper *message);
 | 
			
		||||
 | 
			
		||||
    void textAll(const char * message, size_t len);
 | 
			
		||||
    void textAll(const char * message);
 | 
			
		||||
    void textAll(uint8_t * message, size_t len);
 | 
			
		||||
    void textAll(char * message);
 | 
			
		||||
    void textAll(const String &message);
 | 
			
		||||
    void textAll(const __FlashStringHelper *message); //  need to convert
 | 
			
		||||
    void textAll(AsyncWebSocketMessageBuffer * buffer); 
 | 
			
		||||
 | 
			
		||||
    void binary(uint32_t id, const char * message, size_t len);
 | 
			
		||||
    void binary(uint32_t id, const char * message);
 | 
			
		||||
    void binary(uint32_t id, uint8_t * message, size_t len);
 | 
			
		||||
    void binary(uint32_t id, char * message);
 | 
			
		||||
    void binary(uint32_t id, const String &message);
 | 
			
		||||
    void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
 | 
			
		||||
 | 
			
		||||
    void binaryAll(const char * message, size_t len);
 | 
			
		||||
    void binaryAll(const char * message);
 | 
			
		||||
    void binaryAll(uint8_t * message, size_t len);
 | 
			
		||||
    void binaryAll(char * message);
 | 
			
		||||
    void binaryAll(const String &message);
 | 
			
		||||
    void binaryAll(const __FlashStringHelper *message, size_t len);
 | 
			
		||||
    void binaryAll(AsyncWebSocketMessageBuffer * buffer); 
 | 
			
		||||
 | 
			
		||||
    void message(uint32_t id, AsyncWebSocketMessage *message);
 | 
			
		||||
    void messageAll(AsyncWebSocketMultiMessage *message);
 | 
			
		||||
 | 
			
		||||
    size_t printf(uint32_t id, const char *format, ...)  __attribute__ ((format (printf, 3, 4)));
 | 
			
		||||
    size_t printfAll(const char *format, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
#ifndef ESP32
 | 
			
		||||
    size_t printf_P(uint32_t id, PGM_P formatP, ...)  __attribute__ ((format (printf, 3, 4)));
 | 
			
		||||
#endif
 | 
			
		||||
    size_t printfAll_P(PGM_P formatP, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
 | 
			
		||||
    //event listener
 | 
			
		||||
    void onEvent(AwsEventHandler handler){
 | 
			
		||||
      _eventHandler = handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    uint32_t _getNextId(){ return _cNextId++; }
 | 
			
		||||
    void _addClient(AsyncWebSocketClient * client);
 | 
			
		||||
    void _handleDisconnect(AsyncWebSocketClient * client);
 | 
			
		||||
    void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //  messagebuffer functions/objects. 
 | 
			
		||||
    AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); 
 | 
			
		||||
    AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size); 
 | 
			
		||||
    LinkedList<AsyncWebSocketMessageBuffer *> _buffers;
 | 
			
		||||
    void _cleanBuffers(); 
 | 
			
		||||
 | 
			
		||||
    AsyncWebSocketClientLinkedList getClients() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//WebServer response to authenticate the socket and detach the tcp client from the web server request
 | 
			
		||||
class AsyncWebSocketResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _content;
 | 
			
		||||
    AsyncWebSocket *_server;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocketResponse(const String& key, AsyncWebSocket *server);
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCWEBSOCKET_H_ */
 | 
			
		||||
@@ -0,0 +1,87 @@
 | 
			
		||||
#ifndef ASYNCWEBSYNCHRONIZATION_H_
 | 
			
		||||
#define ASYNCWEBSYNCHRONIZATION_H_
 | 
			
		||||
 | 
			
		||||
// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
 | 
			
		||||
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
 | 
			
		||||
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
 | 
			
		||||
class AsyncWebLock
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
  SemaphoreHandle_t _lock;
 | 
			
		||||
  mutable void *_lockedBy;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  AsyncWebLock() {
 | 
			
		||||
    _lock = xSemaphoreCreateBinary();
 | 
			
		||||
    _lockedBy = NULL;
 | 
			
		||||
    xSemaphoreGive(_lock);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~AsyncWebLock() {
 | 
			
		||||
    vSemaphoreDelete(_lock);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool lock() const {
 | 
			
		||||
    extern void *pxCurrentTCB;
 | 
			
		||||
    if (_lockedBy != pxCurrentTCB) {
 | 
			
		||||
      xSemaphoreTake(_lock, portMAX_DELAY);
 | 
			
		||||
      _lockedBy = pxCurrentTCB;
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void unlock() const {
 | 
			
		||||
    _lockedBy = NULL;
 | 
			
		||||
    xSemaphoreGive(_lock);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
// This is the 8266 version of the Sync Lock which is currently unimplemented
 | 
			
		||||
class AsyncWebLock
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  AsyncWebLock() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~AsyncWebLock() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool lock() const {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void unlock() const {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class AsyncWebLockGuard
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
  const AsyncWebLock *_lock;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  AsyncWebLockGuard(const AsyncWebLock &l) {
 | 
			
		||||
    if (l.lock()) {
 | 
			
		||||
      _lock = &l;
 | 
			
		||||
    } else {
 | 
			
		||||
      _lock = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~AsyncWebLockGuard() {
 | 
			
		||||
    if (_lock) {
 | 
			
		||||
      _lock->unlock();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // ASYNCWEBSYNCHRONIZATION_H_
 | 
			
		||||
							
								
								
									
										465
									
								
								Software/lib/ESP Async WebServer/src/ESPAsyncWebServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								Software/lib/ESP Async WebServer/src/ESPAsyncWebServer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,465 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#ifndef _ESPAsyncWebServer_H_
 | 
			
		||||
#define _ESPAsyncWebServer_H_
 | 
			
		||||
 | 
			
		||||
#include "Arduino.h"
 | 
			
		||||
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include "FS.h"
 | 
			
		||||
 | 
			
		||||
#include "StringArray.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#elif defined(ESP8266)
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#else
 | 
			
		||||
#error Platform not supported
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define DEBUGF(...) //Serial.printf(__VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
class AsyncWebServer;
 | 
			
		||||
class AsyncWebServerRequest;
 | 
			
		||||
class AsyncWebServerResponse;
 | 
			
		||||
class AsyncWebHeader;
 | 
			
		||||
class AsyncWebParameter;
 | 
			
		||||
class AsyncWebRewrite;
 | 
			
		||||
class AsyncWebHandler;
 | 
			
		||||
class AsyncStaticWebHandler;
 | 
			
		||||
class AsyncCallbackWebHandler;
 | 
			
		||||
class AsyncResponseStream;
 | 
			
		||||
 | 
			
		||||
#ifndef WEBSERVER_H
 | 
			
		||||
typedef enum {
 | 
			
		||||
  HTTP_GET     = 0b00000001,
 | 
			
		||||
  HTTP_POST    = 0b00000010,
 | 
			
		||||
  HTTP_DELETE  = 0b00000100,
 | 
			
		||||
  HTTP_PUT     = 0b00001000,
 | 
			
		||||
  HTTP_PATCH   = 0b00010000,
 | 
			
		||||
  HTTP_HEAD    = 0b00100000,
 | 
			
		||||
  HTTP_OPTIONS = 0b01000000,
 | 
			
		||||
  HTTP_ANY     = 0b01111111,
 | 
			
		||||
} WebRequestMethod;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
 | 
			
		||||
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
 | 
			
		||||
 | 
			
		||||
typedef uint8_t WebRequestMethodComposite;
 | 
			
		||||
typedef std::function<void(void)> ArDisconnectHandler;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PARAMETER :: Chainable object to hold GET/POST and FILE parameters
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebParameter {
 | 
			
		||||
  private:
 | 
			
		||||
    String _name;
 | 
			
		||||
    String _value;
 | 
			
		||||
    size_t _size;
 | 
			
		||||
    bool _isForm;
 | 
			
		||||
    bool _isFile;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){}
 | 
			
		||||
    const String& name() const { return _name; }
 | 
			
		||||
    const String& value() const { return _value; }
 | 
			
		||||
    size_t size() const { return _size; }
 | 
			
		||||
    bool isPost() const { return _isForm; }
 | 
			
		||||
    bool isFile() const { return _isFile; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * HEADER :: Chainable object to hold the headers
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebHeader {
 | 
			
		||||
  private:
 | 
			
		||||
    String _name;
 | 
			
		||||
    String _value;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
 | 
			
		||||
    AsyncWebHeader(const String& data): _name(), _value(){
 | 
			
		||||
      if(!data) return;
 | 
			
		||||
      int index = data.indexOf(':');
 | 
			
		||||
      if (index < 0) return;
 | 
			
		||||
      _name = data.substring(0, index);
 | 
			
		||||
      _value = data.substring(index + 2);
 | 
			
		||||
    }
 | 
			
		||||
    ~AsyncWebHeader(){}
 | 
			
		||||
    const String& name() const { return _name; }
 | 
			
		||||
    const String& value() const { return _value; }
 | 
			
		||||
    String toString() const { return String(_name+": "+_value+"\r\n"); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
 | 
			
		||||
 | 
			
		||||
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
 | 
			
		||||
typedef std::function<String(const String&)> AwsTemplateProcessor;
 | 
			
		||||
 | 
			
		||||
class AsyncWebServerRequest {
 | 
			
		||||
  using File = fs::File;
 | 
			
		||||
  using FS = fs::FS;
 | 
			
		||||
  friend class AsyncWebServer;
 | 
			
		||||
  friend class AsyncCallbackWebHandler;
 | 
			
		||||
  private:
 | 
			
		||||
    AsyncClient* _client;
 | 
			
		||||
    AsyncWebServer* _server;
 | 
			
		||||
    AsyncWebHandler* _handler;
 | 
			
		||||
    AsyncWebServerResponse* _response;
 | 
			
		||||
    StringArray _interestingHeaders;
 | 
			
		||||
    ArDisconnectHandler _onDisconnectfn;
 | 
			
		||||
 | 
			
		||||
    String _temp;
 | 
			
		||||
    uint8_t _parseState;
 | 
			
		||||
 | 
			
		||||
    uint8_t _version;
 | 
			
		||||
    WebRequestMethodComposite _method;
 | 
			
		||||
    String _url;
 | 
			
		||||
    String _host;
 | 
			
		||||
    String _contentType;
 | 
			
		||||
    String _boundary;
 | 
			
		||||
    String _authorization;
 | 
			
		||||
    RequestedConnectionType _reqconntype;
 | 
			
		||||
    void _removeNotInterestingHeaders();
 | 
			
		||||
    bool _isDigest;
 | 
			
		||||
    bool _isMultipart;
 | 
			
		||||
    bool _isPlainPost;
 | 
			
		||||
    bool _expectingContinue;
 | 
			
		||||
    size_t _contentLength;
 | 
			
		||||
    size_t _parsedLength;
 | 
			
		||||
 | 
			
		||||
    LinkedList<AsyncWebHeader *> _headers;
 | 
			
		||||
    LinkedList<AsyncWebParameter *> _params;
 | 
			
		||||
    LinkedList<String *> _pathParams;
 | 
			
		||||
 | 
			
		||||
    uint8_t _multiParseState;
 | 
			
		||||
    uint8_t _boundaryPosition;
 | 
			
		||||
    size_t _itemStartIndex;
 | 
			
		||||
    size_t _itemSize;
 | 
			
		||||
    String _itemName;
 | 
			
		||||
    String _itemFilename;
 | 
			
		||||
    String _itemType;
 | 
			
		||||
    String _itemValue;
 | 
			
		||||
    uint8_t *_itemBuffer;
 | 
			
		||||
    size_t _itemBufferIndex;
 | 
			
		||||
    bool _itemIsFile;
 | 
			
		||||
 | 
			
		||||
    void _onPoll();
 | 
			
		||||
    void _onAck(size_t len, uint32_t time);
 | 
			
		||||
    void _onError(int8_t error);
 | 
			
		||||
    void _onTimeout(uint32_t time);
 | 
			
		||||
    void _onDisconnect();
 | 
			
		||||
    void _onData(void *buf, size_t len);
 | 
			
		||||
 | 
			
		||||
    void _addParam(AsyncWebParameter*);
 | 
			
		||||
    void _addPathParam(const char *param);
 | 
			
		||||
 | 
			
		||||
    bool _parseReqHead();
 | 
			
		||||
    bool _parseReqHeader();
 | 
			
		||||
    void _parseLine();
 | 
			
		||||
    void _parsePlainPostChar(uint8_t data);
 | 
			
		||||
    void _parseMultipartPostByte(uint8_t data, bool last);
 | 
			
		||||
    void _addGetParams(const String& params);
 | 
			
		||||
 | 
			
		||||
    void _handleUploadStart();
 | 
			
		||||
    void _handleUploadByte(uint8_t data, bool last);
 | 
			
		||||
    void _handleUploadEnd();
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    File _tempFile;
 | 
			
		||||
    void *_tempObject;
 | 
			
		||||
 | 
			
		||||
    AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
 | 
			
		||||
    ~AsyncWebServerRequest();
 | 
			
		||||
 | 
			
		||||
    AsyncClient* client(){ return _client; }
 | 
			
		||||
    uint8_t version() const { return _version; }
 | 
			
		||||
    WebRequestMethodComposite method() const { return _method; }
 | 
			
		||||
    const String& url() const { return _url; }
 | 
			
		||||
    const String& host() const { return _host; }
 | 
			
		||||
    const String& contentType() const { return _contentType; }
 | 
			
		||||
    size_t contentLength() const { return _contentLength; }
 | 
			
		||||
    bool multipart() const { return _isMultipart; }
 | 
			
		||||
    const char * methodToString() const;
 | 
			
		||||
    const char * requestedConnTypeToString() const;
 | 
			
		||||
    RequestedConnectionType requestedConnType() const { return _reqconntype; }
 | 
			
		||||
    bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
 | 
			
		||||
    void onDisconnect (ArDisconnectHandler fn);
 | 
			
		||||
 | 
			
		||||
    //hash is the string representation of:
 | 
			
		||||
    // base64(user:pass) for basic or
 | 
			
		||||
    // user:realm:md5(user:realm:pass) for digest
 | 
			
		||||
    bool authenticate(const char * hash);
 | 
			
		||||
    bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
 | 
			
		||||
    void requestAuthentication(const char * realm = NULL, bool isDigest = true);
 | 
			
		||||
 | 
			
		||||
    void setHandler(AsyncWebHandler *handler){ _handler = handler; }
 | 
			
		||||
    void addInterestingHeader(const String& name);
 | 
			
		||||
 | 
			
		||||
    void redirect(const String& url);
 | 
			
		||||
 | 
			
		||||
    void send(AsyncWebServerResponse *response);
 | 
			
		||||
    void send(int code, const String& contentType=String(), const String& content=String());
 | 
			
		||||
    void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String());
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
 | 
			
		||||
    size_t headers() const;                     // get header count
 | 
			
		||||
    bool hasHeader(const String& name) const;   // check if header exists
 | 
			
		||||
    bool hasHeader(const __FlashStringHelper * data) const;   // check if header exists
 | 
			
		||||
 | 
			
		||||
    AsyncWebHeader* getHeader(const String& name) const;
 | 
			
		||||
    AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
 | 
			
		||||
    AsyncWebHeader* getHeader(size_t num) const;
 | 
			
		||||
 | 
			
		||||
    size_t params() const;                      // get arguments count
 | 
			
		||||
    bool hasParam(const String& name, bool post=false, bool file=false) const;
 | 
			
		||||
    bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;
 | 
			
		||||
 | 
			
		||||
    AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
 | 
			
		||||
    AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; 
 | 
			
		||||
    AsyncWebParameter* getParam(size_t num) const;
 | 
			
		||||
 | 
			
		||||
    size_t args() const { return params(); }     // get arguments count
 | 
			
		||||
    const String& arg(const String& name) const; // get request argument value by name
 | 
			
		||||
    const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)    
 | 
			
		||||
    const String& arg(size_t i) const;           // get request argument value by number
 | 
			
		||||
    const String& argName(size_t i) const;       // get request argument name by number
 | 
			
		||||
    bool hasArg(const char* name) const;         // check if argument exists
 | 
			
		||||
    bool hasArg(const __FlashStringHelper * data) const;         // check if F(argument) exists
 | 
			
		||||
 | 
			
		||||
    const String& pathArg(size_t i) const;
 | 
			
		||||
 | 
			
		||||
    const String& header(const char* name) const;// get request header value by name
 | 
			
		||||
    const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)    
 | 
			
		||||
    const String& header(size_t i) const;        // get request header value by number
 | 
			
		||||
    const String& headerName(size_t i) const;    // get request header name by number
 | 
			
		||||
    String urlDecode(const String& text) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction;
 | 
			
		||||
 | 
			
		||||
bool ON_STA_FILTER(AsyncWebServerRequest *request);
 | 
			
		||||
 | 
			
		||||
bool ON_AP_FILTER(AsyncWebServerRequest *request);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * REWRITE :: One instance can be handle any Request (done by the Server)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebRewrite {
 | 
			
		||||
  protected:
 | 
			
		||||
    String _from;
 | 
			
		||||
    String _toUrl;
 | 
			
		||||
    String _params;
 | 
			
		||||
    ArRequestFilterFunction _filter;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){
 | 
			
		||||
      int index = _toUrl.indexOf('?');
 | 
			
		||||
      if (index > 0) {
 | 
			
		||||
        _params = _toUrl.substring(index +1);
 | 
			
		||||
        _toUrl = _toUrl.substring(0, index);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    virtual ~AsyncWebRewrite(){}
 | 
			
		||||
    AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
 | 
			
		||||
    bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); }
 | 
			
		||||
    const String& from(void) const { return _from; }
 | 
			
		||||
    const String& toUrl(void) const { return _toUrl; }
 | 
			
		||||
    const String& params(void) const { return _params; }
 | 
			
		||||
    virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * HANDLER :: One instance can be attached to any Request (done by the Server)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebHandler {
 | 
			
		||||
  protected:
 | 
			
		||||
    ArRequestFilterFunction _filter;
 | 
			
		||||
    String _username;
 | 
			
		||||
    String _password;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebHandler():_username(""), _password(""){}
 | 
			
		||||
    AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
 | 
			
		||||
    AsyncWebHandler& setAuthentication(const char *username, const char *password){  _username = String(username);_password = String(password); return *this; };
 | 
			
		||||
    bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); }
 | 
			
		||||
    virtual ~AsyncWebHandler(){}
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
 | 
			
		||||
    virtual void handleUpload(AsyncWebServerRequest *request  __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final  __attribute__((unused))){}
 | 
			
		||||
    virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){}
 | 
			
		||||
    virtual bool isRequestHandlerTrivial(){return true;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * RESPONSE :: One instance is created for each Request (attached by the Handler)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
 | 
			
		||||
} WebResponseState;
 | 
			
		||||
 | 
			
		||||
class AsyncWebServerResponse {
 | 
			
		||||
  protected:
 | 
			
		||||
    int _code;
 | 
			
		||||
    LinkedList<AsyncWebHeader *> _headers;
 | 
			
		||||
    String _contentType;
 | 
			
		||||
    size_t _contentLength;
 | 
			
		||||
    bool _sendContentLength;
 | 
			
		||||
    bool _chunked;
 | 
			
		||||
    size_t _headLength;
 | 
			
		||||
    size_t _sentLength;
 | 
			
		||||
    size_t _ackedLength;
 | 
			
		||||
    size_t _writtenLength;
 | 
			
		||||
    WebResponseState _state;
 | 
			
		||||
    const char* _responseCodeToString(int code);
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebServerResponse();
 | 
			
		||||
    virtual ~AsyncWebServerResponse();
 | 
			
		||||
    virtual void setCode(int code);
 | 
			
		||||
    virtual void setContentLength(size_t len);
 | 
			
		||||
    virtual void setContentType(const String& type);
 | 
			
		||||
    virtual void addHeader(const String& name, const String& value);
 | 
			
		||||
    virtual String _assembleHead(uint8_t version);
 | 
			
		||||
    virtual bool _started() const;
 | 
			
		||||
    virtual bool _finished() const;
 | 
			
		||||
    virtual bool _failed() const;
 | 
			
		||||
    virtual bool _sourceValid() const;
 | 
			
		||||
    virtual void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * SERVER :: One instance
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction;
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
 | 
			
		||||
 | 
			
		||||
class AsyncWebServer {
 | 
			
		||||
  protected:
 | 
			
		||||
    AsyncServer _server;
 | 
			
		||||
    LinkedList<AsyncWebRewrite*> _rewrites;
 | 
			
		||||
    LinkedList<AsyncWebHandler*> _handlers;
 | 
			
		||||
    AsyncCallbackWebHandler* _catchAllHandler;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebServer(uint16_t port);
 | 
			
		||||
    ~AsyncWebServer();
 | 
			
		||||
 | 
			
		||||
    void begin();
 | 
			
		||||
    void end();
 | 
			
		||||
 | 
			
		||||
#if ASYNC_TCP_SSL_ENABLED
 | 
			
		||||
    void onSslFileRequest(AcSSlFileHandler cb, void* arg);
 | 
			
		||||
    void beginSecure(const char *cert, const char *private_key_file, const char *password);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
 | 
			
		||||
    bool removeRewrite(AsyncWebRewrite* rewrite);
 | 
			
		||||
    AsyncWebRewrite& rewrite(const char* from, const char* to);
 | 
			
		||||
 | 
			
		||||
    AsyncWebHandler& addHandler(AsyncWebHandler* handler);
 | 
			
		||||
    bool removeHandler(AsyncWebHandler* handler);
 | 
			
		||||
  
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
 | 
			
		||||
 | 
			
		||||
    AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);
 | 
			
		||||
 | 
			
		||||
    void onNotFound(ArRequestHandlerFunction fn);  //called when handler is not assigned
 | 
			
		||||
    void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
 | 
			
		||||
    void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
 | 
			
		||||
 | 
			
		||||
    void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody 
 | 
			
		||||
  
 | 
			
		||||
    void _handleDisconnect(AsyncWebServerRequest *request);
 | 
			
		||||
    void _attachHandler(AsyncWebServerRequest *request);
 | 
			
		||||
    void _rewriteRequest(AsyncWebServerRequest *request);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DefaultHeaders {
 | 
			
		||||
  using headers_t = LinkedList<AsyncWebHeader *>;
 | 
			
		||||
  headers_t _headers;
 | 
			
		||||
  
 | 
			
		||||
  DefaultHeaders()
 | 
			
		||||
  :_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
 | 
			
		||||
  {}
 | 
			
		||||
public:
 | 
			
		||||
  using ConstIterator = headers_t::ConstIterator;
 | 
			
		||||
 | 
			
		||||
  void addHeader(const String& name, const String& value){
 | 
			
		||||
    _headers.add(new AsyncWebHeader(name, value));
 | 
			
		||||
  }  
 | 
			
		||||
  
 | 
			
		||||
  ConstIterator begin() const { return _headers.begin(); }
 | 
			
		||||
  ConstIterator end() const { return _headers.end(); }
 | 
			
		||||
 | 
			
		||||
  DefaultHeaders(DefaultHeaders const &) = delete;
 | 
			
		||||
  DefaultHeaders &operator=(DefaultHeaders const &) = delete;
 | 
			
		||||
  static DefaultHeaders &Instance() {
 | 
			
		||||
    static DefaultHeaders instance;
 | 
			
		||||
    return instance;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "WebResponseImpl.h"
 | 
			
		||||
#include "WebHandlerImpl.h"
 | 
			
		||||
#include "AsyncWebSocket.h"
 | 
			
		||||
#include "AsyncEventSource.h"
 | 
			
		||||
 | 
			
		||||
#endif /* _AsyncWebServer_H_ */
 | 
			
		||||
							
								
								
									
										544
									
								
								Software/lib/ESP Async WebServer/src/SPIFFSEditor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										544
									
								
								Software/lib/ESP Async WebServer/src/SPIFFSEditor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,544 @@
 | 
			
		||||
#include "SPIFFSEditor.h"
 | 
			
		||||
#include <FS.h>
 | 
			
		||||
 | 
			
		||||
//File: edit.htm.gz, Size: 4151
 | 
			
		||||
#define edit_htm_gz_len 4151
 | 
			
		||||
const uint8_t edit_htm_gz[] PROGMEM = {
 | 
			
		||||
 0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
 | 
			
		||||
 0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED,
 | 
			
		||||
 0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6,
 | 
			
		||||
 0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB,
 | 
			
		||||
 0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A,
 | 
			
		||||
 0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61,
 | 
			
		||||
 0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7,
 | 
			
		||||
 0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02,
 | 
			
		||||
 0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C,
 | 
			
		||||
 0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A,
 | 
			
		||||
 0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89,
 | 
			
		||||
 0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76,
 | 
			
		||||
 0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D,
 | 
			
		||||
 0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9,
 | 
			
		||||
 0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B,
 | 
			
		||||
 0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91,
 | 
			
		||||
 0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78,
 | 
			
		||||
 0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78,
 | 
			
		||||
 0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98,
 | 
			
		||||
 0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E,
 | 
			
		||||
 0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41,
 | 
			
		||||
 0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21,
 | 
			
		||||
 0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F,
 | 
			
		||||
 0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74,
 | 
			
		||||
 0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C,
 | 
			
		||||
 0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0,
 | 
			
		||||
 0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C,
 | 
			
		||||
 0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30,
 | 
			
		||||
 0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9,
 | 
			
		||||
 0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61,
 | 
			
		||||
 0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B,
 | 
			
		||||
 0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9,
 | 
			
		||||
 0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B,
 | 
			
		||||
 0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD,
 | 
			
		||||
 0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3,
 | 
			
		||||
 0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77,
 | 
			
		||||
 0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83,
 | 
			
		||||
 0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF,
 | 
			
		||||
 0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3,
 | 
			
		||||
 0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55,
 | 
			
		||||
 0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3,
 | 
			
		||||
 0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF,
 | 
			
		||||
 0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF,
 | 
			
		||||
 0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60,
 | 
			
		||||
 0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1,
 | 
			
		||||
 0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE,
 | 
			
		||||
 0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F,
 | 
			
		||||
 0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0,
 | 
			
		||||
 0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9,
 | 
			
		||||
 0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5,
 | 
			
		||||
 0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15,
 | 
			
		||||
 0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74,
 | 
			
		||||
 0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D,
 | 
			
		||||
 0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD,
 | 
			
		||||
 0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A,
 | 
			
		||||
 0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6,
 | 
			
		||||
 0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2,
 | 
			
		||||
 0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF,
 | 
			
		||||
 0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53,
 | 
			
		||||
 0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2,
 | 
			
		||||
 0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A,
 | 
			
		||||
 0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83,
 | 
			
		||||
 0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26,
 | 
			
		||||
 0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0,
 | 
			
		||||
 0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0,
 | 
			
		||||
 0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84,
 | 
			
		||||
 0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99,
 | 
			
		||||
 0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5,
 | 
			
		||||
 0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9,
 | 
			
		||||
 0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87,
 | 
			
		||||
 0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F,
 | 
			
		||||
 0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6,
 | 
			
		||||
 0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B,
 | 
			
		||||
 0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D,
 | 
			
		||||
 0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25,
 | 
			
		||||
 0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3,
 | 
			
		||||
 0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F,
 | 
			
		||||
 0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35,
 | 
			
		||||
 0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A,
 | 
			
		||||
 0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6,
 | 
			
		||||
 0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7,
 | 
			
		||||
 0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A,
 | 
			
		||||
 0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9,
 | 
			
		||||
 0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97,
 | 
			
		||||
 0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36,
 | 
			
		||||
 0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C,
 | 
			
		||||
 0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A,
 | 
			
		||||
 0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C,
 | 
			
		||||
 0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F,
 | 
			
		||||
 0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11,
 | 
			
		||||
 0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16,
 | 
			
		||||
 0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA,
 | 
			
		||||
 0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB,
 | 
			
		||||
 0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A,
 | 
			
		||||
 0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6,
 | 
			
		||||
 0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28,
 | 
			
		||||
 0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1,
 | 
			
		||||
 0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E,
 | 
			
		||||
 0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E,
 | 
			
		||||
 0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92,
 | 
			
		||||
 0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05,
 | 
			
		||||
 0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8,
 | 
			
		||||
 0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0,
 | 
			
		||||
 0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85,
 | 
			
		||||
 0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40,
 | 
			
		||||
 0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56,
 | 
			
		||||
 0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47,
 | 
			
		||||
 0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA,
 | 
			
		||||
 0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7,
 | 
			
		||||
 0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD,
 | 
			
		||||
 0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61,
 | 
			
		||||
 0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58,
 | 
			
		||||
 0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D,
 | 
			
		||||
 0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8,
 | 
			
		||||
 0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C,
 | 
			
		||||
 0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA,
 | 
			
		||||
 0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49,
 | 
			
		||||
 0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51,
 | 
			
		||||
 0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00,
 | 
			
		||||
 0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A,
 | 
			
		||||
 0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A,
 | 
			
		||||
 0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35,
 | 
			
		||||
 0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F,
 | 
			
		||||
 0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E,
 | 
			
		||||
 0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C,
 | 
			
		||||
 0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64,
 | 
			
		||||
 0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C,
 | 
			
		||||
 0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1,
 | 
			
		||||
 0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B,
 | 
			
		||||
 0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC,
 | 
			
		||||
 0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42,
 | 
			
		||||
 0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B,
 | 
			
		||||
 0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71,
 | 
			
		||||
 0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F,
 | 
			
		||||
 0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28,
 | 
			
		||||
 0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9,
 | 
			
		||||
 0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD,
 | 
			
		||||
 0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6,
 | 
			
		||||
 0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F,
 | 
			
		||||
 0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5,
 | 
			
		||||
 0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8,
 | 
			
		||||
 0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF,
 | 
			
		||||
 0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62,
 | 
			
		||||
 0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C,
 | 
			
		||||
 0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7,
 | 
			
		||||
 0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89,
 | 
			
		||||
 0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29,
 | 
			
		||||
 0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95,
 | 
			
		||||
 0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7,
 | 
			
		||||
 0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB,
 | 
			
		||||
 0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09,
 | 
			
		||||
 0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F,
 | 
			
		||||
 0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60,
 | 
			
		||||
 0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35,
 | 
			
		||||
 0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6,
 | 
			
		||||
 0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B,
 | 
			
		||||
 0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66,
 | 
			
		||||
 0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25,
 | 
			
		||||
 0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E,
 | 
			
		||||
 0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97,
 | 
			
		||||
 0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC,
 | 
			
		||||
 0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE,
 | 
			
		||||
 0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7,
 | 
			
		||||
 0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13,
 | 
			
		||||
 0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0,
 | 
			
		||||
 0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A,
 | 
			
		||||
 0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93,
 | 
			
		||||
 0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E,
 | 
			
		||||
 0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9,
 | 
			
		||||
 0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78,
 | 
			
		||||
 0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5,
 | 
			
		||||
 0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12,
 | 
			
		||||
 0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E,
 | 
			
		||||
 0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35,
 | 
			
		||||
 0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98,
 | 
			
		||||
 0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58,
 | 
			
		||||
 0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3,
 | 
			
		||||
 0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64,
 | 
			
		||||
 0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39,
 | 
			
		||||
 0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D,
 | 
			
		||||
 0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62,
 | 
			
		||||
 0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48,
 | 
			
		||||
 0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D,
 | 
			
		||||
 0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8,
 | 
			
		||||
 0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9,
 | 
			
		||||
 0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30,
 | 
			
		||||
 0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6,
 | 
			
		||||
 0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1,
 | 
			
		||||
 0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56,
 | 
			
		||||
 0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84,
 | 
			
		||||
 0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0,
 | 
			
		||||
 0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC,
 | 
			
		||||
 0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E,
 | 
			
		||||
 0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39,
 | 
			
		||||
 0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B,
 | 
			
		||||
 0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA,
 | 
			
		||||
 0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1,
 | 
			
		||||
 0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1,
 | 
			
		||||
 0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88,
 | 
			
		||||
 0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4,
 | 
			
		||||
 0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC,
 | 
			
		||||
 0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98,
 | 
			
		||||
 0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97,
 | 
			
		||||
 0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8,
 | 
			
		||||
 0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30,
 | 
			
		||||
 0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA,
 | 
			
		||||
 0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B,
 | 
			
		||||
 0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC,
 | 
			
		||||
 0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45,
 | 
			
		||||
 0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD,
 | 
			
		||||
 0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76,
 | 
			
		||||
 0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD,
 | 
			
		||||
 0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76,
 | 
			
		||||
 0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4,
 | 
			
		||||
 0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF,
 | 
			
		||||
 0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4,
 | 
			
		||||
 0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42,
 | 
			
		||||
 0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43,
 | 
			
		||||
 0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B,
 | 
			
		||||
 0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97,
 | 
			
		||||
 0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73,
 | 
			
		||||
 0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B,
 | 
			
		||||
 0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50,
 | 
			
		||||
 0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51,
 | 
			
		||||
 0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25,
 | 
			
		||||
 0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26,
 | 
			
		||||
 0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80,
 | 
			
		||||
 0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9,
 | 
			
		||||
 0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0,
 | 
			
		||||
 0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74,
 | 
			
		||||
 0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA,
 | 
			
		||||
 0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D,
 | 
			
		||||
 0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F,
 | 
			
		||||
 0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2,
 | 
			
		||||
 0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1,
 | 
			
		||||
 0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99,
 | 
			
		||||
 0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D,
 | 
			
		||||
 0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B,
 | 
			
		||||
 0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD,
 | 
			
		||||
 0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F,
 | 
			
		||||
 0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB,
 | 
			
		||||
 0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47,
 | 
			
		||||
 0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59,
 | 
			
		||||
 0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D,
 | 
			
		||||
 0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD,
 | 
			
		||||
 0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94,
 | 
			
		||||
 0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35,
 | 
			
		||||
 0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81,
 | 
			
		||||
 0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D,
 | 
			
		||||
 0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20,
 | 
			
		||||
 0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB,
 | 
			
		||||
 0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B,
 | 
			
		||||
 0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6,
 | 
			
		||||
 0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17,
 | 
			
		||||
 0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8,
 | 
			
		||||
 0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26,
 | 
			
		||||
 0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57,
 | 
			
		||||
 0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36,
 | 
			
		||||
 0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53,
 | 
			
		||||
 0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SPIFFS_MAXLENGTH_FILEPATH 32
 | 
			
		||||
const char *excludeListFile = "/.exclude.files";
 | 
			
		||||
 | 
			
		||||
typedef struct ExcludeListS {
 | 
			
		||||
    char *item;
 | 
			
		||||
    ExcludeListS *next;
 | 
			
		||||
} ExcludeList;
 | 
			
		||||
 | 
			
		||||
static ExcludeList *excludes = NULL;
 | 
			
		||||
 | 
			
		||||
static bool matchWild(const char *pattern, const char *testee) {
 | 
			
		||||
  const char *nxPat = NULL, *nxTst = NULL;
 | 
			
		||||
 | 
			
		||||
  while (*testee) {
 | 
			
		||||
    if (( *pattern == '?' ) || (*pattern == *testee)){
 | 
			
		||||
      pattern++;testee++;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (*pattern=='*'){
 | 
			
		||||
      nxPat=pattern++; nxTst=testee;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (nxPat){ 
 | 
			
		||||
      pattern = nxPat+1; testee=++nxTst;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  while (*pattern=='*'){pattern++;}  
 | 
			
		||||
  return (*pattern == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool addExclude(const char *item){
 | 
			
		||||
    size_t len = strlen(item);
 | 
			
		||||
    if(!len){
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList));
 | 
			
		||||
    if(!e){
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    e->item = (char *)malloc(len+1);
 | 
			
		||||
    if(!e->item){
 | 
			
		||||
        free(e);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(e->item, item, len+1);
 | 
			
		||||
    e->next = excludes;
 | 
			
		||||
    excludes = e;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void loadExcludeList(fs::FS &_fs, const char *filename){
 | 
			
		||||
    static char linebuf[SPIFFS_MAXLENGTH_FILEPATH];
 | 
			
		||||
    fs::File excludeFile=_fs.open(filename, "r");
 | 
			
		||||
    if(!excludeFile){
 | 
			
		||||
        //addExclude("/*.js.gz");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
    if(excludeFile.isDirectory()){
 | 
			
		||||
      excludeFile.close();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (excludeFile.size() > 0){
 | 
			
		||||
      uint8_t idx;
 | 
			
		||||
      bool isOverflowed = false;
 | 
			
		||||
      while (excludeFile.available()){
 | 
			
		||||
        linebuf[0] = '\0';
 | 
			
		||||
        idx = 0;
 | 
			
		||||
        int lastChar;
 | 
			
		||||
        do {
 | 
			
		||||
          lastChar = excludeFile.read();
 | 
			
		||||
          if(lastChar != '\r'){
 | 
			
		||||
            linebuf[idx++] = (char) lastChar;
 | 
			
		||||
          }
 | 
			
		||||
        } while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH));
 | 
			
		||||
 | 
			
		||||
        if(isOverflowed){
 | 
			
		||||
          isOverflowed = (lastChar != '\n');
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH);
 | 
			
		||||
        linebuf[idx-1] = '\0';
 | 
			
		||||
        if(!addExclude(linebuf)){
 | 
			
		||||
            excludeFile.close();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    excludeFile.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isExcluded(fs::FS &_fs, const char *filename) {
 | 
			
		||||
  if(excludes == NULL){
 | 
			
		||||
      loadExcludeList(_fs, excludeListFile);
 | 
			
		||||
  }
 | 
			
		||||
  ExcludeList *e = excludes;
 | 
			
		||||
  while(e){
 | 
			
		||||
    if (matchWild(e->item, filename)){
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    e = e->next;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WEB HANDLER IMPLEMENTATION
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
SPIFFSEditor::SPIFFSEditor(const fs::FS& fs, const String& username, const String& password)
 | 
			
		||||
#else
 | 
			
		||||
SPIFFSEditor::SPIFFSEditor(const String& username, const String& password, const fs::FS& fs)
 | 
			
		||||
#endif
 | 
			
		||||
:_fs(fs)
 | 
			
		||||
,_username(username)
 | 
			
		||||
,_password(password)
 | 
			
		||||
,_authenticated(false)
 | 
			
		||||
,_startTime(0)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){
 | 
			
		||||
  if(request->url().equalsIgnoreCase("/edit")){
 | 
			
		||||
    if(request->method() == HTTP_GET){
 | 
			
		||||
      if(request->hasParam("list"))
 | 
			
		||||
        return true;
 | 
			
		||||
      if(request->hasParam("edit")){
 | 
			
		||||
        request->_tempFile = _fs.open(request->arg("edit"), "r");
 | 
			
		||||
        if(!request->_tempFile){
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
        if(request->_tempFile.isDirectory()){
 | 
			
		||||
          request->_tempFile.close();
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
      }
 | 
			
		||||
      if(request->hasParam("download")){
 | 
			
		||||
        request->_tempFile = _fs.open(request->arg("download"), "r");
 | 
			
		||||
        if(!request->_tempFile){
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
        if(request->_tempFile.isDirectory()){
 | 
			
		||||
          request->_tempFile.close();
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
      }
 | 
			
		||||
      request->addInterestingHeader("If-Modified-Since");
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if(request->method() == HTTP_POST)
 | 
			
		||||
      return true;
 | 
			
		||||
    else if(request->method() == HTTP_DELETE)
 | 
			
		||||
      return true;
 | 
			
		||||
    else if(request->method() == HTTP_PUT)
 | 
			
		||||
      return true;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
 | 
			
		||||
  if(_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str()))
 | 
			
		||||
    return request->requestAuthentication();
 | 
			
		||||
 | 
			
		||||
  if(request->method() == HTTP_GET){
 | 
			
		||||
    if(request->hasParam("list")){
 | 
			
		||||
      String path = request->getParam("list")->value();
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
      File dir = _fs.open(path);
 | 
			
		||||
#else
 | 
			
		||||
      Dir dir = _fs.openDir(path);
 | 
			
		||||
#endif
 | 
			
		||||
      path = String();
 | 
			
		||||
      String output = "[";
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
      File entry = dir.openNextFile();
 | 
			
		||||
      while(entry){
 | 
			
		||||
#else
 | 
			
		||||
      while(dir.next()){
 | 
			
		||||
        fs::File entry = dir.openFile("r");
 | 
			
		||||
#endif
 | 
			
		||||
        if (isExcluded(_fs, entry.name())) {
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
            entry = dir.openNextFile();
 | 
			
		||||
#endif
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (output != "[") output += ',';
 | 
			
		||||
        output += "{\"type\":\"";
 | 
			
		||||
        output += "file";
 | 
			
		||||
        output += "\",\"name\":\"";
 | 
			
		||||
        output += String(entry.name());
 | 
			
		||||
        output += "\",\"size\":";
 | 
			
		||||
        output += String(entry.size());
 | 
			
		||||
        output += "}";
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
        entry = dir.openNextFile();
 | 
			
		||||
#else
 | 
			
		||||
        entry.close();
 | 
			
		||||
#endif
 | 
			
		||||
      }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
      dir.close();
 | 
			
		||||
#endif
 | 
			
		||||
      output += "]";
 | 
			
		||||
      request->send(200, "application/json", output);
 | 
			
		||||
      output = String();
 | 
			
		||||
    }
 | 
			
		||||
    else if(request->hasParam("edit") || request->hasParam("download")){
 | 
			
		||||
      request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download"));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      const char * buildTime = __DATE__ " " __TIME__ " GMT";
 | 
			
		||||
      if (request->header("If-Modified-Since").equals(buildTime)) {
 | 
			
		||||
        request->send(304);
 | 
			
		||||
      } else {
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len);
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Last-Modified", buildTime);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else if(request->method() == HTTP_DELETE){
 | 
			
		||||
    if(request->hasParam("path", true)){
 | 
			
		||||
        _fs.remove(request->getParam("path", true)->value());
 | 
			
		||||
      request->send(200, "", "DELETE: "+request->getParam("path", true)->value());
 | 
			
		||||
    } else
 | 
			
		||||
      request->send(404);
 | 
			
		||||
  } else if(request->method() == HTTP_POST){
 | 
			
		||||
    if(request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value()))
 | 
			
		||||
      request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value());
 | 
			
		||||
    else
 | 
			
		||||
      request->send(500);
 | 
			
		||||
  } else if(request->method() == HTTP_PUT){
 | 
			
		||||
    if(request->hasParam("path", true)){
 | 
			
		||||
      String filename = request->getParam("path", true)->value();
 | 
			
		||||
      if(_fs.exists(filename)){
 | 
			
		||||
        request->send(200);
 | 
			
		||||
      } else {
 | 
			
		||||
        fs::File f = _fs.open(filename, "w");
 | 
			
		||||
        if(f){
 | 
			
		||||
          f.write((uint8_t)0x00);
 | 
			
		||||
          f.close();
 | 
			
		||||
          request->send(200, "", "CREATE: "+filename);
 | 
			
		||||
        } else {
 | 
			
		||||
          request->send(500);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else
 | 
			
		||||
      request->send(400);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
 | 
			
		||||
  if(!index){
 | 
			
		||||
    if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())){
 | 
			
		||||
      _authenticated = true;
 | 
			
		||||
      request->_tempFile = _fs.open(filename, "w");
 | 
			
		||||
      _startTime = millis();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if(_authenticated && request->_tempFile){
 | 
			
		||||
    if(len){
 | 
			
		||||
      request->_tempFile.write(data,len);
 | 
			
		||||
    }
 | 
			
		||||
    if(final){
 | 
			
		||||
      request->_tempFile.close();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								Software/lib/ESP Async WebServer/src/SPIFFSEditor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Software/lib/ESP Async WebServer/src/SPIFFSEditor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
#ifndef SPIFFSEditor_H_
 | 
			
		||||
#define SPIFFSEditor_H_
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
class SPIFFSEditor: public AsyncWebHandler {
 | 
			
		||||
  private:
 | 
			
		||||
    fs::FS _fs;
 | 
			
		||||
    String _username;
 | 
			
		||||
    String _password; 
 | 
			
		||||
    bool _authenticated;
 | 
			
		||||
    uint32_t _startTime;
 | 
			
		||||
  public:
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
    SPIFFSEditor(const fs::FS& fs, const String& username=String(), const String& password=String());
 | 
			
		||||
#else
 | 
			
		||||
    SPIFFSEditor(const String& username=String(), const String& password=String(), const fs::FS& fs=SPIFFS);
 | 
			
		||||
#endif
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
 | 
			
		||||
    virtual bool isRequestHandlerTrivial() override final {return false;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										193
									
								
								Software/lib/ESP Async WebServer/src/StringArray.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								Software/lib/ESP Async WebServer/src/StringArray.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#ifndef STRINGARRAY_H_
 | 
			
		||||
#define STRINGARRAY_H_
 | 
			
		||||
 | 
			
		||||
#include "stddef.h"
 | 
			
		||||
#include "WString.h"
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
class LinkedListNode {
 | 
			
		||||
    T _value;
 | 
			
		||||
  public:
 | 
			
		||||
    LinkedListNode<T>* next;
 | 
			
		||||
    LinkedListNode(const T val): _value(val), next(nullptr) {}
 | 
			
		||||
    ~LinkedListNode(){}
 | 
			
		||||
    const T& value() const { return _value; };
 | 
			
		||||
    T& value(){ return _value; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T, template<typename> class Item = LinkedListNode>
 | 
			
		||||
class LinkedList {
 | 
			
		||||
  public:
 | 
			
		||||
    typedef Item<T> ItemType;
 | 
			
		||||
    typedef std::function<void(const T&)> OnRemove;
 | 
			
		||||
    typedef std::function<bool(const T&)> Predicate;
 | 
			
		||||
  private:
 | 
			
		||||
    ItemType* _root;
 | 
			
		||||
    OnRemove _onRemove;
 | 
			
		||||
 | 
			
		||||
    class Iterator {
 | 
			
		||||
      ItemType* _node;
 | 
			
		||||
    public:
 | 
			
		||||
      Iterator(ItemType* current = nullptr) : _node(current) {}
 | 
			
		||||
      Iterator(const Iterator& i) : _node(i._node) {}
 | 
			
		||||
      Iterator& operator ++() { _node = _node->next; return *this; }
 | 
			
		||||
      bool operator != (const Iterator& i) const { return _node != i._node; }
 | 
			
		||||
      const T& operator * () const { return _node->value(); }
 | 
			
		||||
      const T* operator -> () const { return &_node->value(); }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
  public:
 | 
			
		||||
    typedef const Iterator ConstIterator;
 | 
			
		||||
    ConstIterator begin() const { return ConstIterator(_root); }
 | 
			
		||||
    ConstIterator end() const { return ConstIterator(nullptr); }
 | 
			
		||||
 | 
			
		||||
    LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {}
 | 
			
		||||
    ~LinkedList(){}
 | 
			
		||||
    void add(const T& t){
 | 
			
		||||
      auto it = new ItemType(t);
 | 
			
		||||
      if(!_root){
 | 
			
		||||
        _root = it;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto i = _root;
 | 
			
		||||
        while(i->next) i = i->next;
 | 
			
		||||
        i->next = it;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    T& front() const {
 | 
			
		||||
      return _root->value();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool isEmpty() const {
 | 
			
		||||
      return _root == nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    size_t length() const {
 | 
			
		||||
      size_t i = 0;
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        i++;
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
    size_t count_if(Predicate predicate) const {
 | 
			
		||||
      size_t i = 0;
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if (!predicate){
 | 
			
		||||
          i++;
 | 
			
		||||
        }
 | 
			
		||||
        else if (predicate(it->value())) {
 | 
			
		||||
          i++;
 | 
			
		||||
        }
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
    const T* nth(size_t N) const {
 | 
			
		||||
      size_t i = 0;
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if(i++ == N)
 | 
			
		||||
          return &(it->value());
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    bool remove(const T& t){
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      auto pit = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if(it->value() == t){
 | 
			
		||||
          if(it == _root){
 | 
			
		||||
            _root = _root->next;
 | 
			
		||||
          } else {
 | 
			
		||||
            pit->next = it->next;
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
          if (_onRemove) {
 | 
			
		||||
            _onRemove(it->value());
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
          delete it;
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        pit = it;
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    bool remove_first(Predicate predicate){
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      auto pit = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if(predicate(it->value())){
 | 
			
		||||
          if(it == _root){
 | 
			
		||||
            _root = _root->next;
 | 
			
		||||
          } else {
 | 
			
		||||
            pit->next = it->next;
 | 
			
		||||
          }
 | 
			
		||||
          if (_onRemove) {
 | 
			
		||||
            _onRemove(it->value());
 | 
			
		||||
          }
 | 
			
		||||
          delete it;
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        pit = it;
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void free(){
 | 
			
		||||
      while(_root != nullptr){
 | 
			
		||||
        auto it = _root;
 | 
			
		||||
        _root = _root->next;
 | 
			
		||||
        if (_onRemove) {
 | 
			
		||||
          _onRemove(it->value());
 | 
			
		||||
        }
 | 
			
		||||
        delete it;
 | 
			
		||||
      }
 | 
			
		||||
      _root = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StringArray : public LinkedList<String> {
 | 
			
		||||
public:
 | 
			
		||||
  
 | 
			
		||||
  StringArray() : LinkedList(nullptr) {}
 | 
			
		||||
  
 | 
			
		||||
  bool containsIgnoreCase(const String& str){
 | 
			
		||||
    for (const auto& s : *this) {
 | 
			
		||||
      if (str.equalsIgnoreCase(s)) {
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* STRINGARRAY_H_ */
 | 
			
		||||
							
								
								
									
										238
									
								
								Software/lib/ESP Async WebServer/src/WebAuthentication.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								Software/lib/ESP Async WebServer/src/WebAuthentication.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,238 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#include "WebAuthentication.h"
 | 
			
		||||
#include <libb64/cencode.h>
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include "mbedtls/md5.h"
 | 
			
		||||
#else
 | 
			
		||||
#include "md5.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Basic Auth hash = base64("username:password")
 | 
			
		||||
 | 
			
		||||
bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
 | 
			
		||||
  if(username == NULL || password == NULL || hash == NULL)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  size_t toencodeLen = strlen(username)+strlen(password)+1;
 | 
			
		||||
  size_t encodedLen = base64_encode_expected_len(toencodeLen);
 | 
			
		||||
  if(strlen(hash) != encodedLen)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  char *toencode = new char[toencodeLen+1];
 | 
			
		||||
  if(toencode == NULL){
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
 | 
			
		||||
  if(encoded == NULL){
 | 
			
		||||
    delete[] toencode;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  sprintf(toencode, "%s:%s", username, password);
 | 
			
		||||
  if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
 | 
			
		||||
    delete[] toencode;
 | 
			
		||||
    delete[] encoded;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  delete[] toencode;
 | 
			
		||||
  delete[] encoded;
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
    mbedtls_md5_context _ctx;
 | 
			
		||||
#else
 | 
			
		||||
    md5_context_t _ctx;
 | 
			
		||||
#endif
 | 
			
		||||
  uint8_t i;
 | 
			
		||||
  uint8_t * _buf = (uint8_t*)malloc(16);
 | 
			
		||||
  if(_buf == NULL)
 | 
			
		||||
    return false;
 | 
			
		||||
  memset(_buf, 0x00, 16);
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
  mbedtls_md5_init(&_ctx);
 | 
			
		||||
  mbedtls_md5_update_ret (&_ctx,data,len);
 | 
			
		||||
  mbedtls_md5_finish_ret(&_ctx,data);
 | 
			
		||||
  mbedtls_internal_md5_process( &_ctx ,data);
 | 
			
		||||
// mbedtls_md5_starts(&_ctx);
 | 
			
		||||
// mbedtls_md5_update(&_ctx, data, len);
 | 
			
		||||
// mbedtls_md5_finish(&_ctx, _buf);
 | 
			
		||||
#else
 | 
			
		||||
  MD5Init(&_ctx);
 | 
			
		||||
  MD5Update(&_ctx, data, len);
 | 
			
		||||
  MD5Final(_buf, &_ctx);
 | 
			
		||||
#endif
 | 
			
		||||
  for(i = 0; i < 16; i++) {
 | 
			
		||||
    sprintf(output + (i * 2), "%02x", _buf[i]);
 | 
			
		||||
  }
 | 
			
		||||
  free(_buf);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String genRandomMD5(){
 | 
			
		||||
#ifdef ESP8266
 | 
			
		||||
  uint32_t r = RANDOM_REG32;
 | 
			
		||||
#else
 | 
			
		||||
  uint32_t r = rand();
 | 
			
		||||
#endif
 | 
			
		||||
  char * out = (char*)malloc(33);
 | 
			
		||||
  if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
 | 
			
		||||
    return "";
 | 
			
		||||
  String res = String(out);
 | 
			
		||||
  free(out);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String stringMD5(const String& in){
 | 
			
		||||
  char * out = (char*)malloc(33);
 | 
			
		||||
  if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
 | 
			
		||||
    return "";
 | 
			
		||||
  String res = String(out);
 | 
			
		||||
  free(out);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String generateDigestHash(const char * username, const char * password, const char * realm){
 | 
			
		||||
  if(username == NULL || password == NULL || realm == NULL){
 | 
			
		||||
    return "";
 | 
			
		||||
  }
 | 
			
		||||
  char * out = (char*)malloc(33);
 | 
			
		||||
  String res = String(username);
 | 
			
		||||
  res.concat(":");
 | 
			
		||||
  res.concat(realm);
 | 
			
		||||
  res.concat(":");
 | 
			
		||||
  String in = res;
 | 
			
		||||
  in.concat(password);
 | 
			
		||||
  if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
 | 
			
		||||
    return "";
 | 
			
		||||
  res.concat(out);
 | 
			
		||||
  free(out);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String requestDigestAuthentication(const char * realm){
 | 
			
		||||
  String header = "realm=\"";
 | 
			
		||||
  if(realm == NULL)
 | 
			
		||||
    header.concat("asyncesp");
 | 
			
		||||
  else
 | 
			
		||||
    header.concat(realm);
 | 
			
		||||
  header.concat( "\", qop=\"auth\", nonce=\"");
 | 
			
		||||
  header.concat(genRandomMD5());
 | 
			
		||||
  header.concat("\", opaque=\"");
 | 
			
		||||
  header.concat(genRandomMD5());
 | 
			
		||||
  header.concat("\"");
 | 
			
		||||
  return header;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
 | 
			
		||||
  if(username == NULL || password == NULL || header == NULL || method == NULL){
 | 
			
		||||
    //os_printf("AUTH FAIL: missing requred fields\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String myHeader = String(header);
 | 
			
		||||
  int nextBreak = myHeader.indexOf(",");
 | 
			
		||||
  if(nextBreak < 0){
 | 
			
		||||
    //os_printf("AUTH FAIL: no variables\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String myUsername = String();
 | 
			
		||||
  String myRealm = String();
 | 
			
		||||
  String myNonce = String();
 | 
			
		||||
  String myUri = String();
 | 
			
		||||
  String myResponse = String();
 | 
			
		||||
  String myQop = String();
 | 
			
		||||
  String myNc = String();
 | 
			
		||||
  String myCnonce = String();
 | 
			
		||||
 | 
			
		||||
  myHeader += ", ";
 | 
			
		||||
  do {
 | 
			
		||||
    String avLine = myHeader.substring(0, nextBreak);
 | 
			
		||||
    avLine.trim();
 | 
			
		||||
    myHeader = myHeader.substring(nextBreak+1);
 | 
			
		||||
    nextBreak = myHeader.indexOf(",");
 | 
			
		||||
 | 
			
		||||
    int eqSign = avLine.indexOf("=");
 | 
			
		||||
    if(eqSign < 0){
 | 
			
		||||
      //os_printf("AUTH FAIL: no = sign\n");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    String varName = avLine.substring(0, eqSign);
 | 
			
		||||
    avLine = avLine.substring(eqSign + 1);
 | 
			
		||||
    if(avLine.startsWith("\"")){
 | 
			
		||||
      avLine = avLine.substring(1, avLine.length() - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(varName.equals("username")){
 | 
			
		||||
      if(!avLine.equals(username)){
 | 
			
		||||
        //os_printf("AUTH FAIL: username\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myUsername = avLine;
 | 
			
		||||
    } else if(varName.equals("realm")){
 | 
			
		||||
      if(realm != NULL && !avLine.equals(realm)){
 | 
			
		||||
        //os_printf("AUTH FAIL: realm\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myRealm = avLine;
 | 
			
		||||
    } else if(varName.equals("nonce")){
 | 
			
		||||
      if(nonce != NULL && !avLine.equals(nonce)){
 | 
			
		||||
        //os_printf("AUTH FAIL: nonce\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myNonce = avLine;
 | 
			
		||||
    } else if(varName.equals("opaque")){
 | 
			
		||||
      if(opaque != NULL && !avLine.equals(opaque)){
 | 
			
		||||
        //os_printf("AUTH FAIL: opaque\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    } else if(varName.equals("uri")){
 | 
			
		||||
      if(uri != NULL && !avLine.equals(uri)){
 | 
			
		||||
        //os_printf("AUTH FAIL: uri\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myUri = avLine;
 | 
			
		||||
    } else if(varName.equals("response")){
 | 
			
		||||
      myResponse = avLine;
 | 
			
		||||
    } else if(varName.equals("qop")){
 | 
			
		||||
      myQop = avLine;
 | 
			
		||||
    } else if(varName.equals("nc")){
 | 
			
		||||
      myNc = avLine;
 | 
			
		||||
    } else if(varName.equals("cnonce")){
 | 
			
		||||
      myCnonce = avLine;
 | 
			
		||||
    }
 | 
			
		||||
  } while(nextBreak > 0);
 | 
			
		||||
 | 
			
		||||
  String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password));
 | 
			
		||||
  String ha2 = String(method) + ":" + myUri;
 | 
			
		||||
  String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2);
 | 
			
		||||
 | 
			
		||||
  if(myResponse.equals(stringMD5(response))){
 | 
			
		||||
    //os_printf("AUTH SUCCESS\n");
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //os_printf("AUTH FAIL: password\n");
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								Software/lib/ESP Async WebServer/src/WebAuthentication.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Software/lib/ESP Async WebServer/src/WebAuthentication.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef WEB_AUTHENTICATION_H_
 | 
			
		||||
#define WEB_AUTHENTICATION_H_
 | 
			
		||||
 | 
			
		||||
#include "Arduino.h"
 | 
			
		||||
 | 
			
		||||
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
 | 
			
		||||
String requestDigestAuthentication(const char * realm);
 | 
			
		||||
bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);
 | 
			
		||||
 | 
			
		||||
//for storing hashed versions on the device that can be authenticated against
 | 
			
		||||
String generateDigestHash(const char * username, const char * password, const char * realm);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										133
									
								
								Software/lib/ESP Async WebServer/src/WebHandlerImpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								Software/lib/ESP Async WebServer/src/WebHandlerImpl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ASYNCWEBSERVERHANDLERIMPL_H_
 | 
			
		||||
#define ASYNCWEBSERVERHANDLERIMPL_H_
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <regex>
 | 
			
		||||
 | 
			
		||||
#include "stddef.h"
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
class AsyncStaticWebHandler: public AsyncWebHandler {
 | 
			
		||||
   using File = fs::File;
 | 
			
		||||
   using FS = fs::FS;
 | 
			
		||||
  private:
 | 
			
		||||
    bool _getFile(AsyncWebServerRequest *request);
 | 
			
		||||
    bool _fileExists(AsyncWebServerRequest *request, const String& path);
 | 
			
		||||
    uint8_t _countBits(const uint8_t value) const;
 | 
			
		||||
  protected:
 | 
			
		||||
    FS _fs;
 | 
			
		||||
    String _uri;
 | 
			
		||||
    String _path;
 | 
			
		||||
    String _default_file;
 | 
			
		||||
    String _cache_control;
 | 
			
		||||
    String _last_modified;
 | 
			
		||||
    AwsTemplateProcessor _callback;
 | 
			
		||||
    bool _isDir;
 | 
			
		||||
    bool _gzipFirst;
 | 
			
		||||
    uint8_t _gzipStats;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    AsyncStaticWebHandler& setIsDir(bool isDir);
 | 
			
		||||
    AsyncStaticWebHandler& setDefaultFile(const char* filename);
 | 
			
		||||
    AsyncStaticWebHandler& setCacheControl(const char* cache_control);
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(const char* last_modified);
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
 | 
			
		||||
  #ifdef ESP8266
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(time_t last_modified);
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated
 | 
			
		||||
  #endif
 | 
			
		||||
    AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncCallbackWebHandler: public AsyncWebHandler {
 | 
			
		||||
  private:
 | 
			
		||||
  protected:
 | 
			
		||||
    String _uri;
 | 
			
		||||
    WebRequestMethodComposite _method;
 | 
			
		||||
    ArRequestHandlerFunction _onRequest;
 | 
			
		||||
    ArUploadHandlerFunction _onUpload;
 | 
			
		||||
    ArBodyHandlerFunction _onBody;
 | 
			
		||||
    bool _isRegex;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false){}
 | 
			
		||||
    void setUri(const String& uri){ 
 | 
			
		||||
      _uri = uri; 
 | 
			
		||||
       _isRegex = uri.startsWith("^") && uri.endsWith("$");
 | 
			
		||||
    }
 | 
			
		||||
    void setMethod(WebRequestMethodComposite method){ _method = method; }
 | 
			
		||||
    void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
 | 
			
		||||
    void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
 | 
			
		||||
    void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }
 | 
			
		||||
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final{
 | 
			
		||||
 | 
			
		||||
      if(!_onRequest)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      if(!(_method & request->method()))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      if (_isRegex) {
 | 
			
		||||
        std::regex rgx(_uri.c_str());
 | 
			
		||||
        std::smatch matches;
 | 
			
		||||
        std::string s(request->url().c_str());
 | 
			
		||||
        if(std::regex_search(s, matches, rgx)) {
 | 
			
		||||
          for (size_t i = 1; i < matches.size(); ++i) { // start from 1
 | 
			
		||||
            request->_addPathParam(matches[i].str().c_str());
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
      } else if (_uri.length() && _uri.endsWith("*")) {
 | 
			
		||||
        String uriTemplate = String(_uri);
 | 
			
		||||
	uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
 | 
			
		||||
        if (!request->url().startsWith(uriTemplate))
 | 
			
		||||
          return false;
 | 
			
		||||
      }
 | 
			
		||||
      else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      request->addInterestingHeader("ANY");
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final {
 | 
			
		||||
      if(_onRequest)
 | 
			
		||||
        _onRequest(request);
 | 
			
		||||
      else
 | 
			
		||||
        request->send(500);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
 | 
			
		||||
      if(_onUpload)
 | 
			
		||||
        _onUpload(request, filename, index, data, len, final);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
 | 
			
		||||
      if(_onBody)
 | 
			
		||||
        _onBody(request, data, len, index, total);
 | 
			
		||||
    }
 | 
			
		||||
    virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
 | 
			
		||||
							
								
								
									
										220
									
								
								Software/lib/ESP Async WebServer/src/WebHandlers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								Software/lib/ESP Async WebServer/src/WebHandlers.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,220 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#include "ESPAsyncWebServer.h"
 | 
			
		||||
#include "WebHandlerImpl.h"
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
 | 
			
		||||
  : _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr)
 | 
			
		||||
{
 | 
			
		||||
  // Ensure leading '/'
 | 
			
		||||
  if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
 | 
			
		||||
  if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
 | 
			
		||||
 | 
			
		||||
  // If path ends with '/' we assume a hint that this is a directory to improve performance.
 | 
			
		||||
  // However - if it does not end with '/' we, can't assume a file, path can still be a directory.
 | 
			
		||||
  _isDir = _path[_path.length()-1] == '/';
 | 
			
		||||
 | 
			
		||||
  // Remove the trailing '/' so we can handle default file
 | 
			
		||||
  // Notice that root will be "" not "/"
 | 
			
		||||
  if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
 | 
			
		||||
  if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
 | 
			
		||||
 | 
			
		||||
  // Reset stats
 | 
			
		||||
  _gzipFirst = false;
 | 
			
		||||
  _gzipStats = 0xF8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){
 | 
			
		||||
  _isDir = isDir;
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){
 | 
			
		||||
  _default_file = String(filename);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){
 | 
			
		||||
  _cache_control = String(cache_control);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
 | 
			
		||||
  _last_modified = String(last_modified);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
 | 
			
		||||
  char result[30];
 | 
			
		||||
  strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified);
 | 
			
		||||
  return setLastModified((const char *)result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef ESP8266
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){
 | 
			
		||||
  return setLastModified((struct tm *)gmtime(&last_modified));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
 | 
			
		||||
  time_t last_modified;
 | 
			
		||||
  if(time(&last_modified) == 0) //time is not yet set
 | 
			
		||||
    return *this;
 | 
			
		||||
  return setLastModified(last_modified);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
 | 
			
		||||
  if(request->method() != HTTP_GET 
 | 
			
		||||
    || !request->url().startsWith(_uri) 
 | 
			
		||||
    || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
 | 
			
		||||
  ){
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (_getFile(request)) {
 | 
			
		||||
    // We interested in "If-Modified-Since" header to check if file was modified
 | 
			
		||||
    if (_last_modified.length())
 | 
			
		||||
      request->addInterestingHeader("If-Modified-Since");
 | 
			
		||||
 | 
			
		||||
    if(_cache_control.length())
 | 
			
		||||
      request->addInterestingHeader("If-None-Match");
 | 
			
		||||
 | 
			
		||||
    DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
 | 
			
		||||
{
 | 
			
		||||
  // Remove the found uri
 | 
			
		||||
  String path = request->url().substring(_uri.length());
 | 
			
		||||
 | 
			
		||||
  // We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
 | 
			
		||||
  bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/');
 | 
			
		||||
 | 
			
		||||
  path = _path + path;
 | 
			
		||||
 | 
			
		||||
  // Do we have a file or .gz file
 | 
			
		||||
  if (!canSkipFileCheck && _fileExists(request, path))
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  // Can't handle if not default file
 | 
			
		||||
  if (_default_file.length() == 0)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // Try to add default file, ensure there is a trailing '/' ot the path.
 | 
			
		||||
  if (path.length() == 0 || path[path.length()-1] != '/')
 | 
			
		||||
    path += "/";
 | 
			
		||||
  path += _default_file;
 | 
			
		||||
 | 
			
		||||
  return _fileExists(request, path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
 | 
			
		||||
#else
 | 
			
		||||
#define FILE_IS_REAL(f) (f == true)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path)
 | 
			
		||||
{
 | 
			
		||||
  bool fileFound = false;
 | 
			
		||||
  bool gzipFound = false;
 | 
			
		||||
 | 
			
		||||
  String gzip = path + ".gz";
 | 
			
		||||
 | 
			
		||||
  if (_gzipFirst) {
 | 
			
		||||
    request->_tempFile = _fs.open(gzip, "r");
 | 
			
		||||
    gzipFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    if (!gzipFound){
 | 
			
		||||
      request->_tempFile = _fs.open(path, "r");
 | 
			
		||||
      fileFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    request->_tempFile = _fs.open(path, "r");
 | 
			
		||||
    fileFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    if (!fileFound){
 | 
			
		||||
      request->_tempFile = _fs.open(gzip, "r");
 | 
			
		||||
      gzipFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool found = fileFound || gzipFound;
 | 
			
		||||
 | 
			
		||||
  if (found) {
 | 
			
		||||
    // Extract the file name from the path and keep it in _tempObject
 | 
			
		||||
    size_t pathLen = path.length();
 | 
			
		||||
    char * _tempPath = (char*)malloc(pathLen+1);
 | 
			
		||||
    snprintf(_tempPath, pathLen+1, "%s", path.c_str());
 | 
			
		||||
    request->_tempObject = (void*)_tempPath;
 | 
			
		||||
 | 
			
		||||
    // Calculate gzip statistic
 | 
			
		||||
    _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
 | 
			
		||||
    if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
 | 
			
		||||
    else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
 | 
			
		||||
    else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const
 | 
			
		||||
{
 | 
			
		||||
  uint8_t w = value;
 | 
			
		||||
  uint8_t n;
 | 
			
		||||
  for (n=0; w!=0; n++) w&=w-1;
 | 
			
		||||
  return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
 | 
			
		||||
{
 | 
			
		||||
  // Get the filename from request->_tempObject and free it
 | 
			
		||||
  String filename = String((char*)request->_tempObject);
 | 
			
		||||
  free(request->_tempObject);
 | 
			
		||||
  request->_tempObject = NULL;
 | 
			
		||||
  if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
 | 
			
		||||
      return request->requestAuthentication();
 | 
			
		||||
 | 
			
		||||
  if (request->_tempFile == true) {
 | 
			
		||||
    String etag = String(request->_tempFile.size());
 | 
			
		||||
    if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
 | 
			
		||||
      request->_tempFile.close();
 | 
			
		||||
      request->send(304); // Not modified
 | 
			
		||||
    } else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) {
 | 
			
		||||
      request->_tempFile.close();
 | 
			
		||||
      AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified
 | 
			
		||||
      response->addHeader("Cache-Control", _cache_control);
 | 
			
		||||
      response->addHeader("ETag", etag);
 | 
			
		||||
      request->send(response);
 | 
			
		||||
    } else {
 | 
			
		||||
      AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
 | 
			
		||||
      if (_last_modified.length())
 | 
			
		||||
        response->addHeader("Last-Modified", _last_modified);
 | 
			
		||||
      if (_cache_control.length()){
 | 
			
		||||
        response->addHeader("Cache-Control", _cache_control);
 | 
			
		||||
        response->addHeader("ETag", etag);
 | 
			
		||||
      }
 | 
			
		||||
      request->send(response);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    request->send(404);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1009
									
								
								Software/lib/ESP Async WebServer/src/WebRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1009
									
								
								Software/lib/ESP Async WebServer/src/WebRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										136
									
								
								Software/lib/ESP Async WebServer/src/WebResponseImpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Software/lib/ESP Async WebServer/src/WebResponseImpl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ASYNCWEBSERVERRESPONSEIMPL_H_
 | 
			
		||||
#define ASYNCWEBSERVERRESPONSEIMPL_H_
 | 
			
		||||
 | 
			
		||||
#ifdef Arduino_h
 | 
			
		||||
// arduino is not compatible with std::vector
 | 
			
		||||
#undef min
 | 
			
		||||
#undef max
 | 
			
		||||
#endif
 | 
			
		||||
#include <vector>
 | 
			
		||||
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
 | 
			
		||||
 | 
			
		||||
class AsyncBasicResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _content;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String());
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncAbstractResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _head;
 | 
			
		||||
    // Data is inserted into cache at begin(). 
 | 
			
		||||
    // This is inefficient with vector, but if we use some other container, 
 | 
			
		||||
    // we won't be able to access it as contiguous array of bytes when reading from it,
 | 
			
		||||
    // so by gaining performance in one place, we'll lose it in another.
 | 
			
		||||
    std::vector<uint8_t> _cache;
 | 
			
		||||
    size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
 | 
			
		||||
    size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
 | 
			
		||||
  protected:
 | 
			
		||||
    AwsTemplateProcessor _callback;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return false; }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef TEMPLATE_PLACEHOLDER
 | 
			
		||||
#define TEMPLATE_PLACEHOLDER '%'
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TEMPLATE_PARAM_NAME_LENGTH 32
 | 
			
		||||
class AsyncFileResponse: public AsyncAbstractResponse {
 | 
			
		||||
  using File = fs::File;
 | 
			
		||||
  using FS = fs::FS;
 | 
			
		||||
  private:
 | 
			
		||||
    File _content;
 | 
			
		||||
    String _path;
 | 
			
		||||
    void _setContentType(const String& path);
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    ~AsyncFileResponse();
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncStreamResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    Stream *_content;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncCallbackResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    AwsResponseFiller _content;
 | 
			
		||||
    size_t _filledLength;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncChunkedResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    AwsResponseFiller _content;
 | 
			
		||||
    size_t _filledLength;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncProgmemResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    const uint8_t * _content;
 | 
			
		||||
    size_t _readLength;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class cbuf;
 | 
			
		||||
 | 
			
		||||
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
 | 
			
		||||
  private:
 | 
			
		||||
    cbuf *_content;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncResponseStream(const String& contentType, size_t bufferSize);
 | 
			
		||||
    ~AsyncResponseStream();
 | 
			
		||||
    bool _sourceValid() const { return (_state < RESPONSE_END); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
    size_t write(const uint8_t *data, size_t len);
 | 
			
		||||
    size_t write(uint8_t data);
 | 
			
		||||
    using Print::write;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */
 | 
			
		||||
							
								
								
									
										697
									
								
								Software/lib/ESP Async WebServer/src/WebResponses.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										697
									
								
								Software/lib/ESP Async WebServer/src/WebResponses.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,697 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#include "ESPAsyncWebServer.h"
 | 
			
		||||
#include "WebResponseImpl.h"
 | 
			
		||||
#include "cbuf.h"
 | 
			
		||||
 | 
			
		||||
// Since ESP8266 does not link memchr by default, here's its implementation.
 | 
			
		||||
void* memchr(void* ptr, int ch, size_t count)
 | 
			
		||||
{
 | 
			
		||||
  unsigned char* p = static_cast<unsigned char*>(ptr);
 | 
			
		||||
  while(count--)
 | 
			
		||||
    if(*p++ == static_cast<unsigned char>(ch))
 | 
			
		||||
      return --p;
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Abstract Response
 | 
			
		||||
 * */
 | 
			
		||||
const char* AsyncWebServerResponse::_responseCodeToString(int code) {
 | 
			
		||||
  switch (code) {
 | 
			
		||||
    case 100: return "Continue";
 | 
			
		||||
    case 101: return "Switching Protocols";
 | 
			
		||||
    case 200: return "OK";
 | 
			
		||||
    case 201: return "Created";
 | 
			
		||||
    case 202: return "Accepted";
 | 
			
		||||
    case 203: return "Non-Authoritative Information";
 | 
			
		||||
    case 204: return "No Content";
 | 
			
		||||
    case 205: return "Reset Content";
 | 
			
		||||
    case 206: return "Partial Content";
 | 
			
		||||
    case 300: return "Multiple Choices";
 | 
			
		||||
    case 301: return "Moved Permanently";
 | 
			
		||||
    case 302: return "Found";
 | 
			
		||||
    case 303: return "See Other";
 | 
			
		||||
    case 304: return "Not Modified";
 | 
			
		||||
    case 305: return "Use Proxy";
 | 
			
		||||
    case 307: return "Temporary Redirect";
 | 
			
		||||
    case 400: return "Bad Request";
 | 
			
		||||
    case 401: return "Unauthorized";
 | 
			
		||||
    case 402: return "Payment Required";
 | 
			
		||||
    case 403: return "Forbidden";
 | 
			
		||||
    case 404: return "Not Found";
 | 
			
		||||
    case 405: return "Method Not Allowed";
 | 
			
		||||
    case 406: return "Not Acceptable";
 | 
			
		||||
    case 407: return "Proxy Authentication Required";
 | 
			
		||||
    case 408: return "Request Time-out";
 | 
			
		||||
    case 409: return "Conflict";
 | 
			
		||||
    case 410: return "Gone";
 | 
			
		||||
    case 411: return "Length Required";
 | 
			
		||||
    case 412: return "Precondition Failed";
 | 
			
		||||
    case 413: return "Request Entity Too Large";
 | 
			
		||||
    case 414: return "Request-URI Too Large";
 | 
			
		||||
    case 415: return "Unsupported Media Type";
 | 
			
		||||
    case 416: return "Requested range not satisfiable";
 | 
			
		||||
    case 417: return "Expectation Failed";
 | 
			
		||||
    case 500: return "Internal Server Error";
 | 
			
		||||
    case 501: return "Not Implemented";
 | 
			
		||||
    case 502: return "Bad Gateway";
 | 
			
		||||
    case 503: return "Service Unavailable";
 | 
			
		||||
    case 504: return "Gateway Time-out";
 | 
			
		||||
    case 505: return "HTTP Version not supported";
 | 
			
		||||
    default:  return "";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebServerResponse::AsyncWebServerResponse()
 | 
			
		||||
  : _code(0)
 | 
			
		||||
  , _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
 | 
			
		||||
  , _contentType()
 | 
			
		||||
  , _contentLength(0)
 | 
			
		||||
  , _sendContentLength(true)
 | 
			
		||||
  , _chunked(false)
 | 
			
		||||
  , _headLength(0)
 | 
			
		||||
  , _sentLength(0)
 | 
			
		||||
  , _ackedLength(0)
 | 
			
		||||
  , _writtenLength(0)
 | 
			
		||||
  , _state(RESPONSE_SETUP)
 | 
			
		||||
{
 | 
			
		||||
  for(auto header: DefaultHeaders::Instance()) {
 | 
			
		||||
    _headers.add(new AsyncWebHeader(header->name(), header->value()));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebServerResponse::~AsyncWebServerResponse(){
 | 
			
		||||
  _headers.free();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::setCode(int code){
 | 
			
		||||
  if(_state == RESPONSE_SETUP)
 | 
			
		||||
    _code = code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::setContentLength(size_t len){
 | 
			
		||||
  if(_state == RESPONSE_SETUP)
 | 
			
		||||
    _contentLength = len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::setContentType(const String& type){
 | 
			
		||||
  if(_state == RESPONSE_SETUP)
 | 
			
		||||
    _contentType = type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::addHeader(const String& name, const String& value){
 | 
			
		||||
  _headers.add(new AsyncWebHeader(name, value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String AsyncWebServerResponse::_assembleHead(uint8_t version){
 | 
			
		||||
  if(version){
 | 
			
		||||
    addHeader("Accept-Ranges","none");
 | 
			
		||||
    if(_chunked)
 | 
			
		||||
      addHeader("Transfer-Encoding","chunked");
 | 
			
		||||
  }
 | 
			
		||||
  String out = String();
 | 
			
		||||
  int bufSize = 300;
 | 
			
		||||
  char buf[bufSize];
 | 
			
		||||
 | 
			
		||||
  snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code));
 | 
			
		||||
  out.concat(buf);
 | 
			
		||||
 | 
			
		||||
  if(_sendContentLength) {
 | 
			
		||||
    snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength);
 | 
			
		||||
    out.concat(buf);
 | 
			
		||||
  }
 | 
			
		||||
  if(_contentType.length()) {
 | 
			
		||||
    snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str());
 | 
			
		||||
    out.concat(buf);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for(const auto& header: _headers){
 | 
			
		||||
    snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str());
 | 
			
		||||
    out.concat(buf);
 | 
			
		||||
  }
 | 
			
		||||
  _headers.free();
 | 
			
		||||
 | 
			
		||||
  out.concat("\r\n");
 | 
			
		||||
  _headLength = out.length();
 | 
			
		||||
  return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
 | 
			
		||||
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
 | 
			
		||||
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
 | 
			
		||||
bool AsyncWebServerResponse::_sourceValid() const { return false; }
 | 
			
		||||
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
 | 
			
		||||
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ return 0; }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * String/Code Response
 | 
			
		||||
 * */
 | 
			
		||||
AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){
 | 
			
		||||
  _code = code;
 | 
			
		||||
  _content = content;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  if(_content.length()){
 | 
			
		||||
    _contentLength = _content.length();
 | 
			
		||||
    if(!_contentType.length())
 | 
			
		||||
      _contentType = "text/plain";
 | 
			
		||||
  }
 | 
			
		||||
  addHeader("Connection","close");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
 | 
			
		||||
  _state = RESPONSE_HEADERS;
 | 
			
		||||
  String out = _assembleHead(request->version());
 | 
			
		||||
  size_t outLen = out.length();
 | 
			
		||||
  size_t space = request->client()->space();
 | 
			
		||||
  if(!_contentLength && space >= outLen){
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), outLen);
 | 
			
		||||
    _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
  } else if(_contentLength && space >= outLen + _contentLength){
 | 
			
		||||
    out += _content;
 | 
			
		||||
    outLen += _contentLength;
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), outLen);
 | 
			
		||||
    _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
  } else if(space && space < outLen){
 | 
			
		||||
    String partial = out.substring(0, space);
 | 
			
		||||
    _content = out.substring(space) + _content;
 | 
			
		||||
    _contentLength += outLen - space;
 | 
			
		||||
    _writtenLength += request->client()->write(partial.c_str(), partial.length());
 | 
			
		||||
    _state = RESPONSE_CONTENT;
 | 
			
		||||
  } else if(space > outLen && space < (outLen + _contentLength)){
 | 
			
		||||
    size_t shift = space - outLen;
 | 
			
		||||
    outLen += shift;
 | 
			
		||||
    _sentLength += shift;
 | 
			
		||||
    out += _content.substring(0, shift);
 | 
			
		||||
    _content = _content.substring(shift);
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), outLen);
 | 
			
		||||
    _state = RESPONSE_CONTENT;
 | 
			
		||||
  } else {
 | 
			
		||||
    _content = out + _content;
 | 
			
		||||
    _contentLength += outLen;
 | 
			
		||||
    _state = RESPONSE_CONTENT;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
 | 
			
		||||
  _ackedLength += len;
 | 
			
		||||
  if(_state == RESPONSE_CONTENT){
 | 
			
		||||
    size_t available = _contentLength - _sentLength;
 | 
			
		||||
    size_t space = request->client()->space();
 | 
			
		||||
    //we can fit in this packet
 | 
			
		||||
    if(space > available){
 | 
			
		||||
      _writtenLength += request->client()->write(_content.c_str(), available);
 | 
			
		||||
      _content = String();
 | 
			
		||||
      _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
      return available;
 | 
			
		||||
    }
 | 
			
		||||
    //send some data, the rest on ack
 | 
			
		||||
    String out = _content.substring(0, space);
 | 
			
		||||
    _content = _content.substring(space);
 | 
			
		||||
    _sentLength += space;
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), space);
 | 
			
		||||
    return space;
 | 
			
		||||
  } else if(_state == RESPONSE_WAIT_ACK){
 | 
			
		||||
    if(_ackedLength >= _writtenLength){
 | 
			
		||||
      _state = RESPONSE_END;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Abstract Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback)
 | 
			
		||||
{
 | 
			
		||||
  // In case of template processing, we're unable to determine real response size
 | 
			
		||||
  if(callback) {
 | 
			
		||||
    _contentLength = 0;
 | 
			
		||||
    _sendContentLength = false;
 | 
			
		||||
    _chunked = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
 | 
			
		||||
  addHeader("Connection","close");
 | 
			
		||||
  _head = _assembleHead(request->version());
 | 
			
		||||
  _state = RESPONSE_HEADERS;
 | 
			
		||||
  _ack(request, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
 | 
			
		||||
  if(!_sourceValid()){
 | 
			
		||||
    _state = RESPONSE_FAILED;
 | 
			
		||||
    request->client()->close();
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  _ackedLength += len;
 | 
			
		||||
  size_t space = request->client()->space();
 | 
			
		||||
 | 
			
		||||
  size_t headLen = _head.length();
 | 
			
		||||
  if(_state == RESPONSE_HEADERS){
 | 
			
		||||
    if(space >= headLen){
 | 
			
		||||
      _state = RESPONSE_CONTENT;
 | 
			
		||||
      space -= headLen;
 | 
			
		||||
    } else {
 | 
			
		||||
      String out = _head.substring(0, space);
 | 
			
		||||
      _head = _head.substring(space);
 | 
			
		||||
      _writtenLength += request->client()->write(out.c_str(), out.length());
 | 
			
		||||
      return out.length();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(_state == RESPONSE_CONTENT){
 | 
			
		||||
    size_t outLen;
 | 
			
		||||
    if(_chunked){
 | 
			
		||||
      if(space <= 8){
 | 
			
		||||
        return 0;
 | 
			
		||||
      }
 | 
			
		||||
      outLen = space;
 | 
			
		||||
    } else if(!_sendContentLength){
 | 
			
		||||
      outLen = space;
 | 
			
		||||
    } else {
 | 
			
		||||
      outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t *buf = (uint8_t *)malloc(outLen+headLen);
 | 
			
		||||
    if (!buf) {
 | 
			
		||||
      // os_printf("_ack malloc %d failed\n", outLen+headLen);
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(headLen){
 | 
			
		||||
      memcpy(buf, _head.c_str(), _head.length());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t readLen = 0;
 | 
			
		||||
 | 
			
		||||
    if(_chunked){
 | 
			
		||||
      // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
 | 
			
		||||
      // See RFC2616 sections 2, 3.6.1.
 | 
			
		||||
      readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
 | 
			
		||||
      if(readLen == RESPONSE_TRY_AGAIN){
 | 
			
		||||
          free(buf);
 | 
			
		||||
          return 0;
 | 
			
		||||
      }
 | 
			
		||||
      outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
 | 
			
		||||
      while(outLen < headLen + 4) buf[outLen++] = ' ';
 | 
			
		||||
      buf[outLen++] = '\r';
 | 
			
		||||
      buf[outLen++] = '\n';
 | 
			
		||||
      outLen += readLen;
 | 
			
		||||
      buf[outLen++] = '\r';
 | 
			
		||||
      buf[outLen++] = '\n';
 | 
			
		||||
    } else {
 | 
			
		||||
      readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
 | 
			
		||||
      if(readLen == RESPONSE_TRY_AGAIN){
 | 
			
		||||
          free(buf);
 | 
			
		||||
          return 0;
 | 
			
		||||
      }
 | 
			
		||||
      outLen = readLen + headLen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(headLen){
 | 
			
		||||
        _head = String();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(outLen){
 | 
			
		||||
        _writtenLength += request->client()->write((const char*)buf, outLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(_chunked){
 | 
			
		||||
        _sentLength += readLen;
 | 
			
		||||
    } else {
 | 
			
		||||
        _sentLength += outLen - headLen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(buf);
 | 
			
		||||
 | 
			
		||||
    if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){
 | 
			
		||||
      _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
    }
 | 
			
		||||
    return outLen;
 | 
			
		||||
 | 
			
		||||
  } else if(_state == RESPONSE_WAIT_ACK){
 | 
			
		||||
    if(!_sendContentLength || _ackedLength >= _writtenLength){
 | 
			
		||||
      _state = RESPONSE_END;
 | 
			
		||||
      if(!_chunked && !_sendContentLength)
 | 
			
		||||
        request->client()->close(true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len)
 | 
			
		||||
{
 | 
			
		||||
    // If we have something in cache, copy it to buffer
 | 
			
		||||
    const size_t readFromCache = std::min(len, _cache.size());
 | 
			
		||||
    if(readFromCache) {
 | 
			
		||||
      memcpy(data, _cache.data(), readFromCache);
 | 
			
		||||
      _cache.erase(_cache.begin(), _cache.begin() + readFromCache);
 | 
			
		||||
    }
 | 
			
		||||
    // If we need to read more...
 | 
			
		||||
    const size_t needFromFile = len - readFromCache;
 | 
			
		||||
    const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
 | 
			
		||||
    return readFromCache + readFromContent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
  if(!_callback)
 | 
			
		||||
    return _fillBuffer(data, len);
 | 
			
		||||
 | 
			
		||||
  const size_t originalLen = len;
 | 
			
		||||
  len = _readDataFromCacheOrContent(data, len);
 | 
			
		||||
  // Now we've read 'len' bytes, either from cache or from file
 | 
			
		||||
  // Search for template placeholders
 | 
			
		||||
  uint8_t* pTemplateStart = data;
 | 
			
		||||
  while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
 | 
			
		||||
    uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
 | 
			
		||||
    // temporary buffer to hold parameter name
 | 
			
		||||
    uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
 | 
			
		||||
    String paramName;
 | 
			
		||||
    // If closing placeholder is found:
 | 
			
		||||
    if(pTemplateEnd) {
 | 
			
		||||
      // prepare argument to callback
 | 
			
		||||
      const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1));
 | 
			
		||||
      if(paramNameLength) {
 | 
			
		||||
        memcpy(buf, pTemplateStart + 1, paramNameLength);
 | 
			
		||||
        buf[paramNameLength] = 0;
 | 
			
		||||
        paramName = String(reinterpret_cast<char*>(buf));
 | 
			
		||||
      } else { // double percent sign encountered, this is single percent sign escaped.
 | 
			
		||||
        // remove the 2nd percent sign
 | 
			
		||||
        memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
 | 
			
		||||
        len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
 | 
			
		||||
        ++pTemplateStart;
 | 
			
		||||
      }
 | 
			
		||||
    } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
 | 
			
		||||
      memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
 | 
			
		||||
      const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
 | 
			
		||||
      if(readFromCacheOrContent) {
 | 
			
		||||
        pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
 | 
			
		||||
        if(pTemplateEnd) {
 | 
			
		||||
          // prepare argument to callback
 | 
			
		||||
          *pTemplateEnd = 0;
 | 
			
		||||
          paramName = String(reinterpret_cast<char*>(buf));
 | 
			
		||||
          // Copy remaining read-ahead data into cache
 | 
			
		||||
          _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
 | 
			
		||||
          pTemplateEnd = &data[len - 1];
 | 
			
		||||
        }
 | 
			
		||||
        else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
 | 
			
		||||
        {
 | 
			
		||||
          // but first, store read file data in cache
 | 
			
		||||
          _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
 | 
			
		||||
          ++pTemplateStart;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
 | 
			
		||||
        ++pTemplateStart;
 | 
			
		||||
    }
 | 
			
		||||
    else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
 | 
			
		||||
      ++pTemplateStart;
 | 
			
		||||
    if(paramName.length()) {
 | 
			
		||||
      // call callback and replace with result.
 | 
			
		||||
      // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
 | 
			
		||||
      // Data after pTemplateEnd may need to be moved.
 | 
			
		||||
      // The first byte of data after placeholder is located at pTemplateEnd + 1.
 | 
			
		||||
      // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
 | 
			
		||||
      const String paramValue(_callback(paramName));
 | 
			
		||||
      const char* pvstr = paramValue.c_str();
 | 
			
		||||
      const unsigned int pvlen = paramValue.length();
 | 
			
		||||
      const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
 | 
			
		||||
      // make room for param value
 | 
			
		||||
      // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
 | 
			
		||||
      if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
 | 
			
		||||
        _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
 | 
			
		||||
        //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
 | 
			
		||||
        memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
 | 
			
		||||
        len = originalLen; // fix issue with truncated data, not sure if it has any side effects
 | 
			
		||||
      } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
 | 
			
		||||
        //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
 | 
			
		||||
        //   Move the entire data after the placeholder
 | 
			
		||||
        memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
 | 
			
		||||
      // 3. replace placeholder with actual value
 | 
			
		||||
      memcpy(pTemplateStart, pvstr, numBytesCopied);
 | 
			
		||||
      // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
 | 
			
		||||
      if(numBytesCopied < pvlen) {
 | 
			
		||||
        _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
 | 
			
		||||
      } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
 | 
			
		||||
        // there is some free room, fill it from cache
 | 
			
		||||
        const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
 | 
			
		||||
        const size_t totalFreeRoom = originalLen - len + roomFreed;
 | 
			
		||||
        len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed;
 | 
			
		||||
      } else { // result is copied fully; it is longer than placeholder text
 | 
			
		||||
        const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1;
 | 
			
		||||
        len = std::min(len + roomTaken, originalLen);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } // while(pTemplateStart)
 | 
			
		||||
  return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * File Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncFileResponse::~AsyncFileResponse(){
 | 
			
		||||
  if(_content)
 | 
			
		||||
    _content.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncFileResponse::_setContentType(const String& path){
 | 
			
		||||
  if (path.endsWith(".html")) _contentType = "text/html";
 | 
			
		||||
  else if (path.endsWith(".htm")) _contentType = "text/html";
 | 
			
		||||
  else if (path.endsWith(".css")) _contentType = "text/css";
 | 
			
		||||
  else if (path.endsWith(".json")) _contentType = "application/json";
 | 
			
		||||
  else if (path.endsWith(".js")) _contentType = "application/javascript";
 | 
			
		||||
  else if (path.endsWith(".png")) _contentType = "image/png";
 | 
			
		||||
  else if (path.endsWith(".gif")) _contentType = "image/gif";
 | 
			
		||||
  else if (path.endsWith(".jpg")) _contentType = "image/jpeg";
 | 
			
		||||
  else if (path.endsWith(".ico")) _contentType = "image/x-icon";
 | 
			
		||||
  else if (path.endsWith(".svg")) _contentType = "image/svg+xml";
 | 
			
		||||
  else if (path.endsWith(".eot")) _contentType = "font/eot";
 | 
			
		||||
  else if (path.endsWith(".woff")) _contentType = "font/woff";
 | 
			
		||||
  else if (path.endsWith(".woff2")) _contentType = "font/woff2";
 | 
			
		||||
  else if (path.endsWith(".ttf")) _contentType = "font/ttf";
 | 
			
		||||
  else if (path.endsWith(".xml")) _contentType = "text/xml";
 | 
			
		||||
  else if (path.endsWith(".pdf")) _contentType = "application/pdf";
 | 
			
		||||
  else if (path.endsWith(".zip")) _contentType = "application/zip";
 | 
			
		||||
  else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
 | 
			
		||||
  else _contentType = "text/plain";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _path = path;
 | 
			
		||||
 | 
			
		||||
  if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
 | 
			
		||||
    _path = _path+".gz";
 | 
			
		||||
    addHeader("Content-Encoding", "gzip");
 | 
			
		||||
    _callback = nullptr; // Unable to process zipped templates
 | 
			
		||||
    _sendContentLength = true;
 | 
			
		||||
    _chunked = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _content = fs.open(_path, "r");
 | 
			
		||||
  _contentLength = _content.size();
 | 
			
		||||
 | 
			
		||||
  if(contentType == "")
 | 
			
		||||
    _setContentType(path);
 | 
			
		||||
  else
 | 
			
		||||
    _contentType = contentType;
 | 
			
		||||
 | 
			
		||||
  int filenameStart = path.lastIndexOf('/') + 1;
 | 
			
		||||
  char buf[26+path.length()-filenameStart];
 | 
			
		||||
  char* filename = (char*)path.c_str() + filenameStart;
 | 
			
		||||
 | 
			
		||||
  if(download) {
 | 
			
		||||
    // set filename and force download
 | 
			
		||||
    snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
 | 
			
		||||
  } else {
 | 
			
		||||
    // set filename and force rendering
 | 
			
		||||
    snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
 | 
			
		||||
  }
 | 
			
		||||
  addHeader("Content-Disposition", buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _path = path;
 | 
			
		||||
 | 
			
		||||
  if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){
 | 
			
		||||
    addHeader("Content-Encoding", "gzip");
 | 
			
		||||
    _callback = nullptr; // Unable to process gzipped templates
 | 
			
		||||
    _sendContentLength = true;
 | 
			
		||||
    _chunked = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _content = content;
 | 
			
		||||
  _contentLength = _content.size();
 | 
			
		||||
 | 
			
		||||
  if(contentType == "")
 | 
			
		||||
    _setContentType(path);
 | 
			
		||||
  else
 | 
			
		||||
    _contentType = contentType;
 | 
			
		||||
 | 
			
		||||
  int filenameStart = path.lastIndexOf('/') + 1;
 | 
			
		||||
  char buf[26+path.length()-filenameStart];
 | 
			
		||||
  char* filename = (char*)path.c_str() + filenameStart;
 | 
			
		||||
 | 
			
		||||
  if(download) {
 | 
			
		||||
    snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
 | 
			
		||||
  } else {
 | 
			
		||||
    snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
 | 
			
		||||
  }
 | 
			
		||||
  addHeader("Content-Disposition", buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  return _content.read(data, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Stream Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _content = &stream;
 | 
			
		||||
  _contentLength = len;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t available = _content->available();
 | 
			
		||||
  size_t outLen = (available > len)?len:available;
 | 
			
		||||
  size_t i;
 | 
			
		||||
  for(i=0;i<outLen;i++)
 | 
			
		||||
    data[i] = _content->read();
 | 
			
		||||
  return outLen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Callback Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) {
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _content = callback;
 | 
			
		||||
  _contentLength = len;
 | 
			
		||||
  if(!len)
 | 
			
		||||
    _sendContentLength = false;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _filledLength = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t ret = _content(data, len, _filledLength);
 | 
			
		||||
  if(ret != RESPONSE_TRY_AGAIN){
 | 
			
		||||
      _filledLength += ret;
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Chunked Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) {
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _content = callback;
 | 
			
		||||
  _contentLength = 0;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _sendContentLength = false;
 | 
			
		||||
  _chunked = true;
 | 
			
		||||
  _filledLength = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t ret = _content(data, len, _filledLength);
 | 
			
		||||
  if(ret != RESPONSE_TRY_AGAIN){
 | 
			
		||||
      _filledLength += ret;
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Progmem Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
 | 
			
		||||
  _code = code;
 | 
			
		||||
  _content = content;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _contentLength = len;
 | 
			
		||||
  _readLength = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t left = _contentLength - _readLength;
 | 
			
		||||
  if (left > len) {
 | 
			
		||||
    memcpy_P(data, _content + _readLength, len);
 | 
			
		||||
    _readLength += len;
 | 
			
		||||
    return len;
 | 
			
		||||
  }
 | 
			
		||||
  memcpy_P(data, _content + _readLength, left);
 | 
			
		||||
  _readLength += left;
 | 
			
		||||
  return left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Response Stream (You can print/write/printf to it, up to the contentLen bytes)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _contentLength = 0;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _content = new cbuf(bufferSize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncResponseStream::~AsyncResponseStream(){
 | 
			
		||||
  delete _content;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
 | 
			
		||||
  return _content->read((char*)buf, maxLen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
 | 
			
		||||
  if(_started())
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if(len > _content->room()){
 | 
			
		||||
    size_t needed = len - _content->room();
 | 
			
		||||
    _content->resizeAdd(needed);
 | 
			
		||||
  }
 | 
			
		||||
  size_t written = _content->write((const char*)data, len);
 | 
			
		||||
  _contentLength += written;
 | 
			
		||||
  return written;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncResponseStream::write(uint8_t data){
 | 
			
		||||
  return write(&data, 1);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										193
									
								
								Software/lib/ESP Async WebServer/src/WebServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								Software/lib/ESP Async WebServer/src/WebServer.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  This library is free software; you can redistribute it and/or
 | 
			
		||||
  modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
  License as published by the Free Software Foundation; either
 | 
			
		||||
  version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  This library is distributed in the hope that it will be useful,
 | 
			
		||||
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
  Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with this library; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
*/
 | 
			
		||||
#include "ESPAsyncWebServer.h"
 | 
			
		||||
#include "WebHandlerImpl.h"
 | 
			
		||||
 | 
			
		||||
bool ON_STA_FILTER(AsyncWebServerRequest *request) {
 | 
			
		||||
  return WiFi.localIP() == request->client()->localIP();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ON_AP_FILTER(AsyncWebServerRequest *request) {
 | 
			
		||||
  return WiFi.localIP() != request->client()->localIP();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AsyncWebServer::AsyncWebServer(uint16_t port)
 | 
			
		||||
  : _server(port)
 | 
			
		||||
  , _rewrites(LinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; }))
 | 
			
		||||
  , _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
 | 
			
		||||
{
 | 
			
		||||
  _catchAllHandler = new AsyncCallbackWebHandler();
 | 
			
		||||
  if(_catchAllHandler == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
  _server.onClient([](void *s, AsyncClient* c){
 | 
			
		||||
    if(c == NULL)
 | 
			
		||||
      return;
 | 
			
		||||
    c->setRxTimeout(3);
 | 
			
		||||
    AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c);
 | 
			
		||||
    if(r == NULL){
 | 
			
		||||
      c->close(true);
 | 
			
		||||
      c->free();
 | 
			
		||||
      delete c;
 | 
			
		||||
    }
 | 
			
		||||
  }, this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebServer::~AsyncWebServer(){
 | 
			
		||||
  reset();  
 | 
			
		||||
  end();
 | 
			
		||||
  if(_catchAllHandler) delete _catchAllHandler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){
 | 
			
		||||
  _rewrites.add(rewrite);
 | 
			
		||||
  return *rewrite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){
 | 
			
		||||
  return _rewrites.remove(rewrite);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){
 | 
			
		||||
  return addRewrite(new AsyncWebRewrite(from, to));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){
 | 
			
		||||
  _handlers.add(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
 | 
			
		||||
  return _handlers.remove(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::begin(){
 | 
			
		||||
  _server.setNoDelay(true);
 | 
			
		||||
  _server.begin();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::end(){
 | 
			
		||||
  _server.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ASYNC_TCP_SSL_ENABLED
 | 
			
		||||
void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){
 | 
			
		||||
  _server.onSslFileRequest(cb, arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){
 | 
			
		||||
  _server.beginSecure(cert, key, password);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){
 | 
			
		||||
  delete request;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){
 | 
			
		||||
  for(const auto& r: _rewrites){
 | 
			
		||||
    if (r->match(request)){
 | 
			
		||||
      request->_url = r->toUrl();
 | 
			
		||||
      request->_addGetParams(r->params());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
 | 
			
		||||
  for(const auto& h: _handlers){
 | 
			
		||||
    if (h->filter(request) && h->canHandle(request)){
 | 
			
		||||
      request->setHandler(h);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  request->addInterestingHeader("ANY");
 | 
			
		||||
  request->setHandler(_catchAllHandler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->setMethod(method);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  handler->onUpload(onUpload);
 | 
			
		||||
  handler->onBody(onBody);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->setMethod(method);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  handler->onUpload(onUpload);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->setMethod(method);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){
 | 
			
		||||
  AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){
 | 
			
		||||
  _catchAllHandler->onRequest(fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){
 | 
			
		||||
  _catchAllHandler->onUpload(fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
 | 
			
		||||
  _catchAllHandler->onBody(fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::reset(){
 | 
			
		||||
  _rewrites.free();
 | 
			
		||||
  _handlers.free();
 | 
			
		||||
  
 | 
			
		||||
  if (_catchAllHandler != NULL){
 | 
			
		||||
    _catchAllHandler->onRequest(NULL);
 | 
			
		||||
    _catchAllHandler->onUpload(NULL);
 | 
			
		||||
    _catchAllHandler->onBody(NULL);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										627
									
								
								Software/lib/ESP Async WebServer/src/edit.htm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										627
									
								
								Software/lib/ESP Async WebServer/src/edit.htm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,627 @@
 | 
			
		||||
<!--This is the plain html source of the hex encoded Editor-Page embedded in SPIFFSEditor.cpp -->
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
<title>ESP Editor</title>
 | 
			
		||||
<style type="text/css" media="screen">
 | 
			
		||||
.cm {
 | 
			
		||||
  z-index: 300;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 5px;
 | 
			
		||||
  border: 1px solid #444;
 | 
			
		||||
  background-color: #F5F5F5;
 | 
			
		||||
  display: none;
 | 
			
		||||
  box-shadow: 0 0 10px rgba( 0, 0, 0, .4 );
 | 
			
		||||
  font-size: 12px;
 | 
			
		||||
  font-family: sans-serif;
 | 
			
		||||
  font-weight:bold;
 | 
			
		||||
}
 | 
			
		||||
.cm ul {
 | 
			
		||||
  list-style: none;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
.cm li {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  min-width: 60px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
.cm span {
 | 
			
		||||
  color: #444;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  padding: 6px;
 | 
			
		||||
}
 | 
			
		||||
.cm li:hover { background: #444; }
 | 
			
		||||
.cm li:hover span { color: #EEE; }
 | 
			
		||||
.tvu ul, .tvu li {
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  list-style: none;
 | 
			
		||||
}
 | 
			
		||||
.tvu input {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
}
 | 
			
		||||
.tvu {
 | 
			
		||||
  font: normal 12px Verdana, Arial, Sans-serif;
 | 
			
		||||
  -moz-user-select: none;
 | 
			
		||||
  -webkit-user-select: none;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
  color: #444;
 | 
			
		||||
  line-height: 16px;
 | 
			
		||||
}
 | 
			
		||||
.tvu span {
 | 
			
		||||
  margin-bottom:5px;
 | 
			
		||||
  padding: 0 0 0 18px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  height: 16px;
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
  background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKOgiihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQIV2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4trRCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC') no-repeat;
 | 
			
		||||
  background-position: 0px 0px;
 | 
			
		||||
}
 | 
			
		||||
.tvu span:hover {
 | 
			
		||||
  text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
@media screen and (-webkit-min-device-pixel-ratio:0){
 | 
			
		||||
  .tvu{
 | 
			
		||||
    -webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @-webkit-keyframes webkit-adjacent-element-selector-bugfix {
 | 
			
		||||
    from { 
 | 
			
		||||
      padding: 0;
 | 
			
		||||
    } 
 | 
			
		||||
    to { 
 | 
			
		||||
      padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#uploader { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  height:28px;
 | 
			
		||||
  line-height: 24px;
 | 
			
		||||
  padding-left: 10px;
 | 
			
		||||
  background-color: #444;
 | 
			
		||||
  color:#EEE;
 | 
			
		||||
}
 | 
			
		||||
#tree { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 28px;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width:160px;
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
}
 | 
			
		||||
#editor, #preview { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 28px;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 160px;
 | 
			
		||||
  border-left:1px solid #EEE;
 | 
			
		||||
}
 | 
			
		||||
#preview {
 | 
			
		||||
  background-color: #EEE;
 | 
			
		||||
  padding:5px;
 | 
			
		||||
}
 | 
			
		||||
#loader { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 36%;
 | 
			
		||||
  right: 40%;
 | 
			
		||||
}
 | 
			
		||||
.loader {
 | 
			
		||||
    z-index: 10000;
 | 
			
		||||
    border: 8px solid #b5b5b5; /* Grey */
 | 
			
		||||
    border-top: 8px solid #3498db; /* Blue */
 | 
			
		||||
    border-bottom: 8px solid #3498db; /* Blue */
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
    width: 240px;
 | 
			
		||||
    height: 240px;
 | 
			
		||||
    animation: spin 2s linear infinite;
 | 
			
		||||
    display:none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes spin {
 | 
			
		||||
    0% { transform: rotate(0deg); }
 | 
			
		||||
    100% { transform: rotate(360deg); }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
if (typeof XMLHttpRequest === "undefined") {
 | 
			
		||||
  XMLHttpRequest = function () {
 | 
			
		||||
    try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
 | 
			
		||||
    try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
 | 
			
		||||
    try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
 | 
			
		||||
    throw new Error("This browser does not support XMLHttpRequest.");
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ge(a){
 | 
			
		||||
  return document.getElementById(a);
 | 
			
		||||
}
 | 
			
		||||
function ce(a){
 | 
			
		||||
  return document.createElement(a);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sortByKey(array, key) {
 | 
			
		||||
  return array.sort(function(a, b) {
 | 
			
		||||
    var x = a[key]; var y = b[key];
 | 
			
		||||
    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var QueuedRequester = function () {
 | 
			
		||||
  this.queue = [];
 | 
			
		||||
  this.running = false;
 | 
			
		||||
  this.xmlhttp = null;
 | 
			
		||||
}
 | 
			
		||||
QueuedRequester.prototype = {
 | 
			
		||||
  _request: function(req){
 | 
			
		||||
    this.running = true;
 | 
			
		||||
    if(!req instanceof Object) return;
 | 
			
		||||
    var that = this;
 | 
			
		||||
    
 | 
			
		||||
    function ajaxCb(x,d){ return function(){
 | 
			
		||||
      if (x.readyState == 4){
 | 
			
		||||
        ge("loader").style.display = "none";
 | 
			
		||||
        d.callback(x.status, x.responseText);
 | 
			
		||||
        if(that.queue.length === 0) that.running = false;
 | 
			
		||||
        if(that.running) that._request(that.queue.shift());
 | 
			
		||||
      }
 | 
			
		||||
    }}
 | 
			
		||||
    
 | 
			
		||||
    ge("loader").style.display = "block";
 | 
			
		||||
    
 | 
			
		||||
    var p = "";
 | 
			
		||||
    if(req.params instanceof FormData){
 | 
			
		||||
      p = req.params;
 | 
			
		||||
    } else if(req.params instanceof Object){
 | 
			
		||||
      for (var key in req.params) {
 | 
			
		||||
        if(p === "")
 | 
			
		||||
          p += (req.method === "GET")?"?":"";
 | 
			
		||||
        else
 | 
			
		||||
          p += "&";
 | 
			
		||||
        p += encodeURIComponent(key)+"="+encodeURIComponent(req.params[key]);
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    this.xmlhttp = new XMLHttpRequest();
 | 
			
		||||
    this.xmlhttp.onreadystatechange = ajaxCb(this.xmlhttp, req);
 | 
			
		||||
    if(req.method === "GET"){
 | 
			
		||||
      this.xmlhttp.open(req.method, req.url+p, true);
 | 
			
		||||
      this.xmlhttp.send();
 | 
			
		||||
    } else {
 | 
			
		||||
      this.xmlhttp.open(req.method, req.url, true);
 | 
			
		||||
      if(p instanceof String)
 | 
			
		||||
        this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
 | 
			
		||||
      this.xmlhttp.send(p);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  stop: function(){
 | 
			
		||||
    if(this.running) this.running = false;
 | 
			
		||||
    if(this.xmlhttp && this.xmlhttp.readyState < 4){
 | 
			
		||||
      this.xmlhttp.abort();
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  add: function(method, url, params, callback){
 | 
			
		||||
    this.queue.push({url:url,method:method,params:params,callback:callback});
 | 
			
		||||
    if(!this.running){
 | 
			
		||||
      this._request(this.queue.shift());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var requests = new QueuedRequester();
 | 
			
		||||
 | 
			
		||||
function createFileUploader(element, tree, editor){
 | 
			
		||||
  var xmlHttp;
 | 
			
		||||
  
 | 
			
		||||
  var refresh = ce("button");
 | 
			
		||||
  refresh.innerHTML = 'Refresh List';
 | 
			
		||||
  ge(element).appendChild(refresh);
 | 
			
		||||
 | 
			
		||||
  var input = ce("input");
 | 
			
		||||
  input.type = "file";
 | 
			
		||||
  input.multiple = false;
 | 
			
		||||
  input.name = "data";
 | 
			
		||||
  input.id="upload-select";
 | 
			
		||||
  ge(element).appendChild(input);
 | 
			
		||||
  
 | 
			
		||||
  var path = ce("input");
 | 
			
		||||
  path.id = "upload-path";
 | 
			
		||||
  path.type = "text";
 | 
			
		||||
  path.name = "path";
 | 
			
		||||
  path.defaultValue = "/";
 | 
			
		||||
  ge(element).appendChild(path);
 | 
			
		||||
  
 | 
			
		||||
  var button = ce("button");
 | 
			
		||||
  button.innerHTML = 'Upload';
 | 
			
		||||
  ge(element).appendChild(button);
 | 
			
		||||
  
 | 
			
		||||
  var mkfile = ce("button");
 | 
			
		||||
  mkfile.innerHTML = 'Create';
 | 
			
		||||
  ge(element).appendChild(mkfile);
 | 
			
		||||
 | 
			
		||||
  var filename     = ce("input");
 | 
			
		||||
  filename.id      = "editor-filename";
 | 
			
		||||
  filename.type    = "text";
 | 
			
		||||
  filename.disabled= true;
 | 
			
		||||
  filename.size    = 20;
 | 
			
		||||
  ge(element).appendChild(filename);
 | 
			
		||||
 | 
			
		||||
  var savefile = ce("button");
 | 
			
		||||
  savefile.innerHTML = ' Save ' ;
 | 
			
		||||
  ge(element).appendChild(savefile);
 | 
			
		||||
 | 
			
		||||
  function httpPostProcessRequest(status, responseText){
 | 
			
		||||
    if(status != 200)
 | 
			
		||||
      alert("ERROR["+status+"]: "+responseText);
 | 
			
		||||
    else
 | 
			
		||||
      tree.refreshPath(path.value);
 | 
			
		||||
  }
 | 
			
		||||
  function createPath(p){
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("path", p);
 | 
			
		||||
    requests.add("PUT", "/edit", formData, httpPostProcessRequest);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  mkfile.onclick = function(e){
 | 
			
		||||
    createPath(path.value);
 | 
			
		||||
    editor.loadUrl(path.value);
 | 
			
		||||
    path.value="/";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  savefile.onclick = function(e){
 | 
			
		||||
    editor.execCommand('saveCommand');
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  refresh.onclick = function(e){
 | 
			
		||||
    tree.refreshPath(path.value);
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  button.onclick = function(e){
 | 
			
		||||
    if(input.files.length === 0){
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("data", input.files[0], path.value);
 | 
			
		||||
    requests.add("POST", "/edit", formData, httpPostProcessRequest);
 | 
			
		||||
    var uploadPath= ge("upload-path");
 | 
			
		||||
    uploadPath.value="/";
 | 
			
		||||
    var uploadSelect= ge("upload-select");
 | 
			
		||||
    uploadSelect.value="";
 | 
			
		||||
  };
 | 
			
		||||
  input.onchange = function(e){
 | 
			
		||||
    if(input.files.length === 0) return;
 | 
			
		||||
    var filename = input.files[0].name;
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
 | 
			
		||||
    var name = /(.*)\.[^.]+$/.exec(filename)[1];
 | 
			
		||||
    if(typeof name !== undefined){
 | 
			
		||||
      filename = name;
 | 
			
		||||
    }
 | 
			
		||||
    path.value = "/"+filename+"."+ext;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createTree(element, editor){
 | 
			
		||||
  var preview = ge("preview");
 | 
			
		||||
  var treeRoot = ce("div");
 | 
			
		||||
  treeRoot.className = "tvu";
 | 
			
		||||
  ge(element).appendChild(treeRoot);
 | 
			
		||||
 | 
			
		||||
  function loadDownload(path){
 | 
			
		||||
    ge('download-frame').src = "/edit?download="+path;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function loadPreview(path){
 | 
			
		||||
    var edfname = ge("editor-filename");
 | 
			
		||||
    edfname.value=path;
 | 
			
		||||
    ge("editor").style.display = "none";
 | 
			
		||||
    preview.style.display = "block";
 | 
			
		||||
    preview.innerHTML = '<img src="/edit?edit='+path+'&_cb='+Date.now()+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function fillFileMenu(el, path){
 | 
			
		||||
    var list = ce("ul");
 | 
			
		||||
    el.appendChild(list);
 | 
			
		||||
    var action = ce("li");
 | 
			
		||||
    list.appendChild(action);
 | 
			
		||||
    if(isImageFile(path)){
 | 
			
		||||
      action.innerHTML = "<span>Preview</span>";
 | 
			
		||||
      action.onclick = function(e){
 | 
			
		||||
        loadPreview(path);
 | 
			
		||||
        if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
      };
 | 
			
		||||
    } else if(isTextFile(path)){
 | 
			
		||||
      action.innerHTML = "<span>Edit</span>";
 | 
			
		||||
      action.onclick = function(e){
 | 
			
		||||
        editor.loadUrl(path);
 | 
			
		||||
        if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    var download = ce("li");
 | 
			
		||||
    list.appendChild(download);
 | 
			
		||||
    download.innerHTML = "<span>Download</span>";
 | 
			
		||||
    download.onclick = function(e){
 | 
			
		||||
      loadDownload(path);
 | 
			
		||||
      if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
    };
 | 
			
		||||
    var delFile = ce("li");
 | 
			
		||||
    list.appendChild(delFile);
 | 
			
		||||
    delFile.innerHTML = "<span>Delete</span>";
 | 
			
		||||
    delFile.onclick = function(e){
 | 
			
		||||
      httpDelete(path);
 | 
			
		||||
      if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function showContextMenu(event, path, isfile){
 | 
			
		||||
    var divContext = ce("div");
 | 
			
		||||
    var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
 | 
			
		||||
    var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
 | 
			
		||||
    var left = event.clientX + scrollLeft;
 | 
			
		||||
    var top = event.clientY + scrollTop;
 | 
			
		||||
    divContext.className = 'cm';
 | 
			
		||||
    divContext.style.display = 'block';
 | 
			
		||||
    divContext.style.left = left + 'px';
 | 
			
		||||
    divContext.style.top = top + 'px';
 | 
			
		||||
    fillFileMenu(divContext, path);
 | 
			
		||||
    document.body.appendChild(divContext);
 | 
			
		||||
    var width = divContext.offsetWidth;
 | 
			
		||||
    var height = divContext.offsetHeight;
 | 
			
		||||
    divContext.onmouseout = function(e){
 | 
			
		||||
      if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){
 | 
			
		||||
        if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(divContext);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function createTreeLeaf(path, name, size){
 | 
			
		||||
    var leaf = ce("li");
 | 
			
		||||
    leaf.id = name;
 | 
			
		||||
    var label = ce("span");
 | 
			
		||||
    label.innerHTML = name;
 | 
			
		||||
    leaf.appendChild(label);
 | 
			
		||||
    leaf.onclick = function(e){
 | 
			
		||||
      if(isTextFile(leaf.id.toLowerCase())){
 | 
			
		||||
        editor.loadUrl(leaf.id);
 | 
			
		||||
      } else if(isImageFile(leaf.id.toLowerCase())){
 | 
			
		||||
        loadPreview(leaf.id);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    leaf.oncontextmenu = function(e){
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      showContextMenu(e, leaf.id, true);
 | 
			
		||||
    };
 | 
			
		||||
    return leaf;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function addList(parent, path, items){
 | 
			
		||||
    sortByKey(items, 'name');
 | 
			
		||||
    var list = ce("ul");
 | 
			
		||||
    parent.appendChild(list);
 | 
			
		||||
    var ll = items.length;
 | 
			
		||||
    for(var i = 0; i < ll; i++){
 | 
			
		||||
      if(items[i].type === "file")
 | 
			
		||||
        list.appendChild(createTreeLeaf(path, items[i].name, items[i].size));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function isTextFile(path){
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(path)[1];
 | 
			
		||||
    if(typeof ext !== undefined){
 | 
			
		||||
      switch(ext){
 | 
			
		||||
        case "txt":
 | 
			
		||||
        case "htm":
 | 
			
		||||
        case "html":
 | 
			
		||||
        case "js":
 | 
			
		||||
        case "css":
 | 
			
		||||
        case "xml":
 | 
			
		||||
        case "json":
 | 
			
		||||
        case "conf":
 | 
			
		||||
        case "ini":
 | 
			
		||||
        case "h":
 | 
			
		||||
        case "c":
 | 
			
		||||
        case "cpp":
 | 
			
		||||
        case "php":
 | 
			
		||||
        case "hex":
 | 
			
		||||
        case "ino":
 | 
			
		||||
        case "pde":
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function isImageFile(path){
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(path)[1];
 | 
			
		||||
    if(typeof ext !== undefined){
 | 
			
		||||
      switch(ext){
 | 
			
		||||
        case "png":
 | 
			
		||||
        case "jpg":
 | 
			
		||||
        case "gif":
 | 
			
		||||
        case "bmp":
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this.refreshPath = function(path){
 | 
			
		||||
    treeRoot.removeChild(treeRoot.childNodes[0]);
 | 
			
		||||
    httpGet(treeRoot, "/");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  function delCb(path){
 | 
			
		||||
    return function(status, responseText){
 | 
			
		||||
      if(status != 200){
 | 
			
		||||
        alert("ERROR["+status+"]: "+responseText);
 | 
			
		||||
      } else {
 | 
			
		||||
        treeRoot.removeChild(treeRoot.childNodes[0]);
 | 
			
		||||
        httpGet(treeRoot, "/");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function httpDelete(filename){
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("path", filename);
 | 
			
		||||
    requests.add("DELETE", "/edit", formData, delCb(filename));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function getCb(parent, path){
 | 
			
		||||
    return function(status, responseText){
 | 
			
		||||
      if(status == 200)
 | 
			
		||||
        addList(parent, path, JSON.parse(responseText));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function httpGet(parent, path){
 | 
			
		||||
    requests.add("GET", "/edit", { list: path }, getCb(parent, path));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  httpGet(treeRoot, "/");
 | 
			
		||||
  return this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createEditor(element, file, lang, theme, type){
 | 
			
		||||
  function getLangFromFilename(filename){
 | 
			
		||||
    var lang = "plain";
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
 | 
			
		||||
    if(typeof ext !== undefined){
 | 
			
		||||
      switch(ext){
 | 
			
		||||
        case "txt": lang = "plain"; break;
 | 
			
		||||
        case "hex": lang = "plain"; break;
 | 
			
		||||
        case "conf": lang = "plain"; break;
 | 
			
		||||
        case "htm": lang = "html"; break;
 | 
			
		||||
        case "js": lang = "javascript"; break;
 | 
			
		||||
        case "h": lang = "c_cpp"; break;
 | 
			
		||||
        case "c": lang = "c_cpp"; break;
 | 
			
		||||
        case "cpp": lang = "c_cpp"; break;
 | 
			
		||||
        case "css":
 | 
			
		||||
        case "scss":
 | 
			
		||||
        case "php":
 | 
			
		||||
        case "html":
 | 
			
		||||
        case "json":
 | 
			
		||||
        case "xml":
 | 
			
		||||
        case "ini":  lang = ext;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return lang;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(typeof file === "undefined") file = "/index.html";
 | 
			
		||||
 | 
			
		||||
  if(typeof lang === "undefined"){
 | 
			
		||||
    lang = getLangFromFilename(file);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(typeof theme === "undefined") theme = "textmate";
 | 
			
		||||
 | 
			
		||||
  if(typeof type === "undefined"){
 | 
			
		||||
    type = "text/"+lang;
 | 
			
		||||
    if(lang === "c_cpp") type = "text/plain";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var editor = ace.edit(element);
 | 
			
		||||
  function httpPostProcessRequest(status, responseText){
 | 
			
		||||
    if(status != 200) alert("ERROR["+status+"]: "+responseText);
 | 
			
		||||
  }
 | 
			
		||||
  function httpPost(filename, data, type){
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("data", new Blob([data], { type: type }), filename);
 | 
			
		||||
    requests.add("POST", "/edit", formData, httpPostProcessRequest);
 | 
			
		||||
  }
 | 
			
		||||
  function httpGetProcessRequest(status, responseText){
 | 
			
		||||
      ge("preview").style.display = "none";
 | 
			
		||||
      ge("editor").style.display = "block";
 | 
			
		||||
      if(status == 200)
 | 
			
		||||
        editor.setValue(responseText);
 | 
			
		||||
      else
 | 
			
		||||
        editor.setValue("");
 | 
			
		||||
      editor.clearSelection();
 | 
			
		||||
  }
 | 
			
		||||
  function httpGet(theUrl){
 | 
			
		||||
      requests.add("GET", "/edit", { edit: theUrl }, httpGetProcessRequest);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
 | 
			
		||||
  editor.setTheme("ace/theme/"+theme);
 | 
			
		||||
  editor.$blockScrolling = Infinity;
 | 
			
		||||
  editor.getSession().setUseSoftTabs(true);
 | 
			
		||||
  editor.getSession().setTabSize(2);
 | 
			
		||||
  editor.setHighlightActiveLine(true);
 | 
			
		||||
  editor.setShowPrintMargin(false);
 | 
			
		||||
  editor.commands.addCommand({
 | 
			
		||||
      name: 'saveCommand',
 | 
			
		||||
      bindKey: {win: 'Ctrl-S',  mac: 'Command-S'},
 | 
			
		||||
      exec: function(editor) {
 | 
			
		||||
        httpPost(file, editor.getValue()+"", type);
 | 
			
		||||
      },
 | 
			
		||||
      readOnly: false
 | 
			
		||||
  });
 | 
			
		||||
  editor.commands.addCommand({
 | 
			
		||||
      name: 'undoCommand',
 | 
			
		||||
      bindKey: {win: 'Ctrl-Z',  mac: 'Command-Z'},
 | 
			
		||||
      exec: function(editor) {
 | 
			
		||||
        editor.getSession().getUndoManager().undo(false);
 | 
			
		||||
      },
 | 
			
		||||
      readOnly: false
 | 
			
		||||
  });
 | 
			
		||||
  editor.commands.addCommand({
 | 
			
		||||
      name: 'redoCommand',
 | 
			
		||||
      bindKey: {win: 'Ctrl-Shift-Z',  mac: 'Command-Shift-Z'},
 | 
			
		||||
      exec: function(editor) {
 | 
			
		||||
        editor.getSession().getUndoManager().redo(false);
 | 
			
		||||
      },
 | 
			
		||||
      readOnly: false
 | 
			
		||||
  });
 | 
			
		||||
  editor.loadUrl = function(filename){
 | 
			
		||||
    var edfname = ge("editor-filename");
 | 
			
		||||
    edfname.value=filename;
 | 
			
		||||
    file = filename;
 | 
			
		||||
    lang = getLangFromFilename(file);
 | 
			
		||||
    type = "text/"+lang;
 | 
			
		||||
    if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
 | 
			
		||||
    httpGet(file);
 | 
			
		||||
  };
 | 
			
		||||
  return editor;
 | 
			
		||||
}
 | 
			
		||||
function onBodyLoad(){
 | 
			
		||||
  var vars = {};
 | 
			
		||||
  var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; });
 | 
			
		||||
  var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
 | 
			
		||||
  var tree = createTree("tree", editor);
 | 
			
		||||
  createFileUploader("uploader", tree, editor);
 | 
			
		||||
  if(typeof vars.file === "undefined") vars.file = "/index.htm";
 | 
			
		||||
  editor.loadUrl(vars.file);
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<script id='ace' src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js" type="text/javascript" charset="utf-8"></script>
 | 
			
		||||
<script>
 | 
			
		||||
  if  (typeof ace.edit == "undefined") {
 | 
			
		||||
    var script = document.createElement('script');
 | 
			
		||||
    script.src = "/ace.js";
 | 
			
		||||
    script.async = false;
 | 
			
		||||
    document.head.appendChild(script);
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
</head>
 | 
			
		||||
<body onload="onBodyLoad();">
 | 
			
		||||
  <div id="loader" class="loader"></div>
 | 
			
		||||
  <div id="uploader"></div>
 | 
			
		||||
  <div id="tree"></div>
 | 
			
		||||
  <div id="editor"></div>
 | 
			
		||||
  <div id="preview" style="display:none;"></div>
 | 
			
		||||
  <iframe id=download-frame style='display:none;'></iframe>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										61
									
								
								Software/platformio.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								Software/platformio.ini
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
; PlatformIO Project Configuration File
 | 
			
		||||
;
 | 
			
		||||
;   Build options: build flags, source filter
 | 
			
		||||
;   Upload options: custom upload port, speed and extra flags
 | 
			
		||||
;   Library options: dependencies, extra library storages
 | 
			
		||||
;   Advanced options: extra scripting
 | 
			
		||||
;
 | 
			
		||||
; Please visit documentation for the other options and examples
 | 
			
		||||
; https://docs.platformio.org/page/projectconf.html
 | 
			
		||||
 | 
			
		||||
[platformio]
 | 
			
		||||
extra_configs =
 | 
			
		||||
  wifi_credentials.ini
 | 
			
		||||
 | 
			
		||||
[env:de-timer]
 | 
			
		||||
platform = espressif32
 | 
			
		||||
board = ttgo-lora32-v1
 | 
			
		||||
board_build.filesystem = littlefs
 | 
			
		||||
;board_build.f_flash = 80000000L
 | 
			
		||||
;board_build.ldscript = eagle.flash.4m1m.ld
 | 
			
		||||
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
monitor_speed = 115200
 | 
			
		||||
 | 
			
		||||
upload_protocol = esptool
 | 
			
		||||
upload_speed = 921600
 | 
			
		||||
;upload_port = 10.0.1.48
 | 
			
		||||
;upload_protocol = espota
 | 
			
		||||
;upload_flags =  
 | 
			
		||||
;   --auth=${wifi_cred.admin_password}
 | 
			
		||||
 | 
			
		||||
build_type = debug
 | 
			
		||||
 | 
			
		||||
build_flags=
 | 
			
		||||
  !python git_rev_macro.py
 | 
			
		||||
  -DSERIAL_DEBUG
 | 
			
		||||
  ;-DWIFI_CLIENT
 | 
			
		||||
  ;-DCAPTIVE
 | 
			
		||||
  -DWIFI_AP_IP_GW=10,0,1,1
 | 
			
		||||
  -DADMIN_PASSWORD=${wifi_cred.admin_password}
 | 
			
		||||
  -DWIFI_SSID=${wifi_cred.wifi_ssid}
 | 
			
		||||
  -DWIFI_PASSWORD=${wifi_cred.wifi_password}
 | 
			
		||||
  -DWIFI_AP_PASSWORD=${wifi_cred.wifi_ap_password}
 | 
			
		||||
  -DDEVICE_NAME='"Dark Emergency Timer"'
 | 
			
		||||
  -DFACTION_1_NAME='"GOF"'
 | 
			
		||||
  -DFACTION_2_NAME='"MILIZ"'
 | 
			
		||||
  -DFACTION_3_NAME='"KGG"'
 | 
			
		||||
 | 
			
		||||
framework = arduino
 | 
			
		||||
lib_deps =
 | 
			
		||||
    smougenot/TM1637@0.0.0-alpha+sha.9486982048
 | 
			
		||||
    ;me-no-dev/ESP Async WebServer @ ^1.2.3  ; local version included due to bug
 | 
			
		||||
    me-no-dev/AsyncTCP @ ^1.1.1
 | 
			
		||||
    sstaub/Ticker @ ^4.2.0
 | 
			
		||||
    adafruit/Adafruit INA219 @ ^1.1.1
 | 
			
		||||
    robtillaart/I2C_EEPROM @ ^1.5.2
 | 
			
		||||
    sandeepmistry/LoRa @ ^0.8.0
 | 
			
		||||
    adafruit/Adafruit SSD1306 @ ^2.4.0
 | 
			
		||||
    adafruit/Adafruit GFX Library @ ^1.10.1
 | 
			
		||||
    adafruit/Adafruit BusIO @ ^1.5.0
 | 
			
		||||
    bblanchon/ArduinoJson @ ^6.19.4
 | 
			
		||||
							
								
								
									
										41
									
								
								Software/src/common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Software/src/common.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
#ifndef _COMMON_H_
 | 
			
		||||
#define _COMMON_H_
 | 
			
		||||
 | 
			
		||||
#define Q(x) #x
 | 
			
		||||
#define QUOTE(x) Q(x)
 | 
			
		||||
 | 
			
		||||
// Module connection pins (ESP GPIO-Nums)
 | 
			
		||||
#define CLK 12
 | 
			
		||||
#define DIO_FAC_1_7SEG 13
 | 
			
		||||
#define DIO_FAC_2_7SEG 17
 | 
			
		||||
#define DIO_FAC_3_7SEG 21
 | 
			
		||||
 | 
			
		||||
#define DIO_FAC_1_TRG 36
 | 
			
		||||
#define FAC_1_TRG_PRESSED LOW
 | 
			
		||||
#define DIO_FAC_2_TRG 37
 | 
			
		||||
#define FAC_2_TRG_PRESSED LOW
 | 
			
		||||
#define DIO_FAC_3_TRG 38
 | 
			
		||||
#define FAC_3_TRG_PRESSED LOW
 | 
			
		||||
 | 
			
		||||
#ifndef HOST_NAME
 | 
			
		||||
#define HOST_NAME "DE_Timer_%06X" // Use printf-Formatting - Chip-ID (uin32_t) will be added
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef OTA_DELAY
 | 
			
		||||
#define OTA_DELAY 50 // ticks -> 10ms / tick
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef ADMIN_PASSWORD
 | 
			
		||||
#error "You need to define ADMIN_PASSWORD for OTA-Update"
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef WIFI_PASSWORD
 | 
			
		||||
#error "You must define an WIFI_PASSWORD for OTA-Update"
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef WIFI_SSID
 | 
			
		||||
#error "You must define an WIFI_SSID for OTA-Update"
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef WIFI_AP_PASSWORD
 | 
			
		||||
#error "You must define an WIFI_AP_PASSWORD for Standalone AP-Mode"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										261
									
								
								Software/src/config.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								Software/src/config.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,261 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
I2C_eeprom ee(0x50, EEPROM_SIZE_BYTES);
 | 
			
		||||
 | 
			
		||||
configData_t ConfigData;
 | 
			
		||||
persistenceData_t PersistenceData;
 | 
			
		||||
const uint16_t eeVersion = 1; // inc
 | 
			
		||||
boolean eeAvailable = false;
 | 
			
		||||
 | 
			
		||||
const uint16_t persistencemarker_Adress = 0; // sizeof 4
 | 
			
		||||
const uint16_t startofConfig_Adress = 16;
 | 
			
		||||
const uint16_t startofPersistence_Adress = startofConfig_Adress + sizeof(ConfigData) + (sizeof(ConfigData) % 16);
 | 
			
		||||
 | 
			
		||||
boolean checkEEPROMavailable();
 | 
			
		||||
 | 
			
		||||
void InitEEPROM()
 | 
			
		||||
{
 | 
			
		||||
  ee.begin();
 | 
			
		||||
  if (!checkEEPROMavailable())
 | 
			
		||||
  {
 | 
			
		||||
    globals.systemStatus = sysStat_Error;
 | 
			
		||||
    MaintainDTC(DTC_NO_EEPROM_FOUND, true);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  GetConfig_EEPROM();
 | 
			
		||||
 | 
			
		||||
  if (ConfigData.EEPROM_Version != eeVersion)
 | 
			
		||||
  {
 | 
			
		||||
    FormatConfig_EEPROM();
 | 
			
		||||
    globals.systemStatus = sysStat_Error;
 | 
			
		||||
    MaintainDTC(DTC_EEPROM_VERSION_BAD, true);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (getPersistanceAddress() > ee.getDeviceSize())
 | 
			
		||||
  {
 | 
			
		||||
    FormatPersistence_EEPROM();
 | 
			
		||||
    globals.systemStatus = sysStat_Error;
 | 
			
		||||
    MaintainDTC(DTC_EEPROM_PDS_MARKER_INVALID, true);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  GetPersistence_EEPROM();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EEPROM_Process()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  switch (globals.requestEEAction)
 | 
			
		||||
  {
 | 
			
		||||
  case EE_CFG_SAVE:
 | 
			
		||||
    StoreConfig_EEPROM();
 | 
			
		||||
    globals.requestEEAction = EE_IDLE;
 | 
			
		||||
    break;
 | 
			
		||||
  case EE_CFG_LOAD:
 | 
			
		||||
    GetConfig_EEPROM();
 | 
			
		||||
    globals.requestEEAction = EE_IDLE;
 | 
			
		||||
    break;
 | 
			
		||||
  case EE_PDS_SAVE:
 | 
			
		||||
    StorePersistence_EEPROM();
 | 
			
		||||
    globals.requestEEAction = EE_IDLE;
 | 
			
		||||
    break;
 | 
			
		||||
  case EE_PDS_LOAD:
 | 
			
		||||
    GetPersistence_EEPROM();
 | 
			
		||||
    globals.requestEEAction = EE_IDLE;
 | 
			
		||||
    break;
 | 
			
		||||
  case EE_IDLE:
 | 
			
		||||
  default:
 | 
			
		||||
    globals.requestEEAction = EE_IDLE;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StoreConfig_EEPROM()
 | 
			
		||||
{
 | 
			
		||||
  ConfigData.checksum = 0;
 | 
			
		||||
  ConfigData.checksum = Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData));
 | 
			
		||||
  if (!checkEEPROMavailable())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  ee.updateBlock(startofConfig_Adress, (uint8_t *)&ConfigData, sizeof(ConfigData));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GetConfig_EEPROM()
 | 
			
		||||
{
 | 
			
		||||
  if (!checkEEPROMavailable())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  ee.readBlock(startofConfig_Adress, (uint8_t *)&ConfigData, sizeof(ConfigData));
 | 
			
		||||
 | 
			
		||||
  uint32_t checksum = ConfigData.checksum;
 | 
			
		||||
  ConfigData.checksum = 0;
 | 
			
		||||
 | 
			
		||||
  if (Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData)) != checksum)
 | 
			
		||||
  {
 | 
			
		||||
    MaintainDTC(DTC_EEPROM_CFG_BAD, true);
 | 
			
		||||
    FormatConfig_EEPROM();
 | 
			
		||||
  }
 | 
			
		||||
  ConfigData.checksum = checksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t getPersistanceAddress()
 | 
			
		||||
{
 | 
			
		||||
  uint32_t eePersistenceMarker;
 | 
			
		||||
  ee.readBlock(persistencemarker_Adress, (uint8_t *)&eePersistenceMarker, sizeof(eePersistenceMarker));
 | 
			
		||||
  return eePersistenceMarker;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void updatePersistanceAddress(uint32_t adress)
 | 
			
		||||
{
 | 
			
		||||
  ee.updateBlock(persistencemarker_Adress, (uint8_t *)&adress, sizeof(adress));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StorePersistence_EEPROM()
 | 
			
		||||
{
 | 
			
		||||
  if (PersistenceData.writeCycleCounter >= EEPROM_ENDURANCE)
 | 
			
		||||
    MovePersistencePage_EEPROM(false);
 | 
			
		||||
  else
 | 
			
		||||
    PersistenceData.writeCycleCounter++;
 | 
			
		||||
 | 
			
		||||
  PersistenceData.checksum = 0;
 | 
			
		||||
  PersistenceData.checksum = Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData));
 | 
			
		||||
 | 
			
		||||
  if (!checkEEPROMavailable())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  ee.updateBlock(getPersistanceAddress(), (uint8_t *)&PersistenceData, sizeof(PersistenceData));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GetPersistence_EEPROM()
 | 
			
		||||
{
 | 
			
		||||
  if (!checkEEPROMavailable())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  ee.readBlock(getPersistanceAddress(), (uint8_t *)&PersistenceData, sizeof(PersistenceData));
 | 
			
		||||
 | 
			
		||||
  uint32_t checksum = PersistenceData.checksum;
 | 
			
		||||
  PersistenceData.checksum = 0;
 | 
			
		||||
 | 
			
		||||
  if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) != checksum)
 | 
			
		||||
  {
 | 
			
		||||
    MaintainDTC(DTC_EEPROM_PDS_BAD, true);
 | 
			
		||||
    FormatPersistence_EEPROM();
 | 
			
		||||
  }
 | 
			
		||||
  PersistenceData.checksum = checksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FormatConfig_EEPROM()
 | 
			
		||||
{
 | 
			
		||||
  configData_t defaults;
 | 
			
		||||
  ConfigData = defaults;
 | 
			
		||||
  ConfigData.EEPROM_Version = eeVersion;
 | 
			
		||||
  StoreConfig_EEPROM();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FormatPersistence_EEPROM()
 | 
			
		||||
{
 | 
			
		||||
  persistenceData_t defaults;
 | 
			
		||||
  PersistenceData = defaults;
 | 
			
		||||
  updatePersistanceAddress(startofPersistence_Adress);
 | 
			
		||||
  StorePersistence_EEPROM();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MovePersistencePage_EEPROM(boolean reset)
 | 
			
		||||
{
 | 
			
		||||
  if (!checkEEPROMavailable())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (reset)
 | 
			
		||||
  {
 | 
			
		||||
    updatePersistanceAddress(startofPersistence_Adress);
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    uint32_t newPersistenceMarker = getPersistanceAddress() + sizeof(PersistenceData);
 | 
			
		||||
 | 
			
		||||
    // check if we reached the End of the EEPROM and Startover at the beginning
 | 
			
		||||
    if ((newPersistenceMarker + sizeof(PersistenceData)) > ee.getDeviceSize())
 | 
			
		||||
    {
 | 
			
		||||
      MaintainDTC(DTC_EEPROM_WORNOUT, true);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      updatePersistanceAddress(newPersistenceMarker);
 | 
			
		||||
      PersistenceData.writeCycleCounter = 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t GetEESize()
 | 
			
		||||
{
 | 
			
		||||
  return ee.getDeviceSize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t Checksum_EEPROM(uint8_t const *data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
  if (data == NULL)
 | 
			
		||||
    return 0;
 | 
			
		||||
  uint32_t crc, mask;
 | 
			
		||||
  crc = 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
  while (len--)
 | 
			
		||||
  {
 | 
			
		||||
    crc ^= *data++;
 | 
			
		||||
    for (uint8_t k = 0; k < 8; k++)
 | 
			
		||||
    {
 | 
			
		||||
      mask = -(crc & 1);
 | 
			
		||||
      crc = (crc >> 1) ^ (0xEDB88320 & mask);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return ~crc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dumpEEPROM(uint16_t memoryAddress, uint16_t length)
 | 
			
		||||
{
 | 
			
		||||
#define BLOCK_TO_LENGTH 16
 | 
			
		||||
 | 
			
		||||
  if (length > ee.getDeviceSize())
 | 
			
		||||
    length = ee.getDeviceSize();
 | 
			
		||||
 | 
			
		||||
  if (memoryAddress + length > ee.getDeviceSize())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (!checkEEPROMavailable())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  char ascii_buf[BLOCK_TO_LENGTH + 1];
 | 
			
		||||
  sprintf(ascii_buf, "%*s", BLOCK_TO_LENGTH, "ASCII");
 | 
			
		||||
  Serial.print(PSTR("\nAddress "));
 | 
			
		||||
  for (int x = 0; x < BLOCK_TO_LENGTH; x++)
 | 
			
		||||
    Serial.printf("%3d", x);
 | 
			
		||||
 | 
			
		||||
  memoryAddress = memoryAddress / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
 | 
			
		||||
  length = (length + BLOCK_TO_LENGTH - 1) / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
 | 
			
		||||
 | 
			
		||||
  for (unsigned int i = 0; i < length; i++)
 | 
			
		||||
  {
 | 
			
		||||
    int blockpoint = memoryAddress % BLOCK_TO_LENGTH;
 | 
			
		||||
    if (blockpoint == 0)
 | 
			
		||||
    {
 | 
			
		||||
      ascii_buf[BLOCK_TO_LENGTH] = 0;
 | 
			
		||||
      Serial.printf("  %s", ascii_buf);
 | 
			
		||||
      Serial.printf("\n0x%05X:", memoryAddress);
 | 
			
		||||
    }
 | 
			
		||||
    ascii_buf[blockpoint] = ee.readByte(memoryAddress);
 | 
			
		||||
    Serial.printf(" %02X", ascii_buf[blockpoint]);
 | 
			
		||||
    if (ascii_buf[blockpoint] < 0x20 || ascii_buf[blockpoint] > 0x7E)
 | 
			
		||||
      ascii_buf[blockpoint] = '.';
 | 
			
		||||
    memoryAddress++;
 | 
			
		||||
  }
 | 
			
		||||
  Serial.println();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boolean checkEEPROMavailable()
 | 
			
		||||
{
 | 
			
		||||
  if (!ee.isConnected())
 | 
			
		||||
  {
 | 
			
		||||
    MaintainDTC(DTC_NO_EEPROM_FOUND, true);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								Software/src/config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								Software/src/config.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
#ifndef _CONFIG_H_
 | 
			
		||||
#define _CONFIG_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <I2C_eeprom.h>
 | 
			
		||||
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
#include "dtc.h"
 | 
			
		||||
 | 
			
		||||
#define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC01
 | 
			
		||||
#define EEPROM_ENDURANCE 1000000
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
  NONE,
 | 
			
		||||
  FACTION_1,
 | 
			
		||||
  FACTION_2,
 | 
			
		||||
  FACTION_3
 | 
			
		||||
} factions_t;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  uint32_t writeCycleCounter = 0;
 | 
			
		||||
  uint32_t faction_1_timer = 0;
 | 
			
		||||
  uint32_t faction_2_timer = 0;
 | 
			
		||||
  uint32_t faction_3_timer = 0;
 | 
			
		||||
  factions_t activeFaction = NONE;
 | 
			
		||||
  uint32_t checksum = 0;
 | 
			
		||||
} persistenceData_t;
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
  BATTERY_UNDEFINED,
 | 
			
		||||
  BATTERY_LIPO_2S,
 | 
			
		||||
  BATTERY_LIPO_3S
 | 
			
		||||
} batteryType_t;
 | 
			
		||||
 | 
			
		||||
const char BatteryString[][10]{
 | 
			
		||||
  "Undefined",
 | 
			
		||||
  "LiPo 2S",
 | 
			
		||||
  "LiPo 3S"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  uint8_t EEPROM_Version = 1;
 | 
			
		||||
  batteryType_t batteryType = BATTERY_UNDEFINED;
 | 
			
		||||
  uint32_t checksum = 0;
 | 
			
		||||
} configData_t;
 | 
			
		||||
 | 
			
		||||
void InitEEPROM();
 | 
			
		||||
void EEPROM_Process();
 | 
			
		||||
void StoreConfig_EEPROM();
 | 
			
		||||
void GetConfig_EEPROM();
 | 
			
		||||
void StorePersistence_EEPROM();
 | 
			
		||||
void GetPersistence_EEPROM();
 | 
			
		||||
void FormatConfig_EEPROM();
 | 
			
		||||
void FormatPersistence_EEPROM();
 | 
			
		||||
uint32_t Checksum_EEPROM(uint8_t const *data, size_t len);
 | 
			
		||||
void dumpEEPROM(uint16_t memoryAddress, uint16_t length);
 | 
			
		||||
void MovePersistencePage_EEPROM(boolean reset);
 | 
			
		||||
uint32_t getPersistanceAddress();
 | 
			
		||||
void updatePersistanceAddress(uint32_t adress);
 | 
			
		||||
uint32_t GetEESize();
 | 
			
		||||
 | 
			
		||||
extern configData_t ConfigData;
 | 
			
		||||
extern persistenceData_t PersistenceData;
 | 
			
		||||
#endif // _CONFIG_H_
 | 
			
		||||
@@ -2,10 +2,10 @@
 | 
			
		||||
#define _DEFAULTS_H_
 | 
			
		||||
 | 
			
		||||
#ifndef WIFI_CLIENT
 | 
			
		||||
#define WIFI_AP
 | 
			
		||||
#define WIFI_ACCESSPOINT
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(WIFI_CLIENT) && defined(WIFI_AP)
 | 
			
		||||
#if defined(WIFI_CLIENT) && defined(WIFI_ACCESSPOINT)
 | 
			
		||||
#error "You can't define AP and CLIENT at the same Time!"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										85
									
								
								Software/src/dtc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								Software/src/dtc.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
#include "dtc.h"
 | 
			
		||||
 | 
			
		||||
DTCEntry_s DTCStorage[MAX_DTC_STORAGE];
 | 
			
		||||
 | 
			
		||||
void MaintainDTC(DTCNums_t DTC_no, boolean active)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
			
		||||
    {
 | 
			
		||||
        if (DTCStorage[i].Number == DTC_no)
 | 
			
		||||
        {
 | 
			
		||||
            if (active && DTCStorage[i].active != DTC_ACTIVE)
 | 
			
		||||
            {
 | 
			
		||||
                Serial.printf("DTC gone active: %d", DTC_no);
 | 
			
		||||
                DTCStorage[i].timestamp = millis();
 | 
			
		||||
                DTCStorage[i].active = DTC_ACTIVE;
 | 
			
		||||
            }
 | 
			
		||||
            if (!active && DTCStorage[i].active == DTC_ACTIVE)
 | 
			
		||||
            {
 | 
			
		||||
                Serial.printf("DTC gone previous: %d", DTC_no);
 | 
			
		||||
                DTCStorage[i].active = DTC_PREVIOUS;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // DTC was not found with upper iteration, but is active
 | 
			
		||||
    // so we need to look for free space to store DTC
 | 
			
		||||
    if (active == true)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (DTCStorage[i].Number == DTC_LAST_DTC)
 | 
			
		||||
            {
 | 
			
		||||
                Serial.printf("new DTC registered: %d", DTC_no);
 | 
			
		||||
                DTCStorage[i].Number = DTC_no;
 | 
			
		||||
                DTCStorage[i].timestamp = millis();
 | 
			
		||||
                DTCStorage[i].active = DTC_ACTIVE;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ClearDTC(DTCNums_t DTC_no)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
			
		||||
    {
 | 
			
		||||
        if (DTCStorage[i].Number == DTC_no)
 | 
			
		||||
        {
 | 
			
		||||
            DTCStorage[i].Number = DTC_LAST_DTC;
 | 
			
		||||
            DTCStorage[i].active = DTC_NONE;
 | 
			
		||||
            DTCStorage[i].timestamp = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ClearAllDTC()
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
			
		||||
    {
 | 
			
		||||
        DTCStorage[i].Number = DTC_LAST_DTC;
 | 
			
		||||
        DTCStorage[i].active = DTC_NONE;
 | 
			
		||||
        DTCStorage[i].timestamp = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DTCNums_t getlastDTC(boolean only_active)
 | 
			
		||||
{
 | 
			
		||||
    int8_t pointer = -1;
 | 
			
		||||
    uint32_t lasttimestamp = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
			
		||||
    {
 | 
			
		||||
        if (DTCStorage[i].Number > 0 && DTCStorage[i].timestamp > lasttimestamp)
 | 
			
		||||
        {
 | 
			
		||||
            if (only_active == false || DTCStorage[i].active == DTC_ACTIVE)
 | 
			
		||||
            {
 | 
			
		||||
                pointer = i;
 | 
			
		||||
                lasttimestamp = DTCStorage[i].timestamp;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return pointer >= 0 ? DTCStorage[pointer].Number : DTC_LAST_DTC;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								Software/src/dtc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Software/src/dtc.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
#ifndef _DTC_H_
 | 
			
		||||
#define _DTC_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_DTC_STORAGE 6
 | 
			
		||||
 | 
			
		||||
typedef enum DTCNums_e
 | 
			
		||||
{
 | 
			
		||||
  DTC_NO_EEPROM_FOUND,
 | 
			
		||||
  DTC_EEPROM_CFG_BAD,
 | 
			
		||||
  DTC_EEPROM_PDS_BAD,
 | 
			
		||||
  DTC_EEPROM_VERSION_BAD,
 | 
			
		||||
  DTC_EEPROM_WORNOUT,                 // this will happen if the EEPROM-cells are all overwritten 1 million times!
 | 
			
		||||
  DTC_EEPROM_PDS_MARKER_INVALID,      // This happens if the Marker of the PersistanceData was pointing to an EE-Adress bigger than the used EEPROM-IC
 | 
			
		||||
  DTC_LAST_DTC
 | 
			
		||||
} DTCNums_t;
 | 
			
		||||
 | 
			
		||||
typedef enum DTCActive_e
 | 
			
		||||
{
 | 
			
		||||
  DTC_ACTIVE,
 | 
			
		||||
  DTC_PREVIOUS,
 | 
			
		||||
  DTC_NONE
 | 
			
		||||
} DTCActive_t;
 | 
			
		||||
 | 
			
		||||
typedef struct DTCEntry_s
 | 
			
		||||
{
 | 
			
		||||
  DTCNums_t Number;
 | 
			
		||||
  uint32_t timestamp;
 | 
			
		||||
  DTCActive_t active;
 | 
			
		||||
} DTCEntry_t;
 | 
			
		||||
 | 
			
		||||
void MaintainDTC(DTCNums_t DTC_no, boolean active);
 | 
			
		||||
void ClearDTC(DTCNums_t DTC_no);
 | 
			
		||||
void ClearAllDTC();
 | 
			
		||||
DTCNums_t getlastDTC(boolean only_active);
 | 
			
		||||
 | 
			
		||||
extern DTCEntry_s DTCStorage[MAX_DTC_STORAGE];
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										36
									
								
								Software/src/globals.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								Software/src/globals.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
#ifndef _GLOBALS_H_
 | 
			
		||||
#define _GLOBALS_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
 | 
			
		||||
typedef enum eSystem_Status
 | 
			
		||||
{
 | 
			
		||||
  sysStat_Startup,
 | 
			
		||||
  sysStat_Normal,
 | 
			
		||||
  sysStat_Error,
 | 
			
		||||
  sysStat_Shutdown
 | 
			
		||||
} tSystem_Status;
 | 
			
		||||
 | 
			
		||||
typedef enum eEERequest
 | 
			
		||||
{
 | 
			
		||||
  EE_IDLE,
 | 
			
		||||
  EE_CFG_SAVE,
 | 
			
		||||
  EE_CFG_LOAD,
 | 
			
		||||
  EE_PDS_SAVE,
 | 
			
		||||
  EE_PDS_LOAD
 | 
			
		||||
} tEERequest;
 | 
			
		||||
 | 
			
		||||
typedef struct Globals_s
 | 
			
		||||
{
 | 
			
		||||
  char DeviceName[33];
 | 
			
		||||
  char DeviceName_ID[43];
 | 
			
		||||
  tSystem_Status systemStatus = sysStat_Startup;
 | 
			
		||||
  tSystem_Status resumeStatus = sysStat_Startup;
 | 
			
		||||
  eEERequest requestEEAction = EE_IDLE;
 | 
			
		||||
  float loadvoltage = 0;
 | 
			
		||||
  int battery_level = 0;
 | 
			
		||||
} Globals_t;
 | 
			
		||||
 | 
			
		||||
extern Globals_t globals;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										20
									
								
								Software/src/lora_net.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Software/src/lora_net.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#include "lora_net.h"
 | 
			
		||||
 | 
			
		||||
uint8_t LoRa_Init()
 | 
			
		||||
{
 | 
			
		||||
  uint8_t success = false;
 | 
			
		||||
  // SPI LoRa pins
 | 
			
		||||
  SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_SS);
 | 
			
		||||
  // setup LoRa transceiver module
 | 
			
		||||
  LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);
 | 
			
		||||
 | 
			
		||||
  if (!LoRa.begin(LORA_BAND))
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("Starting LoRa failed!");
 | 
			
		||||
    success = false;
 | 
			
		||||
  }
 | 
			
		||||
  Serial.println("LoRa Initializing OK!");
 | 
			
		||||
  success = true;
 | 
			
		||||
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								Software/src/lora_net.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Software/src/lora_net.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#ifndef _LORA_NET_H_
 | 
			
		||||
#define _LORA_NET_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <SPI.h>
 | 
			
		||||
#include <LoRa.h>
 | 
			
		||||
 | 
			
		||||
// define the pins used by the LoRa transceiver module
 | 
			
		||||
#define LORA_SCK 5
 | 
			
		||||
#define LORA_MISO 19
 | 
			
		||||
#define LORA_MOSI 27
 | 
			
		||||
#define LORA_SS 18
 | 
			
		||||
#define LORA_RST 14
 | 
			
		||||
#define LORA_DIO0 26
 | 
			
		||||
 | 
			
		||||
// 433E6 for Asia
 | 
			
		||||
// 866E6 for Europe
 | 
			
		||||
// 915E6 for North America
 | 
			
		||||
#define LORA_BAND 8681E5
 | 
			
		||||
 | 
			
		||||
uint8_t LoRa_Init();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										513
									
								
								Software/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										513
									
								
								Software/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,513 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <TM1637Display.h>
 | 
			
		||||
#include <Ticker.h>
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#include <ESPmDNS.h>
 | 
			
		||||
#include <ArduinoOTA.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include <LittleFS.h>
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <Adafruit_INA219.h>
 | 
			
		||||
#include <ArduinoJson.h>
 | 
			
		||||
 | 
			
		||||
// local includes
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
#include "webui.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
#include "dtc.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "lora_net.h"
 | 
			
		||||
#include "oled_display.h"
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
#include <WiFiMulti.h>
 | 
			
		||||
 | 
			
		||||
const char *ssid = QUOTE(WIFI_SSID);
 | 
			
		||||
const char *password = QUOTE(WIFI_PASSWORD);
 | 
			
		||||
const uint32_t connectTimeoutMs = 5000;
 | 
			
		||||
 | 
			
		||||
WiFiMulti wifiMulti;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void SevenSeg_Output();
 | 
			
		||||
void FactionTicker_callback();
 | 
			
		||||
void inputGetterTicker_callback();
 | 
			
		||||
void powerMonitorTicker_callback();
 | 
			
		||||
void EEPROMCyclicPDS_callback();
 | 
			
		||||
void toggleWiFiAP(boolean shutdown = false);
 | 
			
		||||
void SystemShutdown();
 | 
			
		||||
void SetBatteryType(batteryType_t type);
 | 
			
		||||
void ProcessKeyCombos();
 | 
			
		||||
void OverrideDisplay(const uint8_t *message, uint32_t time);
 | 
			
		||||
void LoRaPublish_callback();
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
void wifiMaintainConnectionTicker_callback();
 | 
			
		||||
Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
uint32_t getESPChipID();
 | 
			
		||||
 | 
			
		||||
TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG);
 | 
			
		||||
TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG);
 | 
			
		||||
TM1637Display disp_FAC_3(CLK, DIO_FAC_3_7SEG);
 | 
			
		||||
 | 
			
		||||
Adafruit_INA219 ina219;
 | 
			
		||||
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
DNSServer dnsServer;
 | 
			
		||||
#endif
 | 
			
		||||
AsyncWebServer server(80);
 | 
			
		||||
 | 
			
		||||
Ticker FactionTicker(FactionTicker_callback, 1000, 0, MILLIS);
 | 
			
		||||
Ticker InputGetterTicker(inputGetterTicker_callback, 250, 0, MILLIS);
 | 
			
		||||
Ticker PowerMonitorTicker(powerMonitorTicker_callback, 5000, 0, MILLIS);
 | 
			
		||||
Ticker EEPROMCyclicPDSTicker(EEPROMCyclicPDS_callback, 60000, 0, MILLIS);
 | 
			
		||||
Ticker LoRaPublishTicker(LoRaPublish_callback, 60000, 0, MILLIS);
 | 
			
		||||
 | 
			
		||||
uint8_t Faction_1_dot = 0;
 | 
			
		||||
uint8_t Faction_2_dot = 0;
 | 
			
		||||
uint8_t Faction_3_dot = 0;
 | 
			
		||||
 | 
			
		||||
uint32_t DisplayOverrideFlag = 0;
 | 
			
		||||
uint8_t *DisplayOverrideValue = 0;
 | 
			
		||||
 | 
			
		||||
Globals_t globals;
 | 
			
		||||
 | 
			
		||||
const uint8_t sevenSeg_bat[] = {0x00, 0b01111100, 0b01110111, 0b01111000};
 | 
			
		||||
const uint8_t sevenSeg_low[] = {0b00111000, 0b01011100, 0x00, 0x00};
 | 
			
		||||
const uint8_t sevenSeg_net[] = {0b01010100, 0b01111001, 0b01111000, 0x00};
 | 
			
		||||
const uint8_t sevenSeg_ota[] = {0x3F, 0x78, 0x77, 0x00};
 | 
			
		||||
const uint8_t sevenSeg_flsh[] = {0x71, 0x38, 0x6D, 0x76};
 | 
			
		||||
const uint8_t sevenSeg_file[] = {0x71, 0x30, 0x38, 0x79};
 | 
			
		||||
 | 
			
		||||
void setup()
 | 
			
		||||
{
 | 
			
		||||
  setCpuFrequencyMhz(80);
 | 
			
		||||
  WiFi.setAutoReconnect(false);
 | 
			
		||||
  WiFi.persistent(false);
 | 
			
		||||
  WiFi.disconnect();
 | 
			
		||||
 | 
			
		||||
  Serial.begin(115200);
 | 
			
		||||
  Serial.print("\n\n\n");
 | 
			
		||||
 | 
			
		||||
  strcpy(globals.DeviceName, DEVICE_NAME);
 | 
			
		||||
  snprintf(globals.DeviceName_ID, 42, "%s_%08X", globals.DeviceName, getESPChipID());
 | 
			
		||||
 | 
			
		||||
  if (LoRa_Init())
 | 
			
		||||
    LoRaPublishTicker.start();
 | 
			
		||||
  OLED_Init();
 | 
			
		||||
 | 
			
		||||
  pinMode(DIO_FAC_1_TRG, INPUT_PULLUP);
 | 
			
		||||
  pinMode(DIO_FAC_2_TRG, INPUT_PULLUP);
 | 
			
		||||
  pinMode(DIO_FAC_3_TRG, INPUT_PULLUP);
 | 
			
		||||
 | 
			
		||||
#ifdef SERIAL_DEBUG
 | 
			
		||||
  Serial.setDebugOutput(true);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if (ina219.begin())
 | 
			
		||||
    PowerMonitorTicker.start();
 | 
			
		||||
  else
 | 
			
		||||
    Serial.println("Failed to find INA219 chip");
 | 
			
		||||
 | 
			
		||||
  LittleFS.begin();
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
  WiFi.mode(WIFI_STA);
 | 
			
		||||
  WiFi.setHostname(globals.DeviceName_ID);
 | 
			
		||||
  wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD));
 | 
			
		||||
  WiFiMaintainConnectionTicker.start();
 | 
			
		||||
#else
 | 
			
		||||
  WiFi.mode(WIFI_AP);
 | 
			
		||||
  WiFi.begin(QUOTE(DEVICE_NAME), QUOTE(WIFI_AP_PASSWORD));
 | 
			
		||||
  WiFi.setSleep(true);
 | 
			
		||||
  WiFi.mode(WIFI_OFF);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  InitEEPROM();
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.setPort(8266);
 | 
			
		||||
  ArduinoOTA.setHostname(globals.DeviceName_ID);
 | 
			
		||||
  ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD));
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onStart([]()
 | 
			
		||||
                     {
 | 
			
		||||
                       disp_FAC_1.setBrightness(7);
 | 
			
		||||
                       disp_FAC_2.setBrightness(7);
 | 
			
		||||
                       disp_FAC_3.setBrightness(7);
 | 
			
		||||
                       disp_FAC_1.setSegments(sevenSeg_ota);
 | 
			
		||||
                       disp_FAC_3.clear();
 | 
			
		||||
                       
 | 
			
		||||
                       if (ArduinoOTA.getCommand() == U_FLASH)
 | 
			
		||||
                       {
 | 
			
		||||
                         disp_FAC_2.setSegments(sevenSeg_flsh);
 | 
			
		||||
                       }
 | 
			
		||||
                       else
 | 
			
		||||
                       {
 | 
			
		||||
                         disp_FAC_2.setSegments(sevenSeg_file);
 | 
			
		||||
                         LittleFS.end();
 | 
			
		||||
                       } });
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onEnd([]()
 | 
			
		||||
                   { 
 | 
			
		||||
                       const uint8_t seg_done[] = {0x5E, 0x3F, 0x54, 0x79};
 | 
			
		||||
                       disp_FAC_1.setSegments(seg_done);
 | 
			
		||||
                       disp_FAC_2.clear();
 | 
			
		||||
                       disp_FAC_3.clear(); });
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
 | 
			
		||||
                        { disp_FAC_3.showNumberDecEx((progress / (total / 100))); });
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onError([](ota_error_t error)
 | 
			
		||||
                     {
 | 
			
		||||
                       Serial.printf("Error[%u]: ", error);
 | 
			
		||||
                       if (error == OTA_AUTH_ERROR)
 | 
			
		||||
                         Serial.println("Auth Failed");
 | 
			
		||||
                       else if (error == OTA_BEGIN_ERROR)
 | 
			
		||||
                         Serial.println("Begin Failed");
 | 
			
		||||
                       else if (error == OTA_CONNECT_ERROR)
 | 
			
		||||
                         Serial.println("Connect Failed");
 | 
			
		||||
                       else if (error == OTA_RECEIVE_ERROR)
 | 
			
		||||
                         Serial.println("Receive Failed");
 | 
			
		||||
                       else if (error == OTA_END_ERROR)
 | 
			
		||||
                         Serial.println("End Failed"); });
 | 
			
		||||
  ArduinoOTA.begin();
 | 
			
		||||
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
  dnsServer.start(53, "*", WiFi.softAPIP());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  initWebUI();
 | 
			
		||||
 | 
			
		||||
  EEPROMCyclicPDSTicker.start();
 | 
			
		||||
  FactionTicker.start();
 | 
			
		||||
  InputGetterTicker.start();
 | 
			
		||||
  Serial.println("Setup Done");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void loop()
 | 
			
		||||
{
 | 
			
		||||
  EEPROMCyclicPDSTicker.update();
 | 
			
		||||
  FactionTicker.update();
 | 
			
		||||
  InputGetterTicker.update();
 | 
			
		||||
  PowerMonitorTicker.update();
 | 
			
		||||
  LoRaPublishTicker.update();
 | 
			
		||||
  ArduinoOTA.handle();
 | 
			
		||||
  SevenSeg_Output();
 | 
			
		||||
  EEPROM_Process();
 | 
			
		||||
  OLED_Process();
 | 
			
		||||
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
  dnsServer.processNextRequest();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
  WiFiMaintainConnectionTicker.update();
 | 
			
		||||
#endif
 | 
			
		||||
  if (globals.systemStatus == sysStat_Shutdown)
 | 
			
		||||
    SystemShutdown();
 | 
			
		||||
  yield();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String macToString(const unsigned char *mac)
 | 
			
		||||
{
 | 
			
		||||
  char buf[20];
 | 
			
		||||
  snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
 | 
			
		||||
           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 | 
			
		||||
  return String(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SevenSeg_Output()
 | 
			
		||||
{
 | 
			
		||||
  if (DisplayOverrideFlag > millis())
 | 
			
		||||
  {
 | 
			
		||||
    disp_FAC_1.setBrightness(7);
 | 
			
		||||
    disp_FAC_2.setBrightness(7);
 | 
			
		||||
    disp_FAC_3.setBrightness(7);
 | 
			
		||||
    disp_FAC_1.setSegments(DisplayOverrideValue);
 | 
			
		||||
    disp_FAC_2.clear();
 | 
			
		||||
    disp_FAC_3.clear();
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    if (globals.battery_level < BAT_LOW_PERCENT && millis() % 10000 > 7000)
 | 
			
		||||
    {
 | 
			
		||||
      disp_FAC_1.setBrightness(0);
 | 
			
		||||
      disp_FAC_2.setBrightness(0);
 | 
			
		||||
      disp_FAC_3.setBrightness(0);
 | 
			
		||||
 | 
			
		||||
      disp_FAC_1.setSegments(sevenSeg_bat);
 | 
			
		||||
      disp_FAC_2.setSegments(sevenSeg_low);
 | 
			
		||||
      if (millis() % 3000 < 1500)
 | 
			
		||||
        disp_FAC_3.showNumberDec(globals.battery_level);
 | 
			
		||||
      else
 | 
			
		||||
        disp_FAC_3.showNumberDecEx(globals.loadvoltage * 100, 0x40);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      disp_FAC_1.setBrightness(PersistenceData.activeFaction == FACTION_1 ? 7 : 0);
 | 
			
		||||
      disp_FAC_2.setBrightness(PersistenceData.activeFaction == FACTION_2 ? 7 : 0);
 | 
			
		||||
      disp_FAC_3.setBrightness(PersistenceData.activeFaction == FACTION_3 ? 7 : 0);
 | 
			
		||||
 | 
			
		||||
      disp_FAC_1.showNumberDecEx(PersistenceData.faction_1_timer / 60, Faction_1_dot, true, 4, 0);
 | 
			
		||||
      disp_FAC_2.showNumberDecEx(PersistenceData.faction_2_timer / 60, Faction_2_dot, true, 4, 0);
 | 
			
		||||
      disp_FAC_3.showNumberDecEx(PersistenceData.faction_3_timer / 60, Faction_3_dot, true, 4, 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FactionTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
  switch (PersistenceData.activeFaction)
 | 
			
		||||
  {
 | 
			
		||||
  case FACTION_1:
 | 
			
		||||
    PersistenceData.faction_1_timer++;
 | 
			
		||||
    Faction_1_dot = Faction_1_dot == 0x80 || Faction_1_dot == 0x00 ? 0x10 : Faction_1_dot << 1;
 | 
			
		||||
    Faction_2_dot = 0;
 | 
			
		||||
    Faction_3_dot = 0;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FACTION_2:
 | 
			
		||||
    PersistenceData.faction_2_timer++;
 | 
			
		||||
    Faction_2_dot = Faction_2_dot == 0x80 || Faction_2_dot == 0x00 ? 0x10 : Faction_2_dot << 1;
 | 
			
		||||
    Faction_1_dot = 0;
 | 
			
		||||
    Faction_3_dot = 0;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FACTION_3:
 | 
			
		||||
    PersistenceData.faction_3_timer++;
 | 
			
		||||
    Faction_3_dot = Faction_3_dot == 0x80 || Faction_3_dot == 0x00 ? 0x10 : Faction_3_dot << 1;
 | 
			
		||||
    Faction_1_dot = 0;
 | 
			
		||||
    Faction_2_dot = 0;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void inputGetterTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
  ProcessKeyCombos();
 | 
			
		||||
 | 
			
		||||
  uint8_t keysPressed = 0;
 | 
			
		||||
 | 
			
		||||
  keysPressed = +digitalRead(DIO_FAC_1_TRG) == FAC_1_TRG_PRESSED ? 1 : 0;
 | 
			
		||||
  keysPressed = +digitalRead(DIO_FAC_2_TRG) == FAC_2_TRG_PRESSED ? 1 : 0;
 | 
			
		||||
  keysPressed = +digitalRead(DIO_FAC_3_TRG) == FAC_3_TRG_PRESSED ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
  if (keysPressed > 1)
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("ERROR: More than one Flag active - setting no Faction active");
 | 
			
		||||
    PersistenceData.activeFaction = NONE;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_1_TRG) == FAC_1_TRG_PRESSED)
 | 
			
		||||
    PersistenceData.activeFaction = FACTION_1;
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_2_TRG) == FAC_2_TRG_PRESSED)
 | 
			
		||||
    PersistenceData.activeFaction = FACTION_2;
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_3_TRG) == FAC_3_TRG_PRESSED)
 | 
			
		||||
    PersistenceData.activeFaction = FACTION_3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void powerMonitorTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
  // loadvoltage and percentage is global, because of battery Monitoring
 | 
			
		||||
 | 
			
		||||
  const int bat_min_2s = 680;
 | 
			
		||||
  const int bat_max_2s = 840;
 | 
			
		||||
  const int bat_min_3s = 1020;
 | 
			
		||||
  const int bat_max_3s = 1260;
 | 
			
		||||
 | 
			
		||||
  float shuntvoltage = 0;
 | 
			
		||||
  float current_mA = 0;
 | 
			
		||||
  float busvoltage = 0;
 | 
			
		||||
  float power_mW = 0;
 | 
			
		||||
  int battery_level = 0;
 | 
			
		||||
 | 
			
		||||
  shuntvoltage = ina219.getShuntVoltage_mV();
 | 
			
		||||
  busvoltage = ina219.getBusVoltage_V();
 | 
			
		||||
  current_mA = ina219.getCurrent_mA();
 | 
			
		||||
  power_mW = ina219.getPower_mW();
 | 
			
		||||
  globals.loadvoltage = busvoltage + (shuntvoltage / 1000);
 | 
			
		||||
  switch (ConfigData.batteryType)
 | 
			
		||||
  {
 | 
			
		||||
  case BATTERY_LIPO_2S:
 | 
			
		||||
    battery_level = map(globals.loadvoltage * 100, bat_min_2s, bat_max_2s, 0, 100);
 | 
			
		||||
    globals.battery_level = battery_level < 0 ? 0 : battery_level;
 | 
			
		||||
    break;
 | 
			
		||||
  case BATTERY_LIPO_3S:
 | 
			
		||||
    battery_level = map(globals.loadvoltage * 100, bat_min_3s, bat_max_3s, 0, 100);
 | 
			
		||||
    globals.battery_level = battery_level < 0 ? 0 : battery_level;
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    globals.battery_level = -1;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Serial.printf("Battery Level: %d %%\n", globals.battery_level);
 | 
			
		||||
  // Serial.printf("Bus Voltage: %f V\n", busvoltage);
 | 
			
		||||
  // Serial.printf("Shunt Voltage: %f mV\n", shuntvoltage);
 | 
			
		||||
  // Serial.printf("Load Voltage: %f V\n", globals.loadvoltage);
 | 
			
		||||
  // Serial.printf("Current: %f mA\n", current_mA);
 | 
			
		||||
  // Serial.printf("Power: %f mW\n", power_mW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EEPROMCyclicPDS_callback()
 | 
			
		||||
{
 | 
			
		||||
  StorePersistence_EEPROM();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
void wifiMaintainConnectionTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
  static uint32_t WiFiFailCount = 0;
 | 
			
		||||
  const uint32_t WiFiFailMax = 20;
 | 
			
		||||
 | 
			
		||||
  if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED)
 | 
			
		||||
  {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    if (WiFiFailCount < WiFiFailMax)
 | 
			
		||||
      WiFiFailCount++;
 | 
			
		||||
    else
 | 
			
		||||
      toggleWiFiAP(false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void toggleWiFiAP(boolean shutdown)
 | 
			
		||||
{
 | 
			
		||||
  if (WiFi.getMode() != WIFI_OFF && shutdown == true)
 | 
			
		||||
  {
 | 
			
		||||
    WiFi.mode(WIFI_OFF);
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
    WiFiMaintainConnectionTicker.stop();
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  else if (shutdown == false)
 | 
			
		||||
  {
 | 
			
		||||
    WiFi.mode(WIFI_AP);
 | 
			
		||||
    WiFi.softAPConfig(IPAddress(WIFI_AP_IP_GW), IPAddress(WIFI_AP_IP_GW), IPAddress(255, 255, 255, 0));
 | 
			
		||||
    WiFi.softAP(globals.DeviceName_ID, QUOTE(WIFI_AP_PASSWORD));
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
    WiFiMaintainConnectionTicker.stop();
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SystemShutdown()
 | 
			
		||||
{
 | 
			
		||||
  StoreConfig_EEPROM();
 | 
			
		||||
  ESP.restart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetBatteryType(batteryType_t type)
 | 
			
		||||
{
 | 
			
		||||
  if (ConfigData.batteryType != type)
 | 
			
		||||
  {
 | 
			
		||||
    ConfigData.batteryType = type;
 | 
			
		||||
    globals.requestEEAction = EE_CFG_SAVE;
 | 
			
		||||
    Serial.printf("Set Batterytype to %s\n", type == BATTERY_LIPO_2S ? "2s Lipo" : "3s LiPo");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProcessKeyCombos()
 | 
			
		||||
{
 | 
			
		||||
  typedef enum
 | 
			
		||||
  {
 | 
			
		||||
    KEY_PRESSED,
 | 
			
		||||
    KEY_RELEASED
 | 
			
		||||
  } keyStatus_t;
 | 
			
		||||
 | 
			
		||||
  static keyStatus_t keyStatus_Fac1 = KEY_RELEASED;
 | 
			
		||||
  static uint8_t keyCount_Fac1 = 0;
 | 
			
		||||
  static keyStatus_t keyStatus_Fac2 = KEY_RELEASED;
 | 
			
		||||
  static uint8_t keyCount_Fac2 = 0;
 | 
			
		||||
  static keyStatus_t keyStatus_Fac3 = KEY_RELEASED;
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_3_TRG) == FAC_3_TRG_PRESSED)
 | 
			
		||||
  {
 | 
			
		||||
    keyStatus_Fac3 = KEY_PRESSED;
 | 
			
		||||
 | 
			
		||||
    // Process FactionKey 1 ComboCounter
 | 
			
		||||
    if (digitalRead(DIO_FAC_1_TRG) == FAC_1_TRG_PRESSED && keyStatus_Fac1 == KEY_RELEASED)
 | 
			
		||||
    {
 | 
			
		||||
      keyStatus_Fac1 = KEY_PRESSED;
 | 
			
		||||
      keyCount_Fac1++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (digitalRead(DIO_FAC_1_TRG) != FAC_1_TRG_PRESSED)
 | 
			
		||||
      keyStatus_Fac1 = KEY_RELEASED;
 | 
			
		||||
 | 
			
		||||
    // Process FactionKey 2 ComboCounter
 | 
			
		||||
    if (digitalRead(DIO_FAC_2_TRG) == FAC_2_TRG_PRESSED && keyStatus_Fac2 == KEY_RELEASED)
 | 
			
		||||
    {
 | 
			
		||||
      keyStatus_Fac2 = KEY_PRESSED;
 | 
			
		||||
      keyCount_Fac2++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (digitalRead(DIO_FAC_2_TRG) != FAC_2_TRG_PRESSED)
 | 
			
		||||
      keyStatus_Fac2 = KEY_RELEASED;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_3_TRG) != FAC_3_TRG_PRESSED && keyStatus_Fac3 == KEY_PRESSED)
 | 
			
		||||
  {
 | 
			
		||||
    Serial.printf("KeyCombo 1: %d | 2: %d\n", keyCount_Fac1, keyCount_Fac2);
 | 
			
		||||
 | 
			
		||||
    if (keyCount_Fac1 == 2 && keyCount_Fac2 == 2)
 | 
			
		||||
    {
 | 
			
		||||
      Serial.println("KeyCombo: WiFi AP ON");
 | 
			
		||||
      OverrideDisplay(sevenSeg_net, 5000);
 | 
			
		||||
      toggleWiFiAP(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    keyCount_Fac1 = 0;
 | 
			
		||||
    keyCount_Fac2 = 0;
 | 
			
		||||
    keyStatus_Fac1 = KEY_RELEASED;
 | 
			
		||||
    keyStatus_Fac2 = KEY_RELEASED;
 | 
			
		||||
    keyStatus_Fac3 = KEY_RELEASED;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OverrideDisplay(const uint8_t *message, uint32_t time)
 | 
			
		||||
{
 | 
			
		||||
  DisplayOverrideFlag = millis() + time;
 | 
			
		||||
  DisplayOverrideValue = (uint8_t *)message;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t getESPChipID()
 | 
			
		||||
{
 | 
			
		||||
  uint32_t chipId;
 | 
			
		||||
  for (int i = 0; i < 17; i = i + 8)
 | 
			
		||||
  {
 | 
			
		||||
    chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
 | 
			
		||||
  }
 | 
			
		||||
  return chipId;
 | 
			
		||||
}
 | 
			
		||||
#
 | 
			
		||||
void LoRaPublish_callback()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  StaticJsonDocument<200> doc;
 | 
			
		||||
 | 
			
		||||
  doc["lipo"] = globals.battery_level;
 | 
			
		||||
  doc["afact"] = PersistenceData.activeFaction;
 | 
			
		||||
  doc["fac1"] = PersistenceData.faction_1_timer;
 | 
			
		||||
  doc["fac2"] = PersistenceData.faction_2_timer;
 | 
			
		||||
  doc["fac3"] = PersistenceData.faction_3_timer;
 | 
			
		||||
 | 
			
		||||
  LoRa.beginPacket();
 | 
			
		||||
  serializeJson(doc, LoRa);
 | 
			
		||||
  LoRa.endPacket();
 | 
			
		||||
 | 
			
		||||
  Serial.printf("Sendet LoRa-Status Package\n");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								Software/src/oled_display.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								Software/src/oled_display.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
#include "oled_display.h"
 | 
			
		||||
 | 
			
		||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST);
 | 
			
		||||
 | 
			
		||||
void OLED_Init()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    // reset OLED display via software
 | 
			
		||||
    pinMode(OLED_RST, OUTPUT);
 | 
			
		||||
    digitalWrite(OLED_RST, LOW);
 | 
			
		||||
    delay(20);
 | 
			
		||||
    digitalWrite(OLED_RST, HIGH);
 | 
			
		||||
 | 
			
		||||
    // initialize OLED
 | 
			
		||||
    Wire.begin(OLED_SDA, OLED_SCL);
 | 
			
		||||
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false))
 | 
			
		||||
    { // Address 0x3C for 128x32
 | 
			
		||||
        Serial.println(F("SSD1306 allocation failed"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    display.clearDisplay();
 | 
			
		||||
    display.setTextColor(WHITE);
 | 
			
		||||
    display.setTextSize(1);
 | 
			
		||||
    display.setCursor(0, 0);
 | 
			
		||||
    display.print("DISPLAY INIT");
 | 
			
		||||
    display.display();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OLED_Process()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    display.clearDisplay();
 | 
			
		||||
    display.setCursor(0, 0);
 | 
			
		||||
    display.printf("LiPo: %d%%\n", globals.battery_level);
 | 
			
		||||
    display.print(PersistenceData.activeFaction == FACTION_1 ? "> " : "  ");
 | 
			
		||||
    display.printf("%-5s: %02d:%02d:%02d\n", FACTION_1_NAME, PersistenceData.faction_1_timer / 3600, (PersistenceData.faction_1_timer / 60) % 60, PersistenceData.faction_1_timer % 60);
 | 
			
		||||
    display.print(PersistenceData.activeFaction == FACTION_2 ? "> " : "  ");
 | 
			
		||||
    display.printf("%-5s: %02d:%02d:%02d\n", FACTION_2_NAME, PersistenceData.faction_2_timer / 3600, (PersistenceData.faction_2_timer / 60) % 60, PersistenceData.faction_2_timer % 60);
 | 
			
		||||
    display.print(PersistenceData.activeFaction == FACTION_3 ? "> " : "  ");
 | 
			
		||||
    display.printf("%-5s: %02d:%02d:%02d\n", FACTION_3_NAME, PersistenceData.faction_3_timer / 3600, (PersistenceData.faction_3_timer / 60) % 60, PersistenceData.faction_3_timer % 60);
 | 
			
		||||
    display.display();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								Software/src/oled_display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Software/src/oled_display.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#ifndef _OLED_DISPLAY_H_
 | 
			
		||||
#define _OLED_DISPLAY_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <Adafruit_GFX.h>
 | 
			
		||||
#include <Adafruit_SSD1306.h>
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#define OLED_SDA 4
 | 
			
		||||
#define OLED_SCL 15
 | 
			
		||||
#define OLED_RST 16
 | 
			
		||||
#define SCREEN_WIDTH 128 // OLED display width, in pixels
 | 
			
		||||
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
 | 
			
		||||
 | 
			
		||||
void OLED_Init();
 | 
			
		||||
void OLED_Process();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										209
									
								
								Software/src/webui.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								Software/src/webui.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,209 @@
 | 
			
		||||
#include "webui.h"
 | 
			
		||||
 | 
			
		||||
AsyncWebServer webServer(80);
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
  RESPMSG_HIDE,
 | 
			
		||||
  RESPMSG_SUCCESS,
 | 
			
		||||
  RESPMSG_INFO,
 | 
			
		||||
  RESPMSG_WARNING,
 | 
			
		||||
  RESPMSG_DANGER
 | 
			
		||||
} statusResponseMessage_Type_t;
 | 
			
		||||
char StatusResponseMessage[64];
 | 
			
		||||
statusResponseMessage_Type_t StatusResponseMessage_Type = RESPMSG_INFO;
 | 
			
		||||
 | 
			
		||||
String processor(const String &var);
 | 
			
		||||
void WebserverPOST_Callback(AsyncWebServerRequest *request);
 | 
			
		||||
void WebserverNotFound_Callback(AsyncWebServerRequest *request);
 | 
			
		||||
void Webserver_Callback(AsyncWebServerRequest *request);
 | 
			
		||||
void WebserverCommands_Callback(String input);
 | 
			
		||||
 | 
			
		||||
void initWebUI()
 | 
			
		||||
{
 | 
			
		||||
  if (!LittleFS.begin())
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("An Error has occurred while mounting LittleFS");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  webServer.serveStatic("/static/", LittleFS, "/static/").setCacheControl("max-age=360000");
 | 
			
		||||
  webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
 | 
			
		||||
               { request->redirect("/index.htm"); });
 | 
			
		||||
  webServer.onNotFound(WebserverNotFound_Callback);
 | 
			
		||||
  webServer.on("/index.htm", HTTP_GET, Webserver_Callback);
 | 
			
		||||
  webServer.on("/index.htm", HTTP_POST, WebserverPOST_Callback);
 | 
			
		||||
 | 
			
		||||
  webServer.begin();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String processor(const String &var)
 | 
			
		||||
{
 | 
			
		||||
  if (var == "SHOW_DTC_TABLE")
 | 
			
		||||
    return globals.systemStatus == sysStat_Error ? "" : "hidden";
 | 
			
		||||
 | 
			
		||||
  if (var == "SHOW_RESP_MESSAGE")
 | 
			
		||||
    return StatusResponseMessage_Type != RESPMSG_HIDE ? "" : "hidden";
 | 
			
		||||
 | 
			
		||||
  if (var == "RESP_MESSAGE_TYPE")
 | 
			
		||||
  {
 | 
			
		||||
    switch (StatusResponseMessage_Type)
 | 
			
		||||
    {
 | 
			
		||||
    case RESPMSG_SUCCESS:
 | 
			
		||||
      return "success";
 | 
			
		||||
    case RESPMSG_INFO:
 | 
			
		||||
      return "info";
 | 
			
		||||
    case RESPMSG_WARNING:
 | 
			
		||||
      return "warning";
 | 
			
		||||
    case RESPMSG_DANGER:
 | 
			
		||||
      return "danger";
 | 
			
		||||
    default:
 | 
			
		||||
      return "info";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (var == "RESP_MESSAGE")
 | 
			
		||||
    return String(StatusResponseMessage);
 | 
			
		||||
 | 
			
		||||
  if (var == "BAT_REMAIN_CAPACITY")
 | 
			
		||||
    return String(globals.battery_level);
 | 
			
		||||
 | 
			
		||||
  if (var == "DEVICE_NAME")
 | 
			
		||||
    return String(globals.DeviceName);
 | 
			
		||||
 | 
			
		||||
  if (var == "BAT_VOLTAGE")
 | 
			
		||||
    return String(globals.loadvoltage);
 | 
			
		||||
 | 
			
		||||
  if (var == "DTC_TABLE")
 | 
			
		||||
  {
 | 
			
		||||
    String temp;
 | 
			
		||||
    char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
 | 
			
		||||
    {
 | 
			
		||||
      if (DTCStorage[i].Number > 0)
 | 
			
		||||
      {
 | 
			
		||||
        sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d",
 | 
			
		||||
                DTCStorage[i].timestamp / 86400000,    // Days
 | 
			
		||||
                DTCStorage[i].timestamp / 360000 % 24, // Hours
 | 
			
		||||
                DTCStorage[i].timestamp / 60000 % 60,  // Minutes
 | 
			
		||||
                DTCStorage[i].timestamp / 1000 % 60,   // Seconds
 | 
			
		||||
                DTCStorage[i].timestamp % 1000);       // milliseconds
 | 
			
		||||
 | 
			
		||||
        temp = "<tr><td>" + String(buff_timestamp);
 | 
			
		||||
        temp = temp + "</td><td>" + String(DTCStorage[i].Number) + "</td><td>";
 | 
			
		||||
 | 
			
		||||
        if (DTCStorage[i].active == DTC_ACTIVE)
 | 
			
		||||
          temp = temp + "active";
 | 
			
		||||
        else if (DTCStorage[i].active == DTC_PREVIOUS)
 | 
			
		||||
          temp = temp + "previous";
 | 
			
		||||
        else
 | 
			
		||||
          temp = temp + "none";
 | 
			
		||||
 | 
			
		||||
        temp = temp + "</td></tr>";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return temp;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (var == "PLACEHOLDER")
 | 
			
		||||
    return "placeholder";
 | 
			
		||||
 | 
			
		||||
  if (var == "POINTS_FAC_1")
 | 
			
		||||
  {
 | 
			
		||||
    char buff[12];
 | 
			
		||||
    snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_1_timer / 3600, (PersistenceData.faction_1_timer / 60) % 60, PersistenceData.faction_1_timer % 60);
 | 
			
		||||
    return String(buff);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (var == "POINTS_FAC_2")
 | 
			
		||||
  {
 | 
			
		||||
    char buff[12];
 | 
			
		||||
    snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_2_timer / 3600, (PersistenceData.faction_2_timer / 60) % 60, PersistenceData.faction_2_timer % 60);
 | 
			
		||||
    return String(buff);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (var == "POINTS_FAC_3")
 | 
			
		||||
  {
 | 
			
		||||
    char buff[12];
 | 
			
		||||
    snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_3_timer / 3600, (PersistenceData.faction_3_timer / 60) % 60, PersistenceData.faction_3_timer % 60);
 | 
			
		||||
    return String(buff);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (var == "STATUS_FAC_1")
 | 
			
		||||
    return PersistenceData.activeFaction == FACTION_1 ? "ACTIVE" : "INACTIVE";
 | 
			
		||||
 | 
			
		||||
  if (var == "STATUS_FAC_2")
 | 
			
		||||
    return PersistenceData.activeFaction == FACTION_2 ? "ACTIVE" : "INACTIVE";
 | 
			
		||||
 | 
			
		||||
  if (var == "STATUS_FAC_3")
 | 
			
		||||
    return PersistenceData.activeFaction == FACTION_3 ? "ACTIVE" : "INACTIVE";
 | 
			
		||||
 | 
			
		||||
  if (var == "NAME_FAC_1")
 | 
			
		||||
    return FACTION_1_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "NAME_FAC_2")
 | 
			
		||||
    return FACTION_2_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "NAME_FAC_3")
 | 
			
		||||
    return FACTION_3_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "TITLE")
 | 
			
		||||
    return DEVICE_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "BATTERY_LEVEL")
 | 
			
		||||
  {
 | 
			
		||||
    return String(globals.battery_level);
 | 
			
		||||
  }
 | 
			
		||||
  if (var == "BATTERY_TYPE")
 | 
			
		||||
  {
 | 
			
		||||
    return String(BatteryString[ConfigData.batteryType]);
 | 
			
		||||
  }
 | 
			
		||||
  if (var == "BATTERY_VOLTAGE")
 | 
			
		||||
  {
 | 
			
		||||
    return String(globals.loadvoltage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return String();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Webserver_Callback(AsyncWebServerRequest *request)
 | 
			
		||||
{
 | 
			
		||||
  request->send(LittleFS, "/index.htm", "text/html", false, processor);
 | 
			
		||||
  StatusResponseMessage_Type = RESPMSG_HIDE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WebserverPOST_Callback(AsyncWebServerRequest *request)
 | 
			
		||||
{
 | 
			
		||||
  int paramsNr = request->params();
 | 
			
		||||
  for (int i = 0; i < paramsNr; i++)
 | 
			
		||||
  {
 | 
			
		||||
    AsyncWebParameter *p = request->getParam(i);
 | 
			
		||||
    if (p->name() == "commandInput")
 | 
			
		||||
      WebserverCommands_Callback(p->value());
 | 
			
		||||
  }
 | 
			
		||||
  request->send(LittleFS, "/index.htm", "text/html", false, processor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WebserverNotFound_Callback(AsyncWebServerRequest *request)
 | 
			
		||||
{
 | 
			
		||||
  request->send(404, "text/html", "Not found");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WebserverCommands_Callback(String input)
 | 
			
		||||
{
 | 
			
		||||
  String command = input.substring(0, input.indexOf(' '));
 | 
			
		||||
  command.toUpperCase();
 | 
			
		||||
  StatusResponseMessage_Type = RESPMSG_HIDE;
 | 
			
		||||
 | 
			
		||||
  if (command == "RESET")
 | 
			
		||||
  {
 | 
			
		||||
    strcpy(StatusResponseMessage, "Counter Reset done");
 | 
			
		||||
    StatusResponseMessage_Type = RESPMSG_SUCCESS;
 | 
			
		||||
 | 
			
		||||
    PersistenceData.faction_1_timer = 0;
 | 
			
		||||
    PersistenceData.faction_2_timer = 0;
 | 
			
		||||
    PersistenceData.faction_3_timer = 0;
 | 
			
		||||
    PersistenceData.activeFaction = NONE;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								Software/src/webui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Software/src/webui.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#ifndef _WEBUI_H_
 | 
			
		||||
#define _WEBUI_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <FS.h>
 | 
			
		||||
#include <LittleFS.h>
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
#include "dtc.h"
 | 
			
		||||
 | 
			
		||||
void initWebUI();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										5
									
								
								Software/wifi_credentials.example.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Software/wifi_credentials.example.ini
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
[wifi_cred]
 | 
			
		||||
wifi_ap_password = wifiappass
 | 
			
		||||
wifi_ssid = wifi-ssid
 | 
			
		||||
wifi_password = wifi-pass
 | 
			
		||||
admin_password = ota-password
 | 
			
		||||
@@ -1,151 +0,0 @@
 | 
			
		||||
html {
 | 
			
		||||
  font-family: sans-serif;
 | 
			
		||||
  line-height: 1.15;
 | 
			
		||||
  -ms-text-size-adjust: 100%;
 | 
			
		||||
  -webkit-text-size-adjust: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.container-fluid {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  margin-left: auto;
 | 
			
		||||
  margin-right: auto;
 | 
			
		||||
  padding-right: 15px;
 | 
			
		||||
  padding-left: 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.row {
 | 
			
		||||
  display: -webkit-box;
 | 
			
		||||
  display: -webkit-flex;
 | 
			
		||||
  display: -ms-flexbox;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  -webkit-flex-wrap: wrap;
 | 
			
		||||
  -ms-flex-wrap: wrap;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  margin-right: -15px;
 | 
			
		||||
  margin-left: -15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.col {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  min-height: 1px;
 | 
			
		||||
  padding-right: 15px;
 | 
			
		||||
  padding-left: 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table-fac-1,
 | 
			
		||||
.table-fac-1 > td {
 | 
			
		||||
  background-color: #ffeeba;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table-fac-2,
 | 
			
		||||
.table-fac-2 > td {
 | 
			
		||||
  background-color: #bac9ff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table-fac-3,
 | 
			
		||||
.table-fac-3 > td {
 | 
			
		||||
  background-color: #f5c6cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table {
 | 
			
		||||
  border-collapse: collapse !important;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  max-width: 100%;
 | 
			
		||||
  margin-bottom: 1rem;
 | 
			
		||||
}
 | 
			
		||||
.table td,
 | 
			
		||||
.table th {
 | 
			
		||||
  padding: 0.75rem;
 | 
			
		||||
  vertical-align: top;
 | 
			
		||||
  border-top: 1px solid #eceeef;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
}
 | 
			
		||||
.table thead th {
 | 
			
		||||
  vertical-align: bottom;
 | 
			
		||||
  border-bottom: 2px solid #eceeef;
 | 
			
		||||
}
 | 
			
		||||
.table tbody + tbody {
 | 
			
		||||
  border-top: 2px solid #eceeef;
 | 
			
		||||
}
 | 
			
		||||
.table .table {
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
label {
 | 
			
		||||
  -ms-touch-action: manipulation;
 | 
			
		||||
  touch-action: manipulation;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  margin-bottom: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-group {
 | 
			
		||||
	margin-bottom: 1rem;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
.form-control {
 | 
			
		||||
  display: block;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  padding: 0.5rem 0.75rem;
 | 
			
		||||
  font-size: 1rem;
 | 
			
		||||
  line-height: 1.25;
 | 
			
		||||
  color: #464a4c;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  background-image: none;
 | 
			
		||||
  -webkit-background-clip: padding-box;
 | 
			
		||||
  background-clip: padding-box;
 | 
			
		||||
  border: 1px solid rgba(0, 0, 0, 0.15);
 | 
			
		||||
  border-radius: 0.25rem;
 | 
			
		||||
  -webkit-transition: border-color ease-in-out 0.15s,
 | 
			
		||||
    -webkit-box-shadow ease-in-out 0.15s;
 | 
			
		||||
  transition: border-color ease-in-out 0.15s,
 | 
			
		||||
    -webkit-box-shadow ease-in-out 0.15s;
 | 
			
		||||
  -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
 | 
			
		||||
  transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
 | 
			
		||||
  transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s,
 | 
			
		||||
    -webkit-box-shadow ease-in-out 0.15s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-control:focus {
 | 
			
		||||
  color: #464a4c;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border-color: #5cb3fd;
 | 
			
		||||
  outline: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	font-weight: 400;
 | 
			
		||||
	line-height: 1.25;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	white-space: nowrap;
 | 
			
		||||
	vertical-align: middle;
 | 
			
		||||
	-webkit-user-select: none;
 | 
			
		||||
	-moz-user-select: none;
 | 
			
		||||
	-ms-user-select: none;
 | 
			
		||||
	user-select: none;
 | 
			
		||||
	border: 1px solid transparent;
 | 
			
		||||
	padding: 0.5rem 1rem;
 | 
			
		||||
	font-size: 1rem;
 | 
			
		||||
	border-radius: 0.25rem;
 | 
			
		||||
	-webkit-transition: all 0.2s ease-in-out;
 | 
			
		||||
	-o-transition: all 0.2s ease-in-out;
 | 
			
		||||
	transition: all 0.2s ease-in-out;
 | 
			
		||||
  }
 | 
			
		||||
  .btn:focus,
 | 
			
		||||
  .btn:hover {
 | 
			
		||||
	text-decoration: none;
 | 
			
		||||
  -webkit-box-shadow: 0 0 0 4px rgba(216, 63, 2, 0.25);
 | 
			
		||||
  box-shadow: 0 0 0 4px rgba(216, 63, 2, 0.25);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .btn:focus {
 | 
			
		||||
	outline: 0;
 | 
			
		||||
	-webkit-box-shadow: 0 0 0 2px rgba(2, 117, 216, 0.25);
 | 
			
		||||
	box-shadow: 0 0 0 2px rgba(2, 117, 216, 0.25);
 | 
			
		||||
  }
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								data/favicon.ico
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/favicon.ico
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										100
									
								
								data/index.html
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								data/index.html
									
									
									
									
									
								
							@@ -1,100 +0,0 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
 | 
			
		||||
    <title>%TITLE% - by hiabuto.de</title>
 | 
			
		||||
 | 
			
		||||
    <meta name="description" content="A Project, designed by hiabuto.de">
 | 
			
		||||
    <meta name="author" content="Marcel Peterkau">
 | 
			
		||||
 | 
			
		||||
    <link href="css/style.css" rel="stylesheet">
 | 
			
		||||
	<!--<link href="css/bootstrap.min.css" rel="stylesheet">-->
 | 
			
		||||
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
 | 
			
		||||
    <div class="container-fluid">
 | 
			
		||||
	<div class="row">
 | 
			
		||||
		<div class="col">
 | 
			
		||||
			<h3>
 | 
			
		||||
				%TITLE%
 | 
			
		||||
			</h3>
 | 
			
		||||
			<p>Battery Voltage: %BATTERY_VOLTAGE% V</p>
 | 
			
		||||
			<p>Battery Level: %BATTERY_LEVEL% %</p>
 | 
			
		||||
			<table class="table">
 | 
			
		||||
				<thead>
 | 
			
		||||
					<tr>
 | 
			
		||||
						<th>
 | 
			
		||||
							#
 | 
			
		||||
						</th>
 | 
			
		||||
						<th>
 | 
			
		||||
							Team
 | 
			
		||||
						</th>
 | 
			
		||||
						<th>
 | 
			
		||||
							Points
 | 
			
		||||
						</th>
 | 
			
		||||
						<th>
 | 
			
		||||
							Status
 | 
			
		||||
						</th>
 | 
			
		||||
					</tr>
 | 
			
		||||
				</thead>
 | 
			
		||||
				<tbody>
 | 
			
		||||
					<tr class="table-fac-1">
 | 
			
		||||
						<td>
 | 
			
		||||
							1
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%NAME_FAC_1%
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%POINTS_FAC_1%
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%STATUS_FAC_1%
 | 
			
		||||
						</td>
 | 
			
		||||
					</tr>
 | 
			
		||||
					<tr class="table-fac-2">
 | 
			
		||||
						<td>
 | 
			
		||||
							2
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%NAME_FAC_2%
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%POINTS_FAC_2%
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%STATUS_FAC_2%
 | 
			
		||||
						</td>
 | 
			
		||||
					</tr>
 | 
			
		||||
					<tr class="table-fac-3">
 | 
			
		||||
						<td>
 | 
			
		||||
							3
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%NAME_FAC_3%
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%POINTS_FAC_3%
 | 
			
		||||
						</td>
 | 
			
		||||
						<td>
 | 
			
		||||
							%STATUS_FAC_3%
 | 
			
		||||
						</td>
 | 
			
		||||
					</tr>
 | 
			
		||||
				</tbody>
 | 
			
		||||
			</table> 
 | 
			
		||||
			<form  action="index.html" method="get" role="form">
 | 
			
		||||
				<div class="form-group">		 
 | 
			
		||||
					<label for="commandInput">Command</label>
 | 
			
		||||
					<input type="text" class="form-control" id="commandInput" name="command" />
 | 
			
		||||
				</div>
 | 
			
		||||
				<button type="submit" class="btn">Submit</button>
 | 
			
		||||
			</form>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
; PlatformIO Project Configuration File
 | 
			
		||||
;
 | 
			
		||||
;   Build options: build flags, source filter
 | 
			
		||||
;   Upload options: custom upload port, speed and extra flags
 | 
			
		||||
;   Library options: dependencies, extra library storages
 | 
			
		||||
;   Advanced options: extra scripting
 | 
			
		||||
;
 | 
			
		||||
; Please visit documentation for the other options and examples
 | 
			
		||||
; https://docs.platformio.org/page/projectconf.html
 | 
			
		||||
 | 
			
		||||
[env:d1_mini]
 | 
			
		||||
platform = espressif8266
 | 
			
		||||
board = d1_mini
 | 
			
		||||
board_build.filesystem = littlefs
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.ldscript = eagle.flash.4m1m.ld
 | 
			
		||||
 | 
			
		||||
upload_protocol = espota
 | 
			
		||||
upload_port = ESP_OTA.local
 | 
			
		||||
upload_flags=
 | 
			
		||||
    --auth=UploadTheFlag
 | 
			
		||||
 | 
			
		||||
build_flags=
 | 
			
		||||
    -D SERIAL_DEBUG
 | 
			
		||||
    -D DEVICE_NAME='"DE CTF Timer Prototype 1"'
 | 
			
		||||
    -D WIFI_CLIENT
 | 
			
		||||
    -D WIFI_SSID='"BND_Scanner_#42"'
 | 
			
		||||
    -D WIFI_PASS='"5xMYkerbLMdrsSdF3hpy5DM9"'
 | 
			
		||||
    ;-D WIFI_AP
 | 
			
		||||
    ;-D WIFI_SSID='"Dark Emergency CTF Timer"'
 | 
			
		||||
    ;-D WIFI_PASS='"CaptureTheFlag"'
 | 
			
		||||
    -D FACTION_1_NAME='"GOF"'
 | 
			
		||||
    -D FACTION_2_NAME='"MILIZ"'
 | 
			
		||||
    -D FACTION_3_NAME='"KGG"'
 | 
			
		||||
 | 
			
		||||
framework = arduino
 | 
			
		||||
lib_deps =
 | 
			
		||||
    smougenot/TM1637@0.0.0-alpha+sha.9486982048
 | 
			
		||||
    me-no-dev/ESP Async WebServer @ ^1.2.3
 | 
			
		||||
    sstaub/Ticker @ ^4.2.0
 | 
			
		||||
    adafruit/Adafruit INA219 @ ^1.1.1
 | 
			
		||||
							
								
								
									
										495
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										495
									
								
								src/main.cpp
									
									
									
									
									
								
							@@ -1,495 +0,0 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <TM1637Display.h>
 | 
			
		||||
#include <Ticker.h>
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#include <WiFiMulti.h>
 | 
			
		||||
#elif defined(ESP8266)
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#include <ESP8266WiFiMulti.h>
 | 
			
		||||
#include <ESP8266mDNS.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <ArduinoOTA.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include <LittleFS.h>
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <Adafruit_INA219.h>
 | 
			
		||||
// local includes
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
 | 
			
		||||
// Module connection pins (ESP GPIO-Nums)
 | 
			
		||||
#define CLK 16
 | 
			
		||||
#define DIO_FAC_1_7SEG 14
 | 
			
		||||
#define DIO_FAC_2_7SEG 12
 | 
			
		||||
#define DIO_FAC_3_7SEG 13
 | 
			
		||||
 | 
			
		||||
#define DIO_FAC_1_TRG 0
 | 
			
		||||
#define DIO_FAC_2_TRG 2
 | 
			
		||||
#define DIO_FAC_3_TRG 15
 | 
			
		||||
 | 
			
		||||
enum Factions
 | 
			
		||||
{
 | 
			
		||||
  NONE,
 | 
			
		||||
  FACTION_1,
 | 
			
		||||
  FACTION_2,
 | 
			
		||||
  FACTION_3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void SevenSeg_Output();
 | 
			
		||||
void FactionTicker_callback();
 | 
			
		||||
void serialOutTicker_callback();
 | 
			
		||||
void inputGetterTicker_callback();
 | 
			
		||||
void powerMonitorTicker_callback();
 | 
			
		||||
 | 
			
		||||
TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG);
 | 
			
		||||
TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG);
 | 
			
		||||
TM1637Display disp_FAC_3(CLK, DIO_FAC_3_7SEG);
 | 
			
		||||
 | 
			
		||||
Adafruit_INA219 ina219;
 | 
			
		||||
 | 
			
		||||
WiFiEventHandler stationConnectedHandler;
 | 
			
		||||
WiFiEventHandler stationDisconnectedHandler;
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
ESP8266WiFiMulti wifiMulti;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
DNSServer dnsServer;
 | 
			
		||||
#endif
 | 
			
		||||
AsyncWebServer server(80);
 | 
			
		||||
 | 
			
		||||
Ticker FactionTicker(FactionTicker_callback, 1000, 0, MILLIS);
 | 
			
		||||
Ticker SerialOutputTicker(serialOutTicker_callback, 5000, 0, MILLIS);
 | 
			
		||||
Ticker InputGetterTicker(inputGetterTicker_callback, 250, 0, MILLIS);
 | 
			
		||||
Ticker PowerMonitorTicker(powerMonitorTicker_callback, 5000, 0, MILLIS);
 | 
			
		||||
 | 
			
		||||
Factions activeFaction = NONE;
 | 
			
		||||
 | 
			
		||||
uint32_t Count_Faction_1 = 0;
 | 
			
		||||
uint32_t Count_Faction_2 = 0;
 | 
			
		||||
uint32_t Count_Faction_3 = 0;
 | 
			
		||||
uint8_t Faction_1_dot = 0;
 | 
			
		||||
uint8_t Faction_2_dot = 0;
 | 
			
		||||
uint8_t Faction_3_dot = 0;
 | 
			
		||||
 | 
			
		||||
float loadvoltage = 0;
 | 
			
		||||
int battery_level = 0;
 | 
			
		||||
 | 
			
		||||
const uint8_t sevenSeg_bat[] = {0x00, 0b01111100, 0b01110111, 0b01111000};
 | 
			
		||||
const uint8_t sevenSeg_low[] = {0b00111000, 0b01011100, 0x00, 0x00};
 | 
			
		||||
 | 
			
		||||
String processor(const String &var)
 | 
			
		||||
{
 | 
			
		||||
  char buffer[16] = {0};
 | 
			
		||||
 | 
			
		||||
  if (var == "POINTS_FAC_1")
 | 
			
		||||
    itoa(Count_Faction_1, buffer, 10);
 | 
			
		||||
 | 
			
		||||
  if (var == "POINTS_FAC_2")
 | 
			
		||||
    itoa(Count_Faction_2, buffer, 10);
 | 
			
		||||
 | 
			
		||||
  if (var == "POINTS_FAC_3")
 | 
			
		||||
    itoa(Count_Faction_3, buffer, 10);
 | 
			
		||||
 | 
			
		||||
  if (var == "STATUS_FAC_1")
 | 
			
		||||
    return activeFaction == FACTION_1 ? "ACTIVE" : "INACTIVE";
 | 
			
		||||
 | 
			
		||||
  if (var == "STATUS_FAC_2")
 | 
			
		||||
    return activeFaction == FACTION_2 ? "ACTIVE" : "INACTIVE";
 | 
			
		||||
 | 
			
		||||
  if (var == "STATUS_FAC_3")
 | 
			
		||||
    return activeFaction == FACTION_3 ? "ACTIVE" : "INACTIVE";
 | 
			
		||||
 | 
			
		||||
  if (var == "NAME_FAC_1")
 | 
			
		||||
    return FACTION_1_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "NAME_FAC_2")
 | 
			
		||||
    return FACTION_2_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "NAME_FAC_3")
 | 
			
		||||
    return FACTION_3_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "TITLE")
 | 
			
		||||
    return DEVICE_NAME;
 | 
			
		||||
 | 
			
		||||
  if (var == "BATTERY_LEVEL")
 | 
			
		||||
  {
 | 
			
		||||
    sprintf(buffer, "%d", battery_level);
 | 
			
		||||
    return String(buffer);
 | 
			
		||||
  }
 | 
			
		||||
  if (var == "BATTERY_VOLTAGE")
 | 
			
		||||
  {
 | 
			
		||||
    sprintf(buffer, "%f", loadvoltage);
 | 
			
		||||
    return String(buffer);
 | 
			
		||||
  }
 | 
			
		||||
  return String(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String macToString(const unsigned char *mac)
 | 
			
		||||
{
 | 
			
		||||
  char buf[20];
 | 
			
		||||
  snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
 | 
			
		||||
           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 | 
			
		||||
  return String(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void onStationConnected(const WiFiEventSoftAPModeStationConnected &evt)
 | 
			
		||||
{
 | 
			
		||||
  Serial.print("Station connected: ");
 | 
			
		||||
  Serial.println(macToString(evt.mac));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void onStationDisconnected(const WiFiEventSoftAPModeStationDisconnected &evt)
 | 
			
		||||
{
 | 
			
		||||
  Serial.print("Station disconnected: ");
 | 
			
		||||
  Serial.println(macToString(evt.mac));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String getValue(String data, char separator, int index)
 | 
			
		||||
{
 | 
			
		||||
  int found = 0;
 | 
			
		||||
  int strIndex[] = {0, -1};
 | 
			
		||||
  int maxIndex = data.length() - 1;
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i <= maxIndex && found <= index; i++)
 | 
			
		||||
  {
 | 
			
		||||
    if (data.charAt(i) == separator || i == maxIndex)
 | 
			
		||||
    {
 | 
			
		||||
      found++;
 | 
			
		||||
      strIndex[0] = strIndex[1] + 1;
 | 
			
		||||
      strIndex[1] = (i == maxIndex) ? i + 1 : i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CaptiveRequestHandler : public AsyncWebHandler
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  CaptiveRequestHandler() {}
 | 
			
		||||
  virtual ~CaptiveRequestHandler() {}
 | 
			
		||||
 | 
			
		||||
  bool canHandle(AsyncWebServerRequest *request)
 | 
			
		||||
  {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void handleRequest(AsyncWebServerRequest *request)
 | 
			
		||||
  {
 | 
			
		||||
 | 
			
		||||
    Serial.print("Requested URL: ");
 | 
			
		||||
    Serial.println(request->url());
 | 
			
		||||
 | 
			
		||||
    //List all parameters
 | 
			
		||||
    int params = request->params();
 | 
			
		||||
    for (int i = 0; i < params; i++)
 | 
			
		||||
    {
 | 
			
		||||
      AsyncWebParameter *p = request->getParam(i);
 | 
			
		||||
      if (p->isFile())
 | 
			
		||||
      { //p->isPost() is also true
 | 
			
		||||
        Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
 | 
			
		||||
      }
 | 
			
		||||
      else if (p->isPost())
 | 
			
		||||
      {
 | 
			
		||||
        Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (LittleFS.exists(request->url()) == false)
 | 
			
		||||
    {
 | 
			
		||||
      request->send(404, "text/plain", "Not found");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (request->url() == "/favicon.ico")
 | 
			
		||||
    {
 | 
			
		||||
      request->send(LittleFS, request->url(), "image/x-icon");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (request->url() == "/" || request->url() == "/index.html")
 | 
			
		||||
    {
 | 
			
		||||
      request->send(LittleFS, "/index.html", String(), false, processor);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (getValue(request->url(), '/', 1) == "css")
 | 
			
		||||
    {
 | 
			
		||||
      request->send(LittleFS, request->url(), "text/css");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void SevenSeg_Output()
 | 
			
		||||
{
 | 
			
		||||
  if (battery_level < BAT_LOW_PERCENT)
 | 
			
		||||
  {
 | 
			
		||||
    disp_FAC_1.setBrightness(0);
 | 
			
		||||
    disp_FAC_2.setBrightness(0);
 | 
			
		||||
    disp_FAC_3.setBrightness(0);
 | 
			
		||||
 | 
			
		||||
    disp_FAC_3.setSegments(sevenSeg_bat);
 | 
			
		||||
    disp_FAC_2.setSegments(sevenSeg_low);
 | 
			
		||||
    if (millis() % 5000 > 2500)
 | 
			
		||||
      disp_FAC_1.showNumberDec(battery_level);
 | 
			
		||||
    else
 | 
			
		||||
      disp_FAC_1.showNumberDecEx(loadvoltage * 100, 0x40);
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    disp_FAC_1.setBrightness(activeFaction == FACTION_1 ? 7 : 0);
 | 
			
		||||
    disp_FAC_2.setBrightness(activeFaction == FACTION_2 ? 7 : 0);
 | 
			
		||||
    disp_FAC_3.setBrightness(activeFaction == FACTION_3 ? 7 : 0);
 | 
			
		||||
 | 
			
		||||
    disp_FAC_1.showNumberDecEx(Count_Faction_1 / 60, Faction_1_dot, true, 4, 0);
 | 
			
		||||
    disp_FAC_2.showNumberDecEx(Count_Faction_2 / 60, Faction_2_dot, true, 4, 0);
 | 
			
		||||
    disp_FAC_3.showNumberDecEx(Count_Faction_3 / 60, Faction_3_dot, true, 4, 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FactionTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
  switch (activeFaction)
 | 
			
		||||
  {
 | 
			
		||||
  case FACTION_1:
 | 
			
		||||
    Count_Faction_1++;
 | 
			
		||||
    Faction_1_dot = Faction_1_dot == 0x80 || Faction_1_dot == 0x00 ? 0x10 : Faction_1_dot << 1;
 | 
			
		||||
    Faction_2_dot = 0;
 | 
			
		||||
    Faction_3_dot = 0;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FACTION_2:
 | 
			
		||||
    Count_Faction_2++;
 | 
			
		||||
    Faction_2_dot = Faction_2_dot == 0x80 || Faction_2_dot == 0x00 ? 0x10 : Faction_2_dot << 1;
 | 
			
		||||
    Faction_1_dot = 0;
 | 
			
		||||
    Faction_3_dot = 0;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FACTION_3:
 | 
			
		||||
    Count_Faction_3++;
 | 
			
		||||
    Faction_3_dot = Faction_3_dot == 0x80 || Faction_3_dot == 0x00 ? 0x10 : Faction_3_dot << 1;
 | 
			
		||||
    Faction_1_dot = 0;
 | 
			
		||||
    Faction_2_dot = 0;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void serialOutTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  static uint32_t SerialPrintCount = 0;
 | 
			
		||||
 | 
			
		||||
  if (SerialPrintCount % 10 == 0)
 | 
			
		||||
  {
 | 
			
		||||
    Serial.printf("| %8s | %8s | %8s |\n", FACTION_1_NAME, FACTION_2_NAME, FACTION_3_NAME);
 | 
			
		||||
  }
 | 
			
		||||
  Serial.printf("  %8d   %8d   %8d\n", Count_Faction_1, Count_Faction_2, Count_Faction_3);
 | 
			
		||||
  SerialPrintCount++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void inputGetterTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
  activeFaction = NONE;
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_1_TRG) + digitalRead(DIO_FAC_2_TRG) + !digitalRead(DIO_FAC_3_TRG) < 2)
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("ERROR: More than one Flag active");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_1_TRG) == LOW)
 | 
			
		||||
    activeFaction = FACTION_1;
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_2_TRG) == LOW)
 | 
			
		||||
    activeFaction = FACTION_2;
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_3_TRG) == HIGH)
 | 
			
		||||
    activeFaction = FACTION_3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void powerMonitorTicker_callback()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  // loadvoltage and percentage is global, because of battery Monitoring
 | 
			
		||||
 | 
			
		||||
  float shuntvoltage = 0;
 | 
			
		||||
  float current_mA = 0;
 | 
			
		||||
  float busvoltage = 0;
 | 
			
		||||
  float power_mW = 0;
 | 
			
		||||
 | 
			
		||||
  shuntvoltage = ina219.getShuntVoltage_mV();
 | 
			
		||||
  busvoltage = ina219.getBusVoltage_V();
 | 
			
		||||
  current_mA = ina219.getCurrent_mA();
 | 
			
		||||
  power_mW = ina219.getPower_mW();
 | 
			
		||||
  loadvoltage = busvoltage + (shuntvoltage / 1000);
 | 
			
		||||
  battery_level = map(loadvoltage * 100, 655, 840, 0, 100);
 | 
			
		||||
 | 
			
		||||
  Serial.printf("Battery Level: %d %%\n", battery_level);
 | 
			
		||||
  Serial.printf("Bus Voltage: %f V\n", busvoltage);
 | 
			
		||||
  Serial.printf("Shunt Voltage: %f mV\n", shuntvoltage);
 | 
			
		||||
  Serial.printf("Load Voltage: %f V\n", loadvoltage);
 | 
			
		||||
  Serial.printf("Current: %f mA\n", current_mA);
 | 
			
		||||
  Serial.printf("Power: %f mW\n", power_mW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void setup()
 | 
			
		||||
{
 | 
			
		||||
  pinMode(DIO_FAC_1_TRG, INPUT_PULLUP);
 | 
			
		||||
  pinMode(DIO_FAC_2_TRG, INPUT_PULLUP);
 | 
			
		||||
  pinMode(DIO_FAC_3_TRG, INPUT);
 | 
			
		||||
 | 
			
		||||
  Serial.begin(9600);
 | 
			
		||||
  Serial.print("\n\n\n");
 | 
			
		||||
#ifdef SERIAL_DEBUG
 | 
			
		||||
  Serial.setDebugOutput(true);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if (ina219.begin())
 | 
			
		||||
    PowerMonitorTicker.start();
 | 
			
		||||
  else
 | 
			
		||||
    Serial.println("Failed to find INA219 chip");
 | 
			
		||||
 | 
			
		||||
  LittleFS.begin();
 | 
			
		||||
 | 
			
		||||
  WiFi.persistent(false);
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_AP
 | 
			
		||||
  WiFi.mode(WIFI_AP);
 | 
			
		||||
  WiFi.softAP(WIFI_SSID, WIFI_PASS);
 | 
			
		||||
 | 
			
		||||
  stationConnectedHandler = WiFi.onSoftAPModeStationConnected(&onStationConnected);
 | 
			
		||||
  stationDisconnectedHandler = WiFi.onSoftAPModeStationDisconnected(&onStationDisconnected);
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
  WiFi.mode(WIFI_STA);
 | 
			
		||||
  wifiMulti.addAP(WIFI_SSID, WIFI_PASS);
 | 
			
		||||
 | 
			
		||||
  Serial.println("Connecting Wifi...");
 | 
			
		||||
  if (wifiMulti.run() == WL_CONNECTED)
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("");
 | 
			
		||||
    Serial.println("WiFi connected");
 | 
			
		||||
    Serial.println("IP address: ");
 | 
			
		||||
    Serial.print(WiFi.localIP());
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.setPort(8266);
 | 
			
		||||
  ArduinoOTA.setHostname(OTA_HOST);
 | 
			
		||||
  ArduinoOTA.setPassword(OTA_PASS);
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onStart([]()
 | 
			
		||||
                     {
 | 
			
		||||
                       String type;
 | 
			
		||||
                       if (ArduinoOTA.getCommand() == U_FLASH)
 | 
			
		||||
                       {
 | 
			
		||||
                         type = "sketch";
 | 
			
		||||
                       }
 | 
			
		||||
                       else
 | 
			
		||||
                       {
 | 
			
		||||
                         type = "filesystem";
 | 
			
		||||
                         LittleFS.end();
 | 
			
		||||
                       }
 | 
			
		||||
                       Serial.println("Start updating " + type);
 | 
			
		||||
                     });
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onEnd([]()
 | 
			
		||||
                   { Serial.println("\nEnd"); });
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
 | 
			
		||||
                        { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); });
 | 
			
		||||
 | 
			
		||||
  ArduinoOTA.onError([](ota_error_t error)
 | 
			
		||||
                     {
 | 
			
		||||
                       Serial.printf("Error[%u]: ", error);
 | 
			
		||||
                       if (error == OTA_AUTH_ERROR)
 | 
			
		||||
                         Serial.println("Auth Failed");
 | 
			
		||||
                       else if (error == OTA_BEGIN_ERROR)
 | 
			
		||||
                         Serial.println("Begin Failed");
 | 
			
		||||
                       else if (error == OTA_CONNECT_ERROR)
 | 
			
		||||
                         Serial.println("Connect Failed");
 | 
			
		||||
                       else if (error == OTA_RECEIVE_ERROR)
 | 
			
		||||
                         Serial.println("Receive Failed");
 | 
			
		||||
                       else if (error == OTA_END_ERROR)
 | 
			
		||||
                         Serial.println("End Failed");
 | 
			
		||||
                     });
 | 
			
		||||
  ArduinoOTA.begin();
 | 
			
		||||
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
  dnsServer.start(53, "*", WiFi.softAPIP());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  server.addHandler(new CaptiveRequestHandler());
 | 
			
		||||
  server.begin();
 | 
			
		||||
 | 
			
		||||
  FactionTicker.start();
 | 
			
		||||
  SerialOutputTicker.start();
 | 
			
		||||
  InputGetterTicker.start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void loop()
 | 
			
		||||
{
 | 
			
		||||
  FactionTicker.update();
 | 
			
		||||
  SerialOutputTicker.update();
 | 
			
		||||
  InputGetterTicker.update();
 | 
			
		||||
  PowerMonitorTicker.update();
 | 
			
		||||
  ArduinoOTA.handle();
 | 
			
		||||
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
  dnsServer.processNextRequest();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  SevenSeg_Output();
 | 
			
		||||
 | 
			
		||||
  while (Serial.available() > 0)
 | 
			
		||||
  {
 | 
			
		||||
    char input = Serial.read();
 | 
			
		||||
 | 
			
		||||
    switch (input)
 | 
			
		||||
    {
 | 
			
		||||
    case 'n':
 | 
			
		||||
      activeFaction = NONE;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'g':
 | 
			
		||||
      activeFaction = FACTION_1;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'k':
 | 
			
		||||
      activeFaction = FACTION_3;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'm':
 | 
			
		||||
      activeFaction = FACTION_2;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'x':
 | 
			
		||||
      SerialOutputTicker.stop();
 | 
			
		||||
      Serial.println("SerialOutputTicker.stop()");
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'y':
 | 
			
		||||
      SerialOutputTicker.resume();
 | 
			
		||||
      Serial.println("SerialOutputTicker.resume()");
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user