From be72f87345c38183f0949400099ed59cc007c311 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Mon, 21 Jul 2025 23:10:03 -0700 Subject: [PATCH 1/6] common: Add polarity config override. --- common/omv_csi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/omv_csi.c b/common/omv_csi.c index e5fd7f49c..e116ff511 100644 --- a/common/omv_csi.c +++ b/common/omv_csi.c @@ -464,10 +464,14 @@ int omv_csi_probe(omv_i2c_t *i2c) { // Active power-down state, active reset state // This order is required for all sensors to work correctly. const omv_csi_polarity_t polarity_configs[][2] = { + #if defined(OMV_CSI_POLARITY_CONFIG) + OMV_CSI_POLARITY_CONFIG + #else { OMV_CSI_ACTIVE_HIGH, OMV_CSI_ACTIVE_HIGH }, { OMV_CSI_ACTIVE_HIGH, OMV_CSI_ACTIVE_LOW }, { OMV_CSI_ACTIVE_LOW, OMV_CSI_ACTIVE_HIGH }, { OMV_CSI_ACTIVE_LOW, OMV_CSI_ACTIVE_LOW }, + #endif // OMV_CSI_POLARITY_CONFIG }; // Scan the bus multiple times using different reset and power polarities, From bacfb7aeb71dbafd3af4b86b0210185b713b8fa6 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sun, 20 Jul 2025 15:03:15 -0700 Subject: [PATCH 2/6] modules/py_fir_lepton: Remove lepton control through fir module. --- boards/ARDUINO_NICLA_VISION/omv_boardconfig.h | 6 - boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h | 6 - boards/OPENMV2/omv_boardconfig.h | 6 - boards/OPENMV3/omv_boardconfig.h | 6 - boards/OPENMV4/omv_boardconfig.h | 6 - boards/OPENMV4P/omv_boardconfig.h | 6 - boards/OPENMVPT/omv_boardconfig.h | 22 - boards/OPENMV_AE3/omv_boardconfig.h | 1 - boards/OPENMV_N6/omv_boardconfig.h | 1 - boards/OPENMV_RT1060/omv_boardconfig.h | 6 - boards/RPI_PICO/omv_boardconfig.h | 1 - drivers/lepton/src/LEPTON_I2C_Service.c | 4 +- modules/py_fir.c | 154 +---- modules/py_fir_lepton.c | 562 ------------------ modules/py_fir_lepton.h | 40 -- ports/stm32/stm_hal.c | 10 - 16 files changed, 3 insertions(+), 834 deletions(-) delete mode 100644 modules/py_fir_lepton.c delete mode 100644 modules/py_fir_lepton.h diff --git a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h index 1913afde3..a0c8bb60d 100644 --- a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h +++ b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h @@ -51,7 +51,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 16 @@ -307,9 +306,4 @@ #define OMV_SPI_DISPLAY_RS_PIN (&omv_pin_G12_GPIO) #define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_G1_GPIO) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_FIR_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_FIR_I2C_SPEED) -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI4_ID) - #endif //__OMV_BOARDCONFIG_H__ diff --git a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h index dbb96825e..8fab74089 100644 --- a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h +++ b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h @@ -60,7 +60,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 256 @@ -345,9 +344,4 @@ #define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_C7_GPIO) #define OMV_SPI_DISPLAY_TRIPLE_BUFFER (1) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_FIR_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_FIR_I2C_SPEED) -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI2_ID) - #endif //__OMV_BOARDCONFIG_H__ diff --git a/boards/OPENMV2/omv_boardconfig.h b/boards/OPENMV2/omv_boardconfig.h index 032ab8078..50dfe7464 100644 --- a/boards/OPENMV2/omv_boardconfig.h +++ b/boards/OPENMV2/omv_boardconfig.h @@ -53,7 +53,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 16 @@ -202,9 +201,4 @@ #define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_D12_GPIO) #define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_FIR_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_FIR_I2C_SPEED) -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI2_ID) - #endif //__OMV_BOARDCONFIG_H__ diff --git a/boards/OPENMV3/omv_boardconfig.h b/boards/OPENMV3/omv_boardconfig.h index 128ff57fa..d870785ab 100644 --- a/boards/OPENMV3/omv_boardconfig.h +++ b/boards/OPENMV3/omv_boardconfig.h @@ -53,7 +53,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 16 @@ -202,9 +201,4 @@ #define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_D12_GPIO) #define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_FIR_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_FIR_I2C_SPEED) -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI2_ID) - #endif //__OMV_BOARDCONFIG_H__ diff --git a/boards/OPENMV4/omv_boardconfig.h b/boards/OPENMV4/omv_boardconfig.h index 51f439195..9a67b0219 100644 --- a/boards/OPENMV4/omv_boardconfig.h +++ b/boards/OPENMV4/omv_boardconfig.h @@ -67,7 +67,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 16 @@ -282,9 +281,4 @@ #define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_D12_GPIO) #define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_FIR_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_FIR_I2C_SPEED) -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI2_ID) - #endif //__OMV_BOARDCONFIG_H__ diff --git a/boards/OPENMV4P/omv_boardconfig.h b/boards/OPENMV4P/omv_boardconfig.h index f343f6699..a1196bece 100644 --- a/boards/OPENMV4P/omv_boardconfig.h +++ b/boards/OPENMV4P/omv_boardconfig.h @@ -54,7 +54,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 256 @@ -282,9 +281,4 @@ #define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO) #define OMV_SPI_DISPLAY_TRIPLE_BUFFER (1) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_FIR_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_FIR_I2C_SPEED) -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI2_ID) - #endif //__OMV_BOARDCONFIG_H__ diff --git a/boards/OPENMVPT/omv_boardconfig.h b/boards/OPENMVPT/omv_boardconfig.h index 7675bb406..495d371fb 100644 --- a/boards/OPENMVPT/omv_boardconfig.h +++ b/boards/OPENMVPT/omv_boardconfig.h @@ -38,7 +38,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 256 @@ -346,25 +345,4 @@ #define OMV_FT5X06_SDA_PIN (pin_J14) #define OMV_FT5X06_INT_PIN (&omv_pin_J6_GPIO) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_CSI_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_CSI_I2C_SPEED) - -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI3_ID) - -#define OMV_FIR_LEPTON_RESET_PIN (&omv_pin_D5_GPIO) -#define OMV_FIR_LEPTON_POWER_PIN (&omv_pin_D4_GPIO) -#define OMV_FIR_LEPTON_VSYNC_PIN (&omv_pin_E3_GPIO) - -#define OMV_FIR_LEPTON_MCLK_PIN (&omv_pin_A3_TIM15) -#define OMV_FIR_LEPTON_MCLK_FREQ (24000000) - -#define OMV_FIR_LEPTON_MCLK_TIM (TIM15) -#define OMV_FIR_LEPTON_MCLK_TIM_CHANNEL (TIM_CHANNEL_2) -#define OMV_FIR_LEPTON_MCLK_TIM_CLK_ENABLE() __HAL_RCC_TIM15_CLK_ENABLE() -#define OMV_FIR_LEPTON_MCLK_TIM_CLK_DISABLE() __HAL_RCC_TIM15_CLK_DISABLE() -#define OMV_FIR_LEPTON_MCLK_TIM_FORCE_RESET() __HAL_RCC_TIM15_FORCE_RESET() -#define OMV_FIR_LEPTON_MCLK_TIM_RELEASE_RESET() __HAL_RCC_TIM15_RELEASE_RESET() -#define OMV_FIR_LEPTON_MCLK_TIM_PCLK_FREQ() HAL_RCC_GetPCLK2Freq() - #endif //__OMV_BOARDCONFIG_H__ diff --git a/boards/OPENMV_AE3/omv_boardconfig.h b/boards/OPENMV_AE3/omv_boardconfig.h index d3e1c9f6b..edcbe13ea 100644 --- a/boards/OPENMV_AE3/omv_boardconfig.h +++ b/boards/OPENMV_AE3/omv_boardconfig.h @@ -66,7 +66,6 @@ extern unsigned char OMV_BOARD_UID_ADDR[12]; // Unique address. #define OMV_FIR_MLX90640_ENABLE (0) #define OMV_FIR_MLX90641_ENABLE (0) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (0) // Debugging configuration. #define OMV_TUSBDBG_ENABLE (CORE_M55_HP) diff --git a/boards/OPENMV_N6/omv_boardconfig.h b/boards/OPENMV_N6/omv_boardconfig.h index ad7dad9ff..3db00d6ba 100644 --- a/boards/OPENMV_N6/omv_boardconfig.h +++ b/boards/OPENMV_N6/omv_boardconfig.h @@ -57,7 +57,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (0) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 256 diff --git a/boards/OPENMV_RT1060/omv_boardconfig.h b/boards/OPENMV_RT1060/omv_boardconfig.h index 6b7f0c2b7..6f7e8c6c2 100644 --- a/boards/OPENMV_RT1060/omv_boardconfig.h +++ b/boards/OPENMV_RT1060/omv_boardconfig.h @@ -47,7 +47,6 @@ #define OMV_FIR_MLX90640_ENABLE (1) #define OMV_FIR_MLX90641_ENABLE (1) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (1) // Debugging configuration. #define OMV_TUSBDBG_ENABLE (1) @@ -182,11 +181,6 @@ #define OMV_SPI_DISPLAY_TRIPLE_BUFFER (0) #define OMV_SPI_DISPLAY_RX_CLK_DIV (8) -// FIR Lepton -#define OMV_FIR_LEPTON_I2C_BUS (OMV_FIR_I2C_ID) -#define OMV_FIR_LEPTON_I2C_BUS_SPEED (OMV_FIR_I2C_SPEED) -#define OMV_FIR_LEPTON_SPI_BUS (OMV_SPI3_ID) - // Camera interface configuration. #define OMV_CSI_BASE (CSI) #define OMV_CSI_DMA (DMA0) diff --git a/boards/RPI_PICO/omv_boardconfig.h b/boards/RPI_PICO/omv_boardconfig.h index 6cc6922e7..aaa8eef44 100644 --- a/boards/RPI_PICO/omv_boardconfig.h +++ b/boards/RPI_PICO/omv_boardconfig.h @@ -36,7 +36,6 @@ extern unsigned char *OMV_BOARD_UID_ADDR; // Unique ID address. #define OMV_FIR_MLX90640_ENABLE (0) #define OMV_FIR_MLX90641_ENABLE (0) #define OMV_FIR_AMG8833_ENABLE (1) -#define OMV_FIR_LEPTON_ENABLE (0) // UMM heap block size #define OMV_UMM_BLOCK_SIZE 16 diff --git a/drivers/lepton/src/LEPTON_I2C_Service.c b/drivers/lepton/src/LEPTON_I2C_Service.c index 88ce106de..bc3cb8f00 100755 --- a/drivers/lepton/src/LEPTON_I2C_Service.c +++ b/drivers/lepton/src/LEPTON_I2C_Service.c @@ -68,7 +68,7 @@ /** INCLUDE FILES **/ /******************************************************************************/ #include "omv_boardconfig.h" -#if ((OMV_LEPTON_ENABLE == 1) || (OMV_FIR_LEPTON_ENABLE == 1)) +#if (OMV_LEPTON_ENABLE == 1) #include "LEPTON_Types.h" #include "LEPTON_ErrorCodes.h" @@ -232,4 +232,4 @@ LEP_RESULT LEP_I2C_MasterStatus(omv_i2c_t *bus, return(result); } -#endif // ((OMV_LEPTON_ENABLE == 1) || (OMV_FIR_LEPTON_ENABLE == 1)) +#endif // (OMV_LEPTON_ENABLE == 1) diff --git a/modules/py_fir.c b/modules/py_fir.c index c1ef38110..4cd239af7 100644 --- a/modules/py_fir.c +++ b/modules/py_fir.c @@ -30,8 +30,7 @@ #if OMV_FIR_MLX90621_ENABLE || \ OMV_FIR_MLX90640_ENABLE || \ OMV_FIR_MLX90641_ENABLE || \ - OMV_FIR_AMG8833_ENABLE || \ - OMV_FIR_LEPTON_ENABLE + OMV_FIR_AMG8833_ENABLE #include "omv_i2c.h" #if (OMV_FIR_MLX90621_ENABLE == 1) #include "MLX90621_API.h" @@ -51,10 +50,6 @@ #include "py_helper.h" #include "py_image.h" -#if (OMV_FIR_LEPTON_ENABLE == 1) -#include "py_fir_lepton.h" -#endif - #define MLX90621_ADDR 0x50 #define MLX90621_WIDTH 16 #define MLX90621_HEIGHT 4 @@ -108,9 +103,6 @@ typedef enum fir_sensor_type { #if (OMV_FIR_AMG8833_ENABLE == 1) FIR_AMG8833, #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - FIR_LEPTON - #endif } fir_sensor_type_t; static int fir_width = 0; @@ -308,12 +300,6 @@ static mp_obj_t fir_get_ir(int w, int h, float Ta, float *To, bool mirror, } static mp_obj_t py_fir_deinit() { - #if (OMV_FIR_LEPTON_ENABLE == 1) - if (fir_sensor == FIR_LEPTON) { - fir_lepton_deinit(); - } - #endif - if (fir_sensor != FIR_NONE) { omv_i2c_deinit(&fir_bus); fir_sensor = FIR_NONE; @@ -382,12 +368,6 @@ mp_obj_t py_fir_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) break; } #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - case LEPTON_ADDR: { - type = FIR_LEPTON; - break; - } - #endif default: continue; } @@ -571,28 +551,6 @@ mp_obj_t py_fir_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) return mp_const_none; } #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - case FIR_LEPTON: { - fir_sensor = FIR_LEPTON; - FIR_LEPTON_RETRY: - omv_i2c_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; - omv_i2c_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("Failed to detect a supported FIR sensor.")); } @@ -650,10 +608,6 @@ static mp_obj_t py_fir_refresh() { case FIR_AMG8833: return mp_obj_new_int(fir_ir_fresh_rate); #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - case FIR_LEPTON: - 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")); } @@ -678,77 +632,12 @@ static mp_obj_t py_fir_resolution() { case FIR_AMG8833: return mp_obj_new_int(fir_adc_resolution); #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - case FIR_LEPTON: - 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); -#if (OMV_FIR_LEPTON_ENABLE == 1) -static mp_obj_t py_fir_radiometric() { - if (fir_sensor == FIR_LEPTON) { - return fir_lepton_get_radiometry(); - } else { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Operation not supported by this FIR sensor")); - } -} -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); - } else { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Operation not supported by this FIR sensor")); - } - 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); - } else { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Operation not supported by this FIR sensor")); - } - 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(); - } else { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Operation not supported by this FIR sensor")); - } -} -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(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_timeout }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_timeout, MP_ARG_INT, {.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); - - if (fir_sensor == FIR_LEPTON) { - fir_lepton_trigger_ffc(args[ARG_timeout].u_int); - } else { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Operation not supported by this FIR sensor")); - } - 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) { #if (OMV_FIR_MLX90621_ENABLE == 1) @@ -798,11 +687,6 @@ mp_obj_t py_fir_read_ta() { return mp_obj_new_float(AMG8833_12_TO_16(temp) * 0.0625f); } #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - case FIR_LEPTON: { - return fir_lepton_read_ta(); - } - #endif default: { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized")); } @@ -872,12 +756,6 @@ mp_obj_t py_fir_read_ir(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar return result; } #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - case FIR_LEPTON: { - return fir_lepton_read_ir(fir_width, fir_height, args[ARG_hmirror].u_bool, - args[ARG_vflip].u_bool, args[ARG_transpose].u_bool, args[ARG_timeout].u_int); - } - #endif default: { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized")); } @@ -1086,16 +964,6 @@ mp_obj_t py_fir_snapshot(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a break; } #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - case FIR_LEPTON: { - bool auto_range = args[ARG_scale].u_obj == mp_const_none; - imlib_fill_image_from_lepton(&src_img, fir_width, fir_height, - fir_lepton_get_frame(args[ARG_timeout].u_int), min, max, auto_range, - fir_lepton_get_radiometry_enabled(), fir_lepton_get_temperature(), - args[ARG_hmirror].u_bool, args[ARG_vflip].u_bool, args[ARG_transpose].u_bool); - break; - } - #endif default: { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("FIR sensor is not initialized")); } @@ -1129,9 +997,6 @@ static const mp_rom_map_elem_t globals_dict_table[] = { #if (OMV_FIR_AMG8833_ENABLE == 1) { MP_ROM_QSTR(MP_QSTR_FIR_AMG8833), MP_ROM_INT(FIR_AMG8833) }, #endif - #if (OMV_FIR_LEPTON_ENABLE == 1) - { MP_ROM_QSTR(MP_QSTR_FIR_LEPTON), MP_ROM_INT(FIR_LEPTON) }, - #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) }, @@ -1139,23 +1004,6 @@ static const mp_rom_map_elem_t globals_dict_table[] = { { 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_FIR_LEPTON_ENABLE == 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) }, diff --git a/modules/py_fir_lepton.c b/modules/py_fir_lepton.c deleted file mode 100644 index 53d955c30..000000000 --- a/modules/py_fir_lepton.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * 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 "omv_boardconfig.h" -#if (OMV_FIR_LEPTON_ENABLE == 1) -#include "py/nlr.h" -#include "py/runtime.h" -#include "py/obj.h" -#include "py/mphal.h" -#include "softtimer.h" - -#include "crc16.h" -#include "LEPTON_SDK.h" -#include "LEPTON_AGC.h" -#include "LEPTON_SYS.h" -#include "LEPTON_VID.h" -#include "LEPTON_OEM.h" -#include "LEPTON_RAD.h" -#include "LEPTON_I2C_Reg.h" - -#include "py_helper.h" -#include "omv_common.h" -#include "omv_gpio.h" -#include "omv_spi.h" - -#define FRAMEBUFFER_COUNT 3 -static volatile int framebuffer_tail = 0; -static int framebuffer_head = 0; -static uint16_t *framebuffers[FRAMEBUFFER_COUNT] = {}; - -static int fir_lepton_rad_en = false; -static bool fir_lepton_3 = false; - -#if defined(OMV_FIR_LEPTON_MCLK_TIM) -static TIM_HandleTypeDef fir_lepton_mclk_tim_handle = {}; -#endif - -static LEP_CAMERA_PORT_DESC_T fir_lepton_handle = {}; -static omv_spi_t spi_bus = {}; - -#define VOSPI_HEADER_WORDS (2) // 16-bits -#define VOSPI_PID_SIZE_PIXELS (80) // w, 16-bits per pixel -#define VOSPI_PIDS_PER_SID (60) // h -#define VOSPI_SIDS_PER_FRAME (4) -#define VOSPI_PACKET_SIZE (VOSPI_HEADER_WORDS + VOSPI_PID_SIZE_PIXELS) // 16-bits -#define VOSPI_SID_SIZE_PIXELS (VOSPI_PIDS_PER_SID * VOSPI_PID_SIZE_PIXELS) // 16-bits - -#define VOSPI_BUFFER_SIZE (VOSPI_PACKET_SIZE * 2) // 16-bits -#define VOSPI_CLOCK_SPEED 20000000 // hz -#define VOSPI_SYNC_MS 200 // ms - -#define VOSPI_SPECIAL_PACKET (20) -#define VOSPI_DONT_CARE_PACKET (0x0F00) -#define VOSPI_HEADER_DONT_CARE(x) (((x) & VOSPI_DONT_CARE_PACKET) == VOSPI_DONT_CARE_PACKET) -#define VOSPI_HEADER_PID(id) ((id) & 0x0FFF) -#define VOSPI_HEADER_SID(id) (((id) >> 12) & 0x7) - -static soft_timer_entry_t flir_lepton_spi_rx_timer = {}; -static int fir_lepton_spi_rx_cb_tail = 0; -static int fir_lepton_spi_rx_cb_expected_pid = 0; -static int fir_lepton_spi_rx_cb_expected_sid = 0; -static uint16_t OMV_ATTR_SEC_ALIGN(fir_lepton_buf[VOSPI_BUFFER_SIZE], OMV_VOSPI_DMA_BUFFER, OMV_DMA_ALIGNMENT); -static void fir_lepton_spi_callback(omv_spi_t *spi, void *userdata, void *buf); - -static mp_obj_t fir_lepton_spi_resync_callback(mp_obj_t unused) { - // For triple buffering we are never drawing where tail or head - // (which may instantly update to be equal to tail) is. - fir_lepton_spi_rx_cb_tail = (framebuffer_tail + 1) % FRAMEBUFFER_COUNT; - if (fir_lepton_spi_rx_cb_tail == framebuffer_head) { - fir_lepton_spi_rx_cb_tail = (fir_lepton_spi_rx_cb_tail + 1) % FRAMEBUFFER_COUNT; - } - - omv_spi_transfer_t spi_xfer = { - .rxbuf = fir_lepton_buf, - .size = VOSPI_BUFFER_SIZE, - .flags = OMV_SPI_XFER_DMA, - .callback = fir_lepton_spi_callback, - }; - - omv_gpio_write(spi_bus.cs, 0); - omv_spi_transfer_start(&spi_bus, &spi_xfer); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(fir_lepton_spi_resync_callback_obj, fir_lepton_spi_resync_callback); - -static void fir_lepton_spi_resync() { - flir_lepton_spi_rx_timer.flags = SOFT_TIMER_FLAG_PY_CALLBACK; - flir_lepton_spi_rx_timer.mode = SOFT_TIMER_MODE_ONE_SHOT; - flir_lepton_spi_rx_timer.delta_ms = VOSPI_SYNC_MS; - flir_lepton_spi_rx_timer.py_callback = (mp_obj_t) &fir_lepton_spi_resync_callback_obj; - soft_timer_insert(&flir_lepton_spi_rx_timer, VOSPI_SYNC_MS); -} - -#if defined(OMV_FIR_LEPTON_CHECK_CRC) -static bool fir_lepton_spi_check_crc(const uint16_t *base) { - int id = base[0]; - int packet_crc = base[1]; - int crc = ByteCRC16((id >> 8) & 0x0F, 0); - crc = ByteCRC16(id, crc); - crc = ByteCRC16(0, crc); - crc = ByteCRC16(0, crc); - - for (int i = VOSPI_HEADER_WORDS; i < VOSPI_PACKET_SIZE; i++) { - int value = base[i]; - crc = ByteCRC16(value >> 8, crc); - crc = ByteCRC16(value, crc); - } - - return packet_crc == crc; -} -#endif - -static mp_obj_t fir_lepton_frame_cb = mp_const_none; - -void fir_lepton_spi_callback(omv_spi_t *spi, void *userdata, void *buf) { - const uint16_t *base = (uint16_t *) buf; - - int id = base[0]; - - // Ignore don't care packets. - if (VOSPI_HEADER_DONT_CARE(id)) { - return; - } - - int pid = VOSPI_HEADER_PID(id); - int sid = VOSPI_HEADER_SID(id) - 1; - - // Discard packets with a pid != 0 when waiting for the first packet. - if ((fir_lepton_spi_rx_cb_expected_pid == 0) && (pid != 0)) { - return; - } - - // Discard sidments with a sid != 0 when waiting for the first segment. - if (fir_lepton_3 && (pid == VOSPI_SPECIAL_PACKET) && (fir_lepton_spi_rx_cb_expected_sid == 0) && (sid != 0)) { - fir_lepton_spi_rx_cb_expected_pid = 0; - return; - } - - // Are we in sync with the flir lepton? - if ((pid != fir_lepton_spi_rx_cb_expected_pid) - #if defined(OMV_FIR_LEPTON_CHECK_CRC) - || (!fir_lepton_spi_check_crc(base)) - #endif - || (fir_lepton_3 && (pid == VOSPI_SPECIAL_PACKET) && (sid != fir_lepton_spi_rx_cb_expected_sid))) { - fir_lepton_spi_rx_cb_expected_pid = 0; - fir_lepton_spi_rx_cb_expected_sid = 0; - omv_spi_transfer_abort(&spi_bus); - omv_gpio_write(spi_bus.cs, 1); - fir_lepton_spi_resync(); - return; - } - - memcpy(framebuffers[fir_lepton_spi_rx_cb_tail] - + (fir_lepton_spi_rx_cb_expected_pid * VOSPI_PID_SIZE_PIXELS) - + (fir_lepton_spi_rx_cb_expected_sid * VOSPI_SID_SIZE_PIXELS), - base + VOSPI_HEADER_WORDS, VOSPI_PID_SIZE_PIXELS * sizeof(uint16_t)); - - fir_lepton_spi_rx_cb_expected_pid += 1; - if (fir_lepton_spi_rx_cb_expected_pid == VOSPI_PIDS_PER_SID) { - fir_lepton_spi_rx_cb_expected_pid = 0; - - bool frame_ready = false; - - // For the FLIR Lepton 3 we have to receive all the pids in all the segments. - if (fir_lepton_3) { - fir_lepton_spi_rx_cb_expected_sid += 1; - if (fir_lepton_spi_rx_cb_expected_sid == VOSPI_SIDS_PER_FRAME) { - fir_lepton_spi_rx_cb_expected_sid = 0; - frame_ready = true; - } - // For the FLIR Lepton 1/2 we just have to receive all the pids. - } else { - frame_ready = true; - } - - if (frame_ready) { - // Update tail which means a new image is ready. - framebuffer_tail = fir_lepton_spi_rx_cb_tail; - - // For triple buffering we are never drawing where tail or head - // (which may instantly update to be equal to tail) is. - fir_lepton_spi_rx_cb_tail = (fir_lepton_spi_rx_cb_tail + 1) % FRAMEBUFFER_COUNT; - if (fir_lepton_spi_rx_cb_tail == framebuffer_head) { - fir_lepton_spi_rx_cb_tail = (fir_lepton_spi_rx_cb_tail + 1) % FRAMEBUFFER_COUNT; - } - - // User should use micropython.schedule() in their callback to process the new frame. - if (fir_lepton_frame_cb != mp_const_none) { - mp_call_function_0(fir_lepton_frame_cb); - } - } - } -} - -#if defined(OMV_FIR_LEPTON_VSYNC_PIN) -static mp_obj_t fir_lepton_vsync_cb = NULL; - -static void fir_lepton_extint_callback(void *data) { - if (fir_lepton_vsync_cb) { - mp_call_function_0(fir_lepton_vsync_cb); - } -} -#endif - -void fir_lepton_deinit() { - omv_spi_transfer_abort(&spi_bus); - fir_lepton_spi_rx_cb_expected_pid = 0; - fir_lepton_spi_rx_cb_expected_sid = 0; - fb_alloc_free_till_mark_past_mark_permanent(); - - #if defined(OMV_FIR_LEPTON_MCLK) - HAL_TIM_PWM_Stop(&fir_lepton_mclk_tim_handle, OMV_FIR_LEPTON_MCLK_TIM_CHANNEL); - HAL_TIM_PWM_DeInit(&fir_lepton_mclk_tim_handle); - OMV_FIR_LEPTON_MCLK_TIM_FORCE_RESET(); - OMV_FIR_LEPTON_MCLK_TIM_RELEASE_RESET(); - OMV_FIR_LEPTON_MCLK_TIM_CLK_DISABLE(); - omv_gpio_deinit(OMV_FIR_LEPTON_MCLK_PIN); - #endif - - omv_spi_deinit(&spi_bus); - - #if defined(OMV_FIR_LEPTON_RESET_PIN) - omv_gpio_deinit(OMV_FIR_LEPTON_RESET_PIN); - #endif - - #if defined(OMV_FIR_LEPTON_POWER_PIN) - omv_gpio_deinit(OMV_FIR_LEPTON_POWER_PIN); - #endif -} - -int fir_lepton_init(omv_i2c_t *bus, int *w, int *h, int *refresh, int *resolution) { - omv_spi_config_t spi_config; - omv_spi_default_config(&spi_config, OMV_FIR_LEPTON_SPI_BUS); - - spi_config.baudrate = VOSPI_CLOCK_SPEED; - - spi_config.datasize = 16; - spi_config.bus_mode = OMV_SPI_BUS_RX; - spi_config.nss_enable = false; - spi_config.clk_pol = OMV_SPI_CPOL_HIGH; - spi_config.clk_pha = OMV_SPI_CPHA_2EDGE; - spi_config.dma_flags = OMV_SPI_DMA_CIRCULAR | OMV_SPI_DMA_DOUBLE; - omv_spi_init(&spi_bus, &spi_config); - - omv_gpio_write(spi_bus.cs, 1); - - #if defined(OMV_FIR_LEPTON_RESET_PIN) - omv_gpio_config(OMV_FIR_LEPTON_RESET_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_LOW, -1); - omv_gpio_write(OMV_FIR_LEPTON_RESET_PIN, 1); - #endif - - #if defined(OMV_FIR_LEPTON_POWER_PIN) - omv_gpio_config(OMV_FIR_LEPTON_POWER_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_LOW, -1); - omv_gpio_write(OMV_FIR_LEPTON_POWER_PIN, 1); - #endif - - #if defined(OMV_FIR_LEPTON_MCLK_TIM) - int tclk = OMV_FIR_LEPTON_MCLK_TIM_PCLK_FREQ() * 2; - int period = (tclk / OMV_FIR_LEPTON_MCLK_FREQ) - 1; - - omv_gpio_config(OMV_FIR_LEPTON_MCLK_PIN, OMV_GPIO_MODE_ALT, GPIO_PULLUP, OMV_GPIO_SPEED_MED, -1); - - fir_lepton_mclk_tim_handle.Instance = OMV_FIR_LEPTON_MCLK_TIM; - fir_lepton_mclk_tim_handle.Init.Prescaler = 0; - fir_lepton_mclk_tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP; - fir_lepton_mclk_tim_handle.Init.Period = period; - fir_lepton_mclk_tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - fir_lepton_mclk_tim_handle.Init.RepetitionCounter = 0; - fir_lepton_mclk_tim_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - - TIM_OC_InitTypeDef fir_lepton_mclk_tim_oc_handle; - fir_lepton_mclk_tim_oc_handle.Pulse = period / 2; - fir_lepton_mclk_tim_oc_handle.OCMode = TIM_OCMODE_PWM1; - fir_lepton_mclk_tim_oc_handle.OCPolarity = TIM_OCPOLARITY_HIGH; - fir_lepton_mclk_tim_oc_handle.OCNPolarity = TIM_OCNPOLARITY_HIGH; - fir_lepton_mclk_tim_oc_handle.OCFastMode = TIM_OCFAST_DISABLE; - fir_lepton_mclk_tim_oc_handle.OCIdleState = TIM_OCIDLESTATE_RESET; - fir_lepton_mclk_tim_oc_handle.OCNIdleState = TIM_OCNIDLESTATE_RESET; - - OMV_FIR_LEPTON_MCLK_TIM_CLK_ENABLE(); - HAL_TIM_PWM_Init(&fir_lepton_mclk_tim_handle); - HAL_TIM_PWM_ConfigChannel(&fir_lepton_mclk_tim_handle, - &fir_lepton_mclk_tim_oc_handle, - OMV_FIR_LEPTON_MCLK_TIM_CHANNEL); - HAL_TIM_PWM_Start(&fir_lepton_mclk_tim_handle, OMV_FIR_LEPTON_MCLK_TIM_CHANNEL); - #endif - - #if defined(OMV_FIR_LEPTON_POWER_PIN) - omv_gpio_write(OMV_FIR_LEPTON_POWER_PIN, 0); - mp_hal_delay_ms(10); - - omv_gpio_write(OMV_FIR_LEPTON_POWER_PIN, 1); - mp_hal_delay_ms(10); - #endif - - #if defined(OMV_FIR_LEPTON_RESET_PIN) - omv_gpio_write(OMV_FIR_LEPTON_RESET_PIN, 0); - mp_hal_delay_ms(10); - - omv_gpio_write(OMV_FIR_LEPTON_RESET_PIN, 1); - mp_hal_delay_ms(1000); - #endif - - LEP_RAD_ENABLE_E rad; - LEP_AGC_ROI_T roi; - - for (uint32_t start = mp_hal_ticks_ms();; mp_hal_delay_ms(1)) { - if (LEP_OpenPort(bus, LEP_CCI_TWI, 0, &fir_lepton_handle) == LEP_OK) { - break; - } - - if ((mp_hal_ticks_ms() - start) >= 1000) { - return -1; - } - } - - #if (!defined(OMV_FIR_LEPTON_POWER_PIN)) && (!defined(OMV_FIR_LEPTON_RESET_PIN)) - if (LEP_RunOemReboot(&fir_lepton_handle) != LEP_OK) { - return -2; - } - - mp_hal_delay_ms(1000); - #endif - - for (uint32_t start = mp_hal_ticks_ms();; mp_hal_delay_ms(1)) { - LEP_SDK_BOOT_STATUS_E status; - - if (LEP_GetCameraBootStatus(&fir_lepton_handle, &status) != LEP_OK) { - return -3; - } - - if (status == LEP_BOOT_STATUS_BOOTED) { - break; - } - - if ((mp_hal_ticks_ms() - start) >= 1000) { - return -4; - } - } - - for (uint32_t start = mp_hal_ticks_ms();; mp_hal_delay_ms(1)) { - LEP_UINT16 status; - - if (LEP_DirectReadRegister(&fir_lepton_handle, LEP_I2C_STATUS_REG, &status) != LEP_OK) { - return -5; - } - - if (!(status & LEP_I2C_STATUS_BUSY_BIT_MASK)) { - break; - } - - if ((mp_hal_ticks_ms() - start) >= 1000) { - return -6; - } - } - - if (LEP_GetAgcROI(&fir_lepton_handle, &roi) != LEP_OK) { - return -7; - } - - if (LEP_GetRadEnableState(&fir_lepton_handle, &rad) != LEP_OK) { - return -8; - } - - int flir_w = roi.endCol + 1; - int flir_h = roi.endRow + 1; - fir_lepton_3 = flir_h > VOSPI_PIDS_PER_SID; - fir_lepton_rad_en = rad == LEP_RAD_ENABLE; - *w = flir_w; - *h = flir_h; - *refresh = fir_lepton_3 ? 9 : 27; - *resolution = fir_lepton_rad_en ? 16 : 14; - - #if defined(OMV_FIR_LEPTON_VSYNC_PIN) - if (LEP_SetOemGpioMode(&fir_lepton_handle, LEP_OEM_GPIO_MODE_VSYNC) != LEP_OK) { - return -9; - } - omv_gpio_config(OMV_FIR_LEPTON_VSYNC_PIN, OMV_GPIO_MODE_IT_FALL, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_LOW, -1); - omv_gpio_irq_register(OMV_FIR_LEPTON_VSYNC_PIN, fir_lepton_extint_callback, NULL); - #endif - - /////////////////////////////////////////////////////////////////////// - - fb_alloc_mark(); - - framebuffer_tail = 0; - framebuffer_head = 0; - - for (int i = 0; i < FRAMEBUFFER_COUNT; i++) { - framebuffers[i] = (uint16_t *) fb_alloc0(flir_w * flir_h * sizeof(uint16_t), FB_ALLOC_NO_HINT); - } - - fb_alloc_mark_permanent(); - fir_lepton_spi_resync(); - return 0; -} - -#if defined(OMV_FIR_LEPTON_VSYNC_PIN) -void fir_lepton_register_vsync_cb(mp_obj_t cb) { - omv_gpio_irq_enable(OMV_FIR_LEPTON_VSYNC_PIN, false); - - fir_lepton_vsync_cb = cb; - - if (cb != mp_const_none) { - omv_gpio_irq_enable(OMV_FIR_LEPTON_VSYNC_PIN, true); - } -} -#endif - -mp_obj_t fir_lepton_get_radiometry() { - return mp_obj_new_bool(fir_lepton_rad_en); -} - -void fir_lepton_register_frame_cb(mp_obj_t cb) { - fir_lepton_frame_cb = cb; -} - -mp_obj_t fir_lepton_get_frame_available() { - return mp_obj_new_bool(framebuffer_tail != framebuffer_head); -} - -uint16_t *fir_lepton_get_frame(int timeout) { - int sampled_framebuffer_tail = framebuffer_tail; - - if (timeout >= 0) { - for (uint32_t start = mp_hal_ticks_ms();;) { - sampled_framebuffer_tail = framebuffer_tail; - - if (framebuffer_head != sampled_framebuffer_tail) { - break; - } - - if ((mp_hal_ticks_ms() - start) >= timeout) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Timeout!")); - } - - MICROPY_EVENT_POLL_HOOK - } - } - - framebuffer_head = sampled_framebuffer_tail; - return framebuffers[sampled_framebuffer_tail]; -} - -bool fir_lepton_get_radiometry_enabled() { - return fir_lepton_rad_en; -} - -int fir_lepton_get_temperature() { - LEP_SYS_FPA_TEMPERATURE_KELVIN_T kelvin; - - if (LEP_GetSysFpaTemperatureKelvin(&fir_lepton_handle, &kelvin) != LEP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("FPA Error!")); - } - - return kelvin; -} - -mp_obj_t fir_lepton_read_ta() { - return mp_obj_new_float((fir_lepton_get_temperature() * 0.01f) - 273.15f); -} - -mp_obj_t fir_lepton_read_ir(int w, int h, bool mirror, bool flip, bool transpose, int timeout) { - int kelvin = fir_lepton_get_temperature(); - mp_obj_list_t *list = (mp_obj_list_t *) mp_obj_new_list(w * h, NULL); - const uint16_t *data = fir_lepton_get_frame(timeout); - float min = +FLT_MAX; - float max = -FLT_MAX; - int w_1 = w - 1; - int h_1 = h - 1; - - for (int y = 0; y < h; y++) { - int y_dst = flip ? (h_1 - y) : y; - const uint16_t *raw_row = data + (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; - int raw = raw_row[x]; - - if (!fir_lepton_rad_en) { - raw = (raw - 8192) + kelvin; - } - - float celcius = (raw * 0.01f) - 273.15f; - - if (celcius < min) { - min = celcius; - } - - if (celcius > max) { - max = celcius; - } - - mp_obj_t f = mp_obj_new_float(celcius); - - if (!transpose) { - list_row[x_dst] = f; - } else { - t_list_row[x_dst * h] = f; - } - } - } - - mp_obj_t tuple[4]; - tuple[0] = mp_obj_new_float((kelvin * 0.01f) - 273.15f); - 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); -} - -void fir_lepton_trigger_ffc(int timeout) { - if (LEP_RunSysFFCNormalization(&fir_lepton_handle) != LEP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("FFC Error!")); - } - - if (timeout >= 0) { - for (uint32_t start = mp_hal_ticks_ms();;) { - LEP_SYS_STATUS_E status; - - if (LEP_GetSysFFCStatus(&fir_lepton_handle, &status) != LEP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("SYS Error!")); - } - - if (status == LEP_SYS_STATUS_READY) { - break; - } - - if ((mp_hal_ticks_ms() - start) >= timeout) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Timeout!")); - } - - mp_hal_delay_ms(1); - } - } -} - -#endif // OMV_FIR_LEPTON_ENABLE diff --git a/modules/py_fir_lepton.h b/modules/py_fir_lepton.h deleted file mode 100644 index d02bad003..000000000 --- a/modules/py_fir_lepton.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ -#ifndef __PY_FIR_LEPTON_H__ -#define __PY_FIR_LEPTON_H__ -void fir_lepton_deinit(); -int fir_lepton_init(omv_i2c_t *bus, int *w, int *h, int *refresh, int *resolution); -void fir_lepton_register_vsync_cb(mp_obj_t cb); -mp_obj_t fir_lepton_get_radiometry(); -void fir_lepton_register_frame_cb(mp_obj_t cb); -mp_obj_t fir_lepton_get_frame_available(); -uint16_t *fir_lepton_get_frame(int timeout); -bool fir_lepton_get_radiometry_enabled(); -int fir_lepton_get_temperature(); -mp_obj_t fir_lepton_read_ta(); -mp_obj_t fir_lepton_read_ir(int w, int h, bool mirror, bool flip, bool transpose, int timeout); -void fir_lepton_trigger_ffc(int timeout); -#endif // __PY_FIR_LEPTON_H__ diff --git a/ports/stm32/stm_hal.c b/ports/stm32/stm_hal.c index f89dda298..3a9557b46 100644 --- a/ports/stm32/stm_hal.c +++ b/ports/stm32/stm_hal.c @@ -271,16 +271,6 @@ void HAL_MspInit(void) { OMV_AXI_QOS_LTDC_W_SET(OMV_AXI_QOS_LTDC_W_PRI); #endif - #if defined(OMV_FIR_LEPTON_RESET_PIN) - omv_gpio_config(OMV_FIR_LEPTON_RESET_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1); - omv_gpio_write(OMV_FIR_LEPTON_RESET_PIN, 0); - #endif - - #if defined(OMV_FIR_LEPTON_POWER_PIN) - omv_gpio_config(OMV_FIR_LEPTON_POWER_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1); - omv_gpio_write(OMV_FIR_LEPTON_POWER_PIN, 0); - #endif - #if defined(STM32H7) // This disconnects PA0/PA1 from PA0_C/PA1_C. // PA0_C/PA1_C connect to ADC1/2 Channels P0/P1 From 4c5b996c5b20f82570396c8e89dc1e2695989e96 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sun, 20 Jul 2025 21:02:14 -0700 Subject: [PATCH 3/6] boards/OPENMVPT: Update SPI AF Configs. --- boards/OPENMVPT/omv_boardconfig.h | 6 +++--- boards/OPENMVPT/omv_pins.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/boards/OPENMVPT/omv_boardconfig.h b/boards/OPENMVPT/omv_boardconfig.h index 495d371fb..74d04f37d 100644 --- a/boards/OPENMVPT/omv_boardconfig.h +++ b/boards/OPENMVPT/omv_boardconfig.h @@ -243,7 +243,7 @@ #define OMV_SPI2_SCLK_PIN (&omv_pin_B13_SPI2) #define OMV_SPI2_MISO_PIN (&omv_pin_B14_SPI2) #define OMV_SPI2_MOSI_PIN (&omv_pin_B15_SPI2) -#define OMV_SPI2_SSEL_PIN (&omv_pin_B12_GPIO) +#define OMV_SPI2_SSEL_PIN (&omv_pin_B12_SPI2) #define OMV_SPI2_DMA_TX_CHANNEL (DMA1_Stream4) #define OMV_SPI2_DMA_TX_REQUEST (DMA_REQUEST_SPI2_TX) #define OMV_SPI2_DMA_RX_CHANNEL (DMA1_Stream3) @@ -254,7 +254,7 @@ #define OMV_SPI3_SCLK_PIN (&omv_pin_B3_SPI3) #define OMV_SPI3_MISO_PIN (&omv_pin_B4_SPI3) #define OMV_SPI3_MOSI_PIN (&omv_pin_B5_SPI3) -#define OMV_SPI3_SSEL_PIN (&omv_pin_A15_GPIO) +#define OMV_SPI3_SSEL_PIN (&omv_pin_A15_SPI3) #define OMV_SPI3_DMA_TX_CHANNEL (DMA1_Stream7) #define OMV_SPI3_DMA_TX_REQUEST (DMA_REQUEST_SPI3_TX) #define OMV_SPI3_DMA_RX_CHANNEL (DMA1_Stream2) @@ -265,7 +265,7 @@ #define OMV_SPI5_SCLK_PIN (&omv_pin_H6_SPI5) #define OMV_SPI5_MISO_PIN (&omv_pin_H7_SPI5) #define OMV_SPI5_MOSI_PIN (&omv_pin_J10_SPI5) -#define OMV_SPI5_SSEL_PIN (&omv_pin_K1_GPIO) +#define OMV_SPI5_SSEL_PIN (&omv_pin_K1_SPI5) #define OMV_SPI5_DMA_TX_CHANNEL (DMA2_Stream4) #define OMV_SPI5_DMA_TX_REQUEST (DMA_REQUEST_SPI5_TX) #define OMV_SPI5_DMA_RX_CHANNEL (DMA2_Stream3) diff --git a/boards/OPENMVPT/omv_pins.h b/boards/OPENMVPT/omv_pins.h index e92d02c68..af72cbaf2 100644 --- a/boards/OPENMVPT/omv_pins.h +++ b/boards/OPENMVPT/omv_pins.h @@ -8,7 +8,7 @@ OMV_GPIO_DEFINE(A, 5, NONE, GPIO) OMV_GPIO_DEFINE(A, 6, AF13, DCMI) OMV_GPIO_DEFINE(A, 8, AF1, TIM1) OMV_GPIO_DEFINE(A, 10, NONE, GPIO) -OMV_GPIO_DEFINE(A, 15, NONE, GPIO) +OMV_GPIO_DEFINE(A, 15, AF6, SPI3) OMV_GPIO_DEFINE(B, 0, AF2, TIM3) OMV_GPIO_DEFINE(B, 3, AF6, SPI3) OMV_GPIO_DEFINE(B, 4, AF6, SPI3) @@ -22,6 +22,7 @@ OMV_GPIO_DEFINE(B, 10, AF4, I2C2) OMV_GPIO_DEFINE(B, 11, NONE, GPIO) OMV_GPIO_DEFINE(B, 11, AF4, I2C2) OMV_GPIO_DEFINE(B, 12, NONE, GPIO) +OMV_GPIO_DEFINE(B, 12, AF5, SPI2) OMV_GPIO_DEFINE(B, 13, AF5, SPI2) OMV_GPIO_DEFINE(B, 14, AF5, SPI2) OMV_GPIO_DEFINE(B, 15, AF5, SPI2) @@ -72,6 +73,7 @@ OMV_GPIO_DEFINE(J, 12, AF9, LTDC) OMV_GPIO_DEFINE(J, 15, AF14, LTDC) OMV_GPIO_DEFINE(K, 0, AF14, LTDC) OMV_GPIO_DEFINE(K, 1, NONE, GPIO) +OMV_GPIO_DEFINE(K, 1, AF5, SPI5) OMV_GPIO_DEFINE(K, 2, NONE, GPIO) OMV_GPIO_DEFINE(K, 3, AF14, LTDC) OMV_GPIO_DEFINE(K, 4, AF14, LTDC) From 592a22902b8b3acfecfbea6ab92ed1a7d77eb108 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sun, 20 Jul 2025 21:02:51 -0700 Subject: [PATCH 4/6] drivers/sensors: Fix support for FLIR Lepton 1. --- drivers/sensors/lepton.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/sensors/lepton.c b/drivers/sensors/lepton.c index fe85bd30e..dc6340f6b 100644 --- a/drivers/sensors/lepton.c +++ b/drivers/sensors/lepton.c @@ -314,7 +314,11 @@ static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mo // Use the low gain mode to enable high temperature readings (~450C) on Lepton 3.5 LEP_SYS_GAIN_MODE_E gain_mode = high_temp_mode ? LEP_SYS_GAIN_MODE_LOW : LEP_SYS_GAIN_MODE_HIGH; - if (LEP_SetSysGainMode(&lepton.port, gain_mode) != LEP_OK || + bool hasSetSysGainMode = csi->chip_id == LEPTON_3_5 || + csi->chip_id == LEPTON_3_0 || + csi->chip_id == LEPTON_2_5; + + if ((hasSetSysGainMode && LEP_SetSysGainMode(&lepton.port, gain_mode) != LEP_OK) || LEP_GetAgcROI(&lepton.port, &roi) != LEP_OK || LEP_SetRadEnableState(&lepton.port, measurement_mode) != LEP_OK || LEP_SetAgcEnableState(&lepton.port, !measurement_mode) != LEP_OK || From 1177105663cbc17aed67b942d0d2778f04301a2b Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sun, 20 Jul 2025 21:23:20 -0700 Subject: [PATCH 5/6] boards/OPENMVPT: Enable flir lepton as secondary sensor. --- boards/OPENMVPT/omv_boardconfig.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/OPENMVPT/omv_boardconfig.h b/boards/OPENMVPT/omv_boardconfig.h index 74d04f37d..5f8517005 100644 --- a/boards/OPENMVPT/omv_boardconfig.h +++ b/boards/OPENMVPT/omv_boardconfig.h @@ -33,6 +33,8 @@ #define OMV_OV5640_PLL_CTRL2 (0x64) #define OMV_OV5640_PLL_CTRL3 (0x13) +#define OMV_LEPTON_ENABLE (1) + // FIR drivers configuration. #define OMV_FIR_MLX90621_ENABLE (1) #define OMV_FIR_MLX90640_ENABLE (1) @@ -185,6 +187,9 @@ #define OMV_SOFT_I2C_SIOD_PIN (&omv_pin_B11_GPIO) #define OMV_SOFT_I2C_SPIN_DELAY 64 +// CSI SPI bus +#define OMV_CSI_SPI_ID (3) + // WINC1500 WiFi module SPI bus #define OMV_WINC_SPI_ID (5) #define OMV_WINC_SPI_BAUDRATE (50000000) @@ -208,6 +213,7 @@ #define OMV_CSI_DMA_REQUEST (DMA_REQUEST_DCMI) #define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1) +#define OMV_CSI_MAX_DEVICES (2) #define OMV_CSI_D0_PIN (&omv_pin_C6_DCMI) #define OMV_CSI_D1_PIN (&omv_pin_C7_DCMI) @@ -224,6 +230,8 @@ #define OMV_CSI_RESET_PIN (&omv_pin_A10_GPIO) #define OMV_CSI_POWER_PIN (&omv_pin_D7_GPIO) +#define OMV_CSI_POLARITY_CONFIG { OMV_CSI_ACTIVE_HIGH, OMV_CSI_ACTIVE_LOW } + // Physical I2C buses. // I2C bus 1 From 1a614202ee9a3495f905be89b9960b1cb7fe8346 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sun, 20 Jul 2025 21:24:23 -0700 Subject: [PATCH 6/6] scripts/examples: Update PureThermal example script to new API. --- .../00-HelloWorld/pure_thermal.py | 97 +++++++++++++------ 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/scripts/examples/50-OpenMV-Boards/51-Pure-Thermal/00-HelloWorld/pure_thermal.py b/scripts/examples/50-OpenMV-Boards/51-Pure-Thermal/00-HelloWorld/pure_thermal.py index 200f0d21f..cce6b46c8 100644 --- a/scripts/examples/50-OpenMV-Boards/51-Pure-Thermal/00-HelloWorld/pure_thermal.py +++ b/scripts/examples/50-OpenMV-Boards/51-Pure-Thermal/00-HelloWorld/pure_thermal.py @@ -4,14 +4,14 @@ # # Pure Thermal Example Script # -# Thanks for buying the Pure Thermal OpenMV! This example script shows off thermal video -# overlay onto the color camera image and driving the attached LCD screen and HDMI output. +# Thanks for buying the Pure Thermal OpenMV! This example script shows +# off thermal video overlay onto the color camera image and driving +# the attached LCD screen and HDMI output. -import sensor +import csi import image import time import display -import fir import math import tfp410 @@ -19,13 +19,42 @@ import tfp410 # Color Tracking Thresholds (Grayscale Min, Grayscale Max) threshold_list = [(200, 255)] -sensor.reset() -sensor.set_pixformat(sensor.RGB565) -sensor.set_framesize(sensor.WVGA) +# Set the target temp range here +min_temp_in_celsius = 20.0 +max_temp_in_celsius = 40.0 + +csi0 = csi.CSI(cid=csi.OV5640) +csi0.reset() +csi0.pixformat(csi.RGB565) +csi0.framesize(csi.WVGA) time.sleep_ms(50) -fir.init(fir.FIR_LEPTON) -fir_img = image.Image(fir.width(), fir.height(), sensor.GRAYSCALE) +csi1 = csi.CSI(cid=csi.LEPTON) +csi1.reset(hard=False) +csi1.pixformat(csi.GRAYSCALE) +csi1.framesize(csi.QQVGA) + +# Enables exact temperature measurments from the flir lepton. +# The second argument turns high gain mode on for high temp reading. +csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, False) +csi1.ioctl( + csi.IOCTL_LEPTON_SET_RANGE, min_temp_in_celsius, max_temp_in_celsius +) + +print( + "Lepton Res (%dx%d)" + % ( + csi1.ioctl(csi.IOCTL_LEPTON_GET_WIDTH), + csi1.ioctl(csi.IOCTL_LEPTON_GET_HEIGHT), + ) +) + +print( + "Radiometry Available: " + + ("Yes" if csi1.ioctl(csi.IOCTL_LEPTON_GET_RADIOMETRY) else "No") +) + +fir_img = image.Image(csi1.width(), csi1.height(), image.GRAYSCALE) time.sleep_ms(50) lcd = display.RGBDisplay(framesize=display.FWVGA, refresh=60) @@ -33,7 +62,7 @@ lcd.backlight(True) hdmi = tfp410.TFP410() time.sleep_ms(50) -alpha_pal = image.Image(256, 1, sensor.GRAYSCALE) +alpha_pal = image.Image(256, 1, image.GRAYSCALE) for i in range(256): alpha_pal[i] = int(math.pow((i / 255), 2) * 255) @@ -42,18 +71,20 @@ to_max = None def map_g_to_temp(g): - return ((g * (to_max - to_min)) / 255.0) + to_min + return ( + (g * (max_temp_in_celsius - min_temp_in_celsius)) / 255.0 + ) + min_temp_in_celsius +# Kickstart thermal camera capture. +csi1.snapshot(update=False, blocking=True, image=fir_img) + while True: - img = sensor.snapshot() - # ta: Ambient temperature - # ir: Object temperatures (IR array) - # to_min: Minimum object temperature - # to_max: Maximum object temperature - ta, ir, to_min, to_max = fir.read_ir() + img = csi0.snapshot() + + # Capture the thermal image without blocking. + csi1.snapshot(update=False, blocking=False, image=fir_img) - fir.draw_ir(fir_img, ir, color_palette=None) fir_img_size = fir_img.width() * fir_img.height() # Find IR Blobs @@ -65,8 +96,12 @@ while True: # Collect stats into a list of tuples blob_stats = [] for b in blobs: - blob_stats.append((b.rect(), map_g_to_temp(img.get_statistics(thresholds=threshold_list, - roi=b.rect()).mean()))) + blob_stats.append( + (b.rect(), map_g_to_temp(fir_img.get_statistics( + thresholds=threshold_list, roi=b.rect() + ).mean())) + ) + x_scale = img.width() / fir_img.width() y_scale = img.height() / fir_img.height() img.draw_image(fir_img, 0, 0, x_scale=x_scale, y_scale=y_scale, @@ -76,19 +111,17 @@ while True: # Draw stuff on the colored image for b in blobs: - img.draw_rectangle(int(b.rect()[0] * x_scale), int(b.rect()[1] * y_scale), - int(b.rect()[2] * x_scale), int(b.rect()[3] * y_scale)) + img.draw_rectangle(int(b.rect()[0] * x_scale), + int(b.rect()[1] * y_scale), + int(b.rect()[2] * x_scale), + int(b.rect()[3] * y_scale)) img.draw_cross(int(b.cx() * x_scale), int(b.cy() * y_scale)) + for blob_stat in blob_stats: - img.draw_string(int((blob_stat[0][0] * x_scale) + 4), int((blob_stat[0][1] * y_scale) + 1), + img.draw_string(int((blob_stat[0][0] * x_scale) + 4), + int((blob_stat[0][1] * y_scale) + 1), '%.2f C' % blob_stat[1], mono_space=False, scale=2) - # Draw ambient, min and max temperatures. - img.draw_string(4, 0, 'Lepton Temp: %0.2f C' % ta, - color=(255, 255, 255), mono_space=False, scale=2) - img.draw_string(4, 18, 'Min Temp: %0.2f C' % to_min, - color=(255, 255, 255), mono_space=False, scale=2) - img.draw_string(4, 36, 'Max Temp: %0.2f C' % to_max, - color=(255, 255, 255), mono_space=False, scale=2) - - lcd.write(img, hint=(image.BILINEAR | image.CENTER | image.SCALE_ASPECT_KEEP)) + lcd.write(img, hint=( + image.BILINEAR | image.CENTER | image.SCALE_ASPECT_KEEP + ))