Class: Denko::Board
- Inherits:
-
Object
- Object
- Denko::Board
- 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/i2c_bit_bang.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: 0b0000, output_pwm: 0b0010, output_dac: 0b0100, output_open_drain: 0b0110, output_open_source: 0b1000, input: 0b0001, input_pulldown: 0b0011, input_pullup: 0b0101, }
- UART_BAUD_RATES =
[ 300, 600, 750, 1200, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 74880, 115200, 230400 ]
Instance Attribute Summary collapse
-
#analog_read_high ⇒ Object
(also: #adc_high)
readonly
Returns the value of attribute analog_read_high.
-
#analog_read_resolution ⇒ Object
Returns the value of attribute analog_read_resolution.
-
#analog_write_high ⇒ Object
(also: #pwm_high, #dac_high)
readonly
Returns the value of attribute analog_write_high.
-
#analog_write_resolution ⇒ Object
Returns the value of attribute analog_write_resolution.
-
#aux_limit ⇒ Object
readonly
Returns the value of attribute aux_limit.
-
#eeprom_length ⇒ Object
readonly
Returns the value of attribute eeprom_length.
-
#high ⇒ Object
readonly
Returns the value of attribute high.
-
#i2c_limit ⇒ Object
readonly
Returns the value of attribute i2c_limit.
-
#low ⇒ Object
readonly
Returns the value of attribute low.
-
#map ⇒ Object
readonly
Returns the value of attribute map.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#serial_buffer_size ⇒ Object
readonly
Returns the value of attribute serial_buffer_size.
-
#version ⇒ Object
readonly
Returns the value of attribute version.
Instance Method Summary collapse
- #analog_listen(pin, divider = 16) ⇒ Object
-
#analog_read(pin, negative_pin = nil, gain = nil, sample_rate = nil) ⇒ Object
CMD = 5.
-
#binary_echo(pin, data = []) ⇒ Object
CMD = 98.
- #convert_pin(pin) ⇒ Object
-
#dac_write(pin, value) ⇒ Object
CMD = 4.
-
#digital_listen(pin, divider = 4) ⇒ Object
Convenience methods that wrap set_listener.
-
#digital_read(pin) ⇒ Object
CMD = 2.
-
#digital_write(pin, value) ⇒ Object
CMD = 1.
-
#eeprom ⇒ Object
Component generating convenience methods.
-
#eeprom_read(address, num_bytes) ⇒ Object
CMD = 7.
-
#eeprom_write(address, bytes) ⇒ Object
CMD = 8.
- #finish_write ⇒ Object
-
#halt_resume_check ⇒ Object
CMD = 92.
- #hcsr04_read(echo_pin, trigger_pin) ⇒ Object
-
#i2c_bb_read(scl, sda, address, register, read_length, repeated_start = false) ⇒ Object
CMD = 32.
-
#i2c_bb_search(scl, sda) ⇒ Object
CMD = 30.
- #i2c_bb_setup(scl, sda) ⇒ Object
-
#i2c_bb_write(scl, sda, address, bytes, repeated_start = false) ⇒ Object
CMD = 31.
- #i2c_convert_frequency(freq) ⇒ Object
-
#i2c_read(i2c_index, address, register, read_length, frequency = 100000, repeated_start = false) ⇒ Object
CMD = 35.
-
#i2c_search(i2c_index) ⇒ Object
CMD = 33.
-
#i2c_write(i2c_index, address, bytes, frequency = 100000, repeated_start = false) ⇒ Object
CMD = 34.
- #infrared_emit(pin, frequency, pulses) ⇒ Object
-
#initialize(connection, options = {}) ⇒ Board
constructor
A new instance of Board.
- #load_map(board_name) ⇒ Object
-
#micro_delay(duration) ⇒ Object
CMD = 99.
-
#no_tone(pin) ⇒ Object
CMD = 18.
- #one_wire_read(pin, num_bytes) ⇒ Object
- #one_wire_reset(pin, read_presence = false) ⇒ Object
- #one_wire_search(pin, branch_mask) ⇒ Object
- #one_wire_write(pin, parasite_power, data) ⇒ Object
- #pack(*args, **kwargs) ⇒ Object
- #pin_is_pwm?(pin) ⇒ Boolean
- #platform ⇒ Object
-
#pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200) ⇒ Object
CMD = 9.
-
#pwm_write(pin, value) ⇒ Object
CMD = 3.
-
#servo_toggle(pin, value = :off, min: 544, max: 2400) ⇒ Object
CMD = 10.
-
#servo_write(pin, value = 0) ⇒ Object
CMD = 11.
-
#set_analog_read_resolution(value) ⇒ Object
CMD = 97.
-
#set_analog_write_resolution(value) ⇒ Object
CMD = 96.
-
#set_listener(pin, state = :off, **options) ⇒ Object
CMD = 6.
- #set_pin_debounce(pin, debounce_time) ⇒ Object
-
#set_pin_mode(pin, mode = :input, options = {}) ⇒ Object
CMD = 0.
-
#set_register_divider(value) ⇒ Object
CMD = 95.
- #show_ws2812(pin, pixel_buffer) ⇒ Object
- #spi_bb_header(clock, input, output, select_pin, write, read, mode = 0, bit_order = :msbfirst) ⇒ Object
-
#spi_bb_listen(select_pin, clock: nil, input: nil, read: 0, frequency: nil, mode: 0, bit_order: :msbfirst) ⇒ Object
CMD = 22.
-
#spi_bb_transfer(select_pin, clock: nil, output: nil, input: nil, write: [], read: 0, frequency: nil, mode: 0, bit_order: :msbfirst) ⇒ Object
CMD = 21.
- #spi_header(select_pin, write, read, frequency = 1_000_000, mode = 0, bit_order = :msbfirst) ⇒ Object
- #spi_header_generic(select_pin, write, read, mode = 0, bit_order = :msbfirst) ⇒ Object
-
#spi_limit ⇒ Object
Use all of the board’s aux buffer, except 8 bytes for header, or default to 512.
-
#spi_listen(spi_index, select_pin, read: 0, frequency: 1_000_000, mode: 0, bit_order: :msbfirst) ⇒ Object
CMD = 27.
-
#spi_stop(select_pin) ⇒ Object
CMD = 28.
-
#spi_transfer(spi_index, select_pin, write: [], read: 0, frequency: 1_000_000, mode: 0, bit_order: :msbfirst) ⇒ Object
CMD = 26.
- #stop_listener(pin) ⇒ Object
- #substitute_zero_pins ⇒ Object
-
#tone(pin, frequency, duration = nil) ⇒ Object
CMD = 17.
-
#uart_bb_start(tx, rx, baud, listening = true) ⇒ Object
CMD = 12.
-
#uart_bb_stop ⇒ Object
CMD = 12.
-
#uart_bb_write(data) ⇒ Object
CMD = 13.
-
#uart_start(index, baud, listening = true) ⇒ Object
CMD = 14.
-
#uart_stop(index) ⇒ Object
CMD = 14.
-
#uart_write(index, data) ⇒ Object
CMD = 15.
- #update(line) ⇒ Object
- #write(msg) ⇒ Object
-
#write_and_halt(msg) ⇒ Object
Use Board#write_and_halt to call C++ board functions that disable interrupts for a long time.
Methods included from Denko::Behaviors::Subcomponents
#add_component, #add_hw_i2c, #add_hw_spi, #add_single_pin, #components, #hw_i2c_comps, #hw_spi_comps, #remove_component, #remove_hw_i2c, #remove_hw_spi, #remove_single_pin, #single_pin_components
Constructor Details
#initialize(connection, options = {}) ⇒ Board
Returns a new instance of Board.
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 47 48 49 |
# File 'lib/denko/board.rb', line 13 def initialize(connection, ={}) # Shake hands @connection = connection ack = connection.handshake # Split handshake acknowledgement into separate values. @name, @version, @serial_buffer_size, @aux_limit, @eeprom_length, @i2c_limit = ack.split(",") # Tell connection 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 @connection.remote_buffer_size = @serial_buffer_size # Load board map by name. @name = nil if @name.empty? load_map(@name) @aux_limit = @aux_limit.to_i # 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 # connection calls #update on board when data is received. connection.add_observer(self) # Set digital and analog IO levels. @low = 0 @high = 1 self.analog_write_resolution = [:write_bits] || 8 self.analog_read_resolution = [:read_bits] || 10 end |
Instance Attribute Details
#analog_read_high ⇒ Object (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_read_resolution ⇒ Object
Returns the value of attribute analog_read_resolution.
7 8 9 |
# File 'lib/denko/board.rb', line 7 def analog_read_resolution @analog_read_resolution end |
#analog_write_high ⇒ Object (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 |
#analog_write_resolution ⇒ Object
Returns the value of attribute analog_write_resolution.
7 8 9 |
# File 'lib/denko/board.rb', line 7 def analog_write_resolution @analog_write_resolution end |
#aux_limit ⇒ Object (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_length ⇒ Object (readonly)
Returns the value of attribute eeprom_length.
6 7 8 |
# File 'lib/denko/board.rb', line 6 def eeprom_length @eeprom_length end |
#high ⇒ Object (readonly)
Returns the value of attribute high.
7 8 9 |
# File 'lib/denko/board.rb', line 7 def high @high end |
#i2c_limit ⇒ Object (readonly)
Returns the value of attribute i2c_limit.
6 7 8 |
# File 'lib/denko/board.rb', line 6 def i2c_limit @i2c_limit end |
#low ⇒ Object (readonly)
Returns the value of attribute low.
7 8 9 |
# File 'lib/denko/board.rb', line 7 def low @low end |
#map ⇒ Object (readonly)
Returns the value of attribute map.
7 8 9 |
# File 'lib/denko/board/map.rb', line 7 def map @map end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
6 7 8 |
# File 'lib/denko/board.rb', line 6 def name @name end |
#serial_buffer_size ⇒ Object (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 |
#version ⇒ Object (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
112 113 114 |
# File 'lib/denko/board/core.rb', line 112 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
68 69 70 |
# File 'lib/denko/board/core.rb', line 68 def analog_read(pin, negative_pin=nil, gain=nil, sample_rate=nil) write Message.encode command: 5, pin: pin end |
#binary_echo(pin, data = []) ⇒ Object
CMD = 98
157 158 159 |
# File 'lib/denko/board/core.rb', line 157 def binary_echo(pin, data=[]) write Message.encode command: 98, pin: pin, value: data.length, aux_message: pack(:uint8, data) 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 54 55 56 57 |
# 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 def pin_is_pwm?(pin) false end end |
#dac_write(pin, value) ⇒ Object
CMD = 4
60 61 62 63 64 65 |
# File 'lib/denko/board/core.rb', line 60 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.
108 109 110 |
# File 'lib/denko/board/core.rb', line 108 def digital_listen(pin, divider=4) set_listener(pin, :on, mode: :digital, divider: divider) end |
#digital_read(pin) ⇒ Object
CMD = 2
49 50 51 |
# File 'lib/denko/board/core.rb', line 49 def digital_read(pin) write Message.encode command: 2, pin: pin end |
#digital_write(pin, value) ⇒ Object
CMD = 1
41 42 43 44 45 46 |
# File 'lib/denko/board/core.rb', line 41 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 |
#eeprom ⇒ Object
Component generating convenience methods. TODO: add more!
117 118 119 120 |
# File 'lib/denko/board.rb', line 117 def eeprom raise StandardError, 'board has no built-in EEPROM, or EEPROM disabled in sketch' if @eeprom_length == 0 @eeprom ||= EEPROM::Board.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 write Message.encode command: 8, value: bytes.length, aux_message: address + bytes end |
#finish_write ⇒ Object
51 52 53 54 55 |
# File 'lib/denko/board.rb', line 51 def finish_write sleep 0.001 while @connection.writing? write "\n91\n" sleep 0.001 while @connection.writing? end |
#halt_resume_check ⇒ Object
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.
128 129 130 |
# File 'lib/denko/board/core.rb', line 128 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_bb_read(scl, sda, address, register, read_length, repeated_start = false) ⇒ Object
CMD = 32
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/denko/board/i2c_bit_bang.rb', line 31 def i2c_bb_read(scl, sda, address, register, read_length, repeated_start=false) # Use top bit of address to select stop condition (1), or repated start (0). send_stop = repeated_start ? 0 : 1 # 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: 32, pin: scl, value: sda, aux_message: pack(:uint8, 0x00) + pack(:uint8, address | (send_stop << 7)) + pack(:uint8, read_length) + register_packed end |
#i2c_bb_search(scl, sda) ⇒ Object
CMD = 30
8 9 10 11 12 |
# File 'lib/denko/board/i2c_bit_bang.rb', line 8 def i2c_bb_search(scl, sda) write Message.encode command: 30, pin: scl, value: sda end |
#i2c_bb_setup(scl, sda) ⇒ Object
3 4 5 |
# File 'lib/denko/board/i2c_bit_bang.rb', line 3 def i2c_bb_setup(scl, sda) # Stub method. Other implementations may call this. end |
#i2c_bb_write(scl, sda, address, bytes, repeated_start = false) ⇒ Object
CMD = 31
15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/denko/board/i2c_bit_bang.rb', line 15 def i2c_bb_write(scl, sda, address, bytes, repeated_start=false) bytes = [bytes].flatten unless bytes.class == Array # Use top bit of address to select stop condition (1), or repated start (0). send_stop = repeated_start ? 0 : 1 write Message.encode command: 31, pin: scl, value: sda, aux_message: pack(:uint8, 0x00) + pack(:uint8, address | (send_stop << 7)) + pack(:uint8, bytes.length) + pack(:uint8, bytes) 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(i2c_index, address, register, read_length, frequency = 100000, repeated_start = false) ⇒ Object
CMD = 35
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(i2c_index, 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 # 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, aux_message: pack(:uint8, i2c_convert_frequency(frequency)) + pack(:uint8, address | (send_stop << 7)) + pack(:uint8, read_length) + register_packed end |
#i2c_search(i2c_index) ⇒ Object
CMD = 33
23 24 25 |
# File 'lib/denko/board/i2c.rb', line 23 def i2c_search(i2c_index) write Message.encode command: 33 end |
#i2c_write(i2c_index, address, bytes, frequency = 100000, repeated_start = false) ⇒ Object
CMD = 34
28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/denko/board/i2c.rb', line 28 def i2c_write(i2c_index, address, bytes, frequency=100000, repeated_start=false) bytes = [bytes].flatten 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 write Message.encode command: 34, aux_message: pack(:uint8, i2c_convert_frequency(frequency)) + pack(:uint8, address | (send_stop << 7)) + pack(:uint8, bytes.length) + 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). # # Make length uint16 as well, for aligned memory access on ESP8266. # Pulses max is 2x255 bytes long since each is 2 bytes. length = pack :uint16, pulses.length, max: 2 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
162 163 164 165 166 167 |
# File 'lib/denko/board/core.rb', line 162 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, read_presence = false) ⇒ Object
3 4 5 6 7 |
# File 'lib/denko/board/one_wire.rb', line 3 def one_wire_reset(pin, read_presence=false) write Message.encode command: 41, pin: pin, value: read_presence ? 1 : 0 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 |
#pin_is_pwm?(pin) ⇒ Boolean
54 55 56 |
# File 'lib/denko/board/map.rb', line 54 def pin_is_pwm?(pin) false end |
#platform ⇒ Object
9 10 11 |
# File 'lib/denko/board.rb', line 9 def platform :arduino end |
#pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200) ⇒ Object
CMD = 9
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
54 55 56 57 |
# File 'lib/denko/board/core.rb', line 54 def pwm_write(pin,value) raise ArgumentError, "PWM value cannot be negative" if (value < 0) 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
149 150 151 152 153 154 |
# File 'lib/denko/board/core.rb', line 149 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
141 142 143 144 145 146 |
# File 'lib/denko/board/core.rb', line 141 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
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/denko/board/core.rb', line 73 def set_listener(pin, state=:off, **) # Default to digital listener and validate. [:mode] ||= :digital unless ([:mode] == :digital) || ([:mode] == :analog) raise ArgumentError, "error in mode: #{options[:mode]}. Should be one of: [:digital, :analog]" end mode_byte = ([:mode] == :digital) ? 0 : 1 # Default to 4ms divider if digital, 16ms if analog. if [:mode] == :digital [:divider] ||= 4 else [:divider] ||= 16 end # Convert divider to exponent and validate. exponent = Math.log2([: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_debounce(pin, debounce_time) ⇒ Object
37 38 |
# File 'lib/denko/board/core.rb', line 37 def set_pin_debounce(pin, debounce_time) end |
#set_pin_mode(pin, mode = :input, options = {}) ⇒ Object
CMD = 0
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/denko/board/core.rb', line 17 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 # Set frequency and resolution for PWM if given. aux = nil if (mode == :output_pwm) && aux = [0, 0] aux[0] = [:frequency] if [:frequency] aux[1] = [:resolution] if [:resolution] aux = pack :uint32, aux, min: 8, max: 8 end write Message.encode command: 0, pin: pin, value: PIN_MODES[mode], aux_message: aux end |
#set_register_divider(value) ⇒ Object
CMD = 95
133 134 135 136 137 138 |
# File 'lib/denko/board/core.rb', line 133 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 14 |
# File 'lib/denko/board/led_array.rb', line 3 def show_ws2812(pin, pixel_buffer) # ALWAYS have first 4 bytes set to 0. ESP32 crashes without this! # Last 4 bytes will be settings, but not yet. Just 24-bit 800 kHz for now. settings = pack :uint8, [0, 0, 0, 0, 0, 0, 0, 0] pixel_bytes = pack :uint8, pixel_buffer write_and_halt Message.encode command: 19, pin: pin, value: pixel_buffer.length, aux_message: settings + pixel_bytes end |
#spi_bb_header(clock, input, output, select_pin, write, read, mode = 0, bit_order = :msbfirst) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 |
# File 'lib/denko/board/spi_bit_bang.rb', line 3 def spi_bb_header(clock, input, output, select_pin, write, read, mode=0, bit_order=:msbfirst) raise ArgumentError, "either output or input pin required" unless (input || output) raise ArgumentError, "clock pin required" unless clock # Set the other to disabled if only one given. input ||= 255 output ||= 255 # Generic header + packed pins + empty byte = bit bang SPI header. header = spi_header_generic(select_pin, write, read, mode, bit_order) header = header + pack(:uint8, [clock, input, output, 0]) end |
#spi_bb_listen(select_pin, clock: nil, input: nil, read: 0, frequency: nil, mode: 0, bit_order: :msbfirst) ⇒ Object
CMD = 22
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/denko/board/spi_bit_bang.rb', line 29 def spi_bb_listen(select_pin, clock: nil, input: nil, read: 0, frequency: nil, mode: 0, bit_order: :msbfirst) raise ArgumentError, 'no bytes to read. Give read: argument > 0' unless (read.class == Integer) && (read > 0) raise ArgumentError, "select_pin cannot be nil when reading or listening" if (select_pin == nil) header = spi_bb_header(clock, input, nil, select_pin, [], 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: 0, bit_order: :msbfirst) ⇒ Object
CMD = 21
17 18 19 20 21 22 23 24 25 26 |
# File 'lib/denko/board/spi_bit_bang.rb', line 17 def spi_bb_transfer(select_pin, clock: nil, output: nil, input: nil, write: [], read: 0, frequency: nil, mode: 0, bit_order: :msbfirst) raise ArgumentError, "no bytes given to read or write" if (read <= 0) && (write.empty?) raise ArgumentError, "select_pin cannot be nil when reading or listening" if (read > 0) && (select_pin == nil) header = spi_bb_header(clock, input, output, select_pin, write, read, mode, bit_order) self.write Message.encode command: 21, pin: select_pin, aux_message: header + pack(:uint8, write) end |
#spi_header(select_pin, write, read, frequency = 1_000_000, mode = 0, bit_order = :msbfirst) ⇒ Object
32 33 34 35 36 37 38 39 |
# File 'lib/denko/board/spi.rb', line 32 def spi_header(select_pin, write, read, frequency=1_000_000, mode=0, bit_order=:msbfirst) raise ArgumentError, "error in SPI frequency: #{frequency}" unless [Integer, Float].include?(frequency.class) frequency = frequency.to_i # Generic header + packed frequency = hardware SPI header. header = spi_header_generic(select_pin, write, read, mode, bit_order) header += pack(:uint32, frequency) end |
#spi_header_generic(select_pin, write, read, mode = 0, bit_order = :msbfirst) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/denko/board/spi.rb', line 9 def spi_header_generic(select_pin, write, read, mode=0, bit_order=:msbfirst) raise ArgumentError, "can't read more than #{spi_limit} SPI bytes at a time" if read > spi_limit raise ArgumentError, "can't write more than #{spi_limit} SPI bytes at a time" if write.length > spi_limit # Lowest 2 bits of settings hold the SPI mode number. raise ArgumentError, "invalid SPI mode: #{mode}. Must be 0, 1, 2, or 3" unless (0..3).include? mode settings = mode # Bit 7 of settings toggles MSBFIRST (1) or LSBFIRST (0) for both read and write. settings |= 0b10000000 unless bit_order == :lsbfirst # Bit 6 of settings indicates whether a select pin needs to be toggled. settings |= 0b01000000 if select_pin # 3 bytes are available for read and write length, so 12-bits each. # Bytes 1 and 2 are lower 8 bits for read and write respectively. # Byte 3 is shared: upper 4 hold read[8..12], lower 4 hold write.length[8..12] shared_byte = ((read & 0xF00) >> 4) | ((write.length & 0xF00) >> 8) # Generic portion (first 4 bytes), used by both hardware and bit bang SPI. pack :uint8, [settings, read & 0xFF, write.length & 0xFF, shared_byte] end |
#spi_limit ⇒ Object
Use all of the board’s aux buffer, except 8 bytes for header, or default to 512. NOTE: SPI lengths are sent as 12-bit, so hard upper limit of 4095.
5 6 7 |
# File 'lib/denko/board/spi.rb', line 5 def spi_limit @spi_limit ||= aux_limit ? aux_limit-8 : 512 end |
#spi_listen(spi_index, select_pin, read: 0, frequency: 1_000_000, mode: 0, bit_order: :msbfirst) ⇒ Object
CMD = 27
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/denko/board/spi.rb', line 54 def spi_listen(spi_index, select_pin, read: 0, frequency: 1_000_000, mode: 0, bit_order: :msbfirst) raise ArgumentError, 'no bytes to read. Give read: argument > 0' unless (read.class == Integer) && (read > 0) raise ArgumentError, "select_pin cannot be nil when reading or listening" if (select_pin == nil) header = spi_header(select_pin, [], 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
66 67 68 |
# File 'lib/denko/board/spi.rb', line 66 def spi_stop(select_pin) self.write Message.encode command: 28, pin: select_pin end |
#spi_transfer(spi_index, select_pin, write: [], read: 0, frequency: 1_000_000, mode: 0, bit_order: :msbfirst) ⇒ Object
CMD = 26
42 43 44 45 46 47 48 49 50 51 |
# File 'lib/denko/board/spi.rb', line 42 def spi_transfer(spi_index, select_pin, write: [], read: 0, frequency: 1_000_000, mode: 0, bit_order: :msbfirst) raise ArgumentError, "no bytes given to read or write" if (read <= 0) && (write.empty?) raise ArgumentError, "select_pin cannot be nil when reading or listening" if (read > 0) && (select_pin == nil) header = spi_header(select_pin, 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
116 117 118 |
# File 'lib/denko/board/core.rb', line 116 def stop_listener(pin) set_listener(pin, :off) end |
#substitute_zero_pins ⇒ Object
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
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_stop ⇒ Object
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
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
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
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
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/denko/board.rb', line 96 def update(line) pin, = line.split(":", 2) # Handle messages from hardware I2C buses. match = pin.match /\AI2C(\d*)/ if match dev_index = match[1].to_i dev = hw_i2c_comps[dev_index] dev.update() if dev return end pin = pin.to_i if single_pin_components[pin] single_pin_components[pin].update() end end |
#write(msg) ⇒ Object
73 74 75 |
# File 'lib/denko/board.rb', line 73 def write(msg) @connection.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 Connection 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 Connection, telling it to resume transmisison.
87 88 89 |
# File 'lib/denko/board.rb', line 87 def write_and_halt(msg) @connection.write(msg, true) end |