webOS Article/4. webOS 활용하기

webOS와 websocket을 이용하여 LED 제어하기 2 : ESP와 LED ↔ server

문윤미 2021. 8. 5. 23:14

  • 전체 시스템 소개
  • ESP+LED+websocket
  • Enact webapplication
  • websocket server
  • 시스템 연동

 

ESP와 LED, Websocket 연결하기


먼저, Enact web application을 제외한 ESP보드 및 LED (websocket 클라이언트)와 서버의 통신에 대해 알아보겠습니다.

 

1. websocket server 만들기

from simple_websocket_server import WebSocketServer, WebSocket
import json

class SimpleEcho(WebSocket):
    def connected(self):
        print(self.address, 'connected')
        self.send_message('Hi')
    
    def handle(self):
        print(self.address, 'Receive', self.data)
        if(self.data == 'On'):
            msg = {
                'msgType' : 'status',
                'deviceID' : 'CML0001',
                'ledStatus' : 'On'
            }
        else:
            msg = {
                'msgType' : 'status',
                'deviceID' : 'CML0001',
                'ledStatus' : 'Off'
            }
        self.send_message(json.dumps(msg, indent=4))
        print('Response', msg)

ESP보드 및 LED와 서버가 잘 연결되었는지 확인하기 위해 임시 서버인 simpleEcho를 만들어주었습니다. 

코드는 아래에서 클라이언트와 서버의 통신에 의해 LED에 불이 들어오는 과정에 따라 자세하게 설명할 예정입니다. 

 

IP = 'ip 주소'
PORT = 3001
server = WebSocketServer(IP, PORT, SimpleEcho)
print(IP, PORT, 'SimpleEcho')
server.serve_forever()

본인의 pc가 할당받은 ip 주소를 확인한 후 websocket 서버 코드의 아랫부분을 다음과 같이 변경해줍니다.

ip 주소는 powershell에서 ipconfig 명령을 통해 확인 가능합니다.

 

 

 

2. websocket client 만들기 

#include <ESP8266WiFi.h>
#include <WebSocketClient.h>
#include <ArduinoJson.h>
#include <string.h>

const char* ssid     = "wifi ID";
const char* password = "wifi password";
char path[] = "/";
char host[] = "ip 주소";

다음은 websoket client 즉, ESP보드와 LED의 코드입니다.

자신의 pc가 연결된 와이파이의 아이디와 비밀번호, 위에서 확인한 ip 주소로 바꿔줍니다.

 

WebSocketClient webSocketClient;

// Use WiFiClient class to create TCP connections
WiFiClient client;

StaticJsonDocument<200> doc;

int i=0;

void setup() {
  pinMode(D2, OUTPUT);
            
  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network

  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());

  delay(5000);

 

LED를 D2에 연결해주었기에 D2를 output으로 설정하였습니다. 그 이후에는 ESP보드가 와이파이 연결이 잘 되었는지, 할당받은 IP주소는 무엇인지 확인하는 코드입니다. 

실행시키면 일단 "Connecting to : wifiid"의 메시지가 출력될 것이고, 와이파이 연결에 실패했을 때에는 계속해서 “.”이 출력될 것입니다.

반면에 와이파이 연결에 성공했을 때에는 "WiFi connected", "IP address:"와 같은 문구와 함께 ESP보드가 할당받은 ip 주소가 출력됩니다.

 

// Connect to the websocket server
  if (client.connect("ip 주소", port 번호)) {
    Serial.println("Connected");
  } else {
    Serial.println("Connection failed.");
    while(1) {
      // Hang on failure
    }
  }

여기서도 역시 본인의 pc에 할당받은 ip 주소와 설정한 port 번호를 작성해주면 됩니다.

만약 ESP보드(클라이언트)가 서버와 성공적으로 연결되면 "Connected", 연결되지 않았을 때는 "Connection failed."를 출력할 것입니다.

 

  // Handshake with the server
  webSocketClient.path = path;
  webSocketClient.host = host;
  if (webSocketClient.handshake(client)) {
    Serial.println("Handshake successful");
  } else {
    Serial.println("Handshake failed.");
    while(1) {
      // Hang on failure
    }  
  }

}

클라이언트가 서버에 handshake 요청을 보는데 이것이 받아들여지면 "Handshake successful"이 출력되며 받아들여지지 않으면 "Handshake failed."를 확인할 수 있을 것입니다.

 

 

 if(client.connected()){
    webSocketClient.getData(data);
    if (data.length() > 0) {
      Serial.print("Received data: ");
      Serial.println(data);
      printJsonMsg(data);
      if(i%2) {
        webSocketClient.sendData(String("On"));
        i++;
      }
      else {
        webSocketClient.sendData(String("Off"));
        i++;
      }
    } // end of if-data.length
  }// end of if-client.connected

  delay(3000);
}

루프 안에 JSON 데이터를 담을 문자열 형태의 변수를 선언해주었습니다. 참고로 JSON이란 JavaScript Object Notation의 약자로 속성-값이 쌍으로 이루며 {"key":"value"}와 같은 형태로 표현됩니다.

만약 클라이언트가 서버와 성공적으로 연결되면 if(client.connected()) 이하의 코드가 실행될 것입니다.

 

서버와 클라이언트 최초 연결시 서버로부터 data를 받아오는데 1번 websocket 서버 코드에서 connected 함수가 실행됩니다. 서버는 ‘Hi’라는 메시지를 클라이언트에 전달합니다.

따라서 클라이언트는 data를 받고 "Received data: 'Hi'"를 출력합니다. 

 

 

 

ESP 보드 시리얼 모니터 (1)

 

ESP 보드 시리얼 모니터에는 앞서 설명한 내용을 모두 포함하여 다음과 같은 결과가 출력됩니다.

 

이후 클라이언트는 서버에 한번은 ‘on’, 그 다음엔 ‘off’ 의 응답을 서버에 보냅니다. i가 짝수일 때에는 ‘on’을, 홀수일 때에는 ‘off’를 보내기 위해 조건문을 활용하여 나타냈습니다.

 

그럼 data를 받은 서버는 1번 websocket server 코드를 보면 알 수 있듯이 handle 함수가 실행되어 만약 ‘on’을 받아왔으면 위에 있는 json 포맷을, 만약 ‘off’를 받아왔으면 아래에 위치한 json 포맷의 데이터를 클라이언트에 다시 전송합니다.

 

 

void printJsonMsg(String& data) {
  auto ret = deserializeJson(doc, data);
  const char* msgType = doc["msgType"];
  const char* deviceID = doc["deviceID"];
  const char* ledStatus = doc["ledStatus"];
  
  Serial.print(msgType); Serial.print("\t");
  Serial.print(deviceID); Serial.print("\t"); 
  Serial.println(ledStatus);

서버로부터 데이터를 전송받았을 때 실행되는 printJsonMsg 함수는 다음과 같습니다. 이때 주의할 점은 서버와 클라이언트 간의 통신에서 데이터 교환이 일어날 시에는 문자열로 데이터를 주고 받아야한다는 것입니다. 그러나 코딩할 때에는 object 형식을 선호하기 때문에 deserializeJson을 통해 문자열 형태의 data를 다시 object 형태로 바꾸어주는 것입니다. 그리고 클라이언트가 서버로부터 받은 json 포맷의 data의 value를 차례로 출력합니다.

 

// Serial.println(String(ledStatus).compareTo("On"));
  if(!String(ledStatus).compareTo("On")) {
    digitalWrite(D2, HIGH);
  }
  else{
    digitalWrite(D2, LOW);
  }
}

서버에서 받아온 ledstatus가 "On"이면 LED가 켜지고, "Off"이면 LED가 꺼지도록 구현한 코드입니다. 

 

 

ESP 보드 시리얼 모니터 (2)

 

이해를 돕기위해 시리얼 모니터의 사진을 첨부하였습니다. 서버로부터 'Hi'를 받아온 이후에는 클라이언트가 번갈아가면서 On,Off 중 무엇을 서버로 보냈는지에 따라 서버가 다시 클라이언트로 보내는 응답이 달라지므로 LED를 제어할 수 있게 되는 것입니다.