#include #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 && _selection) 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; }