neotrellis 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/.gitignore +16 -0
- data/.rspec +3 -0
- data/Gemfile +2 -0
- data/LICENSE +674 -0
- data/README.md +113 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/datasheets/adafruit-neotrellis.pdf +0 -0
- data/datasheets/neotrellis.jpg +0 -0
- data/datasheets/seesaw-atsamd09.pdf +0 -0
- data/lib/neotrellis.rb +3 -0
- data/lib/neotrellis/keypad.rb +230 -0
- data/lib/neotrellis/neopixel.rb +198 -0
- data/lib/neotrellis/seesaw.rb +139 -0
- data/lib/neotrellis/version.rb +6 -0
- data/neotrellis.gemspec +30 -0
- metadata +146 -0
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# Neotrellis
|
2
|
+
|
3
|
+
Neotrellis is a ruby driver for Adafruit's NeoTrellis keypad. This device uses an I2C bus to control both the keypad and the RGB led array.
|
4
|
+
|
5
|
+
[Neotrelliis](datasheets/neotrellis.jpg)
|
6
|
+
|
7
|
+
See https://www.adafruit.com/product/3954 for more details on this device.
|
8
|
+
|
9
|
+
This ruby gem is mainly compose of two classes :
|
10
|
+
- Neotrellis::Neopixels to control the leds behind each keys.
|
11
|
+
- Neotrellis::Keypad to execute code when a key is pressed or released on the keypad
|
12
|
+
|
13
|
+
The Keypad class provide two modes to react to key events: using pooling with a loop or using interruption handler. The pooling mode require only the I2C communication to be set up. For the interruption mode to work, the pin INT on the keypad need to be connected to a GPIO input supporting hardware interruption on your computer. A common use case are the pins 15 or 16 of the Raspberry Pi GPIO header. See Raspberry Pi documentation for more details.
|
14
|
+
|
15
|
+
The support of hardware interruption is done by the [YaGPIO](https://github.com/nagius/ya_gpio) gem based on Sysfs interface. It has been designed for Raspberry Pi but should work with any GPIO systems supported by the Sysfs kernel driver.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'neotrellis'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install neotrellis
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
We will suppose here that you are using a Rasberry Pi B+ to control the Neotrellis device. Adapt the I2C device name and pin numbers for other boards.
|
36
|
+
|
37
|
+
First, connect the pins SDA and SLC of the keypad to GPIO pins 3 and 5. The device will appear on the second I2C bus of the Raspberry Pi `/dev/i2c-1`. Te I2C port need to be enabled in the linux kenel to operate. Refer to Raspberry Pi documentation to do so.
|
38
|
+
|
39
|
+
If you want to use the interruption mode also connect the INT pin to the GPIO pin 15 (GPIO22).
|
40
|
+
|
41
|
+
### Example 1: Print a message when key #3 is pressed using polling
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
45
|
+
keypad = Neotrellis::Keypad.new(seesaw)
|
46
|
+
keypad.set_event(2, event: Neotrellis::Keypad::KEY_PRESSED) { |event|
|
47
|
+
puts "Key #{event.key} pressed"
|
48
|
+
}
|
49
|
+
loop do
|
50
|
+
sleep(1)
|
51
|
+
puts "Processing pending events"
|
52
|
+
keypad.sync
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### Example 2: Print a message when key #3 is released using interruption
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
60
|
+
keypad = Neotrellis::Keypad.new(seesaw)
|
61
|
+
keypad.set_event(2, event: Neotrellis::Keypad::KEY_RELEASED) { |event|
|
62
|
+
puts "Key #{event.key}"
|
63
|
+
puts event.edge == Neotrellis::Keypad::KEY_PRESSED ? "pressed" : "released"
|
64
|
+
}
|
65
|
+
keypad.enable_interrupt(22)
|
66
|
+
keypad.wait_for_event
|
67
|
+
```
|
68
|
+
|
69
|
+
### Example 3: Turn on led #2 in red for 2 seconds
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
73
|
+
pixels = Neotrellis::Neopixel.new(seesaw)
|
74
|
+
pixels.set(1, Neotrellis::Neopixel::RED)
|
75
|
+
sleep 2
|
76
|
+
pixels.set(1, Neotrellis::Neopixel::OFF)
|
77
|
+
```
|
78
|
+
|
79
|
+
### Example 4: Turn on the pressed key with a random color
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
83
|
+
keypad = Neotrellis::Keypad.new(seesaw, interrupt_pin: 22)
|
84
|
+
pixels = Neotrellis::Neopixel.new(seesaw)
|
85
|
+
|
86
|
+
Neotrellis::Neopixel::DEFAULT_PIXEL_NUMBER.times do |key|
|
87
|
+
keypad.set_event(key) do |event|
|
88
|
+
if event.edge == Neotrellis::Keypad::KEY_PRESSED
|
89
|
+
pixels.set(event.key, Neotrellis::Neopixel::Color.new(rand(255), rand(255), rand(255)))
|
90
|
+
else
|
91
|
+
pixels.set(event.key, Neotrellis::Neopixel::OFF)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
keypad.wait_for_event
|
96
|
+
```
|
97
|
+
|
98
|
+
### API documentation
|
99
|
+
|
100
|
+
The API documentation is available in the Yard format. To generate it under the `doc` directory, run :
|
101
|
+
|
102
|
+
```
|
103
|
+
bundle exec rake yard
|
104
|
+
```
|
105
|
+
|
106
|
+
## Contributing
|
107
|
+
|
108
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nagius/neotrellis.
|
109
|
+
|
110
|
+
## License
|
111
|
+
|
112
|
+
Copyleft 2019 - Nicolas AGIUS - GNU GPLv3
|
113
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "neotrellis"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
Binary file
|
Binary file
|
Binary file
|
data/lib/neotrellis.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
# Neotrellis - Driver for Adafruit's NeoTrellis keypad
|
2
|
+
# Copyleft 2019 - Nicolas AGIUS <nicolas.agius@lps-it.fr>
|
3
|
+
|
4
|
+
###########################################################################
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
###########################################################################
|
20
|
+
|
21
|
+
require 'ya_gpio'
|
22
|
+
|
23
|
+
module Neotrellis
|
24
|
+
# Driver for the Neotrellis 4x4 keypad.
|
25
|
+
#
|
26
|
+
# @example Print a message when key #3 is pressed
|
27
|
+
# seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
28
|
+
# keypad = Neotrellis::Keypad.new(seesaw)
|
29
|
+
# keypad.set_event(2, event: Neotrellis::Keypad::KEY_PRESSED) { |event|
|
30
|
+
# puts "Key #{event.key} pressed"
|
31
|
+
# }
|
32
|
+
# loop do
|
33
|
+
# sleep(1)
|
34
|
+
# puts "Processing pending events"
|
35
|
+
# keypad.sync
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @example Print a message when key #3 is released using interruption on GPIO pin 22
|
39
|
+
# seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
40
|
+
# keypad = Neotrellis::Keypad.new(seesaw)
|
41
|
+
# keypad.set_event(2, event: Neotrellis::Keypad::KEY_RELEASED) { |event|
|
42
|
+
# puts "Key #{event.key}"
|
43
|
+
# puts event.edge == Neotrellis::Keypad::KEY_PRESSED ? "pressed" : "released"
|
44
|
+
# }
|
45
|
+
# keypad.enable_interrupt(22)
|
46
|
+
# keypad.wait_for_event
|
47
|
+
#
|
48
|
+
# @example Stop waiting for events when key #4 is pressed
|
49
|
+
# seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
50
|
+
# keypad = Neotrellis::Keypad.new(seesaw, interrupt_pin: 22)
|
51
|
+
# keypad.set_event(3, event: Neotrellis::Keypad::KEY_PRESSED) {
|
52
|
+
# keypad.resume
|
53
|
+
# }
|
54
|
+
# keypad.wait_for_event
|
55
|
+
# puts "loop ended"
|
56
|
+
class Keypad
|
57
|
+
|
58
|
+
private
|
59
|
+
# Internal SeeSaw registers
|
60
|
+
KEYPAD_BASE = 0x10
|
61
|
+
|
62
|
+
KEYPAD_STATUS = 0x00
|
63
|
+
KEYPAD_EVENT = 0x01
|
64
|
+
KEYPAD_INTENSET = 0x02
|
65
|
+
KEYPAD_INTENCLR = 0x03
|
66
|
+
KEYPAD_COUNT = 0x04
|
67
|
+
KEYPAD_FIFO = 0x10
|
68
|
+
|
69
|
+
public
|
70
|
+
|
71
|
+
KEY_HIGH = 0 # Key is pressed
|
72
|
+
KEY_LOW = 1 # Key is released
|
73
|
+
KEY_RELEASED = 2 # Key is falling edge
|
74
|
+
KEY_PRESSED = 3 # Key is rising edge
|
75
|
+
KEY_BOTH = 4 # Key is rising edge or falling edge
|
76
|
+
|
77
|
+
# Initialize the keypad driven by a Seesaw chip.
|
78
|
+
#
|
79
|
+
# @param seesaw [Neotrellis::SeeSaw] Seesaw driver
|
80
|
+
# @param interrupt_pin [Integer] GPIO pin used by the interruption handler. If false, the interruption mode will be disabled.
|
81
|
+
# @param debug [Boolean] Enable debug ouput on stdout
|
82
|
+
def initialize(seesaw, interrupt_pin: false, debug: false)
|
83
|
+
@seesaw = seesaw
|
84
|
+
@debug = debug
|
85
|
+
@callbacks = {}
|
86
|
+
|
87
|
+
enable_interrupt(interrupt_pin) if interrupt_pin
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get the number of events (key pressed or released) waiting to be processed in the Seesaw buffer.
|
91
|
+
#
|
92
|
+
# @return [Integer] Number of events
|
93
|
+
def count_events
|
94
|
+
@seesaw.read_byte(KEYPAD_BASE, KEYPAD_COUNT)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Register a callback to execute when a key's event is processed.
|
98
|
+
#
|
99
|
+
# @param key [Integer] ID of the key. Must be between 0 and 15 (for the 4x4 keypad)
|
100
|
+
# @param event [Neotrellis::Keypad::KEY_PRESSED|Neotrellis::Keypad::KEY_RELEASED|Neotrellis::Keypad::KEY_BOTH] Type of event to react to.
|
101
|
+
# @param enabled [Boolean] If false, the callback will be disabled.
|
102
|
+
# @param block [Block] Code to execute when the event is trigerred. A Neotrellis::Keypad::KeyEvent will be passed as argument to the block.
|
103
|
+
def set_event(key, event: KEY_BOTH, enabled: true, &block)
|
104
|
+
if event == KEY_BOTH
|
105
|
+
set_event(key, event: KEY_PRESSED, enabled: enabled, &block)
|
106
|
+
set_event(key, event: KEY_RELEASED, enabled: enabled, &block)
|
107
|
+
else
|
108
|
+
raise "event must be one of KEY_PRESSED, KEY_RELEASED" unless [KEY_PRESSED, KEY_RELEASED].include? event
|
109
|
+
raise "enabled must be a boolean" unless [true, false].include? enabled
|
110
|
+
|
111
|
+
# Convert data to SeeSaw's binary registers
|
112
|
+
key_b = (key/4)*8 + (key%4)
|
113
|
+
edge_b = (1 << (event+1)) | ( enabled ? 1 : 0 )
|
114
|
+
|
115
|
+
@seesaw.write(KEYPAD_BASE, KEYPAD_EVENT, key_b, edge_b)
|
116
|
+
@callbacks[KeyEvent.new(key, event)] = block
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Trigger the callback for each event waiting to be processed.
|
121
|
+
# This method will be automatically called when the interruption mode is enabled.
|
122
|
+
def sync
|
123
|
+
count = count_events()
|
124
|
+
if count >0
|
125
|
+
read_events(count).each do |event|
|
126
|
+
trigger_event(event)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Enable the interruption mode.
|
132
|
+
# In this mode, the `sync()` method will be automatically called when an interruption is triggered by the Seesaw device.
|
133
|
+
# The INT ligne of the keypad need to be connected to this GPIO pin.
|
134
|
+
#
|
135
|
+
# @param pin [Integer] GPIO pin to configure for interruption on. Pin number is in the BCM numbering, as reported by Sysfs.
|
136
|
+
def enable_interrupt(pin)
|
137
|
+
raise "pin must be an integer" unless pin.is_a? Integer
|
138
|
+
|
139
|
+
@interrupt_enabled=true
|
140
|
+
@seesaw.write(KEYPAD_BASE, KEYPAD_INTENSET, 0x01)
|
141
|
+
|
142
|
+
@gpio = YaGPIO.new(pin, YaGPIO::INPUT)
|
143
|
+
@gpio.set_interrupt(YaGPIO::EDGE_FALLING) do
|
144
|
+
puts "DEBUG Interrupt received." if @debug
|
145
|
+
sync
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Tell if the interruption mode is enabled.
|
150
|
+
#
|
151
|
+
# @return [Boolean] True if interruption mode is enabled.
|
152
|
+
def interrupt?
|
153
|
+
@interrupt_enabled
|
154
|
+
end
|
155
|
+
|
156
|
+
# Disable interruption mode and release the GPIO pin.
|
157
|
+
def disable_interrupt
|
158
|
+
@interrupt_enabled=false
|
159
|
+
@seesaw.write(KEYPAD_BASE, KEYPAD_INTENCLR, 0x01)
|
160
|
+
@gpio.close
|
161
|
+
end
|
162
|
+
|
163
|
+
# Wait for an interruption and process all pending event.
|
164
|
+
# The interruption mode must be enabled for this method to work.
|
165
|
+
# This is a blocking method. Use `resume()` inside a callback to stop waiting.
|
166
|
+
def wait_for_event
|
167
|
+
raise "Interrupt is not enabled. Setup enable_interrupt() first" unless interrupt?
|
168
|
+
YaGPIO::wait([@gpio])
|
169
|
+
end
|
170
|
+
|
171
|
+
# Stop waiting for an event in the interruption mode.
|
172
|
+
# This need to be run from a callback triggered by `wait_for_event()`.
|
173
|
+
def resume
|
174
|
+
YaGPIO::resume
|
175
|
+
end
|
176
|
+
|
177
|
+
# Represent an event attached to a key
|
178
|
+
class KeyEvent
|
179
|
+
attr_reader :key # Key ID
|
180
|
+
attr_reader :edge # Event type
|
181
|
+
|
182
|
+
# Create a new event.
|
183
|
+
#
|
184
|
+
# @param key [Integer] Key ID related to the event
|
185
|
+
# @param edge [Neotrellis::Keypad::KEY_PRESSED|Neotrellis::Keypad::KEY_RELEASED] Type of event
|
186
|
+
def initialize(key, edge)
|
187
|
+
@key = key
|
188
|
+
@edge = edge
|
189
|
+
end
|
190
|
+
|
191
|
+
def to_s
|
192
|
+
"Event-#{key}-#{edge}"
|
193
|
+
end
|
194
|
+
|
195
|
+
def ==(other)
|
196
|
+
@key == other.key && @edge == other.edge
|
197
|
+
end
|
198
|
+
|
199
|
+
alias eql? ==
|
200
|
+
|
201
|
+
def hash
|
202
|
+
@key.hash ^ @edge.hash # XOR
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
def read_events(count)
|
209
|
+
@seesaw.read_bytes(count, KEYPAD_BASE, KEYPAD_FIFO).map do |raw|
|
210
|
+
# Convert raw event into key number
|
211
|
+
val = (raw >> 2) & 0x3F
|
212
|
+
key = (val/8)*4 + (val%8)
|
213
|
+
edge = raw & 0x3
|
214
|
+
|
215
|
+
KeyEvent.new(key, edge)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def trigger_event(event)
|
220
|
+
callback = @callbacks[event]
|
221
|
+
if callback.nil?
|
222
|
+
puts "WARNING: No callback defined for #{event}"
|
223
|
+
else
|
224
|
+
callback.call(event)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# vim: ts=4:sw=4:ai
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# Neotrellis - Driver for Adafruit's NeoTrellis keypad
|
2
|
+
# Copyleft 2019 - Nicolas AGIUS <nicolas.agius@lps-it.fr>
|
3
|
+
|
4
|
+
###########################################################################
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
###########################################################################
|
20
|
+
|
21
|
+
|
22
|
+
# Neotrellis is a ruby driver for Adafruit's NeoTrellis keypad
|
23
|
+
module Neotrellis
|
24
|
+
|
25
|
+
# Neopixel is the driver of the RGB led array on the Neotrellis device.
|
26
|
+
#
|
27
|
+
# @example Turn on led #2 in red for 2 seconds with debug output
|
28
|
+
# seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E, debug: true)
|
29
|
+
# pixels = Neotrellis::Neopixel.new(seesaw)
|
30
|
+
# pixels.set(1, Neotrellis::Neopixel::RED)
|
31
|
+
# sleep 2
|
32
|
+
# pixels.set(1, Neotrellis::Neopixel::OFF)
|
33
|
+
#
|
34
|
+
# @example Turn on all leds with color #b5f115 for 2 seconds
|
35
|
+
# seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
36
|
+
# pixels = Neotrellis::Neopixel.new(seesaw)
|
37
|
+
# pixels.fill(Neotrellis::Neopixel::Color.new(181, 241, 21))
|
38
|
+
# sleep 2
|
39
|
+
# pixels.off
|
40
|
+
#
|
41
|
+
# @example Display white columns
|
42
|
+
# seesaw = Neotrellis::Seesaw.new(device: "/dev/i2c-1", addr: 0x2E)
|
43
|
+
# pixels = Neotrellis::Neopixel.new(seesaw, autoshow: false)
|
44
|
+
# Neotrellis::Neopixel::DEFAULT_PIXEL_NUMBER.times { |i|
|
45
|
+
# pixels.set(i, Neotrellis::Neopixel::WHITE) unless i%2 == 0
|
46
|
+
# }
|
47
|
+
# pixels.show
|
48
|
+
class Neopixel
|
49
|
+
attr_reader :brightness # Get the brightness of the leds.
|
50
|
+
attr_accessor :autoshow # Enable autoshow feature. Automatically call `show()` after each update.
|
51
|
+
|
52
|
+
DEFAULT_PIXEL_NUMBER = 16 # Default number of leds on the neotrellis 4x4 keypad
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
NEOPIXEL_BASE = 0x0E
|
57
|
+
|
58
|
+
NEOPIXEL_STATUS = 0x00
|
59
|
+
NEOPIXEL_PIN = 0x01
|
60
|
+
NEOPIXEL_SPEED = 0x02
|
61
|
+
NEOPIXEL_BUF_LENGTH = 0x03
|
62
|
+
NEOPIXEL_BUF = 0x04
|
63
|
+
NEOPIXEL_SHOW = 0x05
|
64
|
+
|
65
|
+
public
|
66
|
+
|
67
|
+
# Initialize a neopixel array driven by a seesaw chip.
|
68
|
+
#
|
69
|
+
# @param seesaw [Neotrellis::SeeSaw] Seesaw driver
|
70
|
+
# @param size [Integer] Number of leds on the array
|
71
|
+
# @param autoshow [Boolean] Automatically call `show()` after each update
|
72
|
+
# @param brightness [Float] Brightness of the leds. Must be between 0.0 and 1.0
|
73
|
+
def initialize(seesaw, size: DEFAULT_PIXEL_NUMBER, autoshow: true, brightness: 1.0)
|
74
|
+
@seesaw = seesaw
|
75
|
+
@pin = 3 # NeoPixel bus is on SeeSaw's pin 3
|
76
|
+
@n = size # Number of NeoPixels on the bus
|
77
|
+
@bpp = 3 # 3 bytes per pixel
|
78
|
+
@autoshow = autoshow # Automaticaly display data in buffer
|
79
|
+
@brightness = [[brightness, 0.0].max, 1.0].min
|
80
|
+
|
81
|
+
# Size of RGB buffer, 2 bytes for Unsigned Int Big Endian
|
82
|
+
buf_length = [@n*@bpp].pack('S>').unpack('C*')
|
83
|
+
|
84
|
+
@seesaw.write(NEOPIXEL_BASE, NEOPIXEL_PIN, @pin)
|
85
|
+
@seesaw.write(NEOPIXEL_BASE, NEOPIXEL_BUF_LENGTH, *buf_length)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Set the brightness of the leds
|
89
|
+
#
|
90
|
+
# @param brightness [Float] Brightness of the leds. Must be between 0.0 and 1.0
|
91
|
+
#
|
92
|
+
# @return [Float] New brightness value
|
93
|
+
def brightness=(brightness)
|
94
|
+
@brightness = [[brightness, 0.0].max, 1.0].min
|
95
|
+
end
|
96
|
+
|
97
|
+
# Set the color of one pixel.
|
98
|
+
# If `autoshow` is false nothing will be displayed until you call the `show()` method.
|
99
|
+
#
|
100
|
+
# @param pixel [Integer] ID of the pixel in the array. Must be between 0 and `size`-1
|
101
|
+
# @param color [Neotrellis::Neopixel::Color] Color to display by the pixel
|
102
|
+
def set(pixel, color)
|
103
|
+
raise "pixel out of range" unless pixel.between?(0, @n-1)
|
104
|
+
|
105
|
+
@seesaw.write(NEOPIXEL_BASE, NEOPIXEL_BUF, *([pixel*@bpp].pack('S>').unpack('C*')), *color.to_b(brightness))
|
106
|
+
show if @autoshow
|
107
|
+
end
|
108
|
+
|
109
|
+
# Render the data already written in the Seesaw buffer.
|
110
|
+
# If `autoshow` is true, this method is called automatically by `set()` and `fill()`
|
111
|
+
def show
|
112
|
+
@seesaw.write(NEOPIXEL_BASE, NEOPIXEL_SHOW)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Set the same color for all pixels of the array.
|
116
|
+
# If `autoshow` is false nothing will be displayed until you call the `show()` method.
|
117
|
+
#
|
118
|
+
# @param color [Neotrellis::Neopixel::Color] Color to display by the pixels
|
119
|
+
def fill(color)
|
120
|
+
# Disable auto show while filling the buffer
|
121
|
+
current_autoshow = @autoshow
|
122
|
+
@autoshow=false
|
123
|
+
|
124
|
+
@n.times do |pixel|
|
125
|
+
set(pixel, color)
|
126
|
+
end
|
127
|
+
|
128
|
+
@autoshow = current_autoshow
|
129
|
+
show if @autoshow
|
130
|
+
end
|
131
|
+
|
132
|
+
# Set a different random color for every pixels of the array.
|
133
|
+
# If `autoshow` is false nothing will be displayed until you call the `show()` method.
|
134
|
+
def fill_random()
|
135
|
+
# Disable auto show while filling the buffer
|
136
|
+
current_autoshow = @autoshow
|
137
|
+
@autoshow=false
|
138
|
+
|
139
|
+
@n.times do |pixel|
|
140
|
+
set(pixel, Color.new(rand(255), rand(255), rand(255)))
|
141
|
+
end
|
142
|
+
|
143
|
+
@autoshow = current_autoshow
|
144
|
+
show if @autoshow
|
145
|
+
end
|
146
|
+
|
147
|
+
# Swtich off all pixels of the array.
|
148
|
+
# If `autoshow` is false nothing will be displayed until you call the `show()` method.
|
149
|
+
def off()
|
150
|
+
fill(OFF)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Define a color to be set on a pixel
|
154
|
+
class Color
|
155
|
+
attr_reader :r, :g, :b # R G B components
|
156
|
+
|
157
|
+
# Create a new color.
|
158
|
+
# R G B values must be between 0 and 255
|
159
|
+
#
|
160
|
+
# @param r [Integer] Red component
|
161
|
+
# @param g [Integer] Green component
|
162
|
+
# @param b [Integer] Blue component
|
163
|
+
def initialize(r, g, b)
|
164
|
+
@r = within_range(r)
|
165
|
+
@g = within_range(g)
|
166
|
+
@b = within_range(b)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Apply a brightness to the color.
|
170
|
+
#
|
171
|
+
# @param brightness [Float] Brightness of the color. Must be between 0.0 and 1.0
|
172
|
+
#
|
173
|
+
# @return [Array] Color values as integer in the order GRB
|
174
|
+
def to_b(brightness = 1.0)
|
175
|
+
# Order is GRB
|
176
|
+
[(@g*brightness).to_i, (@r*brightness).to_i, (@b*brightness).to_i]
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def within_range(byte)
|
182
|
+
[[byte, 0].max, 255].min
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Default common colors
|
187
|
+
OFF = Color.new(0, 0, 0)
|
188
|
+
RED = Color.new(255, 0, 0)
|
189
|
+
YELLOW = Color.new(255, 150, 0)
|
190
|
+
GREEN = Color.new(0, 255, 0)
|
191
|
+
CYAN = Color.new(0, 255, 255)
|
192
|
+
BLUE = Color.new(0, 0, 255)
|
193
|
+
PURPLE = Color.new(180, 0, 255)
|
194
|
+
WHITE = Color.new(255, 255, 255)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# vim: ts=4:sw=4:ai
|