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

Friday 12 February 2016

Configuring exim4 for AWS/Amazon/SES


Exim4 and AWS SES


So if you want to use Amazon SES to send out emails, you'll need to verify your email address under Identity management -> Email addresses, which should send you an email to verify you own the recipient address.

Create SES credentials 

Under Email settings -> SMTP settings.
Don't forget them!

Configure Exim


 dpkg-reconfigure exim4-config
 
  • mail sent by smarthost, received via SMTP or fetchmail
  • your fully qualified domain name (e.g. example.com)
  • 127.0.0.1   for listen address
  • your fully qualified domain name (e.g. example.com) for final destination
  • no relay servers
  • your AWS SES smtp server as outgoing smarthost.  Importantly, don’t use the default port of 25, as 25 is unencrypted, and exim4 won’t send passwords over unencrypted connections without messing around.  So, for example, you might have “email-smtp.us-east-1.amazonaws.com::587
  • Accept defaults for everything else

Create a file /etc/exim4/passwd.client.  This will give exim4 the logon credentials.  Importantly, amazon will resolve to a different server name each time (via the load balancer), so you can’t just put your smtp server name in here.  Your format should be something like:

# password file used when the local exim is authenticating to a remote
# host as a client.
#
# see exim4_passwd_client(5) for more documentation
#
# Example:
### target.mail.server.example:login:password
*.amazonaws.com:<smtp_username>:<smtp_password>
 
Also check your aliases file in /etc/aliases