ArduCAM / Arduino

This is ArduCAM library for Arduino boards
MIT License
473 stars 349 forks source link

OV5642: Slow capture time #515

Open gonzalocasas opened 3 years ago

gonzalocasas commented 3 years ago

I'm using an OV5642 with an Arduino Nano 33 IoT. I would like to get multiple frames per second streamed over WiFi, but so far, I have very high capture times between 1s and 2s. So, I'm very far from the advertised FPS. I am not hoping for a high fps, but at least 10 or 15 fps to get a relative sense of fluidity.

I use the following code:

#include <Wire.h>
#include <WiFiNINA.h>
#include <ArduCAM.h>
#include <SPI.h>
#include "memorysaver.h"
#include "secrets.h"

/* WiFi setup */
char ssid[] = SECRET_SSID;        // network SSID (name)
char pass[] = SECRET_PASS;        // network password
int status = WL_IDLE_STATUS;      // the WiFi radio's status
const int SERVER_PORT = 80;
WiFiServer server(SERVER_PORT);

/* Arducam setup */
#if !(defined (OV5640_MINI_5MP_PLUS)||defined (OV5642_MINI_5MP_PLUS))
#error Please select the hardware platform and camera module in the ../libraries/ArduCAM/memorysaver.h file
#endif

// set pin 7 as the slave select for the digital pot:
const int CS = 7;

static const size_t bufferSize = 2048;
static uint8_t buffer[bufferSize] = {0xFF};
int i = 0;
bool is_header = false;
uint8_t start_capture = 0;

ArduCAM camera(OV5642, CS);
uint8_t read_fifo_burst(ArduCAM camera);

void connectNetwork() {
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);

    status = WiFi.begin(ssid, pass);
    delay(10000);   // wait for connection
  }
  Serial.print("Connected. ");

  server.begin();

  IPAddress ip = WiFi.localIP();
  Serial.print("Started serving on http://");
  Serial.print(ip);
  Serial.print(":");
  Serial.println(SERVER_PORT);
}

void setupArducam() {
  uint8_t vid, pid;
  uint8_t temp;
  Wire.begin();

  // Configure CS
  pinMode(CS, OUTPUT);
  digitalWrite(CS, HIGH);

  // Initialize SPI
  SPI.begin();

  //Reset the CPLD
  camera.write_reg(0x07, 0x80);
  delay(100);
  camera.write_reg(0x07, 0x00);
  delay(100);

  while(1) {
    //Check if the ArduCAM SPI bus is OK
    camera.write_reg(ARDUCHIP_TEST1, 0x55);
    temp = camera.read_reg(ARDUCHIP_TEST1);
    if (temp != 0x55)
    {
      Serial.println(F("Arducam SPI interface Error!"));
      delay(1000);
      continue;
    } else {
      Serial.println(F("Arducam SPI interface OK"));
      break;
    }
  }

  while(1){
    // Check if the camera module type is OV5642
    camera.rdSensorReg16_8(OV5642_CHIPID_HIGH, &vid);
    camera.rdSensorReg16_8(OV5642_CHIPID_LOW, &pid);
    if ((vid != 0x56) || (pid != 0x42)){
      Serial.println(F("Can't find OV5642 module!"));
      delay(1000);
      continue;
    } else {
      Serial.println(F("OV5642 detected"));
      break;
    }
  }

  //Change to JPEG capture mode and initialize the OV5642 module
  camera.set_format(JPEG);
  camera.InitCAM();
  camera.set_bit(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);
  camera.clear_fifo_flag();
  camera.write_reg(ARDUCHIP_FRAMES, 0x00);

  camera.OV5642_set_JPEG_size(OV5642_320x240);
  delay(1000);

}

void startCapture(){
  camera.clear_fifo_flag();
  camera.start_capture();
}

void sendCapture(WiFiClient client, ArduCAM camera){
  uint8_t temp = 0, temp_last = 0;
  size_t len = camera.read_fifo_length();

  if (len >= MAX_FIFO_SIZE) {
    Serial.println(F("Over size."));
  }

  if (len == 0) {
    Serial.println(F("Size is 0."));
  } 

  camera.CS_LOW();
  camera.set_fifo_burst(); 
  if (!client.connected()) return;

  String response = "HTTP/1.1 200 OK\r\n";
  response += "Content-Type: image/jpeg\r\n";
  response += "Content-len: " + String(len) + "\r\n\r\n";
  client.print(response);

  i = 0;
  while (len--) {
    temp_last = temp;
    temp = SPI.transfer(0x00);

    //Read JPEG data from FIFO
    if ((temp == 0xD9) && (temp_last == 0xFF)) { //If find the end ,break while,
      buffer[i++] = temp;  //save the last  0XD9     
      //Write the remain bytes in the buffer
      if (!client.connected()) break;
      client.write(&buffer[0], i);
      is_header = false;
      i = 0;
      camera.CS_HIGH();
      break; 
    }  
    if (is_header == true) { 
      //Write image data to buffer if not full
      if (i < bufferSize)
        buffer[i++] = temp;
      else {
        //Write bufferSize bytes image data to file
        if (!client.connected()) break;
        client.write(&buffer[0], bufferSize);
        i = 0;
        buffer[i++] = temp;
      }        
    }
    else if ((temp == 0xD8) & (temp_last == 0xFF))
    {
      is_header = true;
      buffer[i++] = temp_last;
      buffer[i++] = temp;   
    } 
  }
}

void serveImage(WiFiClient client) {
  delay(1000);
  startCapture();
  Serial.println(F("CAM Capturing"));

  int total_time = millis();
  while (!camera.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK));
  total_time = millis() - total_time;
  Serial.print(F("capture total_time used (in miliseconds):"));
  Serial.println(total_time, DEC);

  Serial.println(F("CAM Capture Done."));
  total_time = millis();
  sendCapture(client, camera);
  total_time = millis() - total_time;
  Serial.print(F("send total_time used (in miliseconds):"));
  Serial.println(total_time, DEC);
  Serial.println(F("CAM send Done."));
}

void serveStream(WiFiClient client) {
  int total_time = 0;
  uint8_t temp = 0, temp_last = 0;
  String response = "HTTP/1.1 200 OK\r\n";
  response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n";
  client.print(response);

  while (1) {
    startCapture();
    total_time = millis();
    while (!camera.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK));
    total_time = millis() - total_time;
    Serial.print(F("frame capture total_time used (in miliseconds):"));
    Serial.println(total_time, DEC);
    size_t len = camera.read_fifo_length();

    if (len >= MAX_FIFO_SIZE) {
      Serial.println(F("Over size."));
      continue;
    }

    if (len == 0) {
      Serial.println(F("Size is 0."));
      continue;
    }

    camera.CS_LOW();
    camera.set_fifo_burst();

    if (!client.connected()) break;

    response = "--frame\r\n";
    response += "Content-Type: image/jpeg\r\n\r\n";
    client.print(response); 
    while (len--) {
      temp_last = temp;
      temp =  SPI.transfer(0x00);

      //Read JPEG data from FIFO
      if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
      {
        buffer[i++] = temp;  //save the last  0XD9     
        //Write the remain bytes in the buffer
        camera.CS_HIGH();; 
        if (!client.connected()) break;
        client.write(&buffer[0], i);
        is_header = false;
        i = 0;
      }  
      if (is_header == true) { 
        //Write image data to buffer if not full
        if (i < bufferSize)
          buffer[i++] = temp;
        else
        {
          //Write bufferSize bytes image data to file
          camera.CS_HIGH(); 
          if (!client.connected()) break;
          client.write(&buffer[0], bufferSize);
          i = 0;
          buffer[i++] = temp;
          camera.CS_LOW();
          camera.set_fifo_burst();
        }        
      } else if ((temp == 0xD8) & (temp_last == 0xFF)) {
        is_header = true;
        buffer[i++] = temp_last;
        buffer[i++] = temp;   
      } 
    }

    if (!client.connected()) break;
  }
}

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial);

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < "1.0.0") {
    Serial.println("Please upgrade the firmware");
  }

  setupArducam();
  connectNetwork();
}

void loop() {
  WiFiClient client = server.available();

  if (client) {

    String requestLine = String();
    boolean requestLineRead = false;
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        if (c == '\n' && currentLineIsBlank) {
          Serial.println(requestLine);

          // Simple HTTP request parsing
          if (requestLine.length() > 13 && requestLine.substring(0, 13).equalsIgnoreCase("GET /capture ")) {
            serveImage(client);
          } else if (requestLine.length() > 12 && requestLine.substring(0, 12).equalsIgnoreCase("GET /stream ")) {
            serveStream(client);
          } else {
            Serial.println("No route handler for this one");
          }
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
          requestLineRead = true;
        } else if (c != '\r') {
          // you've got a character on the current line
          currentLineIsBlank = false;
          if (!requestLineRead) {
            requestLine += c;
          }
        }
      }
    }

    // close the connection:
    delay(1);
    client.stop();
  }
}

which results in output like this on the serial:

00:33:36.980 -> capture total_time used (in miliseconds):1157
00:33:36.980 -> CAM Capture Done.
00:33:37.207 -> send total_time used (in miliseconds):217
00:33:37.207 -> CAM send Done.
00:33:37.431 -> GET /favicon.ico HTTP/1.1
00:33:37.431 -> No route handler for this one
00:33:39.328 -> GET /capture HTTP/1.1
00:33:40.315 -> CAM Capturing
00:33:42.060 -> capture total_time used (in miliseconds):1737
00:33:42.060 -> CAM Capture Done.
00:33:42.275 -> send total_time used (in miliseconds):213
00:33:42.275 -> CAM send Done.
00:33:42.308 -> GET /favicon.ico HTTP/1.1
00:33:42.308 -> No route handler for this one
00:33:44.113 -> GET /capture HTTP/1.1
00:33:45.137 -> CAM Capturing
00:33:46.129 -> capture total_time used (in miliseconds):1008
00:33:46.129 -> CAM Capture Done.
00:33:46.349 -> send total_time used (in miliseconds):213
00:33:46.349 -> CAM send Done.
00:33:46.416 -> GET /favicon.ico HTTP/1.1
00:33:46.416 -> No route handler for this one

Is there anything I could do to improve capture time? Thanks in advance!!