Electron with serial jpeg camera sending partial images to the server

electron
Tags: #<Tag:0x00007f1ca5fa7238>

#1

I am using an electron with this serial jpeg camera to take a photo every 1 minute and stream the image to the server. But the problem is sometimes the full size image is sent and sometimes the image is sent partially(see image examples attached below). Matter of fact most of the images are incomplete, (Out of every 10 images streamed to the server only 2 are full size) .What could be wrong here. Here is my code
full half

#include "Particle.h"

#define HOST "xxxxx.herokuapp.com"
//CAMERA INITIALIZATION
#define VC0706_GET_FBUF_LEN 0x34
#define CAMERABUFFSIZ 100
uint8_t  serialNum;
uint8_t  camerabuff[CAMERABUFFSIZ+1];
uint8_t  bufferLen;
uint16_t frameptr;

TCPClient client;
int port = 80;

byte incomingbyte;
int a=0x0000,j=0,k=0,count=0;
uint8_t MH,ML;
boolean EndFlag=0;
int coun;

void setup() {
    Serial.begin(4800);
    Serial1.begin(38400);
    delay(1000);
}

void loop() {
   streamImageToServer();
   delay(60000);        
 
}

void streamImageToServer(){
    SendResetCmd();
    delay(4000);
    SendTakePhotoCmd();
    delay(1000);
    uint16_t jpglen = camFrameLength();
        byte a[32];
        Serial.println(jpglen);
        String end_request = "";
        String fileName = "myjpg";
      //connect
      if (client.connect(HOST,80)) {
              Serial.println("conected to the server");
               String start_request = "";

               start_request = start_request  + "--AaB03x" + "\r\n"
                                       + "Content-Disposition: form-data; name=picture; filename=" + fileName + "\r\n"
                                       + "Content-Type: image/jpeg" + "\r\n" + "Content-Transfer-Encoding: binary" + "\r\n"+ "\r\n";
              end_request = end_request +  "\r\n--AaB03x --\r\n";
              uint16_t len = start_request.length() + end_request.length()+ (jpglen*32);
              client.println("POST /api/images HTTP/1.1");
              client.println("Host: xxxxxxxxxxx.herokuapp.com");
              client.println("Accept: */*");
              client.println("Content-Type: multipart/form-data; boundary=AaB03x");
              client.print("Content-Length: ");
              client.println(len);
              client.println();
              client.print(start_request);


       while(Serial1.available()>0)
        {
          incomingbyte=Serial1.read();

        }

        while(!EndFlag)
       {
        Serial.println("First time");
          j=0;
          k=0;
          count=0;
          SendReadDataCmd();

          delay(25);
           while(Serial1.available()>0)
           {
                incomingbyte=Serial1.read();
                k++;
                if((k>5)&&(j<32)&&(!EndFlag))
                {
                a[j]=incomingbyte;
                Serial.println(incomingbyte,HEX);
                if((a[j-1]==0xFF)&&(a[j]==0xD9))
                 EndFlag=1;
                 j++;
                 count++;
                }
           }

          int buffer_size = sizeof(a);
          int coun = client.write(a,buffer_size);
          }
           delay(1000);
           client.print(end_request);
           client.stop();
           EndFlag = 0;
           StopTakePhotoCmd();
  }
}

/****Util methods*****/
void common_init(void) {
  frameptr  = 0;
  bufferLen = 0;
  serialNum = 0;
}

void sendCommand(uint8_t cmd, uint8_t args[] = 0, uint8_t argn = 0) {
    Serial1.write((byte)0x56);
    Serial1.write((byte)serialNum);
    Serial1.write((byte)cmd);

    for (uint8_t i=0; i<argn; i++) {
      Serial1.write((byte)args[i]);
    }

}
uint8_t readResponse(uint8_t numbytes, uint8_t timeout) {
  uint8_t counter = 0;
  bufferLen = 0;
  int avail;

  while ((timeout != counter) && (bufferLen != numbytes)){
    avail = Serial1.available();
    if (avail <= 0) {
      delay(1);
      counter++;
      continue;
    }
    counter = 0;
    // there's a byte!
    camerabuff[bufferLen++] = Serial1.read();
  }
  return bufferLen;
}

boolean verifyResponse(uint8_t command) {
  if ((camerabuff[0] != 0x76) ||
      (camerabuff[1] != serialNum) ||
      (camerabuff[2] != command) ||
      (camerabuff[3] != 0x0))
      return false;
  return true;

}

boolean runCommand(uint8_t cmd, uint8_t *args, uint8_t argn,
			   uint8_t resplen, boolean flushflag) {
  if (flushflag) {
    readResponse(100, 10);
  }

  sendCommand(cmd, args, argn);
  if (readResponse(resplen, 200) != resplen)
    return false;
  if (! verifyResponse(cmd))
    return false;
  return true;
}

uint32_t camFrameLength(void) {
  uint8_t args[] = {0x01, 0x00};
  if (!runCommand(VC0706_GET_FBUF_LEN, args, sizeof(args), 9, true))
    return 0;

  uint32_t len;
  len = camerabuff[5];
  len <<= 8;
  len |= camerabuff[6];
  len <<= 8;
  len |= camerabuff[7];
  len <<= 8;
  len |= camerabuff[8];

  return len;
}


void SendResetCmd()
{
      Serial1.write(0x56);
      Serial1.write((byte)0);
      Serial1.write(0x26);
      Serial1.write((byte)0);
}

void SendTakePhotoCmd()
{
      Serial1.write(0x56);
      Serial1.write((byte)0);
      Serial1.write(0x36);
      Serial1.write(0x01);
      Serial1.write((byte)0);
}
void SendReadDataCmd()
{
      MH=a/0x100;
      ML=a%0x100;
      Serial1.write(0x56);
      Serial1.write((byte)0);
      Serial1.write(0x32);
      Serial1.write(0x0c);
      Serial1.write((byte)0);
      Serial1.write(0x0a);
      Serial1.write((byte)0);
      Serial1.write((byte)0);
      Serial1.write(MH);
      Serial1.write(ML);
      Serial1.write((byte)0);
      Serial1.write((byte)0);
      Serial1.write((byte)0);
      Serial1.write(0x20);
      Serial1.write((byte)0);
      Serial1.write(0x0a);
      a+=0x20;                           
}

void StopTakePhotoCmd() {
  Serial1.write((byte)0x56);
  Serial1.write((byte)0x00);
  Serial1.write((byte)0x36);
  Serial1.write((byte)0x01);
  Serial1.write((byte)0x03);
}

}

#2

As some side notes
It’s not best style to have global variables with non-descriptive names (e.g. int a) - even less so when you are “reusing” the same name for something else locally (e.g. byte a[32]).
You are also declaring int coun; globally and locally (in streamImageToServer()).

Also try to adopt a consistent indentation scheme that helps other readers of your code to follow the actual flow of code

There is also no need to “compress” code lines.
Readability of if(k > 5 && j < 32 && !EndFlag) is typically better than if((k>5)&&(j<32)&&(!EndFlag))

TCP communication works best/fastest on Electrons when you send 512 byte chunks.

One possible issue I see is that you are using int buffer_size = sizeof(a); but can’t be always certain that you actually have the buffer filled completely duo to while(Serial1.available()>0)

Another is that you never reset a (which should better be named address) and is used in SendReadDataCmd() to push on the portion you intend to read from the frame buffer.

I’m also not sure where the factor 32 comes from in this line

Some untested code
#include "Particle.h"
SYSTEM_THREAD(ENABLED)

const int  VC0706_GET_FBUF_LEN   = 0x34;
const int  CAMERABUFFSIZ         = 100;
const int  PORT                  = 80;
const char HOST[]                = "xxxxx.herokuapp.com";
const char startRequestPattern[] = "--AaB03x\r\n"
                                   "Content-Disposition: form-data; name=picture; filename=%s\r\n"
                                   "Content-Type: image/jpeg\r\n"
                                   "Content-Transfer-Encoding: binary\r\n\r\n";
const char end_request[]         = "\r\n--AaB03x --\r\n";

const int  chunkSize             = 32; // must be multiple of 8
uint8_t    serialNum;
uint8_t    camerabuff[CAMERABUFFSIZ + 1];
uint8_t    bufferLen;
uint16_t   frameptr;
uint16_t   address = 0x0000;
           
TCPClient  client;

void setup() {
  Serial.begin(4800);
  Serial1.begin(38400);
  delay(1000);
}

void loop() {
  streamImageToServer();
  delay(60000);
}

void streamImageToServer() {
  SendResetCmd();
  delay(4000);
  SendTakePhotoCmd();
  delay(1000);

  byte chunk[chunkSize];
  char fileName[] = "myjpg";
  uint16_t jpglen = camFrameLength();

  Serial.println(jpglen);

  if (client.connect(HOST, PORT)) {
    Serial.println("conected to the server");
    char start_request[sizeof(startRequestPattern) + strlen(filename)];
    snprintf(start_request, sizeof(start_request), startRequestPattern, filename);

    uint16_t len = strlen(start_request) + strlen(end_request) + jpglen;
    client.printf("POST /api/images HTTP/1.1\r\n"
                  "Host: %s\r\n");
                  "Accept: */*\r\n");
                  "Content-Type: multipart/form-data; boundary=AaB03x\r\n");
                  "Content-Length: %d\r\n\r\n", HOST, len);
    client.print(start_request);

    while (Serial1.read() >= 0);    // flush RX buffer

    address = 0x0000;
    boolean EndFlag = false;
    while (!EndFlag) {
      byte incomingbyte;
      int  totalRead = 0;
      int  dataRead = 0;
      
      SendReadDataCmd();

      while (dataRead < chunkSize) {
        if (Serial1.available()) {
          incomingbyte = Serial1.read();
          totalRead++;
          if (totalRead > 5) {    // ignore first 5 bytes (76 00 32 00 00 command response)
            chunk[dataRead] = incomingbyte;
            if ((chunk[dataRead - 1] == 0xFF) && (chunk[dataRead] == 0xD9))
              EndFlag = true;
            dataRead++;
          }
        }
      }

      client.write(chunk, dataRead);
    }
    delay(1000);
    client.print(end_request);
    client.stop();
    StopTakePhotoCmd();
  }
}

/****Util methods*****/
void common_init(void) {
  frameptr = 0;
  bufferLen = 0;
  serialNum = 0;
}

void sendCommand(uint8_t cmd, uint8_t args[] = 0, uint8_t argn = 0) {
  Serial1.write(0x56);
  Serial1.write(serialNum);
  Serial1.write(cmd);

  for (uint8_t i = 0; i < argn; i++)
    Serial1.write(args[i]);
}

uint8_t readResponse(uint8_t numbytes, uint8_t timeout) {
  uint8_t counter = 0;
  bufferLen = 0;

  while ((timeout != counter) && (bufferLen != numbytes)) {
    if (Serial1.available() <= 0) {
      delay(1);
      counter++;
      continue;
    }
    counter = 0;
    // there's a byte!
    camerabuff[bufferLen++] = Serial1.read();
  }
  return bufferLen;
}

boolean verifyResponse(uint8_t command) {
  return (camerabuff[0] == 0x76)
      && (camerabuff[1] == serialNum)
      && (camerabuff[2] == command)
      && (camerabuff[3] == 0x00);
}

boolean runCommand(uint8_t cmd, uint8_t *args, uint8_t argn, uint8_t resplen, boolean flushflag) {
  if (flushflag)
    readResponse(100, 10);
  sendCommand(cmd, args, argn);
  return (readResponse(resplen, 200) == resplen) && (verifyResponse(cmd));
}

uint32_t camFrameLength(void) {
  uint8_t args[] = { 0x01, 0x00 };
  if (!runCommand(VC0706_GET_FBUF_LEN, args, sizeof(args), 9, true))
    return 0;

  uint32_t len;
  len = camerabuff[5];
  len <<= 8;
  len |= camerabuff[6];
  len <<= 8;
  len |= camerabuff[7];
  len <<= 8;
  len |= camerabuff[8];

  return len;
}

void SendResetCmd() {
  Serial1.write(0x56);
  Serial1.write(0x00);
  Serial1.write(0x26);
  Serial1.write(0x00);
}

void SendTakePhotoCmd() {
  Serial1.write(0x56);
  Serial1.write(0x00);
  Serial1.write(0x36);
  Serial1.write(0x01);
  Serial1.write(0x00);
}

void SendReadDataCmd() {
  Serial1.write(0x56);
  Serial1.write(0x00);
  Serial1.write(0x32);
  Serial1.write(0x0c);
  Serial1.write(0x00);
  Serial1.write(0x0a);
  Serial1.write(0x00);
  Serial1.write(0x00);
  Serial1.write(address >>   8);
  Serial1.write(address & 0xFF);
  Serial1.write(0x00);
  Serial1.write(0x00);
  Serial1.write(0x00);
  Serial1.write(chunkSize);
  Serial1.write(0x00);
  Serial1.write(0x0a);
  address += chunkSize;
}

void StopTakePhotoCmd() {
  Serial1.write(0x56);
  Serial1.write(0x00);
  Serial1.write(0x36);
  Serial1.write(0x01);
  Serial1.write(0x03);
}

#3

Thank you @ScruffR I will give this a try. Regarding sending 512 chunks, initially I started with large chunks (1024) but it was still sending partial images, then tried 512 chunks , the same outcome, so I thought maybe it was the issue with sending large data.


#4

@ScruffR Thank you again. After fixing minor compilation errors(filename not defined in this scope…etc.) I ran the code but nothing is sent to the server. Also, I noticed that after it reaches the end of the file( 0xFF && 0xD9 it get stuck in infinite loop between while (dataRead < chunkSize) and if (Serial1.available()) inside while loop but outside If statement.It seems at that time dataRead never exceeds chunkSize.


#5

You can try to change thiss

to

        while (dataRead < chunkSize && !EndFlag) {