버튼으로 경적 울리기 (with ESP32, Raspberry Pi, 피에조 부저)

GAON PARK·2023년 11월 23일
0
post-custom-banner

피에조 부저

피에조 효과를 이용한 부저인데, 초음파 센서와 같은 효과로 동작한다. 진동 주파수를 이용해 원하는 음계를 낼 수 있다.

피에조 효과?

얇은 금속 판에 전기를 주면 진동이 발생하는데 그 발생된 진동으로 인해 파동이 생긴다. 이때 생긴 파동이 귀에 들리는 소리 영역이라면 음파라고 칭하며, 부저로 동작한다. 귀에 들리지 않는 소리 영역이라면 초음파라고 칭하며, 초음파 센서로 동작한다.

회로 연결

라즈베리파이 & 부저

buzzerraspberry pi gpio pin num
sgpio14
-gnd
vcc5v

ESP32 & 버튼

btnesp32 pin
buzzer btn1gnd
buzzer btn2gpio17

코드

라즈베리파이 코드

Tone

나중에 부저로 멜로디를 쓸 일이 있을까 싶어서 음계별 주파수를 딕셔너리로 만들었다.

TONE_DIC = {
    'c4': 261.62,
    'c4#': 277.18,
    'd4': 293.66,
    'd4#': 311.12,
    'e4': 329.62,
    'f4': 349.22,
    'f4#': 369.99,
    'g4': 391.99,
    'g4#': 415.3,
    'a4': 440,
    'a4#': 466.16,
    'b4': 493.88,

    'c5': 523.25,
    'c5#': 554.36,
    'd5': 587.32,
    'd5#': 622.24,
    'e5': 659.25,
    'f5': 698.45,
    'f5#': 739.98,
    'g5': 783.99,
    'g5#': 830.6,
    'a5': 880,
    'a5#': 932.32,
    'b5': 987.76,
}

etc_thread.py

  • 부저가 울리고 있는 동시에 다른 기능도 수행하기 위해 thread로 구현했다.
  • 다른 기능과 마찬가지로 ESP32에서 부저 커맨드를 입력받아 음을 울려야 하기 때문에, mqtt 클라이언트를 만들고 etc topic을 구독하게 했다.
  • buzzer_on이 들어오면 (= ESP32에 연결된 버튼이 pressed 상태에 접어드는 순간) 부저를 play() 하는데, 적당히 C4정도로 줬다. (부저 성능이 안 좋은 건지 소리가 약간 찢어진다;)
  • buzzer_off가 들어오면 (= ESP32에 연결된 버튼이 released 상태에 접어드는 순간) play되고 있던 부저를 stop()한다.

etc_thread.py

import threading  
from gpiozero import TonalBuzzer  
import socket  
import paho.mqtt.client as mqtt  
import tone_dic  
  
  
class EtcThread(threading.Thread):  
    BROKER_ADDRESS = socket.gethostbyname(socket.gethostname())  
    buzzer = TonalBuzzer(14)  
  
    def __init__(self):  
        super().__init__()  
        self.client = mqtt.Client("etc_sub")  
        self.client.connect(self.BROKER_ADDRESS)  
        self.client.subscribe("etc")  
        self.client.on_message = self.on_command  
  
    def on_command(self, client, userdata, message):  
        cmd = str(message.payload.decode("utf-8"))  
        if "buzzer_on" == cmd:  
            self.buzzer.play(tone_dic.TONE_DIC['c4'])  
        elif "buzzer_off" == cmd:  
            self.buzzer.stop()  
  
    def run(self):  
        self.client.loop_forever()

main app.py

스레드 객체로 만든 거니까 app.py 에서도 생성&실행을 해줘야 한다.

import cmd_thread  
import etc_thread  
  
  
if __name__ == '__main__':  
    # ... cmd thread 생성 & start
    etc_th = etc_thread.EtcThread()  
    etc_th.start()  

ESP32 코드

직진, 후진 버튼을 만들었던 것처럼 마찬가지로 thread로 press/release 상태를 측정한다.

  • buzzer 버튼에 맞춘 pin 번호를 define 한다.
  • buzzer_button_check() : 커스텀 객체가 아닌, 기본 Thread 객체를 만들어서 interval 마다 주기적으로 실행할 함수를 정의했다.
  • COMMAND_XXX는 값이 바뀌지 않는 한 mqtt 송신을 한 번만 보내도록 하기 위해 만든 bool 변수로 volatile 키워드를 추가해 과하게 똑똑한 컴파일러의 방해(!)를 차단했다.
#define BUZZER_BUTTON 17                // the number of the buzzer button pin

// 한 번만 보내기 위한 flg 변수  
volatile bool COMMAND_BUZZER = false;
char *ETC_TOPIC = "etc";

// ... EspMQTTClient 코드
// ... go/back command 실행될 스레드 함수 관련 코드
// ... mpu6050 값을 읽을 스레드 클래스 관련 코드
// ... mqtt 송신용 tx() 함수
// ... 

// 스레드가 설정된 interval에 맞춰 주기적으로 실행할 함수
void buzzer_button_check(void) {
  if (digitalRead(BUZZER_BUTTON) == LOW && !COMMAND_BUZZER) {
    // Serial.println("buzzer_on");
    tx(ETC_TOPIC, "buzzer_on");
    COMMAND_BUZZER = true;
  } else if (digitalRead(BUZZER_BUTTON) == HIGH && COMMAND_BUZZER) {
    // Serial.println("buzzer_off");
    tx(ETC_TOPIC, "buzzer_off");
    COMMAND_BUZZER = false;
  }
}

// create thread
Thread buzzer_th = Thread();

// ... thread controller 관련 코드

void setup(void) {
  // ... serial monitor begin
  // ... mqtt client enable 

  // ... command (go/back) button 
  // buzzer button 
  pinMode(BUZZER_BUTTON, INPUT_PULLUP);

  // callback thread func
  // ... (go/back) button callback thread func set
  // buzzer button callback thread func set
  buzzer_th.onRun(buzzer_button_check);
  buzzer_th.setInterval(50);
  
  // ... mpu thread add to thread controller
  // ... cmd thread add to thread controller
  // buzzer thread add to thread controller
  controller.add(&buzzer_th);

  // ... mpu init code 
}
post-custom-banner

0개의 댓글