Transfer photo image faster

Hi,

I'm using a Raspberry pi to issue a get request to my arduino web server. The get request is issued via PhP (on my pi's apache server), which then takes a photo and returns the photo over the ethernet back.

This works great! --- Except for one thing. It's slow. A 640x480 image is almost 20 seconds to transfer.

Is there a way I can speed it up? Here's my code below. I've tried to change the uint8_t buffer to uint64_t buffer but the ethernet client complains in (EthernetClient.h) that it expects an 8 bit unsigned buffer, not 64.

So how do I get it to go faster? Overclock the arduino? Would a mega board be faster? Something else? Any suggestions would be appreciated. I hope to get it to about 1 second delay if not faster.

Thanks!

#include <Adafruit_VC0706.h>
#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
#include <SoftwareSerial.h>

#define chipSelect 4
#define REQ_BUF_SZ   20

// Using SoftwareSerial (Arduino 1.0+) or NewSoftSerial (Arduino 0023 & prior):
#if ARDUINO >= 100
SoftwareSerial cameraconnection = SoftwareSerial(2, 3);
#else
NewSoftSerial cameraconnection = NewSoftSerial(2, 3);
#endif

Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection);

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 1, 101);
EthernetServer server(80);
File webFile;
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer

void setup() {

    // Open serial communications and wait for port to open:
    Serial.begin(9600);
  
     pinMode(7, OUTPUT);
 
    //Try to locate the camera
    if (cam.begin()) {
         Serial.println("Camera Found!");
     } else {
        Serial.println("No camera found?");
        return;
      }     

      /*
      // see if the card is present and can be initialized:
        if (!SD.begin(chipSelect)) {
           Serial.println("Card failed, or not present");
                //don't do anything more:
            return;
         }else{
            Serial.println("Card Found!");
         }
       */

    // start the Ethernet connection and the server:
    Ethernet.begin(mac, ip);
    server.begin();
    Serial.print("server is at ");
    Serial.println(Ethernet.localIP());
}


void loop() {
  // listen for incoming clients
  EthernetClient client = server.available();
  
  if (client) {
    
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        
        char c = client.read();
         if (req_index < (REQ_BUF_SZ - 1)) {
            HTTP_req[req_index] = c;          // save HTTP request character
            req_index++;
            }
        Serial.write(c);
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: image/jpeg");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          //client.println("Refresh: 15");  // refresh the page automatically every 5 sec
          client.println();

       
          Serial.println("VC0706 Camera snapshot test");
          cam.reset();
          cam.begin();
        // Print out the camera version information (optional)
          char *reply = cam.getVersion();
          if (reply == 0) {
            Serial.print("Failed to get version");
          } else {
            Serial.println("-----------------");
            Serial.print(reply);
            Serial.println("-----------------");
          }
          
          cam.setImageSize(VC0706_640x480);
          uint8_t imgsize = cam.getImageSize();
          Serial.print("Image size: ");
          if (imgsize == VC0706_640x480) Serial.println("640x480");
          if (imgsize == VC0706_320x240) Serial.println("320x240");
          if (imgsize == VC0706_160x120) Serial.println("160x120");
          
          
            if (!cam.takePicture())
                  Serial.println("Failed to snap!");
            else{
              Serial.println("Picture taken! -- Let's go!");
              
                /*
                //Remove the file if it's already saved
                 char filename[13];
                 strcpy(filename, "IMAGE00.JPG");
                Serial.print("Storing to:");
                Serial.println(filename);
                if(SD.exists(filename)){
                  Serial.println("Removing file!");
                  SD.remove(filename);
                }
          
                //Save the image to the SD card!
                File imgFile = SD.open(filename, FILE_WRITE);

                if(imgFile){
                  Serial.println("File successfully opened");
                }else{
                  Serial.println("File not opened!");
                } 
                */
                
                // Get the size of the image (frame) taken  
                uint16_t jpglen = cam.frameLength();
                Serial.print("Storing ");
                Serial.print(jpglen, DEC);
                Serial.print(" byte image.");

                int32_t time = millis();
                pinMode(8, OUTPUT);
                // Read all the data up to # bytes!
                byte wCount = 0; // For counting # of writes
                
                digitalWrite(7,HIGH);
                
                while (jpglen > 0) {
                  // read 32 bytes at a time;
                  uint8_t *buffer;
                  uint8_t bytesToRead = min(64, jpglen); 
                  //uint64_t *buffer;
                  //uint64_t bytesToRead = min(256, jpglen);
                  buffer = cam.readPicture(bytesToRead);
                  //imgFile.write(buffer, bytesToRead);
                  client.write(buffer, bytesToRead);
                  if(++wCount >= 64) { // Every 2K, give a little feedback so it doesn't appear locked up
                    Serial.print('.');
                    wCount = 0;
                  }
                 //Serial.print("Read ");  Serial.print(bytesToRead, DEC); Serial.println(" bytes");
                 jpglen -= bytesToRead;
                 }
                 digitalWrite(7,LOW);

                //imgFile.close();
                time = millis() - time;
                Serial.println("Image Saved!");
                Serial.print(time); Serial.println(" ms elapsed");
            }

         
          req_index = 0;
          StrClear(HTTP_req, REQ_BUF_SZ);
          break;
         }
         
         if (c == '\n') {
             // last character on line of received text
             // starting new line with next character read
             currentLineIsBlank = true;
         } 
         else if (c != '\r') {
              // a text character was received from client
              currentLineIsBlank = false;
         }
         
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
    Ethernet.maintain();
  }
}

// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
{
    for (int i = 0; i < length; i++) {
        str[i] = 0;
    }
}

Hi

You don't seem to be saving the file to the SD card - can you therefore remove all that functionality?

For my Freetronics Ethermega card ethernet and SD share SPI - I have to select and deselect them to talk to them. You code does not seem to be doing that - is this an issue?

From what I can see, since you are not saving the image to SD the delay is likely coming from the serial communication with the camera.

Is the camera connecting via serial? Can you crank the serial speed up to 115K bps instead of 9600?

You seem to have the camera and serial monitor operating on the same serial connection - is that right and will it work?

Anyway these are just some thoughts - it may contain something you can use.

Catweazle NZ

two thoughts

  1. double buffering ? will be hard on single threaded Arduino, might need to rewrite libs for that

  2. use a big buffer to read from the camera (e.g. 1024) but send it in chunks of 64 byte over ethernet

while (jpglen > 0) 
{
  uint8_t *buffer;
  int btr = min(1024, jpglen);
  jpglen -= btr;
  buffer = cam.readPicture(btr);

  while (btr > 0)
  {
    int btw = min(64, btr);
    client.write(buffer, btw);
    btr -= btw;
  }
  // feedback
  if( ++wCount >= 64) 
  { 
    Serial.print('.');
    wCount = 0;
  }
}

Don't know if it will be faster or not, can you test?

Thanks robtillaart and CatweazleNZ.

In response to your messages.

  1. Changing the baud did nothing. It was about 18.5K milliseconds before and it's the same even at the higher baud. Does that mean that it's not baud dependent?

Doing some math: My jpg is 48000 bytes. That means at a baud of 9600, we transmit 1200 bytes per second. That comes out to 40 seconds. Yet I transmit in 18.

If you look at the FAQ: F.A.Q. | TTL Serial Camera | Adafruit Learning System

It says the camera transmits at 38400 baud. Does that mean the fastest I will ever get for a 640x480 image is approximately 10 seconds? 48000 bytes/ (38400/8) = approx 10?

  1. The double buffer code didn't work -- it returned nothing. I tried moving the jpglen -= btr; to the end of the loop but then it never seemed to end.

After some more exploration, it seems definitely faster but the string returned contains errors. So the jpg doesn't show up.

If I change the code such that instead of 1024 it's 64, it starts working again:

                      uint8_t *buffer;
                      uint64_t btr = min(64, jpglen);
                      jpglen -= btr;
                      buffer = cam.readPicture(btr);
                    
                      while (btr > 0)
                      {
                        uint8_t btw = min(64, btr);
                        client.write(buffer, btw);
                        btr -= btw;
                      }
                      // feedback
                      if( ++wCount >= 64)
                      {
                        Serial.print('.');
                        wCount = 0;
                      }

Perhaps, the cam.readPicture can't candle more than 64 bytes at a time? Not sure. :-/

CatweazleNZ, I'm not sure what you mean by the statement that the camera and serial operate on the same serial connection? It seems to work fine for me... Just a bit slower.

  1. Changing the baud did nothing. It was about 18.5K milliseconds before and it's the same even at the higher baud. Does that mean that it's not baud dependent?

Where do you set the baud rate for the SoftwareSerial instance? It looks to me like you let the library manage the speed (correctly).

Doing some math: My jpg is 48000 bytes. That means at a baud of 9600, we transmit 1200 bytes per second. That comes out to 40 seconds. Yet I transmit in 18.

Your math is wrong. 9600 / 10 = 960, not 1200. There are start and stop bits that you can't ignore.

It says the camera transmits at 38400 baud. Does that mean the fastest I will ever get for a 640x480 image is approximately 10 seconds? 48000 bytes/ (38400/8) = approx 10?

First, 38400 baud means 3,840 bytes per second, not 4,800. Second, how did you get get 48,000 from multiplying 640 by 480? I get 307,200. If it is a black and white image, with one bit per pixel, that's 38,400 bytes.