Ver2.5 STM32L0로 만드는 초저전력 BLE 비콘, IoT 세상의 문을 열어볼까? 🚀

STM32L0로 만드는 초저전력 BLE 비콘, IoT 세상의 문을 열어볼까? 🚀
배터리 하나로 몇 년을 버티는 마법! 그 비밀을 파헤쳐보자. 코딩 친구, 준비됐어?
안녕! 우리 같이 IoT 세상으로 떠나볼까?
요즘 어딜 가나 'IoT', '사물인터넷' 얘기가 들리지? 맞아, 모든 게 인터넷에 연결되는 세상!
근데 그거 알아? 이 거대한 IoT 세상을 지탱하는 작지만 강력한 주인공이 있다는 거.
바로 '비콘(Beacon)'이라는 녀석이야. 등대처럼 자기 위치나 정보를 끊임없이 주변에 알리는 작은 송신기지.
"음... 비콘? 그거 그냥 블루투스 아니야?" 라고 생각했다면, 반은 맞고 반은 틀려! 😉
우리가 쓸 건 그냥 블루투스가 아니라 'BLE(Bluetooth Low Energy)', 즉 저전력 블루투스야. 이름 그대로 전기를 아~주 조금만 먹는 착한 기술이지. 덕분에 동전만 한 배터리 하나로 몇 달, 심지어 몇 년까지도 거뜬히 버틸 수 있어.
이 글에서는 바로 이 BLE 비콘을, 그것도 '초저전력'에 진심인 MCU, STM32L0 시리즈를 가지고 직접 만들어 볼 거야.
"MCU? STM32? 너무 어려운 거 아니야?" 걱정 마! 내가 친구처럼 옆에서 하나하나 다 알려줄게.
이 글을 다 읽고 나면, 너도 IoT 세상에 네 이름으로 된 작은 등대 하나를 띄울 수 있게 될 거야. 어때, 신나지 않아? 🔥
- C언어 기초는 알지만, 임베디드 세계는 처음인 입문자
- 아두이노는 만져봤는데, 좀 더 '찐' 개발을 해보고 싶은 메이커
- IoT 프로젝트 아이디어는 있는데, 하드웨어 구현이 막막했던 기획자/개발자
- 그냥... STM32랑 BLE가 너무 궁금해서 잠이 안 오는 호기심 천국 너!
자, 그럼 커피 한 잔 타 오고, 마음 단단히 먹고!
STM32L0와 함께하는 초저전력 BLE 비콘 개발 여정, 지금 바로 시작해보자!
1. 기본기 다지기: BLE 비콘, 너 정체가 뭐야?
개발에 뛰어들기 전에, 우리가 만들려는 게 뭔지 정확히 알아야겠지?
'아는 것이 힘이다'라는 말처럼, 기본 개념을 탄탄히 다져두면 나중에 버그가 터져도 당황하지 않고 해결할 수 있어.
1-1. 블루투스 클래식 vs BLE: 뭐가 다른데?
우리가 흔히 쓰는 블루투스 이어폰이나 스피커는 '블루투스 클래식' 기술을 사용해.
얘네들은 음악처럼 대용량 데이터를 실시간으로, 끊김 없이 보내야 하니까 전기를 꽤 많이 먹어.
그래서 매일 충전해줘야 하잖아.
반면에 BLE(Bluetooth Low Energy)는 이름 그대로 '저전력'에 모든 걸 건 녀석이야.
대용량 데이터 전송은 포기하는 대신, 아주 작은 데이터 조각(온도, 습도, 내 위치 등)을 가끔씩, 짧게 보내는 데 특화되어 있어.
비유하자면, 블루투스 클래식은 쉴 새 없이 수다 떠는 친구고, BLE는 "나 여기 있어!"라고 짧게 한 마디만 하고 다시 잠드는 과묵한 친구랄까? 😴
덕분에 전력 소모가 엄청나게 적어서, 비콘 같은 기기에 딱 맞는 거지.
1-2. 비콘의 핵심 동작 원리: '광고(Advertising)'
BLE 기기들은 크게 두 가지 방식으로 통신해. 하나는 스마트폰과 이어폰처럼 1:1로 '연결(Connection)'하는 방식, 다른 하나는 주변에 "나 이런 정보 가지고 있어!"라고 외치기만 하는 '광고(Advertising)' 방식이야.
비콘은 바로 이 '광고' 방식을 사용해.
굳이 누구랑 연결할 필요 없이, 그냥 주기적으로 정해진 신호(Advertising Packet)를 세상에 뿌리는 거야. 마치 라디오 방송국처럼 말이지.
스마트폰이나 다른 BLE 수신기는 이 광고 신호를 '스캔(Scan)'해서 듣고, "아, 근처에 이런 비콘이 있구나!"하고 인식하는 거지.
이 과정에서 서로 연결하고 인증하는 복잡한 절차가 없기 때문에 굉장히 빠르고 효율적이야. 그리고 이게 바로 저전력의 또 다른 비결이기도 해!
1-3. 비콘의 종류: iBeacon vs Eddystone
비콘이 광고 패킷에 어떤 형식으로 정보를 담아 보내느냐에 따라 몇 가지 표준 규격이 있어.
가장 대표적인 두 녀석만 알면 돼.
1. iBeacon (아이비콘) - 애플이 2013년에 발표한, 비콘의 원조 격인 규격이야. - 구조가 아주 단순해. - UUID (Universally Unique Identifier): 이 비콘 그룹의 고유 식별자. "우리는 재능넷 소속 비콘이야!" 같은 느낌. - Major: 그룹 내에서 상위 구분자. "그중에서도 '지식인의 숲' 코너 비콘이지." - Minor: 그룹 내에서 하위 구분자. "그리고 내가 바로 'STM32 개발' 비콘이야." - Tx Power: 1미터 거리에서 측정한 신호 세기. 스마트폰이 이 값을 보고 비콘과의 대략적인 거리를 추측할 수 있어.
2. Eddystone (에디스톤) - 구글이 2015년에 발표한 오픈소스 규격이야. 아이비콘보다 좀 더 똑똑하고 유연해. - 여러 가지 프레임(Frame) 타입을 지원해. - Eddystone-UID: 아이비콘의 UUID/Major/Minor와 비슷한 역할. - Eddystone-URL: 이게 진짜 물건이야! 😲 광고 패킷에 웹사이트 주소(URL)를 담아서 보낼 수 있어. 사용자가 앱 설치 없이도 스마트폰 알림을 통해 바로 웹사이트에 접속하게 만들 수 있지. (Physical Web의 핵심!) - Eddystone-TLM (Telemetry): 비콘 자신의 상태 정보(배터리 전압, 온도, 켜져 있던 시간 등)를 보내. 비콘을 관리할 때 아주 유용해.
우리는 이번 프로젝트에서 구조가 간단하고 이해하기 쉬운 iBeacon을 먼저 만들어 볼 거야.
원리만 이해하면 Eddystone으로 바꾸는 건 식은 죽 먹기니까 걱정 마!
1-4. 우리의 주인공: STM32L0 시리즈
자, 이제 이 모든 걸 구현할 우리의 두뇌, MCU를 소개할게. 바로 STMicroelectronics 사의 STM32L0 시리즈!
STM32는 정말 정말 종류가 많아. 고성능 M 시리즈, 가성비 좋은 F 시리즈 등등...
그중에서도 이름에 'L'이 붙은 애들은 'Low Power', 즉 저전력에 미친(?) 녀석들이야.
STM32L0가 왜 초저전력 비콘에 딱이냐면,
- 다양한 저전력 모드: 그냥 잠자는 게 아니라, 꿀잠(Stop), 겨울잠(Standby), 가사상태(Shutdown) 등 다양한 레벨의 잠자기 모드를 지원해. 우리는 비콘 신호를 한 번 쏘고, 다음 신호를 쏠 때까지 MCU를 'Stop 모드'로 깊이 재워서 전력 소모를 극단적으로 줄일 거야. - 초저전력 주변장치(Peripherals): 일반 모드뿐만 아니라 저전력 모드에서도 동작하는 타이머(LPTIM), ADC, 통신 인터페이스들이 있어서, 잠든 와중에도 최소한의 일은 할 수 있어. - 강력한 생태계: STM32CubeMX라는 그래픽 기반 코드 생성 툴과 STM32CubeIDE라는 통합 개발 환경이 무료로 제공돼. 복잡한 초기 설정을 마우스 클릭 몇 번으로 끝낼 수 있어서 개발이 정말 편해져.
MicroController Unit의 약자야. 작은 컴퓨터라고 생각하면 돼. CPU 코어, 메모리(RAM, Flash), 그리고 GPIO, UART, SPI, I2C 같은 외부 장치와 소통할 수 있는 기능들이 칩 하나에 모두 들어있어. 우리가 작성한 코드가 바로 이 MCU 안에서 돌아가면서 비콘을 제어하게 되는 거지.
기본기 설명은 이 정도로 하고, 이제 진짜 장비 챙겨서 개발 환경을 꾸리러 가보자!
2. 실전 준비: 개발 환경 구축하기
전쟁터에 나가려면 총이랑 총알이 있어야지! 우리도 코딩 전쟁(?)을 시작하기 전에 필요한 장비와 소프트웨어를 준비해야 해.
조금 귀찮을 수 있지만, 첫 단추를 잘 꿰어야 끝까지 순조롭게 갈 수 있으니 차근차근 따라와 봐.
2-1. 하드웨어 준비물 챙기기 🎒
우리가 필요한 하드웨어는 크게 두 가지야. MCU 보드와 BLE 모듈!
1. MCU 보드: NUCLEO-L073RZ - ST에서 만든 개발 보드야. STM32L073RZ라는 MCU가 박혀있고, 바로 PC에 연결해서 코드를 올리고 디버깅할 수 있는 ST-LINK라는 장비까지 포함되어 있어. - 가격도 저렴하고, 아두이노처럼 양쪽에 핀 헤더가 있어서 다른 부품 연결하기도 편해. 입문용으로 이만한 게 없지.
2. BLE 모듈: BlueNRG-MS 기반의 SPI 모듈 (예: X-NUCLEO-IDB05A1) - STM32L0 MCU 자체에는 BLE 기능이 없어. 그래서 BLE 통신을 전문으로 처리해주는 '라디오' 역할의 칩이 필요해. - ST에서는 'BlueNRG'라는 이름의 BLE 칩을 만드는데, 이걸 사용하면 ST의 소프트웨어 라이브러리(X-CUBE-BLE1)와 찰떡궁합이라 개발이 아주 편해. - NUCLEO 보드 위에 바로 끼울 수 있는 '쉴드(Shield)' 형태의 보드(X-NUCLEO-IDB05A1)를 구하면 제일 좋고, 그게 아니더라도 BlueNRG-MS 칩이 달린 작은 모듈을 구해서 점퍼선으로 연결해도 괜찮아. 우리는 SPI라는 통신 방식으로 MCU와 BLE 모듈을 연결할 거야.
3. 기타 - PC와 NUCLEO 보드를 연결할 USB Mini-B 타입 케이블 - (쉴드가 아닌 모듈을 쓸 경우) MCU 보드와 BLE 모듈을 연결할 점퍼 와이어 몇 가닥
물론 요즘에는 MCU와 BLE 기능이 하나의 칩에 합쳐진 'SoC(System on Chip)' 형태의 제품도 많아 (예: nRF52 시리즈, STM32WB 시리즈). 하지만 우리는 학습을 위해 일부러 분리된 구조를 선택했어. MCU의 역할과 BLE 통신의 역할을 명확히 구분해서 이해할 수 있고, SPI 같은 표준 통신 인터페이스를 직접 다뤄보는 좋은 경험이 될 거야.
2-2. 소프트웨어 설치: 우리의 작업실 꾸미기 💻
하드웨어가 준비됐으면 이제 PC에 개발 도구들을 설치할 차례야. 다행히 ST에서 필요한 모든 걸 무료로 제공해줘.
1. STM32CubeIDE 설치 - 이게 바로 우리의 통합 개발 환경(IDE)이야. 코드 편집, 컴파일, 디버깅까지 모든 걸 여기서 해. - ST 공식 홈페이지에 가서 최신 버전을 다운로드하고 설치하면 돼. (회원가입이 필요할 수 있어) - 이클립스(Eclipse) 기반이라 좀 무겁긴 한데, 기능만큼은 막강해.
2. STM32CubeMX (CubeIDE에 포함됨) - 이게 진짜 혁명적인 툴이야. GUI 환경에서 내가 사용할 MCU를 고르고, 원하는 기능(예: "SPI1번 쓸래", "PA5번 핀은 LED 출력으로 할래")을 마우스로 클릭클릭하면, 해당 기능에 필요한 모든 초기화 코드를 자동으로 생성해줘! - 예전에는 데이터시트를 수백 페이지씩 뒤져가며 레지스터 설정을 손으로 다 했는데... 정말 좋은 세상이야. 😭 - STM32CubeIDE를 설치하면 안에 CubeMX 기능이 포함되어 있으니 따로 설치할 필요는 없어.
3. 스마트폰 앱: BLE Scanner - 우리가 만든 비콘이 신호를 잘 쏘고 있는지 확인하려면 '수신기'가 필요하겠지? 우리 주머니 속에 있는 스마트폰이 최고의 수신기야. - 안드로이드는 'nRF Connect for Mobile', 아이폰은 'LightBlue Explorer' 같은 앱을 미리 설치해두자. 주변의 BLE 신호를 스캔해서 자세한 정보를 보여주는 아주 유용한 앱이야.
자, 이제 하드웨어와 소프트웨어 준비가 모두 끝났어.
다음 챕터부터는 진짜 프로젝트를 만들고 코드를 만져볼 거야. 두근거리지? 😉
3. 프로젝트 생성: 첫 삽을 뜨다! (feat. CubeMX)
드디어 코딩의 시간! 하지만 처음부터 새하얀 파일에 `main()`부터 타이핑할 생각하면 막막하지?
걱정 마, 우리에겐 강력한 비서 STM32CubeMX가 있으니까. 이 친구한테 우리가 뭘 할 건지 잘 설명해주면, 귀찮고 복잡한 밑그림은 자기가 알아서 다 그려줄 거야.
3-1. 새 프로젝트 시작하기
1. STM32CubeIDE를 실행하고, `File > New > STM32 Project`를 선택해. 2. 잠시 기다리면 STM32CubeMX 화면이 나타날 거야. 여기서 `MCU/MPU Selector` 탭을 선택. 3. 검색창에 우리가 사용할 MCU 이름인 `STM32L073RZ`를 입력하고, 목록에서 해당 MCU를 선택한 뒤 `Next`를 눌러. 4. 프로젝트 이름을 정해주자. `My_BLE_Beacon` 같은 직관적인 이름이 좋겠지? 다른 설정은 기본값으로 두고 `Finish`를 클릭! 5. "Initialize all peripherals with their default Mode?" 라고 물어보면 `Yes`를 눌러줘. 6. "Open Perspective?" 라고 물어보면 `Yes`를 눌러서 C/C++ 코딩 환경으로 넘어가자.
짜잔! 이제 우리 프로젝트의 뼈대가 만들어졌어. 화면 중앙에는 선택한 MCU의 핀 배치도가 보이고, 왼쪽에는 각종 기능들을 설정할 수 있는 메뉴가 있을 거야.
3-2. 시스템 기본 설정: 클럭과 디버그
MCU가 제대로 뛰려면 심장(클럭)이 필요하고, 우리가 들여다볼 창문(디버그)이 필요해.
- 디버그 설정: 왼쪽 `System Core` 카테고리에서 `SYS`를 클릭. `Debug` 항목을 `Serial Wire`로 변경해줘. 이걸 해줘야 ST-LINK를 통해 MCU 내부를 들여다보고 디버깅할 수 있어. - 클럭 소스 설정: `System Core`에서 `RCC`를 클릭. `High Speed Clock (HSE)`와 `Low Speed Clock (LSE)`를 모두 `Crystal/Ceramic Resonator`로 설정해줘. NUCLEO 보드에는 외부 크리스탈이 달려있어서, 이걸 사용하면 더 정확한 클럭을 만들 수 있어.
이제 `Clock Configuration` 탭으로 이동해봐. 복잡한 다이어그램이 보이지? 겁먹지 마!
우리의 목표는 `HCLK`를 최대 속도인 32MHz로 만드는 거야. CubeMX가 알아서 최적의 값을 찾아주기도 해. `PLLCLK` 소스를 `HSE`로 선택하고, `HCLK` 입력 칸에 `32`를 입력하고 엔터를 치면, 마법처럼 나머지 값들을 자동으로 계산해줄 거야.
MCU의 모든 동작은 클럭 신호에 맞춰서 일어나. 클럭이 빠를수록 연산 속도도 빨라지지. 하지만 그만큼 전력 소모도 커져. 그래서 우리는 평소에는 저속 클럭으로 동작하거나 아예 잠들어 있다가, 필요할 때만 고속 클럭으로 깨어나서 일을 처리하는 전략을 사용할 거야. 이게 바로 저전력 설계의 핵심!
3-3. 주변장치(Peripherals) 설정: SPI와 GPIO
이제 MCU가 외부 세상과 소통할 방법을 정해줄 차례야. 우리는 BLE 모듈과 SPI 통신을 할 거고, 동작 상태를 눈으로 확인하기 위해 LED를 켤 거야.
1. SPI 설정 (BLE 모듈과의 통신 채널) - NUCLEO-L073RZ 보드와 X-NUCLEO-IDB05A1 쉴드의 회로도를 보면, BLE 모듈이 어떤 핀에 연결되어 있는지 알 수 있어. (이런 데이터시트나 회로도 보는 습관을 들이면 정말 좋아!) - `Connectivity` 카테고리에서 `SPI1`을 클릭. `Mode`를 `Full-Duplex Master`로 설정. - `Parameter Settings` 탭에서 설정을 확인하자. 특히 `Clock Prescaler` 값에 따라 통신 속도가 결정되는데, 일단은 16 정도로 무난하게 시작하자. `CPOL`은 `Low`, `CPHA`는 `1 Edge`로 설정해야 BlueNRG-MS 모듈과 타이밍이 맞아. - 이제 핀을 할당해야 해. MCU 핀 그림에서 `PA5`는 `SPI1_SCK`, `PA6`는 `SPI1_MISO`, `PA7`는 `SPI1_MOSI`로 설정될 거야.
2. GPIO 설정 (BLE 모듈 제어 및 LED) - SPI 통신 외에도 BLE 모듈을 리셋하거나, 통신이 가능한지 알려주는 신호를 위한 핀들이 필요해. - `PA0`: `GPIO_EXTI0` (BLE 모듈이 MCU에게 할 말 있을 때 보내는 인터럽트 신호) - `PA1`: `GPIO_Output` (BLE 모듈 리셋 핀) - `PB0`: `GPIO_Output` (SPI Chip Select, CS 핀. 여러 SPI 장치 중 누구와 대화할지 선택하는 핀) - `PA5`: NUCLEO 보드에는 이 핀에 초록색 LED가 연결되어 있어. 우리는 이걸 동작 확인용으로 쓸 거야. 이미 SPI1_SCK로 설정되어 있지만, 나중에 코드에서 GPIO 출력으로도 쓸 수 있어.
핀 설정이 헷갈린다면, NUCLEO 보드와 BLE 쉴드의 매뉴얼을 꼭 참고해!
이런 과정을 통해 하드웨어의 물리적인 연결과 소프트웨어의 논리적인 설정을 일치시키는 거야.
3-4. 미들웨어 추가: X-CUBE-BLE1의 마법
자, 이제 하이라이트야. ST에서 미리 만들어 놓은 BLE 라이브러리 뭉치, 즉 '미들웨어'를 우리 프로젝트에 이식할 거야.
- 왼쪽 `Middleware` 카테고리에서 `X-CUBE-BLE1`을 클릭. - `Platform_Settings` 탭에서 우리가 설정한 핀들을 라이브러리가 인식할 수 있도록 연결해줘야 해. - `SPI_INSTANCE`: `SPI1` - `SPI_CS_PORT`: `GPIOB` - `SPI_CS_PIN`: `GPIO_PIN_0` - `SPI_IRQ_PORT`: `GPIOA` - `SPI_IRQ_PIN`: `GPIO_PIN_0` - `SPI_RESET_PORT`: `GPIOA` - `SPI_RESET_PIN`: `GPIO_PIN_1` - `BLE_Stack_Settings` 탭으로 가서 비콘의 역할을 정해주자. - `Role`: `Peripheral` (비콘은 주변기기 역할) - `GAP Service Settings`에서 `Device Name`을 `MyBeacon`처럼 원하는 이름으로 바꿔주자.
이 설정 몇 번으로, 우리는 복잡한 BLE 스택(프로토콜 덩어리)을 우리 프로젝트에 손쉽게 통합한 거야.
만약 이걸 직접 다 코딩해야 했다면... 아마 이 글은 책 한 권 분량이 되었을지도 몰라. 😱
3-5. 코드 생성!
모든 설정이 끝났어. 이제 오른쪽 상단의 톱니바퀴 모양 아이콘(Generate Code)을 클릭하자.
CubeMX가 우리가 설정한 내용을 바탕으로 C 코드를 자동으로 쫙~ 생성해줄 거야.
코드가 생성되면 STM32CubeIDE의 C/C++ 편집 화면으로 돌아오게 돼.
왼쪽 `Project Explorer`를 보면 `Core`, `Drivers`, `Middlewares` 등 여러 폴더와 파일들이 생긴 걸 볼 수 있어.
특히 `Core/Src/main.c` 파일을 열어봐. `main()` 함수 안에 `MX_GPIO_Init()`, `MX_SPI1_Init()` 같은 함수들이 보이지?
이게 바로 CubeMX가 우리를 위해 만들어준 초기화 코드들이야.
CubeMX가 생성한 코드를 보면 `/* USER CODE BEGIN ... */` 과 `/* USER CODE END ... */` 라는 주석이 곳곳에 보일 거야. 우리가 작성할 코드는 반드시 이 주석 사이에 넣어야 해. 왜냐하면, 나중에 CubeMX 설정을 변경하고 코드를 다시 생성하면, 이 주석 바깥에 있는 내용은 싹 다 날아가고 새로 덮어 써지거든! 이 주석 안에 쓴 코드만 안전하게 보호돼. 명심 또 명심!
후... 큰 산을 하나 넘었어. 이제 우리에겐 잘 차려진 밥상이 있어.
다음 챕터에서는 이 밥상 위에 우리가 원하는 '비콘'이라는 맛있는 요리를 올려볼 거야. 진짜 코딩은 지금부터 시작이야!
4. 핵심 코드 작성: 비콘에 생명을 불어넣자!
자, 이제 진짜 개발자의 시간이 왔어. CubeMX가 차려준 밥상에 숟가락을 얹을 시간! 🥄
우리의 목표는 iBeacon 규격에 맞는 광고(Advertising) 데이터를 만들고, BLE 모듈을 통해 주기적으로 세상에 뿌리는 거야. 그리고 그 사이사이에는 MCU를 깊은 잠에 빠뜨려 전력을 아낄 거지.
주로 작업할 파일은 `Core/Src/main.c` 와 `Middlewares/ST/STM32_BlueNRG_MS/App/app_bluenrg_ms.c` 두 개가 될 거야.
4-1. iBeacon 광고 데이터 준비하기
먼저, iBeacon이 어떤 모양으로 데이터를 보내는지 다시 한번 떠올려보자.
iBeacon Advertising Packet Structure
- Flags (3 bytes): BLE 모드의 일반적인 설정. (LE General Discoverable Mode, BR/EDR Not Supported)
- Manufacturer Specific Data (26 bytes):
- Length (1 byte): 뒤따라올 데이터의 길이 (25)
- Type (1 byte): 'Manufacturer Specific Data' 타입임을 알림 (0xFF)
- Company ID (2 bytes): Apple, Inc. (0x004C)
- Beacon Type (2 bytes): iBeacon임을 알림 (0x0215)
- UUID (16 bytes): 비콘 그룹의 고유 ID
- Major (2 bytes): 그룹 내 상위 구분자
- Minor (2 bytes): 그룹 내 하위 구분자
- Measured Power (1 byte): 1m 거리에서의 수신 신호 강도 (RSSI)
이 구조에 맞춰서 우리만의 비콘 데이터를 C언어 배열 형태로 만들어야 해.
`app_bluenrg_ms.c` 파일 맨 위, `/* USER CODE BEGIN PV */` 와 `/* USER CODE END PV */` 사이에 아래 코드를 추가하자.
/* USER CODE BEGIN PV */
// iBeacon 데이터 구조 정의
uint8_t beacon_adv_data[] = {
// Flags
0x02, 0x01, 0x06,
// Manufacturer Specific Data
0x1A, // Length
0xFF, // Type
0x4C, 0x00, // Company ID (Apple)
0x02, 0x15, // Beacon Type
// UUID (예: 00112233-4455-6677-8899-AABBCCDDEEFF)
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
// Major (예: 1)
0x00, 0x01,
// Minor (예: 2)
0x00, 0x02,
// Measured Power (예: -59 dBm)
0xC5 // 2's complement of -59
};
/* USER CODE END PV */
응, 맞아! UUID는 온라인 UUID 생성기 같은 툴을 사용해서 너만의 고유한 값을 만드는 게 좋아. Major와 Minor 값도 0~65535 사이에서 원하는 값으로 자유롭게 설정할 수 있어. 이 값들을 조합해서 "이건 내 방에 있는 비콘", "이건 거실에 있는 비콘"처럼 구분할 수 있는 거지.
Measured Power 값은 어떻게 정해?
`0xC5`는 -59의 2의 보수 표현이야. 이 값은 실제 비콘과 스마트폰을 1m 거리에 두고, 스마트폰 앱에서 측정된 RSSI 값을 참고해서 정하는 게 가장 정확해. 일단은 예시 값을 그대로 사용해도 괜찮아.
4-2. BLE 스택 초기화 및 광고 시작하기
이제 `app_bluenrg_ms.c` 파일 안에 있는 `MX_BlueNRG_MS_Init(void)` 함수를 찾아서 수정해야 해.
CubeMX가 기본적으로 '연결 가능한' 모드로 코드를 생성하는데, 비콘은 '연결이 필요 없는' 광고만 하면 되거든.
`MX_BlueNRG_MS_Init` 함수 내부를 잘 살펴보면 `aci_gap_set_discoverable()` 이라는 함수 호출 부분이 있을 거야.
이 부분을 과감하게 지우거나 주석 처리하고, 그 자리에 우리가 만든 iBeacon 데이터를 광고하도록 하는 코드를 넣어줄 거야.
`/* USER CODE BEGIN MX_BlueNRG_MS_Init_2 */` 와 `/* USER CODE END MX_BlueNRG_MS_Init_2 */` 사이를 아래와 같이 수정하자.
/* USER CODE BEGIN MX_BlueNRG_MS_Init_2 */
// 1. 기존의 연결 가능한 광고 설정은 주석 처리 또는 삭제
/*
ret = aci_gap_set_discoverable(ADV_IND,
(ADV_INTERVAL_MIN_MS*1000)/625, (ADV_INTERVAL_MAX_MS*1000)/625,
PUBLIC_ADDR, NO_WHITE_LIST_USE,
sizeof(local_name), local_name, 0, NULL, 0, 0);
if (ret != BLE_STATUS_SUCCESS) {
printf("Error while setting discoverable mode.\n");
}
*/
// 2. Public 주소 설정 (선택사항이지만 권장)
ret = aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, bdaddr);
if(ret != BLE_STATUS_SUCCESS){
printf("Setting BD_ADDR failed.\n");
}
// 3. 비연결 모드로 설정 (중요!)
ret = aci_gap_set_non_discoverable();
if (ret != BLE_STATUS_SUCCESS) {
printf("Error while setting non-discoverable mode.\n");
}
// 4. 우리가 만든 iBeacon 데이터를 광고 패킷으로 설정
ret = aci_gap_update_adv_data(sizeof(beacon_adv_data), beacon_adv_data);
if (ret != BLE_STATUS_SUCCESS) {
printf("Error while updating advertising data.\n");
}
printf("BLE Beacon Initialized and Advertising...\n");
/* USER CODE END MX_BlueNRG_MS_Init_2 */
코드를 잠깐 분석해볼까? - `aci_hal_write_config_data()`: BLE 모듈의 고유 주소(MAC 주소 같은 거)를 설정해. - `aci_gap_set_non_discoverable()`: "나랑 연결하려고 하지 마, 난 그냥 내 얘기만 할 거야!" 라고 선언하는 거야. 비콘의 핵심 설정이지. - `aci_gap_update_adv_data()`: 이게 바로 주인공! 우리가 위에서 정의한 `beacon_adv_data` 배열을 실제 광고 데이터로 등록하는 함수야.
`aci_` 로 시작하는 함수들은 BlueNRG-MS BLE 스택이 제공하는 API(Application Programming Interface)야.
우리는 이 함수들을 호출해서 BLE 모듈을 제어하는 거지.
4-3. 첫 컴파일 및 테스트!
자, 이제 첫 테스트 시간이야. 떨리지?
1. NUCLEO 보드를 PC에 USB 케이블로 연결해. 2. STM32CubeIDE 상단 메뉴에서 망치 모양 아이콘(Build)을 클릭해서 컴파일을 진행해. 에러가 없는지 확인! 3. 초록색 재생 버튼 모양 아이콘(Run)을 클릭하면, 코드가 컴파일되고 NUCLEO 보드로 업로드될 거야. 4. 업로드가 완료되면 코드가 바로 실행돼.
이제 미리 설치해둔 스마트폰의 BLE 스캐너 앱(nRF Connect 등)을 켜고 스캔을 시작해봐.
두근두근... 잠시 후, 우리가 설정한 `MyBeacon` 이라는 이름 또는 알 수 없는 기기가 목록에 나타날 거야!
해당 기기를 눌러서 상세 정보를 보면, `Manufacturer Specific Data` 항목에 우리가 입력했던 UUID, Major, Minor 값이 그대로 들어있는 걸 확인할 수 있어.
🎉 축하해! 너는 방금 너의 첫 번째 BLE 비콘을 만드는 데 성공했어! 🎉
하지만... 아직 끝이 아니야. 지금 이 비콘은 전기를 펑펑 쓰고 있어.
MCU가 잠시도 쉬지 않고 `while(1)` 루프를 돌고 있거든. 이제 이 비콘을 진정한 '초저전력' 비콘으로 만드는 마법을 부려볼 시간이야.
4-4. 저전력의 핵심: Stop 모드 적용하기
우리의 전략은 이래: 1. 비콘 광고를 시작한다. (BLE 모듈은 알아서 주기적으로 광고를 쏨) 2. MCU는 할 일이 없으니 깊은 잠(Stop 모드)에 빠진다. 3. (선택사항) 일정 시간 후에 깨어나서 할 일이 있는지 확인하고, 없으면 다시 잠든다.
비콘은 한 번 광고를 시작하라고 명령해두면 BLE 모듈이 알아서 계속 광고를 하기 때문에, MCU는 사실상 아무것도 할 필요가 없어.
그래서 그냥 무한정 재워도 돼.
`main.c` 파일의 `main()` 함수 안으로 가보자. `while(1)` 루프를 찾아서 그 안에 저전력 코드를 추가할 거야.
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// BlueNRG 스택의 이벤트 처리
// 이 함수를 주기적으로 호출해줘야 BLE 스택이 제대로 동작해.
// 하지만 비콘 모드에서는 이벤트가 거의 발생하지 않아.
// 그럼에도 불구하고 안정성을 위해 호출해주는 것이 좋아.
// 실제로는 Stop 모드 진입 전에 한 번만 호출해도 충분할 수 있어.
// 여기서는 루프 안에 두어, 만약 예기치 않은 이벤트로 깨어났을 때 처리하도록 함.
MX_BlueNRG_MS_Process();
// 이제 MCU를 Stop 모드로 진입시켜 전력 소모를 최소화한다.
// WFI (Wait For Interrupt) 명령어로 인터럽트가 발생할 때까지 잠든다.
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}
/* USER CODE END 3 */
이 함수는 STM32L0의 저전력 모드로 진입시키는 HAL 라이브러리 함수야.
- `PWR_LOWPOWERREGULATOR_ON`: Stop 모드에 들어가면서 내부 전압 레귤레이터도 저전력 모드로 전환하라는 의미야. 전력을 더 아낄 수 있지. - `PWR_STOPENTRY_WFI`: 'Wait For Interrupt'의 약자. 어떤 인터럽트 신호가 들어올 때까지 MCU의 대부분 클럭을 멈추고 깊은 잠에 빠지라는 명령어.
그럼 언제 깨어나?
어떤 인터럽트(예: 핀 상태 변화, 타이머 알람 등)가 발생하면 MCU는 Stop 모드에서 깨어나. 우리 코드에서는 BLE 모듈이 `PA0` 핀을 통해 인터럽트를 발생시킬 수 있지만, 비연결 광고 모드에서는 그런 이벤트가 거의 없어. 그래서 사실상 거의 깨어나지 않고 계속 잠들어 있게 돼. 이게 바로 초저전력의 비결!
자, 이 코드를 추가하고 다시 빌드해서 업로드해봐.
스마트폰 앱에서는 아까와 똑같이 비콘 신호가 잡힐 거야. 겉보기엔 아무 차이가 없지.
하지만 내부적으로는 엄청난 변화가 일어났어.
MCU는 이제 수십 mA(밀리암페어)를 소모하던 상태에서, 수 uA(마이크로암페어) 수준으로 전력 소모가 뚝 떨어졌을 거야.
만약 전력 측정 장비(전류계, Power Profiler 등)가 있다면 직접 측정해보는 것도 정말 재밌는 경험이 될 거야. 아마 깜짝 놀랄걸!
이것으로 우리는 STM32L0의 핵심 기능인 '초저전력 모드'를 활용하여, 배터리 하나로 1년 이상도 거뜬히 동작할 수 있는 진짜 저전력 BLE 비콘을 완성했어.
5. 레벨 업: 더 똑똑한 비콘 만들기 & 문제 해결
기본적인 비콘 만들기에 성공했다면, 이제 몇 가지 응용 기술과 문제 해결 팁을 알아볼 시간이야.
이것까지 마스터하면 "나 STM32로 BLE 좀 만져봤어"라고 자신 있게 말할 수 있을걸? 😉
5-1. Eddystone-URL 비콘으로 변신!
iBeacon은 앱이 설치되어 있어야만 그 신호를 해석해서 의미 있는 동작을 할 수 있어.
하지만 구글의 Eddystone-URL 규격을 사용하면, 앱 설치 없이도 사용자에게 웹사이트 링크를 바로 전달할 수 있지.
방법은 간단해. 광고 데이터의 내용만 바꿔주면 돼!
`app_bluenrg_ms.c`에 정의했던 `beacon_adv_data` 배열을 아래와 같이 수정해보자.
// Eddystone-URL 데이터 구조 정의
uint8_t eddystone_adv_data[] = {
// Flags
0x02, 0x01, 0x06,
// Service Data - Eddystone UUID
0x03, 0x03, 0xAA, 0xFE,
// Eddystone-URL Frame
0x13, // Length of this frame
0x16, // Type: Service Data
0xAA, 0xFE, // Eddystone UUID
0x10, // Frame Type: URL
0x00, // Tx Power
// URL Data (예: https://www.jaenung.net)
0x03, // URL Scheme: https://
'j', 'a', 'e', 'n', 'u', 'n', 'g',
0x08 // .net/
};
URL을 그대로 넣는 게 아니라, 공간을 아끼기 위해 약속된 규칙에 따라 인코딩해야 해.
- `0x03`: `https://` 를 의미 - `0x08`: `.net/` 를 의미 - `jaenung` 부분은 아스키코드 값 그대로 들어가.
이런 식으로 `http://www.`, `.com`, `.org` 등 자주 쓰이는 문자열은 한 바이트 코드로 축약해서 표현해. 자세한 내용은 구글의 Eddystone 공식 문서를 참고하면 좋아.
이제 `aci_gap_update_adv_data()` 함수에 `beacon_adv_data` 대신 `eddystone_adv_data`를 넣어주면, 우리 비콘은 이제 아이비콘이 아니라 '재능넷' 주소를 뿌리는 Eddystone-URL 비콘으로 변신하게 돼!
안드로이드폰에서는 주변에 이런 비콘이 있으면 알림을 띄워주기도 해. (Nearby 기능)
5-2. 동적 데이터 광고하기 (예: 배터리 전압)
지금까지는 고정된 데이터만 광고했어. 하지만 진짜 IoT 기기라면 주변 환경이나 자신의 상태(센서 값, 배터리 잔량 등)를 광고해야겠지?
STM32L0에는 ADC(Analog to Digital Converter)라는 기능이 있어서 아날로그 전압을 숫자로 읽을 수 있어.
이를 이용해 배터리 전압을 측정하고, 그 값을 비콘의 Minor 값에 담아서 광고하는 코드를 만들어보자.
1. CubeMX에서 ADC 설정 - CubeMX를 다시 열고 `Analog > ADC`를 선택. - `IN0` (PA0 핀) 같은 채널 하나를 활성화. (실제 배터리 전압을 측정하려면 전압 분배 회로를 구성하고 해당 핀에 연결해야 해) - `Parameter Settings`에서 `Resolution`은 `12 bits`, `Data Alignment`는 `Right alignment` 등으로 설정.
2. 코드 수정 (`main.c`) - `main()` 함수 안, `while(1)` 루프가 시작되기 전에 ADC를 켜고 캘리브레이션을 진행하는 코드를 추가해.
/* USER CODE BEGIN 2 */
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 100); // 첫 변환은 안정화를 위해 버릴 수 있음
/* USER CODE END 2 */
- `while(1)` 루프 안에서 주기적으로 ADC 값을 읽고, 그 값을 광고 데이터에 업데이트한 뒤, 다시 광고를 시작하는 로직을 구현해야 해.
- 이 경우에는 계속 잠들어 있으면 안 되겠지? 타이머 인터럽트를 사용해서 일정 시간(예: 10초)마다 깨어나도록 만들어야 해.
3. 저전력 타이머(LPTIM)로 주기적으로 깨어나기 - CubeMX에서 `Timers > LPTIM1`을 활성화하고, 클럭 소스를 `LSE`로 설정. - `NVIC Settings` 탭에서 LPTIM1 전역 인터럽트를 활성화. - `main.c`에서 LPTIM을 시작하는 코드를 추가하고, 인터럽트 콜백 함수(`HAL_LPTIM_CompareMatchCallback`) 안에 ADC 값을 읽고 광고 데이터를 업데이트하는 코드를 넣는 거야. - 그리고 `while(1)` 루프에서는 다시 `HAL_PWR_EnterSTOPMode()`를 호출해서 잠들면 돼. 타이머가 울리면 MCU가 자동으로 깨어나서 인터럽트 콜백 함수를 실행하고, 그게 끝나면 다시 루프의 시작으로 돌아가 잠들게 되는 거지.
이건 좀 더 고급 주제라 모든 코드를 다 보여주긴 어렵지만, 이런 흐름으로 구현한다는 것만 알아둬도 큰 도움이 될 거야.
혹시 구현하다 막히는 부분이 있다면, **재능넷** 같은 플랫폼에서 STM32 전문가에게 도움을 요청하는 것도 좋은 방법이야!
5-3. 흔히 겪는 문제와 해결 팁 (Troubleshooting)
- 비콘이 스캔되지 않아요! 😭 - 하드웨어 연결 확인: 가장 먼저 SPI 핀(SCK, MISO, MOSI, CS)과 제어 핀(RESET, IRQ) 연결이 정확한지 다시 확인해봐. 점퍼선이 헐겁게 꽂히진 않았는지도 체크! - CubeMX 핀 설정 확인: 코드 생성 시 설정한 핀과 실제 하드웨어 연결이 일치하는지 확인. - 클럭 설정 확인: HSE가 제대로 설정되었는지, PLL 설정이 올바른지 `Clock Configuration` 탭을 다시 한번 보자. - BLE 스택 초기화 확인: `MX_BlueNRG_MS_Init()` 함수가 `main()`에서 제대로 호출되고 있는지, 함수 내부에서 에러를 반환하지는 않는지 `printf` 디버깅을 통해 확인해봐.
- 코드가 업로드되지 않아요! - ST-LINK 드라이버 확인: PC에 ST-LINK 드라이버가 제대로 설치되었는지 장치 관리자에서 확인해봐. - 보드 연결 확인: USB 케이블이 데이터 전송이 가능한 케이블인지, 보드에 전원이 제대로 들어오는지 (LED 점등 확인) 체크. - CubeIDE 디버그 설정 확인: `Run > Debug Configurations` 메뉴에서 디버거가 `ST-LINK`로 제대로 설정되어 있는지 확인.
- 저전력 모드에 들어갔는데 전류 소모가 여전히 높아요! - 사용하지 않는 주변장치 끄기: CubeMX에서 설정했지만 코드에서 사용하지 않는 페리페럴(UART, I2C 등)이 있다면, 해당 페리페럴의 클럭을 꺼주는 코드를 추가하면 전력 소모를 더 줄일 수 있어. (`__HAL_RCC_USART1_CLK_DISABLE()` 등) - GPIO 핀 상태 확인: 사용하지 않는 모든 GPIO 핀은 `Analog` 모드로 설정하는 것이 전력 소모에 가장 유리해. 공중에 떠 있는(Floating) 입력 핀은 누설 전류를 유발할 수 있어. - 디버거 연결 해제: ST-LINK 디버거가 연결된 상태에서는 MCU가 완벽한 저전력 모드로 진입하지 못하는 경우가 많아. 정확한 저전력 소모를 측정하려면 코드를 업로드한 뒤, ST-LINK 연결을 해제하고 보드에 외부 전원만 공급한 상태에서 측정해야 해.
문제가 생겼을 때 가장 좋은 방법은 중간중간에 상태를 출력해서 눈으로 확인하는 거야. CubeMX에서 `USART` 하나를 활성화하고, `printf` 함수를 리다이렉션해서 사용하면 ST-LINK의 가상 COM 포트를 통해 PC 터미널 프로그램(PuTTY, Tera Term 등)으로 로그를 쉽게 볼 수 있어. "BLE Init Start", "BLE Init Success", "Entering Stop Mode" 같은 로그를 찍어보면 어디서 문제가 발생하는지 파악하기 훨씬 쉬워져.
6. 여정을 마치며: 이제 당신도 IoT 개발자!
와, 정말 긴 여정이었어. 여기까지 따라오느라 정말 고생 많았어! 👏
우리는 함께 BLE 기술의 기본 원리를 배우고, STM32L0라는 강력한 MCU를 다루는 법을 익혔어.
STM32CubeMX로 복잡한 초기 설정을 순식간에 해치우고, C 코드를 이용해 iBeacon 데이터를 직접 만들어 세상에 뿌려봤지.
그리고 가장 중요한 것! 그냥 비콘이 아니라, STM32L0의 심장인 '초저전력 기술'을 적용해서 배터리 걱정 없는 진짜 효율적인 비콘을 완성했어.
이제 당신은 단순한 코드 사용자가 아니야. 하드웨어의 특성을 이해하고, 그 성능을 최대한으로 끌어내는 방법을 아는 임베디드 개발자의 길에 첫발을 내디딘 거야.
오늘 만든 이 작은 비콘은 시작일 뿐이야.
여기에 다양한 센서를 붙이면?
- 온습도 센서를 붙이면 스마트 농장이나 창고 관리 시스템이 될 수 있고, - 가속도 센서를 붙이면 물건의 움직임이나 충격을 감지하는 자산 추적 시스템이 될 수 있어. - 조도 센서나 가스 센서를 붙이면 스마트 홈이나 안전 관리 시스템의 일부가 될 수도 있지.
가능성은 무한해. 당신의 상상력이 바로 다음 프로젝트의 청사진이야.
두려워하지 말고 계속해서 새로운 것에 도전해봐. 오늘 배운 지식과 경험이 든든한 발판이 되어줄 거야.
IoT 세상의 문은 이제 막 열렸어. 그 문을 열고 들어갈 주인공은 바로 너야!
앞으로의 멋진 프로젝트들을 기대하며, 너의 개발 여정을 항상 응원할게! 화이팅! 💪
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.

댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.