Battery-powered ESP32 IoT room thermometer with OLED display

A while back, I made a couple of ESP8266-based room thermometers. These have been beavering away happily ever since – one on the desk in my office and one in the living room (or salon as we say in France). The only significant change I’ve made to them is to add the capability for them to report temperatures to an InfluxDB database.

The TTGO ESP32 with 128×64 OLED display. And yes, this one is very dusty as it’s my development board. I’ll use a shiny new one for the final build.

There were a couple of downsides to them, though. First, they are quite big. I could fit them into smaller boxes if: a) I had the boxes; and b) I had the skills. I have neither. Second, they require an external 5V power supply.

When I first came across these ESP32-based dev boards with onboard OLED displays, my first thought was to create new, more compact room thermometers. The 128×64 displays are lovely and crisp, but tiny. Also, the board has a built-in battery connector on-board that, in addition to allowing powering from a battery, also lets you charge a Li-Ion cell, with overcharge/discharge protection.

As you can read in the post I just linked to, these boards – branded TTGO – are not without their quirks. Chief among these is that the I2C port is not on the regular pins for an ESP32. That caused me a lot of confusion, until I read about the trick of creating a TwoWire instance and passing it as a parameter when creating the display object. Read the other post for details – but this is something that would bite me again when it came to the temperature sensor.

I’m sensing something

With a built-in display, this board doesn’t need much else to act as a room thermometer. The main requirement, obviously, is some form of temperature sensor. With the previous thermometers I went with the cheap-and-cheerful DHT22. This gets a lot of flak for being inaccurate, which is somewhat justified, but largely irrelevant in use cases like mine. Nonetheless, I didn’t want to go that way again because the DHT22 is just too damn big.

The GY-21 board with the SHT21 sensor. It’s very small.

I vaguely remembered having bought some temperature sensors, and I was right! In the parts rack were some breakout boards rocking the unfortunately named (and frequently misspelled) SHT21 temperature and humidity sensor. This comes complete with an I2C interface and a promise of being easy to code for. We’ll see.

It’s also very small and very cheap. I think I paid under $3 a pop. The one I have is called a GY-21 breakout board.

Passing objects

First, let’s backtrack a little and revisit that business of TwoWire instances. You’ll see why in a moment. Instead of using ‘Wire’ in the code, here’s what I had to do. This is just partial code, of course (again, see that earlier post for a more in-depth explanation).

#define SDA 4 // Pin for I2C SDA
#define SCL 15 // Pin for I2C SCL
#define OLED_RESET 16
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// create our own TwoWire instance
TwoWire twi = TwoWire(1);

// create the display object, passing in the TwoWire object
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &twi, OLED_RESET);

Then, in the setup section of the code, we have:

// Get I2C started with the unusual pin assignments
twi.begin(SDA,SCL);

And that all works great.

Sensor library

Getting the sensor working wasn’t so easy. There’s a library called Environment available via the Arduino IDE. That didn’t work. And there’s one on GitHub just called SHT21 by e-radionica.com. That didn’t work either.

The problem is that they both assume that the I2C bus is going to be on the default pins – and we all know that’s not the case here.

I needed some way of passing in those pin assignments like we did for the display. Adafruit had kindly made that option possible for the SSD1306 display by having a constructor with a TwoWire instance as one of the parameters. I decided to hack the SHT21 library, renaming it SHT21_TTGO, to give it something similar. It didn’t take much.

I added two lines to the header file. I created a pointer to a TwoWire instance. And I declared the protoype for the constructor.

TwoWire *wire;
SHT21_TTGO(TwoWire *twi = &Wire);

That’s it for the header. In the .cpp file, I added the full constructor. I say ‘full’, but there’s actually nothing in the body of the method. I could have done it all in the header file, but … well, I didn’t.

SHT21_TTGO::SHT21_TTGO(TwoWire *twi): wire(twi ? twi : &Wire) {}

The final step was to change all instances of ‘Wire.’ to: wire-> – as in:

wire->beginTransmission(I2C_ADD);

Now, in my main code for the thermometer – and remembering that ‘twi’ has already been declared for use with the display (as above), we instantiate the sensor object thus:

SHT21_TTGO sht = SHT21_TTGO(&twi);

Now, everything works fine.

Code repos

You can see the code in all its glory in these two GitHub repos:

A couple of things to note about the thermometer code. One, it uses Adafruit GFX fonts. I’ve created a few custom fonts which are used here. So it’s not going to work straight out of the box for you except in the very unlikely situation that you have those exact same fonts. You’re going to have to do some hacking with regard to fonts to get this to function. You could always eliminate the font stuff and use the display’s built-in fonts.

For some reason, the IP address didn’t show in this image. It seems to have something to do with scan rates. Oh, and I’m going to need some kind of box.

Two, the code is peppered with Serial.print() commands. These are for debugging purposes. You can get rid of all of them.

Third, the code includes routines to send data to an InfluxDB database. You can cut those out. I’ve left them in because I plan to talk a lot more about InfluxDB and Grafana in the near future.

Lastly, I use external configuration files for the wifi credentials and InfluxDB connection paramters and credentials. I keep these two files as header-only libraries in the standard ‘libraries’ location for the Arduino IDE. That way I can just include them in any project. I’ve included sample files on GitHub (see below).

Battery power

The TTGO ESP32 boards are supplied with a battery connector and a plug with pigtails. All I needed to do to power the thermometer was connect those pigtail leads to a holder for an 18650 cell. I checked that the board does, indeed, charge the cell, albeit slowly. An LED on the board is bright while charging and dim when done.

All I need to do now is put this lot in some sort of case. If only I had the skill…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.