Browse Source

Button and more

* Add button that toggles active/inactive mode
* Rename hers/his as yang/yin (stop dictating genders)
* Different colors and scan animations for yin&yang
master
The Dod 1 year ago
parent
commit
23d2674a11
5 changed files with 113 additions and 37 deletions
  1. 15
    3
      README.md
  2. BIN
      activation-button.jpg
  3. BIN
      blinkinlove-materix-alone.gif
  4. 95
    31
      blinkinlove-materix.ino
  5. 3
    3
      config-example.h

+ 15
- 3
README.md View File

1
 ### Blinkinlove Materix
1
 ### Blinkinlove Materix
2
 
2
 
3
-_Blinkinlove_ (_אהיבהוב_) is a line of technoromantic jewlery by [zzzen](https://zzzen.com): these accessories come in pairs, and each can blink in a heartbeat rhythm in order to indicate that it can sense that its mate is nearby. The closer the mate is, the faster the blinking. It looks for its mate by performing a wifi or bluetooth scan ()depending on the model).
3
+_Blinkinlove_ (_אהיבהוב_) is a line of technoromantic jewlery by [zzzen](https://zzzen.com): these accessories come in pairs, and each can blink in a heartbeat rhythm in order to indicate that it can sense that its mate is nearby. The closer the mate is, the faster the blinking. It looks for its mate by performing a wifi or bluetooth scan (depending on the model).
4
 
4
 
5
 The _Materix_ model is built on an [Adafruit Feather HUZZAH](https://www.adafruit.com/product/2821) board (with wifi capabilities) with a 4x8 [Neopixel Matrix Featherwing](https://www.adafruit.com/product/2945). For blinking, it uses a heart-shaped Neopixel animation.
5
 The _Materix_ model is built on an [Adafruit Feather HUZZAH](https://www.adafruit.com/product/2821) board (with wifi capabilities) with a 4x8 [Neopixel Matrix Featherwing](https://www.adafruit.com/product/2945). For blinking, it uses a heart-shaped Neopixel animation.
6
 
6
 
7
 ![Blinkinlove Materix when close to mate](blinkinlove-materix.gif)
7
 ![Blinkinlove Materix when close to mate](blinkinlove-materix.gif)
8
 
8
 
9
-Whenever it perfoms a scan, there's a blue indication on the right hand side. When the mate is nearby, this indication is limited to a single pixel, but when the mate can't be found, this becomes a more elaborate "where art thou" animation.
9
+Whenever it perfoms a scan, there's an indication on the right hand side. When the mate is nearby, this indication is limited to a single pixel, but when the mate can't be found, this becomes a more elaborate "where art thou" animation.
10
+
11
+The colors and animations are personalized for each mate, to express that they're connected, but not identical.
12
+
13
+![Blinkinlove Materix desperately seeking mate](blinkinlove-materix-alone.gif)
14
+
15
+### Activation button
16
+![Activation button](activation-button.jpg)
17
+
18
+There's a button that activates/deactivates scan and display functionality (the device always maintains a WiFi access point so that it can be detected by its mate). This saves battery, and also gives the wearer the ability not to be flashing and blinking (handy in many situations).
19
+
20
+After a reset (e.g. when you start charging an empty battery), the materix enters the inactive state (after a vanity scroll of the text "zzzen"), and you'll need to press the button in order to activate it.
21
+
22
+
10
 
23
 
11
-![Blinkinlove Materix desperately seeking mate](blinkinlove-materix-alone.gif)

BIN
activation-button.jpg View File


BIN
blinkinlove-materix-alone.gif View File


+ 95
- 31
blinkinlove-materix.ino View File

1
 /*********
1
 /*********
2
- * Blinkinlove / Materix
3
- * Techno-romantic jewelery based on
4
- * Adafruit Feather HUZZAH boards and
5
- * 4*8 NeoPixel Matrix FeatherWings
2
+   Blinkinlove / Materix
3
+   Techno-romantic jewelery based on
4
+   Adafruit Feather HUZZAH boards and
5
+   4*8 NeoPixel Matrix FeatherWings
6
  *********/
6
  *********/
7
 
7
 
8
+#define DEBUG true
9
+
10
+#define BUTTON_PIN 2
11
+#define PSEUDO_GND 14 // physically close to button. Set to LOW at setup
12
+#define DEBOUNCE_MILLIS 250
13
+unsigned long last_press = 0;
14
+bool is_active = false;
15
+
16
+
8
 #include "config.h"
17
 #include "config.h"
9
 
18
 
10
 #include <Adafruit_NeoPixel.h>
19
 #include <Adafruit_NeoPixel.h>
55
 int current_frame = 0;
64
 int current_frame = 0;
56
 unsigned long last_flip = 0;
65
 unsigned long last_flip = 0;
57
 
66
 
58
-#define NUM_SCAN_PIXELS 4
59
-int scan_pixels[NUM_SCAN_PIXELS] = { 31, 23, 15, 7 };
60
 #define SCAN_FRAME_DURATION 200
67
 #define SCAN_FRAME_DURATION 200
68
+#define SCAN_COMMON_COLOR_YANG strip.Color(16, 32, 64)
69
+#define SCAN_ANIM_COLOR_YANG strip.Color(16, 32, 128)
70
+#define SCAN_COMMON_COLOR_YIN strip.Color(64, 32, 16)
71
+#define SCAN_ANIM_COLOR_YIN strip.Color(128, 32, 16)
72
+
73
+#define NUM_SCAN_ANIM_FRAMES 4
74
+
75
+int scan_yang_common[] = { 31, -1};
76
+int scan_yang_frame_a[] = { 23, -1};
77
+int scan_yang_frame_b[] = { 15, -1};
78
+int scan_yang_frame_c[] = { 7, -1};
79
+int scan_yang_frame_d[] = { -1};
80
+
81
+int *scan_anim_frames_yang[NUM_SCAN_ANIM_FRAMES] = {
82
+  scan_yang_frame_a,
83
+  scan_yang_frame_b,
84
+  scan_yang_frame_c,
85
+  scan_yang_frame_d
86
+};
87
+
88
+int scan_yin_common[] = { 31, -1};
89
+int scan_yin_frame_a[] = { 30, -1};
90
+int scan_yin_frame_b[] = { 22, -1};
91
+int scan_yin_frame_c[] = { 23, -1};
92
+
93
+int *scan_anim_frames_yin[NUM_SCAN_ANIM_FRAMES] = {
94
+  scan_yin_frame_a,
95
+  scan_yin_frame_b,
96
+  scan_yin_frame_c,
97
+  scan_yin_frame_b
98
+};
99
+
61
 
100
 
62
 
101
 
63
 // Zzzen vanity logo (scrolls after reset)
102
 // Zzzen vanity logo (scrolls after reset)
72
 void scrollZzzen(int offs) {
111
 void scrollZzzen(int offs) {
73
   for (int y = 0; y < 4; y++) {
112
   for (int y = 0; y < 4; y++) {
74
     for (int x = 0; x < 8; x++) {
113
     for (int x = 0; x < 8; x++) {
75
-      int xoffs = x+offs, level = 0;
76
-      if (xoffs>=0 && xoffs<24) {
77
-        level = zzzen[24*y+xoffs];
114
+      int xoffs = x + offs, level = 0;
115
+      if (xoffs >= 0 && xoffs < 24) {
116
+        level = zzzen[24 * y + xoffs];
78
       }
117
       }
79
       strip.setPixelColor(8 * y + x, strip.Color(0, 0, level));
118
       strip.setPixelColor(8 * y + x, strip.Color(0, 0, level));
80
     }
119
     }
96
 #define RSSI_FAR -100
135
 #define RSSI_FAR -100
97
 int rssi = 0;
136
 int rssi = 0;
98
 bool scanning = false;
137
 bool scanning = false;
138
+bool is_yang;
99
 
139
 
100
 // set rssi to mate's rssi (0 if not found)
140
 // set rssi to mate's rssi (0 if not found)
101
 void checkScanResult(int numResults) {
141
 void checkScanResult(int numResults) {
146
     current_frame = 0;
186
     current_frame = 0;
147
   }
187
   }
148
   if (scanning) {
188
   if (scanning) {
149
-    if (rssi) {
150
-      strip.setPixelColor(scan_pixels[0], strip.Color(0, 0, 16));
151
-    } else {
152
-      int bright = (nowmillis / SCAN_FRAME_DURATION) % NUM_SCAN_PIXELS;
153
-      for (int i = 0; i < NUM_SCAN_PIXELS; i++) {
154
-        int blueness = i == bright ? 64 : i < bright ? 16 : 0;
155
-        strip.setPixelColor(scan_pixels[i], strip.Color(0, 0, blueness));
189
+    // Show scanning indication
190
+    int *frame = is_yang ? scan_yang_common : scan_yin_common;
191
+    for (int i = 0; frame[i] >= 0; i++) {
192
+      strip.setPixelColor(frame[i], is_yang ? SCAN_COMMON_COLOR_YANG : SCAN_COMMON_COLOR_YIN);
193
+    }
194
+    if (!rssi) {
195
+      int frame_index = (nowmillis / SCAN_FRAME_DURATION) % NUM_SCAN_ANIM_FRAMES;
196
+      frame = is_yang ? scan_anim_frames_yang[frame_index] : scan_anim_frames_yin[frame_index];
197
+      for (int i = 0; frame[i] >= 0; i++) {
198
+        strip.setPixelColor(frame[i], is_yang ? SCAN_ANIM_COLOR_YANG : SCAN_ANIM_COLOR_YIN);
156
       }
199
       }
157
     }
200
     }
158
-    strip.show();
159
   }
201
   }
202
+  strip.show();
160
 }
203
 }
161
 
204
 
162
-
163
 void setup()
205
 void setup()
164
 {
206
 {
165
   Serial.begin(115200);
207
   Serial.begin(115200);
166
   delay(1000);
208
   delay(1000);
167
 
209
 
210
+  pinMode(BUTTON_PIN, INPUT_PULLUP);
211
+  pinMode(PSEUDO_GND, OUTPUT);
212
+  digitalWrite(PSEUDO_GND, LOW);
213
+
168
   strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
214
   strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
169
-  strip.setBrightness(32); // (max = 255)
215
+  strip.setBrightness(16); // (max = 255)
170
   strip.show();            // Turn OFF all pixels ASAP
216
   strip.show();            // Turn OFF all pixels ASAP
171
 
217
 
172
   for (int offs = -23 ; offs < 25; offs++) {
218
   for (int offs = -23 ; offs < 25; offs++) {
173
     scrollZzzen(offs);
219
     scrollZzzen(offs);
174
     delay(100);
220
     delay(100);
175
   }
221
   }
176
-  
222
+
177
   my_mac = WiFi.softAPmacAddress();
223
   my_mac = WiFi.softAPmacAddress();
178
   if (DEBUG) {
224
   if (DEBUG) {
179
     Serial.print("My MAC: ");
225
     Serial.print("My MAC: ");
180
     Serial.println(my_mac);
226
     Serial.println(my_mac);
181
   }
227
   }
182
-  if (my_mac.equals(HERS)) {
183
-    mate_mac = String(HIS);
228
+  if (my_mac.equals(YIN)) {
229
+    is_yang = false;
230
+    mate_mac = String(YANG);
184
   } else {
231
   } else {
185
-    mate_mac = String(HERS);
232
+    is_yang = true;
233
+    mate_mac = String(YIN);
186
   }
234
   }
187
   WiFi.softAP(String("blinkinlove:") + my_mac.substring(12), "whereartthou");
235
   WiFi.softAP(String("blinkinlove:") + my_mac.substring(12), "whereartthou");
188
-
189
 }
236
 }
190
 
237
 
191
 void loop()
238
 void loop()
192
 {
239
 {
193
   unsigned long nowmillis = millis();
240
   unsigned long nowmillis = millis();
194
-  if (nowmillis > next_scan) {
241
+  if ((nowmillis < last_press || nowmillis > last_press + DEBOUNCE_MILLIS) && digitalRead(BUTTON_PIN) == LOW) {
242
+    last_press = nowmillis;
243
+    is_active = !is_active;
195
     if (DEBUG) {
244
     if (DEBUG) {
196
-      Serial.print("Scanning for ");
197
-      Serial.println(mate_mac);
245
+      Serial.print("is_active set to ");
246
+      Serial.println(is_active);
247
+    }
248
+    if (!is_active) {
249
+      for (int i = 0; i<NEOPIXEL_COUNT; i++) {
250
+        strip.setPixelColor(i,strip.Color(0,0,0));
251
+      }
252
+      strip.show();
253
+    }
254
+
255
+  }
256
+  if (is_active) {
257
+    if (nowmillis > next_scan) {
258
+      if (DEBUG) {
259
+        Serial.print("Scanning for ");
260
+        Serial.println(mate_mac);
261
+      }
262
+      scanning = true;
263
+      WiFi.scanNetworksAsync(checkScanResult);
264
+      next_scan = nowmillis + SCAN_INTERVAL;
198
     }
265
     }
199
-    scanning = true;
200
-    WiFi.scanNetworksAsync(checkScanResult);
201
-    next_scan = nowmillis + SCAN_INTERVAL;
266
+    animate(rssi, scanning, nowmillis);
202
   }
267
   }
203
-  animate(rssi, scanning, nowmillis);
204
   delay(20);
268
   delay(20);
205
 }
269
 }

+ 3
- 3
config-example.h View File

3
 // [If you haven't done so already] copy config-example.h to config.h
3
 // [If you haven't done so already] copy config-example.h to config.h
4
 
4
 
5
 // When DEBUG is true, board's MAC address is printed to serial
5
 // When DEBUG is true, board's MAC address is printed to serial
6
-// Once you copy MAC addresses of both mates, use them as HIS/HERS below.
6
+// Once you copy MAC addresses of both mates, use them as YIN/YANG below.
7
 // You can then upload the same code to both mates (each would know to
7
 // You can then upload the same code to both mates (each would know to
8
 // scan for the other one, and not for itself).
8
 // scan for the other one, and not for itself).
9
 
9
 
10
-#define HIS "11:22:33:44:55:66"
11
-#define HERS "ff:ee:dd:cc:bb:aa"
10
+#define YIN "11:22:33:44:55:66"
11
+#define YANG "ff:ee:dd:cc:bb:aa"

Loading…
Cancel
Save