dream-cheeky-led 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 95e5a7d14600aa08f271d1d80a0b4974fcbf0663
4
+ data.tar.gz: a7982c55c9e88eed97afb6693004c7bb8721d2d6
5
+ SHA512:
6
+ metadata.gz: ee8d873c3507793b4ba7de6042f9deafd19983dd7ba112cf7f0db0763cc3f2002a70937a9e8cdeda8028179454ec0a14965102f587b06197e5ba565310ec2d9b
7
+ data.tar.gz: 974be5400e861ceb1bae36f524ec9eca88607cdb6c2fc075c27f3e587fc1246366c9cc913f7017cf745ccf01328fc210282ad127a827e8c6d6489034a8297ce5
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - "1.9.3"
5
+ - "2.0.0"
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dream-cheeky-led.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Pete Nicholls
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.
@@ -0,0 +1,69 @@
1
+ # Dream Cheeky LED Message Board
2
+
3
+ [![Very build status](https://travis-ci.org/Aupajo/dream-cheeky-led.png?branch=master)](https://travis-ci.org/Aupajo/dream-cheeky-led)
4
+ [![So Code Climate](https://codeclimate.com/github/Aupajo/dream-cheeky-led.png)](https://codeclimate.com/github/Aupajo/dream-cheeky-led)
5
+
6
+ ![Such art](http://i.imgur.com/CVZJcqd.jpg)
7
+
8
+ Control the [Dream Cheeky LED Message Board](http://www.dreamcheeky.com/led-message-board) by drawing ASCII.
9
+
10
+ For scrolling text, see also:
11
+
12
+ https://github.com/Lewis-Clayton/dcled_ruby
13
+
14
+ ## Installation
15
+
16
+ **This gem is a prerelease version. The API is liable to change.**
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'dream-cheeky-led', '0.0.1.pre'
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install dream-cheeky-led --pre
29
+
30
+ ## Usage
31
+
32
+ ### Drawing with ASCII
33
+
34
+ The message board contains 21 x 7 pixels. Any drawing that fits within those dimensions will work.
35
+
36
+ ```ruby
37
+ require 'dream-cheeky/led'
38
+
39
+ message_board = DreamCheeky::LEDMessageBoard.first
40
+
41
+ art = <<-ART
42
+ * *
43
+ * *
44
+ *
45
+ * *
46
+ * *
47
+ ART
48
+
49
+ message_board.draw(art)
50
+ ```
51
+
52
+ You can use any character that isn't a space to represent a pixel (e.g. `*`, `x`, `o`).
53
+
54
+ The drawing will appear briefly, and disappear. This is a limitation with the device. To persist the drawing to the screen, wrap the call to `draw` in a loop:
55
+
56
+ ```ruby
57
+ loop do
58
+ message_board.draw(art)
59
+ sleep 0.3
60
+ end
61
+ ```
62
+
63
+ ## Contributing
64
+
65
+ 1. Fork it ( http://github.com/aupajo/dream-cheeky-led/fork )
66
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
67
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
68
+ 4. Push to the branch (`git push origin my-new-feature`)
69
+ 5. Create new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dream-cheeky/led/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dream-cheeky-led"
8
+ spec.version = DreamCheeky::LEDMessageBoard::VERSION
9
+ spec.authors = ["Pete Nicholls"]
10
+ spec.email = ["pete@metanation.com"]
11
+ spec.summary = %q{A Dream Cheeky LED Message Board driver in Ruby.}
12
+ spec.description = %q{Control the Dream Cheeky LED Message Board with ASCII art.}
13
+ spec.homepage = "https://github.com/aupajo/dream-cheeky-led"
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"]
20
+
21
+ spec.add_dependency "libusb"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ end
@@ -0,0 +1,8 @@
1
+ require "libusb"
2
+ require "dream-cheeky/led/version"
3
+ require "dream-cheeky/led/message_board"
4
+ require "dream-cheeky/led/device"
5
+ require "dream-cheeky/led/pattern"
6
+ require "dream-cheeky/led/ascii_drawing"
7
+ require "dream-cheeky/led/ascii_table"
8
+ require "dream-cheeky/led/screen_state"
@@ -0,0 +1,70 @@
1
+ module DreamCheeky
2
+ module LEDMessageBoard
3
+ class ASCIIDrawing
4
+ EMPTY_VALUES = [nil, " "]
5
+
6
+ attr_reader :lines
7
+
8
+ def initialize(string, ignore_leading_whitespace: false)
9
+ @lines = string.split("\n")
10
+ @ignore_leading_whitespace = ignore_leading_whitespace
11
+ end
12
+
13
+ def to_pattern
14
+ Pattern.new(rows)
15
+ end
16
+
17
+ def self.to_pattern(string)
18
+ self.new(string).to_pattern
19
+ end
20
+
21
+ protected
22
+
23
+ def lines
24
+ if ignore_leading_whitespace?
25
+ lines_without_leading_whitespace
26
+ else
27
+ @lines
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def max_row_length
34
+ lines.max_by(&:length).length
35
+ end
36
+
37
+ def ignore_leading_whitespace?
38
+ @ignore_leading_whitespace
39
+ end
40
+
41
+ def rows
42
+ lines.map do |line|
43
+ Array.new(max_row_length).map.with_index do |state, index|
44
+ character_state(line[index])
45
+ end
46
+ end
47
+ end
48
+
49
+ def lines_without_leading_whitespace
50
+ @lines.map do |line|
51
+ line[position_of_earliest_char..-1]
52
+ end
53
+ end
54
+
55
+ def position_of_earliest_char
56
+ @lines.map do |line|
57
+ line.index(/[^\s+]/) || 0
58
+ end.min
59
+ end
60
+
61
+ def is_empty?(char)
62
+ EMPTY_VALUES.include?(char)
63
+ end
64
+
65
+ def character_state(char)
66
+ is_empty?(char) ? Pattern::OFF_STATE : Pattern::ON_STATE
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ module DreamCheeky
2
+ module LEDMessageBoard
3
+ class ASCIITable < ASCIIDrawing
4
+
5
+ def initialize(string, ignore_leading_whitespace: false)
6
+ super string, ignore_leading_whitespace: true
7
+ end
8
+
9
+ protected
10
+
11
+ def lines
12
+ lines_without_leading_whitespace[1..-1].map do |line|
13
+ line[2..-1].each_char.each_slice(2).map(&:first).join
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module DreamCheeky
2
+ module LEDMessageBoard
3
+ class Device
4
+ VENDOR_ID = 0x1d34
5
+ PRODUCT_ID = 0x0013
6
+
7
+ def device_handle
8
+ @device_handle ||= open_handle!
9
+ end
10
+
11
+ def send_bytes(bytes)
12
+ device_handle.control_transfer(
13
+ bmRequestType: 0x21,
14
+ bRequest: 0x09,
15
+ wValue: 0x0000,
16
+ wIndex: 0x0000,
17
+ dataOut: bytes.pack('cccccccc')
18
+ )
19
+ end
20
+
21
+ def send_packets(packets)
22
+ packets.each do |bytes|
23
+ send_bytes(bytes)
24
+ end
25
+ end
26
+
27
+ def draw(ascii)
28
+ pattern = Pattern.from_ascii(ascii)
29
+ screen_state = ScreenState.new(pattern.to_a)
30
+ send_packets(screen_state.packets)
31
+ end
32
+
33
+ private
34
+
35
+ def open_handle!
36
+ usb = LIBUSB::Context.new
37
+ device = usb.devices(idVendor: VENDOR_ID, idProduct: PRODUCT_ID).first
38
+
39
+ handle = device.open
40
+ handle.detach_kernel_driver(0) if handle.kernel_driver_active?(0)
41
+ handle
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,7 @@
1
+ module DreamCheeky
2
+ module LEDMessageBoard
3
+ def self.first
4
+ Device.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ module DreamCheeky
2
+ module LEDMessageBoard
3
+ class Pattern
4
+ OFF_STATE = 0
5
+ ON_STATE = 1
6
+
7
+ def initialize(rows)
8
+ @rows = rows
9
+ end
10
+
11
+ def to_a
12
+ @rows
13
+ end
14
+
15
+ def self.from_ascii(*args)
16
+ ASCIIDrawing.new(*args).to_pattern
17
+ end
18
+
19
+ def self.from_ascii_table(*args)
20
+ ASCIITable.new(*args).to_pattern
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,70 @@
1
+ module DreamCheeky
2
+ module LEDMessageBoard
3
+ class ScreenState
4
+ BRIGHTNESS = {
5
+ max: 0,
6
+ mid: 1,
7
+ min: 2
8
+ }
9
+
10
+ WIDTH = 21
11
+ HEIGHT = 7
12
+
13
+ NUM_PACKETS = (HEIGHT.to_f / 2).ceil
14
+ ROWS_PER_PACKET = 2
15
+
16
+ def initialize(rows, brightness: :max)
17
+ @rows = rows
18
+ @brightness = brightness
19
+ end
20
+
21
+ def packets
22
+ Array.new(NUM_PACKETS) do |i|
23
+ packet(i * ROWS_PER_PACKET)
24
+ end
25
+ end
26
+
27
+ def packet(row_start)
28
+ [
29
+ brightness,
30
+ row_start,
31
+ rows_data(row_start)
32
+ ].flatten.compact
33
+ end
34
+
35
+ private
36
+
37
+ def rows_data(row_start)
38
+ Array.new(ROWS_PER_PACKET) do |i|
39
+ row_data(row_start + i)
40
+ end
41
+ end
42
+
43
+ def row_data(row_index)
44
+ row = @rows[row_index] || Array.new(8, 0)
45
+
46
+ row.fill(0, row.length...WIDTH)
47
+
48
+ row.each_slice(8).map do |states|
49
+ byte_data(states)
50
+ end.reverse
51
+ end
52
+
53
+ def byte_data(states)
54
+ byte = 0
55
+
56
+ 0.upto(7) do |i|
57
+ state = states[i] || 0
58
+ byte |= state << i
59
+ end
60
+
61
+ 0xFF - byte
62
+ end
63
+
64
+ def brightness
65
+ BRIGHTNESS[@brightness]
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ module DreamCheeky
2
+ module LEDMessageBoard
3
+ VERSION = "0.0.1.pre"
4
+ end
5
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ module DreamCheeky::LEDMessageBoard
4
+
5
+ describe Pattern do
6
+ describe "#to_a" do
7
+ it "returns an array of rows" do
8
+ pattern = Pattern.new([
9
+ [0, 0, 0, 1],
10
+ [0, 0, 1, 0],
11
+ [0, 1, 0, 0],
12
+ [1, 0, 0, 0]
13
+ ])
14
+
15
+ expect(pattern.to_a).to eq([
16
+ [0, 0, 0, 1],
17
+ [0, 0, 1, 0],
18
+ [0, 1, 0, 0],
19
+ [1, 0, 0, 0]
20
+ ])
21
+ end
22
+ end
23
+
24
+ describe ".from_ascii" do
25
+ it "reads data from an ASCII drawing" do
26
+ pattern = Pattern.from_ascii <<-CROSS
27
+ x x
28
+ x x
29
+ x
30
+ x x
31
+ x x
32
+ CROSS
33
+
34
+ expect(pattern.to_a).to eq([
35
+ [1, 0, 0, 0, 1],
36
+ [0, 1, 0, 1, 0],
37
+ [0, 0, 1, 0, 0],
38
+ [0, 1, 0, 1, 0],
39
+ [1, 0, 0, 0, 1]
40
+ ])
41
+ end
42
+
43
+ it "can ignore leading whitespace" do
44
+ drawing = <<-DRAWING
45
+ o
46
+ o
47
+ o o
48
+ DRAWING
49
+
50
+ pattern = Pattern.from_ascii(drawing, ignore_leading_whitespace: true)
51
+
52
+ expect(pattern.to_a).to eq([
53
+ [1, 0, 0],
54
+ [0, 1, 0],
55
+ [1, 0, 1]
56
+ ])
57
+ end
58
+ end
59
+
60
+ describe ".from_ascii_table" do
61
+ it "reads data from an ASCII table" do
62
+ grid = <<-GRID
63
+ A B C D E
64
+ 1 x
65
+ 2 x x
66
+ 3 x
67
+ GRID
68
+
69
+ pattern = Pattern.from_ascii_table(grid)
70
+
71
+ expect(pattern.to_a).to eq([
72
+ [0, 0, 0, 1, 0],
73
+ [0, 0, 1, 0, 1],
74
+ [0, 1, 0, 0, 0]
75
+ ])
76
+ end
77
+ end
78
+
79
+ it "translates a pattern into bytes" do
80
+ table = <<-TABLE
81
+ A B C D E F G H I J K L M N O P Q R S T U
82
+ 1 x
83
+ 2 x x
84
+ 3 x x
85
+ 4 x x
86
+ 5 x x
87
+ 6 x x
88
+ 7 x
89
+ TABLE
90
+
91
+
92
+ pattern = Pattern.from_ascii_table(table)
93
+ screen = ScreenState.new(pattern.to_a)
94
+
95
+ expect(screen.packets).to eql([
96
+ [0x00, 0x00, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0x7F],
97
+ [0x00, 0x02, 0xFF, 0xFB, 0xBF, 0xFF, 0xF7, 0xDF],
98
+ [0x00, 0x04, 0xFF, 0xFB, 0xBF, 0xFF, 0xFD, 0x7F],
99
+ [0x00, 0x06, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF]
100
+ ])
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,11 @@
1
+ require 'rspec'
2
+ require 'dream-cheeky/led'
3
+
4
+ # Include support files
5
+ support_files_pattern = File.expand_path('../support/**/*.rb', __FILE__)
6
+ Dir.glob(support_files_pattern).each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ # Will be the default in RSpec 3
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dream-cheeky-led
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ platform: ruby
6
+ authors:
7
+ - Pete Nicholls
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: libusb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Control the Dream Cheeky LED Message Board with ASCII art.
70
+ email:
71
+ - pete@metanation.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .travis.yml
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - dream-cheeky-led.gemspec
83
+ - lib/dream-cheeky/led.rb
84
+ - lib/dream-cheeky/led/ascii_drawing.rb
85
+ - lib/dream-cheeky/led/ascii_table.rb
86
+ - lib/dream-cheeky/led/device.rb
87
+ - lib/dream-cheeky/led/message_board.rb
88
+ - lib/dream-cheeky/led/pattern.rb
89
+ - lib/dream-cheeky/led/screen_state.rb
90
+ - lib/dream-cheeky/led/version.rb
91
+ - spec/pattern_spec.rb
92
+ - spec/spec_helper.rb
93
+ homepage: https://github.com/aupajo/dream-cheeky-led
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>'
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.1
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.0.7
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: A Dream Cheeky LED Message Board driver in Ruby.
117
+ test_files:
118
+ - spec/pattern_spec.rb
119
+ - spec/spec_helper.rb
120
+ has_rdoc: