Thermal Flask that senses the inside temperature and sends the information to a receiver via Bluetooth.

Things used in this project

Hardware components
M5StickC ESP32-PICO Mini IoT Development BoardM5Stack M5StickC ESP32-PICO Mini IoT Development Board×1
Arduino 101Arduino 101×1
Software apps and online services
Arduino IDEArduino IDE


Hey, what’s up, Guys! Pradeep here

In this tutorial, I’m going to show you how to transfer the sensor readings between two ESP32 Boards via Bluetooth protocol.

BLE Smart Thermal Flask by M5Stack

Smart Thermal Flask

Things that you need:

  • M5Stick C
  • M5Stamp Pico
  • LM35 Temperature Sensor

M5Stick C (Source from M5Stack):

M5Stick C

M5StickC is a mini M5Stack, powered by ESP32. It is a portable, easy-to-use, open-source, IoT development board. What it can do? This tiny block is able to realize your idea, enlighten your creativity, and help with your IoT prototyping in a very short time. It will take away a lot of pains from the development process.M5stickC is one of the core devices in the M5Stack product series.

It is built in a continually growing hardware and software ecosystem. It has a lot of compatible modules and units, as well as the open-source code & engineering communities that will help you maximize your benefits in every step of the developing process.

——————————————–>Buy Link<—————————————————-

Power switch operation:

  • Power on: Press the power button for 2 seconds
  • Power off: Press the power button for 6 seconds


  • Baud rate supported by M5StickC: 1200 ~115200, 250K, 500K, 750K, 1500K

Product Features

  • ESP32-based
  • Built-in 6-Axis IMU
  • Red LED
  • IR transmitter
  • Microphone
  • Buttons, LCD(0.96 inch)
  • Built-in Lipo Battery
  • Extendable Socket
  • Wearable & Wall-mounted

Compatible with multi-platform development:

M5Stamp Pico (Source from M5Stack):

M5Stamp Pico

STAMP-PICO features an ultra-compact design with two low-power Xtensa® 32-bit LX6 microprocessors at 240MHz on a PCB as tiny and delicate as a postage stamp. low power consumption. It is ideal for any space-constrained or battery-powered devices such as wearables, medical devices, sensors, and other IoT devices.

1, MULTIFORM: 5 options of installation, means endless possibilities! (SMT, DIP, flywire, grove interface), with a high-temperature resistant plastic shell, 3D antenna and components can be better protected.

2, LOW-CODE DEVELOPMENT: STAMP-PICO supports UIFlow graphical programming platform, scripting-free, cloud push; and fully compatible with Arduino, MicroPython, ESP32-IDF, and other mainstream development platforms to quickly build various applications.

3, HIGH INTEGRATION: STAMP-PICO contains a 5V->3.3V DC/DC port, GPIOx12, programmable RGB light x1, button x1, finely tuned RF circuit, providing stable and reliable wireless communication.

4, STRONG EXPANDABILITY: Easy access to M5Stack’s hardware and software ecology system: a wealth of sensors, actuators, functional modules, and accessories to choose from, and extremely fast adaptation.

——————————————–>Buy Link<——————————————–

Product Features

  • Chip-set:ESP32-PICO-D4 (2.4GHz Wi-Fi dual mode)
  • Support UIFlow graphical programming
  • Multi-IO pinout, support multiple application forms (SMT, DIP, fly-by-wire)
  • Integrated programmable RGB LEDs and buttons
  • Miniature module

LM35 Temperature Sensor:


The LM35 is an integrated analog temperature sensor whose output is proportional to degrees centigrade. Sensors such as the LM35 do not require external calibration or trimming to provide typical accuracy. LM35’s low output impedance, a linear output, and accurate inherent calibration make it especially easy to interface with readouts and control circuits.

Hardware Connections:

Hardware Connection

Connect the LM35’s output pin to M5Stamp’s 36th pin and LM35’s power to 5 V and GND to GND of the PICO.

  • VOUT – > G36
  • GND – > GND
  • VCC – > 5V

That’s all we need for this tutorial.

Programming to the Transmitter:

In this tutorial, M5Stamp pico will act as a transmitter, and it will send a temperature reading to the transmitter once it is connected to the transmitter. You can access the code through the Github repository of the project from here. The file named “Transmitter.ino” is the code for the transmitter.

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <FastLED.h>

// LED Count
#define NUM_LEDS 1
#define DATA_PIN 27

// Define the array of leds

#define Button 39
//BLE server name
#define bleServerName "M5"

#define ADC_VREF_mV 3300.0 // in millivolt
#define ADC_RESOLUTION 4096.0
#define PIN_LM35 36 // ESP32 pin GIOP36 (ADC0) connected to LM35

float temp;
float tempF;
float hum;

// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

bool deviceConnected = false;

#define SERVICE_UUID "91bad492-b950-4226-aa2b-4ede9fa42f59"

// Temperature Characteristic and Descriptor
BLECharacteristic bmeTemperatureCelsiusCharacteristics("cba1d466-344c-4be3-ab3f-189f80dd7518", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor bmeTemperatureCelsiusDescriptor(BLEUUID((uint16_t)0x2902));

//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;

void setup() {
// Start serial communication

FastLED.addLeds<SK6812, DATA_PIN, RGB>(leds, NUM_LEDS);

pinMode(Button, INPUT);

// Create the BLE Device

// Create the BLE Server
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());

// Create the BLE Service
BLEService *bmeService = pServer->createService(SERVICE_UUID);

// Create BLE Characteristics and Create a BLE Descriptor
bmeTemperatureCelsiusDescriptor.setValue("BME temperature Celsius");

// Start the service

// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
Serial.println("Waiting a client connection to notify...");

void loop() {
static bool State;
State = digitalRead(Button);
if (State == 0)
if (deviceConnected) {
if ((millis() - lastTime) > timerDelay) {

// Turn the LED on, then pause
leds[0] = 0xf00000;;

// read the ADC value from the temperature sensor
int adcVal = analogRead(PIN_LM35);
// convert the ADC value to voltage in millivolt
float milliVolt = adcVal * (ADC_VREF_mV / ADC_RESOLUTION);
// convert the voltage to the temperature in °C
temp = milliVolt / 10;

static char temperatureCTemp[6];
dtostrf(temp, 6, 2, temperatureCTemp);
//Set temperature Characteristic value and notify connected client

// Now turn the LED off, then pause
leds[0] = 0x00f000;;

Serial.print("Temperature Celsius: ");
Serial.println(" ºC");

lastTime = millis();


In this code, if you need to change your BLE Server name then change this line as per your needs:

Server Name

And these lines of code contain the basic LM35’s calibration readings, don’t change these.

This is the BLE Service ID to the transmitter:

Here is the service ID for the temperature data.

M5Stamp Pico doesn’t have any reset button, but it has a user-defined button, here I used that one to reset the board.

Finally, upload the code to the M5Stamp Pico, and make sure you have selected the correct board settings:

Arduino IDE

Programming to the Receiver:

In this tutorial, M5Stick C will act as a receiver, and it will receive the temperature reading from the transmitter. You can access the code through the Github repository of the project from here. The file named “Receiver.ino” is the code for the receiver.

#include <M5StickC.h>
#include "BLEDevice.h"

//BLE Server name
#define bleServerName "M5"

/* UUID's of the service, characteristic that we want to read*/
// BLE Service
static BLEUUID bmeServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");

//Temperature Celsius Characteristic
static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");

//Flags stating if should begin connecting and if the connection is up
static boolean doConnect = false;
static boolean connected = false;

//Address of the peripheral device. Address will be found during scanning...
static BLEAddress *pServerAddress;

//Characteristicd that we want to read
static BLERemoteCharacteristic* temperatureCharacteristic;

//Activate notify
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};

//Variables to store temperature and humidity
char* temperatureChar;

//Flags to check whether new temperature and humidity readings are available
boolean newTemperature = false;

//Connect to the BLE Server that has the name, Service, and Characteristics
bool connectToServer(BLEAddress pAddress) {
BLEClient* pClient = BLEDevice::createClient();

// Connect to the remove BLE Server.
Serial.println(" - Connected to server");

// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(bmeServiceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
return (false);

// Obtain a reference to the characteristics in the service of the remote BLE server.
temperatureCharacteristic = pRemoteService->getCharacteristic(temperatureCharacteristicUUID);

if (temperatureCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID");
return false;
Serial.println(" - Found our characteristics");

//Assign callback functions for the Characteristics
return true;

//Callback function that gets called, when another device's advertisement has been received
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.getName() == bleServerName) { //Check if the name of the advertiser matches
advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //Address of advertiser is the one we need
doConnect = true; //Set indicator, stating that we are ready to connect
Serial.println("Device found. Connecting!");

//When the BLE Server sends a new temperature reading with the notify property
static void temperatureNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify) {
//store temperature value
temperatureChar = (char*)pData;
newTemperature = true;

//function that prints the latest sensor readings in the OLED display
void printReadings() {

//Erase the previous contents in the display

M5.Lcd.setCursor(20, 18);
M5.Lcd.print("[ ");
M5.Lcd.print("BLE Smart Flask");
M5.Lcd.print(" ]");
M5.Lcd.setCursor(18, 40);
M5.Lcd.print("Temperature: ");
M5.Lcd.print(" C");

M5.Lcd.setCursor(15, 55);

float x = atof(temperatureChar);
if (x >= 35.0)
Serial.println("It may be a over heat");
M5.Lcd.setCursor(15, 65);

M5.Lcd.print("It's may be over heat");
else {
Serial.println("It's ok to drink");
M5.Lcd.setCursor(30, 65);
M5.Lcd.print("It's ok to drink");

M5.Lcd.setCursor(15, 75);
void setup() {

M5.Lcd.setCursor(10, 35);

//Start serial communication
Serial.println("Starting Arduino BLE Client application...");

//Init BLE device

BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

void loop() {

if (doConnect == true) {
if (connectToServer(*pServerAddress)) {
Serial.println("We are now connected to the BLE Server.");
M5.Lcd.print("Connected to BLE Server");
//Activate the Notify property of each Characteristic
temperatureCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
connected = true;
} else {
Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
M5.Lcd.print("Not Connected");
doConnect = false;
//if new temperature readings are available, print in the OLED
if (newTemperature) {
newTemperature = false;

In this code, rename the transmitter name the same as yours, and make sure the device service ID and Temperature Data ID match with the transmitter.

I have added some lines of code to show the Temperature reading to the TFT Display.

Once done, with the needed changes, upload the code to the M5Stick C.

Board Settings

Code Upload Part

After uploading the code once, press the reset button on the transmitter and the receiver and monitor the serial outputs.


The sensor readings will be uploaded each 10 seconds


I added this with my coffee flask.

Flask Setup

Working Video:


Hardware Connections


Github repository for the project

Repo not found. Make sure the repo is public!