Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions examples/servo/index.js
Original file line number Diff line number Diff line change
@@ -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 (
<Servo
min={0}
max={180}
value={90}
/>
);
}
}

ReactHardware.render(
<Application />,
getPort(),
(inst) => {
console.log('Rendered <%s />', 'Servo');
}
);

72 changes: 72 additions & 0 deletions examples/servo/index.js.bak
Original file line number Diff line number Diff line change
@@ -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 (
<pin
{...this.props}
mode={'SERVO'}
value={value}
/>
);
},

});

class Application extends React.Component {
render():ReactElement {
return (
<SweepingServo
pin={9}
range={[0, 100]}
duration={1000}
/>
);
}
}

ReactHardware.render(
<Application />,
getPort(),
(inst) => {
console.log('Rendered <%s />', 'SweepingServo');
}
);

2 changes: 1 addition & 1 deletion flow-typed/jsx-intrinsics.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<{}>;
Expand Down
39 changes: 39 additions & 0 deletions src/components/Servo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* ReactHardware <Switch /> component.
*
* <Switch
* onChange={({value}) => 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 (
<pin
pin={this.props.pin}
min={this.props.min}
max={this.props.max}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't these new properties need to be included in a viewConfig?

value={this.props.value}
mode={'SERVO'}
/>
);
}
}

export default Servo;

1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
23 changes: 18 additions & 5 deletions src/firmata/HardwareManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
57 changes: 43 additions & 14 deletions src/firmata/__tests__/HardwareManager-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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',
Expand All @@ -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);
});
});
});