diff --git a/app/src/main/java/info/nightscout/android/UploaderApplication.java b/app/src/main/java/info/nightscout/android/UploaderApplication.java
index e83930549d22d99fa37e3c30726ee14bb0aba72e..c84575b56456326cf0990abf97d9794567a2858b 100644
--- a/app/src/main/java/info/nightscout/android/UploaderApplication.java
+++ b/app/src/main/java/info/nightscout/android/UploaderApplication.java
@@ -97,18 +97,6 @@ public class UploaderApplication extends Application {
                 .modules(new HistoryModule())
                 .deleteRealmIfMigrationNeeded()
                 .build();
-
-        Realm storeRealm = Realm.getInstance(storeConfiguration);
-        if (storeRealm.where(DataStore.class).findFirst() == null) {
-            storeRealm.executeTransaction(new Realm.Transaction() {
-                @Override
-                public void execute(Realm realm) {
-                    realm.createObject(DataStore.class);
-                }
-            });
-        }
-        storeRealm.close();
-
     }
 
     public static long getStartupRealtime() {
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 d65acc7899ec9b0766c40a73fe3f8091e25da494..4261eb8ef3352d8c899f01250b3ffd854c721882 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
@@ -119,6 +119,14 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         historyRealm = Realm.getInstance(UploaderApplication.getHistoryConfiguration());
 
         dataStore = storeRealm.where(DataStore.class).findFirst();
+        if (dataStore == null) {
+            storeRealm.executeTransaction(new Realm.Transaction() {
+                @Override
+                public void execute(Realm realm) {
+                    dataStore = realm.createObject(DataStore.class);
+                }
+            });
+        }
 
         // limit date for NS backfill sync period, set to this init date to stop overwrite of older NS data (pref option to override)
         if (dataStore.getNightscoutLimitDate() == null) {
@@ -548,6 +556,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 dataStore.setSysPollErrorRetry(Long.parseLong(sharedPreferences.getString("sysPollErrorRetry", "90000")));
                 dataStore.setSysPollOldSgvRetry(Long.parseLong(sharedPreferences.getString("sysPollOldSgvRetry", "90000")));
                 dataStore.setSysEnableWait500ms(sharedPreferences.getBoolean("sysEnableWait500ms", false));
+                dataStore.setSysEnableUsbPermissionDialog(sharedPreferences.getBoolean("sysEnableUsbPermissionDialog", false));
 
                 // debug
                 dataStore.setDbgEnableExtendedErrors(sharedPreferences.getBoolean("dbgEnableExtendedErrors", false));
@@ -569,6 +578,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 dataStore.setNsProfileDefault(Integer.parseInt(sharedPreferences.getString("nsProfileDefault", "1")));
                 dataStore.setNsActiveInsulinTime(Float.parseFloat(sharedPreferences.getString("nsActiveInsulinTime", "3")));
                 dataStore.setNsEnablePatternChange(sharedPreferences.getBoolean("nsEnablePatternChange", true));
+                dataStore.setNsGramsPerExchange(Integer.parseInt(sharedPreferences.getString("nsGramsPerExchange", "15")));
                 dataStore.setNsEnableInsertBGasCGM(sharedPreferences.getBoolean("nsEnableInsertBGasCGM", false));
 
                 // pattern and preset naming
diff --git a/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java b/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java
index e3c181ff0a8c6b8eb5c7692b4c27a656820fe209..6ca17f569eab3d184973ffe26f2b34813377ef96 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java
@@ -75,14 +75,14 @@ public class PumpHistoryHandler {
         historyRealm = Realm.getInstance(UploaderApplication.getHistoryConfiguration());
 
         historyDB = new ArrayList<>();
-        historyDB.add(new DBitem("CGM", false, 400, historyRealm.where(PumpHistoryCGM.class).findAll()));
-        historyDB.add(new DBitem("BOLUS", true, 30, historyRealm.where(PumpHistoryBolus.class).findAll()));
-        historyDB.add(new DBitem("BASAL", true,30, historyRealm.where(PumpHistoryBasal.class).findAll()));
+        historyDB.add(new DBitem("CGM", false, 300, historyRealm.where(PumpHistoryCGM.class).findAll()));
+        historyDB.add(new DBitem("BOLUS", true, 20, historyRealm.where(PumpHistoryBolus.class).findAll()));
+        historyDB.add(new DBitem("BASAL", true,20, historyRealm.where(PumpHistoryBasal.class).findAll()));
         historyDB.add(new DBitem("PATTERN", true,10, historyRealm.where(PumpHistoryPattern.class).findAll()));
-        historyDB.add(new DBitem("BG", true,30, historyRealm.where(PumpHistoryBG.class).findAll()));
+        historyDB.add(new DBitem("BG", true,20, historyRealm.where(PumpHistoryBG.class).findAll()));
         historyDB.add(new DBitem("PROFILE", false,10, historyRealm.where(PumpHistoryProfile.class).findAll()));
         historyDB.add(new DBitem("MISC", true,10, historyRealm.where(PumpHistoryMisc.class).findAll()));
-        historyDB.add(new DBitem("LOOP", true,300, historyRealm.where(PumpHistoryLoop.class).findAll()));
+        historyDB.add(new DBitem("LOOP", true,200, historyRealm.where(PumpHistoryLoop.class).findAll()));
         historyDB.add(new DBitem("DEBUG", true,20, historyRealm.where(PumpHistoryDebug.class).findAll()));
     }
 
@@ -280,34 +280,74 @@ public class PumpHistoryHandler {
         if (dataStore.isNameBasalPatternChanged() &&
                 (dataStore.isNsEnableProfileSingle() || dataStore.isNsEnableProfileOffset())) {
 
-            userLogMessage("Basal Pattern Names changed, updating pattern switch treatments in Nightscout");
-
-            final RealmResults<PumpHistoryProfile> results = historyRealm
-                    .where(PumpHistoryProfile.class)
-                    .equalTo("profileSwitch", true)
+            final RealmResults<PumpHistoryPattern> results = historyRealm
+                    .where(PumpHistoryPattern.class)
                     .findAll();
-            historyRealm.executeTransaction(new Realm.Transaction() {
+
+            if (results.size() > 0) {
+                Log.d(TAG, "NameBasalPatternChanged: Found " + results.size() + " pattern switch treatments to update");
+                userLogMessage("Basal Pattern Names changed, updating pattern switch treatments in Nightscout");
+
+                historyRealm.executeTransaction(new Realm.Transaction() {
+                    @Override
+                    public void execute(Realm realm) {
+                        for (PumpHistoryPattern record : results) {
+                            record.setUploadREQ(true);
+                            record.setUploadACK(true);
+                        }
+                    }
+                });
+
+            }
+
+            storeRealm.executeTransaction(new Realm.Transaction() {
                 @Override
                 public void execute(Realm realm) {
-                    for (PumpHistoryProfile record : results) {
-                        record.setUploadREQ(true);
-                    }
+                    dataStore.setNameBasalPatternChanged(false);
                 }
             });
+        }
+    }
+
+    public void checkGramsPerExchangeChanged() {
+
+        if (dataStore.isNsGramsPerExchangeChanged()) {
+
+            final RealmResults<PumpHistoryBolus> results = historyRealm
+                    .where(PumpHistoryBolus.class)
+                    .equalTo("programmed", true)
+                    .equalTo("estimate", true)
+                    .equalTo("carbUnits", PumpHistoryParser.CARB_UNITS.EXCHANGES.get())
+                    .findAll();
+
+            if (results.size() > 0) {
+                Log.d(TAG, "GramsPerExchangeChanged: Found " + results.size() + " carb/bolus treatments to update");
+                userLogMessage("Grams per Exchange changed, updating carb/bolus treatments in Nightscout");
+
+                historyRealm.executeTransaction(new Realm.Transaction() {
+                    @Override
+                    public void execute(Realm realm) {
+                        for (PumpHistoryBolus record : results) {
+                            record.setUploadREQ(true);
+                            record.setUploadACK(true);
+                        }
+                    }
+                });
+            }
+
             storeRealm.executeTransaction(new Realm.Transaction() {
                 @Override
                 public void execute(Realm realm) {
-                    dataStore.setNameBasalPatternChanged(false);
+                    dataStore.setNsGramsPerExchangeChanged(false);
                 }
             });
         }
-
     }
 
     public void cgm(final PumpStatusEvent pumpRecord) {
 
         // push the current sgv from status (always have latest sgv available even if there are comms errors after this)
-        if (pumpRecord.isValidSGV()) {
+        if (pumpRecord.isCgmActive()) {
 
             final Date date = pumpRecord.getCgmDate();
             final int rtc = pumpRecord.getCgmRTC();
@@ -317,7 +357,7 @@ public class PumpHistoryHandler {
                     .where(PumpHistoryCGM.class)
                     .findAllSorted("eventDate", Sort.ASCENDING);
 
-            // sgv is available do we need the backfill?
+            // cgm is available do we need the backfill?
             if (dataStore.isSysEnableCgmHistory()
                     && results.size() == 0
                     || (results.size() > 0 && date.getTime() - results.last().getEventDate().getTime() > 9 * 60 * 1000L)) {
@@ -335,6 +375,7 @@ public class PumpHistoryHandler {
                 public void execute(Realm realm) {
                     PumpHistoryCGM.cgm(historyRealm, date, rtc, offset,
                             pumpRecord.getSgv(),
+                            pumpRecord.getCgmExceptionType(),
                             pumpRecord.getCgmTrendString()
                     );
                 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java b/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java
index 72740e1ad646468a66e0f076fa8b6360cf3e512d..dca732f8f8b9ebf03fae3f8eaccb2437aed49ffa 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java
@@ -122,6 +122,11 @@ public class StatusNotification {
     private void updateNotification() {
         Log.d(TAG, "updateNotification called");
 
+        if (realm == null || storeRealm == null || historyRealm == null) {
+            Log.e(TAG, "unexpected null for Realm");
+            return;
+        }
+
         long now = System.currentTimeMillis();
 
         String poll = "";
@@ -150,7 +155,7 @@ public class StatusNotification {
                 if (sgvAge < 60 && deltaTime < 30) {
                     int diff = results.first().getSgv() - results.get(1).getSgv();
                     if (dataStore.isMmolxl())
-                        delta += "Δ " + (diff > 0 ? "+" : "") + new BigDecimal(diff / MMOLXLFACTOR).setScale(2, BigDecimal.ROUND_HALF_UP);
+                        delta += "Δ " + (diff > 0 ? "+" : "") + new BigDecimal(diff / MMOLXLFACTOR).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
                     else
                         delta += "Δ " + (diff > 0 ? "+" : "") + diff;
                     if (deltaTime > 6)
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java
index 2a7af0c0752ba2ba2286e9f9bbdae5773af834f7..527aaeef1fd4fb6fa559f8e62846fe6be63314cd 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java
@@ -359,6 +359,7 @@ public abstract class ContourNextLinkMessage {
                     expectedSegments = multipacketSession.packetsToFetch;
                     fetchMoreData = true;
                 } else if (MedtronicSendMessageRequestMessage.MessageType.MULTIPACKET_SEGMENT_TRANSMISSION.response(cmd)) {
+                    if (multipacketSession == null) throw new UnexpectedMessageException("multipacketSession not initiated before segment received" + tag);
                     multipacketSession.addSegment(decrypted);
                     expectedSegments--;
 
@@ -447,23 +448,27 @@ public abstract class ContourNextLinkMessage {
         }
 
         private void addSegment(byte[] data) throws UnexpectedMessageException {
-            int packetNumber = read16BEtoUInt(data, 0x0003);
-            int packetSize = data.length - 7;
-            segments[packetNumber] = true;
-
-            Log.d(TAG, "*** Got a Multipacket Segment: " + (packetNumber + 1) + " of " + this.packetsToFetch + ", count: " + segmentsFilled() + " [packetSize=" + packetSize + " " + this.packetSize + "/" + this.lastPacketSize + "]");
-
-            if (packetNumber == lastPacketNumber() &&
-                    packetSize != this.lastPacketSize) {
-                throw new UnexpectedMessageException("Multipacket Transfer last packet size mismatch");
-            } else if (packetNumber != lastPacketNumber() &&
-                    packetSize != this.packetSize) {
-                throw new UnexpectedMessageException("Multipacket Transfer packet size mismatch");
-            }
+            try {
+                int packetNumber = read16BEtoUInt(data, 0x0003);
+                int packetSize = data.length - 7;
+                segments[packetNumber] = true;
+
+                Log.d(TAG, "*** Got a Multipacket Segment: " + (packetNumber + 1) + " of " + this.packetsToFetch + ", count: " + segmentsFilled() + " [packetSize=" + packetSize + " " + this.packetSize + "/" + this.lastPacketSize + "]");
+
+                if (packetNumber == lastPacketNumber() &&
+                        packetSize != this.lastPacketSize) {
+                    throw new UnexpectedMessageException("Multipacket Transfer last packet size mismatch");
+                } else if (packetNumber != lastPacketNumber() &&
+                        packetSize != this.packetSize) {
+                    throw new UnexpectedMessageException("Multipacket Transfer packet size mismatch");
+                }
 
-            int to = (packetNumber * this.packetSize) + 1;
-            int from = 5;
-            while (from < packetSize + 5) this.response[to++] = data[from++];
+                int to = (packetNumber * this.packetSize) + 1;
+                int from = 5;
+                while (from < packetSize + 5) this.response[to++] = data[from++];
+            } catch (Exception e) {
+                throw new UnexpectedMessageException("Multipacket Transfer bad segment data received");
+            }
         }
 
         private byte[] missingSegments() {
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java
index 16460d3719cf95bf2c7eafdd99aabb8c76221eed..12ae62f8d4d3e1cdcfbc09c8a1105a4b5a1ef2d4 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java
@@ -93,7 +93,7 @@ public class ReadHistoryRequestMessage extends MedtronicSendMessageRequestMessag
 
         // Check that we have the correct number of bytes in this message
         if (payload.length - HEADER_SIZE != historySizeCompressed) {
-            throw new UnexpectedMessageException("Unexpected update message size");
+            throw new UnexpectedMessageException("Unexpected message size");
         }
 
         byte[] blockPayload;
@@ -101,12 +101,16 @@ public class ReadHistoryRequestMessage extends MedtronicSendMessageRequestMessag
         if (historyCompressed) {
             blockPayload = new byte[historySizeUncompressed];
 
-            LzoAlgorithm algorithm = LzoAlgorithm.LZO1X;
-            LzoDecompressor decompressor = LzoLibrary.getInstance().newDecompressor(algorithm, null);
-            int lzoReturnCode = decompressor.decompress(payload, HEADER_SIZE, historySizeCompressed, blockPayload, 0, new lzo_uintp(historySizeUncompressed));
+            try {
+                LzoAlgorithm algorithm = LzoAlgorithm.LZO1X;
+                LzoDecompressor decompressor = LzoLibrary.getInstance().newDecompressor(algorithm, null);
+                int lzoReturnCode = decompressor.decompress(payload, HEADER_SIZE, historySizeCompressed, blockPayload, 0, new lzo_uintp(historySizeUncompressed));
+                if (lzoReturnCode != LzoTransformer.LZO_E_OK)
+                    throw new UnexpectedMessageException("Error trying to decompress message (" + decompressor.toErrorString(lzoReturnCode) + ")");
+            } catch (Exception e) {
+                throw new UnexpectedMessageException("Error trying to decompress message");
+            }
 
-            if (lzoReturnCode != LzoTransformer.LZO_E_OK)
-                throw new UnexpectedMessageException("Error trying to decompress update message (" + decompressor.toErrorString(lzoReturnCode) + ")");
         } else {
             blockPayload = Arrays.copyOfRange(payload, HEADER_SIZE, payload.length);
         }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java
index ac52965cfac274735d7bd45764f57a169f342bbc..4d9df44416a6130f8dd985788afc36fcbf7aa407 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java
@@ -94,48 +94,53 @@ public class MasterService extends Service {
 
         mContext = this.getBaseContext();
 
-        IntentFilter masterServiceIntentFilter = new IntentFilter();
-        masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_ACTIVE);
-        masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_FINISHED);
-        masterServiceIntentFilter.addAction(Constants.ACTION_STOP_SERVICE);
-        masterServiceIntentFilter.addAction(Constants.ACTION_READ_NOW);
-        masterServiceIntentFilter.addAction(Constants.ACTION_READ_PROFILE);
-        masterServiceIntentFilter.addAction(Constants.ACTION_URCHIN_UPDATE);
-        registerReceiver(masterServiceReceiver, masterServiceIntentFilter);
-
-        registerReceiver(
-                userLogMessageReceiver,
-                new IntentFilter(Constants.ACTION_USERLOG_MESSAGE));
-
-        IntentFilter batteryIntentFilter = new IntentFilter();
-        batteryIntentFilter.addAction(Intent.ACTION_BATTERY_LOW);
-        batteryIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        batteryIntentFilter.addAction(Intent.ACTION_BATTERY_OKAY);
-        registerReceiver(batteryReceiver, batteryIntentFilter);
-
-        IntentFilter usbIntentFilter = new IntentFilter();
-        usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
-        usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
-        usbIntentFilter.addAction(Constants.ACTION_HAS_USB_PERMISSION);
-        usbIntentFilter.addAction(Constants.ACTION_NO_USB_PERMISSION);
-        registerReceiver(usbReceiver, usbIntentFilter);
-
-        MedtronicCnlAlarmManager.setContext(mContext);
-
-        urchin = new Urchin(mContext);
-
-        realm = Realm.getDefaultInstance();
         storeRealm = Realm.getInstance(UploaderApplication.getStoreConfiguration());
         dataStore = storeRealm.where(DataStore.class).findFirst();
 
-        storeRealm.executeTransaction(new Realm.Transaction() {
-            @Override
-            public void execute(Realm realm) {
-                dataStore.setNightscoutReportTime(0);
-                dataStore.setNightscoutAvailable(false);
-                dataStore.clearAllCommsErrors();
-            }
-        });
+        // safety check: don't proceed until main ui has been run and datastore initialised
+        if (dataStore != null) {
+
+            realm = Realm.getDefaultInstance();
+
+            storeRealm.executeTransaction(new Realm.Transaction() {
+                @Override
+                public void execute(Realm realm) {
+                    dataStore.setNightscoutReportTime(0);
+                    dataStore.setNightscoutAvailable(false);
+                    dataStore.clearAllCommsErrors();
+                }
+            });
+
+            IntentFilter masterServiceIntentFilter = new IntentFilter();
+            masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_ACTIVE);
+            masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_FINISHED);
+            masterServiceIntentFilter.addAction(Constants.ACTION_STOP_SERVICE);
+            masterServiceIntentFilter.addAction(Constants.ACTION_READ_NOW);
+            masterServiceIntentFilter.addAction(Constants.ACTION_READ_PROFILE);
+            masterServiceIntentFilter.addAction(Constants.ACTION_URCHIN_UPDATE);
+            registerReceiver(masterServiceReceiver, masterServiceIntentFilter);
+
+            registerReceiver(
+                    userLogMessageReceiver,
+                    new IntentFilter(Constants.ACTION_USERLOG_MESSAGE));
+
+            IntentFilter batteryIntentFilter = new IntentFilter();
+            batteryIntentFilter.addAction(Intent.ACTION_BATTERY_LOW);
+            batteryIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+            batteryIntentFilter.addAction(Intent.ACTION_BATTERY_OKAY);
+            registerReceiver(batteryReceiver, batteryIntentFilter);
+
+            IntentFilter usbIntentFilter = new IntentFilter();
+            usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+            usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+            usbIntentFilter.addAction(Constants.ACTION_HAS_USB_PERMISSION);
+            usbIntentFilter.addAction(Constants.ACTION_NO_USB_PERMISSION);
+            registerReceiver(usbReceiver, usbIntentFilter);
+
+            MedtronicCnlAlarmManager.setContext(mContext);
+
+            urchin = new Urchin(mContext);
+        }
     }
 
     @Override
@@ -143,19 +148,18 @@ public class MasterService extends Service {
         super.onDestroy();
         Log.d(TAG, "onDestroy called");
 
-        urchin.close();
-
-        statusNotification.endNotification();
-
         MedtronicCnlAlarmManager.cancelAlarm();
 
-        if (!storeRealm.isClosed()) storeRealm.close();
-        if (!realm.isClosed()) realm.close();
+        if (userLogMessageReceiver != null) unregisterReceiver(userLogMessageReceiver);
+        if (usbReceiver != null) unregisterReceiver(usbReceiver);
+        if (batteryReceiver != null) unregisterReceiver(batteryReceiver);
+        if (masterServiceReceiver != null) unregisterReceiver(masterServiceReceiver);
+
+        if (urchin != null) urchin.close();
+        if (statusNotification != null) statusNotification.endNotification();
 
-        unregisterReceiver(userLogMessageReceiver);
-        unregisterReceiver(usbReceiver);
-        unregisterReceiver(batteryReceiver);
-        unregisterReceiver(masterServiceReceiver);
+        if (storeRealm != null && !storeRealm.isClosed()) storeRealm.close();
+        if (realm != null && !realm.isClosed()) realm.close();
     }
 
     @Override
@@ -173,6 +177,11 @@ public class MasterService extends Service {
         Log.d(TAG, "Received start id " + startId + ": " + intent);
         //userLogMessage.add(TAG + " Received start id " + startId + ": " + (intent == null ? "null" : ""));
 
+        if (dataStore == null) {
+            // safety check: don't proceed until main ui has been run and datastore initialised
+            stopSelf();
+        }
+
         if (intent == null || startId == 1) {
             Log.i(TAG, "service start");
             //userLogMessage.add(TAG + " service start");
@@ -354,12 +363,14 @@ public class MasterService extends Service {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
-            // received from UsbActivity via OS
+            // received from UsbActivity via OS / app requested permission dialog
             if (Constants.ACTION_HAS_USB_PERMISSION.equals(action)) {
 
-                statusNotification.updateNotification(StatusNotification.NOTIFICATION.NORMAL);
-
-                startCgmServiceDelayed(MedtronicCnlService.USB_WARMUP_TIME_MS);
+                if (hasUsbPermission()) {
+                    userLogMessage.add(ICON_INFO + "Got permission for USB.");
+                    statusNotification.updateNotification(StatusNotification.NOTIFICATION.NORMAL);
+                    startCgmServiceDelayed(MedtronicCnlService.USB_WARMUP_TIME_MS);
+                }
 
                 // received from OS
             } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
@@ -397,12 +408,17 @@ public class MasterService extends Service {
             } else if (Constants.ACTION_NO_USB_PERMISSION.equals(action)) {
                 Log.d(TAG, "No permission to read the USB device.");
                 userLogMessage.add(ICON_WARN + "No permission to read the USB device.");
-                userLogMessage.add(ICON_HELP + "Unplug/plug the Contour Next Link and select 'use by default for this device' to make permission permanent.");
 
                 MedtronicCnlAlarmManager.cancelAlarm();
 
                 statusNotification.updateNotification(StatusNotification.NOTIFICATION.ERROR);
 
+                if (dataStore.isSysEnableUsbPermissionDialog()) {
+                    requestUsbPermission();
+                } else {
+                    userLogMessage.add(ICON_HELP + "Unplug/plug the Contour Next Link and select 'use by default for this device' to make permission permanent.");
+                }
+
             }
         }
     }
@@ -414,6 +430,13 @@ public class MasterService extends Service {
         return !(usbManager != null && cnlDevice != null && !usbManager.hasPermission(cnlDevice));
     }
 
+    private void requestUsbPermission() {
+        UsbManager usbManager = (UsbManager) this.getSystemService(Context.USB_SERVICE);
+        UsbDevice cnlDevice = UsbHidDriver.getUsbDevice(usbManager, MedtronicCnlService.USB_VID, MedtronicCnlService.USB_PID);
+        PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(Constants.ACTION_HAS_USB_PERMISSION), 0);
+        usbManager.requestPermission(cnlDevice, permissionIntent);
+    }
+
     private void showDisconnectionNotification(String title, String message) {
         android.support.v7.app.NotificationCompat.Builder mBuilder =
                 (android.support.v7.app.NotificationCompat.Builder) new android.support.v7.app.NotificationCompat.Builder(this)
diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java
index 42916ec8aad4cadbf7bd0d8feb7066de98248e37..e0226a411edaab755738c4a57e5bf6eaeaf5dcfd 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java
@@ -303,7 +303,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
             cnl0x81 = 0;
 
             try {
-
+/*
                 if (false) {
                     userLogMessage("Adding history from file");
                     new HistoryDebug(mContext).run();
@@ -311,7 +311,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                     nextpoll = 0;
                     return;
                 }
-
+*/
                 long pollInterval = dataStore.getPollInterval();
 
                 if (!openUsbDevice()) {
@@ -524,6 +524,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                                     });
                                 }
 
+                                pumpHistoryHandler.checkGramsPerExchangeChanged();
+
                                 pumpHistoryHandler.update(cnlReader);
                             }
                         }
@@ -651,9 +653,15 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
             userLogMessage(ICON_HELP + "Past historical data can be uploaded to Nightscout via the History Sync option in Advanced Nightscout Settings.");
         }
 */
-        if (dataStore.isNightscoutUpload() && dataStore.isNsEnableProfileUpload() && !pumpHistoryHandler.isProfileUploaded()) {
-            userLogMessage(ICON_INFO + "No basal pattern profile has been uploaded.");
-            userLogMessage(ICON_HELP + "Profiles can be uploaded from the main menu.");
+        if (dataStore.isNightscoutUpload() && dataStore.isNsEnableProfileUpload()) {
+            if (!pumpHistoryHandler.isProfileUploaded()) {
+                userLogMessage(ICON_INFO + "No basal pattern profile has been uploaded.");
+                userLogMessage(ICON_HELP + "Profiles can be uploaded from the main menu.");
+            } else if (dataStore.isNameBasalPatternChanged() &&
+                    (dataStore.isNsEnableProfileSingle() || dataStore.isNsEnableProfileOffset())) {
+                userLogMessage(ICON_INFO + "Basal pattern names have changed, your profile should be updated.");
+                userLogMessage(ICON_HELP + "Profiles can be updated from the main menu.");
+            }
         }
 
         if (PumpBatteryError >= ERROR_PUMPBATTERY_AT) {
@@ -784,7 +792,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                 // was temp ended before expected duration?
                 if (!results.first().isTempBasalActive() && results.get(1).isTempBasalActive()) {
                     int diff = results.get(1).getTempBasalMinutesRemaining() - ageMinutes;
-                    if (diff < -2 || diff > 2) {
+                    if (diff < -4 || diff > 4) {
                         userLogMessage(info + "temp ended");
                         historyNeeded = true;
                     }
@@ -793,7 +801,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                 // was a new temp started while one was in progress?
                 if (results.first().isTempBasalActive() && results.get(1).isTempBasalActive()) {
                     int diff = results.get(1).getTempBasalMinutesRemaining() - results.first().getTempBasalMinutesRemaining() - ageMinutes;
-                    if (diff < -2 || diff > 2) {
+                    if (diff < -4 || diff > 4) {
                         userLogMessage(info + "temp extended");
                         historyNeeded = true;
                     }
@@ -805,7 +813,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                     if (!results.first().isBolusingDual() && results.get(1).isBolusingDual()) {
                         // was dual ended before expected duration?
                         int diff = results.get(1).getBolusingMinutesRemaining() - ageMinutes;
-                        if (diff < -2 || diff > 2) {
+                        if (diff < -4 || diff > 4) {
                             userLogMessage(info + "dual ended");
                             historyNeeded = true;
                         }
@@ -814,7 +822,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                     else if (!results.first().isBolusingSquare() && results.get(1).isBolusingSquare() && !results.get(1).isBolusingNormal()) {
                         // was square ended before expected duration?
                         int diff = results.get(1).getBolusingMinutesRemaining() - ageMinutes;
-                        if (diff < -2 || diff > 2) {
+                        if (diff < -4 || diff > 4) {
                             userLogMessage(info + "square ended");
                             historyNeeded = true;
                         }
@@ -833,7 +841,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                 } else if (!results.first().isBolusingDual() && results.get(1).isBolusingDual()) {
                     // was dual ended before expected duration?
                     int diff = results.get(1).getBolusingMinutesRemaining() - ageMinutes;
-                    if (diff < -2 || diff > 2) {
+                    if (diff < -4 || diff > 4) {
                         userLogMessage(info + "dual ended");
                         historyNeeded = true;
                     }
diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java
index 09782845eaddfce47f9278f56169444535b2865d..82ea1707d90d0f27c6f0cf67028cb79dce8ef290 100644
--- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java
+++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java
@@ -170,10 +170,11 @@ public class PumpHistoryBolus extends RealmObject implements PumpHistoryInterfac
                 double carbInputAsGrams;
                 double carbRatioAsGrams;
                 String exchanges;
+                int gramsPerExchange = dataStore.getNsGramsPerExchange();
                 if (PumpHistoryParser.CARB_UNITS.EXCHANGES.equals(carbUnits)) {
-                    carbInputAsGrams = 15 * carbInput;
-                    carbRatioAsGrams = 15 / carbRatio;
-                    exchanges = String.format(Locale.US, ", ex %.2fu/15g", carbRatio);
+                    carbInputAsGrams = gramsPerExchange * carbInput;
+                    carbRatioAsGrams = gramsPerExchange / carbRatio;
+                    exchanges = String.format(Locale.US, ", ex %.2fu/%sg", carbRatio, gramsPerExchange);
                 } else {
                     carbInputAsGrams = carbInput;
                     carbRatioAsGrams = carbRatio;
diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java
index 5ff4f4b57eb29dc028ecb92816a7e4f25e209bb5..109738fa3622d3cea5c299102539c7f5596ae878 100644
--- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java
+++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java
@@ -61,17 +61,30 @@ public class PumpHistoryCGM extends RealmObject implements PumpHistoryInterface
     public List nightscout(DataStore dataStore) {
         List<UploadItem> uploadItems = new ArrayList<>();
 
-        UploadItem uploadItem = new UploadItem();
-        uploadItems.add(uploadItem);
-        EntriesEndpoints.Entry entry = uploadItem.ack(uploadACK).entry();
+        int sgvNS = sgv;
+        if (sgvNS == 0) {
+            if (sensorException == 2) // SENSOR_CAL_NEEDED
+                sgvNS = 5; // nightscout: '?NC' SENSOR_NOT_CALIBRATED
+            else if (sensorException == 5) // SENSOR_CHANGE_SENSOR_ERROR
+                sgvNS = 1; // nightscout: '?SN' SENSOR_NOT_ACTIVE
+            else if (sensorException == 6) // SENSOR_END_OF_LIFE
+                sgvNS = 1; // nightscout: '?SN' SENSOR_NOT_ACTIVE
+        }
+
+        if (sgvNS > 0) {
+            UploadItem uploadItem = new UploadItem();
+            uploadItems.add(uploadItem);
+            EntriesEndpoints.Entry entry = uploadItem.ack(uploadACK).entry();
 
-        entry.setKey600(key);
-        entry.setType("sgv");
-        entry.setDate(eventDate.getTime());
-        entry.setDateString(eventDate.toString());
+            entry.setKey600(key);
+            entry.setType("sgv");
+            entry.setDate(eventDate.getTime());
+            entry.setDateString(eventDate.toString());
 
-        entry.setSgv(sgv);
-        if (cgmTrend != null) entry.setDirection(PumpHistoryParser.TextEN.valueOf("NS_TREND_" + cgmTrend).getText());
+            entry.setSgv(sgvNS);
+            if (cgmTrend != null)
+                entry.setDirection(PumpHistoryParser.TextEN.valueOf("NS_TREND_" + cgmTrend).getText());
+        }
 
         return uploadItems;
     }
@@ -134,6 +147,7 @@ public class PumpHistoryCGM extends RealmObject implements PumpHistoryInterface
 
     public static void cgm(Realm realm, Date eventDate, int eventRTC, int eventOFFSET,
                            int sgv,
+                           byte sensorException,
                            String trend) {
 
         PumpHistoryCGM object = realm.where(PumpHistoryCGM.class)
@@ -148,6 +162,7 @@ public class PumpHistoryCGM extends RealmObject implements PumpHistoryInterface
             object.setCgmRTC(eventRTC);
             object.setCgmOFFSET(eventOFFSET);
             object.setSgv(sgv);
+            object.setSensorException(sensorException);
             object.setCgmTrend(trend);
             object.setUploadREQ(true);
         }
diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java
index f912a6f7a11535c6017304cba4f51a09815cce1e..10886a783aa72223dbc2dc97d41c40d34a635407 100644
--- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java
+++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java
@@ -110,6 +110,7 @@ public class PumpHistoryProfile extends RealmObject implements PumpHistoryInterf
             bp.carbshr = carbshr;
             bp.dia = dia;
             bp.delay = delay;
+            bp.carbsPerExchange = dataStore.getNsGramsPerExchange();
             bp.parseCarbRatios();
             bp.parseSensitivity();
             bp.parseTargets();
@@ -143,6 +144,7 @@ public class PumpHistoryProfile extends RealmObject implements PumpHistoryInterf
         String carbshr;
         String dia;
         String units;
+        int carbsPerExchange;
         int index = 0;
 
         private BasalProfile newProfile() {
@@ -211,8 +213,8 @@ public class PumpHistoryProfile extends RealmObject implements PumpHistoryInterf
                 for (int i = 0; i < items; i++) {
                     // pump grams/unit rate
                     rate1 = read32BEtoInt(carbRatios, index) / 10;
-                    // pump units/exchange rate (converted by dividing into 15g, the standard amount of grams per exchange)
-                    rate2 = (int) (15 / ((double) (read32BEtoInt(carbRatios, index + 4)) / 1000.0));
+                    // pump units/exchange rate (converted by dividing into carbsPerExchange default=15g, the standard amount of grams per exchange)
+                    rate2 = (int) (carbsPerExchange / ((double) (read32BEtoInt(carbRatios, index + 4)) / 1000.0));
                     time = read8toUInt(carbRatios, index + 8) * 30;
                     carbratio.add(addPeriod(time, "" + (rate1 > 0 ? rate1 : rate2)));
                     index += 9;
diff --git a/app/src/main/java/info/nightscout/android/model/store/DataStore.java b/app/src/main/java/info/nightscout/android/model/store/DataStore.java
index 028d362f27726ac8ef275ed59ce7d1027773e17d..1e959602b4013c50b7e058d325f8329ed76aaaa7 100644
--- a/app/src/main/java/info/nightscout/android/model/store/DataStore.java
+++ b/app/src/main/java/info/nightscout/android/model/store/DataStore.java
@@ -59,6 +59,7 @@ public class DataStore extends RealmObject {
     private long sysPollErrorRetry;
     private long sysPollOldSgvRetry;
     private boolean sysEnableWait500ms;
+    private boolean sysEnableUsbPermissionDialog;
 
     private boolean dbgEnableExtendedErrors;
     private boolean dbgEnableUploadErrors;
@@ -78,6 +79,8 @@ public class DataStore extends RealmObject {
     private int nsProfileDefault;
     private float nsActiveInsulinTime;
     private boolean nsEnablePatternChange;
+    private int nsGramsPerExchange;
+    private boolean nsGramsPerExchangeChanged = false;
     private boolean nsEnableInsertBGasCGM;
 
     private boolean urchinEnable;
@@ -450,6 +453,14 @@ public class DataStore extends RealmObject {
         this.sysEnableWait500ms = sysEnableWait500ms;
     }
 
+    public boolean isSysEnableUsbPermissionDialog() {
+        return sysEnableUsbPermissionDialog;
+    }
+
+    public void setSysEnableUsbPermissionDialog(boolean sysEnableUsbPermissionDialog) {
+        this.sysEnableUsbPermissionDialog = sysEnableUsbPermissionDialog;
+    }
+
     public boolean isDbgEnableExtendedErrors() {
         return dbgEnableExtendedErrors;
     }
@@ -586,6 +597,24 @@ public class DataStore extends RealmObject {
         this.nsEnablePatternChange = nsEnablePatternChange;
     }
 
+    public int getNsGramsPerExchange() {
+        return nsGramsPerExchange;
+    }
+
+    public void setNsGramsPerExchange(int nsGramsPerExchange) {
+        if (this.nsGramsPerExchange != 0 && this.nsGramsPerExchange != nsGramsPerExchange)
+            nsGramsPerExchangeChanged = true;
+        this.nsGramsPerExchange = nsGramsPerExchange;
+    }
+
+    public boolean isNsGramsPerExchangeChanged() {
+        return nsGramsPerExchangeChanged;
+    }
+
+    public void setNsGramsPerExchangeChanged(boolean nsGramsPerExchangeChanged) {
+        this.nsGramsPerExchangeChanged = nsGramsPerExchangeChanged;
+    }
+
     public boolean isNsEnableInsertBGasCGM() {
         return nsEnableInsertBGasCGM;
     }
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java
index 51199137b6d0c03d5ae289f85d47b608282d1652..5d7d7628e6fe7a3439d8d3422ddaf5ebce0cd206 100644
--- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java
@@ -9,6 +9,7 @@ import java.security.NoSuchAlgorithmException;
 import java.text.SimpleDateFormat;
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 
@@ -79,6 +80,15 @@ public class NightScoutUpload {
         uploadEvents(records);
     }
 
+    public static String formatDateForNS(Date date) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
+        SimpleDateFormat dftz = new SimpleDateFormat("Z", Locale.getDefault());
+        String tz = dftz.format(date);
+        if (tz.length() == 5 && (tz.startsWith("+") || tz.startsWith("-")))
+            return df.format(date) + tz.subSequence(0,3) + ":" + tz.subSequence(3,5);
+        return df.format(date);
+    }
+
     private void uploadEvents(List<PumpHistoryInterface> records) throws Exception {
 
         cleanupCheck();
@@ -256,8 +266,6 @@ public class NightScoutUpload {
         }
     }
 
-    private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
-
     private void uploadStatus(List<PumpStatusEvent> records, int uploaderBatteryLevel) throws Exception {
 
         List<DeviceEndpoints.DeviceStatus> deviceEntries = new ArrayList<>();
@@ -344,7 +352,7 @@ public class NightScoutUpload {
             pumpstatus = new PumpStatus(false, false, statusPUMP + " " + statusCGM);
 
             PumpInfo pumpInfo = new PumpInfo(
-                    ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                    formatDateForNS(record.getEventDate()),
                     new BigDecimal(record.getReservoirAmount()).setScale(0, BigDecimal.ROUND_HALF_UP),
                     iob,
                     battery,
@@ -354,7 +362,7 @@ public class NightScoutUpload {
             DeviceStatus deviceStatus = new DeviceStatus(
                     uploaderBatteryLevel,
                     record.getDeviceName(),
-                    ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                    formatDateForNS(record.getEventDate()),
                     pumpInfo
             );
 
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java
index 4bd81d722eb608ca4cc215c74d20e8eeb4a0d93b..b9b431749af4ce443165340dd53ef2418b5d8c3f 100644
--- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java
@@ -4,8 +4,12 @@ import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.support.annotation.NonNull;
 import android.util.Log;
 
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.List;
 
 import info.nightscout.android.UploaderApplication;
@@ -55,9 +59,9 @@ public class NightscoutStatus {
     private String ns_version_code = "0.0.0";
     private String ns_version_channel = "";
     private String ns_version_date = "";
-    private String ns_version_major = "0";
-    private String ns_version_minor = "0";
-    private String ns_version_point = "0";
+    private int ns_version_major = 0;
+    private int ns_version_minor = 0;
+    private int ns_version_point = 0;
 
     private boolean available;
     private long reporttime;
@@ -91,18 +95,21 @@ public class NightscoutStatus {
 
             if (!available || report) {
                 String url = dataStore.getNightscoutURL();
-                available = new getStatus().run(url);
+                String secret = dataStore.getNightscoutSECRET();
+                available = new getStatus().run(url, secret);
 
                 if (available && dataStore.isDbgEnableUploadErrors()) {
 
-                    if (dataStore.getNightscoutReportTime() == 0) {
+                    if (!dataStore.isNightscoutAvailable()) {
                         userLogMessage(ICON_INFO + "Nightscout site is available");
                     }
 
+                    /* This doesn't seem to be a requirement for newer versions of NS
                     if (!ns_authDefaultRoles.contains("readable")) {
                         userLogMessage(ICON_WARN + "Nightscout 'authDefaultRoles' check has failed, data can not be uploaded.");
-                        userLogMessage(ICON_HELP + "Add 'authDefaultRoles' and set to 'readable devicestatus-upload' in Azure or Heroku.");
+                        userLogMessage(ICON_HELP + "Add 'AUTH_DEFAULT_ROLES' and set to 'readable devicestatus-upload' in Azure or Heroku.");
                     }
+                    */
 
                     if (dataStore.isNsEnableTreatments() && !ns_careportal) {
                         userLogMessage(ICON_WARN + "Careportal is not enabled in Nightscout, treatment data can not be uploaded.");
@@ -112,8 +119,12 @@ public class NightscoutStatus {
                     if (report) {
                         reporttime = now;
 
-                        if (Integer.parseInt(ns_version_major) <= NS_MAJOR &&
-                                (Integer.parseInt(ns_version_minor) < NS_MINOR || (Integer.parseInt(ns_version_minor) == NS_MINOR && Integer.parseInt(ns_version_point) < NS_POINT))) {
+                        if (dataStore.isDbgEnableExtendedErrors()) {
+                            userLogMessage("NS version: " + ns_version);
+                        }
+
+                        if (ns_version_major <= NS_MAJOR &&
+                                (ns_version_minor < NS_MINOR || (ns_version_minor == NS_MINOR && ns_version_point < NS_POINT))) {
                             userLogMessage(ICON_HELP + "Your version of Nightscout is out of date. It is recommended to use the latest release version of Nightscout with the 600 Series Uploader.");
                         }
 
@@ -159,19 +170,6 @@ public class NightscoutStatus {
                         if (!(ns_enable.contains("IOB") || ns_enable.contains("iob"))) {
                             userLogMessage(ICON_HELP + "IOB active insulin can be shown in Nightscout. Add 'iob' to your ENABLE string in Azure or Heroku.");
                         }
-
-                        if (false) {
-                            String note = "";
-                            note += "Nightscout Status:";
-                            note += "\nver: " + ns_version;
-                            note += "\ncustomTitle: " + ns_customTitle;
-                            note += "\ncareportal: " + ns_careportal;
-                            note += "\nunits: " + ns_units;
-                            note += "\nauthDefaultRoles: " + ns_authDefaultRoles;
-                            note += "\nenable: " + ns_enable;
-                            note += "\npump: " + ns_pumpFields;
-                            userLogMessage(ICON_INFO + note);
-                        }
                     }
 
                 } else if (!available && dataStore.isDbgEnableUploadErrors())
@@ -192,11 +190,11 @@ public class NightscoutStatus {
     }
 
     private class getStatus extends Thread {
-        public boolean run(String url) {
+        public boolean run(String url, String secret) {
             boolean available = false;
 
             try{
-                UploadApi uploadApi = new UploadApi(url, null);
+                UploadApi uploadApi = new UploadApi(url, formToken(secret));
 
                 statusEndpoints = uploadApi.getStatusEndpoints();
 
@@ -220,16 +218,19 @@ public class NightscoutStatus {
                     }
                     String code[] = ns_version_code.split("\\.");
                     if (code.length == 3) {
-                        ns_version_major = code[0];
-                        ns_version_minor = code[1];
-                        ns_version_point = code[2];
+                        try {
+                            ns_version_major = Integer.parseInt(code[0]);
+                            ns_version_minor = Integer.parseInt(code[1]);
+                            ns_version_point = Integer.parseInt(code[2]);
+                        } catch (Exception ignored) {
+                        }
                     }
                 }
 
                 s = responseBody.body().getName();
                 if (s != null) ns_name = s;
 
-                ns_careportal = responseBody.body().isCareportalEnabled();
+                if (responseBody.body().isCareportalEnabled() != null) ns_careportal = responseBody.body().isCareportalEnabled();
 
                 if (responseBody.body().getSettings() != null) {
 
@@ -254,12 +255,13 @@ public class NightscoutStatus {
 
                     StatusEndpoints.Pump pump = responseBody.body().getExtendedSettings().getPump();
                     if (pump != null) {
-                        ns_pumpFields = pump.getFields();
+                        s = pump.getFields();
+                        if (s != null) ns_pumpFields = s;
                     }
 
                     StatusEndpoints.Devicestatus devicestatus = responseBody.body().getExtendedSettings().getDevicestatus();
                     if (devicestatus != null) {
-                        ns_devicestatus = devicestatus.isAdvanced();
+                        if (devicestatus.isAdvanced() != null) ns_devicestatus = devicestatus.isAdvanced();
                     }
                 }
 
@@ -267,7 +269,7 @@ public class NightscoutStatus {
 
             } catch (Exception e) {
                 Log.e(TAG, "Nightscout status check error", e);
-                if (dataStore.isDbgEnableExtendedErrors())
+                if (dataStore.isDbgEnableUploadErrors())
                     userLogMessage(ICON_WARN + "Nightscout status is not available: " + e.getMessage());
             }
 
@@ -275,6 +277,19 @@ public class NightscoutStatus {
         }
     }
 
+    @NonNull
+    private String formToken(String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+        MessageDigest digest = MessageDigest.getInstance("SHA-1");
+        byte[] bytes = secret.getBytes("UTF-8");
+        digest.update(bytes, 0, bytes.length);
+        bytes = digest.digest();
+        StringBuilder sb = new StringBuilder(bytes.length * 2);
+        for (byte b : bytes) {
+            sb.append(String.format("%02x", b & 0xff));
+        }
+        return sb.toString();
+    }
+
     private boolean isOnline() {
         ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         NetworkInfo netInfo = cm.getActiveNetworkInfo();
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java
index acc8af81c1e098891f9f5402913569d147e45ea3..82fb6f171655f759252a307089ab4638a13617e8 100644
--- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java
@@ -57,8 +57,10 @@ public class NightscoutUploadService extends Service {
         if (intent != null) {
             if (startId == 1)
                 new Upload().start();
-            else
+            else {
                 Log.d(TAG, "Service already in progress with previous task");
+                userLogMessage(ICON_WARN + "Uploading service is busy completing previous task. New records will be uploaded after the next poll.");
+            }
         }
 
         return START_NOT_STICKY;
diff --git a/app/src/main/java/info/nightscout/api/ProfileEndpoints.java b/app/src/main/java/info/nightscout/api/ProfileEndpoints.java
index 1b18827b42d38570027a6ac1e8c5da20710cc873..4f82c6087fc7fdda7d756899c8d73846fc6c9e1a 100644
--- a/app/src/main/java/info/nightscout/api/ProfileEndpoints.java
+++ b/app/src/main/java/info/nightscout/api/ProfileEndpoints.java
@@ -3,12 +3,11 @@ package info.nightscout.api;
 import com.google.gson.annotations.Expose;
 import com.google.gson.annotations.SerializedName;
 
-import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 
+import info.nightscout.android.upload.nightscout.NightScoutUpload;
 import okhttp3.ResponseBody;
 import retrofit2.Call;
 import retrofit2.http.Body;
@@ -116,14 +115,12 @@ public interface ProfileEndpoints {
         }
 
         public void setCreated_at(Date created_at) {
-            this.created_at = ISO8601_DATE_FORMAT.format(created_at);
+            this.created_at = NightScoutUpload.formatDateForNS(created_at);
         }
 
         public void setStartDate(Date startDate) {
-            this.startDate = ISO8601_DATE_FORMAT.format(startDate);
+            this.startDate = NightScoutUpload.formatDateForNS(startDate);
         }
-
-        private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
     }
 
     class BasalProfile {
@@ -250,10 +247,8 @@ public interface ProfileEndpoints {
         }
 
         public void setStartDate(Date startDate) {
-            this.startDate = ISO8601_DATE_FORMAT.format(startDate);
+            this.startDate = NightScoutUpload.formatDateForNS(startDate);
         }
-
-        private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
     }
 
     class TimePeriod {
diff --git a/app/src/main/java/info/nightscout/api/StatusEndpoints.java b/app/src/main/java/info/nightscout/api/StatusEndpoints.java
index 5d6cd1ae56202a39118c1735abeb778c00dde83f..66638bfb1b99543871b78ec13f0cbe3bf9f360c7 100644
--- a/app/src/main/java/info/nightscout/api/StatusEndpoints.java
+++ b/app/src/main/java/info/nightscout/api/StatusEndpoints.java
@@ -31,7 +31,7 @@ public interface StatusEndpoints {
         private String serverTime;
         @SerializedName("serverTimeEpoch")
         @Expose
-        private Long serverTimeEpoch;
+        private Float serverTimeEpoch;
         @SerializedName("apiEnabled")
         @Expose
         private Boolean apiEnabled;
@@ -86,11 +86,11 @@ public interface StatusEndpoints {
             this.serverTime = serverTime;
         }
 
-        public Long getServerTimeEpoch() {
+        public Float getServerTimeEpoch() {
             return serverTimeEpoch;
         }
 
-        public void setServerTimeEpoch(Long serverTimeEpoch) {
+        public void setServerTimeEpoch(Float serverTimeEpoch) {
             this.serverTimeEpoch = serverTimeEpoch;
         }
 
@@ -159,7 +159,7 @@ public interface StatusEndpoints {
         private String units;
         @SerializedName("timeFormat")
         @Expose
-        private Long timeFormat;
+        private Float timeFormat;
         @SerializedName("nightMode")
         @Expose
         private Boolean nightMode;
@@ -180,43 +180,43 @@ public interface StatusEndpoints {
         private Boolean alarmUrgentHigh;
         @SerializedName("alarmUrgentHighMins")
         @Expose
-        private List<Long> alarmUrgentHighMins = null;
+        private List<Float> alarmUrgentHighMins = null;
         @SerializedName("alarmHigh")
         @Expose
         private Boolean alarmHigh;
         @SerializedName("alarmHighMins")
         @Expose
-        private List<Long> alarmHighMins = null;
+        private List<Float> alarmHighMins = null;
         @SerializedName("alarmLow")
         @Expose
         private Boolean alarmLow;
         @SerializedName("alarmLowMins")
         @Expose
-        private List<Long> alarmLowMins = null;
+        private List<Float> alarmLowMins = null;
         @SerializedName("alarmUrgentLow")
         @Expose
         private Boolean alarmUrgentLow;
         @SerializedName("alarmUrgentLowMins")
         @Expose
-        private List<Long> alarmUrgentLowMins = null;
+        private List<Float> alarmUrgentLowMins = null;
         @SerializedName("alarmUrgentMins")
         @Expose
-        private List<Long> alarmUrgentMins = null;
+        private List<Float> alarmUrgentMins = null;
         @SerializedName("alarmWarnMins")
         @Expose
-        private List<Long> alarmWarnMins = null;
+        private List<Float> alarmWarnMins = null;
         @SerializedName("alarmTimeagoWarn")
         @Expose
         private Boolean alarmTimeagoWarn;
         @SerializedName("alarmTimeagoWarnMins")
         @Expose
-        private Long alarmTimeagoWarnMins;
+        private Float alarmTimeagoWarnMins;
         @SerializedName("alarmTimeagoUrgent")
         @Expose
         private Boolean alarmTimeagoUrgent;
         @SerializedName("alarmTimeagoUrgentMins")
         @Expose
-        private Long alarmTimeagoUrgentMins;
+        private Float alarmTimeagoUrgentMins;
         @SerializedName("language")
         @Expose
         private String language;
@@ -231,10 +231,10 @@ public interface StatusEndpoints {
         private String showForecast;
         @SerializedName("focusHours")
         @Expose
-        private Long focusHours;
+        private Float focusHours;
         @SerializedName("heartbeat")
         @Expose
-        private Long heartbeat;
+        private Float heartbeat;
         @SerializedName("baseURL")
         @Expose
         private String baseURL;
@@ -262,11 +262,11 @@ public interface StatusEndpoints {
             this.units = units;
         }
 
-        public Long getTimeFormat() {
+        public Float getTimeFormat() {
             return timeFormat;
         }
 
-        public void setTimeFormat(Long timeFormat) {
+        public void setTimeFormat(Float timeFormat) {
             this.timeFormat = timeFormat;
         }
 
@@ -318,11 +318,11 @@ public interface StatusEndpoints {
             this.alarmUrgentHigh = alarmUrgentHigh;
         }
 
-        public List<Long> getAlarmUrgentHighMins() {
+        public List<Float> getAlarmUrgentHighMins() {
             return alarmUrgentHighMins;
         }
 
-        public void setAlarmUrgentHighMins(List<Long> alarmUrgentHighMins) {
+        public void setAlarmUrgentHighMins(List<Float> alarmUrgentHighMins) {
             this.alarmUrgentHighMins = alarmUrgentHighMins;
         }
 
@@ -334,11 +334,11 @@ public interface StatusEndpoints {
             this.alarmHigh = alarmHigh;
         }
 
-        public List<Long> getAlarmHighMins() {
+        public List<Float> getAlarmHighMins() {
             return alarmHighMins;
         }
 
-        public void setAlarmHighMins(List<Long> alarmHighMins) {
+        public void setAlarmHighMins(List<Float> alarmHighMins) {
             this.alarmHighMins = alarmHighMins;
         }
 
@@ -350,11 +350,11 @@ public interface StatusEndpoints {
             this.alarmLow = alarmLow;
         }
 
-        public List<Long> getAlarmLowMins() {
+        public List<Float> getAlarmLowMins() {
             return alarmLowMins;
         }
 
-        public void setAlarmLowMins(List<Long> alarmLowMins) {
+        public void setAlarmLowMins(List<Float> alarmLowMins) {
             this.alarmLowMins = alarmLowMins;
         }
 
@@ -366,27 +366,27 @@ public interface StatusEndpoints {
             this.alarmUrgentLow = alarmUrgentLow;
         }
 
-        public List<Long> getAlarmUrgentLowMins() {
+        public List<Float> getAlarmUrgentLowMins() {
             return alarmUrgentLowMins;
         }
 
-        public void setAlarmUrgentLowMins(List<Long> alarmUrgentLowMins) {
+        public void setAlarmUrgentLowMins(List<Float> alarmUrgentLowMins) {
             this.alarmUrgentLowMins = alarmUrgentLowMins;
         }
 
-        public List<Long> getAlarmUrgentMins() {
+        public List<Float> getAlarmUrgentMins() {
             return alarmUrgentMins;
         }
 
-        public void setAlarmUrgentMins(List<Long> alarmUrgentMins) {
+        public void setAlarmUrgentMins(List<Float> alarmUrgentMins) {
             this.alarmUrgentMins = alarmUrgentMins;
         }
 
-        public List<Long> getAlarmWarnMins() {
+        public List<Float> getAlarmWarnMins() {
             return alarmWarnMins;
         }
 
-        public void setAlarmWarnMins(List<Long> alarmWarnMins) {
+        public void setAlarmWarnMins(List<Float> alarmWarnMins) {
             this.alarmWarnMins = alarmWarnMins;
         }
 
@@ -398,11 +398,11 @@ public interface StatusEndpoints {
             this.alarmTimeagoWarn = alarmTimeagoWarn;
         }
 
-        public Long getAlarmTimeagoWarnMins() {
+        public Float getAlarmTimeagoWarnMins() {
             return alarmTimeagoWarnMins;
         }
 
-        public void setAlarmTimeagoWarnMins(Long alarmTimeagoWarnMins) {
+        public void setAlarmTimeagoWarnMins(Float alarmTimeagoWarnMins) {
             this.alarmTimeagoWarnMins = alarmTimeagoWarnMins;
         }
 
@@ -414,11 +414,11 @@ public interface StatusEndpoints {
             this.alarmTimeagoUrgent = alarmTimeagoUrgent;
         }
 
-        public Long getAlarmTimeagoUrgentMins() {
+        public Float getAlarmTimeagoUrgentMins() {
             return alarmTimeagoUrgentMins;
         }
 
-        public void setAlarmTimeagoUrgentMins(Long alarmTimeagoUrgentMins) {
+        public void setAlarmTimeagoUrgentMins(Float alarmTimeagoUrgentMins) {
             this.alarmTimeagoUrgentMins = alarmTimeagoUrgentMins;
         }
 
@@ -454,19 +454,19 @@ public interface StatusEndpoints {
             this.showForecast = showForecast;
         }
 
-        public Long getFocusHours() {
+        public Float getFocusHours() {
             return focusHours;
         }
 
-        public void setFocusHours(Long focusHours) {
+        public void setFocusHours(Float focusHours) {
             this.focusHours = focusHours;
         }
 
-        public Long getHeartbeat() {
+        public Float getHeartbeat() {
             return heartbeat;
         }
 
-        public void setHeartbeat(Long heartbeat) {
+        public void setHeartbeat(Float heartbeat) {
             this.heartbeat = heartbeat;
         }
 
@@ -524,46 +524,46 @@ public interface StatusEndpoints {
 
         @SerializedName("bgHigh")
         @Expose
-        private Long bgHigh;
+        private Float bgHigh;
         @SerializedName("bgTargetTop")
         @Expose
-        private Long bgTargetTop;
+        private Float bgTargetTop;
         @SerializedName("bgTargetBottom")
         @Expose
-        private Long bgTargetBottom;
+        private Float bgTargetBottom;
         @SerializedName("bgLow")
         @Expose
-        private Long bgLow;
+        private Float bgLow;
 
-        public Long getBgHigh() {
+        public Float getBgHigh() {
             return bgHigh;
         }
 
-        public void setBgHigh(Long bgHigh) {
+        public void setBgHigh(Float bgHigh) {
             this.bgHigh = bgHigh;
         }
 
-        public Long getBgTargetTop() {
+        public Float getBgTargetTop() {
             return bgTargetTop;
         }
 
-        public void setBgTargetTop(Long bgTargetTop) {
+        public void setBgTargetTop(Float bgTargetTop) {
             this.bgTargetTop = bgTargetTop;
         }
 
-        public Long getBgTargetBottom() {
+        public Float getBgTargetBottom() {
             return bgTargetBottom;
         }
 
-        public void setBgTargetBottom(Long bgTargetBottom) {
+        public void setBgTargetBottom(Float bgTargetBottom) {
             this.bgTargetBottom = bgTargetBottom;
         }
 
-        public Long getBgLow() {
+        public Float getBgLow() {
             return bgLow;
         }
 
-        public void setBgLow(Long bgLow) {
+        public void setBgLow(Float bgLow) {
             this.bgLow = bgLow;
         }
 
diff --git a/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java b/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java
index ebb7e9492fc9b1d82490459d8b3c1b50d6903797..3ba1261c8535f0e00df7eac7f44607afe1b65c81 100644
--- a/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java
+++ b/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java
@@ -3,11 +3,10 @@ package info.nightscout.api;
 import com.google.gson.annotations.SerializedName;
 
 import java.math.BigDecimal;
-import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
-import java.util.Locale;
 
+import info.nightscout.android.upload.nightscout.NightScoutUpload;
 import okhttp3.ResponseBody;
 import retrofit2.Call;
 import retrofit2.http.Body;
@@ -269,10 +268,8 @@ public interface TreatmentsEndpoints {
         }
 
         public void setCreated_at(Date created_at) {
-            this.created_at = ISO8601_DATE_FORMAT.format(created_at);
+            this.created_at = NightScoutUpload.formatDateForNS(created_at);
         }
-
-        private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
     }
 
     // find treatment using key
diff --git a/app/src/main/java/info/nightscout/urchin/Urchin.java b/app/src/main/java/info/nightscout/urchin/Urchin.java
index ccf1b17c0c6836090586d4254ed0399b236f5f6e..e0405497aeab4b84e7d196b67147ed2873aeb6c7 100644
--- a/app/src/main/java/info/nightscout/urchin/Urchin.java
+++ b/app/src/main/java/info/nightscout/urchin/Urchin.java
@@ -538,7 +538,7 @@ public class Urchin {
                     insulin =  results.first().getNormalProgrammedAmount();
             }
 
-            text += new BigDecimal(insulin).setScale(1, BigDecimal.ROUND_HALF_UP).stripTrailingZeros()
+            text += new BigDecimal(insulin).setScale(1, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString()
                     + styleUnits() + styleConcatenate() + styleTime(results.first().getProgrammedDate().getTime());
         }
 
@@ -640,7 +640,6 @@ public class Urchin {
                 && pumpStatusEvent.isCgmActive()
                 && pumpStatusEvent.getEventDate().getTime() >= timeNow - 15 *60000L) {
 
-            text = "";
             if (pumpStatusEvent.isCgmCalibrating())
                 text = "C";
             else {
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 8bd64359842148e86b01db983f79444acfb6bd13..a721247dee3f8ab397b26793fb1748c97980c7a3 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -170,6 +170,44 @@
         <item>8</item>
     </string-array>
 
+    <string-array name="grams_per_exchange">
+        <item>5 Grams</item>
+        <item>6 Grams</item>
+        <item>7 Grams</item>
+        <item>8 Grams</item>
+        <item>9 Grams</item>
+        <item>10 Grams</item>
+        <item>11 Grams</item>
+        <item>12 Grams</item>
+        <item>13 Grams</item>
+        <item>14 Grams</item>
+        <item>15 Grams</item>
+        <item>16 Grams</item>
+        <item>17 Grams</item>
+        <item>18 Grams</item>
+        <item>19 Grams</item>
+        <item>20 Grams</item>
+    </string-array>
+
+    <string-array name="grams_per_exchange_values">
+        <item>5</item>
+        <item>6</item>
+        <item>7</item>
+        <item>8</item>
+        <item>9</item>
+        <item>10</item>
+        <item>11</item>
+        <item>12</item>
+        <item>13</item>
+        <item>14</item>
+        <item>15</item>
+        <item>16</item>
+        <item>17</item>
+        <item>18</item>
+        <item>19</item>
+        <item>20</item>
+    </string-array>
+
     <string-array name="poll_period">
         <item>10 seconds</item>
         <item>15 seconds</item>
@@ -296,14 +334,14 @@
     </string-array>
 
     <string-array name="urchin_battery">
-        <item>0-100</item>
-        <item>0-100&#65130;</item>
-        <item>0-99</item>
-        <item>0-99&#65130;</item>
-        <item>0-9</item>
-        <item>Hi-Lo</item>
-        <item>H-M-L</item>
-        <item>F-H-M-L-E</item>
+        <item>0&#8211;100</item>
+        <item>0&#8211;100&#65130;</item>
+        <item>0&#8211;99</item>
+        <item>0&#8211;99&#65130;</item>
+        <item>0&#8211;9</item>
+        <item>Hi&#8211;Lo</item>
+        <item>H&#8211;M&#8211;L</item>
+        <item>F&#8211;H&#8211;M&#8211;L&#8211;E</item>
     </string-array>
 
     <string-array name="urchin_units">
@@ -317,7 +355,7 @@
         <item>space: \'2.55u 1h15m\'</item>
         <item>dot: \'2.5u·1h15m\'</item>
         <item>bullet: \'2.5u•1h15m\'</item>
-        <item>dash: \'2.5u-1h15m\'</item>
+        <item>dash: \'2.5u&#8211;1h15m\'</item>
         <item>slash: \'2.5u/1h15m\'</item>
         <item>slash: \'2.5u\\1h15m\'</item>
         <item>line: \'2.5u|1h15m\'</item>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e79774620b5153c642eac92db2ba29db1e352e31..32dafd6d2aaf229f2450770929c6c8a38b55b0aa 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -171,6 +171,15 @@
                     android:summary="Send basal pattern changes to Nightscout"/>
             </PreferenceCategory>
 
+            <PreferenceCategory android:title="Misc">
+                <ListPreference android:title="Grams Per Exchange"
+                    android:key="nsGramsPerExchange"
+                    android:defaultValue="15"
+                    android:summary="%s"
+                    android:entries="@array/grams_per_exchange"
+                    android:entryValues="@array/grams_per_exchange_values"/>
+            </PreferenceCategory>
+
             <PreferenceCategory android:title="Pattern and Preset Naming">
                 <PreferenceScreen
                     android:title="Basal Patterns"
@@ -738,6 +747,18 @@
                     android:switchTextOn="on"
                     android:title="Control / Passthrough"/>
             </PreferenceCategory>
+
+            <PreferenceCategory android:title="USB">
+                <info.nightscout.android.utils.CustomSwitchPreference
+                    android:disableDependentsState="false"
+                    android:key="sysEnableUsbPermissionDialog"
+                    android:defaultValue="false"
+                    android:summaryOff="Request permission via OS dialog"
+                    android:summaryOn="Request permission via APP dialog"
+                    android:switchTextOff="off"
+                    android:switchTextOn="on"
+                    android:title="Permission Request"/>
+            </PreferenceCategory>
         </PreferenceScreen>
 
         <PreferenceScreen