===== Thermokamera Melexis MLX90620===== Um die Temperatur zu messen, ohne dabei in Kontakt mit dem Objekt zu treten, funktioniert mit eine Wärmebildkamera. Im Labor haben wir die Melexis MLX90620 Thermo Cam (Abb. unten) zur Verfügung. {{ :bauteile:melexis_mlx90620_thermo_cam.jpg?nolink&300 |Melexis MLX90620 Thermo Cam}} Sie hat 16x4 nach dem [[http://de.wikipedia.org/wiki/Bolometer|Bolometer]]-Prinzip aufgebaute Pixel, die langwellige Infrarotstrahlung messen können. Sie kann damit Temperaturen zwischen $-20^\circ C$ und $300^\circ C$ mit einer Framerate von $0,5Hz - 64Hz$ messen. Im Temperaturbereich von $0^\circ C - 50^\circ C$ ist die Genauigkeit angeblich besser als $\pm 1,5^\circ C$. Hier findet ihr das Datenblatt: [[http://www.melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90620-776.aspx|http://www.melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90620-776.aspx]].\\ ==== Installation ==== Die Kamera läuft auf einer recht niedrigen Spannung von 2.6V und ist deshalb nur mit etwas Aufwand mit einem 5V Arduino zusammen zu betreiben. Einfacher funktioniert es mit einem der [[bauteile:teensy3|Teensy3.0++]], die wir im Labor haben. Die sind auch gleich schnell genug, um die Temperaturen mit einer akzetablen Geschwindigkeit und Präzision zu berechnen. Ein Teensy liefert 3.3V. D.h. um die Kamera nicht zu beschädigen, müsst ihr die Spanung begrenzen.[[https://pavlov.tech/2015/05/15/detecting-crying-eyes/|Hier]] gibt es eine Beispielschaltung zu sehen. == Code-Beispiel == Dieses Beispiel ist eine Modifikation von http://forum.arduino.cc/index.php?topic=122306.0 /***************************************************************** This code is design to interface with the MLX90621 sensor. This sensor is a Low noise High Speed 16x4 Far Infrared array. Here is used to obtain data for making a thermal sensor. More information is available here: http://www.melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/Low-noise-high-speed-16x4-Far-Infrared-array-823.aspx Pixel position: The array consists of 64 IR sensors (also called pixels). Each pixel is identified with its row and column position as Pix(i,j) where i is its row number (from 0 to 3) and j is its column number (from 0 to 15) RAM: The on chip 146x16 RAM is accessible for reading via I2C. The RAM is used for storing the results of measurements of pixels and Ta sensor and is distributes as follows: 64 words for IR sensors. The data is in 2’s complement format 1 word for measurement result of PTAT sensor. The data is 16 bit without sign. The memory map of the RAM is shown below: 0x00=IR Sensor(0,0) result 0x01=IR Sensor(1,0) result 0x02=IR Sensor(2,0) result 0x03=IR Sensor(3,0) result 0x04=IR Sensor(0,1) result 0x05=IR Sensor(1,1) result ... 0x3B=IR Sensor(3,14) result 0x3C=IR Sensor(0,15) result 0x3D=IR Sensor(1,15) result 0x3E=IR Sensor(2,15) result 0x3F=IR Sensor(3,15) result Device Addressing: For accessing to internal EEPROM: 0x50 For accessing to IR array data: 0x60 */ //Libraries to be included #include #include //Begin registers #define CAL_ACOMMON_L 0xD0 #define CAL_ACOMMON_H 0xD1 #define CAL_ACP_L 0xD3 #define CAL_ACP_H 0xD4 #define CAL_BCP 0xD5 #define CAL_alphaCP_L 0xD6 #define CAL_alphaCP_H 0xD7 #define CAL_TGC 0xD8 #define CAL_AI_SCALE 0xD9 #define CAL_BI_SCALE 0xD9 #define VTH_L 0xDA #define VTH_H 0xDB #define KT1_L 0xDC #define KT1_H 0xDD #define KT2_L 0xDE #define KT2_H 0xDF #define KT_SCALE 0xD2 //Common sensitivity coefficients #define CAL_A0_L 0xE0 #define CAL_A0_H 0xE1 #define CAL_A0_SCALE 0xE2 #define CAL_DELTA_A_SCALE 0xE3 #define CAL_EMIS_L 0xE4 #define CAL_EMIS_H 0xE5 //Config register = 0xF5-F6 #define OSC_TRIM_VALUE 0xF7 /* Variables */ const byte refreshRate = 1; //Set this value to your desired refresh frequency. Possible values: 0=0.5Hz, 1=1Hz, 2=2Hz, 4=4Hz, 8=8Hz, 16=16Hz, 32=32Hz. int16_t irData[64]; //Contains the raw IR data from the sensor float temperatures[64]; //Contains the calculated temperatures of each pixel in the array byte eepromData[256]; //Contains the full EEPROM reading from the MLX90621 int16_t k_t1_scale = 0, k_t2_scale = 0, resolution = 0, configuration = 0, cpix = 0; uint16_t ptat = 0; // modified: int16_t irDataAtStart[64]; void setup() { Serial.begin(115200); Wire.begin(); delay(5); readEEPROM(); writeTrimmingValue(); setConfiguration(); // the initial values obtained by the camera are read. They are then substracted from the measured values. // This way, if there is no temperature change compared to the observation start, the Serial Monitor // will display an array of 0. readIR(); for (int i = 0; i<64; i++) { irDataAtStart[i] = irData[i]; } } void loop() { if (checkConfig()) { readEEPROM(); writeTrimmingValue(); setConfiguration(); } for (int i = 0; i < 16; i++) //Every 16 readings check that the POR flag is not set { readIR(); Serial.println(); Serial.println(); delay(1000); } } void readEEPROM() { int i = 0; Serial.print("\nReading EEPPROM..."); for (int j = 0; j < 256; j = j + 32) { Wire.beginTransmission(0x50); Wire.write((byte)j); //Wire.endTransmission(false); // use repeated start to get answer Wire.requestFrom(0x50, 32); i = j; Serial.print("\n["); Serial.print(j); Serial.print("] "); while ( Wire.available() ) { // slave may send less than requested eepromData[i] = (byte)Wire.read(); Serial.print(eepromData[i]); Serial.print(" "); i++; } Wire.endTransmission(); } Serial.println(" "); } void writeTrimmingValue() { Serial.print("\nWriting Trimming Value..."); Wire.beginTransmission(0x60); Wire.write(0x04); Wire.write((byte)eepromData[OSC_TRIM_VALUE] - 0xAA); Wire.write(eepromData[OSC_TRIM_VALUE]);//eepromData[0xF7] Wire.write(0x100 - 0xAA); //0x56 Wire.write(0x00); I2Canswer(Wire.endTransmission()); } void setConfiguration() { Serial.print("\nSetting configuration..."); byte Hz_LSB; switch (refreshRate) { case 0: Hz_LSB = 0b00111111; break; case 1: Hz_LSB = 0b00111110; break; case 2: Hz_LSB = 0b00111101; break; case 4: Hz_LSB = 0b00111100; break; case 8: Hz_LSB = 0b00111011; break; case 16: Hz_LSB = 0b00111010; break; case 32: Hz_LSB = 0b00111001; break; default: Hz_LSB = 0b00111110; } //byte defaultConfig_H =0b00000100; //0x04;//0b00000100; byte defaultConfig_H = 0b00000110; Wire.beginTransmission(0x60); Wire.write(0x03); Wire.write((byte)Hz_LSB - 0x55); Wire.write(Hz_LSB); Wire.write(defaultConfig_H - 0x55);//0x14-0x55 Wire.write(defaultConfig_H); I2Canswer(Wire.endTransmission()); //Read the resolution from the config register resolution = (readConfig() & 0x30) >> 4; Serial.print("Resolution: "); Serial.println(resolution); delay(100); } /***************************************************** IR data of the device that it is read by lines by using the following function. *****************************************************/ void readIR() { int i = 0; Serial.print("Reading IRsensor:"); // Due to wire library buffer limitations, we can only read up to 32 bytes at a time // Thus, the request has been split into multiple different requests to get the full 128 values // Each pixel value takes up two bytes (???) thus NUM_PIXELS * 2 for (int line = 0; line < 4; line++) //4 pixels lines { Serial.print("\n["); Serial.print(line); Serial.print("] "); Wire.beginTransmission(0x60);//Sensor Address Wire.write(0x02);//CMD_Sensor_Read Wire.write(line);//lines:0x00,0x01,0x02,0x03 Wire.write(0x04);//0x04 Wire.write(0x10);//0x10 Wire.endTransmission(false); Wire.requestFrom(0x60, 32);//16 pixels per column x 2 bytes per pixel for (int j = 0; j < 16; j++) { // We read two bytes byte pixelDataLow = Wire.read(); byte pixelDataHigh = Wire.read(); irData[i] = (int16_t) ((pixelDataHigh << 8) | pixelDataLow); Serial.print(irData[i] - irDataAtStart[i]); Serial.print(" "); // modified i++; } } I2Canswer(Wire.endTransmission()); } void I2Canswer(int opcion) { switch (opcion) { case 0: Serial.print(" Success. "); break; case 1: Serial.print(" Data too long to fit in transmit buffer. "); break; case 2: Serial.print(" Received NACK on transmit of address. "); break; case 3: Serial.print(" Received NACK on transmit of data. "); break; default: Serial.print(" Other error. "); } } float getTemperature(int num) { if ((num >= 0) && (num < 64)) return temperatures[num]; else return NULL; } //Poll the MLX90621 for its current status //Returns true if the POR/Brown out bit is set boolean checkConfig() //Every 16 readings check that the POR flag is not set { bool check = !((readConfig() & 0x0400) >> 10); return check; } /**************************************************** Configuration Registers of the device can be read by using the following function. ****************************************************/ int16_t readConfig() { int i = 0; byte configLow = 0, configHigh = 0; Serial.print("\nReading Configuration..."); Wire.beginTransmission(0x60); Wire.write(0x02); Wire.write(0x92); Wire.write(0x00); Wire.write(0x01); I2Canswer(Wire.endTransmission(false)); // use repeated start to get answer ) Wire.requestFrom(0x60, 2); while ( Wire.available() ) { i++; byte configLow = Wire.read(); byte configHigh = Wire.read(); if (i > 2) Serial.print("Error reading Config"); } I2Canswer(Wire.endTransmission()); // use repeated start to get answer ) configuration = ((int16_t) (configHigh << 8) | configLow); Serial.print("Configuration:"); Serial.print(configHigh); Serial.println(configLow); return configuration; }