[사물 인터넷 네트워크와 서비스 구축 강좌] #4-4 센서장치에 웹 소켓 활용하기
강좌 전체보기
.
이전 실습 예제에서는 HTTP client 가 주기적으로 서버에 HTTP 요청을 보내서 데이터를 가져왔습니다. 물론 이 방식이 일반적인 웹 사용법이긴 하지만 특정 서비스에는 적합하지 않은 측면이 있습니다. 예를들어 특정 웹 페이지에 채팅 기능을 넣는다고 생각해보세요. 실시간으로 채팅 메시지가 보여지도록 하려면 브라우는 매우 빠르게 그리고 끊임없이 HTTP request 를 보내야 할 것입니다. 채팅 같은 기능에는 항상 서버-클라이언트가 백그라운드로 연결된 상태를 유지하면서 실시간-양방향으로 자유롭게 데이터를 주고 받는 방식이 필요합니다.
Web Socket 이 이런 자유로운 통신 방식을 제공해줍니다. HTML5에 포함된 Web Socket은 웹 서버와 클라이언트(브라우저의 JavaScript 등) 간 소켓 연결을 유지하게 해 줌으로써, 지속적인 양방향 데이터 전송을 할 수 있습니다. 다만, 이 기능을 사용하기 위해서는 비교적 최신의 브라우저를 사용해야 하는 제약이 있습니다. 그리고 서버에는 WebSocket 통신을 위한 준비가 되어 있어야 합니다. 보통 웹 소켓은 브라우저에서 많이 사용하지만 웹 소켓이 특정 플랫폼에 종속된 것은 아닙니다.
이번 파트에서는 ESP32 모듈을 이용해서 웹 소켓 통신을 구현하는 방법을 다룹니다.
.
.
웹 소켓 서버 구현
ESP32 모듈을 웹 소켓 서버로 구현해 보도록 하겠습니다. 그리고 라즈베리파에에 파이썬으로 웹 소켓 클라이언트를 구현할 것입니다. 웹 소켓이 연결되면 라즈베리파이의 웹 소켓 클라이언트에서 메시지를 보냅니다. 웹 소켓 서버는 받은 메시지를 다시 클라이언트에게 되돌려 줄 것입니다.
ESP32 모듈에 웹 소켓 기능을 넣기 위해 라이브러리 설치가 필요합니다. 아래 링크에서 라이브러리 파일을 받으세요.
다운받은 소스를 아두이노 라이브러리 폴더에 복사합니다. 아두이노 라이브러리 폴더의 경로는 다음과 같습니다. 라이브러리 파일들이 담긴 폴더를 붙여넣기 할 때 라이브러리 폴더의 이름을 WebSocketServer로 만드세요.
- C:\사용자\사용자명\문서\Arduino\libraries
이 라이브러리를 그냥 사용할 경우 기존의 MD5 관련 함수와 충돌이 발생합니다. WebSocketServer 라이브러리 폴더 내 MD5.c 와 MD5.h 파일에서 다음 문자열을 찾아서 모두 치환해 주면됩니다.
- MD5Init →MD5Init_XXX
- MD5Update →MD5Update_XXX
- MD5Final →MD5Final_XXX
_XXX 는 원하는대로 지정하면 됩니다.
이제 ESP32 용 웹 소켓 서버 예제를 올려보겠습니다. 아래 링크에서 스케치를 받을 수 있습니다.
ESP32 서버용 코드를 컴파일 후 업로드하면 ESP32 가 서버로 동작하기 시작합니다. ESP32 모듈의 IP 주소를 꼭 기억해두세요!!
ESP32 모듈에 웹 소켓 서버 구현은 끝났으니 라즈베리파이에 웹 소켓 클라이언트를 구현하겠습니다. 파이썬과 websocket-client 모듈을 이용합니다.
라즈베리파이에 접속 후 PIP 를 이용해 websocket-client 를 설치합니다.
- sudo pip3 install websocket-client
아래 링크에 웹 소켓 클라이언트를 구현한 파이썬 코드가 있습니다.
소스코드에서 ESP32 모듈의 IP 주소를 넣어주세요.
.
import websocket import time ws = websocket.WebSocket() ws.connect("ws://192.168.219.117/") i = 0 nrOfMessages = 200 while i < nrOfMessages: ws.send("message nr: " + str(i)) result = ws.recv() print(result) i=i+1 time.sleep(1) ws.close()
.
파이썬으로 작선된 웹 소켓 클라이언트 코드는 단순합니다. 특정 IP 로 웹 소켓 접속을 한 후, 1초 간격으로 메시지를 200번 보냅니다. 메시지 전송할 때는 ws.send() 를 이용합니다. 메시지 수신은 ws.recv() 를 사용하면 됩니다.
ESP32 웹소켓 서버가 구동되고 있는 상태에서 파이썬 코드를 실행하면 메세지가 오고가는 것을 확인할 수 있습니다.
ESP32 모듈에 업로드 한 소스코드를 살펴보겠습니다. 먼저 setup() 함수를 살펴보면…
.
// 서버 생성시 연결될 포트 지정 WiFiServer server(80); WebSocketServer webSocketServer; void setup() { Serial.begin(115200); delay(10); Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); // 와이파이망에 연결 while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); startServer(); } // 서버 시작 void startServer() { Serial.println("Server start"); server.begin(); }
.
공유기에 연결하고, 웹 서버를 시작했습니다.
웹 서버로 HTTP request 요청이 오면 그 안에 웹 소켓 연결을 요청하는 정보가 있습니다. 이때 웹 소켓 연결을 위한 handshake 과정을 거치면 연결됩니다. loop() 안에서 순차적으로 이 작업을 처리해줍니다.
.
void loop() { // 클라이언트 연결 대기 WiFiClient client = server.available(); // 클라이언트가 연결되면 파일 전송 시작 if(client.connected() && webSocketServer.handshake(client)) { String data; while(client.connected()) { data = webSocketServer.getData(); if(data.length() > 0) { Serial.println("received: "+data); webSocketServer.sendData("send back - "+data); } delay(10); } Serial.println("The client is disconnected"); delay(100); } delay(100); }
.
이 코드는 간단하게 웹 소켓 연결을 구현해주고 있지만 제약사항이 하나 있습니다. 웹 소켓 연결이 되면, 연결이 종료될 때 까지 다른 웹 소켓 연결요청을 처리할 수가 없습니다. 만약 여러개의 웹 소켓 연결을 동시에 처리하고 싶다면 몇 가지 수정을 해줘야 합니다.
기존에 사용했던 WebSocketServer 라이브러리를 삭제하고 대신 다중 웹 소켓 접속을 지원하는 라이브러리를 설치하세요.
그리고 아래 코드를 사용하면 다중 웹 소켓 서버를 만들 수 있습니다. 라즈베리파이에 여러개의 터미널로 접속 한 뒤, 파이썬 웹 소켓 클라이언트 코드를 여러개 실행시켜 확인해보세요.
.
.
웹 소켓 클라이언트 구현
이번에는 ESP32 모듈을 웹 소켓 클라이언트로 사용해 보겠습니다. 외부에 우리가 다룰 수 있는 웹 서버가 존재한다면 이렇게 구현하는 것이 더 효율적이겠죠.
먼저 라즈베리파이에 웹 소켓 서버를 구동시키겠습니다. 라즈베리파이에 접속해서 websockets 파이썬 모듈을 설치해야 합니다.
- sudo pip3 install websockets
설치가 완료되면 아래 코드를 구동시켜보세요.
.
import socket import fcntl import struct import asyncio import websockets def get_ipaddress(network): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) return socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR struct.pack('256s', network[:15].encode('utf-8')) )[20:24]) async def echo(websocket, path): async for message in websocket: print(message) await websocket.send(message) print("Server start : "+get_ipaddress('eth0')) port = 80 asyncio.get_event_loop().run_until_complete( websockets.serve(echo, get_ipaddress('eth0'), port)) asyncio.get_event_loop().run_forever()
.
websocket.server() 함수 호출해서 웹 소켓 서버를 구동시킵니다. 이때 사용할 웹 서버 포트번호와 IP address, 메시지를 받았을 때 실행할 콜백 함수를 함께 전달합니다.
파이썬 코드를 실행시키면 라즈베리파이는 웹 소켓 접속이 있을때까지 대기합니다.
- sudo python webSocketServer.py
이때 표시되는 서버 주소를 기억해주세요.
이제 ESP32 모듈에 웹 소켓 클라이언트를 구현하면 됩니다. 아래 링크의 스케치를 사용하세요.
소스코드 상단의 설정 값을 자신의 환경에 맞게 바꿔줘야 합니다.
.
const char* ssid = "your_ssid"; const char* password = "your_pass"; char path[] = "/"; char host[] = "192.168.1.9"; // 웹소켓 서버 주소
.
공유기 SSID와 비번을 지정해주고, 웹 소켓 서버 주소를 넣어주세요.
이제 ESP32 에 업로드하고 시리얼 모니터를 실행해서 통신 상태를 확인하면 됩니다.
두 장치가 메시지 카운트를 주고 받으면 성공입니다!!
ESP32 모듈에 올린 소스코드를 확인해 보겠습니다. setup() 함수에서 중요한 부분만 추려보면 아래와 같습니다.
.
void setup() { ...... // 서버에 연결 if (client.connect(host, 80)) { Serial.println("Connected"); } else { Serial.println("Connection failed."); } delay(1000); webSocketClient.path = path; webSocketClient.host = host; if (webSocketClient.handshake(client)) { Serial.println("Handshake successful"); } else { Serial.println("Handshake failed."); } }
.
일단 공유기에 연결이 되면 client.connect() 를 통해 라즈베리파이 서버에 연결합니다. 그리고 웹 소켓 연결을 위한 handshake 를 실행합니다. 이 과정이 성공하면 ESP32 와 라즈베리파이는 서로 실시간-양방향 통신이 가능해집니다.
.
void loop() { delay(1000); String data; if (client.connected()) { // 데이터 전송 webSocketClient.sendData("msg count : "+String(count++)); // 데이터 수신 webSocketClient.getData(data); if (data.length() > 0) { Serial.println(data); } } else { Serial.println("Client disconnected."); } delay(3000); }
.
loop() 함수에는 데이터 전송과 수신 코드만 있습니다.
.
.
활용
ESP32 모듈을 이용해서 센서장치를 만들 때, 웹 소켓을 활용하면 서버와 실시간으로 데이터를 주고 받을 수 있습니다. 그럼 서버에서는 실시간 센서 모니터링이 가능하겠죠.
만약 굉장히 많은 수의 센서가 데이터를 전송해야 하는 상황이라면 이런 웹 소켓 방식은 서버측에 버거울 수도 있습니다. 하지만 비교적 적은 수의 센서가 실시간 모니터링이 필요한 데이터를 전송하는 환경이라면 웹 소켓은 한번 고려해 볼만 한 옵션입니다.
참고자료
- ESP32 Arduino: Websocket client
- ESP32 Arduino: Websocket server
- Simple Websocket Client and Server for ESP-8266 (morrissinger)
- Websocket Client and Server for ESP-8266 (Multiple connection)
- 웹소켓 및 HTTP/2 SSE
- 웹 소켓 위키
- 웹소켓 헤더, 프레임 분석 (websocket header, frame)
주의!!! [사물 인터넷 네트워크와 서비스 구축 강좌] 시리즈 관련 문서들은 무단으로 내용의 일부 또는 전체를 게시하여서는 안됩니다. 계속 내용이 업데이트 되는 문서이며, 문서에 인용된 자료의 경우 원작자의 라이센스 문제가 있을 수 있습니다.
.
강좌 전체보기
.