하나 만들어보고 싶은게 생겼습니다.

 

Shift Lighting Indicator의 줄임말인 SLI는 게임 속 차량 정보와 랩 타임 등의 여러 데이터를 가독성있게 출력해주는 일종의 계기판입니다.

SLI가 있으면 게임 속 렌더링 되는 화면 일부를 없애서 시야를 넓히고 레이싱에 더 몰입할 수도 있죠.

특히, 헬멧 시점으로 레이싱 할 때 가려지지 않아서 좋고, 더욱 현실감을 더해줍니다.

 

 

기본 시야에서는 이렇게 보이지가 않아요........ 물론 VR을 사서 밑을 쳐다보면 보이겠죠?^^

아 SLI가 기본 장착된 스티어링 휠도 있습니다!!

 

 

맙소사... 가격이....... 너무합니다 ㅠㅠ 모션기어 사의 SLI 모듈도 13만원이 넘습니다 ㅠㅠ

그래서 직접 만들어보기로 합니다.

우선은 Project Cars 2 게임에서 제공하는 데이터를 받아서 모듈로 전송이 필요하겠죠?

 

 

 

이렇게 게임 옵션에 들어가면 어떤 방식을 사용할지 설정할 수 있습니다.

 

1. 직접 개발한 PC 소프트웨어에서 공유 메모리를 열어 데이터 가공하고 모듈로 다시 전송하는 방법

2. 모듈에서 직접 데이터를 받아 출력하는 방법

 

이 두 가지 방법에서 크게 달라지지 않습니다.

2번 같은 경우는 데이터를 직접 처리해므로 컴퓨팅이 필요하겠지요.

 

하지만 라즈베리 파이는 정말 물건입니다. 이 쪼그만한게 별 걸 다하지요.

그래서 라즈베리에서 UDP 데이터를 직접 받고 직접 LED를 컨트롤하도록 개발하고자 합니다.

 

지금 필요한 준비물은.

라즈베리 파이와 이더넷을 micro usb로 바꿔주는 어댑터 입니다.

 

이더넷을 usb로 바꿔주는 제품을 참 많을텐데, 라즈베리에서 사용하기 가장 적절한 제품은 USB 허브와 이더넷이 합쳐진 게 아닐까요?

 

 

https://www.adafruit.com/product/2992

 

 

이 제품으로 PC와 라즈베리를 같은 네트워크에 묶어주면 UDP 패킷을 받을 수 있습니다.

와이어샤크로 패킷을 찍어본 결과, 브로드캐스트(255.255.255.255) 패킷이기 때문에 네트워크로 연결된 모든 장비에서 데이터를 받을 수 있습니다.

 

 

 

그러면 이제 Project Cars 2 데이터를 라즈베리에서 받을 수 있도록 코딩해봅시다!

공식 홈페이지에서 UDP 패킷 구조가 담겨있는 헤더 파일과 패킷을 받을 수 있는 Windows console 응용프로그램 샘플소스를 제공하지만,

그 샘플을 gcc 등의 Windows가 아닌 환경에서 컴파일하려고 했다가는 엄청난 에러를 만나겠죠 :)

라즈베리파이에서 돌아가는 소스를 올려드립니다.

 

main.cpp

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
 
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
#include <wiringPi.h>
 
#include "SMS_UDP_Definitions.hpp"
 
int sockfd_udp_cli;
 
void intHandler(int dummy)
{
    close(sockfd_udp_cli);
    exit(0);
}
 
int main(int argc, char** argv)
{
    signal(SIGINT, intHandler);
    
    if( wiringPiSetup() < )
    {
        return 1;
    }
    
    puts("\nLocoField SLI\n");
 
    char udp_recvbuff[SMS_UDP_MAX_PACKETSIZE];
    PacketBase packetHeader;
    memset(&packetHeader, 0sizeof(packetHeader));
 
    sockfd_udp_cli = socket(AF_INET, SOCK_DGRAM, 0);
    if( sockfd_udp_cli < )
    {
        puts("ERROR: create socket.\n");
        return 1;
    }
    
    //int flags = fcntl(sockfd_udp_cli, F_GETFL, 0);
    //fcntl(sockfd_udp_cli, F_SETFL, flags | O_NONBLOCK);
    
    struct timeval timeout;      
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    setsockopt(sockfd_udp_cli, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
 
    
    sockaddr_in receiver;
    receiver.sin_family = AF_INET;
    receiver.sin_addr.s_addr = INADDR_ANY;
    receiver.sin_port = htons(SMS_UDP_PORT);
 
    int retval = bind(sockfd_udp_cli, (sockaddr*&receiver, sizeof(receiver));
    if( retval < )
    {
        puts("ERROR: bind socket.\n");
        return 1;
    }
    
    
    sTelemetryData data1;
    memset(&data1, 0sizeof(data1));
    
    while)
    {
        retval = recvfrom(sockfd_udp_cli, (char*)&udp_recvbuff, sizeof(udp_recvbuff), 0, NULL, 0);    
        if( retval > )
        {
            memcpy(&packetHeader, udp_recvbuff, sizeof(PacketBase));
            switch( packetHeader.mPacketType )
            {
            case eCarPhysics:
            {
                memcpy(&data1, udp_recvbuff, sizeof(data1));
            }
            default:
                continue;
            }
        }
    }
 
    intHandler(0);
    return 0;
}
 
cs

 

프로젝트의 종속성은 WiringPi 뿐이네요.

사실 이 프로젝트에서는 전혀 필요가 없어요. 라즈베리에서 GPIO 다룰 일이 많기 때문에 습관적으로 넣는 코드인거죠.

 

코드 자체는 정말 간단합니다.

 

signal 함수로 Ctrl+C로 종료할 때 소켓을 닫구요. 나중에 LED를 사용하게 되면 LED 끄는 코드를 넣으면 됩니다.

 

패킷은 UDP이므로 소켓은 데이터그램으로 만들고, 타임아웃을 설정합니다.

사실 논블록 소켓을 만들고 싶었는데 잘 되지는 않았네요...

 

그리고 recvfrom 함수로 데이터를 받았을 때 Project Cars 2에서 제공하는 구조체에 여러 값들을 복사해줍니다.

제가 소켓에 타임아웃을 설정했으니 받을 데이터가 없으면(게임에서 레이싱 상태가 아니라면)

조건문에서 계속 검사하며 while 문에서 계속 돌고 있겠지요.

 

라즈베리에서 바로 사용하실 수 있게 프로젝트 파일(CMakeLists.txt, MakeFile)도 올려드려요.

질문은 언제든지 환영입니다!

 

project.zip

 

 

참고 웹페이지:

[1] https://www.projectcarsgame.com/project-cars-2-api/

 

'DIY > Project Cars 2' 카테고리의 다른 글

Project Cars 2 SLI 완성!  (0) 2018.05.16
Raspberry Pi에서 Project Cars 2 UDP 데이터 받기  (2) 2018.03.20
  1. Parkseokhyeon 2018.10.24 20:28 신고

    프로젝트 카스2에서 라즈베리 파이로 속도와 RPM 변수를 받아와서 아날로그 계기판을 움직여

    속도 계기판을 만들고 싶습니다.

    가능할까요? 가능하다면 준비해야하는것은 무었일까요?

    • 로코필드 2018.10.26 16:18 신고

      결국엔 아날로그 계기판을 컨트롤 할 수 있어야겠죠? 차에 들어가는 계기판을 떼서 쓸 수 있을지는 모르겠네요.
      제가 당장 만들어야 된다면 스텝 모터를 이용해서 직접 계기판을 만들고 컨트롤하면 되겠네요.

 

 

 

특정 폴더에 아주 많은 디렉토리가 있고, 각 디렉토리의 파일에 접근해야 할 경우 우선 디렉토리의 목록을 모두 가져와야겠죠.

예시로 첨부한 그림처럼 규칙이 어느 정도 보이면 문자열로 처리하면 될텐데, 그렇지 않을 경우 참 곤란합니다.

옛날에 디렉토리 목록을 나열할 방법이 없지는 않을테지만, 라이브러리를 이용하지 않는 이상 코드 라인이 길어지거나 C++ 표준이 아니겠지요.

 

C++17에서는 아주 쉽게 디렉토리 목록을 나열할 클래스를 제공합니다.

filesystem namespace의 directory_iterator 클래스인데요, 역시나 boost 라이브러리에서 넘어왔죠.

 

Visual Studio 2015에서는 해당 클래스를 실험적으로 제공하고 있습니다. C++17 표준이 확정되기 이전이라 그랬겠지요.

따라서 filesystem namespace 앞에 experimental namespace를 붙여야 합니다... ^^;

 

filesystem 헤더 파일을 포함하고 std::experimental::filesystem::directory_iterator 클래스를 사용하시면 되겠습니다.

 

 

예제코드:

1
2
3
4
5
6
7
8
std::string dir = "D:/test/";
std::vector<std::string> paths;
 
forauto& p : std::experimental::filesystem::directory_iterator(dir) )
{
    paths.push_back(p.path().string());
}
 
cs

 

 

참고자료:

[1] http://en.cppreference.com/w/cpp/experimental/fs/directory_iterator 

[2] https://stackoverflow.com/questions/612097/how-can-i-get-the-list-of-files-in-a-directory-using-c-or-c 

 

'코딩 > C++' 카테고리의 다른 글

C++ 디렉토리 목록 나열 - std::filesystem::directory_iterator  (0) 2018.02.22

+ Recent posts