제조공정
| uECG 디바이스 | 1개 |
| Arduino Nano R3 | 1개 |
| Adafruit NeoPixel 링: WS2812 5050 RGB LED | 1개 |
| 리튬이온 배터리 1000mAh | 1개 |
수년간 내 심박동에 맞춰 깜빡이는 LED 작품을 만들고 싶었습니다(완전히 가만히 있을 때뿐만 아니라, 간혹 박동이 건너뛰어도). 근데 이게 놀랍게도 어렵더군요. 수년간 시도하고 실패했습니다. 하지만 이제 가능해졌습니다!
사실 무거운 작업은 uECG가 다 합니다 – 작고 착용 가능한 오픈소스 ECG 디바이스로, 아두이노 친화적인 출력 핀이 있어(각 심박동마다 이 핀이 High/Low로 바뀜). 이 핀 상태 처리하는 게 ECG 신호 처리보다 훨씬 쉽고, 최대한 활용해봤습니다.
업데이트: 무선 링크로 데이터 수신하는 이 프로젝트 2번째 버전도 확인해보세요.
디지털 신호만 다루니 매우 단순합니다. 하지만 착용형으로 쓰려면 대부분 연결을 납땜하는 게 훨씬 신뢰성 높고(작아짐) – 빠른 테스트엔 필요 없지만 격렬한 활동 중 착용하려면 강력 권장.

리포 배터리를 직접 5V 입력으로 썼습니다 – 실수 아닙니다. Vin에 연결하면 안정 작동 안 함(Vin 전압 레귤레이터가 전압 강하 일으켜 여기서 절대 용납 안 됨). 아두이노는 입력 전압 3.4V 이상이면 안정적. 리포 완충 시 4.2V, 3.4V까지 떨어지려면 15% 미만 충전 상태. ~200mAh 이상 배터리면 괜찮은 작동 시간 확보. 배터리는 커넥터로 연결해 주기적 충전 가능하게 하세요.
프로그램 작동 방식: D3 핀 지속 읽기, 변화 감지 시 변경 시간 20개 요소 배열에 저장. 첫 번째와 마지막 요소 차이를 20으로 나누면 박동당 평균 시간(밀리초). 1분(60000ms)을 이 숫자로 나누면 BPM 값. 배열 요소 수 조정 가능. 요소 수 적으면 반응 빠르지만 결과 불안정(박동 감지 문제 시 BPM 큰 변동). 요소 수 많으면 데이터 안정적이나 BPM 급변 시 반응 느림.
BPM을 색상(낮음→높음: 파랑→초록→노랑→분홍→빨강)과 켜지는 LED 수에 매핑: 80 BPM이면 8개 세그먼트 점등, 110이면 11개 등(스케일도 코드에서 조정 가능).
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
// LED 링 DI 핀
#define PIN 11
// 링 픽셀 수
#define NUMPIXELS 16
// uECG 연결용 입력 핀
int in_pin = 3;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
pixels.begin();
pinMode(in_pin, INPUT);
digitalWrite(in_pin, 1); // 풀업 활성화: 중요! uECG에 내부 풀업 없음
}
// 최근 20회 심박 저장해 평균 BPM 계산
// 값 높을수록 신뢰도 ↑, 하지만 BPM 급변 시 출력 변화 감지 느려짐
#define BEAT_HIST 20
long beats[BEAT_HIST];
void push_beat(long ms) {
for(int x = 0; x < BEAT_HIST-1; x++) beats[x] = beats[x+1];
beats[BEAT_HIST-1] = ms;
}
int get_bpm() {
long dt = beats[BEAT_HIST-1] - beats[0];
long bpm = BEAT_HIST * 60000 / dt;
return bpm;
}
long last_pix_upd = 0;
int prev_in_state = 0;
void loop() {
long ms = millis();
int in_state = digitalRead(in_pin); // 박동 미감지 시 1, 감지 시 0
if (in_state == 1 && prev_in_state == 0) {
push_beat(ms);
}
prev_in_state = in_state;
if (ms - last_pix_upd > 10) {
int r, g, b;
last_pix_upd = ms;
int bpm = get_bpm();
int max_bright = 120; // 최대 밝기, 최대 255. 항상 최대로 할 필요 없음
float dd = 20; // 색상 톤 간 BPM 변화폭 (파랑→초록→노랑→분홍→빨강)
float t1 = 90, t2, t3, t4; // t1: "기준" BPM, 이하면 파랑
t2 = t1 + dd;
t3 = t2 + dd;
t4 = t3 + dd;
// 현재 BPM이 t1~t4 중 어디 구간에 있는지 따라 색상 변경
if (bpm < t1) { r = 0; g = 0; b = max_bright; }
else if (bpm < t2) { r = 0; g = max_bright * (bpm-t1)/dd; b = max_bright - g; }
else if (bpm < t3) { r = max_bright * (bpm-t2)/dd; g = max_bright - r; b = r/4; }
else if (bpm < t4) { r = max_bright; g = 0; b = max_bright/2 - max_bright * (bpm-t3)/(2*dd); }
else { r = max_bright; g = 0; b = 0; }
if (in_state) { // 박동 감지 중 아닐 때 1/4 밝기, 박동만 하이라이트
r *= 0.25; g *= 0.25; b *= 0.25;
}
int on_pixels = (bpm+5)/10; // 사용 LED 수: 60 BPM → 6개, 120 → 12개 등
for(int i=0; i
아두이노를 링 안에 넣으면 크기가 거의 완벽하게 맞습니다. 배터리도 근처에 배치. uECG는 가슴에 부착하므로 커넥터 달린 와이어 필요 – 먼저 uECG 부착, 셔츠 입은 후 다른 부품 착용, 마지막으로 커넥터 연결. 안 그러면 착용 매우 불편 – 믿으세요, 해봤습니다 ㅋㅋ



제대로 했으면 모든 커넥터 연결 후 30초 내로 깜빡이며 BPM 표시 시작.
걷기와 달리기 중 테스트 – 달리기 중 배터리가 ECG 센서 위로 튕겨 올라가 읽기 왜곡. 위치를 좀 옮기니 uECG-아두이노 연결 와이어가 너무 짧아 걸음마다 ECG 센서 당겨 읽기 왜곡. 전반적으로 걷기와 정지 시엔 신뢰성 있는 박동 감지, 달리기 땐 안 됨 – 개선 예정. 센서 자체는 다른 셔츠 입을 때 달리기 중에도 BPM 정확히 표시(앱으로 확인).
가슴에 LED가 멋져 보일 순 있지만 실용성 없음. 고개 숙여 맥박 확인 매우 불편. 다음 버전에선 손목 밴드 형태로 박동 표시 예정.
P.S. uECG 프로젝트 관심 있으시면 hackaday 페이지 참고 – 기술 세부사항, PCB 설계, 토론, 프로젝트 로그 많음.
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 11
#define NUMPIXELS 16
int in_pin = 3;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
pixels.begin();
pinMode(in_pin, INPUT);
digitalWrite(in_pin, 1);
}
#define BEAT_HIST 20
long beats[BEAT_HIST];
void push_beat(long ms) {
for(int x = 0; x < BEAT_HIST-1; x++) beats[x] = beats[x+1];
beats[BEAT_HIST-1] = ms;
}
int get_bpm() {
long dt = beats[BEAT_HIST-1] - beats[0];
long bpm = BEAT_HIST * 60000 / dt;
return bpm;
}
long last_pix_upd = 0;
int prev_in_state = 0;
void loop() {
long ms = millis();
int in_state = digitalRead(in_pin);
if (in_state == 1 && prev_in_state == 0) push_beat(ms);
prev_in_state = in_state;
if (ms - last_pix_upd > 10) {
int r, g, b;
last_pix_upd = ms;
int bpm = get_bpm();
int max_bright = 120;
float dd = 20;
float t1 = 90, t2, t3, t4;
t2 = t1 + dd; t3 = t2 + dd; t4 = t3 + dd;
if (bpm < t1) { r = 0; g = 0; b = max_bright; }
else if (bpm < t2) { r = 0; g = max_bright * (bpm-t1)/dd; b = max_bright - g; }
else if (bpm < t3) { r = max_bright * (bpm-t2)/dd; g = max_bright - r; b = r/4; }
else if (bpm < t4) { r = max_bright; g = 0; b = max_bright/2 - max_bright * (bpm-t3)/(2*dd); }
else { r = max_bright; g = 0; b = 0; }
if (in_state) { r *= 0.25; g *= 0.25; b *= 0.25; }
int on_pixels = (bpm+5)/10;
for(int i=0; i

제조공정
부품 및 소모품 아두이노 우노 (Arduino UNO) 1개 스파크펀 단일 리드 심박 모니터 - AD8232 1개 HC-05 블루투스 모듈 1개 스파크펀 납땜용 브레드보드 - 미니 1개 수/암 점퍼 와이어 15개 필요 도구 및 장비 전기 테이프 글루건 프로젝트 개요 영감 뉴스를 보던 중 연간 사망자 수 조사 결과를 보고, 환자가 심장의 전기적 이상을 조기에 감지해 즉시 의사와 연락할 수 있는 장치를 만들기로 결심했습니다. 심장의 전기적 활동에 대해 충분히 조사한 후, 착용 가능하고 누구나
로봇이나 장난감 자동차를 설계하려면 모터에 대한 지식이 필수적입니다. 이 모터들을 제어하는 매우 비용 효율적이고 쉬운 방법은 L293D 드라이버 모터 실드를 사용하는 것입니다. 더 나아가 아두이노 같은 마이크로컨트롤러와 인터페이스해 사용하면 좋습니다. 여러 종류 중 L293D 모터 드라이버는 가장 인기 있는 칩셋 중 하나입니다. 서보, 스테퍼, DC 모터 등 다양한 전자 제작에 응용되기 때문입니다. 하지만 그 핵심 특징은 두 개의 독립적인 DC 모터의 방향과 속도 제어가 가능하다는 점입니다. 이에 대해 더 자세히 알아보겠습니다.