rustle 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +64 -0
- data/Rakefile +1 -0
- data/lib/rustle/color.rb +230 -0
- data/lib/rustle/exceptions.rb +10 -0
- data/lib/rustle/frame.rb +42 -0
- data/lib/rustle/receiver.rb +73 -0
- data/lib/rustle/strip.rb +95 -0
- data/lib/rustle/transition.rb +86 -0
- data/lib/rustle/transitions/fade_to_transition.rb +14 -0
- data/lib/rustle/transitions/wipe_to_transition.rb +20 -0
- data/lib/rustle/version.rb +3 -0
- data/lib/rustle.rb +14 -0
- data/rustle.gemspec +27 -0
- data/rustle_logo.png +0 -0
- data/sketches/basic.ino +82 -0
- data/spec/color_spec.rb +156 -0
- data/spec/receiver_spec.rb +5 -0
- data/spec/spec_helper.rb +8 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a832c77bff50413388dc4b5e7930c623a8807c5e
|
4
|
+
data.tar.gz: 4565b1dfd7ed5ddae95fe044b3419a3dfa850224
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ab2b36845325b8f6ad07c9c78ffbb83c6372b7c167c213d1444ffb1f3c9c18c7ba0fb42f5726c023a04cc2384b450ccf5596129764d41741a0d402bcf56a4774
|
7
|
+
data.tar.gz: 5eeea99ff6e98b0e7d310aa55f5a0b603a5feffa132dc915d6e2d2870c820dd8cc4a010aea53cdfd53a64d1e3dae4cb6f37106573e98d91cf0900a7c8b92bf32
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Rustle Changelog
|
2
|
+
|
3
|
+
## 0.1.1
|
4
|
+
|
5
|
+
#### New
|
6
|
+
|
7
|
+
* Strip now relies on a new class, Frame, to handle its state changes. With this system in place, serialization of strip state is much more efficient, and thus, timing is more precise, and much higher framerates are achievable. Strip objects now also maintain a queue of their future frames, and expose methods which allow frames to be advanced through.
|
8
|
+
* Code is now documented.
|
9
|
+
|
10
|
+
#### Fixed
|
11
|
+
|
12
|
+
* An oversight where the Strip class tried to call `#off` on itself, which raised a NilClass exception.
|
13
|
+
|
14
|
+
#### Deprecated
|
15
|
+
|
16
|
+
* `Color#brighten` and `Color#darken` have both been removed due to their apparent uselessness. They may be restored in future versions should a use for them be found.
|
17
|
+
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Andrew Hamon and Steven Petryk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
![Rustle](rustle_logo.png "Rustle Logo")
|
2
|
+
|
3
|
+
Rustle is a gem that aims to allow you to control an addressable RGB LED strip in Ruby (using an Arduino as a middleman). Run it on your computer, a Raspberry Pi, or whatever you want! All you need is a strip, an Arduino, and a serial connection.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
It's pretty easy.
|
8
|
+
|
9
|
+
$ gem install rustle
|
10
|
+
|
11
|
+
## Getting Started
|
12
|
+
|
13
|
+
### 1. Set up your Arduino
|
14
|
+
|
15
|
+
Upload the [basic.ino](sketches/basic.ino) sketch to your Arduino. Make sure that you take a look at the file first and modify it to suit your physical setup.
|
16
|
+
|
17
|
+
**Gotcha**: the Arduino resets itself upon any serial communication. We are currently considering switching to the [firmata protocol](http://firmata.org/wiki/Main_Page) in order to work around this problem. For now, you will need to **place a 10μF capacitor between the `RESET` and `GND` pins on your Arduino after uploading the sketch** if you want to use Rustle from a static Ruby script.
|
18
|
+
|
19
|
+
The sketch itself is still being worked on. Our goal, in the future, is to simply allow you to call `rustle arduino:prepare` to automatically generate and upload the sketch (with the number of LEDs, frames per second, etc) to the Arduino using [Ino](http://inotool.org/).
|
20
|
+
|
21
|
+
### 2. Connect to the Arduino
|
22
|
+
|
23
|
+
The majority of Rustle's actions are performed by calling methods on a receiver's LED strip. First, connect to your receiver:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
arduino = Rustle::Receiver.new do
|
27
|
+
# replace this path with the one specified by the Arduino IDE
|
28
|
+
port_file "/dev/[PORTFILE]"
|
29
|
+
|
30
|
+
# replace this with the number of LEDs connected to your receiver
|
31
|
+
num_leds 30
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
### 3. Throw a party
|
36
|
+
|
37
|
+
That's it! You can now control your LED strip with Ruby. Try out some of the following (this might be easier in a Ruby console):
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# Adding on to the code from earlier...
|
41
|
+
strip = arduino.strip
|
42
|
+
|
43
|
+
# Make the strip red
|
44
|
+
strip.to Color.hex('f00')
|
45
|
+
|
46
|
+
# Make a fancy-schmancy wipe transition to white
|
47
|
+
strip.transition :wipe_to, 2000, color: white
|
48
|
+
```
|
49
|
+
|
50
|
+
Want to make a custom transition? It's easy! See the [Transition class](lib/rustle/transition.rb) for a quick guide. If you want to make it a part of Rustle, throw it into the [lib/transitions](lib/transitions) folder, and submit a pull request.
|
51
|
+
|
52
|
+
## Documentation
|
53
|
+
|
54
|
+
$ yard
|
55
|
+
|
56
|
+
Alternatively, view it on [RubyDoc.info](http://rubydoc.info/github/stevenpetryk/rustle).
|
57
|
+
|
58
|
+
## Contributing
|
59
|
+
|
60
|
+
1. Fork it
|
61
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
62
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
63
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
64
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/rustle/color.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
module Rustle
|
2
|
+
class Color
|
3
|
+
# @return [Fixnum] the red value of the color
|
4
|
+
attr_reader :r
|
5
|
+
|
6
|
+
# @return [Fixnum] the green value of the color
|
7
|
+
attr_reader :g
|
8
|
+
|
9
|
+
# @return [Fixnum] the blue value of the color
|
10
|
+
attr_reader :b
|
11
|
+
|
12
|
+
# The Regex which verifies that a valid hex color code is supplied. It
|
13
|
+
# merely checks that a string is 6 characters long and consists of
|
14
|
+
# hexadecimal characters.
|
15
|
+
VALID_HEX_COLOR = /^[0-9a-f]{6}$/i
|
16
|
+
|
17
|
+
# Creates a new Color object given RGB values in the range +0..255+. Other
|
18
|
+
# values are accepted, but they will be coerced into this range (this is
|
19
|
+
# preferable to throwing exceptions, because there's a slim chance that an
|
20
|
+
# internal method will have some rounding errors).
|
21
|
+
#
|
22
|
+
# @param [Fixnum] r the red value
|
23
|
+
# @param [Fixnum] g the green value
|
24
|
+
# @param [Fixnum] b the blue value
|
25
|
+
#
|
26
|
+
# @return [Color] a new Color object with the given RGB values.
|
27
|
+
def initialize(r, g, b)
|
28
|
+
@r = normalize_rgb_param(r)
|
29
|
+
@g = normalize_rgb_param(g)
|
30
|
+
@b = normalize_rgb_param(b)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a new Color object given RGB values. Equivalent to {#initialize}.
|
34
|
+
def self.rgb(r, g, b)
|
35
|
+
self.new(r, g, b)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Array<Fixnum>] the Color's RGB values as an array with the
|
39
|
+
# format +[r, g, b]+
|
40
|
+
def to_a
|
41
|
+
[@r, @g, @b].map(&:floor)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Serializes a color into a string of +chars+.
|
45
|
+
# Note that each color is coerced into the range +0..254+, since our
|
46
|
+
# end-of-frame code is 255.
|
47
|
+
#
|
48
|
+
# @return [String] a string representation of the color.
|
49
|
+
def serialize
|
50
|
+
to_a.map { |c| self.class.fit_within_range(c, 0, 254).chr }.join
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks if a color is equal to another color.
|
54
|
+
#
|
55
|
+
# @param [Color] other the other Color object
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# red_rgb = Rustle::Color.rgb(255, 0, 0)
|
59
|
+
# red_hex = Rustle::Color.hex('f00')
|
60
|
+
# blue = Rustle::Color.rgb(0, 0, 255)
|
61
|
+
#
|
62
|
+
# red_rgb.eql? red_hex # => true
|
63
|
+
# red_rgb.eql? blue # => false
|
64
|
+
#
|
65
|
+
# @return [Boolean] true if the colors are equal, false if they are not.
|
66
|
+
def eql?(other)
|
67
|
+
[:r, :g, :b].all? { |c| self.send(c) == other.send(c) }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Creates a new color object given a hexadecimal value.
|
71
|
+
#
|
72
|
+
# @param [String] hex_value the hexadecimal representation of a color
|
73
|
+
#
|
74
|
+
# @return [Color] a new Color object based on the given hexadecimal value.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# # Valid
|
78
|
+
# Color.hex('#faa')
|
79
|
+
# Color.hex('fa03f2')
|
80
|
+
# Color.hex('#fa03f2')
|
81
|
+
#
|
82
|
+
# # Invalid (will raise)
|
83
|
+
# Color.hex('fzx')
|
84
|
+
# Color.hex('af3d')
|
85
|
+
def self.hex(hex_value)
|
86
|
+
# Remove leading hash
|
87
|
+
hex_value = hex_value[1..-1] if hex_value[0] == '#'
|
88
|
+
|
89
|
+
# Convert f00 to ff0000
|
90
|
+
if hex_value.length == 3
|
91
|
+
hex_value = hex_value.split("").map { |s| s+s }.join
|
92
|
+
end
|
93
|
+
|
94
|
+
# Validate the format
|
95
|
+
raise InvalidHexColorCode unless hex_value =~ VALID_HEX_COLOR
|
96
|
+
|
97
|
+
self.new *( hex_value.scan(/.{2}/).map(&:hex) )
|
98
|
+
end
|
99
|
+
|
100
|
+
# Creates a new Color object given HSB color values.
|
101
|
+
#
|
102
|
+
# @param [Fixnum] hue the color's hue (between 0 and 360)
|
103
|
+
# @param [Float] sat the color's saturation (between 0 and 1)
|
104
|
+
# @param [Float] bri the color's brightness (between 0 and 1)
|
105
|
+
#
|
106
|
+
# @return [Color] a new color object based on the given HSB values.
|
107
|
+
def self.hsb(hue, sat, bri)
|
108
|
+
hue = fit_within_range(hue, 0, 360)
|
109
|
+
sat = fit_within_range(sat, 0, 1)
|
110
|
+
bri = fit_within_range(bri, 0, 1)
|
111
|
+
|
112
|
+
if sat == 0
|
113
|
+
r = g = b = bri
|
114
|
+
else
|
115
|
+
hh = hue % 360
|
116
|
+
hh /= 60.0
|
117
|
+
i = hh.to_i
|
118
|
+
f = hh - i
|
119
|
+
|
120
|
+
p = bri * (1 - sat)
|
121
|
+
q = bri * (1 - sat * f)
|
122
|
+
t = bri * (1 - sat * (1 - f))
|
123
|
+
|
124
|
+
case i
|
125
|
+
when 0
|
126
|
+
r = bri
|
127
|
+
g = t
|
128
|
+
b = p
|
129
|
+
when 1
|
130
|
+
r = q
|
131
|
+
g = bri
|
132
|
+
b = p
|
133
|
+
when 2
|
134
|
+
r = p
|
135
|
+
g = bri
|
136
|
+
b = t
|
137
|
+
when 3
|
138
|
+
r = p
|
139
|
+
g = q
|
140
|
+
b = bri
|
141
|
+
when 4
|
142
|
+
r = t
|
143
|
+
g = p
|
144
|
+
b = bri
|
145
|
+
else
|
146
|
+
r = bri
|
147
|
+
g = p
|
148
|
+
b = q
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
self.new *( [r, g, b].map { |c| (c * 255 + 0.5) } )
|
153
|
+
end
|
154
|
+
|
155
|
+
# Converts the Color object to an array of HSB values in the format
|
156
|
+
# [hue, saturation, brightness].
|
157
|
+
#
|
158
|
+
# @return [Array<Fixnum>] the color as an HSB array
|
159
|
+
def to_hsb
|
160
|
+
r = @r / 255.0
|
161
|
+
g = @g / 255.0
|
162
|
+
b = @b / 255.0
|
163
|
+
max = [r, g, b].max
|
164
|
+
min = [r, g, b].min
|
165
|
+
delta = max - min
|
166
|
+
hue = 0.0
|
167
|
+
brightness = max
|
168
|
+
saturation = max == 0 ? 0 : (max - min) / max
|
169
|
+
|
170
|
+
if delta != 0
|
171
|
+
if r == max
|
172
|
+
hue = (g - b) / delta
|
173
|
+
else
|
174
|
+
if g == max
|
175
|
+
hue = 2 + (b - r) / delta
|
176
|
+
else
|
177
|
+
hue = 4 + (r - g) / delta
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
hue *= 60
|
182
|
+
hue += 360 if hue < 0
|
183
|
+
end
|
184
|
+
[hue, saturation, brightness]
|
185
|
+
end
|
186
|
+
|
187
|
+
# Creates an intermediate between the current Color object and another
|
188
|
+
# Color object. It does so by simple linear RGB transitioning, which seems
|
189
|
+
# to work just fine when working with RGB LED strips (even though, in most)
|
190
|
+
# cases, HSB transitioning would be better, it is not worth the extra
|
191
|
+
# processing time).
|
192
|
+
#
|
193
|
+
# @param [Color] other the other Color object
|
194
|
+
# @param [Float] amount the degree to which the color should be transitioned
|
195
|
+
# +(0..1)+.
|
196
|
+
#
|
197
|
+
# @return [Color] the new, partially transitioned Color object
|
198
|
+
#
|
199
|
+
# @example
|
200
|
+
# red = Color.new(255, 0, 0)
|
201
|
+
# blue = Color.new(0, 0, 255)
|
202
|
+
#
|
203
|
+
# red.transition_to(blue, 0.5) # => <Color r=127, g=0, b=127>
|
204
|
+
def transition_to(other, amount)
|
205
|
+
old_col = self.to_a
|
206
|
+
new_col = other.to_a
|
207
|
+
|
208
|
+
diff = old_col.each_with_index.map { |v, i| (new_col[i] - v) * amount }
|
209
|
+
|
210
|
+
self.class.rgb *old_col.each_with_index.map { |v, i| v + diff[i] }
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
# Forces `val` into the range 0..255
|
216
|
+
def normalize_rgb_param(value)
|
217
|
+
self.class.fit_within_range(value, 0, 255)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Coerces `value` into the range given
|
221
|
+
def self.fit_within_range(value, min_allowed, max_allowed)
|
222
|
+
if (value <= max_allowed) && (value >= min_allowed)
|
223
|
+
value
|
224
|
+
else
|
225
|
+
value > max_allowed ? max_allowed : min_allowed
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Rustle
|
2
|
+
# A general Rustle exception
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# Raised when user fails to provide a port_file
|
6
|
+
class PortFileNotSpecified < Error; end
|
7
|
+
|
8
|
+
# Raised when a user provides an invalid hexadecimal color
|
9
|
+
class InvalidHexColorCode < Error; end
|
10
|
+
end
|
data/lib/rustle/frame.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rustle
|
2
|
+
# = Frames
|
3
|
+
#
|
4
|
+
# All strip changes are sent by serializing frames and sending them to the
|
5
|
+
# receiver. Serialization of a frame is done by serializing all {Color}
|
6
|
+
# objects in the +leds+ array (see {Color#serialize}), and then appending the
|
7
|
+
# +char+ 255 to the end. Serialized frames are, thus, strings of +chars+,
|
8
|
+
# where each +char+ is a single RGB color channel.
|
9
|
+
#
|
10
|
+
# It is worth noting that, since 255 is our end-of-frame code, all color data
|
11
|
+
# sent as an integer in the range +0..254+. It is safe to assume that an
|
12
|
+
# approximate 1/255th drop in brightness is not noticeable. There is, however,
|
13
|
+
# the option of modifying the sketch to multiply each RGB channel by
|
14
|
+
# 1.004 and +floor+ing the value.
|
15
|
+
#
|
16
|
+
# Coercion of RGB values into the range +0..254+ is handled in
|
17
|
+
# {Color#serialize}.
|
18
|
+
class Frame
|
19
|
+
# LEDs in a frame are represented as an array of {Color} objects. Each Color
|
20
|
+
# object corresponds to a given LED. The idea is that any physical LED
|
21
|
+
# matches its corresponding Color object.
|
22
|
+
#
|
23
|
+
# @return [Array<Color>] an array of {Color} objects (LEDs).
|
24
|
+
attr_reader :leds
|
25
|
+
|
26
|
+
# Initializes a new frame.
|
27
|
+
#
|
28
|
+
# @param [Array] leds n array of {Color} objects, whose length is the same
|
29
|
+
# as the number of LEDs connected to the strip.
|
30
|
+
def initialize(leds)
|
31
|
+
@leds = leds
|
32
|
+
end
|
33
|
+
|
34
|
+
# Serializes all LED {Color} objects (see {Color#serialize}) and appends
|
35
|
+
# the end-of-frame code (+255.chr+)
|
36
|
+
#
|
37
|
+
# @return [String] the serialized frame, represented as a string of +chars+.
|
38
|
+
def serialize
|
39
|
+
@leds.map(&:serialize).join + 255.chr
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
|
3
|
+
module Rustle
|
4
|
+
# = Receivers
|
5
|
+
#
|
6
|
+
# Your receiver is, effectively, your Arduino. The receiver class's purpose is
|
7
|
+
# to manage the serial communication between Ruby and your Arduino, and to
|
8
|
+
# ensure that changes made to a Strip object are reflected immediately on the
|
9
|
+
# actual LED strip. This class, however, is *not* responsible for actual
|
10
|
+
# serialization; the Frame and Color classes serialize themselves.
|
11
|
+
#
|
12
|
+
# The Receiver class is initialized using a block.
|
13
|
+
#
|
14
|
+
# bedroom_arduino = Rustle::Receiver.new do
|
15
|
+
# port_file "/dev/[PORT_FILE]"
|
16
|
+
# num_leds 30
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Your port file can be found using the Arduino IDE.
|
20
|
+
class Receiver
|
21
|
+
# When your receiver is instantiated, a {Strip} object is automatically
|
22
|
+
# created alongside it.
|
23
|
+
# @return [Strip] the strip associated with the Receiver
|
24
|
+
attr_reader :strip
|
25
|
+
|
26
|
+
# Initializes a new receiver.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# kitchen_arduino = Rustle::Receiver.new do
|
30
|
+
# port_file "/dev/tty.usbmodem411" # replace this
|
31
|
+
# num_leds 60
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# @yieldparam [String] port_file the location of serial port file. It can be
|
35
|
+
# found in the Arduino IDE (or via Ino).
|
36
|
+
# @yieldparam [Fixnum] baud (optional) the serial baud rate (make sure it matches the one in
|
37
|
+
# your sketch)
|
38
|
+
# @yieldparam [Fixnum] num_leds (optional) the total number of LEDs connected to your
|
39
|
+
# receiver. Note that this attribute only affects how the {Strip} object
|
40
|
+
# is instantiated; the number of LEDs is not actually stored in the
|
41
|
+
# Receiver object.
|
42
|
+
def initialize(&block)
|
43
|
+
# Default value
|
44
|
+
@baud = 921_600
|
45
|
+
|
46
|
+
instance_eval &block
|
47
|
+
|
48
|
+
init_serialport
|
49
|
+
end
|
50
|
+
|
51
|
+
# Serializes +frame+ and sends it off to the the serial port. +push_frame+
|
52
|
+
# is used internally, so its direct use is discouraged, but it would work
|
53
|
+
# in theory.
|
54
|
+
# @param [Frame] frame a frame object
|
55
|
+
def push_frame(frame)
|
56
|
+
@port.write frame.serialize
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# instance_eval-specific methods for our DSL
|
62
|
+
def port_file(file); @port_file = file; end
|
63
|
+
def baud(int); @baud = int; end
|
64
|
+
def num_leds(count)
|
65
|
+
@strip = Strip.new(self, count)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Instantiates a new serial port
|
69
|
+
def init_serialport
|
70
|
+
@port = SerialPort.new @port_file, @baud, 8, 1, SerialPort::NONE
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/rustle/strip.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module Rustle
|
4
|
+
# = Strips
|
5
|
+
#
|
6
|
+
# The Strip class' purpose is to respond to requests to change the color of
|
7
|
+
# the physical strip, and send changes to the {Receiver} when appropriate.
|
8
|
+
# All Strip objects also have a buffer, which is simply an array of {Frame}
|
9
|
+
# objects. Calling any method which advances to the next frame causes the
|
10
|
+
# Strip to request the Receiver class to push an update over the serial port.
|
11
|
+
#
|
12
|
+
# New strips cannot be instantiated outside of the {Receiver} class.
|
13
|
+
# See {Receiver#initialize} for details on how Strips are instantiated.
|
14
|
+
class Strip
|
15
|
+
# @return [Receiver] the receiver associated with the strip
|
16
|
+
attr_reader :receiver
|
17
|
+
|
18
|
+
# @return [Fixnum] the number of LEDs connected to the strip
|
19
|
+
attr_reader :num_leds
|
20
|
+
|
21
|
+
# Initializes a new strip given a {Receiver} object and the number of LEDs
|
22
|
+
# attached to the strip.
|
23
|
+
#
|
24
|
+
# @param [Receiver] receiver
|
25
|
+
# @param [Fixnum] num_leds the number of LEDs connected to the strip.
|
26
|
+
def initialize(receiver, num_leds = 32)
|
27
|
+
@receiver = receiver
|
28
|
+
@num_leds = num_leds
|
29
|
+
@queue = []
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Frame] the next frame in the buffer. If the buffer only has one
|
33
|
+
# frame, it returns the current frame.
|
34
|
+
def next_frame
|
35
|
+
@queue[1] || current_frame
|
36
|
+
end
|
37
|
+
|
38
|
+
# Advances the strip to the next frame.
|
39
|
+
def next_frame!
|
40
|
+
@receiver.push_frame(next_frame)
|
41
|
+
@queue.shift if @queue.length > 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Frame] the current frame being displayed by the strip.
|
45
|
+
def current_frame
|
46
|
+
@queue.first
|
47
|
+
end
|
48
|
+
|
49
|
+
# Queues an array of frames for transitions. Note: this does *not*
|
50
|
+
# cause any physical changes; it merely prepares a list thereof.
|
51
|
+
#
|
52
|
+
# @param [Array<Frame>] frames an array of frames to load into the queue.
|
53
|
+
def queue_frames(frames)
|
54
|
+
@queue += frames
|
55
|
+
end
|
56
|
+
|
57
|
+
# Turns off all LEDs on the strip
|
58
|
+
def off!
|
59
|
+
@queue << Frame.new([Color.new(0,0,0)] * @num_leds)
|
60
|
+
next_frame!
|
61
|
+
end
|
62
|
+
|
63
|
+
# Changes all LEDs to a particular color
|
64
|
+
#
|
65
|
+
# @param [Color] color
|
66
|
+
def to(color)
|
67
|
+
@queue << Frame.new([color] * @num_leds)
|
68
|
+
next_frame!
|
69
|
+
end
|
70
|
+
|
71
|
+
# Instantiates and executes a {Transition} subclass.
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# strip = # [initialized strip...]
|
75
|
+
#
|
76
|
+
# # Transition the strip to red in 2 seconds using the wipe-to transition
|
77
|
+
# strip.transition :wipe_to, 2000, color: Rustle::Color.new(255, 0, 0)
|
78
|
+
#
|
79
|
+
# @param [Symbol] klass a symbol representing the name of the class (e.g.
|
80
|
+
# for {WipeToTransition}, the corresponding symbol would be +:wipe_to+).
|
81
|
+
# @param [Fixnum] duration the duration of the transition in milliseconds
|
82
|
+
# @param [Hash] opts a hash containing transition-specific options
|
83
|
+
#
|
84
|
+
# @return [Transition] an instance of a Transition subclass
|
85
|
+
def transition(klass, duration, opts = {})
|
86
|
+
# Default to white
|
87
|
+
opts[:color] ||= Color.rgb(255, 255, 255)
|
88
|
+
|
89
|
+
# :fade_to => FadeToTransition
|
90
|
+
klass = "#{klass.to_s.camelize}Transition".constantize
|
91
|
+
|
92
|
+
klass.new(self, duration, opts).frames
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Rustle
|
2
|
+
# = Transitions
|
3
|
+
#
|
4
|
+
# Transitions are created by subclassing this class. See {FadeToTransition}
|
5
|
+
# for the most basic example.
|
6
|
+
#
|
7
|
+
# == Creating a Transition
|
8
|
+
# You'll need to create a subclass of {Transition} and require it in your
|
9
|
+
# project.
|
10
|
+
#
|
11
|
+
# === Override the subclass's {#setup} and {#animate} methods.
|
12
|
+
# Use +setup+ to create any instance variables you may need to calculate
|
13
|
+
# before the transitio renders. The +opts+ passed in when calling
|
14
|
+
# {Strip#transition} are preserved and sent to this method.
|
15
|
+
#
|
16
|
+
# {#animate} is called once-per-LED-per-frame, and expects you to return a
|
17
|
+
# {Color object}.
|
18
|
+
class Transition
|
19
|
+
attr_reader :frames, :duration
|
20
|
+
|
21
|
+
def initialize(strip, duration, opts = {})
|
22
|
+
@strip = strip
|
23
|
+
@duration = duration
|
24
|
+
@total_frames = (duration.to_f / (1000/Rustle::FRAME_RATE.to_f)).ceil
|
25
|
+
@frame_duration = 1.0/Rustle::FRAME_RATE
|
26
|
+
|
27
|
+
self.setup(opts)
|
28
|
+
|
29
|
+
render
|
30
|
+
end
|
31
|
+
|
32
|
+
# Gets called before the transition begins. Override it to set up any
|
33
|
+
# instance variables you need in the {#animate} method.
|
34
|
+
#
|
35
|
+
# @param [Hash] opts the options hash that is passed in when calling
|
36
|
+
# {Strip#serialize}
|
37
|
+
#
|
38
|
+
# Within this method, you also have the following available:
|
39
|
+
# * +@total_frames+: the total number of frames in the animation
|
40
|
+
# * +@duration+: the duration of the animation in milliseconds
|
41
|
+
# * +@frame_duration+: the duration of an individual frame (generally only
|
42
|
+
# used internally)
|
43
|
+
def setup(opts); end
|
44
|
+
|
45
|
+
# Gets called once-per-LED-per-frame. Override it, and do your calculations
|
46
|
+
# therein. You will have access to any instance variables you declare in
|
47
|
+
# {#setup}.
|
48
|
+
#
|
49
|
+
# @param [Fixnum] led_index the index of the current LED, starting at 0.
|
50
|
+
# @param [Color] starting_color the color that the current LED was
|
51
|
+
# displaying before the transition began
|
52
|
+
# @param [Fixnum] frame_num the current frame number in the transition
|
53
|
+
#
|
54
|
+
# @return [Color] a color object corresponding to what the new LED color
|
55
|
+
# should be.
|
56
|
+
#
|
57
|
+
# Within this method, you also have the following available:
|
58
|
+
# * +@total_frames+: the total number of frames in the animation
|
59
|
+
# * +@duration+: the duration of the animation in milliseconds
|
60
|
+
# * +@frame_duration+: the duration of an individual frame (generally only
|
61
|
+
# used internally)
|
62
|
+
def animate(led_index, starting_color, frame_num); end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def render
|
67
|
+
starting_frame = @strip.current_frame
|
68
|
+
|
69
|
+
@frames = Array.new(@total_frames) do |frame_num|
|
70
|
+
Frame.new(
|
71
|
+
Array.new(@strip.num_leds) do |led_index|
|
72
|
+
# Gets called once per LED per frame
|
73
|
+
animate(led_index, starting_frame.leds[led_index], frame_num)
|
74
|
+
end
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
@strip.queue_frames @frames
|
79
|
+
|
80
|
+
@total_frames.times do
|
81
|
+
@strip.next_frame!
|
82
|
+
sleep @frame_duration
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class FadeToTransition < Rustle::Transition
|
2
|
+
def name
|
3
|
+
:fade_to
|
4
|
+
end
|
5
|
+
|
6
|
+
def setup(opts)
|
7
|
+
@new_color = opts[:color]
|
8
|
+
end
|
9
|
+
|
10
|
+
# Return a color object
|
11
|
+
def animate(led_index, start_color, frame_num)
|
12
|
+
start_color.transition_to @new_color, (frame_num.to_f+1) / @total_frames.to_f
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class WipeToTransition < Rustle::Transition
|
2
|
+
def name
|
3
|
+
:wipe_to
|
4
|
+
end
|
5
|
+
|
6
|
+
def setup(opts)
|
7
|
+
@adj_factor = @total_frames.to_f/@strip.num_leds.to_f
|
8
|
+
@new_color = opts[:color]
|
9
|
+
end
|
10
|
+
|
11
|
+
def animate(led_index, start_color, frame_num)
|
12
|
+
amount = 1.0 /
|
13
|
+
(@total_frames-(led_index)*@adj_factor) *
|
14
|
+
((frame_num+1) - (led_index)*@adj_factor)
|
15
|
+
|
16
|
+
amount = [amount, 0.0].max
|
17
|
+
|
18
|
+
start_color.transition_to @new_color, amount
|
19
|
+
end
|
20
|
+
end
|
data/lib/rustle.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "rustle/version"
|
2
|
+
|
3
|
+
require "rustle/receiver"
|
4
|
+
require "rustle/strip"
|
5
|
+
require "rustle/color"
|
6
|
+
require "rustle/frame"
|
7
|
+
require "rustle/transition"
|
8
|
+
require "rustle/transitions/wipe_to_transition"
|
9
|
+
require "rustle/transitions/fade_to_transition"
|
10
|
+
require "rustle/exceptions"
|
11
|
+
|
12
|
+
module Rustle
|
13
|
+
FRAME_RATE = 60
|
14
|
+
end
|
data/rustle.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rustle/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rustle"
|
8
|
+
spec.version = Rustle::VERSION
|
9
|
+
spec.authors = ["Andrew Hamon", "Steven Petryk"]
|
10
|
+
spec.email = ["and.ham95@gmail.com", "petryk.steven@gmail.com"]
|
11
|
+
spec.description = %q{Rustle is a gem that allows you to control an individually-addressable RGB LED strip via Ruby (with an Arduino acting as a middleman for PWM controls).}
|
12
|
+
spec.summary = %q{Control an individually-addressable RGB LED strip in Ruby.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib", "lib/transitions"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'active_support', '~> 3.0', '>= 3.0.0'
|
22
|
+
spec.add_runtime_dependency 'serialport', '~> 1.3', '>= 1.3.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency 'rspec', '~> 2.14', '>= 2.14.1'
|
27
|
+
end
|
data/rustle_logo.png
ADDED
Binary file
|
data/sketches/basic.ino
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
/**
|
2
|
+
* A basic, one-way Rustle template sketch.
|
3
|
+
*
|
4
|
+
* LICENSE: This source file is subject to the MIT License, which is available
|
5
|
+
* through the World Wide Web at http://opensource.org/licenses/MIT, and is also
|
6
|
+
* included as a part of this repository.
|
7
|
+
*
|
8
|
+
* @author Andrew Hamon <and.ham95@gmail.com>
|
9
|
+
* @author Steven Petryk <petryk.steven@gmail.com>
|
10
|
+
* @copyright 2014 Andrew Hamon
|
11
|
+
* @license MIT License
|
12
|
+
*/
|
13
|
+
|
14
|
+
#include <Adafruit_NeoPixel.h>
|
15
|
+
|
16
|
+
/*
|
17
|
+
* Ensure that these directives match your physical setup.
|
18
|
+
*
|
19
|
+
* Note: The baud rate may need to be decreased depending on your setup. The Uno
|
20
|
+
* seems to handle 921,600 just fine, and it's what Rustle defaults to.
|
21
|
+
*/
|
22
|
+
#define STRIP_LENGTH 30
|
23
|
+
#define STRIP_PIN 6
|
24
|
+
#define BAUD 921600
|
25
|
+
|
26
|
+
// Initialize LED strip
|
27
|
+
Adafruit_NeoPixel myStrip = Adafruit_NeoPixel(STRIP_LENGTH, STRIP_PIN,
|
28
|
+
NEO_GRB + NEO_KHZ800);
|
29
|
+
|
30
|
+
// Current LED index within a frame
|
31
|
+
byte led_index;
|
32
|
+
|
33
|
+
// Each LED is an RGB array. `k` is just a counter (0,1,2 == R,G,B).
|
34
|
+
byte current_led[3];
|
35
|
+
byte k;
|
36
|
+
|
37
|
+
void setup() {
|
38
|
+
// Ensure this matches the baud in your receiver initialization.
|
39
|
+
Serial.begin(BAUD);
|
40
|
+
|
41
|
+
// Initialize counters
|
42
|
+
led_index = 0;
|
43
|
+
k = 0;
|
44
|
+
|
45
|
+
// Here we go
|
46
|
+
myStrip.begin();
|
47
|
+
myStrip.show();
|
48
|
+
}
|
49
|
+
|
50
|
+
void serialEvent() {
|
51
|
+
while(Serial.available()) {
|
52
|
+
byte x = Serial.read();
|
53
|
+
|
54
|
+
// 255 is the end-of-frame code, so we can assume that all other values
|
55
|
+
// are actual RGB values.
|
56
|
+
if (x == 255) {
|
57
|
+
myStrip.show();
|
58
|
+
|
59
|
+
k = led_index = 0;
|
60
|
+
}
|
61
|
+
|
62
|
+
else if (x < 255) {
|
63
|
+
// Data is sent [r,g,b,r,g,b,...] so we must accumulate an entire RGB
|
64
|
+
// triplet before writing to the LED strip.
|
65
|
+
current_led[k] = x;
|
66
|
+
k++;
|
67
|
+
|
68
|
+
// If we've accumulated a triplet...
|
69
|
+
if(k > 2) {
|
70
|
+
// ... we send the pixel.
|
71
|
+
myStrip.setPixelColor(led_index,
|
72
|
+
current_led[0], current_led[1], current_led[2]);
|
73
|
+
|
74
|
+
k = 0;
|
75
|
+
led_index++;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
// Empty loop because sketch is entirely serial event driven
|
82
|
+
void loop() {};
|
data/spec/color_spec.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rustle::Color do
|
4
|
+
|
5
|
+
let(:color) { Rustle::Color.rgb(128,128,128) }
|
6
|
+
|
7
|
+
context "when initialized" do
|
8
|
+
let(:color_with_invalid_params) { Rustle::Color.new(-100,1000,0) }
|
9
|
+
|
10
|
+
it "corrects negative rgb values to zero" do
|
11
|
+
(color_with_invalid_params.r).should eq(0)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "corrects very large rgb values to 255" do
|
15
|
+
(color_with_invalid_params.g).should eq(255)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "makes no changes to rgb values between 0 and 255" do
|
19
|
+
color1 = Rustle::Color.new(0,0,0)
|
20
|
+
(color1.r).should eq(0)
|
21
|
+
(color1.g).should eq(0)
|
22
|
+
(color1.b).should eq(0)
|
23
|
+
|
24
|
+
color2 = Rustle::Color.new(255,255,255)
|
25
|
+
(color2.r).should eq(255)
|
26
|
+
(color2.g).should eq(255)
|
27
|
+
(color2.b).should eq(255)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#to_a" do
|
32
|
+
it "returns an array" do
|
33
|
+
(color.to_a).should be_a(Array)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "the array has the correct [r,g,b] values" do
|
37
|
+
(color.to_a).should eq([128,128,128])
|
38
|
+
end
|
39
|
+
|
40
|
+
it "rounds decimal values down to the nearest whole number" do
|
41
|
+
fractional_rgbs = Rustle::Color.rgb(0.5,10.7,99.9)
|
42
|
+
(fractional_rgbs.to_a).should eq([0, 10, 99])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#to_s" do
|
47
|
+
it "returns a string with information about the color" do
|
48
|
+
(color.to_s).should eq("rgb(128, 128, 128)")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "pads strings so that even small rgb values align properly" do
|
52
|
+
other_color = Rustle::Color.rgb(0,1,2)
|
53
|
+
(other_color.to_s).should eq("rgb( 0, 1, 2)")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#eql?" do
|
58
|
+
let(:colorA) { Rustle::Color.rgb(128,128,128) }
|
59
|
+
let(:another_colorA) { Rustle::Color.rgb(128,128,128) }
|
60
|
+
let(:colorB) { Rustle::Color.rgb(0, 0, 0) }
|
61
|
+
|
62
|
+
it "returns true if the reciever and parameter objects hold equivalent rgb values" do
|
63
|
+
(colorA.eql?(another_colorA)).should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns false if the reciever and parameter objects differ in rgb values" do
|
67
|
+
(colorA.eql?(colorB)).should be_false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "Rustle::Color.hex factory method" do
|
72
|
+
let(:badass_rgb) { Rustle::Color.rgb(186, 218, 85) }
|
73
|
+
|
74
|
+
context "understands hex color codes WITHOUT a leading hash" do
|
75
|
+
let (:badass) { Rustle::Color.hex "BADA55" }
|
76
|
+
|
77
|
+
it "creates a color object" do
|
78
|
+
(badass).should be_a(Rustle::Color)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "has the proper rgb values" do
|
82
|
+
(badass).should eql(badass_rgb)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "understands hex color codes WITH a leading hash" do
|
87
|
+
let(:badass) { Rustle::Color.hex "#BADA55" }
|
88
|
+
|
89
|
+
it "creates a color object" do
|
90
|
+
(badass).should be_a(Rustle::Color)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "has the right rgb values" do
|
94
|
+
(badass).should eql(badass_rgb)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "understands hex color codes in three character format" do
|
99
|
+
let(:bad_hex) { Rustle::Color.hex "#BAD" }
|
100
|
+
let(:bad_rgb) { Rustle::Color.rgb 187, 170, 221 }
|
101
|
+
|
102
|
+
it "creates a color object" do
|
103
|
+
(bad_hex).should be_a(Rustle::Color)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should have the correct rgb values" do
|
107
|
+
(bad_hex).should eql(bad_rgb)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "is case insensitive" do
|
112
|
+
first_badass = Rustle::Color.hex "#BADa55"
|
113
|
+
second_badass = Rustle::Color.hex "#badA55"
|
114
|
+
(first_badass).should eql(second_badass)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "raises an error when given an invalid color code string" do
|
118
|
+
expect { Rustle::Color.hex "#ABCDEFABCDEF" }.to raise_error(Rustle::InvalidHexColorCode)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "Rustle::Color.hsb factory method" do
|
123
|
+
let(:rgb) { Rustle::Color.rgb 187, 170, 221 }
|
124
|
+
let(:hsb) { Rustle::Color.hsb 260, 0.23, 0.866 }
|
125
|
+
# Color: #BBAADD
|
126
|
+
# RGB: (187, 170, 221)
|
127
|
+
# HSV: 260 (260), saturation: 23 (23.0769), value: 87 (86.6667)
|
128
|
+
|
129
|
+
it "produces a color object" do
|
130
|
+
hsb.should be_a(Rustle::Color)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "has the correct rgb properties" do
|
134
|
+
# Use be_within to give some allowance for rounting errors
|
135
|
+
(hsb.r).should be_within(3).of(rgb.r)
|
136
|
+
(hsb.g).should be_within(3).of(rgb.g)
|
137
|
+
(hsb.b).should be_within(3).of(rgb.b)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#to_hsb" do
|
142
|
+
let(:hsb) { Rustle::Color.hsb 180, 0.5, 0.8 }
|
143
|
+
|
144
|
+
it "returns an array of [h,s,v] coordinates" do
|
145
|
+
color.to_hsb.should be_a(Array)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should have the (approximate) [h,s,v] coordinates" do
|
149
|
+
hsb_arr = hsb.to_hsb
|
150
|
+
hsb_arr[0].should be_within(1).of(180)
|
151
|
+
hsb_arr[1].should be_within(0.1).of(0.5)
|
152
|
+
hsb_arr[2].should be_within(0.1).of(0.8)
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rustle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Hamon
|
8
|
+
- Steven Petryk
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-02-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: active_support
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '3.0'
|
21
|
+
- - '>='
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 3.0.0
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.0'
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.0
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: serialport
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- - '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.3.0
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ~>
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '1.3'
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.3.0
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: bundler
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.3'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.3'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rake
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rspec
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ~>
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.14'
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 2.14.1
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ~>
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '2.14'
|
99
|
+
- - '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.14.1
|
102
|
+
description: Rustle is a gem that allows you to control an individually-addressable
|
103
|
+
RGB LED strip via Ruby (with an Arduino acting as a middleman for PWM controls).
|
104
|
+
email:
|
105
|
+
- and.ham95@gmail.com
|
106
|
+
- petryk.steven@gmail.com
|
107
|
+
executables: []
|
108
|
+
extensions: []
|
109
|
+
extra_rdoc_files: []
|
110
|
+
files:
|
111
|
+
- .gitignore
|
112
|
+
- .rspec
|
113
|
+
- CHANGELOG.md
|
114
|
+
- Gemfile
|
115
|
+
- LICENSE
|
116
|
+
- README.md
|
117
|
+
- Rakefile
|
118
|
+
- lib/rustle.rb
|
119
|
+
- lib/rustle/color.rb
|
120
|
+
- lib/rustle/exceptions.rb
|
121
|
+
- lib/rustle/frame.rb
|
122
|
+
- lib/rustle/receiver.rb
|
123
|
+
- lib/rustle/strip.rb
|
124
|
+
- lib/rustle/transition.rb
|
125
|
+
- lib/rustle/transitions/fade_to_transition.rb
|
126
|
+
- lib/rustle/transitions/wipe_to_transition.rb
|
127
|
+
- lib/rustle/version.rb
|
128
|
+
- rustle.gemspec
|
129
|
+
- rustle_logo.png
|
130
|
+
- sketches/basic.ino
|
131
|
+
- spec/color_spec.rb
|
132
|
+
- spec/receiver_spec.rb
|
133
|
+
- spec/spec_helper.rb
|
134
|
+
homepage: ''
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
- lib/transitions
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.2.2
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Control an individually-addressable RGB LED strip in Ruby.
|
159
|
+
test_files:
|
160
|
+
- spec/color_spec.rb
|
161
|
+
- spec/receiver_spec.rb
|
162
|
+
- spec/spec_helper.rb
|
163
|
+
has_rdoc:
|