Skip to content

Omnipod Dash Drift resolution#4499

Open
nl-ruud wants to merge 3 commits intonightscout:devfrom
nl-ruud:issue-4158-dev-fix
Open

Omnipod Dash Drift resolution#4499
nl-ruud wants to merge 3 commits intonightscout:devfrom
nl-ruud:issue-4158-dev-fix

Conversation

@nl-ruud
Copy link

@nl-ruud nl-ruud commented Jan 18, 2026

Omnipod Dash basal drift resolution

This pull request introduces a new "basal correction" feature for the Omnipod Dash pump integration. The main goal is to detect under-delivery of basal insulin and automatically trigger a small correction bolus when needed. The implementation tracks delivered basal and bolus pulses, calculates expected vs. actual insulin delivery, and manages correction logic with safety checks and cooldowns.

Recap of the issue

The Omnipod Dash pump exhibits behavior that causes it to deliver less basal insulin than AAPS expects (issue #4158). This is effectively a hardware limitation of the pump.

The Dash uses an internal timer to determine when a basal pulse of 0.05 U is delivered. Once the timer interval elapses, the pulse is delivered. However, this timer is restarted whenever a basal rate change occurs (and possibly also when a bolus or SMB is delivered).

When used in combination with looping, this leads to structural under-delivery of basal insulin, as the algorithm adjusts the basal rate frequently. While the Medtrum pump compensates for this behavior internally, the Dash does not, and neither does the current Dash driver implementation in AAPS.

The issue is most apparent during the night. During daytime operation, SMBs often result in a basal rate of 0, which masks the effect. In observed usage, this results in approximately 10% of the expected Total Daily Dose (TDD) not being delivered over a 24-hour period. Additionally, glucose targets are often not reached overnight, particularly after meals with prolonged glucose impact (e.g. pasta).

Approach

This pull request introduces automatic basal drift compensation by correcting the pump lag with small correction boluses. A correction bolus of 0.05 U is delivered as soon as the detected basal deficit reaches 0.025 U. Several safety constraints are applied. For example, corrections are not delivered during a temporary basal of 0 unless that temporary basal is the direct result of a recently delivered bolus.

With this approach, the deviation between expected and delivered basal insulin is kept within a bounded range of approximately −0.050 U to +0.025 U.

With this fix applied:

  • The TDD reported by AAPS (under Statistics) matches the total amount actually delivered by the pump (Dash menu → Pod managementHistory).
  • Overnight glucose targets are consistently reached.

Without this fix, a clear discrepancy between reported and delivered TDD can be observed.

Main Changes

Basal Correction Feature

  • Added a new custom command, CommandDeliverBasalCorrection, and integrated it into the command queue and handling logic in OmnipodDashPumpPlugin to trigger a basal correction bolus when under-delivery is detected.
  • Implemented the deliverBasalCorrection method to handle the actual delivery of the correction bolus, including safety checks (reservoir level, bolus in progress, cooldowns, etc.).

Basal Delivery Tracking and Drift Calculation

  • Added logic in OmnipodDashPodStateManagerImpl to track basal and bolus pulses separately, compute actual delivered basal, and calculate "drift" (difference between expected and actual basal delivery). This includes new fields in the pod state and methods for integrating expected delivery over time.
  • The needsBasalCorrection method determines when a correction is needed based on drift, cooldowns, and other safety criteria.

Pod State Update and Logging Refactor

  • Refactored pod state update logic to centralize updates in a new updatePodState method, which also updates basal tracking fields and logs basal delivery/drift for debugging. This method is now used in both default and alarm status response handlers and removes code duplication from these methods.

Logging example:

PUMP_BASAL act=8,10U (tot=33,25U bol=25,15U) exp=9,42U err=-1,32U dErr=-0,02U

Legend for the log fields:

Field Description
act Actual basal units delivered (totbol)
tot Total units delivered
bol Bolus units delivered
exp Expected basal units delivered
err Error / cumulative basal drift (act - exp)
dErr Delta of err on this pump status response

Interface and State Enhancements

  • Updated the OmnipodDashPodStateManager interface and its implementation to support new fields and methods for basal correction tracking (lastBasalCorrectionTime, basalCorrectionInProgress, needsBasalCorrection).

These changes together enable the system to automatically detect and correct small basal under-deliveries, improving insulin delivery reliability and safety.

Tests Performed

To verify the fix, we compared the total insulin delivered at 00:00 at the start of a day with the total at 00:00 at the end of the same day.

Before this fix, the difference consistently resulted in a lower number than the TDD reported by AAPS, confirming under-delivery. After applying the fix, this discrepancy no longer occurs: AAPS TDD matches the pump’s delivered total.

@vanelsberg
Copy link
Contributor

This PR contains unrelated commits from the dev branche, which I think is undesired?
Not an expert on this: maybe consult with @MilosKozak on how to fix.

@vanelsberg
Copy link
Contributor

vanelsberg commented Jan 19, 2026

In addition, it is stated: "It should be reviewed on top of PR #4467"
This makes merging this PR with AAPS-CI impossible. Is there a reason this PR's are separate?
Please consider combining PR#4467 and this PR into one?

@vanelsberg
Copy link
Contributor

vanelsberg commented Jan 19, 2026

Nice 👍
Will need to review and test implementation in detail and test to see how this PR does.

@vanelsberg
Copy link
Contributor

This PR contains lots of file changes and commits.
To enable proper reviewing, please consider squashing all commits into one?

@nl-ruud nl-ruud force-pushed the issue-4158-dev-fix branch 2 times, most recently from df5aa07 to 189629d Compare January 19, 2026 09:21
@nl-ruud
Copy link
Author

nl-ruud commented Jan 19, 2026

@vanelsberg I think I fixed it, there are two commits now in this PR: the first one equals 4467 (quantifying the basal drift) and the second one is resolving the basal drift. does this make sense? I think that also would mean we can close #4467 as the distinction is now just clear in this PR.

@vanelsberg
Copy link
Contributor

vanelsberg commented Jan 19, 2026

I think I fixed it, there are two commits now in this PR....

Yes! This looks a lot better, making it easier to follow implementation. Helps reviewing/testing and - eventually - when ok, acceptance for merging with dev.

(When 4467 is no longer of use, I think best to close it to prevent confusion?)

@nl-ruud nl-ruud force-pushed the issue-4158-dev-fix branch 2 times, most recently from 6b01383 to 189629d Compare January 26, 2026 09:14
@vanelsberg
Copy link
Contributor

vanelsberg commented Feb 3, 2026

@nl-ruud Proposed way to testing this PR:

To see if AAPS is affected first test this whiteout the PR, preferably for 2 or more days. Then merge this PR and retest with current AAPS (this can be current dev or current 3.4 release).

Test:

  1. Check the pod history on the DASH tab.
    Note "Total insulin delivered" at two points in time for a specific day: At 00:00h and 24:00h; the difference is what the Pod reports as insulin delivered for that day period.

  2. Check AAPS TDD statistics
    For that same day (2), check on what is reported as the sum of insulin (first column at the right of the date).

Validation:
"Total insulin delivered" from (1) should closely match what is reported as TDD. Expect a difference no more than 0.05U

Optional, check the AAPS logfiles, like filtering on the terms below:
grep -E 'PUMP_BASAL|deliverBasalCorrection|calculateBolusPulseIncrease|integrateExpectedDelivery' *

@nl-ruud
Copy link
Author

nl-ruud commented Feb 4, 2026

Expect a difference no more than 0.5U

Actually no more than 0.05U. If a bigger difference is perceived then I’d love to see the AAPS logs of that 24 hour period.

Also, the docs currently state “When using SMB, limit the interval to 5 minutes minimum to avoid this issue.”. Even though that does not avoid the issue (as stated there), it may reduce the severity. So reset that setting to default (i believe it’s 3 minutes) before you test if you followed that direction from the docs. Especially if you use a sensor like FSL that updates every minute.

@vanelsberg
Copy link
Contributor

vanelsberg commented Feb 5, 2026

Have been testing/reviewing this PR for the past 4 days (PR merged with current 3.4 dev):
Code looks good.

Measuring insulin drift comparing what AAPS reports on statistics/TDD with actual "insulin delivered" as reported on the DASH pod history. Differences measured are in expected range and tolerance of 0.00..0.05U 👍

Note:
Did need to do some approximations on actual insulin delivered as reported by Pod history for running TBR's at 00:00. For example: TBR running from 23:55h to 00:05 of 0.5U/h, adjust insulin delivered at 23:55 with 0.25 units

results

Now testing for 4 days (similar circumstances, identical Profile) for AAPS without PR4499.
Will share final test and code review results.

@xitation
Copy link

xitation commented Feb 5, 2026

I've cherry picked the commits into Master (3.4.0.0) and installed updated apk with fix applied tonight.

I saw a drift of about 2U of insulin before patch. Will post back findings tomorrow with patch installed.

image

@xitation
Copy link

xitation commented Feb 5, 2026

So far seems to be ok, however it's been beeping when making corrections. I have the Bolus alert / beep enabled only. Annoyed my wife overnight :)

Perhaps could look at using SMB/TBR or one of the other alert beeps for this instead of the Bolus one, I suspect a number of people would leave the Bolus beep enabled.

image

@olorinmaia
Copy link
Contributor

olorinmaia commented Feb 5, 2026

Thanks for looking into this issue and providing a promising fix.

I will also assist with testing. Will log TDD for Dash and AAPS calculated like this:
image

I will first run without this PR to see for myself the difference between Dash TDD and AAPS TDD, I haven't had time to check before now.

I will after a day or two apply PR by cherry-picking commits onto my master build and report back with result. My son only got around 15-20TDD, depending on activity, should I expect to see a big total diff without this PR?

@rickvh3
Copy link

rickvh3 commented Feb 6, 2026

With the fix, I notice that the loop has become more consistent and shows less unpredictable behavior. I’ve been using this since January 23 (14 days now), and I’m satisfied with the result.

@nl-ruud
Copy link
Author

nl-ruud commented Feb 7, 2026

So far seems to be ok, however it's been beeping when making corrections. I have the Bolus alert / beep enabled only. Annoyed my wife overnight :)

If you cherry pick 254dfe3 then the beeps should be gone. Will wait for the others to complete testing and then add a final commit for the PR.

@vanelsberg
Copy link
Contributor

vanelsberg commented Feb 7, 2026

Confirming beeps for correction bolus. Noticed this only twice during testing: 1 unexpected beep (ignored it) and unexpected bolus notification for 0.05U on Watch. The last one triggered some questions (forgot all about this when posting test results on TDD deviations).

Currently starting to test DASH without PR4499 under similar circumstances. After 4 days testing I'l post results here.

.... If you cherry pick [254dfe3] then the beeps should be gone.

@nl-ruud You may want to check if your code change not only skips the beep but also the notification?
Once you added this to the PR I'll retest for "no beeps" and no "notification" and post final test results.

@olorinmaia
Copy link
Contributor

Posting results from logging total insulin delivery for two days, one without PR and one with PR. No issues so far.

We got all beeps disabled. No issues when they are disabled either.

image

@xitation
Copy link

xitation commented Feb 8, 2026

So far seems to be ok, however it's been beeping when making corrections. I have the Bolus alert / beep enabled only. Annoyed my wife overnight :)

If you cherry pick 254dfe3 then the beeps should be gone. Will wait for the others to complete testing and then add a final commit for the PR.

Seems to have resolved beeping, thanks!

In terms of test data here are my results so far:

image

@belevine
Copy link

I have tested this PR for several days now on v3.4. Before building with the PR, I observed a drift of 1.5-2.5 units per day. After the PR, I'm getting no drift or 0.05 units (which I suspect may be more due to a challenge in determining the exact Dash delivered amounts at midnight if the only timestamps are 5-10 mins before or after).

I also have manually bolused while testing the PR and found this also was reported correctly. The AAPS statistics included the manual bolus while the Dash history did not.

@nl-ruud
Copy link
Author

nl-ruud commented Feb 12, 2026

I just submitted a (final?) update to the PR that addresses two issues that were reported by @xitation, which are:

  • no more beeps
  • reduced the cooldown period from 5 minutes to 2 minutes to avoid drift reset

@vanelsberg
Copy link
Contributor

Test without this PR:
image
⚠️Did not trust the high drift values calculated for 08/09 and 09/02: doublecheck my math, but they are correct. Nothing unusual to see in basal rates and.... then I realized I did manual bolus at the time by PEN ("only registering"). To my surprise in AAPS treatments a "register only" bolus is indistinguishable from a normal "meal" type, So I cannot check statistics. No logfiles either as they were already deleted.

As far sa I can remember these manual boluses very likely did amount to 4U. Actual drift values would match expected values?

Test with PR4490:
image
Except for the bolus beeps (which is now fixed) I did not encounter any problems with PR4499. My tests as reported above confirm reported insulin drift for current DASH driver which is effectively fixed with this PR.

Reviewing code for PR4499 with fix I did not see any problems.

@vanelsberg
Copy link
Contributor

I just submitted a (final?) update to the PR

Will retest this for a few days.

@olorinmaia
Copy link
Contributor

olorinmaia commented Feb 13, 2026

Applied latest commit but so far only 0,05+- difference between pump and AAPS TDD. I think this PR is ready for merge, but will keep monitoring / logging. CC: @MilosKozak

image

@vanelsberg
Copy link
Contributor

vanelsberg commented Feb 13, 2026

I think this PR is ready for merge...

100% Agree.

@MilosKozak Ik think this is an important bugfix, affects users especially when on lower TDD (like for kids on DASH)
After merge and testing on dev, we should consider making this a bugfix on 3.4 release?

@vanelsberg
Copy link
Contributor

vanelsberg commented Feb 16, 2026

My final review / food for thought: (feel free to correct me on this!):

(Edit) Post was not relevant for this PR. Moved to separate issue.

@crimsonkage
Copy link

Been testing this for a couple days, it works great.
Some background is that I'm a 1 minute looper using Auto ISF.

Because of the frequency of the loops I was really susceptible to this.
My basal is 24 units a day, but I was missing at least 6 of that. (25% off)
It appears that my profile was boosted by 20% to compensate for the missing basal and it causes some instability in my profile.

I'm still dialing it back down and it appears that my profile is matching what my profile was before starting on AAPS.

A++++ needed fix for 1 minute loopers.

No instability or crashes or pump errors introduced that I can see.

@crimsonkage
Copy link

One other comment on this.

Everyone using a dash will need to be careful and update their profile when installing an AAPS version with this fix included.

My profile basal has dropped over 20% at this point.
(1 minute looper so caused a bigger drift)

I think this will need to be mentioned pretty high up the patch notes / release announcement to get attention.

@nl-ruud
Copy link
Author

nl-ruud commented Feb 19, 2026

Everyone using a dash will need to be careful and update their profile when installing an AAPS version with this fix included.

I think this statement is somewhat too strong. I can imagine the basal drift kind of slipped into users profiles over time, especially when using tools like autotune. However, the underlying issue would still be that the profile itself was inaccurate. If the profile had originally been established using proper basal and/or ISF testing with the loop disabled, it should remain valid.

I think this will need to be mentioned pretty high up the patch notes / release announcement to get attention.

That said, I do agree that the release notes should explicitly mention that basal drift may have (un)intentionally propagated into user profiles. In such cases, users may need to reassess and correct their profile after updating.

@sprig3
Copy link

sprig3 commented Feb 20, 2026

I think this statement is somewhat too strong. I can imagine the basal drift kind of slipped into users profiles over time, especially when using tools like autotune. However, the underlying issue would still be that the profile itself was inaccurate. If the profile had originally been established using proper basal and/or ISF testing with the loop disabled, it should remain valid.

I don't know about others, but I raise my profile by 20% when I loop just to counteract this issue. So, knowing I no longer have to do that is important. (But I bet folks will adjust back quickly.)

@nl-ruud
Copy link
Author

nl-ruud commented Feb 20, 2026

@sprig3 you did read the second part of my comment as well, did you?

@xitation
Copy link

Everyone using a dash will need to be careful and update their profile when installing an AAPS version with this fix included.

I think this statement is somewhat too strong. I can imagine the basal drift kind of slipped into users profiles over time, especially when using tools like autotune. However, the underlying issue would still be that the profile itself was inaccurate. If the profile had originally been established using proper basal and/or ISF testing with the loop disabled, it should remain valid.

I think this will need to be mentioned pretty high up the patch notes / release announcement to get attention.

That said, I do agree that the release notes should explicitly mention that basal drift may have (un)intentionally propagated into user profiles. In such cases, users may need to reassess and correct their profile after updating.

Yeh I agree there is a chance for people to not understand the ramifications of this patch without sufficient warning of that this may have had them do to profiles etc.

I think it's just a good idea to have sufficient warnings around Basil profile maybe being incorrect etc.

@nl-ruud
Copy link
Author

nl-ruud commented Feb 26, 2026

After running this fix for several weeks, I realized that basal drift actually originates when the active temp basal is cancelled. Previously, I was checking for a drift compensation need on every status update. This could introduce an edge case where a status response arrives just before the basal is delivered, potentially causing an early compensation of 0.05U. To address this, I’ve moved the compensation evaluation to immediately follow the (driver internal) temp basal cancellation. This aligns the check with the source of the drift, simplifies logic and eliminates potential timing issues, since at that point we know the temp basal has already been cancelled.

@vanelsberg
Copy link
Contributor

After running this fix for several weeks, I realized that basal drift actually originates ....

Will review test this, but no earlier then at the end of next week. Currently on Medtrum...

@2223333333
Copy link

I realized that basal drift actually originates when the active temp basal is cancelled.

Perhaps even when temp basal 0 is enabled and standard basal is disabled, drift may occur.

@nl-ruud
Copy link
Author

nl-ruud commented Feb 26, 2026

Perhaps even when temp basal 0 is enabled and standard basal is disabled, drift may occur.

When temp basal is 0, no drift can occur (actually existing drift can - and will - decrease if this is the case for hours due to how the Omnipod works). When there is no active (basal) profile, you cannot activate the pump: it requires a basal profile.

But even if this would all not be the case: the bolus pulses, basal pulses and drift are updated every status response: no change there. Just the decision to deliver a compensation is only made when we can know for sure a compensation is needed; the latest commit just eliminates a potential race condition.

@xitation
Copy link

I've merged latest commits, built apk and upgraded. Hold tight while I collect data for next few days.

@nl-ruud
Copy link
Author

nl-ruud commented Feb 27, 2026

I created a (disposable..) Python script that parses the AAPS logs and generates a graph to illustrate how this PR handled the basal drift. Reporting for a week:

  • A negative accumulated error indicates that the pod delivered less insulin than expected.
  • The green bars represent the basal compensation boluses delivered by the PR.
  • When AAPS is zero-temping (dark red line at the bottom of the graph), no compensation is delivered if there is a deficit (e.g., ~10:00 to ~15:00 in the first graph), for safety reasons.
  • On a pod change (diamond in the graph), the accumulated error resets.
2026-02-26 2026-02-27 2026-02-28 2026-03-01 2026-03-02 2026-03-03 2026-03-04

The reconciliation for these dates:

Date Delivered (Pump) TDD (AAPS) Deviation Corrections
2026-02-26 15.20 15.2 0.00 23
2026-02-27 15.30 15.3 0.00 15
2026-02-28 15.05 15.0 0.05 11
2026-03-01 17.75 17.7 0.05 16
2026-03-02 14.60 14.7 -0.10 15
2026-03-03 16.70 16.7 0.00 22
2026-03-04 14.95 14.9 0.05 33

Note: if you want to try this script yourself, make sure all pump related logging is enabled in the log settings!

@nl-ruud
Copy link
Author

nl-ruud commented Feb 28, 2026

Rebased onto dev. No conflicts; updated file paths to match upstream moves.

@xitation
Copy link

xitation commented Mar 2, 2026

Ran your Python Script produced the following:

AndroidAPS_LOG_1772412285131_err_2026-03-02.html

@xitation
Copy link

xitation commented Mar 2, 2026

Latest data extracts:

image

@xitation
Copy link

xitation commented Mar 2, 2026

Hey all,

@nl-ruud rightly pointed out I had forgotten to minus the priming a new pod amount from my values in the previous data posted.

I've fixed this for the data I still have and below is the correct view of my data since cherry picking commit ece3ecc on the 2026/02/27 - 10:30AM.

image

@RSC-RJCgit
Copy link

RSC-RJCgit commented Mar 4, 2026

My percent difference ob about 24 units was more like 12% or more, on 1 minute aISF

1st 2 days modified more like 3.5% and 5.5% of 20u tdd
( next 2 days closer to 2.2 %of 19.1 tdd then 15.75 tdd ),much better for 1 minute looping ( really 1 minute bgl, about 2 minute looping set on 1 minute ) than about 15% of 21u pre- fix.

@nl-ruud nl-ruud force-pushed the issue-4158-dev-fix branch 2 times, most recently from ace4e8f to f01b7d3 Compare March 6, 2026 19:41
@nl-ruud nl-ruud force-pushed the issue-4158-dev-fix branch from 54c0f93 to be1177d Compare March 6, 2026 19:47
@nl-ruud nl-ruud force-pushed the issue-4158-dev-fix branch from be1177d to 6fbf11f Compare March 6, 2026 19:48
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 6, 2026

@nl-ruud
Copy link
Author

nl-ruud commented Mar 6, 2026

To merge this PR into dev, and after discussing with @vanelsberg, I added a semaphore file to enable basal drift compensation. You need an empty file called omnipod_drift_compensation in the extra subfolder of your phone’s AAPS directory for the compensation to be active. Commits are squashed into three parts:

  • Detect basal drift.
  • Automatically compensate basal drift. Logic extended: drift can occur on temp basal cancel, cancel + set, or set without cancel. The last case was previously not considered, which could delay compensation.
  • Make automatic compensation conditional on the semaphore file. This allows merging while enabling broader testing; the file can be removed later.

I believe this PR is ready to merge. Please react with 👍 if you agree or 👎 if you have concerns, and share reasoning if you disagree.

Release notes:

  • Basal drift may have (un)intentionally propagated into user profiles; users may need to reassess their profile after updating.
  • Basal deficit compensation is delivered as a 0.05U bolus and will appear as such in the GUI and Watch app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants