lunes, 6 de febrero de 2017

Arduino + Ethernet Shield + Android = Home Assistant barato



Una casa automatizada y un asistente: Sueño de muchos y privilegio de unos pocos que pueden costearlo ¿Cierto? Bueno, en este articulo voy a refutar ese paradigma para demostrarte que con un poco de ingenio y conocimiento puedes tener una casa inteligente sin invertir más de 100$.

¿Qué necesitamos para el prototipo? 
  • Arduino UNO
  • Ethernet shield
  • Un protoboard
  • Cable para el proto
  • 2 LED y resistencias de 220Ohm
  • Arduino IDE
  • Android Studio (Para desarrollar la aplicación)




Explicación del proyecto: 

En síntesis el proyecto tiene como objetivo el desarrollo de un asistente que pueda ejecutar acciones dentro del hogar (como encender o apagar luces) interpretanto instrucciones por voz del usuario mediante una aplicación para dispositivos inteligentes.

En términos generales, se deben hacer dos cosas: Programar el arduino dentro de la red para que interprete y ejecute ordenes especificas, y realizar una aplicación capaz de interpretar comandos de voz (como "enciende la tv") y enviar las ordenes al arduino (todo bajo TCP/IP).

La aplicación interpreta y envía las ordenes, el arduino las recibe y las ejecuta, un esquema bastante fácil de entender ¿cierto? Manos a la obra.


Programando el Arduino: 

El código para el arduino como mencioné, debe tener asignada una dirección IP dentro del segmento de red del dispositivo Android que tendrá la aplicación que envía los comandos, esto es muy importante, de lo contrario no habrá comunicación. 

En el sketch estoy trabajando los pin del 1 al 5, para trabajar con 5 dispositivos diferentes. 



#include <SPI.h>
#include <Ethernet.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

//la ip de tu arduino
IPAddress ip(192, 168, 1, 177);

// puerto web
EthernetServer server(80);

void setup() {
  pinMode(2,OUTPUT);        
  pinMode(3,OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5,OUTPUT);
  Serial.begin(9600);

  // Inicia el servidor web
  Ethernet.begin(mac, ip);
  server.begin();

}

void loop() {
  // Verifica si hay un cliente conectado
  EthernetClient client = server.available();
  
  if (client) { // si hay un cliente
    boolean currentLineIsBlank = true;
    String buffer = ""; // un buffer para el GET request
    
    while (client.connected()) {

      if (client.available()) {
        char c = client.read();// Lee los datos del cliente
        buffer += c; // Almacena los datos en el buffer
        
        if (c == '\n' && currentLineIsBlank) 
          // Inicia el request http
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println(); // Blank line ==> end response
          break;
        }
        if (c == '\n') { // if New line
          currentLineIsBlank = true;
          buffer = "";  // Clear buffer
        } else if (c == '\r') { // If cariage return...
          //Read in the buffer if there was send "GET /?message=..."
          if(buffer.indexOf("GET /?message=")>=0) {
            // Read the message you said and see if it is a command
            if(buffer.indexOf("rory prende el aire")>=0) {
              digitalWrite(2, HIGH);
            }
            if(buffer.indexOf("rory prende la luz")>=0) {
              digitalWrite(3, HIGH);
            }   
            if(buffer.indexOf("rory apaga el aire")>=0) {
              digitalWrite(2, LOW);
            }
            if(buffer.indexOf("rory apaga la luz")>=0) {
              digitalWrite(3, LOW);
            }
          }
        } else {
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
  }
}

NOTA: Se debe realizar un include a <SPI.h> <Ethernet.h>


Programando la APK

Para este punto necesitas desarrollar la aplicación que funcione como puente de comunicación entre tu teléfono y el Arduino. Es importante que si nunca has trabajado con Android estudio sepas, que lo fundamental que trabajaremos acá son 3 archivos: El Layout, el método principal y el XML de la permisología de la aplicación. 

Como interfaz únicamente utilicé un botón grande que activa la función para que el teléfono escuche tus instrucciones. 







Acá dejo el código del LAYOUT

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
    android:orientation="horizontal">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical">

        <ImageButton
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:id="@+id/btn_speak"
            android:layout_gravity="center_horizontal"
            android:src="@drawable/abc_ic_voice_search_api_mtrl_alpha" ></ImageButton>

    </LinearLayout>

</LinearLayout>

Programando la clase mainactivity 

En esta fase se necesitan varias cosas, inicialmente se requiere el SpeechRecognizer y un tag para el debug: 


private SpeechRecognizer sr;
private static final String TAG = "MainActivity";
Ahora, en la función Oncreate necesitamos programarle la funcionalidad al boton que creamos en el Layout del paso anterior: 


ImageButton speakButton = (ImageButton) findViewById(R.id.btn_speak);
speakButton.setOnClickListener(this);
sr = SpeechRecognizer.createSpeechRecognizer(this);
sr.setRecognitionListener(new listener());

Ahora debemos crear la clase para el reconocimiento de voz: 


class listener implements RecognitionListener
{
    public void onReadyForSpeech(Bundle params)
    {}
    public void onBeginningOfSpeech()
    {}
    public void onRmsChanged(float rmsdB)
    {}
    public void onBufferReceived(byte[] buffer)
    {}
    public void onEndOfSpeech()
    {}
    public void onError(int error)
    {
        if(error != 7) {
            Log.d(TAG,  "error " +  error);
        }
    }
    public void onResults(Bundle results)
    {
        String str = ""; // Create new empty string
        // Get the results from the speech recognizer
        ArrayList data = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
 // If there is data
        if(data.size() != 0) {
            // Add all the data to a string
            for (int i = 0; i < data.size(); i++) {
                Log.d(TAG, "result " + data.get(i));
                str += data.get(i);
                str += " ";
            }
            // Create a lowercase string
            str = str.toLowerCase();
            // Send the GET request with message
            String message = "message=" + str;
            new Background_get().execute(message);
        }
    }
    public void onPartialResults(Bundle partialResults)
    {
        Log.d(TAG, "onPartialResults");
    }
    public void onEvent(int eventType, Bundle params)
    {
        Log.d(TAG, "onEvent " + eventType);
    }
}

Ahora debemos crear una funcion Onclick que active el Speech Listener del boton: 


public void onClick(View v) {
    if (v.getId() == R.id.btn_speak)
    {
        // Activate the speech listener
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,"voice.recognition.test");

        intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,5);
        sr.startListening(intent);
    }
}

Y finalmente lo más importante, la clase que envía las ordenes al Arduino:


private class Background_get extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... params) {
        try {
            /* Change the IP to the IP you set in the arduino sketch */
            URL url = new URL("http://192.168.1.177/?" + params[0]);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder result = new StringBuilder();
            String inputLine;
            while ((inputLine = in.readLine()) != null)
                result.append(inputLine).append("\n");

            in.close();
            connection.disconnect();
            return result.toString();

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }} 

Ahora solo debemos agregar los permisos necesarios en el AndroidManifest.xml:


<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET" />


Conclusión

Las posibilidades son infinitas. Podemos programar cada pin del arduino para ejecutar una tarea diferente dentro de nuestro hogar, cosas tales como:

  • Encendido/Apagado de luces
  • Encendido/Apagado de luces por sector
  • Encendido/Apagado de Electrodomésticos
  • Encendido/Apagado de Aires acondicionado
  • Bajada/Subida de persianas
  • Apertura de cerraduras
Son solo unas cuantas posibilidades para tu proyecto de automatización casero. Y si, obviamente estamos trabajando con voltajes bajos, pero para eso existe un modulo de reles, y conectado en el siguiente esquema podrá ejecutar todas las funcionalidades que menciono: 


Finalmente les dejo un vídeo con la demostración del funcionamiento, espero que sea de utilidad la información que suministro y cualquier duda pueden dejar un comentario. Por cierto, el asistente, como pueden notar tanto en el video como en el código del Skecht, se llama "Rory" En honor a Rory Mercury, personaje del manga/anime GATE. 



Kikeex

Programador web, soporte IT y especialista en seguridad informática. Amante de la tecnología, enemigo de las pseudo ciencias y el conformismo. Irreverente, directo y bastante honesto con mis opiniones.

2 comentarios:

  1. Hola soy Oliver y me gustó mucho tu proyecto, pero no estoy muy capacitado en lo del Android Studio, me podrias poner una foto del MainActivity ya con el codigo agregado como lo hiciste con el Layout?
    Gracias.

    ResponderEliminar
  2. Saludos Oliver,
    Te dejaré el link para que descargues directamente
    la MainActivity. Igualmente, si te da algún problema
    avísame y te comparto el proyecto.

    https://mega.nz/#!R441CZ6b!xJq4-iuC_dNR0n2rdqFKHUEEeEQuRdxY7QfpOeT_D94

    ResponderEliminar

 

Copyright @ 2017 Pwned!!.