update-wipy.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/env python
  2. """
  3. The WiPy firmware update script. Transmits the specified firmware file
  4. over FTP, and then resets the WiPy and optionally verifies that software
  5. was correctly updated.
  6. Usage:
  7. ./update-wipy.py --file "path_to_mcuimg.bin" --verify
  8. Or:
  9. python update-wipy.py --file "path_to_mcuimg.bin"
  10. """
  11. import sys
  12. import argparse
  13. import time
  14. import socket
  15. from ftplib import FTP
  16. from telnetlib import Telnet
  17. def print_exception(e):
  18. print ('Exception: {}, on line {}'.format(e, sys.exc_info()[-1].tb_lineno))
  19. def ftp_directory_exists(ftpobj, directory_name):
  20. filelist = []
  21. ftpobj.retrlines('LIST',filelist.append)
  22. for f in filelist:
  23. if f.split()[-1] == directory_name:
  24. return True
  25. return False
  26. def transfer_file(args):
  27. with FTP(args.ip, timeout=20) as ftp:
  28. print ('FTP connection established')
  29. if '230' in ftp.login(args.user, args.password):
  30. print ('Login successful')
  31. if '250' in ftp.cwd('/flash'):
  32. if not ftp_directory_exists(ftp, 'sys'):
  33. print ('/flash/sys directory does not exist')
  34. if not '550' in ftp.mkd('sys'):
  35. print ('/flash/sys directory created')
  36. else:
  37. print ('Error: cannot create /flash/sys directory')
  38. return False
  39. if '250' in ftp.cwd('sys'):
  40. print ("Entered '/flash/sys' directory")
  41. with open(args.file, "rb") as fwfile:
  42. print ('Firmware image found, initiating transfer...')
  43. if '226' in ftp.storbinary("STOR " + 'mcuimg.bin', fwfile, 512):
  44. print ('File transfer complete')
  45. return True
  46. else:
  47. print ('Error: file transfer failed')
  48. else:
  49. print ('Error: cannot enter /flash/sys directory')
  50. else:
  51. print ('Error: cannot enter /flash directory')
  52. else:
  53. print ('Error: ftp login failed')
  54. return False
  55. def reset_board(args):
  56. success = False
  57. try:
  58. tn = Telnet(args.ip, timeout=5)
  59. print("Connected via Telnet, trying to login now")
  60. if b'Login as:' in tn.read_until(b"Login as:", timeout=5):
  61. tn.write(bytes(args.user, 'ascii') + b"\r\n")
  62. if b'Password:' in tn.read_until(b"Password:", timeout=5):
  63. # needed because of internal implementation details of the WiPy's telnet server
  64. time.sleep(0.2)
  65. tn.write(bytes(args.password, 'ascii') + b"\r\n")
  66. if b'Type "help()" for more information.' in tn.read_until(b'Type "help()" for more information.', timeout=5):
  67. print("Telnet login succeeded")
  68. tn.write(b'\r\x03\x03') # ctrl-C twice: interrupt any running program
  69. time.sleep(1)
  70. tn.write(b'\r\x02') # ctrl-B: enter friendly REPL
  71. if b'Type "help()" for more information.' in tn.read_until(b'Type "help()" for more information.', timeout=5):
  72. tn.write(b"import machine\r\n")
  73. tn.write(b"machine.reset()\r\n")
  74. time.sleep(2)
  75. print("Reset performed")
  76. success = True
  77. else:
  78. print("Error: cannot enter friendly REPL")
  79. else:
  80. print("Error: telnet login failed")
  81. except Exception as e:
  82. print_exception(e)
  83. finally:
  84. try:
  85. tn.close()
  86. except Exception as e:
  87. pass
  88. return success
  89. def verify_update(args):
  90. success = False
  91. firmware_tag = ''
  92. def find_tag (tag):
  93. if tag in firmware_tag:
  94. print("Verification passed")
  95. return True
  96. else:
  97. print("Error: verification failed, the git tag doesn't match")
  98. return False
  99. retries = 0
  100. while True:
  101. try:
  102. # Specify a longer time out value here because the board has just been
  103. # reset and the wireless connection might not be fully established yet
  104. tn = Telnet(args.ip, timeout=10)
  105. print("Connected via telnet again, lets check the git tag")
  106. break
  107. except socket.timeout:
  108. if retries < 5:
  109. print("Timeout while connecting via telnet, retrying...")
  110. retries += 1
  111. else:
  112. print('Error: Telnet connection timed out!')
  113. return False
  114. try:
  115. firmware_tag = tn.read_until (b'with CC3200')
  116. tag_file_path = args.file.rstrip('mcuimg.bin') + 'genhdr/mpversion.h'
  117. if args.tag is not None:
  118. success = find_tag(bytes(args.tag, 'ascii'))
  119. else:
  120. with open(tag_file_path) as tag_file:
  121. for line in tag_file:
  122. bline = bytes(line, 'ascii')
  123. if b'MICROPY_GIT_HASH' in bline:
  124. bline = bline.lstrip(b'#define MICROPY_GIT_HASH ').replace(b'"', b'').replace(b'\r', b'').replace(b'\n', b'')
  125. success = find_tag(bline)
  126. break
  127. except Exception as e:
  128. print_exception(e)
  129. finally:
  130. try:
  131. tn.close()
  132. except Exception as e:
  133. pass
  134. return success
  135. def main():
  136. cmd_parser = argparse.ArgumentParser(description='Update the WiPy firmware with the specified image file')
  137. cmd_parser.add_argument('-f', '--file', default=None, help='the path of the firmware file')
  138. cmd_parser.add_argument('-u', '--user', default='micro', help='the username')
  139. cmd_parser.add_argument('-p', '--password', default='python', help='the login password')
  140. cmd_parser.add_argument('--ip', default='192.168.1.1', help='the ip address of the WiPy')
  141. cmd_parser.add_argument('--verify', action='store_true', help='verify that the update succeeded')
  142. cmd_parser.add_argument('-t', '--tag', default=None, help='git tag of the firmware image')
  143. args = cmd_parser.parse_args()
  144. result = 1
  145. try:
  146. if args.file is None:
  147. raise ValueError('the image file path must be specified')
  148. if transfer_file(args):
  149. if reset_board(args):
  150. if args.verify:
  151. print ('Waiting for the WiFi connection to come up again...')
  152. # this time is to allow the system's wireless network card to
  153. # connect to the WiPy again.
  154. time.sleep(5)
  155. if verify_update(args):
  156. result = 0
  157. else:
  158. result = 0
  159. except Exception as e:
  160. print_exception(e)
  161. finally:
  162. sys.exit(result)
  163. if __name__ == "__main__":
  164. main()