오랜만에 블로그에 글을 남긴다. 

     

      지난 7월부터 12월 초까지 회사일과 집안일 때문에 정신이 없다가 이제 회사일은 끝내논 상태라 조금 여유가 생겼다. 그렇다고 해도 끊어지지 않는다. 이건 팔자에 가깝다고 본다. 어쨋든 틈틈히 진행한 것들은 많은데 정리를 어떻게 할지를 고민하고 있다. 퇴근하고 글을 적는게 그리 쉬운일이 아니고, 주말은 또 주말대로 너무 바쁘다. 그렇지만, 잊어버리지 않기 위해서 차근차근 업데이트를 하려고 한다. 

     

    그 중에서 최근 필자가 해보고 싶었던 ESP32에 Ethernet을 연결하여 사용하는 것이다. 그러면 뭐 간단하게는 유무선 공유기 같은 녀석으로도 만들수 있을 것같고 해서 말이다. 물론, 시중에서 싼걸 구매해서 쓰는게 훨씬 낫겠지만, 앞으로 필자가 개발하는 시스템에서 이러한 녀석이 필요할 지도 모른다는 생각이들어서 미리 진행해보았다. 

     

    1. 개발환경 

      필자가 진행한 개발환경은 다음과 같다. 

      - 하드웨어 : ESP32-S3-DevKitC-1 v 1.1 (정품)

      - 소프트웨어 : Visual Studio Code + ESP-IDF 5.4.0

     

    필자가 플랙스한 espressif 정품 S3

     

    2. 소스코드 

      소스코드는 다음과 같다. 

    #include <string.h>
    #include "esp_log.h"
    #include "esp_event.h"
    #include "nvs_flash.h"
    #include "esp_netif.h"

    #include "driver/spi_master.h"
    #include "driver/gpio.h"

    #include "esp_eth.h"
    #include "esp_eth_mac.h"
    #include "esp_eth_phy.h"
    #include "esp_eth_netif_glue.h"

    #include "esp_system.h"

    static const char *TAG = "W5500";

    // ESP32의 IP 설정 -----------------
    #define PIN_MOSI 11
    #define PIN_MISO 13
    #define PIN_SCLK 12
    #define PIN_CS   10
    #define PIN_INT  9
    #define PIN_RST  3

    #define SPI_HOST SPI2_HOST

    static esp_netif_t *s_eth_netif = NULL;
    static esp_eth_handle_t eth_handle = NULL;


    // ESP32에서 링크가 붙으면 발생되는 이벤트에 대한 처리를 위함.
    static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
    {
        switch (event_id) 
        {
        case ETHERNET_EVENT_CONNECTED:
            ESP_LOGI("ETH", "Link Up");
            // ESP32에서 SPI기반의 W5500 사용 시, Eth가 시작된 이후 DHCP가 진행되는 타이밍이 틀어지면 IP를 받지 못함
            // 따라서, 이벤트에서 커넥트가 되면 바로 DHCP 클라이언트 기능을 시작시키도록 해야함.
            ESP_ERROR_CHECK(esp_netif_dhcpc_start(s_eth_netif)); 
            break;

        case ETHERNET_EVENT_DISCONNECTED:
            ESP_LOGI("ETH", "Link Down");
            // 링크가 끊어지면 DHCP Client도 중단.
            esp_netif_dhcpc_stop(s_eth_netif);
            break;
        }
    }

    // ESP32에서 ip 이벤트가 발생시 처리를 위한 핸들러이다. 
    static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
    {
        // IP가 할당되면 할당된 IP를 출력한다.
        ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
        ESP_LOGI(TAG, "Assigned IP from DHCPs : " IPSTR, IP2STR(&event->ip_info.ip));
    }

    // ESP32의 main()으로 모든 장치를 초기화 하고  이더넷 장치를 시스템에 등록한다.
    void app_main(void)
    {
        ESP_ERROR_CHECK(nvs_flash_init());
        ESP_ERROR_CHECK(esp_netif_init());
        ESP_ERROR_CHECK(esp_event_loop_create_default());

        ESP_ERROR_CHECK(gpio_install_isr_service(0));

        // 네트워크 인터페이스 생성
        esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
        s_eth_netif = esp_netif_new(&netif_cfg);

        // 핸들러 등록
        ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
        ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));

        // Wiznet W5500 리셋 핀 설정 및 리셋 진행
        gpio_config_t rst_cfg = {
            .pin_bit_mask = 1ULL << PIN_RST,
            .mode = GPIO_MODE_OUTPUT,
        };

        gpio_config(&rst_cfg);
        gpio_set_level(PIN_RST, 0);
        vTaskDelay(pdMS_TO_TICKS(100));
        gpio_set_level(PIN_RST, 1);
        vTaskDelay(pdMS_TO_TICKS(100));


        // INT 핀을 풀업으로 설정한다.
        gpio_config_t int_cfg = {
            .pin_bit_mask = 1ULL << PIN_INT,
            .mode = GPIO_MODE_INPUT,
            .pull_up_en = GPIO_PULLUP_ENABLE,
        };
        gpio_config(&int_cfg);

        // SPI 버스 설정 및 초기화 진행
        spi_bus_config_t buscfg = {
            .mosi_io_num = PIN_MOSI,
            .miso_io_num = PIN_MISO,
            .sclk_io_num = PIN_SCLK,
            .quadwp_io_num = -1,
            .quadhd_io_num = -1,
            .max_transfer_sz = 4096,
        };
        ESP_ERROR_CHECK(spi_bus_initialize(SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));

        // SPI 디바이스 설정 - 길이가 긴 와이어로 했더니 20M는 정상동작이 안되는 문제가 있었다. 그래서 8M로 설정.
        spi_device_interface_config_t devcfg = {
            .clock_speed_hz = 8 * 1000 * 1000,
            .mode = 0,
            .spics_io_num = PIN_CS,
            .queue_size = 20,
        };
        eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(SPI_HOST, &devcfg);
        w5500_config.int_gpio_num = PIN_INT;

        // 이더넷 및 맥 설정
        eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
        eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();

        // 설정된 맥 및 파이 적용
        esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
        esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config);

        // 맥과 파이등 정보를 이용하여 이더넷 설정 및 장치 설치
        esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
        ESP_ERROR_CHECK(esp_eth_driver_install(&eth_config, &eth_handle));

        // W5500의 경우 MAC 정보가 없는 경우가 대부분이다. MAC이 없는경우, 공유기등에서
        // 차단시킬 수 있으므로, 임의로 MAC을 지정해주어야 한다.
        uint8_t eth_mac[6] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xab };
        ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, eth_mac) );
        ESP_ERROR_CHECK(esp_netif_attach(s_eth_netif, esp_eth_new_netif_glue(eth_handle)));

        // 이더넷 장치를 시작한다. 
        ESP_ERROR_CHECK(esp_eth_start(eth_handle));
    }

     

     

    3. 실형결과

      빌드를 진행하고 펌웨어를 다운로드(퓨징)하고, 터미널을 통해 확인된  실행 결과는 아래와 같다. 크리티컬한 경고나 오류가 발생되지 않음을 확인할 수 있다. 그리고 맥어드레스도 12:34:56:78:90:ab로 설정된 것을 확인할 수 있고 말이다. 마지막으로 DHCP를 통해 할당받은 ip, mask, gw가 표시됨을 확인할 수 있다.

     

     

    이제 PC에서 ESP32로 Ping을 보내보면 다음과 같이 정상적으로 전송되는 것을 확인할 수 있다.

    PC에서 Ping 테스트를 진행

     

     

    이정도 했으면, 이제 무선으로 부터 데이터를 받아 유선 이더넷으로 보내거나. 반대의 경우도 충분히 만들수 있겠다. 전에 무선하고 웹 서버도 했었으니 다 합치면 간단한 AP장비를 만들어 사용할수 도 있고, 유무선 통신을 위해서 작은 소형장치가 필요하다면, 유선 장비를 무선장비로 쉽게 바꿀수 있다. 게다가 굳이 시리얼로 변경하지 않고 네트워크로 말이다. 

    반응형
    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기