The Arduino Processing Client Fails to Download the File to the PHP Server

advertisements

Background I want pressure sensors (they are working) to trigger the built-in camera and take a picture when cat goes to bed, upload the pic and email me so I can go check out the real-time image on web-site.

PHP Server I have a php server running 127.0.0.1:8080 on the root of this structure:

 NetworkedCat -->
                  data --> script-cat.php
                  index.html
                  NetworkedCat.pde
                  img.jpg
                  save2web.php
                  swiftmailer --> libs, etc

Testing on browser, save2web.php and cat-script.php are working, that is, scripts are uploading and emailing.

Arduino

The Arduino app should do the following:

  1. receive an input from a pressure sensor
  2. verify whether threshold is surpassed
  3. take picture from build-in camera
  4. upload pic to website
  5. send mail informing about the upload

The pressure sensor () is also reading and printing, and threshold has been calibrated.

But NetworkedCat.pde is NOT triggered by the serial events.

Please note:

Arduino Processing opens localhost at another port 80, because php server works on 8080.

If I shorten the Processing code, and test the image capture and upload only, it works. so, the bug must be related to the serial events.

Why is the Processing code below not working?

/*Serial String reader
Context: Arduino

Reads in a string of characters until it gets a linefeed (ASCII 10).
then converts the string into a number
*/

import processing.serial.*;
import processing.video.*;
import processing.net.*;

Serial myPort;              //the serial port
float sensorValue = 0;      //the value form the sensor
float prevSensorValue = 0;  //previous value from the sensor
int threshold = 200;        //above this number, the cat is on the mat

int currentTime = 0;       //the current time as a single number
int lastMailTime = 0;      //last minute you sent a mail
int mailInterval = 60;     //minimum seconds betweeen mails
String mailUrl = "cat-script.php";
int lastPicture = 0;       //last minute you sent a picture
int lastPictureTime = 0;   //last minute you sent a picture
int pictureInterval = 10;  //minimum seconds between pictures

Capture myCam;            //camera capture library instance
String fileName = "img.jpg"; //file name for the picture

//location on your server for the picture script:
String pictureScriptUrl = "save2web.php";
String boundary = "----H4rkNrF"; //string boundary for the post request

Client thisClient;        //instance for the net library

//float xPos = 0;             //horizontal position of the graph
//float lastXPos = 0;         //previous horizontal position  

void setup(){
  size(400, 300);
  //list all the available serial ports
  println(Serial.list());

  myPort = new Serial(this, Serial.list()[7], 9600);

  //reads bytes into a buffer until you get a newline (ASCII 10);
  myPort.bufferUntil('\n');

  //set initial background and smooth drawing:
  background(#543174);
  smooth();
  //for a list of cameras on your computer, use this line:
  println(Capture.list());

  //use the default camera for capture at 30 fps
  myCam = new Capture(this, width, height, 30);
  myCam.start();
}

void draw(){
  //make a single number fmor the current hour, minute, and second
  currentTime = hour() * 3600 + minute() * 60 + second();

  if (myCam.available() == true) {
    //draw the camera image to the screen;
    myCam.read();
    set(0, 0, myCam);

    //get the time as a string
    String timeStamp = nf(hour(), 2) + ":" + nf(minute(), 2)
    + ":" + nf(second(), 2) + "   " + nf(day(), 2) + "-"
    + nf(month(), 2) + "-" + nf(year(), 4);

    //draw a dropshadow for the time text:
    fill(15);
    text(timeStamp, 11, height - 19);
    //draw the main time next
    fill(255);
    text(timeStamp, 10, height - 20);
  }
}

void serialEvent (Serial myPort){
  //get the ASCII string
  String inString = myPort.readStringUntil('\n');

  if (inString != null){
    //trim off any whitespace:
    inString = trim(inString);
    //convert to an int and map to the screen height
    sensorValue = float(inString);
    //println(sensorValue);
    sensorValue = map(sensorValue, 0, 1023, 0, height);
    println(sensorValue);

    if (sensorValue > threshold){
      if(currentTime - lastPictureTime > pictureInterval){
        PImage thisFrame = get();
        thisFrame.save(fileName);
        postPicture();
        lastPictureTime = currentTime;
      }
      //if the last reading was less than the threshold
      //then the cat just got on the mat
      if(prevSensorValue <= threshold){
        println("Cat is on the mat.");
        sendMail();
      }
    }
    else{
      //if the sensor value is less than the threshold,
      //and the previous value was greater, then the cat
      //just left the mat
      if (prevSensorValue > threshold){
        println("Cat is not on the mat.");
      }
    }
    //save the current value for the next time
    prevSensorValue = sensorValue;
  }
}

void sendMail(){
  //how long has passed since the last mail
  int timeDifference = currentTime - lastMailTime;

  if( timeDifference > mailInterval){
    String[] mailScript = loadStrings(mailUrl);
    println("results from mail script:");
    println(mailScript);

    //save the current minute for next time
    lastMailTime = currentTime;
  }
}

EDIT:

By exclusion, the bug is in this last function, but I still haven't found it:

void postPicture(){
 //load the saved image into an array of bytes
  byte[] thisFile=loadBytes(fileName);

//open a new connection to the server
 thisClient = new Client(this, "localhost", 80);

//make an HTTP POST request:
thisClient.write("POST " + pictureScriptUrl + " HTTP/1.1\n");

thisClient.write("Host: localhost\n");

//tell the server you're sending the POST in multiple parts
//and send a unique string that will delineate the parts
thisClient.write("Content-Type: multipart/form-data; boundary=");

thisClient.write(boundary + "\n");

//form the beginning of the request
String requestHead ="--" + boundary + "\n";

requestHead +="Content-Disposition: form-data; name=\"file\"; ";
requestHead += "filename=\"" + fileName + "\"\n";
requestHead +="Content-Type: image/jpeg\n\n";

//form the end of the request
String tail="\n\n--" + boundary + "--\n";

//calculate and send the length of the total request
//including the head of the request, the file, and the tail
int contentLength = requestHead.length() + thisFile.length + tail.length();

 thisClient.write("Content-Length: " + contentLength + "\n\n");
 //send the header of the request, the file and the tail
 thisClient.write(requestHead);
 thisClient.write(thisFile);
 thisClient.write(tail);
 }

This is what is being raised:

java.lang.NullPointerException
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at    javax.imageio.stream.FileCacheImageOutputStream.close(FileCacheImageOutputStream.java:238)
at com.sun.imageio.stream.StreamCloser$CloseAction.performAction(StreamCloser.java:130)
at com.sun.imageio.stream.StreamCloser$1.run(StreamCloser.java:74)
at java.lang.Thread.run(Thread.java:745)

PLUS a Not a Number exception:

sensorValue = map(NaN, 1023, 0, height);

My system is Unix.


Probably better to ask this question on Arduino specific version of stack exchange.

My suggestion Let Arduino Processing take the picture (of the cat). And let it upload the picture to the PHP web-app.

Then the PHP web-app, should receive the file upload (the picture) and sent an e-mail to those who like to receive it by e-mail.

Seems you are running the PHP server on port 8080, then the Arduino Processing app, also need to connect to that port! So update your code, so that the client connects to the server:

The Arduino Processing (the client), need to know, where the PHP server is located on the network. So it need to know the DNS name or the IP-address of the server. So correct the string "name-or-ip", in the below code!

//open a new connection to the server
thisClient = new Client(this, "name-or-ip", 8080);

Tip: In case Arduino Processing is running on the same computer as PHP Server, then "localhost" will work as server connection.