| @@ -41,7 +41,6 @@ | |||
| ****************/ | |||
| Display::Display() : tft(Adafruit_ST7735(CS, DC, RST)) { // @suppress("Abstract class cannot be instantiated") | |||
| starttime = 0; | |||
| configValue = 0.0; | |||
| configUnit = "m/s"; | |||
| distance = 0; | |||
| environment = 1; | |||
| @@ -128,27 +127,13 @@ void Display::showInitialization(void) { | |||
| drawXCenteredText("EchoLoT", &titillium_web_regular16pt7b, 1, ST7735_CYAN, 36); | |||
| drawXCenteredText("designed by", &FreeSans9pt7b, 1, ST7735_WHITE, 64); | |||
| showBrand(16, 84); | |||
| delay(500); | |||
| delay(2000); | |||
| redrawFrame(); | |||
| drawText("powered by", &FreeSans9pt7b, 1, ST7735_CYAN, 35, 30); | |||
| tft.fillRoundRect(46, 42, 68, 68, 7, ST7735_STM32BLUE); | |||
| drawBitmap(48, 44, 64, 64, epd_bitmap_stm32duino_logo_icon, ST7735_WHITE); | |||
| // tft.drawBitmap(5, 100, epd_bitmap_gear, 24, 24, ST7735_YELLOW); | |||
| // tft.drawBitmap(40, 100, epd_bitmap_Zzz_24, 24, 24, ST7735_CYAN); | |||
| // tft.drawBitmap(75, 100, epd_bitmap_Measure_25, 25, 25, ST7735_RED); | |||
| // tft.fillTriangle(75, 98, 80, 102, 75, 107, ST7735_RED); | |||
| // tft.fillTriangle(82, 98, 87, 102, 82, 107, ST7735_RED); | |||
| // tft.fillTriangle(89, 98, 94, 102, 89, 107, ST7735_RED); | |||
| // tft.fillTriangle(96, 98, 101, 102, 96, 107, ST7735_RED); | |||
| // | |||
| // tft.drawBitmap(110, 100, epd_bitmap_air, 24, 24, ST7735_CYAN); | |||
| // tft.drawBitmap(110, 100, epd_bitmap_water, 24, 24, ST7735_BLUE); | |||
| // tft.drawBitmap(110, 100, epd_bitmap_water_s, 24, 24, ST7735_BLUE); | |||
| // tft.drawBitmap(1, 160 - 32, thermometer_icon_32, 32, 32, 0xE71C); | |||
| delay(500); | |||
| delay(2000); | |||
| } | |||
| void Display::display(void) { | |||
| @@ -181,15 +166,18 @@ void Display::showFrame(Status status) { | |||
| } | |||
| } | |||
| void Display::setStatusValues(float airTemp, float waterTemp) { | |||
| void Display::setStatusValues(float airTemp, float waterTemp, float sonicSpeedAir, uint8_t saltPromilleWater) { | |||
| this->airTemp = airTemp; | |||
| this->waterTemp = waterTemp; | |||
| this->sonicSpeedAir = sonicSpeedAir; | |||
| this->saltPromilleWater = saltPromilleWater; | |||
| } | |||
| void Display::setEnvironment(int environment) { | |||
| if (this->environment != environment) { | |||
| this->environment = environment; | |||
| redrawEnvIcon(); | |||
| redrawTempIcon(); | |||
| } | |||
| } | |||
| @@ -200,6 +188,28 @@ void Display::setDistance(uint32_t distance) { | |||
| } | |||
| } | |||
| void Display::setConfigOption(String configOption) { | |||
| if (!this->configOption.equals(configOption)) { | |||
| this->configOption = configOption; | |||
| redrawConfigOption(); | |||
| } | |||
| } | |||
| void Display::setConfigText(String configText) { | |||
| if (!this->configValue.equals(configText)) { | |||
| this->configValue = configText; | |||
| redrawConfigValue(); | |||
| } | |||
| } | |||
| void Display::setConfigUnit(String configUnit) { | |||
| if (!this->configUnit.equals(configUnit)) { | |||
| this->configUnit = configUnit; | |||
| redrawConfigUnit(); | |||
| } | |||
| } | |||
| /* ===== Private methods ===== */ | |||
| void Display::drawBitmap(int x, int y, int w, int h, const uint8_t bitmap[], uint16_t col) { | |||
| tft.drawBitmap(x, y, bitmap, w, h, col); | |||
| @@ -215,14 +225,14 @@ void Display::redrawFrame() { | |||
| tft.drawRoundRect(1, 1, 158, 126, 8, ST7735_CYAN); | |||
| } | |||
| void Display::redrawGrid() { | |||
| void Display::redrawGrid(bool cfg) { | |||
| redrawFrame(); | |||
| tft.drawLine(0, 31, 160, 31, ST7735_CYAN); | |||
| tft.drawLine(0, 32, 160, 32, ST7735_CYAN); | |||
| tft.drawLine(0, 89, 160, 89, ST7735_CYAN); | |||
| tft.drawLine(0, 90, 160, 90, ST7735_CYAN); | |||
| tft.drawLine(80, 90, 80, 128, ST7735_CYAN); | |||
| tft.drawLine(81, 90, 81, 128, ST7735_CYAN); | |||
| tft.drawLine(cfg ? 120 : 80, 90, cfg ? 120 : 80, 128, ST7735_CYAN); | |||
| tft.drawLine(cfg ? 121 : 81, 90, cfg ? 121 : 81, 128, ST7735_CYAN); | |||
| } | |||
| void Display::redrawIdleStatus() { | |||
| @@ -230,22 +240,26 @@ void Display::redrawIdleStatus() { | |||
| tft.drawBitmap(25, 4, epd_bitmap_Zzz_24, 24, 24, ST7735_CYAN); | |||
| drawText("OPERATIV", &titillium_web_semibold8pt7b, 1, ST7735_WHITE, 55, 21); | |||
| float temp = environment == 1 ? airTemp : waterTemp; | |||
| uint16_t col = temp <= 17.0 ? ST7735_WHITE : temp <= 24.0 ? ST7735_GREEN : ST7735_ORANGE; | |||
| uint16_t thH = temp <= 17.0 ? 0 : temp <= 24.0 ? 5 : 9; | |||
| tft.drawBitmap(0, 92, thermometer_icon_32, 32, 32, col); | |||
| tft.drawRect(15, 107 - thH, 2, thH, col); | |||
| drawText(String(temp, 1), &titillium_web_semibold8pt7b, 1, col, 35, 114); | |||
| drawCircle(72, 102, 2, col); | |||
| // float temp = environment == 1 ? airTemp : waterTemp; | |||
| // uint16_t col = temp <= 17.0 ? ST7735_WHITE : temp <= 24.0 ? ST7735_GREEN : ST7735_ORANGE; | |||
| // uint16_t thH = temp <= 17.0 ? 0 : temp <= 24.0 ? 5 : 9; | |||
| // tft.drawBitmap(0, 92, thermometer_icon_32, 32, 32, col); | |||
| // tft.drawRect(15, 107 - thH, 2, thH, col); | |||
| // drawRightAlignedText(String(temp, 1), &titillium_web_semibold8pt7b, 1, col, 96, 114); | |||
| // drawCircle(72, 104, 2, col); | |||
| redrawTempIcon(); | |||
| redrawEnvIcon(); | |||
| redrawDistance(); | |||
| } | |||
| void Display::redrawConfigStatus() { | |||
| redrawGrid(); | |||
| redrawGrid(true); | |||
| tft.drawBitmap(35, 4, epd_bitmap_gear, 24, 24, ST7735_YELLOW); | |||
| drawText("KONFIG.", &titillium_web_semibold8pt7b, 1, ST7735_WHITE, 65, 21); | |||
| drawText("KONFIG.", &titillium_web_semibold8pt7b, 1, ST7735_YELLOW, 65, 21); | |||
| redrawConfigValue(); | |||
| redrawConfigOption(); | |||
| redrawConfigUnit(); | |||
| } | |||
| void Display::redrawEnvIcon() { | |||
| @@ -255,6 +269,17 @@ void Display::redrawEnvIcon() { | |||
| tft.drawBitmap(108, 97, environmentIcon, 24, 24, col); | |||
| } | |||
| void Display::redrawTempIcon() { | |||
| float temp = environment == 1 ? airTemp : waterTemp; | |||
| uint16_t col = temp <= 17.0 ? ST7735_WHITE : temp <= 24.0 ? ST7735_GREEN : ST7735_ORANGE; | |||
| uint16_t thH = temp <= 17.0 ? 0 : temp <= 24.0 ? 5 : 9; | |||
| tft.fillRect(4, 92, 75, 30, ST7735_BLACK); | |||
| tft.drawBitmap(0, 92, thermometer_icon_32, 32, 32, col); | |||
| tft.drawRect(15, 107 - thH, 2, thH, col); | |||
| drawRightAlignedText(String(temp, 1), &titillium_web_semibold8pt7b, 1, col, 96, 114); | |||
| drawCircle(72, 104, 2, col); | |||
| } | |||
| void Display::runMeasureAnimation(bool run) { | |||
| if (!this->doMeasureAnimation && run) { | |||
| this->starttime = millis(); | |||
| @@ -301,16 +326,44 @@ void Display::runMeasureAnimation(bool run) { | |||
| } | |||
| void Display::redrawDistance() { | |||
| tft.fillRect(4, 34, 152, 56, ST7735_BLACK); | |||
| tft.fillRect(4, 34, 152, 54, ST7735_BLACK); | |||
| if (distance == 0) { | |||
| drawXCenteredText("- - -", &titillium_web_regular16pt7b, 1, ST7735_CYAN, 69); | |||
| drawXCenteredText("- - -", &titillium_web_regular16pt7b, 1, ST7735_WHITE, 69); | |||
| } else { | |||
| if (environment == 1) { | |||
| uint16_t dist_CM = distance / 10; | |||
| drawXCenteredText(String(dist_CM) + " cm", &titillium_web_regular16pt7b, 1, ST7735_CYAN, 69); | |||
| drawXCenteredText(String(dist_CM) + " cm", &titillium_web_regular16pt7b, 1, ST7735_WHITE, 69); | |||
| } else { | |||
| float dist_M = float(distance) / 1000; | |||
| drawXCenteredText(String(dist_M, 1) + " m", &titillium_web_regular16pt7b, 1, ST7735_CYAN, 69); | |||
| drawXCenteredText(String(dist_M, 1) + " m", &titillium_web_regular16pt7b, 1, ST7735_WHITE, 69); | |||
| } | |||
| } | |||
| } | |||
| void Display::redrawConfigValue() { | |||
| tft.fillRect(4, 34, 152, 54, ST7735_BLACK); | |||
| drawXCenteredText(configValue, &titillium_web_regular16pt7b, 1, ST7735_ORANGE, 69); | |||
| } | |||
| void Display::redrawConfigOption() { | |||
| tft.fillRect(4, 92, 116, 30, ST7735_BLACK); | |||
| drawText(configOption, &titillium_web_semibold8pt7b, 1, ST7735_WHITE, 6, 113); | |||
| } | |||
| void Display::redrawConfigUnit() { | |||
| int16_t x1, y1; // Top-left corner of text | |||
| uint16_t w, h; // Width and height | |||
| tft.getTextBounds(configUnit, 0, 0, &x1, &y1, &w, &h); | |||
| tft.fillRect(123, 92, 35, 30, ST7735_BLACK); | |||
| drawRightAlignedText(configUnit, &titillium_web_semibold8pt7b, 1, ST7735_WHITE, (37 - w) / 2 + 2, 113); | |||
| if (configUnit.equals(" C")) { | |||
| drawCircle(121 + ((37 - w) / 2), 105, 2, ST7735_WHITE); | |||
| } else if (configUnit.equals(" / ")) { | |||
| drawCircle(124 + ((37 - w) / 2), 105, 2, ST7735_WHITE); | |||
| drawCircle(124 + ((37 - w) / 2) + 10, 110, 2, ST7735_WHITE); | |||
| drawCircle(124 + ((37 - w) / 2) + 15, 110, 2, ST7735_WHITE); | |||
| } | |||
| } | |||
| @@ -21,27 +21,31 @@ | |||
| class Display { | |||
| private: | |||
| Adafruit_ST7735 tft; // @suppress("Abstract class cannot be instantiated") | |||
| String configText; | |||
| String configValue; | |||
| String configOption; | |||
| float configValue; | |||
| String configUnit; | |||
| bool refreshScreen; | |||
| bool doMeasureAnimation; | |||
| unsigned long starttime; | |||
| float airTemp; | |||
| float waterTemp; | |||
| float sonicSpeedAir; | |||
| uint8_t saltPromilleWater; | |||
| int environment; | |||
| uint16_t distance; | |||
| void drawBitmap(int x, int y, int w, int h, const uint8_t bitmap[], uint16_t col); | |||
| void drawRGBBitmap(int x, int y, int w, int h, const uint16_t bitmap[]); | |||
| void redrawFrame(); | |||
| void redrawGrid(); | |||
| void redrawGrid(bool cfg = false); | |||
| void redrawIdleStatus(); | |||
| void redrawConfigStatus(); | |||
| void redrawEnvIcon(); | |||
| void redrawIdleIcon(); | |||
| void redrawTempIcon(); | |||
| void redrawDistance(); | |||
| void redrawConfigValue(); | |||
| void redrawConfigOption(); | |||
| void redrawConfigUnit(); | |||
| public: | |||
| Display(); | |||
| @@ -56,11 +60,13 @@ class Display { | |||
| void drawRightAlignedText(String txt, const GFXfont *font, uint8_t s, uint16_t c, int16_t xOffset, int16_t y); | |||
| void drawText(String txt, const GFXfont *font, uint8_t s, uint16_t c, int16_t x, int16_t y); | |||
| void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); | |||
| void setStatusValues(float airTemp, float waterTemp); | |||
| void setStatusValues(float airTemp, float waterTemp, float sonicSpeedAir, uint8_t saltPromilleWater); | |||
| void setEnvironment(int environment); | |||
| void setDistance(uint32_t distance); | |||
| void runMeasureAnimation(bool run); | |||
| void setConfigOption(String configOption); | |||
| void setConfigText(String configText); | |||
| void setConfigUnit(String configUnit); | |||
| }; | |||
| #endif /* DISPLAY_H_ */ | |||
| @@ -36,6 +36,8 @@ EchoLotSetup echoLotSetup(display); | |||
| void Initialize() { | |||
| display.showInitialization(); | |||
| echoLotSetup.initialize(); | |||
| echoLotSetup.drawInitScreen(); | |||
| delay(2000); | |||
| } | |||
| void setup() { | |||
| @@ -73,15 +75,21 @@ void loop() { | |||
| rotaryControler.loop(); | |||
| if (originStatus != CONFIGURATION) { | |||
| display.setRefreshScreen(); | |||
| display.showFrame(actualStatus); | |||
| echoLotSetup.initialize(); | |||
| } | |||
| if (rotaryControler.isSwitchPressed()) { | |||
| echoLotSetup.onRotaryControlerSwitch(); | |||
| } | |||
| display.showFrame(actualStatus); | |||
| echoLotSetup.onRotaryControlerTurn(rotaryControler.getEncoderMove()); | |||
| if (rotaryControler.isSwitchLongPressed()) { | |||
| Serial.println("RotaryEnc Long SwitchPressed"); | |||
| delay(50); | |||
| // TODO: Save the edited config values | |||
| echoLotSetup.onRotaryControlerLongSwitch(); | |||
| echoLotSetup.initialize(); | |||
| SetActualStatus(IDLE); | |||
| } | |||
| @@ -104,13 +112,13 @@ void loop() { | |||
| SetActualStatus(CONFIGURATION); | |||
| } | |||
| environmentPostion = threePositionSwitch.getPosition(); | |||
| display.setEnvironment(environmentPostion); | |||
| if (measureBtn.isPressing()) { | |||
| SetActualStatus(MEASURING); | |||
| } | |||
| environmentPostion = threePositionSwitch.getPosition(); | |||
| display.setEnvironment(environmentPostion); | |||
| originStatus = IDLE; | |||
| delay(50); | |||
| break; | |||
| @@ -126,11 +134,11 @@ void loop() { | |||
| delayMicroseconds(20); | |||
| digitalWrite(trigPin, LOW); | |||
| //TODO: calculate real distanz according settings | |||
| uint32_t duration = pulseIn(echoPin, HIGH); | |||
| uint32_t distance = calculateDistance(duration); | |||
| display.setDistance(distance); | |||
| display.runMeasureAnimation(true); | |||
| delay(200); | |||
| } else { | |||
| display.setDistance(0); | |||
| @@ -6,8 +6,8 @@ | |||
| */ | |||
| #include "EchoLotSetup.h" | |||
| #include "fonts/titillium_web_regular16pt7b.h" | |||
| #include "fonts/FreeSans7pt7b.h" | |||
| //#include <Fonts/FreeSans9pt7b.h> | |||
| #define ADDR_AIR_TEMP 0 // WORD | |||
| #define ADDR_WATER_TEMP 2 // +2 WORDs | |||
| @@ -31,17 +31,19 @@ EchoLotSetup::EchoLotSetup(Display &display) : display(display) { | |||
| // Initialisierung | |||
| // -------------------------------------------------- | |||
| void EchoLotSetup::initialize() { | |||
| EEPROM.init(); | |||
| readFromEEPROM(); | |||
| display.setStatusValues(airTemp, waterTemp); | |||
| display.clearDisplay(); | |||
| display.showFrame(INITIALIZATION); | |||
| printValues(); | |||
| delay(2000); | |||
| if (doInitialization) { | |||
| EEPROM.init(); | |||
| readFromEEPROM(); | |||
| configStepIndex = 0; | |||
| configStepIndex = 0; | |||
| doInitialization = false; | |||
| String stepTxt = ConfigStep[configStepIndex]; | |||
| String unit = getCfgOptUnitForStepIndex(configStepIndex); | |||
| display.setStatusValues(airTemp, waterTemp, sonicSpeedAir, saltPromilleWater); | |||
| display.setConfigText(String(airTemp, 1)); | |||
| display.setConfigOption(stepTxt); | |||
| display.setConfigUnit(unit); | |||
| doInitialization = false; | |||
| } | |||
| } | |||
| // -------------------------------------------------- | |||
| @@ -49,40 +51,56 @@ void EchoLotSetup::initialize() { | |||
| // -------------------------------------------------- | |||
| void EchoLotSetup::onRotaryControlerSwitch() { | |||
| configStepIndex++; | |||
| if (configStepIndex >= 5) { | |||
| if (configStepIndex > 3) { | |||
| configStepIndex = 0; | |||
| } | |||
| display.setConfigText(getCfgValueForStepIndex(configStepIndex)); | |||
| display.setConfigOption(ConfigStep[configStepIndex]); | |||
| display.setConfigUnit(getCfgOptUnitForStepIndex(configStepIndex)); | |||
| } | |||
| // -------------------------------------------------- | |||
| // Rotary Switch (lang) | |||
| // -------------------------------------------------- | |||
| void EchoLotSetup::onRotaryControlerLongSwitch() { | |||
| save(); | |||
| doInitialization = true; | |||
| } | |||
| // -------------------------------------------------- | |||
| // Rotary Drehung | |||
| // -------------------------------------------------- | |||
| void EchoLotSetup::onRotaryControlerTurn(RotaryEncoder::Direction turn) | |||
| { | |||
| float delta = (turn == RotaryEncoder::Direction::CLOCKWISE) ? 1.0f : -1.0f; | |||
| void EchoLotSetup::onRotaryControlerTurn(RotaryEncoder::Direction turn) { | |||
| switch (configStepIndex) { | |||
| case 0: | |||
| airTemp += delta; | |||
| break; | |||
| case 1: | |||
| waterTemp += delta; | |||
| break; | |||
| case 2: | |||
| sonicSpeedAir += delta; | |||
| break; | |||
| case 3: | |||
| saltPromilleWater += delta; | |||
| break; | |||
| default: | |||
| break; | |||
| if (turn != RotaryEncoder::Direction::NOROTATION) { | |||
| String value = ""; | |||
| int index = 0; | |||
| int sign = (turn == RotaryEncoder::Direction::CLOCKWISE) ? 1 : -1; | |||
| switch (configStepIndex) { | |||
| case 0: | |||
| setAirTemp(min(max(0.0, getAirTemp() + 0.5 * sign), 40.0)); | |||
| value = String(getAirTemp(), 1); | |||
| break; | |||
| case 1: | |||
| setWaterTemp(min(max(0.0, getWaterTemp() + 0.5 * sign), 40.0)); | |||
| value = String(getWaterTemp(), 1); | |||
| break; | |||
| case 2: | |||
| setSonicSpeedAir(min(max(330.0, getSonicSpeedAir() + 0.5 * sign), 343.5)); | |||
| value = String(getSonicSpeedAir(), 1); | |||
| break; | |||
| case 3: | |||
| setSaltPromilleWater(min(max(0, getSaltPromilleWater() + sign), 50)); | |||
| value = String(getSaltPromilleWater()); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| display.setConfigText(value); | |||
| } | |||
| } | |||
| @@ -98,7 +116,7 @@ void EchoLotSetup::save() { | |||
| // Abbrechen | |||
| // -------------------------------------------------- | |||
| void EchoLotSetup::cancel() { | |||
| readFromEEPROM(); | |||
| doInitialization = true; | |||
| } | |||
| // -------------------------------------------------- | |||
| @@ -108,6 +126,15 @@ void EchoLotSetup::clear() { | |||
| display.clearDisplay(); | |||
| } | |||
| // -------------------------------------------------- | |||
| // Initial screen zeichnen | |||
| // -------------------------------------------------- | |||
| void EchoLotSetup::drawInitScreen() { | |||
| display.clearDisplay(); | |||
| display.showFrame(INITIALIZATION); | |||
| printValues(); | |||
| } | |||
| // -------------------------------------------------- | |||
| // EEPROM lesen | |||
| // -------------------------------------------------- | |||
| @@ -167,12 +194,26 @@ String EchoLotSetup::getCfgOptUnitForStepIndex(byte index) { | |||
| switch (index) { | |||
| case 0: | |||
| case 1: | |||
| return "°C"; | |||
| return " C"; | |||
| case 2: | |||
| case 3: | |||
| return "m/s"; | |||
| default: | |||
| return ""; | |||
| return " / "; // for Promille painting | |||
| } | |||
| } | |||
| String EchoLotSetup::getCfgValueForStepIndex(byte configStepIndex) { | |||
| switch (configStepIndex) { | |||
| case 0: | |||
| return String(getAirTemp(), 1); | |||
| case 1: | |||
| return String(getWaterTemp(), 1); | |||
| case 2: | |||
| return String(getSonicSpeedAir(), 1); | |||
| case 3: | |||
| return String(getSaltPromilleWater()); | |||
| default: | |||
| return "- - -"; | |||
| } | |||
| } | |||
| @@ -36,6 +36,7 @@ class EchoLotSetup { | |||
| byte configStepIndex; | |||
| String getCfgOptForStepIndex(byte configStepIndex); | |||
| String getCfgOptUnitForStepIndex(byte configStepIndex); | |||
| String getCfgValueForStepIndex(byte configStepIndex); | |||
| void writeFloatToEEPROM(uint16_t addr, float value); | |||
| float readFloatFromEEPROM(uint16_t addr); | |||
| @@ -43,12 +44,9 @@ class EchoLotSetup { | |||
| int readIntegerFromEEPROM(uint16_t addr); | |||
| public: | |||
| // int eeprom_start_adress = 0; | |||
| const static int eeprom_signatur = 1024; | |||
| const static uint8_t version = 1; | |||
| EchoLotSetup(Display &display); | |||
| void initialize(); | |||
| void drawInitScreen(); | |||
| void onRotaryControlerSwitch(); | |||
| void onRotaryControlerLongSwitch(); | |||
| void onRotaryControlerTurn(RotaryEncoder::Direction turn); | |||
| @@ -2,7 +2,7 @@ | |||
| //This is a automatic generated file | |||
| //Please do not modify this file | |||
| //If you touch this file your change will be overwritten during the next build | |||
| //This file has been generated on 2026-01-25 22:30:05 | |||
| //This file has been generated on 2026-01-27 21:30:42 | |||
| #include "Arduino.h" | |||
| #include "Arduino.h" | |||