diff --git a/examples/servo/index.js b/examples/servo/index.js
new file mode 100644
index 0000000..220be91
--- /dev/null
+++ b/examples/servo/index.js
@@ -0,0 +1,29 @@
+/**
+ * servo.
+ * This is a simple example of a servo animating open and closed.
+ */
+
+import React from 'react';
+import {getPort} from '../port';
+import ReactHardware, {Servo} from '../../src';
+
+class Application extends React.Component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+ReactHardware.render(
+ ,
+ getPort(),
+ (inst) => {
+ console.log('Rendered <%s />', 'Servo');
+ }
+);
+
diff --git a/examples/servo/index.js.bak b/examples/servo/index.js.bak
new file mode 100644
index 0000000..094f483
--- /dev/null
+++ b/examples/servo/index.js.bak
@@ -0,0 +1,72 @@
+/**
+ * servo.
+ * This is a simple example of a servo animating open and closed.
+ */
+
+import React from 'react';
+import {getPort} from '../port';
+import ReactHardware from '../../src';
+import tweenState from 'react-tween-state';
+
+var SweepingServo = React.createClass({
+ mixins: [tweenState.Mixin],
+
+ getInitialState() {
+ return {value: this.props.duration[0]};
+ },
+
+ getDefaultProps() {
+ return {
+ range: [0, 180],
+ duration: 66.6666 * 20,
+ };
+ },
+
+ tween() {
+ this.tweenState('value', {
+ easing: tweenState.easingTypes.linear,
+ duration: this.props.duration,
+ endValue: this.state.value === this.props.range[0] ?
+ this.props.range[1] :
+ this.props.range[0],
+ onEnd: this.tween,
+ });
+ },
+
+ componentDidMount() {
+ this.tween();
+ },
+
+ render() {
+ const value = this.getTweeningValue('value');
+ return (
+
+ );
+ },
+
+});
+
+class Application extends React.Component {
+ render():ReactElement {
+ return (
+
+ );
+ }
+}
+
+ReactHardware.render(
+ ,
+ getPort(),
+ (inst) => {
+ console.log('Rendered <%s />', 'SweepingServo');
+ }
+);
+
diff --git a/flow-typed/jsx-intrinsics.js b/flow-typed/jsx-intrinsics.js
index 65dba57..36be12a 100644
--- a/flow-typed/jsx-intrinsics.js
+++ b/flow-typed/jsx-intrinsics.js
@@ -14,7 +14,7 @@ declare type $JSXIntrinsics = {
mode: 'INPUT'|'OUTPUT'|'ANALOG'|'PWM'| 'SERVO'|'SHIFT'|'I2C'|'ONEWIRE'|'STEPPER'|'IGNORE'|'UNKNOWN';
pin: number|string;
value?: number;
- onRead: ?(...arg:any) => any;
+ onRead?: (...arg:any) => any;
}>;
container: JSXHelper<{}>;
diff --git a/src/components/Servo.js b/src/components/Servo.js
new file mode 100644
index 0000000..f31b812
--- /dev/null
+++ b/src/components/Servo.js
@@ -0,0 +1,39 @@
+/**
+ * ReactHardware component.
+ *
+ * console.log('Switch state changed to %s', value)}
+ * />
+ *
+ * @flow
+ **/
+
+import type {HardwareEvent} from '../types';
+import React, {Component} from 'react';
+
+type P = {
+ pin: number;
+ min?: number;
+ max?: number;
+ value: number;
+}
+
+class Servo extends Component {
+ props: P;
+ static defaultProps: {};
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default Servo;
+
diff --git a/src/components/index.js b/src/components/index.js
index cbfc184..8ac1c3d 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -4,4 +4,5 @@ export {default as Button} from './Button';
export {default as Led} from './Led';
export {default as Potentiometer} from './Potentiometer';
export {default as RGBLed} from './RGBLed';
+export {default as Servo} from './Servo';
export {default as Switch} from './Switch';
diff --git a/src/firmata/HardwareManager.js b/src/firmata/HardwareManager.js
index bf74b74..cd815cb 100644
--- a/src/firmata/HardwareManager.js
+++ b/src/firmata/HardwareManager.js
@@ -212,11 +212,24 @@ export const setPayloadForPin = (
// console.log(`set pinMode of "%s" to "%s"`, payload.pin, payload.mode);
const normalizedPin = analogToDigital(payload.pin);
board.pinMode(normalizedPin, MODES[payload.mode]);
- const communicationType = FIRMATA_COMMUNICATION_METHOD[MODES[payload.mode]];
- if (typeof payload.value !== 'undefined') {
- // console.log(`${communicationType}Write to "%s" with "%s"`, payload.pin, payload.value);
- /* $FlowFixMe computed property call */
- board[`${communicationType}Write`](payload.pin, +payload.value);
+ const communicationType = FIRMATA_COMMUNICATION_METHOD[MODES[payload.mode]].toLowerCase();
+
+ switch (communicationType) {
+ case 'servo':
+ board.servoConfig(normalizedPin, +payload.min, +payload.max);
+ board.servoWrite(normalizedPin, +payload.value);
+ break;
+ case 'digital':
+ board.digitalWrite(normalizedPin, +payload.value);
+ break;
+ case 'analog':
+ board.analogWrite(normalizedPin, +payload.value);
+ break;
+ // todo
+ // * i2c
+ // * debugString
+ // * serial
+ // * sysex
}
if (payload.onRead) {
diff --git a/src/firmata/__tests__/HardwareManager-test.js b/src/firmata/__tests__/HardwareManager-test.js
index c053d98..20632f3 100644
--- a/src/firmata/__tests__/HardwareManager-test.js
+++ b/src/firmata/__tests__/HardwareManager-test.js
@@ -71,11 +71,15 @@ describe('HardwareManager', () => {
beforeEach(() => {
// default Firmata pin mapping is an Uno
hw = new Firmata();
+ // mock-firmata uses board-io that does not call this.addListener(...)
+ hw.digitalRead = require('firmata').prototype.digitalRead.bind(hw);
spyOn(hw, 'pinMode');
spyOn(hw, 'digitalWrite');
- spyOn(hw, 'digitalRead');
+ spyOn(hw, 'digitalRead').and.callThrough();
spyOn(hw, 'analogWrite');
spyOn(hw, 'analogRead');
+ spyOn(hw, 'servoWrite');
+ spyOn(hw, 'servoConfig');
});
it('should handle an easy case', () => {
@@ -119,10 +123,34 @@ describe('HardwareManager', () => {
expect(connection.readers[0].call).toBe(payload.onRead);
});
- // TODO: mock-firmata emit should work
- xit('should handle changing read handlers', () => {
- const before = jasmine.createSpy();
- const after = jasmine.createSpy();
+ it('should handle servo write', () => {
+ const payload = {
+ pin: 0,
+ value: 120,
+ mode: 'SERVO',
+ };
+ const connection = {board: hw, readers: []};
+ setPayloadForPin(connection, payload);
+ expect(hw.servoWrite).toHaveBeenCalledWith(0, 120);
+ });
+
+ it('should map servo properties to servoConfig', () => {
+ const payload = {
+ pin: 0,
+ value: 120,
+ mode: 'SERVO',
+ min: 0,
+ max: 180,
+ };
+ const connection = {board: hw, readers: []};
+ setPayloadForPin(connection, payload);
+ expect(hw.servoConfig).toHaveBeenCalledWith(0, 0, 180);
+ expect(hw.servoWrite).toHaveBeenCalledWith(0, 120);
+ });
+
+ it('should handle changing read handlers', () => {
+ const before = jasmine.createSpy('before');
+ const after = jasmine.createSpy('after');
const initialPayload = {
pin: 0,
mode: 'OUTPUT',
@@ -134,18 +162,19 @@ describe('HardwareManager', () => {
mode: 'OUTPUT',
onRead: after,
};
-
- setPayloadForPin({board: hw, readers: []}, initialPayload);
-
+ const connection = {board: hw, readers: []};
+ setPayloadForPin(connection, initialPayload);
expect(hw.pinMode).toHaveBeenCalled();
-
- expect(hw.digitalRead).toHaveBeenCalledWith(initialPayload.pin, initialPayload.onRead);
+ expect(hw.digitalRead).toHaveBeenCalledTimes(1);
hw.emit('digital-read-0', Infinity);
- expect(before).toHaveBeenCalledWith(Infinity);
+ expect(initialPayload.onRead).toHaveBeenCalledWith(Infinity);
- setPayloadForPin({board: hw}, updatePayload);
- hw.emit('digital-read-1', 3.1415);
- expect(before).toHaveBeenCalledWith(3.1415);
+ setPayloadForPin(connection, updatePayload);
+ hw.emit('digital-read-0', 3.1415);
+ expect(updatePayload.onRead).toHaveBeenCalledWith(3.1415);
+ expect(connection.readers[0].call).toBe(after);
+ expect(hw.digitalRead).toHaveBeenCalledTimes(1);
});
});
});
+