센서
라즈베리파이에서도 아두이노와 같이 센서를 사용할 수 있다. 3차시에서 정리한 GPIO에 각종 센서들을 연결할 수 있다.
택트스위치


택트스위치는, 4개의 핀중 2쌍은 연결되어있고, 그 2쌍은 서로 가운데 스위치를 두고 연결되어 있다.
평소에는 a는 a끼리, b는 b끼리만 통전되다가, 스위치를 누르면 가운데가 연결되며 핀4개가 모두 통전되는 방식이다.
다음과같이 회로를 연결하고, 저번에 구축한 VSC에서 코드를 적용한다.
라즈베리파이는 아두이노와는 다르게 풀업저항이 내장되어있지 않아 플로팅현상이 발생할 수 있다.
따라서 풀업저항을 따로 구성해야한다.

import RPi.GPIO as IoPort
SW1 = 8
Led = 18
IoPort. setmode(IoPort.BCM)
IoPort. setup(Led, IoPort. OUT)
IoPort. setup(Sw1, IoPort. IN)
while True:
rcv = IoPort. input(Sw1)
IoPort. output(Led, rcv)IoPort.setmode(IoPort.BCM): GPIO 핀 번호를 BCM(Broadcom SoC) 방식으로 설정합니다.IoPort.setup(Led, IoPort.OUT): LED 핀을 출력 모드로 설정합니다.IoPort.setup(SW1, IoPort.IN): 스위치 핀을 입력 모드로 설정합니다.rcv = IoPort.input(SW1): 스위치 핀에서 현재 입력 값을 읽어 변수rcv에 저장합니다.IoPort.output(Led, rcv): LED 핀에 스위치에서 읽은 입력 값(rcv)을 출력합니다. 스위치가 눌려 있으면 LED가 켜지고, 그렇지 않으면 꺼집니다.
스위치를 누르면 LED가 켜지는것을 볼 수 있다.
부저

부저에서 소리가 나는 원리는 피에조 효과(압전효과)와 같다. 압전 효과는 물체에 기계적인 압력을 가하면 전압이 발생하고, 역으로 전압을 가하면 기계적인 변형이 발생하는 현상을 뜻한다. 이 압전효과를 이용하여 부저에서는 모듈 안쪽에 얇은 판을 사용하여 미세한 떨림을 만들어내 소리를 발생시킬 수 있는 것이다. 그렇기 때문에 아두이노의 부저는 디지털 핀의 전압을 매우 짧은 시간 안에 바꾸어가며 주파수에 맞는 소리를 낼 수 있도록 되어있다. 부저 또한 PWM방식으로 작동이 된다.
아래와 같이 회로를 구성하고 코드를 적용하면, 부저에서 도,레,미,파,솔,라,시,도가 순서대로 출력되는것을 확인할 수 있다.

import RPi.GPIO as GPIO
import time
GPIO. setmode(GPIO.BCM)
GPIO. setup(12,GPIO. OUT)
scale=|262, 294, 330, 349, 392, 440, 494, 523]
try:
p=GPIO. PWM(12, 100)
p.start( 100)
p. ChangeDutyCycle(90)
for i in range(8):
p. ChangeFrequency(scale[i])
time.sleep (1)
p.stop()
finally:
GPIO.cleanup()GPIO.setmode(GPIO.BCM):- GPIO 핀 번호 체계를 BCM(Broadcom SoC) 방식으로 설정합니다.
- BCM 모드는 GPIO 핀 번호를 칩의 GPIO 번호로 사용합니다.
GPIO.setup(12, GPIO.OUT):- GPIO 핀 번호 12번을 출력 모드로 설정합니다.
- 이 핀에 스피커나 버저를 연결하여 음을 재생합니다.
scale:
- 음악 음계의 주파수 값을 담은 리스트입니다.
- 각각 도(C), 레(D), 미(E), 파(F), 솔(G), 라(A), 시(B), 높은 도(C)의 주파수(단위: Hz)입니다.
- 주파수 값:
- 도 (C4): 262 Hz
- 레 (D4): 294 Hz
- 미 (E4): 330 Hz
- 파 (F4): 349 Hz
- 솔 (G4): 392 Hz
- 라 (A4): 440 Hz
- 시 (B4): 494 Hz
- 높은 도 (C5): 523 Hz
try:- 예외 처리를 위해 사용합니다. 프로그램 실행 중 에러가 발생하더라도
finally블록이 실행되도록 합니다. p = GPIO.PWM(12, 100):- GPIO 핀 12번에서 PWM 신호를 생성하는 객체
p를 만듭니다. - 초기 주파수는 100 Hz로 설정됩니다.
p.start(100):- PWM 신호 출력을 시작합니다.
- 듀티 사이클(Duty Cycle)을 100%로 설정하여 신호를 완전히 켜진 상태로 시작합니다.
p.ChangeDutyCycle(90):- 듀티 사이클을 90%로 변경합니다.
- 이는 신호의 펄스 폭을 90%로 설정하여 적절한 음량을 조절합니다.
for i in range(8)::- 0부터 7까지 총 8번 반복하는 루프를 시작합니다.
- 각 반복에서
scale리스트의 주파수를 사용합니다. p.ChangeFrequency(scale[i]):- PWM 신호의 주파수를
scale[i]로 변경합니다. - 이로써 각 음계에 해당하는 소리가 재생됩니다.
time.sleep(1):- 현재 주파수로 1초 동안 소리를 재생합니다.
- 각 음을 1초씩 연주합니다.
p.stop():- PWM 신호 출력을 중지합니다.
- 모든 음계 연주가 끝난 후 PWM을 종료합니다.
finally:- 예외 발생 여부와 관계없이 마지막에 항상 실행되는 블록입니다.
GPIO.cleanup():- 사용한 GPIO 핀 설정을 초기화합니다.
- 프로그램 종료 시 GPIO 리소스를 깨끗하게 정리하여 다른 프로그램에서 GPIO를 사용할 때 문제를 방지합니다.
가변저항

가변저항의 작동 원리는 다음과 같다.
5V핀을 타고 올라간 전류는 가변저항 내부의 슬라이더를 타고 신호 역할을 하는 단자(A0)를 따라 내려가게 된다.
이 때 슬라이더의 위치에 따라 전류가 흐르는 도선의 길이가 달라지게 되어 저항을 조절할 수 있다.
즉, 옴의 법칙에 따라 도선의 길이가 길어지면 저항의 세기는 커지고, 도선의 길이가 짧아지면 저항이 작아지게 되는 원리이다.
아래와 같이 회로를 연결하고 코드를 적용하면,

import time
import spidev
spi = spidev.SpiDev()
spi.open(0,0)
spi.max_speed_hz = 1000000
def ReadVol(vol):
adc = spi.xfer2([1,(8+vol)<<4,0])
data = ((adc[1]&3)<<8)+adc[2]
return data
mcp3008 = 0
while True:
a_1 = ReadVol(mcp3008)
print('readvol:', a_1,'Voltage:', 3.3*a_1/1024)
time.sleep(0.5)def ReadVol(vol)::- 아날로그 값을 읽어오는 함수
ReadVol를 정의합니다. - *
vol*는 MCP3008의 아날로그 입력 채널 번호(0~7)를 나타냅니다. adc = spi.xfer2([1, (8 + vol) << 4, 0]):- SPI를 통해 3바이트의 데이터를 전송하고 응답을 받습니다.
[1, (8 + vol) << 4, 0]:- 첫 번째 바이트 (
1): Start 비트입니다. - 두 번째 바이트 (
(8 + vol) << 4): - 싱글 엔드 모드에서 채널 번호를 설정합니다.
8 + vol로 채널을 지정하고, 왼쪽으로 4비트 시프트하여 상위 비트로 이동합니다.- 세 번째 바이트 (
0): 자리 채움용 바이트입니다. spi.xfer2()함수는 리스트의 데이터를 SPI로 전송하고, 동일한 길이의 응답 리스트를 반환합니다.data = ((adc[1] & 3) << 8) + adc[2]:- 수신된 데이터에서 10비트 ADC 값을 추출합니다.
adc[1] & 3:- 두 번째 바이트에서 하위 2비트만 사용합니다.
((adc[1] & 3) << 8):- 하위 2비트를 왼쪽으로 8비트 시프트하여 상위 비트로 이동합니다.
adc[2]:- 세 번째 바이트 전체를 사용합니다.
((adc[1] & 3) << 8) + adc[2]:- 상위 비트와 하위 비트를 합쳐 10비트 ADC 값을 얻습니다.
return data:- 읽어온 ADC 값을 반환합니다.
mcp3008 = 0:
- MCP3008의 아날로그 입력 채널 0번을 사용하겠다는 의미입니다.
- 변수
mcp3008에 채널 번호를 저장합니다. a_1 = ReadVol(mcp3008):- 함수
ReadVol를 호출하여 채널 0번의 ADC 값을 읽어옵니다. - 반환된 값을 변수
a_1에 저장합니다. print('readvol:', a_1, 'Voltage:', 3.3 * a_1 / 1024):- 읽어온 ADC 값과 계산된 전압 값을 출력합니다.
readvol::- ADC로부터 읽어온 0~1023 사이의 정수 값(10비트).
Voltage::- 실제 전압 값을 계산합니다.
3.3 * a_1 / 1024:- Raspberry Pi의 SPI는 3.3V 전압 레벨을 사용하므로, 최대 값 1023에 해당하는 전압은 3.3V입니다.
- 따라서, ADC 값을 비례식으로 전압으로 변환합니다.
서보모터

서보 모터는 정확한 각도 회전을 위해 사용된다는 점은 스텝 모터와 비슷하지만 구동되는 방식은 전혀 다르다. 서보 모터는 일반적으로 모터 하나만을 가지고 서보라고 하지는 않는다. 서보(Servo)라는 말 자체가 ‘하인’이라는 말에서 유래되어 따르다, 추종하다 등의 의미를 담고 있다고 하여 제어 명령에 따라 정확한 위치와 속도를 맞출 수 있는 모터를 서보 모터라고 한다. (출처:구글)
모터를 '위치시킨다'라는 말이 스텝 모터와의 차이를 나타내는데, 스텝 모터의 경우 원하는 각도만큼 “회전”하기 위해서, 서보는 “위치”시키기 위해서 사용한다.
서보모터는 회전이 불가하고, 최대 180’까지만 돌 수 있다.
제어 방법은 DC 모터와 같은 PWM이지만, 서보 모터의 PWM은 주파수가 정해져 있으며, 듀티비라기보다는 신호의 유지 시간으로 회전 각도가 결정된다.
서보 모터를 제어하기 위해서는 50Hz의 주파수를 가지는 신호가 입력되어야 한다. 즉, 한 주기 당 20ms의 시간을 가진다는 것. 20ms의 주기 안에서 HIGH 신호의 폭(=시간)이 얼마인가에 따라 서보 모터의 회전 각도가 결정된다.
아래와 같이 회로를 구성하고 코드를 적용하면, 서보모터가 움직이는것을 볼 수 있다.

import RPi.GPIO as GPIO
from time import sleep
servoPin = 12
SERVO_MAX_DUTY = 12
SERVO_MIN_DUTY = 3
GPIO. setmode(GPIO.BOARD)
GPIo.setup(servoPin, GPIO.OUT)
servo = GPIO.PWM(servoPin, 50)
servo.start(0)
def setServoPos(degree):
if degree > 180:
degree = 180
duty = SERVO_MIN_DUTY+( degree*(SERVO_MAX_DUTY-SERVO_MIN_DUTY )/180.0)
print("Degree: {} to {}(Duty)". format(degree, duty))
servo. ChangeDutyCycle(duty)
if __name__== "__main__":
setServoPos(0)
sleep(1)
setServoPos (90)
sleep (1)
setServoPos (50)
sleep (1)
setServoPos (120)
sleep (1)
setServoPos ( 180)
sleep (1)
servo.stop()
GPIO.cleanup()GPIO.setmode(GPIO.BOARD):- GPIO 핀 번호 체계를 물리적 보드 핀 번호로 설정합니다.
GPIO.BOARD는 보드의 실제 핀 번호를 사용한다는 뜻입니다.GPIO.setup(servoPin, GPIO.OUT):- 지정한 핀을 출력 모드로 설정합니다.
servo = GPIO.PWM(servoPin, 50):- PWM 객체를 생성하여 서보 모터를 제어합니다.
servoPin에서 PWM 신호를 생성하며, 주파수는 50Hz로 설정합니다.- 일반적인 서보 모터는 50Hz의 PWM 신호를 사용합니다.
servo.start(0):- PWM 출력을 시작합니다.
- 초기 듀티 사이클은 0%로 설정되어 서보 모터는 움직이지 않습니다.
def setServoPos(degree)::- 서보 모터의 각도를 설정하는 함수입니다.
degree는 설정하려는 각도(0~180도)를 의미합니다.if degree > 180:- 입력된 각도가 180도를 초과하면,
degree = 180- 각도를 180도로 제한합니다.
- 서보 모터는 일반적으로 0도에서 180도까지만 회전합니다.
duty = SERVO_MIN_DUTY + (degree * (SERVO_MAX_DUTY - SERVO_MIN_DUTY) / 180.0):- 입력된 각도에 따라 듀티 사이클을 계산합니다.
- 계산 과정:
(SERVO_MAX_DUTY - SERVO_MIN_DUTY) / 180.0:- 각 1도당 듀티 사이클 증가량을 계산합니다.
degree * 증가량:- 원하는 각도에 해당하는 듀티 사이클 변화를 계산합니다.
SERVO_MIN_DUTY + 변화량:- 최소 듀티 사이클에 변화를 더해 최종 듀티 사이클을 결정합니다.
- 이 계산을 통해 매핑합니다.
print("Degree: {} to {}(Duty)".format(degree, duty)):- 설정하려는 각도와 해당 듀티 사이클 값을 출력합니다.
- 디버깅이나 확인을 위해 사용됩니다.
servo.ChangeDutyCycle(duty):- 계산된 듀티 사이클로 PWM 신호를 변경하여 서보 모터의 각도를 조절합니다.
if __name__ == "__main__"::- 이 코드는 해당 스크립트가 직접 실행될 때만 아래의 코드를 실행하도록 합니다.
- 모듈로 임포트될 때는 실행되지 않습니다.
- 서보 모터 동작 시퀀스:
setServoPos(0):- 서보 모터를 0도로 설정합니다.
sleep(1):- 1초 동안 대기합니다.
setServoPos(90):- 서보 모터를 90도로 설정합니다.
sleep(1):- 1초 동안 대기합니다.
setServoPos(50):- 서보 모터를 50도로 설정합니다.
sleep(1):- 1초 동안 대기합니다.
setServoPos(120):- 서보 모터를 120도로 설정합니다.
sleep(1):- 1초 동안 대기합니다.
setServoPos(180):- 서보 모터를 180도로 설정합니다.
sleep(1):- 1초 동안 대기합니다.
servo.stop():- PWM 신호 출력을 중지합니다.
- 서보 모터 제어를 종료합니다.
GPIO.cleanup():- 사용한 GPIO 핀 설정을 초기화합니다.
- 프로그램 종료 시 GPIO 리소스를 깨끗하게 정리하여 다른 프로그램에서 GPIO를 사용할 때 문제를 방지합니다.