Provide smart lighting to a home using an RGB LED strip and an ESP32. The ESP32 will expose a BLE server that can receive commands from any BLE client. To prove functionality I implemented some simple features such as selecting from a number of preset colors and adjusting brightness.
The NeoPixel LED strip need +5V and GND to power the LED's and the addresing is done using a single data pin, which I have connected to GPIO 17.
Setting up BLE service with 2 Characteristics, one for setting the color and one for setting the brightness. The color receives a string with the desired color (“red”, “blue”, “yellow”, “green”, “purple”, “orange”, “cyan”) and the brightness is a single 8-bit value where 0 is off and 255 is the highest possible brightness.
// BLE server callback class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; }; void onDisconnect(BLEServer* pServer) { deviceConnected = false; } }; /* BLE Characteristics */ BLECharacteristic *colorCharacteristic; class ColorCallback: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *colorCharacteristic) { receivedColor = colorCharacteristic->getValue(); if (receivedColor.length() > 0) { Serial.print("Received Color: "); Serial.println(receivedColor.c_str()); } } }; BLECharacteristic *brightCharacteristic; class BrightnessCallback: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *brightCharacteristic) { receivedBrightness = brightCharacteristic->getValue().c_str()[0]; Serial.print("Received Brightness: "); Serial.println(receivedBrightness, DEC); } }; [ ... ] // Initialize BLE BLEDevice::init("ESP32_LED_Strip"); BLEServer *pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); BLEService *bmeService = pServer->createService(SERVICE_UUID); // Create colorCharacteristic colorCharacteristic = bmeService->createCharacteristic( COLOR_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); colorCharacteristic->setCallbacks(new ColorCallback()); colorCharacteristic->setValue("Hello, World!"); // Create colorCharacteristic brightCharacteristic = bmeService->createCharacteristic( BRIGHTNESS_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); brightCharacteristic->setCallbacks(new BrightnessCallback()); int defaultBrightness = 255; brightCharacteristic->setValue(defaultBrightness); // Start bme service. bmeService->start(); // Start Advertising pServer->getAdvertising()->start(); Serial.println("Waiting a client connection to notify...");
Setting up the LedStrip and control logic is straight-forward. We have global variables holding the current state of the strip that get update on BLECallback triggers. We also have a chgStrip boolean that prevents unneccesary updating of the LedStrip if no changes are currently performed.
// Initialize the NeoPixel library strip.begin(); // Set all pixels to 'off' strip.show(); // Set some default strip values; actualBrightness = 128; actualColor = strip.Color(0, 75, 0); // moderately bright green chgStrip = true; [ ... ] // Check if a device was connected if (deviceConnected) { if (!receivedColor.isEmpty()) { actualColor = getColor(receivedColor); receivedColor = ""; chgStrip = true; } if (receivedBrightness != 0) { actualBrightness = receivedBrightness; receivedBrightness = 0; chgStrip = true; } } // Update Strip. if (chgStrip) { strip.clear(); for(int i=0; i<NUM_PIXELS; i++) { // For each pixel... strip.setPixelColor(i, actualColor); } strip.setBrightness(actualBrightness); strip.show(); // Do not change strip next cycle chgStrip = false; }
Not having proper soldering tools leads to bad electrical contacts and makes it a nightmare to debug any issues. Thankfully, jamming a wire through the ESP32 DevBoard pins worked brilliantly, just don't ask me to move it anywhere.
Initially I wanted to use both BLE and a webpage hosted on the ESP32 itself to allow control of the LED strip from multiple sources but due to memory constraints on the ESP32 I had to choose only one (91% memory usage with the BLE implementation alone).
I find it very annoying how BLE handles characteristics, only having a predefined set of BLEUUID's available to pick and choose from. For example, the was no readily available UUID for brightness or color, just a generic “LED Array” or “Led Strip”.
I choose BLE due to the easy implementaion as baking-in a HTML page in the Arduino IDE seems janky to me. The right way to approach would be by installing a lightweight RTOS such as NuttX or freeRTOS and ditching the BLE for a more customizable webpage, as in our case battery and power constraints are irrelevant.