/* An example analogue meter using a ILI9341 TFT LCD screen This example uses the hardware SPI only and a board based on the ATmega328 Needs Fonts 2, and 7 (also Font 4 if using large scale label) Comment out lines 153 and 197 to reduce needle flicker and to remove need for Font 4 (which uses ~8k of FLASH!) Alan Senior 23/2/2015 */ // These are the pins for the shield! #define YP A1 // must be an analog pin, use "An" notation! #define XM A2 // must be an analog pin, use "An" notation! #define YM 7 // can be a digital pin #define XP 6 // can be a digital pin #include // Core graphics library #include // Hardware-specific library #include #define GREY 0x5AEB #define LCD_CS A3 // Chip Select goes to Analog 3 #define LCD_CD A2 // Command/Data goes to Analog 2 #define LCD_WR A1 // LCD Write goes to Analog 1 #define LCD_RD A0 // LCD Read goes to Analog 0 #define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin Adafruit_ILI9341_8bit_AS tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); float ltx = 0; // Saved x coord of bottom of needle uint16_t osx = 120, osy = 120; // Saved x & y coords uint32_t updateTime = 0; // time for next update int old_analog = -999; // Value last displayed int old_digital = -999; // Value last displayed // Assign human-readable names to some common 16-bit color values: #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF #define ORANGE 0xF400 #define FONT_2 1 #define FONT_4 1 #define FONT_7 1 void setup(void) { // tft.init(); tft.setRotation(2); tft.reset(); delay(10); tft.begin(0x9341); tft.fillScreen(BLACK); analogMeter(); // Draw analogue meter digitalMeter(); // Draw digital meter updateTime = millis(); // Next update time } void loop() { if (updateTime <= millis()) { updateTime = millis() + 500; int reading = 0; reading = random(50, 100); // Test with random value // reading = map(analogRead(A0),0,1023,0,100); // Test with value form Analogue 0 showDigital(reading); // Update digital reading plotNeedle(reading, 8); // Update analogue meter, 8ms delay per needle increment } } // ######################################################################### // Draw the analogue meter on the screen // ######################################################################### void analogMeter() { // Meter outline tft.fillRect(0, 0, 239, 126, GREY); tft.fillRect(5, 3, 230, 119, WHITE); tft.setTextColor(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; // Coodinates of tick to draw float sx = cos((i - 90) * 0.0174532925); float sy = sin((i - 90) * 0.0174532925); uint16_t x0 = sx * (100 + tl) + 120; uint16_t y0 = sy * (100 + tl) + 140; uint16_t x1 = sx * 100 + 120; uint16_t y1 = 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 = sx2 * (100 + tl) + 120; int y2 = sy2 * (100 + tl) + 140; int x3 = sx2 * 100 + 120; int y3 = sy2 * 100 + 140; // Yellow zone limits if (i >= -50 && i < 0) { tft.fillTriangle(x0, y0, x1, y1, x2, y2, YELLOW); tft.fillTriangle(x1, y1, x2, y2, x3, y3, YELLOW); } // Green zone limits if (i >= 0 && i < 25) { tft.fillTriangle(x0, y0, x1, y1, x2, y2, GREEN); tft.fillTriangle(x1, y1, x2, y2, x3, y3, GREEN); } // Orange zone limits if (i >= 25 && i < 50) { tft.fillTriangle(x0, y0, x1, y1, x2, y2, ORANGE); tft.fillTriangle(x1, y1, x2, y2, x3, y3, ORANGE); } // Short scale tick length if (i % 25 != 0) tl = 8; // Recalculate coords incase tick lenght changed x0 = sx * (100 + tl) + 120; y0 = sy * (100 + tl) + 140; x1 = sx * 100 + 120; y1 = sy * 100 + 140; // Draw tick tft.drawLine(x0, y0, x1, y1, BLACK); // Check if labels should be drawn, with position tweaks if (i % 25 == 0) { // Calculate label positions x0 = sx * (100 + tl + 10) + 120; y0 = sy * (100 + tl + 10) + 140; switch (i / 25) { case -2: tft.drawCentreString("0", x0, y0 - 12, FONT_2); break; case -1: tft.drawCentreString("25", x0, y0 - 9, FONT_2); break; case 0: tft.drawCentreString("50", x0, y0 - 6, FONT_2); break; case 1: tft.drawCentreString("75", x0, y0 - 9, FONT_2); break; case 2: tft.drawCentreString("100", x0, y0 - 12, FONT_2); break; } } // Now draw the arc of the scale sx = cos((i + 5 - 90) * 0.0174532925); sy = sin((i + 5 - 90) * 0.0174532925); x0 = sx * 100 + 120; y0 = sy * 100 + 140; // Draw scale arc, don't draw the last part if (i < 50) tft.drawLine(x0, y0, x1, y1, BLACK); } tft.drawString("%RH", 5 + 230 - 40, 119 - 20, FONT_2); // Units at bottom right tft.drawCentreString("%RH", 120, 70, FONT_4); // Comment out to avoid font 4 tft.drawRect(5, 3, 230, 119, BLACK); // Draw bezel line plotNeedle(0,0); // Put meter needle at 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 plotNeedle(int value, byte ms_delay) { tft.setTextColor(BLACK, WHITE); tft.setTextSize(1); char buf[8]; dtostrf(value, 4, 0, buf); tft.drawRightString(buf, 40, 119 - 20, FONT_2); if (value < -10) value = -10; // Limit value to emulate needle end stops if (value > 110) value = 110; // Move the needle util 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 // Calcualte 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 tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, WHITE); tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, WHITE); tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, WHITE); // Re-plot text under needle tft.setTextColor(BLACK); tft.drawCentreString("%RH", 120, 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 postion, magenta makes needle a bit bolder // draws 3 lines to thicken needle tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, RED); tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, MAGENTA); tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, RED); // Slow needle down slightly as it approaches new postion if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5; // Wait before next update delay(ms_delay); } } // ######################################################################### // Draw 3 digit digital meter with faint 7 segment image // ######################################################################### void digitalMeter() { int xpos = 118, ypos = 134; // was 134 tft.fillRect(xpos - 52, ypos - 5, 2 * 50, 50, GREY); tft.fillRect(xpos - 49, ypos - 2, 2 * 47, 44, BLACK); tft.setTextSize(4); tft.setTextColor(7 << 11, BLACK); // Plot over numbers in dim red tft.drawString("888", xpos - 48, ypos+1, FONT_7); } // ######################################################################### // Update digital meter reading // ######################################################################### void showDigital(int value) { if (value==old_digital) return; // return if no change to prevent flicker if (value < 0) value = 0; //Constrain lower limit to 0 if (value > 999) value = 999; //Constrain upper limit to 999 int xpos = 118, ypos = 134+1; // Position with location tweak tft.setTextSize(5); tft.setTextColor(7 << 11, BLACK); // Plot over numbers in dim red tft.drawString("888", xpos - 47, ypos, FONT_7); //Erase old value // Nb. 32 pixels wide +2 gap per digit // Update with new value tft.setTextColor(RED); // Dont draw background to leave dim segments if (value < 10) tft.drawNumber(value, xpos+19, ypos, FONT_7); else if (value < 100) tft.drawNumber(value, xpos - 14, ypos, FONT_7); else tft.drawNumber(value, xpos - 47, ypos, 7); old_digital = value; }