nRF-PPK2 gives unreliable current readings

I'm using the nRF-PPK2 in ampere mode to measure power utilization in a battery powered device over a long period of time (about 16 hours). However, the PPK2 is giving unreliable measurements, as if it is not always sure exactly which measurement resolution it's using. Note, I am using the IRNAS python library for this extended duration (due to PPK2 lag mentioned in another ticket) but I saw similar behavior using the Power Profiler software, so it's either a firmware issue or the two fall prey to the same improper decoding bug.

Anyway, what I expect to see is utilization the starts at around 3.8mA (average) and slowly increases until the thing dies (I'm directly in the battery path, so the battery voltage is not constant). What I see instead is a couple of small discrete jumps (which may or may not be real), a really excessive jump to ~9.5A for an hour (which would probably make my device explode) then a sustained load from 15-18mA for four hours or so (unrealistic utilization given the hardware and battery capacity). Sometimes it's correctable (when it was reading >9A I stopped and started the PPK2 collection and it went back to normal) but other times it's not (resetting during the 15-18mA draw period did not change the return).

I provide two plots below of the observed behavior (Fig 1 includes the crazy 9.5A jump, Fig 2 includes the unrealistic average 15-18mA draw. Note, there's less than the 16 hours I mentioned because I overwrote some data when I restarted the PPK2 collection. Whoops...)

Now, the 15-18mA draw could be accurate, I'm not completely convinced that it was just a resolution issue since the jump didn't come with a factor of 10 increase over what I expected (moving from 5 to 50 µA resolution). That would be very concerning. However, it is completely unrealistic. The device only has a 90mAh battery, which would barely maybe be able to run through the 8 hours in the figures below, let alone the other 8 hours it was running! So something is very much off with the measurement.

How can I debug this? Is it possible my device is defective, or has an old / buggy firmware? Is it possible to output additional data points like the measured voltage and which measurement resolution is in use?

Figure 1: Average current. Drawing "9.5A" for almost 1 hour.

Figure 2: Average current, clipped to [0,20]mA. Likely incorrect > 3.5 hours.

Parents Reply Children
  • Hi, sorry for the late reply. I took over this case from Håkon which is out of office.

    Did you connect the PPK GND line to the GND on the DUT? It may sound strange, but you actually need three wires for getting a stable reading in ampere meter mode on the PPK2. Vout, Vin and GND.

  • Stian, I have it connected from the battery -> PPK Vin, then PPK Vin -> board's power input, then both ground pins are connected to ground. Does that sound right?

  • Hi, yes this is the correct setup.

    Have you run this test several times, and do you see the same thing every time?

    Do you know the battery voltage when this 9.5A occurs? At the moment I don't have any ideas how this can happen. I think I would need to run the same tests here, and try to recreate the problem. Do you think you can share your python script? And what type of battery are you using?

    Thanks,
    Stian

  • I hadn't run this specific test multiple times, but I have observed it more than once. Not often, though, because I haven't conducted one of these long-duration tests in a while. I'll have some coming up pretty soon though, I'll let you know if there are more issues.

    In the mean time, here is the python script I used for data collection. This will log data to disk at 100kHz in real time on reasonable hardware (I ran it on a three year old i7 quad-core mac). It will collect data first with the DUT off to clear anything in the buffer, then run until you send it a keyboard interrupt, and finally collect any remaining data from the buffer. Status output at 1Hz.

    import time
    from ppk2_api.ppk2_api import PPK2_API, PPK2_MP
    from tables import *
    import numpy as np
    import traceback
    import logging
    
    
    logging.basicConfig(
        handlers=[
            logging.FileHandler("power_collector.log"),
            logging.StreamHandler()
        ],
        datefmt='%H:%M:%S',
        format='{name:.<15} {asctime}: [{levelname}] {message}',
        style='{', level=logging.INFO)
    log = logging.getLogger("main")
    
    
    def avg_current(N, s, dt):
        if N == 0: return 'No samples'
        avg = s/N
        unit = 'µA'
        if avg < 1:
            avg *= 1000
            unit = 'nA'
        elif avg > 1000:
            avg /= 1000
            unit = 'mA'
        f = N/dt
        funit = ''
        if f > 1000:
            f /= 1000
            funit = 'k'
        log.info(f'{N:10} samples ~ {f:7.2f}{funit}Hz ~ avg: {avg:7.2f}{unit}')
    
    def doit():
        sampling_rate = 100000              # 100 ksps
        measurement_duration = 60*60*16     # 16 hours
    
        filters = Filters(complib="lzo", complevel=1)
        h5 = open_file(
            "power_samples.h5.lzo", mode="w", title="Samples", filters=filters)
        data = h5.create_earray(
            "/", "samples", Atom.from_dtype(np.dtype("f4")),
            shape=(0,),
            filters=filters,
            expectedrows=sampling_rate*measurement_duration)
    
        ppk2s_connected = PPK2_API.list_devices()
        if(len(ppk2s_connected) == 1):
            ppk2_port = ppk2s_connected[0]
            log.info(f'Found PPK2 at {ppk2_port}')
        else:
            log.error(f'Too many connected PPK2\'s: {ppk2s_connected}')
            exit()
    
        ppk2 = PPK2_API(ppk2_port)
        if not ppk2.get_modifiers():
            raise Exception("Invalid modifiers!")
        ppk2.use_ampere_meter()         # set ampere meter mode
        ppk2.toggle_DUT_power("OFF")    # disable DUT power
        ppk2.set_source_voltage(3700)   # Changing, connected directly to battery
    
        ppk2.ser.timeout = 0
    
        ppk2.start_measuring()  # start measuring
    
        log.info("DUT off, starting collection.")
    
        # measurements are a constant stream of bytes
        # multiprocessing variant starts a process in the background which constantly
        # polls the device in order to prevent losing samples. It will buffer the
        # last 10s (by default) of data so get_data() can be called less frequently.
        N = 0
        s = 0
        i = 0
        t0 = time.time()
        while True:
            read_data = ppk2.get_data()
            if len(read_data) > 0:
                samples = ppk2.get_samples(read_data)
                data.append(samples)
    
                N += len(samples)
                s += sum(samples)
                t1 = time.time()
                dt = t1-t0
                if dt > 1:
                    avg_current(N, s, dt)
                    N = 0
                    s = 0
                    i += 1
                    if i == 5: break
                    t0 = t1
    
    
        ppk2.toggle_DUT_power("ON")
    
        log.info("DUT ON")
        try:
            N = 0
            s = 0
            t0 = time.time()
            while True:
                read_data = ppk2.get_data()
                if len(read_data) > 0:
                    samples = ppk2.get_samples(read_data)
                    data.append(samples)
    
                    N += len(samples)
                    s += sum(samples)
                    t1 = time.time()
                    dt = t1-t0
                    if dt > 1:
                        avg_current(N, s, dt)
                        N = 0
                        s = 0
                        t0 = t1
    
        except KeyboardInterrupt:
            log.info("Interrupted, stopping.")
        except:
            log.error(traceback.format_exc())
    
        ppk2.stop_measuring()
        ppk2.toggle_DUT_power("OFF")
    
        log.info("DUT OFF. Reading any remaining data...")
        N = 0
        s = 0
        t0 = time.time()
        while True:
            read_data = ppk2.get_data()
            if len(read_data) > 0:
                samples = ppk2.get_samples(read_data)
                data.append(samples)
    
                N += len(samples)
                s += sum(samples)
                t1 = time.time()
                dt = t1-t0
            else:
                avg_current(N, s, dt)
                N = 0
                s = 0
                t0 = t1
                break
    
        log.info("Done, flushing data to disk...")
    
        data.flush()
        h5.close()
    
    if __name__ == '__main__':
        doit()

  • Thanks for the script. I can set up a test here with that script, let it run for a day or two, and see if I can reproduce the issue. There has been a few updates to both the PPK2 firmware and the desktop software since last time, so the problem might have disappeared. Let me know if you see any more issues. Thanks

Related