반응형

아두이노나 ESP8266 관련 프로그래밍을 하다가 보면 가끔 현재 시간 정보가 필요한 경우가 있다. 이럴 경우 쉽게 사용할 수 있는 방법은 예전에 소개한 바 있는 RTC 모듈(Real Time Clock)이라는 장치를 이용하는 것이다. 하지만 장치가 인터넷에 연결되어 있다면 RTC가 필요 없다. 간단하게 현재의 시각을 알 수 있는 방법이 따로 있기 때문이다. 바로 웹서버에 요청(Request)해서 응답(Response)이 오는 HTTP 헤더 정보에 바로 Date, Time 정보가 있는 것이다. 이 정보를 파싱해서 자신의 코드에 삽입해서 이용하면 된다. 


 

그럼 HTTP 헤더란? 


우리가 사용하는 인터넷은 HTTP (Hyper Text Transfer Protocol) 라는 규약(프로토콜)을 이용해서 서버와 브라우저간에 통신을 하게 되고 정보를 교환하게 되는데 이 때 서버에서 브라우저 쪽으로 덧붙여서 보내주는 정보라고 보면 된다. 이 정보 안에는 Date/Time 정보가 있어서 이 것을 이용하면 현재의 시간을 구할 수 있는 것이다. 실제로 구글 크롬에서 F12 버튼을 누르면 HTTP 헤더 정보를 볼 수 있다.


Chrome 에서 F12 를 눌러서 개발자 도구에서 HTTP 헤더를 보는 모습


ESP8266 을 이용해서 서버에 빈(Empty) 요청을 날리면 위와 같이 웹서버에서 Date 라는 Name 으로 날짜, 시간 정보를 준다. 기준은 그리니치 표준시 기준인 GMT 기준으로 제공하기 때문에 우리나라 시간으로 변환을 원한다면 GMT+9시간을 해야한다.


■ 소스


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
-- retrieve the current time from Google
 
function queryTime()
  conn=net.createConnection(net.TCP, 0
 
  conn:on("connection",function(conn, payload)
    conn:send("HEAD / HTTP/1.1\r\n".. 
               "Host: google.com\r\n"..
               "Accept: */*\r\n"..
               "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)"..
               "\r\n\r\n"
                end)
            
  conn:on("receive", function(conn, payload)
    print('Retrieved in '..((tmr.now()-t)/1000)..' milliseconds.')
    print('Google says it is '..string.sub(payload,string.find(payload,"Date: ")
           +6,string.find(payload,"Date: ")+35))
    conn:close()
    end
  t = tmr.now()    
  conn:connect(80,'google.com')
end
 
tmr.alarm(010 * 10001, function() queryTime() end )
 
print("Queries every 10 sec the time from Google server")
print("Stop this by tmr.stop(0)")
cs


소스는 간단하다. google.com 으로 요청을 날리고 응답받은 HTTP 헤더 정보인 payload 에서 Date 부분을 추출해서 표시해주는 소스이다. 여기서는 google 에 요청했지만 다른 웹서버 URL 을 사용해도 상관이 없다.

 

 

실행결과는 위와 같다. 10초 마다 google 서버의 시간을 받아와서 print 해 주고 있다.


이 정보는 HTTP 헤더 정보인 payload 변수에 담겨져 있는 내용인데 payload 정보도 print 해 보았다. 위와 같이 payload 의 마지막 부분에 Date 가 있는 것을 알 수 있다.


소스에 GMT+9 를 하고 적절한 디스플레이를 연결하면 정확한 시간을 보여줄 수 있는 인터넷 시계도 만들 수 있을 것 같다.

반응형
반응형

'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

반응형
반응형

이번에는 NodeMCU, Lua, ESP8266 (Amica Board) 를 가지고 인터넷을 통하여 릴레이(Relay) 를 제어해 보려고 한다. 릴레이에 대해서는 이 블로그에서 많이 다루었지만 인터넷으로 릴레이를 제어하는 것은 처음인 듯 하다. 릴레이를 인터넷을 통하여 제어할 수 있다는 의미는 세계 어디서든 인터넷에만 연결되어 있으면 집안 또는 원격지의 전기, 전자기구 들을 제어할 수 있다는 말과 같다. 활용하기에 따라 요즘 IoT 광고에 나오는 기능들을 충분히 DIY로 구현해 볼 수 있다고 생각한다.


릴레이는 예전에 테스트 해 보았던 4채널 릴레이 모듈을 이용했다. 4개의 릴레이가 붙어 있어서 4개의 전원을 ON/OFF 할 수 있다. VCC, GND 와 제어를 위한 IN1 ~ IN4 단자가 달려 있다.


ESP8266 보드는 이제껏 계속 사용해 왔던 위와 같은 Amica 라는 이름의 ESP8266 개발보드를 이용했다. 펌웨어는 'NodeMCU_float_0.9.6-dev_20150704' 버전을 이용했다. 


그런데 문제가 발생했다. 위의 개발보드의 단자를 보면 3.3V 출력 밖에 없다. 하지만 릴레이 모듈은 5V 에서 동작한다. 외부전원을 사용해 볼까? 생각하다가 그냥 보드의 3.3V 에 연결해서 테스트해 보기로 했다. 연결해보니 정상적으로 동작하긴 한다. 그러나 5V 로 동작할 때 보다 뭔가 릴레이의 접점이 붙는 소리가 힘이 없다. 테스트용으로는 상관이 없겠지만 신뢰성 있는 장치를 만들 경우에는 릴레이에 정격의 전압을 반드시 사용해야 할 듯 하다.


 ESP8266

 Relay (4 Channel)

 3.3V

 VCC

 GND

 GND

 D1

 IN1

 D2

 IN2

 D3

 IN3

 D4

 IN4


연결은 위의 표와 같이 해 주었다.


■ 소스

internet_relay.lua 소스는 http://www.robokits.co.in 의 소스를 참고하여 수정하였다. 원래는 2채널용 소스였고 잡다한 HTML 태그가 달려 있었는데 정리해 주고, 인터넷 연결 부분의 소스를 분리해 주었다.


credentials.lua

1
2
3
-- WiFi Connect information
SSID = "와이파이 이름"
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
-- 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
--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);
 
--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 _, _, 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 Relay 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/>";
        html_buffer1 = html_buffer1.."</body></html>";
    
    
        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
        --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:close();
        collectgarbage();
    end)
end)
 
cs


internet_relay.lua 소스를 보면 ESP8266 이 웹서버로 동작한다. 8080 포트로 클라이언트의 요청을 기다리고 있다가 클라이언트(브라우저)의 요청이 있을 경우 html_buffer 의 내용을 브라우저로 보내준다. HTML 의 내용에는 각 릴레이에 해당되는 버튼에 링크태그를 걸어서 버튼 클릭 시 pin 이라는 name 으로 릴레이의 ON/OFF value 를 보내준다. 이 값을 가지고 if 문을 이용하여 각각의 릴레이를 ON/OFF 하도록 제어하는 것이다.




위의 소스를 보드에 업로드 하고 브라우저에서 ESP8266에 할당된 IP + 설정한 포트번호를 치고 들어가면 위와 같이 버튼 8개가 나열된다. 각 버튼 클릭 시 릴레이 1 ~ 4 가 제대로 제어가 됨을 확인 하였다.



물론 위와 같이 공유기의 내부IP를 이용한 접속은 말 그대로 내부IP(사설IP)이기 때문에 집에서 밖에 콘트롤 할 수 없으며 별 의미가 없다.  이럴 경우는 공유기의 DDNS, 포트포워딩 설정을 이용하면 외부에서도 접근이 가능하다. 이에 대해서는 예전 글 "외부에서 NodeMCU 웹서버 접속하기" 를 참고하면 된다.



▶추가사항 (2016.07.29) : http://deneb21.tistory.com/427 에 조금 더 업그레이드 된 소스를 올려 놓았다. (릴레이 현재상태를 알 수 있도록 함.)

반응형
반응형

NodeMCU Lua 사용해보기 지난 10편에서는 ThingSpeak 에 ESP8266 보드를 이용해서 DHT11 의 데이터를 업로드 해 보았다.  이번에는 비슷한 서비스인 dweet.io 라는 IoT 클라우드 서비스를 이용해 보려고 한다. ThingSpeak 는 회원가입이 필요 했는데 dweet 은 회원가입이 필요 없고 Things 의 이름을 Key 로 사용한다. 고유한 이름으로 Thing 의 이름을 지정해주고 자신의 IoT 데이터를 업로드 하면 되는 것이다.


dweet.io 사이트에서 어떠한 오퍼레이션 없이 유니크한 Thing Name 을 가지고 dweet 으로 데이터를 보내면 dweet.io 에서 데이터를 받아서 저장하기 때문에 바로 데이터 업로드를 위한 하드웨어 및 소스를 준비하면 된다.  ESP8266 보드인 Amica 에 DHT11 온습도 센서를 아래와 같이 연결을 하였다.


 ESP8266 (Amica Board)

 DHT11

 D5

 Signal

 3.3V

 VCC (+)

 GND

 GND


■ 소스


credentials.lua

1
2
3
4
-- WiFi Connect information
SSID = "Your WiFi SSID"
PASSWORD = "Your 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
-- 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("dht11_dweet.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


dht11_dweet.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
DHT_PIN = 5 -- defined by DHT shield (D5)
HOST    = "dweet.io"
THING_NAME   = "Your Thing Name"
 
function read_DHT()
  status,temp,humi,temp_decimial,humi_decimial = dht.read(DHT_PIN)
  if( status == dht.OK ) then
    print("DHT Temperature = "..temp.." grdC  ".."Humidity = "..humi.." %")
  elseif( status == dht.ERROR_CHECKSUM ) then
    print"DHT Checksum error." );
  elseif( status == dht.ERROR_TIMEOUT ) then
    print"DHT Time out." );
  end
end
 
function dweet()
  read_DHT()
  conn=net.createConnection(net.TCP,0
  conn:on("receive", function(conn, pl) print("response: ",pl) end)
  conn:on("connection", function(conn, payload) 
          print("connected"
          conn:send("POST /dweet/for/"..THING_NAME.."?"
          .. "temp=" .. temp 
          .. "&humi=" .. humi 
          .. " HTTP/1.1\r\n"
          .. "Host: " .. HOST .. "\r\n"
          .. "Connection: close\r\n"
          .. "Accept: */*\r\n\r\n"end)
  conn:on("disconnection", function(conn, payload)
          print("disconnected"end)
  conn:connect(80, HOST)
end
 
dweet()
 
-- dweets every 60 sec.
tmr.alarm(010 * 10001, function() dweet() end )
 
print("Dweets every 60 sec DHT11 data to dweet.io")
print("Stop this by tmr.stop(0)")
cs


credentials.lua 와 init.lua 에서 ESP8266 보드를 와이파이에 연결시키고 dht11_dweet.lua 에서 dweet.io 로 DHT11의 온습도 데이터를 업로드 한다. 위의 3가지 lua 파일을 ESP8266 보드에 업로드 하고 실행하면 되는 것이다. dweet.io 사이트에서는 아무것도 설정해 주지 않아도 된다. 다만 위의 소스에서 THING_NAME 의 값은 유니트한 값이어야 한다.


ESPlorer 에서 위와 같이 데이터를 업로드하고 실행하면 dweet.io 에서 데이터가 잘 전송되었다는 리턴값을 보내 준다.


소스를 수정 후 업데이트 할 경우에는 위에 사용된 타이머 0번을 Stop 시키고 업로드 해야 한다. tmr.stop(0) 명령어를 이용해서 타이머를 멈추고 업로드 하면 된다.



이제 dweet.io 사이트에 들어가 본다.


■ dweet.io

https://dweet.io/ 

 

dweet.io 사이트에서 상단의 Play 를 클릭하면 아래와 같이 API 설정 목록이 뜬다.


Play 메뉴에서 여러가지 API 에 대한 설명을 볼 수 있고 자신의 Thing 에 대한 설정을 할 수 있다. 둘러보면 모두 이해할 수 있는 내용이다. 이 API 들을 이용해서 데이터를 끌어와 관련 웹이나 스마트폰 앱 또는 또 다른 Thing 을 만들 수 있을 것이다.


위의 소스를 이용해서 데이터를 업로드한 dweet 의 THING NAME 은 'deneb_nodemcu_dht11_01' 이다. http://dweet.io/follow/deneb_nodemcu_dht11_01 (이 URL 은 언제든지 내가 ESP8266의 전원을 끊으면 동작하지 않을 수 있음) 를 클릭해서 들어가보면 데이터가 잘 업로드 되고 있는 것을 알 수 있다.



위의 화면에서 'Create a Custom Dashboard' 를 클릭하면 freeboard.io 사이트로 이동을 하는데 Things의 데이터를 비주얼하게 표현해 주는 사이트이다. 


사이트에 가입하면 위와 같이 게이지 같은 위젯도 추가가 가능하다. 


dweet.io 를 사용해본 소감은 ThingSpeak 보다 간단해서인지 왠지 적응이 잘 안된다. 너무 오픈되어 있어서 그런가? 좀 혼란스럽다. 아직까지는 ThingSpeak 가 더 마음에 든다. 앞으로 dweet 에 대해 새로운 사항을 알게되면 이 포스팅에 내용을 추가할 생각이다.

반응형
반응형

예전에 IoT 클라우드 서비스인 ThingSpeak.com 에 대해서 알아보고 실제로 ESP8266 모듈인 ESP-01과 아두이노를 이용해서 DHT22 의 온도, 습도 데이터를 ThingSpeak 에 올려본 적이 있습니다. 하지만 AT Command 기반 이었고 ESP-01 은 아두이노에서 읽은 센서 데이터를 받아서 인터넷에 올려주는 역할만을 했습니다. 이번에는 NodeMCU 를 이용해서 DHT11 센서의 온도와 습도 데이터를 ThingSpeak 에 올려보도록 하겠습니다. 지난 글에서 구현 했던 DHT11 센서의 데이터를 읽어서 온도, 습도를 알려주는 웹서버는 인터넷을 통해서 들어오는 요청에 대해서 응답을 줬었지만 ThingSpeak 연동은 센서 데이터를 ESP8266을 통해서 보내는 것이므로 반대의 개념이 되겠습니다. ThingSpeak 서비스에 대해서는 예전에 자세하게 다룬 적이 있어서 링크로 대신 합니다. 



ThingSpeak 서비스 개념도 입니다. 센서로 부터 데이터를 받은 ESP8266 보드가 데이터를 ThingSpeak 서버에 업로드하고 그 데이터를 스마트폰, PC, 아두이노, 또 다른 ESP8266 등 n개의 장치로 보내서 활용이 가능 합니다. 물론 받은 데이터로 또 다른 장치를 제어할 수도 있습니다. 활용하기 나름이죠.


ESP8266 보드는 여전히 ESP-12E 기반의 Amica 보드를 이용 합니다. 센서는 DHT11 온도 습도 센서를 이용합니다. 센서와 ESP8266 개발보드와의 연결은 아래의 이전 글 을 참고해서 연결하면 됩니다. 똑같습니다. 다시 말하자면 센서의 전원은 Amica 보드의 3.3V 에 연결하고 Signal 은 D5에 연결했습니다.


이제 ESPlorer를 열고 ESP8266 Amica 보드를 연결하고 코딩을 합니다. credentials.lua 와 init.lua 는 NodeMCU 웹서버 만들기 글에서 사용한 것과 동일 합니다. 대신 dht11_thingspeak.lua 를 init.lua 에서 call 해 줍니다.


■ 소스 #1 (credentials.lua)

1
2
3
4
-- WiFi Connect information
SSID = "your wifi name"
PASSWORD = "your wifi password"
 
cs



■ 소스 #2 (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
-- 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")
        dofile("dht11_thingspeak.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


■ 소스 #3 (dht11_thingspeak.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
pin = 5 -- dht11 signal pin
temp = 0
humi = 0
writekey = "your thinkspeak channel write api key"
 
tmr.alarm(1,15000,1,function()readDHT()sendData(temp,humi)end)
 
--read DHT11 temp, humi data function
function readDHT()
    status, temp, humi = dht.read(pin)
    if status == dht.OK then
        temp = math.floor(temp)
        humi = math.floor(humi)
    elseif status == dht.ERROR_CHECKSUM then
        print"DHT Checksum error." )
    elseif status == dht.ERROR_TIMEOUT then
        print"DHT timed out." )
    end
    dht = nil
end
 
--send data to thingspeak
function sendData(temp,humi)
    -- conection to thingspeak.com
    print("Sending data to thingspeak.com")
    conn=net.createConnection(net.TCP, 0
    conn:on("receive", function(conn, payload) print(payload) end)
    
    -- api.thingspeak.com 184.106.153.149
    conn:connect(80,'184.106.153.149'
    conn:send("GET /update?key="..writekey.."&field1="..temp.."&field2="..humi.." HTTP/1.1\r\n"
    conn:send("Host: api.thingspeak.com\r\n"
    conn:send("Accept: */*\r\n"
    conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
    conn:send("\r\n")
    conn:on("sent",function(conn)
    print("Closing connection")
    conn:close()
    end)
    conn:on("disconnection", function(conn)
    print("Got disconnection...")
  end)
end
 
cs


인터넷을 통해서 thingspeak에 dht11 의 온도, 습도 데이터를 업로드 하는 소스 입니다. 4번째 줄 writekey 부분에 자신의 thingspeak 채널의 Write API Key 를 입력해야 합니다.


물론 thingspeak 채널에는 온도, 습도를 받을 수 있는 2개의 field 가 등록되어 있어야 합니다. 위의 링크의 thingspeak 글을 참고하면 쉽게 만들 수 있습니다.



소스를 모두 업로드 하고 장치를 Soft Reset 하면 위와 같이 데이터를 ThinsSpeak 에 업로드 하기 시작 합니다.


ThingSpeak.com 에 접속해서 데이터를 업로드 하고 있는 채널에 들어가면 위와 같이 우리집 온도와 습도가 그래프로 표시 됩니다. 물론 스마트폰 웹브라우저로도 볼 수 있으며 데이터를 csv, json, xml 형식으로 다운로드 받을 수도 있습니다. 



위의 링크를 참조하면 자신의 스마트폰에 위젯앱을 추가하여 데이터를 실시간으로 모니터링 할 수 있으며 입계값을 설정하여 경고가 울릴 수 있게 할 수도 있습니다. 예를 들어 온도가 30도가 넘으면 경고 알람을 울리게 설정할수도 있는 것이죠.


스마트폰에서 ThingSpeak 위젯앱 'IoT ThingSpeak Monitor Widget' 을 설치하고 설정한 모습 입니다.

반응형
반응형

지난 글들에서 NodeMCU Lua 를 이용해서 간단한 웹서버를 만들어 보았고 공유기 설정을 통하여 NodeMCU ESP8266 웹서버를 외부에서도 접속이 가능하도록 만들어 보았습니다. 하지만 브라우저로 웹서버에 접속해도 Hello NodeMCU 라는 글자만 출력되었었죠. 아무런 정보도 표시되지 않는다면 IoT 로서의 의미가 없습니다. 이번에는 온습도 센서인 DHT11 을 ESP8266 개발보드인 Amica 에 연결해서 웹서버에 접속하면 온도와 습도가 표시되도록 해 보겠습니다. 어디든 인터넷만 연결이 되면 스마트폰, PC 등에서 현재 집의 온도와 습도를 알 수 있는 것이죠.


제가 가지고 있는 DHT11 센서 입니다. 저항이 달려 있는 모듈형태로 되어 있어서 보드와 연결 시 따로 저항을 연결하지 않아도 됩니다. 모듈형태가 아니라 위의 파란부분의 핀 4개짜리 센서만 가지고 있다면 5K 정도의 저항을 Signal 과 VCC 사이에 달아 주어야 합니다. DHT11 에 대한 자세한 사용법은 [여기] 를 참고 하시면 됩니다.


그리고 Amica  ESP8266 개발 보드 입니다. DHT11 이 3V~5.5V를 지원하므로 전원은 3.3V 핀을 이용하면 됩니다. 연결은 아래의 표를 참조해서 연결하면 됩니다.


 DHT 11

 ESP8266 (Amica)

 Signal

 D5

 VCC

 3.3V

 GND

 GND


연결이 되었으면 이제 소스를 코딩하면 됩니다. 소스는 이전 글에서 해 보았던 웹서버 만들기의 credentials.lua, init.lua, webserver.lua 를 변형해서 사용할 것 입니다.


■ credentials.lua

와이파이 연결정보를 가지고 있는 파일 입니다.


1
2
3
4
-- WiFi Connect information
SSID = "와이파이이름"
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
-- 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")
    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




■ webserver.lua

웹서버를 실행하고 DHT11 센서의 값을 읽어서 웹에서 요청이 들어오면 준비된 HTML (온도 및 습도값 포함) 을 전송하는 기능을 합니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pin = 5 -- dht11 signal pin
srv = net.createServer(net.TCP)
srv:listen(8080, function(conn)
 
    status, temp, humi, temp_dec, humi_dec = dht.read(pin)
    if status == dht.OK then
        temp_1 = math.floor(temp)
        humi_1 = math.floor(humi)
    elseif status == dht.ERROR_CHECKSUM then
        print"DHT Checksum error." )
    elseif status == dht.ERROR_TIMEOUT then
        print"DHT timed out." )
    end
    tmr.delay(1000000)
    conn:on("receive", function(sck, payload)
        print(payload)
        sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>My House Temperature & Humidity<br/></h1>" .. "<br/><h1>Temperature = " .. temp .. "C</hr>" .. "<br/><h1>Humidity = " .. humi .. "%</hr>")
    end)
    conn:on("sent", function(sck) sck:close() end)
end)
print("webserver ready...")
cs


위의 소스를 보면 이전 글에서 작성 했던 웹서버 코드를 기반으로 DHT11 값을 추가해서 HTML 을 리턴 할 수 있도록 수정해 주었습니다.


DHT11 의 경우 초기의 NodeMCU 같은 경우는 별도의 라이브러리를 사용해야 코드가 단순화 되었었는데 지금은 위와 같이 간단하게 읽어오는 것이 가능 합니다. 아마도 펌웨어 자체에 라이브러리가 포함이 된 듯 합니다.



위의 3개의 소스를 ESPlorer 를 통해서 ESP8266 개발보드에 'Save to ESP' 해서 업로드 해 줍니다. init.lua 는 맨 마지막에 업로드 해 주는 것이 좋습니다.


소스를 모두 업로드 하고 Reset 을 눌러서 Soft Reset 을 한 번 해 줍니다. 시리얼 창에 'webserver ready...' 라는 메시지가 나오면 준비가 완료된 것 입니다.


이제 브라우저를 열고 저의 공유기에서 ESP8266 웹서버에 할당된 IP 인 192.168.0.18 로 접속해서 온도와 습도가 잘 나오는지 확인해 봅니다. 포트는 8080 을 이용했으므로 다음과 같이 브라우저의 주소창에 입력하면 됩니다. http://192.168.0.18:8080/



이번에는 외부에서 스마트폰 LTE 연결로 확인을 해 보겠습니다. 물론 이것을 가능하게 하려면 [여기] 를 참고 하셔서 공유기의 DDNS, 포트 포워드 를 설정해서 외부에서도 ESP8266 웹서버에 접속 가능하도록 만들어 주어야 합니다.


위와 같이 스마트폰으로 외부에서도 DDNS에서 만들어준 URL을 입력하면 DHT11 센서가 측정한 값을 실시간으로 볼 수 있습니다. 이를 응용하면 가스누출, 조명 밝기, 침입자 탐지 등도 원격지에서 모니터링 할 수도 있을 것 같습니다.



▶ 추가사항 (2016.07.08) : 위의 webserver.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
pin = 5 -- dht11 signal pin
temp = 0 -- initialize temperature
humi = 0 -- initialize humidity
port = 8080 -- web port
 
srv = net.createServer(net.TCP)
srv:listen(port, function(conn)
    ReadDHT()   -- dht read function call
    tmr.delay(1000000)
    conn:on("receive", function(sck, payload)
        print(payload)
        --// response html
        sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>My House Temperature & Humidity<br/></h1>" .. "<br/><h1>Temperature = " .. temp .. "C</hr>" .. "<br/><h1>Humidity = " .. humi .. "%</hr>")
    end)
    -- response
    conn:on("sent", function(sck) sck:close() end)
end)
print("webserver listening..."
 
 
function ReadDHT()
    status, temp, humi = dht.read(pin)
    if status == dht.OK then
        temp = math.floor(temp)
        humi = math.floor(humi)
    elseif status == dht.ERROR_CHECKSUM then
        print"DHT Checksum error." )
    elseif status == dht.ERROR_TIMEOUT then
        print"DHT timed out." )
    end
    dht = nil   --release dht module
end
 
cs


반응형
반응형

지난 NodeMCU 관련 글 7편 에서 ESP8266 보드(Amica Board)에 간단한 웹서버를 만들어 보았습니다. 하지만 공유기의 내부 IP 를 통해서만 접속이 가능 했습니다. IoT 의 개념이 외부에서도 집안의 장치에 마음대로 접근해서 컨트롤 하는 것이 목표이기 때문에 외부에서 접속이 안된다면 별 의미가 없습니다. 공유기의 DDNS 서비스와 포트포워딩 기능을 이용하여 지난번 구현 하였던 웹서버를 외부에서도 접속할 수 있도록 해 보겠습니다. 사실 이 글은 NodeMCU 글이라기 보다는 공유기 설정 관련 글이지만 어차피 NodeMCU 시리즈를 엮는데 필요할 것 같아서 8편 글로 올립니다.


이 글에서 사용한 공유기는 ipTime 유무선 공유기 입니다. 하지만 다른 회사의 공유기를 사용하고 있는 분들이라도 DDNS나 포트포워딩의 개념은 같으므로 적용하는데 별 어려움은 없을 것 입니다.



■ DDNS 서비스란?

먼저 DDNS는 Dynamic DNS 의 약자 입니다. 동적 DNS 라고 하는데 먼저 DNS의 개념을 알아야 합니다.  DNS는 도메인 네임을 IP로 바꿔주어서 인터넷에 연결할 수 있도록 합니다.  모든 인터넷 서버는 IP를 가지고 있으니까요. 우리가 www.naver.com 이라는 도메임을 주소창에 치면 DNS 서버가 이것을 받아서 해당되는 IP를 가지고 있는 서버로 연결해 주는 것이죠. 그런데 Naver 같은 회사는 고정 IP 를 서버에 사용 합니다. 그래서 IP가 변할 일이 없습니다. 반면 가정의 인터넷은 IP가 변합니다.(물론 고정 IP서비스도 있긴 함) 동적 IP를 할당하는 것이죠. IP는 한정된 자원이기 때문에 하나의 IP를 여러사람이 돌아가면서 쓰도록 해 놓은 것 입니다. 고로 기존의 DNS 서비스에는 등록이 불가능 합니다. 이럴 경우 DDNS 라는 서비스를 사용하게 됩니다. 


ipTime 같은 경우는 iptime.org 라는 DDNS 서비스를 자사의 공유기 사용자들에게 무료로 사용하도록 해 주고 있습니다. 서비스에 가입을 하면 공유기가 공인IP를 모니터링 하게 되고 IP가 변경될 경우 이 공인 IP를 DDNS 서비스에 등록해서 계속 갱신하도록 해 줍니다. 고로 xxxxxx.iptime.org 라는 자신만의 도메인으로 가정의 웹서버에 접속 할 수 있도록 해 주는 원리 입니다.


하지만 ipTime 같은 경우는 자체의 DDNS 서비스를 구현하고 공유기 구매자들에게 무료로 제공하고 있지만 그렇지 않은 경우도 많습니다. 그러므로 DDNS 기능을 사용하기 위해서는 공유기 구입 전 공유기가 DDNS 서비스를 지원하는지 알아봐야 합니다. 만약 DDNS 서비스가 지원되지 않는다면 자신의 공인 IP를 알아내서 연결하는 방법을 쓸 수 있습니다. 자신의 공인 IP를 알아내는 방법은 [여기] 를 클릭하면 알 수 있습니다.

 


■ 포트포워딩 이란?

포트포워딩이랑 공유기에서 DDNS 등을 통해 외부에서 요청이 들어올 경우 설정해 놓은 웹서버로 연결해주는 기능 입니다. 예를 들어 xxxxxx.iptime.org:8080 이라고 외부에서 브라우저에 주소를 쳐서 연결을 요청하는 경우 먼저 DDNS 서비스가 이를 공인 IP로 변경해 줍니다. xxxxxx.iptime.org:8080 -> 218.237.234.156:8080 이런 식으로 변경이 됩니다. 그러면 218.237.234.156 IP를 쓰고 있는 공유기를 찾아서 요청 정보를 공유기가 받습니다. 공유기에서는 포트포워딩 목록을 보고 내부 IP 인 192.168.0.18:8080 을 사용하는 NodeMCU 웹서버로 요청을 보내 줍니다. 그럼 NodeMCU 에서는 준비된 응답(HTML)을 보내주게되는 것입니다.


이제 공유기를 실제로 설정해 보겠습니다.


ipTime 공유기의 관리자에 들어 갑니다. 접속 IP 는 192.168.0.1 입니다. 관리화면으로 이동 합니다.


ipTime 공유기의 DDNS 설정 입니다. 고급설정 -> 특수기능 하위에 있습니다. 저의 경우 이미 만들어 놓은 DDNS 도메인이 있어서 저렇게 나옵니다. 추가 버튼을 눌러서 서비스에 가입을 해 주면 됩니다. 접속 도메인에 사용할 자신만의 호스트이름과 이메일, 비밀번호 등을 설정하면 쉽게 추가할 수 있습니다.


 DDNS설정이 끝났으면 포트포워딩을 설정 합니다. 고급설정 -> NAT/라우터관리 하위에 있습니다. 아랫 부분에 새로운 포트포워딩 규칙을 적는 곳이 있습니다. 적절한 이름을 적어주고 프로토콜은 http, tcp 를 선택합니다. 포트는 저 같은 경우는 8080을 할당해 주었습니다. 이렇게 하게되면 지난 NodeMCU 7편에서 설정했던 webserver.lua 의 소스의 포트 설정 부분을 80 에서 8080으로 바꾸어서 다시 업로드 해 주어야 합니다. 그리고 NodeMCU 에 할당된 내부 IP를 적어 줍니다. 모든 것을 이상없이 적었으면 '적용' 버튼을 누르면 됩니다. 


새 포트포워드 규칙이 생성되었습니다. 맨위 오른쪽의 저장 버튼을 눌러서 전체 설정을 한 번 더 저장해 줍니다.


이제 테스트를 하면 됩니다. 테스트 전 [NodeMCU Lua 사용해보기 #7 - 간단한 웹서버 만들어보기] 에서 만들었던 NodeMCU 는 전원이 꼭 들어가 있어야 합니다. 그리고 위에서 말 했지만 다시 말하자면 webserver.lua 의 포트를 아래와 같이 수정해서 다시 업로드 해 주어야 합니다.


1
2
3
4
5
6
7
8
9
-- a simple HTTP server
srv = net.createServer(net.TCP)
srv:listen(8080, function(conn) -- 서비스 포트 수정
    conn:on("receive", function(sck, payload)
        print(payload)
        sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1> Hello, NodeMCU.</h1>")
    end)
    conn:on("sent", function(sck) sck:close() end)
end)
cs


3번째 줄의 포트번호 설정이 80 -> 8080 으로 변경 되었습니다. 나머지는 같습니다.


이제 외부에서 NodeMCU 웹서버가 제대로 응답을 해 주는지 확인을 해 봐야 합니다. 직접 확인을 하기 위해 근처의 PC방이나 친구집에 가서 테스트 할 수도 있겠지만 제일 쉬운 방법은 스마트폰의 와이파이를 끄고 LTE 나 3G로 연결해서 스마트폰의 브라우저로 확인해 보는 것입니다.


저의 스마트폰 브라우저에서 DDNS에서 설정한 호스트명으로 연결한 모습 입니다. (저의 호스트명은 지웠습니다. 개인적으로 사용하고 있는 DDNS 서비스라서요.) 외부에서도 연결이 잘 됩니다. 이제 나만의 NodeMCU 웹서버는 인터넷 연결만 가능하면 미국이건 중국이건 아프리카건 어디에서든 연결이 가능한 서버가 된 것입니다.

반응형
반응형

이번에는 NodeMCU Lua 를 사용해서 간단한 웹서버를 만들어보고 브라우저로 웹서버로 접속해서 응답을 얻어보겠습니다. 웹서버를 만들기 위해서는 'NodeMCU Lua 사용해보기 #5 - 인터넷에 연결해보기' 를 먼저 공부해야 합니다. 인터넷에 연결이 되어 있어야 웹서버가 동작 하니까요. 일단 간단하게 브라우저에서 ESP8266 장치로 요청을 보내면 장치가 준비된 HTML 을 브라우저로 응답을 보내주는 형식 입니다.


NodeMCU 펌웨어가 설치된 ESP8266 장치를 PC에 연결 하고 ESPlorer 를 실행 합니다. Open 을 눌러서 PC와 통신 가능 상태로 만들어 줍니다.


웹서버 기능은 총 3개의 Lua 파일로 만들었습니다. 와이파이 연결 정보를 담고 있는 credentials.lua, 인터넷 연결을 해 주는 init.lua, 그리고 웹서버의 역할을 해 주는 webserver.lua 의 3개 파일로 구현 할 수 있습니다.


■ credentials.lua


1
2
3
4
-- WiFi Connect information
SSID = "와이파이 이름"
PASSWORD = "와이파이 패스워드"
 
cs


위와 같이 와이파이 연결 정보를 SSID, PASSWORD 변수에 담아 두는 소스 입니다.


■ 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
-- 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("Running")
        file.close("init.lua")
        -- the actual application is stored in 'application.lua'
        dofile("webserver.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


init.lua 파일은 ESP8266 이 부팅 시 가장 먼저 실행하는 파일 입니다. 이 파일에 인터넷 연결 소스를 작성 합니다. 와이파이 연결에 credentials.lua 의 내용을 dofile("credentials.lua")  부분을 통해서 참조하게 되고 와이파이에 연결 합니다. 와이파이 연결 후 startup() function을 call 하고 startup() 은 실행 후 dofile("webserver.lua") 을 통해서 webserver.lua 파일을 실행하게 됩니다. 


■ webserver.lua

1
2
3
4
5
6
7
8
9
10
-- a simple HTTP server
srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
    conn:on("receive", function(sck, payload)
        print(payload)
        sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1> Hello, NodeMCU.</h1>")
    end)
    conn:on("sent", function(sck) sck:close() end)
end)
 
cs


웹서버로 브라우저의 요청이 오면 가지고 있던 HTML 을 응답으로 내어주는 가장 간단한 형태의 웹서버 구현 입니다. 


HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1> Hello, NodeMCU.</h1>


위의 HTML 코드를 브라우저로 보내 줍니다. 그리고 브라우저에서는 웹서버에서 응답받은 내용을 브라우저에 표시해 주겠죠. 그리고 위의 소스를 보면 srv:listen(80, function(conn)  부분이 있습니다. 


여기에서 80은 웹서버의 서비스 포트 입니다. 80은 기본 웹서비스 포트로서 브라우저 URL 입력창에 포트 번호를 붙이지 않아도 됩니다. 만약 포트를 80이 아니라 8080 같은 포트로 설정하면 URL 마지막에 포트 번호를 표기 해야 합니다. 예를 들면 http://192.168.0.18:8080 이런 식으로 주소를 적어야 합니다. 혹시나 가정용 인터넷을 이용할 경우에는 80포트를 인터넷 회사에서 사용하지 못하도록 막아 놓는 경우도 있기 때문에 8080, 8088... 등 다른 포트를 이용해서 외부에서 연결을 설정 하기도 합니다. 



위와 같이 3개의 소스 파일을 ESPlorer 에서 작성하고 Save to ESP 버튼을 눌러서 ESP8266 보드에 업로드 해 줍니다. 업로드 순서는 init.lua 파일을 가장 나중에 업로드 해 주시면 됩니다. 왜냐하면 업로드 후 자동으로 실행이 되는데 나머지 2개의 파일을 찾지 못하면 에러를 내기 때문에 그렇습니다. 업로드 후 Reset 버튼을 눌러서 Soft Reset 해 봅니다. 보드가 다시 시작 하면서 init.lua 를 자동으로 실행하게 되고 위와 같이 192.168.0.18 이라는 IP를 잡고 웹서버가 실행이 됩니다. 고로 웹서버의 IP 가 접속주소가 됩니다.


브라우저를 하나 열고 제대로 ESP8266에서 작동 중인 웹서버가 제대로 응답을 주는지 확인해 봅니다.


PC의 브라우저를 열고 웹서버 주소인 192.168.0.18 로 연결해 보았습니다. 응답을 잘 주고 있습니다.


PC뿐 안니라 스마트폰의 브라우저 에서도 웹서버 연결이 잘 됩니다.


이상으로 NodeMCU Lua를 이용해서 ESP8266 보드에서 간단한 웹서버를 구현해 보았습니다.  간단하지만 센서값을 인터넷을 통해 보여주거나 반대로 외부에서 ESP8266에 연결된 장치 들을 콘트롤 할 수 있는 기초가 마련된 것 입니다.




▶ 추가사항 (2016.07.25) : http://orasman.tistory.com/358 여기에 위의 소스의 개선점을 트랙백으로 올려 주셨네요. 서버가 닫히지 않았을 경우 닫아주는 소스가 추가 되었습니다.

반응형

+ Recent posts