Source code for openpibo.device

"""
MCU를 제어하여, 부품을 제어합니다.

Class:
:obj:`~openpibo.device.Device`
"""

import serial
import time
from threading import Lock
import requests
import urllib

[문서]class Device: """ Functions: :data:`~openpibo.device.Device.code_list` :meth:`~openpibo.device.Device.send_cmd` :meth:`~openpibo.device.Device.send_raw` :meth:`~openpibo.device.Device.eye_on` :meth:`~openpibo.device.Device.eye_on_s` :meth:`~openpibo.device.Device.eye_off` :meth:`~openpibo.device.Device.get_battery` :meth:`~openpibo.device.Device.get_dc` :meth:`~openpibo.device.Device.get_system` :meth:`~openpibo.device.Device.get_pir` :meth:`~openpibo.device.Device.get_touch` :meth:`~openpibo.device.Device.get_button` 메인컨트롤러를 제어하여 파이보의 여러가지 상태를 체크하거나, 눈 색깔을 변경합니다. example:: from openpibo.device import Device device = Device() # 아래의 모든 예제 이전에 위 코드를 먼저 사용합니다. 메시지 상세 설명:: * VERSION(10): 파이보의 버전을 출력합니다. * msg: '#10:!' * result: 버전정보 (예: '10:FWN200312A') * HALT(11): 파이보를 종료합니다. * msg: '#11:!' * result: 'ok' * DC_CONN(14): 파이보의 충전기 연결 여부를 확인합니다. * msg: '#14:!' * result: '14:on' * ``on``: 연결 되어있음 * ``off``: 연결 되어있지 않음 * BATTERY(15): 파이보의 배터리 잔량을 확인합니다. * msg: '#15:!' * result: 배터리 잔량 정보 (예: '15:100%') * REBOOT(17): 설정 초기화 (Not use) * msg: '#17:!' * result: 'ok' * NEOPIXEL(20): 파이보의 눈(네오픽셀) 색을 변경합니다. - 양쪽 동일하게 설정 * msg: '#20:255,255,255!' * data: 네오픽셀 색 R,G,B ('R,G,B') (예: '255,255,255') * 'R,G,B' 의 포맷으로 입력 * 각 R, G, B는 0~255 정수 * result: 'ok' * NEOPIXEL_FADE(21): 파이보의 눈(네오픽셀) 색을 천천히 변경합니다. - 양쪽 동일하게 설정 * msg: '#21:255,255,255,10!' * data: 네오픽셀 색 R,G,B와 색 변경 속도 d ('R,G,B,d') (예: '255,255,255,10') * 'R,G,B,d' 의 포맷으로 입력 * 각 R, G, B는 0~255 정수 * d는 색상 단위 변화 1당 걸리는 시간으로, 단위는 ms * result: 'ok' * NEOPIXEL_BRIGHTNESS(22): 파이보의 눈(네오픽셀) 밝기를 조절합니다. * msg: '#22:64!' * data: 네오픽셀 밝기 (예: 64) * 0~255 정수 * 기본값: 64 * result: 'ok' * NEOPIXEL_EACH(23): 파이보의 양쪽 눈(네오픽셀) 색을 각각 변경합니다. - 양쪽 각각 설정 * msg: '#23:255,255,255,255,255,255!' * data: 왼쪽 네오픽셀 색 R,G,B와 오른쪽 네오픽셀 색 R,G,B ('R,G,B,R,G,B') (예: '255,255,255,255,255,255') * 'R,G,B,R,G,B' 의 포맷으로 입력 * 각 R, G, B는 0~255 정수 * result: 'ok' * NEOPIXEL_FADE_EACH(24): 파이보의 양쪽 눈(네오픽셀) 색을 각각 천천히 변경합니다. * msg: '#24:255,255,255,255,255,255,10!' * data: 왼쪽 네오픽셀 색 R,G,B와 오른쪽 네오픽셀 색 R,G,B와 색 변화 속도 d ('R,G,B,R,G,B,d') (예: '255,255,255,255,255,255,10') * 'R,G,B,R,G,B' 의 포맷으로 입력 * 각 R, G, B는 0~255 정수 * d는 색상 단위 변화 1당 걸리는 시간으로, 단위는 ms * result: 'ok' * NEOPIXEL_LOOP(25): 파이보의 눈(네오픽셀) 색을 무지개색으로 일정시간동안 변경합니다. * msg: '#25:10!' * data: 색 변화 속도 d (예: 10) * d는 색상 단위 변화 1당 걸리는 시간으로, 단위는 ms * result: 'ok' * NEOPIXEL_OFFSET_SET(26): 파이보의 눈(네오픽셀) 색의 초기 설정 * msg: '#26:255,255,255,255,255,255!' * data: 왼쪽 네오픽셀 오프셋 R,G,B와 오른쪽 네오픽셀 오프셋 R,G,B ('R,G,B,R,G,B') (예: '255,255,255,255,255,255') * 'R,G,B,R,G,B' 의 포맷으로 입력 * 각 R, G, B는 0~255 정수 * result: 'ok' * NEOPIXEL_OFFSET_GET(27): 파이보의 눈(네오픽셀) 오프셋 정보를 반환합니다. * msg: '#27:255,255,255,255,255,255!' * data: 네오픽셀 오프셋 정보 (예: '27:255,255,255,255,255,255') * result: - * NEOPIXEL_EACH_ORG(28): 파이보의 양쪽 눈(네오픽셀) 색을 각각 변경합니다. 단, 오프셋의 영향을 받지 않습니다. * msg: '#28:255,255,255,255,255,255!' * data: 왼쪽 네오픽셀 색 R,G,B와 오른쪽 네오픽셀 색 R,G,B ('R,G,B,R,G,B') (예: '255,255,255,255,255,255') * 'R,G,B,R,G,B' 의 포맷으로 입력 * 각 R, G, B는 0~255 정수 * result: 'ok' * PIR(30): PIR 센서를 켜고 끕니다. * msg: '#30:on!' * data: PIR 센서의 활성화 여부 (예: 'on') * ``on`` : PIR 센서를 켭니다. * ``off`` : PIR 센서를 끕니다. * result: 'ok' * SYSTEM(40): 각종 시스템 정보를 출력합니다. * msg: '#40:!' * data: - * result: 아래의 정보가 모두 출력됩니다. * PIR 감지: * ``person`` : PIR센서가 적외선의 변화를 감지할 때 출력됩니다. * ``nobody`` : ``person`` 출력 후 2초간 적외선의 변화가 없을 때 1회 출력됩니다. * ``-`` : 적외선의 변화가 감지되지 않을 때 또는 PIR센서가 비활성화 상태일 때 출력됩니다. * Touch 감지: * ``touch`` : 터치센서 감지 시 출력됩니다. * ``-`` : 터치센서 감지가 안되면 출력됩니다. * DC잭 연결감지: * ``on`` : DC잭 감지 시 1회 출력됩니다. 이후 ``-`` 가 출력됩니다. * ``off`` : DC잭 감지 해제시 1회 출력됩니다. 이후 ``-`` 가 출력됩니다. * ``-`` : DC잭 신호의 변화가 없을 때 출력됩니다. * 버튼 감지: * ``on`` : 전원 버튼 누름 감지 시 출력됩니다. * ``-`` : 전원 버튼 누름 감지가 안되면 출력됩니다. * 시스템리셋: 현재 지원되지 않습니다. * 전원종료: 현재 지원되지 않습니다. """ code_list ={ "VERSION" :"10", "HALT" :"11", "DC_CONN" :"14", "BATTERY" :"15", "REBOOT" :"17", "NEOPIXEL" :"20", "NEOPIXEL_FADE" :"21", "NEOPIXEL_BRIGHTNESS" :"22", "NEOPIXEL_EACH" :"23", "NEOPIXEL_FADE_EACH" :"24", "NEOPIXEL_LOOP" :"25", "NEOPIXEL_OFFSET_SET" :"26", "NEOPIXEL_OFFSET_GET" :"27", "NEOPIXEL_EACH_ORG" :"28", "PIR" :"30", "SYSTEM" :"40", } def __init__(self, api_mode=True, port=80): """ Device 클래스를 초기화합니다. """ if api_mode == False: self.dev = serial.Serial(port="/dev/ttyS0", baudrate=9600) self.lock = Lock() self.api_mode = api_mode self.port = port self.code_val_list = [i for i in Device.code_list.values()]
[문서] def send_cmd(self, code, data:str=""): """ Device에 메시지 코드/데이터를 전송합니다. 입력된 메시지는 ``#code:data!`` 의 포맷으로 변경되어 전달됩니다. 메시지 상세 설명은 위에서 확인할 수 있습니다. example:: device.send_cmd(20, '255,255,255') :param str or int code: 메시지 코드 * 10 : VERSION * 11 : HALT * 14 : DC_CONN * 15 : BATTERY * 17 : REBOOT * 20 : NEOPIXEL * 21 : NEOPIXEL_FADE * 22 : NEOPIXEL_BRIGHTNESS * 23 : NEOPIXEL_EACH * 24 : NEOPIXEL_FADE_EACH * 25 : NEOPIXEL_LOOP * 26 : NEOPIXEL_OFFSET_SET * 27 : NEOPIXEL_OFFSET_GET * 28 : NEOPIXEL_EACH_ORG * 30 : PIR * 40 : SYSTEM :param str data: 메시지 ``code`` 의 값에 따라 데이터의 형식이 다릅니다. code에 따라 data가 요구되지 않을 수도 있습니다. example:: device.send_cmd(20, '255,255,255') device.send_cmd(21, '255,255,255,10') device.send_cmd(22, '64') device.send_cmd(23, '255,255,255,255,255,255') device.send_cmd(24, '255,255,255,255,255,255,10') device.send_cmd(25, '2') device.send_cmd(26, '255,255,255,255,255,255') device.send_cmd(28, '255,255,255,255,255,255') device.send_cmd(30, 'on') **자세한 설명은 상단 "메시지 상세 설명" 참고하시기 바랍니다** :returns str: Device로부터 받은 응답 """ if not str(code) in self.code_val_list: raise Exception(f'"{code}" not support') return self.send_raw("#{}:{}!".format(code, data))
[문서] def send_raw(self, raw): """ Device에 실제 메시지를 전송하고 응답을 받습니다. 메시지 상세 설명은 위에서 확인할 수 있습니다. example:: device.send_raw('#20:255,255,255!') device.send_raw('#22:64!') :param str raw: 실제 전달되는 메시지 Device에 전송되는 메시지의 포맷은 ``#code:data!`` 입니다. 해당 메시지를 메인 컨트롤러에 전송합니다. **자세한 설명은 상단 "메시지 상세 설명" 참고하시기 바랍니다** :returns: Device로부터 받은 응답 """ # if self.lock.locked() == True: # return False if self.api_mode == True: # RESTAPI 모드 return requests.get(f"http://0.0.0.0:{self.port}/device/{urllib.parse.quote(raw)}").json() self.lock.acquire() self.dev.write(raw.encode('utf-8')) data = "" while True: ch = self.dev.read().decode() if ch == '#' or ch == '\r' or ch == '\n': continue if ch == '!': break data += ch time.sleep(0.01) self.lock.release() return data
[문서] def eye_on(self, *color, intv=0): """ LED를 켭니다. example:: device.eye_on(255,0,0) # 양쪽 눈 제어 device.eye_on(0,255,0,0,0,255) # 양쪽 눈 각각 제어 :param color: * RGB (0~255 숫자) * (R, G, B) -> 양쪽 눈 함께 제어 * (R,G,B,R,G,B) -> 양쪽 눈 각각 제어 :param intv: * interval (RGB 값이 1씩 바뀌는 시간) * 0 이면 off """ if len(color) == 0: raise Exception("color is required") if len(color) not in (3, 6): raise Exception(f"len({color}) must be 3 or 6") for v in color: if v < 0 or v > 255: raise Exception("All color must be 0~255") if intv == 0: code = '20' if len(color) == 3 else '23' return self.send_raw(f'#{code}:{",".join(str(p) for p in color)}!') else: code = '21' if len(color) == 3 else '24' return self.send_raw(f'#{code}:{",".join(str(p) for p in color)},{intv}!')
[문서] def eye_on_s(self, colors, intv=0): """ LED를 켭니다. example:: device.eye_on_s(['#ffffff', '#ffffff']) # 양쪽 눈 제어 :param colors: * RGB 16진수 문자 리스트 ['#ffffff', '#ffffff'] """ data = [] data.append(int(colors[0][1:3], 16)) data.append(int(colors[0][3:5], 16)) data.append(int(colors[0][5:7], 16)) data.append(int(colors[1][1:3], 16)) data.append(int(colors[1][3:5], 16)) data.append(int(colors[1][5:7], 16)) if intv == 0: return self.send_raw(f'#23:{",".join(str(p) for p in data)}!') else: return self.send_raw(f'#24:{",".join(str(p) for p in data)},{intv}!')
[문서] def eye_off(self): """ LED를 끕니다. example:: device.eye_off() """ return self.send_raw('#20:0,0,0!')
[문서] def get_battery(self, v=False): """ 배터리 정보를 요청합니다. example:: device.get_battery() :param v: 값만 가져올지 메시지 전체를 가져올지 선택 :returns: 배터리 정보 응답 """ return self.send_raw('#15:!') if v == False else self.send_raw('#15:!').split(':')[1]
[문서] def get_dc(self, v=False): """ DC커넥터 정보를 요청합니다. example:: device.get_dc() :param v: 값만 가져올지 메시지 전체를 가져올지 선택 :returns: DC커넥터 연결 정보 응답 """ return self.send_raw('#14:!') if v == False else self.send_raw('#14:!').split(':')[1]
[문서] def get_system(self, v=False): """ 시스템 메시지를 요청합니다. example:: device.get_system() :param v: 값만 가져올지 메시지 전체를 가져올지 선택 :param name: 가져올 값 선택 ('pir'|'touch'|'dc'|'button'|'all') :returns: 시스템 메시지 정보 응답 (pir-touch-dc-button) """ return self.send_raw('#40:!') if v == False else self.send_raw('#40:!').split(':')[1]
[문서] def get_pir(self): """ 시스템 메시지를 요청합니다. (pir) example:: device.get_pir() :returns: 시스템 메시지 정보 응답에서 pir 값 추출 """ return self.send_raw('#40:!').split(':')[1].split('-')[0]
[문서] def get_touch(self): """ 시스템 메시지를 요청합니다. (touch) example:: device.get_touch() :returns: 시스템 메시지 정보 응답에서 touch 값 추출 """ return self.send_raw('#40:!').split(':')[1].split('-')[1]
[문서] def get_button(self): """ 시스템 메시지를 요청합니다. (button) example:: device.get_button() :returns: 시스템 메시지 정보 응답에서 button 값 추출 """ return self.send_raw('#40:!').split(':')[1].split('-')[3]
if __name__ == "__main__": device = Device() print(1, device.eye_on(255,0,0)) print(2, device.send_raw('#21:0,0,255,10')) print(3, device.get_battery()) print(4, device.get_dc()) print(5, device.get_system()) print(6, device.send_raw('#40:!'))