PsychichHTTP v2-dev
This commit is contained in:
@@ -1,66 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PsychicHTTP Demo</title>
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Basic Request Examples</h1>
|
||||
<ul>
|
||||
<li><a href="/api?foo=bar">API Call</a></li>
|
||||
<li><a href="/redirect">Redirection</a></li>
|
||||
<li><a href="/auth-digest">Authentication (Digest)</a></li>
|
||||
<li><a href="/auth-basic">Authentication (Basic)</a></li>
|
||||
<li><a href="/cookies">Cookies Demo</a></li>
|
||||
<li><a href="/404">404</a></li>
|
||||
</ul>
|
||||
|
||||
<h1>Static Serving</h1>
|
||||
<p>
|
||||
<a href="/alien.png"><img width="60" src="/alien.png"></a>
|
||||
<a href="/img/request_flow.png"><img width="60" src="/img/request_flow.png"></a>
|
||||
</p>
|
||||
<p><a href="/myfile.txt">Text File</a></p>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PsychicHTTP Demo</title>
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<h1>Simple POST Form</h1>
|
||||
<form action="/post" method="post">
|
||||
<label for="param1">Parameter 1:</label>
|
||||
<input type="text" id="param1" name="param1" value="Parameter 1">
|
||||
<br>
|
||||
<label for="param2">Parameter 2:</label>
|
||||
<input type="text" id="param2" name="param2" value="Parameter 2">
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Basic Request Examples</h1>
|
||||
<ul>
|
||||
<li><a href="/api?foo=bar">API Call</a></li>
|
||||
<li><a href="/redirect">Redirection</a></li>
|
||||
<li><a href="/auth-digest">Authentication (Digest)</a></li>
|
||||
<li><a href="/auth-basic">Authentication (Basic)</a></li>
|
||||
<li><a href="/cookies">Cookies Demo</a></li>
|
||||
<li><a href="/404">404</a></li>
|
||||
</ul>
|
||||
|
||||
<h1>Basic File Upload</h1>
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<label for="newfile">Upload a file</label>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<input id="newfile" type="file" onchange="setpath()" style="width:100%;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="filepath">Set path on server</label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="filepath" type="text" style="width:100%;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button id="upload" type="button" onclick="upload()">Upload</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<script>
|
||||
<h1>Utilities</h1>
|
||||
<ul>
|
||||
<li><a href="/websocket-test.html">WebSocket Tester</a></li>
|
||||
</ul>
|
||||
|
||||
<h1>Static Serving</h1>
|
||||
<p>
|
||||
<a href="/alien.png"><img width="60" src="/alien.png"></a>
|
||||
<a href="/img/request_flow.png"><img width="60" src="/img/request_flow.png"></a>
|
||||
</p>
|
||||
<p><a href="/myfile.txt">Text File</a></p>
|
||||
|
||||
<h1>Simple POST Form</h1>
|
||||
<form action="/post" method="post">
|
||||
<label for="param1">Parameter 1:</label>
|
||||
<input type="text" id="param1" name="param1" value="Parameter 1">
|
||||
<br>
|
||||
<label for="param2">Parameter 2:</label>
|
||||
<input type="text" id="param2" name="param2" value="Parameter 2">
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
|
||||
<h1>Basic File Upload</h1>
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<label for="newfile">Upload a file</label>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<input id="newfile" type="file" onchange="setpath()" style="width:100%;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="filepath">Set path on server</label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="filepath" type="text" style="width:100%;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button id="upload" type="button" onclick="upload()">Upload</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<script>
|
||||
function setpath() {
|
||||
var default_path = document.getElementById("newfile").files[0].name;
|
||||
document.getElementById("filepath").value = default_path;
|
||||
@@ -72,7 +79,7 @@
|
||||
|
||||
/* Max size of an individual file. Make sure this
|
||||
* value is same as that set in file_server.c */
|
||||
var MAX_FILE_SIZE = 2048*1024;
|
||||
var MAX_FILE_SIZE = 2048 * 1024;
|
||||
var MAX_FILE_SIZE_STR = "2MB";
|
||||
|
||||
if (fileInput.length == 0) {
|
||||
@@ -81,9 +88,9 @@
|
||||
alert("File path on server is not set!");
|
||||
} else if (filePath.indexOf(' ') >= 0) {
|
||||
alert("File path on server cannot have spaces!");
|
||||
} else if (filePath[filePath.length-1] == '/') {
|
||||
} else if (filePath[filePath.length - 1] == '/') {
|
||||
alert("File name not specified after path!");
|
||||
} else if (fileInput[0].size > 200*1024) {
|
||||
} else if (fileInput[0].size > 200 * 1024) {
|
||||
alert("File size must be less than 200KB!");
|
||||
} else {
|
||||
document.getElementById("newfile").disabled = true;
|
||||
@@ -92,7 +99,7 @@
|
||||
|
||||
var file = fileInput[0];
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
xhttp.onreadystatechange = function () {
|
||||
if (xhttp.readyState == 4) {
|
||||
if (xhttp.status == 200) {
|
||||
document.open();
|
||||
@@ -111,126 +118,125 @@
|
||||
xhttp.send(file);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<h1>Multipart POST Form</h1>
|
||||
<form action="/multipart" method="post" enctype="multipart/form-data">
|
||||
<label for="param1">Parameter 1:</label>
|
||||
<input type="text" id="param1" name="param1" value="Parameter 1">
|
||||
<br>
|
||||
<h1>Multipart POST Form</h1>
|
||||
<form action="/multipart" method="post" enctype="multipart/form-data">
|
||||
<label for="param1">Parameter 1:</label>
|
||||
<input type="text" id="param1" name="param1" value="Parameter 1">
|
||||
<br>
|
||||
|
||||
<label for="param2">Parameter 2:</label>
|
||||
<input type="text" id="param2" name="param2" value="Parameter 2">
|
||||
<br>
|
||||
<label for="param2">Parameter 2:</label>
|
||||
<input type="text" id="param2" name="param2" value="Parameter 2">
|
||||
<br>
|
||||
|
||||
<label for="file-upload">File Upload:</label>
|
||||
<input type="file" id="file-upload" name="file_upload" accept=".txt, .html, .pdf, .png, .jpg, .gif" required>
|
||||
<br>
|
||||
|
||||
<input type="submit" value="Upload File">
|
||||
</form>
|
||||
<label for="file-upload">File Upload:</label>
|
||||
<input type="file" id="file-upload" name="file_upload" accept=".txt, .html, .pdf, .png, .jpg, .gif"
|
||||
required>
|
||||
<br>
|
||||
|
||||
<h1>Websocket Demo</h1>
|
||||
<input type="text" id="message_input" placeholder="Type your message">
|
||||
<button onclick="sendMessage()">Send Message</button>
|
||||
<button onclick="websocketConnect()">Connect</button>
|
||||
<div>
|
||||
<textarea id="websocket_output" style="width: 50%; height: 250px;"></textarea>
|
||||
</div>
|
||||
<input type="submit" value="Upload File">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
let socket;
|
||||
const outputText = document.getElementById('websocket_output');
|
||||
const messageInput = document.getElementById('message_input');
|
||||
<h1>Websocket Demo</h1>
|
||||
<input type="text" id="message_input" placeholder="Type your message">
|
||||
<button onclick="sendMessage()">Send Message</button>
|
||||
<button onclick="websocketConnect()">Connect</button>
|
||||
<div>
|
||||
<textarea id="websocket_output" style="width: 50%; height: 250px;"></textarea>
|
||||
</div>
|
||||
|
||||
function websocketConnect()
|
||||
{
|
||||
// Create a WebSocket connection
|
||||
socket = new WebSocket((location.protocol === "https:" ? "wss://" : "ws://") + window.location.host + "/ws");
|
||||
|
||||
// Event handler for when the WebSocket connection is open
|
||||
socket.addEventListener('open', (event) => {
|
||||
outputText.value += `[socket] connection opened!\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when a message is received from the WebSocket server
|
||||
socket.addEventListener('message', (event) => {
|
||||
// Echo the received message into the output div
|
||||
let data = event.data.trim();
|
||||
outputText.value += `[received] ${data}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when an error occurs with the WebSocket connection
|
||||
socket.addEventListener('error', (event) => {
|
||||
let data = event.data.trim();
|
||||
outputText.value += `[error] ${event.data}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when the WebSocket connection is closed
|
||||
socket.addEventListener('close', (event) => {
|
||||
outputText.value += `[socket] connection closed!\n`;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to send a message to the WebSocket server
|
||||
function sendMessage() {
|
||||
if (socket.readyState == WebSocket.OPEN) {
|
||||
const message = messageInput.value.trim();
|
||||
if (message) {
|
||||
socket.send(message);
|
||||
messageInput.value = ''; // Clear the input field after sending the message
|
||||
outputText.value += `[sent] ${message}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outputText.value += `[error] Not Connected\n`;
|
||||
<script>
|
||||
let socket;
|
||||
const outputText = document.getElementById('websocket_output');
|
||||
const messageInput = document.getElementById('message_input');
|
||||
|
||||
function websocketConnect() {
|
||||
// Create a WebSocket connection
|
||||
socket = new WebSocket((location.protocol === "https:" ? "wss://" : "ws://") + window.location.host + "/ws");
|
||||
|
||||
// Event handler for when the WebSocket connection is open
|
||||
socket.addEventListener('open', (event) => {
|
||||
outputText.value += `[socket] connection opened!\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when a message is received from the WebSocket server
|
||||
socket.addEventListener('message', (event) => {
|
||||
// Echo the received message into the output div
|
||||
let data = event.data.trim();
|
||||
outputText.value += `[received] ${data}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when an error occurs with the WebSocket connection
|
||||
socket.addEventListener('error', (event) => {
|
||||
let data = event.data.trim();
|
||||
outputText.value += `[error] ${event.data}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when the WebSocket connection is closed
|
||||
socket.addEventListener('close', (event) => {
|
||||
outputText.value += `[socket] connection closed!\n`;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to send a message to the WebSocket server
|
||||
function sendMessage() {
|
||||
if (socket.readyState == WebSocket.OPEN) {
|
||||
const message = messageInput.value.trim();
|
||||
if (message) {
|
||||
socket.send(message);
|
||||
messageInput.value = ''; // Clear the input field after sending the message
|
||||
outputText.value += `[sent] ${message}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>EventSource Demo</h1>
|
||||
<button onclick="eventSourceConnect()">Connect</button>
|
||||
<div>
|
||||
<textarea id="eventsource_output" style="width: 50%; height: 250px;"></textarea>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const dataElement = document.getElementById('eventsource_output');
|
||||
|
||||
function eventSourceConnect()
|
||||
{
|
||||
const eventSource = new EventSource('/events');
|
||||
|
||||
eventSource.onopen = () => {
|
||||
dataElement.value += `[connected]\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
};
|
||||
|
||||
eventSource.addEventListener('millis', (event) => {
|
||||
let data = event.data.trim()
|
||||
dataElement.value += `[millis] ${data}\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
});
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
let data = event.data.trim()
|
||||
dataElement.value += `[message] ${data}\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
};
|
||||
|
||||
eventSource.onerror = (error) => {
|
||||
dataElement.value += `[error] ${error}\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
eventSource.close();
|
||||
};
|
||||
else {
|
||||
outputText.value += `[error] Not Connected\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
}
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>EventSource Demo</h1>
|
||||
<button onclick="eventSourceConnect()">Connect</button>
|
||||
<div>
|
||||
<textarea id="eventsource_output" style="width: 50%; height: 250px;"></textarea>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const dataElement = document.getElementById('eventsource_output');
|
||||
|
||||
function eventSourceConnect() {
|
||||
const eventSource = new EventSource('/events');
|
||||
|
||||
eventSource.onopen = () => {
|
||||
dataElement.value += `[connected]\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
};
|
||||
|
||||
eventSource.addEventListener('millis', (event) => {
|
||||
let data = event.data.trim()
|
||||
dataElement.value += `[millis] ${data}\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
});
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
let data = event.data.trim()
|
||||
dataElement.value += `[message] ${data}\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
};
|
||||
|
||||
eventSource.onerror = (error) => {
|
||||
dataElement.value += `[error] ${error}\n`;
|
||||
dataElement.scrollTop = dataElement.scrollHeight;
|
||||
eventSource.close();
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
135
lib/PsychicHttp/examples/platformio/data/www/websocket-test.html
Normal file
135
lib/PsychicHttp/examples/platformio/data/www/websocket-test.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebSocket Message Rate Test</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>WebSocket Message Rate Test</h1>
|
||||
<p>Time Remaining: <span id="time_remaining">0</span></p>
|
||||
<p>Messages Count: <span id="message_count">0</span></p>
|
||||
<p>Messages per second: <span id="rate">0</span></p>
|
||||
<label for="duration">Test Duration (seconds):</label>
|
||||
<input type="number" id="duration" value="30" min="1">
|
||||
<p>
|
||||
<button id="startTestSmall">Start Test (256b json)</button>
|
||||
<button id="startTestBig">Start Test (2k json)</button>
|
||||
</p>
|
||||
<p id="status"></p>
|
||||
|
||||
<script>
|
||||
let ws;
|
||||
let messageCount = 0;
|
||||
let startTime;
|
||||
let endTime;
|
||||
let testRunning = false;
|
||||
let smallPayload = { "user": { "id": 123456789, "name": "JohnDoe", "email": "johndoe@example.com", "preferences": { "theme": "dark", "notifications": { "email": true, "sms": false }, "language": "en" } } };
|
||||
let bigPayload = { "user": { "id": 123456789, "name": "JohnDoe", "email": "johndoe@example.com", "preferences": { "theme": "dark", "notifications": { "email": true, "sms": false }, "language": "en", "options": { "option1": "value1", "option2": "value2", "option3": "value3", "option4": "value4", "option5": "value5", "option6": "value6", "option7": "value7", "option8": "value8", "option9": "value9", "option10": "value10", "option11": "value11", "option12": "value12", "option13": "value13", "option14": "value14", "option15": "value15", "option16": "value16", "option17": "value17", "option18": "value18", "option19": "value19", "option20": "value20", "option21": "value21", "option22": "value22", "option23": "value23", "option24": "value24", "option25": "value25", "option26": "value26", "option27": "value27", "option28": "value28", "option29": "value29", "option30": "value30", "option31": "value31", "option32": "value32", "option33": "value33", "option34": "value34", "option35": "value35", "option36": "value36", "option37": "value37", "option38": "value38", "option39": "value39", "option40": "value40", "option41": "value41", "option42": "value42", "option43": "value43", "option44": "value44", "option45": "value45", "option46": "value46", "option47": "value47", "option48": "value48", "option49": "value49", "option50": "value50", "option51": "value51", "option52": "value52", "option53": "value53", "option54": "value54", "option55": "value55", "option56": "value56", "option57": "value57", "option58": "value58", "option59": "value59", "option60": "value60", "option61": "value61", "option62": "value62", "option63": "value63", "option64": "value64", "option65": "value65", "option66": "value66", "option67": "value67", "option68": "value68", "option69": "value69", "option70": "value70", "option71": "value71", "option72": "value72", "option73": "value73", "option74": "value74", "option75": "value75", "option76": "value76", "option77": "value77", "option78": "value78", "option79": "value79", "option80": "value80", "option81": "value81", "option82": "value82", "option83": "value83", "option84": "value84", "option85": "value85", "option86": "value86", "option87": "value87", "option88": "value88", "option89": "value89", "option90": "value90", "option91": "value91", "option92": "value92", "option93": "value93", "option94": "value94", "option95": "value95", "option96": "value96", "option97": "value97", "option98": "value98", "option99": "value99", "option100": "value100", "option101": "value101", "option102": "value102", "option103": "value103", "option104": "value104", "option105": "value105", "option106": "value106", "option107": "value107", "option108": "value108", "option109": "value109", "option110": "value110", "option111": "value111", "option112": "value112", "option113": "value113", "option114": "value114", "option115": "value115", "option116": "value116", "option117": "value117", "option118": "value118", "option119": "value119", "option120": "value120" } } } };
|
||||
let payload;
|
||||
|
||||
// Function to update the message rate
|
||||
function updateRate(force = false) {
|
||||
const currentTime = Date.now();
|
||||
const elapsedTime = (currentTime - startTime) / 1000; // in seconds
|
||||
let remainingTime = (endTime - currentTime) / 1000; // in seconds
|
||||
remainingTime = Math.max(0, remainingTime);
|
||||
const rate = messageCount / elapsedTime;
|
||||
|
||||
if (rate) {
|
||||
document.getElementById('rate').innerText = rate.toFixed(2);
|
||||
document.getElementById('message_count').innerText = messageCount;
|
||||
document.getElementById('time_remaining').innerText = remainingTime.toFixed(2);
|
||||
}
|
||||
|
||||
if (testRunning)
|
||||
setTimeout(updateRate, 25);
|
||||
}
|
||||
|
||||
function startTestSmall() {
|
||||
payload = smallPayload;
|
||||
startTest();
|
||||
}
|
||||
|
||||
function startTestBig() {
|
||||
payload = bigPayload;
|
||||
startTest();
|
||||
}
|
||||
|
||||
// Function to start the WebSocket connection and the test
|
||||
function startTest() {
|
||||
if (testRunning) return;
|
||||
|
||||
console.log("Payload length: " + JSON.stringify(payload).length);
|
||||
|
||||
document.getElementById('startTestSmall').disabled = true;
|
||||
document.getElementById('startTestBig').disabled = true;
|
||||
|
||||
document.getElementById('status').innerText = 'Connecting';
|
||||
|
||||
const durationInput = document.getElementById('duration').value;
|
||||
const testDuration = parseInt(durationInput) * 1000 || 60000; // default to 60 seconds if invalid
|
||||
|
||||
// Determine the WebSocket protocol based on the current protocol
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
ws = new WebSocket(`${protocol}${window.location.host}/ws`);
|
||||
|
||||
ws.onopen = function () {
|
||||
document.getElementById('status').innerText = 'Connected';
|
||||
startTime = Date.now();
|
||||
endTime = startTime + testDuration;
|
||||
messageCount = 0;
|
||||
testRunning = true;
|
||||
sendAndReceiveMessage();
|
||||
|
||||
updateRate();
|
||||
};
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
try {
|
||||
const parsedData = JSON.parse(event.data);
|
||||
|
||||
if (parsedData.user) {
|
||||
messageCount++;
|
||||
if (testRunning) {
|
||||
sendAndReceiveMessage();
|
||||
}
|
||||
}
|
||||
} catch (err) { }
|
||||
};
|
||||
|
||||
ws.onerror = function (error) {
|
||||
document.getElementById('status').innerText = 'Error: ' + error.message;
|
||||
};
|
||||
|
||||
ws.onclose = function () {
|
||||
//document.getElementById('status').innerText = 'Connection Closed';
|
||||
};
|
||||
}
|
||||
|
||||
// Function to send a message and wait for the response
|
||||
function sendAndReceiveMessage() {
|
||||
if (Date.now() >= endTime) {
|
||||
testRunning = false;
|
||||
document.getElementById('startTestSmall').disabled = false;
|
||||
document.getElementById('startTestBig').disabled = false;
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.close();
|
||||
}
|
||||
document.getElementById('status').innerText = 'Test Finished';
|
||||
updateRate(true);
|
||||
} else {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify(payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('startTestSmall').addEventListener('click', startTestSmall);
|
||||
document.getElementById('startTestBig').addEventListener('click', startTestBig);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
@@ -12,19 +12,44 @@
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32-s3-devkitc-1
|
||||
upload_port = /dev/ttyACM0
|
||||
monitor_port = /dev/ttyACM1
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
lib_deps =
|
||||
; devmode: with this disabled make a symlink from platformio/lib to the PsychicHttp directory
|
||||
;hoeken/PsychicHttp
|
||||
bblanchon/ArduinoJson
|
||||
; hoeken/PsychicHttp
|
||||
; PIO is not able to consider installed project in CI
|
||||
;../..
|
||||
board_build.filesystem = littlefs
|
||||
|
||||
[env:default]
|
||||
build_flags =
|
||||
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN
|
||||
;-D ENABLE_ASYNC
|
||||
-Wall
|
||||
-Wextra
|
||||
|
||||
; [env:arduino3]
|
||||
; platform = https://github.com/platformio/platform-espressif32.git
|
||||
; platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#master
|
||||
[env:arduino2]
|
||||
platform = espressif32@6.8.1
|
||||
|
||||
[env:arduino2-ssl]
|
||||
platform = espressif32@6.8.1
|
||||
build_flags = -D PSY_ENABLE_SSL
|
||||
|
||||
[env:arduino2-regex]
|
||||
platform = espressif32@6.8.1
|
||||
build_flags = -D PSY_ENABLE_REGEX
|
||||
|
||||
[env:arduino3]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
|
||||
[env:arduino3-ssl]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
build_flags = -D PSY_ENABLE_SSL
|
||||
|
||||
[env:arduino3-regex]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
build_flags = -D PSY_ENABLE_REGEX
|
||||
|
||||
[env:waveshare-4-3-touchscreen]
|
||||
lib_deps = ${env.lib_deps}
|
||||
https://github.com/esp-arduino-libs/ESP32_IO_Expander
|
||||
build_flags =
|
||||
-D PSY_ENABLE_SDCARD
|
||||
-D WAVESHARE_43_TOUCH
|
||||
|
||||
@@ -9,66 +9,113 @@
|
||||
*/
|
||||
|
||||
/**********************************************************************************************
|
||||
* Note: this demo relies on various files to be uploaded on the LittleFS partition
|
||||
* PlatformIO -> Build Filesystem Image and then PlatformIO -> Upload Filesystem Image
|
||||
**********************************************************************************************/
|
||||
* Note: this demo relies on various files to be uploaded on the LittleFS partition
|
||||
* PlatformIO -> Build Filesystem Image and then PlatformIO -> Upload Filesystem Image
|
||||
**********************************************************************************************/
|
||||
|
||||
#include "_secret.h"
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <esp_sntp.h>
|
||||
#include "_secret.h"
|
||||
#include <LittleFS.h>
|
||||
#include <PsychicHttp.h>
|
||||
//#include <PsychicHttpsServer.h> //uncomment this to enable HTTPS / SSL
|
||||
#include <WiFi.h>
|
||||
#include <esp_sntp.h>
|
||||
|
||||
// #define this to enable SD card support
|
||||
#ifdef PSY_ENABLE_SDCARD
|
||||
|
||||
#ifdef WAVESHARE_43_TOUCH
|
||||
#include <ESP_IOExpander_Library.h>
|
||||
// Extend IO Pin define
|
||||
#define TP_RST 1
|
||||
#define LCD_BL 2
|
||||
#define LCD_RST 3
|
||||
#define SD_CS 4
|
||||
#define USB_SEL 5
|
||||
|
||||
// I2C Pin define
|
||||
#define I2C_MASTER_NUM I2C_NUM_0
|
||||
#define I2C_MASTER_SDA_IO 8
|
||||
#define I2C_MASTER_SCL_IO 9
|
||||
|
||||
#define SD_MOSI 11
|
||||
#define SD_CLK 12
|
||||
#define SD_MISO 13
|
||||
#define SD_SS -1
|
||||
#else
|
||||
#define SD_MOSI 11
|
||||
#define SD_CLK 12
|
||||
#define SD_MISO 13
|
||||
#define SD_SS 5
|
||||
#endif
|
||||
|
||||
#include <FS.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#endif
|
||||
|
||||
// #define this to enable SSL at build (or switch to the 'ssl' build target in vscode)
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
#include <PsychicHttpsServer.h>
|
||||
#endif
|
||||
|
||||
// debugging library
|
||||
#ifdef PSY_DEVMODE
|
||||
#include <ArduinoTrace.h>
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#error "You need to enter your wifi credentials. Rename secret.h to _secret.h and enter your credentials there."
|
||||
#endif
|
||||
|
||||
//Enter your WIFI credentials in secret.h
|
||||
const char *ssid = WIFI_SSID;
|
||||
const char *password = WIFI_PASS;
|
||||
// Enter your WIFI credentials in secret.h
|
||||
const char* ssid = WIFI_SSID;
|
||||
const char* password = WIFI_PASS;
|
||||
|
||||
// Set your SoftAP credentials
|
||||
const char *softap_ssid = "PsychicHttp";
|
||||
const char *softap_password = "";
|
||||
IPAddress softap_ip(10, 0, 0, 1);
|
||||
const char* softap_ssid = "PsychicHttp";
|
||||
const char* softap_password = "";
|
||||
IPAddress softap_ip(10, 0, 0, 1);
|
||||
|
||||
//credentials for the /auth-basic and /auth-digest examples
|
||||
const char *app_user = "admin";
|
||||
const char *app_pass = "admin";
|
||||
const char *app_name = "Your App";
|
||||
// credentials for the /auth-basic and /auth-digest examples
|
||||
const char* app_user = "admin";
|
||||
const char* app_pass = "admin";
|
||||
const char* app_name = "Your App";
|
||||
|
||||
//hostname for mdns (psychic.local)
|
||||
const char *local_hostname = "psychic";
|
||||
LoggingMiddleware loggingMiddleware;
|
||||
AuthenticationMiddleware basicAuth;
|
||||
AuthenticationMiddleware digestAuth;
|
||||
|
||||
//#define PSY_ENABLE_SSL to enable ssl
|
||||
// hostname for mdns (psychic.local)
|
||||
const char* local_hostname = "psychic";
|
||||
|
||||
// #define PSY_ENABLE_SSL to enable ssl
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
bool app_enable_ssl = true;
|
||||
String server_cert;
|
||||
String server_key;
|
||||
bool app_enable_ssl = true;
|
||||
String server_cert;
|
||||
String server_key;
|
||||
#endif
|
||||
|
||||
//our main server object
|
||||
// our main server object
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
PsychicHttpsServer server;
|
||||
PsychicHttpsServer server;
|
||||
#else
|
||||
PsychicHttpServer server;
|
||||
PsychicHttpServer server;
|
||||
#endif
|
||||
PsychicWebSocketHandler websocketHandler;
|
||||
PsychicEventSource eventSource;
|
||||
CorsMiddleware corsMiddleware;
|
||||
|
||||
//NTP server stuff
|
||||
const char *ntpServer1 = "pool.ntp.org";
|
||||
const char *ntpServer2 = "time.nist.gov";
|
||||
// NTP server stuff
|
||||
const char* ntpServer1 = "pool.ntp.org";
|
||||
const char* ntpServer2 = "time.nist.gov";
|
||||
const long gmtOffset_sec = 0;
|
||||
const int daylightOffset_sec = 0;
|
||||
struct tm timeinfo;
|
||||
|
||||
// Callback function (gets called when time adjusts via NTP)
|
||||
void timeAvailable(struct timeval *t)
|
||||
void timeAvailable(struct timeval* t)
|
||||
{
|
||||
if (!getLocalTime(&timeinfo)) {
|
||||
Serial.println("Failed to obtain time");
|
||||
@@ -83,10 +130,12 @@ void timeAvailable(struct timeval *t)
|
||||
|
||||
bool connectToWifi()
|
||||
{
|
||||
//dual client and AP mode
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
// WiFi.mode(WIFI_AP); // ap only mode
|
||||
// WiFi.mode(WIFI_STA); // client only mode
|
||||
WiFi.mode(WIFI_AP_STA); // ap and client
|
||||
|
||||
// Configure SoftAP
|
||||
// dual client and AP mode
|
||||
WiFi.softAPConfig(softap_ip, softap_ip, IPAddress(255, 255, 255, 0)); // subnet FF FF FF 00
|
||||
WiFi.softAP(softap_ssid, softap_password);
|
||||
IPAddress myIP = WiFi.softAPIP();
|
||||
@@ -107,10 +156,8 @@ bool connectToWifi()
|
||||
int numberOfTries = 20;
|
||||
|
||||
// Wait for the WiFi event
|
||||
while (true)
|
||||
{
|
||||
switch (WiFi.status())
|
||||
{
|
||||
while (true) {
|
||||
switch (WiFi.status()) {
|
||||
case WL_NO_SSID_AVAIL:
|
||||
Serial.println("[WiFi] SSID not found");
|
||||
break;
|
||||
@@ -140,15 +187,12 @@ bool connectToWifi()
|
||||
}
|
||||
delay(tryDelay);
|
||||
|
||||
if (numberOfTries <= 0)
|
||||
{
|
||||
if (numberOfTries <= 0) {
|
||||
Serial.print("[WiFi] Failed to connect to WiFi!");
|
||||
// Use disconnect function to force stop trying to connect
|
||||
WiFi.disconnect();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
numberOfTries--;
|
||||
}
|
||||
}
|
||||
@@ -156,198 +200,247 @@ bool connectToWifi()
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef PSY_ENABLE_SDCARD
|
||||
bool setupSDCard()
|
||||
{
|
||||
#ifdef WAVESHARE_43_TOUCH
|
||||
ESP_IOExpander* expander = new ESP_IOExpander_CH422G((i2c_port_t)I2C_MASTER_NUM, ESP_IO_EXPANDER_I2C_CH422G_ADDRESS_000, I2C_MASTER_SCL_IO, I2C_MASTER_SDA_IO);
|
||||
expander->init();
|
||||
expander->begin();
|
||||
expander->multiPinMode(TP_RST | LCD_BL | LCD_RST | SD_CS | USB_SEL, OUTPUT);
|
||||
expander->multiDigitalWrite(TP_RST | LCD_BL | LCD_RST, HIGH);
|
||||
|
||||
// use extend GPIO for SD card
|
||||
expander->digitalWrite(SD_CS, LOW);
|
||||
SPI.setHwCs(false);
|
||||
#endif
|
||||
|
||||
SPI.begin(SD_CLK, SD_MISO, SD_MOSI, SD_SS);
|
||||
if (!SD.begin()) {
|
||||
Serial.println("SD Card Mount Failed");
|
||||
return false;
|
||||
}
|
||||
uint8_t cardType = SD.cardType();
|
||||
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("No SD card attached");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.print("SD Card Type: ");
|
||||
if (cardType == CARD_MMC) {
|
||||
Serial.println("MMC");
|
||||
} else if (cardType == CARD_SD) {
|
||||
Serial.println("SDSC");
|
||||
} else if (cardType == CARD_SDHC) {
|
||||
Serial.println("SDHC");
|
||||
} else {
|
||||
Serial.println("UNKNOWN");
|
||||
}
|
||||
|
||||
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
|
||||
Serial.printf("SD Card Size: %lluMB\n", cardSize);
|
||||
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
|
||||
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void setup()
|
||||
{
|
||||
esp_log_level_set(PH_TAG, ESP_LOG_DEBUG);
|
||||
esp_log_level_set("httpd_uri", ESP_LOG_DEBUG);
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
Serial.printf("ESP-IDF Version: %s\n", esp_get_idf_version());
|
||||
|
||||
#ifdef ESP_ARDUINO_VERSION_STR
|
||||
Serial.printf("Arduino Version: %s\n", ESP_ARDUINO_VERSION_STR);
|
||||
#else
|
||||
Serial.printf("Arduino Version: %d.%d.%d\n", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH);
|
||||
#endif
|
||||
Serial.printf("PsychicHttp Version: %s\n", PSYCHIC_VERSION_STR);
|
||||
|
||||
// We start by connecting to a WiFi network
|
||||
// To debug, please enable Core Debug Level to Verbose
|
||||
if (connectToWifi())
|
||||
{
|
||||
//Setup our NTP to get the current time.
|
||||
if (connectToWifi()) {
|
||||
// Setup our NTP to get the current time.
|
||||
sntp_set_time_sync_notification_cb(timeAvailable);
|
||||
sntp_servermode_dhcp(1); // (optional)
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 2)
|
||||
esp_sntp_servermode_dhcp(1); // (optional)
|
||||
#else
|
||||
sntp_servermode_dhcp(1); // (optional)
|
||||
#endif
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
|
||||
|
||||
//set up our esp32 to listen on the local_hostname.local domain
|
||||
if (!MDNS.begin(local_hostname)) {
|
||||
// set up our esp32 to listen on the psychic.local domain
|
||||
if (MDNS.begin(local_hostname))
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
else
|
||||
Serial.println("Error starting mDNS");
|
||||
return;
|
||||
}
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
if(!LittleFS.begin())
|
||||
{
|
||||
if (!LittleFS.begin()) {
|
||||
Serial.println("LittleFS Mount Failed. Do Platform -> Build Filesystem Image and Platform -> Upload Filesystem Image from VSCode");
|
||||
return;
|
||||
}
|
||||
|
||||
//look up our keys?
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
if (app_enable_ssl)
|
||||
{
|
||||
File fp = LittleFS.open("/server.crt");
|
||||
if (fp)
|
||||
{
|
||||
server_cert = fp.readString();
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
// look up our keys?
|
||||
if (app_enable_ssl) {
|
||||
File fp = LittleFS.open("/server.crt");
|
||||
if (fp) {
|
||||
server_cert = fp.readString();
|
||||
|
||||
// Serial.println("Server Cert:");
|
||||
// Serial.println(server_cert);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("server.pem not found, SSL not available");
|
||||
app_enable_ssl = false;
|
||||
}
|
||||
fp.close();
|
||||
|
||||
File fp2 = LittleFS.open("/server.key");
|
||||
if (fp2)
|
||||
{
|
||||
server_key = fp2.readString();
|
||||
|
||||
// Serial.println("Server Key:");
|
||||
// Serial.println(server_key);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("server.key not found, SSL not available");
|
||||
app_enable_ssl = false;
|
||||
}
|
||||
fp2.close();
|
||||
// Serial.println("Server Cert:");
|
||||
// Serial.println(server_cert);
|
||||
} else {
|
||||
Serial.println("server.pem not found, SSL not available");
|
||||
app_enable_ssl = false;
|
||||
}
|
||||
#endif
|
||||
fp.close();
|
||||
|
||||
//setup server config stuff here
|
||||
server.config.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
|
||||
File fp2 = LittleFS.open("/server.key");
|
||||
if (fp2) {
|
||||
server_key = fp2.readString();
|
||||
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
server.ssl_config.httpd.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
|
||||
|
||||
//do we want secure or not?
|
||||
if (app_enable_ssl)
|
||||
{
|
||||
server.listen(443, server_cert.c_str(), server_key.c_str());
|
||||
|
||||
//this creates a 2nd server listening on port 80 and redirects all requests HTTPS
|
||||
PsychicHttpServer *redirectServer = new PsychicHttpServer();
|
||||
redirectServer->config.ctrl_port = 20424; // just a random port different from the default one
|
||||
redirectServer->listen(80);
|
||||
redirectServer->onNotFound([](PsychicRequest *request) {
|
||||
String url = "https://" + request->host() + request->url();
|
||||
return request->redirect(url.c_str());
|
||||
});
|
||||
// Serial.println("Server Key:");
|
||||
// Serial.println(server_key);
|
||||
} else {
|
||||
Serial.println("server.key not found, SSL not available");
|
||||
app_enable_ssl = false;
|
||||
}
|
||||
else
|
||||
server.listen(80);
|
||||
#else
|
||||
server.listen(80);
|
||||
#endif
|
||||
fp2.close();
|
||||
}
|
||||
|
||||
// do we want secure or not?
|
||||
if (app_enable_ssl) {
|
||||
server.setCertificate(server_cert.c_str(), server_key.c_str());
|
||||
|
||||
// this creates a 2nd server listening on port 80 and redirects all requests HTTPS
|
||||
PsychicHttpServer* redirectServer = new PsychicHttpServer();
|
||||
redirectServer->config.ctrl_port = 20424; // just a random port different from the default one
|
||||
redirectServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
||||
String url = "https://" + request->host() + request->url();
|
||||
return response->redirect(url.c_str()); });
|
||||
}
|
||||
#endif
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Server", "PsychicHttp");
|
||||
|
||||
//serve static files from LittleFS/www on / only to clients on same wifi network
|
||||
//this is where our /index.html file lives
|
||||
// curl -i http://psychic.local/
|
||||
PsychicStaticFileHandler* handler = server.serveStatic("/", LittleFS, "/www/");
|
||||
handler->setFilter(ON_STA_FILTER);
|
||||
handler->setCacheControl("max-age=60");
|
||||
loggingMiddleware.setOutput(Serial);
|
||||
|
||||
//serve static files from LittleFS/www-ap on / only to clients on SoftAP
|
||||
//this is where our /index.html file lives
|
||||
server.serveStatic("/", LittleFS, "/www-ap/")->setFilter(ON_AP_FILTER);
|
||||
basicAuth.setUsername(app_user);
|
||||
basicAuth.setPassword(app_pass);
|
||||
basicAuth.setRealm(app_name);
|
||||
basicAuth.setAuthMethod(HTTPAuthMethod::BASIC_AUTH);
|
||||
basicAuth.setAuthFailureMessage("You must log in.");
|
||||
|
||||
//serve static files from LittleFS/img on /img
|
||||
//it's more efficient to serve everything from a single www directory, but this is also possible.
|
||||
// curl -i http://psychic.local/img/request_flow.png
|
||||
digestAuth.setUsername(app_user);
|
||||
digestAuth.setPassword(app_pass);
|
||||
digestAuth.setRealm(app_name);
|
||||
digestAuth.setAuthMethod(HTTPAuthMethod::DIGEST_AUTH);
|
||||
digestAuth.setAuthFailureMessage("You must log in.");
|
||||
|
||||
// corsMiddleware.setAllowCredentials(true);
|
||||
// corsMiddleware.setOrigin("http://www.example.com,https://www.example.com,http://api.example.com,https://api.example.com");
|
||||
// corsMiddleware.setHeaders("Origin,X-Requested-With,Content-Type,Accept,Content-Type,Authorization,X-Access-Token");
|
||||
|
||||
server.addMiddleware(&loggingMiddleware);
|
||||
// this will send CORS headers on every HTTP_OPTIONS request that contains the Origin: header
|
||||
server.addMiddleware(&corsMiddleware);
|
||||
|
||||
// rewrites!
|
||||
server.rewrite("/rewrite", "/api?foo=rewrite");
|
||||
|
||||
// serve static files from LittleFS/www on / only to clients on same wifi network
|
||||
// this is where our /index.html file lives
|
||||
// curl -i http://psychic.local/
|
||||
server.serveStatic("/", LittleFS, "/www/")
|
||||
->setCacheControl("max-age=60")
|
||||
->addFilter(ON_STA_FILTER);
|
||||
|
||||
// serve static files from LittleFS/www-ap on / only to clients on SoftAP
|
||||
// this is where our /index.html file lives
|
||||
server.serveStatic("/", LittleFS, "/www-ap/")->addFilter(ON_AP_FILTER);
|
||||
|
||||
// serve static files from LittleFS/img on /img
|
||||
// it's more efficient to serve everything from a single www directory, but this is also possible.
|
||||
// curl -i http://psychic.local/img/request_flow.png
|
||||
server.serveStatic("/img", LittleFS, "/img/");
|
||||
|
||||
//you can also serve single files
|
||||
// curl -i http://psychic.local/myfile.txt
|
||||
#ifdef PSY_ENABLE_SDCARD
|
||||
// if we detect an SD card, serve all files from sd:/ on http://psychic.local/sd
|
||||
if (setupSDCard())
|
||||
server.serveStatic("/sd", SD, "/");
|
||||
#endif
|
||||
|
||||
// you can also serve single files
|
||||
// curl -i http://psychic.local/myfile.txt
|
||||
server.serveStatic("/myfile.txt", LittleFS, "/custom.txt");
|
||||
|
||||
//example callback everytime a connection is opened
|
||||
server.onOpen([](PsychicClient *client) {
|
||||
Serial.printf("[http] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
// example callback everytime a connection is opened
|
||||
server.onOpen([](PsychicClient* client) { Serial.printf("[http] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString().c_str()); });
|
||||
|
||||
//example callback everytime a connection is closed
|
||||
server.onClose([](PsychicClient *client) {
|
||||
Serial.printf("[http] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
// example callback everytime a connection is closed
|
||||
server.onClose([](PsychicClient* client) { Serial.printf("[http] connection #%u closed\n", client->socket()); });
|
||||
|
||||
//api - json message passed in as post body
|
||||
// curl -i -X POST -H "Content-Type: application/json" -d '{"foo":"bar"}' http://psychic.local/api
|
||||
server.on("/api", HTTP_POST, [](PsychicRequest *request, JsonVariant &json)
|
||||
{
|
||||
// api - json message passed in as post body
|
||||
// curl -i -X POST -H "Content-Type: application/json" -d '{"foo":"bar"}' http://psychic.local/api
|
||||
server.on("/api", HTTP_POST, [](PsychicRequest* request, PsychicResponse* resp, JsonVariant& json) {
|
||||
JsonObject input = json.as<JsonObject>();
|
||||
|
||||
//create our response json
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request);
|
||||
// create our response json
|
||||
PsychicJsonResponse response(resp);
|
||||
JsonObject output = response.getRoot();
|
||||
|
||||
output["msg"] = "status";
|
||||
output["status"] = "success";
|
||||
output["millis"] = millis();
|
||||
output["method"] = request->methodStr();
|
||||
|
||||
//work with some params
|
||||
if (input.containsKey("foo"))
|
||||
{
|
||||
// work with some params
|
||||
if (input.containsKey("foo")) {
|
||||
String foo = input["foo"];
|
||||
output["foo"] = foo;
|
||||
}
|
||||
|
||||
|
||||
return response.send();
|
||||
});
|
||||
|
||||
//ip - get info about the client
|
||||
// curl -i http://psychic.local/ip
|
||||
server.on("/ip", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
// ip - get info about the client
|
||||
// curl -i http://psychic.local/ip
|
||||
server.on("/ip", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
String output = "Your IP is: " + request->client()->remoteIP().toString();
|
||||
return request->reply(output.c_str());
|
||||
return response->send(output.c_str());
|
||||
});
|
||||
|
||||
//client connect/disconnect to a url
|
||||
// curl -i http://psychic.local/handler
|
||||
PsychicWebHandler *connectionHandler = new PsychicWebHandler();
|
||||
connectionHandler->onRequest([](PsychicRequest *request)
|
||||
{
|
||||
return request->reply("OK");
|
||||
});
|
||||
connectionHandler->onOpen([](PsychicClient *client) {
|
||||
Serial.printf("[handler] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
connectionHandler->onClose([](PsychicClient *client) {
|
||||
Serial.printf("[handler] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
// client connect/disconnect to a url
|
||||
// curl -i http://psychic.local/handler
|
||||
PsychicWebHandler* connectionHandler = new PsychicWebHandler();
|
||||
connectionHandler->onRequest([](PsychicRequest* request, PsychicResponse* response) { return response->send("OK"); });
|
||||
connectionHandler->onOpen([](PsychicClient* client) { Serial.printf("[handler] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString().c_str()); });
|
||||
connectionHandler->onClose([](PsychicClient* client) { Serial.printf("[handler] connection #%u closed\n", client->socket()); });
|
||||
|
||||
//add it to our server
|
||||
// add it to our server
|
||||
server.on("/handler", connectionHandler);
|
||||
|
||||
//api - parameters passed in via query eg. /api?foo=bar
|
||||
// curl -i 'http://psychic.local/api?foo=bar'
|
||||
server.on("/api", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
//showcase some of the variables
|
||||
Serial.println(request->host());
|
||||
Serial.println(request->uri());
|
||||
Serial.println(request->path());
|
||||
Serial.println(request->queryString());
|
||||
|
||||
//create a response object
|
||||
//create our response json
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request);
|
||||
// api - parameters passed in via query eg. /api?foo=bar
|
||||
// curl -i 'http://psychic.local/api?foo=bar'
|
||||
server.on("/api", HTTP_GET, [](PsychicRequest* request, PsychicResponse* resp) {
|
||||
// create our response json
|
||||
PsychicJsonResponse response = PsychicJsonResponse(resp);
|
||||
JsonObject output = response.getRoot();
|
||||
|
||||
output["msg"] = "status";
|
||||
output["status"] = "success";
|
||||
output["millis"] = millis();
|
||||
output["method"] = request->methodStr();
|
||||
|
||||
//work with some params
|
||||
if (request->hasParam("foo"))
|
||||
{
|
||||
// work with some params
|
||||
if (request->hasParam("foo")) {
|
||||
String foo = request->getParam("foo")->value();
|
||||
output["foo"] = foo;
|
||||
}
|
||||
@@ -355,112 +448,135 @@ void setup()
|
||||
return response.send();
|
||||
});
|
||||
|
||||
//JsonResponse example
|
||||
// curl -i http://psychic.local/json
|
||||
server.on("/json", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request);
|
||||
// curl -i -X GET 'http://psychic.local/any'
|
||||
// curl -i -X POST 'http://psychic.local/any'
|
||||
server.on("/any", HTTP_ANY, [](PsychicRequest* request, PsychicResponse* resp) {
|
||||
// create our response json
|
||||
PsychicJsonResponse response = PsychicJsonResponse(resp);
|
||||
JsonObject output = response.getRoot();
|
||||
|
||||
output["msg"] = "status";
|
||||
output["status"] = "success";
|
||||
output["millis"] = millis();
|
||||
output["method"] = request->methodStr();
|
||||
|
||||
return response.send();
|
||||
});
|
||||
|
||||
// curl -i 'http://psychic.local/simple'
|
||||
server.on("/simple", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
return response->send("Simple");
|
||||
})
|
||||
->setURIMatchFunction(MATCH_SIMPLE);
|
||||
|
||||
#ifdef PSY_ENABLE_REGEX
|
||||
// curl -i 'http://psychic.local/regex/23'
|
||||
// curl -i 'http://psychic.local/regex/4223'
|
||||
server.on("^/regex/([\\d]+)/?$", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
// look up our regex matches
|
||||
std::smatch matches;
|
||||
if (request->getRegexMatches(matches)) {
|
||||
String output;
|
||||
output += "Matches: " + String(matches.size()) + "<br/>\n";
|
||||
output += "Matched URI: " + String(matches.str(0).c_str()) + "<br/>\n";
|
||||
output += "Match 1: " + String(matches.str(1).c_str()) + "<br/>\n";
|
||||
|
||||
return response->send(output.c_str());
|
||||
} else
|
||||
return response->send("No regex match.");
|
||||
})
|
||||
->setURIMatchFunction(MATCH_REGEX);
|
||||
#endif
|
||||
|
||||
// JsonResponse example
|
||||
// curl -i http://psychic.local/json
|
||||
server.on("/json", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
PsychicJsonResponse jsonResponse = PsychicJsonResponse(response);
|
||||
|
||||
char key[16];
|
||||
char value[32];
|
||||
JsonObject root = response.getRoot();
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
JsonObject root = jsonResponse.getRoot();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
sprintf(key, "key%d", i);
|
||||
sprintf(value, "value is %d", i);
|
||||
root[key] = value;
|
||||
}
|
||||
|
||||
return response.send();
|
||||
});
|
||||
|
||||
//how to redirect a request
|
||||
// curl -i http://psychic.local/redirect
|
||||
server.on("/redirect", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
return request->redirect("/alien.png");
|
||||
return jsonResponse.send();
|
||||
});
|
||||
|
||||
//how to do basic auth
|
||||
// curl -i --user admin:admin http://psychic.local/auth-basic
|
||||
server.on("/auth-basic", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
if (!request->authenticate(app_user, app_pass))
|
||||
return request->requestAuthentication(BASIC_AUTH, app_name, "You must log in.");
|
||||
return request->reply("Auth Basic Success!");
|
||||
});
|
||||
// how to redirect a request
|
||||
// curl -i http://psychic.local/redirect
|
||||
server.on("/redirect", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) { return response->redirect("/alien.png"); });
|
||||
|
||||
//how to do digest auth
|
||||
// curl -i --user admin:admin http://psychic.local/auth-digest
|
||||
server.on("/auth-digest", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
if (!request->authenticate(app_user, app_pass))
|
||||
return request->requestAuthentication(DIGEST_AUTH, app_name, "You must log in.");
|
||||
return request->reply("Auth Digest Success!");
|
||||
});
|
||||
// how to do basic auth
|
||||
// curl -i --user admin:admin http://psychic.local/auth-basic
|
||||
server.on("/auth-basic", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
return response->send("Auth Basic Success!");
|
||||
})->addMiddleware(&basicAuth);
|
||||
|
||||
//example of getting / setting cookies
|
||||
// curl -i -b cookie.txt -c cookie.txt http://psychic.local/cookies
|
||||
server.on("/cookies", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
PsychicResponse response(request);
|
||||
// how to do digest auth
|
||||
// curl -i --user admin:admin http://psychic.local/auth-digest
|
||||
server.on("/auth-digest", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
return response->send("Auth Digest Success!");
|
||||
})->addMiddleware(&digestAuth);
|
||||
|
||||
// example of getting / setting cookies
|
||||
// curl -i -b cookie.txt -c cookie.txt http://psychic.local/cookies
|
||||
server.on("/cookies", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
int counter = 0;
|
||||
if (request->hasCookie("counter"))
|
||||
{
|
||||
counter = std::stoi(request->getCookie("counter").c_str());
|
||||
char cookie[14];
|
||||
size_t size = sizeof(cookie);
|
||||
if (request->getCookie("counter", cookie, &size) == ESP_OK) {
|
||||
// value is null-terminated.
|
||||
counter = std::stoi(cookie);
|
||||
counter++;
|
||||
}
|
||||
sprintf(cookie, "%d", counter);
|
||||
|
||||
char cookie[10];
|
||||
sprintf(cookie, "%i", counter);
|
||||
|
||||
response.setCookie("counter", cookie);
|
||||
response.setContent(cookie);
|
||||
return response.send();
|
||||
response->setCookie("counter", cookie);
|
||||
response->setContent(cookie);
|
||||
return response->send();
|
||||
});
|
||||
|
||||
//example of getting POST variables
|
||||
// curl -i -d "param1=value1¶m2=value2" -X POST http://psychic.local/post
|
||||
server.on("/post", HTTP_POST, [](PsychicRequest *request)
|
||||
{
|
||||
// example of getting POST variables
|
||||
// curl -i -d "param1=value1¶m2=value2" -X POST http://psychic.local/post
|
||||
// curl -F "param1=value1" -F "param2=value2" -X POST http://psychic.local/post
|
||||
server.on("/post", HTTP_POST, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
String output;
|
||||
output += "Param 1: " + request->getParam("param1")->value() + "<br/>\n";
|
||||
output += "Param 2: " + request->getParam("param2")->value() + "<br/>\n";
|
||||
|
||||
return request->reply(output.c_str());
|
||||
return response->send(output.c_str());
|
||||
});
|
||||
|
||||
//you can set up a custom 404 handler.
|
||||
// curl -i http://psychic.local/404
|
||||
server.onNotFound([](PsychicRequest *request)
|
||||
{
|
||||
return request->reply(404, "text/html", "Custom 404 Handler");
|
||||
});
|
||||
// you can set up a custom 404 handler.
|
||||
// curl -i http://psychic.local/404
|
||||
server.onNotFound([](PsychicRequest* request, PsychicResponse* response) { return response->send(404, "text/html", "Custom 404 Handler"); });
|
||||
|
||||
//handle a very basic upload as post body
|
||||
PsychicUploadHandler *uploadHandler = new PsychicUploadHandler();
|
||||
uploadHandler->onUpload([](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) {
|
||||
// handle a very basic upload as post body
|
||||
PsychicUploadHandler* uploadHandler = new PsychicUploadHandler();
|
||||
uploadHandler->onUpload([](PsychicRequest* request, const String& filename, uint64_t index, uint8_t* data, size_t len, bool last) {
|
||||
File file;
|
||||
String path = "/www/" + filename;
|
||||
|
||||
Serial.printf("Writing %d/%d bytes to: %s\n", (int)index+(int)len, request->contentLength(), path.c_str());
|
||||
Serial.printf("Writing %d/%d bytes to: %s\n", (int)index + (int)len, request->contentLength(), path.c_str());
|
||||
|
||||
if (last)
|
||||
Serial.printf("%s is finished. Total bytes: %d\n", path.c_str(), (int)index+(int)len);
|
||||
Serial.printf("%s is finished. Total bytes: %llu\n", path.c_str(), (uint64_t)index + (uint64_t)len);
|
||||
|
||||
//our first call?
|
||||
// our first call?
|
||||
if (!index)
|
||||
file = LittleFS.open(path, FILE_WRITE);
|
||||
else
|
||||
file = LittleFS.open(path, FILE_APPEND);
|
||||
|
||||
if(!file) {
|
||||
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if(!file.write(data, len)) {
|
||||
if (!file.write(data, len)) {
|
||||
Serial.println("Write failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@@ -468,42 +584,41 @@ void setup()
|
||||
return ESP_OK;
|
||||
});
|
||||
|
||||
//gets called after upload has been handled
|
||||
uploadHandler->onRequest([](PsychicRequest *request)
|
||||
{
|
||||
// gets called after upload has been handled
|
||||
uploadHandler->onRequest([](PsychicRequest* request, PsychicResponse* response) {
|
||||
String url = "/" + request->getFilename();
|
||||
String output = "<a href=\"" + url + "\">" + url + "</a>";
|
||||
|
||||
return request->reply(output.c_str());
|
||||
return response->send(output.c_str());
|
||||
});
|
||||
|
||||
//wildcard basic file upload - POST to /upload/filename.ext
|
||||
// use http://psychic.local/ to test
|
||||
// wildcard basic file upload - POST to /upload/filename.ext
|
||||
// use http://psychic.local/ to test
|
||||
server.on("/upload/*", HTTP_POST, uploadHandler);
|
||||
|
||||
//a little bit more complicated multipart form
|
||||
PsychicUploadHandler *multipartHandler = new PsychicUploadHandler();
|
||||
multipartHandler->onUpload([](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) {
|
||||
// a little bit more complicated multipart form
|
||||
PsychicUploadHandler* multipartHandler = new PsychicUploadHandler();
|
||||
multipartHandler->onUpload([](PsychicRequest* request, const String& filename, uint64_t index, uint8_t* data, size_t len, bool last) {
|
||||
File file;
|
||||
String path = "/www/" + filename;
|
||||
|
||||
//some progress over serial.
|
||||
Serial.printf("Writing %d bytes to: %s\n", (int)len, path.c_str());
|
||||
// some progress over serial.
|
||||
Serial.printf("Writing %d bytes to: %s @ index %llu\n", (int)len, path.c_str(), index);
|
||||
if (last)
|
||||
Serial.printf("%s is finished. Total bytes: %d\n", path.c_str(), (int)index+(int)len);
|
||||
Serial.printf("%s is finished. Total bytes: %llu\n", path.c_str(), (uint64_t)index + (uint64_t)len);
|
||||
|
||||
//our first call?
|
||||
// our first call?
|
||||
if (!index)
|
||||
file = LittleFS.open(path, FILE_WRITE);
|
||||
else
|
||||
file = LittleFS.open(path, FILE_APPEND);
|
||||
|
||||
if(!file) {
|
||||
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if(!file.write(data, len)) {
|
||||
if (!file.write(data, len)) {
|
||||
Serial.println("Write failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@@ -511,57 +626,83 @@ void setup()
|
||||
return ESP_OK;
|
||||
});
|
||||
|
||||
//gets called after upload has been handled
|
||||
multipartHandler->onRequest([](PsychicRequest *request)
|
||||
{
|
||||
if (request->hasParam("file_upload"))
|
||||
{
|
||||
PsychicWebParameter *file = request->getParam("file_upload");
|
||||
// gets called after upload has been handled
|
||||
multipartHandler->onRequest([](PsychicRequest* request, PsychicResponse* response) {
|
||||
String output;
|
||||
if (request->hasParam("file_upload")) {
|
||||
PsychicWebParameter* file = request->getParam("file_upload");
|
||||
|
||||
String url = "/" + file->value();
|
||||
String output;
|
||||
|
||||
output += "<a href=\"" + url + "\">" + url + "</a><br/>\n";
|
||||
output += "Bytes: " + String(file->size()) + "<br/>\n";
|
||||
output += "Param 1: " + request->getParam("param1")->value() + "<br/>\n";
|
||||
output += "Param 2: " + request->getParam("param2")->value() + "<br/>\n";
|
||||
|
||||
return request->reply(output.c_str());
|
||||
}
|
||||
else
|
||||
return request->reply("No upload.");
|
||||
|
||||
if (request->hasParam("param1"))
|
||||
output += "Param 1: " + request->getParam("param1")->value() + "<br/>\n";
|
||||
if (request->hasParam("param2"))
|
||||
output += "Param 2: " + request->getParam("param2")->value() + "<br/>\n";
|
||||
|
||||
return response->send(output.c_str());
|
||||
});
|
||||
|
||||
//wildcard basic file upload - POST to /upload/filename.ext
|
||||
// use http://psychic.local/ to test
|
||||
// wildcard basic file upload - POST to /upload/filename.ext
|
||||
// use http://psychic.local/ to test
|
||||
// just multipart data: curl -F "param1=multi" -F "param2=part" http://psychic.local/multipart
|
||||
server.on("/multipart", HTTP_POST, multipartHandler);
|
||||
|
||||
//a websocket echo server
|
||||
// npm install -g wscat
|
||||
// wscat -c ws://psychic.local/ws
|
||||
websocketHandler.onOpen([](PsychicWebSocketClient *client) {
|
||||
Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
|
||||
// form only multipart handler
|
||||
// curl -F "param1=multi" -F "param2=part" http://psychic.local/multipart-data
|
||||
PsychicUploadHandler* multipartFormHandler = new PsychicUploadHandler();
|
||||
multipartFormHandler->onRequest([](PsychicRequest* request, PsychicResponse* response) {
|
||||
String output;
|
||||
if (request->hasParam("param1"))
|
||||
output += "Param 1: " + request->getParam("param1")->value() + "<br/>\n";
|
||||
if (request->hasParam("param2"))
|
||||
output += "Param 2: " + request->getParam("param2")->value() + "<br/>\n";
|
||||
|
||||
return response->send(output.c_str());
|
||||
});
|
||||
server.on("/multipart-data", HTTP_POST, multipartFormHandler);
|
||||
|
||||
// a websocket echo server
|
||||
// npm install -g wscat
|
||||
// Plaintext: wscat -c ws://psychic.local/ws
|
||||
// SSL: wscat -n -c wss://psychic.local/ws
|
||||
websocketHandler.onOpen([](PsychicWebSocketClient* client) {
|
||||
Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString().c_str());
|
||||
client->sendMessage("Hello!");
|
||||
});
|
||||
websocketHandler.onFrame([](PsychicWebSocketRequest *request, httpd_ws_frame *frame) {
|
||||
Serial.printf("[socket] #%d sent: %s\n", request->client()->socket(), (char *)frame->payload);
|
||||
websocketHandler.onFrame([](PsychicWebSocketRequest* request, httpd_ws_frame* frame) {
|
||||
// Serial.printf("[socket] #%d sent: %s\n", request->client()->socket(), String((char*)frame->payload, frame->len).c_str());
|
||||
return request->reply(frame);
|
||||
});
|
||||
websocketHandler.onClose([](PsychicWebSocketClient *client) {
|
||||
Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
websocketHandler.onClose([](PsychicWebSocketClient* client) { Serial.printf("[socket] connection #%u closed\n", client->socket()); });
|
||||
server.on("/ws", &websocketHandler);
|
||||
|
||||
//EventSource server
|
||||
// curl -i -N http://psychic.local/events
|
||||
eventSource.onOpen([](PsychicEventSourceClient *client) {
|
||||
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
|
||||
// EventSource server
|
||||
// curl -i -N http://psychic.local/events
|
||||
eventSource.onOpen([](PsychicEventSourceClient* client) {
|
||||
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString().c_str());
|
||||
client->send("Hello user!", NULL, millis(), 1000);
|
||||
});
|
||||
eventSource.onClose([](PsychicEventSourceClient *client) {
|
||||
Serial.printf("[eventsource] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
eventSource.onClose([](PsychicEventSourceClient* client) { Serial.printf("[eventsource] connection #%u closed\n", client->socket()); });
|
||||
server.on("/events", &eventSource);
|
||||
|
||||
// example of using POST data inside the filter
|
||||
// works: curl -F "secret=password" http://psychic.local/post-filter
|
||||
// 404: curl -F "foo=bar" http://psychic.local/post-filter
|
||||
server.on("/post-filter", HTTP_POST, [](PsychicRequest* request, PsychicResponse* response) {
|
||||
String output;
|
||||
output += "Secret: " + request->getParam("secret")->value() + "<br/>\n";
|
||||
|
||||
return response->send(output.c_str());
|
||||
})
|
||||
->addFilter([](PsychicRequest* request) {
|
||||
request->loadParams();
|
||||
return request->hasParam("secret");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,14 +711,21 @@ char output[60];
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (millis() - lastUpdate > 2000)
|
||||
{
|
||||
sprintf(output, "Millis: %d\n", millis());
|
||||
if (millis() - lastUpdate > 1000) {
|
||||
sprintf(output, "Millis: %lu\n", millis());
|
||||
websocketHandler.sendAll(output);
|
||||
|
||||
sprintf(output, "%d", millis());
|
||||
sprintf(output, "%lu", millis());
|
||||
eventSource.send(output, "millis", millis(), 0);
|
||||
|
||||
lastUpdate = millis();
|
||||
}
|
||||
|
||||
// just some dev code to test that starting / stopping the server works okay.
|
||||
// delay(5000);
|
||||
// Serial.println("Stopping Server");
|
||||
// server.stop();
|
||||
// delay(5000);
|
||||
// Serial.println("Starting Server");
|
||||
// server.start();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||
Reference in New Issue
Block a user