/********* Blinkinlove / Materix Techno-romantic jewelery based on Adafruit Feather HUZZAH boards and 4*8 NeoPixel Matrix FeatherWings *********/ #define DEBUG true #define BUTTON_PIN 2 #define PSEUDO_GND 14 // physically close to button. Set to LOW at setup #define DEBOUNCE_MILLIS 250 unsigned long last_press = 0; bool is_active = false; #include "config.h" #include // Which pin on the Arduino is connected to the NeoPixels? // Default for the featherwing is 16, but for Huzzah it can't work // 15 is recommended (requires cutting 16 jumper and soldering 15) #define NEOPIXEL_PIN 15 // How many NeoPixels are attached to the Arduino? #define NEOPIXEL_COUNT (4*8) Adafruit_NeoPixel strip(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); #include int8_p heart0[8 * 4] PROGMEM = { 0, 16, 64, 0, 116, 16, 0, 0, 0, 64, 32, 64, 32, 144, 0, 0, 0, 0, 128, 48, 128, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0 }; int8_p heart1[8 * 4] PROGMEM = { 0, 32, 160, 0, 160, 32, 0, 0, 0, 192, 64, 128, 64, 192, 0, 0, 0, 0, 192, 96, 192, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0 }; void drawImage(int8_p *img, int rscale, int gscale, int bscale) { for (int i = 0; i < 8 * 4; i++) { int level = img[i]; strip.setPixelColor(i, strip.Color((level * rscale) / 100, (level * gscale) / 100, (level * bscale) / 100)); } strip.show(); } #define NUM_FRAMES 9 int8_p *frames[NUM_FRAMES] = { heart1, heart1, heart0, heart1, heart1, heart0, heart0, heart0, heart0 }; #define DURATION_FAST 30 #define DURATION_SLOW 300 int current_frame = 0; unsigned long last_flip = 0; #define SCAN_FRAME_DURATION 200 #define SCAN_COMMON_COLOR_YANG strip.Color(16, 0, 64) #define SCAN_ANIM_COLOR_YANG strip.Color(16, 0, 128) #define SCAN_COMMON_COLOR_YIN strip.Color(64, 0, 16) #define SCAN_ANIM_COLOR_YIN strip.Color(128, 0, 16) #define NUM_SCAN_ANIM_FRAMES 4 int scan_yang_common[] = { 31, -1}; int scan_yang_frame_a[] = { 23, -1}; int scan_yang_frame_b[] = { 15, -1}; int scan_yang_frame_c[] = { 7, -1}; int scan_yang_frame_d[] = { -1}; int *scan_anim_frames_yang[NUM_SCAN_ANIM_FRAMES] = { scan_yang_frame_a, scan_yang_frame_b, scan_yang_frame_c, scan_yang_frame_d }; int scan_yin_common[] = { 31, -1}; int scan_yin_frame_a[] = { 30, -1}; int scan_yin_frame_b[] = { 22, -1}; int scan_yin_frame_c[] = { 23, -1}; int *scan_anim_frames_yin[NUM_SCAN_ANIM_FRAMES] = { scan_yin_frame_a, scan_yin_frame_b, scan_yin_frame_c, scan_yin_frame_b }; // Zzzen vanity logo (scrolls after reset) int8_p zzzen[24 * 4] PROGMEM = { 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 255, 128, 128, 255, 0, 255, 255, 0, 255, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 255, 0, 0, 255 }; void scrollZzzen(int offs) { for (int y = 0; y < 4; y++) { for (int x = 0; x < 8; x++) { int xoffs = x + offs, level = 0; if (xoffs >= 0 && xoffs < 24) { level = zzzen[24 * y + xoffs]; } strip.setPixelColor(8 * y + x, strip.Color(0, 0, level)); } } strip.show(); } #include "ESP8266WiFi.h" #define SCAN_INTERVAL 6000 unsigned long next_scan = 0; String my_mac; String mate_mac; #define RSSI_NEAR -20 #define RSSI_FAR -100 int rssi = 0; bool scanning = false; bool is_yang; // set rssi to mate's rssi (0 if not found) void checkScanResult(int numResults) { rssi = 0; if (numResults == 0) { if (DEBUG) { Serial.println("no networks found"); } } else { if (DEBUG) { Serial.print(numResults); Serial.println(" networks found"); } for (int i = 0; i < numResults; ++i) { if (mate_mac.equals(WiFi.BSSIDstr(i))) { rssi = WiFi.RSSI(i); break; } } } if (DEBUG) { Serial.print(mate_mac); if (rssi) { Serial.print(" RSSI: "); Serial.println(rssi); } else { Serial.println(" not found"); } } scanning = false; } // Show heart animation frame according to rssi) // If scanning: indicate that (animation if no rssi, single pixel otherwise) void animate(int rssi, bool scanning, unsigned long nowmillis) { int rscale = is_yang ? 50 : 100, gscale = 0, bscale = is_yang ? 100 : 75; if (rssi) { int duration = constrain( map(rssi, RSSI_NEAR, RSSI_FAR, DURATION_FAST, DURATION_SLOW), DURATION_FAST, DURATION_SLOW); if ((nowmillis - last_flip) > duration || nowmillis < last_flip) { drawImage(frames[current_frame], rscale, gscale, bscale); current_frame = (current_frame + 1) % NUM_FRAMES; last_flip = nowmillis; } } else { drawImage(heart0, rscale, gscale, bscale); current_frame = 0; } if (scanning) { // Show scanning indication int *frame = is_yang ? scan_yang_common : scan_yin_common; for (int i = 0; frame[i] >= 0; i++) { strip.setPixelColor(frame[i], is_yang ? SCAN_COMMON_COLOR_YANG : SCAN_COMMON_COLOR_YIN); } if (!rssi) { int frame_index = (nowmillis / SCAN_FRAME_DURATION) % NUM_SCAN_ANIM_FRAMES; frame = is_yang ? scan_anim_frames_yang[frame_index] : scan_anim_frames_yin[frame_index]; for (int i = 0; frame[i] >= 0; i++) { strip.setPixelColor(frame[i], is_yang ? SCAN_ANIM_COLOR_YANG : SCAN_ANIM_COLOR_YIN); } } } strip.show(); } void setup() { Serial.begin(115200); delay(1000); pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(PSEUDO_GND, OUTPUT); digitalWrite(PSEUDO_GND, LOW); strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) strip.setBrightness(32); // (max = 255) strip.show(); // Turn OFF all pixels ASAP for (int offs = -23 ; offs < 25; offs++) { scrollZzzen(offs); delay(100); } my_mac = WiFi.softAPmacAddress(); if (DEBUG) { Serial.print("My MAC: "); Serial.println(my_mac); } if (my_mac.equals(YIN)) { is_yang = false; mate_mac = String(YANG); } else { is_yang = true; mate_mac = String(YIN); } WiFi.softAP(String("blinkinlove:") + my_mac.substring(12), "whereartthou"); } void loop() { unsigned long nowmillis = millis(); if ((nowmillis < last_press || nowmillis > last_press + DEBOUNCE_MILLIS) && digitalRead(BUTTON_PIN) == LOW) { last_press = nowmillis; is_active = !is_active; if (DEBUG) { Serial.print("is_active set to "); Serial.println(is_active); } if (!is_active) { for (int i = 0; i < NEOPIXEL_COUNT; i++) { strip.setPixelColor(i, strip.Color(0, 0, 0)); } strip.show(); } } if (is_active) { if (nowmillis > next_scan) { if (DEBUG) { Serial.print("Scanning for "); Serial.println(mate_mac); } scanning = true; WiFi.scanNetworksAsync(checkScanResult); next_scan = nowmillis + SCAN_INTERVAL; } animate(rssi, scanning, nowmillis); } delay(20); }