Reputation: 2730
I am writing some motor controls with node.js using cylon.js. I have a servo, which when you give it an angle to go to has a callback function. When it finishes that function, I want to do another reading, and give it a new angle, with the callback to do another reading...so forth and so on.
the current code is:
function ControlServo(servo, angleSensor){
robo.servo.angle(angleSensor.Read(), controlServo(servo, angleSensor));
}
That gets stack overflowed in like a quarter second.
Upvotes: 0
Views: 199
Reputation: 1391
It's not possible to do what you want to do with Cylon.js. Cylon.js's internal "write" operation "callback" is not async and doesn't get called when the move is mechanically completed. It gets called immediately after the write operation. Cylon can only write the angle value to the servo, which mechanically moves the horn at its max capable speed. If it's a slow servo, it could take 2 whole seconds from 0 to 180 degrees before it's actually mechanically complete. In the meantime, Cylon has already called the callback. The reason for this is because there is no way to generalize that callback behavior in a way that would be consistently correct for all servo models, without doing a little extra work.
In Johnny-Five we've implemented speed control that's enabled by providing a "time to complete" argument. This is done by dividing the distance to the new angle into steps to move in the specified "time to complete". A side effect of this process is that Johnny-Five servo instances can know when the move is mechanically complete, because the steps are smaller and the timing controlled. As a result, we have a "move:complete" event that emits when any timed move is completed.
var servo = new five.Servo(9); servo.on("move:complete", function() { // we've arrived! }); // change takes 500ms to complete servo.to(180, 500);
That can easily be combined with an analog sensor:
var servo = new five.Servo(9); var sensor = new five.Sensor({ pin: "A0", scale: [ 0, 180 ] }); servo.on("move:complete", function() { update(); }); function update() { // change takes 200ms to complete servo.to(sensor.value, 200); } update();
Even simpler:
var servo = new five.Servo(9); var sensor = new five.Sensor("A0"); sensor.scale(0, 180).on("change", function() { servo.to(this.value); });
Upvotes: 3
Reputation: 19857
A better way to solve this is using a timeout
instead of recursing. This ends up calling your function on the next tick, which will never overflow.
function ControlServo(servo, angleSensor){
robo.servo.angle(angleSensor.Read(), function() {
setTimeout(function() { ControlServo(servo, angleSensor)}, 0);
});
};
You could shorten this by moving the timeout
into the servo.angle
function, but you might need it to be a callback for other uses. The method above requires no other change, since the callback is just setting the timeout.
Another option is setImmediate
, which appears to put the function call at the end of the current tick, instead of the beginning of the next one. Since setTimeout
will always introduce a slight delay, setImmediate
might be faster; however, I don't know what other tradeoffs might be made using this as I haven't used it much myself.
Upvotes: 2