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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5cfb6606ad602a8898f7ec7b89975f748209df1
4
- data.tar.gz: 59ed02cca9b2b04f99b127bfef1137cfd5264a3b
3
+ metadata.gz: 40336d05916f6d9efd8ed00b90b0e0c470bf3f90
4
+ data.tar.gz: cdc426844412559ffd66de9a3c744105c938a766
5
5
  SHA512:
6
- metadata.gz: a95f04120c75c6374e8a76c23023ac2e5dee45db4dd1c116cf1450b0fbf8101d5670b01a338e456855e00ea2e75a00378a4c27f113fe7e9de33e897dded0e705
7
- data.tar.gz: 1ebe65ebaa9162a1466d8cb6cbd6d4206bc7524f00c52e9fe91aef32c92a1a4e6258a4c8fa115b167e75367dddce548afd629c040a8ee3adf879de696e797f75
6
+ metadata.gz: a73f59c02624d41198d4cda1b8cb1250bd8659c2a69c2e5a47b3c5d66930892a296f72ffc0c3c38c1fed6c32b6226b3547c09d061b130ff8693c15cbdfbc2a9b
7
+ data.tar.gz: b9f570c6e1feefa82b0e0eaf2f400016f352fd8a7a244d448c224fa2c0c629c5f6a3a7de685e162090d77496628d6c6503b50381d72eec04fed2bdf8da66e05a
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0
4
+ - Add multistrip support
5
+ - Add simulation support
6
+ - Remake base objects, add cleaner configuration
7
+
3
8
  ## 0.2.0
4
9
  -Set pi_piper as regular dependency
5
10
  -Added some utility functions
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
- ## Installation
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
- $ bundle
15
+ ```
16
+ $ bundle
17
+ ```
16
18
 
17
19
  Or install it yourself as:
18
20
 
19
- $ gem install apa102_rbpi
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
- include Apa102Rbpi
27
-
28
- led = Apa102.new(1)
41
+ my_strip = Apa102Rbpi.strip
29
42
  loop do
30
- led.set_pixel!(0, 0xffffff)
43
+ my_strip.set_pixel!(0, 0xffffff)
31
44
  sleep 1
32
- led.set_pixel!(0, 0)
45
+ my_strip.set_pixel!(0, 0)
33
46
  sleep 1
34
47
  end
35
48
  ```
36
49
 
37
- ## Development
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
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
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
@@ -28,4 +28,5 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'bundler', '~> 1.9'
29
29
  spec.add_development_dependency 'rake', '~> 10.0'
30
30
  spec.add_dependency 'pi_piper', '~> 1.9'
31
+ spec.add_dependency 'paint'
31
32
  end
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
@@ -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, :spi_hz, :led_frame_rgb_offsets
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(num_leds, opts = {})
13
- @num_leds = num_leds || 1
7
+ def initialize
8
+ @num_leds = 1
14
9
 
15
10
  # default brightness, must be within 0-31
16
- @brightness = opts[:brightness] || 31
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 = opts[:led_frame_rgb_offsets] || { red: 3, green: 2, blue: 1 }
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
- clear!
30
+ @led_frames = []
31
+ @substrips = {}
32
+ @mirrors = {}
29
33
  end
30
34
 
31
- # Sets color of a single pixel at specified position in the strip
32
- # accepts an array of [r,g,b] values or a single 3 byte value
33
- def set_pixel(pos, color, brightness = @brightness)
34
- idx = pos * 4
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
- raise 'Invalid color'
40
+ @interface = PiPiper::Spi
47
41
  end
48
42
  end
49
43
 
50
- def set_pixel!(pos, color, brightness = @brightness)
51
- set_pixel(pos, color, brightness)
52
- show!
44
+ def num_leds=(num)
45
+ @num_leds = num
46
+ @end_frame = calculate_end_frame
53
47
  end
54
48
 
55
- def clear!
56
- @num_leds.times do |l|
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
- Spi.begin do |s|
54
+ interface.begin do |s|
65
55
  s.clock(@spi_hz)
66
- s.write(START_FRAME + @led_frames + @end_frame)
56
+ s.write(@start_frame + @led_frames + @end_frame)
67
57
  end
68
58
  end
69
59
 
70
- private
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
+
@@ -1,3 +1,3 @@
1
1
  module Apa102Rbpi
2
- VERSION = '0.2.0'
2
+ VERSION = '1.0.0'
3
3
  end
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.2.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-02-22 00:00:00.000000000 Z
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: