To help my kids learn programming, we decided to build our own Smart Home system. This is Part 1 of the series, setting up the development environment and getting the room temperature.
Getting started
We decided to use ESP32, because it comes with integrated Wi-Fi and Bluetooth support, and allows us to use the Arduino IDE for development.
After Arduino IDE is installed, we need to do the following to install the Arduino-ESP32 support:
- Open the Preferences window from Arduino IDE, add this additional boards manager URL:
https://espressif.github.io/arduino-esp32/package_esp32_index.json
. - From the Tools menu, open Board –> Boards Manager, and install the
esp32
platform. - Restart the IDE.
To verify the environment is correctly set, we can run the example from File –> Examples –> ESP32 –> ChipID –> GetChipID. The output from the Serial Monitor should be something like this:
ESP32 Chip model = ESP32-D0WD-V3 Rev 3
This chip has 2 cores
Chip ID: 15003448
Getting room temperature
We can use e.g. the DHT11 sensor to get room temperature and humidity. We can connect the DHT11 sensor to ESP32 like this:
- DHT11’s
+
pin to ESP32’s3v3
pin. - DHT11’s
-
pin to ESP32’sGND
pin. - DHT11’s
out
pin to ESP32’s GPIO pin. Here, I’m using GPIO 27.
Note: Make sure we have installed the “DHT sensor library” from Sketch –> Include Library –> Manage Libraries.
Now, let’s write the code to get room temperature:
#include <DHT.h>
DHT* dht;
void setup() {
Serial.begin(115200);
dht = new DHT(27, DHT11);
dht->begin();
}
void loop() {
float temperature = dht->readTemperature();
float humidity = dht->readHumidity();
Serial.printf("Temperature: %f, Humidity: %f\n", temperature, humidity);
delay(2000);
}
Arduino code structure
Unlike traditional C++ code, there is no main()
function in Arduino. Instead, it has a setup()
and a loop()
function.
setup()
The setup()
function will only run once, after each power up or reset of the ESP32 board. This function should be used to initialize variables, start using libraries, etc.
In the code above, we did the following two things in setup()
:
- Calls
Serial.begin()
to set the data rate for serial data transmission. This is needed for the board to communicate with Serial Monitor. - Creates a
DHT
instance and start the DHT11 sensor.
loop()
The loop()
function is called repeatedly after the initialization, allowing the program to control the board.
Here, we read the temperature and humidity value from the sensor, and print them to the Serial Monitor. Keep in mind that if we want a “pause” for the program before loop()
is called agin, we should call delay()
to tell the board to “slow down”.
Broadcast temperature over BLE
Set up BLE service
To broadcast the data from ESP32, we need to first set up the BLE service:
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLE2902.h>
BLECharacteristic *temperatureCharacteristic;
BLECharacteristic *humidityCharacteristic;
void setup() {
BLEDevice::init("My ESP32");
BLEServer *server = BLEDevice::createServer();
BLEService *service = server->createService("181A");
temperatureCharacteristic = createBLECharacteristic(service, "2A6E");
humidityCharacteristic = createBLECharacteristic(service, "2A6F");
service->start();
// ...
}
Here, we first initialize the BLE device, and create a server which provides the Environmental Sensing service with UUID 0x181A (defined by the BLE Spec).
Next, we create two characteristics for temperature (0x2A6E) and humidity (0x2A6F).
Finally, we start the service.
Create BLE characteristics
The createBLECharacteristic()
function is defined as below:
BLECharacteristic *createBLECharacteristic(BLEService *service, const char* uuid) {
BLECharacteristic *characteristic = service->createCharacteristic(
uuid,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
characteristic->addDescriptor(new BLE2902());
return characteristic;
}
For each characteristic, we can set the following properties:
PROPERTY_READ
: The client can read the data. However, if the value is changed, the client can not know it until next read.PROPERTY_NOTIFY
: The server can push the data to the clients at anytime. We must add theBLE2902
descriptor.PROPERTY_INDICATE
: This is the same asPROPERTY_NOTIFY
, but it requires the client to send a response upon data arrival.PROPERTY_BROADCAST
: It indicates that the value of the characteristic is included in the advertising packets, so that the client doesn’t have to connect to the server to read the data.PROPERTY_WRITE
: The client can update the data, and require the server to send an acknowledgement. In this case, it doesn’t make any sense, so we don’t set the flag.PROPERTY_WRITE_NR
: Same asPROPERTY_WRITE
, but doesn’t require the acknowledgement from the server.
Enabling advertising
Optionally, we can enable advertising so that clients can discover the service through scanning:
void setup() {
// ...
BLEAdvertising *advertising = BLEDevice::getAdvertising();
advertising->addServiceUUID("181A");
advertising->setScanResponse(true);
BLEDevice::startAdvertising();
}
Broadcast the data
Now, we can broadcast the temperature data:
int previousTemperature = INT_MIN;
void loop() {
int currentTemperature = round(dht->readTemperature() * 100.0F);
if (previousTemperature != currentTemperature) {
temperatureCharacteristic->setValue(currentValue);
temperatureCharacteristic->notify();
previousTemperature = currentTemperature;
}
// ...
}
Here, the setValue()
function updates the value of the characteristic. Next time the client reads it, the new values will be available. The notify()
functions pushes the updated value to the client.
Note that according to the BLE spec, both temperature and humidity data should be multiplied by 100, and convert to integers.
On Android, we can use the nRF Connect app to read the data.
Conclusion
With less than $10 dollars for the hardware, and few lines of software, we have a simple smart temperature and humidity meter set up.
In the future, we will gradually expand the project to support more features, e.g. connecting the device to a chat bot so that we can know the room temperature even when we are away from home, and turn on the air conditioner before we get back.
Stay tuned!