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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- M2U5ZTcxOWE3OGMxYTllMWY1YTMzNTY4MGZjMzJkYzRmNDdiNjk3Yg==
4
+ NmMxYTllNTkyNTllOTk2ZTgwMWQxYTgzZGEwODFkNGI0NjRhMGFiYw==
5
5
  data.tar.gz: !binary |-
6
- MTA2NDAwNjY4YTZjNWM4MjZlNDBkMTcxNjU3NzkxOGEyYzliNDY3Mg==
6
+ MTg3NDhlZGFiZGM4NzBlNzgyN2MyOTU0ODYwOTlkMjAwNmFjMTlkMA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZWUzZTk1YzEwYWMxMzJiMGJiZTU0NjdjMjFiMjRjOTdmOGU2Zjk3NDkwM2U2
10
- MTExY2U4ODdlZDFhYjE4NTNlYTcwMTQ5NWMxOTRiZjY0OWVlMDFhMGRiNjNj
11
- NjYzYTY4YTk4ZmQ4ZDc1OTA2N2IxMjZlOTlmMTRiYzg0ZTU4Y2E=
9
+ ZTUyNDAwYjJiNjI1OWNjYjllYThlMTNmMWVhMGY5NDQwOGJkYTU1MzEyMzg3
10
+ YTFkNGY1OTAyYmU4MmEyYjc1ODcxY2U1Yzc4NjFkZWFhYmE0MzAwZGMxYjUz
11
+ MDdlOTAxNGE1Y2JiMDc0MjE4NGIzYzdiZjEzODZjNzlhOTQ3NWU=
12
12
  data.tar.gz: !binary |-
13
- ZjdjYTNmZGZmYmI3MGE3N2ZmYWUwMDViNWUxMjAzMzlmZmIwYWIyNzFkNDky
14
- ZjM1NDM0N2I2ODllNDhlMmM3YTdhNWExNTMwMzgxZjY3NDlhYWMyYzAxMjA4
15
- YTdjZTRlNzUzZjljM2MxZjAzNmZhMjQwNjEzOGJkZTlmOTUzMWQ=
13
+ ZDM2NTlhMTg4YjQ3YWUxODhiMmFkZDU4MTQ4ZWI1NTdkM2M5OGM5MDI2NGJk
14
+ ZjE2ZjYzMmUxMmUyMjZjMDhmNTNiNzE5MjQ0NzUzOTI3MGViZmUxNjY5NTYy
15
+ NTIzYzI2MjljOWMzOTM2ZGMwY2NiNWM1NGQ4YjIxMDFkNmRkMjM=
@@ -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
- Ruby code to interface with a [BlinkyTape controller board](http://blinkinlabs.myshopify.com/collections/frontpage/products/blinkytape-control-board)
5
- running the custom firmware in the `arduino` directory of this repository. See
6
- the [Blinkinlabs arduino instructions](http://blinkinlabs.com/blinkytape/arduino/)
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.
@@ -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 3000
17
- #define PULSE_DURATION_MED 1500
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 command, if so, hurry things along
80
- if (Serial.available() > 0) {
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 (Serial.available() > 0) {
127
- byte c = Serial.read();
128
- c -= 65; // align to ASCII "A"
129
- switch (c) {
130
- case 32:
131
- brightness = BRIGHTNESS_MIN;
132
- FastLED.setBrightness(brightness);
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 33:
135
- brightness = BRIGHTNESS_MED;
136
- FastLED.setBrightness(brightness);
180
+ case 1:
181
+ hue = HUE_ORANGE;
137
182
  break;
138
- case 34:
139
- brightness = BRIGHTNESS_MAX;
140
- FastLED.setBrightness(brightness);
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
- default:
143
- if (c < 24) { // filter out garbage values
144
- fade(CHANGE_MIN_VAL, CHANGE_DURATION / 2);
145
-
146
- switch (c & B00000011) {
147
- case 0:
148
- pulse = false;
149
- pulse_duration = CHANGE_DURATION;
150
- break;
151
- case 1:
152
- pulse = true;
153
- pulse_duration = PULSE_DURATION_SLOW;
154
- break;
155
- case 2:
156
- pulse = true;
157
- pulse_duration = PULSE_DURATION_MED;
158
- break;
159
- case 3:
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);
@@ -57,7 +57,7 @@ class BlinkyTapeOrb
57
57
 
58
58
  logger.info("updating with color=#{color}, pulse=#{pulse}")
59
59
 
60
- send((65 + (color * 4) + pulse).chr)
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((65 + 32 + brightness).chr)
70
+ send(('a'.ord + brightness).chr)
71
71
  end
72
72
 
73
73
  private
@@ -1,3 +1,3 @@
1
1
  class BlinkyTapeOrb
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
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.1
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