사물인터넷(IoT)2016. 7. 26. 22:44

'NodeMCU Lua 사용해보기 #12 - 인터넷을 통한 릴레이 제어 #1' 글에서는 어떻게 인터넷을 통해서 릴레이(Relay)를 제어할 수 있는지 알아보았다. 그런데 사용을 하다보면 원격지에서 어떤 릴레이가 현재 ON 상태이고 OFF 상태인지 알 수 있는 방법이 없다. 만약 릴레이에 선풍기를 연결해 두었으면 현재 꺼진 상태인지 켜진 상태인지 알 수가 없는 것이다. 그래서 이번에는 NodeMCU 의 gpio 모듈을 이용해서 현재 릴레이의 상태를 알 수 있도록 소스를 수정해서 개선해 보았다. 이전 글에서 모든 연결과 조건은 동일하며 internet_relay.lua 의 소스만 수정하면 된다.


■ NodeMCU gpio Module Documentation

https://nodemcu.readthedocs.io/en/dev/en/modules/gpio/


위의 NodeMCU Documentation 사이트에 들어가면 gpio 모듈에 대한 설명이 나온다. 이 중에서 gpio.read() 를 이용해서 현재 릴레이의 상태를 알 수 있다.


위의 사이트에 들어가면 나오는 gpio.read() 의 사용설명이다. gpio.read(핀번호) 로 현재 해당 gpio 단자의 상태를 알 수 있다. 0 이면 Low, 1 이면 High 의 상태인 것이다. 릴레이에서는 High가 OFF, Low 가 ON 상태 이므로 릴레이 제어에 사용하는 4개의 gpio 핀의 상태를 읽어서 HTML 에서 출력해 주도록 internet_relay.lua 소스를 수정하면 될 것이다. 


■ 소스

인터넷 연결을 위한 credentials.lua 와 init.lua 의 소스는 이전 1편의 글과 동일하다.  internet_relay.lua 소스만 수정이 되었다. 그래도 모든 소스를 다시 올려 본다.


credentials.lua

1
2
3
4
-- WiFi Connect information
SSID = "WiFi Name(SSID)"
PASSWORD = "WiFi Password"
 
cs


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
-- http://deneb21.tistory.com/
-- load credentials, 'SSID' and 'PASSWORD' declared and initialize in there
dofile("credentials.lua")
 
function startup()
    if file.open("init.lua"== nil then
        print("init.lua deleted or renamed")
    else
        print("WiFi Connected...")
        file.close("init.lua")
        -- the actual application is stored in 'application.lua'
        --dofile("webserver.lua")
        dofile("internet_relay.lua")
    end
end
 
print("Connecting to WiFi access point...")
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID, PASSWORD)
wifi.sta.connect()
tmr.alarm(110001, function()
    if wifi.sta.getip() == nil then
        print("Waiting for IP address...")
    else
        tmr.stop(1)
        print("WiFi connection established, IP address: " .. wifi.sta.getip())
        print("You have 3 seconds to abort")
        print("Waiting...")
        tmr.alarm(030000, startup)
    end
end)
 
cs


internet_relay.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
--http://deneb21.tistory.com/
--Configure relay ouutput pins, pins are floating and relay opto needs ground to be activated. So pins are kept high on startup.
Relay1 = 1
Relay2 = 2
Relay3 = 3
Relay4 = 4
 
gpio.mode(Relay1, gpio.OUTPUT)
gpio.write(Relay1, gpio.HIGH);
gpio.mode(Relay2, gpio.OUTPUT)
gpio.write(Relay2, gpio.HIGH);
gpio.mode(Relay3, gpio.OUTPUT)
gpio.write(Relay3, gpio.HIGH);
gpio.mode(Relay4, gpio.OUTPUT)
gpio.write(Relay4, gpio.HIGH);
 
print("internet relay standby...")
 
--Create server and send html data, process request from html for relay on/off.
srv=net.createServer(net.TCP)
srv:listen(8080,function(conn) --change port number if required. Provides flexibility when controlling through internet.
    conn:on("receive", function(client,request)
        local html_buffer = "";
        local html_buffer1 = "";
        local html_buffer2 = "";
        local gpio1, gpio2, gpio3, gpio4
        
        local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
        if(method == nil)then
            _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
        end
        local _GET = {}
        if (vars ~= nil)then
            for k, v in string.gmatch(vars, "(%w+)=(%w+)&*"do
                _GET[k] = v
            end
        end
 
        html_buffer = html_buffer.."<html><head><meta http-equiv=\"Content-Language\" content=\"en-us\"><meta http-equiv=\"Content-Type\" content=\"text/html;\">";
        html_buffer = html_buffer.."</head><body><font size=5>Internet Relay1 Control (NodeMCU/Lua, ESP8266)</font></br>";
        html_buffer1 = html_buffer1.."<a href=\"?pin=ON1\"><button style=\"width:300\">Relay1 ON</button></a><a href=\"?pin=OFF1\"><button style=\"width:300\">Relay1 OFF</button></a><br/>";
        html_buffer1 = html_buffer1.."<a href=\"?pin=ON2\"><button style=\"width:300\">Relay2 ON</button></a><a href=\"?pin=OFF2\"><button style=\"width:300\">Relay2 OFF</button></a><br/>";
        html_buffer1 = html_buffer1.."<a href=\"?pin=ON3\"><button style=\"width:300\">Relay3 ON</button></a><a href=\"?pin=OFF3\"><button style=\"width:300\">Relay3 OFF</button></a><br/>";
        html_buffer1 = html_buffer1.."<a href=\"?pin=ON4\"><button style=\"width:300\">Relay4 ON</button></a><a href=\"?pin=OFF4\"><button style=\"width:300\">Relay4 OFF</button></a><br/>";
    
    
        local _on,_off = "",""
        if(_GET.pin == "ON1")then
              gpio.write(Relay1, gpio.LOW);
        elseif(_GET.pin == "OFF1")then
              gpio.write(Relay1, gpio.HIGH);
        elseif(_GET.pin == "ON2")then
              gpio.write(Relay2, gpio.LOW);
        elseif(_GET.pin == "OFF2")then
              gpio.write(Relay2, gpio.HIGH);
        elseif(_GET.pin == "ON3")then
              gpio.write(Relay3, gpio.LOW);
        elseif(_GET.pin == "OFF3")then
              gpio.write(Relay3, gpio.HIGH);
        elseif(_GET.pin == "ON4")then
              gpio.write(Relay4, gpio.LOW);
        elseif(_GET.pin == "OFF4")then
              gpio.write(Relay4, gpio.HIGH);                            
        end
 
        -- Relay Status Read
        if(gpio.read(1)==0) then gpio1 = "ON" else gpio1 = "OFF" end
        if(gpio.read(2)==0) then gpio2 = "ON" else gpio2 = "OFF" end
        if(gpio.read(3)==0) then gpio3 = "ON" else gpio3 = "OFF" end
        if(gpio.read(4)==0) then gpio4 = "ON" else gpio4 = "OFF" end
 
        -- Relay Status print
        print("gpio1:"..gpio1)
        print("gpio2:"..gpio2)
        print("gpio3:"..gpio3)
        print("gpio4:"..gpio4)
 
        -- Relay Status print html
        html_buffer2 = html_buffer2.."<br/>Relay1 : "..gpio1;
        html_buffer2 = html_buffer2.."<br/>Relay2 : "..gpio2;
        html_buffer2 = html_buffer2.."<br/>Relay3 : "..gpio3;
        html_buffer2 = html_buffer2.."<br/>Relay4 : "..gpio4;
        html_buffer2 = html_buffer2.."</body></html>";
        
        --Buffer is sent in smaller chunks as due to limited memory ESP8266 cannot handle more than 1460 bytes of data.
        client:send(html_buffer);
        client:send(html_buffer1);
        client:send(html_buffer2);        
        client:close();
        collectgarbage();
    end)
end)
 
cs


internet_relay.lua 소스를 보면 65행 에서부터 gpio.read() 모듈을 사용해서 해당 릴레이의 상태값을 얻어온다. 얻어온 값 (0 은 ON, 1 은 OFF) 에 따라 'ON' 또는 'OFF' 문자열을 할당한다. 그리고 html_buffer2 에 릴레이 상태 표시를 위한 HTML 을 만들어 준다. 기존의 html_buffer 와 html_buffer1 에 html_buffer2 를 붙여서 클라이언트(브라우저) 요청 시 HTML 을 출력해 준다.



 

동작 화면을 캡처해 보았다. 릴레이가 동작하는 장면도 같이 찍고 싶었지만 손이 3개면 가능하겠는데 2개라 찍지 못했다. 아무튼 잘 작동한다. 브라우저를 닫았다가 다시 열어도 잘 작동한다.


이것을 조금 더 발전시킨다면 인터넷으로 켜고 끌 수 있는 멀티콘센트도 충분히 만들 수 있을 것 같다. 물론 샤오미를 비롯해서 이미 많은 기업에서 Smart Plug 또는 WiFi Plug 라는 이름으로 이미 나와 있지만 말이다.


Xiaomi Smart Plug




▶추가사항(2016.09.30) : 위의 소스를 작성하면서 참고한 소스와 사이트를 정리해 본다.

http://www.instructables.com/id/WiFi-Internet-Controlled-Relays-using-ESP8266-Quic/

http://robokits.co.in/download/init.zip

init.zip

반응형
Posted by 대네브 (deneb)

댓글을 달아 주세요

  1. 먼저 포스트를 감사히 잘 보고 있습니다.

    다만 위 포스터를 읽는 중에 궁금한 것이 있어 질문 드립니다. local _, _, method, path, vars = string.find()열에서 local _, _, 는 무엇을 의미하는 것입니까? 또한 string.find가 원 문자열에서 찾고자 하는 문자열의 값을 반환하는 함수 같은데 위와 같은 사용형태에서는 어떻게 되는 지 가르쳐주실수 있을까요?

    2016.09.29 21:38 신고 [ ADDR : EDIT/ DEL : REPLY ]
    • 질문을 보고 오랜만에 소스를 들여다 봤습니다. 결론은 저도 잘 모르겠습니다. 죄송합니다. ^^;; string.find 로 뭔가를 찾아서 변수에 저장하긴 하는데 vars 만 뭔가에 쓰이고 나머지는 쓰이지도 않네요...;;; 필요가 없는 부분일수도 있습니다. 저도 ESP8266 을 하면서 Lua 라는 언어를 처음 봤고 저도 공부하는 입장이거든요.

      위 소스는 이 포스팅을 쓰면서 여러 곳에서 소스를 참고해서 제 나름대로 변형해서 구현한 것 입니다. 글 작성 당시 참고 출처를 기록해 놓지 않아서 한참 다시 구글링 해서 추가사항으로 포스팅에 적어 놓았습니다. 원 소스를 참고하시면 뭔가 무언가 도움이 될 것도 같습니다. 그리고 국내 사이트에 Lua 관련 자료가 너무 적어요. 혹시 무언지 알아내시면 저에게도 알려주시면 정말 감사하겠습니다.

      2016.09.30 13:00 신고 [ ADDR : EDIT/ DEL ]
  2. 릴레이

    안녕하세요!

    일단 제가 사용할 수 있는 iot (?) 기기를 만들 수 있게 포스팅 올려주신 부분 정말 감사합니다.
    처음에 nodemcu 보드 사고 어떻게 해야할지 막막했는데 찾다 보니 이런 블로그가 있어 쉽게 할 수 있었네요.
    그래서 제 생각에 맞게 여러 코드들을 수정하다보니 오류도 뜨고 헤매는 부분도 많았지만, 결국은 완성 시켰네요.

    그런데 추가하고 싶은 기능이 생겨 어언 2일을 찾다 적용시켜보고 해도 답이 나오지 않아 질문을 드립니다.

    제가 추가하고 싶은 기능은
    현재 인터넷으로 릴레이를 컨트롤 할 수 있습니다, 근데 여기에 또 tact 스위치(이하 스위치)를 1개를 달아서
    불이 켜져있을때 스위치를 클릭하면 불이 꺼지고 또 클릭하면 불이 켜지고. 토글로 꼈다 켰다 하는게
    추가하고 싶은 목적입니다. 예) http://deneb21.tistory.com/379 http://deneb21.tistory.com/378

    간단하게 말해 릴레이 토글입니다. (아마도?)

    딱 379번 포스팅하신게 제 목적에 맞는데 아두이노 IDE랑 nodemcu랑 조금 코드가 다른 것 같아서 적용을 시키기가
    어렵네요..;;

    다른 방법으로 적용시켜보니
    스위치를 누르니 꺼지기만 하더군요...ㄷㄷ

    제가 생각하는 토글기능을 사용할 수 있는 다른 방법이 있을까요?
    이 것에 대한 힌트를 얻고 싶어 질문드립니다.

    ------------
    사실 질문을 엄청 길게 썼었지만 너무 길게 쓰면 작성자님 시간을 뺏는것 같아서요 ㅎㅎ;;
    핵심만 썼습니다! (그래도 긴 편인것 같지만...ㅠㅠ)

    그리고 작성자님 덕분에 어려워 보이던 iot를 하네요, 이 포스팅 방법이 제일 쉬운 것 같아요!
    감사드립니다 (__)

    2017.01.15 03:02 [ ADDR : EDIT/ DEL : REPLY ]
    • 이 글에 있는 gpio 상태값 읽기와 스위치 관련글 보시면 감이 오실거에요. 하지만 두개의 글은 서로 언어가 다르죠. 아두이노는 C언어, nodemcu는 lua 라는 언어를 사용합니다. 그래서 lua문법을 좀 알아야 합니다. 그리고 일단 디바운스 개념은 빼고 스위치를 적용해 보는것도 방법일듯 합니다. 소스가 간단해 지거든요. 뺀다고 안되지는 않아요. 참고로 스위치 입력은 아마도 gpio 모듈을 적절하게 사용하면 구현이 가능할 것 같아요. https://nodemcu.readthedocs.io/en/master/en/modules/gpio/

      2017.01.15 09:24 신고 [ ADDR : EDIT/ DEL ]
    • 릴레이

      답변 감사드립니다.
      역시 lua 공부를 좀 해봐야겠어요.

      2017.01.16 04:39 [ ADDR : EDIT/ DEL ]
  3. 감사합니다 글 따라서 해보니 잘되네요
    그런데 소비전력이 적지 않을것 같아 배터리로 24시간 작동시키기 힘들 것 같은데, 소비전력을 최소로 하는 방법이 있을까요?

    2017.01.17 23:25 신고 [ ADDR : EDIT/ DEL : REPLY ]
    • ESP8266 에도 Sleep Mode 가 있습니다.
      이를 적절하게 이용하면 전력을 절약할 수 있습니다.
      저도 아직 해보진 않았네요.

      자세한 내용은 아래의 문서를 보세요.

      http://www.espressif.com/sites/default/files/9b-esp8266-low_power_solutions_en_0.pdf

      2017.01.17 23:34 신고 [ ADDR : EDIT/ DEL ]
  4. IoT공부중

    글 잘 보고있습니다!
    저는 이렇게 인터넷으로 제어도 할 수 있지만 안드로이드 어플로 제어하는 방법에 대해서 알고싶습니다.
    혹시 참고될 만한 게시글이 있을까요??

    2017.02.07 19:24 [ ADDR : EDIT/ DEL : REPLY ]
    • 아직 저의 블로그에서는 다루지 않았지만 대표적인 앱으로는 blynk 가 있습니다. 공부해 보시길 바랍니다

      2017.02.07 19:33 신고 [ ADDR : EDIT/ DEL ]
    • IoT공부중

      감사합니다. 그런데 방향이 좀 잘 안잡힙니다..

      안드로이드 어플을 사용해서 할때는 어플에서 버튼을 눌렀을 때 그 특정 값이 지정된 IP주소(서버)로 보내지게 코딩을 하고

      esplorer에서 그 서버에 온 값을 읽어서 판단하여 led등을 제어하게 코딩 해야할까요??

      2017.02.08 02:14 [ ADDR : EDIT/ DEL ]
    • 뭐 기본적인 개념은 그렇죠.
      App - Internet - ESP8266 - LED

      2017.02.08 17:39 신고 [ ADDR : EDIT/ DEL ]
  5. Sonoff 라고 보드 전원따로 연결할필요없이 만들어진 기성품이 있네요. nodemcu처럼 프로그래밍도 되나봐요

    2017.02.11 08:41 신고 [ ADDR : EDIT/ DEL : REPLY ]
  6. 1

    막힘없이 잘 진행되고있어요 감사합니다.
    그런데 혹시 이 웹서버에 비밀번호 시스템을 만드는 방법은 없을까요?
    이 게시글처럼 버튼을 눌러서 핀을 작동 시키는데, 비밀번호를 먼저 맞게 눌러야 진행이 되듯이.. 로그인처럼요.
    이렇게 웹서버를 그냥 사용한다면 외부에서 이 웹주소를 알게되면 누구나 사용이 가능하게 되버려서요..
    코딩을 직접해보는데 lua언어에 너무 익숙치가않네요..

    2017.02.15 23:06 [ ADDR : EDIT/ DEL : REPLY ]
    • 그건 웹프로그래밍의 문제 같네요. nodemcu 쪽에서 할 일은 아닌듯 합니다.

      2017.02.15 23:14 신고 [ ADDR : EDIT/ DEL ]
    • 1

      아.. esplorer에서 lua언어로는 로그인시스템을 만들순없는건가요?
      이미 웹서버를 이걸로 만들었는데 그럼 어디쪽을 손봐야하는거죠?ㅠㅠ

      2017.02.16 00:26 [ ADDR : EDIT/ DEL ]
    • 서버 접속할때 저 컨트롤화면을 보려면 로그인 절차를 거치도록 만들면 되겠죠?

      2017.02.16 09:43 신고 [ ADDR : EDIT/ DEL ]
    • 1

      https://nodemcu.readthedocs.io/en/dev/
      이 사이트에서 모듈쪽 보면서 코딩하고있습니다.

      만들어놓은 서버에 비밀번호를 입력할 수 있는 박스?를 따로 만들고 login버튼도 만들어서 버튼을 눌렀을 때 그 입력한 비밀번호를 읽어와서 가령 0000이면 LED를 켜는 버튼을 활성화 시키는 방향으로 하려합니다.

      LED버튼을 활성화하는것은
      if문을 써서 버튼쪽을 전부 묶어버리면 수월할 것 같은데 서버에 비밀번호 입력창을 띄우는 것과 그것을 읽어들이는 것 쪽이 너무어렵네요ㅠㅠ

      밤새 안자고 해봤는데 진전이 없습니다.. 혹시 코딩을 좀 도와주실수 있으신가요?ㅠㅠ

      2017.02.16 16:42 [ ADDR : EDIT/ DEL ]
    • 그것까지는 제가 도와드리지 못할 것 같아요.
      이 글과 별로 상관이 없는 웹프로그래밍의 영역이고
      '공지사항' 에도 적혀 있듯이 개인적으로 소스를 봐드리지는 않습니다.
      다만 웹프로그래밍 중에서 로그인처리, 로그인 세션 관련 부분을 공부해 보시면 무언가 될 것도 같네요.

      2017.02.16 20:08 신고 [ ADDR : EDIT/ DEL ]