jonny_jr9 ee5bad53ee Revert to V2.0 single board (additional folder)
New controller will be run with single controller at first...
get single board version from V2.0 and create new folder
(two boards version is kept)
-> copied firmware from e6e586e5855d81ee726bb9a0fbe8ab12def5eeef
2023-09-07 12:17:33 +02:00

274 lines
8.2 KiB
C++

extern "C"
{
#include <stdio.h>
#include "mdns.h"
#include "cJSON.h"
#include "esp_spiffs.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "freertos/queue.h"
}
#include "http.hpp"
#include "config.hpp"
//tag for logging
static const char * TAG = "http";
static httpd_handle_t server = NULL;
//==============================
//===== start mdns service =====
//==============================
//TODO: test this, not working?
//function that initializes and starts mdns server for host discovery
void start_mdns_service()
{
//init queue for sending joystickdata from http endpoint to control task
mdns_init();
mdns_hostname_set("armchair");
mdns_instance_name_set("electric armchair");
}
//===========================
//======= default url =======
//===========================
//serve requested files from spiffs
static esp_err_t on_default_url(httpd_req_t *req)
{
ESP_LOGI(TAG, "Opening page for URL: %s", req->uri);
esp_vfs_spiffs_conf_t esp_vfs_spiffs_conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true};
esp_vfs_spiffs_register(&esp_vfs_spiffs_conf);
char path[600];
if (strcmp(req->uri, "/") == 0)
strcpy(path, "/spiffs/index.html");
else
sprintf(path, "/spiffs%s", req->uri);
char *ext = strrchr(path, '.');
if (ext == NULL || strncmp(ext, ".local", strlen(".local")) == 0)
{
httpd_resp_set_status(req, "301 Moved Permanently");
httpd_resp_set_hdr(req, "Location", "/");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
if (strcmp(ext, ".css") == 0)
httpd_resp_set_type(req, "text/css");
if (strcmp(ext, ".js") == 0)
httpd_resp_set_type(req, "text/javascript");
if (strcmp(ext, ".png") == 0)
httpd_resp_set_type(req, "image/png");
FILE *file = fopen(path, "r");
if (file == NULL)
{
httpd_resp_send_404(req);
esp_vfs_spiffs_unregister(NULL);
return ESP_OK;
}
char lineRead[256];
while (fgets(lineRead, sizeof(lineRead), file))
{
httpd_resp_sendstr_chunk(req, lineRead);
}
httpd_resp_sendstr_chunk(req, NULL);
esp_vfs_spiffs_unregister(NULL);
return ESP_OK;
}
//==============================
//===== httpJoystick class =====
//==============================
//-----------------------
//----- constructor -----
//-----------------------
httpJoystick::httpJoystick( httpJoystick_config_t config_f ){
//copy config struct
config = config_f;
}
//--------------------------
//---- receiveHttpData -----
//--------------------------
//joystick endpoint - function that is called when data is received with post request at /api/joystick
esp_err_t httpJoystick::receiveHttpData(httpd_req_t *req){
//--- add header ---
//to allow cross origin (otherwise browser fails when app is running on another host)
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
//--- get data from http request ---
char buffer[100];
memset(&buffer, 0, sizeof(buffer));
httpd_req_recv(req, buffer, req->content_len);
ESP_LOGD(TAG, "/api/joystick: received data: %s", buffer);
//--- parse received json string to json object ---
cJSON *payload = cJSON_Parse(buffer);
ESP_LOGV(TAG, "parsed json: \n %s", cJSON_Print(payload));
//--- extract relevant items from json object ---
cJSON *x_json = cJSON_GetObjectItem(payload, "x");
cJSON *y_json = cJSON_GetObjectItem(payload, "y");
//--- save items to struct ---
joystickData_t data = { };
//note cjson can only interpret values as numbers when there are no quotes around the values in json (are removed from json on client side)
//convert json to double to float
data.x = static_cast<float>(x_json->valuedouble);
data.y = static_cast<float>(y_json->valuedouble);
//log received and parsed values
ESP_LOGI(TAG, "received values: x=%.3f y=%.3f",
data.x, data.y);
// scaleCoordinate(input, min, max, center, tolerance_zero_per, tolerance_end_per)
data.x = scaleCoordinate(data.x+1, 0, 2, 1, config.toleranceZeroX_Per, config.toleranceEndPer);
data.y = scaleCoordinate(data.y+1, 0, 2, 1, config.toleranceZeroY_Per, config.toleranceEndPer);
//--- calculate radius with new/scaled coordinates ---
data.radius = sqrt(pow(data.x,2) + pow(data.y,2));
//TODO: radius tolerance? (as in original joystick func)
//limit radius to 1
if (data.radius > 1) {
data.radius = 1;
}
//--- calculate angle ---
data.angle = (atan(data.y/data.x) * 180) / 3.141;
//--- evaluate position ---
data.position = joystick_evaluatePosition(data.x, data.y);
//log processed values
ESP_LOGI(TAG, "processed values: x=%.3f y=%.3f radius=%.3f angle=%.3f pos=%s",
data.x, data.y, data.radius, data.angle, joystickPosStr[(int)data.position]);
//--- free memory ---
cJSON_Delete(payload);
//--- send data to control task via queue ---
//xQueueSend( joystickDataQueue, ( void * )&data, ( TickType_t ) 0 );
//changed to length = 1 -> overwrite - older values are no longer relevant
xQueueOverwrite( joystickDataQueue, ( void * )&data );
//--- return http response ---
httpd_resp_set_status(req, "204 NO CONTENT");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
//-------------------
//----- getData -----
//-------------------
//wait for and return joystick data from queue, if timeout return NULL
joystickData_t httpJoystick::getData(){
//--- get joystick data from queue ---
if( xQueueReceive( joystickDataQueue, &dataRead, pdMS_TO_TICKS(config.timeoutMs) ) ) {
ESP_LOGD(TAG, "getData: received data (from queue): x=%.3f y=%.3f radius=%.3f angle=%.3f",
dataRead.x, dataRead.y, dataRead.radius, dataRead.angle);
}
//--- timeout ---
//no new data received within configured timeout
else {
//send error message when last received data did NOT result in CENTER position
if (dataRead.position != joystickPos_t::CENTER) {
//change data to "joystick center" data to stop the motors
dataRead = dataCenter;
ESP_LOGE(TAG, "TIMEOUT - no data received for 3s -> set to center");
}
}
return dataRead;
}
//--------------------------------------------
//--- receiveHttpData for httpJoystickMain ---
//--------------------------------------------
//function that wraps pointer to member function of httpJoystickMain instance in a "normal" function which the webserver can run on joystick URL
//declare pointer to receiveHttpData method of httpJoystick class
esp_err_t (httpJoystick::*pointerToReceiveFunc)(httpd_req_t *req) = &httpJoystick::receiveHttpData;
esp_err_t on_joystick_url(httpd_req_t *req){
//run pointer to receiveHttpData function of httpJoystickMain instance
return (httpJoystickMain.*pointerToReceiveFunc)(req);
}
//============================
//===== init http server =====
//============================
//function that initializes http server and configures available urls
void http_init_server()
{
//---- configure webserver ----
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.uri_match_fn = httpd_uri_match_wildcard;
//---- start webserver ----
ESP_ERROR_CHECK(httpd_start(&server, &config));
//----- define URLs -----
//note: ignore warning here, cant define elements separately, causes crash
httpd_uri_t joystick_url = {
.uri = "/api/joystick",
.method = HTTP_POST,
.handler = on_joystick_url,
};
httpd_register_uri_handler(server, &joystick_url);
httpd_uri_t default_url = {
.uri = "/*",
.method = HTTP_GET,
.handler = on_default_url};
httpd_register_uri_handler(server, &default_url);
//previous approach with sockets:
// httpd_uri_t socket_joystick_url = {
// .uri = "/ws-api/joystick",
// .method = HTTP_GET,
// .handler = on_socket_joystick_url,
// .is_websocket = true};
// httpd_register_uri_handler(server, &socket_joystick_url);
}
//============================
//===== stop http server =====
//============================
//function that destroys the http server
void http_stop_server()
{
printf("stopping http\n");
httpd_stop(server);
}