Feature to generate Config File in PSLab Android application

In this blog, I will explain the feature to generate “Config File” in PSLab Android Application 

What is a Config File?

The main aim of this feature is to make PSLab board a self data logger, which would read user-defined configs from a config file stored on SD card connected to PSLab board and based on instrument, parameters and time interval stored in config file PSLab board would automatically log those values. 

Now as the first step of this feature, an option is added to PSLab Android application, where user can create a config file. User can select an instrument, parameters associated with that instrument and time interval. With this feature, user can easily generate a config file which can later be used by PSLab board for logging.

User Interface

The option to generate a config file is given in the side navigation menu on the main screen. 

(Figure 1: Generate Config file menu)

Once the user selects the “Generate Config File” option, the user will be directed to the following screen where user can create a config file with intended parameters

(Figure 2: Generate Config File UI)

As can be seen in the screenshot above the user can select instruments for which the config file needs to be created from a drop-down menu. User can specify the time interval, for which the data should be logged by the PSLab board. Based on the instrument selected by the user corresponding parameters will be shown at the bottom. User can select whichever parameters are required and click on “CREATE CONFIG FILE” button and a config file will be saved on device local storage. 

A config file for Oscilloscope with 25-sec interval and CH1, CH2 and CH3 parameters would look something like below,

(Figure 3: Sample config File )

Implementation

When a user clicks on Create Config File button, First we check whether the user has provided a time interval, if not a toast message appears to let the user know that time interval is missing. This is done using the following lines of code,

createConfigFileBtn.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
     interval = intervalEditText.getText().toString();
     if (interval.length() == 0) {
        Toast.makeText(CreateConfigActivity.this, getResources().getString(R.string.no_interval_message), Toast.LENGTH_SHORT).show();
                }

Once the user sets the time interval and selects the parameters, the following lines of code generates a string array containing params selected by the user.

ArrayList<String> selectedParamsList = new ArrayList<>();
for (int i = 0; i < paramsListContainer.getChildCount(); i ++) {
    CheckBox checkBox = (CheckBox) paramsListContainer.getChildAt(i);
    if (checkBox.isChecked()) {
       selectedParamsList.add(instrumentParamsList.get(selectedItem)[i]);
    }
}

After we have the list of selected parameters we call the following function to create the config file

private void createConfigFile(ArrayList<String> params) {
        String instrumentName = instrumentsList.get(selectedItem);
        String fileName = "pslab_config.txt";
        String basepath = Environment.getExternalStorageDirectory().getAbsolutePath();

        File baseDirectory = new File(basepath + File.separator + CSVLogger.CSV_DIRECTORY);
        if (!baseDirectory.exists()) {
            try {
                baseDirectory.mkdir();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        File configFile = new File(basepath + File.separator + CSVLogger.CSV_DIRECTORY + File.separator + fileName);
        if (!configFile.exists()) {
            try {
                configFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            FileWriter writer = new FileWriter(configFile);
            writer.write("instrument: " + instrumentName + "\n");
            writer.write("interval: " + interval + " " + intervalUnit + "\n");
            String param = String.join(",", params);
            writer.write("params: " + param);
            writer.flush();
            writer.close();
            CustomSnackBar.showSnackBar(rootView, getString(R.string.file_created_success_message), null, null, Snackbar.LENGTH_SHORT);
        } catch (IOException e) {
            e.printStackTrace();
            CustomSnackBar.showSnackBar(rootView, getString(R.string.file_created_fail_message), null, null, Snackbar.LENGTH_SHORT);
        }

    }

In the first part of this function, we check whether there exists a PSLab directory in the local storage of the device, if not the directory is created. After that, we create a file named “pslab_config.txt”. After that, we use FileWriter to write data to the file. 

In a nutshell with this feature user can create config files easily. The following GIF demonstrated this functionality.

(Figure 4: GIF of the functionality)

References

Tags: PSLab, Android, GSoC 19, Config File, data logger

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
Close Menu