Raspberry Pi IoT: The Best Project to get started with IoT ! Pt 2

From our STAR Makers: Vaibhav Mathur and Aradhana Kumarraspberry pi iot

In this part we will be building the android application for the Raspberry Pi IoT project from Part 1 of this tutorial: https://diyhacking.com/best-raspberry-pi-iot-project/. You can download the project files for the android application from here: Android App.

If you are a beginner to Android, checking out resources like these will be helpful: tutorial video, building your first app.

STEP 8: Creating the Android application and connecting it to the server

Objective:
1.To receive the temperature readings on your android device.
2.To display the database of the temperature readings sent by the pi.

Structural Flow :

1.To receive the temperature readings on your android device,we need our android app to receive data from the server created above.
2.For this we will use Google Cloud Messaging, HTTP Protocol (Downstream messaging).
Following will be the directional flow of data in GCM Downstream messaging:

Web Server–>Google Cloud Messaging
(The web application will connect to Google cloud messaging API url and send notification message to GCM server to push it to the registered Android device).

Before we write the script to send GCM push notifications, you’ll have to create a Google API key for your application.

  • Save the project number created with the project. You’ll have to use it in configuring the Android application.
  • From the API Manager, select Overview and then enable Google Cloud Messaging (from under Mobile APIs) .16-min
  • Now, select Credentials and click on Create Credentials and then on API key and make a new server key (do this from the same device which hosts your web server). Save the API key generated, it will be used when the server communicates with Google service.17-min
  • Create a new file ‘sending-push-notifications.php’ in your DocumentRoot Directory or download the code file from here: https://diyhacking.com/projects/RaspIoT.zip

What this code does:

When a new application user registers with Google GCM, the application sends the GCM RegID to the server which gets saved in the table regids that we created earlier. As soon as the recorded temperature exceeds the set threshold, the file ‘write-to-database.py’ sends a url request to the server to run this file to send an alert to the user. Your web server then connects to Google server and sends the message which is pushed by the server to registered devices.

That is all there is to the server side scripting for this project.

STEP 9:Building the Android Application for the Raspberry Pi IoT system

The Android application is a GCM client and it will register with GCM server and receive GCM-Registration Id .Then it will,.share the GCM-Reg-Id  with the application server.Finally, it will receive push notification from the GCM server and alert user.

*The application is developed on Android Studio,thus it is recommended that you install android studio.  *Make sure that you are running/testing  the application on a real android device than on the emulator

Please make sure,before starting, that you have the following:-

1.Google Play Services:

It is in the bottom below Extras in the SDK Manager18-min

After installing, Add the following line of code in your build.gradle:

Inside dependencies, add the following line:

compile ‘com.google.android.gms:play-services:6.5.87’

Sync the project after addition.

If everything looks alright, move to the next step.

2.Google Project Id

During development of the web server, we created a project in the Google console for creating the Google API server key. Google will assign a project -ID. We need to use that Google project Id to register this Android application with the GCM server.

Now let’s start creating activities for the app.

1. Create Class Connection.java :19-min

Make sure the MESSAGE_KEY is same here and at the server (in our case, we have used m).
APP_SERVER_URL is the url of your server that you created before(Put your server url in here).
GOOGLE_PROJECT_ID is the project id that you created before.(Put your Google project id here).

2.Create a class Register.java :

package com.example.android.gcmapp;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.google.android.gms.gcm.GoogleCloudMessaging;

import java.io.IOException;
import java.security.AccessControlContext;

/**
* Created by my on 7/12/2016.
*/
public class Register extends Activity {
   Button btnGCMRegister;
   Button btnAppShare;
   GoogleCloudMessaging gcm;
   Context context;
   String regId;

   public static final String REG_ID = "regId";
   private static final String APP_VERSION = "appVersion";

   static final String TAG = "Register Activity";

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_register);

       context = getApplicationContext();

       btnGCMRegister = (Button) findViewById(R.id.btnGCMRegister);
       btnGCMRegister.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View arg0) {
               if (TextUtils.isEmpty(regId)) {
                   regId = registerGCM();
                   Log.d("RegisterActivity", "GCM RegId: " + regId);
               } else {
                   Toast.makeText(getApplicationContext(),
                           "Already Registered with GCM Server!",
                           Toast.LENGTH_LONG).show();
               }
           }
       });

       btnAppShare = (Button) findViewById(R.id.btnAppShare);
       btnAppShare.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View arg0) {
               if (TextUtils.isEmpty(regId)) {
                   Toast.makeText(getApplicationContext(), "RegId is empty!",
                           Toast.LENGTH_LONG).show();
               } else {
                   Intent i = new Intent(getApplicationContext(),
                           MainActivity.class);
                   i.putExtra("regId", regId);
                   Log.d("RegisterActivity",
                           "onClick of Share: Before starting main activity.");
                   startActivity(i);
                   finish();
                   Log.d("RegisterActivity", "onClick of Share: After finish.");
               }
           }
       });
   }

   public String registerGCM() {

       gcm = GoogleCloudMessaging.getInstance(this);
       regId = getRegistrationId(context);

       if (TextUtils.isEmpty(regId)) {

           registerInBackground();

           Log.d("RegisterActivity",
                   "registerGCM - successfully registered with GCM server - regId: "
                           + regId);
       } else {
           Toast.makeText(getApplicationContext(),
                   "RegId already available. RegId: " + regId,
                   Toast.LENGTH_LONG).show();
       }
       return regId;
   }

   private String getRegistrationId(Context context) {
       final SharedPreferences prefs = getSharedPreferences(
               MainActivity.class.getSimpleName(), Context.MODE_PRIVATE);
       String registrationId = prefs.getString(REG_ID, "");
       if (registrationId.isEmpty()) {
           Log.i(TAG, "Registration not found.");
           return "";
       }
       int registeredVersion = prefs.getInt(APP_VERSION, Integer.MIN_VALUE);
       int currentVersion = getAppVersion(context);
       if (registeredVersion != currentVersion) {
           Log.i(TAG, "App version changed.");
           return "";
       }
       return registrationId;
   }

   private static int getAppVersion(Context context) {
       try {
           PackageInfo packageInfo = context.getPackageManager()
                   .getPackageInfo(context.getPackageName(), 0);
           return packageInfo.versionCode;
       } catch (PackageManager.NameNotFoundException e) {
           Log.d("RegisterActivity",
                   "I never expected this! Going down, going down!" + e);
           throw new RuntimeException(e);
       }
   }

   private void registerInBackground() {
       new AsyncTask() {
           @Override
           protected Object doInBackground(Object[] params) {
               String msg = "";
               try {
                   if (gcm == null) {
                       gcm = GoogleCloudMessaging.getInstance(context);
                   }
                   regId = gcm.register(Connection.GOOGLE_PROJECT_ID);
                   Log.d("RegisterActivity", "registerInBackground - regId: "
                           + regId);
                   msg = "Device registered, registration ID=" + regId;

                   storeRegistrationId(context, regId);
               } catch (IOException ex) {
                   msg = "Error :" + ex.getMessage();
                   Log.d("RegisterActivity", "Error: " + msg);
               }
               Log.d("RegisterActivity", "AsyncTask completed: " + msg);
               return msg;

               //return null;
           }

           //@Override
           /*protected String doInBackground(Void... params) {
               String msg = "";
               try {
                   if (gcm == null) {
                       gcm = GoogleCloudMessaging.getInstance(context);
                   }
                   regId = gcm.register(Connection.GOOGLE_PROJECT_ID);
                   Log.d("RegisterActivity", "registerInBackground - regId: "
                           + regId);
                   msg = "Device registered, registration ID=" + regId;

                   storeRegistrationId(context, regId);
               } catch (IOException ex) {
                   msg = "Error :" + ex.getMessage();
                   Log.d("RegisterActivity", "Error: " + msg);
               }
               Log.d("RegisterActivity", "AsyncTask completed: " + msg);
               return msg;
           }*/

           //@Override
           protected void onPostExecute(String msg) {
               Toast.makeText(getApplicationContext(),
                       "Registered with GCM Server." + msg, Toast.LENGTH_LONG)
                       .show();
           }
       }.execute(null, null, null);
   }

   private void storeRegistrationId(Context context, String regId) {
       final SharedPreferences prefs = getSharedPreferences(
               MainActivity.class.getSimpleName(), Context.MODE_PRIVATE);
       int appVersion = getAppVersion(context);
       Log.i(TAG, "Saving regId on app version " + appVersion);
       SharedPreferences.Editor editor = prefs.edit();
       editor.putString(REG_ID, regId);
       editor.putInt(APP_VERSION, appVersion);
       editor.commit();
   }
}

This class Registers your android device with Google-Cloud-Server. Google-Cloud-Server sends a Registration Id to your Android Device. Now we need to Send this Id to the Server.

3.Create class ShareWithServer.java :

package com.example.android.gcmapp;

import android.content.Context;
import android.util.Log;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
* Created by my on 7/12/2016.
*/
public class ShareWithServer {
   public String shareRegIdWithAppServer(final Context context,
                                         final String regId) {

       String result = "";
       Map<String, String> paramsMap = new HashMap<String, String>();
       paramsMap.put("regId", regId);
       try {
           URL serverUrl = null;
           try {
               serverUrl = new URL(Connection.APP_SERVER_URL);
           } catch (MalformedURLException e) {
               Log.e("AppUtil", "URL Connection Error: "
                       + Connection.APP_SERVER_URL, e);
               result = "Invalid URL: " + Connection.APP_SERVER_URL;
           }

           StringBuilder postBody = new StringBuilder();
           Iterator<Map.Entry<String, String>> iterator = paramsMap.entrySet()
                   .iterator();

           while (iterator.hasNext()) {
               Map.Entry<String, String> param = iterator.next();
               postBody.append(param.getKey()).append('=')
                       .append(param.getValue());
               if (iterator.hasNext()) {
                   postBody.append('&');
               }
           }
           String body = postBody.toString();
           byte[] bytes = body.getBytes();
           HttpURLConnection httpCon = null;
           try {
               httpCon = (HttpURLConnection) serverUrl.openConnection();
               httpCon.setDoOutput(true);
               httpCon.setUseCaches(false);
               httpCon.setFixedLengthStreamingMode(bytes.length);
               httpCon.setRequestMethod("POST");
               httpCon.setRequestProperty("Content-Type",
                       "application/x-www-form-urlencoded;charset=UTF-8");
               OutputStream out = httpCon.getOutputStream();
               out.write(bytes);
               out.close();

               int status = httpCon.getResponseCode();
               if (status == 200) {
                   result = "RegId shared with Application Server. RegId: "
                           + regId;
               } else {
                   result = "Post Failure." + " Status: " + status;
               }
           } finally {
               if (httpCon != null) {
                   httpCon.disconnect();
               }
           }

       } catch (IOException e) {
           result = "Post Failure. Error in sharing with App Server.";
           Log.e("AppUtil", "Error in sharing with App Server: " + e);
       }
       return result;
   }
}

The above code is used to send the registration Id to the server. But the app never did so, now let’s finish that task too!

4.Create class MainActivity.java :

package com.example.android.gcmapp;

import android.content.Context;
import android.os.AsyncTask;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class MainActivity extends Activity {

   ShareWithServer appUtil;
   String regId;
   AsyncTask shareRegidTask;

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       appUtil = new ShareWithServer();

       regId = getIntent().getStringExtra("regId");
       //regId = getIntent().getStringExtra("message");
       Log.d("MainActivity", "regId: " + regId);

       //getLastIndexOfs=regId.lastIndexOf("s");
       //msg=regId.substring(getLastIndexOfs);
       //TextView displayTemp = (TextView) findViewById(R.id.lblMessage);
       //  displayTemp.setText("Temperature is " + regId);

       final Context context = this;
       shareRegidTask = new AsyncTask() {
           @Override
           protected Object doInBackground(Object[] params) {
               String result = appUtil.shareRegIdWithAppServer(context, regId);
               return result;

           }


           //@Override
           protected void onPostExecute(String result) {
               shareRegidTask = null;
               Toast.makeText(getApplicationContext(), result,
                       Toast.LENGTH_LONG).show();
           }

       };
       shareRegidTask.execute(null, null, null);

       /*Intent newIntent=new Intent(this,DisplayDatabase.class);
       newIntent.putExtra("key",regId);
       startActivity(newIntent);*/
   }
}

Add next , a class GCMNotificationIntentS for notification generation whenever there is a temperature alert.

5.Create class GCMNotificationIntentS.java :

package com.example.android.gcmapp;

import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.google.android.gms.gcm.GoogleCloudMessaging;

/**
* Created by my on 7/12/2016.
*/
public class GCMNotificationIntentS extends IntentService {

   public static final int NOTIFICATION_ID = 1;
   private NotificationManager mNotificationManager;
   NotificationCompat.Builder builder;

   public GCMNotificationIntentS() {
       super("GcmIntentService");
   }

   public static final String TAG = "GCMNotificationIntentS";

   @Override
   protected void onHandleIntent(Intent intent) {
       Bundle extras = intent.getExtras();
       GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);

       String messageType = gcm.getMessageType(intent);

       if (!extras.isEmpty()) {
           if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR
                   .equals(messageType)) {
               sendNotification("Send error: " + extras.toString());
           } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
                   .equals(messageType)) {
               sendNotification("Deleted messages on server: "
                       + extras.toString());
           } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE
                   .equals(messageType)) {

               for (int i = 0; i < 3; i++) {
                   Log.i(TAG,
                           "Working... " + (i + 1) + "/5 @ "
                                   + SystemClock.elapsedRealtime());
                   try {
                       Thread.sleep(5000);
                   } catch (InterruptedException e) {
                   }

               }
               Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());

               sendNotification("" + extras.get(Connection.MESSAGE_KEY));
               Log.i(TAG, "Received: " + extras.toString());
           }
       }
       GcmBroadcastR.completeWakefulIntent(intent);
   }

   private void sendNotification(String msg) {
       Log.d(TAG, "Preparing to send notification...: " + msg);
       mNotificationManager = (NotificationManager) this
               .getSystemService(Context.NOTIFICATION_SERVICE);

/*        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
               new Intent(this, MainActivity.class), 0);
*/
       Intent pendingIntent = new Intent(this, DisplayText.class);
       pendingIntent.putExtra("message", msg.substring(msg.lastIndexOf(" ")));

       PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
               pendingIntent, PendingIntent.FLAG_UPDATE_CURRENT);


       NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
               this).setSmallIcon(R.drawable.gcm_cloud)
               .setContentTitle("Temperature Notification")
               .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
               .setContentText(msg);

       mBuilder.setContentIntent(contentIntent);
       mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
       Log.d(TAG, "Notification sent successfully.");
   }
}

6.Create GcmBroacastR.java :

package com.example.android.gcmapp;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;

/**
* Created by my on 7/12/2016.
*/
public class GcmBroadcastR extends WakefulBroadcastReceiver {

   @Override
   public void onReceive(Context context, Intent intent) {
       ComponentName comp = new ComponentName(context.getPackageName(),
               GCMNotificationIntentS.class.getName());
       startWakefulService(context, (intent.setComponent(comp)));
       setResultCode(Activity.RESULT_OK);
   }
}

Now we will create an activity where the user will get the temperature reading and a button on that activity will open the URL where we stored the database of the past temperatures.

7.Create class DisplayText.java :

package com.example.android.gcmapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.widget.TextView;

/**
* Created by my on 7/12/2016.
*/
public class DisplayText extends Activity {
   String regId;
   @Override
   public void onCreate(Bundle savedInstanceState) {

       super.onCreate(savedInstanceState);
       setContentView(R.layout.display_database);
       //String data = getIntent().getExtras().getString("key");
       regId = getIntent().getStringExtra("message");
       TextView display=(TextView)findViewById(R.id.showData);
       display.setText(regId);
//        db.insertTemp(regId);
   }

   public void readRecords(View view){

       WebView webview = (WebView) findViewById(R.id.webview);
       webview.loadUrl("http://192.168.0.117/temp-readings.php");
      /*Cursor r= db.getData(1);
       TextView display=(TextView)findViewById(R.id.showData);
       String show= r.getString(r.getColumnIndex(DatabaseHandler.TEMP_READINGS_VALUE));
       display.setText(show);
   */}

}

Now we are almost finished with the creation of the application. You don’t want the app to show the registration prompt every time the app is opened. So, will need to add the following code. This will register the user’s android device the first time they open it. Otherwise, the DisplayText.java will open.

8.Create class StartActivity.java :

package com.example.android.gcmapp;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;

/**
* Created by my on 7/12/2016.
*/public class StartActivity  extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       // TODO Auto-generated method stub
       super.onCreate(savedInstanceState);

       SharedPreferences settings = getSharedPreferences("prefs", 0);
       boolean firstRun = settings.getBoolean("firstRun", false);
       if (firstRun == false)//if running for first time
       //Splash will load for first time
       {
           SharedPreferences.Editor editor = settings.edit();
           editor.putBoolean("firstRun", true);
           editor.commit();
           Intent i = new Intent(StartActivity.this, Register.class);
           startActivity(i);
           finish();
       } else {

           Intent a = new Intent(StartActivity.this, DisplayText.class);
           startActivity(a);
           finish();
       }
   }
}

Now, download and paste the android Manifest file, the three layout files we have used:
1. activity_main.xml
2.activity_register.xml
3.display_database.xml

and other files:
1.styles.xml
2.strings.xml
3.dimens.xml

from here: https://diyhacking.com/projects/xmlFiles.zip. Alternatively, instead of building the project from scratch, download the entire project files from here: Android App.
Edit your styles.xml, strings.xml, dimens.xml, etc accordingly.

Note: You should add images to the drawable folder.

Yaay!! We are finally done with the android app, I hope you were able to develop the app by following the steps. Your App on first open should be like this:20-min

First, register your app with Google GCM and then send GCM-RegId with Server. After this, you will see this:21-min

Now whenever there will be a notification or you open your app again, you will be directed to this:22-min

If there is a notification,it will be displayed above the records. 

Congratulations!

You have completed the tutorial. We hope you were able to develop the project with minimal problems. 

Vaibhav Mathur and Aradhana Kumar

Vaibhav Mathur and Aradhana Kumar

Submitted by Vaibhav Mathur and Aradhana Kumar

Aradhana Kumar: currently studying in IIT, Kharagpur. She has completed her second year in chemical engineering and has hands on experience with the raspberry pi along with other sensors, she has coded in python, php,MySQL, and their likes.

Vaibhav Mathur: currently studying under the University of Delhi, pursuing bachelor in technology in computer science in his fourth year. Has working knowledge with languages like C,C++ as well as Java. He has hands on experience with raspberry pi and other sensors, he also has developed many Android applications with different multi nationals as well as under a professor.

Recommended Posts

Leave a Comment

+ 69 = 74

Start typing and press Enter to search