Inconveniente con dtostrf

Buenas!!! Ando teniendo un inconveniente con la función "dtostrf" para convertir de float a string.

El tema es que estoy desarrollando un termómetro de máxima y mínima y muestro los valores en un display oled, para lo cual, luego de obtener los valores de temperatura con sensores DS18B20, convierto los valores con dicha función para mostrarlos en pantalla. El problema es que, cuando llamo a esta función varias veces seguidas, hay varios valores que no se convierten, por lo que no me figuran en pantalla... Y NO SÉ POR QUÉ!!! :confused:
Ya probé poniendo delay entre ellas y tampoco funciona....

Les paso el programa y una captura del monitor serie donde se ve que no se generan.

Gracias!!!!

#include <Wire.h> //para manejar i2c
#include <OneWire.h> //Se importan las librerías para sensor ds18b20
#include <DallasTemperature.h>// librerìa para manejar sensor ds18b20
// Configuramos para comunicar con sensores
OneWire ourWire1(2);                //Se establece el pin 2  como bus OneWire
OneWire ourWire2(5);                //Se establece el pin 5  como bus OneWire
// Indicamos el pin asignado al sensor 1-Wire a DallasTemperature 
DallasTemperature sensors1(&ourWire1); //Se declara una variable u objeto para nuestro sensor1
DallasTemperature sensors2(&ourWire2); //Se declara una variable u objeto para nuestro sensor2  
#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
#include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Spi.h"

 // Use the corresponding display class:

 // Initialize the OLED display using SPI
 // D5 -> CLK
 // D7 -> MOSI (DOUT)
 // D0 -> RES
 // D2 -> DC
 // D8 -> CS
  SSD1306Spi        display(D0, D2, D8);

  float tin,ta,tmin,tmax;
  char buffertin[4],bufferta[4],buffertmin[4],buffertmax[4];
  
void setup() {
  //delay(1000);
  display.init();
  display.flipScreenVertically();
  display.setContrast(255);
  sensors1.begin();   //Se inicia el sensor 1
  sensors2.begin();   //Se inicia el sensor 2
  delay(500);
  Serial.begin(115200);
  delay(500);
  sensors1.setResolution(10);
  sensors2.setResolution(9);
  sensors1.requestTemperatures(); //Prepara el sensor para la lectura
  tin=sensors1.getTempCByIndex(0); // Almacenamos la temperatura del sensor de temperatura principal;
  sensors2.requestTemperatures(); //Prepara el sensor para la lectura
  ta=sensors2.getTempCByIndex(0); // Almacenamos la temperatura del sensor de temperatura ambiente
  tmin=tin;
  tmax=tin;
  dtostrf(tmin,4,1,buffertmin); // Convierto un numero float a una cadena de texto
  dtostrf(tmax,4,1,buffertmax); // Convierto un numero float a una cadena de texto
  dtostrf(tin,4,1,buffertin); // Convierto un numero float a una cadena de texto
  dtostrf(ta,4,1,bufferta); // Convierto un numero float a una cadena de texto
  delay(1000);
  Serial.println(buffertin);
    Serial.println(bufferta);
    Serial.println(buffertmin);
    Serial.println(buffertmax);
}

void loop() 
  {
    display.resetDisplay();
    display.drawRect(0,0,128,64); //Recuadro de pantalla
    display.drawLine(71,16,128,16); //Base de Ta
    display.drawLine(71,33,128,33); //Base de Tmin
    display.drawLine(0,49,128,49); //Techo de hora y fecha
    display.drawLine(71,0,71,49); //vertical
    display.display();
    display.setFont(Arimo_12); //font de título, Tmin, Tmax, fecha y hora
    display.drawString(73, 2, "Ta:"); //Temp. ambiente
    display.drawString(90, 2, bufferta); //
    display.drawString(73, 18, "Tm:"); //Tmin
    display.drawString(95, 18, buffertmin); //
    display.drawString(73, 35, "TM:"); //Tmax
    display.drawString(95, 35, buffertmax); //
    display.drawString(3, 50, "DD/MM/AAAA  hh:mm");
    display.display();
    display.setFont(ArialMT_Plain_16); //font de Tin
    display.drawString(3, 2, "Tin: [°C]");
    display.setFont(Century_Schoolbook_L_Roman_30); //font de temperatura principal
    display.drawString(3, 15, buffertin);
    
    display.display();
    
    sensors1.requestTemperatures(); //Prepara los sensores para la lectura
    tin=sensors1.getTempCByIndex(0); // Almacenamos la temperatura del sensor de temperatura principal;
    sensors2.requestTemperatures(); //Prepara el sensor para la lectura
    ta=sensors2.getTempCByIndex(0); // Almacenamos la temperatura del sensor de temperatura ambiente
    dtostrf(tin,4,1,buffertin); // Convierto un numero float a una cadena de texto
    dtostrf(ta,4,1,bufferta); // Convierto un numero float a una cadena de texto
    if(tin>tmax)
    {
      tmax=tin;
      dtostrf(tmax,4,1,buffertmax);
    }
    if(tin<tmin)
    {
      tmin=tin;
      dtostrf(tmin,4,1,buffertmin);
    }
    Serial.print("Temp. principal: "); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.print(tin); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.println(" grados Centigrados"); 
    Serial.print("Valor min.: "); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.print(tmin); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.println(" grados Centigrados"); 
    Serial.print("Valor max.: "); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.print(tmax); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.println(" grados Centigrados"); 
    Serial.print("Temp. ambiente: "); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.print(ta); //Se lee e imprime la temperatura en grados Celsius del sensor ds18b20
    Serial.println(" grados Centigrados"); 
    Serial.println(buffertin);
    Serial.println(bufferta);
    Serial.println(buffertmin);
    Serial.println(buffertmax);
    delay(1000);
  }

Probaste aumentando en 1 o 2 el tamaño de cada buffer?
Yo normalmente y no es muy profesional lo que diré, no me quedo corto y uso buffers de 8 caracteres casi siempre.
Si tengo limitaciones de memoria entonces hago bien las cosas.

Comenzaré diciendo que creo que surbyte tiene razón, que se trata de un problema del tamaño de los buffers y la solución es la que él propone. Soy muy nuevo aquí y, por lo poco que he visto, también creo que surbyte está muy ocupado tratando de atender y resolver todas las dudas y problemas que se plantea en este foro (veo que responde a la mayoría de las consultas, y si yo estuviera en su lugar seguramente estaría más que estresado). Y precisamente por ello creo que no le dedica más tiempo a sus respuestas, después de todo ya te ha dado la solución, por tratar de resolver el mayor número de consultas posibles. Es por ello que creo que por eso va "al grano" y alguna que otra vez no da mucha explicación del porqué, sólo da la solución o plantea por donde se podría "atacar el problema". Así que, a riesgo de hacerme el "enteradillo", voy a tratar de explicar un poco qué es lo que está pasando y cual es la raíz del problema. Sirva esta explicación para entender un poco más cómo trabaja el C y ciertos problemas que pueden aparecer con los "buffers" en C/C++. (Nota: los .ino de los sketch de Arduino son programas en C++). Claro está que esto va más orientado a aquellos que son nuevos en estas lides.

Cuando hacemos una declaración de una variable así: char buffertin[4]; lo que estamos haciendo es reservar 4 bytes de memoria para la variable bufferin y eso es todo lo que tenemos para guardar datos en esa variable. Si tratamos de guardar más de 4 bytes lo que vamos a lograr es que "el sobrante" se "guarde" en una zona de memoria que no tenemos "reservada" para ello. Y lo más probable es que "sobreescriba" con esos datos "sobrantes" la información de otra variable. Así que: si reservas 4 bytes ojo con "guardar" más de 4 bytes.

Vale, podemos guardar 4 bytes, ¿qué es lo que vamos a guardar en este caso? ¿Un número? No, un número no, vamos a guardar "una cadena" ("un texto" sería otra forma de decirlo), un texto que contiene la representación de un número. En este caso vamos a suponer que vamos a guardar el texto que representa la temperatura 17.25 guardada en la variable tin... bueno, no exactamente los 17.25, ya que el valor numérico tin lo vamos a convertir en "una cadena" (texto) que represente ese valor con un sólo decimal. Para ello usamos la funcion: dtostrf(tin,4,1,buffertin); Con esto hemos convertido el valor numérico de tin en un texto que hemos guardado en el "buffer" bufferin y ese texto es 17.2 (texto formado por los cuatro caracteres '1', '7', '.' y '2'). ¡Perfecto! (pensarán algunos), hemos guardado cuatro caracteres (un byte por cada uno) en una variable que tiene reservado espacio para almacenar hasta cuatro caracteres... Sí, pero no...

...las cadenas de C no son sólo lo que se ve, también son lo que no se ve. Una cadena de C está formada por una serie de bytes que representa cada uno un carácter de la cadena (el espacio también cuenta como un carácter aunque no se vea) más un último byte cuyo valor es cero. ¿Cómo? ¿Un byte cuyo valor es cero? Sí, un byte cuyo valor es cero. Así es como sabe el C que ha llegado al final de una cadena, porque se encuentra un carácter cuyo valor es cero (no confundir con el carácter 0). Como siempre, para saber más está Google, buscas y encuentras. Pero si nadie te pone sobre la pista de qué buscar, no vas a encontrar nada ya que nada buscas.

Así que el texto 17.2 es el carácter '1', seguido del carácter '7', seguido del carácter '.', seguido del carácter '2' y seguido por un byte que vale cero. Si cara carácter es un byte, tenemos cuatro caracteres "visibles" y un byte "escondido", en total cinco bytes... Oops! ¿Cómo que cinco? ¿Cinco? Si yo he reservado cuatro... Sí cinco, aunque sólo vamos cuatro. Pues, como ya se han de imaginar los que hasta ahora no sabían de esto, la llamada a la función está guardando 5 bytes en una zona de memoria donde se han reservado 4 bytes.

¿Y qué pasa si se guarda de más? Pues que se guarda de más, simplemente eso. Bueno, que se guarda de más y donde no debe. Esa operación "invade" otras variables y "mete basura" en esas otras variables. Esto produce que el programa haga "cosas raras", "comportamientos anómalos", que se cuelgue, qué sé yo ni nadie... ¡La hemos pifiado!

¿Y el C no controla que estamos "guardando" más datos de los que caben? Pues no. No lo controla. Digamos que "no pierde el tiempo con comprobaciones que no tendría porqué hacer". Eso hace que el C sea rápido, ya que no pierde el tiempo con ese tipo de comprobaciones, pero a la vez hace que sea peligroso... muy peligroso. Los buffers, cadenas y punteros son las cosas más temidas del C porque si no las tienes "controladas" te pueden hacer un destrozo.

Por eso la solución es "un buffer más grande", pero sin pasarse que no siempre podemos desperdiciar la escasa memoria de la que disponemos en un Arduino. Surbyte confiesa "avergonzado" que el suele usar un buffer de 8 caracteres. Entiendo que es de 8 bytes, con lo cual, si descontamos el byte "cero" del final de la cadena, son de 7 caracteres visibles... pues que tenga cuidado, porque si lo guarda en una variable con decimales es porque querrá los decimales y, normalmente, solemos trabajar con dos decimales... dos decimales más el punto decimal hacen 3 (sí, el punto decimal es un carácter y ocupa un byte). Siete menos tres, quedan cuatro. Cuatro caracteres para la parte entera. Eso deja un valor máximo de 9999 para la parte entera... un valor máximo, que no mínimo. El valor mínimo sería -999 ya que el signo negativo también ocupa su byte. Resumiendo, si el buffer es de un tamaño de 8 bytes, y trabajamos con 2 decimales, podremos representar valores desde el -999.99 hasta el 9999.99, cualquier valor que se salga de ese rango hará que nuestro programa empiece a fallar y "hacer cosas raras".

Así que mi consejo es "estudiar cada caso" y ver cuán grande puede llegar a ser nuestra cadena (y por si las moscas darle un par de bytes más de tamaño). Cierto es que andamos escasos de RAM y no es cuestión de tener cientos de buffers enormes... así que mejor tener pocos buffers del tamaño adecuado. Si tienen que ser grandes han de ser grandes, qué se le va hacer, pero no necesariamente tenemos porqué tener cientos. ¿En este caso hemos de tener guardado a la vez los cuatro valores convertidos a texto? Yo creo que no, que se podría tener un único buffer de un tamaño "decente" y utilizarlo para los cuatro valores. Se convierte un valor, se muestra y se pasa al siguiente, así con los cuatro. Lo que sí que se ha de tener más cuidado con el programa. Pero ojo no nos pasemos, no siempre se puede utilizar un mismo buffer para dos cosas distintas, porque por necesidades del programa puede ser que se requiera que los dos "valores" estén en el buffer a la vez, con lo cual necesitamos dos bufers sí o sí. Cada caso siempre hay que estudiarlo.

Así que en C una cosa es un carácter y otra cosa es una cadena. Cuando en C tenemos escrito 'A', esto representa un único byte cuyo valor es 65 y representa el carácter A (ver en una tabla ASCII). Mientras que si lo que hay escrito es "A", esto representa una cadena formada por dos bytes el primer byte vale 65 y representa el carácter A y el segundo bytes tiene el valor cero para indicar que es el final de la cadena (también llamado carácter nulo). Esa es la gran diferencia en C entre las comillas simples y las comillas dobles. Por cierto, una cadena vacía (dobles comillas seguidas de otras dobles comillas sin nada en medio) ocupa un byte con valor cero para indicar que es el final de la cadena.

Por último comentar que en C++ hay unas cadenas "más inteligentes" y muchísimo más seguras, las String... Pero "la inteligencia" y "la seguridad" se pagan. Las String consumen más memoria y más ciclos de CPU que las cadenas de C de toda la vida (que también está disponibles en C++/Arduino). Y, obviamente, "no son lo mismo" las String que las cadena de texto, por lo que no siempre se pueden usar por igual ni hacer las mismas cosas con las unas que con las otras.

Lamento el tostón y espero que se me haya entendido algo... o no.

IgnoranteAbsoluto:
¿Y el C no controla que estamos "guardando" más datos de los que caben? Pues no. No lo controla. Digamos que "no pierde el tiempo con comprobaciones que no tendría porqué hacer". Eso hace que el C sea rápido, ya que no pierde el tiempo con ese tipo de comprobaciones, pero a la vez hace que sea peligroso... muy peligroso. Los buffers, cadenas y punteros son las cosas más temidas del C porque si no las tienes "controladas" te pueden hacer un destrozo.

Añadiéndole a esa parte: a eso se le llama "manejo manual de memoria". Los problemas como desbordamiento de búfer normalmente se manejan mediante las famosas "excepciones"; sin embargo, ya que estamos en un entorno de escasos recursos y carente de sistema operativo, esta característica se elimina del lenguaje; recayendo esta responsabilidad en el programador.

Aunque más bien yo lo llamaría "manejo semi-automático de memoria". La parte manual está en los punteros; y la parte automática está en las funciones de asignación/desasignación (malloc, calloc, realloc, free), construcción/destrucción de objetos y declaración explícita de arrays.
El manejo puramente manual lo encontrarás sólo en el lenguaje ensamblador.

El lenguaje de Arduino (que básicamente es C++) carece de "recolector de basura"; razón por la cuál se suele criticar al objeto String en lo que concierne al uso de memoria.

Todas las respuestas y ayudas impecables!!! Aumenté el tamaño de los buffers y se fueron todos los problemas. Además, con la terrible explicación de IgnoranteAbsoluto aprendí cosas que en la web no se encuentran fácilmente.

Muchas gracias!!!