본문 바로가기
Raspberry Pi

Raspberry Pi #6 - wiringPi 라이브러리를 이용한 핀 제어(C/C++)

by Spring13 2025. 4. 1.

라즈베리파이에서, GPIO 제어는 파이썬을 이용하여 제어할 수 있다. 물론, 다른 언어로도 가능하다. 그 예로는, C/C++(wiringPi), Java(Pi4J), Node.js(rpi-gpio), Go(periph) 등이다.

 

이 글에서는 wiringPi를 이용해 C/C++로 GPIO를 제어할 것이다.


wiringPi 라이브러리 설치


라이브러리를 사용하기 위해서는 라이브러리를 사용해야 한다.

 

아래의 명령을 입력한다.

sudo apt update
sudo apt install wiringpi

경우에 따라, 설치가 완료되었음에도 라이브러리 파일을 찾을 수 없는 등의 오류가 발생할 수 있다(필자가 그랬다).

이런 경우는 먼저 소스 코드를 이용해 빌드해 볼 수 있다.

 

일단 아래의 명령을 입력한다:

mkdir wiringpi
cd wiringpi

디렉터리를 새로 생성하는 이유는 관리를 편하게 하기 위해서이다.

 

wiringpi 디렉터리에서 아래의 명령을 입력한다.

git clone https://github.com/WiringPi/WiringPi.git

 

하위 디렉터리로 이동하여 빌드한다.

cd WiringPi
./build

 

문제가 생겨 삭제하려면, 아래의 명령을 입력한다.

./build uninstall

 

관련 도움말은 디랙터리의 INSTALL 파일에 저장되어 있다. 아래 접은 글에 해당 내용을 옮겨 놓았다.

더보기
How to install wiringPi
=======================

The easiest way is to use the supplied 'build' script:

  ./build

that should do a complete install or upgrade of wiringPi for you.

That will install a dynamic library.

Some distributions do not have /usr/local/lib in the default LD_LIBRARY_PATH. To
fix this, you need to edit /etc/ld.so.conf and add in a single line:

  /usr/local/lib

then run the ldconfig command.

  sudo ldconfig

To un-install wiringPi:

  ./build uninstall

For help and support see:

* https://github.com/WiringPi/WiringPi/issues

경우에 따라, 소스 코드에서 빌드하였음에도 불구하고, 라이브러리 파일을 찾을 수 없는 등의 오류가 발생할 수 있다(필자가 그랬다).

이러면 최후의 수단으로 릴리스된 파일을 DPKG를 이용해 설치해 볼 수 있다.

 

GitHub 페이지로 이동한다.

릴리스 페이지로 이동 후 알맞은 패키지를 내려받는다. 현재 대부분의 Debian 계열 리눅스는 64비트 운영체제이므로, arm64 버전을 내려받으면 되지만, 구형 라즈베리 및 그 운영체제의 경우, 32비트인 경우도 있으니, 주의하자. 파이 5의 경우는 64비트이다.

 

터미널 환경의 경우, 아래의 명령을 입력한다.

wget https://github.com/WiringPi/WiringPi/releases/download/3.10/wiringpi_3.10_arm64.deb #64비트
wget https://github.com/WiringPi/WiringPi/releases/download/3.10/wiringpi_3.10_armhf.deb #32비트

 

파일을 내려받았으면 DPKG나 APT를 이용해 설치한다.

 

아래의 예문을 참고하여 명령을 입력한다.

sudo dpkg -i "패키지 경로"
sudo apt install "패키지 경로"

 

필자의 경우 아래의 명령을 입력하였다.

sudo dpkg -i /home/sprout1345/wiringpi_3.10_arm64.deb


설치가 완료되었다면, "gpio -v""gpio readall" 명령을 실행해보자.

아래와 같이 출력된다면 정상적으로 설치된 것이다.

sprout1345@sproutpi:~$ gpio -v
gpio version: 3.10
Copyright (c) 2012-2024 Gordon Henderson and contributors
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty

Hardware details:
  Type: Pi 5, Revision: 01, Memory: 8192MB, Maker: Sony 

System details:
  * Device tree present.
      Model: Raspberry Pi 5 Model B Rev 1.1
  * Supports full  user-level GPIO access via memory.
  * Supports basic user-level GPIO access via /dev/gpiomem.
  * Supports basic user-level GPIO access via /dev/gpiochip (slow).

sprout1345@sproutpi:~$ gpio readall
 +-----+-----+---------+------+---+---Pi 5---+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 | ALT3 | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 | ALT3 | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   -  | 0 |  7 || 8  | 1 | IN   | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 0 | IN   | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |  OUT | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   -  | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   -  | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |  OUT | 0 | 23 || 24 | 0 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   -  | 0 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   -  | 0 | 31 || 32 | 0 |  -   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   -  | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   -  | 0 | 35 || 36 | 0 |  -   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   -  | 0 | 37 || 38 | 0 |  -   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 |  -   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 5---+---+------+---------+-----+-----+

이런 출력이 나오지 않고, 경고문이 출력된다면 정상 설치되지 않은 것이다.

 

정 안된다면 다른 라이브러리를 이용하는 것도 좋다.


설치가 완료되었으면 코드를 작성하자.

 

주의할 점은, wiringPi은 기존의 BCM 번호, 그러니까 Broadcom GPIO 번호 체계의 GPIO 핀 번호를 사용하지 않고, 자체적인 핀 번호를 이용한다.

이 번호는 상술한 "gpio readall" 명령을 통해 확인할 수 있다. 해당 명령으로 인한 출력에서 'wPi' 라인이 바로 그것이다.

기존의 BCM 체계를 사용할 수도 있지만, 추가 옵션이 붙는 등 명령이 조금 복잡해진다.


예를 들어, BCM GPIO 17 핀wPi 0 핀이다.

 

이를 통해 아래와 같은 명령을 실행할 수 있다.

# Usage: gpio write pin value

# wPi 0번 핀(BCM GPIO 17)을 OUTPUT으로 설정
gpio mode 0 out

# wPi 0번 핀(BCM GPIO 17)을 HIGH로 설정
gpio write 0 1

# wPi 0번 핀(BCM GPIO 17)을 LOW로 설정
gpio write 0 0

# wPi 0번 핀(BCM GPIO 17)을 INPUT으로 설정(중요함)
# 핀을 원래의 상태로 돌려놓는 역할을 함
gpio mode 0 in

 

BCM 번호 체계를 사용하려면 "-g" 옵션이 붙는다.

# Usage: gpio -g write pin value
# -g 옵션이 붙는 위치 매우 중요

# wPi 0번 핀(BCM GPIO 17)을 OUTPUT으로 설정
gpio -g mode 17 out

# wPi 0번 핀(BCM GPIO 17)을 HIGH로 설정
gpio -g write 17 1

# wPi 0번 핀(BCM GPIO 17)을 LOW로 설정
gpio -g write 17 0

# wPi 0번 핀(BCM GPIO 17)을 INPUT으로 설정(중요함)
# 핀을 원래의 상태로 돌려놓는 역할을 함
gpio -g mode 17 in

코드 작성


wiringPi는 C/C++에서 사용 가능하다. 즉, C 스타일과 C++ 스타일 모두를 이용할 수 있다는 것이다.

간단한 코드를 작성할 때에는 C로 작성할 때와 차이가 거의 없다.
그러나 복잡한 함수를 사용하기 시작하면 사실상 순수 C, C++ 코딩의 영역에 들어서게 되므로, 자신의 필요에 맞게 작성하면 된다.


C로 작성


WiringPi 번호 체계 사용


일단 먼저 WiringPi 번호 체계를 사용하여 C로 코드를 작성해 보자.

WiringPi 번호 체계를 사용하려면 'wiringPiSetup()'를 사용한다.

 

다음은 예문이다.

// LED.c

#include <wiringPi.h>
#include <stdio.h>

#define LED_PIN 0  // wPi 핀 번호 0. 이 경우는 BCM GPIO 17핀

int main(void) {
    wiringPiSetup();	// 필수 함수. WiringPi 번호 체계를 사용하여 핀을 제어.

    pinMode(LED_PIN, OUTPUT);  // 핀을 출력 모드로 설정

    while (1) {
        digitalWrite(LED_PIN, HIGH);  // 핀을 켜기
        printf("LED ON\n");	// 문구 출력
        delay(1000);                  // 1초 대기
        digitalWrite(LED_PIN, LOW);   // 핀을 끄기
        printf("LED OFF\n");	// 문구 출력
        delay(1000);                  // 1초 대기
    }

    return 0;
}

코드를 보면 알 수 있듯, 아두이노의 코드와 상당히 유사한 것을 확인할 수 있다. 이는 wiringPi가 아두이노의 코드를 참고한 것도 있지만, 기본적으로 아두이노가 C/C++ 기반의 코드로 작동하기 때문이다. 컴파일 과정을 살펴보면 내부적으로 스케치를 C++ 환경에서 컴파일한다.

문제 없이 컴파일되는 것을 확인할 수 있다.

그 예시로, 아두이노 IDE에서 C 라이브러리를 문제없이 사용할 수 있다. 정작 C++ 라이브러리는 최적화 문제 때문에 없어서 못 쓰쓴다.


따라서 이렇게 하면 아두이노와 거의 같은 스타일로 코드를 작성할 수 있다.

// LED.c

#include <wiringPi.h>
#include <stdio.h>

#define LED_PIN 0  // wPi 핀 번호 0. 이 경우는 BCM GPIO 17핀

// 기본 함수 선언
void setup();
void loop();

int main(void) {
    wiringPiSetup();	// 필수 함수. WiringPi 번호 체계를 사용하여 핀을 제어.

    setup();

    while (1) 
    {
        loop();
    }

    return 0;
}

void setup()
{
    pinMode(LED_PIN, OUTPUT);  // 핀을 출력 모드로 설정
}

void loop()
{
    digitalWrite(LED_PIN, HIGH);  // 핀을 켜기
    printf("LED ON\n");	// 문구 출력
    delay(1000);                  // 1초 대기
    digitalWrite(LED_PIN, LOW);   // 핀을 끄기
    printf("LED OFF\n");	// 문구 출력
    delay(1000);                  // 1초 대기
}

이렇게 하면 main() 함수를 건드릴 필요 없이 아두이노 스타일로 코딩이 가능하다.


BCM 번호 체계를 사용


이번에는 BCM 번호 체계를 사용하여 코드를 작성해보자.

BCM 번호 체계를 사용하려면 'wiringPiSetup()' 대신 'wiringPiSetupGpio()'를 사용한다.

 

예제는 아래와 같다.

// LED.c

#include <wiringPi.h>
#include <stdio.h>

#define LED_PIN 17  // BCM GPIO 17핀

// 기본 함수 선언
void setup();
void loop();

int main(void) {
    wiringPiSetupGpio();	// 필수 함수. BCM 번호 체계를 사용하여 핀을 제어.

    setup();

    while (1) 
    {
        loop();
    }

    return 0;
}

void setup()
{
    pinMode(LED_PIN, OUTPUT);  // 핀을 출력 모드로 설정
}

void loop()
{
    digitalWrite(LED_PIN, HIGH);  // 핀을 켜기
    printf("LED ON\n");	// 문구 출력
    delay(1000);                  // 1초 대기
    digitalWrite(LED_PIN, LOW);   // 핀을 끄기
    printf("LED OFF\n");	// 문구 출력
    delay(1000);                  // 1초 대기
}

C++로 작성


상술하였듯, 간단한 코드를 작성할 때에는 C로 작성할 때와 차이가 거의 없다.

그러나 복잡한 코드를 작성하면 차이가 벌어진다. 그냥 자신에게 익숙한 것을 사용하면 된다.


WiringPi 번호 체계 사용


먼저 WiringPi 번호 체계를 사용하여 C++로 코드를 작성해 보자.

C와 마찬가지로 WiringPi 번호 체계를 사용하려면 'wiringPiSetup()'를 사용한다.

 

다음은 예문이다.

// LED.cpp

#include <wiringPi.h>
#include <iostream>

#define LED_PIN 0  // wPi 핀 번호 0. 이 경우는 BCM GPIO 17핀

// 기본 함수 선언
void setup();
void loop();

int main() {
    wiringPiSetup();    // 필수 함수. WiringPi 번호 체계를 사용하여 핀을 제어.

    setup();

    while (true) 
    {
        loop();
    }

    return 0;
}

void setup()
{
    pinMode(LED_PIN, OUTPUT);  // 핀을 출력 모드로 설정
}

void loop()
{
    digitalWrite(LED_PIN, HIGH);  // 핀을 켜기
    std::cout << "LED ON" << std::endl;	// 문구 출력
    delay(1000);                  // 1초 대기
    digitalWrite(LED_PIN, LOW);   // 핀을 끄기
    std::cout << "LED OFF" << std::endl;	// 문구 출력
    delay(1000);                  // 1초 대기
}

BCM 번호 체계를 사용


이번에는 BCM 번호 체계를 사용하여 코드를 작성해보자.

이 역시 C와 마찬가지로  BCM 번호 체계를 사용하려면 'wiringPiSetup()' 대신 'wiringPiSetupGpio()'를 사용한다.

 

예제는 아래와 같다.

// LED.cpp

#include <wiringPi.h>
#include <iostream>

#define LED_PIN 17  // BCM GPIO 17핀

// 기본 함수 선언
void setup();
void loop();

int main() {
    wiringPiSetupGpio();    // 필수 함수. BCM 번호 체계를 사용하여 핀을 제어.

    setup();

    while (true) 
    {
        loop();
    }

    return 0;
}

void setup()
{
    pinMode(LED_PIN, OUTPUT);  // 핀을 출력 모드로 설정
}

void loop()
{
    digitalWrite(LED_PIN, HIGH);  // 핀을 켜기
    std::cout << "LED ON" << std::endl;	// 문구 출력
    delay(1000);                  // 1초 대기
    digitalWrite(LED_PIN, LOW);   // 핀을 끄기
    std::cout << "LED OFF" << std::endl;	// 문구 출력
    delay(1000);                  // 1초 대기
}

작성한 코드 실행하기


아래의 예문을 참고하여 명령을 입력한다.

gcc "소스 파일의 경로" -o "실행 파일의 경로" -l wiringPi	# C로 작성한 경우
g++ "소스 파일의 경로" -o "실행 파일의 경로" -l wiringPi	# C++로 작성한 경우

 

gcc ./LED.c -o LED -l wiringPi	# C로 작성한 경우
g++ ./LED.cpp -o LED -l wiringPi	# C++로 작성한 경우

별도의 경고문이 없으면 컴파일이 완료된 것이다.

 

이제 실행하면 된다.

아래의 명령을 입력하되, 아래의 오류가 발생한다면 sudo 권한으로 실행한다..

sprout1345@sproutpi:~$ ./LED
wiringPiSetup: Unable to open /sys/bus/pci/devices/0000:01:00.0/resource1 or /dev/gpiomem0: Permission denied.
  Aborting your program because if it can not access the GPIO
  hardware then it most certianly won't work
  Try running with sudo?
sprout1345@sproutpi:~$ sudo ./LED

여담


wiringPi를 이용해 C/C++ 코드에서 OUTPUT 상태로 활성화 한 핀은 프로그램을 종료하여도 OUTPUT 상태로 남아있다.

이를 INPUT 상태로 원상 복구하기 위해서는 "gpio mode <wPi 핀 번호> in"으로 일일이 끄거나, 프로그램을 종료할 때  핀 모드를 INPUT 상태로 전환하게끔 C/C++ 코드를 작성하여야 한다.

 

INPUT 상태로 만들어야 하는지 자세한 정보를 알고 싶다면 아래 참고 자료 항목에 첨부된 영상을 보면 된다.


wiringPiSetup()를 사용하던 wiringPiSetupGpio()를 사용하던 사용자의 마음이지만, 되도록이면 wiringPiSetupGpio()를 사용하는 것이 나을 수 있다. 라즈베리 파이 특성 상, 많은 사람들이 pythongpiozero 등을 이용하여 코드를 작성하는데, 여기서 사용하는 핀 체계는 BCM 번호 체계이다. 나중에 C/C++ ↔ Python 간 코드를 변경/참고할 일이 있을 경우, wiringPi 번호 체계를 사용하면 일일히 핀 번호를 변경해주어야 하지만, BCM 번호 체계를 사용할 경우 그런 수고를 덜 수 있다.


참고 자료


더보기

Raspberry Pi 시리즈


728x90
반응형