diff --git a/main/config.cpp b/main/config.cpp index fdef38c..30f6e12 100644 --- a/main/config.cpp +++ b/main/config.cpp @@ -48,15 +48,23 @@ control_config_t configControl = { .timeoutMs = 5*60*1000, //time of inactivity after which the mode gets switched to IDLE .timeoutTolerancePer = 5, //percentage the duty can vary between timeout checks considered still inactive //--- http mode --- - .http_toleranceZeroX_Per = 3, //percentage around joystick axis the coordinate snaps to 0 - .http_toleranceZeroY_Per = 10, - .http_toleranceEndPer = 2, //percentage before joystick end the coordinate snaps to 1/-1 - .http_timeoutMs = 3000 //time no new data was received before the motors get turned off }; +//------------------------------- +//----- httpJoystick config ----- +//------------------------------- +httpJoystick_config_t configHttpJoystickMain{ + .toleranceZeroX_Per = 3, //percentage around joystick axis the coordinate snaps to 0 + .toleranceZeroY_Per = 10, + .toleranceEndPer = 2, //percentage before joystick end the coordinate snaps to 1/-1 + .timeoutMs = 3000 //time no new data was received before the motors get turned off +}; + + + //-------------------------------------- //------- joystick configuration ------- //-------------------------------------- @@ -110,8 +118,11 @@ gpio_evaluatedSwitch buttonJoystick(GPIO_NUM_33, true, false); //pullup true, no //create buzzer object on pin 12 with gap between queued events of 100ms buzzer_t buzzer(GPIO_NUM_12, 100); +//create global httpJoystick object +httpJoystick httpJoystickMain(configHttpJoystickMain); + //create global control object -controlledArmchair control(configControl, &buzzer, &motorLeft, &motorRight); +controlledArmchair control(configControl, &buzzer, &motorLeft, &motorRight, &httpJoystickMain); diff --git a/main/config.hpp b/main/config.hpp index 2dce508..20db9bc 100644 --- a/main/config.hpp +++ b/main/config.hpp @@ -8,6 +8,7 @@ #include "buzzer.hpp" #include "control.hpp" #include "fan.hpp" +#include "http.hpp" //create global controlledMotor instances for both motors @@ -26,6 +27,9 @@ extern buzzer_t buzzer; //create global control object extern controlledArmchair control; +//create global httpJoystick object +extern httpJoystick httpJoystickMain; + //configuration for fans extern fan_config_t configFanLeft; extern fan_config_t configFanRight; diff --git a/main/control.cpp b/main/control.cpp index 0e7fb60..b672977 100644 --- a/main/control.cpp +++ b/main/control.cpp @@ -12,7 +12,6 @@ extern "C" #include "config.hpp" #include "control.hpp" -#include "http.hpp" //tag for logging @@ -27,7 +26,8 @@ controlledArmchair::controlledArmchair ( control_config_t config_f, buzzer_t * buzzer_f, controlledMotor* motorLeft_f, - controlledMotor* motorRight_f + controlledMotor* motorRight_f, + httpJoystick* httpJoystick_f ){ //copy configuration @@ -36,6 +36,7 @@ controlledArmchair::controlledArmchair ( buzzer = buzzer_f; motorLeft = motorLeft_f; motorRight = motorRight_f; + httpJoystickMain_l = httpJoystick_f; //set default mode from config modePrevious = config.defaultMode; @@ -85,58 +86,25 @@ void controlledArmchair::startHandleLoop() { break; case controlMode_t::HTTP: - //TODO: outsource this code to http.cpp? //create emptry struct for receiving data from http function joystickData_t dataRead = { }; //--- get joystick data from queue --- - if( xQueueReceive( joystickDataQueue, &dataRead, pdMS_TO_TICKS(500) ) ) { - //reset timestamp lastAction - http_timestamp_lastData = esp_log_timestamp(); + //Note this function waits several seconds (httpconfig.timeoutMs) for data to arrive, otherwise Center data or NULL is returned + //TODO: as described above, when changing modes it might delay a few seconds for the change to apply + dataRead = httpJoystickMain_l->getData(); + //--- generate motor commands --- + ESP_LOGD(TAG, "generating commands from x=%.3f y=%.3f radius=%.3f angle=%.3f", dataRead.x, dataRead.y, dataRead.radius, dataRead.angle); + //Note: timeout (no data received) is handled in getData method + commands = joystick_generateCommandsDriving(dataRead); - ESP_LOGD(TAG, "received data (from queue): x=%.3f y=%.3f radius=%.3f angle=%.3f", - dataRead.x, dataRead.y, dataRead.radius, dataRead.angle); + //--- apply commands to motors --- + //TODO make motorctl.setTarget also accept motorcommand struct directly + motorRight->setTarget(commands.right.state, commands.right.duty); + motorLeft->setTarget(commands.left.state, commands.left.duty); + break; - //--- scale coordinates --- - //note: scaleCoordinate function currently can not handle negative input -> added offset to input - // scaleCoordinate(input, min, max, center, tolerance_zero_per, tolerance_end_per) - dataRead.x = scaleCoordinate(dataRead.x+1, 0, 2, 1, config.http_toleranceZeroX_Per, config.http_toleranceEndPer); - dataRead.y = scaleCoordinate(dataRead.y+1, 0, 2, 1, config.http_toleranceZeroY_Per, config.http_toleranceEndPer); - //--- re-calculate radius, angle and position with new/scaled coordinates --- - dataRead.radius = sqrt(pow(dataRead.x,2) + pow(dataRead.y,2)); - dataRead.angle = (atan(dataRead.y/dataRead.x) * 180) / 3.141; - dataRead.position = joystick_evaluatePosition(dataRead.x, dataRead.y); - - ESP_LOGD(TAG, "processed/scaled data: x=%.3f y=%.3f radius=%.3f", dataRead.x, dataRead.y, dataRead.radius); - - //--- generate motor commands --- - //pass received joystick data from http queue to generatecommands function from joystick.hpp - ESP_LOGV(TAG, "generating commands..."); - ESP_LOGD(TAG, "generating commands from x=%.3f y=%.3f radius=%.3f angle=%.3f", dataRead.x, dataRead.y, dataRead.radius, dataRead.angle); - commands = joystick_generateCommandsDriving(dataRead); - - //--- apply commands to motors --- - //TODO make motorctl.setTarget also accept motorcommand struct directly - motorRight->setTarget(commands.right.state, commands.right.duty); - motorLeft->setTarget(commands.left.state, commands.left.duty); - } - - //--- timeout --- - //turn off motors when motor still on and no new data received for some time - if ( - (esp_log_timestamp() - http_timestamp_lastData > config.http_timeoutMs) //no data received for x seconds - && (commands.left.state != motorstate_t::IDLE || commands.right.state != motorstate_t::IDLE) //at least one motor is still running - ){ - ESP_LOGE(TAG, "TIMEOUT - no data received for 3s -> stopping motors"); - //copy preset commands for idling both motors - commands = cmds_bothMotorsIdle; - motorRight->setTarget(commands.right.state, commands.right.duty); - motorLeft->setTarget(commands.left.state, commands.left.duty); - } - - break; - - //TODO: add other modes here + // //TODO: add other modes here } diff --git a/main/control.hpp b/main/control.hpp index a053470..cc83964 100644 --- a/main/control.hpp +++ b/main/control.hpp @@ -3,6 +3,7 @@ #include "motordrivers.hpp" #include "motorctl.hpp" #include "buzzer.hpp" +#include "http.hpp" //-------------------------------------------- @@ -19,11 +20,6 @@ typedef struct control_config_t { //--- timeout --- uint32_t timeoutMs; //time of inactivity after which the mode gets switched to IDLE float timeoutTolerancePer; //percentage the duty can vary between timeout checks considered still inactive - //--- http mode --- - float http_toleranceZeroX_Per;//percentage around joystick axis the coordinate snaps to 0 - float http_toleranceZeroY_Per; - float http_toleranceEndPer; //percentage before joystick end the coordinate snaps to 1/-1 - uint32_t http_timeoutMs; //time no new data was received before the motors get turned off } control_config_t; @@ -41,7 +37,8 @@ class controlledArmchair { control_config_t config_f, buzzer_t* buzzer_f, controlledMotor* motorLeft_f, - controlledMotor* motorRight_f + controlledMotor* motorRight_f, + httpJoystick* httpJoystick_f ); //--- functions --- @@ -70,6 +67,7 @@ class controlledArmchair { buzzer_t* buzzer; controlledMotor* motorLeft; controlledMotor* motorRight; + httpJoystick* httpJoystickMain_l; //---variables --- //struct for motor commands returned by generate functions of each mode diff --git a/main/http.cpp b/main/http.cpp index d885f63..a544c54 100644 --- a/main/http.cpp +++ b/main/http.cpp @@ -3,7 +3,6 @@ extern "C" #include #include "mdns.h" #include "cJSON.h" -#include "esp_http_server.h" #include "esp_spiffs.h" #include "esp_wifi.h" #include "freertos/FreeRTOS.h" @@ -14,14 +13,13 @@ extern "C" } #include "http.hpp" -#include "joystick.hpp" +#include "config.hpp" //tag for logging static const char * TAG = "http"; static httpd_handle_t server = NULL; -QueueHandle_t joystickDataQueue = xQueueCreate( 1, sizeof( struct joystickData_t ) ); //joystickData_t http_readFromJoystickQueue @@ -99,12 +97,25 @@ static esp_err_t on_default_url(httpd_req_t *req) -//=============================== -//====== joystick endpoint ====== -//=============================== -//function that is called when data is received with post request at /api/joystick -esp_err_t on_joystick_url(httpd_req_t *req) -{ +//============================== +//===== httpJoystick class ===== +//============================== +//----------------------- +//----- constructor ----- +//----------------------- +httpJoystick::httpJoystick( httpJoystick_config_t config_f ){ + //copy config struct + config = config_f; + //initialize queue for joystick data + QueueHandle_t joystickDataQueue = xQueueCreate( 1, sizeof( struct joystickData_t ) ); +} + + +//-------------------------- +//---- 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", "*"); @@ -134,32 +145,79 @@ esp_err_t on_joystick_url(httpd_req_t *req) data.y = static_cast(y_json->valuedouble); data.radius = static_cast(radius_json->valuedouble); data.angle = static_cast(angle_json->valuedouble); - - //--- evaluate joystick position enum --- - //data.position = joystick_evaluatePosition(data.x, data.y); - - //log received and parsed values - ESP_LOGI(TAG, "parsed values from received json: \n x=%.3f y=%.3f radius=%.3f angle=%.3f", + ESP_LOGI(TAG, "received values: x=%.3f y=%.3f radius=%.3f angle=%.3f", data.x, data.y, data.radius, data.angle); + // 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); + + //--- re-calculate radius, angle and position with new/scaled coordinates --- + data.radius = sqrt(pow(data.x,2) + pow(data.y,2)); + data.angle = (atan(data.y/data.x) * 180) / 3.141; + 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); +} + + //============================ @@ -169,9 +227,6 @@ esp_err_t on_joystick_url(httpd_req_t *req) void http_init_server() { - - - //configure webserver httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.uri_match_fn = httpd_uri_match_wildcard; diff --git a/main/http.hpp b/main/http.hpp index 6723c13..eab53ef 100644 --- a/main/http.hpp +++ b/main/http.hpp @@ -1,6 +1,14 @@ #pragma once -extern QueueHandle_t joystickDataQueue; +extern "C" +{ +#include "esp_http_server.h" +} + +#include "joystick.hpp" + + + //============================ //===== init http server ===== @@ -21,3 +29,43 @@ void start_mdns_service(); //============================ //function that destroys the http server void http_stop_server(); + + +//============================== +//===== httpJoystick class ===== +//============================== +//class that receices that from a HTTP post request, generates and scales joystick data and provides the data in a queue + +//struct with configuration parameters +typedef struct httpJoystick_config_t { + float toleranceZeroX_Per;//percentage around joystick axis the coordinate snaps to 0 + float toleranceZeroY_Per; + float toleranceEndPer; //percentage before joystick end the coordinate snaps to 1/-1 + uint32_t timeoutMs; //time no new data was received before the motors get turned off +} httpJoystick_config_t; + + +class httpJoystick{ + public: + //--- constructor --- + httpJoystick( httpJoystick_config_t config_f ); + + //--- functions --- + joystickData_t getData(); //wait for and return joystick data from queue, if timeout return CENTER + + esp_err_t receiveHttpData(httpd_req_t *req); //function that is called when data is received with post request at /api/joystick + + private: + //--- variables --- + httpJoystick_config_t config; + QueueHandle_t joystickDataQueue = xQueueCreate( 1, sizeof( struct joystickData_t ) ); + //struct for receiving data from http function, and storing data of last update + joystickData_t dataRead; + const joystickData_t dataCenter = { + .position = joystickPos_t::CENTER, + .x = 0, + .y = 0, + .radius = 0, + .angle = 0 + }; +};