Measuring CO2 with MQ135

Introduction

A MQ135 sensor on a small PCB.

The MQ135 is a cheap gas sensor that is primarily intended for detecting the presence of flammable gases. It is marketed as a generalized “air quality” sensor, rather than precision device for measuring the concentration of any specific gas. Nevertheless, according to the data sheet the MQ135 is capable of measuring the concentrations of several gases, one of which is CO2. This makes it an attractive low-cost alternative to more specialized (and more expensive) CO2-specific sensors.

Much has been written about using the MQ135 as a CO2 sensor, mostly focusing on using it together with Arduino. This article will, firstly, discuss the working principles of the MQ135 sensor and how to read the data sheet, and secondly present how to use it with the PSLab.

Theory of operation

The MQ135 consists of a surface covered in a thin layer of SnO2, and a heater resistor which serves to raise the temperature of the SnO2 surface to several hundred degrees Celsius. SnO2 is an n-type semiconductor, in which donor electrons are excited to the conduction band at elevated temperatures. However, SnO2 when exposed to air readily adsorbs oxygen onto its surface. The adsorption reaction consumes electrons from the conduction band to form negatively charged oxygen species. Therefore, SnO2 has low conductivity in clean air. [Shimizu]

In the presence of a flammable gas, the gas will also adsorb onto the sensor surface, where it consumes the adsorbed oxygen species to form CO2 and H2O. This reaction releases the donor electrons back into the conduction band, thereby raising the conductivity of the sensor. By quantifying the conductivity response to the presence of various gases, a thin SnO2 surface can be used to determine the concentration of the gas. [Shimizu]

CO2, notably, is not a flammable gas. Here, the sensing mechanism is different; instead of reacting with adsorbed oxygen directly, CO2 sensing relies on water vapor first reacting with adsorbed oxygen to form adsorbed hydroxide (OH). CO2 then in turn reacts with the adsorbed hydroxide, forming carbonate (CO32-). This process ultimately also returns electrons to the SnO2 conduction band, and therefore results in increased conductivity. [Wang et al.]

SnO2 gas sensors can also detect the presence of reducing gases such as NOx. Here, the sensing mechanism is the opposite; the reducing gas adsorbs onto the sensor surface, consuming additional electrons from the conduction band, and thereby lowers the conductivity further. The MQ135 data sheet does not specify response curves for any reducing gases, so while the sensor should be sensitive to such gases, the user would have to experimentally determine the response curves themselves. [Shimizu]

MQ135 gas response curves

The MQ135 data sheet provides a graph showing how the sensor resistance changes with the concentration of several gases. Figure 1 shows a reproduction of the data sheet graph, where the points have been manually extracted from the data sheet graph using WebPlotDigitizer.

Note that the y-axis is Rs/R0, which is the ratio between the sensor resistance at any given moment (Rs) and the sensor resistance at a specific set of conditions (R0). This is because the sensor resistance depends on the exact amount of active material on the sensor surface, which varies between individual sensors. Therefore, the resistance response must be normalized against a predefined state in order for results from different sensors to be comparable. For the MQ135, this predefined state is 100 ppm ammonia vapor in otherwise clean air at 20 °C and 65% relative humidity. The sensor resistance at this state must be known in order for the curves in Figure 1 to apply. This may seem like a problem, since most people will not be able to reproduce those conditions. Fortunately, there are other ways to derive R0. More on that later.

The responses show clearly linear behavior in loglog-scale, which means that the relationship between resistance and concentration can be expressed as:

RS / R0 = a * cb    (1)

where a and b are constants, and c is gas concentration in ppm.

The curves in the Figure 1 have been fitted using exponential regression, specifically scipy.optimize.curve_fit.

Figure 2: Data sheet gas response values (points) and fitted curves (lines).

Equation 1 gives the sensor resistance as a function of gas concentration, but we are interested in the opposite relation, i.e. gas concentration as a function of sensor resistance. A little elementary algebra gets us there:

c = (1 / a)1/b * (RS / R0)1/b = A * (RS / R0)B    (2)

With that, we can correlate sensor resistance with gas concentration for any of the gases in Figure 1. Now, we need a way to determine the sensor resistance.

MQ135 electrical characteristics

As previously mentioned, the MQ135 behaves as a variable resistor when exposed to different gas concentrations. In order to measure gas concentration using equation 2, we need a way to measure the sensor resistance.

Figure 3: Circuit diagram of a typical sensing circuit using a MQ135. Note the voltage divider in the upper path.

By connecting a resistor in series with the sensor and measuring the voltage between them we can create a voltage divider, as in figure 3. The expression for the output voltage from a voltage divider is:

Vout = RL / (RS + RL) * Vin    (3)

Rearrange to isolate the sensor resistance:

RS = RL * (Vin / Vout – 1)    (4)

Understanding R0

While equation 4 provides a way to measure the sensor resistance, the sensor resistance alone is not enough to determine the gas concentration. This is because every individual sensor has a unique resistance response, based on the amount and distribution of active material on the sensor surface. Therefore, in order to compare the response of one sensor with other sensors, the response must be normalized. R0 is the normalization factor, and is defined as the sensor resistance at a known set of conditions, i.e. a certain temperature, relative, humidity, and most importantly a certain gas concentration. The ratio of the sensor resistance and R0 can be compared between sensors.

The MQ135 data sheet defines R0 as the sensor resistance at 100 ppm ammonia at 20 °C and 65% relative humidity. Of course, it is possible to redefine R0 at some other set of conditions, but if R0 is redefined, the curves in Figure 2 no longer apply! The only situation where it makes sense to redefine R0 is if you have the means to create new response curves for the gas you wish to measure. Otherwise, keeping R0 defined as above is the best available option.

That means we need a way to calculate R0, since most people will not be able to recreate the conditions at which it is defined. By rearranging equation 2, we get:

R0 = RS * (1 / A * c)-1/B    (5)

Since we can measure the sensor resistance, that means the only unknown term in the RHS of equation 5 is the concentration. In other words, if we can measure the sensor resistance in any known gas concentration, we can calculate R0.

As it happens, we do know the concentration of one of the gases in figure 2: Carbon dioxide. The concentration of CO2 in outside air is around 400 ppm (and rising, check co2.earth for up to date values if you are reading this some years after 2021).

Accounting for temperature and humidity

The sensor response also depends on ambient temperature and humidity. The data sheet specifies the effect on sensor response at four different temperatures and two different humidity levels for ammonia vapor. Figure 4 shows a reproduction of the data sheet graph, plus fitted curves.

Figure 4: Data sheet temperature / humidity corrections (points) and fitter curves (lines).

From figure 4 we can see that the sensor resistance reaches a minimum at some temperature TM and then starts to increase. The reason the resistance decreases as the temperature increases toward TM is that higher surface temperature contributes to faster reaction rates between the gas and the adsorbed oxygen. The increase in sensor resistance at temperatures above TM can be explained by faster re-adsorption of oxygen at higher temperatures, which means more oxygen will cover the sensor surface for the same flammable gas concentration. [Shimizu]

We can also see that sensor resistance seems to decrease with at higher relative humidity. The mechanism for this is less clear. On the one hand, water vapor can adsorb onto the sensor surface, thereby decreasing the oxygen coverage and increasing sensor conductivity. On the other hand, water vapor can heal defective adsorption sites, which increases the surface area available for oxygen adsorption and therefore decreases sensor conductivity. [Wicker et al.] Additionally, recall that the CO2 sensing mechanism is highly dependent on the presence of water. Wang et al. reports a non-linear sensor response to CO2 with respect to humidity, with a response peak at 34% RH. With that in mind, the data sheet humidity dependence graph (which only has two data points and therefore must be assumed to be linear) is of limited utility when measuring CO2.

With that, we have all the pieces we need to use the MQ135 to measure CO2 concentration. Or any other of the gases in figure 2, for that matter. Enough theory, let’s take a look at how to do it in practice using the Pocket Science Lab!

Using the MQ135 with PSLab

If you have just the sensor itself, connect it as shown in figure 5, where the sensor is represented by a SparkFun gas sensor breakout board. CH1 is used to monitor the output voltage of the voltage divider between the sensor and the load resistor (which is 22K in this exaple circuit). Additionally, CH2 is used to monitor the voltage level of the PSLab’s +5V line. It is important that the voltage on the +5V line does not drop below 4.9 V, as the MQ135 will not reach its operating temperature otherwise. The MQ135 heater resistor draws up to 200 mA of current, which may be more power than the USB port powering the PSLab can supply. The PSLab does not regulate the +5V line, so it is up to the USB port to deliver enough current.

Figure 5: Guide to connect MQ135 to PSLab.
Figure 6: A MQ135 mounted on a PCB with a built-in load resistor (highlighted in red), as well as some other circuitry.

If you are using a SNS-MQ135 board, shown in figure 6, simply connect the AO pin to CH1, without the external load resistor. The SNS-MQ135 has its own load resistor, which unfortunately is only 1K. Such a small load resistor means the output voltage on AO will be very small when measuring CO2, which limits measurement accuracy. If possible, you should desolder the built-in SNS-MQ135 load resistor (outlined in red in figure 6), and use an external load resistor as in figure 5 instead. The caveat about the +5V level also applies to the SNS-MQ135.

The MQ135 must be allowed to heat to its operating temperature before it can be used. The data sheet recommends a heating period of at least 24h before use. Additionally, if the sensor hasn’t been used for a long time (or ever, if it is new), it must be allowed to burn in for even longer (>72h) in order to burn off various substances which have reacted with the surface during that time.

After the MQ135 has been allowed to heat up and burn in properly, we can use pslab-python 2.1.0+ to measure the ambient CO2 concentration.

Start by creating a multimeter to measure the voltage level on the +5V line:

>>> import pslab
>>> multimeter = pslab.Multimeter()
>>> multimeter.measure_voltage(“CH2”)
4.961014

If the voltage is at least 4.9 V, the next step is to determine the sensor’s R0. It is a good idea to use an average value over a period of time (this only needs to be done once for each sensor):

>>> import time
>>> from pslab.external.gas_sensor import MQ135
>>> mq135 = MQ135(gas=”CO2”, r_load=22e3)
>>> r0 = []
>>> for i in range(3600):
>>>     r0.append(mq135.measure_r0(400))
>>>     time.sleep(1)
>>> mq135.r0 = sum(r0) / len(r0)

Finally:

>>> mq135.measure_concentration()
418.067514310052683

Done!

References

Shimizu Y. (2014) SnO2 Gas Sensor. In: Kreysa G., Ota K., Savinell R.F. (eds) Encyclopedia of Applied Electrochemistry. Springer, New York, NY. https://doi.org/10.1007/978-1-4419-6996-5_475

Wang et al. (2016) CO2-sensing properties and mechanism of nano-SnO2 thick-film sensor, Sensors and Actuators B: Chemical, Volume 227, Pages 73-84, ISSN 0925-4005, https://doi.org/10.1016/j.snb.2015.12.025

Wicker et al. (2017) Ambient Humidity Influence on CO Detection with SnO2 Gas Sensing Materials. A Combined DRIFTS/DFT Investigation, The Journal of Physical Chemistry C, Volume 121, Number 45, Pages 25064-25073, https://doi.org/10.1021/acs.jpcc.7b06253

Continue Reading Measuring CO2 with MQ135

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.

Continue Reading Creating Activity for Visualizing Recorded Sensor Data from List Items

How to pass data between fragments of an Activity in Android app

This blog demonstrates how to pass values of a variable between two fragments of a single activity. The blog will mainly include the demonstration of passing values between fragments while using BottomSheet Navigation as done in PSLab Android application.

This blog contains the work done by me in the Lux Meter instrument of the PSLab Android app of passing data from LuxMeterConfiguration fragment to LuxMeterData fragment as shown in the featured image to set the high limit for the pointer and to set the update period of the Lux Sensor. The blog will solve the difficult task of communication between two fragments of a single activity. For passing data between multiple fragments of different activities, refer to [1].

How to pass data between fragments?

In this blog, I will pass data from Fragment 2 to Fragment 1 only. But vice versa or passing data from both the fragments can also be made using the same given approach.

  • First, make a static method in Fragment 1 which can set the parameters i.e. the value of the variables as soon as the fragment is inflated as follow
public static void setParameters(int one, int two, int three) {
        Fragment1.firstValue = one;
        Fragment1.secondValue = two;
        Fragment1.thirdValue = three;
    }
  • Now, there is one point to mark that Fragment 1 will be inflated only when Fragment 2 gets destroyed. Else, other than default inflation of Fragment 1, there is no way Fragment 1 can be inflated after navigating to Fragment 2.
  • So, override the OnDestroy() method of Fragment 2 and use the setParameters() method to set the value of variables from Fragment 2 to be used in Fragment 1.
@Override
    public void onDestroyView() {
        super.onDestroyView();
        highValue = getValueFromText(highLimit, 0, highLimitMax);
        updatePeriodValue = getValueFromText(updatePeriod, updatePeriodMin, updatePeriodMax + 100);
        Fragment1.setParameters(selectedSensor, highValue, updatePeriodValue);
    }

Here, the highValue, updatePeriodValue and selectedSensor are the variables being used in the Lux Meter fragment in PSLab Android app. But they can be replaced by the necessary variables as per the app.

So, in this way, we can pass data between the fragments of the same Activity in an Android application. Above demonstration can be extended in passing values between multiple fragments of the same Activity by creating different methods in different fragments.

Resources

  1. Blog on how to pass data between fragments of different/same activities: https://www.journaldev.com/14207/android-passing-data-between-fragments

Continue Reading How to pass data between fragments of an Activity in Android app

Prevent Android Activity from Operating while using Bottom Sheet in PSLab App

This blog demonstrates how to prevent the Android Activity in the background from operating while the Bottom Sheet is up in the foreground. The demonstration will be purely from the work I have done under PR #1355 in PSLab Android repository.

Why prevent the Activity from operating?

When using Bottom Sheet in Android, it is preferable to dim the screen behind the Bottom Sheet to provide a good user experience. But the dimming of the screen is itself an indication that the screen won’t work. Also, if the Bottom Sheet is open and while sliding it, if, by mistake, any button in the background of the bottom sheet gets pressed, then if the function related to that button starts executing then it can create a bad user experience.

For example, in PSLab Android app, in Accelerometer instrument, there are record/pause and delete buttons in the toolbar as shown in figure 1. Now, if the bottom sheet is opened and while closing it if the delete button is by mistake pressed by the user, then whole recorded data gets deleted. Thus, it’s a good practice to prevent the background Activity from operating while Bottom Sheet is opened.

Figure 1. Accelerometer Instrument in PSLab Android app

How to prevent the Activity from operating?

In this demonstration, I will use the method followed by PSLab Android app in creating a Bottom Sheet and making the background dim using a View widget. A step by step guide on how to make a Bottom Sheet as in PSLab Android app can be found in [1] and [2].

Strategy

The strategy used in solving this problem is setting an OnClickListener to the View that is used to dim the background and close the Bottom Sheet (if open) and hide the View as soon as the method is called. The View is again made visible when an upward slide gesture is made to open the Bottom Sheet.

Follow the below steps to get the desired results:

  • First, in OnCreate() method, set the OnTouchListener to the view.
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
                              if(bottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED)
                    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
tvShadow.setVisibility(View.GONE);
      }
});
  • Now, override the OnSlide() method of the GestureDetector class and add the following code to it.
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
    Float value = (float) MathUtils.map((double) slideOffset, 0.0, 1.0, 0.0, 0.8);
    view.setVisibility(View.VISIBLE);
    view.setAlpha(value);
   }

So, now test the Bottom Sheet and you will find that the Bottom Sheet will get closed as soon as the click is made outside it if it is opened. The demonstration of the working of the above code is shown in figure 2.

Figure 2. Demonstration of preventing the background Activity from operating while Bottom Sheet is up

Resources

  1. http://thetechnocafe.com/make-bottom-sheet-android/: Blog on how to make a Bottom Sheet in Android

Continue Reading Prevent Android Activity from Operating while using Bottom Sheet in PSLab App

How to use Mobile Sensors as Instruments in PSLab Android App

This blog demonstrates how to use built-in mobile sensors in an Android application. This blog will mainly feature my work done in PSLab Android repository of making a Compass and Accelerometer instrument using built-in mobile sensors.

How to access built-in mobile sensors?

Android provides an abstract class called SensorManager which is able to communicate with the hardware i.e. here the sensors in the mobile. But the SensorManager can’t provide continuous data fetched by the sensor. For this, Android provides an interface known as SensorEventListener which receives notifications from SensorManager whenever there is a new sensor data.

How to implement the functionality of sensors in Android app?

Following is a step by step process on how to add support for different sensors in an Android app

  • First, make a new class which extends SensorEventListener and override the default methods.
public class SensorActivity extends Activity implements SensorEventListener {

     public SensorActivity() {
        // Default Constructor      
     }

     @Override
     public void onAccuracyChanged(Sensor sensor, int accuracy) {
     }

     @Override
     public void onSensorChanged(SensorEvent event) {
     }
 }

Here, the SensorActivity() is the default constructor of the class and the onAccuracyChanged() and onSensorChanged() methods will be explained soon.

  • Now declare the SensorManager and use the sensor needed in the app.
private final SensorManager mSensorManager;
private final Sensor mAccelerometer;

     public SensorActivity() {
         mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
         mAccelerometer =        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
     }

Here, I have used Sensor.TYPE_ACCELEROMETER to use the built-in Accelerometer in the device. Some of the other options available are:

  1. TYPE_LIGHT – To measure ambient light
  2. TYPE_MAGNETOMETER – To measure magnetic field along different axis
  3. TYPE_GYROSCOPE – To measure movements (sudden changes) in any particular direction

The list of all available sensors in Android can be found in [1].

  • It is necessary to disable the sensors especially when the activity is paused. Failing to do so can drain the battery in just a few hours.

NOTE: The system will not disable sensors automatically when the screen turns off.

So, to save the battery and make the app efficient, we can use the registerListener method to notify the SensorManager to start fetching data from sensor and unregisterListener to notify it to stop.

@Override
protected void onResume() {
         super.onResume();
         mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
     }

@Override
     protected void onPause() {
         super.onPause();
         mSensorManager.unregisterListener(this);
     }


The onResume() method activates when the app is resumed from a paused state and the onPause() method is called when the app is paused i.e. some other app draws over the current app.

  • Now coming back to onAccuracyChanged() and onSensorChanged() methods, the onAccuracyChanged() method is used to set the accuracy of a sensor. For example, while using GeoLocation sensor, sometimes the position of the mobile isn’t very accurate and so we can define the accuracy level in this method so that the fetched data is used for calculations only if it is in the provided range. And the onSensorChanged() method is the main method where all the data is processed as soon as the new data is notified.

To get the latest value from the sensor, we can use

@Override
public void onSensorChanged(SensorEvent event) {
   data = Float.valueOf(event.values[0]);
   unRegisterListener();
}

Here, the event is an instance of the SensorEvent class which provides the updated data fetched from the sensor. Event.values is used to get the values for any of the three axis including the bias in their values. Following is the list of the index for which we can get a necessary value

values[0] = x_uncalib without bias compensation
values[1] = y_uncalib without bias compensation
values[2] = z_uncalib without bias compensation
values[3] = estimated x_bias
values[4] = estimated y_bias 
values[5] = estimated z_bias

So, in this way, we can add support for any built-in mobile sensor in our Android application.

Resources

Continue Reading How to use Mobile Sensors as Instruments in PSLab Android App

How to Add Icons or Menus into the PSLab Android App Toolbar

This blog demonstrates how to add different icons like help, play, pause, etc. and/or menu bar in the toolbar of an Android app along with setting their visibilities on the toolbar i.e. to display the icons only when space is available else to add them in the menu. The topic will be mainly explained by taking the example of menus and icons added to the PSLab app.

How to add a menu in a toolbar?

Following are the steps to add a menu or an icon in the toolbar widget of the Android app

  • First, add toolbar widget to the main layout file as follows
<android.support.v7.widget.Toolbar
   android:id="@+id/compass_toolbar"
   android:layout_width="match_parent"
   android:layout_height="?attr/actionBarSize"
   android:background="?attr/colorPrimary"
   app:popupTheme="@style/AppTheme.PopupOverlay"
   app:title="@string/compass" />

Here, popupTheme is the theme that activates when inflating the toolbar. Usually, it is kept similar to the default theme of the toolbar.

  • Now as the toolbar is ready, we can make the menu that needs to be inflated on the toolbar. For making a menu, make a folder named menu in the resources folder. Now, add a menu resource file in it by giving a proper name and then add the following code
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
       android:id="@+id/compass_help_icon"
       android:icon="@drawable/compass_help_icon"
       android:title="@string/show_axis_help"
       app:showAsAction="always" />
</menu>

A detailed explanation of the above code is as follows:

  1. The <menu>…</menu> covers all the items in the menu. There can be sub-menu and also sub-sub-menu too. To make a sub-menu, use <menu>…</menu> inside the main menu.
  2. The <item> tag inside the menu defines a specific item to be included in the menu. The icon attribute of an item is used to show the icon on the toolbar. The title attribute of an item is used to show the text inside the menu if space isn’t available to show the icon on the toolbar. The showAsAction attribute is used to define the method of an item i.e. how the item should be visible to the user. Following are some of the values that showAsAction attribute can take:
    • always – It is used to show the icon of the item on the toolbar everytime
    • never – It is used to show the item as a text in the menu everytime the activity is opened
    • ifRoom – It is used to show the icon on the toolbar if there is enough space else the item is included in the menu

NOTE: Always give IDs to menu items as they are used to distinctly identify the item in the java code.

Figure 1. Example of menu and icons in toolbar in PSLab app

As shown in figure 1, the first two icons have always value in their showAsAction attribute whereas other items have never values in their showAsAction attribute.

  • Now the layout and the menu are ready to be inflated from the Java code. First, the toolbar needs to be set up from the Java code. So find the toolbar with its id and then write the following line in the code.
setSupportActionBar(mToolbar);
  • Now the toolbar is ready and so the menu can be inflated on it. So, override the following method to inflate the menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
   MenuInflater inflater = getMenuInflater();
   inflater.inflate(R.menu.activity_compass_help_menu, menu);
   return true;
}

Here, the getMenuInflater() method is used to inflate the menu on the toolbar.

  • Now override the onCreateOptionsMenu() method to do the predefined task of selecting the icon or the item from the menu.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case R.id.compass_help_icon:
           // Do something
           break;
       default:
           break;
   }
   return true;
}

So, in this way a menu can be made so that the number of items delivered to the user can be increased by using the minimum space possible.

Resources

  1. https://developer.android.com/guide/topics/ui/menus – Android Developers guide on how to make a menu in Android
Continue Reading How to Add Icons or Menus into the PSLab Android App Toolbar

Detecting Barometer sensor in PSLab Android App

The Pocket Science Lab Android app has Barometer Instrument implemented in it. Although the instrument  is currently working through the mobile sensors and not the PSLab i2c library as there were some issues in the i2c communication with PSLab device.

Thus as the barometer was completely working on through the mobile sensors, there was a major problem coming up. Majority of the mobiles don’t have the barometer sensor which was required, only a  few of the latest devices have the sensors in them.

This issues created problem as now anyone who would have used the barometer instrument would have made an impression that the App was itself not working.

Figure(1) : Showing the stagnant barometer Instrument

Thus this created a bad impression for both the app and it’s developers.

Solving the issue

To solve this major bug, it required to first detect the barometer instrument and then implementing an alert dialog-box showing that barometer sensor is not present in his device.

  • Detecting the Barometer Sensor[2]

The barometer sensor was detected using the sensorManager class of Java

sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensor = sensorManager != null ? sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) : null;

Thus using this code the barometer sensor is detected, if sensor was not present the  sensorManger would be null.

  • Implementing the alert-box[1]

Thus if the sensorManger variable was null it notified that the sensor was not present in           the device and corresponding to which an alert-box was implemeneted

if (sensor == null) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (!isFinishing()) {
                new AlertDialog.Builder(BarometerActivity.this)
                    .setTitle(R.string.barometer_alert_title)
                    .setMessage(R.string.barometer_alert_description)
                    .setCancelable(false)
                    .setPositiveButton("ok", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    }).show();
            }
        }
    });

Thus as we can see in the code snippet the alert dialog-box will appear if  sensor is not present as shown in figure (2).

Figure(2) : Screenshot showing alert-box

Resources

  1. Android Alert-box Example, Mkyong.com:
    https://www.mkyong.com/android/android-alert-dialog-example/
  2. Creating a Barometer Application for Android, medium.com:
    https://medium.com/@ssaurel/creating-a-barometer-application-for-android-1c0a5c10b20e

Continue Reading Detecting Barometer sensor in PSLab Android App

Implementing Toolbar and Exporting Data in CSV format in Multimeter

The latest feature which was implemented in the  Pocket Science Lab Android app is the data exporting feature which was implemented in almost each and every sensor. Now in addition to sensors now the data saving functionality is also implemented in Multimeter and this blog is regarding how the data exporting functionality was implemented for multimeter.

For implementing the data exporting feature first the Toolbar was implemented in the PSLab Android App.

Implementing the Toolbar[1]

For implementing the firstly the front-end of multimeter was changed and the Toolbar was added to it.

<android.support.design.widget.AppBarLayout
  android:id="@+id/top_app_bar_layout"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:theme="@style/AppTheme.AppBarOverlay">

  <android.support.v7.widget.Toolbar
      android:id="@+id/multimeter_toolbar"
      android:layout_width="match_parent"
      android:layout_height="?attr/actionBarSize"
      android:background="?attr/colorPrimary"
      app:popupTheme="@style/AppTheme.PopupOverlay"
      app:title="Multimeter" />

The above xml codes shows the implementation of toolbar.

Figure (1): Showing the implementation of multimeter toolbar

  • Backend

For implementing the toolbar the onCreatemenu  was implemented for multimeter

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.multimeter_log_menu, menu);
this.menu = menu;
return true;
}

For the same a separate menu was created for multimeter

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/record_pause_data"
android:icon="@drawable/record_icon"
android:title="@string/record_csv_data"
app:showAsAction="always" />
<item
android:id="@+id/delete_csv_data"
android:icon="@drawable/delete_icon"
android:title="@string/delete_csv_data"
app:showAsAction="ifRoom" />
<item
android:id="@+id/record_csv_data"
android:icon="@drawable/menu_icon_save"
android:title="@string/save_csv_data"
app:showAsAction="never" />
<item
android:id="@+id/settings"
android:title="@string/nav_settings"
app:showAsAction="never" />
</menu>

Figure(2): Showing Toolbar of Multimeter

Thus through this the basic UI of the toolbar was implemented.

Implementing the Data Export Feature[2]

After implementing the toolbar for multimeter the multimeter the data exporting feature was implemented through the toolbar. The data export feature was implemented in such a manner that the user can start data recording by clicking on the record button after which the recording starts.

Figure(3):Showing snackbar after the data recording is started

and then when the user wants to end the experiment he may click on the pause button after which he gets an option either to export the data in csv format or either to delete the data.

Figure(4):Showing the menu bar for exporting data in csv

After clicking on the export data button the snacker appears which finally tells the user that the data is exported in this particular location (mentioned in the snackbar)

Figure(5):Showing the snackbar for showing location of the CSV folder

Further going to the csv folder we get the excel sheet in which the data is exported

Figure(6):Showing the excel sheet of data recorded

And this is how the data exporting feature was implemented in the PSLab android app. The users can use this functionality to perform experiments using multimeter in PSLab.

Resources

  1. Medium, Android Toolbar for AppCompatActivity:
    https://medium.com/@101/android-toolbar-for-appcompatactivity-671b1d10f354
  2. Code Project, Exporting data in CSV format:
    https://www.codeproject.com/questions/491823/read-fwritepluscsvplusinplusplusandroid

 

Continue Reading Implementing Toolbar and Exporting Data in CSV format in Multimeter

Measuring Current in PSLab Android App

The Pocket Science Lab Android app has got various functionality such as voltmeter, resistance measurement, capacitance measurement, frequency measurement as well as Count pulse measurement , one of the missing functionality among these is the Ammeter, currently in PSLab there is no direct way of measuring current. In this blog I will be discussing an indirect method of measuring current in PSLab.

Basics of measuring current

Generally in all multimeters , current is measured using  an Ammeter which uses the property of galvanometer to measure to measure current in PSLab. But as the PSLab doesn’t has any such embedded Galvanometer we cannot have a seperate Ammeter in it, but there is another method to measure the current, which is using the famous OHm’s law i.e V/I = R

In PSLab we can measure the voltage across any elements, plus we can also measure  the resistance of any circuit element in PSLab, the theory used for measuring current is stated as follows.

  1. We connect the current-source to any known resistant(or any resistance) and then measure the voltage across the resistor.
  2. Finally using ohm’s law the current will be voltage / resistance.[2]

Step-by-step guide on measuring the current

Here is the step by step guide on how to measure the current in PSLab:-

  1. Take any arbitrary resistor and measure it’s resistance. To measure the resistance follow these steps :-
    1. Take a resistance and connect it to any two pins of breadboard
    2. Connect one end to the sen pin and the other end to the GND pin of PSLab device.
    3. Now go to the Android app and select multimeter instrument.
    4. Now in the multimeter instrument set the knob to resistance, in the measure section
    5. Now click on the read button and then it will show the resistance of the resistor
  2. Now that you have measured the resistance of the resistor, now connect the current source across the resistors.
  3. Now finally measure the voltage across the resistor. To measure the voltage follow these steps.
    1. Take two male to male wires and connect it’s two individual ends to the two ends of the resistor.
    2. Now connect one of the other two ends of the wire to the any one of the CH1, CH2 or CH3 and the other end to GND respectively.
    3. Now go to the Android app and select multimeter instrument.
    4. Now in the multimeter instrument set the knob to the channel which you have selected i.e CH1, CH2 or CH3 , in the voltage section.
    5. Now click on the read button and then it will show the voltage across the resistor.
  4. Now using the using ohm’s law the current will be voltage / resistance.

Thus using these steps one can find the current in PSLab Android App.

Images for the Experiment

Figure (1): Showing resistance measurement

Figure(2) Showing voltage measurement

Resources

  1. Using-a-Multimeter, dengarden.com:
    https://dengarden.com/home-improvement/Using-a-Multimeter
  2. Wikipedia, Ohm’s Law:
    https://en.wikipedia.org/wiki/Ohm%27s_law

Continue Reading Measuring Current in PSLab Android App

Implementing Layouts for Different Screens in Android PSLab App

The Pocket Science Lab Android app has various functionalities implemented in it, one of the major implemented functionalities is the knob in the multimeter, In my previous blog I have mentioned about the knobs and how exactly it has been implemented, one the major problems in the knob is that it doesn’t fits correctly in all layouts, even though constraint layout is used , as it has different text-views positioned in a circular constraint in which exact circular radius has to be mentioned (which cannot be constrained), mentioned in my previous-blog there comes the idea of creating different layouts for different-screens in android.

Need to create multiple layouts for different screen

There arises this question of the use of creating multiple layouts for different screens when you can create a flexible layout using tools such as constraint layout. The answer to this question is that in some cases such as as the case of circular constraint or using a linear-layout  one has to use use hard-coded dimensions which thus makes difference in various screens on, there comes the need of using size and orientation quantifiers i.e making layout according to screen sizes. Als

Figure 1:Screenshot

Figure 1 shows how different layouts are made for multimeter according to the screen-size.

Size quantifiers in android

Size quantifier is a name that specifies an individual configuration (in this case size) corresponding to which resources can be used.

For example, here are some default and alternative resources:

res/
   drawable/
       icon.png
       background.png
   drawable-hdpi/
       icon.png
       background.png

In this case -hdpi is a size quantifier, there are default size quantifiers at the drawable folder.

Making different layouts according to size-quantifiers[1][2]

This blog will give a step by step guide on how to make layouts using size quantifiers.

  1. Right click the res folder in android studio and select New-> Android Resource directory.
  2. A screen like below will appear
  3. Change the resource type to layout.
  4. From the available quantifiers select the quantifier which you would require, (in our case it is size).
  5. After selecting the quantifier click the >> button after which the following screen will get displayed.
  6. Now select the screen-size and click ok to make the directory, note that only one directory can be made in a single go.
  7. After this make different layouts according to the screen-sizes and then save the respective alternative resources in this new directory. The resource files must be named exactly the same as the default resource files.

Thus by following these steps one can make different layouts according to screen-sizes in android.

Resources

  1. Support different screen sizes, Android developers https://developer.android.com/training/multiscreen/screensizes#TaskUseSizeQuali (Link to repo)
  2. App resources overview, Android developers https://developer.android.com/guide/topics/resources/providing-resources#ScreenSizeQualifier

Continue Reading Implementing Layouts for Different Screens in Android PSLab App