Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 4개 있습니다.)
(시리즈 글이 6개 있습니다.)
사물인터넷: 9. Visual Studio 2017에서 Raspberry Pi C++ 응용 프로그램 제작
; https://www.sysnet.pe.kr/2/0/11358

개발 환경 구성: 339. WSL을 이용해 윈도우 PC 1대에서 Linux 응용 프로그램을 Visual Studio로 개발하는 방법
; https://www.sysnet.pe.kr/2/0/11390

개발 환경 구성: 340. WSL을 이용해 윈도우 PC 1대에서 openSUSE 응용 프로그램을 Visual Studio로 개발하는 방법
; https://www.sysnet.pe.kr/2/0/11391

개발 환경 구성: 428. Visual Studio 2019 - CMake를 이용한 리눅스 빌드 환경 설정
; https://www.sysnet.pe.kr/2/0/11825

개발 환경 구성: 431. Visual Studio 2019 - CMake를 이용한 공유/실행(so/out) 리눅스 프로젝트 설정
; https://www.sysnet.pe.kr/2/0/11833

개발 환경 구성: 434. Visual Studio 2019 - 리눅스 프로젝트를 이용한 공유/실행(so/out) 프로그램 개발 환경 설정
; https://www.sysnet.pe.kr/2/0/11844




Visual Studio 2017에서 Raspberry Pi C++ 응용 프로그램 제작

비주얼 스튜디오 2017에서 Visual C++ / "Cross Platform" / "Linux" 범주에서 "Blink (Raspberry)" 프로젝트를 선택해 생성하면 readme.html 파일이 추가되는데, 사실 이 웹 페이지에 모든 설명이 아주 자세하게 나와 있습니다.

그래도 ^^ 간단하게 직접 실습한 결과를 올려 봅니다.




우선 라즈베리 파이(제로)에서 다음의 명령어를 수행해서 원격 도구를 미리 설치해 둡니다.

$ sudo apt-get install openssh-server g++ gdb gdbserver

그다음, (윈도우 PC의 비주얼 스튜디오에서) 생성한 "Blink (Raspberry)" 프로젝트를 보면 기본 예제를 포함한 main.cpp 파일이 있는데,

#include <wiringPi.h>

// LED Pin - wiringPi pin 0 is BCM_GPIO 17.
// we have to use BCM numbering when initializing with wiringPiSetupSys
// when choosing a different pin number please use the BCM numbering, also
// update the Property Pages - Build Events - Remote Post-Build Event command 
// which uses gpio export for setup for wiringPiSetupSys
#define LED 17

int main(void)
{
    wiringPiSetupSys();

    pinMode(LED, OUTPUT);

    while (true)
    {
        digitalWrite(LED, HIGH);  // On
        delay(500); // ms
        digitalWrite(LED, LOW);   // Off
        delay(500);
    }

    return 0;
}

빌드를 시도하면 다음과 같은 설정 창이 뜹니다.

rasp_vs_2017_1.png

각각의 값은 다음과 같습니다.

Host name: 라즈베리의 파이의 주소
Port: SSH 서버의 소켓 포트
User name: 사용자 계정
Authentication type: Password
Password: [User name]의 암호

올바로 입력하면 이후 출력 창에 다음과 같은 빌드 결과를 볼 수 있습니다.

1>------ Build started: Project: rasp_vusb_server, Configuration: Debug ARM ------
1>Validating architecture
1>Validating sources
1>Copying sources remotely to '192.168.10.120'
1>Starting remote build
1>Compiling sources:
1>main.cpp
1>Linking objects
1>rasp_vusb_server.vcxproj -> C:\testapp\rasp_vusb_server\bin\ARM\Debug\rasp_vusb_server.out
1>Invoking gpio export 17 out
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

오~~~ 정말 매끄럽군요. ^^

실행 파일로 출력된 rasp_vusb_server.out 파일을 samba로 공유한 폴더에 올려놓고 라즈베리 파이(제로)에 연결한 putty 콘솔 창으로 테스트하니 잘 실행이 됩니다. 사실 굳이 복사하지 않아도 됩니다. 이미 빌드할 때부터 다음의 경로에 배포가 되어 있습니다.

/home/[사용자계정]/projects/[프로젝트명]
/home/[사용자계정]/projects/[프로젝트명]/bin/ARM/Debug




당연한 이야기겠지만 Raspberry Pi Zero(OTG)를 다른 컴퓨터에 연결해 가상 키보드로 쓰는 방법 글에서 다룬 다음의 소스 코드도,

Linux USB HID gadget driver
; https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BUF_LEN 512

struct options {
    const char    *opt;
    unsigned char val;
};

static struct options kmod[] = {
    { .opt = "--left-ctrl",.val = 0x01 },
    { .opt = "--right-ctrl",.val = 0x10 },
    { .opt = "--left-shift",.val = 0x02 },
    { .opt = "--right-shift",.val = 0x20 },
    { .opt = "--left-alt",.val = 0x04 },
    { .opt = "--right-alt",.val = 0x40 },
    { .opt = "--left-meta",.val = 0x08 },
    { .opt = "--right-meta",.val = 0x80 },
    { .opt = NULL }
};

static struct options kval[] = {
    { .opt = "--return",.val = 0x28 },
    { .opt = "--esc",.val = 0x29 },
    { .opt = "--bckspc",.val = 0x2a },
    { .opt = "--tab",.val = 0x2b },
    { .opt = "--spacebar",.val = 0x2c },
    { .opt = "--caps-lock",.val = 0x39 },
    { .opt = "--f1",.val = 0x3a },
    { .opt = "--f2",.val = 0x3b },
    { .opt = "--f3",.val = 0x3c },
    { .opt = "--f4",.val = 0x3d },
    { .opt = "--f5",.val = 0x3e },
    { .opt = "--f6",.val = 0x3f },
    { .opt = "--f7",.val = 0x40 },
    { .opt = "--f8",.val = 0x41 },
    { .opt = "--f9",.val = 0x42 },
    { .opt = "--f10",.val = 0x43 },
    { .opt = "--f11",.val = 0x44 },
    { .opt = "--f12",.val = 0x45 },
    { .opt = "--insert",.val = 0x49 },
    { .opt = "--home",.val = 0x4a },
    { .opt = "--pageup",.val = 0x4b },
    { .opt = "--del",.val = 0x4c },
    { .opt = "--end",.val = 0x4d },
    { .opt = "--pagedown",.val = 0x4e },
    { .opt = "--right",.val = 0x4f },
    { .opt = "--left",.val = 0x50 },
    { .opt = "--down",.val = 0x51 },
    { .opt = "--kp-enter",.val = 0x58 },
    { .opt = "--up",.val = 0x52 },
    { .opt = "--num-lock",.val = 0x53 },
    { .opt = NULL }
};

int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
    char *tok = strtok(buf, " ");
    int key = 0;
    int i = 0;

    for (; tok != NULL; tok = strtok(NULL, " ")) {

        if (strcmp(tok, "--quit") == 0)
            return -1;

        if (strcmp(tok, "--hold") == 0) {
            *hold = 1;
            continue;
        }

        if (key < 6) {
            for (i = 0; kval[i].opt != NULL; i++)
                if (strcmp(tok, kval[i].opt) == 0) {
                    report[2 + key++] = kval[i].val;
                    break;
                }
            if (kval[i].opt != NULL)
                continue;
        }

        if (key < 6)
            if (islower(tok[0])) {
                report[2 + key++] = (tok[0] - ('a' - 0x04));
                continue;
            }

        for (i = 0; kmod[i].opt != NULL; i++)
            if (strcmp(tok, kmod[i].opt) == 0) {
                report[0] = report[0] | kmod[i].val;
                break;
            }
        if (kmod[i].opt != NULL)
            continue;

        if (key < 6)
            fprintf(stderr, "unknown option: %s\n", tok);
    }
    return 8;
}

static struct options mmod[] = {
    { .opt = "--b1",.val = 0x01 },
    { .opt = "--b2",.val = 0x02 },
    { .opt = "--b3",.val = 0x04 },
    { .opt = NULL }
};

int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
    char *tok = strtok(buf, " ");
    int mvt = 0;
    int i = 0;
    for (; tok != NULL; tok = strtok(NULL, " ")) {

        if (strcmp(tok, "--quit") == 0)
            return -1;

        if (strcmp(tok, "--hold") == 0) {
            *hold = 1;
            continue;
        }

        for (i = 0; mmod[i].opt != NULL; i++)
            if (strcmp(tok, mmod[i].opt) == 0) {
                report[0] = report[0] | mmod[i].val;
                break;
            }
        if (mmod[i].opt != NULL)
            continue;

        if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) {
            errno = 0;
            report[1 + mvt++] = (char)strtol(tok, NULL, 0);
            if (errno != 0) {
                fprintf(stderr, "Bad value:'%s'\n", tok);
                report[1 + mvt--] = 0;
            }
            continue;
        }

        fprintf(stderr, "unknown option: %s\n", tok);
    }
    return 3;
}

static struct options jmod[] = {
    { .opt = "--b1",.val = 0x10 },
    { .opt = "--b2",.val = 0x20 },
    { .opt = "--b3",.val = 0x40 },
    { .opt = "--b4",.val = 0x80 },
    { .opt = "--hat1",.val = 0x00 },
    { .opt = "--hat2",.val = 0x01 },
    { .opt = "--hat3",.val = 0x02 },
    { .opt = "--hat4",.val = 0x03 },
    { .opt = "--hatneutral",.val = 0x04 },
    { .opt = NULL }
};

int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
    char *tok = strtok(buf, " ");
    int mvt = 0;
    int i = 0;

    *hold = 1;

    /* set default hat position: neutral */
    report[3] = 0x04;

    for (; tok != NULL; tok = strtok(NULL, " ")) {

        if (strcmp(tok, "--quit") == 0)
            return -1;

        for (i = 0; jmod[i].opt != NULL; i++)
            if (strcmp(tok, jmod[i].opt) == 0) {
                report[3] = (report[3] & 0xF0) | jmod[i].val;
                break;
            }
        if (jmod[i].opt != NULL)
            continue;

        if (!(tok[0] == '-' && tok[1] == '-') && mvt < 3) {
            errno = 0;
            report[mvt++] = (char)strtol(tok, NULL, 0);
            if (errno != 0) {
                fprintf(stderr, "Bad value:'%s'\n", tok);
                report[mvt--] = 0;
            }
            continue;
        }

        fprintf(stderr, "unknown option: %s\n", tok);
    }
    return 4;
}

void print_options(char c)
{
    int i = 0;

    if (c == 'k') {
        printf("    keyboard options:\n"
            "        --hold\n");
        for (i = 0; kmod[i].opt != NULL; i++)
            printf("\t\t%s\n", kmod[i].opt);
        printf("\n  keyboard values:\n"
            "        [a-z] or\n");
        for (i = 0; kval[i].opt != NULL; i++)
            printf("\t\t%-8s%s", kval[i].opt, i % 2 ? "\n" : "");
        printf("\n");
    }
    else if (c == 'm') {
        printf("    mouse options:\n"
            "        --hold\n");
        for (i = 0; mmod[i].opt != NULL; i++)
            printf("\t\t%s\n", mmod[i].opt);
        printf("\n  mouse values:\n"
            "        Two signed numbers\n"
            "--quit to close\n");
    }
    else {
        printf("    joystick options:\n");
        for (i = 0; jmod[i].opt != NULL; i++)
            printf("\t\t%s\n", jmod[i].opt);
        printf("\n  joystick values:\n"
            "        three signed numbers\n"
            "--quit to close\n");
    }
}

int main(int argc, const char *argv[])
{
    const char *filename = NULL;
    int fd = 0;
    char buf[BUF_LEN];
    int cmd_len;
    char report[8];
    int to_send = 8;
    int hold = 0;
    fd_set rfds;
    int retval, i;

    if (argc < 3) {
        fprintf(stderr, "Usage: %s devname mouse|keyboard|joystick\n",
            argv[0]);
        return 1;
    }

    if (argv[2][0] != 'k' && argv[2][0] != 'm' && argv[2][0] != 'j')
        return 2;

    filename = argv[1];

    if ((fd = open(filename, O_RDWR, 0666)) == -1) {
        perror(filename);
        return 3;
    }

    print_options(argv[2][0]);

    while (42) {

        FD_ZERO(&rfds);
        FD_SET(STDIN_FILENO, &rfds);
        FD_SET(fd, &rfds);

        retval = select(fd + 1, &rfds, NULL, NULL, NULL);
        if (retval == -1 && errno == EINTR)
            continue;
        if (retval < 0) {
            perror("select()");
            return 4;
        }

        if (FD_ISSET(fd, &rfds)) {
            cmd_len = read(fd, buf, BUF_LEN - 1);
            printf("recv report:");
            for (i = 0; i < cmd_len; i++)
                printf(" %02x", buf[i]);

            printf("\n");
        }

        if (FD_ISSET(STDIN_FILENO, &rfds)) {
            memset(report, 0x0, sizeof(report));
            cmd_len = read(STDIN_FILENO, buf, BUF_LEN - 1);

            if (cmd_len == 0)
                break;

            buf[cmd_len - 1] = '\0';
            hold = 0;

            memset(report, 0x0, sizeof(report));
            if (argv[2][0] == 'k')
                to_send = keyboard_fill_report(report, buf, &hold);
            else if (argv[2][0] == 'm')
                to_send = mouse_fill_report(report, buf, &hold);
            else
                to_send = joystick_fill_report(report, buf, &hold);

            if (to_send == -1)
                break;

            if (write(fd, report, to_send) != to_send) {
                perror(filename);
                return 5;
            }
            if (!hold) {
                memset(report, 0x0, sizeof(report));
                if (write(fd, report, to_send) != to_send) {
                    perror(filename);
                    return 6;
                }
            }
        }
    }

    close(fd);
    return 0;
}

아무런 변경 없이 Visual Studio에서 잘 빌드가 됩니다. 게다가 F5 키로 시작하는 디버깅까지 지원이 됩니다.

rasp_vs_2017_2.png

오~~~ 그동안 별다르게 느끼지 못했던 마이크로소프트의 다중 플랫폼 지원을 정말 제대로 체감하는 순간입니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 5/4/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2019-05-10 10시23분
얼굴 구분하기 - LBPH를 이용하여|작성자 오픈랩
; https://blog.naver.com/roboholic84/221533459586
정성태

... 31  32  33  34  35  36  37  38  39  40  [41]  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12594정성태4/11/20218150.NET Framework: 1035. C# - kubectl 명령어 또는 REST API 대신 Kubernetes 클라이언트 라이브러리를 통해 프로그래밍으로 접근 [1]파일 다운로드1
12593정성태4/10/20219289개발 환경 구성: 567. Docker Desktop for Windows - kubectl proxy 없이 k8s 대시보드 접근 방법
12592정성태4/10/20219108개발 환경 구성: 566. Docker Desktop for Windows - k8s dashboard의 Kubeconfig 로그인 및 Skip 방법
12591정성태4/9/202112328.NET Framework: 1034. C# - byte 배열을 Hex(16진수) 문자열로 고속 변환하는 방법 [2]파일 다운로드1
12590정성태4/9/20218880.NET Framework: 1033. C# - .NET 4.0 이하에서 Console.IsInputRedirected 구현 [1]
12589정성태4/8/202110177.NET Framework: 1032. C# - Environment.OSVersion의 문제점 및 윈도우 운영체제의 버전을 구하는 다양한 방법 [1]
12588정성태4/7/202110724개발 환경 구성: 565. PowerShell - New-SelfSignedCertificate를 사용해 CA 인증서 생성 및 인증서 서명 방법
12587정성태4/6/202111438개발 환경 구성: 564. Windows 10 - ClickOnce 배포처럼 사용할 수 있는 MSIX 설치 파일 [1]
12586정성태4/5/20219202오류 유형: 710. Windows - Restart-Computer / shutdown 명령어 수행 시 Access is denied(E_ACCESSDENIED)
12585정성태4/5/20218967개발 환경 구성: 563. 기본 생성된 kubeconfig 파일의 내용을 새롭게 생성한 인증서로 구성하는 방법
12584정성태4/1/20219669개발 환경 구성: 562. kubeconfig 파일 없이 kubectl 옵션만으로 실행하는 방법
12583정성태3/29/202111210개발 환경 구성: 561. kubectl 수행 시 다른 k8s 클러스터로 접속하는 방법
12582정성태3/29/20219870오류 유형: 709. Visual C++ - 컴파일 에러 error C2059: syntax error: '__stdcall'
12581정성태3/28/20219795.NET Framework: 1031. WinForm/WPF에서 Console 창을 띄워 출력하는 방법 (2) - Output 디버깅 출력을 AllocConsole로 우회 [2]
12580정성태3/28/20218588오류 유형: 708. SQL Server Management Studio - Execution Timeout Expired.
12579정성태3/28/20218602오류 유형: 707. 중첩 가상화(Nested Virtualization) - The virtual machine could not be started because this platform does not support nested virtualization.
12578정성태3/27/20218924개발 환경 구성: 560. Docker Desktop for Windows 기반의 Kubernetes 구성 (2) - WSL 2 인스턴스에 kind가 구성한 k8s 서비스 위치
12577정성태3/26/202110969개발 환경 구성: 559. Docker Desktop for Windows 기반의 Kubernetes 구성 - WSL 2 인스턴스에 kind 도구로 k8s 클러스터 구성
12576정성태3/25/20218766개발 환경 구성: 558. Docker Desktop for Windows에서 DockerDesktopVM 기반의 Kubernetes 구성 (2) - k8s 서비스 위치
12575정성태3/24/20217897개발 환경 구성: 557. Docker Desktop for Windows에서 DockerDesktopVM 기반의 Kubernetes 구성
12574정성태3/23/202111771.NET Framework: 1030. C# Socket의 Close/Shutdown 동작 (동기 모드)
12573정성태3/22/20219644개발 환경 구성: 556. WSL 인스턴스 초기 설정 명령어 [1]
12572정성태3/22/20219178.NET Framework: 1029. C# - GC 호출로 인한 메모리 압축(Compaction)을 확인하는 방법파일 다운로드1
12571정성태3/21/20218347오류 유형: 706. WSL 2 기반으로 "Enable Kubernetes" 활성화 시 초기화 실패 [1]
12570정성태3/19/202112666개발 환경 구성: 555. openssl - CA로부터 인증받은 새로운 인증서를 생성하는 방법
12569정성태3/18/202111539개발 환경 구성: 554. WSL 인스턴스 export/import 방법 및 단축 아이콘 설정 방법
... 31  32  33  34  35  36  37  38  39  40  [41]  42  43  44  45  ...