강좌 전체보기
.
이전 실습 예제에서는 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)
주의!!! [사물 인터넷 네트워크와 서비스 구축 강좌] 시리즈 관련 문서들은 무단으로 내용의 일부 또는 전체를 게시하여서는 안됩니다. 계속 내용이 업데이트 되는 문서이며, 문서에 인용된 자료의 경우 원작자의 라이센스 문제가 있을 수 있습니다.
.
강좌 전체보기
.
많은 정보 얻어갑니다 정말 감사합니다.
하나 여쭤보고 싶은 부분이 있습니다
아두이노와 서버가 TCP 연결상태 중에 서버는 소켓 통신 외에 다른일들은 처리 못하는건가요?
예를들면 안드로이드 어플로 아두이노를 제어하기 위해 서버를 거치는 방식으로 iot 프로젝트를 진행하려고 하는데
아두이노와 서버가 소켓 통신이 진행중이라면, 서버와 안드로이드도 소켓 통신이 가능한지 궁금합니다.
아니면 아두이노와 서버가 소켓 통신 중에, 서버와 안드로이드는 단순 httpURLconnection 연결을 통해 통신이 가능한지 궁금합니다
감사합니다 잘 써먹을게요^^