pynrfjprog works, but unable to disconnect from target or emu

I'm migrating a production jig driver application from using multiple calls to an nrfjprog binary to the pynrfjprog library instead. It's gone almost perfectly, but I have a problem I can't figure out: after I've done all the steps--erased, reflashed, and reset the target device--I can't clean up the connection by closing the connection to the target device, or to the J-Link (emu), or to the DLL API in Python. It just hangs forever if I try. The only thing that works instead is killing the process and restarting it.

I'm running a virtualenv (managed by pipenv) of Python 3.11.4 on a Macbook Pro (macOS Sonoma 14.1), with pynrfjprog v10.23.2 (the latest as of this post). I'm flashing a u-blox BMD-340 module built around an nRF52840 chip. I have no issues whatsoever flashing it with other means.

I originally thought the problem was related to some threading that I had in my code, but I pared it down to something as simple as possible, and it still happens. Here's the code that I'm testing with:

import logging

logging.basicConfig(format='%(asctime)s - %(name)s/%(levelname)s - %(message)s', level=logging.INFO)

logging.info("Loading nrfjprog integration module...")
from pynrfjprog import LowLevel

nrf_api = LowLevel.API("NRF52")

logging.info("Opening connection to nrfjprog API")
nrf_api.open()

logging.info("Opening connection to J-Link probe")
nrf_api.connect_to_emu_without_snr()

emu_snr = nrf_api.read_connected_emu_snr()
logging.info("Probe connection established, SNR " + str(emu_snr))

nrf_api.connect_to_device()
device_info = nrf_api.read_device_info()
logging.info("Connected to target device: " + str(device_info))

logging.info("Disconnecting from target device")
nrf_api.disconnect_from_device()

logging.info("Closing connection to J-Link probe")
nrf_api.disconnect_from_emu()

logging.info("Closing connection to nrfjprog API")
nrf_api.close()

logging.info("All nrfjprog connections closed")
exit(0)

When I run this, the output stops before the disconnection from the target device completes. This shows a limited stack trace for where it's stuck after I press Ctrl+C:

JeffMBP:minijig jrowberg$ pipenv run python ./minijig.py
2023-12-29 15:46:03,579 - root/INFO - Loading nrfjprog integration module...
2023-12-29 15:46:03,657 - root/INFO - Opening connection to nrfjprog API
2023-12-29 15:46:03,685 - root/INFO - Opening connection to J-Link probe
2023-12-29 15:46:04,060 - root/INFO - Probe connection established, SNR 820101513
2023-12-29 15:46:04,369 - root/INFO - Connected to target device: (<DeviceVersion.NRF52840_xxAA_REV2: 86523907>, <DeviceName.NRF52840: 86523904>, <DeviceMemory.AA: 1>, <DeviceRevision.REV2: 21>)
2023-12-29 15:46:04,369 - root/INFO - Disconnecting from target device
^CTraceback (most recent call last):
  File "/Users/jrowberg/minijig/./minijig.py", line 20, in <module>
    nrf_api.disconnect_from_device()
  File "/Users/jrowberg/.local/share/virtualenvs/minijig-F0vOpSJS/lib/python3.11/site-packages/pynrfjprog/LowLevel.py", line 719, in disconnect_from_device
    result = self._lib.NRFJPROG_disconnect_from_device_inst(self._handle)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt

If I comment out the call to nrf_api.disconnect_from_device(), then it hangs inside the disconnect_from_emu() call instead. If I comment that out, then it hangs inside the nrf_api object's close() call instead. If I comment all of the clean-up out entirely and let the app attempt to deal with it, like a neanderthal, then the final build-in exit() call never returns, and a Ctrl+C reveals it's still stuck inside self._lib.NRFJPROG_close_dll_inst(ctypes.byref(self._handle)).

How can I fix this? Needing to kill and restart the jig driver script after every device goes through it kinda defeats the purpose.

  • Hi Jeff

    I have forwarded the information to the developers and will update then i hear from them

    Regards

    Runar

  • Could you try the following. 

    with LowLevel.API('NRF52') as api:
        api.connect_to_emu_without_snr()
     
        # Do stuff here...
     
    without any explicit calls to disconnect nor close.

  • Unfortunately, I already tried that approach without luck while attempting to follow the example code on Github more closely. I've just tried again, and the behavior is the same. The new code looks like this:

    import logging
    
    
    logging.basicConfig(format='%(asctime)s - %(name)s/%(levelname)s - %(message)s', level=logging.INFO)
    
    
    logging.info("Loading nrfjprog integration module...")
    from pynrfjprog import LowLevel
    
    
    logging.info("Opening connection to nrfjprog API")
    with LowLevel.API("NRF52") as api:
        logging.info("Opening connection to J-Link probe")
        api.connect_to_emu_without_snr()
        emu_snr = api.read_connected_emu_snr()
        logging.info("Probe connection established, SNR " + str(emu_snr))
        api.connect_to_device()
        device_info = api.read_device_info()
        logging.info("Connected to target device: " + str(device_info))
        
        #logging.info("Disconnecting from target device")
        #api.disconnect_from_device()
        #logging.info("Closing connection to J-Link probe")
        #api.disconnect_from_emu()
        #logging.info("Closing connection to nrfjprog API")
    
    
    logging.info("All nrfjprog connections closed")
    exit(0)

    ...producing the following output. Note where I have to press Ctrl+C again to terminate manually, revealing that it's stuck once again in the same self._lib.NRFJPROG_close_dll_inst(ctypes.byref(self._handle)) call that (presumably) gets executed automatically when the "with" block closes.

    JeffMBP:minijig jrowberg$ pipenv run python ./minijig.py
    2024-01-02 09:20:36,675 - root/INFO - Loading nrfjprog integration module...
    2024-01-02 09:20:36,774 - root/INFO - Opening connection to nrfjprog API
    2024-01-02 09:20:36,918 - root/INFO - Opening connection to J-Link probe
    2024-01-02 09:20:37,301 - root/INFO - Probe connection established, SNR 820101513
    2024-01-02 09:20:37,443 - root/INFO - Connected to target device: (<DeviceVersion.NRF52840_xxAA_REV2: 86523907>, <DeviceName.NRF52840: 86523904>, <DeviceMemory.AA: 1>, <DeviceRevision.REV2: 21>)
    ^CTraceback (most recent call last):
      File "/Users/jrowberg/minijig/./minijig.py", line 9, in <module>
        with LowLevel.API("NRF52") as api:
      File "/Users/jrowberg/.local/share/virtualenvs/minijig-F0vOpSJS/lib/python3.11/site-packages/pynrfjprog/LowLevel.py", line 2480, in __exit__
        self.close()
      File "/Users/jrowberg/.local/share/virtualenvs/minijig-F0vOpSJS/lib/python3.11/site-packages/pynrfjprog/LowLevel.py", line 270, in close
        self._lib.NRFJPROG_close_dll_inst(ctypes.byref(self._handle))
    KeyboardInterrupt

    In case it matters (or helps), I've tried this same code with two different J-Link devices; one is a J-Link EDU Mini, and the other is a full J-Link BASE Compact. They do the same thing.

  • Thanks for the update. I have forwarded the new information to the developers

    Regards

    Runar

  • Just a quick question. Which version of Jlink are you using?

    Regards

    Runar

Related