diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ba17b3..048ece0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ ## j5e Repo Anatomy -One goal of j5e is write once, run anywhere. We are building toward this goal before TC-53 IOCP conformant platforms exist. To that end, the structure of this repo may change to help us support more platforms. The JS is the same across platforms, but the different implementations of ECMAScript Modules may require special concessions (package.json vs manifest.json and varying module resolution schemes). +One goal of j5e is write once, run anywhere. We are building toward this goal before TC-53 IOCP conformant platforms exist. To that end, the structure of this repo may change to help us support more platforms. The JS is the same across platforms, but the different implementations of ECMAScript Modules may require special concessions (package.json vs manifest.json and varying module resolution schemes for example). ### Special Scaffolding j5e is designed to import just the parts it needs, so subpath exports in node.js are absolutely necessary. To make everything link up you will need to symlink your dev folder to itself. From the root run ```npm link``` and then ```npm link j5e```. This will allow tests to import subpaths, and for subpaths to import their siblings. @@ -11,55 +11,4 @@ j5e is designed to import just the parts it needs, so subpath exports in node.js Tests are run using Mocha, Chai, Sinon, and node's built-in Assert module. Each class has its own js file in the ```./tests/``` folder. You can run tests by running ```npm run test``` or on a specific module by running ```npm run test ./test/rgb``` for example. **Test Organiztion** -Test files should be organized with the following heirarchy - -````js -describe('className', function() { - - describe('Instantiation', function() { - // All tests related to default instantiation - /* describe('Options', function() { - describe('someOptionProperty', async function() { - - it('should be configured appropriately for the option', async function() { - // ... - }); - // [ All other tests related to sink ] - }); - }); */ - // [ All other options, each with it's own describe ] - } - - describe('Properties', function() { - /* describe('someProperty', function() { - it('should respong with the property value', async function() { - // ... - }); - [ all other tests related to someProperty ] - }); - */ - // [ All other properties, each with it's own describe ] - }); - - describe('Methods', function() { - /* describe('doSomething', function() { - it('should doSomething', async function() { - // ... - }); - // [ all other tests related to doSomething ] - }); */ - // [ All other methods, each with it's own describe ] - }); - - describe('Events', function() { - /* describe('someEvent', function() { - it('should fire the event at the right time', async function() { - // ... - }); - // [ all other tests related to the event ] - }); */ - // [ All other Events, each with it's own describe ] - }); - -}); -```` +Test files should be organized with the heirarchy described in [/build/templates/test.js](build/templates/test.js). diff --git a/build/templates/test.js b/build/templates/test.js new file mode 100644 index 0000000..87d04fe --- /dev/null +++ b/build/templates/test.js @@ -0,0 +1,77 @@ +import assert from "assert"; +import sinon from "sinon"; +import { Digital, PWM } from "@dtex/mock-io"; +import ClassName from "j5e/classname"; + +describe("ClassName", function() { + + describe("Instantiation", function() { + + // All tests related to default instantiation + + describe("Options", function() { + + describe("someOptionProperty", async function() { + + it("should be configured appropriately for the option", async function() { + // ... + }); + + // [ All other tests related to this option ] + + }); + }); + + // [ All other options, each with it's own describe ] + + }); + + describe("Properties", function() { + + describe("someProperty", function() { + + it("should respond with the property value", async function() { + // ... + }); + + // [ all other tests related to someProperty ] + + }); + + // [ All other properties, each with it's own describe ] + + }); + + describe("Methods", function() { + + describe("someMethod", function() { + + it("should do the right thing", async function() { + // ... + }); + + // [ all other tests related to someMethod ] + + }); + + // [ All other methods, each with it's own describe ] + + }); + + describe("Events", function() { + + describe("someEvent", function() { + + it("should emit the event at the appropriate time", async function() { + // ... + }); + + // [ all other tests related to someEvent ] + + }); + + // [ All other events, each with it's own describe ] + + }); + +}); diff --git a/docs/module-j5e_servo-Servo.html b/docs/module-j5e_servo-Servo.html index 0a57c5e..cc0ff77 100644 --- a/docs/module-j5e_servo-Servo.html +++ b/docs/module-j5e_servo-Servo.html @@ -170,9 +170,9 @@
Parameters
deadband Array.<number> <optional>
- [1500,1500] + [90,90] - The range at which a continuos motion servo will not turn + The degree range at which a continuos motion servo will not turn @@ -321,6 +321,13 @@
Properties
The corrected position (factors in offset and invert) + + pulseWidth + Array.<object> + + The corrected pulseWidth (factors in offset and invert) + + @@ -384,7 +391,7 @@
Properties
-

servo/index.js, line 68

+

servo/index.js, line 69

@@ -503,7 +510,7 @@
Parameters
-

servo/index.js, line 213

+

servo/index.js, line 208

@@ -595,7 +602,7 @@
Parameters
-

servo/index.js, line 314

+

servo/index.js, line 310

@@ -677,7 +684,7 @@
Parameters
-

servo/index.js, line 336

+

servo/index.js, line 332

@@ -759,7 +766,7 @@
Parameters
-

servo/index.js, line 358

+

servo/index.js, line 354

@@ -851,7 +858,7 @@
Parameters
-

servo/index.js, line 390

+

servo/index.js, line 386

@@ -908,7 +915,7 @@

home @@ -993,7 +1000,7 @@
Parameters
@@ -1042,7 +1049,7 @@

stop @@ -1124,7 +1131,7 @@
Parameters
@@ -1206,7 +1213,7 @@
Parameters
diff --git a/docs/servo_index.js.html b/docs/servo_index.js.html index aa4b3ef..e83d88a 100644 --- a/docs/servo_index.js.html +++ b/docs/servo_index.js.html @@ -131,7 +131,7 @@

servo/index.js

* @param {(string|constructor)} [options.io=builtin/pwm] - If passing an object, a string specifying a path to the IO provider or a constructor * @param {string} [options.type="standard"] - Type of servo ("standard" or "continuous") * @param {number[]} [options.pwmRange=[600, 2400]] - The pulse width range in microseconds - * @param {number[]} [options.deadband=[1500,1500]] - The range at which a continuos motion servo will not turn + * @param {number[]} [options.deadband=[90,90]] - The degree range at which a continuos motion servo will not turn * @param {number[]} [options.range=[0, 180]] - The allowed range of motion in degrees * @param {number[]} [options.deviceRange=[0, 180]] - The physical range of the servo in degrees * @param {number} [options.startAt="Any value within options.range"] - The desired start position of the servo @@ -146,6 +146,7 @@

servo/index.js

* @property {object[]} last.timestamp - Timestamp of position update * @property {object[]} last.target - The user requested position * @property {object[]} last.degrees - The corrected position (factors in offset and invert) + * @property {object[]} last.pulseWidth - The corrected pulseWidth (factors in offset and invert) * @property {number} position - The most recent request and corrected position (factors in offset and invert) * @example * <caption>Sweep a servo back and forth</caption> @@ -175,14 +176,8 @@

servo/index.js

}); this.#state.pwmRange = options.pwmRange || [600, 2400]; - - // This line is a hack until we can write in µs - this.#state.pwmRange = [34, 120]; this.#state.degreeRange = options.degreeRange || [0, 180]; - this.#state.deadband = options.deadband || [1500, 1500]; - - // This line is a hack until we can write in µs - this.#state.deadband = options.deadband || [77, 77]; + this.#state.deadband = options.deadband || [90, 90]; this.#state.offset = options.offset || 0; this.#state.startAt = options.startAt || (this.#state.degreeRange[1] - this.#state.degreeRange[0]) / 2; this.#state.range = options.range || [0 - this.offset, 180 - this.offset]; @@ -218,8 +213,6 @@

servo/index.js

this.initialize(options); - // If "startAt" is defined and center is falsy - // set servo to min or max degrees if (typeof options.startAt !== "undefined") { this.to(options.startAt); } else { @@ -248,7 +241,7 @@

servo/index.js

/** * Calls the write param on the IO instance for this servo. - * @param {number} degrees - The absolute position to move a servo to + * @param {number} pulseWidth - The target pulseWidth * @returns {Servo} * @ignore */ @@ -259,17 +252,19 @@

servo/index.js

return this; } - // Map value from degreeRange to pwmRange - let microseconds = map( - degrees, - this.#state.degreeRange[0], this.#state.degreeRange[1], - this.#state.pwmRange[0], this.#state.pwmRange[1] - ); + // Presumably not all IO's will support writeMicroseconds + if (this.io.writeMicroseconds) { + + let microseconds = map(degrees, this.#state.degreeRange[0], this.#state.degreeRange[1], this.#state.pwmRange[0], this.#state.pwmRange[1]); + this.io.write(microseconds | 0); + + } else { + + let value = map(degrees, this.#state.degreeRange[0], this.#state.degreeRange[1], 0, this.io.resolution ** 2 - 1); + this.io.write(value | 0); - // Restrict values to integers - microseconds |= 0; + } - this.io.write(microseconds); } /** @@ -378,6 +373,7 @@

servo/index.js

degrees: degrees, target: target }); + } } @@ -609,7 +605,7 @@

servo/index.js

*/ cw(speed = 1) { speed = constrain(speed, 0, 1); - speed = map(speed, 0, 1, this.#state.deadband[1] + 1, this.#state.pwmRange[1]); + speed = map(speed, 0, 1, this.#state.deadband[1] + 1, this.#state.degreeRange[1]); return this.to(speed); } diff --git a/examples/servo-sweep/main.js b/examples/servo-sweep/main.js new file mode 100644 index 0000000..7a920a6 --- /dev/null +++ b/examples/servo-sweep/main.js @@ -0,0 +1,4 @@ +import Servo from "j5e/servo"; + +const servo = await new Servo(12); +servo.sweep(); \ No newline at end of file diff --git a/examples/servo-sweep/manifest.json b/examples/servo-sweep/manifest.json new file mode 100644 index 0000000..b29e394 --- /dev/null +++ b/examples/servo-sweep/manifest.json @@ -0,0 +1,9 @@ +{ + "include": [ + "$(MODDABLE)/modules/io/manifest.json", + "$(j5e)/lib/servo/manifest.json" + ], + "modules": { + "*": "./main" + } +} \ No newline at end of file diff --git a/lib/servo/index.js b/lib/servo/index.js index 162ee6d..784f343 100644 --- a/lib/servo/index.js +++ b/lib/servo/index.js @@ -35,7 +35,7 @@ class Servo extends Emitter { * @param {(string|constructor)} [options.io=builtin/pwm] - If passing an object, a string specifying a path to the IO provider or a constructor * @param {string} [options.type="standard"] - Type of servo ("standard" or "continuous") * @param {number[]} [options.pwmRange=[600, 2400]] - The pulse width range in microseconds - * @param {number[]} [options.deadband=[1500,1500]] - The range at which a continuos motion servo will not turn + * @param {number[]} [options.deadband=[90,90]] - The degree range at which a continuos motion servo will not turn * @param {number[]} [options.range=[0, 180]] - The allowed range of motion in degrees * @param {number[]} [options.deviceRange=[0, 180]] - The physical range of the servo in degrees * @param {number} [options.startAt="Any value within options.range"] - The desired start position of the servo @@ -50,6 +50,7 @@ class Servo extends Emitter { * @property {object[]} last.timestamp - Timestamp of position update * @property {object[]} last.target - The user requested position * @property {object[]} last.degrees - The corrected position (factors in offset and invert) + * @property {object[]} last.pulseWidth - The corrected pulseWidth (factors in offset and invert) * @property {number} position - The most recent request and corrected position (factors in offset and invert) * @example * Sweep a servo back and forth @@ -79,14 +80,8 @@ class Servo extends Emitter { }); this.#state.pwmRange = options.pwmRange || [600, 2400]; - - // This line is a hack until we can write in µs - this.#state.pwmRange = [34, 120]; this.#state.degreeRange = options.degreeRange || [0, 180]; - this.#state.deadband = options.deadband || [1500, 1500]; - - // This line is a hack until we can write in µs - this.#state.deadband = options.deadband || [77, 77]; + this.#state.deadband = options.deadband || [90, 90]; this.#state.offset = options.offset || 0; this.#state.startAt = options.startAt || (this.#state.degreeRange[1] - this.#state.degreeRange[0]) / 2; this.#state.range = options.range || [0 - this.offset, 180 - this.offset]; @@ -122,8 +117,6 @@ class Servo extends Emitter { this.initialize(options); - // If "startAt" is defined and center is falsy - // set servo to min or max degrees if (typeof options.startAt !== "undefined") { this.to(options.startAt); } else { @@ -152,7 +145,7 @@ class Servo extends Emitter { /** * Calls the write param on the IO instance for this servo. - * @param {number} degrees - The absolute position to move a servo to + * @param {number} pulseWidth - The target pulseWidth * @returns {Servo} * @ignore */ @@ -163,17 +156,19 @@ class Servo extends Emitter { return this; } - // Map value from degreeRange to pwmRange - let microseconds = map( - degrees, - this.#state.degreeRange[0], this.#state.degreeRange[1], - this.#state.pwmRange[0], this.#state.pwmRange[1] - ); + // Presumably not all IO's will support writeMicroseconds + if (this.io.writeMicroseconds) { + + let microseconds = map(degrees, this.#state.degreeRange[0], this.#state.degreeRange[1], this.#state.pwmRange[0], this.#state.pwmRange[1]); + this.io.write(microseconds | 0); + + } else { + + let value = map(degrees, this.#state.degreeRange[0], this.#state.degreeRange[1], 0, this.io.resolution ** 2 - 1); + this.io.write(value | 0); - // Restrict values to integers - microseconds |= 0; + } - this.io.write(microseconds); } /** @@ -282,6 +277,7 @@ class Servo extends Emitter { degrees: degrees, target: target }); + } } @@ -513,7 +509,7 @@ class Servo extends Emitter { */ cw(speed = 1) { speed = constrain(speed, 0, 1); - speed = map(speed, 0, 1, this.#state.deadband[1] + 1, this.#state.pwmRange[1]); + speed = map(speed, 0, 1, this.#state.deadband[1] + 1, this.#state.degreeRange[1]); return this.to(speed); } diff --git a/package.json b/package.json index 98c88d3..0e6478e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "j5e", - "version": "0.4.3", + "version": "0.4.4", "description": "j5e is a device framework built for ECMA TC-53's IO pattern", "main": "index.js", "exports": { diff --git a/test/servo.js b/test/servo.js index 0ebde32..f25d773 100644 --- a/test/servo.js +++ b/test/servo.js @@ -1,6 +1,6 @@ import assert from "assert"; import sinon from "sinon"; -import { Digital, PWM } from "@dtex/mock-io"; +import { PWM } from "@dtex/mock-io"; import Servo from "j5e/servo"; describe("Servo - Standard", function() { @@ -17,6 +17,103 @@ describe("Servo - Standard", function() { }); + describe("Options", function() { + + // pwmRange + // deadband + // Range + // deviceRange + // startAt + // offset + // invert + // center + + describe("type", async function() { + + it("should behave as contiuous rotation servo when type is \"continuous\"", async function() { + const servo = await new Servo({ + pin: 12, + io: PWM, + type: "continuous" + }); + + servo.cw(); + assert.equal(servo.position, 180); + + servo.stop(); + assert.equal(servo.position, 90); + }); + + }); + }); + + // [ All other options, each with it's own describe ] + + }); + + describe("Properties", function() { + + // history + // last + // position + + describe("someProperty", function() { + + it("should respond with the property value", async function() { + // ... + }); + + // [ all other tests related to someProperty ] + + }); + + // [ All other properties, each with it's own describe ] + + }); + + describe("Methods", function() { + + // to + // step + // min + // max + // center + // home + // sweep + // stop + // cw + // ccw + // normalize + // rangeToKeyFrames + describe("someMethod", function() { + + it("should do the right thing", async function() { + // ... + }); + + // [ all other tests related to someMethod ] + + }); + + // [ All other methods, each with it's own describe ] + + }); + + describe("Events", function() { + + //moveComplete + describe("someEvent", function() { + + it("should emit the event at the appropriate time", async function() { + // ... + }); + + // [ all other tests related to someEvent ] + + }); + + // [ All other events, each with it's own describe ] + }); });