/********* Rui Santos Complete instructions at https://RandomNerdTutorials.com/esp32-ble-server-client/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. *********/ #include "BLEDevice.h" //Default Temperature is in Celsius //Comment the next line for Temperature in Fahrenheit #define temperatureCelsius //BLE Server name (the other ESP32 name running the server sketch) #define bleServerName "ESP32" #define bleServerAddress "64:e8:33:da:a2:b6" /* UUID's of the service, characteristic that we want to read*/ // BLE Service static BLEUUID bmeServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59"); //Temperature Celsius Characteristic static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518"); //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; //Activate notify const uint8_t notificationOn[] = {0x1, 0x0}; const uint8_t notificationOff[] = {0x0, 0x0}; //Variables to store temperature and humidity char* temperatureChar; //Flags to check whether new temperature and humidity readings are available boolean newTemperature = 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); if (temperatureCharacteristic == 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); return true; } //Callback function that gets called, when another device's advertisement has been received class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice advertisedDevice) { Serial.print("Found Device: "); Serial.println(advertisedDevice.getAddress().toString().c_str()); if (advertisedDevice.getAddress().toString() == bleServerAddress) { //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!"); } } }; int last_update = 0; void deserialize_int16_t(uint8_t* buffer, int16_t* values, size_t count) { for (size_t i = 0; i < count; ++i) { values[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]); } } //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) { // Check if the length of the received data is as expected if (length % 2 != 0) { // Handle error: The length should be even for 2-byte integers Serial.println("Error: Invalid data length"); return; } // Assuming you know the number of int16_t values in the received data size_t count = length / 2; int16_t receivedValues[count]; // Deserialize the received data deserialize_int16_t(pData, receivedValues, count); // Process the received values as needed for (size_t i = 0; i < count; ++i) { Serial.print(receivedValues[i]); Serial.print(" "); } Serial.println(); } void setup() { //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); 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){ newTemperature = false; } // Check for a reset command from serial if (Serial.available() > 0) { char command = Serial.read(); // Check if the received command is 'R' if (command == 'R' || command == 'r') { Serial.println("Resetting..."); delay(1000); // Optional delay for visibility in the Serial Monitor ESP.restart(); // This function resets the ESP32 } } delay(1000); // Delay a second between loops. }