Implementing Experiment Functionality in PSLab Android

Using the PSLab Hardware Device, users can perform experiments in various domains like Electronics, Electrical, Physics, School Level experiments, etc. These experiments can be performed using functionalities exposed by hardware device like Programmable Voltage Sources, Programmable Current Source, etc. In this post we will try implementing the functionality to perform an experiment using the PSLab Hardware Device and the PSLab Android App.

Let us take the Ohm’s law experiment as an example and see how it’s implement using the  PSLab Android App.

Ohm’s law states that the current through a conductor between two points is directly proportional to the voltage across the two points, effectively using a constant of proportionality called Resistance (R) where,

R = V / I

Schematic

Layout to perform Ohm’s law experiment

The Ohm’s law experiment requires a variable current, so a seekbar is provided to change the current coming from PCS channel, values of which are continuously reflected in the TextView next to it.

Implementation

The Read button has a listener attached to it. Once it is clicked, the currentValue is updated with the value parsed from the seekbar progress and the selectedChannel variable is assigned from the spinner. These variables are used by the background thread to change the current supplied by current source (PCS pin) of the device and to read the updated voltage from the selected channel of the device.

btnReadVoltage.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       selectedChannel = channelSelectSpinner.getSelectedItem().toString();
       currentValue = Double.parseDouble(tvCurrentValue.getText().toString());
       if (scienceLab.isConnected()) {
           CalcDataPoint calcDataPoint = new CalcDataPoint();
           calcDataPoint.execute();
       } else {
           Toast.makeText(getContext(), "Device not connected", Toast.LENGTH_SHORT).show();
       }
   }
});

CalcDataPoint is an AsyncTask which does all the underlying work like setting the current at the PCS channel, reading the voltage from the CH1 channel and triggering the update of the data points on the graph.

private class CalcDataPoint extends AsyncTask<Void, Void, Void> {

   @Override
   protected Void doInBackground(Void... params) {
       scienceLab.setPCS((float) currentValue);
       switch (selectedChannel) {
           case "CH1":
               voltageValue = scienceLab.getVoltage("CH1", 5);
               break;
           case "CH2":
               voltageValue = scienceLab.getVoltage("CH2", 5);
               break;
           case "CH3":
               voltageValue = scienceLab.getVoltage("CH3", 5);
               break;
           default:
               voltageValue = scienceLab.getVoltage("CH1", 5);
       }
       x.add((float) currentValue);
       y.add((float) voltageValue);
       return null;
   }

   @Override
   protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       updateGraph();
   }
}

updateGraph() method is used to update the graph on UI thread. It creates a new dataset from the points which were added by the background thread and refreshes the graph with it using the invalidate() method.

private void updateGraph() {
   tvVoltageValue.setText(df.format(voltageValue));
   List<ILineDataSet> dataSets = new ArrayList<>();
   List<Entry> temp = new ArrayList<>();
   for (int i = 0; i < x.size(); i++) {
       temp.add(new Entry(x.get(i), y.get(i)));
   }
   LineDataSet dataSet = new LineDataSet(temp, "I-V Characteristic");
   dataSet.setColor(Color.RED);
   dataSet.setDrawValues(false);
   dataSets.add(dataSet);
   outputChart.setData(new LineData(dataSets));
   outputChart.invalidate();
}

Roadmap

We are planning to add an option to support multiple trials of the same experiment and save each trails for further reference. App flow to perform experiment is based on Science Journal app by Google.

Resources

  • Article on Ohm’s law and Power on electronics-tutorial
  • To know more about Voltage, Current, Resistance and Ohm’s law, head on to detailed tutorial on sparkfun
  • Implementation of perform experiment functionality in PSLab Desktop App
Continue Reading

Performing the Experiments Using the PSLab Android App

General laboratory experiments can be performed using core functionalities offered by the PSLab hardware device like Programmable Voltage Sources, Programmable Current Source, Analog to Digital Converter, Frequency Counter, Function Generators, etc. In this post we will  have a brief look on a general laboratory experiment and how we can perform it using the  PSLab Android App and the PSLab hardware device.

We are going to take Zener I-V Characteristics Curve experiment as an example to understand how we can perform a general experiment using the PSLab device. First, we will  look at the general laboratory experiment and it’s format. Then we will see how that experiment can be performed using the PSLab Android App and the PSLab Hardware Device.

Experiment Format of General Experiment in Laboratory

AIM: In this experiment, our aim is to observe the relation between the voltage and the corresponding current that was generated. We will then plot it to get the dependence.

Apparatus:

  • A Zener Diode
  • A DC Voltage Supplier
  • Bread Board
  • 100 ohm resistor
  • 2 multimeter for measuring current and voltages
  • Connecting wires

Theory: A Zener Diode is constructed for operation in the reverse breakdown region.The relation between I-V is almost linear in this case, Vz = Vz0 + Iz * Rz , where Rz is the dynamic resistance of the zener at the operating point and Vz0 is the voltage at which the straight-line approximation of the I-V characteristic intersects the horizontal axis. After reaching a certain voltage, called the breakdown voltage, the current increases drastically even for a small change in voltage. However, there is no appreciable change in voltage accompanying this current change. So, when we plot the graph, we get a curve which is very near to the x-axis and nearly parallel to it until a particular potential value, called the Zener potential, is reached. After the Zener potential Vz value, there will be a sudden change and the graph becomes exponential.

Source: learning about electronics

Procedure: Construct the circuit as shown in figure below

Now, start increasing the voltage until a reading in the multimeter for current can be obtained. Note that reading. Now, start increasing the input voltage and take the corresponding current readings. Using the set of readings observed,  construct a V vs I graph. This graph gives us the I-V characteristics. The slope of the curve at any point gives the dynamic resistance at that voltage.

Result: The Characteristic curve has been verified after plotting V-I data points on the graph.

Experiment format in PSLab Android App

We have a ViewPager that renders two fragments:

  1. Experiment Doc– It consists of information like the Aim of experiment, Schematic, Output screenshot that we will get after the experiment has been performed.
  2. Experiment Setup– It consists of the setup to configure the PSLab device. This fragment is analogous to the experiment apparatus of the laboratory.  

Below is a gif showing the experiment doc of the Zener I-V experiment which is to be performed using the PSLab device. It consists of a schematic and a screenshot of the output that we get after performing the experiment.

Source: PSLab Android

Make the circuit connections on a breadboard as shown in the schematic. After the circuit is complete we need to configure experiment.

Source: PSLab Android

To configure the experiment, we give the initial voltage, the final voltage and the step size. After clicking on START EXPERIMENT, the voltage is varied on the PV1 channel from the initial voltage to final voltage by increasing the voltage in step size. At each variation of voltage, the current is calculated by dividing the voltage difference between resistor by its resistance value i.e

I = ( VPV1 - VCH1 ) / R

As soon as the initial voltage reaches the final voltage, the experiment stops and data points are plotted on the graph. From the graph we can see the change in the current through a zener diode when the voltage varies across it’s terminals.

The output that was obtained after the experiment is I-V characteristic curve for Zener Diode as shown in the image below.

It can be clearly seen that after the breakdown voltage (~0.7V) the  current increases drastically with respect to the  increase in the voltage. After this point, the voltage can be considered  nearly constant unlike the current which varies exponentially.

In the PSLab Android App, there are read-back errors while reading bytes serially from the PSLab Hardware Device. As a result, the data points are not read accurately and an inaccurate plot is generated on the graph as shown in the image below.

Source: PSLab Android

Resources

Continue Reading

SPI Communication in PSLab

PSLab supports communication using the Serial Peripheral Interface (SPI) protocol. The Desktop App as well as the Android App have the framework set-up to use this feature. SPI protocol is mainly used by a few sensors which can be connected to PSLab. For supporting SPI communication, the PSLab Communication library has a dedicated class defined for SPI. A brief overview of how SPI communication works and its advantages & limitations can be found here.

The class dedicated for SPI communication with numerous methods defined in them. The methods required for a particular SPI sensor may differ slightly, however, in general most sensors utilise a certain common set of methods. The set of methods that are commonly used are listed below with their functions.

In the setParameters method, the SPI parameters like Clock Polarity (CKP/CPOL), Clock Edge (CKE/CPHA), SPI modes (SMP) and other parameters like primary and secondary prescalar which are specific to the device used.

Primary Prescaler (0,1,2,3) for 64MHz clock->(64:1,16:1,4:1,1:1)

Secondary prescaler (0,1,..7)->(8:1,7:1,..1:1)

The values of CKP/CPOL and CKE/CPHA needs to set using the following convention and according to our requirements.

  • At CPOL=0 the base value of the clock is zero, i.e. the idle state is 0 and active state is 1.
    • For CPHA=0, data is captured on the clock’s rising edge (low→high transition) and data is changed at the falling edge (high→low transition).
    • For CPHA=1, data is captured on the clock’s falling edge (high→low transition) and data is changed at the rising edge (low→high transition).
  • At CPOL=1 the base value of the clock is one (inversion of CPOL=0), i.e. the idle state is 1 and active state is 0.
    • For CPHA=0, data is captured on the clock’s falling edge (high→low transition) and data is changed at the rising edge (low→high transition).
    • For CPHA=1, data is captured on the clock’s rising edge (low→high transition) and data is changed at the falling edge (high→low transition).

public void setParameters(int primaryPreScalar, int secondaryPreScalar, Integer CKE, Integer CKP, Integer SMP) throws IOException {
        if (CKE != null) this.CKE = CKE;
        if (CKP != null) this.CKP = CKP;
        if (SMP != null) this.SMP = SMP;

        packetHandler.sendByte(commandsProto.SPI_HEADER);
        packetHandler.sendByte(commandsProto.SET_SPI_PARAMETERS);
        packetHandler.sendByte(secondaryPreScalar | (primaryPreScalar << 3) | (this.CKE << 5) | (this.CKP << 6) | (this.SMP << 7));
        packetHandler.getAcknowledgement();
    }

 

The start method is responsible for sending the instruction to initiate the SPI communication and it takes the channel which will be used for communication as input.

public void start(int channel) throws IOException {
        packetHandler.sendByte(commandsProto.SPI_HEADER);
        packetHandler.sendByte(commandsProto.START_SPI);
        packetHandler.sendByte(channel);
    }

 

The setCS method is responsible for selecting the slave with which the SPI communication has to be done. This feature of SPI communication is known as Chip Select (CS) or Slave Select (SS). A master can use multiple Chip/Slave Select pins for communication whereas a slave utilises just one pin as SPI is based on single master multiple slaves principle. The capacity of PSLab is limited to two slave devices at a time.

public void setCS(String channel, int state) throws IOException {
        String[] chipSelect = new String[]{"CS1", "CS2"};
        channel = channel.toUpperCase();
        if (Arrays.asList(chipSelect).contains(channel)) {
            int csNum = Arrays.asList(chipSelect).indexOf(channel) + 9;
            packetHandler.sendByte(commandsProto.SPI_HEADER);
            if (state == 1)
                packetHandler.sendByte(commandsProto.STOP_SPI);
            else
                packetHandler.sendByte(commandsProto.START_SPI);
            packetHandler.sendByte(csNum);
        } else {
            Log.d(TAG, "Channel does not exist");
        }
    }

 

The stop method is responsible for sending the instruction to the stop the communication with the slave.

public void stop(int channel) throws IOException {
        packetHandler.sendByte(commandsProto.SPI_HEADER);
        packetHandler.sendByte(commandsProto.STOP_SPI);
        packetHandler.sendByte(channel);
    }

 

PSLab SPI class has methods defined for sending either 8-bit or 16-bit data over SPI which are further classified on whether they request the acknowledgement byte (it helps to know whether the communication was successful or unsuccessful) or not.

The methods are so named send8, send16, send8_burst and send16_burst . The burst methods do not request any acknowledgement value and as a result work faster than the normal methods.

public int send16(int value) throws IOException {
        packetHandler.sendByte(commandsProto.SPI_HEADER);
        packetHandler.sendByte(commandsProto.SEND_SPI16);
        packetHandler.sendInt(value);
        int retValue = packetHandler.getInt();
        packetHandler.getAcknowledgement();
        return retValue;
    }

 

Resources:

Continue Reading

Using the Audio Jack to make an Oscilloscope in the PSLab Android App

The PSLab Android App allows users to access functionality provided by the PSLab hardware device, but in the interest of appealing to a larger audience that may not have immediate access to the device, we’re working on implementing some additional functionalities to perform experiments using only the hardware and sensors that are available in most android phones. The mentors suggested that the audio jack (Microphone input) of phones can be hacked to make it function as an Oscilloscope. Similarly, the audio output can also be used as a 2-channel arbitrary waveform generator. So I did a little research and found some articles which described how it can be done. In this post, I will dive a bit into the following aspects –

  • AudioJack specifications for android devices
  • Android APIs that provide access to audio hardware of device
  • Integrating both to achieve scope functionality

Audio Jack specification for android devices

In a general audio jack interface, the configuration CTIA(LRGM – Left, Right, Ground, Mic) is present as shown in the image below. Some interfaces also have OMTP(LRMG – Left, Right, Mic, Ground) configuration in which the common and mic inputs are interchanged. In the image, Common refers to ground.

Source: howtogeek

If we simply cut open the wire of a cheap pair of earphones (stolen from an airplane? 😉 ) , we  will gain access to all terminals (Left, Right, Common, Mic Input) illustrated in the image below

Source: flickr

Android APIs that provide access to audio hardware of device

AudioRecord and AudioTrack are two classes in android that manage recording and playback respectively. We require only AudioRecord to implement scope functionality. We shall first create an object of the AudioRecord class, and use that object to read the audio buffer as and when required.

Creating an AudioRecord object: we need the following parameters to initialise an AudioRecord object.

SAMPLING_RATE: Almost all mobile devices support sampling rate of 44100 Hz. In this context, the definition is number of audio samples taken per second.

RECORDER_AUDIO_ENCODING: Audio encoding describes bit representation of audio data. Here we used PCM_16BIT encoding this means stream of bits generated from PCM are segregated in a set of 16 bits.

getMinimumBufferSize() returns minimum buffer size in byte units required to create an AudioRecord object successfully.

private static final int SAMPLING_RATE = 44100;
private static final int RECORDING_CHANNEL = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private AudioRecord audioRecord = null;
private int minRecorderBufferSize;
minRecorderBufferSize = AudioRecord.getMinBufferSize(SAMPLING_RATE, RECORDING_CHANNEL, RECORDER_AUDIO_ENCODING);
audioRecord = new AudioRecord(
       MediaRecorder.AudioSource.MIC,
       SAMPLING_RATE,
       RECORDING_CHANNEL,
       RECORDER_AUDIO_ENCODING,
       minRecorderBufferSize);

audioRecord object can be used to read audio buffer from audio hardware using read() method.

minRecorderBuffer size is in byte units and 2 bytes constitute a short in JAVA. Thus size of short buffer needed is half the total number of bytes.

short[] audioBuffer = new short[minRecorderBufferSize / 2];
audioRecord.read(audioBuffer, 0, audioBuffer.length);

Now audioBuffer has the audio data as a signed 16 bit values. We need to process the buffer data and plot the processed data points on chart to completely implement scope functionality. I am still looking for relation between the signed 16-bit value of audio buffer and actual mic bias voltage. According to android headset specs, Mic bias voltage is between 1.8-2.9V.

Using AudioRecord class to create a scope in PSLab Android

In PSLab Android App, there is already an Oscilloscope made to capture and plot the data received from PSLab device. To make a cheap oscilloscope, cut open the wire of a cheap headset and expose terminals as illustrated in the image above and provide input signal at microphone input terminal.

Note: Don’t provide a voltage more than 2V at mic input terminal, it can damage your android device. To be sure check peak voltage from external voltmeter of the signal that you want to apply on scope and if it’s greater than 2V, I suggest you to first make a voltage divider to lower the voltage and then you are good to go.

To integrate plotting of audio buffer, we simply need to create another thread that captures audio data and updates the UI with the processed buffer data.

public class captureAudioBuffer extends AsyncTask<Void, Void, Void> {

        private AudioJack audioJack;
        private short[] buffer; 
        public captureAudioBuffer(AudioJack audioJack) {
            this.audioJack = audioJack;
        }

        @Override
        protected Void doInBackground(Void... params) {
            buffer = audioJack.read();
            Log.v("AudioBuffer", Arrays.toString(buffer));
            audioJack.release();
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            // UPDATE UI ACCORDING TO READ BUFFER DATA 
            Log.v("Execution Done", "Completed");
        }
    }

For complete code of AudioJack class, please refer pslab-android-app.

Resources

Continue Reading

Making Custom Change Listeners in PSLab Android

In this post we are going to learn how to make custom change listeners. There are many use cases for custom change listeners like if you want to initiate some action when some variable’s value is changed. In PSLab android app, this was required during initialisation of PSLab hardware device, it takes about 3-4 seconds to initialise the device which includes reading calibration data from device and process it. So before starting the initialisation process, app notifies user with the message, “Initialising Wait …” and after initialisation is done, user is notified with the message “Initialisation Completed”.

There might be other ways to accomplish this but I found making a custom change listener for boolean and trigger notifying user action on change of boolean value to be most organised way to do it.

Another way I can think of is to pass the fragment reference to the class  constructor for which the object is to be made. And Views need to be made public for access from that object to change status after some work is done.

Let’s look at an example, we would change status in a fragment after some task in object instantiation is completed.

Implementation

Class with variable on which custom change listener is required:
Create a class and declare a variable for which you want to listen the value change to trigger some action. In this example we have created a InitializationVariable class and defined a boolean variable named initialised.

Define an interface inside the class and that’s where the trick lies. When you set/change the value of the variable through a function setVariable(boolean value) in this case, note that we are triggering the interface method too.

public class InitializationVariable {

   public boolean initialised = false;
   private onValueChangeListener valueChangeListener;

   public boolean isInitialised() {
       return initialised;
   }

   public void setVariable(boolean value) {
       initialised = value;
       if (valueChangeListener != null) valueChangeListener.onChange();
   }

   public onValueChangeListener getValueChangeListener() {
       return valueChangeListener;
   }

   public void setValueChangeListener(onValueChangeListener valueChangeListener) {
       this.valueChangeListener = valueChangeListener;
   }

   public interface onValueChangeListener {
       void onChange();
   }

}

Create an object of above class in activity/fragment:
Create an object to the class we just made and attach onValueChangeListener to it. This example shows how it’s used in PSLab Android, you can use it anywhere but remember to access view elements from a valid context.

public static InitializationVariable booleanVariable;
public class HomeFragment extends Fragment {

   @BindView(R.id.tv_initialisation_status)
   TextView tvInitializationStatus;

   public static InitializationVariable booleanVariable;// object whose value change is noted

   public static HomeFragment newInstance() {
       HomeFragment homeFragment = new HomeFragment();
       return homeFragment;
   }

   @Nullable
   @Override
   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.home_fragment, container, false);
       unbinder = ButterKnife.bind(this, view);
       return view;
   }

   @Override
   public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
       super.onViewCreated(view, savedInstanceState);

       booleanVariable.setValueChangeListener(new InitializationVariable.onValueChangeListener() {
           @Override
           public void onChange() {
               if (booleanVariable.isInitialised())
                   tvInitializationStatus.setText("Initialsation Completed");
               else
                   tvInitializationStatus.setText("Initialising Wait ...");
           }
       });
  }
}

Now whenever booleanVariable.setVariable(value) is called, it triggers the onValueChangeListener where you can manage the action you wanted to do on value change.
This is similar to how other listeners are implemented .You implement an interface and call those interface methods on some value change and classes which implement those interface have overridden methods which handle the action after change.

Hopefully this post gives you an insight about how change listeners are implemented.

Note: This post was specific to PSLab Android App, you can create custom change listener on any variable in any class and perform action on value of the variable getting changed.

Resources

Continue Reading

Opening Local HTML Files in PSLab Android App

The PSLab Android App allows users to perform experiments using the PSLab device. The experience to perform an experiment should resemble the generic way to perform the experiment. So we associated an Experiment Doc file which the user can refer to while performing experiment. Just like a regular lab manual, the experiment doc contains the AIM, THEORY & FORMULAS, SCHEMATIC, OUTPUT, etc. In the PSLab Desktop App, since there was already a provision for using HTML docs and so I  avoided reinventing the wheel and used those html files as it is.

    

The problem we faced was how to open a bunch of HTML files with their corresponding CSS, JS files in Android’s webView.

There are two ways it can be done:

  • Host the experiment docs on a server and make a request from the android app for the specific experiment doc like Diode I-V, Zener I-V, etc.
  • Put the folder containing all html, CSS, js files in assets folder and request for the HTML doc files locally.

The PSLab developer team went with the second option as the availability of  Internet  is necessary for the performing experiment if we follow the first option and so to avoid this dependence on the Internet, we went with the second option and stored HTML docs locally in assets folder.

Implementation

  • Put the folder containing all the HTML, CSS, JS files in the assets folder in your android project. In this case the folder is DOC_HTML.

  • Define the WebView in xml and take the webView’s reference in your activity/fragment
    In xml
<WebView
   android:id="@+id/perform_experiment_wv"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />

In activity/fragment

webView = (WebView) view.findViewById(R.id.perform_experiment_wv);
  • Load the url in webView in the format as shown below
webView.loadUrl("file:///android_asset/DOC_HTML/apps/" + htmlFile);

“file:///” acts as resource identifier, so file:///android_asset/ actually points to “pslab-android/app/src/main/assets/”.
From the assets directory, we can a provide route to any HTML file. Here I put all HTML files in apps folder and used the string variable “htmlFile” to point to the specific html file.

Similarly html files stored in the external storage can also be accessed but there are some cases you need to handle. For example,if external storage is mounted, you can’t request the html file from external storage.

To request html files from external storage, make sure that you have the following permission in your AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();

Relative to baseDir you can specify the path from your html files, like

baseDir + “DOC_HTML/apps” + htmlFile

Conclusion

Putting HTML files in the assets folder and requesting it by webView’s loadURL() method is the best but there are various drawbacks of using this method like the increase in size of the apk. In our case, the normal apk size was 3MB but after adding the html doc folder it increased to 7MB. It increased by almost an additional size of the html folder added in assets. As it’s written, in the android’s project overview guide, the assets folder contains files that should be compiled into an .apk file as-is.

Resources

Continue Reading

Plotting Digital Logic Lines In PSLab Android App

The PSLab device offers the Logic Analyzer functionality. A Logic Analyzer is a laboratory instrument that can capture and display digital signals from a digital system or circuit. It is similar to what an oscilloscope is for analog signals and is used to study timing relationship between different logic lines. It plots the logic lines/timing diagram which tells us the information about the state of the Digital System at any instant of time. For example, in the image below we can study the states of digital signals from channels ID1, ID2, ID3 at different times and find parameters like the propagation delay. It’s also used to find errors in Integrated Circuits (ICs) and debug logic circuits.

How I plotted ideal logic lines using MPAndroid Chart library?

Conventional method of adding data points results in the plot as illustrated in the image below. By conventional method I mean basically adding Y-axis (logic state) values corresponding to X-axis values (timestamp).

Result with normal adding and plotting data-points

In the above plot, logic lines follow non-ideal behaviour i.e they take some time in changing their state from high to low. This non-ideal behaviour of these lines increases when the user zooms in graph to analyse timestamps.

Solution to how we can achieve ideal behaviour of logic lines:

A better solution is to make use of timestamps for generating logic lines i.e time instants at which logic made a transition from HIGH -> LOW or LOW -> HIGH. Lets try to figure out with an example:

Timestamps = { 1, 3, 5, 8, 12 } and initial state is HIGH ( i.e at t = 0, it’s HIGH ). This implies that at t = 1, transition from HIGH to LOW took place so at t = 0, it’s HIGH, t = 1 it’s both HIGH and LOW,  at t = 2 it’s LOW.
Now at t = 0 & t = 2, you can simple put y = 1 and 0 respectively. But how do you add data-point for t = 1. Trick is to see how transition is taking place, if it’s HIGH to LOW then add first 1 for t = 1 and then 0 for t = 1.
So the set of points look something like this:

( Y, X ) ( LOGIC , TIME ) -> ( 1, 0 ) ( 1, 1 ) ( 0, 1) ( 0, 2 ) ( 0, 3 ) ( 1, 3 )  ( 1, 4 ) …

Code snippet for adding coordinates in this fashion:

int[] time = timeStamps.get(j);
for (int i = 0; i < time.length; i++) {
   if (initialState) {
       // Transition from HIGH -> LOW
       tempInput.add(new Entry(time[i], 1));
       tempInput.add(new Entry(time[i], 0));
   } else {
       // Transition from LOW -> HIGH
       tempInput.add(new Entry(time[i], 0));
       tempInput.add(new Entry(time[i], 1));
   }

   // changing state variable
   initialState = !initialState;
}

After adding data-points in above mentioned way, we obtained ideal logic lines successfully as illustrated in the image given below

Resources

Continue Reading

Expandable ListView In PSLab Android App

In the PSLab Android App, we show a list of experiments for the user to perform or refer to while performing an experiment, using PSLab hardware device. A long list of experiments need to be subdivided into topics like Electronics, Electrical, School Level, Physics, etc. In turn, each category like Electronics, Electrical, etc can have a sub-list of experiments like:

  • Electronics
    • Diode I-V characteristics
    • Zener I-V characteristics
    • Transistor related experiments
  • Electrical
    • Transients RLC
    • Bode Plots
    • Ohm’s Law

This list can continue in similar fashion for other categories as well. We had to  display  this experiment list to the users with a good UX, and ExpandableListView seemed the most appropriate option.

ExpandableListView is a two-level listView. In the Group view an individual item can be expanded to show it’s children. The Items associated with ExpandableListView come from ExpandableListAdapter.

 

 

 

 

 

 

 

 

Implementation of Experiments List Using ExpandableListView

First, the ExpandableListView was declared in the xml layout file inside some container like LinearLayout/RelativeLayout.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">
   <ExpandableListView
       android:id="@+id/saved_experiments_elv"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:divider="@color/colorPrimaryDark"
       android:dividerHeight="2dp" />
</LinearLayout>

Then we populated the data onto the ExpandableListView, by making an adapter for ExpandableListView by extending BaseExpandableListAdapter and implementing its methods. We then passed a Context, List<String> and Map<String,List<String>> to the Adapter constructor.

Context: for inflating the layout

List<String>: contains titles of unexpanded list

Map<String,List<String>>: contains sub-list mapped with title string

public SavedExperimentAdapter(Context context,
                                 List<String> experimentGroupHeader,
                                 HashMap<String, List<String>> experimentList) {
       this.context = context;
       this.experimentHeader = experimentGroupHeader;
       this.experimentList = experimentList;
   }

In getGroupView() method, we inflate, set title and return group view i.e the main list that we see on clicking and the  sub-list is expanded. You can define your own layout in xml and inflate it. For PSLab Android, we used the default one provided by Android

 android.R.layout.simple_expandable_list_item_2
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
   String headerTitle = (String) getGroup(groupPosition);
   if (convertView == null) {
       LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       convertView = inflater.inflate(android.R.layout.simple_expandable_list_item_2, null);
   }
   TextView tvExperimentListHeader = (TextView) convertView.findViewById(android.R.id.text1);
   tvExperimentListHeader.setTypeface(null, Typeface.BOLD);
   tvExperimentListHeader.setText(headerTitle);
   TextView tvTemp = (TextView) convertView.findViewById(android.R.id.text2);
   tvTemp.setText(experimentDescription.get(groupPosition));
   return convertView;
}

Similarly, in getChildView() method, we inflate, set data and return child view. We wanted simple TextView as sub-list item thus inflated the layout containing only TextView and setText by taking reference of textView from the inflated view.

@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
   String experimentName = (String) getChild(groupPosition, childPosition);
   if (convertView == null) {
       LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       convertView = inflater.inflate(R.layout.experiment_list_item, null);
   }
   TextView tvExperimentTitle = (TextView) convertView.findViewById(R.id.exp_list_item);
   tvExperimentTitle.setText(experimentName);
   return convertView;
}

The complete code for the Adapter can be seen here.

After creating the adapter we proceeded similarly to the normal ListView. Take the reference for ExpandableListView by findViewById() or BindView if you are using ButterKnife and set the adapter as an instance of adapter created above.

@BindView(R.id.saved_experiments_elv)
ExpandableListView experimentExpandableList;
experimentAdapter = new SavedExperimentAdapter(context, headerList, map);
experimentExpandableList.setAdapter(experimentAdapter);
Source: PSLab Android

Roadmap

We are planning to divide the experiment sub-list into categories like

  • Electronics
    • Diode
      • Diode I-V
      • Zener I-V
      • Diode Clamping
      • Diode Clipping
    • BJT and FET
      • Transistor CB (Common Base)
      • Transistor CE (Common Emitter)
      • Transistor Amplifier
      • N-FET output characteristic
    • Op-Amps
  • Electrical

This is a bit more complex than it looks, I tried using an ExpandableListView as a child for a group item but ran into some errors. I will write a post as soon as this view hierarchy has been achieved.

Resources

Continue Reading

Real time Sensor Data Analysis on PSLab Android

PSLab device has the capacity to connect plug and play sensors through the I2C bus. The sensors are capable of providing data in real time. So, the PSLab Android App and the Desktop app need to have the feature to fetch real time sensor values and display the same in the user interface along with plotting the values on a simple graph.

The UI was made following the guidelines of Google’s Material Design and incorporating some ideas from the Science Journal app. Cards are used for making each section of the UI. There are segregated sections for real time updates and plotting where the real time data can be visualised. A methods for fetching the data are run continuously in the background which receive the data from the sensor and then update the screen.

The following section denotes a small portion of the UI responsible for displaying the data on the screen continuously and are quite simple enough. There are a number of TextViews which are being constantly updated on the screen. Their number depends on the type and volume of data sent by the sensor.

<TextView
       android:layout_width="wrap_content"
       android:layout_height="30dp"
       android:layout_gravity="start"
       android:text="@string/ax"
       android:textAlignment="textStart"
       android:textColor="@color/black"
       android:textSize="@dimen/textsize_edittext"
       android:textStyle="bold" />

<TextView
       android:id="@+id/tv_sensor_mpu6050_ax"
       android:layout_width="wrap_content"
       android:layout_height="30dp"
       android:layout_gravity="start"
       android:textAlignment="textStart"
       android:textColor="@color/black"
       android:textSize="@dimen/textsize_edittext"
       android:textStyle="bold" />

 

The section here represents the portion of the UI responsible for displaying the graph. Like all other parts of the UI of PSLab Android, MPAndroidChart is being used here for plotting the graph.

<LinearLayout
       android:layout_width="match_parent"
       android:layout_height="160dp"
       android:layout_marginTop="40dp">

       <com.github.mikephil.charting.charts.LineChart
               android:id="@+id/chart_sensor_mpu6050"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:background="#000" />
</LinearLayout>

 

Since the updates needs to continuous, a process should be continuously run for updating the display of the data and the graph. There are a variety of options available in Android in this regard like using a Timer on the UI thread and keep updating the data continuously, using ASyncTask to run a process in the background etc.

The issue with the former is that since all the processes i.e. fetching the data and updating the textviews & graph will run on the UI thread, the UI will become laggy. So, the developer team chose to use ASyncTask and make all the processes run in the background so that the UI thread functions smoothly.

A new class SensorDataFetch which extends AsyncTask is defined and its object is created in a runnable and the use of runnable ensures that the thread is run continuously till the time the fragment is used by the user.

scienceLab = ScienceLabCommon.scienceLab;
i2c = scienceLab.i2c;
try {
    MPU6050 = new MPU6050(i2c);
} catch (IOException e) {
    e.printStackTrace();
}
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        while (true) {
            if (scienceLab.isConnected()) {
                try {
                    sensorDataFetch = new SensorDataFetch();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                sensorDataFetch.execute();
            }
        }
    }
};
new Thread(runnable).start();

 

The following is the code for the ASyncTask created. There are two methods defined here doInBackground and onPostExecute which are responsible for fetching the data and updating the display respectively.

The raw data is fetched using the getRaw method of the MPU6050 object and stored in an ArrayList. The data type responsible for storing the data will depend on the return type of the getRaw method of each sensor class and might be different for other sensors. The data returned by getRaw is semi-processed and the data just needs to be split in sections before presenting it for display.

The PSLab Android app’s sensor files can be viewed here and they can give a better idea about how the sensors are calibrated, how the intrinsic nonlinearity is taken care of, how the communication actually works etc.

After the data is stored, the control moves to the onPostExecute method, here the textviews on the display and the chart are updated. The updation is slowed down a bit so that the user can visualize the data received.

private class SensorDataFetch extends AsyncTask<Void, Void, Void> {
   MPU6050 MPU6050 = new MPU6050(i2c);
   ArrayList<Double> dataMPU6050 = new ArrayList<Double>();

   private SensorDataFetch(MPU6050 MPU6050) throws IOException {
   }

   @Override
   protected Void doInBackground(Void... params) {
       try {
           if (MPU6050 != null) {
               dataMPU6050 = MPU6050.getRaw();
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
           return null;
   }

   protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       tvSensorMPU6050ax.setText(String.valueOf(dataMPU6050.get(0)));
       tvSensorMPU6050ay.setText(String.valueOf(dataMPU6050.get(1)));
       tvSensorMPU6050az.setText(String.valueOf(dataMPU6050.get(2)));
       tvSensorMPU6050gx.setText(String.valueOf(dataMPU6050.get(3)));
       tvSensorMPU6050gy.setText(String.valueOf(dataMPU6050.get(4)));
       tvSensorMPU6050gz.setText(String.valueOf(dataMPU6050.get(5)));
       tvSensorMPU6050temp.setText(String.valueOf(dataMPU6050.get(6)));
   }
}

The detailed implementation of the same can be found here.

Additional Resources

  1. Learn more about how real time sensor data analysis can be used in various fields like IOT http://ieeexplore.ieee.org/document/7248401/
  2. Google Fit guide on how to use native built-in sensors on phones, smart watches etc. https://developers.google.com/fit/android/sensors
  3. A simple starter guide to build an app capable of real time sensor data analysis http://developer.telerik.com/products/building-an-android-app-that-displays-live-accelerometer-data/
  4. Learn more about using AsyncTask https://developer.android.com/reference/android/os/AsyncTask.html
Continue Reading

Handling graph plots using MPAndroid chart in PSLab Android App

In PSLab Android App, we expose the Oscilloscope and Logic Analyzer functionality of PSLab hardware device. After reading data-points to plot, we need to show plot data on graphs for better understanding and visualisation. Sometimes we need to save graphs to show output/findings of the experiment. Hence we will be using MPAndroidChart library to plot and save graphs as it provides a nice and clean methods to do so.

First add MPAndroid Chart as dependency in your app build.gradle to include the library

dependencies {
 compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'
}

For chart view in your layout file, there are many available options like Line Chart, Bar Chart, Pie Chart, etc. For this post I am going to use Line Chart.

Add LineChart in your layout file

<com.github.mikephil.charting.charts.LineChart
        android:id="@+id/lineChart"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

Take a reference to LineChart of layout file in your Activity/Fragment

LineChart lineChart = (LineChart) findViewById(R.id.chart);// Activity
LineChart lineChart = (LineChart) view.findViewById(R.id.chart);// Fragment

Now we add dataset to LineChart of layout file, I am going to add data for two curves sine and cosine function to plot sine and cosine wave on LineChart. We create two different LineDataSet one for the sine curve entries and other for the cosine curve entries.

List <Entry> sinEntries = new ArrayList<>(); // List to store data-points of sine curve 
List <Entry> cosEntries = new ArrayList<>(); // List to store data-points of cosine curve

// Obtaining data points by using Math.sin and Math.cos functions
for( float i = 0; i < 7f; i += 0.02f ){
sinEntries.add(new Entry(i,(float)Math.sin(i)));
cosEntries.add(new Entry(i,(float)Math.cos(i)));
}

List<ILineDataSet> dataSets = new ArrayList<>(); // for adding multiple plots

LineDataSet sinSet = new LineDataSet(sinEntries,"sin curve");
LineDataSet cosSet = new LineDataSet(cosEntries,"cos curve");

// Adding colors to different plots 
cosSet.setColor(Color.GREEN);
cosSet.setCircleColor(Color.GREEN);
sinSet.setColor(Color.BLUE);
sinSet.setCircleColor(Color.BLUE);

// Adding each plot data to a List
dataSets.add(sinSet);
dataSets.add(cosSet);

// Setting datapoints and invalidating chart to update with data points
lineChart.setData(new LineData(dataSets));
lineChart.invalidate();

After adding datasets to chart and invalidating it, chart is refreshed with the data points which were added in dataset.

After plotting graph output would look like the image below:

You can change the dataset and invalidate chart to update it with latest dataset.

To save graph plot, make sure you have permission to write to external storage, if not add it into your manifest file

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

To save the photo of chart into Gallery:

lineChart.saveToGallery("title");

To save a some specific location:

lineChart.saveToPath("title", "Location on SD Card");

If you want to do some resizing in chart or save two three charts in a single image, you can do so by taking out the Bitmaps and processing them to meet your requirements:

lineChart.getChartBitmap();

Resources

Continue Reading
Close Menu