Techno-romantic jewlery based on Adafruit Feather HUZZAH and NeoPixel Matrix Featherwing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

blinkinlove-materix.ino 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*********
  2. Blinkinlove / Materix
  3. Techno-romantic jewelery based on
  4. Adafruit Feather HUZZAH boards and
  5. 4*8 NeoPixel Matrix FeatherWings
  6. *********/
  7. #define DEBUG true
  8. #define BUTTON_PIN 2
  9. #define PSEUDO_GND 14 // physically close to button. Set to LOW at setup
  10. #define DEBOUNCE_MILLIS 250
  11. unsigned long last_press = 0;
  12. bool is_active = false;
  13. #include "config.h"
  14. #include <Adafruit_NeoPixel.h>
  15. // Which pin on the Arduino is connected to the NeoPixels?
  16. // Default for the featherwing is 16, but for Huzzah it can't work
  17. // 15 is recommended (requires cutting 16 jumper and soldering 15)
  18. #define NEOPIXEL_PIN 15
  19. // How many NeoPixels are attached to the Arduino?
  20. #define NEOPIXEL_COUNT (4*8)
  21. Adafruit_NeoPixel strip(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
  22. #include <PGMWrap.h>
  23. int8_p heart0[8 * 4 * 3] PROGMEM = {
  24. 0, 0, 0, 16, 0, 0, 64, 0, 0, 0, 0, 0, 116, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0,
  25. 0, 0, 0, 64, 0, 0, 32, 0, 0, 60, 0, 0, 29, 0, 0, 145, 0, 0, 0, 0, 0, 0, 0, 0,
  26. 0, 0, 0, 0, 0, 0, 125, 1, 1, 48, 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  27. 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  28. };
  29. int8_p heart1[8 * 4 * 3] PROGMEM = {
  30. 0, 0, 0, 32, 0, 0, 160, 0, 0, 0, 0, 0, 185, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0,
  31. 0, 0, 0, 160, 0, 0, 64, 0, 0, 120, 0, 0, 58, 0, 0, 200, 0, 0, 0, 0, 0, 0, 0, 0,
  32. 0, 0, 0, 0, 0, 0, 190, 1, 1, 96, 0, 0, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  33. 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  34. };
  35. void drawImage(int8_p *img) {
  36. for (int i = 0; i < 8 * 4; i++) {
  37. strip.setPixelColor(i, strip.Color(img[i * 3], img[i * 3 + 1], img[i * 3 + 2]));
  38. }
  39. strip.show();
  40. }
  41. #define NUM_FRAMES 9
  42. int8_p *frames[NUM_FRAMES] = {
  43. heart1, heart1, heart0,
  44. heart1, heart1, heart0,
  45. heart0, heart0, heart0
  46. };
  47. #define DURATION_FAST 30
  48. #define DURATION_SLOW 300
  49. int current_frame = 0;
  50. unsigned long last_flip = 0;
  51. #define SCAN_FRAME_DURATION 200
  52. #define SCAN_COMMON_COLOR_YANG strip.Color(16, 32, 64)
  53. #define SCAN_ANIM_COLOR_YANG strip.Color(16, 32, 128)
  54. #define SCAN_COMMON_COLOR_YIN strip.Color(64, 32, 16)
  55. #define SCAN_ANIM_COLOR_YIN strip.Color(128, 32, 16)
  56. #define NUM_SCAN_ANIM_FRAMES 4
  57. int scan_yang_common[] = { 31, -1};
  58. int scan_yang_frame_a[] = { 23, -1};
  59. int scan_yang_frame_b[] = { 15, -1};
  60. int scan_yang_frame_c[] = { 7, -1};
  61. int scan_yang_frame_d[] = { -1};
  62. int *scan_anim_frames_yang[NUM_SCAN_ANIM_FRAMES] = {
  63. scan_yang_frame_a,
  64. scan_yang_frame_b,
  65. scan_yang_frame_c,
  66. scan_yang_frame_d
  67. };
  68. int scan_yin_common[] = { 31, -1};
  69. int scan_yin_frame_a[] = { 30, -1};
  70. int scan_yin_frame_b[] = { 22, -1};
  71. int scan_yin_frame_c[] = { 23, -1};
  72. int *scan_anim_frames_yin[NUM_SCAN_ANIM_FRAMES] = {
  73. scan_yin_frame_a,
  74. scan_yin_frame_b,
  75. scan_yin_frame_c,
  76. scan_yin_frame_b
  77. };
  78. // Zzzen vanity logo (scrolls after reset)
  79. int8_p zzzen[24 * 4] PROGMEM = {
  80. 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 0, 0, 255,
  81. 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 255, 128, 128, 255, 0, 255, 255, 0, 255,
  82. 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 255, 255,
  83. 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 255, 0, 0, 255
  84. };
  85. void scrollZzzen(int offs) {
  86. for (int y = 0; y < 4; y++) {
  87. for (int x = 0; x < 8; x++) {
  88. int xoffs = x + offs, level = 0;
  89. if (xoffs >= 0 && xoffs < 24) {
  90. level = zzzen[24 * y + xoffs];
  91. }
  92. strip.setPixelColor(8 * y + x, strip.Color(0, 0, level));
  93. }
  94. }
  95. strip.show();
  96. }
  97. #include "ESP8266WiFi.h"
  98. #define SCAN_INTERVAL 6000
  99. unsigned long next_scan = 0;
  100. String my_mac;
  101. String mate_mac;
  102. #define RSSI_NEAR -20
  103. #define RSSI_FAR -100
  104. int rssi = 0;
  105. bool scanning = false;
  106. bool is_yang;
  107. // set rssi to mate's rssi (0 if not found)
  108. void checkScanResult(int numResults) {
  109. rssi = 0;
  110. if (numResults == 0) {
  111. if (DEBUG) {
  112. Serial.println("no networks found");
  113. }
  114. } else {
  115. if (DEBUG) {
  116. Serial.print(numResults);
  117. Serial.println(" networks found");
  118. }
  119. for (int i = 0; i < numResults; ++i) {
  120. if (mate_mac.equals(WiFi.BSSIDstr(i))) {
  121. rssi = WiFi.RSSI(i);
  122. break;
  123. }
  124. }
  125. }
  126. if (DEBUG) {
  127. Serial.print(mate_mac);
  128. if (rssi) {
  129. Serial.print(" RSSI: ");
  130. Serial.println(rssi);
  131. } else {
  132. Serial.println(" not found");
  133. }
  134. }
  135. scanning = false;
  136. }
  137. // Show heart animation frame according to rssi)
  138. // If scanning: indicate that (animation if no rssi, single pixel otherwise)
  139. void animate(int rssi, bool scanning, unsigned long nowmillis) {
  140. if (rssi) {
  141. int duration = constrain(
  142. map(rssi, RSSI_NEAR, RSSI_FAR, DURATION_FAST, DURATION_SLOW),
  143. DURATION_FAST, DURATION_SLOW);
  144. if ((nowmillis - last_flip) > duration || nowmillis < last_flip) {
  145. drawImage(frames[current_frame]);
  146. current_frame = (current_frame + 1) % NUM_FRAMES;
  147. last_flip = nowmillis;
  148. }
  149. } else {
  150. drawImage(heart0);
  151. current_frame = 0;
  152. }
  153. if (scanning) {
  154. // Show scanning indication
  155. int *frame = is_yang ? scan_yang_common : scan_yin_common;
  156. for (int i = 0; frame[i] >= 0; i++) {
  157. strip.setPixelColor(frame[i], is_yang ? SCAN_COMMON_COLOR_YANG : SCAN_COMMON_COLOR_YIN);
  158. }
  159. if (!rssi) {
  160. int frame_index = (nowmillis / SCAN_FRAME_DURATION) % NUM_SCAN_ANIM_FRAMES;
  161. frame = is_yang ? scan_anim_frames_yang[frame_index] : scan_anim_frames_yin[frame_index];
  162. for (int i = 0; frame[i] >= 0; i++) {
  163. strip.setPixelColor(frame[i], is_yang ? SCAN_ANIM_COLOR_YANG : SCAN_ANIM_COLOR_YIN);
  164. }
  165. }
  166. }
  167. strip.show();
  168. }
  169. void setup()
  170. {
  171. Serial.begin(115200);
  172. delay(1000);
  173. pinMode(BUTTON_PIN, INPUT_PULLUP);
  174. pinMode(PSEUDO_GND, OUTPUT);
  175. digitalWrite(PSEUDO_GND, LOW);
  176. strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  177. strip.setBrightness(16); // (max = 255)
  178. strip.show(); // Turn OFF all pixels ASAP
  179. for (int offs = -23 ; offs < 25; offs++) {
  180. scrollZzzen(offs);
  181. delay(100);
  182. }
  183. my_mac = WiFi.softAPmacAddress();
  184. if (DEBUG) {
  185. Serial.print("My MAC: ");
  186. Serial.println(my_mac);
  187. }
  188. if (my_mac.equals(YIN)) {
  189. is_yang = false;
  190. mate_mac = String(YANG);
  191. } else {
  192. is_yang = true;
  193. mate_mac = String(YIN);
  194. }
  195. WiFi.softAP(String("blinkinlove:") + my_mac.substring(12), "whereartthou");
  196. }
  197. void loop()
  198. {
  199. unsigned long nowmillis = millis();
  200. if ((nowmillis < last_press || nowmillis > last_press + DEBOUNCE_MILLIS) && digitalRead(BUTTON_PIN) == LOW) {
  201. last_press = nowmillis;
  202. is_active = !is_active;
  203. if (DEBUG) {
  204. Serial.print("is_active set to ");
  205. Serial.println(is_active);
  206. }
  207. if (!is_active) {
  208. for (int i = 0; i<NEOPIXEL_COUNT; i++) {
  209. strip.setPixelColor(i,strip.Color(0,0,0));
  210. }
  211. strip.show();
  212. }
  213. }
  214. if (is_active) {
  215. if (nowmillis > next_scan) {
  216. if (DEBUG) {
  217. Serial.print("Scanning for ");
  218. Serial.println(mate_mac);
  219. }
  220. scanning = true;
  221. WiFi.scanNetworksAsync(checkScanResult);
  222. next_scan = nowmillis + SCAN_INTERVAL;
  223. }
  224. animate(rssi, scanning, nowmillis);
  225. }
  226. delay(20);
  227. }