/* * This file is part of the OpenMV project. * * Copyright (c) 2013-2021 Ibrahim Abdelkader * Copyright (c) 2013-2021 Kwabena W. Agyeman * * This work is licensed under the MIT license, see the file LICENSE for details. * * FIR Python module. */ #include "py/runtime.h" #include "py/objlist.h" #include "omv_boardconfig.h" #include "cambus.h" #if (OMV_ENABLE_FIR_MLX90621 == 1) #include "MLX90621_API.h" #include "MLX90621_I2C_Driver.h" #endif #if (OMV_ENABLE_FIR_MLX90640 == 1) #include "MLX90640_API.h" #include "MLX90640_I2C_Driver.h" #endif #if (OMV_ENABLE_FIR_MLX90641 == 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" #if (OMV_ENABLE_FIR_LEPTON == 1) #include "py_fir_lepton.h" #endif #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 cambus_t fir_bus = {}; #if ((OMV_ENABLE_FIR_MLX90621 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90641 == 1)) static void *fir_mlx_data = NULL; #endif static enum { FIR_NONE, #if (OMV_ENABLE_FIR_MLX90621 == 1) FIR_MLX90621, #endif #if (OMV_ENABLE_FIR_MLX90640 == 1) FIR_MLX90640, #endif #if (OMV_ENABLE_FIR_MLX90641 == 1) FIR_MLX90641, #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) FIR_AMG8833, #endif #if (OMV_ENABLE_FIR_LEPTON == 1) FIR_LEPTON #endif } fir_sensor = FIR_NONE; 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; // img->w == data_w && img->h == data_h && img->bpp == IMAGE_BPP_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_ENABLE_FIR_MLX90621 == 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, fir_mlx_data); MLX90621_CalculateTo(data, fir_mlx_data, 0.95f, *Ta - 8, To); fb_free(); } #endif #if (OMV_ENABLE_FIR_MLX90640 == 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); // 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, fir_mlx_data); MLX90640_CalculateTo(data, 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, fir_mlx_data); MLX90640_CalculateTo(data, fir_mlx_data, 0.95f, *Ta - 8, To); fb_free(); } #endif #if (OMV_ENABLE_FIR_MLX90641 == 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); PY_ASSERT_TRUE_MSG(MLX90641_GetFrameData(MLX90641_ADDR, data) >= 0, "Failed to read the MLX90641 sensor data!"); *Ta = MLX90641_GetTa(data, fir_mlx_data); MLX90641_CalculateTo(data, fir_mlx_data, 0.95f, *Ta - 8, To); fb_free(); } #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) static void fir_AMG8833_get_frame(float *Ta, float *To) { int16_t temp; int error = 0; error |= cambus_write_bytes(&fir_bus, AMG8833_ADDR, (uint8_t [1]){AMG8833_THERMISTOR_REGISTER}, 1, CAMBUS_XFER_NO_STOP); error |= cambus_read_bytes(&fir_bus, AMG8833_ADDR, (uint8_t *) &temp, sizeof(temp), CAMBUS_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 |= cambus_write_bytes(&fir_bus, AMG8833_ADDR, (uint8_t [1]){AMG8833_TEMPERATURE_REGISTER}, 1, CAMBUS_XFER_NO_STOP); error |= cambus_read_bytes(&fir_bus, AMG8833_ADDR, (uint8_t *) data, AMG8833_WIDTH * AMG8833_HEIGHT * 2, CAMBUS_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; int w_1 = w - 1; int h_1 = h - 1; if (!src_transpose) { for (int y = 0; y < h; y++) { int y_dst = flip ? (h_1 - y) : 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_1 - x) : 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_1 - x) : 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_1 - y) : 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 (OMV_ENABLE_FIR_LEPTON == 1) if (fir_sensor == FIR_LEPTON) { fir_lepton_deinit(); } #endif if (fir_sensor != FIR_NONE) { cambus_deinit(&fir_bus); fir_sensor = FIR_NONE; } #if ((OMV_ENABLE_FIR_MLX90621 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90641 == 1)) if (fir_mlx_data != NULL) { xfree(fir_mlx_data); 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(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { py_fir_deinit(); bool first_init = true; int type = py_helper_keyword_int(n_args, args, 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_type), -1); if (type == -1) { FIR_SCAN_RETRY: cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_SPEED_STANDARD); switch (cambus_scan(&fir_bus)) { #if (OMV_ENABLE_FIR_MLX90621 == 1) case (MLX90621_ADDR << 1): { type = FIR_MLX90621; break; } #endif #if (OMV_ENABLE_FIR_MLX90640 == 1) case (MLX90640_ADDR << 1): { type = FIR_MLX90640; break; } #endif #if (OMV_ENABLE_FIR_MLX90640 == 0) && (OMV_ENABLE_FIR_MLX90641 == 1) case (MLX90641_ADDR << 1): { type = FIR_MLX90641; break; } #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case AMG8833_ADDR: { type = FIR_AMG8833; break; } #endif #if (OMV_ENABLE_FIR_LEPTON == 1) case LEPTON_ADDR: { type = FIR_LEPTON; break; } #endif default: { if (first_init) { first_init = false; // Try to recover the bus once as it may be stuck from an interrupted run. // This is mostly needed for the AMG8833 cambus_pulse_scl(&fir_bus); goto FIR_SCAN_RETRY; } else { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to detect any FIR sensor")); } } } cambus_deinit(&fir_bus); } // Initialize the detected sensor. first_init = true; switch (type) { case FIR_NONE: { return mp_const_none; } #if (OMV_ENABLE_FIR_MLX90621 == 1) case FIR_MLX90621: { // parse refresh rate and ADC resolution int ir_fresh_rate = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_refresh), 64); int adc_resolution = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_resolution), 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; fir_mlx_data = xalloc(sizeof(paramsMLX90621)); fir_sensor = FIR_MLX90621; FIR_MLX90621_RETRY: cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_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, fir_mlx_data); fb_alloc_free_till_mark(); if (error != 0) { if (first_init) { first_init = false; cambus_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 cambus_deinit(&fir_bus); cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_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_ENABLE_FIR_MLX90640 == 1) case FIR_MLX90640: { // parse refresh rate and ADC resolution int ir_fresh_rate = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_refresh), 32); int adc_resolution = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_resolution), 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; fir_mlx_data = xalloc(sizeof(paramsMLX90640)); fir_sensor = FIR_MLX90640; FIR_MLX90640_RETRY: cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_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, fir_mlx_data); fb_alloc_free_till_mark(); if (error != 0) { if (first_init) { first_init = false; cambus_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 cambus_deinit(&fir_bus); cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_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_ENABLE_FIR_MLX90641 == 1) case FIR_MLX90641: { // parse refresh rate and ADC resolution int ir_fresh_rate = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_refresh), 32); int adc_resolution = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_resolution), 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; fir_mlx_data = xalloc(sizeof(paramsMLX90641)); fir_sensor = FIR_MLX90641; FIR_MLX90641_RETRY: cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_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, fir_mlx_data); fb_alloc_free_till_mark(); if (error != 0) { if (first_init) { first_init = false; cambus_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 cambus_deinit(&fir_bus); cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_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_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: { fir_sensor = FIR_AMG8833; FIR_AMG8833_RETRY: cambus_init(&fir_bus, FIR_I2C_ID, CAMBUS_SPEED_STANDARD); int error = 0; error |= cambus_write_bytes(&fir_bus, AMG8833_ADDR, (uint8_t [1]){AMG8833_RESET_REGISTER}, 1, CAMBUS_XFER_SUSPEND); error |= cambus_write_bytes(&fir_bus, AMG8833_ADDR, (uint8_t [1]){AMG8833_INITIAL_RESET_VALUE}, 1, CAMBUS_XFER_NO_FLAGS); if (error != 0) { if (first_init) { first_init = false; cambus_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 #if (OMV_ENABLE_FIR_LEPTON == 1) case FIR_LEPTON: { fir_sensor = FIR_LEPTON; FIR_LEPTON_RETRY: cambus_init(&fir_bus, OMV_FIR_LEPTON_I2C_BUS, OMV_FIR_LEPTON_I2C_BUS_SPEED); int error = fir_lepton_init(&fir_bus, &fir_width, &fir_height, &fir_ir_fresh_rate, &fir_adc_resolution); if (error != 0) { if (first_init) { first_init = false; cambus_pulse_scl(&fir_bus); goto FIR_LEPTON_RETRY; } else { py_fir_deinit(); mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to init the Lepton!")); } } return mp_const_none; } #endif default: { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid sensor type!")); } } 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_const_none; return mp_obj_new_int(fir_sensor); } 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_const_none; return mp_obj_new_int(fir_width); } 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_const_none; return mp_obj_new_int(fir_height); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_height_obj, py_fir_height); static mp_obj_t py_fir_refresh() { #if (OMV_ENABLE_FIR_MLX90621 == 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_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90641 == 1) const int mlx_90640_1_refresh_rates[8] = {0, 1, 2, 4, 8, 16, 32, 64}; #endif switch (fir_sensor) { case FIR_NONE: return mp_const_none; #if (OMV_ENABLE_FIR_MLX90621 == 1) case FIR_MLX90621: return mp_obj_new_int(mlx_90621_refresh_rates[fir_ir_fresh_rate]); #endif #if (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1) #if (OMV_ENABLE_FIR_MLX90640 == 1) case FIR_MLX90640: #endif #if (OMV_ENABLE_FIR_MLX90641 == 1) case FIR_MLX90641: #endif return mp_obj_new_int(mlx_90640_1_refresh_rates[fir_ir_fresh_rate]); #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: return mp_obj_new_int(fir_ir_fresh_rate); #endif #if (OMV_ENABLE_FIR_LEPTON == 1) case FIR_LEPTON: return mp_obj_new_int(fir_ir_fresh_rate); #endif default: return mp_const_none; } } STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_refresh_obj, py_fir_refresh); static mp_obj_t py_fir_resolution() { switch (fir_sensor) { case FIR_NONE: return mp_const_none; #if (OMV_ENABLE_FIR_MLX90621 == 1) case FIR_MLX90621: return mp_obj_new_int(fir_adc_resolution + 15); #endif #if (OMV_ENABLE_FIR_MLX90640 == 1) || (OMV_ENABLE_FIR_MLX90640 == 1) #if (OMV_ENABLE_FIR_MLX90640 == 1) case FIR_MLX90640: #endif #if (OMV_ENABLE_FIR_MLX90641 == 1) case FIR_MLX90641: #endif return mp_obj_new_int(fir_adc_resolution + 16); #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: return mp_obj_new_int(fir_adc_resolution); #endif #if (OMV_ENABLE_FIR_LEPTON == 1) case FIR_LEPTON: return mp_obj_new_int(fir_adc_resolution); #endif default: return mp_const_none; } } STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_resolution_obj, py_fir_resolution); #if (OMV_ENABLE_FIR_LEPTON == 1) static mp_obj_t py_fir_radiometric() { if (fir_sensor == FIR_LEPTON) { return fir_lepton_get_radiometry(); } return mp_const_true; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_radiometric_obj, py_fir_radiometric); #if defined(OMV_FIR_LEPTON_VSYNC_PRESENT) static mp_obj_t py_fir_register_vsync_cb(mp_obj_t cb) { if (fir_sensor == FIR_LEPTON) { fir_lepton_register_vsync_cb(cb); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_fir_register_vsync_cb_obj, py_fir_register_vsync_cb); #endif static mp_obj_t py_fir_register_frame_cb(mp_obj_t cb) { if (fir_sensor == FIR_LEPTON) { fir_lepton_register_frame_cb(cb); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_fir_register_frame_cb_obj, py_fir_register_frame_cb); static mp_obj_t py_fir_get_frame_available() { if (fir_sensor == FIR_LEPTON) { return fir_lepton_get_frame_available(); } return mp_const_true; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_fir_get_frame_available_obj, py_fir_get_frame_available); static mp_obj_t py_fir_trigger_ffc(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { if (fir_sensor == FIR_LEPTON) { fir_lepton_trigger_ffc(n_args, args, kw_args); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_fir_trigger_ffc_obj, 0, py_fir_trigger_ffc); #endif mp_obj_t py_fir_read_ta() { switch(fir_sensor) { case FIR_NONE: { return mp_const_none; } #if (OMV_ENABLE_FIR_MLX90621 == 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, fir_mlx_data)); fb_alloc_free_till_mark(); return result; } #endif #if (OMV_ENABLE_FIR_MLX90640 == 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, fir_mlx_data)); fb_alloc_free_till_mark(); return result; } #endif #if (OMV_ENABLE_FIR_MLX90641 == 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, fir_mlx_data)); fb_alloc_free_till_mark(); return result; } #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) case FIR_AMG8833: { int16_t temp; int error = 0; error |= cambus_write_bytes(&fir_bus, AMG8833_ADDR, (uint8_t [1]){AMG8833_THERMISTOR_REGISTER}, 1, CAMBUS_XFER_NO_STOP); error |= cambus_read_bytes(&fir_bus, AMG8833_ADDR, (uint8_t *) &temp, sizeof(temp), CAMBUS_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 #if (OMV_ENABLE_FIR_LEPTON == 1) case FIR_LEPTON: { return fir_lepton_read_ta(); } #endif } 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(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { bool arg_hmirror = py_helper_keyword_int(n_args, args, 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_hmirror), false); bool arg_vflip = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_vflip), false); fir_transposed = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_transpose), false); #if (OMV_ENABLE_FIR_LEPTON == 1) int arg_timeout = py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_timeout), -1); #endif switch(fir_sensor) { case FIR_NONE: { return mp_const_none; } #if (OMV_ENABLE_FIR_MLX90621 == 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, arg_hmirror ^ true, arg_vflip, fir_transposed, true); fb_alloc_free_till_mark(); return result; } #endif #if (OMV_ENABLE_FIR_MLX90640 == 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, arg_hmirror ^ true, arg_vflip, fir_transposed, false); fb_alloc_free_till_mark(); return result; } #endif #if (OMV_ENABLE_FIR_MLX90641 == 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, arg_hmirror ^ true, arg_vflip, fir_transposed, false); fb_alloc_free_till_mark(); return result; } #endif #if (OMV_ENABLE_FIR_AMG8833 == 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, arg_hmirror ^ true, arg_vflip, fir_transposed, true); fb_alloc_free_till_mark(); return result; } #endif #if (OMV_ENABLE_FIR_LEPTON == 1) case FIR_LEPTON: { return fir_lepton_read_ir(fir_width, fir_height, arg_hmirror, arg_vflip, fir_transposed, arg_timeout); } #endif } 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(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { image_t *dst_img = py_helper_arg_to_image_mutable(args[0]); image_t src_img; src_img.bpp = IMAGE_BPP_GRAYSCALE; size_t len; mp_obj_t *items, *arg_to; mp_obj_get_array(args[1], &len, &items); if (len == 3) { src_img.w = mp_obj_get_int(items[0]); src_img.h = mp_obj_get_int(items[1]); mp_obj_get_array_fixed_n(items[2], src_img.w * src_img.h, &arg_to); } else if (fir_sensor != FIR_NONE) { src_img.w = fir_transposed ? fir_height : fir_width; src_img.h = fir_transposed ? fir_width : fir_height; // Handle if the user passed an array of the array. if (len == 1) { mp_obj_get_array_fixed_n(*items, src_img.w * src_img.h, &arg_to); } else { mp_obj_get_array_fixed_n(args[1], src_img.w * src_img.h, &arg_to); } } else { mp_raise_msg(&mp_type_TypeError, MP_ERROR_TEXT("Invalid IR array!")); } int arg_x_off = 0; int arg_y_off = 0; uint offset = 2; if (n_args > 2) { if (MP_OBJ_IS_TYPE(args[2], &mp_type_tuple) || MP_OBJ_IS_TYPE(args[2], &mp_type_list)) { mp_obj_t *arg_vec; mp_obj_get_array_fixed_n(args[2], 2, &arg_vec); arg_x_off = mp_obj_get_int(arg_vec[0]); arg_y_off = mp_obj_get_int(arg_vec[1]); offset = 3; } else if (n_args > 3) { arg_x_off = mp_obj_get_int(args[2]); arg_y_off = mp_obj_get_int(args[3]); offset = 4; } else if (n_args > 2) { mp_raise_msg(&mp_type_TypeError, MP_ERROR_TEXT("Expected x and y offset!")); } } float arg_x_scale = 1.f; bool got_x_scale = py_helper_keyword_float_maybe(n_args, args, offset + 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_scale), &arg_x_scale); float arg_y_scale = 1.f; bool got_y_scale = py_helper_keyword_float_maybe(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_scale), &arg_y_scale); rectangle_t arg_roi; py_helper_keyword_rectangle_roi(&src_img, n_args, args, offset + 2, kw_args, &arg_roi); float tmp_x_scale = dst_img->w / ((float) arg_roi.w); float tmp_y_scale = dst_img->h / ((float) arg_roi.h); float tmp_scale = IM_MIN(tmp_x_scale, tmp_y_scale); if (n_args == 2) { arg_x_off = fast_floorf((dst_img->w - (arg_roi.w * tmp_scale)) / 2.f); arg_y_off = fast_floorf((dst_img->h - (arg_roi.h * tmp_scale)) / 2.f); } if (!got_x_scale) { arg_x_scale = tmp_scale; } if (!got_y_scale) { arg_y_scale = tmp_scale; } int arg_rgb_channel = py_helper_keyword_int(n_args, args, offset + 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1); if ((arg_rgb_channel < -1) || (2 < arg_rgb_channel)) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("-1 <= rgb_channel <= 2!")); } int arg_alpha = py_helper_keyword_int(n_args, args, offset + 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 128); if ((arg_alpha < 0) || (256 < arg_alpha)) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("0 <= alpha <= 256!")); } const uint16_t *color_palette = py_helper_keyword_color_palette(n_args, args, offset + 5, kw_args, rainbow_table); const uint8_t *alpha_palette = py_helper_keyword_alpha_palette(n_args, args, offset + 6, kw_args, NULL); image_hint_t hint = py_helper_keyword_int(n_args, args, offset + 7, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_hint), 0); int arg_x_size; bool got_x_size = py_helper_keyword_int_maybe(n_args, args, offset + 8, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_size), &arg_x_size); int arg_y_size; bool got_y_size = py_helper_keyword_int_maybe(n_args, args, offset + 9, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_size), &arg_y_size); if (got_x_scale && got_x_size) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either x_scale or x_size not both!")); } if (got_y_scale && got_y_size) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either y_scale or y_size not both!")); } if (got_x_size) { arg_x_scale = arg_x_size / ((float) arg_roi.w); } if (got_y_size) { arg_y_scale = arg_y_size / ((float) arg_roi.h); } if ((!got_x_scale) && (!got_x_size) && got_y_size) { arg_x_scale = arg_y_scale; } if ((!got_y_scale) && (!got_y_size) && got_x_size) { arg_y_scale = arg_x_scale; } mp_obj_t scale_obj = py_helper_keyword_object(n_args, args, offset + 10, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_scale), NULL); float min = FLT_MAX, max = -FLT_MAX; if (scale_obj) { mp_obj_t *arg_scale; mp_obj_get_array_fixed_n(scale_obj, 2, &arg_scale); min = mp_obj_get_float(arg_scale[0]); max = mp_obj_get_float(arg_scale[1]); } else { for (int i = 0, ii = src_img.w * src_img.h; i < ii; i++) { float temp = mp_obj_get_float(arg_to[i]); if (temp < min) { min = temp; } if (temp > max) { max = temp; } } } 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, arg_to, min, max); imlib_draw_image(dst_img, &src_img, arg_x_off, arg_y_off, arg_x_scale, arg_y_scale, &arg_roi, arg_rgb_channel, arg_alpha, color_palette, alpha_palette, hint, 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(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { if (fir_sensor == FIR_NONE) return mp_const_none; bool arg_hmirror = py_helper_keyword_int(n_args, args, 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_hmirror), false); bool arg_vflip = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_vflip), false); bool arg_transpose = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_transpose), false); image_t src_img; src_img.bpp = IMAGE_BPP_GRAYSCALE; src_img.w = arg_transpose ? fir_height : fir_width; src_img.h = arg_transpose ? fir_width : fir_height; float arg_x_scale = 1.f; bool got_x_scale = py_helper_keyword_float_maybe(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_scale), &arg_x_scale); float arg_y_scale = 1.f; bool got_y_scale = py_helper_keyword_float_maybe(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_scale), &arg_y_scale); rectangle_t arg_roi; py_helper_keyword_rectangle_roi(&src_img, n_args, args, 5, kw_args, &arg_roi); int arg_rgb_channel = py_helper_keyword_int(n_args, args, 6, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1); if ((arg_rgb_channel < -1) || (2 < arg_rgb_channel)) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("-1 <= rgb_channel <= 2!")); } int arg_alpha = py_helper_keyword_int(n_args, args, 7, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 128); if ((arg_alpha < 0) || (256 < arg_alpha)) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("0 <= alpha <= 256!")); } const uint16_t *color_palette = py_helper_keyword_color_palette(n_args, args, 8, kw_args, rainbow_table); const uint8_t *alpha_palette = py_helper_keyword_alpha_palette(n_args, args, 9, kw_args, NULL); image_hint_t hint = py_helper_keyword_int(n_args, args, 10, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_hint), 0); int arg_x_size; bool got_x_size = py_helper_keyword_int_maybe(n_args, args, 11, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_size), &arg_x_size); int arg_y_size; bool got_y_size = py_helper_keyword_int_maybe(n_args, args, 12, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_size), &arg_y_size); if (got_x_scale && got_x_size) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either x_scale or x_size not both!")); } if (got_y_scale && got_y_size) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either y_scale or y_size not both!")); } if (got_x_size) { arg_x_scale = arg_x_size / ((float) arg_roi.w); } if (got_y_size) { arg_y_scale = arg_y_size / ((float) arg_roi.h); } if ((!got_x_scale) && (!got_x_size) && got_y_size) { arg_x_scale = arg_y_scale; } if ((!got_y_scale) && (!got_y_size) && got_x_size) { arg_y_scale = arg_x_scale; } mp_obj_t scale_obj = py_helper_keyword_object(n_args, args, 13, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_scale), NULL); float min, max; if (scale_obj) { mp_obj_t *arg_scale; mp_obj_get_array_fixed_n(scale_obj, 2, &arg_scale); min = mp_obj_get_float(arg_scale[0]); max = mp_obj_get_float(arg_scale[1]); } int arg_pixformat = py_helper_keyword_int(n_args, args, 14, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_pixformat), PIXFORMAT_RGB565); if ((arg_pixformat != PIXFORMAT_GRAYSCALE) && (arg_pixformat != PIXFORMAT_RGB565)) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid pixformat!")); } mp_obj_t copy_to_fb_obj = py_helper_keyword_object(n_args, args, 15, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy_to_fb), NULL); bool copy_to_fb = false; image_t *arg_other = NULL; if (copy_to_fb_obj) { if (mp_obj_is_integer(copy_to_fb_obj)) { copy_to_fb = mp_obj_get_int(copy_to_fb_obj); } else { arg_other = py_helper_arg_to_image_mutable_bayer(copy_to_fb_obj); } } if (copy_to_fb) { fb_update_jpeg_buffer(); } image_t dst_img; dst_img.w = fast_floorf(arg_roi.w * arg_x_scale); dst_img.h = fast_floorf(arg_roi.h * arg_y_scale); dst_img.bpp = (arg_pixformat == PIXFORMAT_RGB565) ? IMAGE_BPP_RGB565 : IMAGE_BPP_GRAYSCALE; if (copy_to_fb) { py_helper_set_to_framebuffer(&dst_img); } else if (arg_other) { bool fb = py_helper_is_equal_to_framebuffer(arg_other); size_t size = fb ? framebuffer_get_buffer_size() : image_size(arg_other); PY_ASSERT_TRUE_MSG((image_size(&dst_img) <= size), "The new image won't fit in the target frame buffer!"); arg_other->w = dst_img.w; arg_other->h = dst_img.h; arg_other->bpp = dst_img.bpp; dst_img.data = arg_other->data; py_helper_update_framebuffer(&dst_img); } else { dst_img.data = xalloc(image_size(&dst_img)); } #if (OMV_ENABLE_FIR_LEPTON == 1) int arg_timeout = py_helper_keyword_int(n_args, args, 16, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_timeout), -1); #endif fb_alloc_mark(); src_img.data = fb_alloc(src_img.w * src_img.h * sizeof(uint8_t), FB_ALLOC_NO_HINT); switch(fir_sensor) { #if (OMV_ENABLE_FIR_MLX90621 == 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 (!scale_obj) { 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, arg_hmirror ^ true, arg_vflip, arg_transpose, true); fb_free(); break; } #endif #if (OMV_ENABLE_FIR_MLX90640 == 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 (!scale_obj) { 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, arg_hmirror ^ true, arg_vflip, arg_transpose, false); fb_free(); break; } #endif #if (OMV_ENABLE_FIR_MLX90641 == 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 (!scale_obj) { 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, arg_hmirror ^ true, arg_vflip, arg_transpose, false); fb_free(); break; } #endif #if (OMV_ENABLE_FIR_AMG8833 == 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 (!scale_obj) { 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, arg_hmirror ^ true, arg_vflip, arg_transpose, true); fb_free(); break; } #endif #if (OMV_ENABLE_FIR_LEPTON == 1) case FIR_LEPTON: { fir_lepton_fill_image(&src_img, fir_width, fir_height, !scale_obj, min, max, arg_hmirror, arg_vflip, arg_transpose, arg_timeout); break; } #endif default: { break; } } imlib_draw_image(&dst_img, &src_img, 0, 0, arg_x_scale, arg_y_scale, &arg_roi, arg_rgb_channel, arg_alpha, color_palette, alpha_palette, (hint & (~IMAGE_HINT_CENTER)) | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL); fb_alloc_free_till_mark(); 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) }, { MP_ROM_QSTR(MP_QSTR_FIR_NONE), MP_ROM_INT(FIR_NONE) }, #if (OMV_ENABLE_FIR_MLX90621 == 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_ENABLE_FIR_MLX90640 == 1) { MP_ROM_QSTR(MP_QSTR_FIR_MLX90640), MP_ROM_INT(FIR_MLX90640) }, #endif #if (OMV_ENABLE_FIR_MLX90641 == 1) { MP_ROM_QSTR(MP_QSTR_FIR_MLX90641), MP_ROM_INT(FIR_MLX90641) }, #endif #if (OMV_ENABLE_FIR_AMG8833 == 1) { MP_ROM_QSTR(MP_QSTR_FIR_AMG8833), MP_ROM_INT(FIR_AMG8833) }, #endif #if (OMV_ENABLE_FIR_LEPTON == 1) { MP_ROM_QSTR(MP_QSTR_FIR_LEPTON), MP_ROM_INT(FIR_LEPTON) }, #endif { MP_ROM_QSTR(MP_QSTR_PALETTE_RAINBOW), MP_ROM_INT(COLOR_PALETTE_RAINBOW) }, { MP_ROM_QSTR(MP_QSTR_PALETTE_IRONBOW), MP_ROM_INT(COLOR_PALETTE_IRONBOW) }, { MP_ROM_QSTR(MP_QSTR_GRAYSCALE), MP_ROM_INT(PIXFORMAT_GRAYSCALE) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(PIXFORMAT_RGB565) }, { 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) }, #if (OMV_ENABLE_FIR_LEPTON == 1) { MP_ROM_QSTR(MP_QSTR_radiometric), MP_ROM_PTR(&py_fir_radiometric_obj) }, #if defined(OMV_FIR_LEPTON_VSYNC_PRESENT) { MP_ROM_QSTR(MP_QSTR_register_vsync_cb), MP_ROM_PTR(&py_fir_register_vsync_cb_obj) }, #else { MP_ROM_QSTR(MP_QSTR_register_vsync_cb), MP_ROM_PTR(&py_func_unavailable_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_register_frame_cb), MP_ROM_PTR(&py_fir_register_frame_cb_obj) }, { MP_ROM_QSTR(MP_QSTR_get_frame_available), MP_ROM_PTR(&py_fir_get_frame_available_obj) }, { MP_ROM_QSTR(MP_QSTR_trigger_ffc), MP_ROM_PTR(&py_fir_trigger_ffc_obj) }, #else { MP_ROM_QSTR(MP_QSTR_radiometric), MP_ROM_PTR(&py_func_unavailable_obj) }, { MP_ROM_QSTR(MP_QSTR_register_vsync_cb), MP_ROM_PTR(&py_func_unavailable_obj) }, { MP_ROM_QSTR(MP_QSTR_register_frame_cb), MP_ROM_PTR(&py_func_unavailable_obj) }, { MP_ROM_QSTR(MP_QSTR_get_frame_available), MP_ROM_PTR(&py_func_unavailable_obj) }, { MP_ROM_QSTR(MP_QSTR_trigger_ffc), MP_ROM_PTR(&py_func_unavailable_obj) }, #endif { 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(); }