This video just show how
MicroPython bluetooth module examples
run on ESP32-C3, Ai-Thinker NodeMCU ESP-C3-32S-Kit and Espressif
ESP32-C3-DevKitM-1, both running MicroPython
v1.19.1 on 2022-06-18. Finally, have bi-direction BLE communication between ESP32-C3 dev. boards.
MicroPython bluetooth module
provides an interface to a Bluetooth controller on a board. Currently this
supports Bluetooth Low Energy (BLE) in Central, Peripheral, Broadcaster, and
Observer roles, as well as GATT Server and Client and L2CAP
connection-oriented-channels. A device may operate in multiple roles
concurrently. Pairing (and bonding) is supported on some ports.
This post show how to implement BLE UART communication between NodeMCU
ESP-S3-12K-Kit (in Arduino framework usiing arduino-esp32) and HC-42 BLE
Module.
Note that in HC-42: - Search UUID: FFF0 -
Service UUID: FFE0 - Transparent data transmission UUID: FFE1
We
have to follow it in arduino code in ESP-S3-12K-Kit.
ESP32S3_BLE_client_HC42.ino, modified from "ESP32 BLE Arduino" > "BLE_client" example, for HC-42.
/**
* A BLE client example that is rich in capabilities.
* There is a lot new capabilities implemented.
* author unknown
* updated by chegewara
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
#define SEARCH_UUID "FFF0"
#define SERVICE_UUID "FFE0"
#define TRAN_UUID "FFE1"// The remote service we wish to connect to.
static BLEUUID searchUUID(SEARCH_UUID);
static BLEUUID serviceUUID(SERVICE_UUID);
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID(TRAN_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: ");
Serial.println((char*)pData);
}
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");
pClient->setMTU(517); //set client to request maximum MTU from
//server (default is 23 otherwise)
// 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");
// Obtain a reference to the characteristic 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());
// We have found a device, let us now see if it contains the service
// we are looking for.
if (advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(searchUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
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);
} // End of setup.
// This is the Arduino main loop function.
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()) {
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 we are connected to a peer BLE Server,
// update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
String newValue = "Time since boot: " + String(millis()/1000) +"\n";
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
// Set the characteristic's value to be the array of bytes
// that is actually a string.
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just example to start scan
// after disconnect, most likely there
// is better way to do it in arduino
}
delay(1000); // Delay a second between loops.
} // End of loop
ESP32S3_BLE_uart_client_HC42.ino, bi-directional BLE UART communication.
/**
* A BLE UART client example run on ESP32-S3,
* act as client, connect to HC-42, to establish
* BLE UART communication.
*
* Modified from "ESP32 BLE Arduino" > "BLE_client"
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
#define SEARCH_UUID "FFF0"
#define SERVICE_UUID "FFE0"
#define TRANS_UUID "FFE1"
// The remote service we wish to connect to.
static BLEUUID searchUUID(SEARCH_UUID);
static BLEUUID serviceUUID(SERVICE_UUID);
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID(TRANS_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) {
if (length > 0){
Serial.printf("%i :\t", length);
for (int i=0; i<length; i++)
Serial.print((char) pData[i]);
Serial.println();
}
}
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");
pClient->setMTU(517); //set client to request maximum MTU from server
//(default is 23 otherwise)
// 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");
// Obtain a reference to the characteristic 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());
// We have found a device, let us now see if it contains the service
// we are looking for.
if (advertisedDevice.haveServiceUUID()
&& advertisedDevice.isAdvertisingService(searchUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
delay(1000); //it's seem that adding delay here make it more stable
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);
} // End of setup.
// This is the Arduino main loop function.
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()) {
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 we are connected to a peer BLE Server,
// update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
if (Serial.available()) {
String newValue = "";
while(Serial.available()){
char c = Serial.read();
newValue += c;
}
newValue += "\n";
Serial.println(newValue);
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
}
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just example to start scan
// after disconnect, most likely there
// is better way to do it in arduino
}
delay(1000); // Delay a second between loops.
} // End of loop
The HC-42 Bluetooth serial communication module is a new generation of
data transmission module based on Bluetooth Specification V5.0 BLE Bluetooth
protocol. It can be set using AT Command.
Connection between HC-42 and FTDI232 (USB to serial UART adapter)
connect to host computer
(Raspberry Pi)
||||
+-------------+ +-------------+
|HC-42 | | FTDI232 |
| | | (VCC=3.3V) |
| | | |
| | | |
| | | |
| | | |
| S | | |
| T | | |
| A R T G V K | | D V C G |
| T X X N C E | | T R T C T N |
| E D D D C Y | | R X X C S D |
+-------------+ +-------------+
| | | | | | | |
| | | +-----------------|-|-+ |
| | +-------------------|-|-----+
| +---------------------+ |
+-------------------------+
It's a exercise of CircuitPython to implement BLE UART between server and
client. All boards are flashed with CircuitPython 7.2.4 firmware, with
adafruit_ble 8.2.3.
The server side run on Seeed XIAO BLE Sense (nRF52840) + Expansion board. The code modified from Adafruit CircuitPython ble_uart_echo_test.py
example, or
here, setup BLE UART Server, but display on SSD1306 I2C OLED instead of echo back.
If server side offline and the BLE link disconnected, then server side
online again and re-connected, the client side will fail; tested on both
ESP-C3-32S-Kit and ESP32-S3-DevKitC-1.
Exercise code:
cpyXIAOBLE_ble_uart_server_ssd1306.py, run on server side.
"""
Run CircuitPython 7.2.4 on
Seeed XIAO nRF52840 Sense with nRF52840 + Expansion Board.
Modified from ble_uart_echo_test.py.
Act as BLE server, wait connection and
display received line on ssd13106 OLED.
libs need:
- adafruit_ble folder
- adafruit_displayio_ssd1306.mpy
"""
import os
import sys
import board
import busio
import displayio
import adafruit_displayio_ssd1306
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
from adafruit_ble import __name__ as BLE_NAME
from adafruit_ble import __version__ as BLE_VERSION
displayio.release_displays()
# Create the I2C interface and display object of SSD1306_I2C.
i2c = busio.I2C(board.SCL, board.SDA)
ssd1306_i2c_addr = 60
display_width =128
display_height = 64
display_bus = displayio.I2CDisplay(
i2c, device_address=ssd1306_i2c_addr)
display = adafruit_displayio_ssd1306.SSD1306(
display_bus, width=display_width, height=display_height)
# with displayio initialized, and have nothing displayed.
# displayio act like a REPL terminal.
# anything print() will be displayed on displayio also.
print("=================================================")
info = sys.implementation[0] + ' ' + os.uname()[3] + '\n' + \
'run on ' + os.uname()[4]
print(info)
print("=================================================")
print(BLE_NAME, ":", BLE_VERSION)
print("=================================================")
print(adafruit_displayio_ssd1306.__name__, adafruit_displayio_ssd1306.__version__)
print("SCL: ", board.SCL)
print("SDA: ", board.SDA)
print(display)
print("display.width x height: ",
display.width, " x ", display.height)
#=================================
ble = BLERadio()
uart = UARTService()
advertisement = ProvideServicesAdvertisement(uart)
#=== scroll up to print screen ===
print()
print()
print()
print(" ---- hello ----")
while True:
ble.start_advertising(advertisement)
print("Waiting to connect")
while not ble.connected:
pass
print("Connected")
while ble.connected:
s = uart.readline()
if s:
print(s.decode())
uart.write(s)
print("~ bye ~")
cpyESP32x3_ble_uart_client_repl.py, run on client side.
"""
Run CircuitPython 7.2.4 on
AITHinker ESP32-C3S_Kit with ESP32-C3FN4/ESP32-S3-DevKitC-1-N8R8 with ESP32S3.
Modified from ble_uart_echo_client.py.
Act as BLE client, connect to server,
get user input from REPL and send to server.
libs need:
- adafruit_ble folder
"""
import os
import sys
import time
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
from adafruit_ble import __name__ as BLE_NAME
from adafruit_ble import __version__ as BLE_VERSION
print("=================================================")
info = sys.implementation[0] + ' ' + os.uname()[3] + '\n' + \
'run on ' + os.uname()[4]
print(info)
print("=================================================")
print(BLE_NAME, ":", BLE_VERSION)
print("=================================================")
ble = BLERadio()
while True:
while ble.connected and any(
UARTService in connection for connection in ble.connections
):
for connection in ble.connections:
if UARTService not in connection:
continue
uart = connection[UARTService]
# input() will block the code.
# if connection lost while waiting user input,
# ConnectionError will thrown in uart.write()
userinput = input("\nEnter something: ")
print(userinput)
try:
uart.write(userinput)
except ConnectionError as exc:
print("ConnectionError:", exc)
print("disconnected, scanning")
for advertisement in ble.start_scan(ProvideServicesAdvertisement, timeout=1):
if UARTService not in advertisement.services:
continue
ble.connect(advertisement)
print("connected")
break
ble.stop_scan()
Seeed XIAO BLE Sense: XIAOBLE_CallbackLED_OLED.ino, act as BLE
Peripheral, control LED and OLED display base on BLE received data.
Arduino Nano RP2040 Connect: RP2040Con_LedControl_OLED.ino, act as
BLE Central, read button (the yellow wire) and get user input from Serial,
send to Peripheral to control LED and display on OLED.
XIAOBLE_CallbackLED_OLED.ino
/*
Run on XIAO BLESensor:
This example creates a BLE peripheral with service that contains a
characteristic to control an LED and OLED. The callback features of the
library are used.
Modified from ArduinoBLE > Peripheral > CallbackLED example
*/
#include <ArduinoBLE.h>
#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h>
#define MyLocalName "XIAO BLE"
#define MyStringChar_UUID "22a28815-c6bd-401b-a0f1-d47c42a0bd70"
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service
// create switch characteristic and allow remote device to read and write
BLEByteCharacteristic switchCharacteristic(
"19B10001-E8F2-537E-4F6C-D104768A1214",
BLERead | BLEWrite);
BLEStringCharacteristic myStringCharacteristic(
MyStringChar_UUID,
BLERead | BLEWrite, 16);
const int ledPin = LED_BUILTIN; // pin to use for the LED
void setup() {
u8x8.begin();
u8x8.setFlipMode(1); //rotary 180
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.setCursor(0, 0);
u8x8.print("XIAO BLE");
Serial.begin(9600);
//comment to skip Serial port waiting,
//such that it can sork stand alone without computer.
//while (!Serial);
pinMode(ledPin, OUTPUT); // use the LED pin as an output
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
// set the local name peripheral advertises
BLE.setLocalName(MyLocalName);
// set the UUID for the service this peripheral advertises
BLE.setAdvertisedService(ledService);
// add the characteristic to the service
ledService.addCharacteristic(switchCharacteristic);
ledService.addCharacteristic(myStringCharacteristic);
// add service
BLE.addService(ledService);
// assign event handlers for connected, disconnected to peripheral
BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
// assign event handlers for characteristic
switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten);
myStringCharacteristic.setEventHandler(BLEWritten, myStringCharacteristicWritten);
// set an initial value for the characteristic
switchCharacteristic.setValue(0);
myStringCharacteristic.setValue("XIAO BLE");
// start advertising
BLE.advertise();
Serial.println(("Bluetooth device active, waiting for connections..."));
}
void loop() {
// poll for BLE events
BLE.poll();
}
void blePeripheralConnectHandler(BLEDevice central) {
// central connected event handler
Serial.print("Connected event, central: ");
Serial.println(central.address());
}
void blePeripheralDisconnectHandler(BLEDevice central) {
// central disconnected event handler
Serial.print("Disconnected event, central: ");
Serial.println(central.address());
}
void switchCharacteristicWritten(BLEDevice central,
BLECharacteristic characteristic) {
// central wrote new value to characteristic, update LED
Serial.print("Characteristic event, written: ");
if (switchCharacteristic.value()) {
Serial.println("LED on");
digitalWrite(ledPin, !HIGH);
} else {
Serial.println("LED off");
digitalWrite(ledPin, !LOW);
}
}
void myStringCharacteristicWritten(BLEDevice central,
BLECharacteristic characteristic) {
// central wrote new value to characteristic, update LED
Serial.println("mySttringCharacteristic event, written: ");
Serial.println("myStringCharacteristic received: len=" +
String(myStringCharacteristic.valueLength()));
String valString = myStringCharacteristic.value();
Serial.println(valString);
u8x8.clear();
u8x8.setCursor(0, 0);
u8x8.print(valString);
Serial.println();
}
RP2040Con_LedControl_OLED.ino
/*
run on Aduino RP2040 NANO Connect
This example scans for BLE peripherals until one with the advertised service
"19b10000-e8f2-537e-4f6c-d104768a1214" UUID is found. Once discovered and connected,
it will remotely control the BLE Peripheral's LED, when the button is pressed or released.
And also get user input from Serial Monitor, send to BLE Peripheral
modified from ArduinoBLE > Central > LedControl
*/
#include <ArduinoBLE.h>
// variables for button
const int buttonPin = 2;
int oldButtonState = LOW;
#define MyLocalName "XIAO BLE"
#define MyStringChar_UUID "22a28815-c6bd-401b-a0f1-d47c42a0bd70"
void setup() {
Serial.begin(9600);
while (!Serial);
// configure the button pin as input
pinMode(buttonPin, INPUT_PULLUP);
// initialize the BLE hardware
BLE.begin();
Serial.println("BLE Central - LED control");
// start scanning for peripherals
BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
}
void loop() {
// check if a peripheral has been discovered
BLEDevice peripheral = BLE.available();
if (peripheral) {
// discovered a peripheral, print out address, local name, and advertised service
Serial.print("Found ");
Serial.print(peripheral.address());
Serial.print(" '");
Serial.print(peripheral.localName());
Serial.print("' ");
Serial.print(peripheral.advertisedServiceUuid());
Serial.println();
if (peripheral.localName() != MyLocalName) {
return;
}
// stop scanning
BLE.stopScan();
controlLed(peripheral);
// peripheral disconnected, start scanning again
BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
}
}
void controlLed(BLEDevice peripheral) {
// connect to the peripheral
Serial.println("Connecting ...");
if (peripheral.connect()) {
Serial.println("Connected");
} else {
Serial.println("Failed to connect!");
return;
}
// discover peripheral attributes
Serial.println("Discovering attributes ...");
if (peripheral.discoverAttributes()) {
Serial.println("Attributes discovered");
} else {
Serial.println("Attribute discovery failed!");
peripheral.disconnect();
return;
}
// retrieve the LED/OLED characteristic
BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10001-e8f2-537e-4f6c-d104768a1214");
BLECharacteristic oledCharacteristic = peripheral.characteristic(MyStringChar_UUID);
/*
if (!ledCharacteristic) {
Serial.println("Peripheral does not have LED characteristic!");
peripheral.disconnect();
return;
} else if (!ledCharacteristic.canWrite()) {
Serial.println("Peripheral does not have a writable LED characteristic!");
peripheral.disconnect();
return;
}
*/
while (peripheral.connected()) {
// while the peripheral is connected
// read the button pin
int buttonState = !digitalRead(buttonPin);
if (oldButtonState != buttonState) {
// button changed
oldButtonState = buttonState;
if (buttonState) {
Serial.println("button pressed");
// button is pressed, write 0x01 to turn the LED on
ledCharacteristic.writeValue((byte)0x01);
} else {
Serial.println("button released");
// button is released, write 0x00 to turn the LED off
ledCharacteristic.writeValue((byte)0x00);
}
}
if (Serial.available() > 0){
String stringIn = Serial.readString();
Serial.println(stringIn);
oledCharacteristic.writeValue(stringIn.c_str());
}
}
Serial.println("Peripheral disconnected");
}
XIAO BLE Sense (with Seeeduino XIAO Expansion board)
act as Central, connect Peripher, send command base on user button on Expansion
board, to control LED on off.
Load with ArduinoBLE > Central >
LedControl example.
To make it work, change in following,
marked in RED:
XIAOBLE_LedControl.ino
/*
LED Control
This example scans for BLE peripherals until one with the advertised service
"19b10000-e8f2-537e-4f6c-d104768a1214" UUID is found. Once discovered and connected,
it will remotely control the BLE Peripheral's LED, when the button is pressed or released.
The circuit:
- Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
- Button with pull-up resistor connected to pin 2.
You can use it with another board that is compatible with this library and the
Peripherals -> LED example.
This example code is in the public domain.
*/
#include <ArduinoBLE.h>
// variables for button
const int buttonPin = 1;
int oldButtonState = LOW;
void setup() {
Serial.begin(9600);
while (!Serial);
// configure the button pin as input
pinMode(buttonPin, INPUT_PULLUP);
// initialize the BLE hardware
BLE.begin();
Serial.println("BLE Central - LED control");
// start scanning for peripherals
BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
}
void loop() {
// check if a peripheral has been discovered
BLEDevice peripheral = BLE.available();
if (peripheral) {
// discovered a peripheral, print out address, local name, and advertised service
Serial.print("Found ");
Serial.print(peripheral.address());
Serial.print(" '");
Serial.print(peripheral.localName());
Serial.print("' ");
Serial.print(peripheral.advertisedServiceUuid());
Serial.println();
if (peripheral.localName() != "LED") {
return;
}
// stop scanning
BLE.stopScan();
controlLed(peripheral);
// peripheral disconnected, start scanning again
BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
}
}
void controlLed(BLEDevice peripheral) {
// connect to the peripheral
Serial.println("Connecting ...");
if (peripheral.connect()) {
Serial.println("Connected");
} else {
Serial.println("Failed to connect!");
return;
}
// discover peripheral attributes
Serial.println("Discovering attributes ...");
if (peripheral.discoverAttributes()) {
Serial.println("Attributes discovered");
} else {
Serial.println("Attribute discovery failed!");
peripheral.disconnect();
return;
}
// retrieve the LED characteristic
BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10001-e8f2-537e-4f6c-d104768a1214");
if (!ledCharacteristic) {
Serial.println("Peripheral does not have LED characteristic!");
peripheral.disconnect();
return;
} else if (!ledCharacteristic.canWrite()) {
Serial.println("Peripheral does not have a writable LED characteristic!");
peripheral.disconnect();
return;
}
while (peripheral.connected()) {
// while the peripheral is connected
// read the button pin
int buttonState = !digitalRead(buttonPin);
if (oldButtonState != buttonState) {
// button changed
oldButtonState = buttonState;
if (buttonState) {
Serial.println("button pressed");
// button is pressed, write 0x01 to turn the LED on
ledCharacteristic.writeValue((byte)0x01);
} else {
Serial.println("button released");
// button is released, write 0x00 to turn the LED off
ledCharacteristic.writeValue((byte)0x00);
}
}
}
Serial.println("Peripheral disconnected");
}
This exercise run on ESP32/ESP32-C3 to perform BLE communication, to notify
data update. Both run on arduino-esp32 framework.
The BLE server run on ESP32-DevKitC V4, read DHT11 temperature &
humidity, display on ST7789 SPI TFT, and notify connected client. The BLE client
run on ESP32-C3-DevKitM-1, connect to BLE server, update SSD1306 I2C OLED
once notified data updated.
both advertisedDevice.haveServiceUUID() and
advertisedDevice.isAdvertisingService(serviceUUID) in client return false. So
the client cannot find the server.
To solve it, I change the setting (follow BLE_server example):
then both advertisedDevice.haveServiceUUID() and
advertisedDevice.isAdvertisingService(serviceUUID) in client return true, such
that the server can be found.
ESP32_DHT_ST789_graphic_BLE_2021-12-14.ino, server side run on ESP32-DevKitC
V4.
/*
Execise run on ESP32 (ESP32-DevKitC V4) with arduino-esp32 2.0.1,
read DHT11 Humidity & Temperature Sensor,
and display on ST7789 SPI TFT, 2" IPS 240x320, with graph.
BLE function added.
Library needed:
- DHT sensor library for ESPx by beegee_tokyo
- Adafruit ST7735 and ST7789 Library by Adafruit
- Adafruit GFX Library by Adafruit
Modify from examples DHT_ESP32 of DHT sensor library for ESPx
Connection between DHT11 and ESP32 (GPIO#)
-----------------------------------------------
DHT11 ESP32
----- -----
VCC* 3V3
DATA** 32
NC
GND GND
* * - depends on module, my DHT11 module is 3V3~5V operate.
* ** - depends on your module, maybe you have to add a
pull-up resistor (~10K Ohm) betwee DATA and VCC.
Connection between ST7789 SPI and ESP32 (GPIO#)
-----------------------------------------------
ST7789 SPI ESP32
---------- -----
GND GND
VCC 3V3
SCL 18
SDA 23
RES 26
DC 25
CS 33
BLK 3V3
*/
#include "DHTesp.h"
#include <Ticker.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <Fonts/FreeMonoBold12pt7b.h>
#include <SPI.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#ifndef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP32 ONLY!)
#error Select ESP32 board.
#endif
DHTesp dht;
void tempTask(void *pvParameters);
bool getTemperature();
void triggerGetTemp();
/** Task handle for the light value read task */
TaskHandle_t tempTaskHandle = NULL;
/** Ticker for temperature reading */
Ticker tempTicker;
/** Comfort profile */
ComfortState cf;
/** Flag if task should run */
bool tasksEnabled = false;
/** Pin number for DHT11 data pin */
int dhtPin = 32; //17;
//hardware SPI MOSI 23
//hardware SPI SCK 18
#define TFT_CS 33
#define TFT_RST 26
#define TFT_DC 25
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
bool rqsUpdate = false;
TempAndHumidity updateValues;
unsigned long prvUpdateMillis;
#define FRAME_TOPX 0
#define FRAME_TOPY 200
#define FRAME_WIDTH 240
#define FRAME_HEIGHT 100
#define FRAME_BOTTOMY FRAME_TOPY + FRAME_HEIGHT
#define SCR_HEIGHT 320
int idx = 0;
#define IDX_MAX 240
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
BLECharacteristic* pChar_temp = NULL;
BLECharacteristic* pChar_humi = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "FC8601FC-7829-407B-9C2E-4D3F117DFF2D"
#define CHAR_UUID_TEMP "0FD31907-35AE-4BB0-8AB1-51F98C05B326"
#define CHAR_UUID_HUMI "D01D3AF7-818D-4A90-AECF-0E1EC48AA5F0"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
BLEDevice::startAdvertising();
Serial.println("MyServerCallbacks.onConnect");
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("MyServerCallbacks.onDisconnect");
}
};
/**
initTemp
Setup DHT library
Setup task and timer for repeated measurement
@return bool
true if task and timer are started
false if task or timer couldn't be started
*/
bool initTemp() {
byte resultValue = 0;
// Initialize temperature sensor
dht.setup(dhtPin, DHTesp::DHT11);
Serial.println("DHT initiated");
// Start task to get temperature
xTaskCreatePinnedToCore(
tempTask, /* Function to implement the task */
"tempTask ", /* Name of the task */
4000, /* Stack size in words */
NULL, /* Task input parameter */
5, /* Priority of the task */
&tempTaskHandle, /* Task handle. */
1); /* Core where the task should run */
if (tempTaskHandle == NULL) {
Serial.println("Failed to start task for temperature update");
return false;
} else {
// Start update of environment data every XX seconds
tempTicker.attach(2, triggerGetTemp);
}
return true;
}
/**
triggerGetTemp
Sets flag dhtUpdated to true for handling in loop()
called by Ticker getTempTimer
*/
void triggerGetTemp() {
if (tempTaskHandle != NULL) {
xTaskResumeFromISR(tempTaskHandle);
}
}
/**
Task to reads temperature from DHT11 sensor
@param pvParameters
pointer to task parameters
*/
void tempTask(void *pvParameters) {
Serial.println("tempTask loop started");
while (1) // tempTask loop
{
if (tasksEnabled) {
// Get temperature values
getTemperature();
}
// Got sleep again
vTaskSuspend(NULL);
}
}
/**
getTemperature
Reads temperature from DHT11 sensor
@return bool
true if temperature could be aquired
false if aquisition failed
*/
bool getTemperature() {
// Reading temperature for humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
TempAndHumidity newValues = dht.getTempAndHumidity();
// Check if any reads failed and exit early (to try again).
if (dht.getStatus() != 0) {
Serial.println("DHT11 error status: " + String(dht.getStatusString()));
return false;
}
rqsUpdate = true;
updateValues = newValues;
return true;
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("DHT ESP32 example with tasks");
//init DHT
initTemp();
// Signal end of setup() to tasks
tasksEnabled = true;
//init BLE
// Create the BLE Device
BLEDevice::init("ESP32-DHT11");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic for temp and humi
pChar_temp = pService->createCharacteristic(
CHAR_UUID_TEMP,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pChar_humi = pService->createCharacteristic(
CHAR_UUID_HUMI,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pChar_temp->addDescriptor(new BLE2902());
pChar_humi->addDescriptor(new BLE2902());
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
// Erik updated@2021-12-14
//pAdvertising->setScanResponse(false);
//pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
//print for information only
Serial.println("pAdvertising->setScanResponse(true)");
Serial.println("pAdvertising->setMinPreferred(0x06)");
Serial.println("pAdvertising->setMinPreferred(0x12)");
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
//init ST7789
tft.init(240, 320); // Init ST7789 320x240
tft.setRotation(2);
tft.setFont(&FreeMonoBold12pt7b);
tft.setTextWrap(true);
tft.fillScreen(ST77XX_RED);
delay(300);
tft.fillScreen(ST77XX_GREEN);
delay(300);
tft.fillScreen(ST77XX_BLUE);
delay(300);
tft.setCursor(0, 0);
tft.setTextColor(ST77XX_RED);
tft.print("\n");
tft.print("ESP32 + DHT11 + ST7789\n");
prvUpdateMillis = millis();
}
void loop() {
if (!tasksEnabled) {
// Wait 2 seconds to let system settle down
delay(2000);
// Enable task that will read values from the DHT sensor
tasksEnabled = true;
if (tempTaskHandle != NULL) {
vTaskResume(tempTaskHandle);
}
}
if (rqsUpdate) {
unsigned long curUpdateMillis = millis();
tft.fillRect(0, 53, 240, 75, ST77XX_BLUE );
tft.setCursor(0, 70);
tft.setTextColor(ST77XX_WHITE);
tft.print(" temp.: " + String(updateValues.temperature));
tft.setCursor(0, 95);
tft.setTextColor(ST77XX_WHITE);
tft.print(" humi.: " + String(updateValues.humidity));
tft.setCursor(0, 115);
tft.setTextColor(ST77XX_WHITE);
tft.print(" mills.: " + String(curUpdateMillis - prvUpdateMillis));
prvUpdateMillis = curUpdateMillis;
if (idx == 0) {
tft.fillRect(FRAME_TOPX, FRAME_TOPY,
FRAME_WIDTH, SCR_HEIGHT - FRAME_TOPY,
ST77XX_BLUE);
}
tft.drawLine(
FRAME_TOPX + idx, FRAME_BOTTOMY,
FRAME_TOPX + idx, FRAME_BOTTOMY - (int)updateValues.temperature,
ST77XX_WHITE);
idx++;
if (idx >= IDX_MAX)
idx = 0;
char bufTemp[5];
char bufHumi[5];
//convert floating point value to String
dtostrf(updateValues.temperature, 0, 2, bufTemp);
dtostrf(updateValues.humidity, 0, 2, bufHumi);
pChar_temp->setValue((uint8_t*)bufTemp, 5);
pChar_temp->notify();
pChar_humi->setValue((uint8_t*)bufHumi, 5);
pChar_humi->notify();
//Serial.println(" T:" + String(updateValues.temperature) + " H:" + String(updateValues.humidity));
rqsUpdate = false;
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
Serial.println("disconnecting");
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
Serial.println("connecting");
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
yield();
}
ESP32C3_BLE_client.ino, client side run on ESP32-C3-DevKitM-1. Copy from
BLE_client example, with UUID updated.
/**
* A BLE client example that is rich in capabilities.
* There is a lot new capabilities implemented.
* author unknown
* updated by chegewara
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
#define SERVICE_UUID "FC8601FC-7829-407B-9C2E-4D3F117DFF2D"
#define CHAR_UUID_TEMP "0FD31907-35AE-4BB0-8AB1-51F98C05B326"
#define CHAR_UUID_HUMI "D01D3AF7-818D-4A90-AECF-0E1EC48AA5F0"
// The remote service we wish to connect to.
static BLEUUID serviceUUID(SERVICE_UUID);
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID(CHAR_UUID_TEMP);
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: ");
Serial.println((char*)pData);
}
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");
pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)
// 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");
// Obtain a reference to the characteristic 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());
if(advertisedDevice.haveServiceUUID()){
Serial.println("- advertisedDevice.haveServiceUUID()");
}
if(advertisedDevice.isAdvertisingService(serviceUUID)){
Serial.println("- advertisedDevice.isAdvertisingService(serviceUUID)");
}
// We have found a device, let us now see 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;
Serial.println("*** device found ***");
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
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);
} // End of setup.
// This is the Arduino main loop function.
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()) {
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 we are connected to a peer BLE Server, update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
String newValue = "Time since boot: " + String(millis()/1000);
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
// Set the characteristic's value to be the array of bytes that is actually a string.
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
}
delay(1000); // Delay a second between loops.
} // End of loop
ESP32C3_BLE_DHT11_client.ino, updated to recognize temperature & humidity.
/**
* modified from BLE_client
* to work with ESP32_DHT_ST789_graphic_BLE.ino,
* to monitor temp/humi.
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
#define SERVICE_UUID "FC8601FC-7829-407B-9C2E-4D3F117DFF2D"
#define CHAR_UUID_TEMP "0FD31907-35AE-4BB0-8AB1-51F98C05B326"
#define CHAR_UUID_HUMI "D01D3AF7-818D-4A90-AECF-0E1EC48AA5F0"
// The remote service we wish to connect to.
static BLEUUID serviceUUID(SERVICE_UUID);
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID_TEMP(CHAR_UUID_TEMP);
static BLEUUID charUUID_HUMI(CHAR_UUID_HUMI);
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteChar_temp;
static BLERemoteCharacteristic* pRemoteChar_humi;
static BLEAdvertisedDevice* myDevice;
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
String strCharUUID = pBLERemoteCharacteristic->getUUID().toString().c_str();
Serial.printf("Notify callback for characteristic: ");
strCharUUID.toUpperCase();
Serial.println(strCharUUID);
if(strCharUUID.equals(CHAR_UUID_TEMP)){
Serial.print("temp: ");
for (int i=0; i<length; i++)
Serial.print((char) pData[i]);
}else if(strCharUUID.equals(CHAR_UUID_HUMI)){
Serial.print("humi: ");
for (int i=0; i<length; i++)
Serial.print((char) pData[i]);
}
Serial.println();
}
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.
// if you pass BLEAdvertisedDevice instead of address,
// it will be recognized type of peer device address (public or private)
pClient->connect(myDevice);
Serial.println(" - Connected to server");
//set client to request maximum MTU from server (default is 23 otherwise)
pClient->setMTU(517);
// 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");
// Obtain a reference to the characteristic in the service
// of the remote BLE server.
pRemoteChar_temp = pRemoteService->getCharacteristic(charUUID_TEMP);
if (pRemoteChar_temp == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID_TEMP.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found temp characteristic");
pRemoteChar_humi = pRemoteService->getCharacteristic(charUUID_HUMI);
if (pRemoteChar_humi == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID_TEMP.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found humi characteristic");
// Read the value of the characteristic.
if(pRemoteChar_temp->canRead()) {
std::string value = pRemoteChar_temp->readValue();
Serial.print("The characteristic temp value was: ");
Serial.println(value.c_str());
}
if(pRemoteChar_humi->canRead()) {
std::string value = pRemoteChar_humi->readValue();
Serial.print("The characteristic humi value was: ");
Serial.println(value.c_str());
}
if(pRemoteChar_temp->canNotify())
pRemoteChar_temp->registerForNotify(notifyCallback);
if(pRemoteChar_humi->canNotify())
pRemoteChar_humi->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());
if(advertisedDevice.haveServiceUUID()){
Serial.println("- haveServiceUUID()");
}
if(advertisedDevice.isAdvertisingService(serviceUUID)){
Serial.println("- isAdvertisingService(serviceUUID)");
}
// We have found a device,
// let us now see 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;
Serial.println("*** device found ***");
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
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);
} // End of setup.
// This is the Arduino main loop function.
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()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server.");
}
doConnect = false;
}
// If we are connected to a peer BLE Server,
// update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
}else if(doScan){
// this is just example to start scan after disconnect,
// most likely there is better way to do it in arduino
BLEDevice::getScan()->start(0);
}
delay(1000); // Delay a second between loops.
} // End of loop
/**
* modified from BLE_client
* to work with ESP32_DHT_ST789_graphic_BLE.ino,
* to monitor temp/humi.
* and display on SSD1306 I2C OLED
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SDA_pin 3
#define SCL_pin 2
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C //0x3D ///
Adafruit_SSD1306 display;
#define SERVICE_UUID "FC8601FC-7829-407B-9C2E-4D3F117DFF2D"
#define CHAR_UUID_TEMP "0FD31907-35AE-4BB0-8AB1-51F98C05B326"
#define CHAR_UUID_HUMI "D01D3AF7-818D-4A90-AECF-0E1EC48AA5F0"
// The remote service we wish to connect to.
static BLEUUID serviceUUID(SERVICE_UUID);
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID_TEMP(CHAR_UUID_TEMP);
static BLEUUID charUUID_HUMI(CHAR_UUID_HUMI);
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteChar_temp;
static BLERemoteCharacteristic* pRemoteChar_humi;
static BLEAdvertisedDevice* myDevice;
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
String strCharUUID = pBLERemoteCharacteristic->getUUID().toString().c_str();
Serial.printf("Notify callback for characteristic: ");
strCharUUID.toUpperCase();
Serial.println(strCharUUID);
if(strCharUUID.equals(CHAR_UUID_TEMP)){
Serial.print("temp: ");
String strTemp = (char*)pData;
Serial.print(strTemp);
displayTemp(strTemp);
}else if(strCharUUID.equals(CHAR_UUID_HUMI)){
Serial.print("humi: ");
String strHumi = (char*)pData;
Serial.print(strHumi);
displayHumi(strHumi);
}
Serial.println();
}
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
displayPrompt("onConnect");
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
displayPrompt("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.
// if you pass BLEAdvertisedDevice instead of address,
// it will be recognized type of peer device address (public or private)
pClient->connect(myDevice);
Serial.println(" - Connected to server");
//set client to request maximum MTU from server (default is 23 otherwise)
pClient->setMTU(517);
// 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");
// Obtain a reference to the characteristic in the service
// of the remote BLE server.
pRemoteChar_temp = pRemoteService->getCharacteristic(charUUID_TEMP);
if (pRemoteChar_temp == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID_TEMP.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found temp characteristic");
pRemoteChar_humi = pRemoteService->getCharacteristic(charUUID_HUMI);
if (pRemoteChar_humi == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID_TEMP.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found humi characteristic");
// Read the value of the characteristic.
if(pRemoteChar_temp->canRead()) {
std::string value = pRemoteChar_temp->readValue();
Serial.print("The characteristic temp value was: ");
Serial.println(value.c_str());
displayTemp(value.c_str());
}
if(pRemoteChar_humi->canRead()) {
std::string value = pRemoteChar_humi->readValue();
Serial.print("The characteristic humi value was: ");
Serial.println(value.c_str());
displayHumi(value.c_str());
}
if(pRemoteChar_temp->canNotify())
pRemoteChar_temp->registerForNotify(notifyCallback);
if(pRemoteChar_humi->canNotify())
pRemoteChar_humi->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());
if(advertisedDevice.haveServiceUUID()){
Serial.println("- haveServiceUUID()");
}
if(advertisedDevice.isAdvertisingService(serviceUUID)){
Serial.println("- isAdvertisingService(serviceUUID)");
}
// We have found a device,
// let us now see 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;
Serial.println("*** device found ***");
displayPrompt("found");
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void initScreen(){
Wire.setPins(SDA_pin,SCL_pin);
display = Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.display();
delay(500);
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE); // Draw white text
display.cp437(true); // Use full 256 char 'Code Page 437' font
display.setCursor(10, 10);
display.printf("ESP32C3");
display.setCursor(10, 28);
display.printf("BLE");
display.fillRect(0, 0, display.width()-1, display.height()-1, SSD1306_INVERSE);
display.display();
delay(3000);
// Clear the buffer
display.clearDisplay();
display.display();
}
void displayTemp(String temp){
//erase display of old temp
display.fillRect(40, 10, 80, 18, SSD1306_BLACK);
display.setCursor(10, 10);
display.setTextSize(1);
display.print("temp");
display.setCursor(40, 10);
display.setTextSize(2);
display.print(temp);
display.display();
}
void displayHumi(String humi){
//erase display of old humi
display.fillRect(40, 30, 80, 18, SSD1306_BLACK);
display.setCursor(10, 30);
display.setTextSize(1);
display.print("humi");
display.setCursor(40, 30);
display.setTextSize(2);
display.print(humi);
display.display();
}
void displayPrompt(String prompt){
//erase display of old temp
display.fillRect(10, 10, 110, 40, SSD1306_BLACK);
display.setCursor(40, 10);
display.setTextSize(1);
display.print(prompt);
display.display();
}
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
initScreen();
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);
displayPrompt("Scan");
Serial.println("Scan");
} // End of setup.
// This is the Arduino main loop function.
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()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server.");
}
doConnect = false;
}
// If we are connected to a peer BLE Server,
// update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
}else if(doScan){
// this is just example to start scan after disconnect,
// most likely there is better way to do it in arduino
displayPrompt("Scan");
Serial.println("Scan");
BLEDevice::getScan()->start(0);
}
delay(1000); // Delay a second between loops.
} // End of loop
Exercise on ESP32 (Arduino framework) work with DHT11 temperature & humidity sensor, with display on
ST7789 SPI LCD and also with BLE function.
The DHT11 (or DHT22 and similar) are cheap temperature and humidity
sensors. The communicate with a uC is over a single wire.
The electric connection to the ESP32 is very simple, as the DHT series can
be powered direct with 3.3V. Only 3 wires are needed: VCC, GND and the data
line.
Connection between DHT11 and ESP32 (GPIO#)
-----------------------------------------------
DHT11 ESP32
----- -----
VCC* 3V3
DATA** 32
NC
GND GND
* - depends on module, my DHT11 module is 3V3~5V operate.
** - depends on your module, maybe you have to add a
pull-up resistor (~10K Ohm) betwee DATA and VCC.
Connection between ST7789 SPI and ESP32 (GPIO#)
-----------------------------------------------
ST7789 SPI ESP32
---------- -----
GND GND
VCC 3V3
SCL 18
SDA 23
RES 26
DC 25
CS 33
BLK 3V3
Exercise code:
ESP32_DHT_ST789.ino, modified from DHT_ESP32 example of DHT sensor library for
ESPx, with interface to ST7789 SPI LCD.
#include "DHTesp.h"
#include <Ticker.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <Fonts/FreeMonoBold12pt7b.h>
#include <SPI.h>
#ifndef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP32 ONLY!)
#error Select ESP32 board.
#endif
/**************************************************************/
/* Example how to read DHT sensors from an ESP32 using multi- */
/* tasking. */
/* This example depends on the Ticker library to wake up */
/* the task every 5 seconds */
/**************************************************************/
DHTesp dht;
void tempTask(void *pvParameters);
bool getTemperature();
void triggerGetTemp();
/** Task handle for the light value read task */
TaskHandle_t tempTaskHandle = NULL;
/** Ticker for temperature reading */
Ticker tempTicker;
/** Comfort profile */
ComfortState cf;
/** Flag if task should run */
bool tasksEnabled = false;
/** Pin number for DHT11 data pin */
int dhtPin = 32; //17;
#define TFT_CS 33
#define TFT_RST 26
#define TFT_DC 25
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
bool rqsUpdate = false;
TempAndHumidity updateValues;
/**
* initTemp
* Setup DHT library
* Setup task and timer for repeated measurement
* @return bool
* true if task and timer are started
* false if task or timer couldn't be started
*/
bool initTemp() {
byte resultValue = 0;
// Initialize temperature sensor
dht.setup(dhtPin, DHTesp::DHT11);
Serial.println("DHT initiated");
// Start task to get temperature
xTaskCreatePinnedToCore(
tempTask, /* Function to implement the task */
"tempTask ", /* Name of the task */
4000, /* Stack size in words */
NULL, /* Task input parameter */
5, /* Priority of the task */
&tempTaskHandle, /* Task handle. */
1); /* Core where the task should run */
if (tempTaskHandle == NULL) {
Serial.println("Failed to start task for temperature update");
return false;
} else {
// Start update of environment data every 20 seconds
tempTicker.attach(5, triggerGetTemp);
}
return true;
}
/**
* triggerGetTemp
* Sets flag dhtUpdated to true for handling in loop()
* called by Ticker getTempTimer
*/
void triggerGetTemp() {
if (tempTaskHandle != NULL) {
xTaskResumeFromISR(tempTaskHandle);
}
}
/**
* Task to reads temperature from DHT11 sensor
* @param pvParameters
* pointer to task parameters
*/
void tempTask(void *pvParameters) {
Serial.println("tempTask loop started");
while (1) // tempTask loop
{
if (tasksEnabled) {
// Get temperature values
getTemperature();
}
// Got sleep again
vTaskSuspend(NULL);
}
}
/**
* getTemperature
* Reads temperature from DHT11 sensor
* @return bool
* true if temperature could be aquired
* false if aquisition failed
*/
bool getTemperature() {
// Reading temperature for humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
TempAndHumidity newValues = dht.getTempAndHumidity();
// Check if any reads failed and exit early (to try again).
if (dht.getStatus() != 0) {
Serial.println("DHT11 error status: " + String(dht.getStatusString()));
return false;
}
float heatIndex = dht.computeHeatIndex(newValues.temperature, newValues.humidity);
float dewPoint = dht.computeDewPoint(newValues.temperature, newValues.humidity);
float cr = dht.getComfortRatio(cf, newValues.temperature, newValues.humidity);
String comfortStatus;
switch(cf) {
case Comfort_OK:
comfortStatus = "Comfort_OK";
break;
case Comfort_TooHot:
comfortStatus = "Comfort_TooHot";
break;
case Comfort_TooCold:
comfortStatus = "Comfort_TooCold";
break;
case Comfort_TooDry:
comfortStatus = "Comfort_TooDry";
break;
case Comfort_TooHumid:
comfortStatus = "Comfort_TooHumid";
break;
case Comfort_HotAndHumid:
comfortStatus = "Comfort_HotAndHumid";
break;
case Comfort_HotAndDry:
comfortStatus = "Comfort_HotAndDry";
break;
case Comfort_ColdAndHumid:
comfortStatus = "Comfort_ColdAndHumid";
break;
case Comfort_ColdAndDry:
comfortStatus = "Comfort_ColdAndDry";
break;
default:
comfortStatus = "Unknown:";
break;
};
Serial.println(" T:" + String(newValues.temperature)
+ " H:" + String(newValues.humidity)
+ " I:" + String(heatIndex)
+ " D:" + String(dewPoint)
+ " " + comfortStatus);
rqsUpdate = true;
updateValues = newValues;
return true;
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("DHT ESP32 example with tasks");
//init DHT
initTemp();
// Signal end of setup() to tasks
tasksEnabled = true;
//init ST7789
tft.init(240, 320); // Init ST7789 320x240
tft.setRotation(3);
tft.setFont(&FreeMonoBold12pt7b);
tft.setTextWrap(true);
tft.fillScreen(ST77XX_RED);
delay(300);
tft.fillScreen(ST77XX_GREEN);
delay(300);
tft.fillScreen(ST77XX_BLUE);
delay(300);
tft.fillScreen(ST77XX_BLACK);
delay(300);
tft.setCursor(0, 0);
tft.setTextColor(ST77XX_RED);
tft.setCursor(0, 0);
tft.setTextColor(ST77XX_RED);
tft.print("\n");
tft.print("ESP32 + DHT11 + ST7789\n");
}
void loop() {
if (!tasksEnabled) {
// Wait 2 seconds to let system settle down
delay(2000);
// Enable task that will read values from the DHT sensor
tasksEnabled = true;
if (tempTaskHandle != NULL) {
vTaskResume(tempTaskHandle);
}
}
if(rqsUpdate){
tft.fillRect(10, 30, 300, 53, ST77XX_BLACK);
tft.setCursor(5, 50);
tft.setTextColor(ST77XX_WHITE);
tft.print(" temperature: " + String(updateValues.temperature));
tft.setCursor(5, 75);
tft.setTextColor(ST77XX_WHITE);
tft.print(" humidity: " + String(updateValues.humidity));
Serial.println(" T:" + String(updateValues.temperature) + " H:" + String(updateValues.humidity));
rqsUpdate = false;
}
yield();
}
ESP32_DHT_ST789_graphic.ino, display on ST7789 SPI LCD with graph.
/*
* Execise run on ESP32 (ESP32-DevKitC V4) with arduino-esp32 2.0.1,
* read DHT11 Humidity & Temperature Sensor,
* and display on ST7789 SPI TFT, 2" IPS 240x320, with graph.
*
* Library needed:
* - DHT sensor library for ESPx by beegee_tokyo
* - Adafruit ST7735 and ST7789 Library by Adafruit
* - Adafruit GFX Library by Adafruit
*
* Modify from examples DHT_ESP32 of DHT sensor library for ESPx
*
* Connection between DHT11 and ESP32 (GPIO#)
* -----------------------------------------------
* DHT11 ESP32
* ----- -----
* VCC* 3V3
* DATA** 32
* NC
* GND GND
*
* * - depends on module, my DHT11 module is 3V3~5V operate.
*
* ** - depends on your module, maybe you have to add a
* pull-up resistor (~10K Ohm) betwee DATA and VCC.
*
* Connection between ST7789 SPI and ESP32 (GPIO#)
* -----------------------------------------------
* ST7789 SPI ESP32
* ---------- -----
* GND GND
* VCC 3V3
* SCL 18
* SDA 23
* RES 26
* DC 25
* CS 33
* BLK 3V3
*
*/
#include "DHTesp.h"
#include <Ticker.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <Fonts/FreeMonoBold12pt7b.h>
#include <SPI.h>
#ifndef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP32 ONLY!)
#error Select ESP32 board.
#endif
DHTesp dht;
void tempTask(void *pvParameters);
bool getTemperature();
void triggerGetTemp();
/** Task handle for the light value read task */
TaskHandle_t tempTaskHandle = NULL;
/** Ticker for temperature reading */
Ticker tempTicker;
/** Comfort profile */
ComfortState cf;
/** Flag if task should run */
bool tasksEnabled = false;
/** Pin number for DHT11 data pin */
int dhtPin = 32; //17;
//hardware SPI MOSI 23
//hardware SPI SCK 18
#define TFT_CS 33
#define TFT_RST 26
#define TFT_DC 25
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
bool rqsUpdate = false;
TempAndHumidity updateValues;
unsigned long prvUpdateMillis;
#define FRAME_TOPX 0
#define FRAME_TOPY 200
#define FRAME_WIDTH 240
#define FRAME_HEIGHT 100
#define FRAME_BOTTOMY FRAME_TOPY + FRAME_HEIGHT
#define SCR_HEIGHT 320
int idx = 0;
#define IDX_MAX 240
/**
* initTemp
* Setup DHT library
* Setup task and timer for repeated measurement
* @return bool
* true if task and timer are started
* false if task or timer couldn't be started
*/
bool initTemp() {
byte resultValue = 0;
// Initialize temperature sensor
dht.setup(dhtPin, DHTesp::DHT11);
Serial.println("DHT initiated");
// Start task to get temperature
xTaskCreatePinnedToCore(
tempTask, /* Function to implement the task */
"tempTask ", /* Name of the task */
4000, /* Stack size in words */
NULL, /* Task input parameter */
5, /* Priority of the task */
&tempTaskHandle, /* Task handle. */
1); /* Core where the task should run */
if (tempTaskHandle == NULL) {
Serial.println("Failed to start task for temperature update");
return false;
} else {
// Start update of environment data every XX seconds
tempTicker.attach(2, triggerGetTemp);
}
return true;
}
/**
* triggerGetTemp
* Sets flag dhtUpdated to true for handling in loop()
* called by Ticker getTempTimer
*/
void triggerGetTemp() {
if (tempTaskHandle != NULL) {
xTaskResumeFromISR(tempTaskHandle);
}
}
/**
* Task to reads temperature from DHT11 sensor
* @param pvParameters
* pointer to task parameters
*/
void tempTask(void *pvParameters) {
Serial.println("tempTask loop started");
while (1) // tempTask loop
{
if (tasksEnabled) {
// Get temperature values
getTemperature();
}
// Got sleep again
vTaskSuspend(NULL);
}
}
/**
* getTemperature
* Reads temperature from DHT11 sensor
* @return bool
* true if temperature could be aquired
* false if aquisition failed
*/
bool getTemperature() {
// Reading temperature for humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
TempAndHumidity newValues = dht.getTempAndHumidity();
// Check if any reads failed and exit early (to try again).
if (dht.getStatus() != 0) {
Serial.println("DHT11 error status: " + String(dht.getStatusString()));
return false;
}
float heatIndex = dht.computeHeatIndex(newValues.temperature, newValues.humidity);
float dewPoint = dht.computeDewPoint(newValues.temperature, newValues.humidity);
float cr = dht.getComfortRatio(cf, newValues.temperature, newValues.humidity);
String comfortStatus;
switch(cf) {
case Comfort_OK:
comfortStatus = "Comfort_OK";
break;
case Comfort_TooHot:
comfortStatus = "Comfort_TooHot";
break;
case Comfort_TooCold:
comfortStatus = "Comfort_TooCold";
break;
case Comfort_TooDry:
comfortStatus = "Comfort_TooDry";
break;
case Comfort_TooHumid:
comfortStatus = "Comfort_TooHumid";
break;
case Comfort_HotAndHumid:
comfortStatus = "Comfort_HotAndHumid";
break;
case Comfort_HotAndDry:
comfortStatus = "Comfort_HotAndDry";
break;
case Comfort_ColdAndHumid:
comfortStatus = "Comfort_ColdAndHumid";
break;
case Comfort_ColdAndDry:
comfortStatus = "Comfort_ColdAndDry";
break;
default:
comfortStatus = "Unknown:";
break;
};
Serial.println(" T:" + String(newValues.temperature)
+ " H:" + String(newValues.humidity)
+ " I:" + String(heatIndex)
+ " D:" + String(dewPoint)
+ " " + comfortStatus);
rqsUpdate = true;
updateValues = newValues;
return true;
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("DHT ESP32 example with tasks");
//init DHT
initTemp();
// Signal end of setup() to tasks
tasksEnabled = true;
//init ST7789
tft.init(240, 320); // Init ST7789 320x240
tft.setRotation(2);
tft.setFont(&FreeMonoBold12pt7b);
tft.setTextWrap(true);
tft.fillScreen(ST77XX_RED);
delay(300);
tft.fillScreen(ST77XX_GREEN);
delay(300);
tft.fillScreen(ST77XX_BLUE);
delay(300);
tft.setCursor(0, 0);
tft.setTextColor(ST77XX_RED);
tft.print("\n");
tft.print("ESP32 + DHT11 + ST7789\n");
prvUpdateMillis = millis();
}
void loop() {
if (!tasksEnabled) {
// Wait 2 seconds to let system settle down
delay(2000);
// Enable task that will read values from the DHT sensor
tasksEnabled = true;
if (tempTaskHandle != NULL) {
vTaskResume(tempTaskHandle);
}
}
if(rqsUpdate){
unsigned long curUpdateMillis = millis();
tft.fillRect(0, 53, 240, 75, ST77XX_BLUE );
tft.setCursor(0, 70);
tft.setTextColor(ST77XX_WHITE);
tft.print(" temp.: " + String(updateValues.temperature));
tft.setCursor(0, 95);
tft.setTextColor(ST77XX_WHITE);
tft.print(" humi.: " + String(updateValues.humidity));
tft.setCursor(0, 115);
tft.setTextColor(ST77XX_WHITE);
tft.print(" mills.: " + String(curUpdateMillis-prvUpdateMillis));
prvUpdateMillis = curUpdateMillis;
if(idx==0){
tft.fillRect(FRAME_TOPX, FRAME_TOPY,
FRAME_WIDTH, SCR_HEIGHT-FRAME_TOPY,
ST77XX_BLUE);
}
tft.drawLine(
FRAME_TOPX+idx, FRAME_BOTTOMY,
FRAME_TOPX+idx, FRAME_BOTTOMY-(int)updateValues.temperature,
ST77XX_WHITE);
idx++;
if(idx >= IDX_MAX)
idx = 0;
Serial.println(" T:" + String(updateValues.temperature) + " H:" + String(updateValues.humidity));
rqsUpdate = false;
}
yield();
}
ESP32_DHT_ST789_graphic_BLE.ino, with display on ST7789 SPI LCD, and BLE
function added.
/*
Execise run on ESP32 (ESP32-DevKitC V4) with arduino-esp32 2.0.1,
read DHT11 Humidity & Temperature Sensor,
and display on ST7789 SPI TFT, 2" IPS 240x320, with graph.
BLE function added.
Library needed:
- DHT sensor library for ESPx by beegee_tokyo
- Adafruit ST7735 and ST7789 Library by Adafruit
- Adafruit GFX Library by Adafruit
Modify from examples DHT_ESP32 of DHT sensor library for ESPx
Connection between DHT11 and ESP32 (GPIO#)
-----------------------------------------------
DHT11 ESP32
----- -----
VCC* 3V3
DATA** 32
NC
GND GND
* * - depends on module, my DHT11 module is 3V3~5V operate.
* ** - depends on your module, maybe you have to add a
pull-up resistor (~10K Ohm) betwee DATA and VCC.
Connection between ST7789 SPI and ESP32 (GPIO#)
-----------------------------------------------
ST7789 SPI ESP32
---------- -----
GND GND
VCC 3V3
SCL 18
SDA 23
RES 26
DC 25
CS 33
BLK 3V3
*/
#include "DHTesp.h"
#include <Ticker.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <Fonts/FreeMonoBold12pt7b.h>
#include <SPI.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#ifndef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP32 ONLY!)
#error Select ESP32 board.
#endif
DHTesp dht;
void tempTask(void *pvParameters);
bool getTemperature();
void triggerGetTemp();
/** Task handle for the light value read task */
TaskHandle_t tempTaskHandle = NULL;
/** Ticker for temperature reading */
Ticker tempTicker;
/** Comfort profile */
ComfortState cf;
/** Flag if task should run */
bool tasksEnabled = false;
/** Pin number for DHT11 data pin */
int dhtPin = 32; //17;
//hardware SPI MOSI 23
//hardware SPI SCK 18
#define TFT_CS 33
#define TFT_RST 26
#define TFT_DC 25
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
bool rqsUpdate = false;
TempAndHumidity updateValues;
unsigned long prvUpdateMillis;
#define FRAME_TOPX 0
#define FRAME_TOPY 200
#define FRAME_WIDTH 240
#define FRAME_HEIGHT 100
#define FRAME_BOTTOMY FRAME_TOPY + FRAME_HEIGHT
#define SCR_HEIGHT 320
int idx = 0;
#define IDX_MAX 240
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
BLECharacteristic* pChar_temp = NULL;
BLECharacteristic* pChar_humi = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "FC8601FC-7829-407B-9C2E-4D3F117DFF2D"
#define CHAR_UUID_TEMP "0FD31907-35AE-4BB0-8AB1-51F98C05B326"
#define CHAR_UUID_HUMI "D01D3AF7-818D-4A90-AECF-0E1EC48AA5F0"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
BLEDevice::startAdvertising();
Serial.println("MyServerCallbacks.onConnect");
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("MyServerCallbacks.onDisconnect");
}
};
/**
initTemp
Setup DHT library
Setup task and timer for repeated measurement
@return bool
true if task and timer are started
false if task or timer couldn't be started
*/
bool initTemp() {
byte resultValue = 0;
// Initialize temperature sensor
dht.setup(dhtPin, DHTesp::DHT11);
Serial.println("DHT initiated");
// Start task to get temperature
xTaskCreatePinnedToCore(
tempTask, /* Function to implement the task */
"tempTask ", /* Name of the task */
4000, /* Stack size in words */
NULL, /* Task input parameter */
5, /* Priority of the task */
&tempTaskHandle, /* Task handle. */
1); /* Core where the task should run */
if (tempTaskHandle == NULL) {
Serial.println("Failed to start task for temperature update");
return false;
} else {
// Start update of environment data every XX seconds
tempTicker.attach(2, triggerGetTemp);
}
return true;
}
/**
triggerGetTemp
Sets flag dhtUpdated to true for handling in loop()
called by Ticker getTempTimer
*/
void triggerGetTemp() {
if (tempTaskHandle != NULL) {
xTaskResumeFromISR(tempTaskHandle);
}
}
/**
Task to reads temperature from DHT11 sensor
@param pvParameters
pointer to task parameters
*/
void tempTask(void *pvParameters) {
Serial.println("tempTask loop started");
while (1) // tempTask loop
{
if (tasksEnabled) {
// Get temperature values
getTemperature();
}
// Got sleep again
vTaskSuspend(NULL);
}
}
/**
getTemperature
Reads temperature from DHT11 sensor
@return bool
true if temperature could be aquired
false if aquisition failed
*/
bool getTemperature() {
// Reading temperature for humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
TempAndHumidity newValues = dht.getTempAndHumidity();
// Check if any reads failed and exit early (to try again).
if (dht.getStatus() != 0) {
Serial.println("DHT11 error status: " + String(dht.getStatusString()));
return false;
}
rqsUpdate = true;
updateValues = newValues;
return true;
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("DHT ESP32 example with tasks");
//init DHT
initTemp();
// Signal end of setup() to tasks
tasksEnabled = true;
//init BLE
// Create the BLE Device
BLEDevice::init("ESP32-DHT11");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic for temp and humi
pChar_temp = pService->createCharacteristic(
CHAR_UUID_TEMP,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pChar_humi = pService->createCharacteristic(
CHAR_UUID_HUMI,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pChar_temp->addDescriptor(new BLE2902());
pChar_humi->addDescriptor(new BLE2902());
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
//init ST7789
tft.init(240, 320); // Init ST7789 320x240
tft.setRotation(2);
tft.setFont(&FreeMonoBold12pt7b);
tft.setTextWrap(true);
tft.fillScreen(ST77XX_RED);
delay(300);
tft.fillScreen(ST77XX_GREEN);
delay(300);
tft.fillScreen(ST77XX_BLUE);
delay(300);
tft.setCursor(0, 0);
tft.setTextColor(ST77XX_RED);
tft.print("\n");
tft.print("ESP32 + DHT11 + ST7789\n");
prvUpdateMillis = millis();
}
void loop() {
if (!tasksEnabled) {
// Wait 2 seconds to let system settle down
delay(2000);
// Enable task that will read values from the DHT sensor
tasksEnabled = true;
if (tempTaskHandle != NULL) {
vTaskResume(tempTaskHandle);
}
}
if (rqsUpdate) {
unsigned long curUpdateMillis = millis();
tft.fillRect(0, 53, 240, 75, ST77XX_BLUE );
tft.setCursor(0, 70);
tft.setTextColor(ST77XX_WHITE);
tft.print(" temp.: " + String(updateValues.temperature));
tft.setCursor(0, 95);
tft.setTextColor(ST77XX_WHITE);
tft.print(" humi.: " + String(updateValues.humidity));
tft.setCursor(0, 115);
tft.setTextColor(ST77XX_WHITE);
tft.print(" mills.: " + String(curUpdateMillis - prvUpdateMillis));
prvUpdateMillis = curUpdateMillis;
if (idx == 0) {
tft.fillRect(FRAME_TOPX, FRAME_TOPY,
FRAME_WIDTH, SCR_HEIGHT - FRAME_TOPY,
ST77XX_BLUE);
}
tft.drawLine(
FRAME_TOPX + idx, FRAME_BOTTOMY,
FRAME_TOPX + idx, FRAME_BOTTOMY - (int)updateValues.temperature,
ST77XX_WHITE);
idx++;
if (idx >= IDX_MAX)
idx = 0;
char bufTemp[5];
char bufHumi[5];
//convert floating point value to String
dtostrf(updateValues.temperature, 0, 2, bufTemp);
dtostrf(updateValues.humidity, 0, 2, bufHumi);
pChar_temp->setValue((uint8_t*)bufTemp, 5);
pChar_temp->notify();
pChar_humi->setValue((uint8_t*)bufHumi, 5);
pChar_humi->notify();
//Serial.println(" T:" + String(updateValues.temperature) + " H:" + String(updateValues.humidity));
rqsUpdate = false;
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
Serial.println("disconnecting");
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
Serial.println("connecting");
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
yield();
}
ex_pyBLE.py, Python3 code run on Raspberry Pi 4B running 32-bit
Raspberry Pi OS (bullseye), to monitor temperature & humidity from
ESP32_DHT_ST789_graphic_BLE.ino.
# To install bluepy for Python3:
# $ sudo pip3 install bluepy
from bluepy import btle
from datetime import datetime
class MyDelegate(btle.DefaultDelegate):
def __init__(self, handleTemp, handleHumi):
self.handleTemp = handleTemp
self.handleHumi = handleHumi
btle.DefaultDelegate.__init__(self)
# ... initialise here
def handleNotification(self, cHandle, data):
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
if cHandle == self.handleTemp:
print("temp:", data, "@", current_time)
elif cHandle == self.handleHumi:
print("humi:", data, "@", current_time)
# Initialisation -------
address = "24:0A:C4:E8:0F:9A"
service_uuid = "FC8601FC-7829-407B-9C2E-4D3F117DFF2D"
char_uuid_temp = "0FD31907-35AE-4BB0-8AB1-51F98C05B326"
char_uuid_humi = "D01D3AF7-818D-4A90-AECF-0E1EC48AA5F0"
p = btle.Peripheral(address)
#p.setDelegate(MyDelegate())
# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID(service_uuid)
ch_temp = svc.getCharacteristics(char_uuid_temp)[0]
ch_humi = svc.getCharacteristics(char_uuid_humi)[0]
print("ch_temp handle", ch_temp.getHandle())
print("ch_humi handle", ch_humi.getHandle())
p.setDelegate(MyDelegate(ch_temp.getHandle(), ch_humi.getHandle()))
"""
Remark for setup_data for bluepy noification-
Actually I don't understand how come setup_data = b"\x01\x00",
and ch.valHandle + 1.
Just follow suggestion by searching in internet:
https://stackoverflow.com/questions/32807781/
ble-subscribe-to-notification-using-gatttool-or-bluepy
"""
setup_data = b"\x01\x00"
#ch.write(setup_data)
p.writeCharacteristic(ch_temp.valHandle + 1, setup_data)
p.writeCharacteristic(ch_humi.valHandle + 1, setup_data)
print("=== Main Loop ===")
while True:
if p.waitForNotifications(1.0):
# handleNotification() was called
continue
#print("Waiting...")
# Perhaps do something else here
remark: BUT, I found that ex_pyBLE.py run on Raspberry Pi is sometimes unstable; with error of: luepy.btle.BTLEDisconnectError: Failed to connect to peripheral ...