Using bash and gatttool to get readings from Xiaomi Mijia LYWSD03MMC Temperature Humidity sensor

There is a new inexpensive Temperature and humidity sensor by xiaomi.
This time is no longer round ,

Xiaomi Mijia LYWSD03MMC Bluetooth 4.2 Temperature Humidity sensor

if you like me would like to get the temperature and humidity data from time to time to import in a graphing tool like grafana, there is a simple solution using classic bash tools and gatttool.
First you have to indentify the mac-address of your little sensor, for this , just make sure the sensor is in range of your linux device ans launch a
hcitool lescann

this command will spit out all the bluetooth devices in range
just find the line with the name of the device and copy the mac address A4:C1:38:8C:77:CA LYWSD03MMC

then you must start a little bash script like this :

#!/bin/bash
bt=$(timeout 15 gatttool -b A4:C1:38:8C:77:CA --char-write-req --handle='0x0038' --value="0100" --listen)
if [ -z "$bt" ]
then
echo "The reading failed"
else
echo "Got data"
echo $bt temphexa=$(echo $bt | awk -F ' ' '{print $12$11}'| tr [:lower:] [:upper:] )

humhexa=$(echo $bt | awk -F ' ' '{print $13}'| tr [:lower:] [:upper:])
temperature100=$(echo "ibase=16; $temphexa" | bc)
humidity=$(echo "ibase=16; $humhexa" | bc)
echo "scale=2;$temperature100/100"|bc
echo $humidity
fi

this is a skeleton that you can improve , but for now it pretty much work like that ,

first it use gatttol to connect to the sensor and listen for 15 sec
During these fifteen seconds , you can be pretty much sure to receive at least some data like this :

Characteristic value was written successfully Notification handle = 0x0036 value: 58 03 47 a0 0b Notification handle = 0x0036 value: 55 03 47 a0 0b

this tell me that during the 15 sec of connection i received the information that i need two times.
what i’m after are the value 58 03 for the temperature , and 47 for the humidity.

the temperature is little endian format its mean that the two group must be inverted before decoding the data. i invert the values with awk and decode them using bc.
bc doesn’t like when the hex values are not capitalised so tr is used to do that.

bc then give you the temperature multiplied by 100. relaunch bc to divice per 100,

For the humidity its simpler , you get the value in one step without inverting anyting

Then you can do what you need with theses two vars , insert then in some database etc ..

there is room from improvement: for example this script is not capable of decoding negative temperature.
i will post an improved version when i figure out how to do it

21 thoughts on “Using bash and gatttool to get readings from Xiaomi Mijia LYWSD03MMC Temperature Humidity sensor

  1. I am getting a syntax error at line 11 – unmatched ‘ character – any ideas on how to correct this?

  2. I removed the extra ‘ character and have made some other changes and it is working well for me , thankyou.

  3. Here is my try on this. I wonder if the extra bytes are battery voltage (mV). Works with negative temperature values, tested from -25C up to + 25C.

    -20.30 69 2.815 [12 f8 45 ff 0a]
    -1.10 82 2.815 [92 ff 52 ff 0a]
    -0.77 82 2.815 [b3 ff 52 ff 0a]
    -0.46 81 2.815 [d2 ff 51 ff 0a]
    -0.18 81 2.815 [ee ff 51 ff 0a]
    0.11 80 2.815 [0b 00 50 ff 0a]
    0.39 80 2.815 [27 00 50 ff 0a]
    0.66 80 2.815 [42 00 50 ff 0a]
    0.92 79 2.815 [5c 00 4f ff 0a]
    1.25 79 2.815 [7d 00 4f ff 0a]
    21.34 51 2.815 [56 08 33 ff 0a]

    8< – – – 8< – – – 8< – – – 8< – – – 8 “-0.05” and “.05” -> “0.05”
    temperature=$(echo “scale=2; $temperature100 / 100” | bc | sed ‘s:^\(-\?\)\.\(.*\)$:\10.\2:’)

    battery=$(echo “scale=3; $battery1000 / 1000” | bc)

    echo $temperature $humidity $battery “[$hexa]”
    fi

  4. (second try, sorry. Half of the message disappeared(?) after “Submit”)

    Here is my try on this. I wonder if the extra bytes are battery voltage (mV). Works with negative temperature values, tested from -25C up to + 25C.

    -20.30 69 2.815 [12 f8 45 ff 0a]
    -1.10 82 2.815 [92 ff 52 ff 0a]
    -0.77 82 2.815 [b3 ff 52 ff 0a]
    -0.46 81 2.815 [d2 ff 51 ff 0a]
    -0.18 81 2.815 [ee ff 51 ff 0a]
    0.11 80 2.815 [0b 00 50 ff 0a]
    0.39 80 2.815 [27 00 50 ff 0a]
    0.66 80 2.815 [42 00 50 ff 0a]
    0.92 79 2.815 [5c 00 4f ff 0a]
    1.25 79 2.815 [7d 00 4f ff 0a]
    21.34 51 2.815 [56 08 33 ff 0a]

    #!/bin/bash

    bt=$(timeout 25 gatttool -b A4:C1:38:53:C7:B2 –char-write-req –handle=0x0038 –value=0100 –listen | grep “Notification handle” -m 1)

    if [ -z “$bt” ]
    then
    echo “The reading failed”
    else
    #echo “Got data”
    hexa=$(echo $bt | awk ‘{print $6 ” ” $7 ” ” $8 ” ” $9 ” ” $10}’)

    temphexa=$(echo $bt | awk ‘{print $7$6}’ | tr ‘[:lower:]’ ‘[:upper:]’)
    humhexa=$(echo $bt | awk ‘{print $8}’ | tr ‘[:lower:]’ ‘[:upper:]’)
    batthexa=$(echo $bt | awk ‘{print $10$9}’ | tr ‘[:lower:]’ ‘[:upper:]’)

    temperature100=$(echo “ibase=16; $temphexa” | bc)
    humidity=$(echo “ibase=16; $humhexa” | bc)
    battery1000=$(echo “ibase=16; $batthexa” | bc)

    if [ $temperature100 -gt 32767 ]; then
    temperature100=$(($temperature100 – 65536))
    fi

    # Add missing leading zero if needed (sed): “-.05” -> “-0.05” and “.05” -> “0.05”
    temperature=$(echo “scale=2; $temperature100 / 100” | bc | sed ‘s:^\(-\?\)\.\(.*\)$:\10.\2:’)

    battery=$(echo “scale=3; $battery1000 / 1000” | bc)

    echo $temperature $humidity $battery “[$hexa]”
    fi

  5. Battery level it is reported in hex on UUID 0x2a19 (got it from nRFConnect on Android)

    gatttool -b $mac –char-read –uuid 0x2a19 –listen
    handle: 0x001b value: 63

    #convert it to dec. In my case is 99%
    gatttool -b $mac –char-read –uuid 0x2a19 | awk ‘{print “ibase=16;”, $4}’ | bc
    99

  6. # Device Name
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2A00′ | tr -d [:space:] | xxd -r -p -s19
    # LYWSD03MMC

    # Battery Level
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2a19′ | tr [:lower:] [:upper:] | tr -d [:space:] | tail -c -2 | echo “ibase=16; $(cat -)” | bc
    # 99

    # Model Number String
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2A24′ | tr -d [:space:] | xxd -r -p -s19
    # LYWSD03MMC

    # Serial Number String
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2A25′ | tr -d [:space:] | xxd -r -p -s19
    # XX.X-XXX-XX-XXXXX-

    # Firmware Revision String
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2A26′ | tr -d [:space:] | xxd -r -p -s19
    # 1.0.0_0109

    # Hardware Revision String
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2A27′ | tr -d [:space:] | xxd -r -p -s19
    # B1.4

    # Software Revision String
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2A28′ | tr -d [:space:] | xxd -r -p -s19
    # 0109

    # Manufacturer Name String
    gatttool -b XX:XX:XX:XX:XX:XX –char-read –uuid=’0x2A29′ | tr -d [:space:] | xxd -r -p -s19
    # miaomiaoce.com

  7. I understand how you get this: A4:C1:38:8C:77:CA LYWSD03MMC

    But you didn’t explain how you got this: –handle=’0x0038′

    Where does the handle come from? Trial and error?

  8. I’m using slightly modiefied script for reading multiple sensors on raspberry zeroW:

    #!/bin/bash
    Sensor1=A4:C1:38:88:D3:2E
    Sensor2=A4:C1:38:52:5A:26
    Sensor3=A4:C1:38:36:3E:D1
    Sensor1name=Bedroom
    Sensor2name=Anopinperse
    Sensor3name=Fridge

    readsensor () {
    bt=$(sleep 1;timeout 15 gatttool -b $Sensor –char-write-req –handle=’0x0038′ ->
    }

    result () {
    while [[ “$bt” =~ .*error*. ]]; do
    readsensor
    done
    humhexa=$(echo $bt | awk -F ‘ ‘ ‘{print $13}’| tr [:lower:] [:upper:])
    humidity=$(echo “ibase=16; $humhexa” | bc)
    while [[ “$humidity” -gt 101 || -z “$humidity” ]];do
    readsensor
    done
    }

    printsensor () {
    temphexa=$(echo $bt | awk -F ‘ ‘ ‘{print $12$11}’| tr [:lower:] [:upper:] )
    humhexa=$(echo $bt | awk -F ‘ ‘ ‘{print $13}’| tr [:lower:] [:upper:])
    temperature100=$(echo “ibase=16; $temphexa” | bc)
    humidity=$(echo “ibase=16; $humhexa” | bc)
    echo -n “$Sensorname Temperature: ”
    echo “scale=2;$temperature100/100″|bc
    echo -n “$Sensorname Humidity: ”
    echo $humidity
    echo “”
    }

    Sensor=$(echo $Sensor1)
    Sensorname=$(echo $Sensor1name)
    readsensor
    result
    printsensor

    Sensor=$(echo $Sensor2)
    Sensorname=$(echo $Sensor2name)
    readsensor
    result
    printsensor

    Sensor=$(echo $Sensor3)
    Sensorname=$(echo $Sensor3name)
    readsensor
    result
    printsensor

  9. The original script only works as intended at positive temperatures. LYWSD03MMC can handle negative values down to -9.9 °C. The easy solution is to only use the hex values and let a C program do the rest.

    By the way, even after 9 months in a humid environment, the sensor boards show no signs of deterioration. Only the battery loses its capacity very quickly at temperatures below 10 degrees.

  10. Her’s my script:

    # struct sensor_data {
    # char mac_addr[18];
    # char temp[5];
    # char hum[3];
    # char batt[5];
    # } xdata;

    #define array
    SENSORS=(tinker#a4:C1:38:B4:59:4C
    nixda#aa:bb:38:b4:e9:4f)

    RSLT=”tmpfile”
    touch $RSLT

    # poll sensors
    for ITEM in “${SENSORS[@]}” ; do
    NAME=”${ITEM%%#*}”
    MAC_ADDRESS=”${ITEM##*#}”

    # collect sensor data
    DATA=$(timeout 15 gatttool -t public -b $MAC_ADDRESS –char-write-req –handle=’0x0038′ –value=”0100″ –listen | grep “Notification handle” -m 1)

    DATUM=`date +%y-%m-%d\ %H:%M:%S` # get data collection time

    if [ -z “$DATA” ]
    then # reading failed

    # Clear sensor structure data
    TEMP=’0000′ # 0 is a valid result!
    HUM=’00’ # 0 is not a valid result
    BATT=’0000′ # 0 is not a valid result

    # log read error
    printf “$MAC_ADDRESS\t$DATUM\tBT fail [2]\n” >> xfail.log

    else # got data

    TEMP=$(echo $DATA | awk -F ‘ ‘ ‘{print $7$6}’)
    HUM=$(echo $DATA | awk -F ‘ ‘ ‘{print $8}’)
    BATT=$(echo $DATA | awk -F ‘ ‘ ‘{print $10$9}’)

    fi

    printf “$MAC_ADDRESS\x00” >> $RSLT
    printf “$TEMP\x00” >> $RSLT
    printf “$HUM\x00” >> $RSLT
    printf “$BATT\x00” >> $RSLT
    done
    mv $RSLT rsltfile

    The rsltfile is all ASCII. Simply read it and print data in c, phyton, whatever.

  11. Hello. I’m trying to make It work on a Rapsberry pi 4 Model B running openhabian.5.10.63.

    I detect the MAC , but gatttool always returns me:

    connect to A4:C1:38:BA:D5:35: Connection refused (111)

    Any idea? Thank you!

  12. i was just searcing for these things thanks for posting them Xiaomi is a good product hope to see new models in the coming time thanks for updating

Leave a Comment

Your email address will not be published. Required fields are marked *