diff --git a/.development b/.development new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..156a9f5 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# TFT_eWidget + +An Arduino IDE compatible TFT GUI widget library. \ No newline at end of file diff --git a/examples/Buttons/Button_demo/Button_demo.ino b/examples/Buttons/Button_demo/Button_demo.ino new file mode 100644 index 0000000..54d76bc --- /dev/null +++ b/examples/Buttons/Button_demo/Button_demo.ino @@ -0,0 +1,191 @@ +// Button widget demo, requires display with touch screen + +// Requires widget library here: +// https://github.com/Bodmer/TFT_eWidget + +#include "FS.h" +#include "Free_Fonts.h" // Include the header file attached to this sketch + +#include // Hardware-specific library +#include // Widget library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +#define CALIBRATION_FILE "/TouchCalData1" +#define REPEAT_CAL false + +TFT_eWidget btnL = TFT_eWidget(&tft); +TFT_eWidget btnR = TFT_eWidget(&tft); + +#define BUTTON_W 100 +#define BUTTON_H 50 + +// Create an array of button instances to use in for() loops +// This is more useful where large numbers of buttons are employed +TFT_eWidget* btn[] = {&btnL , &btnR};; +uint8_t buttonCount = sizeof(btn) / sizeof(btn[0]); + +void btnL_pressAction(void) +{ + if (btnL.justPressed()) { + Serial.println("Left button just pressed"); + btnL.drawSmoothButton(true); + } +} + +void btnL_releaseAction(void) +{ + static uint32_t waitTime = 1000; + if (btnL.justReleased()) { + Serial.println("Left button just released"); + btnL.drawSmoothButton(false); + btnL.setReleaseTime(millis()); + waitTime = 10000; + } + else { + if (millis() - btnL.getReleaseTime() >= waitTime) { + waitTime = 1000; + btnL.setReleaseTime(millis()); + btnL.drawSmoothButton(!btnL.getState()); + } + } +} + +void btnR_pressAction(void) +{ + if (btnR.justPressed()) { + btnR.drawSmoothButton(!btnR.getState(), 3, TFT_BLACK, btnR.getState() ? "OFF" : "ON"); + Serial.print("Button toggled: "); + if (btnR.getState()) Serial.println("ON"); + else Serial.println("OFF"); + btnR.setPressTime(millis()); + } + + // if button pressed for more than 1 sec... + if (millis() - btnR.getPressTime() >= 1000) { + Serial.println("Stop pressing my buttton......."); + } + else Serial.println("Right button is being pressed"); +} + +void btnR_releaseAction(void) +{ + // Not action +} + +void initButtons() { + uint16_t x = (tft.width() - BUTTON_W) / 2; + uint16_t y = tft.height() / 2 - BUTTON_H - 10; + btnL.initButtonUL(x, y, BUTTON_W, BUTTON_H, TFT_WHITE, TFT_RED, TFT_BLACK, "Button", 1); + btnL.setPressAction(btnL_pressAction); + btnL.setReleaseAction(btnL_releaseAction); + btnL.drawSmoothButton(false, 3, TFT_BLACK); // 3 is outline width, TFT_BLACK is the surrounding background colour for anti-aliasing + + y = tft.height() / 2 + 10; + btnR.initButtonUL(x, y, BUTTON_W, BUTTON_H, TFT_WHITE, TFT_BLACK, TFT_GREEN, "OFF", 1); + btnR.setPressAction(btnR_pressAction); + //btnR.setReleaseAction(btnR_releaseAction); + btnR.drawSmoothButton(false, 3, TFT_BLACK); // 3 is outline width, TFT_BLACK is the surrounding background colour for anti-aliasing +} + +void setup() { + Serial.begin(115200); + tft.begin(); + tft.setRotation(0); + tft.fillScreen(TFT_BLACK); + tft.setFreeFont(FF18); + + // Calibrate the touch screen and retrieve the scaling factors + touch_calibrate(); + initButtons(); +} + +void loop() { + static uint32_t scanTime = millis(); + uint16_t t_x = 9999, t_y = 9999; // To store the touch coordinates + + // Scan keys every 50ms at most + if (millis() - scanTime >= 50) { + // Pressed will be set true if there is a valid touch on the screen + bool pressed = tft.getTouch(&t_x, &t_y); + scanTime = millis(); + for (uint8_t b = 0; b < buttonCount; b++) { + if (pressed) { + if (btn[b]->contains(t_x, t_y)) { + btn[b]->press(true); + btn[b]->pressAction(); + } + } + else { + btn[b]->press(false); + btn[b]->releaseAction(); + } + } + } + +} + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!LittleFS.begin()) { + Serial.println("Formating file system"); + LittleFS.format(); + LittleFS.begin(); + } + + // check if calibration file exists and size is correct + if (LittleFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + LittleFS.remove(CALIBRATION_FILE); + } + else + { + File f = LittleFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = LittleFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} diff --git a/examples/Buttons/Button_demo/Free_Fonts.h b/examples/Buttons/Button_demo/Free_Fonts.h new file mode 100644 index 0000000..77249ef --- /dev/null +++ b/examples/Buttons/Button_demo/Free_Fonts.h @@ -0,0 +1,377 @@ +// Attach this header file to your sketch to use the GFX Free Fonts. You can write +// sketches without it, but it makes referencing them easier. + +// This calls up ALL the fonts but they only get loaded if you actually +// use them in your sketch. +// +// No changes are needed to this header file unless new fonts are added to the +// library "Fonts/GFXFF" folder. +// +// To save a lot of typing long names, each font can easily be referenced in the +// sketch in three ways, either with: +// +// 1. Font file name with the & in front such as &FreeSansBoldOblique24pt7b +// an example being: +// +// tft.setFreeFont(&FreeSansBoldOblique24pt7b); +// +// 2. FF# where # is a number determined by looking at the list below +// an example being: +// +// tft.setFreeFont(FF32); +// +// 3. An abbreviation of the file name. Look at the list below to see +// the abbreviations used, for example: +// +// tft.setFreeFont(FSSBO24) +// +// Where the letters mean: +// F = Free font +// M = Mono +// SS = Sans Serif (double S to distinguish is form serif fonts) +// S = Serif +// B = Bold +// O = Oblique (letter O not zero) +// I = Italic +// # = point size, either 9, 12, 18 or 24 +// +// Setting the font to NULL will select the GLCD font: +// +// tft.setFreeFont(NULL); // Set font to GLCD + +#define LOAD_tftFF + +#ifdef LOAD_tftFF // Only include the fonts if LOAD_tftFF is defined in User_Setup.h + +// Use these when printing or drawing text in GLCD and high rendering speed fonts +#define GFXFF 1 +#define GLCD 0 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +// Use the following when calling setFont() +// +// Reserved for GLCD font // FF0 +// + +#define TT1 &TomThumb + +#define FM9 &FreeMono9pt7b +#define FM12 &FreeMono12pt7b +#define FM18 &FreeMono18pt7b +#define FM24 &FreeMono24pt7b + +#define FMB9 &FreeMonoBold9pt7b +#define FMB12 &FreeMonoBold12pt7b +#define FMB18 &FreeMonoBold18pt7b +#define FMB24 &FreeMonoBold24pt7b + +#define FMO9 &FreeMonoOblique9pt7b +#define FMO12 &FreeMonoOblique12pt7b +#define FMO18 &FreeMonoOblique18pt7b +#define FMO24 &FreeMonoOblique24pt7b + +#define FMBO9 &FreeMonoBoldOblique9pt7b +#define FMBO12 &FreeMonoBoldOblique12pt7b +#define FMBO18 &FreeMonoBoldOblique18pt7b +#define FMBO24 &FreeMonoBoldOblique24pt7b + +#define FSS9 &FreeSans9pt7b +#define FSS12 &FreeSans12pt7b +#define FSS18 &FreeSans18pt7b +#define FSS24 &FreeSans24pt7b + +#define FSSB9 &FreeSansBold9pt7b +#define FSSB12 &FreeSansBold12pt7b +#define FSSB18 &FreeSansBold18pt7b +#define FSSB24 &FreeSansBold24pt7b + +#define FSSO9 &FreeSansOblique9pt7b +#define FSSO12 &FreeSansOblique12pt7b +#define FSSO18 &FreeSansOblique18pt7b +#define FSSO24 &FreeSansOblique24pt7b + +#define FSSBO9 &FreeSansBoldOblique9pt7b +#define FSSBO12 &FreeSansBoldOblique12pt7b +#define FSSBO18 &FreeSansBoldOblique18pt7b +#define FSSBO24 &FreeSansBoldOblique24pt7b + +#define FS9 &FreeSerif9pt7b +#define FS12 &FreeSerif12pt7b +#define FS18 &FreeSerif18pt7b +#define FS24 &FreeSerif24pt7b + +#define FSI9 &FreeSerifItalic9pt7b +#define FSI12 &FreeSerifItalic12pt7b +#define FSI19 &FreeSerifItalic18pt7b +#define FSI24 &FreeSerifItalic24pt7b + +#define FSB9 &FreeSerifBold9pt7b +#define FSB12 &FreeSerifBold12pt7b +#define FSB18 &FreeSerifBold18pt7b +#define FSB24 &FreeSerifBold24pt7b + +#define FSBI9 &FreeSerifBoldItalic9pt7b +#define FSBI12 &FreeSerifBoldItalic12pt7b +#define FSBI18 &FreeSerifBoldItalic18pt7b +#define FSBI24 &FreeSerifBoldItalic24pt7b + +#define FF0 NULL //ff0 reserved for GLCD +#define FF1 &FreeMono9pt7b +#define FF2 &FreeMono12pt7b +#define FF3 &FreeMono18pt7b +#define FF4 &FreeMono24pt7b + +#define FF5 &FreeMonoBold9pt7b +#define FF6 &FreeMonoBold12pt7b +#define FF7 &FreeMonoBold18pt7b +#define FF8 &FreeMonoBold24pt7b + +#define FF9 &FreeMonoOblique9pt7b +#define FF10 &FreeMonoOblique12pt7b +#define FF11 &FreeMonoOblique18pt7b +#define FF12 &FreeMonoOblique24pt7b + +#define FF13 &FreeMonoBoldOblique9pt7b +#define FF14 &FreeMonoBoldOblique12pt7b +#define FF15 &FreeMonoBoldOblique18pt7b +#define FF16 &FreeMonoBoldOblique24pt7b + +#define FF17 &FreeSans9pt7b +#define FF18 &FreeSans12pt7b +#define FF19 &FreeSans18pt7b +#define FF20 &FreeSans24pt7b + +#define FF21 &FreeSansBold9pt7b +#define FF22 &FreeSansBold12pt7b +#define FF23 &FreeSansBold18pt7b +#define FF24 &FreeSansBold24pt7b + +#define FF25 &FreeSansOblique9pt7b +#define FF26 &FreeSansOblique12pt7b +#define FF27 &FreeSansOblique18pt7b +#define FF28 &FreeSansOblique24pt7b + +#define FF29 &FreeSansBoldOblique9pt7b +#define FF30 &FreeSansBoldOblique12pt7b +#define FF31 &FreeSansBoldOblique18pt7b +#define FF32 &FreeSansBoldOblique24pt7b + +#define FF33 &FreeSerif9pt7b +#define FF34 &FreeSerif12pt7b +#define FF35 &FreeSerif18pt7b +#define FF36 &FreeSerif24pt7b + +#define FF37 &FreeSerifItalic9pt7b +#define FF38 &FreeSerifItalic12pt7b +#define FF39 &FreeSerifItalic18pt7b +#define FF40 &FreeSerifItalic24pt7b + +#define FF41 &FreeSerifBold9pt7b +#define FF42 &FreeSerifBold12pt7b +#define FF43 &FreeSerifBold18pt7b +#define FF44 &FreeSerifBold24pt7b + +#define FF45 &FreeSerifBoldItalic9pt7b +#define FF46 &FreeSerifBoldItalic12pt7b +#define FF47 &FreeSerifBoldItalic18pt7b +#define FF48 &FreeSerifBoldItalic24pt7b + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Now we define "s"tring versions for easy printing of the font name so: +// tft.println(sFF5); +// will print +// Mono bold 9 +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define sFF0 "GLCD" +#define sTT1 "Tom Thumb" +#define sFF1 "Mono 9" +#define sFF2 "Mono 12" +#define sFF3 "Mono 18" +#define sFF4 "Mono 24" + +#define sFF5 "Mono bold 9" +#define sFF6 "Mono bold 12" +#define sFF7 "Mono bold 18" +#define sFF8 "Mono bold 24" + +#define sFF9 "Mono oblique 9" +#define sFF10 "Mono oblique 12" +#define sFF11 "Mono oblique 18" +#define sFF12 "Mono oblique 24" + +#define sFF13 "Mono bold oblique 9" +#define sFF14 "Mono bold oblique 12" +#define sFF15 "Mono bold oblique 18" +#define sFF16 "Mono bold oblique 24" // Full text line is too big for 480 pixel wide screen + +#define sFF17 "Sans 9" +#define sFF18 "Sans 12" +#define sFF19 "Sans 18" +#define sFF20 "Sans 24" + +#define sFF21 "Sans bold 9" +#define sFF22 "Sans bold 12" +#define sFF23 "Sans bold 18" +#define sFF24 "Sans bold 24" + +#define sFF25 "Sans oblique 9" +#define sFF26 "Sans oblique 12" +#define sFF27 "Sans oblique 18" +#define sFF28 "Sans oblique 24" + +#define sFF29 "Sans bold oblique 9" +#define sFF30 "Sans bold oblique 12" +#define sFF31 "Sans bold oblique 18" +#define sFF32 "Sans bold oblique 24" + +#define sFF33 "Serif 9" +#define sFF34 "Serif 12" +#define sFF35 "Serif 18" +#define sFF36 "Serif 24" + +#define sFF37 "Serif italic 9" +#define sFF38 "Serif italic 12" +#define sFF39 "Serif italic 18" +#define sFF40 "Serif italic 24" + +#define sFF41 "Serif bold 9" +#define sFF42 "Serif bold 12" +#define sFF43 "Serif bold 18" +#define sFF44 "Serif bold 24" + +#define sFF45 "Serif bold italic 9" +#define sFF46 "Serif bold italic 12" +#define sFF47 "Serif bold italic 18" +#define sFF48 "Serif bold italic 24" + +#else // LOAD_tftFF not defined so setup defaults to prevent error messages + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Free fonts are not loaded in User_Setup.h so we must define all as font 1 +// to prevent compile error messages +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define GFXFF 1 +#define GLCD 1 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +#define FF0 1 +#define FF1 1 +#define FF2 1 +#define FF3 1 +#define FF4 1 +#define FF5 1 +#define FF6 1 +#define FF7 1 +#define FF8 1 +#define FF9 1 +#define FF10 1 +#define FF11 1 +#define FF12 1 +#define FF13 1 +#define FF14 1 +#define FF15 1 +#define FF16 1 +#define FF17 1 +#define FF18 1 +#define FF19 1 +#define FF20 1 +#define FF21 1 +#define FF22 1 +#define FF23 1 +#define FF24 1 +#define FF25 1 +#define FF26 1 +#define FF27 1 +#define FF28 1 +#define FF29 1 +#define FF30 1 +#define FF31 1 +#define FF32 1 +#define FF33 1 +#define FF34 1 +#define FF35 1 +#define FF36 1 +#define FF37 1 +#define FF38 1 +#define FF39 1 +#define FF40 1 +#define FF41 1 +#define FF42 1 +#define FF43 1 +#define FF44 1 +#define FF45 1 +#define FF46 1 +#define FF47 1 +#define FF48 1 + +#define FM9 1 +#define FM12 1 +#define FM18 1 +#define FM24 1 + +#define FMB9 1 +#define FMB12 1 +#define FMB18 1 +#define FMB24 1 + +#define FMO9 1 +#define FMO12 1 +#define FMO18 1 +#define FMO24 1 + +#define FMBO9 1 +#define FMBO12 1 +#define FMBO18 1 +#define FMBO24 1 + +#define FSS9 1 +#define FSS12 1 +#define FSS18 1 +#define FSS24 1 + +#define FSSB9 1 +#define FSSB12 1 +#define FSSB18 1 +#define FSSB24 1 + +#define FSSO9 1 +#define FSSO12 1 +#define FSSO18 1 +#define FSSO24 1 + +#define FSSBO9 1 +#define FSSBO12 1 +#define FSSBO18 1 +#define FSSBO24 1 + +#define FS9 1 +#define FS12 1 +#define FS18 1 +#define FS24 1 + +#define FSI9 1 +#define FSI12 1 +#define FSI19 1 +#define FSI24 1 + +#define FSB9 1 +#define FSB12 1 +#define FSB18 1 +#define FSB24 1 + +#define FSBI9 1 +#define FSBI12 1 +#define FSBI18 1 +#define FSBI24 1 + +#endif // LOAD_tftFF diff --git a/examples/Graphs/Graph_demo_1/Graph_demo_1.ino b/examples/Graphs/Graph_demo_1/Graph_demo_1.ino new file mode 100644 index 0000000..e3020c4 --- /dev/null +++ b/examples/Graphs/Graph_demo_1/Graph_demo_1.ino @@ -0,0 +1,80 @@ +// Demonstrate graph widget functions with a single trace instance +// One trace can be drawn at a time with one trace instance + +// Requires widget library here: +// https://github.com/Bodmer/TFT_eWidget + +#include +TFT_eSPI tft = TFT_eSPI(); + +#include // Widget library + +GraphWidget gr = GraphWidget(&tft); // Graph widget gr instance with pointer to tft +TraceWidget tr = TraceWidget(&gr); // Graph trace tr with pointer to gr + +const float gxLow = 0.0; +const float gxHigh = 100.0; +const float gyLow = -512.0; +const float gyHigh = 512.0; + +void setup() { + Serial.begin(115200); + + tft.begin(); + tft.setRotation(3); + tft.fillScreen(TFT_BLACK); + + // Graph area is 200 pixels wide, 150 pixels high, dark grey background + gr.createGraph(200, 150, tft.color565(5, 5, 5)); + + // x scale units is from 0 to 100, y scale units is -512 to 512 + gr.setGraphScale(gxLow, gxHigh, gyLow, gyHigh); + + // X grid starts at 0 with lines every 20 x-scale units + // Y grid starts at -512 with lines every 64 y-scale units + // blue grid + gr.setGraphGrid(gxLow, 20.0, gyLow, 64.0, TFT_BLUE); + + // Draw empty graph, top left corner at pixel coordinate 40,10 on TFT + gr.drawGraph(40, 10); + + // Start a trace with using red, trace points are in x and y scale units + // In this example a horizontal line is drawn + tr.startTrace(TFT_RED); + // Add a trace point at 0.0,0.0 on graph + tr.addPoint(0.0, 0.0); + // Add another point at 100.0, 0.0 this will be joined via line to the last point added + tr.addPoint(100.0, 0.0); + + // Start a new trace with using white + tr.startTrace(TFT_WHITE); +} + +void loop() { + static uint32_t plotTime = millis(); + static float gx = 0.0, gy = 0.0; + static float delta = 10.0; + + // Create a new plot point every 100ms + if (millis() - plotTime >= 100) { + plotTime = millis(); + + // Add a plot, first point in a trace will be a single pixel (if within graph area) + tr.addPoint(gx, gy); + gx += 1.0; + if (gy > 500.0) delta = -10.0; + if (gy < -500.0) delta = 10.0; + gy += delta; + + // If the end of the graph x ais is reached start a new trace at 0.0,0.0 + if (gx > gxHigh) { + gx = 0.0; + gy = 0.0; + + // Draw empty graph at 40,10 on display to clear old one + gr.drawGraph(40, 10); + // Start new trace + tr.startTrace(TFT_GREEN); + } + } +} diff --git a/examples/Graphs/Graph_demo_2/Graph_demo_2.ino b/examples/Graphs/Graph_demo_2/Graph_demo_2.ino new file mode 100644 index 0000000..ff540ca --- /dev/null +++ b/examples/Graphs/Graph_demo_2/Graph_demo_2.ino @@ -0,0 +1,105 @@ +// Demonstrate graph widget functions with two independant trace instances +// Multiple traces can be drawn at a time with multiple trace instances +// Note: Traces are automatically clipped at graph boundaries by widget library + +// Requires widget library here: +// https://github.com/Bodmer/TFT_eWidget + +#include +TFT_eSPI tft = TFT_eSPI(); + +#include // Widget library + +GraphWidget gr = GraphWidget(&tft); // Graph widget + +// Traces are drawn on tft using graph instance +TraceWidget tr1 = TraceWidget(&gr); // Graph trace 1 +TraceWidget tr2 = TraceWidget(&gr); // Graph trace 2 + +void setup() { + Serial.begin(115200); + delay(5000); + tft.begin(); + tft.setRotation(3); + tft.fillScreen(TFT_BLACK); + + // Graph area is 200 pixels wide, 150 high, dark grey background + gr.createGraph(200, 150, tft.color565(5, 5, 5)); + + // x scale units is from 0 to 100, y scale units is -50 to 50 + gr.setGraphScale(0.0, 100.0, -50.0, 50.0); + + // X grid starts at 0 with lines every 10 x-scale units + // Y grid starts at -50 with lines every 25 y-scale units + // blue grid + gr.setGraphGrid(0.0, 10.0, -50.0, 25.0, TFT_BLUE); + + // Draw empty graph, top left corner at 40,10 on TFT + gr.drawGraph(40, 10); + + // Start a trace with using red and another with green + tr1.startTrace(TFT_RED); + tr2.startTrace(TFT_GREEN); + + // Add points on graph to trace 1 using graph scale factors + tr1.addPoint(0.0, 0.0); + tr1.addPoint(100.0, 0.0); + + // Add points on graph to trace 2 using graph scale factors + // Points are off graph so the plotted line is clipped to graph area + tr2.addPoint(0.0, -100.0); + tr2.addPoint(100.0, 100.0); + + // Get x,y pixel coordinates of any scaled point on graph + // and ring that point. + tft.drawCircle(gr.getPointX(50.0), gr.getPointY(0.0), 5, TFT_MAGENTA); + + // Draw the x axis scale + tft.setTextDatum(TC_DATUM); // Top centre text datum + tft.drawNumber(0, gr.getPointX(0.0), gr.getPointY(-50.0) + 3); + tft.drawNumber(50, gr.getPointX(50.0), gr.getPointY(-50.0) + 3); + tft.drawNumber(100, gr.getPointX(100.0), gr.getPointY(-50.0) + 3); + + // Draw the y axis scale + tft.setTextDatum(MR_DATUM); // Middle right text datum + tft.drawNumber(-50, gr.getPointX(0.0), gr.getPointY(-50.0)); + tft.drawNumber(0, gr.getPointX(0.0), gr.getPointY(0.0)); + tft.drawNumber(50, gr.getPointX(0.0), gr.getPointY(50.0)); + + // Restart traces with new colours + tr1.startTrace(TFT_WHITE); + tr2.startTrace(TFT_YELLOW); +} + +void loop() { + static uint32_t plotTime = millis(); + static float gx = 0.0, gy = 0.0; + static float delta = 7.0; + + // Sample periodically + if (millis() - plotTime >= 100) { + plotTime = millis(); + + // Add a new point on each trace + tr1.addPoint(gx, gy); + tr2.addPoint(gx, gy/2.0); // half y amplitude + + // Create next plot point + gx += 1.0; + gy += delta; + if (gy > 70.0) { delta = -7.0; gy = 70.0; } + if (gy < -70.0) { delta = 7.0; gy = -70.0; } + + // If the end of the graph is reached start 2 new traces + if (gx > 100.0) { + gx = 0.0; + gy = 0.0; + + // Draw empty graph at 40,10 on display + gr.drawGraph(40, 10); + // Start new trace + tr1.startTrace(TFT_GREEN); + tr2.startTrace(TFT_YELLOW); + } + } +} diff --git a/examples/Graphs/Reflow_graph_demo/Free_Fonts.h b/examples/Graphs/Reflow_graph_demo/Free_Fonts.h new file mode 100644 index 0000000..77249ef --- /dev/null +++ b/examples/Graphs/Reflow_graph_demo/Free_Fonts.h @@ -0,0 +1,377 @@ +// Attach this header file to your sketch to use the GFX Free Fonts. You can write +// sketches without it, but it makes referencing them easier. + +// This calls up ALL the fonts but they only get loaded if you actually +// use them in your sketch. +// +// No changes are needed to this header file unless new fonts are added to the +// library "Fonts/GFXFF" folder. +// +// To save a lot of typing long names, each font can easily be referenced in the +// sketch in three ways, either with: +// +// 1. Font file name with the & in front such as &FreeSansBoldOblique24pt7b +// an example being: +// +// tft.setFreeFont(&FreeSansBoldOblique24pt7b); +// +// 2. FF# where # is a number determined by looking at the list below +// an example being: +// +// tft.setFreeFont(FF32); +// +// 3. An abbreviation of the file name. Look at the list below to see +// the abbreviations used, for example: +// +// tft.setFreeFont(FSSBO24) +// +// Where the letters mean: +// F = Free font +// M = Mono +// SS = Sans Serif (double S to distinguish is form serif fonts) +// S = Serif +// B = Bold +// O = Oblique (letter O not zero) +// I = Italic +// # = point size, either 9, 12, 18 or 24 +// +// Setting the font to NULL will select the GLCD font: +// +// tft.setFreeFont(NULL); // Set font to GLCD + +#define LOAD_tftFF + +#ifdef LOAD_tftFF // Only include the fonts if LOAD_tftFF is defined in User_Setup.h + +// Use these when printing or drawing text in GLCD and high rendering speed fonts +#define GFXFF 1 +#define GLCD 0 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +// Use the following when calling setFont() +// +// Reserved for GLCD font // FF0 +// + +#define TT1 &TomThumb + +#define FM9 &FreeMono9pt7b +#define FM12 &FreeMono12pt7b +#define FM18 &FreeMono18pt7b +#define FM24 &FreeMono24pt7b + +#define FMB9 &FreeMonoBold9pt7b +#define FMB12 &FreeMonoBold12pt7b +#define FMB18 &FreeMonoBold18pt7b +#define FMB24 &FreeMonoBold24pt7b + +#define FMO9 &FreeMonoOblique9pt7b +#define FMO12 &FreeMonoOblique12pt7b +#define FMO18 &FreeMonoOblique18pt7b +#define FMO24 &FreeMonoOblique24pt7b + +#define FMBO9 &FreeMonoBoldOblique9pt7b +#define FMBO12 &FreeMonoBoldOblique12pt7b +#define FMBO18 &FreeMonoBoldOblique18pt7b +#define FMBO24 &FreeMonoBoldOblique24pt7b + +#define FSS9 &FreeSans9pt7b +#define FSS12 &FreeSans12pt7b +#define FSS18 &FreeSans18pt7b +#define FSS24 &FreeSans24pt7b + +#define FSSB9 &FreeSansBold9pt7b +#define FSSB12 &FreeSansBold12pt7b +#define FSSB18 &FreeSansBold18pt7b +#define FSSB24 &FreeSansBold24pt7b + +#define FSSO9 &FreeSansOblique9pt7b +#define FSSO12 &FreeSansOblique12pt7b +#define FSSO18 &FreeSansOblique18pt7b +#define FSSO24 &FreeSansOblique24pt7b + +#define FSSBO9 &FreeSansBoldOblique9pt7b +#define FSSBO12 &FreeSansBoldOblique12pt7b +#define FSSBO18 &FreeSansBoldOblique18pt7b +#define FSSBO24 &FreeSansBoldOblique24pt7b + +#define FS9 &FreeSerif9pt7b +#define FS12 &FreeSerif12pt7b +#define FS18 &FreeSerif18pt7b +#define FS24 &FreeSerif24pt7b + +#define FSI9 &FreeSerifItalic9pt7b +#define FSI12 &FreeSerifItalic12pt7b +#define FSI19 &FreeSerifItalic18pt7b +#define FSI24 &FreeSerifItalic24pt7b + +#define FSB9 &FreeSerifBold9pt7b +#define FSB12 &FreeSerifBold12pt7b +#define FSB18 &FreeSerifBold18pt7b +#define FSB24 &FreeSerifBold24pt7b + +#define FSBI9 &FreeSerifBoldItalic9pt7b +#define FSBI12 &FreeSerifBoldItalic12pt7b +#define FSBI18 &FreeSerifBoldItalic18pt7b +#define FSBI24 &FreeSerifBoldItalic24pt7b + +#define FF0 NULL //ff0 reserved for GLCD +#define FF1 &FreeMono9pt7b +#define FF2 &FreeMono12pt7b +#define FF3 &FreeMono18pt7b +#define FF4 &FreeMono24pt7b + +#define FF5 &FreeMonoBold9pt7b +#define FF6 &FreeMonoBold12pt7b +#define FF7 &FreeMonoBold18pt7b +#define FF8 &FreeMonoBold24pt7b + +#define FF9 &FreeMonoOblique9pt7b +#define FF10 &FreeMonoOblique12pt7b +#define FF11 &FreeMonoOblique18pt7b +#define FF12 &FreeMonoOblique24pt7b + +#define FF13 &FreeMonoBoldOblique9pt7b +#define FF14 &FreeMonoBoldOblique12pt7b +#define FF15 &FreeMonoBoldOblique18pt7b +#define FF16 &FreeMonoBoldOblique24pt7b + +#define FF17 &FreeSans9pt7b +#define FF18 &FreeSans12pt7b +#define FF19 &FreeSans18pt7b +#define FF20 &FreeSans24pt7b + +#define FF21 &FreeSansBold9pt7b +#define FF22 &FreeSansBold12pt7b +#define FF23 &FreeSansBold18pt7b +#define FF24 &FreeSansBold24pt7b + +#define FF25 &FreeSansOblique9pt7b +#define FF26 &FreeSansOblique12pt7b +#define FF27 &FreeSansOblique18pt7b +#define FF28 &FreeSansOblique24pt7b + +#define FF29 &FreeSansBoldOblique9pt7b +#define FF30 &FreeSansBoldOblique12pt7b +#define FF31 &FreeSansBoldOblique18pt7b +#define FF32 &FreeSansBoldOblique24pt7b + +#define FF33 &FreeSerif9pt7b +#define FF34 &FreeSerif12pt7b +#define FF35 &FreeSerif18pt7b +#define FF36 &FreeSerif24pt7b + +#define FF37 &FreeSerifItalic9pt7b +#define FF38 &FreeSerifItalic12pt7b +#define FF39 &FreeSerifItalic18pt7b +#define FF40 &FreeSerifItalic24pt7b + +#define FF41 &FreeSerifBold9pt7b +#define FF42 &FreeSerifBold12pt7b +#define FF43 &FreeSerifBold18pt7b +#define FF44 &FreeSerifBold24pt7b + +#define FF45 &FreeSerifBoldItalic9pt7b +#define FF46 &FreeSerifBoldItalic12pt7b +#define FF47 &FreeSerifBoldItalic18pt7b +#define FF48 &FreeSerifBoldItalic24pt7b + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Now we define "s"tring versions for easy printing of the font name so: +// tft.println(sFF5); +// will print +// Mono bold 9 +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define sFF0 "GLCD" +#define sTT1 "Tom Thumb" +#define sFF1 "Mono 9" +#define sFF2 "Mono 12" +#define sFF3 "Mono 18" +#define sFF4 "Mono 24" + +#define sFF5 "Mono bold 9" +#define sFF6 "Mono bold 12" +#define sFF7 "Mono bold 18" +#define sFF8 "Mono bold 24" + +#define sFF9 "Mono oblique 9" +#define sFF10 "Mono oblique 12" +#define sFF11 "Mono oblique 18" +#define sFF12 "Mono oblique 24" + +#define sFF13 "Mono bold oblique 9" +#define sFF14 "Mono bold oblique 12" +#define sFF15 "Mono bold oblique 18" +#define sFF16 "Mono bold oblique 24" // Full text line is too big for 480 pixel wide screen + +#define sFF17 "Sans 9" +#define sFF18 "Sans 12" +#define sFF19 "Sans 18" +#define sFF20 "Sans 24" + +#define sFF21 "Sans bold 9" +#define sFF22 "Sans bold 12" +#define sFF23 "Sans bold 18" +#define sFF24 "Sans bold 24" + +#define sFF25 "Sans oblique 9" +#define sFF26 "Sans oblique 12" +#define sFF27 "Sans oblique 18" +#define sFF28 "Sans oblique 24" + +#define sFF29 "Sans bold oblique 9" +#define sFF30 "Sans bold oblique 12" +#define sFF31 "Sans bold oblique 18" +#define sFF32 "Sans bold oblique 24" + +#define sFF33 "Serif 9" +#define sFF34 "Serif 12" +#define sFF35 "Serif 18" +#define sFF36 "Serif 24" + +#define sFF37 "Serif italic 9" +#define sFF38 "Serif italic 12" +#define sFF39 "Serif italic 18" +#define sFF40 "Serif italic 24" + +#define sFF41 "Serif bold 9" +#define sFF42 "Serif bold 12" +#define sFF43 "Serif bold 18" +#define sFF44 "Serif bold 24" + +#define sFF45 "Serif bold italic 9" +#define sFF46 "Serif bold italic 12" +#define sFF47 "Serif bold italic 18" +#define sFF48 "Serif bold italic 24" + +#else // LOAD_tftFF not defined so setup defaults to prevent error messages + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Free fonts are not loaded in User_Setup.h so we must define all as font 1 +// to prevent compile error messages +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define GFXFF 1 +#define GLCD 1 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +#define FF0 1 +#define FF1 1 +#define FF2 1 +#define FF3 1 +#define FF4 1 +#define FF5 1 +#define FF6 1 +#define FF7 1 +#define FF8 1 +#define FF9 1 +#define FF10 1 +#define FF11 1 +#define FF12 1 +#define FF13 1 +#define FF14 1 +#define FF15 1 +#define FF16 1 +#define FF17 1 +#define FF18 1 +#define FF19 1 +#define FF20 1 +#define FF21 1 +#define FF22 1 +#define FF23 1 +#define FF24 1 +#define FF25 1 +#define FF26 1 +#define FF27 1 +#define FF28 1 +#define FF29 1 +#define FF30 1 +#define FF31 1 +#define FF32 1 +#define FF33 1 +#define FF34 1 +#define FF35 1 +#define FF36 1 +#define FF37 1 +#define FF38 1 +#define FF39 1 +#define FF40 1 +#define FF41 1 +#define FF42 1 +#define FF43 1 +#define FF44 1 +#define FF45 1 +#define FF46 1 +#define FF47 1 +#define FF48 1 + +#define FM9 1 +#define FM12 1 +#define FM18 1 +#define FM24 1 + +#define FMB9 1 +#define FMB12 1 +#define FMB18 1 +#define FMB24 1 + +#define FMO9 1 +#define FMO12 1 +#define FMO18 1 +#define FMO24 1 + +#define FMBO9 1 +#define FMBO12 1 +#define FMBO18 1 +#define FMBO24 1 + +#define FSS9 1 +#define FSS12 1 +#define FSS18 1 +#define FSS24 1 + +#define FSSB9 1 +#define FSSB12 1 +#define FSSB18 1 +#define FSSB24 1 + +#define FSSO9 1 +#define FSSO12 1 +#define FSSO18 1 +#define FSSO24 1 + +#define FSSBO9 1 +#define FSSBO12 1 +#define FSSBO18 1 +#define FSSBO24 1 + +#define FS9 1 +#define FS12 1 +#define FS18 1 +#define FS24 1 + +#define FSI9 1 +#define FSI12 1 +#define FSI19 1 +#define FSI24 1 + +#define FSB9 1 +#define FSB12 1 +#define FSB18 1 +#define FSB24 1 + +#define FSBI9 1 +#define FSBI12 1 +#define FSBI18 1 +#define FSBI24 1 + +#endif // LOAD_tftFF diff --git a/examples/Graphs/Reflow_graph_demo/Reflow_graph_demo.ino b/examples/Graphs/Reflow_graph_demo/Reflow_graph_demo.ino new file mode 100644 index 0000000..e2aa5b2 --- /dev/null +++ b/examples/Graphs/Reflow_graph_demo/Reflow_graph_demo.ino @@ -0,0 +1,117 @@ +// Monitors a thermocuple connected to SPI MAX31855 device + +// Requires widget library here: +// https://github.com/Bodmer/TFT_eWidget + +#include +#include "Free_Fonts.h" + +#include + +TFT_eSPI tft = TFT_eSPI(); + +// Chip select for default SPI bus +#define MAXCS 21 + +Adafruit_MAX31855 thermocouple(MAXCS); + +#include // Widget library +GraphWidget gr = GraphWidget(&tft); // Graph widget +TraceWidget tr = TraceWidget(&gr); // Graph trace tr with pointer to gr + +void setup() { + Serial.begin(115200); + + tft.begin(); + tft.setRotation(3); + tft.fillScreen(TFT_BLACK); + tft.setTextColor(TFT_WHITE, tft.color565(5, 5, 5)); + tft.setFreeFont(FF18); + tft.setTextPadding(tft.textWidth("888")); + + gr.createGraph(200, 150, tft.color565(5, 5, 5)); + gr.setGraphGrid(0.0, 60.0, 25.0, 25.0, tft.color565(15, 15, 15)); + gr.setGraphScale(0.0, 300.0, 25.0, 225.0); + gr.drawGraph(40, 40); + + // Plot the solder melt temperature line (183 deg. C) + tr.startTrace(TFT_RED); + tr.addPoint(0.0, 183.0); + tr.addPoint(300.0, 183.0); + + // Plot the desired temperature profile + tr.startTrace(tft.color565(100, 100, 100)); + tr.addPoint(0.0, 25.0); + tr.addPoint(50.0, 120.0); + tr.addPoint(140.0, 170.0); + tr.addPoint(200.0, 225.0); + tr.addPoint(220.0, 225.0); + tr.addPoint(260.0, 150.0); + + tr.startTrace(TFT_WHITE); + + // wait for MAX chip to stabilize + delay(500); + + Serial.print("Initializing sensor..."); + if (!thermocouple.begin()) { + Serial.println("ERROR."); + while (1) delay(10); + } + Serial.println("DONE."); + + float c = thermocouple.readCelsius(); + if (isnan(c)) { + Serial.println("Something wrong with thermocouple!"); + } +} + +void loop() { + static uint32_t scanTime = millis(); + static float gx = 0.0, gy = 25.0; + static bool cool = false; + static uint16_t cnt = 0; + + // Sample periodically + if (millis() - scanTime >= 1000) { + float c = thermocouple.readCelsius(); + tft.drawNumber(c + 0.5, 40, 40); + tr.addPoint(gx, c); + gx += 1.0; + + if (gy > 225.0) { + gy = 224.0; + cool = true; + cnt = 0; + } + + if (cool) { + if (cnt > 20) gy -= 1.5; + cnt++; + if (gy < 25.0) { + cool = false; + gr.drawGraph(40, 40); + tr.startTrace(TFT_RED); + tr.addPoint(0.0, 183.0); + tr.addPoint(300.0, 183.0); + + tr.startTrace(tft.color565(100, 100, 100)); + tr.addPoint(0.0, 25.0); + tr.addPoint(50.0, 120.0); + tr.addPoint(140.0, 170.0); + tr.addPoint(200.0, 225.0); + tr.addPoint(220.0, 225.0); + tr.addPoint(260.0, 150.0); + + tr.startTrace(TFT_GREEN); + gx = 0.0; + } + } + else { + if (gy < 120 || gy > 170.0) gy += 1.5; + else gy += 0.40; + } + + scanTime = millis(); + } +} diff --git a/examples/Meters/Analogue_meters/Analogue_meters.ino b/examples/Meters/Analogue_meters/Analogue_meters.ino new file mode 100644 index 0000000..dcc3c3c --- /dev/null +++ b/examples/Meters/Analogue_meters/Analogue_meters.ino @@ -0,0 +1,82 @@ +/* + Example animated analogue meters + + Needs Font 2 (also Font 4 if using large scale label) + + Make sure all the display driver and pin connections are correct by + editing the User_Setup.h file in the TFT_eSPI library folder. + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### + + Requires widget library here: + https://github.com/Bodmer/TFT_eWidget +*/ + +#include // Hardware-specific library +#include // Widget library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +MeterWidget amps = MeterWidget(&tft); +MeterWidget volts = MeterWidget(&tft); +MeterWidget ohms = MeterWidget(&tft); + +#define LOOP_PERIOD 35 // Display updates every 35 ms + +void setup(void) +{ + tft.init(); + tft.setRotation(0); + Serial.begin(115200); // For debug + + + // Colour zones are set as a start and end percentage of full scale (0-100) + // If start and end of a colour zone are the same then that colour is not used + // --Red-- -Org- -Yell- -Grn- + amps.setZones(75, 100, 50, 75, 25, 50, 0, 25); // Example here red starts at 75% and ends at 100% of full scale + // Meter is 239 pixels wide and 126 pixels high + amps.analogMeter(0, 0, 2.0, "mA", "0", "0.5", "1.0", "1.5", "2.0"); // Draw analogue meter at 0, 0 + + // Colour draw order is red, orange, yellow, green. So red can be full scale with green drawn + // last on top to indicate a "safe" zone. + // -Red- -Org- -Yell- -Grn- + volts.setZones(0, 100, 25, 75, 0, 0, 40, 60); + volts.analogMeter(0, 128, 10.0, "V", "0", "2.5", "5", "7.5", "10"); // Draw analogue meter at 0, 128 + + // No coloured zones if not defined + ohms.analogMeter(0, 256, 100, "R", "0", "", "50", "", "100"); // Draw analogue meter at 0, 128 +} + + +void loop() +{ + static int d = 0; + static uint32_t updateTime = 0; + + if (millis() - updateTime >= LOOP_PERIOD) + { + updateTime = millis(); + + d += 4; if (d > 360) d = 0; + + // Create a Sine wave for testing, value is in range 0 - 100 + float value = 50.0 + 50.0 * sin((d + 0) * 0.0174532925); + + float current; + mapValue(value, current, (float)0.0, (float)100.0, (float)0.0, (float)2.0); + //Serial.print("I = "); Serial.print(current); + amps.updateNeedle(current, 0); + + float voltage; + mapValue(value, voltage, (float)0.0, (float)100.0, (float)0.0, (float)10.0); + //Serial.print(", V = "); Serial.println(voltage); + volts.updateNeedle(voltage, 0); + + float resistance; + mapValue(value, resistance, (float)0.0, (float)100.0, (float)0.0, (float)100.0); + //Serial.print(", R = "); Serial.println(resistance); + ohms.updateNeedle(resistance, 0); + } +} diff --git a/examples/Sliders/Slider_demo/Free_Fonts.h b/examples/Sliders/Slider_demo/Free_Fonts.h new file mode 100644 index 0000000..77249ef --- /dev/null +++ b/examples/Sliders/Slider_demo/Free_Fonts.h @@ -0,0 +1,377 @@ +// Attach this header file to your sketch to use the GFX Free Fonts. You can write +// sketches without it, but it makes referencing them easier. + +// This calls up ALL the fonts but they only get loaded if you actually +// use them in your sketch. +// +// No changes are needed to this header file unless new fonts are added to the +// library "Fonts/GFXFF" folder. +// +// To save a lot of typing long names, each font can easily be referenced in the +// sketch in three ways, either with: +// +// 1. Font file name with the & in front such as &FreeSansBoldOblique24pt7b +// an example being: +// +// tft.setFreeFont(&FreeSansBoldOblique24pt7b); +// +// 2. FF# where # is a number determined by looking at the list below +// an example being: +// +// tft.setFreeFont(FF32); +// +// 3. An abbreviation of the file name. Look at the list below to see +// the abbreviations used, for example: +// +// tft.setFreeFont(FSSBO24) +// +// Where the letters mean: +// F = Free font +// M = Mono +// SS = Sans Serif (double S to distinguish is form serif fonts) +// S = Serif +// B = Bold +// O = Oblique (letter O not zero) +// I = Italic +// # = point size, either 9, 12, 18 or 24 +// +// Setting the font to NULL will select the GLCD font: +// +// tft.setFreeFont(NULL); // Set font to GLCD + +#define LOAD_tftFF + +#ifdef LOAD_tftFF // Only include the fonts if LOAD_tftFF is defined in User_Setup.h + +// Use these when printing or drawing text in GLCD and high rendering speed fonts +#define GFXFF 1 +#define GLCD 0 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +// Use the following when calling setFont() +// +// Reserved for GLCD font // FF0 +// + +#define TT1 &TomThumb + +#define FM9 &FreeMono9pt7b +#define FM12 &FreeMono12pt7b +#define FM18 &FreeMono18pt7b +#define FM24 &FreeMono24pt7b + +#define FMB9 &FreeMonoBold9pt7b +#define FMB12 &FreeMonoBold12pt7b +#define FMB18 &FreeMonoBold18pt7b +#define FMB24 &FreeMonoBold24pt7b + +#define FMO9 &FreeMonoOblique9pt7b +#define FMO12 &FreeMonoOblique12pt7b +#define FMO18 &FreeMonoOblique18pt7b +#define FMO24 &FreeMonoOblique24pt7b + +#define FMBO9 &FreeMonoBoldOblique9pt7b +#define FMBO12 &FreeMonoBoldOblique12pt7b +#define FMBO18 &FreeMonoBoldOblique18pt7b +#define FMBO24 &FreeMonoBoldOblique24pt7b + +#define FSS9 &FreeSans9pt7b +#define FSS12 &FreeSans12pt7b +#define FSS18 &FreeSans18pt7b +#define FSS24 &FreeSans24pt7b + +#define FSSB9 &FreeSansBold9pt7b +#define FSSB12 &FreeSansBold12pt7b +#define FSSB18 &FreeSansBold18pt7b +#define FSSB24 &FreeSansBold24pt7b + +#define FSSO9 &FreeSansOblique9pt7b +#define FSSO12 &FreeSansOblique12pt7b +#define FSSO18 &FreeSansOblique18pt7b +#define FSSO24 &FreeSansOblique24pt7b + +#define FSSBO9 &FreeSansBoldOblique9pt7b +#define FSSBO12 &FreeSansBoldOblique12pt7b +#define FSSBO18 &FreeSansBoldOblique18pt7b +#define FSSBO24 &FreeSansBoldOblique24pt7b + +#define FS9 &FreeSerif9pt7b +#define FS12 &FreeSerif12pt7b +#define FS18 &FreeSerif18pt7b +#define FS24 &FreeSerif24pt7b + +#define FSI9 &FreeSerifItalic9pt7b +#define FSI12 &FreeSerifItalic12pt7b +#define FSI19 &FreeSerifItalic18pt7b +#define FSI24 &FreeSerifItalic24pt7b + +#define FSB9 &FreeSerifBold9pt7b +#define FSB12 &FreeSerifBold12pt7b +#define FSB18 &FreeSerifBold18pt7b +#define FSB24 &FreeSerifBold24pt7b + +#define FSBI9 &FreeSerifBoldItalic9pt7b +#define FSBI12 &FreeSerifBoldItalic12pt7b +#define FSBI18 &FreeSerifBoldItalic18pt7b +#define FSBI24 &FreeSerifBoldItalic24pt7b + +#define FF0 NULL //ff0 reserved for GLCD +#define FF1 &FreeMono9pt7b +#define FF2 &FreeMono12pt7b +#define FF3 &FreeMono18pt7b +#define FF4 &FreeMono24pt7b + +#define FF5 &FreeMonoBold9pt7b +#define FF6 &FreeMonoBold12pt7b +#define FF7 &FreeMonoBold18pt7b +#define FF8 &FreeMonoBold24pt7b + +#define FF9 &FreeMonoOblique9pt7b +#define FF10 &FreeMonoOblique12pt7b +#define FF11 &FreeMonoOblique18pt7b +#define FF12 &FreeMonoOblique24pt7b + +#define FF13 &FreeMonoBoldOblique9pt7b +#define FF14 &FreeMonoBoldOblique12pt7b +#define FF15 &FreeMonoBoldOblique18pt7b +#define FF16 &FreeMonoBoldOblique24pt7b + +#define FF17 &FreeSans9pt7b +#define FF18 &FreeSans12pt7b +#define FF19 &FreeSans18pt7b +#define FF20 &FreeSans24pt7b + +#define FF21 &FreeSansBold9pt7b +#define FF22 &FreeSansBold12pt7b +#define FF23 &FreeSansBold18pt7b +#define FF24 &FreeSansBold24pt7b + +#define FF25 &FreeSansOblique9pt7b +#define FF26 &FreeSansOblique12pt7b +#define FF27 &FreeSansOblique18pt7b +#define FF28 &FreeSansOblique24pt7b + +#define FF29 &FreeSansBoldOblique9pt7b +#define FF30 &FreeSansBoldOblique12pt7b +#define FF31 &FreeSansBoldOblique18pt7b +#define FF32 &FreeSansBoldOblique24pt7b + +#define FF33 &FreeSerif9pt7b +#define FF34 &FreeSerif12pt7b +#define FF35 &FreeSerif18pt7b +#define FF36 &FreeSerif24pt7b + +#define FF37 &FreeSerifItalic9pt7b +#define FF38 &FreeSerifItalic12pt7b +#define FF39 &FreeSerifItalic18pt7b +#define FF40 &FreeSerifItalic24pt7b + +#define FF41 &FreeSerifBold9pt7b +#define FF42 &FreeSerifBold12pt7b +#define FF43 &FreeSerifBold18pt7b +#define FF44 &FreeSerifBold24pt7b + +#define FF45 &FreeSerifBoldItalic9pt7b +#define FF46 &FreeSerifBoldItalic12pt7b +#define FF47 &FreeSerifBoldItalic18pt7b +#define FF48 &FreeSerifBoldItalic24pt7b + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Now we define "s"tring versions for easy printing of the font name so: +// tft.println(sFF5); +// will print +// Mono bold 9 +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define sFF0 "GLCD" +#define sTT1 "Tom Thumb" +#define sFF1 "Mono 9" +#define sFF2 "Mono 12" +#define sFF3 "Mono 18" +#define sFF4 "Mono 24" + +#define sFF5 "Mono bold 9" +#define sFF6 "Mono bold 12" +#define sFF7 "Mono bold 18" +#define sFF8 "Mono bold 24" + +#define sFF9 "Mono oblique 9" +#define sFF10 "Mono oblique 12" +#define sFF11 "Mono oblique 18" +#define sFF12 "Mono oblique 24" + +#define sFF13 "Mono bold oblique 9" +#define sFF14 "Mono bold oblique 12" +#define sFF15 "Mono bold oblique 18" +#define sFF16 "Mono bold oblique 24" // Full text line is too big for 480 pixel wide screen + +#define sFF17 "Sans 9" +#define sFF18 "Sans 12" +#define sFF19 "Sans 18" +#define sFF20 "Sans 24" + +#define sFF21 "Sans bold 9" +#define sFF22 "Sans bold 12" +#define sFF23 "Sans bold 18" +#define sFF24 "Sans bold 24" + +#define sFF25 "Sans oblique 9" +#define sFF26 "Sans oblique 12" +#define sFF27 "Sans oblique 18" +#define sFF28 "Sans oblique 24" + +#define sFF29 "Sans bold oblique 9" +#define sFF30 "Sans bold oblique 12" +#define sFF31 "Sans bold oblique 18" +#define sFF32 "Sans bold oblique 24" + +#define sFF33 "Serif 9" +#define sFF34 "Serif 12" +#define sFF35 "Serif 18" +#define sFF36 "Serif 24" + +#define sFF37 "Serif italic 9" +#define sFF38 "Serif italic 12" +#define sFF39 "Serif italic 18" +#define sFF40 "Serif italic 24" + +#define sFF41 "Serif bold 9" +#define sFF42 "Serif bold 12" +#define sFF43 "Serif bold 18" +#define sFF44 "Serif bold 24" + +#define sFF45 "Serif bold italic 9" +#define sFF46 "Serif bold italic 12" +#define sFF47 "Serif bold italic 18" +#define sFF48 "Serif bold italic 24" + +#else // LOAD_tftFF not defined so setup defaults to prevent error messages + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Free fonts are not loaded in User_Setup.h so we must define all as font 1 +// to prevent compile error messages +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define GFXFF 1 +#define GLCD 1 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +#define FF0 1 +#define FF1 1 +#define FF2 1 +#define FF3 1 +#define FF4 1 +#define FF5 1 +#define FF6 1 +#define FF7 1 +#define FF8 1 +#define FF9 1 +#define FF10 1 +#define FF11 1 +#define FF12 1 +#define FF13 1 +#define FF14 1 +#define FF15 1 +#define FF16 1 +#define FF17 1 +#define FF18 1 +#define FF19 1 +#define FF20 1 +#define FF21 1 +#define FF22 1 +#define FF23 1 +#define FF24 1 +#define FF25 1 +#define FF26 1 +#define FF27 1 +#define FF28 1 +#define FF29 1 +#define FF30 1 +#define FF31 1 +#define FF32 1 +#define FF33 1 +#define FF34 1 +#define FF35 1 +#define FF36 1 +#define FF37 1 +#define FF38 1 +#define FF39 1 +#define FF40 1 +#define FF41 1 +#define FF42 1 +#define FF43 1 +#define FF44 1 +#define FF45 1 +#define FF46 1 +#define FF47 1 +#define FF48 1 + +#define FM9 1 +#define FM12 1 +#define FM18 1 +#define FM24 1 + +#define FMB9 1 +#define FMB12 1 +#define FMB18 1 +#define FMB24 1 + +#define FMO9 1 +#define FMO12 1 +#define FMO18 1 +#define FMO24 1 + +#define FMBO9 1 +#define FMBO12 1 +#define FMBO18 1 +#define FMBO24 1 + +#define FSS9 1 +#define FSS12 1 +#define FSS18 1 +#define FSS24 1 + +#define FSSB9 1 +#define FSSB12 1 +#define FSSB18 1 +#define FSSB24 1 + +#define FSSO9 1 +#define FSSO12 1 +#define FSSO18 1 +#define FSSO24 1 + +#define FSSBO9 1 +#define FSSBO12 1 +#define FSSBO18 1 +#define FSSBO24 1 + +#define FS9 1 +#define FS12 1 +#define FS18 1 +#define FS24 1 + +#define FSI9 1 +#define FSI12 1 +#define FSI19 1 +#define FSI24 1 + +#define FSB9 1 +#define FSB12 1 +#define FSB18 1 +#define FSB24 1 + +#define FSBI9 1 +#define FSBI12 1 +#define FSBI18 1 +#define FSBI24 1 + +#endif // LOAD_tftFF diff --git a/examples/Sliders/Slider_demo/Slider_demo.ino b/examples/Sliders/Slider_demo/Slider_demo.ino new file mode 100644 index 0000000..5b6147e --- /dev/null +++ b/examples/Sliders/Slider_demo/Slider_demo.ino @@ -0,0 +1,201 @@ +// Slider widget demo, requires display with touch screen + +// Requires widget library here: +// https://github.com/Bodmer/TFT_eWidget + +#include "FS.h" +#include +#include "Free_Fonts.h" // Include the header file attached to this sketch + +#include +#include // Widget library + +TFT_eSPI tft = TFT_eSPI(); +TFT_eSprite knob = TFT_eSprite(&tft); // Sprite for the slide knob + +#define CALIBRATION_FILE "/TouchCalData1" +#define REPEAT_CAL false + +SliderWidget s1 = SliderWidget(&tft, &knob); // Slider 1 widget +SliderWidget s2 = SliderWidget(&tft, &knob); // Slider 2 widget + + +void setup() { + Serial.begin(115200); + tft.begin(); + tft.setRotation(0); + tft.fillScreen(TFT_BLACK); + tft.setFreeFont(FF18); + + // Calibrate the touch screen and retrieve the scaling factors + if (REPEAT_CAL) { + touch_calibrate(); + tft.fillScreen(TFT_BLACK); + } + + // Create a parameter set for the slider + slider_t param; + + // Slider slot parameters + param.slotWidth = 9; // Note: ends of slot will be rounded and anti-aliased + param.slotLength = 200; // Length includes rounded ends + param.slotColor = TFT_BLUE; // Slot colour + param.slotBgColor = TFT_BLACK; // Slot background colour for anti-aliasing + param.orientation = H_SLIDER; // sets it "true" for horizontal + + // Slider control knob parameters (smooth rounded rectangle) + param.knobWidth = 15; // Always along x axis + param.knobHeight = 25; // Always along y axis + param.knobRadius = 5; // Corner radius + param.knobColor = TFT_WHITE; // Anti-aliased with slot backgound colour + param.knobLineColor = TFT_RED; // Colour of marker line (set to same as knobColor for no line) + + // Slider range and movement speed + param.sliderLT = 0; // Left side for horizontal, top for vertical slider + param.sliderRB = 100; // Right side for horizontal, bottom for vertical slider + param.startPosition = 50; // Start position for control knob + param.sliderDelay = 0; // Microseconds per pixel movement delay (0 = no delay) + + // Create slider using parameters and plot at 0,0 + s1.drawSlider(0, 0, param); + + // Show bounding box (1 pixel outside slider working area) + int16_t x, y; // x and y can be negative + uint16_t w, h; // Width and height + s1.getBoundingRect(&x, &y, &w, &h); // Update x,y,w,h with bounding box + tft.drawRect(x, y, w, h, TFT_DARKGREY); // Draw rectangle outline +/* + // Alternative discrete fns to create/modify same slider - but fn sequence is important... + s1.createSlider(9, 200, TFT_BLUE, TFT_BLACK, H_SLIDER); + s1.createKnob(15, 25, 5, TFT_WHITE, TFT_RED); + s1.setSliderScale(0, 100); + s1.drawSlider(0, 0); +*/ + delay(1000); + s1.setSliderPosition(50); + delay(1000); + s1.setSliderPosition(100); + + // Update any parameters that are different for slider 2 + param.slotWidth = 4; + param.orientation = V_SLIDER; // sets it "false" for vertical + + param.knobWidth = 19; + param.knobHeight = 19; + param.knobRadius = 19/2; // Half w and h so creates a circle + + param.sliderLT = 200; // Top for vertical slider + param.sliderRB = 0; // Bottom for vertical slider + param.sliderDelay = 2000; // 2ms per pixel movement delay (movement is blocking until complete) + + s2.drawSlider(0, 50, param); + + s2.getBoundingRect(&x, &y, &w, &h); + tft.drawRect(x, y, w, h, TFT_DARKGREY); +/* + // Alternative discrete fns to create/modify same slider - but fn sequence is important... + s2.createSlider(4, 200, TFT_BLUE, TFT_BLACK, V_SLIDER); + s2.createKnob(19, 19, 9, TFT_WHITE, TFT_RED); + s2.setSliderScale(200, 0, 2000); + s2.drawSlider(0, 50); +*/ + // Move slider under software control + delay(1000); + s2.setSliderPosition(50); + delay(1000); + s2.setSliderPosition(100); + +} + +void loop() { + static uint32_t scanTime = millis(); + uint16_t t_x = 9999, t_y = 9999; // To store the touch coordinates + + // Scan for touch every 50ms + if (millis() - scanTime >= 20) { + // Pressed will be set true if there is a valid touch on the screen + if( tft.getTouch(&t_x, &t_y, 250) ) { + if (s1.checkTouch(t_x, t_y)) { + Serial.print("Slider 1 = "); Serial.println(s1.getSliderPosition()); + } + if (s2.checkTouch(t_x, t_y)) { + Serial.print("Slider 2 = "); Serial.println(s2.getSliderPosition()); + } + } + scanTime = millis(); + } + + //s1.moveTo(random(101)); + //delay(250); + //s2.moveTo(random(101)); + //delay(250); +} + + + + + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!LittleFS.begin()) { + Serial.println("Formating file system"); + LittleFS.format(); + LittleFS.begin(); + } + + // check if calibration file exists and size is correct + if (LittleFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + LittleFS.remove(CALIBRATION_FILE); + } + else + { + File f = LittleFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = LittleFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..289bf62 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,64 @@ +# TFT_eWidget library + +TFT_eWidget KEYWORD1 +ButtonWidget KEYWORD1 + +initButton KEYWORD2 +initButtonUL KEYWORD2 +setLabelDatum KEYWORD2 +drawButton KEYWORD2 +contains KEYWORD2 +press KEYWORD2 +isPressed KEYWORD2 +justPressed KEYWORD2 +justReleased KEYWORD2 + +setPressAction KEYWORD2 +setReleaseAction KEYWORD2 + +setPressTime KEYWORD2 +setReleaseTime KEYWORD2 +getPressTime KEYWORD2 +getReleaseTime KEYWORD2 + +drawSmoothButton KEYWORD2 +getState KEYWORD2 + + +SliderWidget KEYWORD1 +drawSlider KEYWORD2 +createSlider KEYWORD2 +createKnob KEYWORD2 +setSliderScale KEYWORD2 +setSliderPosition KEYWORD2 +getSliderPosition KEYWORD2 +getBoundingBox KEYWORD2 +getBoundingRect KEYWORD2 +checkTouch KEYWORD2 + + +TraceWidget KEYWORD1 +startTrace KEYWORD2 +addPoint KEYWORD2 +getLastPointX KEYWORD2 +getLastPointY KEYWORD2 + + +GraphWidget KEYWORD1 +createGraph KEYWORD2 +setGraphPosition KEYWORD2 +getGraphPosition KEYWORD2 +drawGraph KEYWORD2 +setGraphScale KEYWORD2 +setGraphGrid KEYWORD2 +getBoundingBox KEYWORD2 +getBoundingRect KEYWORD2 +getPointX KEYWORD2 +getPointY KEYWORD2 +addLine KEYWORD2 + + +MeterWidget KEYWORD1 +analogMeter KEYWORD2 +setZones KEYWORD2 +updateNeedle KEYWORD2 diff --git a/library.json b/library.json new file mode 100644 index 0000000..d5c245b --- /dev/null +++ b/library.json @@ -0,0 +1,22 @@ +{ + "name": "TFT_eWidget", + "version": "0.0.1", + "keywords": "Arduino, tft, display, button, gui, graph, meter, slider", + "description": "A TFT GUI widget library", + "repository": + { + "type": "git", + "url": "https://github.com/Bodmer/TFT_eWidgit" + }, + "authors": + [ + { + "name": "Bodmer", + "email": "bodmer@anola.net", + "maintainer": true + } + ], + "frameworks": "arduino", + "platforms": "*", + "headers": "TFT_eWidget.h" +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..bd09cac --- /dev/null +++ b/library.properties @@ -0,0 +1,11 @@ +name=TFT_eWidget +version=0.0.1 +author=Bodmer +maintainer=Bodmer +sentence=A TFT GUI widget library +paragraph=A TFT support library providing button, graph, meter, and slider class functions. +category=Display +url=https://github.com/Bodmer/TFT_eWidgit +architectures=* +includes=TFT_eWidget.h + diff --git a/src/TFT_eWidget.cpp b/src/TFT_eWidget.cpp new file mode 100644 index 0000000..14b1a5a --- /dev/null +++ b/src/TFT_eWidget.cpp @@ -0,0 +1 @@ +#include "TFT_eWidget.h" diff --git a/src/TFT_eWidget.h b/src/TFT_eWidget.h new file mode 100644 index 0000000..b47947c --- /dev/null +++ b/src/TFT_eWidget.h @@ -0,0 +1,21 @@ +/*************************************************************************************** + + TFT display Widget library comprising following classes: + 1. ButtonWidget - button library + 2. SliderWidget - control sliders + +***************************************************************************************/ +#ifndef _TFT_eWidgetH_ +#define _TFT_eWidgetH_ + +//Standard support +#include + +#include + +#include "widgets/button/ButtonWidget.h" +#include "widgets/slider/SliderWidget.h" +#include "widgets/meter/Meter.h" +#include "widgets/graph/GraphWidget.h" +#include "widgets/graph/TraceWidget.h" +#endif \ No newline at end of file diff --git a/src/widgets/button/ButtonWidget.cpp b/src/widgets/button/ButtonWidget.cpp new file mode 100644 index 0000000..2bb032f --- /dev/null +++ b/src/widgets/button/ButtonWidget.cpp @@ -0,0 +1,163 @@ +#include "ButtonWidget.h" + +/*************************************************************************************** +** Code for the GFX button UI element +** Grabbed from Adafruit_tft library and enhanced to handle any label font +***************************************************************************************/ +ButtonWidget::ButtonWidget(TFT_eSPI *tft) { + _tft = tft; + _xd = 0; + _yd = 0; + _textdatum = MC_DATUM; + _label[9] = '\0'; + _currstate = false; + _laststate = false; + _inverted = false; +} + +void ButtonWidget::setPressAction(actionCallback action) +{ + pressAction = action; +} + +void ButtonWidget::setReleaseAction(actionCallback action) +{ + releaseAction = action; +} + +// Classic initButton() function: pass center & size +void ButtonWidget::initButton(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, uint16_t textcolor, char *label, uint8_t textsize) +{ + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor, label, textsize); +} + +// Newer function instead accepts upper-left corner & size +void ButtonWidget::initButtonUL(int16_t x1, int16_t y1, uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, uint16_t textcolor, char *label, uint8_t textsize) +{ + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; + _outlinecolor = outline; + _outlinewidth = 2; + _fillcolor = fill; + _textcolor = textcolor; + _textsize = textsize; + strncpy(_label, label, 9); + _pressTime = 0xFFFFFFFF; + _releaseTime = 0xFFFFFFFF; +} + +// Adjust text datum and x, y deltas +void ButtonWidget::setLabelDatum(int16_t x_delta, int16_t y_delta, uint8_t datum) +{ + _xd = x_delta; + _yd = y_delta; + _textdatum = datum; +} + +void ButtonWidget::drawButton(bool inverted, String long_name) { + uint16_t fill, outline, text; + + _inverted = inverted; + + if(!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + _tft->fillRoundRect(_x1, _y1, _w, _h, r, fill); + _tft->drawRoundRect(_x1, _y1, _w, _h, r, outline); + + if (_tft->textfont == 255) { + _tft->setCursor(_x1 + (_w / 8), + _y1 + (_h / 4)); + _tft->setTextColor(text); + _tft->setTextSize(_textsize); + _tft->print(_label); + } + else { + _tft->setTextColor(text, fill); + _tft->setTextSize(_textsize); + + uint8_t tempdatum = _tft->getTextDatum(); + _tft->setTextDatum(_textdatum); + uint16_t tempPadding = _tft->getTextPadding(); + _tft->setTextPadding(0); + + if (long_name == "") + _tft->drawString(_label, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd); + else + _tft->drawString(long_name, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd); + + _tft->setTextDatum(tempdatum); + _tft->setTextPadding(tempPadding); + } +} + +void ButtonWidget::drawSmoothButton(bool inverted, int16_t outlinewidth, uint32_t bgcolor, String long_name) { + uint16_t fill, outline, text; + if (bgcolor != 0x00FFFFFF) _bgcolor = bgcolor; + if (outlinewidth >=0) _outlinewidth = outlinewidth; + _inverted = inverted; + + if(!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + if (outlinewidth > 0) _tft->fillSmoothRoundRect(_x1, _y1, _w, _h, r, outline, _bgcolor); + _tft->fillSmoothRoundRect(_x1+_outlinewidth, _y1+_outlinewidth, _w-(2*_outlinewidth), _h-(2*_outlinewidth), r-_outlinewidth, fill, outline); + + if (_tft->textfont == 255) { + _tft->setCursor(_x1 + (_w / 8), + _y1 + (_h / 4)); + _tft->setTextColor(text); + _tft->setTextSize(_textsize); + _tft->print(_label); + } + else { + _tft->setTextColor(text, fill); + _tft->setTextSize(_textsize); + + uint8_t tempdatum = _tft->getTextDatum(); + _tft->setTextDatum(_textdatum); + uint16_t tempPadding = _tft->getTextPadding(); + _tft->setTextPadding(0); + + if (long_name == "") + _tft->drawString(_label, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd); + else + _tft->drawString(long_name, _x1 + (_w/2) + _xd, _y1 + (_h/2) - 4 + _yd); + + _tft->setTextDatum(tempdatum); + _tft->setTextPadding(tempPadding); + } +} + +bool ButtonWidget::contains(int16_t x, int16_t y) { + return ((x >= _x1) && (x < (_x1 + _w)) && + (y >= _y1) && (y < (_y1 + _h))); +} + +void ButtonWidget::press(bool p) { + _laststate = _currstate; + _currstate = p; +} + +bool ButtonWidget::isPressed() { return _currstate; } +bool ButtonWidget::justPressed() { return (_currstate && !_laststate); } +bool ButtonWidget::justReleased() { return (!_currstate && _laststate); } diff --git a/src/widgets/button/ButtonWidget.h b/src/widgets/button/ButtonWidget.h new file mode 100644 index 0000000..ab78aad --- /dev/null +++ b/src/widgets/button/ButtonWidget.h @@ -0,0 +1,79 @@ +/*************************************************************************************** +// The following button class has been ported over from the Adafruit_tft library. +// A slightly different implementation in this ButtonWidget library allows: +// +// 1. The button labels to be in any font +// 2. Allow longer labels +// 3. Allow text datum to be set +// 4. Allow invert state to be read via getState(), true = inverted +// 5. Define pressed and released callbacks per button instances +// +// The library uses functions present in TFT_eSPI +// https://github.com/Bodmer/TFT_eSPI +***************************************************************************************/ +#ifndef _ButtonWidgetH_ +#define _ButtonWidgetH_ + +//Standard support +#include + +#include + +//#include +//typedef std::function actionCallback; + typedef void (*actionCallback)(void); + static void dummyButtonAction(void) { }; // In case user callback is not defined! + +class ButtonWidget : public TFT_eSPI { + + public: + + ButtonWidget(TFT_eSPI *tft); + + // "Classic" initButton() uses centre & size + void initButton(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, uint16_t textcolor, char *label, uint8_t textsize); + + // New/alt initButton() uses upper-left corner & size + void initButtonUL(int16_t x1, int16_t y1, uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, uint16_t textcolor, char *label, uint8_t textsize); + + // Adjust text datum and x, y deltas + void setLabelDatum(int16_t x_delta, int16_t y_delta, uint8_t datum = MC_DATUM); + + actionCallback pressAction = dummyButtonAction; // Press action callback + actionCallback releaseAction = dummyButtonAction; // Release action callback + + void setPressAction(actionCallback action); + void setReleaseAction(actionCallback action); + + void setPressTime(uint32_t pressTime) { _pressTime = pressTime; } + void setReleaseTime(uint32_t releaseTime){ _releaseTime = releaseTime; } + uint32_t getPressTime(void) { return _pressTime; } + uint32_t getReleaseTime(void){ return _releaseTime; } + + + + void drawButton(bool inverted = false, String long_name = ""); + void drawSmoothButton(bool inverted = false, int16_t outlinewidth = -1, uint32_t bgcolor = 0x00FFFFFF, String long_name = ""); + bool contains(int16_t x, int16_t y); + + void press(bool p); + bool isPressed(); + bool justPressed(); + bool justReleased(); + + bool getState(void) {return _inverted;} // Get inverted state, true = inverted + + + private: + TFT_eSPI *_tft; + int16_t _x1, _y1; // Coordinates of top-left corner of button + int16_t _xd, _yd; // Button text datum offsets (wrt centre of button) + uint16_t _w, _h; // Width and height of button + uint8_t _textsize, _textdatum; // Text size multiplier and text datum for button + uint16_t _outlinecolor, _fillcolor, _textcolor, _outlinewidth, _bgcolor; + char _label[10]; // Button text is 9 chars maximum unless long_name used + uint32_t _pressTime, _releaseTime; + bool _inverted, _currstate, _laststate; // Button states +}; + +#endif diff --git a/src/widgets/graph/GraphWidget.cpp b/src/widgets/graph/GraphWidget.cpp new file mode 100644 index 0000000..d3eb9d7 --- /dev/null +++ b/src/widgets/graph/GraphWidget.cpp @@ -0,0 +1,282 @@ +/*************************************************************************************** + +***************************************************************************************/ + +#include "GraphWidget.h" + + +/*************************************************************************************** +** Function name: GraphWidget +** Description: Constructor with pointers to TFT and sprite instances +***************************************************************************************/ +GraphWidget::GraphWidget(TFT_eSPI *tft) +{ + _tft = tft; +} + +/*************************************************************************************** +** Function name: createGraph +** Description: Create graph with parameters +***************************************************************************************/ +bool GraphWidget::createGraph(uint16_t graphWidth, uint16_t graphHeight, uint16_t bgColor) +{ + _width = graphWidth; + _height = graphHeight; + _bgColor = bgColor; + + return 0; +} + +/*************************************************************************************** +** Function name: setGraphScale +** Description: Set slider scale range with movement delay in us per pixel +***************************************************************************************/ +void GraphWidget::setGraphScale(float xmin, float xmax, float ymin, float ymax) +{ + _xMin = xmin; + _xMax = xmax; + _yMin = ymin; + _yMax = ymax; +} + +/*************************************************************************************** +** Function name: setGraphScale +** Description: Set slider scale range with movement delay in us per pixel +***************************************************************************************/ +void GraphWidget::setGraphGrid(float xsval, float xinc, float ysval, float yinc, uint16_t gridColor) +{ + _xGridStart = xsval; + _xGridInc = xinc; + _yGridStart = ysval; + _yGridInc = yinc; + _gridColor = gridColor; +} + +/*************************************************************************************** +** Function name: drawGraph +** Description: drawGraph to TFT screen with set parameters +***************************************************************************************/ +void GraphWidget::drawGraph(uint16_t x, uint16_t y) +{ + _tft->fillRect(x, y, _width, _height, _bgColor); + + for (float px = _xGridStart; px <= _xMax; px += _xGridInc) + { + uint16_t gx = map(px, _xMin, _xMax, 0, _width); + _tft->drawFastVLine(x + gx, y, _height, _gridColor); + } + + for (float py = _yGridStart; py <= _yMax; py += _yGridInc) + { + uint16_t gy = map(py, _yMin, _yMax, _height, 0); + _tft->drawFastHLine(x, y + gy, _width, _gridColor); + } + + _xpos = x; + _ypos = y; +} + +/*************************************************************************************** +** Function name: setGraphPosition +** Description: Set the graph top left corner position without drawing it +***************************************************************************************/ +void GraphWidget::setGraphPosition(uint16_t x, uint16_t y) +{ + _xpos = x; + _ypos = y; +} + +/*************************************************************************************** +** Function name: getGraphPosition +** Description: Get the graph top left corner position +***************************************************************************************/ +void GraphWidget::getGraphPosition(uint16_t *x, uint16_t *y) +{ + *x = _xpos; + *y = _ypos; +} + +/*************************************************************************************** +** Function name: getBoundingBox +** Description: Return the bounding box as coordinates +***************************************************************************************/ +void GraphWidget::getBoundingBox(int16_t *xs, int16_t *ys, int16_t *xe, int16_t *ye) +{ + // Bounds already corrected for Sprite wipe action + *xs = _xpos; + *ys = _ypos; + *xe = _xpos + _width; + *ye = _ypos + _height; +} + +/*************************************************************************************** +** Function name: getBoundingRect +** Description: Return outside bounding box rectangle x,y and width,height +***************************************************************************************/ +void GraphWidget::getBoundingRect(int16_t *x, int16_t *y, uint16_t *w, uint16_t *h) +{ + *x = _xpos; + *y = _ypos; + *w = _width; + *h = _height; +} + +/*************************************************************************************** +** Function name: getPointX +** Description: Get x pixel coordinates of a position on display +***************************************************************************************/ +int16_t GraphWidget::getPointX(float xval) +{ + int16_t gx = 0.5 + mapFloat(xval, _xMin, _xMax, 0, _width); + + return _xpos + gx; +} + +/*************************************************************************************** +** Function name: getPointY +** Description: Get y pixel coordinates of a position on display +***************************************************************************************/ +int16_t GraphWidget::getPointY(float yval) +{ + int16_t gy = 0.5 + mapFloat(yval, _yMin, _yMax, _height, 0); + + return _ypos + gy; +} + +/*************************************************************************************** +** Function name: addLine +** Description: Add new line segment on graph +***************************************************************************************/ +bool GraphWidget::addLine(float xs, float ys, float xe, float ye, uint16_t col) +{ + if (clipTrace(&xs, &ys, &xe, &ye)) + { + // All or part of line is in graph area + int16_t cxs = _xpos + 0.5 + mapFloat(xs, _xMin, _xMax, 0, _width); + int16_t cys = _ypos + 0.5 + mapFloat(ys, _yMin, _yMax, _height, 0); + int16_t cxe = _xpos + 0.5 + mapFloat(xe, _xMin, _xMax, 0, _width); + int16_t cye = _ypos + 0.5 + mapFloat(ye, _yMin, _yMax, _height, 0); + _tft->drawLine(cxs, cys, cxe, cye, col); + return true; + } + + // No part of line was in graph area + return false; +} + + +/*************************************************************************************** +** Function name: regionCode +** Description: Compute region code for a point(x, y) +***************************************************************************************/ +uint16_t GraphWidget::regionCode(float x, float y) +{ + // Assume inside + int code = INSIDE; + + if (x < _xMin) // to the left of rectangle + code |= LEFT; + else if (x > _xMax) // to the right of rectangle + code |= RIGHT; + if (y < _yMin) // below the rectangle + code |= BOTTOM; + else if (y > _yMax) // above the rectangle + code |= TOP; + return code; +} + +/*************************************************************************************** +** Function name: clipTrace +** Description: Implement Cohen-Sutherland clipping algorithm +***************************************************************************************/ +// https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm +bool GraphWidget::clipTrace(float *xs, float *ys, float *xe, float *ye) +{ + float x1 = *xs; + float y1 = *ys; + float x2 = *xe; + float y2 = *ye; + + // Compute region codes + uint16_t code1 = regionCode(x1, y1); + uint16_t code2 = regionCode(x2, y2); + bool inside = false; + + while (true) { + if ((code1 == 0) && (code2 == 0)) { + // bitwise OR is 0: both points inside window; trivially accept and exit loop + inside = true; + break; + } + else if (code1 & code2) { + // bitwise AND is not 0: both points share an outside zone (LEFT, RIGHT, TOP, + // or BOTTOM), so both must be outside window; exit loop (accept is false) + break; + } + else { + // failed both tests, so calculate the line segment to clip + // from an outside point to an intersection with clip edge + uint16_t code_out; + float x = 0, y = 0; + + // At least one endpoint is outside the clip rectangle; pick it. + if (code1 != 0) + code_out = code1; + else + code_out = code2; + + // Now find the intersection point; + // use formulas: + // slope = (y1 - y0) / (x1 - x0) + // x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax + // y = y0 + slope * (xm - x0), where xm is xmin or xmax + // No need to worry about divide-by-zero because, in each case, the + // code_out bit being tested guarantees the denominator is non-zero + if (code_out & TOP) { + // point is above the clip rectangle + x = x1 + (x2 - x1) * (_yMax - y1) / (y2 - y1); + y = _yMax; + } + else if (code_out & BOTTOM) { + // point is below the rectangle + x = x1 + (x2 - x1) * (_yMin - y1) / (y2 - y1); + y = _yMin; + } + else if (code_out & RIGHT) { + // point is to the right of rectangle + y = y1 + (y2 - y1) * (_xMax - x1) / (x2 - x1); + x = _xMax; + } + else if (code_out & LEFT) { + // point is to the left of rectangle + y = y1 + (y2 - y1) * (_xMin - x1) / (x2 - x1); + x = _xMin; + } + + // Now we move outside point to intersection point to clip + // and get ready for next pass. + if (code_out == code1) { + x1 = x; + y1 = y; + code1 = regionCode(x1, y1); + } + else { + x2 = x; + y2 = y; + code2 = regionCode(x2, y2); + } + } + } + + // Return the clipped coordinates + if (inside) + { + *xs = x1; + *ys = y1; + *xe = x2; + *ye = y2; + } + + // Return true if a line segment need to be drawn in rectangle + return inside; +} diff --git a/src/widgets/graph/GraphWidget.h b/src/widgets/graph/GraphWidget.h new file mode 100644 index 0000000..675816c --- /dev/null +++ b/src/widgets/graph/GraphWidget.h @@ -0,0 +1,81 @@ +/*************************************************************************************** + +***************************************************************************************/ +#ifndef _GraphWidgetH_ +#define _GraphWidgetH_ + +//Standard support +#include + +#include +// Created by Bodmer from widget sketch functions + +class GraphWidget : public TFT_eSPI { + + public: + + GraphWidget(TFT_eSPI *tft); + + bool createGraph(uint16_t graphWidth, uint16_t graphHeight, uint16_t bgColor); + void setGraphGrid(float xsval, float xinc, float ysval, float yinc, uint16_t gridColor); + + void setGraphPosition(uint16_t x, uint16_t y); + void getGraphPosition(uint16_t *x, uint16_t *y); + + void drawGraph(uint16_t x, uint16_t y); + + void setGraphScale(float xmin, float xmax, float ymin, float ymax); + + void getBoundingBox(int16_t *xs, int16_t *ys, int16_t *xe, int16_t *ye); + void getBoundingRect(int16_t *x, int16_t *y, uint16_t *w, uint16_t *h); + + int16_t getPointX(float xval); + int16_t getPointY(float yval); + + bool addLine(float xs, float ys, float xe, float ye, uint16_t col); + + // createGraph + uint16_t _width; + uint16_t _height; + + // setGraphScale + float _xMin = 0.0; + float _xMax = 100.0; + float _yMin = 0.0; + float _yMax = 100.0; + + // drawGraph + uint16_t _xpos = 0; + uint16_t _ypos = 0; + + private: + + // Map a float to a new range + float mapFloat(float ip, float ipmin, float ipmax, float tomin, float tomax) + { + return tomin + (((tomax - tomin) * (ip - ipmin))/ (ipmax - ipmin)); + } + + uint16_t regionCode(float x, float y); + bool clipTrace(float *xs, float *ys, float *xe, float *ye); + + uint16_t _gridColor; + uint16_t _bgColor; + + // setGraphGrid + float _xGridStart = 0.0; + float _xGridInc = 10.0; + float _yGridStart = 0.0; + float _yGridInc = 10.0; + + // Defining region codes + const uint16_t INSIDE = 0x0; // 0000 + const uint16_t LEFT = 0x1; // 0001 + const uint16_t RIGHT = 0x2; // 0010 + const uint16_t BOTTOM = 0x4; // 0100 + const uint16_t TOP = 0x8; // 1000 + + TFT_eSPI *_tft; +}; + +#endif \ No newline at end of file diff --git a/src/widgets/graph/TraceWidget.cpp b/src/widgets/graph/TraceWidget.cpp new file mode 100644 index 0000000..8828580 --- /dev/null +++ b/src/widgets/graph/TraceWidget.cpp @@ -0,0 +1,72 @@ +/*************************************************************************************** + +***************************************************************************************/ + +#include "TraceWidget.h" + +/*************************************************************************************** +** Function name: TraceWidget +** Description: Constructor with pointers to TFT and sprite instances +***************************************************************************************/ +TraceWidget::TraceWidget(GraphWidget *gw) { + _gw = gw; +} + +/*************************************************************************************** +** Function name: startTrace +** Description: Start a new trace +***************************************************************************************/ +void TraceWidget::startTrace(uint16_t ptColor) +{ + _newTrace = true; + _ptColor = ptColor; + _xpt = 0; + _ypt = 0; +} + +/*************************************************************************************** +** Function name: addPoint +** Description: Add new point on graph +***************************************************************************************/ +bool TraceWidget::addPoint(float xval, float yval) +{ + // Map point to display pixel coordinate + _xpt = 0.5 + _gw->getPointX(xval); + _ypt = 0.5 + _gw->getPointY(yval); + + // If it is a new trace then a single pixel is drawn + if (_newTrace) + { + _newTrace = false; + _xval = xval; + _yval = yval; + } + + // Draw the line segment on graph (gets clipped to graph area) + bool updated = _gw->addLine(_xval, _yval, xval, yval, _ptColor); + + // Store last graph scale point for this trace + _xval = xval; + _yval = yval; + + // Returns true if part of resulting line drawn in graph area + return updated; +} + +/*************************************************************************************** +** Function name: getLastPointX +** Description: Get x pixel coordinates of last point plotted +***************************************************************************************/ +uint16_t TraceWidget::getLastPointX(void) +{ + return _xpt; +} + +/*************************************************************************************** +** Function name: getLastPointX +** Description: Get y pixel coordinates of last point plotted +***************************************************************************************/ +uint16_t TraceWidget::getLastPointY(void) +{ + return _ypt; +} diff --git a/src/widgets/graph/TraceWidget.h b/src/widgets/graph/TraceWidget.h new file mode 100644 index 0000000..c3e896d --- /dev/null +++ b/src/widgets/graph/TraceWidget.h @@ -0,0 +1,49 @@ +/*************************************************************************************** + +***************************************************************************************/ +#ifndef _TraceWidgetH_ +#define _TraceWidgetH_ + +//Standard support +#include + +#include +#include "GraphWidget.h" + +// Created by Bodmer from widget sketch functions + +class TraceWidget : public TFT_eSPI { + + public: + + TraceWidget(GraphWidget *gw); + + void startTrace(uint16_t ptColor); + bool addPoint(float xval, float yval); + + uint16_t getLastPointX(void); + uint16_t getLastPointY(void); + + private: + + // Map a float to a new range + float mapFloat(float ip, float ipmin, float ipmax, float tomin, float tomax) + { + return tomin + (((tomax - tomin) * (ip - ipmin))/ (ipmax - ipmin)); + } + + uint16_t regionCode(float x, float y); + bool clipTrace(float *xs, float *ys, float *xe, float *ye); + + // trace + bool _newTrace = true; + uint16_t _ptColor = TFT_WHITE; + uint16_t _xpt = 0; + uint16_t _ypt = 0; + float _xval = 0; + float _yval = 0; + + GraphWidget *_gw; +}; + +#endif \ No newline at end of file diff --git a/src/widgets/meter/Meter.cpp b/src/widgets/meter/Meter.cpp new file mode 100644 index 0000000..c97063d --- /dev/null +++ b/src/widgets/meter/Meter.cpp @@ -0,0 +1,235 @@ + #include "Arduino.h" + #include "Meter.h" + +// ######################################################################### +// Meter constructor +// ######################################################################### + MeterWidget::MeterWidget(TFT_eSPI* tft) + { + ltx = 0; // Saved x coord of bottom of needle + osx = 120, osy = 120; // Saved x & y coords + old_analog = -999; // Value last displayed + old_digital = -999; // Value last displayed + + mx = 0; + my = 0; + + factor = 1.0; + + mlabel[8] = '\0'; + + // Defaults + strncpy(ms0, "0", 4); + strncpy(ms1, "25", 4); + strncpy(ms2, "50", 4); + strncpy(ms3, "75", 4); + strncpy(ms4, "100", 4); + + redStart = 0; + redEnd = 0; + orangeStart = 0; + orangeEnd = 0; + yellowStart = 0; + yellowEnd = 0; + greenStart = 0; + greenEnd = 0; + + ntft = tft; + } + +// ######################################################################### +// Draw meter meter at x, y and define full scale range & the scale labels +// ######################################################################### +void MeterWidget::analogMeter(uint16_t x, uint16_t y, float fullScale, const char *label, const char *s0, const char *s1, const char *s2, const char *s3, const char *s4) +{ + // Save offsets for needle plotting + mx = x; + my = y; + factor = 100.0/fullScale; + + strncpy(mlabel, label, 8); + + strncpy(ms0, s0, 4); + strncpy(ms1, s1, 4); + strncpy(ms2, s2, 4); + strncpy(ms3, s3, 4); + strncpy(ms4, s4, 4); + + // Meter outline + ntft->fillRect(x, y, 239, 126, TFT_GREY); + ntft->fillRect(x + 5, y + 3, 230, 119, TFT_WHITE); + + ntft->setTextColor(TFT_BLACK); // Text colour + + // Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing) + for (int i = -50; i < 51; i += 5) { + // Long scale tick length + int tl = 15; + + // Coordinates of tick to draw + float sx = cos((i - 90) * 0.0174532925); + float sy = sin((i - 90) * 0.0174532925); + uint16_t x0 = x + sx * (100 + tl) + 120; + uint16_t y0 = y + sy * (100 + tl) + 140; + uint16_t x1 = x + sx * 100 + 120; + uint16_t y1 = y + sy * 100 + 140; + + // Coordinates of next tick for zone fill + float sx2 = cos((i + 5 - 90) * 0.0174532925); + float sy2 = sin((i + 5 - 90) * 0.0174532925); + int x2 = x + sx2 * (100 + tl) + 120; + int y2 = y + sy2 * (100 + tl) + 140; + int x3 = x + sx2 * 100 + 120; + int y3 = y + sy2 * 100 + 140; + + // Red zone limits + if (redEnd > redStart) { + if (i >= redStart && i < redEnd) { + ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_RED); + ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_RED); + } + } + + // Orange zone limits + if (orangeEnd > orangeStart) { + if (i >= orangeStart && i < orangeEnd) { + ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_ORANGE); + ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_ORANGE); + } + } + + // Yellow zone limits + if (yellowEnd > yellowStart) { + if (i >= yellowStart && i < yellowEnd) { + ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_YELLOW); + ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_YELLOW); + } + } + + // Green zone limits + if (greenEnd > greenStart) { + if (i >= greenStart && i < greenEnd) { + ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN); + ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN); + } + } + + // Short scale tick length + if (i % 25 != 0) tl = 8; + + // Recalculate coords in case tick length changed + x0 = x + sx * (100 + tl) + 120; + y0 = y + sy * (100 + tl) + 140; + x1 = x + sx * 100 + 120; + y1 = y + sy * 100 + 140; + + // Draw tick + ntft->drawLine(x0, y0, x1, y1, TFT_BLACK); + + // Check if labels should be drawn, with position tweaks + if (i % 25 == 0) { + // Calculate label positions + x0 = x + sx * (100 + tl + 10) + 120; + y0 = y + sy * (100 + tl + 10) + 140; + switch (i / 25) { + case -2: ntft->drawCentreString(ms0, x0, y0 - 12, 2); break; + case -1: ntft->drawCentreString(ms1, x0, y0 - 9, 2); break; + case 0: ntft->drawCentreString(ms2, x0, y0 - 6, 2); break; + case 1: ntft->drawCentreString(ms3, x0, y0 - 9, 2); break; + case 2: ntft->drawCentreString(ms4, x0, y0 - 12, 2); break; + } + } + + // Now draw the arc of the scale + sx = cos((i + 5 - 90) * 0.0174532925); + sy = sin((i + 5 - 90) * 0.0174532925); + x0 = x + sx * 100 + 120; + y0 = y + sy * 100 + 140; + // Draw scale arc, don't draw the last part + if (i < 50) ntft->drawLine(x0, y0, x1, y1, TFT_BLACK); + } + + ntft->drawString(mlabel, x + 5 + 230 - 40, y + 119 - 20, 2); // Units at bottom right + ntft->drawCentreString(mlabel, x + 120, y + 70, 4); // Comment out to avoid font 4 + ntft->drawRect(x + 5, y + 3, 230, 119, TFT_BLACK); // Draw bezel line + + updateNeedle(0, 0); +} + +// ######################################################################### +// Update needle position +// This function is blocking while needle moves, time depends on ms_delay +// 10ms minimises needle flicker if text is drawn within needle sweep area +// Smaller values OK if text not in sweep area, zero for instant movement but +// does not look realistic... (note: 100 increments for full scale deflection) +// ######################################################################### +void MeterWidget::updateNeedle(float val, uint32_t ms_delay) +{ + int value = val * factor; + + ntft->setTextColor(TFT_BLACK, TFT_WHITE); + char buf[8]; dtostrf(value/factor, 5, 1, buf); + + ntft->drawRightString(buf, mx + 50, my + 119 - 20, 2); + + if (value < -10) value = -10; // Limit value to emulate needle end stops + if (value > 110) value = 110; + + // Move the needle until new value reached + while (value != old_analog) { + if (old_analog < value) old_analog++; + else old_analog--; + + if (ms_delay == 0) old_analog = value; // Update immediately id delay is 0 + + float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle + // Calculate tip of needle coords + float sx = cos(sdeg * 0.0174532925); + float sy = sin(sdeg * 0.0174532925); + + // Calculate x delta of needle start (does not start at pivot point) + float tx = tan((sdeg + 90) * 0.0174532925); + + // Erase old needle image + ntft->drawLine(mx + 120 + 20 * ltx - 1, my + 140 - 20, mx + osx - 1, my + osy, TFT_WHITE); + ntft->drawLine(mx + 120 + 20 * ltx, my + 140 - 20, mx + osx, my + osy, TFT_WHITE); + ntft->drawLine(mx + 120 + 20 * ltx + 1, my + 140 - 20, mx + osx + 1, my + osy, TFT_WHITE); + + // Re-plot text under needle + ntft->setTextColor(TFT_BLACK); + ntft->drawCentreString(mlabel, mx + 120, my + 70, 4); // // Comment out to avoid font 4 + + // Store new needle end coords for next erase + ltx = tx; + osx = sx * 98 + 120; + osy = sy * 98 + 140; + + // Draw the needle in the new position, magenta makes needle a bit bolder + // draws 3 lines to thicken needle + ntft->drawLine(mx + 120 + 20 * ltx - 1, my + 140 - 20, mx + osx - 1, my + osy, TFT_RED); + ntft->drawLine(mx + 120 + 20 * ltx, my + 140 - 20, mx + osx, my + osy, TFT_MAGENTA); + ntft->drawLine(mx + 120 + 20 * ltx + 1, my + 140 - 20, mx + osx + 1, my + osy, TFT_RED); + + // Slow needle down slightly as it approaches new position + if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5; + + // Wait before next update + delay(ms_delay); + } +} + +// ######################################################################### +// Set red, orange, yellow and green start+end zones as a % of full scale +// ######################################################################### +void MeterWidget::setZones(uint16_t rs, uint16_t re, uint16_t os, uint16_t oe, uint16_t ys, uint16_t ye, uint16_t gs, uint16_t ge) +{ + // Meter scale is -50 to +50 + redStart = rs - 50; + redEnd = re - 50; + orangeStart = os - 50; + orangeEnd = oe - 50; + yellowStart = ys - 50; + yellowEnd = ye - 50; + greenStart = gs - 50; + greenEnd = ge - 50; +} \ No newline at end of file diff --git a/src/widgets/meter/Meter.h b/src/widgets/meter/Meter.h new file mode 100644 index 0000000..7107f9b --- /dev/null +++ b/src/widgets/meter/Meter.h @@ -0,0 +1,61 @@ + +#ifndef Meter_h +#define Meter_h + +#include "Arduino.h" +#include "TFT_eSPI.h" + +// Meter boarder colour +#define TFT_GREY 0x5AEB + +// Meter class +class MeterWidget +{ + public: + MeterWidget(TFT_eSPI* tft); + + // Set red, orange, yellow and green start+end zones as a percentage of full scale + void setZones(uint16_t rs, uint16_t re, uint16_t os, uint16_t oe, uint16_t ys, uint16_t ye, uint16_t gs, uint16_t ge); + // Draw meter meter at x, y and define full scale range plus the scale labels + void analogMeter(uint16_t x, uint16_t y, float fullScale, const char *string, const char *s0, const char *s1, const char *s2, const char *s3, const char *s4); + // Move needle to new position + void updateNeedle(float value, uint32_t ms_delay); + + private: + // Pointer to TFT_eSPI class functions + TFT_eSPI* ntft; + + float ltx; // x delta of needle start + uint16_t osx, osy; // Saved x & y coords of needle end + int old_analog; // Value last displayed + int old_digital; // Value last displayed + + // x, y coord of top left corner of meter graphic + uint16_t mx; + uint16_t my; + + // Scale factor + float factor; + + // Scale label + char mlabel[9]; + + // Scale values + char ms0[5]; + char ms1[5]; + char ms2[5]; + char ms3[5]; + char ms4[5]; + + // Scale colour zone start end end values + int16_t redStart; + int16_t redEnd; + int16_t orangeStart; + int16_t orangeEnd; + int16_t yellowStart; + int16_t yellowEnd; + int16_t greenStart; + int16_t greenEnd; +}; + +#endif diff --git a/src/widgets/slider/SliderWidget.cpp b/src/widgets/slider/SliderWidget.cpp new file mode 100644 index 0000000..5eff685 --- /dev/null +++ b/src/widgets/slider/SliderWidget.cpp @@ -0,0 +1,277 @@ +/*************************************************************************************** + +***************************************************************************************/ + +#include "SliderWidget.h" + +#define H_SLIDER true +#define V_SLIDER false + +// Swap any type +template static inline void +swap_val(T& a, T& b) { T t = a; a = b; b = t; } + +/*************************************************************************************** +** Function name: SliderWidget +** Description: Constructor with pointers to TFT and sprite instances +***************************************************************************************/ +SliderWidget::SliderWidget(TFT_eSPI *tft, TFT_eSprite *spr) { + _tft = tft; + _spr = spr; +} + +/*************************************************************************************** +** Function name: createSlider +** Description: Create slider with slot parameters +***************************************************************************************/ +bool SliderWidget::createSlider(uint16_t slotWidth, uint16_t slotLength, uint16_t slotColor, uint16_t bgColor, bool orientation) { + _slotWidth = slotWidth; + _slotLength = slotLength; + _slotColor = slotColor; + _slotBgColor = bgColor; + _horiz = orientation; + _kposPrev = slotLength/2; + return 0; +} + +/*************************************************************************************** +** Function name: createKnob +** Description: Create the slider control knob with parameters +***************************************************************************************/ +void SliderWidget::createKnob(uint16_t kwidth, uint16_t kheight, uint16_t kradius, uint16_t kcolor1, uint16_t kcolor2) { + _kwidth = kwidth; + _kheight = kheight; + _kradius = kradius; + _kcolor1 = kcolor1; + _kcolor2 = kcolor2; + + _sliderPos = _slotLength/2; +} + + +/*************************************************************************************** +** Function name: setSliderScale +** Description: Set slider scale range with movement delay in us per pixel +***************************************************************************************/ +void SliderWidget::setSliderScale(int16_t min, int16_t max, uint16_t usdelay) { + setSliderScale(min, max); + _usdelay = usdelay; +} + +/*************************************************************************************** +** Function name: setSliderScale +** Description: Set slider scale range (no movement delay) +***************************************************************************************/ +void SliderWidget::setSliderScale(int16_t min, int16_t max) { + + if (min > max) { + _invert = true; + swap_val(min, max); + } + else { + _invert = false; + } + _sliderMin = min; + _sliderMax = max; + _sliderPos = min; +} + +/*************************************************************************************** +** Function name: setSliderPosition +** Description: Set slider position to a value in the set scale range +***************************************************************************************/ +void SliderWidget::setSliderPosition(int16_t val) +{ + moveTo(_invert ? _sliderMax - val : val); +} + +/*************************************************************************************** +** Function name: getSliderPosition +** Description: Get the current slider value in set scale range +***************************************************************************************/ +int16_t SliderWidget::getSliderPosition(void) { + return _invert ? _sliderMax - _sliderPos : _sliderPos; +} + +/*************************************************************************************** +** Function name: checkTouch +** Description: Check is touch x, are inside slider box, if so move slider +***************************************************************************************/ +bool SliderWidget::checkTouch(uint16_t tx, uint16_t ty) +{ + if (tx >= _sxs && tx <= _sxe && ty >= _sys && ty <= _sye) { + uint16_t tp, kd; + if(_horiz) { + tp = tx - _sxs; + kd = _kwidth; + } + else { + tp = ty - _sys; + kd = _kheight; + } + int16_t tv = map(tp, _slotWidth/2 + kd/2 + 1, _slotLength - _slotWidth/2 - kd/2 - 1, _sliderMin, _sliderMax); + tv = constrain( tv, _sliderMin, _sliderMax); + moveTo(tv); + return true; + } + return false; +} + +/*************************************************************************************** +** Function name: drawSlider +** Description: drawSlider to TFT screen with set parameters +***************************************************************************************/ +void SliderWidget::drawSlider(uint16_t x, uint16_t y, slider_t param) +{ + // createSlider + _slotWidth = param.slotWidth; + _slotLength = param.slotLength; + _slotColor = param.slotColor; + _slotBgColor = param.slotBgColor; + _horiz = param.orientation; + + // createKnob + _kwidth = param.knobWidth; + _kheight = param.knobHeight; + _kradius = param.knobRadius; + _kcolor1 = param.knobColor; + _kcolor2 = param.knobLineColor; + + // setSliderScale + setSliderScale(param.sliderLT, param.sliderRB); + + _sliderPos = param.startPosition; + _usdelay = param.sliderDelay; + + + _kposPrev = _slotLength/2; + + drawSlider(x, y); +} + + +/*************************************************************************************** +** Function name: drawSlider +** Description: drawSlider to TFT screen and set slider position value +***************************************************************************************/ +void SliderWidget::drawSlider(uint16_t x, uint16_t y) +{ + if(_horiz) { + _xpos = x + 2; + _ypos = y + _kheight/2 + 2; + //_tft->drawWideLine(_xpos, _ypos, _xpos + _slotLength, _ypos_ypos,_slotWidth, _slotColor, _slotBgColor); + _tft->fillSmoothRoundRect(_xpos, _ypos - _slotWidth/2, _slotLength, _slotWidth, _slotWidth/2, _slotColor, _slotBgColor); + _sxs = _xpos - 1; + _sys = _ypos - _kheight/2 - 1; + _sxe = _xpos + _slotLength + 2; + _sye = _ypos + _kheight/2 + 3; + } + else { + _xpos = x + _kwidth/2 + 2; + _ypos = y + 2; + _tft->fillSmoothRoundRect(_xpos - _slotWidth/2, _ypos, _slotWidth, _slotLength, _slotWidth/2, _slotColor, _slotBgColor); + _sxs = _xpos - _kwidth/2 - 1; + _sys = _ypos - 1; + _sxe = _xpos + _kwidth/2 + 3; + _sye = _ypos + _slotLength + 2; + } + uint16_t kd = (_horiz ? _kwidth : _kheight); + _kposPrev = map(_sliderPos, _sliderMin, _sliderMax, _slotWidth/2 + 1, _slotLength - _slotWidth/2 - kd - 1); + setSliderPosition(_sliderPos); +} + +/*************************************************************************************** +** Function name: moveTo (private fn) +** Description: Move the slider to a new value in set range +***************************************************************************************/ +void SliderWidget::moveTo(int16_t val) +{ + val = constrain( val, _sliderMin, _sliderMax ); + + //if (val == _sliderPos) return; + + _sliderPos = val; + + uint16_t kd = (_horiz ? _kwidth : _kheight); + uint16_t kpos = map(val, _sliderMin, _sliderMax, _slotWidth/2 + 1, _slotLength - _slotWidth/2 - kd - 1); + + _spr->createSprite(_kwidth + 2, _kheight + 2); + + if (_usdelay == 0 && abs(kpos - _kposPrev) > kd + 1) { + drawKnob(kpos); + _kposPrev = kpos; + } + else { + int8_t dp = 1; + if (kpos < _kposPrev) dp = -1; + while (kpos != _kposPrev) {_kposPrev += dp; drawKnob(_kposPrev); delayMicroseconds(_usdelay);} + } + + _spr->deleteSprite(); +} + +/*************************************************************************************** +** Function name: drawKnob (private fn) +** Description: Draw the slider control knob at pixel kpos position +***************************************************************************************/ +void SliderWidget::drawKnob(uint16_t kpos) +{ + uint16_t x, y; + + if(_horiz) { + x = _xpos + kpos - 1; + y = _ypos - _kheight/2 - 1; + _spr->fillRect(0, _kheight/2 - _slotWidth/2 + 1, _kwidth + 2, _slotWidth, _slotColor); + } + else { + x = _xpos - _kwidth/2 - 1; + y = _ypos + kpos - 1; + _spr->fillRect(_kwidth/2 - _slotWidth/2 + 1, 0, _slotWidth, _kheight + 2, _slotColor); + } + + if (_usdelay == 0 && abs(kpos - _kposPrev) > (_horiz ? _kwidth : _kheight) + 1) { + if(_horiz) _spr->pushSprite(_xpos + _kposPrev, y); + else _spr->pushSprite(_xpos, y + _kposPrev); + } + + // Draw slider outline + _spr->fillSmoothRoundRect(1, 1, _kwidth, _kheight, _kradius, _kcolor1, _slotBgColor); + + // Draw marker stripe + if(_kcolor1 != _kcolor2) { + if(_horiz) { + _spr->drawFastVLine(_kwidth/2 + 1, 1, _kheight, _kcolor2); + } + else { + _spr->drawFastHLine(1, _kheight/2 + 1, _kwidth, _kcolor2); + } + } + _spr->pushSprite(x,y); +} + +/*************************************************************************************** +** Function name: getBoundingBox +** Description: Return the bounding box as coordinates +***************************************************************************************/ +void SliderWidget::getBoundingBox(int16_t *xs, int16_t *ys, int16_t *xe, int16_t *ye) +{ + // Bounds already corrected for Sprite wipe action + *xs = _sxs; + *ys = _sys; + *xe = _sxe; + *ye = _sye; +} + + +/*************************************************************************************** +** Function name: getBoundingRect +** Description: Return outside bounding box rectangle x,y and width,height +***************************************************************************************/ +void SliderWidget::getBoundingRect(int16_t *x, int16_t *y, uint16_t *w, uint16_t *h) +{ + // Corrected to be outside slider draw zone + *x = _sxs - 1; + *y = _sys - 1; + *w = _sxe - _sxs + 1; + *h = _sye - _sys + 1; +} diff --git a/src/widgets/slider/SliderWidget.h b/src/widgets/slider/SliderWidget.h new file mode 100644 index 0000000..ca67d14 --- /dev/null +++ b/src/widgets/slider/SliderWidget.h @@ -0,0 +1,108 @@ +/*************************************************************************************** + +***************************************************************************************/ +#ifndef _SliderWidgetH_ +#define _SliderWidgetH_ + +//Standard support +#include + +#include +// Created by Bodmer from widget sketch functions + +#define H_SLIDER true +#define V_SLIDER false + +typedef struct slider_t { + // createSlider + uint16_t slotWidth = 5; + uint16_t slotLength = 100; + uint16_t slotColor = TFT_GREEN; + uint16_t slotBgColor = TFT_BLACK; + bool orientation = true; + + // createKnob + uint16_t knobWidth = 21; + uint16_t knobHeight =21; + uint16_t knobRadius = 5; + uint16_t knobColor = TFT_WHITE; + uint16_t knobLineColor = TFT_RED; + + // setSliderScale + int16_t sliderLT = 0.0; + int16_t sliderRB = 100.0; + int16_t startPosition = 50; + uint16_t sliderDelay = 2000; + +} slider_param; + + +class SliderWidget : public TFT_eSPI { + + public: + + SliderWidget(TFT_eSPI *tft, TFT_eSprite *spr); + + void drawSlider(uint16_t x, uint16_t y); + void drawSlider(uint16_t x, uint16_t y, slider_t param); + + bool createSlider(uint16_t slotWidth, uint16_t slotLength, uint16_t slotColor, uint16_t bgColor, bool orientation); + + void createKnob(uint16_t kwidth, uint16_t kheight, uint16_t kradius, uint16_t kcolor1, uint16_t kcolor2); + + void setSliderScale(int16_t min, int16_t max, uint16_t usdelay); + void setSliderScale(int16_t min, int16_t max); + + void setSliderPosition(int16_t val); + int16_t getSliderPosition(void); + + void getBoundingBox(int16_t *xs, int16_t *ys, int16_t *xe, int16_t *ye); + void getBoundingRect(int16_t *x, int16_t *y, uint16_t *w, uint16_t *h); + + bool checkTouch(uint16_t tx, uint16_t ty); + + private: + void moveTo(int16_t val); + void drawKnob(uint16_t kpos); + + // createSlider + uint16_t _slotWidth; + uint16_t _slotLength; + uint16_t _slotColor; + uint16_t _slotBgColor; + bool _horiz; + + // createKnob + uint16_t _kwidth; + uint16_t _kheight; + uint16_t _kradius; + uint16_t _kcolor1; + uint16_t _kcolor2; + + // setSliderScale + int16_t _sliderMin = 0.0; + int16_t _sliderMax = 100.0; + uint16_t _usdelay = 0; + + // drawSlider + uint16_t _xpos = 0; + uint16_t _ypos = 0; + + bool _invert = false; + + // moveTo + int16_t _sliderPos; + uint16_t _kposPrev; + + + // checkTouch + uint16_t _sxs; + uint16_t _sys; + uint16_t _sxe; + uint16_t _sye; + + TFT_eSprite *_spr; + TFT_eSPI *_tft; +}; + +#endif \ No newline at end of file