decoding-carelink
decoding-carelink copied to clipboard
Enhancement request: Would love to be able to program basal schedules.
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.
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)"
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);
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.
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
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.
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.
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.
@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.
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)
ok, probably not the highest priority thing, but sounds like it might open up other possibilities
There are commands to program all kinds of things, solving these issues would unlock those capabilities as well (bolus wizard settings, etc).