Merge branch 'encoder' into dev - store parameters in nvs
Several functional menu items were added, and parameters set via menu are stored in nvs and loaded again at startup
This commit is contained in:
		
						commit
						fab4d442e6
					
				| @ -67,7 +67,7 @@ void buttonCommands::action (uint8_t count, bool lastPressLong){ | |||||||
|     // ## no command ##
 |     // ## no command ##
 | ||||||
|     default: |     default: | ||||||
|         ESP_LOGE(TAG, "no command for count=%d and long=%d defined", count, lastPressLong); |         ESP_LOGE(TAG, "no command for count=%d and long=%d defined", count, lastPressLong); | ||||||
|         buzzer->beep(3, 400, 100); |         buzzer->beep(3, 200, 100); | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     case 1: |     case 1: | ||||||
| @ -76,7 +76,7 @@ void buttonCommands::action (uint8_t count, bool lastPressLong){ | |||||||
|         { |         { | ||||||
|             control->changeMode(controlMode_t::MENU); |             control->changeMode(controlMode_t::MENU); | ||||||
|             ESP_LOGW(TAG, "1x long press -> change to menu mode"); |             ESP_LOGW(TAG, "1x long press -> change to menu mode"); | ||||||
|             buzzer->beep(1, 1000, 1); |             buzzer->beep(20, 20, 10); | ||||||
|             vTaskDelay(500 / portTICK_PERIOD_MS); |             vTaskDelay(500 / portTICK_PERIOD_MS); | ||||||
|         } |         } | ||||||
|         // ## toggle joystick freeze ##
 |         // ## toggle joystick freeze ##
 | ||||||
|  | |||||||
| @ -90,6 +90,7 @@ sabertooth2x60_config_t sabertoothConfig = { | |||||||
| // TODO add motor name string -> then use as log tag?
 | // TODO add motor name string -> then use as log tag?
 | ||||||
| //--- configure left motor (contol) ---
 | //--- configure left motor (contol) ---
 | ||||||
| motorctl_config_t configMotorControlLeft = { | motorctl_config_t configMotorControlLeft = { | ||||||
|  |     .name = "left", | ||||||
|     .msFadeAccel = 1500, // acceleration of the motor (ms it takes from 0% to 100%)
 |     .msFadeAccel = 1500, // acceleration of the motor (ms it takes from 0% to 100%)
 | ||||||
|     .msFadeDecel = 1000, // deceleration of the motor (ms it takes from 100% to 0%)
 |     .msFadeDecel = 1000, // deceleration of the motor (ms it takes from 100% to 0%)
 | ||||||
|     .currentLimitEnabled = false, |     .currentLimitEnabled = false, | ||||||
| @ -101,6 +102,7 @@ motorctl_config_t configMotorControlLeft = { | |||||||
| 
 | 
 | ||||||
| //--- configure right motor (contol) ---
 | //--- configure right motor (contol) ---
 | ||||||
| motorctl_config_t configMotorControlRight = { | motorctl_config_t configMotorControlRight = { | ||||||
|  |     .name = "right", | ||||||
|     .msFadeAccel = 1500, // acceleration of the motor (ms it takes from 0% to 100%)
 |     .msFadeAccel = 1500, // acceleration of the motor (ms it takes from 0% to 100%)
 | ||||||
|     .msFadeDecel = 1000, // deceleration of the motor (ms it takes from 100% to 0%)
 |     .msFadeDecel = 1000, // deceleration of the motor (ms it takes from 100% to 0%)
 | ||||||
|     .currentLimitEnabled = false, |     .currentLimitEnabled = false, | ||||||
| @ -219,3 +221,14 @@ rotary_encoder_t encoder_config = { | |||||||
| 	.btn_pressed_time_us = 20000, | 	.btn_pressed_time_us = 20000, | ||||||
| 	.btn_state = RE_BTN_RELEASED //default state
 | 	.btn_state = RE_BTN_RELEASED //default state
 | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //-----------------------------------
 | ||||||
|  | //--- joystick command generation ---
 | ||||||
|  | //-----------------------------------
 | ||||||
|  | //configure parameters for motor command generation from joystick data
 | ||||||
|  | joystickGenerateCommands_config_t joystickGenerateCommands_config{ | ||||||
|  |     .maxDuty = 100, | ||||||
|  |     .dutyOffset = 5, // duty at which motors start immediately
 | ||||||
|  |     .altStickMapping = false, | ||||||
|  | }; | ||||||
| @ -32,14 +32,17 @@ controlledArmchair::controlledArmchair( | |||||||
|     controlledMotor *motorLeft_f, |     controlledMotor *motorLeft_f, | ||||||
|     controlledMotor *motorRight_f, |     controlledMotor *motorRight_f, | ||||||
|     evaluatedJoystick *joystick_f, |     evaluatedJoystick *joystick_f, | ||||||
|  |     joystickGenerateCommands_config_t *joystickGenerateCommands_config_f, | ||||||
|     httpJoystick *httpJoystick_f, |     httpJoystick *httpJoystick_f, | ||||||
|     automatedArmchair_c *automatedArmchair_f, |     automatedArmchair_c *automatedArmchair_f, | ||||||
|     cControlledRest *legRest_f, |     cControlledRest *legRest_f, | ||||||
|     cControlledRest *backRest_f) |     cControlledRest *backRest_f, | ||||||
|  |     nvs_handle_t * nvsHandle_f) | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     //copy configuration
 |     //copy configuration
 | ||||||
|     config = config_f; |     config = config_f; | ||||||
|  |     joystickGenerateCommands_config = *joystickGenerateCommands_config_f; | ||||||
|     //copy object pointers
 |     //copy object pointers
 | ||||||
|     buzzer = buzzer_f; |     buzzer = buzzer_f; | ||||||
|     motorLeft = motorLeft_f; |     motorLeft = motorLeft_f; | ||||||
| @ -49,9 +52,13 @@ controlledArmchair::controlledArmchair( | |||||||
|     automatedArmchair = automatedArmchair_f; |     automatedArmchair = automatedArmchair_f; | ||||||
|     legRest = legRest_f; |     legRest = legRest_f; | ||||||
|     backRest = backRest_f; |     backRest = backRest_f; | ||||||
|  |     nvsHandle = nvsHandle_f; | ||||||
|     //set default mode from config
 |     //set default mode from config
 | ||||||
|     modePrevious = config.defaultMode; |     modePrevious = config.defaultMode; | ||||||
|      |      | ||||||
|  |     // override default config value if maxDuty is found in nvs
 | ||||||
|  |     loadMaxDuty(); | ||||||
|  | 
 | ||||||
|     //TODO declare / configure controlled motors here instead of config (unnecessary that button object is globally available - only used here)?
 |     //TODO declare / configure controlled motors here instead of config (unnecessary that button object is globally available - only used here)?
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -104,7 +111,7 @@ void controlledArmchair::startHandleLoop() { | |||||||
|                 //additionaly scale coordinates (more detail in slower area)
 |                 //additionaly scale coordinates (more detail in slower area)
 | ||||||
|                 joystick_scaleCoordinatesLinear(&stickData, 0.6, 0.35); //TODO: add scaling parameters to config
 |                 joystick_scaleCoordinatesLinear(&stickData, 0.6, 0.35); //TODO: add scaling parameters to config
 | ||||||
|                 //generate motor commands
 |                 //generate motor commands
 | ||||||
|                 commands = joystick_generateCommandsDriving(stickData, altStickMapping); |                 commands = joystick_generateCommandsDriving(stickData, &joystickGenerateCommands_config); | ||||||
|                 //apply motor commands
 |                 //apply motor commands
 | ||||||
|                 motorRight->setTarget(commands.right.state, commands.right.duty);  |                 motorRight->setTarget(commands.right.state, commands.right.duty);  | ||||||
|                 motorLeft->setTarget(commands.left.state, commands.left.duty);  |                 motorLeft->setTarget(commands.left.state, commands.left.duty);  | ||||||
| @ -138,7 +145,7 @@ void controlledArmchair::startHandleLoop() { | |||||||
|                 ESP_LOGD(TAG, "generating commands from x=%.3f  y=%.3f  radius=%.3f  angle=%.3f", stickData.x, stickData.y, stickData.radius, stickData.angle); |                 ESP_LOGD(TAG, "generating commands from x=%.3f  y=%.3f  radius=%.3f  angle=%.3f", stickData.x, stickData.y, stickData.radius, stickData.angle); | ||||||
|                 //--- generate motor commands ---
 |                 //--- generate motor commands ---
 | ||||||
|                 //Note: timeout (no data received) is handled in getData method
 |                 //Note: timeout (no data received) is handled in getData method
 | ||||||
|                 commands = joystick_generateCommandsDriving(stickData, altStickMapping); |                 commands = joystick_generateCommandsDriving(stickData, &joystickGenerateCommands_config); | ||||||
| 
 | 
 | ||||||
|                 //--- apply commands to motors ---
 |                 //--- apply commands to motors ---
 | ||||||
|                 //TODO make motorctl.setTarget also accept motorcommand struct directly
 |                 //TODO make motorctl.setTarget also accept motorcommand struct directly
 | ||||||
| @ -268,8 +275,8 @@ bool controlledArmchair::toggleFreezeInputMassage() | |||||||
| // toggle between normal and alternative stick mapping (joystick reverse position inverted)
 | // toggle between normal and alternative stick mapping (joystick reverse position inverted)
 | ||||||
| bool controlledArmchair::toggleAltStickMapping() | bool controlledArmchair::toggleAltStickMapping() | ||||||
| { | { | ||||||
|     altStickMapping = !altStickMapping; |     joystickGenerateCommands_config.altStickMapping = !joystickGenerateCommands_config.altStickMapping; | ||||||
|     if (altStickMapping) |     if (joystickGenerateCommands_config.altStickMapping) | ||||||
|     { |     { | ||||||
|         buzzer->beep(6, 70, 50); |         buzzer->beep(6, 70, 50); | ||||||
|         ESP_LOGW(TAG, "changed to alternative stick mapping"); |         ESP_LOGW(TAG, "changed to alternative stick mapping"); | ||||||
| @ -279,7 +286,7 @@ bool controlledArmchair::toggleAltStickMapping() | |||||||
|         buzzer->beep(1, 500, 100); |         buzzer->beep(1, 500, 100); | ||||||
|         ESP_LOGW(TAG, "changed to default stick mapping"); |         ESP_LOGW(TAG, "changed to default stick mapping"); | ||||||
|     } |     } | ||||||
|     return altStickMapping; |     return joystickGenerateCommands_config.altStickMapping; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -419,7 +426,7 @@ void controlledArmchair::changeMode(controlMode_t modeNew) { | |||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
| 		case controlMode_t::IDLE: | 		case controlMode_t::IDLE: | ||||||
| 			buzzer->beep(1, 1500, 0); | 			buzzer->beep(1, 1000, 0); | ||||||
| #ifdef JOYSTICK_LOG_IN_IDLE | #ifdef JOYSTICK_LOG_IN_IDLE | ||||||
| 			esp_log_level_set("evaluatedJoystick", ESP_LOG_DEBUG); | 			esp_log_level_set("evaluatedJoystick", ESP_LOG_DEBUG); | ||||||
| #endif | #endif | ||||||
| @ -503,3 +510,56 @@ void controlledArmchair::toggleMode(controlMode_t modePrimary){ | |||||||
|         changeMode(modePrimary); |         changeMode(modePrimary); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //-------------------------------
 | ||||||
|  | //------ loadDecelDuration ------
 | ||||||
|  | //-------------------------------
 | ||||||
|  | // update local config value when maxDuty is stored in nvs
 | ||||||
|  | void controlledArmchair::loadMaxDuty(void) | ||||||
|  | { | ||||||
|  |     // default value is already loaded (constructor)
 | ||||||
|  |     // read from nvs
 | ||||||
|  |     uint16_t valueRead; | ||||||
|  |     esp_err_t err = nvs_get_u16(*nvsHandle, "c-maxDuty", &valueRead); | ||||||
|  |     switch (err) | ||||||
|  |     { | ||||||
|  |     case ESP_OK: | ||||||
|  |         ESP_LOGW(TAG, "Successfully read value '%s' from nvs. Overriding default value %.2f with %.2f", "c-maxDuty", joystickGenerateCommands_config.maxDuty, valueRead/100.0); | ||||||
|  |         joystickGenerateCommands_config.maxDuty = (float)(valueRead/100.0); | ||||||
|  |         break; | ||||||
|  |     case ESP_ERR_NVS_NOT_FOUND: | ||||||
|  |         ESP_LOGW(TAG, "nvs: the value '%s' is not initialized yet, keeping default value %.2f", "c-maxDuty", joystickGenerateCommands_config.maxDuty); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ESP_LOGE(TAG, "Error (%s) reading nvs!", esp_err_to_name(err)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //-----------------------------------
 | ||||||
|  | //---------- writeMaxDuty -----------
 | ||||||
|  | //-----------------------------------
 | ||||||
|  | // write provided value to nvs to be persistent and update local variable in joystickGenerateCommmands_config struct
 | ||||||
|  | // note: duty percentage gets stored as uint with factor 100 (to get more precision)
 | ||||||
|  | void controlledArmchair::writeMaxDuty(float newValue){ | ||||||
|  |     // check if unchanged
 | ||||||
|  |     if(joystickGenerateCommands_config.maxDuty == newValue){ | ||||||
|  |         ESP_LOGW(TAG, "value unchanged at %.2f, not writing to nvs", newValue); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     // update nvs value
 | ||||||
|  |     ESP_LOGW(TAG, "updating nvs value '%s' from %.2f to %.2f", "c-maxDuty", joystickGenerateCommands_config.maxDuty, newValue) ; | ||||||
|  |     esp_err_t err = nvs_set_u16(*nvsHandle, "c-maxDuty", (uint16_t)(newValue*100)); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed writing"); | ||||||
|  |     err = nvs_commit(*nvsHandle); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed committing updates"); | ||||||
|  |     else | ||||||
|  |         ESP_LOGI(TAG, "nvs: successfully committed updates"); | ||||||
|  |     // update variable
 | ||||||
|  |     joystickGenerateCommands_config.maxDuty = newValue; | ||||||
|  | } | ||||||
| @ -1,5 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | extern "C" | ||||||
|  | { | ||||||
|  | #include "nvs_flash.h" | ||||||
|  | #include "nvs.h" | ||||||
|  | } | ||||||
| #include "motordrivers.hpp" | #include "motordrivers.hpp" | ||||||
| #include "motorctl.hpp" | #include "motorctl.hpp" | ||||||
| #include "buzzer.hpp" | #include "buzzer.hpp" | ||||||
| @ -50,10 +55,12 @@ class controlledArmchair { | |||||||
|                 controlledMotor* motorLeft_f, |                 controlledMotor* motorLeft_f, | ||||||
|                 controlledMotor* motorRight_f, |                 controlledMotor* motorRight_f, | ||||||
|                 evaluatedJoystick* joystick_f, |                 evaluatedJoystick* joystick_f, | ||||||
|  |                 joystickGenerateCommands_config_t* joystickGenerateCommands_config_f, | ||||||
|                 httpJoystick* httpJoystick_f, |                 httpJoystick* httpJoystick_f, | ||||||
|                 automatedArmchair_c* automatedArmchair, |                 automatedArmchair_c* automatedArmchair, | ||||||
|                 cControlledRest * legRest, |                 cControlledRest * legRest, | ||||||
|                 cControlledRest * backRest |                 cControlledRest * backRest, | ||||||
|  |                 nvs_handle_t * nvsHandle_f | ||||||
|                 ); |                 ); | ||||||
| 
 | 
 | ||||||
|         //--- functions ---
 |         //--- functions ---
 | ||||||
| @ -79,27 +86,37 @@ class controlledArmchair { | |||||||
|         controlMode_t getCurrentMode() const {return mode;}; |         controlMode_t getCurrentMode() const {return mode;}; | ||||||
|         const char *getCurrentModeStr() const { return controlModeStr[(int)mode]; }; |         const char *getCurrentModeStr() const { return controlModeStr[(int)mode]; }; | ||||||
| 
 | 
 | ||||||
|  |         //--- mode specific ---
 | ||||||
|         // releases or locks joystick in place when in massage mode, returns true when input is frozen
 |         // releases or locks joystick in place when in massage mode, returns true when input is frozen
 | ||||||
|         bool toggleFreezeInputMassage(); |         bool toggleFreezeInputMassage(); | ||||||
| 
 |  | ||||||
|         // toggle between normal and alternative stick mapping (joystick reverse position inverted), returns true when alt mapping is active
 |         // toggle between normal and alternative stick mapping (joystick reverse position inverted), returns true when alt mapping is active
 | ||||||
|         bool toggleAltStickMapping(); |         bool toggleAltStickMapping(); | ||||||
| 
 | 
 | ||||||
|  |         // configure max dutycycle (in joystick or http mode)
 | ||||||
|  |         void setMaxDuty(float maxDutyNew) { writeMaxDuty(maxDutyNew); }; | ||||||
|  |         float getMaxDuty() const {return joystickGenerateCommands_config.maxDuty; }; | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
| 
 | 
 | ||||||
|         //--- functions ---
 |         //--- functions ---
 | ||||||
|         //function that evaluates whether there is no activity/change on the motor duty for a certain time, if so a switch to IDLE is issued. - has to be run repeatedly in a slow interval
 |         //function that evaluates whether there is no activity/change on the motor duty for a certain time, if so a switch to IDLE is issued. - has to be run repeatedly in a slow interval
 | ||||||
|         void handleTimeout(); |         void handleTimeout(); | ||||||
| 
 | 
 | ||||||
|  |         void loadMaxDuty(); //load stored value for maxDuty from nvs
 | ||||||
|  |         void writeMaxDuty(float newMaxDuty); //write new value for maxDuty to nvs
 | ||||||
|  | 
 | ||||||
|         //--- objects ---
 |         //--- objects ---
 | ||||||
|         buzzer_t* buzzer; |         buzzer_t* buzzer; | ||||||
|         controlledMotor* motorLeft; |         controlledMotor* motorLeft; | ||||||
|         controlledMotor* motorRight; |         controlledMotor* motorRight; | ||||||
|         httpJoystick* httpJoystickMain_l; |         httpJoystick* httpJoystickMain_l; | ||||||
|         evaluatedJoystick* joystick_l; |         evaluatedJoystick* joystick_l; | ||||||
|  |         joystickGenerateCommands_config_t joystickGenerateCommands_config; | ||||||
|         automatedArmchair_c *automatedArmchair; |         automatedArmchair_c *automatedArmchair; | ||||||
|         cControlledRest * legRest; |         cControlledRest * legRest; | ||||||
|         cControlledRest * backRest; |         cControlledRest * backRest; | ||||||
|  |         //handle for using the nvs flash (persistent config variables)
 | ||||||
|  |         nvs_handle_t * nvsHandle; | ||||||
| 
 | 
 | ||||||
|         //---variables ---
 |         //---variables ---
 | ||||||
|         //struct for motor commands returned by generate functions of each mode
 |         //struct for motor commands returned by generate functions of each mode
 | ||||||
| @ -109,7 +126,6 @@ class controlledArmchair { | |||||||
| 
 | 
 | ||||||
|         //store joystick data
 |         //store joystick data
 | ||||||
|         joystickData_t stickData; |         joystickData_t stickData; | ||||||
|         bool altStickMapping; //alternative joystick mapping (reverse mapped differently)
 |  | ||||||
| 
 | 
 | ||||||
|         //variables for http mode
 |         //variables for http mode
 | ||||||
|         uint32_t http_timestamp_lastData = 0; |         uint32_t http_timestamp_lastData = 0; | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ extern "C" { | |||||||
| #include "freertos/FreeRTOS.h" | #include "freertos/FreeRTOS.h" | ||||||
| #include "freertos/task.h" | #include "freertos/task.h" | ||||||
| #include "esp_log.h" | #include "esp_log.h" | ||||||
|  | #include "nvs_flash.h" | ||||||
|  | #include "nvs.h" | ||||||
| 
 | 
 | ||||||
| #include "ssd1306.h" | #include "ssd1306.h" | ||||||
| #include "font8x8_basic.h" | #include "font8x8_basic.h" | ||||||
| @ -42,6 +44,7 @@ typedef struct display_task_parameters_t { | |||||||
|     speedSensor * speedLeft; |     speedSensor * speedLeft; | ||||||
|     speedSensor * speedRight; |     speedSensor * speedRight; | ||||||
|     buzzer_t *buzzer; |     buzzer_t *buzzer; | ||||||
|  |     nvs_handle_t * nvsHandle; | ||||||
| } display_task_parameters_t; | } display_task_parameters_t; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ extern "C" | |||||||
| #include <esp_system.h> | #include <esp_system.h> | ||||||
| #include <esp_event.h> | #include <esp_event.h> | ||||||
| #include <nvs_flash.h> | #include <nvs_flash.h> | ||||||
|  | #include "nvs.h" | ||||||
| #include "freertos/FreeRTOS.h" | #include "freertos/FreeRTOS.h" | ||||||
| #include "freertos/task.h" | #include "freertos/task.h" | ||||||
| #include "driver/gpio.h" | #include "driver/gpio.h" | ||||||
| @ -93,9 +94,12 @@ esp_err_t on_joystick_url(httpd_req_t *req) | |||||||
|     return (httpJoystickMain->*pointerToReceiveFunc)(req); |     return (httpJoystickMain->*pointerToReceiveFunc)(req); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //-- tag for logging --
 | //--- tag for logging ---
 | ||||||
| static const char * TAG = "main"; | static const char * TAG = "main"; | ||||||
| 
 | 
 | ||||||
|  | //-- handle passed to tasks for accessing nvs --
 | ||||||
|  | nvs_handle_t nvsHandle; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -140,23 +144,23 @@ void createObjects() | |||||||
| 
 | 
 | ||||||
| 	// create controlled motor instances (motorctl.hpp)
 | 	// create controlled motor instances (motorctl.hpp)
 | ||||||
|     // with configurations from config.cpp
 |     // with configurations from config.cpp
 | ||||||
|     motorLeft = new controlledMotor(setLeftFunc, configMotorControlLeft); |     motorLeft = new controlledMotor(setLeftFunc, configMotorControlLeft, &nvsHandle); | ||||||
|     motorRight = new controlledMotor(setRightFunc, configMotorControlRight); |     motorRight = new controlledMotor(setRightFunc, configMotorControlRight, &nvsHandle); | ||||||
| 
 | 
 | ||||||
|     // create speedsensor instances
 |     // create speedsensor instances
 | ||||||
|     // with configurations from config.cpp
 |     // with configurations from config.cpp
 | ||||||
|     speedLeft = new speedSensor(speedLeft_config); |     speedLeft = new speedSensor(speedLeft_config); | ||||||
|     speedRight = new speedSensor(speedRight_config); |     speedRight = new speedSensor(speedRight_config); | ||||||
| 
 | 
 | ||||||
|     // create joystic instance (joystick.hpp)
 |     // create joystick instance (joystick.hpp)
 | ||||||
|     joystick = new evaluatedJoystick(configJoystick); |     joystick = new evaluatedJoystick(configJoystick, &nvsHandle); | ||||||
| 
 | 
 | ||||||
|     // create httpJoystick object (http.hpp)
 |     // create httpJoystick object (http.hpp)
 | ||||||
|     httpJoystickMain = new httpJoystick(configHttpJoystickMain); |     httpJoystickMain = new httpJoystick(configHttpJoystickMain); | ||||||
|     http_init_server(on_joystick_url); |     http_init_server(on_joystick_url); | ||||||
| 
 | 
 | ||||||
|     // create buzzer object on pin 12 with gap between queued events of 100ms
 |     // create buzzer object on pin 12 with gap between queued events of 1ms
 | ||||||
|     buzzer = new buzzer_t(GPIO_NUM_12, 100); |     buzzer = new buzzer_t(GPIO_NUM_12, 1); | ||||||
| 
 | 
 | ||||||
|     // create objects for controlling the chair position
 |     // create objects for controlling the chair position
 | ||||||
|     //                       gpio_up, gpio_down, name
 |     //                       gpio_up, gpio_down, name
 | ||||||
| @ -165,7 +169,7 @@ void createObjects() | |||||||
| 
 | 
 | ||||||
|     // create control object (control.hpp)
 |     // create control object (control.hpp)
 | ||||||
|     // with configuration from config.cpp
 |     // with configuration from config.cpp
 | ||||||
|     control = new controlledArmchair(configControl, buzzer, motorLeft, motorRight, joystick, httpJoystickMain, automatedArmchair, legRest, backRest); |     control = new controlledArmchair(configControl, buzzer, motorLeft, motorRight, joystick, &joystickGenerateCommands_config, httpJoystickMain, automatedArmchair, legRest, backRest, &nvsHandle); | ||||||
| 
 | 
 | ||||||
|     // create automatedArmchair_c object (for auto-mode) (auto.hpp)
 |     // create automatedArmchair_c object (for auto-mode) (auto.hpp)
 | ||||||
|     automatedArmchair = new automatedArmchair_c(motorLeft, motorRight); |     automatedArmchair = new automatedArmchair_c(motorLeft, motorRight); | ||||||
| @ -207,6 +211,22 @@ extern "C" void app_main(void) { | |||||||
| 	//--- initialize encoder ---
 | 	//--- initialize encoder ---
 | ||||||
| 	const QueueHandle_t encoderQueue = encoder_init(&encoder_config); | 	const QueueHandle_t encoderQueue = encoder_init(&encoder_config); | ||||||
| 
 | 
 | ||||||
|  | 	//--- initialize nvs-flash ---  (for persistant config values)
 | ||||||
|  | 	ESP_LOGW(TAG, "initializing nvs-flash..."); | ||||||
|  | 	esp_err_t err = nvs_flash_init(); | ||||||
|  | 	if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) | ||||||
|  | 	{ | ||||||
|  | 		ESP_LOGE(TAG, "NVS truncated -> deleting flash"); | ||||||
|  | 		// Retry nvs_flash_init
 | ||||||
|  | 		ESP_ERROR_CHECK(nvs_flash_erase()); | ||||||
|  | 		err = nvs_flash_init(); | ||||||
|  | 	} | ||||||
|  | 	ESP_ERROR_CHECK(err); | ||||||
|  | 	//--- open nvs-flash ---
 | ||||||
|  | 	err = nvs_open("storage", NVS_READWRITE, &nvsHandle); | ||||||
|  | 	if (err != ESP_OK) | ||||||
|  | 		ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); | ||||||
|  | 
 | ||||||
| 	printf("\n"); | 	printf("\n"); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -266,8 +286,8 @@ extern "C" void app_main(void) { | |||||||
| 	//-----------------------------------
 | 	//-----------------------------------
 | ||||||
| 	//----- create task for display -----
 | 	//----- create task for display -----
 | ||||||
| 	//-----------------------------------
 | 	//-----------------------------------
 | ||||||
| 	////task that handles the display (show stats, handle menu in 'MENU' mode)
 | 	//task that handles the display (show stats, handle menu in 'MENU' mode)
 | ||||||
| 	display_task_parameters_t display_param = {display_config, control, joystick, encoderQueue, motorLeft, motorRight, speedLeft, speedRight, buzzer}; | 	display_task_parameters_t display_param = {display_config, control, joystick, encoderQueue, motorLeft, motorRight, speedLeft, speedRight, buzzer, &nvsHandle}; | ||||||
| 	xTaskCreate(&display_task, "display_task", 3*2048, &display_param, 3, NULL); | 	xTaskCreate(&display_task, "display_task", 3*2048, &display_param, 3, NULL); | ||||||
| 
 | 
 | ||||||
| 	vTaskDelay(200 / portTICK_PERIOD_MS); //wait for all tasks to finish initializing
 | 	vTaskDelay(200 / portTICK_PERIOD_MS); //wait for all tasks to finish initializing
 | ||||||
|  | |||||||
| @ -25,7 +25,10 @@ static int value = 0; | |||||||
| //================================
 | //================================
 | ||||||
| //===== CONFIGURE MENU ITEMS =====
 | //===== CONFIGURE MENU ITEMS =====
 | ||||||
| //================================
 | //================================
 | ||||||
| // note: when line4 * and line5 * are empty the value is printed large
 | // Instructions / Behavior:
 | ||||||
|  | // - when line4 * and line5 * are empty the value is printed large
 | ||||||
|  | // - when 3rd element is not NULL (pointer to defaultValue function) return int value of that function is shown in line 2
 | ||||||
|  | // - when 2nd element is NULL (pointer to currentValue function): instead of current value "click to confirm is shown" in line 3
 | ||||||
| 
 | 
 | ||||||
| //#########################
 | //#########################
 | ||||||
| //#### center Joystick ####
 | //#### center Joystick ####
 | ||||||
| @ -33,27 +36,149 @@ static int value = 0; | |||||||
| void item_centerJoystick_action(display_task_parameters_t * objects, SSD1306_t * display, int value){ | void item_centerJoystick_action(display_task_parameters_t * objects, SSD1306_t * display, int value){ | ||||||
|     if (!value) return; |     if (!value) return; | ||||||
|     ESP_LOGW(TAG, "defining joystick center"); |     ESP_LOGW(TAG, "defining joystick center"); | ||||||
|     (*objects).joystick->defineCenter(); |     objects->joystick->defineCenter(); | ||||||
|     //objects->joystick->defineCenter();
 |     objects->buzzer->beep(3, 60, 40); | ||||||
|     //joystick->defineCenter();
 |  | ||||||
| } | } | ||||||
| int item_centerJoystick_value(display_task_parameters_t * objects){ |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| menuItem_t item_centerJoystick = { | menuItem_t item_centerJoystick = { | ||||||
|     item_centerJoystick_action, // function action
 |     item_centerJoystick_action, // function action
 | ||||||
|     item_centerJoystick_value, |     NULL,                       // function get initial value or NULL(show in line 2)
 | ||||||
|     0,                      // valueMin
 |     NULL,                       // function get default value or NULL(dont set value, show msg)
 | ||||||
|     1,                      // valueMAx
 |     0,                          // valueMin
 | ||||||
|     1,                      // valueIncrement
 |     0,                          // valueMax
 | ||||||
|     "Center Joystick",      // title
 |     0,                          // valueIncrement
 | ||||||
|     "Center Joystick", // line1 (above value)
 |     "Center Joystick ",          // title
 | ||||||
|     "click to confirm",   // line2 (above value)
 |     "Center Joystick ",          // line1 (above value)
 | ||||||
|     "defines current",                     // line4 * (below value)
 |     "",                         // line2 (above value)
 | ||||||
|     "pos as center",                     // line5 *
 |     "defines current ",          // line4 * (below value)
 | ||||||
|     "click to confirm",     // line6
 |     "pos as center   ",            // line5 *
 | ||||||
|     "set 0 to cancel",  // line7
 |     "",                         // line6
 | ||||||
|  |     "=>long to cancel",           // line7
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // ############################
 | ||||||
|  | // #### calibrate Joystick ####
 | ||||||
|  | // ############################
 | ||||||
|  | // continously show/update joystick data on display
 | ||||||
|  | #define CALIBRATE_JOYSTICK_UPDATE_INTERVAL 50 | ||||||
|  | void item_calibrateJoystick_action(display_task_parameters_t *objects, SSD1306_t *display, int value) | ||||||
|  | { | ||||||
|  |     //--- variables ---
 | ||||||
|  |     bool running = true; | ||||||
|  |     joystickCalibrationMode_t mode = X_MIN; | ||||||
|  |     rotary_encoder_event_t event; | ||||||
|  |     int valueNow = 0; | ||||||
|  | 
 | ||||||
|  |     //-- pre loop instructions --
 | ||||||
|  |     ESP_LOGW(TAG, "starting joystick calibration sequence"); | ||||||
|  |     ssd1306_clear_screen(display, false); | ||||||
|  | 
 | ||||||
|  |     //-- show static lines --
 | ||||||
|  |     // show first line (title)
 | ||||||
|  |     displayTextLine(display, 0, false, true, "calibrate stick"); | ||||||
|  |     // show last line (info)
 | ||||||
|  |     displayTextLineCentered(display, 7, false, true, " click: confirm "); | ||||||
|  |     // show initital state
 | ||||||
|  |     displayTextLineCentered(display, 1, true, false, "%s", "X-min"); | ||||||
|  | 
 | ||||||
|  |     //-- loop until all positions are defined --
 | ||||||
|  |     while (running && objects->control->getCurrentMode() == controlMode_t::MENU) | ||||||
|  |     { | ||||||
|  |         // repeatedly print adc value depending on currently selected axis
 | ||||||
|  |         switch (mode) | ||||||
|  |         { | ||||||
|  |         case X_MIN: | ||||||
|  |         case X_MAX: | ||||||
|  |             displayTextLineCentered(display, 4, true, false, "%d", valueNow = objects->joystick->getRawX()); // large
 | ||||||
|  |             break; | ||||||
|  |         case Y_MIN: | ||||||
|  |         case Y_MAX: | ||||||
|  |             displayTextLineCentered(display, 4, true, false, "%d", valueNow = objects->joystick->getRawY()); // large
 | ||||||
|  |             break; | ||||||
|  |         case X_CENTER: | ||||||
|  |         case Y_CENTER: | ||||||
|  |             displayTextLine(display, 4, false, false, "    x = %d", objects->joystick->getRawX()); | ||||||
|  |             displayTextLine(display, 5, false, false, "    y = %d", objects->joystick->getRawY()); | ||||||
|  |             displayTextLine(display, 6, false, false, "release & click!"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // handle encoder event
 | ||||||
|  |         // save and next when button clicked, exit when long pressed
 | ||||||
|  |         if (xQueueReceive(objects->encoderQueue, &event, CALIBRATE_JOYSTICK_UPDATE_INTERVAL / portTICK_PERIOD_MS)) | ||||||
|  |         { | ||||||
|  |             objects->control->resetTimeout(); | ||||||
|  |             switch (event.type) | ||||||
|  |             { | ||||||
|  |             case RE_ET_BTN_CLICKED: | ||||||
|  |                 objects->buzzer->beep(2, 120, 50); | ||||||
|  |                 switch (mode) | ||||||
|  |                 { | ||||||
|  |                 case X_MIN: | ||||||
|  |                     // save x min position
 | ||||||
|  |                     ESP_LOGW(TAG, "calibrate-stick: saving  X_MIN"); | ||||||
|  |                     objects->joystick->writeCalibration(mode, valueNow); | ||||||
|  |                     displayTextLineCentered(display, 1, true, false, "%s", "X-max"); | ||||||
|  |                     mode = X_MAX; | ||||||
|  |                     break; | ||||||
|  |                 case X_MAX: | ||||||
|  |                     // save x max position
 | ||||||
|  |                     ESP_LOGW(TAG, "calibrate-stick: saving  X_MAX"); | ||||||
|  |                     objects->joystick->writeCalibration(mode, valueNow); | ||||||
|  |                     displayTextLineCentered(display, 1, true, false, "%s", "Y-min"); | ||||||
|  |                     mode = Y_MIN; | ||||||
|  |                     break; | ||||||
|  |                 case Y_MIN: | ||||||
|  |                     // save y min position
 | ||||||
|  |                     ESP_LOGW(TAG, "calibrate-stick: saving  Y_MIN"); | ||||||
|  |                     objects->joystick->writeCalibration(mode, valueNow); | ||||||
|  |                     displayTextLineCentered(display, 1, true, false, "%s", "Y-max"); | ||||||
|  |                     mode = Y_MAX; | ||||||
|  |                     break; | ||||||
|  |                 case Y_MAX: | ||||||
|  |                     // save y max position
 | ||||||
|  |                     ESP_LOGW(TAG, "calibrate-stick: saving  Y_MAX"); | ||||||
|  |                     objects->joystick->writeCalibration(mode, valueNow); | ||||||
|  |                     displayTextLineCentered(display, 1, true, false, "%s", "CENTR"); | ||||||
|  |                     mode = X_CENTER; | ||||||
|  |                     break; | ||||||
|  |                 case X_CENTER: | ||||||
|  |                 case Y_CENTER: | ||||||
|  |                     // save center position
 | ||||||
|  |                     ESP_LOGW(TAG, "calibrate-stick: saving  CENTER -> finished"); | ||||||
|  |                     objects->joystick->defineCenter(); | ||||||
|  |                     // finished
 | ||||||
|  |                     running = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case RE_ET_BTN_LONG_PRESSED: | ||||||
|  |                 //exit to main-menu
 | ||||||
|  |                 objects->buzzer->beep(1, 1000, 10); | ||||||
|  |                 ESP_LOGW(TAG, "aborting calibration sqeuence"); | ||||||
|  |                 running = false; | ||||||
|  |             case RE_ET_CHANGED: | ||||||
|  |             case RE_ET_BTN_PRESSED: | ||||||
|  |             case RE_ET_BTN_RELEASED: | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | menuItem_t item_calibrateJoystick = { | ||||||
|  |     item_calibrateJoystick_action, // function action
 | ||||||
|  |     NULL,                          // function get initial value or NULL(show in line 2)
 | ||||||
|  |     NULL,                          // function get default value or NULL(dont set value, show msg)
 | ||||||
|  |     0,                             // valueMin
 | ||||||
|  |     0,                             // valueMax
 | ||||||
|  |     0,                             // valueIncrement
 | ||||||
|  |     "Calibrate Stick ",            // title
 | ||||||
|  |     "   Calibrate    ",            // line1 (above value)
 | ||||||
|  |     "   Joystick     ",            // line2 (above value)
 | ||||||
|  |     " click to start ",            // line4 * (below value)
 | ||||||
|  |     "   sequence     ",            // line5 *
 | ||||||
|  |     "                ",            // line6
 | ||||||
|  |     "=>long to cancel",            // line7
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -61,6 +186,7 @@ menuItem_t item_centerJoystick = { | |||||||
| //#### debug Joystick ####
 | //#### debug Joystick ####
 | ||||||
| //########################
 | //########################
 | ||||||
| //continously show/update joystick data on display
 | //continously show/update joystick data on display
 | ||||||
|  | #define DEBUG_JOYSTICK_UPDATE_INTERVAL 50 | ||||||
| void item_debugJoystick_action(display_task_parameters_t * objects, SSD1306_t * display, int value) | void item_debugJoystick_action(display_task_parameters_t * objects, SSD1306_t * display, int value) | ||||||
| { | { | ||||||
|     //--- variables ---
 |     //--- variables ---
 | ||||||
| @ -68,8 +194,6 @@ void item_debugJoystick_action(display_task_parameters_t * objects, SSD1306_t * | |||||||
|     rotary_encoder_event_t event; |     rotary_encoder_event_t event; | ||||||
| 
 | 
 | ||||||
|     //-- pre loop instructions --
 |     //-- pre loop instructions --
 | ||||||
|     if (!value) // dont open menu when value was set to 0
 |  | ||||||
|         return; |  | ||||||
|     ESP_LOGW(TAG, "showing joystick debug page"); |     ESP_LOGW(TAG, "showing joystick debug page"); | ||||||
|     ssd1306_clear_screen(display, false); |     ssd1306_clear_screen(display, false); | ||||||
|     // show title
 |     // show title
 | ||||||
| @ -90,40 +214,39 @@ void item_debugJoystick_action(display_task_parameters_t * objects, SSD1306_t * | |||||||
|         displayTextLine(display, 5, false, false, "pos=%-12s ", joystickPosStr[(int)data.position]); |         displayTextLine(display, 5, false, false, "pos=%-12s ", joystickPosStr[(int)data.position]); | ||||||
| 
 | 
 | ||||||
|         // exit when button pressed
 |         // exit when button pressed
 | ||||||
|         if (xQueueReceive(objects->encoderQueue, &event, 20 / portTICK_PERIOD_MS)) |         if (xQueueReceive(objects->encoderQueue, &event, DEBUG_JOYSTICK_UPDATE_INTERVAL / portTICK_PERIOD_MS)) | ||||||
|         { |         { | ||||||
|  |             objects->control->resetTimeout(); | ||||||
|             switch (event.type) |             switch (event.type) | ||||||
|             { |             { | ||||||
|             case RE_ET_BTN_CLICKED: |             case RE_ET_BTN_CLICKED: | ||||||
|  |             case RE_ET_BTN_LONG_PRESSED: | ||||||
|                 running = false; |                 running = false; | ||||||
|  |                 objects->buzzer->beep(1, 100, 10); | ||||||
|                 break; |                 break; | ||||||
|             case RE_ET_CHANGED: |             case RE_ET_CHANGED: | ||||||
|             case RE_ET_BTN_PRESSED: |             case RE_ET_BTN_PRESSED: | ||||||
|             case RE_ET_BTN_RELEASED: |             case RE_ET_BTN_RELEASED: | ||||||
|             case RE_ET_BTN_LONG_PRESSED: |  | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int item_debugJoystick_value(display_task_parameters_t * objects){ |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| menuItem_t item_debugJoystick = { | menuItem_t item_debugJoystick = { | ||||||
|     item_debugJoystick_action, // function action
 |     item_debugJoystick_action, // function action
 | ||||||
|     item_debugJoystick_value, |     NULL,                      // function get initial value or NULL(show in line 2)
 | ||||||
|     0,                      // valueMin
 |     NULL,                      // function get default value or NULL(dont set value, show msg)
 | ||||||
|     1,                      // valueMAx
 |     0,                         // valueMin
 | ||||||
|     1,                      // valueIncrement
 |     0,                         // valueMax
 | ||||||
|     "Debug joystick",      // title
 |     0,                         // valueIncrement
 | ||||||
|     "Debug joystick", // line1 (above value)
 |     "Debug joystick  ",        // title
 | ||||||
|     "",   // line2 (above value)
 |     "Debug joystick  ",        // line1 (above value)
 | ||||||
|     "click to enter",                     // line4 * (below value)
 |     "",                        // line2 (above value)
 | ||||||
|     "debug screen",                     // line5 *
 |     "",                        // line4 * (below value)
 | ||||||
|     "prints values",     // line6
 |     "debug screen    ",        // line5 *
 | ||||||
|     "set 0 to cancel",  // line7
 |     "prints values   ",        // line6
 | ||||||
|  |     "=>long to cancel",        // line7
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -132,29 +255,29 @@ menuItem_t item_debugJoystick = { | |||||||
| //########################
 | //########################
 | ||||||
| void maxDuty_action(display_task_parameters_t * objects, SSD1306_t * display, int value) | void maxDuty_action(display_task_parameters_t * objects, SSD1306_t * display, int value) | ||||||
| { | { | ||||||
|     //TODO actually store the value
 |     objects->control->setMaxDuty(value); | ||||||
|     ESP_LOGW(TAG, "set max duty to %d", value); |  | ||||||
| } | } | ||||||
| int maxDuty_currentValue(display_task_parameters_t * objects) | int maxDuty_currentValue(display_task_parameters_t * objects) | ||||||
| { | { | ||||||
|     //TODO get real current value
 |     return (int)objects->control->getMaxDuty(); | ||||||
|     return 84; |  | ||||||
| } | } | ||||||
| menuItem_t item_maxDuty = { | menuItem_t item_maxDuty = { | ||||||
|     maxDuty_action, // function action
 |     maxDuty_action,       // function action
 | ||||||
|     maxDuty_currentValue, |     maxDuty_currentValue, // function get initial value or NULL(show in line 2)
 | ||||||
|     1,                  // valueMin
 |     NULL,                 // function get default value or NULL(dont set value, show msg)
 | ||||||
|     99,                 // valueMAx
 |     1,                    // valueMin
 | ||||||
|     1,                  // valueIncrement
 |     100,                  // valueMax
 | ||||||
|     "max duty",     // title
 |     1,                    // valueIncrement
 | ||||||
|     "",                 // line1 (above value)
 |     "Set max Duty    ",   // title
 | ||||||
|     "  set max-duty: ", // line2 (above value)
 |     "",                   // line1 (above value)
 | ||||||
|     "",                 // line4 * (below value)
 |     "  set max-duty: ",   // line2 (above value)
 | ||||||
|     "",                 // line5 *
 |     "",                   // line4 * (below value)
 | ||||||
|     "      1-99      ", // line6
 |     "",                   // line5 *
 | ||||||
|     "     percent    ", // line7
 |     "      1-100     ",   // line6
 | ||||||
|  |     "     percent    ",   // line7
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| //######################
 | //######################
 | ||||||
| //##### accelLimit #####
 | //##### accelLimit #####
 | ||||||
| //######################
 | //######################
 | ||||||
| @ -167,21 +290,27 @@ int item_accelLimit_value(display_task_parameters_t * objects) | |||||||
| { | { | ||||||
|     return objects->motorLeft->getFade(fadeType_t::ACCEL); |     return objects->motorLeft->getFade(fadeType_t::ACCEL); | ||||||
| } | } | ||||||
|  | int item_accelLimit_default(display_task_parameters_t * objects) | ||||||
|  | { | ||||||
|  |     return objects->motorLeft->getFadeDefault(fadeType_t::ACCEL); | ||||||
|  | } | ||||||
| menuItem_t item_accelLimit = { | menuItem_t item_accelLimit = { | ||||||
|     item_accelLimit_action, // function action
 |     item_accelLimit_action,  // function action
 | ||||||
|     item_accelLimit_value, |     item_accelLimit_value,   // function get initial value or NULL(show in line 2)
 | ||||||
|     0,                // valueMin
 |     item_accelLimit_default, // function get default value or NULL(dont set value, show msg)
 | ||||||
|     10000,            // valueMAx
 |     0,                       // valueMin
 | ||||||
|     100,              // valueIncrement
 |     10000,                   // valueMax
 | ||||||
|     "Accel limit",    // title
 |     100,                     // valueIncrement
 | ||||||
|     "Accel limit /",  // line1 (above value)
 |     "Accel limit     ",      // title
 | ||||||
|     "Fade up time",   // line2 (above value)
 |     " Fade up time   ",      // line1 (above value)
 | ||||||
|     "",               // line4 * (below value)
 |     "",                      // line2 <= showing "default = %d"
 | ||||||
|     "",               // line5 *
 |     "",                      // line4 * (below value)
 | ||||||
|     "milliseconds",   // line6
 |     "",                      // line5 *
 | ||||||
|     "from 0 to 100%", // line7
 |     "milliseconds    ",      // line6
 | ||||||
|  |     "from 0 to 100%  ",      // line7
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| // ######################
 | // ######################
 | ||||||
| // ##### decelLimit #####
 | // ##### decelLimit #####
 | ||||||
| // ######################
 | // ######################
 | ||||||
| @ -194,21 +323,65 @@ int item_decelLimit_value(display_task_parameters_t * objects) | |||||||
| { | { | ||||||
|     return objects->motorLeft->getFade(fadeType_t::DECEL); |     return objects->motorLeft->getFade(fadeType_t::DECEL); | ||||||
| } | } | ||||||
|  | int item_decelLimit_default(display_task_parameters_t * objects) | ||||||
|  | { | ||||||
|  |     return objects->motorLeft->getFadeDefault(fadeType_t::DECEL); | ||||||
|  | } | ||||||
| menuItem_t item_decelLimit = { | menuItem_t item_decelLimit = { | ||||||
|     item_decelLimit_action, // function action
 |     item_decelLimit_action,  // function action
 | ||||||
|     item_decelLimit_value, |     item_decelLimit_value,   // function get initial value or NULL(show in line 2)
 | ||||||
|     0,                // valueMin
 |     item_decelLimit_default, // function get default value or NULL(dont set value, show msg)
 | ||||||
|     10000,            // valueMAx
 |     0,                       // valueMin
 | ||||||
|     100,              // valueIncrement
 |     10000,                   // valueMax
 | ||||||
|     "Decel limit",    // title
 |     100,                     // valueIncrement
 | ||||||
|     "Decel limit /",  // line1 (above value)
 |     "Decel limit     ",      // title
 | ||||||
|     "Fade down time", // line2 (above value)
 |     " Fade down time ",      // line1 (above value)
 | ||||||
|     "",               // line4 * (below value)
 |     "",                      // line2 <= showing "default = %d"
 | ||||||
|     "",               // line5 *
 |     "",                      // line4 * (below value)
 | ||||||
|     "milliseconds",   // line6
 |     "",                      // line5 *
 | ||||||
|     "from 100 to 0%", // line7
 |     "milliseconds    ",      // line6
 | ||||||
|  |     "from 100 to 0%  ",      // line7
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | //#####################
 | ||||||
|  | //####### RESET #######
 | ||||||
|  | //#####################
 | ||||||
|  | void item_reset_action(display_task_parameters_t *objects, SSD1306_t *display, int value) | ||||||
|  | { | ||||||
|  |     objects->buzzer->beep(1, 2000, 0); | ||||||
|  |     // close and erase NVS
 | ||||||
|  |     ESP_LOGW(TAG, "closing and ERASING non-volatile-storage..."); | ||||||
|  |     nvs_close(*(objects->nvsHandle)); | ||||||
|  |     ESP_ERROR_CHECK(nvs_flash_erase()); | ||||||
|  |     // show message restarting
 | ||||||
|  |     ssd1306_clear_screen(display, false); | ||||||
|  |     displayTextLineCentered(display, 0, false, true, ""); | ||||||
|  |     displayTextLineCentered(display, 1, true, true, "RE-"); | ||||||
|  |     displayTextLineCentered(display, 4, true, true, "START"); | ||||||
|  |     displayTextLineCentered(display, 7, false, true, ""); | ||||||
|  |     vTaskDelay(1000 / portTICK_PERIOD_MS); // wait for buzzer to beep
 | ||||||
|  |     // restart
 | ||||||
|  |     ESP_LOGW(TAG, "RESTARTING"); | ||||||
|  |     esp_restart(); | ||||||
|  | } | ||||||
|  | menuItem_t item_reset = { | ||||||
|  |     item_reset_action,  // function action
 | ||||||
|  |     NULL,               // function get initial value or NULL(show in line 2)
 | ||||||
|  |     NULL,               // function get default value or NULL(dont set value, show msg)
 | ||||||
|  |     0,                  // valueMin
 | ||||||
|  |     0,                  // valueMax
 | ||||||
|  |     0,                  // valueIncrement
 | ||||||
|  |     "RESET defaults  ", // title
 | ||||||
|  |     "   reset nvs    ", // line1 (above value)
 | ||||||
|  |     "  and restart   ", // line2 <= showing "default = %d"
 | ||||||
|  |     "reset all stored", // line4 * (below value)
 | ||||||
|  |     "   parameters   ", // line5 *
 | ||||||
|  |     "",                 // line6
 | ||||||
|  |     "=>long to cancel", // line7
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| //#####################
 | //#####################
 | ||||||
| //###### example ######
 | //###### example ######
 | ||||||
| //#####################
 | //#####################
 | ||||||
| @ -217,42 +390,49 @@ void item_example_action(display_task_parameters_t * objects, SSD1306_t * displa | |||||||
|     return; |     return; | ||||||
| } | } | ||||||
| int item_example_value(display_task_parameters_t * objects){ | int item_example_value(display_task_parameters_t * objects){ | ||||||
|     return 53; |     return 53; //initial value shown / changed from
 | ||||||
|  | } | ||||||
|  | int item_example_valueDefault(display_task_parameters_t * objects){ | ||||||
|  |     return 931; // optionally shown in line 2 as "default = %d"
 | ||||||
| } | } | ||||||
| menuItem_t item_example = { | menuItem_t item_example = { | ||||||
|     item_example_action, // function action
 |     item_example_action, // function action
 | ||||||
|     item_example_value, |     item_example_value,  // function get initial value or NULL(show in line 2)
 | ||||||
|     -255,             // valueMin
 |     NULL,                // function get default value or NULL(dont set value, show msg)
 | ||||||
|     255,              // valueMAx
 |     -255,                // valueMin
 | ||||||
|     2,                // valueIncrement
 |     255,                 // valueMax
 | ||||||
|     "example-item-max",     // title
 |     2,                   // valueIncrement
 | ||||||
|     "line 1 - above", // line1 (above value)
 |     "example-item-max",  // title
 | ||||||
|     "line 2 - above", // line2 (above value)
 |     "line 1 - above  ",  // line1 (above value)
 | ||||||
|     "line 4 - below", // line4 * (below value)
 |     "line 2 - above  ",  // line2 (above value)
 | ||||||
|     "line 5 - below", // line5 *
 |     "line 4 - below  ",  // line4 * (below value)
 | ||||||
|     "line 6 - below", // line6
 |     "line 5 - below  ",  // line5 *
 | ||||||
|     "line 7 - last",  // line7
 |     "line 6 - below  ",  // line6
 | ||||||
|  |     "line 7 - last   ",  // line7
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| menuItem_t item_last = { | menuItem_t item_last = { | ||||||
|     item_example_action, // function action
 |     item_example_action, // function action
 | ||||||
|     item_example_value, |     item_example_value,  // function get initial value or NULL(show in line 2)
 | ||||||
|     -500,               // valueMin
 |     item_example_valueDefault, // function get default value or NULL(dont set value, show msg)
 | ||||||
|     4500,               // valueMAx
 |     -500,                // valueMin
 | ||||||
|     50,                 // valueIncrement
 |     4500,                // valueMax
 | ||||||
|     "set large number", // title
 |     50,                  // valueIncrement
 | ||||||
|     "line 1 - above",   // line1 (above value)
 |     "set large number",  // title
 | ||||||
|     "line 2 - above",   // line2 (above value)
 |     "line 1 - above  ",  // line1 (above value)
 | ||||||
|     "",                 // line4 * (below value)
 |     "line 2 - above  ",  // line2 (above value)
 | ||||||
|     "",                 // line5 *
 |     "",                  // line4 * (below value)
 | ||||||
|     "line 6 - below",   // line6
 |     "",                  // line5 *
 | ||||||
|     "line 7 - last",    // line7
 |     "line 6 - below  ",  // line6
 | ||||||
| 
 |     "line 7 - last   ",  // line7
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| //store all configured menu items in one array
 | 
 | ||||||
| menuItem_t menuItems[] = {item_centerJoystick, item_debugJoystick, item_accelLimit, item_decelLimit, item_example, item_last}; | //####################################################
 | ||||||
| int itemCount = 6; | //### store all configured menu items in one array ###
 | ||||||
|  | //####################################################
 | ||||||
|  | const menuItem_t menuItems[] = {item_centerJoystick, item_calibrateJoystick, item_debugJoystick, item_maxDuty, item_accelLimit, item_decelLimit, item_reset, item_example, item_last}; | ||||||
|  | const int itemCount = 9; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -298,43 +478,79 @@ void showItemList(SSD1306_t *display, int selectedItem) | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //---------------------------
 | 
 | ||||||
| //----- showValueSelect -----
 | //-----------------------------
 | ||||||
| //---------------------------
 | //--- showValueSelectStatic ---
 | ||||||
| // function that renders value-select screen to display (one update)
 | //-----------------------------
 | ||||||
| // shows configured text of selected item and currently selected value
 | // function that renders lines that do not update of value-select screen to display (initial update)
 | ||||||
| // TODO show previous value in one line?
 | // shows configured text of currently selected item
 | ||||||
| // TODO update changed line only (value)
 | void showValueSelectStatic(display_task_parameters_t * objects, SSD1306_t *display, int selectedItem) | ||||||
| void showValueSelect(SSD1306_t *display, int selectedItem) |  | ||||||
| { | { | ||||||
|     //-- show title line --
 |     //-- show title line --
 | ||||||
|     displayTextLine(display, 0, false, true, " -- set value -- "); // inverted
 |     displayTextLine(display, 0, false, true, " -- set value -- "); // inverted
 | ||||||
| 
 | 
 | ||||||
|     //-- show text above value --
 |     //-- show text above value --
 | ||||||
|     displayTextLine(display, 1, false, false, "%-16s", menuItems[selectedItem].line1); |     displayTextLine(display, 1, false, false, "%-16s", menuItems[selectedItem].line1); | ||||||
|     displayTextLine(display, 2, false, false, "%-16s", menuItems[selectedItem].line2); | 
 | ||||||
|  |     //-- show line 2 or default value ---
 | ||||||
|  |     if (menuItems[selectedItem].defaultValue != NULL){ | ||||||
|  |         displayTextLineCentered(display, 2, false, false, "default = %d", menuItems[selectedItem].defaultValue(objects)); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         // displayTextLine(display, 2, false, false, "previous=%d", menuItems[selectedItem].currentValue(objects)); // <= show previous value
 | ||||||
|  |         displayTextLine(display, 2, false, false, "%-16s", menuItems[selectedItem].line2); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     //-- show value and other configured lines --
 |     //-- show value and other configured lines --
 | ||||||
|     // print value large, if 2 description lines are empty
 |     // print value large, if two description lines are empty
 | ||||||
|     if (strlen(menuItems[selectedItem].line4) == 0 && strlen(menuItems[selectedItem].line5) == 0) |     if (strlen(menuItems[selectedItem].line4) == 0 && strlen(menuItems[selectedItem].line5) == 0) | ||||||
|     { |     { | ||||||
|         // print large value + line5 and line6
 |         // print less lines: line5 and line6 only (due to large value)
 | ||||||
|         displayTextLineCentered(display, 3, true, false, "%d", value); //large centered
 |         //displayTextLineCentered(display, 3, true, false, "%d", value); //large centered (value shown in separate function)
 | ||||||
|         displayTextLine(display, 6, false, false, "%-16s", menuItems[selectedItem].line6); |         displayTextLine(display, 6, false, false, "%-16s", menuItems[selectedItem].line6); | ||||||
|         displayTextLine(display, 7, false, false, "%-16s", menuItems[selectedItem].line7); |         displayTextLine(display, 7, false, false, "%-16s", menuItems[selectedItem].line7); | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|         displayTextLineCentered(display, 3, false, false, "%d", value); //centered
 |         //displayTextLineCentered(display, 3, false, false, "%d", value); //centered (value shown in separate function)
 | ||||||
|         // print description lines 4 to 7
 |         // print description lines 4 to 7
 | ||||||
|         displayTextLine(display, 4, false, false, "%-16s", menuItems[selectedItem].line4); |         displayTextLine(display, 4, false, false, "%-16s", menuItems[selectedItem].line4); | ||||||
|         displayTextLine(display, 5, false, false, "%-16s", menuItems[selectedItem].line5); |         displayTextLine(display, 5, false, false, "%-16s", menuItems[selectedItem].line5); | ||||||
|         displayTextLine(display, 6, false, false, "%-16s", menuItems[selectedItem].line6); |         displayTextLine(display, 6, false, false, "%-16s", menuItems[selectedItem].line6); | ||||||
|         displayTextLine(display, 7, false, false, "%-16s", menuItems[selectedItem].line7); |         displayTextLine(display, 7, false, false, "%-16s", menuItems[selectedItem].line7); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     //-- show info msg instead of value --
 | ||||||
|  |     //when pointer to default value func not defined (set value not used, action only)
 | ||||||
|  |     if (menuItems[selectedItem].currentValue == NULL) | ||||||
|  |     { | ||||||
|  |         //show static text
 | ||||||
|  |         displayTextLineCentered(display, 3, false, true, "%s", "click to confirm"); | ||||||
|  |     } | ||||||
|  |     // otherwise value gets updated in next iteration of menu-handle function
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | //-----------------------------
 | ||||||
|  | //----- updateValueSelect -----
 | ||||||
|  | //-----------------------------
 | ||||||
|  | // update line with currently set value only (increses performance significantly)
 | ||||||
|  | void updateValueSelect(SSD1306_t *display, int selectedItem) | ||||||
|  | { | ||||||
|  |     // print value large, if 2 description lines are empty
 | ||||||
|  |     if (strlen(menuItems[selectedItem].line4) == 0 && strlen(menuItems[selectedItem].line5) == 0) | ||||||
|  |     { | ||||||
|  |         // print large and centered value in line 3-5
 | ||||||
|  |         displayTextLineCentered(display, 3, true, false, "%d", value); // large centered
 | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         //print value centered in line 3
 | ||||||
|  |         displayTextLineCentered(display, 3, false, false, "%d", value); // centered
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| //========================
 | //========================
 | ||||||
| @ -342,8 +558,8 @@ void showValueSelect(SSD1306_t *display, int selectedItem) | |||||||
| //========================
 | //========================
 | ||||||
| //controls menu with encoder input and displays the text on oled display
 | //controls menu with encoder input and displays the text on oled display
 | ||||||
| //function is repeatedly called by display task when in menu state
 | //function is repeatedly called by display task when in menu state
 | ||||||
| #define QUEUE_TIMEOUT 3000 //timeout no encoder event - to handle timeout and not block the display loop
 | #define QUEUE_TIMEOUT 3000 //timeout no encoder event - to not block the display loop and actually handle menu-timeout
 | ||||||
| #define MENU_TIMEOUT 60000 //inactivity timeout (switch to IDLE mode)
 | #define MENU_TIMEOUT 60000 //inactivity timeout (switch to IDLE mode) note: should be smaller than IDLE timeout in control task
 | ||||||
| void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) | void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) | ||||||
| { | { | ||||||
|     static uint32_t lastActivity = 0; |     static uint32_t lastActivity = 0; | ||||||
| @ -362,7 +578,9 @@ void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) | |||||||
|         // wait for encoder event
 |         // wait for encoder event
 | ||||||
|         if (xQueueReceive(objects->encoderQueue, &event, QUEUE_TIMEOUT / portTICK_PERIOD_MS)) |         if (xQueueReceive(objects->encoderQueue, &event, QUEUE_TIMEOUT / portTICK_PERIOD_MS)) | ||||||
|         { |         { | ||||||
|  |             // reset menu- and control-timeout on any encoder event
 | ||||||
|             lastActivity = esp_log_timestamp(); |             lastActivity = esp_log_timestamp(); | ||||||
|  |             objects->control->resetTimeout(); | ||||||
|             switch (event.type) |             switch (event.type) | ||||||
|             { |             { | ||||||
|             case RE_ET_CHANGED: |             case RE_ET_CHANGED: | ||||||
| @ -370,33 +588,46 @@ void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) | |||||||
|                 if (event.diff < 0) |                 if (event.diff < 0) | ||||||
|                 { |                 { | ||||||
|                     if (selectedItem != itemCount - 1) |                     if (selectedItem != itemCount - 1) | ||||||
|  |                     { | ||||||
|  |                         objects->buzzer->beep(1, 20, 0); | ||||||
|                         selectedItem++; |                         selectedItem++; | ||||||
|                     ESP_LOGD(TAG, "showing next item: %d '%s'", selectedItem, menuItems[selectedItem].title); |                         ESP_LOGD(TAG, "showing next item: %d '%s'", selectedItem, menuItems[selectedItem].title); | ||||||
|  |                     } | ||||||
|                     //note: display will update at start of next run
 |                     //note: display will update at start of next run
 | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     if (selectedItem != 0) |                     if (selectedItem != 0) | ||||||
|  |                     { | ||||||
|  |                         objects->buzzer->beep(1, 20, 0); | ||||||
|                         selectedItem--; |                         selectedItem--; | ||||||
|                     ESP_LOGD(TAG, "showing previous item: %d '%s'", selectedItem, menuItems[selectedItem].title); |                         ESP_LOGD(TAG, "showing previous item: %d '%s'", selectedItem, menuItems[selectedItem].title); | ||||||
|  |                     } | ||||||
|                     //note: display will update at start of next run
 |                     //note: display will update at start of next run
 | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             case RE_ET_BTN_CLICKED: |             case RE_ET_BTN_CLICKED: | ||||||
|                 //--- switch to edit value page ---
 |                 //--- switch to edit value page ---
 | ||||||
|  |                 objects->buzzer->beep(1, 50, 10); | ||||||
|                 ESP_LOGI(TAG, "Button pressed - switching to state SET_VALUE"); |                 ESP_LOGI(TAG, "Button pressed - switching to state SET_VALUE"); | ||||||
|                 // change state (menu to set value)
 |                 // change state (menu to set value)
 | ||||||
|                 menuState = SET_VALUE; |                 menuState = SET_VALUE; | ||||||
|                 // get currently configured value
 |  | ||||||
|                 value = menuItems[selectedItem].currentValue(objects); |  | ||||||
|                 // clear display
 |                 // clear display
 | ||||||
|                 ssd1306_clear_screen(display, false); |                 ssd1306_clear_screen(display, false); | ||||||
|  |                 //update static content of set-value screen once at change only
 | ||||||
|  |                 showValueSelectStatic(objects, display, selectedItem); | ||||||
|  |                 // get currently configured value, when value-select feature is actually used in this item
 | ||||||
|  |                 if (menuItems[selectedItem].currentValue != NULL) | ||||||
|  |                     value = menuItems[selectedItem].currentValue(objects); | ||||||
|  |                 else | ||||||
|  |                     value = 0; | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             //exit menu mode
 |  | ||||||
|             case RE_ET_BTN_LONG_PRESSED: |             case RE_ET_BTN_LONG_PRESSED: | ||||||
|                 //change to previous mode (e.g. JOYSTICK)
 |                 //--- exit menu mode ---
 | ||||||
|  |                 // change to previous mode (e.g. JOYSTICK)
 | ||||||
|  |                 objects->buzzer->beep(12, 15, 8); | ||||||
|                 objects->control->toggleMode(controlMode_t::MENU); //currently already in MENU -> changes to previous mode
 |                 objects->control->toggleMode(controlMode_t::MENU); //currently already in MENU -> changes to previous mode
 | ||||||
|                 ssd1306_clear_screen(display, false); |                 ssd1306_clear_screen(display, false); | ||||||
|                 break; |                 break; | ||||||
| @ -412,38 +643,55 @@ void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) | |||||||
|         //---- State SET VALUE ----
 |         //---- State SET VALUE ----
 | ||||||
|         //-------------------------
 |         //-------------------------
 | ||||||
|     case SET_VALUE: |     case SET_VALUE: | ||||||
|         // wait for encoder event
 |         // update currently selected value
 | ||||||
|         showValueSelect(display, selectedItem); |         // note: static lines are updated at mode change
 | ||||||
|  |         if (menuItems[selectedItem].currentValue != NULL) // dont update when set-value not used for this item
 | ||||||
|  |             updateValueSelect(display, selectedItem); | ||||||
| 
 | 
 | ||||||
|  |         // wait for encoder event
 | ||||||
|         if (xQueueReceive(objects->encoderQueue, &event, QUEUE_TIMEOUT / portTICK_PERIOD_MS)) |         if (xQueueReceive(objects->encoderQueue, &event, QUEUE_TIMEOUT / portTICK_PERIOD_MS)) | ||||||
|         { |         { | ||||||
|             lastActivity = esp_log_timestamp(); |             objects->control->resetTimeout(); | ||||||
|             switch (event.type) |             switch (event.type) | ||||||
|             { |             { | ||||||
|             case RE_ET_CHANGED: |             case RE_ET_CHANGED: | ||||||
|                 //-- change value --
 |                 //-- change value --
 | ||||||
|                 // increment value
 |                 // no need to increment value when item configured to not show value
 | ||||||
|                 if (event.diff < 0) |                 if (menuItems[selectedItem].currentValue != NULL) | ||||||
|                     value += menuItems[selectedItem].valueIncrement; |                 { | ||||||
|                 else |                     objects->buzzer->beep(1, 25, 10); | ||||||
|                     value -= menuItems[selectedItem].valueIncrement; |                     // increment value
 | ||||||
|                 // limit to min/max range
 |                     if (event.diff < 0) | ||||||
|                 if (value > menuItems[selectedItem].valueMax) |                         value += menuItems[selectedItem].valueIncrement; | ||||||
|                     value = menuItems[selectedItem].valueMax; |                     else | ||||||
|                 if (value < menuItems[selectedItem].valueMin) |                         value -= menuItems[selectedItem].valueIncrement; | ||||||
|                     value = menuItems[selectedItem].valueMin; |                     // limit to min/max range
 | ||||||
|  |                     if (value > menuItems[selectedItem].valueMax) | ||||||
|  |                         value = menuItems[selectedItem].valueMax; | ||||||
|  |                     if (value < menuItems[selectedItem].valueMin) | ||||||
|  |                         value = menuItems[selectedItem].valueMin; | ||||||
|  |                 } | ||||||
|                 break; |                 break; | ||||||
|             case RE_ET_BTN_CLICKED: |             case RE_ET_BTN_CLICKED: | ||||||
|                 //-- apply value --
 |                 //-- apply value --
 | ||||||
|                 ESP_LOGI(TAG, "Button pressed - running action function with value=%d for item '%s'", value, menuItems[selectedItem].title); |                 ESP_LOGI(TAG, "Button pressed - running action function with value=%d for item '%s'", value, menuItems[selectedItem].title); | ||||||
|  |                 objects->buzzer->beep(2, 50, 50); | ||||||
|                 menuItems[selectedItem].action(objects, display, value); |                 menuItems[selectedItem].action(objects, display, value); | ||||||
|                 menuState = MAIN_MENU; |                 menuState = MAIN_MENU; | ||||||
|                 break; |                 break; | ||||||
|  |             case RE_ET_BTN_LONG_PRESSED: | ||||||
|  |                 //-- exit value select to main menu --
 | ||||||
|  |                 objects->buzzer->beep(2, 100, 50); | ||||||
|  |                 ssd1306_clear_screen(display, false); | ||||||
|  |                 menuState = MAIN_MENU; | ||||||
|  |                 break; | ||||||
|             case RE_ET_BTN_PRESSED: |             case RE_ET_BTN_PRESSED: | ||||||
|             case RE_ET_BTN_RELEASED: |             case RE_ET_BTN_RELEASED: | ||||||
|             case RE_ET_BTN_LONG_PRESSED: |  | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |             // reset menu- and control-timeout on any encoder event
 | ||||||
|  |             lastActivity = esp_log_timestamp(); | ||||||
|  |             objects->control->resetTimeout(); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| @ -452,7 +700,7 @@ void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) | |||||||
|     //--------------------
 |     //--------------------
 | ||||||
|     //--- menu timeout ---
 |     //--- menu timeout ---
 | ||||||
|     //--------------------
 |     //--------------------
 | ||||||
|     //close menu and switch to IDLE mode when no encoder event within MENU_TIMEOUT
 |     //close menu and switch to IDLE mode when no encoder event occured within MENU_TIMEOUT
 | ||||||
|     if (esp_log_timestamp() - lastActivity > MENU_TIMEOUT) |     if (esp_log_timestamp() - lastActivity > MENU_TIMEOUT) | ||||||
|     { |     { | ||||||
|         ESP_LOGW(TAG, "TIMEOUT - no activity for more than %ds -> closing menu, switching to IDLE", MENU_TIMEOUT/1000); |         ESP_LOGW(TAG, "TIMEOUT - no activity for more than %ds -> closing menu, switching to IDLE", MENU_TIMEOUT/1000); | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ typedef struct | |||||||
| { | { | ||||||
|     void (*action)(display_task_parameters_t * objects, SSD1306_t * display, int value);   // pointer to function run when confirmed
 |     void (*action)(display_task_parameters_t * objects, SSD1306_t * display, int value);   // pointer to function run when confirmed
 | ||||||
|     int (*currentValue)(display_task_parameters_t * objects); // pointer to function to get currently configured value
 |     int (*currentValue)(display_task_parameters_t * objects); // pointer to function to get currently configured value
 | ||||||
|  |     int (*defaultValue)(display_task_parameters_t * objects); // pointer to function to get currently configured value
 | ||||||
|     int valueMin;          // min allowed value
 |     int valueMin;          // min allowed value
 | ||||||
|     int valueMax;          // max allowed value
 |     int valueMax;          // max allowed value
 | ||||||
|     int valueIncrement;    // amount changed at one encoder tick (+/-)
 |     int valueIncrement;    // amount changed at one encoder tick (+/-)
 | ||||||
|  | |||||||
| @ -82,7 +82,7 @@ void buzzer_t::processQueue(){ | |||||||
|             // otherwise waits for at least 7 weeks
 |             // otherwise waits for at least 7 weeks
 | ||||||
|             if( xQueueReceive( beepQueue, &entryRead, portMAX_DELAY ) ) |             if( xQueueReceive( beepQueue, &entryRead, portMAX_DELAY ) ) | ||||||
|             { |             { | ||||||
|                 ESP_LOGW(TAG_BUZZER, "Read entry from queue: count=%d, msOn=%d, msOff=%d", entryRead.count, entryRead.msOn, entryRead.msOff); |                 ESP_LOGI(TAG_BUZZER, "Read entry from queue: count=%d, msOn=%d, msOff=%d", entryRead.count, entryRead.msOn, entryRead.msOff); | ||||||
| 
 | 
 | ||||||
|                 //beep requested count with requested delays
 |                 //beep requested count with requested delays
 | ||||||
|                 for (int i = entryRead.count; i--;){ |                 for (int i = entryRead.count; i--;){ | ||||||
|  | |||||||
| @ -19,8 +19,9 @@ static const char * TAG_CMD = "joystickCommands"; | |||||||
| //-------- constructor --------
 | //-------- constructor --------
 | ||||||
| //-----------------------------
 | //-----------------------------
 | ||||||
| //copy provided struct with all configuration and run init function
 | //copy provided struct with all configuration and run init function
 | ||||||
| evaluatedJoystick::evaluatedJoystick(joystick_config_t config_f){ | evaluatedJoystick::evaluatedJoystick(joystick_config_t config_f, nvs_handle_t * nvsHandle_f){ | ||||||
|     config = config_f; |     config = config_f; | ||||||
|  |     nvsHandle = nvsHandle_f; | ||||||
|     init(); |     init(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -30,7 +31,7 @@ evaluatedJoystick::evaluatedJoystick(joystick_config_t config_f){ | |||||||
| //---------- init ------------
 | //---------- init ------------
 | ||||||
| //----------------------------
 | //----------------------------
 | ||||||
| void evaluatedJoystick::init(){ | void evaluatedJoystick::init(){ | ||||||
|     ESP_LOGI(TAG, "initializing joystick"); |     ESP_LOGW(TAG, "initializing ADC's and loading calibration..."); | ||||||
|     //initialize adc
 |     //initialize adc
 | ||||||
|     adc1_config_width(ADC_WIDTH_BIT_12); //=> max resolution 4096
 |     adc1_config_width(ADC_WIDTH_BIT_12); //=> max resolution 4096
 | ||||||
|                                           |                                           | ||||||
| @ -41,6 +42,12 @@ void evaluatedJoystick::init(){ | |||||||
|     adc1_config_channel_atten(config.adc_x, ADC_ATTEN_DB_11); //max voltage
 |     adc1_config_channel_atten(config.adc_x, ADC_ATTEN_DB_11); //max voltage
 | ||||||
|     adc1_config_channel_atten(config.adc_y, ADC_ATTEN_DB_11); //max voltage
 |     adc1_config_channel_atten(config.adc_y, ADC_ATTEN_DB_11); //max voltage
 | ||||||
| 
 | 
 | ||||||
|  |     //load stored calibration values (if not found loads defaults from config)
 | ||||||
|  |     loadCalibration(X_MIN); | ||||||
|  |     loadCalibration(X_MAX); | ||||||
|  |     loadCalibration(Y_MIN); | ||||||
|  |     loadCalibration(Y_MAX); | ||||||
|  | 
 | ||||||
|     //define joystick center from current position
 |     //define joystick center from current position
 | ||||||
|     defineCenter(); //define joystick center from current position
 |     defineCenter(); //define joystick center from current position
 | ||||||
| } | } | ||||||
| @ -81,17 +88,17 @@ joystickData_t evaluatedJoystick::getData() { | |||||||
|     ESP_LOGV(TAG, "getting X coodrdinate..."); |     ESP_LOGV(TAG, "getting X coodrdinate..."); | ||||||
| 	uint32_t adcRead; | 	uint32_t adcRead; | ||||||
| 	adcRead = readAdc(config.adc_x, config.x_inverted); | 	adcRead = readAdc(config.adc_x, config.x_inverted); | ||||||
|     float x = scaleCoordinate(readAdc(config.adc_x, config.x_inverted), config.x_min, config.x_max, x_center,  config.tolerance_zeroX_per, config.tolerance_end_per); |     float x = scaleCoordinate(readAdc(config.adc_x, config.x_inverted), x_min, x_max, x_center,  config.tolerance_zeroX_per, config.tolerance_end_per); | ||||||
|     data.x = x; |     data.x = x; | ||||||
| 	ESP_LOGD(TAG, "X: adc-raw=%d \tadc-conv=%d \tmin=%d \t max=%d \tcenter=%d \tinverted=%d => x=%.3f", | 	ESP_LOGD(TAG, "X: adc-raw=%d \tadc-conv=%d \tmin=%d \t max=%d \tcenter=%d \tinverted=%d => x=%.3f", | ||||||
|         adc1_get_raw(config.adc_x), adcRead,  config.x_min, config.x_max, x_center, config.x_inverted, x); |         adc1_get_raw(config.adc_x), adcRead,  x_min, x_max, x_center, config.x_inverted, x); | ||||||
| 
 | 
 | ||||||
|     ESP_LOGV(TAG, "getting Y coodrinate..."); |     ESP_LOGV(TAG, "getting Y coodrinate..."); | ||||||
| 	adcRead = readAdc(config.adc_y, config.y_inverted); | 	adcRead = readAdc(config.adc_y, config.y_inverted); | ||||||
|     float y = scaleCoordinate(adcRead, config.y_min, config.y_max, y_center,  config.tolerance_zeroY_per, config.tolerance_end_per); |     float y = scaleCoordinate(adcRead, y_min, y_max, y_center,  config.tolerance_zeroY_per, config.tolerance_end_per); | ||||||
|     data.y = y; |     data.y = y; | ||||||
| 	ESP_LOGD(TAG, "Y: adc-raw=%d \tadc-conv=%d \tmin=%d \t max=%d \tcenter=%d \tinverted=%d => y=%.3lf", | 	ESP_LOGD(TAG, "Y: adc-raw=%d \tadc-conv=%d \tmin=%d \t max=%d \tcenter=%d \tinverted=%d => y=%.3lf", | ||||||
|         adc1_get_raw(config.adc_y), adcRead,  config.y_min, config.y_max, y_center, config.y_inverted, y); |         adc1_get_raw(config.adc_y), adcRead,  y_min, y_max, y_center, config.y_inverted, y); | ||||||
| 
 | 
 | ||||||
|     //calculate radius
 |     //calculate radius
 | ||||||
|     data.radius = sqrt(pow(data.x,2) + pow(data.y,2)); |     data.radius = sqrt(pow(data.x,2) + pow(data.y,2)); | ||||||
| @ -297,7 +304,7 @@ joystickPos_t joystick_evaluatePosition(float x, float y){ | |||||||
| //========= joystick_CommandsDriving =========
 | //========= joystick_CommandsDriving =========
 | ||||||
| //============================================
 | //============================================
 | ||||||
| //function that generates commands for both motors from the joystick data
 | //function that generates commands for both motors from the joystick data
 | ||||||
| motorCommands_t joystick_generateCommandsDriving(joystickData_t data, bool altStickMapping){ | motorCommands_t joystick_generateCommandsDriving(joystickData_t data, joystickGenerateCommands_config_t * config){ | ||||||
| 
 | 
 | ||||||
|     //struct with current data of the joystick
 |     //struct with current data of the joystick
 | ||||||
|     //typedef struct joystickData_t {
 |     //typedef struct joystickData_t {
 | ||||||
| @ -310,10 +317,8 @@ motorCommands_t joystick_generateCommandsDriving(joystickData_t data, bool altSt | |||||||
| 
 | 
 | ||||||
| 	//--- variables ---
 | 	//--- variables ---
 | ||||||
|     motorCommands_t commands; |     motorCommands_t commands; | ||||||
|     float dutyMax = 100; //TODO add this to config, make changeable during runtime
 |  | ||||||
| 
 |  | ||||||
|     float dutyOffset = 5; //immediately starts with this duty, TODO add this to config
 |     float dutyOffset = 5; //immediately starts with this duty, TODO add this to config
 | ||||||
|     float dutyRange = dutyMax - dutyOffset; |     float dutyRange = config->maxDuty - config->dutyOffset; | ||||||
|     float ratio = fabs(data.angle) / 90; //90degree = x=0 || 0degree = y=0
 |     float ratio = fabs(data.angle) / 90; //90degree = x=0 || 0degree = y=0
 | ||||||
| 	 | 	 | ||||||
| 	//--- snap ratio to max at angle threshold --- 
 | 	//--- snap ratio to max at angle threshold --- 
 | ||||||
| @ -327,7 +332,7 @@ motorCommands_t joystick_generateCommandsDriving(joystickData_t data, bool altSt | |||||||
| 	*/ | 	*/ | ||||||
| 
 | 
 | ||||||
|     //--- experimental alternative control mode ---
 |     //--- experimental alternative control mode ---
 | ||||||
|     if (altStickMapping == true){ |     if (config->altStickMapping == true){ | ||||||
|         //swap BOTTOM_LEFT and BOTTOM_RIGHT
 |         //swap BOTTOM_LEFT and BOTTOM_RIGHT
 | ||||||
|         if (data.position == joystickPos_t::BOTTOM_LEFT){ |         if (data.position == joystickPos_t::BOTTOM_LEFT){ | ||||||
|             data.position = joystickPos_t::BOTTOM_RIGHT; |             data.position = joystickPos_t::BOTTOM_RIGHT; | ||||||
| @ -570,3 +575,123 @@ motorCommands_t joystick_generateCommandsShaking(joystickData_t data){ | |||||||
| 
 | 
 | ||||||
|     return commands; |     return commands; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // corresponding storage key strings to each joystickCalibratenMode variable
 | ||||||
|  | const char *calibrationStorageKeys[] = {"stick_x-min", "stick_x-max", "stick_y-min", "stick_y-max", "", ""}; | ||||||
|  | 
 | ||||||
|  | //-------------------------------
 | ||||||
|  | //------- loadCalibration -------
 | ||||||
|  | //-------------------------------
 | ||||||
|  | // loads selected calibration value from nvs or default values from config if no data stored
 | ||||||
|  | void evaluatedJoystick::loadCalibration(joystickCalibrationMode_t mode) | ||||||
|  | { | ||||||
|  |     // determine desired variables
 | ||||||
|  |     int *configValue, *usedValue; | ||||||
|  |     switch (mode) | ||||||
|  |     { | ||||||
|  |     case X_MIN: | ||||||
|  |         configValue = &(config.x_min); | ||||||
|  |         usedValue = &x_min; | ||||||
|  |         break; | ||||||
|  |     case X_MAX: | ||||||
|  |         configValue = &(config.x_max); | ||||||
|  |         usedValue = &x_max; | ||||||
|  |         break; | ||||||
|  |     case Y_MIN: | ||||||
|  |         configValue = &(config.y_min); | ||||||
|  |         usedValue = &y_min; | ||||||
|  |         break; | ||||||
|  |     case Y_MAX: | ||||||
|  |         configValue = &(config.y_max); | ||||||
|  |         usedValue = &y_max; | ||||||
|  |         break; | ||||||
|  |     case X_CENTER: | ||||||
|  |     case Y_CENTER: | ||||||
|  |     default: | ||||||
|  |         // center position is not stored in nvs, it gets defined at startup or during calibration
 | ||||||
|  |         ESP_LOGE(TAG, "loadCalibration: 'center_x' and 'center_y' are not stored in nvs -> not assigning anything"); | ||||||
|  |         // defineCenter();
 | ||||||
|  |         return;  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // read from nvs
 | ||||||
|  |     int16_t valueRead; | ||||||
|  |     esp_err_t err = nvs_get_i16(*nvsHandle, calibrationStorageKeys[(int)mode], &valueRead); | ||||||
|  |     switch (err) | ||||||
|  |     { | ||||||
|  |     case ESP_OK: | ||||||
|  |         ESP_LOGW(TAG, "Successfully read value '%s' from nvs. Overriding default value %d with %d", calibrationStorageKeys[(int)mode], *configValue, valueRead); | ||||||
|  |         *usedValue = (int)valueRead; | ||||||
|  |         break; | ||||||
|  |     case ESP_ERR_NVS_NOT_FOUND: | ||||||
|  |         ESP_LOGW(TAG, "nvs: the value '%s' is not initialized yet, loading default value %d", calibrationStorageKeys[(int)mode], *configValue); | ||||||
|  |         *usedValue = *configValue; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ESP_LOGE(TAG, "Error (%s) reading nvs!", esp_err_to_name(err)); | ||||||
|  |         *usedValue = *configValue; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //-------------------------------
 | ||||||
|  | //------- loadCalibration -------
 | ||||||
|  | //-------------------------------
 | ||||||
|  | // loads selected calibration value from nvs or default values from config if no data stored
 | ||||||
|  | void evaluatedJoystick::writeCalibration(joystickCalibrationMode_t mode, int newValue) | ||||||
|  | { | ||||||
|  |     // determine desired variables
 | ||||||
|  |     int *configValue, *usedValue; | ||||||
|  |     switch (mode) | ||||||
|  |     { | ||||||
|  |     case X_MIN: | ||||||
|  |         configValue = &(config.x_min); | ||||||
|  |         usedValue = &x_min; | ||||||
|  |         break; | ||||||
|  |     case X_MAX: | ||||||
|  |         configValue = &(config.x_max); | ||||||
|  |         usedValue = &x_max; | ||||||
|  |         break; | ||||||
|  |     case Y_MIN: | ||||||
|  |         configValue = &(config.y_min); | ||||||
|  |         usedValue = &y_min; | ||||||
|  |         break; | ||||||
|  |     case Y_MAX: | ||||||
|  |         configValue = &(config.y_max); | ||||||
|  |         usedValue = &y_max; | ||||||
|  |         break; | ||||||
|  |     case X_CENTER: | ||||||
|  |         x_center = newValue; | ||||||
|  |         ESP_LOGW(TAG, "writeCalibration: 'center_x' or 'center_y' are not stored in nvs -> loading only"); | ||||||
|  |         return; | ||||||
|  |     case Y_CENTER: | ||||||
|  |         y_center = newValue; | ||||||
|  |         ESP_LOGW(TAG, "writeCalibration: 'center_x' or 'center_y' are not stored in nvs -> loading only"); | ||||||
|  |     default: | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // check if unchanged
 | ||||||
|  |     if (*usedValue == newValue) | ||||||
|  |     { | ||||||
|  |         ESP_LOGW(TAG, "writeCalibration: value '%s' unchanged at %d, not writing to nvs", calibrationStorageKeys[(int)mode], newValue); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // update nvs value
 | ||||||
|  |     ESP_LOGW(TAG, "writeCalibration: updating nvs value '%s' from %d to %d", calibrationStorageKeys[(int)mode], *usedValue, newValue); | ||||||
|  |     esp_err_t err = nvs_set_i16(*nvsHandle, calibrationStorageKeys[(int)mode], newValue); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed writing"); | ||||||
|  |     err = nvs_commit(*nvsHandle); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed committing updates"); | ||||||
|  |     else | ||||||
|  |         ESP_LOGI(TAG, "nvs: successfully committed updates"); | ||||||
|  |     // update variable
 | ||||||
|  |     *usedValue = newValue; | ||||||
|  | } | ||||||
| @ -8,6 +8,9 @@ extern "C" | |||||||
| #include "driver/adc.h" | #include "driver/adc.h" | ||||||
| #include "esp_log.h" | #include "esp_log.h" | ||||||
| #include "esp_err.h" | #include "esp_err.h" | ||||||
|  | #include "nvs_flash.h" | ||||||
|  | #include "nvs.h" | ||||||
|  | #include <stdbool.h> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #include <cmath> | #include <cmath> | ||||||
| @ -55,6 +58,7 @@ typedef struct joystick_config_t { | |||||||
| enum class joystickPos_t {CENTER, Y_AXIS, X_AXIS, TOP_RIGHT, TOP_LEFT, BOTTOM_LEFT, BOTTOM_RIGHT}; | enum class joystickPos_t {CENTER, Y_AXIS, X_AXIS, TOP_RIGHT, TOP_LEFT, BOTTOM_LEFT, BOTTOM_RIGHT}; | ||||||
| extern const char* joystickPosStr[7]; | extern const char* joystickPosStr[7]; | ||||||
| 
 | 
 | ||||||
|  | typedef enum joystickCalibrationMode_t { X_MIN = 0, X_MAX, Y_MIN, Y_MAX, X_CENTER, Y_CENTER } joystickCalibrationMode_t; | ||||||
| 
 | 
 | ||||||
| //struct with current data of the joystick
 | //struct with current data of the joystick
 | ||||||
| typedef struct joystickData_t { | typedef struct joystickData_t { | ||||||
| @ -66,35 +70,56 @@ typedef struct joystickData_t { | |||||||
| } joystickData_t; | } joystickData_t; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // struct with parameters provided to joystick_GenerateCommandsDriving()
 | ||||||
|  | typedef struct joystickGenerateCommands_config_t { | ||||||
|  |     float maxDuty; | ||||||
|  |     float dutyOffset; | ||||||
|  |     bool altStickMapping; | ||||||
|  | } joystickGenerateCommands_config_t; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| //------------------------------------
 | //------------------------------------
 | ||||||
| //----- evaluatedJoystick class  -----
 | //----- evaluatedJoystick class  -----
 | ||||||
| //------------------------------------
 | //------------------------------------
 | ||||||
| class evaluatedJoystick { | class evaluatedJoystick | ||||||
|     public: | { | ||||||
|         //--- constructor ---
 | public: | ||||||
|         evaluatedJoystick(joystick_config_t config_f); |     //--- constructor ---
 | ||||||
|  |     evaluatedJoystick(joystick_config_t config_f, nvs_handle_t * nvsHandle); | ||||||
| 
 | 
 | ||||||
|         //--- functions ---
 |     //--- functions ---
 | ||||||
|         joystickData_t getData(); //read joystick, calculate values and return the data in a struct
 |     joystickData_t getData(); // read joystick, calculate values and return the data in a struct
 | ||||||
|         void defineCenter(); //define joystick center from current position
 |     // get raw adc value (inversion applied)
 | ||||||
|  |     int getRawX() { return readAdc(config.adc_x, config.x_inverted); } | ||||||
|  |     int getRawY() { return readAdc(config.adc_y, config.y_inverted); } | ||||||
|  |     void defineCenter(); // define joystick center from current position
 | ||||||
|  |     void writeCalibration(joystickCalibrationMode_t mode, int newValue); // load certain new calibration value and store it in nvs
 | ||||||
| 
 | 
 | ||||||
|     private: | private: | ||||||
|         //--- functions ---
 |     //--- functions ---
 | ||||||
|         //initialize adc inputs, define center
 |     // initialize adc inputs, define center
 | ||||||
|         void init(); |     void init(); | ||||||
|         //read adc while making multiple samples with option to invert the result
 |     // loads selected calibration value from nvs or default values from config if no data stored
 | ||||||
|  |     void loadCalibration(joystickCalibrationMode_t mode); | ||||||
|  |         // read adc while making multiple samples with option to invert the result
 | ||||||
|         int readAdc(adc1_channel_t adc_channel, bool inverted = false); |         int readAdc(adc1_channel_t adc_channel, bool inverted = false); | ||||||
| 
 | 
 | ||||||
|         //--- variables ---
 |         //--- variables ---
 | ||||||
|  |         // handle for using the nvs flash (persistent config variables)
 | ||||||
|  |         nvs_handle_t *nvsHandle; | ||||||
|         joystick_config_t config; |         joystick_config_t config; | ||||||
|  | 
 | ||||||
|  |         int x_min; | ||||||
|  |         int x_max; | ||||||
|  |         int y_min; | ||||||
|  |         int y_max; | ||||||
|         int x_center; |         int x_center; | ||||||
|         int y_center; |         int y_center; | ||||||
| 
 | 
 | ||||||
|         joystickData_t data; |         joystickData_t data; | ||||||
|         float x; |         float x; | ||||||
|         float y; |         float y; | ||||||
| }; |     }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -103,7 +128,7 @@ class evaluatedJoystick { | |||||||
| //============================================
 | //============================================
 | ||||||
| //function that generates commands for both motors from the joystick data
 | //function that generates commands for both motors from the joystick data
 | ||||||
| //motorCommands_t joystick_generateCommandsDriving(evaluatedJoystick joystick);
 | //motorCommands_t joystick_generateCommandsDriving(evaluatedJoystick joystick);
 | ||||||
| motorCommands_t joystick_generateCommandsDriving(joystickData_t data, bool altStickMapping = false); | motorCommands_t joystick_generateCommandsDriving(joystickData_t data, joystickGenerateCommands_config_t * config); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ static const char * TAG = "motor-control"; | |||||||
| //task for handling the motors (ramp, current limit, driver)
 | //task for handling the motors (ramp, current limit, driver)
 | ||||||
| void task_motorctl( void * task_motorctl_parameters ){ | void task_motorctl( void * task_motorctl_parameters ){ | ||||||
| 	task_motorctl_parameters_t *objects = (task_motorctl_parameters_t *)task_motorctl_parameters; | 	task_motorctl_parameters_t *objects = (task_motorctl_parameters_t *)task_motorctl_parameters; | ||||||
|     ESP_LOGW(TAG, "Task-motorctl: starting handle loop..."); |     ESP_LOGW(TAG, "Task-motorctl: starting handle loops for left and right motor..."); | ||||||
|     while(1){ |     while(1){ | ||||||
|     	objects->motorRight->handle(); |     	objects->motorRight->handle(); | ||||||
|     	objects->motorLeft->handle(); |     	objects->motorLeft->handle(); | ||||||
| @ -29,19 +29,17 @@ void task_motorctl( void * task_motorctl_parameters ){ | |||||||
| //======== constructor ========
 | //======== constructor ========
 | ||||||
| //=============================
 | //=============================
 | ||||||
| //constructor, simultaniously initialize instance of motor driver 'motor' and current sensor 'cSensor' with provided config (see below lines after ':')
 | //constructor, simultaniously initialize instance of motor driver 'motor' and current sensor 'cSensor' with provided config (see below lines after ':')
 | ||||||
| controlledMotor::controlledMotor(motorSetCommandFunc_t setCommandFunc,  motorctl_config_t config_control):  | controlledMotor::controlledMotor(motorSetCommandFunc_t setCommandFunc,  motorctl_config_t config_control, nvs_handle_t * nvsHandle_f):  | ||||||
| 	cSensor(config_control.currentSensor_adc, config_control.currentSensor_ratedCurrent) { | 	cSensor(config_control.currentSensor_adc, config_control.currentSensor_ratedCurrent) { | ||||||
| 		//copy parameters for controlling the motor
 | 		//copy parameters for controlling the motor
 | ||||||
| 		config = config_control; | 		config = config_control; | ||||||
| 		//pointer to update motot dury method
 | 		//pointer to update motot dury method
 | ||||||
| 		motorSetCommand = setCommandFunc; | 		motorSetCommand = setCommandFunc; | ||||||
| 		//copy configured default fading durations to actually used variables
 |         //pointer to nvs handle
 | ||||||
| 		msFadeAccel = config.msFadeAccel; |         nvsHandle = nvsHandle_f; | ||||||
| 		msFadeDecel = config.msFadeDecel; |  | ||||||
| 
 | 
 | ||||||
|  |         //create queue, initialize config values
 | ||||||
| 		init(); | 		init(); | ||||||
| 		//TODO: add currentsensor object here
 |  | ||||||
| 		//currentSensor cSensor(config.currentSensor_adc, config.currentSensor_ratedCurrent);
 |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -54,7 +52,11 @@ void controlledMotor::init(){ | |||||||
|     if (commandQueue == NULL) |     if (commandQueue == NULL) | ||||||
|     ESP_LOGE(TAG, "Failed to create command-queue"); |     ESP_LOGE(TAG, "Failed to create command-queue"); | ||||||
|     else |     else | ||||||
|     ESP_LOGW(TAG, "Initialized command-queue"); |     ESP_LOGI(TAG, "[%s] Initialized command-queue", config.name); | ||||||
|  | 
 | ||||||
|  |     // load config values from nvs, otherwise use default from config object
 | ||||||
|  |     loadAccelDuration(); | ||||||
|  |     loadDecelDuration(); | ||||||
| 
 | 
 | ||||||
| 	//cSensor.calibrateZeroAmpere(); //currently done in currentsensor constructor TODO do this regularly e.g. in idle?
 | 	//cSensor.calibrateZeroAmpere(); //currently done in currentsensor constructor TODO do this regularly e.g. in idle?
 | ||||||
| } | } | ||||||
| @ -104,7 +106,7 @@ void controlledMotor::handle(){ | |||||||
|     //--- receive commands from queue ---
 |     //--- receive commands from queue ---
 | ||||||
|     if( xQueueReceive( commandQueue, &commandReceive, ( TickType_t ) 0 ) ) |     if( xQueueReceive( commandQueue, &commandReceive, ( TickType_t ) 0 ) ) | ||||||
|     { |     { | ||||||
|         ESP_LOGD(TAG, "Read command from queue: state=%s, duty=%.2f", motorstateStr[(int)commandReceive.state], commandReceive.duty); |         ESP_LOGD(TAG, "[%s] Read command from queue: state=%s, duty=%.2f", config.name, motorstateStr[(int)commandReceive.state], commandReceive.duty); | ||||||
|         state = commandReceive.state; |         state = commandReceive.state; | ||||||
|         dutyTarget = commandReceive.duty; |         dutyTarget = commandReceive.duty; | ||||||
| 		receiveTimeout = false; | 		receiveTimeout = false; | ||||||
| @ -139,7 +141,7 @@ void controlledMotor::handle(){ | |||||||
| 		receiveTimeout = true; | 		receiveTimeout = true; | ||||||
| 		state = motorstate_t::IDLE; | 		state = motorstate_t::IDLE; | ||||||
| 		dutyTarget = 0; | 		dutyTarget = 0; | ||||||
| 		ESP_LOGE(TAG, "TIMEOUT, no target data received for more than %ds -> switch to IDLE", TIMEOUT_IDLE_WHEN_NO_COMMAND/1000); | 		ESP_LOGE(TAG, "[%s] TIMEOUT, no target data received for more than %ds -> switch to IDLE", config.name, TIMEOUT_IDLE_WHEN_NO_COMMAND/1000); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|     //--- calculate increment ---
 |     //--- calculate increment ---
 | ||||||
| @ -164,7 +166,7 @@ void controlledMotor::handle(){ | |||||||
| 	if (state == motorstate_t::BRAKE){ | 	if (state == motorstate_t::BRAKE){ | ||||||
| 		ESP_LOGD(TAG, "braking - skip fading"); | 		ESP_LOGD(TAG, "braking - skip fading"); | ||||||
| 		motorSetCommand({motorstate_t::BRAKE, dutyTarget}); | 		motorSetCommand({motorstate_t::BRAKE, dutyTarget}); | ||||||
| 		ESP_LOGI(TAG, "Set Motordriver: state=%s, duty=%.2f - Measurements: current=%.2f, speed=N/A", motorstateStr[(int)state], dutyNow, currentNow); | 		ESP_LOGI(TAG, "[%s] Set Motordriver: state=%s, duty=%.2f - Measurements: current=%.2f, speed=N/A", config.name, motorstateStr[(int)state], dutyNow, currentNow); | ||||||
| 		//dutyNow = 0;
 | 		//dutyNow = 0;
 | ||||||
| 		return; //no need to run the fade algorithm
 | 		return; //no need to run the fade algorithm
 | ||||||
| 	} | 	} | ||||||
| @ -211,7 +213,7 @@ void controlledMotor::handle(){ | |||||||
| 			} else if (dutyNow > currentLimitDecrement) { | 			} else if (dutyNow > currentLimitDecrement) { | ||||||
| 				dutyNow -= currentLimitDecrement; | 				dutyNow -= currentLimitDecrement; | ||||||
| 			} | 			} | ||||||
| 			ESP_LOGW(TAG, "current limit exceeded! now=%.3fA max=%.1fA => decreased duty from %.3f to %.3f", currentNow, config.currentMax, dutyOld, dutyNow); | 			ESP_LOGW(TAG, "[%s] current limit exceeded! now=%.3fA max=%.1fA => decreased duty from %.3f to %.3f", config.name, currentNow, config.currentMax, dutyOld, dutyNow); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -255,7 +257,7 @@ void controlledMotor::handle(){ | |||||||
| 
 | 
 | ||||||
|     //--- apply new target to motor ---
 |     //--- apply new target to motor ---
 | ||||||
|     motorSetCommand({state, (float)fabs(dutyNow)}); |     motorSetCommand({state, (float)fabs(dutyNow)}); | ||||||
| 	ESP_LOGI(TAG, "Set Motordriver: state=%s, duty=%.2f - Measurements: current=%.2f, speed=N/A", motorstateStr[(int)state], dutyNow, currentNow); | 	ESP_LOGI(TAG, "[%s] Set Motordriver: state=%s, duty=%.2f - Measurements: current=%.2f, speed=N/A", config.name, motorstateStr[(int)state], dutyNow, currentNow); | ||||||
|     //note: BRAKE state is handled earlier
 |     //note: BRAKE state is handled earlier
 | ||||||
|      |      | ||||||
| 
 | 
 | ||||||
| @ -275,7 +277,7 @@ void controlledMotor::setTarget(motorstate_t state_f, float duty_f){ | |||||||
|         .state = state_f, |         .state = state_f, | ||||||
|         .duty = duty_f |         .duty = duty_f | ||||||
|     }; |     }; | ||||||
|     ESP_LOGI(TAG, "setTarget: Inserting command to queue: state='%s'(%d), duty=%.2f", motorstateStr[(int)commandSend.state], (int)commandSend.state, commandSend.duty); |     ESP_LOGI(TAG, "[%s] setTarget: Inserting command to queue: state='%s'(%d), duty=%.2f", config.name, motorstateStr[(int)commandSend.state], (int)commandSend.state, commandSend.duty); | ||||||
|     //send command to queue (overwrite if an old command is still in the queue and not processed)
 |     //send command to queue (overwrite if an old command is still in the queue and not processed)
 | ||||||
|     xQueueOverwrite( commandQueue, ( void * )&commandSend); |     xQueueOverwrite( commandQueue, ( void * )&commandSend); | ||||||
|     //xQueueSend( commandQueue, ( void * )&commandSend, ( TickType_t ) 0 );
 |     //xQueueSend( commandQueue, ( void * )&commandSend, ( TickType_t ) 0 );
 | ||||||
| @ -316,6 +318,22 @@ uint32_t controlledMotor::getFade(fadeType_t fadeType){ | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | //==============================
 | ||||||
|  | //======= getFadeDefault =======
 | ||||||
|  | //==============================
 | ||||||
|  | //return default accel / decel time (from config)
 | ||||||
|  | uint32_t controlledMotor::getFadeDefault(fadeType_t fadeType){ | ||||||
|  |     switch(fadeType){ | ||||||
|  |         case fadeType_t::ACCEL: | ||||||
|  |             return config.msFadeAccel; | ||||||
|  |             break; | ||||||
|  |         case fadeType_t::DECEL: | ||||||
|  |             return config.msFadeDecel; | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| //===============================
 | //===============================
 | ||||||
| @ -328,12 +346,13 @@ void controlledMotor::setFade(fadeType_t fadeType, uint32_t msFadeNew){ | |||||||
|     //TODO: mutex for msFade variable also used in handle function
 |     //TODO: mutex for msFade variable also used in handle function
 | ||||||
|     switch(fadeType){ |     switch(fadeType){ | ||||||
|         case fadeType_t::ACCEL: |         case fadeType_t::ACCEL: | ||||||
|             ESP_LOGW(TAG, "changed fade-up time from %d to %d", msFadeAccel, msFadeNew); |             ESP_LOGW(TAG, "[%s] changed fade-up time from %d to %d", config.name, msFadeAccel, msFadeNew); | ||||||
|             msFadeAccel = msFadeNew;  |             writeAccelDuration(msFadeNew); | ||||||
|             break; |             break; | ||||||
|         case fadeType_t::DECEL: |         case fadeType_t::DECEL: | ||||||
|             ESP_LOGW(TAG, "changed fade-down time from %d to %d", msFadeDecel, msFadeNew); |             ESP_LOGW(TAG, "[%s] changed fade-down time from %d to %d",config.name, msFadeDecel, msFadeNew); | ||||||
|             msFadeDecel = msFadeNew; |             // write new value to nvs and update the variable
 | ||||||
|  |             writeDecelDuration(msFadeNew); | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -390,3 +409,118 @@ bool controlledMotor::toggleFade(fadeType_t fadeType){ | |||||||
|     return enabled; |     return enabled; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //-----------------------------
 | ||||||
|  | //----- loadAccelDuration -----
 | ||||||
|  | //-----------------------------
 | ||||||
|  | // load stored value from nvs if not successfull uses config default value
 | ||||||
|  | void controlledMotor::loadAccelDuration(void) | ||||||
|  | { | ||||||
|  |     // load default value
 | ||||||
|  |     msFadeAccel = config.msFadeAccel; | ||||||
|  |     // read from nvs
 | ||||||
|  |     uint32_t valueNew; | ||||||
|  |     char key[15]; | ||||||
|  |     snprintf(key, 15, "m-%s-accel", config.name); | ||||||
|  |     esp_err_t err = nvs_get_u32(*nvsHandle, key, &valueNew); | ||||||
|  |     switch (err) | ||||||
|  |     { | ||||||
|  |     case ESP_OK: | ||||||
|  |         ESP_LOGW(TAG, "Successfully read value '%s' from nvs. Overriding default value %d with %d", key, config.msFadeAccel, valueNew); | ||||||
|  |         msFadeAccel = valueNew; | ||||||
|  |         break; | ||||||
|  |     case ESP_ERR_NVS_NOT_FOUND: | ||||||
|  |         ESP_LOGW(TAG, "nvs: the value '%s' is not initialized yet, keeping default value %d", key, msFadeAccel); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ESP_LOGE(TAG, "Error (%s) reading nvs!", esp_err_to_name(err)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------
 | ||||||
|  | //----- loadDecelDuration -----
 | ||||||
|  | //-----------------------------
 | ||||||
|  | void controlledMotor::loadDecelDuration(void) | ||||||
|  | { | ||||||
|  |     // load default value
 | ||||||
|  |     msFadeDecel = config.msFadeDecel; | ||||||
|  |     // read from nvs
 | ||||||
|  |     uint32_t valueNew; | ||||||
|  |     char key[15]; | ||||||
|  |     snprintf(key, 15, "m-%s-decel", config.name); | ||||||
|  |     esp_err_t err = nvs_get_u32(*nvsHandle, key, &valueNew); | ||||||
|  |     switch (err) | ||||||
|  |     { | ||||||
|  |     case ESP_OK: | ||||||
|  |         ESP_LOGW(TAG, "Successfully read value '%s' from nvs. Overriding default value %d with %d", key, config.msFadeDecel, valueNew); | ||||||
|  |         msFadeDecel = valueNew; | ||||||
|  |         break; | ||||||
|  |     case ESP_ERR_NVS_NOT_FOUND: | ||||||
|  |         ESP_LOGW(TAG, "nvs: the value '%s' is not initialized yet, keeping default value %d", key, msFadeDecel); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ESP_LOGE(TAG, "Error (%s) reading nvs!", esp_err_to_name(err)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //------------------------------
 | ||||||
|  | //----- writeAccelDuration -----
 | ||||||
|  | //------------------------------
 | ||||||
|  | // write provided value to nvs to be persistent and update the local variable msFadeAccel
 | ||||||
|  | void controlledMotor::writeAccelDuration(uint32_t newValue) | ||||||
|  | { | ||||||
|  |     // check if unchanged
 | ||||||
|  |     if(msFadeAccel == newValue){ | ||||||
|  |         ESP_LOGW(TAG, "value unchanged at %d, not writing to nvs", newValue); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     // generate nvs storage key
 | ||||||
|  |     char key[15]; | ||||||
|  |     snprintf(key, 15, "m-%s-accel", config.name); | ||||||
|  |     // update nvs value
 | ||||||
|  |     ESP_LOGW(TAG, "[%s] updating nvs value '%s' from %d to %d", config.name, key, msFadeAccel, newValue); | ||||||
|  |     esp_err_t err = nvs_set_u32(*nvsHandle, key, newValue); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed writing"); | ||||||
|  |     err = nvs_commit(*nvsHandle); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed committing updates"); | ||||||
|  |     else | ||||||
|  |         ESP_LOGI(TAG, "nvs: successfully committed updates"); | ||||||
|  |     // update variable
 | ||||||
|  |     msFadeAccel = newValue; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //------------------------------
 | ||||||
|  | //----- writeDecelDuration -----
 | ||||||
|  | //------------------------------
 | ||||||
|  | // write provided value to nvs to be persistent and update the local variable msFadeDecel
 | ||||||
|  | // TODO: reduce duplicate code
 | ||||||
|  | void controlledMotor::writeDecelDuration(uint32_t newValue) | ||||||
|  | { | ||||||
|  |     // check if unchanged
 | ||||||
|  |     if(msFadeDecel == newValue){ | ||||||
|  |         ESP_LOGW(TAG, "value unchanged at %d, not writing to nvs", newValue); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     // generate nvs storage key
 | ||||||
|  |     char key[15]; | ||||||
|  |     snprintf(key, 15, "m-%s-decel", config.name); | ||||||
|  |     // update nvs value
 | ||||||
|  |     ESP_LOGW(TAG, "[%s] updating nvs value '%s' from %d to %d", config.name, key, msFadeDecel, newValue); | ||||||
|  |     esp_err_t err = nvs_set_u32(*nvsHandle, key, newValue); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed writing"); | ||||||
|  |     err = nvs_commit(*nvsHandle); | ||||||
|  |     if (err != ESP_OK) | ||||||
|  |         ESP_LOGE(TAG, "nvs: failed committing updates"); | ||||||
|  |     else | ||||||
|  |         ESP_LOGI(TAG, "nvs: successfully committed updates"); | ||||||
|  |     // update variable
 | ||||||
|  |     msFadeDecel = newValue; | ||||||
|  | } | ||||||
| @ -7,6 +7,8 @@ extern "C" | |||||||
| #include "freertos/queue.h" | #include "freertos/queue.h" | ||||||
| #include "esp_log.h" | #include "esp_log.h" | ||||||
| #include "esp_timer.h" | #include "esp_timer.h" | ||||||
|  | #include "nvs_flash.h" | ||||||
|  | #include "nvs.h" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #include "motordrivers.hpp" | #include "motordrivers.hpp" | ||||||
| @ -28,12 +30,13 @@ typedef void (*motorSetCommandFunc_t)(motorCommand_t cmd); | |||||||
| class controlledMotor { | class controlledMotor { | ||||||
|     public: |     public: | ||||||
|         //--- functions ---
 |         //--- functions ---
 | ||||||
|         controlledMotor(motorSetCommandFunc_t setCommandFunc,  motorctl_config_t config_control); //constructor with structs for configuring motordriver and parameters for control TODO: add configuration for currentsensor
 |         controlledMotor(motorSetCommandFunc_t setCommandFunc,  motorctl_config_t config_control, nvs_handle_t * nvsHandle); //constructor with structs for configuring motordriver and parameters for control TODO: add configuration for currentsensor
 | ||||||
|         void handle(); //controls motor duty with fade and current limiting feature (has to be run frequently by another task)
 |         void handle(); //controls motor duty with fade and current limiting feature (has to be run frequently by another task)
 | ||||||
|         void setTarget(motorstate_t state_f, float duty_f = 0); //adds target command to queue for handle function
 |         void setTarget(motorstate_t state_f, float duty_f = 0); //adds target command to queue for handle function
 | ||||||
|         motorCommand_t getStatus(); //get current status of the motor (returns struct with state and duty)
 |         motorCommand_t getStatus(); //get current status of the motor (returns struct with state and duty)
 | ||||||
| 
 | 
 | ||||||
|         uint32_t getFade(fadeType_t fadeType); //get currently set acceleration or deceleration fading time
 |         uint32_t getFade(fadeType_t fadeType); //get currently set acceleration or deceleration fading time
 | ||||||
|  |         uint32_t getFadeDefault(fadeType_t fadeType); //get acceleration or deceleration fading time from config
 | ||||||
|         void setFade(fadeType_t fadeType, bool enabled); //enable/disable acceleration or deceleration fading
 |         void setFade(fadeType_t fadeType, bool enabled); //enable/disable acceleration or deceleration fading
 | ||||||
|         void setFade(fadeType_t fadeType, uint32_t msFadeNew); //set acceleration or deceleration fade time
 |         void setFade(fadeType_t fadeType, uint32_t msFadeNew); //set acceleration or deceleration fade time
 | ||||||
|         bool toggleFade(fadeType_t fadeType); //toggle acceleration or deceleration on/off
 |         bool toggleFade(fadeType_t fadeType); //toggle acceleration or deceleration on/off
 | ||||||
| @ -44,7 +47,11 @@ class controlledMotor { | |||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|         //--- functions ---
 |         //--- functions ---
 | ||||||
|         void init(); //creates currentsensor objects, motordriver objects and queue
 |         void init(); // creates command-queue and initializes config values
 | ||||||
|  |         void loadAccelDuration(void); // load stored value for msFadeAccel from nvs
 | ||||||
|  |         void loadDecelDuration(void); | ||||||
|  |         void writeAccelDuration(uint32_t newValue); // write value to nvs and update local variable
 | ||||||
|  |         void writeDecelDuration(uint32_t newValue); | ||||||
| 
 | 
 | ||||||
|         //--- objects ---
 |         //--- objects ---
 | ||||||
|         //queue for sending commands to the separate task running the handle() function very fast
 |         //queue for sending commands to the separate task running the handle() function very fast
 | ||||||
| @ -60,6 +67,8 @@ class controlledMotor { | |||||||
|         //struct for storing control specific parameters
 |         //struct for storing control specific parameters
 | ||||||
|         motorctl_config_t config; |         motorctl_config_t config; | ||||||
|         motorstate_t state = motorstate_t::IDLE; |         motorstate_t state = motorstate_t::IDLE; | ||||||
|  |         //handle for using the nvs flash (persistent config variables)
 | ||||||
|  |         nvs_handle_t * nvsHandle; | ||||||
| 
 | 
 | ||||||
|         float currentMax; |         float currentMax; | ||||||
|         float currentNow; |         float currentNow; | ||||||
|  | |||||||
| @ -41,6 +41,7 @@ typedef struct motorCommands_t { | |||||||
| 
 | 
 | ||||||
| //struct with all config parameters for a motor regarding ramp and current limit
 | //struct with all config parameters for a motor regarding ramp and current limit
 | ||||||
| typedef struct motorctl_config_t { | typedef struct motorctl_config_t { | ||||||
|  |     char * name;    //name for unique nvs storage-key prefix and logging
 | ||||||
|     uint32_t msFadeAccel; //acceleration of the motor (ms it takes from 0% to 100%)
 |     uint32_t msFadeAccel; //acceleration of the motor (ms it takes from 0% to 100%)
 | ||||||
|     uint32_t msFadeDecel; //deceleration of the motor (ms it takes from 100% to 0%)
 |     uint32_t msFadeDecel; //deceleration of the motor (ms it takes from 100% to 0%)
 | ||||||
| 	bool currentLimitEnabled; | 	bool currentLimitEnabled; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user