JLink Remote Server upload fails

Hello PlatformIO Community,

We are trying to use the JLink Remote Server, which you can find here: J-Link Remote Server.

When we click on “Debug”, everything works fine:

  • PlatformIO opens the JLink Commander.
  • It asks for the remote server since it doesn’t find a local USB device.
  • Finally, it starts remote debugging session.

However, when we just click on “Upload”, it tries to find a connected USB device and ultimately fails. Below are the error details:

Configuring upload protoco> l…
AVAILABLE: blackmagic, cmsis-dap, jlink, mbed, stlink
CURRENT: upload_protocol = jlink
Uploading .pio\build\nucleo_l476rg\firmware.bin
SEGGER J-Link Commander V8.12f (Compiled Feb 12 2025 13:39:33)
DLL version V8.12f, compiled Feb 12 2025 13:38:41

J-Link Command File read successfully.
Processing script file…
J-Link>h
J-Link connection not established yet but required for command.
Connecting to J-Link via USB…FAILED: Cannot connect to J-Link.
J-Link>loadbin .pio\build\nucleo_l476rg\firmware.bin, 0x08000000
J-Link connection not established yet but required for command.
Connecting to J-Link via USB…FAILED: Cannot connect to J-Link.
J-Link>r
J-Link connection not established yet but required for command.
Connecting to J-Link via USB…FAILED: Cannot connect to J-Link.
J-Link>q

It would be great if we could also use the PlatformIO upload function with the remote server. Additionally, it would be helpful to know how to specify the IP address of the remote server in the platformio.ini file, so we don’t have to input it every time we start a debugging session.

Thank you for your help! We look forward to your responses.

I discovered a solution by creating an additional script, extra_script.py, inspired by the post here. The procedure is also described in more detail in the documentation in the Custom Debugging section. Further information can be found in the Advanced Scripting section, especially under Construction Environments.

from os import makedirs
from os.path import isdir, join
from platform import system

Import('env')

board = env.BoardConfig()
upload_protocol = env.subst("$UPLOAD_PROTOCOL")


def _jlink_cmd_script(env, source):
    build_dir = env.subst("$BUILD_DIR")
    if not isdir(build_dir):
        makedirs(build_dir)
    script_path = join(build_dir, "upload.jlink")
    commands = [
        "h",
        "loadbin %s, %s" % (source, board.get(
            "upload.offset_address", "0x08000000")),
        "r",
        "q"
    ]
    with open(script_path, "w") as fp:
        fp.write("\n".join(commands))
    return script_path


env.Replace(
    __jlink_cmd_script=_jlink_cmd_script,
    UPLOADER="JLink.exe" if system() == "Windows" else "JLinkExe",
    UPLOADERFLAGS=[
        "ip", "10.0.240.197",
        "-device", board.get("debug", {}).get("jlink_device"),
        "-speed", env.GetProjectOption("debug_speed", "4000"),
        "-if", ("jtag" if upload_protocol == "jlink-jtag" else "swd"),
        "-autoconnect", "1",
        "-NoGui", "0"
    ],
    UPLOADCMD='$UPLOADER $UPLOADERFLAGS -CommanderScript "${__jlink_cmd_script(__env__, SOURCE)}"'   
)
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]

The core issue stems from the default JLink upload script found here.

env.Replace(
  __jlink_cmd_script=_jlink_cmd_script,
  UPLOADER="JLink.exe" if system() == "Windows" else "JLinkExe",
  UPLOADERFLAGS=[
      "-device", board.get("debug", {}).get("jlink_device"),
      "-speed", env.GetProjectOption("debug_speed", "4000"),
      "-if", ("jtag" if upload_protocol == "jlink-jtag" else "swd"),
      "-autoconnect", "1",
      "-NoGui", "1"
  ],
  UPLOADCMD='$UPLOADER $UPLOADERFLAGS -CommanderScript "${__jlink_cmd_script(__env__, SOURCE)}"'
)

upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]

It disables the GUI by setting “-NoGui”, “1”. This prevents the JLink Commander window from appearing, thus making it impossible to manually enter the IP address.

While this custom script from the above solution works as intended, it feels a bit overly complex. Is there a simpler way to overriding or extending the $UPLOADERFLAGS in the platformio.ini file?