Class: Denko::Board

Inherits:
Object
  • Object
show all
Includes:
Denko::Behaviors::Subcomponents
Defined in:
lib/denko/board.rb,
lib/denko/board/i2c.rb,
lib/denko/board/map.rb,
lib/denko/board/spi.rb,
lib/denko/board/core.rb,
lib/denko/board/tone.rb,
lib/denko/board/uart.rb,
lib/denko/board/pulse.rb,
lib/denko/board/servo.rb,
lib/denko/board/eeprom.rb,
lib/denko/board/infrared.rb,
lib/denko/board/one_wire.rb,
lib/denko/board/led_array.rb,
lib/denko/board/message_pack.rb,
lib/denko/board/spi_bit_bang.rb,
lib/denko/board/uart_bit_bang.rb

Constant Summary collapse

I2C_FREQUENCIES =
{
  100000  => 0x00,
  400000  => 0x01,
  1000000 => 0x02,
  3400000 => 0x03,
}
MAPS_FOLDER =
File.join(Denko.root, "vendor/board-maps/yaml")
DIVIDERS =
[1, 2, 4, 8, 16, 32, 64, 128]
PIN_MODES =
{
              output:         0b000,
              output_pwm:     0b010,
              output_dac:     0b100,
              input:          0b001,
              input_pulldown: 0b011,
              input_pullup:   0b101,
              input_output:   0b111
}
UART_BAUD_RATES =
[
  300, 600, 750, 1200, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 74880, 115200, 230400
]

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Denko::Behaviors::Subcomponents

#add_component, #components, #remove_component, #single_pin_components

Constructor Details

#initialize(transport, options = {}) ⇒ Board

Returns a new instance of Board.

Raises:

  • (StandardError)


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/denko/board.rb', line 9

def initialize(transport, options={})
  # Shake hands
  @transport = transport
  ack = transport.handshake

  # Split handshake acknowledgement into separate values.
  @name, @version, @serial_buffer_size, @aux_limit, @eeprom_length, @i2c_limit = ack.split(",")

  # Tell transport how much serial buffer the board has, for flow control.
  @serial_buffer_size = @serial_buffer_size.to_i
  raise StandardError, "no serial buffer size given in handshake" if @serial_buffer_size < 1
  @transport.remote_buffer_size = @serial_buffer_size

  # Load board map by name.
  @name = nil if @name.empty?
  load_map(@name)

  # Leave room for null termination of aux messages.
  @aux_limit = @aux_limit.to_i - 1

  # Set I2C transaction size limit. Safe minimum is 32.
  # This makes I2C fail silently if board does not implement.
  @i2c_limit = @i2c_limit.to_i
  @i2c_limit = 32 if @i2c_limit == 0

  # Remaining settings
  @version       = nil if @version.empty?
  @eeprom_length = @eeprom_length.to_i

  # Transport calls #update on board when data is received.
  transport.add_observer(self)
  
  # Set digital and analog IO levels.
  @low  = 0
  @high = 1
  self.analog_write_resolution = options[:write_bits] || 8
  self.analog_read_resolution  = options[:read_bits]  || 10
end

Instance Attribute Details

#analog_read_highObject (readonly) Also known as: adc_high

Returns the value of attribute analog_read_high.



7
8
9
# File 'lib/denko/board.rb', line 7

def analog_read_high
  @analog_read_high
end

#analog_write_highObject (readonly) Also known as: pwm_high, dac_high

Returns the value of attribute analog_write_high.



7
8
9
# File 'lib/denko/board.rb', line 7

def analog_write_high
  @analog_write_high
end

#aux_limitObject (readonly)

Returns the value of attribute aux_limit.



6
7
8
# File 'lib/denko/board.rb', line 6

def aux_limit
  @aux_limit
end

#eeprom_lengthObject (readonly)

Returns the value of attribute eeprom_length.



6
7
8
# File 'lib/denko/board.rb', line 6

def eeprom_length
  @eeprom_length
end

#highObject (readonly)

Returns the value of attribute high.



7
8
9
# File 'lib/denko/board.rb', line 7

def high
  @high
end

#i2c_limitObject (readonly)

Returns the value of attribute i2c_limit.



10
11
12
# File 'lib/denko/board/i2c.rb', line 10

def i2c_limit
  @i2c_limit
end

#lowObject (readonly)

Returns the value of attribute low.



7
8
9
# File 'lib/denko/board.rb', line 7

def low
  @low
end

#mapObject (readonly)

Returns the value of attribute map.



7
8
9
# File 'lib/denko/board/map.rb', line 7

def map
  @map
end

#nameObject (readonly)

Returns the value of attribute name.



6
7
8
# File 'lib/denko/board.rb', line 6

def name
  @name
end

#serial_buffer_sizeObject (readonly)

Returns the value of attribute serial_buffer_size.



6
7
8
# File 'lib/denko/board.rb', line 6

def serial_buffer_size
  @serial_buffer_size
end

#versionObject (readonly)

Returns the value of attribute version.



6
7
8
# File 'lib/denko/board.rb', line 6

def version
  @version
end

Instance Method Details

#analog_listen(pin, divider = 16) ⇒ Object



99
100
101
# File 'lib/denko/board/core.rb', line 99

def analog_listen(pin, divider=16)
  set_listener(pin, :on, mode: :analog, divider: divider)
end

#analog_read(pin, negative_pin = nil, gain = nil, sample_rate = nil) ⇒ Object

CMD = 5



55
56
57
# File 'lib/denko/board/core.rb', line 55

def analog_read(pin, negative_pin=nil, gain=nil, sample_rate=nil)
  write Message.encode command: 5, pin: pin
end

#analog_read_resolutionObject



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

def analog_read_resolution
  @read_bits
end

#analog_read_resolution=(value) ⇒ Object



60
61
62
63
64
# File 'lib/denko/board.rb', line 60

def analog_read_resolution=(value)
  set_analog_read_resolution(value)
  @read_bits = value
  @analog_read_high = (2 ** @read_bits) - 1
end

#analog_write_resolutionObject



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

def analog_write_resolution
  @write_bits
end

#analog_write_resolution=(value) ⇒ Object



54
55
56
57
58
# File 'lib/denko/board.rb', line 54

def analog_write_resolution=(value)
  set_analog_write_resolution(value)
  @write_bits = value
  @analog_write_high = (2 ** @write_bits) - 1
end

#convert_pin(pin) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/denko/board/map.rb', line 29

def convert_pin(pin)
  # Convert non numerical strings to symbols.
  pin = pin.to_sym if (pin.class == String) && !(pin.match (/\A\d+\.*\d*/))

  # Handle symbols.
  if (pin.class == Symbol)
    if map && map[pin]
      return map[pin]
    elsif map
      raise ArgumentError, "error in pin: #{pin.inspect}. Make sure that pin is defined for this board by calling Board#map"
    else
      raise ArgumentError, "error in pin: #{pin.inspect}. Given a Symbol, but board has no map. Try using GPIO integer instead"
    end
  end

  # Handle integers.
  return pin if pin.class == Integer

  # Try #to_i on anyting else. Will catch numerical strings.
  begin
    return pin.to_i
  rescue
    raise ArgumentError, "error in pin: #{pin.inspect}"
  end
end

#dac_write(pin, value) ⇒ Object

CMD = 4



47
48
49
50
51
52
# File 'lib/denko/board/core.rb', line 47

def dac_write(pin,value)
  if (value < 0) || (value > dac_high)
    raise ArgumentError, "cannot write DAC value: #{value}. Should be Integer in range 0..#{dac_high} "
  end
  write Message.encode command: 4, pin: pin, value: value.round
end

#digital_listen(pin, divider = 4) ⇒ Object

Convenience methods that wrap set_listener.



95
96
97
# File 'lib/denko/board/core.rb', line 95

def digital_listen(pin, divider=4)
  set_listener(pin, :on, mode: :digital, divider: divider)
end

#digital_read(pin) ⇒ Object

CMD = 2



34
35
36
# File 'lib/denko/board/core.rb', line 34

def digital_read(pin)
  write Message.encode command: 2, pin: pin
end

#digital_write(pin, value) ⇒ Object

CMD = 1



26
27
28
29
30
31
# File 'lib/denko/board/core.rb', line 26

def digital_write(pin,value)
  unless (value == 1) || (value == 0)
    raise ArgumentError, "cannot write digital value: #{value}. Should be Integer either 0 or 1" 
  end
  write Message.encode command: 1, pin: pin, value: value
end

#eepromObject

Component generating convenience methods. TODO: add more!

Raises:

  • (StandardError)


112
113
114
115
# File 'lib/denko/board.rb', line 112

def eeprom
  raise StandardError, 'board has no built-in EEPROM, or EEPROM disabled in sketch' if @eeprom_length == 0
  @eeprom ||= EEPROM::BuiltIn.new(board: self)
end

#eeprom_read(address, num_bytes) ⇒ Object

CMD = 7



4
5
6
7
8
9
# File 'lib/denko/board/eeprom.rb', line 4

def eeprom_read(address, num_bytes)
  address = pack :uint16, address
  write Message.encode  command: 7,
                        value: num_bytes,
                        aux_message: address
end

#eeprom_write(address, bytes) ⇒ Object

CMD = 8



12
13
14
15
16
17
18
# File 'lib/denko/board/eeprom.rb', line 12

def eeprom_write(address, bytes)
  address = pack :uint16, address
  bytes  = pack :uint8, bytes, min: 1, max: 128
  write Message.encode  command: 8,
                        value: bytes.length,
                        aux_message: address + bytes
end

#finish_writeObject



48
49
50
51
52
# File 'lib/denko/board.rb', line 48

def finish_write
  sleep 0.001 while @transport.writing?
  write "\n91\n"
  sleep 0.001 while @transport.writing?
end

#halt_resume_checkObject

CMD = 92

For diagnostics and testing mostly. What this does: 1) Tell the Connection to halt transmission immediately, after this message. 2) The board will send back a ready signal, which the Connection should read and resume transmisison.

See comments on Board#write_and_halt for more info and use case.



115
116
117
# File 'lib/denko/board/core.rb', line 115

def halt_resume_check
  write_and_halt Message.encode command: 92
end

#hcsr04_read(echo_pin, trigger_pin) ⇒ Object



26
27
28
# File 'lib/denko/board/pulse.rb', line 26

def hcsr04_read(echo_pin, trigger_pin)
  write Message.encode(command: 20, pin: echo_pin, value: trigger_pin)
end

#i2c_convert_frequency(freq) ⇒ Object



12
13
14
15
16
17
18
19
20
# File 'lib/denko/board/i2c.rb', line 12

def i2c_convert_frequency(freq)
  # Default to 100 kHz.
  freq = 100000 unless freq

  unless I2C_FREQUENCIES.include?(freq)
    raise ArgumentError, "I2C frequency must be in: #{I2C_FREQUENCIES.keys.inspect}" 
  end
  I2C_FREQUENCIES[freq]
end

#i2c_read(address, register, read_length, frequency = 100000, repeated_start = false) ⇒ Object

CMD = 35

Raises:

  • (ArgumentError)


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/denko/board/i2c.rb', line 43

def i2c_read(address, register, read_length, frequency=100000, repeated_start=false)
  raise ArgumentError, "I2C read must be 1..#{i2c_limit} bytes long" if (read_length > i2c_limit || read_length < 1)

  # Use top bit of address to select stop condition (1), or repated start (0).
  send_stop = repeated_start ? 0 : 1
  frequency = i2c_convert_frequency(frequency)

  # A register address starting register address can be given (up to 4 bytes)
  if register
    register = [register].flatten
    raise ArgumentError, 'maximum 4 byte register address for I2C read' if register.length > 4
    register_packed = pack(:uint8, [register.length] + register)
  else
    register_packed = pack(:uint8, [0])
  end

  write Message.encode  command:      35,
                        pin:          address | (send_stop << 7),
                        value:        read_length,
                        aux_message:  pack(:uint8, frequency) + register_packed
end

#i2c_searchObject

CMD = 33



23
24
25
# File 'lib/denko/board/i2c.rb', line 23

def i2c_search
  write Message.encode command: 33
end

#i2c_write(address, bytes, frequency = 100000, repeated_start = false) ⇒ Object

CMD = 34

Raises:

  • (ArgumentError)


28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/denko/board/i2c.rb', line 28

def i2c_write(address, bytes, frequency=100000, repeated_start=false)
  bytes = [bytes] unless bytes.class == Array
  raise ArgumentError, "I2C write must be 1..#{i2c_limit} bytes long" if (bytes.length > i2c_limit || bytes.length < 1)
  
  # Use top bit of address to select stop condition (1), or repated start (0).
  send_stop = repeated_start ? 0 : 1
  frequency = i2c_convert_frequency(frequency)
  
  write Message.encode  command:     34,
                        pin:         address | (send_stop << 7),
                        value:       bytes.length,
                        aux_message: pack(:uint8, frequency) + pack(:uint8, bytes)
end

#infrared_emit(pin, frequency, pulses) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/denko/board/infrared.rb', line 3

def infrared_emit(pin, frequency, pulses)
  #
  # Limit to 255 marks/spaces (not pairs) for now.
  #
  # Length must be 1-byte long, not literally 1
  # Pulses max is 2x255 bytes long since each is 2 bytes.
  length = pack :uint8,  pulses.length,  max: 1
  bytes  = pack :uint16, pulses, min: 1, max: 510

  write Message.encode command: 16,
                      pin: pin,
                      value: frequency,
                      aux_message: length + bytes
end

#load_map(board_name) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
# File 'lib/denko/board/map.rb', line 17

def load_map(board_name)
  if board_name
    map_path = File.join(MAPS_FOLDER, "#{board_name}.yml")
    @map = YAML.load_file(map_path)
    substitute_zero_pins
  else
    @map = nil
  end
rescue
  raise StandardError, "error loading board map from file for board name: '#{board_name}'"
end

#micro_delay(duration) ⇒ Object

CMD = 99



144
145
146
147
148
149
# File 'lib/denko/board/core.rb', line 144

def micro_delay(duration)
  if (duration < 0) || (duration > 0xFFFF)
    raise ArgumentError, "error in duration: #{duration}. Should be Integer in range 0..65535"
  end
  write Message.encode command: 99, aux_message: pack(:uint16, [duration])
end

#no_tone(pin) ⇒ Object

CMD = 18



19
20
21
# File 'lib/denko/board/tone.rb', line 19

def no_tone(pin)
  write Message.encode command: 18, pin: pin
end

#one_wire_read(pin, num_bytes) ⇒ Object



28
29
30
31
32
# File 'lib/denko/board/one_wire.rb', line 28

def one_wire_read(pin, num_bytes)
  write Message.encode  command: 44,
                        pin: pin,
                        value: num_bytes
end

#one_wire_reset(pin, value = 0) ⇒ Object



3
4
5
6
7
# File 'lib/denko/board/one_wire.rb', line 3

def one_wire_reset(pin, value=0)
  write Message.encode  command: 41,
                        pin: pin,
                        value: value
end

#one_wire_search(pin, branch_mask) ⇒ Object



9
10
11
12
13
# File 'lib/denko/board/one_wire.rb', line 9

def one_wire_search(pin, branch_mask)
  write Message.encode  command: 42,
                        pin: pin,
                        aux_message: pack(:uint64, branch_mask, max: 8)
end

#one_wire_write(pin, parasite_power, *data) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/denko/board/one_wire.rb', line 15

def one_wire_write(pin, parasite_power, *data)
  bytes  = pack :uint8, data, min: 1, max: 127 # Should be 128 with 0 = 1.

  # Set high bit of length if the bus must drive high after write.
  length = bytes.length
  length = length | 0b10000000 if parasite_power

  write Message.encode  command: 43,
                        pin: pin,
                        value: length,
                        aux_message: bytes
end

#pack(*args, **kwargs) ⇒ Object



3
4
5
# File 'lib/denko/board/message_pack.rb', line 3

def pack(*args, **kwargs)
  Message.pack(*args, **kwargs)
end

#pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200) ⇒ Object

CMD = 9

Raises:

  • (ArgumentError)


4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/denko/board/pulse.rb', line 4

def pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200)
  # Validation
  raise ArgumentError, "error in reset: #{reset}. Should be either #{high} or #{low}"         if reset && ![high, low].include?(reset)
  raise ArgumentError, "errror in reset_time: #{reset_time}. Should be 0..65535 microseconds" if (reset_time < 0) || (reset_time > 0xFFFF)
  raise ArgumentError, "errror in pulse_limit: #{pulse_limit}. Should be 0..255 pulses"       if (pulse_limit < 0) || (pulse_limit > 0xFF)
  raise ArgumentError, "errror in timeout: #{timeout}. Should be 0..65535 milliseconds"       if (timeout < 0) || (timeout > 0xFFFF)
  
  # Bit 0 of settings mask controls whether to hold high/low for reset.
  settings = reset ? 1 : 0

  # Bit 1 of settings mask controls whether to hold high (1) or to hold low (0).
  settings = settings | 0b10 if (reset && reset != low)

  # Pack and send.
  aux = pack :uint16, [reset_time, timeout]
  aux << pack(:uint8, pulse_limit)
  write Message.encode  command: 9,
                        pin: pin,
                        value: settings,
                        aux_message: aux
end

#pwm_write(pin, value) ⇒ Object

CMD = 3



39
40
41
42
43
44
# File 'lib/denko/board/core.rb', line 39

def pwm_write(pin,value)
  if  (value < 0) || (value > pwm_high)
    raise ArgumentError, "cannot write PWM value: #{value}. Should be Integer in range 0..#{pwm_high} "
  end
  write Message.encode command: 3, pin: pin, value: value.round
end

#servo_toggle(pin, value = :off, min: 544, max: 2400) ⇒ Object

CMD = 10



4
5
6
7
8
9
# File 'lib/denko/board/servo.rb', line 4

def servo_toggle(pin, value=:off, min: 544, max: 2400)
  write Message.encode  command: 10,
                        pin: pin,
                        value: (value == :off) ? 0 : 1,
                        aux_message: pack(:uint16, [min, max])
end

#servo_write(pin, value = 0) ⇒ Object

CMD = 11



12
13
14
15
16
# File 'lib/denko/board/servo.rb', line 12

def servo_write(pin, value=0)
  write Message.encode  command: 11,
                        pin: pin,
                        aux_message: pack(:uint16, value)
end

#set_analog_read_resolution(value) ⇒ Object

CMD = 97



136
137
138
139
140
141
# File 'lib/denko/board/core.rb', line 136

def set_analog_read_resolution(value)
  if (value < 0) || (value > 16)
    raise ArgumentError, "cannot set resolution: #{value}. Should be Integer in range 0..16"
  end
  write Message.encode(command: 97, value: value)
end

#set_analog_write_resolution(value) ⇒ Object

CMD = 96



128
129
130
131
132
133
# File 'lib/denko/board/core.rb', line 128

def set_analog_write_resolution(value)
  if (value < 0) || (value > 16)
    raise ArgumentError, "cannot set resolution: #{value}. Should be Integer in range 0..16"
  end
  write Message.encode(command: 96, value: value)
end

#set_listener(pin, state = :off, **options) ⇒ Object

CMD = 6



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/denko/board/core.rb', line 60

def set_listener(pin, state=:off, **options)
  # Default to digital listener and validate.
  options[:mode] ||= :digital
  unless (options[:mode] == :digital) || (options[:mode] == :analog) 
    raise ArgumentError, "error in mode: #{options[:mode]}. Should be one of: [:digital, :analog]"
  end
  mode_byte = (options[:mode] == :digital) ? 0 : 1

  # Default to 4ms divider if digital, 16ms if analog.
  if options[:mode] == :digital
    options[:divider] ||= 4
  else
    options[:divider] ||= 16
  end
  
  # Convert divider to exponent and validate.
  exponent = Math.log2(options[:divider]).round
  if (exponent < 0) || (exponent > 7)
    raise ArgumentError, "error in divider: #{options[:divider]}. Should be one of: #{DIVIDERS.inspect}"
  end

  # Validate state.
  unless (state == :on) || (state == :off) 
    raise ArgumentError, "error in state: #{options[:state]}. Should be one of: [:on, :off]"
  end
  state_byte = (state == :on) ? 1 : 0

  # Send it.
  write Message.encode  command: 6,
                        pin: pin,
                        value: state_byte,
                        aux_message: pack(:uint8, [mode_byte, exponent])
end

#set_pin_mode(pin, mode = :input) ⇒ Object

CMD = 0



16
17
18
19
20
21
22
23
# File 'lib/denko/board/core.rb', line 16

def set_pin_mode(pin, mode=:input)
  unless PIN_MODES.keys.include? mode
    raise ArgumentError, "cannot set mode: #{mode}. Should be one of: #{PIN_MODES.keys.inspect}"
  end
  write Message.encode  command: 0,
                        pin: pin,
                        value: PIN_MODES[mode]
end

#set_register_divider(value) ⇒ Object

CMD = 95



120
121
122
123
124
125
# File 'lib/denko/board/core.rb', line 120

def set_register_divider(value)
  unless DIVIDERS.include?(value)
    raise ArgumentError, "error in divider: #{value}. Should be one of: #{DIVIDERS.inspect}"
  end
  write Message.encode(command: 95, value: value)
end

#show_ws2812(pin, pixel_buffer) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
# File 'lib/denko/board/led_array.rb', line 3

def show_ws2812(pin, pixel_buffer)
  # Settings are blank for now.
  settings = pack :uint8, [0, 0, 0, 0]
  
  packed_pixels = pack :uint8, pixel_buffer
  
  write_and_halt Message.encode command: 19,
                                pin: pin,
                                value: pixel_buffer.length,
                                aux_message: settings + packed_pixels
end

#spi_bb_header(clock, input, output, write, read, mode, bit_order) ⇒ Object

Raises:

  • (ArgumentError)


3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# File 'lib/denko/board/spi_bit_bang.rb', line 3

def spi_bb_header(clock, input, output, write, read, mode, bit_order)
  # Validate clock and data pins
  raise ArgumentError, "no clock pin given" unless clock
  raise ArgumentError, "no input or output pin given. Require either or both" unless(input || output)

  # Set the other to disabled if only one given.
  input  ||= 255
  ouptut ||= 255

  # Get the generic part of the SPI header. 
  header = spi_header_generic(write, read, mode, bit_order)

  # Generic header + packed pins + empty byte = bit bang SPI bheader.
  header = header + pack(:uint8, [clock, input, output, 0])
end

#spi_bb_listen(select_pin, clock: nil, input: nil, read: 0, frequency: nil, mode: nil, bit_order: nil) ⇒ Object

CMD = 22

Raises:

  • (ArgumentError)


31
32
33
34
35
36
37
38
39
# File 'lib/denko/board/spi_bit_bang.rb', line 31

def spi_bb_listen(select_pin, clock: nil, input: nil, read: 0, frequency: nil, mode: nil, bit_order: nil)
  raise ArgumentError, 'no bytes to read. Give read: argument > 0' unless (read > 0)

  header = spi_bb_header(clock, input, nil, [], read, mode, bit_order)

  self.write Message.encode command: 22,
                            pin: select_pin,
                            aux_message: header
end

#spi_bb_transfer(select_pin, clock: nil, output: nil, input: nil, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil) ⇒ Object

CMD = 21

Raises:

  • (ArgumentError)


20
21
22
23
24
25
26
27
28
# File 'lib/denko/board/spi_bit_bang.rb', line 20

def spi_bb_transfer(select_pin, clock: nil, output: nil, input: nil, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil)
  raise ArgumentError, "no bytes given to read or write" if (read == 0) && (write.empty?)

  header = spi_bb_header(clock, input, output, write, read, mode, bit_order)

  self.write Message.encode command: 21,
                            pin: select_pin,
                            aux_message: header + pack(:uint8, write)
end

#spi_header(write, read, frequency, mode, bit_order) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/denko/board/spi.rb', line 24

def spi_header(write, read, frequency, mode, bit_order)
  # Set default frequency and validate.
  frequency ||= 1000000
  unless [Integer, Float].include? frequency.class
    raise ArgumentError, "error in SPI frequency: #{frequency.inspect}"
  end

  # Get the generic part of the SPI header. 
  header = spi_header_generic(write, read, mode, bit_order)

  # Generic header + packed frequency = hardware SPI header.
  header + pack(:uint32, frequency)
end

#spi_header_generic(write, read, mode, bit_order) ⇒ Object

Raises:

  • (ArgumentError)


3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/denko/board/spi.rb', line 3

def spi_header_generic(write, read, mode, bit_order)
  # Defaults.
  mode      ||= 0
  bit_order ||= :msbfrst

  raise ArgumentError, "can't read more than 255 SPI bytes at a time" if read > 255
  raise ArgumentError, "can't write more than 255 SPI bytes at a time" if write.length > 255

  # Lowest 2 bits of settings control the SPI mode.
  settings = mode
  unless (0..3).include? settings
    raise ArgumentError, "invalid SPI mode: #{settings.inspect}. Must be 0, 1, 2, or 3"
  end

  # Bit 7 of settings toggles MSBFIRST (1) or LSBFIRST (0) for both read and write.
  settings = settings | 0b10000000 unless bit_order == :lsbfirst

  # Return generic portion of header (used by both hardware and bit bang SPI).
  pack(:uint8, [settings, read, write.length])
end

#spi_listen(select_pin, read: 0, frequency: nil, mode: nil, bit_order: nil) ⇒ Object

CMD = 27

Raises:

  • (ArgumentError)


50
51
52
53
54
55
56
57
58
# File 'lib/denko/board/spi.rb', line 50

def spi_listen(select_pin, read: 0, frequency: nil, mode: nil, bit_order: nil)
  raise ArgumentError, 'no bytes to read. Give read: argument > 0' unless (read > 0)

  header = spi_header([], read, frequency, mode, bit_order)
  
  self.write Message.encode command: 27,
                            pin: select_pin,
                            aux_message: header
end

#spi_stop(select_pin) ⇒ Object

CMD = 28



61
62
63
# File 'lib/denko/board/spi.rb', line 61

def spi_stop(select_pin)
  self.write Message.encode command: 28, pin: select_pin
end

#spi_transfer(select_pin, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil) ⇒ Object

CMD = 26

Raises:

  • (ArgumentError)


39
40
41
42
43
44
45
46
47
# File 'lib/denko/board/spi.rb', line 39

def spi_transfer(select_pin, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil)
  raise ArgumentError, "no bytes given to read or write" if (read == 0) && (write.empty?)

  header = spi_header(write, read, frequency, mode, bit_order)

  self.write Message.encode command: 26,
                            pin: select_pin,
                            aux_message: header + pack(:uint8, write)
end

#stop_listener(pin) ⇒ Object



103
104
105
# File 'lib/denko/board/core.rb', line 103

def stop_listener(pin)
  set_listener(pin, :off)
end

#substitute_zero_pinsObject



9
10
11
12
13
14
15
# File 'lib/denko/board/map.rb', line 9

def substitute_zero_pins
  ["SDA", "SCL", "MOSI", "MISO", "SCK", "SS"].each do |name|
    symbol      = name.to_sym
    zero_symbol = (name + "0").to_sym
    @map[symbol] = @map[zero_symbol] if (@map[zero_symbol] && !@map[symbol])
  end
end

#tone(pin, frequency, duration = nil) ⇒ Object

CMD = 17

Raises:

  • (ArgumentError)


4
5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/denko/board/tone.rb', line 4

def tone(pin, frequency, duration=nil)
  raise ArgumentError, "Tone cannot generate frequencies lower than 31Hz"     if frequency < 31
  raise ArgumentError, "Tone duration cannot be more than 65535 milliseconds" if (duration && (duration > 0xFFFF))

  # Pack the frequency and optional duration as binary.
  aux = pack(:uint16, frequency)
  aux << pack(:uint16, duration) if duration

  write Message.encode command: 17,
                            pin: pin,
                            value: duration ? 1 : 0,
                            aux_message: aux
end

#uart_bb_start(tx, rx, baud, listening = true) ⇒ Object

CMD = 12



4
5
6
7
8
9
10
11
12
# File 'lib/denko/board/uart_bit_bang.rb', line 4

def uart_bb_start(tx, rx, baud, listening=true)
  config  = 0b01000000
  config |= 0b10000000 if listening

  self.write Message.encode command:      12,
                            pin:          tx,
                            value:        rx,
                            aux_message:  pack(:uint32, baud) + pack(:uint8, config)
end

#uart_bb_stopObject

CMD = 12



15
16
17
18
19
# File 'lib/denko/board/uart_bit_bang.rb', line 15

def uart_bb_stop
  config = 0b00000000
  self.write Message.encode command:      12,
                            aux_message:  pack(:uint32, [0]) + pack(:uint8, config)
end

#uart_bb_write(data) ⇒ Object

CMD = 13



22
23
24
25
26
27
28
29
30
31
# File 'lib/denko/board/uart_bit_bang.rb', line 22

def uart_bb_write(data)
  if data.class == Array
    data = pack(:uint8, data)
  elsif data.class == String
  else
    raise ArgumentError, "data to write to UART should be Array of bytes or String. Given: #{data.inspect}"
  end

  self.write Message.encode(command: 13, value: data.length, aux_message: data)
end

#uart_start(index, baud, listening = true) ⇒ Object

CMD = 14

Raises:

  • (ArgumentError)


8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/denko/board/uart.rb', line 8

def uart_start(index, baud, listening=true)
  raise ArgumentError, "given UART: #{index} out of range. Only 1..3 supported" if (index < 1 || index > 3)
  unless UART_BAUD_RATES.include?(baud)
    raise ArgumentError, "given baud rate: #{baud} not supported. Must be in #{UART_BAUD_RATES.inspect}"
  end

  config  = index | 0b01000000
  config |= 0b10000000 if listening

  self.write Message.encode command:     14,
                            pin:         config,
                            aux_message: pack(:uint32, baud)
end

#uart_stop(index) ⇒ Object

CMD = 14

Raises:

  • (ArgumentError)


23
24
25
26
# File 'lib/denko/board/uart.rb', line 23

def uart_stop(index)
  raise ArgumentError, "given UART: #{index} out of range. Only 1..3 supported" if (index < 1 || index > 3)
  self.write Message.encode(command: 14, pin: index)
end

#uart_write(index, data) ⇒ Object

CMD = 15

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/denko/board/uart.rb', line 29

def uart_write(index, data)
  raise ArgumentError, "given UART: #{index} out of range. Only 1..3 supported" if (index < 1 || index > 3)

  if data.class == Array
    data = pack(:uint8, data)
  elsif data.class == String
  else
    raise ArgumentError, "data to write to UART should be Array of bytes or String. Given: #{data.inspect}"
  end

  self.write Message.encode(command: 15, pin: index, value: data.length, aux_message: data)
end

#update(line) ⇒ Object



101
102
103
104
105
106
107
# File 'lib/denko/board.rb', line 101

def update(line)
  pin, message = line.split(":", 2)
  pin = pin.to_i
  if single_pin_components[pin]
    single_pin_components[pin].update(message)
  end
end

#write(msg) ⇒ Object



78
79
80
# File 'lib/denko/board.rb', line 78

def write(msg)
  @transport.write(msg)
end

#write_and_halt(msg) ⇒ Object

Use Board#write_and_halt to call C++ board functions that disable interrupts for a long time. “Long” being more than 1 serial character (~85us for 115200 baud).

The “halt” part tells the TxRx to halt transmission to the board after this message. Since it expects interrupts to be disabled, any data sent could be lost.

When the board function has re-enabled interrupts, it should call sendReady(). That signal is read by the TxRx, telling it to resume transmisison.



92
93
94
# File 'lib/denko/board.rb', line 92

def write_and_halt(msg)
  @transport.write(msg, true)
end