Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set I2C pins in Orange Pi Lite single board computer #64

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
# mlx90640-library

MLX90640 library functions

# Python

This fork also incorporates code from pjaos.

To run the python module as pi user (non-root). Use:

sudo usermod -a -G i2c pi

To build (requires numpy/cffi):

python setup.py install

Run examples in examples folder. Need pygame + x server to run gui.

![Screenshot of pygame interface](/python/examples/thermal-cam-pygame-hand.png)
107 changes: 107 additions & 0 deletions functions/MLX90640_LINUX_I2C_Driver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @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.
*
* Note: The original is from https://github.com/pimoroni/mlx90640-library with
* PR patch from https://github.com/pjaos/mlx90640-library to allow compilation.
*
*/
#include "MLX90640_I2C_Driver.h"
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>

int i2c_fd = 0;
const char *i2c_device = "/dev/i2c-1";

void MLX90640_I2CInit()
{

}

int MLX90640_I2CRead(uint8_t slaveAddr, uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data)
{
if(!i2c_fd){
if ((i2c_fd = open(i2c_device, O_RDWR)) < 0) {
return -1;
}
}

char cmd[2] = {(char)(startAddress >> 8), (char)(startAddress & 0xFF)};
char buf[1664];
uint16_t *p = data;
struct i2c_msg i2c_messages[2];
struct i2c_rdwr_ioctl_data i2c_messageset[1];

i2c_messages[0].addr = slaveAddr;
i2c_messages[0].flags = 0;
i2c_messages[0].len = 2;
i2c_messages[0].buf = (__u8*)cmd;

i2c_messages[1].addr = slaveAddr;
i2c_messages[1].flags = I2C_M_RD | I2C_M_NOSTART;
i2c_messages[1].len = nMemAddressRead * 2;
i2c_messages[1].buf = (__u8*)buf;

//result = write(i2c_fd, cmd, 3);
//result = read(i2c_fd, buf, nMemAddressRead*2);
i2c_messageset[0].msgs = i2c_messages;
i2c_messageset[0].nmsgs = 2;

memset(buf, 0, nMemAddressRead * 2);

if (ioctl(i2c_fd, I2C_RDWR, &i2c_messageset) < 0) {
printf("I2C Read Error!\n");
return -1;
}

for(int count = 0; count < nMemAddressRead; count++){
int i = count << 1;
*p++ = ((uint16_t)buf[i] << 8) | buf[i+1];
}

return 0;
}

void MLX90640_I2CFreqSet(int freq)
{
}

int MLX90640_I2CWrite(uint8_t slaveAddr, uint16_t writeAddress, uint16_t data)
{
char cmd[4] = {(char)(writeAddress >> 8), (char)(writeAddress & 0x00FF), (char)(data >> 8), (char)(data & 0x00FF)};

struct i2c_msg i2c_messages[1];
struct i2c_rdwr_ioctl_data i2c_messageset[1];

i2c_messages[0].addr = slaveAddr;
i2c_messages[0].flags = 0;
i2c_messages[0].len = 4;
i2c_messages[0].buf = (__u8*)cmd;

i2c_messageset[0].msgs = i2c_messages;
i2c_messageset[0].nmsgs = 1;

if (ioctl(i2c_fd, I2C_RDWR, &i2c_messageset) < 0) {
printf("I2C Write Error!\n");
return -1;
}

return 0;
}
69 changes: 69 additions & 0 deletions python/MLX90640.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import cffi
from pathlib import Path

import numpy as np

lib_path = Path(__file__).parent / "_MLX90640.cpython-37m-arm-linux-gnueabihf.so"

ffi = cffi.FFI()
ffi.cdef("""
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[4];
int16_t ct[4];
float alpha[768];
int16_t offset[768];
float kta[768];
float kv[768];
float cpAlpha[2];
int16_t cpOffset[2];
float ilChessC[3];
uint16_t brokenPixels[5];
uint16_t outlierPixels[5];
} paramsMLX90640;

int DumpEE(uint8_t slaveAddr, uint16_t *eeData);
int GetFrameData(uint8_t slaveAddr, uint16_t *frameData);
int ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
float GetVdd(uint16_t *frameData, const paramsMLX90640 *params);
float GetTa(uint16_t *frameData, const paramsMLX90640 *params);
void GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result);
void CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result);
int SetResolution(uint8_t slaveAddr, uint8_t resolution);
int GetCurResolution(uint8_t slaveAddr);
int SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate);
int GetRefreshRate(uint8_t slaveAddr);
int GetSubPageNumber(uint16_t *frameData);
int GetCurMode(uint8_t slaveAddr);
int SetInterleavedMode(uint8_t slaveAddr);
int SetChessMode(uint8_t slaveAddr);
void BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params);

""")

API = ffi.dlopen(str(lib_path))

def temperature_data_to_ndarray(frame):
"""Converts c buffer storing temp data to numpy array of shape 24, 32"""
a = np.frombuffer(ffi.buffer(frame, np.dtype(np.float32).itemsize*768), dtype=np.float32)
return a.reshape((24, 32))

_hertz_refreshrate_pairs = [
(0.5, 0), (1, 1), (2, 2), (4, 3),
(8, 4), (16, 5), (32, 6), (64, 7)]

hertz_to_refresh_rate = {h: rr for h, rr in _hertz_refreshrate_pairs}
refresh_rate_to_hertz = {rr: h for h, rr in _hertz_refreshrate_pairs}

82 changes: 82 additions & 0 deletions python/cffi_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include <stdint.h>
#include <iostream>
#include <cstring>
#include <fstream>
#include <chrono>
#include <thread>
#include <math.h>
#include "../headers/MLX90640_API.h"
#include "../headers/MLX90640_I2C_Driver.h"

extern "C" int I2CRead(uint8_t slaveAddr,uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data) {
return MLX90640_I2CRead(slaveAddr, startAddress, nMemAddressRead, data);
}

extern "C" int I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data) {
return MLX90640_I2CWrite(slaveAddr, writeAddress, data);
}

extern "C" int DumpEE(uint8_t slaveAddr, uint16_t *eeData) {
return MLX90640_DumpEE(slaveAddr, eeData);
}

extern "C" int GetFrameData(uint8_t slaveAddr, uint16_t *frameData) {
return MLX90640_GetFrameData(slaveAddr, frameData);
}

extern "C" int ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640) {
return MLX90640_ExtractParameters(eeData, mlx90640);
}

extern "C" float GetVdd(uint16_t *frameData, const paramsMLX90640 *params) {
return MLX90640_GetVdd(frameData, params);
}

extern "C" float GetTa(uint16_t *frameData, const paramsMLX90640 *params) {
return MLX90640_GetTa(frameData, params);
}

extern "C" void GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result) {
return MLX90640_GetImage(frameData, params, result);
}

extern "C" void CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result) {
return MLX90640_CalculateTo(frameData, params, emissivity, tr, result);
}

extern "C" int SetResolution(uint8_t slaveAddr, uint8_t resolution) {
return MLX90640_SetResolution(slaveAddr, resolution);
}

extern "C" int GetCurResolution(uint8_t slaveAddr) {
return MLX90640_GetCurResolution(slaveAddr);
}

extern "C" int SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate) {
return MLX90640_SetRefreshRate(slaveAddr, refreshRate);
}

extern "C" int GetRefreshRate(uint8_t slaveAddr) {
return MLX90640_GetRefreshRate(slaveAddr);
}

extern "C" int GetSubPageNumber(uint16_t *frameData) {
return MLX90640_GetSubPageNumber(frameData);
}

extern "C" int GetCurMode(uint8_t slaveAddr) {
return MLX90640_GetCurMode(slaveAddr);
}

extern "C" int SetInterleavedMode(uint8_t slaveAddr) {
return MLX90640_SetInterleavedMode(slaveAddr);
}

extern "C" int SetChessMode(uint8_t slaveAddr) {
return MLX90640_SetChessMode(slaveAddr);
}

extern "C" void BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params) {
return MLX90640_BadPixelsCorrection(pixels, to, mode, params);
}

61 changes: 61 additions & 0 deletions python/examples/direct_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import sys
import time

import numpy as np

from MLX90640 import API, ffi, temperature_data_to_ndarray, hertz_to_refresh_rate

MLX_I2C_ADDR = 0x33

hertz = 8

# settings
API.SetRefreshRate(MLX_I2C_ADDR, hertz_to_refresh_rate[hertz])
API.SetChessMode(MLX_I2C_ADDR)

# POR
time.sleep(.08) # wait 80ms
time.sleep(2/hertz) # delay det by refresh rate

# Extract calibration data from EEPROM and store in RAM
eeprom_data = ffi.new("uint16_t[832]")
params = ffi.new("paramsMLX90640*")
API.DumpEE(MLX_I2C_ADDR, eeprom_data)
API.ExtractParameters(eeprom_data, params)
print(params.KsTa)
print(params.kVdd)
for i in range(5):
print(params.brokenPixels[i])
for i in range(5):
print(params.outlierPixels[i])

# TODO: if absolute - wait 4 mins

TA_SHIFT = 8 # the default shift for a MLX90640 device in open air
emissivity = 0.95

frame_buffer = ffi.new("uint16_t[834]")
image_buffer = ffi.new("float[768]")

print("Calc Hertz should be close to chosen value (%s)" % hertz)
last = time.monotonic()
while True:
API.GetFrameData(MLX_I2C_ADDR, frame_buffer);
now = time.monotonic()
diff = now - last
print("Calc Hz: %s" % (1/diff))
last = now

# reflected temperature based on the sensor
# ambient temperature
tr = API.GetTa(frame_buffer, params) - TA_SHIFT

# The object temperatures for all 768 pixels in a
# frame are stored in the mlx90640To array
API.CalculateTo(frame_buffer, params, emissivity, tr, image_buffer);
print("Subpage no: %s" % API.GetSubPageNumber(frame_buffer))

print(temperature_data_to_ndarray(image_buffer))



Loading