ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ESP32] ESP32 WOL 서버 + LAN 이야기
    ESP32 2024. 1. 14. 20:24

     공유기 포트포워딩과 윈도우 설정창에서 몇번 클릭만 하면 집 밖에서 편하게 원격접속을 할 수 있다. 그러나 원격 접속을 하기위해 컴퓨터를 24시간 켜두는 것은 부담스러울 것이다. PC 전원까지 원격으로 켤 수 있으면, 더할 나위 없을 것이다. 공유기에 WOL 서버가 내장되어 있기는 하지만, 이를 통해 PC 전원을 킬려면 원격 접속 포트를 열어두어놔야 한다. 고작 PC를 켜자고 공유기 원격 포트를 열어두는 것은 보안상 좋지 않고, WOL 서버를 직접 만들어보게 되었다. 라즈베리파이 같은 걸 24시간 켜두면서 WOL 서버로 사용하거나, 다른 IoT 장치를 구매하여 원격으로 컴퓨터의 전원버튼을 누르는 등 여러 방법이 있겠지만, 지금부터 소개할 ESP32로 WOL 서버를 구축하는 것이 가장 컴팩트하고 저렴한 방법일 것이라 생각한다.

    0. 준비할 것

    1.ESP32 devkit 1, ESP-C3 Super mini등 ESP 칩셋이 있는 마이크로컨트롤러 개발보드 + 케이블, 스마트폰 충전기

    (단 싱글코어인 ESP8266에서 멀티스레딩 코드가 제대로 작동할 지 확인하지 못함)

    *제가 테스트해본 것은 ESP32 devkit 1과 ESP-C3 Supermini 입니다.

     

    2. DHCP 고정 할당과 포트포워딩을 할 수 있는 공유기

    U+ GADP7500 공유기처럼 분명 포트포워딩 4개를 설정했는데 그중 하나만 제대로 작동하거나, (예전에는 잘 되었지만)DHCP 고정할당을 하면 '192.168.219.1에서 보낸 데이터가 없습니다' 같은 메세지를 띄우는 공유기가 있다. 게다가 LoopBack 기능도 없어서 내부에서는 공유기의 공인아이피를 통해 접속을 못하고, 데이터를 이용해서 테스트해보아야 한다. 이런 공유기를 사용하는 경우, 괜히 삽질하지 말고 VPN과 라즈베리파이를 사용하는 방법으로 택하는 것을 추천한다. Google Cloud Plaform(GCP)같은 외부에 VPN 서버를 구축한 뒤, 라즈베리파이를 24시간 VPN에 연결해두어, 라즈베리파이를 통해 Magic Packet을 전송하고, VPN를 통해 원격접속하면 된다.

     

    3. Ubuntu

    Ubuntu를 설치하기 어려운 경우라면 WSL을 이용하면 된다.

    https://sidreco.tistory.com/17

     

    [WSL]윈도우에서 리눅스 시스템 사용하기

    원도우가 GUI가 잘되어 있어 일반적인 목적으로는 사용하기 편리하지만, 개발을 하기에는 불편한 점이 많다. 리눅스를 사용해보았다면 알 수 있겠지만, 명령줄(Command Line)에서 작업할 일이 많은

    sidreco.tistory.com

    esp-idf까지 같이 설치해주어야 한다.

     

    4. Python

    #바이너리 업로드용 도구 설치
    pip install esptool 
    python.exe -m pip install esptool #scripts가 환경 변수에 등록되있지 않다면 이 방법으로 pip를 실행시킬 수 있음

    1.배경지식

    이미 알고 있거나, 원리는 별로 궁금하지 않고 작동만 되면 된다고 생각한다면 넘어가도 좋다.

    1.1 Wake On LAN(WOL)

     Wake on Lan은 1996년 Intel과 IBM이 공동으로 개발한 네트워크 표준으로 Magic Packet이라 하는 특정 패킷을 통해 컴퓨터의 전원을 키는 방식이다. WIFI 등을 무선 네트워크를 통해 전원을 켜고 싶다면 Wake on Wireless Lan(WoWLAN)을 사용해야 한다. (2024-01-14 위키피디아 웨이크 온 랜) 아래는 Wireshark로 캡처한 매직패킷이다. (맥주소의 뒷부분은 가렸다. 참고로 맥주소의 앞에 절반은 제조업체를 식별하기 위한 부분이고, 뒷부분은 장치 고유의 번호이다.)

    WireShark로 캡처한 Iptime 공유기에서 전송한 WOL 패킷 전문
    WireShark로 캡처한 Iptime 공유기에서 전송한 WOL 패킷

    앞에 42 바이트는 이 패킷이 어디서왔으며, 어디로 전송되어야 하는지, 패킷의 길이는 얼마이며, 패킷의 무결성 검증에 이용되는 부분등이 있다. 그리고 뒤에 102 바이트는 내용물에 해당하며, ff ff ff ff ff ff가 나온 뒤 대상의 맥주소가 16번 반복된다.

    https://calvinjmkim.tistory.com/12

     

    그리고 WOL 패킷은 특별한 네트워크 장비를 사용하는 경우가 아니라면, 반드시 브로드캐스트 주소로 전송해야 한다. 이유를 알려면 패킷에 대해 조금 더 자세히 살펴볼 필요가 있다..

    위 사진처럼 패킷의 첫부분은 대상의 MAC 주소이며(브로드캐스트인 경우 ff ff ff ff ff ff), 이를 통해 랜카드는 자신에게 온 패킷인지 확인한 후, 자신에게 온 것이 아니라면 무시한다. 그래서 패킷을 전송하려면 받을 대상의 맥주소를 알아야 하며, Address Resolution Protocol(ARP) 프로토콜을 통해 같은 LAN에 있는 IP에 대응되는 맥주소를 알아내고, 이를 ARP 테이블에 저장한다.(윈도우에서는 arp -a 를 통해 ARP 테이블을 확인할 수 있다.)

    Linux arp-scan실행때 캡처한 ARP 패킷

    이제 집밖에서 집에 서브넷으로 WOL 패킷을 전송한다고 생각하자.(포트포워딩은 하였다 가정한다.) 컴퓨터가 꺼진 뒤 얼마간은 WOL 패킷을 전송하였을 때, 공유기의 ARP 테이블에 해당 아이피와 맥주소가 남아 있기 때문에 문제없이 컴퓨터가 켜질 것이다.( 실험해보니 U+ GADP7500는 2시간 전후로 갱신되는 듯하다.) 그러나 ARP 테이블이 갱신되고 난 다음에는 아무리 집밖에서 WOL 패킷을 전송한다 하여도, 집 내부 컴퓨터로 패킷이 전송되지 않는다. 아무리 공유기에서 ARP를 보내봤자 이에 응답할 장치의 전원이 꺼진 상태이기 때문이다. (단 ARP 테이블을 고정할 수 있는 공유기라면 해당되지 않는다.) 그러므로 이러한 문제를 피하기 위해선 브로드캐스트 주소로 WOL 패킷을 전송해야 하는 것이다. (브로드캐스트로 전송하는 경우 공유기는 ARP 테이블을 확인한 뒤, ARP로 맥주소를 알아낼 필요없이 패킷의 맨 앞부분에 ff ff ff ff ff ff만 적어서 전송하면 되기 때문이다.)

     

    여기서 대부분의 라우터나 공유기는 같은 서브넷에서만 브로드캐스트 요청을 받아들이고, 외부에서는 브로드캐스트에 접근할 수 없게 하므로, 같은 서브넷에서 브로드캐스트로 매직패킷을 전송할 WOL 서버가 필요한 것이다. 요즘 공유기에는 아래 그림처럼 WOL 서버가 내장되어 있어 편리하게 이용할 수 있지만, 외부 접속 포트를 열어두어야 하므로 보안이 취약해지는 단점이 있다. (만약 공유기 내장 기능을 사용한다면, 접속 아이디와 비번을 잘 관리해야한다.)

    Iptime 공유기의 WOL 기능

     

    1.2 매직 패킷 전송코드

    https://github.com/sidreco214/esp-wol-server

     

    GitHub - sidreco214/esp-wol-server: ESP32 HTTPS WOL Server

    ESP32 HTTPS WOL Server. Contribute to sidreco214/esp-wol-server development by creating an account on GitHub.

    github.com

     

     

    여기에 모든 코드를 설명하기에는 코드가 너무 긴 관계로, 가장 핵심이라 생각하는 Posix Socket을 사용해서 WOL 패킷을 전송하는 부분만 첨부하였다. Magic Packet은 UDP 프로토콜을 사용해서 전송하며, 보통 9번이나 7번 포트를 사용한다.(TCP로 전송할 경우 handshake가 일어나야 하기 때문에 패킷을 못받을 수도 있다.) 

    //util.h
    /*
    nvs 저장 공간을 아끼고, 불러오는 속도를 빠르게 하기 위해서, union을 이용해 mac address를 붙여서 하나의 큰 숫자로 저장함
    
    문자열로 저장시 6*2=12 바이트
    int64_t로 저장시 8 바이트의 저장공간
    */
    typedef union {
        uint8_t addr[6];
        int64_t data;
    } mac_addr_t;
    
    /*후략*/
    
    
    //esp-wol-server.cpp
    /*생략*/
    #define WOL_PORT 9
    #define IP_ADDR_FAMILY AF_INET //ipv4: AF_INET     ipv6: AF_INET6
    #define IP_PROTOCOL IPPROTO_IP //ipv4: IPPROTO_IP  ipv6: IPPROTO_IPV6
    #define UDP_SEND_TIMEOUT 10
    
    /*중략*/
    
    static esp_err_t wol_post_uri_handler(httpd_req_t* req) {
    	//POST wol 요청이 있을 때 호출되는 콜백 함수
    	/*
        	버퍼를 읽어온 뒤, 보낸 인증 정보와 NVS에 저장한 인증 정보가 일치하는 지 확인하는 코드
        */
        
        //create magic packet
        char magic_packet[103] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
        for(char* packet = magic_packet+6; packet < magic_packet + 102; packet += 6)
            memcpy(packet, mac.addr, sizeof(mac.addr));
        
        //send magic packet
        udp_client_send_param_t udp_send_param;
        udp_send_param.dest_ip_addr = broadcast_ip;
        udp_send_param.dest_port = WOL_PORT;
        udp_send_param.addr_family = IP_ADDR_FAMILY;
        udp_send_param.ip_protocol = IP_PROTOCOL;
        udp_send_param.timeout_sec = UDP_SEND_TIMEOUT;
        udp_send_param.message = magic_packet;
        esp_err_t err = udp_client_send(&udp_send_param);
    
        if(err != ESP_OK) {
            //end response
            httpd_resp_send_500(req);
            ESP_LOGE(ESP_HTTPS_SERVER_TAG, "Failed to send magic packet");
            return ESP_FAIL;
        }
    
        //end response
        httpd_resp_send_chunk(req, "OK\n", HTTPD_RESP_USE_STRLEN);
        httpd_resp_send_chunk(req, NULL, 0);
    
        
    }

     

     

    #ifndef _UDP_CLIENT_H_
    #define _UDP_CLIENT_H_
    
    #include <sys/param.h>
    
    #include "lwip/err.h"
    #include "lwip/sockets.h"
    #include "lwip/sys.h"
    #include "lwip/netdb.h"
    
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    
    #include "esp_system.h"
    #include "esp_err.h"
    #include "esp_log.h"
    
    #define UDP_CLIENT_TAG "UDP Client"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    typedef struct {
        char* dest_ip_addr;
        unsigned int dest_port;
        int addr_family;
        int ip_protocol;
        uint timeout_sec;
        char* message;
    } udp_client_send_param_t;
    
    esp_err_t udp_client_send(udp_client_send_param_t* param);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    #include "udp_client.h"
    
    esp_err_t udp_client_send(udp_client_send_param_t *param) {
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(param->dest_ip_addr);
        dest_addr.sin_family = param->addr_family;
        dest_addr.sin_port = htons(param->dest_port);
    
        int sock = socket(param->addr_family, SOCK_DGRAM, param->ip_protocol);
        if(sock < 0) {
            ESP_LOGE(UDP_CLIENT_TAG, "Unable to create socket: errno %d", errno);
            return ESP_FAIL;
        }
    
        //Set timeout
        struct timeval timeout;
        timeout.tv_sec = param->timeout_sec;
        timeout.tv_usec = 0;
        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
    
        ESP_LOGI(UDP_CLIENT_TAG, "Socket created, sending to %s:%d", param->dest_ip_addr, param->dest_port);
    
        while(true) {
            esp_err_t err = sendto(sock, param->message, strlen(param->message), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
            if(err < 0) {
                ESP_LOGE(UDP_CLIENT_TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
            vTaskDelay(1000 / portTICK_PERIOD_MS);
    
            if(sock != -1) {
                ESP_LOGI(UDP_CLIENT_TAG, "Success, shutdown and close socket");
                shutdown(sock, 0);
                close(sock);
                break;
            }
        }
        return ESP_OK;
    }

     

    참고

    https://github.com/espressif/esp-idf/tree/master/examples/protocols/sockets/udp_client

    https://github.com/espressif/esp-idf/tree/master/examples/protocols/http_server/simple

    https://github.com/espressif/esp-idf/tree/master/examples/protocols/https_server/simple

     

    2. 빌드 방법

    README.md에 친절하게 적어두었다고 생각을 하지만, 여기에도 적는다.

    1. 다운로드

    cd ~/esp
    git clone https://github.com/sidreco214/esp-wol-server.git
    cd esp-wol-server
    get_idf
    idf.py set-target esp32 #esp32가 아닌 경우 esp32c3 등으로 알아서 맞출 것

     

    2.menuconfig

    idf.py menuconfig

    Compiler Option -> Optimization Level (Optimize for performance (-O2)) #Release build
    Component Config -> HTTP Server -> Max HTTP Request Header Length 1024
    Component Config -> ESP HTTPS Server -> Enable ESP_HTTPS_SERVER
    Component Config -> ESP System Setting -> CPU frequency(240 MHz)
    Partition Table -> Partition Table (Custom partition table CSV)

     

    ESP32-C3 Super mini 인경우 추가 설정

    ESP WOL Server configuration -> [*]LED On-State Inversion

     

    이외에도 Serial flasher config에서 flash 사이즈를 맞출 것

     

    3.configuration

    3.1 openssl

    mkdir certs
    openssl req -newkey rsa:2048 -nodes -keyout certs/prvtkey.pem -x509 -days 3650 -out certs/servercert.pem -subj "/C=KR/CN=ESP32 WOL Server"

     

    3.2 WIFI 데이터, 브로드캐스트 IP 정보 입력

    이 방법 외에도 업로드 후 시리얼 통신으로도 입력할 수 있다.(후술)

    vim nvs-partition.csv

    vim이 없으면 sudo apt install vim -y 로 설치할 것

    i를 눌러 insert모드로 변경한 뒤 수정을 하고, esc로 빠져나온 뒤 :wq를 입력해서 저장하고 나오면 된다.

    nvs-partition.csv 형식

    key,type,encoding,value
    Storage,namespace,,
    WIFI_SSID,data,string,YOUR_WIFI SSID
    WIFI_PW,data,string,YOUR_WIFI_PASSWORD
    BROADCAST_IP,data,string,YOUR_BRAOADCAST_IP
    USERNAME,namespace,,
    pw,data,string,YOUR_PASSWORD
    mac,data,i64,YOUR_MAC_DATA
    USERNAME2,namespace,,
    pw,data,string,YOUR_PASSWORD2
    mac,data,i64,YOUR_MAC_DATA2
    ...

    예시

    key,type,encoding,value
    Storage,namespace,,
    WIFI_SSID,data,string,wifissid
    WIFI_PW,data,string,12345678
    BROADCAST_IP,data,string,255.255.255.255
    Home_PC,namespace,,
    pw,data,string,1234
    mac,data,i64,255781797119858

    참고로 255.255.255.255는 ipv4 전체에 대한 브로드캐스트 주소이나 대부분의 내트워크 장비에서는 상위 네트워크까지 브로드캐스트 패킷이 넘어가지 않는다.(이게 쉽게 가능하였다면, 전세게 인터넷은 브로드캐스트 테러로 인해 수시로 다운되었을 것이다.) 따라서 테스트 해보지는 않았지만, 이중 공유기인 상황이라면, 상위 네트워크에 있는 장치로 브로드캐스트 패킷을 전달하기 위해선, 192.168.0.255 처럼 특정 서브넷에 대한 브로드캐스트 주소를 사용해야할 것이다. (이는 서브넷 마스크에 따라 달라지니 유의할 것)

     

    mac설정 방법

    계산기 열어서 프로그래머 탭으로 변경

    hex클릭

    cmd를 열어서 ipconfig /all

    인터넷이 연결된 어댑터의 맥주소를 찾을 것(WSL이 설치되어 있으면, vEthernet (WSL) 등이 있고, wifi 기능이 있는 경우도 이더넷 어댑터가 여러개 있으니 주의할 것)

     

    만약 맥주소가 72-47-28-d7-a1-e8이면 계산기에 다음과 같이 거꾸로 입력한다.

    DEC을 클릭해서나온 숫자를 nvs-partition.csv에 붙여넣으면 된다.

     

    nvs 파티션 생성

    python ~/esp/esp-idf/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py generate nvs-partition.csv nvs-partition.bin 0x6000

     

    3.build

    idf.py build

    4.업로드

    만약 그냥 우분투라면 편하게 다음 명령어를 입력하면 된다.

    idf.py -p /dev/ttyUSB0 flash
    python ~/esp/esp-idf/components/partition_table/parttool.py -p /dev/ttyUSB0 write_partition --partition-name=nvs --input nvs-partition.bin
    idf.py -p /dev/ttyUSB0 flash monitor

     

    그러나 WSL 환경이고, WSL에 시리얼 포트를 연결하는 것이 귀찮기 때문에 생성된 파일을 윈도우로 가져와서 윈도우에서 업로드할 것이다.

    빌드하고 뜬 메세지를 참고하면 다음과 같다.

    python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size 4MB --flash_freq 40m 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/esp-wol-server.bin 0x187000 build/storage.bin

    build/bootloader/bootloader.bin

    build/partition_table/partition-table.bin

    build/esp-wol-server.bin

    build/storage.bin

    이 파일과 아까 생성한 nvs-partition.bin만 윈도우로 가져오면 되는 것이다.

     

    참고로 COM 포트를 알기 위해선

    Win + X -> 장치관리자

    이렇게 COM 포트를 알 수 있다.

     

    pip install esptool
    python -m esptool --chip esp32 -b 460800 -p COM3 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size 4MB --flash_freq 40m 0x1000 bootloader.bin 0x8000 partition-table.bin 0x9000 nvs-partition.bin 0x10000 esp-wol-server.bin 0x187000 storage.bin

     

    컴퓨터에 연결하였을 때는 전력이 부족해서 WIFI 연결이 안될 수도 있다. 충전기에 연결하면 아래 사진 처럼 WIFI가 잘 연결된다.

    WIFI가 연결되면 내장 LED의 불이 켜진다.(파란색)

    충전기에 연결해도 불이 켜지지 않는 경우는 WIFI SSID와 비번을 다시 한 번 확인해보고, LED 로직이 반전된 보드가 아닌 지 확인해보면 된다. (위에서도 언급하였지만 일부 개발보드(ESP32-C3 Super mini 등)은 LED의 cathode가 GPIO에 연결되어 있어서 로직이 반전되야 하기때문에 이 경우 반드시 menuconfig -> ESP WOL Server configuration -> [*]LED On-State Inversion 을 설정해주어야 한다.)

     

    번외 시리얼 통신으로 데이터 업로드하기

    컴퓨터에 연결하였을 때는 전력이 부족해서 WIFI 연결하려고 하면 Brownout Detector가 작동하여 계속 재시작하기 때문에 시리얼로 데이터를 업로드하기 어렵다. 이때는 충전기에 한번 꽃아서 캐패시터를 충전시킨 뒤 다시 컴퓨터에 연결하면 문제없이 진행할 수 있다. 시리얼 콘솔은 아두이노 시리얼 콘솔이나 VSCode 확장, puTTY등 아무거나 사용해도 된다.

     

    3. 서버가 잘 돌아가고 있는 지 확인하기

    먼저 내부에서 테스트를 해보는 것을 추천한다.

    arp -a(윈도우), arp-scan(리눅스), 공유기 페이지 등에서 ESP32가 어떤 아이피를 할당 받았는지 확인한 다음

    https://xxx.xxx.xxx.xxx 이동하면 서버가 잘 돌아가고 있는지 확인해볼 수 있다.

    만든 인증서가 신뢰할 수 있는 Certicate Authority(CA)에서 서명해준 것이 아니라 self-signed이기 때문에 이런 경고가 뜨는 것이다.

     

    고급 -> 안전하지 않음으로 이동 하면 아래 사진에 있는 문구가 반겨준다.

     

    4. cURL로 WOL 요청을 보내기

     Client URL(cURL)은 크로스 플랫폼으로 개발되어 윈도우에서 사용할 수 있다.

    또한 별도에 설치할 필요 없이 윈도우에 내장되어있어 cmd에서도 curl을 사용할 수 있다. 우리가 보낼 HTTPS 요청을 다음과 같은 내용이다.

    POST /wol HTTP/1.1
    Host: hostname
    Content-Type: application/x-www-form-urlencoded
    Authorization: Basic <Base64_Encoded_Credential>
    
    name=USER_NAME

    아무나 접속해서 PC를 켜면 안되기 때문에 Base64 인증을 사용하여 권한을 제한하였다. 이 요청은 Transport Layer Security(TLS)를 통해 암호화되어 전송되기 때문에, 복호화가 가능한 Base64라도 괜찮을 것이라 생각하였으며, 다음과 같은 명령으로 전송할 수 있다.

    curl -u "<UserName>:<password>" -d "name=<UserName>" -H "Content-Type: application/x-www-form-urlencoded" -k "https://ESP32_IP_ADDRESS/wol"

     

    성공적으로 전송하였다면 다음과 같은 로그를 볼 수 있다.

    Executing action: monitor
    Running idf_monitor in directory /home/hjubuntu/esp/upload
    Executing "/home/hjubuntu/.espressif/python_env/idf5.3_py3.10_env/bin/python /home/hjubuntu/esp/esp-idf/tools/idf_monitor.py -p /dev/ttyUSB0 -b 115200 --toolchain-prefix xtensa-esp32-elf- --target esp32 --revision 0 /home/hjubuntu/esp/upload/build/hello.elf -m '/home/hjubuntu/.espressif/python_env/idf5.3_py3.10_env/bin/python' '/home/hjubuntu/esp/esp-idf/tools/idf.py' '-p' '/dev/ttyUSB0'"...
    --- esp-idf-monitor 1.3.4 on /dev/ttyUSB0 115200 ---
    --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
    ets Jun  8 2016 00:22:57
    
    rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0030,len:7172
    load:0x40078000,len:15532
    load:0x40080400,len:4
    load:0x40080404,len:3904
    entry 0x40080640
    I (29) boot: ESP-IDF v5.3-dev-1288-g5524b692ee-dirty 2nd stage bootloader
    I (29) boot: compile time Jan 14 2024 23:19:17
    I (31) boot: Multicore bootloader
    I (35) boot: chip revision: v1.0
    I (39) boot.esp32: SPI Speed      : 40MHz
    I (43) boot.esp32: SPI Mode       : DIO
    I (48) boot.esp32: SPI Flash Size : 4MB
    I (53) boot: Enabling RNG early entropy source...
    I (58) boot: Partition Table:
    I (62) boot: ## Label            Usage          Type ST Offset   Length
    I (69) boot:  0 nvs              WiFi data        01 02 00009000 00006000
    I (76) boot:  1 phy_init         RF data          01 01 0000f000 00001000
    I (84) boot:  2 factory          factory app      00 00 00010000 00177000
    I (91) boot:  3 storage          Unknown data     01 82 00187000 00005000
    I (99) boot: End of partition table
    I (103) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=37d58h (228696) map
    I (190) esp_image: segment 1: paddr=00047d80 vaddr=3ffb0000 size=03cb8h ( 15544) load
    I (196) esp_image: segment 2: paddr=0004ba40 vaddr=40080000 size=045d8h ( 17880) load
    I (203) esp_image: segment 3: paddr=00050020 vaddr=400d0020 size=cc6c8h (837320) map
    I (491) esp_image: segment 4: paddr=0011c6f0 vaddr=400845d8 size=126f0h ( 75504) load
    I (532) boot: Loaded app from partition at offset 0x10000
    I (532) boot: Disabling RNG early entropy source...
    I (544) cpu_start: Multicore app
    I (553) cpu_start: Pro cpu start user code
    I (553) cpu_start: cpu freq: 240000000 Hz
    I (553) cpu_start: Application information:
    I (556) cpu_start: Project name:     esp-wol-server
    I (561) cpu_start: App version:      0024f31
    I (566) cpu_start: Compile time:     Jan 14 2024 23:19:00
    I (572) cpu_start: ELF file SHA256:  8c97e6201...
    I (578) cpu_start: ESP-IDF:          v5.3-dev-1288-g5524b692ee-dirty
    I (585) cpu_start: Min chip rev:     v0.0
    I (589) cpu_start: Max chip rev:     v3.99
    I (594) cpu_start: Chip rev:         v1.0
    I (599) heap_init: Initializing. RAM available for dynamic allocation:
    I (606) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
    I (612) heap_init: At 3FFB92C0 len 00026D40 (155 KiB): DRAM
    I (618) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
    I (625) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
    I (631) heap_init: At 40096CC8 len 00009338 (36 KiB): IRAM
    I (639) spi_flash: detected chip: generic
    I (642) spi_flash: flash io: dio
    I (648) main_task: StarI (678) NVS: initialize NVS
    I (678) NVS: Opening NVS, namespace: Storage
    I (678) NVS: Closing NVS, namespace: Storage
    NVS Wifi SSID:(보안상 삭제), Wifi PW:(보안상 삭제), Broadcast ip:255.255.255.255
    I (698) wifi:wifi driver task: 3ffc1e68, prio:23, stack:6656, core=0
    I (708) wifi:wifi firmware version: 1200a48
    I (708) wifi:wifi certification version: v7.0
    I (708) wifi:config NVS flash: enabled
    I (708) wifi:config nano formating: disabled
    I (708) wifi:Init data frame dynamic rx buffer num: 32
    I (718) wifi:Init static rx mgmt buffer num: 5
    I (718) wifi:Init management short buffer num: 32
    I (728) wifi:Init dynamic tx buffer num: 32
    I (728) wifi:Init static rx buffer size: 1600
    I (738) wifi:Init static rx buffer num: 10
    I (738) wifi:Init dynamic rx buffer num: 32
    I (738) wifi_init: rx ba win: 6
    I (748) wifi_init: accept mbox: 6
    I (748) wifi_init: tcpip mbox: 32
    I (748) wifi_init: udp mbox: 6
    I (758) wifi_init: tcp mbox: 6
    I (758) wifi_init: tcp tx win: 5760
    I (768) wifi_init: tcp rx win: 5760
    I (768) wifi_init: tcp mss: 1440
    I (768) wifi_init: WiFi IRAM OP enabled
    I (778) wifi_init: WiFi RX IRAM OP enabled
    I (788) phy_init: phy_version 4791,2c4672b,Dec 20 2023,16:06:06
    I (858) wifi:mode : sta (e8:68:e7:21:97:1c)
    I (858) wifi:enable tsf
    I (858) Wifi Station: wifi_init_sta finished.
    I (858) Wifi Station: Try connecting WIFI
    I (878) wifi:new:<9,1>, old:<1,0>, ap:<255,255>, sta:<9,1>, prof:1
    I (878) wifi:state: init -> auth (b0)
    I (878) wifi:state: auth -> assoc (0)
    I (888) wifi:state: assoc -> run (10)
    I (1098) wifi:connected with (보안상 삭제), aid = 2, channel 9, 40U, bssid = (보안상 삭제)
    I (1098) wifi:security: WPA2-PSK, phy: bgn, rssi: -29
    I (1108) wifi:pm start, type: 1
    
    I (1108) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us
    I (1108) wifi:AP's beacon interval = 102400 us, DTIM period = 1
    I (1118) Wifi Station: WIFI is connected
    I (2118) esp_netif_handlers: sta ip: 192.168.0.7, mask: 255.255.255.0, gw: 192.168.0.1
    I (2118) Wifi Station: got ip:192.168.0.7
    I (2118) Wifi Station: connected to SSID:(보안상 삭제), IP:192.168.0.7, MAC Address:e8-68-e7-21-97-1c
    I (3128) SPIFFS: Initializing SPIFFS
    I (3128) SPIFFS: SPIFFS Check
    I (3138) SPIFFS: Partition: toal 11546, used 3514
    I (3168) SPIFFS: SPIFFS unmount
    I (3168) HTTPS Server: Starting Server
    I (3168) esp_https_server: Starting server
    I (3178) esp_https_server: Server listening on port 443
    I (3178) HTTPS Server: Registering URI handler
    I (3188) HTTPS Server: Server configuration done
    I (82838) wifi:<ba-add>idx:0 (ifx:0, 90:9f:33:bb:45:d0), tid:0, ssn:7, winSize:64
    I (82838) esp_https_server: performing session handshake
    I (83558) HTTPS Server: HTTPS POST /wol
    I (83558) HTTPS Server: Received name=HJ
    I (83558) HTTPS Server: WOL Request: name=HJ
    I (83558) NVS: Opening NVS, namespace: HJ
    I (83568) HTTPS Server: Found Authorization header: Basic (보안상 삭제)
    I (83568) HTTPS Server: Authentication success
    I (83578) NVS: Closing NVS, namespace: HJ
    I (83578) UDP Client: Send Magic Packet on Broadcast:255.255.255.255 , MAC:(보안상 삭제)
    I (83588) UDP Client: Socket created, sending to 255.255.255.255:9
    I (84598) UDP Client: Success, shutdown and close socket

    Wireshark로 캡처한 패킷 전문
    Wireshark로 캡처한 패킷 내용

    Discard 프로토콜이라 뜨지만 컴퓨터를 켜는데는 지장이 없다.

     

    5.공유기(Iptime) 설정과 집밖에서 요청 보내기

    테스트한 환경은 유선공유기 밑에 유무선 공유기가 허브 모드로 연결되어있는 환경이다. 따라서 무선공유기쪽은 추가로 설정할 필요가 없었지만, 허브 모드가 아닌 공유기 모드라면 이중 공유기로 두 공유기 모두 포트포워딩을 해주어야 한다.

     

    1.고정 아이피 할당

    로그에서 보였던 ESP32의 맥주소와 아이피를 잘 기억해두었다가, DHCP 서버에 위와 같이 등록해주면 된다.

     

    2.포트포워딩

    HTTPS 프로토콜이 TCP 443번 포트를 사용하므로 내부포트는 TCP 443으로 지정하고, 외부 포트는 내부와 똑같이 설정해도 되고, 사진처럼 보안을 위해 다른 포트를 사용해도 된다.

     

    3. DDNS 설정 (선택)

    밖에서 요청을 보낸다면, 네이버에 '내 아이피'라고 검색하면 나오는 공인 아이피 주소로 접속해야한다. 이 아이피 주소를 잘 적어둔 뒤 사용해도 되지만, 공인 아이피 주소는 유동적이기 때문에 만에하나 주소가 바뀌게 되면(경험상 공유기가 계속 켜져있으면 거의 바뀌지 않는다.) 다시 집에 돌아와 확인해야한다. 이때문에 Dynamic DNS (DDNS)를 사용하여, 내가 지정한 도메인이 항상 공유기의 공인 아이피를 가르키도록 설정하는 것이 좋다.

    4. 외부에서 요청 보내기

    curl -u "<UserName>:<Password>" -d "name=<UserName>" -H "Content-Type: application/x-www-form-urlencoded" -k "https://(지정한 이름).iptime.org:<port>/wol"

    외부와 내부포트를 똑같이 443으로 하였다면 포트 부분을 적을 필요없이 -k "https://(지정한 이름).iptime.org/wol"로 설정하면 된다.

     

    6. WOL 설정

    MSI 공식 홈페이지를 참고하였다.

    https://kr.msi.com/support/technical_details/MB_Wake_On_LAN

     

    MSI Korea

    MSI는 메인보드, 그래픽카드, 노트북, 데스크탑, 모니터, 게이밍 기어 등 다방면에서 최고의 제품을 제공하는 글로벌 하드웨어 브랜드입니다.

    kr.msi.com

    6.1 BIOS 설정

    메인보드마다 설정 방법이 다르므로 여기서는 설명하지 않겠다. 그러나 공통적으로 Advanced 탭에 Wake On Lan 설정이 있으며, 이를 활성화시켜주면 된다. 그리고 ErP 설정을 비활성화해야 하는데, 여기서 ErP는 Energy Using Product의 약자이며, 전원을 꺼진 상태에서도 켜져있는 부분을 의미한다. ErP Saving Option이 켜져있으면, 전원을 껏을 때 렌카드의 전원도 같이 차단되어버려 WOL 패킷을 받을 수 었다. 구형 메인보드를 사용하는 경우 이러한 설정을 할 수 없는 경우가 있는데, 옵션은 없지만 ErP Saving만 제대로 꺼두면 WOL 기능이 작동하는 경우도 있으니 참고할 것.

     

    6.2 렌카드 드라이버 설정

    Win + X키를 누르고 장치 관리자(M)을 누른다.

    네트워크 어댑터에서 본인이 사용하고 있는 유선랜 장치를 찾는다. (모른다면 본인의 메인보드에 무슨 유선랜 카드가 있는 지 검색해볼 것)

    고급 탭에서 WOL 관련 기능이 꺼져있다면 모두 킨다.

    전원 관리탭에서 다음과 같이 설정한다. (사람마다 이 설정을 어떻게 해야하는 지 말이 다른데, MSI 공식 홈페이지에 있는 설명을 따랐다.)

    Win + R을 누른 뒤 control을 입력해서 제어판에 들어간다. 

    하드웨어 및 소리 -> 전원 옵션->전원 단추 작동 설정 버튼을 누른다.

     

    변경 내용을 저장한다.

     

    6.3  Ubuntu 환경인 경우

    sudo vim /etc/netplan/00-installer-config.yaml

     

    #/etc/netplan/00-installer-config.yaml
    network:
      ethernets:
        enp2s0: #예시, 네트워크 장치 이름은 본인 것에 맞추어서 할 것
          dchp4: true
          wakeonlan: true
      version: 2

    7. 원격 데스크톱 설정

    1. win + i를 눌러 설정창을 연다.

    2. 시스템 -> 원격 데스크톱

    참고로 Windows Pro 버전만 원격 데스크톱을 활성화할 수 있다. Home 버전인 경우 윈도우 Pro 키가 있다면 이를 입력해서 업그레이드하거나, Pro로 다시 윈도우를 설치하는 방법이 있다. 

     

    3. 포트포워딩

    IP를 고정할당한 뒤, TCP 3389 포트로 향하는 포트를 열어주면 된다.

     

    4. 로컬 계정 암호 생성

    로컬 계정에 암호가 없다면, 암호를 설정해야 원격 데스크톱을 사용할 수 있다.

     

    5. 다른 pc에서 접속하기

    (공인아이피 or DDNS 도메인):(포트)로 입력해주면 된다.

     

    또는 Win + R을 누른뒤 아래와 같은 방법으로 바로 연결할 수 있다.

    7. 기타

    ESP32-C3 Super mini로 테스트해보았을 때는 연결 상태가 좋지 않은지 몇 번 요청에 실패하기도 하였지만, 쓸 수는 있을 것 같다.

     

     

    댓글

Designed by Tistory.