I have a program JLinkExe
which I can start in Windows/Powershell:
& "C:\Program Files\SEGGER\JLink\JLink.exe" -nogui 1
Or in Linux/WSL:
JLinkExe -nogui 1
In both cases program starts, prints some data and presents a J-Link>
prompt.
SEGGER J-Link Commander V7.88k (Compiled Jul 5 2023 15:02:18)
DLL version V7.88k, compiled Jul 5 2023 15:00:41
Connecting to J-Link via USB...O.K.
Firmware: J-Link STLink V21 compiled Aug 12 2019 10:29:20
Hardware version: V1.00
J-Link uptime (since boot): N/A (Not supported by this model)
S/N: 775087052
VTref=3.300V
Type "connect" to establish a target connection, '?' for help
J-Link>
At this point user needs to start inputting commands (each ending with enter/newline) eventually leading to flashing of some firmware to the target embedded system. The commands that I have to send are:
connect
STM32F429ZI
SWD
4000
erase
loadbin program.elf , 0x0
q
The problem here is that after command 4000
application JLinkExe
spawns a GUI window where user needs to use a mouse to click Accept button.
I want to write a python3
& pytest
script that will automate this so I managed to do this so far:
import pytest
import subprocess
import pyautogui
import sys
import shutil
import os
JLINK_SERIAL = "115081052"
TARGET = "STM32F429ZI"
@pytest.fixture
def canScriptRun() -> None:
# Newline for nicer pytest formatting.
print("\n")
printSeparator()
# Check whether script is ran by superuser / admin.
if (os.name == "nt"):
print("Operating system: NT compliant")
if (os.access("Program Files", os.W_OK) is False):
print("User: not admin (continue)")
else:
print("User: admin")
elif (os.name == "posix"):
print("Operating system: Posix compliant")
if (os.access("/etc/", os.W_OK) is False):
print("User: not superuser (exit)")
sys.exit(0)
else:
print("User: superuser")
printSeparator()
@pytest.fixture
def flash(canScriptRun) -> None:
# JLinkExe child process creation.
if (os.name == "nt"):
p = subprocess.Popen(
[r"C:\Program Files\SEGGER\JLink\JLink.exe", "-nogui", "1"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
text=True,
shell=True
)
elif (os.name == "posix"):
p = subprocess.Popen(
["JLinkExe", "-nogui", "1"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
text=True
)
# Send newline-separated commands to the process & leave the running
# process as it is.
stdout, stderr = p.communicate(
input="connect\nSTM32F429ZI\nS\n4000\n",
timeout=None
)
print(stdout)
print(stderr)
# Locate & click the GUI accept button based on it's screenshot saved in project folder.
buttonCenterLocation = pyautogui.locateCenterOnScreen("accept.png")
pyautogui.move(buttonCenterLocation, 1)
pyautogui.click()
def printSeparator() -> None:
terminalWidth = shutil.get_terminal_size().columns
for i in range(0, terminalWidth):
print('-', end='')
def test_tests(flash) -> None:
# Newline for nicer pytest formatting.
print("\n")
pass
Once command 4000
executes a GUI window appears prompting me to click Accept button (screenshoot), but mouse will not move to that position and will not make a click. This is because when GUI window is displayed JLinkExe
is blocked and script also waits...
Any idea on how to solve this?
This is because subprocess.Popen.communicate(timeout=None)
blocks the python script until the external program terminates, so your pyautogui block is never reached unless the window is closed.
Just call that with a finite timeout, suppress the exception and move on.
Note that this means you won't be able to read stdout and stderr until the process is terminated!
@pytest.fixture
def flash(canScriptRun) -> None:
# ...
try:
p.communicate(
input="connect\nSTM32F429ZI\nS\n4000\n",
timeout=10
)
except subprocess.TimeoutError:
pass
buttonCenterLocation = pyautogui.locateCenterOnScreen("accept.png")
pyautogui.move(buttonCenterLocation, 1)
pyautogui.click()
(sdtout, stderr) = p.communicate()
print(stdout)
print(stderr)
Pytest is really not the adequate resource for what you want to do, which is Robotic Process Automation (RPA)
There are a bunch of RPA libraries for Python, research those instead: https://pypi.org/search/?c=Framework+%3A%3A+Robot+Framework