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
									
									
								
							
							
						
						@@ -1,5 +1 @@
 | 
				
			|||||||
.pio
 | 
					Bestellung.xlsx
 | 
				
			||||||
.vscode/.browse.c_cpp.db*
 | 
					 | 
				
			||||||
.vscode/c_cpp_properties.json
 | 
					 | 
				
			||||||
.vscode/launch.json
 | 
					 | 
				
			||||||
.vscode/ipch
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								Documentation/Datasheets/E220-900T22D_UserManual_EN_v1.0.pdf
									
									
									
									
									
										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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						
							
								
								
									
										18457
									
								
								Hardware/DE-Timer.kicad_pcb
									
									
									
									
									
										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
									
								
							
							
						
						
							
								
								
									
										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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					data/
 | 
				
			||||||
 | 
					.pio
 | 
				
			||||||
 | 
					.vscode/.browse.c_cpp.db*
 | 
				
			||||||
 | 
					.vscode/c_cpp_properties.json
 | 
				
			||||||
 | 
					.vscode/launch.json
 | 
				
			||||||
 | 
					.vscode/ipch
 | 
				
			||||||
 | 
					wifi_credentials.ini
 | 
				
			||||||
							
								
								
									
										0
									
								
								Software/README.md
									
									
									
									
									
										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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						
							
								
								
									
										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
									
								
							
							
						
						@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					COMPONENT_ADD_INCLUDEDIRS := src
 | 
				
			||||||
 | 
					COMPONENT_SRCDIRS := src
 | 
				
			||||||
 | 
					CXXFLAGS += -fno-rtti
 | 
				
			||||||
							
								
								
									
										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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						
							
								
								
									
										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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						
							
								
								
									
										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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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>
 | 
				
			||||||
@@ -12,14 +12,14 @@
 | 
				
			|||||||
extra_configs =
 | 
					extra_configs =
 | 
				
			||||||
  wifi_credentials.ini
 | 
					  wifi_credentials.ini
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[env:d1_mini]
 | 
					[env:de-timer]
 | 
				
			||||||
platform = espressif8266
 | 
					platform = espressif32
 | 
				
			||||||
board = d1_mini
 | 
					board = ttgo-lora32-v1
 | 
				
			||||||
board_build.filesystem = littlefs
 | 
					board_build.filesystem = littlefs
 | 
				
			||||||
board_build.f_flash = 80000000L
 | 
					;board_build.f_flash = 80000000L
 | 
				
			||||||
board_build.ldscript = eagle.flash.4m1m.ld
 | 
					;board_build.ldscript = eagle.flash.4m1m.ld
 | 
				
			||||||
 | 
					
 | 
				
			||||||
monitor_filters = esp8266_exception_decoder
 | 
					monitor_filters = esp32_exception_decoder
 | 
				
			||||||
monitor_speed = 115200
 | 
					monitor_speed = 115200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
upload_protocol = esptool
 | 
					upload_protocol = esptool
 | 
				
			||||||
@@ -34,7 +34,6 @@ build_type = debug
 | 
				
			|||||||
build_flags=
 | 
					build_flags=
 | 
				
			||||||
  !python git_rev_macro.py
 | 
					  !python git_rev_macro.py
 | 
				
			||||||
  -DSERIAL_DEBUG
 | 
					  -DSERIAL_DEBUG
 | 
				
			||||||
  -DREMOTE_DEBUG
 | 
					 | 
				
			||||||
  ;-DWIFI_CLIENT
 | 
					  ;-DWIFI_CLIENT
 | 
				
			||||||
  ;-DCAPTIVE
 | 
					  ;-DCAPTIVE
 | 
				
			||||||
  -DWIFI_AP_IP_GW=10,0,1,1
 | 
					  -DWIFI_AP_IP_GW=10,0,1,1
 | 
				
			||||||
@@ -50,9 +49,13 @@ build_flags=
 | 
				
			|||||||
framework = arduino
 | 
					framework = arduino
 | 
				
			||||||
lib_deps =
 | 
					lib_deps =
 | 
				
			||||||
    smougenot/TM1637@0.0.0-alpha+sha.9486982048
 | 
					    smougenot/TM1637@0.0.0-alpha+sha.9486982048
 | 
				
			||||||
    joaolopesf/RemoteDebug @ ^2.1.2
 | 
					    ;me-no-dev/ESP Async WebServer @ ^1.2.3  ; local version included due to bug
 | 
				
			||||||
    me-no-dev/ESP Async WebServer @ ^1.2.3
 | 
					    me-no-dev/AsyncTCP @ ^1.1.1
 | 
				
			||||||
    sstaub/Ticker @ ^4.2.0
 | 
					    sstaub/Ticker @ ^4.2.0
 | 
				
			||||||
    adafruit/Adafruit INA219 @ ^1.1.1
 | 
					    adafruit/Adafruit INA219 @ ^1.1.1
 | 
				
			||||||
    robtillaart/I2C_EEPROM @ ^1.5.2
 | 
					    robtillaart/I2C_EEPROM @ ^1.5.2
 | 
				
			||||||
    me-no-dev/ESP Async WebServer @ ^1.2.3
 | 
					    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
 | 
				
			||||||
@@ -5,17 +5,17 @@
 | 
				
			|||||||
#define QUOTE(x) Q(x)
 | 
					#define QUOTE(x) Q(x)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Module connection pins (ESP GPIO-Nums)
 | 
					// Module connection pins (ESP GPIO-Nums)
 | 
				
			||||||
#define CLK 16
 | 
					#define CLK 12
 | 
				
			||||||
#define DIO_FAC_1_7SEG 14
 | 
					#define DIO_FAC_1_7SEG 13
 | 
				
			||||||
#define DIO_FAC_2_7SEG 12
 | 
					#define DIO_FAC_2_7SEG 17
 | 
				
			||||||
#define DIO_FAC_3_7SEG 13
 | 
					#define DIO_FAC_3_7SEG 21
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DIO_FAC_1_TRG 0
 | 
					#define DIO_FAC_1_TRG 36
 | 
				
			||||||
#define FAC_1_TRG_PRESSED LOW
 | 
					#define FAC_1_TRG_PRESSED LOW
 | 
				
			||||||
#define DIO_FAC_2_TRG 2
 | 
					#define DIO_FAC_2_TRG 37
 | 
				
			||||||
#define FAC_2_TRG_PRESSED LOW
 | 
					#define FAC_2_TRG_PRESSED LOW
 | 
				
			||||||
#define DIO_FAC_3_TRG 15
 | 
					#define DIO_FAC_3_TRG 38
 | 
				
			||||||
#define FAC_3_TRG_PRESSED HIGH
 | 
					#define FAC_3_TRG_PRESSED LOW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef HOST_NAME
 | 
					#ifndef HOST_NAME
 | 
				
			||||||
#define HOST_NAME "DE_Timer_%06X" // Use printf-Formatting - Chip-ID (uin32_t) will be added
 | 
					#define HOST_NAME "DE_Timer_%06X" // Use printf-Formatting - Chip-ID (uin32_t) will be added
 | 
				
			||||||
							
								
								
									
										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
									
								
							
							
						
						@@ -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
 | 
				
			||||||
@@ -2,14 +2,15 @@
 | 
				
			|||||||
#include <TM1637Display.h>
 | 
					#include <TM1637Display.h>
 | 
				
			||||||
#include <Ticker.h>
 | 
					#include <Ticker.h>
 | 
				
			||||||
#include <DNSServer.h>
 | 
					#include <DNSServer.h>
 | 
				
			||||||
#include <ESP8266WiFi.h>
 | 
					#include <WiFi.h>
 | 
				
			||||||
#include <ESPAsyncTCP.h>
 | 
					#include <AsyncTCP.h>
 | 
				
			||||||
#include <ESP8266mDNS.h>
 | 
					#include <ESPmDNS.h>
 | 
				
			||||||
#include <ArduinoOTA.h>
 | 
					#include <ArduinoOTA.h>
 | 
				
			||||||
#include <ESPAsyncWebServer.h>
 | 
					#include <ESPAsyncWebServer.h>
 | 
				
			||||||
#include <LittleFS.h>
 | 
					#include <LittleFS.h>
 | 
				
			||||||
#include <Wire.h>
 | 
					#include <Wire.h>
 | 
				
			||||||
#include <Adafruit_INA219.h>
 | 
					#include <Adafruit_INA219.h>
 | 
				
			||||||
 | 
					#include <ArduinoJson.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// local includes
 | 
					// local includes
 | 
				
			||||||
#include "defaults.h"
 | 
					#include "defaults.h"
 | 
				
			||||||
@@ -18,23 +19,17 @@
 | 
				
			|||||||
#include "globals.h"
 | 
					#include "globals.h"
 | 
				
			||||||
#include "dtc.h"
 | 
					#include "dtc.h"
 | 
				
			||||||
#include "common.h"
 | 
					#include "common.h"
 | 
				
			||||||
 | 
					#include "lora_net.h"
 | 
				
			||||||
#ifdef REMOTE_DEBUG
 | 
					#include "oled_display.h"
 | 
				
			||||||
#include <RemoteDebug.h>
 | 
					 | 
				
			||||||
#include "rmtdbghelp.h"
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#define debugV Serial.println
 | 
					 | 
				
			||||||
#define debugE Serial.println
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef WIFI_CLIENT
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
#include <ESP8266WiFiMulti.h>
 | 
					#include <WiFiMulti.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char *ssid = QUOTE(WIFI_SSID);
 | 
					const char *ssid = QUOTE(WIFI_SSID);
 | 
				
			||||||
const char *password = QUOTE(WIFI_PASSWORD);
 | 
					const char *password = QUOTE(WIFI_PASSWORD);
 | 
				
			||||||
const uint32_t connectTimeoutMs = 5000;
 | 
					const uint32_t connectTimeoutMs = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ESP8266WiFiMulti wifiMulti;
 | 
					WiFiMulti wifiMulti;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SevenSeg_Output();
 | 
					void SevenSeg_Output();
 | 
				
			||||||
@@ -47,26 +42,15 @@ void SystemShutdown();
 | 
				
			|||||||
void SetBatteryType(batteryType_t type);
 | 
					void SetBatteryType(batteryType_t type);
 | 
				
			||||||
void ProcessKeyCombos();
 | 
					void ProcessKeyCombos();
 | 
				
			||||||
void OverrideDisplay(const uint8_t *message, uint32_t time);
 | 
					void OverrideDisplay(const uint8_t *message, uint32_t time);
 | 
				
			||||||
 | 
					void LoRaPublish_callback();
 | 
				
			||||||
#ifdef REMOTE_DEBUG
 | 
					 | 
				
			||||||
RemoteDebug Debug;
 | 
					 | 
				
			||||||
String IpAddress2String(const IPAddress &ipAddress);
 | 
					 | 
				
			||||||
void processCmdRemoteDebug();
 | 
					 | 
				
			||||||
void RemoteDebug_formatCFG();
 | 
					 | 
				
			||||||
void RemoteDebug_formatPersistence();
 | 
					 | 
				
			||||||
void RemotDebug_printSystemInfo();
 | 
					 | 
				
			||||||
void RemoteDebug_printWifiInfo();
 | 
					 | 
				
			||||||
void RemoteDebug_CheckEEPOM();
 | 
					 | 
				
			||||||
void RemoteDebug_dumpConfig();
 | 
					 | 
				
			||||||
void RemoteDebug_dumpPersistance();
 | 
					 | 
				
			||||||
void RemoteDebug_ShowDTCs();
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef WIFI_CLIENT
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
void wifiMaintainConnectionTicker_callback();
 | 
					void wifiMaintainConnectionTicker_callback();
 | 
				
			||||||
Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS);
 | 
					Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t getESPChipID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG);
 | 
					TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG);
 | 
				
			||||||
TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG);
 | 
					TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG);
 | 
				
			||||||
TM1637Display disp_FAC_3(CLK, DIO_FAC_3_7SEG);
 | 
					TM1637Display disp_FAC_3(CLK, DIO_FAC_3_7SEG);
 | 
				
			||||||
@@ -82,6 +66,7 @@ Ticker FactionTicker(FactionTicker_callback, 1000, 0, MILLIS);
 | 
				
			|||||||
Ticker InputGetterTicker(inputGetterTicker_callback, 250, 0, MILLIS);
 | 
					Ticker InputGetterTicker(inputGetterTicker_callback, 250, 0, MILLIS);
 | 
				
			||||||
Ticker PowerMonitorTicker(powerMonitorTicker_callback, 5000, 0, MILLIS);
 | 
					Ticker PowerMonitorTicker(powerMonitorTicker_callback, 5000, 0, MILLIS);
 | 
				
			||||||
Ticker EEPROMCyclicPDSTicker(EEPROMCyclicPDS_callback, 60000, 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_1_dot = 0;
 | 
				
			||||||
uint8_t Faction_2_dot = 0;
 | 
					uint8_t Faction_2_dot = 0;
 | 
				
			||||||
@@ -101,34 +86,24 @@ const uint8_t sevenSeg_file[] = {0x71, 0x30, 0x38, 0x79};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void setup()
 | 
					void setup()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  system_update_cpu_freq(SYS_CPU_80MHZ);
 | 
					  setCpuFrequencyMhz(80);
 | 
				
			||||||
 | 
					  WiFi.setAutoReconnect(false);
 | 
				
			||||||
  WiFi.persistent(false);
 | 
					  WiFi.persistent(false);
 | 
				
			||||||
 | 
					  WiFi.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Serial.begin(115200);
 | 
					  Serial.begin(115200);
 | 
				
			||||||
  Serial.print("\n\n\n");
 | 
					  Serial.print("\n\n\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  strcpy(globals.DeviceName, DEVICE_NAME);
 | 
					  strcpy(globals.DeviceName, DEVICE_NAME);
 | 
				
			||||||
  snprintf(globals.DeviceName_ID, 42, "%s_%08X", globals.DeviceName, ESP.getChipId());
 | 
					  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_1_TRG, INPUT_PULLUP);
 | 
				
			||||||
  pinMode(DIO_FAC_2_TRG, INPUT_PULLUP);
 | 
					  pinMode(DIO_FAC_2_TRG, INPUT_PULLUP);
 | 
				
			||||||
  pinMode(DIO_FAC_3_TRG, INPUT);
 | 
					  pinMode(DIO_FAC_3_TRG, INPUT_PULLUP);
 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef REMOTE_DEBUG
 | 
					 | 
				
			||||||
  if (MDNS.begin(globals.DeviceName_ID))
 | 
					 | 
				
			||||||
    MDNS.addService("telnet", "tcp", 23);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Debug.begin(globals.DeviceName_ID);
 | 
					 | 
				
			||||||
  Debug.setResetCmdEnabled(true);
 | 
					 | 
				
			||||||
  Debug.showProfiler(false);
 | 
					 | 
				
			||||||
  Debug.showColors(true);
 | 
					 | 
				
			||||||
  Debug.setPassword(QUOTE(ADMIN_PASSWORD));
 | 
					 | 
				
			||||||
  Debug.setSerialEnabled(true);
 | 
					 | 
				
			||||||
  Debug.showDebugLevel(true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Debug.setHelpProjectsCmds(helpCmd);
 | 
					 | 
				
			||||||
  Debug.setCallBackProjectCmds(&processCmdRemoteDebug);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef SERIAL_DEBUG
 | 
					#ifdef SERIAL_DEBUG
 | 
				
			||||||
  Serial.setDebugOutput(true);
 | 
					  Serial.setDebugOutput(true);
 | 
				
			||||||
@@ -147,6 +122,9 @@ void setup()
 | 
				
			|||||||
  wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD));
 | 
					  wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD));
 | 
				
			||||||
  WiFiMaintainConnectionTicker.start();
 | 
					  WiFiMaintainConnectionTicker.start();
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
 | 
					  WiFi.mode(WIFI_AP);
 | 
				
			||||||
 | 
					  WiFi.begin(QUOTE(DEVICE_NAME), QUOTE(WIFI_AP_PASSWORD));
 | 
				
			||||||
 | 
					  WiFi.setSleep(true);
 | 
				
			||||||
  WiFi.mode(WIFI_OFF);
 | 
					  WiFi.mode(WIFI_OFF);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -217,16 +195,15 @@ void loop()
 | 
				
			|||||||
  FactionTicker.update();
 | 
					  FactionTicker.update();
 | 
				
			||||||
  InputGetterTicker.update();
 | 
					  InputGetterTicker.update();
 | 
				
			||||||
  PowerMonitorTicker.update();
 | 
					  PowerMonitorTicker.update();
 | 
				
			||||||
 | 
					  LoRaPublishTicker.update();
 | 
				
			||||||
  ArduinoOTA.handle();
 | 
					  ArduinoOTA.handle();
 | 
				
			||||||
  SevenSeg_Output();
 | 
					  SevenSeg_Output();
 | 
				
			||||||
  EEPROM_Process();
 | 
					  EEPROM_Process();
 | 
				
			||||||
 | 
					  OLED_Process();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CAPTIVE
 | 
					#ifdef CAPTIVE
 | 
				
			||||||
  dnsServer.processNextRequest();
 | 
					  dnsServer.processNextRequest();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef REMOTE_DEBUG
 | 
					 | 
				
			||||||
  Debug.handle();
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef WIFI_CLIENT
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
  WiFiMaintainConnectionTicker.update();
 | 
					  WiFiMaintainConnectionTicker.update();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -374,12 +351,12 @@ void powerMonitorTicker_callback()
 | 
				
			|||||||
    break;
 | 
					    break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  debugV("Battery Level: %d %%", globals.battery_level);
 | 
					  // Serial.printf("Battery Level: %d %%\n", globals.battery_level);
 | 
				
			||||||
  debugV("Bus Voltage: %f V", busvoltage);
 | 
					  // Serial.printf("Bus Voltage: %f V\n", busvoltage);
 | 
				
			||||||
  debugV("Shunt Voltage: %f mV", shuntvoltage);
 | 
					  // Serial.printf("Shunt Voltage: %f mV\n", shuntvoltage);
 | 
				
			||||||
  debugV("Load Voltage: %f V", globals.loadvoltage);
 | 
					  // Serial.printf("Load Voltage: %f V\n", globals.loadvoltage);
 | 
				
			||||||
  debugV("Current: %f mA", current_mA);
 | 
					  // Serial.printf("Current: %f mA\n", current_mA);
 | 
				
			||||||
  debugV("Power: %f mW", power_mW);
 | 
					  // Serial.printf("Power: %f mW\n", power_mW);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EEPROMCyclicPDS_callback()
 | 
					void EEPROMCyclicPDS_callback()
 | 
				
			||||||
@@ -433,164 +410,13 @@ void SystemShutdown()
 | 
				
			|||||||
  ESP.restart();
 | 
					  ESP.restart();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef REMOTE_DEBUG
 | 
					 | 
				
			||||||
void processCmdRemoteDebug()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  String lastCmd = Debug.getLastCommand();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (lastCmd == "sysinfo")
 | 
					 | 
				
			||||||
    RemotDebug_printSystemInfo();
 | 
					 | 
				
			||||||
  else if (lastCmd == "netinfo")
 | 
					 | 
				
			||||||
    RemoteDebug_printWifiInfo();
 | 
					 | 
				
			||||||
  else if (lastCmd == "formatCFG")
 | 
					 | 
				
			||||||
    RemoteDebug_formatCFG();
 | 
					 | 
				
			||||||
  else if (lastCmd == "formatPDS")
 | 
					 | 
				
			||||||
    RemoteDebug_formatPersistence();
 | 
					 | 
				
			||||||
  else if (lastCmd == "checkEE")
 | 
					 | 
				
			||||||
    RemoteDebug_CheckEEPOM();
 | 
					 | 
				
			||||||
  else if (lastCmd == "dumpEE1k")
 | 
					 | 
				
			||||||
    dumpEEPROM(0, 1024);
 | 
					 | 
				
			||||||
  else if (lastCmd == "dumpEE")
 | 
					 | 
				
			||||||
    dumpEEPROM(0, EEPROM_SIZE_BYTES);
 | 
					 | 
				
			||||||
  else if (lastCmd == "resetPageEE")
 | 
					 | 
				
			||||||
    MovePersistencePage_EEPROM(true);
 | 
					 | 
				
			||||||
  else if (lastCmd == "dumpCFG")
 | 
					 | 
				
			||||||
    RemoteDebug_dumpConfig();
 | 
					 | 
				
			||||||
  else if (lastCmd == "dumpPDS")
 | 
					 | 
				
			||||||
    RemoteDebug_dumpPersistance();
 | 
					 | 
				
			||||||
  else if (lastCmd == "saveEE")
 | 
					 | 
				
			||||||
    StoreConfig_EEPROM();
 | 
					 | 
				
			||||||
  else if (lastCmd == "showdtc")
 | 
					 | 
				
			||||||
    RemoteDebug_ShowDTCs();
 | 
					 | 
				
			||||||
  else if (lastCmd == "bat_3s")
 | 
					 | 
				
			||||||
    SetBatteryType(BATTERY_LIPO_3S);
 | 
					 | 
				
			||||||
  else if (lastCmd == "bat_2s")
 | 
					 | 
				
			||||||
    SetBatteryType(BATTERY_LIPO_2S);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemoteDebug_formatCFG()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  debugA("Formatting Config-EEPROM and reseting to default");
 | 
					 | 
				
			||||||
  FormatConfig_EEPROM();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemoteDebug_formatPersistence()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  debugA("Formatting Persistence-EEPROM and reseting to default");
 | 
					 | 
				
			||||||
  FormatPersistence_EEPROM();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemotDebug_printSystemInfo()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  debugA("DE Timer Mk1");
 | 
					 | 
				
			||||||
  debugA("Hostname: %s", globals.DeviceName_ID);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  FlashMode_t ideMode = ESP.getFlashChipMode();
 | 
					 | 
				
			||||||
  debugA("Sdk version: %s", ESP.getSdkVersion());
 | 
					 | 
				
			||||||
  debugA("Core Version: %s", ESP.getCoreVersion().c_str());
 | 
					 | 
				
			||||||
  debugA("Boot Version: %u", ESP.getBootVersion());
 | 
					 | 
				
			||||||
  debugA("Boot Mode: %u", ESP.getBootMode());
 | 
					 | 
				
			||||||
  debugA("CPU Frequency: %u MHz", ESP.getCpuFreqMHz());
 | 
					 | 
				
			||||||
  debugA("Reset reason: %s", ESP.getResetReason().c_str());
 | 
					 | 
				
			||||||
  debugA("Flash Size: %d", ESP.getFlashChipRealSize());
 | 
					 | 
				
			||||||
  debugA("Flash Size IDE: %d", ESP.getFlashChipSize());
 | 
					 | 
				
			||||||
  debugA("Flash ide mode:  %s", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT"
 | 
					 | 
				
			||||||
                                                         : ideMode == FM_DIO    ? "DIO"
 | 
					 | 
				
			||||||
                                                         : ideMode == FM_DOUT   ? "DOUT"
 | 
					 | 
				
			||||||
                                                                                : "UNKNOWN"));
 | 
					 | 
				
			||||||
  debugA("OTA-Pass: %s", QUOTE(ADMIN_PASSWORD));
 | 
					 | 
				
			||||||
  debugA("Git-Revison: %s", GIT_REV);
 | 
					 | 
				
			||||||
  debugA("I2C EEPROM Size: %d", GetEESize());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemoteDebug_dumpConfig()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  debugA("EEPROM_Version: %d", ConfigData.EEPROM_Version);
 | 
					 | 
				
			||||||
  debugA("BatteryType: %d", ConfigData.batteryType);
 | 
					 | 
				
			||||||
  debugA("checksum: 0x%08X", ConfigData.checksum);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemoteDebug_dumpPersistance()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  debugA("writeCycleCounter: %d", PersistenceData.writeCycleCounter);
 | 
					 | 
				
			||||||
  debugA("faction_1_timer: %d", PersistenceData.faction_1_timer);
 | 
					 | 
				
			||||||
  debugA("faction_2_timer: %d", PersistenceData.faction_2_timer);
 | 
					 | 
				
			||||||
  debugA("faction_2_timer: %d", PersistenceData.faction_3_timer);
 | 
					 | 
				
			||||||
  debugA("activeFaction: %d", PersistenceData.activeFaction);
 | 
					 | 
				
			||||||
  debugA("checksum: %d", PersistenceData.checksum);
 | 
					 | 
				
			||||||
  debugA("PSD Adress: 0x%04X", getPersistanceAddress());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemoteDebug_printWifiInfo()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemoteDebug_CheckEEPOM()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  uint32_t checksum = PersistenceData.checksum;
 | 
					 | 
				
			||||||
  PersistenceData.checksum = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) == checksum)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    debugA("PersistenceData EEPROM Checksum OK\n");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    debugA("PersistenceData EEPROM Checksum BAD\n");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  PersistenceData.checksum = checksum;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  checksum = ConfigData.checksum;
 | 
					 | 
				
			||||||
  ConfigData.checksum = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData)) == checksum)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    debugA("ConfigData EEPROM Checksum OK\n");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    debugA("ConfigData EEPROM Checksum BAD\n");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ConfigData.checksum = checksum;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RemoteDebug_ShowDTCs()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
 | 
					 | 
				
			||||||
  char buff_active[9];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (DTCStorage[i].active == DTC_ACTIVE)
 | 
					 | 
				
			||||||
        strcpy(buff_active, "active");
 | 
					 | 
				
			||||||
      else if (DTCStorage[i].active == DTC_PREVIOUS)
 | 
					 | 
				
			||||||
        strcpy(buff_active, "previous");
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        strcpy(buff_active, "none");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      debugA("%s \t %6d \t %s", buff_timestamp, DTCStorage[i].Number, buff_active);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SetBatteryType(batteryType_t type)
 | 
					void SetBatteryType(batteryType_t type)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  if (ConfigData.batteryType != type)
 | 
					  if (ConfigData.batteryType != type)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    ConfigData.batteryType = type;
 | 
					    ConfigData.batteryType = type;
 | 
				
			||||||
    globals.requestEEAction = EE_CFG_SAVE;
 | 
					    globals.requestEEAction = EE_CFG_SAVE;
 | 
				
			||||||
    debugV("Set Batterytype to %s", type == BATTERY_LIPO_2S ? "2s Lipo" : "3s LiPo");
 | 
					    Serial.printf("Set Batterytype to %s\n", type == BATTERY_LIPO_2S ? "2s Lipo" : "3s LiPo");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -635,7 +461,7 @@ void ProcessKeyCombos()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (digitalRead(DIO_FAC_3_TRG) != FAC_3_TRG_PRESSED && keyStatus_Fac3 == KEY_PRESSED)
 | 
					  if (digitalRead(DIO_FAC_3_TRG) != FAC_3_TRG_PRESSED && keyStatus_Fac3 == KEY_PRESSED)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    debugV("KeyCombo 1: %d | 2: %d", keyCount_Fac1, keyCount_Fac2);
 | 
					    Serial.printf("KeyCombo 1: %d | 2: %d\n", keyCount_Fac1, keyCount_Fac2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (keyCount_Fac1 == 2 && keyCount_Fac2 == 2)
 | 
					    if (keyCount_Fac1 == 2 && keyCount_Fac2 == 2)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -656,4 +482,32 @@ void OverrideDisplay(const uint8_t *message, uint32_t time)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  DisplayOverrideFlag = millis() + time;
 | 
					  DisplayOverrideFlag = millis() + time;
 | 
				
			||||||
  DisplayOverrideValue = (uint8_t *)message;
 | 
					  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
									
								
							
							
						
						@@ -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
									
								
							
							
						
						@@ -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
 | 
				
			||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
#include <Arduino.h>
 | 
					#include <Arduino.h>
 | 
				
			||||||
#include <FS.h>
 | 
					#include <FS.h>
 | 
				
			||||||
#include <LittleFS.h>
 | 
					#include <LittleFS.h>
 | 
				
			||||||
#include <ESPAsyncTCP.h>
 | 
					#include <AsyncTCP.h>
 | 
				
			||||||
#include <ESPAsyncWebServer.h>
 | 
					#include <ESPAsyncWebServer.h>
 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#include "globals.h"
 | 
					#include "globals.h"
 | 
				
			||||||
							
								
								
									
										200
									
								
								data/index.htm
									
									
									
									
									
								
							
							
						
						@@ -1,200 +0,0 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					 | 
				
			||||||
<html>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<head>
 | 
					 | 
				
			||||||
  <meta charset="utf-8" />
 | 
					 | 
				
			||||||
  <title>%DEVICE_NAME%</title>
 | 
					 | 
				
			||||||
  <meta http-equiv="content-type" content="text/html;charset=UTF-8">
 | 
					 | 
				
			||||||
  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
					 | 
				
			||||||
  <link rel="stylesheet" href="static/css/bootstrap.min.css">
 | 
					 | 
				
			||||||
  <link rel="stylesheet" href="static/css/custom.css">
 | 
					 | 
				
			||||||
  <script src="static/js/jquery-3.3.1.min.js"></script>
 | 
					 | 
				
			||||||
  <script src="static/js/bootstrap.bundle.min.js"></script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png">
 | 
					 | 
				
			||||||
  <link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png">
 | 
					 | 
				
			||||||
  <link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png">
 | 
					 | 
				
			||||||
  <link rel="manifest" href="static/img/site.webmanifest">
 | 
					 | 
				
			||||||
  <link rel="mask-icon" href="static/img/safari-pinned-tab.svg" color="#111111">
 | 
					 | 
				
			||||||
  <link rel="shortcut icon" href="static/img/favicon.ico">
 | 
					 | 
				
			||||||
  <meta name="msapplication-TileColor" content="#111111">
 | 
					 | 
				
			||||||
  <meta name="msapplication-config" content="static/img/browserconfig.xml">
 | 
					 | 
				
			||||||
  <meta name="theme-color" content="#111111">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <script type="text/javascript">
 | 
					 | 
				
			||||||
    var opacity = 0;
 | 
					 | 
				
			||||||
    var intervalID = 0;
 | 
					 | 
				
			||||||
    window.onload = setTimeout(() => {  fadeout(); }, 4000);
 | 
					 | 
				
			||||||
    function fadeout() {
 | 
					 | 
				
			||||||
      intervalID = setInterval(hide, 200);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    function hide() {
 | 
					 | 
				
			||||||
      var responseMessage = document.getElementById("responseMessage");
 | 
					 | 
				
			||||||
      opacity =
 | 
					 | 
				
			||||||
        Number(window.getComputedStyle(responseMessage).getPropertyValue("opacity"))
 | 
					 | 
				
			||||||
      if (opacity > 0) {
 | 
					 | 
				
			||||||
        opacity = opacity - 0.1;
 | 
					 | 
				
			||||||
        responseMessage.style.opacity = opacity
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      else {
 | 
					 | 
				
			||||||
        responseMessage.remove()
 | 
					 | 
				
			||||||
        clearInterval(intervalID);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } 
 | 
					 | 
				
			||||||
  </script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</head>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<body>
 | 
					 | 
				
			||||||
  <header class="sticky-top">
 | 
					 | 
				
			||||||
    <nav class="navbar navbar-expand-sm navbar-dark bg-dark">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <a class="navbar-brand" href="#">
 | 
					 | 
				
			||||||
        <img src="static/img/logo.png" width="30" height="30" class="d-inline-block align-top" alt="">
 | 
					 | 
				
			||||||
        %DEVICE_NAME%</a>
 | 
					 | 
				
			||||||
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup"
 | 
					 | 
				
			||||||
        aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
 | 
					 | 
				
			||||||
        <span class="navbar-toggler-icon"></span>
 | 
					 | 
				
			||||||
      </button>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <div class="collapse navbar-collapse pr-2 mt-2 bg-secondary" id="navbarNavAltMarkup">
 | 
					 | 
				
			||||||
        <ul class="navbar-nav nav align-items-end">
 | 
					 | 
				
			||||||
          <li class="nav-item">
 | 
					 | 
				
			||||||
            <a class="nav-link active" href="#tab_home" data-toggle="tab">Home</a>
 | 
					 | 
				
			||||||
          </li>
 | 
					 | 
				
			||||||
          <li class="nav-item">
 | 
					 | 
				
			||||||
            <a class="nav-link" href="#tab_settings" data-toggle="tab">Settings</a>
 | 
					 | 
				
			||||||
          </li>
 | 
					 | 
				
			||||||
        </ul>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </nav>
 | 
					 | 
				
			||||||
  </header>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <main class="container-fluid py-3 flex-fill">
 | 
					 | 
				
			||||||
    <!-- Tabs Content -->
 | 
					 | 
				
			||||||
    <div class="tab-content">
 | 
					 | 
				
			||||||
      <!-- Div Tab Home-->
 | 
					 | 
				
			||||||
      <div class="tab-pane fade show active" id="tab_home" role="tabpanel">
 | 
					 | 
				
			||||||
        <!-- Jumbotron -->
 | 
					 | 
				
			||||||
        <div class="jumbotron text-center">
 | 
					 | 
				
			||||||
          <p class="p-4">
 | 
					 | 
				
			||||||
            <img src="static/img/logo.png">
 | 
					 | 
				
			||||||
          </p>
 | 
					 | 
				
			||||||
          <h3>%DEVICE_NAME%</h3>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <!-- AlertMessageBox-->
 | 
					 | 
				
			||||||
        <div id="responseMessage" class="alert alert-%RESP_MESSAGE_TYPE%" %SHOW_RESP_MESSAGE%>
 | 
					 | 
				
			||||||
          %RESP_MESSAGE%
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="container-fluid pb-5">
 | 
					 | 
				
			||||||
          <h4>BATTERY: %BATTERY_TYPE%</h4>
 | 
					 | 
				
			||||||
          <div class="progress">
 | 
					 | 
				
			||||||
            <div class="progress-bar text-light" role="progressbar" aria-valuenow="%BAT_REMAIN_CAPACITY%"
 | 
					 | 
				
			||||||
              aria-valuemin="0" aria-valuemax="100" style="width: %BAT_REMAIN_CAPACITY%%">
 | 
					 | 
				
			||||||
              %BAT_REMAIN_CAPACITY%% / %BAT_VOLTAGE% V
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="container-fluid pb-5">
 | 
					 | 
				
			||||||
          <h4>FACTIONS</h4>
 | 
					 | 
				
			||||||
          <div class="table-responsive">
 | 
					 | 
				
			||||||
            <table class="table text-light text-center">
 | 
					 | 
				
			||||||
              <thead>
 | 
					 | 
				
			||||||
                <tr>
 | 
					 | 
				
			||||||
                  <th scope="col">Faction</td>
 | 
					 | 
				
			||||||
                  <th scope="col">Time</td>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
              </thead>
 | 
					 | 
				
			||||||
              <tbody>
 | 
					 | 
				
			||||||
                <tr>
 | 
					 | 
				
			||||||
                  <td class="align-items-center justify-content-center" scope="col">
 | 
					 | 
				
			||||||
                    <a href="#">
 | 
					 | 
				
			||||||
                      <img src="static/img/logo_fac1.png" class="img-responsive" alt="" />
 | 
					 | 
				
			||||||
                      <div class="desc">
 | 
					 | 
				
			||||||
                        <p class="desc_content">%NAME_FAC_1%</p>
 | 
					 | 
				
			||||||
                      </div>
 | 
					 | 
				
			||||||
                    </a>
 | 
					 | 
				
			||||||
                  </td>
 | 
					 | 
				
			||||||
                  <td class="align-items-center justify-content-center" scope="col">%POINTS_FAC_1%</td>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
                <tr>
 | 
					 | 
				
			||||||
                  <td class="align-items-center justify-content-center" scope="col">
 | 
					 | 
				
			||||||
                    <a href="#">
 | 
					 | 
				
			||||||
                      <img src="static/img/logo_fac2.png" class="img-responsive" alt="" />
 | 
					 | 
				
			||||||
                      <div class="desc">
 | 
					 | 
				
			||||||
                        <p class="desc_content">%NAME_FAC_2%</p>
 | 
					 | 
				
			||||||
                      </div>
 | 
					 | 
				
			||||||
                    </a>
 | 
					 | 
				
			||||||
                  </td>
 | 
					 | 
				
			||||||
                  <td class="align-items-center justify-content-center" scope="col">%POINTS_FAC_2%</td>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
                <tr>
 | 
					 | 
				
			||||||
                  <td class="align-items-center justify-content-center" scope="col">
 | 
					 | 
				
			||||||
                    <a href="#">
 | 
					 | 
				
			||||||
                      <img src="static/img/logo_fac3.png" class="img-responsive" alt="" />
 | 
					 | 
				
			||||||
                      <div class="desc">
 | 
					 | 
				
			||||||
                        <p class="desc_content">%NAME_FAC_3%</p>
 | 
					 | 
				
			||||||
                      </div>
 | 
					 | 
				
			||||||
                    </a>
 | 
					 | 
				
			||||||
                  </td>
 | 
					 | 
				
			||||||
                  <td class="align-items-center justify-content-center" scope="col">%POINTS_FAC_3%</td>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
              </tbody>
 | 
					 | 
				
			||||||
            </table>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="container-fluid pb-5" %SHOW_DTC_TABLE%>
 | 
					 | 
				
			||||||
          <h4>Fehlercodes</h4>
 | 
					 | 
				
			||||||
          <table class="table text-light">
 | 
					 | 
				
			||||||
            <tbody>
 | 
					 | 
				
			||||||
              <tr>
 | 
					 | 
				
			||||||
                <th class="col-md-4" scope="col">Timestamp</td>
 | 
					 | 
				
			||||||
                <th class="col-md-4" scope="col">DTC</td>
 | 
					 | 
				
			||||||
                <th class="col-md-4" scope="col">active</td>
 | 
					 | 
				
			||||||
              </tr>
 | 
					 | 
				
			||||||
              %DTC_TABLE%
 | 
					 | 
				
			||||||
            </tbody>
 | 
					 | 
				
			||||||
          </table>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <!-- Div Tab Home-->
 | 
					 | 
				
			||||||
      <!-- Div Tab Settings-->
 | 
					 | 
				
			||||||
      <div class="tab-pane fade" id="tab_settings" role="tabpanel">
 | 
					 | 
				
			||||||
        <h3>Settings</h3>
 | 
					 | 
				
			||||||
        <hr>
 | 
					 | 
				
			||||||
        <form method="POST">
 | 
					 | 
				
			||||||
          <div class="form-group row">
 | 
					 | 
				
			||||||
            <label for="commandInput" class="col-sm-2 col-form-label">Command</label>
 | 
					 | 
				
			||||||
            <div class="col-sm-10">
 | 
					 | 
				
			||||||
              <input type="text" name="commandInput" class="form-control bg-light" id="commandInput"
 | 
					 | 
				
			||||||
                placeholder="type command here...">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="form-group row">
 | 
					 | 
				
			||||||
            <div class="col-sm-12">
 | 
					 | 
				
			||||||
              <button name="cmdsubmit" type="submit" class="btn btn-primary float-right">Apply</button>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </form>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <!-- Div Tab Settings-->
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <!-- Tabs Content -->
 | 
					 | 
				
			||||||
  </main>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <!-- Footer -->
 | 
					 | 
				
			||||||
  <footer class="fixed-bottom bg-secondary font-weight-light text-light text-center py-3">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <!-- Copyright -->
 | 
					 | 
				
			||||||
    <span class=" footer-copyright">
 | 
					 | 
				
			||||||
      © 2022 <a class="text-reset fw-bold" href="https://hiabuto.de/">Hiabuto Defence Systems</a>
 | 
					 | 
				
			||||||
    </span>
 | 
					 | 
				
			||||||
    <!-- Copyright -->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  </footer>
 | 
					 | 
				
			||||||
  <!-- Footer -->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</body>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</html>
 | 
					 | 
				
			||||||
@@ -1,28 +0,0 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					 | 
				
			||||||
<html>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<head>
 | 
					 | 
				
			||||||
  <meta charset="utf-8" />
 | 
					 | 
				
			||||||
  <title>%DEVICE_NAME%</title>
 | 
					 | 
				
			||||||
  <meta http-equiv="content-type" content="text/html;charset=UTF-8">
 | 
					 | 
				
			||||||
  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
					 | 
				
			||||||
  <link rel="stylesheet" href="static/css/bootstrap.min.css">
 | 
					 | 
				
			||||||
  <link rel="stylesheet" href="static/css/custom.css">
 | 
					 | 
				
			||||||
  <script src="static/js/jquery.min.js"></script>
 | 
					 | 
				
			||||||
  <script src="static/js/bootstrap.min.js"></script>
 | 
					 | 
				
			||||||
  <link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png">
 | 
					 | 
				
			||||||
  <link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png">
 | 
					 | 
				
			||||||
  <link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png">
 | 
					 | 
				
			||||||
  <link rel="manifest" href="static/img/site.webmanifest">
 | 
					 | 
				
			||||||
  <meta http-equiv="refresh" content="3; url='/index.htm'" />
 | 
					 | 
				
			||||||
</head>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<body>
 | 
					 | 
				
			||||||
  <div class="container" style="display: flex; justify-content: center; align-items: center; height: 100vh">
 | 
					 | 
				
			||||||
    <div class="alert alert-success">
 | 
					 | 
				
			||||||
      <strong>Bitte warten!</strong> Änderungen werden übernommen.
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
</body>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</html>
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								data/static/css/bootstrap.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 34 KiB  | 
| 
		 Before Width: | Height: | Size: 55 KiB  | 
| 
		 Before Width: | Height: | Size: 30 KiB  | 
@@ -1,9 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<browserconfig>
 | 
					 | 
				
			||||||
    <msapplication>
 | 
					 | 
				
			||||||
        <tile>
 | 
					 | 
				
			||||||
            <square150x150logo src="/mstile-150x150.png"/>
 | 
					 | 
				
			||||||
            <TileColor>#da532c</TileColor>
 | 
					 | 
				
			||||||
        </tile>
 | 
					 | 
				
			||||||
    </msapplication>
 | 
					 | 
				
			||||||
</browserconfig>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 15 KiB  | 
| 
		 Before Width: | Height: | Size: 32 KiB  | 
| 
		 Before Width: | Height: | Size: 37 KiB  | 
| 
		 Before Width: | Height: | Size: 26 KiB  | 
| 
		 Before Width: | Height: | Size: 34 KiB  | 
| 
		 Before Width: | Height: | Size: 18 KiB  | 
@@ -1,345 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" standalone="no"?>
 | 
					 | 
				
			||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
 | 
					 | 
				
			||||||
 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
 | 
					 | 
				
			||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
 width="300.000000pt" height="300.000000pt" viewBox="0 0 300.000000 300.000000"
 | 
					 | 
				
			||||||
 preserveAspectRatio="xMidYMid meet">
 | 
					 | 
				
			||||||
<metadata>
 | 
					 | 
				
			||||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
 | 
					 | 
				
			||||||
</metadata>
 | 
					 | 
				
			||||||
<g transform="translate(0.000000,300.000000) scale(0.100000,-0.100000)"
 | 
					 | 
				
			||||||
fill="#000000" stroke="none">
 | 
					 | 
				
			||||||
<path d="M210 2778 c1 -198 2 -221 15 -204 13 18 17 18 41 5 31 -16 41 -36 26
 | 
					 | 
				
			||||||
-51 -8 -8 -15 -8 -25 0 -19 16 -49 -22 -53 -69 -3 -30 1 -38 16 -42 26 -7 25
 | 
					 | 
				
			||||||
-27 -1 -34 -18 -5 -20 -11 -15 -47 3 -23 6 -48 5 -56 0 -8 -2 -168 -4 -355
 | 
					 | 
				
			||||||
l-4 -340 31 -18 c29 -17 31 -16 69 5 22 12 38 27 35 33 -2 7 0 18 5 26 7 11 9
 | 
					 | 
				
			||||||
11 9 -3 0 -26 13 -23 93 25 39 24 98 58 130 76 31 18 57 38 58 44 0 7 1 63 1
 | 
					 | 
				
			||||||
125 1 87 -2 116 -14 130 -26 29 -30 59 -13 92 8 16 19 30 23 30 8 0 7 212 -2
 | 
					 | 
				
			||||||
235 -3 8 -2 15 2 15 10 0 8 62 -2 78 -4 7 -5 12 -1 12 3 0 1 9 -5 20 -6 11
 | 
					 | 
				
			||||||
-17 20 -25 20 -8 0 -15 5 -15 11 0 16 -26 39 -45 39 -12 0 -15 6 -10 21 8 26
 | 
					 | 
				
			||||||
78 139 86 139 3 0 11 -36 18 -80 13 -79 23 -101 36 -80 4 7 11 8 18 1 14 -11
 | 
					 | 
				
			||||||
36 -14 103 -12 l52 1 25 53 c15 28 40 67 56 85 29 31 31 32 51 15 27 -23 25
 | 
					 | 
				
			||||||
-37 -10 -100 l-30 -53 75 0 c41 0 74 3 74 8 -3 18 2 32 11 32 6 0 10 -9 10
 | 
					 | 
				
			||||||
-20 0 -17 7 -20 39 -20 29 0 41 5 44 18 4 15 5 15 6 0 1 -13 13 -18 54 -20 99
 | 
					 | 
				
			||||||
-6 227 3 227 15 0 7 9 23 19 37 l19 24 18 -37 c19 -39 32 -43 118 -37 30 2 60
 | 
					 | 
				
			||||||
23 49 34 -13 13 -13 48 -1 44 7 -3 14 -20 15 -38 2 -18 5 -35 7 -37 2 -2 30
 | 
					 | 
				
			||||||
-3 62 -1 53 3 57 4 41 19 -18 19 -13 29 15 29 13 0 18 -7 18 -25 0 -33 10 -32
 | 
					 | 
				
			||||||
50 10 19 19 38 35 42 35 5 0 16 -16 24 -36 13 -31 18 -35 47 -32 20 2 32 8 32
 | 
					 | 
				
			||||||
18 0 8 9 15 20 16 48 2 48 3 34 22 -13 15 -12 16 3 4 14 -11 23 -9 59 17 23
 | 
					 | 
				
			||||||
17 46 31 52 31 8 0 539 302 556 316 2 2 -186 4 -418 3 -265 0 -419 -3 -416 -9
 | 
					 | 
				
			||||||
4 -6 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 -16 10 -35 10 -24 0 -35 -4 -33 -12
 | 
					 | 
				
			||||||
1 -7 -6 -26 -17 -41 l-18 -29 -16 29 c-8 15 -14 34 -13 41 2 9 -21 12 -88 12
 | 
					 | 
				
			||||||
-70 0 -89 -3 -84 -12 6 -10 4 -10 -8 0 -23 18 -184 13 -180 -6 1 -8 -3 -12
 | 
					 | 
				
			||||||
-10 -9 -7 2 -12 10 -11 16 2 7 -16 11 -52 11 -42 0 -55 -3 -55 -15 0 -19 -34
 | 
					 | 
				
			||||||
-19 -50 0 -10 12 -45 14 -193 14 -128 0 -184 -4 -189 -12 -6 -9 -8 -9 -8 1 0
 | 
					 | 
				
			||||||
7 -13 12 -30 12 -16 0 -30 -4 -30 -10 0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 4
 | 
					 | 
				
			||||||
7 -12 10 -44 9 -28 -1 -51 -5 -51 -9 0 -4 10 -19 21 -34 30 -38 39 -87 23
 | 
					 | 
				
			||||||
-122 -11 -26 -12 -27 -13 -6 -1 28 -81 108 -91 91 -6 -8 -10 -8 -16 2 -4 7 -3
 | 
					 | 
				
			||||||
14 3 16 6 2 -2 17 -18 34 -26 27 -33 29 -104 29 -61 0 -75 -3 -75 -15 0 -9 -7
 | 
					 | 
				
			||||||
-27 -16 -41 l-17 -25 -12 23 c-6 13 -8 28 -3 34 14 19 -4 24 -89 24 l-83 0 0
 | 
					 | 
				
			||||||
-222z m2395 192 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4
 | 
					 | 
				
			||||||
11 -10z m-107 -33 c-2 -11 -9 -12 -28 -5 -24 9 -24 10 -6 24 22 15 40 6 34
 | 
					 | 
				
			||||||
-19z m-1928 -52 c0 -5 -11 -23 -25 -40 l-24 -30 -24 26 -25 26 21 48 21 47 28
 | 
					 | 
				
			||||||
-33 c15 -19 28 -38 28 -44z m1139 13 c-5 -10 -14 -18 -19 -18 -16 0 -23 49 -9
 | 
					 | 
				
			||||||
66 11 14 14 13 25 -8 8 -15 9 -29 3 -40z m186 42 c3 -5 1 -10 -4 -10 -6 0 -11
 | 
					 | 
				
			||||||
5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m-1066 -9 c11 -7 10 -11 -5 -19 -18
 | 
					 | 
				
			||||||
-10 -16 -14 19 -47 35 -33 63 -82 53 -92 -2 -2 -25 17 -50 42 -54 52 -72 90
 | 
					 | 
				
			||||||
-55 111 13 16 19 17 38 5z m106 -1 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6
 | 
					 | 
				
			||||||
2 10 4 10 3 0 8 -4 11 -10z m169 -17 c9 -22 -12 -43 -23 -24 -10 15 -4 41 9
 | 
					 | 
				
			||||||
41 4 0 11 -8 14 -17z m391 7 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4
 | 
					 | 
				
			||||||
10 3 0 8 -4 11 -10z m465 -30 c0 -13 -5 -18 -15 -14 -8 4 -15 12 -15 20 0 8 7
 | 
					 | 
				
			||||||
14 15 14 8 0 15 -9 15 -20z m-1573 -22 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3
 | 
					 | 
				
			||||||
-3 4 -12 1 -19z m592 13 c17 -11 7 -41 -14 -41 -9 0 -15 9 -15 25 0 27 6 30
 | 
					 | 
				
			||||||
29 16z m881 -1 c0 -5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10
 | 
					 | 
				
			||||||
-4 10 -10z m-1541 -31 c33 -32 37 -44 21 -54 -6 -3 -10 4 -10 17 l0 23 -18
 | 
					 | 
				
			||||||
-23 -18 -24 -18 27 c-16 25 -14 65 4 65 5 0 22 -14 39 -31z m1131 11 c0 -5 -4
 | 
					 | 
				
			||||||
-10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-300 -20 c0
 | 
					 | 
				
			||||||
-5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m250
 | 
					 | 
				
			||||||
-10 c0 -11 -4 -20 -10 -20 -5 0 -10 9 -10 20 0 11 5 20 10 20 6 0 10 -9 10
 | 
					 | 
				
			||||||
-20z m116 -32 l2 -42 -19 23 c-14 17 -16 27 -9 42 17 30 25 22 26 -23z m179
 | 
					 | 
				
			||||||
22 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z
 | 
					 | 
				
			||||||
m-891 -16 c4 -9 1 -23 -6 -31 -10 -12 -13 -12 -20 1 -9 15 -1 46 12 46 4 0 11
 | 
					 | 
				
			||||||
-7 14 -16z m-106 -35 c-2 -6 -8 -10 -13 -10 -5 0 -11 4 -13 10 -2 6 4 11 13
 | 
					 | 
				
			||||||
11 9 0 15 -5 13 -11z m719 -44 c-4 -53 4 -77 18 -55 10 16 25 1 25 -24 0 -27
 | 
					 | 
				
			||||||
-44 -71 -60 -61 -6 4 -10 17 -8 30 1 12 -4 35 -12 49 -13 25 -12 31 9 72 13
 | 
					 | 
				
			||||||
24 25 44 27 44 3 0 3 -25 1 -55z m-167 29 c0 -8 -5 -12 -10 -9 -6 4 -8 11 -5
 | 
					 | 
				
			||||||
16 9 14 15 11 15 -7z m560 -30 c0 -8 -4 -14 -10 -14 -5 0 -10 9 -10 21 0 11 5
 | 
					 | 
				
			||||||
17 10 14 6 -3 10 -13 10 -21z m270 -46 c0 -23 -12 -23 -34 1 -12 13 -15 27
 | 
					 | 
				
			||||||
-11 41 l7 21 19 -22 c10 -12 19 -30 19 -41z m-1830 18 c-13 -13 -35 7 -25 24
 | 
					 | 
				
			||||||
5 8 11 8 21 -1 10 -8 12 -15 4 -23z m968 4 c9 -16 23 -30 30 -30 13 0 15 -10
 | 
					 | 
				
			||||||
6 -34 -6 -15 -8 -15 -19 1 -11 15 -13 15 -20 -6 -5 -18 -9 -20 -21 -10 -29 24
 | 
					 | 
				
			||||||
-36 48 -22 79 16 38 25 38 46 0z m-362 -88 c-6 -5 -56 44 -56 54 0 3 8 16 17
 | 
					 | 
				
			||||||
31 l17 26 14 -53 c7 -29 11 -55 8 -58z m257 72 c-7 -19 -23 -7 -23 18 0 17 3
 | 
					 | 
				
			||||||
18 14 8 8 -6 12 -18 9 -26z m-673 16 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3
 | 
					 | 
				
			||||||
6 -1 10 4 10 6 0 11 -4 11 -10z m1060 0 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10
 | 
					 | 
				
			||||||
-3 6 -1 10 4 10 6 0 11 -4 11 -10z m58 -35 c-8 -17 -15 -22 -21 -16 -7 7 -6
 | 
					 | 
				
			||||||
15 3 26 20 24 31 18 18 -10z m606 9 c3 -8 2 -12 -4 -9 -6 3 -10 10 -10 16 0
 | 
					 | 
				
			||||||
14 7 11 14 -7z m-1469 -14 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4
 | 
					 | 
				
			||||||
10 3 0 8 -4 11 -10z m-395 -21 c0 -5 -4 -9 -10 -9 -5 0 -10 7 -10 16 0 8 5 12
 | 
					 | 
				
			||||||
10 9 6 -3 10 -10 10 -16z m1044 5 c3 -8 2 -12 -4 -9 -6 3 -10 10 -10 16 0 14
 | 
					 | 
				
			||||||
7 11 14 -7z m-602 -36 c-9 -9 -12 -7 -12 12 0 19 3 21 12 12 9 -9 9 -15 0 -24z
 | 
					 | 
				
			||||||
m-432 8 c0 -2 3 -12 7 -21 5 -14 2 -16 -18 -10 -31 9 -32 11 -24 24 6 10 35
 | 
					 | 
				
			||||||
16 35 7z m111 -68 c24 -29 23 -37 -5 -71 l-22 -28 -2 61 c0 33 1 60 4 60 3 0
 | 
					 | 
				
			||||||
14 -10 25 -22z m73 -14 c3 -8 2 -12 -4 -9 -6 3 -10 10 -10 16 0 14 7 11 14 -7z
 | 
					 | 
				
			||||||
m-185 -94 c17 -22 31 -44 31 -49 0 -6 4 -11 10 -11 5 0 -2 -12 -15 -26 -14
 | 
					 | 
				
			||||||
-15 -25 -31 -25 -35 0 -5 -7 -6 -15 -3 -8 3 -13 9 -12 12 1 4 -4 22 -12 40
 | 
					 | 
				
			||||||
-10 24 -11 35 -2 46 8 10 6 19 -9 39 -25 31 -25 36 -1 30 11 -3 33 -22 50 -43z
 | 
					 | 
				
			||||||
m150 -30 c0 -44 0 -45 -14 -19 -8 14 -13 39 -11 55 5 41 5 41 16 24 5 -8 10
 | 
					 | 
				
			||||||
-35 9 -60z m-54 20 c-3 -5 -11 -10 -16 -10 -6 0 -7 5 -4 10 3 6 11 10 16 10 6
 | 
					 | 
				
			||||||
0 7 -4 4 -10z m35 -115 c11 7 29 -26 23 -42 -2 -6 1 -14 6 -18 21 -12 11 -25
 | 
					 | 
				
			||||||
-39 -49 -44 -21 -70 -21 -56 2 2 4 8 35 11 67 9 74 13 82 32 56 8 -12 18 -19
 | 
					 | 
				
			||||||
23 -16z m-80 -35 c0 -5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0
 | 
					 | 
				
			||||||
10 -4 10 -10z m237 -17 c8 -12 12 -27 8 -33 -8 -13 -65 26 -65 45 0 22 41 13
 | 
					 | 
				
			||||||
57 -12z m-56 -33 c18 -10 26 -49 13 -63 -6 -5 -64 55 -64 67 0 9 29 7 51 -4z
 | 
					 | 
				
			||||||
m-78 -66 c-4 -11 -9 -12 -19 -4 -7 7 -10 18 -7 26 4 11 9 12 19 4 7 -7 10 -18
 | 
					 | 
				
			||||||
7 -26z m49 -46 c-15 -15 -27 14 -17 40 6 15 8 15 18 -6 7 -16 7 -26 -1 -34z
 | 
					 | 
				
			||||||
m87 -90 c5 -16 6 -28 1 -28 -17 0 -60 63 -60 88 l0 27 25 -30 c13 -16 29 -42
 | 
					 | 
				
			||||||
34 -57z m-235 -89 c-10 -16 -24 -9 -24 12 0 18 2 19 15 9 8 -7 12 -16 9 -21z
 | 
					 | 
				
			||||||
m-37 -65 c-1 -2 -13 6 -27 16 -23 17 -23 20 -10 45 l14 26 13 -43 c6 -23 11
 | 
					 | 
				
			||||||
-43 10 -44z m284 23 c-8 -8 -11 -3 -11 19 1 25 2 27 11 11 8 -14 8 -22 0 -30z
 | 
					 | 
				
			||||||
m-201 -8 c0 -6 -4 -7 -10 -4 -5 3 -10 11 -10 16 0 6 5 7 10 4 6 -3 10 -11 10
 | 
					 | 
				
			||||||
-16z m45 -59 c3 -11 11 -20 17 -20 7 0 7 -4 0 -13 -6 -7 -9 -23 -6 -34 3 -12
 | 
					 | 
				
			||||||
1 -30 -4 -40 -10 -17 -11 -17 -17 2 -11 33 -20 48 -30 46 -18 -4 -23 43 -7 62
 | 
					 | 
				
			||||||
20 23 39 22 47 -3z m167 -37 c-4 -24 -6 -25 -17 -9 -7 9 -11 26 -8 37 8 30 31
 | 
					 | 
				
			||||||
5 25 -28z m-102 -5 c0 -19 -2 -20 -10 -8 -13 19 -13 30 0 30 6 0 10 -10 10
 | 
					 | 
				
			||||||
-22z m-257 -49 c3 -14 1 -32 -3 -39 -7 -11 -12 -7 -21 13 -9 21 -9 30 1 42 15
 | 
					 | 
				
			||||||
19 17 17 23 -16z m157 -59 c0 -25 -4 -30 -25 -30 -31 0 -31 7 -3 37 27 29 28
 | 
					 | 
				
			||||||
29 28 -7z"/>
 | 
					 | 
				
			||||||
<path d="M2695 2824 c-44 -25 -82 -48 -85 -52 -3 -4 -11 -8 -19 -10 -7 -1 -38
 | 
					 | 
				
			||||||
-19 -70 -39 -31 -20 -60 -38 -66 -39 -5 -1 -57 -29 -113 -63 -57 -34 -118 -70
 | 
					 | 
				
			||||||
-135 -79 -18 -9 -78 -44 -134 -76 l-101 -59 18 -35 c14 -25 16 -40 9 -53 -23
 | 
					 | 
				
			||||||
-43 -42 -35 -64 26 l-13 38 -24 -24 c-13 -13 -33 -24 -43 -24 -13 0 -20 -7
 | 
					 | 
				
			||||||
-21 -20 -2 -37 -5 -40 -17 -23 -12 14 -25 9 -140 -55 l-127 -72 2 -47 c1 -55
 | 
					 | 
				
			||||||
-23 -98 -32 -58 -7 31 -26 36 -27 8 -1 -60 -3 -68 -21 -68 -26 0 -44 41 -26
 | 
					 | 
				
			||||||
58 8 7 14 22 14 32 0 19 -4 18 -57 -11 -32 -18 -64 -39 -70 -47 -7 -9 -13 -12
 | 
					 | 
				
			||||||
-13 -8 0 4 -13 0 -29 -10 -18 -11 -31 -29 -37 -53 -6 -25 -15 -36 -26 -35 -11
 | 
					 | 
				
			||||||
1 -21 -10 -29 -33 l-11 -35 -27 26 c-30 28 -53 33 -69 13 -12 -14 11 -52 31
 | 
					 | 
				
			||||||
-52 7 0 13 -12 15 -27 3 -22 -1 -28 -16 -28 -20 0 -39 16 -42 35 -3 23 -12 55
 | 
					 | 
				
			||||||
-17 55 -13 0 -133 -76 -133 -85 0 -6 -10 -15 -22 -20 -12 -6 -25 -21 -29 -35
 | 
					 | 
				
			||||||
-4 -14 -7 -18 -8 -8 -2 27 -19 31 -51 11 -28 -16 -35 -26 -31 -47 1 -4 -9 -6
 | 
					 | 
				
			||||||
-23 -2 -14 3 -30 1 -38 -6 -8 -7 -49 -31 -92 -53 -43 -22 -89 -50 -102 -62
 | 
					 | 
				
			||||||
-13 -12 -24 -20 -24 -17 0 3 -8 1 -17 -5 -10 -5 -18 -19 -18 -32 0 -12 -6 -24
 | 
					 | 
				
			||||||
-12 -26 -7 -3 -13 2 -13 11 0 21 -4 20 -61 -10 -40 -22 -48 -31 -43 -49 4 -17
 | 
					 | 
				
			||||||
-6 -31 -47 -66 -28 -24 -60 -50 -70 -57 -16 -11 -19 -27 -19 -108 0 -81 3 -96
 | 
					 | 
				
			||||||
18 -101 64 -23 134 -105 116 -135 -4 -6 4 -4 17 3 26 13 253 144 309 178 19
 | 
					 | 
				
			||||||
11 46 26 60 33 14 7 27 15 30 18 3 3 26 16 52 30 83 43 83 44 59 83 -11 19
 | 
					 | 
				
			||||||
-21 45 -21 58 0 24 37 95 45 87 10 -10 42 -118 35 -118 -5 0 -13 -14 -19 -30
 | 
					 | 
				
			||||||
l-10 -31 42 26 c23 14 95 55 160 92 109 63 117 70 112 94 -3 14 -2 36 4 50 l9
 | 
					 | 
				
			||||||
24 7 -30 c13 -58 12 -57 57 -29 24 15 45 28 47 30 2 2 -4 20 -15 39 -17 31
 | 
					 | 
				
			||||||
-17 37 -4 45 22 14 27 12 27 -7 0 -10 1 -31 2 -48 2 -29 2 -30 19 -10 9 11 23
 | 
					 | 
				
			||||||
20 30 21 29 2 33 5 30 22 -2 13 3 17 25 15 34 -2 34 7 1 38 -42 39 -55 71 -43
 | 
					 | 
				
			||||||
112 5 19 9 39 8 44 -7 40 26 44 70 8 24 -21 28 -30 22 -47 -6 -16 0 -33 22
 | 
					 | 
				
			||||||
-64 l30 -43 36 24 c20 14 52 32 71 42 19 9 63 34 98 55 34 21 66 38 71 38 4 0
 | 
					 | 
				
			||||||
5 17 2 38 -4 20 -4 55 0 77 11 60 1 99 -38 145 -31 36 -35 46 -29 78 6 40 28
 | 
					 | 
				
			||||||
62 62 62 18 0 31 -21 80 -130 33 -71 65 -130 72 -130 6 0 12 -9 12 -20 0 -23
 | 
					 | 
				
			||||||
8 -25 32 -6 14 10 15 15 5 26 -10 10 -8 10 10 1 18 -9 24 -8 32 7 7 12 6 25
 | 
					 | 
				
			||||||
-2 39 -47 81 -48 83 -34 83 25 0 65 -39 72 -70 7 -33 8 -33 107 23 36 21 69
 | 
					 | 
				
			||||||
45 72 53 3 8 11 12 17 9 5 -4 29 6 52 21 23 16 44 29 47 29 3 0 28 14 55 29
 | 
					 | 
				
			||||||
28 16 67 39 88 51 20 12 35 25 32 28 -3 4 -2 5 1 2 9 -6 84 34 84 45 0 6 7 10
 | 
					 | 
				
			||||||
16 10 8 0 35 12 60 26 36 21 44 31 44 54 0 16 -11 42 -25 59 l-26 30 23 3 c20
 | 
					 | 
				
			||||||
3 23 10 26 50 2 38 0 47 -11 42 -9 -3 -16 3 -19 14 -6 23 9 55 23 47 5 -4 9
 | 
					 | 
				
			||||||
38 9 104 0 61 -3 111 -7 111 -5 0 -44 -21 -88 -46z m33 -44 c12 -12 22 -23 22
 | 
					 | 
				
			||||||
-25 0 -11 -24 -55 -30 -55 -3 0 -11 23 -16 50 -12 55 -8 60 24 30z m42 -70 c0
 | 
					 | 
				
			||||||
-5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-150
 | 
					 | 
				
			||||||
-52 c0 -16 -3 -19 -11 -11 -6 6 -8 16 -5 22 11 17 16 13 16 -11z m-97 -14 c-7
 | 
					 | 
				
			||||||
-18 -19 -18 -26 0 -7 18 1 26 18 19 8 -2 11 -11 8 -19z m15 -100 c12 -22 -9
 | 
					 | 
				
			||||||
-79 -33 -88 -10 -4 -13 12 -13 70 0 72 1 75 18 57 9 -11 22 -28 28 -39z m-316
 | 
					 | 
				
			||||||
-26 c-7 -7 -12 -8 -12 -2 0 6 3 14 7 17 3 4 9 5 12 2 2 -3 -1 -11 -7 -17z m20
 | 
					 | 
				
			||||||
-93 c0 -5 -5 -11 -11 -13 -6 -2 -11 4 -11 13 0 9 5 15 11 13 6 -2 11 -8 11
 | 
					 | 
				
			||||||
-13z m-212 -20 c0 -8 -2 -15 -4 -15 -2 0 -6 7 -10 15 -3 8 -1 15 4 15 6 0 10
 | 
					 | 
				
			||||||
-7 10 -15z m173 -31 c22 -22 24 -29 15 -50 -13 -27 -25 -30 -51 -10 -14 11
 | 
					 | 
				
			||||||
-17 22 -12 50 9 43 14 44 48 10z m-113 -19 c0 -9 -6 -12 -16 -8 -13 5 -13 7
 | 
					 | 
				
			||||||
-2 14 17 11 18 11 18 -6z m48 -25 c-1 -16 -6 -30 -10 -30 -9 0 -10 22 -2 44
 | 
					 | 
				
			||||||
10 25 15 19 12 -14z m182 -20 c13 -31 6 -100 -10 -100 -14 0 -40 43 -40 67 0
 | 
					 | 
				
			||||||
31 18 75 29 69 4 -3 14 -19 21 -36z m-390 -20 c0 -5 -2 -10 -4 -10 -3 0 -8 5
 | 
					 | 
				
			||||||
-11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m66 -57 c-10 -11 -25 18 -18 35 4
 | 
					 | 
				
			||||||
13 8 11 15 -6 6 -12 7 -25 3 -29z m-226 7 c0 -5 -4 -10 -10 -10 -5 0 -10 5
 | 
					 | 
				
			||||||
-10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m483 -47 c-5 -12 -7 -12 -14 -1 -5 7
 | 
					 | 
				
			||||||
-9 19 -9 27 0 11 3 11 14 1 8 -6 12 -18 9 -27z m207 16 c0 -5 -4 -9 -10 -9 -5
 | 
					 | 
				
			||||||
0 -10 7 -10 16 0 8 5 12 10 9 6 -3 10 -10 10 -16z m-123 -11 c-3 -8 -6 -5 -6
 | 
					 | 
				
			||||||
6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m-617 -15 c0 -21 -3 -24 -9 -14 -5 8 -7
 | 
					 | 
				
			||||||
20 -4 28 8 21 13 15 13 -14z m383 -22 c3 -11 2 -22 -1 -25 -9 -10 -31 15 -25
 | 
					 | 
				
			||||||
30 7 20 20 17 26 -5z m-113 -27 c0 -8 -4 -12 -10 -9 -5 3 -10 10 -10 16 0 5 5
 | 
					 | 
				
			||||||
9 10 9 6 0 10 -7 10 -16z m-273 -66 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4
 | 
					 | 
				
			||||||
-12 1 -19z m296 -22 c3 -8 0 -22 -7 -32 -13 -17 -14 -16 -20 4 -11 34 15 60
 | 
					 | 
				
			||||||
27 28z m-643 -46 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0
 | 
					 | 
				
			||||||
11 -4 11 -10z m178 -23 c13 -15 -1 -42 -17 -33 -13 8 -15 46 -2 46 5 0 13 -6
 | 
					 | 
				
			||||||
19 -13z m93 -20 c-1 -12 -15 -9 -19 4 -3 6 1 10 8 8 6 -3 11 -8 11 -12z m108
 | 
					 | 
				
			||||||
-29 c5 -13 16 -39 25 -59 l15 -37 -27 3 c-35 4 -72 16 -72 23 0 7 43 92 47 92
 | 
					 | 
				
			||||||
2 0 7 -10 12 -22z m-159 -29 c0 -18 -18 -9 -23 12 -4 16 -3 18 9 8 8 -6 14
 | 
					 | 
				
			||||||
-15 14 -20z m-95 11 c11 -17 -5 -32 -21 -19 -7 6 -11 15 -8 20 7 12 21 11 29
 | 
					 | 
				
			||||||
-1z m-128 -22 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m-31 -58
 | 
					 | 
				
			||||||
c2 -35 -6 -38 -23 -10 -11 16 -10 21 5 29 9 6 17 11 17 11 0 0 1 -13 1 -30z
 | 
					 | 
				
			||||||
m-306 -26 c0 -8 -4 -12 -10 -9 -5 3 -10 13 -10 21 0 8 5 12 10 9 6 -3 10 -13
 | 
					 | 
				
			||||||
10 -21z m573 -21 c-3 -7 -9 -13 -13 -13 -14 0 -18 24 -7 45 10 19 10 19 17 0
 | 
					 | 
				
			||||||
4 -11 5 -26 3 -32z m-337 -56 c-4 -18 1 -38 12 -55 l17 -27 -33 23 c-40 29
 | 
					 | 
				
			||||||
-64 72 -51 93 5 8 9 25 9 38 0 24 1 23 26 -10 18 -25 24 -42 20 -62z m124 57
 | 
					 | 
				
			||||||
c0 -24 -17 -15 -22 11 -4 20 -3 23 8 14 8 -6 14 -18 14 -25z m310 -8 c-15 -15
 | 
					 | 
				
			||||||
-32 7 -24 29 7 17 8 17 21 -1 9 -12 10 -21 3 -28z m-725 -12 c-3 -29 -2 -34 9
 | 
					 | 
				
			||||||
-25 11 9 15 5 18 -23 4 -33 3 -34 -13 -20 -36 33 -49 104 -19 104 6 0 8 -15 5
 | 
					 | 
				
			||||||
-36z m107 14 c6 -17 -1 -38 -13 -38 -5 0 -9 11 -9 25 0 26 14 34 22 13z m64
 | 
					 | 
				
			||||||
-53 c4 -8 1 -22 -6 -30 -9 -11 -9 -22 1 -49 11 -31 10 -41 -5 -72 l-17 -37
 | 
					 | 
				
			||||||
-24 34 c-14 18 -25 43 -25 55 0 24 30 84 43 84 4 0 7 7 7 15 0 20 19 19 26 0z
 | 
					 | 
				
			||||||
m189 6 c3 -5 1 -12 -4 -15 -5 -3 -11 1 -15 9 -6 16 9 21 19 6z m-515 -31 c0
 | 
					 | 
				
			||||||
-5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m-45 -40
 | 
					 | 
				
			||||||
c7 -23 -2 -43 -16 -35 -12 8 -12 55 0 55 5 0 12 -9 16 -20z m205 -15 c0 -18
 | 
					 | 
				
			||||||
-4 -23 -15 -19 -15 6 -20 35 -9 47 12 11 24 -3 24 -28z m-90 -32 l-1 -58 -19
 | 
					 | 
				
			||||||
24 c-23 27 -25 44 -8 71 21 33 28 23 28 -37z m-231 -20 c1 -26 0 -45 -1 -42
 | 
					 | 
				
			||||||
-2 2 -11 13 -20 25 -16 18 -16 22 -2 42 8 12 16 22 18 22 1 0 4 -21 5 -47z
 | 
					 | 
				
			||||||
m290 1 c21 -27 36 -63 28 -70 -10 -10 -39 10 -52 37 -29 56 -13 79 24 33z m86
 | 
					 | 
				
			||||||
-4 c-5 -8 -11 -8 -17 -2 -6 6 -7 16 -3 22 5 8 11 8 17 2 6 -6 7 -16 3 -22z
 | 
					 | 
				
			||||||
m163 -52 c-7 -19 -21 -12 -23 9 -1 18 1 20 12 11 8 -6 12 -15 11 -20z m-113
 | 
					 | 
				
			||||||
-8 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m-42
 | 
					 | 
				
			||||||
-46 c-4 -11 -9 -12 -19 -4 -7 7 -10 18 -7 26 4 11 9 12 19 4 7 -7 10 -18 7
 | 
					 | 
				
			||||||
-26z m-453 -28 c0 -6 -8 -20 -16 -31 l-15 -20 -15 20 c-8 11 -20 20 -27 20 -6
 | 
					 | 
				
			||||||
0 -13 8 -15 19 -2 11 0 16 7 12 5 -4 15 3 21 15 l12 21 24 -23 c13 -12 24 -27
 | 
					 | 
				
			||||||
24 -33z m344 9 c-6 -15 -8 -15 -16 -2 -4 8 -5 23 -2 32 6 15 8 15 16 2 4 -8 5
 | 
					 | 
				
			||||||
-23 2 -32z m-152 -44 c-5 -3 -12 -13 -16 -21 -3 -8 -4 -4 -2 9 3 14 1 30 -4
 | 
					 | 
				
			||||||
36 -6 7 -3 20 8 36 l17 24 3 -39 c2 -21 -1 -42 -6 -45z m308 -1 c0 -5 -2 -10
 | 
					 | 
				
			||||||
-4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-655 -17 c13 -12
 | 
					 | 
				
			||||||
19 -13 24 -4 14 23 6 -53 -8 -79 -13 -23 -14 -23 -21 -5 -10 27 -21 105 -16
 | 
					 | 
				
			||||||
105 3 0 12 -7 21 -17z m239 -19 c-6 -3 -10 -18 -8 -34 4 -32 -6 -39 -24 -17
 | 
					 | 
				
			||||||
-13 15 11 58 31 56 8 0 9 -2 1 -5z m50 -20 c3 -8 2 -12 -4 -9 -6 3 -10 10 -10
 | 
					 | 
				
			||||||
16 0 14 7 11 14 -7z m-71 -86 c-19 -33 -40 -39 -35 -10 4 18 29 42 45 42 5 0
 | 
					 | 
				
			||||||
1 -14 -10 -32z m-198 -102 c7 3 27 -7 44 -23 l31 -28 -26 -23 -27 -23 6 30 c6
 | 
					 | 
				
			||||||
28 5 30 -16 20 -19 -9 -22 -17 -19 -47 l3 -37 -25 40 c-25 40 -26 65 -3 94 7
 | 
					 | 
				
			||||||
8 13 9 15 3 2 -6 9 -9 17 -6z m-125 -46 c0 -22 -16 -35 -25 -20 -9 14 4 52 16
 | 
					 | 
				
			||||||
45 5 -4 9 -15 9 -25z"/>
 | 
					 | 
				
			||||||
<path d="M2720 2219 c-68 -38 -62 -31 -62 -73 0 -41 -17 -47 -46 -16 l-19 20
 | 
					 | 
				
			||||||
-24 -22 c-14 -13 -29 -23 -34 -23 -22 0 -185 -101 -181 -112 3 -7 -1 -10 -9
 | 
					 | 
				
			||||||
-6 -11 4 -109 -42 -133 -62 -1 -1 7 -23 19 -49 31 -70 24 -211 -12 -250 -9
 | 
					 | 
				
			||||||
-11 -29 34 -29 67 0 11 -9 31 -21 43 -14 15 -19 29 -14 43 3 12 11 19 16 15 5
 | 
					 | 
				
			||||||
-3 7 -9 5 -13 -3 -4 0 -13 5 -21 7 -11 9 -5 8 20 -1 19 1 59 5 88 l8 53 -24
 | 
					 | 
				
			||||||
-14 c-24 -15 -366 -215 -428 -249 -221 -125 -349 -202 -344 -209 3 -5 2 -14
 | 
					 | 
				
			||||||
-2 -21 -5 -8 -9 -7 -13 5 -6 15 -9 15 -36 -3 -19 -13 -24 -20 -14 -20 31 0 40
 | 
					 | 
				
			||||||
-24 20 -54 l-19 -28 -12 36 c-13 41 -21 43 -58 18 -15 -10 -35 -22 -45 -25
 | 
					 | 
				
			||||||
-11 -4 -21 -16 -24 -28 -4 -14 -8 -17 -13 -8 -6 8 -25 1 -71 -26 -77 -46 -100
 | 
					 | 
				
			||||||
-65 -93 -76 3 -5 0 -9 -5 -9 -6 0 -11 5 -11 11 0 5 -5 7 -10 4 -6 -4 -8 -11
 | 
					 | 
				
			||||||
-5 -16 11 -16 -5 -31 -21 -18 -11 9 -54 -12 -203 -97 -180 -103 -191 -111
 | 
					 | 
				
			||||||
-191 -139 -1 -25 -2 -27 -11 -12 -14 24 -24 22 -83 -15 -28 -17 -67 -40 -86
 | 
					 | 
				
			||||||
-50 -129 -73 -180 -103 -180 -108 0 -4 17 -16 38 -28 l39 -22 13 25 c16 29 27
 | 
					 | 
				
			||||||
32 27 7 0 -51 5 -61 39 -81 19 -12 36 -21 37 -21 1 0 11 15 21 34 13 24 23 33
 | 
					 | 
				
			||||||
34 29 11 -5 14 -2 9 10 -3 9 -3 17 2 17 8 0 12 -18 28 -118 5 -29 15 -39 87
 | 
					 | 
				
			||||||
-80 44 -26 84 -46 89 -44 4 1 7 -3 7 -8 0 -6 12 -16 28 -23 15 -6 49 -27 77
 | 
					 | 
				
			||||||
-44 27 -18 52 -33 56 -33 3 0 14 18 24 40 10 22 22 40 27 40 18 0 47 -82 38
 | 
					 | 
				
			||||||
-106 -8 -20 -4 -26 26 -45 34 -21 36 -21 49 -3 13 18 14 18 34 0 12 -11 20
 | 
					 | 
				
			||||||
-28 19 -38 -2 -14 47 -47 212 -146 118 -71 216 -128 218 -126 2 1 -2 11 -9 22
 | 
					 | 
				
			||||||
-11 17 -10 24 2 38 13 16 14 14 19 -19 l5 -36 74 44 c40 25 74 45 76 45 1 0
 | 
					 | 
				
			||||||
29 16 61 35 50 30 59 40 60 66 1 16 10 39 20 50 18 20 18 20 31 -6 8 -14 16
 | 
					 | 
				
			||||||
-25 19 -25 3 0 25 11 50 24 l45 24 -25 18 c-19 15 -24 25 -19 46 6 30 22 35
 | 
					 | 
				
			||||||
55 17 15 -8 19 -17 14 -39 -5 -28 -2 -27 124 48 71 42 130 83 130 90 0 6 10
 | 
					 | 
				
			||||||
12 22 12 16 0 85 36 127 67 2 1 -2 9 -8 17 -7 8 -9 20 -5 27 5 8 12 5 23 -9
 | 
					 | 
				
			||||||
16 -21 17 -21 209 94 l192 116 0 244 c0 134 -3 244 -6 244 -3 0 -46 -24 -97
 | 
					 | 
				
			||||||
-54 -51 -29 -112 -65 -137 -78 -119 -66 -220 -130 -216 -137 3 -4 1 -13 -4
 | 
					 | 
				
			||||||
-21 -7 -12 -10 -12 -15 2 -6 16 -9 16 -30 2 -13 -8 -29 -28 -35 -45 -11 -26
 | 
					 | 
				
			||||||
-14 -28 -25 -12 -11 15 -20 12 -96 -32 -134 -78 -127 -70 -85 -97 46 -30 60
 | 
					 | 
				
			||||||
-64 49 -119 l-9 -44 -44 2 c-25 1 -47 -3 -51 -9 -15 -25 21 -68 78 -93 52 -23
 | 
					 | 
				
			||||||
55 -27 48 -52 -13 -56 -27 -59 -94 -19 -34 20 -64 36 -67 36 -21 0 -64 77 -64
 | 
					 | 
				
			||||||
114 0 7 19 19 43 27 61 23 62 50 1 88 l-48 29 -28 -19 c-15 -11 -28 -26 -28
 | 
					 | 
				
			||||||
-34 0 -7 -9 -23 -20 -36 l-19 -24 -1 23 c0 28 -3 27 -67 -7 -52 -28 -52 -28
 | 
					 | 
				
			||||||
-33 -46 22 -20 26 -55 8 -73 -20 -20 -28 -14 -28 21 0 59 -11 62 -78 21 -34
 | 
					 | 
				
			||||||
-20 -62 -38 -62 -41 0 -2 18 -31 40 -63 22 -32 40 -61 40 -65 0 -3 -17 5 -38
 | 
					 | 
				
			||||||
18 -20 13 -45 26 -55 30 -9 4 -26 22 -37 41 -16 26 -66 61 -215 150 -107 64
 | 
					 | 
				
			||||||
-195 118 -195 120 0 2 56 36 125 75 69 40 125 76 125 80 0 16 -46 47 -68 45
 | 
					 | 
				
			||||||
-16 -1 -21 3 -18 12 3 8 1 14 -4 14 -6 0 -10 -6 -10 -12 0 -18 -20 13 -20 31
 | 
					 | 
				
			||||||
0 10 3 11 14 2 11 -9 15 -5 18 24 2 19 0 41 -4 48 -6 9 -8 8 -8 -3 -1 -12 -3
 | 
					 | 
				
			||||||
-11 -14 3 -8 10 -11 27 -8 39 5 20 6 20 18 3 8 -10 16 -16 18 -14 10 9 8 86
 | 
					 | 
				
			||||||
-3 129 -16 66 -14 79 7 52 28 -37 101 -47 160 -23 72 29 75 29 100 -1 31 -36
 | 
					 | 
				
			||||||
23 -58 -23 -59 -19 -1 -39 -3 -45 -4 -5 -1 -14 -3 -18 -4 -12 -1 -14 -93 -3
 | 
					 | 
				
			||||||
-109 4 -8 24 -17 42 -21 26 -5 35 -13 39 -33 l5 -26 125 73 c92 54 128 80 137
 | 
					 | 
				
			||||||
101 8 19 12 23 13 12 0 -10 3 -18 6 -18 16 0 143 78 173 106 36 34 38 43 40
 | 
					 | 
				
			||||||
157 1 44 3 47 20 38 20 -11 39 -54 48 -106 3 -16 6 -31 7 -32 1 -1 59 32 129
 | 
					 | 
				
			||||||
72 70 41 146 85 170 98 113 64 167 99 167 107 0 6 7 10 16 10 27 0 75 33 69
 | 
					 | 
				
			||||||
48 -3 8 1 23 9 34 14 20 14 20 21 -1 3 -12 11 -21 17 -21 13 0 128 69 128 77
 | 
					 | 
				
			||||||
0 4 -3 3 -6 0 -8 -8 -39 30 -48 59 -4 12 -2 34 4 49 10 27 11 27 30 10 20 -18
 | 
					 | 
				
			||||||
20 -17 20 65 0 72 -2 81 -13 66 -13 -18 -15 -17 -34 8 -12 14 -28 26 -38 26
 | 
					 | 
				
			||||||
-9 0 -13 5 -10 10 4 6 11 8 16 5 5 -4 9 -2 9 3 0 19 33 22 51 6 18 -17 19 -14
 | 
					 | 
				
			||||||
19 79 0 54 -3 97 -7 97 -5 0 -33 -14 -63 -31z m-42 -220 c-2 -6 -8 -10 -13
 | 
					 | 
				
			||||||
-10 -5 0 -11 4 -13 10 -2 6 4 11 13 11 9 0 15 -5 13 -11z m-246 -46 c20 -18
 | 
					 | 
				
			||||||
24 -58 8 -68 -16 -10 -60 23 -55 40 3 8 7 22 10 30 6 19 14 19 37 -2z m248
 | 
					 | 
				
			||||||
-42 c0 -3 -8 -17 -17 -31 l-17 -24 -12 35 c-18 48 -17 49 16 38 17 -6 30 -14
 | 
					 | 
				
			||||||
30 -18z m-317 -36 c-7 -21 -13 -19 -13 6 0 11 4 18 10 14 5 -3 7 -12 3 -20z
 | 
					 | 
				
			||||||
m-183 -9 c0 -11 -19 -15 -25 -6 -3 5 1 10 9 10 9 0 16 -2 16 -4z m450 -40 c-8
 | 
					 | 
				
			||||||
-8 -13 -7 -20 4 -12 20 -1 33 16 19 11 -9 12 -15 4 -23z m-500 -6 c0 -10 -5
 | 
					 | 
				
			||||||
-22 -11 -25 -6 -5 -4 -11 6 -19 15 -11 15 -14 -4 -36 l-20 -25 -2 30 c-2 17
 | 
					 | 
				
			||||||
-3 39 -4 50 -2 27 14 57 26 50 5 -4 9 -15 9 -25z m-10 -120 c0 -5 -2 -10 -4
 | 
					 | 
				
			||||||
-10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-215 -10 c3 -5 1
 | 
					 | 
				
			||||||
-10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m152 -36 c7 3
 | 
					 | 
				
			||||||
15 1 17 -3 3 -5 18 -11 34 -14 24 -5 28 -11 30 -44 1 -21 5 -55 8 -76 5 -37 5
 | 
					 | 
				
			||||||
-38 -18 -26 -40 21 -128 82 -128 88 -1 3 -7 31 -14 62 l-14 57 36 -24 c20 -14
 | 
					 | 
				
			||||||
42 -22 49 -20z m433 6 c0 -5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10
 | 
					 | 
				
			||||||
6 0 10 -4 10 -10z m-690 -93 c0 -5 -5 -5 -10 -2 -6 4 -8 10 -5 15 3 5 -4 11
 | 
					 | 
				
			||||||
-15 15 -22 7 -26 35 -8 53 10 10 15 3 25 -30 7 -24 13 -47 13 -51z m121 -25
 | 
					 | 
				
			||||||
c15 -27 15 -31 -1 -61 l-16 -33 -17 26 c-9 14 -17 31 -17 38 0 9 -3 9 -10 -2
 | 
					 | 
				
			||||||
-5 -8 -10 -11 -10 -7 0 12 40 67 48 67 4 0 14 -13 23 -28z m270 -19 c-9 -17
 | 
					 | 
				
			||||||
-10 -17 -16 0 -4 9 -4 25 0 35 6 16 7 16 16 0 7 -12 7 -24 0 -35z m-505 1 c17
 | 
					 | 
				
			||||||
-16 24 -32 21 -45 -3 -10 0 -19 6 -20 7 0 2 -4 -11 -10 -25 -10 -72 13 -72 35
 | 
					 | 
				
			||||||
0 12 23 66 28 66 2 0 14 -12 28 -26z m73 -8 c7 -8 8 -17 3 -20 -6 -3 -12 3
 | 
					 | 
				
			||||||
-15 14 -6 24 -4 25 12 6z m491 -36 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6
 | 
					 | 
				
			||||||
-1 10 4 10 6 0 11 -4 11 -10z m-746 -36 c3 -8 2 -12 -4 -9 -6 3 -10 10 -10 16
 | 
					 | 
				
			||||||
0 14 7 11 14 -7z m-42 -56 c-9 -9 -12 -7 -12 12 0 19 3 21 12 12 9 -9 9 -15 0
 | 
					 | 
				
			||||||
-24z m405 10 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m-457 -59
 | 
					 | 
				
			||||||
c0 -10 -4 -19 -10 -19 -5 0 -10 12 -10 26 0 14 4 23 10 19 6 -3 10 -15 10 -26z
 | 
					 | 
				
			||||||
m203 16 c4 -8 2 -17 -2 -20 -5 -2 -11 4 -14 15 -6 23 8 27 16 5z m87 -84 c0
 | 
					 | 
				
			||||||
-11 5 -21 11 -23 5 -2 7 -11 3 -21 -5 -14 -13 -17 -33 -11 -14 4 -37 8 -51 9
 | 
					 | 
				
			||||||
-16 1 -26 9 -28 21 -3 15 -1 16 9 7 9 -10 18 -7 41 12 35 30 48 31 48 6z
 | 
					 | 
				
			||||||
m-485 -70 c-10 -16 -25 -3 -25 22 0 20 1 20 16 5 9 -9 13 -21 9 -27z m407 -88
 | 
					 | 
				
			||||||
c2 -3 -9 -7 -24 -9 -23 -2 -28 1 -29 19 0 12 -2 30 -4 40 -2 12 5 8 25 -13 16
 | 
					 | 
				
			||||||
-17 30 -34 32 -37z m-572 41 c0 -8 -5 -12 -10 -9 -6 4 -8 11 -5 16 9 14 15 11
 | 
					 | 
				
			||||||
15 -7z m-157 -46 c6 -42 -2 -49 -21 -18 -9 15 -10 26 -1 41 13 25 15 24 22
 | 
					 | 
				
			||||||
-23z m47 32 c0 -5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4
 | 
					 | 
				
			||||||
10 -10z m590 -36 c0 -8 -4 -14 -10 -14 -5 0 -10 9 -10 21 0 11 5 17 10 14 6
 | 
					 | 
				
			||||||
-3 10 -13 10 -21z m-537 -25 c-5 -5 -9 3 -11 19 -1 24 0 25 9 9 7 -11 7 -23 2
 | 
					 | 
				
			||||||
-28z m107 20 c0 -11 -4 -18 -10 -14 -5 3 -7 12 -3 20 7 21 13 19 13 -6z m1585
 | 
					 | 
				
			||||||
-32 c0 -26 -20 -25 -23 2 -3 16 1 22 10 19 7 -3 13 -12 13 -21z m-1897 -43 c1
 | 
					 | 
				
			||||||
-14 6 -33 11 -42 9 -18 4 -42 -8 -42 -10 0 -31 46 -31 68 0 14 17 42 25 42 0
 | 
					 | 
				
			||||||
0 2 -11 3 -26z m57 7 c3 -5 1 -12 -5 -16 -5 -3 -10 1 -10 9 0 18 6 21 15 7z
 | 
					 | 
				
			||||||
m187 -8 c10 -9 18 -23 18 -32 -1 -19 -29 -72 -30 -55 0 7 -9 14 -20 17 -14 3
 | 
					 | 
				
			||||||
-20 14 -20 32 0 19 4 26 14 22 10 -4 12 0 9 14 -6 23 4 24 29 2z m73 -32 c3
 | 
					 | 
				
			||||||
-5 1 -12 -5 -16 -5 -3 -10 1 -10 9 0 18 6 21 15 7z m1385 -67 c0 -9 -5 -12
 | 
					 | 
				
			||||||
-12 -8 -27 15 -38 27 -32 35 8 13 44 -9 44 -27z m-1552 -8 c7 1 8 0 3 -2 -6
 | 
					 | 
				
			||||||
-3 -8 -11 -6 -17 17 -56 15 -67 -7 -67 -48 1 -73 50 -52 105 6 14 9 14 28 -3
 | 
					 | 
				
			||||||
11 -11 27 -18 34 -16z m109 2 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1
 | 
					 | 
				
			||||||
-19z m105 -62 c-16 -30 -17 -30 -20 -8 -3 18 -7 21 -17 12 -10 -8 -14 -4 -18
 | 
					 | 
				
			||||||
16 -3 18 -1 22 6 15 18 -18 24 -12 19 22 l-4 32 25 -29 c25 -29 25 -30 9 -60z
 | 
					 | 
				
			||||||
m-515 58 c10 -28 -13 -37 -25 -9 -10 21 -9 25 4 25 8 0 17 -7 21 -16z m373
 | 
					 | 
				
			||||||
-49 c0 -8 -4 -17 -10 -20 -6 -4 -10 5 -10 20 0 15 4 24 10 20 6 -3 10 -12 10
 | 
					 | 
				
			||||||
-20z m-320 -16 c0 -24 -12 -24 -27 0 -10 15 -10 21 0 24 18 6 27 -2 27 -24z
 | 
					 | 
				
			||||||
m-110 -5 c0 -8 -5 -12 -10 -9 -6 4 -8 11 -5 16 9 14 15 11 15 -7z m220 12 c0
 | 
					 | 
				
			||||||
-3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z m1790
 | 
					 | 
				
			||||||
-38 c0 -30 0 -31 -20 -13 -19 17 -19 19 -2 31 23 18 22 18 22 -18z m-1950 -34
 | 
					 | 
				
			||||||
c0 -8 -5 -12 -10 -9 -6 4 -8 11 -5 16 9 14 15 11 15 -7z m100 6 c0 -5 -2 -10
 | 
					 | 
				
			||||||
-4 -10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m301 -16 c9 -11 8
 | 
					 | 
				
			||||||
-21 -1 -44 -16 -39 -26 -38 -33 3 -3 17 -8 40 -12 49 -5 13 -2 15 14 11 11 -3
 | 
					 | 
				
			||||||
26 -11 32 -19z m1259 -51 c0 -30 -20 -73 -35 -73 -6 0 -19 15 -27 34 -16 32
 | 
					 | 
				
			||||||
-16 35 9 71 l26 38 13 -24 c7 -13 13 -34 14 -46z m-1873 35 c-3 -8 -6 -5 -6 6
 | 
					 | 
				
			||||||
-1 11 2 17 5 13 3 -3 4 -12 1 -19z m2082 -63 c1 -50 -12 -59 -41 -28 -28 31
 | 
					 | 
				
			||||||
-29 47 -2 88 l18 28 13 -29 c7 -16 12 -42 12 -59z m-1686 38 c7 -43 3 -53 -24
 | 
					 | 
				
			||||||
-53 -10 0 -28 -9 -39 -20 -11 -11 -23 -20 -27 -20 -5 0 -9 -11 -10 -25 -1 -14
 | 
					 | 
				
			||||||
-8 -34 -16 -46 -14 -20 -16 -18 -27 38 -6 32 -12 64 -13 72 -2 14 115 89 139
 | 
					 | 
				
			||||||
90 6 1 14 -15 17 -36z m95 9 c14 -9 16 -159 1 -167 -9 -6 -59 56 -59 75 0 7 9
 | 
					 | 
				
			||||||
28 21 46 11 19 18 38 14 44 -7 12 5 13 23 2z m-535 -39 c-6 -14 -8 -14 -14 3
 | 
					 | 
				
			||||||
-12 30 -10 37 5 24 8 -6 12 -19 9 -27z m777 -88 c-6 -19 -13 -35 -15 -35 -3 0
 | 
					 | 
				
			||||||
-15 16 -29 36 -24 35 -24 38 -9 70 l16 34 24 -36 c22 -31 23 -39 13 -69z
 | 
					 | 
				
			||||||
m-313 53 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m385 -50 c-13
 | 
					 | 
				
			||||||
-13 -26 8 -17 30 6 16 7 16 17 -1 7 -13 7 -22 0 -29z m908 2 c0 -5 -2 -10 -4
 | 
					 | 
				
			||||||
-10 -3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m-845 -53 c17 -33 17
 | 
					 | 
				
			||||||
-35 -1 -65 -10 -18 -21 -32 -24 -31 -3 0 -15 13 -28 30 l-24 29 22 35 c12 19
 | 
					 | 
				
			||||||
25 35 29 35 4 0 16 -15 26 -33z m-265 -59 l0 -32 -16 23 c-14 19 -14 25 -3 32
 | 
					 | 
				
			||||||
19 12 19 13 19 -23z m-220 -19 c0 -6 -4 -7 -10 -4 -5 3 -10 11 -10 16 0 6 5 7
 | 
					 | 
				
			||||||
10 4 6 -3 10 -11 10 -16z m436 -96 c-11 -12 -27 13 -20 32 7 17 8 17 17 -3 6
 | 
					 | 
				
			||||||
-12 7 -25 3 -29z m-52 -44 c-8 -13 -24 -1 -24 18 0 13 3 13 15 3 8 -7 12 -16
 | 
					 | 
				
			||||||
9 -21z m196 16 c0 -8 -4 -15 -10 -15 -5 0 -7 7 -4 15 4 8 8 15 10 15 2 0 4 -7
 | 
					 | 
				
			||||||
4 -15z m-93 -61 c17 -4 -4 -54 -23 -54 -9 0 -34 48 -34 66 0 2 34 -4 57 -12z
 | 
					 | 
				
			||||||
m-57 -20 c0 -8 -4 -14 -10 -14 -5 0 -10 9 -10 21 0 11 5 17 10 14 6 -3 10 -13
 | 
					 | 
				
			||||||
10 -21z m60 -60 c0 -8 -5 -12 -10 -9 -6 4 -8 11 -5 16 9 14 15 11 15 -7z m103
 | 
					 | 
				
			||||||
-40 c18 -24 18 -25 -7 -55 l-26 -31 0 26 c0 14 0 27 0 29 -1 1 -5 19 -9 39 -8
 | 
					 | 
				
			||||||
32 -7 35 8 26 9 -5 24 -21 34 -34z m364 14 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13
 | 
					 | 
				
			||||||
3 -3 4 -12 1 -19z m-247 -69 c0 -20 -15 -26 -24 -10 -8 13 4 42 15 35 5 -3 9
 | 
					 | 
				
			||||||
-14 9 -25z m-63 -64 c0 -8 -4 -12 -9 -9 -5 3 -6 10 -3 15 9 13 12 11 12 -6z"/>
 | 
					 | 
				
			||||||
<path d="M210 982 c0 -45 3 -82 6 -82 5 0 68 35 83 46 3 2 -42 62 -81 109 -4
 | 
					 | 
				
			||||||
5 -8 -28 -8 -73z"/>
 | 
					 | 
				
			||||||
<path d="M1440 1001 c-7 -15 -7 -21 0 -21 12 0 25 28 17 36 -3 3 -10 -4 -17
 | 
					 | 
				
			||||||
-15z"/>
 | 
					 | 
				
			||||||
</g>
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 24 KiB  | 
@@ -1,19 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
    "name": "",
 | 
					 | 
				
			||||||
    "short_name": "",
 | 
					 | 
				
			||||||
    "icons": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "src": "/android-chrome-192x192.png",
 | 
					 | 
				
			||||||
            "sizes": "192x192",
 | 
					 | 
				
			||||||
            "type": "image/png"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "src": "/android-chrome-256x256.png",
 | 
					 | 
				
			||||||
            "sizes": "256x256",
 | 
					 | 
				
			||||||
            "type": "image/png"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "theme_color": "#ffffff",
 | 
					 | 
				
			||||||
    "background_color": "#ffffff",
 | 
					 | 
				
			||||||
    "display": "standalone"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								data/static/js/bootstrap.bundle.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										2
									
								
								data/static/js/jquery-3.3.1.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,16 +0,0 @@
 | 
				
			|||||||
const char helpCmd[] = "sysinfo     - System Info\r\n"
 | 
					 | 
				
			||||||
                       "netinfo     - WiFi Info\r\n"
 | 
					 | 
				
			||||||
                       "showdtc     - Show all DTCs\r\n"
 | 
					 | 
				
			||||||
                       "cleardtc    - Clear all DTCs\r\n"
 | 
					 | 
				
			||||||
                       "formatPDS   - Format Persistence EEPROM Data\r\n"
 | 
					 | 
				
			||||||
                       "formatCFG   - Format Configuration EEPROM Data\r\n"
 | 
					 | 
				
			||||||
                       "checkEE     - Check EEPROM with checksum\r\n"
 | 
					 | 
				
			||||||
                       "dumpEE1k    - dump the first 1kb of EEPROM to Serial\r\n"
 | 
					 | 
				
			||||||
                       "dumpEE      - dump the whole EPPROM to Serial\r\n"
 | 
					 | 
				
			||||||
                       "resetPageEE - Reset the PersistenceData Page\r\n"
 | 
					 | 
				
			||||||
                       "dumpCFG     - print Config struct\r\n"
 | 
					 | 
				
			||||||
                       "dumpPDS     - print PersistanceStruct\r\n"
 | 
					 | 
				
			||||||
                       "saveEE      - save EE-Data\r\n"
 | 
					 | 
				
			||||||
                       "bat_3s      - set BatteryType to 3S LiPo\r\n"
 | 
					 | 
				
			||||||
                       "bat_2s      - set BatteryType to 2S LiPo\r\n"
 | 
					 | 
				
			||||||
                       ;
 | 
					 | 
				
			||||||
@@ -1,5 +0,0 @@
 | 
				
			|||||||
[wifi_cred]
 | 
					 | 
				
			||||||
wifi_ap_password = ilovekggmorethangof
 | 
					 | 
				
			||||||
admin_password = i_am_dr.hiabuto
 | 
					 | 
				
			||||||
wifi_ssid = BND_Scanner_#42
 | 
					 | 
				
			||||||
wifi_password = 5xMYkerbLMdrsSdF3hpy5DM9
 | 
					 | 
				
			||||||