New hardwareinterfaces API

This topic is opened for discussing the implementation of a simplified hardwareinterfaces API.
Goal is to allow a user to:

  • Use the API for implementing ideas in javascropt/node.js directly.
  • allow an easy way to program adapters for other hardware then the Arduino Yun

Continuing the discussion from Add non Arduino I/O points

Yes
I think if we have all of these constructors at the beginning of the server.js helps to keep the overview.

It’s maybe a bit confusing.
The server follows a push concept.

In the beginning the server looks through all available hardware interfaces and calls exports.receive one time from the server.js to start (in the Arduino case) a serialServer with a callback.
Whenever there is data coming in within the serialServer the callback triggers the objectEngine() in the server.js file. This is what I thought could be replaced with listenIO(). writeIOToServer() might be a better description.

When the objectEngine() processes a link that points to the serialServer then exports.send is triggered. This is what I thought about being writeIO(). IODataFromServer from server might be a better description.

Because of syncing the Arduino Atmega with the Server there are a couple of possitions in the Server.js that calls exports.init this tells the arduino library that the server is fully loaded and ready to run the initialization process.

To make the init resulting processes more clear I think we can split them in to:

addIO() would add the name of a new ObjectValue in to a buffer.

clearIO() would do a couple of things:

  1. check if the buffered ObjectValue name is present in the server.
    If it exist keep it with its state, if not add a new one.
  2. delete all ObjectValue that are not in the addIO() name buffer
  3. clear the name buffer.
  4. check that all links are valid.

developerIO()
activate or deactivate the developer functionality.

2 Likes

Well I’m running into a problem when running the object code from github. I installed the code on a Yun and didn’t change anything. But when i run “node server.js” it crashes:

Loading template: documentation
developer
add
I will save: yun2Test and: led id: 0
add
I will save: yun2Test and: generator id: 1
clear
------del---

/mnt/sda1/HybridObjects/hardwareInterfaces/arduinoYun/index.js:299
            for (var key in objectExp[objectID].objectValues) {
                                               ^
TypeError: Cannot read property 'objectValues' of undefined
    at clearIO (/mnt/sda1/HybridObjects/hardwareInterfaces/arduinoYun/index.js:299:48)
    at SerialPort.<anonymous> (/mnt/sda1/HybridObjects/hardwareInterfaces/arduinoYun/index.js:256:21)
    at SerialPort.emit (events.js:95:17)
    at /usr/lib/node_modules/serialport/parsers.js:25:17
    at Array.forEach (native)
    at Object.parser (/usr/lib/node_modules/serialport/parsers.js:24:13)
    at Object.SerialPort.options.dataCallback (/usr/lib/node_modules/serialport/serialport.js:149:15)
    at SerialPortFactory.SerialPort._emitData (/usr/lib/node_modules/serialport/serialport.js:320:20)
    at afterRead (/usr/lib/node_modules/serialport/serialport.js:296:20)
    at /usr/lib/node_modules/serialport/serialport.js:312:9

I’ve added one HybridObject via the webinterface and uploaded the readWrite sketch. It’s a little bit weird because when the “add add clear” sequence runs first immediately after uploading the sketch it works and I can’t figure out what’s different the following times. I can’t see how “objectExp[objectID]” could become “undefined”

Ah ok, it seems that for some reason in server.js the callback of

templateModule.loadAllModules(modulesList, function () {
    // start system
    if (globalVariables.debug) console.log("Starting System: ");
    loadHybridObjects();
    startSystem();

});

is never executed on my setup so objectExp never gets initialized.

EDIT: It seems like the template loading happens asynchronously so that the loadAllModules function immediately returns, am I right?

This is an interesting behavior. It must have slipped in when marcteys added a new developer fronted.
I think it is good to not make the server dependent on (or encapsulated) the asynchronous startup of the developer frontend. I just had a quick check and it seems the server and the frontend work with not having

loadHybridObjects();
startSystem();

encapsulated as a callback. But I have no Yun until monday to test the data monitoring part.
I just send marcteys an email if he can join the conversation about it.

Yes, that’s what I did then, just moved the two functions outside. Seems to work so far…

2 Likes

Ok, I’ve started to implement the API. I forked the project to: GitHub - Carsten87/object: Hybrid Objects code

I added a new file: libraries/HybridObjectsHardwareInterfaces and moved all the interface code there. The following functions are implemented so far:

  1. writeIOToServer()
  2. addIO()
  3. clearIO() (didn’t change that, still missing the link checking)
  4. developerIO()

Additionally I removed loadHybridObjects() and startSystem() from the callback as mentioned above.

I also started to implement a hardwareInterface for Philips Hue. It’s not much use yet, still missing vital parts, the exports.send() part is not yet implemented and so far the onOff state is the only IO Point. But it’s already using the newly implemented functions. So it might be good to look at it to see how to use them.

What I plan to do next:

  • Add some comments
  • extend the functionality of the PhilipsHue hardwareInterface
  • test it all

P.S. I would love to get some feedback and I’m open to suggestions :wink:

UPDATE
I cleaned up the API and removed the passthrough parameters from the functions. I added the setup() function which initalizes the references to those parameters and is called once in server.js. So the parameters don’t need to be passed to the functions anymore and are hidden from the API user.

UPDATE2
I’m now using the new API functions in the yun hardware interface. But I can’t test it all right now, I will do that somewhen this week and tell you if it’s working :slight_smile:

1 Like

And I’ve come up with some more questions:

  1. Even if I don’t add it, I always get a “test” IO point in my HybridObjects. At least it appears on the WebFrontend and produces random values. What’s that about?
  2. What exactly is the init() function supposed to do? It’s called three times from server.js so it cannot be used to put some setup stuff in there which should only run once, right?
1 Like

Sounds very good. I am looking forward to see the progress and your comments. I have time later this week for it.

Have you checked if your Arduino still has a “test” object uploaded. You can create new objects by simply adding them in the arduino code. You can also run the server.js from your computer. The server will then ignore the YunInterface. Is the test object still showing up when you run it from your computer?

init() asks the arduino library to run the setup communication with the server. It is placed at multiple locations, to guarantee synchronization. It is possible that some of these calls are not needed. I will have a closer look at it later this week as well.

1 Like

@valentin The “test” IO point appears in every HybridObject I create, be it a “arduinoYun” or “philipsHue” HybridObject. It does not appear in an objects objectValues though (at least not when clearIO() is called, that’s when I checked). That’s why I suspect it is just added to the GUI at some point. But I didn’t have the time yet to really look into it.

@Carsten Maybe this one helps: Can you comment the lines 243 to 247 in the file HybridObjectsWebFrontend.js and see if it still comes up?

jsonVariables.push({
   "test": infoCount,
   "value" : "test",
   "IOName" : d.getMilliseconds()
});

@valentin Thank you! I quickly checked it. Commenting these lines seems to do the trick.

OK, I’ve tested it and I’ve found some small bugs which I already fixed. I couldn’t test the yunInterface so far (maybe next week) but with the philipsHue interface it works fine.

The only things I deem to be quite ugly are the index and amount variables in addIO() and clearIO(). Isn’t the index only used in clearIO()? Or did I overlook it somewhere else? Instead of the amount variable the user could pass the ioNames of the IO points he wishes to keep. I think that would be more understandable. What do you think?

My updated TODOS:

  • add comments
  • add some error handling to the philipsHue interface
  • add some more functionality to the philipsHue interface
  • get rid of index and amount (maybe, depending on your input)
  • test the yunInterface

index and amount are relevant for communicating with the arduino library.

Index is used to keep a reference to the numerical index in the arduino array, independent from the js object “index” position (which can change).
It helps to minimize the SerialData traffic to just sending numbers.

By initializing the object, the server checks if a IOpoint with the added index exists.
If the name is identical it keeps the IOpoint and its data. If not, the IOpoint is deleted and a new IOpoint with the index is added.

To clear the objectValues, the arduino library sends the amount of IOpoints the server can count with.
The server deletes all IOpoints with a bigger index.

This is all done so that the existing values associated with the IOpoints are not overwritten every time the server restarts.

Because all the appearance of the IOpoint in the GUI and all relevant data about the IOpoint is saved in the objectValues and in a file on disk for the next program start, you need to keep track what is already existing the moment you run the initialization. Otherwise your GUI will reset with every start.

We can move the amount and index code in to the ArduinoYun interface. It is very specific and should not be in a generalized API.
I had a lookup table for the index before. We can bring this idea back. The lookup table simply keeps track of the arduino numerical index associated with the objectValues name index.
This will allow to remove the index from the objectValues and all the routines outside of the ArduinoYun hardware interface.

Never the less, the API should have some code to keep track what IOpoints are used, if they existed before and which IOpoints are obsolet and can be deleted.

Yes that’s exactly what I thought. So I can remove the index from the addIO() function and move the index management to the yun interface. At the moment clearIO() basically gets the amount and removes every IOpoint with an index greater or equal to amount. What I thought is that instead of passing the amount you could pass the names of the IOpoints and remove any IOpoints with names different than the ones passed. The effect would be the same but the function would be easier to use/understand I think.

Exactly, this would be the better solution.

Ok, I have implemented the stuff described above. But while experimenting around i realized that it is no good to have a global clear variable because every hardware interface only knows its own state and not the state of other hardware interfaces so setting this variable can have undesired effects. I think you don’t really need the clear variable at all. You should just call clearIO() after the addIO() calls and that’s it. Or am I missing something?

It’s similar with the developer variable. Because it has a global impact I’m not sure if it should be set in a hardwareInterface. Theoretically the developer of one hardwareInterface could set developer to true and the developer of another could set it to false and in the end it would be set to the value which was defined in the last hardwareInterface loaded. That’s sort of random. But on the other hand I have no clue where else to put it…

The hardware interface has to describe in every ObjectValue where the provided value comes from.
This is relevant for the routing when data is processed.

For example the values from the Yun interface indicate “arduinoYun”

Line 165:   
ObjectValue = function () {
        ...
        this.type = "arduinoYun";
    }

When the server processes an incoming link, that has a destination value with the type “arduinoYun” it knows that it needs to be processed with the ArduinoYun hardware interface.
The Philips Hue probably would indicate to be the “philipsHue”

  • We can call clearIO(“interfaceName”) within the hardwareInterface after all addIO()'s have been called. The clearIO function would then only process values that contain the indicated type.

  • The developer variable has to be set from within the hardwareInterfaces so that it can be controlled from everywhere. But the model can be like this: It is on by default and will be switched off if a developer wants it. This way you can change the developer mode from within arduino and you never need to touch the server code.