firmata 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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