jonny_jr9 4f8c421168 Integrate menu into control, Rework prev. button menu
Armchair functions as before (all tasks enabled).
Note: probably wrong encoder pin set in encoder.hpp

Old button menu works as usual (opimized code).
You can switch to new MENU state with 1x long press
and exit the menu with 1x long press

button.cpp: - use encoder queue instead of evaluated switch
            - simplify code, rework actions

control.cpp: Add MENU state/mode
            -> control task: turns motors off and idles
            -> button task idles  (button menu disabled)
            -> display task switches state to handle menu
control.cpp: Optimize structure:
            Add methods to freeze stick and toggle stick mapping

display.cpp: show status screen or handle menu depending on mode, simpilfy task

main.cpp: re-enable button task, disable buzzer logging

menu.cpp: Change events, Add menu exit condition
2024-02-14 13:40:46 +01:00

335 lines
11 KiB
C++

#include "display.hpp"
extern "C"{
#include <driver/adc.h>
#include "esp_ota_ops.h"
}
#include "menu.hpp"
//==== display config ====
#define I2C_INTERFACE y
#define SCL_GPIO 22
#define SDA_GPIO 23
#define RESET_GPIO 15 // FIXME remove this
// the following options are set in menuconfig: (see sdkconfig)
// #define CONFIG_OFFSETX 2 //note: the larger display (actual 130x64) needs 2 pixel offset (prevents bugged column)
// #define CONFIG_I2C_PORT_0 y
//=== content config ===
#define STARTUP_MSG_TIMEOUT 2000
#define ADC_BATT_VOLTAGE ADC1_CHANNEL_6
#define BAT_CELL_COUNT 7
//--------------------------
//------- getVoltage -------
//--------------------------
//TODO duplicate code: getVoltage also defined in currentsensor.cpp -> outsource this
//local function to get average voltage from adc
float getVoltage1(adc1_channel_t adc, uint32_t samples){
//measure voltage
int measure = 0;
for (int j=0; j<samples; j++){
measure += adc1_get_raw(adc);
ets_delay_us(50);
}
return (float)measure / samples / 4096 * 3.3;
}
//======================
//===== variables ======
//======================
//display
SSD1306_t dev;
//tag for logging
static const char * TAG = "display";
//======================
//==== display_init ====
//======================
//note CONFIG_OFFSETX is used (from menuconfig)
void display_init(){
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); //max voltage
ESP_LOGW("display", "INTERFACE is i2c");
ESP_LOGW("display", "SDA_GPIO=%d",SDA_GPIO);
ESP_LOGW("display", "SCL_GPIO=%d",SCL_GPIO);
ESP_LOGW("display", "RESET_GPIO=%d",RESET_GPIO);
i2c_master_init(&dev, SDA_GPIO, SCL_GPIO, RESET_GPIO);
#if FLIP
dev._flip = true;
ESP_LOGW("display", "Flip upside down");
#endif
ESP_LOGI("display", "Panel is 128x64");
ssd1306_init(&dev, 128, 64);
ssd1306_clear_screen(&dev, false);
ssd1306_contrast(&dev, 0xff);
}
//----------------------------------
//------- getBatteryVoltage --------
//----------------------------------
float getBatteryVoltage(){
#define BAT_VOLTAGE_CONVERSION_FACTOR 11.9
float voltageRead = getVoltage1(ADC_BATT_VOLTAGE, 1000);
float battVoltage = voltageRead * 11.9; //note: factor comes from simple test with voltmeter
ESP_LOGD(TAG, "batteryVoltage - voltageAdc=%f, voltageConv=%f, factor=%.2f", voltageRead, battVoltage, BAT_VOLTAGE_CONVERSION_FACTOR);
return battVoltage;
}
//----------------------------------
//------- getBatteryPercent --------
//----------------------------------
//TODO find better/more accurate table?
//configure discharge curve of one cell with corresponding known voltage->chargePercent values
const float voltageLevels[] = {3.00, 3.45, 3.68, 3.74, 3.77, 3.79, 3.82, 3.87, 3.92, 3.98, 4.06, 4.20};
const float percentageLevels[] = {0.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0};
float getBatteryPercent(){
float voltage = getBatteryVoltage();
float cellVoltage = voltage/BAT_CELL_COUNT;
int size = sizeof(voltageLevels) / sizeof(voltageLevels[0]);
int sizePer = sizeof(percentageLevels) / sizeof(percentageLevels[0]);
//check if configured correctly
if (size != sizePer) {
ESP_LOGE(TAG, "getBatteryPercent - count of configured percentages do not match count of voltages");
return 0;
}
if (cellVoltage <= voltageLevels[0]) {
return 0.0;
} else if (cellVoltage >= voltageLevels[size - 1]) {
return 100.0;
}
//scale voltage linear to percent in matched range
for (int i = 1; i < size; ++i) {
if (cellVoltage <= voltageLevels[i]) {
float voltageRange = voltageLevels[i] - voltageLevels[i - 1];
float voltageOffset = cellVoltage - voltageLevels[i - 1];
float percentageRange = percentageLevels[i] - percentageLevels[i - 1];
float percentageOffset = percentageLevels[i - 1];
float percent = percentageOffset + (voltageOffset / voltageRange) * percentageRange;
ESP_LOGD(TAG, "getBatPercent - cellVoltage=%.3f => percentage=%.3f", cellVoltage, percent);
ESP_LOGD(TAG, "getBatPercent - matched range: %.2fV-%.2fV => %.1f%%-%.1f%%", voltageLevels[i-1], voltageLevels[i], percentageLevels[i-1], percentageLevels[i]);
return percent;
}
}
ESP_LOGE(TAG, "getBatteryPercent - unknown voltage range");
return 0.0; //unknown range
}
//-----------------------
//----- showScreen1 -----
//-----------------------
//shows overview on entire display:
//percentage, voltage, current, mode, rpm, speed
void showScreen1()
{
char buf[20];
char buf1[20];
int len, len1;
//-- battery percentage --
// TODO update when no load (currentsensors = ~0A) only
len1 = snprintf(buf1, sizeof(buf1), "B:%02.0f%%", getBatteryPercent());
ssd1306_display_text_x3(&dev, 0, buf1, len1, false);
//-- voltage and current --
len = snprintf(buf, sizeof(buf), "%04.1fV %04.1f:%04.1fA",
getBatteryVoltage(),
fabs(motorLeft.getCurrentA()),
fabs(motorRight.getCurrentA()));
ssd1306_display_text(&dev, 3, buf, len, false);
//-- control state --
len = snprintf(buf, sizeof(buf), "%s ", control.getCurrentModeStr());
ssd1306_display_text_x3(&dev, 4, buf, len, false);
//-- speed and RPM --
len = snprintf(buf, sizeof(buf), "%3.1fkm/h %03.0f:%03.0fR",
fabs((speedLeft.getKmph() + speedRight.getKmph()) / 2),
speedLeft.getRpm(),
speedRight.getRpm());
ssd1306_display_text(&dev, 7, buf, len, false);
// debug speed sensors
ESP_LOGD(TAG, "%s", buf);
}
//------------------------
//---- showStartupMsg ----
//------------------------
//shows welcome message and information about current version
void showStartupMsg(){
char buf[20];
int len;
const esp_app_desc_t * desc = esp_ota_get_app_description();
//show message
len = snprintf(buf, 20, "START");
ssd1306_display_text_x3(&dev, 0, buf, len, false);
//show git-tag
len = snprintf(buf, 20, "%s", desc->version);
ssd1306_display_text(&dev, 4, buf, len, false);
//show build-date (note: date,time of last clean build)
len = snprintf(buf, 20, "%s", desc->date);
ssd1306_display_text(&dev, 6, buf, len, false);
//show build-time
len = snprintf(buf, 20, "%s", desc->time);
ssd1306_display_text(&dev, 7, buf, len, false);
}
//============================
//======= display task =======
//============================
#define STATUS_SCREEN_UPDATE_INTERVAL 500
// TODO: separate task for each loop?
void display_task(void *pvParameters)
{
// initialize display
display_init();
// TODO check if successfully initialized
// show startup message
showStartupMsg();
vTaskDelay(STARTUP_MSG_TIMEOUT / portTICK_PERIOD_MS);
// repeatedly update display with content
while (1)
{
if (control.getCurrentMode() == controlMode_t::MENU)
{
//uses encoder events to control menu and updates display
handleMenu(&dev);
}
else //show status screen in any other mode
{
showScreen1();
vTaskDelay(STATUS_SCREEN_UPDATE_INTERVAL / portTICK_PERIOD_MS);
}
// TODO add pages and menus
}
}
//-----------------------------------
//---- text-related example code ----
//-----------------------------------
//ssd1306_display_text(&dev, 0, "SSD1306 128x64", 14, false);
//ssd1306_display_text(&dev, 1, "ABCDEFGHIJKLMNOP", 16, false);
//ssd1306_display_text(&dev, 2, "abcdefghijklmnop",16, false);
//ssd1306_display_text(&dev, 3, "Hello World!!", 13, false);
////ssd1306_clear_line(&dev, 4, true);
////ssd1306_clear_line(&dev, 5, true);
////ssd1306_clear_line(&dev, 6, true);
////ssd1306_clear_line(&dev, 7, true);
//ssd1306_display_text(&dev, 4, "SSD1306 128x64", 14, true);
//ssd1306_display_text(&dev, 5, "ABCDEFGHIJKLMNOP", 16, true);
//ssd1306_display_text(&dev, 6, "abcdefghijklmnop",16, true);
//ssd1306_display_text(&dev, 7, "Hello World!!", 13, true);
//
//// Display Count Down
//uint8_t image[24];
//memset(image, 0, sizeof(image));
//ssd1306_display_image(&dev, top, (6*8-1), image, sizeof(image));
//ssd1306_display_image(&dev, top+1, (6*8-1), image, sizeof(image));
//ssd1306_display_image(&dev, top+2, (6*8-1), image, sizeof(image));
//for(int font=0x39;font>0x30;font--) {
// memset(image, 0, sizeof(image));
// ssd1306_display_image(&dev, top+1, (7*8-1), image, 8);
// memcpy(image, font8x8_basic_tr[font], 8);
// if (dev._flip) ssd1306_flip(image, 8);
// ssd1306_display_image(&dev, top+1, (7*8-1), image, 8);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
//}
//
//// Scroll Up
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, 0, "---Scroll UP---", 16, true);
////ssd1306_software_scroll(&dev, 7, 1);
//ssd1306_software_scroll(&dev, (dev._pages - 1), 1);
//for (int line=0;line<bottom+10;line++) {
// lineChar[0] = 0x01;
// sprintf(&lineChar[1], " Line %02d", line);
// ssd1306_scroll_text(&dev, lineChar, strlen(lineChar), false);
// vTaskDelay(500 / portTICK_PERIOD_MS);
//}
//vTaskDelay(3000 / portTICK_PERIOD_MS);
//
//// Scroll Down
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, 0, "--Scroll DOWN--", 16, true);
////ssd1306_software_scroll(&dev, 1, 7);
//ssd1306_software_scroll(&dev, 1, (dev._pages - 1) );
//for (int line=0;line<bottom+10;line++) {
// lineChar[0] = 0x02;
// sprintf(&lineChar[1], " Line %02d", line);
// ssd1306_scroll_text(&dev, lineChar, strlen(lineChar), false);
// vTaskDelay(500 / portTICK_PERIOD_MS);
//}
//vTaskDelay(3000 / portTICK_PERIOD_MS);
//// Page Down
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, 0, "---Page DOWN---", 16, true);
//ssd1306_software_scroll(&dev, 1, (dev._pages-1) );
//for (int line=0;line<bottom+10;line++) {
// //if ( (line % 7) == 0) ssd1306_scroll_clear(&dev);
// if ( (line % (dev._pages-1)) == 0) ssd1306_scroll_clear(&dev);
// lineChar[0] = 0x02;
// sprintf(&lineChar[1], " Line %02d", line);
// ssd1306_scroll_text(&dev, lineChar, strlen(lineChar), false);
// vTaskDelay(500 / portTICK_PERIOD_MS);
//}
//vTaskDelay(3000 / portTICK_PERIOD_MS);
//// Horizontal Scroll
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, center, "Horizontal", 10, false);
//ssd1306_hardware_scroll(&dev, SCROLL_RIGHT);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_LEFT);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_STOP);
//
//// Vertical Scroll
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, center, "Vertical", 8, false);
//ssd1306_hardware_scroll(&dev, SCROLL_DOWN);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_UP);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_STOP);
//
//// Invert
//ssd1306_clear_screen(&dev, true);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, center, " Good Bye!!", 12, true);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//// Fade Out
//ssd1306_fadeout(&dev);