Added analog voltage display

This commit is contained in:
Thomas Hooge 2024-12-24 20:24:48 +01:00
parent df5ff1c91a
commit 0274b5d755
4 changed files with 268 additions and 119 deletions

View File

@ -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<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}
void fillPoly4(const std::vector<Point>& 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);

View File

@ -39,6 +39,14 @@ GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay();
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
#endif
struct Point {
double x;
double y;
};
Point rotatePoint(const Point& origin, const Point& p, double angle);
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle);
void fillPoly4(const std::vector<Point>& 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);

View File

@ -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<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}
void fillPoly4(const std::vector<Point>& 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<Point> pts = {

View File

@ -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<Point> 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<int, String> 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);