NodeMCU and DHT11

Our third and fourth data are temperature and humidity. We will use a DHT11 sensor with the NodeMCU.

Get module and NodeMCU boilerplate from GitHub


I found out that DHT11 library can be compiled into a firmware. So this time no fancy code only usage of an available library.
But, my current firmware was without this lib. So a quick visit here: and a new firmware is ready.
Except for DHT I selected: bit, cjson, dht, file, gpio, i2c, net, node, spi, tmr, uart, wifi. Two minutes later I had an email with two links, one for integer and second for float version, we will use float.

Device updated and another problem! Critical exception in main.lua at line:

send_socket:connect(PORT, wifi.sta.getbroadcast())

There was an update in firmware and from now on we cannot preset target IP and port, they must be set in send function.
To fix this we need to delete this line, open network_message.lua and change socket:send (line 40) to:

socket:send(PORT, wifi.sta.getbroadcast(), json)  

Node is working again, we may focus on getting readings from DHT.

Our worker should respond to the incoming messages. But maybe it is a good idea to add an option to broadcast values at the defined time, like each 1 minute or 10 minutes.

As for the message, we will support one event: dht.readings and optionally broadcast


There are supposed to be three outputs, voltage, ground and signal. So why DHT has 4 pins? No idea:)

DHT           NodeMCU
1 -+----------- 3v
    \-[==]-\  4k7
2 ----------+-- G5

4 ------------- GND

Pin 1 goes to 3V (or 5V) Pin 2 to G5 (or any you select) and resistor 4k7 that wire pin 1 and 2.

Module and handler

Our first task is to get some reading from DHT and prepare a timer.

local mydht = {}
mydht.__index = mydht

setmetatable(mydht, {
    __call = function (cls, ...)

function, socket, interval)
    local self = setmetatable({}, mydht) = pin
    self.socket = socket
    if interval == nil then interval = 1000*60 end

    if self.socket ~= nil then
        self.tmr = tmr.create()
        self.tmr:register(interval, tmr.ALARM_AUTO, function()
            local readings = mydht.get_readings(self)
            if readings['temp'] ~= nil then        
                message = network_message.prepareMessage()            
                message.event = "dht.status"
                message.parameters = readings          
                network_message.sendMessage(socket, message)

    return self

function mydht:get_readings()
    status, temp, humi, temp_dec, humi_dec = dht.read11(
    if status ~= dht.OK then
        temp = nil
        humi = nil        

    return {["temp"] = temp, ["humi"] = humi}
return mydht

We start the timer only when the socket is passed to the constructor. If the node needs to work only in response mode just omit socket.
The interval defines how often node broadcast current readings, the default value is a 1 minute.

Check if we have readings:

network_message = require "network_message"
send_socket = net.createConnection(net.UDP, 0)
dht11 = require "dht11"
mydht = dht11(5, send_socket, 5000)
a = mydht:get_readings()
print(a['humi'], a['temp'])

Time for the handler. From what I see reading is not expensive so we won’t use a buffer. Our handler will read data directly from the sensor and return it.

local mydht_handler = {}
mydht_handler.__index = mydht_handler

setmetatable(mydht_handler, {
    __call = function (cls, ...)

    local self = setmetatable({}, mydht_handler)
    self.node = node
    return self

function mydht_handler:handle(socket, message)
    response = false
    if message ~= nil and message.event ~= nil then
        if message.event == 'dht.readings' then
            message = network_message.prepareMessage()
            message.response = self.node:get_readings()
            network_message.sendMessage(socket, message)
            response = true


    return response

return mydht_handler

We need to change main.lua :

network_message = require "network_message"
server_listener = require "server_listener"
send_socket = net.createConnection(net.UDP, 0)
dht11 = require "dht11"
mydht = dht11(5, send_socket, 5000)
a = mydht:get_readings()
print(a['humi'], a['temp'])

dht11_handler = require "dht11_handler"
dht_handler = dht11_handler(mydht)
server_listener.add("dht", dht_handler)

Let’s test our handler with hopefully familiar Python script:

import socket
import json

address = ('', 5053)
packet = {
    'protocol': 'iot:1',
    'node': 'computer',
    'chip_id': 'd45656b45afb58b1f0a46',
    'event': 'dht.readings',
    'targets': [

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
msg = json.dumps(packet)
s.sendto(msg.encode(), address)

(buf, ip) = s.recvfrom(1024)
print("from %s: %s" % (ip, buf.decode()))

And… nothing. That was a surprise! Tried few times and no response. A quick check for light sensor response and… nothing. Not good. This require some investigation, right Mr Watson? 🙂

Fixing network communication

Something more has changed with new firmware.
Yep, I was right, connection listener has more parameters, port and IP address. We probably need to send the response using them.
I had to pass them to handlers, and from handlers to network_message and there is a checker if we have values or we need to use default one. The default values are used when we are sending message and not response.
A quick test and.. uff, it is working.
Now I need to refactor all the handlers…


Seems it is working 🙂 Now we have a node that can return temperature and humidity, ideal info to display:)



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s