App Inventor Chart Components: Project Summary
In the previous post on the App Inventor Chart Components project, I have covered data importing from Files, as well as from the Web. Previously, we have also seen data importing from TinyDB and CloudDB components, and we have also seen the concept of Observable Data Sources throughout the two posts. This post will focus on the final type of Data Sources – Real Time Data Sources.
Firstly, let’s recall the hierarchy of Chart Data Sources:
Note how the Real Time Chart Data Source interface directly extends from the Observable Chart Data Source interface. This has been done due to the following observations about Real Time Chart Data Sources:
As we can see, the Real Time Chart Data Source and the Observable Chart Data Source interfaces are very similar. The key difference between the two interfaces is that the Observable Chart Data Source changes its data, while the Real Time Chart Data Source keeps sending new data (and does not otherwise store the old data)
The RealTimeChartDataSource interface, on it’s own, is very simple:
In reality, the interface is needed mainly only to differentiate Real Time Chart Data Sources from the Observable Chart Data Sources, therefore it does not come with any extra methods.
Chart Data components have a corresponding interface to listen for Real Time Chart Data Source events:
The interface is very similar to the ChartDataSourceChangeListener seen before, except that the method name differs, as it is applied for a different case. In order to not make the interface for receiving ObservableChartDdataSource eveents even more complex, a separate interface has been made to handle value receiving events.
When thinking about real time sources, the first, and easiest way to apply and test the defined concepts were using the pre-existing Android Sensor components which require minimal setup.
Now we are going to look at adapting a few Sensor components to function as Real Time Chart Data Sources.
The first sensor that I went around to implementing as a Real TIme Data Source was the Accelerometer Sensor component.
For context, the AccelerometerSensor produces and updates the following values:
With these values in mind, and the fact that we would like to plot different values (and perhaps even all of them via multiple Data Series), we then define the key values as follows:
The first step is to actually implement the RealTimeChartDataSource interface in the existing AccelerometerSensor:
The key is a String (X, Y or Z), and the value is a float, since the values produced by the sensor are numeric.
Next, in the class itself, we implement the methods required to observe the AccelerometerSensor:
The code is highly similar to the code we have seen before for the other Observable Chart Data Sources, and handles simple, basic functionality of adding, removing and informing Data Source Observers of new data. Note that the onReceiveValue method is called on Data Components in the notifyDataObservers method.
Next, we implement the getDataValue method to return the appropriate values:
Here we make use of the defined key values for each dimension, and return the corresponding value.
Finally, in order to send the data to the Chart Data component, we add the following lines to the AccelerationChanged Event:
We essentially inform all Data Observers with the value changes of all 3 values, with the corresponding key. It is then up to the Chart Data component to interpret the value.
With this implementation in place, we have essentially adapted the AccelerometerSensor to be a fully compatible real time Chart Data Source.
Now, let’s take a look at another sensor which was also made to be Chart Data compatible.
The Pedometer is a sensor which keeps track of the number of steps taken, and the total distance travelled.
The Pedometer, in essence, has the same characteristics as the Accelerometer Sensor as a Real Time Chart Data Source – the key values are Strings, the data values are Floats, and the observing process is the same. The only difference is in the values, and the events which trigger the data updating.
The key values are summarized by the getDataValue method:
With regards to data value updating, let us consider the two Events which update the step count:
The only lines that were added were the notifyDataObservers method calls in order to notify all the observers of data updating. With this implementation, we have fully adapted the Peodmeter to act as a real time Chart Data Source.
Let’s take a look at another example of a sensor implementation to act as a real time Chart Data Source – the LocationSensor.
The sensor updates the longitude, latitude, altitude and the speed observed by the device.
The getDataValue summarizes the key values in use for the Sensor:
In order to send the data to the observers, the following 4 method calls are added to the LocationChanged event, which is responsible for updating the parameters:
With this implementation, we have made the LocationSensor to function as a fully observable real time Chart Data Source.
Amongst the presented Sensors, the following sensors are also supported as Real Time Chart Data Sources:
Since the behavior is very similar to the presented sensors, and only the type of data is different, we will not go into detail on these sensor importing options. As we have seen, the implementation for each Sensor is very similar, and in fact, in the future we could create an abstract class which handles most of the functionality for us and requires minimal changes to create observable sensor components.
After implementing the Android Sensors, another possibility of a Data Source is the BluetoothClient component, which connects the device to a Bluetooth component, and allows data to be read.
An example use case of such a data importing method is connecting an Arduino via Bluetooth to the phone, then sending data from the Arduino via the Bluetooth connection, and then plotting the data on a Chart.
The BluetoothClient is a rather interesting case in terms of observing due to the data coming in via an InputStream and the lack of observability of a Bluetooth connection in general.
However, the BluetoothClient API allows to check whether the client is connected, and whether bytes are available to be read.
Due to the lack of observability, the idea is to then poll for data in a fixed rate, constantly checking for data, and sending the data to the observers if data was read.
Moreover, an important thing to note is that data is read as a stream from the Bluetooth connection, meaning that the data comes in as a byte stream. To differentiate the values to use as the imported values, we use a delimiter to detect the end of the value.
To further illustrate this, consider that the Bluetooth connection sends the numbers 1, 2, … and so on in increasing order. As a stream, the data looks as follows:
In fact, there is no logical way to differentiate this data. On the side of the Android implementation, we cannot know whether the intended numbers were 1, 2, 3, …, or if it was the single number 12345678910.
To circumvent this, the BluetoothClient actually allows the use of a delimiter byte which is used to distinguish separate values. Therefore, if on the Bluetooth connection side the data would be sent in the following format:
We could then use the character ** as the delimiter to retrieve the value 1, 2, 3 and so on.
Another point to take care of is importing different data from a single Source. Consider the scenario where we have a single Arduino, and we would like to send different data, and plot them on separate Data Series (e.g. separate lines). Right now, we have the same data for all the Data Sources, and we cannot do much differentiation there, since all of the data is sent via the same stream.
However, we can introduce prefixes to mark the data. For example, consider the situation where we would like to send and receive both humidity and temperature via the Bluetooth connection. We could then send the data as follows:
Here, we use two prefixes: t: for temperature, and h: for humidity (as well as the delimiter \n). If we specify the prefix to read data from for the Data Series, we can then effectively feed different data to different Data Sources, all with a single Bluetooth device.
The idea & process can be summarized by the following illustration:
Let us now take a look at the implementation itself.
The first step is to implement the RealTimeChartDataSource interface for the BluetoothClient:
The interesting part here is that both the key and the value are Strings. The reason this choice was made is because of an implementation implication. Consider the scenario where we would like to extract a Data value from a String which contains both a prefix, and a value. Now, suppose we use a numeric prefix, for instance the number 1. Then we receive the following data:
The real value is, in fact, 2, while the prefix is 1. Again, we have no real way of detecting which part is the prefix, and which part is the data without having the prefix beforehand. As it turns out, the prefix is, in fact, a variable in the Chart Data component. Due to this reason, it was decided to leave it up for the Chart Data Source component to interpret the data values. As such, the BluetoothClient does not actually have a key value in it’s internal implementation. The key value is, figuratively, the prefix.
The getDataValue method looks as follows:
This method is used to instantly read data until the delimiter, and then return the result of reading. Data is only read if the BluetoothClient component is connected, and there is at least a single byte that is receivable over Bluetooth.
Note how the key value is not used at all; This is a design implication due to the chosen prefix parsing design.
The metohd used to notify Data Observers is the same as for the Sensor components:
The differences in observing lie in the add and remove methods:
The idea behind the dataPollService related method calls is the fact that the BluetoothClient component might be used outside of Chart related activity. As such, since the Data Poll Service reads in all the data (thus making the Read block calls no longer be able to retrieve the data), the service should only be started if a Data Observer is attached to the BluetoothClient. As such, the Data Polling service is only started whenever the first Data Observer is added, and if, after removal, no more Data Observers exist, the Data Poll Service should be shut down.
The polling itself is implemented as follows:
The idea behind this is to have a background task constantly running which attempts to read Data from the BluetoothClient. Provided that data exists (the client is connected, and at least a single byte can be retrieved), the observers are then notified of the new value.
Let’s consider an example app. Suppose we would like to plot the temperature and the humidity on a single Line Chart.
We will use the prefix t: for the temperature, and h: for humidity. For the delimiter, we will use the newline character \n.
Provided that we connect our BluetoothClient correctly, we would additionally specify the following Designer properties for the Bluetooth Client, and for the Chart Data components:
For the BluetoothClient, we set the DelimiiterByte to 10, which corresponds to the LF newline character (by the ASCII table)
For the Data Series, we use the BluetoothClient as the Source property, and we specify the corresponding prefixes as the DataSourceValues (t: for temperature and h: for humidity)
For the Arduino, we then create the following simple project which makes use of data read in from a DHT sensor:
The simple Arduino code does the following:
The Arduino setup itself looks like this:
In essence, in the setup, the DHT11 temperature and humidity sensor is connected to pin 7 to provide input to it, as well as to the breadboard to power on the sensor.
The HC05 Bluetooth Module is connected to pins 10 and 11 and powered on to allow Bluetooth connections to the Arduino, which in turn allows sending the temperature and sensor data to the device.
This animation illustrates the resulting Line Chart which plots real time data:
Finally, let us take a look at the bulk of the code that handles the actual value receiving in the Chart Data components:
The rather lengthy method consists of two parts – determining whether data can be imported from the retrieved value to the current Chart Data component, and then actually importing the data to the Data Series.
With regards to the BluetoothClient component, the data is only imported if the retrieved value starts with the local DataSourceValue property, which corresponds to the prefix to use for the BluetoothClient Data Source. If that is the case, the actual value is extracted by cutting off the prefix off the retrieved value.
In the usual case (for the Sensors), the data is imported either if the key is null (all Data Observer convention), or if the key equals the local DataSourceValue.
If it the criterion for importing the data is satisfied, the data is then imported on the UI thread. The reason this is done is since the data arrives in real time, the updates come in faster on the UI thread, and tearing is also prevented upon Chart refreshes.
With regards to data importing, the t value is retrieved from the parent Chart component. Since multiple Data Series could all be time series, the Chart component itself is then designated to take care of syncing the t value across the entire Chart. The t value itself represents the current x value to use to represent the time.
A local reference of the t value is kept to pass in as an argument. For now, we will not go into the details of the getSyncedTValue method in this post, since the method is in quite an early stage.
With this implementation in mind, the Chart Data components are now able to accept data from all Real Time Chart Data Sources that were described in the post.
For the curious readers, the pull requests related to implementing Real Time Chart Data Source importing can be found here:
This has been the final post covering the implemented Chart Data Importing options. Future posts will cover the last two Chart types scoped for the project – the Bar Chart, and the Pie Chart.
Stay tuned!
Overview In the last post, I have thoroughly covered the implemented Pie Chart type for the App Inventor Chart components project that I have been working on...
Overview Last post, I have wrapped up the implemented methods for importing data to the Charts for the App Inventor Charts project that I have been working o...
Overview In the previous post on the App Inventor Chart Components project, I have covered data importing from Files, as well as from the Web. Previously, we...
Overview In the previous post on the App Inventor Chart Components project, we have looked at an extensive description of Chart Data Source concepts and real...
Overview It has been a while since the last post due to a highly busy period! A lot of blog posts were postponed, but now a series of posts will follow in th...
Overview Following up on the App Inventor Chart components project, this blog post will focus on a specific workflow aspect that allows to select the Chart t...
Overview With the workflow for the App Inventor Chart components established, the next step is to define the concepts and format of the Data that can be impo...
Problem While working with Charts in Android using the MPAndroidChart library, one inconsistency that I stumbled upon is the fact that all the Charts support...
Overview In the last post, I have previewed the workflow of the Charts components that I am working on for App Inventor. In this post, I will dive into some ...
Overview Following up on the Chart prototypes, this post will focus on previewing the current progress on the Charts components in App Inventor.
Overview In continuation to the previous previous blog post, this post will focus on a Line Chart Component prototype in App Inventor.
Overview During the initial steps of the project to develop Chart Components for App Inventor, the major focus was on the design of the components such that ...
As the community bonding period is nearing to an end and the coding period is about to begin, I would like give a status update on what happened in the last ...
Introduction I am Evaldas Latoškinas, currently a first year Computer Science & Engineering international student in the Netherlands at TU Delft. Origina...