rgpio 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +103 -0
- data/LICENSE +21 -0
- data/PLAN.md +347 -0
- data/README.md +969 -0
- data/examples/adc.rb +60 -0
- data/examples/adc_led.rb +44 -0
- data/examples/button.rb +33 -0
- data/examples/lcd.rb +47 -0
- data/examples/lcd_thermometer.rb +57 -0
- data/examples/led.rb +31 -0
- data/examples/lowlevel/blink.rb +46 -0
- data/examples/lowlevel/button.rb +69 -0
- data/examples/lowlevel/servo.rb +76 -0
- data/examples/motion_sensor.rb +70 -0
- data/examples/motor.rb +38 -0
- data/examples/pwm_info.rb +68 -0
- data/examples/pwm_jitter.rb +139 -0
- data/examples/pwm_led.rb +56 -0
- data/examples/rgb_balance.rb +65 -0
- data/examples/rgb_led.rb +72 -0
- data/examples/servo.rb +70 -0
- data/examples/temperature.rb +53 -0
- data/lib/rgpio/bytes.rb +12 -0
- data/lib/rgpio/chip.rb +271 -0
- data/lib/rgpio/devices/adt7410.rb +116 -0
- data/lib/rgpio/devices/device.rb +41 -0
- data/lib/rgpio/devices/input_device.rb +150 -0
- data/lib/rgpio/devices/mcp3208.rb +104 -0
- data/lib/rgpio/devices/motor.rb +51 -0
- data/lib/rgpio/devices/output_device.rb +66 -0
- data/lib/rgpio/devices/pwm_channel.rb +25 -0
- data/lib/rgpio/devices/pwm_output_device.rb +109 -0
- data/lib/rgpio/devices/rgb_led.rb +175 -0
- data/lib/rgpio/devices/servo.rb +161 -0
- data/lib/rgpio/devices/st7032.rb +234 -0
- data/lib/rgpio/i2c.rb +175 -0
- data/lib/rgpio/line_request.rb +184 -0
- data/lib/rgpio/native.rb +238 -0
- data/lib/rgpio/pwm.rb +321 -0
- data/lib/rgpio/software_pwm.rb +290 -0
- data/lib/rgpio/spi.rb +208 -0
- data/lib/rgpio/version.rb +3 -0
- data/lib/rgpio.rb +99 -0
- metadata +152 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Measure how accurate a PWM waveform really is, using the kernel's own edge
|
|
4
|
+
# timestamps. A diagnostic, not a demo — nothing here is part of the gem's API.
|
|
5
|
+
#
|
|
6
|
+
# Wiring: one jumper between two header pins.
|
|
7
|
+
# GPIO23 (pin 16, output) ---- GPIO24 (pin 18, input)
|
|
8
|
+
#
|
|
9
|
+
# Driving a GPIO output straight into a GPIO input is safe; no resistor needed.
|
|
10
|
+
#
|
|
11
|
+
# Run (Ruby SoftwarePWM, generated in a forked process so the measuring loop
|
|
12
|
+
# does not compete with it for the GVL):
|
|
13
|
+
# ruby examples/pwm_jitter.rb --hz 50 --duty 0.075 --seconds 5
|
|
14
|
+
#
|
|
15
|
+
# Run (measure something else driving the pin — e.g. Python gpiozero, or
|
|
16
|
+
# HardwarePWM wired from GPIO12):
|
|
17
|
+
# ruby examples/pwm_jitter.rb --external --seconds 5
|
|
18
|
+
#
|
|
19
|
+
# What the numbers mean: `pulse` is the high time a servo reads as its position
|
|
20
|
+
# (1 us is about 0.09 degrees on a 180-degree servo), `period` is the frame
|
|
21
|
+
# rate. Spread matters more than the mean — a mean that is 3 us off is a fixed
|
|
22
|
+
# offset you can calibrate out, while a 200 us spread is visible twitching.
|
|
23
|
+
|
|
24
|
+
require_relative "../lib/rgpio"
|
|
25
|
+
|
|
26
|
+
options = {
|
|
27
|
+
out: 23, in: 24, hz: 50.0, duty: 0.075, seconds: 5.0,
|
|
28
|
+
spin: Rgpio::SoftwarePWM::DEFAULT_SPIN_US, external: false,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ARGV.each_with_index do |arg, i|
|
|
32
|
+
case arg
|
|
33
|
+
when "--out" then options[:out] = ARGV[i + 1].to_i
|
|
34
|
+
when "--in" then options[:in] = ARGV[i + 1].to_i
|
|
35
|
+
when "--hz" then options[:hz] = ARGV[i + 1].to_f
|
|
36
|
+
when "--duty" then options[:duty] = ARGV[i + 1].to_f
|
|
37
|
+
when "--seconds" then options[:seconds] = ARGV[i + 1].to_f
|
|
38
|
+
when "--spin" then options[:spin] = ARGV[i + 1].to_i
|
|
39
|
+
when "--external" then options[:external] = true
|
|
40
|
+
when "--help", "-h"
|
|
41
|
+
puts File.read(__FILE__).lines.grep(/^#/).join
|
|
42
|
+
exit 0
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def stats(samples)
|
|
47
|
+
return nil if samples.empty?
|
|
48
|
+
|
|
49
|
+
sorted = samples.sort
|
|
50
|
+
mean = samples.sum / samples.size.to_f
|
|
51
|
+
variance = samples.sum { |v| (v - mean)**2 } / samples.size
|
|
52
|
+
{
|
|
53
|
+
n: samples.size, mean: mean, sd: Math.sqrt(variance),
|
|
54
|
+
min: sorted.first, p50: sorted[sorted.size / 2],
|
|
55
|
+
p99: sorted[(sorted.size * 0.99).floor], max: sorted.last,
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def report(label, target_us, samples)
|
|
60
|
+
s = stats(samples)
|
|
61
|
+
return puts("#{label}: no samples") unless s
|
|
62
|
+
|
|
63
|
+
errors = samples.map { |v| (v - target_us).abs }
|
|
64
|
+
e = stats(errors)
|
|
65
|
+
puts format("%-7s target %8.1f us n=%d", label, target_us, s[:n])
|
|
66
|
+
puts format(" measured mean %8.1f sd %6.1f min %8.1f p50 %8.1f max %8.1f",
|
|
67
|
+
s[:mean], s[:sd], s[:min], s[:p50], s[:max])
|
|
68
|
+
puts format(" |error| mean %8.1f p50 %6.1f p99 %8.1f max %8.1f",
|
|
69
|
+
e[:mean], e[:p50], e[:p99], e[:max])
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Collect edge events for `seconds`, then report pulse widths and periods.
|
|
73
|
+
def measure(gpio, seconds, target_period_us, target_pulse_us)
|
|
74
|
+
events = []
|
|
75
|
+
Rgpio::Chip.open do |chip|
|
|
76
|
+
request = chip.request_lines(offsets: [gpio], direction: :input, edge: :both,
|
|
77
|
+
bias: :disabled, consumer: "pwm_jitter")
|
|
78
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
|
|
79
|
+
while Process.clock_gettime(Process::CLOCK_MONOTONIC) < deadline
|
|
80
|
+
events.concat(request.read_edge_events(timeout: 0.05, capacity: 64))
|
|
81
|
+
end
|
|
82
|
+
request.release
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
pulses = []
|
|
86
|
+
periods = []
|
|
87
|
+
last_rise = nil
|
|
88
|
+
events.each do |event|
|
|
89
|
+
if event[:type] == :rising
|
|
90
|
+
periods << ((event[:timestamp_ns] - last_rise) / 1000.0) if last_rise
|
|
91
|
+
last_rise = event[:timestamp_ns]
|
|
92
|
+
elsif last_rise
|
|
93
|
+
pulses << ((event[:timestamp_ns] - last_rise) / 1000.0)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
puts "edges: #{events.size}"
|
|
98
|
+
report("pulse", target_pulse_us, pulses)
|
|
99
|
+
report("period", target_period_us, periods)
|
|
100
|
+
|
|
101
|
+
dropped = periods.count { |p| p > target_period_us * 1.5 }
|
|
102
|
+
puts format("dropped/late cycles (period > 1.5x target): %d of %d", dropped, periods.size)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
period_us = 1_000_000.0 / options[:hz]
|
|
106
|
+
pulse_us = period_us * options[:duty]
|
|
107
|
+
|
|
108
|
+
puts "libgpiod #{Rgpio.version} | measuring GPIO#{options[:in]}, #{options[:seconds]} s"
|
|
109
|
+
|
|
110
|
+
if options[:external]
|
|
111
|
+
puts "generator: external (not driven by this script)"
|
|
112
|
+
measure(options[:in], options[:seconds], period_us, pulse_us)
|
|
113
|
+
else
|
|
114
|
+
puts format("generator: Rgpio::SoftwarePWM on GPIO%d, %g Hz, duty %.4f, spin_us %d (forked)",
|
|
115
|
+
options[:out], options[:hz], options[:duty], options[:spin])
|
|
116
|
+
ready_r, ready_w = IO.pipe
|
|
117
|
+
|
|
118
|
+
pid = fork do
|
|
119
|
+
ready_r.close
|
|
120
|
+
pwm = Rgpio::SoftwarePWM.new(options[:out], frequency: options[:hz], duty_cycle: options[:duty],
|
|
121
|
+
spin_us: options[:spin], consumer: "pwm_jitter_gen")
|
|
122
|
+
pwm.enable
|
|
123
|
+
ready_w.puts "ready"
|
|
124
|
+
ready_w.close
|
|
125
|
+
sleep options[:seconds] + 1.5
|
|
126
|
+
pwm.close
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
ready_w.close
|
|
130
|
+
ready_r.gets
|
|
131
|
+
ready_r.close
|
|
132
|
+
sleep 0.2 # let the first few cycles settle before sampling
|
|
133
|
+
|
|
134
|
+
begin
|
|
135
|
+
measure(options[:in], options[:seconds], period_us, pulse_us)
|
|
136
|
+
ensure
|
|
137
|
+
Process.waitpid(pid)
|
|
138
|
+
end
|
|
139
|
+
end
|
data/examples/pwm_led.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Fade an LED up and down with PWM, on any GPIO line.
|
|
4
|
+
#
|
|
5
|
+
# Wiring (the same as examples/led.rb):
|
|
6
|
+
# GPIO4 (pin 7) -- 1k resistor -- LED anode (long leg)
|
|
7
|
+
# LED cathode (short leg) -- GND (pin 6)
|
|
8
|
+
#
|
|
9
|
+
# No dtoverlay and no config.txt entry: the brightness comes from
|
|
10
|
+
# Rgpio::SoftwarePWM, which drives any line. On GPIO12/13/18/19 you can pass
|
|
11
|
+
# pwm: :hardware instead for a peripheral-timed waveform.
|
|
12
|
+
#
|
|
13
|
+
# Run:
|
|
14
|
+
# ruby examples/pwm_led.rb
|
|
15
|
+
|
|
16
|
+
require_relative "../lib/rgpio"
|
|
17
|
+
|
|
18
|
+
LED_GPIO = 4
|
|
19
|
+
STEPS = 50
|
|
20
|
+
STEP_DELAY = 0.02
|
|
21
|
+
HOLD = 3
|
|
22
|
+
|
|
23
|
+
# Say what the LED is doing as it happens, so the terminal and the bench agree.
|
|
24
|
+
$stdout.sync = true
|
|
25
|
+
|
|
26
|
+
led = Rgpio::PWMLED.new(LED_GPIO)
|
|
27
|
+
puts "LED on GPIO#{LED_GPIO}, #{led.frequency} Hz. Ctrl-C to stop."
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
puts "1) fading up and down, three times"
|
|
31
|
+
3.times do
|
|
32
|
+
(0..STEPS).each do |i|
|
|
33
|
+
led.value = i / STEPS.to_f
|
|
34
|
+
sleep STEP_DELAY
|
|
35
|
+
end
|
|
36
|
+
(0..STEPS).reverse_each do |i|
|
|
37
|
+
led.value = i / STEPS.to_f
|
|
38
|
+
sleep STEP_DELAY
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
puts "2) holding fixed levels for #{HOLD} s each — one call per level, no loop"
|
|
43
|
+
[0.05, 0.25, 0.5, 1.0, 0.5].each do |level|
|
|
44
|
+
puts format(" value = %.2f (%.0f%% duty)", level, level * 100)
|
|
45
|
+
led.value = level
|
|
46
|
+
sleep HOLD
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
puts "3) off"
|
|
50
|
+
led.off
|
|
51
|
+
sleep 1
|
|
52
|
+
rescue Interrupt
|
|
53
|
+
puts "\nStopped."
|
|
54
|
+
ensure
|
|
55
|
+
led.close
|
|
56
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Find the per-channel balance that makes an RGB LED's white look white.
|
|
4
|
+
#
|
|
5
|
+
# The three dies are not equally bright for equal duty — red drops about 1.9 V
|
|
6
|
+
# against 3.1 V for green and blue, so on a 3.3 V line with equal resistors the
|
|
7
|
+
# channels get very different currents, and the white ends up tinted. This walks
|
|
8
|
+
# through candidate scales with the LED showing white, prints each one, and waits
|
|
9
|
+
# for Enter so you can look before it moves on.
|
|
10
|
+
#
|
|
11
|
+
# Wiring: the same as examples/rgb_led.rb.
|
|
12
|
+
# GPIO17 (pin 11) -- 1k -- red, GPIO27 (pin 13) -- 1k -- green,
|
|
13
|
+
# GPIO22 (pin 15) -- 1k -- blue, common leg -- GND (pin 9)
|
|
14
|
+
#
|
|
15
|
+
# Run:
|
|
16
|
+
# ruby examples/rgb_balance.rb
|
|
17
|
+
#
|
|
18
|
+
# Note the scale that looks neutral and pass it from then on:
|
|
19
|
+
# Rgpio::RGBLED.new(red: 17, green: 27, blue: 22, balance: [0.7, 1.0, 0.6])
|
|
20
|
+
#
|
|
21
|
+
# Only reducing is possible: whichever channel is weakest stays at 1.0 and the
|
|
22
|
+
# others come down to meet it, so a tinted white costs some brightness.
|
|
23
|
+
|
|
24
|
+
require_relative "../lib/rgpio"
|
|
25
|
+
|
|
26
|
+
RED_GPIO = 17
|
|
27
|
+
GREEN_GPIO = 27
|
|
28
|
+
BLUE_GPIO = 22
|
|
29
|
+
|
|
30
|
+
# Each step keeps the weakest-looking channel at full and pulls one or two of the
|
|
31
|
+
# others down. Green is usually the weak one on 3.3 V, which reads as a blue or
|
|
32
|
+
# magenta white; the later rows dim red and blue further.
|
|
33
|
+
CANDIDATES = [
|
|
34
|
+
[1.0, 1.0, 1.0],
|
|
35
|
+
[0.8, 1.0, 0.8],
|
|
36
|
+
[0.7, 1.0, 0.7],
|
|
37
|
+
[0.6, 1.0, 0.6],
|
|
38
|
+
[0.5, 1.0, 0.5],
|
|
39
|
+
[0.7, 1.0, 0.5],
|
|
40
|
+
[0.5, 1.0, 0.7],
|
|
41
|
+
[1.0, 0.8, 0.8],
|
|
42
|
+
].freeze
|
|
43
|
+
|
|
44
|
+
$stdout.sync = true
|
|
45
|
+
|
|
46
|
+
led = Rgpio::RGBLED.new(red: RED_GPIO, green: GREEN_GPIO, blue: BLUE_GPIO)
|
|
47
|
+
led.color = :white
|
|
48
|
+
|
|
49
|
+
puts "The LED is showing white. Press Enter to try the next balance, Ctrl-C to stop."
|
|
50
|
+
puts
|
|
51
|
+
|
|
52
|
+
begin
|
|
53
|
+
CANDIDATES.each_with_index do |balance, i|
|
|
54
|
+
led.balance = balance
|
|
55
|
+
puts format("%d/%d balance = %s", i + 1, CANDIDATES.size, balance.inspect)
|
|
56
|
+
$stdin.gets
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
puts "Through all of them. Re-run to compare the ones you liked."
|
|
60
|
+
rescue Interrupt
|
|
61
|
+
puts "\nStopped."
|
|
62
|
+
ensure
|
|
63
|
+
led.off
|
|
64
|
+
led.close
|
|
65
|
+
end
|
data/examples/rgb_led.rb
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Cycle a full-colour LED through the corners of the colour cube, then mix.
|
|
4
|
+
#
|
|
5
|
+
# Wiring (common cathode: the long leg is the common one and goes to GND):
|
|
6
|
+
# GPIO17 (pin 11) -- 1k resistor -- red leg
|
|
7
|
+
# GPIO27 (pin 13) -- 1k resistor -- green leg
|
|
8
|
+
# GPIO22 (pin 15) -- 1k resistor -- blue leg
|
|
9
|
+
# common leg -- GND (pin 9)
|
|
10
|
+
#
|
|
11
|
+
# For a common-anode LED, tie the common leg to 3.3 V and pass active_low: true.
|
|
12
|
+
#
|
|
13
|
+
# White comes out tinted unless the channels are scaled to match — see BALANCE
|
|
14
|
+
# below and examples/rgb_balance.rb.
|
|
15
|
+
#
|
|
16
|
+
# GPIO2/3/4 work just as well (the pins the book uses) as long as the I2C bus is
|
|
17
|
+
# not enabled, since GPIO2/3 are SDA/SCL. Three software PWM channels need no
|
|
18
|
+
# dtoverlay; the hardware PWM peripheral could not do this at all, because the
|
|
19
|
+
# 40-pin header exposes only two of its channels.
|
|
20
|
+
#
|
|
21
|
+
# Run:
|
|
22
|
+
# ruby examples/rgb_led.rb
|
|
23
|
+
|
|
24
|
+
require_relative "../lib/rgpio"
|
|
25
|
+
|
|
26
|
+
RED_GPIO = 17
|
|
27
|
+
GREEN_GPIO = 27
|
|
28
|
+
BLUE_GPIO = 22
|
|
29
|
+
HOLD = 2
|
|
30
|
+
|
|
31
|
+
# Per-channel scale that makes white look white. This is the value the LED used
|
|
32
|
+
# for verification wanted — red at full, green and blue trimmed 20% — and it is
|
|
33
|
+
# a property of the part, not of the gem: run examples/rgb_balance.rb to find
|
|
34
|
+
# yours. [1.0, 1.0, 1.0] is the gem's default, i.e. no correction.
|
|
35
|
+
BALANCE = [1.0, 0.8, 0.8].freeze
|
|
36
|
+
|
|
37
|
+
# Say what the device is doing as it happens, so the terminal and the bench agree.
|
|
38
|
+
$stdout.sync = true
|
|
39
|
+
|
|
40
|
+
# Walk from one colour to another, a step at a time.
|
|
41
|
+
def fade(led, from, to, steps: 50, delay: 0.02)
|
|
42
|
+
(0..steps).each do |i|
|
|
43
|
+
position = i / steps.to_f
|
|
44
|
+
led.color = from.zip(to).map { |a, b| a + ((b - a) * position) }
|
|
45
|
+
sleep delay
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
led = Rgpio::RGBLED.new(red: RED_GPIO, green: GREEN_GPIO, blue: BLUE_GPIO, balance: BALANCE)
|
|
50
|
+
puts "RGB LED on GPIO#{RED_GPIO} (red) / #{GREEN_GPIO} (green) / #{BLUE_GPIO} (blue). Ctrl-C to stop."
|
|
51
|
+
puts "1) each named colour for #{HOLD} s"
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
Rgpio::RGBLED::COLORS.each_key do |name|
|
|
55
|
+
next if name == :off
|
|
56
|
+
|
|
57
|
+
puts " #{name}"
|
|
58
|
+
led.color = name
|
|
59
|
+
sleep HOLD
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
puts "2) fading red into blue and back, twice"
|
|
63
|
+
2.times do
|
|
64
|
+
fade(led, Rgpio::RGBLED::COLORS[:red], Rgpio::RGBLED::COLORS[:blue])
|
|
65
|
+
fade(led, Rgpio::RGBLED::COLORS[:blue], Rgpio::RGBLED::COLORS[:red])
|
|
66
|
+
end
|
|
67
|
+
rescue Interrupt
|
|
68
|
+
puts "\nStopped."
|
|
69
|
+
ensure
|
|
70
|
+
led.off
|
|
71
|
+
led.close
|
|
72
|
+
end
|
data/examples/servo.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Sweep an RC servo, positioned by value (-1..1) and by angle.
|
|
4
|
+
#
|
|
5
|
+
# Wiring:
|
|
6
|
+
# GPIO4 (pin 7) -- servo signal (usually yellow or orange)
|
|
7
|
+
# 5 V (pin 2 or 4) -- servo power (red)
|
|
8
|
+
# GND (pin 6 or any GND) -- servo ground (brown or black)
|
|
9
|
+
#
|
|
10
|
+
# A servo under load draws more than the Pi's 5 V rail likes to give; if the Pi
|
|
11
|
+
# reboots mid-sweep, power the servo from its own supply with a common ground.
|
|
12
|
+
#
|
|
13
|
+
# No dtoverlay and no config.txt entry: the pulses come from Rgpio::SoftwarePWM,
|
|
14
|
+
# which measured 6 us of spread at this frame rate on an idle Pi 5 — about half a
|
|
15
|
+
# degree. On GPIO12/13/18/19 you can pass pwm: :hardware for a peripheral-timed
|
|
16
|
+
# pulse instead. examples/lowlevel/servo.rb drives the peripheral directly.
|
|
17
|
+
#
|
|
18
|
+
# Pulse widths vary by servo. 1000..2000 us is the range every hobby servo
|
|
19
|
+
# understands; widen it only as far as the datasheet allows, since a servo driven
|
|
20
|
+
# past its travel buzzes and heats up.
|
|
21
|
+
#
|
|
22
|
+
# Run:
|
|
23
|
+
# ruby examples/servo.rb
|
|
24
|
+
|
|
25
|
+
require_relative "../lib/rgpio"
|
|
26
|
+
|
|
27
|
+
SERVO_GPIO = 4
|
|
28
|
+
|
|
29
|
+
# Say what the device is doing as it happens, so the terminal and the bench agree.
|
|
30
|
+
$stdout.sync = true
|
|
31
|
+
|
|
32
|
+
servo = Rgpio::Servo.new(SERVO_GPIO, min_pulse_us: 1000, max_pulse_us: 2000)
|
|
33
|
+
puts "Servo on GPIO#{SERVO_GPIO}, #{servo.pwm.frequency} Hz frames. Ctrl-C to stop."
|
|
34
|
+
|
|
35
|
+
begin
|
|
36
|
+
puts "centre (#{servo.pulse_width_us.round} us)"
|
|
37
|
+
sleep 1
|
|
38
|
+
|
|
39
|
+
2.times do
|
|
40
|
+
puts "one end"
|
|
41
|
+
servo.min
|
|
42
|
+
sleep 1
|
|
43
|
+
puts "the other"
|
|
44
|
+
servo.max
|
|
45
|
+
sleep 1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
servo.mid
|
|
49
|
+
sleep 0.5
|
|
50
|
+
|
|
51
|
+
puts "sweeping by angle"
|
|
52
|
+
2.times do
|
|
53
|
+
(-90..90).step(2) do |degrees|
|
|
54
|
+
servo.angle = degrees
|
|
55
|
+
sleep 0.01
|
|
56
|
+
end
|
|
57
|
+
(-90..90).step(2).reverse_each do |degrees|
|
|
58
|
+
servo.angle = degrees
|
|
59
|
+
sleep 0.01
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
puts "detaching — the horn goes limp and the servo stops drawing current"
|
|
64
|
+
servo.detach
|
|
65
|
+
sleep 1
|
|
66
|
+
rescue Interrupt
|
|
67
|
+
puts "\nStopped."
|
|
68
|
+
ensure
|
|
69
|
+
servo.close
|
|
70
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Read the ambient temperature from an ADT7410 I2C sensor once a second.
|
|
4
|
+
#
|
|
5
|
+
# Wiring (the sensor breakout runs on 3.3 V):
|
|
6
|
+
# 3.3V (pin 1) -- VDD
|
|
7
|
+
# GND (pin 9) -- GND
|
|
8
|
+
# GPIO2 (pin 3, SDA) -- SDA
|
|
9
|
+
# GPIO3 (pin 5, SCL) -- SCL
|
|
10
|
+
#
|
|
11
|
+
# Prerequisite — the header I2C bus must be enabled:
|
|
12
|
+
# sudo raspi-config nonint do_i2c 0 # or: dtparam=i2c_arm=on in config.txt
|
|
13
|
+
# sudo reboot
|
|
14
|
+
#
|
|
15
|
+
# Verify:
|
|
16
|
+
# ls /dev/i2c-1
|
|
17
|
+
# i2cdetect -y 1 # the sensor answers at 0x48 (0x49..0x4b if A0/A1 are high)
|
|
18
|
+
#
|
|
19
|
+
# Run:
|
|
20
|
+
# ruby examples/temperature.rb
|
|
21
|
+
|
|
22
|
+
require_relative "../lib/rgpio"
|
|
23
|
+
|
|
24
|
+
ADDRESS = 0x48
|
|
25
|
+
INTERVAL = 1.0
|
|
26
|
+
|
|
27
|
+
$stdout.sync = true # so the readings still appear when piped to a file
|
|
28
|
+
|
|
29
|
+
puts "I2C buses: #{Rgpio::I2C.buses.inspect}"
|
|
30
|
+
|
|
31
|
+
sensor = Rgpio::ADT7410.new(address: ADDRESS)
|
|
32
|
+
|
|
33
|
+
unless sensor.detected?
|
|
34
|
+
warn format("No ADT7410 at 0x%02x (ID register read back 0x%02x).", ADDRESS, sensor.id)
|
|
35
|
+
warn "Check the wiring and `i2cdetect -y 1`."
|
|
36
|
+
exit 1
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts format("ADT7410 at 0x%02x, ID 0x%02x, %d-bit mode. Ctrl-C to stop.", ADDRESS, sensor.id, sensor.resolution)
|
|
40
|
+
|
|
41
|
+
# The first conversion after power-up is still running for ~240 ms.
|
|
42
|
+
sleep Rgpio::ADT7410::CONVERSION_TIME
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
loop do
|
|
46
|
+
puts format("%.4f degC", sensor.temperature)
|
|
47
|
+
sleep INTERVAL
|
|
48
|
+
end
|
|
49
|
+
rescue Interrupt
|
|
50
|
+
puts "\nStopped."
|
|
51
|
+
ensure
|
|
52
|
+
sensor.close
|
|
53
|
+
end
|
data/lib/rgpio/bytes.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Rgpio
|
|
2
|
+
# Turning argument lists into the bytes that go on a bus. Shared by {I2C} and
|
|
3
|
+
# {SPI} so that `write(0x40, "Hi")` and `write(0x40, 0x48, 0x69)` mean the same
|
|
4
|
+
# thing on both.
|
|
5
|
+
module Bytes
|
|
6
|
+
# @param bytes [Array<Integer, String, Array>]
|
|
7
|
+
# @return [String] binary string
|
|
8
|
+
def self.pack(bytes)
|
|
9
|
+
Array(bytes).flatten.map { |b| b.is_a?(String) ? b.b : [b].pack("C") }.join
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|