pigpiod 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7402311d7d55303c25209cbe6dd119955681714fe86e8ee830a0931575ddc79e
4
+ data.tar.gz: 9a1805587185f5724683d4f36801bb534ea8b5cd256e08f288161c6a59dace40
5
+ SHA512:
6
+ metadata.gz: 608f85c221dc559a57c9273febf63c9c583049caa28a7e1fd506c9bb9c48bc0320cff7ce8694e8b58ec8cf8a2f019fc29671b6545cb766f3c446b0faee1b234f
7
+ data.tar.gz: 01e51a27bd50621c79380383735fb5a954379c552d94d1a636242be1830ef16ccc956888ca2ee4e454b3e61e03693a12a6e9785ef93bf2cefff29efbf556d397
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "mqtt-homeassistant"
5
+ require "optparse"
6
+ require "pigpiod"
7
+ require "yaml"
8
+
9
+ config_file = nil
10
+ device_id = "pigpio"
11
+
12
+ options = OptionParser.new do |opts|
13
+ opts.banner = "Usage: pigpio_mqtt_bridge MQTT_URI [options]"
14
+
15
+ opts.on("-c", "--config=YML", "Specify a configuration YAML file to set up the pins") { |v| config_file = v }
16
+ opts.on("-d", "--device-id=ID", "Specify Homie device ID") { |v| device_id = v }
17
+ opts.on("-h", "--help", "Prints this help") do
18
+ puts opts
19
+ exit
20
+ end
21
+ end
22
+
23
+ options.parse!
24
+
25
+ unless ARGV.length == 1
26
+ puts options
27
+ exit 1
28
+ end
29
+
30
+ config = {}
31
+ config = YAML.load_file(config_file) if config_file
32
+
33
+ config.each do |pin, gpio_config|
34
+ pin = pin.to_i
35
+ raise "Unrecognized gpio" unless (gpio = PiGPIO.instance.gpios[pin])
36
+
37
+ gpio.mode = gpio_config[:mode].to_sym if gpio_config[:mode]
38
+ gpio.pull_up_down = gpio_config[:pull_up_down].to_sym if gpio_config[:pull_up_down]
39
+ gpio.invert_logic = gpio_config[:invert_logic] if gpio_config[:invert_logic]
40
+ end
41
+
42
+ homie = MQTT::Homie::Device.new(device_id, "PiGPIO", mqtt: ARGV[0])
43
+ homie.home_assistant_device = {
44
+ manufacturer: "Raspberry Pi Foundation"
45
+ }
46
+
47
+ PiGPIO.instance.gpios.each do |gpio|
48
+ homie.node("gpio#{gpio.g}", "GPIO #{gpio.g}", "GPIO") do |node|
49
+ node.property("mode", "Mode", :enum, gpio.mode, format: PiGPIO::GPIO::MODES)
50
+ node.property("level", "Logic Level", :boolean, gpio.level, hass: :switch) do |value, prop|
51
+ next unless gpio.mode == :output
52
+
53
+ prop.value = gpio.level = value
54
+ end
55
+ node.property("pull-up-down", "Internal Pull Up/Down Resistor", :enum, retained: false,
56
+ format: %w[up down off]) do |value|
57
+ gpio.pull_up_down = value.to_sym
58
+ end
59
+ end
60
+ end
61
+
62
+ homie.publish
63
+ homie.join
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PiGPIO
4
+ class Error < RuntimeError
5
+ attr_reader :code
6
+
7
+ def initialize(code, message)
8
+ @code = code
9
+ super(message)
10
+ end
11
+ end
12
+
13
+ ERRORS = [
14
+ "gpioInitialise failed",
15
+ "GPIO not 0-31",
16
+ "GPIO not 0-53",
17
+ "mode not 0-7",
18
+ "level not 0-1",
19
+ "pud not 0-2",
20
+ "pulsewidth not 0 or 500-2500",
21
+ "dutycycle outside set range",
22
+ "timer not 0-9",
23
+ "ms not 10-60000",
24
+ "timetype not 0-1",
25
+ "seconds < 0",
26
+ "micros not 0-999999",
27
+ "gpioSetTimerFunc failed",
28
+ "timeout not 0-60000",
29
+ "DEPRECATED: no alert func",
30
+ "clock peripheral not 0-1",
31
+ "DEPRECATED: bad clk source",
32
+ "clock micros not 1, 2, 4, 5, 8, or 10",
33
+ "buf millis not 100-10000",
34
+ "dutycycle range not 25-40000",
35
+ "signum not 0-63",
36
+ "can't open pathname",
37
+ "no handle available",
38
+ "unknown handle",
39
+ "ifFlags > 4",
40
+ "DMA channel not 0-15",
41
+ "socket port not 1024-32000",
42
+ "unrecognized fifo command",
43
+ "DMA secondary channel not 0-15",
44
+ "function called before gpioInitialise",
45
+ "function called after gpioInitialise",
46
+ "waveform mode not 0-3",
47
+ "bad parameter in gpioCfgInternals call",
48
+ "baud rate not 50-250K(RX)/50-1M(TX)",
49
+ "waveform has too many pulses",
50
+ "waveform has too many chars",
51
+ "no bit bang serial read on GPIO",
52
+ "bad (null) serial structure parameter",
53
+ "bad (null) serial buf parameter",
54
+ "GPIO operation not permitted",
55
+ "one or more GPIO not permitted",
56
+ "bad WVSC subcommand",
57
+ "bad WVSM subcommand",
58
+ "bad WVSP subcommand",
59
+ "trigger pulse length not 1-100",
60
+ "invalid script",
61
+ "unknown script id",
62
+ "add serial data offset > 30 minutes",
63
+ "GPIO already in use",
64
+ "must read at least a byte at a time",
65
+ "script parameter id not 0-9",
66
+ "script has duplicate tag",
67
+ "script has too many tags",
68
+ "illegal script command",
69
+ "script variable id not 0-149",
70
+ "no more room for scripts",
71
+ "can't allocate temporary memory",
72
+ "socket read failed",
73
+ "socket write failed",
74
+ "too many script parameters (> 10)",
75
+ "script initialising",
76
+ "script has unresolved tag",
77
+ "bad MICS delay (too large)",
78
+ "bad MILS delay (too large)",
79
+ "non existent wave id",
80
+ "No more CBs for waveform",
81
+ "No more OOL for waveform",
82
+ "attempt to create an empty waveform",
83
+ "no more waveforms",
84
+ "can't open I2C device",
85
+ "can't open serial device",
86
+ "can't open SPI device",
87
+ "bad I2C bus",
88
+ "bad I2C address",
89
+ "bad SPI channel",
90
+ "bad i2c/spi/ser open flags",
91
+ "bad SPI speed",
92
+ "bad serial device name",
93
+ "bad serial baud rate",
94
+ "bad i2c/spi/ser parameter",
95
+ "i2c write failed",
96
+ "i2c read failed",
97
+ "bad SPI count",
98
+ "ser write failed",
99
+ "ser read failed",
100
+ "ser read no data available",
101
+ "unknown command",
102
+ "spi xfer/read/write failed",
103
+ "bad (NULL) pointer",
104
+ "no auxiliary SPI on Pi A or B",
105
+ "GPIO is not in use for PWM",
106
+ "GPIO is not in use for servo pulses",
107
+ "GPIO has no hardware clock",
108
+ "GPIO has no hardware PWM",
109
+ "invalid hardware PWM frequency",
110
+ "hardware PWM dutycycle not 0-1M",
111
+ "invalid hardware clock frequency",
112
+ "need password to use hardware clock 1",
113
+ "illegal, PWM in use for main clock",
114
+ "serial data bits not 1-32",
115
+ "serial (half) stop bits not 2-8",
116
+ "socket/pipe message too big",
117
+ "bad memory allocation mode",
118
+ "too many I2C transaction segments",
119
+ "an I2C transaction segment failed",
120
+ "SMBus command not supported by driver",
121
+ "no bit bang I2C in progress on GPIO",
122
+ "bad I2C write length",
123
+ "bad I2C read length",
124
+ "bad I2C command",
125
+ "bad I2C baud rate, not 50-500k",
126
+ "bad chain loop count",
127
+ "empty chain loop",
128
+ "too many chain counters",
129
+ "bad chain command",
130
+ "bad chain delay micros",
131
+ "chain counters nested too deeply",
132
+ "chain is too long",
133
+ "deprecated function removed",
134
+ "bit bang serial invert not 0 or 1",
135
+ "bad ISR edge value, not 0-2",
136
+ "bad ISR initialisation",
137
+ "loop forever must be last command",
138
+ "bad filter parameter",
139
+ "bad pad number",
140
+ "bad pad drive strength",
141
+ "file open failed",
142
+ "bad file mode",
143
+ "bad file flag",
144
+ "bad file read",
145
+ "bad file write",
146
+ "file not open for read",
147
+ "file not open for write",
148
+ "bad file seek",
149
+ "no files match pattern",
150
+ "no permission to access file",
151
+ "file is a directory",
152
+ "bad shell return status",
153
+ "bad script name",
154
+ "bad SPI baud rate, not 50-500k",
155
+ "no bit bang SPI in progress on GPIO",
156
+ "bad event id",
157
+ "Used by Python",
158
+ "not available on BCM2711",
159
+ "only available on BCM2711"
160
+ ].freeze
161
+ private_constant :ERRORS
162
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PiGPIO
4
+ class GPIO
5
+ MODE_VALUES = %i[
6
+ input
7
+ output
8
+ alt5
9
+ alt4
10
+ alt0
11
+ alt1
12
+ alt2
13
+ alt3
14
+ ].freeze
15
+ MODE_CODES = {
16
+ input: "R",
17
+ output: "W",
18
+ alt0: 0,
19
+ alt1: 1,
20
+ alt2: 2,
21
+ alt3: 3,
22
+ alt4: 4,
23
+ alt5: 5
24
+ }.freeze
25
+ MODES = MODE_CODES.keys.freeze
26
+ private_constant :MODE_VALUES, :MODE_CODES
27
+
28
+ attr_accessor :invert_logic
29
+ attr_reader :g
30
+
31
+ def initialize(pigpio, g) # rubocop:disable Naming/MethodParameterName
32
+ @pigpio = pigpio
33
+ @g = g
34
+ @invert_logic = false
35
+ end
36
+
37
+ def inspect
38
+ vals = {
39
+ g: g,
40
+ invert_logic: invert_logic,
41
+ mode: @mode
42
+ }.compact
43
+ "#<PiGPIO::GPIO #{vals.map { |k, v| "#{k}=#{v.inspect}" }.join(" ")}>"
44
+ end
45
+
46
+ def mode
47
+ @mode ||= MODE_VALUES[command("mg #{g}")]
48
+ end
49
+
50
+ def mode=(value)
51
+ raise ArgumentError, "Invalid mode #{value.inspect}" unless MODE_CODES.key?(value)
52
+
53
+ @mode = nil
54
+ command("m #{g} #{MODE_CODES[value]}")
55
+ end
56
+
57
+ def pull_up_down=(value)
58
+ raise ArgumentError, "Invalid setting #{value.inspect}" unless %i[up down off].include?(value)
59
+
60
+ command("pud #{g} #{value.to_s.first}")
61
+ end
62
+
63
+ def level
64
+ result = !command("r #{g}").zero?
65
+ result = !result if invert_logic
66
+ result
67
+ end
68
+
69
+ def level=(value)
70
+ raise ArgumentError, "Invalid level #{value.inspect}" unless [true, false].include?(value)
71
+
72
+ value = !value if invert_logic
73
+ command("w #{g} #{value ? 1 : 0}")
74
+ end
75
+
76
+ private
77
+
78
+ def command(command)
79
+ @pigpio.send(:command, command)
80
+ end
81
+ end
82
+ end
data/lib/pigpiod.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "io/wait"
4
+ require "singleton"
5
+
6
+ require "pi_gpio/errors"
7
+ require "pi_gpio/gpio"
8
+
9
+ class PiGPIO
10
+ include Singleton
11
+
12
+ attr_reader :gpios
13
+
14
+ def initialize
15
+ @pigpio = File.open("/dev/pigpio", "wb")
16
+ @pigout = File.open("/dev/pigout", "rb")
17
+
18
+ @gpios = Array.new(54) { |g| GPIO.new(self, g) }
19
+ end
20
+
21
+ def inspect
22
+ "#<PiGPIO>"
23
+ end
24
+
25
+ private
26
+
27
+ def command(command)
28
+ @pigpio.puts(command)
29
+ @pigpio.flush
30
+ @pigout.wait_readable
31
+ result = @pigout.read_nonblock(256).strip.to_i
32
+ if result.negative?
33
+ error_index = -result - 1
34
+ raise Error.new(result, ERRORS[error_index])
35
+ end
36
+ result
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pigpiod
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Cody Cutrer
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mqtt-homeassistant
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: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '9.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '9.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.23'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.23'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-performance
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.12'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.12'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.6'
97
+ description:
98
+ email: cody@cutrer.com'
99
+ executables:
100
+ - pigpio_mqtt_bridge
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - exe/pigpio_mqtt_bridge
105
+ - lib/pi_gpio/errors.rb
106
+ - lib/pi_gpio/gpio.rb
107
+ - lib/pigpiod.rb
108
+ homepage: https://github.com/ccutrer/ruby-pigpiod
109
+ licenses:
110
+ - MIT
111
+ metadata:
112
+ rubygems_mfa_required: 'true'
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '2.5'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.2.5
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Publish pigpiod access to MQTT
132
+ test_files: []