diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 457116347..5d60c5fb4 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -74,4 +74,5 @@ Index | Define | Driver | Device | Address(es) | Description
49 | USE_VEML6075 | xsns_70 | VEML6075 | 0x10 | UVA/UVB/UVINDEX Sensor
50 | USE_VEML7700 | xsns_71 | VEML7700 | 0x10 | Ambient light intensity sensor
51 | USE_MCP9808 | xsns_72 | MCP9808 | 0x18 - 0x1F | Temperature sensor
- 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor
\ No newline at end of file
+ 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor
+ 53 | USE_MLX90640 | xdrv_84 | MLX90640 | 0x33 | IR array temperature sensor
\ No newline at end of file
diff --git a/lib/mlx90640-library/MLX90640_API.cpp b/lib/mlx90640-library/MLX90640_API.cpp
new file mode 100644
index 000000000..87d871530
--- /dev/null
+++ b/lib/mlx90640-library/MLX90640_API.cpp
@@ -0,0 +1,1640 @@
+/**
+ * @copyright (C) 2017 Melexis N.V.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include
+#include
+#include
+
+void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640);
+int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2);
+float GetMedian(float *values, int n);
+int IsPixelBad(uint16_t pixel,paramsMLX90640 *params);
+int ValidateFrameData(uint16_t *frameData);
+int ValidateAuxData(uint16_t *auxData);
+int MLX90640_I2CRead(uint8_t addr, uint32_t reg, uint16_t len, uint16_t *reg_data);
+int MLX90640_I2CWrite(uint8_t _deviceAddress, unsigned int writeAddress, uint16_t data);
+
+// I2C
+#define I2C_BUFFER_LENGTH 128
+
+int MLX90640_I2CRead(uint8_t addr, uint32_t reg, uint16_t len, uint16_t *reg_data){
+ int bytesRemaining = len * 2;
+ int dataSpot = 0; //Start at beginning of array
+ while (bytesRemaining > 0)
+ {
+ Wire.beginTransmission(addr);
+ Wire.write(reg >> 8); //MSB
+ Wire.write(reg & 0xFF); //LSB
+ if (Wire.endTransmission(false) != 0) //Do not release bus
+ {
+ return (0); //Sensor did not ACK
+ }
+ int numberOfBytesToRead = bytesRemaining;
+ if (numberOfBytesToRead > I2C_BUFFER_LENGTH) numberOfBytesToRead = I2C_BUFFER_LENGTH;
+ Wire.requestFrom((int)addr, numberOfBytesToRead);
+ if (Wire.available())
+ {
+ for (uint32_t x = 0 ; x < numberOfBytesToRead / 2; x++)
+ {
+ reg_data[dataSpot] = Wire.read() << 8; //MSB
+ reg_data[dataSpot] |= Wire.read(); //LSB
+ dataSpot++;
+ }
+ }
+ bytesRemaining -= numberOfBytesToRead;
+ reg += numberOfBytesToRead / 2;
+ }
+ return (0); //Success
+}
+
+int MLX90640_I2CWrite(uint8_t _deviceAddress, unsigned int writeAddress, uint16_t data)
+{
+ Wire.beginTransmission((uint8_t)_deviceAddress);
+ Wire.write(writeAddress >> 8); //MSB
+ Wire.write(writeAddress & 0xFF); //LSB
+ Wire.write(data >> 8); //MSB
+ Wire.write(data & 0xFF); //LSB
+ if (Wire.endTransmission() != 0)
+ {
+ //Sensor did not ACK
+ return (-1);
+ }
+ uint16_t dataCheck;
+ MLX90640_I2CRead(_deviceAddress, writeAddress, 1, &dataCheck);
+ if (dataCheck != data)
+ {
+ return -2;
+ }
+ return (0); //Success
+}
+
+int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData)
+{
+ return MLX90640_I2CRead(slaveAddr, 0x2400, 832, eeData);
+}
+
+int MLX90640_SynchFrame(uint8_t slaveAddr)
+{
+ uint16_t dataReady = 0;
+ uint16_t statusRegister;
+ int error = 1;
+
+ error = MLX90640_I2CWrite(slaveAddr, 0x8000, 0x0030);
+ if(error == -1)
+ {
+ return error;
+ }
+
+ while(dataReady == 0)
+ {
+ error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
+ if(error != 0)
+ {
+ return error;
+ }
+ dataReady = statusRegister & 0x0008;
+ }
+
+ return 0;
+}
+
+// int MLX90640_TriggerMeasurement(uint8_t slaveAddr) // ATM not used in Tasmota
+// {
+// int error = 1;
+// uint16_t ctrlReg;
+
+// error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &ctrlReg);
+
+// if ( error != 0)
+// {
+// return error;
+// }
+
+// ctrlReg |= 0x8000;
+// error = MLX90640_I2CWrite(slaveAddr, 0x800D, ctrlReg);
+
+// if ( error != 0)
+// {
+// return error;
+// }
+
+// // error = MLX90640_I2CGeneralReset();
+
+// // if ( error != 0)
+// // {
+// // return error;
+// // }
+
+// error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &ctrlReg);
+
+// if ( error != 0)
+// {
+// return error;
+// }
+
+// if ((ctrlReg & 0x8000) != 0)
+// {
+// return -9;
+// }
+
+// return 0;
+// }
+
+int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData)
+{
+ uint16_t dataReady = 0;
+ uint16_t controlRegister1;
+ uint16_t statusRegister;
+ int error = 1;
+ uint16_t data[64];
+ uint8_t cnt = 0;
+
+ while(dataReady == 0)
+ {
+ error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
+ if(error != 0)
+ {
+ return error;
+ }
+ dataReady = statusRegister & 0x0008;
+ }
+
+ error = MLX90640_I2CWrite(slaveAddr, 0x8000, 0x0030);
+ if(error == -1)
+ {
+ return error;
+ }
+
+ error = MLX90640_I2CRead(slaveAddr, 0x0400, 768, frameData);
+ if(error != 0)
+ {
+ return error;
+ }
+
+ error = MLX90640_I2CRead(slaveAddr, 0x0700, 64, data);
+ if(error != 0)
+ {
+ return error;
+ }
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ frameData[832] = controlRegister1;
+ frameData[833] = statusRegister & 0x0001;
+
+ if(error != 0)
+ {
+ return error;
+ }
+
+ error = ValidateAuxData(data);
+ if(error == 0)
+ {
+ for(cnt=0; cnt<64; cnt++)
+ {
+ frameData[cnt+768] = data[cnt];
+ }
+ }
+
+ error = ValidateFrameData(frameData);
+ if (error != 0)
+ {
+ return error;
+ }
+
+ return frameData[833];
+}
+
+int ValidateFrameData(uint16_t *frameData)
+{
+ uint8_t line = 0;
+
+ for(int i=0; i<768; i+=32)
+ {
+ if((frameData[i] == 0x7FFF) && (line%2 == frameData[833])) return -8;
+ line = line + 1;
+ }
+
+ return 0;
+}
+
+int ValidateAuxData(uint16_t *auxData)
+{
+
+ if(auxData[0] == 0x7FFF) return -8;
+
+ for(int i=8; i<19; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=20; i<23; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=24; i<33; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=40; i<51; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=52; i<55; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=56; i<64; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ return 0;
+
+}
+
+int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640, int _chunk) // Tasmota
+{
+ int error = 0;
+ switch(_chunk){
+ case 0:
+ ExtractVDDParameters(eeData, mlx90640);
+ ExtractPTATParameters(eeData, mlx90640);
+ ExtractGainParameters(eeData, mlx90640);
+ ExtractTgcParameters(eeData, mlx90640);
+ ExtractResolutionParameters(eeData, mlx90640);
+ ExtractKsTaParameters(eeData, mlx90640);
+ ExtractKsToParameters(eeData, mlx90640);
+ break;
+ case 1:
+ ExtractCPParameters(eeData, mlx90640);
+ ExtractAlphaParameters(eeData, mlx90640);
+ break;
+ case 2:
+ ExtractOffsetParameters(eeData, mlx90640);
+ break;
+ case 3:
+ ExtractKtaPixelParameters(eeData, mlx90640);
+ break;
+ case 4:
+ ExtractKvPixelParameters(eeData, mlx90640);
+ break;
+ case 5:
+ ExtractCILCParameters(eeData, mlx90640);
+ error = ExtractDeviatingPixels(eeData, mlx90640);
+ break;
+ }
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ value = (resolution & 0x03) << 10;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+
+ if(error == 0)
+ {
+ value = (controlRegister1 & 0xF3FF) | value;
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetCurResolution(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int resolutionRAM;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error != 0)
+ {
+ return error;
+ }
+ resolutionRAM = (controlRegister1 & 0x0C00) >> 10;
+
+ return resolutionRAM;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ value = (refreshRate & 0x07)<<7;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error == 0)
+ {
+ value = (controlRegister1 & 0xFC7F) | value;
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetRefreshRate(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int refreshRate;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error != 0)
+ {
+ return error;
+ }
+ refreshRate = (controlRegister1 & 0x0380) >> 7;
+
+ return refreshRate;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetInterleavedMode(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+
+ if(error == 0)
+ {
+ value = (controlRegister1 & 0xEFFF);
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetChessMode(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+
+ if(error == 0)
+ {
+ value = (controlRegister1 | 0x1000);
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetCurMode(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int modeRAM;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error != 0)
+ {
+ return error;
+ }
+ modeRAM = (controlRegister1 & 0x1000) >> 12;
+
+ return modeRAM;
+}
+
+//------------------------------------------------------------------------------
+void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result, uint8_t _part)
+{
+ float vdd;
+ float ta;
+ float ta4;
+ float tr4;
+ float taTr;
+ float gain;
+ float irDataCP[2];
+ float irData;
+ float alphaCompensated;
+ uint8_t mode;
+ int8_t ilPattern;
+ int8_t chessPattern;
+ int8_t pattern;
+ int8_t conversionPattern;
+ float Sx;
+ float To;
+ float alphaCorrR[4];
+ int8_t range;
+ uint16_t subPage;
+ float ktaScale;
+ float kvScale;
+ float alphaScale;
+ float kta;
+ float kv;
+
+ subPage = frameData[833];
+ vdd = MLX90640_GetVdd(frameData, params);
+ ta = MLX90640_GetTa(frameData, params);
+
+ ta4 = (ta + 273.15);
+ ta4 = ta4 * ta4;
+ ta4 = ta4 * ta4;
+ tr4 = (tr + 273.15);
+ tr4 = tr4 * tr4;
+ tr4 = tr4 * tr4;
+ taTr = tr4 - (tr4-ta4)/emissivity;
+
+ ktaScale = pow(2,(double)params->ktaScale);
+ kvScale = pow(2,(double)params->kvScale);
+ alphaScale = pow(2,(double)params->alphaScale);
+
+ alphaCorrR[0] = 1 / (1 + params->ksTo[0] * 40);
+ alphaCorrR[1] = 1 ;
+ alphaCorrR[2] = (1 + params->ksTo[1] * params->ct[2]);
+ alphaCorrR[3] = alphaCorrR[2] * (1 + params->ksTo[2] * (params->ct[3] - params->ct[2]));
+
+//------------------------- Gain calculation -----------------------------------
+ gain = frameData[778];
+ if(gain > 32767)
+ {
+ gain = gain - 65536;
+ }
+
+ gain = params->gainEE / gain;
+
+//------------------------- To calculation -------------------------------------
+ mode = (frameData[832] & 0x1000) >> 5;
+
+ irDataCP[0] = frameData[776];
+ irDataCP[1] = frameData[808];
+ for( int i = 0; i < 2; i++)
+ {
+ if(irDataCP[i] > 32767)
+ {
+ irDataCP[i] = irDataCP[i] - 65536;
+ }
+ irDataCP[i] = irDataCP[i] * gain;
+ }
+ irDataCP[0] = irDataCP[0] - params->cpOffset[0] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+ if( mode == params->calibrationModeEE)
+ {
+ irDataCP[1] = irDataCP[1] - params->cpOffset[1] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+ }
+ else
+ {
+ irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+ }
+
+ uint32_t _offset = _part*(768/2);
+ for( int pixelNumber = _offset; pixelNumber < (_offset+(768/2)); pixelNumber++)
+ {
+ ilPattern = pixelNumber / 32 - (pixelNumber / 64) * 2;
+ chessPattern = ilPattern ^ (pixelNumber - (pixelNumber/2)*2);
+ conversionPattern = ((pixelNumber + 2) / 4 - (pixelNumber + 3) / 4 + (pixelNumber + 1) / 4 - pixelNumber / 4) * (1 - 2 * ilPattern);
+
+ if(mode == 0)
+ {
+ pattern = ilPattern;
+ }
+ else
+ {
+ pattern = chessPattern;
+ }
+
+ if(pattern == frameData[833])
+ {
+ irData = frameData[pixelNumber];
+ if(irData > 32767)
+ {
+ irData = irData - 65536;
+ }
+ irData = irData * gain;
+
+ kta = params->kta[pixelNumber]/ktaScale;
+ kv = params->kv[pixelNumber]/kvScale;
+ irData = irData - params->offset[pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3));
+
+ if(mode != params->calibrationModeEE)
+ {
+ irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern;
+ }
+
+ irData = irData - params->tgc * irDataCP[subPage];
+ irData = irData / emissivity;
+
+ alphaCompensated = SCALEALPHA*alphaScale/params->alpha[pixelNumber];
+ alphaCompensated = alphaCompensated*(1 + params->KsTa * (ta - 25));
+
+ Sx = alphaCompensated * alphaCompensated * alphaCompensated * (irData + alphaCompensated * taTr);
+ Sx = sqrt(sqrt(Sx)) * params->ksTo[1];
+
+ To = sqrt(sqrt(irData/(alphaCompensated * (1 - params->ksTo[1] * 273.15) + Sx) + taTr)) - 273.15;
+
+ if(To < params->ct[1])
+ {
+ range = 0;
+ }
+ else if(To < params->ct[2])
+ {
+ range = 1;
+ }
+ else if(To < params->ct[3])
+ {
+ range = 2;
+ }
+ else
+ {
+ range = 3;
+ }
+
+ To = sqrt(sqrt(irData / (alphaCompensated * alphaCorrR[range] * (1 + params->ksTo[range] * (To - params->ct[range]))) + taTr)) - 273.15;
+
+ result[pixelNumber] = To;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+// void MLX90640_GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result)
+// {
+// float vdd;
+// float ta;
+// float gain;
+// float irDataCP[2];
+// float irData;
+// float alphaCompensated;
+// uint8_t mode;
+// int8_t ilPattern;
+// int8_t chessPattern;
+// int8_t pattern;
+// int8_t conversionPattern;
+// float image;
+// uint16_t subPage;
+// float ktaScale;
+// float kvScale;
+// float kta;
+// float kv;
+
+// subPage = frameData[833];
+// vdd = MLX90640_GetVdd(frameData, params);
+// ta = MLX90640_GetTa(frameData, params);
+
+// ktaScale = pow(2,(double)params->ktaScale);
+// kvScale = pow(2,(double)params->kvScale);
+
+// //------------------------- Gain calculation -----------------------------------
+// gain = frameData[778];
+// if(gain > 32767)
+// {
+// gain = gain - 65536;
+// }
+
+// gain = params->gainEE / gain;
+
+// //------------------------- Image calculation -------------------------------------
+// mode = (frameData[832] & 0x1000) >> 5;
+
+// irDataCP[0] = frameData[776];
+// irDataCP[1] = frameData[808];
+// for( int i = 0; i < 2; i++)
+// {
+// if(irDataCP[i] > 32767)
+// {
+// irDataCP[i] = irDataCP[i] - 65536;
+// }
+// irDataCP[i] = irDataCP[i] * gain;
+// }
+// irDataCP[0] = irDataCP[0] - params->cpOffset[0] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+// if( mode == params->calibrationModeEE)
+// {
+// irDataCP[1] = irDataCP[1] - params->cpOffset[1] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+// }
+// else
+// {
+// irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+// }
+
+// for( int pixelNumber = 0; pixelNumber < 768; pixelNumber++)
+// {
+// ilPattern = pixelNumber / 32 - (pixelNumber / 64) * 2;
+// chessPattern = ilPattern ^ (pixelNumber - (pixelNumber/2)*2);
+// conversionPattern = ((pixelNumber + 2) / 4 - (pixelNumber + 3) / 4 + (pixelNumber + 1) / 4 - pixelNumber / 4) * (1 - 2 * ilPattern);
+
+// if(mode == 0)
+// {
+// pattern = ilPattern;
+// }
+// else
+// {
+// pattern = chessPattern;
+// }
+
+// if(pattern == frameData[833])
+// {
+// irData = frameData[pixelNumber];
+// if(irData > 32767)
+// {
+// irData = irData - 65536;
+// }
+// irData = irData * gain;
+
+// kta = params->kta[pixelNumber]/ktaScale;
+// kv = params->kv[pixelNumber]/kvScale;
+// irData = irData - params->offset[pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3));
+
+// if(mode != params->calibrationModeEE)
+// {
+// irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern;
+// }
+
+// irData = irData - params->tgc * irDataCP[subPage];
+
+// alphaCompensated = params->alpha[pixelNumber];
+
+// image = irData*alphaCompensated;
+
+// result[pixelNumber] = image;
+// }
+// }
+// }
+
+//------------------------------------------------------------------------------
+
+float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params)
+{
+ float vdd;
+ float resolutionCorrection;
+
+ int resolutionRAM;
+
+ vdd = frameData[810];
+ if(vdd > 32767)
+ {
+ vdd = vdd - 65536;
+ }
+ resolutionRAM = (frameData[832] & 0x0C00) >> 10;
+ resolutionCorrection = pow(2, (double)params->resolutionEE) / pow(2, (double)resolutionRAM);
+ vdd = (resolutionCorrection * vdd - params->vdd25) / params->kVdd + 3.3;
+
+ return vdd;
+}
+
+//------------------------------------------------------------------------------
+
+float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params)
+{
+ float ptat;
+ float ptatArt;
+ float vdd;
+ float ta;
+
+ vdd = MLX90640_GetVdd(frameData, params);
+
+ ptat = frameData[800];
+ if(ptat > 32767)
+ {
+ ptat = ptat - 65536;
+ }
+
+ ptatArt = frameData[768];
+ if(ptatArt > 32767)
+ {
+ ptatArt = ptatArt - 65536;
+ }
+ ptatArt = (ptat / (ptat * params->alphaPTAT + ptatArt)) * pow(2, (double)18);
+
+ ta = (ptatArt / (1 + params->KvPTAT * (vdd - 3.3)) - params->vPTAT25);
+ ta = ta / params->KtPTAT + 25;
+
+ return ta;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetSubPageNumber(uint16_t *frameData)
+{
+ return frameData[833];
+
+}
+
+//------------------------------------------------------------------------------
+void MLX90640_BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params)
+{
+ float ap[4];
+ uint8_t pix;
+ uint8_t line;
+ uint8_t column;
+
+ pix = 0;
+ while(pixels[pix] != 0xFFFF)
+ {
+ line = pixels[pix]>>5;
+ column = pixels[pix] - (line<<5);
+
+ if(mode == 1)
+ {
+ if(line == 0)
+ {
+ if(column == 0)
+ {
+ to[pixels[pix]] = to[33];
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = to[62];
+ }
+ else
+ {
+ to[pixels[pix]] = (to[pixels[pix]+31] + to[pixels[pix]+33])/2.0;
+ }
+ }
+ else if(line == 23)
+ {
+ if(column == 0)
+ {
+ to[pixels[pix]] = to[705];
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = to[734];
+ }
+ else
+ {
+ to[pixels[pix]] = (to[pixels[pix]-33] + to[pixels[pix]-31])/2.0;
+ }
+ }
+ else if(column == 0)
+ {
+ to[pixels[pix]] = (to[pixels[pix]-31] + to[pixels[pix]+33])/2.0;
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = (to[pixels[pix]-33] + to[pixels[pix]+31])/2.0;
+ }
+ else
+ {
+ ap[0] = to[pixels[pix]-33];
+ ap[1] = to[pixels[pix]-31];
+ ap[2] = to[pixels[pix]+31];
+ ap[3] = to[pixels[pix]+33];
+ to[pixels[pix]] = GetMedian(ap,4);
+ }
+ }
+ else
+ {
+ if(column == 0)
+ {
+ to[pixels[pix]] = to[pixels[pix]+1];
+ }
+ else if(column == 1 || column == 30)
+ {
+ to[pixels[pix]] = (to[pixels[pix]-1]+to[pixels[pix]+1])/2.0;
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = to[pixels[pix]-1];
+ }
+ else
+ {
+ if(IsPixelBad(pixels[pix]-2,params) == 0 && IsPixelBad(pixels[pix]+2,params) == 0)
+ {
+ ap[0] = to[pixels[pix]+1] - to[pixels[pix]+2];
+ ap[1] = to[pixels[pix]-1] - to[pixels[pix]-2];
+ if(fabs(ap[0]) > fabs(ap[1]))
+ {
+ to[pixels[pix]] = to[pixels[pix]-1] + ap[1];
+ }
+ else
+ {
+ to[pixels[pix]] = to[pixels[pix]+1] + ap[0];
+ }
+ }
+ else
+ {
+ to[pixels[pix]] = (to[pixels[pix]-1]+to[pixels[pix]+1])/2.0;
+ }
+ }
+ }
+ pix = pix + 1;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int16_t kVdd;
+ int16_t vdd25;
+
+ kVdd = eeData[51];
+
+ kVdd = (eeData[51] & 0xFF00) >> 8;
+ if(kVdd > 127)
+ {
+ kVdd = kVdd - 256;
+ }
+ kVdd = 32 * kVdd;
+ vdd25 = eeData[51] & 0x00FF;
+ vdd25 = ((vdd25 - 256) << 5) - 8192;
+
+ mlx90640->kVdd = kVdd;
+ mlx90640->vdd25 = vdd25;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float KvPTAT;
+ float KtPTAT;
+ int16_t vPTAT25;
+ float alphaPTAT;
+
+ KvPTAT = (eeData[50] & 0xFC00) >> 10;
+ if(KvPTAT > 31)
+ {
+ KvPTAT = KvPTAT - 64;
+ }
+ KvPTAT = KvPTAT/4096;
+
+ KtPTAT = eeData[50] & 0x03FF;
+ if(KtPTAT > 511)
+ {
+ KtPTAT = KtPTAT - 1024;
+ }
+ KtPTAT = KtPTAT/8;
+
+ vPTAT25 = eeData[49];
+
+ alphaPTAT = (eeData[16] & 0xF000) / pow(2, (double)14) + 8.0f;
+
+ mlx90640->KvPTAT = KvPTAT;
+ mlx90640->KtPTAT = KtPTAT;
+ mlx90640->vPTAT25 = vPTAT25;
+ mlx90640->alphaPTAT = alphaPTAT;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int16_t gainEE;
+
+ gainEE = eeData[48];
+ if(gainEE > 32767)
+ {
+ gainEE = gainEE -65536;
+ }
+
+ mlx90640->gainEE = gainEE;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float tgc;
+ tgc = eeData[60] & 0x00FF;
+ if(tgc > 127)
+ {
+ tgc = tgc - 256;
+ }
+ tgc = tgc / 32.0f;
+
+ mlx90640->tgc = tgc;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ uint8_t resolutionEE;
+ resolutionEE = (eeData[56] & 0x3000) >> 12;
+
+ mlx90640->resolutionEE = resolutionEE;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float KsTa;
+ KsTa = (eeData[60] & 0xFF00) >> 8;
+ if(KsTa > 127)
+ {
+ KsTa = KsTa -256;
+ }
+ KsTa = KsTa / 8192.0f;
+
+ mlx90640->KsTa = KsTa;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int KsToScale;
+ int8_t step;
+
+ step = ((eeData[63] & 0x3000) >> 12) * 10;
+
+ mlx90640->ct[0] = -40;
+ mlx90640->ct[1] = 0;
+ mlx90640->ct[2] = (eeData[63] & 0x00F0) >> 4;
+ mlx90640->ct[3] = (eeData[63] & 0x0F00) >> 8;
+
+ mlx90640->ct[2] = mlx90640->ct[2]*step;
+ mlx90640->ct[3] = mlx90640->ct[2] + mlx90640->ct[3]*step;
+ mlx90640->ct[4] = 400;
+
+ KsToScale = (eeData[63] & 0x000F) + 8;
+ KsToScale = 1 << KsToScale;
+
+ mlx90640->ksTo[0] = eeData[61] & 0x00FF;
+ mlx90640->ksTo[1] = (eeData[61] & 0xFF00) >> 8;
+ mlx90640->ksTo[2] = eeData[62] & 0x00FF;
+ mlx90640->ksTo[3] = (eeData[62] & 0xFF00) >> 8;
+
+ for(int i = 0; i < 4; i++)
+ {
+ if(mlx90640->ksTo[i] > 127)
+ {
+ mlx90640->ksTo[i] = mlx90640->ksTo[i] - 256;
+ }
+ mlx90640->ksTo[i] = mlx90640->ksTo[i] / KsToScale;
+ }
+
+ mlx90640->ksTo[4] = -0.0002;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int accRow[24];
+ int accColumn[32];
+ int p = 0;
+ int alphaRef;
+ uint8_t alphaScale;
+ uint8_t accRowScale;
+ uint8_t accColumnScale;
+ uint8_t accRemScale;
+ float alphaTemp[768];
+ float temp;
+
+
+ accRemScale = eeData[32] & 0x000F;
+ accColumnScale = (eeData[32] & 0x00F0) >> 4;
+ accRowScale = (eeData[32] & 0x0F00) >> 8;
+ alphaScale = ((eeData[32] & 0xF000) >> 12) + 30;
+ alphaRef = eeData[33];
+
+ for(int i = 0; i < 6; i++)
+ {
+ p = i * 4;
+ accRow[p + 0] = (eeData[34 + i] & 0x000F);
+ accRow[p + 1] = (eeData[34 + i] & 0x00F0) >> 4;
+ accRow[p + 2] = (eeData[34 + i] & 0x0F00) >> 8;
+ accRow[p + 3] = (eeData[34 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ if (accRow[i] > 7)
+ {
+ accRow[i] = accRow[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ p = i * 4;
+ accColumn[p + 0] = (eeData[40 + i] & 0x000F);
+ accColumn[p + 1] = (eeData[40 + i] & 0x00F0) >> 4;
+ accColumn[p + 2] = (eeData[40 + i] & 0x0F00) >> 8;
+ accColumn[p + 3] = (eeData[40 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 32; i ++)
+ {
+ if (accColumn[i] > 7)
+ {
+ accColumn[i] = accColumn[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ alphaTemp[p] = (eeData[64 + p] & 0x03F0) >> 4;
+ if (alphaTemp[p] > 31)
+ {
+ alphaTemp[p] = alphaTemp[p] - 64;
+ }
+ alphaTemp[p] = alphaTemp[p]*(1 << accRemScale);
+ alphaTemp[p] = (alphaRef + (accRow[i] << accRowScale) + (accColumn[j] << accColumnScale) + alphaTemp[p]);
+ alphaTemp[p] = alphaTemp[p] / pow(2,(double)alphaScale);
+ alphaTemp[p] = alphaTemp[p] - mlx90640->tgc * (mlx90640->cpAlpha[0] + mlx90640->cpAlpha[1])/2;
+ alphaTemp[p] = SCALEALPHA/alphaTemp[p];
+ }
+ }
+
+ temp = alphaTemp[0];
+ for(int i = 1; i < 768; i++)
+ {
+ if (alphaTemp[i] > temp)
+ {
+ temp = alphaTemp[i];
+ }
+ }
+
+ alphaScale = 0;
+ while(temp < 32767.4)
+ {
+ temp = temp*2;
+ alphaScale = alphaScale + 1;
+ }
+
+ for(int i = 0; i < 768; i++)
+ {
+ temp = alphaTemp[i] * pow(2,(double)alphaScale);
+ mlx90640->alpha[i] = (temp + 0.5);
+
+ }
+
+ mlx90640->alphaScale = alphaScale;
+
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int occRow[24];
+ int occColumn[32];
+ int p = 0;
+ int16_t offsetRef;
+ uint8_t occRowScale;
+ uint8_t occColumnScale;
+ uint8_t occRemScale;
+
+
+ occRemScale = (eeData[16] & 0x000F);
+ occColumnScale = (eeData[16] & 0x00F0) >> 4;
+ occRowScale = (eeData[16] & 0x0F00) >> 8;
+ offsetRef = eeData[17];
+ if (offsetRef > 32767)
+ {
+ offsetRef = offsetRef - 65536;
+ }
+
+ for(int i = 0; i < 6; i++)
+ {
+ p = i * 4;
+ occRow[p + 0] = (eeData[18 + i] & 0x000F);
+ occRow[p + 1] = (eeData[18 + i] & 0x00F0) >> 4;
+ occRow[p + 2] = (eeData[18 + i] & 0x0F00) >> 8;
+ occRow[p + 3] = (eeData[18 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ if (occRow[i] > 7)
+ {
+ occRow[i] = occRow[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ p = i * 4;
+ occColumn[p + 0] = (eeData[24 + i] & 0x000F);
+ occColumn[p + 1] = (eeData[24 + i] & 0x00F0) >> 4;
+ occColumn[p + 2] = (eeData[24 + i] & 0x0F00) >> 8;
+ occColumn[p + 3] = (eeData[24 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 32; i ++)
+ {
+ if (occColumn[i] > 7)
+ {
+ occColumn[i] = occColumn[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ mlx90640->offset[p] = (eeData[64 + p] & 0xFC00) >> 10;
+ if (mlx90640->offset[p] > 31)
+ {
+ mlx90640->offset[p] = mlx90640->offset[p] - 64;
+ }
+ mlx90640->offset[p] = mlx90640->offset[p]*(1 << occRemScale);
+ mlx90640->offset[p] = (offsetRef + (occRow[i] << occRowScale) + (occColumn[j] << occColumnScale) + mlx90640->offset[p]);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int p = 0;
+ int8_t KtaRC[4];
+ int8_t KtaRoCo;
+ int8_t KtaRoCe;
+ int8_t KtaReCo;
+ int8_t KtaReCe;
+ uint8_t ktaScale1;
+ uint8_t ktaScale2;
+ uint8_t split;
+ float ktaTemp[768];
+ float temp;
+
+ KtaRoCo = (eeData[54] & 0xFF00) >> 8;
+ if (KtaRoCo > 127)
+ {
+ KtaRoCo = KtaRoCo - 256;
+ }
+ KtaRC[0] = KtaRoCo;
+
+ KtaReCo = (eeData[54] & 0x00FF);
+ if (KtaReCo > 127)
+ {
+ KtaReCo = KtaReCo - 256;
+ }
+ KtaRC[2] = KtaReCo;
+
+ KtaRoCe = (eeData[55] & 0xFF00) >> 8;
+ if (KtaRoCe > 127)
+ {
+ KtaRoCe = KtaRoCe - 256;
+ }
+ KtaRC[1] = KtaRoCe;
+
+ KtaReCe = (eeData[55] & 0x00FF);
+ if (KtaReCe > 127)
+ {
+ KtaReCe = KtaReCe - 256;
+ }
+ KtaRC[3] = KtaReCe;
+
+ ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8;
+ ktaScale2 = (eeData[56] & 0x000F);
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ split = 2*(p/32 - (p/64)*2) + p%2;
+ ktaTemp[p] = (eeData[64 + p] & 0x000E) >> 1;
+ if (ktaTemp[p] > 3)
+ {
+ ktaTemp[p] = ktaTemp[p] - 8;
+ }
+ ktaTemp[p] = ktaTemp[p] * (1 << ktaScale2);
+ ktaTemp[p] = KtaRC[split] + ktaTemp[p];
+ ktaTemp[p] = ktaTemp[p] / pow(2,(double)ktaScale1);
+ //ktaTemp[p] = ktaTemp[p] * mlx90640->offset[p];
+ }
+ }
+
+ temp = fabs(ktaTemp[0]);
+ for(int i = 1; i < 768; i++)
+ {
+ if (fabs(ktaTemp[i]) > temp)
+ {
+ temp = fabs(ktaTemp[i]);
+ }
+ }
+
+ ktaScale1 = 0;
+ while(temp < 63.4)
+ {
+ temp = temp*2;
+ ktaScale1 = ktaScale1 + 1;
+ }
+
+ for(int i = 0; i < 768; i++)
+ {
+ temp = ktaTemp[i] * pow(2,(double)ktaScale1);
+ if (temp < 0)
+ {
+ mlx90640->kta[i] = (temp - 0.5);
+ }
+ else
+ {
+ mlx90640->kta[i] = (temp + 0.5);
+ }
+
+ }
+
+ mlx90640->ktaScale = ktaScale1;
+}
+
+
+//------------------------------------------------------------------------------
+
+void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int p = 0;
+ int8_t KvT[4];
+ int8_t KvRoCo;
+ int8_t KvRoCe;
+ int8_t KvReCo;
+ int8_t KvReCe;
+ uint8_t kvScale;
+ uint8_t split;
+ float kvTemp[768];
+ float temp;
+
+ KvRoCo = (eeData[52] & 0xF000) >> 12;
+ if (KvRoCo > 7)
+ {
+ KvRoCo = KvRoCo - 16;
+ }
+ KvT[0] = KvRoCo;
+
+ KvReCo = (eeData[52] & 0x0F00) >> 8;
+ if (KvReCo > 7)
+ {
+ KvReCo = KvReCo - 16;
+ }
+ KvT[2] = KvReCo;
+
+ KvRoCe = (eeData[52] & 0x00F0) >> 4;
+ if (KvRoCe > 7)
+ {
+ KvRoCe = KvRoCe - 16;
+ }
+ KvT[1] = KvRoCe;
+
+ KvReCe = (eeData[52] & 0x000F);
+ if (KvReCe > 7)
+ {
+ KvReCe = KvReCe - 16;
+ }
+ KvT[3] = KvReCe;
+
+ kvScale = (eeData[56] & 0x0F00) >> 8;
+
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ split = 2*(p/32 - (p/64)*2) + p%2;
+ kvTemp[p] = KvT[split];
+ kvTemp[p] = kvTemp[p] / pow(2,(double)kvScale);
+ //kvTemp[p] = kvTemp[p] * mlx90640->offset[p];
+ }
+ }
+
+ temp = fabs(kvTemp[0]);
+ for(int i = 1; i < 768; i++)
+ {
+ if (fabs(kvTemp[i]) > temp)
+ {
+ temp = fabs(kvTemp[i]);
+ }
+ }
+
+ kvScale = 0;
+ while(temp < 63.4)
+ {
+ temp = temp*2;
+ kvScale = kvScale + 1;
+ }
+
+ for(int i = 0; i < 768; i++)
+ {
+ temp = kvTemp[i] * pow(2,(double)kvScale);
+ if (temp < 0)
+ {
+ mlx90640->kv[i] = (temp - 0.5);
+ }
+ else
+ {
+ mlx90640->kv[i] = (temp + 0.5);
+ }
+
+ }
+
+ mlx90640->kvScale = kvScale;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float alphaSP[2];
+ int16_t offsetSP[2];
+ float cpKv;
+ float cpKta;
+ uint8_t alphaScale;
+ uint8_t ktaScale1;
+ uint8_t kvScale;
+
+ alphaScale = ((eeData[32] & 0xF000) >> 12) + 27;
+
+ offsetSP[0] = (eeData[58] & 0x03FF);
+ if (offsetSP[0] > 511)
+ {
+ offsetSP[0] = offsetSP[0] - 1024;
+ }
+
+ offsetSP[1] = (eeData[58] & 0xFC00) >> 10;
+ if (offsetSP[1] > 31)
+ {
+ offsetSP[1] = offsetSP[1] - 64;
+ }
+ offsetSP[1] = offsetSP[1] + offsetSP[0];
+
+ alphaSP[0] = (eeData[57] & 0x03FF);
+ if (alphaSP[0] > 511)
+ {
+ alphaSP[0] = alphaSP[0] - 1024;
+ }
+ alphaSP[0] = alphaSP[0] / pow(2,(double)alphaScale);
+
+ alphaSP[1] = (eeData[57] & 0xFC00) >> 10;
+ if (alphaSP[1] > 31)
+ {
+ alphaSP[1] = alphaSP[1] - 64;
+ }
+ alphaSP[1] = (1 + alphaSP[1]/128) * alphaSP[0];
+
+ cpKta = (eeData[59] & 0x00FF);
+ if (cpKta > 127)
+ {
+ cpKta = cpKta - 256;
+ }
+ ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8;
+ mlx90640->cpKta = cpKta / pow(2,(double)ktaScale1);
+
+ cpKv = (eeData[59] & 0xFF00) >> 8;
+ if (cpKv > 127)
+ {
+ cpKv = cpKv - 256;
+ }
+ kvScale = (eeData[56] & 0x0F00) >> 8;
+ mlx90640->cpKv = cpKv / pow(2,(double)kvScale);
+
+ mlx90640->cpAlpha[0] = alphaSP[0];
+ mlx90640->cpAlpha[1] = alphaSP[1];
+ mlx90640->cpOffset[0] = offsetSP[0];
+ mlx90640->cpOffset[1] = offsetSP[1];
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float ilChessC[3];
+ uint8_t calibrationModeEE;
+
+ calibrationModeEE = (eeData[10] & 0x0800) >> 4;
+ calibrationModeEE = calibrationModeEE ^ 0x80;
+
+ ilChessC[0] = (eeData[53] & 0x003F);
+ if (ilChessC[0] > 31)
+ {
+ ilChessC[0] = ilChessC[0] - 64;
+ }
+ ilChessC[0] = ilChessC[0] / 16.0f;
+
+ ilChessC[1] = (eeData[53] & 0x07C0) >> 6;
+ if (ilChessC[1] > 15)
+ {
+ ilChessC[1] = ilChessC[1] - 32;
+ }
+ ilChessC[1] = ilChessC[1] / 2.0f;
+
+ ilChessC[2] = (eeData[53] & 0xF800) >> 11;
+ if (ilChessC[2] > 15)
+ {
+ ilChessC[2] = ilChessC[2] - 32;
+ }
+ ilChessC[2] = ilChessC[2] / 8.0f;
+
+ mlx90640->calibrationModeEE = calibrationModeEE;
+ mlx90640->ilChessC[0] = ilChessC[0];
+ mlx90640->ilChessC[1] = ilChessC[1];
+ mlx90640->ilChessC[2] = ilChessC[2];
+}
+
+//------------------------------------------------------------------------------
+
+int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ uint16_t pixCnt = 0;
+ uint16_t brokenPixCnt = 0;
+ uint16_t outlierPixCnt = 0;
+ int warn = 0;
+ int i;
+
+ for(pixCnt = 0; pixCnt<5; pixCnt++)
+ {
+ mlx90640->brokenPixels[pixCnt] = 0xFFFF;
+ mlx90640->outlierPixels[pixCnt] = 0xFFFF;
+ }
+
+ pixCnt = 0;
+ while (pixCnt < 768 && brokenPixCnt < 5 && outlierPixCnt < 5)
+ {
+ if(eeData[pixCnt+64] == 0)
+ {
+ mlx90640->brokenPixels[brokenPixCnt] = pixCnt;
+ brokenPixCnt = brokenPixCnt + 1;
+ }
+ else if((eeData[pixCnt+64] & 0x0001) != 0)
+ {
+ mlx90640->outlierPixels[outlierPixCnt] = pixCnt;
+ outlierPixCnt = outlierPixCnt + 1;
+ }
+
+ pixCnt = pixCnt + 1;
+
+ }
+
+ if(brokenPixCnt > 4)
+ {
+ warn = -3;
+ }
+ else if(outlierPixCnt > 4)
+ {
+ warn = -4;
+ }
+ else if((brokenPixCnt + outlierPixCnt) > 4)
+ {
+ warn = -5;
+ }
+ else
+ {
+ for(pixCnt=0; pixCntbrokenPixels[pixCnt],mlx90640->brokenPixels[i]);
+ if(warn != 0)
+ {
+ return warn;
+ }
+ }
+ }
+
+ for(pixCnt=0; pixCntoutlierPixels[pixCnt],mlx90640->outlierPixels[i]);
+ if(warn != 0)
+ {
+ return warn;
+ }
+ }
+ }
+
+ for(pixCnt=0; pixCntbrokenPixels[pixCnt],mlx90640->outlierPixels[i]);
+ if(warn != 0)
+ {
+ return warn;
+ }
+ }
+ }
+
+ }
+
+
+ return warn;
+
+}
+
+//------------------------------------------------------------------------------
+
+ int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2)
+ {
+ int pixPosDif;
+
+ pixPosDif = pix1 - pix2;
+ if(pixPosDif > -34 && pixPosDif < -30)
+ {
+ return -6;
+ }
+ if(pixPosDif > -2 && pixPosDif < 2)
+ {
+ return -6;
+ }
+ if(pixPosDif > 30 && pixPosDif < 34)
+ {
+ return -6;
+ }
+
+ return 0;
+ }
+
+//------------------------------------------------------------------------------
+
+float GetMedian(float *values, int n)
+ {
+ float temp;
+
+ for(int i=0; ioutlierPixels[i] || pixel == params->brokenPixels[i])
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------------
diff --git a/lib/mlx90640-library/MLX90640_API.h b/lib/mlx90640-library/MLX90640_API.h
new file mode 100644
index 000000000..efbcdff5b
--- /dev/null
+++ b/lib/mlx90640-library/MLX90640_API.h
@@ -0,0 +1,74 @@
+/**
+ * @copyright (C) 2017 Melexis N.V.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#ifndef _MLX90640_API_H_
+#define _MLX90640_API_H_
+
+#include
+
+#define SCALEALPHA 0.000001
+
+typedef struct
+ {
+ int16_t kVdd;
+ int16_t vdd25;
+ float KvPTAT;
+ float KtPTAT;
+ uint16_t vPTAT25;
+ float alphaPTAT;
+ int16_t gainEE;
+ float tgc;
+ float cpKv;
+ float cpKta;
+ uint8_t resolutionEE;
+ uint8_t calibrationModeEE;
+ float KsTa;
+ float ksTo[5];
+ int16_t ct[5];
+ uint16_t alpha[768];
+ uint8_t alphaScale;
+ int16_t offset[768];
+ int8_t kta[768];
+ uint8_t ktaScale;
+ int8_t kv[768];
+ uint8_t kvScale;
+ float cpAlpha[2];
+ int16_t cpOffset[2];
+ float ilChessC[3];
+ uint16_t brokenPixels[5];
+ uint16_t outlierPixels[5];
+ } paramsMLX90640;
+
+ int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData);
+ int MLX90640_SynchFrame(uint8_t slaveAddr);
+ // int MLX90640_TriggerMeasurement(uint8_t slaveAddr);
+ int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData);
+ int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640,int _chunk);
+ float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params);
+ float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params);
+ // void MLX90640_GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result);
+ void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result, uint8_t _part);
+ int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution);
+ int MLX90640_GetCurResolution(uint8_t slaveAddr);
+ int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate);
+ int MLX90640_GetRefreshRate(uint8_t slaveAddr);
+ int MLX90640_GetSubPageNumber(uint16_t *frameData);
+ int MLX90640_GetCurMode(uint8_t slaveAddr);
+ int MLX90640_SetInterleavedMode(uint8_t slaveAddr);
+ int MLX90640_SetChessMode(uint8_t slaveAddr);
+ void MLX90640_BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params);
+
+#endif
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 961a82e10..d74cecfe5 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -558,6 +558,7 @@
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
// #define USE_HP303B // [I2cDriver52] Enable HP303B temperature and pressure sensor (I2C address 0x76 or 0x77) (+6k2 code)
+// #define USE_MLX90640 // [I2cDriver53] Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+4k9 code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
diff --git a/tasmota/xdrv_84_MLX90640.ino b/tasmota/xdrv_84_MLX90640.ino
new file mode 100644
index 000000000..a1016c917
--- /dev/null
+++ b/tasmota/xdrv_84_MLX90640.ino
@@ -0,0 +1,626 @@
+/*
+ xdrv_84_MLX90640.ino - MLX90640 support for Tasmota
+
+ Copyright (C) 2020 Christian Baars and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+
+ --------------------------------------------------------------------------------------------
+ Version yyyymmdd Action Description
+ --------------------------------------------------------------------------------------------
+ 0.9.0.0 20200827 started - based on https://github.com/melexis/mlx90640-library
+*/
+
+#ifdef USE_I2C
+#ifdef USE_MLX90640
+
+#define MLX90640_ADDRESS 0x33
+#define MLX90640_POI_NUM 6 //some parts of the JS are hardcoded for 6!!
+
+/*********************************************************************************************\
+* MLX90640
+\*********************************************************************************************/
+
+#define XDRV_84 84
+#define XI2C_53 53 // See I2CDEVICES.md
+#include
+
+const char MLX90640type[] PROGMEM = "MLX90640";
+
+#ifdef USE_WEBSERVER
+#define WEB_HANDLE_MLX90640 "mlx"
+const char HTTP_BTN_MENU_MLX90640[] PROGMEM = "
";
+#endif // USE_WEBSERVER
+
+struct {
+ uint32_t type:1;
+ uint32_t ready:1;
+ uint32_t dumpedEE:1;
+ uint32_t extractedParams:1;
+ paramsMLX90640 *params;
+ float Ta;
+ uint16_t Frame[834];
+ float To[768];
+ uint8_t pois[2*MLX90640_POI_NUM] = {2,1, 30,1, 10,12, 22,12, 2,23, 30,23}; // {x1,y1,x2,y2,...,x6,y6}
+} MLX90640;
+
+/*********************************************************************************************\
+ * commands
+\*********************************************************************************************/
+
+#define D_CMND_MLX90640 "MLX"
+
+const char S_JSON_MLX90640_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MLX90640 "%s\":%d}";
+const char S_JSON_MLX90640_COMMAND[] PROGMEM = "{\"" D_CMND_MLX90640 "%s\"}";
+const char kMLX90640_Commands[] PROGMEM = "POI";
+
+enum MLX90640_Commands { // commands useable in console or rules
+ CMND_MLX90640_POI // MLXPOIn xxyy - set POI number n to x,y
+ };
+
+/************************************************************************\
+ * Web GUI
+\************************************************************************/
+#ifdef USE_WEBSERVER
+
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_MLX90640_1_SNS_SIZE = 389;
+const char HTTP_MLX90640_1_SNS_COMPRESSED[] PROGMEM = "\x3D\x3C\x1F\xF4\x65\x2A\x2B\x32\x18\xCF\x87\xDD\x33\x65\x1D\x86\xBB\x33\xB0\x41"
+ "\xA4\x7D\x9F\x81\xE7\x7A\x90\xDB\x18\x7C\x3B\xA6\x76\x10\xB6\x75\x1B\x0E\x43\xA8"
+ "\x8C\x8E\x43\xA8\x8D\x87\x28\xEA\x23\x23\x94\x77\x8F\x87\xE1\x02\x0D\x13\xAC\xD8"
+ "\x72\x1D\xE3\xD6\x77\x48\xC8\xE5\x1D\x64\x6C\x39\x47\x78\xEC\x3B\xA4\x64\x72\x1D"
+ "\x64\x6C\x39\x0E\xF1\xDB\x23\x61\xCA\x3C\x10\x20\xE3\x3A\x36\xC7\x9A\x3E\x2E\x63"
+ "\xE8\xB4\x6D\x8F\x33\xC1\x9D\xFD\x07\x7C\x67\x7E\x3A\x83\xA3\x61\xD4\x3D\xF1\x0F"
+ "\x06\x77\xF4\x3C\x43\x0D\x87\x50\xCC\xD3\xE1\xEF\x1E\xF9\xE0\xCE\xFE\xBE\x56\x7C"
+ "\x3D\xE3\xDF\x3C\x18\x17\xC1\xD6\xE7\x21\xE7\x44\x37\x05\xF9\x90\xCC\xF1\xDD\x04"
+ "\x2C\x65\x33\x3A\x3B\xC8\xF6\x82\x0E\x87\xF6\x1D\x23\xE0\x21\x66\x87\x41\xE7\x44"
+ "\x3B\x05\xF0\x9B\xC3\xC4\x18\x5A\xFA\x8B\xEC\x3A\x3B\xA7\x78\xF0\x67\x7F\x46\xC4"
+ "\x7C\x4C\xCE\x8E\x81\x85\xAF\xA8\x8D\x87\x5F\xD8\x74\x74\x09\x98\xA3\xC6\x98\x3B"
+ "\xA6\xC3\xF0\xE5\xD3\x3B\xC7\xB4\x8D\x87\xC3\x97\x11\xE0\xF7\x17\xDD\x0B\xFF\x23"
+ "\xDA\x6C\x3C\xD1\x0D\xBA\x14\x74\x30\x16\x67\xCE\xE8\xDB\x18\x77\x4D\x87\x51\xC6"
+ "\x75\x5D\x33\xA9\x9D\x57\x0E\x88\xEF\x1D\xE3\xA8\x8C\x81\x32\xF9\xDD\x04\x5D\x04"
+ "\x8C\x91\xD6\xBE\xC3\xA3\xA5\x60\xC3\xBC\x75\x1C\x67\x55\x63\x3A\x99\xD5\x56\x74"
+ "\x47\x78\xEF\x1E\xE3\xC1\xEE";
+#define HTTP_MLX90640_1_SNS Decompress(HTTP_MLX90640_1_SNS_COMPRESSED,HTTP_MLX90640_1_SNS_SIZE).c_str()
+#else
+const char HTTP_MLX90640_1_SNS[] PROGMEM =
+ ""
+;
+#endif //USE_UNISHOX_COMPRESSION
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_MLX90640_4b_SNS_SIZE = 418;
+const char HTTP_MLX90640_4b_SNS_COMPRESSED[] PROGMEM = "\x3D\x07\x60\x86\x4B\x38\x2C\xB1\x0F\x87\xDF\x9D\x0B\x18\x77\x4E\xF1\xE0\xFB\x3F"
+ "\x0F\x40\xEF\x8C\xEF\xCB\x44\x3E\x1F\x63\x42\x36\x1F\x68\x7F\x44\xA1\x47\xC3\xEC"
+ "\xE5\xE3\x3E\xCE\xE1\x0A\x7A\x3C\x2A\x2B\x8F\x87\xD9\xCA\xC6\x7D\x9F\x87\xA1\xD8"
+ "\x40\x83\x83\x9F\x87\xA0\x9A\x66\x7E\x1E\x87\x60\x9A\x66\x7E\x1E\x9E\x61\x30\xE9"
+ "\x68\x87\xC3\xEC\x66\x69\x04\x7D\xAC\xE0\xC5\x5F\x0F\x33\xE1\xF6\x37\x3C\x77\x4E"
+ "\xF1\xF6\x7E\x1E\x98\x32\xB7\x39\x19\xD8\x42\xD9\xF0\xFB\x38\xCF\xB3\xF0\x88\x61"
+ "\x61\x69\xD6\x72\x1E\x87\x61\x02\x0D\x40\x4B\xB8\x72\x10\x20\xDC\x39\x44\x0A\x77"
+ "\x0E\x51\x02\x0D\xC3\x96\x40\xA7\x70\xE5\x90\x20\xDC\x39\x84\x0A\x77\x0E\x61\x02"
+ "\x0D\xC3\x9A\x40\xA7\x70\xE6\x90\x20\xDC\x39\xC4\x08\xB7\x0E\xC0\x41\xE1\x2A\x01"
+ "\xFC\x3D\x04\xD3\x30\x41\xE2\x0C\xE4\x3E\xC8\x10\xF8\x5B\x13\x4C\xCF\xC2\x18\x58"
+ "\x5A\x75\x9C\x67\x99\xDC\x3D\x0B\xC3\x2F\x96\x88\x7C\x3E\xEC\xE4\x3E\xCF\xC3\xD0"
+ "\xEC\x2F\x0C\xBE\x3F\x26\x3B\x32\xF2\x0D\x1D\xDF\x3E\xF6\x7C\xEF\x02\x2E\x1E\x08"
+ "\x39\x11\xCA\x20\x44\xC8\x8E\xC1\xD8\x21\x91\xF8";
+#define HTTP_MLX90640_4b_SNS Decompress(HTTP_MLX90640_4b_SNS_COMPRESSED,HTTP_MLX90640_4b_SNS_SIZE).c_str()
+#else
+const char HTTP_MLX90640_4b_SNS[] PROGMEM =
+ ""
+ ""
+ ""
+ ""
+ ""
+ "POI-0: °C (sensor)
"
+ ""
+ ""
+ ;
+#endif //USE_UNISHOX_COMPRESSION
+void MLX90640UpdateGUI(void){
+ WSContentStart_P("mlx");
+ WSContentSendStyle();
+ WSContentSend_P(HTTP_MLX90640_1_SNS);
+ WSContentSend_P(HTTP_MLX90640_2a_SNS);
+ WSContentSend_P(HTTP_MLX90640_2b_SNS);
+ WSContentSend_P(HTTP_MLX90640_3a_SNS);
+ WSContentSend_P(HTTP_MLX90640_3b_SNS);
+ WSContentSend_P(HTTP_MLX90640_4a_SNS);
+ WSContentSend_P(HTTP_MLX90640_4b_SNS);
+ WSContentSpaceButton(BUTTON_MAIN);
+ WSContentStop();
+}
+
+void MLX90640HandleWebGuiResponse(void){
+ char tmp[(MLX90640_POI_NUM*2)+4];
+ WebGetArg("ul", tmp, sizeof(tmp)); // update line
+ if (strlen(tmp)) {
+ uint8_t _line = atoi(tmp);
+ // AddLog_P2(LOG_LEVEL_DEBUG, "MLX90640: send line %u", _line);
+ float _buf[65];
+ if(_line==0){_buf[0]=1000+MLX90640.Ta;} //ambient temperature modulation hack
+ else{_buf[0]=(float)_line;}
+ memcpy((char*)&_buf[1],(char*)&MLX90640.To[_line*64],64*4);
+ Webserver->send(200,PSTR("application/octet-stream"),(const char*)&_buf,65*4);
+ return;
+ }
+ WebGetArg("up", tmp, sizeof(tmp)); // update POI to browser
+ if (strlen(tmp)==1) {
+ Webserver->send(200,PSTR("application/octet-stream"),(const char*)&MLX90640.pois,MLX90640_POI_NUM*2);
+ return;
+ }
+ else if (strlen(tmp)>2) { // receive updated POI from browser
+ uint32_t _poi = atoi(tmp);
+ uint32_t _poiNum = (_poi-(_poi%10000))/10000;
+ MLX90640.pois[_poiNum*2] = (_poi%10000)/100;
+ MLX90640.pois[(_poiNum*2)+1] = _poi%100;
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RAW: %u, POI-%u: x: %u, y: %u"),_poi,_poiNum,MLX90640.pois[_poiNum],MLX90640.pois[_poiNum+1]);
+ for(int i = 0;i(MLX90640_POI_NUM-1)&&XdrvMailbox.index<1) return false;
+ _idx = (XdrvMailbox.index-1)*2;
+ if (XdrvMailbox.data_len > 0) {
+ uint32_t _coord = TextToInt(XdrvMailbox.data);
+ MLX90640.pois[_idx] = (_coord%10000)/100;
+ if(MLX90640.pois[_idx]>31) MLX90640.pois[_idx]=31;
+ MLX90640.pois[_idx+1] = _coord%100;
+ if(MLX90640.pois[_idx+1]>23) MLX90640.pois[_idx+1]=23;
+ }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("POI-%u = x:%u,y:%u"),XdrvMailbox.index,MLX90640.pois[_idx],MLX90640.pois[_idx+1]);
+ Response_P(S_JSON_MLX90640_COMMAND_NVALUE, command, XdrvMailbox.payload);
+ break;
+ default:
+ // else for Unknown command
+ serviced = false;
+ break;
+ }
+ } else {
+ return false;
+ }
+ return serviced;
+}
+
+/************************************************************************\
+ * Init
+\************************************************************************/
+void MLX90640init()
+{
+ if (MLX90640.type || !I2cSetDevice(MLX90640_ADDRESS)) { return; }
+
+ Wire.setClock(400000);
+ int status = -1;
+ if(!MLX90640.dumpedEE){
+ status = MLX90640_DumpEE(MLX90640_ADDRESS, MLX90640.Frame);
+ if (status != 0){
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("Failed to load system parameters"));
+ }
+ else {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MLX90640: started"));
+ MLX90640.type = true;
+ }
+ MLX90640.params = new paramsMLX90640;
+ }
+}
+
+/************************************************************************\
+ * Run loop
+\************************************************************************/
+void MLX90640every100msec(){
+ static uint32_t _job = 0;
+ int status;
+ uint32_t _time;
+
+ if(!MLX90640.extractedParams){
+ static uint32_t _chunk = 0;
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: will read chunk: %u"), _chunk);
+ _time = millis();
+ status = MLX90640_ExtractParameters(MLX90640.Frame, MLX90640.params, _chunk);
+ if (status == 0){
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: parameter received after: %u msec, status: %u"), TimePassedSince(_time), status);
+ }
+ if (_chunk == 5) MLX90640.extractedParams = true;
+ _chunk++;
+ return;
+ }
+
+ switch(_job){
+ case 0:
+ if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){
+ _job=-1;
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: frame not ready"));
+ break;
+ }
+ // _time = millis();
+ status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: got frame 0 in %u msecs, status: %i"), TimePassedSince(_time), status);
+ break;
+ case 1:
+ MLX90640.Ta = MLX90640_GetTa(MLX90640.Frame, MLX90640.params);
+ break;
+ case 2:
+ // _time = millis();
+ MLX90640_CalculateTo(MLX90640.Frame, MLX90640.params, 0.95f, MLX90640.Ta - 8, MLX90640.To, 0);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
+ break;
+ case 5:
+ if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){
+ _job=4;
+ break;
+ }
+ // _time = millis();
+ status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame);
+ // // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: got frame 1 in %u msecs, status: %i"), TimePassedSince(_time), status);
+ break;
+ case 7:
+ // _time = millis();
+ MLX90640_CalculateTo(MLX90640.Frame, MLX90640.params, 0.95f, MLX90640.Ta - 8, MLX90640.To, 1);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
+ break;
+ default:
+ break;
+ }
+ _job++;
+ if(_job>10) _job=0;
+}
+
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+void MLX90640Show(uint8_t json)
+{
+ char amb_tstr[FLOATSZ];
+ dtostrfd(MLX90640.Ta, Settings.flag2.temperature_resolution, amb_tstr);
+ if (json) {
+ ResponseAppend_P(PSTR(",\"MLX90640\":{\"" D_JSON_TEMPERATURE "\":[%s"), amb_tstr);
+ for(int i = 0;ion("/mlx", MLX90640HandleWebGui);
+ break;
+#endif // USE_WEBSERVER
+ case FUNC_COMMAND:
+ result = MLX90640Cmd();
+ break;
+ }
+ }
+ return result;
+}
+
+#endif // USE_MLX90640_SENSOR
+#endif // USE_I2C
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 26f7fe900..cd32a5402 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -227,7 +227,7 @@ a_features = [[
"USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM",
"USE_HP303B","USE_TCP_BRIDGE","USE_TELEINFO","USE_LMT01",
"USE_PROMETHEUS","USE_IEM3000","USE_DYP","USE_I2S_AUDIO",
- "","","","",
+ "USE_MLX90640","","","",
"","USE_TTGO_WATCH","USE_ETHERNET","USE_WEBCAM"
],[
"","","","",