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;
}
빌드를 시도하면 다음과 같은 설정 창이 뜹니다.
각각의 값은 다음과 같습니다.
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 키로 시작하는 디버깅까지 지원이 됩니다.
오~~~ 그동안 별다르게 느끼지 못했던 마이크로소프트의 다중 플랫폼 지원을 정말 제대로 체감하는 순간입니다. ^^
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]