Bluetooth Low Energy Server on ESP32 development board
Step by step guide for the code installed on an ESP32 set up as Bluetooth Low Energy (BLE) server. We are going to prepare the sketch file in Arduino IDE and the ServerBLE class.
June 05, 2020
Objectives
- BLE Server running on an ESP32 development board
What is covered
Code repository
Primers
- Installing Arduino IDE
- If you prefer to write your Arduino code in let’s say Visual Studio Code
- Installing the ESP32 Board in Arduino IDE (Mac OS X and Linux instructions)
- Getting Started with ESP32 Bluetooth Low Energy (BLE) on Arduino IDE
- Installing Additional Arduino Libraries
- Writing a Library for Arduino
Using the breadboard to wire up ESP32
To power up the board you may proceed however you want: a USB cable connected to your computer, a 5V DC 1A power adapter connected to the wall socket, or a trusty breadboard power supply like YwRobot 5 VDC & 3.3 VDC. Such a power supply will be handy if you are using sensors that require 5 VDC because ESP32 is running on 3.3 VDC. In such a case make sure you wire everything’s GND to the GND of the 5V power supply.
Please refer to the Fritzing file from above for details. You can ignore the parts related to the Arduino Nano, the LCD 2004, to the stepper motor. I will cover those in subsequent blog posts. Also, please read from above on how to install and set up the Arduino IDE and the ESP32 board.
I have found useful while uploading the sketch file into the ESP32 board to press the “BOOT” switch. It might become a headache to expect the board to always figure out that you are trying to upload something.
The Arduino sketch .ino file for the BLE server
To generate some test data for the BLE server I am using 3 temporary sensors: a photoresistor, a hall effect sensor, and a temperature and humidity sensor. We are going to import the DHT.h
(DHT Sensor Library from Adafruit, required by the temperature and humidity DHT 11 sensor), Wire.h
(required for the I2C communication with Arduino Nano, covered in a future blog post), Lcd.h
(header of the custom class for the LCD 2004), ServerBLE.h
(header of the custom class for the server).
We instantiate the lcd
and server
objects on lines 6-7 and we also assign values to the required constants for the I2C address, pins, and for the variables on lines 9-29. settings
array is for storing the values received by the BLE server from a BLE client. previousMillis
is used to debounce the data received from the sensors at a sensorsInterval
of 100 miliseconds.
#include <DHT.h>
#include <Wire.h>
#include "Lcd.h"
#include "ServerBLE.h"
Lcd lcd;
ServerBLE server;
const int I2CSlaveAddress = 4;
const int translatorOEPin = 23;
const int translatorDelay = 10000;
const int size = 4;
int settings[size] = {0, 0, 0, 0};
int prevSettings[size] = {0, 0, 0, 0};
TwoWire I2Ctwo = TwoWire(1);
bool messageSent = false;
const int tempPin = 15;
DHT dht(tempPin, DHT11);
const int photoPin = 35;
const int magneticPin = 32;
unsigned long previousMillis = 0;
const long sensorsInterval = 100;
The setup
function deals with setting the pinMode
for the sensors, for the bidirectional (3.3V to 5V) logic level translator OE
output enable pin, it starts the server, the I2C wire, the LCD and the DHT sensor.
void setup(void) {
pinMode(photoPin, INPUT);
pinMode(magneticPin, INPUT);
pinMode(translatorOEPin, OUTPUT);
digitalWrite(translatorOEPin, LOW);
Serial.begin(115200);
server.start();
digitalWrite(translatorOEPin, HIGH);
Serial.println("Translator output enabled. Waiting for " + String(translatorDelay / 1000) + " seconds...");
delay(translatorDelay);
I2Ctwo.begin(21, 22, 100000);
lcd.start();
dht.begin();
}
The loop
function is doing 2 main jobs: it checks if the BLE server
has new settings to be sent to the lcd
object to be printed on the LCD 2004 screen and to be sent via I2C to the Arduino Nano board as a char
array of cumulated characters (lines 4-26). It also reads the analog data from the 3 sensors and sends that data to the BLE server
joined as a String
(lines 28-46). That cumulated variable of the String
type is then converted into a char
array before sending it to the BLE server
object.
void loop(void) {
refreshSettings();
if (checkIfNewValues(settings, prevSettings, size) == true) {
if (isStopping() == true) {
lcd.printNoSettings();
} else {
lcd.printSettings(settings[0], settings[1], settings[2], settings[3]);
Serial.println("From Lcd: " + lcd.getSettingsAsString());
}
I2Ctwo.beginTransmission(I2CSlaveAddress);
for (byte i = 0; i < size; i++) {
char t[30];
dtostrf(settings[i], 1, 0, t);
I2Ctwo.write(t);
if (i < size - 1) {
I2Ctwo.write("x");
}
prevSettings[i] = settings[i];
}
I2Ctwo.endTransmission();
delay(500);
} else {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= sensorsInterval) {
previousMillis = currentMillis;
int tempVal = dht.readTemperature() * 10;
int photoVal = analogRead(photoPin);
int magneticVal = analogRead(magneticPin);
Serial.println("Photo sensor " + String(photoVal));
Serial.println("Magnetic sensor " + String(magneticVal));
Serial.println("Temp sensor " + String(tempVal));
String cumulated = String(magneticVal) + "x" + String(photoVal) + "x" + String(tempVal);
char buffer[cumulated.length() + 1];
cumulated.toCharArray(buffer, cumulated.length() + 1);
server.setSensorsValue((char*)&buffer);
}
}
}
The ServerBLE library
This library is built using the great work of Neil Kolban (nkolban github account). To easily understand what this library is doing, let’s take a look at the definitions from the ServerBLE.h
header file. The libraries included at the beginning of the file are useful in creating the server, defining the services and the BLE characteristics used to exchange data with the BLE Client.
The public attributes and methods (lines 12-15) include of course the constructor, the start
method that instantiates the server, 2 services for read and write BLE characteristics, advertises the server. The other 2 methods setSensorsValue
and getSettings
are available to the sketch .ino file to send and receive data from the object created with this class.
The private attributes and methods (lines 17-28) include instances of BLEServer
, BLECharacteristic
and BLEService
one for read services and one for write services, as returned values of the two private methods createBLEReadService
, createBLEWriteService
.
#ifndef ServerBLE_h
#define ServerBLE_h
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
class ServerBLE {
public:
ServerBLE();
void start();
void setSensorsValue(char* buffer);
std::string getSettings();
private:
BLEServer *_server;
BLECharacteristic *_sensorsCharacteristic;
BLECharacteristic *_settingsCharacteristic;
BLEService *createBLEReadService(
byte type,
char* serviceUUID,
char* characteristicUUID
);
BLEService *createBLEWriteService(
byte type,
char* serviceUUID,
char* characteristicUUID
);
};
#endif
To see the implementation of these definitions, please take a look at the ServerBLE.cpp
source file here. You may notice the secret.h
used to define the constants with UUIDs for services and characteristics. You can use an online service like Online UUID Generator for those universally unique identifiers.
#define SERVICE_UUID_SENSORS "<your-UUID>"
#define SERVICE_UUID_SETTINGS "<your-UUID>"
#define CHARACTERISTIC_UUID_SENSORS "<your-UUID>"
#define CHARACTERISTIC_UUID_SETTINGS "<your-UUID>"