Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 83 additions & 80 deletions .github/workflows/build-run-applications.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build ESP-BSP apps
name: Build and Run ESP-BSP apps

# This job builds all examples and test_applications in this repo
# Applications are selected for build based on changes files and dependencies defined in .build-test-rules.yml
Expand Down Expand Up @@ -47,30 +47,30 @@ jobs:
- idf_ver: "latest"
parallel_count: 5
parallel_index: 5
- idf_ver: "release-v5.1"
parallel_count: 2
parallel_index: 1
- idf_ver: "release-v5.1"
parallel_count: 2
parallel_index: 2
- idf_ver: "release-v5.2"
parallel_count: 2
parallel_index: 1
- idf_ver: "release-v5.2"
parallel_count: 2
parallel_index: 2
- idf_ver: "release-v5.3"
parallel_count: 2
parallel_index: 1
- idf_ver: "release-v5.3"
parallel_count: 2
parallel_index: 2
- idf_ver: "release-v5.4"
parallel_count: 2
parallel_index: 1
- idf_ver: "release-v5.4"
parallel_count: 2
parallel_index: 2
#- idf_ver: "release-v5.1"
# parallel_count: 2
# parallel_index: 1
#- idf_ver: "release-v5.1"
# parallel_count: 2
# parallel_index: 2
#- idf_ver: "release-v5.2"
# parallel_count: 2
# parallel_index: 1
#- idf_ver: "release-v5.2"
# parallel_count: 2
# parallel_index: 2
#- idf_ver: "release-v5.3"
# parallel_count: 2
# parallel_index: 1
#- idf_ver: "release-v5.3"
# parallel_count: 2
# parallel_index: 2
#- idf_ver: "release-v5.4"
# parallel_count: 2
# parallel_index: 1
#- idf_ver: "release-v5.4"
# parallel_count: 2
# parallel_index: 2
runs-on: ubuntu-latest
container: espressif/idf:${{ matrix.idf_ver }}
steps:
Expand Down Expand Up @@ -124,60 +124,60 @@ jobs:
- runs-on: "esp-box-3"
marker: "esp_box_3"
target: "esp32s3"
- runs-on: "esp32_c3_lcdkit"
marker: "esp32_c3_lcdkit"
target: "esp32c3"
- runs-on: "esp32_p4_box"
marker: "esp32_p4_box"
target: "esp32p4"
- runs-on: "esp32_p4_function_ev_board"
marker: "esp32_p4_function_ev_board"
target: "esp32p4"
- runs-on: "esp32_s3_eye"
marker: "esp32_s3_eye"
target: "esp32s3"
- runs-on: "esp32_s3_lcd_ev_board"
marker: "esp32_s3_lcd_ev_board"
target: "esp32s3"
- runs-on: "esp32_s3_lcd_ev_board"
marker: "esp32_s3_lcd_ev_board_2"
target: "esp32s3"
- runs-on: "esp32_s3_usb_otg"
marker: "esp32_s3_usb_otg"
target: "esp32s3"
- runs-on: "esp_wrover_kit"
marker: "esp_wrover_kit"
target: "esp32"
- runs-on: "m5dial"
marker: "m5dial"
target: "esp32s3"
- runs-on: "m5stack_core"
marker: "m5stack_core"
target: "esp32"
- runs-on: "m5stack_core_2"
marker: "m5stack_core_2"
target: "esp32"
- runs-on: "m5stack_core_s3"
marker: "m5stack_core_s3"
target: "esp32s3"
- runs-on: "m5stack_core_s3"
marker: "m5stack_core_s3_se"
target: "esp32s3"
- runs-on: "esp32_azure_iot_kit"
marker: "esp32_azure_iot_kit"
target: "esp32"
- runs-on: "esp_bsp_devkit"
marker: "esp_bsp_devkit"
target: "esp32s3"
- runs-on: "esp_bsp_generic"
marker: "esp_bsp_generic"
target: "esp32s3"
- runs-on: "esp32_s3_korvo_2"
marker: "esp32_s3_korvo_2"
target: "esp32s3"
- runs-on: "m5_atom_s3"
marker: "m5_atom_s3"
target: "esp32s3"
#- runs-on: "esp32_c3_lcdkit"
# marker: "esp32_c3_lcdkit"
# target: "esp32c3"
#- runs-on: "esp32_p4_box"
# marker: "esp32_p4_box"
# target: "esp32p4"
#- runs-on: "esp32_p4_function_ev_board"
# marker: "esp32_p4_function_ev_board"
# target: "esp32p4"
#- runs-on: "esp32_s3_eye"
# marker: "esp32_s3_eye"
# target: "esp32s3"
#- runs-on: "esp32_s3_lcd_ev_board"
# marker: "esp32_s3_lcd_ev_board"
# target: "esp32s3"
#- runs-on: "esp32_s3_lcd_ev_board"
# marker: "esp32_s3_lcd_ev_board_2"
# target: "esp32s3"
#- runs-on: "esp32_s3_usb_otg"
# marker: "esp32_s3_usb_otg"
# target: "esp32s3"
#- runs-on: "esp_wrover_kit"
# marker: "esp_wrover_kit"
# target: "esp32"
#- runs-on: "m5dial"
# marker: "m5dial"
# target: "esp32s3"
#- runs-on: "m5stack_core"
# marker: "m5stack_core"
# target: "esp32"
#- runs-on: "m5stack_core_2"
# marker: "m5stack_core_2"
# target: "esp32"
#- runs-on: "m5stack_core_s3"
# marker: "m5stack_core_s3"
# target: "esp32s3"
#- runs-on: "m5stack_core_s3"
# marker: "m5stack_core_s3_se"
# target: "esp32s3"
#- runs-on: "esp32_azure_iot_kit"
# marker: "esp32_azure_iot_kit"
# target: "esp32"
#- runs-on: "esp_bsp_devkit"
# marker: "esp_bsp_devkit"
# target: "esp32s3"
#- runs-on: "esp_bsp_generic"
# marker: "esp_bsp_generic"
# target: "esp32s3"
#- runs-on: "esp32_s3_korvo_2"
# marker: "esp32_s3_korvo_2"
# target: "esp32s3"
#- runs-on: "m5_atom_s3"
# marker: "m5_atom_s3"
# target: "esp32s3"
env:
TEST_RESULT_NAME: test_results_${{ matrix.runner.target }}_${{ matrix.runner.marker }}_${{ matrix.idf_ver }}
BENCHMARK_RESULT_NAME: benchmark_${{ matrix.runner.target }}_${{ matrix.runner.marker }}_${{ matrix.idf_ver }}
Expand All @@ -197,7 +197,9 @@ jobs:
env:
PIP_EXTRA_INDEX_URL: "https://dl.espressif.com/pypi/"
run: |
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code opencv-python numpy
apt-get update && apt-get install -y libgl1
apt-get update && apt-get install -y v4l-utils
- name: Download latest results
uses: actions/download-artifact@v4
with:
Expand All @@ -216,6 +218,7 @@ jobs:
benchmark_*.md
benchmark_*.json
benchmark.json
*.jpg
- name: Check if benchmark files exist
id: check_files
run: |
Expand Down
103 changes: 102 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
import cv2
import subprocess
import numpy as np
import time
from pathlib import Path


def pytest_generate_tests(metafunc):
Expand Down Expand Up @@ -38,3 +43,99 @@ def pytest_collection_modifyitems(config, items):
marker_option = "[" + config.getoption("-m") + "]"
if marker_option not in item.nodeid:
item.add_marker(pytest.mark.skip(reason="Not for selected params"))


def bsp_image_correction(image, width, height):
pts_src = np.float32([
[160, 80], # top-left
[1044, 87], # top-right
[279, 720], # bottom-left
[913, 718] # bottom-right
])

pts_dst = np.float32([
[0, 0],
[width, 0],
[0, height],
[width, height]
])

matrix = cv2.getPerspectiveTransform(pts_src, pts_dst)
warped = cv2.warpPerspective(image, matrix, (width, height))
return warped


def bsp_fisheye_correction(image, width, height):
K = np.array([
[width, 0, width / 2], # Focal length x
[0, width, height / 2], # Focal length y
[0, 0, 1]]) # Principal point
# [k1, k2, p1, p2, k3] - main is k1 a k2
dist_coeffs = np.array([-0.3, 0.1, 0, 0, 0])

new_K, _ = cv2.getOptimalNewCameraMatrix(K, dist_coeffs, (width, height), 1, (width, height))
map1, map2 = cv2.initUndistortRectifyMap(K, dist_coeffs, None, new_K, (width, height), 5)
undistorted = cv2.remap(image, map1, map2, interpolation=cv2.INTER_LINEAR)

return undistorted


def bsp_capture_image(image_path, board):
# Enable auto-focus
# subprocess.run(["v4l2-ctl", "-d", "/dev/video0", "--set-ctrl=focus_auto=1"])
# Manual focus
subprocess.run(["v4l2-ctl", "-d", "/dev/video0", "--set-ctrl=focus_auto=0"])
subprocess.run(["v4l2-ctl", "-d", "/dev/video0", "--set-ctrl=focus_absolute=50"])
# Manual exposition
subprocess.run(["v4l2-ctl", "-d", "/dev/video0", "--set-ctrl=exposure_auto=1"])
subprocess.run(["v4l2-ctl", "-d", "/dev/video0", "--set-ctrl=exposure_absolute=1"])

# Return video from the first webcam on your computer.
cap = cv2.VideoCapture(0)
# Set FullHD resolution (1920x1080)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

# TODO: Camera calibration

# reads frames from a camera
# ret checks return at each frame
ret, frame = cap.read()
if ret:
# Image rotation
frame = cv2.rotate(frame, cv2.ROTATE_180)
# TODO: Camera calibration / Perspective transform
# TODO: Change size image
# TODO: Crop image for {board}

# correction image perspective and crop
frame = bsp_image_correction(frame, 1000, 884)
# correction of fisheye
frame = bsp_fisheye_correction(frame, 1000, 884)
# crop
frame = frame[30:848, 38:980]

# Save image
cv2.imwrite(image_path, frame)
print(f"Image saved {image_path}")
else:
print("Cannot save image.")

# Close the window / Release webcam
cap.release()


def bsp_test_image(board, example, expectation):
image_file = f"snapshot_{board}_{example}.jpg"
bsp_capture_image(image_file, board)


@pytest.fixture()
def bsp_test_capture_image(request):
board = request.node.callspec.id
path = Path(str(request.node.fspath))
test_name = path.parent.name
yield
time.sleep(5) # wait 5 seconds
print(f"Capturing image for: {board}")
bsp_test_image(board, test_name, "")
4 changes: 2 additions & 2 deletions examples/display/pytest_display.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0

import pytest
Expand All @@ -20,6 +20,6 @@
@pytest.mark.m5stack_core_s3
@pytest.mark.m5stack_core_s3_se
@pytest.mark.m5_atom_s3
def test_display_example(dut: Dut) -> None:
def test_display_example(dut: Dut, bsp_test_capture_image) -> None:
dut.expect_exact('example: Display LVGL animation')
dut.expect_exact('main_task: Returned from app_main()')
2 changes: 1 addition & 1 deletion examples/display_lvgl_demos/pytest_display_lvgl_demos.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
@pytest.mark.m5dial
@pytest.mark.m5stack_core_s3
@pytest.mark.m5stack_core_s3_se
def test_display_example(dut: Dut) -> None:
def test_display_example(dut: Dut, bsp_test_capture_image) -> None:
dut.expect_exact('app_main: Display LVGL demo')
dut.expect_exact('main_task: Returned from app_main()')
Loading