ESP01 and DHT11

When I wrote about ESP01 and relay module this was obvious, that some similar modules should be available. And yes, we have similar module, this time with DHT11 for temperature and humidity.

If You wonder how You could use them – I will help You. First, hardware. DHT11 uses just singe pin for communication, and in this module DHT11 is connected to GPIO02 of ESP01.

You could argue, that having all GPIOs brought out would be better, but… we have what we have. ESP01 placement can be a bit problematic since it covers DHT11 making airflow a little bit difficult. But as result we have very compact module (25 x 21 x 16 mm) accepting power supply in range 3.7 – 12 V.

So, you need to program it. Without all GPIOs you have to program ESP01 outside of module, and eventually use OTA after first time. Simplest way is to use special USB dongle for ESP01 modules.

But what to upload? To make life easier for you I have made simple example which connects to WiFi, read temperature and humidity periodically and make readings available as JSON. Platformio project is attached at end of this post. After You upload it to ESP01, place ESP01 in DHT module, provide power just navigate to dht.local (you can change this name) and will see something similar to:

So, 49% of humidity, temperature is 26.1°C and about 13.5 seconds has passed since measurement has been taken. OK, so what steps are required?

First, you need provide WiFi credentials. File for this is src/wifi.h which is not included in attached project. I don’t add such data to our repo with code, instead there is example file src/wifi-example.h. Edit it, provide SSID name and password then rename file as src/wifi.h. In this file you can change also default dht hostname if needed. Remember – your computer has to support MDNS/Bonjur. As a Linux user I don’t have to worry about that, not sure if Windows supports it oob. In case dht.local can not be found, then probably you have to check your router what IP address was assigned to ESP.

This is all You have to do – now just compile and upload code to ESP and it should work. I will explain a bit how it is working, but first one warning – in this code OTA updates (over the air) are enabled. Anyone who has access to configured WiFi can upload any code to ESP. If You don’t have control over who has access to this WiFi you can disable OTA before you upload code. Just comment all ArduinoOTA calls in setup and loop.

How it works?

In Arduino world first setup is being called. In this function you configure needed elements. First we connect to WiFi using credentials provided in wifi.h. This code you can find in all ESP8266 examples, so nothing new probably.

Next block enables Arduino OTA. As I wrote – if You don’t want have option to upload code via WiFi disable it.

ArduinoOTA.setHostname(HOSTNAME);
ArduinoOTA.begin(true);

First we set hostname which will be used by OTA. BTW that you can enter dht.local to reach ESP01 in your WiFi is a side effect of OTA enabled. Without OTA you can reach ESP01 just by its IP address (or you need to add MDNS support).

Second we start it, and true used as argument tells ArduinoOTA to broadcast its name. This is not all elements required to have OTA working. In loop you have to call ArduinoOTA.handle() with each iteration, or OTA won’t work.

Second object we initialize is ESP8266WebServer server(80) – object which handles all things needed for ESP8266 to act as simple webserver and provide JSON with measurements.

server.begin();
server.on("/", root_page);

begin just configures internals of ESP8266WebServer. Second line creates association between root page (which is represented by "/" path) and root_page function. Please note, that after root_page there is no parenthesis. If you would add them here, this function would be called here. You don’t want that here, you want to tell ESP8266WebServer that if client request for "/" path then call function root_page.

In more complicated web servers you will use server.on several times, each time making association between path in URL and function which should be called to handle this request.

Webserver, similar to OTA server has to work all the time, so again, in loop there has to be server.handleClient() call or webserver won’t be responding to requests.

Third block in setup handles DHT configuration. I have used DHTesp library, and right now I have noticed it is not maintained anymore. Works for me, but you may consider to use some other library.

DHTesp dht;

dht is object defined as global one at program beginning:

dht.setup(02, DHTesp::DHT11);
delay(dht.getMinimumSamplingPeriod());
getResults();

setup tells library that DHT is connected to GPIO 02 and that it is DHT11 sensor. Next we stop for a while to make sure DHT11 is ready to response. In case DHT11 it needs at least 1 second before next reading can be done.

After that we call getResults function which stores readings in global variables temperature and humidity. Let’s see how it looks:

void getResults() {
    temperature = dht.getTemperature();
    humidity = dht.getHumidity();
    last_measure = millis();
}

While storing measurements is self explanatory, last_measure store current time since ESP8266 start/reset. This value is returned by millis() (in milliseconds). We will later use that to know when we can call getResults again. last_measure is global variable.

Now examine loop function. Besides two lines for OTA and webserver handling whole code is:

if (millis() - last_measure > 20 * 1000) {
    getResults();
}

millis() - last_measure equals to number of milliseconds have passed since last call to getResults. If this is more than 20 seconds, then we call getResults again. This is a way to delay some actions (or do it periodically) and do not use dealy. Just store value of millis when You first done some action, and then calculate how much time has passed since that action. When it is bigger than required interval – do action again. But remember – you have store new value of millis in that variable. If you forget to update that value – with next check this condition will be still true and your action will be called with each time loop is called.

OK, so what have left? root_page – handler function. ESP8266WebServer parse incoming requests and when matches defined path with handler function then just call that function. Function has to prepare response and send to client. Simplest way to achieve that is keep server global variable then handler function (root_page in this case) can prepare response and send it to client using server.send method.

Now, we want to send JSON with data as response. I suggest you to use ArduinoJSON library. Even simple JSON can be tricky to generate if you want to keep it valid format, even when something unexpected will happen. To show you that I will generate JSON response “manually”:

resp = "{";
if (humidity != NAN) {
    resp.concat("\"rh\":");
    resp.concat(String(humidity));
}

if (temperature != NAN) {
    if (resp.length() > 2) {
        resp.concat(",");
    }
    resp.concat("\"temp\":");
    resp.concat(String(temperature));
}
if (resp.length() > 2) {
    resp.concat(",");
}
resp.concat("\"age\":");
resp.concat(String((millis() - last_measure) / 1000.0));
resp.concat("}");

In perfect world we will always have last value of RH or temperature. However, you may encounter some random events leading to not having valid reading. Thus, we check if we have that reading. For failed reading library should return special value – NAN (not an number) and in such case we don’t add value to JSON. But that lead to problem when adding second value. If humidity was not available, then we should not add comma before "temp". Lucky we – with such simple structure of JSON when we don’t have previous value, that means only opening bracket { is present. So number of characters in response is good enough as indicator whether we should add comma.

This is a reason you should really use ArduinoJSON if you want to generate proper JSON in more bulletproof way. With more complicated structure of JSON that will be real nightmare to generate it by hand. Use library for that.

We have now response stored in resp. Now we are sending it to client. Just:

server.send(200, "application/json", resp);

First argument is HTTP response status code. There is a lot of them and in my opinion, when you write requests handlers for ESP8266 you will use 200 (everything OK) maybe 204 (OK and there is empty response to client) 301/302 redirect to other URL. In case you are interested, read a full list on Wikipedia.

Second argument is so called media type. This is a way how web server says what can be expected inside a response, how client should understand response body. For JSON it is “application/json”, for HTML it will be “text/html” and so on… Last argument is just a response itself.

This response is very simple, but if yow will write more complex application which returns much more data (HTML can be quite verbose) maybe is worth to read about server.sendContent. This function allow to send data in chunks, allowing for better memory usage. With send you have to create whole response and then send it to client. It seems that 80 kB of RAM which ESP8266 has is a plenty, but can be easily depleted.

What else?

Writing server returning some data and running on ESP8266 is simple task. But if you’ have background in web development and would like to create more complex pages/applications running on ESP8266 – forget about that. Despite being much faster and resourceful than AVR Arduinos ESP8266 is still to small for such tasks. So, keep it simple – response in JSON, some simple, single request HTML is OK. But adding complexity will soon render such page into unresponsive mammoth (ESP8266WebServer is blocking and single threaded webserver).

OK, here is promised Platformio project with code to run on ESP01 DHT module.