denko-piboard 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +154 -132
  4. data/Rakefile +0 -5
  5. data/board_maps/README.md +59 -0
  6. data/board_maps/orange_pi_zero_2w.yml +85 -0
  7. data/board_maps/radxa_zero3.yml +88 -0
  8. data/board_maps/raspberry_pi.yml +95 -0
  9. data/denko_piboard.gemspec +6 -7
  10. data/examples/digital_io/bench_out.rb +22 -0
  11. data/examples/digital_io/rotary_encoder.rb +31 -0
  12. data/examples/display/ssd1306.rb +43 -0
  13. data/examples/i2c/bitbang_aht10.rb +18 -0
  14. data/examples/i2c/bitbang_search.rb +24 -0
  15. data/examples/i2c/bitbang_ssd1306_bench.rb +29 -0
  16. data/examples/i2c/search.rb +24 -0
  17. data/examples/led/blink.rb +10 -0
  18. data/examples/led/fade.rb +22 -0
  19. data/examples/led/ws2812_bounce.rb +36 -0
  20. data/examples/motor/servo.rb +16 -0
  21. data/examples/pi_system_monitor.rb +10 -8
  22. data/examples/pulse_io/buzzer.rb +34 -0
  23. data/examples/pulse_io/infrared.rb +25 -0
  24. data/examples/sensor/aht10.rb +17 -0
  25. data/examples/sensor/dht.rb +24 -0
  26. data/examples/sensor/ds18b20.rb +59 -0
  27. data/examples/sensor/hcsr04.rb +16 -0
  28. data/examples/sensor/neat_tph_readings.rb +32 -0
  29. data/examples/spi/bb_loopback.rb +31 -0
  30. data/examples/spi/loopback.rb +37 -0
  31. data/examples/spi/output_register.rb +38 -0
  32. data/lib/denko/piboard.rb +11 -2
  33. data/lib/denko/piboard_base.rb +18 -64
  34. data/lib/denko/piboard_core.rb +148 -130
  35. data/lib/denko/piboard_core_optimize_lookup.rb +31 -0
  36. data/lib/denko/piboard_hardware_pwm.rb +27 -0
  37. data/lib/denko/piboard_i2c.rb +59 -82
  38. data/lib/denko/piboard_i2c_bb.rb +48 -0
  39. data/lib/denko/piboard_infrared.rb +7 -44
  40. data/lib/denko/piboard_led_array.rb +9 -0
  41. data/lib/denko/piboard_map.rb +121 -38
  42. data/lib/denko/piboard_one_wire.rb +42 -0
  43. data/lib/denko/piboard_pulse.rb +11 -68
  44. data/lib/denko/piboard_servo.rb +8 -7
  45. data/lib/denko/piboard_spi.rb +44 -74
  46. data/lib/denko/piboard_spi_bb.rb +41 -0
  47. data/lib/denko/piboard_tone.rb +15 -26
  48. data/lib/denko/piboard_version.rb +1 -1
  49. data/scripts/99-denko.rules +9 -0
  50. data/scripts/set_permissions.rb +131 -0
  51. metadata +45 -17
  52. data/ext/gpiod/extconf.rb +0 -9
  53. data/ext/gpiod/gpiod.c +0 -179
  54. data/lib/gpiod.rb +0 -6
@@ -1,119 +1,96 @@
1
1
  module Denko
2
2
  class PiBoard
3
+ # Address ranges 0..7 and 120..127 are reserved.
4
+ # Try each address in 8..119 (0x08 to 0x77).
5
+ I2C_ADDRESS_RANGE = (0x08..0x77).to_a
6
+
3
7
  # Maximum amount of bytes that can be read or written in a single I2C operation.
4
8
  def i2c_limit
5
9
  65535
6
10
  end
7
11
 
8
- def i2c_mutex
9
- @i2c_mutex ||= Mutex.new
10
- end
11
-
12
12
  # CMD = 33
13
- def i2c_search
14
- i2c_mutex.synchronize do
13
+ def i2c_search(index)
14
+ i2c_mutex(index).synchronize do
15
15
  found_string = ""
16
16
 
17
- # Address ranges 0..7 and 120..127 are reserved.
18
- # Try each address in 8..19 (0x08 to 0x77).
19
- (0x08..0x77).each do |address|
20
- i2c_open(1, address)
21
- byte = Pigpio::IF.i2c_read_byte(pi_handle, i2c_handle)
22
- # Add to the string colon separated if byte was valid.
23
- found_string << "#{address}:" if byte >= 0
24
- i2c_close
25
- end
17
+ # I2C device may have reserved addresses. Exclude them.
18
+ addresses = I2C_ADDRESS_RANGE - map[:i2cs][index][:reserved_addresses].to_a
26
19
 
27
- # Remove trailing colon.
28
- found_string.chop! unless found_string.empty?
20
+ addresses.each do |address|
21
+ handle = i2c_open(index, address)
22
+ bytes = LGPIO.i2c_read_device(handle, 1)
23
+ found_string << "#{address}:" if bytes[0] > 0
24
+ i2c_close(handle)
25
+ end
29
26
 
30
- # Update the bus as if message came from microcontroller.
31
- self.update(2, found_string)
27
+ update_i2c(index, found_string)
32
28
  end
33
29
  end
34
30
 
35
31
  # CMD = 34
36
- def i2c_write(address, bytes, frequency=100000, repeated_start=false)
37
- i2c_mutex.synchronize do
38
- raise ArgumentError, "can't write more than #{i2c_limit} bytes to I2C" if bytes.length > i2c_limit
39
-
40
- # Pack length as a 16-bit uint, then unpack it into 2 litle endian bytes.
41
- length = [bytes.length].pack("S").unpack("C*")
42
-
43
- # Prepend length to the bytes.
44
- bytes = length + bytes
45
-
46
- # Prepend write command and escape character (necessary for double byte write length).
47
- bytes = [0x01, 0x07] + bytes
48
-
49
- # Enable and (re)disable repeated start as needed.
50
- bytes = [0x02] + bytes + [0x03] if repeated_start
51
-
52
- # Null terminate the command sequence.
53
- bytes = bytes + [0x00]
54
-
55
- # Send the command to the I2C1 interface, packed as uint8 string.
56
- i2c_open(1, address)
57
- Pigpio::IF.i2c_zip(pi_handle, i2c_handle, bytes.pack("C*"), 0)
58
- i2c_close
32
+ def i2c_write(index, address, bytes, frequency=nil, repeated_start=false)
33
+ i2c_mutex(index).synchronize do
34
+ raise ArgumentError, "exceeded #{i2c_limit} bytes for #i2c_write" if bytes.length > i2c_limit
35
+
36
+ handle = i2c_open(index, address)
37
+ result = LGPIO.i2c_write_device(handle, bytes)
38
+ i2c_close(handle)
39
+ i2c_c_error("write", result, index, address) if result < 0
59
40
  end
60
41
  end
61
42
 
62
43
  # CMD = 35
63
- def i2c_read(address, register, read_length, frequency=100000, repeated_start=false)
64
- i2c_mutex.synchronize do
44
+ def i2c_read(index, address, register, read_length, frequency=nil, repeated_start=false)
45
+ i2c_mutex(index).synchronize do
65
46
  raise ArgumentError, "can't read more than #{i2c_limit} bytes to I2C" if read_length > i2c_limit
66
-
67
- # Start with number of bytes to read (16-bit number) represented as 2 little endian bytes.
68
- buffer = [read_length].pack("S").unpack("C*")
69
-
70
- # Prepend read command and escape character (necessary for double byte write length).
71
- buffer = [0x01, 0x06] + buffer
72
47
 
73
- # If a start register was given, write it first.
48
+ handle = i2c_open(index, address)
74
49
  if register
75
- register = [register].flatten
76
- raise ArgumentError, "can't pre-write a register address > 4 bytes" if register.length > 4
77
- buffer = [0x07, register.length] + register + buffer
50
+ result = LGPIO.i2c_write_device(handle, register)
51
+ i2c_c_error("read (register write)", result, index, address) if result < 0
78
52
  end
79
53
 
80
- # Enable and (re)disable repeated start as needed.
81
- buffer = [0x02] + buffer + [0x03] if repeated_start
54
+ bytes = LGPIO.i2c_read_device(handle, read_length)
55
+ i2c_close(handle)
56
+ i2c_c_error("read", bytes, index, address) if bytes.class == Integer
82
57
 
83
- # Null terminate the command sequence.
84
- buffer = buffer + [0x00]
85
-
86
- # Send the command to the I2C1 interface, packed as uint8 string.
87
- i2c_open(1, address)
88
- read_bytes = Pigpio::IF.i2c_zip(pi_handle, i2c_handle, buffer.pack("C*"), read_length)
89
- i2c_close
90
-
91
- # Pigpio returned an error. Denko expects blank message after address.
92
- if read_bytes.class == Integer
93
- message = "#{address}-"
94
- else
95
- # Format the bytes like denko expects from a microcontroller.
96
- message = read_bytes.split("").map { |byte| byte.ord.to_s }.join(",")
97
- message = "#{address}-#{message}"
98
- end
99
-
100
- # Call update with the message, as if it came from pin 2 (I2C1 SDA pin).
101
- self.update(2, message)
58
+ # Prepend the address (0th element) to the data, and update the bus.
59
+ bytes.unshift(address)
60
+ update_i2c(index, bytes)
102
61
  end
103
62
  end
104
63
 
105
64
  private
106
65
 
107
- attr_reader :i2c_handle
66
+ def i2c_mutex(index)
67
+ i2c_mutexes[index] ||= Mutex.new
68
+ end
69
+
70
+ def i2c_mutexes
71
+ @i2c_mutexes ||= []
72
+ end
73
+
74
+ def i2c_open(index, address, flags=0x00)
75
+ handle = LGPIO.i2c_open(index, address, flags)
76
+ i2c_c_error("open", handle, index, address) if handle < 0
77
+ handle
78
+ end
79
+
80
+ def i2c_close(handle)
81
+ result = LGPIO.i2c_close(handle)
82
+ if result < 0
83
+ raise StandardError, "lgpio C I2C close error: #{result} for handle #{handle}"
84
+ end
85
+ end
108
86
 
109
- def i2c_open(bus_index, address)
110
- @i2c_handle = Pigpio::IF.i2c_open(pi_handle, bus_index, address, 0)
111
- raise StandardError, "I2C error, code #{@i2c_handle}" if @i2c_handle < 0
87
+ def i2c_c_error(name, error, index, address)
88
+ raise StandardError, "lgpio C I2C #{name} error: #{error} for /dev/i2c-#{index} with address 0x#{address.to_s(16)}"
112
89
  end
113
90
 
114
- def i2c_close
115
- Pigpio::IF.i2c_close(pi_handle, i2c_handle)
116
- @i2c_handle = nil
91
+ def update_i2c(index, data)
92
+ dev = hw_i2c_comps[index]
93
+ dev.update(data) if dev
117
94
  end
118
95
  end
119
96
  end
@@ -0,0 +1,48 @@
1
+ module Denko
2
+ class PiBoard
3
+ def i2c_bbs
4
+ @i2c_bbs ||= []
5
+ end
6
+
7
+ def i2c_bb_interface(scl, sda)
8
+ # Convert the pins into a config array to check.
9
+ ch, cl = gpio_tuple(scl)
10
+ dh, dl = gpio_tuple(sda)
11
+ config = [ch, cl, dh, dl]
12
+
13
+ # Check if any already exists with that array and return it.
14
+ i2c_bbs.each { |bb| return bb if (config == bb.config) }
15
+
16
+ # If not, create one.
17
+ hash = { scl: { handle: ch, line: cl },
18
+ sda: { handle: dh, line: dl } }
19
+
20
+ i2c_bb = LGPIO::I2CBitBang.new(hash)
21
+ i2c_bbs << i2c_bb
22
+ i2c_bb
23
+ end
24
+
25
+ def i2c_bb_search(scl, sda)
26
+ interface = i2c_bb_interface(scl, sda)
27
+ devices = interface.search
28
+ found_string = ""
29
+ found_string = devices.join(":") if devices
30
+ self.update(sda, found_string)
31
+ end
32
+
33
+ def i2c_bb_write(scl, sda, address, bytes, repeated_start=false)
34
+ interface = i2c_bb_interface(scl, sda)
35
+ interface.write(address, bytes)
36
+ end
37
+
38
+ def i2c_bb_read(scl, sda, address, register, read_length, repeated_start=false)
39
+ interface = i2c_bb_interface(scl, sda)
40
+ interface.write(address, register) if register
41
+ bytes = interface.read(address, read_length)
42
+
43
+ # Prepend the address (0th element) to the data, and update the SDA pin.
44
+ bytes.unshift(address)
45
+ self.update(sda, bytes)
46
+ end
47
+ end
48
+ end
@@ -1,51 +1,14 @@
1
1
  module Denko
2
2
  class PiBoard
3
3
  def infrared_emit(pin, frequency, pulses)
4
- # 32-bit mask where only the bit corresponding to the GPIO in use is set.
5
- pin_mask = 1 << pin
6
-
7
- # IR frequency given in kHz. Find half wave time in microseconds.
8
- if @aarch64
9
- # Compensate for pigpio doubling wave times on 64-bit systems.
10
- half_wave_time = (250.0 / frequency)
11
- else
12
- # True half wave time.
13
- half_wave_time = (500.0 / frequency)
14
- end
4
+ # Main gem uses frequency in kHz. Set it in Hz.
5
+ pwm = hardware_pwm_from_pin(pin, frequency: frequency*1000)
15
6
 
16
- # Standard wave setup.
17
- new_wave
18
- wave.tx_stop
19
- wave.clear
20
- wave.add_new
21
-
22
- # Build an array of pulses to add to the wave.
23
- wave_pulses = []
24
- pulses.each_with_index do |pulse, index|
25
- # Even indices send the carrier wave.
26
- if (index % 2 == 0)
27
- cycles = (pulse / (half_wave_time * 2)).round
28
- cycles.times do
29
- wave_pulses << wave.pulse(pin_mask, 0x00, half_wave_time)
30
- wave_pulses << wave.pulse(0x00, pin_mask, half_wave_time)
31
- end
32
-
33
- # Odd indices are idle.
34
- else
35
- if @aarch64
36
- # Half idle times for 64-bit systems.
37
- wave_pulses << wave.pulse(0x00, pin_mask, pulse / 2)
38
- else
39
- wave_pulses << wave.pulse(0x00, pin_mask, pulse)
40
- end
41
- end
42
- end
43
- wave.add_generic(wave_pulses)
44
- wave_id = wave.create
45
-
46
- # Temporary workaround while Wave#send_once gets fixed.
47
- Pigpio::IF.wave_send_once(@pi_handle, wave_id)
48
- # wave.send_once(wave_id)
7
+ # The actual strings for the sysfs PWM interface.
8
+ duty_path = "#{pwm.path}duty_cycle"
9
+ duty_ns = (0.333333 * pwm.period).round.to_s
10
+
11
+ pwm.tx_wave_ook(duty_path, duty_ns, pulses)
49
12
  end
50
13
  end
51
14
  end
@@ -0,0 +1,9 @@
1
+ module Denko
2
+ class PiBoard
3
+ def show_ws2812(pin, pixel_buffer, spi_index:)
4
+ handle = spi_open(spi_index, 2_400_000, 0)
5
+ LGPIO.spi_ws2812_write(handle, pixel_buffer)
6
+ spi_close(handle)
7
+ end
8
+ end
9
+ end
@@ -1,49 +1,132 @@
1
+ require 'yaml'
2
+
1
3
  module Denko
2
4
  class PiBoard
3
- MAP = {
4
- # Only use SPI1 interface.
5
- SS: 18,
6
- MISO: 19,
7
- MOSI: 20,
8
- SCK: 21,
9
-
10
- # Only use I2C1 interface.
11
- SDA: 2,
12
- SCL: 3,
13
-
14
- # Single UART
15
- TX: 14,
16
- RX: 15,
17
- }
18
-
19
- def map
20
- MAP
21
- end
5
+ DEFAULT_MAP_FILE = ".denko_piboard_map.yml"
22
6
 
23
- def convert_pin(pin)
24
- # Convert non numerical strings to symbols.
25
- pin = pin.to_sym if (pin.class == String) && !(pin.match (/\A\d+\.*\d*/))
26
-
27
- # Handle symbols.
28
- if (pin.class == Symbol)
29
- if map && map[pin]
30
- return map[pin]
31
- elsif map
32
- raise ArgumentError, "error in pin: #{pin.inspect}. Make sure that pin is defined for this board by calling Board#map"
33
- else
34
- raise ArgumentError, "error in pin: #{pin.inspect}. Given a Symbol, but board has no map. Try using GPIO integer instead"
7
+ attr_reader :map, :alert_lut, :gpiochip_lookup_optimized
8
+
9
+ def parse_map(map_yaml)
10
+ @map = YAML.load_file(map_yaml, symbolize_names: true)
11
+
12
+ # Validate GPIO chip and line numbers for pins. Also build a lookup table for alerts.
13
+ @alert_lut = []
14
+ map[:pins].each_pair do |k, h|
15
+ raise StandardError, "invalid pin number: #{k} in YAML map :pins. Should be Integer" unless k.class == Integer
16
+ raise StandardError, "invalid GPIO chip for GPIO #{k[:chip]}. Should be Integer" unless h[:chip].class == Integer
17
+ raise StandardError, "invalid GPIO chip for GPIO #{k[:line]}. Should be Integer" unless h[:line].class == Integer
18
+ @alert_lut[h[:chip]] ||= []
19
+ @alert_lut[h[:chip]][h[:line]] = k
20
+ end
21
+
22
+ # Validate PWMs
23
+ map[:pwms].each_pair do |k, h|
24
+ raise StandardError, "invalid pin number: #{k} in YAML map :pwms. Should be Integer" unless k.class == Integer
25
+ raise StandardError, "invalid pwmchip: #{h[:pwmchip]}} for pin #{k}. Should be Integer" unless h[:pwmchip].class == Integer
26
+ raise StandardError, "invalid channel: #{h[:channel]}} for pin #{k}. Should be Integer" unless h[:channel].class == Integer
27
+
28
+ dev_path = "/sys/class/pwm/pwmchip#{h[:pwmchip]}/pwm#{h[:channel]}"
29
+ raise StandardError, "board map error. Pin #{k} appears to be bound to both #{dev_path} and #{bound_pins[k]}" if bound_pins[k]
30
+ bound_pins[k] = dev_path
31
+ end
32
+
33
+ # Validate I2Cs
34
+ map[:i2cs].each_pair do |k, h|
35
+ raise StandardError, "invalid I2C index: #{k} in YAML map :i2cs. Should be Integer" unless k.class == Integer
36
+ dev_path = "/dev/i2c-#{k}"
37
+
38
+ [:scl, :sda].each do |pin_sym|
39
+ pin = h[pin_sym]
40
+ raise StandardError, "missing #{pin_sym}: for I2C#{k}" unless pin
41
+ raise StandardError, "invalid #{pin_sym}: #{pin} for I2C#{k}. Should be Integer" unless pin.class == Integer
42
+ raise StandardError, "board map error. Pin #{pin} appears to be bound to both #{dev_path} and #{bound_pins[pin]}" if bound_pins[pin]
43
+ bound_pins[pin] = dev_path
44
+ end
45
+ end
46
+
47
+ # Validate SPIs
48
+ map[:spis].each_pair do |k, h|
49
+ raise StandardError, "invalid SPI index: #{k} in YAML map :spis. Should be Integer" unless k.class == Integer
50
+ dev_path = "dev/spidev#{k}.0"
51
+
52
+ [:clk, :mosi, :miso, :cs0].each do |pin_sym|
53
+ pin = h[pin_sym]
54
+ raise StandardError, "missing #{pin_sym}: for SPI#{k}" if (pin_sym == :clk) && !pin
55
+ next unless pin
56
+
57
+ raise StandardError, "invalid #{pin_sym}: #{pin} for SPI#{k}. Should be Integer" unless pin.class == Integer
58
+ raise StandardError, "board map error. Pin #{pin} appears to be bound to both #{dev_path} and #{bound_pins[pin]}" if bound_pins[pin]
59
+ bound_pins[pin] = dev_path
35
60
  end
36
61
  end
37
62
 
38
- # Handle integers.
39
- return pin if pin.class == Integer
63
+ load_gpiochip_lookup_optimizations
64
+ end
65
+
66
+ #
67
+ # Monkey patch to eliminate lookups, and improve performance,
68
+ # when all GPIO lines are on a single gpiochip, and the readable
69
+ # GPIO numbers exactly match the GPIO line numbers.
70
+ #
71
+ def load_gpiochip_lookup_optimizations
72
+ @gpiochip_lookup_optimized = false
73
+
74
+ # Makes performance slightly worse on YJIT?
75
+ return if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
76
+
77
+ # All pins must be defined on the same gpiochip.
78
+ unique_gpiochips = map[:pins].each_value.map { |pin_def| pin_def[:chip] }.uniq
79
+ return if unique_gpiochips.length != 1
40
80
 
41
- # Try #to_i on anyting else. Will catch numerical strings.
42
- begin
43
- return pin.to_i
44
- rescue
45
- raise ArgumentError, "error in pin: #{pin.inspect}"
81
+ # For each pin, the key integer must be equal to the line integer.
82
+ map[:pins].each_pair do |gpio_num, pin_def|
83
+ return unless (gpio_num == pin_def[:line])
46
84
  end
85
+
86
+ # Open the handle so it can be given as a literal in the optimized methods.
87
+ gpiochip_single_handle = gpio_handle(unique_gpiochips.first)
88
+
89
+ code = File.read(File.dirname(__FILE__) + "/piboard_core_optimize_lookup.rb")
90
+ code = code.gsub("__GPIOCHIP_SINGLE_HANDLE__", gpiochip_single_handle.to_s)
91
+
92
+ singleton_class.class_eval(code)
93
+ @gpiochip_lookup_optimized = true
94
+ end
95
+
96
+ # Make a new tuple, given a human-readable pin number, using values from the map.
97
+ def gpio_tuple(index)
98
+ return gpio_tuples[index] if gpio_tuples[index]
99
+
100
+ raise ArgumentError, "pin #{index} does not exist or not included in map" unless map[:pins][index]
101
+ raise ArgumentError, "pin #{index} cannot be used as GPIO. Bound to #{bound_pins[index]}" if bound_pins[index]
102
+
103
+ handle = gpio_handle(map[:pins][index][:chip])
104
+ line = map[:pins][index][:line]
105
+
106
+ gpio_tuples[index] = [handle, line]
107
+ end
108
+
109
+ # Cache tuples of [handle, line_number], keyed to human-readable pin numbers.
110
+ def gpio_tuples
111
+ @gpio_tuples ||= []
112
+ end
113
+
114
+ # Store multiple LGPIO handles, since one board might have multiple chips.
115
+ def gpio_handle(index)
116
+ gpio_handles[index] ||= LGPIO.chip_open(index)
117
+ end
118
+
119
+ def gpio_handles
120
+ @gpio_handles ||= []
121
+ end
122
+
123
+ # Keep track of pins bound by non-GPIO peripherals.
124
+ def bound_pins
125
+ @bound_pins ||= []
126
+ end
127
+
128
+ def convert_pin(pin)
129
+ pin.to_i if pin
47
130
  end
48
131
  end
49
132
  end
@@ -0,0 +1,42 @@
1
+ module Denko
2
+ class PiBoard
3
+ def one_wires
4
+ @one_wires ||= []
5
+ end
6
+
7
+ def one_wire_interface(pin)
8
+ handle, gpio = gpio_tuple(pin)
9
+
10
+ # Check if any already exists with that array and return it.
11
+ one_wires.each { |bb| return bb if (handle == bb.handle && gpio == bb.gpio) }
12
+
13
+ # If not, create one.
14
+ one_wire = LGPIO::OneWire.new(handle, gpio)
15
+ one_wires << one_wire
16
+ one_wire
17
+ end
18
+
19
+ def one_wire_reset(gpio, check_presence=0)
20
+ interface = one_wire_interface(gpio)
21
+ presence = interface.reset ? 0 : 1
22
+ self.update(gpio, [presence]) if check_presence != 0
23
+ end
24
+
25
+ def one_wire_search(gpio, branch_mask)
26
+ interface = one_wire_interface(gpio)
27
+ result_array = interface.search_pass(branch_mask)
28
+ self.update(gpio, result_array)
29
+ end
30
+
31
+ def one_wire_write(gpio, parasite, *data)
32
+ interface = one_wire_interface(gpio)
33
+ interface.write(data.flatten, parasite: parasite)
34
+ end
35
+
36
+ def one_wire_read(gpio, length)
37
+ interface = one_wire_interface(gpio)
38
+ result_array = interface.read(length)
39
+ self.update(gpio, result_array)
40
+ end
41
+ end
42
+ end
@@ -1,7 +1,12 @@
1
1
  require 'timeout'
2
2
 
3
3
  module Denko
4
- class PiBoard
4
+ class PiBoard
5
+ def hcsr04_read(echo_pin, trigger_pin)
6
+ microseconds = LGPIO.gpio_read_ultrasonic(@gpio_handle, trigger_pin, echo_pin, 10)
7
+ self.update(echo_pin, microseconds.to_s)
8
+ end
9
+
5
10
  def pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200)
6
11
  # Validation
7
12
  raise ArgumentError, "error in reset: #{reset}. Should be either #{high} or #{low}" if reset && ![high, low].include?(reset)
@@ -9,74 +14,12 @@ module Denko
9
14
  raise ArgumentError, "errror in pulse_limit: #{pulse_limit}. Should be 0..255 pulses" if (pulse_limit < 0) || (pulse_limit > 0xFF)
10
15
  raise ArgumentError, "errror in timeout: #{timeout}. Should be 0..65535 milliseconds" if (timeout < 0) || (timeout > 0xFFFF)
11
16
 
12
- if reset
13
- # Reset pulse will be captured as the first 2 edges.
14
- expected_edges = pulse_limit + 2
15
- else
16
- # First edge is a starting reference, so store 1 extra.
17
- expected_edges = pulse_limit + 1
18
- end
19
-
20
- # Storage for absolute tick time of each edge received from pigpio.
21
- edges = Array.new(expected_edges) {0}
22
- edge_index = 0
23
-
24
- # Switch to input mode immediately if no reset.
25
- set_pin_mode(pin, :input) unless reset
26
-
27
- # Add callback to catch edges.
28
- callback = get_gpio(pin).callback(EITHER_EDGE) do |tick, level, pin_cb|
29
- edges[edge_index] = tick
30
- edge_index += 1
31
- callback.cancel if edge_index == expected_edges
32
- end
33
-
34
- # If using reset pulse, do it, and the mode switch, while the callback is active.
35
- if reset
36
- set_pin_mode(pin, :output)
37
- Denko::GPIOD.set_value(pin, reset)
38
- sleep(reset_time / 1000000.0)
39
-
40
- # Set pull to opposite direction of reset.
41
- if (reset == low)
42
- set_pin_mode(pin, :input_pullup)
43
- else
44
- set_pin_mode(pin, :input_pulldown)
45
- end
46
- end
47
-
48
- # Wait for pulses or timeout.
49
- begin
50
- Timeout::timeout(timeout / 1000.0) do
51
- loop do
52
- edge_index == expected_edges ? break : sleep(0.001)
53
- end
54
- end
55
- rescue Timeout::Error
56
- # Allow less than pulse_limit to be read.
57
- end
58
- callback.cancel
59
-
60
- # Ignore the first 2 edges (enable pulse) if reset used, 1 edge (starting reference) if not.
61
- pulse_offset = reset ? 2 : 1
62
- pulse_count = edge_index - pulse_offset
63
-
64
- # Handle no pulses read.
65
- if pulse_count < 1
66
- self.update(pin, "")
67
- return nil
17
+ pulses = LGPIO.gpio_read_pulses_us(@gpio_handle, pin, reset_time, reset, pulse_limit, timeout)
18
+ if pulses.class == Array
19
+ self.update(pin, pulses.join(","))
20
+ elsif pulse.class == Integer
21
+ raise "could not read pulses from GPIO #{pin}. LGPIO error: #{pulses}"
68
22
  end
69
-
70
- # Convert from edge times to pulses.
71
- i = 0
72
- pulses = Array.new(pulse_count) {0}
73
- while i < pulse_count
74
- pulses[i] = edges[i+pulse_offset] - edges[i+pulse_offset-1]
75
- i += 1
76
- end
77
-
78
- # Format pulses as if coming from a microcontroller, and update the component.
79
- self.update(pin, pulses.join(","))
80
23
  end
81
24
  end
82
25
  end
@@ -2,17 +2,18 @@ module Denko
2
2
  class PiBoard
3
3
  # CMD = 10
4
4
  def servo_toggle(pin, value=:off, options={})
5
- if value == :off
6
- pwm_clear(pin)
7
- digital_write(pin, 0)
8
- else
9
- @pwms[pin] = :servo
5
+ pwm = hardware_pwm_from_pin(pin)
6
+ if (value == :off)
7
+ pwm.duty_cycle = 0
8
+ pwm.disable
9
+ elsif
10
+ pwm.frequency = 50
10
11
  end
11
12
  end
12
-
13
+
13
14
  # CMD = 11
14
15
  def servo_write(pin, value=0)
15
- Pigpio::IF.set_servo_pulsewidth(pi_handle, pin, value)
16
+ hardware_pwms[pin].duty_us = value
16
17
  end
17
18
  end
18
19
  end