Socket 사용하기

최영진·2023년 1월 9일
0

ESP32

목록 보기
3/4

1. 라이브러리 설치

https://github.com/Links2004/arduinoWebSockets

위의 링크에서 라이브러리를 설치한다. 위의 라이브러리가 그나마 비교적 자료도 많고 예시도 많아서 사용하기 좋다.
설치하고 나면 IDE에서 'WebSockets'라는 라이브러리명을 찾아볼 수 있다.

내가 사용한 보드는 ESP32 기반이었기 때문에 예제코드 중 ESP32 > 'WebSocketClientSocketIOack.ino'라는 파일을 변형해서 사용했다.
'WebSocketClinet.ino' 파일은 WebSocket 클라이언트를 사용하는 예제이고, 'WebSocketClientSocketIOack.ino' 파일은 SocketIO 클라이언트를 사용하는 예제로 엄연히 다른 예제이다.

2. 예제코드

파일을 열면 파일의 이름과 작성된 날짜가 적힌 주석이 보일 것이다.
그 밑에 헤더파일들이 보일 것인데, 그 중 "WiFiMulti.h"는 사용하지 않을 것이기 때문에 주석처리를 하거나 지워준다. WiFiMulti를 이용해 와이파이에 연결하면 에러가 발생하고 연결도 되지 않기 때문에 사용하지 않는다. 마찬가지로, WiFiMulti로 선언된 객체도 지워준다.

2-1. socketIOEvent()

socketIOEvent 함수는 socketIO를 통해 메시지를 수신하면 메시지를 처리하는 함수이다. setup() 함수에서 socketIO.onEvent(socketIOEvent)를 통해 이벤트 처리 함수로 등록한다.

매개변수

  • socketIOmessageType_t type : 이벤트의 타입을 입력받는 매개변수
  • uint8_t* payload : 이벤트의 데이터의 입력받는 매개변수
  • size_t length : 데이터의 길이를 입력받는 매개변수
    내용
    switch - case문을 이용해 이벤트의 종류에 따라 작동하게 설계되어 있다.
  • sIOtype_CONNECT : socketIO로 연결이 성공할 때 작동
  • sIOtype_DISCONNECT : socketIO로 연결이 해제될 때 작동
  • sIOtype_EVENT : socketIO로 이벤트가 수신됐을 때 작동
    이 부분에 ack 신호를 보내는 코드가 작성되어 있을 텐데, 필요에 따라 안의 내용은 지워도 상관없다. 이벤트의 내용을 계산해서 ack 신호를 보내는 내용에 불과하다.
  • sIOtype_ACK : socketIO로 이벤트를 보내고 ack 신호가 수신됐을 때 작동
  • sIOtype_ERROR : 에러가 발생하면 작동
  • sIOtype_BINARY_EVENT
  • sIOtype_BINARY_ACK

실제 코드

void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
  switch(type) {
    case sIOtype_CONNECT:
      USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);

      // join default namespace (no auto join in Socket.IO V3)
      socketIO.send(sIOtype_CONNECT, "/");
      break;
    case sIOtype_DISCONNECT:
      USE_SERIAL.printf("[IOc] Disconnected!\n");
      break;
    case sIOtype_EVENT:
    /*{
      char * sptr = NULL;
      int id = strtol((char *)payload, &sptr, 10);
      USE_SERIAL.printf("[IOc] get event: %s id: %d\n", payload, id);
      if(id) {
        payload = (uint8_t *)sptr;
      }
      DynamicJsonDocument doc(1024);
      DeserializationError error = deserializeJson(doc, payload, length);
      if(error) {
        USE_SERIAL.print(F("deserializeJson() failed: "));
        USE_SERIAL.println(error.c_str());
        return;
      }

      String eventName = doc[0];
      USE_SERIAL.printf("[IOc] event name: %s\n", eventName.c_str());

      // Message Includes a ID for a ACK (callback)
      if(id) {
        // creat JSON message for Socket.IO (ack)
        DynamicJsonDocument docOut(1024);
        JsonArray array = docOut.to<JsonArray>();

        // add payload (parameters) for the ack (callback function)
        JsonObject param1 = array.createNestedObject();
        param1["now"] = millis();

        // JSON to String (serializion)
        String output;
        output += id;
        serializeJson(docOut, output);

        // Send event
        socketIO.send(sIOtype_ACK, output);
      }
    }*/
      break;
    case sIOtype_ACK:
      USE_SERIAL.printf("[IOc] get ack: %u\n", length);
      break;
    case sIOtype_ERROR:
      USE_SERIAL.printf("[IOc] get error: %u\n", length);
      break;
    case sIOtype_BINARY_EVENT:
      USE_SERIAL.printf("[IOc] get binary: %u\n", length);
      break;
    case sIOtype_BINARY_ACK:
      USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
      break;
  }
}

와이파이에는 연결되었는데 sIOtype_DISCONNECT의 내용만 출력되어서 한동안 고생했던 기억이 있다. 이벤트를 수신했을 때 아두이노를 작동시키고자 한다면 sIOtype_EVENT의 내용을 수정해서 사용하면 된다.

2-2. setup()

기본적인 설정을 하는 함수이다. WiFi 연결과 socketIO 연결도 진행하게 된다.
따로 사용하고 싶은 하드웨어나 장치가 있으면 일반적인 아두이노처럼 pinMode 설정도 하면 된다.
내용

  • Serial 설정
    USE_SERIAL.begin(115200) : Serial.begin() 명령어이다. 115200 대신 보율(baud rate)를 넣으면 된다.
    for 문을 통해 시작 및 재시작할 때 1초마다 대기신호를 시리얼 모니터에 출력한다.
  • WiFi 연결
    WifiMulti.addAP부터 while문까지는 사용하지 않으므로 주석처리하거나 삭제한다. 대신 일반 WiFi.h의 WiFi 연결방법을 사용한다. 아래의 코드를 참고하기 바란다.
    WiFi.begin()에서 SSID와 PASSWORD만 바꿔 사용하면 될 것이다.
    while 문으로 WiFi가 연결되어 있지 않으면 연결될 때까지 기다리게 되어 있다.
    연결이 완료되면 WiFi.localIP() 함수를 통해 연결된 장치의 IP주소를 반환하여 출력한다.
  • socketIO 연결
    socketIO.begin() : socketIO를 시작하는 함수. 매개변수는 순서대로 연결하고자 하는 서버의 IP주소, 포트번호, URL을 입력하면 된다. URL은 바꾸지 말고 그냥 사용한다.
    socketIO.onEvent() : socketIO가 이벤트를 수신했을 때 이벤트를 처리할 이벤트 처리 함수를 등록하는 함수이다.

실제 코드

void setup() {
  //USE_SERIAL.begin(921600);
  USE_SERIAL.begin(115200);

  //Serial.setDebugOutput(true);
  USE_SERIAL.setDebugOutput(true);

  USE_SERIAL.println();
  USE_SERIAL.println();
  USE_SERIAL.println();

    for(uint8_t t = 4; t > 0; t--) {
        USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
        USE_SERIAL.flush();
        delay(1000);
    }
  
  /*WiFiMulti.addAP("EM1", "hjy01033557007");

  //WiFi.disconnect();
  while(WiFiMulti.run() != WL_CONNECTED) {
    USE_SERIAL.print(".");
    delay(100);
  }*/
  WiFi.mode(WIFI_STA);
  WiFi.begin("EM1", "hjy01033557007");
  Serial.println("\nConnecting");

  while(WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(100);
  }
  USE_SERIAL.println();

  String ip = WiFi.localIP().toString();
  USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());

  // server address, port and URL
  socketIO.begin("192.168.0.19", 8080, "/socket.io/?EIO=4");

  // event handler
  socketIO.onEvent(socketIOEvent);
}

2-3. loop()

loop() 함수는 코드 한 줄만 추가하고 자신이 실행하고 싶은 코드를 작성하면 된다.

  • socketIO.loop() : socketIO를 계속 실행시켜주는 함수로, 실시간으로 이벤트를 수신하는 역할을 맡는다. 실질적인 socketIO 작동 함수이다.

예제코드에서는 이벤트를 아두이노에서 보내는 코드도 있다. 이 코드는 간단하게 주석으로 설명하고 넘어가겠다. 아래 코드를 참고해주면 좋겠다.

실제 코드

//일정 시간마다 socketIO 이벤트를 전송하는데, 전에 작동했던 시간을 저장하는 변수이다.
//unsigned long messageTimestamp = 0;
void loop() {
  socketIO.loop();
  
  //현재의 실행시간을 반환하여 변수에 저장한다.
  /*uint64_t now = millis();

  //작동한지 일정 시간이 흐르면 재작동하는데, 아래의 코드 상으로는 2초마다 재작동한다.
  if(now - messageTimestamp > 2000) {
    messageTimestamp = now;

    // creat JSON message for Socket.IO (event)
    //SocketIO로 보낼 JSON 메시지(=Event) 객체를 생성한다.
    DynamicJsonDocument doc(1024);
    JsonArray array = doc.to<JsonArray>();

    // add evnet name
    // Hint: socket.on('event_name', ....
    //객체에 event_name을 추가한다. 이벤트 처리함수의 매개변수 중 type에 해당하는 부분이다.
    array.add("event_name");

    // add payload (parameters) for the event
    //객체에 데이터를 추가한다. 이벤트 처리함수의 매개변수 중 payload에 해당하는 부분이다.
    JsonObject param1 = array.createNestedObject();
    param1["now"] = (uint32_t) now;

    // JSON to String (serializion)
    //JSON 메시지를 문자열로 직렬화한다.
    String output;
    serializeJson(doc, output);

    // Send event
    //직렬화한 메시지를 송신(전송)한다.
    socketIO.sendEVENT(output);

    // Print JSON for debugging
    //보낸 메시지를 시리얼 모니터에 출력하여 확인한다.
    USE_SERIAL.println(output);
  }*/
}

! 위의 내용은 개인의 경혐과 지식을 바탕으로 작성된 것임을 알려드립니다.
! 참고
아두이노에서 Socket.IO 사용해보기
[socket.io] 프로젝트 준비2 - 아두이노 Socket.io

profile
공부한 것을 정리하는 곳

0개의 댓글