EEVblog Electronics Community Forum

Products => Test Equipment => Topic started by: kgmuzungu on December 08, 2019, 07:04:35 pm

Title: 121GW bluetooth logging with Python
Post by: kgmuzungu on December 08, 2019, 07:04:35 pm
Hi,

my dream would come true if I simply could receive voltage readings from the 121GW via bluetooth with Python (preferably on Windows but Linux is fine too). I have not started digging around. I will post my findings.

If anyone has already a solution or hints, pls let me know. Also if you found a solution not using Python, pls let me know.

Looking forward,
K
Title: Re: 121GW bluetooth logging with Python
Post by: kgmuzungu on December 10, 2019, 07:36:43 pm
Slowly converging towards the goal. The 121GW uses Bluetooth 4.x Low Energy, which cannot be simply accessed via PySerial library.

Nice introduction to Bluetooth Low Energy: https://www.youtube.com/watch?v=5fQR2PHMDWE (https://www.youtube.com/watch?v=5fQR2PHMDWE)
Great Android app where can see check what BLE devices are sending: https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en (https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en)

I tried to install python bleak. That is tricky. You need Visual Studio installed, path variables set correctly and all sorts of things. I ran into too many troubles getting pythonnet, the bridge from Python to UWP/.NET so that I dumped this approach. I guess if you get pythonnet installed than you have a good chance to read values from the 121GW.

I did not try under Linux yet, as it is not my primary goal.

My next try will be to buy a BLED112 dongle which supports BGAPI which in turn should be accessible via PyGATT. I hope that does the trick then.
Title: Re: 121GW bluetooth logging with Python
Post by: kgmuzungu on December 19, 2019, 09:53:57 pm
SOLVED

For Windows 10 use the BLED112 Bluetooth Low Energy dongle.
I have used pygatt (https://github.com/peplin/pygatt (https://github.com/peplin/pygatt))
also useful https://sigrok.org/wiki/EEVBlog_121GW (https://sigrok.org/wiki/EEVBlog_121GW)
121GW firmware 2.02

Example Python code (Python 3.8)
Code: [Select]
from binascii import hexlify
from datetime import datetime
import pygatt
import logging
import time

# logging.basicConfig()
# logging.getLogger('pygatt').setLevel(logging.DEBUG)

# The BGAPI backend will attempt to auto-discover the serial device name of the
# attached BGAPI-compatible USB adapter.
# GATTTool only works under Linux
# adapter = pygatt.backends.GATTToolBackend()

# adapter = pygatt.BGAPIBackend()
adapter = pygatt.BGAPIBackend(serial_port='COM3')


def callback_func(handle, data):
    global count
    global prevTimeStamp
    timestamp = datetime.now()
    timeDelta = (timestamp - prevTimeStamp).microseconds
    print("§§§ in callback: handle:", handle)
    print("§§§ in callback: raw data:", data)
    # print("§§§ in callback: hex data: %s" % hexlify(data))
    print("§§§ in callback: str data:", hexlify(data).decode('utf-8'))
    print("§§§ in callback: str data length:", len(hexlify(data).decode('utf-8')))
    print("§§§ timedelta: ", timeDelta)
    prevTimeStamp = timestamp
    count = count + 1
    print("§§§ count:", count)
    print("")


prevTimeStamp = datetime.now()
print(prevTimeStamp)
count = 0

try:
    print("§§§ before start")
    adapter.start()

    """
    result = adapter.scan(timeout=5)
    for item in result:
        scan_name = item['name']
        scan_rssi = item['rssi']
        scan_address = item['address']
        print(scan_address, scan_name, scan_rssi)
    """

    print("§§§ after start. adapter: ", adapter)
    device = adapter.connect('88:6B:0F:D3:0C:CF')
    print("§§§ after connect. device: ", device)
    print("§§§ rssi:", device.get_rssi())
    # uuid here has to be a characteristic, like 121GW multimeter custom characteristic:
    # you cannot read this characteristic directly. it is WRITE, INDICATE. you have to subscribe it.
    uuid = "e7add780-b042-4876-aae1-112855353cc1"
    print("§§§ read handle:", device.get_handle(uuid))
    device.subscribe(uuid, callback=callback_func, indication=True)
    print("§§§ after subscribe")
    print("§§§ before sleep")
    time.sleep(5)
    print("§§§ after sleep")
finally:
    adapter.stop()

With the 7th reading you get a stable sequence of one reading returns 0xf2 and the following 0x17... (36 char)
With the first byte 0xf2: primary display: byte 6 mode, byte 7 range, byte 8 & 9 value; secondary display: byte 10: mode, byte 11: range?, bytes 12 & 13 value

Range is 0x02 corresponds with the 3rd range in the datasheet. If the reading would be negative the range would be 42 not 02
 
mode 01 = VDC
mode 03 = mVDC
mode 04 = mVAC
mode 09 = ohm
not complete...

example
volt DC 1.2940 to hex 0x328c , sec display temp 22.0 hex 0xdc, range to 5VDC, range 0x00, mode 0x01
example = 'f2178000000100328c640100de00060a40002d'