Add Brightness reduction at inactive, Optimize timeout

display.cpp:
    - add timeout where display brightness gets reduced
    - rework display-task loop to handle timeouts
    - run commands when changing from/to status-page (toggle scrolling)
    - add BRIGHTNESS_TEST option

config: Add display timeouts to config.cpp

control:
    - dont reset timeout at mode change (happens not always at user input)
    - remove deprecated config option
This commit is contained in:
jonny 2024-03-02 13:01:29 +01:00
parent 0d082a52d8
commit 3514dd6bf2
5 changed files with 131 additions and 45 deletions

View File

@ -201,16 +201,21 @@ speedSensor_config_t speedRight_config{
//------------------------- //-------------------------
//-------- display -------- //-------- display --------
//------------------------- //-------------------------
display_config_t display_config { display_config_t display_config{
// hardware initialization
.gpio_scl = GPIO_NUM_22, .gpio_scl = GPIO_NUM_22,
.gpio_sda = GPIO_NUM_23, .gpio_sda = GPIO_NUM_23,
.gpio_reset = -1, //negative number disables reset feature .gpio_reset = -1, // negative number disables reset feature
.width = 128, .width = 128,
.height = 64, .height = 64,
.offsetX = 2, .offsetX = 2,
.flip = false, .flip = false,
.contrast = 0xff, //max: 255 .contrastNormal = 170, // max: 255
}; // display task
.contrastReduced = 30, // max: 255
.timeoutReduceContrastMs = 5 * 60 * 1000, // actions at certain inactivity
.timeoutSwitchToScreensaverMs = 30 * 60 * 1000
};

View File

@ -337,7 +337,7 @@ void controlledArmchair::resetTimeout(){
// switch to IDLE when no activity (prevent accidential movement) // switch to IDLE when no activity (prevent accidential movement)
// notify "power still on" when in IDLE for a very long time (prevent battery drain when forgotten to turn off) // notify "power still on" when in IDLE for a very long time (prevent battery drain when forgotten to turn off)
// this function has to be run repeatedly (can be slow interval) // this function has to be run repeatedly (can be slow interval)
#define TIMEOUT_POWER_STILL_ON_BEEP_INTERVAL_MS 10 * 60 * 1000 // beep every 30 minutes for someone to notice #define TIMEOUT_POWER_STILL_ON_BEEP_INTERVAL_MS 5 * 60 * 1000 // beep every 5 minutes for someone to notice
// note: timeout durations are configured in config.cpp // note: timeout durations are configured in config.cpp
void controlledArmchair::handleTimeout() void controlledArmchair::handleTimeout()
{ {
@ -378,8 +378,6 @@ void controlledArmchair::handleTimeout()
//----------------------------------- //-----------------------------------
//function to change to a specified control mode //function to change to a specified control mode
void controlledArmchair::changeMode(controlMode_t modeNew) { void controlledArmchair::changeMode(controlMode_t modeNew) {
//reset timeout timer
resetTimeout();
//exit if target mode is already active //exit if target mode is already active
if (mode == modeNew) { if (mode == modeNew) {

View File

@ -29,7 +29,6 @@ typedef struct control_config_t {
//timeout options //timeout options
uint32_t timeoutSwitchToIdleMs; //time of inactivity after which the mode gets switched to IDLE uint32_t timeoutSwitchToIdleMs; //time of inactivity after which the mode gets switched to IDLE
uint32_t timeoutNotifyPowerStillOnMs; uint32_t timeoutNotifyPowerStillOnMs;
float timeoutTolerancePer; //percentage the duty can vary between timeout checks considered still inactive
} control_config_t; } control_config_t;

View File

@ -12,8 +12,14 @@ extern "C"{
#define STARTUP_MSG_TIMEOUT 2000 #define STARTUP_MSG_TIMEOUT 2000
#define ADC_BATT_VOLTAGE ADC1_CHANNEL_6 #define ADC_BATT_VOLTAGE ADC1_CHANNEL_6
#define BAT_CELL_COUNT 7 #define BAT_CELL_COUNT 7
// continously vary display contrast from 0 to 250 in OVERVIEW status screen
//#define BRIGHTNESS_TEST
//=== variables ===
// every function can access the display configuration from config.cpp
static display_config_t displayConfig;
//-------------------------- //--------------------------
//------- getVoltage ------- //------- getVoltage -------
@ -61,7 +67,10 @@ void display_init(display_config_t config){
ssd1306_init(&dev, config.width, config.height, config.offsetX); ssd1306_init(&dev, config.width, config.height, config.offsetX);
ssd1306_clear_screen(&dev, false); ssd1306_clear_screen(&dev, false);
ssd1306_contrast(&dev, config.contrast); ssd1306_contrast(&dev, config.contrastNormal);
//store configuration locally (e.g. for accessing timeouts)
displayConfig = config;
} }
@ -233,7 +242,7 @@ float getBatteryPercent()
//############################# //#############################
//shows overview on entire display: //shows overview on entire display:
//Battery percentage, voltage, current, mode, rpm, speed //Battery percentage, voltage, current, mode, rpm, speed
#define STATUS_SCREEN_OVERVIEW_UPDATE_INTERVAL 500 #define STATUS_SCREEN_OVERVIEW_UPDATE_INTERVAL 400
void showStatusScreenOverview(display_task_parameters_t *objects) void showStatusScreenOverview(display_task_parameters_t *objects)
{ {
//-- battery percentage -- //-- battery percentage --
@ -263,8 +272,20 @@ void showStatusScreenOverview(display_task_parameters_t *objects)
objects->speedLeft->getRpm(), objects->speedLeft->getRpm(),
objects->speedRight->getRpm()); objects->speedRight->getRpm());
vTaskDelay(STATUS_SCREEN_OVERVIEW_UPDATE_INTERVAL / portTICK_PERIOD_MS); vTaskDelay(STATUS_SCREEN_OVERVIEW_UPDATE_INTERVAL / portTICK_PERIOD_MS);
//-- brightness test --
#ifdef BRIGHTNESS_TEST
// continously vary brightness/contrast for testing
displayConfig.contrastNormal += 10;
if (displayConfig.contrastNormal > 255)
displayConfig.contrastNormal = 0;
ssd1306_contrast(&dev, displayConfig.contrastNormal);
vTaskDelay(100 / portTICK_PERIOD_MS);
ESP_LOGW(TAG, "TEST BRIGHTNESS, setting to %d", displayConfig.contrastNormal);
#endif
} }
//############################ //############################
//##### showScreen Speed ##### //##### showScreen Speed #####
//############################ //############################
@ -314,8 +335,6 @@ void showStatusScreenJoystick(display_task_parameters_t * objects)
#define STATUS_SCREEN_MOTORS_UPDATE_INTERVAL 150 #define STATUS_SCREEN_MOTORS_UPDATE_INTERVAL 150
void showStatusScreenMotors(display_task_parameters_t *objects) void showStatusScreenMotors(display_task_parameters_t *objects)
{ {
// print all joystick data
joystickData_t data = objects->joystick->getData();
displayTextLine(&dev, 0, true, false, "%-4.0fW ", fabs(objects->motorLeft->getCurrentA()) * getBatteryVoltage()); displayTextLine(&dev, 0, true, false, "%-4.0fW ", fabs(objects->motorLeft->getCurrentA()) * getBatteryVoltage());
displayTextLine(&dev, 3, true, false, "%-4.0fW ", fabs(objects->motorRight->getCurrentA()) * getBatteryVoltage()); displayTextLine(&dev, 3, true, false, "%-4.0fW ", fabs(objects->motorRight->getCurrentA()) * getBatteryVoltage());
//displayTextLine(&dev, 0, true, false, "L:%02.0f%%", objects->motorLeft->getStatus().duty); //displayTextLine(&dev, 0, true, false, "L:%02.0f%%", objects->motorLeft->getStatus().duty);
@ -333,37 +352,40 @@ void showStatusScreenMotors(display_task_parameters_t *objects)
//############################### //###############################
//#### showScreen Sreensaver #### //#### showScreen Sreensaver ####
//############################### //###############################
// show minimal text scrolling across screen to prevent burn in // show inactivity duration and battery perventage scrolling across screen the entire screen to prevent burn in
// indicates that armchair is still on (probably "forgotten to be turned off") #define STATUS_SCREEN_SCREENSAVER_DELAY_NEXT_LINE_MS 10*1000
#define STATUS_SCREEN_TIMEOUT_NEXT_LINE_SEC 6 #define STATUS_SCREEN_SCREENSAVER_UPDATE_INTERVAL 500
void showStatusScreenScreensaver(display_task_parameters_t *objects) void showStatusScreenScreensaver(display_task_parameters_t *objects)
{ {
// to prevent burn-in only showing minimal and scrolling text // note: scrolling is enabled at screen change (display_selectStatusPage())
static int msPassed = 0;
static int currentLine = 0;
static bool lineChanging = false;
// clear display once when rotating to next line
if (lineChanging) {
ssd1306_clear_screen(&dev, false); ssd1306_clear_screen(&dev, false);
ssd1306_hardware_scroll(&dev, SCROLL_RIGHT); lineChanging = false;
}
// update text every iteration to prevent empty screen at start
displayTextLine(&dev, currentLine, false, false, "IDLE since:");
displayTextLine(&dev, currentLine + 1, false, false, "%.1fh, B:%02.0f%%",
(float)objects->control->getInactivityDurationMs() / 1000 / 60 / 60,
getBatteryPercent());
// loop through all lines (also scroll down) // to not block the display task for several seconds returning every e.g. 500ms here
for (int line = 0; line < 7; line++) // -> ensures detection of activity (exit condition) in task loop is handled regularly
{ if (msPassed > STATUS_SCREEN_SCREENSAVER_DELAY_NEXT_LINE_MS) // switch to next line is due
ssd1306_clear_screen(&dev, false); {
displayTextLine(&dev, line, false, false, "IDLE since"); msPassed = 0; // rest seconds count
displayTextLine(&dev, line + 1, false, false, "%.1fh, B:%02.0f%%", (float)objects->control->getInactivityDurationMs() / 1000 / 60 / 60, getBatteryPercent()); // increment / rotate to next line
// check exit condition while waiting some time before switching to next line if (++currentLine >= 7) // rotate to next line
int secondsPassed = 0; currentLine = 0;
while (secondsPassed < STATUS_SCREEN_TIMEOUT_NEXT_LINE_SEC) lineChanging = true; //clear screen in next run
{ }
secondsPassed ++; // wait update-update interval and increment passed time after each run
vTaskDelay(1000 / portTICK_PERIOD_MS); vTaskDelay(STATUS_SCREEN_SCREENSAVER_UPDATE_INTERVAL / portTICK_PERIOD_MS);
// switch to default status screen, when IDLE mode is exited (timeout has ended) msPassed += STATUS_SCREEN_SCREENSAVER_UPDATE_INTERVAL;
if (objects->control->getCurrentMode() != controlMode_t::IDLE) // note: scrolling is disabled at screen change (display_selectStatusPage())
{
display_selectStatusPage(STATUS_SCREEN_OVERVIEW);
ssd1306_hardware_scroll(&dev, SCROLL_STOP);
return;
}
}
}
ssd1306_hardware_scroll(&dev, SCROLL_STOP);
} }
//######################## //########################
@ -388,17 +410,39 @@ void showStartupMsg(){
//============================ //============================
//===== selectStatusPage ===== //===== selectStatusPage =====
//============================ //============================
void display_selectStatusPage(displayStatusPage_t newStatusPage){ void display_selectStatusPage(displayStatusPage_t newStatusPage)
{
//-- run commands when switching FROM certain mode --
switch (selectedStatusPage)
{
case STATUS_SCREEN_SCREENSAVER: // disable scrolling when exiting screensaver
ssd1306_hardware_scroll(&dev, SCROLL_STOP);
break;
default:
break;
}
ESP_LOGW(TAG, "switching statusPage from %d to %d", (int)selectedStatusPage, (int)newStatusPage); ESP_LOGW(TAG, "switching statusPage from %d to %d", (int)selectedStatusPage, (int)newStatusPage);
selectedStatusPage = newStatusPage; selectedStatusPage = newStatusPage;
//-- run commands when switching TO certain mode --
switch (selectedStatusPage)
{
case STATUS_SCREEN_SCREENSAVER:
ssd1306_clear_screen(&dev, false);
ssd1306_hardware_scroll(&dev, SCROLL_RIGHT);
break;
default:
break;
}
} }
//============================ //============================
//======= display task ======= //======= display task =======
//============================ //============================
// TODO: separate task for each loop? // TODO: separate task for each loop?
#define DISPLAY_IDLE_TIMEOUT_SCREENSAVER_MS 15*60*1000
void display_task(void *pvParameters) void display_task(void *pvParameters)
{ {
@ -444,9 +488,44 @@ void display_task(void *pvParameters)
showStatusScreenScreensaver(objects); showStatusScreenScreensaver(objects);
break; break;
} }
// switch to screensaver when no user activity for a long time, to prevent burn in
if (objects->control->getInactivityDurationMs() > DISPLAY_IDLE_TIMEOUT_SCREENSAVER_MS) //--- handle timeouts ---
selectedStatusPage = STATUS_SCREEN_SCREENSAVER; uint32_t inactiveMs = objects->control->getInactivityDurationMs();
//-- screensaver --
// handle switch to screensaver when no user input for a long time
if (inactiveMs > displayConfig.timeoutSwitchToScreensaverMs) // timeout - switch to screensaver is due
{
if (selectedStatusPage != STATUS_SCREEN_SCREENSAVER){ // switch/log only once at change
ESP_LOGW(TAG, "no activity for more than %d min, switching to screensaver", inactiveMs / 1000 / 60);
display_selectStatusPage(STATUS_SCREEN_SCREENSAVER);
}
}
else if (selectedStatusPage == STATUS_SCREEN_SCREENSAVER) // exit screensaver when there was recent activity
{
ESP_LOGW(TAG, "recent activity detected, disabling screensaver");
display_selectStatusPage(STATUS_SCREEN_OVERVIEW);
}
//-- reduce brightness --
// handle brightness reduction when no user input for some time
static bool brightnessIsReduced = false;
if (inactiveMs > displayConfig.timeoutReduceContrastMs) // threshold exceeded - reduction of brightness is due
{
if (!brightnessIsReduced) //change / log only once at change
{
// reduce display brightness (less burn in)
ESP_LOGW(TAG, "no activity for more than %d min, reducing display brightness to %d/255", inactiveMs / 1000 / 60, displayConfig.contrastReduced);
ssd1306_contrast(&dev, displayConfig.contrastReduced);
brightnessIsReduced = true;
}
}
else if (brightnessIsReduced) // threshold not exceeded anymore, but still reduced
{
// increase display brighness again
ESP_LOGW(TAG, "recent activity detected, increasing brightness again");
ssd1306_contrast(&dev, displayConfig.contrastNormal);
brightnessIsReduced = false;
}
} }
// TODO add pages and menus // TODO add pages and menus
} }

View File

@ -22,6 +22,7 @@ extern "C" {
// configuration for initializing display (passed to task as well) // configuration for initializing display (passed to task as well)
typedef struct display_config_t { typedef struct display_config_t {
// initialization
gpio_num_t gpio_scl; gpio_num_t gpio_scl;
gpio_num_t gpio_sda; gpio_num_t gpio_sda;
int gpio_reset; // negative number means reset pin is not connected or not used int gpio_reset; // negative number means reset pin is not connected or not used
@ -29,7 +30,11 @@ typedef struct display_config_t {
int height; int height;
int offsetX; int offsetX;
bool flip; bool flip;
int contrast; // display-task
int contrastNormal;
int contrastReduced;
uint32_t timeoutReduceContrastMs;
uint32_t timeoutSwitchToScreensaverMs;
} display_config_t; } display_config_t;