Como logro hacer un promediador en mi sketch

hola de nuevo, desde hace un par de dias me encuentro realizando una practica que consiste de forma muy rudimentaria por asi decirlo, en una especie de incubadora, la gracia de este proyecto consiste en un foco y un ventilador que funcione como extractor, para eso estoy usando un arduino uno, un transistor npn BD 137, un ventilador que recicle de una vieja pc de escritorio, y un optoacoplador moc 3011 conectado a un triac mac12D que controlan a un foco de 80W que es el que se encargar de generar calor y por su puesto un sensor lm35, opte por estos materiales puesto que es para una practica y la mayoria de mat. pues ya los tenia, el problema actual recae en el sensor, puesto que me da lecturas algo inestables, ya que en mi codigo solo le pido que tome una temperatura cada seg. y quisiera hacerlo un poco mas preciso, entonces leyendo vi que esto se podia resolver tomando varias muestras del sensor y promediandolas, se que se puede hacer con un ciclo “for” pero no se como podría ponerlo en mi codigo, ojala y me pudiesen ayudar, abajo anexo el codigo por si gustan revisarlo.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //Inicializamos la libreria con el numero de los pines a utilizar


float tempC;    //Aqui se van a guardar los datos de la temperatura del sensor
int lectura;    //Se agina el valor que sea leido por el sensor
int Sensor = A5;  //se declara el pin en el que estara el sensor, refiriendose en ocaciones futuras como variable "sensor"
int ventilador = 6;   //se declara la variable donde se colocara el ventilador
int foco = 7;

void setup()
{
analogReference(INTERNAL);  //usando la referencia interna del arduino, configuramos a este para que opere con 1,1V, limitando la temp. a solo de 0 a 110°C de los 150 que puede medir el LM35
                            //pero sino se hace esto, no se puede obtener la resolucion de 0.1 °C                            
 lcd.begin(16, 2);         // Configuramos el numero de columnas y filas del LCD. 
   lcd.print("Temperatura: ");  //impresion de mensaje
   lcd.setCursor(0,1);
   lcd.print ("      Grados");
   pinMode (6, OUTPUT);           //el pin del ventilador se declarara como salida
   delay (100);                  //se refresca el lcd
}

void loop()
{
lectura = analogRead(Sensor);  //asignacion de lo que se mida en la variable sensor, a la variable lectura
tempC = lectura / 9.31;        //ATENCION, al cambiar la ref analogica del arduino de 5v a 1.1v se tiene que reformular la ec. para obtener la temperatura de tal forma que
                               // se divide 1./1024 = 0.001074V = 1.0742 mV. Si 10mV es igual a 1 grado Celsius, 10 / 1.0742 = ~ 9,31.
                              // Así, por cada cambio de 9,31 en la lectura analógica, hay un grado de cambio de temperatura.
  lcd.setCursor (0,1);
  lcd.print(tempC);      //se imprime la temperatura en el display
  delay (500);           //cada 500ms
  
   if(tempC >25)   //Si la temperatura es mayor que 25  en este caso se encendera el ventilador para disipar calor... Mich no seas menso "<" es menor que
  {
    digitalWrite (6, HIGH);
  }
  
  else          //sino se apagará
 { 
    digitalWrite (6, LOW);
 }
  
 if (tempC < 20)
 {
 digitalWrite (7, HIGH);
 }
  
  else
  
  {
  digitalWrite(7, LOW);
  }
}

Gracias por dedicar parte de su tiempo para ayudarme

Hola mike_117

Lo que vos pedís podría ser así:

lectura = 0;
for ( int i = 0 ; i < 10 ; i++ ) {
lectura += analogRead(Sensor);
delay(50);
}
lectura /= 10;

Hace 10 lecturas que va sumando en la variable “lectura”, y luego divide el valor por 10.
Cada lectura con un delay de 50 mS. Por lo que el ciclo demora en total 500 mS.
Reemplaza en tu código el “delay(500);” y “lectura = analogRead(Sensor);”.

Nota: Yo lo pensé con 10 lecturas, pero puede ser el numero que quieras. Tene presente que el máximo valor de una variable “int” es 32000 y algo. Sino definila “long”.

Saludos.

Hace tiempo que esperaba la oportunidad para volver a enseñar la técnica del promedio móvil.
Es un filtro digital de excelente perfomance!!! A mi me encanta y lo uso casi siempre.

Esta técnica permite suavizar una señal sin perder información, y sin gastar 500mseg como sugiere la mayoría de los promedios comunes.
Mi único gasto está en el arranque, tomo SAMPLES_TO_AVERAGE que deseo (en este caso 256) y luego devuelvo el control a loop(), y ya tengo la tempC promediada y continúo con el siguiente dato.
Intenten variar la temperatura y verán como sigue el comportamiento variable de una seÑal.

Luce como complicado inicialmente pero no lo es.
Acá esta la teoría del promedio móvil.

#include <LiquidCrystal.h>
#define SAMPLES_FAST_AVERAGE 	64
#define SAMPLES_TO_AVERAGE 		256


LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //Inicializamos la libreria con el numero de los pines a utilizar

int Sensor = A5;  //se declara el pin en el que estara el sensor, refiriendose en ocaciones futuras como variable "sensor"
int ventilador = 6;   //se declara la variable donde se colocara el ventilador
int foco = 7;
float tempC;    //Aqui se van a guardar los datos de la temperatura del sensor
int lectura;
float RawTemp;

void setup(){
	analogReference(INTERNAL);  //usando la referencia interna del arduino, configuramos a este para que opere con 1,1V, limitando la temp. a solo de 0 a 110°C de los 150 que puede medir el LM35
                            //pero sino se hace esto, no se puede obtener la resolucion de 0.1 °C                            
 	lcd.begin(16, 2);         // Configuramos el numero de columnas y filas del LCD. 
   lcd.print("Temperatura: ");  //impresion de mensaje
   lcd.setCursor(0,1);
   lcd.print ("      Grados");
   pinMode (6, OUTPUT);           //el pin del ventilador se declarara como salida
   for (int i= 0; i<SAMPLES_TO_AVERAGE; i++) {
   		lectura = analogRead(Sensor);  //asignacion de lo que se mida en la variable sensor, a la variable lectura
		RawTemp = lectura / 9.31;        //ATENCION, al cambiar la ref analogica del arduino de 5v a 1.1v se tiene que reformular la ec. para obtener la temperatura de tal forma que
                               // se divide 1./1024 = 0.001074V = 1.0742 mV. Si 10mV es igual a 1 grado Celsius, 10 / 1.0742 = ~ 9,31.
                              // Así, por cada cambio de 9,31 en la lectura analógica, hay un grado de cambio de temperatura.

    	tempC = tempC - (tempC / SAMPLES_TO_AVERAGE) + RawTemp;   // para la primer iteraccion veran que RawTemp vale 0 y que 0 - 0 + tempC toma el primer valor 
   }


}

void loop()
{
	lectura = analogRead(Sensor);  //asignacion de lo que se mida en la variable sensor, a la variable lectura
	RawTemp = lectura / 9.31;        //ATENCION, al cambiar la ref analogica del arduino de 5v a 1.1v se tiene que reformular la ec. para obtener la temperatura de tal forma que
                               // se divide 1./1024 = 0.001074V = 1.0742 mV. Si 10mV es igual a 1 grado Celsius, 10 / 1.0742 = ~ 9,31.
                              // Así, por cada cambio de 9,31 en la lectura analógica, hay un grado de cambio de temperatura.

    tempC = tempC - (tempC / SAMPLES_TO_AVERAGE) + RawTemp;   // aca ya trae 256 muestras previas promediadas.
                            
  	
  	lcd.setCursor (0,1);
  	lcd.print(tempC);      //se imprime la temperatura en el display
  
   	if (tempC > 25)   //Si la temperatura es mayor que 25  en este caso se encendera el ventilador para disipar calor... Mich no seas menso "<" es menor que
       	digitalWrite (6, HIGH);
    else          //sino se apagará
      	digitalWrite (6, LOW);
   
   	if (tempC < 20)
      	digitalWrite (7, HIGH);
   	else
      	digitalWrite(7, LOW);
 }

Estimado surbyte:

Lo felicito por el conocimiento de matemática. Sí, un promedio movil , que no conocía, es una gran solución. Yo conocía algo similar que es el filtro de Kalman (muy útil para los acelerometros). Aunque me parece la ecuación debería ser (no lo tome a mal :D ):

tempC = tempC + (RawTemp - tempC) / SAMPLES_TO_AVERAGE;

Algún delay dejaría en el ciclo para no calentar el micro y ahorrar energía. Y en cuanto a cargar los datos al principio se puede obviar.

Un saludos desde Buenos Aires.

Bueno, tu corrección es correcta perrociego, se ve que copie mal y olvidé lo que tu mencionas. El valor debe promediarse y por eso es móvil.

Bueno, pruébalo y luego me cuentas si ahora tienes algo mas estable. A mi me da muybuenos resultados usando mediciones de presión que de otro modo son horribles.

Gracias por el consejo, surbyte y al compañero que contribuyo a corregir la ecuación, les informo que tal como dijo surbyte, he obtenido mediciones mucho mas precisas con mi sensor, pero ahora me ha surgido otro problema que por mas que le busco no puedo hallarle solucion, no se si sera por el desvelo que he dedicado tratando de solucionar esto o porque no cuento con el conocimiento necesario :disappointed_relieved: Lo que sucede es que ahora quisiera que la temperatura se ajustara en base a los datos que quiero pero con un boton.

por si no me explique bien en el punto anterior, lo que quiero es añadir un boton a mi codigo que haga algo asi;

Si el boton es precionado, regular la temperatura a 25°C , de tal forma que si la temperatura en ese momento estuviese por debajo de la deseada, 20° por ejemplo, encienda el foco que tengo para que genere calor, o por otra parte, si la temperatura esta por encima de la deseada, encender el ventilador para extraer aire del espacio en el que esta. Intente hacerlo comparando datos con “&&” pero termine haciendo un revoltijo en el codigo que me llevo tiempo componer, añado mi codigo por cualquier duda y de verdad que agradeceria cualquier tipo de ayuda.

#include <LiquidCrystal.h>
#define SAMPLES_FAST_AVERAGE 	64
#define SAMPLES_TO_AVERAGE 		100 // en vez de 256 muestras, solo tomo 100 puesto que me causaba un retardo de casi 6 seg. en el programa, desconozco a que se deba esto. 



LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //Inicializamos la libreria con el numero de los pines a utilizar

int Sensor = A5;  //se declara el pin en el que estara el sensor, refiriendose en ocaciones futuras como variable "sensor"
int ventilador = 6;   //se declara la variable donde se colocara el ventilador
int foco = 10;
float tempC;    //Aqui se van a guardar los datos de la temperatura del sensor
int lectura;
float RawTemp;
float temperatura_deseada;  
int boton1 = A4;  //aqui es donde ira el boton que al presionar regulara la temp.



void setup(){
	analogReference(INTERNAL);  //usando la referencia interna del arduino, configuramos a este para que opere con 1,1V, limitando la temp. a solo de 0 a 110°C de los 150 que puede medir el LM35
                            //pero sino se hace esto, no se puede obtener la resolucion de 0.1 °C                            
 	lcd.begin(16, 2);         // Configuramos el numero de columnas y filas del LCD. 
   lcd.print("Temperatura: ");  //impresion de mensaje
   lcd.setCursor(0,1);
  
   lcd.print ("      Grados");
   pinMode (6, OUTPUT);           //el pin del ventilador se declarara como salida
   for (int i= 0; i<SAMPLES_TO_AVERAGE; i++) {
   		lectura = analogRead(Sensor);  //asignacion de lo que se mida en la variable sensor, a la variable lectura
		RawTemp = lectura / 9.31;        //ATENCION, al cambiar la ref analogica del arduino de 5v a 1.1v se tiene que reformular la ec. para obtener la temperatura de tal forma que
                               // se divide 1./1024 = 0.001074V = 1.0742 mV. Si 10mV es igual a 1 grado Celsius, 10 / 1.0742 = ~ 9,31.
                              // Así, por cada cambio de 9,31 en la lectura analógica, hay un grado de cambio de temperatura.

    	tempC = tempC  + (RawTemp - tempC) / SAMPLES_TO_AVERAGE;   // para la primer iteraccion veran que RawTemp vale 0 y que 0 - 0 + tempC toma el primer valor 
    delay(50);
   }
    
   boton1 = analogRead(A4); 
   
  delay (40);

}


void loop()
{
	lectura = analogRead(Sensor);  //asignacion de lo que se mida en la variable sensor, a la variable lectura
	RawTemp = lectura / 9.31;        //ATENCION, al cambiar la ref analogica del arduino de 5v a 1.1v se tiene que reformular la ec. para obtener la temperatura de tal forma que
                               // se divide 1./1024 = 0.001074V = 1.0742 mV. Si 10mV es igual a 1 grado Celsius, 10 / 1.0742 = ~ 9,31.
                              // Así, por cada cambio de 9,31 en la lectura analógica, hay un grado de cambio de temperatura.

    tempC = tempC  + (RawTemp - tempC) / SAMPLES_TO_AVERAGE;   // aca ya trae 100 muestras previas promediadas.
                            
  	
  	lcd.setCursor (0,1);
  	lcd.print(tempC);      //se imprime la temperatura en el display
  
  delay(300);
  
      
////////////////////////// AQUI es donde pienso que se puede hacer de esta forma, pero creo que estoy mal/////////

      if (tempC != 25 && boton1 = 1)   //Si la temperatura es diferente de 25 y el boton fue presionado...
{
       digitalWrite (6, HIGH);  //enciendo el ventilador, aunque esto deberia depende de si la temp esta por encima o por de de bajo de la deseada
       digitalWrite (10, LOW);
       }
       
    else          //sino se apagará
      {	
      digitalWrite (6, LOW);
      }
      
    



}

Lo que quieres hacer es como muy forzado.
Lo que se hace es calentar y apagar el calentador y que la inercia termica haga el resto
O sea

if (tempC > 25.0) 
     digitalWrite(LamparaPin, LOW);
if (tempC < 24.5)  
          digitalWrite(LamparaPin, HIGH);
}

bueno eso muestra que hay una zona entre 24.5 y 25 que no hago nada

Si esto no es suficiente debes usar un PID que te ajustará mejor la temperatura.

Disculpen que les interrumpa en su discusión pero es que me interesó demasiado el tema, a mi las cuentas no me cuadran no se si es por mi ignorancia o porque no les entiendo el procedimiento. El promedio mobil es un concepto sencillo pero no lo había comprendido, me tomo todo el fin de semana estudiarlo y probarlo. El principal problema que a mi juicio tiene su código es que no tiene como recordar los valores o mediciones iniciales que son la base para realizar el promedio de los datos.

float temp;
float valor;
int i;
float sensorValue[10];

void setup() {
  lcd.begin(16, 2);  // Print a message to the LCD.
  pinMode(relePin,OUTPUT);
  analogReference(INTERNAL);
  for(i=0;i<10;i++)
  {
  sensorValue[i] = analogRead(sensorPin); //tomo 10 medidas y las guardo en el vector sensorValue[]
  delay(100);
  }
  for(i=0;i<10;i++)
  valor += (sensorValue[i]);  //sumo las 10 medidas
  valor=valor/i;  //hago el promedio
  temp = valor*110/1023;  //obtengo el valor de la temperatura 
}

Y en la funcion loop

void loop() {

  centenas = 0;
  decenas = 0;
  unidades = 0;
  decimas = 0;
  centecimas = 0;
  
 
  for(i=0;i<9;i++)
    sensorValue[i]=sensorValue[i+1]; //recuerdo las primeras mediciones excepto la primera
  sensorValue[9] = analogRead(sensorPin); // el ultimo dato del vector es la medida actual
  delay(100);
  for(i=0;i<10;i++)
  valor +=sensorValue[i]; //sumo los valores 
  valor = (valor/i);  //hago el promedio
  temp = valor*110/1023;  //obtengo la temperatura
  ......

El ejercicio esta interesante ojalá podamos discutirlo y me hagan ver en que estoy equivocado, Solo estoy revisando el proceso de la medición y realizar un promedio, lo que están haciendo ustedes puede funcionar pero no entiendo. Por favor revisen el código y me dicen si funciona o no. Seria de mucha ayuda sus comentarios