blinkytape-orb 0.0.1 → 0.0.2
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.
- 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
|
+

|
156
|
+

|
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
|