Creating Activity for Visualizing Recorded Sensor Data from List Items

In previous blog Using RealmRecyclerView Adapter to Show Recorded Sensor Experiments[2], I  have created a DataLoggerActivity in PSLab Android app containing RecyclerView showing a list having all the recorded experiments where every list item shows the date, time and the sensor instrument used for recording the data, but there arises below questions:-

  • What if the user wants to see the actual recorded data in form of a graph?
  • How the user can see the location recorded along with the data on the map?
  • How can the user export that data?

There is no way I could show all of that information just on a list item so I created another activity called “SensorGraphViewActivity” the layout of that activity is shown in the figure below:

Figure 1 shows the layout of the Activity as produced in Android editor

The layout contains three views:-

  1. At the top there is graph view which I created using Android MP chart which will show the recorded data plotted on it forming the exact same curve that was created while recording it, this way it is useful to visualize the data and also there is also a play button on the top which simulates the data as it was being plotted on the graph in real time.
  2. In the middle, there is a Card view containing two rows which will simply show the date and time of recording.
  3. At the bottom, there is a Map view which shows the location of the user which would be fetched when the user recorded the data.

This is the gist of the layout file created for the activity.

But now the question arises:-

How to show the data in the activity for the item that the user wanted?

For that, I implemented click listener on every list item by simply adding it inside the onBindViewHolder() method

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
   SensorLogged temp = getItem(position);
   holder.sensor.setText(temp.getSensor());
   Date date = new Date(temp.getDateTimeStart());
   holder.dateTime.setText(String.valueOf(sdf.format(date)));
   holder.cardView.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
                    ...
       });
}

and inside the click listener I performed following three steps:-

  1. First I stored the position of the item clicked inside a variable.

    int positionVar = holder.getAdapterPosition();
  2. Then I used that position from the variable to fetch related data from the Realm database by simply using getItem() method which returns the SensorLogged[1] RealmObject at that position as I used a special type of RecyclerView Adapter called as RealmRecyclerViewAdapter[2].

    int positionVar = holder.getAdapterPosition();
  3. Then I created an Intent to open the SensorGraphViewActivity and passed the related data (i.e., sensortype, foreignkey, latitude, longitude, timezone, date, time) from SensorLogged[1] object to activity in form of extras.
    Intent intent = new Intent(context, SensorGraphViewActivity.class);
    intent.putExtra(SensorGraphViewActivity.TYPE_SENSOR, item.getSensor());
    intent.putExtra(SensorGraphViewActivity.DATA_FOREIGN_KEY, item.getUniqueRef());
    intent.putExtra(SensorGraphViewActivity.DATE_TIME_START,item.getDateTimeStart());
    intent.putExtra(SensorGraphViewActivity.DATE_TIME_END,item.getDateTimeEnd());
    intent.putExtra(SensorGraphViewActivity.TIME_ZONE,item.getTimeZone());
    intent.putExtra(SensorGraphViewActivity.LATITUDE,item.getLatitude());
    intent.putExtra(SensorGraphViewActivity.LONGITUDE,item.getLongitude());
    
    context.startActivity(intent);
    

And, in the SensorGraphViewActivity, I used getIntent() method to fetch all those extra data in the form of Bundle.
For showing the data in the graph I used the foreign key fetched from the intent and queried all the LuxData[1] RealmObject containing that foreignkey in the form of RealmResult<LuxData>[2] ArrayList and used that list to populate the graph.

Long foreignKey = intent.getLongExtra(DATA_FOREIGN_KEY, -1);
Realm realm = Realm.getDefaultInstance();
entries = new ArrayList<>();
RealmResults<LuxData> results = realm.where(LuxData.class).equalTo(DATA_FOREIGN_KEY, foreignKey).findAll();
for (LuxData item : results) {
   entries.add(new Entry((float) item.getTimeElapsed(), item.getLux()));
}

For the map, I fetched the latitude and longitude again from the intent and used the coordinates to show the location on the open street view map.

Thread thread = new Thread(new Runnable() {
   @Override
   public void run() {
       IMapController mapController = map.getController();
       mapController.setZoom((double) 9);
       GeoPoint startPoint = new GeoPoint(latitude, latitude);
       mapController.setCenter(startPoint);
   }
});

For map purposes, of course, I used a separate thread as it is a heavy and time-consuming process and it could lead the app to lag for a long time which could hamper the User Experience.

Thus after the data being plotted on the map and coordinated being plotted on the map, we can see the layout of the activity as shown in Figure 2.

Figure 2 shows the layout of the activity after being populated with data.

I also created the export button in the toolbar that will use the CSVLogger[3] class implemented inside the PSLab android app to export the data in the form of CSV file and save it in the external storage directory.

Resources

  1. Storing Recorded Sensor Data in Realm Database – My blog where I created the Realm Model classes to store recorded data.
  2. Using RealmRecyclerView Adapter to Show Recorded Sensor Experiments – My previous blog where I created the RecyclerView.
  3. Saving Sensor Data in CSV format – Blog by Padmal storing the data in CSV format.

Implementing Progress Bar in PSLab App

This blog is a step by step guide on how to make a functional progress bar in an Android app by taking an example of the progress bar implemented in PSLab Android application. The example is based on my work done in PSLab Android repository under PR #1077 and so it will only demonstrate making a simple progress bar (both linear and circular) and not the one showing progress in percentage too.

How progress bar was implemented in the PSLab app?

Both horizontal and circular progress bar is available in the Material Design Library provided by Google in Android Studio. So, no extra dependencies are needed.

Just drag and drop the progress bar of whichever shape necessary i.e. circular or horizontal, directly on the screen available in the Design tab as shown in Figure 1.

Figure 1. Design tab in Android Studio

There are two ways to use the progress bar available in the Material Design Library.

  • To show a loading screen
  • To show the progress of a process

Loading Screen

Loading Screens are used when the time that will be taken by the process is not known. So, an indeterminate circular progress bar is used to show that some process is going on and so the user needs to wait.

Layout

A circular progress bar is used for this process and so drag and drop the circular progress bar as shown in figure 1. Now set the position, height, and width of the progress bar in the layout as necessary. To set the color of the progress bar, use attribute android:indeterminateTint

Backend

To implement this type of functionality, use the setVisibility function to show the progress bar while some process is taking place in the backend and immediately remove it as soon as the result is ready to be displayed. To make the progress bar visible use progressBar.setVisibility(View.VISIBLE) and to make it invisible use progressBar.setVisibility(View.GONE)

Showing Progress

This is a very common type of process and is used by most of the apps. A horizontal progress bar is used to show the actual progress of the process taking place in the backend on a scale of 0-100 (the scale may vary) where 0 means the process hasn’t started and 100 means the result is now ready to be displayed.

Layout

Horizontal Progress bars are used for this type of usage. So, drag and drop the horizontal progress bar as shown in figure 1. Now set the position, height, and width of the progress bar in the layout as necessary. Different styles of the progress bar can be found in the documentation [1].

Backend

Initially, set the progress of the bar to 0 as no process is taking place by using method setProgress(). Now as soon as the process starts, to increase the progress by a fixed value, use progressBar.incrementProgressBy() method and to set the progress directly, use progressBar.setProgress() method.

So in this way, a progress bar can be implemented in an Android application. Other features like adding custom designs and animations can be done by making the necessary shapes and animations respectively and using the functions available in the documentation [1].

Resources

  1. https://developer.android.com/reference/android/widget/ProgressBar – Documentation of Progress Bar

 

Making custom dialog boxes in PSLab app

This blog covers the topic of how to create custom dialog boxes in an Android app by taking an example of how it was implemented in PSLab Android application. The custom dialog box that will be illustrated in this blog will have a layout similar to that from my PR feat: added a guide for Logic Analyzer Instrument using Alert Dialog Box in PSLab Android repository. But all the main functionalities that can be added to a custom dialog will be illustrated in this blog.

How custom dialog was implemented in the PSLab app?

  • The first step is to make a layout for the custom dialog box. The layout of custom dialog should be such that it shouldn’t occupy the whole screen as the idea here is to overlay the dialog over the activity and not replace the whole activity with the dialog layout. The layout can be implemented by using the following attributes :
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

   <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <TextView
           android:id="@+id/custom_dialog_text"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"/>

       <ImageView
           android:id="@+id/custom_dialog_schematic"
           android:layout_width="wrap_content"
           android:layout_height="@dimen/schematic_height" />

       <TextView
           android:id="@+id/description_text"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" />

       <CheckBox
           android:id="@+id/toggle_show_again"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content" />

       <RelativeLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:layout_below="@id/toggle_show_again">

           <Button
               android:id="@+id/dismiss_button"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content" />

       </RelativeLayout>
   </RelativeLayout>
</ScrollView>

The result of the above layout after adding proper margins and padding is as shown in figure 1.

Figure 1. The output of the layout made for custom dialog

  • Now as the layout is ready, we can focus on adding Java code to inflate the layout over a particular activity. Make a function in the activity or the fragment in which the custom dialog is to be inflated. Add the following code (if the layout is same as that in PSLab app else modify the code as per the layout) in the function to display the custom dialog box.
public void howToConnectDialog(String title, String intro, int iconID, String desc) {
   try {
       final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
       LayoutInflater inflater = getLayoutInflater();
       View dialogView = inflater.inflate(R.layout.custom_dialog_box, null);

 // Find and set all the attributes used in the layout and then create the dialog as shown
       
       builder.setView(dialogView);
       builder.setTitle(title);
       final AlertDialog dialog = builder.create();
       ok_button.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
                dialog.dismiss();
           }
       });
       dialog.show();
   } catch (Exception e) {
       e.printStackTrace();
   }
}

Here, the LayoutInflater inflates the custom dialog layout in the Alertdialog builder. The Alertdialog builder is used to hold and set values for all the views present in the inflated layout. The builder then creates a final custom dialog when builder.create() method is called. Finally, the dialog is shown by calling method dialog.show() and is dismissed by calling method dialog.dismiss().

  • Now the dialog box is ready and it will be inflated when the above method is called. But the “Don’t show again” checkbox currently has no functionality and so the dialog box will pop up although “Don’t show again” checkbox is ticked mark by the user. For adding this functionality, we will need to use Shared Preferences to save the state of the dialog box. For using Shared Preferences, first, add the following lines of code in the above method
final SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
Boolean skipMessage = settings.getBoolean("skipMessage", false);

The PREFS_NAME is the key to distinctly identify the state of the given dialog box from the stored state of many dialog boxes (if any). A suggested key name is “customDialogPreference”. The skipMessage is a boolean used here to check the state if the dialog box should be inflated or not.

Now format the onClickListener of the OK button with the following code :

Boolean checkBoxResult = false;
if (dontShowAgain.isChecked())
   checkBoxResult = true;
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("skipMessage", checkBoxResult);
editor.apply();
dialog.dismiss();

The following code checks the state of “Don’t show again” dialog and then inflates the custom dialog if applicable. If the checkbox is ticked mark then on pressing the OK button the dialog won’t be inflated again.

Resources

  1. https://developer.android.com/guide/topics/ui/dialogs – Android Documentation on Dialogs (Great Resource to make a custom dialog)

Using RealmRecyclerView Adapter to show list of recorded sensor data from Realm Database

In previous blog Storing Recorded Sensor Data in Realm Database we have stored the data fetched from sensors into the Realm Database by defining model classes.

In this blog, we will use the data stored in the Realm to display a list of recorded experiments in the form of well defining card view items so that it is easier for the user to understand.

For showing the list we will make use of RecyclerView  widget provided by Android which is a more advanced version of the List view and is used to display large data sets in a vertical list, horizontal list, grid, staggered grid etc.

RecyclerView  works in accordance with RecyclerView Adapter which is core engine that is responsible of inflating the layout of list items, populating the items with data, recycling of list item views when they go out of viewing screen and much more.

For this blog, we are going to use a special RecyclerView Adapter provided by Realm itself because it integrates properly with the Realm Database and handles modifying, addition, deletion or updating of Realm data automatically and efficiently.   

Step 1 Adding the dependencies

As always first we need to add the following code in our build.gradle file to add the dependency of Realm database and RealmRecyclerViewAdapter.

dependencies {
   implementation"com.android.support:recyclerview-v7:27.1.1 "
   implementation 'io.realm:android-adapters:2.1.1'
}

Step 2 Adding RecyclerView widget in our Activity layout file

First, we need to create an activity and name it as “DataLoggerActivity”, inside the layout of the Activity add the <RecyclerView> widget. This RecyclerView will act as a container of our list item.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".activity.DataLoggerActivity">

    <android.support.v7.widget.RecyclerView
        android:layout_below="@id/top_app_bar_layout"
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</RelativeLayout>

Step 3 Creating the layout and View holder for the list item

We have to create the layout of the list item which will be inflated by the Adapter. So for this create an XML file in res folder and name it “data_list_item.xml”. For the list of the experiments, we want to show Name of the experiment, recording time, recording date for every list item. For this we will make use of <CardView> and <TextView>. This gist shows the code of xml file.

The layout of the list item created is shown in Figure 2

Figure 1 Layout of list item showing mock information

Now we need to create a view holder for this layout which we need to pass to the Adapter, the following code shows the implementation of View Holder for above list item layout.

public class ViewHolder extends RecyclerView.ViewHolder {
   private TextView sensor, dateTime;
   ImageView deleteIcon;
   private CardView cardView;

   public ViewHolder(View itemView) {
       super(itemView);
       dateTime = itemView.findViewById(R.id.date_time);
       sensor = itemView.findViewById(R.id.sensor_name);
       deleteIcon = itemView.findViewById(R.id.delete_item);
       cardView = itemView.findViewById(R.id.data_item_card);
   }
}

Step 4 Creating the adapter for RecyclerView  

In this step, we will start by creating a class called “SensorLoggedListAdpater” and for using use the RecyclerView adapter provided by Realm we need to make this class extend the RealmRecyclerViewAdpater class.

But for that we need to pass two generic parameter:

  1. Model Class : This is class which define a Realm model, for this, we will pass a reference of “SensorLogged.class” which is defined in the previous blog as we want to show the list experiments which are stored using “SensorLogged” model class.
  2. ViewHolder : For this, we will pass the ViewHolder that we have created in Step 3.

As every RecyclerView Adapter needs a arraylist which contains the list of object containing information which we have to populate on the list item, the RealmRecyclerViewAdpater needs data in form of RealmResult to operate on, so we will create a constructor and pass in the RealmResult list in the super() method which we need to provide when we initialize this adapter in our “DataLoggerActivity” class.

public SensorLoggerListAdapter(RealmResults<SensorLogged> list, Activity context) {
   super(list, true, true);
   this.context = context;
   realm = Realm.getDefaultInstance();
}

Now we need to override two methods provided by RealmRecyclerViewAdapter class that are:

  1. public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType): In which we will inflate the layout of list item “dta_list_tem.xml” which we have created in Step 3.
  2. public void onBindViewHolder(@NonNull final ViewHolder holder, int position): In which we will populate the list item view using references stored in the ViewHolder with the data which we have provided while initializing the adapter.
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
   View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.logger_data_item, parent, false);
   return new ViewHolder(itemView);
}

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
   SensorLogged temp = getItem(position);
   holder.sensor.setText(temp.getSensor());
   Date date = new Date(temp.getDateTimeStart());
   holder.dateTime.setText(String.valueOf(sdf.format(date)));
}

Step 5 Initializing the Adapter in Data Logger Activity and connecting with RecyclerView

Now we head to our Data Logger Activity, here in OnCreate() method first we will create a object of RecyclerView, then we will initialize our adapter by passing the RealmResult<SensorLogged> list which we have queried from the Realm Database.

Then we will set the LinearLayoutManager and finally, we will connect the the Adapter with the RecyclerView.

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_data_logger);
   ButterKnife.bind(this);

   Realm realm = Realm.getDefaultInstance();

   RealmResults<SensorLogged> results;
   String title;
  
   results = realm.where(SensorLogged.class)
           .findAll()
           .sort("dateTimeStart", Sort.DESCENDING);

   SensorLoggerListAdapter adapter = new SensorLoggerListAdapter(results, this);
   LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);

   recyclerView.setLayoutManager(linearLayoutManager);

   recyclerView.setAdapter(adapter);
}

After following all the above steps we have finally a activity as shown in Figure 4.

Figure 2 showing a list of recorded experiments with the instrument
name and date time of an experiment

Thus we have successfully displayed a list of the experiments from the data stored in the Realm Database using RealmRecyclerViewAdapter.

Resources

  1. https://academy.realm.io/posts/android-realm-listview/ – Blog on creating a To-do list on Realm official website
  2. https://gist.github.com/Avjeet/2f350feeafff17ec855a39891d8c2d66  Gist of layout of list item used

Storing Recorded Sensor Data in Realm Database

PSLab android app provides various new features like accessing data from the sensors that are either inbuilt into the Android phone or common I2C sensors which are connected to the PSLab device through PIC microcontroller. But the problem is that if the user records the data one time he/she may not be able to view that data in the future as there was no way to save that data somewhere. Saved data can be used for school experiments, preparing reports, research purposes etc.

So, now we have integrated Realm database with the Sensor Data Logger module which is a mobile database that can be used to store real-time data in fast and flawless manner. It is a object oriented database so it stores data in the form of objects which makes it usage with object oriented programming language like Java much easier.

In this blog we will demonstrate the process of storing data from one instrument  i.e., Lux Meter which records illuminance with respect to time to understand the process.

First, we have defined a model class “SensorLogged” which contains information pertaining to all one experiment performed by the user. It will have fields like time of start of recording, the time of the end of the recording, date of recording, sensor name etc.

Whenever a user performs an experiment we will store a object of the SensorLogged model class in realm database containing info for that experiment.

public class SensorLogged extends RealmObject {

   private String sensor;
   private long dateTimeStart;
   @PrimaryKey
   private long uniqueRef;
   private long dateTimeEnd;

   public SensorLogged(String sensor) {
       this.sensor = sensor;
   }

   public void setSensor(String sensor) {
       this.sensor = sensor;
   }
   public void setDateTimeStart(long dateTimeStart) {
       this.dateTimeStart = dateTimeStart;
   }
   public void setUniqueRef(long uniqueRef) {
       this.uniqueRef = uniqueRef;
   }
   public void setDateTimeEnd(long dateTimeEnd) {
       this.dateTimeEnd = dateTimeEnd;
   }
}

 

For storing Lux data we have to define a model class “LuxData” which defines all the fields in one reading of experiment.

public class LuxData extends RealmObject {
   private long foreignKey;
   private float lux;
   private long timeElapsed;
   public LuxData() {
   }
   public LuxData(float lux, long timeElapsed) {
       this.lux = lux;
       this.timeElapsed = timeElapsed;
   }
   public long getForeignKey() {
       return foreignKey;
   }
   public void setForeignKey(long foreignKey) {
       this.foreignKey = foreignKey;
   }
}

We will use the object of this class for every reading of one measurement and provide them with the same Foreign Key which will be Primary key uniqueRef of “SensorLogged” model class.

In this way, we can query all the reading belonging to one measurement from the database containing all the LuxData entries.

For storing the data in Realm database we will follow these steps:

  1. Begin the Realm transaction.

    realm.beginTransaction();
  2. Create a object of “SensorLogged” model class for every measurement with the unique Ref as the primary key and store the information like time of start, date of start, sensor name etc. copy it to the Realm Database.

    SensorLogged sensorLogged = realm.createObject(SensorLogged.class, uniqueRef);
    sensorLogged.setSensor("Lux Meter");
    sensorLogged.setDateTimeStart(startTime);
    realm.copyToRealm(sensorLogged);
  3. For every sensor, reading create a object of LuxData and store the reading in it with the time elapsed and set all the object to same Foreign Key which is same as the Primary key stored in “SensorLogged.class” for this experiment in the previous step and copy it to Realm Database.

    for (int i = 0; i < luxRealmData.size(); i++) {
       LuxData tempObject = luxRealmData.get(i);
       tempObject.setForeignKey(uniqueRef);
       realm.copyToRealm(tempObject);
       }
  4. Commit the transaction

    realm.commitTransaction();

Therefore now the data fetched for each sensor for every experiment is now being saved to the Realm database which we can easily query by using the following code. 

Below code will query all the SensorLogged object in the form of RealmResult<SensorLogged> list which we can use to show to the user the list of all experiments.

results = realm.where(SensorLogged.class)
       .findAll()
       .sort("dateTimeStart", Sort.DESCENDING);

And the code below will query all the LuxData object that contains reading belonging to one experiment whose uniqueRef has been provided as the ForeignKey.

RealmResults<LuxData> results = realm.where(LuxData.class).equalTo("foreignKey",uniqueRef).findAll();

Resources

  1. Realm Database official documentation for Java: https://realm.io/docs/java/latest
  2. AndroidHive blog on Android Working with Realm Database: https://www.androidhive.info/2016/05/android-working-with-realm-database-replacing-sqlite-core-data

 

 

Implement Sensor Data Fetching Using AsyncTask

In PSLab android app we have implemented sensor data fetching of various sensors inbuilt in the mobile device like light sensor, accelerometer, gyrometer. We can use PSLab to log the data and show in the form of the graph or maybe export the data in the form of CSV format for future use.

But recording data from the phone sensor imposes a serious problem in the performance of the Android app as it is a costly to process in terms of memory, resources and time. In CS terms there is too much work that has to be done on the single main thread which sometimes leads to lag and compromises the UX.

So as a solution we applied a concept of the Multithreading provided by Java in which we can shift the heavy process to a separate background thread so that the main thread never gets interrupted during fetching the sensor data and the background thread handles all the fetching and updates the UI as soon as it gets the data, till then the Main thread continues to serves the user so to user the application remains always responsive.

For implementing this we used a special class provided by Android Framework called AsyncTask. Which provides below methods:-

  • doInBackground() : This method contains the code which needs to be executed in the background. In this method, we can send results multiple times to the UI thread by publishProgress() method.

  • onPreExecute() : This method contains the code which is executed before the background processing starts.

  • onPostExecute() : This method is called after doInBackground method completes processing. Result from doInBackground is passed to this method.

  • onProgressUpdate() : This method receives progress updates from doInBackground() method, which is published via publishProgress() method, and this method can use this progress update to update the UI thread.

  • onCancelled(): This method is called when the background task has been canceled. Here we can free up resources or write some cleanup code to avoid memory leaks.

We created a class SensorDataFetch and extended this AsyncTask class and override its methods according to our needs.

private class SensorDataFetch extends AsyncTask<Void, Void, Void> implements SensorEventListener {

   private float data;
   private long timeElapsed;

   @Override
   protected Void doInBackground(Void... params) {
      
       sensorManager.registerListener(this, sensor, updatePeriod);
       return null;
   }

   protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       visualizeData();
   }

   @Override
   protected void onPreExecute() {
 super.onPreExecute();
   //do nothing
   }

   @Override
   protected void onProgressUpdate(Void... values) {
       super.onProgressUpdate(values);
          //do nothing
   }

   @Override
   protected void onCancelled() {
       super.onCancelled();
          //do nothing
   }

In doInBackground() method we implemented the fetching raw data from the sensor by registering the listener and in onPostExecute() method we updated that data on the UI to be viewed by the user.

When this process is being run in the background thread the Main UI thread is free and remains responsive to the user. We can see in Figure 1 below that the UI is responsive to the user swipe action even when the sensor data is updating continuously on the screen.

Figure 1 shows Lux Meter responding to user swipe while fetching sensor data flawlessly.

 

Resources

https://developer.android.com/reference/android/os/AsyncTask – Android Developer documentation for Async Task class.

Snackbar Implementation in PSLab Android App

In PSLab android app we have developed the functionality of logging sensor data in CSV format. We can start and stop the data recording using the save button in the upper right corner of the menu bar and toast message was shown to notify the user for logging status whether it is started or stopped but it leads to some problem like:-

  • The user doesn’t know where the logged file has been created in the external storage.
  • If the user accidentally clicked on the save button the data logging will start the user have to manually go the storage location and delete the recently created unwanted CSV file.

What’s the solution?

The solution to both these problem is solved by implementing Snackbar instead of Toast message.

According to Material Design documentation:-

The Snackbar widget provides brief feedback about an operation through a message at the bottom of the screen. Snackbar disappears automatically, either after a timeout or after a user interaction elsewhere on the screen, and can also be swiped off the screen.

Snackbar can also offer the ability to perform an action, such as undoing an action that was just taken or retrying an action that had failed.

 

Figure 1 shows a Snackbar sample
(Source: – https://material.io/develop/android/components/snackbar/ )

 

To implement the Snackbar in our Android app I started by creating a custom snack bar class which contains all the code to create and show the Snackbar on the screen.

public class CustomSnackBar {

   public static void showSnackBar(@NonNull CoordinatorLayout holderLayout,  
                                   @NonNull String displayText,
                                   String actionText, 
                                   View.OnClickListener clickListener){
       
   Snackbar snackbar =     
              Snackbar.make(holderLayout,displayText,Snackbar.LENGTH_LONG)
              .setAction(actionText, clickListener);

  //do your customization here
}

The custom class contains a static method ‘showSnackBar()’ having parameters:

Parameter Return Type Description
holderLayout CoordinatorLayout Container layout in which the snack bar will be shown at the bottom (should not be null)
displayText String Text to be displayed in the content of Snackbar (should not be null)
actionText String Clickable text which has some action associated with it
clickListener View.OnClickListener On click listener specifying an action to be performed when actionText is clicked

 

Inside the method, I called the static make()  method provided by the Snackbar class and passed holderlayout, displayText and duration of Snackbar in this case Snackbar.LENGTH_LONG as parameters.

Then I called setAction() and passed in the actionText and the clickListener as parameters in it to set the action text. If we pass in null no action text will be generated.

Then, if we want to changes the action text color we can do that by calling setActionTextColor() and passing in the desired color.

snackbar.setActionTextColor(ContextCompat.getColor(holderLayout.getContext(), R.color.colorPrimary));

And if we want to change the content text color then we need to first get the view then we need to get the instance of TextView containing the content text using findViewById() and passing android.support.design.R.id.snackbar_text which is default ID for context TextView, and then call setTextColor() to set the desired color.

View sbView = snackbar.getView();
   TextView textView =     
             sbView.findViewById(android.support.design.R.id.snackbar_text);
       textView.setTextColor(Color.WHITE);
   }

So, now our Snackbar engine is complete now we need to call CustomSnackBar class static method showSnackbar() in our sensor data logger.

For doing this I replaced all the instances of the Toast message with the ‘CustomSnackBar’ by passing in the desired messages that were being passed in Toast message.

But I still need to find the location of our stored CSV file and a method to delete the current generated CSV file.

For that, I did below modification to the CSVLogger class in PSLab android app.

public class CSVLogger {
   private static final String CSV_DIRECTORY = "PSLab";
   public CSVLogger(String category) {
       this.category = category;
       setupPath();
   }
   /*Below methods are included at the bottom of the class */
   public String getCurrentFilePath() {
       return Environment.getExternalStorageDirectory().getAbsolutePath() +
               File.separator + CSV_DIRECTORY + File.separator + category;
   }
   public void deleteFile() {
       csvFile.delete();
   }
}

Now for passing the location of the stored file and implementing delete option, I called the below method when the CSV logging is stopped by the user:

CustomSnackBar.showSnackBar((CoordinatorLayout) parent.findViewById(R.id.cl),

                    “CSV File stored at” + " " +lux_logger.getCurrentFilePath(),
  
                    “DELETE”,

                    new View.OnClickListener() {
                              Override
                              public void onClick(View view) {
                                             lux_logger.deleteFile();    
                              });

By doing this I get a Snackbar as shown in Figure 2, clicking on the “DELETE” text deletes the current CSV file.

Figure 2 shows snackbar showing file stored location and delete option

 

So, implementing Snackbar helped to make the app interactive and keeps user notified and control the data logging.

Resources

  1. https://www.journaldev.com/10324/android-snackbar-example-tutorial – Android SnackBar example implemetation tutorial
  2. https://material.io/develop/android/components/snackbar/ – Android Material Desing implementation of Snackbar.

Creating step designs from KiCAD for PSLab

PSLab hardware device is developed using KiCAD. It is an open source PCB designing tool which we can use for free and it has almost all the features needed to build a professional PCB. But it lacks one thing. It cannot generate and export 3D models. In fact there is a 3D viewer in KiCAD but there is no way to export it. When manufacturing PSLab devices, it was required by the manufacturers so that they can have a clear understanding how the components are placed. This step is necessary for them to design the production line.

Before we get started, there are few prerequisites to help us get this done. They are as follows;

  1. FreeCAD: Open source 3D modeling software
  2. KiCAD step up tools: External library to import KiCAD PCB layouts to FreeCAD

You may need to follow installation instructions to install FreeCAD from the link given. Once we are all set, extract the KiCAD Stepup tools. There we can find a set of python libraries and some bash scripts. We can either use the scripts or type commands ourselves. I found scripts having some issues configuring paths.

To fire up FreeCAD with KiCAD stepup tools enabled, type the following command on your console;

$ freecad kicad-StepUp-tools.FCMacro

Make sure the console is pointing to the directory where the FCMacro file is located. This will open up FreeCAD and if you opened it already and saw the opening screen of FreeCAD, you’d notice a whole new toolbar is added.

Here you can see many tools related to import and export step files and 3D models from outside libraries and folders. Each tool is specific;

  • Load-kicad-footprint:

This tool is useful to generate a step file for an individual PCB component, say a resistor into a step file.

  • Export-to-kicad:

There are instances where when we design a custom foot print, and KiCAD doesn’t have the 3D model. In such a case we can export such a model to KiCAD

  • Load-kicad:

This is the tool we are using to export PSLab PCB board to step format. Before we move on to this tool there is one last configuration we have to do. FreeCAD doesn’t know where we have put KiCAD 3D models. This library simply transforms the available 3D models in KiCAD into step files and build the final output combining all of them together as in the featured image of this blog post. To setup the 3D model path, in KiCAD, there is a path configuration option. Copy the path under “KISYS3DMOD”.

Figure 2: Path Configuration dialog box in KiCAD

And paste it into the ini file named “ksu-config.ini” which you can find in home folder.

Figure 3: Place to add 3D model path in ksu-config.ini file

Once that is done, click on the Load-KiCAD tool icon and browse to the repository where the PSLab hardware files are located at. Open the board file and FreeCAD will generate part by part and finally output the complete design. Now we can export the design in plenty of formats such as steps, stl another similar file format and many more.

Reference:

  1. https://www.freecadweb.org/wiki/Download
  2. http://kicad-pcb.org/external-tools/stepup/

Use Travis to extract testing APKs for PSLab

Travis CI is a popular continuous integration tool built to test software and deployments done at GitHub repositories. They provide free plans to open source projects. PSLab Android is using Travis to ensure that all the pull requests made and the merges are build-bug frees. Travis can do this pretty well, but that is not all it can do. It’s a powerful tool and we can think of it as a virtual machine (with high specs) installed in a server for us to utilize efficiently.

There was a need in PSLab development that, if we want to test the functionalities of code at a branch. To fulfil this need, one has to download the branch to his local machine and use gradle to build a new apk. This is a tedious task, not to mention reviewers, even developers wouldn’t like to do this.

If the apk files were generated and in a branch, we can simply download them using a console command.

$ wget https://raw.github.com/fossasia/<repository>/<branch>/<file with extension>

 

With the help of Travis integration, we can create a simple script to generate these apks for us. This is done as follows;

Once the Travis build is complete for a triggering event such as a pull request, it will run it’s “after_success” block. We are going to execute a script at this point. Simply add the following code snippet.

after_success:
  - 'if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then bash <script-name>.sh; fi'

 

This will run the script you have mentioned using bash. Here we will have the following code snippets in the specified bash script.

First of all we have to define the branch we want to build. This can be done using a variable assignment.

export DEVELOPMENT_BRANCH=${DEVELOPMENT_BRANCH:-development}

 

Once the build is complete, there will be new folders in the virtual machine. One of them is the app folder. Inside this folder contains the build folder where all the apk files are generated. So the next step is to copy these apk files to a place of our preference. I am using a folder named apk to make it much sense.

cd apk
\cp -r ../app/build/outputs/apk/*/**.apk .
\cp -r ../app/build/outputs/apk/debug/output.json debug-output.json
\cp -r ../app/build/outputs/apk/release/output.json release-output.json
\cp -r ../README.md .

 

Usually, the build folder has following apk files

  • app-debug.apk
  • app-release-unsigned.apk
  • app-release.apk

Release apks are usually signed with a key and it would cause issues while installation. So we have to filter out the debug apk that we usually use for debugging and get it as the output apk. This involves simple file handling operations in Linux and a bit of git.

First of all, rename the apk file so that it will be different from other files.

# Rename apks with dev prefixes
mv app-debug.apk app-dev-debug.apk

 

Then add and commit them to a specific branch where we want the output from.

git add -A
git commit -am "Travis build pushed [development]"
git push origin apk --force --quiet> /dev/null

 

Once it is all done, you will have a branch created and updated with the apk files you have defined.

 

Figure 1: UI of pslab-android apk branch

Reference:

  1. https://travis-ci.org/

I2C communication in PSLab Android

PSLab device is a compact electronic device with a variety of features. One of them is the ability to integrate sensors and get readings from them. One might think that why they should use PSLab device to get sensor readings and they can use some other hardware like Arduino. In those devices, user has to create firmware and need to know how to interface them with correct sampling rates and settings. But with PSLab, they all come in the whole package. User just have to plug the device to his phone and the sensor to device and he’s ready.

The idea of this blog post is to show how this sophisticated process is actually happening. Before that, let me give you a basic introduction on how I2C communication protocol works. I2C protocol is superior to UART and SPI protocols as they are device limited or requires many wires. But with I2C, you can literally connect thousands of sensors with just 4 wires. These wires are labeled as follows;

  • VCC – Power line
  • GND – Ground line
  • SDA – Data line
  • SCL – Signal clock

It is required that the SDA and SCL lines need to be connected to VCC line using two pull up resistors. But that’s just hardware. Let’s move on to learn how I2C communicate.

Here there is this communicating concept called master and slave. To start communication, master issues a global signal like a broadcast to all the devices connected to SCL and SDA lines. This signal contains the address of the slave, master needs to address and get data from. If the slave received this call to him, he will pull down the SDA line to signal the master that he heard him and ready to communicate with him. Here communication means reading or writing data. Then the communication happens and the link between master and slave breaks giving opportunity to other masters and slaves.

One might think this is a slow process. But these signals are transmitted at high frequencies. In PSLab it is at 100 kHz and that is one millisecond.

PSLab library has a special class to handle I2C communication. That is

public class I2C {/**/}

 

Once this class is initiated, one has to call the start function to start communication. This method requires the address we wish to communicate with and the mode of operation stating if it is a read or write operation

public int start(int address, int rw) throws IOException {
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_START);
   packetHandler.sendByte((address << 1) | rw & 0xff);
   return (packetHandler.getAcknowledgement() >> 4);
}

 

Once the address is sent out, protocol requires us to stop and wait for acknowledgement.

public void wait() throws IOException {
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_WAIT);
   packetHandler.getAcknowledgement();
}

 

If there are no congestion in the lines such as reading from multiple devices, the acknowledgement will be instantaneous. Once that is complete, we can start communication either byte-wise or bulk-wise

public int send(int data) throws IOException {
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_SEND);
   packetHandler.sendByte(data);
   return (packetHandler.getAcknowledgement() >> 4);
}

 

As an example, reading sensor values at a given interval can be done using the following method call.

public ArrayList<Byte> read(int length) throws IOException {
   ArrayList<Byte> data = new ArrayList<>();
   for (int i = 0; i < length - 1; i++) {
       packetHandler.sendByte(commandsProto.I2C_HEADER);
       packetHandler.sendByte(commandsProto.I2C_READ_MORE);
       data.add(packetHandler.getByte());
       packetHandler.getAcknowledgement();
   }
   packetHandler.sendByte(commandsProto.I2C_HEADER);
   packetHandler.sendByte(commandsProto.I2C_READ_END);
   data.add(packetHandler.getByte());
   packetHandler.getAcknowledgement();
   return data;
}

 

Once we get the data bundle, either we can save them or display in a graph whatever the way it’s convenient.

Reference:

  1. https://en.wikipedia.org/wiki/I%C2%B2C