monome_serial 1.0.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.
- data/.gitignore +6 -0
- data/README +27 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/assets/monome_serial_final.png +0 -0
- data/assets/monome_serial_logo.graffle +6528 -0
- data/docs/CONTRIBUTORS +1 -0
- data/docs/LICENSE +22 -0
- data/docs/TODO +79 -0
- data/examples/toggler.rb +6 -0
- data/lib/monome_serial.rb +40 -0
- data/lib/monome_serial/examples/toggle.rb +44 -0
- data/lib/monome_serial/monome_communicator.rb +90 -0
- data/lib/monome_serial/serial_communicator.rb +26 -0
- data/lib/monome_serial/serial_communicator/binary_patterns/fourtyh.rb +54 -0
- data/lib/monome_serial/serial_communicator/binary_patterns/series.rb +283 -0
- data/lib/monome_serial/serial_communicator/communicator.rb +16 -0
- data/lib/monome_serial/serial_communicator/dummy_communicator.rb +30 -0
- data/lib/monome_serial/serial_communicator/real_communicator.rb +97 -0
- data/monome_serial.gemspec +69 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/monome_communicator_spec.rb +84 -0
- data/spec/unit/monome_serial_spec.rb +24 -0
- metadata +100 -0
data/docs/CONTRIBUTORS
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Sam Aaron - samaaron@gmail.com
|
data/docs/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2009 Sam Aaron
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
|
4
|
+
obtaining a copy of this software and associated documentation
|
|
5
|
+
files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use,
|
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the
|
|
9
|
+
Software is furnished to do so, subject to the following
|
|
10
|
+
conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/docs/TODO
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
make some kind of use of these mappings...
|
|
2
|
+
MMAP_COORDS_256 = {
|
|
3
|
+
[0,0] => {
|
|
4
|
+
:top => ["00001111"],
|
|
5
|
+
:bottom => ["11110000"],
|
|
6
|
+
:left => ["00000000"],
|
|
7
|
+
:right => ["11111111"]
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
[15,15] => {
|
|
11
|
+
:top => ["11110000"],
|
|
12
|
+
:bottom => ["00001111"],
|
|
13
|
+
:left => ["11111111"],
|
|
14
|
+
:right => ["00000000"]
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
[7,7] => {
|
|
18
|
+
:top => ["01111000"],
|
|
19
|
+
:bottom => ["10000111"],
|
|
20
|
+
:left => ["01110111"],
|
|
21
|
+
:right => ["10001000"]
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
[2,9] => {
|
|
25
|
+
:top => ["10011101"],
|
|
26
|
+
:bottom => ["01100010"],
|
|
27
|
+
:left => ["00101001"],
|
|
28
|
+
:right => ["11010110"]
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
[10,4] => {
|
|
32
|
+
:top => ["01000101"],
|
|
33
|
+
:bottom => ["10111010"],
|
|
34
|
+
:left => ["10100100"],
|
|
35
|
+
:right => ["01011011"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
PMAP_COORDS_TO_MMAP_COORDS = {
|
|
40
|
+
#pmap #mmap
|
|
41
|
+
:top => :right,
|
|
42
|
+
:bottom => :left,
|
|
43
|
+
:left => :top,
|
|
44
|
+
:right => :bottom
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
MMAP_ROW_256 = {
|
|
48
|
+
[1,255] => {
|
|
49
|
+
#left to right, 11111111
|
|
50
|
+
:right => ["01001110", "11111111"],
|
|
51
|
+
:bottom => ["01011110", "11111111"],
|
|
52
|
+
:left => ["01000001", "11111111"],
|
|
53
|
+
:top => ["01010001", "11111111"]
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
[1,0] => {
|
|
57
|
+
#left to right, 00000000
|
|
58
|
+
:right => ["01001110", "00000000"],
|
|
59
|
+
:bottom => ["01011110", "00000000"],
|
|
60
|
+
:left => ["01000001", "00000000"],
|
|
61
|
+
:top => ["01010001", "00000000"]
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
[1,197] => {
|
|
65
|
+
#left to right, 10100011
|
|
66
|
+
:right => ["01001110", "10100011"],
|
|
67
|
+
:bottom => ["01011110", "11000101"],
|
|
68
|
+
:left => ["01000001", "11000101"],
|
|
69
|
+
:top => ["01010001", "10100011"]
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
[1, 197, 47] => {
|
|
73
|
+
#left to right, 1010001111110100
|
|
74
|
+
:right => ["01101110", "11110100", "10100011"],
|
|
75
|
+
:bottom => ["01111110", "11000101", "00101111"],
|
|
76
|
+
:left => ["01100001", "11000101", "00101111"],
|
|
77
|
+
:top => ["01110001", "11110100", "10100011"]
|
|
78
|
+
}
|
|
79
|
+
}
|
data/examples/toggler.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#require external libraries
|
|
2
|
+
require 'activesupport'
|
|
3
|
+
require 'fcntl'
|
|
4
|
+
|
|
5
|
+
require 'monome_serial/serial_communicator'
|
|
6
|
+
require 'monome_serial/serial_communicator/communicator'
|
|
7
|
+
require 'monome_serial/serial_communicator/dummy_communicator'
|
|
8
|
+
require 'monome_serial/serial_communicator/binary_patterns/series'
|
|
9
|
+
require 'monome_serial/serial_communicator/binary_patterns/fourtyh'
|
|
10
|
+
require 'monome_serial/monome_communicator'
|
|
11
|
+
require 'monome_serial/examples/toggle'
|
|
12
|
+
|
|
13
|
+
module MonomeSerial
|
|
14
|
+
class NoMonomesFoundError < StandardError ; end
|
|
15
|
+
|
|
16
|
+
def self.detect_monome
|
|
17
|
+
detect_monomes.first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.detect_monomes
|
|
21
|
+
find_ttys.map{|tty| MonomeCommunicator.new(tty)}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.find_ttys
|
|
25
|
+
possible_monome_io_file_matchers = ['/dev/ttyUSB*', '/dev/tty.usbserial-m*']
|
|
26
|
+
files = possible_monome_io_file_matchers.inject([]) do |files, to_try|
|
|
27
|
+
files << Dir[to_try]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
files.flatten!
|
|
31
|
+
|
|
32
|
+
if files.empty? then
|
|
33
|
+
raise NoMonomesFoundError,
|
|
34
|
+
"No monomes were found connected to your computer"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
files
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module MonomeSerial
|
|
2
|
+
module Examples
|
|
3
|
+
class Toggle
|
|
4
|
+
def initialize
|
|
5
|
+
|
|
6
|
+
@monome = MonomeSerial.detect_monome
|
|
7
|
+
@light_lit = Hash.new(false)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Enter a loop in which you read events from the monome and if the
|
|
11
|
+
# event is a :keypress, then toggle the led on the monome
|
|
12
|
+
def start
|
|
13
|
+
Thread.new do
|
|
14
|
+
loop do
|
|
15
|
+
action, x, y = @monome.read
|
|
16
|
+
toggle_led(x,y) if action == :keydown
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def toggle_led(x,y)
|
|
24
|
+
toggle_led_on_monome(x,y)
|
|
25
|
+
toggle_led_status(x,y)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Depending on the state of the @light_lit hash for the given
|
|
29
|
+
# coordinates, extinguish or illuminate the monome's lamp
|
|
30
|
+
def toggle_led_on_monome(x,y)
|
|
31
|
+
if @light_lit[[x,y]]
|
|
32
|
+
@monome.extinguish_lamp(x,y)
|
|
33
|
+
else
|
|
34
|
+
@monome.illuminate_lamp(x,y)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def toggle_led_status(x,y)
|
|
39
|
+
@light_lit[[x,y]] = !@light_lit[[x,y]]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module MonomeSerial
|
|
2
|
+
class MonomeCommunicator
|
|
3
|
+
#It's important to note that the vanilla behaviour of these methods
|
|
4
|
+
#doesn't match that of the OSC protocol specification as implemented
|
|
5
|
+
#by the original OS X MonomeSerial application. In order to match
|
|
6
|
+
#these behviours it is necessary to map the calls appropriately for
|
|
7
|
+
#the given device and rotation. This class just provides raw access
|
|
8
|
+
#to the serial protocol and serves it unadultered.
|
|
9
|
+
|
|
10
|
+
attr_reader :communicator, :serial, :protocol
|
|
11
|
+
|
|
12
|
+
def initialize(tty_path, protocol="series")
|
|
13
|
+
raise ArgumentError, "Unexpected protocol type: #{protocol}. Expected 40h or series" unless protocol == "40h" || protocol == "series"
|
|
14
|
+
|
|
15
|
+
@protocol = protocol
|
|
16
|
+
|
|
17
|
+
#try to pull out serial for the monome represented by
|
|
18
|
+
#this tty_path
|
|
19
|
+
match = tty_path.match /m(\d+h?)-(\d+)/
|
|
20
|
+
@serial = match ? match[2] : "Serial Unknown"
|
|
21
|
+
|
|
22
|
+
#get communicator (will return a DummyCommunicator if the path
|
|
23
|
+
#isn't correct)
|
|
24
|
+
@communicator = SerialCommunicator.get_communicator(tty_path)
|
|
25
|
+
|
|
26
|
+
#include the correct binary patterns
|
|
27
|
+
if @protocol == "40h"
|
|
28
|
+
extend SerialCommunicator::BinaryPatterns::Fourtyh
|
|
29
|
+
else
|
|
30
|
+
extend SerialCommunicator::BinaryPatterns::Series
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def illuminate_lamp(x,y)
|
|
35
|
+
@communicator.write([led_on_pattern, x_y_coord_pattern(x,y)])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def extinguish_lamp(x,y)
|
|
39
|
+
@communicator.write([led_off_pattern, x_y_coord_pattern(x,y)])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def illuminate_row(row, pattern)
|
|
43
|
+
case pattern.size
|
|
44
|
+
when 8 then
|
|
45
|
+
@communicator.write([row_of_8_pattern(row), pattern])
|
|
46
|
+
when 16 then
|
|
47
|
+
@communicator.write([row_of_16_pattern(row), pattern[0..7], pattern[8..15]])
|
|
48
|
+
else
|
|
49
|
+
raise ArgumentError, "Incorrect length of pattern sent to MonomeSerial::Monome#illumninate_row. Expected 8 or 16, got #{pattern.size}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def illuminate_column(col, pattern)
|
|
54
|
+
case pattern.size
|
|
55
|
+
when 8 then
|
|
56
|
+
@communicator.write([col_of_8_pattern(col), pattern])
|
|
57
|
+
when 16 then
|
|
58
|
+
@communicator.write([col_of_16_pattern(col), pattern[0..7], pattern[8..15]])
|
|
59
|
+
else
|
|
60
|
+
raise ArgumentError, "Incorrect length of pattern sent to MonomeSerial::Monome#illumninate_col. Expected 8 or 16, got #{pattern.size}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def illuminate_all
|
|
65
|
+
@communicator.write([all_pattern])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def extinguish_all
|
|
69
|
+
@communicator.write([clear_pattern])
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def illuminate_frame(quadrant, patterns)
|
|
73
|
+
raise ArgumentError, "Incorrect number of patterns sent to MonomeSerial::Monome#illuminate_frame. Expected 8, got #{patterns.size}" unless patterns.size == 8
|
|
74
|
+
reversed_patterns = patterns.map{|pat| pat.reverse}
|
|
75
|
+
@communicator.write([frame_pattern(quadrant), *reversed_patterns])
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def brightness=(intensity)
|
|
79
|
+
@communicator.write([brightness_pattern(intensity)])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def read
|
|
83
|
+
@communicator.read
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def is_real?
|
|
87
|
+
@communicator && @communicator.class != MonomeSerial::SerialCommunicator::DummyCommunicator
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module MonomeSerial
|
|
2
|
+
module SerialCommunicator
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :suppress_warnings
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.get_communicator(tty_path)
|
|
8
|
+
begin
|
|
9
|
+
raise IOError unless File.exists?(tty_path)
|
|
10
|
+
raise RuntimeError unless (RUBY_VERSION.split('.').join.to_i >= 191) && (Object.const_defined?("RUBY_ENGINE") && Object.const_get("RUBY_ENGINE") == "ruby")
|
|
11
|
+
require 'monome_serial/serial_communicator/real_communicator'
|
|
12
|
+
require 'termios'
|
|
13
|
+
return RealCommunicator.new(tty_path)
|
|
14
|
+
rescue IOError
|
|
15
|
+
puts "Supplied path tty IO file isn't valid, loading up DummyCommunicator instead of a real one" unless suppress_warnings
|
|
16
|
+
return DummyCommunicator.new
|
|
17
|
+
rescue RuntimeError
|
|
18
|
+
puts "Incorrect Ruby version (want MRI Ruby 1.9.1 or higher), loading up DummyCommunicator instead of a real one" unless suppress_warnings
|
|
19
|
+
return DummyCommunicator.new
|
|
20
|
+
rescue LoadError
|
|
21
|
+
puts "Could not load the termios extension. Please install it. Loading up DummyCommunicator instead of a real one" unless suppress_warnings
|
|
22
|
+
return DummyCommunicator.new
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module MonomeSerial
|
|
2
|
+
module SerialCommunicator
|
|
3
|
+
module BinaryPatterns
|
|
4
|
+
module Fourtyh
|
|
5
|
+
#please implement me
|
|
6
|
+
#http://docs.monome.org/doku.php?id=tech:protocol:40h
|
|
7
|
+
|
|
8
|
+
def led_on_pattern
|
|
9
|
+
raise NotImplementedError, "please implement me"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def led_off_pattern
|
|
13
|
+
raise NotImplementedError, "please implement me"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def x_y_coord_pattern(x,y)
|
|
17
|
+
raise NotImplementedError, "please implement me"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def row_of_8_pattern(row)
|
|
21
|
+
raise NotImplementedError, "please implement me"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def row_of_16_pattern(row)
|
|
25
|
+
raise NotImplementedError, "please implement me"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def col_of_8_pattern(col)
|
|
29
|
+
raise NotImplementedError, "please implement me"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def col_of_16_pattern(col)
|
|
33
|
+
raise NotImplementedError, "please implement me"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def clear_pattern
|
|
37
|
+
raise NotImplementedError, "please implement me"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def all_pattern
|
|
41
|
+
raise NotImplementedError, "please implement me"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def frame_pattern(quadrant)
|
|
45
|
+
raise NotImplementedError, "please implement me"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def brightness_pattern(brightness)
|
|
49
|
+
raise NotImplementedError, "please implement me"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
module MonomeSerial
|
|
2
|
+
module SerialCommunicator
|
|
3
|
+
module BinaryPatterns
|
|
4
|
+
module Series
|
|
5
|
+
# monmome serial protocol series 256/128/64
|
|
6
|
+
# brian crabtree - tehn@monome.org
|
|
7
|
+
#
|
|
8
|
+
# revision: 070903
|
|
9
|
+
#
|
|
10
|
+
#
|
|
11
|
+
# from device:
|
|
12
|
+
#
|
|
13
|
+
# message id: (0) keydown
|
|
14
|
+
# bytes: 2
|
|
15
|
+
# format: iiii.... xxxxyyyy
|
|
16
|
+
# i (message id) = 0
|
|
17
|
+
# x (x value) = 0-15 (four bits)
|
|
18
|
+
# y (y value) = 0-15 (four bits)
|
|
19
|
+
# decode: id match: byte 0 >> 4 == 0
|
|
20
|
+
# x: byte 1 >> 4
|
|
21
|
+
# y: byte 1 & 0x0f
|
|
22
|
+
#
|
|
23
|
+
#
|
|
24
|
+
# message id: (1) keyup
|
|
25
|
+
# bytes: 2
|
|
26
|
+
# format: iiii.... xxxxyyyy
|
|
27
|
+
# i (message id) = 1
|
|
28
|
+
# x (x value) = 0-15 (four bits)
|
|
29
|
+
# y (y value) = 0-15 (four bits)
|
|
30
|
+
# decode: id match: byte 0 >> 4 == 1
|
|
31
|
+
# x: byte 1 >> 4
|
|
32
|
+
# y: byte 1 & 0x0f
|
|
33
|
+
#
|
|
34
|
+
#
|
|
35
|
+
#
|
|
36
|
+
# to device:
|
|
37
|
+
#
|
|
38
|
+
# message id: (2) led_on
|
|
39
|
+
# bytes: 2
|
|
40
|
+
# format: iiii.... xxxxyyyy
|
|
41
|
+
# i (message id) = 2
|
|
42
|
+
# x (x value) = 0-15 (four bits)
|
|
43
|
+
# y (y value) = 0-15 (four bits)
|
|
44
|
+
# encode: byte 0 = (id) << 4 = 32
|
|
45
|
+
# byte 1 = (x << 4) | y
|
|
46
|
+
#
|
|
47
|
+
#
|
|
48
|
+
# message id: (3) led_off
|
|
49
|
+
# bytes: 2
|
|
50
|
+
# format: iiii.... xxxxyyyy
|
|
51
|
+
# i (message id) = 3
|
|
52
|
+
# x (x value) = 0-15 (four bits)
|
|
53
|
+
# y (y value) = 0-15 (four bits)
|
|
54
|
+
# encode: byte 0 = (id) << 4 = 48
|
|
55
|
+
# byte 1 = (x << 4) | y
|
|
56
|
+
#
|
|
57
|
+
#
|
|
58
|
+
# message id: (4) led_row1
|
|
59
|
+
# bytes: 2
|
|
60
|
+
# format: iiiiyyyy aaaaaaaa
|
|
61
|
+
# i (message id) = 4
|
|
62
|
+
# y (row to update) = 0-15 (4 bits)
|
|
63
|
+
# a (row data 0-7) = 0-255 (8 bits)
|
|
64
|
+
# encode: byte 0 = ((id) << 4) | y = 64 + y
|
|
65
|
+
# byte 1 = a
|
|
66
|
+
#
|
|
67
|
+
#
|
|
68
|
+
# message id: (5) led_col1
|
|
69
|
+
# bytes: 2
|
|
70
|
+
# format: iiiixxxx aaaaaaaa
|
|
71
|
+
# i (message id) = 5
|
|
72
|
+
# x (col to update) = 0-15 (4 bits)
|
|
73
|
+
# a (col data 0-7) = 0-255 (8 bits)
|
|
74
|
+
# encode: byte 0 = ((id) << 4) | x = 80 + x
|
|
75
|
+
# byte 1 = a
|
|
76
|
+
#
|
|
77
|
+
#
|
|
78
|
+
# message id: (6) led_row2
|
|
79
|
+
# bytes: 3
|
|
80
|
+
# format: iiiiyyyy aaaaaaaa bbbbbbbb
|
|
81
|
+
# i (message id) = 6
|
|
82
|
+
# y (row to update) = 0-15 (4 bits)
|
|
83
|
+
# a (row data 0-7) = 0-255 (8 bits)
|
|
84
|
+
# b (row data 8-15) = 0-255 (8 bits)
|
|
85
|
+
# encode: byte 0 = ((id) << 4) | y = 96 + y
|
|
86
|
+
# byte 1 = a
|
|
87
|
+
# byte 2 = b
|
|
88
|
+
#
|
|
89
|
+
#
|
|
90
|
+
# message id: (7) led_col2
|
|
91
|
+
# bytes: 3
|
|
92
|
+
# format: iiiixxxx aaaaaaaa bbbbbbbb
|
|
93
|
+
# i (message id) = 7
|
|
94
|
+
# x (col to update) = 0-15 (4 bits)
|
|
95
|
+
# a (col data 0-7) = 0-255 (8 bits)
|
|
96
|
+
# b (col data 8-15) = 0-255 (8 bits)
|
|
97
|
+
# encode: byte 0 = ((id) << 4) | x = 112 + x
|
|
98
|
+
# byte 1 = a
|
|
99
|
+
# byte 2 = b
|
|
100
|
+
#
|
|
101
|
+
#
|
|
102
|
+
# message id: (8) led_frame
|
|
103
|
+
# bytes: 9
|
|
104
|
+
# format: iiii..qq aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh
|
|
105
|
+
# i (message id) = 8
|
|
106
|
+
# q (quadrant) = 0-3 (2 bits)
|
|
107
|
+
# a-h (row data 0-7, per row) = 0-255 (8 bits)
|
|
108
|
+
# encode: byte 0 = ((id) << 4) | q = 128 + q
|
|
109
|
+
# byte 1,2,3,4,5,6,7,8 = a,b,c,d,e,f,g,h
|
|
110
|
+
# note: quadrants are from top left to bottom right, as shown:
|
|
111
|
+
# 0 1
|
|
112
|
+
# 2 3
|
|
113
|
+
#
|
|
114
|
+
# message id: (9) clear
|
|
115
|
+
# bytes: 1
|
|
116
|
+
# format: iiii---c
|
|
117
|
+
# i (message id) = 9
|
|
118
|
+
# c (clear state) = 0-1 (1 bit)
|
|
119
|
+
# encode: byte 0 = ((id) << 4) | c = 144 + c
|
|
120
|
+
# note: clear state of 0 turns off all leds.
|
|
121
|
+
# clear state of 1 turns on all leds.
|
|
122
|
+
#
|
|
123
|
+
#
|
|
124
|
+
# message id: (10) intensity
|
|
125
|
+
# bytes: 1
|
|
126
|
+
# format: iiiibbbb
|
|
127
|
+
# i (message id) = 10
|
|
128
|
+
# b (brightness) = 0-15 (4 bits)
|
|
129
|
+
# encode: byte 0 = ((id) << 4) | b = 160 + b
|
|
130
|
+
#
|
|
131
|
+
#
|
|
132
|
+
# message id: (11) mode
|
|
133
|
+
# bytes: 1
|
|
134
|
+
# format: iiii..mm
|
|
135
|
+
# i (message id) = 11
|
|
136
|
+
# m (mode) = 0-3 (2 bits)
|
|
137
|
+
# encode: byte 0 = ((id) << 4) | m = 176 + m
|
|
138
|
+
# note: mode = 0 : normal
|
|
139
|
+
# mode = 1 : test (all leds on)
|
|
140
|
+
# mode = 2 : shutdown (all leds off)
|
|
141
|
+
#
|
|
142
|
+
#
|
|
143
|
+
#
|
|
144
|
+
#
|
|
145
|
+
#
|
|
146
|
+
# auxiliary ports
|
|
147
|
+
#
|
|
148
|
+
# to device:
|
|
149
|
+
#
|
|
150
|
+
# message id: (12) activate port
|
|
151
|
+
# bytes: 1
|
|
152
|
+
# format: iiiiaaaa
|
|
153
|
+
# i (message id) = 12
|
|
154
|
+
# a (which port) = 0-15 (four bits)
|
|
155
|
+
# encode: byte 0 = (id) << 4 = 192 + a
|
|
156
|
+
#
|
|
157
|
+
#
|
|
158
|
+
# message id: (13) deactivate port
|
|
159
|
+
# bytes: 1
|
|
160
|
+
# format: iiiiaaaa
|
|
161
|
+
# i (message id) = 13
|
|
162
|
+
# a (which port) = 0-15 (four bits)
|
|
163
|
+
# encode: byte 0 = (id) << 4 = 208 + a
|
|
164
|
+
#
|
|
165
|
+
#
|
|
166
|
+
# from device:
|
|
167
|
+
#
|
|
168
|
+
# message id: (14) auxiliary input
|
|
169
|
+
# bytes: 2
|
|
170
|
+
# format: iiiiaaaa dddddddd
|
|
171
|
+
# i (message id) = 14
|
|
172
|
+
# a (port number) = 0-15 (four bits)
|
|
173
|
+
# d (data) = 0-255 (eight bits)
|
|
174
|
+
# decode: id match: byte 0 >> 4 == 1
|
|
175
|
+
# a: byte 0 & 0x0f
|
|
176
|
+
# d: byte 1
|
|
177
|
+
INT_TO_BIN_STRING = {
|
|
178
|
+
0 => "0000",
|
|
179
|
+
1 => "0001",
|
|
180
|
+
2 => "0010",
|
|
181
|
+
3 => "0011",
|
|
182
|
+
4 => "0100",
|
|
183
|
+
5 => "0101",
|
|
184
|
+
6 => "0110",
|
|
185
|
+
7 => "0111",
|
|
186
|
+
8 => "1000",
|
|
187
|
+
9 => "1001",
|
|
188
|
+
10 => "1010",
|
|
189
|
+
11 => "1011",
|
|
190
|
+
12 => "1100",
|
|
191
|
+
13 => "1101",
|
|
192
|
+
14 => "1110",
|
|
193
|
+
15 => "1111"
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
X_Y_COORD_PATTERNS = (0..15).inject({}) do |hash, x|
|
|
197
|
+
(0..15).each {|y| hash[[x,y]] = INT_TO_BIN_STRING[x] + INT_TO_BIN_STRING[y]}
|
|
198
|
+
hash
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
EIGHT_LIGHT_ROW_PATTTERNS = (0..15).inject({}) do |hash, row|
|
|
202
|
+
hash[row] = "0100" + INT_TO_BIN_STRING[row]
|
|
203
|
+
hash
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
SIXTEEN_LIGHT_ROW_PATTERNS = (0..15).inject({}) do |hash, row|
|
|
207
|
+
hash[row] = "0110" + INT_TO_BIN_STRING[row]
|
|
208
|
+
hash
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
EIGHT_LIGHT_COL_PATTERNS = (0..15).inject({}) do |hash, row|
|
|
213
|
+
hash[row] = "0101" + INT_TO_BIN_STRING[row]
|
|
214
|
+
hash
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
SIXTEEN_LIGHT_COL_PATTERNS = (0..15).inject({}) do |hash, row|
|
|
218
|
+
hash[row] = "0111" + INT_TO_BIN_STRING[row]
|
|
219
|
+
hash
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def led_on_pattern
|
|
223
|
+
"00100000"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def led_off_pattern
|
|
227
|
+
"00110000"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def x_y_coord_pattern(x, y)
|
|
231
|
+
X_Y_COORD_PATTERNS[[x,y]]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def row_of_8_pattern(row)
|
|
235
|
+
EIGHT_LIGHT_ROW_PATTTERNS[row]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def col_of_8_pattern(col)
|
|
239
|
+
EIGHT_LIGHT_COL_PATTERNS[col]
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def row_of_16_pattern(row)
|
|
243
|
+
SIXTEEN_LIGHT_ROW_PATTERNS[row]
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def col_of_16_pattern(col)
|
|
247
|
+
SIXTEEN_LIGHT_COL_PATTERNS[col]
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def clear_pattern
|
|
251
|
+
"10010000"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def all_pattern
|
|
255
|
+
"10010001"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def frame_pattern(quadrant)
|
|
259
|
+
unless quadrant >= 1 && quadrant <= 4 then
|
|
260
|
+
raise ArgumentError,
|
|
261
|
+
"Expecting quadrant to be between 1 and 4 inclusively, got #{quadrant}"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
quadrant_pattern = case quadrant
|
|
265
|
+
when 1 then "00"
|
|
266
|
+
when 2 then "01"
|
|
267
|
+
when 3 then "10"
|
|
268
|
+
when 4 then "11"
|
|
269
|
+
end
|
|
270
|
+
"100000" + quadrant_pattern
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def brightness_pattern(brightness)
|
|
275
|
+
raise ArgumentError, "Expecting a brightness between 0 and 15 inclusively, got #{brightness}" unless brightness >= 0 && brightness <= 15
|
|
276
|
+
|
|
277
|
+
"1010" + INT_TO_BIN_STRING[brightness]
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|