diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index 5fa9521..5ef81d8 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -180,6 +180,48 @@ String xdrDelete(String input){ return input; } +Point rotatePoint(const Point& origin, const Point& p, double angle) { + // rotate poind around origin by degrees + Point rotated; + double phi = angle * M_PI / 180.0; + double dx = p.x - origin.x; + double dy = p.y - origin.y; + rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy; + rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy; + return rotated; +} + +std::vector rotatePoints(const Point& origin, const std::vector& pts, double angle) { + std::vector rotatedPoints; + for (const auto& p : pts) { + rotatedPoints.push_back(rotatePoint(origin, p, angle)); + } + return rotatedPoints; +} + +void fillPoly4(const std::vector& p4, uint16_t color) { + getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color); + getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color); +} + +// Draw centered text +void drawTextCenter(int16_t cx, int16_t cy, String text) { + int16_t x1, y1; + uint16_t w, h; + getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h); + getdisplay().setCursor(cx - w / 2, cy + h / 2); + getdisplay().print(text); +} + +// Draw right aligned text +void drawTextRalign(int16_t x, int16_t y, String text) { + int16_t x1, y1; + uint16_t w, h; + getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h); + getdisplay().setCursor(x - w, y); + getdisplay().print(text); +} + // Show a triangle for trend direction high (x, y is the left edge) void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){ getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color); diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h index 0c7c117..c07bd54 100644 --- a/lib/obp60task/OBP60Extensions.h +++ b/lib/obp60task/OBP60Extensions.h @@ -39,6 +39,14 @@ GxEPD2_BW & getdisplay(); GxEPD2_BW & getdisplay(); #endif +struct Point { + double x; + double y; +}; +Point rotatePoint(const Point& origin, const Point& p, double angle); +std::vector rotatePoints(const Point& origin, const std::vector& pts, double angle); +void fillPoly4(const std::vector& p4, uint16_t color); + void hardwareInit(); void setPortPin(uint pin, bool value); // Set port pin for extension port @@ -58,6 +66,9 @@ void setBuzzerPower(uint power); // Set buzzer power String xdrDelete(String input); // Delete xdr prefix from string +void drawTextCenter(int16_t cx, int16_t cy, String text); +void drawTextRalign(int16_t x, int16_t y, String text); + void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color); diff --git a/lib/obp60task/PageFluid.cpp b/lib/obp60task/PageFluid.cpp index 37dc2dc..ae7349e 100644 --- a/lib/obp60task/PageFluid.cpp +++ b/lib/obp60task/PageFluid.cpp @@ -22,43 +22,6 @@ TODO */ -struct Point { - double x; - double y; -}; - -Point rotatePoint(const Point& origin, const Point& p, double angle) { - // rotate poind around origin by degrees - Point rotated; - double phi = angle * M_PI / 180.0; - double dx = p.x - origin.x; - double dy = p.y - origin.y; - rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy; - rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy; - return rotated; -} - -std::vector rotatePoints(const Point& origin, const std::vector& pts, double angle) { - std::vector rotatedPoints; - for (const auto& p : pts) { - rotatedPoints.push_back(rotatePoint(origin, p, angle)); - } - return rotatedPoints; -} - -void fillPoly4(const std::vector& p4, uint16_t color) { - getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color); - getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color); -} - -void drawTextCentered(int16_t tx, int16_t ty, String text) { - int16_t x, y; - uint16_t w, h; - getdisplay().getTextBounds(text, 0, 0, &x, &y, &w, &h); - getdisplay().setCursor(tx - w / 2, ty + h / 2); - getdisplay().print(text); -} - #define fuel_width 16 #define fuel_height 16 static unsigned char fuel_bits[] = { @@ -171,7 +134,7 @@ class PageFluid : public Page{ } else { strcpy(buffer, "---"); } - drawTextCentered(c.x, c.y + r - 20, String(buffer)); + drawTextCenter(c.x, c.y + r - 20, String(buffer)); // draw symbol (as bitmap) switch (fluidtype) { @@ -197,18 +160,18 @@ class PageFluid : public Page{ // scale texts getdisplay().setFont(&Ubuntu_Bold8pt7b); p = {c.x, c.y - r + 30}; - drawTextCentered(p.x, p.y, "1/2"); + drawTextCenter(p.x, p.y, "1/2"); pr = rotatePoint(c, p, -60); - drawTextCentered(pr.x, pr.y, "1/4"); + drawTextCenter(pr.x, pr.y, "1/4"); pr = rotatePoint(c, p, 60); - drawTextCentered(pr.x, pr.y, "3/4"); + drawTextCenter(pr.x, pr.y, "3/4"); // empty and full getdisplay().setFont(&Ubuntu_Bold12pt7b); p = rotatePoint(c, {c.x, c.y - r + 30}, -130); - drawTextCentered(p.x, p.y, "E"); + drawTextCenter(p.x, p.y, "E"); p = rotatePoint(c, {c.x, c.y - r + 30}, 130); - drawTextCentered(p.x, p.y, "F"); + drawTextCenter(p.x, p.y, "F"); // lines std::vector pts = { diff --git a/lib/obp60task/PageVoltage.cpp b/lib/obp60task/PageVoltage.cpp index e187d60..a1e95fa 100644 --- a/lib/obp60task/PageVoltage.cpp +++ b/lib/obp60task/PageVoltage.cpp @@ -11,6 +11,7 @@ bool keylock = false; // Keylock int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s bool trend = true; // Trend indicator [0|1], 0=off, 1=on double raw = 0; +char mode = 'D'; // display mode (A)nalog | (D)igital public: PageVoltage(CommonData &common){ @@ -24,6 +25,16 @@ public: return 0; // Commit the key } + // Switch display mode + if (key == 2) { + if (mode == 'A') { + mode = 'D'; + } else { + mode = 'A'; + } + return 0; + } + // Trend indicator if(key == 5){ trend = !trend; @@ -38,6 +49,41 @@ public: return key; } + void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) { + getdisplay().setFont(&Ubuntu_Bold8pt7b); + getdisplay().setCursor(x, y); + if (prefix) { + getdisplay().print("Avg: "); + } + switch (average) { + case 0: + getdisplay().print("1s"); + break; + case 1: + getdisplay().print("10s"); + break; + case 2: + getdisplay().print("60s"); + break; + case 3: + getdisplay().print("300s"); + break; + default: + getdisplay().print("1s"); + break; + } + } + + void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) { + getdisplay().setFont(&Ubuntu_Bold16pt7b); + getdisplay().setCursor(x, y); + getdisplay().print("V"); + getdisplay().fillRect(x, y + 6, 22, 3, color); + getdisplay().fillRect(x, y + 11, 6, 3, color); + getdisplay().fillRect(x + 8, y + 11, 6, 3, color); + getdisplay().fillRect(x + 16, y + 11, 6, 3, color); + } + virtual void displayPage(CommonData &commonData, PageData &pageData) { GwConfigHandler *config = commonData.config; @@ -135,92 +181,177 @@ public: // Set display in partial refresh mode getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update - // Show name - getdisplay().setTextColor(commonData.fgcolor); - getdisplay().setFont(&Ubuntu_Bold32pt7b); - getdisplay().setCursor(20, 100); - getdisplay().print(name1); // Value name + if (mode == 'D') { + // Display mode digital - // Show unit - getdisplay().setFont(&Ubuntu_Bold20pt7b); - getdisplay().setCursor(270, 100); - getdisplay().print("V"); + // Show name + getdisplay().setTextColor(commonData.fgcolor); + getdisplay().setFont(&Ubuntu_Bold32pt7b); + getdisplay().setCursor(20, 100); + getdisplay().print(name1); // Value name - // Show battery type - getdisplay().setFont(&Ubuntu_Bold8pt7b); - getdisplay().setCursor(295, 100); - getdisplay().print(batType); + // Show unit + getdisplay().setFont(&Ubuntu_Bold20pt7b); + getdisplay().setCursor(270, 100); + getdisplay().print("V"); - // Show average settings - getdisplay().setFont(&Ubuntu_Bold8pt7b); - getdisplay().setCursor(320, 84); - switch (average) { - case 0: - getdisplay().print("Avg: 1s"); - break; - case 1: - getdisplay().print("Avg: 10s"); - break; - case 2: - getdisplay().print("Avg: 60s"); - break; - case 3: - getdisplay().print("Avg: 300s"); - break; - default: - getdisplay().print("Avg: 1s"); - break; - } + // Show battery type + getdisplay().setFont(&Ubuntu_Bold8pt7b); + getdisplay().setCursor(295, 100); + getdisplay().print(batType); - // Reading bus data or using simulation data - getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b); - getdisplay().setCursor(20, 240); - if(simulation == true){ - if(batVoltage == "12V"){ - value1 = 12.0; - } - if(batVoltage == "24V"){ - value1 = 24.0; - } - value1 += float(random(0, 5)) / 10; // Simulation data - getdisplay().print(value1,1); - } - else{ - // Check for valid real data, display also if hold values activated - if(valid1 == true || holdvalues == true){ - // Resolution switching - if(value1 < 10){ - getdisplay().print(value1,2); + // Show average settings + printAvg(average, 320, 84, true); + + // Reading bus data or using simulation data + getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b); + getdisplay().setCursor(20, 240); + if(simulation == true){ + if(batVoltage == "12V"){ + value1 = 12.0; } - if(value1 >= 10 && value1 < 100){ - getdisplay().print(value1,1); - } - if(value1 >= 100){ - getdisplay().print(value1,0); + if(batVoltage == "24V"){ + value1 = 24.0; } + value1 += float(random(0, 5)) / 10; // Simulation data + getdisplay().print(value1,1); } else{ - getdisplay().print("---"); // Missing bus data - } - } - - // Trend indicator - // Show trend indicator - if(trend == true){ - getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area - getdisplay().fillRect(315, 183, 35, 4, commonData.fgcolor); // Draw separator - if(int(raw * 10) > int(valueTrend * 10)){ - displayTrendHigh(320, 174, 11, commonData.fgcolor); // Show high indicator + // Check for valid real data, display also if hold values activated + if(valid1 == true || holdvalues == true){ + // Resolution switching + if(value1 < 10){ + getdisplay().print(value1,2); + } + if(value1 >= 10 && value1 < 100){ + getdisplay().print(value1,1); + } + if(value1 >= 100){ + getdisplay().print(value1,0); + } + } + else{ + getdisplay().print("---"); // Missing bus data + } } - if(int(raw * 10) < int(valueTrend * 10)){ - displayTrendLow(320, 195, 11, commonData.fgcolor); // Show low indicator - } - } - // No trend indicator - else{ - getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area - } + // Trend indicator + // Show trend indicator + if(trend == true){ + getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area + getdisplay().fillRect(315, 183, 35, 4, commonData.fgcolor); // Draw separator + if(int(raw * 10) > int(valueTrend * 10)){ + displayTrendHigh(320, 174, 11, commonData.fgcolor); // Show high indicator + } + if(int(raw * 10) < int(valueTrend * 10)){ + displayTrendLow(320, 195, 11, commonData.fgcolor); // Show low indicator + } + } + // No trend indicator + else{ + getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area + } + + } + else { + // Display mode analog + + // center + Point c = {260, 270}; + uint8_t r = 240; + + Point p1, p2; + std::vector pts; + + // Instrument + getdisplay().drawCircleHelper(c.x, c.y, r + 2, 0x01, commonData.fgcolor); + getdisplay().drawCircleHelper(c.x, c.y, r + 1, 0x01, commonData.fgcolor); + getdisplay().drawCircleHelper(c.x, c.y, r , 0x01, commonData.fgcolor); + + // Scale + // angle to voltage scale mapping + std::map mapping = { + {15, "10"}, {30, "11"}, {45, "12"}, {60, "13"}, {75, "14"} + }; + pts = { + {c.x - r, c.y - 1}, + {c.x - r + 12, c.y - 1}, + {c.x - r + 12, c.y + 1}, + {c.x - r, c.y + 1} + }; + getdisplay().setFont(&Ubuntu_Bold10pt7b); + for (int angle = 3; angle < 90; angle += 3) { + if (angle % 15 == 0) { + fillPoly4(rotatePoints(c, pts, angle), commonData.fgcolor); + p1 = rotatePoint(c, {c.x - r + 30, c.y}, angle); + drawTextCenter(p1.x, p1.y, mapping[angle]); + } + else { + p1 = rotatePoint(c, {c.x - r, c.y}, angle); + p2 = rotatePoint(c, {c.x - r + 6, c.y}, angle); + getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData.fgcolor); + } + } + + // Pointer rotation and limits + double angle; + if (not valid1) { + angle = -0.5; + } + else { + if (value1 > 15.0) { + angle = 91; + } + else if (value1 <= 9) { + angle = -0.5; + } + else { + angle = (value1 - 9) * 15; + } + } + + // Pointer + // thick part + pts = { + {c.x - 2, c.y + 3}, + {c.x - r + 38, c.y + 2}, + {c.x - r + 38, c.y - 2}, + {c.x - 2, c.y - 3} + }; + fillPoly4(rotatePoints(c, pts, angle), commonData.fgcolor); + // thin part + pts = { + {c.x - r + 40, c.y + 1}, + {c.x - r + 5, c.y + 1}, + {c.x - r + 5, c.y -1}, + {c.x - r + 40, c.y - 1}, + }; + fillPoly4(rotatePoints(c, pts, angle), commonData.fgcolor); + + // base + getdisplay().fillCircle(c.x, c.y, 8, commonData.fgcolor); + getdisplay().fillCircle(c.x, c.y, 6, commonData.bgcolor); + + // Symbol + printVoltageSymbol(40, 60, commonData.fgcolor); + + // Additional informatio at right side + getdisplay().setFont(&Ubuntu_Bold8pt7b); + getdisplay().setCursor(300, 60); + getdisplay().print("Source:"); + getdisplay().setCursor(300, 80); + getdisplay().print(name1); + + getdisplay().setCursor(300, 110); + getdisplay().print("Type:"); + getdisplay().setCursor(300, 130); + getdisplay().print(batType); + + getdisplay().setCursor(300, 160); + getdisplay().print("Avg:"); + printAvg(average, 300, 180, false); + + } // Key Layout getdisplay().setTextColor(commonData.fgcolor); @@ -228,6 +359,8 @@ public: if(keylock == false){ getdisplay().setCursor(10, 290); getdisplay().print("[AVG]"); + getdisplay().setCursor(62, 290); + getdisplay().print("[MODE]"); getdisplay().setCursor(130, 290); getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]"); getdisplay().setCursor(293, 290);