Android Question B4A Library [B4X] BLE 2 - Bluetooth Low Energy : Send / Receive Data

GiovanniPolese

Well-Known Member
Licensed User
Longtime User
Hi to Everybody
I have an Esp32 (Heltec V3) device which communicates with BLE. Using existing Apps it works. Now my problem is to make an App which sends and receives data (Ascii strings). A sort of "Chat". After browsing posts here and trying some examples, I see that receiving data is someway explained, but not sending data. Does someone here have any hint?
Thanks in advance
 

GiovanniPolese

Well-Known Member
Licensed User
Longtime User
Hi Erel. Thanks. But there are some examples on the site and I am confused. There is a BLE_Example as long as a BLEExample and B4XPeripherals which deal with BLE, if I remember well. Moreover somebody also did a Ble3 library. Normally I start from examples and the situation is not clear. In the meawhile I developed an App which sends data but doesn't receive an answer. It works on Sending. It is connected to a device named "Remote" which receives the command and sends an answer, and I already verified the process with other commercial Apps. Therefore I just have the problem of receiving the answer. I attach here the code as long as the log. Thanks again.
B4X:
#Region  Project Attributes 
    #ApplicationLabel: BleTest
    #VersionCode: 1
    #VersionName: 
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes 
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
    Dim rp As RuntimePermissions
    Private ble As BleManager2
    Private rp As RuntimePermissions

    Private SERVICE_UUID As String = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
    Private WRITE_UUID As String = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
    Private NOTIFY_UUID As String = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    Private btnSend As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout")
    If FirstTime Then
        ble.Initialize("ble")
    End If
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Button1_Click
    'xui.MsgboxAsync("Hello world!", "B4X")
    StartScan
End Sub

Sub btnSend_Click
    ble.WriteData(SERVICE_UUID, WRITE_UUID, "A".GetBytes("UTF8"))
    Log("TX: A")
End Sub

Sub StartScan
    rp.CheckAndRequest(rp.PERMISSION_ACCESS_FINE_LOCATION)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)

    If Result Then
        ble.Scan(Array As String())
    End If
End Sub

Sub ble_DeviceFound (Name As String, Id As String, AdvertisingData As Map, RSSI As Double)
    Log("Trovato: " & Name)

    If Name = "Remote" Then
        ble.StopScan
        ble.Connect(Id)
    End If
End Sub

Sub ble_ServicesDiscovered (Services As List)
    Log("Services OK")

    Sleep(300)

    ble.SetNotify(SERVICE_UUID, NOTIFY_UUID, True)
    Log("Notify attivato")
End Sub

Sub ble_Connected (Services As List)
    Log("Connesso!")
    CallSubDelayed(Me, "EnableNotify")
End Sub

Sub ble_CharacteristicChanged (ServiceId As String, CharacteristicId As String, Value() As Byte)
    Log("RAW RX: " & BytesToString(Value, 0, Value.Length, "UTF8"))
End Sub

Sub EnableNotify
    Log("Attivo notify...")
    Sleep(500)

    'ble.SetIndication(SERVICE_UUID, NOTIFY_UUID, True)
    ble.SetNotify(SERVICE_UUID, NOTIFY_UUID, True)
End Sub

The log:
** Activity (main) Create (first time) **
** Activity (main) Resume **
Trovato:
Trovato: Remote
Discovering services.
Connesso!
Attivo notify...
Setting descriptor. Success = true
writing descriptor: true
TX: A
 
Upvote 0

GiovanniPolese

Well-Known Member
Licensed User
Longtime User
Ok. I normally use B4XPages. This is just a preliminary experiment, to verify communication. I must investigate this problem better. Thanks for your patience.
 
Upvote 0

GiovanniPolese

Well-Known Member
Licensed User
Longtime User
Hi again.
With my surprise, the project attached, gets some results. Therefore, before aborting this way, i decided to go on with the discussion. As a matter of fact the code is simple and also the behaviour is simple. So, probably, it is simple to close the discussion. Nevertheless maybe there may be something useful in this temptative. The flow is the following:
1) btnSend_Click calls WriteData
2) a first DataAvailable event is raised. it contains a "b" object of length 7, but it seems null (trying to log(b) crashes). So what is it?
3) a second btnSend is triggered: this is the first strange thing: i didn't click the button twice for sure
4) a second DataAvailable even is raised. The "b" object now contains the correct answer, but here also we have something strange: the length is always 20, and, effectively, the answer is truncated.
This is what happens.
The code:
B4X:
#Region  Project Attributes 
    #ApplicationLabel: BleTest
    #VersionCode: 1
    #VersionName: 
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes 
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
    Dim rp As RuntimePermissions
    Private ble As BleManager2
    Private rp As RuntimePermissions
    Private DeviceName="Remote" As String
    Private SERVICE_UUID As String = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
    Private WRITE_UUID   As String = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
    Private NOTIFY_UUID  As String = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
    
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    Private btnSend As Button
    Private edtCommand As EditText
    Private lblAnswer As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout")
    If FirstTime Then
        ble.Initialize("ble")
    End If
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub btnScan_Click
    Wait For (StartScan) Complete (Result As Boolean)
    btnSend.Enabled=Result
End Sub

Sub btnSend_Click
    Log("Sent : " & edtCommand.Text)
    ble.WriteData(SERVICE_UUID, WRITE_UUID,edtCommand.Text.GetBytes("UTF8"))
    lblAnswer.Text=""
End Sub

Sub StartScan As ResumableSub
    rp.CheckAndRequest(rp.PERMISSION_ACCESS_FINE_LOCATION)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)

    If Result Then
        ble.Scan(Array As String())
    End If
    Return Result
End Sub

Sub ble_DeviceFound (Name As String, Id As String, AdvertisingData As Map, RSSI As Double)
    Log("Found: " & Name)

    If Name = DeviceName Then
        ble.StopScan
        ble.Connect(Id)
    End If
End Sub

Sub ble_ServicesDiscovered (Services As List)
    Log("Services OK")

End Sub

Sub ble_Connected (Services As List)
    Log("Connected!")
    Sleep(1000)
    ble.SetNotify(SERVICE_UUID, NOTIFY_UUID, True)
    Log("Notify activated")
End Sub

Sub ble_DataAvailable (sid As String, Characteristics As Map)
    'Log("Data available>" & sid & "<")
    Dim b() As Byte = Characteristics.Get(NOTIFY_UUID)
    Log(b.Length)
    If b<>Null Then
        lblAnswer.Text = BytesToString(b, 0, b.Length, "utf8")
        Log(lblAnswer.Text)
    End If
End Sub

[/code}

the Log:

** Activity (main) Pause, UserClosed = true **
** Activity (main) Create (first time) **
** Activity (main) Resume **
Found: Remote
Found: 
Discovering services.
Connected!
Setting descriptor. Success = true
writing descriptor: true
Notify activated
Sent : A
7
Sent: A
20
A SRX3,102838,3135,9  <<< two caracters are missing, but for longer answers, only 20 chars are got
 

Attachments

  • BleTest.zip
    9.3 KB · Views: 11
Upvote 0

GiovanniPolese

Well-Known Member
Licensed User
Longtime User
Moreover, i did another step ahead : adding a ReadData after the WriteData, and a delay, i get the right answer, despite the situation is not clear:
B4X:
Sub btnSend_Click
    Log("Sent : " & edtCommand.Text)
    ble.WriteData(SERVICE_UUID, WRITE_UUID,edtCommand.Text.GetBytes("UTF8"))
    lblAnswer.Text=""
    Sleep(10000)
    ble.ReadData(SERVICE_UUID)
End Sub
the log is: (with << my comments)

Found: Remote
Discovering services.
Connected!
Setting descriptor. Success = true
writing descriptor: true
Notify activated
Sent : A << first command clicked
7
Sent: A << first null answer
20
A SRX3,102838,3135,9 << first incomplete answer
23
A SRX3,102838,3135,9022 << second complete answer
Sent : Ea << another command
8
Sent: Ea << first null answer
20
Ea 0100,0,0.000,0,1. << first incomplete answer
40
Ea 0100,0,0.000,0,1.351,83.6136,330.0428 << second complete answer
 
Upvote 0

GiovanniPolese

Well-Known Member
Licensed User
Longtime User
As a further element, the delay before the ReadData must be enough to allow the answer to be delivered by the other device. Moreover i got a final conclusion, which is barely heuristic: the DataAvailable events raised before that the delay put between the WriteData and the ReadData must be discarded: the right answer is got after that the Sleep between the two above calls has elapsed. Of course this is not a clean solution, but, at least it is a fact.
So I introduced a new variable, discarding first 2 wrong DataAvailable.
The log is:
** Activity (main) Create (first time) **

** Activity (main) Resume **
Found:
Found: Remote
Discovering services.
Connected!
Setting descriptor. Success = true
writing descriptor: true
Notify activated
Sent : A
Now Reading..
23
A SRX3,102838,3135,9022 << correct
Sent : Ea
Now Reading..
40
Ea 0100,0,0.000,0,1.351,83.6132,330.0450 << correct

The code is attached for any other eventual comment.
 

Attachments

  • BleTest.zip
    9.4 KB · Views: 8
Upvote 0

GiovanniPolese

Well-Known Member
Licensed User
Longtime User
At the end, I better defined the situation, and obtained a satisfying condition. I still have doubts and, for what I have read, what I did is not teoretically correct. But it works in my case, and I eliminated the problem of putting an arbitrary Sleep between the WriteData and a following ReadData. According to theory, I should not use ReadData. BTW I wonder myself why this function was done.. Anyway the points are resumed in the following:
1) I have two Esp32 Heltec V3 connected via LoRa.
2) First Heltec (Remote) rceives a BLE command from a smartphone, sends to the other Heltec (Proxy), which sends the command to an instrument (via BT classic, because it has also an HC05 board on it), this second Heltec (Proxy) waits the consequent answer from the Instrument, sends it to the first Heltec (Remote) which finally delivers the answer back to the phone, via BLE.
3) The problem concerns only the BLE between smartphone and Heltec (Remote) i.e. the last step.
4) The sketch on Remote delivers only a notification, when returns the answer. This is verified by logs inside the sketch.
5) Nevertheless the smartphone receives two DataAvailable events, the first with incomplete data, the second with everything ok.
6) The only solution that I found is to put a ReadData inside the DataAvailable event. Of course preventing repetitions etc. (see code)
7) I suppose that with another sketch not issuing the double notification, things may run better.

In my case, everything is managed by these two subs:

B4X:
Sub btnSend_Click
    Log("Sent : " & edtCommand.Text)
    ble.WriteData(SERVICE_UUID, WRITE_UUID,edtCommand.Text.GetBytes("UTF8"))
    lblAnswer.Text=""
    discard=True
End Sub

Sub ble_DataAvailable (sid As String, Characteristics As Map)
    
    Dim b() As Byte = Characteristics.Get(NOTIFY_UUID)
    
    If b<>Null Then
        lblAnswer.Text = BytesToString(b, 0, b.Length, "utf8")
        Log(lblAnswer.Text)
        If discard=True Then 
          ble.ReadData(SERVICE_UUID)
          discard=False
        End If
    End If
    btnSend.Enabled=True
End Sub
[code]
 

Attachments

  • BleTest.zip
    9.3 KB · Views: 8
Upvote 0
Cookies are required to use this site. You must accept them to continue using the site. Learn more…