Android HttpUrlConnection с Arduino Uno

Всех с новым годом :)

Я работаю над проектом домашней автоматизации, используя приложение Arduino Uno и Android, которое может обмениваться данными через мой маршрутизатор TP-Link.

У меня возникает java.io.IOException: неожиданное завершение потока при подключении, когда я отправляю запрос на Arduino и не могу его решить.

01-01 16:51:47.771 10592-11256/com.projects.mahmoudmahdi.etherdroid E/EtherDroid: java.io.IOException: unexpected end of stream on Connection{192.168.1.215:80, proxy=DIRECT@ hostAddress=192.168.1.215 cipherSuite=none protocol=http/1.1} (recycle count=0)
    at com.android.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:210)
    at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80)
    at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:904)
    at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:788)
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:443)
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:388)
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:501)
    at com.projects.mahmoudmahdi.etherdroid.MainActivity$commandArduino.doInBackground(MainActivity.java:50)
    at com.projects.mahmoudmahdi.etherdroid.MainActivity$commandArduino.doInBackground(MainActivity.java:31)
    at android.os.AsyncTask$2.call(AsyncTask.java:295)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    at java.lang.Thread.run(Thread.java:818)
Caused by: java.io.EOFException: \n not found: size=0 content=...
    at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:200)
    at com.android.okhttp.internal.http.HttpConnection.readHeaders(HttpConnection.java:220)
    at com.android.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:199)
    at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80) 
    at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:904) 
    at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:788) 
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:443) 
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:388) 
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:501) 
    at com.projects.mahmoudmahdi.etherdroid.MainActivity$commandArduino.doInBackground(MainActivity.java:50) 
    at com.projects.mahmoudmahdi.etherdroid.MainActivity$commandArduino.doInBackground(MainActivity.java:31) 
    at android.os.AsyncTask$2.call(AsyncTask.java:295) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
    at java.lang.Thread.run(Thread.java:818) 

Мой скетч на ардуино:

#include <UIPEthernet.h>


int relay = 2;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };   //physical mac address
byte ip[] = { 192, 168, 1, 215 };                      // ip in lan (that's what you need to use in your browser. (F("192.168.1.200"))
byte gateway[] = { 192, 168, 1, 10 };                   // internet access via router
byte subnet[] = { 255, 255, 255, 0 };                  //subnet mask

EthernetServer server(80);                             //server port

String readString;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);

  pinMode(relay, OUTPUT);

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


void loop() {
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    //clearing string for next read
    readString = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {
          //store characters to string
          readString += c;
          Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {

          Serial.println(readString); //print to serial monitor for debuging

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");

          //stopping client
          client.stop();

          //controls the Arduino if you press the buttons
          if (readString.indexOf("?relay1on") > 0) {
            digitalWrite(relay, LOW);
            Serial.println("relay is on");
          }
          if (readString.indexOf("?relay1off") > 0) {
            digitalWrite(relay, HIGH);
            Serial.println("relay is off");
          }
        }
      }
    }
  }
}

Мой код Android:

package com.projects.mahmoudmahdi.etherdroid;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import java.net.HttpURLConnection;
import java.net.URL;

public class MainActivity extends AppCompatActivity implements OnClickListener {

    private class commandArduino extends AsyncTask<URL, Void, Integer> {
        @Override
        protected Integer doInBackground(URL... url) {

            int response = 0;

            if (isNetworkAvailable()) {

                // params comes from the execute() call: params[0] is the url.
                try {
                    HttpURLConnection urlConnection = (HttpURLConnection) url[0].openConnection();
                    urlConnection.setRequestMethod("GET");

                    urlConnection.connect();

                    switch (urlConnection.getResponseCode()) {
                        case HttpURLConnection.HTTP_OK:
                            Log.d("RESPONSE", "**OK**");
                            break; // fine, go on
                        case HttpURLConnection.HTTP_GATEWAY_TIMEOUT:
                            Log.d("RESPONSE", "**gateway timeout**");
                            break;// retry
                        case HttpURLConnection.HTTP_UNAVAILABLE:
                            Log.d("RESPONSE", "**unavailable**");
                            break;// retry, server is unstable
                        default:
                            Log.d("RESPONSE", "**unknown response code**.");
                            break; // abort
                    }

                    urlConnection.disconnect();
                    Log.d("RESPONSE", "Aborting download of dataset.");
                } catch (Exception e) {
                    Log.e("EtherDroid", "STACKTRACE");
                    Log.e("EtherDroid", Log.getStackTraceString(e));
                }
            }
            return response;
        }
    }

    static boolean relay1 = false;
    commandArduino cmdAdn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button Relay1 = (Button) findViewById(R.id.relay1);

        Relay1.setOnClickListener(this);


    }

    private boolean isNetworkAvailable() {
        boolean available = false;
        /** Getting the system's connectivity service */
        ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

        /** Getting active network interface  to get the network's status */
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

        if (networkInfo != null && networkInfo.isAvailable())
            available = true;

        /** Returning the status of the network */
        return available;
    }

    public void onClick(View thisView) {
        cmdAdn = new commandArduino();
        switch (thisView.getId()) {
            case R.id.relay1:
                if (!relay1) {
                    relay1 = true;
                    try {
                        cmdAdn.execute(new URL("http://192.168.1.215/?relay1on"));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Toast.makeText(getApplicationContext(), "relay1 on", Toast.LENGTH_SHORT).show();
                } else {
                    relay1 = false;
                    try {
                        cmdAdn.execute(new URL("http://192.168.1.215/?relay1off"));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Toast.makeText(getApplicationContext(), "relay1 off", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

person Mahmoud Mahdi    schedule 01.01.2016    source источник


Ответы (1)


В общем, вам не хватает:

  1. Отправить данные в ответ
  2. Предоставление сокету времени для отправки данных перед закрытием
  3. Чтение ответа Android перед закрытием соединения.

Более подробно, обязательно отправьте некоторые данные после установления соединения:

client.println("HTTP/1.1 200 OK"); //send new page
client.println("Content-Type: text/html");
client.println("Connection: close");  // the connection will be closed after completion of the response
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("</html>");

// Give it time to close
delay(1);
client.close();

Ссылка: https://www.arduino.cc/en/Tutorial/WebServer, здесь полный пример:

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(); // NOTE: You're missing this in your code
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          // output the value of each analog input pin
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
            int sensorReading = analogRead(analogChannel);
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is ");
            client.print(sensorReading);
            client.println("<br />");
          }
          client.println("</html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
    Ethernet.maintain();
  }
}

Кроме того, рассмотрите возможность использования json вместо html для ответа Arduino. Для этого вам нужно изменить:

client.println("HTTP/1.1 200 OK");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.println();
client.println("{\"status\":\"ok\"}"); // Or whatever you want to change
person jlhonora    schedule 01.01.2016
comment
Кстати, как я могу отправить JSON? - person Mahmoud Mahdi; 01.01.2016
comment
@MahmoudMahdi добавил некоторые детали - person jlhonora; 01.01.2016
comment
Большое спасибо, задержка очень помогла здесь :) - person Mahmoud Mahdi; 02.01.2016