FreeStyle Libre

Reverse engineered by Pascal Fribi, editing and expansion by Diego Elio Pettenò.

Important device notes

USB IDs

Device Vendor ID Product ID
FreeStyle Libre 1a61 3650

Protocol

This device uses the shared HID protocol used by other meters in the FreeStyle family. Text commands are sent by the original software as message type 0x21, with responses as 0x60, but the device appears to accept the commands as type 0x60 as well (compatible with other FreeStyle devices).

Strings Handling

The official software has known UTF-8 handling bugs, but the device itself appears to display strings correctly if encoded in UTF-8.

Commands

The Libre supports a distinct set of commands from those described in the shared protocol. In particular, the following commands are not supported:

  • $serlnum? — replaced by $sn?.

A number of new text commands are added instead.

$dbrnum?

dbrnum-response = "DBRECORDS = " 1*DIGIT CRLF

The response includes the number of records in the database (history results.)

This number is permanent across factory resets.

$history?

The $history? command returns all the automatic measurements taken by the sensors. It does not include the immediate measurements on user request, nor strip measurements (blood glucose and β-ketone).

The output follows the Multiple record command output format as described in the shared protocol documentation.

Record fields

  1. record-id = 1*5DIGIT
  2. unknown = "12"
  3. month = 1*2DIGIT
  4. day = 1*2DIGIT
  5. year = 1*2DIGIT
  6. hour = 1*2DIGIT
  7. minute = 1*2DIGIT
  8. second = 1*2DIGIT
  9. unknown = "1"
  10. unknown = "0"
  11. unknown = "0"
  12. unknown = "0"
  13. unknown = "0" / "1"

    This value appears to only be 1 for the first reading of the sensor (with sensor runtime reporting 15 minutes).

  14. value = 1*DIGIT

  15. sensor-runtime-minutes = 1*DIGIT
  16. error-bitfield = 1*5DIGIT

    This field needs to be interpreted as a bitfield (in decimal representation). Flag 0x8000 indicates an error (invalid reading). If the error flag is set, the remaining bits refer to more error details that are not clear.

$arresult?

The $arresult? returns manual measurements taken by the user, either by scanning the sensor or using a testing strip (for either blood sugar or β-ketone measurement)

The output follows the Multiple record command output format as described in the shared protocol documentation.

Multiple record types have been identified; the second value in the record identifies the type; type 2 is a manual reading, record type 5 identifies a time change event.

Reading record fields

  1. record-id = 1*5DIGIT
  2. record-type = "2"
  3. month = 1*2DIGIT
  4. day = 1*2DIGIT
  5. year = 1*2DIGIT
  6. hour = 1*2DIGIT
  7. minute = 1*2DIGIT
  8. second = 1*2DIGIT
  9. unknown = "1"
  10. Identifies the type of reading in the record

    reading-type = blood-glucose / blood-ketone / sensor-glucose
    blood-glucose = "0"
    blood-ketone = "1"
    sensor-glucose = "2"
    
  11. unknown = "0"

  12. unknown = "0" / "1"

    This appears to be 1 when either an error is present, or when the read value is LO.

  13. value = 1*DIGIT

    When reading-type is either blood-glucose or sensor-glucose, this represent the blood sugar reading in mg/dL.

    When reading-type is blood-ketone, this represent the β-ketone reading in mmol/l after apply value/18. It seems that the value is reported in mg/dL and the conversion is using the wrong molar mass system for conversion. But based on actual measurements the results are correct this way.

  14. unknown = "0" / "1"

    This appears to be 0 for values read from a blood strip, and 1 for values read from sensor. Appears redundant with the reading-type field.

  15. Corresponds to the arrow indicator in the UI.

    direction-indicator = none / down-fast / down / steady / up / up-fast none = "0" down-fast = "1" down = "2" steady = "3" up = "4" up-fast = "5"

  16. sports-flag = "0" / "1"

  17. medication-flag = "0" / "1"
  18. rapid-acting-insulin-flag = "0" / "1"

    See field 44 for value†.

  19. long-acting-insulin-flag = "0" / "1"

    See field 24 for value.

  20. custom-comments-bitfield = 1*DIGIT

    Custom comments 1-6 flags. To be interpreted as a bitfield, LSB first.

  21. unknown = "0"

  22. unknown = "0"
  23. unknown = "0" / "1" / "2" / "3" / "4" / "5"
  24. Value of long Acting insulin in 0.5 IE. If you want proper IE, divide by 2
  25. unknown = "0" / "1"
  26. food-flag = "0" / "1"

    See field 27 for value.

  27. food-carbs-grams = 1*DIGIT

  28. unknown = "0"
  29. error-bitfield = 1*5DIGIT

    This field needs to be interpreted as a bitfield (in decimal representation). Flag 0x8000 indicates an error (invalid reading). If the error flag is set, the remaining bits refer to more error details that are not clear.

    In the Event View on the device, these correspond to Err3 errors.

  30. custom-comment-1 = DQUOTE *VCHAR DQUOTE

  31. custom-comment-2 = DQUOTE *VCHAR DQUOTE
  32. custom-comment-3 = DQUOTE *VCHAR DQUOTE
  33. custom-comment-4 = DQUOTE *VCHAR DQUOTE
  34. custom-comment-5 = DQUOTE *VCHAR DQUOTE
  35. custom-comment-6 = DQUOTE *VCHAR DQUOTE
  36. unknown = "7"
  37. month = 1*2DIGIT * †
  38. day = 1*2DIGIT * †
  39. year = 1*2DIGIT * †
  40. hour = 1*2DIGIT * †
  41. minute = 1*2DIGIT * †
  42. second = 1*2DIGIT * †
  43. unknown = "1" * †
  44. Value of rapid acting insulin in 0.5 IE. If you want proper IE, divide by 2.

* These values are identical to values 3-9, regardless of what time the notes were added to the record. Which seems to make them entirely redundant.

† The number of columns in a record can be different. If a rapid acting insulin value has been set, then it is > 44, otherwise the record has only entries up to comment 6.

‡ Be aware that custom comments are shown in every record. The bitfield in field 19. shows which comments are actually set.

Time change record fields

  1. record-id = 1*5DIGIT
  2. record-type = "5"
  3. new-month = 1*2DIGIT
  4. new-day = 1*2DIGIT
  5. new-year = 1*2DIGIT
  6. new-hour = 1*2DIGIT
  7. new-minute = 1*2DIGIT
  8. new-second = 1*2DIGIT
  9. unknown
  10. old-month = 1*2DIGIT
  11. old-day = 1*2DIGIT
  12. old-year = 1*2DIGIT
  13. old-hour = 1*2DIGIT
  14. old-minute = 1*2DIGIT
  15. old-second = 1*2DIGIT
  16. unknown
  17. unknown
  18. unknown
  19. unknown
  20. unknown

$swreset

Restart the Libre device, not erasing any information.

swreset-cmd = "$swreset"
swreset-response = CRLF

$resetpatient

This command completely resets the FreeStyle Libre reader, bringing it effectively to factory reset.

CAUTION! This erases all information on the device, including currently-enabled sensors.

While not accessible via the protocol, a reset does not erases the "Event Log" as available on the device.

resetpatient-cmd = "$resetpatient"
resetpatient-response = *("Erasing Sector @ 0x" 6 HEXDIGIT CRLF)

Sound Settings

It's possible to query and set the notification and touch tone settings of FreeStyle Libre devices through two commands.

The commands have first been identified on FreeStyle Insulinx but are not tested on it.

notifications = notifications-enabled / notifications-disabled
notifications-enabled = "1"
notifications-disabled = "0"

vibrate = vibrate-enabled / vibrate-disabled
vibrate-enabled = "1"
vibrate-disabled = "0"

touch-tone = touch-tone-enabled / touch-tone-disabled
touch-tone-enabled = "1"
touch-tone-disabled = "0"

volume = volume-high / volume-low
volume-high = "1"
volume-low = "0"

ntsound-cmd = "$ntsound" ("?" / "," notifications "," vibrate)
ntsound-query-response = notifications "," vibrate CRLF

btsound-cmd = "$btsound" ("?" / "," touch-tone "," volume)
btsound-query-response = touch-tone "," volume

Reminders ($getrmndr)

It's possible to read the reminders status for the reader device.

The commands have first been identified on FreeStyle Insulinx but are not tested on it.

get-reminder-command = "$getrmndr,0"
get-reminder-response = 12 (reminder-entry CRLF)
reminder-entry = reminder-repeat ","
                 reminder-action ","
                 ( reminder-enabled / reminder-disabled ) ","
                 reminder-hour "," reminder-minute

reminder-repeat = reminder-off / reminder-daily / reminder-timer
reminer-off = "0"
reminder-once = "1"
reminder-daily = "2"
reminder-timer = "3"

reminder-action = reminder-glucose / reminder-insulin / reminder-alarm
reminder-glucose = "10"
reminder-insulin = "11"
reminder-alarm = "12"

reminder-enabled = "1"
reminder-disabled = "0"

reminder-hour = 1*2 DIGIT
reminder-minute = 1*2 DIGIT

Note that while other $getrmndr arguments than 0 will be accepted by the device, they return no data at all.

When the repeat is "off" (0), all other fields are also 0.

When the repeat is timer, the hour/minutes are the repeat time from the beginning of the reminder.

Native Glucose Units

The $uom? command reports the native unit of measure of the device.

uom-cmd = "$uom?"
uom-msg = uom-mmoll / uom-mgdl CRLF

uom-mmol = "0"
uom-mgdl = "1"

Other Commands

The following commands are sent by various software used by the Libre, but their output is not obvious.

$brandname?

Appears to report the brand name of the device, possibly differing among marketing regions.

brandname-cmd = "$brandname?"
brandname-msg = "FreeStyle Libre" CRLF

$marketlev?

Unknown respoonse.

marketlev-cmd = "$marketlev?"
marketlev-msg = "1,0,0,0" CRLF

Language settings

The command $lang? reports the currently configured language.

The command $lang, allows changing the configured language (if available in the firmware of the device.) Note that this allows to configure a language that is not visible to set in the interface, though in that case the reader will display the selected language as Español.

The command $langset? reports the list of languages available to set through the UI itself.

And similarly the $langset, command allows to change the list of available languages.

get-lang-cmd = "$lang?"
get-lang-msg = language-code CRLF

set-lang-cmd = "$lang," language-code
set-lang-msg = CRLF

get-configured-langs-cmd = "$langset?"
get-configured-langs-msg = language-code *("," language-code)

set-configured-langs-cmd = "$langset" 1*("," language-code)
set-configured-langs-msg = CRLF

language-code = pt-br / es-us / en-us / de / fr / it / en-gb / fr-ca /
                zh / jp / nl / sv / es-es / no / dk / fi / el / pl /
                pt-pt / ru / tr / ar / he

pt-br = "0"
es-us = "1"
en-us = "2"
de = "3"
fr = "4"
it = "5"
en-gb = "6"
fr-ca = "7"
zh = "8"
jp = "9"
nl = "10"
sv = "11"
es-es = "12"
no = "13"
dk = "14"
fi = "15"
el = "16"
pl = "17"
pt-pt = "18"
ru = "19"
tr = "20"
ar = "21"
he = "22"

$iobstatus?

Originally identified on FreeStyle Insulinx.

iobstatus-cmd = "$iobstatus?"
iobstatus-msg = "255,255" CRLF

$foodunits?

Originally identified on FreeStyle Insulinx.

foodunits-cmd = "$foodunits?"
foodunits-msg = "0" CRLF

$svgsdef?

Originally identified on FreeStyle Insulinx.

svgsdef-cmd = "$svgsdef?"
svgsdef-msg = "255" CRLF

$corsetup?

Originally identified on FreeStyle Insulinx.

corsetup-cmd = "$corsetup?"
corsetup-msg = "255" CRLF

$insdose?

Originally identified on FreeStyle Insulinx.

insdose-cmd = "$insdose?"
insdose-msg = "1" CRLF

$inscalsetup?

Originally identified on FreeStyle Insulinx.

inscalsetup-cmd = "$inscalsetup?"
inscalsetup-msg = "0,0" CRLF

$carbratio?

Originally identified on FreeStyle Insulinx.

carbratio-cmd = "$carbratio?"
carbratio-msg = "0,0" CRLF
                "1,0" CRLF
                "2,0" CRLF
                "3,0" CRLF
                "4,0" CRLF
                "0" CRLF

$svgsratio?

Originally identified on FreeStyle Insulinx.

svgsratio-cmd = "$svgsratio?"
svgsratio-msg = "0,0" CRLF
                "1,0" CRLF
                "2,0" CRLF
                "3,0" CRLF
                "4,0" CRLF
                "0" CRLF

$cttype?

Originally identified on FreeStyle Insulinx.

cttype-cmd = "$cttype?"
cttype-msg = "255" CRLF

$bgdrop?

Originally identified on FreeStyle Insulinx.

bgdrop-cmd = "$bgdrop?"
bgdrop-msg = "0,255" CRLF
             "1,0" CRLF
             "2,0" CRLF
             "3,0" CRLF
             "4,0" CRLF
             "0" CRLF

$bgtrgt?

Originally identified on FreeStyle Insulinx.

bgtrgt-cmd = "$bgtrgt?"
bgtrgt-msg = "0,0" CRLF
             "1,0" CRLF
             "2,0" CRLF
             "3,0" CRLF
             "4,0" CRLF
             "0" CRLF

$bgtgrng?

Originally identified on FreeStyle Insulinx.

bgtgrng-cmd = "$bgtgrng?"
bgtgrng-msg = "0,0,0" CRLF
              "1,0,0" CRLF
              "2,0,0" CRLF
              "3,0,0" CRLF
              "4,0,0" CRLF
              "0" CRLF

$tagsenbl?

Originally identified on FreeStyle Insulinx.

tagsenbl-cmd = "$tagsenbl?"
tagsenbl-msg = CRLF

$patch?

Return information about the sensors (patches) that the device initialized.

The returned message is in a similar text format to "multi records" responses, but with a different structure.

patch-response = empty-log / patch-response-detailed
empty-log = "Log Empty" CRLF

csv-line = *( value "," ) value
value = 1*DIGIT
checksum = 8HEXDIGIT

patch-response-detailed = csv-line CRLF
                          "PID: " csv-line CRLF
                          "SW Versions: " csv-line CRLF
                          csv-line CRLF
                          "Event Log: " csv-line CRLF
                          csv-line CRLF
                          "AFE Cal Data: " csv-line CRLF
                          "Unused: " csv-line
                          "1," checksum

The <checksum> field is calculated in the same way as multiple records commands: by summing up the ASCII value of each of the bytes forming the response set, up to and including the final newline before 1,.