apa102_rbpi 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/CHANGELOG.md +5 -0
- data/README.md +91 -12
- data/apa102_rbpi.gemspec +1 -0
- data/lib/apa102_rbpi.rb +21 -0
- data/lib/apa102_rbpi/apa102.rb +34 -46
- data/lib/apa102_rbpi/spi_simulator.rb +52 -0
- data/lib/apa102_rbpi/strip.rb +108 -0
- data/lib/apa102_rbpi/version.rb +1 -1
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40336d05916f6d9efd8ed00b90b0e0c470bf3f90
|
4
|
+
data.tar.gz: cdc426844412559ffd66de9a3c744105c938a766
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a73f59c02624d41198d4cda1b8cb1250bd8659c2a69c2e5a47b3c5d66930892a296f72ffc0c3c38c1fed6c32b6226b3547c09d061b130ff8693c15cbdfbc2a9b
|
7
|
+
data.tar.gz: b9f570c6e1feefa82b0e0eaf2f400016f352fd8a7a244d448c224fa2c0c629c5f6a3a7de685e162090d77496628d6c6503b50381d72eec04fed2bdf8da66e05a
|
data/.rspec
ADDED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Apa102Rbpi
|
2
|
-
|
3
2
|
Simple library to drive APA102/Dotstar LEDs using a Raspberry Pi
|
4
3
|
|
5
|
-
##
|
4
|
+
## Setup
|
5
|
+
The Pi can communicate directly with a APA102 LED strip through the [SPI bus](https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md). Connect the data and clock inputs on the strip to the MOSI and SCLK pins on the Pi board. Be sure to use a 5V power adapter and a logic level shifter for the APA102 inputs as the Pi only outputs 3.3V. After handling the hardware, you're ready to tackle the fun part!
|
6
6
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
@@ -12,33 +12,112 @@ gem 'apa102_rbpi'
|
|
12
12
|
|
13
13
|
And then execute:
|
14
14
|
|
15
|
-
|
15
|
+
```
|
16
|
+
$ bundle
|
17
|
+
```
|
16
18
|
|
17
19
|
Or install it yourself as:
|
18
20
|
|
19
|
-
|
21
|
+
```
|
22
|
+
$ gem install apa102_rbpi
|
23
|
+
```
|
20
24
|
|
21
25
|
## Usage
|
26
|
+
I recommend playing around in the ruby console when getting started.
|
27
|
+
|
28
|
+
Note that you must have root permissions to use SPI! Use:
|
29
|
+
```
|
30
|
+
sudo irb
|
31
|
+
```
|
32
|
+
or
|
33
|
+
```
|
34
|
+
sudo bundle exec pretty_lights.rb
|
35
|
+
```
|
22
36
|
|
23
37
|
Simple blinker:
|
38
|
+
|
24
39
|
```ruby
|
25
40
|
require 'apa102_rbpi'
|
26
|
-
|
27
|
-
|
28
|
-
led = Apa102.new(1)
|
41
|
+
my_strip = Apa102Rbpi.strip
|
29
42
|
loop do
|
30
|
-
|
43
|
+
my_strip.set_pixel!(0, 0xffffff)
|
31
44
|
sleep 1
|
32
|
-
|
45
|
+
my_strip.set_pixel!(0, 0)
|
33
46
|
sleep 1
|
34
47
|
end
|
35
48
|
```
|
36
49
|
|
37
|
-
##
|
50
|
+
## Configuration
|
51
|
+
```ruby
|
52
|
+
Apa102Rbpi.configure do |c|
|
53
|
+
c.num_leds = 100
|
54
|
+
c.led_frame_rgb_offsets = {
|
55
|
+
red: 3,
|
56
|
+
blue: 2,
|
57
|
+
green: 1
|
58
|
+
}
|
59
|
+
c.brightness = 31
|
60
|
+
c.spi_hz = 8000000
|
61
|
+
c.simulate = ENV['simulate'] || false
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
#### num_leds
|
66
|
+
Set this to your strip's total leds.
|
67
|
+
|
68
|
+
#### led_frame_rgb_offsets
|
69
|
+
Some strips are manufactured under different specifications such that the red/green/blue data frames are in different positions. If you try to input a [r,g,b] color into your strip and end up seeing [b,g,r], for example, you should pass in:
|
70
|
+
```
|
71
|
+
{red: 3, green: 2, blue: 1}
|
72
|
+
```
|
73
|
+
|
74
|
+
#### brightness
|
75
|
+
Sets the strip's default brightness a range of 0-31
|
76
|
+
|
77
|
+
#### spi_hz
|
78
|
+
Controls how fast data is sent through the SPI bus. The default should be fine for most.
|
38
79
|
|
39
|
-
|
80
|
+
#### simulate
|
81
|
+
Set this to `true` if you wish to print your strip to the console rather than to the actual hardware strip. Terminal colors will simulate the strip, allowing you to test out patterns anywhere without needing a PI or a connected LED strip.
|
82
|
+
|
83
|
+
## Multi-strip usage
|
84
|
+
The Pi only has a single SPI bus, so it can technically only communicate with a single, contiguous strip. However you can link multiple strips in a chain using [connectors](http://www.ebay.com/itm/10pcs-10mm-4-Pin-two-Connector-with-Cable-For-SMD-LED-5050-RGB-Strip-Light-/181896959852). To ease development with multiple strips, this library offers a few convenience methods for addressing these. See the example below:
|
85
|
+
```ruby
|
86
|
+
# Assume 2 connected strips parallel to each other:
|
87
|
+
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ====\
|
88
|
+
# [19, 18, 17, 16, 15, 14, 13, 12, 11, 10] ===/
|
89
|
+
|
90
|
+
Apa102Rbpi.configure do |c|
|
91
|
+
c.num_leds = 20
|
92
|
+
end
|
93
|
+
|
94
|
+
entire_strip = Apa102Rbpi.strip
|
95
|
+
top_strip = Apa102Rbpi::Strip.new([0,9])
|
96
|
+
bottom_strip = Apa102Rbpi::Strip.new([10,19],
|
97
|
+
{
|
98
|
+
# lets you set different offsets if your connected strips have different specs
|
99
|
+
led_frame_rgb_offsets: {red: 3, green: 1, blue: 2},
|
100
|
+
brightness: 10
|
101
|
+
})
|
102
|
+
|
103
|
+
# sets pixel 10 red, because the 0th pixel of bottom_strip is at idx 10
|
104
|
+
bottom_strip.set_pixel!(0, 0xff0000)
|
105
|
+
# sets that same pixel blue
|
106
|
+
entire_strip.set_pixel!(10, 0x0000ff)
|
107
|
+
|
108
|
+
# sets both the top and bottom strip to display the same content
|
109
|
+
top_strip.mirror(bottom_strip)
|
110
|
+
top_strip.clear!
|
111
|
+
top_strip.set_pixel!(2, 0x00ff00)
|
112
|
+
# at this point, both pixel 2 and 12 are lit
|
113
|
+
|
114
|
+
top_strip.clear!
|
115
|
+
# reverses the indices of the bottom strip only
|
116
|
+
bottom_strip.reverse
|
117
|
+
top_strip.set_pixel!(2, 0x00ff00)
|
118
|
+
# at this point, pixel 2 and 17 are lit.
|
119
|
+
```
|
40
120
|
|
41
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
42
121
|
|
43
122
|
## Contributing
|
44
123
|
|
data/apa102_rbpi.gemspec
CHANGED
data/lib/apa102_rbpi.rb
CHANGED
@@ -1,6 +1,27 @@
|
|
1
|
+
require 'pi_piper'
|
2
|
+
require 'apa102_rbpi/spi_simulator'
|
3
|
+
require 'apa102_rbpi/strip'
|
1
4
|
require 'apa102_rbpi/apa102'
|
2
5
|
require 'apa102_rbpi/utils'
|
3
6
|
require 'apa102_rbpi/version'
|
4
7
|
|
5
8
|
module Apa102Rbpi
|
9
|
+
def self.configure
|
10
|
+
clear_config!
|
11
|
+
yield base
|
12
|
+
strip.clear!
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.base
|
16
|
+
@base ||= Apa102.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.clear_config!
|
20
|
+
@base = nil
|
21
|
+
@strip = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.strip
|
25
|
+
@strip ||= Strip.new
|
26
|
+
end
|
6
27
|
end
|
data/lib/apa102_rbpi/apa102.rb
CHANGED
@@ -1,77 +1,65 @@
|
|
1
|
-
require 'pi_piper'
|
2
|
-
|
3
1
|
module Apa102Rbpi
|
4
2
|
class Apa102
|
5
|
-
include PiPiper
|
6
|
-
|
7
|
-
START_FRAME = [0x00] * 4
|
8
3
|
|
9
|
-
attr_accessor :brightness
|
10
|
-
attr_reader :num_leds, :
|
4
|
+
attr_accessor :brightness, :spi_hz, :led_frame_rgb_offsets, :led_frames
|
5
|
+
attr_reader :num_leds, :interface, :start_frame, :end_frame, :simulate
|
11
6
|
|
12
|
-
def initialize
|
13
|
-
@num_leds
|
7
|
+
def initialize
|
8
|
+
@num_leds = 1
|
14
9
|
|
15
10
|
# default brightness, must be within 0-31
|
16
|
-
@brightness =
|
17
|
-
@spi_hz = opts[:spi_hz] || 8000000
|
11
|
+
@brightness = 31
|
18
12
|
|
19
13
|
# some strips may have different led frame specs
|
20
14
|
# set offsets if using a non-default strip
|
21
15
|
# offset: 0 1 2 3
|
22
16
|
# frame = [header|blue|green|red],
|
23
|
-
@led_frame_rgb_offsets
|
17
|
+
@led_frame_rgb_offsets = {
|
18
|
+
red: 3,
|
19
|
+
green: 2,
|
20
|
+
blue: 1
|
21
|
+
}
|
22
|
+
@spi_hz = 8000000
|
23
|
+
@simulate = false
|
24
|
+
|
25
|
+
@interface = PiPiper::Spi
|
26
|
+
@start_frame = [0x00] * 4
|
27
|
+
@end_frame = calculate_end_frame
|
24
28
|
|
25
|
-
@led_frames = []
|
26
|
-
@end_frame = [0x00] * (@num_leds / 2.0).ceil
|
27
29
|
|
28
|
-
|
30
|
+
@led_frames = []
|
31
|
+
@substrips = {}
|
32
|
+
@mirrors = {}
|
29
33
|
end
|
30
34
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
if color.is_a?(Integer)
|
36
|
-
@led_frames[idx] = led_frame_hdr(brightness)
|
37
|
-
@led_frames[idx + @led_frame_rgb_offsets[:red]] = (color & 0xFF0000) >> 16
|
38
|
-
@led_frames[idx + @led_frame_rgb_offsets[:green]] = (color & 0x00FF00) >> 8
|
39
|
-
@led_frames[idx + @led_frame_rgb_offsets[:blue]] = (color & 0x0000FF)
|
40
|
-
elsif color.is_a?(Array)
|
41
|
-
@led_frames[idx] = led_frame_hdr(brightness)
|
42
|
-
@led_frames[idx + @led_frame_rgb_offsets[:red]] = color[0]
|
43
|
-
@led_frames[idx + @led_frame_rgb_offsets[:green]] = color[1]
|
44
|
-
@led_frames[idx + @led_frame_rgb_offsets[:blue]] = color[2]
|
35
|
+
def simulate=(bool)
|
36
|
+
@simulate = bool
|
37
|
+
if @simulate
|
38
|
+
@interface = ::Apa102Rbpi::SpiSimulator #TODO
|
45
39
|
else
|
46
|
-
|
40
|
+
@interface = PiPiper::Spi
|
47
41
|
end
|
48
42
|
end
|
49
43
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
44
|
+
def num_leds=(num)
|
45
|
+
@num_leds = num
|
46
|
+
@end_frame = calculate_end_frame
|
53
47
|
end
|
54
48
|
|
55
|
-
def
|
56
|
-
@num_leds
|
57
|
-
set_pixel(l, 0)
|
58
|
-
end
|
59
|
-
show!
|
49
|
+
def calculate_end_frame
|
50
|
+
[0x00] * (@num_leds / 2.0).ceil
|
60
51
|
end
|
61
52
|
|
62
|
-
# Writes out the led frames to the strip
|
63
53
|
def show!
|
64
|
-
|
54
|
+
interface.begin do |s|
|
65
55
|
s.clock(@spi_hz)
|
66
|
-
s.write(
|
56
|
+
s.write(@start_frame + @led_frames + @end_frame)
|
67
57
|
end
|
68
58
|
end
|
69
59
|
|
70
|
-
|
71
|
-
|
72
|
-
# First 3 bits high, then 5 bits for brightness
|
73
|
-
def led_frame_hdr(brightness)
|
74
|
-
(brightness & 0b00011111) | 0b11100000
|
60
|
+
def print
|
61
|
+
::Apa102Rbpi::SpiSimulator.display(@led_frames)
|
75
62
|
end
|
76
63
|
end
|
77
64
|
end
|
65
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'paint'
|
2
|
+
|
3
|
+
module Apa102Rbpi
|
4
|
+
module SpiSimulator
|
5
|
+
class << self
|
6
|
+
def begin
|
7
|
+
yield self
|
8
|
+
end
|
9
|
+
|
10
|
+
# Placeholder method, no-op
|
11
|
+
def clock(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Prints a strip in place
|
15
|
+
def write(data)
|
16
|
+
# strip off header frame bytes
|
17
|
+
base.start_frame.size.times { data.shift }
|
18
|
+
# strip off end frame byes
|
19
|
+
base.end_frame.size.times { data.pop }
|
20
|
+
|
21
|
+
# carriage return at the end lets us update strip display in place
|
22
|
+
print "[#{extract_pixels(data)}]\r"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Prints a strip on its own line
|
26
|
+
def display(led_frames)
|
27
|
+
puts "[#{extract_pixels(led_frames)}]"
|
28
|
+
end
|
29
|
+
|
30
|
+
def extract_pixels(led_frames)
|
31
|
+
pixels = ""
|
32
|
+
(led_frames.size / 4).times do |idx|
|
33
|
+
idx *= 4
|
34
|
+
# frame data is 32 bytes:
|
35
|
+
# first byte is brightness (unused)
|
36
|
+
# next 3 bytes depend on the frame offsets
|
37
|
+
|
38
|
+
r = led_frames[idx + base.led_frame_rgb_offsets[:red]] || 0
|
39
|
+
g = led_frames[idx + base.led_frame_rgb_offsets[:green]] || 0
|
40
|
+
b = led_frames[idx + base.led_frame_rgb_offsets[:blue]] || 0
|
41
|
+
pixels << Paint['•', [r, g, b]]
|
42
|
+
end
|
43
|
+
|
44
|
+
pixels
|
45
|
+
end
|
46
|
+
|
47
|
+
def base
|
48
|
+
Apa102Rbpi.base
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Apa102Rbpi
|
2
|
+
class Strip
|
3
|
+
|
4
|
+
attr_reader :mirrors, :head, :tail, :num_leds
|
5
|
+
attr_accessor :led_frame_rgb_offsets, :brightness
|
6
|
+
|
7
|
+
def initialize(len = base.num_leds, opts = {})
|
8
|
+
if len.is_a?(Array)
|
9
|
+
@head = len[0]
|
10
|
+
@tail = len[1]
|
11
|
+
if @head > @tail
|
12
|
+
raise "Beginning of strip can't be a higher index than end"
|
13
|
+
end
|
14
|
+
@num_leds = (@tail - @head) + 1
|
15
|
+
else
|
16
|
+
@num_leds = len
|
17
|
+
@head = 0
|
18
|
+
@tail = @num_leds - 1
|
19
|
+
end
|
20
|
+
|
21
|
+
@reverse = false
|
22
|
+
|
23
|
+
@led_frame_rgb_offsets = opts[:led_frame_rgb_offsets] || base.led_frame_rgb_offsets
|
24
|
+
@brightness = opts[:brightness] || base.brightness
|
25
|
+
@mirrors = Set.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def reverse
|
29
|
+
@reverse = !@reverse
|
30
|
+
end
|
31
|
+
|
32
|
+
def reversed?
|
33
|
+
@reverse
|
34
|
+
end
|
35
|
+
|
36
|
+
def mirror(strip)
|
37
|
+
if strip.num_leds == @num_leds
|
38
|
+
@mirrors.add(strip)
|
39
|
+
strip.mirrors.add(self)
|
40
|
+
else
|
41
|
+
raise 'Strips must be of same length to be mirrored!'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def show!
|
46
|
+
base.show!
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_pixel(pos, color, brightness = @brightness)
|
50
|
+
q = [self]
|
51
|
+
seen = Set.new([self])
|
52
|
+
frames = base.led_frames
|
53
|
+
while(substrip = q.pop)
|
54
|
+
substrip.mirrors.each do |mirror|
|
55
|
+
unless seen.include?(mirror)
|
56
|
+
q.push(mirror)
|
57
|
+
seen.add(mirror)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
idx = if substrip.reversed?
|
62
|
+
4 * ((substrip.tail - pos) % base.num_leds)
|
63
|
+
else
|
64
|
+
4 * ((pos + substrip.head) % base.num_leds)
|
65
|
+
end
|
66
|
+
|
67
|
+
if color.is_a?(Integer)
|
68
|
+
frames[idx] = substrip.led_frame_hdr(brightness)
|
69
|
+
frames[idx + substrip.led_frame_rgb_offsets[:red]] = (color & 0xFF0000) >> 16
|
70
|
+
frames[idx + substrip.led_frame_rgb_offsets[:green]] = (color & 0x00FF00) >> 8
|
71
|
+
frames[idx + substrip.led_frame_rgb_offsets[:blue]] = (color & 0x0000FF)
|
72
|
+
elsif color.is_a?(Array)
|
73
|
+
frames[idx] = substrip.led_frame_hdr(brightness)
|
74
|
+
frames[idx + substrip.led_frame_rgb_offsets[:red]] = color[0]
|
75
|
+
frames[idx + substrip.led_frame_rgb_offsets[:green]] = color[1]
|
76
|
+
frames[idx + substrip.led_frame_rgb_offsets[:blue]] = color[2]
|
77
|
+
else
|
78
|
+
raise 'Invalid color'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_pixel!(pos, color, brightness = @brightness)
|
84
|
+
set_pixel(pos, color, brightness)
|
85
|
+
show!
|
86
|
+
end
|
87
|
+
|
88
|
+
def clear
|
89
|
+
@num_leds.times do |l|
|
90
|
+
set_pixel(l, 0)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def clear!
|
95
|
+
clear
|
96
|
+
show!
|
97
|
+
end
|
98
|
+
|
99
|
+
def led_frame_hdr(brightness = @brightness)
|
100
|
+
(brightness & 0b00011111) | 0b11100000
|
101
|
+
end
|
102
|
+
|
103
|
+
def base
|
104
|
+
Apa102Rbpi.base
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
data/lib/apa102_rbpi/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apa102_rbpi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- matl33t
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: paint
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description:
|
56
70
|
email:
|
57
71
|
- leung.mattp@gmail.com
|
@@ -60,6 +74,7 @@ extensions: []
|
|
60
74
|
extra_rdoc_files: []
|
61
75
|
files:
|
62
76
|
- ".gitignore"
|
77
|
+
- ".rspec"
|
63
78
|
- CHANGELOG.md
|
64
79
|
- Gemfile
|
65
80
|
- LICENSE.txt
|
@@ -70,6 +85,8 @@ files:
|
|
70
85
|
- bin/setup
|
71
86
|
- lib/apa102_rbpi.rb
|
72
87
|
- lib/apa102_rbpi/apa102.rb
|
88
|
+
- lib/apa102_rbpi/spi_simulator.rb
|
89
|
+
- lib/apa102_rbpi/strip.rb
|
73
90
|
- lib/apa102_rbpi/utils.rb
|
74
91
|
- lib/apa102_rbpi/version.rb
|
75
92
|
homepage:
|
@@ -98,4 +115,3 @@ signing_key:
|
|
98
115
|
specification_version: 4
|
99
116
|
summary: Simple library to drive APA102/Dotstar LEDs using a Raspberry Pi.
|
100
117
|
test_files: []
|
101
|
-
has_rdoc:
|