-
Embedded Swift: 핀(GPIO Pin) 제어하기 (1) - LED 깜빡이기공부/Swift(프로그래밍 언어) 2026. 4. 23. 20:26반응형
GPIO(General Purpose Input/Output) 핀은 마이크로컨트롤러나 라즈베리 파이 같은 임베디드 시스템에서 외부 센서, LED, 모터 등과 디지털 신호(0 또는 1, High/Low)를 주고받는 범용 입출력 핀입니다. 프로그램 런타임에 입력(Input) 또는 출력(Output) 모드를 설정하여 제어할 수 있는 것이 특징입니다. 범용 입출력 핀, I/O 핀, 디지털 핀, GPIO 포트 라는 이름으로도 불립니다.
주요 특징 및 기능
- 범용성: 고정된 기능 대신 사용자가 상황에 따라 입력 또는 출력 핀으로 설정 가능.
- 디지털 제어: 3.3V 또는 5.5V 의 논리 레벨(High/Low)로 동작.
- 구성: 라즈베리 파이 40핀 등 보드 내외에 위치하며, 입력 시 신호 감지, 출력 시 기기 구동.
활용 사례
- 출력(Output): LED 온/오프 제어, 모터 드라이버 제어, 릴레이 스위칭, LCD 디스플레이 구동.
- 입력(Input): 푸시 버튼 눌림 감지, 동작 센서(PIR) 신호 감지, 조도 센서 값 읽기.
- 통신: I2C, SPI, UART 같은 통신 프로토콜 핀으로도 사용 가능.
이번 포스트에서는 단순 출력을 활용하여 LED를 깜빡이는 예제를 진행합니다.
동작 원리
- 핀 모드 설정: Embedded Swift 프로그래밍으로 핀을 INPUT 또는 OUTPUT으로 설정.
- 출력: HIGH(1) 신호를 보내 전류를 공급하거나 LOW(0)로 접지(GND).
- 입력: 외부의 전압 신호를 받아 0(Low) 혹은 1(High) 상태를 감지.
방법
마이크로컨트롤러(ESP32-C6)를 핀과 연결

ESP32의 GPIO 핀 및 핀헤더
기판의 가장자리에 있는 구멍이 GPIO 핀이며, 그 옆에 있는 연결 파트는 '핀 헤더'라고 합니다. 일반적으로 핀 헤더를 연결한 후, 납땜을 해서 고정시키는 것이 일반적입니다. 납땜을 하지 않으면 연결상태가 불안정해 동작이 제대로 되지 않을 수 있습니다. 테스트 목적이라면 납땜하지 않을 수도 있습니다.
핀 헤더를 납땜한 모습 전원 연결
컨트롤러를 브레드보드에 꼽고, 핀 중 GND를 전원 레일의 (-)에 연결합니다. 핀 중 5V(또는 Vcc)를 전원 레일의 (+)에 연결합니다. 다음 브레드보드 또는 ESP32의 USB로 전원을 공급하면 컨트롤러의 전원이 켜져야 합니다.

브레드보드에 꽂은 모습 위의 사진을 보면 브레드보드를 꼽으면 남는 자리가 없습니다. 이처럼 기판 크기에 따라 브레드보드에 꼽을 자리가 하나도 없을 수 있습니다. 제 경우에는 공간 9칸을 차지하고 자리 하나가 남았기 때문에 그대로 진행합니다.
내장 LED가 표시되도록 미리 빌드 및 플래시하고, 전원이 정상적으로 연결되었는지 확인합니다. (방법은 아래 포스트 링크 참조)
Embedded Swift 설치, 코드 작성, ESP32-C6에서 빌드 및 작동시키는 방법
맥북에 ESP32-C6 드라이버 및 필수 프로그램 설치 방법 [임베디드] ESP32-C6 맥북 등 macOS에서 빌드 및 동작 테스트 하기ESP32-C6 라는 임베디드 보드의 빌드 및 동작이 잘 되는지 'macOS' 에서 확인하는 방
infoarmory.tistory.com
컨트롤러 프로그래밍 및 빌드 & 플래시
사전 설정을 통해 GPIO를 다룰 수 있는 라이브러리를 추가해야 합니다. BridgingHeader.h 파일을 열고 #include "driver/gpio.h"를 추가합니다.
#ifndef BRIDGING_HEADER_H #define BRIDGING_HEADER_H #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "led_strip.h" #include "sdkconfig.h" #include "driver/gpio.h" // 👈 추가 // ... // #endif /* BRIDGING_HEADER_H */GPIO를 리셋하고 Input/Output 여부를 설정하는 것이 중요합니다. GPIOPin 이라는 이름의 구조체를 만들고 초기화 부분을 추가합니다.
struct GPIOPin { let pinNumber: Int init(pinNumber: Int) { self.pinNumber = pinNumber gpio_reset_pin(gpio_num_t(Int32(pinNumber))) gpio_set_direction(gpio_num_t(Int32(pinNumber)), GPIO_MODE_OUTPUT) } // ... }- pinNumber: 사용할 핀의 번호로, 기판에 1, 2, 3... 등의 숫자로 적혀 있습니다.
- gpio_reset_pin: 기존에 부여되어 있던 핀의 역할을 리셋합니다. 이 부분을 실행하지 않는 경우 이용할 수 있는 핀의 개수가 매우 적을 수 있습니다. 제 경우에는 리셋하지 않은 경우 2번 핀, 15번 핀 두개만이 원래부터 비어있던 핀이어서 사용할 수 있었습니다.
- gpio_set_direction: Input/Output을 설정합니다. 이번 예제는 출력만 사용하므로 GPIO_MODE_OUTPUT을 사용합니다.
다음 GPIO의 출력을 High/Low로 토글시키는 함수를 추가합니다.
func set(_ level: Bool) { gpio_set_level(gpio_num_t(Int32(pinNumber)), level ? 1 : 0) } func high() { set(true) } func low() { set(false) }- gpio_set_level: GPIO 의 레벨을 High 또는 Low로 설정합니다. 1을 입력하면 High, 0을 입력하면 Low가 됩니다.
- high의 경우 (+) 레일과 연결한 것과 동일한 효과가 되고, low인 경우 접지에 연결한 것과 동일한 효과가 됩니다.
이용할 핀 목록 작성
struct GPIO { let pins: [GPIOPin] init(pinNumbers: [Int] = [2, 3, 4, 5, 6, 7, 10, 11, 15]) { self.pins = pinNumbers.map { GPIOPin(pinNumber: $0) } } }- 편의상 구조체로 만들었으나 구조체 말고도 단순 배열 또는 단일 GPIOPin 오브젝트로도 추가할 수 있습니다.
메인 프로그램 작성
@_cdecl("app_main") func main() { ExamplePin.run() }func delay(ms: UInt32) { vTaskDelay(ms / (1000 / UInt32(configTICK_RATE_HZ))) }struct ExamplePin { static func run() { let strip = LedStrip(gpioPin: 8, maxLeds: 1) strip.setPixel(index: 0, color: .init(r: 2, g: 2, b: 0)) strip.refresh() let gpio = GPIO() // 👈 이용 가능한 GPIO 목록 추가 while true { gpio.pins.forEach { $0.high() } // 👈 GPIO를 전부 켜기 strip.setPixel(index: 0, color: .init(r: UInt8.random(in: 0..<4), g: UInt8.random(in: 0..<4), b: UInt8.random(in: 0..<4))) strip.refresh() delay(ms: 1000) // 1초 딜레이 gpio.pins.forEach { $0.low() } // 👈 GPIO를 전부 끄기 strip.clear() delay(ms: 1000) // 1초 딜레이 } } }- LED strip 부분은 내장 LED 테스트용 코드이기 때문에 이 예제와는 무관합니다.
- 다만 LED 스트립이 핀 번호 8번을 점유하고 있기 때문에, 핀 8번은 외부에서 사용할 수 없다는 점은 참고해주세요
- while 문
- 이용 가능한 GPIO 목록을 forEach로 순회하며 1초간 high 상태로 만듭니다.
- 다음 GPIO 목록을 다시 순회하며 1초간 low 상태로 만듭니다.
- 이를 통해 핀 연결이 제대로 되었다면 외부 LED가 1초간 켰다 꺼짐을 반복하게 되고, 내장 LED도 동일하게 동작하도록 하였으므로 같은 타이밍에 불빛이 켜지고 꺼져야 합니다.
빌드 & 플래시를 하여 컨트롤러를 업데이트합니다.
LED 출력 확인
이렇게 해서 프로그래밍 방식으로 핀에 high/low를 번갈아가면서 출력하도록 했다면, 이것을 외부 LED에 연결해서 실제로 핀 출력이 의도대로 되고 있는지 확인해야 합니다.
원래 브레드보드에 단독으로 LED를 연결하는 방법에 알아보겠습니다.
브레드보드 사용법 및 실습 예제: LED 1개, 버튼을 통해 LED 켜기
브레드보드를 처음 쓰는 초보자 기준으로 가장 간단하면서 핵심 개념을 배우는 회로를 만들어 보는 방법을 설명하도록 하겠습니다. 브레드보드 구조브레드보드는 내부가 이렇게 연결되어 있습
infoarmory.tistory.com

(+) 전원 레일과 LED의 긴 다리 부분 (애노드)이 연결되어 있는데, 여기서 전원 레일에 연결된 부분을 빼고 이것을 ESP32의 핀에 연결합니다. 핀이 High 가 된 경우 (+) 레일에 연결한 것과 동일한 효과를 나타내므로 LED가 켜져야 합니다.
동작할 것이라 예상되는 핀 중 하나인 15번 핀을 사용하여 점퍼선 등으로 LED 긴 다리 부분(애노드)와 연결합니다.

전원을 넣어 불빛이 동기화가 되는지 확인합니다. 나머지 핀도 동일한 방법으로 테스트합니다.

동작 GIF [참고] ESP32-C6-WROOM-1 기준 안전 GPIO (권장)
아래 핀들은 일반 출력/입력으로 안정적으로 사용 가능한 핀입니다.
2, 3, 4, 5, 6, 7, 10, 11, 15왜 이 핀들이 안전한가
- 플래시/PSRAM 미사용 핀
- USB / JTAG / UART 충돌 없음
- 출력 가능
- 부트 영향 없음
주의해야 할 핀 (사용 피하기)
- 0 → 부트 스트랩 (부팅 과정에 사용)
- 1 → TX (로그 출력)
- 8 → 지금 strip 사용 중
- 9 → 내부 기능 충돌 가능
- 12~14 → 상황 따라 제약 있음
- 16~17 → 일부 보드에서 제한
- 18~20 → 보드별 편차 큼
반응형'공부 > Swift(프로그래밍 언어)' 카테고리의 다른 글
Embedded Swift: 핀(GPIO Pin) 제어하기 (2) - 입력 받기 (Input Mode) (1) 2026.04.29 [SwiftUI] SF Symbol에서 계층, 팔레트, 여러 가지 색상 사용하기 (0) 2026.04.28 Embedded Swift: ESP32-C6을 Wifi에 연결시켜 웹 서버로 사용하기, 브라우저에서 LED 제어 (0) 2026.03.31 Swift iOS: 디바이스 자체 설정에 방향 락이 걸려있을 때도 인식하게 하는 법 (0) 2026.03.23 Embedded Swift 설치, 코드 작성, ESP32-C6에서 빌드 및 작동시키는 방법 (0) 2026.03.21