Measuring capacitor in PSLab and its Bugs

In this blog I will discuss about how we have measured capacitance in Pocket Science Lab and the  issues in capacitance measurement which was finally solved.

Measuring capacitance in PSLab device

To measure capacitance we need to go to the multimeter instrument from the instrument section of the PSLab

Figure 1.  Showing Multimeter Tile

Capacitance in PSLab is measured by keeping the capacitor or the element of which capacitance is to be measured between the CAP and ground pin.

 Figure 2.  Showing CAP pins in PSLab

For measuring capacitance in PSLab we use a specific method in which we supply a constant current to the CAP pin and thus we charge the capacitor to the maximum level, the math involved in it is as follow:-

We know that

Q{charge stored} = C*V

Also

Q= I * time

Where

I=current (constant)

Thus the capacitance

C = Q / V

Therefore

C = I*time / V (measured). – (1)

Therefore we know the current supplied, we know the voltage measured and we have also set the time to charge the capacitor and thus we get the capacitance from equation (1).

Code implementation for measuring capacitance

This is the primary code for getting the data for measuring capacitance in which we pass the current range and the current time through which the data gets fetched from the device which is then further processed in another function in which we finally get the capacitance.

public double[] getCapacitance(int currentRange, int chargeTime) { // time in uSec
        try {
            mPacketHandler.sendByte(mCommandsProto.COMMON);
            mPacketHandler.sendByte(mCommandsProto.GET_CAPACITANCE);
            mPacketHandler.sendByte(currentRange);
            mPacketHandler.sendInt(chargeTime);
            Thread.sleep((long)(chargeTime * 1e-6 + .02));
            int VCode;
            do VCode = mPacketHandler.getVoltageSummation();
            while (VCode == -1);
            double v = 3.3 * VCode / 4095;
            mPacketHandler.getAcknowledgement();
            double chargeCurrent = this.currents[currentRange];
            double c = 0;
            if (v != 0)
                c = (chargeCurrent * chargeTime * 1e-6 / v - this.SOCKET_CAPACITANCE) / this.currentScalars[currentRange];
            return new double[] {
                v,
                c
            };
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return null;

In the above function we can clearly see how we send the bytes in the device by the sendByte function through which various functions are sending current, setting voltage, setting current range etc are done in the device and then we can see how the voltage measured is taken using the getVoltageSummition method (of packet Handler class) , how we get the current and finally using them in equation (1) we get the capacitance of the element.

The following implementation is taken from the PSLab desktop app where the same method is used to measure capacitance.

Bugs in measuring capacitance

The capacitance measurement although was working in the desktop app but had bugs in the android app. It could never read the correct value also everytime gave a null value for capacitance.

Figure 3.  Showing null value for capacitance  PSLab

Solving the Bug [2]

After a deep research in the inside  the code of the capacitance measurement it was found that the error was caused while fetching the incorrect data from the device and processing it. The device gives a negative one value when there is any error in capacitance measurement  and that was being processed, thus the error was solved by introducing a do while loop as shown

 do VCode = mPacketHandler.getVoltageSummation();
 while (VCode == -1);

And thus now only the correct data fetched is processed further and thus the bug was solved after which the capacitance measurement was correct.

Resources

Continue Reading

Channel Communications Error of PSLab

The Pocket Science Lab multimeter has got three channels namely CH1,CH2 and CH3 with different ranges for measuring the voltages, but there was a channel communication error occuring at CH1 and CH2 pins of PSLab. This blogs will give a brief description of the channel communication error and how was it solved by me.

In the previous blog I have discussed about the channel communication of PSLab and how it works. In this blog i will be discussing about the channel communication error which was occuring while using CH1 and CH2 pins of PSLab android.

Communication between PSLab device and Android App

As discussed in the previous blog the communication between the PSLab and the android occurs with the help of the USBManger class of android.

One of the major function which makes it possible is the bulk transfer function of the android

amtWritten = mConnection.bulkTransfer(mWriteEndpoint, src, written, writeLength, timeoutMillis);

As shown in the above code, there is a timeout that some time required for this function to be executed, and otherwise this function will return a negative value which will mean that the communication is not successful.

Voltage Measuring Functions

The main function which gets called while pressing the button for measuring voltage is the getVoltage function which simultaneously calls the volmeterAutoRange function as well as the getAverage voltage function. The voltageAutoRnge function also calls the getAverage function inside of it.

public double getVoltage(String channelName, Integer sample) {
    this.voltmeterAutoRange(channelName);
    double Voltage = this.getAverageVoltage(channelName, sample);
    if (channelName.equals("CH3")) {
        return Voltage;
    } else {
        return 2 * Voltage;
    }
}

Calling both these functions simultaneously results in calling of the bulktranfer method

VoltmeterAutoRange function:-

private double voltmeterAutoRange(String channelName) {
    if (this.analogInputSources.get(channelName).gainPGA == 0)
        return 0;
    this.setGain(channelName, 0, true);
    double V = this.getAverageVoltage(channelName, null);
    return this.autoSelectRange(channelName, V);
}

The getAverage voltage function calls the getRawableVoltage function which thus calls the USBManger class functions of read and write, thus calling the bulkTranfer function.

Thus as the bulk transfer function is called simultaneously it caused problem in communication.

Solving the issue

The communication related  issues were finally solved when these bugs were spotted, the solution to this issue is that the voltageAutoRange function’s return value was never used in the codes and was thus not required.[2]The voltageAutoRange function was calling the getAverageVoltage function just to get a return value. Thus I formatted the function and now it looks like this-

private void voltmeterAutoRange(String channelName) {
    if (this.analogInputSources.get(channelName).gainPGA != 0) {
        this.setGain(channelName, 0, true);
    }
}

And thus finally the issue was solved and all things were working fine in channel communication.

Resources

Continue Reading

Working with Shared Preferences in PSLab App

This blog demonstrates how to work with Shared Preferences in an Android app. The blog includes a detailed explanation about what are the methods available to store data in Android and where to use Shared Preferences (a type of data storage method) to save extra memory usage and work efficiently. After the detailed explanation is a step by step guide on how to use Shared Preferences in any Android app by taking an example of one used in PSLab Android app under PR #1236

What are methods available in Android for data storage ?

Android provides a variety of methods to store data some of which are

  1. Shared Preferences
  2. Internal Storage
  3. External Storage
  4. SQLite Database

A very brief description of the above four data storage method would be

  • Shared Preference – Used to store key-data pair for a given app
  • Internal Storage – Used to store any type of data such as pictures, videos, etc. which can be used only within the app
  • External Storage – Used to store any type of data such as audio, video, etc. which can be shared between different apps or different systems
  • SQLite database – It is also a type of Internal Storage method but with a different programming language in use which is SQL

Where to use different data storage methods?

Following are some of the  distinct cases where the above-mentioned data storing methods can be differentiated

  • Shared Preference – Shared Preference should be used when a very small amount of data i.e. key-value pair data is to be stored. An example of it would be storing the state of a widget when an app is closed and restoring the state when the app is opened again.
  • Internal Storage – Internal storage should be used while storing data such as text files, audio, video, photographs, etc. but occupying a very less device memory space. So, internal storage should be used when a limited amount of data needs to be stored for app execution.
  • External Storage – External storage should be used when data to be stored is very large and as a result, Internal storage can’t be used. External Storage can also write data on external memories like SD Card, etc.

How to use Shared Preferences in an Android app?

Following is a step by step guide on how Shared Preferences were used in PSLab Android app

  • First, declare a variable using final and static keyword so as to make its value permanent because it will be used to differentiate current activity/fragment data from other activity/fragment data in a common folder of Shared Preference.
private static final String FRAG_CONFIG = "LuxMeterConfig";
  • Now, we can make Shared Preferences for current activity/fragment by using the code:

When in Activity:

final SharedPreferences settings = getSharedPreferences(FRAG_CONFIG, Context.MODE_PRIVATE);

When in fragment:

final SharedPreferences settings = getActivity().getSharedPreferences(FRAG_CONFIG, Context.MODE_PRIVATE);

Here Context.MODE_PRIVATE is a context through which we define our Shared Preference i.e. for the current context it means that the above made Shared Preference can only be used inside the current activity/fragment. A detailed description of other modes available can be found in [1].

  • Now, Shared Preference for current activity/fragment is ready for use and so now, we can add as many numbers of the key-value pair as we want by using the following code
settings.getInt("HighValue", 2000);

Here, “HighValue” is the key whereas 2000 is the value. The above method is used to give a default value when a pair is created.

  • Now to edit the value of any before-made pair, we can use the Editor method available in the Shared Preference class to edit the default value.
SharedPreferences.Editor editor = settings.edit();
editor.putInt("HighValue", 5000);
editor.apply();

Here, the editor is an instance of edit() method available in Shared Preference class. After changing the default value of the key, we can use apply() method to apply the changes to the default key-value pair.

Where to find the Shared Preference folder on the target device?

To find the Shared Preference folder for any Android application, do the following steps:

  • Connect the target device (device on which app is installed) to the system running Android Studio.
  • Now click on the “Device File Explorer” button in Android Studio as shown in figure 1.

Figure 1. Device File Explorer button in Android Studio

  • Now after clicking the button, a list of folders would pop up as shown in figure 2.

Figure 2. Screenshot of Android Studio showing list of folders on the device

  • Now follow the given path, and you can see the desired folder
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml

So, in this way, the Shared Preferences can be used for data storage in any Android application.

Resources

  1. https://www.androidauthority.com/how-to-store-data-locally-in-android-app-717190/ – Documentation on different modes available to define the context of Shared Preference
  2. https://stackoverflow.com/questions/6146106/where-are-shared-preferences-stored – StackOverflow Q/A for where the Shared Preferences are stored on the target device
Continue Reading

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

 

Continue Reading

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)
Continue Reading

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
Continue Reading

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

 

 

Continue Reading

Voltage Measurement through Channels in PSLab

The Pocket Science Lab multimeter has got three channels namely CH1,CH2 and CH3 with different ranges for measuring the voltages.This blog will give a brief description on how we measure voltages in channels.

Measuring Voltages at channels can be divided into three parts:-

        1. Communication between between device and Android.
        2. Setting up analog channel (analog constants)
        3. Voltage measuring function of android.

Communication between PSLab device and Android App

The communication between the PSLab device and  Android occurs through the help of UsbManger package of CommunicationHandler class of the app. The main two functions involved in the communication are read and write functions in which we send particular number of bytes and then we receive certain bytes.

The read function :-

public int read(byte[] dest, int bytesToBeRead, int timeoutMillis) throws IOException {
    int numBytesRead = 0;
    //synchronized (mReadBufferLock) {
    int readNow;
    Log.v(TAG, "TO read : " + bytesToBeRead);
    int bytesToBeReadTemp = bytesToBeRead;
    while (numBytesRead < bytesToBeRead) {
        readNow = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, bytesToBeReadTemp, timeoutMillis);
        if (readNow < 0) {
            Log.e(TAG, "Read Error: " + bytesToBeReadTemp);
            return numBytesRead;
        } else {
            //Log.v(TAG, "Read something" + mReadBuffer);
            System.arraycopy(mReadBuffer, 0, dest, numBytesRead, readNow);
            numBytesRead += readNow;
            bytesToBeReadTemp -= readNow;
            //Log.v(TAG, "READ : " + numBytesRead);
            //Log.v(TAG, "REMAINING: " + bytesToBeRead);
        }
    }
    //}
    Log.v("Bytes Read", "" + numBytesRead);
    return numBytesRead;
}

Similarly the write function is –

public int write(byte[] src, int timeoutMillis) throws IOException {
    if (Build.VERSION.SDK_INT < 18) {
        return writeSupportAPI(src, timeoutMillis);
    }
    int written = 0;
    while (written < src.length) {
        int writeLength, amtWritten;
        //synchronized (mWriteBufferLock) {
        writeLength = Math.min(mWriteBuffer.length, src.length - written);
        // bulk transfer supports offset from API 18
        amtWritten = mConnection.bulkTransfer(mWriteEndpoint, src, written, writeLength, timeoutMillis);
        //}
        if (amtWritten < 0) {
            throw new IOException("Error writing " + writeLength +
                " bytes at offset " + written + " length=" + src.length);
        }
        written += amtWritten;
    }
    return written;
}

Although these are the core functions used for communication but the data received through these functions are further processed using another class known as PacketHandler. In the PacketHandler class also there are two major functions i.e sendByte and getByte(), these are the main functions which are further used in other classes for communication.

The sendByte function:-

public void sendByte(int val) throws IOException {
    if (!connected) {
        throw new IOException("Device not connected");
    }
    if (!loadBurst) {
        try {
            mCommunicationHandler.write(new byte[] {
                (byte)(val & 0xff), (byte)((val >> 8) & 0xff)
            }, timeout);
        } catch (IOException e) {
            Log.e("Error in sending int", e.toString());
            e.printStackTrace();
        }
    } else {
        burstBuffer.put(new byte[] {
            (byte)(val & 0xff), (byte)((val >> 8) & 0xff)
        });
    }
}

As we can see that in this function also the main function used is the write function of communicationHandler but in this class the data is further processed.

Setting Up the Analog Constants

For setting up the ranges, gains and other properties of channels, a different class of AnalogConstants is implemented in the android app, in this class all the properties which are used by the channels are defined which are further used in the sendByte() functions for communication.

public class AnalogConstants {

    public double[] gains = {1, 2, 4, 5, 8, 10, 16, 32, 1 / 11.};
    public String[] allAnalogChannels = {"CH1", "CH2", "CH3", "MIC", "CAP", "SEN", "AN8"};
    public String[] biPolars = {"CH1", "CH2", "CH3", "MIC"};
    public Map<String, double[]> inputRanges = new HashMap<>();
    public Map<String, Integer> picADCMultiplex = new HashMap<>();

    public AnalogConstants() {

        inputRanges.put("CH1", new double[]{16.5, -16.5});
        inputRanges.put("CH2", new double[]{16.5, -16.5});
        inputRanges.put("CH3", new double[]{-3.3, 3.3});
        inputRanges.put("MIC", new double[]{-3.3, 3.3});
        inputRanges.put("CAP", new double[]{0, 3.3});
        inputRanges.put("SEN", new double[]{0, 3.3});
        inputRanges.put("AN8", new double[]{0, 3.3});

        picADCMultiplex.put("CH1", 3);
        picADCMultiplex.put("CH2", 0);
        picADCMultiplex.put("CH3", 1);
        picADCMultiplex.put("MIC", 2);
        picADCMultiplex.put("AN4", 4);
        picADCMultiplex.put("SEN", 7);
        picADCMultiplex.put("CAP", 5);
        picADCMultiplex.put("AN8", 8);

    }
}

Also in the AnalogInput sources class many other properties such as CHOSA( a variable assigned to denote the analog to decimal conversion constant of each channel) are also defined

public AnalogInputSource(String channelName) {
    AnalogConstants analogConstants = new AnalogConstants();
    this.channelName = channelName;
    range = analogConstants.inputRanges.get(channelName);
    gainValues = analogConstants.gains;
    this.CHOSA = analogConstants.picADCMultiplex.get(channelName);
    calPoly10 = new PolynomialFunction(new double[] {
        0.,
        3.3 / 1023,
        0.
    });
    calPoly12 = new PolynomialFunction(new double[] {
        0.,
        3.3 / 4095,
        0.
    });
    if (range[1] - range[0] < 0) {
        inverted = true;
        inversion = -1;
    }
    if (channelName.equals("CH1")) {
        gainEnabled = true;
        gainPGA = 1;
        gain = 0;
    } else if (channelName.equals("CH2")) {
        gainEnabled = true;
        gainPGA = 2;
        gain = 0;
    }
    gain = 0;
    regenerateCalibration();
}

Also in this constructor a polynomial function is also called which further plays an important  role in measuring voltage as it is through this polynomial function we get the voltage of channels in the science lab class , also it is also used in oscilloscope for plotting the graph . So this was the setup of analog channels.

Voltage Measuring Functions

There are two major functions for measuring voltages which are present in the scienceLab class

  • getAverageVoltage
  • getRawableVoltage

Here are the functions

private double getRawAverageVoltage(String channelName) {
    try {
        int chosa = this.calcCHOSA(channelName);
        mPacketHandler.sendByte(mCommandsProto.ADC);
        mPacketHandler.sendByte(mCommandsProto.GET_VOLTAGE_SUMMED);
        mPacketHandler.sendByte(chosa);
        int vSum = mPacketHandler.getVoltageSummation();
        mPacketHandler.getAcknowledgement();
        return vSum / 16.0;
    } catch (IOException | NullPointerException e) {
        e.printStackTrace();
        Log.e(TAG, "Error in getRawAverageVoltage");
    }
    return 0;
}

This is the major function which takes the data from the communicationHandler class via packetHandler. Further this function is used in the getAverageVoltage function.

private double getAverageVoltage(String channelName, Integer sample) {
    if (sample == null) sample = 1;
    PolynomialFunction poly;
    double sum = 0;
    poly = analogInputSources.get(channelName).calPoly12;
    ArrayList < Double > vals = new ArrayList < > ();
    for (int i = 0; i < sample; i++) {
        vals.add(getRawAverageVoltage(channelName));
    }
    for (int j = 0; j < vals.size(); j++) {
        sum = sum + poly.value(vals.get(j));
    }
    return sum / vals.size();
}

This function uses the data from the getRawableVoltage function and uses it the polynomial generated in the analog lasses to calculate the final voltage. Thus this was the core backend of calculating the voltages through channels in PSLab.

Resources:

Continue Reading

Using Multimeter in PSLab Android Application

The Pocket Science Lab as we all know is on the verge of development and new features and UI are added almost every day. One such feature is the implementation of multimeter which I have also discussed in my previous blogs.

Figure (1) : Screenshot of the multimeter

But although many functionality of multimeter such as resistance measurement are working perfectly, there are still various bugs in the multimeter. This blog is dedicated to using multimeter in the android app.

Using the multimeter

Figure (2): Screenshot showing guide of multimeter

Figure (2) shows the guide of the multimeter, i.e how basic basic elements such as resistance and voltage are measured using the multimeter. The demonstration of measuring the resistance and voltage are given below.

Measuring the resistance

The resistance is measure by connecting the SEN pin to the positive end of resistor and the GND pin to the negative end of resistor and then clicking the RESISTANCE button.

                   Figure (3) : Demonstration of resistance measurement

Measuring the voltage

To measure the voltage as said in the Guide, directly connect the power source to the the channel pins, although currently only the CH3 pin will show the most accurate results, work is going on other channel improvisation as well.

Figure (4) : Demonstration of Voltage measurement

 

And thus this is how the multimeter is used is used in the android app.  Of course there are still many many features such as capacitance measurements which are yet to be implemented and the work is going on them

Resources:

Continue Reading

Deploying loklak search on Heroku & Using config to store and load external URLs

It is really important to have a separate configuration for all the hardcoded URLs having multiple instances across the project. It would help in testing and comparing projects like loklak with a separate configuration for hardcoded URLs. We would also be discussing deployment of Angular based loklak on heroku through this blog post.

Creating shared URL config

The idea here is to export a const Object containing all the collective hardcoded URLs used inside the project. We would try to store similar/collective URLs inside a same key e.g. all the github URLs must go inside key: ‘github’.

export const defaultUrlConfig = {
	fossasia: {
		root: 'https://fossasia.org',
		blog: 'https://blog.fossasia.org'
	},
	loklak: {
		apiServer: 'https://api.loklak.org',
		apiBase: 'loklak.org',
		blog: 'http://blog.loklak.net',
		dev: 'https://dev.loklak.org',
		apps: 'https://apps.loklak.org'
	},
	github: {
		loklak: 'https://github.com/loklak',
		fossasia: 'https://github.com/fossasia'
	},
	phimpme: {
		root: 'https://phimp.me'
	},
	susper: {
		root: 'https://susper.com'
	},
	susiChat: {
		root: 'https://chat.susi.ai'
	},
	pslab: {
		root: 'https://pslab.fossasia.org'
	},
	eventyay: {
		root: 'https://eventyay.com'
	}
};

 

Storing URLs with a key instead of storing it inside a simple Array, makes it easier to add a new URL at required place and easy to modify the existing ones. Now, this configuration can easily be called inside any Component file.

Using config inside the component

Now, the work is really simple. We just need to import the configuration file inside the required Component and store/use directly the respective URLs.

First step would be to import the configuration file as follows:

import { defaultUrlConfig } from ‘../shared/url-config’;

 

Note: Respective path for url-config file for different components might differ.

Now, we would need to store the defaultUrlConfig inside a variable.

public configUrl = defaultUrlConfig;

 

At this point, we have all the configuration URLs which could be extracted from configUrl.

Displaying URLs in HTML

We would use Angular’s interpolation binding syntax to display the URLs from configUrl in HTML. Let’s say we want to display FOSSASIA’s github url in HTML, then we would simply need to do:

{{ configUrl.github.fossasia }}

 

This could be used as an example to to replace all the hardcoded URLs inside the project.

Deploying loklak search on Heroku

Note: I am skipping the initial steps of creating a project on heroku. It is very easy to setup a project on heroku, for initial steps please follow up here.

First step in this direction would be to add a server.js file in root directory and add the following express server code in it:

const express = require(‘express’);
const path = require(‘path’);
const app = express();
app.use(express.static(__dirname + ‘/dist/<name-of-app>’));
app.get(‘/*’, function(req,res) {
res.sendFile(path.join(__dirname+‘/dist/<name-of-app>/index.html’));
});
app.listen(process.env.PORT || 8080);

 

Second step would be to add the following commands inside package.json at respective attributes.

“postinstall”: “ng build –aot -prod”
“start”: “node server.js”

 

Testing

Click on the respective URL link inside the project UI to test the configuration for hardcoded URLs. To check the deployment on heroku, please open the following URLs:

Development branch: loklak-search-dev.herokuapp.com

Master branch: loklak-search.herokuapp.com

Resources

Continue Reading
Close Menu