Forum: Mikrocontroller und Digitale Elektronik Wärmebildkamera mit dem MLX90640 und Arduino Due


von Christoph E. (stoppi)



Lesenswert?

Hallo!

Frohe Weihnachten euch allen...

Hier (https://forum.mosfetkiller.de/viewtopic.php?f=6&t=20202) habe ich 
bereits meine erste Wärmebildkamera mit dem MLX90614 DCI vorgestellt. 
Das große Manko dieser Anordnung war die lange Belichtungszeit von rund 
5 Minuten. Nun gibt es von Melexis einen neuen Sensor, den MLX90640. 
Dieser besitzt immerhin eine Auflösung von 32x24, also insgesamt 768 
Pixel.

Habe heute einen aus Fernost bestellt und zwar jenen mit dem 55°x35° 
Gesichtsfeld: 
https://de.aliexpress.com/item/MLX90640-Kamera-Modul-IR-32-24-GY-MCU90640-Infrarot-Thermometrische-Dot-Matrix-Sensor-32x24-Sensor-Modul/32962087015.html?ws_ab_test=searchweb0_0%2Csearchweb201602_5_10065_10068_10547_319_317_10548_10696_10084_453_454_10083_10618_10304_10307_10820_10821_538_537_10302_536_10059_10884_10887_100031_321_322_10103%2Csearchweb201603_51%2CppcSwitch_0&algo_pvid=1d6caa67-fdeb-4212-90aa-f50add383914&algo_expid=1d6caa67-fdeb-4212-90aa-f50add383914-0

Da ich ja bekanntlich möglichst wenig auf fertige Produkte zurückgreifen 
möchte, habe ich mich bereits um die Software gekümmert.

github-Link für das Auslesen des Sensors: 
https://github.com/sparkfun/SparkFun_MLX90640_Arduino_Example

Als Prozessor kommt ein Arduino Due zum Einsatz, da dieser einen 
ausreichenden Speicher für die doch komplexen Berechungen besitzt. Im 
Internet findet man immer wieder einzelne Berichte, dass es Probleme mit 
dem Arduino Due geben soll. Ich hoffe, dass sich dann alles in Luft 
auflöst. Display ist das von mir schon bei mehreren Projekten zum 
Einsatz gekommene mit 480x320 Pixel 
(https://www.ebay.com/itm/3-5inch-TFT-LCD-Color-Display-Module-320x480-Support-for-Ar-Mega2560-16-Bit/163228939051?epid=20023227698&hash=item260133f32b:g:2xgAAOSwvoNbh4~D:rk:19:pf:0).

Als Farbskala für das Wärmebild kommt ein "Nachbau" der abgebildeten zum 
Einsatz. War gar nicht so einfach, einen möglichst simplen, nur aus 
linesaren Verläufen bestehenden Klon zu erstellen. Habe das Farbspekrum 
nun in 180 Pixel unterteilt und übergebe an das Unterprogramm eben auch 
einen Wert im Intervall [0,180]. Die Abstimmung auf die gemessene 
Temperatur ist dann einfach. Sie lautet: Pixel i = 180 * (T_gemessen - 
T_min) / (T_max - T_min). So bilde ich den gesamten Temperaturbereich 
[T_min,T_max] auf den Zahlenbereich [0, 180] ab.

Auf dem Display wird neben dem Falschfarbenwärmebild noch seitlich die 
färbige Temperaturskala mit T_min und T_max angezeigt. So schöpfe ich 
immer den gesamten Farbraum von dunkelblau bis weiß aus. Zudem wird noch 
die Temperatur des Pixels in der Mitte angezeigt (siehe Kreuz).

Leider entsprechen die Farben auf dem Photo vom Display bei weitem nicht 
dem tatsächlichen Erscheinungsbild (violett wird z.B. nahezu gar nicht 
dargestellt). Dieses stimmt bei weitem mehr mit den Vorgaben überein, 
als es den Anschein hat...

Wenn der Sensor eingetroffen ist, geht es hier weiter.

: Bearbeitet durch User
von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Habe den Farbverlauf nochmals geändert, passt jetzt besser (Farben 
werden aber durch die Kamera nach wie vor nicht 1:1 wiedergegeben...)

von Johannes S. (Gast)


Angehängte Dateien:

Lesenswert?

soll der Pixelmatsch schon das Wärmebild sein? Da scheint eher etwas mit 
dem einlesen nicht zu klappen als mit der Farbpalette.
Für einen Omron Sensor habe ich mir eine C++ Klasse für eine Farbpalette 
gebaut. Es können Farben als Stützpunkte eingefügt werden und dazwischen 
wird interpoliert, so können verschiedene Farbverläufe realisiert 
werden. Der RGB565 Werte wird berechnet, als Optimierung könnte man noch 
eine Tabelle vorberechnen lassen. Ich habe das auf einem STM32F407 
laufen, der ist so schnell das eine Tabelle nicht nötig war. Das TFT für 
Board ist allerdings nicht besonders gut, die Farben sind extrem 
blickwinkelabhängig.
Im Anhang ist meine C++ Klasse und wird so benutzt:
1
    // create palette
2
    ColorPalette cpRainbow;
3
    cpRainbow.addItem(   0.0f,   0,   0, 255);      //   0 % blue
4
    cpRainbow.addItem(  33.0f,   0, 255,   0);      //  33 % green
5
    cpRainbow.addItem(  66.0f, 255, 255,   0);      //  66 % yellow
6
    cpRainbow.addItem( 100.0f, 255,   0,   0);      // 100 % red
7
8
    cpRainbow.setMinMax(20.0f, 35.0f);
9
10
    // create palette
11
    ColorPalette cpBlueRed;
12
    cpBlueRed.addItem(   0.0f,   0,   0, 255);      //   0 % blue
13
    cpBlueRed.addItem(  50.0f, 255, 255, 255);      //  50 % white
14
    cpBlueRed.addItem( 100.0f, 255,   0,   0);      // 100 % red
15
16
    cpBlueRed.setMinMax(20.0f, 35.0f);
17
18
    // get color from palette
19
    uint16_t color = cpBlueRed.getColor(temperature);

von Christoph E. (stoppi)


Lesenswert?

Danke für den Beitrag...
Das Wärmebild besteht noch aus reinen Zufallstemperaturen [random(180)], 
da ich ja den Sensor noch nicht habe.

von Johannes S. (Gast)


Angehängte Dateien:

Lesenswert?

Christoph E. schrieb:
> Das Wärmebild besteht noch aus reinen Zufallstemperaturen [random(180)],
> da ich ja den Sensor noch nicht habe.

ok, das war durch 'Farben werden aber durch die Kamera nach wie vor 
nicht 1:1 wiedergegeben...' etwas missverständlich.

Im Anhang ein Bild wie das auf minem Display aussieht. Der Balken gibt 
den Farbverlauf von 0..max wieder.

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

So, der Sensor ist gestern angekommen. Leider bringe ich ihn am Arduino 
Due nicht zum Laufen. Ich erhalte anstelle der Temperaturen nur den 
"Wert" nan

Habe mich im Internet nach einer Lösung umgesehen, aber mit einem Due 
hat scheinbar noch keiner den MLX90640 erfolgreich betrieben...

Habe schon so einiges versucht. Den PS-pin an/nicht an GND usw.

Betreiben tue ich das Board an 5V, da es einen 3.3V-Spannungswandler 
integriert hat. Die SDA/SCL-Leitungen verfügen über je einen 2.2kOhm 
pull-up Widerstand.

Eine Frage an die Spezialisten: Mein Board verfügt neben der SDA/SCL 
Schnittstelle auch noch über RX/TX-pins. Das Board verfügt bereits über 
einen eigenen microcontroller, der dann die 768 Temperaturen ausgibt. Im 
Netz findet man ein Programm, welches die Daten dann gleich graphisch 
ausgibt. Hat jemand vielleicht eine Idee, wie ich diese Daten mit dem 
Arduino Due an den RX/TX pins abgreifen und auslesen kann?

Wenn das alles nicht weiterführt, werde ich mir wohl einen empfohlenen 
Teensy besorgen müssen. Nur kann ich mit einem Teensy auch so einfach 
wie mit dem Arduino Due ein 320x480 display anschließen?

Link zum Sensor: 
https://www.aliexpress.com/snapshot/0.html?spm=a2g0s.9042647.6.2.77b74c4dxkdXfJ&orderId=97282295260631&productId=32958277975

sparkfun-Seite mit einem anderen board nur mit SDA/SCL: 
https://learn.sparkfun.com/tutorials/qwiic-ir-array-mlx90640-hookup-guide/all

Verwendete Software: 
https://github.com/sparkfun/SparkFun_MLX90640_Arduino_Example/tree/master/Firmware/Example1_BasicReadings

Danke im voraus für eure Hilfe...

: Bearbeitet durch User
von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

So, ich habe inzwischen aus China Software für PC und Arduino erhalten 
(siehe code). Verwende wie gesagt einen Arduino Due. RX/TX vom Sensor 
habe ich mit TX1/RX1 vom Due verbunden. Problem ist, es tut sich im 
seriellen Monitor überhaupt nichts. TX vom MLX90640 sendet aber Signale 
(siehe Abbildung).

Da ich mit Kommunikationscode alles andere als fit bin, vielleicht hätte 
einer von den Profis für mich einen Tipp was ich noch versuchen könnte. 
Danke im voraus...

P.S.: Die Zeilen mit Serial1 und Serial2 sind nicht original aus China, 
da hat sich der Compiler aber immer aufgeregt...

P.P.S: Bei GYSerial.available()> 0 kommt scheinbar gar nichts an...


1
//GY_mlx90640 ARDUINO
2
3
//   GY_mlx90640                 arduino pro mini
4
/*
5
1.GY_mlx90640_uart arduino
6
2.GY_mlx90640 VCC  GND
7
3.arduino_TX---GY_mlx90640_RX   Rest arduino
8
4.arduino__RX---GY_mlx90640_TX
9
5.GYSerial,baud=9600
10
6.arduino_pin_11---FT232_RX
11
7.arduino_pin_10---FT232_TX
12
8.PCSerial ,baud=115200
13
*/
14
15
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16
// Ändere hier, welcher Hardware Serial für was verwendet wird.
17
#define PCSerial   Serial2
18
#define GYSerial   Serial1
19
20
uint16_t temp1=0;
21
int16_t temp2=0;
22
23
uint8_t Re_buf[10];
24
uint16_t re_count=0,rest_count=0;
25
uint16_t counter=0;
26
27
unsigned char sign=0;
28
int led = 13;
29
float myMap(float x, float in_min, float in_max, float out_min, float out_max);
30
float max_temp=0;
31
float min_temp=500;
32
33
34
//-----------------------------------------------------------
35
void setup()
36
   {
37
    GYSerial.begin(9600);  
38
    PCSerial.begin(115200);
39
   
40
    delay(1000);    
41
   }
42
43
44
//-------------------------------------------------------------
45
void loop()
46
   {
47
    if (GYSerial.available()>0) 
48
       {   
49
        Re_buf[counter]=(unsigned char)GYSerial.read();
50
  
51
        if(counter==0&&Re_buf[0]!=0x5A) return;    
52
        else  if(counter==1&&Re_buf[1]!=0x5A)
53
           {
54
            counter=0;
55
            return;
56
           };       
57
        
58
        if(counter<6)    
59
           counter++;       
60
        if(counter==4)               
61
           {    
62
            if(Re_buf[2]==0x02&&Re_buf[3]==0x06)
63
               sign=1;
64
            else
65
               counter=0;
66
           }     
67
        if(counter>5)
68
           {
69
            float temp=(float)(Re_buf[4]+Re_buf[5]*256)/100;
70
            
71
            if(temp>max_temp)
72
               max_temp=temp;
73
            
74
            if(temp<min_temp)
75
               min_temp=temp;
76
        
77
            counter=4;
78
            re_count++;
79
      
80
        if(rest_count>20)
81
           {
82
            min_temp=500;
83
            max_temp=0;
84
            rest_count=0;
85
           }
86
       
87
        //PCSerial.print(temp); 
88
        // PCSerial.print(","); 
89
        
90
        float inv= myMap(temp,min_temp,max_temp,0.5,1);
91
      
92
        if(inv>0.75)
93
           PCSerial.print("**"); 
94
        else
95
           PCSerial.print("--"); 
96
        
97
        if((re_count%32)==0)
98
           PCSerial.println(); 
99
        
100
        if(re_count%768==0)
101
           PCSerial.print("TA:"); 
102
        
103
        if(re_count==769)
104
           { 
105
            rest_count++;
106
            counter=0;
107
            PCSerial.print(temp); 
108
            PCSerial.println(); 
109
            re_count=0;
110
           }
111
       }
112
     }
113
   } 
114
115
116
//---------------------------------------------------------------
117
float myMap(float x, float in_min, float in_max, float out_min, float out_max)
118
   {
119
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
120
   }

: Bearbeitet durch User
von Cornelius (Gast)


Lesenswert?

Hallo Christoph,

vor einiger Zeit habe ich auch mit dem Sensor und einem ESP32 herum 
probiert.
Es ging ewig lange nichts und ich habe schon geglaubt, es liegt am 
Sensor.
Dann habe ich einen zweiten bekommen, da ging auch nichts.

Irgendwann hatte ich aber ein Lösung gefunden: Der Sensor hat ein EEPROM 
eingebaut und es ist vermutlich so, dass die meiste Open-Source-Software 
nur mit den vor eingestellten Werten funktioniert ( ich weiß nicht mehr, 
ob es Adafruit war, die den Sensor mit vor eingestellten Werten 
ausliefern ). Die anderen gehen dann nicht mit der Software.
Ich habe die dann rein geschrieben, und siehe da, beide Sensoren gingen.
Leider weiß ich schon nicht mehr, welche es waren.

von Christoph E. (stoppi)


Lesenswert?

Hallo Cornelius!

Vielen Dank für deinen interessanten Hinweis. Ich habe meinen Sensor 
über aliexpress gekauft und er hat bereits einen microcontroller an 
Bord, der per serieller Schnittstelle (Tx/Rx) eben die 768 Temperaturen 
ausgibt. Die reinen Sensoren von Sparkfun etc. haben den nicht und daher 
kommunizieren diese direkt mit dem Sensor über den I2C-Bus (SCL,SDA)...

Oben im Programm habe ich nun PCSerial mit Serial definiert. So kann ich 
zumindest das array Re_buf[Counter] auslesen. Die Werte sind aber auch 
nicht nachvollziehbar...

: Bearbeitet durch User
von Cornelius (Gast)


Lesenswert?

Das hier sind die Werte ab Adresse 0x8000, mit dem der Sensor geht.
Vielleicht hilft Dir das weiter:

// Sparkfun sensor
Device found @ 0x33
0: 9
1: AB
2: 499B
3: 0
4: 2061
5: 5
6: 320
7: 3E0
8: 1A26
9: A228
10: 185
11: 499
12: 0
13: 1901
14: 0
15: 0

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Vielen Dank Cornelius für deine Hilfe!

Habe nun das Programm so weit wie möglich entschlackt. Ich erhalte jetzt 
mit Baud = 115200 im 32x24-Raster auch zum Teil recht plausible Werte 
(Zimmertemperaturen um die 21°C bzw. Handtemperatur um die 35°C), aber 
auch etliche nicht nachvollziehbare Temperaturen über 100-400°C.

Vielleicht hat ja jemand von euch noch einen Tipp für mich...

1
//GY_mlx90640 ARDUINO
2
3
//   GY_mlx90640                 arduino pro mini
4
/*
5
1.GY_mlx90640_uart arduino
6
2.GY_mlx90640 VCC  GND
7
3.arduino_TX---GY_mlx90640_RX   Rest arduino
8
4.arduino__RX---GY_mlx90640_TX
9
5.GYSerial,baud=9600
10
6.arduino_pin_11---FT232_RX
11
7.arduino_pin_10---FT232_TX
12
8.PCSerial ,baud=115200
13
*/
14
15
#define PCSerial Serial
16
#define GYSerial Serial1
17
18
uint8_t Re_buf[10];
19
uint16_t re_count = 0;
20
uint16_t counter = 0;
21
22
unsigned char sign = 0;
23
int led = 13;
24
25
26
//-----------------------------------------------------------
27
void setup()
28
   {
29
    GYSerial.begin(115200);  
30
    PCSerial.begin(115200);
31
   
32
    delay(1000);    
33
   }
34
35
36
//-------------------------------------------------------------
37
void loop()
38
   {
39
    if (GYSerial.available() > 0) 
40
       {   
41
        Re_buf[counter]=(unsigned char)GYSerial.read();
42
43
        if(counter == 0 && Re_buf[0] != 0x5A) return;
44
        else if(counter == 1 && Re_buf[1]!= 0x5A)
45
           {
46
            counter = 0;
47
            return;
48
           };      
49
    
50
        if(counter < 6)    
51
           counter++;       
52
    
53
        if(counter == 4)             
54
           {    
55
            if(Re_buf[2] == 0x02 && Re_buf[3] == 0x06)
56
               sign = 1;
57
            else
58
               counter = 0;
59
           }     
60
      
61
        if(counter > 5)
62
           {
63
            float temp = (float)(Re_buf[4] + Re_buf[5] * 256) / 100;
64
      
65
            counter = 4;
66
        
67
            re_count++;
68
       
69
            PCSerial.print(temp); 
70
            PCSerial.print(","); 
71
       
72
            if((re_count%32) == 0)
73
               PCSerial.println(); 
74
            
75
            if(re_count == 769)
76
               { 
77
                counter = 0;
78
                PCSerial.println(); 
79
                re_count=0;
80
               }
81
           }
82
      }
83
   }

von Elias K. (elik)


Lesenswert?

Auf dem Oszibild oben sehe ich eine UART-Datenstrom mit 115.200 kBaud, 
8N1. Ich tippe auf ein Problem mit dem Stoppbit. Da es nur eines ist, 
könnte das Probleme mit der Synchronisation über einen so langen 
Datenstrom geben.

Schau dir mal den Wikieintrag zu Baudratenquarz an.

von Christoph E. (stoppi)


Lesenswert?

Danke Elias für deinen Hinweis. Das klingt dann aber nach unlösbaren 
Problem oder? Denn die Länge bzw. Aufteilung des Datenstroms kann ich ja 
so nicht beeinflussen.

Deine Vermutung mit timing-Problemen würde sich mit der Beobachtung 
decken, dass zumindest zu Beginn der Übertragung die Temperaturen 
stimmen und erst nach einer Zeit durch einen Zeitdrift falsche Werte 
eingelesen werden...

: Bearbeitet durch User
von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Würde es ggf. Sinn machen die baudrate z.B. auf 9600 zu senken um mehr 
Genauigkeit beim Einlesen der Temperaturen zu bekommen?

von Elias K. (elik)


Lesenswert?

Christoph E. schrieb:
> Würde es ggf. Sinn machen die baudrate z.B. auf 9600 zu senken um mehr
> Genauigkeit beim Einlesen der Temperaturen zu bekommen?

Probieren geht über studieren. Könnte klappen Die Übertragung eines 
Frames dauert dann aber schon ganz schön lang.

Eine schöne Möglichkeit wäre, wenn du den Controller bei der Camera auf 
8N2 (2 Stoppbits) setzen könntest, und den Arduino auf 8N1 Empfang. Das 
gibt eine kleine Lücke zur Re-Synchronisation.

Auch gut wäre, wenn beide Seiten passende Quarze haben. Der Controller 
an der Kamera nutzt aber wahrscheinlich den internen Taktgenerator, der 
idR alles andere als genaue Takte generiert. Also eher nicht 
praktikabel...
Lesestoff: 
https://www.allaboutcircuits.com/technical-articles/the-uart-baud-rate-clock-how-accurate-does-it-need-to-be/

Evtl. lohnt es auch, den UART auf dem Arduino per Hand zu tunen. Also 
die Register im Atmega selbst zu setzen. Dazu findet sich im Atmega 
Datenblatt unter Kapitel USART jede Menge Lesestoff.

Vielleicht reicht es auch, wenn du den Takt auf dem Arduino etwas 
schneller/langsamer machst. Das könnte durch einen Austausch der 
Kondensatoren am Quarz klappen.
Lesestoff: 
https://www.all-electronics.de/wp-content/uploads/migrated/article-pdf/113375/174ag0206.pdf

von Christoph E. (stoppi)



Lesenswert?

Vielen Dank Elias für die Tipps...

Es geht voran. Habe nun mittels TTL-USB-Adapter die China-PC-Software 
ausprobiert und siehe da, ich erhalte ein Bild.

Problem ist nur, dass ich im Programm trotz vorhandener Buttons 
scheinbar die baud-rate nicht ändern kann. Deshalb schnell in teraterm 
die baud-rate von 115200 auf 9600 gesenkt. Und siehe da, ich habe nun 
mit dem Arduino-Programm keine falschen Temperaturen mehr.

Schönheitsfehler: Ich erhalte so nur etwa 1 Bild pro Sekunde...

Das eigenartige ist aber nun, dass ich mit teraterm (siehe Abbildung) 
die baud-rate nicht mehr z.B. auf 115200 zurücksetzen kann. Mache ich da 
irgendetwas falsch?

Gebe die Kommandos laut Abbildung (danke count-doku für den link) ein. 
Wie gesagt, bei der ersten Umstellung von 115200 auf 9600 hat es so 
geklappt, nur jetzt nicht mehr...

Als nächstes werde ich die 768 Temperaturen in einem array abspeichern 
und dann mit dem bereits geschriebenen Programm graphisch ausgeben.

von Seine LED leuchtet nicht (Gast)


Lesenswert?

Hallo Christoph,

ein interessantes Thema. Danke dass du uns teilnehmen lässt.

Frage
Es gibt von den MLX90640 zwei Typen, mit BAA und BAB am Ende.
Welchen hast du im Einsatz?

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Habe den MLX90640BAB mit 55°x35° Gesichtsfeld...

Schlechte Nachrichten: Der Sensor scheint tot zu sein. Es kommen über TX 
keine Signale mehr. Warum dies so ist, weiß ich leider nicht. Sprich ein 
Fehler (Überspannung o.ä.) ist mir nicht bewusst.

Werde jetzt versuchen, einen zweiten günstig zu bekommen. Anbei noch das 
letzte Bild von mir und Halogenlampe an der Wohnungsdecke...

von Christoph M. (mchris)


Lesenswert?

>Schlechte Nachrichten: Der Sensor scheint tot zu sein. Es kommen über TX
>keine Signale mehr.

Gib nicht so schnell auf. Mit dem Arduino-Due kannst Du einen 
I2C-Scanner programmieren und dann mal sehen, ob der Sensor noch auf die 
Adresse antwortet.
https://playground.arduino.cc/Main/I2cScanner
Vielleicht ist ja nur die Konfiguration des Sensors zerschossen.

von Christoph E. (stoppi)



Lesenswert?

Zu früh möchte ich die Flinte eh nicht ins Korn werfen...

Habe mir die Signale nochmals angesehen. Also wie gesagt TX ist tot. 
Über I2C kommen aber noch Signale und der I2C-Scanner erkennt den Sensor 
an 0x33, was ja auch passen würde.

Das Sparkfun-Programm für den MLX90640, welches auf den I2C zurückgreift 
funktioniert zwar nicht, liefert aber diesselben Ergebnisse wie noch vor 
wenigen Tagen (siehe Abbildung MLX90640_Arduino_44)

Ich tippe daher auf einen defekten Microcontroller (STM32F103). Was sagt 
ihr?

: Bearbeitet durch User
von Jedi Knight (Gast)


Lesenswert?

Christoph E. schrieb:

> Ich tippe daher auf einen defekten Microcontroller (STM32F103). Was sagt
> ihr?

Na also, halb so wild. Ein Microcontroller Ersatz lässt sich doch 
schnell und billig beschaffen. Sollte man m.E. sowieso immer i.d. 
Schublade liegen haben.

Allerdings sollte auch ein mutmaßlicher Grund da sein, WARUM etwas 
kaputt gegangen sein könnte. Von selbst gehen ICs i.d.R. nicht kaput.

Bleib dran. Ich beobachte, es interessiert mich.
und viel Erfolg :-)

von Christoph M. (mchris)


Lesenswert?

>Ich tippe daher auf einen defekten Microcontroller (STM32F103). Was sagt
>ihr?

Schwierig zu sagen. Ich könnte mir vorstellen, dass Du die EEPROM 
Konfiguration im MLX zerschossen hast und das Sparkfun Programm nur mit 
voreingestellten Werten funktioniert.

Was dann hilft: Werte einfach neu rein schreiben. Zur Not kann man sie 
auch einfach jedesmal beim Start ins RAM schreiben. So kannst Du 
übrigens auch Deinen MC testen: Einfach Werte ins RAM schreiben und 
zurück lesen.

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

So, habe mich nun auf die Suche gemacht, wie ich das EEPROM des Sensors 
auslesen und verändern kann.

Bin hier bei einem Programm für den Arduino fündig geworden: 
http://forum.arduino.cc/index.php?topic=351494.msg3004267#msg3004267

Ich muss ja laut Cornelius (siehe Beitrag weiter oben) folgende Werte 
ins EEPROM ab Adresse 0x8000 schreiben:

// Sparkfun sensor
Device found @ 0x33
0: 9
1: AB
2: 499B
3: 0
4: 2061
5: 5
6: 320
7: 3E0
8: 1A26
9: A228
10: 185
11: 499
12: 0
13: 1901
14: 0
15: 0

Ausgelesen habe ich bei meinem MLX90640 Sensor einmal folgende Werte 
(siehe auch Abbildung):

9  172  27039  0  8289  5  800  992  2571  24488  390  1177  0  6657  0 
0

Jetzt regt sich das Programm aber beim "Wert" AB usw. auf. Muss ich nun 
AB etwa vom hexadezimalen ins dezimale Zahlensystem umwandeln und statt 
AB = 10 * 16 + 11 * 1 = 171 ins EEPROM schreiben?


1
#include <Wire.h>
2
#define I2C_EEPROM_ADRESSE 0x33
3
#define EEPROM_SPEICHER_ADR 0x8000
4
5
const uint16_t Anzahl_Rezepte = 16;  // anzahl Rezepte
6
uint16_t Zaehler[Anzahl_Rezepte] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
7
bool oneshot = true;
8
9
void setup()
10
   {
11
    Wire.begin();
12
    Serial.begin(9600);
13
    Serial.println("Programmanfang");
14
    Serial.print("Zaehlerfeld vor dem Lesen: \t");
15
    
16
    for (uint16_t j = 0; j < (sizeof(Zaehler) / sizeof(Zaehler[0])); j++)
17
       {
18
        Serial.print(Zaehler[j]);
19
        Serial.print(" ");
20
       }
21
       
22
    Serial.println();
23
    
24
    bool zaehlerzuruecksetzen = false;  // auf true zum Loeschen, auch zur Speicherinitialisierung auf 0
25
    
26
    if (zaehlerzuruecksetzen)
27
       {
28
        i2c_eeprom_updateUIntArray(I2C_EEPROM_ADRESSE, EEPROM_SPEICHER_ADR, Zaehler, sizeof(Zaehler) / sizeof(Zaehler[0]));  // Zaehler zuruecksetzen
29
       }
30
    else
31
       {
32
        i2c_eeprom_readUIntArray(I2C_EEPROM_ADRESSE, EEPROM_SPEICHER_ADR, Zaehler, sizeof(Zaehler) / sizeof(Zaehler[0]));
33
       }
34
  
35
    Serial.print("Zaehlerfeld nach dem Lesen: \t");
36
    
37
    for (uint16_t j = 0; j < (sizeof(Zaehler) / sizeof(Zaehler[0])); j++)
38
       {
39
        Serial.print(Zaehler[j]);
40
        Serial.print(" ");
41
       }
42
    
43
    Serial.println();
44
   }
45
46
void loop()
47
   {
48
    if (oneshot)
49
       {
50
        Zaehler[0] = 9;
51
        Zaehler[1] = AB;
52
        Zaehler[2] = 499B;
53
        Zaehler[3] = 0;
54
        Zaehler[4] = 2061;
55
        Zaehler[5] = 5;
56
        Zaehler[6] = 320;
57
        Zaehler[7] = 3E0;
58
        Zaehler[8] = 1A26;
59
        Zaehler[9] = A228;
60
        Zaehler[10] = 185;
61
        Zaehler[11] = 499;
62
        Zaehler[12] = 0;
63
        Zaehler[13] = 1901;
64
        Zaehler[14] = 0;
65
        Zaehler[15] = 0;
66
            
67
        i2c_eeprom_updateUIntArray(I2C_EEPROM_ADRESSE, EEPROM_SPEICHER_ADR, Zaehler, sizeof(Zaehler) / sizeof(Zaehler[0]));
68
69
        Serial.print("Zaehlerfeld nach dem Schreiben: \t");
70
        
71
        for (uint16_t j = 0; j < (sizeof(Zaehler) / sizeof(Zaehler[0])); j++)
72
           {
73
            Serial.print(Zaehler[j]);
74
            Serial.print(" ");
75
           }
76
        
77
        Serial.println();
78
        
79
        oneshot = false;
80
       }
81
   }
82
83
84
85
void i2c_eeprom_updateUIntArray(int16_t deviceaddress, uint16_t eeaddress, uint16_t *ptr, uint16_t anzahlElemente)
86
   {
87
    uint16_t buf[anzahlElemente];
88
    i2c_eeprom_readUIntArray(deviceaddress, eeaddress, buf, sizeof(buf) / sizeof(buf[0]));
89
    uint16_t eadr=eeaddress;
90
    
91
    for (uint16_t j = 0; j < anzahlElemente; j++)
92
       {
93
        if ((ptr[j] >> 8) != (buf[j] >> 8))
94
           {
95
            Wire.beginTransmission(deviceaddress);
96
            Wire.write((int)(eadr >> 8)); // MSB
97
            Wire.write((int)(eadr & 0xFF)); // LSB
98
            Wire.write(ptr[j] >> 8);
99
            Wire.endTransmission();
100
            delay(10);
101
           }
102
       
103
        eadr++;
104
    
105
        if ((ptr[j] & 0xFF) != (buf[j] & 0xFF))
106
           {
107
            Wire.beginTransmission(deviceaddress);
108
            Wire.write((int)(eadr >> 8)); // MSB
109
            Wire.write((int)(eadr & 0xFF)); // LSB
110
            Wire.write(ptr[j] & 0xFF);
111
            Wire.endTransmission();
112
            delay(10);
113
           }
114
        eadr++;
115
       }
116
   }
117
118
119
120
void i2c_eeprom_readUIntArray(int16_t deviceaddress, uint16_t eeaddress, uint16_t *ptr, uint16_t anzahlElemente)
121
   {
122
    uint16_t data = 0xFF;
123
    delay(2);
124
    
125
    Wire.beginTransmission(deviceaddress);
126
    Wire.write((int)(eeaddress >> 8)); // MSB
127
    Wire.write((int)(eeaddress & 0xFF)); // LSB
128
    Wire.endTransmission();
129
    Wire.requestFrom(deviceaddress, anzahlElemente << 1);
130
    
131
    for (uint16_t j = 0; j < anzahlElemente; j++)
132
       {
133
        delay(5);
134
        data = Wire.read() << 8;
135
        delay(5);
136
        data = data | Wire.read();
137
        ptr[j] = data;
138
       }
139
   }

von Christoph M. (mchris)


Angehängte Dateien:

Lesenswert?

Eigentlich reicht es, wenn Du erst mal die Konfigurationsregister ( RAM 
) richtig beschriebst ( Datenblatt S.16 ).

Beitrag #5715981 wurde vom Autor gelöscht.
Beitrag #5715984 wurde vom Autor gelöscht.
von Christoph E. (stoppi)



Lesenswert?

So, habe nun die HEX-Werte in DEZ-Werte umgewandelt und versucht, den
EEPROM-Speicher abzuändern. Er behält aber scheinbar nicht die
geänderten Werte, da noch die alten Werte gelesen werden....

Kann mir da jemand vielleicht helfen? Danke im voraus...

1
#include <Wire.h>
2
#define I2C_EEPROM_ADRESSE 0x33
3
#define EEPROM_SPEICHER_ADR 0x8000
4
5
const uint16_t Anzahl_Rezepte = 16;  // anzahl Rezepte
6
uint16_t Zaehler[Anzahl_Rezepte] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
7
bool oneshot = true;
8
9
void setup()
10
   {
11
    Wire.begin();
12
    Serial.begin(9600);
13
    Serial.println("Programmanfang");
14
15
    i2c_eeprom_readUIntArray(I2C_EEPROM_ADRESSE, EEPROM_SPEICHER_ADR, Zaehler, sizeof(Zaehler) / sizeof(Zaehler[0]));
16
  
17
    Serial.print("Zaehlerfeld vor dem Schreiben: \t");
18
    
19
    for (uint16_t j = 0; j < (sizeof(Zaehler) / sizeof(Zaehler[0])); j++)
20
       {
21
        Serial.print(Zaehler[j]);
22
        Serial.print(" ");
23
       }
24
    
25
    Serial.println();
26
   }
27
28
void loop()
29
   {
30
    //oneshot = false;
31
    
32
    if (oneshot)
33
       {
34
        Zaehler[0] = 9;
35
        Zaehler[1] = 171;      // = HEX AB
36
        Zaehler[2] = 18843;    // = HEX 499B
37
        Zaehler[3] = 0;
38
        Zaehler[4] = 8289;     // = HEX 2061
39
        Zaehler[5] = 5;
40
        Zaehler[6] = 800;      // = HEX 320
41
        Zaehler[7] = 992;      // = HEX 3E0
42
        Zaehler[8] = 6694;     // = HEX 1A26
43
        Zaehler[9] = 41512;    // = HEX A228
44
        Zaehler[10] = 389;     // = HEX 185
45
        Zaehler[11] = 1177;    // = HEX 499
46
        Zaehler[12] = 0;
47
        Zaehler[13] = 6401;    // = HEX 1901
48
        Zaehler[14] = 0;
49
        Zaehler[15] = 0;
50
51
        Serial.print("neu zu schreibende Werte: \t");
52
53
        for (uint16_t j = 0; j < (sizeof(Zaehler) / sizeof(Zaehler[0])); j++)
54
           {
55
            Serial.print(Zaehler[j]);
56
            Serial.print(" ");
57
           }
58
        
59
        i2c_eeprom_updateUIntArray(I2C_EEPROM_ADRESSE, EEPROM_SPEICHER_ADR, Zaehler, sizeof(Zaehler) / sizeof(Zaehler[0]));
60
61
62
        i2c_eeprom_readUIntArray(I2C_EEPROM_ADRESSE, EEPROM_SPEICHER_ADR, Zaehler, sizeof(Zaehler) / sizeof(Zaehler[0]));
63
        
64
        Serial.println();
65
        Serial.print("Zaehlerfeld nach dem Schreiben: \t");
66
        
67
        for (uint16_t j = 0; j < (sizeof(Zaehler) / sizeof(Zaehler[0])); j++)
68
           {
69
            Serial.print(Zaehler[j]);
70
            Serial.print(" ");
71
           }
72
        
73
        Serial.println();
74
        
75
        oneshot = false;
76
       }
77
   }
78
79
80
81
void i2c_eeprom_updateUIntArray(int16_t deviceaddress, uint16_t eeaddress, uint16_t *ptr, uint16_t anzahlElemente)
82
   {
83
    uint16_t buf[anzahlElemente];
84
    i2c_eeprom_readUIntArray(deviceaddress, eeaddress, buf, sizeof(buf) / sizeof(buf[0]));
85
    uint16_t eadr=eeaddress;
86
    
87
    for (uint16_t j = 0; j < anzahlElemente; j++)
88
       {
89
        if ((ptr[j] >> 8) != (buf[j] >> 8))
90
           {
91
            Wire.beginTransmission(deviceaddress);
92
            Wire.write((int)(eadr >> 8)); // MSB
93
            Wire.write((int)(eadr & 0xFF)); // LSB
94
            Wire.write(ptr[j] >> 8);
95
            Wire.endTransmission();
96
            delay(10);
97
           }
98
       
99
        eadr++;
100
    
101
        if ((ptr[j] & 0xFF) != (buf[j] & 0xFF))
102
           {
103
            Wire.beginTransmission(deviceaddress);
104
            Wire.write((int)(eadr >> 8)); // MSB
105
            Wire.write((int)(eadr & 0xFF)); // LSB
106
            Wire.write(ptr[j] & 0xFF);
107
            Wire.endTransmission();
108
            delay(10);
109
           }
110
        eadr++;
111
       }
112
   }
113
114
115
116
void i2c_eeprom_readUIntArray(int16_t deviceaddress, uint16_t eeaddress, uint16_t *ptr, uint16_t anzahlElemente)
117
   {
118
    uint16_t data = 0xFF;
119
    delay(2);
120
    
121
    Wire.beginTransmission(deviceaddress);
122
    Wire.write((int)(eeaddress >> 8)); // MSB
123
    Wire.write((int)(eeaddress & 0xFF)); // LSB
124
    Wire.endTransmission();
125
    Wire.requestFrom(deviceaddress, anzahlElemente << 1);
126
    
127
    for (uint16_t j = 0; j < anzahlElemente; j++)
128
       {
129
        delay(5);
130
        data = Wire.read() << 8;
131
        delay(5);
132
        data = data | Wire.read();
133
        ptr[j] = data;
134
       }
135
   }

von Elias K. (elik)


Angehängte Dateien:

Lesenswert?

Christoph E. schrieb:
> Ich tippe daher auf einen defekten Microcontroller (STM32F103). Was sagt
> ihr?
Der STM32 ist der zusätzliche Controller auf der Kameraplatine? Gut 
möglich, dass der die Hufe gehoben hat. Vielleicht auch nur irgendein 
Problem in der Ansteuerung. Alles in allem aber eine Blackbox, wo man 
nicht weiß, was Sache ist. (Oder hast du das Programm, was da drauf 
ist?) Deswegen würde ich mich (wie du ja bereits machst) auf I2C 
konzentrieren.

Wieso die neuen Werte im Speicher nicht ankommen, kann ich aus dem Code 
nicht erkennen. Hast du die Möglichkeit, mit dem Oszi die Übertragung 
aufzunehmen? Wenn ja, dann schau dir die Telegramme doch mal an, ob die 
passen. Z.B. den ersten Schreibbefehl und das erste gelesene Telegramm.

Hast du im Datenblatt auf Seite 13 gesehen, dass du eine ca. 4s Pause 
nach Power On Reset einhalten solltest?

Hast du probiert, die vom I2C zurückgelesenen Werte direkt nach "data = 
Wire.read()" auszugeben, um Fehler im Programm auszuschließen?

Was soll die folgende Zeile erreichen? "if ((ptr[j] >> 8) != (buf[j] >> 
8))" Wieso schreibst du nicht pauschal alle Werte?

Hast du im Datenblatt gesehen, dass das LSB der Deviceadresse angibt, ob 
die folgenden Daten geschrieben oder gelesen werden? (Siehe Bild) Ich 
kann in dem Programm nicht erkennen, dass du die Deviceadresse 
entsprechend ändern würdest.

von Christoph M. (mchris)


Lesenswert?

>So, habe nun die HEX-Werte in DEZ-Werte umgewandelt und versucht, den
>EEPROM-Speicher abzuändern.

Versuche erst mal, nur die Ram-Registerwerte direkt am Anfang des 
Programms zu schreiben und zu lesen. Mehr brauchst Du nicht.

von Christoph E. (stoppi)


Lesenswert?

Hallo!

Vielen Dank für die Rückmeldung.

@Elias: Programm des MC habe ich leider nicht...
Ist das Ansprechen/Abändern von Speicherzuständen über I²C nicht 
softwareseitig standardisiert? Falls nein, und dieser MLX90640-sensor 
z.B. ein spezielles/das oben angehängte Ansprechen verlangt, wie müsste 
ich dann die Programmzeilen abändern?

@Christoph: RAM ist ja wieder ein ganz eigener Speicherbereich (0x0400 
bis 0x07FF). Was fange ich dann mit den Speicherwerten von Cornelius an?

Bin wie gesagt alles andere als der Fachmann auf diesen Gebiet...

: Bearbeitet durch User
von Elias K. (elik)


Lesenswert?

Christoph E. schrieb:
> Ist das Ansprechen/Abändern von Speicherzuständen über I²C nicht
> softwareseitig standardisiert?

Habe nochmal kurz recherchiert. Wahrscheinlich kümmern sich die beiden 
Funktionen Wire.beginTransmission() und Wire.requestFrom() um das 
besagte Lesen-/Schreiben-Bit. Trotzdem schadet es sicher nicht, sich am 
Oszi anzuschauen, ob exakt die Daten übermittelt werden, die du 
erwartest.

RAM/ROM: Ich kann nicht ohne weiteres im Datenblatt finden, welche 
Speicherbereiche du überhaupt ändern kannst. Also nimm etwas, was du 
sicher ändern darfst. Zum Beispiel das Datenwort bei Adresse 0x800D, Bit 
7, 8 und 9. Am Anfang reicht es auch, wenn du nur ein Bit ändern kannst. 
Das eliminiert viele versteckte Fehlerquellen in Schleifen, Pointern, 
... Wenn dir das gelingt, dann kannst du weiterschauen.

von Christoph M. (mchris)


Lesenswert?

>@Christoph: RAM ist ja wieder ein ganz eigener Speicherbereich (0x0400
>bis 0x07FF). Was fange ich dann mit den Speicherwerten von Cornelius an?

Die Konfigurationsregister liegen ab 0x8000.
Schau Dir im Datenblatt deren Bedeutung an, und versuche sie zu 
verstehen.

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

So, habe das Datenblatt studiert aber es ergeben sich viele Fragen für 
mich. Diese habe ich versucht in den angehängten Abbildungen klar zu 
machen...

von Thomas Malwinter (Gast)


Lesenswert?

Hier wollte ich ursprünglich posten:

Hier gibt es auch ein Problem mit dem Auslesen:

Beitrag "Wer nutzt das Nucleo-64 und kann mir"

Allerding liegt das wohl an einer I2C-Lib. Der wo helfen könnte, wollte
anscheinend nicht...

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Hallo!

Danke Thomas für den link...

Habe nun diese Zeilen zum Abändern des EEPROMS unter Verwendung der 
melexis-library geschrieben:

1
const uint16_t len = 16;
2
uint16_t adr = 0x8000;
3
uint16_t Buffer[len];
4
5
#include <Wire.h>
6
#include "MLX90640_API.h"
7
#include "MLX90640_I2C_Driver.h"
8
9
10
void setup() {
11
  // put your setup code here, to run once:
12
    Wire.begin();
13
    Wire.setClock(400000); //Increase I2C clock speed to 400kHz
14
   
15
    Serial.begin(9600);
16
   
17
   
18
    MLX90640_I2CRead(0x33, adr, len, Buffer);
19
    for (int n = 0; n < len; n++)
20
    {
21
      Serial.print(n); Serial.print(": "); Serial.println(Buffer[n], HEX);
22
    }
23
24
    MLX90640_I2CWrite(0x33, 0x800D, 6401);    // HEX 1901
25
    //MLX90640_I2CWrite(0x33, 0x800D, 6657);    // HEX 1A01
26
   
27
    delay(4000);
28
29
    MLX90640_I2CRead(0x33, adr, len, Buffer);
30
    for (int n = 0; n < len; n++)
31
    {
32
      Serial.print(n); Serial.print(": "); Serial.println(Buffer[n], HEX);
33
    }
34
   
35
}
36
37
void loop() {
38
  // put your main code here, to run repeatedly:
39
40
}


Damit kann ich z.B. den Speicherwert 1A01 (HEX) an der Stelle 0x800D in 
1901 (HEX) umwandeln und wieder retour.

Mit anderen Stellen, z.B. 0x8000, funktioniert das nicht. Die sind ja 
auch scheinbar für Melexis reserviert...

Warum muss/kann ich aber die Stelle 0x800D direkt mittels 
MLX90640_I2CWrite(0x33, 0x800D, 6401);  ansprechen und verändern? Die 
Speicheradresse 0x800D ist ja mit 0x240C verknüpft und nur diese lässt 
sich doch verändern und dann auf 0x800D übertragen?

Was ist mit den anderen Speicherwerten von Cornelius ab 0x8000? Die kann 
ich wohl nicht ändern oder?

Die Veränderung in 1901 der Stelle 0x800D hat übrigens leider keine 
Auswirkungen auf das Auslesen der Temperaturen. Erhalte nach wie vor 
"nan"

Danke im voraus für eure Hilfe...

: Bearbeitet durch User
von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Es gibt Neuigkeiten: Heute ist ein ESP32 angekommen und ich habe 
natürlich sofort den Arduino-Code ausprobiert und....

... heureka, der Sensor lebt noch und übermittelt passende Temperaturen. 
Damit dies so ist, muss jedoch in der Datei MLX90640_I2C_Driver.cpp zu 
Beginn die Zeile #include <Arduino.h> hinzugefügt werden.

Von den 768 Pixel scheint nur ein einziges defekt zu sein, da es die 
Temperatur "nan" liefert. Da übernehme ich nach Abfrage aber den 
Temperaturwert des Nachbarn.

Jetzt warte ich noch auf das ILI9341 320x240 display, dann biegt dieses 
Projekt in die Zielgerade ein.

von Marc (Gast)


Lesenswert?

>Es gibt Neuigkeiten: Heute ist ein ESP32 angekommen und ich habe
>natürlich sofort den Arduino-Code ausprobiert und....

Welchen? Den gleichen wie auf dem Due?
Falls nicht, musst Du einfach nur die Stelle im Code finden, in der das 
Register richtig beschrieben wird und in den Due-Code kopieren ....

von Christoph E. (stoppi)


Lesenswert?

Nein, ist derselbe Code wie jener, der eben mit dem Arduino Due nicht 
funktioniert hat...

von Marc (Gast)


Lesenswert?

>Nein, ist derselbe Code wie jener, der eben mit dem Arduino Due nicht
>funktioniert hat...

Erstaunlich ... an was könnte es liegen: timing, I2C Widerstände?

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

So, habe nun auch die Software auf die geringere Auflösung von 320x240 
Pixel abgeändert...

von Christoph E. (stoppi)



Lesenswert?

Heute ist das ILI9341 display angekommen und ich habe die 
Wärmebildkamera fertig stellen können. Anbei noch die Verkabelung, die 
display-pins, einige Testbilder, der code und das youtube-video:

https://www.youtube.com/watch?v=k6qim96wB4k
1
#include <Wire.h>
2
#include "MLX90640_API.h"
3
#include "MLX90640_I2C_Driver.h"
4
#include "SPI.h"
5
#include "Adafruit_GFX.h"
6
#include "Adafruit_ILI9341.h"
7
8
// For the ESP-WROVER_KIT, these are the default.
9
#define TFT_CS   15 
10
#define TFT_DC    2
11
#define TFT_MOSI 13
12
#define TFT_CLK  14
13
#define TFT_RST  26
14
#define TFT_MISO 12
15
#define TFT_LED  27
16
17
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);
18
19
const byte MLX90640_address = 0x33; //Default 7-bit unshifted address of the MLX90640
20
21
#define TA_SHIFT 8 //Default shift for MLX90640 in open air
22
23
static float mlx90640To[768];
24
paramsMLX90640 mlx90640;
25
26
int xPos, yPos;                                // Abtastposition
27
int R_colour, G_colour, B_colour;              // RGB-Farbwert
28
int i, j;                                      // Zählvariable
29
float T_max, T_min;                            // maximale bzw. minimale gemessene Temperatur
30
float T_center;                                // Temperatur in der Bildschirmmitte
31
32
33
34
35
// ***************************************
36
// **************** SETUP ****************
37
// ***************************************
38
39
void setup()
40
   {
41
    Serial.begin(115200);    
42
    
43
    Wire.begin();
44
    Wire.setClock(400000); //Increase I2C clock speed to 400kHz
45
46
    while (!Serial); //Wait for user to open terminal
47
    
48
    Serial.println("MLX90640 IR Array Example");
49
50
    if (isConnected() == false)
51
       {
52
        Serial.println("MLX90640 not detected at default I2C address. Please check wiring. Freezing.");
53
        while (1);
54
       }
55
       
56
    Serial.println("MLX90640 online!");
57
58
    //Get device parameters - We only have to do this once
59
    int status;
60
    uint16_t eeMLX90640[832];
61
    
62
    status = MLX90640_DumpEE(MLX90640_address, eeMLX90640);
63
  
64
    if (status != 0)
65
       Serial.println("Failed to load system parameters");
66
67
    status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
68
  
69
    if (status != 0)
70
       {
71
        Serial.println("Parameter extraction failed");
72
        Serial.print(" status = ");
73
        Serial.println(status);
74
       }
75
76
    //Once params are extracted, we can release eeMLX90640 array
77
78
    MLX90640_I2CWrite(0x33, 0x800D, 6401);    // Schreibt den Wert 1901 (HEX) = 6401 (DEC) ins Register an die Stelle 0x800D, damit der Sensor ausgelesen werden kann!!!
79
                                              // =======================================================================================================================
80
81
    //MLX90640_SetRefreshRate(MLX90640_address, 0x00); //Set rate to 0.25Hz effective - Works
82
    //MLX90640_SetRefreshRate(MLX90640_address, 0x01); //Set rate to 0.5Hz effective - Works
83
    //MLX90640_SetRefreshRate(MLX90640_address, 0x02); //Set rate to 1Hz effective - Works
84
    //MLX90640_SetRefreshRate(MLX90640_address, 0x03); //Set rate to 2Hz effective - Works
85
    MLX90640_SetRefreshRate(MLX90640_address, 0x04); //Set rate to 4Hz effective - Works
86
    //MLX90640_SetRefreshRate(MLX90640_address, 0x05); //Set rate to 8Hz effective - Works at 800kHz
87
    //MLX90640_SetRefreshRate(MLX90640_address, 0x06); //Set rate to 16Hz effective - Works at 800kHz
88
    //MLX90640_SetRefreshRate(MLX90640_address, 0x07); //Set rate to 32Hz effective - fails
89
    
90
    
91
    pinMode(TFT_LED, OUTPUT);
92
    digitalWrite(TFT_LED, HIGH);
93
94
    tft.begin();
95
96
    tft.setRotation(1);
97
98
    tft.fillScreen(ILI9341_BLACK);
99
    tft.fillRect(0, 0, 319, 13, tft.color565(255, 0, 10));
100
    tft.setCursor(100, 3);
101
    tft.setTextSize(1);
102
    tft.setTextColor(ILI9341_YELLOW, tft.color565(255, 0, 10));
103
    tft.print("Thermographie - stoppi");    
104
105
    tft.drawLine(250, 210 - 0, 258, 210 - 0, tft.color565(255, 255, 255));
106
    tft.drawLine(250, 210 - 30, 258, 210 - 30, tft.color565(255, 255, 255));
107
    tft.drawLine(250, 210 - 60, 258, 210 - 60, tft.color565(255, 255, 255));
108
    tft.drawLine(250, 210 - 90, 258, 210 - 90, tft.color565(255, 255, 255));
109
    tft.drawLine(250, 210 - 120, 258, 210 - 120, tft.color565(255, 255, 255));
110
    tft.drawLine(250, 210 - 150, 258, 210 - 150, tft.color565(255, 255, 255));
111
    tft.drawLine(250, 210 - 180, 258, 210 - 180, tft.color565(255, 255, 255));
112
113
    
114
    tft.setCursor(80, 220);
115
    tft.setTextColor(ILI9341_WHITE, tft.color565(0, 0, 0));
116
    tft.print("T+ = ");    
117
118
119
    // Zeichnen der seitlichen Farbskala
120
    // =================================
121
 
122
    for (i = 0; i < 181; i++)
123
       {
124
        //value = random(180);
125
        
126
        getColour(i);
127
        tft.drawLine(240, 210 - i, 250, 210 - i, tft.color565(R_colour, G_colour, B_colour));
128
       } 
129
130
   } 
131
132
133
134
// *******************************************
135
// ************** HAUPTSCHLEIFE **************
136
// *******************************************
137
138
void loop()
139
   {
140
    for (byte x = 0 ; x < 2 ; x++) //Read both subpages
141
       {
142
        uint16_t mlx90640Frame[834];
143
        int status = MLX90640_GetFrameData(MLX90640_address, mlx90640Frame);
144
    
145
        if (status < 0)
146
           {
147
            Serial.print("GetFrame Error: ");
148
            Serial.println(status);
149
           }
150
151
        float vdd = MLX90640_GetVdd(mlx90640Frame, &mlx90640);
152
        float Ta = MLX90640_GetTa(mlx90640Frame, &mlx90640);
153
154
        float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
155
        float emissivity = 0.95;
156
157
        MLX90640_CalculateTo(mlx90640Frame, &mlx90640, emissivity, tr, mlx90640To);
158
       }
159
160
       
161
    // determine of T_min and T_max and find out error pixel
162
    // =====================================================
163
164
    mlx90640To[1*32 + 21] = 0.5 * (mlx90640To[1*32 + 20] + mlx90640To[1*32 + 22]);    // elininate the error-pixels
165
    mlx90640To[4*32 + 30] = 0.5 * (mlx90640To[4*32 + 29] + mlx90640To[4*32 + 31]);    // elininate the error-pixels
166
    
167
    T_min = mlx90640To[0];
168
    T_max = mlx90640To[0];
169
170
    for (i = 1; i < 768; i++)
171
       {
172
        if((mlx90640To[i] > -41) && (mlx90640To[i] < 301))
173
           {
174
            if(mlx90640To[i] < T_min)
175
               {
176
                T_min = mlx90640To[i];
177
               }
178
179
            if(mlx90640To[i] > T_max)
180
               {
181
                T_max = mlx90640To[i];
182
               }
183
           }
184
        else if(i > 0)
185
           {
186
            mlx90640To[i] = mlx90640To[i-1];
187
           }
188
        else
189
           {
190
            mlx90640To[i] = mlx90640To[i+1];
191
           }
192
       }
193
194
195
    // determine of T_center
196
    // =====================
197
198
    T_center = mlx90640To[11* 32 + 15];    
199
200
201
    // drawing the picture
202
    // ===================
203
204
    for (i = 0 ; i < 24 ; i++)
205
       {
206
        for (j = 0; j < 32; j++)
207
           {
208
            mlx90640To[i*32 + j] = 180.0 * (mlx90640To[i*32 + j] - T_min) / (T_max - T_min);
209
                       
210
            getColour(mlx90640To[i*32 + j]);
211
            
212
            tft.fillRect(217 - j * 7, 35 + i * 7, 7, 7, tft.color565(R_colour, G_colour, B_colour));
213
           }
214
       }
215
216
    tft.fillRect(260, 25, 47, 10, tft.color565(0, 0, 0));
217
    tft.fillRect(260, 205, 47, 10, tft.color565(0, 0, 0));    
218
    tft.fillRect(115, 220, 47, 10, tft.color565(0, 0, 0));    
219
220
    tft.setTextColor(ILI9341_WHITE, tft.color565(0, 0, 0));
221
    tft.setCursor(265, 25);
222
    tft.print(T_max, 1);
223
    tft.setCursor(265, 205);
224
    tft.print(T_min, 1);
225
    tft.setCursor(120, 220);
226
    tft.print(T_center, 1);
227
228
    tft.setCursor(310, 25);
229
    tft.print("C");
230
    tft.setCursor(310, 205);
231
    tft.print("C");
232
    tft.setCursor(165, 220);
233
    tft.print("C");
234
235
    tft.drawLine(217 - 15*7 + 3.5 - 5, 11*7 + 35 + 3.5, 217 - 15*7 + 3.5 + 5, 11*7 + 35 + 3.5, tft.color565(255, 255, 255));
236
    tft.drawLine(217 - 15*7 + 3.5, 11*7 + 35 + 3.5 - 5, 217 - 15*7 + 3.5, 11*7 + 35 + 3.5 + 5,  tft.color565(255, 255, 255));
237
 
238
    delay(20);
239
   }
240
   
241
242
243
// =========================
244
// ===== Farbbestimmung ====
245
// =========================
246
247
void getColour(int j)
248
   {
249
    if (j >= 0 && j < 30)
250
       {
251
        R_colour = 0;
252
        G_colour = 0;
253
        B_colour = 20 + (120.0/30.0) * j;
254
       }
255
    
256
    if (j >= 30 && j < 60)
257
       {
258
        R_colour = (120.0 / 30) * (j - 30.0);
259
        G_colour = 0;
260
        B_colour = 140 - (60.0/30.0) * (j - 30.0);
261
       }
262
263
    if (j >= 60 && j < 90)
264
       {
265
        R_colour = 120 + (135.0/30.0) * (j - 60.0);
266
        G_colour = 0;
267
        B_colour = 80 - (70.0/30.0) * (j - 60.0);
268
       }
269
270
    if (j >= 90 && j < 120)
271
       {
272
        R_colour = 255;
273
        G_colour = 0 + (60.0/30.0) * (j - 90.0);
274
        B_colour = 10 - (10.0/30.0) * (j - 90.0);
275
       }
276
277
    if (j >= 120 && j < 150)
278
       {
279
        R_colour = 255;
280
        G_colour = 60 + (175.0/30.0) * (j - 120.0);
281
        B_colour = 0;
282
       }
283
284
    if (j >= 150 && j <= 180)
285
       {
286
        R_colour = 255;
287
        G_colour = 235 + (20.0/30.0) * (j - 150.0);
288
        B_colour = 0 + 255.0/30.0 * (j - 150.0);
289
       }
290
291
   }
292
   
293
   
294
//Returns true if the MLX90640 is detected on the I2C bus
295
boolean isConnected()
296
   {
297
    Wire.beginTransmission((uint8_t)MLX90640_address);
298
  
299
    if (Wire.endTransmission() != 0)
300
       return (false); //Sensor did not ACK
301
    
302
    return (true);
303
   }

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Mit dem eingetroffenen Batteriehalter konnte ich heute meine 
Wärmebildkamera finalisieren.

Ich betreibe die Kamera mit nur einem 18650-Akku und nachfolgendem 
step-up-Wandler (auf 5V), da sonst der 3.3V-Spannungsregler auf dem 
ESP32 zu warm wird.

Hier 
https://as-electronic.net/epages/f5f396c6-206d-4fb6-8b4c-7e9832c1a74f.sf/de_DE/?ObjectPath=/Shops/f5f396c6-206d-4fb6-8b4c-7e9832c1a74f/Products/MLX90640ESF-BAB-000 
ist der MLX9640-Sensor aus Deutschland für rund 40 Euro zu beziehen. 
Damit ist es möglich, die Kamera um nur ca. 54 Euro nachzubauen...

von Marcel (Gast)


Lesenswert?

Ich weiß der Thread ist älter, habe jedoch eine kurze Frage zu den MLX 
Sensoren.

Muss man Pullup Widerstände auf die SDA- und SCL-Leitungen hinzufügen 
wenn man den Sensor ohne fertiges Breakout-Board kauft? Sprich, den 
Sensor wie im letzten Kauflink von as-electronic. Du hast ja von 
Aliexpress das blaue Breakout-Board benutzt (ich vermute, dass da 
nämlich Pullups drauf sind).

LG

von Christoph E. (stoppi)


Angehängte Dateien:

Lesenswert?

Hallo!

Ja, sind auf beiden Leitungen pullups (2.2 kOhm), siehe Bild

von 14_km (Gast)


Lesenswert?

vielen Dank für den tollen Thread  - das ist wirklich außerordentlich 
interessant.

Bin begeistert über diese tolle IDEE. Ihr seid Super

Vg

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.