// safety check to make sure an expected 0x81 response is received before next expected 0x80 response
        // very infrequent as clearMessage catches most issues but very important to save a CNL error situation
        protected async Task <byte[]> ReadMessage_0x81(UsbHidDriver mDevice, int timeout = READ_TIMEOUT_MS)
        {
            byte[] responseBytes;
            bool   doRetry;
            var    expected = (byte)0x81;

            do
            {
                responseBytes = await ReadMessage(mDevice, timeout);

                var actual = responseBytes[18];
                if (actual != expected)
                {
                    doRetry = true;
                    Log.d(TAG, "readMessage0x81: did not get 0x81 response, got " + HexDump.ToHexstring(actual));
                    MedtronicCnlService.cnl0x81++;
                }
                else
                {
                    doRetry = false;
                    break;
                }
            } while (doRetry);

            return(responseBytes);
        }
        protected async Task <byte[]> ReadFromPump(UsbHidDriver mDevice, MedtronicCnlSession pumpSession, string tag)
        {
            tag = " (" + tag + ")";

            MultipacketSession multipacketSession = null;

            byte[] tupple;
            byte[] payload   = null;
            byte[] decrypted = null;

            bool fetchMoreData    = true;
            int  retry            = 0;
            int  expectedSegments = 0;

            int cmd;

            while (fetchMoreData)
            {
                if (multipacketSession != null)
                {
                    do
                    {
                        if (expectedSegments < 1)
                        {
                            tupple = multipacketSession.MissingSegments();
                            new MultipacketResendPacketsMessage(pumpSession, tupple).send(mDevice);
                            expectedSegments = read16BEtoUInt(tupple, 0x02);
                        }
                        try
                        {
                            payload = await ReadMessage(mDevice, MULTIPACKET_TIMEOUT_MS);

                            break;
                        }
                        catch (TimeoutException e)
                        {
                            if (++retry >= SEGMENT_RETRY)
                            {
                                throw new TimeoutException("Timeout waiting for response from pump (multipacket)" + tag);
                            }
                            Log.d(TAG, "*** Multisession timeout, expecting:" + expectedSegments + " retry: " + retry);
                            expectedSegments = 0;
                        }
                    } while (retry > 0);
                }
                else
                {
                    try
                    {
                        payload = await ReadMessage(mDevice, READ_TIMEOUT_MS);
                    }
                    catch (TimeoutException e)
                    {
                        throw new TimeoutException("Timeout waiting for response from pump" + tag);
                    }
                }

                if (payload.Length < 0x0039)
                {
                    Log.d(TAG, "*** bad response" + HexDump.DumpHexstring(payload, 0x12, payload.Length - 0x14));
                    fetchMoreData = true;
                }
                else
                {
                    decrypted = Decode(pumpSession, payload);

                    cmd = read16BEtoUInt(decrypted, RESPONSE_COMMAND);
                    Log.d(TAG, "CMD: " + HexDump.ToHexstring(cmd));

                    if (MedtronicSendMessageRequestMessage.MessageType.EHSM_SESSION.response(cmd))
                    {
                        // EHSM_SESSION(0)
                        Log.d(TAG, "*** EHSM response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = true;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.NAK_COMMAND.response(cmd))
                    {
                        Log.d(TAG, "*** NAK response" + HexDump.DumpHexstring(decrypted));
                        ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                        // if multipacket was in progress we may need to clear 2 EHSM_SESSION(1) messages from pump
                        short nakcmd  = read16BEtoShort(decrypted, 3);
                        byte  nakcode = decrypted[5];
                        throw new UnexpectedMessageException("Pump sent a NAK(" + string.Format("%02X", nakcmd) + ":" + string.Format("%02X", nakcode) + ") response" + tag);
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.INITIATE_MULTIPACKET_TRANSFER.response(cmd))
                    {
                        multipacketSession = new MultipacketSession(decrypted);
                        new AckMessage(pumpSession, MedtronicSendMessageRequestMessage.MessageType.INITIATE_MULTIPACKET_TRANSFER.response()).send(mDevice);
                        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--;

                        if (multipacketSession.PayloadComplete)
                        {
                            Log.d(TAG, "*** Multisession Complete");
                            new AckMessage(pumpSession, MedtronicSendMessageRequestMessage.MessageType.MULTIPACKET_SEGMENT_TRANSMISSION.response()).send(mDevice);

                            // read 0412 = EHSM_SESSION(1)
                            payload = await ReadMessage(mDevice, READ_TIMEOUT_MS);

                            decrypted = Decode(pumpSession, payload);
                            Log.d(TAG, "*** response" + HexDump.DumpHexstring(decrypted));

                            return(multipacketSession.Response);
                        }
                        else
                        {
                            fetchMoreData = true;
                        }
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.END_HISTORY_TRANSMISSION.response(cmd))
                    {
                        Log.d(TAG, "*** END_HISTORY_TRANSMISSION response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_PUMP_TIME.response(cmd))
                    {
                        Log.d(TAG, "*** READ_PUMP_TIME response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_PUMP_STATUS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_PUMP_STATUS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_HISTORY_INFO.response(cmd))
                    {
                        Log.d(TAG, "*** READ_HISTORY_INFO response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BASAL_PATTERN.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BASAL_PATTERN response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BOLUS_WIZARD_CARB_RATIOS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BOLUS_WIZARD_CARB_RATIOS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BOLUS_WIZARD_SENSITIVITY_FACTORS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BOLUS_WIZARD_SENSITIVITY_FACTORS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BOLUS_WIZARD_BG_TARGETS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BOLUS_WIZARD_BG_TARGETS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else
                    {
                        Log.d(TAG, "*** ??? response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = true;
                    }
                }
            }

            // when returning non-multipacket decrypted data, we need to trim the 2 byte checksum
            if (decrypted == null)
            {
                return(payload);
            }
            return(decrypted.Partial(0, decrypted.Length - 2));
        }