Showing posts with label IoT. Show all posts
Showing posts with label IoT. Show all posts

Monday, 15 February 2016

NodeMCU + DHT22 (ESP8266) wifi thermometer/humidity sensors pt2 with OLED

Wifi temperature / humidity sensor for under £20 with an OLED display

So I wanted a display output for the wifi temp modules, so I bought a few of these: http://www.ebay.co.uk/itm/111474769297?_trksid=p2057872.m2749.l2649&ssPageName=STRK%3AMEBIDX%3AIT
which are 128x64 displays with an i2c interface.It uses a multi-master protocol and has 2 x i2c lines which are SDA (serial data) and SCL (serial clock).

The reason for these, is that they use less power than a traditional LCD, have persistence when powering them, and are brighter than LCDs.


Code wise, I separated it into main code and a init loader (init being the first code loaded when the esp boots).
This basically 
Loader:
gpio.mode(1,gpio.OUTPUT)
gpio.write(1,gpio.HIGH)
wifi.setmode(wifi.STATION)
wifi.sta.config("wifi name","wifi passwd")
print (wifi.sta.getip())
gpio.write(1,gpio.LOW)
dofile("main.lua")


Main.lua
print("Running main")
gpio.mode(2,gpio.OUTPUT)
BLUE=1
gpio.mode(BLUE,gpio.OUTPUT)
gpio.write(BLUE,gpio.HIGH)
-- Set the LED to flash
pwm.stop(2)
pwm.setup(2,1,10)
pwm.start(2)
-- Load library

sda_pin = 5
scl_pin = 6
dht_pin = 4
oled_addr = 0x3c
-- Counter for heartbeat
cnt = 1
state = 0
-- global for display/read dht
humidtwo = 0
temptwo = 0
-- Heap limit
heap_limit= 22000

function init_OLED(sda,scl)
     sla = 0x3c
     i2c.setup(0, sda, scl, i2c.SLOW)
     disp = u8g.ssd1306_128x64_i2c(sla)
     disp:setFont(u8g.font_6x12)
     disp:setFontRefHeightExtendedText()
     disp:setDefaultForegroundColor()
     disp:setFontPosTop()
end
function read_sensor_values()
  local varh,vart
  dht22.read(dht_pin)
  varh = dht22.getHumidity()
  vart = dht22.getTemperature()
  if varh ~= nil then
    humidtwo = (varh/10).."."..(varh%10)
  else
    print ("Previous H : " ..humidtwo)
  end
  if vart ~= nil then
    temptwo = (vart/10).."."..(vart%10)
  else
    print ("Previous T : " ..temptwo)
  end
end

function display_sensor_values(vvar)
  disp:firstPage()
  disp:setFont(u8g.font_6x10)
  disp:setFontRefHeightExtendedText()
  disp:setDefaultForegroundColor()
  disp:setFontPosTop()
  local x,y,ip,nm,st,deg,result
  if state==1 then
    st=string.char(176)
  else
    st=" "
  end
  deg=string.char(176)
  repeat
    disp:drawRFrame(0, 0, 128-1, 64-1, 1)
    x=4
    y=8
    disp:drawStr(x, y, 'T:' .. (temptwo) .. deg ..'C   H:' .. (humidtwo) .. '%RH' )
    y = y+14
    disp:drawStr(x, y,  st .. ' H:' .. (node.heap()) .. ' C:' .. (cnt) )
    y = y+14
    if wifi.sta.status()==0 then result='STA_IDLE' end
    if wifi.sta.status()==1 then node.restart() end
    if wifi.sta.status()==2 then result='STA_WRONG PASSWD' end
    if wifi.sta.status()==3 then result='STA_NO AP FOUND' end
    if wifi.sta.status()==4 then result='STA_CONNECT FAILED' end
    if wifi.sta.status()==5 then result='STA_GOT_IP' end
    if vvar==1 then
      ssid,password,bssid_set,bssid=wifi.sta.getconfig()
      result= ssid
      ssid,password,bssid_set,bssid=nil,nil,nil,nil
    end
    disp:drawStr(x, y, result  )
    y = y+14
    ip,nm=wifi.sta.getip()
    if ip ~= nil then
        disp:drawStr(x, y, 'IP:' .. (ip)  )
    else
        disp:drawStr(x, y, 'IP: Cannot get IP')
        node.restart()
    end

  until disp:nextPage() == false

  cnt = cnt + 1
  if cnt > 999 then
     cnt=1
  end
end

init_OLED(5,6)

i2c.setup(0, sda_pin, scl_pin, i2c.SLOW)
disp = u8g.ssd1306_128x64_i2c(oled_addr)

  sv=net.createServer(net.TCP, 2)
  sv:listen(80,function(c)
      c:on("receive", function(c, pl)
         print(pl)
         if pl=="1" then
            print ("gpio1 low")
            gpio.write(1,gpio.LOW)
         end
         if pl=="2" then
            print ("gpio1 high")
            gpio.write(1,gpio.HIGH)
         end
      end)
        dht22 = require("dht22")
        gpio.mode(BLUE,gpio.OUTPUT)
        gpio.write(BLUE,gpio.HIGH)
        read_sensor_values()
        display_sensor_values()
        dht22=nil
        print("Humidity:    "..humidtwo.." %")
        print("Temperature: "..temptwo.." deg C")
        c:send("H:"..humidtwo.." ; T:"..temptwo.."\r\n")
        c:close()
        gpio.write(BLUE,gpio.LOW)
      
        hp=node.heap()
        if hp<5000 then
            node.restart()
        end
  
        collectgarbage()
       end)
     
tmr.alarm( 2, 1000, 1, function()
  if state == 0 then
    display_sensor_values(1)
  end
  if state == 1 then
    display_sensor_values(0)
  end
  state = (state + 1) %2
  collectgarbage()
end)
The code above requires dht22.lc to be loaded ( from here ), which should be compiled on the ESP2866, once uploaded as a LUA.

Doing a list should show something like this:
dht22.lc        : 1308 bytes
init.lua        : 199 bytes
main.lc         : 4176 bytes
main.lua        : 3521 bytes
The OLED display should be like so:
T: 25.3oC   H: 26.9%RH
* H:22824  C:288
STA_GOT_IP
IP: 10.10.0.164

So with this it will display the (T:) temperature in the top left, with the humidity level (H:) in the top right.
The next line shows a heartbeat icon, followed by heap size in bytes (H:).
I've programmed this so that if the heap space get below 5000 bytes, it will restart itself.
C: is the count, and when it reaches 999, it will go back to 1. This is purely to show it's doing some processing.
The next line alternates between the wifi status codes and the actual SSID it's connected to. This alternates every second.
The last line (IP:) shows the ip currently assigned to the device.

My output to RRD graph is something like this:



The drop outs are caused by the wifi signal (or lack of).
Part 1 here

Wednesday, 4 November 2015

NodeMCU + DHT22 (ESP8266) wifi thermometer/humidity sensors pt1

Wifi temperature / humidity sensor for under £20

Overview

So I travel a lot, and want to know what's happening in the house whilst I am abroad.
I've researched and there are many solutions out there (like here or here), but mostly they are way, way over priced at over £150 EACH per unit.

I need at least 5 units, for the bathroom, kitchen, boiler room etc. so even doing it with a raspberry pi would have been pricey at around £40-50 per 'sensor' (using the DHT22 for readings), so an outlay of about £250.

What I have found is something even better. The ESP8266. This is a self contained wifi module with built in TCP/IP stack for around £3 and under the size of a 50pence piece. Whoa - cheap eh? Ok, so the issue here is that it can't do a 5v-3v logic shift nor can I be bothered to figure out the electronics to step it down and connect it to USB TTL etc.

So.... I researched a bit more and there is a board that combines the ESP8266 and the step down and the USB TTL and allows me to either publish data via a message queue or host a webserver! This is the NodeMCU

Fantastic little thing that does everything you need it to + can be powered from a micro USB connection.

Parts list

ok so the parts list (you may not need to purchase everything, as you may have some parts already)
1 x NodeMCU (Amazon or ebay) - Beware of the version you get tho! Check the PCB colour and size.
1 x DHT22 temperature/humidity sensor (Amazon or ebay)
A breadboard /protoboard to prototype stuff (pimoroni) - you'll solder it onto this.
A solderless breadboard to test things work (pimoroni)
Soldering iron
Micro USB cable for connecting to a PC
some cheap micro usb charger
Wires for testing (Jumper jerky makes it easier)
Wires for permanent installation (pimoroni)

Total cost of the above is about £30 for the first unit. Cost of the 2nd unit will be about £13, 3rd unit about £13 too, so for 5 units, it's about £16 each. Not bad

Circuit diagram

 

Dev software

ESP software - for loading up LUAs and code

Firmware

Download the firmware

Flashing firmware

config tab
select first slot
flash on tab one
ensure the MAC addresses show. If not, then something went wron.
A green tick at the end shows success.

Modules

DHT22 from here

Upload it with esplorer from above using the 'upload' button. Then compile by clicking reload on the middle right side, then a list of modules will appear. Right click and select compile.

Code

Set the wifi access point and set NIC to DHCP

print(wifi.sta.getip())
--nil
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","password")
print(wifi.sta.getip())
--192.168.18.110
 
View wifi SSIDs
function listap(t)
  for k,v in pairs(t) do 
    print(k.." : "..v) 
  end 
end 
wifi.sta.getap(listap)

onboard LED

Flash the blue LED near the USB connector
gpio.write(0,gpio.LOW)
gpio.write(0,gpio.HIGH)

GPIO LEDs

Flash an LED on GPIO pin 2
You can change the duty cycles by checking the docs http://www.nodemcu.com/docs/
 pwm.setup(2,1,10)
pwm.start(2)

Reading temperature/humidity

PIN=4
dht22 = require("dht22")
dht22.read(PIN)
t = dht22.getTemperature()
h = dht22.getHumidity()
humi=(h/10).."."..(h%10)
temp=(t/10).."."..(t%10)
print("Humidity:    "..humi.." %")
print("Temperature: "..temp.." deg C")
dht22 = nil
package.loaded["dht22"]=nil

 
All together:
print(wifi.sta.getip())
gpio.mode(2,gpio.OUTPUT)
--gpio.write(2,gpio.HIGH)
--gpio.write(2,gpio.LOW)
--gpio.write(1,gpio.LOW)
gpio.write(0,gpio.LOW)
BLUE=1 -- blue LED is on GPIO 1
pwm.stop(2)
pwm.stop(1)

pwm.setup(2,1,10)
pwm.start(2)

gpio.write(0,gpio.HIGH)


  sv=net.createServer(net.TCP, 2) 
  sv:listen(80,function(c)
      c:on("receive", function(c, pl)
         print(pl) 
      end)
        gpio.mode(BLUE,gpio.OUTPUT)
        gpio.write(BLUE,gpio.HIGH)
        PIN=4
        dht22 = require("dht22")
        dht22.read(PIN)
        t = dht22.getTemperature()
        h = dht22.getHumidity()
        humi=(h/10).."."..(h%10)
        temp=(t/10).."."..(t%10)
        print("Humidity:    "..humi.." %")
        print("Temperature: "..temp.." deg C")
        dht22 = nil
        package.loaded["dht22"]=nil
        c:send("H:"..humi.." ; T:"..temp.."\r\n")
        c:close()
        gpio.write(BLUE,gpio.LOW)
       end)
Note the above server doesn't respond to normal HTTP requests.  it responds with a simple:
H:xx ; T: xx
and disconnects after the initial connect.
You can use either curl or netcat to get the data.
It also flashes the blue LED whilst a network operation is in effect.
The white LED I've used blinks every second, to show it is working.

Dashboard

RRDtool - Create the RRD

This creates a RRD for 5 sensors, values 0 to 100 and 524160 data points (60*24*7*52) so 1 data point a minute
<?
    echo "Creates blank temp graphs";
    $options = array (
     "--step", "60",            // Use a step-size of 1 minute
     "--start", "-1 day",         "DS:hum1:GAUGE:100:0:100",
     "DS:hum2:GAUGE:100:0:100",
     "DS:hum3:GAUGE:100:0:100",
     "DS:hum4:GAUGE:100:0:100",
     "DS:hum5:GAUGE:100:0:100",
     "RRA:AVERAGE:0.3:1:524160",
    );


    $ret = rrd_create("./hum.rrd", $options);
    if (! $ret) {
     echo "<b>Creation error: </b>".rrd_error()."\n";
    }
?>

In part two, I'll show how to render the graphs

Part 2 here