//#include #include //#include #include #include #include // POWER SAVE ATTEMPTS //#include // Methods uint64_t StrToHex(const char* str); void CleanDatabase(); void FillLEDsFromPaletteColors( uint8_t colorIndex); void UpdatePalette(); void Vibrate(); // Bluetooth configuration #define BLE_ACTIVE_SCAN false #define BLE_SCAN_INTERVAL 100 #define BLE_SCAN_TIME 1 // in seconds #define BLE_WINDOW 99 // less or equal setInterval value // LED configuration #define LED_TYPE WS2812B #define LED_DATA_PIN 1 #define LED_NUM_LEDS 7 #define LED_COLOR_ORDER RGB #define LED_BRIGHTNESS 96 #define LED_FRAMES_PER_SECOND 24 #define LED_SETUP_DISPLAY_DELAY 500 #define LED_PALETTE_SIZE 16 // VIBRATION MOTOR configuration #define VIBRO_PIN 2 #define VIBRO_REPEAT 3 #define VIBRO_SMALL_DELAY 100 #define VIBRO_BIG_DELAY 500 // DATA maintenance configuration #define UPDATE_INTERVAL 10000 // how often do we update de database? #define MAX_VANISH_COUNTER 3 // if updateinterval is 10000 => 30s // Number of devices that can be monitored (might cause a reset because of memory oversaturation!) // If you are using more then 16, you have to use CRGBPalette256 instead of CRGBPalette16 // It probably makes sense to align this with LED_NUM_LEDS #define MAX_MONITOR_DEVICES 7 // Minimum transmit power the device needs to be recognice (this defines the min proximity needed for the device to be used) // TODO: add a table here that defines distance in meters for scenarios where the device is in plain sight #define MIN_RSSI_POWER -50 // CONSTS const std::map overrideColors = { {"36E898", "FF0000"}, // MiiBand Klaas }; const std::map blackList = { //{ "36E898", nullptr }, }; #define DEVICE_TYPE_PUBLIC_LAMP 1 #define DEVICE_TYPE_PRIVATE_LAMP 2 #define DEVICE_TYPE_AMULETT 3 #define DEVICE_TYPE DEVICE_TYPE_PUBLIC_LAMP #ifndef DEVICE_TYPE // WE DO NOT HAVE A DEVICETYPE, so we do not have any variables here // to make sure the code compile fails #error "DEVICE_TYPE is not defined" #elif DEVICE_TYPE == DEVICE_TYPE_PUBLIC_LAMP // This is a PUBLIC LAMP, no restrictions here // Does not have a vibration motor static const bool useVibro = false; // WARNING: IF THIS LIST CONTAINS > 0 VALUES, NO OTHER DEVICES WILL BE ALLOWED // IF THIS LIST IS EMPTY IT WILL NOT BE USED static const std::map whiteList = {}; #elif DEVICE_TYPE == DEVICE_TYPE_PRIVATE_LAMP // This is a PRIVATE LAMP, restricted to a list of users // Does not have a vibration motor static const bool useVibro = false; // WARNING: IF THIS LIST CONTAINS > 0 VALUES, NO OTHER DEVICES WILL BE ALLOWED // IF THIS LIST IS EMPTY IT WILL NOT BE USED static const std::map whiteList = { { "36E898", nullptr }, }; #elif DEVICE_TYPE == DEVICE_TYPE_AMULETT // This is an portable device, restricted to a list of users // Comes with a vibration motor to alert wearer static const bool useVibro = true; // WARNING: IF THIS LIST CONTAINS > 0 VALUES, NO OTHER DEVICES WILL BE ALLOWED // IF THIS LIST IS EMPTY IT WILL NOT BE USED static const std::map whiteList = { { "36E898", nullptr }, }; #else #error "Unknown DEVICE_TYPE" #endif // VARIABLES BLEScan *pBLEScan; CRGB leds[LED_NUM_LEDS]; CRGBPalette16 pal; std::map deviceDatabase; std::mutex databaseMutex; std::mutex blendMutex; volatile uint8_t startindex; volatile uint8_t stepSegments; TimerEvent updateTimer; class AdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice advertisedDevice) { if (advertisedDevice.getRSSI() > MIN_RSSI_POWER) { return; } String addr = advertisedDevice.getAddress().toString(); String color = String(addr.substring(9,11)+addr.substring(12,14)+addr.substring(15,17)); color.toUpperCase(); // check if whitelist applies and device is allowed { if (whiteList.size() > 0) { auto it = whiteList.find(color); if (it == whiteList.end()) { return; } } } // check if device is blacklisted { auto it = blackList.find(color); if (it != blackList.end()) { return; } } // check if an override color for the found device exists { auto it = overrideColors.find(color); if (it != overrideColors.end() && !it->second.isEmpty()) { color = it->second; } } // when the device is not in the database yet, vibrate on first connect if (useVibro) { auto it = deviceDatabase.find(color); if (it != deviceDatabase.end()) { Vibrate(); } } if (deviceDatabase.contains(color) || deviceDatabase.size() + 1 < MAX_MONITOR_DEVICES) { deviceDatabase[color] = MAX_VANISH_COUNTER; } Serial.printf("Address: %s RSSI: %d TX Power: %d Calculated color: %s \n", addr.c_str(), advertisedDevice.getRSSI(), advertisedDevice.getTXPower(), color.c_str()); UpdatePalette(); } }; void setup() { Serial.begin(115200); if (useVibro) { pinMode(VIBRO_PIN, OUTPUT); } UpdatePalette(); FastLED.setBrightness(LED_BRIGHTNESS); FastLED.addLeds(leds, LED_NUM_LEDS).setCorrection(TypicalLEDStrip); fill_solid(leds, LED_NUM_LEDS, CRGB::Red ); FastLED.show(); FastLED.delay(LED_SETUP_DISPLAY_DELAY); fill_solid(leds, LED_NUM_LEDS, CRGB::Green ); FastLED.show(); FastLED.delay(LED_SETUP_DISPLAY_DELAY); fill_solid(leds, LED_NUM_LEDS, CRGB::Blue ); FastLED.show(); FastLED.delay(LED_SETUP_DISPLAY_DELAY); fill_solid(leds, LED_NUM_LEDS, CRGB::Black ); FastLED.show(); startindex = 0; updateTimer.set(UPDATE_INTERVAL, CleanDatabase); Serial.println("Scanning..."); BLEDevice::init(""); pBLEScan = BLEDevice::getScan(); //create new scan pBLEScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); pBLEScan->setActiveScan(BLE_ACTIVE_SCAN); //active scan uses more power, but get results faster pBLEScan->setInterval(BLE_SCAN_INTERVAL); pBLEScan->setWindow(BLE_WINDOW); // less or equal setInterval value // POWER SAVING! //WiFi.disconnect(true); // Disconnect from the network //WiFi.mode(WIFI_OFF); } void loop() { updateTimer.update(); { std::lock_guard lock(databaseMutex); BLEScanResults *foundDevices = pBLEScan->start(BLE_SCAN_TIME, false); pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory } FillLEDsFromPaletteColors((startindex++) % LED_PALETTE_SIZE); FastLED.show(); FastLED.delay(1000/LED_FRAMES_PER_SECOND); } // Converts a string to a 64bit HEX value uint64_t StrToHex(const char* str) { return (uint64_t) strtoull(str, 0, 16); } // Cleans the database, updates VANISH_TICKERS void CleanDatabase() { std::lock_guard lock(databaseMutex); std::vector keys; for (auto &itr : deviceDatabase) { Serial.printf("Decrement: %s -> %d\n", itr.first, itr.second); itr.second--; if(itr.second == 0) { keys.push_back(itr.first); } } for (const auto &itr : keys) { deviceDatabase.erase(itr); Serial.printf("Have not seen %s for %d ticks, removing!\n", itr, MAX_VANISH_COUNTER); } if (!keys.empty() || deviceDatabase.size() < 1) { // there are changes to the database, we need to update the palette UpdatePalette(); } } // Renderes Colors from the calculated palette to the led strip void FillLEDsFromPaletteColors( uint8_t colorIndex) { for( int i = 0; i < LED_NUM_LEDS; ++i) { leds[i] = pal[i+colorIndex]; } } // Updates the palette using module to create a repeated fillpatern of all detected devices using the lower 24bits of the btle address void UpdatePalette() { std::lock_guard lock(blendMutex); std::vector colors; int numColors = deviceDatabase.size(); int pos = 0 ; for (auto itr: deviceDatabase) { colors.push_back(itr.first); } if (colors.empty()) { fill_solid(pal.entries, LED_PALETTE_SIZE, CRGB::Black); } else { for (int i=0; i