| #!/usr/bin/env python3 |
| # Copyright 2020 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Detects attached Teensy boards connected via usb.""" |
| |
| import logging |
| import re |
| import subprocess |
| import typing |
| |
| from pathlib import Path |
| from typing import List |
| |
| import pw_arduino_build.log |
| |
| _LOG = logging.getLogger('teensy_detector') |
| |
| |
| class UnknownArduinoCore(Exception): |
| """Exception raised when a given core can not be found.""" |
| |
| |
| def log_subprocess_output(level, output): |
| """Logs subprocess output line-by-line.""" |
| |
| lines = output.decode('utf-8', errors='replace').splitlines() |
| for line in lines: |
| _LOG.log(level, line) |
| |
| |
| class BoardInfo(typing.NamedTuple): |
| """Information about a connected dev board.""" |
| dev_name: str |
| usb_device_path: str |
| protocol: str |
| label: str |
| arduino_upload_tool_name: str |
| |
| def test_runner_args(self) -> List[str]: |
| return [ |
| "--set-variable", f"serial.port.protocol={self.protocol}", |
| "--set-variable", f"serial.port={self.usb_device_path}", |
| "--set-variable", f"serial.port.label={self.dev_name}" |
| ] |
| |
| |
| def detect_boards(arduino_package_path=False) -> list: |
| """Detect attached boards, returning a list of Board objects.""" |
| |
| teensy_core = Path() |
| if arduino_package_path: |
| teensy_core = Path(arduino_package_path) |
| else: |
| teensy_core = Path("third_party/arduino/cores/teensy") |
| if not teensy_core.exists(): |
| teensy_core = Path( |
| "third_party/pigweed/third_party/arduino/cores/teensy") |
| |
| if not teensy_core.exists(): |
| raise UnknownArduinoCore |
| |
| teensy_device_line_regex = re.compile( |
| r"^(?P<address>[^ ]+) (?P<dev_name>[^ ]+) " |
| r"\((?P<label>[^)]+)\) ?(?P<rest>.*)$") |
| |
| boards = [] |
| detect_command = [(teensy_core / "hardware" / "tools" / |
| "teensy_ports").absolute().as_posix(), "-L"] |
| |
| # TODO(tonymd): teensy_ports -L on windows does not return the right port |
| # string Example: |
| # |
| # $ teensy_ports -L |
| # Port_#0001.Hub_#0003 COM3 (Teensy 3.6) Serial |
| # |
| # So we get "-port=Port_#0001.Hub_#0003" |
| # But it should be "-port=usb:0/140000/0/1" |
| |
| process = subprocess.run(detect_command, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT) |
| if process.returncode != 0: |
| _LOG.error("Command failed with exit code %d.", process.returncode) |
| _LOG.error("Full command:") |
| _LOG.error("") |
| _LOG.error(" %s", " ".join(detect_command)) |
| _LOG.error("") |
| _LOG.error("Process output:") |
| log_subprocess_output(logging.ERROR, process.stdout) |
| _LOG.error('') |
| for line in process.stdout.decode("utf-8", errors="replace").splitlines(): |
| device_match_result = teensy_device_line_regex.match(line) |
| if device_match_result: |
| teensy_device = device_match_result.groupdict() |
| boards.append( |
| BoardInfo(dev_name=teensy_device["dev_name"], |
| usb_device_path=teensy_device["address"], |
| protocol="Teensy", |
| label=teensy_device["label"], |
| arduino_upload_tool_name="teensyloader")) |
| return boards |
| |
| |
| def main(): |
| """This detects and then displays all attached discovery boards.""" |
| |
| pw_arduino_build.log.install(logging.INFO) |
| |
| boards = detect_boards() |
| if not boards: |
| _LOG.info("No attached boards detected") |
| for idx, board in enumerate(boards): |
| _LOG.info("Board %d:", idx) |
| _LOG.info(" - Name: %s", board.label) |
| _LOG.info(" - Port: %s", board.dev_name) |
| _LOG.info(" - Address: %s", board.usb_device_path) |
| _LOG.info(" - Test runner args: %s", |
| " ".join(board.test_runner_args())) |
| |
| |
| if __name__ == "__main__": |
| main() |