3축 모터 제어 개선 코드
html
피코 측 파이썬
실습 코드 다운로드(3개)
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코드 생성후 스마트폰으로 찍음