From 746b47a601a24be50c24b242fb2e2429253a8fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luy=C3=A1=C5=A1=20=C5=98=C3=ADha?= Date: Mon, 18 Apr 2022 20:40:05 +0200 Subject: [PATCH 01/16] fix micropython breakout_roundlcd rendering of graphics primitives --- libraries/pico_graphics/pico_graphics.hpp | 2 +- .../modules/breakout_roundlcd/breakout_roundlcd.c | 2 +- .../modules/breakout_roundlcd/breakout_roundlcd.cpp | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 4992e8dc..a80857cf 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -87,4 +87,4 @@ namespace pimoroni { void line(Point p1, Point p2); }; -} \ No newline at end of file +} diff --git a/micropython/modules/breakout_roundlcd/breakout_roundlcd.c b/micropython/modules/breakout_roundlcd/breakout_roundlcd.c index 4010fcca..00bc2fb2 100644 --- a/micropython/modules/breakout_roundlcd/breakout_roundlcd.c +++ b/micropython/modules/breakout_roundlcd/breakout_roundlcd.c @@ -75,4 +75,4 @@ const mp_obj_module_t breakout_roundlcd_user_cmodule = { //////////////////////////////////////////////////////////////////////////////////////////////////// MP_REGISTER_MODULE(MP_QSTR_breakout_roundlcd, breakout_roundlcd_user_cmodule, MODULE_BREAKOUT_ROUNDLCD_ENABLED); //////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +//////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/micropython/modules/breakout_roundlcd/breakout_roundlcd.cpp b/micropython/modules/breakout_roundlcd/breakout_roundlcd.cpp index c84ce05c..6a92809f 100644 --- a/micropython/modules/breakout_roundlcd/breakout_roundlcd.cpp +++ b/micropython/modules/breakout_roundlcd/breakout_roundlcd.cpp @@ -474,10 +474,10 @@ mp_obj_t BreakoutRoundLCD_triangle(size_t n_args, const mp_obj_t *pos_args, mp_m int x1 = args[ARG_x1].u_int; int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x1].u_int; - int y2 = args[ARG_y1].u_int; - int x3 = args[ARG_x1].u_int; - int y3 = args[ARG_y1].u_int; + int x2 = args[ARG_x2].u_int; + int y2 = args[ARG_y2].u_int; + int x3 = args[ARG_x3].u_int; + int y3 = args[ARG_y3].u_int; Point p1(x1, y1); Point p2(x2, y2); @@ -504,8 +504,8 @@ mp_obj_t BreakoutRoundLCD_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t int x1 = args[ARG_x1].u_int; int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x1].u_int; - int y2 = args[ARG_y1].u_int; + int x2 = args[ARG_x2].u_int; + int y2 = args[ARG_y2].u_int; Point p1(x1, y1); Point p2(x2, y2); From 6028bc16b6ab5c9c0100b5345fa5923ba6462502 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 19 Apr 2022 13:01:54 +0100 Subject: [PATCH 02/16] VL53L5CX: Initial bringup --- .gitmodules | 4 + drivers/CMakeLists.txt | 1 + drivers/vl53l5cx/CMakeLists.txt | 1 + drivers/vl53l5cx/platform.c | 188 ++++++++++++++++ drivers/vl53l5cx/platform.h | 219 +++++++++++++++++++ drivers/vl53l5cx/src | 1 + drivers/vl53l5cx/vl53l5cx.cmake | 16 ++ drivers/vl53l5cx/vl53l5cx.cpp | 0 drivers/vl53l5cx/vl53l5cx.hpp | 5 + examples/CMakeLists.txt | 1 + examples/breakout_vl53l5cx/CMakeLists.txt | 12 + examples/breakout_vl53l5cx/vl53l5cx_demo.cpp | 41 ++++ 12 files changed, 489 insertions(+) create mode 100644 drivers/vl53l5cx/CMakeLists.txt create mode 100644 drivers/vl53l5cx/platform.c create mode 100644 drivers/vl53l5cx/platform.h create mode 160000 drivers/vl53l5cx/src create mode 100644 drivers/vl53l5cx/vl53l5cx.cmake create mode 100644 drivers/vl53l5cx/vl53l5cx.cpp create mode 100644 drivers/vl53l5cx/vl53l5cx.hpp create mode 100644 examples/breakout_vl53l5cx/CMakeLists.txt create mode 100644 examples/breakout_vl53l5cx/vl53l5cx_demo.cpp diff --git a/.gitmodules b/.gitmodules index 474f7d55..dc560193 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,7 @@ [submodule "micropython/modules/qrcode"] path = micropython/modules/qrcode url = https://github.com/pimoroni/QR-Code-Generator +[submodule "drivers/vl53l5cx/src"] + path = drivers/vl53l5cx/src + url = https://github.com/ST-mirror/VL53L5CX_ULD_driver + branch = lite/en diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 32c6169c..33016316 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -29,3 +29,4 @@ add_subdirectory(hub75) add_subdirectory(uc8151) add_subdirectory(pwm) add_subdirectory(servo) +add_subdirectory(vl53l5cx) diff --git a/drivers/vl53l5cx/CMakeLists.txt b/drivers/vl53l5cx/CMakeLists.txt new file mode 100644 index 00000000..1322c351 --- /dev/null +++ b/drivers/vl53l5cx/CMakeLists.txt @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/vl53l5cx.cmake) \ No newline at end of file diff --git a/drivers/vl53l5cx/platform.c b/drivers/vl53l5cx/platform.c new file mode 100644 index 00000000..5dece719 --- /dev/null +++ b/drivers/vl53l5cx/platform.c @@ -0,0 +1,188 @@ +/******************************************************************************* +* Copyright (c) 2020, STMicroelectronics - All Rights Reserved +* +* This file is part of the VL53L5CX Ultra Lite Driver and is dual licensed, +* either 'STMicroelectronics Proprietary license' +* or 'BSD 3-clause "New" or "Revised" License' , at your option. +* +******************************************************************************** +* +* 'STMicroelectronics Proprietary license' +* +******************************************************************************** +* +* License terms: STMicroelectronics Proprietary in accordance with licensing +* terms at www.st.com/sla0081 +* +* STMicroelectronics confidential +* Reproduction and Communication of this document is strictly prohibited unless +* specifically authorized in writing by STMicroelectronics. +* +* +******************************************************************************** +* +* Alternatively, the VL53L5CX Ultra Lite Driver may be distributed under the +* terms of 'BSD 3-clause "New" or "Revised" License', in which case the +* following provisions apply instead of the ones mentioned above : +* +******************************************************************************** +* +* License terms: BSD 3-clause "New" or "Revised" License. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* +*******************************************************************************/ + + +#include "platform.h" +#include "pico/stdlib.h" + +uint8_t RdByte( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t *p_value) +{ + const uint8_t buf[2] = { + RegisterAdress >> 8, + RegisterAdress & 0xff + }; + i2c_write_blocking(p_platform->i2c, p_platform->address, buf, sizeof(buf), true); + if(i2c_read_blocking(p_platform->i2c, p_platform->address, p_value, 1, false) != PICO_ERROR_GENERIC){ + return 0; + } + + return 255; +} + +uint8_t WrByte( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t value) +{ + const uint8_t buf[3] = { + RegisterAdress >> 8, + RegisterAdress & 0xff, + value, + }; + if(i2c_write_blocking(p_platform->i2c, p_platform->address, buf, sizeof(buf), false) != PICO_ERROR_GENERIC) { + return 0; + } + + return 255; +} + +uint8_t WrMulti( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t *p_values, + uint32_t size) +{ + uint8_t buf[size + 2]; + buf[0] = RegisterAdress >> 8; + buf[1] = RegisterAdress & 0xff; + + memcpy(buf + 2, p_values, size); + + if(i2c_write_blocking(p_platform->i2c, p_platform->address, buf, size + 2, false) != PICO_ERROR_GENERIC) { + return 0; + } + + return 255; +} + +uint8_t RdMulti( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t *p_values, + uint32_t size) +{ + const uint8_t buf[2] = { + RegisterAdress >> 8, + RegisterAdress & 0xff + }; + i2c_write_blocking(p_platform->i2c, p_platform->address, buf, sizeof(buf), true); + if(i2c_read_blocking(p_platform->i2c, p_platform->address, p_values, size, false) != PICO_ERROR_GENERIC){ + return 0; + } + + return 255; +} + +uint8_t Reset_Sensor( + VL53L5CX_Platform *p_platform) +{ + uint8_t status = 0; + + /* (Optional) Need to be implemented by customer. This function returns 0 if OK */ + + /* Set pin LPN to LOW */ + /* Set pin AVDD to LOW */ + /* Set pin VDDIO to LOW */ + WaitMs(p_platform, 100); + + /* Set pin LPN of to HIGH */ + /* Set pin AVDD of to HIGH */ + /* Set pin VDDIO of to HIGH */ + WaitMs(p_platform, 100); + + return status; +} + +void SwapBuffer( + uint8_t *buffer, + uint16_t size) +{ + uint32_t i, tmp; + + /*for(auto i = 0u; i < size / 4u; i++) { + uint32_t *dword = &((uint32_t *)buffer)[i]; + *dword = __builtin_bswap32(*dword); + }*/ + + /* Example of possible implementation using */ + for(i = 0; i < size; i = i + 4) + { + tmp = ( + buffer[i]<<24) + |(buffer[i+1]<<16) + |(buffer[i+2]<<8) + |(buffer[i+3]); + + memcpy(&(buffer[i]), &tmp, 4); + } +} + +uint8_t WaitMs( + VL53L5CX_Platform *p_platform, + uint32_t TimeMs) +{ + sleep_ms(TimeMs); + return 0; +} diff --git a/drivers/vl53l5cx/platform.h b/drivers/vl53l5cx/platform.h new file mode 100644 index 00000000..9a4548e9 --- /dev/null +++ b/drivers/vl53l5cx/platform.h @@ -0,0 +1,219 @@ +/******************************************************************************* +* Copyright (c) 2020, STMicroelectronics - All Rights Reserved +* +* This file is part of the VL53L5CX Ultra Lite Driver and is dual licensed, +* either 'STMicroelectronics Proprietary license' +* or 'BSD 3-clause "New" or "Revised" License' , at your option. +* +******************************************************************************** +* +* 'STMicroelectronics Proprietary license' +* +******************************************************************************** +* +* License terms: STMicroelectronics Proprietary in accordance with licensing +* terms at www.st.com/sla0081 +* +* STMicroelectronics confidential +* Reproduction and Communication of this document is strictly prohibited unless +* specifically authorized in writing by STMicroelectronics. +* +* +******************************************************************************** +* +* Alternatively, the VL53L5CX Ultra Lite Driver may be distributed under the +* terms of 'BSD 3-clause "New" or "Revised" License', in which case the +* following provisions apply instead of the ones mentioned above : +* +******************************************************************************** +* +* License terms: BSD 3-clause "New" or "Revised" License. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* +*******************************************************************************/ + +#ifndef _PLATFORM_H_ +#define _PLATFORM_H_ +#pragma once + +#include +#include + +#include "hardware/i2c.h" + +/** + * @brief Structure VL53L5CX_Platform needs to be filled by the customer, + * depending on his platform. At least, it contains the VL53L5CX I2C address. + * Some additional fields can be added, as descriptors, or platform + * dependencies. Anything added into this structure is visible into the platform + * layer. + */ + +typedef struct +{ + /* To be filled with customer's platform. At least an I2C address/descriptor + * needs to be added */ + /* Example for most standard platform : I2C address of sensor */ + uint16_t address; + i2c_inst_t *i2c; + +} VL53L5CX_Platform; + +/* + * @brief The macro below is used to define the number of target per zone sent + * through I2C. This value can be changed by user, in order to tune I2C + * transaction, and also the total memory size (a lower number of target per + * zone means a lower RAM). The value must be between 1 and 4. + */ + +#define VL53L5CX_NB_TARGET_PER_ZONE 1U + +/* + * @brief The macro below can be used to avoid data conversion into the driver. + * By default there is a conversion between firmware and user data. Using this macro + * allows to use the firmware format instead of user format. The firmware format allows + * an increased precision. + */ + +// #define VL53L5CX_USE_RAW_FORMAT + +/* + * @brief All macro below are used to configure the sensor output. User can + * define some macros if he wants to disable selected output, in order to reduce + * I2C access. + */ + +// #define VL53L5CX_DISABLE_AMBIENT_PER_SPAD +// #define VL53L5CX_DISABLE_NB_SPADS_ENABLED +// #define VL53L5CX_DISABLE_NB_TARGET_DETECTED +// #define VL53L5CX_DISABLE_SIGNAL_PER_SPAD +// #define VL53L5CX_DISABLE_RANGE_SIGMA_MM +// #define VL53L5CX_DISABLE_DISTANCE_MM +// #define VL53L5CX_DISABLE_REFLECTANCE_PERCENT +// #define VL53L5CX_DISABLE_TARGET_STATUS +// #define VL53L5CX_DISABLE_MOTION_INDICATOR + +/** + * @param (VL53L5CX_Platform*) p_platform : Pointer of VL53L5CX platform + * structure. + * @param (uint16_t) Address : I2C location of value to read. + * @param (uint8_t) *p_values : Pointer of value to read. + * @return (uint8_t) status : 0 if OK + */ + +uint8_t RdByte( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t *p_value); + +/** + * @brief Mandatory function used to write one single byte. + * @param (VL53L5CX_Platform*) p_platform : Pointer of VL53L5CX platform + * structure. + * @param (uint16_t) Address : I2C location of value to read. + * @param (uint8_t) value : Pointer of value to write. + * @return (uint8_t) status : 0 if OK + */ + +uint8_t WrByte( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t value); + +/** + * @brief Mandatory function used to read multiples bytes. + * @param (VL53L5CX_Platform*) p_platform : Pointer of VL53L5CX platform + * structure. + * @param (uint16_t) Address : I2C location of values to read. + * @param (uint8_t) *p_values : Buffer of bytes to read. + * @param (uint32_t) size : Size of *p_values buffer. + * @return (uint8_t) status : 0 if OK + */ + +uint8_t RdMulti( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t *p_values, + uint32_t size); + +/** + * @brief Mandatory function used to write multiples bytes. + * @param (VL53L5CX_Platform*) p_platform : Pointer of VL53L5CX platform + * structure. + * @param (uint16_t) Address : I2C location of values to write. + * @param (uint8_t) *p_values : Buffer of bytes to write. + * @param (uint32_t) size : Size of *p_values buffer. + * @return (uint8_t) status : 0 if OK + */ + +uint8_t WrMulti( + VL53L5CX_Platform *p_platform, + uint16_t RegisterAdress, + uint8_t *p_values, + uint32_t size); + +/** + * @brief Optional function, only used to perform an hardware reset of the + * sensor. This function is not used in the API, but it can be used by the host. + * This function is not mandatory to fill if user don't want to reset the + * sensor. + * @param (VL53L5CX_Platform*) p_platform : Pointer of VL53L5CX platform + * structure. + * @return (uint8_t) status : 0 if OK + */ + +uint8_t Reset_Sensor( + VL53L5CX_Platform *p_platform); + +/** + * @brief Mandatory function, used to swap a buffer. The buffer size is always a + * multiple of 4 (4, 8, 12, 16, ...). + * @param (uint8_t*) buffer : Buffer to swap, generally uint32_t + * @param (uint16_t) size : Buffer size to swap + */ + +void SwapBuffer( + uint8_t *buffer, + uint16_t size); +/** + * @brief Mandatory function, used to wait during an amount of time. It must be + * filled as it's used into the API. + * @param (VL53L5CX_Platform*) p_platform : Pointer of VL53L5CX platform + * structure. + * @param (uint32_t) TimeMs : Time to wait in ms. + * @return (uint8_t) status : 0 if wait is finished. + */ + +uint8_t WaitMs( + VL53L5CX_Platform *p_platform, + uint32_t TimeMs); + +#endif // _PLATFORM_H_ \ No newline at end of file diff --git a/drivers/vl53l5cx/src b/drivers/vl53l5cx/src new file mode 160000 index 00000000..834fa4e5 --- /dev/null +++ b/drivers/vl53l5cx/src @@ -0,0 +1 @@ +Subproject commit 834fa4e53119b987ae9357afc9dfacc6ef8261f8 diff --git a/drivers/vl53l5cx/vl53l5cx.cmake b/drivers/vl53l5cx/vl53l5cx.cmake new file mode 100644 index 00000000..085947ce --- /dev/null +++ b/drivers/vl53l5cx/vl53l5cx.cmake @@ -0,0 +1,16 @@ +add_library(vl53l5cx INTERFACE) + +target_sources(vl53l5cx INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/vl53l5cx.cpp + ${CMAKE_CURRENT_LIST_DIR}/platform.c + ${CMAKE_CURRENT_LIST_DIR}/src/VL53L5CX_ULD_API/src/vl53l5cx_api.c + ${CMAKE_CURRENT_LIST_DIR}/src/VL53L5CX_ULD_API/src/vl53l5cx_plugin_detection_thresholds.c +) + +target_include_directories(vl53l5cx INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/src/VL53L5CX_ULD_API/inc +) + +# Pull in pico libraries that we need +target_link_libraries(vl53l5cx INTERFACE pico_stdlib hardware_i2c) diff --git a/drivers/vl53l5cx/vl53l5cx.cpp b/drivers/vl53l5cx/vl53l5cx.cpp new file mode 100644 index 00000000..e69de29b diff --git a/drivers/vl53l5cx/vl53l5cx.hpp b/drivers/vl53l5cx/vl53l5cx.hpp new file mode 100644 index 00000000..811297e6 --- /dev/null +++ b/drivers/vl53l5cx/vl53l5cx.hpp @@ -0,0 +1,5 @@ +#pragma once + +extern "C" { +#include "drivers/vl53l5cx/src/VL53L5CX_ULD_API/inc/vl53l5cx_api.h" +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e3293cb9..d7c8e1e1 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(breakout_as7262) add_subdirectory(breakout_bh1745) add_subdirectory(breakout_icp10125) add_subdirectory(breakout_scd41) +add_subdirectory(breakout_vl53l5cx) add_subdirectory(pico_display) add_subdirectory(pico_display_2) diff --git a/examples/breakout_vl53l5cx/CMakeLists.txt b/examples/breakout_vl53l5cx/CMakeLists.txt new file mode 100644 index 00000000..953461bf --- /dev/null +++ b/examples/breakout_vl53l5cx/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME vl53l5cx_demo) + +add_executable( + ${OUTPUT_NAME} + vl53l5cx_demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_i2c vl53l5cx pimoroni_i2c) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp b/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp new file mode 100644 index 00000000..c47ba126 --- /dev/null +++ b/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp @@ -0,0 +1,41 @@ +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "drivers/vl53l5cx/vl53l5cx.hpp" + +#include "common/pimoroni_i2c.hpp" + +//using namespace pimoroni; + +pimoroni::I2C i2c(4, 5); + +VL53L5CX_Configuration configuration { + .platform = VL53L5CX_Platform{ + .address = 0x29, + .i2c = i2c0 + }, +}; + +int main() { + stdio_init_all(); + + vl53l5cx_init(&configuration); + vl53l5cx_set_ranging_mode(&configuration, VL53L5CX_RANGING_MODE_AUTONOMOUS); + vl53l5cx_set_resolution(&configuration, VL53L5CX_RESOLUTION_4X4); + vl53l5cx_start_ranging(&configuration); + + while(true) { + uint8_t is_ready; + if(vl53l5cx_check_data_ready(&configuration, &is_ready) == VL53L5CX_STATUS_OK) { + if(is_ready){ + VL53L5CX_ResultsData result; + if(vl53l5cx_get_ranging_data(&configuration, &result) == VL53L5CX_STATUS_OK) { + printf("Distance: %dmm\n", result.distance_mm[0]); + } + } + } + sleep_ms(20); + } + + return 0; +} From 637f6d18ca2b70e42108eedc93258b68c7636425 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 19 Apr 2022 17:24:06 +0100 Subject: [PATCH 03/16] VL53L5CX: Class wrapper --- drivers/vl53l5cx/vl53l5cx.cpp | 61 ++++++++++++++++++++ drivers/vl53l5cx/vl53l5cx.hpp | 53 +++++++++++++++++ examples/breakout_vl53l5cx/vl53l5cx_demo.cpp | 33 ++++------- 3 files changed, 126 insertions(+), 21 deletions(-) diff --git a/drivers/vl53l5cx/vl53l5cx.cpp b/drivers/vl53l5cx/vl53l5cx.cpp index e69de29b..78e0b33e 100644 --- a/drivers/vl53l5cx/vl53l5cx.cpp +++ b/drivers/vl53l5cx/vl53l5cx.cpp @@ -0,0 +1,61 @@ +#include "vl53l5cx.hpp" + +namespace pimoroni { + bool VL53L5CX::init() { + uint8_t status = vl53l5cx_init(configuration); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::start_ranging() { + uint8_t status = vl53l5cx_start_ranging(configuration); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::stop_ranging() { + uint8_t status = vl53l5cx_stop_ranging(configuration); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_i2c_address(uint8_t i2c_address) { + /* Must be a 7-bit i2c address */ + uint8_t status = vl53l5cx_set_i2c_address(configuration, i2c_address << 1); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_ranging_mode(RangingMode ranging_mode) { + uint8_t status = vl53l5cx_set_ranging_mode(configuration, (uint8_t)ranging_mode); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_ranging_frequency_hz(uint8_t ranging_frequency_hz) { + uint8_t status = vl53l5cx_set_ranging_frequency_hz(configuration, ranging_frequency_hz); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_resolution(Resolution resolution) { + /* One of VL53L5CX_RESOLUTION_4X4 or VL53L5CX_RESOLUTION_8X8 */ + uint8_t status = vl53l5cx_set_resolution(configuration, (uint8_t)resolution); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_integration_time_ms(uint32_t integration_time_ms) { + /* Integration time between 2ms and 1000ms */ + uint8_t status = vl53l5cx_set_integration_time_ms(configuration, integration_time_ms); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_sharpener_percent(uint8_t sharpener_percent) { + /* Sharpener intensity from 0 to 99 */ + uint8_t status = vl53l5cx_set_sharpener_percent(configuration, sharpener_percent); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_target_order(TargetOrder target_order) { + uint8_t status = vl53l5cx_set_target_order(configuration, (uint8_t)target_order); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_power_mode(PowerMode power_mode) { + uint8_t status = vl53l5cx_set_power_mode(configuration, (uint8_t)power_mode); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::data_ready() { + uint8_t is_ready; + uint8_t status = vl53l5cx_check_data_ready(configuration, &is_ready); + return status == VL53L5CX_STATUS_OK && is_ready; + } + bool VL53L5CX::get_data(ResultsData *results) { + uint8_t status = vl53l5cx_get_ranging_data(configuration, results); + return status == VL53L5CX_STATUS_OK; + } +} \ No newline at end of file diff --git a/drivers/vl53l5cx/vl53l5cx.hpp b/drivers/vl53l5cx/vl53l5cx.hpp index 811297e6..5b3990cf 100644 --- a/drivers/vl53l5cx/vl53l5cx.hpp +++ b/drivers/vl53l5cx/vl53l5cx.hpp @@ -2,4 +2,57 @@ extern "C" { #include "drivers/vl53l5cx/src/VL53L5CX_ULD_API/inc/vl53l5cx_api.h" +} + +#include "common/pimoroni_i2c.hpp" +#include "src/VL53L5CX_ULD_API/inc/vl53l5cx_api.h" + +namespace pimoroni { + class VL53L5CX { + public: + typedef VL53L5CX_ResultsData ResultsData; + enum TargetOrder : uint8_t { + TARGET_ORDER_CLOSEST = VL53L5CX_TARGET_ORDER_CLOSEST, + TARGET_ORDER_STRONGEST = VL53L5CX_TARGET_ORDER_STRONGEST + }; + enum Resolution : uint8_t { + RESOLUTION_4X4 = VL53L5CX_RESOLUTION_4X4, + RESOLUTION_8X8 = VL53L5CX_RESOLUTION_8X8 + }; + enum RangingMode : uint8_t { + RANGING_MODE_CONTINUOUS = VL53L5CX_RANGING_MODE_CONTINUOUS, + RANGING_MODE_AUTONOMOUS = VL53L5CX_RANGING_MODE_AUTONOMOUS + }; + enum PowerMode : uint8_t { + POWER_MODE_SLEEP = VL53L5CX_POWER_MODE_SLEEP, + POWER_MODE_WAKEUP = VL53L5CX_POWER_MODE_WAKEUP + }; + + // 7-bit version of the default address (0x52) + static const uint8_t DEFAULT_ADDRESS = VL53L5CX_DEFAULT_I2C_ADDRESS >> 1; + + VL53L5CX(I2C *i2c, uint8_t i2c_addr=DEFAULT_ADDRESS) { + configuration = new VL53L5CX_Configuration{ + .platform = VL53L5CX_Platform{ + .address = i2c_addr, + .i2c = i2c->get_i2c() + }, + }; + } + bool init(); + bool start_ranging(); + bool stop_ranging(); + bool set_i2c_address(uint8_t i2c_address); + bool set_ranging_mode(RangingMode ranging_mode); + bool set_ranging_frequency_hz(uint8_t ranging_frequency_hz); + bool set_resolution(Resolution resolution); + bool set_integration_time_ms(uint32_t integration_time_ms); + bool set_sharpener_percent(uint8_t sharpener_percent); + bool set_target_order(TargetOrder target_order); + bool set_power_mode(PowerMode power_mode); + bool data_ready(); + bool get_data(ResultsData *results); + private: + VL53L5CX_Configuration *configuration; + }; } \ No newline at end of file diff --git a/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp b/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp index c47ba126..d6e6f5ff 100644 --- a/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp +++ b/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp @@ -5,37 +5,28 @@ #include "common/pimoroni_i2c.hpp" -//using namespace pimoroni; +using namespace pimoroni; -pimoroni::I2C i2c(4, 5); - -VL53L5CX_Configuration configuration { - .platform = VL53L5CX_Platform{ - .address = 0x29, - .i2c = i2c0 - }, -}; +I2C i2c(4, 5); +VL53L5CX vl53l5cx(&i2c); int main() { stdio_init_all(); - vl53l5cx_init(&configuration); - vl53l5cx_set_ranging_mode(&configuration, VL53L5CX_RANGING_MODE_AUTONOMOUS); - vl53l5cx_set_resolution(&configuration, VL53L5CX_RESOLUTION_4X4); - vl53l5cx_start_ranging(&configuration); + vl53l5cx.init(); + vl53l5cx.set_ranging_mode(VL53L5CX::RANGING_MODE_AUTONOMOUS); + vl53l5cx.set_resolution(VL53L5CX::RESOLUTION_4X4); + vl53l5cx.start_ranging(); while(true) { - uint8_t is_ready; - if(vl53l5cx_check_data_ready(&configuration, &is_ready) == VL53L5CX_STATUS_OK) { - if(is_ready){ - VL53L5CX_ResultsData result; - if(vl53l5cx_get_ranging_data(&configuration, &result) == VL53L5CX_STATUS_OK) { - printf("Distance: %dmm\n", result.distance_mm[0]); - } + if(vl53l5cx.data_ready()) { + VL53L5CX::ResultsData result; + if(vl53l5cx.get_data(&result)) { + printf("Distance: %dmm\n", result.distance_mm[0]); } } sleep_ms(20); } return 0; -} +} \ No newline at end of file From f79b2efa9ea52a3af1df36fcf3af4c5fedb842a0 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 20 Apr 2022 14:32:13 +0100 Subject: [PATCH 04/16] VL53L5CX: Fix 32k runtime allocation in platform i2c WrMulti --- drivers/vl53l5cx/platform.c | 57 +++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/drivers/vl53l5cx/platform.c b/drivers/vl53l5cx/platform.c index 5dece719..c3582fe0 100644 --- a/drivers/vl53l5cx/platform.c +++ b/drivers/vl53l5cx/platform.c @@ -104,17 +104,64 @@ uint8_t WrMulti( uint8_t *p_values, uint32_t size) { - uint8_t buf[size + 2]; + uint8_t buf[2]; buf[0] = RegisterAdress >> 8; buf[1] = RegisterAdress & 0xff; - memcpy(buf + 2, p_values, size); + // Send the 16-bit address with no STOP condition + int result = i2c_write_blocking(p_platform->i2c, p_platform->address, buf, sizeof(buf), true); - if(i2c_write_blocking(p_platform->i2c, p_platform->address, buf, size + 2, false) != PICO_ERROR_GENERIC) { - return 0; + // Handle an error early... it gets dicey from here + if(result == PICO_ERROR_GENERIC) return 255; + + // The VL53L5CX does not support "Repeated Start" and the Pico's I2C API doesn't + // let us send more bytes without sending another start condition. + + // The horrow below lets us send out "p_values" followed by a STOP condition, + // without having to copy everything into a temporary buffer. + uint8_t *src = p_values; + + // Send the rest of the data, followed by a STOP condition + // This re-implements the relevant portion of i2c_write_blocking_internal which is NOT sent a timeout check function by i2c_write_blocking + for (int byte_ctr = 0; byte_ctr < size; ++byte_ctr) { + bool last = byte_ctr == size - 1; + p_platform->i2c->hw->data_cmd = + bool_to_bit(last) << I2C_IC_DATA_CMD_STOP_LSB | *src++; + + // Wait until the transmission of the address/data from the internal + // shift register has completed. For this to function correctly, the + // TX_EMPTY_CTRL flag in IC_CON must be set. The TX_EMPTY_CTRL flag + // was set in i2c_init. + do { + tight_loop_contents(); + } while (!(p_platform->i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_EMPTY_BITS)); + + if (p_platform->i2c->hw->tx_abrt_source) { + // Note clearing the abort flag also clears the reason, and + // this instance of flag is clear-on-read! Note also the + // IC_CLR_TX_ABRT register always reads as 0. + p_platform->i2c->hw->clr_tx_abrt; + + // An abort on the LAST byte means things are probably fine + if(last) { + // TODO Could there be an abort while waiting for the STOP + // condition here? If so, additional code would be needed here + // to take care of the abort. + do { + tight_loop_contents(); + } while (!(p_platform->i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_STOP_DET_BITS)); + } else { + // Ooof, unhandled abort. Fail? + return 255; + } + } } - return 255; + // Not sure it matters where we clear this, but by default a "nostop" style write + // will set this flag so the next transaction starts with a "Repeated Start." + p_platform->i2c->restart_on_next = false; + + return 0; } uint8_t RdMulti( From fcd4914cbb28f50cfa5f01fdf47716d17436e54e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 20 Apr 2022 14:33:00 +0100 Subject: [PATCH 05/16] VL53L5CX: MicroPython bindings. --- drivers/vl53l5cx/vl53l5cx.hpp | 4 + .../breakout_vl53l5cx/micropython.cmake | 28 +++ .../modules/breakout_vl53l5cx/vl53l5cx.c | 78 ++++++++ .../modules/breakout_vl53l5cx/vl53l5cx.cpp | 184 ++++++++++++++++++ .../modules/breakout_vl53l5cx/vl53l5cx.h | 24 +++ micropython/modules/micropython.cmake | 1 + 6 files changed, 319 insertions(+) create mode 100644 micropython/modules/breakout_vl53l5cx/micropython.cmake create mode 100644 micropython/modules/breakout_vl53l5cx/vl53l5cx.c create mode 100644 micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp create mode 100644 micropython/modules/breakout_vl53l5cx/vl53l5cx.h diff --git a/drivers/vl53l5cx/vl53l5cx.hpp b/drivers/vl53l5cx/vl53l5cx.hpp index 5b3990cf..874cec3c 100644 --- a/drivers/vl53l5cx/vl53l5cx.hpp +++ b/drivers/vl53l5cx/vl53l5cx.hpp @@ -52,6 +52,10 @@ namespace pimoroni { bool set_power_mode(PowerMode power_mode); bool data_ready(); bool get_data(ResultsData *results); + + VL53L5CX_Configuration* get_configuration() { + return configuration; + } private: VL53L5CX_Configuration *configuration; }; diff --git a/micropython/modules/breakout_vl53l5cx/micropython.cmake b/micropython/modules/breakout_vl53l5cx/micropython.cmake new file mode 100644 index 00000000..08d106df --- /dev/null +++ b/micropython/modules/breakout_vl53l5cx/micropython.cmake @@ -0,0 +1,28 @@ +add_library(usermod_vl53l5cx INTERFACE) + +target_sources(usermod_vl53l5cx INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/vl53l5cx.c + ${CMAKE_CURRENT_LIST_DIR}/vl53l5cx.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/vl53l5cx.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/platform.c + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/src/VL53L5CX_ULD_API/src/vl53l5cx_api.c + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/src/VL53L5CX_ULD_API/src/vl53l5cx_plugin_detection_thresholds.c +) + +target_include_directories(usermod_vl53l5cx INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/ + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/src/VL53L5CX_ULD_API/inc +) + +target_compile_definitions(usermod_vl53l5cx INTERFACE + MODULE_VL53L5CX_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_vl53l5cx) + +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/vl53l5cx.c + PROPERTIES COMPILE_FLAGS + "-Wno-discarded-qualifiers -Wno-implicit-int" +) \ No newline at end of file diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.c b/micropython/modules/breakout_vl53l5cx/vl53l5cx.c new file mode 100644 index 00000000..8b614845 --- /dev/null +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.c @@ -0,0 +1,78 @@ +#include "vl53l5cx.h" + + +MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX___del___obj, VL53L5CX___del__); + +MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX_start_ranging_obj, VL53L5CX_start_ranging); +MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX_stop_ranging_obj, VL53L5CX_stop_ranging); + +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_i2c_address_obj, VL53L5CX_set_i2c_address); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_ranging_mode_obj, VL53L5CX_set_ranging_mode); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_ranging_frequency_hz_obj, VL53L5CX_set_ranging_frequency_hz); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_resolution_obj, VL53L5CX_set_resolution); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_integration_time_ms_obj, VL53L5CX_set_integration_time_ms); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_sharpener_percent_obj, VL53L5CX_set_sharpener_percent); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_target_order_obj, VL53L5CX_set_target_order); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_power_mode_obj, VL53L5CX_set_power_mode); + +MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX_data_ready_obj, VL53L5CX_data_ready); +MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX_get_data_obj, VL53L5CX_get_data); + + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t VL53L5CX_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&VL53L5CX___del___obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_ranging), MP_ROM_PTR(&VL53L5CX_start_ranging_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_ranging), MP_ROM_PTR(&VL53L5CX_stop_ranging_obj) }, + + { MP_ROM_QSTR(MP_QSTR_set_i2c_address), MP_ROM_PTR(&VL53L5CX_set_i2c_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_ranging_mode), MP_ROM_PTR(&VL53L5CX_set_ranging_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_ranging_frequency_hz), MP_ROM_PTR(&VL53L5CX_set_ranging_frequency_hz_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_resolution), MP_ROM_PTR(&VL53L5CX_set_resolution_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_integration_time_ms), MP_ROM_PTR(&VL53L5CX_set_integration_time_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_sharpener_percent), MP_ROM_PTR(&VL53L5CX_set_sharpener_percent_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_target_order), MP_ROM_PTR(&VL53L5CX_set_target_order_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_power_mode), MP_ROM_PTR(&VL53L5CX_set_power_mode_obj) }, + + { MP_ROM_QSTR(MP_QSTR_data_ready), MP_ROM_PTR(&VL53L5CX_data_ready_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_data), MP_ROM_PTR(&VL53L5CX_get_data_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(VL53L5CX_locals_dict, VL53L5CX_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t VL53L5CX_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_vl53l5cx, + .print = VL53L5CX_print, + .make_new = VL53L5CX_make_new, + .locals_dict = (mp_obj_dict_t*)&VL53L5CX_locals_dict, +}; + +/***** Module Globals *****/ +STATIC const mp_map_elem_t vl53l5cx_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_vl53l5cx) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_VL53L5CX), (mp_obj_t)&VL53L5CX_type }, + + { MP_ROM_QSTR(MP_QSTR_TARGET_ORDER_CLOSEST), MP_ROM_INT(VL53L5CX_TARGET_ORDER_CLOSEST) }, + { MP_ROM_QSTR(MP_QSTR_TARGET_ORDER_STRONGEST), MP_ROM_INT(VL53L5CX_TARGET_ORDER_STRONGEST) }, + + { MP_ROM_QSTR(MP_QSTR_RESOLUTION_4X4), MP_ROM_INT(VL53L5CX_RESOLUTION_4X4) }, + { MP_ROM_QSTR(MP_QSTR_RESOLUTION_8X8), MP_ROM_INT(VL53L5CX_RESOLUTION_8X8) }, + + { MP_ROM_QSTR(MP_QSTR_RANGING_MODE_CONTINUOUS), MP_ROM_INT(VL53L5CX_RANGING_MODE_CONTINUOUS) }, + { MP_ROM_QSTR(MP_QSTR_RANGING_MODE_AUTONOMOUS), MP_ROM_INT(VL53L5CX_RANGING_MODE_AUTONOMOUS) }, + + { MP_ROM_QSTR(MP_QSTR_POWER_MODE_SLEEP), MP_ROM_INT(VL53L5CX_POWER_MODE_SLEEP) }, + { MP_ROM_QSTR(MP_QSTR_POWER_MODE_WAKEUP), MP_ROM_INT(VL53L5CX_POWER_MODE_WAKEUP) }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_vl53l5cx_globals, vl53l5cx_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t vl53l5cx_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_vl53l5cx_globals, +}; + +/***** Module Registration: as "breakout_vl53l5cx" *****/ +MP_REGISTER_MODULE(MP_QSTR_breakout_vl53l5cx, vl53l5cx_user_cmodule, MODULE_VL53L5CX_ENABLED); diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp b/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp new file mode 100644 index 00000000..2a736ed9 --- /dev/null +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp @@ -0,0 +1,184 @@ +#include +#include "vl53l5cx.hpp" +#include "pico/multicore.h" + +#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) + + +extern "C" { +#include "vl53l5cx.h" +#include "pimoroni_i2c.h" + +typedef struct _mp_obj_float_t { + mp_obj_base_t base; + mp_float_t value; +} mp_obj_float_t; + +const mp_obj_float_t const_float_1 = {{&mp_type_float}, 1.0f}; + +/***** I2C Struct *****/ +typedef struct _PimoroniI2C_obj_t { + mp_obj_base_t base; + pimoroni::I2C *i2c; +} _PimoroniI2C_obj_t; + + +/***** Variables Struct *****/ +typedef struct _VL53L5CX_obj_t { + mp_obj_base_t base; + _PimoroniI2C_obj_t *i2c; + pimoroni::VL53L5CX* breakout; +} _VL53L5CX_obj_t; + + +/***** Print *****/ +void VL53L5CX_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; // Unused input parameter + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + mp_print_str(print, "VL53L5CX( "); + + + mp_print_str(print, "i2c = "); + mp_obj_print_helper(print, mp_obj_new_int((self->breakout->get_configuration()->platform.i2c == i2c0) ? 0 : 1), PRINT_REPR); + + mp_print_str(print, " addr = "); + mp_obj_print_helper(print, mp_obj_new_int(self->breakout->get_configuration()->platform.address), PRINT_REPR); + + mp_print_str(print, " )"); +} + +/***** Destructor ******/ +mp_obj_t VL53L5CX___del__(mp_obj_t self_in) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->stop_ranging(); + delete self->breakout; + return mp_const_none; +} + +/***** Constructor *****/ +mp_obj_t VL53L5CX_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + _VL53L5CX_obj_t *self = nullptr; + + enum { + ARG_i2c, + ARG_addr + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_OBJ, {.u_obj = nullptr} }, + { MP_QSTR_addr, MP_ARG_INT, {.u_int = pimoroni::VL53L5CX::DEFAULT_ADDRESS} } + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!MP_OBJ_IS_TYPE(args[ARG_i2c].u_obj, &PimoroniI2C_type)) { + mp_raise_ValueError(MP_ERROR_TEXT("VL53L5CX: Bad i2C object")); + return mp_const_none; + } + + _PimoroniI2C_obj_t *i2c = (_PimoroniI2C_obj_t *)MP_OBJ_TO_PTR(args[ARG_i2c].u_obj); + int addr = args[ARG_addr].u_int; + + self = m_new_obj_with_finaliser(_VL53L5CX_obj_t); + self->base.type = &VL53L5CX_type; + self->i2c = i2c; + self->breakout = new pimoroni::VL53L5CX(i2c->i2c, addr); + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: error initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +mp_obj_t VL53L5CX_start_ranging(mp_obj_t self_in) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->start_ranging(); + return mp_const_none; +} + +mp_obj_t VL53L5CX_stop_ranging(mp_obj_t self_in) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->stop_ranging(); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_i2c_address(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_i2c_address(mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_ranging_mode(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_ranging_mode((pimoroni::VL53L5CX::RangingMode)mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_ranging_frequency_hz(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_ranging_frequency_hz(mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_resolution(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_resolution((pimoroni::VL53L5CX::Resolution)mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_integration_time_ms(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_integration_time_ms(mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_sharpener_percent(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_sharpener_percent(mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_target_order(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_target_order((pimoroni::VL53L5CX::TargetOrder)mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_power_mode(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + self->breakout->set_power_mode((pimoroni::VL53L5CX::PowerMode)mp_obj_get_int(value)); + return mp_const_none; +} + +mp_obj_t VL53L5CX_data_ready(mp_obj_t self_in) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + return self->breakout->data_ready() ? mp_const_true : mp_const_false; +} + +mp_obj_t VL53L5CX_get_data(mp_obj_t self_in) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + pimoroni::VL53L5CX::ResultsData results; + self->breakout->get_data(&results); + + static const int TUPLE_SIZE = VL53L5CX_RESOLUTION_8X8 * VL53L5CX_NB_TARGET_PER_ZONE; + + mp_obj_t tuple_distance_mm[TUPLE_SIZE]; + mp_obj_t tuple_reflectance[TUPLE_SIZE]; + + // Build a tuple of motion data + for(int i = 0u; i < TUPLE_SIZE; i++) { + tuple_distance_mm[i] = mp_obj_new_int(results.distance_mm[i]); + tuple_reflectance[i] = mp_obj_new_int(results.reflectance[i]); + } + + mp_obj_t tuple[3]; + tuple[0] = mp_obj_new_int(results.silicon_temp_degc); + tuple[1] = mp_obj_new_tuple(TUPLE_SIZE, tuple_distance_mm); + tuple[2] = mp_obj_new_tuple(TUPLE_SIZE, tuple_reflectance); + + return mp_obj_new_tuple(3, tuple); +} + +} \ No newline at end of file diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.h b/micropython/modules/breakout_vl53l5cx/vl53l5cx.h new file mode 100644 index 00000000..50470a74 --- /dev/null +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.h @@ -0,0 +1,24 @@ +#include "py/runtime.h" +#include "drivers/vl53l5cx/src/VL53L5CX_ULD_API/inc/vl53l5cx_api.h" + +extern const mp_obj_type_t VL53L5CX_type; + +extern void VL53L5CX_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); + +extern mp_obj_t VL53L5CX___del__(mp_obj_t self_in); +extern mp_obj_t VL53L5CX_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); + +extern mp_obj_t VL53L5CX_start_ranging(mp_obj_t self_in); +extern mp_obj_t VL53L5CX_stop_ranging(mp_obj_t self_in); + +extern mp_obj_t VL53L5CX_set_i2c_address(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_ranging_mode(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_ranging_frequency_hz(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_resolution(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_integration_time_ms(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_sharpener_percent(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_target_order(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_power_mode(mp_obj_t self_in, mp_obj_t value); + +extern mp_obj_t VL53L5CX_data_ready(mp_obj_t self_in); +extern mp_obj_t VL53L5CX_get_data(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/micropython.cmake b/micropython/modules/micropython.cmake index 8aa983c2..ee44e1a5 100644 --- a/micropython/modules/micropython.cmake +++ b/micropython/modules/micropython.cmake @@ -29,6 +29,7 @@ include(breakout_bme280/micropython) include(breakout_bmp280/micropython) include(breakout_icp10125/micropython) include(breakout_scd41/micropython) +include(breakout_vl53l5cx/micropython) include(pico_scroll/micropython) include(pico_rgb_keypad/micropython) From 4b86faaf129c0045bf326e1880318576d55d95f7 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 20 Apr 2022 19:18:53 +0100 Subject: [PATCH 06/16] VL53L5CX: Fix memory leak. Add get_resolution. --- drivers/vl53l5cx/vl53l5cx.cpp | 8 ++++++++ drivers/vl53l5cx/vl53l5cx.hpp | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/vl53l5cx/vl53l5cx.cpp b/drivers/vl53l5cx/vl53l5cx.cpp index 78e0b33e..b550d71b 100644 --- a/drivers/vl53l5cx/vl53l5cx.cpp +++ b/drivers/vl53l5cx/vl53l5cx.cpp @@ -29,8 +29,16 @@ namespace pimoroni { bool VL53L5CX::set_resolution(Resolution resolution) { /* One of VL53L5CX_RESOLUTION_4X4 or VL53L5CX_RESOLUTION_8X8 */ uint8_t status = vl53l5cx_set_resolution(configuration, (uint8_t)resolution); + if(status == VL53L5CX_STATUS_OK) { + this->resolution = resolution; + } return status == VL53L5CX_STATUS_OK; } + VL53L5CX::Resolution VL53L5CX::get_resolution() { + //Resolution resolution = RESOLUTION_4X4; + //vl53l5cx_get_resolution(configuration, (uint8_t *)&resolution); + return this->resolution; + } bool VL53L5CX::set_integration_time_ms(uint32_t integration_time_ms) { /* Integration time between 2ms and 1000ms */ uint8_t status = vl53l5cx_set_integration_time_ms(configuration, integration_time_ms); diff --git a/drivers/vl53l5cx/vl53l5cx.hpp b/drivers/vl53l5cx/vl53l5cx.hpp index 874cec3c..af58917b 100644 --- a/drivers/vl53l5cx/vl53l5cx.hpp +++ b/drivers/vl53l5cx/vl53l5cx.hpp @@ -39,13 +39,19 @@ namespace pimoroni { }, }; } + ~VL53L5CX() { + delete configuration; + } bool init(); bool start_ranging(); bool stop_ranging(); bool set_i2c_address(uint8_t i2c_address); bool set_ranging_mode(RangingMode ranging_mode); bool set_ranging_frequency_hz(uint8_t ranging_frequency_hz); + bool set_resolution(Resolution resolution); + Resolution get_resolution(); + bool set_integration_time_ms(uint32_t integration_time_ms); bool set_sharpener_percent(uint8_t sharpener_percent); bool set_target_order(TargetOrder target_order); @@ -58,5 +64,6 @@ namespace pimoroni { } private: VL53L5CX_Configuration *configuration; + Resolution resolution = RESOLUTION_8X8; }; } \ No newline at end of file From 70637d0f0eca3e7e4f5c8e9bab737db43c2f2e69 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 20 Apr 2022 19:19:40 +0100 Subject: [PATCH 07/16] VL53L5CX: MicroPython error handling and attrtuple data. --- .../modules/breakout_vl53l5cx/vl53l5cx.cpp | 121 ++++++++++++++---- 1 file changed, 94 insertions(+), 27 deletions(-) diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp b/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp index 2a736ed9..89f85703 100644 --- a/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp @@ -50,7 +50,7 @@ void VL53L5CX_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t k /***** Destructor ******/ mp_obj_t VL53L5CX___del__(mp_obj_t self_in) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->stop_ranging(); + //self->breakout->stop_ranging(); // i2c object might have been deleted already? delete self->breakout; return mp_const_none; } @@ -94,61 +94,91 @@ mp_obj_t VL53L5CX_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw mp_obj_t VL53L5CX_start_ranging(mp_obj_t self_in) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->start_ranging(); + bool status = self->breakout->start_ranging(); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: start_ranging error"); + } return mp_const_none; } mp_obj_t VL53L5CX_stop_ranging(mp_obj_t self_in) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->stop_ranging(); + bool status = self->breakout->stop_ranging(); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: stop_ranging error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_i2c_address(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_i2c_address(mp_obj_get_int(value)); + bool status = self->breakout->set_i2c_address(mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_i2c_address error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_ranging_mode(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_ranging_mode((pimoroni::VL53L5CX::RangingMode)mp_obj_get_int(value)); + bool status = self->breakout->set_ranging_mode((pimoroni::VL53L5CX::RangingMode)mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_ranging_mode error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_ranging_frequency_hz(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_ranging_frequency_hz(mp_obj_get_int(value)); + bool status = self->breakout->set_ranging_frequency_hz(mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_ranging_frequency_hz error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_resolution(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_resolution((pimoroni::VL53L5CX::Resolution)mp_obj_get_int(value)); + bool status = self->breakout->set_resolution((pimoroni::VL53L5CX::Resolution)mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_resolution error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_integration_time_ms(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_integration_time_ms(mp_obj_get_int(value)); + bool status = self->breakout->set_integration_time_ms(mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_integration_time_ms error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_sharpener_percent(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_sharpener_percent(mp_obj_get_int(value)); + bool status = self->breakout->set_sharpener_percent(mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_sharpener_percent error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_target_order(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_target_order((pimoroni::VL53L5CX::TargetOrder)mp_obj_get_int(value)); + bool status = self->breakout->set_target_order((pimoroni::VL53L5CX::TargetOrder)mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_target_order error"); + } return mp_const_none; } mp_obj_t VL53L5CX_set_power_mode(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); - self->breakout->set_power_mode((pimoroni::VL53L5CX::PowerMode)mp_obj_get_int(value)); + bool status = self->breakout->set_power_mode((pimoroni::VL53L5CX::PowerMode)mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_power_mode error"); + } return mp_const_none; } @@ -160,25 +190,62 @@ mp_obj_t VL53L5CX_data_ready(mp_obj_t self_in) { mp_obj_t VL53L5CX_get_data(mp_obj_t self_in) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); pimoroni::VL53L5CX::ResultsData results; - self->breakout->get_data(&results); - - static const int TUPLE_SIZE = VL53L5CX_RESOLUTION_8X8 * VL53L5CX_NB_TARGET_PER_ZONE; - - mp_obj_t tuple_distance_mm[TUPLE_SIZE]; - mp_obj_t tuple_reflectance[TUPLE_SIZE]; - - // Build a tuple of motion data - for(int i = 0u; i < TUPLE_SIZE; i++) { - tuple_distance_mm[i] = mp_obj_new_int(results.distance_mm[i]); - tuple_reflectance[i] = mp_obj_new_int(results.reflectance[i]); + bool status = self->breakout->get_data(&results); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: get_data error"); } - mp_obj_t tuple[3]; - tuple[0] = mp_obj_new_int(results.silicon_temp_degc); - tuple[1] = mp_obj_new_tuple(TUPLE_SIZE, tuple_distance_mm); - tuple[2] = mp_obj_new_tuple(TUPLE_SIZE, tuple_reflectance); + // Get the current resolution so we only look at valid results. + // This is so that our average distance works out and doesn't include bogus data. + int scale = (uint8_t)self->breakout->get_resolution(); + int tuple_size = scale * VL53L5CX_NB_TARGET_PER_ZONE; - return mp_obj_new_tuple(3, tuple); + // TODO This doesn't really handle VL53L5CX_NB_TARGET_PER_ZONE > 1 gracefully + // the zone data should be split into separate tuples + + mp_obj_t tuple_distance_mm[tuple_size]; + mp_obj_t tuple_reflectance[tuple_size]; + + int32_t average_distance = 0; + int32_t average_reflectance = 0; + + // Build a tuple of motion data + for(int i = 0u; i < tuple_size; i++) { + tuple_distance_mm[i] = mp_obj_new_int(results.distance_mm[i]); + tuple_reflectance[i] = mp_obj_new_int(results.reflectance[i]); + average_distance += results.distance_mm[i]; + average_reflectance += results.reflectance[i]; + } + + average_distance /= tuple_size; + average_reflectance /= tuple_size; + + // TODO motion data is all zeros, why? + + /*mp_obj_t tuple_motion_data[32]; + + for(int i = 0u; i < 32; i++) { + tuple_motion_data[i] = mp_obj_new_int(results.motion_indicator.motion[i]); + } + + mp_obj_t tuple_motion[] = { + mp_obj_new_int(results.motion_indicator.global_indicator_1), + mp_obj_new_int(results.motion_indicator.global_indicator_2), + mp_obj_new_tuple(sizeof(tuple_motion_data) / sizeof(mp_obj_t), tuple_motion_data) + };*/ + + mp_obj_t tuple[] = { + mp_obj_new_int(average_distance), // Average distance + mp_obj_new_int(average_reflectance), // Average reflectance + //mp_obj_new_tuple(sizeof(tuple_motion) / sizeof(mp_obj_t), tuple_motion), // Motion data + mp_obj_new_int(tuple_size), // Number of results + mp_obj_new_tuple(tuple_size, tuple_distance_mm), // Full distance results + mp_obj_new_tuple(tuple_size, tuple_reflectance) // Full reflectange results + }; + + STATIC const qstr tuple_fields[] = {MP_QSTR_distance_avg, MP_QSTR_reflectance_avg, MP_QSTR_results, MP_QSTR_distance, MP_QSTR_reflectance}; + + return mp_obj_new_attrtuple(tuple_fields, sizeof(tuple) / sizeof(mp_obj_t), tuple); } } \ No newline at end of file From 02d3d853f8ce27a1160dab2f6e5480013ea37844 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 20 Apr 2022 19:19:51 +0100 Subject: [PATCH 08/16] VL53L5CX: MicroPython example. --- .../breakout_vl53l5cx/vl53l5cx_demo.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py diff --git a/micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py b/micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py new file mode 100644 index 00000000..d38296ba --- /dev/null +++ b/micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py @@ -0,0 +1,32 @@ +import pimoroni_i2c +import breakout_vl53l5cx +import time + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +# Sensor startup time is proportional to i2c baudrate +# HOWEVER many sensors may not run at > 400KHz (400000) +i2c = pimoroni_i2c.PimoroniI2C(**PINS_BREAKOUT_GARDEN, baudrate=2_000_000) + +print("Starting up sensor...") +t_sta = time.ticks_ms() +sensor = breakout_vl53l5cx.VL53L5CX(i2c) +t_end = time.ticks_ms() +print("Done in {}ms...".format(t_end - t_sta)) + +# Make sure to set resolution and other settings *before* you start ranging +sensor.set_resolution(breakout_vl53l5cx.RESOLUTION_4X4) +sensor.start_ranging() + +while True: + if sensor.data_ready(): + # "data" is a namedtuple (attrtuple technically) + # it includes average readings as "distance_avg" and "reflectance_avg"a + # plus a full 4x4 or 8x8 set of readings (as a 1d tuple) for both values. + data = sensor.get_data() + print("{}mm {}% (avg: {}mm {}%)".format( + data.distance[0], + data.reflectance[0], + data.distance_avg, + data.reflectance_avg)) From 7c47b1c53fba52da69236442c50081d6661bbefe Mon Sep 17 00:00:00 2001 From: Andy Piper Date: Sat, 23 Apr 2022 21:47:09 +0100 Subject: [PATCH 09/16] Updating the samples README to reflect current contents --- micropython/examples/badger2040/README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/micropython/examples/badger2040/README.md b/micropython/examples/badger2040/README.md index adfa1167..22a9bcba 100644 --- a/micropython/examples/badger2040/README.md +++ b/micropython/examples/badger2040/README.md @@ -14,6 +14,7 @@ - [Image](#image) - [QR gen](#qr-gen) - [Launcher](#launcher) + - [Conway](#conway) ## Function Examples @@ -56,10 +57,12 @@ python3 convert.py --binary --resize image_file_1.png image_file_2.png image_fil A checklist application, letting you navigate through items and tick each of them off. +* `checklist.txt` - A text file containing the titles of items for the list. + ### Clock [clock.py](clock.py) -A simple clock showing the time and date, that uses the E Ink's fast speed to update every second +A simple clock showing the time and date, that uses the E Ink's fast speed to update every second. ### E-Book [ebook.py](ebook.py) @@ -83,10 +86,17 @@ python3 convert.py --binary --resize image_file_1.png image_file_2.png image_fil ### QR gen [qrgen.py](qrgen.py) -This application looks for a file on your MicroPython drive: -- `qrcode.txt` - A text file containing 9 lines. The first line should be a URL which will be converted into and displayed as a QR code. Up to 8 more lines of information can be added, which will be shown as plain text to the right of the QR code. +Displays and lets you cycle through multiple QR codes, with configuration stored in text files within the MicroPython device's `/qrcodes` directory. + +- `/qrcodes/qrcode.txt` - A text file containing 9 lines. The first line should be a URL which will be converted into and displayed as a QR code. Up to 8 more lines of information can be added, which will be shown as plain text to the right of the QR code. +- `/qrcodes/*.txt` - additional text files can be created using the same format. All text files can be cycled through. ### Launcher [launcher.py](launcher.py) -A launcher-style application, that provide a menu of other applications that can be loaded, as well as information such as battery level. +A launcher-style application, providing a menu of other applications that can be loaded, as well as information such as battery level. + +### Conway +[conway.py](conway.py) + +Conway's classic Game of Life, implemented on the Badger. Note: this application is *not* linked from the Launcher by default - it can be run directly using Thonny or your MicroPython editor of choice, or you can modify the Launcher to add it (you'll want to update `launchericons.png` as well) From cbd28ca432033088ced3a4a46e5708a8a571caf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20=C5=98=C3=ADha?= Date: Tue, 19 Apr 2022 14:55:12 +0200 Subject: [PATCH 10/16] add micropython drawing primitives demo to breakout_roundlcd --- .../drawing_primitives_demo.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 micropython/examples/breakout_roundlcd/drawing_primitives_demo.py diff --git a/micropython/examples/breakout_roundlcd/drawing_primitives_demo.py b/micropython/examples/breakout_roundlcd/drawing_primitives_demo.py new file mode 100644 index 00000000..86bec1bc --- /dev/null +++ b/micropython/examples/breakout_roundlcd/drawing_primitives_demo.py @@ -0,0 +1,101 @@ +from breakout_roundlcd import BreakoutRoundLCD + + +width = BreakoutRoundLCD.WIDTH +height = BreakoutRoundLCD.HEIGHT + +display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565) +display = BreakoutRoundLCD(display_buffer) + +display.set_backlight(1) + +while True: + display.set_pen(0, 0, 0) + display.clear() + + # circle + display.set_pen(255, 0, 0) + display.circle( + width // 5, # center point x + height // 3, # center point y + 16, # radius + ) + + # rectangle + display.set_pen(255, 255, 0) + display.rectangle( + int((width * 2 / 5) - 16), # starting point x + int(height // 3) - 8, # starting point y + 32, # width + 16, # height + ) + + # triangle + display.set_pen(0, 255, 0) + display.triangle( + int(width * 3 / 5), int(height // 3) - 16, # point 1 x, y + int(width * 3 / 5) - 16, int(height // 3) + 16, # point 2 x, y + int(width * 3 / 5) + 16, int(height // 3) + 16, # point 3 x, y + ) + + # character + display.set_pen(0, 255, 255) + display.character( + 64, # int character code + int(width * 4 / 5 - 16), # box starting point x + int(height // 3) - 16, # box starting point y + 4, # scale - not required, default is 2 + ) + + # pixel span + display.set_pen(255, 255, 255) + display.pixel_span( + int(width * 1 / 5), # starting point x + int(height * 2.5 / 5), # starting point y + 130, # length + ) + + # text + display.set_pen(0, 0, 255) + display.text( + 'test text', # text + int(width // 5), # box starting point x + int(height * 3 / 5), # box starting point y + True, # word wrap + 6, # scale - not required, default is 2 + ) + + # lines + display.set_pen(255, 0, 255) + display.line( + 0, # staring point x + int(height / 2), # staring point y + width, # end point x + height, # end point y + ) + display.line( + width, # staring point x + int(height / 2), # staring point y + 0, # end point x + height, # end point y + ) + + # set clip + display.set_clip( + int(width * 2 / 5), + int(height * 2 / 5), + int(width * 1 / 5), + int(height * 1 / 5) + ) + + # draw a clipped circle + display.circle( + int(width * 2 / 5), + int(height * 2 / 5), + 32, + ) + + # remove clip + display.remove_clip() + + display.update() From 4de8762381d346e888f21479ca4fb61f11fc182a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20=C5=98=C3=ADha?= Date: Tue, 19 Apr 2022 16:55:15 +0200 Subject: [PATCH 11/16] fix micorpython line and triangle primitives for breakout LCDs --- .../breakout_colourlcd160x80.cpp | 12 ++++++------ .../breakout_colourlcd240x240.cpp | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp b/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp index 47f9092c..03ef5175 100644 --- a/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp +++ b/micropython/modules/breakout_colourlcd160x80/breakout_colourlcd160x80.cpp @@ -493,10 +493,10 @@ mp_obj_t BreakoutColourLCD160x80_triangle(size_t n_args, const mp_obj_t *pos_arg int x1 = args[ARG_x1].u_int; int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x1].u_int; - int y2 = args[ARG_y1].u_int; - int x3 = args[ARG_x1].u_int; - int y3 = args[ARG_y1].u_int; + int x2 = args[ARG_x2].u_int; + int y2 = args[ARG_y2].u_int; + int x3 = args[ARG_x3].u_int; + int y3 = args[ARG_y3].u_int; Point p1(x1, y1); Point p2(x2, y2); @@ -523,8 +523,8 @@ mp_obj_t BreakoutColourLCD160x80_line(size_t n_args, const mp_obj_t *pos_args, m int x1 = args[ARG_x1].u_int; int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x1].u_int; - int y2 = args[ARG_y1].u_int; + int x2 = args[ARG_x2].u_int; + int y2 = args[ARG_y2].u_int; Point p1(x1, y1); Point p2(x2, y2); diff --git a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp b/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp index 56650064..670029e2 100644 --- a/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp +++ b/micropython/modules/breakout_colourlcd240x240/breakout_colourlcd240x240.cpp @@ -493,10 +493,10 @@ mp_obj_t BreakoutColourLCD240x240_triangle(size_t n_args, const mp_obj_t *pos_ar int x1 = args[ARG_x1].u_int; int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x1].u_int; - int y2 = args[ARG_y1].u_int; - int x3 = args[ARG_x1].u_int; - int y3 = args[ARG_y1].u_int; + int x2 = args[ARG_x2].u_int; + int y2 = args[ARG_y2].u_int; + int x3 = args[ARG_x3].u_int; + int y3 = args[ARG_y3].u_int; Point p1(x1, y1); Point p2(x2, y2); @@ -523,8 +523,8 @@ mp_obj_t BreakoutColourLCD240x240_line(size_t n_args, const mp_obj_t *pos_args, int x1 = args[ARG_x1].u_int; int y1 = args[ARG_y1].u_int; - int x2 = args[ARG_x1].u_int; - int y2 = args[ARG_y1].u_int; + int x2 = args[ARG_x2].u_int; + int y2 = args[ARG_y2].u_int; Point p1(x1, y1); Point p2(x2, y2); From 3afead416a7df9ba65d03ac658a8b038544f5649 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 28 Apr 2022 14:38:34 +0100 Subject: [PATCH 12/16] VL53L5CX: Bringup Motion and add example. --- drivers/vl53l5cx/vl53l5cx.cpp | 18 +++++++++- drivers/vl53l5cx/vl53l5cx.hpp | 10 ++++++ examples/breakout_vl53l5cx/vl53l5cx_demo.cpp | 5 ++- .../breakout_vl53l5cx/vl53l5cx_demo.py | 2 +- .../breakout_vl53l5cx/vl53l5cx_motion.py | 35 +++++++++++++++++++ .../breakout_vl53l5cx/micropython.cmake | 1 + .../modules/breakout_vl53l5cx/vl53l5cx.c | 6 ++++ .../modules/breakout_vl53l5cx/vl53l5cx.cpp | 30 ++++++++++++---- .../modules/breakout_vl53l5cx/vl53l5cx.h | 3 ++ 9 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 micropython/examples/breakout_vl53l5cx/vl53l5cx_motion.py diff --git a/drivers/vl53l5cx/vl53l5cx.cpp b/drivers/vl53l5cx/vl53l5cx.cpp index b550d71b..d962f21b 100644 --- a/drivers/vl53l5cx/vl53l5cx.cpp +++ b/drivers/vl53l5cx/vl53l5cx.cpp @@ -2,9 +2,17 @@ namespace pimoroni { bool VL53L5CX::init() { + if(!is_alive()) { + return false; + } uint8_t status = vl53l5cx_init(configuration); return status == VL53L5CX_STATUS_OK; } + bool VL53L5CX::is_alive() { + uint8_t is_alive = 0; + uint8_t status = vl53l5cx_is_alive(configuration, &is_alive); + return is_alive == 1 && status == VL53L5CX_STATUS_OK; + } bool VL53L5CX::start_ranging() { uint8_t status = vl53l5cx_start_ranging(configuration); return status == VL53L5CX_STATUS_OK; @@ -13,9 +21,17 @@ namespace pimoroni { uint8_t status = vl53l5cx_stop_ranging(configuration); return status == VL53L5CX_STATUS_OK; } + bool VL53L5CX::enable_motion_indicator(Resolution resolution) { + uint8_t status = vl53l5cx_motion_indicator_init(configuration, motion_configuration, resolution); + return status == VL53L5CX_STATUS_OK; + } + bool VL53L5CX::set_motion_distance(uint16_t distance_min, uint16_t distance_max) { + uint8_t status = vl53l5cx_motion_indicator_set_distance_motion(configuration, motion_configuration, distance_min, distance_max); + return status == VL53L5CX_STATUS_OK; + } bool VL53L5CX::set_i2c_address(uint8_t i2c_address) { /* Must be a 7-bit i2c address */ - uint8_t status = vl53l5cx_set_i2c_address(configuration, i2c_address << 1); + uint8_t status = vl53l5cx_set_i2c_address(configuration, i2c_address); return status == VL53L5CX_STATUS_OK; } bool VL53L5CX::set_ranging_mode(RangingMode ranging_mode) { diff --git a/drivers/vl53l5cx/vl53l5cx.hpp b/drivers/vl53l5cx/vl53l5cx.hpp index af58917b..9198c58f 100644 --- a/drivers/vl53l5cx/vl53l5cx.hpp +++ b/drivers/vl53l5cx/vl53l5cx.hpp @@ -2,10 +2,12 @@ extern "C" { #include "drivers/vl53l5cx/src/VL53L5CX_ULD_API/inc/vl53l5cx_api.h" +#include "drivers/vl53l5cx/src/VL53L5CX_ULD_API/inc/vl53l5cx_plugin_motion_indicator.h" } #include "common/pimoroni_i2c.hpp" #include "src/VL53L5CX_ULD_API/inc/vl53l5cx_api.h" +#include "src/VL53L5CX_ULD_API/inc/vl53l5cx_plugin_motion_indicator.h" namespace pimoroni { class VL53L5CX { @@ -38,13 +40,20 @@ namespace pimoroni { .i2c = i2c->get_i2c() }, }; + motion_configuration = new VL53L5CX_Motion_Configuration{}; } ~VL53L5CX() { delete configuration; + delete motion_configuration; } bool init(); + bool is_alive(); bool start_ranging(); bool stop_ranging(); + + bool enable_motion_indicator(Resolution resolution); + bool set_motion_distance(uint16_t distance_min, uint16_t distance_max); + bool set_i2c_address(uint8_t i2c_address); bool set_ranging_mode(RangingMode ranging_mode); bool set_ranging_frequency_hz(uint8_t ranging_frequency_hz); @@ -64,6 +73,7 @@ namespace pimoroni { } private: VL53L5CX_Configuration *configuration; + VL53L5CX_Motion_Configuration *motion_configuration; Resolution resolution = RESOLUTION_8X8; }; } \ No newline at end of file diff --git a/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp b/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp index d6e6f5ff..c0eb829d 100644 --- a/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp +++ b/examples/breakout_vl53l5cx/vl53l5cx_demo.cpp @@ -13,7 +13,10 @@ VL53L5CX vl53l5cx(&i2c); int main() { stdio_init_all(); - vl53l5cx.init(); + bool result = vl53l5cx.init(); + if(!result) { + printf("Error initializing...\n"); + } vl53l5cx.set_ranging_mode(VL53L5CX::RANGING_MODE_AUTONOMOUS); vl53l5cx.set_resolution(VL53L5CX::RESOLUTION_4X4); vl53l5cx.start_ranging(); diff --git a/micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py b/micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py index d38296ba..ff0f1edd 100644 --- a/micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py +++ b/micropython/examples/breakout_vl53l5cx/vl53l5cx_demo.py @@ -22,7 +22,7 @@ sensor.start_ranging() while True: if sensor.data_ready(): # "data" is a namedtuple (attrtuple technically) - # it includes average readings as "distance_avg" and "reflectance_avg"a + # it includes average readings as "distance_avg" and "reflectance_avg" # plus a full 4x4 or 8x8 set of readings (as a 1d tuple) for both values. data = sensor.get_data() print("{}mm {}% (avg: {}mm {}%)".format( diff --git a/micropython/examples/breakout_vl53l5cx/vl53l5cx_motion.py b/micropython/examples/breakout_vl53l5cx/vl53l5cx_motion.py new file mode 100644 index 00000000..10ffec38 --- /dev/null +++ b/micropython/examples/breakout_vl53l5cx/vl53l5cx_motion.py @@ -0,0 +1,35 @@ +import pimoroni_i2c +import breakout_vl53l5cx +import time +from ulab import numpy + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +# Sensor startup time is proportional to i2c baudrate +# HOWEVER many sensors may not run at > 400KHz (400000) +i2c = pimoroni_i2c.PimoroniI2C(**PINS_BREAKOUT_GARDEN, baudrate=2_000_000) + +print("Starting up sensor...") +t_sta = time.ticks_ms() +sensor = breakout_vl53l5cx.VL53L5CX(i2c) +t_end = time.ticks_ms() +print("Done in {}ms...".format(t_end - t_sta)) + +# Make sure to set resolution and other settings *before* you start ranging +sensor.set_resolution(breakout_vl53l5cx.RESOLUTION_4X4) + +sensor.enable_motion_indicator(breakout_vl53l5cx.RESOLUTION_4X4) +sensor.set_motion_distance(400, 1400) + +sensor.start_ranging() + +while True: + if sensor.data_ready(): + # "data" is a namedtuple (attrtuple technically) + # it includes average readings as "distance_avg" and "reflectance_avg" + # plus a full 4x4 or 8x8 set of readings (as a 1d tuple) for both values. + # Motion data is available in "motion_detection.motion" + data = sensor.get_data() + motion = numpy.array(data.motion_indicator.motion[0:16], dtype=numpy.int16).reshape((4, 4)) + print(motion) diff --git a/micropython/modules/breakout_vl53l5cx/micropython.cmake b/micropython/modules/breakout_vl53l5cx/micropython.cmake index 08d106df..71880809 100644 --- a/micropython/modules/breakout_vl53l5cx/micropython.cmake +++ b/micropython/modules/breakout_vl53l5cx/micropython.cmake @@ -6,6 +6,7 @@ target_sources(usermod_vl53l5cx INTERFACE ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/vl53l5cx.cpp ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/platform.c ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/src/VL53L5CX_ULD_API/src/vl53l5cx_api.c + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/src/VL53L5CX_ULD_API/src/vl53l5cx_plugin_motion_indicator.c ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/vl53l5cx/src/VL53L5CX_ULD_API/src/vl53l5cx_plugin_detection_thresholds.c ) diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.c b/micropython/modules/breakout_vl53l5cx/vl53l5cx.c index 8b614845..04be5574 100644 --- a/micropython/modules/breakout_vl53l5cx/vl53l5cx.c +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.c @@ -6,6 +6,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX___del___obj, VL53L5CX___del__); MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX_start_ranging_obj, VL53L5CX_start_ranging); MP_DEFINE_CONST_FUN_OBJ_1(VL53L5CX_stop_ranging_obj, VL53L5CX_stop_ranging); +MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_enable_motion_indicator_obj, VL53L5CX_enable_motion_indicator); +MP_DEFINE_CONST_FUN_OBJ_3(VL53L5CX_set_motion_distance_obj, VL53L5CX_set_motion_distance); + MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_i2c_address_obj, VL53L5CX_set_i2c_address); MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_ranging_mode_obj, VL53L5CX_set_ranging_mode); MP_DEFINE_CONST_FUN_OBJ_2(VL53L5CX_set_ranging_frequency_hz_obj, VL53L5CX_set_ranging_frequency_hz); @@ -26,6 +29,9 @@ STATIC const mp_rom_map_elem_t VL53L5CX_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_start_ranging), MP_ROM_PTR(&VL53L5CX_start_ranging_obj) }, { MP_ROM_QSTR(MP_QSTR_stop_ranging), MP_ROM_PTR(&VL53L5CX_stop_ranging_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_motion_indicator), MP_ROM_PTR(&VL53L5CX_enable_motion_indicator_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_motion_distance), MP_ROM_PTR(&VL53L5CX_set_motion_distance_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_i2c_address), MP_ROM_PTR(&VL53L5CX_set_i2c_address_obj) }, { MP_ROM_QSTR(MP_QSTR_set_ranging_mode), MP_ROM_PTR(&VL53L5CX_set_ranging_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_set_ranging_frequency_hz), MP_ROM_PTR(&VL53L5CX_set_ranging_frequency_hz_obj) }, diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp b/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp index 89f85703..662a982f 100644 --- a/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.cpp @@ -110,6 +110,24 @@ mp_obj_t VL53L5CX_stop_ranging(mp_obj_t self_in) { return mp_const_none; } +mp_obj_t VL53L5CX_enable_motion_indicator(mp_obj_t self_in, mp_obj_t value) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + bool status = self->breakout->enable_motion_indicator((pimoroni::VL53L5CX::Resolution)mp_obj_get_int(value)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: enable_motion_indicator error"); + } + return mp_const_none; +} + +mp_obj_t VL53L5CX_set_motion_distance(mp_obj_t self_in, mp_obj_t distance_min, mp_obj_t distance_max) { + _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); + bool status = self->breakout->set_motion_distance(mp_obj_get_int(distance_min), mp_obj_get_int(distance_max)); + if(!status) { + mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_motion_distance error"); + } + return mp_const_none; +} + mp_obj_t VL53L5CX_set_i2c_address(mp_obj_t self_in, mp_obj_t value) { _VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t); bool status = self->breakout->set_i2c_address(mp_obj_get_int(value)); @@ -220,30 +238,30 @@ mp_obj_t VL53L5CX_get_data(mp_obj_t self_in) { average_distance /= tuple_size; average_reflectance /= tuple_size; - // TODO motion data is all zeros, why? - - /*mp_obj_t tuple_motion_data[32]; + mp_obj_t tuple_motion_data[32]; for(int i = 0u; i < 32; i++) { tuple_motion_data[i] = mp_obj_new_int(results.motion_indicator.motion[i]); } + STATIC const qstr tuple_motion_fields[] = {MP_QSTR_global_indicator_1, MP_QSTR_global_indicator_2, MP_QSTR_motion}; + mp_obj_t tuple_motion[] = { mp_obj_new_int(results.motion_indicator.global_indicator_1), mp_obj_new_int(results.motion_indicator.global_indicator_2), mp_obj_new_tuple(sizeof(tuple_motion_data) / sizeof(mp_obj_t), tuple_motion_data) - };*/ + }; mp_obj_t tuple[] = { mp_obj_new_int(average_distance), // Average distance mp_obj_new_int(average_reflectance), // Average reflectance - //mp_obj_new_tuple(sizeof(tuple_motion) / sizeof(mp_obj_t), tuple_motion), // Motion data + mp_obj_new_attrtuple(tuple_motion_fields, sizeof(tuple_motion) / sizeof(mp_obj_t), tuple_motion), // Motion data mp_obj_new_int(tuple_size), // Number of results mp_obj_new_tuple(tuple_size, tuple_distance_mm), // Full distance results mp_obj_new_tuple(tuple_size, tuple_reflectance) // Full reflectange results }; - STATIC const qstr tuple_fields[] = {MP_QSTR_distance_avg, MP_QSTR_reflectance_avg, MP_QSTR_results, MP_QSTR_distance, MP_QSTR_reflectance}; + STATIC const qstr tuple_fields[] = {MP_QSTR_distance_avg, MP_QSTR_reflectance_avg, MP_QSTR_motion_indicator, MP_QSTR_results, MP_QSTR_distance, MP_QSTR_reflectance}; return mp_obj_new_attrtuple(tuple_fields, sizeof(tuple) / sizeof(mp_obj_t), tuple); } diff --git a/micropython/modules/breakout_vl53l5cx/vl53l5cx.h b/micropython/modules/breakout_vl53l5cx/vl53l5cx.h index 50470a74..74c151eb 100644 --- a/micropython/modules/breakout_vl53l5cx/vl53l5cx.h +++ b/micropython/modules/breakout_vl53l5cx/vl53l5cx.h @@ -11,6 +11,9 @@ extern mp_obj_t VL53L5CX_make_new(const mp_obj_type_t *type, size_t n_args, size extern mp_obj_t VL53L5CX_start_ranging(mp_obj_t self_in); extern mp_obj_t VL53L5CX_stop_ranging(mp_obj_t self_in); +extern mp_obj_t VL53L5CX_enable_motion_indicator(mp_obj_t self_in, mp_obj_t value); +extern mp_obj_t VL53L5CX_set_motion_distance(mp_obj_t self_in, mp_obj_t distance_min, mp_obj_t distance_max); + extern mp_obj_t VL53L5CX_set_i2c_address(mp_obj_t self_in, mp_obj_t value); extern mp_obj_t VL53L5CX_set_ranging_mode(mp_obj_t self_in, mp_obj_t value); extern mp_obj_t VL53L5CX_set_ranging_frequency_hz(mp_obj_t self_in, mp_obj_t value); From f66663aa0ea595dfa4db5a2cb27283d325b392a9 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 6 May 2022 12:11:29 +0100 Subject: [PATCH 13/16] Bump Blinka and PlatformDetect Blinka -> v7.3.0 for RP2040 i2c pin permutations fix PlatformDetect -> 3.22.1 for MicroPython fixes --- .github/workflows/micropython-with-blinka.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/micropython-with-blinka.yml b/.github/workflows/micropython-with-blinka.yml index 0250061e..b43f703d 100644 --- a/.github/workflows/micropython-with-blinka.yml +++ b/.github/workflows/micropython-with-blinka.yml @@ -9,8 +9,8 @@ on: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) MICROPYTHON_VERSION: v1.18 - BLINKA_VERSION: 6.20.1 - PLATFORMDETECT_VERSION: 3.19.3 + BLINKA_VERSION: 7.3.0 + PLATFORMDETECT_VERSION: 3.22.1 BUILD_TYPE: Release BOARD_TYPE: PICO From 8392401cf9dc123ebebda62021b3da630dad24cc Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 6 May 2022 17:56:10 +0100 Subject: [PATCH 14/16] Badger 2040: Fix partial update hang for #348 --- micropython/modules/badger2040/badger2040.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index fbd29c26..748e8c3a 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -179,7 +179,7 @@ MICROPY_EVENT_POLL_HOOK } absolute_time_t t_end = make_timeout_time_ms(self->badger2040->update_time()); - self->badger2040->partial_update(x, y, w, h); + self->badger2040->partial_update(x, y, w, h, false); // Ensure blocking for the minimum amount of time // in cases where "is_busy" is unreliable. From 323d805585b0af179f27ce7f20bb4e2865ea9c55 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Sat, 7 May 2022 21:14:18 +0100 Subject: [PATCH 15/16] Blinka: Add generic_micropython --- .github/workflows/micropython-with-blinka.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/micropython-with-blinka.yml b/.github/workflows/micropython-with-blinka.yml index b43f703d..ea3aa8d5 100644 --- a/.github/workflows/micropython-with-blinka.yml +++ b/.github/workflows/micropython-with-blinka.yml @@ -64,6 +64,7 @@ jobs: cp -r pimoroni-pico-${GITHUB_SHA}/micropython/modules_py/* micropython/ports/rp2/modules/ mkdir -p micropython/ports/rp2/modules/adafruit_blinka/microcontroller/ cp -r Adafruit_Blinka/src/adafruit_blinka/microcontroller/rp2040 micropython/ports/rp2/modules/adafruit_blinka/microcontroller/ + cp -r Adafruit_Blinka/src/adafruit_blinka/microcontroller/generic_micropython micropython/ports/rp2/modules/adafruit_blinka/microcontroller/ mkdir -p micropython/ports/rp2/modules/adafruit_blinka/board/raspberrypi/ cp Adafruit_Blinka/src/adafruit_blinka/microcontroller/__init__.py micropython/ports/rp2/modules/adafruit_blinka/microcontroller/ cp Adafruit_Blinka/src/adafruit_blinka/board/__init__.py micropython/ports/rp2/modules/adafruit_blinka/board/ From 85fc718268f4cb1c090cb7c6fb3ec2d0483fad7c Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 9 May 2022 17:17:44 +0100 Subject: [PATCH 16/16] VL53L5CX: Add object tracking MicroPython example. --- .../vl53l5cx_object_tracking.py | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 micropython/examples/breakout_vl53l5cx/vl53l5cx_object_tracking.py diff --git a/micropython/examples/breakout_vl53l5cx/vl53l5cx_object_tracking.py b/micropython/examples/breakout_vl53l5cx/vl53l5cx_object_tracking.py new file mode 100644 index 00000000..8f05d28a --- /dev/null +++ b/micropython/examples/breakout_vl53l5cx/vl53l5cx_object_tracking.py @@ -0,0 +1,98 @@ +import pimoroni_i2c +import breakout_vl53l5cx +import time +from ulab import numpy + +# This example attempts to track a "bright" object (such as a white business card) +# It uses reflectance to identify the target and compute the X/Y coordinates +# of its "center of mass" in the sensors view. + +# Motion indication only works at distances > 400mm so it's not +# really useful as a method to reject data. + +# Configure your distance and brightness thresholds to suit your object +DISTANCE_THRESHOLD = 400 # Distance in mm +REFLECTANCE_THRESHOLD = 60 # Estimated reflectance in % + + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +# Sensor startup time is proportional to i2c baudrate +# HOWEVER many sensors may not run at > 400KHz (400000) +i2c = pimoroni_i2c.PimoroniI2C(**PINS_BREAKOUT_GARDEN, baudrate=2_000_000) + +print("Starting up sensor...") +t_sta = time.ticks_ms() +sensor = breakout_vl53l5cx.VL53L5CX(i2c) +t_end = time.ticks_ms() +print("Done in {}ms...".format(t_end - t_sta)) + +# Make sure to set resolution and other settings *before* you start ranging +sensor.set_resolution(breakout_vl53l5cx.RESOLUTION_8X8) +sensor.set_ranging_frequency_hz(15) +sensor.start_ranging() + + +while True: + time.sleep(1.0 / 60) + if sensor.data_ready(): + # "data" is a namedtuple (attrtuple technically) + # it includes average readings as "distance_avg" and "reflectance_avg" + # plus a full 4x4 or 8x8 set of readings (as a 1d tuple) for both values. + data = sensor.get_data() + + reflectance = numpy.array(data.reflectance).reshape((8, 8)) + distance = numpy.array(data.distance).reshape((8, 8)) + + scalar = 0 + target_distance = 0 + n_distances = 0 + # Filter out unwanted reflectance values + for ox in range(8): + for oy in range(8): + d = distance[ox][oy] + r = reflectance[ox][oy] + if d > DISTANCE_THRESHOLD or r < REFLECTANCE_THRESHOLD: + reflectance[ox][oy] = 0 + else: + scalar += r + + # Get a total from all the distances within our accepted target + for ox in range(8): + for oy in range(8): + d = distance[ox][oy] + r = reflectance[ox][oy] + if r > 0: + target_distance += d + n_distances += 1 + + # Average the target distance + if n_distances > 0: + target_distance /= n_distances + else: + target_distance = 0 + + # Flip reflectance now we've applied distance + # both fields are upside-down! + reflectance = numpy.flip(reflectance, axis=0) + + # Calculate the center of mass along X and Y + x = 0 + y = 0 + if scalar > 0: + for ox in range(8): + for oy in range(8): + y += reflectance[ox][oy] * ox + y /= scalar + y /= 3.5 + y -= 1.0 + + for oy in range(8): + for ox in range(8): + x += reflectance[ox][oy] * oy + x /= scalar + x /= 3.5 + x -= 1.0 + + print(round(x, 2), round(y, 2), round(target_distance, 2))