summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/monitor.py44
-rw-r--r--tools/reset.py73
-rw-r--r--tools/reset_leonardo.py109
-rw-r--r--tools/upload.sh12
-rw-r--r--typings/serial/serialposix.pyi34
-rw-r--r--typings/serial/serialutil.pyi37
-rw-r--r--typings/serial/serialwin32.pyi34
-rw-r--r--typings/serial/tools/list_ports.pyi16
-rw-r--r--typings/serial/tools/list_ports_common.pyi16
-rw-r--r--typings/serial/tools/list_ports_linux.pyi11
-rw-r--r--typings/serial/tools/list_ports_windows.pyi8
11 files changed, 285 insertions, 109 deletions
diff --git a/tools/monitor.py b/tools/monitor.py
new file mode 100644
index 0000000..f909efb
--- /dev/null
+++ b/tools/monitor.py
@@ -0,0 +1,44 @@
+"""Serial monitor."""
+import argparse
+import sys
+import asyncio
+import os
+
+if os.name == "nt": # NT-based operating systems (Windows)
+ from serial.serialwin32 import Serial
+elif os.name == "posix":
+ from serial.serialposix import Serial
+else:
+ raise NotImplementedError(
+ "Sorry no implementation for your platform ({}) available."
+ .format(sys.platform)
+ )
+
+
+async def read(port: str, baud_rate: int):
+ """Reads a serial port."""
+ with Serial(port, baud_rate) as serial_port:
+ while(serial_port.is_open):
+ sys.stdout.buffer.write(serial_port.read())
+ sys.stdout.flush()
+
+
+async def main():
+ """Monitors serial output."""
+ parser = argparse.ArgumentParser(
+ description="A tool for monitoring a Arduino")
+
+ parser.add_argument(
+ "port", help="A serial device port e.g. /dev/ttyACM0 or com3")
+ parser.add_argument("baud_rate", help="The serial device baud rate")
+
+ args = parser.parse_args()
+
+ await read(args.port, args.baud_rate)
+
+if __name__ == "__main__":
+ try:
+ asyncio.run(main())
+ except KeyboardInterrupt:
+ print("\nReceived keyboard interrupt. Exiting...")
+ exit(0)
diff --git a/tools/reset.py b/tools/reset.py
new file mode 100644
index 0000000..6398c50
--- /dev/null
+++ b/tools/reset.py
@@ -0,0 +1,73 @@
+"""A tool to reset a Arduino."""
+import os
+import sys
+from typing import List
+import argparse
+from time import sleep
+
+import serial.tools.list_ports
+from serial.tools.list_ports_common import ListPortInfo
+
+
+if os.name == "nt": # NT-based operating systems (Windows)
+ from serial.serialwin32 import Serial
+elif os.name == "posix":
+ from serial.serialposix import Serial
+else:
+ raise NotImplementedError(
+ "Sorry no implementation for your platform ({}) available."
+ .format(sys.platform)
+ )
+
+
+def get_ports():
+ """Returns a list of serial ports attached."""
+ serial_ports = serial.tools.list_ports.comports()
+
+ return [port.device for port in serial_ports]
+
+
+def compare_ports(
+ avail_ports: List[str],
+ wanted_ports: List[str]
+):
+ return [port for port in avail_ports if port in wanted_ports]
+
+
+class ParsedArguments:
+ """Parsed command-line arguments."""
+ port: str
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description='Reset an Arduino')
+
+ parser.add_argument('port', help='Serial device e.g. /dev/ttyACM0 or com3')
+
+ args = parser.parse_args(namespace=ParsedArguments)
+
+ ports: List[str] = []
+
+ while args.port not in ports:
+ ports = get_ports()
+
+ sys.stderr.write(
+ f"PORTS {{{', '.join(ports)}}} / {{{args.port}}} "
+ f"=> {{{', '.join(compare_ports(ports, [args.port]))}}}\n"
+ )
+ sys.stderr.flush()
+
+ sleep(0.5)
+
+ ser = Serial(args.port, 57600)
+
+ ser.baudrate = 1200
+
+ # ser.setRTS(True)
+ # ser.setDTR(False)
+
+ ser.close()
+
+ sleep(2)
+
+ print(get_ports()[0])
diff --git a/tools/reset_leonardo.py b/tools/reset_leonardo.py
deleted file mode 100644
index be53aa1..0000000
--- a/tools/reset_leonardo.py
+++ /dev/null
@@ -1,109 +0,0 @@
-"""Tool used to reset a Arduino Leonardo board."""
-from os import O_NDELAY, O_NOCTTY, O_RDWR, open as os_open, close as os_close
-import sys
-from termios import (
- B1200,
- TCSANOW,
- TIOCM_DTR,
- TIOCM_RTS,
- TIOCMBIC,
- TIOCMBIS,
- tcgetattr,
- tcsetattr
-)
-from fcntl import ioctl
-from argparse import ArgumentParser
-from dataclasses import dataclass
-from pathlib import Path
-from struct import pack
-from time import sleep
-
-
-@dataclass
-class ParsedArguments:
- """Parsed command-line arguments."""
- serial_port: Path
-
-
-class TTYAttributes:
- """TTY attributes."""
-
- def __init__(self, tty_fd: int):
- """Initializes the TTY attributes."""
- self.__tty_fd = tty_fd
- self.__attrs = tcgetattr(tty_fd)
-
- @property
- def ispeed(self):
- """The ispeed."""
- return self.__attrs[4]
-
- @ispeed.setter
- def ispeed(self, ispeed: int):
- self.__attrs[4] = ispeed
-
- @property
- def ospeed(self):
- """The ospeed."""
- return self.__attrs[5]
-
- @ospeed.setter
- def ospeed(self, ospeed: int):
- self.__attrs[5] = ospeed
-
- def set(self):
- """Sets the TTY attributes."""
- tcsetattr(self.__tty_fd, TCSANOW, self.__attrs)
-
-
-RESET_BAUD_RATE = B1200
-
-DESCRIPTION = "Tool used to reset a Arduino Leonardo board."
-
-
-def main():
- """Main."""
- parser = ArgumentParser(allow_abbrev=False, description=DESCRIPTION)
-
- parser.add_argument(
- "serial_port",
- type=Path,
- help="The serial port for a Arduino Leonardo board"
- )
-
- arguments = parser.parse_args(namespace=ParsedArguments)
-
- is_serial_port_char_device = arguments.serial_port.is_char_device()
-
- # Verify that the serial port exists
- if not arguments.serial_port.is_file() and not is_serial_port_char_device:
- print("Error: The serial port file doesn't exist")
- sys.exit(1)
-
- # Verify that the serial port is a character device
- if not is_serial_port_char_device:
- print("Error: The serial port file is not a character device")
- sys.exit(1)
-
- serial_port_fd = os_open(
- str(arguments.serial_port),
- O_RDWR | O_NOCTTY | O_NDELAY
- )
-
- tty_attributes = TTYAttributes(serial_port_fd)
-
- tty_attributes.ispeed = RESET_BAUD_RATE
- tty_attributes.ospeed = RESET_BAUD_RATE
-
- tty_attributes.set()
-
- ioctl(serial_port_fd, TIOCMBIS, pack("I", TIOCM_RTS))
- ioctl(serial_port_fd, TIOCMBIC, pack("I", TIOCM_DTR))
-
- os_close(serial_port_fd)
-
- sleep(1.98)
-
-
-if __name__ == "__main__":
- main()
diff --git a/tools/upload.sh b/tools/upload.sh
new file mode 100644
index 0000000..eddf4de
--- /dev/null
+++ b/tools/upload.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+DEVICE_PATH=$1
+PYTHON_CMD=$2
+AVRDUDE=$3
+AVRDUDE_COM_OPTS=$4
+AVRDUDE_ARD_OPTS=$5
+AVRDUDE_UPLOAD_HEX=$6
+
+new_port=$($PYTHON_CMD ./tools/reset.py $DEVICE_PATH 2> /dev/tty)
+
+$AVRDUDE $AVRDUDE_COM_OPTS $AVRDUDE_ARD_OPTS $new_port $AVRDUDE_UPLOAD_HEX
diff --git a/typings/serial/serialposix.pyi b/typings/serial/serialposix.pyi
new file mode 100644
index 0000000..9f41fd2
--- /dev/null
+++ b/typings/serial/serialposix.pyi
@@ -0,0 +1,34 @@
+from typing import Any, Optional
+from serial.serialutil import SerialBase
+
+
+class PlatformSpecificBase:
+ ...
+
+
+class PlatformSpecific(PlatformSpecificBase):
+ ...
+
+
+class Serial(SerialBase, PlatformSpecific):
+ """\
+ Serial port class POSIX implementation. Serial port configuration is
+ done with termios and fcntl. Runs on Linux and many other Un*x like
+ systems.
+ """
+
+ def close(self) -> None:
+ """Close port"""
+ ...
+
+ def read(self, size: Optional[int] = ...) -> bytes:
+ """\
+ Read size bytes from the serial port. If a timeout is set it may
+ return less characters as requested. With no timeout it will block
+ until the requested number of bytes is read.
+ """
+ ...
+
+ def __enter__(self) -> Serial: ...
+
+ def __exit__(self, *args: Any, **kwargs: Any) -> None: ...
diff --git a/typings/serial/serialutil.pyi b/typings/serial/serialutil.pyi
new file mode 100644
index 0000000..d24f7d1
--- /dev/null
+++ b/typings/serial/serialutil.pyi
@@ -0,0 +1,37 @@
+import io
+from typing import Any, Optional
+
+
+class SerialBase(io.RawIOBase):
+ """\
+ Serial port base class. Provides __init__ function and properties to
+ get/set port settings.
+ """
+
+ def __init__(
+ self,
+ port: Optional[str] = ...,
+ baudrate: Optional[int] = ...,
+ **kwargs: Any
+ ) -> None:
+ """Initialize comm port object. If a "port" is given, then the port
+ will be opened immediately. Otherwise a Serial port object in closed
+ state is returned.
+ """
+ ...
+
+ @property
+ def baudrate(self) -> int: ...
+
+ @baudrate.setter
+ def baudrate(self) -> None: ...
+
+ def setRTS(self, value: Optional[bool] = ...) -> None:
+ ...
+
+ def setDTR(self, value: Optional[bool] = ...) -> None:
+ ...
+
+ @property
+ def is_open(self) -> bool:
+ ...
diff --git a/typings/serial/serialwin32.pyi b/typings/serial/serialwin32.pyi
new file mode 100644
index 0000000..5124dd2
--- /dev/null
+++ b/typings/serial/serialwin32.pyi
@@ -0,0 +1,34 @@
+"""
+This type stub file was generated by pyright.
+"""
+
+from typing import Any, Optional
+from serial.serialutil import SerialBase
+
+
+class Serial(SerialBase):
+ """Serial port implementation for Win32 based on ctypes."""
+
+ def __init__(
+ self,
+ port: Optional[str] = ...,
+ baudrate: Optional[int] = ...,
+ **kwargs: Any
+ ) -> None:
+ ...
+
+ def close(self) -> None:
+ """Close port"""
+ ...
+
+ def read(self, size: Optional[int] = ...) -> bytes:
+ """\
+ Read size bytes from the serial port. If a timeout is set it may
+ return less characters as requested. With no timeout it will block
+ until the requested number of bytes is read.
+ """
+ ...
+
+ def __enter__(self) -> Serial: ...
+
+ def __exit__(self, *args: Any, **kwargs: Any) -> None: ...
diff --git a/typings/serial/tools/list_ports.pyi b/typings/serial/tools/list_ports.pyi
new file mode 100644
index 0000000..67df6f6
--- /dev/null
+++ b/typings/serial/tools/list_ports.pyi
@@ -0,0 +1,16 @@
+"""\
+This module will provide a function called comports that returns an
+iterable (generator or list) that will enumerate available com ports. Note that
+on some systems non-existent ports may be listed.
+
+Additionally a grep function is supplied that can be used to search for ports
+based on their descriptions or hardware ID.
+"""
+from typing import Any, List, Optional
+
+from list_ports_common import ListPortInfo
+
+
+def comports(include_links: Optional[Any] = ...) -> List[ListPortInfo]:
+ """Return a list of info objects about serial ports"""
+ ...
diff --git a/typings/serial/tools/list_ports_common.pyi b/typings/serial/tools/list_ports_common.pyi
new file mode 100644
index 0000000..60920d0
--- /dev/null
+++ b/typings/serial/tools/list_ports_common.pyi
@@ -0,0 +1,16 @@
+from typing import Any, Optional
+
+
+class ListPortInfo:
+ """Info collection base class for serial ports"""
+
+ def __init__(
+ self,
+ device: Any,
+ skip_link_detection: Optional[Any] = ...
+ ) -> None:
+ ...
+
+ @property
+ def device(self) -> str:
+ ...
diff --git a/typings/serial/tools/list_ports_linux.pyi b/typings/serial/tools/list_ports_linux.pyi
new file mode 100644
index 0000000..ccb94ed
--- /dev/null
+++ b/typings/serial/tools/list_ports_linux.pyi
@@ -0,0 +1,11 @@
+from typing import Any, List, Optional
+import serial.tools.list_ports_common
+
+
+class SysFS(serial.tools.list_ports_common.ListPortInfo):
+ """Wrapper for easy sysfs access and device info"""
+ ...
+
+
+def comports(include_links: Optional[Any] = ...) -> List[SysFS]:
+ ...
diff --git a/typings/serial/tools/list_ports_windows.pyi b/typings/serial/tools/list_ports_windows.pyi
new file mode 100644
index 0000000..314af5b
--- /dev/null
+++ b/typings/serial/tools/list_ports_windows.pyi
@@ -0,0 +1,8 @@
+from typing import Any, List, Optional
+
+from serial.tools.list_ports_common import ListPortInfo
+
+
+def comports(include_links: Optional[Any] = ...) -> List[ListPortInfo]:
+ """Return a list of info objects about serial ports"""
+ ...