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.1KB

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