blinkytape-orb 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/LICENSE.md +48 -0
- data/README.md +120 -3
- data/arduino/blinkytape_orb.ino +90 -65
- data/hardware/photos/hardware-side.jpg +0 -0
- data/hardware/photos/hardware-top.jpg +0 -0
- data/lib/blinkytape-orb.rb +2 -2
- data/lib/blinkytape-orb/version.rb +1 -1
- metadata +4 -3
- data/spec/lib/.blinkytape-orb_spec.rb.swp +0 -0
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NmMxYTllNTkyNTllOTk2ZTgwMWQxYTgzZGEwODFkNGI0NjRhMGFiYw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MTg3NDhlZGFiZGM4NzBlNzgyN2MyOTU0ODYwOTlkMjAwNmFjMTlkMA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTUyNDAwYjJiNjI1OWNjYjllYThlMTNmMWVhMGY5NDQwOGJkYTU1MzEyMzg3
|
10
|
+
YTFkNGY1OTAyYmU4MmEyYjc1ODcxY2U1Yzc4NjFkZWFhYmE0MzAwZGMxYjUz
|
11
|
+
MDdlOTAxNGE1Y2JiMDc0MjE4NGIzYzdiZjEzODZjNzlhOTQ3NWU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZDM2NTlhMTg4YjQ3YWUxODhiMmFkZDU4MTQ4ZWI1NTdkM2M5OGM5MDI2NGJk
|
14
|
+
ZjE2ZjYzMmUxMmUyMjZjMDhmNTNiNzE5MjQ0NzUzOTI3MGViZmUxNjY5NTYy
|
15
|
+
NTIzYzI2MjljOWMzOTM2ZGMwY2NiNWM1NGQ4YjIxMDFkNmRkMjM=
|
data/LICENSE.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Jon Tai
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Hardware initialization and interrupt handling arduino code was copied from
|
26
|
+
https://github.com/Blinkinlabs/BlinkyTape_Arduino, which has the following
|
27
|
+
license:
|
28
|
+
|
29
|
+
The MIT License (MIT)
|
30
|
+
|
31
|
+
Copyright (c) 2013 Blinkinlabs, LLC
|
32
|
+
|
33
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
34
|
+
this software and associated documentation files (the "Software"), to deal in
|
35
|
+
the Software without restriction, including without limitation the rights to
|
36
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
37
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
38
|
+
subject to the following conditions:
|
39
|
+
|
40
|
+
The above copyright notice and this permission notice shall be included in all
|
41
|
+
copies or substantial portions of the Software.
|
42
|
+
|
43
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
44
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
45
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
46
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
47
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
48
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,12 +1,53 @@
|
|
1
1
|
BlinkyTape Orb
|
2
2
|
==============
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
the
|
4
|
+
The code in this repository makes a [BlinkyTape controller board](http://blinkinlabs.myshopify.com/collections/frontpage/products/blinkytape-control-board)
|
5
|
+
act like an ambientdevices Ambient Orb and allows controlling it from Ruby over
|
6
|
+
USB. Controlling the Orb from other languages should be simple—it only requires
|
7
|
+
sending a few ASCII characters over the serial interface.
|
8
|
+
|
9
|
+
Ambient Orbs are great for displaying background information such as stocks,
|
10
|
+
the weather, or whether or not the `master` branch of your repository is
|
11
|
+
passing tests.
|
12
|
+
|
13
|
+
## Custom Firmware for BlinkyTape controller board
|
14
|
+
|
15
|
+
A BlinkyTape has multiple individually-addressable LEDs connected to it. It can
|
16
|
+
recieve commands over the serial interface. The default firmware expects 3
|
17
|
+
bytes per pixel (values for red, green, and blue), followed by a `255` byte to
|
18
|
+
display everything. This protocol is required for compatibility with BlinkyTape
|
19
|
+
software such as [PatternPaint](http://blinkinlabs.com/blinkytape/patternpaint/).
|
20
|
+
|
21
|
+
On the other hand, the Ambient Orb accepts a color and animation, encoded as
|
22
|
+
ASCII. After recieving an update, the Orb continues to animate (pulse) on its
|
23
|
+
own. Like on the BlinkyTape, the button is used to cycle through several
|
24
|
+
brightness levels.
|
25
|
+
|
26
|
+
The `arduino` directory in this repository contains custom firmware for the
|
27
|
+
BlinkyTape that makes it act more like an Ambient Orb. There are some
|
28
|
+
differences:
|
29
|
+
|
30
|
+
* Only 6 colors and 4 pulse speeds are accepted (vs. 37 and 10,
|
31
|
+
respectively)
|
32
|
+
* As a result, the serial protocol is simplified to 1 ASCII character per
|
33
|
+
update
|
34
|
+
* Brightness can be controlled via the serial protocol as well as via the
|
35
|
+
hardware button (it's unclear if the Orb allowed this as well)
|
36
|
+
* Animations were recreated from memory without comparing them side by side
|
37
|
+
with the Orb, so timing and transitions may vary.
|
38
|
+
|
39
|
+
See the [Blinkinlabs arduino instructions](http://blinkinlabs.com/blinkytape/arduino/)
|
7
40
|
for how to install the firmware. The firmware can be made to work on an Arduino
|
8
41
|
Uno R3 with some modifications, mainly around the button handling.
|
9
42
|
|
43
|
+
## Ruby Gem
|
44
|
+
|
45
|
+
The `blinkytape-orb` gem is a thin wrapper around the serial protocol
|
46
|
+
(described below). It also does automatic device detection if you only have one
|
47
|
+
Orb connected.
|
48
|
+
|
49
|
+
Here's how you might use it:
|
50
|
+
|
10
51
|
```ruby
|
11
52
|
require 'blinkytape-orb'
|
12
53
|
|
@@ -43,3 +84,79 @@ sleep(3)
|
|
43
84
|
sleep(10)
|
44
85
|
end
|
45
86
|
```
|
87
|
+
|
88
|
+
# Serial Protocol
|
89
|
+
|
90
|
+
The custom firmware accepts a single ASCII character per update. Regular
|
91
|
+
updates encode both a color and pulse speed. Some characters are set aside to
|
92
|
+
allow controlling brightness.
|
93
|
+
|
94
|
+
## Regular Updates (Color and Pulse Speed)
|
95
|
+
|
96
|
+
The two lowest bits encode the pulse speed:
|
97
|
+
|
98
|
+
* `00` - No pulsing
|
99
|
+
* `01` - Slow pulsing
|
100
|
+
* `10` - Medium pulsing
|
101
|
+
* `11` - Fast pulsing
|
102
|
+
|
103
|
+
The next three bits encode the color:
|
104
|
+
|
105
|
+
* `000` - Red
|
106
|
+
* `001` - Orange
|
107
|
+
* `010` - Yellow
|
108
|
+
* `011` - Green
|
109
|
+
* `100` - Blue
|
110
|
+
* `101` - Purple
|
111
|
+
|
112
|
+
To make the protocol easier to type by hand in a serial monitor, commands are
|
113
|
+
"shifted up" by 65 (ASCII "A"). For example, green and medium pulsing would be
|
114
|
+
encoded as `01110` (14), but this is sent as 79 (65+4), which is "O".
|
115
|
+
|
116
|
+
This encoding can be done easily in Ruby:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
('A'.ord + (color * 4) + pulse).chr
|
120
|
+
```
|
121
|
+
|
122
|
+
In other words, "A" is red with no pulsing, followed by "B" for slow pulsing,
|
123
|
+
"C" for medium pulsing, and "D" for fast pulsing. "E" is orange with no
|
124
|
+
pulsing, "F" is orange with slow pulsing, etc.
|
125
|
+
|
126
|
+
## Brightness Updates
|
127
|
+
|
128
|
+
There are three settings encoded in two bits:
|
129
|
+
|
130
|
+
* `00` - Dim
|
131
|
+
* `01` - Medium
|
132
|
+
* `10` - Bright
|
133
|
+
|
134
|
+
These are "shifted up" by 97 (ASCII "a"). So "a" is dim, "b" is medium, and "c"
|
135
|
+
is bright. The Ruby one-liner for encoding is:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
('a'.ord + brightness).chr
|
139
|
+
```
|
140
|
+
|
141
|
+
# Hardware Setup
|
142
|
+
|
143
|
+
I stuck the [BlinkyTape controller board](http://blinkinlabs.myshopify.com/collections/frontpage/products/blinkytape-control-board)
|
144
|
+
on the plastic base of the Orb with a small styrofoam riser (poor man's 3D
|
145
|
+
printer) and some double-sided tape. The [adafruit NeoPixel 12-LED ring](https://www.adafruit.com/product/1643)
|
146
|
+
easily fits on the plastic base and in the hole in the bottom of the glass part
|
147
|
+
of the orb. The ring rests on slightly higher styrofoam risers, above the
|
148
|
+
controller board. I got the JST connector version of the controller board,
|
149
|
+
snipped off the connector, and soldered the wires directly to the NeoPixel
|
150
|
+
ring. The glass orb simply rests on the plastic base since there isn't anything
|
151
|
+
for the base to screw into anymore. (The wires kind of help keep the base
|
152
|
+
attached to the glass.) I considered getting a NeoPixel 7-LED jewel to sit in
|
153
|
+
the center of the ring, but the 12 LEDs are plenty bright even in daylight.
|
154
|
+
|
155
|
+
![Hardware, Side View](https://raw.githubusercontent.com/jtai/blinkytape-orb/master/hardware/photos/hardware-side.jpg)
|
156
|
+
![Hardware, Top View](https://raw.githubusercontent.com/jtai/blinkytape-orb/master/hardware/photos/hardware-top.jpg)
|
157
|
+
|
158
|
+
adafruit makes NeoPixel LEDs in a variety of form factors. I built another
|
159
|
+
"Orb" using the [8-LED strip](https://www.adafruit.com/products/1426) that has
|
160
|
+
no enclosure—it is just attached to the underside of my monitor and lights up
|
161
|
+
the monitor stand. The setup is relatively cheap, easy to assemble, and small
|
162
|
+
enough to install just about anywhere.
|
data/arduino/blinkytape_orb.ino
CHANGED
@@ -13,8 +13,8 @@
|
|
13
13
|
// visuals
|
14
14
|
#define PULSE_MAX_VAL 255
|
15
15
|
#define PULSE_MIN_VAL 160
|
16
|
-
#define PULSE_DURATION_SLOW
|
17
|
-
#define PULSE_DURATION_MED
|
16
|
+
#define PULSE_DURATION_SLOW 6000
|
17
|
+
#define PULSE_DURATION_MED 3000
|
18
18
|
#define PULSE_DURATION_FAST 0
|
19
19
|
|
20
20
|
#define CHANGE_MIN_VAL 64
|
@@ -27,6 +27,8 @@
|
|
27
27
|
// state
|
28
28
|
CRGB leds[NUM_LEDS];
|
29
29
|
|
30
|
+
byte prev_command;
|
31
|
+
byte command;
|
30
32
|
bool initialized;
|
31
33
|
|
32
34
|
uint8_t hue;
|
@@ -38,10 +40,46 @@ bool buttonDebounced;
|
|
38
40
|
uint8_t brightness;
|
39
41
|
|
40
42
|
|
43
|
+
void checkSerial() {
|
44
|
+
if (Serial.available() > 0) {
|
45
|
+
command = Serial.read();
|
46
|
+
}
|
47
|
+
|
48
|
+
// handle brightness changes immediately
|
49
|
+
// if command was a brightness change or an invalid command,
|
50
|
+
// swallow it to prevent rushing through the next fade
|
51
|
+
switch (command) {
|
52
|
+
case 97:
|
53
|
+
brightness = BRIGHTNESS_MIN;
|
54
|
+
FastLED.setBrightness(brightness);
|
55
|
+
command = prev_command;
|
56
|
+
break;
|
57
|
+
case 98:
|
58
|
+
brightness = BRIGHTNESS_MED;
|
59
|
+
FastLED.setBrightness(brightness);
|
60
|
+
command = prev_command;
|
61
|
+
break;
|
62
|
+
case 99:
|
63
|
+
brightness = BRIGHTNESS_MAX;
|
64
|
+
FastLED.setBrightness(brightness);
|
65
|
+
command = prev_command;
|
66
|
+
break;
|
67
|
+
default:
|
68
|
+
if (command - 65 >= 24) { // swallow garbage values
|
69
|
+
command = prev_command;
|
70
|
+
}
|
71
|
+
break;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
41
75
|
void fade(uint8_t new_val, long duration) {
|
42
76
|
if (val == new_val) {
|
77
|
+
checkSerial();
|
78
|
+
|
43
79
|
// setBrightness() requires a call to delay() periodically
|
80
|
+
// without this line, we can't change the brightness when we're not pulsing
|
44
81
|
FastLED.delay(duration);
|
82
|
+
|
45
83
|
return;
|
46
84
|
}
|
47
85
|
|
@@ -76,8 +114,10 @@ void fade(uint8_t new_val, long duration) {
|
|
76
114
|
}
|
77
115
|
FastLED.show();
|
78
116
|
|
79
|
-
// check to see if we've been given a new
|
80
|
-
if
|
117
|
+
// check to see if we've been given a new hue or pulse command
|
118
|
+
// if so, hurry things along
|
119
|
+
checkSerial();
|
120
|
+
if (command != prev_command) {
|
81
121
|
FastLED.delay(change_delay_ms);
|
82
122
|
} else {
|
83
123
|
FastLED.delay(delay_ms);
|
@@ -93,6 +133,8 @@ void setup() {
|
|
93
133
|
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
|
94
134
|
FastLED.setCorrection(LED_CORRECTION);
|
95
135
|
|
136
|
+
prev_command = 122; // initialize to invalid command so any valid command will trigger action
|
137
|
+
command = 122;
|
96
138
|
initialized = false;
|
97
139
|
|
98
140
|
val = PULSE_MAX_VAL;
|
@@ -123,72 +165,55 @@ void loop() {
|
|
123
165
|
fade(PULSE_MIN_VAL, pulse_duration / 2);
|
124
166
|
}
|
125
167
|
|
126
|
-
if (
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
168
|
+
if (command != prev_command) {
|
169
|
+
fade(CHANGE_MIN_VAL, CHANGE_DURATION / 2);
|
170
|
+
|
171
|
+
prev_command = command;
|
172
|
+
initialized = true;
|
173
|
+
|
174
|
+
byte c = command - 65; // align to ASCII "A"
|
175
|
+
|
176
|
+
switch ((c & B00011100) >> 2) {
|
177
|
+
case 0:
|
178
|
+
hue = HUE_RED;
|
133
179
|
break;
|
134
|
-
case
|
135
|
-
|
136
|
-
FastLED.setBrightness(brightness);
|
180
|
+
case 1:
|
181
|
+
hue = HUE_ORANGE;
|
137
182
|
break;
|
138
|
-
case
|
139
|
-
|
140
|
-
|
183
|
+
case 2:
|
184
|
+
hue = HUE_YELLOW;
|
185
|
+
break;
|
186
|
+
case 3:
|
187
|
+
hue = HUE_GREEN;
|
188
|
+
break;
|
189
|
+
case 4:
|
190
|
+
hue = HUE_BLUE;
|
191
|
+
break;
|
192
|
+
case 5:
|
193
|
+
hue = HUE_PURPLE;
|
141
194
|
break;
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
pulse = true;
|
161
|
-
pulse_duration = PULSE_DURATION_FAST;
|
162
|
-
break;
|
163
|
-
}
|
164
|
-
|
165
|
-
switch ((c & B00011100) >> 2) {
|
166
|
-
case 0:
|
167
|
-
hue = HUE_RED;
|
168
|
-
break;
|
169
|
-
case 1:
|
170
|
-
hue = HUE_ORANGE;
|
171
|
-
break;
|
172
|
-
case 2:
|
173
|
-
hue = HUE_YELLOW;
|
174
|
-
break;
|
175
|
-
case 3:
|
176
|
-
hue = HUE_GREEN;
|
177
|
-
break;
|
178
|
-
case 4:
|
179
|
-
hue = HUE_BLUE;
|
180
|
-
break;
|
181
|
-
case 5:
|
182
|
-
hue = HUE_PURPLE;
|
183
|
-
break;
|
184
|
-
}
|
185
|
-
|
186
|
-
initialized = true;
|
187
|
-
|
188
|
-
fade(PULSE_MIN_VAL, CHANGE_DURATION / 2);
|
189
|
-
}
|
195
|
+
}
|
196
|
+
|
197
|
+
switch (c & B00000011) {
|
198
|
+
case 0:
|
199
|
+
pulse = false;
|
200
|
+
pulse_duration = CHANGE_DURATION;
|
201
|
+
break;
|
202
|
+
case 1:
|
203
|
+
pulse = true;
|
204
|
+
pulse_duration = PULSE_DURATION_SLOW;
|
205
|
+
break;
|
206
|
+
case 2:
|
207
|
+
pulse = true;
|
208
|
+
pulse_duration = PULSE_DURATION_MED;
|
209
|
+
break;
|
210
|
+
case 3:
|
211
|
+
pulse = true;
|
212
|
+
pulse_duration = PULSE_DURATION_FAST;
|
190
213
|
break;
|
191
214
|
}
|
215
|
+
|
216
|
+
fade(PULSE_MIN_VAL, CHANGE_DURATION / 2);
|
192
217
|
}
|
193
218
|
|
194
219
|
fade(PULSE_MAX_VAL, pulse_duration / 2);
|
Binary file
|
Binary file
|
data/lib/blinkytape-orb.rb
CHANGED
@@ -57,7 +57,7 @@ class BlinkyTapeOrb
|
|
57
57
|
|
58
58
|
logger.info("updating with color=#{color}, pulse=#{pulse}")
|
59
59
|
|
60
|
-
send((
|
60
|
+
send(('A'.ord + (color * 4) + pulse).chr)
|
61
61
|
end
|
62
62
|
|
63
63
|
def setBrightness(brightness)
|
@@ -67,7 +67,7 @@ class BlinkyTapeOrb
|
|
67
67
|
|
68
68
|
logger.info("setting brightness=#{brightness}")
|
69
69
|
|
70
|
-
send((
|
70
|
+
send(('a'.ord + brightness).chr)
|
71
71
|
end
|
72
72
|
|
73
73
|
private
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blinkytape-orb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Tai
|
@@ -33,12 +33,14 @@ extra_rdoc_files: []
|
|
33
33
|
files:
|
34
34
|
- .gitignore
|
35
35
|
- Gemfile
|
36
|
+
- LICENSE.md
|
36
37
|
- README.md
|
37
38
|
- arduino/blinkytape_orb.ino
|
38
39
|
- blinkytape-orb.gemspec
|
40
|
+
- hardware/photos/hardware-side.jpg
|
41
|
+
- hardware/photos/hardware-top.jpg
|
39
42
|
- lib/blinkytape-orb.rb
|
40
43
|
- lib/blinkytape-orb/version.rb
|
41
|
-
- spec/lib/.blinkytape-orb_spec.rb.swp
|
42
44
|
- spec/lib/blinkytape-orb_spec.rb
|
43
45
|
- spec/spec_helper.rb
|
44
46
|
homepage: https://github.com/jtai/blinkytape-orb
|
@@ -65,6 +67,5 @@ signing_key:
|
|
65
67
|
specification_version: 4
|
66
68
|
summary: Ruby code to interface with BlinkyTape controller board running custom firmware
|
67
69
|
test_files:
|
68
|
-
- spec/lib/.blinkytape-orb_spec.rb.swp
|
69
70
|
- spec/lib/blinkytape-orb_spec.rb
|
70
71
|
- spec/spec_helper.rb
|
Binary file
|