Skip to content
Snippets Groups Projects
Commit 9be72337 authored by Sebastian Pape's avatar Sebastian Pape
Browse files

Initial Commit

parents
No related branches found
No related tags found
No related merge requests found
#include <ESP32Servo.h>
#include "MotionToPhotonServer.h"
#include "esp_task_wdt.h"
/* Forward Declarations */
void startMeasureing(unsigned int numberOfSamples);
unsigned int getPhotoDiodeReading();
void setThresholdValue(unsigned int newThreshold);
unsigned int getThresholdValue();
String getStatus();
unsigned int getCurrentNumberOfResults();
void zeroServo();
void abortMeasurement();
/* Enums */
enum Status {Ready = 0, Measuring = 1};
String Status_Text[2] = {"Ready", "Measuring"};
enum InternalStatus {Zeroed, NotZeroed, Stopping};
/* Hardware */
Servo rotation_servo;
#define PhotoPin 33
#define ServoPin 12
#define SensePin 4
#define ButtonPin 23
/* Global Values */
int zeroValueForServo = 0;
long startTime = -1;
int triggerValue = 2500;
unsigned int requested_measurements = -1;
Status current_status = Status::Ready;
InternalStatus current_internal_status = InternalStatus::NotZeroed;
/* Result Storage */
#define result_buffer_length 500
unsigned long result_buffer[result_buffer_length];
unsigned int result_number = 0;
/* Server */
MotionToPhotonServer server(result_buffer, result_buffer_length, getPhotoDiodeReading, getThresholdValue, setThresholdValue, getStatus, startMeasureing, getCurrentNumberOfResults, abortMeasurement);
/* Multicore */
TaskHandle_t TaskCore0;
TaskHandle_t TaskCore1;
void core0( void * pvParameters ){
while(true){
if(current_status != Status::Measuring){
delay(5); //just small delay
esp_task_wdt_reset();
continue;
}
if(current_internal_status == InternalStatus::NotZeroed
&& result_number < requested_measurements){
zeroServo(); //Rearm
delay(5000);
rotation_servo.write(0); //big movement
}
if(current_internal_status == InternalStatus::Zeroed){
if(digitalRead(SensePin)){
current_internal_status = InternalStatus::Stopping;
startTime = micros();
}
}
if(current_internal_status == InternalStatus::Stopping
&& getPhotoDiodeReading() < triggerValue){
result_buffer[result_number] = (micros() - startTime)/1000.0;
Serial.println("Time: " + String(result_buffer[result_number]));
result_number++;
current_internal_status = InternalStatus::NotZeroed;
//Finished Measurements
if(result_number >= requested_measurements){
current_status = Status::Ready;
requested_measurements = -1;
}
}
esp_task_wdt_reset();
}
}
void core1( void * pvParameters ){
while(true){
server.serve();
esp_task_wdt_reset();
}
}
void setup() {
Serial.begin(115200);
rotation_servo.setPeriodHertz(50); // standard 50 hz servo
rotation_servo.attach(ServoPin, 540, 2470);
rotation_servo.write(0);
pinMode(PhotoPin, INPUT);
pinMode(SensePin, INPUT_PULLDOWN);
server.setup();
esp_task_wdt_init(6000,false);
xTaskCreatePinnedToCore(
core0, // Task function.
"Meter", // name of task.
10000, // Stack size of task
NULL, // parameter of the task
1, // priority of the task
&TaskCore0, // Task handle to keep track of created task
0); // pin task to core 0
delay(500);
xTaskCreatePinnedToCore(
core1, // Task function.
"Server", // name of task.
10000, // Stack size of task
NULL, // parameter of the task
1, // priority of the task
&TaskCore1, // Task handle to keep track of created task
0); // pin task to core 0
delay(500);
}
void loop(){} //Still needed for arduino core to work
void startMeasureing(unsigned int numberOfSamples){
memset(result_buffer, 0l, result_buffer_length * sizeof(long)); //clear buffer
result_number = 0;
requested_measurements = numberOfSamples;
current_status = Status::Measuring;
}
unsigned int getPhotoDiodeReading(){
return analogRead(PhotoPin);
}
void abortMeasurement(){
current_status = Status::Ready;
requested_measurements = -1;
rotation_servo.write(0);
current_internal_status = InternalStatus::NotZeroed;
}
void setThresholdValue(unsigned int newThreshold){
triggerValue = newThreshold;
}
unsigned int getThresholdValue(){
return triggerValue;
}
String getStatus(){
return Status_Text[current_status];
}
unsigned int getCurrentNumberOfResults(){
return max(result_number, 0u);
}
void zeroServo(){
//start a little lower than last time
zeroValueForServo -= 5;
rotation_servo.write(zeroValueForServo);
delay(50);
do{
zeroValueForServo++;
rotation_servo.write(zeroValueForServo);
delay(100);
} while (!digitalRead(SensePin));
Serial.println("Servo zeroed at: " + String(zeroValueForServo));
current_internal_status = InternalStatus::Zeroed;
}
#include "MotionToPhotonServer.h"
/* Server Setup */
MotionToPhotonServer::MotionToPhotonServer(unsigned long* result_buffer_, unsigned int result_buffer_length_, unsigned int (* getPhotodiodeReadingFunction)(), unsigned int (* getThresholdFunction)(), void (* setThresholdFunction)(unsigned int), String (* getStatusFunction)(), void (* startMeasureFunction)(unsigned int), unsigned int (* getCurrentNumberOfResultsFunction)(), void (* abortMeasurementFunction)()) {
getPhotodiodeReading = getPhotodiodeReadingFunction;
getThreshold = getThresholdFunction;
setThreshold = setThresholdFunction;
getStatus = getStatusFunction;
startMeasure = startMeasureFunction;
abortMeasurement = abortMeasurementFunction;
getCurrentNumberOfResults = getCurrentNumberOfResultsFunction;
result_buffer = result_buffer_;
result_buffer_length = result_buffer_length_;
}
void MotionToPhotonServer::setup(){
if(!Serial) Serial.begin(115200);
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
server.on("/", [this](){this->pageIndex();} );
server.on("/measure", [this](){this->pageMeasure();});
server.on("/results", [this](){this->pageResults();});
server.on("/status", [this](){this->pageStatus();});
server.on("/sensor", [this](){this->pageSensor();});
server.on("/setThreshold", [this](){this->pageSetThreshold();});
server.on("/getThreshold", [this](){this->pageGetThreshold();});
server.on("/abort", [this](){this->pageAbort();});
server.onNotFound([this](){this->pageNotFound();});
server.begin();
Serial.println("HTTP server started");
}
void MotionToPhotonServer::serve() {
server.handleClient();
}
/* Pages to Serve */
void MotionToPhotonServer::pageAbort(){
abortMeasurement();
server.send(200, "text/plain", "Ok.");
}
void MotionToPhotonServer::pageStatus(){
server.send(200, "text/plain", String(getStatus()));
}
void MotionToPhotonServer::pageSetThreshold(){
if(server.args() <= 0){
server.send(400, "text/plain", "No parameters given. Give t=<threshold>");
return;
}
unsigned int threshold = String(server.arg(0)).toInt();
setThreshold(threshold);
server.send(200, "text/plain", "Ok.");
}
void MotionToPhotonServer::pageGetThreshold(){
server.send(200, "text/plain", String(getThreshold()));
}
void MotionToPhotonServer::pageSensor(){
server.send(200, "text/plain", String(getPhotodiodeReading()));
}
void MotionToPhotonServer::pageMeasure(){
if(server.args() <= 0){
server.send(400, "text/plain", "No parameters given. Give t=<times to measure>");
return;
}
unsigned int timesToMeasure = String(server.arg(0)).toInt();
startMeasure(timesToMeasure);
Serial.println("Measuring " + String(timesToMeasure) + " times!");
server.send(200, "text/plain", "Ok.");
}
void MotionToPhotonServer::pageResults(){
Serial.println("Client wants index page.");
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/plain", "");
unsigned int currentNumberOfResults = getCurrentNumberOfResults();
for(unsigned int i = 0; i < currentNumberOfResults; i++){
server.sendContent(String(result_buffer[i]) + ((i != currentNumberOfResults-1) ? ",\n":""));
}
server.client().stop();
}
void MotionToPhotonServer::pageIndex(){
Serial.println("Client wants index page.");
server.send(200, "text/html", readFile("index.html"));
}
void MotionToPhotonServer::pageNotFound(){
server.send(404, "text/plain", "Not found!");
}
/* Utility */
String MotionToPhotonServer::readFile(String filename){
memset(file_buffer, '\0', file_buffer_length * sizeof(char)); //clear buffer
File file = SPIFFS.open("/" + filename, "r");
if (!file) return "";
if(file.size() > file_buffer_length) return "Error Reading File.";
unsigned int offset = 0;
while(file.available()){
file_buffer[offset] = file.read();
offset++;
}
return String(file_buffer);
}
#ifndef MotionToPhotonServer_h
#define MotionToPhotonServer_h
#include "Arduino.h"
#include <WebServer.h>
#include "SPIFFS.h"
class MotionToPhotonServer
{
private:
unsigned int (* getPhotodiodeReading)();
unsigned int (* getThreshold)();
unsigned int (* getCurrentNumberOfResults)();
String (* getStatus)();
void (* setThreshold)(unsigned int);
void (* startMeasure)(unsigned int);
void (* abortMeasurement)();
/* Put your SSID & Password */
const char* ssid = "Calibratio"; // Enter SSID here
const char* password = ""; //Enter Password here
/* Put IP Address details */
IPAddress local_ip = IPAddress(192,168,1,1);
IPAddress gateway = IPAddress(192,168,1,1);
IPAddress subnet = IPAddress(255,255,255,0);
WebServer server;
/* File Read Buffer */
#define file_buffer_length 8192
char file_buffer[file_buffer_length];
/* Result Buffer */
unsigned long* result_buffer;
unsigned int result_buffer_length;
/* Pages */
void pageSensor();
void pageStatus();
void pageSetThreshold();
void pageGetThreshold();
void pageMeasure();
void pageResults();
void pageIndex();
void pageAbort();
void pageNotFound();
/* Utility */
String readFile(String filename);
public:
/* Server functions */
MotionToPhotonServer(unsigned long* result_buffer_, unsigned int result_buffer_length_, unsigned int (* getPhotodiodeReadingFunction)(), unsigned int (* getThresholdFunction)(), void (* setThresholdFunction)(unsigned int), String (* getStatusFunction)(), void (* startMeasureFunction)(unsigned int), unsigned int (* getCurrentNumberOfResultsFunction)(), void (* abortMeasurementFunction)());
void setup();
void serve();
};
#endif
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<style>
* {
font-family: Arial, Helvetica, sans-serif;
}
.box{
border: 2px solid green;
display: flex;
flex-direction: column;
align-items: stretch;
}
.value{
text-align: center;
}
.button{
color: #fff;
background-color: #28a745;
display: block;
text-align: center;
vertical-align: middle;
user-select: none;
padding: 5px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
input, textarea, .niceBorder {
border-radius: 5px;
margin: 5px;
padding: 3px;
border-color: green;
}
</style>
<script>
function makeRequest(site){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "/" + site, false);
xhttp.send();
return xhttp.responseText;
}
function startMeasureN(){
if(document.getElementById("numberOfCycles").checkValidity()){
makeRequest('measure?n=' + document.getElementById("numberOfCycles").value);
}
}
function getResults(){
document.getElementById("results").value = makeRequest("results");
calcStatistics();
}
function getStatus(){
document.getElementById("status").textContent = makeRequest("status");
}
function getSensor(){
document.getElementById("lightSensor").textContent = makeRequest("sensor");
}
function setThreshold(){
if(document.getElementById("sensorThreshhold").checkValidity()){
makeRequest('setThreshold?t=' + document.getElementById("sensorThreshhold").value);
}
}
function getThreshold(){
document.getElementById("sensorThreshhold").value = makeRequest('getThreshold');
}
/* Statistics */
function sum(array) {
var num = 0;
for (var i = 0, l = array.length; i < l; i++) num += parseInt(array[i]);
return num;
}
function mean(array) {
return sum(array) / array.length;
}
function median(array) {
array.sort(function(a, b) {
return a - b;
});
var mid = array.length / 2;
return mid % 1 ? array[mid - 0.5] : (array[mid - 1] + array[mid]) / 2;
}
function variance(array) {
var m = mean(array);
return mean(array.map(function(num) {
return Math.pow(num - m, 2);
}));
}
function standardDeviation(array) {
return Math.sqrt(variance(array));
}
function calcStatistics(){
var array = document.getElementById("results").value.split(",");
if(array.length == 0) return;
document.getElementById("statistics").innerHTML = ""
+ "Mean: " + mean(array) + "<br />"
+ "Median: " + median(array) + "<br />"
+ "Variance: " + variance(array) + "<br />"
+ "StandardDeviation: " + standardDeviation(array);
}
document.onload = function(){
getThreshold();
getStatus();
getSensor();
}
</script>
</head>
<body style="display: flex; flex-direction: row; align-items: center; flex-wrap: wrap; justify-content: center;">
<div class="box niceBorder" style="flex: 0 1 75%; text-align: center; padding: 20px;">
<div style="font-size: 3em">Calibratio</div><br />
This device measures the movement to photon latency!
</div>
<div style="display: flex; flex-direction: column; align-items: stretch; flex-wrap: wrap; justify-content: center;">
<div id="statistics" class="box niceBorder" style="width: 500px; height: 80px;"></div>
<textarea id="results" class="textbox" style="width: 500px; height: 500px;"></textarea>
</div>
<div style="display: flex; flex-direction: column; align-items: stretch; flex-wrap: wrap; justify-content: center;">
<div class="box niceBorder">
<div style="padding: 5px;">Status:</div>
<div class="value" id="status">Ready</div>
<div class="button" onclick="getStatus()">Refresh!</div>
</div>
<div class="box niceBorder">
<div style="padding: 5px;">Light Sensor Reading:</div>
<div class="value" id="lightSensor">0</div>
<div class="button" onclick="getSensor()">Refresh!</div>
</div>
<div class="box niceBorder">
<div style="padding: 5px;">Sensor Threshold:</div>
<input type="number" id="sensorThreshhold" min="1" max="5000" value="2500">
<div class="button" onclick="setThreshold()">Set!</div>
</div>
<div class="box niceBorder">
<div style="padding: 5px;">Times to Meassure:</div>
<input type="number" id="numberOfCycles" min="1" max="500" value="1">
<div class="button" onclick="startMeasureN()">Meassure!</div>
<div class="button" style="background-color: #dc3545;" onclick="makeRequest('abort')">Abort!</div>
</div>
<div class="box niceBorder"><div class="button" onclick="getResults()">Get Results!</div></div>
</div>
</body>
</html>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment