본문 바로가기
Firmware Programming

[Firmware Programming] C++ ESP32-CAM 보드 UDP camera frame 패킷 순서 제어 및 실시간 전송

by TYB 2024. 2. 10.
반응형

이전글을 아래 링크 참고

 

[Firmware Programming] ESP32-CAM 보드 usb로 upload 하기

필자는 아두이노 우노를 통해 업로드를 시도하였으나, 실패했다. 이틀 동안 해결방법을 찾아보았으나, 역시 실패하였다. 그래서 USB to gpio핀을 통해 direct로 연결해서 upload를 하였더니, 정상적으

program-developers-story.tistory.com

 

[Firmware Programming] ESP32-CAM 보드 UDP camera frame 패킷 순서 제어 및 실시간 전송

ESP32-CAM 연결 및 업로드, udp 통신방식은 각자 이해가 된 상태라고 가정하고 진행합니다. 참고자료가 있으면 찾아가면서 하려고 했으나, UDP를 통한 실시간 영상 전송 관련 알고리즘이나 코드를 거

program-developers-story.tistory.com

 

 

c++로 변환은 했는데 문제가 발생했다.

 

화질이 QVGA (320x240) 로 너무 구려서 VGA(640x480)로 업데이트 했는데 udp 패킷 순서가 3~4개일 때는 별로 안꼬여서 이전 글의 알고리즘이 잘 작동했는데 8~9개 정도로 늘어나니까 답도 없이 꼬여버렸다...

패킷을 수신하긴 하는데 합치다가 중간에 이가 빠져있으니 저장을 못하고 그냥 다음 이미지 조립으로 넘어가는 걸 반복했음.

 

일단 코드를 올리자면

 

아두이노 쪽 코드 수정은 이 부분만 수정하면 frame size가 결정됨. 

    s->set_framesize(s, FRAMESIZE_VGA);//여기 frame size를 통해 보내는 frame size가 결정됨.
 

 

sensor.h에 들어가보면 FRAMESIZE가 enum으로 정의되어 있고, 원하는 해상도를 선택해서 코드에 넣어주면 적용가능함.

 

그럼 이제 서버측 C++ 코드가 나와줘야겠죠?

 

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")  // Link with ws2_32.lib

#define UDP_PORT 12345
#define MAX_PACKET_SIZE 1450
#define MAX_PACKETS 10

int main() {
    // Initialize Winsock
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "Failed to initialize Winsock." << std::endl;
        return -1;
    }

    // Create a UDP socket
    SOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpSocket == INVALID_SOCKET) {
        std::cerr << "Error creating UDP socket." << std::endl;
        WSACleanup();
        return -1;
    }

    // Bind the socket to the specified IP address and port
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(UDP_PORT);

    if (bind(udpSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cerr << "Error binding UDP socket." << std::endl;
        closesocket(udpSocket);
        WSACleanup();
        return -1;
    }

    // Initialize variables for receiving data
    std::vector<std::vector<uint8_t>> tempData(MAX_PACKETS, std::vector<uint8_t>());
    std::vector<uint8_t> combinedData;
    combinedData.reserve(MAX_PACKET_SIZE * MAX_PACKETS);

    while (true) {
        // Receive data from the socket
        uint8_t buffer[MAX_PACKET_SIZE];
        sockaddr_in clientAddr;
        int addrLen = sizeof(clientAddr);
        ssize_t bytesReceived = recvfrom(udpSocket, reinterpret_cast<char*>(buffer), sizeof(buffer), 0, (struct sockaddr*)&clientAddr, &addrLen);

        if (bytesReceived < 0) {
            std::cerr << "Error receiving data." << std::endl;
            break;
        }

        // Extract frame and packet information
        uint8_t frameNo = buffer[0];
        uint8_t packetNo = buffer[1];
        uint8_t* imageData = buffer + 2;
        size_t imageSize = bytesReceived - 2;

        std::cout << "Received Frame: " << static_cast<int>(frameNo) << ", Packet: " << static_cast<int>(packetNo)
                  << ", Image Size: " << imageSize << " bytes" << std::endl;

        // Combine image data
        tempData[packetNo].insert(tempData[packetNo].end(), imageData, imageData + imageSize);

        // Check if all packets for the frame have been received
        if (imageSize < MAX_PACKET_SIZE-2 && packetNo > 0) {
            // Combine data from all packets
            for (int index = 0; index <= packetNo; ++index) {
                if (tempData[index].empty()) {
                    tempData = std::vector<std::vector<uint8_t>>(MAX_PACKETS, std::vector<uint8_t>());
                    combinedData.clear();
                    continue;
                }
                combinedData.insert(combinedData.end(), tempData[index].begin(), tempData[index].end());
            }

            // Save the image to a file (replace this with your own image saving logic)
            std::string filename = "received_image_" + std::to_string(frameNo) + ".jpg";
            std::ofstream outFile(filename, std::ios::binary);
            outFile.write(reinterpret_cast<const char*>(combinedData.data()), combinedData.size());
            outFile.close();

            std::cout << "Image saved as " << filename << std::endl;

            // Reset data for the next frame
            tempData = std::vector<std::vector<uint8_t>>(MAX_PACKETS, std::vector<uint8_t>());
            combinedData.clear();
        }
    }

    // Close the UDP socket and cleanup Winsock
    closesocket(udpSocket);
    WSACleanup();

    return 0;
}

 

 

vscode 환경이고 터미널에 gcc를 쳐서 컴파일을 해줬음.

 

PS C:\Users\wooch\vscode_source> gcc .\ESP32_CAM_SERVER_UDP.cpp -o udp_server -lws2_32 -lstdc++ 
PS C:\Users\wooch\vscode_source> .\udp_server.exe

 

근데 글 작성하면서 보니까 packet size는 11000~16000까지도 가는데 서버측 packet size를 10으로 줘서 문제가 생긴 것 같다는 느낌을 빡 받았음.

일단 20으로 바꿔본다.

 

엇?! 되네??

640 480으로 들어오고 3초 정도 했을 때30개 정도 들어왔다. 10fps 정도 잡히는 듯 했다.

 

요건 정상적으로 저장된 이미지고 초점이 안잡혀서 흐릿하긴 한데, 빵판이랑 버즈는 잘 보임.

 

다만 문제는 저장한 이미지 파일 중 0Byte인 파일이 가끔 있다는 점과 아래 사진처럼 짤려서 받는게 있다는 점이다.

근데 신기한 점은 중간까지 쓰여진 파일은 단 하나도 없다는 점이다. 모두 패킷 하나만 저장한 거 처럼 이 위치에서 짤려있음.

이 문제를 해결해서 다시 돌아오겠소..아디오스.. 

 

 


 

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")  // Link with ws2_32.lib

#define UDP_PORT 12345
#define MAX_PACKET_SIZE 1450
#define MAX_PACKETS 20

int main() {
    // Initialize Winsock
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "Failed to initialize Winsock." << std::endl;
        return -1;
    }

    // Create a UDP socket
    SOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpSocket == INVALID_SOCKET) {
        std::cerr << "Error creating UDP socket." << std::endl;
        WSACleanup();
        return -1;
    }

    // Bind the socket to the specified IP address and port
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(UDP_PORT);

    if (bind(udpSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cerr << "Error binding UDP socket." << std::endl;
        closesocket(udpSocket);
        WSACleanup();
        return -1;
    }

    // Initialize variables for receiving data
    std::vector<std::vector<uint8_t>> tempData(MAX_PACKETS, std::vector<uint8_t>());
    std::vector<uint8_t> combinedData;
    combinedData.reserve(MAX_PACKET_SIZE * MAX_PACKETS);

    while (true) {
        // Receive data from the socket
        uint8_t buffer[MAX_PACKET_SIZE];
        sockaddr_in clientAddr;
        int addrLen = sizeof(clientAddr);
        ssize_t bytesReceived = recvfrom(udpSocket, reinterpret_cast<char*>(buffer), sizeof(buffer), 0, (struct sockaddr*)&clientAddr, &addrLen);

        if (bytesReceived < 0) {
            std::cerr << "Error receiving data." << std::endl;
            break;
        }

        // Extract frame and packet information
        uint8_t frameNo = buffer[0];
        uint8_t packetNo = buffer[1];
        uint8_t* imageData = buffer + 2;
        size_t imageSize = bytesReceived - 2;

        std::cout << "Received Frame: " << static_cast<int>(frameNo) << ", Packet: " << static_cast<int>(packetNo)
                  << ", Image Size: " << imageSize << " bytes" << std::endl;

        // Combine image data
        tempData[packetNo].insert(tempData[packetNo].end(), imageData, imageData + imageSize);

        // Check if all packets for the frame have been received
        if (imageSize < MAX_PACKET_SIZE-2 && packetNo > 0) {
            // Combine data from all packets
            for (int index = 0; index <= packetNo; ++index) {
                if (tempData[index].empty()) {
                    tempData = std::vector<std::vector<uint8_t>>(MAX_PACKETS, std::vector<uint8_t>());
                    combinedData.clear();
                    continue;
                }
                combinedData.insert(combinedData.end(), tempData[index].begin(), tempData[index].end());
            }

            // Save the image to a file (replace this with your own image saving logic)
            std::string filename = "received_image_" + std::to_string(frameNo) + ".jpg";
            std::ofstream outFile(filename, std::ios::binary);
            outFile.write(reinterpret_cast<const char*>(combinedData.data()), combinedData.size());
            outFile.close();
    }

    // Close the UDP socket and cleanup Winsock
    closesocket(udpSocket);
    WSACleanup();

    return 0;
}

 

 

해결 중 만난 끔찍한 혼종.. 계속 해결하겠슴다..

반응형