openmv/modules/py_fir.c
iabdalkader fbc8d14cda modules: Update framebuffer API.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 16:05:55 +02:00

1030 lines
41 KiB
C

/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2013-2024 OpenMV, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* FIR Python module.
*/
#include "py/runtime.h"
#include "py/objlist.h"
#include "omv_boardconfig.h"
#if OMV_FIR_MLX90621_ENABLE || \
OMV_FIR_MLX90640_ENABLE || \
OMV_FIR_MLX90641_ENABLE || \
OMV_FIR_AMG8833_ENABLE
#include "omv_i2c.h"
#if (OMV_FIR_MLX90621_ENABLE == 1)
#include "MLX90621_API.h"
#include "MLX90621_I2C_Driver.h"
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
#include "MLX90640_API.h"
#include "MLX90640_I2C_Driver.h"
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
#include "MLX90641_API.h"
#include "MLX90641_I2C_Driver.h"
#endif
#include "framebuffer.h"
#include "py_assert.h"
#include "py_helper.h"
#include "py_image.h"
#define MLX90621_ADDR 0x50
#define MLX90621_WIDTH 16
#define MLX90621_HEIGHT 4
#define MLX90621_EEPROM_DATA_SIZE 256
#define MLX90621_FRAME_DATA_SIZE 66
#define MLX90640_ADDR 0x33
#define MLX90640_WIDTH 32
#define MLX90640_HEIGHT 24
#define MLX90640_EEPROM_DATA_SIZE 832
#define MLX90640_FRAME_DATA_SIZE 834
#define MLX90641_ADDR 0x33
#define MLX90641_WIDTH 16
#define MLX90641_HEIGHT 12
#define MLX90641_EEPROM_DATA_SIZE 832
#define MLX90641_FRAME_DATA_SIZE 242
#define AMG8833_ADDR 0xD2
#define AMG8833_WIDTH 8
#define AMG8833_HEIGHT 8
#define AMG8833_RESET_REGISTER 0x01
#define AMG8833_THERMISTOR_REGISTER 0x0E
#define AMG8833_TEMPERATURE_REGISTER 0x80
#define AMG8833_INITIAL_RESET_VALUE 0x3F
#define LEPTON_ADDR 0x54
#define AMG8833_12_TO_16(value) \
({ \
__typeof__ (value) __value = (value); \
if ((__value >> 11) & 1) { \
__value |= 1 << 15; \
} \
__value & 0x87FF; \
})
static omv_i2c_t fir_bus = {};
typedef enum fir_sensor_type {
FIR_NONE,
#if (OMV_FIR_MLX90621_ENABLE == 1)
FIR_MLX90621,
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
FIR_MLX90640,
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
FIR_MLX90641,
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
FIR_AMG8833,
#endif
} fir_sensor_type_t;
static int fir_width = 0;
static int fir_height = 0;
static int fir_ir_fresh_rate = 0;
static int fir_adc_resolution = 0;
static bool fir_transposed = false;
static fir_sensor_type_t fir_sensor = FIR_NONE;
// img->w == data_w && img->h == data_h && img->pixfmt == PIXFORMAT_GRAYSCALE
static void fir_fill_image_float_obj(image_t *img, mp_obj_t *data, float min, float max) {
float tmp = min;
min = (min < max) ? min : max;
max = (max > tmp) ? max : tmp;
float diff = 255.f / (max - min);
for (int y = 0; y < img->h; y++) {
int row_offset = y * img->w;
mp_obj_t *raw_row = data + row_offset;
uint8_t *row_pointer = ((uint8_t *) img->data) + row_offset;
for (int x = 0; x < img->w; x++) {
float raw = mp_obj_get_float(raw_row[x]);
if (raw < min) {
raw = min;
}
if (raw > max) {
raw = max;
}
int pixel = fast_roundf((raw - min) * diff);
row_pointer[x] = __USAT(pixel, 8);
}
}
}
#if (OMV_FIR_MLX90621_ENABLE == 1)
static void fir_MLX90621_get_frame(float *Ta, float *To) {
uint16_t *data = fb_alloc(MLX90621_FRAME_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
PY_ASSERT_TRUE_MSG(MLX90621_GetFrameData(data) >= 0,
"Failed to read the MLX90621 sensor data!");
*Ta = MLX90621_GetTa(data, MP_STATE_PORT(fir_mlx_data));
MLX90621_CalculateTo(data, MP_STATE_PORT(fir_mlx_data), 0.95f, *Ta - 8, To);
fb_free();
}
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
static void fir_MLX90640_get_frame(float *Ta, float *To) {
uint16_t *data = fb_alloc(MLX90640_FRAME_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
// Wait for a new data to be available before calling GetFrameData.
MLX90640_SynchFrame(MLX90640_ADDR);
// Calculate 1st sub-frame...
PY_ASSERT_TRUE_MSG(MLX90640_GetFrameData(MLX90640_ADDR, data) >= 0,
"Failed to read the MLX90640 sensor data!");
*Ta = MLX90640_GetTa(data, MP_STATE_PORT(fir_mlx_data));
MLX90640_CalculateTo(data, MP_STATE_PORT(fir_mlx_data), 0.95f, *Ta - 8, To);
// Calculate 2nd sub-frame...
PY_ASSERT_TRUE_MSG(MLX90640_GetFrameData(MLX90640_ADDR, data) >= 0,
"Failed to read the MLX90640 sensor data!");
*Ta = MLX90640_GetTa(data, MP_STATE_PORT(fir_mlx_data));
MLX90640_CalculateTo(data, MP_STATE_PORT(fir_mlx_data), 0.95f, *Ta - 8, To);
fb_free();
}
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
static void fir_MLX90641_get_frame(float *Ta, float *To) {
uint16_t *data = fb_alloc(MLX90641_FRAME_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
// Wait for a new data to be available before calling GetFrameData.
MLX90641_SynchFrame(MLX90641_ADDR);
PY_ASSERT_TRUE_MSG(MLX90641_GetFrameData(MLX90641_ADDR, data) >= 0,
"Failed to read the MLX90641 sensor data!");
*Ta = MLX90641_GetTa(data, MP_STATE_PORT(fir_mlx_data));
MLX90641_CalculateTo(data, MP_STATE_PORT(fir_mlx_data), 0.95f, *Ta - 8, To);
fb_free();
}
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
static void fir_AMG8833_get_frame(float *Ta, float *To) {
int16_t temp;
int error = 0;
error |= omv_i2c_write_bytes(&fir_bus,
AMG8833_ADDR,
(uint8_t [1]) {AMG8833_THERMISTOR_REGISTER},
1, OMV_I2C_XFER_NO_STOP);
error |= omv_i2c_read_bytes(&fir_bus, AMG8833_ADDR, (uint8_t *) &temp, sizeof(temp), OMV_I2C_XFER_NO_FLAGS);
PY_ASSERT_TRUE_MSG((error == 0), "Failed to read the AMG8833 sensor data!");
*Ta = AMG8833_12_TO_16(temp) * 0.0625f;
int16_t *data = fb_alloc(AMG8833_WIDTH * AMG8833_HEIGHT * sizeof(int16_t), FB_ALLOC_NO_HINT);
error |= omv_i2c_write_bytes(&fir_bus,
AMG8833_ADDR,
(uint8_t [1]) {AMG8833_TEMPERATURE_REGISTER},
1, OMV_I2C_XFER_NO_STOP);
error |= omv_i2c_read_bytes(&fir_bus,
AMG8833_ADDR,
(uint8_t *) data,
AMG8833_WIDTH * AMG8833_HEIGHT * 2,
OMV_I2C_XFER_NO_FLAGS);
PY_ASSERT_TRUE_MSG((error == 0), "Failed to read the AMG8833 sensor data!");
for (int i = 0, ii = AMG8833_WIDTH * AMG8833_HEIGHT; i < ii; i++) {
To[i] = AMG8833_12_TO_16(data[i]) * 0.25f;
}
fb_free();
}
#endif
static mp_obj_t fir_get_ir(int w, int h, float Ta, float *To, bool mirror,
bool flip, bool dst_transpose, bool src_transpose) {
mp_obj_list_t *list = (mp_obj_list_t *) mp_obj_new_list(w * h, NULL);
float min = FLT_MAX;
float max = -FLT_MAX;
if (!src_transpose) {
for (int y = 0; y < h; y++) {
int y_dst = flip ? (h - y - 1) : y;
float *raw_row = To + (y * w);
mp_obj_t *list_row = list->items + (y_dst * w);
mp_obj_t *t_list_row = list->items + y_dst;
for (int x = 0; x < w; x++) {
int x_dst = mirror ? (w - x - 1) : x;
float raw = raw_row[x];
if (raw < min) {
min = raw;
}
if (raw > max) {
max = raw;
}
mp_obj_t f = mp_obj_new_float(raw);
if (!dst_transpose) {
list_row[x_dst] = f;
} else {
t_list_row[x_dst * h] = f;
}
}
}
} else {
for (int x = 0; x < w; x++) {
int x_dst = mirror ? (w - x - 1) : x;
float *raw_row = To + (x * h);
mp_obj_t *t_list_row = list->items + (x_dst * h);
mp_obj_t *list_row = list->items + x_dst;
for (int y = 0; y < h; y++) {
int y_dst = flip ? (h - y - 1) : y;
float raw = raw_row[y];
if (raw < min) {
min = raw;
}
if (raw > max) {
max = raw;
}
mp_obj_t f = mp_obj_new_float(raw);
if (!dst_transpose) {
list_row[y_dst * w] = f;
} else {
t_list_row[y_dst] = f;
}
}
}
}
mp_obj_t tuple[4];
tuple[0] = mp_obj_new_float(Ta);
tuple[1] = MP_OBJ_FROM_PTR(list);
tuple[2] = mp_obj_new_float(min);
tuple[3] = mp_obj_new_float(max);
return mp_obj_new_tuple(4, tuple);
}
static mp_obj_t py_fir_deinit() {
if (fir_sensor != FIR_NONE) {
omv_i2c_deinit(&fir_bus);
fir_sensor = FIR_NONE;
}
#if ((OMV_FIR_MLX90621_ENABLE == 1) || (OMV_FIR_MLX90640_ENABLE == 1) || (OMV_FIR_MLX90641_ENABLE == 1))
if (MP_STATE_PORT(fir_mlx_data) != NULL) {
MP_STATE_PORT(fir_mlx_data) = NULL;
}
#endif
fir_width = 0;
fir_height = 0;
fir_ir_fresh_rate = 0;
fir_adc_resolution = 0;
fir_transposed = false;
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_fir_deinit_obj, py_fir_deinit);
mp_obj_t py_fir_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_type, ARG_refresh, ARG_resolution };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_type, MP_ARG_INT, {.u_int = -1 } },
{ MP_QSTR_refresh, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_resolution, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
py_fir_deinit();
bool first_init = true;
int type = args[ARG_type].u_int;
if (type == -1) {
FIR_SCAN_RETRY:
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_STANDARD);
// Scan and detect any supported sensor.
uint8_t dev_list[10];
int dev_size = omv_i2c_scan(&fir_bus, dev_list, sizeof(dev_list));
for (int i = 0; i < dev_size && type == -1; i++) {
switch (dev_list[i]) {
#if (OMV_FIR_MLX90621_ENABLE == 1)
case (MLX90621_ADDR << 1): {
type = FIR_MLX90621;
break;
}
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
case (MLX90640_ADDR << 1): {
type = FIR_MLX90640;
break;
}
#endif
#if (OMV_FIR_MLX90640_ENABLE == 0) \
&& (OMV_FIR_MLX90641_ENABLE == 1)
case (MLX90641_ADDR << 1): {
type = FIR_MLX90641;
break;
}
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
case AMG8833_ADDR: {
type = FIR_AMG8833;
break;
}
#endif
default:
continue;
}
}
if (type == -1 && first_init) {
first_init = false;
// Recover bus and scan one more time.
omv_i2c_pulse_scl(&fir_bus);
goto FIR_SCAN_RETRY;
}
omv_i2c_deinit(&fir_bus);
}
// Initialize the detected sensor.
first_init = true;
switch (type) {
#if (OMV_FIR_MLX90621_ENABLE == 1)
case FIR_MLX90621: {
// Set refresh rate and ADC resolution
uint32_t ir_fresh_rate = args[ARG_refresh].u_int != -1 ? args[ARG_refresh].u_int : 64;
uint32_t adc_resolution = args[ARG_resolution].u_int != -1 ? args[ARG_resolution].u_int : 18;
// sanitize values
ir_fresh_rate = 14 - __CLZ(__RBIT((ir_fresh_rate > 512) ? 512 : ((ir_fresh_rate < 1) ? 1 : ir_fresh_rate)));
adc_resolution = ((adc_resolution > 18) ? 18 : ((adc_resolution < 15) ? 15 : adc_resolution)) - 15;
MP_STATE_PORT(fir_mlx_data) = m_malloc(sizeof(paramsMLX90621));
fir_sensor = FIR_MLX90621;
FIR_MLX90621_RETRY:
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_FULL); // The EEPROM must be read at <= 400KHz.
MLX90621_I2CInit(&fir_bus);
fb_alloc_mark();
uint8_t *eeprom = fb_alloc(MLX90621_EEPROM_DATA_SIZE * sizeof(uint8_t), FB_ALLOC_NO_HINT);
int error = MLX90621_DumpEE(eeprom);
error |= MLX90621_Configure(eeprom);
error |= MLX90621_SetRefreshRate(ir_fresh_rate);
error |= MLX90621_SetResolution(adc_resolution);
error |= MLX90621_ExtractParameters(eeprom, MP_STATE_PORT(fir_mlx_data));
fb_alloc_free_till_mark();
if (error != 0) {
if (first_init) {
first_init = false;
omv_i2c_pulse_scl(&fir_bus);
goto FIR_MLX90621_RETRY;
} else {
py_fir_deinit();
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to init the MLX90621!"));
}
}
// Switch to FAST speed
omv_i2c_deinit(&fir_bus);
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_FAST);
fir_width = MLX90621_WIDTH;
fir_height = MLX90621_HEIGHT;
fir_ir_fresh_rate = ir_fresh_rate;
fir_adc_resolution = adc_resolution;
return mp_const_none;
}
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
case FIR_MLX90640: {
// Set refresh rate and ADC resolution
uint32_t ir_fresh_rate = args[ARG_refresh].u_int != -1 ? args[ARG_refresh].u_int : 32;
uint32_t adc_resolution = args[ARG_resolution].u_int != -1 ? args[ARG_resolution].u_int : 19;
// sanitize values
ir_fresh_rate = __CLZ(__RBIT((ir_fresh_rate > 64) ? 64 : ((ir_fresh_rate < 1) ? 1 : ir_fresh_rate))) + 1;
adc_resolution = ((adc_resolution > 19) ? 19 : ((adc_resolution < 16) ? 16 : adc_resolution)) - 16;
MP_STATE_PORT(fir_mlx_data) = m_malloc(sizeof(paramsMLX90640));
fir_sensor = FIR_MLX90640;
FIR_MLX90640_RETRY:
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_FULL); // The EEPROM must be read at <= 400KHz.
MLX90640_I2CInit(&fir_bus);
fb_alloc_mark();
uint16_t *eeprom = fb_alloc(MLX90640_EEPROM_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
int error = MLX90640_DumpEE(MLX90640_ADDR, eeprom);
error |= MLX90640_SetRefreshRate(MLX90640_ADDR, ir_fresh_rate);
error |= MLX90640_SetResolution(MLX90640_ADDR, adc_resolution);
error |= MLX90640_ExtractParameters(eeprom, MP_STATE_PORT(fir_mlx_data));
fb_alloc_free_till_mark();
if (error != 0) {
if (first_init) {
first_init = false;
omv_i2c_pulse_scl(&fir_bus);
goto FIR_MLX90640_RETRY;
} else {
py_fir_deinit();
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to init the MLX90640!"));
}
}
// Switch to FAST speed
omv_i2c_deinit(&fir_bus);
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_FAST);
fir_width = MLX90640_WIDTH;
fir_height = MLX90640_HEIGHT;
fir_ir_fresh_rate = ir_fresh_rate;
fir_adc_resolution = adc_resolution;
return mp_const_none;
}
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
case FIR_MLX90641: {
// Set refresh rate and ADC resolution
uint32_t ir_fresh_rate = args[ARG_refresh].u_int != -1 ? args[ARG_refresh].u_int : 32;
uint32_t adc_resolution = args[ARG_resolution].u_int != -1 ? args[ARG_resolution].u_int : 19;
// sanitize values
ir_fresh_rate = __CLZ(__RBIT((ir_fresh_rate > 64) ? 64 : ((ir_fresh_rate < 1) ? 1 : ir_fresh_rate))) + 1;
adc_resolution = ((adc_resolution > 19) ? 19 : ((adc_resolution < 16) ? 16 : adc_resolution)) - 16;
MP_STATE_PORT(fir_mlx_data) = m_malloc(sizeof(paramsMLX90641));
fir_sensor = FIR_MLX90641;
FIR_MLX90641_RETRY:
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_FULL); // The EEPROM must be read at <= 400KHz.
MLX90641_I2CInit(&fir_bus);
fb_alloc_mark();
uint16_t *eeprom = fb_alloc(MLX90641_EEPROM_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
int error = MLX90641_DumpEE(MLX90641_ADDR, eeprom);
error |= MLX90641_SetRefreshRate(MLX90641_ADDR, ir_fresh_rate);
error |= MLX90641_SetResolution(MLX90641_ADDR, adc_resolution);
error |= MLX90641_ExtractParameters(eeprom, MP_STATE_PORT(fir_mlx_data));
fb_alloc_free_till_mark();
if (error != 0) {
if (first_init) {
first_init = false;
omv_i2c_pulse_scl(&fir_bus);
goto FIR_MLX90641_RETRY;
} else {
py_fir_deinit();
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to init the MLX90641!"));
}
}
// Switch to FAST speed
omv_i2c_deinit(&fir_bus);
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_FAST);
fir_width = MLX90641_WIDTH;
fir_height = MLX90641_HEIGHT;
fir_ir_fresh_rate = ir_fresh_rate;
fir_adc_resolution = adc_resolution;
return mp_const_none;
}
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
case FIR_AMG8833: {
fir_sensor = FIR_AMG8833;
FIR_AMG8833_RETRY:
omv_i2c_init(&fir_bus, OMV_FIR_I2C_ID, OMV_I2C_SPEED_STANDARD);
int error = omv_i2c_write_bytes(&fir_bus, AMG8833_ADDR,
(uint8_t [2]) {AMG8833_RESET_REGISTER, AMG8833_INITIAL_RESET_VALUE}, 2, 0);
if (error != 0) {
if (first_init) {
first_init = false;
omv_i2c_pulse_scl(&fir_bus);
goto FIR_AMG8833_RETRY;
} else {
py_fir_deinit();
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to init the AMG8833!"));
}
}
fir_width = AMG8833_WIDTH;
fir_height = AMG8833_HEIGHT;
fir_ir_fresh_rate = 10;
fir_adc_resolution = 12;
return mp_const_none;
}
#endif
default: {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to detect a supported FIR sensor."));
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_init_obj, 0, py_fir_init);
static mp_obj_t py_fir_type() {
if (fir_sensor != FIR_NONE) {
return mp_obj_new_int(fir_sensor);
}
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_fir_type_obj, py_fir_type);
static mp_obj_t py_fir_width() {
if (fir_sensor != FIR_NONE) {
return mp_obj_new_int(fir_width);
}
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_fir_width_obj, py_fir_width);
static mp_obj_t py_fir_height() {
if (fir_sensor != FIR_NONE) {
return mp_obj_new_int(fir_height);
}
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_fir_height_obj, py_fir_height);
static mp_obj_t py_fir_refresh() {
#if (OMV_FIR_MLX90621_ENABLE == 1)
const int mlx_90621_refresh_rates[16] = {512, 512, 512, 512, 512, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0};
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1) || (OMV_FIR_MLX90641_ENABLE == 1)
const int mlx_90640_1_refresh_rates[8] = {0, 1, 2, 4, 8, 16, 32, 64};
#endif
switch (fir_sensor) {
#if (OMV_FIR_MLX90621_ENABLE == 1)
case FIR_MLX90621:
return mp_obj_new_int(mlx_90621_refresh_rates[fir_ir_fresh_rate]);
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
case FIR_MLX90640:
return mp_obj_new_int(mlx_90640_1_refresh_rates[fir_ir_fresh_rate]);
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
case FIR_MLX90641:
return mp_obj_new_int(mlx_90640_1_refresh_rates[fir_ir_fresh_rate]);
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
case FIR_AMG8833:
return mp_obj_new_int(fir_ir_fresh_rate);
#endif
default:
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_fir_refresh_obj, py_fir_refresh);
static mp_obj_t py_fir_resolution() {
switch (fir_sensor) {
#if (OMV_FIR_MLX90621_ENABLE == 1)
case FIR_MLX90621:
return mp_obj_new_int(fir_adc_resolution + 15);
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
case FIR_MLX90640:
return mp_obj_new_int(fir_adc_resolution + 16);
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
case FIR_MLX90641:
return mp_obj_new_int(fir_adc_resolution + 16);
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
case FIR_AMG8833:
return mp_obj_new_int(fir_adc_resolution);
#endif
default:
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_fir_resolution_obj, py_fir_resolution);
mp_obj_t py_fir_read_ta() {
switch (fir_sensor) {
#if (OMV_FIR_MLX90621_ENABLE == 1)
case FIR_MLX90621: {
fb_alloc_mark();
uint16_t *data = fb_alloc(MLX90621_FRAME_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
PY_ASSERT_TRUE_MSG(MLX90621_GetFrameData(data) >= 0,
"Failed to read the MLX90640 sensor data!");
mp_obj_t result = mp_obj_new_float(MLX90621_GetTa(data, MP_STATE_PORT(fir_mlx_data)));
fb_alloc_free_till_mark();
return result;
}
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
case FIR_MLX90640: {
fb_alloc_mark();
uint16_t *data = fb_alloc(MLX90640_FRAME_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
PY_ASSERT_TRUE_MSG(MLX90640_GetFrameData(MLX90640_ADDR, data) >= 0,
"Failed to read the MLX90640 sensor data!");
mp_obj_t result = mp_obj_new_float(MLX90640_GetTa(data, MP_STATE_PORT(fir_mlx_data)));
fb_alloc_free_till_mark();
return result;
}
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
case FIR_MLX90641: {
fb_alloc_mark();
uint16_t *data = fb_alloc(MLX90641_FRAME_DATA_SIZE * sizeof(uint16_t), FB_ALLOC_NO_HINT);
PY_ASSERT_TRUE_MSG(MLX90641_GetFrameData(MLX90641_ADDR, data) >= 0,
"Failed to read the MLX90641 sensor data!");
mp_obj_t result = mp_obj_new_float(MLX90641_GetTa(data, MP_STATE_PORT(fir_mlx_data)));
fb_alloc_free_till_mark();
return result;
}
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
case FIR_AMG8833: {
int16_t temp;
int error = 0;
error |= omv_i2c_write_bytes(&fir_bus,
AMG8833_ADDR,
(uint8_t [1]) {AMG8833_THERMISTOR_REGISTER},
1,
OMV_I2C_XFER_NO_STOP);
error |= omv_i2c_read_bytes(&fir_bus, AMG8833_ADDR, (uint8_t *) &temp, sizeof(temp), OMV_I2C_XFER_NO_FLAGS);
PY_ASSERT_TRUE_MSG((error == 0), "Failed to read the AMG8833 sensor data!");
return mp_obj_new_float(AMG8833_12_TO_16(temp) * 0.0625f);
}
#endif
default: {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_0(py_fir_read_ta_obj, py_fir_read_ta);
mp_obj_t py_fir_read_ir(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_hmirror, ARG_vflip, ARG_transpose, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_hmirror, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
{ MP_QSTR_vflip, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
{ MP_QSTR_transpose, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
{ MP_QSTR_timeout, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
fir_transposed = args[ARG_transpose].u_bool;
switch (fir_sensor) {
#if (OMV_FIR_MLX90621_ENABLE == 1)
case FIR_MLX90621: {
fb_alloc_mark();
float Ta, *To = fb_alloc(MLX90621_WIDTH * MLX90621_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_MLX90621_get_frame(&Ta, To);
mp_obj_t result = fir_get_ir(MLX90621_WIDTH, MLX90621_HEIGHT, Ta, To, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, true);
fb_alloc_free_till_mark();
return result;
}
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
case FIR_MLX90640: {
fb_alloc_mark();
float Ta, *To = fb_alloc(MLX90640_WIDTH * MLX90640_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_MLX90640_get_frame(&Ta, To);
mp_obj_t result = fir_get_ir(MLX90640_WIDTH, MLX90640_HEIGHT, Ta, To, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, false);
fb_alloc_free_till_mark();
return result;
}
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
case FIR_MLX90641: {
fb_alloc_mark();
float Ta, *To = fb_alloc(MLX90641_WIDTH * MLX90641_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_MLX90641_get_frame(&Ta, To);
mp_obj_t result = fir_get_ir(MLX90641_WIDTH, MLX90641_HEIGHT, Ta, To, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, false);
fb_alloc_free_till_mark();
return result;
}
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
case FIR_AMG8833: {
fb_alloc_mark();
float Ta, *To = fb_alloc(AMG8833_WIDTH * AMG8833_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_AMG8833_get_frame(&Ta, To);
mp_obj_t result = fir_get_ir(AMG8833_WIDTH, AMG8833_HEIGHT, Ta, To, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, true);
fb_alloc_free_till_mark();
return result;
}
#endif
default: {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_read_ir_obj, 0, py_fir_read_ir);
mp_obj_t py_fir_draw_ir(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {
ARG_x, ARG_y, ARG_x_scale, ARG_y_scale, ARG_roi, ARG_channel, ARG_alpha,
ARG_color_palette, ARG_alpha_palette, ARG_hint, ARG_scale
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_x_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_y_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_roi, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_rgb_channel, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_alpha, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 255 } },
{ MP_QSTR_color_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_INT(COLOR_PALETTE_RAINBOW)} },
{ MP_QSTR_alpha_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_hint, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Sanity checks
if (fir_sensor == FIR_NONE) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
if (args[ARG_channel].u_int < -1 || args[ARG_channel].u_int > 2) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("RGB channel can be 0, 1, or 2"));
}
if (args[ARG_alpha].u_int < 0 || args[ARG_alpha].u_int > 255) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Alpha ranges between 0 and 255"));
}
image_t src_img = {
.w = fir_transposed ? fir_height : fir_width,
.h = fir_transposed ? fir_width : fir_height,
.pixfmt = PIXFORMAT_GRAYSCALE,
//.data is allocated later.
};
image_t *dst_img = py_helper_arg_to_image(pos_args[0], ARG_IMAGE_MUTABLE);
mp_obj_t *ir_array;
mp_obj_get_array_fixed_n(pos_args[1], src_img.w * src_img.h, &ir_array);
rectangle_t roi = py_helper_arg_to_roi(args[ARG_roi].u_obj, &src_img);
float x_scale = 1.0f;
float y_scale = 1.0f;
py_helper_arg_to_scale(args[ARG_x_scale].u_obj, args[ARG_y_scale].u_obj, &x_scale, &y_scale);
float min = FLT_MAX;
float max = -FLT_MAX;
py_helper_arg_to_minmax(args[ARG_scale].u_obj, &min, &max, ir_array, src_img.w * src_img.h);
const uint16_t *color_palette = py_helper_arg_to_palette(args[ARG_color_palette].u_obj, PIXFORMAT_RGB565);
const uint8_t *alpha_palette = py_helper_arg_to_palette(args[ARG_alpha_palette].u_obj, PIXFORMAT_GRAYSCALE);
fb_alloc_mark();
src_img.data = fb_alloc(src_img.w * src_img.h * sizeof(uint8_t), FB_ALLOC_NO_HINT);
fir_fill_image_float_obj(&src_img, ir_array, min, max);
imlib_draw_image(dst_img, &src_img, args[ARG_x].u_int, args[ARG_y].u_int, x_scale, y_scale, &roi,
args[ARG_channel].u_int, args[ARG_alpha].u_int, color_palette, alpha_palette,
args[ARG_hint].u_int, NULL, NULL, NULL, NULL);
fb_alloc_free_till_mark();
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_draw_ir_obj, 2, py_fir_draw_ir);
mp_obj_t py_fir_snapshot(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {
ARG_hmirror, ARG_vflip, ARG_transpose, ARG_x_scale, ARG_y_scale, ARG_roi, ARG_channel,
ARG_alpha, ARG_color_palette, ARG_alpha_palette, ARG_hint, ARG_scale, ARG_pixformat,
ARG_copy_to_fb, ARG_timeout
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_hmirror, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
{ MP_QSTR_vflip, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
{ MP_QSTR_transpose, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
{ MP_QSTR_x_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_y_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_roi, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_rgb_channel, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_alpha, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 255 } },
{ MP_QSTR_color_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_INT(COLOR_PALETTE_RAINBOW)} },
{ MP_QSTR_alpha_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_hint, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_pixformat, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = PIXFORMAT_RGB565 } },
{ MP_QSTR_copy_to_fb, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
{ MP_QSTR_timeout, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Sanity checks
if (args[ARG_channel].u_int < -1 || args[ARG_channel].u_int > 2) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("RGB channel can be 0, 1, or 2"));
}
if (args[ARG_alpha].u_int < 0 || args[ARG_alpha].u_int > 255) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Alpha ranges between 0 and 255"));
}
if ((args[ARG_pixformat].u_int != PIXFORMAT_GRAYSCALE) && (args[ARG_pixformat].u_int != PIXFORMAT_RGB565)) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid pixformat"));
}
image_t src_img = {
.w = args[ARG_transpose].u_bool ? fir_height : fir_width,
.h = args[ARG_transpose].u_bool ? fir_width : fir_height,
.pixfmt = PIXFORMAT_GRAYSCALE,
//.data is allocated later.
};
rectangle_t roi = py_helper_arg_to_roi(args[ARG_roi].u_obj, &src_img);
float x_scale = 1.0f;
float y_scale = 1.0f;
py_helper_arg_to_scale(args[ARG_x_scale].u_obj, args[ARG_y_scale].u_obj, &x_scale, &y_scale);
image_t dst_img = {
.w = fast_floorf(roi.w * x_scale),
.h = fast_floorf(roi.h * y_scale),
.pixfmt = args[ARG_pixformat].u_int,
};
if (args[ARG_copy_to_fb].u_bool) {
py_helper_set_to_framebuffer(&dst_img);
} else {
dst_img.data = m_malloc(image_size(&dst_img));
}
float min = FLT_MAX;
float max = -FLT_MAX;
py_helper_arg_to_minmax(args[ARG_scale].u_obj, &min, &max, NULL, 0);
const uint16_t *color_palette = py_helper_arg_to_palette(args[ARG_color_palette].u_obj, PIXFORMAT_RGB565);
const uint8_t *alpha_palette = py_helper_arg_to_palette(args[ARG_alpha_palette].u_obj, PIXFORMAT_GRAYSCALE);
fb_alloc_mark();
// Allocate source image data.
src_img.data = fb_alloc(src_img.w * src_img.h * sizeof(uint8_t), FB_ALLOC_NO_HINT);
switch (fir_sensor) {
#if (OMV_FIR_MLX90621_ENABLE == 1)
case FIR_MLX90621: {
float Ta, *To = fb_alloc(MLX90621_WIDTH * MLX90621_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_MLX90621_get_frame(&Ta, To);
if (args[ARG_scale].u_obj == mp_const_none) {
fast_get_min_max(To, MLX90621_WIDTH * MLX90621_HEIGHT, &min, &max);
}
imlib_fill_image_from_float(&src_img, MLX90621_WIDTH, MLX90621_HEIGHT, To, min, max, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, true);
break;
}
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
case FIR_MLX90640: {
float Ta, *To = fb_alloc(MLX90640_WIDTH * MLX90640_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_MLX90640_get_frame(&Ta, To);
if (args[ARG_scale].u_obj == mp_const_none) {
fast_get_min_max(To, MLX90640_WIDTH * MLX90640_HEIGHT, &min, &max);
}
imlib_fill_image_from_float(&src_img, MLX90640_WIDTH, MLX90640_HEIGHT, To, min, max, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, false);
break;
}
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
case FIR_MLX90641: {
float Ta, *To = fb_alloc(MLX90641_WIDTH * MLX90641_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_MLX90641_get_frame(&Ta, To);
if (args[ARG_scale].u_obj == mp_const_none) {
fast_get_min_max(To, MLX90641_WIDTH * MLX90641_HEIGHT, &min, &max);
}
imlib_fill_image_from_float(&src_img, MLX90641_WIDTH, MLX90641_HEIGHT, To, min, max, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, false);
break;
}
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
case FIR_AMG8833: {
float Ta, *To = fb_alloc(AMG8833_WIDTH * AMG8833_HEIGHT * sizeof(float), FB_ALLOC_NO_HINT);
fir_AMG8833_get_frame(&Ta, To);
if (args[ARG_scale].u_obj == mp_const_none) {
fast_get_min_max(To, AMG8833_WIDTH * AMG8833_HEIGHT, &min, &max);
}
imlib_fill_image_from_float(&src_img, AMG8833_WIDTH, AMG8833_HEIGHT, To, min, max, !args[ARG_hmirror].u_bool,
args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, true);
break;
}
#endif
default: {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized"));
}
}
imlib_draw_image(&dst_img, &src_img, 0, 0, x_scale, y_scale, &roi,
args[ARG_channel].u_int, args[ARG_alpha].u_int, color_palette, alpha_palette,
(args[ARG_hint].u_int & (~IMAGE_HINT_CENTER)) | IMAGE_HINT_BLACK_BACKGROUND,
NULL, NULL, NULL, NULL);
fb_alloc_free_till_mark();
if (args[ARG_copy_to_fb].u_bool) {
framebuffer_update_preview(&dst_img);
}
return py_image_from_struct(&dst_img);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_snapshot_obj, 0, py_fir_snapshot);
static const mp_rom_map_elem_t globals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fir) },
#if (OMV_FIR_MLX90621_ENABLE == 1)
{ MP_ROM_QSTR(MP_QSTR_FIR_SHIELD), MP_ROM_INT(FIR_MLX90621) },
{ MP_ROM_QSTR(MP_QSTR_FIR_MLX90621), MP_ROM_INT(FIR_MLX90621) },
#endif
#if (OMV_FIR_MLX90640_ENABLE == 1)
{ MP_ROM_QSTR(MP_QSTR_FIR_MLX90640), MP_ROM_INT(FIR_MLX90640) },
#endif
#if (OMV_FIR_MLX90641_ENABLE == 1)
{ MP_ROM_QSTR(MP_QSTR_FIR_MLX90641), MP_ROM_INT(FIR_MLX90641) },
#endif
#if (OMV_FIR_AMG8833_ENABLE == 1)
{ MP_ROM_QSTR(MP_QSTR_FIR_AMG8833), MP_ROM_INT(FIR_AMG8833) },
#endif
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&py_fir_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&py_fir_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&py_fir_type_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&py_fir_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&py_fir_height_obj) },
{ MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&py_fir_refresh_obj) },
{ MP_ROM_QSTR(MP_QSTR_resolution), MP_ROM_PTR(&py_fir_resolution_obj) },
{ MP_ROM_QSTR(MP_QSTR_read_ta), MP_ROM_PTR(&py_fir_read_ta_obj) },
{ MP_ROM_QSTR(MP_QSTR_read_ir), MP_ROM_PTR(&py_fir_read_ir_obj) },
{ MP_ROM_QSTR(MP_QSTR_draw_ir), MP_ROM_PTR(&py_fir_draw_ir_obj) },
{ MP_ROM_QSTR(MP_QSTR_snapshot), MP_ROM_PTR(&py_fir_snapshot_obj) }
};
static MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table);
const mp_obj_module_t fir_module = {
.base = { &mp_type_module },
.globals = (mp_obj_t) &globals_dict,
};
void py_fir_init0() {
py_fir_deinit();
}
#if ((OMV_FIR_MLX90621_ENABLE == 1) || (OMV_FIR_MLX90640_ENABLE == 1) || (OMV_FIR_MLX90641_ENABLE == 1))
MP_REGISTER_ROOT_POINTER(void *fir_mlx_data);
#endif
MP_REGISTER_MODULE(MP_QSTR_fir, fir_module);
#endif