Created
August 5, 2019 15:14
-
-
Save UriShX/2b1f1c7b461b466a4b4ae336d52653dd to your computer and use it in GitHub Desktop.
Fading / breathing LED on a ESP32 dev board, controlled via BLE. Control web app at: https://urishx.github.io/ESP32_fader/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Fading / breathing LED on a ESP32 dev board, controlled via BLE. | |
* Control web app at: https://urishx.github.io/ESP32_fader/. | |
* Features auto ranging and sync via subscription. | |
* Also outputs data to serial monitor. | |
* Written by Uri Shani, May 2019. | |
* MIT licensed (at least my very own original contributions). | |
* | |
* Breathing LED from https://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ | |
* also: https://arduinoelectronics.wordpress.com/ | |
* | |
* esp32 LEDC tutorial: https://techtutorialsx.com/2017/06/15/esp32-arduino-led-pwm-fading/ | |
*/ | |
#include <BLEUtils.h> | |
#include <BLEServer.h> | |
#include <BLEDevice.h> | |
#include <BLEAdvertising.h> | |
#include <BLE2902.h> // BLE notify and indicate properties | |
/** freeRTOS task handles */ | |
TaskHandle_t blinkDelayTask; | |
TaskHandle_t sendBLEdataTask; | |
/** Characteristic for digital output */ | |
BLECharacteristic *pCharacteristicRx; | |
BLECharacteristic *pCharacteristicTx; | |
/** BLE Advertiser */ | |
BLEAdvertising* pAdvertising; | |
/** BLE Service */ | |
BLEService *pService; | |
/** BLE Server */ | |
BLEServer *pServer; | |
bool deviceConnected = false; | |
bool oldDeviceConnected = false; | |
uint8_t txValue = 0; | |
// See the following for generating UUIDs: | |
// https://www.uuidgenerator.net/ | |
#define SERVICE_UUID "48696828-8aba-4445-b1d2-9fe5c3e47382" // UART service UUID | |
#define CHARACTERISTIC_UUID_RX_TX "7dd57463-acc5-48eb-9b7f-3052779322de" | |
unsigned int selection = 7; // default blinking rate | |
const unsigned int _min = 2; // minimal blinking rate | |
const unsigned int _max = 15; // maximum blinking rate | |
// formatting mask for using up to 4 single byte values in 32 bit unsigned int | |
const unsigned int _mask = ((0xff << 24) | (0xff << 16) | (0xff << 8)); | |
unsigned int sendVal = (_max << 24) | (_min << 16) | selection; | |
String inputString = ""; // a String to hold incoming data | |
boolean stringComplete = false; // whether the string is complete | |
void setup() { | |
Serial.begin(115200); // Start serial communication | |
// LED fading task, to run with little to no dependency on the timing of Arduino's loop() | |
xTaskCreatePinnedToCore( | |
blinkLED5, | |
"blinkDelayTask", | |
1024, | |
NULL, | |
1, | |
&blinkDelayTask, | |
1 | |
); | |
delay(500); | |
// BLE communication task | |
xTaskCreatePinnedToCore( | |
sendBLEdata, | |
"sendBLEdataTask", | |
2048, | |
NULL, | |
1, | |
&sendBLEdataTask, | |
1 | |
); | |
delay(500); | |
inputString.reserve(20); // malloc for serial input | |
initBLE(true); | |
Serial.println("\n\nType numbers in the range of 2 to 15 to set delay period. Initial period is 7."); | |
Serial.println(sendVal, BIN); | |
} | |
void loop() { | |
//receive from serial terminal | |
//should be handled in ISR, no simple implementation in Arduino IDE for ESP32 | |
if (Serial.available() > 0) { | |
float i; | |
char inByte = (char)Serial.read(); | |
inputString += inByte; | |
if (inByte == '\n') { | |
stringComplete = true; | |
} | |
} | |
/* Handle strings when newline arrives | |
Arduino terminal adds "\r\n" to each recieved string | |
' ' space | |
'\t' horizontal tab | |
'\n' newline | |
'\v' vertical tab | |
'\f' feed | |
'\r' carriage return | |
*/ | |
if (stringComplete) { | |
if (inputString[0] >= 49 && inputString[0] <= 57) { | |
unsigned int oldSelection = selection; | |
selection = inputString.toInt(); | |
if (selection > _max || selection < _min) { | |
Serial.println("Number out of range! Select numbers in the range of 2 to 15."); | |
selection = oldSelection; | |
} else { | |
Serial.printf("Delay period set to:\t%i\n", selection); | |
sendVal = (sendVal & _mask) ^ selection; | |
} | |
} | |
inputString = ""; | |
stringComplete = false; | |
} | |
} | |
void blinkLED5(void * parameter) { | |
// init(): | |
TickType_t xLastWakeTime; | |
TickType_t xPeriod = pdMS_TO_TICKS(selection); | |
// the number of the LED pin | |
const int ledPin = 5; // 5 corresponds to GPIO5, Lolin32 built-in LED | |
// setting PWM properties | |
const int freq = 5000; | |
const int ledChannel = 0; | |
const int resolution = 8; | |
unsigned int dutyCycle = 255; | |
unsigned int i = 300; | |
xLastWakeTime = xTaskGetTickCount(); | |
// configure LED PWM functionalitites | |
ledcSetup(ledChannel, freq, resolution); | |
// attach the channel to the GPIO to be controlled | |
ledcAttachPin(ledPin, ledChannel); | |
// LED "breathing" loop | |
while(1) { | |
xPeriod = pdMS_TO_TICKS(selection); | |
dutyCycle = 255 - int((exp(sin(i/2000.0*PI*10)) - 0.36787944)*108.492); | |
ledcWrite(ledChannel, dutyCycle); | |
if (i < 699) i++; | |
else i = 300; | |
vTaskDelayUntil(&xLastWakeTime, xPeriod); | |
} | |
} | |
// BLE callback functions | |
class MyServerCallbacks: public BLEServerCallbacks { | |
void onConnect(BLEServer* pServer) { | |
deviceConnected = true; | |
}; | |
void onDisconnect(BLEServer* pServer) { | |
deviceConnected = false; | |
pAdvertising->start(); | |
} | |
}; | |
// recieve breathing rate update over BLE | |
class MyCallbacks: public BLECharacteristicCallbacks { | |
void onWrite(BLECharacteristic *pCharacteristic) { | |
std::string rxValue = pCharacteristic->getValue(); | |
char rxNum = 0; | |
// get value, print it out | |
if (rxValue.length() > 0) { | |
Serial.println("*********"); | |
Serial.print("Received Value: "); | |
for (int i = 0; i < rxValue.length(); i++) { | |
Serial.print(rxValue[i]); | |
rxNum += rxValue[i]; | |
} | |
Serial.printf("\n%x\n", rxNum); | |
Serial.println("*********"); | |
} | |
// check if value is in range | |
if (rxNum > _max || rxNum < _min) { | |
// if value out of range print rejection message to serial monitor | |
Serial.println("Number out of range! Select numbers in the range of 2 to 15."); | |
} else { | |
// if value is in range set global variable (selection) to recieved value | |
selection = rxNum; | |
// format the recieve value for BLE | |
sendVal = (sendVal & _mask) ^ selection; | |
// print acknowledgment message to serial monitor | |
Serial.printf("Delay period set to:\t%d\n", selection); | |
} | |
} | |
}; | |
/** | |
initBLE | |
Initialize BLE service and characteristic | |
Start BLE server and service advertising | |
*/ | |
void initBLE(bool onOff) { | |
Serial.printf("initBLE core %d\n ", xPortGetCoreID()); | |
if (onOff) { | |
if (BLEDevice::getInitialized() == 0) { | |
// Initialize BLE and set output power | |
BLEDevice::init("ESP32 UART Service"); | |
BLEDevice::setPower(ESP_PWR_LVL_P7); | |
// Create BLE Server | |
pServer = BLEDevice::createServer(); | |
// Set server callbacks | |
pServer->setCallbacks(new MyServerCallbacks()); | |
// Create BLE Service | |
pService = pServer->createService(BLEUUID(SERVICE_UUID), 20); | |
// Create BLE Characteristic for reading, writing, and notifying led fading rate | |
pCharacteristicRx = pService->createCharacteristic( | |
BLEUUID(CHARACTERISTIC_UUID_RX_TX), | |
BLECharacteristic::PROPERTY_READ | | |
BLECharacteristic::PROPERTY_WRITE | | |
BLECharacteristic::PROPERTY_NOTIFY | |
); | |
pCharacteristicRx->setCallbacks(new MyCallbacks()); | |
pCharacteristicRx->addDescriptor(new BLE2902()); // notify descriptor | |
} | |
// Start the service | |
pService->start(); | |
// Start advertising | |
pAdvertising = pServer->getAdvertising(); | |
pAdvertising->start(); | |
} else if (!onOff) { | |
// this is a provision for starting and stopping ble services, not fully implemented here | |
pAdvertising->stop(); | |
pService->stop(); | |
BLEDevice::deinit(true); | |
} | |
} | |
// BLE notification task | |
void sendBLEdata(void * parameter) { | |
TickType_t xLastWakeTime; | |
TickType_t xPeriod = pdMS_TO_TICKS(500); | |
xLastWakeTime = xTaskGetTickCount(); | |
while(1) { | |
// if the device is connected via BLE try to send notifications | |
if (deviceConnected) { | |
// format data to be sent | |
pCharacteristicRx->setValue(sendVal); | |
std::string asSet = pCharacteristicRx->getValue(); | |
String setString = ""; | |
Serial.print("set characteristic value as:\t"); | |
for (byte i = 0; i < asSet.length(); i++) { | |
char stringChar = asSet[i]; | |
setString += stringChar; | |
} | |
Serial.println(setString); | |
// test if notifications are enabled by client | |
byte testNotify = *pCharacteristicRx->getDescriptorByUUID((uint16_t)0x2902)->getValue(); | |
// if enabled, send value over BLE | |
if (testNotify == 1) { | |
pCharacteristicRx->notify(); // Send the value to the app! | |
Serial.print("*** Sent Int: \t"); | |
Serial.print(sendVal, DEC); | |
Serial.println(" ***"); | |
} else { | |
// else print failure message to serial monitor | |
Serial.print("notify failed, value of 0x2902 descriptor:\t"); | |
Serial.println(testNotify, HEX);//*pCharacteristicMeas->getDescriptorByUUID((uint16_t)0x2902)->getValue(), HEX); | |
} | |
} | |
// sleep task for 500 ms | |
vTaskDelayUntil(&xLastWakeTime, xPeriod); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment