MCUFriend 3.5"TFT Arcing Bar Graph Coding Help - Page 1
Forum section: electronics / general

author: Inverted18650
author_rank: Frequent Contributor
timestamp: February 07, 2018, 10:03:47 pm
content: I bought some 3.5" LCDs a few days ago and I have been playing with examples from several libraries since they arrived. I am trying to make an "arcing bar graph" to indicate voltage that it reads from a ADS1115 ADC. My display works with the MCUFriend library and/or the UTFTGLUE for tft. and myGLCD. command prompts respectively. The only LCD constructor (if that's the correct term) that works is "UTFTGLUE myGLCD(0x9488,A2,A1,A3,A4,A0)."bar graph code:https://github.com/HX2003/color_bar_tft/blob/master/color_bar_tft.inoarc code:https://forum.arduino.cc/index.php?topic=413186.0After several hours, I was able to get the above sketches to work by themselves. Can someone help me combine these two sketches into a single arcing bar graph.This is what my bar graph code looks like now and I have included a pic of the LCD rendering for both. The last phot is from the "diagnose_TFT_support" sketch in the MCUFriend Examples.#include <Adafruit_GFX.h>#include <MCUFRIEND_kbv.h>MCUFRIEND_kbv tft;#include <UTFTGLUE.h>UTFTGLUE myGLCD(0x9488,A2,A1,A3,A4,A0);//Colors by HEX#define LTBLUE 0xB6DF#define LTTEAL 0xBF5F#define LTGREEN 0xBFF7#define LTCYAN 0xC7FF#define LTRED 0xFD34#define LTMAGENTA 0xFD5F#define LTYELLOW 0xFFF8#define LTORANGE 0xFE73#define LTPINK 0xFDDF#define LTPURPLE 0xCCFF#define LTGREY 0xE71C#define BLUE 0x001F#define TEAL 0x0438#define GREEN 0x07E0#define CYAN 0x07FF#define RED 0xF800#define MAGENTA 0xF81F#define YELLOW 0xFFE0#define ORANGE 0xFD20#define PINK 0xF81F#define PURPLE 0x801F#define GREY 0xC618#define WHITE 0xFFFF#define BLACK 0x0000#define DKBLUE 0x000D#define DKTEAL 0x020C#define DKGREEN 0x03E0#define DKCYAN 0x03EF#define DKRED 0x6000#define DKMAGENTA 0x8008#define DKYELLOW 0x8400#define DKORANGE 0x8200#define DKPINK 0x9009#define DKPURPLE 0x4010#define DKGREY 0x4A49//Color table for scalebyte rgbtable{{0,0,255},{0,32,255},{0,64,255},{0,96,255},{0,128,255},{0,160,255},{0,192,255},{0,224,255},{0,255,255},{0,255,224},{0,255,192},{0,255,160},{0,255,128},{0,255,96},{0,255,64},{0,255,32},{0,255,0},{32,255,0},{64,255,0},{96,255,0},{128,255,0},{160,255,0},{192,255,0},{224,255,0},{255,255,0},{255,224,0},{255,192,0},{255,160,0},{255,128,0},{255,96,0},{255,64,0},{255,32,0},{255,0,0}};uint16_t Control1, eightcolour;//this *and the next 4 lines* are to make sure the TFT LCD functionsuint16_t ID;void setup(void){ID = myGLCD.readID();if (ID == 0xB509) Control1 = myGLCD.readReg(0x00B);else Control1 = myGLCD.readReg(0x007);myGLCD.InitLCD();//start LCDmyGLCD.setRotation(1);//set orientation of LCD 0=port, 1= landscape 90, 2=port 180, 3=landscape 270myGLCD.fillScreen(DKORANGE);//set background color}//----------------------------------------------------------//drawColorBar(value 0-32, x coordinate of left of graph, y coordinate of top left corner of graph, size);void drawColorBar(int value,int spotx, int spoty, int pix){for(int i=0;i<33;i++){if(i<=value){myGLCD.setColor(rgbtable,rgbtable,rgbtable);} else {myGLCD.setColor(GREY);}myGLCD.fillRect(spotx+(i*pix),spoty,spotx+(i*pix)+pix/2,spoty+38);//last digit was 18, now 38, controls length of boxes}/*//used later to add boxes to receive actual value and print to LCDmyGLCD.setColor(BLACK);myGLCD.fillRect(spotx+(32*pix)+58,spoty,spotx+(32*pix)+90,spoty+17);myGLCD.setColor(rgbtable,rgbtable,rgbtable);myGLCD.print("V: "+String(value)+"",spotx+(32*pix)+10,spoty+4);*/}void loop() {for(int i=0; i<33; i++){//drawColorBar(i,0,35,3);//more rows of graphs, smallest to largest//drawColorBar(i,0,65,4);//drawColorBar(i,0,95,5);drawColorBar(i,0,50,6);drawColorBar(i,0,100,7);drawColorBar(i,0,150,8);drawColorBar(i,0,200,9);drawColorBar(i,0,250,10);delay(1);}myGLCD.setColor(RED);for(int i=32; i>=0; i--){//drawColorBar(i,0,35,3);//drawColorBar(i,0,65,4);//drawColorBar(i,0,95,5);drawColorBar(i,0,50,6);drawColorBar(i,0,100,7);drawColorBar(i,0,150,8);drawColorBar(i,0,200,9);drawColorBar(i,0,250,10);delay(1);}}

author: djacobow
author_rank: Super Contributor
timestamp: February 07, 2018, 10:20:04 pm
content: All the clues you need are in the sketches themselves. The bargraph draws a series of rectangles up the required level, changing the color as they go. The arc instead draws two triangles together with each step so that they form a trapezoid with the outside edges with the appropriate angle for the place in the arc.What is it you want help with? Understanding C++? Understanding the math behind the triangles?

author: Inverted18650
author_rank: Frequent Contributor
timestamp: February 07, 2018, 11:25:59 pm
content: My apologies, I need help with the math of the triangles & understanding how to draw them in the correct location with proper spacing/interval. Should I try to do them one at a time, or study this function and try to alter it? There are other libraries for this, that seem much easier to use, but they are not compatible with my LCD or at least, I cannot get the code correct. I also tried to use the 'geo.(&myGLCD)' but it was not supported. Any help at all would be greatly appreciated, as I have very little experience in this realm but want to branch out from the basic 20x4 LCD's.

author: Inverted18650
author_rank: Frequent Contributor
timestamp: February 07, 2018, 11:55:22 pm
content: This is my "working" arc code (just draws arc, from original sketch). I am going through it, line by line, adding comments to whatever I can decipher. Here is what I have so far.#include <Adafruit_GFX.h>#include <MCUFRIEND_kbv.h>MCUFRIEND_kbv tft;#include <UTFTGLUE.h>UTFTGLUE myGLCD(0x9488,A2,A1,A3,A4,A0);#define DEG2RAD 0.0174532925//to convert degrees to radians//color table#define LTBLUE 0xB6DF#define LTTEAL 0xBF5F#define LTGREEN 0xBFF7#define LTCYAN 0xC7FF#define LTRED 0xFD34#define LTMAGENTA 0xFD5F#define LTYELLOW 0xFFF8#define LTORANGE 0xFE73#define LTPINK 0xFDDF#define LTPURPLE 0xCCFF#define LTGREY 0xE71C#define BLUE 0x001F#define TEAL 0x0438#define GREEN 0x07E0#define CYAN 0x07FF#define RED 0xF800#define MAGENTA 0xF81F#define YELLOW 0xFFE0#define ORANGE 0xFD20#define PINK 0xF81F#define PURPLE 0x801F#define GREY 0xC618#define WHITE 0xFFFF#define BLACK 0x0000#define DKBLUE 0x000D#define DKTEAL 0x020C#define DKGREEN 0x03E0#define DKCYAN 0x03EF#define DKRED 0x6000#define DKMAGENTA 0x8008#define DKYELLOW 0x8400#define DKORANGE 0x8200#define DKPINK 0x9009#define DKPURPLE 0x4010#define DKGREY 0x4A49uint16_t Control1, eightcolour;//lcd color supportuint16_t ID;//stores driver IDvoid setup(void){ID = myGLCD.readID(); //from example,reads IDif (ID == 0xB509) Control1 = myGLCD.readReg(0x00B);else Control1 = myGLCD.readReg(0x007);myGLCD.InitLCD();//start LCDmyGLCD.setRotation(1);//0=port, 1=land_90,2=port_180,3=land_270myGLCD.fillScreen(TEAL);//background color}void loop() {int w =15;//thickness?/pixels, was 4, "int n" is the thicknessint rx = 200;//distance from radius(x) of circle?int ry = 150;//distance from radius (y) of circle?for (int n = 0; n < 2; n++) { // "int n<" changes number of rows of trianglesfillArc(240, 160, 300, 40, rx-n*w, ry-n*w, w, 31-n*6);}while(1);}// #########################################################################// Draw an arc with a defined thickness// #########################################################################// x,y == coords of centre of arc// start_angle = 0 - 359// seg_count = number of 3 degree segments to draw (120 => 360 degree arc)// rx = x axis radius// yx = y axis radius// w = width (thickness) of arc in pixels// colour = 16 bit colour value// Note if rx and ry are the same an arc of a circle is drawnint fillArc(int x, int y, int start_angle, int seg_count, int rx, int ry, int w, unsigned int colour){byte seg = 3; // Segments are 3 degrees wide = 120 segments for 360 degreesbyte inc = 3; // Draw segments every 3 degrees, increase to 6 for segmented ring// Draw colour blocks every inc degreesfor (int i = start_angle; i < start_angle + seg * seg_count; i += inc) {// Calculate pair of coordinates for segment startfloat sx = cos((i - 90) * DEG2RAD);float sy = sin((i - 90) * DEG2RAD);uint16_t x0 = sx * (rx - w) + x;uint16_t y0 = sy * (ry - w) + y;uint16_t x1 = sx * rx + x;uint16_t y1 = sy * ry + y;// Calculate pair of coordinates for segment endfloat sx2 = cos((i + seg - 90) * DEG2RAD);float sy2 = sin((i + seg - 90) * DEG2RAD);int x2 = sx2 * (rx - w) + x;int y2 = sy2 * (ry - w) + y;int x3 = sx2 * rx + x;int y3 = sy2 * ry + y;myGLCD.fillTriangle(x0, y0, x1, y1, x2, y2,DKRED);myGLCD.fillTriangle(x1, y1, x2, y2, x3, y3, DKMAGENTA);}

author: Inverted18650
author_rank: Frequent Contributor
timestamp: February 08, 2018, 12:27:29 am
content: void loop() {int w =40; //height of pixels, was 4, "int n" is the thicknessint rx = 200;//distance from radius(x) of circle?int ry = 150;//distance from radius (y) of circle?for (int n = 0; n < 1; n++) { // "int n<" changes number of rows of trianglesfillArc(240, 160, 300,8(<- THIS ONE), rx-n*w, ry-n*w, w, 31-n*6);In the code above, I changed the originL 40 to an 8, and now have a row of double trianlges that is 8 segments long on the screen. I would like to be able to add 4 more and be able to independently control the color of each of the 5 segments, and control the color variations within each segment, from light to dark, or normal to neon...That is the general idea anyway, but I may have to draw each of the 40 "double triangles" by themselves and color them individually.

author: djacobow
author_rank: Super Contributor
timestamp: February 08, 2018, 01:17:02 am
content: >> would like to be able to add 4 more and be able to independently control the color of each of the 5 segments, and control the color variations within each segment, from light to dark, or normal to neon...That is right. You will have to draw a segment of the arc, change the color, draw the next segment, etc.Personally, I would work this out on my local computer using a simple language like python first, and once I had that working, I'd convert to the target platform.Here is a slightly annotated Python script that uses ye olde fashioned turtle graphics. Obviously, you cannot use the code, but it shows you how the math works. If you can install python with the turtle module, you should be able to run it straight away.A couple of things to note:* rather than store a table of colors, it will take up less space overall to generate the colors on the fly. Look at my function to do so.* otoh, if you can remove all floating point and the transcendental functions, you can save a lot of code space.* I did nothing to optimize this code. There are lots of places here to reuse variables rather than recalculate them. I'll let you sort that out.Code:#!/usr/bin/env python3import timeimport turtleimport mathclass arcMaker():def __init__(self):self.center = [0,0] # where the fulcrum of the "needle" isself.diameter = 300 # how long the needle isself.thickness = 40 # how thick the drawn line isself.min_angle = 30 # where to start the arc; where is zeroself.max_angle = 150 # where 100% arc endsself.slivers = 100 # the number of "segments" -- more is finer# image, fewer is faster# this is only to make our drawing primitive match to the one in# the user's library. This one draws a triangle given three pointsdef makeTriangle(self,p1,p2,p3,color):turtle.penup()turtle.goto(p1)turtle.pencolor(*color)turtle.pendown()turtle.goto(p2)turtle.goto(p3)turtle.goto(p1)turtle.penup()# python does trig in radians. Let's work in degrees, and have them# go clockwise rather than CCW as is more normaldef degCos(self,x):x = 180 - xreturn math.cos(x * math.pi / 180)def degSin(self,x):x = 180 - xreturn math.sin(x * math.pi / 180)# make a color from the rainbow based on a fraction. This is not# quite rainbow order, but it is very, very simple. these are on [0,1]# rather then [0,255] so, if you need the latter, just multiply by 255.def fractionToColor(self,fract):r = fractg = 1 - 2 * abs(fract-0.5)b = 1-fractreturn (r,g,b)# draws a single trapezoid segment of the arcdef makeArcSegment(self, start_angle, end_angle, color):# calculate four points that define a trapezoid that# we will want to draw.# inner leftp0 = [self.degCos(start_angle) * self.diameter,self.degSin(start_angle) * self.diameter]# outer leftp1 = [self.degCos(start_angle) * (self.diameter + self.thickness),self.degSin(start_angle) * (self.diameter + self.thickness)]# outer rightp2 = [self.degCos(end_angle) * (self.diameter + self.thickness),self.degSin(end_angle) * (self.diameter + self.thickness)]# inner rightp3 = [self.degCos(end_angle) * self.diameter,self.degSin(end_angle) * self.diameter]# translate those points if we want an origin other than [0,0]def addPoints(a,b):return [ a + b, a + b ]p0 = addPoints(p0, self.center)p1 = addPoints(p1, self.center)p2 = addPoints(p2, self.center)p3 = addPoints(p3, self.center)# draw the trapezoid as two trianglesself.makeTriangle(p0,p1,p2,color)self.makeTriangle(p2,p3,p0,color)# draws an arc, taking a fraction [0,1] as an argumentdef makeArc(self, fraction):final_angle = self.min_angle + fraction * (self.max_angle - self.min_angle)current_angle = self.min_anglecurrent_fraction = 0while current_angle < final_angle:print(current_angle)current_plus_1 = current_angle + (self.max_angle - self.min_angle) / self.sliversself.makeArcSegment(current_angle, current_plus_1, self.fractionToColor(current_fraction))current_angle += (self.max_angle - self.min_angle) / self.sliverscurrent_fraction += 1 / self.sliversif __name__ == '__main__':turtle.speed(0)am = arcMaker()am.makeArc(1)while True:time.sleep(1)

author: Inverted18650
author_rank: Frequent Contributor
timestamp: February 08, 2018, 01:21:11 am
content: Thank you for your assistance. After I study your functions, I will take your advice and try to get it going in "Processing" first.Update: thanks again, it finally makes sense and it’s coming along much faster now.

author: Inverted18650
author_rank: Frequent Contributor
timestamp: February 08, 2018, 02:32:38 am
content: https://github.com/Bodmer/TFT_HX8357/blob/master/examples/Meter_ring_HX8357_1/Meter_ring_HX8357_1.inoAfter a bit more searching, I found this library. I will go to the Arduino forum and try to help converting it for my display. I think its perfect for my projects.

author: Inverted18650
author_rank: Frequent Contributor
timestamp: February 09, 2018, 03:21:36 pm
content: I was able to speak with the creator of the MCUFriend library on the Arduino forum and now the "Meter_Ring" sketch works with my setup.https://forum.arduino.cc/index.php?topic=366304.2175Code:/*An example showing 'ring' analogue meter on a HX8357 TFTcolour screenNeeds Fonts 2, 4 and 7 (also Font 6 if using a large size meter)*/// Meter colour schemes#define RED2RED 0#define GREEN2GREEN 1#define BLUE2BLUE 2#define BLUE2RED 3#define GREEN2RED 4#define RED2GREEN 5#include "TFT_HX8357GLUE.h"TFT_HX8357GLUE tft;#ifndef AVR#include <avr/dtostrf.h>#endif#define HX8357_GREY 0x2104 // Dark grey 16 bit colouruint32_t runTime = -99999; // time for next updateint reading = 0; // Value to be displayedint d = 0; // Variable used for the sinewave test waveformboolean alert = 0;int8_t ramp = 1;void setup(void) {tft.begin();//Serial.begin(9600);tft.setRotation(1);tft.fillScreen(HX8357_BLACK);}void loop() {if (millis() - runTime >= 0L) { // Execute every 2srunTime = millis();// Test with a slowly changing value from a Sine function//d += 4; if (d >= 360) d = 0;// Set the the position, gap between meters, and inner radius of the metersint xpos = 0, ypos = 5, gap = 4, radius = 52;// Draw meter and get back x position of next meter// Test with Sine wave function, normally reading will be from a sensor//reading = 250 + 250 * sineWave(d+0);//xpos = gap + ringMeter(reading, 0, 500, xpos, ypos, radius, "mA", GREEN2RED); // Draw analogue meter//reading = 20 + 30 * sineWave(d+60);//xpos = gap + ringMeter(reading, -10, 50, xpos, ypos, radius, "degC", BLUE2RED); // Draw analogue meter//reading = 50 + 50 * sineWave(d + 120);//ringMeter(reading, 0, 100, xpos, ypos, radius, "%RH", BLUE2BLUE); // Draw analogue meter// Draw two more larger meters//xpos = 20, ypos = 115, gap = 24, radius = 64;//reading = 1000 + 150 * sineWave(d + 90);//xpos = gap + ringMeter(reading, 850, 1150, xpos, ypos, radius, "mb", BLUE2RED); // Draw analogue meter//reading = 15 + 15 * sineWave(d + 150);//xpos = gap + ringMeter(reading, 0, 30, xpos, ypos, radius, "Volts", GREEN2GREEN); // Draw analogue meter// Draw a large meterxpos = 480/2 - 160, ypos = 0, gap = 15, radius = 170;reading +=(ramp);if (reading>98) ramp = -1;if (reading<0) ramp = 1;// Comment out above meters, then uncomment the next line to show large meterringMeter(reading,0,100, xpos,ypos,radius," Watts",GREEN2RED); // Draw analogue meter}}// #########################################################################// Draw the meter on the screen, returns x coord of righthand side// #########################################################################int ringMeter(int value, int vmin, int vmax, int x, int y, int r, char *units, byte scheme){// Minimum value of r is about 52 before value text intrudes on ring// drawing the text first is an optionx += r; y += r; // Calculate coords of centre of ringint w = r / 3; // Width of outer ring is 1/4 of radiusint angle = 150; // Half the sweep angle of meter (300 degrees)int v = map(value, vmin, vmax, -angle, angle); // Map the value to an angle vbyte seg = 3; // Segments are 3 degrees wide = 100 segments for 300 degreesbyte inc = 6; // Draw segments every 3 degrees, increase to 6 for segmented ring// Variable to save "value" text colour from scheme and set defaultint colour = HX8357_BLUE;// Draw colour blocks every inc degreesfor (int i = -angle+inc/2; i < angle-inc/2; i += inc) {// Calculate pair of coordinates for segment startfloat 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 endfloat 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 trianglesswitch (scheme) {case 0: colour = HX8357_RED; break; // Fixed colourcase 1: colour = HX8357_GREEN; break; // Fixed colourcase 2: colour = HX8357_BLUE; break; // Fixed colourcase 3: colour = rainbow(map(i, -angle, angle, 0, 127)); break; // Full spectrum blue to redcase 4: colour = rainbow(map(i, -angle, angle, 70, 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 = HX8357_BLUE; break; // Fixed colour}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, HX8357_GREY);tft.fillTriangle(x1, y1, x2, y2, x3, y3, HX8357_GREY);}}// Convert value to a stringchar buf;byte len = 3; if (value > 999) len = 5;dtostrf(value, len, 0, buf);buf = ' '; buf[len+1] = 0; // Add blanking space and terminator, helps to centre text too!// Set the text colour to defaulttft.setTextSize(1);if (value<vmin || value>vmax) {drawAlert(x,y+90,50,1);}else {drawAlert(x,y+90,50,0);}tft.setTextColor(HX8357_WHITE, HX8357_BLACK);// Uncomment next line to set the text colour to the last segment value!tft.setTextColor(colour, HX8357_BLACK);tft.setTextDatum(MC_DATUM);// Print value, if the meter is large then use big font 8, othewise use 4if (r > 84) {tft.setTextPadding(55*3); // Allow for 3 digits each 55 pixels widetft.drawString(buf, x, y, 8); // Value in middle}else {tft.setTextPadding(3 * 14); // Allow for 3 digits each 14 pixels widetft.drawString(buf, x, y, 4); // Value in middle}tft.setTextSize(1);tft.setTextPadding(0);// Print units, if the meter is large then use big font 4, othewise use 2tft.setTextColor(HX8357_WHITE, HX8357_BLACK);if (r > 84) tft.drawString(units, x, y + 60, 4); // Units displayelse tft.drawString(units, x, y + 15, 2); // Units display// Calculate and return right hand side x coordinatereturn x + r;}void drawAlert(int x, int y , int side, boolean draw){if (draw && !alert) {tft.fillTriangle(x, y, x+30, y+47, x-30, y+47, rainbow(95));tft.setTextColor(HX8357_BLACK);tft.drawCentreString("!", x, y + 6, 4);alert = 1;}else if (!draw) {tft.fillTriangle(x, y, x+30, y+47, x-30, y+47, HX8357_BLACK);alert = 0;}}// #########################################################################// 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 = redbyte red = 0; // Red is the top 5 bits of a 16 bit colour valuebyte green = 0;// Green is the middle 6 bitsbyte blue = 0; // Blue is the bottom 5 bitsbyte 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;}// #########################################################################// Return a value in range -1 to +1 for a given phase angle in degrees// #########################################################################float sineWave(int phase) {return sin(phase * 0.0174532925);}I a, still working on my "Processing" sketch as well. I am learning to code in gerneral and learning more about the displays each day. I look forward to updating this project and once I better understand hw each driver interacts with each model Arduino, I will design and order some new conversion PCBs from JLCPCB.