Eddie Fiorentine Eddie Fiorentine - 7 months ago 80
Python Question

Raspberry Pi - Interfacing with an i2c enabled IR temp sensor (MLX90614)

So I was recruited to my school's Baja racing team where we design build and compete with offroad dune-buggy type cars. I am a CS major and have quite a bit experience with python, and therefore was asked to help out the electrical team to interface with all of the sensors which we wanted on the car. So far so good, but now I am using an IR temperature sensor which reads ambient and object temperatures. (We are going to put this somewhere on the engine to read it's temperature and output to our GUI)

The problem is that the only libraries that seem to have used this sensor are all written in C, and normally used with arduinos... Nonetheless I compiled and edited some C code that I found online and it works great! In C. :( Since our project is completely based in python; I'd really like some help reading this sensor via i2c and Python, although I really don't have a ton of experience in writing libraries, especially not for electronics. Any tips would be great to steer me in the correct direction. Here is the C code that we are currently using that I basically want the same thing in Python:

//fordit: gcc MLXi2c.c -o i2c -l bcm2835
#include <stdio.h>
#include <bcm2835.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include<time.h>
#define AVG 1 //averaging samples
#define LOGTIME 10 //loging period
int main(int argc, char **argv)
{
unsigned char buf[6];
unsigned char i,reg;
double temp=0,calc=0, skytemp,atemp;
FILE *flog;
flog=fopen("mlxlog.csv", "a");..
bcm2835_init();
bcm2835_i2c_begin();
bcm2835_i2c_set_baudrate(25000);.
// set address...........................................................................................
bcm2835_i2c_setSlaveAddress(0x5a);
....
printf("\nOk, your device is working!!\n");
....
....
while(1) {
time_t t = time(NULL);
<------>struct tm tm = *localtime(&t);
<------>calc=0;
<------>reg=7;
<------>for(i=0;i<AVG;i++){
<------> bcm2835_i2c_begin();
<------> bcm2835_i2c_write (&reg, 1);
<------> bcm2835_i2c_read_register_rs(&reg,&buf[0],3);
<------> temp = (double) (((buf[1]) << 8) + buf[0]);
<------> temp = (temp * 0.02)-0.01;
<--> temp = temp - 273.15;
<------> calc+=temp;
<------> sleep(1);
<------> }
<------>skytemp=calc/AVG;
<------>calc=0;
<------>reg=6;
<------>for(i=0;i<AVG;i++){
<------> bcm2835_i2c_begin();
<------> bcm2835_i2c_write (&reg, 1);
<------> bcm2835_i2c_read_register_rs(&reg,&buf[0],3);
<------> temp = (double) (((buf[1]) << 8) + buf[0]);
<------> temp = (temp * 0.02)-0.01;
<--> temp = temp - 273.15;
<------> calc+=temp;
<------> sleep(1);
<------> }
<------>atemp=calc/AVG;
<------>printf("%02d-%02d %02d:%02d:%02d\n Tambi=%04.2f C, Tobj=%04.2f C\n", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,atemp,skytemp);
<------>fprintf(flog,"%04d-%02d-%02d %02d:%02d:%02d,%04.2f,%04.02f\n",tm.tm_year+1900, tm.tm_mon +1, tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec,atemp,skytemp);
<------>fflush(flog);
<------>sleep(LOGTIME-(2*AVG));
}
...
printf("[done]\n");
}


thanks in advance!


  • Eddie


jDo jDo
Answer

Changes:

  • removed while(1) - only one reading per execution (set up loop in python instead)
  • the last printf now outputs json
  • added return 0; to main() so python's subprocess module knows what's up

Save this as mlx90614_query.c and compile:

//fordit:  gcc mlx90614_query.c -o mlx90614_query -l bcm2835
#include <stdio.h>
#include <bcm2835.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#define AVG 1   //averaging samples
#define LOGTIME 10  //loging period

int main(int argc, char **argv)
{
    unsigned char buf[6];
    unsigned char i,reg;
    double temp=0,calc=0, skytemp,atemp;
    FILE *flog;
    flog=fopen("mlxlog.csv", "a");
    bcm2835_init();
    bcm2835_i2c_begin();
    bcm2835_i2c_set_baudrate(25000);
    // set address
    bcm2835_i2c_setSlaveAddress(0x5a);

    printf("\nOk, your device is working!!\n");

    time_t t = time(NULL);
    struct tm tm = *localtime(&t);
    calc=0;
    reg=7;

    for(i=0;i<AVG;i++){
        bcm2835_i2c_begin();
        bcm2835_i2c_write (&reg, 1);
        bcm2835_i2c_read_register_rs(&reg,&buf[0],3);
        temp = (double) (((buf[1]) << 8) + buf[0]);
        temp = (temp * 0.02)-0.01;
        temp = temp - 273.15;
        calc+=temp;
        sleep(1);
    }

    skytemp=calc/AVG;
    calc=0;
    reg=6;

    for(i=0;i<AVG;i++){
        bcm2835_i2c_begin();
        bcm2835_i2c_write (&reg, 1);
        bcm2835_i2c_read_register_rs(&reg,&buf[0],3);
        temp = (double) (((buf[1]) << 8) + buf[0]);
        temp = (temp * 0.02)-0.01;
        temp = temp - 273.15;
        calc+=temp;
        sleep(1);
    }

    atemp=calc/AVG;

    printf("|{\"time\":{\"month\":\"%02d\",\"day\":\"%02d\",\"hour\":\"%02d\",\"min\":\"%02d\",\"sec\":\"%02d\"},\"data\":{\"t_ambi\":\"%04.2f\",\"t_obj\":\"%04.2f\"}}|\n", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,atemp,skytemp);

    fprintf(flog,"%04d-%02d-%02d %02d:%02d:%02d,%04.2f,%04.02f\n",tm.tm_year+1900, tm.tm_mon +1, tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec,atemp,skytemp);

    fflush(flog);
    sleep(LOGTIME-(2*AVG));

    printf("[done]\n");

    return 0;
}

Your C program now outputs your existing debug messages plus this json ("|" used as delimiter):

{
    "time": {
        "month": "03",
        "day": "09",
        "hour": "20",
        "min": "24",
        "sec": "28"
    },
    "data": {
        "t_ambi": "77.77",
        "t_obj": "34.44"
    }
}

Save this as a python script in the same folder as mlx90614_query (the extra spaces will cause trouble if you copy/paste to an interactive interpreter):

from __future__ import print_function
import json
import subprocess
import sys

sensor_dict = {}

py3 = False

# python version check determines string handling

try:
    if (sys.version_info > (3, 0)):
        py3 = True    
except:
    pass

try:

    subp_ret = subprocess.check_output(["./mlx90614_query"])

    # at this point, subprocess succeeded and subp_ret holds your output 

    if py3:
        subp_ret = subp_ret.decode('utf8')

    sensor_dict = json.loads(subp_ret.split("|")[1].strip())

    # cast temperatures to float and print

    for k in sensor_dict["data"]:
        val = float(sensor_dict["data"][k]) 
        sensor_dict["data"][k] = val  
        print (k, "\t", val) 

    # cast date/time segments to int and print

    for k in sensor_dict["time"]:
        val = int(sensor_dict["time"][k])
        sensor_dict["time"][k] = val  
        print (k, "\t", val)         

    # Now go win that race! :P

except Exception as e:
    print(str(e))

Output:

$ gcc mlx90614_query.c -o mlx90614_query -l bcm2835
$

$ ./mlx90614_query
Ok, your device is working!!
|{"time":{"month":"03","day":"09","hour":"21","min":"45","sec":"53"},"data":{"t_ambi":"0.00","t_obj":"0.00"}}|
[done]


$ python3.4 mlx_print.py 
t_obj    34.44
t_ambi   77.77
hour     21
sec      33
min      58
month    3
day      9

$ python2 mlx_print.py 
t_ambi   77.77
t_obj    34.44
min      58
sec      37
day      9
hour     21
month    3

Sorry for stealing your homework - Just <3 to code :D