Increment or decrement a value between two ranges

I want to finely control the position of a servo by having two buttons, one which will move it in one direction (as far as a pre-set limit) and the other to move it in the opposite towards the other limit. So I picture having a variable, and having that variable increased by one action and decreased by the other. But I have no clue as to how to trigger a single increment or decrement in a manner that I can control it on the dashboard. Any ideas?

Maybe have two buttons that generate a message, and then in a function have that message trigger an IF THEN that either increments or decrements the value, then returns the new message?

Well you could start with a simple numeric input node as that already has up and down buttons…
You can set the min to max range - then possibly use a range node to shift them if needed - to then feed the servo.

If this is on a Pi - then the node-red-contrib-pi-gpiod node servo outputs seem (to me at least) to be very stable.

[{"id":"bce0423a.be2f4","type":"ui_button","z":"4284f9c0.bf64b8","name":"","group":"d0ee889e.a7d828","order":0,"width":0,"height":0,"passthru":false,"label":"increment","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"","x":132.5,"y":3803.3499755859375,"wires":[["8807cd2d.c52ef"]]},{"id":"4da36fc2.012c8","type":"ui_button","z":"4284f9c0.bf64b8","name":"","group":"d0ee889e.a7d828","order":0,"width":0,"height":0,"passthru":false,"label":"decrement","color":"","bgcolor":"","icon":"","payload":"false","payloadType":"bool","topic":"","x":144.5,"y":3870.35009765625,"wires":[["8807cd2d.c52ef"]]},{"id":"8807cd2d.c52ef","type":"function","z":"4284f9c0.bf64b8","name":"","func":"var claw = 90;\nif (msg.payload === true) {\nclaw = claw++;\n}\nif (msg.payload === false) {\n claw = claw--;\n}\nvar newMsg = { payload: claw };\nreturn newMsg;","outputs":1,"noerr":0,"x":337.5,"y":3828.4500122070312,"wires":[["d39a3690.b7fca8","3ee50320.4de2bc"]]},{"id":"d39a3690.b7fca8","type":"debug","z":"4284f9c0.bf64b8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":563.5,"y":3828.2999877929687,"wires":[]},{"id":"3ee50320.4de2bc","type":"ui_text","z":"4284f9c0.bf64b8","group":"d0ee889e.a7d828","order":0,"width":0,"height":0,"name":"","label":"text","format":"{{msg.payload}}","layout":"row-spread","x":543.5,"y":3772.3499755859375,"wires":[]},{"id":"d0ee889e.a7d828","type":"ui_group","z":"","name":"Notifications","tab":"a635a66f.44b2d8","order":3,"disp":true,"width":"6"},{"id":"a635a66f.44b2d8","type":"ui_tab","z":"","name":"UPS1","icon":"dashboard","order":2}]

Is what I have tried so far, but obviously I have botched something in the syntax.

Yes, the numeric input widget is great. Thanks. I forgot that it had up and down buttons.

I’m using this on a PI, and running the output to an I2C servo driver board.

Still would like to get my function to work, for learning sake.

Just as one idea …

[{"id":"7ba617c.f70ffe8","type":"ui_button","z":"dfeaf865.51c758","name":"","group":"cd8751a3.f6fba","order":7,"width":0,"height":0,"passthru":false,"label":"Subir temperatura","color":"red","bgcolor":"#E9967A","icon":"fa-arrow-circle-up","payload":"1","payloadType":"num","topic":"","x":170,"y":4600,"wires":[["4a536d89.12adc4"]]},{"id":"10c3a218.e230fe","type":"ui_button","z":"dfeaf865.51c758","name":"","group":"cd8751a3.f6fba","order":8,"width":0,"height":0,"passthru":false,"label":"Bajar temperatura","color":"blue","bgcolor":"#3FADB5","icon":"fa-arrow-circle-down","payload":"1","payloadType":"num","topic":"","x":150,"y":4680,"wires":[["107c94cb.1ae2bb"]]},{"id":"6550849b.6f0b3c","type":"inject","z":"dfeaf865.51c758","name":"","topic":"","payload":"21","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":"","x":299,"y":4742,"wires":[["c0f15889.239e98"]]},{"id":"25ce51ea.8d375e","type":"change","z":"dfeaf865.51c758","name":"","rules":[{"t":"set","p":"decrement","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":4680,"wires":[["a66f041.d5e9bf8"]]},{"id":"9b2069d5.c3d768","type":"change","z":"dfeaf865.51c758","name":"","rules":[{"t":"set","p":"increment","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":4600,"wires":[["a66f041.d5e9bf8"]]},{"id":"c0f15889.239e98","type":"change","z":"dfeaf865.51c758","name":"","rules":[{"t":"set","p":"reset","pt":"msg","to":"21","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":495,"y":4740,"wires":[["a66f041.d5e9bf8"]]},{"id":"a66f041.d5e9bf8","type":"counter","z":"dfeaf865.51c758","name":"","init":"17","step":1,"lower":"17","upper":"30","mode":"decrement","outputs":"2","x":740,"y":4740,"wires":[["da10f784.81b1e8","8b0b857e.a512a8"],[]]},{"id":"50fcd1dd.64875","type":"ui_button","z":"dfeaf865.51c758","name":"","group":"cd8751a3.f6fba","order":10,"width":"3","height":"1","passthru":false,"label":"Max temp","color":"red","bgcolor":"#E9967A","icon":"fa-thermometer-full ","payload":"30","payloadType":"num","topic":"","x":260,"y":4820,"wires":[["e889f3be.78a8d"]]},{"id":"81fc8451.f6d268","type":"ui_button","z":"dfeaf865.51c758","name":"","group":"cd8751a3.f6fba","order":9,"width":"3","height":"1","passthru":false,"label":"Min temp","color":"blue","bgcolor":"#3FADB5","icon":"fa-thermometer-empty","payload":"17","payloadType":"num","topic":"","x":264,"y":4866,"wires":[["b4f5cda0.cd1a7"]]},{"id":"594ae56.d2fc91c","type":"change","z":"dfeaf865.51c758","name":"","rules":[{"t":"set","p":"reset","pt":"msg","to":"30","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":4820,"wires":[["a66f041.d5e9bf8"]]},{"id":"942131df.140b7","type":"change","z":"dfeaf865.51c758","name":"","rules":[{"t":"set","p":"reset","pt":"msg","to":"17","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":4860,"wires":[["a66f041.d5e9bf8"]]},{"id":"4a536d89.12adc4","type":"switch","z":"dfeaf865.51c758","name":"","property":"AC_salon","propertyType":"global","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","outputs":1,"x":350,"y":4600,"wires":[["9b2069d5.c3d768"]]},{"id":"107c94cb.1ae2bb","type":"switch","z":"dfeaf865.51c758","name":"","property":"AC_salon","propertyType":"global","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","outputs":1,"x":330,"y":4680,"wires":[["25ce51ea.8d375e"]]},{"id":"e889f3be.78a8d","type":"switch","z":"dfeaf865.51c758","name":"","property":"AC_salon","propertyType":"global","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","outputs":1,"x":410,"y":4820,"wires":[["594ae56.d2fc91c"]]},{"id":"b4f5cda0.cd1a7","type":"switch","z":"dfeaf865.51c758","name":"","property":"AC_salon","propertyType":"global","rules":[{"t":"eq","v":"on","vt":"str"}],"checkall":"true","outputs":1,"x":410,"y":4860,"wires":[["942131df.140b7"]]},{"id":"da10f784.81b1e8","type":"ui_gauge","z":"dfeaf865.51c758","name":"consigna","group":"cd8751a3.f6fba","order":6,"width":"0","height":"0","gtype":"gage","title":"Consigna","label":"ºC","format":"{{value}}","min":"17","max":"30","colors":["#0728ad","#ff6317","#ca3838"],"seg1":"22","seg2":"25","x":840,"y":4660,"wires":[]},{"id":"cd8751a3.f6fba","type":"ui_group","z":"","name":"AC Dormitorio","tab":"b5ad9abd.3a6c38","order":3,"disp":true,"width":"6"},{"id":"b5ad9abd.3a6c38","type":"ui_tab","z":"","name":"C. REMOTOS","icon":"wifi","order":3}]

Hi - when you post code/flows - can you surround it with ``` at start and end as the quotes don’t then get mangled into so called smart quotes, which don’t import back into Node-RED correctly. (I have edited both posts above) - Thanks

Good to know I had no idea it was not shown correctly, thanks I will take it into consideration next time.

Regards

var claw;
if (msg.topic === "init") {
claw = msg.payload
    }
else if  (msg.topic === "increment") {
claw = ++ claw
}
else if (msg.topic === "decrement") {
claw = -- claw
}
var newMsg = { payload: claw };
return newMsg;

Can anyone spot what is wrong with my function? It doesn’t increment or decrement. I’m trying to use msg.topic as the trigger.

[{"id":"bce0423a.be2f4","type":"ui_button","z":"4284f9c0.bf64b8","name":"","group":"d0ee889e.a7d828","order":0,"width":0,"height":0,"passthru":false,"label":"increment","color":"","bgcolor":"","icon":"","payload":"0","payloadType":"num","topic":"increment","x":132.5,"y":3803.3499755859375,"wires":[["8807cd2d.c52ef"]]},{"id":"4da36fc2.012c8","type":"ui_button","z":"4284f9c0.bf64b8","name":"","group":"d0ee889e.a7d828","order":0,"width":0,"height":0,"passthru":false,"label":"decrement","color":"","bgcolor":"","icon":"","payload":"0","payloadType":"num","topic":"decrement","x":144.5,"y":3870.35009765625,"wires":[["8807cd2d.c52ef"]]},{"id":"8807cd2d.c52ef","type":"function","z":"4284f9c0.bf64b8","name":"","func":"var claw;\nif (msg.topic === \"init\") {\nclaw = msg.payload\n    }\nelse if  (msg.topic === \"increment\") {\nclaw = ++ claw\n}\nelse if (msg.topic === \"decrement\") {\nclaw = -- claw\n}\nvar newMsg = { payload: claw };\nreturn newMsg;","outputs":1,"noerr":0,"x":339.5,"y":3827.449951171875,"wires":[["d39a3690.b7fca8","3ee50320.4de2bc"]]},{"id":"d39a3690.b7fca8","type":"debug","z":"4284f9c0.bf64b8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":563.5,"y":3828.2999877929687,"wires":[]},{"id":"3ee50320.4de2bc","type":"ui_text","z":"4284f9c0.bf64b8","group":"d0ee889e.a7d828","order":0,"width":0,"height":0,"name":"","label":"text","format":"{{msg.payload}}","layout":"row-spread","x":543.5,"y":3772.3499755859375,"wires":[]},{"id":"209cc4ea.c967dc","type":"ui_numeric","z":"4284f9c0.bf64b8","name":"","label":"numeric","group":"d0ee889e.a7d828","order":0,"width":0,"height":0,"passthru":true,"topic":"","format":"{{value}}","min":"1","max":"180","step":1,"x":128.5,"y":3941.3499755859375,"wires":[["3ee50320.4de2bc"]]},{"id":"ed778b45.c6e768","type":"inject","z":"4284f9c0.bf64b8","name":"","topic":"init","payload":"90","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":119.5,"y":3751.1499938964844,"wires":[["8807cd2d.c52ef"]]},{"id":"d0ee889e.a7d828","type":"ui_group","z":"","name":"Notifications","tab":"a635a66f.44b2d8","order":3,"disp":true,"width":"6"},{"id":"a635a66f.44b2d8","type":"ui_tab","z":"","name":"UPS1","icon":"dashboard","order":2}]

Note - use ``` (3 backticks) around your code blocks to get them to properly format)

The problem here is your claw variable. Variables within a Function node do not exist between calls to the Funciton. So your claw variable isn’t remembering the value between calls.

You need to use flow context to store data between calls to the function - https://nodered.org/docs/writing-functions#storing-data

For example:

// Get the current value of the 'claw' context property - default to 0
var claw = flow.get("claw")||0;

if (msg.topic === "init") {
   // set its value locally
   claw = msg.payload;
} else if  (msg.topic === "increment") {
   claw++;
} else if (msg.topic === "decrement") {
   claw--;
}
// Save the new value back to context so it will be available next time
flow.set('claw',claw);
// Update the message payload and return - no need to create a new msg
msg.payload = claw;
return msg;

With the value stored in flow context, you’ll be able to refer to it in other places - for example, the initialisation of the value could be done by wiring the Inject node to a Change node what is configured to set flow.claw to msg.payload.

Nick

Thank you. Lots to learn. That flow context will be very useful in the future. I always wondered how some nodes, with no wired connections, shared data.

Sorry about the quotes problem. I thought it was quotes, now I know its back ticks, if I can find them on my keyboard.

To set a max and a minimum will be doing something like???

“”// Get the current value of the ‘claw’ context property - default to 5
var claw = flow.get(“claw”)||5;

if (msg.topic === “init”) {
// set its value locally
claw = msg.payload;
}
else if ((msg.topic === “increment”) && (claw <= 10)) {
claw++;
} else if ((msg.topic === “decrement”) && (claw >= 0)) {
claw–;
}
// Save the new value back to context so it will be available next time
flow.set(‘claw’,claw);
// Update the message payload and return - no need to create a new msg
msg.payload = claw;
return msg; “”

I’m doing something wrong because is not working but don’t know what…

Thank you David. I forgot about that detail!

That looks ok (though it will give a max and min of 11 and -1 as it will inc if == 10 and dec if ==0) which may not be exactly what you expected. Other than that you will need to tell us what is not working.

By the way, unless you need claw in another node in the flow using the context then use node.context not flow.context.

Allows me to deploy fine but then tells me:

30/5/2018 22:03:50node: counter
function : (error)
"SyntaxError: Invalid or unexpected token"
// Get the current value of the 'claw' context property - default to 5
var claw = flow.get("claw")||5;

if (msg.topic === "init") {
   // set its value locally
   claw = msg.payload;
} 
else if ((msg.topic === “increment”) && (claw < 11)) {
claw++;
} else if ((msg.topic === “decrement”) && (claw > -1)) {
claw--;
}
// Save the new value back to context so it will be available next time
flow.set('claw',claw);
// Update the message payload and return - no need to create a new msg
msg.payload = claw;
return msg;
[{"id":"1227b68f.ead989","type":"function","z":"3a79879.af7b678","name":"counter","func":"// Get the current value of the 'claw' context property - default to 5\nvar claw = flow.get(\"claw\")||5;\n\nif (msg.topic === \"init\") {\n   // set its value locally\n   claw = msg.payload;\n} \nelse if ((msg.topic === “increment”) && (claw < 11)) {\nclaw++;\n} else if ((msg.topic === “decrement”) && (claw > -1)) {\nclaw--;\n}\n// Save the new value back to context so it will be available next time\nflow.set('claw',claw);\n// Update the message payload and return - no need to create a new msg\nmsg.payload = claw;\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":80,"wires":[["9606d127.d944f"]]},{"id":"749c878c.234b48","type":"inject","z":"3a79879.af7b678","name":"","topic":"init","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":40,"wires":[["1227b68f.ead989"]]},{"id":"5905f8b1.98de18","type":"inject","z":"3a79879.af7b678","name":"","topic":"increment","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":80,"wires":[["1227b68f.ead989"]]},{"id":"41054bc1.6ff844","type":"inject","z":"3a79879.af7b678","name":"","topic":"decrement","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":120,"wires":[["1227b68f.ead989"]]},{"id":"9606d127.d944f","type":"debug","z":"3a79879.af7b678","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":610,"y":200,"wires":[]}]

Actually this won't work the way you think either. When you get to a value of 0 then the next time it executes that line it will call flow.get, which will return 0 as it should, but since 0 is interpreted as false it will execute the other side of the OR and so set claw to 5. If you want to set it to 5 the first time round then you will need to test for undefined rather than just use ||

I didn’t know anyway changing this value to 0 makes no difference, I have same fault after deploy

The actual value I will be moving between will be between roughly 1000 and 2000, as that is the value in microseconds that drives the servo to a particular position on a robotic arm. I will have to experiment with what is a reasonable increment or decrement value that produces a small movement. Doubt it will be 1. So likely +=5 or 10 for increments. Wouldn’t matter if it stepped by one increment above or below the “limits”. It will be handy to have buttons that inject preset positions for multiple servos, and then use the increment buttons to fine tune a position or move slowly.

A pressure sensor on the claw that stops the squeeze at a certain value is a feature I may implement, that way I can just tell the claw to close and it will stop at a certain grip value.

You have some funny quotes at the beginning and end of the function.

dceejay told me tu put when pasting here a flow, I just see he told me to put ``` not “”