Add script for controlling Nexus MCU via pexpect

Change-Id: Iefbd14346288e53865291c7b871131a364728aea
diff --git a/nexus_mcu.py b/nexus_mcu.py
new file mode 100755
index 0000000..b06d2e4
--- /dev/null
+++ b/nexus_mcu.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright 2023 Google LLC
+#
+# 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.
+
+import argparse
+import sys
+import serial
+from pexpect_serial import SerialSpawn
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Run commands on nexus MCU")
+    parser.add_argument(
+        "--nexus_id",
+        action="store",
+        required=True,
+        help="Two digit numeric ID of the nexus board to control")
+    parser.add_argument("command", action='store')
+    args = parser.parse_args()
+
+    fd = serial.Serial(f"/dev/Nexus-FTDI-{args.nexus_id}-MCU-UART", "115200")
+    port = SerialSpawn(fd)
+    # Redirect all port chatter to stdout buffer since it's in binary mode
+    port.logfile = sys.stdout.buffer
+
+    port.send(f"{args.command}\r\n")
+    reply = port.expect([">", "Unrecognized command: .*\r\n>"])
+    if reply:
+        print(f"MCU did not recognize the command \"{args.command}\"")
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/python-requirements.txt b/python-requirements.txt
index e1f095a..5750f55 100644
--- a/python-requirements.txt
+++ b/python-requirements.txt
@@ -32,8 +32,10 @@
 matplotlib
 netifaces
 pandas
+pexpect-serial
 psutil
 pyfzf
+pyserial
 pyyaml
 requests
 robotframework==6.0.2