decoding-carelink icon indicating copy to clipboard operation
decoding-carelink copied to clipboard

Enhancement request: Would love to be able to program basal schedules.

Open TC2013 opened this issue 9 years ago • 11 comments

Apparently using ParadigmPAL software, one can make basal changes and upload them to his or her insulin pump. Would love to see if there is a way to add this feature to decocare. If I can be pointed in the right direction, I'm willing to put the hours in.


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

TC2013 avatar Mar 25 '15 21:03 TC2013

Great feature request!

Here's a list of all the commands I found:

./ddms/deviceportreader/ComStation0.java:8:    static final byte CMD_INITIALIZE = 1;
./ddms/deviceportreader/ComStation0.java:9:    static final byte CMD_READ_STATUS = 2;
./ddms/deviceportreader/ComStation0.java:10:    static final byte CMD_SELF_TEST = 3;
./ddms/deviceportreader/ComStation0.java:11:    static final byte CMD_TRANSMIT_PACKET = 4;
./ddms/deviceportreader/ComStation0.java:12:    static final byte CMD_TRANSMIT_LAST_PACKET = 5;
./ddms/deviceportreader/ComStation0.java:13:    static final byte CMD_RS232_MODE_SELECT = 6;
./ddms/deviceportreader/ComStation0.java:14:    static final byte CMD_RF_MODE_SELECT = 7;
./ddms/deviceportreader/ComStation0.java:15:    static final byte CMD_TRANSFER_DATA = 8;
./ddms/deviceportreader/ComStation0.java:16:    static final byte CMD_SET_FEATURES = 9;
./ddms/deviceportreader/ComStation0.java:17:    static final byte CMD_MULTI_TRANSMIT = 10;
./ddms/deviceportreader/MMX22.java:12:    private static final int CMD_READ_SENSOR_SETTINGS = 153;
./ddms/deviceportreader/MMX22.java:13:    private static final int CMD_SET_SENSOR_SETTINGS = 61;
./ddms/deviceportreader/MMPump$ReaderWriter.java:275:        //   529: ldc             " (CMD FAILED)"
./ddms/deviceportreader/MMPump$ReaderWriter.java:677:        //   529: ldc             " (CMD FAILED)"
./ddms/deviceportreader/MM512$Command.java:10:    private static final int CMD_PACKET_LENGTH = 7;
./ddms/deviceportreader/MM512$Command.java:158:        MedicalDevice.logInfoMedium(this, "sendCommand: SENDING CMD " + this + "for pump #" + MM512.this.m_serialNumber);
./ddms/deviceportreader/MM512.java:24:    private static final int CMD_POWER_CTRL = 93;
./ddms/deviceportreader/MM512.java:25:    private static final int CMD_BEGIN_PARAMETER_SETTING = 38;
./ddms/deviceportreader/MM512.java:26:    private static final int CMD_END_PARAMETER_SETTING = 39;
./ddms/deviceportreader/MM512.java:27:    private static final int CMD_TEMP_BASAL_RATE = 76;
./ddms/deviceportreader/MM512.java:28:    private static final int CMD_READ_ERROR_STATUS = 117;
./ddms/deviceportreader/MM512.java:29:    private static final int CMD_READ_FIRMWARE_VER = 116;
./ddms/deviceportreader/MM512.java:30:    private static final int CMD_READ_PUMP_ID = 113;
./ddms/deviceportreader/MM512.java:31:    private static final int CMD_READ_PUMP_STATE = 131;
./ddms/deviceportreader/MM512.java:32:    private static final int CMD_READ_REMOTE_CTRL_IDS = 118;
./ddms/deviceportreader/MM512.java:33:    private static final int CMD_READ_TEMP_BASAL = 152;
./ddms/deviceportreader/MM512.java:34:    private static final int CMD_READ_RTC = 112;
./ddms/deviceportreader/MM512.java:35:    private static final int CMD_SET_RF_REMOTE_ID = 81;
./ddms/deviceportreader/MM512.java:36:    private static final int CMD_SET_ALERT_TYPE = 84;
./ddms/deviceportreader/MM512.java:37:    private static final int CMD_SET_AUTO_OFF = 78;
./ddms/deviceportreader/MM512.java:38:    private static final int CMD_SET_BLOCK_ENABLE = 82;
./ddms/deviceportreader/MM512.java:39:    private static final int CMD_SET_CURRENT_PATTERN = 74;
./ddms/deviceportreader/MM512.java:40:    private static final int CMD_SET_EASY_BOLUS_ENABLE = 79;
./ddms/deviceportreader/MM512.java:41:    private static final int CMD_SET_MAX_BOLUS = 65;
./ddms/deviceportreader/MM512.java:42:    private static final int CMD_SET_PATTERNS_ENABLE = 85;
./ddms/deviceportreader/MM512.java:43:    private static final int CMD_SET_RF_ENABLE = 87;
./ddms/deviceportreader/MM512.java:44:    private static final int CMD_SET_RTC = 64;
./ddms/deviceportreader/MM512.java:45:    private static final int CMD_SET_TIME_FORMAT = 92;
./ddms/deviceportreader/MM512.java:46:    private static final int CMD_SET_VAR_BOLUS_ENABLE = 69;
./ddms/deviceportreader/MM512.java:47:    private static final int CMD_READ_STD_PROFILES = 146;
./ddms/deviceportreader/MM512.java:48:    private static final int CMD_READ_A_PROFILES = 147;
./ddms/deviceportreader/MM512.java:49:    private static final int CMD_READ_B_PROFILES = 148;
./ddms/deviceportreader/MM512.java:50:    private static final int CMD_READ_SETTINGS = 145;
./ddms/deviceportreader/MM512.java:51:    private static final int CMD_SET_STD_PROFILE = 111;
./ddms/deviceportreader/MM512.java:52:    private static final int CMD_SET_A_PROFILE = 48;
./ddms/deviceportreader/MM512.java:53:    private static final int CMD_SET_B_PROFILE = 49;
./ddms/deviceportreader/MM512.java:54:    private static final int CMD_SET_MAX_BASAL = 110;
./ddms/deviceportreader/MM512.java:55:    private static final int CMD_READ_BG_ALARM_CLOCKS = 142;
./ddms/deviceportreader/MM512.java:56:    private static final int CMD_READ_BG_ALARM_ENABLE = 151;
./ddms/deviceportreader/MM512.java:57:    private static final int CMD_READ_BG_REMINDER_ENABLE = 144;
./ddms/deviceportreader/MM512.java:58:    private static final int CMD_READ_BG_TARGETS = 140;
./ddms/deviceportreader/MM512.java:59:    private static final int CMD_READ_BG_UNITS = 137;
./ddms/deviceportreader/MM512.java:60:    private static final int CMD_READ_BOLUS_WIZARD_SETUP_STATUS = 135;
./ddms/deviceportreader/MM512.java:61:    private static final int CMD_READ_CARB_RATIOS = 138;
./ddms/deviceportreader/MM512.java:62:    private static final int CMD_READ_CARB_UNITS = 136;
./ddms/deviceportreader/MM512.java:63:    private static final int CMD_READ_LOGIC_LINK_IDS = 149;
./ddms/deviceportreader/MM512.java:64:    private static final int CMD_READ_INSULIN_SENSITIVITIES = 139;
./ddms/deviceportreader/MM512.java:65:    private static final int CMD_READ_RESERVOIR_WARNING = 143;
./ddms/deviceportreader/MM512.java:66:    private static final int CMD_READ_PUMP_MODEL_NUMBER = 141;
./ddms/deviceportreader/MM512.java:67:    private static final int CMD_SET_BG_ALARM_CLOCKS = 107;
./ddms/deviceportreader/MM512.java:68:    private static final int CMD_SET_BG_ALARM_ENABLE = 103;
./ddms/deviceportreader/MM512.java:69:    private static final int CMD_SET_BG_REMINDER_ENABLE = 108;
./ddms/deviceportreader/MM512.java:70:    private static final int CMD_SET_BOLUS_WIZARD_SETUP = 94;
./ddms/deviceportreader/MM512.java:71:    private static final int CMD_SET_INSULIN_ACTION_TYPE = 88;
./ddms/deviceportreader/MM512.java:72:    private static final int CMD_SET_LOGIC_LINK_ENABLE = 51;
./ddms/deviceportreader/MM512.java:73:    private static final int CMD_SET_LOGIC_LINK_ID = 50;
./ddms/deviceportreader/MM512.java:74:    private static final int CMD_SET_RESERVOIR_WARNING = 106;
./ddms/deviceportreader/MM512.java:75:    private static final int CMD_SET_TEMP_BASAL_TYPE = 104;
./ddms/deviceportreader/MM512.java:76:    private static final int CMD_SUSPEND_RESUME = 77;
./ddms/deviceportreader/MM512.java:864:        private static final int CMD_PACKET_LENGTH = 7;
./ddms/deviceportreader/MM512.java:1012:            MedicalDevice.logInfoMedium(this, "sendCommand: SENDING CMD " + this + "for pump #" + MM512.this.m_serialNumber);
./ddms/deviceportreader/MMX15.java:12:    private static final int CMD_READ_SETTINGS = 192;
./ddms/deviceportreader/MMX15.java:13:    private static final int CMD_READ_BG_TARGETS = 159;
./ddms/deviceportreader/MMX15.java:14:    private static final int CMD_SET_BOLUS_WIZARD_SETUP = 178;
./ddms/deviceportreader/MMX15.java:15:    private static final int CMD_READ_BOLUS_REMINDER_ENABLE = 197;
./ddms/deviceportreader/MMX15.java:16:    private static final int CMD_READ_BOLUS_REMINDERS = 198;
./ddms/deviceportreader/MMX15.java:17:    private static final int CMD_READ_CURRENT_PUMP_STATUS = 206;
./ddms/deviceportreader/MMX15.java:18:    private static final int CMD_SET_KEYPAD_LOCK_UNLOCK_KEYPAD = 185;
./ddms/deviceportreader/MMPump.java:21:    static final int TYPE_CMD_PARAMS = 11;
./ddms/deviceportreader/MMPump.java:702:            //   529: ldc             " (CMD FAILED)"
./ddms/deviceportreader/MMPump.java:1107:            //   529: ldc             " (CMD FAILED)"

bewest avatar Mar 25 '15 21:03 bewest

From MM512.java:

 181         this.m_cmdWriteProfileSTD = new CommandWrite(111, "Set Profile Standard", 144);
 182         this.m_cmdWriteProfileA = new CommandWrite(48, "Set Profile A", 144);
 183         this.m_cmdWriteProfileB = new CommandWrite(49, "Set Profile B", 144);           
 184         this.m_cmdWriteMaxBasal = new CommandWrite(110, "Set Maximum Basal", 2);        
 185         this.m_cmdWriteAbsoluteMaxBasal = new CommandWrite(110, "Set Absolute Maximum Ba     sal", 2);                        

bewest avatar Mar 25 '15 21:03 bewest

Looks like bunch of changes will be needed, according to this the profile/schedule is 144 bytes. The protocol itself only handles 64 bytes at a time, so this requires munging the one schedule into several frames and using stick.py to transfer multiple frames per command to be sent.

Again, in MM512.java, the functions makeCommandPacket vs makeDataPacket is very interesting, especially with respect to how makeDataPacket is used repeatedly by executeIO in variety of ways in Command, which is re-used by CommandWrite, depending on how long the payload is.

bewest avatar Mar 25 '15 22:03 bewest

hmmmm - command number 7 looks kinda interesting......... I wonder if we can use that to set the right mode for the newer pumps........ 87 too

kenstack avatar Mar 25 '15 22:03 kenstack

For basal profile, I suspect the data structure is the same as the history event data structure (as most things tend to be).

https://github.com/bewest/decoding-carelink/blob/6e096043133d3662f70da24d70ed4b47faa677d9/decocare/commands.py#L851-L903

It appears to be a list of 3 byte tuples:

  • rate
  • ?? padding? zero byte?
  • offset, the number of 30 minute intervals since midnight, this translates to an "hour:minute" time of day.

bewest avatar Mar 25 '15 22:03 bewest

For someone feeling adventurous, you can test new commands by hand, but not sure if these type of commands require different handling by the underlying stick.py.

Something like:


mm-send-comm.py ManualCommand 111 --maxRecords 2 --params 40 --params 0 --params 0

Might set a 24 schedule for 1.0 unit... but probably not.

bewest avatar Mar 25 '15 22:03 bewest

I just tried that and it's going to be a little more involved than just the three params I think. I also ran this, which is the current schedule, with just the first byte changed.

don't do this

If you do this, you will have to remove the stick and plug it back in again to make it work.

do stuff with an insulin pump over RF

using Namespace(autoinit=False, bytesPerRecord=64, code=111, command='ManualCommand', descr='Experimental command', dryrun=False, effectTime=0.5, init=False, maxRecords=1, name=None, no_postlude=False, no_prelude=False, no_rf_prelude=False, params=['40', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00'], port='', postfix=None, prefix=None, prefix_path='', save=False, saveall=False, serial='466990', session_life=10, verbose=None)

{'radio': {'errors.crc': 0,
           'errors.naks': 0,
           'errors.sequence': 0,
           'errors.timeouts': 0,
           'packets.received': 2L,
           'packets.transmit': 2L},
 'usb': {'errors.crc': 0,
         'errors.naks': 0,
         'errors.sequence': 0,
         'errors.timeouts': 0,
         'packets.received': 16L,
         'packets.transmit': 16L}}

PUMP MODEL: ReadPumpModel:size[64]:data:'554'

ERROR:decocare.stick:size (14) is less than 64 and not 15, which may cause an error. CRITICAL:decocare.stick:download_packet:Stick transmit[TransmitPacket:None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:data:unknown] reader[ReadRadio:size:14] download_i[1] status[LinkStatus:0x03:status:size=14:size(14)] poll_size[14] poll_i[False] command[ReadRadio:size:14]:ERROR:bad dl raw! bytearray(b'\x01U\x00\x00\x02\x01\x00\x0e\x05\x04\x00\x90\x84\x03554\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'):ACK!? WARNING:decocare.stick:Stick transmit[TransmitPacket:None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:data:unknown] reader[ReadRadio:size:14] download_i[2] status[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)] poll_size[0] poll_i[False] command[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)]:download(attempts[2],expect[0],results[0]:data[0]):BAD AILING WARNING:decocare.stick:Stick transmit[TransmitPacket:None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:data:unknown] reader[ReadRadio:size:14] download_i[3] status[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)] poll_size[0] poll_i[False] command[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)]:download(attempts[3],expect[0],results[0]:data[0]):BAD AILING WARNING:decocare.stick:Stick transmit[TransmitPacket:None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:data:unknown] reader[ReadRadio:size:14] download_i[4] status[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)] poll_size[0] poll_i[False] command[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)]:download(attempts[4],expect[0],results[0]:data[0]):BAD AILING WARNING:decocare.stick:Stick transmit[TransmitPacket:None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:data:unknown] reader[ReadRadio:size:14] download_i[5] status[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)] poll_size[0] poll_i[False] command[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)]:download(attempts[5],expect[0],results[0]:data[0]):BAD AILING WARNING:decocare.stick:Stick transmit[TransmitPacket:None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:data:unknown] reader[ReadRadio:size:14] download_i[6] status[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)] poll_size[0] poll_i[False] command[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)]:download(attempts[6],expect[0],results[0]:data[0]):BAD AILING WARNING:decocare.stick:Stick transmit[TransmitPacket:None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:data:unknown] reader[ReadRadio:size:14] download_i[7] status[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)] poll_size[0] poll_i[False] command[LinkStatus:0x03:status:size=??LinkStatus:error:True:reason:[]:size(0)]:download(attempts[7],expect[0],results[0]:data[0]):BAD AILING CRITICAL:decocare.session:this seems like a problem response: None:{'retries': 0, 'effectTime': 0.5, 'code': 111, 'name': None, 'descr': 'Experimental command', 'bytesPerRecord': 64, 'maxRecords': 1, 'params': [40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'serial': '466990'}:size[64]:

decoded:

''

end stats

{'radio': {'errors.crc': 0,
           'errors.naks': 0,
           'errors.sequence': 0,
           'errors.timeouts': 0,
           'packets.received': 111L,
           'packets.transmit': 111L},
 'usb': {'errors.crc': 0,
         'errors.naks': 0,
         'errors.sequence': 2,
         'errors.timeouts': 0,
         'packets.received': 328704L,
         'packets.transmit': 2418671669L}}

At this point, I had to remove the stick and plug it back in again. Not good at all.

bewest avatar Mar 26 '15 04:03 bewest

@TC2013 and I were just chatting and he pointed me to this, has anyone looked writing the basal schedule again? What about the other profile settings like sensitivity and carb-ratios.

jasoncalabrese avatar Jan 09 '16 20:01 jasoncalabrese

2 issues:

  • [ ] - sending more than 64 bytes in parameters/commands (we know the radio iterates over frames sent with an ACK per frame, but not sure what/how to do this with the comlink2 serial protocol)
  • [ ] - figuring out the exact format/params to send (usually it's fairly similar to what is in the history payloads)

bewest avatar Jan 09 '16 21:01 bewest

ok, probably not the highest priority thing, but sounds like it might open up other possibilities

jasoncalabrese avatar Jan 09 '16 21:01 jasoncalabrese

There are commands to program all kinds of things, solving these issues would unlock those capabilities as well (bolus wizard settings, etc).

bewest avatar Jan 09 '16 21:01 bewest