UNESCO Hackathon at FOSSASIA Summit in Singapore

Join the UNESCO Open Data Hackathon at the FOSSASIA Summit, create open source apps and games that tackle climate change, environment and sustainable development challenges, and win awesome prizes! The hackathon takes place from Saturday 24 March to Sunday 25 March 2018 at the Lifelong Learning Institute in Singapore.

We are specifically interested in applications and games that set an example for others who could replicate solutions in other countries, and in particular in the Mekong countries, to tackle the sustainable development challenges. It is our goal to engage the developer community to develop innovative applications in open source by leveraging the open data and knowledge available.

We are inviting developers, designers, open source contributors, bloggers, journalists and all FOSSASIA community members to be part of the UNESCO Hackathon. We are especially encouraging applications from the Mekong region to join the contest. The hackathon is open for all and awesome prizes are waiting for you!

For participants from outside of Singapore we have the possibility to host them in a Singapore hostel. Please apply here. The number is limited. UNESCO encourages the application of women and girls.

How do I sign up?

1. Get your ticket to the Event on eventyay.com.

2. Sign up on Devpost.

3. Join the Gitter channel at https://gitter.im/fossasia/hackathon (requires login with Github).

4. Find team members and create your team preferably at least 3 members and maximum 5 contributors. You are also welcome to sign up and then wait until the Presentation of Ideas on Saturday before deciding to join a team, however we’d encourage you to form/join a team in advance if you already have an idea that you’d like to work on.

5. Join the event at the Lifelong Learning Institute on Saturday, March 24 at the opening at 2.00 pm until 10.00 pm and on Sunday, March 25 from 9.00 am until 5.00 pm.

Visit the website at unesco.sciencehack.asia and stay connected, join the event on Facebook and Meetup and follow FOSSASIA on Twitter.

UNESCO Hackathon Schedule

Hackathon Opening: March 24, 2018

12.00 Registration Opens
14:00 Opening
14.10 Intro of Background, Rules and Prizes
14:20 Presentation of Ideas, Teams and Team Building Activities
15:00 Begin of Hacking Activities
19.30 Dinner
22:00 Closing of Space

Hackday: March 25, 2018

08:00 – 09:00 Breakfast
09:00 – 13:00 Hack Activities Continue
13:00 – 13:30 Lunch
13:30 – 15:00 Hacking Continues
14:00 Submission Form Closes
15:15 – 16:00 Presentation of Outcome
16:00 Judges Withdraw for Consultation
16:30 Award Announcement and Ceremony
17:00 Summit Closing

Location/Venue

Lifelong Learning Institute

Address: 11 Eunos Road 8, Singapore 408601

Prizes

Prizes are awarded for three teams, and each team prize with a value of 1000 SGD. Win cool gear, hardware, raspis, Arduinos and more!

Project Submission Requirements

For the expected outcome of the hack, the applications or games shall be open source and use open data to tackle the climate change, environment and sustainable development challenges.

They shall address one or several of the following requirements:

  1. Respond to pressing environmental challenges at local, national or regional levels in Asia

  2. Enable the visualization of data in an innovative and/or easy-to-understand way

  3. Mobilize and create engagement of variety of stakeholders and sectors in society on climate change, environment and sustainable development

  4. Gender-sensitive prototype, recognizing or encouraging women’s participation in sustainable development

Functioning App

An important point is, is the prototype or showcase functioning? We prefer real code and design implementations over mockups.

What to enter

Please submit a link to the app, a Github repo link and a short presentation as a download or on Google drive (ensure it is set to public sharing). You can also share anything else to demonstrate your work and let us test it.

  • Video: The platform accepts links to YouTube, Vimeo or Youku. If you like you can post a short video to demonstrate your work.

  • File Upload: There is also an option to upload a file. The platform allows submitters to upload one file, though they can combine files into a single ZIP file.

  • Other: The platform requires contestants to enter an entry name and description. Please also accept the the conditions of the contest including sharing your work under certified Open Source license.

Platform

Share information about what operating systems or devices can your hack run on.

Ressources

Include information about API, SDK, or data set, that are required to run the app.

New vs. Existing

Any work done need to be new for the competition. Existing apps are not eligible. However the specific details what is acceptable and what is not will be determined by the jury. For example existing apps that have been modified substantially and include entirely new functionality would still be eligible.

Submission Rights & Display

The submissions should be Open Source and licensed under a compliant Open Source/Free Software license. They should be upload to a Github repository.

We also request the right to use the winners’ names and work to promote the competition and hackathons in future.

Links

UNESCO Hackathon: https://unesco.sciencehack.asia

FOSSASIA Summit: https://2018.fossasia.org

Tickets: https://eventyay.com/e/db15e7db/

Project Signup: https://fossasia-unesco.devpost.com

Facebook: https://www.facebook.com/events/139329623548116/

Meetup: https://www.meetup.com/FOSSASIA-Singapore-Open-Technology-Meetup/events/247899257/

FOSSASIA: https://twitter.com/fossasia

List of Open Data Resources in Asia

Data portals across Asia: http://dataportals.org
China: http://opendatachina.com
Singapore http://data.gov.sg
Indonesia: https://petabencana.id/map/jakarta
Cambodia: https://opendevelopmentcambodia.net
Thailand https://data.go.th, http://catalog.opendata.in.th
Vietnam: https://vietnam.opendevelopmentmekong.net/data/
World Bank: https://data.worldbank.org
India http://data.gov.in

Filling Audio Buffer to Generate Waves in the PSLab Android App

The PSLab Android App works as an oscilloscope and a wave generator using the audio jack of the Android device. The implementation of the oscilloscope in the Android device using the in-built mic has been discussed in the blog post “Using the Audio Jack to make an Oscilloscope in the PSLab Android App” and the same has been discussed in the context of wave generator in the blog post “Implement Wave Generation Functionality in the PSLab Android App”. This post is a continuation of the post related to the implementation of wave generation functionality in the PSLab Android App. In this post, the subject matter of discussion is the way to fill the audio buffer so that the resulting wave generated is either a Sine Wave, a Square Wave or a Sawtooth Wave. The resultant audio buffer would be played using the AudioTrack API of Android to generate the corresponding wave. The waves we are trying to generate are periodic waves.

Periodic Wave: A wave whose displacement has a periodic variation with respect to time or distance, or both.

Thus, the problem reduces to generating a pulse which will constitute a single time period of the wave. Suppose we want to generate a sine wave; if we generate a continuous stream of pulses as illustrated in the image below, we would get a continuous sine wave. This is the main concept that we shall try to implement using code.

Initialise AudioTrack Object

AudioTrack object is initialised using the following parameters:

  • STREAM TYPE: Type of stream like STREAM_SYSTEM, STREAM_MUSIC, STREAM_RING, etc. For wave generation purposes we are using stream music. Every stream has its own maximum and minimum volume level.  
  • SAMPLING RATE: It is the rate at which the source samples the audio signal.
  • BUFFER SIZE IN BYTES: Total size of the internal buffer in bytes from where the audio data is read for playback.
  • MODES: There are two modes-
    • MODE_STATIC: Audio data is transferred from Java to the native layer only once before the audio starts playing.
    • MODE_STREAM: Audio data is streamed from Java to the native layer as audio is being played.

getMinBufferSize() returns the estimated minimum buffer size required for an AudioTrack object to be created in the MODE_STREAM mode.

minTrackBufferSize = AudioTrack.getMinBufferSize(SAMPLING_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(
       AudioManager.STREAM_MUSIC,
       SAMPLING_RATE,
       AudioFormat.CHANNEL_OUT_MONO,
       AudioFormat.ENCODING_PCM_16BIT,
       minTrackBufferSize,
       AudioTrack.MODE_STREAM);

Fill Audio Buffer to Generate Sine Wave

Depending on the values in the audio buffer, the wave is generated by the AudioTrack object. Therefore, to generate a specific kind of wave, we need to fill the audio buffer with some specific values. The values are governed by the wave equation of the signal that we want to generate.

public short[] createBuffer(int frequency) {
   short[] buffer = new short[minTrackBufferSize];
   double f = frequency;
   double q = 0;
   double level = 16384;
   final double K = 2.0 * Math.PI / SAMPLING_RATE;

   for (int i = 0; i < minTrackBufferSize; i++) {
         f += (frequency - f) / 4096.0;
         q += (q < Math.PI) ? f * K : (f * K) - (2.0 * Math.PI);
         buffer[i] = (short) Math.round(Math.sin(q));
   }
   return buffer;
}

Fill Audio Buffer to Generate Square Wave

To generate a square wave, let’s assume the time period to be t units. So, we need the amplitude to be equal to A for t/2 units and -A for the next t/2 units. Repeating this pulse continuously, we will get a square wave.

buffer[i] = (short) ((q > 0.0) ? 1 : -1);

Fill Audio Buffer to Generate Sawtooth Wave

Ramp signals increases linearly with time. A Ramp pulse has been illustrated in the image below:

We need repeated ramp pulses to generate a continuous sawtooth wave.

buffer[i] = (short) Math.round((q / Math.PI));

Finally, when the audio buffer is generated, write it to the audio sink for playback using write() method exposed by the AudioTrack object.

audioTrack.write(buffer, 0, buffer.length);

Resources

Export Sensor Data from the PSLab Android App

The PSLab Android App allows users to log data from the sensors connected to the PSLab hardware device. Sensor Data is stored locally but can be exported in various formats. Currently the app supports exporting data in .txt and .csv (comma-separated values) format. Exported data can be used by other users or scientists to study or analyze the data. Data can also be used by other softwares like Python, GNU octave, Matlab to further process it or visualise it in 3D. In this post, we will discuss how to export the locally stored realm data in .txt or .csv format. We will take the data of MPU6050 sensor as an example for understanding how locally logged data is exported.

Query Local Realm Data

We have attached a long click listener to sensor list view that detects which list item is selected. Clicking any sensor from sensor list for slightly longer than usual would result in a dialog popping up with the option to

  • Export Data: Results in exporting data in a format which is selected in App settings
  • Share Data: Shares sensor data with other users or on social media (yet to be implemented)
Source: PSLab Android App

As soon as the Export Data option is selected from the dialog, sensor data of the corresponding sensor is queried. The data model of the sensor and how it’s saved in the local realm database is discussed in the post Sensor Data Logging in the PSLab Android App.

RealmResults<DataMPU6050> results = realm.where(DataMPU6050.class).findAll();

Once we get the required data, we need to write it in .txt or .csv format depending on what the user has selected as a preference in App Settings.

Getting User Preference from App Settings

The format in which the sensor data should be exported is presented to the user as a preference in App Settings. Currently the app supports two formats .txt and .csv.

Source: PSLab Android App
private String format;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
String formatValue = preferences.getString("export_data_format_list", "0");
if ("0".equals(formatValue))
   format = "txt";
else
   format = "csv";

Export Data in .txt Format

To export the sensor data in .txt format, we need to create a .txt file in the external storage. folder variable is a path to PSLab Android folder in the external storage. If the folder doesn’t exist, it will be created.

File folder = new File(Environment.getExternalStorageDirectory() + File.separator + "PSLab Android");

After getting reference of the app folder in the external storage, we would create a text file in the PSLab Android folder. As soon as the text file is created, we initialize the FileOutputStream object to write data into the text file. The sensor data that was queried in the previous section is written into the text file just created. Finally after the complete sensor data is written, the stream is closed by stream.close() method.

FileOutputStream stream = null;
File file = new File(folder, "sensorData.txt");
try {
   stream = new FileOutputStream(file);
   for (DataMPU6050 temp : results) {
       stream.write((String.valueOf(temp.getAx()) + " " + temp.getAy() + " " + temp.getAz() + " " +
               temp.getGx() + " " + temp.getGy() + " " + temp.getGz() + " " + temp.getTemperature() + "\n").getBytes());
   }
} catch (IOException e) {
   e.printStackTrace();
} finally {
   try {
       if (stream != null) {
           stream.close();
       }
   } catch (IOException e) {
       e.printStackTrace();
   }
}

Export Data in .csv Format

Writing data in .csv format is similar to that in .txt format. As CSV stands for Comma Separated Values, which means each data value is separated by “,” (comma). It is similar to an excel sheet. The first row consists of labels that denote the type of value in that particular column. The other rows consist of the sensor data, with each row corresponding to a sample of the sensor data.

File file = new File(folder, "sensorData.csv");
PrintWriter writer;
try {
   writer = new PrintWriter(file);
   StringBuilder stringBuilder = new StringBuilder();
   stringBuilder.append("Ax,Ay,Ax,Gx,Gy,Gz,Temperature\n");
   for (DataMPU6050 temp : results) {
       stringBuilder.append(String.valueOf(temp.getAx()));
       stringBuilder.append(',');
       stringBuilder.append(String.valueOf(temp.getAy()));
       stringBuilder.append(',');
       stringBuilder.append(String.valueOf(temp.getAz()));
       stringBuilder.append(',');
       stringBuilder.append(String.valueOf(temp.getGx()));
       stringBuilder.append(',');
       stringBuilder.append(String.valueOf(temp.getGy()));
       stringBuilder.append(',');
       stringBuilder.append(String.valueOf(temp.getGz()));
       stringBuilder.append(',');
       stringBuilder.append(String.valueOf(temp.getTemperature()));
       stringBuilder.append('\n');
   }
   writer.write(stringBuilder.toString());
   writer.close();
} catch (FileNotFoundException e) {
   e.printStackTrace();
}

Resources

Sensor Data Logging in the PSLab Android App

The PSLab Android App allows users to log data from sensors connected to the PSLab hardware device. The Connected sensors should support I2C, SPI communication protocols to communicate with the PSLab device successfully. The only prerequisite is the additional support for the particular sensor plugin in Android App. The user can log data from various sensors and measure parameters like temperature, humidity, acceleration, magnetic field, etc. These parameters are useful in predicting and monitoring the environment and in performing many experiments.

The support for the sensor plugins was added during the porting python communication library code to Java. In this post,  we will discuss how we logged real time sensor data from the PSLab Hardware Device. We used Realm database to store the sensor data locally. We have taken the MPU6050 sensor as an example to understand the complete process of logging sensor data.

Creating Realm Object for MPU6050 Sensor Data

The MPU6050 sensor gives the acceleration and gyroscope readings along the three axes X, Y and Z. So the data object storing the readings of the mpu sensor have variables to store the acceleration and gyroscope readings along all three axes.

public class DataMPU6050 extends RealmObject {

   private double ax, ay, az;
   private double gx, gy, gz;
   private double temperature;

   public DataMPU6050() {  }

   public DataMPU6050(double ax, double ay, double az, double gx, double gy, double gz, double temperature) {
       this.ax = ax;
       this.ay = ay;
       this.az = az;
       this.gx = gx;
       this.gy = gy;
       this.gz = gz;
       this.temperature = temperature;
   }

  // getter and setter for all variables
}

Creating Runnable to Start/Stop Data Logging

To sample the sensor data at 500ms interval, we created a runnable object and passed it to another thread which would prevent lagging of the UI thread. We can start/stop logging by changing the value of the boolean loggingThreadRunning on button click. TaskMPU6050 is an AsyncTask which reads each sample of sensor data from the PSLab device, it gets executed inside a while loop which is controlled by boolean loggingThreadRunning. Thread.sleep(500) pauses the thread for 500ms, this is also one of the reason to transfer the logging to another thread instead of logging the sensor data in UI thread. If such 500ms delays are incorporated in UI thread, app experience won’t be smooth for the users.

Runnable loggingRunnable = new Runnable() {
   @Override
   public void run() {
       try {
           MPU6050 sensorMPU6050 = new MPU6050(i2c);
           while (loggingThreadRunning) {
               TaskMPU6050 taskMPU6050 = new TaskMPU6050(sensorMPU6050);
               taskMPU6050.execute();
              // use lock object to synchronize threads
               Thread.sleep(500);
           }
       } catch (IOException   InterruptedException e) {
           e.printStackTrace();
       }
   }
};

Sampling of Sensor Data

We created an AsyncTask to read each sample of the sensor data from the PSLab device in the background thread. The getRaw() method read raw values from the sensor and returned an ArrayList containing the acceleration and gyro values. After the values were read successfully, they were updated in the data card in the foreground which was visible to the user. This data card acts as a real-time screen for the user. All the samples read are appended to ArrayList mpu6050DataList, when the user clicks on button Save Data, the collected samples are saved to the local realm database.

private ArrayList<DataMPU6050> mpu6050DataList = new ArrayList<>();

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

   private MPU6050 sensorMPU6050;
   private ArrayList<Double> dataMPU6050 = new ArrayList<>();

   TaskMPU6050(MPU6050 mpu6050) {
       this.sensorMPU6050 = mpu6050;
   }

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

   @Override
   protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       // update data card TextViews with data read.
       DataMPU6050 tempObject = new DataMPU6050(dataMPU6050.get(0), dataMPU6050.get(1), dataMPU6050.get(2),
               dataMPU6050.get(4), dataMPU6050.get(5), dataMPU6050.get(6), dataMPU6050.get(3));
       mpu6050DataList.add(tempObject);
       synchronized (lock) {
           lock.notify();
       }
   }
}
Source: PSLab Android App

There is an option for Start/Stop Logging, clicking on which will change the value of boolean loggingThreadRunning which stops starts/stops the logging thread.

When the Save Data button is clicked, all the samples of sensor data collected from the  PSLab device till that point are saved to the local realm database.

realm.beginTransaction();
for (DataMPU6050 tempObject : mpu6050DataList) {
   realm.copyToRealm(tempObject);
}
realm.commitTransaction();

Data can also be written asynchronously to the local realm database. For other methods to write to a real database refer write section of Realm docs.

Resources

Implement Wave Generation Functionality in The PSLab Android App

The PSLab Android App works as an Oscilloscope using the audio jack of Android device. The implementation for the scope using in-built mic is discussed in the post Using the Audio Jack to make an Oscilloscope in the PSLab Android App. Another application which can be implemented by hacking the audio jack is Wave Generation. We can generate different types of signals on the wires connected to the audio jack using the Android APIs that control the Audio Hardware. In this post, I will discuss about how we can generate wave by using the Android APIs for controlling the audio hardware.

Configuration of Audio Jack for Wave Generation

Simply cut open the wire of a cheap pair of earphones to gain control of its terminals and attach alligator pins by soldering or any other hack(jugaad) that you can think of. After you are done with the tinkering of the earphone jack, it should look something like shown in the image below.

Source: edn.com

If your earphones had mic, it would have an extra wire for mic input. In any general pair of earphones the wire configuration is almost the same as shown in the image below.

Source: flickr

Android APIs for Controlling Audio Hardware

AudioRecord and AudioTrack are the two classes in Android that manages recording and playback respectively. For Wave Generation application we only need AudioTrack class.

Creating an AudioTrack object: We need the following parameters to initialise an AudioTrack object.

STREAM TYPE: Type of stream like STREAM_SYSTEM, STREAM_MUSIC, STREAM_RING, etc. For wave generation purpose we are using stream music. Every stream has its own maximum and minimum volume level.

SAMPLING RATE: it is the rate at which source samples the audio signal.

BUFFER SIZE IN BYTES: total size in bytes of the internal buffer from where the audio data is read for playback.

MODES: There are two modes

  • MODE_STATIC: Audio data is transferred from Java to native layer only once before the audio starts playing.
  • MODE_STREAM: Audio data is streamed from Java to native layer as audio is being played.

getMinBufferSize() returns the estimated minimum buffer size required for an AudioTrack object to be created in the MODE_STREAM mode.

private int minTrackBufferSize;
private static final int SAMPLING_RATE = 44100;
minTrackBufferSize = AudioTrack.getMinBufferSize(SAMPLING_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);

audioTrack = new AudioTrack(
       AudioManager.STREAM_MUSIC,
       SAMPLING_RATE,
       AudioFormat.CHANNEL_OUT_MONO,
       AudioFormat.ENCODING_PCM_16BIT,
       minTrackBufferSize,
       AudioTrack.MODE_STREAM);

Function createBuffer() creates the audio buffer that is played using the audio track object i.e audio track object would write this buffer on playback stream. Function below fills random values in the buffer due to which a random signal is generated. If we want to generate some specific wave like Square Wave, Sine Wave, Triangular Wave, we have to fill the buffer accordingly.

public short[] createBuffer(int frequency) {
   // generating a random buffer for now
   short[] buffer = new short[minTrackBufferSize];
   for (int i = 0; i < minTrackBufferSize; i++) {
       buffer[i] = (short) (random.nextInt(32767) + (-32768));
   }
   return buffer;
}

We created a write() method and passed the audio buffer created in above step as an argument to the method. This method writes audio buffer into audio stream for playback.

public void write(short[] buffer) {
   /* write buffer to audioTrack */
   audioTrack.write(buffer, 0, buffer.length);
}

Amplitude of the signal can be controlled by changing the volume level of the stream on which the buffer is being played. As we are playing the audio in music stream, so STREAM_MUSIC is passed as a parameter to the setStreamVolume() method.

value: value is amplitude level of the stream. Every stream has its different amplitude levels. getStreamMaxVolume(STREAM_TYPE) method is used to find the maximum valid amplitude level of any stream.
flag: this stackoverflow post explain all the flags of the AudioManager class.

AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, value, flag);

Roadmap

We are working on implementing methods to fill audio buffer with specific values such that waves like Sinusoidal wave, Square Wave, Sawtooth Wave can be generated during the playback of the buffer using the AudioTrack object.

Resources

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

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

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

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

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