Close

And the Answer Is...

A project log for Think-a-Tron 2020

In 1960 Hasbro unleashed its first personal "computer" to the masses, decades before IMSAI, Apple, or Commodore. Remembering Think-a-Tron.

michael-gardiMichael Gardi 12/23/2020 at 15:521 Comment

The Hardware

I did a fair amount of research before I decided to use QR codes to encode my answers. I discovered that there was a lot of activity around using the ESP32-CAM module to read QR codes. 

The ESP32-CAM is a very small camera module that combines an ESP32-S chip with an OV2640 camera that sells for about $10.

Since I needed an MCU for this project anyway this seemed like a good fit. Once the ESP32-CAM is programmed, and assuming that the built-in microSD slot is not used, there are 10 I/O pins available for general use. Notice that there is no USB port. Programing the ESP32-CAM requires an FTDI programmer.

I bought a "bundle" that included the ESP32-CAM with OV2640 camera installed, an FTDI programmer, and a 40 pin bundle of female/female jumper wires for about $15 US off of Amazon. 

I won't go into details here, but you can click this link for a great tutorial on setting up the ESP32-CAM.  I followed these instructions and got the Video Streaming Server working easily. Pretty cool stuff.

The Software

If you google "ESP32-CAM QR code reader" you will get a lot of results. Having looked at a few of the videos and read a number of the tutorials returned by that search, I realized that the big difference between what has been done so far, and what I am trying to do, is that the QR codes being scanned in these posts were all fairly large.  I am trying read a 1 1/2 inch square QR code from the back of a business card.  So I setup a little test rig to see if it could be done (spoiler alert: it can).

The trivia card is inserted into a holder that can slide towards and away from the stationary camera. The ESP32-CAM is connected to the FTDI programmer. I ran the Video Streaming Server example mentioned in the installation tutorial. 

I used this setup to determine the optimal distance between card and camera by sliding the card into a position so that the QR code occupies most (say 90%) of the frame.  This turned out to be about 65 mm for me.  With the card positioned, focus the camera to get the sharpest image possible buy carefully twisting the lens.  To this end I designed a small tool to securely hold the camera in place while the lens was being turned. 

You have to apply a fair amount of torque to adjust the lens.

Once the camera was calibrated it was time to try reading the QR code.  It tried a couple of different tutorials, but found that the one linked here worked best for me. The only changes I made to the test code from that tutorial were to translate the Portuguese comments to English, and to enable the ESP32-CAM's on board flash when reading the QR code.

//Bibliotecas utilizadas
#include "ESPino32CAM.h"
#include "ESPino32CAM_QRCode.h"
 
ESPino32CAM cam;   // Image capture object
ESPino32QRCode qr; // Image decoding object
 
// Set the camera pins
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22
#define flash 4
 
void setup() {
   
  Serial.begin(115200);
  Serial.println("QR Code Reader");
  // Define the flash pin.
  pinMode(flash,OUTPUT);
  digitalWrite(flash, LOW); // Turn off the flash.
     
  // Configure the camera pins.
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_VGA;  
  config.jpeg_quality = 4;
  config.fb_count = 1;
   
  esp_err_t err = esp_camera_init(&config); // Initialize the camera.
   
  if (err != ESP_OK) {
     
    Serial.printf("Camera start failed with error 0x%x", err);//Informa erro se a câmera não for iniciada corretamente
    delay(1000);
    ESP.restart(); // Reboot the ESP
     
  }
 
  // Initialize the decoding object.
  qr.init(&cam);
  sensor_t *s = cam.sensor();
  s->set_framesize(s, FRAMESIZE_CIF);
  s->set_whitebal(s, true);
   
  Serial.println();
  Serial.println("Waiting for code."); 
 
}
 
void loop()
{
  unsigned long pv_time  = millis();
  digitalWrite(flash, HIGH); // Turn on the flash.
  delay(500);
  camera_fb_t *fb = cam.capture(); // Captura a imagem
  digitalWrite(flash, LOW); // Turn off the flash.
  if (!fb)
  {
    Serial.println("Image capture failed.");
    return;
  }
  dl_matrix3du_t *rgb888, *rgb565;
  if (cam.jpg2rgb(fb, &rgb888))
  {
    rgb565 = cam.rgb565(rgb888);
  }
  cam.clearMemory(rgb888);
  cam.clearMemory(rgb565);
  dl_matrix3du_t *image_rgb;
  if (cam.jpg2rgb(fb, &image_rgb))
  {
    cam.clearMemory(fb);
         
    qrResoult res = qr.recognition(image_rgb); // Decodes the image containing the data.
     
    if (res.status) // If you can decode the image it shows the data on the screen.
    { 
      String result = "QR Code Read: " + res.payload; // Variable to show the data contained in the QR Code.
      Serial.println();
      Serial.println(result);  // Shows data on the serial monitor,  
    }
    else{ // If you do not wait to receive code.
       Serial.println();
       Serial.println("Waiting for code."); 
     }          
    }
     
  cam.clearMemory(image_rgb); // Delete image to receive a new image.
}

And here is the result.

So I guess that answer is A. Moving on.

Discussions

kemploe wrote 05/28/2022 at 00:39 point

Hi Michael,

Please help me. 

I try to compile the code and get this error:

In file included from C:\Users\kunto\Downloads\_SmartHome\QR Scanner\qr_code_scanner_esp32s\qr_code_scanner_esp32s.ino:1:
C:\Users\kunto\Documents\Arduino\libraries\ESPIno32CAM\src/ESPino32CAM.h:7:10: fatal error: fd_forward.h: No such file or directory
 #include "fd_forward.h" // face Detect
          ^~~~~~~~~~~~~~
compilation terminated.
exit status 1
Error compiling for board AI Thinker ESP32-CAM.

Thank you.

  Are you sure? yes | no