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,11 +1,23 @@
1 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 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 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,10 +1,19 @@
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 17
 #include "config.h"
9 18
 
10 19
 #include <Adafruit_NeoPixel.h>
@@ -55,9 +64,39 @@ int8_p *frames[NUM_FRAMES] = {
55 64
 int current_frame = 0;
56 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 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 102
 // Zzzen vanity logo (scrolls after reset)
@@ -72,9 +111,9 @@ int8_p zzzen[24 * 4] PROGMEM = {
72 111
 void scrollZzzen(int offs) {
73 112
   for (int y = 0; y < 4; y++) {
74 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 118
       strip.setPixelColor(8 * y + x, strip.Color(0, 0, level));
80 119
     }
@@ -96,6 +135,7 @@ String mate_mac;
96 135
 #define RSSI_FAR -100
97 136
 int rssi = 0;
98 137
 bool scanning = false;
138
+bool is_yang;
99 139
 
100 140
 // set rssi to mate's rssi (0 if not found)
101 141
 void checkScanResult(int numResults) {
@@ -146,60 +186,84 @@ void animate(int rssi, bool scanning, unsigned long nowmillis) {
146 186
     current_frame = 0;
147 187
   }
148 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 205
 void setup()
164 206
 {
165 207
   Serial.begin(115200);
166 208
   delay(1000);
167 209
 
210
+  pinMode(BUTTON_PIN, INPUT_PULLUP);
211
+  pinMode(PSEUDO_GND, OUTPUT);
212
+  digitalWrite(PSEUDO_GND, LOW);
213
+
168 214
   strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
169
-  strip.setBrightness(32); // (max = 255)
215
+  strip.setBrightness(16); // (max = 255)
170 216
   strip.show();            // Turn OFF all pixels ASAP
171 217
 
172 218
   for (int offs = -23 ; offs < 25; offs++) {
173 219
     scrollZzzen(offs);
174 220
     delay(100);
175 221
   }
176
-  
222
+
177 223
   my_mac = WiFi.softAPmacAddress();
178 224
   if (DEBUG) {
179 225
     Serial.print("My MAC: ");
180 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 231
   } else {
185
-    mate_mac = String(HERS);
232
+    is_yang = true;
233
+    mate_mac = String(YIN);
186 234
   }
187 235
   WiFi.softAP(String("blinkinlove:") + my_mac.substring(12), "whereartthou");
188
-
189 236
 }
190 237
 
191 238
 void loop()
192 239
 {
193 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 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 268
   delay(20);
205 269
 }

+ 3
- 3
config-example.h View File

@@ -3,9 +3,9 @@
3 3
 // [If you haven't done so already] copy config-example.h to config.h
4 4
 
5 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 7
 // You can then upload the same code to both mates (each would know to
8 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