From c2ba5686e4b8bdc3a19291418359a3bc902a123f Mon Sep 17 00:00:00 2001 From: Donovan Buck Date: Sat, 7 Apr 2018 22:56:43 -0500 Subject: [PATCH] Add ability to set servo position in microseconds If servo.write value is < 544 then SERVO_WRITE degrees, otherwise SERVO_WRITE_MICROSECONDS. Includes tests. --- lib/particle.js | 92 +++++++++++++++++++++++++++++++++++++++++------- package.json | 6 +++- readme.md | 30 +++++++++++++++- test/particle.js | 57 ++++++++++++++++++++++++++++-- 4 files changed, 168 insertions(+), 17 deletions(-) diff --git a/lib/particle.js b/lib/particle.js index 2810eae..11232c5 100644 --- a/lib/particle.js +++ b/lib/particle.js @@ -14,10 +14,10 @@ var errors = { }; var pins = [ - { id: "D0", modes: [0, 1, 3, 4] }, - { id: "D1", modes: [0, 1, 3, 4] }, - { id: "D2", modes: [0, 1, 3, 4] }, - { id: "D3", modes: [0, 1, 3, 4] }, + { id: "D0", modes: [0, 1, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, + { id: "D1", modes: [0, 1, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, + { id: "D2", modes: [0, 1, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, + { id: "D3", modes: [0, 1, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, { id: "D4", modes: [0, 1] }, { id: "D5", modes: [0, 1] }, { id: "D6", modes: [0, 1] }, @@ -26,14 +26,14 @@ var pins = [ { id: "", modes: [] }, { id: "", modes: [] }, - { id: "A0", modes: [0, 1, 2, 3, 4] }, - { id: "A1", modes: [0, 1, 2, 3, 4] }, + { id: "A0", modes: [0, 1, 2, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, + { id: "A1", modes: [0, 1, 2, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, { id: "A2", modes: [0, 1, 2] }, { id: "A3", modes: [0, 1, 2] }, - { id: "A4", modes: [0, 1, 2, 3, 4] }, - { id: "A5", modes: [0, 1, 2, 3, 4] }, - { id: "A6", modes: [0, 1, 2, 3, 4] }, - { id: "A7", modes: [0, 1, 2, 3, 4] } + { id: "A4", modes: [0, 1, 2, 3, 4], pwm: { servoMin: 1000, servoMax: 2000 }}, + { id: "A5", modes: [0, 1, 2, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, + { id: "A6", modes: [0, 1, 2, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, + { id: "A7", modes: [0, 1, 2, 3, 4], pwm: { servoMin: 600, servoMax: 2400 }}, ]; var modes = Object.freeze({ @@ -252,6 +252,7 @@ function Particle(opts) { name: pin.id, supportedModes: pin.modes, mode: pin.modes[0], + pwm: pin.pwm, value: 0 }; }); @@ -479,10 +480,9 @@ Particle.prototype.pinMode = function(pin, mode) { return this; }; -["analogWrite", "digitalWrite", "servoWrite"].forEach(function(fn) { +["analogWrite", "digitalWrite"].forEach(function(fn) { var isAnalog = fn === "analogWrite"; - var isServo = fn === "servoWrite"; - var action = isAnalog ? 0x02 : (isServo ? 0x41 : 0x01); + var action = isAnalog ? 0x02 : 0x01; Particle.prototype[fn] = function(pin, value) { var state = priv.get(this); @@ -502,6 +502,67 @@ Particle.prototype.pinMode = function(pin, mode) { }; }); +Particle.prototype.servoWrite = function(pin, value) { + var state = priv.get(this); + var buffer; + var offset = pin[0] === "A" ? 10 : 0; + var pinInt = (String(pin).replace(/A|D/i, "") | 0) + offset; + + if (value < 544) { + buffer = new Buffer(3); + value = constrain(value, 0, 180); + buffer[0] = 0x41; + buffer[1] = pinInt; + buffer[2] = value; + } else { + buffer = new Buffer(4); + value = constrain(value, this.pins[pinInt].pwm.servoMin, this.pins[pinInt].pwm.servoMax); + buffer[0] = 0x43; + buffer[1] = pinInt; + buffer[2] = value & 0x7f; + buffer[3] = value >> 7; + } + + // console.log(buffer); + state.socket.write(buffer); + this.pins[pinInt].value = value; + + return this; +}; + +Particle.prototype.servoConfig = function(pin, min, max) { + var offset = pin[0] === "A" ? 10 : 0; + var pinInt = (String(pin).replace(/A|D/i, "") | 0) + offset; + var temp; + + if (typeof pin === "object" && pin !== null) { + temp = pin; + pin = temp.pin; + min = temp.min; + max = temp.max; + } + + if (typeof pin === "undefined") { + throw new Error("servoConfig: pin must be specified"); + } + + if (typeof min === "undefined") { + throw new Error("servoConfig: min must be specified"); + } + + if (typeof max === "undefined") { + throw new Error("servoConfig: max must be specified"); + } + + if (this.pins[pinInt].mode !== modes.SERVO) { + this.pinMode(pinInt, modes.SERVO); + } + + pins[pinInt].pwm.servoMin = min; + pins[pinInt].pwm.servoMax = max; + +}; + // TODO: Define protocol for gather this information. ["analogRead", "digitalRead"].forEach(function(fn) { var isAnalog = fn === "analogRead"; @@ -812,6 +873,11 @@ Particle.prototype.close = function() { state.server.close(); }; +function scale(value, inMin, inMax, outMin, outMax) { + return (value - inMin) * (outMax - outMin) / + (inMax - inMin) + outMin; +} + function constrain(value, lower, upper) { return Math.min(upper, Math.max(lower, value)); } diff --git a/package.json b/package.json index 6a2feb7..29a0789 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "particle-io", "description": "Particle Core & Photon IO Plugin for Johnny-Five", - "version": "0.14.0", + "version": "0.15.0", "homepage": "https://github.com/rwaldron/particle-io", "author": { "name": "Rick Waldron ", @@ -31,6 +31,10 @@ { "name": "Brian Genisio", "email": "" + }, + { + "name": "Donovan Buck", + "email": "" } ], "keywords": [ diff --git a/readme.md b/readme.md index cde577e..3856017 100644 --- a/readme.md +++ b/readme.md @@ -214,7 +214,7 @@ board.on("ready", function() { **servoWrite(pin, value)** -> Sets the pin to a value between 0 and 180, where the value represents degrees of the servo horn. The value is converted to a PWM signal. PWM is available on D0, D1, A0, A1, A4, A5, A6 and A7. +> If the value is less than 544, sets the pin to a value constrained between 0 and 180, where the value represents degrees of the servo horn. If the value is 545 or greater, sets the pin to a value in microseconds. Example: ```js @@ -228,6 +228,34 @@ board.on("ready", function() { }); ``` +```js +var board = new Particle(...); + +board.on("ready", function() { + + // Set the servo duty cycle to 1759 microseconds + // Note that the servo pwm frequency is 50mhz and + // the default servo range is 600-2400 microseconds + this.servoWrite("D0", 1759); + +}); +``` + +**servoConfig(pin, min, max)** + +> Sets the range for the servo PWM duty cycle. The default range is 600 - 2400 microseconds. Can be called instead of pin mode. + +Example: +```js +var board = new Particle(...); + +board.on("ready", function() { + + this.servoConfig("D0", 690, 2310); + +}); +``` + **digitalRead(pin, handler)** Setup a continuous read handler for specific digital pin (D0-D7). diff --git a/test/particle.js b/test/particle.js index c5f30b5..45d2c4d 100644 --- a/test/particle.js +++ b/test/particle.js @@ -759,7 +759,6 @@ exports["Particle.prototype.servoWrite"] = { } test.done(); }, - servoWriteDigital: function(test) { test.expect(3); @@ -774,7 +773,6 @@ exports["Particle.prototype.servoWrite"] = { } test.done(); }, - servoWriteAnalog: function(test) { test.expect(3); @@ -787,6 +785,61 @@ exports["Particle.prototype.servoWrite"] = { for (var i = 0; i < sent.length; i++) { test.equal(sent[i], buffer.readUInt8(i)); } + test.done(); + }, + servoWriteOutsideRange: function(test) { + test.expect(3); + + var sent = [0x41, 10, 180]; + + this.particle.servoWrite("A0", 220); + + var buffer = this.socketwrite.args[0][0]; + + for (var i = 0; i < sent.length; i++) { + test.equal(sent[i], buffer.readUInt8(i)); + } + test.done(); + }, + servoWriteMicroseconds: function(test) { + test.expect(4); + + var sent = [0x43, 10, 14, 12]; + + this.particle.servoWrite("A0", 1550); + + var buffer = this.socketwrite.args[0][0]; + + for (var i = 0; i < sent.length; i++) { + test.equal(sent[i], buffer.readUInt8(i)); + } + test.done(); + }, + servoWriteMicrosecondsOutsideRange: function(test) { + test.expect(4); + + var sent = [0x43, 10, 96, 18]; + + this.particle.servoWrite("A0", 3000); + + var buffer = this.socketwrite.args[0][0]; + + for (var i = 0; i < sent.length; i++) { + test.equal(sent[i], buffer.readUInt8(i)); + } + test.done(); + }, + servoConfig: function(test) { + test.expect(4); + + test.equal(this.particle.pins[10].pwm.servoMin, 600); + test.equal(this.particle.pins[10].pwm.servoMax, 2400); + + this.particle.servoConfig("A0", 1000, 2000); + + test.equal(this.particle.pins[10].pwm.servoMin, 1000); + test.equal(this.particle.pins[10].pwm.servoMax, 2000); + test.done(); } };