Enterprise Resource Planning Blogs by SAP
Get insights and updates about cloud ERP and RISE with SAP, SAP S/4HANA and SAP S/4HANA Cloud, and more enterprise management capabilities with SAP blog posts.
cancel
Showing results for 
Search instead for 
Did you mean: 


Whereas the 1st part of this post dealt about the very technical basics for the GY302 adapter (be it on hardware / sensor side or on software / B1i side), this 2nd part now gets things concrete and make it happen - so hands- and heads-on!

Before we do some kind of development, we need to have the hardware ready to go. That's pretty easy in this case, just connect the sensor to the Raspi using some female - female Dupont  jumper wires as in the diagram below:

Raspi3_GY302_Steckplatine

Be cautious about the proper interconnection of the wires, as otherwise, you erroneously could be deemed as a smoker in your office!

The major part of work here is the software side though! As a starting point, find the complete Javascript adapter code in the attached document gy302 - it's pretty compact, isn't it? - The how-to is the art!

But let's go step by step and have a look to the details of the code!
As already mentioned in the first part of the post, the Javascript code for the ECMA-Adapter just starts execution from the first line on as if it would be e.g. a simple command-prompt level batch file.
Thus, starting at the top, the first section comprises some preparation / initialization on language level:
// DESPITE IT MAY LOOK LIKE, THIS IS ALMOST ENTIRELY JAVA HEREIN !!!
var lux = 0;

// get the thread class (beacuse we need to do small pauses later on)
var Thread = Java.type ("java.lang.Thread");

// Create a Java array of primitive types in Javascript!
var ByteArray = Java.type ("byte[]");

// for later byte conversion to a number
var Byte = Java.type ("java.lang.Byte");

// get the resource-dispenser class from the common part of the B1i Raspi-Adapter
var Pi4JResourceDispenser = Java.type ("com.sap.b1i.xxxpi.common.Pi4JResourceDispenser");

So the first question is: Which language(s)? - Having a look to the line
var lux = 0;

it's still Javascript where a global variable is defined and initialized (this kind of global variables will be accessible / visible in the whole batch, but as well in Java(script) functions / methods invoked from it, as we still will see. But proceeding from there on, our further variables defined so far still are Javascript variables, but already contain Java prone content!
They simply load further on required Java class definitions to variables using the Java.type construct of Nashorn (exactly this construct is not a part of Ecmascript itself nor Java - you won't find it in the JDK or e.g. in the browser!).
As the referenced fully qualified class-names imply, we need some stuff from the plain JDK, but as well our aforementioned Pi4JResourceDispenser.
// grab the I2C bus in order to execute stuff, having an access t/o of 2000mSecs (this is B1i doing the overall coordination...)
// the resource dispenser just only guards/wraps/serializes I2C execution so that no other parties try this at the same time
Pi4JResourceDispenser.executeI2cDevice (performGY302, 2000);

Here, our complete adapter-call goes (we'll zoom in a minute for the details). We're using the Pi4JResourceDispenser to wrap us the whole upcoming I2C-bus communication in a singleton critical section. If all parties (hopefully) use that coordination mechanism, our upcoming I2C communication does not get disturbed - otherwise, the overall result very likely would get to goulash (but not taste well at all).

Now. let's act like the program itself - before proceeding with the overall batch, the function calling the adapter must be invoked / inspected first. The way of this function call itself still contains a lot of Nashorn / Java semantics: When having a closer look to the documentation of Pi4JResourceDispenser.executeI2cDevice (...),
it can be seen that it expects as an argument an implementation of a Java interface (the I2cExecutor), as it is a Java class, and Java classes must behave that way, even if they only would like to get handed over the pointer to a simple function only. Even that it would be possible in Nashorn to define implementations of such interfaces on Javascript level, things get a bit easier in practice due to some convenient shortcut:
In this case, no fully-fledged interface (with potentially multiple functions / methods) is required; therefore, the interface of question also just only defines one method to supply. In such a special case, Nashorn allows to fulfill the requirement of an implementation of such a simplistic interface by just only passing a plain and simple Javascript function only - Nashorn does the necessary plumbing behind the scenes in order to "sell" that treatment to Java. So at the end of the day, it is fine to define and pass a simple Javascript function that in turn accepts one argument (as the defined method in the original interface also does - on that level, we need to be accurate). This function in turn does our true work, as the next part of the code shows up:
// that's the worker function that shortly claims the I2C bus and performs the necessary activities
function performGY302 (i2cbus)
{
// the GY302/30 default-address is 0x23 (this is pi4j acting!)
var device = i2cbus.getDevice (0x23);

// now perform setting the measurement-mode for the sensor: choose the 1 Lux resolution mode
device.write (0x10);

// wait >180 mSec to complete measurement
Thread.sleep (200);

var potatoBuffer = new ByteArray (2);

// now grab the measured value
var cnt = device.read (potatoBuffer, 0, 2);
if (cnt != 2)
scriptIO.throwHardException ("Error on reading the measurement from GY302/30 !!!");

// make sure the single bytes are not interpreted as signed values by Java (if being >127 / high bit set)!
var high = Byte.toUnsignedInt (potatoBuffer [0]);
var low = Byte.toUnsignedInt (potatoBuffer [1]);

// get result out of the 2 bytes, make a number of it and divide by 1.2
lux = Math.floor (((high * 256) + low) / 1.2);

print ("Have measured " + lux + " Lux!");
}

The argument the function gets passed in the real call is of the same type as defined in the original Java interface: In our case, it is a class-instance of the Pi4J library that hosts the concrete I2C-bus accessibility. That way, we've got in yet another Java class even that we did not define / load it explicitly! Such a kind of small bootstrapping and in turn rich outcome of Java types can get pretty common in Java decorated Javascript code.

Before we proceed, it's time to get aware of about how to programmatically access the device (only via I2C in our case). So I2C by itself just only tells us how to do it (and Pi4J helps us to do so), but we now need the information about what exactly to do:
In fact, we need the detail information about how to treat and program the sensor.
Needing that, an important source of information is the datasheet available for EVERY electronic components, as e.g. for our BH1750 light sensor.
I feel responsible to alleviate the sudden discouragement crept over you if you should have clicked to the recent link - that was the hard way to go!

Fortunately, the datasheet for a device only is the (very) last rescue to go for or if the very last few subtle details need to get clarified. In the times of Internet and shared knowledge, there exist rather other sources more easily to understand. Search the Web e.g. for "BH1750 Tutorial", and you'll find the introduction you'll need (skip these repeated over-simple intros that just tell you the two or so buttons to press - they give no true information). A good starting point e.g. is this intro (caution - also this link finally contains the anti-personnel mine!).
For getting closer, you want to (roughly) know how the device works, how it gets electrically connected (if you're as well the person in charge of building the hardware)  and what commands it needs to be used.
For sure, if doing sensor-development, you also want to know about hexadecimal and binary numbers (and their relation to each other...). The programmer's view of the calculator in Windows is a good tool for that purpose.

Often, such intros also have demo-code attached, and you want to mimic it to some extent. The language / environment the demo code is for does not matter (C, Arduino, ...); it very likely won't be Java and for sure won't be B1i. But all code contains the very truth you're after, the way how the device is accessed.
So the demo-code on the mentioned Website unveils (one of) the I2C-address(es) for the device access; that's the information you want for now.
// the GY302/30 default-address is 0x23 (this is pi4j acting!) 
var device = i2cbus.getDevice (0x23);

In turn, coming back to our Javascript function, it first connects via I2C to our sensor device, using (one of) the well known device address(es), here it is hex 0x23. So the info about the address comes out of the info-material, the knowledge how to apply it from Pi4J / B1i. What is returned back can be seen as a kind of session object, being usable to interact with the device now.
// now perform setting the measurement-mode for the sensor: choose the 1 Lux resolution mode 
device.write (0x10);

Next, we write some command to the device that adjusts the requested operation mode: As to be seen out of the intro article (and finally the datasheet on page #5), we choose continuous measurements in High Resolution mode #1 (means, result value is roughly in 1 Lux units). In fact the value of hex 0x10 stands for that.
// wait >180 mSec to complete measurement 
Thread.sleep (200);

As we've learned from the datasheet, we have to wait a bit before retrieving the result so that the measurement can be completed (that's a typical area where the datasheet and everyone in the world has its own opinion about) - so better grant a bit more time than required.
var potatoBuffer = new ByteArray (2);

Now let's get to retrieve the result. Doing that, there comes up an unexpected obstacle - we get back bytes, but finally need a Java number. Not that trivial as we'll see. Therefore, let's first build a memory area being able to hold 2 consecutive bytes. As Javascript (again) is pretty high level, we need to bother Java for doing it for us. But also Java itself distinguishes between high and low levels: For our case, we need a primitive data-type from Java only, but no objects! We've prepared this in our prior initialization section; our Javascript variable from above now really holds two plain vanilla bytes only.
// now grab the measured value 
var cnt = device.read (potatoBuffer, 0, 2);
if (cnt != 2)
scriptIO.throwHardException ("Error on reading the measurement from GY302/30 !!!");

The method-call above now tries to read two bytes from the I2C bus, it's our (raw) measure result! For plausibility, it reflects the count of bytes that really had been read. If there is encountered a mismatch, that's an error and we explicitly throw an exception (using some convenience method of the scriptIO-object).
An exception alternatively also could be thrown using ECMAScript's inbuilt native exception mechanism (one of the few but important things ECMAScript does by itself).
// make sure the single bytes are not interpreted as signed values by Java (if being >127 / high bit set)!
var high = Byte.toUnsignedInt (potatoBuffer [0]);
var low = Byte.toUnsignedInt (potatoBuffer [1]);

// get result out of the 2 bytes, make a number of it and divide by 1.2
lux = Math.floor (((high * 256) + low) / 1.2);

What follows is the assembly of the usable conversion result out of the raw byte data:
As already mentioned, it needs to take care that this conversion goes properly, as Java typically is not able to distinguish between signed and unsigned numbers on a data-type level; therefore, a pretty "high" unsigened number (concretely, a value that has set the highest significant Bit) could be interpreted as a signed one, thereby producing rubbish for the overall value!
That is why those two result bytes, both containing unsigned numeric content, are converted to Java number by using explicitly functions that address this trait.
From that point on (having high and low), "signing-trouble" is not an issue any more. Therefore, the remainder can be done using standard Java(script) arithmetic for calculation. First, it is about to combine the two 8-bit holding numeric values to a completed 16-bit value. At the end of the day, the info about the sensor says that we need to divide the value by 1.2 in order to get the reading in Lux. As we do not want fractions of a value, we round down to a simple integer value that we assign to our global lux variable so that a later retrieval is possible.

That's all we need to do in our function for the time-critical and singleton device access. That's why our function is complete now and we leave it so that others from now on also can access the I2C-bus again.
// Prepare outbound XML document exactly in the intended shape
var outMsg = new Array ();
outMsg [0] = new Object ();
(outMsg [0]).type = "element";
(outMsg [0]).name = "lux";
(outMsg [0]).value = new Array ();
(outMsg [0]).value [0] = new Object ();
((outMsg [0]).value [0]).type = "text";
((outMsg [0]).value [0]).name = "";
((outMsg [0]).value [0]).value = lux;​

Being back in our main part of the script again, the next thing to do is to prepare the overall result message. For our adapter, we want to output nice, simple and easy-to-consume XML. That requires in turn some (more) work on Javascript side and the usage of one of the new JSON payloadtype conversion modes. We have to assemble the message in a suitable way on Javascript side using the artificial JSON format as described in the JSON payloadtype converter JSONPltConf document.
In our script, we could start to directly assemble JSON as text, but there is a powerful and more convenient better way to do so as being offered in ECMAScript, it is the capability of serializing any kind of Javascript data structures to textual JSON graphs.
That is why we assemble such data structures in a way that the JSON converter later on will understand this as the artificial JSON representation of the XML of our choice to create. Having a look to the lines above doing so, we see another powerful trait of ECMAScript: The ability to craft / assemble Javascript objects "on the fly" by just adding programmatically new properties not known before to the target object. From the point in time having done so, the object (that is the single live instance of a dynamically assembling class in fact)  just has got another member. It is possible to assign any types that way, be it plain values, other objects, arrays or even whole functions.
Last but not least, we assign the global lux value of question to the right place in our data structure.
// Serialize the Javascript data-structure to a JSON-Graph ...
var outData = JSON.stringify (outMsg);

// ... and finally set it as the output of the script
scriptIO.setJsonOutMessage (outData, false);

We're going to enter home stretch! Using JSON.stringify (...) as the powerful capability of ECMAScript to serialize our data structure to a textual JSON graph, we now hold our output data in a shape ready to pass back to B1i via the mandatory JSON -> XML payloadtype conversion. We use scriptIO.SetJsonOutMessage (...) for doing so. Very important is the 2nd boolean argument in that case where we advise that we want input articifial JSON that shall get rendered to free-style / natural XML.

Using B1iP's ECMA-Adapter, it probably never had been easier to access low level hardware devices for integration purposes! Become one of the many adopters to do so!

(This blog-post originally appeared on my Da Vinci Space Blog: https://davincispace.wordpress.com/2018/07/13/b1i-gy302-adapter-mk-ii-heands-on/)