Masoman3
Posts: 6
Joined: Mon Feb 29, 2016 2:37 am

Node.js - onoff input watch callback issues

Mon Jun 20, 2016 3:35 pm

So I'm trying to write a small web app that can control the GPIO ports dynamically, as in you can assign a pin as an input (which will then use the watch for interrupts and use socket.io to update the html pages) , an output (which can be controlled on the web page), or as unused. The issue is with the inputs, I have some code that is run on startup that loads all the pin configurations from a mongodb database, run in a for loop with a switch case inside for inputs/outputs.

So with the setup out of the way, my issue is that no matter what point the index was up when assigning the watch callback function to the input pin in the array, when the watch is actually called, it uses the last value the index iterator was up to (i.e. 17 in an array of length 17).

No matter what I have tried, I can't figure out a way for the watch function to find out what it's calling GPIO pin was, or even have a persistent variable assigned to it. It always seems to use the data as it is after all the functions have been called.

Code: Select all

function LoadGPIO()
{
  OpenPins = [];
  var collection = db.get('gpio');
  collection.find({}, {}, function(err, docs){
      console.log(docs);
		for(var i = 0; i < docs.length; i++){
			switch(docs[i].mode)
			{
			  case 'input':
				var Pin = new GPIO(docs[i].GPIOpin, 'in', 'both');
				OpenPins.push({gpio: Pin, pin: docs[i].GPIOpin});
				OpenPins[OpenPins.length - 1].gpio.watch(function(err, value){
					if (err) {
						throw err;
					}
					io.emit('updatePin', {'type': 'state', 'args': (value ? 0 : 1), 'pinID': docs[i]._id});
				});
				break;
			  case 'output':
				var Pin = new GPIO(docs[i].GPIOpin, 'out');
				OpenPins.push({gpio: Pin, pin: docs[i].GPIOpin});
				OpenPins[OpenPins.length - 1].gpio.write(docs[i].state);
				break;
			}
			console.log(docs[i].GPIOpin + ' ' + docs[i].state);
		}
		
    });
}
The funny thing is, is that this only happens when I first load the pins in the startup, if I then set the pin to unused, then back to input on the webpage, it works perfectly. Here's the code that's run on a socket.io connection:

Code: Select all

io.on('connection', function(socket){
	db.get('gpio').find({}, {'sort':'pin'}, function(err, docs){
		socket.emit('generateTable', docs);
	});	
	
	socket.on('updatePin', function(data){
		var collection = db.get('gpio');
		var setQuery = {};
		setQuery[data.type] = data.args;
		collection.update(
			{'_id': data.pinID}, //Query
			{$set: setQuery} //New Data
		);
		if (data.type == 'mode')
		{
			collection.findOne({'_id': data.pinID}).on('success', function(doc){
				var index = OpenPins.map(function(arrayItem) { return arrayItem.pin; }).indexOf(doc.GPIOpin);
				console.log(OpenPins.map(function(arrayItem) { return arrayItem.pin; }));
				if (index != -1)
				{
					OpenPins[index].gpio.unexport();
					OpenPins.splice(index, 1);
				}
				switch(doc.mode)
				{
				  case 'input':
					var Pin = new GPIO(doc.GPIOpin, 'in', 'both');
					OpenPins.push({gpio: Pin, pin: doc.GPIOpin});
					OpenPins[OpenPins.length - 1].gpio.watch(function(err, value){
						if (err) {
							throw err;
						}
						io.emit('updatePin', {'type': 'state', 'args': (value ? 0 : 1), 'pinID': doc._id});
						console.log(doc.GPIOpin + ' ' + value);
					});
					break;
				  case 'output':
					var Pin = new GPIO(doc.GPIOpin, 'out');
					OpenPins.push({gpio: Pin, pin: doc.GPIOpin});
					OpenPins[OpenPins.length - 1].gpio.write(doc.state);
					break;
				}
			});
		}
		else if(data.type == 'state')
		{
			collection.findOne({'_id': data.pinID}).on('success', function(doc){
				OpenPins[OpenPins.map(function(arrayItem) { return arrayItem.pin; }).indexOf(doc.GPIOpin)].gpio.write(doc.state);
			});
		}
		socket.broadcast.emit('updatePin', data);
	});
});
Anyone got any more experience on Javascript Callback functions that can help me figure out a way I can return the proper ID of the gpio pin that triggered the watch callback?

fivdi
Posts: 464
Joined: Sun Sep 23, 2012 8:09 pm
Contact: Website

Re: Node.js - onoff input watch callback issues

Mon Jun 20, 2016 9:14 pm

There are two demo programs below, bad.js and good.js. bad.js has the same issue as your program. good.js shows one technique that can be used to avoid the issue.

Code: Select all

pi@raspberrypi:~/demo $ cat bad.js 
var i;

for (i = 1; i <= 5; i += 1) {
  setTimeout(function () {
    console.log(i);
  }, 1);
}
pi@raspberrypi:~/demo $ node bad.js 
6
6
6
6
6
pi@raspberrypi:~/demo $ 

Code: Select all

pi@raspberrypi:~/demo $ cat good.js 
var i;

for (i = 1; i <= 5; i += 1) {
  (function (j) {
    setTimeout(function () {
      console.log(j);
    }, 1);
  })(i);
}
pi@raspberrypi:~/demo $ node good.js 
1
2
3
4
5
pi@raspberrypi:~/demo $ 

Masoman3
Posts: 6
Joined: Mon Feb 29, 2016 2:37 am

Re: Node.js - onoff input watch callback issues

Mon Jun 20, 2016 9:28 pm

Thanks for the help, is this still the case when the callback function has predefined parameters? On the onoff GitHub page it says that it'll take two parameters, the first being an error and the second being the value read from the pin. So can I add a third parameter that onoff will ignore and I can use for my own parameter?

fivdi
Posts: 464
Joined: Sun Sep 23, 2012 8:09 pm
Contact: Website

Re: Node.js - onoff input watch callback issues

Mon Jun 20, 2016 9:43 pm

Masoman3 wrote:Thanks for the help, is this still the case when the callback function has predefined parameters?
You're welcome :) and yes.
Masoman3 wrote:On the onoff GitHub page it says that it'll take two parameters, the first being an error and the second being the value read from the pin. So can I add a third parameter that onoff will ignore and I can use for my own parameter?
I'm afraid not.

You'll need to learn a bit about JavaScript closures, for example at: https://developer.mozilla.org/en/docs/W ... t/Closures. In particular, look at the section titled "Creating closures in loops: A common mistake".

Here's a good free book about JavaScript scopes and closures: https://github.com/getify/You-Dont-Know ... 20closures

fivdi
Posts: 464
Joined: Sun Sep 23, 2012 8:09 pm
Contact: Website

Re: Node.js - onoff input watch callback issues

Mon Jun 20, 2016 9:50 pm

However, something like the following could be used to achieve the same thing as a third parameter.

Code: Select all

var i, gpio;

for (i = 1; i <= 5; i += 1) {
  gpio = new Gpio(...);
  (function (gpiox, theThirdParameter) {
    gpiox.watch(function(err, value) {
      // gpiox (which was gpio), err, value, and theThirdParameter (which was i) can be used here
    })
  })(gpio, i);
}

Masoman3
Posts: 6
Joined: Mon Feb 29, 2016 2:37 am

Re: Node.js - onoff input watch callback issues

Tue Jun 21, 2016 12:18 pm

Thanks for the help fivdi, I used the closures and it worked a treat. Thanks for making such an awesome API and having time to help out someone with what must seem like such a simple problem to someone experienced in Javascript.

Return to “Troubleshooting”