ESP8266 Android Application to Control Arduino Digital Pins and Toggle LEDs

by Miguel on January 20, 2015

in ESP8266

In this post you’ll find the Arduino and Android app code you need to control the digital pins of your Arduino from an Android phone through the ESP8266. The video below shows the app and explains the code, the code is below the video.

[フレーム]

Arduino Code:

The Arduino code looks for the string +IPD whenever there is data available in the ESP8266, if the string is there then that means that a device (in this case the Android phone) is connecting to the ESP. The software then looks for the string pin=, then reads the pin number, toggles the pin, and sends back the string “Pin x is ON/OFF”.

The code is below and works with every pin number, but don’t use pins 0-3 since those are used for serial communication and the ESP8266 communication. Also, to get your ESP8266 and/or debug simply open the serial comm window in your Arduino IDE.

#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(2,3); // make RX Arduino line is pin 2, make TX Arduino line is pin 3.
 // This means that you need to connect the TX line from the esp to the Arduino's pin 2
 // and the RX line from the esp to the Arduino's pin 3
void setup()
{
 Serial.begin(9600);
 esp8266.begin(9600); // your esp's baud rate might be different
 
 pinMode(11,OUTPUT);
 digitalWrite(11,LOW);
 
 pinMode(12,OUTPUT);
 digitalWrite(12,LOW);
 
 pinMode(13,OUTPUT);
 digitalWrite(13,LOW);
 
 pinMode(10,OUTPUT);
 digitalWrite(10,LOW);
 
 sendCommand("AT+RST\r\n",2000,DEBUG); // reset module
 sendCommand("AT+CWMODE=1\r\n",1000,DEBUG); // configure as access point
 sendCommand("AT+CWJAP=\"mySSID\",\"myPassword\"\r\n",3000,DEBUG);
 delay(10000);
 sendCommand("AT+CIFSR\r\n",1000,DEBUG); // get ip address
 sendCommand("AT+CIPMUX=1\r\n",1000,DEBUG); // configure for multiple connections
 sendCommand("AT+CIPSERVER=1,80\r\n",1000,DEBUG); // turn on server on port 80
 
 Serial.println("Server Ready");
}
void loop()
{
 if(esp8266.available()) // check if the esp is sending a message 
 {
 
 if(esp8266.find("+IPD,"))
 {
 delay(1000); // wait for the serial buffer to fill up (read all the serial data)
 // get the connection id so that we can then disconnect
 int connectionId = esp8266.read()-48; // subtract 48 because the read() function returns 
 // the ASCII decimal value and 0 (the first decimal number) starts at 48
 
 esp8266.find("pin="); // advance cursor to "pin="
 
 int pinNumber = (esp8266.read()-48); // get first number i.e. if the pin 13 then the 1st number is 1
 int secondNumber = (esp8266.read()-48);
 if(secondNumber>=0 && secondNumber<=9)
 {
 pinNumber*=10;
 pinNumber +=secondNumber; // get second number, i.e. if the pin number is 13 then the 2nd number is 3, then add to the first number
 }
 
 digitalWrite(pinNumber, !digitalRead(pinNumber)); // toggle pin 
 
 // build string that is send back to device that is requesting pin toggle
 String content;
 content = "Pin ";
 content += pinNumber;
 content += " is ";
 
 if(digitalRead(pinNumber))
 {
 content += "ON";
 }
 else
 {
 content += "OFF";
 }
 
 sendHTTPResponse(connectionId,content);
 
 // make close command
 String closeCommand = "AT+CIPCLOSE="; 
 closeCommand+=connectionId; // append connection id
 closeCommand+="\r\n";
 
 sendCommand(closeCommand,1000,DEBUG); // close connection
 }
 }
}
/*
* Name: sendData
* Description: Function used to send data to ESP8266.
* Params: command - the data/command to send; timeout - the time to wait for a response; debug - print to Serial window?(true = yes, false = no)
* Returns: The response from the esp8266 (if there is a reponse)
*/
String sendData(String command, const int timeout, boolean debug)
{
 String response = "";
 
 int dataSize = command.length();
 char data[dataSize];
 command.toCharArray(data,dataSize);
 
 esp8266.write(data,dataSize); // send the read character to the esp8266
 if(debug)
 {
 Serial.println("\r\n====== HTTP Response From Arduino ======");
 Serial.write(data,dataSize);
 Serial.println("\r\n========================================");
 }
 
 long int time = millis();
 
 while( (time+timeout) > millis())
 {
 while(esp8266.available())
 {
 
 // The esp has data so display its output to the serial window 
 char c = esp8266.read(); // read the next character.
 response+=c;
 } 
 }
 
 if(debug)
 {
 Serial.print(response);
 }
 
 return response;
}
/*
* Name: sendHTTPResponse
* Description: Function that sends HTTP 200, HTML UTF-8 response
*/
void sendHTTPResponse(int connectionId, String content)
{
 
 // build HTTP response
 String httpResponse;
 String httpHeader;
 // HTTP Header
 httpHeader = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n"; 
 httpHeader += "Content-Length: ";
 httpHeader += content.length();
 httpHeader += "\r\n";
 httpHeader +="Connection: close\r\n\r\n";
 httpResponse = httpHeader + content + " "; // There is a bug in this code: the last character of "content" is not sent, I cheated by adding this extra space
 sendCIPData(connectionId,httpResponse);
}
/*
* Name: sendCIPDATA
* Description: sends a CIPSEND=<connectionId>,<data> command
*
*/
void sendCIPData(int connectionId, String data)
{
 String cipSend = "AT+CIPSEND=";
 cipSend += connectionId;
 cipSend += ",";
 cipSend +=data.length();
 cipSend +="\r\n";
 sendCommand(cipSend,1000,DEBUG);
 sendData(data,1000,DEBUG);
}
/*
* Name: sendCommand
* Description: Function used to send data to ESP8266.
* Params: command - the data/command to send; timeout - the time to wait for a response; debug - print to Serial window?(true = yes, false = no)
* Returns: The response from the esp8266 (if there is a reponse)
*/
String sendCommand(String command, const int timeout, boolean debug)
{
 String response = "";
 
 esp8266.print(command); // send the read character to the esp8266
 
 long int time = millis();
 
 while( (time+timeout) > millis())
 {
 while(esp8266.available())
 {
 
 // The esp has data so display its output to the serial window 
 char c = esp8266.read(); // read the next character.
 response+=c;
 } 
 }
 
 if(debug)
 {
 Serial.print(response);
 }
 
 return response;
}

Android Code:

The Android code below was compiled with Android studio and should work with Android devices that have Android 2.2 or above.

Activity File

The activity file processes the user’s interaction with the UI. When a button a clicked the IP address and port number are saved so you don’t have to type them again, then the HTTP request is sent to the IP address specified with the parameter pin and the value of the pin.

package com.allaboutee.httphelper;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
public class HomeActivity extends Activity implements View.OnClickListener {
 public final static String PREF_IP = "PREF_IP_ADDRESS";
 public final static String PREF_PORT = "PREF_PORT_NUMBER";
 // declare buttons and text inputs
 private Button buttonPin11,buttonPin12,buttonPin13;
 private EditText editTextIPAddress, editTextPortNumber;
 // shared preferences objects used to save the IP address and port so that the user doesn't have to
 // type them next time he/she opens the app.
 SharedPreferences.Editor editor;
 SharedPreferences sharedPreferences;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_home);
 sharedPreferences = getSharedPreferences("HTTP_HELPER_PREFS",Context.MODE_PRIVATE);
 editor = sharedPreferences.edit();
 // assign buttons
 buttonPin11 = (Button)findViewById(R.id.buttonPin11);
 buttonPin12 = (Button)findViewById(R.id.buttonPin12);
 buttonPin13 = (Button)findViewById(R.id.buttonPin13);
 // assign text inputs
 editTextIPAddress = (EditText)findViewById(R.id.editTextIPAddress);
 editTextPortNumber = (EditText)findViewById(R.id.editTextPortNumber);
 // set button listener (this class)
 buttonPin11.setOnClickListener(this);
 buttonPin12.setOnClickListener(this);
 buttonPin13.setOnClickListener(this);
 // get the IP address and port number from the last time the user used the app,
 // put an empty string "" is this is the first time.
 editTextIPAddress.setText(sharedPreferences.getString(PREF_IP,""));
 editTextPortNumber.setText(sharedPreferences.getString(PREF_PORT,""));
 }
 @Override
 public void onClick(View view) {
 // get the pin number
 String parameterValue = "";
 // get the ip address
 String ipAddress = editTextIPAddress.getText().toString().trim();
 // get the port number
 String portNumber = editTextPortNumber.getText().toString().trim();
 // save the IP address and port for the next time the app is used
 editor.putString(PREF_IP,ipAddress); // set the ip address value to save
 editor.putString(PREF_PORT,portNumber); // set the port number to save
 editor.commit(); // save the IP and PORT
 // get the pin number from the button that was clicked
 if(view.getId()==buttonPin11.getId())
 {
 parameterValue = "11";
 }
 else if(view.getId()==buttonPin12.getId())
 {
 parameterValue = "12";
 }
 else
 {
 parameterValue = "13";
 }
 // execute HTTP request
 if(ipAddress.length()>0 && portNumber.length()>0) {
 new HttpRequestAsyncTask(
 view.getContext(), parameterValue, ipAddress, portNumber, "pin"
 ).execute();
 }
 }
 /**
 * Description: Send an HTTP Get request to a specified ip address and port.
 * Also send a parameter "parameterName" with the value of "parameterValue".
 * @param parameterValue the pin number to toggle
 * @param ipAddress the ip address to send the request to
 * @param portNumber the port number of the ip address
 * @param parameterName
 * @return The ip address' reply text, or an ERROR message is it fails to receive one
 */
 public String sendRequest(String parameterValue, String ipAddress, String portNumber, String parameterName) {
 String serverResponse = "ERROR";
 try {
 HttpClient httpclient = new DefaultHttpClient(); // create an HTTP client
 // define the URL e.g. http://myIpaddress:myport/?pin=13 (to toggle pin 13 for example)
 URI website = new URI("http://"+ipAddress+":"+portNumber+"/?"+parameterName+"="+parameterValue);
 HttpGet getRequest = new HttpGet(); // create an HTTP GET object
 getRequest.setURI(website); // set the URL of the GET request
 HttpResponse response = httpclient.execute(getRequest); // execute the request
 // get the ip address server's reply
 InputStream content = null;
 content = response.getEntity().getContent();
 BufferedReader in = new BufferedReader(new InputStreamReader(
 content
 ));
 serverResponse = in.readLine();
 // Close the connection
 content.close();
 } catch (ClientProtocolException e) {
 // HTTP error
 serverResponse = e.getMessage();
 e.printStackTrace();
 } catch (IOException e) {
 // IO error
 serverResponse = e.getMessage();
 e.printStackTrace();
 } catch (URISyntaxException e) {
 // URL syntax error
 serverResponse = e.getMessage();
 e.printStackTrace();
 }
 // return the server's reply/response text
 return serverResponse;
 }
 /**
 * An AsyncTask is needed to execute HTTP requests in the background so that they do not
 * block the user interface.
 */
 private class HttpRequestAsyncTask extends AsyncTask<Void, Void, Void> {
 // declare variables needed
 private String requestReply,ipAddress, portNumber;
 private Context context;
 private AlertDialog alertDialog;
 private String parameter;
 private String parameterValue;
 /**
 * Description: The asyncTask class constructor. Assigns the values used in its other methods.
 * @param context the application context, needed to create the dialog
 * @param parameterValue the pin number to toggle
 * @param ipAddress the ip address to send the request to
 * @param portNumber the port number of the ip address
 */
 public HttpRequestAsyncTask(Context context, String parameterValue, String ipAddress, String portNumber, String parameter)
 {
 this.context = context;
 alertDialog = new AlertDialog.Builder(this.context)
 .setTitle("HTTP Response From IP Address:")
 .setCancelable(true)
 .create();
 this.ipAddress = ipAddress;
 this.parameterValue = parameterValue;
 this.portNumber = portNumber;
 this.parameter = parameter;
 }
 /**
 * Name: doInBackground
 * Description: Sends the request to the ip address
 * @param voids
 * @return
 */
 @Override
 protected Void doInBackground(Void... voids) {
 alertDialog.setMessage("Data sent, waiting for reply from server...");
 if(!alertDialog.isShowing())
 {
 alertDialog.show();
 }
 requestReply = sendRequest(parameterValue,ipAddress,portNumber, parameter);
 return null;
 }
 /**
 * Name: onPostExecute
 * Description: This function is executed after the HTTP request returns from the ip address.
 * The function sets the dialog's message with the reply text from the server and display the dialog
 * if it's not displayed already (in case it was closed by accident);
 * @param aVoid void parameter
 */
 @Override
 protected void onPostExecute(Void aVoid) {
 alertDialog.setMessage(requestReply);
 if(!alertDialog.isShowing())
 {
 alertDialog.show(); // show dialog
 }
 }
 /**
 * Name: onPreExecute
 * Description: This function is executed before the HTTP request is sent to ip address.
 * The function will set the dialog's message and display the dialog.
 */
 @Override
 protected void onPreExecute() {
 alertDialog.setMessage("Sending data to server, please wait...");
 if(!alertDialog.isShowing())
 {
 alertDialog.show();
 }
 }
 }
}

Layout File

This file simply creates the UI (buttons and textboxes with hints).

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <LinearLayout
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:orientation="vertical">
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginTop="20dp"
 android:text="IP Address:"
 android:id="@+id/textView" />
 <EditText
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:hint="e.g. 192.168.0.10"
 android:id="@+id/editTextIPAddress" />
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Port Number:"
 android:id="@+id/textView2" />
 <EditText
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:inputType="number"
 android:ems="10"
 android:hint="e.g. 80"
 android:id="@+id/editTextPortNumber" />
 <Button
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:text="Pin 11"
 android:id="@+id/buttonPin11" />
 <Button
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:text="Pin 12"
 android:id="@+id/buttonPin12" />
 <Button
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:text="Pin 13"
 android:id="@+id/buttonPin13" />
 </LinearLayout>
</ScrollView>

Manifest File

The only change I had to make to the manifest file was to add the Internet permission to allow for HTTP requests.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.allaboutee.httphelper" >
 <uses-permission android:name="android.permission.INTERNET" />
 <application
 android:allowBackup="true"
 android:icon="@drawable/ic_launcher"
 android:label="@string/app_name"
 android:theme="@style/AppTheme" >
 <activity
 android:name=".HomeActivity"
 android:label="@string/app_name" >
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>
 </application>
</manifest>

AltStyle によって変換されたページ (->オリジナル) /