denko-piboard 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7bd44c89b18336b4d540b81e6fd7e74cf3dd51a733f148e9acd8278795942249
4
+ data.tar.gz: 02d317de93789676488866e9560ca4d5da91014f47509029991b4be31ab20c9f
5
+ SHA512:
6
+ metadata.gz: c8bb78c2b3e62273b21ec520d35ba54465d3edf8f2e93811f7aaca0aca1e089152a7a0479715754d77301bc8ad938a39086a8bb1cb127566b5ac9c126946956d
7
+ data.tar.gz: d305d28242a97dd14c546516c5ba4b5853e249822981c59ed55e1fb949573524c3744e1a1b2f7889658de8afcedeb469b018acc5dc233fb0e29798a923827e33
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 denko-rb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # denko-piboard 0.13.0
2
+
3
+ ### Raspberry Pi GPIO in Ruby
4
+
5
+ This gem adds support for the Raspberry Pi GPIO interface to the [`denko`](https://github.com/denko-rb/denko) gem. Unlike the main gem, which requires an external microcontroller, this lets you to connect peripherals directly to the Pi.
6
+
7
+ `Denko::PiBoard` is a drop-in replacement for `Denko::Board`, which would represent a connected micrcontroller. Everything maps to the Pi's built-in GPIO pins instead.
8
+
9
+ **Note:** This is not for the Raspberry Pi Pico (W) / RP2040. That microcontroller works with the main gem.
10
+
11
+ ## Example
12
+ Create a script, `led_button.rb`:
13
+
14
+ ```ruby
15
+ require 'denko/piboard'
16
+
17
+ # Board instance for the Pi.
18
+ board = Denko::PiBoard.new
19
+
20
+ # LED connected to GPIO4.
21
+ led = Denko::LED.new(board: board, pin: 4)
22
+
23
+ # Momentary button connected to GPIO17, using internal pullup.
24
+ button = Denko::DigitalIO::Button.new(board: board, pin: 17, pullup: true)
25
+
26
+ # Led on when button is down (0)
27
+ button.down do
28
+ puts "Button down"
29
+ led.on
30
+ end
31
+
32
+ # Led is off when button is up (1)
33
+ button.up do
34
+ puts "Button up"
35
+ led.off
36
+ end
37
+
38
+ # Sleep main thread. Ctrl+C to quit.
39
+ sleep
40
+ ```
41
+
42
+ Run it:
43
+ ```shell
44
+ ruby led_button.rb
45
+ ```
46
+ #### More Examples
47
+ Some Pi-specific code is shown in this gem's [examples](examples) folder, but most examples are in the [main gem](https://github.com/denko-rb/denko/tree/master/examples). They must be modified to work with the Pi's GPIO:
48
+
49
+ 1. Replace setup code:
50
+ ```ruby
51
+ # Replace this:
52
+ require 'bundler/setup'
53
+ require 'denko'
54
+ # With this:
55
+ require 'denko/piboard'
56
+
57
+ # Replace this:
58
+ connection = Denko::Connection::Serial.new()
59
+ board = Denko::Board.new()
60
+ # With this:
61
+ board = Denko::PiBoard.new
62
+ ```
63
+
64
+ 2. Update GPIO/pin numbers as needed. Raspberry Pi pinouts can be found [here](https://pinout.xyz/).
65
+
66
+ **Note:** Not all features from all examples are implemented yet, nor can be implemented. See [Features](#features) below.
67
+
68
+ ## Installation
69
+ This gem depends on the [pigpio library](https://github.com/joan2937/pigpio) and [pigpio gem](https://github.com/nak1114/ruby-extension-pigpio), which provides Ruby bindings, as well as [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git).
70
+
71
+ #### 1. Install pigpio and libgpiod packages
72
+ ```shell
73
+ sudo apt install pigpio libgpiod-dev
74
+ ```
75
+
76
+ #### 2. Install pigpio gem
77
+ The `pigpio` gem has a couple bugs. Until fixes are merged, please install from [this fork](https://github.com/vickash/ruby-extension-pigpio):
78
+ ```shell
79
+ git clone https://github.com/vickash/ruby-extension-pigpio.git
80
+ cd ruby-extension-pigpio
81
+ gem build
82
+ gem install ruby-extension-pigpio-0.1.11.gem
83
+ ```
84
+
85
+ #### 3. Install denko gem
86
+ This gem is very new. It __will not__ work with the version of `denko` (0.11.3) currently available from RubyGems. Install the latest version (future 0.13.0) from the master branch instead:
87
+ ```shell
88
+ git clone https://github.com/denko-rb/denko.git
89
+ cd denko
90
+ git submodule init
91
+ git submodule update
92
+ gem build
93
+ gem install denko-0.13.0.gem
94
+ ```
95
+
96
+ #### 4. Install denko/piboard gem
97
+ Again, since this gem is so new, install from the latest master branch:
98
+ ```shell
99
+ git clone https://github.com/denko-rb/denko-piboard.git
100
+ cd denko-piboard
101
+ gem build
102
+ gem install denko-piboard-0.13.0.gem
103
+ ```
104
+
105
+ **Note:** `sudo` may be needed before `gem install` if using the preinstalled Ruby on a Raspberry Pi.
106
+
107
+ ## Pi Setup
108
+ Depending on your Pi setup, libgpiod may limit GPIO access to the `root` user. If this is the case, Ruby scripts will fail with a `libgpiod` error. To give your user account permission to access GPIO, add it to the `gpio` group.
109
+ ```
110
+ sudo usermod -a -G gpio YOUR_USERNAME
111
+ ```
112
+
113
+ I2C, SPI and the hardware UART are disabled on the Pi by default. Enable them with the built in utility:
114
+ ```shell
115
+ sudo raspi-config
116
+ ```
117
+
118
+ Select "Interfacing Options" from the menu and enable as needed. More info in the [Features](#features) section.
119
+
120
+ #### pigpiod
121
+ The `pigpio` package includes `pigpiod`, which runs in the background as root, providing GPIO access. Ruby scripts won't work if it isn't running. You should only need to start it once per boot. You can automate it, or start manually with:
122
+ ```shell
123
+ sudo pigpiod -s 10
124
+ ```
125
+
126
+ **Note:** `-s 10` sets `pigpiod` to tick every 10 microseconds, lowering CPU use. Valid values are: 1, 2, 4, 5, 8, 10 (5 default).
127
+
128
+ ## Features
129
+
130
+ ### Already Implemented
131
+ - Internal Pull Down/Up
132
+ - Digital Out
133
+ - Digital In
134
+ - PWM Out (use on any pin disables PCM out, cancels Servo on same pin)
135
+ - Servo (use on any pin disables PCM out, cancels PWM Out on same pin)
136
+ - ToneOut (uses waves, one at a time per board, cancels any Infrared Out)
137
+ - Infrared Out (uses waves, one at a time per board, cancels any Tone Out)
138
+ - DHT Class Temperature + Humidity Sensors
139
+ - I2C
140
+ - Must enable with `raspi-config` before use. Instructions [here](https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c).
141
+ - I2C hardware clock cannot be set dynamically, like a microcontroller. Must set in `/boot/config.txt`. Default is 100 kHz. 400 kHz recommended if transferring a lot of data, like with SSD1306 OLED. See [here](https://www.raspberrypi-spy.co.uk/2018/02/change-raspberry-pi-i2c-bus-speed/) for instructions.
142
+
143
+ ### Partially Implemented
144
+ - SPI
145
+ - Must enable with `raspi-config` before use. Insturctions [here](https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-spi).
146
+ - Only Uses SPI1 interface, not SPI0.
147
+ - Does not bind CE pins according to GPIO pinout. Any pin can be used for chip enable.
148
+ - SPI modes 1 and 3 may not work.
149
+ - No listeners yet.
150
+
151
+ ### To Be Implemented
152
+ - OneWire
153
+ - Hardware UART
154
+ - BitBang I2C
155
+ - BitBang SPI
156
+ - BitBang UART
157
+ - WS2812
158
+
159
+ ### Differences
160
+ - Listeners are still polled in a thread, but always at 1ms.
161
+ - pigpio has very fast native input callbacks available, but events are not received in order on a global basis, only per pin. This creates issues where event order between pins is important (like a RotaryEncoder). May expose this functionality for SinglePin components later.
162
+
163
+ ### Incompatible
164
+ - EEPROM (Use the filesystem for persistence instead)
165
+ - Analog IO (No analog pins on Raspberry Pi. Use ADC or DAC over I2C or SPI)
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "rake/extensiontask"
2
+
3
+ Rake::ExtensionTask.new "gpiod" do |ext|
4
+ ext.lib_dir = "lib/gpiod"
5
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'lib/denko/piboard_version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'denko-piboard'
5
+ s.version = Denko::PiBoard::VERSION
6
+ s.licenses = ['MIT']
7
+ s.summary = "Use Raspberry Pi built-in GPIO as a Board class with the denko gem"
8
+ s.description = "Denko::PiBoard is a drop-in replacement for Denko::Board. Use denko features and component classes to be used directly on a Raspberry Pi."
9
+
10
+ s.authors = ["vickash"]
11
+ s.email = 'mail@vickash.com'
12
+ s.files = Dir['**/*'].reject { |f| f.match /.gem\z/}
13
+ s.homepage = 'https://github.com/denko-rb/denko-piboard'
14
+ s.metadata = { "source_code_uri" => "https://github.com/denko-rb/denko-piboard" }
15
+
16
+ # libgpio C extension
17
+ s.extensions = %w[ext/gpiod/extconf.rb]
18
+
19
+ s.add_dependency 'pigpio', '~> 0.1'
20
+ s.add_dependency 'denko', '~> 0.13'
21
+ end
@@ -0,0 +1,58 @@
1
+ #
2
+ # This example uses an SSD1306 OLED display, connected to the Pi's I2C1 interface.
3
+ # I2C1 pins are GPIO2 (SDA) and GPIO3 (SCL).
4
+ #
5
+ # CPU usage is measured using `mpstat`. To install it:
6
+ # sudo apt install sysstat
7
+ #
8
+ # RAM usage is measured using `free`.
9
+ #
10
+ # Each loop, the OLED refreshes, showing date, time, CPU usage and RAM usage.
11
+ #
12
+ require 'denko/piboard'
13
+
14
+ # Special character that fills a 4x6 rectangle.
15
+ # Used to make bar graphs for CPU and RAM usage.
16
+ BAR_ELEMENT = [0x00, 0x7E, 0x7E, 0x7E, 0x00]
17
+
18
+ board = Denko::PiBoard.new
19
+ i2c = Denko::I2C::Bus.new(board: board, pin: :SDA)
20
+
21
+ oled = Denko::Display::SSD1306.new(bus: i2c, rotate: true)
22
+ canvas = oled.canvas
23
+
24
+ # Only do this once since total RAM won't change.
25
+ total_ram = `free -h | awk 'NR==2 {gsub("Mi", "", $2); print $2}'`
26
+ total_ram = total_ram.to_i
27
+ ram_bar_factor = total_ram / 25.0
28
+
29
+ loop do
30
+ # CPU Usage (automatically delays for 1 second)
31
+ mpstat_result = `mpstat -P ALL 1 1 | awk '/^Average:/ && ++count == 2 {print 100 - $12"%"}'`
32
+ cpu_percent = mpstat_result.chop.to_f
33
+
34
+ canvas.clear
35
+ canvas.text_cursor = [0, 16]
36
+ canvas.print "CPU Usage: #{('%.3f' % cpu_percent).rjust(8, ' ')}%"
37
+
38
+ canvas.text_cursor = [0, 24]
39
+ (cpu_percent / 4).ceil.times { canvas.raw_char(BAR_ELEMENT) }
40
+
41
+ # RAM Usage
42
+ ram_usage = `free -h | awk 'NR==2 {gsub("Mi", "", $3); print $3}'`
43
+ ram_usage = ram_usage.to_i
44
+
45
+ canvas.text_cursor = [0, 40]
46
+ ram_string = "#{ram_usage}/#{total_ram}MB"
47
+ canvas.print "RAM Usage:#{ram_string.rjust(11, ' ')}"
48
+
49
+ canvas.text_cursor = [0, 48]
50
+ (ram_usage / ram_bar_factor).ceil.times { canvas.raw_char(BAR_ELEMENT) }
51
+
52
+ # Date and time just before write.
53
+ canvas.text_cursor = [0,0]
54
+ canvas.print(Time.now.strftime('%a %b %d %-l:%M %p'))
55
+
56
+ # Only refresh the area in use.
57
+ oled.draw(0, 127, 0, 56)
58
+ end
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ #
4
+ # Need libgpiod-dev installed.
5
+ # sudo apt install libgpiod-dev
6
+ #
7
+ $libs += " -lgpiod"
8
+
9
+ create_makefile('gpiod/gpiod')
data/ext/gpiod/gpiod.c ADDED
@@ -0,0 +1,179 @@
1
+ #include <ruby.h>
2
+ #include <gpiod.h>
3
+
4
+ #define GPIO_CHIP_NAME "gpiochip0"
5
+
6
+ static struct gpiod_chip *chip;
7
+
8
+ // Save mapping of lowest 32 GPIOs to gpiod_line structs.
9
+ static struct gpiod_line *lines[32] = { NULL };
10
+
11
+ // Input and output values.
12
+ static int gpio_number;
13
+ static int gpio_value;
14
+ static int return_value;
15
+
16
+ static VALUE open_chip(VALUE self) {
17
+ chip = gpiod_chip_open_by_name(GPIO_CHIP_NAME);
18
+ if (!chip) {
19
+ rb_raise(rb_eRuntimeError, "libgpiod error: Could not open GPIO chip");
20
+ return Qnil;
21
+ }
22
+ return Qnil;
23
+ }
24
+
25
+ static VALUE close_chip(VALUE self) {
26
+ gpiod_chip_close(chip);
27
+ return Qnil;
28
+ }
29
+
30
+ static void validate_gpio_number(int gpio_number) {
31
+ if ((gpio_number < 0) || (gpio_number > 31)) {
32
+ VALUE error_message = rb_sprintf("libgpiod error: GPIO line (%d) out of range. Valid range is 0..31", gpio_number);
33
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
34
+ }
35
+ }
36
+
37
+ static void validate_gpio_value(int value) {
38
+ if (!((value == 0) || (value == 1))) {
39
+ VALUE error_message = rb_sprintf("libgpiod error: GPIO value (%d) out of range. Valid values are 0 or 1", value);
40
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
41
+ }
42
+ }
43
+
44
+ static VALUE open_line_output(VALUE self, VALUE gpio) {
45
+ gpio_number = NUM2INT(gpio);
46
+ validate_gpio_number(gpio_number);
47
+
48
+ lines[gpio_number] = gpiod_chip_get_line(chip, gpio_number);
49
+ if (!lines[gpio_number]) {
50
+ gpiod_chip_close(chip);
51
+ VALUE error_message = rb_sprintf("libgpiod error: Could not get GPIO line %d", gpio_number);
52
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
53
+ return Qnil;
54
+ }
55
+
56
+ return_value = gpiod_line_request_output(lines[gpio_number], "GPIOD_RB", 0);
57
+ if (return_value < 0) {
58
+ gpiod_chip_close(chip);
59
+ VALUE error_message = rb_sprintf("libgpiod error: Could not request output for GPIO line %d", gpio_number);
60
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
61
+ return Qnil;
62
+ }
63
+
64
+ return Qnil;
65
+ }
66
+
67
+ static VALUE set_value(VALUE self, VALUE gpio, VALUE value) {
68
+ gpio_number = NUM2INT(gpio);
69
+ validate_gpio_number(gpio_number);
70
+ gpio_value = NUM2INT(value);
71
+ validate_gpio_value(gpio_value);
72
+
73
+ return_value = gpiod_line_set_value(lines[gpio_number], gpio_value);
74
+
75
+ if (return_value < 0) {
76
+ gpiod_chip_close(chip);
77
+ VALUE error_message = rb_sprintf("libgpiod error: Could not set value %d on GPIO line %d", gpio_value, gpio_number);
78
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
79
+ return Qnil;
80
+ }
81
+
82
+ return value;
83
+ }
84
+
85
+ static VALUE open_line_input(VALUE self, VALUE gpio) {
86
+ gpio_number = NUM2INT(gpio);
87
+ validate_gpio_number(gpio_number);
88
+
89
+ lines[gpio_number] = gpiod_chip_get_line(chip, gpio_number);
90
+ if (!lines[gpio_number]) {
91
+ gpiod_chip_close(chip);
92
+ VALUE error_message = rb_sprintf("libgpiod error: Could not get GPIO line %d", gpio_number);
93
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
94
+ return Qnil;
95
+ }
96
+
97
+ return_value = gpiod_line_request_input(lines[gpio_number], "GPIOD_RB");
98
+ if (return_value < 0) {
99
+ gpiod_chip_close(chip);
100
+ VALUE error_message = rb_sprintf("libgpiod error: Could not request input for GPIO line %d", gpio_number);
101
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
102
+ return Qnil;
103
+ }
104
+
105
+ return Qnil;
106
+ }
107
+
108
+ static VALUE get_value(VALUE self, VALUE gpio) {
109
+ gpio_number = NUM2INT(gpio);
110
+ validate_gpio_number(gpio_number);
111
+
112
+ return_value = gpiod_line_get_value(lines[gpio_number]);
113
+
114
+ if (return_value < 0) {
115
+ gpiod_chip_close(chip);
116
+ VALUE error_message = rb_sprintf("libgpiod error: Could not get value from GPIO line %d", gpio_number);
117
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
118
+ return Qnil;
119
+ }
120
+
121
+ return INT2NUM(return_value);
122
+ }
123
+
124
+ static VALUE close_line(VALUE self, VALUE gpio) {
125
+ gpio_number = NUM2INT(gpio);
126
+ validate_gpio_number(gpio_number);
127
+
128
+ // Only try to close the line if it was opened before.
129
+ if (lines[gpio_number] == NULL) return Qnil;
130
+
131
+ gpiod_line_release(lines[gpio_number]);
132
+ lines[gpio_number] = NULL;
133
+ return Qnil;
134
+ }
135
+
136
+ static VALUE set_value_raw(VALUE self, VALUE gpio, VALUE value) {
137
+ gpio_number = NUM2INT(gpio);
138
+ gpio_value = NUM2INT(value);
139
+
140
+ return_value = gpiod_line_set_value(lines[gpio_number], gpio_value);
141
+
142
+ if (return_value < 0) {
143
+ gpiod_chip_close(chip);
144
+ VALUE error_message = rb_sprintf("libgpiod error: Could not set value %d on GPIO line %d", gpio_value, gpio_number);
145
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
146
+ return Qnil;
147
+ }
148
+ return value;
149
+ }
150
+
151
+ static VALUE get_value_raw(VALUE self, VALUE gpio) {
152
+ gpio_number = NUM2INT(gpio);
153
+
154
+ return_value = gpiod_line_get_value(lines[gpio_number]);
155
+
156
+ if (return_value < 0) {
157
+ gpiod_chip_close(chip);
158
+ VALUE error_message = rb_sprintf("libgpiod error: Could not get value from GPIO line %d", gpio_number);
159
+ rb_raise(rb_eRuntimeError, "%s", StringValueCStr(error_message));
160
+ return Qnil;
161
+ }
162
+ return INT2NUM(return_value);
163
+ }
164
+
165
+ void Init_gpiod(void) {
166
+ VALUE mDenko = rb_define_module("Denko");
167
+ VALUE mGPIOD = rb_define_module_under(mDenko, "GPIOD");
168
+ rb_define_singleton_method(mGPIOD, "open_chip", open_chip, 0);
169
+ rb_define_singleton_method(mGPIOD, "close_chip", close_chip, 0);
170
+ rb_define_singleton_method(mGPIOD, "open_line_output", open_line_output, 1);
171
+ rb_define_singleton_method(mGPIOD, "set_value", set_value, 2);
172
+ rb_define_singleton_method(mGPIOD, "open_line_input", open_line_input, 1);
173
+ rb_define_singleton_method(mGPIOD, "get_value", get_value, 1);
174
+ rb_define_singleton_method(mGPIOD, "close_line", close_line, 1);
175
+
176
+ // These do no validation.
177
+ rb_define_singleton_method(mGPIOD, "set_value_raw", set_value_raw, 2);
178
+ rb_define_singleton_method(mGPIOD, "get_value_raw", get_value_raw, 1);
179
+ }
@@ -0,0 +1,11 @@
1
+ require_relative '../gpiod'
2
+ require_relative 'piboard_version'
3
+ require_relative 'piboard_map'
4
+ require_relative 'piboard_base'
5
+ require_relative 'piboard_core'
6
+ require_relative 'piboard_pulse'
7
+ require_relative 'piboard_servo'
8
+ require_relative 'piboard_tone'
9
+ require_relative 'piboard_infrared'
10
+ require_relative 'piboard_i2c'
11
+ require_relative 'piboard_spi'
@@ -0,0 +1,81 @@
1
+ require 'denko'
2
+ require 'pigpio'
3
+
4
+ module Denko
5
+ class PiBoard
6
+ include Pigpio::Constant
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 = []
17
+
18
+ # Listener state
19
+ @pin_listeners = []
20
+ @listen_mutex = Mutex.new
21
+ @listen_states = Array.new(32) { 0 }
22
+ @listen_thread = nil
23
+ @listen_reading = 0
24
+
25
+ # PiGPIO callback state. Unused for now.
26
+ @pin_callbacks = []
27
+
28
+ # Logic levels
29
+ @low = 0
30
+ @high = 1
31
+ @pwm_high = 255
32
+
33
+ # Use the pigpiod interface directly.
34
+ @pi_handle = Pigpio::IF.pigpio_start
35
+ exit(-1) if @pi_handle < 0
36
+
37
+ # Open the libgpiod interface too.
38
+ Denko::GPIOD.open_chip
39
+ end
40
+
41
+ def finish_write
42
+ Pigpio::IF.pigpio_stop(@pi_handle)
43
+ Denko::GPIOD.close_chip
44
+ end
45
+
46
+ #
47
+ # Use standard Subcomponents behavior.
48
+ #
49
+ include Behaviors::Subcomponents
50
+
51
+ def update(pin, message, time=nil)
52
+ if single_pin_components[pin]
53
+ single_pin_components[pin].update(message)
54
+ end
55
+ 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
+ end
81
+ end
@@ -0,0 +1,171 @@
1
+ module Denko
2
+ class PiBoard
3
+ # CMD = 0
4
+ def set_pin_mode(pin, mode=:input, glitch_time=nil)
5
+ # Close the line in libgpiod, if was already open.
6
+ Denko::GPIOD.close_line(pin)
7
+
8
+ pwm_clear(pin)
9
+ gpio = get_gpio(pin)
10
+
11
+ # Output
12
+ if mode.to_s.match /output/
13
+ gpio.mode = PI_OUTPUT
14
+
15
+ # Use pigpiod for setup, but still open line in libgpiod.
16
+ Denko::GPIOD.open_line_output(pin)
17
+
18
+ # Input
19
+ else
20
+ gpio.mode = PI_INPUT
21
+
22
+ # State change valid only if steady for this many microseconds.
23
+ # Only applies to callbacks hooked through pigpiod.
24
+ if glitch_time
25
+ gpio.glitch_filter(glitch_time)
26
+ end
27
+
28
+ # Pull down/up/none
29
+ if mode.to_s.match /pulldown/
30
+ gpio.pud = PI_PUD_DOWN
31
+ elsif mode.to_s.match /pullup/
32
+ gpio.pud = PI_PUD_UP
33
+ else
34
+ gpio.pud = PI_PUD_OFF
35
+ end
36
+
37
+ # Use pigpiod for setup, but still open line in libgpiod.
38
+ Denko::GPIOD.open_line_input(pin)
39
+ end
40
+ end
41
+
42
+ # CMD = 1
43
+ def digital_write(pin, value)
44
+ pwm_clear(pin)
45
+ Denko::GPIOD.set_value(pin, value)
46
+ end
47
+
48
+ # CMD = 2
49
+ def digital_read(pin)
50
+ unless @pwms[pin]
51
+ state = Denko::GPIOD.get_value(pin)
52
+ self.update(pin, state)
53
+ return state
54
+ end
55
+ end
56
+
57
+ # CMD = 3
58
+ def pwm_write(pin, value)
59
+ # Disable servo if necessary.
60
+ pwm_clear(pin) if @pwms[pin] == :servo
61
+
62
+ unless @pwms[pin]
63
+ @pwms[pin] = get_gpio(pin).pwm
64
+ @pwms[pin].frequency = 1000
65
+ end
66
+ @pwms[pin].dutycycle = value
67
+ end
68
+
69
+ # PiGPIO native callbacks. Unused now.
70
+ def set_alert(pin, state=:off, options={})
71
+ # Listener on
72
+ if state == :on && !@pin_callbacks[pin]
73
+ callback = get_gpio(pin).callback(EITHER_EDGE) do |tick, level, pin_cb|
74
+ update(pin_cb, level, tick)
75
+ end
76
+ @pin_callbacks[pin] = callback
77
+
78
+ # Listener off
79
+ else
80
+ @pin_callbacks[pin].cancel if @pin_callbacks[pin]
81
+ @pin_callbacks[pin] = nil
82
+ end
83
+ end
84
+
85
+ # CMD = 6
86
+ def set_listener(pin, state=:off, options={})
87
+ # Listener on
88
+ if state == :on && !@pin_listeners.include?(pin)
89
+ add_listener(pin)
90
+ else
91
+ remove_listener(pin)
92
+ end
93
+ end
94
+
95
+ def digital_listen(pin, divider=4)
96
+ set_listener(pin, :on, {})
97
+ end
98
+
99
+ def stop_listener(pin)
100
+ set_listener(pin, :off)
101
+ end
102
+
103
+ def add_listener(pin)
104
+ @listen_mutex.synchronize do
105
+ @pin_listeners |= [pin]
106
+ @pin_listeners.sort!
107
+ @listen_states[pin] = Denko::GPIOD.get_value(pin)
108
+ end
109
+ start_listen_thread
110
+ end
111
+
112
+ def remove_listener(pin)
113
+ @listen_mutex.synchronize do
114
+ @pin_listeners.delete(pin)
115
+ @listen_states[pin] = nil
116
+ end
117
+ end
118
+
119
+ def start_listen_thread
120
+ return if @listen_thread
121
+
122
+ @listen_thread = Thread.new do
123
+ #
124
+ # @listen_monitor_thread will adjust sleep time dyanmically,
125
+ # targeting even timing of 1 millisecond between loops.
126
+ #
127
+ @listen_count = 0
128
+ @listen_sleep = 0.001
129
+ start_time = Time.now
130
+
131
+ loop do
132
+ @listen_mutex.synchronize do
133
+ @pin_listeners.each do |pin|
134
+ @listen_reading = Denko::GPIOD.get_value(pin)
135
+ self.update(pin, @listen_reading) if (@listen_reading != @listen_states[pin])
136
+ @listen_states[pin] = @listen_reading
137
+ end
138
+ end
139
+ @listen_count += 1
140
+ sleep(@listen_sleep)
141
+ end
142
+ end
143
+
144
+ @listen_monitor_thread = Thread.new do
145
+ loop do
146
+ # Sample the listen rate over 5 seconds.
147
+ time1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
148
+ count1 = @listen_count
149
+ sleep(5)
150
+ time2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
151
+ count2 = @listen_count
152
+
153
+ # Quick maths.
154
+ loops = count2 - count1
155
+ time = time2 - time1
156
+ active_time_per_loop = (time - (loops * @listen_sleep)) / loops
157
+
158
+ # Target 1 millisecond.
159
+ @listen_sleep = 0.001 - active_time_per_loop
160
+ end
161
+ end
162
+ end
163
+
164
+ def stop_listen_thread
165
+ @listen_monitor_thread.kill
166
+ @listen_monitor_thread = nil
167
+ @listen_thread.kill
168
+ @listen_thread = nil
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,119 @@
1
+ module Denko
2
+ class PiBoard
3
+ # Maximum amount of bytes that can be read or written in a single I2C operation.
4
+ def i2c_limit
5
+ 65535
6
+ end
7
+
8
+ def i2c_mutex
9
+ @i2c_mutex ||= Mutex.new
10
+ end
11
+
12
+ # CMD = 33
13
+ def i2c_search
14
+ i2c_mutex.synchronize do
15
+ found_string = ""
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
26
+
27
+ # Remove trailing colon.
28
+ found_string.chop! unless found_string.empty?
29
+
30
+ # Update the bus as if message came from microcontroller.
31
+ self.update(2, found_string)
32
+ end
33
+ end
34
+
35
+ # 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
59
+ end
60
+ end
61
+
62
+ # CMD = 35
63
+ def i2c_read(address, register, read_length, frequency=100000, repeated_start=false)
64
+ i2c_mutex.synchronize do
65
+ 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
+
73
+ # If a start register was given, write it first.
74
+ 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
78
+ end
79
+
80
+ # Enable and (re)disable repeated start as needed.
81
+ buffer = [0x02] + buffer + [0x03] if repeated_start
82
+
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)
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ attr_reader :i2c_handle
108
+
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
112
+ end
113
+
114
+ def i2c_close
115
+ Pigpio::IF.i2c_close(pi_handle, i2c_handle)
116
+ @i2c_handle = nil
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,51 @@
1
+ module Denko
2
+ class PiBoard
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
15
+
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)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,49 @@
1
+ module Denko
2
+ 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
22
+
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"
35
+ end
36
+ end
37
+
38
+ # Handle integers.
39
+ return pin if pin.class == Integer
40
+
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}"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,82 @@
1
+ require 'timeout'
2
+
3
+ module Denko
4
+ class PiBoard
5
+ def pulse_read(pin, reset: false, reset_time: 0, pulse_limit: 100, timeout: 200)
6
+ # Validation
7
+ raise ArgumentError, "error in reset: #{reset}. Should be either #{high} or #{low}" if reset && ![high, low].include?(reset)
8
+ raise ArgumentError, "errror in reset_time: #{reset_time}. Should be 0..65535 microseconds" if (reset_time < 0) || (reset_time > 0xFFFF)
9
+ raise ArgumentError, "errror in pulse_limit: #{pulse_limit}. Should be 0..255 pulses" if (pulse_limit < 0) || (pulse_limit > 0xFF)
10
+ raise ArgumentError, "errror in timeout: #{timeout}. Should be 0..65535 milliseconds" if (timeout < 0) || (timeout > 0xFFFF)
11
+
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
68
+ 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
+ end
81
+ end
82
+ end
@@ -0,0 +1,18 @@
1
+ module Denko
2
+ class PiBoard
3
+ # CMD = 10
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
10
+ end
11
+ end
12
+
13
+ # CMD = 11
14
+ def servo_write(pin, value=0)
15
+ Pigpio::IF.set_servo_pulsewidth(pi_handle, pin, value)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,100 @@
1
+ module Denko
2
+ class PiBoard
3
+
4
+ def spi_config(mode, bit_order)
5
+ # Config is a 32-bit mask, where bits 0 and 1 are a 2-bit number equal to the SPI mode.
6
+ # Default to SPI mode 0 when none given.
7
+ config = mode || 0
8
+ raise ArgumentError, "invalid SPI mode #{config}" unless (0..3).include? config
9
+
10
+ # Default to :msbfirst when bit_order not given.
11
+ bit_order ||= :msbfirst
12
+ unless (bit_order == :msbfirst) || (bit_order == :lsbfirst)
13
+ raise ArgumentError, "invalid bit order #{bitorder}"
14
+ end
15
+
16
+ # Bits 14 and 15 control MSBFIRST (0) or LSBFIRST (1) for MOSI and MISO respectively.
17
+ # Use same order for both directions like Arduino does.
18
+ config |= (0b11 << 14) if bit_order == :lsbfirst
19
+
20
+ # Use SPI1 interface instead of SPI0.
21
+ # Setting bit 8 means we're using SPI1.
22
+ config |= (0b1 << 8)
23
+
24
+ # Bits 5-7 set leaves all CE pins free, and allows any GPIO to be used for chip enable.
25
+ # We toggle separately in #spi_transfer.
26
+ config |= (0b111 << 5)
27
+
28
+ return config
29
+ end
30
+
31
+ # CMD = 26
32
+ def spi_transfer(select_pin, write: [], read: 0, frequency: nil, mode: nil, bit_order: nil)
33
+ # Default to 1MHz SPI frequency.
34
+ frequency ||= 1000000
35
+
36
+ # Make SPI config mask.
37
+ config = spi_config(mode, bit_order)
38
+
39
+ # Open SPI handle.
40
+ spi_open(frequency, config)
41
+
42
+ # Chip enable low. select_pin == 255 means no chip enable (mostly for APA102 LEDs).
43
+ digital_write(select_pin, 0) unless select_pin == 255
44
+
45
+ # Do the SPI transfer.
46
+ write_bytes = write.pack("C*")
47
+ read_bytes = Pigpio::IF.spi_xfer(pi_handle, spi_handle, write_bytes)
48
+
49
+ # Close SPI handle.
50
+ spi_close
51
+
52
+ # Chip enable high. select_pin == 255 means no chip enable (mostly for APA102 LEDs).
53
+ digital_write(select_pin, 1) unless select_pin == 255
54
+
55
+ # Handle spi_xfer errors.
56
+ raise StandardError, "spi_xfer error, code #{read_bytes}" if read_bytes.class == Integer
57
+
58
+ # Handle read bytes.
59
+ if read > 0
60
+ message = ""
61
+
62
+ # Format like a microcontrolelr would. Limit to number of bytes requested.
63
+ i = 0
64
+ while i < read
65
+ message = "#{message},#{read_bytes[i].ord}"
66
+ end
67
+
68
+ # Call update with the message as if coming from select_pin.
69
+ self.update(select_pin, message)
70
+ end
71
+ end
72
+
73
+ # CMD = 27
74
+ def spi_listen
75
+ end
76
+
77
+ # CMD = 28
78
+ def spi_stop
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader :spi_handle
84
+
85
+ def spi_open(frequency, config)
86
+ # Give SPI channel as 0 (SPI CE0), even though we are toggling chip enable separately.
87
+ @spi_handle = Pigpio::IF.spi_open(pi_handle, 0, frequency, config)
88
+ raise StandardError, "SPI error, code #{@spi_handle}" if @spi_handle < 0
89
+ end
90
+
91
+ def spi_close
92
+ Pigpio::IF.spi_close(pi_handle, spi_handle)
93
+ @spi_handle = nil
94
+ end
95
+
96
+ def spi_listeners
97
+ @spi_listeners ||= Array.new
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,39 @@
1
+ module Denko
2
+ class PiBoard
3
+ # CMD = 17
4
+ def tone(pin, frequency, duration=nil)
5
+ # 32-bit mask where only the bit corresponding to the GPIO in use is set.
6
+ pin_mask = 1 << pin
7
+
8
+ if @aarch64
9
+ # pigpio doubles wave times on 64-bit systems for some reason. Halve it to compensate.
10
+ half_wave_time = (250000.0 / frequency).round
11
+ else
12
+ # This is the true halve wave time.
13
+ half_wave_time = (500000.0 / frequency).round
14
+ end
15
+
16
+ # Standard wave setup.
17
+ new_wave
18
+ wave.tx_stop
19
+ wave.clear
20
+ wave.add_new
21
+
22
+ # Build wave with a single cycle that will repeat.
23
+ wave.add_generic [
24
+ wave.pulse(pin_mask, 0x00, half_wave_time),
25
+ wave.pulse(0x00, pin_mask, half_wave_time)
26
+ ]
27
+ wave_id = wave.create
28
+
29
+ # Temporary workaround while Wave#send_repeat gets fixed.
30
+ Pigpio::IF.wave_send_repeat(@pi_handle, wave_id)
31
+ # wave.send_repeat(wave_id)
32
+ end
33
+
34
+ # CMD = 18
35
+ def no_tone(pin)
36
+ stop_wave
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ module Denko
2
+ class PiBoard
3
+ VERSION = '0.13.0'
4
+ end
5
+ end
data/lib/gpiod.rb ADDED
@@ -0,0 +1,6 @@
1
+ require_relative 'gpiod/gpiod'
2
+
3
+ module Denko
4
+ module GPIOD
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: denko-piboard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.13.0
5
+ platform: ruby
6
+ authors:
7
+ - vickash
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-06-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pigpio
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: denko
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.13'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.13'
41
+ description: Denko::PiBoard is a drop-in replacement for Denko::Board. Use denko features
42
+ and component classes to be used directly on a Raspberry Pi.
43
+ email: mail@vickash.com
44
+ executables: []
45
+ extensions:
46
+ - ext/gpiod/extconf.rb
47
+ extra_rdoc_files: []
48
+ files:
49
+ - LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - denko_piboard.gemspec
53
+ - examples/pi_system_monitor.rb
54
+ - ext/gpiod/extconf.rb
55
+ - ext/gpiod/gpiod.c
56
+ - lib/denko/piboard.rb
57
+ - lib/denko/piboard_base.rb
58
+ - lib/denko/piboard_core.rb
59
+ - lib/denko/piboard_i2c.rb
60
+ - lib/denko/piboard_infrared.rb
61
+ - lib/denko/piboard_map.rb
62
+ - lib/denko/piboard_pulse.rb
63
+ - lib/denko/piboard_servo.rb
64
+ - lib/denko/piboard_spi.rb
65
+ - lib/denko/piboard_tone.rb
66
+ - lib/denko/piboard_version.rb
67
+ - lib/gpiod.rb
68
+ homepage: https://github.com/denko-rb/denko-piboard
69
+ licenses:
70
+ - MIT
71
+ metadata:
72
+ source_code_uri: https://github.com/denko-rb/denko-piboard
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubygems_version: 3.4.13
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Use Raspberry Pi built-in GPIO as a Board class with the denko gem
92
+ test_files: []