본문 바로가기

MicroPython

Chapter 5 - 신호등 제어기

길거리에서 흔히 발견되는 신호등에서도 마이크로컨트롤러가 사용됩니다.

신호등의 불빛은 타이머, 교통량 등에 따라 조절 됩니다.

 

피코를 이용하여 간단한 신호등 제어기를 만들어 보겠습니다.

 

준비물 :

빨간색, 주황색, 주황색 LED 각각 1개씩

330 옴 저항 3개,

Active 피에조 부저 (piezoelectric buzzer) 1개

M2M 점퍼 와이어 다수

 


간단한 신호등(A simple traffic light)

 

서킷구성

1) 빨간색, 주황색, 초록색 LED를 꽂는다. 이때 긴 다리(anode)는 GPIO와 연결 시킨다. 반대편 짧은 다리(cathode)는 ground (38번핀) 와 연결시킨다.

 

2) 330옴 저항 3개는 LED 긴 다리 앞쪽에 위치 시킨다. LED에 들어가는 전류를 줄이기 위해 저항을 사용한다. 그렇지 않으면 LED가 과전류로 인해 타버림.

 

3) GP15, GP14, GP13는 저항과 연결시켜준다.

 

소스코드

 

import machine
import utime

led_red = machine.Pin(15, machine.Pin.OUT) # 빨간색 LED를 GP15와 연결
led_amber = machine.Pin(14, machine.Pin.OUT) # 주황색 LED를 GP14와 연결
led_green = machine.Pin(13, machine.Pin.OUT) # 초록색 LED를 GP13과 연결

while True:
	led_red.value(1) # 빨간색 5초 동안 점등
	utime.sleep(5)
    
	led_amber.value(1) # 주황색 2초 동안 점등
	utime.sleep(2)
    
	led_red.value(0) # 빨간색, 주황색 끄고 초록색 5초 동안 점등
	led_amber.value(0)
	led_green.value(1)
	utime.sleep(5)
    
	led_green.value(0) # 초록색 끄고, 주황색 5초 동안 점등
	led_amber.value(1)
	utime.sleep(5)
    
	led_amber.value(0) # 주황색 소등

 

위의 코드를 Traffic_Lights.py로 저장하고 실행하면 아래 순서로 무한 반복 실행됩니다.

 

정지신호 빨간색 점등 -> 경고신호 주황색 점등 -> 빨간색, 주황색 꺼지면서 진행신호 초록색 점등 -> 초록색 꺼지고 주황색 점등 -> 주황색 꺼짐

 

실제 신호등 체계에서는 초록색이 더 길게 점등되겠죠? 여기 예제에서는 짧게 5초만 점등하였습니다.

 


개선된 신호등

 

보행자가 그리 많지 않은 곳에서는 '보행자작동신호기'가 운영되고 있습니다. 보행자작동신호기 (naver.com)

 

평소에는 보행자 신호등에 불이 켜지지 않고 차량의 교통만 컨트롤 하다가 보행자가 버튼을 누를 경우에만 보행자 신호등에 녹색 신호가 켜지는 것입니다.

 

이번 예제는 위의 예제에서 '보행자작동신호기' 역할을 하는 버튼과 부저를 추가한 예제 입니다.

 

준비물 :

 

위 예제의 구성품 + 부저 + 스위치 버튼

 

서킷구성

 

1) 스위치 버튼의 한쪽 다리에 GP16을 다른 다리에 3.3 V 전원을 연결한다. 3.3.V 전원은 36번 핀을 통해 공급됨.

2) 부저는 GP12와 접지에 연결한다.

 

소스코드

 

import machine
import utime
import _thread

led_red = machine.Pin(15, machine.Pin.OUT)
led_amber = machine.Pin(14, machine.Pin.OUT)
led_green = machine.Pin(13, machine.Pin.OUT)
button = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_DOWN) # 스위치 버튼은 GP16 연결. INPUT, 풀다운 모드(평상시 0볼트)
buzzer = machine.Pin(12, machine.Pin.OUT) # 부저는 GP12 연결. OUTPUT

global button_pressed # 전역 변수
button_pressed = False # 스위치 버튼 기본값은 False

def button_reader_thread(): #사용자 정의 함수
    global button_pressed
    while True:
        if button.value() == 1: # 스위치 버튼이 눌리면
        	button_pressed = True # 값을 True로 변경
        utime.sleep(0.01) # 0.01초 대기
        
_thread.start_new_thread(button_reader_thread, ())
    
while True:
    if button_pressed == True: # sub-thread에서 button_pressed 값이 True로 변경되면
        led_red.value(1)
        for i in range(10): # 부저 울리기 10번 반복
            buzzer.value(1) 
            utime.sleep(0.2)
            buzzer.value(0)
            utime.sleep(0.2)
        global button_pressed
        button_pressed = False # button_pressed 값 초기화
    led_red.value(1)
    utime.sleep(5)
    led_amber.value(1)
    utime.sleep(2)
    led_red.value(0)
    led_amber.value(0)
    led_green.value(1)
    utime.sleep(5)
    led_green.value(0)
    led_amber.value(1)
    utime.sleep(5)
    led_amber.value(0)

 

소스코드가 위의 예제와 크게 다르지 않습니다.

 

평소에는 빨간불 5초 -> 주황불 2초 -> 초록불 5초 -> 주황불 5초 순서로 무한 반복되다가 스위치 버튼이 눌리면 특정한 동작을 수행하도록 구성됩니다.

 

import _thread

 

이를 위해 우선 _thread 모듈을 불러 옵니다. _thread 모듈의 상세한 설명은 아래 링크 참조

17.9. _thread — Low-level threading API — Python 3.5.9 documentation

 

17.9. _thread — Low-level threading API — Python 3.5.9 documentation

17.9. _thread — Low-level threading API This module provides low-level primitives for working with multiple threads (also called light-weight processes or tasks) — multiple threads of control sharing their global data space. For synchronization, simple

docs.python.org

 

_thread : multiple thread를 구동하기 위한 모듈. 간단한 process(task) 동작 용도. global 전역 변수를 통해 core끼리 데이터를 공유할 수 있음. RP2040 마이크로컨트롤러는 dual core 이므로 동시에 2개까지 동작 가능.

 

global button_pressed
button_pressed = False

 

다음으로 button_pressed 전역 변수를 설정합니다. 각 thread는 서로 독립적이라 local 변수가 공유되지 않습니다. 하지만 global 변수 설정을 통해 thread간 정보를 주고 받을수 있습니다. 스위치 버튼이 눌리지 않았을 때의 초기값은 False로 설정합니다.

 

def button_reader_thread(): #사용자 정의 함수
    global button_pressed
    while True:
        if button.value() == 1: # 스위치 버튼이 눌리면
        	button_pressed = True # 값을 True로 변경
        utime.sleep(0.01) # 0.01초 대기

 

sub-thread에서 실행될 사용자 정의 함수 'button_reader_thread를 구성합니다. 함수 내에서 이미 정의된 전역변수를 사용하려면 다시 global button_pressed로 선언해 줍니다. while True를 통해 0.01초 간격으로 무한 반복하면서 스위치 버튼이 눌렸는지 모니터링을 합니다.

 

_thread.start_new_thread(button_reader_thread, ())

 

이 줄을 통해 sub-thread가 실행됩니다. start_new_thread 메서드에서는 실행할 함수를 argument로 입력하고 (button_reader_thread), 그 함수에서 필요한 argument를 입력하게 되어 있습니다. 이미 정의된 button_reader_thread는 argument가 필요하지 않으므로 ()로 입력하였습니다.

 

sub-thread와는 별개로 병렬적으로 main-thread는 while True 구문이 실행됩니다.

 

코드를 실행시키면 신호등이 정해진 순서에 맞게 점등 됩니다. 스위치 버튼이 눌리면 sub-thread를 통해 값이 True로 변경이 되고 다음번 main-thread loop에서 button_pressed 값이 True인지 검사하는 if 구문을 통해 buzzer를 0.2초 간격으로 10번 울리는 동작이 수행 됩니다.

 

도전과제

 

1) 보행 신호를 좀 더 길게 변경하기

2) 다른 스위치 버튼을 추가하여 보행자작동신호기 추가하기