Class: Denko::PiBoard
- Inherits:
-
Object
- Object
- Denko::PiBoard
- Includes:
- Behaviors::Subcomponents
- Defined in:
- lib/denko/piboard_i2c.rb,
lib/denko/piboard_map.rb,
lib/denko/piboard_spi.rb,
lib/denko/piboard_base.rb,
lib/denko/piboard_core.rb,
lib/denko/piboard_tone.rb,
lib/denko/piboard_pulse.rb,
lib/denko/piboard_servo.rb,
lib/denko/piboard_i2c_bb.rb,
lib/denko/piboard_spi_bb.rb,
lib/denko/piboard_version.rb,
lib/denko/piboard_infrared.rb,
lib/denko/piboard_one_wire.rb,
lib/denko/piboard_led_array.rb,
lib/denko/piboard_hardware_pwm.rb
Constant Summary collapse
- I2C_ADDRESS_RANGE =
Address ranges 0..7 and 120..127 are reserved. Try each address in 8..119 (0x08 to 0x77).
(0x08..0x77).to_a
- DEFAULT_MAP_FILE =
".denko_piboard_map.yml"
- LOW =
0
- HIGH =
1
- PWM_HIGH =
100
- REPORT_SLEEP_TIME =
0.001
- INPUT_MODES =
[:input, :input_pullup, :input_pulldown]
- OUTPUT_MODES =
[:output, :output_pwm, :output_open_drain, :output_open_source]
- PIN_MODES =
INPUT_MODES + OUTPUT_MODES
- VERSION =
'0.14.0'
Instance Attribute Summary collapse
-
#alert_lut ⇒ Object
readonly
Returns the value of attribute alert_lut.
-
#gpiochip_lookup_optimized ⇒ Object
readonly
Returns the value of attribute gpiochip_lookup_optimized.
-
#map ⇒ Object
readonly
Returns the value of attribute map.
-
#spi_bbs ⇒ Object
readonly
Returns the value of attribute spi_bbs.
Instance Method Summary collapse
- #analog_listen(pin, divider = 16) ⇒ Object
- #analog_read(pin, negative_pin = nil, gain = nil, sample_rate = nil) ⇒ Object
- #analog_write_resolution ⇒ Object
- #binary_echo(pin, data = []) ⇒ Object
-
#bound_pins ⇒ Object
Keep track of pins bound by non-GPIO peripherals.
- #convert_pin(pin) ⇒ Object
- #dac_write(pin, value) ⇒ Object
- #digital_listen(pin, divider = 4) ⇒ Object
- #digital_read(pin) ⇒ Object
- #digital_write(pin, value) ⇒ Object
- #finish_write ⇒ Object
- #get_report ⇒ Object
-
#gpio_handle(index) ⇒ Object
Store multiple LGPIO handles, since one board might have multiple chips.
- #gpio_handles ⇒ Object
-
#gpio_tuple(index) ⇒ Object
Make a new tuple, given a human-readable pin number, using values from the map.
-
#gpio_tuples ⇒ Object
Cache tuples of [handle, line_number], keyed to human-readable pin numbers.
- #halt_resume_check ⇒ Object
- #hardware_pwm_from_pin(pin, options = {}) ⇒ Object
- #hardware_pwms ⇒ Object
- #hcsr04_read(echo_pin, trigger_pin) ⇒ Object
- #high ⇒ Object
- #i2c_bb_interface(scl, sda) ⇒ Object
- #i2c_bb_read(scl, sda, address, register, read_length, repeated_start = false) ⇒ Object
- #i2c_bb_search(scl, sda) ⇒ Object
- #i2c_bb_write(scl, sda, address, bytes, repeated_start = false) ⇒ Object
- #i2c_bbs ⇒ Object
-
#i2c_limit ⇒ Object
Maximum amount of bytes that can be read or written in a single I2C operation.
-
#i2c_read(index, address, register, read_length, frequency = nil, repeated_start = false) ⇒ Object
CMD = 35.
-
#i2c_search(index) ⇒ Object
CMD = 33.
-
#i2c_write(index, address, bytes, frequency = nil, repeated_start = false) ⇒ Object
CMD = 34.
- #infrared_emit(pin, frequency, pulses) ⇒ Object
-
#initialize(map_yaml_file = nil) ⇒ PiBoard
constructor
A new instance of PiBoard.
-
#load_gpiochip_lookup_optimizations ⇒ Object
Monkey patch to eliminate lookups, and improve performance, when all GPIO lines are on a single gpiochip, and the readable GPIO numbers exactly match the GPIO line numbers.
- #low ⇒ Object
- #micro_delay(duration) ⇒ Object
-
#no_tone(pin) ⇒ Object
CMD = 18.
- #one_wire_interface(pin) ⇒ Object
- #one_wire_read(gpio, length) ⇒ Object
- #one_wire_reset(gpio, check_presence = 0) ⇒ Object
- #one_wire_search(gpio, branch_mask) ⇒ Object
- #one_wire_write(gpio, parasite, *data) ⇒ Object
- #one_wires ⇒ Object
- #parse_map(map_yaml) ⇒ Object
- #pin_configs ⇒ Object
- #pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200) ⇒ Object
- #pwm_high ⇒ Object
- #pwm_write(pin, duty) ⇒ Object
-
#servo_toggle(pin, value = :off, options = {}) ⇒ Object
CMD = 10.
-
#servo_write(pin, value = 0) ⇒ Object
CMD = 11.
- #set_analog_read_resolution(value) ⇒ Object
- #set_analog_write_resolution(value) ⇒ Object
- #set_listener(pin, state = :off, options = {}) ⇒ Object
- #set_pin_debounce(pin, debounce_time) ⇒ Object
- #set_pin_mode(pin, mode = :input, options = {}) ⇒ Object
- #set_register_divider(value) ⇒ Object
- #show_ws2812(pin, pixel_buffer, spi_index:) ⇒ Object
- #spi_bb_interface(clock, input, output) ⇒ Object
- #spi_bb_listen(*arg, **kwargs) ⇒ Object
- #spi_bb_transfer(select, clock:, input: nil, output: nil, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil) ⇒ Object
- #spi_flags(mode) ⇒ Object
- #spi_listen(*arg, **kwargs) ⇒ Object
- #spi_listeners ⇒ Object
- #spi_stop(pin) ⇒ Object
- #spi_transfer(index, select, write: [], read: 0, frequency: 1_000_000, mode: 0, bit_order: nil) ⇒ Object
- #start_alert_thread ⇒ Object
- #start_gpio_reports ⇒ Object
- #stop_alert_thread ⇒ Object
- #stop_listener(pin) ⇒ Object
-
#tone(pin, frequency, duration = nil) ⇒ Object
CMD = 17.
- #tone_busy(pin) ⇒ Object
- #update(pin, message) ⇒ Object
Constructor Details
#initialize(map_yaml_file = nil) ⇒ PiBoard
Returns a new instance of PiBoard.
16 17 18 19 20 21 22 23 |
# File 'lib/denko/piboard_base.rb', line 16 def initialize(map_yaml_file=nil) map_yaml_file ||= Dir.home + "/" + DEFAULT_MAP_FILE unless File.exist?(map_yaml_file) raise StandardError, "board map file not given to PiBoard#new, and does not exist at #{map_yaml_file}" end parse_map(map_yaml_file) end |
Instance Attribute Details
#alert_lut ⇒ Object (readonly)
Returns the value of attribute alert_lut.
7 8 9 |
# File 'lib/denko/piboard_map.rb', line 7 def alert_lut @alert_lut end |
#gpiochip_lookup_optimized ⇒ Object (readonly)
Returns the value of attribute gpiochip_lookup_optimized.
7 8 9 |
# File 'lib/denko/piboard_map.rb', line 7 def gpiochip_lookup_optimized @gpiochip_lookup_optimized end |
#map ⇒ Object (readonly)
Returns the value of attribute map.
7 8 9 |
# File 'lib/denko/piboard_map.rb', line 7 def map @map end |
#spi_bbs ⇒ Object (readonly)
Returns the value of attribute spi_bbs.
3 4 5 |
# File 'lib/denko/piboard_spi_bb.rb', line 3 def spi_bbs @spi_bbs end |
Instance Method Details
#analog_listen(pin, divider = 16) ⇒ Object
125 126 127 |
# File 'lib/denko/piboard_core.rb', line 125 def analog_listen(pin, divider=16) raise NotImplementedError, "PiBoard#analog_read not implemented" end |
#analog_read(pin, negative_pin = nil, gain = nil, sample_rate = nil) ⇒ Object
88 89 90 |
# File 'lib/denko/piboard_core.rb', line 88 def analog_read(pin, negative_pin=nil, gain=nil, sample_rate=nil) raise NotImplementedError, "PiBoard#analog_read not implemented" end |
#analog_write_resolution ⇒ Object
14 |
# File 'lib/denko/piboard_base.rb', line 14 def analog_write_resolution; 8; end |
#binary_echo(pin, data = []) ⇒ Object
149 150 151 |
# File 'lib/denko/piboard_core.rb', line 149 def binary_echo(pin, data=[]) raise NotImplementedError, "PiBoard#binary_echo not implemented" end |
#bound_pins ⇒ Object
Keep track of pins bound by non-GPIO peripherals.
124 125 126 |
# File 'lib/denko/piboard_map.rb', line 124 def bound_pins @bound_pins ||= [] end |
#convert_pin(pin) ⇒ Object
128 129 130 |
# File 'lib/denko/piboard_map.rb', line 128 def convert_pin(pin) pin.to_i if pin end |
#dac_write(pin, value) ⇒ Object
84 85 86 |
# File 'lib/denko/piboard_core.rb', line 84 def dac_write(pin, value) raise NotImplementedError, "PiBoard#dac_write not implemented" end |
#digital_listen(pin, divider = 4) ⇒ Object
121 122 123 |
# File 'lib/denko/piboard_core.rb', line 121 def digital_listen(pin, divider=4) set_listener(pin, :on, {}) end |
#digital_read(pin) ⇒ Object
63 64 65 66 67 68 69 70 71 72 |
# File 'lib/denko/piboard_core.rb', line 63 def digital_read(pin) if hardware_pwms[pin] state = hardware_pwms[pin].duty_percent else handle, line = gpio_tuple(pin) state = LGPIO.gpio_read(handle, line) end self.update(pin, state) return state end |
#digital_write(pin, value) ⇒ Object
54 55 56 57 58 59 60 61 |
# File 'lib/denko/piboard_core.rb', line 54 def digital_write(pin, value) if hardware_pwms[pin] hardware_pwms[pin].duty_percent = (value == 0) ? 0 : 100 else handle, line = gpio_tuple(pin) LGPIO.gpio_write(handle, line, value) end end |
#finish_write ⇒ Object
25 26 27 |
# File 'lib/denko/piboard_base.rb', line 25 def finish_write gpio_handles.each { |h| LGPIO.chip_close(h) if h } end |
#get_report ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/denko/piboard_core.rb', line 167 def get_report report = LGPIO.gpio_get_report if report if chip = alert_lut[report[:chip]] if pin = chip[report[:gpio]] update(pin, report[:level]) end end else sleep 0.001 end end |
#gpio_handle(index) ⇒ Object
Store multiple LGPIO handles, since one board might have multiple chips.
115 116 117 |
# File 'lib/denko/piboard_map.rb', line 115 def gpio_handle(index) gpio_handles[index] ||= LGPIO.chip_open(index) end |
#gpio_handles ⇒ Object
119 120 121 |
# File 'lib/denko/piboard_map.rb', line 119 def gpio_handles @gpio_handles ||= [] end |
#gpio_tuple(index) ⇒ Object
Make a new tuple, given a human-readable pin number, using values from the map.
97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/denko/piboard_map.rb', line 97 def gpio_tuple(index) return gpio_tuples[index] if gpio_tuples[index] raise ArgumentError, "pin #{index} does not exist or not included in map" unless map[:pins][index] raise ArgumentError, "pin #{index} cannot be used as GPIO. Bound to #{bound_pins[index]}" if bound_pins[index] handle = gpio_handle(map[:pins][index][:chip]) line = map[:pins][index][:line] gpio_tuples[index] = [handle, line] end |
#gpio_tuples ⇒ Object
Cache tuples of [handle, line_number], keyed to human-readable pin numbers.
110 111 112 |
# File 'lib/denko/piboard_map.rb', line 110 def gpio_tuples @gpio_tuples ||= [] end |
#halt_resume_check ⇒ Object
133 134 135 |
# File 'lib/denko/piboard_core.rb', line 133 def halt_resume_check raise NotImplementedError, "PiBoard#halt_resume_check not implemented" end |
#hardware_pwm_from_pin(pin, options = {}) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/denko/piboard_hardware_pwm.rb', line 7 def hardware_pwm_from_pin(pin, ={}) # Find existing hardware PWM, change the frequency if needed, then return it. frequency = [:frequency] pwm = hardware_pwms[pin] if pwm pwm.frequency = frequency if (frequency && pwm.frequency != frequency) return pwm end # Make sure it's in the board map before trying to use it. raise StandardError, "no hardware PWM in board map for pin #{pin}" unless map[:pwms][pin] # Make a new hardware PWM. pwmchip = map[:pwms][pin][:pwmchip] channel = map[:pwms][pin][:channel] frequency ||= 1000 pwm = LGPIO::HardwarePWM.new(pwmchip, channel, frequency: frequency) hardware_pwms[pin] = pwm end |
#hardware_pwms ⇒ Object
3 4 5 |
# File 'lib/denko/piboard_hardware_pwm.rb', line 3 def hardware_pwms @hardware_pwms ||= [] end |
#hcsr04_read(echo_pin, trigger_pin) ⇒ Object
5 6 7 8 |
# File 'lib/denko/piboard_pulse.rb', line 5 def hcsr04_read(echo_pin, trigger_pin) microseconds = LGPIO.gpio_read_ultrasonic(@gpio_handle, trigger_pin, echo_pin, 10) self.update(echo_pin, microseconds.to_s) end |
#high ⇒ Object
12 |
# File 'lib/denko/piboard_base.rb', line 12 def high; HIGH; end |
#i2c_bb_interface(scl, sda) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/denko/piboard_i2c_bb.rb', line 7 def i2c_bb_interface(scl, sda) # Convert the pins into a config array to check. ch, cl = gpio_tuple(scl) dh, dl = gpio_tuple(sda) config = [ch, cl, dh, dl] # Check if any already exists with that array and return it. i2c_bbs.each { |bb| return bb if (config == bb.config) } # If not, create one. hash = { scl: { handle: ch, line: cl }, sda: { handle: dh, line: dl } } i2c_bb = LGPIO::I2CBitBang.new(hash) i2c_bbs << i2c_bb i2c_bb end |
#i2c_bb_read(scl, sda, address, register, read_length, repeated_start = false) ⇒ Object
38 39 40 41 42 43 44 45 46 |
# File 'lib/denko/piboard_i2c_bb.rb', line 38 def i2c_bb_read(scl, sda, address, register, read_length, repeated_start=false) interface = i2c_bb_interface(scl, sda) interface.write(address, register) if register bytes = interface.read(address, read_length) # Prepend the address (0th element) to the data, and update the SDA pin. bytes.unshift(address) self.update(sda, bytes) end |
#i2c_bb_search(scl, sda) ⇒ Object
25 26 27 28 29 30 31 |
# File 'lib/denko/piboard_i2c_bb.rb', line 25 def i2c_bb_search(scl, sda) interface = i2c_bb_interface(scl, sda) devices = interface.search found_string = "" found_string = devices.join(":") if devices self.update(sda, found_string) end |
#i2c_bb_write(scl, sda, address, bytes, repeated_start = false) ⇒ Object
33 34 35 36 |
# File 'lib/denko/piboard_i2c_bb.rb', line 33 def i2c_bb_write(scl, sda, address, bytes, repeated_start=false) interface = i2c_bb_interface(scl, sda) interface.write(address, bytes) end |
#i2c_bbs ⇒ Object
3 4 5 |
# File 'lib/denko/piboard_i2c_bb.rb', line 3 def i2c_bbs @i2c_bbs ||= [] end |
#i2c_limit ⇒ Object
Maximum amount of bytes that can be read or written in a single I2C operation.
8 9 10 |
# File 'lib/denko/piboard_i2c.rb', line 8 def i2c_limit 65535 end |
#i2c_read(index, address, register, read_length, frequency = nil, repeated_start = false) ⇒ Object
CMD = 35
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/denko/piboard_i2c.rb', line 44 def i2c_read(index, address, register, read_length, frequency=nil, repeated_start=false) i2c_mutex(index).synchronize do raise ArgumentError, "can't read more than #{i2c_limit} bytes to I2C" if read_length > i2c_limit handle = i2c_open(index, address) if register result = LGPIO.i2c_write_device(handle, register) i2c_c_error("read (register write)", result, index, address) if result < 0 end bytes = LGPIO.i2c_read_device(handle, read_length) i2c_close(handle) i2c_c_error("read", bytes, index, address) if bytes.class == Integer # Prepend the address (0th element) to the data, and update the bus. bytes.unshift(address) update_i2c(index, bytes) end end |
#i2c_search(index) ⇒ Object
CMD = 33
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/denko/piboard_i2c.rb', line 13 def i2c_search(index) i2c_mutex(index).synchronize do found_string = "" # I2C device may have reserved addresses. Exclude them. addresses = I2C_ADDRESS_RANGE - map[:i2cs][index][:reserved_addresses].to_a addresses.each do |address| handle = i2c_open(index, address) bytes = LGPIO.i2c_read_device(handle, 1) found_string << "#{address}:" if bytes[0] > 0 i2c_close(handle) end update_i2c(index, found_string) end end |
#i2c_write(index, address, bytes, frequency = nil, repeated_start = false) ⇒ Object
CMD = 34
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/denko/piboard_i2c.rb', line 32 def i2c_write(index, address, bytes, frequency=nil, repeated_start=false) i2c_mutex(index).synchronize do raise ArgumentError, "exceeded #{i2c_limit} bytes for #i2c_write" if bytes.length > i2c_limit handle = i2c_open(index, address) result = LGPIO.i2c_write_device(handle, bytes) i2c_close(handle) i2c_c_error("write", result, index, address) if result < 0 end end |
#infrared_emit(pin, frequency, pulses) ⇒ Object
3 4 5 6 7 8 9 10 11 12 |
# File 'lib/denko/piboard_infrared.rb', line 3 def infrared_emit(pin, frequency, pulses) # Main gem uses frequency in kHz. Set it in Hz. pwm = hardware_pwm_from_pin(pin, frequency: frequency*1000) # The actual strings for the sysfs PWM interface. duty_path = "#{pwm.path}duty_cycle" duty_ns = (0.333333 * pwm.period).round.to_s pwm.tx_wave_ook(duty_path, duty_ns, pulses) end |
#load_gpiochip_lookup_optimizations ⇒ Object
Monkey patch to eliminate lookups, and improve performance, when all GPIO lines are on a single gpiochip, and the readable GPIO numbers exactly match the GPIO line numbers.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/denko/piboard_map.rb', line 71 def load_gpiochip_lookup_optimizations @gpiochip_lookup_optimized = false # Makes performance slightly worse on YJIT? return if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? # All pins must be defined on the same gpiochip. unique_gpiochips = map[:pins].each_value.map { |pin_def| pin_def[:chip] }.uniq return if unique_gpiochips.length != 1 # For each pin, the key integer must be equal to the line integer. map[:pins].each_pair do |gpio_num, pin_def| return unless (gpio_num == pin_def[:line]) end # Open the handle so it can be given as a literal in the optimized methods. gpiochip_single_handle = gpio_handle(unique_gpiochips.first) code = File.read(File.dirname(__FILE__) + "/piboard_core_optimize_lookup.rb") code = code.gsub("__GPIOCHIP_SINGLE_HANDLE__", gpiochip_single_handle.to_s) singleton_class.class_eval(code) @gpiochip_lookup_optimized = true end |
#low ⇒ Object
11 |
# File 'lib/denko/piboard_base.rb', line 11 def low; LOW; end |
#micro_delay(duration) ⇒ Object
153 154 155 |
# File 'lib/denko/piboard_core.rb', line 153 def micro_delay(duration) LGPIO.micro_delay(duration) end |
#no_tone(pin) ⇒ Object
CMD = 18
20 21 22 |
# File 'lib/denko/piboard_tone.rb', line 20 def no_tone(pin) digital_write(pin, HIGH) end |
#one_wire_interface(pin) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/denko/piboard_one_wire.rb', line 7 def one_wire_interface(pin) handle, gpio = gpio_tuple(pin) # Check if any already exists with that array and return it. one_wires.each { |bb| return bb if (handle == bb.handle && gpio == bb.gpio) } # If not, create one. one_wire = LGPIO::OneWire.new(handle, gpio) one_wires << one_wire one_wire end |
#one_wire_read(gpio, length) ⇒ Object
36 37 38 39 40 |
# File 'lib/denko/piboard_one_wire.rb', line 36 def one_wire_read(gpio, length) interface = one_wire_interface(gpio) result_array = interface.read(length) self.update(gpio, result_array) end |
#one_wire_reset(gpio, check_presence = 0) ⇒ Object
19 20 21 22 23 |
# File 'lib/denko/piboard_one_wire.rb', line 19 def one_wire_reset(gpio, check_presence=0) interface = one_wire_interface(gpio) presence = interface.reset ? 0 : 1 self.update(gpio, [presence]) if check_presence != 0 end |
#one_wire_search(gpio, branch_mask) ⇒ Object
25 26 27 28 29 |
# File 'lib/denko/piboard_one_wire.rb', line 25 def one_wire_search(gpio, branch_mask) interface = one_wire_interface(gpio) result_array = interface.search_pass(branch_mask) self.update(gpio, result_array) end |
#one_wire_write(gpio, parasite, *data) ⇒ Object
31 32 33 34 |
# File 'lib/denko/piboard_one_wire.rb', line 31 def one_wire_write(gpio, parasite, *data) interface = one_wire_interface(gpio) interface.write(data.flatten, parasite: parasite) end |
#one_wires ⇒ Object
3 4 5 |
# File 'lib/denko/piboard_one_wire.rb', line 3 def one_wires @one_wires ||= [] end |
#parse_map(map_yaml) ⇒ Object
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/denko/piboard_map.rb', line 9 def parse_map(map_yaml) @map = YAML.load_file(map_yaml, symbolize_names: true) # Validate GPIO chip and line numbers for pins. Also build a lookup table for alerts. @alert_lut = [] map[:pins].each_pair do |k, h| raise StandardError, "invalid pin number: #{k} in YAML map :pins. Should be Integer" unless k.class == Integer raise StandardError, "invalid GPIO chip for GPIO #{k[:chip]}. Should be Integer" unless h[:chip].class == Integer raise StandardError, "invalid GPIO chip for GPIO #{k[:line]}. Should be Integer" unless h[:line].class == Integer @alert_lut[h[:chip]] ||= [] @alert_lut[h[:chip]][h[:line]] = k end # Validate PWMs map[:pwms].each_pair do |k, h| raise StandardError, "invalid pin number: #{k} in YAML map :pwms. Should be Integer" unless k.class == Integer raise StandardError, "invalid pwmchip: #{h[:pwmchip]}} for pin #{k}. Should be Integer" unless h[:pwmchip].class == Integer raise StandardError, "invalid channel: #{h[:channel]}} for pin #{k}. Should be Integer" unless h[:channel].class == Integer dev_path = "/sys/class/pwm/pwmchip#{h[:pwmchip]}/pwm#{h[:channel]}" raise StandardError, "board map error. Pin #{k} appears to be bound to both #{dev_path} and #{bound_pins[k]}" if bound_pins[k] bound_pins[k] = dev_path end # Validate I2Cs map[:i2cs].each_pair do |k, h| raise StandardError, "invalid I2C index: #{k} in YAML map :i2cs. Should be Integer" unless k.class == Integer dev_path = "/dev/i2c-#{k}" [:scl, :sda].each do |pin_sym| pin = h[pin_sym] raise StandardError, "missing #{pin_sym}: for I2C#{k}" unless pin raise StandardError, "invalid #{pin_sym}: #{pin} for I2C#{k}. Should be Integer" unless pin.class == Integer raise StandardError, "board map error. Pin #{pin} appears to be bound to both #{dev_path} and #{bound_pins[pin]}" if bound_pins[pin] bound_pins[pin] = dev_path end end # Validate SPIs map[:spis].each_pair do |k, h| raise StandardError, "invalid SPI index: #{k} in YAML map :spis. Should be Integer" unless k.class == Integer dev_path = "dev/spidev#{k}.0" [:clk, :mosi, :miso, :cs0].each do |pin_sym| pin = h[pin_sym] raise StandardError, "missing #{pin_sym}: for SPI#{k}" if (pin_sym == :clk) && !pin next unless pin raise StandardError, "invalid #{pin_sym}: #{pin} for SPI#{k}. Should be Integer" unless pin.class == Integer raise StandardError, "board map error. Pin #{pin} appears to be bound to both #{dev_path} and #{bound_pins[pin]}" if bound_pins[pin] bound_pins[pin] = dev_path end end load_gpiochip_lookup_optimizations end |
#pin_configs ⇒ Object
186 187 188 |
# File 'lib/denko/piboard_core.rb', line 186 def pin_configs @pin_configs ||= [] end |
#pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200) ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/denko/piboard_pulse.rb', line 10 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) pulses = LGPIO.gpio_read_pulses_us(@gpio_handle, pin, reset_time, reset, pulse_limit, timeout) if pulses.class == Array self.update(pin, pulses.join(",")) elsif pulse.class == Integer raise "could not read pulses from GPIO #{pin}. LGPIO error: #{pulses}" end end |
#pwm_high ⇒ Object
13 |
# File 'lib/denko/piboard_base.rb', line 13 def pwm_high; PWM_HIGH; end |
#pwm_write(pin, duty) ⇒ Object
74 75 76 77 78 79 80 81 82 |
# File 'lib/denko/piboard_core.rb', line 74 def pwm_write(pin, duty) if hardware_pwms[pin] hardware_pwms[pin].duty_percent = duty else frequency = pin_configs[pin][:frequency] || 1000 handle, line = gpio_tuple(pin) LGPIO.tx_pwm(handle, line, frequency, duty, 0, 0) end end |
#servo_toggle(pin, value = :off, options = {}) ⇒ Object
CMD = 10
4 5 6 7 8 9 10 11 12 |
# File 'lib/denko/piboard_servo.rb', line 4 def servo_toggle(pin, value=:off, ={}) pwm = hardware_pwm_from_pin(pin) if (value == :off) pwm.duty_cycle = 0 pwm.disable elsif pwm.frequency = 50 end end |
#servo_write(pin, value = 0) ⇒ Object
CMD = 11
15 16 17 |
# File 'lib/denko/piboard_servo.rb', line 15 def servo_write(pin, value=0) hardware_pwms[pin].duty_us = value end |
#set_analog_read_resolution(value) ⇒ Object
145 146 147 |
# File 'lib/denko/piboard_core.rb', line 145 def set_analog_read_resolution(value) raise NotImplementedError, "PiBoard#set_analog_read_resolution not implemented" end |
#set_analog_write_resolution(value) ⇒ Object
141 142 143 |
# File 'lib/denko/piboard_core.rb', line 141 def set_analog_write_resolution(value) raise NotImplementedError, "PiBoard#set_analog_write_resolution not implemented" end |
#set_listener(pin, state = :off, options = {}) ⇒ Object
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/denko/piboard_core.rb', line 92 def set_listener(pin, state=:off, ={}) # Validate listener is digital only. [:mode] ||= :digital unless [:mode] == :digital raise ArgumentError, "error in mode: #{[:mode]}. Should be one of: [:digital]" end # Validate state. unless (state == :on) || (state == :off) raise ArgumentError, "error in state: #{[:state]}. Should be one of: [:on, :off]" end # Only way to stop getting alerts is to free the GPIO. LGPIO.gpio_free(*gpio_tuple(pin)) # Reclaim it as input if needed. config = pin_configs[pin] config ||= { mode: :input, debounce_time: nil } if state == :on if config set_pin_mode(pin, config[:mode]) set_pin_debounce(pin, config[:debounce_time]) end if state == :on LGPIO.gpio_claim_alert(*gpio_tuple(pin), 0, LGPIO::BOTH_EDGES) start_alert_thread unless @alert_thread end end |
#set_pin_debounce(pin, debounce_time) ⇒ Object
46 47 48 49 50 51 52 |
# File 'lib/denko/piboard_core.rb', line 46 def set_pin_debounce(pin, debounce_time) return unless debounce_time result = LGPIO.gpio_set_debounce(*gpio_tuple(pin), debounce_time) raise "could not set debounce for pin #{pin}. lgpio C error: #{result}" if result < 0 pin_configs[pin] = pin_configs[pin].to_h.merge(debounce_time: debounce_time) end |
#set_pin_mode(pin, mode = :input, options = {}) ⇒ Object
8 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 |
# File 'lib/denko/piboard_core.rb', line 8 def set_pin_mode(pin, mode=:input, ={}) # Is the mode valid? unless PIN_MODES.include?(mode) raise ArgumentError, "cannot set mode: #{mode}. Should be one of: #{PIN_MODES.inspect}" end # If pin is bound to hardware PWM, allow it to be used as :output_pwm. OR :output. if map[:pwms][pin] if (mode == :output_pwm) return hardware_pwm_from_pin(pin, ) elsif (mode == :output) puts "WARNING: using hardware PWM on pin #{pin} as GPIO. Will be slower than regular GPIO." return hardware_pwm_from_pin(pin, ) else raise "Pin #{pin} is bound to hardware PWM. It can only be used as :output or :output_pwm" end end # Attempt to free the pin. LGPIO.gpio_free(*gpio_tuple(pin)) # Try to claim the GPIO. if OUTPUT_MODES.include?(mode) flags = LGPIO::SET_PULL_NONE flags = LGPIO::SET_OPEN_DRAIN if mode == :output_open_drain flags = LGPIO::SET_OPEN_SOURCE if mode == :output_open_source result = LGPIO.gpio_claim_output(*gpio_tuple(pin), flags, LOW) else flags = LGPIO::SET_PULL_NONE flags = LGPIO::SET_PULL_UP if mode == :input_pullup flags = LGPIO::SET_PULL_DOWN if mode == :input_pulldown result = LGPIO.gpio_claim_input(*gpio_tuple(pin), flags) end raise "could not claim GPIO for pin #{pin}. lgpio C error: #{result}" if result < 0 pin_configs[pin] = pin_configs[pin].to_h.merge(mode: mode).merge() end |
#set_register_divider(value) ⇒ Object
137 138 139 |
# File 'lib/denko/piboard_core.rb', line 137 def set_register_divider(value) raise NotImplementedError, "PiBoard#set_register_divider not implemented" end |
#show_ws2812(pin, pixel_buffer, spi_index:) ⇒ Object
3 4 5 6 7 |
# File 'lib/denko/piboard_led_array.rb', line 3 def show_ws2812(pin, pixel_buffer, spi_index:) handle = spi_open(spi_index, 2_400_000, 0) LGPIO.spi_ws2812_write(handle, pixel_buffer) spi_close(handle) end |
#spi_bb_interface(clock, input, output) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/denko/piboard_spi_bb.rb', line 9 def spi_bb_interface(clock, input, output) # Convert the pins into a config array to check. ch, cl = gpio_tuple(clock) ih, il = input ? gpio_tuple(input) : [nil, nil] oh, ol = output ? gpio_tuple(output) : [nil, nil] config = [ch, cl, ih, il, oh, ol] # Check if any already exists with that array and return it. spi_bbs.each { |bb| return bb if (config == bb.config) } # If not, create one. hash = { clock: { handle: ch, line: cl }, input: { handle: ih, line: il }, output: { handle: oh, line: ol } } spi_bb = LGPIO::SPIBitBang.new(hash) spi_bbs << spi_bb spi_bb end |
#spi_bb_listen(*arg, **kwargs) ⇒ Object
37 38 39 |
# File 'lib/denko/piboard_spi_bb.rb', line 37 def spi_bb_listen(*arg, **kwargs) raise NotImplementedError, "PiBoard#spi_bb_listen not implemented yet" end |
#spi_bb_transfer(select, clock:, input: nil, output: nil, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil) ⇒ Object
29 30 31 32 33 34 35 |
# File 'lib/denko/piboard_spi_bb.rb', line 29 def spi_bb_transfer(select, clock:, input: nil, output: nil, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil) interface = spi_bb_interface(clock, input, output) select_hash = select ? { handle: gpio_tuple(select)[0], line: gpio_tuple(select)[0] } : nil bytes = interface.transfer(write: write, read: read, select: select_hash, order: bit_order, mode: mode) self.update(select, bytes) if (read > 0 && select) end |
#spi_flags(mode) ⇒ Object
3 4 5 6 7 8 9 10 11 |
# File 'lib/denko/piboard_spi.rb', line 3 def spi_flags(mode) mode ||= 0 raise ArgumentError, "invalid SPI mode #{mode}" unless (0..3).include? mode # Flags is a 32-bit mask. Bits [1..0] are the SPI mode. Default to 0. config = mode return config end |
#spi_listen(*arg, **kwargs) ⇒ Object
31 32 33 |
# File 'lib/denko/piboard_spi.rb', line 31 def spi_listen(*arg, **kwargs) raise NotImplementedError, "PiBoard#spi_listen not implemented yet" end |
#spi_listeners ⇒ Object
38 39 40 |
# File 'lib/denko/piboard_spi.rb', line 38 def spi_listeners @spi_listeners ||= Array.new end |
#spi_stop(pin) ⇒ Object
35 36 |
# File 'lib/denko/piboard_spi.rb', line 35 def spi_stop(pin) end |
#spi_transfer(index, select, write: [], read: 0, frequency: 1_000_000, mode: 0, bit_order: nil) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/denko/piboard_spi.rb', line 13 def spi_transfer(index, select, write:[], read:0, frequency: 1_000_000, mode: 0, bit_order: nil) # Default frequency. Flags just has mode. frequency ||= 1_000_000 flags = spi_flags(mode) handle = spi_open(index, frequency, flags) # Handle select_pin unless it's same as CS0 for this interface. digital_write(select, 0) if select && (select != map[:spis][index][:cs0]) bytes = LGPIO.spi_xfer(handle, write, read) spi_close(handle) digital_write(select, 1) if select && (select != map[:spis][index][:cs0]) spi_c_error("xfer", bytes, index) if bytes.class == Integer # Update component attached to select pin with read bytes. self.update(select, bytes) if (read > 0 && select) end |
#start_alert_thread ⇒ Object
157 158 159 160 |
# File 'lib/denko/piboard_core.rb', line 157 def start_alert_thread start_gpio_reports @alert_thread = Thread.new { loop { get_report } } end |
#start_gpio_reports ⇒ Object
180 181 182 183 184 |
# File 'lib/denko/piboard_core.rb', line 180 def start_gpio_reports return if @reporting_started LGPIO.gpio_start_reporting @reporting_started = true end |
#stop_alert_thread ⇒ Object
162 163 164 165 |
# File 'lib/denko/piboard_core.rb', line 162 def stop_alert_thread Thread.kill(@alert_thread) if @alert_thread @alert_thread = nil end |
#stop_listener(pin) ⇒ Object
129 130 131 |
# File 'lib/denko/piboard_core.rb', line 129 def stop_listener(pin) set_listener(pin, :off) end |
#tone(pin, frequency, duration = nil) ⇒ Object
CMD = 17
4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/denko/piboard_tone.rb', line 4 def tone(pin, frequency, duration=nil) if @hardware_pwms[pin] @hardware_pwms[pin].frequency = frequency @hardware_pwms[pin].duty_percent = 33 sleep duration if duration else raise ArgumentError, "maximum software PWM frequency is 10 kHz" if frequency > 10_000 cycles = 0 cycles = (frequency * duration).round if duration sleep 0.05 while (LGPIO.tx_room(@gpio_handle, pin, LGPIO::TX_PWM) == 0) LGPIO.tx_pwm(@gpio_handle, pin, frequency, 33, 0, cycles) end end |
#tone_busy(pin) ⇒ Object
24 25 26 |
# File 'lib/denko/piboard_tone.rb', line 24 def tone_busy(pin) LGPIO.tx_busy(@gpio_handle, pin, LGPIO::TX_PWM) end |
#update(pin, message) ⇒ Object
29 30 31 32 33 |
# File 'lib/denko/piboard_base.rb', line 29 def update(pin, ) if single_pin_components[pin] single_pin_components[pin].update() end end |