무엇을 만드나
더운 날 운동장에 나가면 스스로는 잘 못 느끼는 사이에 몸이 힘들어집니다. 땀이 나는데도 계속 뛰고, 그늘에 앉을 때를 놓칩니다. 이번에 만들 것은 「지금 쉬어 갈 때가 되었다」를 손목에서 알려 주는 밴드입니다.
| 하고 싶은 일 | 운동하는 동안 쉬어 갈 때를 놓치지 않게 하고 싶다. |
| 무엇을 읽나 | 「힘들다」는 못 읽는다. 대신 주변의 온도와 습도를 읽는다. 습도가 높으면 땀이 잘 마르지 않아 더 힘들어지기 때문이다. → 온습도 센서 |
| 어떻게 판단하나 | 온도와 습도를 함께 보아 0부터 3까지 단계를 매긴다. 값이 흔들리므로 여러 번 읽어 가운데 값을 쓴다. |
| 무엇으로 답하나 | 밴드의 색으로 늘 보여 주고, 단계가 올라가는 그 순간에만 짧게 소리를 낸다. |
| 틀릴 수 있는 자리 | 읽는 것은 주변 공기이지 내 몸이 아니다. 같은 자리에 있어도 사람마다 힘든 정도가 다르다. |
몸의 값을 다루는 두 선을 여기서 지킨다
이 밴드의 판단은 단계를 고르는 데까지이고, 건강 상태를 해석하지 않습니다. 「지금 이곳의 공기가 이런 단계다」를 알릴 뿐입니다. 그래서 붉은빛의 뜻을 「위험하다」가 아니라 「쉬어 가는 것을 생각해 볼 때」로 정하고, 설명 카드에도 그렇게 적습니다.
또 이 밴드는 아무 기록도 밖으로 보내지 않습니다. 읽은 값은 판단에 쓰고 버립니다. 누가 언제 어디에 있었는지 남지 않습니다.
준비물과 연결
| 부품 | 신호선을 꽂을 핀 | 몸에 차는 것이라 달라지는 점 |
|---|---|---|
| 빛 띠 | GP0 |
알을 넷만 쓴다. 손목에 두르려면 길면 안 된다 |
| 온습도 센서 | GP22 |
피부에 닿지 않게 바깥쪽을 향해 붙인다. 몸에 닿으면 체온을 읽는다 |
| 버저 | GP15 |
아주 짧게만 쓴다. 소리는 주변에도 들린다 |
「몸에 차는 것이라 달라지는 점」 열이 몸에 차기 때문에 달라지는 부분입니다. 벽에 거는 작품이라면 신경 쓰지 않아도 될 것들이 몸에 차는 순간 전부 문제가 됩니다.
센서가 피부에 닿으면 어떤 값이 읽힐까요? 그리고 그 값으로 판단하면 어떤 일이 벌어질지 말해 보세요.
흔들림 다루기
책상 위에서는 값이 얌전합니다. 손목에 차고 뛰면 같은 자리에서도 값이 오르내립니다. 손이 몸에 가까워졌다 멀어지고, 바람이 스쳤다 말고, 팔을 들었다 놓기 때문입니다.
다섯 번 읽어 25 · 28 · 29 · 30 · 33이 나왔다고 합시다. 평균을 내면 29입니다. 그런데 33 대신 50이 튀어 들어왔다면 평균은 32.4가 됩니다. 값 하나가 결과를 통째로 끌고 갑니다.
가운데 값은 다릅니다. 33이 50으로 바뀌어도 가운데는 여전히 29입니다. 크기순으로 늘어놓고 한가운데 것을 고르므로, 튄 값이 한두 개라면 양쪽 끝에서 튄 값(이상치)은 저절로 버려집니다.
def middle(values):
"""읽은 값들을 크기순으로 늘어놓고 가운데 것을 고른다.
몸에 닿은 센서는 조금만 움직여도 값이 튄다.
가장 크게 튄 값과 가장 작게 튄 값을 자연스럽게 버리는 방법이다.
"""
ordered = sorted(values)
return ordered[len(ordered) // 2]sorted(values)는 값을 작은 것부터 늘어놓은 새 목록을 만들어 줍니다. 원래 목록은 그대로 둡니다.len(ordered) // 2는 가운데 자리의 번호입니다. 다섯 개면5 // 2가 2가 되고, 번호는 0부터 세므로 셋째 값을 가리킵니다.- 홀수 개를 읽어야 가운데가 하나로 딱 정해집니다. 그래서 다섯 번입니다.
가운데 값이 늘 나은 것은 아니다
값이 정말로 빠르게 오르고 있을 때는 가운데 값이 오히려 늦게 따라옵니다. 갑자기 뜨거워진 상황을 한 박자 늦게 알아채는 것입니다. 흔들림을 잠재우는 것과 빨리 알아채는 것은 서로 맞바꾸는 관계입니다.
이 밴드에서는 흔들림을 잠재우는 쪽을 골랐습니다. 몇 초 늦어도 되는 일이고, 잘못 울리는 알림이 훨씬 성가시기 때문입니다.
빨리 알아채는 것이 더 중요한 장치를 하나 떠올려 보세요. 그런 장치라면 흔들림을 어떻게 다루어야 할까요?
단계 매기기
습도 하나만 보고 색을 정할 수도 있지만, 이번에는 값이 둘입니다. 온도만 봐도 안 되고 습도만 봐도 안 됩니다. 같은 30도라도 습도가 높으면 훨씬 힘듭니다. 땀이 마르면서 몸을 식혀 주는데, 습도가 높으면 잘 마르지 않기 때문입니다.
둘을 함께 보려면 하나의 숫자로 합쳐야 합니다. 여기서는 아주 단순하게 합치겠습니다.
def level_of(temperature, humidity):
"""온도와 습도를 함께 보고 0부터 3까지의 단계를 매긴다.
이 셈은 수업에서 원리를 확인하려고 우리가 지어낸 것이다.
공식 지표가 아니므로 건강을 판단하는 근거로 쓰면 안 된다.
"""
score = temperature + (humidity - 40) * 0.1
if score < 27:
return 0
if score < 30:
return 1
if score < 33:
return 2
return 3이 셈은 우리가 지어낸 것이다
temperature + (humidity - 40) * 0.1이라는 식은
수업에서 원리를 확인하려고 만든 것이지 공식 지표가 아닙니다. 실제로
쓰이는 지표는 훨씬 복잡하고, 나라와 기관마다 기준도 다릅니다.
그래도 이 식에서 배울 것이 있습니다. 이 식은 「습도 10퍼센트가 온도 1도만큼 힘들게 한다」고 정한 셈입니다. 이 0.1이라는 숫자에 우리의 결정이 담겨 있고, 바꾸면 밴드의 성격이 달라집니다.
| 온도 | 습도 | 합친 값 | 단계 |
|---|---|---|---|
| 28도 | 40% | 28 + 0 = 28.0 | 1단계 — 슬슬 더움 |
| 28도 | 80% | 28 + 4 = 32.0 | 2단계 — 조심. 온도는 같은데 단계가 올랐다 |
| 32도 | 30% | 32 − 1 = 31.0 | 2단계 — 조심. 건조하면 조금 낫다고 본 셈이다 |
| 32도 | 75% | 32 + 3.5 = 35.5 | 3단계 — 쉬어 가기 |
28도 · 80% 줄과 32도 · 30% 줄을 비교해 보세요. 온도가 4도나 차이 나는데 같은 단계가 나왔습니다. 이것이 「둘을 함께 본다」는 말의 뜻입니다. 이 결과가 그럴듯한지 아닌지를 실제로 차 보고 해석해서 0.1을 고쳐 나가면 됩니다.
자주 고칠 숫자는 맨 위에 모은다
이 프로그램에서 READ_EVERY · SAMPLES ·
COUNT처럼 대문자 이름을 맨 위에 모아 두었습니다.
고칠 일이 생기면 맨 위만 보면 되기 때문입니다.
단계를 나누는 27 · 30 · 33도 같은 이유로 위로 뺄 수 있습니다. 지금은 식과 함께 읽는 편이 이해하기 쉬워서 그대로 두었습니다. 이런 결정도 만드는 사람의 몫입니다.
식의 0.1을 0.3으로 바꾸면 밴드는 어떻게 달라질까요? 「합친 값」을 계산한 표의 28도 · 80% 줄로 직접 계산해 보고 답해 보세요.
완성과 조정
이제 전체를 이어 붙입니다. 알리는 방식을 먼저 정합니다. 「높은 값이 이어질 때」 울리게 할 수도 있지만, 여기서는 「단계가 올라갈 때」 울립니다.
# 손목에 차는 「더위 조심 밴드」.
# 온도와 습도를 함께 보고 조심할 단계를 매겨 색으로 알린다.
# 단계가 올라간 그 순간에만 짧게 한 번 소리를 낸다.
#
# 빛 띠 -> GP0
# 온습도 센서 -> GP22
# 버저 -> GP15
from machine import Pin, PWM
import neopixel
import dht
import time
DATA_PIN = 0
SENSOR_PIN = 22
BUZZER_PIN = 15
COUNT = 4 # 밴드에는 알을 적게 쓴다
READ_EVERY = 10 # 몇 초마다 읽을지 - 자주 읽으면 전지가 빨리 닳는다
SAMPLES = 5 # 한 번 판단할 때 몇 번 읽을지
COLORS = (
(0, 30, 20), # 0단계 - 편안함
(40, 30, 0), # 1단계 - 슬슬 더움
(60, 20, 0), # 2단계 - 조심
(70, 0, 0), # 3단계 - 쉬어 가기
)
strip = neopixel.NeoPixel(Pin(DATA_PIN), COUNT)
sensor = dht.DHT11(Pin(SENSOR_PIN))
buzzer = PWM(Pin(BUZZER_PIN))
def middle(values):
"""읽은 값들을 크기순으로 늘어놓고 가운데 것을 고른다.
몸에 닿은 센서는 조금만 움직여도 값이 튄다.
가장 크게 튄 값과 가장 작게 튄 값을 자연스럽게 버리는 방법이다.
"""
ordered = sorted(values)
return ordered[len(ordered) // 2]
def read_steady():
"""여러 번 읽어 흔들림이 적은 온도와 습도를 얻는다."""
temperatures = []
humidities = []
for _ in range(SAMPLES):
sensor.measure()
temperatures.append(sensor.temperature())
humidities.append(sensor.humidity())
time.sleep(1.5)
return middle(temperatures), middle(humidities)
def level_of(temperature, humidity):
"""온도와 습도를 함께 보고 0부터 3까지의 단계를 매긴다.
이 셈은 수업에서 원리를 확인하려고 우리가 지어낸 것이다.
공식 지표가 아니므로 건강을 판단하는 근거로 쓰면 안 된다.
"""
score = temperature + (humidity - 40) * 0.1
if score < 27:
return 0
if score < 30:
return 1
if score < 33:
return 2
return 3
def paint(color):
"""밴드의 알을 모두 한 색으로 물들인다."""
for i in range(COUNT):
strip[i] = color
strip.write()
def beep():
"""짧게 한 번 소리를 낸다."""
buzzer.freq(660)
buzzer.duty_u16(1500)
time.sleep(0.15)
buzzer.duty_u16(0)
last_level = 0
while True:
temperature, humidity = read_steady()
level = level_of(temperature, humidity)
paint(COLORS[level])
if level > last_level: # 올라갈 때만 알린다
beep()
last_level = level
time.sleep(READ_EVERY)| 코드 | 하는 일 |
|---|---|
if level > last_level: |
지난번보다 단계가 올라갔을 때만 울린다. 그대로이거나 내려갔으면 조용하다. |
last_level = level |
이번 단계를 다음번의 비교 대상으로 남겨 둔다. 이 줄이 없으면 늘 0과 비교하게 되어, 1단계 이상인 동안 읽을 때마다 계속 울린다. 0단계일 때는 울리지 않는다. |
이 방식의 좋은 점은 3단계에 오래 머물러도 한 번만 울린다는 것입니다. 아쉬운 점은 3단계가 계속되는데도 다시 알려 주지 않는다는 것입니다. 무엇을 고를지는 쓰는 사람이 어떤 상황인지에 달렸습니다.
시험해 보는 법
- 먼저
level_of()만 따로 시험합니다. 코드를 보드에 올리기 전에, 「합친 값」을 계산한 표의 줄마다 손으로 계산해 값이 맞는지 확인합니다. - 보드에 올려 현재 단계를 화면에 찍어 봅니다. 색과 단계가 맞게 짝지어졌는지 봅니다.
- 단계를 억지로 올려 봅니다. 손을 오므려 센서를 감싸고 입김을 불면 온도와 습도가 함께 오릅니다.
- 소리가 한 번만 났는지 확인합니다. 여러 번 났다면
먼저
last_level을 갱신하는 줄이 있는지 봅니다. 줄이 있는데도 울린다면 값이 단계의 경계 근처를 오가며 내려갔다 올라가기를 반복하는 것입니다. - 기다리는 시간이 길어 답답하면
READ_EVERY와SAMPLES를 잠깐 줄이고, 확인이 끝나면 되돌려 놓습니다.
해 보기 — 내 밴드로 고치기
- 단계를 넷이 아니라 셋으로 줄여 보세요. 손목에서 색을 구별하기가 더 쉬워집니다. 대신 무엇을 잃게 되나요?
- 3단계가 10분 넘게 이어지면 한 번 더 알리도록 고쳐 보세요. 3단계가 몇 번 이어졌는지 세는 변수를 하나 두면 됩니다.
- 소리를 아예 빼고 빛이 천천히 깜빡이게 바꿔 보세요. 운동장에서는 소리가 잘 안 들리기도 합니다.
- 설명 카드를 만듭니다. 무엇을 읽는지, 무엇은 읽지 않는지, 기록을 남기지 않는다는 것을 적습니다.
「기록을 남기지 않는다」는 항목을 빠뜨리지 마세요. 몸에 차는 물건을 남에게 건넬 때 「이것이 나에 대해 무엇을 아는가」는 누구나 궁금해합니다.
last_level = level 줄을 지우면 어떤 일이 벌어질까요? 코드를 따라가며
말로 설명해 보세요.
핵심 정리
무엇을 만드나
- 운동 중에 쉬어 갈 때를 놓치지 않게 알려 주는 손목 밴드다.
- 읽는 것은 주변 공기이지 내 몸이 아니다. 단계를 고를 뿐 건강을 해석하지 않는다.
- 몸에 차는 순간 알의 개수 · 센서의 방향 · 소리의 길이가 전부 문제가 된다.
흔들림 다루기
- 몸에 차고 움직이면 센서의 값이 튄다. 여러 번 읽어 가운데 값을 쓴다.
- 평균은 튄 값 하나에 통째로 끌려가지만 가운데 값은 버틴다.
- 흔들림을 잠재우는 것과 빨리 알아채는 것은 맞바꾸는 관계다.
단계 매기기
- 값이 둘이면 하나의 숫자로 합쳐 판단한다.
- 합치는 식은 수업용으로 지어낸 것이다. 공식 지표가 아니다.
- 식 안의 0.1에 우리의 결정이 담겨 있다. 바꾸면 밴드의 성격이 달라진다.
완성과 조정
- 단계가 올라갈 때만 알리면 오래 머물러도 한 번만 울린다.
- 대신 계속되는데도 다시 알리지 않는 아쉬움이 남는다.
- 몸에 차는 물건에는 「무엇을 아는가」를 밝히는 설명 카드가 필요하다.
확인 퀴즈
배운 내용을 확인해 봅시다.
다섯 번 읽어 가운데 값을 쓰는 까닭은?
같은 28도인데 습도가 높을 때 단계가 오르는 까닭은?
last_level = level 줄을 지우면?
이 밴드의 판단 식에 대해 바르게 말한 것은?