#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "tft_ext.h"
#define TFT_DC 9
#define TFT_CS 10
//https://github.com/Bodmer/TFT_eSPI/blob/master/TFT_eSPI.h
#define BLACK 0x0000
#define WHITE 0xFFFF
#define GREEN 0x07E0
#define BLUE 0x001F
#define YELLOW 0xFFE0
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREY 0x2104
#define ORANGE 0xFDA0
// Meter colour schemes
#define RED2RED 0
#define GREEN2GREEN 1
#define BLUE2BLUE 2
#define BLUE2RED 3
#define GREEN2RED 4
#define RED2GREEN 5
TFTExt tft = TFTExt(TFT_CS, TFT_DC);
uint32_t runTime = -99999; // time for next update
int redNewReading = 0; // for red value to be displayed
int redPrevReading = 0; // for red value to be displayed
int grnNewReading = 0; // for green value to be displayed
int grnPrevReading = 0; // for green value to be displayed
int bluNewReading = 0; // for blue value to be displayed
int bluPrevReading = 0; // for blue value to be displayed
int d = 0; // Variable used for the sinewave test waveform
void setup()
{
tft.begin();
tft.setRotation(3);
tft.fillScreen(BLACK);
}
void loop()
{
if (millis() - runTime >= 1000L) { // Execute every 1s
runTime = millis();
// Test with a slowly changing value from a Sine function
d += 5; if (d >= 255) d = 0; //slowly increment the values by 5
int xpos = 0, ypos = 50, gap = 4, radius = 52;
////for(d = 0; d<= 255; d += 5) //slowly increment the values by 5
//{
redPrevReading = redNewReading;
redNewReading = 255 * abs(sineWave(d));
grnPrevReading = grnNewReading;
grnNewReading = 255 * abs(sineWave(d));
bluPrevReading = bluNewReading;
bluNewReading = 255 * abs(sineWave(d));
//}
//// Test with Sine wave function, normally reading will be from a sensor
//redNewReading = 255 * sineWave(d+0);
// Draw meter and get back x position of next meter
xpos = gap + ringMeter(redPrevReading, redNewReading, 0, 255, xpos, ypos, radius, "RED", RED2RED); // Draw red analogue meter
//grnNewReading = 255 * sineWave(d+60);
xpos = gap + ringMeter(grnPrevReading, grnNewReading, 0, 255, xpos, ypos, radius, "GREEN", GREEN2GREEN); // Draw green analogue meter
//bluNewReading = 255 * sineWave(d+10);
ringMeter(bluPrevReading, bluNewReading, 0, 255, xpos, ypos, radius, "BLUE", BLUE2BLUE); // Draw analogue meter
}
}
// #########################################################################
// Draw the meter on the screen, returns x coord of righthand side
// #########################################################################
int ringMeter(int prevVal, int newVal, int vmin, int vmax, int x, int y, int r, char *units, byte scheme)
{
int text_colour = 0; // To hold the text colour
// Minimum value of r is about 52 before value text intrudes on ring
// drawing the text first is an option
x += r; y += r; // Calculate coords of centre of ring
int w = r / 4; // Width of outer ring is 1/4 of radius
int angle = 150; // Half the sweep angle of meter (300 degrees)
int v = map(newVal, vmin, vmax, -angle, angle); // Map the value to an angle v
byte seg = 5; // Segments are 5 degrees wide = 60 segments for 300 degrees
byte inc = 10; // Draw segments every 5 degrees, increase to 10 for segmented ring
// Draw colour blocks every inc degrees
for (int i = -angle; i < angle; i += inc) {
// Choose colour from scheme
int colour = 0;
switch (scheme) {
case 0: colour = RED; break; // Fixed colour
case 1: colour = GREEN; break; // Fixed colour
case 2: colour = BLUE; break; // Fixed colour
case 3: colour = rainbow(map(i, -angle, angle, 0, 127)); break; // Full spectrum blue to red
case 4: colour = rainbow(map(i, -angle, angle, 63, 127)); break; // Green to red (high temperature etc)
case 5: colour = rainbow(map(i, -angle, angle, 127, 63)); break; // Red to green (low battery etc)
default: colour = BLUE; break; // Fixed colour
}
// Calculate pair of coordinates for segment start
float sx = cos((i - 90) * 0.0174532925);
float sy = sin((i - 90) * 0.0174532925);
uint16_t x0 = sx * (r - w) + x;
uint16_t y0 = sy * (r - w) + y;
uint16_t x1 = sx * r + x;
uint16_t y1 = sy * r + y;
// Calculate pair of coordinates for segment end
float sx2 = cos((i + seg - 90) * 0.0174532925);
float sy2 = sin((i + seg - 90) * 0.0174532925);
int x2 = sx2 * (r - w) + x;
int y2 = sy2 * (r - w) + y;
int x3 = sx2 * r + x;
int y3 = sy2 * r + y;
if (i < v) { // Fill in coloured segments with 2 triangles
tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour);
tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour);
text_colour = colour; // Save the last colour drawn
}
else // Fill in blank segments
{
tft.fillTriangle(x0, y0, x1, y1, x2, y2, GREY);
tft.fillTriangle(x1, y1, x2, y2, x3, y3, GREY);
text_colour = colour; // this needed so that the value get printed even if the bars are greyed out
}
}
// Convert value to a string
//char buf[3]; // up to 3 digit only
//byte len = 4; if (value > 999) len = 5;
// dtostrf(newVal, 3, 0, buf); // minimum width is 2 to cover 1-digits when refreshing
// Uncomment next line to set the text colour to the last segment value!
tft.setTextColor(text_colour, BLACK);
tft.fillRect(x - 40, y + 52, 90, 25, BLACK);
// draw value
if(newVal < 10 ) // 1-digit
drawInt(newVal, 3, x - 5, y + 55, 3, text_colour, BLACK); // the number subracted from x, i.e. 5, is done by trial and error. the higher the value, the more the text moves to the left
else if (newVal >= 10 && newVal < 100) // 2-digit
drawInt(newVal, 3, x - 15, y + 55, 3, text_colour, BLACK); // newVal, min len of digit, x, y, size, text color, text background color
else // 3-digit
drawInt(newVal, 3, x - 26, y + 55, 3, text_colour, BLACK);
// draw 'color'
drawCentreString(units, x, y + 2, 2, WHITE, BLACK);
return x + r;
}
// #########################################################################
// Return a 16 bit rainbow colour
// #########################################################################
unsigned int rainbow(byte value)
{
// Value is expected to be in range 0-127
// The value is converted to a spectrum colour from 0 = blue through to 127 = red
byte red = 0; // Red is the top 5 bits of a 16 bit colour value
byte green = 0;// Green is the middle 6 bits
byte blue = 0; // Blue is the bottom 5 bits
byte quadrant = value / 32;
if (quadrant == 0) {
blue = 31;
green = 2 * (value % 32);
red = 0;
}
if (quadrant == 1) {
blue = 31 - (value % 32);
green = 63;
red = 0;
}
if (quadrant == 2) {
blue = 0;
green = 63;
red = value % 32;
}
if (quadrant == 3) {
blue = 0;
green = 63 - 2 * (value % 32);
red = 31;
}
return (red << 11) + (green << 5) + blue;
}
//https://forum.arduino.cc/t/tft-gettextbounds/911559
uint16_t * drawCentreString(const char *buf, uint16_t x, uint16_t y, uint8_t textSize, uint16_t textColor, uint16_t bgColor)
{
int16_t x1, y1;
static uint16_t size[2] = {0, 0};
tft.setTextSize(textSize);
tft.setTextColor(textColor, bgColor);
tft.setTextWrap(false); //https://forums.adafruit.com/viewtopic.php?f=25&t=96909#p487371
tft.getTextBounds(buf, 0, y, &x1, &y1, &size[0], &size[1]); //calc width of new string
tft.setCursor(x - size[0] / 2, y - size[1] / 2);
//tft.print(buf);
tft.printNew(buf, buf, bgColor);
return size;
}
uint16_t * drawString(const char *buf, uint16_t x, uint16_t y, uint8_t textSize, uint16_t textColor, uint16_t bgColor)
{
static uint16_t size[2] = {0, 0};
tft.setTextSize(textSize);
tft.setTextColor(textColor, bgColor);
tft.setTextWrap(false); //https://forums.adafruit.com/viewtopic.php?f=25&t=96909#p487371
tft.setCursor(x, y);
//tft.print(buf);
tft.printNew(buf, buf, bgColor);
return size;
}
uint16_t * drawInt(int value, int numOfDigits, uint16_t x, uint16_t y, uint8_t textSize, uint16_t textColor, uint16_t bgColor)
{
//int16_t x1, y1;
static uint16_t size[2] = {0, 0};
tft.setTextSize(textSize);
tft.setTextColor(textColor, bgColor);
tft.setTextWrap(false); //https://forums.adafruit.com/viewtopic.php?f=25&t=96909#p487371
tft.setCursor(x, y);
tft.printNew(value, 3, bgColor);
return size;
}
float sineWave(int phase)
{
return sin(phase * 0.0174532925);
}