Last active
November 11, 2019 22:03
-
-
Save Palantir555/62721eddfafa7a5de742ea8f7cfb8330 to your computer and use it in GitHub Desktop.
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
/** | |
* 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