Alineación de textos en TFT

Hola,
Aunque es del mismo montaje, abro otro hilo como derivación del anterior, ya que trata temas diferentes.
Mi necesidad es alinear valores (de una variable) a la derecha. Dichos valores pueden cambiar desde una cifra hasta 4 cifras. Y claro, si hago un UTFT.print(x,y,"123") y luego la variable cambia a 89 en la pantalla queda un bonito 893.
Sinceramente he leido mucho sobre las funciones sprintf y sprintf_p, que formatean el texto, pero, sinceramente, o no las entiendo, o no funcionan como deben en arduino.
Sobre todo me parece que fallan los valores de formato que van con variables.

Ejemplo:

byte longitud =5; // Longitud de la cadena de salida sea cual sea el número de cifras del valor 
char texto [5] ; //Array para los char de salida
int numero =1234; // Valor del ejemplo (4 cifras)
sprintf("texto,|%*d|\n", longitud, numero);

Esto debería dar [ 1234], lo pongo entre corchetes para apreciar el espacio antes del 1234 que debe haber. El asterisco es para indicar que longitud es el valor que debe coger (|%5d|).

Esto no me funciona en el arduino Mega, no sé si es que lo implento mal o no sé lo que me pasa, pero sólo consigo caracteres raros e incluso cuelgues del arduino. (Por si fuera la RAM, queda bastante (más de 1K) en el momento de hacer sprinf)

Y no hablo de los decimales, porque no he conseguido hacerlo funcionar tampoco.

Es que me gustaría que la función que devuelve la String del texto sea la misma para cualquier número de cifras, tanto para enteros como para decimales, y no hacer una funcion para 3 cifras otra para 4 y otra para 5, y otras tantas para cuando llevan decimales (por suerte siempre es un solo decimal).
Por eso los argumentos del "formato" (|%*d|) deben ser variables.

Estoy pensando en hacer una funcion de manera que agregue los espacios delante según necesite, pero me parace un poco, digamos que ¿chapuza??? :rofl: y que seguro que tarda mucho tiempo en completarla

¿alguna otra sugerencia para realizar el alineamiento a la derecha de los valores y que no se superpongan?

Muchas gracias de nuevo

El problema con los decimales es que sprintf() no trabaja con float (en arduino).

Una solución que se me ocurre, suponiendo que quieres imprimir un número con 2 decimales y que siempre ocupe 8 posiciones

float numero = 123.45;
String cadena = "        ";  // 8 espacios

cadena += String(numero, 2);

cadena = cadena.substring(cadena.length() - 8));

Con lo que cadena contendrá "__123.45" (2 espacios delante del número).
Y si numero fuese 1.23 entonces cadena contendria "____1.23" (4 espacios delante del número).

Seguramente es mejorable pero, bueno, tené en cuenta que es domingo... :wink:

Saludos

No te ha funcionado porque no le has dado el espacio necesario al buffer. El buffer requiere 1 byte por cada número mas un byte extra para indicar que byte de terminación \0.
Un número como 123.45, son si cuentas 5 números, mas un punto decimal o sea 6 caracteres o bytes, mas un byte extra es decir 7 bytes.
Entonces un buffer de 7 es suficiente pero cuando usas un lcd yo siempre hago el buffer del tamaño de la linea y solo uso elementos temporales que me sirven para tareas intermedias del tamaño digamos mas justo.
Tu problema es fácil con enteros y mas complicado con decimales.
Con enteros usas %d o %4d para 4 digitos como 0000 a 9999 y si pones %5d sera de 00000 a 99999.
Ahora con decimales o numeros reales debes usar este truco.
Supongamos tu ejemplo 123.45 tiene 3 numeros enteros, 2 decmales pero 6 caracteres en total, entonces requieres de un array de 7 bytes

numero_decimal = 123.45;
char str[7], buffer[20];
dtostrf(numero_decimal, 6, 2, str);
sprintf(buffer,"%s", str);

Como verás hago el truco de pasar un float a cadena de char y luego lo formateo como una cadena fija
En el caso de los ESP tienes la suerte que tienen implementado el punto flotante entonces ahi puedes directamente poner

numero_decimal = 123.45;
buffer[20];
sprintf(buffer,"%5.2f", numero_decimal);

prueba tal vez deba ser 3.2f no me acuerdo ahora.

Con PlatformIO puedes agregarle a Arduino un comando al compilador que permite a cualquier arduino hacer esto mismo que te he dicho hacen nativamente los ESP.
Eso se debe a que los Arduino tienen deshabilitado el uso de esta librería por cuestión de tamaño de la Flash pero como uno maneja eso de acuerdo al proyecto. Es muy util disponer de dicho control.

Hola
@gatul
Muchas gracias. Esa es, más o menos, la función que he hecho. Vale si el número de digitos es conocido de antemano. Pero para que valga para cualquier número de dígitos tiene que ser un poco más compleja. Lo primero es que String cadena variará en tamaño, y para eso hay que hacer matrices dinámicas y tengo que decir que las pruebas que he hecho no han sido muy satisfactorias. La tengo hecha con un for para añadir los espacios correspondiente y tengo que decir que tengo que pulirla porque no funciona del todo bien.
Resumiendo, que si quiero que me formatee voltajes, los valores van a estar entre 1 y 20, por lo que si el valor fuese 1, la salida debería ser _1v, y si son 14, 14v (pongo _ para representar espacios). Así en la TFT tengo un espacio reservado para 2 dígitos y siempre estará alineado en su sitio. Pero si le mando formatear revoluciones, entonces los valores estarán desde 0 a 9999, con lo cual si es 1, la salida deberia ser ___1RPM y si son 3250, la salidad debería ser 3250RPM y reservo los espacios necesarios en la TFT.

/*
-value: valor para formatear
-len: longitud necesaria de la cadena
-sufix: sufijo para añadir (por ejemplo "RPM")
*/
String format_text_int(long value, byte len, String sufix)
{
  String text = String(value);
  byte len1 = text.length();
  if (len1 == len)
   return text;
  String spaces = "     ";
  spaces = spaces.substring(0, len - len1);
  text = spaces + text + sufix;
  return text;
}

Creo que es el space.substring el que me está dando problemas, pero la puliré.

@Surbyte
Muchas gracias por tu respuesta. No es un decimal lo que pretendía, es un entero "1234". Tienes toda la razón que el buffer tiene que ser de 6 en vez de 5, pero no funciona ni con 5, ni con 8, ni con 10...

byte longitud =6; // Longitud de la cadena de salida sea cual sea el número de cifras del valor
char texto [6] ; //Array para los char de salida
int numero =1234; // Valor del ejemplo (4 cifras)
sprintf("texto,|%*d|\n", longitud, numero);
//La salida debería ser _12345\0

El problema creo que está en la parte |%*d|. Según la documentación que he consultado (varias webs) el asterisco significa que tiene que coger la primera variable, es decir longitud lo cual, en este caso, debería ejecutar |%5d| y en el caso de que la longitud de los valores fuesen sólo 2 dígitos (por ejemplo del 1 al 99) debería ejecutar |%2d|

Este, me funciona perfectamente para convertir un byte en su string hexadecimal:

char  bufferEscritura[30]:
/*data_read es un struct.
uint16_t id:  Registro en la base de datos
uint8_t dl: Longitud de los datos que envia detrás (<=8)
uint8_t data[8]: byte Datos

name es un char que identifica a que base de datos. Es 'A', 'B' o 'C'
Los datos están leidos en otra función.

Ejemplo: id=100, dl=3 ,data = 0, 81, 125 (en hexadecimal 0x0064, 0x03, 0x00, 0x51, 0,x7D)
y tiene que ir a la base de datos 'A'
*/
void escribreDatos(char name, data_read *d) 
{
  memset(bufferEscritura, 0x00, sizeof(bufferEscritura)); //Llena el buffer con ceros
  uint8_t pos = 0;
  posicion += sprintf(bufferEscritura, "%c%04X", name, d.id);
  for (int i = 0; i < f.dl; i++)
  {
    posicion += sprintf(bufferEscritura + pos, "%02X", d.data[i]);
  } 

//La salida es 4100640300517D

No es mío, lo saqué de una página y lo adapté a mis necesidades.
El formato sería:
%c = Pasa el byte a char
%04X= Pon el id con cuatro dígitos (4) y en hexadecimal (X) y lo alineas a la izquierda y le pones ceros delante cuando tenga menos de 4 dígitos(0)
y con los datos
%02X = Pon el dato con dos dígitos (2) y en hexadecimal (X) y le pones ceros a la izquierda cuando tenga menos de 2 dígitos (0)

Muchas gracias a ambos por vuestro tiempo.

Editado para corregir un formato y añadir lo de las posiciones reservadas en la TFT.

No, por eso tomo el substring, porque no conozco el número de dígitos.

Si, esa es la idea, al concatenar los espacios con los dígitos cadena aumenta y luego se trunca al tamaño deseado, es una simple concatenación no creo que haya que complicarla tanto llevándolo al terreno de matrices dinámicas.

Lo que haces en format_text_int() yo lo haría así

String format_text_int(long value, byte len, String sufix) {
  String text = "                ";
  text = text + String(value);
  text = text.substring(text.length() - len)) + sufix;
  return text;
}

Obviamente que solo trabaja con enteros pero ¿y si pasas el número como string?

String format_text_int(String value, byte len, String sufix) {
  String text = "                ";
  text = text + value;
  text = text.substring(text.length() - len)) + sufix;
  return text;
}

y la llamas con, por ejemplo,

float volts = 12.3;

@ format_text_int(String(volts), 10, "V");

long rpm = 12345;

@ format_text_int(String(rpm), 10, "RPM");

@ simboliza la sentencia que uses para imprimir.

sufix no lo has tomado en cuenta y queda excluido del len, no se si realmente es lo que quieres, o sea, solo justificar el número.

Así como está planteado, la salida quedaría

      12.3V
     12345RPM

Para justificar incluso el sufijo habría que hacer

String format_text_int(String value, byte len, String sufix) {
  String text = "                ";
  text = text + value + sufix;
  text = text.substring(text.length() - len));
  return text;
}

Y la salida sería

     12.3V
  12345RPM

Saludos

PD: Así como en arduino sprintf() no trabaja con float tampoco funciona con el modificador * .

Gatul
:flushed: oppps! Cierto, varia la longitud.
Bueno, ya confirmado que los parametros no funcionan en arduino, (sinceramente creo que lo leí en alguna página refieriendose al arduino, pero tiene pinta que el redactor lo escribiera para C++ sin pensar que no funcionaba) lo que pones es la solución.
Viéndolas, veo que era más sencillo de lo que planteaba. (Confirmado a veces me complico la vida demasiado :rofl:)
Con su permiso las voy a utilizar y adaptarlas a mis necesidades, aunque tiene pinta de que serán pocas las modificaciones.
Muchísimas gracias por vuestro tiempo y ayuda.

Use pero no abuse. :joy: :rofl:

Un gusto haberte ayudado.

Saludos

esto esta mal!! por segunda vez porque ya te lo había señalado. No tiene el buffer o cadena donde guardas el texto formateado.
Mira como lo he hecho o definido

int numero = 12345;
char buffer[20];

sprintf(buffer,"Lo que gustes :%5d", numero);

buffer[20]; es la clave

Otro error del que ahora me percato

sprintf("texto,|%*d|\n", longitud, numero);

No puedes usar %*d que no se de donde lo has sacado para que la longitud sea modificable, no funciona asi, se pone %5d para indicar 5 lugares o %3d para 3 lugares pero no es variable.

Surbyte
Como ya me avisó Gatul en arduino el "*" no funciona, pero si se puede usar en C++. Por eso no funcionaba ni con 5, ni con 6, ni con 100. Con la longitud fija, funciona perfectamente (por ejemplo |%5d|)
Adapto los consejos de Gatul.
Muchísimas gracias