Compare commits
	
		
			18 Commits
		
	
	
		
			Release-1.
			...
			10c098ad33
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 10c098ad33 | |||
| 3c3feeb8b5 | |||
| 893a57707d | |||
| 790261b6b7 | |||
| 0b6c432c70 | |||
| c88b532978 | |||
| 27b8cb0166 | |||
| 56872ea856 | |||
| d3506fd479 | |||
| ba90e98565 | |||
| 7b2c853b0d | |||
| ed8f65b92e | |||
| 69e5249a9d | |||
| b9f3b39684 | |||
| 63f8c34d97 | |||
| d99216815a | |||
| e6b5c0e3f0 | |||
| 9aa266792d | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -3,3 +3,4 @@
 | 
				
			|||||||
.vscode/c_cpp_properties.json
 | 
					.vscode/c_cpp_properties.json
 | 
				
			||||||
.vscode/launch.json
 | 
					.vscode/launch.json
 | 
				
			||||||
.vscode/ipch
 | 
					.vscode/ipch
 | 
				
			||||||
 | 
					wifi_credentials.ini
 | 
				
			||||||
							
								
								
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -3,5 +3,8 @@
 | 
				
			|||||||
    // for the documentation about the extensions.json format
 | 
					    // for the documentation about the extensions.json format
 | 
				
			||||||
    "recommendations": [
 | 
					    "recommendations": [
 | 
				
			||||||
        "platformio.platformio-ide"
 | 
					        "platformio.platformio-ide"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "unwantedRecommendations": [
 | 
				
			||||||
 | 
					        "ms-vscode.cpptools-extension-pack"
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,151 +0,0 @@
 | 
				
			|||||||
html {
 | 
					 | 
				
			||||||
  font-family: sans-serif;
 | 
					 | 
				
			||||||
  line-height: 1.15;
 | 
					 | 
				
			||||||
  -ms-text-size-adjust: 100%;
 | 
					 | 
				
			||||||
  -webkit-text-size-adjust: 100%;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
body {
 | 
					 | 
				
			||||||
  margin: 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.container-fluid {
 | 
					 | 
				
			||||||
  position: relative;
 | 
					 | 
				
			||||||
  margin-left: auto;
 | 
					 | 
				
			||||||
  margin-right: auto;
 | 
					 | 
				
			||||||
  padding-right: 15px;
 | 
					 | 
				
			||||||
  padding-left: 15px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.row {
 | 
					 | 
				
			||||||
  display: -webkit-box;
 | 
					 | 
				
			||||||
  display: -webkit-flex;
 | 
					 | 
				
			||||||
  display: -ms-flexbox;
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  -webkit-flex-wrap: wrap;
 | 
					 | 
				
			||||||
  -ms-flex-wrap: wrap;
 | 
					 | 
				
			||||||
  flex-wrap: wrap;
 | 
					 | 
				
			||||||
  margin-right: -15px;
 | 
					 | 
				
			||||||
  margin-left: -15px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.col {
 | 
					 | 
				
			||||||
  position: relative;
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  min-height: 1px;
 | 
					 | 
				
			||||||
  padding-right: 15px;
 | 
					 | 
				
			||||||
  padding-left: 15px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.table-fac-1,
 | 
					 | 
				
			||||||
.table-fac-1 > td {
 | 
					 | 
				
			||||||
  background-color: #ffeeba;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.table-fac-2,
 | 
					 | 
				
			||||||
.table-fac-2 > td {
 | 
					 | 
				
			||||||
  background-color: #bac9ff;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.table-fac-3,
 | 
					 | 
				
			||||||
.table-fac-3 > td {
 | 
					 | 
				
			||||||
  background-color: #f5c6cb;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.table {
 | 
					 | 
				
			||||||
  border-collapse: collapse !important;
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  max-width: 100%;
 | 
					 | 
				
			||||||
  margin-bottom: 1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.table td,
 | 
					 | 
				
			||||||
.table th {
 | 
					 | 
				
			||||||
  padding: 0.75rem;
 | 
					 | 
				
			||||||
  vertical-align: top;
 | 
					 | 
				
			||||||
  border-top: 1px solid #eceeef;
 | 
					 | 
				
			||||||
  text-align: center;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.table thead th {
 | 
					 | 
				
			||||||
  vertical-align: bottom;
 | 
					 | 
				
			||||||
  border-bottom: 2px solid #eceeef;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.table tbody + tbody {
 | 
					 | 
				
			||||||
  border-top: 2px solid #eceeef;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.table .table {
 | 
					 | 
				
			||||||
  background-color: #fff;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
label {
 | 
					 | 
				
			||||||
  -ms-touch-action: manipulation;
 | 
					 | 
				
			||||||
  touch-action: manipulation;
 | 
					 | 
				
			||||||
  display: inline-block;
 | 
					 | 
				
			||||||
  margin-bottom: 0.5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.form-group {
 | 
					 | 
				
			||||||
	margin-bottom: 1rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.form-control {
 | 
					 | 
				
			||||||
  display: block;
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  padding: 0.5rem 0.75rem;
 | 
					 | 
				
			||||||
  font-size: 1rem;
 | 
					 | 
				
			||||||
  line-height: 1.25;
 | 
					 | 
				
			||||||
  color: #464a4c;
 | 
					 | 
				
			||||||
  background-color: #fff;
 | 
					 | 
				
			||||||
  background-image: none;
 | 
					 | 
				
			||||||
  -webkit-background-clip: padding-box;
 | 
					 | 
				
			||||||
  background-clip: padding-box;
 | 
					 | 
				
			||||||
  border: 1px solid rgba(0, 0, 0, 0.15);
 | 
					 | 
				
			||||||
  border-radius: 0.25rem;
 | 
					 | 
				
			||||||
  -webkit-transition: border-color ease-in-out 0.15s,
 | 
					 | 
				
			||||||
    -webkit-box-shadow ease-in-out 0.15s;
 | 
					 | 
				
			||||||
  transition: border-color ease-in-out 0.15s,
 | 
					 | 
				
			||||||
    -webkit-box-shadow ease-in-out 0.15s;
 | 
					 | 
				
			||||||
  -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
 | 
					 | 
				
			||||||
  transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
 | 
					 | 
				
			||||||
  transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s,
 | 
					 | 
				
			||||||
    -webkit-box-shadow ease-in-out 0.15s;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.form-control:focus {
 | 
					 | 
				
			||||||
  color: #464a4c;
 | 
					 | 
				
			||||||
  background-color: #fff;
 | 
					 | 
				
			||||||
  border-color: #5cb3fd;
 | 
					 | 
				
			||||||
  outline: 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.btn {
 | 
					 | 
				
			||||||
	display: inline-block;
 | 
					 | 
				
			||||||
	font-weight: 400;
 | 
					 | 
				
			||||||
	line-height: 1.25;
 | 
					 | 
				
			||||||
	text-align: center;
 | 
					 | 
				
			||||||
	white-space: nowrap;
 | 
					 | 
				
			||||||
	vertical-align: middle;
 | 
					 | 
				
			||||||
	-webkit-user-select: none;
 | 
					 | 
				
			||||||
	-moz-user-select: none;
 | 
					 | 
				
			||||||
	-ms-user-select: none;
 | 
					 | 
				
			||||||
	user-select: none;
 | 
					 | 
				
			||||||
	border: 1px solid transparent;
 | 
					 | 
				
			||||||
	padding: 0.5rem 1rem;
 | 
					 | 
				
			||||||
	font-size: 1rem;
 | 
					 | 
				
			||||||
	border-radius: 0.25rem;
 | 
					 | 
				
			||||||
	-webkit-transition: all 0.2s ease-in-out;
 | 
					 | 
				
			||||||
	-o-transition: all 0.2s ease-in-out;
 | 
					 | 
				
			||||||
	transition: all 0.2s ease-in-out;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .btn:focus,
 | 
					 | 
				
			||||||
  .btn:hover {
 | 
					 | 
				
			||||||
	text-decoration: none;
 | 
					 | 
				
			||||||
  -webkit-box-shadow: 0 0 0 4px rgba(216, 63, 2, 0.25);
 | 
					 | 
				
			||||||
  box-shadow: 0 0 0 4px rgba(216, 63, 2, 0.25);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .btn:focus {
 | 
					 | 
				
			||||||
	outline: 0;
 | 
					 | 
				
			||||||
	-webkit-box-shadow: 0 0 0 2px rgba(2, 117, 216, 0.25);
 | 
					 | 
				
			||||||
	box-shadow: 0 0 0 2px rgba(2, 117, 216, 0.25);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/favicon.ico
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										200
									
								
								data/index.htm
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,200 @@
 | 
				
			|||||||
 | 
					<!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>
 | 
				
			||||||
							
								
								
									
										100
									
								
								data/index.html
									
									
									
									
									
								
							
							
						
						@@ -1,100 +0,0 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					 | 
				
			||||||
<html lang="en">
 | 
					 | 
				
			||||||
  <head>
 | 
					 | 
				
			||||||
    <meta charset="utf-8">
 | 
					 | 
				
			||||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
					 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <title>%TITLE% - by hiabuto.de</title>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <meta name="description" content="A Project, designed by hiabuto.de">
 | 
					 | 
				
			||||||
    <meta name="author" content="Marcel Peterkau">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <link href="css/style.css" rel="stylesheet">
 | 
					 | 
				
			||||||
	<!--<link href="css/bootstrap.min.css" rel="stylesheet">-->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  </head>
 | 
					 | 
				
			||||||
  <body>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div class="container-fluid">
 | 
					 | 
				
			||||||
	<div class="row">
 | 
					 | 
				
			||||||
		<div class="col">
 | 
					 | 
				
			||||||
			<h3>
 | 
					 | 
				
			||||||
				%TITLE%
 | 
					 | 
				
			||||||
			</h3>
 | 
					 | 
				
			||||||
			<p>Battery Voltage: %BATTERY_VOLTAGE% V</p>
 | 
					 | 
				
			||||||
			<p>Battery Level: %BATTERY_LEVEL% %</p>
 | 
					 | 
				
			||||||
			<table class="table">
 | 
					 | 
				
			||||||
				<thead>
 | 
					 | 
				
			||||||
					<tr>
 | 
					 | 
				
			||||||
						<th>
 | 
					 | 
				
			||||||
							#
 | 
					 | 
				
			||||||
						</th>
 | 
					 | 
				
			||||||
						<th>
 | 
					 | 
				
			||||||
							Team
 | 
					 | 
				
			||||||
						</th>
 | 
					 | 
				
			||||||
						<th>
 | 
					 | 
				
			||||||
							Points
 | 
					 | 
				
			||||||
						</th>
 | 
					 | 
				
			||||||
						<th>
 | 
					 | 
				
			||||||
							Status
 | 
					 | 
				
			||||||
						</th>
 | 
					 | 
				
			||||||
					</tr>
 | 
					 | 
				
			||||||
				</thead>
 | 
					 | 
				
			||||||
				<tbody>
 | 
					 | 
				
			||||||
					<tr class="table-fac-1">
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							1
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%NAME_FAC_1%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%POINTS_FAC_1%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%STATUS_FAC_1%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
					</tr>
 | 
					 | 
				
			||||||
					<tr class="table-fac-2">
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							2
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%NAME_FAC_2%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%POINTS_FAC_2%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%STATUS_FAC_2%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
					</tr>
 | 
					 | 
				
			||||||
					<tr class="table-fac-3">
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							3
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%NAME_FAC_3%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%POINTS_FAC_3%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
						<td>
 | 
					 | 
				
			||||||
							%STATUS_FAC_3%
 | 
					 | 
				
			||||||
						</td>
 | 
					 | 
				
			||||||
					</tr>
 | 
					 | 
				
			||||||
				</tbody>
 | 
					 | 
				
			||||||
			</table> 
 | 
					 | 
				
			||||||
			<form  action="index.html" method="get" role="form">
 | 
					 | 
				
			||||||
				<div class="form-group">		 
 | 
					 | 
				
			||||||
					<label for="commandInput">Command</label>
 | 
					 | 
				
			||||||
					<input type="text" class="form-control" id="commandInput" name="command" />
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
				<button type="submit" class="btn">Submit</button>
 | 
					 | 
				
			||||||
			</form>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
  </body>
 | 
					 | 
				
			||||||
</html>
 | 
					 | 
				
			||||||
							
								
								
									
										28
									
								
								data/post.htm
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					<!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
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										5739
									
								
								data/static/css/custom.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								data/static/img/android-chrome-192x192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 34 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/android-chrome-256x256.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 55 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 30 KiB  | 
							
								
								
									
										9
									
								
								data/static/img/browserconfig.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<browserconfig>
 | 
				
			||||||
 | 
					    <msapplication>
 | 
				
			||||||
 | 
					        <tile>
 | 
				
			||||||
 | 
					            <square150x150logo src="/mstile-150x150.png"/>
 | 
				
			||||||
 | 
					            <TileColor>#da532c</TileColor>
 | 
				
			||||||
 | 
					        </tile>
 | 
				
			||||||
 | 
					    </msapplication>
 | 
				
			||||||
 | 
					</browserconfig>
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/static/img/favicon-16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/favicon-32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 32 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/logo_fac1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 37 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/logo_fac2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 26 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/logo_fac3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 34 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/static/img/mstile-150x150.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										345
									
								
								data/static/img/safari-pinned-tab.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,345 @@
 | 
				
			|||||||
 | 
					<?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>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 24 KiB  | 
							
								
								
									
										19
									
								
								data/static/img/site.webmanifest
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "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
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										2
									
								
								data/static/js/jquery-3.3.1.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										8
									
								
								git_rev_macro.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					revision = (
 | 
				
			||||||
 | 
					    subprocess.check_output(["git", "rev-parse", "--short=10", "HEAD"])
 | 
				
			||||||
 | 
					    .strip()
 | 
				
			||||||
 | 
					    .decode("utf-8")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					print("-DGIT_REV='\"%s\"'" % revision)
 | 
				
			||||||
							
								
								
									
										2
									
								
								lib/ESP Async WebServer/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					.vscode
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
							
								
								
									
										1
									
								
								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
									
								
								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
									
								
								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
									
								
								lib/ESP Async WebServer/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								lib/ESP Async WebServer/_config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					theme: jekyll-theme-cayman
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/ESP Async WebServer/component.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					COMPONENT_ADD_INCLUDEDIRS := src
 | 
				
			||||||
 | 
					COMPONENT_SRCDIRS := src
 | 
				
			||||||
 | 
					CXXFLAGS += -fno-rtti
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/ESP Async WebServer/keywords.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					JsonArray	KEYWORD1
 | 
				
			||||||
 | 
					add	KEYWORD2
 | 
				
			||||||
 | 
					createArray	KEYWORD3
 | 
				
			||||||
							
								
								
									
										33
									
								
								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
									
								
								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
									
								
								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
									
								
								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
									
								
								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
									
								
								lib/ESP Async WebServer/src/AsyncWebSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										350
									
								
								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_ */
 | 
				
			||||||
							
								
								
									
										87
									
								
								lib/ESP Async WebServer/src/AsyncWebSynchronization.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -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
									
								
								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
									
								
								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
									
								
								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
									
								
								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
									
								
								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
									
								
								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
									
								
								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
									
								
								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
									
								
								lib/ESP Async WebServer/src/WebRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										136
									
								
								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
									
								
								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
									
								
								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
									
								
								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>
 | 
				
			||||||
@@ -8,27 +8,40 @@
 | 
				
			|||||||
; Please visit documentation for the other options and examples
 | 
					; Please visit documentation for the other options and examples
 | 
				
			||||||
; https://docs.platformio.org/page/projectconf.html
 | 
					; https://docs.platformio.org/page/projectconf.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[env:d1_mini]
 | 
					[platformio]
 | 
				
			||||||
platform = espressif8266
 | 
					extra_configs =
 | 
				
			||||||
board = d1_mini
 | 
					  wifi_credentials.ini
 | 
				
			||||||
board_build.filesystem = littlefs
 | 
					 | 
				
			||||||
board_build.f_flash = 80000000L
 | 
					 | 
				
			||||||
board_build.ldscript = eagle.flash.4m1m.ld
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
upload_protocol = espota
 | 
					[env:de-timer]
 | 
				
			||||||
upload_port = ESP_OTA.local
 | 
					platform = espressif32
 | 
				
			||||||
upload_flags=
 | 
					board = ttgo-lora32-v1
 | 
				
			||||||
    --auth=UploadTheFlag
 | 
					board_build.filesystem = littlefs
 | 
				
			||||||
 | 
					;board_build.f_flash = 80000000L
 | 
				
			||||||
 | 
					;board_build.ldscript = eagle.flash.4m1m.ld
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					monitor_filters = esp32_exception_decoder
 | 
				
			||||||
 | 
					monitor_speed = 115200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					upload_protocol = esptool
 | 
				
			||||||
 | 
					upload_speed = 921600
 | 
				
			||||||
 | 
					;upload_port = 10.0.1.48
 | 
				
			||||||
 | 
					;upload_protocol = espota
 | 
				
			||||||
 | 
					;upload_flags =  
 | 
				
			||||||
 | 
					;   --auth=${wifi_cred.admin_password}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build_type = debug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
build_flags=
 | 
					build_flags=
 | 
				
			||||||
 | 
					  !python git_rev_macro.py
 | 
				
			||||||
  -DSERIAL_DEBUG
 | 
					  -DSERIAL_DEBUG
 | 
				
			||||||
    -D DEVICE_NAME='"DE CTF Timer Prototype 1"'
 | 
					  ;-DWIFI_CLIENT
 | 
				
			||||||
    -D WIFI_CLIENT
 | 
					  ;-DCAPTIVE
 | 
				
			||||||
    -D WIFI_SSID='"BND_Scanner_#42"'
 | 
					  -DWIFI_AP_IP_GW=10,0,1,1
 | 
				
			||||||
    -D WIFI_PASS='"5xMYkerbLMdrsSdF3hpy5DM9"'
 | 
					  -DADMIN_PASSWORD=${wifi_cred.admin_password}
 | 
				
			||||||
    ;-D WIFI_AP
 | 
					  -DWIFI_SSID=${wifi_cred.wifi_ssid}
 | 
				
			||||||
    ;-D WIFI_SSID='"Dark Emergency CTF Timer"'
 | 
					  -DWIFI_PASSWORD=${wifi_cred.wifi_password}
 | 
				
			||||||
    ;-D WIFI_PASS='"CaptureTheFlag"'
 | 
					  -DWIFI_AP_PASSWORD=${wifi_cred.wifi_ap_password}
 | 
				
			||||||
 | 
					  -DDEVICE_NAME='"Dark Emergency Timer"'
 | 
				
			||||||
  -DFACTION_1_NAME='"GOF"'
 | 
					  -DFACTION_1_NAME='"GOF"'
 | 
				
			||||||
  -DFACTION_2_NAME='"MILIZ"'
 | 
					  -DFACTION_2_NAME='"MILIZ"'
 | 
				
			||||||
  -DFACTION_3_NAME='"KGG"'
 | 
					  -DFACTION_3_NAME='"KGG"'
 | 
				
			||||||
@@ -36,6 +49,8 @@ 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
 | 
				
			||||||
    me-no-dev/ESP Async WebServer @ ^1.2.3
 | 
					    ;me-no-dev/ESP Async WebServer @ ^1.2.3  ; local version included due to bug
 | 
				
			||||||
 | 
					    me-no-dev/AsyncTCP @ ^1.1.1
 | 
				
			||||||
    sstaub/Ticker @ ^4.2.0
 | 
					    sstaub/Ticker @ ^4.2.0
 | 
				
			||||||
    adafruit/Adafruit INA219 @ ^1.1.1
 | 
					    adafruit/Adafruit INA219 @ ^1.1.1
 | 
				
			||||||
 | 
					    robtillaart/I2C_EEPROM @ ^1.5.2
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								src/common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					#ifndef _COMMON_H_
 | 
				
			||||||
 | 
					#define _COMMON_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define Q(x) #x
 | 
				
			||||||
 | 
					#define QUOTE(x) Q(x)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Module connection pins (ESP GPIO-Nums)
 | 
				
			||||||
 | 
					#define CLK 16
 | 
				
			||||||
 | 
					#define DIO_FAC_1_7SEG 14
 | 
				
			||||||
 | 
					#define DIO_FAC_2_7SEG 12
 | 
				
			||||||
 | 
					#define DIO_FAC_3_7SEG 13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DIO_FAC_1_TRG 0
 | 
				
			||||||
 | 
					#define FAC_1_TRG_PRESSED LOW
 | 
				
			||||||
 | 
					#define DIO_FAC_2_TRG 2
 | 
				
			||||||
 | 
					#define FAC_2_TRG_PRESSED LOW
 | 
				
			||||||
 | 
					#define DIO_FAC_3_TRG 15
 | 
				
			||||||
 | 
					#define FAC_3_TRG_PRESSED HIGH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef HOST_NAME
 | 
				
			||||||
 | 
					#define HOST_NAME "DE_Timer_%06X" // Use printf-Formatting - Chip-ID (uin32_t) will be added
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef OTA_DELAY
 | 
				
			||||||
 | 
					#define OTA_DELAY 50 // ticks -> 10ms / tick
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ADMIN_PASSWORD
 | 
				
			||||||
 | 
					#error "You need to define ADMIN_PASSWORD for OTA-Update"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifndef WIFI_PASSWORD
 | 
				
			||||||
 | 
					#error "You must define an WIFI_PASSWORD for OTA-Update"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifndef WIFI_SSID
 | 
				
			||||||
 | 
					#error "You must define an WIFI_SSID for OTA-Update"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifndef WIFI_AP_PASSWORD
 | 
				
			||||||
 | 
					#error "You must define an WIFI_AP_PASSWORD for Standalone AP-Mode"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										261
									
								
								src/config.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,261 @@
 | 
				
			|||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I2C_eeprom ee(0x50, EEPROM_SIZE_BYTES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					configData_t ConfigData;
 | 
				
			||||||
 | 
					persistenceData_t PersistenceData;
 | 
				
			||||||
 | 
					const uint16_t eeVersion = 1; // inc
 | 
				
			||||||
 | 
					boolean eeAvailable = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint16_t persistencemarker_Adress = 0; // sizeof 4
 | 
				
			||||||
 | 
					const uint16_t startofConfig_Adress = 16;
 | 
				
			||||||
 | 
					const uint16_t startofPersistence_Adress = startofConfig_Adress + sizeof(ConfigData) + (sizeof(ConfigData) % 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					boolean checkEEPROMavailable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void InitEEPROM()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ee.begin();
 | 
				
			||||||
 | 
					  if (!checkEEPROMavailable())
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    globals.systemStatus = sysStat_Error;
 | 
				
			||||||
 | 
					    MaintainDTC(DTC_NO_EEPROM_FOUND, true);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  GetConfig_EEPROM();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (ConfigData.EEPROM_Version != eeVersion)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    FormatConfig_EEPROM();
 | 
				
			||||||
 | 
					    globals.systemStatus = sysStat_Error;
 | 
				
			||||||
 | 
					    MaintainDTC(DTC_EEPROM_VERSION_BAD, true);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (getPersistanceAddress() > ee.getDeviceSize())
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    FormatPersistence_EEPROM();
 | 
				
			||||||
 | 
					    globals.systemStatus = sysStat_Error;
 | 
				
			||||||
 | 
					    MaintainDTC(DTC_EEPROM_PDS_MARKER_INVALID, true);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  GetPersistence_EEPROM();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EEPROM_Process()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (globals.requestEEAction)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					  case EE_CFG_SAVE:
 | 
				
			||||||
 | 
					    StoreConfig_EEPROM();
 | 
				
			||||||
 | 
					    globals.requestEEAction = EE_IDLE;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case EE_CFG_LOAD:
 | 
				
			||||||
 | 
					    GetConfig_EEPROM();
 | 
				
			||||||
 | 
					    globals.requestEEAction = EE_IDLE;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case EE_PDS_SAVE:
 | 
				
			||||||
 | 
					    StorePersistence_EEPROM();
 | 
				
			||||||
 | 
					    globals.requestEEAction = EE_IDLE;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case EE_PDS_LOAD:
 | 
				
			||||||
 | 
					    GetPersistence_EEPROM();
 | 
				
			||||||
 | 
					    globals.requestEEAction = EE_IDLE;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case EE_IDLE:
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    globals.requestEEAction = EE_IDLE;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void StoreConfig_EEPROM()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ConfigData.checksum = 0;
 | 
				
			||||||
 | 
					  ConfigData.checksum = Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData));
 | 
				
			||||||
 | 
					  if (!checkEEPROMavailable())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ee.updateBlock(startofConfig_Adress, (uint8_t *)&ConfigData, sizeof(ConfigData));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GetConfig_EEPROM()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (!checkEEPROMavailable())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ee.readBlock(startofConfig_Adress, (uint8_t *)&ConfigData, sizeof(ConfigData));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t checksum = ConfigData.checksum;
 | 
				
			||||||
 | 
					  ConfigData.checksum = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData)) != checksum)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    MaintainDTC(DTC_EEPROM_CFG_BAD, true);
 | 
				
			||||||
 | 
					    FormatConfig_EEPROM();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ConfigData.checksum = checksum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t getPersistanceAddress()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  uint32_t eePersistenceMarker;
 | 
				
			||||||
 | 
					  ee.readBlock(persistencemarker_Adress, (uint8_t *)&eePersistenceMarker, sizeof(eePersistenceMarker));
 | 
				
			||||||
 | 
					  return eePersistenceMarker;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void updatePersistanceAddress(uint32_t adress)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ee.updateBlock(persistencemarker_Adress, (uint8_t *)&adress, sizeof(adress));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void StorePersistence_EEPROM()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (PersistenceData.writeCycleCounter >= EEPROM_ENDURANCE)
 | 
				
			||||||
 | 
					    MovePersistencePage_EEPROM(false);
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    PersistenceData.writeCycleCounter++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PersistenceData.checksum = 0;
 | 
				
			||||||
 | 
					  PersistenceData.checksum = Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!checkEEPROMavailable())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ee.updateBlock(getPersistanceAddress(), (uint8_t *)&PersistenceData, sizeof(PersistenceData));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GetPersistence_EEPROM()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (!checkEEPROMavailable())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ee.readBlock(getPersistanceAddress(), (uint8_t *)&PersistenceData, sizeof(PersistenceData));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t checksum = PersistenceData.checksum;
 | 
				
			||||||
 | 
					  PersistenceData.checksum = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) != checksum)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    MaintainDTC(DTC_EEPROM_PDS_BAD, true);
 | 
				
			||||||
 | 
					    FormatPersistence_EEPROM();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  PersistenceData.checksum = checksum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FormatConfig_EEPROM()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  configData_t defaults;
 | 
				
			||||||
 | 
					  ConfigData = defaults;
 | 
				
			||||||
 | 
					  ConfigData.EEPROM_Version = eeVersion;
 | 
				
			||||||
 | 
					  StoreConfig_EEPROM();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FormatPersistence_EEPROM()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  persistenceData_t defaults;
 | 
				
			||||||
 | 
					  PersistenceData = defaults;
 | 
				
			||||||
 | 
					  updatePersistanceAddress(startofPersistence_Adress);
 | 
				
			||||||
 | 
					  StorePersistence_EEPROM();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MovePersistencePage_EEPROM(boolean reset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (!checkEEPROMavailable())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (reset)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    updatePersistanceAddress(startofPersistence_Adress);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    uint32_t newPersistenceMarker = getPersistanceAddress() + sizeof(PersistenceData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // check if we reached the End of the EEPROM and Startover at the beginning
 | 
				
			||||||
 | 
					    if ((newPersistenceMarker + sizeof(PersistenceData)) > ee.getDeviceSize())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      MaintainDTC(DTC_EEPROM_WORNOUT, true);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      updatePersistanceAddress(newPersistenceMarker);
 | 
				
			||||||
 | 
					      PersistenceData.writeCycleCounter = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t GetEESize()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  return ee.getDeviceSize();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t Checksum_EEPROM(uint8_t const *data, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (data == NULL)
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  uint32_t crc, mask;
 | 
				
			||||||
 | 
					  crc = 0xFFFFFFFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (len--)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    crc ^= *data++;
 | 
				
			||||||
 | 
					    for (uint8_t k = 0; k < 8; k++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      mask = -(crc & 1);
 | 
				
			||||||
 | 
					      crc = (crc >> 1) ^ (0xEDB88320 & mask);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return ~crc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dumpEEPROM(uint16_t memoryAddress, uint16_t length)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#define BLOCK_TO_LENGTH 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (length > ee.getDeviceSize())
 | 
				
			||||||
 | 
					    length = ee.getDeviceSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (memoryAddress + length > ee.getDeviceSize())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!checkEEPROMavailable())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  char ascii_buf[BLOCK_TO_LENGTH + 1];
 | 
				
			||||||
 | 
					  sprintf(ascii_buf, "%*s", BLOCK_TO_LENGTH, "ASCII");
 | 
				
			||||||
 | 
					  Serial.print(PSTR("\nAddress "));
 | 
				
			||||||
 | 
					  for (int x = 0; x < BLOCK_TO_LENGTH; x++)
 | 
				
			||||||
 | 
					    Serial.printf("%3d", x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  memoryAddress = memoryAddress / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
 | 
				
			||||||
 | 
					  length = (length + BLOCK_TO_LENGTH - 1) / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (unsigned int i = 0; i < length; i++)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    int blockpoint = memoryAddress % BLOCK_TO_LENGTH;
 | 
				
			||||||
 | 
					    if (blockpoint == 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      ascii_buf[BLOCK_TO_LENGTH] = 0;
 | 
				
			||||||
 | 
					      Serial.printf("  %s", ascii_buf);
 | 
				
			||||||
 | 
					      Serial.printf("\n0x%05X:", memoryAddress);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ascii_buf[blockpoint] = ee.readByte(memoryAddress);
 | 
				
			||||||
 | 
					    Serial.printf(" %02X", ascii_buf[blockpoint]);
 | 
				
			||||||
 | 
					    if (ascii_buf[blockpoint] < 0x20 || ascii_buf[blockpoint] > 0x7E)
 | 
				
			||||||
 | 
					      ascii_buf[blockpoint] = '.';
 | 
				
			||||||
 | 
					    memoryAddress++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  Serial.println();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					boolean checkEEPROMavailable()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (!ee.isConnected())
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    MaintainDTC(DTC_NO_EEPROM_FOUND, true);
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										69
									
								
								src/config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					#ifndef _CONFIG_H_
 | 
				
			||||||
 | 
					#define _CONFIG_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Arduino.h>
 | 
				
			||||||
 | 
					#include <Wire.h>
 | 
				
			||||||
 | 
					#include <I2C_eeprom.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "globals.h"
 | 
				
			||||||
 | 
					#include "dtc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC01
 | 
				
			||||||
 | 
					#define EEPROM_ENDURANCE 1000000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  NONE,
 | 
				
			||||||
 | 
					  FACTION_1,
 | 
				
			||||||
 | 
					  FACTION_2,
 | 
				
			||||||
 | 
					  FACTION_3
 | 
				
			||||||
 | 
					} factions_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  uint32_t writeCycleCounter = 0;
 | 
				
			||||||
 | 
					  uint32_t faction_1_timer = 0;
 | 
				
			||||||
 | 
					  uint32_t faction_2_timer = 0;
 | 
				
			||||||
 | 
					  uint32_t faction_3_timer = 0;
 | 
				
			||||||
 | 
					  factions_t activeFaction = NONE;
 | 
				
			||||||
 | 
					  uint32_t checksum = 0;
 | 
				
			||||||
 | 
					} persistenceData_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  BATTERY_UNDEFINED,
 | 
				
			||||||
 | 
					  BATTERY_LIPO_2S,
 | 
				
			||||||
 | 
					  BATTERY_LIPO_3S
 | 
				
			||||||
 | 
					} batteryType_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char BatteryString[][10]{
 | 
				
			||||||
 | 
					  "Undefined",
 | 
				
			||||||
 | 
					  "LiPo 2S",
 | 
				
			||||||
 | 
					  "LiPo 3S"
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  uint8_t EEPROM_Version = 1;
 | 
				
			||||||
 | 
					  batteryType_t batteryType = BATTERY_UNDEFINED;
 | 
				
			||||||
 | 
					  uint32_t checksum = 0;
 | 
				
			||||||
 | 
					} configData_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void InitEEPROM();
 | 
				
			||||||
 | 
					void EEPROM_Process();
 | 
				
			||||||
 | 
					void StoreConfig_EEPROM();
 | 
				
			||||||
 | 
					void GetConfig_EEPROM();
 | 
				
			||||||
 | 
					void StorePersistence_EEPROM();
 | 
				
			||||||
 | 
					void GetPersistence_EEPROM();
 | 
				
			||||||
 | 
					void FormatConfig_EEPROM();
 | 
				
			||||||
 | 
					void FormatPersistence_EEPROM();
 | 
				
			||||||
 | 
					uint32_t Checksum_EEPROM(uint8_t const *data, size_t len);
 | 
				
			||||||
 | 
					void dumpEEPROM(uint16_t memoryAddress, uint16_t length);
 | 
				
			||||||
 | 
					void MovePersistencePage_EEPROM(boolean reset);
 | 
				
			||||||
 | 
					uint32_t getPersistanceAddress();
 | 
				
			||||||
 | 
					void updatePersistanceAddress(uint32_t adress);
 | 
				
			||||||
 | 
					uint32_t GetEESize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern configData_t ConfigData;
 | 
				
			||||||
 | 
					extern persistenceData_t PersistenceData;
 | 
				
			||||||
 | 
					#endif // _CONFIG_H_
 | 
				
			||||||
@@ -2,10 +2,10 @@
 | 
				
			|||||||
#define _DEFAULTS_H_
 | 
					#define _DEFAULTS_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef WIFI_CLIENT
 | 
					#ifndef WIFI_CLIENT
 | 
				
			||||||
#define WIFI_AP
 | 
					#define WIFI_ACCESSPOINT
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(WIFI_CLIENT) && defined(WIFI_AP)
 | 
					#if defined(WIFI_CLIENT) && defined(WIFI_ACCESSPOINT)
 | 
				
			||||||
#error "You can't define AP and CLIENT at the same Time!"
 | 
					#error "You can't define AP and CLIENT at the same Time!"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										85
									
								
								src/dtc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					#include "dtc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DTCEntry_s DTCStorage[MAX_DTC_STORAGE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MaintainDTC(DTCNums_t DTC_no, boolean active)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (DTCStorage[i].Number == DTC_no)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (active && DTCStorage[i].active != DTC_ACTIVE)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Serial.printf("DTC gone active: %d", DTC_no);
 | 
				
			||||||
 | 
					                DTCStorage[i].timestamp = millis();
 | 
				
			||||||
 | 
					                DTCStorage[i].active = DTC_ACTIVE;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!active && DTCStorage[i].active == DTC_ACTIVE)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Serial.printf("DTC gone previous: %d", DTC_no);
 | 
				
			||||||
 | 
					                DTCStorage[i].active = DTC_PREVIOUS;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // DTC was not found with upper iteration, but is active
 | 
				
			||||||
 | 
					    // so we need to look for free space to store DTC
 | 
				
			||||||
 | 
					    if (active == true)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (DTCStorage[i].Number == DTC_LAST_DTC)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Serial.printf("new DTC registered: %d", DTC_no);
 | 
				
			||||||
 | 
					                DTCStorage[i].Number = DTC_no;
 | 
				
			||||||
 | 
					                DTCStorage[i].timestamp = millis();
 | 
				
			||||||
 | 
					                DTCStorage[i].active = DTC_ACTIVE;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ClearDTC(DTCNums_t DTC_no)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (DTCStorage[i].Number == DTC_no)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            DTCStorage[i].Number = DTC_LAST_DTC;
 | 
				
			||||||
 | 
					            DTCStorage[i].active = DTC_NONE;
 | 
				
			||||||
 | 
					            DTCStorage[i].timestamp = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ClearAllDTC()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DTCStorage[i].Number = DTC_LAST_DTC;
 | 
				
			||||||
 | 
					        DTCStorage[i].active = DTC_NONE;
 | 
				
			||||||
 | 
					        DTCStorage[i].timestamp = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DTCNums_t getlastDTC(boolean only_active)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int8_t pointer = -1;
 | 
				
			||||||
 | 
					    uint32_t lasttimestamp = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = 0; i < MAX_DTC_STORAGE; i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (DTCStorage[i].Number > 0 && DTCStorage[i].timestamp > lasttimestamp)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (only_active == false || DTCStorage[i].active == DTC_ACTIVE)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                pointer = i;
 | 
				
			||||||
 | 
					                lasttimestamp = DTCStorage[i].timestamp;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return pointer >= 0 ? DTCStorage[pointer].Number : DTC_LAST_DTC;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/dtc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					#ifndef _DTC_H_
 | 
				
			||||||
 | 
					#define _DTC_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Arduino.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_DTC_STORAGE 6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum DTCNums_e
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  DTC_NO_EEPROM_FOUND,
 | 
				
			||||||
 | 
					  DTC_EEPROM_CFG_BAD,
 | 
				
			||||||
 | 
					  DTC_EEPROM_PDS_BAD,
 | 
				
			||||||
 | 
					  DTC_EEPROM_VERSION_BAD,
 | 
				
			||||||
 | 
					  DTC_EEPROM_WORNOUT,                 // this will happen if the EEPROM-cells are all overwritten 1 million times!
 | 
				
			||||||
 | 
					  DTC_EEPROM_PDS_MARKER_INVALID,      // This happens if the Marker of the PersistanceData was pointing to an EE-Adress bigger than the used EEPROM-IC
 | 
				
			||||||
 | 
					  DTC_LAST_DTC
 | 
				
			||||||
 | 
					} DTCNums_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum DTCActive_e
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  DTC_ACTIVE,
 | 
				
			||||||
 | 
					  DTC_PREVIOUS,
 | 
				
			||||||
 | 
					  DTC_NONE
 | 
				
			||||||
 | 
					} DTCActive_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct DTCEntry_s
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  DTCNums_t Number;
 | 
				
			||||||
 | 
					  uint32_t timestamp;
 | 
				
			||||||
 | 
					  DTCActive_t active;
 | 
				
			||||||
 | 
					} DTCEntry_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MaintainDTC(DTCNums_t DTC_no, boolean active);
 | 
				
			||||||
 | 
					void ClearDTC(DTCNums_t DTC_no);
 | 
				
			||||||
 | 
					void ClearAllDTC();
 | 
				
			||||||
 | 
					DTCNums_t getlastDTC(boolean only_active);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern DTCEntry_s DTCStorage[MAX_DTC_STORAGE];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/globals.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					#ifndef _GLOBALS_H_
 | 
				
			||||||
 | 
					#define _GLOBALS_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Arduino.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum eSystem_Status
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  sysStat_Startup,
 | 
				
			||||||
 | 
					  sysStat_Normal,
 | 
				
			||||||
 | 
					  sysStat_Error,
 | 
				
			||||||
 | 
					  sysStat_Shutdown
 | 
				
			||||||
 | 
					} tSystem_Status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum eEERequest
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  EE_IDLE,
 | 
				
			||||||
 | 
					  EE_CFG_SAVE,
 | 
				
			||||||
 | 
					  EE_CFG_LOAD,
 | 
				
			||||||
 | 
					  EE_PDS_SAVE,
 | 
				
			||||||
 | 
					  EE_PDS_LOAD
 | 
				
			||||||
 | 
					} tEERequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct Globals_s
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  char DeviceName[33];
 | 
				
			||||||
 | 
					  char DeviceName_ID[43];
 | 
				
			||||||
 | 
					  tSystem_Status systemStatus = sysStat_Startup;
 | 
				
			||||||
 | 
					  tSystem_Status resumeStatus = sysStat_Startup;
 | 
				
			||||||
 | 
					  eEERequest requestEEAction = EE_IDLE;
 | 
				
			||||||
 | 
					  float loadvoltage = 0;
 | 
				
			||||||
 | 
					  int battery_level = 0;
 | 
				
			||||||
 | 
					} Globals_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern Globals_t globals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										723
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						@@ -1,50 +1,51 @@
 | 
				
			|||||||
#include <Arduino.h>
 | 
					#include <Arduino.h>
 | 
				
			||||||
#include <TM1637Display.h>
 | 
					#include <TM1637Display.h>
 | 
				
			||||||
#include <Ticker.h>
 | 
					#include <Ticker.h>
 | 
				
			||||||
#ifdef CAPTIVE
 | 
					 | 
				
			||||||
#include <DNSServer.h>
 | 
					#include <DNSServer.h>
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef ESP32
 | 
					 | 
				
			||||||
#include <WiFi.h>
 | 
					#include <WiFi.h>
 | 
				
			||||||
#include <AsyncTCP.h>
 | 
					#include <AsyncTCP.h>
 | 
				
			||||||
#include <WiFiMulti.h>
 | 
					#include <ESPmDNS.h>
 | 
				
			||||||
#elif defined(ESP8266)
 | 
					 | 
				
			||||||
#include <ESP8266WiFi.h>
 | 
					 | 
				
			||||||
#include <ESPAsyncTCP.h>
 | 
					 | 
				
			||||||
#include <ESP8266WiFiMulti.h>
 | 
					 | 
				
			||||||
#include <ESP8266mDNS.h>
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// local includes
 | 
					// local includes
 | 
				
			||||||
#include "defaults.h"
 | 
					#include "defaults.h"
 | 
				
			||||||
 | 
					#include "webui.h"
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "globals.h"
 | 
				
			||||||
 | 
					#include "dtc.h"
 | 
				
			||||||
 | 
					#include "common.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Module connection pins (ESP GPIO-Nums)
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
#define CLK 16
 | 
					#include <WiFiMulti.h>
 | 
				
			||||||
#define DIO_FAC_1_7SEG 14
 | 
					 | 
				
			||||||
#define DIO_FAC_2_7SEG 12
 | 
					 | 
				
			||||||
#define DIO_FAC_3_7SEG 13
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DIO_FAC_1_TRG 0
 | 
					const char *ssid = QUOTE(WIFI_SSID);
 | 
				
			||||||
#define DIO_FAC_2_TRG 2
 | 
					const char *password = QUOTE(WIFI_PASSWORD);
 | 
				
			||||||
#define DIO_FAC_3_TRG 15
 | 
					const uint32_t connectTimeoutMs = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum Factions
 | 
					WiFiMulti wifiMulti;
 | 
				
			||||||
{
 | 
					#endif
 | 
				
			||||||
  NONE,
 | 
					 | 
				
			||||||
  FACTION_1,
 | 
					 | 
				
			||||||
  FACTION_2,
 | 
					 | 
				
			||||||
  FACTION_3
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SevenSeg_Output();
 | 
					void SevenSeg_Output();
 | 
				
			||||||
void FactionTicker_callback();
 | 
					void FactionTicker_callback();
 | 
				
			||||||
void serialOutTicker_callback();
 | 
					 | 
				
			||||||
void inputGetterTicker_callback();
 | 
					void inputGetterTicker_callback();
 | 
				
			||||||
void powerMonitorTicker_callback();
 | 
					void powerMonitorTicker_callback();
 | 
				
			||||||
 | 
					void EEPROMCyclicPDS_callback();
 | 
				
			||||||
 | 
					void toggleWiFiAP(boolean shutdown = false);
 | 
				
			||||||
 | 
					void SystemShutdown();
 | 
				
			||||||
 | 
					void SetBatteryType(batteryType_t type);
 | 
				
			||||||
 | 
					void ProcessKeyCombos();
 | 
				
			||||||
 | 
					void OverrideDisplay(const uint8_t *message, uint32_t time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
 | 
					void wifiMaintainConnectionTicker_callback();
 | 
				
			||||||
 | 
					Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t getESPChipID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG);
 | 
					TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG);
 | 
				
			||||||
TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG);
 | 
					TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG);
 | 
				
			||||||
@@ -52,309 +53,49 @@ TM1637Display disp_FAC_3(CLK, DIO_FAC_3_7SEG);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Adafruit_INA219 ina219;
 | 
					Adafruit_INA219 ina219;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WiFiEventHandler stationConnectedHandler;
 | 
					 | 
				
			||||||
WiFiEventHandler stationDisconnectedHandler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef WIFI_CLIENT
 | 
					 | 
				
			||||||
ESP8266WiFiMulti wifiMulti;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef CAPTIVE
 | 
					#ifdef CAPTIVE
 | 
				
			||||||
DNSServer dnsServer;
 | 
					DNSServer dnsServer;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
AsyncWebServer server(80);
 | 
					AsyncWebServer server(80);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ticker FactionTicker(FactionTicker_callback, 1000, 0, MILLIS);
 | 
					Ticker FactionTicker(FactionTicker_callback, 1000, 0, MILLIS);
 | 
				
			||||||
Ticker SerialOutputTicker(serialOutTicker_callback, 5000, 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Factions activeFaction = NONE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint32_t Count_Faction_1 = 0;
 | 
					 | 
				
			||||||
uint32_t Count_Faction_2 = 0;
 | 
					 | 
				
			||||||
uint32_t Count_Faction_3 = 0;
 | 
					 | 
				
			||||||
uint8_t Faction_1_dot = 0;
 | 
					uint8_t Faction_1_dot = 0;
 | 
				
			||||||
uint8_t Faction_2_dot = 0;
 | 
					uint8_t Faction_2_dot = 0;
 | 
				
			||||||
uint8_t Faction_3_dot = 0;
 | 
					uint8_t Faction_3_dot = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
float loadvoltage = 0;
 | 
					uint32_t DisplayOverrideFlag = 0;
 | 
				
			||||||
int battery_level = 0;
 | 
					uint8_t *DisplayOverrideValue = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Globals_t globals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const uint8_t sevenSeg_bat[] = {0x00, 0b01111100, 0b01110111, 0b01111000};
 | 
					const uint8_t sevenSeg_bat[] = {0x00, 0b01111100, 0b01110111, 0b01111000};
 | 
				
			||||||
const uint8_t sevenSeg_low[] = {0b00111000, 0b01011100, 0x00, 0x00};
 | 
					const uint8_t sevenSeg_low[] = {0b00111000, 0b01011100, 0x00, 0x00};
 | 
				
			||||||
 | 
					const uint8_t sevenSeg_net[] = {0b01010100, 0b01111001, 0b01111000, 0x00};
 | 
				
			||||||
String processor(const String &var)
 | 
					const uint8_t sevenSeg_ota[] = {0x3F, 0x78, 0x77, 0x00};
 | 
				
			||||||
{
 | 
					const uint8_t sevenSeg_flsh[] = {0x71, 0x38, 0x6D, 0x76};
 | 
				
			||||||
  char buffer[16] = {0};
 | 
					const uint8_t sevenSeg_file[] = {0x71, 0x30, 0x38, 0x79};
 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "POINTS_FAC_1")
 | 
					 | 
				
			||||||
    itoa(Count_Faction_1, buffer, 10);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "POINTS_FAC_2")
 | 
					 | 
				
			||||||
    itoa(Count_Faction_2, buffer, 10);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "POINTS_FAC_3")
 | 
					 | 
				
			||||||
    itoa(Count_Faction_3, buffer, 10);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "STATUS_FAC_1")
 | 
					 | 
				
			||||||
    return activeFaction == FACTION_1 ? "ACTIVE" : "INACTIVE";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "STATUS_FAC_2")
 | 
					 | 
				
			||||||
    return activeFaction == FACTION_2 ? "ACTIVE" : "INACTIVE";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "STATUS_FAC_3")
 | 
					 | 
				
			||||||
    return activeFaction == FACTION_3 ? "ACTIVE" : "INACTIVE";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "NAME_FAC_1")
 | 
					 | 
				
			||||||
    return FACTION_1_NAME;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "NAME_FAC_2")
 | 
					 | 
				
			||||||
    return FACTION_2_NAME;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "NAME_FAC_3")
 | 
					 | 
				
			||||||
    return FACTION_3_NAME;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "TITLE")
 | 
					 | 
				
			||||||
    return DEVICE_NAME;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (var == "BATTERY_LEVEL")
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    sprintf(buffer, "%d", battery_level);
 | 
					 | 
				
			||||||
    return String(buffer);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (var == "BATTERY_VOLTAGE")
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    sprintf(buffer, "%f", loadvoltage);
 | 
					 | 
				
			||||||
    return String(buffer);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return String(buffer);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
String macToString(const unsigned char *mac)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  char buf[20];
 | 
					 | 
				
			||||||
  snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
 | 
					 | 
				
			||||||
           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 | 
					 | 
				
			||||||
  return String(buf);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void onStationConnected(const WiFiEventSoftAPModeStationConnected &evt)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  Serial.print("Station connected: ");
 | 
					 | 
				
			||||||
  Serial.println(macToString(evt.mac));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void onStationDisconnected(const WiFiEventSoftAPModeStationDisconnected &evt)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  Serial.print("Station disconnected: ");
 | 
					 | 
				
			||||||
  Serial.println(macToString(evt.mac));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
String getValue(String data, char separator, int index)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  int found = 0;
 | 
					 | 
				
			||||||
  int strIndex[] = {0, -1};
 | 
					 | 
				
			||||||
  int maxIndex = data.length() - 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (int i = 0; i <= maxIndex && found <= index; i++)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    if (data.charAt(i) == separator || i == maxIndex)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      found++;
 | 
					 | 
				
			||||||
      strIndex[0] = strIndex[1] + 1;
 | 
					 | 
				
			||||||
      strIndex[1] = (i == maxIndex) ? i + 1 : i;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class CaptiveRequestHandler : public AsyncWebHandler
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  CaptiveRequestHandler() {}
 | 
					 | 
				
			||||||
  virtual ~CaptiveRequestHandler() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool canHandle(AsyncWebServerRequest *request)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void handleRequest(AsyncWebServerRequest *request)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Serial.print("Requested URL: ");
 | 
					 | 
				
			||||||
    Serial.println(request->url());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //List all parameters
 | 
					 | 
				
			||||||
    int params = request->params();
 | 
					 | 
				
			||||||
    for (int i = 0; i < params; i++)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      AsyncWebParameter *p = request->getParam(i);
 | 
					 | 
				
			||||||
      if (p->isFile())
 | 
					 | 
				
			||||||
      { //p->isPost() is also true
 | 
					 | 
				
			||||||
        Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      else if (p->isPost())
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (LittleFS.exists(request->url()) == false)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      request->send(404, "text/plain", "Not found");
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (request->url() == "/favicon.ico")
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      request->send(LittleFS, request->url(), "image/x-icon");
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (request->url() == "/" || request->url() == "/index.html")
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      request->send(LittleFS, "/index.html", String(), false, processor);
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (getValue(request->url(), '/', 1) == "css")
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      request->send(LittleFS, request->url(), "text/css");
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SevenSeg_Output()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if (battery_level < BAT_LOW_PERCENT)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    disp_FAC_1.setBrightness(0);
 | 
					 | 
				
			||||||
    disp_FAC_2.setBrightness(0);
 | 
					 | 
				
			||||||
    disp_FAC_3.setBrightness(0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    disp_FAC_3.setSegments(sevenSeg_bat);
 | 
					 | 
				
			||||||
    disp_FAC_2.setSegments(sevenSeg_low);
 | 
					 | 
				
			||||||
    if (millis() % 5000 > 2500)
 | 
					 | 
				
			||||||
      disp_FAC_1.showNumberDec(battery_level);
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      disp_FAC_1.showNumberDecEx(loadvoltage * 100, 0x40);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    disp_FAC_1.setBrightness(activeFaction == FACTION_1 ? 7 : 0);
 | 
					 | 
				
			||||||
    disp_FAC_2.setBrightness(activeFaction == FACTION_2 ? 7 : 0);
 | 
					 | 
				
			||||||
    disp_FAC_3.setBrightness(activeFaction == FACTION_3 ? 7 : 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    disp_FAC_1.showNumberDecEx(Count_Faction_1 / 60, Faction_1_dot, true, 4, 0);
 | 
					 | 
				
			||||||
    disp_FAC_2.showNumberDecEx(Count_Faction_2 / 60, Faction_2_dot, true, 4, 0);
 | 
					 | 
				
			||||||
    disp_FAC_3.showNumberDecEx(Count_Faction_3 / 60, Faction_3_dot, true, 4, 0);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void FactionTicker_callback()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  switch (activeFaction)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  case FACTION_1:
 | 
					 | 
				
			||||||
    Count_Faction_1++;
 | 
					 | 
				
			||||||
    Faction_1_dot = Faction_1_dot == 0x80 || Faction_1_dot == 0x00 ? 0x10 : Faction_1_dot << 1;
 | 
					 | 
				
			||||||
    Faction_2_dot = 0;
 | 
					 | 
				
			||||||
    Faction_3_dot = 0;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  case FACTION_2:
 | 
					 | 
				
			||||||
    Count_Faction_2++;
 | 
					 | 
				
			||||||
    Faction_2_dot = Faction_2_dot == 0x80 || Faction_2_dot == 0x00 ? 0x10 : Faction_2_dot << 1;
 | 
					 | 
				
			||||||
    Faction_1_dot = 0;
 | 
					 | 
				
			||||||
    Faction_3_dot = 0;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  case FACTION_3:
 | 
					 | 
				
			||||||
    Count_Faction_3++;
 | 
					 | 
				
			||||||
    Faction_3_dot = Faction_3_dot == 0x80 || Faction_3_dot == 0x00 ? 0x10 : Faction_3_dot << 1;
 | 
					 | 
				
			||||||
    Faction_1_dot = 0;
 | 
					 | 
				
			||||||
    Faction_2_dot = 0;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  default:
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void serialOutTicker_callback()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static uint32_t SerialPrintCount = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (SerialPrintCount % 10 == 0)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    Serial.printf("| %8s | %8s | %8s |\n", FACTION_1_NAME, FACTION_2_NAME, FACTION_3_NAME);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  Serial.printf("  %8d   %8d   %8d\n", Count_Faction_1, Count_Faction_2, Count_Faction_3);
 | 
					 | 
				
			||||||
  SerialPrintCount++;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void inputGetterTicker_callback()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  activeFaction = NONE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (digitalRead(DIO_FAC_1_TRG) + digitalRead(DIO_FAC_2_TRG) + !digitalRead(DIO_FAC_3_TRG) < 2)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    Serial.println("ERROR: More than one Flag active");
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (digitalRead(DIO_FAC_1_TRG) == LOW)
 | 
					 | 
				
			||||||
    activeFaction = FACTION_1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (digitalRead(DIO_FAC_2_TRG) == LOW)
 | 
					 | 
				
			||||||
    activeFaction = FACTION_2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (digitalRead(DIO_FAC_3_TRG) == HIGH)
 | 
					 | 
				
			||||||
    activeFaction = FACTION_3;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void powerMonitorTicker_callback()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // loadvoltage and percentage is global, because of battery Monitoring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  float shuntvoltage = 0;
 | 
					 | 
				
			||||||
  float current_mA = 0;
 | 
					 | 
				
			||||||
  float busvoltage = 0;
 | 
					 | 
				
			||||||
  float power_mW = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  shuntvoltage = ina219.getShuntVoltage_mV();
 | 
					 | 
				
			||||||
  busvoltage = ina219.getBusVoltage_V();
 | 
					 | 
				
			||||||
  current_mA = ina219.getCurrent_mA();
 | 
					 | 
				
			||||||
  power_mW = ina219.getPower_mW();
 | 
					 | 
				
			||||||
  loadvoltage = busvoltage + (shuntvoltage / 1000);
 | 
					 | 
				
			||||||
  battery_level = map(loadvoltage * 100, 655, 840, 0, 100);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Serial.printf("Battery Level: %d %%\n", battery_level);
 | 
					 | 
				
			||||||
  Serial.printf("Bus Voltage: %f V\n", busvoltage);
 | 
					 | 
				
			||||||
  Serial.printf("Shunt Voltage: %f mV\n", shuntvoltage);
 | 
					 | 
				
			||||||
  Serial.printf("Load Voltage: %f V\n", loadvoltage);
 | 
					 | 
				
			||||||
  Serial.printf("Current: %f mA\n", current_mA);
 | 
					 | 
				
			||||||
  Serial.printf("Power: %f mW\n", power_mW);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void setup()
 | 
					void setup()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  setCpuFrequencyMhz(80);
 | 
				
			||||||
 | 
					  WiFi.setAutoReconnect (false);
 | 
				
			||||||
 | 
					  WiFi.persistent(false);
 | 
				
			||||||
 | 
					  WiFi.disconnect(); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Serial.begin(115200);
 | 
				
			||||||
 | 
					  Serial.print("\n\n\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  strcpy(globals.DeviceName, DEVICE_NAME);
 | 
				
			||||||
 | 
					  snprintf(globals.DeviceName_ID, 42, "%s_%08X", globals.DeviceName, getESPChipID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Serial.begin(9600);
 | 
					 | 
				
			||||||
  Serial.print("\n\n\n");
 | 
					 | 
				
			||||||
#ifdef SERIAL_DEBUG
 | 
					#ifdef SERIAL_DEBUG
 | 
				
			||||||
  Serial.setDebugOutput(true);
 | 
					  Serial.setDebugOutput(true);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -366,53 +107,51 @@ void setup()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  LittleFS.begin();
 | 
					  LittleFS.begin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  WiFi.persistent(false);
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef WIFI_AP
 | 
					 | 
				
			||||||
  WiFi.mode(WIFI_AP);
 | 
					 | 
				
			||||||
  WiFi.softAP(WIFI_SSID, WIFI_PASS);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  stationConnectedHandler = WiFi.onSoftAPModeStationConnected(&onStationConnected);
 | 
					 | 
				
			||||||
  stationDisconnectedHandler = WiFi.onSoftAPModeStationDisconnected(&onStationDisconnected);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
  WiFi.mode(WIFI_STA);
 | 
					  WiFi.mode(WIFI_STA);
 | 
				
			||||||
  wifiMulti.addAP(WIFI_SSID, WIFI_PASS);
 | 
					  WiFi.setHostname(globals.DeviceName_ID);
 | 
				
			||||||
 | 
					  wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD));
 | 
				
			||||||
  Serial.println("Connecting Wifi...");
 | 
					  WiFiMaintainConnectionTicker.start();
 | 
				
			||||||
  if (wifiMulti.run() == WL_CONNECTED)
 | 
					#else
 | 
				
			||||||
  {
 | 
					  WiFi.mode(WIFI_AP);
 | 
				
			||||||
    Serial.println("");
 | 
					  WiFi.begin(QUOTE(DEVICE_NAME), QUOTE(WIFI_AP_PASSWORD));
 | 
				
			||||||
    Serial.println("WiFi connected");
 | 
					  WiFi.setSleep(true);
 | 
				
			||||||
    Serial.println("IP address: ");
 | 
					  WiFi.mode(WIFI_OFF);
 | 
				
			||||||
    Serial.print(WiFi.localIP());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  InitEEPROM();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ArduinoOTA.setPort(8266);
 | 
					  ArduinoOTA.setPort(8266);
 | 
				
			||||||
  ArduinoOTA.setHostname(OTA_HOST);
 | 
					  ArduinoOTA.setHostname(globals.DeviceName_ID);
 | 
				
			||||||
  ArduinoOTA.setPassword(OTA_PASS);
 | 
					  ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ArduinoOTA.onStart([]()
 | 
					  ArduinoOTA.onStart([]()
 | 
				
			||||||
                     {
 | 
					                     {
 | 
				
			||||||
                       String type;
 | 
					                       disp_FAC_1.setBrightness(7);
 | 
				
			||||||
 | 
					                       disp_FAC_2.setBrightness(7);
 | 
				
			||||||
 | 
					                       disp_FAC_3.setBrightness(7);
 | 
				
			||||||
 | 
					                       disp_FAC_1.setSegments(sevenSeg_ota);
 | 
				
			||||||
 | 
					                       disp_FAC_3.clear();
 | 
				
			||||||
 | 
					                       
 | 
				
			||||||
                       if (ArduinoOTA.getCommand() == U_FLASH)
 | 
					                       if (ArduinoOTA.getCommand() == U_FLASH)
 | 
				
			||||||
                       {
 | 
					                       {
 | 
				
			||||||
                         type = "sketch";
 | 
					                         disp_FAC_2.setSegments(sevenSeg_flsh);
 | 
				
			||||||
                       }
 | 
					                       }
 | 
				
			||||||
                       else
 | 
					                       else
 | 
				
			||||||
                       {
 | 
					                       {
 | 
				
			||||||
                         type = "filesystem";
 | 
					                         disp_FAC_2.setSegments(sevenSeg_file);
 | 
				
			||||||
                         LittleFS.end();
 | 
					                         LittleFS.end();
 | 
				
			||||||
                       }
 | 
					                       } });
 | 
				
			||||||
                       Serial.println("Start updating " + type);
 | 
					 | 
				
			||||||
                     });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ArduinoOTA.onEnd([]()
 | 
					  ArduinoOTA.onEnd([]()
 | 
				
			||||||
                   { Serial.println("\nEnd"); });
 | 
					                   { 
 | 
				
			||||||
 | 
					                       const uint8_t seg_done[] = {0x5E, 0x3F, 0x54, 0x79};
 | 
				
			||||||
 | 
					                       disp_FAC_1.setSegments(seg_done);
 | 
				
			||||||
 | 
					                       disp_FAC_2.clear();
 | 
				
			||||||
 | 
					                       disp_FAC_3.clear(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
 | 
					  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
 | 
				
			||||||
                        { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); });
 | 
					                        { disp_FAC_3.showNumberDecEx((progress / (total / 100))); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ArduinoOTA.onError([](ota_error_t error)
 | 
					  ArduinoOTA.onError([](ota_error_t error)
 | 
				
			||||||
                     {
 | 
					                     {
 | 
				
			||||||
@@ -426,70 +165,320 @@ void setup()
 | 
				
			|||||||
                       else if (error == OTA_RECEIVE_ERROR)
 | 
					                       else if (error == OTA_RECEIVE_ERROR)
 | 
				
			||||||
                         Serial.println("Receive Failed");
 | 
					                         Serial.println("Receive Failed");
 | 
				
			||||||
                       else if (error == OTA_END_ERROR)
 | 
					                       else if (error == OTA_END_ERROR)
 | 
				
			||||||
                         Serial.println("End Failed");
 | 
					                         Serial.println("End Failed"); });
 | 
				
			||||||
                     });
 | 
					 | 
				
			||||||
  ArduinoOTA.begin();
 | 
					  ArduinoOTA.begin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CAPTIVE
 | 
					#ifdef CAPTIVE
 | 
				
			||||||
  dnsServer.start(53, "*", WiFi.softAPIP());
 | 
					  dnsServer.start(53, "*", WiFi.softAPIP());
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  server.addHandler(new CaptiveRequestHandler());
 | 
					  initWebUI();
 | 
				
			||||||
  server.begin();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  EEPROMCyclicPDSTicker.start();
 | 
				
			||||||
  FactionTicker.start();
 | 
					  FactionTicker.start();
 | 
				
			||||||
  SerialOutputTicker.start();
 | 
					 | 
				
			||||||
  InputGetterTicker.start();
 | 
					  InputGetterTicker.start();
 | 
				
			||||||
 | 
					  Serial.println("Setup Done");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void loop()
 | 
					void loop()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  EEPROMCyclicPDSTicker.update();
 | 
				
			||||||
  FactionTicker.update();
 | 
					  FactionTicker.update();
 | 
				
			||||||
  SerialOutputTicker.update();
 | 
					 | 
				
			||||||
  InputGetterTicker.update();
 | 
					  InputGetterTicker.update();
 | 
				
			||||||
  PowerMonitorTicker.update();
 | 
					  PowerMonitorTicker.update();
 | 
				
			||||||
  ArduinoOTA.handle();
 | 
					  ArduinoOTA.handle();
 | 
				
			||||||
 | 
					  SevenSeg_Output();
 | 
				
			||||||
 | 
					  EEPROM_Process();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CAPTIVE
 | 
					#ifdef CAPTIVE
 | 
				
			||||||
  dnsServer.processNextRequest();
 | 
					  dnsServer.processNextRequest();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
 | 
					  WiFiMaintainConnectionTicker.update();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  if (globals.systemStatus == sysStat_Shutdown)
 | 
				
			||||||
 | 
					    SystemShutdown();
 | 
				
			||||||
 | 
					  yield();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SevenSeg_Output();
 | 
					String macToString(const unsigned char *mac)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  while (Serial.available() > 0)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    char input = Serial.read();
 | 
					  char buf[20];
 | 
				
			||||||
 | 
					  snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
 | 
				
			||||||
 | 
					           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 | 
				
			||||||
 | 
					  return String(buf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (input)
 | 
					void SevenSeg_Output()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    case 'n':
 | 
					  if (DisplayOverrideFlag > millis())
 | 
				
			||||||
      activeFaction = NONE;
 | 
					  {
 | 
				
			||||||
 | 
					    disp_FAC_1.setBrightness(7);
 | 
				
			||||||
 | 
					    disp_FAC_2.setBrightness(7);
 | 
				
			||||||
 | 
					    disp_FAC_3.setBrightness(7);
 | 
				
			||||||
 | 
					    disp_FAC_1.setSegments(DisplayOverrideValue);
 | 
				
			||||||
 | 
					    disp_FAC_2.clear();
 | 
				
			||||||
 | 
					    disp_FAC_3.clear();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    if (globals.battery_level < BAT_LOW_PERCENT && millis() % 10000 > 7000)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      disp_FAC_1.setBrightness(0);
 | 
				
			||||||
 | 
					      disp_FAC_2.setBrightness(0);
 | 
				
			||||||
 | 
					      disp_FAC_3.setBrightness(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      disp_FAC_1.setSegments(sevenSeg_bat);
 | 
				
			||||||
 | 
					      disp_FAC_2.setSegments(sevenSeg_low);
 | 
				
			||||||
 | 
					      if (millis() % 3000 < 1500)
 | 
				
			||||||
 | 
					        disp_FAC_3.showNumberDec(globals.battery_level);
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					        disp_FAC_3.showNumberDecEx(globals.loadvoltage * 100, 0x40);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      disp_FAC_1.setBrightness(PersistenceData.activeFaction == FACTION_1 ? 7 : 0);
 | 
				
			||||||
 | 
					      disp_FAC_2.setBrightness(PersistenceData.activeFaction == FACTION_2 ? 7 : 0);
 | 
				
			||||||
 | 
					      disp_FAC_3.setBrightness(PersistenceData.activeFaction == FACTION_3 ? 7 : 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      disp_FAC_1.showNumberDecEx(PersistenceData.faction_1_timer / 60, Faction_1_dot, true, 4, 0);
 | 
				
			||||||
 | 
					      disp_FAC_2.showNumberDecEx(PersistenceData.faction_2_timer / 60, Faction_2_dot, true, 4, 0);
 | 
				
			||||||
 | 
					      disp_FAC_3.showNumberDecEx(PersistenceData.faction_3_timer / 60, Faction_3_dot, true, 4, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FactionTicker_callback()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  switch (PersistenceData.activeFaction)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					  case FACTION_1:
 | 
				
			||||||
 | 
					    PersistenceData.faction_1_timer++;
 | 
				
			||||||
 | 
					    Faction_1_dot = Faction_1_dot == 0x80 || Faction_1_dot == 0x00 ? 0x10 : Faction_1_dot << 1;
 | 
				
			||||||
 | 
					    Faction_2_dot = 0;
 | 
				
			||||||
 | 
					    Faction_3_dot = 0;
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case 'g':
 | 
					  case FACTION_2:
 | 
				
			||||||
      activeFaction = FACTION_1;
 | 
					    PersistenceData.faction_2_timer++;
 | 
				
			||||||
 | 
					    Faction_2_dot = Faction_2_dot == 0x80 || Faction_2_dot == 0x00 ? 0x10 : Faction_2_dot << 1;
 | 
				
			||||||
 | 
					    Faction_1_dot = 0;
 | 
				
			||||||
 | 
					    Faction_3_dot = 0;
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case 'k':
 | 
					  case FACTION_3:
 | 
				
			||||||
      activeFaction = FACTION_3;
 | 
					    PersistenceData.faction_3_timer++;
 | 
				
			||||||
      break;
 | 
					    Faction_3_dot = Faction_3_dot == 0x80 || Faction_3_dot == 0x00 ? 0x10 : Faction_3_dot << 1;
 | 
				
			||||||
 | 
					    Faction_1_dot = 0;
 | 
				
			||||||
    case 'm':
 | 
					    Faction_2_dot = 0;
 | 
				
			||||||
      activeFaction = FACTION_2;
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    case 'x':
 | 
					 | 
				
			||||||
      SerialOutputTicker.stop();
 | 
					 | 
				
			||||||
      Serial.println("SerialOutputTicker.stop()");
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    case 'y':
 | 
					 | 
				
			||||||
      SerialOutputTicker.resume();
 | 
					 | 
				
			||||||
      Serial.println("SerialOutputTicker.resume()");
 | 
					 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  default:
 | 
					  default:
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void inputGetterTicker_callback()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ProcessKeyCombos();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t keysPressed = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  keysPressed = +digitalRead(DIO_FAC_1_TRG) == FAC_1_TRG_PRESSED ? 1 : 0;
 | 
				
			||||||
 | 
					  keysPressed = +digitalRead(DIO_FAC_2_TRG) == FAC_2_TRG_PRESSED ? 1 : 0;
 | 
				
			||||||
 | 
					  keysPressed = +digitalRead(DIO_FAC_3_TRG) == FAC_3_TRG_PRESSED ? 1 : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (keysPressed > 1)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    Serial.println("ERROR: More than one Flag active - setting no Faction active");
 | 
				
			||||||
 | 
					    PersistenceData.activeFaction = NONE;
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (digitalRead(DIO_FAC_1_TRG) == FAC_1_TRG_PRESSED)
 | 
				
			||||||
 | 
					    PersistenceData.activeFaction = FACTION_1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (digitalRead(DIO_FAC_2_TRG) == FAC_2_TRG_PRESSED)
 | 
				
			||||||
 | 
					    PersistenceData.activeFaction = FACTION_2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (digitalRead(DIO_FAC_3_TRG) == FAC_3_TRG_PRESSED)
 | 
				
			||||||
 | 
					    PersistenceData.activeFaction = FACTION_3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void powerMonitorTicker_callback()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // loadvoltage and percentage is global, because of battery Monitoring
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const int bat_min_2s = 680;
 | 
				
			||||||
 | 
					  const int bat_max_2s = 840;
 | 
				
			||||||
 | 
					  const int bat_min_3s = 1020;
 | 
				
			||||||
 | 
					  const int bat_max_3s = 1260;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float shuntvoltage = 0;
 | 
				
			||||||
 | 
					  float current_mA = 0;
 | 
				
			||||||
 | 
					  float busvoltage = 0;
 | 
				
			||||||
 | 
					  float power_mW = 0;
 | 
				
			||||||
 | 
					  int battery_level = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  shuntvoltage = ina219.getShuntVoltage_mV();
 | 
				
			||||||
 | 
					  busvoltage = ina219.getBusVoltage_V();
 | 
				
			||||||
 | 
					  current_mA = ina219.getCurrent_mA();
 | 
				
			||||||
 | 
					  power_mW = ina219.getPower_mW();
 | 
				
			||||||
 | 
					  globals.loadvoltage = busvoltage + (shuntvoltage / 1000);
 | 
				
			||||||
 | 
					  switch (ConfigData.batteryType)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					  case BATTERY_LIPO_2S:
 | 
				
			||||||
 | 
					    battery_level = map(globals.loadvoltage * 100, bat_min_2s, bat_max_2s, 0, 100);
 | 
				
			||||||
 | 
					    globals.battery_level = battery_level < 0 ? 0 : battery_level;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case BATTERY_LIPO_3S:
 | 
				
			||||||
 | 
					    battery_level = map(globals.loadvoltage * 100, bat_min_3s, bat_max_3s, 0, 100);
 | 
				
			||||||
 | 
					    globals.battery_level = battery_level < 0 ? 0 : battery_level;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    globals.battery_level = -1;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Serial.printf("Battery Level: %d %%\n", globals.battery_level);
 | 
				
			||||||
 | 
					  Serial.printf("Bus Voltage: %f V\n", busvoltage);
 | 
				
			||||||
 | 
					  Serial.printf("Shunt Voltage: %f mV\n", shuntvoltage);
 | 
				
			||||||
 | 
					  Serial.printf("Load Voltage: %f V\n", globals.loadvoltage);
 | 
				
			||||||
 | 
					  Serial.printf("Current: %f mA\n", current_mA);
 | 
				
			||||||
 | 
					  Serial.printf("Power: %f mW\n", power_mW);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EEPROMCyclicPDS_callback()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  StorePersistence_EEPROM();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
 | 
					void wifiMaintainConnectionTicker_callback()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static uint32_t WiFiFailCount = 0;
 | 
				
			||||||
 | 
					  const uint32_t WiFiFailMax = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    if (WiFiFailCount < WiFiFailMax)
 | 
				
			||||||
 | 
					      WiFiFailCount++;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      toggleWiFiAP(false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void toggleWiFiAP(boolean shutdown)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (WiFi.getMode() != WIFI_OFF && shutdown == true)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    WiFi.mode(WIFI_OFF);
 | 
				
			||||||
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
 | 
					    WiFiMaintainConnectionTicker.stop();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else if (shutdown == false)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    WiFi.mode(WIFI_AP);
 | 
				
			||||||
 | 
					    WiFi.softAPConfig(IPAddress(WIFI_AP_IP_GW), IPAddress(WIFI_AP_IP_GW), IPAddress(255, 255, 255, 0));
 | 
				
			||||||
 | 
					    WiFi.softAP(globals.DeviceName_ID, QUOTE(WIFI_AP_PASSWORD));
 | 
				
			||||||
 | 
					#ifdef WIFI_CLIENT
 | 
				
			||||||
 | 
					    WiFiMaintainConnectionTicker.stop();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SystemShutdown()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  StoreConfig_EEPROM();
 | 
				
			||||||
 | 
					  ESP.restart();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SetBatteryType(batteryType_t type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (ConfigData.batteryType != type)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    ConfigData.batteryType = type;
 | 
				
			||||||
 | 
					    globals.requestEEAction = EE_CFG_SAVE;
 | 
				
			||||||
 | 
					    Serial.printf("Set Batterytype to %s\n", type == BATTERY_LIPO_2S ? "2s Lipo" : "3s LiPo");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ProcessKeyCombos()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  typedef enum
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    KEY_PRESSED,
 | 
				
			||||||
 | 
					    KEY_RELEASED
 | 
				
			||||||
 | 
					  } keyStatus_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static keyStatus_t keyStatus_Fac1 = KEY_RELEASED;
 | 
				
			||||||
 | 
					  static uint8_t keyCount_Fac1 = 0;
 | 
				
			||||||
 | 
					  static keyStatus_t keyStatus_Fac2 = KEY_RELEASED;
 | 
				
			||||||
 | 
					  static uint8_t keyCount_Fac2 = 0;
 | 
				
			||||||
 | 
					  static keyStatus_t keyStatus_Fac3 = KEY_RELEASED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (digitalRead(DIO_FAC_3_TRG) == FAC_3_TRG_PRESSED)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    keyStatus_Fac3 = KEY_PRESSED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Process FactionKey 1 ComboCounter
 | 
				
			||||||
 | 
					    if (digitalRead(DIO_FAC_1_TRG) == FAC_1_TRG_PRESSED && keyStatus_Fac1 == KEY_RELEASED)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      keyStatus_Fac1 = KEY_PRESSED;
 | 
				
			||||||
 | 
					      keyCount_Fac1++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (digitalRead(DIO_FAC_1_TRG) != FAC_1_TRG_PRESSED)
 | 
				
			||||||
 | 
					      keyStatus_Fac1 = KEY_RELEASED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Process FactionKey 2 ComboCounter
 | 
				
			||||||
 | 
					    if (digitalRead(DIO_FAC_2_TRG) == FAC_2_TRG_PRESSED && keyStatus_Fac2 == KEY_RELEASED)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      keyStatus_Fac2 = KEY_PRESSED;
 | 
				
			||||||
 | 
					      keyCount_Fac2++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (digitalRead(DIO_FAC_2_TRG) != FAC_2_TRG_PRESSED)
 | 
				
			||||||
 | 
					      keyStatus_Fac2 = KEY_RELEASED;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (digitalRead(DIO_FAC_3_TRG) != FAC_3_TRG_PRESSED && keyStatus_Fac3 == KEY_PRESSED)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    Serial.printf("KeyCombo 1: %d | 2: %d\n", keyCount_Fac1, keyCount_Fac2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (keyCount_Fac1 == 2 && keyCount_Fac2 == 2)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      Serial.println("KeyCombo: WiFi AP ON");
 | 
				
			||||||
 | 
					      OverrideDisplay(sevenSeg_net, 5000);
 | 
				
			||||||
 | 
					      toggleWiFiAP(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    keyCount_Fac1 = 0;
 | 
				
			||||||
 | 
					    keyCount_Fac2 = 0;
 | 
				
			||||||
 | 
					    keyStatus_Fac1 = KEY_RELEASED;
 | 
				
			||||||
 | 
					    keyStatus_Fac2 = KEY_RELEASED;
 | 
				
			||||||
 | 
					    keyStatus_Fac3 = KEY_RELEASED;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void OverrideDisplay(const uint8_t *message, uint32_t time)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  DisplayOverrideFlag = millis() + time;
 | 
				
			||||||
 | 
					  DisplayOverrideValue = (uint8_t *)message;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t getESPChipID()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  uint32_t chipId;
 | 
				
			||||||
 | 
					  for (int i = 0; i < 17; i = i + 8)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return chipId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										209
									
								
								src/webui.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,209 @@
 | 
				
			|||||||
 | 
					#include "webui.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AsyncWebServer webServer(80);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  RESPMSG_HIDE,
 | 
				
			||||||
 | 
					  RESPMSG_SUCCESS,
 | 
				
			||||||
 | 
					  RESPMSG_INFO,
 | 
				
			||||||
 | 
					  RESPMSG_WARNING,
 | 
				
			||||||
 | 
					  RESPMSG_DANGER
 | 
				
			||||||
 | 
					} statusResponseMessage_Type_t;
 | 
				
			||||||
 | 
					char StatusResponseMessage[64];
 | 
				
			||||||
 | 
					statusResponseMessage_Type_t StatusResponseMessage_Type = RESPMSG_INFO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String processor(const String &var);
 | 
				
			||||||
 | 
					void WebserverPOST_Callback(AsyncWebServerRequest *request);
 | 
				
			||||||
 | 
					void WebserverNotFound_Callback(AsyncWebServerRequest *request);
 | 
				
			||||||
 | 
					void Webserver_Callback(AsyncWebServerRequest *request);
 | 
				
			||||||
 | 
					void WebserverCommands_Callback(String input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void initWebUI()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (!LittleFS.begin())
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    Serial.println("An Error has occurred while mounting LittleFS");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  webServer.serveStatic("/static/", LittleFS, "/static/").setCacheControl("max-age=360000");
 | 
				
			||||||
 | 
					  webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
 | 
				
			||||||
 | 
					               { request->redirect("/index.htm"); });
 | 
				
			||||||
 | 
					  webServer.onNotFound(WebserverNotFound_Callback);
 | 
				
			||||||
 | 
					  webServer.on("/index.htm", HTTP_GET, Webserver_Callback);
 | 
				
			||||||
 | 
					  webServer.on("/index.htm", HTTP_POST, WebserverPOST_Callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  webServer.begin();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String processor(const String &var)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (var == "SHOW_DTC_TABLE")
 | 
				
			||||||
 | 
					    return globals.systemStatus == sysStat_Error ? "" : "hidden";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "SHOW_RESP_MESSAGE")
 | 
				
			||||||
 | 
					    return StatusResponseMessage_Type != RESPMSG_HIDE ? "" : "hidden";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "RESP_MESSAGE_TYPE")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    switch (StatusResponseMessage_Type)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    case RESPMSG_SUCCESS:
 | 
				
			||||||
 | 
					      return "success";
 | 
				
			||||||
 | 
					    case RESPMSG_INFO:
 | 
				
			||||||
 | 
					      return "info";
 | 
				
			||||||
 | 
					    case RESPMSG_WARNING:
 | 
				
			||||||
 | 
					      return "warning";
 | 
				
			||||||
 | 
					    case RESPMSG_DANGER:
 | 
				
			||||||
 | 
					      return "danger";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "info";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "RESP_MESSAGE")
 | 
				
			||||||
 | 
					    return String(StatusResponseMessage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "BAT_REMAIN_CAPACITY")
 | 
				
			||||||
 | 
					    return String(globals.battery_level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "DEVICE_NAME")
 | 
				
			||||||
 | 
					    return String(globals.DeviceName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "BAT_VOLTAGE")
 | 
				
			||||||
 | 
					    return String(globals.loadvoltage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "DTC_TABLE")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    String temp;
 | 
				
			||||||
 | 
					    char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if (DTCStorage[i].Number > 0)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d",
 | 
				
			||||||
 | 
					                DTCStorage[i].timestamp / 86400000,    // Days
 | 
				
			||||||
 | 
					                DTCStorage[i].timestamp / 360000 % 24, // Hours
 | 
				
			||||||
 | 
					                DTCStorage[i].timestamp / 60000 % 60,  // Minutes
 | 
				
			||||||
 | 
					                DTCStorage[i].timestamp / 1000 % 60,   // Seconds
 | 
				
			||||||
 | 
					                DTCStorage[i].timestamp % 1000);       // milliseconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        temp = "<tr><td>" + String(buff_timestamp);
 | 
				
			||||||
 | 
					        temp = temp + "</td><td>" + String(DTCStorage[i].Number) + "</td><td>";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (DTCStorage[i].active == DTC_ACTIVE)
 | 
				
			||||||
 | 
					          temp = temp + "active";
 | 
				
			||||||
 | 
					        else if (DTCStorage[i].active == DTC_PREVIOUS)
 | 
				
			||||||
 | 
					          temp = temp + "previous";
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          temp = temp + "none";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        temp = temp + "</td></tr>";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return temp;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "PLACEHOLDER")
 | 
				
			||||||
 | 
					    return "placeholder";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "POINTS_FAC_1")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    char buff[12];
 | 
				
			||||||
 | 
					    snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_1_timer / 3600, (PersistenceData.faction_1_timer / 60) % 60, PersistenceData.faction_1_timer % 60);
 | 
				
			||||||
 | 
					    return String(buff);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "POINTS_FAC_2")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    char buff[12];
 | 
				
			||||||
 | 
					    snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_2_timer / 3600, (PersistenceData.faction_2_timer / 60) % 60, PersistenceData.faction_2_timer % 60);
 | 
				
			||||||
 | 
					    return String(buff);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "POINTS_FAC_3")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    char buff[12];
 | 
				
			||||||
 | 
					    snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_3_timer / 3600, (PersistenceData.faction_3_timer / 60) % 60, PersistenceData.faction_3_timer % 60);
 | 
				
			||||||
 | 
					    return String(buff);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "STATUS_FAC_1")
 | 
				
			||||||
 | 
					    return PersistenceData.activeFaction == FACTION_1 ? "ACTIVE" : "INACTIVE";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "STATUS_FAC_2")
 | 
				
			||||||
 | 
					    return PersistenceData.activeFaction == FACTION_2 ? "ACTIVE" : "INACTIVE";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "STATUS_FAC_3")
 | 
				
			||||||
 | 
					    return PersistenceData.activeFaction == FACTION_3 ? "ACTIVE" : "INACTIVE";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "NAME_FAC_1")
 | 
				
			||||||
 | 
					    return FACTION_1_NAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "NAME_FAC_2")
 | 
				
			||||||
 | 
					    return FACTION_2_NAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "NAME_FAC_3")
 | 
				
			||||||
 | 
					    return FACTION_3_NAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "TITLE")
 | 
				
			||||||
 | 
					    return DEVICE_NAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (var == "BATTERY_LEVEL")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    return String(globals.battery_level);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (var == "BATTERY_TYPE")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    return String(BatteryString[ConfigData.batteryType]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (var == "BATTERY_VOLTAGE")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    return String(globals.loadvoltage);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return String();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Webserver_Callback(AsyncWebServerRequest *request)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  request->send(LittleFS, "/index.htm", "text/html", false, processor);
 | 
				
			||||||
 | 
					  StatusResponseMessage_Type = RESPMSG_HIDE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void WebserverPOST_Callback(AsyncWebServerRequest *request)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  int paramsNr = request->params();
 | 
				
			||||||
 | 
					  for (int i = 0; i < paramsNr; i++)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    AsyncWebParameter *p = request->getParam(i);
 | 
				
			||||||
 | 
					    if (p->name() == "commandInput")
 | 
				
			||||||
 | 
					      WebserverCommands_Callback(p->value());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  request->send(LittleFS, "/index.htm", "text/html", false, processor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void WebserverNotFound_Callback(AsyncWebServerRequest *request)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  request->send(404, "text/html", "Not found");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void WebserverCommands_Callback(String input)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  String command = input.substring(0, input.indexOf(' '));
 | 
				
			||||||
 | 
					  command.toUpperCase();
 | 
				
			||||||
 | 
					  StatusResponseMessage_Type = RESPMSG_HIDE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (command == "RESET")
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    strcpy(StatusResponseMessage, "Counter Reset done");
 | 
				
			||||||
 | 
					    StatusResponseMessage_Type = RESPMSG_SUCCESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PersistenceData.faction_1_timer = 0;
 | 
				
			||||||
 | 
					    PersistenceData.faction_2_timer = 0;
 | 
				
			||||||
 | 
					    PersistenceData.faction_3_timer = 0;
 | 
				
			||||||
 | 
					    PersistenceData.activeFaction = NONE;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/webui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					#ifndef _WEBUI_H_
 | 
				
			||||||
 | 
					#define _WEBUI_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Arduino.h>
 | 
				
			||||||
 | 
					#include <FS.h>
 | 
				
			||||||
 | 
					#include <LittleFS.h>
 | 
				
			||||||
 | 
					#include <AsyncTCP.h>
 | 
				
			||||||
 | 
					#include <ESPAsyncWebServer.h>
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "globals.h"
 | 
				
			||||||
 | 
					#include "dtc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void initWebUI();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										5
									
								
								wifi_credentials.example.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					[wifi_cred]
 | 
				
			||||||
 | 
					wifi_ap_password = wifiappass
 | 
				
			||||||
 | 
					wifi_ssid = wifi-ssid
 | 
				
			||||||
 | 
					wifi_password = wifi-pass
 | 
				
			||||||
 | 
					admin_password = ota-password
 | 
				
			||||||