Descargas, guías, trucos, gameplays...

domingo, 7 de mayo de 2017

Arduino - Leer multiples MLX90614 (termómetros infrarrojos) cambiando sus direcciones




Realizar un control de temperatura con termómetro infrarrojo puede requerir de más de un punto de medición, pero, ¿Cómo hacerlo en Arduino si solamente se cuenta con un pin SDA y uno SCL?. En general se requieren dos pasos:

- Cambiar la Slave address de cada sensor
- Leer los sensores uno a uno con su respectiva Slave address

Tomar en cuenta que la Slave address de fabrica de los MLX90614 es 0x5A, además tienen una Universal 0x0 en caso de que se la cambies y olvides cual le has colocado. Si se conectan ambos sensores con la dirección 0x5A simplemente el Arduino no muestra las lecturas en el Serial monitor.


Si no tienen la libreria I²Cmaster que se usa la pueden descargar aquí: Librería I²Cmaster


Cambiando las direcciones

Para cambiar la dirección se debe hacer con los sensores uno por uno con el siguiente código:

#include <i2cmaster.h>

byte MLXAddr = 0x5A<<1;   // Nueva dirección 
//byte MLXAddr = 0;       // Dirección universal

void setup(){
 Serial.begin(9600);
 Serial.println("Iniciando...");
 
 i2c_init();                              //Inicializar i2c bus
 PORTC = (1 << PORTC4) | (1 << PORTC5);   //Pullups
 
 delay(5000);                    // Espera la conexión serial
 cambiarDirec(0x5A, 0x00);       // Cambia la dirección al nuevo valor, en este caso cambia a 0x50
}

void loop(){
}

//Función con comentarios originales de http://forum.arduino.cc
word cambiarDirec(byte NuevaDirec1, byte NuevaDirec2) {

 Serial.println("> Cambiar dirección");

 i2c_start_wait(0 + I2C_WRITE);    //send start condition and write bit
 i2c_write(0x2E);                  //send command for device to return address
 i2c_write(0x00);                  // send low byte zero to erase
 i2c_write(0x00);                  //send high byte zero to erase
 if (i2c_write(0x6F) == 0) {
   i2c_stop();                     //Release bus, end transaction
   Serial.println("  Dato borrado");
 }
 else {
   i2c_stop();                     //Release bus, end transaction
   Serial.println("  Fallo borrando dato");
   return -1;
 }

 Serial.print("  Dato escrito: ");
 Serial.print(NuevaDirec1, HEX);
 Serial.print(", ");
 Serial.println(NuevaDirec2, HEX);

 for (int a = 0; a != 256; a++) {
   i2c_start_wait(0 + I2C_WRITE);  //send start condition and write bit
   i2c_write(0x2E);                //send command for device to return address
   i2c_write(NuevaDirec1);            // send low byte zero to erase
   i2c_write(NuevaDirec2);            //send high byte zero to erase
   if (i2c_write(a) == 0) {
     i2c_stop();                   //Release bus, end transaction
     delay(100);                   // then wait 10ms
     Serial.print("CRC correcto: 0x");
     Serial.println(a, HEX);
     return a;
   }
 }
 i2c_stop();                       //Release bus, end transaction
 Serial.println("CRC Correcto no encontrado");
 return -1;
}



Para hacerlo, conectar el sensor al Arduino:



en el siguiente fragmento de código sustituir las dos lineas donde esta 0x5A, con el primer sensor en lugar de 0x5A escribir 0x50



 ejecutar el código hasta ver en el Monitor serie lo siguiente:



con el segundo sensor en lugar de 0x5A escribir 0x50, ejecutar el código hasta ver en el Monitor serie lo siguiente:



Nota: si el Monitor serie solamente muestra lo siguiente y no avanza, 


entonces desconecten el sensor unos 5 segundos vuelvan a conectar el sensor y ejecuten nuevamente el código. Si esto tampoco funciona, entonces desconecten el sensor, ejecuten el código sin el sensor,  conectar el sensor y ejecuten nuevamente el código (En los siguientes pasos donde se ejecute código y no se obtengan resultados hagan los mismo)



Probando las nuevas direcciones

El siguiente código es para leer un solo sensor en una dirección especifica:

/**
 * Termómetro Infrarrojo MLX90614
 * por Jaime Patarroyo
 * basado en 'Is it hot? Arduino + MLX90614 IR Thermometer' por bildr.blog
 * 
 * Devuelve la temperatura en Celcius y Fahrenheit de un termómetro 
 * infrarrojo MLX90614, conectado a los pines TWI/I²C (en la tarjeta Wiring 
 * v1: 0 (SCL) y 1 (SDA) y en la tarjeta Wiring S: 8 (SCL) and 9 (SDA)).
 */

#include <i2cmaster.h>


int deviceAddress = 0x5A<<1;    // En la hoja técnica del MLX906114, 0x5A es 
                                // la dirección de comunicación I²C por defecto.
                                // Corra la dirección 1 bit a la derecha, la
                                // librería I²Cmaster solo necesita los 7 bits
                                // mas significativos para la dirección.

float celcius = 0;              // Variable que contiene la temperatura en Celcius.
float fahrenheit = 0;           // Variable que contiene la temperatura en Fahrenheit.

void setup() {
  Serial.begin(9600);           // Inicia la comunicación serial a 9600bps.

  i2c_init();                               // Inicia el bus i2c.
  PORTC = (1 << PORTC4) | (1 << PORTC5);    // Habilita ‘pullups’.
}

void loop() {
  celcius = temperatureCelcius(deviceAddress);  // Lee los datos del MLX90614
                                                // con la dirección dada,
                                                // los transforma en la
                                                // temperatura en Celcius y
                                                // la almacena en la variable
                                                // celcius.
  
  fahrenheit = (celcius*1.8) + 32;     // Convierte Celcius en Fahrenheit 
                                       // y almacena en la variable Fahrenheit.

  Serial.print("Celcius: ");           // Imprime ambas lecturas en el
  Serial.println(celcius);             // puerto serial.
  Serial.print("Fahrenheit: ");
  Serial.println(fahrenheit);
  Serial.println();

  delay(1000);                         // Espera un segundo antes de imprimir de nuevo.
}

float temperatureCelcius(int address) {
  int dev = address;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  // Escribe
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // Lee
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck();       // Lee 1 byte y envía ack.
  data_high = i2c_readAck();      // Lee 1 byte y envía ack
  pec = i2c_readNak();
  i2c_stop();

  // Esto convierte los bytes altos y bajos juntos y procesa la temperatura.
  double tempFactor = 0.02;       // 0.02 grados por LSB (medida de
                                  // resolución del MLX90614).
  double tempData = 0x0000;       
  int frac;                       // Datos después del punto decimal.

  // Esto oculta el error del byte alto y lo mueve a la izquierda
  // 8 bits y agrega el byte bajo.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;
  float celcius = tempData - 273.15;
  
  // Retorna la temperatura en Celcius.
  return celcius;
}


en el siguiente fragmento de código sustituir la linea donde esta 0x5A, con el primer sensor en lugar de 0x5A escribir 0x50 ejecutar para probar y con el segundo sensor en lugar de 0x5A escribir 0x55 ejecutar para probar.



si obtienen lecturas erróneas como estas:


recuerden desconectar el sensor, ejecutar código, volver a conectar........, y obtendrán las lecturas correctas:





Obteniendo lecturas de ambos sensores



El siguiente código es para obtener la lectura de ambos sensores indicado sus direcciones respectivas:

#include <i2cmaster.h>

int direccionSensor1 = 0x50<<1;   // Dirección del sensor 1 
int direccionSensor2 = 0x55<<1;   // Dirección del sensor 2

float celcius1 = 0;   // Guardara temperatura 1 en Celcius
float celcius2 = 0;   // Guardara temperatura 2 en Celcius

void setup()
{
  Serial.begin(9600);                       // Comunicación serial en 9600bps.
  i2c_init();                               // Inicializar  i2c bus.
  PORTC = (1 << PORTC4) | (1 << PORTC5);    // Pullups.
}

void loop()
{
  celcius1 = temperaturaCelcius(direccionSensor1);   //Lee la temperatura para cada sensor y
  celcius2 = temperaturaCelcius(direccionSensor2);   //lo guarda en la respectiva variable
  
  /*fahrenheit1 = (celcius1*1.8) + 32;     // Sidesean convertir en Fahrenheit 
  fahrenheit2 = (celcius2*1.8) + 32;*/

  Serial.print("Sensor 1: ");    
  Serial.print(celcius1);       //Imprimir valor 1         
  Serial.print("ºC");           
  Serial.println();             //nueva linea
  Serial.print("Sensor 2: ");
  Serial.print(celcius2);       //Imprimir valor 2
  Serial.print("ºC");
  Serial.println();
  Serial.println();

  delay(1000);                         // Espera un segundo para tomar otro par de lecturas
}

//Función con comentarios originales de http://wiki.wiring.co
float temperaturaCelcius(int address) {
  int dev = address;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  // Write
  i2c_start_wait(dev+I2C_WRITE);
  i2c_write(0x07);

  // Read
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_readAck();       // Read 1 byte and then send ack.
  data_high = i2c_readAck();      // Read 1 byte and then send ack.
  pec = i2c_readNak();
  i2c_stop();

  // This converts high and low bytes together and processes temperature, 
  // MSB is a error bit and is ignored for temps.
  double tempFactor = 0.02;       // 0.02 degrees per LSB (measurement 
                                  // resolution of the MLX90614).
  double tempData = 0x0000;       // Zero out the data
  int frac;                       // Data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 
  // 8 bits and adds the low byte.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;
  float celcius = tempData - 273.15;
  
  // Returns temperature un Celcius.
  return celcius;
}

como ven el código ya tiene las dos direcciones que hemos indicado antes 0x50 y 0x55, la otra dirección que pueden probar para un tercer sensor es la que trae de fabrica 0x5A. Aparte de estas tres aun no he investigado como definir otras para conectar 4 o más sensores.

Conectamos los sensores


ejecutamos el código y veremos ambos valores en el Monitor serie:





Mira en esta entrada como mostrar las lecturas en una LCD 20x4: Dos termómetros MLX90614 con LCD 20x4 YwRobot






No hay comentarios:

Publicar un comentario