Sensor, process and transparently persist your IoT data with an end-to-end object-oriented system
Mariano Martinez Peck
Posted on July 9, 2019
Let’s suppose a typical IoT / Edge Computing developer/deployment stack:
- One (maybe low-level) language with its own syntax and IDE to read/write data from sensors or anything GPIO-related.
- Another (likely high-level) language / IDE that applies business logic at increasing complexity into the collected data.
- A database.
It’s likely that language 1) and 2) are different. Therefore, you may need different IDEs, developers may need to know several languages, code reusability is not that simple anymore, etc. The languages could even be from different paradigms, such as procedural vs OOP.
Then, you must persist on a database that is likely either relational or NoSQL. In doing so you need to map your objects into either tables or key/value kind of storage. A.k.a impedance mismatch.
Now imagine if you could have one paradigm, a simple language and IDE to rule them all? In an earlier post, I expressed my own thoughts about why Smalltalk language could be a good fit for IoT and Edge Computing.
In this post, I will show exactly that. The setup will be a Raspberry Pi 3B+ running Raspbian and VASmalltalk. The latter will use the pigpio Smalltalk wrapper to fetch temperature using a DS18B20 thermometer sensor, reify those measures as first class objects, apply some business / domain logic and finally transparently persist those objects on a remote GemStone object-oriented database.
BTW, what originated this post was a really nice Smalltalk community-driven effort.
Sensing and processing IoT data
In a previous post you can see how you can use the pigpio
wrapper from Smalltalk. In this case, all we had to do is to connect a DS18B20 thermometer sensor and use the class OneWireDS18B20ThermometerDevice
from our wrapper and just send the message #temperature
. The wires connections are pretty easy as you just need to connect 3 cables to the GPIOs. I will let these details for another post. So we have the sensor part ready.
Liquid error: internal
The next step was to reify the temperature measures and store them in a collection. For that, we (actually, Richard Sargent, from GemTalks Systems Inc.) created this simple model application. It contains a single class:
Object subclass: #RjsTemperatureSensorReading
classInstanceVariableNames: ''
instanceVariableNames: 'whenTaken temperature '
classVariableNames: 'Readings '
poolDictionaries: ''
The list of readings is stored in a class variable named Readings
and the class provides some helper methods to create new instances as well as to record and report them:
Important: Note that this is one of the reasons why I believe Smalltalk could make a big difference with other languages. With the increase of IoT and Edge Computing I think that we will start to run more business logic on the board or the edge. It’s not just about sensing data anymore. You can apply complex business logic and process it right there. The more complex that code becomes, the more I think Smalltalk works well as a target implementation language.
For the sake of making the post easier to understand the “business logic” here is quite simple. But again, it could be as complex as involving some AI (Artificial Intelligence), ML (Machine Learning), or any other domain specific logic.
Setting up VASmalltalk for GemStone
A couple of years ago I wrote a blog post about what GemStone is: an object system with transparent persistence.
Liquid error: internal
There is a GemStone tool called GBS (GemStone Builder for Smalltalk)which allows a client Smalltalk (VASmalltalk in this case) to transparently persists and retrieve objects from a GemStone database.
So the first step is to download GBS and install it into your VASmalltalk image. You can read the documentation of the previous link for that. But it basically means download a .dat
files and then import and load the GBS maps:
The next step is to install the GemStone RPC libraries (ARM-compiled in this case) in the Pi, which allows connecting the client Smalltalk to the remote GemStone over the wire. Again, that’s as simple as going into the GemStone website, and download the Raspberry libs.
Once you download the libs you must then move them to the expected place of VASmalltalk which is usually the root directory:
cp ~/Instantiations/GemStone/gciArmLinuxLibs-3.4.4/*.so ~/Instantiations/Vast92Ecap2_Linux/
Then you must tell GBS which exact libs to use, so you can evaluate this code in Smalltalk:
GbsConfiguration current libraryName: 'libgcirpc-3.4.4-32.so'.
Finally, you must at least add one GemStone session information so that you can try to connect. You can do this from the main menu GemStone
-> Sessions
-> Add
button:
Or…via scripting
If you are lazy like me and you prefer scripting all this, then this is what I do:
"Import"
StsConfigurationMapsBrowser new importAllConfigMapsFrom: '/home/pi/Instantiations/GemStone/GemBuilder5.4.5/gbs5.4.5.dat'.
"Load latest"
(EmConfigurationMap editionsFor: 'GBSGemBuilder') first loadWithRequiredMaps.
" Set libraries"
GbsConfiguration current libraryName: 'libgcirpc-3.4.4-32.so'.
"Add the session to my Gemstone database"
GBSM addParameters: (GbsSessionParameters new
gemStoneName: 'gs_343_gbs';
username: 'XXXX';
password: 'YYYY';
rememberPassword: true;
gemService: '!@XXXX#netldi:50400!gemnetobject';
yourself).
For this post, I will be connecting to a GemStone running remotely in a cloud.
Connecting, browsing and evaluating GemStone code from VASmalltalk
The first thing we wanna try is just to “connect” the local VASmalltalk running on the Pi to the GemStone running in the cloud.
Liquid error: internal
For that, we can use the main menu GemStone
-> Sessions
-> select the session -> Login RPC
button. If it works, you will see the connection listed below.
Now that we are connected, we can play with GemStone…we can open a workspace and evaluate, inspect and display code on it with GemStone
-> Tools
-> New GS Workspace
:
You can even browse GemStone classes, implementors, senders, class references, etc, etc.
Finally, transparent, scalable and object-oriented persistence
Being able to browse and evaluate the code of a remote GemStone from a VASmalltalk running on a Pi is pretty cool. But even better is to really use it as a transparent, scalable, object-oriented database.
Ok….so we can now have domain objects representing temperature measures and the GPIO access to sensor it for real. How do we persist this on GemStone with GBS?
First thing is to connect to a GemStone session as we did before. Then we must “copy” the definition of RjsTemperatureSensorReading
from VASmalltalk to GemStone. For that, we just browse the class, right click, then Create in GS
.
Cool. The class now also exists on GemStone. So…let’s do the final magic:
"
GemStone Set Up:
1) Execute 'RjsTemperatureSensorReading clearReadings.' in VA Smalltalk to initialize the class variable.
2) Go to GBS and open a session into the stone.
3) Use GBS to push the class RjsTemperatureSensorReading to the server (right click -> 'Compile in GS'.
4) Open a GemStone workspace and execute 'RjsTemperatureSensorReading clearReadings.' in GemStone to initialize the class variable.
GemStone -> Tools -> New GS Workspace
5) Commit.
6) Execute below lines
"
| session connector measures |
session := GBSM currentSession.
connector := GbsClassVarConnector
stName: #RjsTemperatureSensorReading
gsName: #RjsTemperatureSensorReading
cvarName: #Readings.
connector updateSTOnConnect.
connector connectInSession: session.
session evaluate: 'RjsTemperatureSensorReading clearReadings.'.
measures := OrderedCollection new.
measures add: TestOneWireDS18B20ThermometerDevice fetchTemperature -> DateTime now.
(Delay forSeconds: 2) wait.
measures add: TestOneWireDS18B20ThermometerDevice fetchTemperature -> DateTime now.
(Delay forSeconds: 2) wait.
measures add: TestOneWireDS18B20ThermometerDevice fetchTemperature -> DateTime now.
measures do: [:each |
(RjsTemperatureSensorReading newTemperature: each key asOf: each value) recordSample.
].
((session evaluate: '
RjsTemperatureSensorReading sampleReport')
copyReplacing: Character cr withObject: Character lf)
inspect.
session commitTransaction.
RjsTemperatureSensorReading sampleReport inspect.
That code basically tells GemStone to map the class variable Readings
from the client Smalltalk to itself. Then I fetch the real temperature (with the sensor), reify a measure object, store the object into the collection, wait 2 seconds, start again. And like that 3 times. Finally, I make both, VASmalltalk and GemStone print a report on the class variable Readings
.
I will now put the sensor on hot water (so that you can see the temperature changing! hahaha) and run above snippet. These are the results:
You can see the 3 measures, at what time they were recorded and what temperature that was (in Celsius).
Note that each report string where generated in different Smalltalks and how the collection was in sync automatically. It’s funny to see that VASmalltalk prints dates as 06/19/2019
while GemStone does 19/06/2019
.
Conclusion
Note that everything explained in this post doesn’t just work on a Raspberry Pi, but on any ARM-based SBC. Actually… on ANY platform that VASmalltalk and GemStone runs. Of course, there are drawbacks and limits too:
- Currently with VASmalltalk we can target SBC that have hardware enough to run Linux.
- The wrapped pigpio library only works on Raspberry Pi… For other platforms we would need to wrap another library.
- As it usually happens with Smalltalk, we don’t have as many libraries as other languages have, like Python.
However, I personally believe that having a fully object-oriented, mature, simple and powerful programming language like Smalltalk for doing IoT is great. Combined with a scalable, transparent, object-oriented persistency accessible from any SBC (Single Board Computer) conforms a unique stack of technologies.
Liquid error: internal
Posted on July 9, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.