Reducing UI lags with AsyncTask in PSLab Android

In the Oscilloscope Activity, communication with the PSLab device goes in parallel with updations of the graph which result in inoperable UI if both these functions are performed in the main thread. This would severely degrade the user experience. In order to avoid this, we simply used AsyncTask. AsyncTasks are used to perform communications with the device in the background thread and update UI when the task in background thread completes. AsyncTask thus solves the problem of making UI super laggy while performing certain time-consuming functions. The UI remains responsive throughout.

More about AsyncTask

AsyncTask is an abstract Android class which helps the Android applications to handle the Main UI thread in a more efficient way. AsyncTask class allows to perform long lasting background operations and update the results in UI thread without affecting the main thread.

Implementing AsyncTask in Android applications

  • Create a new class inside Activity class and extend AsyncTask:

private class Task extends AsyncTask<Void, Void, Void> {
  	protected Long doInBackground(aVoid) {
     	}
 	protected void onProgressUpdate(aVoid) {
     	}
 	protected void onPostExecute(aVoid) {
     	}
}
  • Execute the task:

new Task().execute();

How they are used in Oscilloscope Activity?

The following diagram explains how AsyncTasks are used in Oscilloscope Activity. 

AsyncTask in PSLab Android App

A public class extending AsyncTask is defined, this task is executed from another thread.

public class Task extends AsyncTask<String, Void, Void> {
   ArrayList<Entry> entries;
   String analogInput;

 

doInBackgroundMethod performs the part related to communication with the PSLab device.

Here we are capturing the data from the hardware using captureTraces and fetchTraces method.

 @Override
   protected Void doInBackground(String... params) {
       try {
           analogInput = params[0];
           //no. of samples and timegap still need to be determined
           scienceLab.captureTraces(1, 800, 10, analogInput, false, null);
           Log.v("Sleep Time", "" + (800 * 10 * 1e-3));
           Thread.sleep((long) (800 * 10 * 1e-3));
           HashMap<String, double[]> data = scienceLab.fetchTrace(1); //fetching data
           double[] xData = data.get("x");
           double[] yData = data.get("y");
           entries = new ArrayList<Entry>();
           for (int i = 0; i < xData.length; i++) {
               entries.add(new Entry((float) xData[i], (float) yData[i]));
           }
       }
       catch (NullPointerException e){
           cancel(true);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return null;
   }

 

After the thread, completely executed onPostExecute is called to update the UI/graph. This method runs on the main thread.

 @Override
   protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       LineDataSet dataset = new LineDataSet(entries, analogInput);
       LineData lineData = new LineData(dataset);
       dataset.setDrawCircles(false);
       mChart.setData(lineData);
       mChart.invalidate();    //refresh the chart
       synchronized (lock){
           lock.notify();
       }
   }
}

 

This simply solves the problem of lags and the Oscilloscope works like a charm.

Resources

Continue Reading

Using Hierarchical Blocks in KiCAD to Collaborate in PSLab Hardware Development

The PSLab hardware project designed in KiCAD, an ECAD tool; doesn’t support collaborative features like Git providing for software projects. As explained in a previous blog post on techniques to help up with project collaboration, this blog post will demonstrate how two developers can work together on the same hardware project.

The difficulties arise as the whole project is in one big schematic file. Editing made by one developer will affect to the editing done by the other developers causing merge conflicts. KiCAD doesn’t compile nicely if the changes were fixed manually most of the cases.

The solution practiced in the pslab-hardware project is using hierarchical blocks. This blog post will use a KiCAD project with an oscillator implementation and a voltage regulator implementation just like the ones in pslab-hardware schematics. To avoid complications in understanding changes in a huge circuit, only these two modules will be implemented separately in the blog.

Initially the project will look like the following figure;

Sheet1 Sheet2

These two hierarchical blocks will be created as different .sch files in the project directory as follows;

Assume two different developers are working on these two different blocks. That is the key concept in collaborating hardware projects in KiCAD. As long as the outer connections (pins) don’t get changed, edits made to one block will have no effect on the other blocks.

Developer 1 decided that the existing power circuit is not efficient for the PSLab device. So he decided to change the circuit in Sheet 1. The circuit before and after modification is shown in the table below.

Sheet 1 (Before) Sheet 1 (After)

If we take a look at the git status now, it will be as follows;

From this it is noticeable that neither the main schematic file nor Developer2.sch hasn’t been touched by the edits made to Developer1.sch file. This avoids merge conflicts happening when all the developers are working on the same schematic file.

Resources :

Continue Reading

Adding Tablet support for PSLab Android App

Making layouts compatible with tablet definitely, helps in increasing the target audience. Tablets are different than smartphones, they available in size as big as 7’’ and 10’’. This gives developers/designers a lot of screen space to work on which in turn can be utilized in a way different than smartphones. In the PSLab Oscilloscope Activity and in fact the entire application needs to have tablet support. To achieve these two approaches are used. They are as follow:

  1. Creating layouts compatible with the tablet
  2. Programmatically differentiate between phone and tablet

Creating layouts compatible with the Tablet

A series of following steps help to create layouts for the tablet with much ease

  1. Right click on layouts. Then move the cursor to new and select Layout resource file.

  1. A new resource file dialog box will appear. Under filename type the same name of the file for which you want to create tablet layout. Under directory name type layout-sw600dp for the layout of 7’’ tablet and layout-sw720dp for layout of 10’’ tablet.

  1. The Android Studio will automatically create a folder with two layouts, one for phone and another for tablet inside layouts folder. Now you can work on tablet layout and make the app compatible with the tablet.

Programmatically differentiate between Phone and Table

In Oscilloscope Activity of PSLab Android App, the dimensions of the layout are programmatically set. These dimensions are different from that should be used for the tablet. So, it is important for the app to know whether it’s operating on a phone or a tablet.

This can be achieved using the following steps.

  1. Right click on resources, move the cursor on new and select Android resource file.

  1. A new resource file dialog will appear, under file name type isTablet and press OK. Here we are creating a resource for a phone.

  1. In the XML file isTablet write the following code.

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <bool name="isTablet">false</bool>
</resources>

This resource returns false when accessed.

  1. Repeat 1, 2 step and under new resource file dialog box type values-600dp. Then write the following code.

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <bool name="isTablet">true</bool>
</resources>

This resource returns true when accessed.

  1. Now you can access this resource in Activity simply writing the following code.

boolean tabletSize = getResources().getBoolean(R.bool.isTablet);

tabletSize will be true if accessed in a tablet otherwise it will be false. Hence code can written accordingly.

By this way, we can find whether the application is running on tablet or phone programmatically.

Resources

 

Continue Reading

Developing Oscilloscope UI in PSLab Android App

User Interface (UI) is one of the most important part of any software development. In PSLab while developing the UI of the Oscilloscope the following points are very critical.

  1. The UI should expose all the functionalities of Oscilloscope that can be performed using PSLab.
  2. The UI should be very convenient to use. It should allow the user to access functionalities of the PSLab with ease.
  3. The UI should be attractive.

Since Android smartphones come with relatively small size, it was a challenge to develop a UI for Oscilloscope. In this blog, I am going to mention the steps involved in developing the Oscilloscope UI.

Step 1: Creating a mockup

Initially, a mock-up for Oscilloscope UI is created using moqups tool. Later, the mock-up was discussed in public channel where fellow developers and mentors approved it. Let’s discuss some benefits of adopting this layout for Oscilloscope Activity.

  • The graph is ensured maximum screen space as it is the most important component/section of the Oscilloscope. This is also the reason why we kept the screen orientation to landscape.
  • The widgets don’t populate the screen, make the UI look clean.
  • The UI is comparable to basic app’s people use in their daily lives hence very convenient to use.

Mockup of Oscilloscope UI developed using moqups tool

Step 2: Deciding the API to be used

In Oscilloscope Activity, the main component is the graph. The captured data from the PSLab device is plotted on the graph. We decided to use MPAndroidCharts for the same.

Step 3: Deciding the space given to different sections of the UI

The next step was to decide how much screen space each section of Oscilloscope should acquire. There are 3 sections of the Oscilloscope UI.

  1. Graph
  2. Side panel consisting of buttons, each button loads a different set Oscilloscope controls and features in 3.
  3. A lower panel which is basically a fragment displaying controls and features corresponding to the button selected in 2.

By trying different dimensions and arrangements the following configuration fits the best.

To achieve this, the dimensions of different sections is set programmatically. This makes the UI compatible with different screen sizes.

public void onWindowFocusChanged() {

       RelativeLayout.LayoutParams lineChartParams = (RelativeLayout.LayoutParams) mChartLayout.getLayoutParams();
       lineChartParams.height = height * 2 / 3;
       lineChartParams.width = width * 5 / 6;
       mChartLayout.setLayoutParams(lineChartParams);
       RelativeLayout.LayoutParams frameLayoutParams = (RelativeLayout.LayoutParams) frameLayout.getLayoutParams();
       frameLayoutParams.height = height / 3;
       frameLayoutParams.width = width * 5 / 6;
       frameLayout.setLayoutParams(frameLayoutParams);
   
}

onWindowFocusChanged method is called in onCreate method. Here we are first receiving current layout parameters and then setting new layout parameters.

Step 4: Developing each section

  1. Graph

The graph needs to be customized concerning following requirements

  • Dual y axis, one dedicated to CH2 and another to analog input selected.
  • Black background
  • Grid lines
  • Scaling
  • Initial scale for x and y axis.

To achieve this a chartInit method is created which initializes the graph as per required. It is called in onCreate method.

2. Side Panel

It is a simple layout consisting of image buttons and text views. It is used to replace fragments in Lower Panel. To achieve this, image buttons and textviews were added to the layout and image buttons weight is set to 2. Later onClick listeners were added to both image buttons and textviews.

3. Lower Panel

The lower panel is frame layout which accommodates different fragments (one at a time). To achieve these different fragments are created that are ChannelsParametersFragment, TimebaseTriggerFragment, DataAnalysisFragment and XYPlotFragment. In ChannelsParametersFragment, TimebaseTriggerFragment and XYPlotFragment fragments, constraint views are used whereas in TimebaseTriggerFragment table layout is used. Each fragment allows the user to access different controls and features.

The Final Layout

The above is the GIF of the Oscilloscope UI.

This covers various steps for developing Oscilloscope UI in PSLab Android App.

Resources

Continue Reading

How to Read PIC Data-Sheet and Add a New Functionality to PSLab Firmware

Reading data-sheets is not a fun task. Going through tens of hundreds of pages with numerical, mathematical and scientific data is not fun reading. This blog post attempts to simplify reading the available data-sheets related to PIC24 micro-controller used in the PSLab device to help reader with implementing a new feature in PSLab firmware.

There are many features available in the PSLab device, such as; UART, SPI, I2C, ADC and Basic I/O reading. The “basic” implementation techniques do not vary much from one feature to other. That being stated this blog will carry out the basic implementation techniques one should follow and basic knowledge on PIC micro-controller programming to save himself from the trouble going through the 500+ pages in PIC data-sheets.

PIC Basics:

Before go into implementation there are few facts one should know about PIC programming.

– In the micro-controller values are saved in a memory block known as Registers. The values saved in these registers are volatile as they are all set to 0 regardless the value they were assigned when the power is off.

– Micro-controller configurations are made by setting values to these registers. Even turning on and off a whole feature like UART in PSLab device can be done using setting 0 to UARTEN register bit.

– When it comes to I/O ports, there are two different types of registers called TRIS and LAT/PORT. By setting 1 to TRIS ports will make the relevant pin an input pin. Setting it to 0 will make it an output pin. Easy way to remember this is think 1 as I in input and 0 as O in output. In UART implementation of PSLab, pin RP40 is set as an input pin to receive the data stream and pin RP39 is set as an output pin to send the data stream out. These settings are made using TRIS port settings. PORT registers save the value received by the relevant input pin attached to it.


The above figure extracted from mikroe learning materials, illustrates different stages an I/O pin can handle. As an extra point, ANSEL register makes the pin support digital signals or analog signals as per user requirements.

– In PIC, some registers such as PORT, TRIS and registers with similar functionalities are combined together. To access the value of each individual register can be done using dot notation. Assume the program requires to access the 8th register in TRISB register set. Note that the registers are indexed from zero. This implies that the 8th register will have the index 8 in the register sub-set. The following syntax is used to access the register;

TRISBbits.TRISB7

 

The above points cover the basic knowledge one should have when developing firmware to PSLab device.

How to implement a feature like UART in PSLab firmware?

The first thing to know when implementing a new feature is that the developer needs to be familiar with the relevant hardware protocols. As an example, to implement UART; relevant protocol is RS232. If the feature is I2C; one should know about the I2C protocol.

Once the feature is familiar, next step is to refer the PIC data-sheet and resources on how to implement it in firmware. As for demonstrative purposes, this blog will continue with UART implementation.

Download the latest data-sheet from MicroChip official website and browse to the table of content. It consists of a set of features supported by the micro-controller implemented in the PSLab device. Find the entry related to the feature being implemented. In this case it will be Universal Asynchronous Receiver Transmitter (UART).

Each feature will contain a description following this format explaining what are the options it support and its constraints.

One must be aware of the fact that not every pin in the micro-controller can be used for any feature as he desires. The “PINOUT I/O DESCRIPTIONS” section in the data-sheet explains which pins are capable of the feature being implemented. According to these details, the pins should be initiated as Input/Output pins as well as Digital/Analog pins.

The next step is to refer to the control registers related to the feature. They are all mentioned in the data-sheet under the specific feature. There are some notations available in this section which resembles something like the following;

PDSEL<1:0>: Parity and Data Selection bits
11 = 9-bit data, no parity
10 = 8-bit data, odd parity
01 = 8-bit data, even parity
00 = 8-bit data, no parity

 

This represents a register with two bits. By setting either 11 or 10 or 01 or 00; different implementations can be achieved.

In PSLab firmware this is implemented as;

U1MODEbits.PDSEL = 0;

 

which implements UART feature with 8-bits stream having no parity bits for error correction.

In UART feature implemented in PSLab device, receiving bit stream is fetched by reading the register values in U1STAbits.URXDA and data is transmitted using U1TXREG. All these registers are mentioned in the control registers section in the feature.

Resources:

Continue Reading

Performing Fourier Transforms in the PSLab Android App

Oscilloscope is one of the key features of PSLab. When periodic signals such as sine waves are read by the Oscilloscope, curve fitting functions are used to construct a curve that has the best fit to a series of data points. In PSLab, the sine curve fitting involves the Fourier Transforms. FFT (short for “Fast Fourier Transform”) is nothing more than a curve-fit of sines and cosines to some given data. In order to understand the implementation of Fourier Transforms in PSLab Android App let’s first have a look at the Fourier transform equations.

The first equation here is the Forward Fourier transform. It converts the function of time (t) into the function of frequency (ω).
The second equation is Inverse Fourier transform. It does the opposite to first equation ie. it converts the function of frequency (ω) into the function of time (t).

So, first I will answer what is transform?
It is the mapping between two different sets of domains. In this case, the information is changed from the time domain to frequency domain. The data in these domains look different but represent the same information. A transform will get you from one representation to another.

Fourier transforms converts between the time domain f(t) and the frequency domain F(ω).
Performing Fourier Transforms in Android
Let’s perform Forward Fourier transform. This means it converts the function of time (t) into the function of frequency (ω). We will use Apache Maths Commons to perform Fourier transforms. Since we have finite input data set we will calculate Discrete Fourier Transform (DFT).
The algorithm which is being used here is Fast Fourier Transform (FFT) which is the best algorithm to calculate Fourier transforms.

FastFourierTransformer fastFourierTransformer = 
      new FastFourierTransformer(DftNormalization.STANDARD);

Here we are creating an instance of FastFourierTransformer which passed STANDARD normalization convention to its constructor. Normalization other than STANDARD is UNITARY.
Complex complex[] = fastFourierTransformer.transform(input, TransformType.FORWARD);

Here, input array and TransformType. FORWARD is also passed to transform method. Input is an array of data representing time whereas TransformType. FORWARD defines the type of Fourier transform that should be performed ie. forward or inverse.

Complex complex[] = fastFourierTransformer.transform(input, TransformType.FORWARD);

The output will be an array of complex number. Each data point will be represented like the following graph in the complex plane.

Suppose the amplitude of the data point given in above graph is 1 and phase shift is 45°. So, solving this we will get 2/√2 as both real and imaginary components. Therefore, F (ω) would be 1/√2 + (1/√2) i.
Dealing with Complex numbers in Java
A complex number has both real and imaginary part. Using Apache Maths Commons we can use Complex to represent a complex number.

Complex number = new Complex(1,2);
System.out.println(number);
Output
1.0 + 2.0i

We can also get real and imaginary parts separately of Complex numbers.

System.out.println(number.getReal());
System.out.println(number.getImaginary());
Output
1.0
2.0

The array of the Complex numbers can be implemented like the following

Complex[] number;
Complex c1 = new Complex(1, 2);
Complex c2 = new Complex(3, 4);
number = new Complex[]{c1, c2};
System.out.println(Arrays.toString(number));
System.out.println(number[1]);
Output

[(1.0, 2.0), (3.0, 4.0)]
(3.0, 4.0)

Resources

Continue Reading

Creating Sensor Libraries in PSLab

The I2C bus of the PSLab enables access to a whole range of sensors capable of measuring parameters ranging from light intensity, humidity, and temperature, to acceleration, passive infrared, and magnetism.

Support for each sensor in the communication library is implemented as a small Python library that depends in the I2C communication module for PSLab.

However, most sensors have capabilities that are not just limited to data readouts, but also enable various configuration options.

This blog post explains how a common format followed across the sensor libraries enables the graphical utilities such as data loggers and control panels to dynamically create widgets pertaining to the various configuration options.

The following variables and methods must be present in each sensor file in order to enable the graphical utilities to correctly function:

Name: A generic name for the sensor to be shown in menus and such. e.g. ‘Altimeter BMP180’

GetRaw(): A function that returns a list of values read from the sensor. The values may have been directly read from the sensor, or derived based on some parameters/equations.

For example, the BMP180 altitude sensor is actually a pressure and temperature sensor. The altitude value is derived from these two using an equation. Therefore, the getRaw function for the BMP180 returns a list of three values, viz, [temperature, pressure, altitude]

NUMPLOTS: A constant that stores the number of available dataPoints in the list returned by the getRaw function. This enables the graphical utilities to create required number of traces . In case of the BMP180, it is 3

PLOTNAMES: A list of text strings to be displayed in the plot legend . For the BMP180, the following list is defined : [‘Temperature’, ‘Pressure’, ‘Altitude’]

params: A dictionary that stores the function names for configuring various features of the sensor, and options that can be passed to the function. For example, for the BMP180 altimeter, and oversampling parameter is available, and can take values 0,1,2 and 3 . Therefore, params = {‘setOversampling’: [0, 1, 2, 3]}

The Sensor data Logger application uses this dictionary to auto-generate menus with the ‘key’ as the name , and corresponding ‘values’ as a submenu . When the user opens a menu and clicks on a ‘value’ , the ‘value’ is passed to a function whose name is the corresponding key , and which must be defined in the sensor’s class.

When the above are defined, menus and plots are automatically generated, and saves considerable time and effort for graphical utility development since no sensor specific code needs to be added on that front.

The following Params dictionary defined in the class of MPU6050 creates a menu as shown in the second image:

self.params = { 'powerUp':['Go'],
'setGyroRange':[250,500,1000,2000],
'setAccelRange':[2,4,8,16],
'KalmanFilter':[.01,.1,1,10,100,1000,10000,'OFF']
}

As shown in the image , when the user clicks on ‘8’ , MPU6050.setAccelRange(8) is executed.

Improving the flexibility of the auto-generated menus

The above approach is a little limited, since only a fixed set of values can be used for configuration options, and there may be cases where a flexible input is required.

This is the case with the Kalman filter option, where the user may want to provide the intensity of the filter as a decimal value. Therefore we shall implement a more flexible route for the params dictionary, and allow the value to certains keys to be objects other than lists.

Functions with user defined variable inputs are defined as Spinbox/QDoubleSpinBox.

KalmanFilter is defined in the following entry in Params:

‘KalmanFilter’:{‘dataType’:’double’,’min’:0,’max’:1000,’prefix’:’value: ‘}

Screenshot of the improved UI with MPU6050.

In this instance, the user can input a custom value, and the KalmanFilter function is called with the same.

Additional Reading:

[1]: Using sensors with PSLab Android

[2]: Analyzing sensor data with PSLab android
[3]: YouTube video to understand analysis of data from MPU6050 with Arduino – https://www.youtube.com/watch?v=taZHl4Mr-Pk

Continue Reading

Create a Distance Sensor using PSLab

PSLab device is a small lab which supports a ton of features. Among its many features, integrating a distance measuring sensor like HC SR04 sonar sensor into it is one of them. This blog post will bring out the basic concepts behind a sonar sensor available in the current market, how it measures distance and how it is implemented in the PSLab device.

A sonar sensor uses a sound wave with a very high frequency. These waves are called ultrasonic waves. They cannot be heard by the naked ear. Human ear can only hear frequencies from 20 Hz up to 20 kHz. Generally HC SR04 sensors use a wave with frequency as high as 40 kHz so this makes sense. The basic principal behind the sensor is the reflectance property of sound. Time is calculated from the transmission time up to the time receiving the reflected sound wave. Then using general moment equation S = ut; with the use of speed of sound, the distance can be measured.

The figure shows a HC SR04 ultrasound sensor. They are quiet famous in the electronic field; especially among hobbyists in making simple robots and DIY projects. They can be easily configured to measure distance from the sensor up to 400 cm with a measuring angle of 15 degrees. This angular measurement comes into action with the fact that sound travels through a medium in a spherical nature. This sensor will not give accurate measurements when used for scenarios like measuring distance to very thin objects as they reflect sound poorly or there will not be any reflectance at all.

There are four pins in the HC SR04 sonar sensor. Corner pins in the two sides are for powering up the Sonar sensor. The two pins named ECHO and TRIG pins are the important pins in this context. When the TRIG pin (Trigger for short) is excited with a set of 8 square pulses at a rate of 40 kHz, the ECHO pin will reach to logic HIGH state which is the supply voltage (+5 V). When the transmitted sound wave is reflected back to the sensor, this high state of the ECHO pin will shift to logic LOW state. If a timer is turned on when the ECHO pin goes to logic HIGH state, we can measure how long it was taken for the sound beam to return to the sensor by turning off the timer when the ECHO pin goes to logic LOW state.

Having described the general implementation of a sonar sensor; a similar implementation is available in PSLab device. As mentioned earlier, TRIG pin requires a triggering pulse of 8 set of square waves at 40 kHz. This is achieved in PSLab using SQR pulse generating pins. The time is measured from the transmitting point until the receiving point to evaluate the distance. The real distance to the obstacle in front of the sensor can be calculated using following steps;

  1. Measure total round trip time of the sound beam. Take it as t
  2. Calculate the time taken for the beam to travel from sensor to the obstacle. It will be t/2
  3. Use motion equation S = ut to calculate the actual distance taking u = speed of sound in air. Substituting the time value calculated in step 2 to t, S will produce the distance

Resources:

Continue Reading

Communication by pySerial python module in PSLab

In the PSLab Desktop App we use Python for communication between the PC and PSLab device. The PSLab device is connected to PC via USB cable. The power for the hardware device is provided by the host through USB which in this case is a PC. We need well structured methods to establish communication between PC and PSLab device and this is where pySerial module comes in. We will discuss how to communicate efficiently from PC to a device like PSLab itself using pySerial module.

How to read and write data back to PSLab device?

pySerial is a python module which is used to communicate serially with microcontroller devices like Arduino, RaspBerry Pi, PSLab (Pocket Science Lab), etc. Serial data transfer is easier using this module, you just need to open a port and obtain serial object, which provides useful and powerful functionality. Users can send string (which is an array of bytes) or any other data type all data types can be expressed as byte string using struct module in python, read a specific number of bytes or read till some specific character like ‘\n’ is encountered. We are using this module to create custom read and write functions.

How to Install pySerial and obtain serial object for communication?

You can install pySerial using pip by following command

pip install pyserial

Once it’s installed we can now import it in our python script for use.

Obtain Serial Object

In Linux

>>> import serial
>>> ser = serial.Serial(‘/dev/ttyUSB0’)

In Windows

>>> ser = serial.Serial()
>>> ser.baudrate = 19200
>>> ser.port = ‘COM1’

Or

>>> ser = serial.Serial(‘COM1’, 19200)

You can specify other properties like timeout, stopbits, etc to Serial constructor.

Complete list of parameters is available here. Now this “ser” is an object of Serial class that provides all the functionalities through its interface. In PSLab we obtain a serial object and implement custom methods to handle communication which isn’t directly provided by pySerial, for example if we need to implement a function to get the version of the PSLab device connected. Inside the version read function we need to send some bytes to the device in order to obtain the version string from device as a byte response.

What goes under the hood?

We send some sequence of bytes to PSLab device, every sequence of bytes corresponds to a unique function which is already written in device’s firmware. Device recognises the function and responses accordingly.

Let’s look at code to understand it better.

ser.write(struct.Struct(‘B’).pack(11))  #  Sends 11 as byte string
ser.write(struct.Struct(‘B’).pack(5))   #  Sends 5 as bytes string
x = ser.readline()                      #  Reads bytes until ‘\n’ is encountered   

To understand packing and unpacking using struct module, you can have a read at my other blog post Packing And Unpacking Data in JAVA in which I discussed packing and unpacking of data as byte strings and touched a bit on How it’s done in Python.  

You can specify how many bytes you want to read like shown in code below, which is showing and example for 100 bytes :

x = ser.read(100)

After your communication is complete you can simply close the port by:

ser.close()

Based on these basic interface methods more complex functions can be written to handle your specific needs. More details one how to implement custom methods is available at python-communication-library of PSLab which uses pySerial for communication between Client and PSLab device.

An example of custom read function is suppose I want to write a function to read an int from the device. int is of 2 bytes as firmware is written in C, so we read 2 bytes from device and unpack them in client side i.e on PC. For more such custom functions refer packet_handler.py of PSLab python communication library.

def getInt(self):
      “””
      reads two bytes from the serial port and
      returns an integer after combining them
      “””
      ss = ser.read(2)  # reading 2 bytes from serial object
      try:
          if len(ss) == 2:
              return CP.ShortInt.unpack(ss)[0]  # unpacking bytes to make int
      except Exception as ex:
          self.raiseException(ex, “Communication Error , Function : get_Int”)

Resources

Continue Reading

Environment Monitoring with PSLab

In this post, we shall explore the working principle and output signals of particulate matter sensors, and explore how the PSLab can be used as a data acquisition device for these.

Working Principle

A commonly used technique employed by particulate matter sensors is to study the diffraction of light by dust particles, and estimate the concentration based on a parameter termed the ‘occupancy factor’. The following image illustrates how the most elementary particle sensors work using a photogate, and a small heating element to ensure continuous air flow by convection.

Occupancy Rate

Each time a dust particle of aerodynamic diameters 2.5um passes through the lit area, a phenomenon called Mie scattering which defines scattering of an electromagnetic plane wave by a homogenous sphere of diameter comparable to the wavelength of incident light, results in a photo-signal to be detected by the photosensor.  In more accurate dust sensors, a single wavelength source with a high quality factor such as a laser is used instead of LEDs which typically have broader spectra.

The signal output from the photosensor is in the form of intermittent digital pulses whenever a particle is detected. The occupancy ratio can be determined by measuring the sum total of time when a positive signal was output from the sensor to the total averaging time. The readings can be taken over a fairly long amount of time such as 30 seconds in order to get a more accurate representation of the occupancy ratio.

Using the Logic analyzer to capture and interpret signals

The PSLab has a built-in logic analyzer that can acquire data signals up to 67 seconds long at its highest sampling rate, and this period is more than sufficient to record and interpret a dataset from a dust sensor. An inexpensive dust sensor, DSM501A was chosen for the readings, and the following results were obtained

Dust sensor readings from an indoor, climate controlled environment. After the 100 second mark, the windows were opened to expose the sensor to the outdoor environment.

A short averaging time has resulted in large fluctuations in the readings, and therefore it is important to maintain longer averaging times for stable measurements.

Recording data with a python script instead of the app

The output of the dust sensor must be connected to ID1 of the PSLab, and both devices must share a common ground which is a prerequisite for exchange of DC signals. All that is required is to start the logic analyzer in single channel mode, wait for a specified averging time, and interpret the acquired data

Record_dust_sensor.py

from PSL import sciencelab   #import the required library
import time
import numpy as np
I = sciencelab.connect()           #Create the instance
I.start_one_channel_LA(channel='ID1',channel_mode=1,trigger_mode=0)  #record all level changes
time.sleep(30)   #Wait for 30 seconds while the PSLab gathers data from the dust sensor
a,_,_,_,e =I.get_LA_initial_states()      #read the status of the logic analyzer
raw_data =I.fetch_long_data_from_LA(a,1)  #fetch number of samples available in chan #1
I.dchans[0].load_data(e,raw_data)  
stamps =I.dchans[0].timestamps    #Obtain a copy of the timestamps
if len(stamps)>2:   #If more than two timestamps are available (At least one dust particle was detected
		if not self.I.dchans[0].initial_state:   #Ensure the starting position of timestamps
			stamps = stamps[1:] - stamps[0]   # is in the LOW state
	diff = np.diff(stamps)   #create an array of individual time gaps between successive level changes


	lows = diff[::2]      #Array of time durations when a particle was not present
	highs = diff[1::2]    #Array of time durations when a particle was present
	low_occupancy = 100*sum(lows)/stamps[-1] #Occupancy ratio
print (low_occupancy) # datasheets of individual dust sensors also provide a mathematical
                      #equation to interpret the occupancy ratio as concentration of
				#particulate matter

Further Reading, and application notes:

[1] LED based  dust Sensor application note

Continue Reading
Close Menu