메인
home
소프트웨어
home
⚗️

웹 블루투스 통신 pico 제어

3축 모터 제어 개선 코드

html
피코 측 파이썬

실습 코드 다운로드(3개)

ble_advertising.py
1.1KB
ble_simple_peripheral.py
2.7KB
main.py
5.9KB
Archive.zip
5.8KB

Web-Bluetooth 기술이란?

웹블루투스 원본 자료

GPT와 대화
테스트 사이트

pico 측 코드

# 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()
C
복사
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
C
복사

rc카 형태로 웹블루투스 리모콘 사이트 만들기

GPT와 대화
콘솔창 신호 표시 코드

최종본

웹블루투스 신호 송수신 사이트
위 사이트 html 코드
웹사이트 호스팅 : 넷플리파이 이용
서보모터 두개 연결 회로 : 3번, 5번
피코측 코드 : 서보모터 2개 연결
main.py
import bluetooth import time from machine import Pin, PWM from micropython import const from ble_advertising import advertising_payload # Nordic UART Service UUIDs _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 BLEUARTServo: 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._payload = advertising_payload(name=name, services=[_UART_SERVICE_UUID]) # 서보모터 초기화 self._init_servos() self._advertise() def _init_servos(self): # 온보드 LED 설정 self.led = Pin("LED", Pin.OUT) self.led.value(1) # 연결 확인을 위해 초기 LED 켜짐 # 서보모터 설정 self.servo_horiz = PWM(Pin(3)) # 수평 방향 서보모터 (GP3) self.servo_vert = PWM(Pin(5)) # 수직 방향 서보모터 (GP5) # 서보모터의 주파수를 50Hz로 설정 self.servo_horiz.freq(50) self.servo_vert.freq(50) # 최소 및 최대 듀티 사이클 값 정의 self.min_duty = 1638 # 0도에 해당하는 듀티 사이클 self.max_duty = 8192 # 180도에 해당하는 듀티 사이클 # 초기 각도를 90도로 설정 (중앙 위치) self.angle_horiz = 90 self.angle_vert = 90 # 서보모터를 초기 위치로 이동 self.servo_horiz.duty_u16(self.angle_to_duty(self.angle_horiz)) self.servo_vert.duty_u16(self.angle_to_duty(self.angle_vert)) # LED 상태 변수 self.led_state = 1 def angle_to_duty(self, angle): # 주어진 각도(0~180)를 듀티 사이클로 변환하는 함수 return int(self.min_duty + (self.max_duty - self.min_duty) * angle / 180) 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) command = msg.decode().strip() print('Received:', command) self._handle_command(command) def _handle_command(self, command): if command == 'led': # LED 상태 토글 self.led.value(not self.led_state) self.led_state = 1 - self.led_state print("LED 상태 변경") elif command == 'w': # 위로 이동: 수직 각도를 증가 self.angle_vert += 10 if self.angle_vert > 180: self.angle_vert = 180 self.servo_vert.duty_u16(self.angle_to_duty(self.angle_vert)) print(f"위로 이동: 수직 각도 = {self.angle_vert}") elif command == 's': # 아래로 이동: 수직 각도를 감소 self.angle_vert -= 10 if self.angle_vert < 0: self.angle_vert = 0 self.servo_vert.duty_u16(self.angle_to_duty(self.angle_vert)) print(f"아래로 이동: 수직 각도 = {self.angle_vert}") elif command == 'a': # 왼쪽으로 이동: 수평 각도를 감소 self.angle_horiz -= 10 if self.angle_horiz < 0: self.angle_horiz = 0 self.servo_horiz.duty_u16(self.angle_to_duty(self.angle_horiz)) print(f"왼쪽으로 이동: 수평 각도 = {self.angle_horiz}") elif command == 'd': # 오른쪽으로 이동: 수평 각도를 증가 self.angle_horiz += 10 if self.angle_horiz > 180: self.angle_horiz = 180 self.servo_horiz.duty_u16(self.angle_to_duty(self.angle_horiz)) print(f"오른쪽으로 이동: 수평 각도 = {self.angle_horiz}") elif command == 'x': # 각도를 초기화하여 중앙 위치로 이동 self.angle_horiz = 90 self.angle_vert = 90 self.servo_horiz.duty_u16(self.angle_to_duty(self.angle_horiz)) self.servo_vert.duty_u16(self.angle_to_duty(self.angle_vert)) print("각도를 초기화합니다: 수평 각도 = 90, 수직 각도 = 90") else: print("유효하지 않은 명령입니다. 'w', 'a', 's', 'd', 'x' 또는 'led'를 사용하세요.") def _advertise(self, interval_us=500000): print("Advertising...") self._ble.gap_advertise(interval_us, adv_data=self._payload) def main(): ble = bluetooth.BLE() uart_servo = BLEUARTServo(ble) try: while True: time.sleep_ms(1000) except KeyboardInterrupt: pass if __name__ == "__main__": main()
C
복사
스마트폰에서도 동작 테스트
- 웹사이트에서 마우스 우클릭 후 qr코드 생성후 스마트폰으로 찍음