DreherTankController/src/display/display.cpp

758 lines
25 KiB
C++

#include <Arduino.h>
#include "../DallasTemperature/DallasTemperature.h"
#include "display.h"
Display::Display(
uint8_t cs,
uint8_t btnNext,
uint8_t btnPrev,
uint8_t btnSelect,
uint8_t btnCancel,
uint8_t bgLED,
parameters *params,
values *vals,
modbusParameters *modbus,
PSensor *sensor,
valveStates *vStates)
: U8G2_ST7920_128X64_F_HW_SPI(U8G2_R0, cs)
, btnNext(btnNext)
, btnPrev(btnPrev)
, btnSelect(btnSelect)
, btnCancel(btnCancel)
, bgLed(bgLED)
, _params(params)
, _vals(vals)
, _modbusParams(modbus)
, _pSensor(sensor)
, _vStates(vStates)
{
// Bei 2 MHz funtionierte im Test das Display schon nicht mehr
// setBusClock(1000000);
}
void Display::process()
{
switch (_event()) {
case _BACK_TO_HOME_SCREEN:
_selection = 0;
_home();
return;
case U8X8_MSG_GPIO_MENU_NEXT:
_nextEvent();
_select();
break;
case U8X8_MSG_GPIO_MENU_PREV:
_prevEvent();
_select();
break;
case U8X8_MSG_GPIO_MENU_SELECT:
if (_selection) {
parameters params;
modbusParameters modbus;
switch (_selection) {
case _MENU_MAIN:
_saveCursorPosForLevel(MenuLevel_1);
switch (_prevCursor) {
case _POS_TEMPERATURE_MENU:
_select(_MENU_TEMPERATURE);
break;
case _POS_PRESSURE_MENU:
_select(_MENU_PRESSURE);
break;
case _POS_SYSTEM:
_restrictInputValue(0, 255);
_select(_VERIFY_ADMIN);
break;
case _POS_RESET:
_select(_SELECTION_RESET);
break;
}
break;
case _MENU_TEMPERATURE:
_saveCursorPosForLevel(MenuLevel_2);
switch (_prevCursor) {
case _POS_TEMPERATURE_ENABLED:
_cursor = _params->tEn;
_select(_SELECTION_TEMPERATURE_ENABLED);
break;
case _POS_TEMPERATURE_SETPOINT:
_val = _params->ts1;
_restrictInputValue(_MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT, Precision_2);
_select(_SELECTION_TEMPERATURE_SETPOINT);
break;
case _POS_TEMPERATURE_HYSTERESIS:
_val = _params->th1;
_restrictInputValue(_MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS, Precision_2);
_select(_SELECTION_TEMPERATURE_HYSTERESIS);
break;
}
break;
case _MENU_PRESSURE:
_saveCursorPosForLevel(MenuLevel_2);
switch (_prevCursor) {
case _POS_PRESSURE_ENABLE_INCREASE:
_cursor = _params->pInc;
_select(_SELECTION_PRESSURE_ENABLE_INCREASE);
break;
case _POS_PRESSURE_ENABLE_DECREASE:
_cursor = _params->pDec;
_select(_SELECTION_PRESSURE_ENABLE_DECREASE);
break;
case _POS_PRESSURE_SETPOINT:
_val = _params->ps;
_restrictInputValue(_MIN_P_SETPOINT, _MAX_P_SETPOINT, Precision_2);
_select(_SELECTION_PRESSURE_SETPOINT);
break;
case _POS_PRESSURE_HYSTERESIS:
_val = _params->ph;
_restrictInputValue(_MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS, Precision_2);
_select(_SELECTION_PRESSURE_HYSTERESIS);
break;
}
break;
case _MENU_SYSTEM:
_saveCursorPosForLevel(MenuLevel_2);
switch (_prevCursor) {
case _POS_MODBUS_MENU:
_select(_MENU_SETTINGS_MODBUS);
break;
case _POS_P_SENSOR_SELECTION:
_cursor = (*_pSensor) - 1;
_select(_SELECTION_P_SENSOR);
break;
}
break;
case _MENU_SETTINGS_MODBUS:
_saveCursorPosForLevel(MenuLevel_3);
switch (_prevCursor) {
case _POS_MODBUS_ADDRESS:
_val = _modbusParams->address;
_restrictInputValue(_MODBUS_ADDR_MIN, _MODBUS_ADDR_MAX);
_select(_SELECTION_MODBUS_ADDRESS);
break;
case _POS_MODBUS_BAUDRATE:
_baudrateCursorPos();
_select(_SELECTION_MODBUS_BAUDRATE);
break;
case _POS_MODBUS_DELAY:
_val = _modbusParams->delay;
_restrictInputValue(_MODBUS_DELAY_MIN, _MODBUS_DELAY_MAX, Precision_1);
_select(_SELECTION_MODBUS_DELAY);
break;
}
break;
case _SELECTION_TEMPERATURE_ENABLED:
params.tEn = _cursor;
setParams(params);
_select(_MENU_TEMPERATURE, true);
break;
case _SELECTION_TEMPERATURE_SETPOINT:
// Serial.print("\n_val : "); Serial.println((float) _val);
// Serial.print("_val / 100: "); Serial.println(((float) _val) / 100);
params.ts1 = _val;
setParams(params);
_select(_MENU_TEMPERATURE, true);
break;
case _SELECTION_TEMPERATURE_HYSTERESIS:
params.th1 = _val;
setParams(params);
_select(_MENU_TEMPERATURE, true);
break;
case _SELECTION_PRESSURE_ENABLE_INCREASE:
params.pInc = _cursor;
setParams(params);
_select(_MENU_PRESSURE, true);
break;
case _SELECTION_PRESSURE_ENABLE_DECREASE:
params.pDec = _cursor;
setParams(params);
_select(_MENU_PRESSURE, true);
break;
case _SELECTION_PRESSURE_SETPOINT:
params.ps = _val;
setParams(params);
_select(_MENU_PRESSURE, true);
break;
case _SELECTION_PRESSURE_HYSTERESIS:
params.ph = _val;
setParams(params);
_select(_MENU_PRESSURE, true);
break;
case _SELECTION_MODBUS_BAUDRATE:
_setModbusBaudrate();
_select(_MENU_SETTINGS_MODBUS, true);
break;
case _SELECTION_MODBUS_ADDRESS:
modbus.address = _val;
setModbusParams(modbus);
_select(_MENU_SETTINGS_MODBUS, true);
break;
case _SELECTION_MODBUS_DELAY:
modbus.delay = _val;
setModbusParams(modbus);
_select(_MENU_SETTINGS_MODBUS, true);
break;
case _SELECTION_P_SENSOR:
switch (_cursor) {
case _POS_P_SMC_1_5V_0_5BAR:
setPSensor(SMC_1_5V_0_5BAR);
break;
case _POS_P_GEMS_0_5V_0_6BAR:
setPSensor(GEMS_0_5V_0_6BAR);
break;
}
_restoreCursor();
_select(_MENU_SYSTEM);
break;
case _VERIFY_ADMIN:
if (_val == _ADMIN_CODE) {
_select(_MENU_SYSTEM);
} else {
_restoreCursor();
_select(_MENU_MAIN);
}
_val = 0;
break;
case _SELECTION_RESET:
if (_cursor == 1) {
reset();
} else {
_restoreCursor();
_select(_MENU_MAIN);
}
break;
}
}
break;
case U8X8_MSG_GPIO_MENU_HOME:
if (_selection) {
_val = 0;
if (!_restoreCursor()) {
_home();
return;
}
switch (_selection) {
case _MENU_MAIN:
_selection = 0;
_home();
return;
case _SELECTION_TEMPERATURE_ENABLED:
case _SELECTION_TEMPERATURE_SETPOINT:
case _SELECTION_TEMPERATURE_HYSTERESIS:
_select(_MENU_TEMPERATURE);
break;
case _SELECTION_PRESSURE_ENABLE_INCREASE:
case _SELECTION_PRESSURE_ENABLE_DECREASE:
case _SELECTION_PRESSURE_SETPOINT:
case _SELECTION_PRESSURE_HYSTERESIS:
_select(_MENU_PRESSURE);
break;
case _MENU_SETTINGS_MODBUS:
case _SELECTION_P_SENSOR:
_select(_MENU_SYSTEM);
break;
case _SELECTION_MODBUS_ADDRESS:
case _SELECTION_MODBUS_BAUDRATE:
case _SELECTION_MODBUS_DELAY:
_select(_MENU_SETTINGS_MODBUS);
break;
default:
_select(_MENU_MAIN);
}
}
break;
}
if (millis() - _prevT > 250) {
_prevT = millis();
if (!_selection) {
_home();
}
}
}
void Display::_select(const uint8_t selection, const bool restoreCursor=false)
{
if (restoreCursor)
_restoreCursor();
_selection = selection;
_select();
}
void Display::_select()
{
setFont(_SEL_FONT);
setFontRefHeightAll();
_inputValueActive = false;
_stepSize = 1;
_printtime("select screen");
switch (_selection) {
case _MENU_MAIN:
_menu_depth = 0;
sprintf(_text, "Hauptmenü (%s)", _VERSION_STRING);
_selCnt = my_UserInterfaceSelectionList(&u8g2, _text, _cursor, "Temperatur\nDruck\nSystem\nReset");
break;
case _MENU_TEMPERATURE:
_menu_depth = 1;
_selCnt = my_UserInterfaceSelectionList(&u8g2, "Temperatur", _cursor, "Kühlung de/aktivieren\nSollwert\nHysterese");
break;
case _SELECTION_TEMPERATURE_ENABLED:
_menu_depth = 2;
_selCnt = my_UserInterfaceMessage(&u8g2, "Kühlung", nullptr, "aktivieren", " nein \n ja ", _cursor);
break;
case _SELECTION_TEMPERATURE_SETPOINT:
_menu_depth = 2;
_inputValue(5, "Temperatur\nSollwert", "__float__");
break;
case _SELECTION_TEMPERATURE_HYSTERESIS:
_menu_depth = 2;
_inputValue(5, "Temperatur\nHysterese", "__float__");
break;
case _MENU_PRESSURE:
_menu_depth = 1;
_selCnt = my_UserInterfaceSelectionList(&u8g2, "Druck", _cursor, "Drucksteigerung\nDruckabfall\nSollwert\nHysterese");
break;
case _SELECTION_PRESSURE_ENABLE_INCREASE:
_menu_depth = 2;
_selCnt = my_UserInterfaceMessage(&u8g2, "Drucksteigerung", nullptr, "aktivieren", " nein \n ja ", _cursor);
break;
case _SELECTION_PRESSURE_ENABLE_DECREASE:
_menu_depth = 2;
_selCnt = my_UserInterfaceMessage(&u8g2, "Druckabfall", nullptr, "aktivieren", " nein \n ja ", _cursor);
break;
case _SELECTION_PRESSURE_SETPOINT:
_menu_depth = 2;
_inputValue(5, "Druck\nSollwert", "__float__");
break;
case _SELECTION_PRESSURE_HYSTERESIS:
_menu_depth = 2;
_inputValue(5, "Druck\nHysterese", "__float__");
break;
case _VERIFY_ADMIN:
_menu_depth = 1;
_inputValueActive = true;
sprintf(_text, "%d", _val);
my_UserInterfaceInputValueString(&u8g2, "Systemeinstellungen\nBerechtigung\nnachweisen:", nullptr, _text);
break;
case _MENU_SYSTEM:
_menu_depth = 1;
_selCnt = my_UserInterfaceSelectionList(&u8g2, "System", _cursor, "Modbus\nDrucksensor");
break;
case _MENU_SETTINGS_MODBUS:
_menu_depth = 2;
_selCnt = my_UserInterfaceSelectionList(&u8g2, "Modbus", _cursor, "Adresse\nBaudrate\nAntwort-Delay");
break;
case _SELECTION_MODBUS_BAUDRATE:
_menu_depth = 3;
_selCnt = my_UserInterfaceSelectionList(&u8g2, "Baudrate", _cursor, "300\n1200\n2400\n4800\n9600\n19200\n38400\n57600\n115200");
break;
case _SELECTION_MODBUS_ADDRESS:
_menu_depth = 3;
sprintf(_text, "Dezimal: %d", _val);
_inputValue(1, "Modbus Adresse");
break;
case _SELECTION_MODBUS_DELAY:
_menu_depth = 3;
sprintf(_text, "%d.%.d", _val / 10, _val % 10);
_inputValue(1, "Modbus\nAntwort-Verzögerung\nin Millisekunden");
break;
case _SELECTION_P_SENSOR:
_menu_depth = 2;
_selCnt = my_UserInterfaceSelectionList(&u8g2, "Drucksensor", _cursor, "SMC: 1-5V,0-5 bar\nGems: 0-5V,0-6 bar");
break;
case _SELECTION_RESET:
_menu_depth = 1;
_selCnt = my_UserInterfaceMessage(&u8g2, "Controller neustarten", "--------------------", "Wirklich neustarten?", " nein \n ja ", _cursor);
break;
default:
_menu_depth = 0;
}
}
void Display::_home()
{
// Das Display hat eine Auflösung von 128 x 64 Pixel
// Mit der Schriftart u8g2_font_ncenB10_te stellt \xb0 das °-Zeichen dar
int ts1a = _params->ts1 / 100;
int ts1b = _params->ts1 % 100;
int th1a = _params->th1 / 100;
int th1b = _params->th1 % 100;
int psa = _params->ps / 100;
int psb = _params->ps % 100;
int pha = _params->ph / 100;
int phb = _params->ph % 100;
bool cEn = _params->cEn;
bool tEn = (!cEn) ? false : _params->tEn;
bool pInc = (!cEn) ? false : _params->pInc;
bool pDec = (!cEn) ? false : _params->pDec;
bool pEn = pInc || pDec;
uint8_t pState = (_vStates->pInc) ? 1 : (_vStates->pDec) ? 2 : 0;
bool tState = _vStates->t1;
// %u in sprintf() geht nur bis uint16_t
String baudStr(_modbusParams->baudrate);
_printtime("homescreen");
firstPage();
do {
setFont(u8g2_font_fub20_tf);
if (_vals->t1 == _SENSOR_FAULT)
sprintf(_text, "--.-- \xb0\C");
else
sprintf(_text, "%d.%.2d \xb0\C", _vals->t1 / 100, _vals->t1 % 100);
drawStr(0, 27, _text);
if (_vals->p == _SENSOR_FAULT)
sprintf(_text, "--.-- bar");
else
sprintf(_text, "%d.%.2d bar", _vals->p / 100, _vals->p % 100);
drawStr(0, 57, _text);
//drawHLine(0, 57, 128);
setFont(u8g2_font_micro_tr);
//setFont(u8g2_font_5x7_tr);
sprintf(_text, "%d.%.2d+-%d.%.2d Regelung %saktiv", ts1a, ts1b, th1a, th1b, (tEn) ? "": "in");
drawStr(0, 5, _text);
sprintf(_text, "%d.%.2d+-%d.%.2d P+ %s P- %s", psa, psb, pha, phb, (pInc) ? "ein": "aus", (pDec) ? "ein": "aus");
drawStr(0, 35, _text);
// sprintf(_text, "Modbus Adr. %u, Baudr. %u", _modbusParams->address, _modbusParams->baudrate);
sprintf(_text, "Modbus Adr. %u, Baudr. %s", _modbusParams->address, baudStr.c_str());
drawStr(0, 64, _text);
if (cEn) {
if (tEn) {
if (tState) {
setFont(u8g2_font_open_iconic_arrow_2x_t);
drawStr(112, 25, "\x58"); // Kreislaufsymbol
}
} else {
setFont(u8g2_font_open_iconic_check_2x_t);
drawStr(112, 25, "\x42"); // X in schwarzem Kreis
}
if (!pEn) {
setFont(u8g2_font_open_iconic_check_2x_t);
drawStr(112, 55, "\x42"); // X in schwarzem Kreis
} else if (pState == 1) {
setFont(u8g2_font_open_iconic_arrow_2x_t);
drawStr(112, 55, "\x4b"); // Pfeil rauf
} else if (pState == 2) {
setFont(u8g2_font_open_iconic_arrow_2x_t);
drawStr(112, 55, "\x48"); // Pfeil runter
}
} else {
setFont(u8g2_font_open_iconic_check_2x_t);
// drawStr(112, 25, "\x42"); // X in schwarzem Kreis
// drawStr(112, 55, "\x42"); // X in schwarzem Kreis
drawStr(112, 22, "\x42"); // X in schwarzem Kreis
drawStr(112, 58, "\x42"); // X in schwarzem Kreis
setFont(u8g2_font_open_iconic_embedded_2x_t);
drawStr(113, 40, "\x4e"); // Power-Symbol
// drawLine(0, 0, 128, 64);
// drawLine(128, 0, 0, 64);
}
} while (nextPage());
}
void Display::init()
{
pinMode(bgLed, OUTPUT);
digitalWrite(bgLed, true);
begin(btnSelect, btnNext, btnPrev, U8X8_PIN_NONE, U8X8_PIN_NONE, btnCancel);
}
void Display::modbusProblem()
{
firstPage();
do {
setFont(_STD_FONT);
uint8_t y = _FIRS_LINE_Y;
drawStr(0, y, "Problem:");
y += _STD_LINEHEIGHT;
drawStr(0, y, "Fehler Modbus");
} while (nextPage());
}
void Display::oneWireDevicesDetected(uint8_t count)
{
setFont(_SEL_FONT);
setFontRefHeightAll();
sprintf(_text, "%u", count);
userInterfaceMessage("OneWire-Sensoren", "Erfolgreich erkannt:", _text, " OK ");
}
void Display::tooMuchOneWireDevicesDetected(uint8_t actual, uint8_t max)
{
setFont(_SEL_FONT);
setFontRefHeightAll();
sprintf(_text, "%u (max: %u)", actual, max);
userInterfaceMessage("Fehler OneWire", "Zu viele Sensoren:", _text, " OK ");
}
void Display::noOneWireDevicesDetected()
{
setFont(_SEL_FONT);
setFontRefHeightAll();
userInterfaceMessage("Fehler OneWire", "Keine Sensoren", "erkannt", " OK ");
}
void Display::greeting()
{
firstPage();
do {
setFont(_STD_FONT);
uint8_t y = _FIRS_LINE_Y;
drawStr(0, y, "Dreher");
y += _STD_LINEHEIGHT;
drawStr(0, y, "Brauanlagen");
} while (nextPage());
}
void Display::bgLight(uint8_t value)
{
_bgLightAnalog = value;
_bgLightBool = (value > 0) ? true : false;
analogWrite(bgLed, value);
}
void Display::bgLight(bool state)
{
_bgLightAnalog = (state) ? 255 : 0;
_bgLightBool = state;
digitalWrite(bgLed, state);
}
bool Display::bgLightIncrease(uint8_t step)
{
_bgLightBool = true;
if (_bgLightAnalog == 255) {
return true;
}
int val = (int) _bgLightAnalog;
if (val + step >= 255) {
_bgLightAnalog = 255;
digitalWrite(bgLed, true);
return true;
} else {
_bgLightAnalog += step;
analogWrite(bgLed, _bgLightAnalog);
}
return false;
}
bool Display::bgLightDecrease(uint8_t step)
{
if (_bgLightAnalog == 0) {
_bgLightBool = false;
return true;
}
int val = (int) _bgLightAnalog;
if (val - step <= 0) {
_bgLightAnalog = 0;
_bgLightBool = false;
digitalWrite(bgLed, false);
return true;
} else {
_bgLightBool = true;
_bgLightAnalog -= step;
analogWrite(bgLed, _bgLightAnalog);
}
return false;
}
uint8_t Display::bgLightAnalog()
{
return _bgLightAnalog;
}
bool Display::bgLightState()
{
return _bgLightBool;
}
void Display::_baudrateCursorPos()
{
switch (_modbusParams->baudrate) {
case 300: _cursor = _POS_BAUDRATE_300; break;
case 1200: _cursor = _POS_BAUDRATE_1200; break;
case 2400: _cursor = _POS_BAUDRATE_2400; break;
case 4800: _cursor = _POS_BAUDRATE_4800; break;
case 19200: _cursor = _POS_BAUDRATE_19200; break;
case 38400: _cursor = _POS_BAUDRATE_38400; break;
case 57600: _cursor = _POS_BAUDRATE_57600; break;
case 115200: _cursor = _POS_BAUDRATE_115200; break;
default: _cursor = _POS_BAUDRATE_9600;
}
}
void Display::_setModbusBaudrate()
{
modbusParameters params;
switch (_cursor) {
case _POS_BAUDRATE_300: params.baudrate = 300; break;
case _POS_BAUDRATE_1200: params.baudrate = 1200; break;
case _POS_BAUDRATE_2400: params.baudrate = 2400; break;
case _POS_BAUDRATE_4800: params.baudrate = 4800; break;
case _POS_BAUDRATE_19200: params.baudrate = 19200; break;
case _POS_BAUDRATE_38400: params.baudrate = 38400; break;
case _POS_BAUDRATE_57600: params.baudrate = 57600; break;
case _POS_BAUDRATE_115200: params.baudrate = 115200; break;
default: params.baudrate = 9600;
}
setModbusParams(params);
}
uint8_t Display::_event()
{
uint8_t event = getMenuEvent();
if (event)
return event;
bool next = !digitalRead(btnNext);
bool prev = !digitalRead(btnPrev);
bool cancel = !digitalRead(btnCancel);
if (next || prev || cancel) {
if (millis() - _checkFastStepTime > _BEGIN_FAST_STEPS_DELAY) {
if (millis() - _lastFastStep > _FAST_STEP_MS) {
_lastFastStep = millis();
if (next)
event = U8X8_MSG_GPIO_MENU_NEXT;
else if (prev)
event = U8X8_MSG_GPIO_MENU_PREV;
else if (cancel)
event = _BACK_TO_HOME_SCREEN;
if (millis() - _checkFastStepTime > _BEGIN_BIG_STEPS_DELAY)
_enableBigSteps = true;
}
}
} else {
_checkFastStepTime = millis();
_enableBigSteps = false;
}
return event;
}
void Display::_nextEvent()
{
if (_selection) {
if (_inputValueActive) {
if (_val > _valMin) {
int step = (_enableBigSteps) ? _BIG_STEP * _stepSize : _stepSize;
if (_val - step > _valMin)
_val -= step;
else
_val = _valMin;
}
} else {
_cursor++;
if (_cursor >= _selCnt) {
_cursor = 0;
}
}
} else {
_selection = _MENU_MAIN;
}
}
void Display::_prevEvent()
{
if (_selection) {
if (_inputValueActive) {
if (_val < _valMax) {
int step = (_enableBigSteps) ? _BIG_STEP * _stepSize : _stepSize;
if (_val < step)
_val = step;
else if (_val + step < _valMax)
_val += step;
else
_val = _valMax;
}
} else {
if (_cursor == 0) {
_cursor = _selCnt;
}
_cursor--;
}
} else {
_selection = _MENU_MAIN;
}
}
void Display::_saveCursorPosForLevel(MenuLevel level)
{
switch (level) {
case MenuLevel_1:
_cur_l1 = _cursor;
break;
case MenuLevel_2:
_cur_l2 = _cursor;
break;
case MenuLevel_3:
_cur_l3 = _cursor;
break;
}
_prevCursor = _cursor;
_cursor = 0;
}
void Display::_restrictInputValue(int min, int max, Precision p=Precision_0)
{
char s1[10];
char s2[10];
switch (p) {
case Precision_1:
if (min % 10 == 0)
sprintf(s1, "%d", min / 10);
else
sprintf(s1, "%d.%.1d", min / 10, min % 10);
if (max % 10 == 0)
sprintf(s2, "%d", max / 10);
else
sprintf(s2, "%d.%.1d", max / 10, max % 10);
break;
case Precision_2:
if (min % 100 == 0)
sprintf(s1, "%d", min / 100);
else
sprintf(s1, "%d.%.2d", min / 100, min % 100);
if (max % 100 == 0)
sprintf(s2, "%d", max / 100);
else
sprintf(s2, "%d.%.2d", max / 100, max % 100);
break;
default:
sprintf(s1, "%d", min);
sprintf(s2, "%d", max);
}
_valMin = min;
_valMax = max;
sprintf(_text2, "min: %s, max: %s", s1, s2);
}
void Display::_inputValue(const uint8_t stepSize, const char *title, const char *val=nullptr, const char *text=nullptr)
{
if (!val) {
val = _text;
} else if (val == "__float__") {
sprintf(_text, "%d.%.2d", _val / 100, _val % 100);
val = _text;
}
if (!text)
text = _text2;
_inputValueActive = true;
_stepSize = stepSize;
my_UserInterfaceInputValueString(&u8g2, title, text, val);
}
uint8_t Display::_restoreCursor()
{
switch (_menu_depth) {
case 1:
_cursor = _cur_l1;
break;
case 2:
_cursor = _cur_l2;
break;
case 3:
_cursor = _cur_l3;
break;
default:
_cursor = 0;
_selection = 0;
}
return _menu_depth;
}