【Rust】Raspberry Pi로 부저 제어하기
Table of Contents
개요
트랜지스터, 부저, 택트 스위치를 사용하여 Rust로 부저 울림을 제어하는 방법을 설명합니다.
부품
사용하는 부품은 다음과 같습니다.
- NPN 트랜지스터 (S8050) * 1
- 택트 스위치 * 1
- 1kΩ 저항 * 1
- 10kΩ 저항 * 2
- 액티브 부저 * 1
- 점퍼선 * 9
회로
회로는 Freenove를 확인해 주세요. Raspberry Pi의 GPIO에서 출력할 수 있는 전류가 작기 때문에 큰 전류가 필요한 액티브 부저를 구동시킬 수 없습니다. 따라서 트랜지스터로 전류를 증폭시켜 액티브 부저가 동작할 수 있도록 하는 간단한 회로가 구성되어 있습니다.
코드
cargo로 rppal과 ctrlc를 추가하고 코드를 작성해 주세요.
Doorbell
이것은 버튼을 누르면 잠시 부저가 울리는 코드입니다.
use rppal::gpio::{Gpio, InputPin, OutputPin, Trigger};
use std::error::Error;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
const BUZZER_PIN: u8 = 17;
const BTN_PIN: u8 = 18;
const POLL_TIMEOUT_MS: u64 = 10;
fn main() -> Result<(), Box<dyn Error>> {
print_startup_message();
let (mut buzzer_pin, mut btn_pin) = initialize_gpio()?;
initialize_buzzer(&mut buzzer_pin);
setup_button_interrupt(&mut btn_pin)?;
let running = setup_signal_handler()?;
println!("버튼 누름을 기다리는 중...");
run_interrupt_loop(&running, &mut buzzer_pin, &mut btn_pin)?;
cleanup(&mut buzzer_pin, &mut btn_pin)?;
Ok(())
}
fn print_startup_message() {
println!("프로그램을 시작합니다...");
}
fn initialize_gpio() -> Result<(OutputPin, InputPin), Box<dyn Error>> {
let gpio = Gpio::new()?;
let buzzer_pin = gpio.get(BUZZER_PIN)?.into_output();
let btn_pin = gpio.get(BTN_PIN)?.into_input();
Ok((buzzer_pin, btn_pin))
}
fn initialize_buzzer(buzzer_pin: &mut OutputPin) {
buzzer_pin.set_low();
}
fn setup_button_interrupt(btn_pin: &mut InputPin) -> Result<(), Box<dyn Error>> {
btn_pin.set_interrupt(Trigger::Both, None)?;
Ok(())
}
fn setup_signal_handler() -> Result<Arc<AtomicBool>, Box<dyn Error>> {
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})?;
Ok(running)
}
fn run_interrupt_loop(
running: &Arc<AtomicBool>,
buzzer_pin: &mut OutputPin,
btn_pin: &mut InputPin,
) -> Result<(), Box<dyn Error>> {
while running.load(Ordering::SeqCst) {
if let Some(_) = btn_pin.poll_interrupt(true, Some(Duration::from_millis(POLL_TIMEOUT_MS)))? {
handle_button_interrupt(buzzer_pin, btn_pin);
}
}
Ok(())
}
fn handle_button_interrupt(buzzer_pin: &mut OutputPin, btn_pin: &InputPin) {
if is_button_pressed(btn_pin) {
turn_on_buzzer(buzzer_pin);
print_buzzer_on_message();
} else {
turn_off_buzzer(buzzer_pin);
print_buzzer_off_message();
}
}
fn is_button_pressed(btn_pin: &InputPin) -> bool {
btn_pin.is_low()
}
fn turn_on_buzzer(buzzer_pin: &mut OutputPin) {
buzzer_pin.set_high();
}
fn turn_off_buzzer(buzzer_pin: &mut OutputPin) {
buzzer_pin.set_low();
}
fn print_buzzer_on_message() {
println!("버튼이 눌려져 부저가 켜졌습니다 >>>");
}
fn print_buzzer_off_message() {
println!("버튼이 놓여져 부저가 꺼졌습니다 <<<");
}
fn cleanup(buzzer_pin: &mut OutputPin, btn_pin: &mut InputPin) -> Result<(), Box<dyn Error>> {
println!("프로그램을 종료합니다");
let _ = btn_pin.clear_interrupt();
turn_off_buzzer(buzzer_pin);
Ok(())
}
Alertor
이것은 버튼을 누르고 있는 동안 부저가 계속 울리는 코드입니다.
use rppal::gpio::{Gpio, InputPin, OutputPin};
use std::error::Error;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
const BUZZER_PIN: u8 = 17;
const BTN_PIN: u8 = 18;
const ALERTOR_FREQUENCY: f64 = 220.0; // 220Hz
const ALERTOR_DUTY_CYCLE: f64 = 0.5; // 50% 듀티 사이클
const LOOP_DELAY_MS: u64 = 10;
const ALERTOR_DURATION_MS: u64 = 100;
fn main() -> Result<(), Box<dyn Error>> {
print_startup_message();
let (mut buzzer_pin, btn_pin) = initialize_gpio()?;
initialize_buzzer(&mut buzzer_pin);
let running = setup_signal_handler()?;
println!("버튼 누름을 기다리는 중...");
run_main_loop(&running, &mut buzzer_pin, &btn_pin)?;
cleanup(&mut buzzer_pin)?;
Ok(())
}
fn print_startup_message() {
println!("프로그램을 시작합니다...");
}
fn initialize_gpio() -> Result<(OutputPin, InputPin), Box<dyn Error>> {
let gpio = Gpio::new()?;
let buzzer_pin = gpio.get(BUZZER_PIN)?.into_output();
let btn_pin = gpio.get(BTN_PIN)?.into_input();
Ok((buzzer_pin, btn_pin))
}
fn initialize_buzzer(buzzer_pin: &mut OutputPin) {
buzzer_pin.set_low();
}
fn setup_signal_handler() -> Result<Arc<AtomicBool>, Box<dyn Error>> {
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})?;
Ok(running)
}
fn run_main_loop(
running: &Arc<AtomicBool>,
buzzer_pin: &mut OutputPin,
btn_pin: &InputPin,
) -> Result<(), Box<dyn Error>> {
while running.load(Ordering::SeqCst) {
if is_button_pressed(btn_pin) {
play_alertor_sound(buzzer_pin)?;
print_alertor_on_message();
} else {
stop_alertor_sound(buzzer_pin)?;
print_alertor_off_message();
}
thread::sleep(Duration::from_millis(LOOP_DELAY_MS));
}
Ok(())
}
fn is_button_pressed(btn_pin: &InputPin) -> bool {
btn_pin.is_low()
}
fn play_alertor_sound(buzzer_pin: &mut OutputPin) -> Result<(), Box<dyn Error>> {
buzzer_pin.set_pwm_frequency(ALERTOR_FREQUENCY, ALERTOR_DUTY_CYCLE)?;
thread::sleep(Duration::from_millis(ALERTOR_DURATION_MS));
Ok(())
}
fn stop_alertor_sound(buzzer_pin: &mut OutputPin) -> Result<(), Box<dyn Error>> {
buzzer_pin.clear_pwm()?;
buzzer_pin.set_low();
Ok(())
}
fn print_alertor_on_message() {
println!("경보가 켜졌습니다 >>> ");
}
fn print_alertor_off_message() {
println!("경보가 꺼졌습니다 <<<");
}
fn cleanup(buzzer_pin: &mut OutputPin) -> Result<(), Box<dyn Error>> {
println!("프로그램을 종료합니다");
stop_alertor_sound(buzzer_pin)?;
Ok(())
}
동작 모습
시ン플한 회로로 Rust에서 놀아봤습니다 pic.twitter.com/zb7gMyNoqU
— K (@rmc_km) June 9, 2025
요약
트랜지스터의 사용법을 확인하면서 Rust로 부저의 울림 방식을 제어하는 방법을 배울 수 있었습니다. 회로 자체가 간단하므로 트랜지스터로 전류를 증폭시켜 부저가 동작하는 것을 확인할 수 있었습니다. 전원이 2계통 필요하지만 Raspberry Pi라면 간단히 공급할 수 있고 트랜지스터 학습을 간단하게 할 수 있습니다. 앞으로도 임베디드와 회로를 함께 정리해서 계속 배워나가고 싶습니다.