4.1.3. BLE 연결
블루투스 기기 Scan
Python에서 바나나 체온계를 스캔하고 연결하기 위해 필요한 모듈들을 import해야 한다. main.py 파일에 다음과 같이 모듈들을 import 한다.
import asyncio # 비동기 프로그래밍을 위한 모듈
from typing import Optional # 타입 힌트를 위한 모듈
from bleak import BleakScanner, BleakClient # Bluetooth Low Energy(BLE) 통신을 위한 모듈이 모듈들은 각각 특정 기능을 제공한다.
asyncio
비동기 프로그래밍을 지원하는 모듈이다.
BLE 장치와의 통신은 시간이 걸릴 수 있으므로, 비동기 처리를 통해 프로그램의 효율성을 높인다.
typing (Optional)
Python에 타입 힌트를 추가하기 위한 모듈이다.
코드의 가독성을 높이고 잠재적인 오류를 미리 발견할 수 있게 도와준다.
bleak (BleakScanner, BleakClient)
Bluetooth Low Energy (BLE) 장치와 통신하기 위한 라이브러리이다.
BleakScanner: BLE 장치를 검색(스캔)하는 데 사용된다.
BleakClient: 검색된 BLE 장치에 연결하고 데이터를 주고받는 데 사용된다.
모듈을 import한 후, 프로그램의 메인 부분을 작성한다. Python은 스크립트가 직접 실행될 때 특별한 동작을 수행하도록 하기 위해 다음과 같은 조건문을 사용한다.
이 조건문은 '이 파일이 직접 실행되는 경우에만 아래 코드를 실행하라'는 의미이다. 이렇게 함으로써 이 파일이 다른 프로그램에 의해 모듈로 import될 때는 실행되지 않고, 직접 실행될 때만 원하는 동작을 수행하게 할 수 있다.
비동기 처리를 위해 현재 실행중인 이벤트 루프를 가져온다.
주변의 BLE 기기를 Scan하기 위해 scan 함수를 정의한다.
위 코드는 Bluetooth Low Energy (BLE) 장치를 스캔하는 비동기 함수이다. 주요 기능은 다음과 같다:
ts100_device리스트를 초기화하여 발견된 TS100 장치들을 저장할 준비를 한다.스캔 시작을 알리는 메시지를 출력한다.
BleakScanner.discover()를 사용하여 주변의 BLE 장치들을 비동기적으로 스캔한다.스캔 종료를 알리는 메시지를 출력한다.
스캔된 각 장치에 대해:
장치 이름이 없으면 "Unknown Device"로 설정한다.
장치 이름에 "TS100"이 포함되어 있으면:
해당 장치를
ts100_device리스트에 추가한다.장치 이름을 출력한다.
발견된 TS100 장치들의 리스트를 반환한다. 이 함수는 비동기(
async)로 정의되어 있어, 스캔 과정이 완료될 때까지 다른 작업을 수행할 수 있게 해준다. 이는 BLE 스캔이 시간이 걸릴 수 있는 작업이기 때문에 효율적인 방법이다.
비동기 이벤트 루프를 사용해서 scan 함수를 호출하여 scan 함수가 종료될 때 까지 기다린다.
지금까지의 코드를 실행하면 콘솔에 scan된 바나나 체온계 기기를 확인할 수 있다.
다음은 지금까지의 전체 코드이다.
블루투스 기기 선택
스캔된 BLE(Bluetooth Low Energy) 기기의 정보를 사용자에게 시각적으로 보여주기 위해, GUI(그래픽 사용자 인터페이스)를 만들 필요가 있다. 이를 위해 Python의 표준 GUI 라이브러리인 tkinter를 사용한다. tkinter는 Python에 기본으로 포함된 GUI 툴킷이다. 별도의 설치 없이 바로 사용할 수 있다. 'tk'라는 별칭으로 import하는 것이 일반적이다.
tkinter의 주요 기능은 다음과 같다.
애플리케이션의 메인 창 생성
버튼, 레이블, 텍스트 박스 등 다양한 UI 요소 제공
마우스 클릭, 키보드 입력 등의 이벤트 처리
다양한 레이아웃 관리 기능으로 위젯 배치
이렇게 tkinter를 사용함으로써, 사용자는 터미널에서 텍스트로만 정보를 보는 것이 아니라, 시각적으로 더 이해하기 쉽고 상호작용이 가능한 형태로 BLE 기기 정보를 확인할 수 있다.
Scan된 device를 확인할 수 있는 show_scan_devices() 함수를 추가한다. Scan된 device가 화면에 버튼 형태로 나타나고, 버튼을 클릭했을 때 button_click() 함수가 호출되어 화면이 사라지는 형태로 구현되었다.
위 코드를 실행하면 그림과 같이 화면에 Scan된 기기를 확인할 수 있고, 연결하고자 하는 Device를 선택하면 화면이 종료된다.
지금까지의 내용이 반영된 코드이다.
블루투스 기기 연결
Scan된 device를 담는 전역변수 selected_device를 추가한다.
button_click() 함수에 연결하고자 하는 바나나 체온계를 selected_device 변수에 담는 코드를 추가한다.
블루투스 기기를 연결하기 위해 connect 함수를 추가한다. 비동기 처리를 위해 async 함수로 구현한다.
main에 연결요청을 수행할 device를 선택했을 때 connect 과정을 수행하는 코드를 추가한다.
위 코드를 실행하면 콘솔(Terminal)에 다음과 같이 출력된다.이 출력되는것을 확인할 수 있다.
처음 페어링을 진행하는 경우 PIN Code를 입력해야한다.
바나나 체온계 뒷면의 PIN Code 6자리를 입력하면 된다.
지금까지의 내용이 반영된 코드이다. 다음은 위 과정을 모두 수행한 코드이다.
Service, Characteristic 요청
블루투스 연결이 완료 후, 바나나 체온계에서 제공하는 전체 Service와 Characteristic을 확인하기 위해 바나나 체온계에 Service와 Characteristic을 요청한다.
Service 요청을 위한 get_service_and_characterisc 함수를 추가한다.
get_service_and_characteristic 함수를 main함수에서 호출한다.
위 코드를 실행하여 바나나 체온계에서 제공되는 Service와 Characteristic을 살펴볼 수 있다.
지금까지의 내용이 반영된 코드이다.
온도정보 가져오기
바나나 체온계의 여러 characteristic 중 온도정보를 받아오는 기능을 구현한다. 온도 정보는 characteristic 에서 확인할 수 있다.
Service: Health Thermometer(00001809-0000-1000-8000-00805f9b34fb)
Characteristic: Temperature Measurement(00002a1c-0000-1000-8000-00805f9b34fb)
온도정보 수신
여러 Service 중 Health Thermometer와 관련된 service만 수신받기 위해 get_service_and_characteristic 함수를 수정한다.
이후 온도정보 characteristic을 통해 notify를 수신하기 위해 함수를 수정한다.
연결된 기기가 종료될 때까지 이벤트 루프를 종료하지 않기 위해 wait_connect 함수를 추가하고 main을 수정한다.
이후 코드를 실행하면 온도정보가 콘솔창에 출력되는것을 확인할 수 있다. 현재 wait_connect 함수는 별도의 종료를 하지않으면 코드가 계속 실행되므로 우측 상단의 쓰레기통 아이콘을 클릭해 코드실행을 종료한다.
지금까지의 내용이 반영된 코드이다.
온도정보 변환
바나나 체온계로부터 수신받은 데이터는 온도와 날짜 정보로 이루어져 있다. 온도 정보를 변환하는 함수를 작성한다.
날짜정보를 변환하는 함수를 작성한다.
기존에 작성했던 notify_callback 함수를 온도정보와 날짜정보를 받아와 출력하는 함수로 변경한다.
이제 프로그램을 이후 프로젝트를 실행하면 변환된 온도정보가 출력된다.
지금까지의 내용이 반영된 코드이다.
날짜정보 수정
현재 바나나 체온계에서 수신되는 날짜정보는 현재 시간과 맞지 않는다. 올바른 시간을 확인하려면 바나나 체온계에 현재 시간을 알려주는 과정이 필요하다.
다음은 날짜와 관련된 특성이다.
Service: Health Thermometer(00001809-0000-1000-8000-00805f9b34fb)
Characteristic: Date Time(00002a08-0000-1000-8000-00805f9634fb)
현재 시간을 리턴하는 함수를 구현한다.
Date Time Characteristic이 수신되었을 때 현재 날씨를 업로드하기 위해 get_service_and_characteristic 함수를 수정한다.
이후 코드를 실행하면 올바른 날짜로 수정되어 온도정보가 수신되는것을 확인할 수 있다.
지금까지의 내용이 반영된 코드이다.
데이터 저장
바나나 체온계로부터 수신한 온도정보를 데이터베이스를 사용하여 저장한다.
데이터베이스란
데이터베이스란 여러 사람이 공유하고 사용할 목적으로 통합 관리되는 정보의 집합이다. 몇 개의 자료 파일을 조직적으로 통합하여 자료 항목의 중복을 없애고 자료를 구조화하여 기억시켜 놓은 자료의 집합체라고 할 수 있다.
일반적으로 데이터베이스는 다음과 같은 기능을 제공한다
데이터 저장
데이터를 체계적으로 저장하여 나중에 쉽게 검색하고 사용할 수 있다. 예를 들어, 바나나 체온계에서 측정한 온도 데이터를 날짜와 시간별로 저장할 수 있다.
데이터 검색
저장된 데이터 중 원하는 정보를 쉽게 찾을 수 있다. 예를 들어, 특정 날짜의 체온 데이터를 검색할 수 있다.
데이터 수정
저장된 데이터를 필요에 따라 수정할 수 있다. 예를 들어, 잘못된 온도 기록을 수정할 수 있다.
데이터 삭제
더 이상 필요하지 않은 데이터를 삭제할 수 있다. 예를 들어, 오래된 데이터를 삭제하여 저장 공간을 확보할 수 있다.
데이터베이스의 구성 요소는 다음과 같다.
테이블
데이터를 행(row)과 열(column)로 구성된 형태로 저장하는 가장 기본적인 단위이다. 예를 들어, 체온 데이터를 저장하는 테이블에는 날짜, 시간, 체온 등의 열이 있을 수 있다.
레코드
테이블의 각 행을 레코드라고 한다. 각 레코드는 하나의 데이터 항목에 대한 정보를 포함한다. 예를 들어, 특정 시간에 측정된 체온 정보가 하나의 레코드가 된다.
필드
테이블의 각 열을 필드라고 한다. 필드는 데이터의 특정 속성을 나타낸다. 예를 들어, 체온 테이블에서 날짜, 시간, 체온은 각각 하나의 필드가 된다.
기본 키
각 레코드를 고유하게 식별할 수 있는 필드이다. 예를 들어, 체온 데이터를 고유하게 식별하기 위해 날짜와 시간을 조합하여 기본 키로 사용할 수 있다.
데이터베이스의 장점은 다음과 같다.
데이터 일관성
데이터베이스는 데이터를 중복 없이 저장하고 관리하기 때문에 데이터의 일관성을 유지할 수 있다.
데이터 무결성
데이터베이스는 저장된 데이터가 정확하고 신뢰할 수 있도록 다양한 제약 조건을 설정할 수 있다.
데이터 보안
데이터베이스는 접근 권한을 설정하여 데이터를 보호할 수 있다. 예를 들어, 특정 사용자만 데이터를 조회하거나 수정할 수 있도록 제한할 수 있다.
이러한 데이터베이스의 기능과 구성 요소를 이해하면, 데이터를 효율적으로 저장하고 관리할 수 있게 된다. 초보자들도 데이터베이스를 활용하여 체계적으로 데이터를 다룰 수 있는 능력을 기를 수 있다.
이러한 장점때문에 여기서도 데이터베이스를 사용해서 데이터를 저장한다.
데이터베이스 저장
여기서는 별도의 라이브러리를 설치하지 않고, 단일 파일로 저장되어 간편하게 사용할 수 있는 데이터베이스를 사용하기 위한 툴로 SQLite를 사용한다. sqlite를 import하는 코드를 추가한다.
이후 데이터베이스를 생성하기 위해 코드를 추가한다.
코드를 실행하면 좌측 상단에 temperature.db가 생성된것을 확인할 수 있다.
온도정보 notify를 수신했을 때 데이터 변환 후 데이터정보를 저장할 수 있도록 코드를 변환한다.
코드작성 후 실행하면 올바르게 데이터베이스에 온도정보가 저장된다. 저장된 결과는 다음 장인 "온도정보 확인"에서 볼 수 있다.
지금까지의 내용이 반영된 코드이다.
온도정보 확인
온도정보 확인을 위해 VSC(Visual Studio Code)의 Extension → sqlite 검색 → SQLite Viewer를 설치한다.
설치완료 후 VSC(Visual Studio Code)의 Explorer → temperature.db 클릭 → Open Anyway 클릭 → SQLite Viewer를 순서대로 클릭한다.
temperature.db에 온도정보가 들어가 있는 것을 확인할 수 있다.
SQLite Viewer는 데이터가 실시간으로 업데이트되지 않으므로 데이터베이스를 새롭게 갱신하고 싶다면 좌측 상단의 Refresh 버튼을 클릭해 데이터를 갱신한다.
데이터 그래프로 그리기
온도정보를 그래프로 그리기 위해 코드를 추가해 라이브러리를 import한다.
그래프에 그려질 온도정보를 담고있는 배열을 전역변수로 선언한다.
그래프 설정 함수를 추가한다.
set_graph 함수를 호출하는 코드를 main함수에 추가한다.
parse_temperature_information 함수에 그래프 그리는 부분을 추가로 작성한다.
이후 코드를 실행하면 온도정보가 그래프로 나타나는것을 확인할 수 있다.
지금까지의 내용이 반영된 코드이다.
Last updated