diff --git a/app/build.gradle b/app/build.gradle
index d056240b5279649d8dbba489ecfc0876187aeae7..2196be3c7498a513b019b2390c94f56395dc3be8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -115,6 +115,7 @@ dependencies {
     compile 'com.android.support:cardview-v7:25.3.1'
     compile 'org.apache.commons:commons-lang3:3.4'
     compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar'
+    compile 'com.mikepenz:ionicons-typeface:2.0.1.3@aar'
     compile 'uk.co.chrisjenx:calligraphy:2.2.0'
     compile 'com.bugfender.sdk:android:0.7.2'
     compile 'com.jjoe64:graphview:4.0.1'
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
index 373489b351c88f124b26921b5d3b02bba0f35699..f0b9dd1aa68aa5965e29420cd5c23df5870f20a9 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
@@ -30,11 +30,14 @@ import android.support.v7.app.NotificationCompat;
 import android.support.v7.view.menu.ActionMenuItemView;
 import android.support.v7.widget.Toolbar;
 import android.text.format.DateUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.ScrollView;
 import android.widget.TextView;
 import android.widget.TextView.BufferType;
 import android.widget.Toast;
@@ -96,11 +99,23 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     private SharedPreferences prefs = null;
     private PumpInfo mActivePump;
     private TextView mTextViewLog; // This will eventually move to a status page.
+    private ScrollView mScrollView;
     private GraphView mChart;
     private Handler mUiRefreshHandler = new Handler();
     private Runnable mUiRefreshRunnable = new RefreshDisplayRunnable();
     private Realm mRealm;
     private StatusMessageReceiver statusMessageReceiver = new StatusMessageReceiver();
+    private UsbReceiver usbReceiver = new UsbReceiver();
+    private BatteryReceiver batteryReceiver = new BatteryReceiver();
+
+    private DateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss", Locale.US);
+
+    protected void sendStatus(String message) {
+        Intent localIntent =
+                new Intent(MedtronicCnlIntentService.Constants.ACTION_STATUS_MESSAGE)
+                        .putExtra(MedtronicCnlIntentService.Constants.EXTENDED_DATA, message);
+        LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
+    }
 
     /**
      * calculate the next poll timestamp based on last svg event
@@ -109,7 +124,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
      * @return timestamp
      */
     public static long getNextPoll(PumpStatusEvent pumpStatusData) {
-        long nextPoll = pumpStatusData.getSgvDate().getTime() + pumpStatusData.getPumpTimeOffset(),
+        long nextPoll = pumpStatusData.getSgvDate().getTime(),
                 now = System.currentTimeMillis(),
                 pollInterval = ConfigurationStore.getInstance().getPollInterval();
 
@@ -198,9 +213,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         batteryIntentFilter.addAction(Intent.ACTION_BATTERY_LOW);
         batteryIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         batteryIntentFilter.addAction(Intent.ACTION_BATTERY_OKAY);
-        registerReceiver(new BatteryReceiver(), batteryIntentFilter);
+        registerReceiver(batteryReceiver, batteryIntentFilter);
 
-        UsbReceiver usbReceiver = new UsbReceiver();
         IntentFilter usbIntentFilter = new IntentFilter();
         usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
@@ -285,7 +299,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                             finish();
                         } else if (drawerItem.equals(itemGetNow)) {
                             // It was triggered by user so start reading of data now and not based on last poll.
-                            startCgmService(0);
+                            sendStatus("Requesting poll now...");
+                            startCgmService(System.currentTimeMillis() + 1000);
                         } else if (drawerItem.equals(itemClearLog)) {
                             clearLogText();
                         } else if (drawerItem.equals(itemCheckForUpdate)) {
@@ -298,12 +313,17 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 .build();
 
         mTextViewLog = (TextView) findViewById(R.id.textview_log);
+        mScrollView = (ScrollView) findViewById(R.id.scrollView);
+        mScrollView.setSmoothScrollingEnabled(true);
 
         mChart = (GraphView) findViewById(R.id.chart);
 
         // disable scrolling at the moment
         mChart.getViewport().setScalable(false);
         mChart.getViewport().setScrollable(false);
+        mChart.getViewport().setYAxisBoundsManual(true);
+        mChart.getViewport().setMinY(80);
+        mChart.getViewport().setMaxY(120);
         mChart.getViewport().setXAxisBoundsManual(true);
         final long now = System.currentTimeMillis(),
                 left = now - chartZoom * 60 * 60 * 1000;
@@ -363,8 +383,9 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
-        startCgmService();
         startDisplayRefreshLoop();
+        statusStartup();
+        startCgmService();
     }
 
     @Override
@@ -419,11 +440,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         }
     }
 
-    private void refreshDisplay() {
-        cancelDisplayRefreshLoop();
-        startDisplayRefreshLoop();
-    }
-
     private void clearLogText() {
         statusMessageReceiver.clearMessages();
     }
@@ -444,8 +460,23 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 .start();
     }
 
+    private void statusStartup() {
+        sendStatus(MedtronicCnlIntentService.ICON_HEART + "Nightscout 600 Series Uploader");
+        sendStatus(MedtronicCnlIntentService.ICON_SETTING + "Poll interval: " + (configurationStore.getPollInterval() / 60000) +" minutes");
+        sendStatus(MedtronicCnlIntentService.ICON_SETTING + "Low battery poll interval: " + (configurationStore.getLowBatteryPollInterval() / 60000) +" minutes");
+    }
+
+    private void refreshDisplay() {
+        cancelDisplayRefreshLoop();
+        mUiRefreshHandler.post(mUiRefreshRunnable);;
+    }
+    private void refreshDisplay(int delay) {
+        cancelDisplayRefreshLoop();
+        mUiRefreshHandler.postDelayed(mUiRefreshRunnable, delay);
+    }
+
     private void startDisplayRefreshLoop() {
-        mUiRefreshHandler.post(mUiRefreshRunnable);
+        refreshDisplay(50);
     }
 
     private void cancelDisplayRefreshLoop() {
@@ -461,8 +492,13 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             RealmResults<PumpStatusEvent> results = mRealm.where(PumpStatusEvent.class)
                     .findAllSorted("eventDate", Sort.DESCENDING);
             if (results.size() > 0) {
-                startCgmService(getNextPoll(results.first()) + delay);
-                return;
+                long nextPoll = getNextPoll(results.first());
+                long pollInterval = results.first().getBatteryPercentage() > 25 ? ConfigurationStore.getInstance().getPollInterval() : ConfigurationStore.getInstance().getLowBatteryPollInterval();
+                if ((nextPoll - MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS - results.first().getSgvDate().getTime()) <= pollInterval) {
+                    startCgmService(nextPoll + delay);
+                    sendStatus("Next poll due at: " + dateFormatter.format(nextPoll + delay));
+                    return;
+                }
             }
         }
         startCgmService(System.currentTimeMillis() + (delay == 0 ? 1000 : delay));
@@ -518,16 +554,32 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         notificationManager.cancel(MainActivity.USB_DISCONNECT_NOFICATION_ID);
     }
 
+    @Override
+    protected void onResume() {
+        Log.i(TAG, "onResume called");
+        super.onResume();
+        // Focus status log to most recent on returning to app
+        mScrollView.post(new Runnable() {
+            public void run() {
+                mScrollView.fullScroll(View.FOCUS_DOWN);
+            }
+        });
+    }
+
     @Override
     protected void onDestroy() {
         Log.i(TAG, "onDestroy called");
         super.onDestroy();
 
+        unregisterReceiver(usbReceiver);
+        unregisterReceiver(batteryReceiver);
+
         PreferenceManager.getDefaultSharedPreferences(getBaseContext()).unregisterOnSharedPreferenceChangeListener(this);
         cancelDisplayRefreshLoop();
 
-        mRealm.close();
-
+        if (!mRealm.isClosed()) {
+            mRealm.close();
+        }
         if (!mEnableCgmService) {
             stopCgmService();
         }
@@ -588,7 +640,12 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         if (activePumpMac != 0L && (mActivePump == null || !mActivePump.isValid() || mActivePump.getPumpMac() != activePumpMac)) {
             if (mActivePump != null) {
                 // remove listener on old pump
-                mActivePump.removeAllChangeListeners();
+                mRealm.executeTransaction(new Realm.Transaction() {
+                    @Override
+                    public void execute(Realm sRealm) {
+                        mActivePump.removeAllChangeListeners();
+                    }
+                });
                 mActivePump = null;
             }
 
@@ -598,6 +655,11 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                     .findFirst();
 
             if (pump != null && pump.isValid()) {
+
+                // first change listener start can miss fresh data and not update until next poll, force a refresh now
+                RemoveOutdatedRecords();
+                refreshDisplay(1000);
+
                 mActivePump = pump;
                 mActivePump.addChangeListener(new RealmChangeListener<PumpInfo>() {
                     long lastQueryTS = 0;
@@ -611,28 +673,10 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
                         lastQueryTS = pump.getLastQueryTS();
 
-                        // Delete invalid or old records from Realm
-                        // TODO - show an error message if the valid records haven't been uploaded
-                        final RealmResults<PumpStatusEvent> results =
-                                mRealm.where(PumpStatusEvent.class)
-                                        .equalTo("sgv", 0)
-                                        .or()
-                                        .lessThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000)))
-                                        .findAll();
-
-                        if (results.size() > 0) {
-                            mRealm.executeTransaction(new Realm.Transaction() {
-                                @Override
-                                public void execute(Realm realm) {
-                                    // Delete all matches
-                                    Log.d(TAG, "Deleting " + results.size() + " records from realm");
-                                    results.deleteAllFromRealm();
-                                }
-                            });
-                        }
+                        RemoveOutdatedRecords();
+                        refreshDisplay(1000);
 
                         // TODO - handle isOffline in NightscoutUploadIntentService?
-                        refreshDisplay();
                     }
                 });
             }
@@ -641,6 +685,26 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         return mActivePump;
     }
 
+    private void RemoveOutdatedRecords() {
+        // Delete invalid or old records from Realm
+        // TODO - show an error message if the valid records haven't been uploaded
+        final RealmResults<PumpStatusEvent> results =
+                mRealm.where(PumpStatusEvent.class)
+                        .equalTo("sgv", 0)
+                        .or()
+                        .lessThan("sgvDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000)))
+                        .findAll();
+        if (results.size() > 0) {
+            mRealm.executeTransaction(new Realm.Transaction() {
+                @Override
+                public void execute(Realm realm) {
+                    // Delete all matches
+                    Log.d(TAG, "Deleting " + results.size() + " records from realm");
+                    results.deleteAllFromRealm();
+                }
+            });
+        }
+    }
 
     public static String strFormatSGV(double sgvValue) {
         ConfigurationStore configurationStore = ConfigurationStore.getInstance();
@@ -660,6 +724,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     }
 
     public static String renderTrendSymbol(PumpStatusEvent.CGM_TREND trend) {
+        // TODO - symbols used for trend arrow may vary per device, find a more robust solution
         switch (trend) {
             case DOUBLE_UP:
                 return "\u21c8";
@@ -721,6 +786,15 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             }
 
             mTextViewLog.setText(sb.toString(), BufferType.EDITABLE);
+
+            // auto scroll status log
+            if ((mScrollView.getChildAt(0).getBottom() < mScrollView.getHeight()) || ((mScrollView.getChildAt(0).getBottom() - mScrollView.getScrollY() - mScrollView.getHeight()) < (mScrollView.getHeight() / 3))) {
+                mScrollView.post(new Runnable() {
+                    public void run() {
+                        mScrollView.fullScroll(View.FOCUS_DOWN);
+                    }
+                });
+            }
         }
 
         public void clearMessages() {
@@ -735,6 +809,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     private class RefreshDisplayRunnable implements Runnable {
         @Override
         public void run() {
+            long nextRun = 60000L;
+
             TextView textViewBg = (TextView) findViewById(R.id.textview_bg);
             TextView textViewBgTime = (TextView) findViewById(R.id.textview_bg_time);
             TextView textViewUnits = (TextView) findViewById(R.id.textview_units);
@@ -754,6 +830,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             }
 
             updateChart(mRealm.where(PumpStatusEvent.class)
+                    .notEqualTo("sgv", 0)
                     .greaterThan("sgvDate", new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24))
                     .findAllSorted("sgvDate", Sort.ASCENDING));
 
@@ -770,6 +847,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                     sgvString = "\u2014"; // &mdash;
                 }
 
+                nextRun = 60000L - (System.currentTimeMillis() - pumpStatusData.getSgvDate().getTime()) % 60000L;
                 textViewBg.setText(sgvString);
                 textViewBgTime.setText(DateUtils.getRelativeTimeSpanString(pumpStatusData.getSgvDate().getTime()));
 
@@ -807,8 +885,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
             }
 
-            // Run myself again in 60 seconds;
-            mUiRefreshHandler.postDelayed(this, 60000L);
+            // Run myself again in 60 (or less) seconds;
+            mUiRefreshHandler.postDelayed(this, nextRun);
         }
 
         private void updateChart(RealmResults<PumpStatusEvent> results) {
@@ -879,7 +957,12 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                             paint.setColor(Color.YELLOW);
                         else
                             paint.setColor(Color.RED);
-                        canvas.drawCircle(x, y, 3.6f, paint);
+                        float dotSize = 3.0f;
+                        if (chartZoom == 3) dotSize = 2.0f;
+                        else if (chartZoom == 6) dotSize = 2.0f;
+                        else if (chartZoom == 12) dotSize = 1.65f;
+                        else if (chartZoom == 24) dotSize = 1.25f;
+                        canvas.drawCircle(x, y, dipToPixels(getApplicationContext(), dotSize), paint);
                     }
                 });
 
@@ -891,15 +974,30 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 }
             }
 
+            // TODO - chart viewport needs rework as currently using a workaround to handle updating
+
             // set viewport to latest SGV
             long lastSGVTimestamp = (long) mChart.getSeries().get(0).getHighestValueX();
+
+            long min_x = (((lastSGVTimestamp + 150000 - (chartZoom * 60 * 60 * 1000)) / 60000) * 60000);
+            long max_x = lastSGVTimestamp + 90000;
+
             if (!hasZoomedChart) {
-                mChart.getViewport().setMaxX(lastSGVTimestamp);
-                mChart.getViewport().setMinX(lastSGVTimestamp - chartZoom * 60 * 60 * 1000);
+                mChart.getViewport().setMinX(min_x);
+                mChart.getViewport().setMaxX(max_x);
+            }
+            if (entries.length > 0) {
+                ((PointsGraphSeries) mChart.getSeries().get(0)).resetData(entries);
             }
+
         }
     }
 
+    private static float dipToPixels(Context context, float dipValue) {
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics);
+    }
+
     /**
      * has to be done in MainActivity thread
      */
@@ -938,7 +1036,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 if (mEnableCgmService) {
                     clearDisconnectionNotification();
                 }
-
+                dataStore.clearAllCommsErrors();
+                sendStatus(MedtronicCnlIntentService.ICON_INFO + "Contour Next Link plugged in.");
                 if (hasUsbPermission()) {
                     // Give the USB a little time to warm up first
                     startCgmServiceDelayed(MedtronicCnlIntentService.USB_WARMUP_TIME_MS);
@@ -950,6 +1049,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 Log.d(TAG, "USB unplugged");
                 if (mEnableCgmService) {
                     showDisconnectionNotification("USB Error", "Contour Next Link unplugged.");
+                    sendStatus(MedtronicCnlIntentService.ICON_WARN + "USB error. Contour Next Link unplugged.");
                 }
             } else if (MedtronicCnlIntentService.Constants.ACTION_NO_USB_PERMISSION.equals(action)) {
                 Log.d(TAG, "No permission to read the USB device.");
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java
index 47c33dcf6a980e1a93dff4fb3ebefc40ecae3767..83efbe0ef2da5b2ae6f80fd8a21bda4e38392d9b 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java
@@ -40,10 +40,18 @@ public class MedtronicResponseMessage extends ContourNextLinkResponseMessage {
             // Replace the encrypted bytes by their decrypted equivalent (same block size)
             byte encryptedPayloadSize = payload[56];
 
+            if (encryptedPayloadSize == 0) {
+                throw new EncryptionException( "Could not decrypt Medtronic Message (encryptedPayloadSize == 0)" );
+            }
+
             ByteBuffer encryptedPayload = ByteBuffer.allocate(encryptedPayloadSize);
             encryptedPayload.put(payload, 57, encryptedPayloadSize);
             byte[] decryptedPayload = decrypt(pumpSession.getKey(), pumpSession.getIV(), encryptedPayload.array());
 
+            if (decryptedPayload == null) {
+                throw new EncryptionException( "Could not decrypt Medtronic Message (decryptedPayload == null)" );
+            }
+
             // Now that we have the decrypted payload, rewind the mPayload, and overwrite the bytes
             // TODO - because this messes up the existing CCITT, do we want to have a separate buffer for the decrypted payload?
             // Should be fine provided we check the CCITT first...
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
index 6fbe8768dadc0ece6003a59b3cdad7dcc88d32a6..9e98c309fdb0310012e3d640e4ce717b64b4e346 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
@@ -42,6 +42,10 @@ public class PumpStatusRequestMessage extends MedtronicSendMessageRequestMessage
         }
         // Read the 0x80
         byte[] payload = readMessage(mDevice);
+        // if pump sends an unexpected response get the next response as pump can resend or send out of sequence and this avoids comms errors
+        if (payload.length < 0x9C) {
+            payload = readMessage(mDevice);
+        }
 
         // clear unexpected incoming messages
         clearMessage(mDevice);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
index 6a70ee7ed1b6d3248102202e43f49d88e26bca86..5778359c0995759e19d9856d5cc20a020a40f775 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
@@ -191,12 +191,18 @@ public class PumpStatusResponseMessage extends MedtronicSendMessageResponseMessa
         pumpRecord.setLowSuspendActive(lowSuspendActive);
 
         // Recent Bolus Wizard BGL
-        pumpRecord.setRecentBolusWizard(recentBolusWizard);
-        // there is a BolusWizard usage & the IOB increased
-        if (activeInsulin > DataStore.getInstance().getLastPumpStatus().getActiveInsulin()) {
+        if (bolusWizardBGL > 0
+                && (DataStore.getInstance().getLastPumpStatus().getSgvDate().getTime() - System.currentTimeMillis() > 15 * 60 * 1000
+                || (DataStore.getInstance().getLastBolusWizardBGL() != bolusWizardBGL
+                && DataStore.getInstance().getLastPumpStatus().getBolusWizardBGL() != bolusWizardBGL)
+                )
+        ) {
+            pumpRecord.setRecentBolusWizard(true);
             pumpRecord.setBolusWizardBGL(bolusWizardBGL); // In mg/DL
         } else {
+            pumpRecord.setRecentBolusWizard(false);
             pumpRecord.setBolusWizardBGL(0); // In mg/DL
         }
+        DataStore.getInstance().setLastBolusWizardBGL(bolusWizardBGL);
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
index 7332a48e0c88c609cd9f2db4a7687b6f6c6195d3..56a53fba524f6c1374f40689017897b48f61192c 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
@@ -37,6 +37,10 @@ public class PumpTimeRequestMessage extends MedtronicSendMessageRequestMessage<P
         }
         // Read the 0x80
         byte[] payload = readMessage(mDevice);
+        // if pump sends an unexpected response get the next response as pump can resend or send out of sequence and this avoids comms errors
+        if (payload.length < 0x49) {
+            payload = readMessage(mDevice);
+        }
 
         // Pump sends additional 0x80 message when not using EHSM, lets clear this and any unexpected incoming messages
         clearMessage(mDevice);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyRequestMessage.java
index 1814fe87fa8df4322fdeead228477434986ca466..bf1a7ea9a35fd995d7ba49e7beb31ad4a3996100 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyRequestMessage.java
@@ -3,6 +3,7 @@ package info.nightscout.android.medtronic.message;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
 import info.nightscout.android.medtronic.exception.ChecksumException;
 import info.nightscout.android.medtronic.exception.EncryptionException;
+import info.nightscout.android.medtronic.exception.UnexpectedMessageException;
 
 /**
  * Created by volker on 10.12.2016.
@@ -14,7 +15,7 @@ public class RequestLinkKeyRequestMessage extends ContourNextLinkBinaryRequestMe
     }
 
     @Override
-    protected RequestLinkKeyResponseMessage getResponse(byte[] payload) throws ChecksumException, EncryptionException {
+    protected RequestLinkKeyResponseMessage getResponse(byte[] payload) throws ChecksumException, EncryptionException, UnexpectedMessageException {
         return new RequestLinkKeyResponseMessage(mPumpSession, payload);
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyResponseMessage.java
index e5bb5ce76b280c6d8dd3a8508653b289feac251f..fceea5a4a0898797e56d7d342389763d64b95fdb 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyResponseMessage.java
@@ -1,22 +1,33 @@
 package info.nightscout.android.medtronic.message;
 
+import android.util.Log;
+
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
 import info.nightscout.android.medtronic.MedtronicCnlSession;
 import info.nightscout.android.medtronic.exception.ChecksumException;
 import info.nightscout.android.medtronic.exception.EncryptionException;
+import info.nightscout.android.medtronic.exception.UnexpectedMessageException;
 
 /**
  * Created by lgoedhart on 10/05/2016.
  */
 public class RequestLinkKeyResponseMessage extends MedtronicResponseMessage {
+    private static final String TAG = RequestLinkKeyResponseMessage.class.getSimpleName();
 
     private byte[] key;
 
-    protected RequestLinkKeyResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException {
+    protected RequestLinkKeyResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException, UnexpectedMessageException {
         super(pumpSession, payload);
 
+        if (this.encode().length < (0x57 - 4)) {
+            // Invalid message. Don't try and parse it
+            // TODO - deal with this more elegantly
+            Log.e(TAG, "Invalid message received for requestLinkKey");
+            throw new UnexpectedMessageException("Invalid message received for requestLinkKey, Contour Next Link is not paired with pump.");
+        }
+
         ByteBuffer infoBuffer = ByteBuffer.allocate(55);
         infoBuffer.order(ByteOrder.BIG_ENDIAN);
         infoBuffer.put(this.encode(), 0x21, 55);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java
index b58d702ce647bf2c1014af714954c15107661788..6b92546346bbcdc3674f54aba25813ebc9153dbe 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java
@@ -53,7 +53,7 @@ public class MedtronicCnlAlarmManager {
         cancelAlarm();
 
         Log.d(TAG, "Alarm set to fire at " + new Date(millis));
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
             alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(millis, null), pendingIntent);
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             // Android 5.0.0 + 5.0.1 (e.g. Galaxy S4) has a bug.
@@ -66,8 +66,11 @@ public class MedtronicCnlAlarmManager {
 
     // restarting the alarm after MedtronicCnlIntentService.POLL_PERIOD_MS from now
     public static void restartAlarm() {
-        //setAlarmAfterMillis(MainActivity.pollInterval + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS);
-        setAlarmAfterMillis(ConfigurationStore.getInstance().getPollInterval()); // grace already accounted for when using current intent time to set default restart
+        // Due to potential of some versions of android to mangle alarms and clash with polling times
+        // the default alarm reset is set to POLL_PERIOD_MS + 60 seconds
+        // It's expected to trigger between polls if alarm has not been honored with a safe margin greater then
+        // the around 10 minutes that some OS versions force during sleep
+        setAlarmAfterMillis(MedtronicCnlIntentService.POLL_PERIOD_MS + 60000L); // grace already accounted for when using current intent time to set default restart
     }
 
     // Cancel the alarm.
diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java
index 1ef2deaa0780b4b08c679af3365a0c1009c7cf32..bf3c333c40b8b990778f18afff76b49638c68651 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java
@@ -49,6 +49,25 @@ public class MedtronicCnlIntentService extends IntentService {
     public final static long LOW_BATTERY_POLL_PERIOD_MS = 900000L;
     // Number of additional seconds to wait after the next expected CGM poll, so that we don't interfere with CGM radio comms.
     public final static long POLL_GRACE_PERIOD_MS = 30000L;
+    public final static long POLL_PRE_GRACE_PERIOD_MS = 45000L;
+
+    public static final String ICON_WARN = "{ion-alert-circled} ";
+    public static final String ICON_BGL = "{ion-waterdrop} ";
+    public static final String ICON_USB = "{ion-usb} ";
+    public static final String ICON_INFO = "{ion-information_circled} ";
+    public static final String ICON_HELP = "{ion-ios-lightbulb} ";
+    public static final String ICON_SETTING = "{ion-android-settings} ";
+    public static final String ICON_HEART = "{ion-heart} ";
+    public static final String ICON_STAR = "{ion-ios-star} ";
+
+    // show warning message after repeated errors
+    private final static int ERROR_COMMS_AT = 4;
+    private final static int ERROR_CONNECT_AT = 4;
+    private final static int ERROR_SIGNAL_AT = 4;
+    private final static float ERROR_UNAVAILABLE_AT = 12;       // warning at
+    private final static float ERROR_UNAVAILABLE_RATE = 12 / 3; // expected per hour / acceptable unavailable per hour
+    private final static float ERROR_UNAVAILABLE_DECAY = -1;    // decay rate for each good sgv received
+
     private static final String TAG = MedtronicCnlIntentService.class.getSimpleName();
 
     private UsbHidDriver mHidDevice;
@@ -104,6 +123,23 @@ public class MedtronicCnlIntentService extends IntentService {
         }
     }
 
+/*
+
+Notes on Errors:
+
+CNL-PUMP pairing and registered devices
+
+CNL: paired PUMP: paired UPLOADER: registered = ok
+CNL: paired PUMP: paired UPLOADER: unregistered = ok
+CNL: paired PUMP: unpaired UPLOADER: registered = "Could not communicate with the pump. Is it nearby?"
+CNL: paired PUMP: unpaired UPLOADER: unregistered = "Could not communicate with the pump. Is it nearby?"
+CNL: unpaired PUMP: paired UPLOADER: registered = "Timeout communicating with the Contour Next Link."
+CNL: unpaired PUMP: paired UPLOADER: unregistered = "Invalid message received for requestLinkKey, Contour Next Link is not paired with pump."
+CNL: unpaired PUMP: unpaired UPLOADER: registered = "Timeout communicating with the Contour Next Link."
+CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received for requestLinkKey, Contour Next Link is not paired with pump."
+
+*/
+
     protected void onHandleIntent(Intent intent) {
         Log.d(TAG, "onHandleIntent called");
         try {
@@ -120,7 +156,12 @@ public class MedtronicCnlIntentService extends IntentService {
             // do more method extraction refactorings to make this method easier to grasp
 
             final long timePollStarted = System.currentTimeMillis();
-            final long timeLastGoodSGV = dataStore.getLastPumpStatus().getSgvDate().getTime();
+
+            long timeLastGoodSGV = dataStore.getLastPumpStatus().getSgvDate().getTime();
+            if (dataStore.getLastPumpStatus().getSgv() == 0
+                || timePollStarted - timeLastGoodSGV > 24 * 60 * 60 * 1000) {
+                timeLastGoodSGV = 0;
+            }
 
             final long timePollExpected;
             if (timeLastGoodSGV != 0) {
@@ -130,8 +171,8 @@ public class MedtronicCnlIntentService extends IntentService {
             }
 
             // avoid polling when too close to sensor-pump comms
-            if (((timePollExpected - timePollStarted) > 5000L) && ((timePollExpected - timePollStarted) < (POLL_GRACE_PERIOD_MS + 45000L))) {
-                sendStatus("Please wait: Poll due in " + ((timePollExpected - timePollStarted) / 1000L) + " seconds");
+            if (((timePollExpected - timePollStarted) > 5000L) && ((timePollExpected - timePollStarted) < (POLL_PRE_GRACE_PERIOD_MS + POLL_GRACE_PERIOD_MS))) {
+                sendStatus("Please wait: Pump is expecting sensor communication. Poll due in " + ((timePollExpected - timePollStarted) / 1000L) + " seconds");
                 MedtronicCnlAlarmManager.setAlarm(timePollExpected);
                 return;
             }
@@ -140,6 +181,10 @@ public class MedtronicCnlIntentService extends IntentService {
             long pollInterval = configurationStore.getPollInterval();
             if ((pumpBatteryLevel > 0) && (pumpBatteryLevel <= 25)) {
                 pollInterval = configurationStore.getLowBatteryPollInterval();
+                sendStatus(ICON_WARN + "Warning: pump battery low");
+                if (pollInterval != configurationStore.getPollInterval()) {
+                    sendStatus(ICON_SETTING + "Low battery poll interval: " + (pollInterval / 60000) +" minutes");
+               }
             }
 
             // TODO - throw, don't return
@@ -176,7 +221,11 @@ public class MedtronicCnlIntentService extends IntentService {
 
                     cnlReader.requestReadInfo();
 
-                    String key = info.getKey();
+                    // always get LinkKey on startup to handle re-paired CNL-PUMP key changes
+                    String key = null;
+                    if (dataStore.getCommsSuccessCount() > 0) {
+                        key = info.getKey();
+                    }
 
                     if (key == null) {
                         cnlReader.requestLinkKey();
@@ -202,15 +251,25 @@ public class MedtronicCnlIntentService extends IntentService {
 
                     byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel());
                     if (radioChannel == 0) {
-                        sendStatus("Could not communicate with the pump. Is it nearby?");
+                        sendStatus(ICON_WARN + "Could not communicate with the pump. Is it nearby?");
                         Log.i(TAG, "Could not communicate with the pump. Is it nearby?");
+                        dataStore.incCommsConnectThreshold();
                         pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
                     } else if (cnlReader.getPumpSession().getRadioRSSIpercentage() < 5) {
                         sendStatus(String.format(Locale.getDefault(), "Connected on channel %d  RSSI: %d%%", (int) radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage()));
-                        sendStatus("Warning: pump signal too weak. Is it nearby?");
+                        sendStatus(ICON_WARN + "Warning: pump signal too weak. Is it nearby?");
                         Log.i(TAG, "Warning: pump signal too weak. Is it nearby?");
+                        dataStore.incCommsConnectThreshold();
+                        dataStore.incCommsSignalThreshold();
                         pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
                     } else {
+                        dataStore.decCommsConnectThreshold();
+                        if (cnlReader.getPumpSession().getRadioRSSIpercentage() < 20) {
+                            if (dataStore.getCommsSignalThreshold() < ERROR_SIGNAL_AT) dataStore.incCommsSignalThreshold();
+                        } else {
+                            dataStore.decCommsSignalThreshold();
+                        }
+
                         dataStore.setActivePumpMac(pumpMAC);
 
                         activePump.setLastRadioChannel(radioChannel);
@@ -230,62 +289,81 @@ public class MedtronicCnlIntentService extends IntentService {
                         long pumpOffset = pumpTime - System.currentTimeMillis();
                         Log.d(TAG, "Time offset between pump and device: " + pumpOffset + " millis.");
 
+                        if (Math.abs(pumpOffset) > 10 * 60 * 1000) {
+                            sendStatus(ICON_WARN + "Warning: Time difference between Pump and Uploader excessive."
+                                    + " Pump is over " + (Math.abs(pumpOffset) / 60000L) + " minutes " + (pumpOffset > 0 ? "ahead" : "behind") + " of time used by uploader.");
+                            sendStatus(ICON_HELP + "The uploader phone/device should have the current time provided by network. Pump clock drifts forward and needs to be set to correct time occasionally.");
+                        }
+
                         // TODO - send ACTION to MainActivity to show offset between pump and uploader.
                         pumpRecord.setPumpTimeOffset(pumpOffset);
                         pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset));
                         cnlReader.updatePumpStatus(pumpRecord);
 
-                    if (pumpRecord.getSgv() != 0) {
-                        String offsetSign = "";
-                        if (pumpOffset > 0) {
-                            offsetSign = "+";
-                        }
-                        sendStatus("SGV: " + MainActivity.strFormatSGV(pumpRecord.getSgv()) + "  At: " + dateFormatter.format(pumpRecord.getSgvDate().getTime()) + "  Pump: " + offsetSign + (pumpOffset / 1000L) + "sec");  //note: event time is currently stored with offset
-
-                        // Check if pump sent old event when new expected
-                        if (dataStore.getLastPumpStatus() != null &&
-                                dataStore.getLastPumpStatus().getSgvDate() != null &&
-                                pumpRecord.getSgvDate().getTime() - dataStore.getLastPumpStatus().getSgvDate().getTime() < 5000L &&
-                                timePollExpected - timePollStarted < 5000L) {
-                            sendStatus("Pump sent old SGV event");
+                        if (pumpRecord.getSgv() != 0) {
+                            sendStatus("SGV: " + MainActivity.strFormatSGV(pumpRecord.getSgv())
+                                + "  At: " + dateFormatter.format(pumpRecord.getSgvDate().getTime())
+                                + "  Pump: " + (pumpOffset > 0 ? "+" : "") + (pumpOffset / 1000L) + "sec");
+                            // Check if pump sent old event when new expected
+                            if (dataStore.getLastPumpStatus() != null &&
+                                    dataStore.getLastPumpStatus().getSgvDate() != null &&
+                                    pumpRecord.getSgvDate().getTime() - dataStore.getLastPumpStatus().getSgvDate().getTime() < 5000L &&
+                                    timePollExpected - timePollStarted < 5000L) {
+                                sendStatus(ICON_WARN + "Pump sent old SGV event");
+                                if (dataStore.getCommsUnavailableThreshold() < ERROR_UNAVAILABLE_AT) dataStore.addCommsUnavailableThreshold(ERROR_UNAVAILABLE_RATE / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L));
+                                // pump may have missed sensor transmission or be delayed in posting to status message
+                                // in most cases the next scheduled poll will have latest sgv, occasionally it is available this period after a delay
+                                // if user selects double poll option we try again this period or wait until next
+                                pollInterval = POLL_PERIOD_MS / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L);
+                            } else {
+                                dataStore.addCommsUnavailableThreshold(ERROR_UNAVAILABLE_DECAY);
+                            }
+
+                            dataStore.clearUnavailableSGVCount(); // reset unavailable sgv count
+
+                            // Check that the record doesn't already exist before committing
+                            RealmResults<PumpStatusEvent> checkExistingRecords = activePump.getPumpHistory()
+                                    .where()
+                                    .equalTo("sgvDate", pumpRecord.getSgvDate())
+                                    .equalTo("sgv", pumpRecord.getSgv())
+                                    .findAll();
+
+                            // There should be the 1 record we've already added in this transaction.
+                            if (checkExistingRecords.size() == 0) {
+                                timeLastGoodSGV = pumpRecord.getSgvDate().getTime();
+                                activePump.getPumpHistory().add(pumpRecord);
+                                dataStore.setLastPumpStatus(pumpRecord);
+                                if (pumpRecord.getBolusWizardBGL() != 0) {
+                                    sendStatus(ICON_BGL +"Recent finger BG: " + MainActivity.strFormatSGV(pumpRecord.getBolusWizardBGL()));
+                                }
+                            }
+
+                        } else {
+                            sendStatus(ICON_WARN + "SGV: unavailable from pump");
+                            dataStore.incUnavailableSGVCount(); // poll clash detection
+                            if (dataStore.getCommsUnavailableThreshold() < ERROR_UNAVAILABLE_AT) dataStore.addCommsUnavailableThreshold(ERROR_UNAVAILABLE_RATE);
                         }
 
-                        dataStore.clearUnavailableSGVCount(); // reset unavailable sgv count
-
-                        // Check that the record doesn't already exist before committing
-                        RealmResults<PumpStatusEvent> checkExistingRecords = activePump.getPumpHistory()
-                                .where()
-                                .equalTo("sgvDate", pumpRecord.getSgvDate())
-                                .equalTo("sgv", pumpRecord.getSgv())
-                                .findAll();
-
-                        // There should be the 1 record we've already added in this transaction.
-                        if (checkExistingRecords.size() == 0) {
-                            activePump.getPumpHistory().add(pumpRecord);
-                            dataStore.setLastPumpStatus(pumpRecord);
-                        }
-
-                    } else {
-                        sendStatus("SGV: unavailable from pump");
-                        dataStore.incUnavailableSGVCount(); // poll clash detection
-                    }
-
                         realm.commitTransaction();
                         // Tell the Main Activity we have new data
                         sendMessage(Constants.ACTION_UPDATE_PUMP);
+                        dataStore.incCommsSuccessCount();
+                        dataStore.clearCommsErrorCount();
                     }
 
                 } catch (UnexpectedMessageException e) {
-                    Log.e(TAG, "Unexpected Message", e);
-                    sendStatus("Communication Error: " + e.getMessage());
+                    dataStore.incCommsErrorCount();
                     pollInterval = 60000L; // retry once during this poll period, this allows for transient radio noise
+                    Log.e(TAG, "Unexpected Message", e);
+                    sendStatus(ICON_WARN + "Communication Error: " + e.getMessage());
                 } catch (TimeoutException e) {
+                    dataStore.incCommsErrorCount();
+                    pollInterval = 90000L; // retry once during this poll period, this allows for transient radio noise
                     Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
-                    sendStatus("Timeout communicating with the Contour Next Link.");
-                    pollInterval = 60000L; // retry once during this poll period, this allows for transient radio noise
+                    sendStatus(ICON_WARN + "Timeout communicating with the Contour Next Link / Pump.");
                 } catch (NoSuchAlgorithmException e) {
                     Log.e(TAG, "Could not determine CNL HMAC", e);
-                    sendStatus("Error connecting to Contour Next Link: Hashing error.");
+                    sendStatus(ICON_WARN + "Error connecting to Contour Next Link: Hashing error.");
                 } finally {
                     try {
                         cnlReader.closeConnection();
@@ -296,20 +374,25 @@ public class MedtronicCnlIntentService extends IntentService {
 
                 }
             } catch (IOException e) {
+                dataStore.incCommsErrorCount();
                 Log.e(TAG, "Error connecting to Contour Next Link.", e);
-                sendStatus("Error connecting to Contour Next Link.");
+                sendStatus(ICON_WARN + "Error connecting to Contour Next Link.");
             } catch (ChecksumException e) {
+                dataStore.incCommsErrorCount();
                 Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e);
-                sendStatus("Checksum error getting message from the Contour Next Link.");
+                sendStatus(ICON_WARN + "Checksum error getting message from the Contour Next Link.");
             } catch (EncryptionException e) {
+                dataStore.incCommsErrorCount();
                 Log.e(TAG, "Error decrypting messages from Contour Next Link.", e);
-                sendStatus("Error decrypting messages from Contour Next Link.");
+                sendStatus(ICON_WARN + "Error decrypting messages from Contour Next Link.");
             } catch (TimeoutException e) {
+                dataStore.incCommsErrorCount();
                 Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
-                sendStatus("Timeout communicating with the Contour Next Link.");
+                sendStatus(ICON_WARN + "Timeout communicating with the Contour Next Link.");
             } catch (UnexpectedMessageException e) {
+                dataStore.incCommsErrorCount();
                 Log.e(TAG, "Could not close connection.", e);
-                sendStatus("Could not close connection: " + e.getMessage());
+                sendStatus(ICON_WARN + "Could not close connection: " + e.getMessage());
             } finally {
                 if (!realm.isClosed()) {
                     if (realm.isInTransaction()) {
@@ -321,21 +404,46 @@ public class MedtronicCnlIntentService extends IntentService {
 
                 uploadPollResults();
                 scheduleNextPoll(timePollStarted, timeLastGoodSGV, pollInterval);
+
+                // TODO - Refactor warning system
+                if (dataStore.getCommsErrorCount() >= ERROR_COMMS_AT) {
+                    sendStatus(ICON_WARN + "Warning: multiple comms/timeout errors detected.");
+                    sendStatus(ICON_HELP + "Try: disconnecting and reconnecting the Contour Next Link to phone / restarting phone / check pairing of CNL with Pump.");
+                }
+                if (dataStore.getCommsUnavailableThreshold() >= ERROR_UNAVAILABLE_AT) {
+                    dataStore.clearCommsUnavailableThreshold();
+                    sendStatus(ICON_WARN + "Warning: SGV unavailable from pump is happening often. The pump is missing transmissions from the sensor / in warm-up phase / environment radio noise.");
+                    sendStatus(ICON_HELP + "Keep pump on same side of body as sensor. Avoid using body sensor locations that can block radio signal. Sensor may be old / faulty and need changing (check pump graph for gaps).");
+                }
+                if (dataStore.getCommsConnectThreshold() >= ERROR_CONNECT_AT * (configurationStore.isReducePollOnPumpAway() ? 2 : 1)) {
+                    dataStore.clearCommsConnectThreshold();
+                    sendStatus(ICON_WARN + "Warning: connecting to pump is failing often.");
+                    sendStatus(ICON_HELP + "Keep pump nearby to uploader phone/device. The body can block radio signals between pump and uploader.");
+                }
+                if (dataStore.getCommsSignalThreshold() >= ERROR_SIGNAL_AT) {
+                    dataStore.clearCommsSignalThreshold();
+                    sendStatus(ICON_WARN + "Warning: RSSI radio signal from pump is generally weak and may increase errors.");
+                    sendStatus(ICON_HELP + "Keep pump nearby to uploader phone/device. The body can block radio signals between pump and uploader.");
+                }
+
             }
         } finally {
             MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
         }
     }
 
+    // TODO - Refactor polling system and make super clear how polling is calculated and why certain precautions are needed
     private void scheduleNextPoll(long timePollStarted, long timeLastGoodSGV, long pollInterval) {
         // smart polling and pump-sensor poll clash detection
+        long now = System.currentTimeMillis();
         long lastActualPollTime = timePollStarted;
         if (timeLastGoodSGV > 0) {
-            lastActualPollTime = timeLastGoodSGV + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((System.currentTimeMillis() - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS));
+            lastActualPollTime = timeLastGoodSGV + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((now - timeLastGoodSGV + POLL_GRACE_PERIOD_MS) / POLL_PERIOD_MS));
         }
         long nextActualPollTime = lastActualPollTime + POLL_PERIOD_MS;
         long nextRequestedPollTime = lastActualPollTime + pollInterval;
-        if ((nextRequestedPollTime - System.currentTimeMillis()) < 10000L) {
+        // check if request is really needed
+        if (nextRequestedPollTime - now < 10000L) {
             nextRequestedPollTime = nextActualPollTime;
         }
         // extended unavailable SGV may be due to clash with the current polling time
@@ -344,10 +452,17 @@ public class MedtronicCnlIntentService extends IntentService {
             if (timeLastGoodSGV == 0) {
                 nextRequestedPollTime += POLL_PERIOD_MS / 5L; // if there is a uploader/sensor poll clash on startup then this will push the next attempt out by 60 seconds
             } else if (dataStore.getUnavailableSGVCount() > 2) {
-                sendStatus("Warning: No SGV available from pump for " + dataStore.getUnavailableSGVCount() + " attempts");
-                nextRequestedPollTime += ((long) ((dataStore.getUnavailableSGVCount() - 2) % 5)) * (POLL_PERIOD_MS / 10L); // adjust poll time in 1/10 steps to avoid potential poll clash (max adjustment at 5/10)
+                sendStatus(ICON_WARN + "Warning: No SGV available from pump for " + dataStore.getUnavailableSGVCount() + " attempts");
+                long offsetPollTime = ((long) ((dataStore.getUnavailableSGVCount() - 2) % 5)) * (POLL_PERIOD_MS / 10L); // adjust poll time in 1/10 steps to avoid potential poll clash (max adjustment at 5/10)
+                sendStatus("Adjusting poll: "  + dateFormatter.format(nextRequestedPollTime) +  " +" + (offsetPollTime / 1000) + "sec");
+                nextRequestedPollTime += offsetPollTime;
             }
         }
+        // check if requested poll time is too close to next actual poll time
+        if (nextRequestedPollTime > nextActualPollTime - POLL_GRACE_PERIOD_MS - POLL_PRE_GRACE_PERIOD_MS
+                && nextRequestedPollTime < nextActualPollTime) {
+            nextRequestedPollTime = nextActualPollTime;
+        }
         MedtronicCnlAlarmManager.setAlarm(nextRequestedPollTime);
         sendStatus("Next poll due at: " + dateFormatter.format(nextRequestedPollTime));
     }
@@ -357,14 +472,14 @@ public class MedtronicCnlIntentService extends IntentService {
      */
     private boolean openUsbDevice() {
         if (!hasUsbHostFeature()) {
-            sendStatus("It appears that this device doesn't support USB OTG.");
+            sendStatus(ICON_WARN + "It appears that this device doesn't support USB OTG.");
             Log.e(TAG, "Device does not support USB OTG");
             return false;
         }
 
         UsbDevice cnlStick = UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID);
         if (cnlStick == null) {
-            sendStatus("USB connection error. Is the Contour Next Link plugged in?");
+            sendStatus(ICON_WARN + "USB connection error. Is the Contour Next Link plugged in?");
             Log.w(TAG, "USB connection error. Is the CNL plugged in?");
             return false;
         }
@@ -378,7 +493,7 @@ public class MedtronicCnlIntentService extends IntentService {
         try {
             mHidDevice.open();
         } catch (Exception e) {
-            sendStatus("Unable to open USB device");
+            sendStatus(ICON_WARN + "Unable to open USB device");
             Log.e(TAG, "Unable to open serial device", e);
             return false;
         }
diff --git a/app/src/main/java/info/nightscout/android/utils/DataStore.java b/app/src/main/java/info/nightscout/android/utils/DataStore.java
index 39fe120d971acda594d91203e040fd2125c2a3de..86bfa944f893a0fa8925c7c0da7d6c7a00a2b73c 100644
--- a/app/src/main/java/info/nightscout/android/utils/DataStore.java
+++ b/app/src/main/java/info/nightscout/android/utils/DataStore.java
@@ -1,6 +1,8 @@
 package info.nightscout.android.utils;
 
 
+import org.apache.commons.lang3.time.DateUtils;
+
 import java.util.Date;
 
 import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
@@ -16,7 +18,13 @@ public class DataStore {
     private PumpStatusEvent lastPumpStatus;
     private int uploaderBatteryLevel = 0;
     private int unavailableSGVCount = 0;
+    private int lastBolusWizardBGL = 0;
     private long activePumpMac = 0;
+    private int commsErrorCount = 0;
+    private int commsSuccessCount = 0;
+    private int commsConnectThreshold = 0;
+    private int commsSignalThreshold = 0;
+    private float commsUnavailableThreshold = 0;
 
     private DataStore() {}
 
@@ -26,7 +34,8 @@ public class DataStore {
 
             // set some initial dummy values
             PumpStatusEvent dummyStatus = new PumpStatusEvent();
-            dummyStatus.setSgvDate(new Date());
+            dummyStatus.setSgvDate(DateUtils.addDays(new Date(), -1));
+            dummyStatus.setSgv(0);
 
             // bypass setter to avoid dealing with a real Realm object
             instance.lastPumpStatus = dummyStatus;
@@ -65,10 +74,19 @@ public class DataStore {
     public void clearUnavailableSGVCount() {
         this.unavailableSGVCount = 0;
     }
+
     public void setUnavailableSGVCount(int unavailableSGVCount) {
         this.unavailableSGVCount = unavailableSGVCount;
     }
 
+    public int getLastBolusWizardBGL() {
+        return lastBolusWizardBGL;
+    }
+
+    public void setLastBolusWizardBGL(int lastBolusWizardBGL) {
+        this.lastBolusWizardBGL = lastBolusWizardBGL;
+    }
+
     public long getActivePumpMac() {
         return activePumpMac;
     }
@@ -76,4 +94,81 @@ public class DataStore {
     public void setActivePumpMac(long activePumpMac) {
         this.activePumpMac = activePumpMac;
     }
+
+    public int getCommsErrorCount() {
+        return commsErrorCount;
+    }
+
+    public int incCommsErrorCount() { return commsErrorCount++; }
+
+    public int decCommsErrorThreshold() {
+        if (commsErrorCount > 0) commsErrorCount--;
+        return commsErrorCount;}
+
+    public void clearCommsErrorCount() {
+        this.commsErrorCount = 0;
+    }
+
+    public int getCommsSuccessCount() {
+        return commsSuccessCount;
+    }
+
+    public int incCommsSuccessCount() { return commsSuccessCount++; }
+
+    public int decCommsSuccessCount() {
+        if (commsSuccessCount > 0) commsSuccessCount--;
+        return commsSuccessCount;}
+
+    public void clearCommsSuccessCount() {
+        this.commsSuccessCount = 0;
+    }
+
+    public int getCommsConnectThreshold() {
+        return commsConnectThreshold;
+    }
+
+    public int incCommsConnectThreshold() { return commsConnectThreshold++; }
+
+    public int decCommsConnectThreshold() {
+        if (commsConnectThreshold > 0) commsConnectThreshold--;
+        return commsConnectThreshold;}
+
+    public void clearCommsConnectThreshold() {
+        this.commsConnectThreshold = 0;
+    }
+
+    public int getCommsSignalThreshold() {
+        return commsSignalThreshold;
+    }
+
+    public int incCommsSignalThreshold() { return commsSignalThreshold++; }
+
+    public int decCommsSignalThreshold() {
+        if (commsSignalThreshold > 0) commsSignalThreshold--;
+        return commsSignalThreshold;}
+
+    public void clearCommsSignalThreshold() {
+        this.commsSignalThreshold = 0;
+    }
+
+    public float getCommsUnavailableThreshold() {
+        return commsUnavailableThreshold;
+    }
+
+    public float addCommsUnavailableThreshold(float value) {
+        commsUnavailableThreshold+= value;
+        if (commsUnavailableThreshold < 0) commsUnavailableThreshold = 0;
+        return commsUnavailableThreshold;}
+
+    public void clearCommsUnavailableThreshold() {
+        this.commsUnavailableThreshold = 0;
+    }
+
+    public void clearAllCommsErrors() {
+        this.commsErrorCount = 0;
+        this.commsSuccessCount = 0;
+        this.commsConnectThreshold = 0;
+        this.commsSignalThreshold = 0;
+        this.commsUnavailableThreshold = 0;
+    }
 }
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 9810c1ad7339738df8fb7cde34bbd38dbee7c78a..8d2fad1e9ffb117584baa409ab72db8c361d45df 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -59,8 +59,8 @@
                 android:textSize="70sp" />
 
             <LinearLayout
-                android:layout_width="0dp"
-                android:layout_height="fill_parent"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
                 android:layout_gravity="bottom"
                 android:layout_weight="1"
                 android:gravity="bottom|center_horizontal"
@@ -135,7 +135,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
 
-            <TextView
+            <com.mikepenz.iconics.view.IconicsTextView
                 android:id="@+id/textview_log"
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index cfb3458e897140a9308cac32f15041128630caad..8e0fb08456bf9ed24e653d8372d145c94e0ec3e9 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -3,7 +3,6 @@
     <string-array name="poll_interval">
         <item>5 min</item>
         <item>10 min</item>
-        <item>12 min</item>
         <item>15 min</item>
         <item>20 min</item>
         <item>30 min</item>
@@ -14,7 +13,6 @@
     <string-array name="poll_interval_millis">
         <item>300000</item>
         <item>600000</item>
-        <item>720000</item>
         <item>900000</item>
         <item>1200000</item>
         <item>1800000</item>