Subo librería de voltimetro TFT con aguja

Subo librería de voltimetro TFT con aguja
Es una reforma de uno que hice hace mas de 20 años para PC con msdos…viejos tiempos.
Adaptado a TFT y Arduino sirve. No es excesivamente correcto el código pero funciona bien.

Se puede modificar a gusto y adaptarlo.
Subo un ejemplo con cuatro distintos instrumentos para que se pueda ver el uso.

Solo pido un favor, a quien lo mejore, modifique , o cambien para usarlo en otras TFTS que suba el código cuando este probado.

Subo el código en varios post porque es largo

volt_cir.h

// nombre(puntero a tft, x inicio, y inicio, diametro, max_valor_impreso, max (escala), divisiones , rojo, emp angulo, fin angulo, simetrico);

//puntero es un puntero a la pantalla TFT que vamos a utilizar
// x inicio - Y inicio son las coordenadas de posicion del instrumnto en pantalla TFT
//diametro es el diametro del circulo de medida
//max_valor_impreso es el valor maximo de la escala impresa del instrumento
//max es el valor maximo a representar por la aguja del instrumento (fondo de escala)
//divisiones es el numero de segmentos de la representacion de la escala del instrumento
// rojo es el numero de segmentos de escala que estaran indicados en rojo como de alerta , si es simetrico lo representara en la parte negativa y la positiva
//emp angulo es el angulo en grados de inicio del circulo de medida
//fin angulo es el angulo en grados del final del circulo de medida
//simetrico == 1 es un instrumento de medida simetrico con el cero central

#pragma once

#ifndef Volt_cir_h
#define Volt_cir_h

#if defined(__AVR__)
#include "Arduino.h"
#elif defined(__PIC32MX__)
#include "WProgram.h"
#elif defined(__arm__)
#include "Arduino.h"
#endif

#include <UTFT.h>
#include <UTFT_Geometry.h>

#if ((!defined(UTFT_VERSION)) || (UTFT_VERSION<241))
#error : You will need UTFT v2.41 or higher to use this add-on library...
#endif

extern uint8_t SmallFont[];
extern uint8_t SevenSegNumFont[];
extern uint8_t BigFont[];
extern uint8_t Dingbats1_XL[];

class VOLT_CIR
{
public:

	

	VOLT_CIR(UTFT *ptrUTFT, int posicion_x, int posicion_y, int diametro, double max_valor, double max_escala, int numero_divisiones, int valor_rojo, double emp_ang, double fin_ang, int simetrico);
	void chasis(void);
	void aguja(double valor);

private:

	int xcentro, ycentro, vol_x, vol_y;
	int tamano, escala;
	int radio, radio_aguja;
	double divisiones, rojo, color, max;
	int sim;
	double posx, posy, old_posx, old_posy;
	double ro, startAngle, endAngle, start_rad, end_rad, start_rad_rojo, recorrido_aguja;

protected:

	UTFT		*_UTFT;
	word		 _color_background, _color_border, _color_hilite;
	
};
#endif

1º parte de volt_cir,.cpp

#include <VOLT_CIR.h>
#include <UTFT.h>
#include <UTFT_Geometry.h>

// nombre(puntero a tft, x inicio, y inicio, diametro, max_valor_impreso,max escala, divisiones , rojo,emp angulo, fin angulo,int simetrico);

VOLT_CIR::VOLT_CIR(UTFT *ptrUTFT, int posicion_x, int posicion_y, int diametro, double max_valor,double max_escala, int numero_divisiones,int valor_rojo, double emp_cir,double fin_cir, int simetrico)
{
	// puntero a pantalla
	
	_UTFT = ptrUTFT;
  
	// variables de uso de la clase y calculos iniciales

	max = max_valor;
	
	// limita el valor maximo
	if (max >1000)
		max = 1000;
	if (max < 1)
		max = 1;
	
	escala = max_escala;

	// limita el tamaño del voltimetro

	radio = diametro / 2;
	if (radio > 100)
		radio = 100;
	if (radio < 25)
		radio = 25;

	
	radio_aguja = radio - (radio / 6);
	xcentro = posicion_x + radio;
	ycentro = posicion_y + radio ;
	
	divisiones = numero_divisiones;
	
	// limita el numero de divisiones de la escala a 10
	if (divisiones > 10)
		divisiones = 10;
	
	if (simetrico == 1)	// si el un instrumento simetrico tiene que tener numero de divisiones impar
		if (divisiones / 2 != int(divisiones / 2))	//comprueba si es impar
			divisiones = divisiones + 1;		// si no lo es suma 1 para que lo sea
	
	rojo = divisiones  - valor_rojo;
	
	startAngle = emp_cir;  // angulo de inicio de el medidor en grados
	endAngle = fin_cir;		// angulo final del medidor en grados

	// calculamos en radianes los grados que hay entre division y division de las marcas de la escala

	ro = ((abs(startAngle - endAngle) / divisiones) * 3.14) / 180;

	// calcula en radianes el angulo de recorrido de la aguja maximo entre max y minimo 
	
	recorrido_aguja = abs(startAngle - endAngle) * 3.14 / 180;
	sim = simetrico;
}

2º parte de volt_cir.cpp

void VOLT_CIR::chasis(void)
{
	
	int a;
	double radio_let = 0;
	double radio_rojo = 0;
	double px, py, cx, cy, thickness;
	double startRojo, endRojo;
	signed int numero;

	//Pasamos los grados a radianes ya que sin() y cos() se parametrizan en radianes

	start_rad = (startAngle * 3.14) / 180;
	end_rad = (endAngle * 3.14) / 180;

	thickness = 1;	  // grueso de la linea del arco	
	
	// calculamos el radio de la marca roja (en caso de que haya se dibujara)

	radio_rojo = radio - (2 * (radio / 7));

					  
	// Obtiene informacion sobre la configuracion anterior

	uint8_t	*_font_current = _UTFT->getFont();;
	word	_current_color = _UTFT->getColor();
	word	_current_back = _UTFT->getBackColor();
	
    _UTFT->setColor(VGA_WHITE);		// dibuja en blanco
	_UTFT->setFont(SmallFont);

	// PINTA EL ARCO BLANCO DEL MEDIDOR
	
	if (startAngle != endAngle)
	{
		for (int i = 0; i<thickness; i++)
		{
			px = xcentro + cos(start_rad) * (radio + thickness + i);
			py = ycentro + sin(start_rad) * (radio + thickness + i);
			for (int d = startAngle + 1; d<endAngle + 1; d++)
			{
				cx = xcentro + cos((d*3.14) / 180) * (radio + thickness + i);
				cy = ycentro + sin((d*3.14) / 180) * (radio + thickness + i);
				_UTFT->drawLine(px, py, cx, cy);
				px = cx;
				py = cy;
			}
		}
	}
	else
	{
		px = xcentro + cos(start_rad) * (radio + thickness);
		py = ycentro + sin(start_rad) * (radio + thickness);
		cx = xcentro + cos(start_rad) * (radio - thickness);
		cy = ycentro + sin(start_rad) * (radio - thickness);
		_UTFT->drawLine(px, py, cx, cy);
	}
	
	//DIBUJA EL  ARCO ROJO EN CASO DE ESTAR DEFINIDO
	
	//calcula el largo de las lineas de señalizacion

	thickness = radio / 7;

	if (rojo != divisiones)  // si se ha definido marca roja
		{
		_UTFT->setColor(VGA_RED);

		//si el medidor es simetrico (+-)
		
		if(sim==1)		
			{
		
			//marca roja de la parte de negativo

			startRojo = startAngle;	//angulo de empiece del rojo
			endRojo = startAngle + (abs(startAngle - endAngle) / divisiones)*(divisiones-rojo);		//angulo de final del rojo	
			start_rad_rojo = (startRojo * 3.14) / 180;  // angulo de empiece del rojo en radianes	
			if (startRojo != endRojo)
				{
				for (int i = 0; i < thickness; i++)
					{
					px = xcentro + (cos(start_rad_rojo) * (radio_rojo + thickness + i));
					py = ycentro + (sin(start_rad_rojo) * (radio_rojo + thickness + i));
					for (int d = startRojo; d < endRojo + 1; d++)
						{
						cx = xcentro + cos((d * 3.14) / 180) * (radio_rojo + thickness + i);
						cy = ycentro + sin((d * 3.14) / 180) * (radio_rojo + thickness + i);
						_UTFT->drawLine(px, py, cx, cy);
						px = cx;
						py = cy;
						}
					}
				}
			else
				{
				px = xcentro + (cos(start_rad_rojo) * (radio_rojo + thickness));
				py = ycentro + (sin(start_rad_rojo) * (radio_rojo + thickness));
				cx = xcentro + (cos(start_rad_rojo) * (radio_rojo - thickness));
				cy = ycentro + (sin(start_rad_rojo) * (radio_rojo - thickness));
				_UTFT->drawLine(px, py, cx, cy);
				}
			}

		// marca roja de la parte de positivo
		
		startRojo = startAngle + abs(startAngle - endAngle) * rojo / divisiones;	//angulo de empiece del rojo
		endRojo = endAngle;							//angulo de final del rojo	
		start_rad_rojo = (startRojo * 3.14) / 180;  // angulo de empiece del rojo en radianes

		if (startRojo != endRojo)
			{
			
			for (int i = 0; i < thickness; i++)
				{
				px = xcentro + (cos(start_rad_rojo) * (radio_rojo + thickness + i));
				py = ycentro + (sin(start_rad_rojo) * (radio_rojo + thickness + i));
				for (int d = startRojo ; d < endRojo + 1; d++)
					{
					cx = xcentro + cos((d * 3.14) / 180) * (radio_rojo + thickness + i);
					cy = ycentro + sin((d * 3.14) / 180) * (radio_rojo + thickness + i);
					_UTFT->drawLine(px, py, cx, cy);
					px = cx;
					py = cy;
					}
				}
			}
		else
			{
			px = xcentro + (cos(start_rad_rojo) * (radio_rojo + thickness));
			py = ycentro + (sin(start_rad_rojo) * (radio_rojo + thickness));
			cx = xcentro + (cos(start_rad_rojo) * (radio_rojo - thickness));
			cy = ycentro + (sin(start_rad_rojo) * (radio_rojo - thickness));
			_UTFT->drawLine(px, py, cx, cy);
			}
		}
	
	
	_UTFT->setColor(VGA_WHITE);

	// FIN DEL ROJO
		

	// Pinta las lineas divisoras del arco de medida

	_UTFT->setFont(SmallFont);
		
		// el + 18 es por el tamaño de las letras

		radio_let = radio + 14;
		
		for (a = 0; a != divisiones + 1  ; a++)
		{

			px = xcentro + ((radio - thickness) *  cos((start_rad)+(ro*a)));
			py = ycentro + ((radio - thickness) *  sin((start_rad)+(ro*a)));

			cx = xcentro + (radio * cos((start_rad)+(ro*a)));
			cy = ycentro + (radio * sin((start_rad)+(ro*a)));


			// DIBUJA LAS LINEAS SE ESCALA

			_UTFT->drawLine(cx, cy, px, py);


			// ESCRIBE LOS LETREROS
			
			
			if (sim == 1)		//SI ES SIMETRICO +- DE MEDIDA CON CERO EN CENTRO
				{

				cx = xcentro - (radio / 8) + (radio_let * cos((start_rad)+(ro*a)));
				cy = ycentro - (radio / 10) + (radio_let *sin((start_rad)+(ro*a)));
				if (a > (divisiones / 2))
					numero = (max / (divisiones/2)) * (a-(divisiones/2));  // calcula los digitos del lado positivo del medidor, si es simetrico
				else
					numero = (-max / (divisiones / 2)) * (a - (divisiones / 2));  // calcula los digitos del lado negativo del medidor, si es simetrico
				
				//ESCRIBE LA ESCALA EN ROJO SI ES SIMETRICO	
				if ((a > rojo - 1 && rojo > 0) || (((a-1) < (divisiones-rojo)) && rojo > 0))		//SI HAY PARTE DE LA ESCALA EN ROJO
					{
					_UTFT->setColor(VGA_RED);
					_UTFT->printNumI(numero, cx, cy);
					_UTFT->setColor(VGA_WHITE);
					}
				else
					{
					if(numero==0)  //para que salga centrado el cero en caso de simetrico
						cx = xcentro - 3 + (radio_let * cos((start_rad)+(ro*a)));
					_UTFT->printNumI(numero, cx, cy);	//ESCRIBE EL NUMERO
					}
				}
			else				//SI NO ES SIMETRICO 
				{
				cx = xcentro - (radio / 8) + (radio_let * cos((start_rad)+(ro*a)));
				cy = ycentro - (radio / 12) + (radio_let * sin((start_rad)+(ro*a)));
				numero = (max / divisiones) * a;
				if ((a > rojo-1) && (rojo != divisiones))		//SI HAY PARTE DE LA ESCALA EN ROJO
					{
					_UTFT->setColor(VGA_RED);
					_UTFT->printNumI(numero, cx, cy);	//escribe los numeros rojos
					_UTFT->setColor(VGA_WHITE);
					}
				else
					_UTFT->printNumI(numero, cx, cy);	//ESCRIBE EL NUMERO
				}
	
		}
	
	// escribe el mensaje


	if (radio <50)
		_UTFT->setFont(SmallFont);
	else
		_UTFT->setFont(BigFont);
	//_UTFT->print(mensaje, xcentro - radio / 2, ycentro + radio/3, 0);
	
	// devuelve la antigua configuracion de color que estabamos usando
	
	_UTFT->setFont(_font_current);
	_UTFT->setColor(_current_color);
	_UTFT->setBackColor(_current_back);
	}

3º parte de volt_cir.cpp

void VOLT_CIR::aguja(double valor)
{

    // Obtiene informacion sobre la configuracion anterior

    uint8_t *_font_current = _UTFT->getFont();;
    word    _current_color = _UTFT->getColor();
    word    _current_back = _UTFT->getBackColor();

    // corrige en funcion de la escala el valor
    // si el medidor es simetrico (escala +-) hace la conversion

    if(sim==1)
        valor = start_rad + (recorrido_aguja/2)+(recorrido_aguja*(valor / escala)/2);
    else
        valor = start_rad + (recorrido_aguja*(valor/escala));

    // fondo de escala es 2


    posx = xcentro + (radio_aguja * cos(valor));
    posy = ycentro + (radio_aguja * sin(valor));

    // borra la aguja anterior      

    _UTFT->setColor(VGA_BLACK);
    _UTFT->drawLine(xcentro, ycentro, old_posx,  old_posy );

    // Pinta la aguja

    _UTFT->setColor(VGA_WHITE);
    _UTFT->drawLine(xcentro, ycentro,  posx,  posy);

    // guarda la posicion anterior de la aguja

    old_posx = posx;
    old_posy = posy;

    // devuelve la antigua configuracion

    _UTFT->setFont(_font_current);
    _UTFT->setColor(_current_color);
    _UTFT->setBackColor(_current_back);
    }

ejemplo de uso de la librería con 4 instrumentos, normal, simétrico con marcas rojas y sin ellas…etc

#include <UTFT.h>
#include <VOLT_CIR.H>

// Declare which fonts we will be using

extern uint8_t SmallFont[];
extern uint8_t SevenSegmentFull[];
extern uint8_t BigFont[];
extern uint8_t Dingbats1_XL[];

// TFT grande con arduino MEGA

UTFT   myGLCD(ITDB32S, 38, 39, 40, 41);

// nombre(puntero a tft, x inicio, y inicio, diametro, max_valor_impreso, max (escala), divisiones , rojo, emp angulo, fin angulo, simetrico);

//puntero es un puntero a la pantalla TFT que vamos a utilizar
// x inicio - Y inicio son las coordenadas de posicion del instrumnto en pantalla TFT
//diametro es el diametro del circulo de medida
//max_valor_impreso es el valor maximo de la escala impresa del instrumento
//max es el valor maximo a representar por la aguja del instrumento (fondo de escala)
//divisiones es el numero de segmentos de la representacion de la escala del instrumento
// rojo es el numero de segmentos de escala que estaran indicados en rojo como de alerta , si es simetrico lo representara en la parte negativa y la positiva
//emp angulo es el angulo en grados de inicio del circulo de medida
//fin angulo es el angulo en grados del final del circulo de medida
//simetrico == 1 es un instrumento de medida simetrico con el cero central

// ejemplos de lo que se puede hacer

VOLT_CIR volt1(&myGLCD, 25, 35, 60, 10, 1000, 10, 2, -180, 90, 0);
VOLT_CIR volt2(&myGLCD, 165,  25, 130, 100, 1000, 7, 1, -225, 45, 1);
VOLT_CIR volt3(&myGLCD, 25, 145, 120, 100, 1000, 5, 0, -135, -45, 0);
VOLT_CIR volt4(&myGLCD, 165, 175, 100, 10, 1000, 5, 1, -135, -45, 0);



void setup()
{
	myGLCD.InitLCD();
	myGLCD.clrScr();
	myGLCD.fillScr(VGA_BLACK);
	volt1.chasis();
	volt2.chasis();
	volt3.chasis();
	volt4.chasis();
}

void loop()
{
	int i = 0;
	int a = 0;
	while (1)
		{
		if (a == 0)
			i = i + 10;
		else
			i=i-10;
		volt1.aguja(i);
		volt2.aguja(-i);
		volt3.aguja(i);
		volt4.aguja(i);

		if (i > 1000)
			a = 1;
		if (i < 0)
			a = 0;
		}
}