[PyQt] How to communicate with a BLE device with PyQt5?

s.achterop at rug.nl s.achterop at rug.nl
Tue Nov 12 14:50:47 GMT 2019


Hello List,

Maybe not for this list, but I don't know a better one:(

I try to create an app using PyQt5/QML software to talk to the Bluno m0.
This is an Arduino compatible clone with a CC2540 chip from TI for BLE.
The current state of the app can be found on https://github.com/SietseAchterop/Bluno_pyqt5
It is derived from a Python port of the lowenergyscanner app from qtconnectivity.
It also contains an arduino sketch, test3.ino, for the Bluno m0 and a Linux python script, blunoTerminal.py, that works, using the bluepy library.

The problem I have is with reading and writing characteristics that come with Bluetooth low energy.
Beforehand I have to mention that the Bluno is not properly conforming to the BLE specs, meaning that it does not have the Client Characteristic Configuration Descriptor (CCCD, 0x2902) for the characteristic we use.
Notify is on per default, so it can notify without using a CCCD, as exemplified by apps on linux, android and iOS.

But I do not get response from the device in my app, which is running on Debian/Buster with PyQt5 (5.13.2)

After connecting and retrieving the services and characteristic I try to communinate with the DBF1 characteristic named 'Serial'.
Calling WriteCharacteristic works, I can see the data arriving at the device. And the device does send something back, but I cannot read it.
The only thing readCharacteristic yields is the FIRST character of the string send.

Here some code snippets from main.py:

         self.currentService.characteristicWritten.connect(self.writtenToBluno)
         self.currentService.error.connect(self.errorBluno)
         self.currentService.characteristicChanged.connect(self.charChanged)
         self.currentService.characteristicRead.connect(self.charRead)

Here the callbacks are set for reading, writing and changing the characteristic.
When pushing a "Send" button a command is created and sendtoBluno is called to write to DBF1:

     def sendtoBluno(self, charinfo):
         c = self.currentCharacteristic.getCharacteristic()
         self.currentService.writeCharacteristic(c, charinfo.encode(), QtBt.QLowEnergyService.WriteMode.WriteWithResponse)

And finally the callbacks:

     @pyqtSlot(QtBt.QLowEnergyCharacteristic, QByteArray)
     def writtenToBluno(self, c, result):
         print(f'Written callback {c.uuid()}   {result}')

     @pyqtSlot(QtBt.QLowEnergyCharacteristic, QByteArray)
     def charChanged(self, c, result):
         print(f'Changed callback {c}   {result}')

     @pyqtSlot(QtBt.QLowEnergyCharacteristic, QByteArray)
     def charRead(self, c, result):
         print(f'Read callback {c}   {result}')
         self.blresult = result
         self.characChanged.emit()

     @pyqtSlot(QtBt.QLowEnergyService.ServiceError)
     def errorBluno(self, e):
         print(f'errorBluno {e}')

When, in the app, the command hello is entered and send to the bluno, the following is shown.

    setCommand to hello
    Written callback PyQt5.QtCore.QUuid('{0000dfb1-0000-1000-8000-00805f9b34fb}')   b'hello\r'

The '\r' is added in the code, needed to get a response from the Bluno.
When I later push the "Read" button, the following function is called to read DBF1

   @pyqtSlot()
     def dddd(self):
         c = self.currentCharacteristic.getCharacteristic()
         self.currentService.readCharacteristic(c)

The response then is:

    Read callback <PyQt5.QtBluetooth.QLowEnergyCharacteristic object at 0x7f62277fb798>   b'h'

I use the separate Read button because I don't understand how reading from a characteristic works. The Qt documentation states that you have to read readCharacteristic and set a connect a signal. But if data is send on a regular basis, will that one call to readCharacteristic do and will the signal 
be used throughout?

Back to the app. The writtenToBluno callback shows the data that was send. Ok, but not very useful.
The charRead callback ONLY shows the first character of the string, also when the Read button is called more often.
I would have expected the response 'ack', with is what the bluno did send back.

The question is, why does the read not yield the data send from the Bluno.
The basic assumption is that when I do a writeCharacteristic that, when the bluno sends something back, that data should be retrievable by using readCharacteristic.

Hopefully can someone point me in the right direction.
   Thanks in advance,
                  Sietse



More information about the PyQt mailing list