denko-piboard 0.13.2 → 0.15.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 +4 -4
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/README.md +181 -132
- data/Rakefile +0 -5
- data/board_maps/README.md +59 -0
- data/board_maps/le_potato.yml +89 -0
- data/board_maps/orange_pi_zero_2w.yml +85 -0
- data/board_maps/radxa_zero3.yml +88 -0
- data/board_maps/raspberry_pi.yml +95 -0
- data/board_maps/raspberry_pi5.yml +95 -0
- data/denko_piboard.gemspec +6 -7
- data/examples/digital_io/bench_out.rb +22 -0
- data/examples/digital_io/rotary_encoder.rb +31 -0
- data/examples/display/ssd1306.rb +53 -0
- data/examples/i2c/bitbang_aht10.rb +18 -0
- data/examples/i2c/bitbang_search.rb +24 -0
- data/examples/i2c/bitbang_ssd1306_bench.rb +29 -0
- data/examples/i2c/search.rb +24 -0
- data/examples/led/blink.rb +10 -0
- data/examples/led/fade.rb +22 -0
- data/examples/led/ws2812_bounce.rb +36 -0
- data/examples/motor/servo.rb +16 -0
- data/examples/pi_system_monitor.rb +10 -8
- data/examples/pulse_io/buzzer.rb +34 -0
- data/examples/pulse_io/infrared.rb +25 -0
- data/examples/sensor/aht10.rb +17 -0
- data/examples/sensor/dht.rb +24 -0
- data/examples/sensor/ds18b20.rb +59 -0
- data/examples/sensor/hcsr04.rb +16 -0
- data/examples/sensor/neat_tph_readings.rb +32 -0
- data/examples/spi/bb_loopback.rb +31 -0
- data/examples/spi/loopback.rb +37 -0
- data/examples/spi/output_register.rb +38 -0
- data/lib/denko/piboard.rb +10 -2
- data/lib/denko/piboard_base.rb +21 -63
- data/lib/denko/piboard_core.rb +150 -130
- data/lib/denko/piboard_core_optimize_lookup.rb +31 -0
- data/lib/denko/piboard_hardware_pwm.rb +32 -0
- data/lib/denko/piboard_i2c.rb +59 -82
- data/lib/denko/piboard_i2c_bb.rb +48 -0
- data/lib/denko/piboard_infrared.rb +7 -44
- data/lib/denko/piboard_led_array.rb +9 -0
- data/lib/denko/piboard_map.rb +125 -38
- data/lib/denko/piboard_one_wire.rb +42 -0
- data/lib/denko/piboard_pulse.rb +11 -68
- data/lib/denko/piboard_spi.rb +47 -73
- data/lib/denko/piboard_spi_bb.rb +41 -0
- data/lib/denko/piboard_tone.rb +15 -26
- data/lib/denko/piboard_version.rb +1 -1
- data/scripts/99-denko.rules +9 -0
- data/scripts/set_permissions.rb +131 -0
- metadata +48 -21
- data/ext/gpiod/extconf.rb +0 -9
- data/ext/gpiod/gpiod.c +0 -179
- data/lib/denko/piboard_servo.rb +0 -18
- data/lib/gpiod.rb +0 -6
@@ -0,0 +1,25 @@
|
|
1
|
+
#
|
2
|
+
# Send remote control infrared signals on a hardware PWM pin.
|
3
|
+
#
|
4
|
+
require 'denko/piboard'
|
5
|
+
|
6
|
+
# Must be assigned to a hardware PWM channel in your board map.
|
7
|
+
PIN = 226
|
8
|
+
|
9
|
+
board = Denko::PiBoard.new
|
10
|
+
ir = Denko::PulseIO::IRTransmitter.new(board: board, pin: PIN)
|
11
|
+
|
12
|
+
# NEC Raw-Data=0xF708FB04. LSBFIRST, so the binary for each hex digit below is backward.
|
13
|
+
code = [ 9000, 4500, # Start bit
|
14
|
+
560, 560, 560, 560, 560, 1690, 560, 560, # 0010 0x4 command
|
15
|
+
560, 560, 560, 560, 560, 560, 560, 560, # 0000 0x0 command
|
16
|
+
560, 1690, 560, 1690, 560,560, 560, 1690, # 1101 0xB command inverted
|
17
|
+
560, 1690, 560, 1690, 560, 1690, 560, 1690, # 1111 0xF command inverted
|
18
|
+
560, 560, 560, 560, 560, 560, 560, 1690, # 0001 0x8 address
|
19
|
+
560, 560, 560, 560, 560, 560, 560, 560, # 0000 0x0 address
|
20
|
+
560, 1690, 560, 1690, 560, 1690, 560, 560, # 1110 0x7 address inverted
|
21
|
+
560, 1690, 560, 1690, 560, 1690, 560, 1690, # 1111 0xF address inverted
|
22
|
+
560] # Stop bit
|
23
|
+
|
24
|
+
ir.emit(code)
|
25
|
+
board.finish_write
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# AHT10 sensor over I2C, for temperature and humidity.
|
3
|
+
#
|
4
|
+
require 'denko/piboard'
|
5
|
+
require_relative 'neat_tph_readings'
|
6
|
+
|
7
|
+
board = Denko::PiBoard.new
|
8
|
+
# Use the first hardware I2C interface.
|
9
|
+
i2c_index = board.map[:i2cs].keys.first
|
10
|
+
bus = Denko::I2C::Bus.new(board: board, index: i2c_index)
|
11
|
+
|
12
|
+
# Poll it and print readings.
|
13
|
+
sensor.poll(5) do |reading|
|
14
|
+
print_tph_reading(reading)
|
15
|
+
end
|
16
|
+
|
17
|
+
sleep
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# Use a DHT class (DHT-11 / DHT-22) sensor for temperature and humidity.
|
3
|
+
#
|
4
|
+
require 'denko/piboard'
|
5
|
+
require_relative 'neat_tph_readings'
|
6
|
+
|
7
|
+
DHT_PIN = 267
|
8
|
+
|
9
|
+
board = Denko::PiBoard.new
|
10
|
+
sensor = Denko::Sensor::DHT.new(board: board, pin: DHT_PIN)
|
11
|
+
|
12
|
+
sensor.read
|
13
|
+
puts "Temperature unit helpers: #{sensor.temperature} \xC2\xB0C | #{sensor.temperature_f} \xC2\xB0F | #{sensor.temperature_k} K"
|
14
|
+
puts
|
15
|
+
|
16
|
+
# Don't try to read it again too quickly.
|
17
|
+
sleep(1)
|
18
|
+
|
19
|
+
# Poll it and print readings.
|
20
|
+
sensor.poll(5) do |reading|
|
21
|
+
print_tph_reading(reading)
|
22
|
+
end
|
23
|
+
|
24
|
+
sleep
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# Use a Dallas DS18B20 temperature sensor on a 1-Wire bus.
|
3
|
+
#
|
4
|
+
require 'denko/piboard'
|
5
|
+
|
6
|
+
PIN = 256
|
7
|
+
|
8
|
+
board = Denko::PiBoard.new
|
9
|
+
bus = Denko::OneWire::Bus.new(board: board, pin: PIN)
|
10
|
+
|
11
|
+
# The bus detects parasite power automatically when initialized.
|
12
|
+
# It can tell that parasite power is in use, but not by WHICH devices.
|
13
|
+
if bus.parasite_power
|
14
|
+
puts "Parasite power detected..."; puts
|
15
|
+
end
|
16
|
+
|
17
|
+
# Call #device_present to reset the bus and return presence pulse as a boolean.
|
18
|
+
if bus.device_present?
|
19
|
+
puts "Devices present on bus..."; puts
|
20
|
+
else
|
21
|
+
puts "No devices present on bus... Quitting..."
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
# Calling #search finds connected devices and stores them in #found_devices.
|
26
|
+
# Each hash contains a device's ROM address and matching Ruby class if one exists.
|
27
|
+
bus.search
|
28
|
+
count = bus.found_devices.count
|
29
|
+
puts "Found #{count} device#{'s' if count > 1} on the bus:"
|
30
|
+
puts bus.found_devices.inspect; puts
|
31
|
+
|
32
|
+
# We can use the search results to setup instances of the device classes.
|
33
|
+
ds18b20s = []
|
34
|
+
bus.found_devices.each do |d|
|
35
|
+
if d[:class] == Denko::Sensor::DS18B20
|
36
|
+
ds18b20s << d[:class].new(bus: bus, address: d[:address])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Format a reading for printing on a line.
|
41
|
+
def print_reading(reading, sensor)
|
42
|
+
print "#{Time.now.strftime '%Y-%m-%d %H:%M:%S'} - "
|
43
|
+
print "Serial(HEX): #{sensor.serial_number} | Res: #{sensor.resolution} bits | "
|
44
|
+
|
45
|
+
if reading[:crc_error]
|
46
|
+
puts "CRC check failed for this reading!"
|
47
|
+
else
|
48
|
+
fahrenheit = (reading[:temperature] * 1.8 + 32).round(1)
|
49
|
+
puts "#{reading[:temperature]} \xC2\xB0C | #{fahrenheit} \xC2\xB0F"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
ds18b20s.each do |sensor|
|
54
|
+
sensor.poll(5) do |reading|
|
55
|
+
print_reading(reading, sensor)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
sleep
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#
|
2
|
+
# Use an HC-SR04 ultrasonic distance sensor.
|
3
|
+
#
|
4
|
+
require 'denko/piboard'
|
5
|
+
|
6
|
+
ECHO_PIN = 228
|
7
|
+
TRIGGER_PIN = 270
|
8
|
+
|
9
|
+
board = Denko::PiBoard.new
|
10
|
+
hcsr04 = Denko::Sensor::HCSR04.new(board: board, pins: {trigger: TRIGGER_PIN, echo: ECHO_PIN})
|
11
|
+
|
12
|
+
hcsr04.poll(1) do |distance|
|
13
|
+
puts "Distance: #{distance} mm"
|
14
|
+
end
|
15
|
+
|
16
|
+
sleep
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#
|
2
|
+
# This helper method can be used in temp/pressure/humidity sensor examples.
|
3
|
+
# Give a hash with readings as float values and it prints them neatly.
|
4
|
+
#
|
5
|
+
def print_tph_reading(reading)
|
6
|
+
elements = []
|
7
|
+
|
8
|
+
# Temperature
|
9
|
+
if reading[:temperature]
|
10
|
+
formatted_temp = reading[:temperature].to_f.round(2).to_s.ljust(5, '0')
|
11
|
+
elements << "Temperature: #{formatted_temp} \xC2\xB0C"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Pressure
|
15
|
+
if reading[:pressure]
|
16
|
+
formatted_pressure = reading[:pressure].round(2).to_s.ljust(7, '0')
|
17
|
+
elements << "Pressure: #{formatted_pressure} Pa"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Humidity
|
21
|
+
if reading[:humidity]
|
22
|
+
formatted_humidity = reading[:humidity].round(2).to_s.ljust(5, '0')
|
23
|
+
elements << "Humidity: #{formatted_humidity} %"
|
24
|
+
end
|
25
|
+
|
26
|
+
return if elements.empty?
|
27
|
+
|
28
|
+
# Time
|
29
|
+
print "#{Time.now.strftime '%Y-%m-%d %H:%M:%S'} - "
|
30
|
+
|
31
|
+
puts elements.join(" | ")
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'denko'
|
2
|
+
require 'denko/piboard'
|
3
|
+
|
4
|
+
CLOCK_PIN = 256
|
5
|
+
INPUT_PIN = 271
|
6
|
+
OUTPUT_PIN = 258
|
7
|
+
SELECT_PIN = 272
|
8
|
+
|
9
|
+
board = Denko::PiBoard.new
|
10
|
+
bus = Denko::SPI::BitBang.new(board: board, pins: { clock: CLOCK_PIN, input: INPUT_PIN, output: OUTPUT_PIN })
|
11
|
+
|
12
|
+
TEST_DATA = [0, 1, 2, 3, 4, 5, 6, 7]
|
13
|
+
|
14
|
+
# Create a simple test component class.
|
15
|
+
class SPITester
|
16
|
+
include Denko::SPI::Peripheral::SinglePin
|
17
|
+
end
|
18
|
+
spi_tester = SPITester.new(bus: bus, pin: SELECT_PIN)
|
19
|
+
spi_tester.add_callback do |rx_bytes|
|
20
|
+
# If MOSI and MISO are connected this should match TEST_DATA.
|
21
|
+
# If not, should be 8 bytes of 255.
|
22
|
+
puts "RX bytes: #{rx_bytes.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Send the test data.
|
26
|
+
puts "TX bytes: #{TEST_DATA.inspect}"
|
27
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 8)
|
28
|
+
|
29
|
+
# Wait for read callback to run.
|
30
|
+
sleep 1
|
31
|
+
board.finish_write
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'denko'
|
2
|
+
require 'denko/piboard'
|
3
|
+
|
4
|
+
# Use board map from ~/.denko_piboard_map.yml
|
5
|
+
board = Denko::PiBoard.new
|
6
|
+
|
7
|
+
# Use the first hardware SPI interface, and its defined :cs0 pin.
|
8
|
+
spi_index = board.map[:spis].keys.first
|
9
|
+
chip_select = board.map[:spis][spi_index][:cs0]
|
10
|
+
bus = Denko::SPI::Bus.new(board: board, index: spi_index)
|
11
|
+
|
12
|
+
TEST_DATA = [0, 1, 2, 3, 4, 5, 6, 7]
|
13
|
+
|
14
|
+
# Create a simple test component class.
|
15
|
+
class SPITester
|
16
|
+
include Denko::SPI::Peripheral::SinglePin
|
17
|
+
end
|
18
|
+
spi_tester = SPITester.new(bus: bus, pin: chip_select)
|
19
|
+
spi_tester.add_callback do |rx_bytes|
|
20
|
+
# If MOSI and MISO are connected this should match TEST_DATA.
|
21
|
+
# If not, should be 8 bytes of 255.
|
22
|
+
puts "Result : #{rx_bytes.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Send and receive same data.
|
26
|
+
puts "Tx 8 / Rx 8 : #{TEST_DATA.inspect}"
|
27
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 8)
|
28
|
+
|
29
|
+
puts "Tx 8 / Rx 12: #{TEST_DATA.inspect}"
|
30
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 12)
|
31
|
+
|
32
|
+
puts "Tx 8 / Rx 4 : #{TEST_DATA.inspect}"
|
33
|
+
spi_tester.spi_transfer(write: TEST_DATA, read: 4)
|
34
|
+
|
35
|
+
# Wait for read callback to run.
|
36
|
+
sleep 1
|
37
|
+
board.finish_write
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#
|
2
|
+
# Example of LED connected through an output shift register (74HC595).
|
3
|
+
# Can be used over either a bit bang or hardware SPI interface.
|
4
|
+
#
|
5
|
+
require 'denko/piboard'
|
6
|
+
|
7
|
+
LED_PIN = 3 # On the register's parallel outputs
|
8
|
+
|
9
|
+
# Use board map from ~/.denko_piboard_map.yml
|
10
|
+
board = Denko::PiBoard.new
|
11
|
+
|
12
|
+
# Use the first hardware SPI interface, and its defined :cs0 pin.
|
13
|
+
spi_index = board.map[:spis].keys.first
|
14
|
+
chip_select = board.map[:spis][spi_index][:cs0]
|
15
|
+
bus = Denko::SPI::Bus.new(board: board, index: spi_index)
|
16
|
+
|
17
|
+
# OutputRegister needs a bus and pin (chip select).
|
18
|
+
# Other options and their defaults:
|
19
|
+
# bytes: 1 - For daisy-chaining registers
|
20
|
+
# spi_frequency: 1000000 - Only affects hardware SPI interfaces
|
21
|
+
# spi_mode: 0
|
22
|
+
# spi_bit_order: :msbfirst
|
23
|
+
#
|
24
|
+
register = Denko::SPI::OutputRegister.new(bus: bus, pin: chip_select)
|
25
|
+
|
26
|
+
# Turn on the LED by setting the corresponding bit, then writing to the register.
|
27
|
+
register.bit_set(LED_PIN, 1)
|
28
|
+
register.write
|
29
|
+
|
30
|
+
# OutputRegister is a BoardProxy. It has #digital_write, and other methods from Board.
|
31
|
+
register.digital_write(LED_PIN, 0)
|
32
|
+
|
33
|
+
# DigitalOutputs can treat it as a Board.
|
34
|
+
led = Denko::LED.new(board: register, pin: LED_PIN)
|
35
|
+
|
36
|
+
# Blink the LED and sleep the main thread.
|
37
|
+
led.blink 0.5
|
38
|
+
sleep
|
data/lib/denko/piboard.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
|
-
require_relative '../gpiod'
|
2
1
|
require_relative 'piboard_version'
|
3
2
|
require_relative 'piboard_map'
|
4
3
|
require_relative 'piboard_base'
|
4
|
+
|
5
5
|
require_relative 'piboard_core'
|
6
|
+
|
6
7
|
require_relative 'piboard_pulse'
|
7
|
-
require_relative 'piboard_servo'
|
8
8
|
require_relative 'piboard_tone'
|
9
|
+
|
10
|
+
require_relative 'piboard_hardware_pwm'
|
9
11
|
require_relative 'piboard_infrared'
|
12
|
+
|
10
13
|
require_relative 'piboard_i2c'
|
11
14
|
require_relative 'piboard_spi'
|
15
|
+
require_relative 'piboard_led_array'
|
16
|
+
|
17
|
+
require_relative 'piboard_i2c_bb'
|
18
|
+
require_relative 'piboard_spi_bb'
|
19
|
+
require_relative 'piboard_one_wire'
|
data/lib/denko/piboard_base.rb
CHANGED
@@ -1,81 +1,39 @@
|
|
1
1
|
require 'denko'
|
2
|
-
require '
|
2
|
+
require 'lgpio'
|
3
3
|
|
4
4
|
module Denko
|
5
5
|
class PiBoard
|
6
|
-
include
|
7
|
-
|
8
|
-
attr_reader :high, :low, :pwm_high
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
# On 64-bit systems there's a pigpio bug where wavelengths are doubled.
|
12
|
-
@aarch64 = RUBY_PLATFORM.match(/aarch64/)
|
13
|
-
|
14
|
-
# Pin state
|
15
|
-
@pins = []
|
16
|
-
@pwms = []
|
6
|
+
include Behaviors::Subcomponents
|
17
7
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
8
|
+
LOW = 0
|
9
|
+
HIGH = 1
|
10
|
+
PWM_HIGH = 100
|
11
|
+
def low; LOW; end
|
12
|
+
def high; HIGH; end
|
13
|
+
def pwm_high; PWM_HIGH; end
|
14
|
+
def analog_write_resolution; 8; end
|
15
|
+
|
16
|
+
def initialize(map_yaml_file=nil)
|
17
|
+
map_yaml_file ||= Dir.home + "/" + DEFAULT_MAP_FILE
|
18
|
+
unless File.exist?(map_yaml_file)
|
19
|
+
raise StandardError, "board map file not given to PiBoard#new, and does not exist at #{map_yaml_file}"
|
20
|
+
end
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
@high = 1
|
31
|
-
@pwm_high = 255
|
22
|
+
parse_map(map_yaml_file)
|
23
|
+
end
|
32
24
|
|
33
|
-
|
34
|
-
|
35
|
-
exit(-1) if @pi_handle < 0
|
36
|
-
|
37
|
-
# Open the libgpiod interface too.
|
38
|
-
Denko::GPIOD.open_chip
|
25
|
+
def platform
|
26
|
+
:linux
|
39
27
|
end
|
40
28
|
|
41
29
|
def finish_write
|
42
|
-
|
43
|
-
Denko::GPIOD.close_chip
|
30
|
+
gpio_handles.each { |h| LGPIO.chip_close(h) if h }
|
44
31
|
end
|
45
32
|
|
46
|
-
|
47
|
-
# Use standard Subcomponents behavior.
|
48
|
-
#
|
49
|
-
include Behaviors::Subcomponents
|
50
|
-
|
51
|
-
def update(pin, message, time=nil)
|
33
|
+
def update(pin, message)
|
52
34
|
if single_pin_components[pin]
|
53
35
|
single_pin_components[pin].update(message)
|
54
36
|
end
|
55
37
|
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
attr_reader :pi_handle, :wave
|
60
|
-
|
61
|
-
def get_gpio(pin)
|
62
|
-
@pins[pin] ||= Pigpio::UserGPIO.new(@pi_handle, pin)
|
63
|
-
end
|
64
|
-
|
65
|
-
def pwm_clear(pin)
|
66
|
-
@pwms[pin] = nil
|
67
|
-
end
|
68
|
-
|
69
|
-
def new_wave
|
70
|
-
stop_wave
|
71
|
-
@wave = Pigpio::Wave.new(pi_handle)
|
72
|
-
end
|
73
|
-
|
74
|
-
def stop_wave
|
75
|
-
return unless wave
|
76
|
-
wave.tx_stop
|
77
|
-
wave.clear
|
78
|
-
@wave = nil
|
79
|
-
end
|
80
38
|
end
|
81
39
|
end
|