import log import usocket as socket import ubinascii as binascii import urandom as random import log import ure as re import ustruct as struct import urandom as random import _thread import utime import checkNet import ujson import utime from uio import StringIO """ 运行本例程,需要通过串口线连接开发板的 MAIN 口和PC,在PC上通过串口工具 打开 MAIN 口,并向该端口发送数据,即可看到 PC 发送过来的消息。 """ from machine import UART ''' * 参数1:端口 注:EC100YCN平台与EC600SCN平台,UARTn作用如下 UART0 - DEBUG PORT UART1 – BT PORT UART2 – MAIN PORT UART3 – USB CDC PORT * 参数2:波特率 * 参数3:data bits (5~8) * 参数4:Parity (0:NONE 1:EVEN 2:ODD) * 参数5:stop bits (1~2) * 参数6:flow control (0: FC_NONE 1:FC_HW) ''' from ucollections import namedtuple REG_REQUEST = { "jsonrpc": "2.0", "id": "1", "method":"/iotserver/device/reg", "params":{ "secret_key":"ELXPMM12bxE0LQ6YtWr9u7tyZZSMWALKAIiLdDlwUai5SBUS", "serial_number":"SN_TEST_REAL_001" } } GRAVITY = 9.8 LOGGER = log.getLogger(__name__) # Opcodes OP_CONT = 0x0 OP_TEXT = 0x1 OP_BYTES = 0x2 OP_CLOSE = 0x8 OP_PING = 0x9 OP_PONG = 0xa # Close codes CLOSE_OK = 1000 CLOSE_GOING_AWAY = 1001 CLOSE_PROTOCOL_ERROR = 1002 CLOSE_DATA_NOT_SUPPORTED = 1003 CLOSE_BAD_DATA = 1007 CLOSE_POLICY_VIOLATION = 1008 CLOSE_TOO_BIG = 1009 CLOSE_MISSING_EXTN = 1010 CLOSE_BAD_CONDITION = 1011 URL_RE = re.compile(r'(wss|ws)://([A-Za-z0-9-\.]+)(?:\:([0-9]+))?(/.+)?') URI = namedtuple('URI', ('protocol', 'hostname', 'port', 'path')) def urlparse(uri): """Parse ws:// URLs""" match = URL_RE.match(uri) if match: protocol = match.group(1) host = match.group(2) port = match.group(3) path = match.group(4) if protocol == 'wss': if port is None: port = 443 elif protocol == 'ws': if port is None: port = 80 else: raise ValueError('Scheme {} is invalid'.format(protocol)) return URI(protocol, host, int(port), path) class NoDataException(Exception): pass class ConnectionClosed(Exception): pass class Websocket(object): """ Basis of the Websocket protocol. This can probably be replaced with the C-based websocket module, but this one currently supports more options. """ is_client = False def __init__(self, sock, debug=False): self.sock = sock self.open = True self.debug = debug def __enter__(self): return self def __exit__(self, exc_type, exc, tb): self.close() def settimeout(self, timeout): self.sock.settimeout(timeout) def read_frame(self, max_size=None): """ Read a frame from the socket. See https://tools.ietf.org/html/rfc6455#section-5.2 for the details. """ # Frame header two_bytes = self.sock.read(2) if not two_bytes: raise NoDataException byte1, byte2 = struct.unpack('!BB', two_bytes) # Byte 1: FIN(1) _(1) _(1) _(1) OPCODE(4) fin = bool(byte1 & 0x80) opcode = byte1 & 0x0f # Byte 2: MASK(1) LENGTH(7) mask = bool(byte2 & (1 << 7)) length = byte2 & 0x7f if length == 126: # Magic number, length header is 2 bytes length, = struct.unpack('!H', self.sock.read(2)) elif length == 127: # Magic number, length header is 8 bytes length, = struct.unpack('!Q', self.sock.read(8)) if mask: # Mask is 4 bytes mask_bits = self.sock.read(4) try: data = self.sock.read(length) except MemoryError: # We can't receive this many bytes, close the socket if self.debug: LOGGER.info("Frame of length %s too big. Closing", length) self.close(code=CLOSE_TOO_BIG) return True, OP_CLOSE, None if mask: data = bytes(b ^ mask_bits[i % 4] for i, b in enumerate(data)) return fin, opcode, data def write_frame(self, opcode, data=b''): """ Write a frame to the socket. See https://tools.ietf.org/html/rfc6455#section-5.2 for the details. """ fin = True mask = self.is_client # messages sent by client are masked length = len(data) # Frame header # Byte 1: FIN(1) _(1) _(1) _(1) OPCODE(4) byte1 = 0x80 if fin else 0 byte1 |= opcode # Byte 2: MASK(1) LENGTH(7) byte2 = 0x80 if mask else 0 if length < 126: # 126 is magic value to use 2-byte length header byte2 |= length self.sock.write(struct.pack('!BB', byte1, byte2)) elif length < (1 << 16): # Length fits in 2-bytes byte2 |= 126 # Magic code self.sock.write(struct.pack('!BBH', byte1, byte2, length)) elif length < (1 << 64): byte2 |= 127 # Magic code self.sock.write(struct.pack('!BBQ', byte1, byte2, length)) else: raise ValueError() if mask: # Mask is 4 bytes mask_bits = struct.pack('!I', random.getrandbits(32)) self.sock.write(mask_bits) data = bytes(b ^ mask_bits[i % 4] for i, b in enumerate(data)) self.sock.write(data) def recv(self): """ Receive data from the websocket. This is slightly different from 'websockets' in that it doesn't fire off a routine to process frames and put the data in a queue. If you don't call recv() sufficiently often you won't process control frames. """ assert self.open while self.open: try: fin, opcode, data = self.read_frame() except NoDataException: return '' except ValueError: if self.debug: LOGGER.info("Failed to read frame. Socket dead.") self._close() raise ConnectionClosed() if not fin: raise NotImplementedError() if opcode == OP_TEXT: return data.decode('utf-8') elif opcode == OP_BYTES: return data elif opcode == OP_CLOSE: self._close() return elif opcode == OP_PONG: # Ignore this frame, keep waiting for a data frame continue elif opcode == OP_PING: # We need to send a pong frame if self.debug: LOGGER.info("Sending PONG") self.write_frame(OP_PONG, data) # And then wait to receive continue elif opcode == OP_CONT: # This is a continuation of a previous frame raise NotImplementedError(opcode) else: raise ValueError(opcode) def send(self, buf): """Send data to the websocket.""" assert self.open if isinstance(buf, str): opcode = OP_TEXT buf = buf.encode('utf-8') elif isinstance(buf, bytes): opcode = OP_BYTES else: raise TypeError() self.write_frame(opcode, buf) def close(self, code=CLOSE_OK, reason=''): """Close the websocket.""" if not self.open: return buf = struct.pack('!H', code) + reason.encode('utf-8') self.write_frame(OP_CLOSE, buf) self._close() def _close(self): if self.debug: LOGGER.info("Connection closed") self.open = False self.sock.close() class WebsocketClient(Websocket): is_client = True class Client(object): @staticmethod def connect(uri, headers=None, debug=False): """ Connect a websocket. :param uri: example ws://172.16.185.123/ :param headers: k, v of header :param debug: allow output log :return: """ if not headers: headers = dict() if not isinstance(headers, dict): raise Exception("headers must be dict type but {} you given.".format(type(headers))) uri = urlparse(uri) assert uri if debug: LOGGER.info("open connection %s:%s", uri.hostname, uri.port) sock = socket.socket() addr = socket.getaddrinfo(uri.hostname, uri.port) sock.connect(addr[0][4]) if uri.protocol == 'wss': import ussl sock = ussl.wrap_socket(sock) def send_header(header, *args): if debug: LOGGER.info(str(header), *args) sock.write(header % args + '\r\n') # Sec-WebSocket-Key is 16 bytes of random base64 encoded key = binascii.b2a_base64(bytes(random.getrandbits(8) for _ in range(16)))[:-1] send_header(b'GET %s HTTP/1.1', uri.path or '/') send_header(b'Host: %s:%s', uri.hostname, uri.port) send_header(b'Connection: Upgrade') send_header(b'Upgrade: websocket') send_header(b'Sec-WebSocket-Key: %s', key) send_header(b'Sec-WebSocket-Version: 13') send_header(b'Origin: http://{hostname}:{port}'.format( hostname=uri.hostname, port=uri.port) ) for k, v in headers.items(): send_header('{}:{}'.format(k, v).encode()) send_header(b'') header = sock.readline()[:-2] assert header.startswith(b'HTTP/1.1 101 '), header # We don't (currently) need these headers # FIXME: should we check the return key? while header: if debug: LOGGER.info(str(header)) header = sock.readline()[:-2] return WebsocketClient(sock, debug) def recv(cli): while True: # 死循环接收数据 recv_data = cli.recv() print(recv_data) if not recv_data: # 服务器关闭连接或客户端关闭连接 print("cli close") cli.close() break #io = StringIO() #ujson.dump("{}".format(recv_data), io) request = ujson.loads("{}".format(recv_data)) if "method" in request: if request["method"] == "/lampControl/push": uart_test = Example_uart(bate=9600) print(request["params"]) print("{}".format(request["params"])) uart_test.uartWrite("{}".format(request["params"])) utime.sleep(2) print(uart_test.uartRead(1024)) del uart_test continue if request["method"] == "/lampControl": uart_test = Example_uart(bate=9600) print(request["params"]) print("{}".format(request["params"])) uart_test.uartWrite("{}".format(request["params"])) utime.sleep(2) msg = uart_test.uartRead(1024) print(msg) utf8_msg = msg.decode() resp = { "jsonrpc": "2.0", "id": request["id"], "result":utf8_msg, "error":{ "message":"", "code":200, "data":"" } } cli.send(ujson.dumps(resp)) del uart_test continue if request["method"] == "towgo.websocket.ping": del request["params"] del request["method"] request["error"]={'message':"no",'code':500} json_str = ujson.dumps(request) cli.send(json_str) continue # 设置日志输出级别 log.basicConfig(level=log.INFO) uart_log = log.getLogger("UART") class Example_uart(object): def __init__(self, no=UART.UART2, bate=115200, data_bits=8, parity=0, stop_bits=1, flow_control=0): self.uart = UART(no, bate, data_bits, parity, stop_bits, flow_control) #self.uart.set_callback(self.callback) def callback(self, para): uart_log.info("call para:{}".format(para)) if(0 == para[0]): self.uartRead(para[2]) def uartWrite(self, msg): uart_log.info("write msg:{}".format(msg)) self.uart.write(msg) def uartRead(self, len): msg = self.uart.read(len) utf8_msg = msg.decode() uart_log.info("UartRead msg: {}".format(utf8_msg)) return utf8_msg def uartWrite_test(self): for i in range(10): write_msg = "Hello count={}".format(i) self.uartWrite(write_msg) utime.sleep(1) if __name__ == '__main__': while True: stagecode, subcode = checkNet.wait_network_connected(30) if stagecode == 3 and subcode == 1: try: client = Client.connect('wss://iotserver.ruixininfo.com/jsonrpc/websocket', debug=False) client.send(ujson.dumps(REG_REQUEST)) #print(s) #utime.sleep(2) print("发送注册信息成功,开始接收信息") recv(client) except Exception as e: print(e) # We can't receive this many bytes, close the socket utime.sleep(10)