Advertisement
Guest User

Untitled

a guest
Aug 7th, 2016
187
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.32 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. #from __future__ import print_function
  4.  
  5. """
  6. pyboard interface
  7.  
  8. This module provides the Pyboard class, used to communicate with and
  9. control the pyboard over a serial USB connection.
  10.  
  11. Example usage:
  12.  
  13. 1. first import this file with:
  14. import pyboard
  15.  
  16. 2. Then create a conection:
  17.  
  18. in case of a serial port:
  19. pyb = pyboard.Pyboard('/dev/ttyACM0')
  20.  
  21. or in Windows:
  22. pyb = pyboard.Pyboard('COM2')
  23.  
  24. For telnet connections:
  25. pyb = pyboard.Pyboard('192.168.1.1')
  26.  
  27. Or even (if the name is defined in the DNS or the 'hosts' file:
  28. pyb = pyboard.Pyboard('mypyboard')
  29.  
  30. You can also include a custom port number:
  31. pyb = pyboard.Pyboard('mypyboard:2323')
  32.  
  33. Then:
  34.  
  35. pyb.enter_raw_repl()
  36. pyb.exec('pyb.LED(1).on()')
  37. pyb.exit_raw_repl()
  38.  
  39.  
  40. Note: if using Python2 then pyb.exec must be written as pyb.exec_.
  41. To run a script from the local machine on the board and print out the results:
  42.  
  43. import pyboard
  44. pyboard.execfile('test.py', device='/dev/ttyACM0')
  45.  
  46. This script can also be run directly. To execute a local script, use:
  47.  
  48. ./pyboard.py test.py
  49.  
  50. Or:
  51.  
  52. python pyboard.py test.py
  53.  
  54. """
  55.  
  56. """ESP8266 support"""
  57. from collections import deque
  58.  
  59. import time
  60.  
  61. websocket_allowed = True
  62.  
  63. def println(message):
  64. pass
  65. print(message)
  66.  
  67. try:
  68. from websocket import WebSocketApp
  69. import websocket
  70. import threading
  71. from threading import Thread
  72. except:
  73. import pip
  74. try:
  75. pip.main(['install', '--no-compile', 'websocket'])
  76. pip.main(['install', '--no-compile', 'websocket-client'])
  77. pip.main(['install', '--no-compile', 'threading'])
  78. except:
  79. println('cannot install packages')
  80. websocket_allowed = False
  81.  
  82. # do it again
  83. from websocket import WebSocketApp
  84. import websocket
  85. import threading
  86.  
  87.  
  88. class Websock_connection(threading.Thread):
  89.  
  90. def __init__(self, uri):
  91. threading.Thread.__init__(self)
  92.  
  93. self.connected = False
  94.  
  95. self.daemon = True
  96. self.fifo = deque()
  97. self.fifo_lock = threading.Lock()
  98.  
  99. self.uri = uri
  100. self.timeout = 10.0
  101. # websocket.enableTrace(logging.root.getEffectiveLevel() < logging.INFO)
  102. self.ws = websocket.WebSocketApp("%s" % self.uri,
  103. on_message=self.on_message,
  104. on_error=self.on_error,
  105. on_close=self.on_close)
  106. self.start()
  107.  
  108. self.connected = False
  109. if b'Password:' in self.read_until("Password:", timeout=10):
  110. self.connected = True
  111.  
  112. println("Connected: %s" % self.connected)
  113. println("uri: %s" % self.uri)
  114.  
  115. # user is not used
  116. def authenticate(self, user, password):
  117. println("Authentication start")
  118. self.ws.send(password + "\r")
  119. self.connected = True
  120. if not b'WebREPL connected' in self.read_until("WebREPL connected", timeout=10):
  121. self.connected = False
  122. raise PyboardError('Invalid credentials')
  123.  
  124. println("Authentication success")
  125. return True
  126.  
  127. def run(self):
  128.  
  129. self.ws.run_forever()
  130.  
  131. def __del__(self):
  132. self.close()
  133.  
  134. def on_message(self, ws, message):
  135. self.fifo.extend(message)
  136.  
  137. try:
  138. self.fifo_lock.release()
  139. except:
  140. pass
  141.  
  142. def on_error(self, ws, error):
  143.  
  144. try:
  145. self.fifo_lock.release()
  146. except:
  147. pass
  148.  
  149. def on_close(self, ws):
  150.  
  151. try:
  152. self.fifo_lock.release()
  153. except:
  154. pass
  155.  
  156. def close(self):
  157. try:
  158. self.ws.close()
  159. println("ws.close")
  160. try:
  161. self.fifo_lock.release()
  162. except:
  163. pass
  164. except:
  165. try:
  166. self.fifo_lock.release()
  167. except:
  168. pass
  169.  
  170. def read(self, size=1, blocking=True):
  171.  
  172. data = ''
  173.  
  174. tstart = time.time()
  175.  
  176. while (len(data) < size) and (time.time() - tstart < self.timeout):
  177.  
  178. if len(self.fifo) > 0:
  179. data += self.fifo.popleft()
  180. elif blocking:
  181. self.fifo_lock.acquire()
  182.  
  183. return data.encode("utf-8")
  184.  
  185. def write(self, data):
  186. println("Will write data %s" % data)
  187. self.ws.send(data)
  188. return len(data)
  189.  
  190. def inWaiting(self):
  191. return len(self.fifo)
  192.  
  193. def survives_soft_reset(self):
  194. return False
  195.  
  196. def keep_alive(self):
  197. pass
  198.  
  199. def read_some(self):
  200. return self.inWaiting()
  201.  
  202. def read_eager(self):
  203. return ''
  204.  
  205. def flush(self):
  206. pass
  207.  
  208. def read_until(self, ending, timeout=10):
  209. current_timeout = self.timeout
  210. self.timeout = 0.1
  211.  
  212. timeout_count = 0
  213. data = ''
  214. while True:
  215. if data.endswith(ending):
  216. break
  217. elif self.inWaiting() > 0:
  218. new_data = self.read(1)
  219. data = data + new_data
  220.  
  221. timeout_count = 0
  222. else:
  223. timeout_count += 1
  224. if timeout is not None and timeout_count >= 100 * timeout:
  225. break
  226. time.sleep(0.01)
  227.  
  228. self.timeout = current_timeout
  229. return data
  230.  
  231. @staticmethod
  232. def is_websocket_uri(uri):
  233. if not websocket_allowed:
  234. println("websocket_allowed == False")
  235.  
  236. return websocket_allowed and 'ws://' in uri
  237.  
  238. """end ESP8266 support"""
  239.  
  240. import sys
  241. import time
  242. import os
  243. import stat
  244.  
  245. try:
  246. stdout = sys.stdout.buffer
  247. except AttributeError:
  248. # Python2 doesn't have buffer attr
  249. stdout = sys.stdout
  250.  
  251. def stdout_write_bytes(b):
  252. b = b.replace(b"\x04", b"")
  253. stdout.write(b)
  254. stdout.flush()
  255.  
  256. class PyboardError(BaseException):
  257. def __init__(self, value):
  258. self.value = value
  259. println("Exception %s" % value)
  260. def __str__(self):
  261. return repr(self.value)
  262.  
  263.  
  264. class Periodic:
  265. def __init__(self, period, callback):
  266. self.thread = Thread(target=self.on_timer, args=(period, callback))
  267. self.run = True
  268. self.thread.start()
  269.  
  270. def __del__(self):
  271. self.stop()
  272.  
  273. def stop(self):
  274. self.run = False
  275.  
  276. def on_timer(self, period, callback):
  277. while self.run == True:
  278. time.sleep(period)
  279. callback()
  280.  
  281.  
  282. class Serial_connection:
  283. def __init__(self, device, baudrate=115200, timeout=0):
  284. import serial
  285. delayed = False
  286. self.connected = False
  287. for attempt in range(timeout + 1):
  288. try:
  289. self.stream = serial.Serial(device, baudrate=baudrate, interCharTimeout=1)
  290. self.connected = True
  291. self.__expose_stream_methods()
  292. if sys.version_info[0] >= 3:
  293. self.__in_waiting = lambda: self.stream.in_waiting
  294. else:
  295. self.__in_waiting = self.stream.inWaiting
  296.  
  297. if attempt != 0:
  298. print('') # to break the dotted line printed on retries
  299. break
  300. except (OSError, IOError): # Py2 and Py3 have different errors
  301. if timeout == 0:
  302. continue
  303.  
  304. if attempt == 0 and timeout != 0:
  305. sys.stdout.write('Waiting {} seconds for pyboard '.format(timeout))
  306.  
  307. time.sleep(1)
  308. sys.stdout.write('.')
  309. sys.stdout.flush()
  310.  
  311. def authenticate(self, user, password):
  312. # needs no authentication
  313. return True
  314.  
  315. def keep_alive(self):
  316. pass
  317.  
  318. def __expose_stream_methods(self):
  319. self.close = self.stream.close
  320. self.write = self.stream.write
  321.  
  322. def read_until(self, ending, timeout):
  323. ''' implement a telnetlib-like read_until '''
  324. current_timeout = self.stream.timeout
  325. self.stream.timeout = 0.1
  326.  
  327. timeout_count = 0
  328. data = ''
  329. while True:
  330. if data.endswith(ending):
  331. break
  332.  
  333. new_data = self.stream.read(1)
  334. data = data + new_data
  335. if new_data == '':
  336. timeout_count += 1
  337. if timeout_count >= 10 * timeout:
  338. break
  339.  
  340. self.stream.timeout = current_timeout
  341. return data
  342.  
  343. def read_eager(self):
  344. ''' implement a telnetlib-like read_eager '''
  345. waiting = self.__in_waiting()
  346. if waiting == 0:
  347. return ''
  348. return self.stream.read(waiting)
  349.  
  350. def read_some(self):
  351. ''' implement a telnetlib-like read_some '''
  352. waiting = self.__in_waiting()
  353. if waiting != 0:
  354. return self.stream.read(waiting)
  355. else:
  356. return self.stream.read(1)
  357.  
  358. @staticmethod
  359. def is_serial_port(name):
  360. return '/dev/tty' in name or name[:3] == 'COM' or (os.path.exists(name) == True and \
  361. stat.S_ISCHR(os.stat(name).st_mode) == True)
  362.  
  363.  
  364. class Telnet_connection:
  365. import telnetlib
  366. import socket
  367.  
  368. def __init__(self, uri, timeout=15, read_timeout=10):
  369. self.__read_timeout = read_timeout
  370. self.connected = False
  371. try:
  372. address, port = self.__separateAdressPort(uri)
  373. self.stream = Telnet_connection.telnetlib.Telnet(address, port, timeout=15)
  374. self.__socket = self.stream.get_socket()
  375. self.__socket.setsockopt(Telnet_connection.socket.SOL_SOCKET,
  376. Telnet_connection.socket.SO_KEEPALIVE, 1)
  377. self.connected = True
  378. self.__expose_stream_methods()
  379.  
  380. except Telnet_connection.socket.error:
  381. pass
  382.  
  383. def _wait_for_exact_text(self, remote_text):
  384. remote_text = remote_text.encode('ascii')
  385. return remote_text in self.stream.read_until(remote_text, self.__read_timeout)
  386.  
  387. def _wait_for_multiple_exact_text(self, remote_options):
  388. for i in xrange(0, len(remote_options)):
  389. remote_options[i] = remote_options[i].encode('ascii')
  390. return self.stream.expect(remote_options, self.__read_timeout)[0]
  391.  
  392.  
  393. def _wait_and_respond(self, remote_text, response, delay_before=0):
  394. response = response.encode('ascii')
  395. if self._wait_for_exact_text(remote_text) == True:
  396. if delay_before != 0:
  397. time.sleep(delay_before)
  398. self.stream.write(response + b'\r\n')
  399. return True
  400. else:
  401. return False
  402.  
  403. def authenticate(self, user, password):
  404. if self._wait_and_respond('Login as:', user) == True and \
  405. self._wait_and_respond('Password:', password, 0.2) == True:
  406. status = self._wait_for_multiple_exact_text([
  407. 'Type "help()" for more information.',
  408. 'Invalid credentials, try again.'])
  409. if status == 0:
  410. return True
  411. elif status == 1:
  412. raise PyboardError('Invalid credentials')
  413. else:
  414. return False
  415.  
  416. def close(self):
  417. self.__socket.shutdown(Telnet_connection.socket.SHUT_RDWR)
  418. self.stream.close()
  419.  
  420. def write(self, data):
  421. self.stream.write(data)
  422. return len(data)
  423.  
  424. def keep_alive(self):
  425. self.__socket.sendall(Telnet_connection.telnetlib.IAC +
  426. Telnet_connection.telnetlib.NOP +
  427. Telnet_connection.telnetlib.NOP)
  428.  
  429. def __expose_stream_methods(self):
  430. self.read_until = self.stream.read_until
  431. self.read_eager = self.stream.read_eager
  432. self.read_some = self.stream.read_some
  433.  
  434. def __separateAdressPort(self, string):
  435. import re
  436. match = re.match(r'([\w.]+)$|([\w.]+):(\d+)$', string)
  437. if match is None:
  438. raise PyboardError('\nInvalid address')
  439. address = match.group(1)
  440. port = 23
  441. if address == None:
  442. address = match.group(2)
  443. port = int(match.group(3))
  444.  
  445. return address, port
  446.  
  447. class Pyboard:
  448. def __init__(self, device, baudrate=115200, user='micro', password='python', wait=0, keep_alive=0):
  449. self.connected = False
  450. if Serial_connection.is_serial_port(device) == True:
  451. self.connection = Serial_connection(device, baudrate=baudrate, timeout=wait)
  452. elif Websock_connection.is_websocket_uri(device):
  453. self.connection = Websock_connection(device)
  454. else:
  455. self.connection = Telnet_connection(device)
  456.  
  457. if self.connection.connected == False:
  458. raise PyboardError('\nFailed to establish a connection with the board at: ' + device)
  459.  
  460. if self.connection.authenticate(user, password) == False:
  461. raise PyboardError('\nFailed to authenticate with the board at: ' + device)
  462.  
  463. if keep_alive != 0:
  464. self.keep_alive_interval = Periodic(keep_alive, self._keep_alive)
  465.  
  466. self.connected = True
  467.  
  468. def close(self):
  469. if self.connected == False:
  470. return
  471. try:
  472. self.keep_alive_interval.stop()
  473. self.connected = False
  474. self.connection.close()
  475. try:
  476. self.__disconnected_callback()
  477. except:
  478. pass
  479. except:
  480. # the connection might not exist, so ignore this one
  481. pass
  482.  
  483. def set_disconnected_callback(self, callback):
  484. self.__disconnected_callback = callback
  485.  
  486. def flush(self):
  487. # flush input (without relying on serial.flushInput())
  488. while self.connection.read_eager():
  489. pass
  490.  
  491. def _keep_alive(self):
  492. if self.connected == False:
  493. return
  494. try:
  495. self.connection.keep_alive()
  496. except:
  497. self.close()
  498.  
  499. def check_connection(self):
  500. self._keep_alive()
  501. return self.connected
  502.  
  503. def read_until(self, ending, timeout=10, data_consumer=None):
  504. if data_consumer == None:
  505. data = self.connection.read_until(ending, timeout)
  506. else:
  507. data = ""
  508. cycles = int(timeout / 0.1)
  509. for i in xrange(1, cycles):
  510. new_data = self.connection.read_until(ending, 0.1)
  511. data = data + new_data
  512. data_consumer(new_data)
  513. if new_data.endswith(ending):
  514. break
  515. return data
  516.  
  517. def enter_raw_repl(self):
  518. self.connection.write(b'\r\x03\x03') # ctrl-C twice: interrupt any running program
  519.  
  520. self.flush()
  521.  
  522. self.connection.write(b'\r\x01') # ctrl-A: enter raw REPL
  523.  
  524. data = self.read_until(b'raw REPL; CTRL-B to exit\r\n>')
  525. if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'):
  526. println("Error could not enter raw repl, data1: %s" % data)
  527. raise PyboardError('could not enter raw repl')
  528.  
  529. self.connection.write(b'\x04') # ctrl-D: soft reset
  530. data = self.read_until(b'soft reboot\r\n')
  531. if not data.endswith(b'soft reboot\r\n'):
  532. println("Error could not enter raw repl, data2: %s" % data)
  533. raise PyboardError('could not enter raw repl')
  534. # By splitting this into 2 reads, it allows boot.py to print stuff,
  535. # which will show up after the soft reboot and before the raw REPL.
  536. data = self.read_until(b'raw REPL; CTRL-B to exit\r\n')
  537. if not data.endswith(b'raw REPL; CTRL-B to exit\r\n'):
  538. println("Error could not enter raw repl, data3: %s" % data)
  539. raise PyboardError('could not enter raw repl')
  540.  
  541. def exit_raw_repl(self):
  542. self.connection.write(b'\r\x02') # ctrl-B: enter friendly REPL
  543.  
  544. def follow(self, timeout, data_consumer=None):
  545. # wait for normal output
  546. data = self.read_until(b'\x04', timeout=timeout, data_consumer=data_consumer)
  547. if not data.endswith(b'\x04'):
  548. raise PyboardError('timeout waiting for first EOF reception')
  549. data = data[:-1]
  550.  
  551. # wait for error output
  552. data_err = self.read_until(b'\x04', timeout=timeout)
  553. if not data_err.endswith(b'\x04'):
  554. raise PyboardError('timeout waiting for second EOF reception')
  555. data_err = data_err[:-1]
  556.  
  557. # return normal and error output
  558. return data, data_err
  559.  
  560. def send(self, data):
  561. data = data.encode('ascii')
  562. try:
  563. self.connection.write(data)
  564. except:
  565. self.close()
  566.  
  567. def recv(self, callback):
  568. import socket
  569.  
  570. while True:
  571. try:
  572. data = self.connection.read_some()
  573. if data:
  574. callback(data)
  575. else:
  576. break
  577. except socket.timeout:
  578. continue
  579. except:
  580. break
  581.  
  582. self.close()
  583.  
  584. def _wait_for_exact_text(self, remote_text):
  585. remote_text = remote_text.decode('ascii')
  586. return remote_text in self.connection.read_until(remote_text)
  587.  
  588. def reset(self):
  589. self.exit_raw_repl()
  590. if not self._wait_for_exact_text(b'Type "help()" for more information.\r\n'):
  591. println("could not enter reset1")
  592. raise PyboardError('could not enter reset')
  593. self.connection.write(b'\x04')
  594. if not self._wait_for_exact_text(b'PYB: soft reboot\r\n'):
  595. println("could not enter reset2")
  596. raise PyboardError('could not enter reset')
  597.  
  598.  
  599. def exec_raw_no_follow(self, command):
  600. if isinstance(command, bytes):
  601. command_bytes = command
  602. else:
  603. command_bytes = command.encode('utf8')
  604.  
  605. # check we have a prompt
  606. data = self.read_until(b'>')
  607. if not data.endswith(b'>'):
  608. raise PyboardError('could not enter raw repl')
  609.  
  610. # write command
  611. for i in range(0, len(command_bytes), 256):
  612. self.connection.write(command_bytes[i:min(i + 256, len(command_bytes))])
  613. time.sleep(0.01)
  614. self.connection.write(b'\x04')
  615.  
  616. # check if we could exec command
  617. data = self.connection.read_until(b'OK')
  618. if data != b'OK':
  619. raise PyboardError('could not exec command')
  620.  
  621. def exec_raw(self, command, timeout=10, data_consumer=None):
  622. self.exec_raw_no_follow(command);
  623. return self.follow(timeout, data_consumer)
  624.  
  625. def eval(self, expression):
  626. ret = self.exec_('print({})'.format(expression))
  627. ret = ret.strip()
  628. return ret
  629.  
  630. def exec_(self, command):
  631. ret, ret_err = self.exec_raw(command)
  632. if ret_err:
  633. raise PyboardError('exception', ret, ret_err)
  634. return ret
  635.  
  636. def execfile(self, filename):
  637. with open(filename, 'rb') as f:
  638. pyfile = f.read()
  639. return self.exec_(pyfile)
  640.  
  641. def get_time(self):
  642. t = str(self.eval('pyb.RTC().datetime()'), encoding='utf8')[1:-1].split(', ')
  643. return int(t[4]) * 3600 + int(t[5]) * 60 + int(t[6])
  644.  
  645. # in Python2 exec is a keyword so one must use "exec_"
  646. # but for Python3 we want to provide the nicer version "exec"
  647. setattr(Pyboard, "exec", Pyboard.exec_)
  648.  
  649. def execfile(filename, device='/dev/ttyACM0', baudrate=115200, user='micro', password='python'):
  650. pyb = Pyboard(device, baudrate, user, password)
  651. pyb.enter_raw_repl()
  652. output = pyb.execfile(filename)
  653. stdout_write_bytes(output)
  654. pyb.exit_raw_repl()
  655. pyb.close()
  656.  
  657. def main():
  658. import argparse
  659. cmd_parser = argparse.ArgumentParser(description='Run scripts on the pyboard.')
  660. cmd_parser.add_argument('--device', default='ws://192.168.76.9:8266', help='the serial device or the IP address of the pyboard')
  661. cmd_parser.add_argument('-b', '--baudrate', default=115200, help='the baud rate of the serial device')
  662. cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username')
  663. cmd_parser.add_argument('-p', '--password', default='anton1009', help='the telnet login password')
  664. cmd_parser.add_argument('-c', '--command', help='program passed in as string')
  665. cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available')
  666. cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]')
  667. cmd_parser.add_argument('files', nargs='*', help='input files')
  668. args = cmd_parser.parse_args()
  669.  
  670. def execbuffer(buf):
  671. try:
  672. pyb = Pyboard(args.device, args.baudrate, args.user, args.password, args.wait)
  673. pyb.enter_raw_repl()
  674. ret, ret_err = pyb.exec_raw(buf, timeout=None, data_consumer=stdout_write_bytes)
  675. pyb.exit_raw_repl()
  676. pyb.close()
  677. except PyboardError as er:
  678. print(er)
  679. sys.exit(1)
  680. except KeyboardInterrupt:
  681. sys.exit(1)
  682. if ret_err:
  683. stdout_write_bytes(ret_err)
  684. sys.exit(1)
  685.  
  686. if args.command is not None:
  687. execbuffer(args.command.encode('utf-8'))
  688.  
  689. for filename in args.files:
  690. with open(filename, 'rb') as f:
  691. pyfile = f.read()
  692. execbuffer(pyfile)
  693.  
  694. if args.follow or (args.command is None and len(args.files) == 0):
  695. try:
  696. pyb = Pyboard(args.device, args.baudrate, args.user, args.password, args.wait)
  697. ret, ret_err = pyb.follow(timeout=10, data_consumer=stdout_write_bytes)
  698. pyb.close()
  699. except PyboardError as er:
  700. print(er)
  701. sys.exit(1)
  702. except KeyboardInterrupt:
  703. sys.exit(1)
  704. if ret_err:
  705. stdout_write_bytes(ret_err)
  706. sys.exit(1)
  707.  
  708. if __name__ == "__main__":
  709. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement