Tuesday, February 16, 2016

OpenHAB living room lights automation

Since I am the proud owner of five ESP8266 modules I was wondering how to make best use of them. After trying NodeMCU and Arduino I finaly tipped over to the NodeMCU side. I don't really like NodeMCU since I want more control over the hardware but it is simple enough and has some working libraries that really speed things up.

For starters I uploaded the firmware that I downloaded from nodemcu-build.com with only the absolutely essential modules enabled, plus mqtt and ow in order to be able to read from a Dallas DS18B20S OneWire digital temperature sensor. MQTT is the protocol developed for the Internet of Things (IoT) and it is a really convenient way for communication between the server and the IoT device. Further information on MQTT you can find here.

Then I set up the server. I am using an old Pentium4@3GHz PC with 1GB RAM with Ubuntu Server 14.04 LTS operating system installed. I installed mosquito which is the MQTT server, mysql for data persistence and openhab which is the main framework that provides the user interface and binds everything together.

ESP-01 module

On the ESP8266 side I used the two GPIO. GPIO0 was used to read the DS18B20S and GPIO2 was used to control a MOSFET, through a resistor of course, connected to the LEDs. The files I uploaded to the module are the following:
init.lua

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
--Init  
 DeviceID="esp01"  
 RoomID="LR"
 gpio0 = 3
 gpio2 = 4

 Broker="192.168.X.X"
 t = require("ds18b20")
 t.setup(gpio0)addrs = t.addrs()
 pwm.setup(gpio2,100,0)

 m = mqtt.Client("ESP8266".. DeviceID, 180, "user", "password")  
 m:lwt("/lwt", "ESP8266", 0, 0)  
 m:on("offline", function(con)   
    print ("Mqtt Reconnecting...")   
    tmr.alarm(1, 10000, tmr.ALARM_SINGLE, function()  
      m:connect(Broker, 1883, 0, function(conn)   
        print("Mqtt Connected to:" .. Broker)  
        mqtt_sub() --run the subscription function  
      end)  
    end)  
 end)  

 tmr.alarm(2,10000,tmr.ALARM_AUTO, function()
   lrtemp = t.read()
   print ("Temperature: "..lrtemp.."'C")
   m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p2/state",lrtemp,0,0)
 end)

 -- on publish message receive event  
 m:on("message", function(conn, topic, data)   
    print("Recieved:" .. topic .. ":" .. data)   
      if (data=="ON") then  
      print("Enabling Output")   
      pwm.setduty(gpio2,1023)
      m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","ON",0,0)  
    elseif (data=="OFF") then  
      print("Disabling Output")   
      pwm.setduty(gpio2,0)
      m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state","OFF",0,0)  
    elseif ((tonumber(data)>=0) and (tonumber(data)<=100)) then
      print("Setting PWM to " .. data)
      pwm.setduty(gpio2,((data * 1024) / 100) - 1)
      m:publish("/home/".. RoomID .."/" .. DeviceID .. "/p1/state",data .. "%",0,0)
    else  
      print("Invalid - Ignoring")   
    end   
 end)  
 function mqtt_sub()  
    m:subscribe("/home/".. RoomID .."/" .. DeviceID .. "/p1/com",0, function(conn)   
      print("Mqtt Subscribed to OpenHAB feed for device " .. DeviceID)  
    end)  
 end  
 tmr.alarm(0, 1000, tmr.ALARM_AUTO, function()  
  if wifi.sta.status() == 5 and wifi.sta.getip() ~= nil then  
    tmr.stop(0)  
    m:connect(Broker, 1883, 0, function(conn)   
      print("Mqtt Connected to:" .. Broker)  
      mqtt_sub() --run the subscription function  
    end)  
  end  
 end)

ds18b20.lua

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
--------------------------------------------------------------------------------
-- DS18B20 one wire module for NODEMCU
-- NODEMCU TEAM
-- LICENCE: http://opensource.org/licenses/MIT
-- Vowstar <vowstar@nodemcu.com>
-- 2015/02/14 sza2 <sza2trash@gmail.com> Fix for negative values
--------------------------------------------------------------------------------

-- Set module name as parameter of require
local modname = ...
local M = {}
_G[modname] = M
--------------------------------------------------------------------------------
-- Local used variables
--------------------------------------------------------------------------------
-- DS18B20 dq pin
local pin = nil
-- DS18B20 default pin
local defaultPin = 9
--------------------------------------------------------------------------------
-- Local used modules
--------------------------------------------------------------------------------
-- Table module
local table = table
-- String module
local string = string
-- One wire module
local ow = ow
-- Timer module
local tmr = tmr
-- Limited to local environment
setfenv(1,M)
--------------------------------------------------------------------------------
-- Implementation
--------------------------------------------------------------------------------
C = 0
F = 1
K = 2
function setup(dq)
  pin = dq
  if(pin == nil) then
    pin = defaultPin
  end
  ow.setup(pin)
end

function addrs()
  setup(pin)
  tbl = {}
  ow.reset_search(pin)
  repeat
    addr = ow.search(pin)
    if(addr ~= nil) then
      table.insert(tbl, addr)
    end
    tmr.wdclr()
  until (addr == nil)
  ow.reset_search(pin)
  return tbl
end

function readNumber(addr, unit)
  result = nil
  setup(pin)
  flag = false
  if(addr == nil) then
    ow.reset_search(pin)
    count = 0
    repeat
      count = count + 1
      addr = ow.search(pin)
      tmr.wdclr()
    until((addr ~= nil) or (count > 100))
    ow.reset_search(pin)
  end
  if(addr == nil) then
    return result
  end
  crc = ow.crc8(string.sub(addr,1,7))
  if (crc == addr:byte(8)) then
    if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then
      -- print("Device is a DS18S20 family device.")
      ow.reset(pin)
      ow.select(pin, addr)
      ow.write(pin, 0x44, 1)
      -- tmr.delay(1000000)
      present = ow.reset(pin)
      ow.select(pin, addr)
      ow.write(pin,0xBE,1)
      -- print("P="..present)
      data = nil
      data = string.char(ow.read(pin))
      for i = 1, 8 do
        data = data .. string.char(ow.read(pin))
      end
      -- print(data:byte(1,9))
      crc = ow.crc8(string.sub(data,1,8))
      -- print("CRC="..crc)
      if (crc == data:byte(9)) then
        t = (data:byte(1) + data:byte(2) * 256)
        if (t > 32767) then
          t = t - 65536
        end

        if (addr:byte(1) == 0x28) then
          t = t * 625  -- DS18B20, 4 fractional bits
        else
          t = t * 5000 -- DS18S20, 1 fractional bit
        end

        if(unit == nil or unit == 'C') then
        else
          return nil
        end
        t = t / 10000
        return t
      end
      tmr.wdclr()
    else
    -- print("Device family is not recognized.")
    end
  else
  -- print("CRC is not valid!")
  end
  return result
end

function read(addr, unit)
  t = readNumber(addr, unit)
  if (t == nil) then
    return nil
  else
    return t
  end
end

-- Return module table
return M

Of course I changed the MQTT broker  (which is another name for server) address to the real one. Also there is a modification I made in the ds18b20.lua file, which I downloaded from the NodeMCU site,  because I installed the firmware which does not support float point numbers, and this is a choice I have absolutely no good reason for making it. There are two more things to be done, firstly provide a way to store the dimming percentage value and recall it on the ON command, and secondly add some error handling code. The fortunate thing about NodeMCU is that when it runs into an unhandled error it just restarts.

OpenHAB Main Menu
Now it is time to set up the server side of things. Below are the three configuration files :
default.sitemap

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
sitemap default label="Main Menu"
{
  Frame label="Home" {
    Switch item=lamp1 label="Living Room LEDs"
    Slider item=led1 label="Living Room Dimmer"
  }
  Frame label="Temperatures" {
    Text item=lrtemp {
      Frame {
        Chart item=lrtemp period=D refresh=30000
        Chart item=lrtemp period=W refresh=30000
      }
    }
  }  
}

default.items

1
2
3
4
5
6
7
Group All

Switch lamp1 "Living Room LEDs" (all) {mqtt=">[broker:/home/LR/esp01/p1/com:command:on:ON],>[broker:/home/LR/esp01/p1/com:command:off:OFF],<[broker:/home/LR/esp01/p1/state:state:default]"}

Dimmer  led1  "LR Dimmer [%s %%]"  (all) {mqtt=">[broker:/home/LR/esp01/p1/com:command:*:default"}

Number lrtemp "Living Room Temperature [%.1f]" (all) {mqtt="<[broker:/home/LR/esp01/p2/state:state:default]"}

mysql.persist

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Strategies {
    everyHour : "0 0 * * * ?"
    everyDay  : "0 0 0 * * ?"
    everyMinute : "0 * * * * ?"

    // if no strategy is specified for an item entry below, the default list will be used
    default = everyChange
}

Items {
    lrtemp : strategy = everyMinute
}

After everything is setup I can now sit on my favorite sofa and enjoy staring at the charts below for hours on end.
OpenHAB Living Room temperature chart
Thanks for reading this

Monday, January 25, 2016

Motion activated switch mains modification

A 3 Watt  LED is fine for a night light but wouldn't it be better to be able to switch a bigger load like the lights connected to the mains. Well I redesigned to act as 220 Volt motion activated switch, taking into consideration that I have a lot of junk laying around in my lab taking up space. So with the help of an old 5.7 Volt Nokia cell phone charger and a low voltage drop regulator (AMS1117 5V) I had exactly 5 Volts for the motion sensing sensor. The thing with that sensor is that it is a dirt cheap ebay ordered sensor with little to almost no documentation. In order to isolate the low voltage circuit from the high voltage circuit I used a MOC3061 from my stash and a BTA16 to do the actual switching. MOC3061 needs 15mA to signal the BTA16 to turn on which is not a lot so I figured that it is something the sensor's output could handle. I was wrong. The voltage rised to 1V when the sensor's output was high which very low for MOC3061 that needs at least 1.5V. Next step was to find something to amplify the signal, and what's better than the old trusty 2N2222A that a number of them I have in my possession for about 20 years. I actually bought them when I was in High school. One resistor to the base of the transistor and we are rolling. The motion sensor switch is now switching a big 30W CFL light bulb. This is the schematic diagram :
And here is a photo :

Wednesday, January 20, 2016

Motion activated night light

Last night our neighbors were robbed while being inside the house. Tonight I was feeling uneasy and wanted some way to be able to know if someone is in the house. So I searched my lab and found a motion sensor I had ordered a couple of years ago and a 3 Watt white LED. Laying around was also a 12 Volt power supply from an old router, a dirt cheap DC-DC converter that I had ordered in bulk and some resistors. There was only one ingredient missing, the MOSFET. I have an envelope filled with all kinds of electronic components in a TO-220 package. After a while and many 7805 regulators I found one that luckily could work with the 3.3V output of the motion sensor. I connected the power supply to the DC-DC converter and configured it to 4V accounting for the losses from the transistor. The converter's negative pin was connected to the sensor's negative pin and the transistor's source pin. The converter's positive pin was connected to the sensor's positive pin and the LED's positive pin. The sensor's output was connected to the translator's gate through a 220 Ohm resistor. Finally the LED's negative pin was connected to the transistor's drain pin. I double checked the connections and plugged it to the mains. And voila, a motion activated night light. Now I can sleep a little easier. Here is a picture of the contraption