Web-Bluetooth 기술이란?

How to Read Raspberry Pico Temperature and Transmit it to a Browser using Web Bluetooth

웹블루투스 원본 자료

ferrygun - Overview

GPT와 대화

ChatGPT - Pico Web Bluetooth Terminal

테스트 사이트

pico_serial_webble_terminal

pico 측 코드

image.png

main.py

# ble_uart_peripheral.py

import bluetooth
import time
from ble_advertising import advertising_payload
from micropython import const

_UART_SERVICE_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
_UART_TX_CHAR_UUID = bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E')  # Central에서 알림 받을 특성
_UART_RX_CHAR_UUID = bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E')  # Central에서 쓰기 할 특성

_UART_TX = (
    _UART_TX_CHAR_UUID,
    bluetooth.FLAG_NOTIFY,
)
_UART_RX = (
    _UART_RX_CHAR_UUID,
    bluetooth.FLAG_WRITE,
)

_UART_SERVICE = (
    _UART_SERVICE_UUID,
    (_UART_TX, _UART_RX),
)

_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)

class BLEUART:
    def __init__(self, ble, name='Pico', rxbuf=100):
        self._ble = ble
        self._ble.active(True)
        self._ble.irq(self._irq)
        
        ((self._tx_handle, self._rx_handle),) = self._ble.gatts_register_services((_UART_SERVICE,))
        
        self._connections = set()
        self._rx_buffer = bytearray()
        
        self._handler = None
        
        self._payload = advertising_payload(name=name, services=[_UART_SERVICE_UUID])
        
        self._advertise()
    
    def irq(self, handler):
        self._handler = handler
    
    def _irq(self, event, data):
        if event == _IRQ_CENTRAL_CONNECT:
            conn_handle, addr_type, addr = data
            print('Connected', addr)
            self._connections.add(conn_handle)
        elif event == _IRQ_CENTRAL_DISCONNECT:
            conn_handle, addr_type, addr = data
            print('Disconnected', addr)
            self._connections.remove(conn_handle)
            self._advertise()
        elif event == _IRQ_GATTS_WRITE:
            conn_handle, value_handle = data
            if value_handle == self._rx_handle:
                msg = self._ble.gatts_read(self._rx_handle)
                print('Received:', msg.decode().strip())
                # 받은 데이터를 그대로 전송 (에코)
                for conn_handle in self._connections:
                    self._ble.gatts_notify(conn_handle, self._tx_handle, msg)
    
    def write(self, data):
        for conn_handle in self._connections:
            self._ble.gatts_notify(conn_handle, self._tx_handle, data)
    
    def _advertise(self, interval_us=500000):
        self._ble.gap_advertise(interval_us, adv_data=self._payload)

def demo():
    ble = bluetooth.BLE()
    uart = BLEUART(ble)
    
    try:
        while True:
            time.sleep_ms(1000)
    except KeyboardInterrupt:
        pass

if __name__ == "__main__":
    demo()

ble_advertising.py

# ble_advertising.py

import struct
from micropython import const

_ADV_TYPE_FLAGS = const(0x01)
_ADV_TYPE_NAME = const(0x09)
_ADV_TYPE_UUID16_COMPLETE = const(0x03)
_ADV_TYPE_UUID128_COMPLETE = const(0x07)

def advertising_payload(limited_disc=False, br_edr=False, name=None, services=None):
    payload = bytearray()

    # 플래그
    flags = (0x02 if limited_disc else 0x06) + (0x00 if br_edr else 0x04)
    payload += struct.pack('BB', 2, _ADV_TYPE_FLAGS)
    payload += struct.pack('B', flags)

    # 이름
    if name:
        name_bytes = name.encode('utf-8')
        payload += struct.pack('BB', len(name_bytes) + 1, _ADV_TYPE_NAME)
        payload += name_bytes

    # 서비스 UUID
    if services:
        for uuid in services:
            b = bytes(uuid)
            if len(b) == 2:
                payload += struct.pack('BB', len(b) + 1, _ADV_TYPE_UUID16_COMPLETE)
                payload += b
            elif len(b) == 16:
                payload += struct.pack('BB', len(b) + 1, _ADV_TYPE_UUID128_COMPLETE)
                payload += b

    return payload