2:30am on a tuesday night.
Week 11
Networking and communication
For network and communication week, I decided to test out bluetooth connection with ESP32.
I want to create two modules where I can send button press commands to the other via low powered bluetooth. This is the physical setup I have:
The left module has the button press and will act as the server. The right module will be the client.
I followed the random nerd tutorials to set this up here is the link: https://randomnerdtutorials.com/esp32-bluetooth-low-energy-ble-arduino-ide/
Before testing the button press, I want to make sure the connection for each module is working, so I downloaded a bluetooth connection app on my phone and tested the connection.
The connection was successful.
With the connection working I added the button code to the arduino sketch, here is the code for the server and button.
#include
#include
#include
#include
//BLE server name
#define SERC_NAME "BME280_ESP32"
#define PIN_BTN SCL
bool conn = false;
int btnprv = HIGH;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "91bad492-b950-4226-aa2b-4ede9fa42f59"
// Temperature Characteristic and Descriptor
BLECharacteristic btnchar("cba1d466-344c-4be3-ab3f-189f80dd7518", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor btndesc(BLEUUID((uint16_t)0x2902));
//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
conn = true;
};
void onDisconnect(BLEServer* pServer) {
conn = false;
}
};
void setup() {
// Start serial communication
Serial.begin(115200);
pinMode(PIN_BTN,INPUT_PULLUP);
// Create the BLE Device
BLEDevice::init(SERC_NAME);
// Create the BLE Server
BLEServer *serr = BLEDevice::createServer();
serr->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *serc = serr->createService(SERVICE_UUID);
// Create BLE Characteristics and Create a BLE Descriptor
serc->addCharacteristic(&btnchar);
btnchar.setValue("Button Characteristics");
btnchar.addDescriptor(new BLE2902());
// Start the service
serc->start();
// Start advertising
BLEAdvertising *advr = BLEDevice::getAdvertising();
advr->addServiceUUID(SERVICE_UUID);
serr->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
int b = digitalRead(PIN_BTN);
if (b == LOW && btnprv == HIGH){
Serial.println("P");
if (conn) {
btnchar.setValue("1");
btnchar.notify();
}
}else if (b == HIGH && btnprv == LOW){
Serial.println("R");
if (conn){
btnchar.setValue("0");
btnchar.notify();
}
}
btnprv = b;
}
Here is the code for the client:
#include "BLEDevice.h"
#define temperatureCelsius
//BLE Server name (the other ESP32 name running the server sketch)
#define bleServerName "BME280_ESP32"
/* UUID's of the service, characteristic that we want to read*/
// BLE Service
static BLEUUID bmeServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");
// BLE Characteristics
#ifdef temperatureCelsius
//Temperature Celsius Characteristic
static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");
#else
//Temperature Fahrenheit Characteristic
static BLEUUID temperatureCharacteristicUUID("f78ebbff-c8b7-4107-93de-889a6a06d408");
#endif
// Humidity Characteristic
static BLEUUID humidityCharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");
//Flags stating if should begin connecting and if the connection is up
static boolean doConnect = false;
static boolean connected = false;
//Address of the peripheral device. Address will be found during scanning...
static BLEAddress *pServerAddress;
//Characteristicd that we want to read
static BLERemoteCharacteristic* temperatureCharacteristic;
static BLERemoteCharacteristic* humidityCharacteristic;
//Activate notify
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};
// #define SCREEN_WIDTH 128 // OLED display width, in pixels
// #define SCREEN_HEIGHT 64 // OLED display height, in pixels
//Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//Variables to store temperature and humidity
char* temperatureChar;
char* humidityChar;
//Flags to check whether new temperature and humidity readings are available
boolean newTemperature = false;
boolean newHumidity = false;
//Connect to the BLE Server that has the name, Service, and Characteristics
bool connectToServer(BLEAddress pAddress) {
BLEClient* pClient = BLEDevice::createClient();
// Connect to the remove BLE Server.
pClient->connect(pAddress);
Serial.println(" - Connected to server");
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(bmeServiceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(bmeServiceUUID.toString().c_str());
return (false);
}
// Obtain a reference to the characteristics in the service of the remote BLE server.
temperatureCharacteristic = pRemoteService->getCharacteristic(temperatureCharacteristicUUID);
humidityCharacteristic = pRemoteService->getCharacteristic(humidityCharacteristicUUID);
if (temperatureCharacteristic == nullptr || humidityCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID");
return false;
}
Serial.println(" - Found our characteristics");
//Assign callback functions for the Characteristics
temperatureCharacteristic->registerForNotify(temperatureNotifyCallback);
humidityCharacteristic->registerForNotify(humidityNotifyCallback);
return true;
}
//Callback function that gets called, when another device's advertisement has been received
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.getName() == bleServerName) { //Check if the name of the advertiser matches
advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //Address of advertiser is the one we need
doConnect = true; //Set indicator, stating that we are ready to connect
Serial.println("Device found. Connecting!");
}
}
};
//When the BLE Server sends a new temperature reading with the notify property
static void temperatureNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify) {
//store temperature value
temperatureChar = (char*)pData;
newTemperature = true;
}
//When the BLE Server sends a new humidity reading with the notify property
static void humidityNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify) {
//store humidity value
humidityChar = (char*)pData;
newHumidity = true;
Serial.print(newHumidity);
}
//function that prints the latest sensor readings in the OLED display
void printReadings(){
// display.clearDisplay();
// display temperature
// display.setTextSize(1);
// display.setCursor(0,0);
// display.print("Temperature: ");
// display.setTextSize(2);
// display.setCursor(0,10);
// display.print(temperatureChar);
// display.setTextSize(1);
// display.cp437(true);
// display.write(167);
// display.setTextSize(2);
Serial.print("Temperature:");
Serial.print(temperatureChar);
#ifdef temperatureCelsius
//Temperature Celsius
// display.print("C");
Serial.print("C");
#else
//Temperature Fahrenheit
// display.print("F");
Serial.print("F");
#endif
//display humidity
// display.setTextSize(1);
// display.setCursor(0, 35);
// display.print("Humidity: ");
// display.setTextSize(2);
// display.setCursor(0, 45);
// display.print(humidityChar);
// display.print("%");
// display.display();
// Serial.print(" Humidity:");
// Serial.print(humidityChar);
// Serial.println("%");
}
void setup() {
//OLED display setup
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
// if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
// Serial.println(F("SSD1306 allocation failed"));
// for(;;); // Don't proceed, loop forever
// }
// display.clearDisplay();
// display.setTextSize(2);
// display.setTextColor(WHITE,0);
// display.setCursor(0,25);
// display.print("BLE Client");
// display.display();
//Start serial communication
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
//Init BLE device
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 30 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(30);
}
void loop() {
// 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(*pServerAddress)) {
Serial.println("We are now connected to the BLE Server.");
//Activate the Notify property of each Characteristic
temperatureCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
humidityCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
connected = true;
} else {
Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
}
doConnect = false;
}
//if new temperature readings are available, print in the OLED
if (newTemperature && newHumidity){
newTemperature = false;
newHumidity = false;
printReadings();
}
delay(1000); // Delay a second between loops.
}
Here is the final result, as I press the button on the server module, 1 shows up in the serial monitor of the client code and when I release 0 is shown.
Here is a better image: