Class: Firmata::Board

Inherits:
Object
  • Object
show all
Includes:
EventSpitter
Defined in:
lib/firmata/board.rb

Defined Under Namespace

Classes: Pin

Constant Summary collapse

INPUT =

Public: Fixnum byte for pin mode input.

0x00
OUTPUT =

Public: Fixnum byte for pin mode output.

0x01
ANALOG =

Public: Fixnum byte for pin mode analog.

0x02
PWM =

Public: Fixnum byte for pin mode pulse width modulation.

0x03
SERVO =

Public: Fixnum byte for pin mode servo.

0x04
LOW =
0
HIGH =
1
REPORT_VERSION =

Internal: Fixnum byte command for protocol version

0xF9
SYSTEM_RESET =

Internal: Fixnum byte command for system reset

0xFF
DIGITAL_MESSAGE =

Internal: Fixnum byte command for digital I/O message

0x90
DIGITAL_MESSAGE_RANGE =

Pubilc: Fixnum byte for range for digital pins for digital 2 byte data format

0x90..0x9F
ANALOG_MESSAGE =

Internal: Fixnum byte command for an analog I/O message

0xE0
ANALOG_MESSAGE_RANGE =

Internal: Fixnum byte range for analog pins for analog 14-bit data format

0xE0..0xEF
REPORT_ANALOG =

Internal: Fixnum byte command to report analog pin

0xC0
REPORT_DIGITAL =

Internal: Fixnum byte command to report digital port

0xD0
PIN_MODE =

Internal: Fixnum byte command to set pin mode (I/O)

0xF4
START_SYSEX =

Internal: Fixnum byte command for start of Sysex message

0xF0
END_SYSEX =

Internal: Fixnum byte command for end of Sysex message

0xF7
CAPABILITY_QUERY =

Internal: Fixnum byte sysex command for capabilities query

0x6B
CAPABILITY_RESPONSE =

Internal: Fixnum byte sysex command for capabilities response

0x6C
PIN_STATE_QUERY =

Internal: Fixnum byte sysex command for pin state query

0x6D
PIN_STATE_RESPONSE =

Internal: Fixnum byte sysex command for pin state response

0x6E
ANALOG_MAPPING_QUERY =

Internal: Fixnum byte sysex command for analog mapping query

0x69
ANALOG_MAPPING_RESPONSE =

Internal: Fixnum byte sysex command for analog mapping response

0x6A
FIRMWARE_QUERY =

Internal: Fixnum byte sysex command for firmware query and response

0x79

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(port) ⇒ Board

Public: Initialize a Board

port - a String port or an Object that responds to read and write.



75
76
77
78
79
80
81
82
83
# File 'lib/firmata/board.rb', line 75

def initialize(port)
  @serial_port = port.is_a?(String) ? SerialPort.new(port, 57600, 8, 1, SerialPort::NONE) : port
  @serial_port.read_timeout = 2
  @major_version = 0
  @minor_version = 0
  @pins = []
  @analog_pins = []
  @connected = false
end

Instance Attribute Details

#analog_pinsObject (readonly)

Public: Returns the Array of analog pins on Arduino.



68
69
70
# File 'lib/firmata/board.rb', line 68

def analog_pins
  @analog_pins
end

#firmware_nameObject (readonly)

Public: Returns the String firmware name of Arduion.



70
71
72
# File 'lib/firmata/board.rb', line 70

def firmware_name
  @firmware_name
end

#pinsObject (readonly)

Public: Returns the Array of pins on Arduino.



66
67
68
# File 'lib/firmata/board.rb', line 66

def pins
  @pins
end

#serial_portObject (readonly)

Public: Returns the SerialPort port the Arduino is attached to.



64
65
66
# File 'lib/firmata/board.rb', line 64

def serial_port
  @serial_port
end

Instance Method Details

#analog_write(pin, value) ⇒ Object Also known as: servo_write

Public: Write an analog messege.

pin - The Integer pin to write to. value - The Integer value to write to the pin between 0-255.

Returns nothing.



306
307
308
309
# File 'lib/firmata/board.rb', line 306

def analog_write(pin, value)
  @pins[pin].value = value
  write(ANALOG_MESSAGE | pin, value & 0x7F, (value >> 7) & 0x7F)
end

#connectObject

Public: Make connection to Arduio.

Returns Firmata::Board board.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/firmata/board.rb', line 95

def connect
  unless @connected
    once('report_version', ->() do
      once('firmware_query', ->() do
        once('capability_query', ->() do
          once('analog_mapping_query', ->() do
            @connected = true
            emit('ready')
          end)
          query_analog_mapping
       end)
        query_capabilities
      end)
    end)

     until connected?
      read_and_process
      delay(0.5)
    end
  end

  self
end

#connected?Boolean

Pubilc: Check if a connection to Arduino has been made.

Returns Boolean connected state.

Returns:

  • (Boolean)


88
89
90
# File 'lib/firmata/board.rb', line 88

def connected?
  @connected
end

#delay(seconds) ⇒ Object

Public: Ask the Arduino to sleep for a number of seconds.

seconds - The Integer seconds to sleep for.

Returns nothing.



324
325
326
# File 'lib/firmata/board.rb', line 324

def delay(seconds)
  sleep(seconds)
end

#digital_write(pin, value) ⇒ Object

Public: Write a value to a digital pin.

pin - The Integer pin to write to. value - The value to write (HIGH or LOW).

Returns nothing.



287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/firmata/board.rb', line 287

def digital_write(pin, value)
  port = (pin / 8).floor
  port_value = 0

  @pins[pin].value = value

  8.times do |i|
    port_value |= (1 << i) unless @pins[8 * port + i].value.zero?
  end

  write(DIGITAL_MESSAGE | port, port_value & 0x7F, (port_value >> 7) & 0x7F)
end

#process(data) ⇒ Object

Internal: Process a series of bytes.

data: The String data to process.

Returns nothing.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/firmata/board.rb', line 145

def process(data)
  bytes = StringIO.new(String(data)).bytes
  bytes.each do |byte|
    case byte
    when REPORT_VERSION
      @major_version = bytes.next
      @minor_version = bytes.next

      emit('report_version')

    when ANALOG_MESSAGE_RANGE
      least_significant_byte = bytes.next
      most_significant_byte = bytes.next

      value = least_significant_byte | (most_significant_byte << 7)
      pin = byte & 0x0F

      if analog_pin = analog_pins[pin]
        pins[analog_pin].value = value
      end

    when DIGITAL_MESSAGE_RANGE
      port           = byte & 0x0F
      first_bitmask  = bytes.next
      second_bitmask = bytes.next
      port_value     = first_bitmask | (second_bitmask << 7)

      8.times do |i|
        pin_number = 8 * port + i
        if pin = pins[pin_number] and pin.mode == INPUT
          value = (port_value >> (i & 0x07)) & 0x01
          pin.value = value
          emit('digital-read', pin_number, value)
        end
      end

    when START_SYSEX
      current_buffer = [byte]
      begin
        current_buffer.push(bytes.next)
      end until current_buffer.last == END_SYSEX

      command = current_buffer[1]

      case command
      when CAPABILITY_RESPONSE
        supportedModes = 0
        n = 0

        current_buffer.slice(2, current_buffer.length - 3).each do |byte|
          if byte == 127
            modesArray = []
            # the pin modes
            [ INPUT, OUTPUT, ANALOG, PWM, SERVO ].each do |mode|
               modesArray.push(mode) unless (supportedModes & (1 << mode)).zero?
            end

            @pins.push(Pin.new(modesArray, OUTPUT, 0))

            supportedModes = 0
            n = 0
            next
          end

          supportedModes |= (1 << byte) if n.zero?

          n ^= 1
        end

        emit('capability_query')

      when ANALOG_MAPPING_RESPONSE
        pin_index = 0

        current_buffer.slice(2, current_buffer.length - 3).each do |byte|

          @pins[pin_index].analog_channel = byte

          @analog_pins.push(pin_index) unless byte == 127

          pin_index += 1
        end

        emit('analog_mapping_query')

      when PIN_STATE_RESPONSE
        pin       = pins[current_buffer[2]]
        pin.mode  = current_buffer[3]
        pin.value = current_buffer[4]

        pin.value |= (current_buffer[5] << 7) if current_buffer.size > 6

        pin.value |= (current_buffer[6] << 14) if current_buffer.size > 7

      when FIRMWARE_QUERY
        @firmware_name = current_buffer.slice(4, current_buffer.length - 5).reject { |b| b.zero? }.map(&:chr).join
        emit('firmware_query')

      else
        puts 'bad byte'
      end
    end
  end
rescue StopIteration
  # do nadda
end

#query_analog_mappingObject

Public: Ask the Arduino which pins (used with pin mode message) correspond to the analog channels.

Returns nothing.



369
370
371
# File 'lib/firmata/board.rb', line 369

def query_analog_mapping
  write(START_SYSEX, ANALOG_MAPPING_QUERY, END_SYSEX)
end

#query_capabilitiesObject

Public: Ask the Arduino about its capabilities and current state.

Returns nothing.



362
363
364
# File 'lib/firmata/board.rb', line 362

def query_capabilities
  write(START_SYSEX, CAPABILITY_QUERY, END_SYSEX)
end

#query_firmwareObject

Public: Ask the Ardution for its firmware name.

Returns nothing.



346
347
348
# File 'lib/firmata/board.rb', line 346

def query_firmware
  write(FIRMWARE_QUERY)
end

#query_pin_state(pin) ⇒ Object

Public: Ask the Arduino for the current configuration of any pin.

pin - The Integer pin to query on the board.

Returns nothing.



355
356
357
# File 'lib/firmata/board.rb', line 355

def query_pin_state(pin)
  write(START_SYSEX, PIN_STATE_QUERY, pin.to_i, END_SYSEX)
end

#readObject

Internal: Read data from the underlying serial port.

Returns String data read for serial port.



135
136
137
138
# File 'lib/firmata/board.rb', line 135

def read
  serial_port.read_nonblock(4096)
rescue EOFError
end

#read_and_processObject

Public: Read the serial port and process the results

Returns nothing.



255
256
257
# File 'lib/firmata/board.rb', line 255

def read_and_process
  process(read)
end

#report_versionObject

Public: Ask the Arduino to report its version.

Returns nothing.



339
340
341
# File 'lib/firmata/board.rb', line 339

def report_version
  write(REPORT_VERSION)
end

#resetObject

Public: Send a SYSTEM_RESET to the Arduino

Returns nothing.



262
263
264
# File 'lib/firmata/board.rb', line 262

def reset
  write(SYSTEM_RESET)
end

#set_pin_mode(pin, mode) ⇒ Object

Public: Set the mode for a pin.

pin - The Integer pin to set. mode - The Fixnum mode (INPUT, OUTPUT, ANALOG, PWM or SERVO)

Examples

set_pin_mode(13, OUTPUT)

Returns nothing.



276
277
278
279
# File 'lib/firmata/board.rb', line 276

def set_pin_mode(pin, mode)
  pins[pin].mode = mode
  write(PIN_MODE, pin, mode)
end

#start_pin_reportingObject

Public: Turn pin analog and digital reporting on.

Returns nothing.



389
390
391
# File 'lib/firmata/board.rb', line 389

def start_pin_reporting
  toggle_pin_reporting(1)
end

#stop_pin_reportingObject

Public: Turn pin analog and digital reporting off.

Returns nothing.



396
397
398
# File 'lib/firmata/board.rb', line 396

def stop_pin_reporting
  toggle_pin_reporting(0)
end

#toggle_pin_reporting(state) ⇒ Object

Internal: Toggle the pin analog and digtal reporting off and on.

state - The Integer to turn the pin on (1) or off (0).

Returns nothing.



379
380
381
382
383
384
# File 'lib/firmata/board.rb', line 379

def toggle_pin_reporting(state)
  16.times do |i|
    write(REPORT_DIGITAL | i, state)
    write(REPORT_ANALOG | i, state)
  end
end

#versionObject

Public: The major and minor firmware version on the board. Will report as “0.0” if report_version command has not been run.

Returns String the firmware version as “minor.major”.



332
333
334
# File 'lib/firmata/board.rb', line 332

def version
  [@major_version, @minor_version].join('.')
end

#write(*commands) ⇒ Object

Internal: Write data to the underlying serial port.

commands - Zero or more byte commands to be written.

Examples

write(START_SYSEX, CAPABILITY_QUERY, END_SYSEX)

Returns nothing.



128
129
130
# File 'lib/firmata/board.rb', line 128

def write(*commands)
  serial_port.write_nonblock(commands.map(&:chr).join)
end