Skip to content

Instantly share code, notes, and snippets.

@Palantir555

Palantir555/ble_client.ino

Last active Nov 11, 2019
Embed
What would you like to do?
/**
* This sketch is the BLE GATT client for a halloween costume.
* For more information on the project, check this post:
* https://jcjc-dev.com/2019/11/11/esp32-arduino-bluetooth-halloween-costume/
*
* Author: Juan Carlos Jimenez Caballero
*
* License: Fully Open Source. Use at your own peril
* This firmware uses code found in Aruino's ESP32 BLE GATT example, and some
* snippets from other unlicensed open source projects
*/
#include "BLEDevice.h"
/* These must be the same as the server's firmware */
#define BLE_STORM_SERVICE_UUID "ca626879-77c5-4f6d-922c-e8fa1a6cb83c"
#define BLE_PROP_CHARACTERISTIC_UUID "08ea4673-8b2b-4394-8390-d6e6e96b4974"
#define USE_AMBIANCE_JUMPER 1
#if USE_AMBIANCE_JUMPER
/* jumper sets if we should create lightning regularly without BLE input */
/* If this jumper is set, the device will emulate thunder and lightning every
X seconds even if there's no BLE input */
#define GPIO_AMBIANCE_JUMPER_PIN 19
#define AMBIANCE_RANDOM_DELAY_MIN 45000
#define AMBIANCE_RANDOM_DELAY_MAX 65000
#endif
#define USE_LEDSTRIP_OUTPUT 1
#if USE_LEDSTRIP_OUTPUT
#define GPIO_LEDSTRIP_PIN 21
#define LEDC_CHANNEL_0 0 /* use first of 16 channels (started from zero) */
#define LEDC_TIMER_13_BIT 13 /* use 13 bit precission for LEDC timer */
#define LEDC_BASE_FREQ 5000 /* use 5000 Hz as a LEDC base frequency */
/* Since the ESP32 does not currently support analogWrite, use our own: */
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255)
{
/* calculate duty, 8191 from 2 ^ 13 - 1 */
uint32_t duty = (8191 / valueMax) * min(value, valueMax);
/* write duty to LEDC */
ledcWrite(channel, duty);
}
#endif
#define USE_AUDIO_OUTPUT 1
#if USE_AUDIO_OUTPUT
#include "DFRobotDFPlayerMini.h"
#include "SoftwareSerial.h"
#define AUDIO_RX_PIN 18 /* DFplayer RX to Arduino */
#define AUDIO_TX_PIN 17 /* DFplayer TX to Arduino */
#define AUDIO_BUSY_PIN 16 /* DFplayer BUSY connected to Arduino */
SoftwareSerial audioSoftwareSerial;
DFRobotDFPlayerMini audioDFPlayer;
constexpr SoftwareSerialConfig audioSwSerialConfig = SWSERIAL_8N1;
#endif
static boolean doLightningShow = true;
/* The remote service we wish to connect to */
static BLEUUID serviceUUID(BLE_STORM_SERVICE_UUID);
/* The characteristic of the remote service we are interested in */
static BLEUUID charUUID(BLE_PROP_CHARACTERISTIC_UUID);
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify)
{
Serial.print("Notify callback for characteristic ");
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
Serial.print("data: ");
uint32_t value = 0;
memcpy(&value, pData, sizeof(uint32_t));
Serial.println(value);
/* All notifications are the same. Value is irrelevant in this project: */
doLightningShow = true;
}
void thunder_play(int filenumber)
{
audioDFPlayer.playMp3Folder(filenumber);
}
void lightning_LED_show(void)
{
int flashCount =
random(15, 35); /* Min. and max. number of flashes each loop */
int flashBrightnessMin = 10; /* LED flash min. brightness (0-255) */
int flashBrightnessMax = 255; /* LED flash max. brightness (0-255) */
int flashDurationMin = 1; /* Min. duration of each seperate flash */
int flashDurationMax = 50; /* Max. duration of each seperate flash */
int nextFlashDelayMin = 1; /* Min, delay between each flash and the next */
int nextFlashDelayMax = 150; /* Max, delay between each flash and the next */
bool pin_val = LOW;
for(int flash = 0; flash <= flashCount; flash += 1) {
/* set the brightness on LEDC channel 0 */
ledcAnalogWrite(LEDC_CHANNEL_0,
random(flashBrightnessMin, flashBrightnessMax));
/* Keep it turned on, random duration */
delay(random(flashDurationMin, flashDurationMax));
ledcAnalogWrite(LEDC_CHANNEL_0, 0);
/* Random delay before next flash */
delay(random(nextFlashDelayMin, nextFlashDelayMax));
}
}
class MyClientCallback : public BLEClientCallbacks
{
void onConnect(BLEClient* pclient)
{
}
void onDisconnect(BLEClient* pclient)
{
connected = false;
Serial.println("onDisconnect");
}
};
bool connectToServer()
{
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
/* Connect to the remove BLE Server */
pClient->connect(myDevice); /* if you pass BLEAdvertisedDevice instead of
* address, it will be recognized type of peer
* device address (public or private) */
Serial.println(" - Connected to server");
/* Obtain a reference to the service we are after in the remote BLE server */
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if(pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
/* Get a reference to the charact in the service of the remote BLE server */
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if(pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
/* Read the value of the characteristic */
if(pRemoteCharacteristic->canRead()) {
std::string value = pRemoteCharacteristic->readValue();
Serial.print("The characteristic value was: ");
Serial.println(value.c_str());
}
if(pRemoteCharacteristic->canNotify())
pRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
return true;
}
/**
* Scan for BLE servers and find the first one that advertises the service we
* are looking for.
*/
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
{
/* Called for each advertising BLE server */
void onResult(BLEAdvertisedDevice advertisedDevice)
{
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
/* Device found. Check if it contains the service we are looking for */
if(advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
}
}
};
void setup()
{
/* Init serial comms with the development computer */
Serial.begin(115200);
/* Init GPIO */
#if USE_AMBIANCE_JUMPER
pinMode(GPIO_AMBIANCE_JUMPER_PIN, INPUT_PULLUP);
#endif
#if USE_LEDSTRIP_OUTPUT
/* Setup timer for PWM control of the LEDs, and attach timer to a led pin */
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(GPIO_LEDSTRIP_PIN, LEDC_CHANNEL_0);
#endif
#if USE_AUDIO_OUTPUT
/* Init MP3-TF-16P module */
audioSoftwareSerial.begin(9600, AUDIO_RX_PIN, AUDIO_TX_PIN,
audioSwSerialConfig);
delay(500); /* Important! On an ESP32, removing this delay causes a failure
to .begin() */
while(!audioDFPlayer.begin(
audioSoftwareSerial)) { /* Use softwareSerial to communicate with mp3 */
Serial.println("Unable to begin. Check connection and SD card, or "
"reset the Arduino.");
delay(500);
}
Serial.println("DFPlayer Mini online.");
audioDFPlayer.setTimeOut(500); /* Set serial communictaion time out 500ms */
audioDFPlayer.volume(30); /* Set volume value (0~30). */
audioDFPlayer.EQ(
DFPLAYER_EQ_BASS); /* Set EQ to BASS (normal/pop/rock/jazz/classic/bass)
*/
audioDFPlayer.outputDevice(
DFPLAYER_DEVICE_SD); /* Set device we use SD as default */
audioDFPlayer.enableDAC(); /* Enable On-chip DAC */
#endif
/* Init Bluetooth stack */
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
/* Retrieve a Scanner and set the callback we want to use to be informed
* when we have detected a new device. Specify that we want active scanning
* and start the scan to run for 5 seconds.
*/
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
}
void loop()
{
static unsigned long last_storm_timestamp_ms = 0;
static unsigned long time_to_next_storm_ms =
random(AMBIANCE_RANDOM_DELAY_MIN, AMBIANCE_RANDOM_DELAY_MAX);
/* If the flag "doConnect" is true then we have scanned for and found the
* desired BLE Server with which we wish to connect. Now we connect to it.
* Once we are connected we set the connected flag to be true.
*/
if(doConnect == true) {
if(connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println(
"We have failed to connect to the server; there is nothin "
"more we will do.");
}
doConnect = false;
}
#if USE_AMBIANCE_JUMPER
if(digitalRead(GPIO_AMBIANCE_JUMPER_PIN) == LOW) {
if((millis() - last_storm_timestamp_ms) >= time_to_next_storm_ms) {
doLightningShow = true;
time_to_next_storm_ms =
random(AMBIANCE_RANDOM_DELAY_MIN, AMBIANCE_RANDOM_DELAY_MAX);
}
}
#endif
if(connected) {
} else if(doScan) {
/* this is just eample to start scan after disconnect, most
* likely there is better way to do it in arduino
*/
BLEDevice::getScan()->start(0);
}
if(doLightningShow) {
#if USE_AUDIO_OUTPUT
/* NOTE: Thunder sound files must be named 0001.mp3 ... 0017.mp3 */
int thunderFile = random(1, 17);
if(digitalRead(AUDIO_BUSY_PIN) == HIGH) {
/* audio chip idle */
thunder_play(thunderFile);
}
#endif
lightning_LED_show();
ledcAnalogWrite(LEDC_CHANNEL_0, 0);
doLightningShow = false;
last_storm_timestamp_ms = millis();
}
delay(10);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment