본문 바로가기

WCH/CH32V20x

CH32V208 SSM - Ethernet Web Server 테스트

 

 

CH32V208을 이용하여 기본적인 이더넷 테스트가 되었기 때문에 Ethernet Web Server 테스트를 해 보자. 28핀 소형 QFN패키지에도 Ehternet Phy(10M) 가내장되어 있어 소형의 이더넷 웹서를 제작할 수 있을것 같다.

 

우선 가장 간단하게 웹페이지를 출려하는 웹서버를 만들어 보자.

CH32V208을 이용한 WebServer 기본 뼈대 코드는 아래와 같다.

int main(void)
{
    u8 i;  // 반복문에서 사용할 변수 (8비트 정수)

    Delay_Init();                  // 시스템 딜레이 초기화 (타이머 기반)
    USART_Printf_Init(115200);     // USART 초기화 (보드와 PC 간의 시리얼 통신, 속도 115200bps)
    GPIOInit();                    // GPIO 포트 초기화

    printf("WEB SERVER\r\n");      // 프로그램 시작 메시지 출력
    printf("SystemClk:%d\r\n", SystemCoreClock); // 현재 시스템 클록 주파수 출력
    printf("net version:%x\n", WCHNET_GetVer()); // 네트워크 라이브러리 버전 확인

    // 컴파일 시 정의된 라이브러리 버전과 실제 동작 중인 네트워크 스택 버전 비교
    if ( WCHNET_LIB_VER != WCHNET_GetVer()) {
        printf("version error.\n");   // 버전 불일치 시 에러 메시지 출력
    }
	
    // 현재 설정된 IP 주소 출력
    printf("ip:\n");
    for (i = 0; i < 4; i++)
        printf("%d.", IPAddr[i]);
    printf("\n");

    WCHNET_GetMacAddr(MACAddr);    // 칩의 고유 MAC 주소를 가져옴
    printf("mac addr:");
    for (i = 0; i < 6; i++)
        printf("%x ", MACAddr[i]); // MAC 주소 6바이트 출력
    printf("\n");

    http_request = (st_http_request*) RecvBuffer; // 수신 버퍼를 HTTP 요청 구조체로 캐스팅

    TIM2_Init(); // TIM2 타이머 초기화 (주기적 이벤트 처리용)

    // 이더넷 라이브러리 초기화 (IP, 게이트웨이, 서브넷, MAC 주소 등록)
    i = ETH_LibInit(IPAddr, GWIPAddr, IPMask, MACAddr);
    mStopIfError(i); // 에러 발생 시 멈추기
    if (i == WCHNET_ERR_SUCCESS)
        printf("WCHNET_LibInit Success\r\n"); // 초기화 성공 메시지

    WCHNET_CreateTcpSocketListen(); // TCP 소켓 생성 및 리슨 모드 설정 (웹 서버 포트용)

    // 설정용 TCP 소켓 생성 (외부 장치와 통신하는 별도의 포트)
    WCHNET_CreateCfgSocket(Port_CfgBuf->mode, Port_CfgBuf->des_ip, DESPORT, SRCPORT);

    // 메인 루프: 네트워크 스택과 웹 서버 동작 유지
    while(1)
    {
        /* 이더넷 스택의 메인 태스크 수행
         * 내부 데이터 처리 및 네트워크 패킷 관리 */
        WCHNET_MainTask();

        /* 이더넷 전역 인터럽트 발생 여부 확인
         * 발생 시 전역 인터럽트 핸들러 실행 */
        if(WCHNET_QueryGlobalInt())
        {
            WCHNET_HandleGlobalInt();
        }

        // 웹 서버 동작 함수 호출 (HTTP 요청 처리 등)
        Web_Server();
    }
}

 

 

Web_Server 함수에서 웹페이 요청에 대한 처리를 해 주면된다.

특히 METHOD_GET 메시지에 맞게 index 페이지나 led 제어 요청에 대한 페이지 처리를 해 주면 된다.

void Web_Server(void)
{
    char *para_p;   // POST 요청에서 전달된 파라미터 위치를 가리키는 포인터
    u32 len;        // 전송할 데이터 길이

    // 새로운 데이터가 수신되었을 때만 실행
    if(DealDataflag){
        DealDataflag = 0;  // 플래그 리셋 (데이터 처리 완료 상태)

        // 수신된 HTTP 요청을 파싱하여 http_request 구조체에 저장
        ParseHttpRequest(http_request, RecvBuffer);

        // HTTP 요청 메소드(GET / POST / ERROR)에 따라 분기 처리
        switch (http_request->METHOD)
        {
            case METHOD_ERR:
                // 파싱 실패나 지원하지 않는 메소드일 경우
                break;

            case METHOD_POST:   // 'POST' 요청 처리
  
                break;

            case METHOD_GET:    // 'GET' 요청 처리
                name = (char*)GetURLName(http_request->URL);   // 요청된 URL 경로 추출
                printf("URL name: %s\n",name);
                ParseURLType(&http_request->TYPE, name);       // 리소스 타입 판별

                // URL이 "/"(루트)
                if (strlen(name) == 1) {
                    len = sizeof(Html_index);
			        copy_flash(Html_index, len );

                    WCHNET_SocketSend(socket, HtmlBuffer, &len);
                } 
                
                // 브라우저가 자동으로 요청하는 favicon.ico (아이콘) 무시
                else if(strstr(name, "favicon.ico") != NULL) {
                    printf("-> %s\n", name);
                }
                // "/off" 요청 → LED OFF
                else if(strstr(name, "off") != NULL) {
                    GPIO_WriteBit(GPIOA, GPIO_Pin_0, 1);   // LED OFF
                    printf("LED OFF-> %s\n", name);

                    len = sizeof(Html_index);
        			copy_flash(Html_index, len );

                    WCHNET_SocketSend(socket, HtmlBuffer, &len);
                }
                // "/on" 요청 → LED ON
                else if(strstr(name, "on") != NULL) {
                    GPIO_WriteBit(GPIOA, GPIO_Pin_0, 0);   // LED ON
                    printf("LED ON-> %s\n", name);

                    len = sizeof(Html_index);
        copy_flash(Html_index, len );

                    WCHNET_SocketSend(socket, HtmlBuffer, &len);
                }


                // 처리 완료 후 소켓 연결 종료
                WCHNET_SocketClose(socket, TCP_CLOSE_NORMAL);
                break;

            default:
                // 정의되지 않은 HTTP 메소드 처리
                break;
        }
    }
}

 

 

index 페이지는 Html_index 버퍼에 저장하면된다. 우선 간단하게 아스키로 페이지를 만들고 추후 복잡한 페이지는 바이너리로 변환해서 저장 하도록 하면 된다.

const char Html_index[] = {
"<!DOCTYPE html><html><body><center><h1>CH32V208 Simple Web Server - &#128522;</h1>\
<a href=\"led_on\"><button>LEDON</button></a>    \
<a href=\"led_off\"><button>LEDOFF</button></a></center></body></html>"
};

 

 

이렇게 수정후 웹서버에 접속하면 간단한 페이지가 출력되고 LED ON, LED OFF  버튼의 요청에 따라 페이지를 출력하고 LED를 제어 하는 코드가 수행된다.

 


이제 조금 더 복잡한 웹페이지를 수행해 보자.

Web_Server 함수에서 웹페이지에서 요청하는 다양한 페이지에 대한 처리를 추가 해주면 된다.

void Web_Server(void)
{
    char *para_p;
    u32 len;

    if(DealDataflag){
        DealDataflag = 0;
        ParseHttpRequest(http_request, RecvBuffer);
        switch (http_request->METHOD)
        {
            case METHOD_ERR:
                break;

            case METHOD_POST:                                       //'post' request
              
                break;

            case METHOD_GET:                                        //'get' request
                name = (char*)GetURLName(http_request->URL);
                printf("URL name: %s\n",name);
                ParseURLType(&http_request->TYPE, name);


                if (strlen(name) == 1) {
                    //Refresh_Html(Html_index, Para_Login, 2);

                    copy_flash(Html_index, sizeof(Html_index));

                    len = strlen(HtmlBuffer);
                    WCHNET_SocketSend(socket, HtmlBuffer, &len);
                } 
                else if(strstr(name, "favicon.ico") != NULL) {             //Request to get the "main" web page

                    printf("-> %s\n", name);

                }                   
                else if(strstr(name, "led_off") != NULL) {             //Request to get the "main" web page
                    GPIO_WriteBit(GPIOA, GPIO_Pin_0, 1);
                    printf("LED OFF-> %s\n", name);

                    //Refresh_Html(Html_index, Para_Login, 2);
                    copy_flash(Html_index, sizeof(Html_index));
                    len = strlen(HtmlBuffer);
                    WCHNET_SocketSend(socket, HtmlBuffer, &len);
                }                  
                else if(strstr(name, "led_on") != NULL) {             //Request to get the "main" web page
                    //GPIO_ResetBits(GPIOA, GPIO_Pin_0);
                    GPIO_WriteBit(GPIOA, GPIO_Pin_0, 0);
                    printf("LED ON-> %s\n", name);

                    //Refresh_Html(Html_index, Para_Login, 2);
                    copy_flash(Html_index, sizeof(Html_index));
                    len = strlen(HtmlBuffer);
                    WCHNET_SocketSend(socket, HtmlBuffer, &len);


                }
                else if(strstr(name, "lamp_1.png") != NULL) {
                    len = sizeof(PNG_lamp_1);
                    copy_flash(PNG_lamp_1, len);
                    WCHNET_SocketSend(socket, HtmlBuffer, &len);
                }                
                
                /*After the request is processed, the current
                 * socket connection is closed, and a new connection
                 * will be established when the browser sends the next
                 * request.*/
                WCHNET_SocketClose(socket, TCP_CLOSE_NORMAL);
                break;

            default:
                break;
        }
    }
}

 

 

이미지도 출력하고 좀더 복잡한 페이지도 쉽게(? 불편한점이 많다) 출력 가능하다. 비교 할만한 칩셋으로 ESP32C3 + W5500웹서버 테스트 예제와 비교하면 부족한 점이 많지만 가격대비 성능로 보면 간단한 프로젝트에는 적용하기 좋을것 같다.

반응형