[PYTHON] Notify LINE of body temperature from BLE thermometer with Raspberry Pi # 2

Introduction

This is a continuation of # 1 that notifies LINE of body temperature from the BLE thermometer with Raspberry Pi. This time we will implement it in python. GATT with python. The BLE library uses bluepy.

installation of bluepy

The BLE library uses bluepy. Install bluepy.

$ sudo apt-get update
$ sudo apt-get -y upgrade
$ sudo apt-get -y install python3-pip libglib2.0-dev
$ sudo pip3 install bluepy
>Successfully installed bluepy-1.3.0

$ reboot

Try various things with bluepy

Talk to BLE device with bluepy.

Scan for advertisement packets

First, scan the advertisement packet.

from bluepy import btle

scanner = btle.Scanner(0) 
devices = scanner.scan(3.0) 
for device in devices:
  print(f'BLE Address:{device.addr}')
  for (adTypeCode, description, valueText) in device.getScanData():
    print(f'- {description}:{valueText}')

The code is simple, but it has traps and requires ** sudo privileges ** to run scans with bluepy. If you do not have enough permissions, you will get an error with scanner.scan ().

Put the thermometer in ** pairing mode ** and then run py.

$ sudo python3 bluepyadv.py
BLE Address:18:93:d7:76:c9:b8
- Flags:05
- Incomplete 16b Services:00001809-0000-1000-8000-00805f9b34fb
- 0x12:5000a000
- Tx Power:00
- Complete Local Name:A&D_UT201BLE_76C9B8
BLE Address:6f:1a:a5:25:58:55
- Flags:06
- Manufacturer:4c001005571cf70bed

BLE Address: 18: 93: d7: 76: c9: b8 is the information of the thermometer device. It has been scanned properly.

Connect and get a list of services

Get a list of services by connecting to a thermometer. The BLE address is like a MAC address, so it depends on the device. Please specify the BLE address of your device. The BLE address of my thermometer is 18: 93: D7: 76: C9: B8.

from bluepy import btle

BLE_ADDRESS="18:93:D7:76:C9:B8"

peripheral = btle.Peripheral()
peripheral.connect(BLE_ADDRESS)

for service in peripheral.getServices():
    print(f'Service UUID:{service.uuid}')
    for characteristic in service.getCharacteristics():
        print(f'- Characteristic UUID:{characteristic.uuid} , Handle:{hex(characteristic.getHandle())} , Property:{characteristic.propertiesToString()}')

Measure with a thermometer and execute py when it is in ** transmission mode **. Since it connects, it is in transmission mode, not pairing mode. You don't have to have sudo privileges as before.

$ python3 bluepyconnect.py
Service UUID:00001800-0000-1000-8000-00805f9b34fb
- Characteristic UUID:00002a00-0000-1000-8000-00805f9b34fb , Handle:0x3 , Property:READ 
- Characteristic UUID:00002a01-0000-1000-8000-00805f9b34fb , Handle:0x5 , Property:READ 
- Characteristic UUID:00002a02-0000-1000-8000-00805f9b34fb , Handle:0x7 , Property:READ WRITE 
- Characteristic UUID:00002a03-0000-1000-8000-00805f9b34fb , Handle:0x9 , Property:WRITE 
- Characteristic UUID:00002a04-0000-1000-8000-00805f9b34fb , Handle:0xb , Property:READ 
Service UUID:00001801-0000-1000-8000-00805f9b34fb
- Characteristic UUID:00002a05-0000-1000-8000-00805f9b34fb , Handle:0xe , Property:INDICATE 
Service UUID:00001809-0000-1000-8000-00805f9b34fb
- Characteristic UUID:00002a1c-0000-1000-8000-00805f9b34fb , Handle:0x12 , Property:INDICATE 
- Characteristic UUID:00002a1d-0000-1000-8000-00805f9b34fb , Handle:0x15 , Property:READ 
- Characteristic UUID:00002a08-0000-1000-8000-00805f9b34fb , Handle:0x17 , Property:READ WRITE 
Service UUID:0000180a-0000-1000-8000-00805f9b34fb
- Characteristic UUID:00002a29-0000-1000-8000-00805f9b34fb , Handle:0x1a , Property:READ 
- Characteristic UUID:00002a24-0000-1000-8000-00805f9b34fb , Handle:0x1c , Property:READ 
- Characteristic UUID:00002a25-0000-1000-8000-00805f9b34fb , Handle:0x1e , Property:READ 
- Characteristic UUID:00002a27-0000-1000-8000-00805f9b34fb , Handle:0x20 , Property:READ 
- Characteristic UUID:00002a26-0000-1000-8000-00805f9b34fb , Handle:0x22 , Property:READ 
- Characteristic UUID:00002a28-0000-1000-8000-00805f9b34fb , Handle:0x24 , Property:READ 
- Characteristic UUID:00002a23-0000-1000-8000-00805f9b34fb , Handle:0x26 , Property:READ 
- Characteristic UUID:00002a2a-0000-1000-8000-00805f9b34fb , Handle:0x28 , Property:READ 
Service UUID:0000180f-0000-1000-8000-00805f9b34fb
- Characteristic UUID:00002a19-0000-1000-8000-00805f9b34fb , Handle:0x2b , Property:READ 
Service UUID:233bf000-5a34-1b6d-975c-000d5690abe4
- Characteristic UUID:233bf001-5a34-1b6d-975c-000d5690abe4 , Handle:0x2e , Property:READ WRITE 

Get data from the thermometer

Try to receive the measured data.

import sys
from bluepy import btle

BLE_ADDRESS="18:93:D7:76:C9:B8"
SERVICE_UUID="00001809-0000-1000-8000-00805f9b34fb"

class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)

    def handleNotification(self, cHandle, data):
        print("Handle = " + hex(cHandle))
        print("- Flags = " + hex(data[0]))
        print("- C1:Temperature Measurement Value(Celsius) = " + hex(data[1])+":"+hex(data[2])+":"+hex(data[3])+":"+hex(data[4]))
        print("- C3:Time Stamp = " + hex(data[5])+":"+hex(data[6])+":"+hex(data[7])+":"+hex(data[8])+":"+hex(data[9])+":"+hex(data[10])+":"+hex(data[11]))

if __name__ == '__main__':
    print("Start")
    print("Connecting Wait...")
    try:
        peripheral = btle.Peripheral()
        peripheral.connect(BLE_ADDRESS)
    except:
        print("connect Error!")
        sys.exit(0)

    print("Connected!")
    peripheral.withDelegate(MyDelegate())

    # Enable Indicate
    peripheral.writeCharacteristic(0x13, b'\x02\x00', True)

    #Wait for notification
    print("Indicate Wait...")
    try:
        TIMEOUT = 3.0
        while True:
            if peripheral.waitForNotifications(TIMEOUT):
                # handleNotification()Was called
                continue

            # handleNotification()Was not called even after waiting for TIME OUT seconds
            print("wait...")
    except:
        #Come here when disconnected
        print("except!")

    print("end")

The point is that the event handler is registered with peripheral.withDelegate (MyDelegate ()) and peripheral.writeCharacteristic (0x13, b'\ x02 \ x00', True) is done.

** Indicate starts when you set 0200 to the characteristic of handle 0x13 ** It is as in Previous explanation. The handleNotification () event is fired when data is received. The received data is jammed in data.

Caution The handle value of 0x13 may differ depending on the device (unconfirmed), so check the handle value with gatttool and specify the value of your device.

pi@raspberrypi:~/work/git/BLEThermometer/src $ python3 bluepygatt.py
Start
Connecting Wait...
Connected!
Indicate Wait...
Handle = 0x12
- Flags = 0x6
- C1:Temperature Measurement Value(Celsius) = 0x73:0x1:0x0:0xff
- C3:Time Stamp = 0xe4:0x7:0x5:0x4:0xa:0x7:0x2f
wait...
wait...
except!
end

In this execution log

--Body temperature = 0x73: 0x1: 0x0: 0xff --Timestamp = xe4: 0x7: 0x5: 0x4: 0xa: 0x7: 0x2f

You can see that it was removed.

Parsing received data

Perspective of measurements

I found out earlier that "body temperature = 0x73: 0x1: 0x0: 0xff", but this 4-byte data is a binary in ** IEEE 11073 32bit float ** format. I couldn't find an easy way to convert an IEEE 11073 32bit float binary to a float type variable in python. I couldn't help it, so I made it myself.

import numpy as np

def to_float_from_11073_32bit_float(data):
    tmp = int.from_bytes(data,'little')
    uint32val = np.array([tmp],dtype=np.uint32)

    #Find the mantissa(0-24bit)
    tmp = bin(uint32val[0] & 0xffffff)
    mantissa = int(tmp,0)

    #Find the index part(25-32bit)
    tmp = int(data[3])
    tmp2 = np.array([tmp],dtype=np.byte)
    exponent = int(tmp2[0])

    #Calculate real numbers
    ret = round(mantissa * pow(10,exponent),1)
    return ret

# 37.1
temp = to_float_from_11073_32bit_float(b'\x73\x01\x00\xff')
print("temp = " + str(temp) + " C")

I will do it.

$ python3 to_float_from_11073_32bit_float.py 
temp = 37.1 C

0x73: 0x1: 0x0: 0xff is now ** 37.1 **. Very good.

Timestamp perspective

The time stamp is 7 bytes. Temperature Measurement C3 format.

def to_date_time(data):

    tmp = data[0:2]
    yyyy = int.from_bytes(tmp,'little')

    mm = int(data[2])
    dd = int(data[3])
    hh = int(data[4])
    min = int(data[5])
    ss = int(data[6])

    strdate_time=str(yyyy)+"/"+str(mm)+"/"+str(dd)+" "+str(hh)+":"+str(min)+":"+str(ss)
    return strdate_time

# 2020/5/4 10:07:47
date_time = to_date_time(b'\xe4\x07\x05\x04\x0a\x07\x2f')
print("date_time = " + date_time)

I will do it.

$ python3 to_date_time.py 
date_time = 2020/5/4 10:7:47

So far, you can do the same thing with bulepy that you did with gatttool last time.

Notify by LINE

By the way, I had to notify the measurement result on LINE. LINE notifications are easy with LINE Notify.

Implement LINE notification by referring to the following article.

-Create a mechanism to record attendance and departure with Raspberry Pi, MAMORIO and LINE

The Token written in this source is invalid, so issue it and use a valid one.

import requests

#LINE notification
def send_notify(access_token,comment):
        if( len(access_token) <= 0 ):
                return

        url = "https://notify-api.line.me/api/notify"
        headers = {'Authorization': 'Bearer ' + access_token}
        message = comment
        payload = {'message': message}
        print(message)
        requests.post(url, headers=headers, params=payload,)

send_notify("token xxx","comment")

I will do it.

$ python3 sendline.py 
comment

Kita.

IMG_6943.png

Production

From here is the production. It's easy because all you have to do is do what you've looked up so far in order.

memo_04.png

The python source code is a bit long.

#You need sudo privileges to run this py.
#Scanner without permission.scan()Will result in an error.
from bluepy import btle
import sys
import time
import to_float_from_11073_32bit_float as tofl
import to_date_time as todt
import sendline as line

# define
SERVICE_UUID="00001809-0000-1000-8000-00805f9b34fb"

# global
BLE_ADDRESS="xx:xx:xx:xx:xx:xx"
TOKEN = "this is token"

def scan():
    try:
        scanner = btle.Scanner(0)
        devices = scanner.scan(3.0)

        for device in devices:
            print(f'SCAN BLE_ADDR:{device.addr}')

            if(device.addr.lower()==BLE_ADDRESS.lower()):
                print("Find!")
                return True
    except:
        print("scan Error!")
        return False
    print("---")
    return False

class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)

    def handleNotification(self, cHandle, data):
        print("Indicate Handle = " + hex(cHandle))
        print("Flags = " + hex(data[0]))
        print("C1:Temperature Measurement Value(Celsius) = " + hex(data[1])+":"+hex(data[2])+":"+hex(data[3])+":"+hex(data[4]))
        print("C3:Time Stamp = " + hex(data[5])+":"+hex(data[6])+":"+hex(data[7])+":"+hex(data[8])+":"+hex(data[9])+":"+hex(data[10])+":"+hex(data[11]))

        temp = tofl.to_float_from_11073_32bit_float(data[1:5])
        print("temp = " + str(temp))
        timestamp = todt.to_date_time(data[5:12])
        print("timestamp = " + timestamp)
        line.send_notify(TOKEN,str(temp)+" C "+timestamp)

def main():
    #
    # Scan
    #
    print("<Scan Start>")
    while True:
        scanresult = scan()
        if( scanresult==True):
            break
        time.sleep(3)
    print("Scan End")


    #
    # Connect
    #
    print("Connect Start")
    try:
        peripheral = btle.Peripheral()
        peripheral.connect(BLE_ADDRESS)
    except:
        print("connect Error!")
        sys.exit(0)

    print("Connected!")
    service = peripheral.getServiceByUUID(SERVICE_UUID)
    peripheral.withDelegate(MyDelegate())

    # Enable Indicate
    peripheral.writeCharacteristic(0x0013, b'\x02\x00', True)

    #Wait for notification
    print("Indicate Wait...")
    try:
        TIMEOUT = 3.0
        while True:
            if peripheral.waitForNotifications(TIMEOUT):
                # handleNotification()Was called
                continue

            # handleNotification()Was not called even after waiting for TIME OUT seconds
            print("wait...")
    except:
        print("except!")

    print("<end>")

if __name__ == '__main__':
    print(sys.argv[0])
    #global TOKEN
    TOKEN = sys.argv[1]
    print("token = " + TOKEN)

    #gloval BLE_ADDRESS
    BLE_ADDRESS = sys.argv[2]
    print("BLE device = " + BLE_ADDRESS)

    while True:
        main()
        time.sleep(3)

When you run py, the scan will start and you will measure your body temperature. When the measurement is completed, it will be automatically received and the body temperature and time stamp (measurement date and time) will be notified to LINE.

$ sudo python3 bluepythermo.py [token] 18:93:D7:76:C9:B8
bluepythermo.py
token = [token]
BLE device = 18:93:D7:76:C9:B8
<Scan Start>
SCAN BLE_ADDR:18:93:d7:76:c9:b8
Find!
Scan End
Connect Start
Connected!
Indicate Wait...
Indicate Handle = 0x12
Flags = 0x6
C1:Temperature Measurement Value(Celsius) = 0x72:0x1:0x0:0xff
C3:Time Stamp = 0xe4:0x7:0x5:0x4:0xa:0x16:0x1c
temp = 37.0
timestamp = 2020/5/4 10:22:28
37.0 C 2020/5/4 10:22:28
wait...
wait...
except!
<end>

I got a notification on LINE properly.

IMG_6944.png

If you put a Raspberry Pi in the room and run py, it will operate as expected by repeating scan → LINE notification. If you install a thermometer and Raspberry Pi, you can also notify the body temperature of a person far away, so you can use it to check your survival.

** This is definitely a body temperature logging! ** **

Thank you for your support

I can't buy a thermometer or Raspberry Pi right now ...

Recommended Posts

Notify LINE of body temperature from BLE thermometer with Raspberry Pi # 1
Notify LINE of body temperature from BLE thermometer with Raspberry Pi # 2
Measure CPU temperature of Raspberry Pi with Python
Periodically notify the processing status of Raspberry Pi with python → Google Spreadsheet → LINE
Production of temperature control system with Raspberry Pi and ESP32 (1)
Output from Raspberry Pi to Line
Production of temperature control system with Raspberry Pi and ESP32 (2) Production of transmission device
CSV output of pulse data with Raspberry Pi (CSV output)
Get CPU information of Raspberry Pi with Python
Get temperature and humidity with DHT11 and Raspberry Pi
Record temperature and humidity with systemd on Raspberry Pi
GPGPU with Raspberry Pi
Take the value of SwitchBot thermo-hygrometer with Raspberry Pi
Log the value of SwitchBot thermo-hygrometer with Raspberry Pi
Face detection from images taken with Raspberry Pi camera
DigitalSignage with Raspberry Pi
Let's operate GPIO of Raspberry Pi with Python CGI
Simulate temperature measurement with Raspberry Pi + Flask + SQLite + Ajax
Easily make a TweetBot that notifies you of temperature and humidity with Raspberry Pi + DHT11.
Play to notify Slack of environmental data using AWS PaaS from SensorTag via Raspberry Pi3
I tried running Movidius NCS with python of Raspberry Pi3
I tried using the DS18B20 temperature sensor with Raspberry Pi
[RaspberryPi] [python] Inform LINE of room temperature with temperature sensor + IFTTT
Measure and compare temperature with Raspberry Pi and automatically generate graph
Creating a temperature / humidity monitor with Raspberry Pi (pigpio version)
How to get temperature from switchBot thermo-hygrometer using raspberry Pi
Make a BLE thermometer and get the temperature with Pythonista3
Mutter plants with Raspberry Pi
With skype, notify with skype from python!
Image recognition of garbage with Edge (Raspberry Pi) from zero knowledge using AutoML Vsion and TPU
Make a thermometer with Raspberry Pi and make it viewable with a browser Part 4
Graph display of household power consumption with 3GPI and Raspberry Pi
Get US stock price from Python with Web API with Raspberry Pi
[Note] Using 16x2-digit character LCD (1602A) from Python with Raspberry Pi
Periodically log the value of Omron environment sensor with Raspberry Pi
Use vl53l0x with Raspberry Pi (python)
Servo motor control with Raspberry Pi
Serial communication with Raspberry Pi + PySerial
OS setup with Raspberry Pi Imager
Try L Chika with raspberry pi
VPN server construction with Raspberry Pi
Notify LINE of train operation information
Try moving 3 servos with Raspberry Pi
Using a webcam with Raspberry Pi
CSV output of pulse data with Raspberry Pi (confirm analog input with python)
Try tweeting arXiv's RSS feed on twitter from Raspberry Pi with python
[Python + PHP] Make a temperature / humidity / barometric pressure monitor with Raspberry Pi
Notify LINE of location information (Google Maps) with GPS multi-unit SORACOM Edition
Logging the value of Omron environment sensor with Raspberry Pi (USB type)