Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quirc esp-eye example (IEC-51) #240

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
48 changes: 48 additions & 0 deletions quirc/examples/esp-eye-qrcode/.devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
FROM espressif/idf
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem with devcontainers in general, but we usually don't add them directly to the example projects. Perhaps we can add one at project level, though. Still, I would prefer if this is done in a separate PR.


ARG DEBIAN_FRONTEND=nointeractive
ARG CONTAINER_USER=esp
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN apt-get update \
&& apt install -y -q \
cmake \
git \
hwdata \
libglib2.0-0 \
libnuma1 \
libpixman-1-0 \
linux-tools-virtual \
&& rm -rf /var/lib/apt/lists/*

RUN update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20

# QEMU
ENV QEMU_REL=esp-develop-20220919
ENV QEMU_SHA256=f6565d3f0d1e463a63a7f81aec94cce62df662bd42fc7606de4b4418ed55f870
ENV QEMU_DIST=qemu-${QEMU_REL}.tar.bz2
ENV QEMU_URL=https://github.com/espressif/qemu/releases/download/${QEMU_REL}/${QEMU_DIST}

ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8

RUN wget --no-verbose ${QEMU_URL} \
&& echo "${QEMU_SHA256} *${QEMU_DIST}" | sha256sum --check --strict - \
&& tar -xf $QEMU_DIST -C /opt \
&& rm ${QEMU_DIST}

ENV PATH=/opt/qemu/bin:${PATH}

RUN groupadd --gid $USER_GID $CONTAINER_USER \
&& adduser --uid $USER_UID --gid $USER_GID --disabled-password --gecos "" ${CONTAINER_USER} \
&& usermod -a -G dialout $CONTAINER_USER
USER ${CONTAINER_USER}
ENV USER=${CONTAINER_USER}
WORKDIR /home/${CONTAINER_USER}

RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc

ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]

CMD ["/bin/bash", "-c"]
47 changes: 47 additions & 0 deletions quirc/examples/esp-eye-qrcode/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.183.0/containers/ubuntu
{
"name": "ESP-IDF QEMU",
"build": {
"dockerfile": "Dockerfile"
},
// Add the IDs of extensions you want installed when the container is created
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
/* the path of workspace folder to be opened after container is running
*/
"workspaceFolder": "${localWorkspaceFolder}",
"mounts": [
"source=extensionCache,target=/root/.vscode-server/extensions,type=volume"
],
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"idf.espIdfPath": "/opt/esp/idf",
"idf.customExtraPaths": "",
"idf.pythonBinPath": "/opt/esp/python_env/idf5.1_py3.8_env/bin/python",
"idf.toolsPath": "/opt/esp",
"idf.gitPath": "/usr/bin/git"
},
"extensions": [
"ms-vscode.cpptools",
"espressif.esp-idf-extension"
],
},
"codespaces": {
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"idf.espIdfPath": "/opt/esp/idf",
"idf.customExtraPaths": "",
"idf.pythonBinPath": "/opt/esp/python_env/idf5.1_py3.8_env/bin/python",
"idf.toolsPath": "/opt/esp",
"idf.gitPath": "/usr/bin/git"
},
"extensions": [
"ms-vscode.cpptools",
"espressif.esp-idf-extension"
],
}
},
"runArgs": ["--privileged"]
}
8 changes: 8 additions & 0 deletions quirc/examples/esp-eye-qrcode/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(app_main)
10 changes: 10 additions & 0 deletions quirc/examples/esp-eye-qrcode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ------ | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
| | X | | | | | | |
# ESP-EYE QR-CODE with QUIRC

In this example the esp-eye takes a picture every few ms and check if the image contains valid qr-codes. If detected, it shows a message on the terminal (e.g. `Data: This is an esp-eye test`). It may not decode correctly the qr-code and in this case an error message is shown (`DECODE FAILED: ECC failure`).

## How to use example

Build and flash on an esp-eye. It may work with small changes also on ESP32-CAM and ESP32-S3-EYE.
6 changes: 6 additions & 0 deletions quirc/examples/esp-eye-qrcode/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
idf_component_register( SRCS
app_peripherals.c
app_main.c
INCLUDE_DIRS
include
)
85 changes: 85 additions & 0 deletions quirc/examples/esp-eye-qrcode/main/app_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "app_peripherals.h"
#include "quirc.h"

static const char *TAG = "APP_CODE_SCANNER";

static void decode_task()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FreeRTOS task function definition should look like this:

Suggested change
static void decode_task()
static void decode_task(void* arg)

{
if(ESP_OK != app_camera_init()) {
vTaskDelete(NULL);
return;
}

camera_fb_t *fb = NULL;

// Initializing the quirc handle
struct quirc *q = quirc_new();
if (!q) {
ESP_LOGE(TAG,"Failed to allocate memory\n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline at the end of format strings is not necessary for ESP_LOG, it's added automatically.

Also, missing space after comma. (Have you installed the pre-commit hooks using pre-commit install? They should automatically format the source files.)

exit(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error handling is a bit inconsistent. In the check above you are doing vTaskDelete(), here you are using exit. And in the check just below you only log the error code and continue.

Let's try to make this more consistent. You can decide the error handling strategy for this function — return or abort — and use the same approach in the whole function.

}


// Get image size through fb parameters
fb = esp_camera_fb_get();
if(fb == NULL){
ESP_LOGI(TAG, "camera get failed\n");
}

uint16_t p_width = fb->width;
uint16_t p_height = fb->height;


if (quirc_resize(q, p_width,p_height) < 0) {
ESP_LOGE(TAG,"Failed to allocate video memory\n");
exit(1);
}

struct quirc_code code;
struct quirc_data data;
quirc_decode_error_t err;
uint16_t num_codes;

while (1)
{
fb = esp_camera_fb_get();
if(fb == NULL){
ESP_LOGI(TAG, "camera get failed\n");
continue;
}
// Decode Progress

memcpy(quirc_begin(q, NULL, NULL), fb->buf, fb->len);
quirc_end(q);

num_codes = quirc_count(q);
for (uint16_t i = 0; i < num_codes; i++) {

quirc_extract(q, i, &code);
/* Decoding stage */
err = quirc_decode(&code, &data);
if (err)
printf("%d/%d] DECODE FAILED: %s\n", i+1,num_codes,quirc_strerror(err));
else
printf("%d/%d] DATA: %s\n",i+1,num_codes,data.payload);
}

esp_camera_fb_return(fb);
vTaskDelay(10 / portTICK_PERIOD_MS);
}

quirc_destroy(q);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vTaskDelete(NULL); missing here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the code isn't supposed to be reached. I'm not sure what the best approach would be here.
Do you suggest the vTaskDelete or to remove the quirc_destroy completely?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends on the error handling strategy — see the other comment about this.

If you decide to handle errors by terminating the task, probably having the cleanup path with quirc_destroy would make sense. You could use an out: goto label and use macros like ESP_GOTO_ON_ERROR to handle errors and perform cleanup.

If you decide to handle errors by aborting (which is not an unreasonable strategy for an example project) then quirc_destroy can be removed since it's unreachable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok then since as you say it's an example, I think it's best to keep it the simplest possible. I removed the quirc_destroy.



void app_main()
FBEZ marked this conversation as resolved.
Show resolved Hide resolved
{
xTaskCreatePinnedToCore(decode_task, TAG, 40 * 1024, NULL, 6, NULL, 0);
}
65 changes: 65 additions & 0 deletions quirc/examples/esp-eye-qrcode/main/app_peripherals.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "app_peripherals.h"
#include "esp_log.h"
#include "esp_system.h"

static const char *TAG = "app_peripherals";

esp_err_t app_camera_init()
{
ESP_LOGI(TAG, "Camera module is %s", CAMERA_MODULE_NAME);

camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = CAMERA_PIN_D0;
config.pin_d1 = CAMERA_PIN_D1;
config.pin_d2 = CAMERA_PIN_D2;
config.pin_d3 = CAMERA_PIN_D3;
config.pin_d4 = CAMERA_PIN_D4;
config.pin_d5 = CAMERA_PIN_D5;
config.pin_d6 = CAMERA_PIN_D6;
config.pin_d7 = CAMERA_PIN_D7;
config.pin_xclk = CAMERA_PIN_XCLK;
config.pin_pclk = CAMERA_PIN_PCLK;
config.pin_vsync = CAMERA_PIN_VSYNC;
config.pin_href = CAMERA_PIN_HREF;
config.pin_sccb_sda = CAMERA_PIN_SIOD;
config.pin_sccb_scl = CAMERA_PIN_SIOC;
config.pin_pwdn = CAMERA_PIN_PWDN;
config.pin_reset = CAMERA_PIN_RESET;
config.xclk_freq_hz = XCLK_FREQ_HZ;
config.pixel_format = CAMERA_PIXFORMAT;
config.frame_size = CAMERA_FRAME_SIZE;
config.jpeg_quality = 5;
config.fb_count = CAMERA_FB_COUNT;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;

// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
return ESP_FAIL;
}

sensor_t *s = esp_camera_sensor_get();
if (s->id.PID == OV3660_PID || s->id.PID == OV2640_PID)
s->set_vflip(s, 1); //flip it back
else if (s->id.PID == GC0308_PID){
s->set_hmirror(s, 0);
}
else if (s->id.PID == GC032A_PID){
s->set_vflip(s, 1);
}

if (s->id.PID == OV3660_PID)
{
s->set_brightness(s, 2);
s->set_contrast(s, 3);
}

return ESP_OK;
}


11 changes: 11 additions & 0 deletions quirc/examples/esp-eye-qrcode/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp32-camera: "^2.0.5"
espressif/quirc:
version: "^1.2.0"
# This line define the local path of the component because this
# example is part of the component. This line is optional.
override_path: "../../.."
## Required IDF version
idf:
version: ">=4.3.0"
Loading
Loading