firmata 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in firmata.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 'Mike Breen'
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Firmata
2
+
3
+ A Ruby implementation of the [Firmata protocol](http://firmata.org/wiki/V2.2ProtocolDetails).
4
+
5
+ This library is inspired by the awesome [firmata](http://jgautier.github.com/firmata/) by [jgautier](https://github.com/jgautier).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'firmata'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install firmata
20
+
21
+ ## Prerequisites
22
+
23
+ 1. Download the [Arduio IDE](http://www.arduino.cc/en/Main/Software) for your OS
24
+ 2. Plug in your Arduino via USB
25
+ 3. Open the Arduino IDE, select: File > Examples > Firmata > StandardFirmata
26
+ 4. Click the Upload button
27
+ 5. Make note of the serial port: Tools > Serial Port
28
+
29
+ ## Usage
30
+
31
+ Here is a simple example using IRB that will turn pin 13 on and off.
32
+ (Replace xxxxx with the USB port from step 5 in Prerequisites)
33
+
34
+ 1.9.3p194 :001 > require 'firmata'
35
+ 1.9.3p194 :002 > board = Firmata::Board.new('/dev/tty.usbmodemxxxxx')
36
+ 1.9.3p194 :003 > board.connect
37
+ 1.9.3p194 :004 > board.connected?
38
+ => true
39
+ 1.9.3p194 :005 > board.version
40
+ => "2.3"
41
+ 1.9.3p194 :006 > board.firmware_name
42
+ => "StandardFirmata"
43
+ 1.9.3p194 :007 > board.digital_write(13, Firmata::Board::HIGH)
44
+ 1.9.3p194 :008 > board.digital_write(13, Firmata::Board::LOW)
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs.push "lib"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => [:test]
data/firmata.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/firmata/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["'Mike Breen'"]
6
+ gem.email = ["hardbap@gmail.com"]
7
+ gem.description = %q{A lib for working with the Firmata protocol in Ruby.}
8
+ gem.summary = %q{}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "firmata"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Firmata::VERSION
17
+
18
+ gem.add_runtime_dependency("serialport", ["~> 1.1.0"])
19
+ gem.add_runtime_dependency("event_spitter")
20
+ end
@@ -0,0 +1,340 @@
1
+ require 'serialport'
2
+ require 'event_spitter'
3
+
4
+ module Firmata
5
+ class Board
6
+ include EventSpitter
7
+
8
+ # Internal: Data structure representing a pin on Arduino.
9
+ Pin = Struct.new(:supportedModes, :mode, :value, :analog_channel)
10
+
11
+ # Public: Fixnum byte for pin mode input.
12
+ INPUT = 0x00
13
+ # Public: Fixnum byte for pin mode output.
14
+ OUTPUT = 0x01
15
+ # Public: Fixnum byte for pin mode analog.
16
+ ANALOG = 0x02
17
+ # Public: Fixnum byte for pin mode pulse width modulation.
18
+ PWM = 0x03
19
+ # Public: Fixnum byte for pin mode servo.
20
+ SERVO = 0x04
21
+
22
+ LOW = 0
23
+ HIGH = 1
24
+
25
+ # Internal: Fixnum byte command for protocol version
26
+ REPORT_VERSION = 0xF9
27
+ # Internal: Fixnum byte command for system reset
28
+ SYSTEM_RESET = 0xFF
29
+ # Internal: Fixnum byte command for digital I/O message
30
+ DIGITAL_MESSAGE = 0x90
31
+ # Pubilc: Fixnum byte for range for digital pins for digital 2 byte data format
32
+ DIGITAL_MESSAGE_RANGE = 0x90..0x9F
33
+ # Internal: Fixnum byte command for an analog I/O message
34
+ ANALOG_MESSAGE = 0xE0
35
+ # Internal: Fixnum byte range for analog pins for analog 14-bit data format
36
+ ANALOG_MESSAGE_RANGE = 0xE0..0xEF
37
+ # Internal: Fixnum byte command to report analog pin
38
+ REPORT_ANALOG = 0xC0
39
+ # Internal: Fixnum byte command to report digital port
40
+ REPORT_DIGITAL = 0xD0
41
+ # Internal: Fixnum byte command to set pin mode (I/O)
42
+ PIN_MODE = 0xF4
43
+
44
+ # Internal: Fixnum byte command for start of Sysex message
45
+ START_SYSEX = 0xF0
46
+ # Internal: Fixnum byte command for end of Sysex message
47
+ END_SYSEX = 0xF7
48
+ # Internal: Fixnum byte sysex command for capabilities query
49
+ CAPABILITY_QUERY = 0x6B
50
+ # Internal: Fixnum byte sysex command for capabilities response
51
+ CAPABILITY_RESPONSE = 0x6C
52
+ # Internal: Fixnum byte sysex command for pin state query
53
+ PIN_STATE_QUERY = 0x6D
54
+ # Internal: Fixnum byte sysex command for pin state response
55
+ PIN_STATE_RESPONSE = 0x6E
56
+ # Internal: Fixnum byte sysex command for analog mapping query
57
+ ANALOG_MAPPING_QUERY = 0x69
58
+ # Internal: Fixnum byte sysex command for analog mapping response
59
+ ANALOG_MAPPING_RESPONSE = 0x6A
60
+ # Internal: Fixnum byte sysex command for firmware query and response
61
+ FIRMWARE_QUERY = 0x79
62
+
63
+ # Public: Returns the SerialPort port the Arduino is attached to.
64
+ attr_reader :serial_port
65
+ # Public: Returns the Array of pins on Arduino.
66
+ attr_reader :pins
67
+ # Public: Returns the Array of analog pins on Arduino.
68
+ attr_reader :analog_pins
69
+ # Public: Returns the String firmware name of Arduion.
70
+ attr_reader :firmware_name
71
+
72
+ def initialize(port)
73
+ @serial_port = port.is_a?(String) ? SerialPort.new(port, 57600, 8, 1, SerialPort::NONE) : port
74
+ @serial_port.read_timeout = 2
75
+ @major_version = 0
76
+ @minor_version = 0
77
+ @pins = []
78
+ @analog_pins = []
79
+ @connected = false
80
+ end
81
+
82
+ def connected?
83
+ @connected
84
+ end
85
+
86
+ def connect
87
+ unless @connected
88
+ self.once('report_version', ->() do
89
+ self.once('firmware_query', ->() do
90
+ self.once('capability_query', ->() do
91
+ self.once('analog_mapping_query', ->() do
92
+ @connected = true
93
+ emit('ready')
94
+ end)
95
+ query_analog_mapping
96
+ end)
97
+ query_capabilities
98
+ end)
99
+ end)
100
+ end
101
+ end
102
+
103
+ # Internal: Write data to the underlying serial port.
104
+ #
105
+ # commands - Zero or more byte commands to be written.
106
+ #
107
+ # Examples
108
+ #
109
+ # write(START_SYSEX, CAPABILITY_QUERY, END_SYSEX)
110
+ #
111
+ # Returns nothing.
112
+ def write(*commands)
113
+ serial_port.write(commands.map(&:chr).join)
114
+ end
115
+
116
+ # Internal: Read data from the underlying serial port.
117
+ #
118
+ # Returns Enumerator of bytes.
119
+ def read
120
+ serial_port.bytes
121
+ end
122
+
123
+ # Internal: Process a series of bytes.
124
+ #
125
+ # bytes: An Enumerator of bytes (default: read())
126
+ #
127
+ # Returns nothing.
128
+ def process(bytes = read)
129
+ bytes.each do |byte|
130
+ case byte
131
+ when REPORT_VERSION
132
+ @major_version = bytes.next
133
+ @minor_version = bytes.next
134
+
135
+ emit('report_version')
136
+
137
+ when ANALOG_MESSAGE_RANGE
138
+ least_significant_byte = bytes.next
139
+ most_significant_byte = bytes.next
140
+
141
+ value = least_significant_byte | (most_significant_byte << 7)
142
+ pin = byte & 0x0F
143
+
144
+ if analog_pin = analog_pins[pin]
145
+ pins[analog_pin].value = value
146
+ end
147
+
148
+ when START_SYSEX
149
+ current_buffer = [byte]
150
+ begin
151
+ current_buffer.push(bytes.next)
152
+ end until current_buffer.last == END_SYSEX
153
+
154
+ command = current_buffer[1]
155
+
156
+ case command
157
+ when CAPABILITY_RESPONSE
158
+ supportedModes = 0
159
+ n = 0
160
+
161
+ current_buffer.slice(2, current_buffer.length - 3).each do |byte|
162
+ if byte == 127
163
+ modesArray = []
164
+ # the pin modes
165
+ [ INPUT, OUTPUT, ANALOG, PWM, SERVO ].each do |mode|
166
+ modesArray.push(mode) unless (supportedModes & (1 << mode)).zero?
167
+ end
168
+
169
+ @pins.push(Pin.new(modesArray, OUTPUT, 0))
170
+
171
+ supportedModes = 0
172
+ n = 0
173
+ next
174
+ end
175
+
176
+ supportedModes |= (1 << byte) if n.zero?
177
+
178
+ n ^= 1
179
+ end
180
+
181
+ emit('capability_query')
182
+
183
+ when ANALOG_MAPPING_RESPONSE
184
+ pin_index = 0
185
+
186
+ current_buffer.slice(2, current_buffer.length - 3).each do |byte|
187
+
188
+ @pins[pin_index].analog_channel = byte
189
+
190
+ @analog_pins.push(pin_index) unless byte == 127
191
+
192
+ pin_index += 1
193
+ end
194
+
195
+ emit('analog_mapping_query')
196
+
197
+ when PIN_STATE_RESPONSE
198
+ pin = pins[current_buffer[2]]
199
+ pin.mode = current_buffer[3]
200
+ pin.value = current_buffer[4]
201
+
202
+ pin.value |= (current_buffer[5] << 7) if current_buffer.size > 6
203
+
204
+ pin.value |= (current_buffer[6] << 14) if current_buffer.size > 7
205
+
206
+ when FIRMWARE_QUERY
207
+ @firmware_name = current_buffer.slice(4, current_buffer.length - 5).reject { |b| b.zero? }.map(&:chr).join
208
+ emit('firmware_query')
209
+
210
+ else
211
+ # TODO decide what to do with unknown message
212
+ end
213
+ end
214
+ end
215
+ rescue StopIteration
216
+ # do nadda
217
+ end
218
+
219
+ # Public: Send a SYSTEM_RESET to the Arduino
220
+ #
221
+ # Returns nothing.
222
+ def reset
223
+ write(SYSTEM_RESET)
224
+ end
225
+
226
+ # Public: Set the mode for a pin.
227
+ #
228
+ # pin - The Integer pin to set.
229
+ # mode - The Fixnum mode (INPUT, OUTPUT, ANALOG, PWM or SERVO)
230
+ #
231
+ # Examples
232
+ #
233
+ # pin_mode(13, OUTPUT)
234
+ #
235
+ # Returns nothing.
236
+ def pin_mode(pin, mode)
237
+ pins[pin].mode = mode
238
+ write(PIN_MODE, pin, mode)
239
+ end
240
+
241
+ # Public: Write a value to a digital pin.
242
+ #
243
+ # pin - The Integer pin to write to.
244
+ # value - The value to write (HIGH or LOW).
245
+ #
246
+ # Returns nothing.
247
+ def digital_write(pin, value)
248
+ port = (pin / 8).floor
249
+ port_value = 0
250
+
251
+ @pins[pin].value = value
252
+
253
+ 8.times do |i|
254
+ port_value |= (1 << i) unless @pins[8 * port + i].value.zero?
255
+ end
256
+
257
+ write(DIGITAL_MESSAGE | port, port_value & 0x7F, (port_value >> 7) & 0x7F)
258
+ end
259
+
260
+ # Public: Ask the Arduino to sleep for a number of seconds.
261
+ #
262
+ # seconds - The Integer seconds to sleep for.
263
+ #
264
+ # Returns nothing.
265
+ def delay(seconds)
266
+ sleep(seconds)
267
+ end
268
+
269
+ # Public: The major and minor firmware version on the board. Will report as
270
+ # "0.0" if report_version command has not been run.
271
+ #
272
+ # Returns String the firmware version as "minor.major".
273
+ def version
274
+ [@major_version, @minor_version].join('.')
275
+ end
276
+
277
+ # Public: Ask the Arduino to report its version.
278
+ #
279
+ # Returns nothing.
280
+ def report_version
281
+ write(REPORT_VERSION)
282
+ end
283
+
284
+ # Public: Ask the Ardution for its firmware name.
285
+ #
286
+ # Returns nothing.
287
+ def query_firmware
288
+ write(FIRMWARE_QUERY)
289
+ end
290
+
291
+ # Public: Ask the Arduino for the current configuration of any pin.
292
+ #
293
+ # pin - The Integer pin to query on the board.
294
+ #
295
+ # Returns nothing.
296
+ def query_pin_state(pin)
297
+ write(START_SYSEX, PIN_STATE_QUERY, pin.to_i, END_SYSEX)
298
+ end
299
+
300
+ # Public: Ask the Arduino about its capabilities and current state.
301
+ #
302
+ # Returns nothing.
303
+ def query_capabilities
304
+ write(START_SYSEX, CAPABILITY_QUERY, END_SYSEX)
305
+ end
306
+
307
+ # Public: Ask the Arduino which pins (used with pin mode message) correspond to the analog channels.
308
+ #
309
+ # Returns nothing.
310
+ def query_analog_mapping
311
+ write(START_SYSEX, ANALOG_MAPPING_QUERY, END_SYSEX)
312
+ end
313
+
314
+ # Internal: Toggle the pin analog and digtal reporting off and on.
315
+ #
316
+ # state - The Integer to turn the pin on (1) or off (0).
317
+ #
318
+ # Returns nothing.
319
+ def toggle_pin_reporting(state)
320
+ 16.times do |i|
321
+ write(REPORT_DIGITAL | i, state)
322
+ write(REPORT_ANALOG | i, state)
323
+ end
324
+ end
325
+
326
+ # Public: Turn pin analog and digital reporting on.
327
+ #
328
+ # Returns nothing.
329
+ def turn_pin_reporting_on
330
+ toggle_pin_reporting(1)
331
+ end
332
+
333
+ # Public: Turn pin analog and digital reporting off.
334
+ #
335
+ # Returns nothing.
336
+ def turn_pin_reporting_off
337
+ toggle_pin_reporting(0)
338
+ end
339
+ end
340
+ end
@@ -0,0 +1,3 @@
1
+ module Firmata
2
+ VERSION = "0.0.2"
3
+ end
data/lib/firmata.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'firmata/version'
2
+ require 'firmata/board'
data/sample.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'bundler/setup'
2
+ require 'firmata'
3
+
4
+ board = Firmata::Board.new('/dev/tty.usbmodemfa131')
5
+
6
+ board.on('ready', ->() do
7
+
8
+ 10.times do
9
+ board.digital_write 13, Firmata::Board::HIGH
10
+ board.delay 1
11
+
12
+ board.digital_write 13, Firmata::Board::LOW
13
+ board.delay 1
14
+ end
15
+
16
+ end)
17
+
18
+ board.connect
19
+
20
+ Thread.new do
21
+ loop do
22
+ board.read
23
+ sleep 1
24
+ end
25
+ end
@@ -0,0 +1,198 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+
4
+ require_relative '../lib/firmata'
5
+ require_relative 'fake_serial_port'
6
+
7
+ class BoardTest < MiniTest::Unit::TestCase
8
+
9
+ def mock_serial_port(*args, &block)
10
+ mock_port = MiniTest::Mock.new
11
+ mock_port.expect(:read_timeout=, 2, [2])
12
+ mock_port.expect(:is_a?, false, [nil])
13
+
14
+ if block_given?
15
+ yield mock_port
16
+ else
17
+ expected = args.map(&:chr).join
18
+ mock_port.expect(:write, 1, [expected])
19
+ end
20
+
21
+ mock_port
22
+ end
23
+
24
+ def test_writing_report_version
25
+ mock_sp = mock_serial_port(Firmata::Board::REPORT_VERSION)
26
+
27
+ board = Firmata::Board.new(mock_sp)
28
+ board.report_version
29
+
30
+ mock_sp.verify
31
+ end
32
+
33
+ def test_processing_report_version
34
+ board = Firmata::Board.new(FakeSerialPort.new)
35
+ board.report_version
36
+ board.process
37
+
38
+ assert_equal '2.3', board.version
39
+ end
40
+
41
+ def test_writing_capability_query
42
+ mock_sp = mock_serial_port(Firmata::Board::START_SYSEX, Firmata::Board::CAPABILITY_QUERY, Firmata::Board::END_SYSEX)
43
+
44
+ board = Firmata::Board.new(mock_sp)
45
+ board.query_capabilities
46
+
47
+ mock_sp.verify
48
+ end
49
+
50
+ def test_processing_capabilities_query
51
+ board = Firmata::Board.new(FakeSerialPort.new)
52
+ board.query_capabilities
53
+ board.process
54
+
55
+ assert_equal 20, board.pins.size
56
+ end
57
+
58
+ def test_writing_analog_mapping_query
59
+ mock_sp = mock_serial_port(Firmata::Board::START_SYSEX, Firmata::Board::ANALOG_MAPPING_QUERY, Firmata::Board::END_SYSEX)
60
+
61
+ board = Firmata::Board.new(mock_sp)
62
+ board.query_analog_mapping
63
+
64
+ mock_sp.verify
65
+ end
66
+
67
+ def test_processing_analog_mapping_query
68
+ board = Firmata::Board.new(FakeSerialPort.new)
69
+ board.query_capabilities
70
+ board.process
71
+
72
+ board.query_analog_mapping
73
+ board.process
74
+
75
+ assert_equal 6, board.analog_pins.size
76
+ end
77
+
78
+ def test_write_pin_mode
79
+ mock_sp = mock_serial_port(Firmata::Board::PIN_MODE, 13, Firmata::Board::OUTPUT)
80
+
81
+ board = Firmata::Board.new(mock_sp)
82
+ board.pins[13] = Firmata::Board::Pin.new([0, 1, 4], 0, 0, nil)
83
+
84
+ board.pin_mode(13, Firmata::Board::OUTPUT)
85
+
86
+ assert_equal Firmata::Board::OUTPUT, board.pins[13].mode
87
+ end
88
+
89
+ def test_write_pin_state_query
90
+ mock_sp = mock_serial_port(Firmata::Board::START_SYSEX, Firmata::Board::PIN_STATE_QUERY, 13, Firmata::Board::END_SYSEX)
91
+
92
+ board = Firmata::Board.new(mock_sp)
93
+ board.query_pin_state(13)
94
+
95
+ mock_sp.verify
96
+ end
97
+
98
+ def test_processing_pin_state_query
99
+ board = Firmata::Board.new(FakeSerialPort.new)
100
+ board.query_capabilities
101
+ board.process
102
+
103
+ board.pins[13].mode = Firmata::Board::INPUT
104
+
105
+ board.query_pin_state(13)
106
+ board.process
107
+
108
+ assert_equal Firmata::Board::OUTPUT, board.pins[13].mode
109
+ end
110
+
111
+ def test_write_turn_pin_reporting_on
112
+ mock_sp = mock_serial_port do |mock|
113
+ 16.times do |i|
114
+ mock.expect(:write, 2, [[Firmata::Board::REPORT_DIGITAL | i, 1].map(&:chr).join])
115
+ mock.expect(:write, 2, [[(Firmata::Board::REPORT_ANALOG | i), 1].map(&:chr).join])
116
+ end
117
+ end
118
+
119
+ board = Firmata::Board.new(mock_sp)
120
+ board.turn_pin_reporting_on
121
+
122
+ mock_sp.verify
123
+ end
124
+
125
+ def test_turn_pin_reporting_off
126
+ mock_sp = mock_serial_port do |mock|
127
+ 16.times do |i|
128
+ mock.expect(:write, 2, [[Firmata::Board::REPORT_DIGITAL | i, 0].map(&:chr).join])
129
+ mock.expect(:write, 2, [[(Firmata::Board::REPORT_ANALOG | i), 0].map(&:chr).join])
130
+ end
131
+ end
132
+
133
+ board = Firmata::Board.new(mock_sp)
134
+ board.turn_pin_reporting_off
135
+
136
+ mock_sp.verify
137
+ end
138
+
139
+ def test_processing_analog_message
140
+ fake_port = FakeSerialPort.new
141
+ board = Firmata::Board.new(fake_port)
142
+
143
+ board.query_capabilities
144
+ board.process
145
+
146
+ board.query_analog_mapping
147
+ board.process
148
+
149
+ fake_port.buffer = "\xE0\e\u0005\xE1N\u0004\xE2A\u0004\xE3C\u0004\xE4o\u0004\xE5f\u0004\xE0w\u0004\xE1]\u0004\xE2N\u0004\xE3I\u0004\xE4x\u0004\xE5m\u0004\xE0m\u0004\xE1`\u0004\xE2T\u0004\xE3L\u0004\xE4s\u0004\xE5l\u0004\xE0i\u0004\xE1`\u0004\xE2V\u0004\xE3N\u0004\xE4m\u0004\xE5j\u0004\xE0f\u0004\xE1^\u0004\xE2U\u0004\xE3O\u0004\xE4h\u0004\xE5g\u0004\xE0d\u0004\xE1\\\u0004\xE2T\u0004\xE3O\u0004\xE4d\u0004\xE5d\u0004\xE0b\u0004\xE1Z\u0004\xE2S\u0004\xE3N\u0004\xE4`\u0004\xE5a\u0004\xE0_\u0004\xE1X\u0004\xE2Q\u0004\xE3L\u0004\xE4\\\u0004\xE5^\u0004\xE0\\\u0004\xE1T\u0004\xE2N\u0004\xE3J\u0004\xE4X\u0004\xE5Z\u0004\xE0X\u0004\xE1Q\u0004\xE2K\u0004\xE3G\u0004\xE4T\u0004\xE5W\u0004\xE0V\u0004\xE1O\u0004\xE2H\u0004\xE3D\u0004\xE4Q\u0004\xE5T\u0004\xE0S\u0004\xE1L\u0004\xE2F\u0004\xE3B\u0004\xE4N\u0004\xE5Q\u0004\xE0P\u0004\xE1J\u0004\xE2C\u0004\xE3@\u0004\xE4K\u0004\xE5N\u0004\xE0N\u0004\xE1G\u0004\xE2A\u0004\xE3>\u0004\xE4I\u0004\xE5L\u0004\xE0L\u0004\xE1D\u0004\xE2?\u0004\xE3<\u0004\xE4F\u0004\xE5I\u0004\xE0I\u0004\xE1B\u0004\xE2=\u0004\xE39\u0004\xE4C\u0004\xE5F\u0004\xE0F\u0004\xE1?\u0004\xE2:\u0004\xE37\u0004\xE4@\u0004\xE5C\u0004\xE0C\u0004\xE1<\u0004\xE28\u0004\xE34\u0004\xE4>\u0004\xE5A\u0004\xE0A\u0004\xE1:\u0004\xE26\u0004\xE32\u0004\xE4<\u0004\xE5?\u0004\xE0?\u0004\xE19\u0004\xE24\u0004\xE31\u0004\xE4:\u0004\xE5=\u0004\xE0>\u0004\xE17\u0004\xE22\u0004\xE3/\u0004\xE49\u0004\xE5;\u0004\xE0<\u0004\xE15\u0004\xE20\u0004\xE3-\u0004\xE46\u0004\xE59\u0004\xE09\u0004\xE12\u0004\xE2.\u0004\xE3+\u0004\xE44\u0004\xE57\u0004\xE07\u0004\xE10\u0004\xE2,\u0004\xE3)\u0004\xE42\u0004\xE55\u0004\xE05\u0004\xE1.\u0004\xE2*\u0004\xE3'\u0004\xE40\u0004\xE52\u0004\xE03\u0004\xE1-\u0004\xE2(\u0004\xE3&\u0004\xE4/\u0004\xE51\u0004\xE02\u0004\xE1+\u0004\xE2&\u0004\xE3$\u0004\xE4-\u0004\xE50\u0004\xE00\u0004\xE1*\u0004\xE2%\u0004\xE3#\u0004\xE4,\u0004\xE5.\u0004\xE0/\u0004\xE1(\u0004\xE2$\u0004\xE3!\u0004\xE4*\u0004\xE5,\u0004\xE0-\u0004\xE1&\u0004\xE2\"\u0004\xE3\u001F\u0004\xE4(\u0004\xE5*\u0004\xE0+\u0004\xE1$\u0004\xE2 \u0004\xE3\u001D\u0004\xE4&\u0004\xE5(\u0004\xE0)\u0004\xE1\"\u0004\xE2\u001F\u0004\xE3\u001C\u0004\xE4%\u0004\xE5'\u0004\xE0(\u0004\xE1!\u0004\xE2\u001D\u0004\xE3\e\u0004\xE4$\u0004\xE5&\u0004\xE0&\u0004\xE1 \u0004\xE2\u001C\u0004\xE3\u001A\u0004\xE4#\u0004\xE5%\u0004\xE0&\u0004\xE1\u001F\u0004\xE2\e\u0004\xE3\u0019\u0004\xE4\"\u0004\xE5$\u0004\xE0$\u0004\xE1\u001E\u0004\xE2\u0019\u0004\xE3\u0018\u0004\xE4 \u0004\xE5\"\u0004\xE0\"\u0004\xE1\u001C\u0004\xE2\u0018\u0004\xE3\u0016\u0004\xE4\u001F\u0004\xE5 \u0004\xE0!\u0004\xE1\u001A\u0004\xE2\u0017\u0004\xE3\u0014\u0004\xE4\u001D\u0004\xE5\u001E\u0004\xE0\u001F\u0004\xE1\u0019\u0004\xE2\u0015\u0004\xE3\u0013\u0004\xE4\u001C\u0004\xE5\u001D\u0004\xE0\u001E\u0004\xE1\u0018\u0004\xE2\u0013\u0004\xE3\u0012\u0004\xE4\e\u0004\xE5\u001C\u0004\xE0\u001D\u0004\xE1\u0017\u0004\xE2\u0014\u0004\xE3\u0012\u0004\xE4\e\u0004\xE5\u001C\u0004\xE0\u001D\u0004\xE1\u0016\u0004\xE2\u0013\u0004\xE3\u0011\u0004\xE4\u001A\u0004\xE5\e\u0004\xE0\u001C\u0004\xE1\u0015\u0004\xE2\u0011\u0004\xE3\u0010\u0004\xE4\u0018\u0004\xE5\u0019\u0004\xE0\u001A\u0004\xE1\u0014\u0004\xE2\u0010\u0004\xE3\u000E\u0004\xE4\u0017\u0004\xE5\u0018\u0004\xE0\u0018\u0004\xE1\u0012\u0004\xE2\u000E\u0004\xE3\r\u0004\xE4\u0015\u0004\xE5\u0016\u0004\xE0\u0017\u0004\xE1\u0011\u0004\xE2\u000E\u0004\xE3\f\u0004\xE4\u0015\u0004\xE5\u0016\u0004\xE0\u0016\u0004\xE1\u0010\u0004\xE2\f\u0004\xE3\v\u0004\xE4\u0014\u0004\xE5\u0015\u0004\xE0\u0016\u0004\xE1\u000F\u0004\xE2\v\u0004\xE3\n\u0004\xE4\u0013\u0004\xE5\u0014\u0004\xE0\u0015\u0004\xE1\u000F\u0004\xE2\v\u0004\xE3\t\u0004\xE4\u0012\u0004\xE5\u0013\u0004\xE0\u0014\u0004\xE1\u000E\u0004\xE2\n\u0004\xE3\t\u0004\xE4\u0012\u0004\xE5\u0012\u0004\xE0\u0013\u0004\xE1\f\u0004\xE2\t\u0004\xE3\a\u0004\xE4\u0010\u0004\xE5\u0011\u0004\xE0\u0011\u0004\xE1\v\u0004\xE2\b\u0004\xE3\u0006\u0004\xE4\u000F\u0004\xE5\u000F\u0004\xE0\u0010\u0004\xE1\n\u0004\xE2\u0006\u0004\xE3\u0005\u0004\xE4\u000E\u0004\xE5\u000F\u0004\xE0\u000F\u0004\xE1\t\u0004\xE2\u0006\u0004\xE3\u0004\u0004\xE4\r\u0004\xE5\u000E\u0004\xE0\u000F\u0004\xE1\t\u0004\xE2\u0006\u0004\xE3\u0004\u0004\xE4\r\u0004\xE5\u000E\u0004\xE0\u000E\u0004\xE1\b\u0004\xE2\u0004\u0004\xE3\u0003\u0004\xE4\f\u0004\xE5\r\u0004\xE0\u000E\u0004\xE1\a\u0004\xE2\u0004\u0004\xE3\u0003\u0004"
150
+ board.process
151
+
152
+ board.analog_pins.each do |pin|
153
+ refute_nil board.pins[pin].analog_channel, "Analog channel not set for pin #{pin}"
154
+ end
155
+ end
156
+
157
+ def test_reset
158
+ mock_sp = mock_serial_port(Firmata::Board::SYSTEM_RESET)
159
+
160
+ board = Firmata::Board.new(mock_sp)
161
+ board.reset
162
+
163
+ mock_sp.verify
164
+ end
165
+
166
+ def test_write_firmware_query
167
+ mock_sp = mock_serial_port(Firmata::Board::FIRMWARE_QUERY)
168
+
169
+ board = Firmata::Board.new(mock_sp)
170
+ board.query_firmware
171
+
172
+ mock_sp.verify
173
+ end
174
+
175
+ def test_process_firmware_query
176
+ fake_port = FakeSerialPort.new
177
+ fake_port.buffer = "\xF0y\u0002\u0003S\u0000t\u0000a\u0000n\u0000d\u0000a\u0000r\u0000d\u0000F\u0000i\u0000r\u0000m\u0000a\u0000t\u0000a\u0000\xF7"
178
+ board = Firmata::Board.new(fake_port)
179
+
180
+ board.process
181
+
182
+ assert_equal 'StandardFirmata', board.firmware_name, 'Firmware Name is incorrect'
183
+ end
184
+
185
+ def test_digital_write
186
+ mock_sp = mock_serial_port(145, 127, 1)
187
+ board = Firmata::Board.new(mock_sp)
188
+
189
+ 8.times do |x|
190
+ board.pins[x + 8] = Firmata::Board::Pin.new([], 0, 250 + x, nil)
191
+ end
192
+
193
+ board.digital_write(13, 1)
194
+
195
+ mock_sp.verify
196
+ end
197
+
198
+ end
@@ -0,0 +1,53 @@
1
+ require 'stringio'
2
+
3
+ class FakeSerialPort
4
+ Board = Firmata::Board
5
+
6
+ attr_accessor :buffer
7
+ attr_accessor :read_timeout
8
+
9
+ def initialize
10
+ @buffer = ""
11
+ @read_timeout = 0
12
+ end
13
+
14
+ def write(command)
15
+ val = case command.getbyte(0)
16
+ when Board::SYSTEM_RESET
17
+ ""
18
+ when Board::REPORT_VERSION
19
+ "\xF9\u0002\u0003"
20
+
21
+ when Board::REPORT_DIGITAL, Board::REPORT_ANALOG
22
+ "\xE0\e\u0005\xE1N\u0004\xE2A\u0004\xE3C\u0004\xE4o\u0004\xE5f\u0004\xE0w\u0004\xE1]\u0004\xE2N\u0004\xE3I\u0004\xE4x\u0004\xE5m\u0004\xE0m\u0004\xE1`\u0004\xE2T\u0004\xE3L\u0004\xE4s\u0004\xE5l\u0004\xE0i\u0004\xE1`\u0004\xE2V\u0004\xE3N\u0004\xE4m\u0004\xE5j\u0004\xE0f\u0004\xE1^\u0004\xE2U\u0004\xE3O\u0004\xE4h\u0004\xE5g\u0004\xE0d\u0004\xE1\\\u0004\xE2T\u0004\xE3O\u0004\xE4d\u0004\xE5d\u0004\xE0b\u0004\xE1Z\u0004\xE2S\u0004\xE3N\u0004\xE4`\u0004\xE5a\u0004\xE0_\u0004\xE1X\u0004\xE2Q\u0004\xE3L\u0004\xE4\\\u0004\xE5^\u0004\xE0\\\u0004\xE1T\u0004\xE2N\u0004\xE3J\u0004\xE4X\u0004\xE5Z\u0004\xE0X\u0004\xE1Q\u0004\xE2K\u0004\xE3G\u0004\xE4T\u0004\xE5W\u0004\xE0V\u0004\xE1O\u0004\xE2H\u0004\xE3D\u0004\xE4Q\u0004\xE5T\u0004\xE0S\u0004\xE1L\u0004\xE2F\u0004\xE3B\u0004\xE4N\u0004\xE5Q\u0004\xE0P\u0004\xE1J\u0004\xE2C\u0004\xE3@\u0004\xE4K\u0004\xE5N\u0004\xE0N\u0004\xE1G\u0004\xE2A\u0004\xE3>\u0004\xE4I\u0004\xE5L\u0004\xE0L\u0004\xE1D\u0004\xE2?\u0004\xE3<\u0004\xE4F\u0004\xE5I\u0004\xE0I\u0004\xE1B\u0004\xE2=\u0004\xE39\u0004\xE4C\u0004\xE5F\u0004\xE0F\u0004\xE1?\u0004\xE2:\u0004\xE37\u0004\xE4@\u0004\xE5C\u0004\xE0C\u0004\xE1<\u0004\xE28\u0004\xE34\u0004\xE4>\u0004\xE5A\u0004\xE0A\u0004\xE1:\u0004\xE26\u0004\xE32\u0004\xE4<\u0004\xE5?\u0004\xE0?\u0004\xE19\u0004\xE24\u0004\xE31\u0004\xE4:\u0004\xE5=\u0004\xE0>\u0004\xE17\u0004\xE22\u0004\xE3/\u0004\xE49\u0004\xE5;\u0004\xE0<\u0004\xE15\u0004\xE20\u0004\xE3-\u0004\xE46\u0004\xE59\u0004\xE09\u0004\xE12\u0004\xE2.\u0004\xE3+\u0004\xE44\u0004\xE57\u0004\xE07\u0004\xE10\u0004\xE2,\u0004\xE3)\u0004\xE42\u0004\xE55\u0004\xE05\u0004\xE1.\u0004\xE2*\u0004\xE3'\u0004\xE40\u0004\xE52\u0004\xE03\u0004\xE1-\u0004\xE2(\u0004\xE3&\u0004\xE4/\u0004\xE51\u0004\xE02\u0004\xE1+\u0004\xE2&\u0004\xE3$\u0004\xE4-\u0004\xE50\u0004\xE00\u0004\xE1*\u0004\xE2%\u0004\xE3#\u0004\xE4,\u0004\xE5.\u0004\xE0/\u0004\xE1(\u0004\xE2$\u0004\xE3!\u0004\xE4*\u0004\xE5,\u0004\xE0-\u0004\xE1&\u0004\xE2\"\u0004\xE3\u001F\u0004\xE4(\u0004\xE5*\u0004\xE0+\u0004\xE1$\u0004\xE2 \u0004\xE3\u001D\u0004\xE4&\u0004\xE5(\u0004\xE0)\u0004\xE1\"\u0004\xE2\u001F\u0004\xE3\u001C\u0004\xE4%\u0004\xE5'\u0004\xE0(\u0004\xE1!\u0004\xE2\u001D\u0004\xE3\e\u0004\xE4$\u0004\xE5&\u0004\xE0&\u0004\xE1 \u0004\xE2\u001C\u0004\xE3\u001A\u0004\xE4#\u0004\xE5%\u0004\xE0&\u0004\xE1\u001F\u0004\xE2\e\u0004\xE3\u0019\u0004\xE4\"\u0004\xE5$\u0004\xE0$\u0004\xE1\u001E\u0004\xE2\u0019\u0004\xE3\u0018\u0004\xE4 \u0004\xE5\"\u0004\xE0\"\u0004\xE1\u001C\u0004\xE2\u0018\u0004\xE3\u0016\u0004\xE4\u001F\u0004\xE5 \u0004\xE0!\u0004\xE1\u001A\u0004\xE2\u0017\u0004\xE3\u0014\u0004\xE4\u001D\u0004\xE5\u001E\u0004\xE0\u001F\u0004\xE1\u0019\u0004\xE2\u0015\u0004\xE3\u0013\u0004\xE4\u001C\u0004\xE5\u001D\u0004\xE0\u001E\u0004\xE1\u0018\u0004\xE2\u0013\u0004\xE3\u0012\u0004\xE4\e\u0004\xE5\u001C\u0004\xE0\u001D\u0004\xE1\u0017\u0004\xE2\u0014\u0004\xE3\u0012\u0004\xE4\e\u0004\xE5\u001C\u0004\xE0\u001D\u0004\xE1\u0016\u0004\xE2\u0013\u0004\xE3\u0011\u0004\xE4\u001A\u0004\xE5\e\u0004\xE0\u001C\u0004\xE1\u0015\u0004\xE2\u0011\u0004\xE3\u0010\u0004\xE4\u0018\u0004\xE5\u0019\u0004\xE0\u001A\u0004\xE1\u0014\u0004\xE2\u0010\u0004\xE3\u000E\u0004\xE4\u0017\u0004\xE5\u0018\u0004\xE0\u0018\u0004\xE1\u0012\u0004\xE2\u000E\u0004\xE3\r\u0004\xE4\u0015\u0004\xE5\u0016\u0004\xE0\u0017\u0004\xE1\u0011\u0004\xE2\u000E\u0004\xE3\f\u0004\xE4\u0015\u0004\xE5\u0016\u0004\xE0\u0016\u0004\xE1\u0010\u0004\xE2\f\u0004\xE3\v\u0004\xE4\u0014\u0004\xE5\u0015\u0004\xE0\u0016\u0004\xE1\u000F\u0004\xE2\v\u0004\xE3\n\u0004\xE4\u0013\u0004\xE5\u0014\u0004\xE0\u0015\u0004\xE1\u000F\u0004\xE2\v\u0004\xE3\t\u0004\xE4\u0012\u0004\xE5\u0013\u0004\xE0\u0014\u0004\xE1\u000E\u0004\xE2\n\u0004\xE3\t\u0004\xE4\u0012\u0004\xE5\u0012\u0004\xE0\u0013\u0004\xE1\f\u0004\xE2\t\u0004\xE3\a\u0004\xE4\u0010\u0004\xE5\u0011\u0004\xE0\u0011\u0004\xE1\v\u0004\xE2\b\u0004\xE3\u0006\u0004\xE4\u000F\u0004\xE5\u000F\u0004\xE0\u0010\u0004\xE1\n\u0004\xE2\u0006\u0004\xE3\u0005\u0004\xE4\u000E\u0004\xE5\u000F\u0004\xE0\u000F\u0004\xE1\t\u0004\xE2\u0006\u0004\xE3\u0004\u0004\xE4\r\u0004\xE5\u000E\u0004\xE0\u000F\u0004\xE1\t\u0004\xE2\u0006\u0004\xE3\u0004\u0004\xE4\r\u0004\xE5\u000E\u0004\xE0\u000E\u0004\xE1\b\u0004\xE2\u0004\u0004\xE3\u0003\u0004\xE4\f\u0004\xE5\r\u0004\xE0\u000E\u0004\xE1\a\u0004\xE2\u0004\u0004\xE3\u0003\u0004"
23
+
24
+ when Board::FIRMWARE_QUERY
25
+ "\xF0y\u0002\u0003S\u0000t\u0000a\u0000n\u0000d\u0000a\u0000r\u0000d\u0000F\u0000i\u0000r\u0000m\u0000a\u0000t\u0000a\u0000\xF7"
26
+
27
+ when Board::START_SYSEX
28
+ case command.getbyte(1)
29
+ when Board::ANALOG_MAPPING_QUERY
30
+ "\xF0j\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u007F\u0000\u0001\u0002\u0003\u0004\u0005\xF7"
31
+
32
+ when Board::CAPABILITY_QUERY
33
+ "\xF0l\u007F\u007F\u0000\u0001\u0001\u0001\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0003\b\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0003\b\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0003\b\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0003\b\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0003\b\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0003\b\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0004\u000E\u007F\u0000\u0001\u0001\u0001\u0002\n\u007F\u0000\u0001\u0001\u0001\u0002\n\u007F\u0000\u0001\u0001\u0001\u0002\n\u007F\u0000\u0001\u0001\u0001\u0002\n\u007F\u0000\u0001\u0001\u0001\u0002\n\u0006\u0001\u007F\u0000\u0001\u0001\u0001\u0002\n\u0006\u0001\u007F\xF7"
34
+
35
+ when Board::PIN_STATE_QUERY
36
+ "\xF0n\r\u0001\u0000\xF7"
37
+ end
38
+ end
39
+
40
+ @buffer << val
41
+ val.length
42
+ end
43
+
44
+ def bytes
45
+ bytes = StringIO.new(@buffer).bytes
46
+ @buffer = ""
47
+ bytes
48
+ end
49
+
50
+ def read
51
+ @buffer.read
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: firmata
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ! '''Mike Breen'''
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: serialport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: event_spitter
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: A lib for working with the Firmata protocol in Ruby.
47
+ email:
48
+ - hardbap@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - firmata.gemspec
59
+ - lib/firmata.rb
60
+ - lib/firmata/board.rb
61
+ - lib/firmata/version.rb
62
+ - sample.rb
63
+ - test/board_test.rb
64
+ - test/fake_serial_port.rb
65
+ homepage: ''
66
+ licenses: []
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 1.8.24
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: ''
89
+ test_files:
90
+ - test/board_test.rb
91
+ - test/fake_serial_port.rb