acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
TFT_ring_meter.ino (9396B)
1 /* 2 An example showing 'ring' analogue meter on a TFT 3 colour screen 4 5 Needs Fonts 2, 4 and 7 (also Font 6 if using a large size meter) 6 */ 7 8 // Meter colour schemes 9 #define RED2RED 0 10 #define GREEN2GREEN 1 11 #define BLUE2BLUE 2 12 #define BLUE2RED 3 13 #define GREEN2RED 4 14 #define RED2GREEN 5 15 16 #define TFT_GREY 0x2104 // Dark grey 16 bit colour 17 18 #include "Alert.h" // Out of range alert icon 19 20 #include <TFT_eSPI.h> // Hardware-specific library 21 #include <SPI.h> 22 23 TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height 24 25 uint32_t runTime = -99999; // time for next update 26 27 int reading = 0; // Value to be displayed 28 int d = 0; // Variable used for the sinewave test waveform 29 bool range_error = 0; 30 int8_t ramp = 1; 31 32 void setup(void) { 33 tft.begin(); 34 //Serial.begin(9600); 35 tft.setRotation(1); 36 37 tft.fillScreen(TFT_BLACK); 38 } 39 40 41 void loop() { 42 if (millis() - runTime >= 0L) { // Execute every TBD ms 43 runTime = millis(); 44 45 // Test with a slowly changing value from a Sine function 46 //d += 4; if (d >= 360) d = 0; 47 48 // Set the the position, gap between meters, and inner radius of the meters 49 int xpos = 0, ypos = 5, gap = 4, radius = 52; 50 51 // Draw meter and get back x position of next meter 52 53 // Test with Sine wave function, normally reading will be from a sensor 54 //reading = 250 + 250 * sineWave(d+0); 55 //xpos = gap + ringMeter(reading, 0, 500, xpos, ypos, radius, "mA", GREEN2RED); // Draw analogue meter 56 57 //reading = 20 + 30 * sineWave(d+60); 58 //xpos = gap + ringMeter(reading, -10, 50, xpos, ypos, radius, "degC", BLUE2RED); // Draw analogue meter 59 60 //reading = 50 + 50 * sineWave(d + 120); 61 //ringMeter(reading, 0, 100, xpos, ypos, radius, "%RH", BLUE2BLUE); // Draw analogue meter 62 63 64 // Draw two more larger meters 65 //xpos = 20, ypos = 115, gap = 24, radius = 64; 66 67 //reading = 1000 + 150 * sineWave(d + 90); 68 //xpos = gap + ringMeter(reading, 850, 1150, xpos, ypos, radius, "mb", BLUE2RED); // Draw analogue meter 69 70 //reading = 15 + 15 * sineWave(d + 150); 71 //xpos = gap + ringMeter(reading, 0, 30, xpos, ypos, radius, "Volts", GREEN2GREEN); // Draw analogue meter 72 73 // Draw a large meter 74 xpos = 480/2 - 160, ypos = 0, gap = 15, radius = 170; 75 reading +=(ramp); 76 if (reading>98) ramp = -1; 77 if (reading<0) ramp = 1; 78 // Comment out above meters, then uncomment the next line to show large meter 79 ringMeter(reading,0,100, xpos,ypos,radius," Watts",GREEN2RED); // Draw analogue meter 80 if (reading<0) delay(1000); 81 } 82 } 83 84 // ######################################################################### 85 // Draw the meter on the screen, returns x coord of righthand side 86 // ######################################################################### 87 int ringMeter(int value, int vmin, int vmax, int x, int y, int r, const char *units, byte scheme) 88 { 89 // Minimum value of r is about 52 before value text intrudes on ring 90 // drawing the text first is an option 91 92 x += r; y += r; // Calculate coords of centre of ring 93 94 int w = r / 3; // Width of outer ring is 1/4 of radius 95 96 int angle = 150; // Half the sweep angle of meter (300 degrees) 97 98 int v = map(value, vmin, vmax, -angle, angle); // Map the value to an angle v 99 100 byte seg = 3; // Segments are 3 degrees wide = 100 segments for 300 degrees 101 byte inc = 6; // Draw segments every 3 degrees, increase to 6 for segmented ring 102 103 // Variable to save "value" text colour from scheme and set default 104 int colour = TFT_BLUE; 105 106 // Draw colour blocks every inc degrees 107 for (int i = -angle+inc/2; i < angle-inc/2; i += inc) { 108 // Calculate pair of coordinates for segment start 109 float sx = cos((i - 90) * 0.0174532925); 110 float sy = sin((i - 90) * 0.0174532925); 111 uint16_t x0 = sx * (r - w) + x; 112 uint16_t y0 = sy * (r - w) + y; 113 uint16_t x1 = sx * r + x; 114 uint16_t y1 = sy * r + y; 115 116 // Calculate pair of coordinates for segment end 117 float sx2 = cos((i + seg - 90) * 0.0174532925); 118 float sy2 = sin((i + seg - 90) * 0.0174532925); 119 int x2 = sx2 * (r - w) + x; 120 int y2 = sy2 * (r - w) + y; 121 int x3 = sx2 * r + x; 122 int y3 = sy2 * r + y; 123 124 if (i < v) { // Fill in coloured segments with 2 triangles 125 switch (scheme) { 126 case 0: colour = TFT_RED; break; // Fixed colour 127 case 1: colour = TFT_GREEN; break; // Fixed colour 128 case 2: colour = TFT_BLUE; break; // Fixed colour 129 case 3: colour = rainbow(map(i, -angle, angle, 0, 127)); break; // Full spectrum blue to red 130 case 4: colour = rainbow(map(i, -angle, angle, 70, 127)); break; // Green to red (high temperature etc) 131 case 5: colour = rainbow(map(i, -angle, angle, 127, 63)); break; // Red to green (low battery etc) 132 default: colour = TFT_BLUE; break; // Fixed colour 133 } 134 tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour); 135 tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour); 136 //text_colour = colour; // Save the last colour drawn 137 } 138 else // Fill in blank segments 139 { 140 tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREY); 141 tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREY); 142 } 143 } 144 // Convert value to a string 145 char buf[10]; 146 byte len = 3; if (value > 999) len = 5; 147 dtostrf(value, len, 0, buf); 148 buf[len] = ' '; buf[len+1] = 0; // Add blanking space and terminator, helps to centre text too! 149 // Set the text colour to default 150 tft.setTextSize(1); 151 152 if (value<vmin || value>vmax) { 153 drawAlert(x,y+90,50,1); 154 } 155 else { 156 drawAlert(x,y+90,50,0); 157 } 158 159 tft.setTextColor(TFT_WHITE, TFT_BLACK); 160 // Uncomment next line to set the text colour to the last segment value! 161 tft.setTextColor(colour, TFT_BLACK); 162 tft.setTextDatum(MC_DATUM); 163 // Print value, if the meter is large then use big font 8, othewise use 4 164 if (r > 84) { 165 tft.setTextPadding(55*3); // Allow for 3 digits each 55 pixels wide 166 tft.drawString(buf, x, y, 8); // Value in middle 167 } 168 else { 169 tft.setTextPadding(3 * 14); // Allow for 3 digits each 14 pixels wide 170 tft.drawString(buf, x, y, 4); // Value in middle 171 } 172 tft.setTextSize(1); 173 tft.setTextPadding(0); 174 // Print units, if the meter is large then use big font 4, othewise use 2 175 tft.setTextColor(TFT_WHITE, TFT_BLACK); 176 if (r > 84) tft.drawString(units, x, y + 60, 4); // Units display 177 else tft.drawString(units, x, y + 15, 2); // Units display 178 179 // Calculate and return right hand side x coordinate 180 return x + r; 181 } 182 183 void drawAlert(int x, int y , int side, bool draw) 184 { 185 if (draw && !range_error) { 186 drawIcon(alert, x - alertWidth/2, y - alertHeight/2, alertWidth, alertHeight); 187 range_error = 1; 188 } 189 else if (!draw) { 190 tft.fillRect(x - alertWidth/2, y - alertHeight/2, alertWidth, alertHeight, TFT_BLACK); 191 range_error = 0; 192 } 193 } 194 195 // ######################################################################### 196 // Return a 16 bit rainbow colour 197 // ######################################################################### 198 unsigned int rainbow(byte value) 199 { 200 // Value is expected to be in range 0-127 201 // The value is converted to a spectrum colour from 0 = blue through to 127 = red 202 203 byte red = 0; // Red is the top 5 bits of a 16 bit colour value 204 byte green = 0;// Green is the middle 6 bits 205 byte blue = 0; // Blue is the bottom 5 bits 206 207 byte quadrant = value / 32; 208 209 if (quadrant == 0) { 210 blue = 31; 211 green = 2 * (value % 32); 212 red = 0; 213 } 214 if (quadrant == 1) { 215 blue = 31 - (value % 32); 216 green = 63; 217 red = 0; 218 } 219 if (quadrant == 2) { 220 blue = 0; 221 green = 63; 222 red = value % 32; 223 } 224 if (quadrant == 3) { 225 blue = 0; 226 green = 63 - 2 * (value % 32); 227 red = 31; 228 } 229 return (red << 11) + (green << 5) + blue; 230 } 231 232 // ######################################################################### 233 // Return a value in range -1 to +1 for a given phase angle in degrees 234 // ######################################################################### 235 float sineWave(int phase) { 236 return sin(phase * 0.0174532925); 237 } 238 239 240 //==================================================================================== 241 // This is the function to draw the icon stored as an array in program memory (FLASH) 242 //==================================================================================== 243 244 // To speed up rendering we use a 64 pixel buffer 245 #define BUFF_SIZE 64 246 247 // Draw array "icon" of defined width and height at coordinate x,y 248 // Maximum icon size is 255x255 pixels to avoid integer overflow 249 250 void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, int8_t height) { 251 252 uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel) 253 254 tft.startWrite(); 255 256 // Set up a window the right size to stream pixels into 257 tft.setAddrWindow(x, y, width, height); 258 259 // Work out the number whole buffers to send 260 uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE; 261 262 // Fill and send "nb" buffers to TFT 263 for (int i = 0; i < nb; i++) { 264 for (int j = 0; j < BUFF_SIZE; j++) { 265 pix_buffer[j] = pgm_read_word(&icon[i * BUFF_SIZE + j]); 266 } 267 tft.pushColors(pix_buffer, BUFF_SIZE); 268 } 269 270 // Work out number of pixels not yet sent 271 uint16_t np = ((uint16_t)height * width) % BUFF_SIZE; 272 273 // Send any partial buffer left over 274 if (np) { 275 for (int i = 0; i < np; i++) pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]); 276 tft.pushColors(pix_buffer, np); 277 } 278 279 tft.endWrite(); 280 } 281