Not so bad temperature meter. Using ESP8266 and a DHT11

In case you are a nut case and want everything monitored and under control, as I do, you NEED a temperature and humidity monitoring device that is at least semi-reliable, throwable, checkable, cheap and ugly as fuck.

Also, the results can be seen here: https://bastart.spoton.cz/temphumi/ and especially, the graphing for battery state (3rd graph from top): https://bastart.spoton.cz/status/

Search no more, as you have found it, read on and relive the ecstatic feelings of designing an ass-ripping ultra portable ESP8266 powered temperhumi meter for a couple [f|b]ucks.

Da parts.

When it comes to hardware selection, it was an obvious choice. What better a device to find than an ESP8266-12F in it’s all bare guts sticking out. The DHT11 is very inprecise, but cheap and easy to work with. Also, with every unit manufactured – a polar bear dies. Go get ’em. For the other stuff, a few capacitors, some resistors etc…

Heck, I shall put a list of all that shit for this MOFO project just here:

  • ESP8266-12E/F
  • DHT11, or even the more advanced DHT22 would work, I assume
  • battery case for 3x AAA sized batteries (1.2V NiMh preferred)
  • self-made PCB – more on that later
  • 2x10k, 1x2k7 resistors
  • 1x 100nF, 1x10uF capacitors
  • couple pin headers, if you want to have it fancy (DIN 2.54mm)
  • want a case? Get a case then

Da code.

As nobody cares about the hardware part, let’s look at the software part. My choice of language to fit this non-demanding and stupidly ugly project – YEAH! Python. Micro Python that is, as this small thing runs on these small things (i.e. uPython runs on ESP8266). No interrupts, no memory allocations, nothing of the stuff that grown-ups do with microprocessors.

Alright, alright, you expect a chunk of code, copy paste it and move on. Well, here you go then. **slab**

import dht
import machine
import utime
import urequests
import network
import json
import esp
from flashbdev import bdev

"""
Variables
--------------------------------------------------------------------------------
"""
# Do we want to measure internal or external? VCC / ADC
ADC_MODE_VCC = 255
ADC_MODE_ADC = 0
# Network credentials pulled from inet.conf residing on the FS
# options check
possible_opts = ['ssid', 'pass', 'ip', 'netmask', 'gw', 'destination', 'dns']
# Let's set location, will be set as a tag in influx
_LOCATION_TYPE = "Tea"
# INFLUX config - can be part of the config file, no?
_INFLUX_HOST = '192.168.8.15' 
_INFLUX_PORT = '8086'
_INFLUX_DB = 'indb'
_INFLUX_USER = 'pi'
_INFLUX_PASS = 'password'


"""
Functions
--------------------------------------------------------------------------------
"""

def set_adc_mode(mode):
    # Switch between internal and external ADC. Returns if restart is needed
    # Mode up in variables
    sector_size = bdev.SEC_SIZE
    flash_size = esp.flash_size() # device dependent
    init_sector = int(flash_size / sector_size - 4)
    data = bytearray(esp.flash_read(init_sector * sector_size, sector_size))
    if data[107] == mode:
        # flash is already correct; nothing to do
        return False
    else:
        data[107] = mode  # re-write flash
        esp.flash_erase(init_sector)
        esp.flash_write(init_sector * sector_size, data)
        print("ADC mode changed in flash; restart to use it!")
        return True

def check_wake_state():
    # check if the device woke from a deep sleep
    if machine.reset_cause() == machine.DEEPSLEEP_RESET:
        print('woke from a deep sleep')
    else:
        print(machine.reset_cause())
    utime.sleep_ms(10)

def deep_sleep(sleep_minutes):
    sleep_ms = sleep_minutes * 60 * 1000
    rtc = machine.RTC()
    rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP)
    rtc.alarm(rtc.ALARM0, sleep_ms)
    machine.deepsleep()

def read_network_config():
    config_dict = {}
    try:
        with open('inet.conf', 'r') as conf_handler:
            config = conf_handler.readlines()
            for item in config:
                tmp_list = item.split("=")
                option = tmp_list[0].strip()
                if len(tmp_list) == 2 and option in possible_opts:
                    value = tmp_list[1].strip()
                    config_dict.update({option: value})
                else:
                    pass
    except Exception as e:
        print("WARNING: Errors in INFRA config, still going for AP")
        return False
    print(config_dict)
    return config_dict

def set_up_infra(_SSID, _PASS, _TIMEOUT):
    sta = network.WLAN(network.STA_IF)
    ap = network.WLAN(network.AP_IF)
    print("Disabling AP")
    ap.active(False)
    print("Activating INFRA")
    sta.active(True)
    sta.isconnected() # False, it should be # Comments by Yoda
    print("Connecting to infra")
    sta.connect(_SSID, _PASS)
    connection_tout = _TIMEOUT
    print("Let's wait for the network to come up") # Hopefully not indefinitely
    # That's why we have a nice timeout, so it does not drain the batt on con
    while not (sta.isconnected()):
        if connection_tout > 0:
            print("Trying... {} more times".format(connection_tout))
            utime.sleep_ms(500)
            connection_tout = connection_tout - 1
        else:
            print("Out of retrys while waiting for network, going to sleep")
            utime.sleep_ms(500)
            return False
    network_config = sta.ifconfig()
    return network_config

"""
Network configuration section
--------------------------------------------------------------------------------
"""
try:
    net_config = read_network_config()
    if net_config:
        net_conf_result = set_up_infra(net_config["ssid"],
                                       net_config["pass"],
                                       10)
    else:
        print("ERROR: cannot read from inet.conf file")
    utime.sleep_ms(100)
    if net_conf_result:
        print("Connected:")
        print("  IP: {}".format(net_conf_result[0]))
        print("  MASK: {}".format(net_conf_result[1]))
        print("  GW: {}".format(net_conf_result[2]))
        print("  DNS: {}\n".format(net_conf_result[3]))
    else:
        print("WARNING: Network config not done")
except Exception as e:
    print("ERROR: Network configuration failed with: {}".format(e))
print("Network section done")

"""
Measure temperhumi
--------------------------------------------------------------------------------
"""
print("getting the temperature here")
d1 = machine.Pin(5, machine.Pin.IN)
try:
    local_dht = dht.DHT11(d1)
    local_dht.measure()
    temperature = local_dht.temperature()
    humidity = local_dht.humidity()
    print("T: {}".format(temperature))
    print("H: {}".format(humidity))
except Exception as e:
    print("ERROR: Temp measurement: {}".format(e))
    temperature = 255
    humidity = 255

"""
Measure battery
--------------------------------------------------------------------------------
"""
print("Setting ADC mode here")
set_adc_mode(ADC_MODE_VCC)
vcc = machine.ADC(1)
val = vcc.read()
utime.sleep(2)
battery = vcc.read()


"""
Data send
--------------------------------------------------------------------------------
"""
if net_conf_result:
    post_str = "http://{}:{}/write?db={}&u={}&p={}"
    post = post_str.format(_INFLUX_HOST,
                           _INFLUX_PORT, 
                           _INFLUX_DB,
                           _INFLUX_USER,
                           _INFLUX_PASS)
    data_str = "usense,type={_type} humidity={_humi} \n " + \
                "usense,type={_type} temperature={_temp}\n " + \
                "usense,type={_type} battery={_batt}"
    data = data_str.format(_type=_LOCATION_TYPE,
                           _humi=humidity,
                           _temp=temperature,
                           _batt=battery)
    print(data)
    try:
        result = urequests.post(post, data=data)
    except Exception as e:
        print("ERROR: Data send 'urequest': {}".format(e))
        # Go to sleep, cause why the fuck not
        deep_sleep(28)
    print(result.text)
else:
    print("WARNING: Not sending data, as infra is not available")
# Sleep, deep. You earned it
deep_sleep(28)

So you want a better guide to the hardware part, eh? Wait for it and hold my beer…

How to put it together

You might want to read a couple articles first. namely http://wp.spoton.cz/2017/11/21/bricking-and-flashing-esp-01s-8266/ to see how to flash an ESP and http://wp.spoton.cz/2017/11/29/micropython-on-esp-01-8266/ on how to setup micropython on an ESP.

Leave a Reply

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