아두이노 나노 33 IoT 휴대용 날씨 스테이션: 블린크 연동
부품 및 소모품
| 아두이노 나노 33 IoT | 1개 |
| BME280 날씨 센서 (온도/습도/기압) | 1개 |
| 0.96인치 OLED 디스플레이 (128×64) | 1개 |
| 플라스틱 케이스 (3.9×2.7인치) | 1개 |
| 마이크로 USB 케이블 | 1개 |
| 수-암 브레드보드 점퍼 와이어 | 1세트 |
| USB 벽면 충전기 | 1개 |
필요한 도구
- 글루건 (에폭시 권장: 여름철 실외 설치 시)
앱 및 온라인 서비스
- 아두이노 IDE
- Blynk (IoT 플랫폼, 취미용 무료)
프로젝트 소개
스마트폰과 IoT 기기 연결 앱 여러 가지 써봤지만 실망스러웠는데, Blynk는 달랐습니다. 유연하고 UI 예쁘고, 안드로이드/iOS 모두 지원, 다양한 IoT 플랫폼 호환, 취미용 무료! 이 프로젝트로 Blynk 탐구해 봤습니다.
하드웨어
구성 단순: 나노 33 IoT(와이파이 내장) + BME280 센서(온도/습도/기압 일체형) + 1인치 OLED. 모두 3.3V 구동. OLED는 선택 사항이지만, Blynk 연동 전 디버깅용으로 아주 유용합니다.
회로도: 센서와 OLED 모두 I2C 인터페이스. 이론상 한 I2C 버스로 여러 기기 가능하지만 라이브러리 충돌로 두 개의 I2C 포트 사용.
- 센서: 기본 I2C (아날로그 핀 A4=SDA, A5=SCL)
- OLED: 보조 I2C (디지털 핀 D4=SCL, D5=SDA) — 순서 주의: SDA가 D5
투명 전면 플라스틱 케이스에 수납 (와이파이용 RF 투과). 센서는 바닥 1/2인치 구멍 바로 안쪽에 위치해 외부 공기 노출. 핫글루로 고정 (실외 여름 설치 시 에폭시 권장). 전원은 USB 케이블 하나로 프로그래밍/구동 겸용.
소프트웨어
라이브러리 3개 (아두이노 라이브러리 매니저에서 설치):
Adafruit_BME280_Library (센서)
ss_oled (OLED)
Blynk (블린크)
각 라이브러리 예제로 따로 테스트해 보면 이해 빠릅니다.
Blynk 예제는 ESP8266 기준이라 나노 33 IoT용으로 수정 필요:
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
나노 33 IoT는 WiFiNINA 사용. 또한 #include <avr/dtostrf.h>로 센서 숫자값을 OLED 표시용 문자열로 변환.
Blynk는 문자열/숫자 둘 다 표시 가능하지만, 그래프(SuperChart) 위젯은 숫자 타입만 작동합니다. 처음에 문자열로 보냈더니 그래프 안 그려져 고생함.
기압 해수면 보정: 고도 1000피트(≈305m)당 약 1인치Hg(≈33.9hPa) 상승. 코드에서 1.3 더해 보정 (작성자 고도 1300피트). 본인 고도에 맞게 수정하세요. 정확한 보정표 온라인 참조.
블린크 없이 날씨 스테이션만 쓰기
코드 내 Blynk.begin() 줄 주석 처리하면 OLED 단독으로 동작. (주석 안 풀면 블린크 연결 대기하며 멈춤)
블린크 연동하기
1. 블린크 시작하기
앱 설치 → 계정 생성 → 새 프로젝트 → 인증 토큰 이메일 수신. 하드웨어 'Arduino Nano 33 IoT' 선택 (없으면 'Other' → WiFiNINA). 기본 예제 Blynk Blink로 온보드 LED(D13) 제어 테스트 후 본 프로젝트 진행 권장.
2. 스마트폰 앱 위젯 구성
- 편집 모드(우상단 아이콘) → 좌측 스와이프로 위젯 툴박스 열기
- Labeled Value 위젯 3개 추가 (Displays 섹션)
| 변수 | 가상 핀 | 라벨 형식 | 색상 | 소수점 |
| 온도 | V3 | Temp /pin.#/ °F | 녹색 | 1자리 |
| 습도 | V4 | Humid /pin.#/ % | 금색 | 1자리 |
| 기압 | V5 | Press /pin.##/ inHg | 빨간색 | 2자리 |
라벨의 /pin.#/이 실시간 값 자리. 위젯 늘려 전체 텍스트 보이게. 새로고침 간격 'Push' 모드 (아두이노가 1분마다 푸시).
가상 핀(Virtual Pin): 하드웨어 핀이 아닌 변수에 번호 붙여 앱에서 참조. 코드에서 Blynk.virtualWrite(V3, ftemp) 등으로 전송.
3. 슈퍼차트(SuperChart) 그래프 추가
- SuperChart 위젯 추가 → 아래로 늘려 화면 채우기
- 설정: X축 표시, 해상도: Live, 1hr, 6hr, 1day, 1wk, 1mo, 3mo
- 데이터 스트림 3개: V3(온도), V4(습도), V5(기압)
- Y축 스케일: 온도 67~100, 습도 34~66, 기압 0~33 (화면 3등분)
- 색상 라벨값과 맞춤, Y축 표시 켜기
4. 프로젝트 공유
편집 모드 → 상단 중앙 'Project Settings' → Sharing 켜기 → 링크 생성 → QR코드 공유. 상대방은 블린크 앱만 있으면 계정 없이도 보기 가능.
블린크 '에너지' 포인트: 위젯/공유 시 소모. 최초 2500포인트 무료 (이 프로젝트 충분). 추가 프로젝트 시 $6에 5000포인트 구매 가능.
코드
Tiny Internet Weather Station
/* Arduino Nano 33 IoT + BME280 + 0.96" OLED + Blynk */
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BME280.h>
#include <avr/dtostrf.h>
#include <ss_oled.h>
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
char auth[] = "YourBlynkProjectCode";
char ssid[] = "YourWifiID";
char pass[] = "YourWiFiPassword";
Adafruit_BME280 bme;
Adafruit_Sensor *bme_temp = bme.getTemperatureSensor();
Adafruit_Sensor *bme_pressure = bme.getPressureSensor();
Adafruit_Sensor *bme_humidity = bme.getHumiditySensor();
#define SDA_PIN 5
#define SCL_PIN 4
#define RESET_PIN -1
#define OLED_ADDR -1
#define FLIP180 0
#define INVERT 0
#define USE_HW_I2C 0
#define MY_OLED OLED_128x64
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
SSOLED ssoled;
BlynkTimer timer;
void setup() {
if (!bme.begin(0x76)) { while (1) delay(10); }
bme_temp->printSensorDetails();
bme_pressure->printSensorDetails();
bme_humidity->printSensorDetails();
oledInit(&ssoled, MY_OLED, OLED_ADDR, FLIP180, INVERT, USE_HW_I2C, SDA_PIN, SCL_PIN, RESET_PIN, 400000L);
oledFill(&ssoled, 0x0, 1);
Blynk.begin(auth, ssid, pass); // 블린크 없이 쓸 땐 이 줄 주석 처리
timer.setInterval(60000L, myupdate); // 1분마다 업데이트
myupdate(); // 초기 1회 실행
}
void loop() {
Blynk.run();
timer.run();
}
void myupdate() {
sensors_event_t temp_event, pressure_event, humidity_event;
bme_temp->getEvent(&temp_event);
bme_pressure->getEvent(&pressure_event);
bme_humidity->getEvent(&humidity_event);
float ctemp = temp_event.temperature;
float ftemp = 32 + (9 * ctemp) / 5;
float hum = humidity_event.relative_humidity;
float ppress = pressure_event.pressure; // hPa
float mpress = 1.3 + ppress / 33.8939; // inHg + 고도 보정 (본인 값으로 변경)
char mytemp[8], myhum[8], mypress[8];
dtostrf(ftemp, 6, 2, mytemp);
dtostrf(hum, 6, 2, myhum);
dtostrf(mpress, 6, 2, mypress);
// OLED 업데이트
oledWriteString(&ssoled, 0, 2, 1, (char *)"Weather Station", FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 2, 3, (char *)"Temp = ", FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 50, 3, (char *)mytemp, FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 105, 3, (char *)"F", FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 2, 5, (char *)"Humid=", FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 50, 5, (char *)myhum, FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 105, 5, (char *)"%", FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 2, 7, (char *)"Press=", FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 50, 7, (char *)mypress, FONT_NORMAL, 0, 1);
oledWriteString(&ssoled, 0, 105, 7, (char *)"in", FONT_NORMAL, 0, 1);
// 블린크 푸시
Blynk.virtualWrite(V3, ftemp);
Blynk.virtualWrite(V4, hum);
Blynk.virtualWrite(V5, mpress);
}
회로도