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.