serial_ext.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import json
  2. import os
  3. import sys
  4. import click
  5. from idf_py_actions.errors import FatalError
  6. from idf_py_actions.global_options import global_options
  7. from idf_py_actions.tools import ensure_build_directory, run_tool, run_target, get_sdkconfig_value
  8. PYTHON = sys.executable
  9. def action_extensions(base_actions, project_path):
  10. def _get_default_serial_port(args):
  11. # Import is done here in order to move it after the check_environment() ensured that pyserial has been installed
  12. try:
  13. import serial.tools.list_ports
  14. esptool_path = os.path.join(os.environ["IDF_PATH"], "components/esptool_py/esptool/")
  15. sys.path.insert(0, esptool_path)
  16. import esptool
  17. ports = list(sorted(p.device for p in serial.tools.list_ports.comports()))
  18. esp = esptool.get_default_connected_device(serial_list=ports, port=None, connect_attempts=3,
  19. initial_baud=args.baud)
  20. return esp.serial_port
  21. except Exception:
  22. raise FatalError("No serial ports found. Connect a device, or use '-p PORT' option to set a specific port.")
  23. def _get_esptool_args(args):
  24. esptool_path = os.path.join(os.environ["IDF_PATH"], "components/esptool_py/esptool/esptool.py")
  25. if args.port is None:
  26. args.port = _get_default_serial_port(args)
  27. result = [PYTHON, esptool_path]
  28. result += ["-p", args.port]
  29. result += ["-b", str(args.baud)]
  30. with open(os.path.join(args.build_dir, "flasher_args.json")) as f:
  31. flasher_args = json.load(f)
  32. extra_esptool_args = flasher_args["extra_esptool_args"]
  33. result += ["--before", extra_esptool_args["before"]]
  34. result += ["--after", extra_esptool_args["after"]]
  35. result += ["--chip", extra_esptool_args["chip"]]
  36. if not extra_esptool_args["stub"]:
  37. result += ["--no-stub"]
  38. return result
  39. def _get_commandline_options(ctx):
  40. """ Return all the command line options up to first action """
  41. # This approach ignores argument parsing done Click
  42. result = []
  43. for arg in sys.argv:
  44. if arg in ctx.command.commands_with_aliases:
  45. break
  46. result.append(arg)
  47. return result
  48. def monitor(action, ctx, args, print_filter, monitor_baud, encrypted):
  49. """
  50. Run idf_monitor.py to watch build output
  51. """
  52. desc_path = os.path.join(args.build_dir, "project_description.json")
  53. if not os.path.exists(desc_path):
  54. ensure_build_directory(args, ctx.info_name)
  55. with open(desc_path, "r") as f:
  56. project_desc = json.load(f)
  57. elf_file = os.path.join(args.build_dir, project_desc["app_elf"])
  58. if not os.path.exists(elf_file):
  59. raise FatalError("ELF file '%s' not found. You need to build & flash the project before running 'monitor', "
  60. "and the binary on the device must match the one in the build directory exactly. "
  61. "Try '%s flash monitor'." % (elf_file, ctx.info_name), ctx)
  62. idf_monitor = os.path.join(os.environ["IDF_PATH"], "tools/idf_monitor.py")
  63. monitor_args = [PYTHON, idf_monitor]
  64. esp_port = args.port or _get_default_serial_port(args)
  65. monitor_args += ["-p", esp_port]
  66. if not monitor_baud:
  67. monitor_baud = os.getenv("IDF_MONITOR_BAUD") or os.getenv("MONITORBAUD") or project_desc["monitor_baud"]
  68. monitor_args += ["-b", monitor_baud]
  69. monitor_args += ["--toolchain-prefix", project_desc["monitor_toolprefix"]]
  70. coredump_decode = get_sdkconfig_value(project_desc["config_file"], "CONFIG_ESP_COREDUMP_DECODE")
  71. if coredump_decode is not None:
  72. monitor_args += ["--decode-coredumps", coredump_decode]
  73. target_arch_riscv = get_sdkconfig_value(project_desc["config_file"], "CONFIG_IDF_TARGET_ARCH_RISCV")
  74. if target_arch_riscv:
  75. monitor_args += ["--decode-panic", "backtrace", "--target", project_desc["target"]]
  76. if print_filter is not None:
  77. monitor_args += ["--print_filter", print_filter]
  78. monitor_args += [elf_file]
  79. if encrypted:
  80. monitor_args += ['--encrypted']
  81. idf_py = [PYTHON] + _get_commandline_options(ctx) # commands to re-run idf.py
  82. monitor_args += ["-m", " ".join("'%s'" % a for a in idf_py)]
  83. if "MSYSTEM" in os.environ:
  84. monitor_args = ["winpty"] + monitor_args
  85. run_tool("idf_monitor", monitor_args, args.project_dir)
  86. def flash(action, ctx, args):
  87. """
  88. Run esptool to flash the entire project, from an argfile generated by the build system
  89. """
  90. ensure_build_directory(args, ctx.info_name)
  91. esp_port = args.port or _get_default_serial_port(args)
  92. run_target(action, args, {"ESPBAUD": str(args.baud),"ESPPORT": esp_port})
  93. def erase_flash(action, ctx, args):
  94. ensure_build_directory(args, ctx.info_name)
  95. esptool_args = _get_esptool_args(args)
  96. esptool_args += ["erase_flash"]
  97. run_tool("esptool.py", esptool_args, args.build_dir)
  98. def global_callback(ctx, global_args, tasks):
  99. encryption = any([task.name in ("encrypted-flash", "encrypted-app-flash") for task in tasks])
  100. if encryption:
  101. for task in tasks:
  102. if task.name == "monitor":
  103. task.action_args["encrypted"] = True
  104. break
  105. baud_rate = {
  106. "names": ["-b", "--baud"],
  107. "help": "Baud rate for flashing.",
  108. "scope": "global",
  109. "envvar": "ESPBAUD",
  110. "default": 460800,
  111. }
  112. port = {
  113. "names": ["-p", "--port"],
  114. "help": "Serial port.",
  115. "scope": "global",
  116. "envvar": "ESPPORT",
  117. "default": None,
  118. }
  119. serial_actions = {
  120. "global_action_callbacks": [global_callback],
  121. "actions": {
  122. "flash": {
  123. "callback": flash,
  124. "help": "Flash the project.",
  125. "options": global_options + [baud_rate, port],
  126. "order_dependencies": ["all", "erase_flash"],
  127. },
  128. "erase_flash": {
  129. "callback": erase_flash,
  130. "help": "Erase entire flash chip.",
  131. "options": [baud_rate, port],
  132. },
  133. "monitor": {
  134. "callback":
  135. monitor,
  136. "help":
  137. "Display serial output.",
  138. "options": [
  139. port, {
  140. "names": ["--print-filter", "--print_filter"],
  141. "help":
  142. ("Filter monitor output. "
  143. "Restrictions on what to print can be specified as a series of <tag>:<log_level> items "
  144. "where <tag> is the tag string and <log_level> is a character from the set "
  145. "{N, E, W, I, D, V, *} referring to a level. "
  146. 'For example, "tag1:W" matches and prints only the outputs written with '
  147. 'ESP_LOGW("tag1", ...) or at lower verbosity level, i.e. ESP_LOGE("tag1", ...). '
  148. 'Not specifying a <log_level> or using "*" defaults to Verbose level. '
  149. 'Please see the IDF Monitor section of the ESP-IDF documentation '
  150. 'for a more detailed description and further examples.'),
  151. "default":
  152. None,
  153. }, {
  154. "names": ["--monitor-baud", "-B"],
  155. "type":
  156. click.INT,
  157. "help": ("Baud rate for monitor. "
  158. "If this option is not provided IDF_MONITOR_BAUD and MONITORBAUD "
  159. "environment variables and project_description.json in build directory "
  160. "(generated by CMake from project's sdkconfig) "
  161. "will be checked for default value."),
  162. }, {
  163. "names": ["--encrypted", "-E"],
  164. "is_flag": True,
  165. "help": ("Enable encrypted flash targets. "
  166. "IDF Monitor will invoke encrypted-flash and encrypted-app-flash targets "
  167. "if this option is set. This option is set by default if IDF Monitor was invoked "
  168. "together with encrypted-flash or encrypted-app-flash target."),
  169. }
  170. ],
  171. "order_dependencies": [
  172. "flash",
  173. "encrypted-flash",
  174. "partition_table-flash",
  175. "bootloader-flash",
  176. "app-flash",
  177. "encrypted-app-flash",
  178. ],
  179. },
  180. "partition_table-flash": {
  181. "callback": flash,
  182. "help": "Flash partition table only.",
  183. "options": [baud_rate, port],
  184. "order_dependencies": ["partition_table", "erase_flash"],
  185. },
  186. "bootloader-flash": {
  187. "callback": flash,
  188. "help": "Flash bootloader only.",
  189. "options": [baud_rate, port],
  190. "order_dependencies": ["bootloader", "erase_flash"],
  191. },
  192. "app-flash": {
  193. "callback": flash,
  194. "help": "Flash the app only.",
  195. "options": [baud_rate, port],
  196. "order_dependencies": ["app", "erase_flash"],
  197. },
  198. "encrypted-app-flash": {
  199. "callback": flash,
  200. "help": "Flash the encrypted app only.",
  201. "order_dependencies": ["app", "erase_flash"],
  202. },
  203. "encrypted-flash": {
  204. "callback": flash,
  205. "help": "Flash the encrypted project.",
  206. "order_dependencies": ["all", "erase_flash"],
  207. },
  208. },
  209. }
  210. return serial_actions