Android Question Bluetooth and asyncstreams New_Data arriving late - PLEASE URGENT

EduardoElias

Well-Known Member
Licensed User
Longtime User
I am using the code below to read a paired bluetooth-serial converter.

It works very well in my test application, where I have ONE BUTTON for each command.

When I try to integrate on my application I have a big problem.

I need to send a request to the device, the request is received. And the device responds.

However the New_Data event does not happen until I get out of the SUB where I made the request.

I know I should be going event based, however this case the user is doing an action that needed that and everything depends on this. The program must wait for the answer.

In fact, the answer comes extremely quick, however the event is only fired AFTER I get out of the SUB.

I have used DOEVENTS, making a loop trying to wait for that, but that does not work.

I really dont know how to be in a loop waiting for the request arrive in the New_Data. It is milliseconds.


This is the code that I use to read the bluetooth

B4X:
'Class module
Sub Class_Globals
    Dim BTMac                As String  
    Dim Connected            As Boolean
  
    Private OSerial        As Serial
    Private OStream        As AsyncStreams
    Private RXData            As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
    OSerial.Initialize("OnSerial")
    Connected = False
End Sub

' show available BT devices for choose and return the MAC of the selected
Public Sub SelectionDialog As String
    Dim PairedDevices As Map
    PairedDevices = OSerial.GetPairedDevices
    Dim l As List
    l.Initialize
    For i = 0 To PairedDevices.Size - 1
        l.Add(PairedDevices.GetKeyAt(i))
    Next
    Dim res As Int
    res = InputList(l, "Selecione Bluetooth", -1) 'show list with paired devices
    If res <> DialogResponse.CANCEL Then
        BTMac = PairedDevices.Get(l.Get(res))
        Return BTMac
    Else
        Return ""
    End If
End Sub

Public Sub Connect
    If BTMac = "" Then
        ToastMessageShow("Informe Bluetooth as Ser conectado", False)
    Else
        If Not(Connected) Then
            OSerial.Connect(BTMac) 'convert the name to mac address and connect
        End If
    End If
End Sub

Public Sub Disconnect
    OStream.Close
    OnStream_Terminated
End Sub

Private Sub OnSerial_Connected (Success As Boolean)
    OSerial.Listen
    Connected = Success
    If Success Then
        OStream.Initialize(OSerial.InputStream, OSerial.OutputStream, "OnStream")
        ToastMessageShow("Bluetooth Conectado", False)
    End If
End Sub

Private Sub OnStream_Error
    Log("###Bluetooth "&LastException)
    OStream.Close
    OnStream_Terminated
End Sub

Private Sub OnStream_Terminated
    Log("###Bluetooth stream terminada")
    Connected = False
    OSerial.Disconnect
End Sub

Private Sub OnStream_NewData(Buffer() As Byte)
    Dim s As String
    s = OnlyNumbers(BytesToString(Buffer, 0, Buffer.Length, "UTF-8"))
    If s <> "" Then
        RXData = s
    End If
    Log("###Bluetooth recebido dados "&s)
End Sub

Sub RequestPeso
    Log("###Bluetooth request peso ")
    If OStream.IsInitialized Then
        Dim msg = Chr(5)
        OStream.Write(msg.GetBytes("UTF-8"))
'        RXData = ""
    End If
End Sub

Public Sub SendPrice(Price As String)
    Dim s As String
    Dim v As BigDecimal
    v.Initialize(Price)
    v.MovePointRight(2)
    s = Chr(2)&FillZeroes(OnlyNumbers(v.ToPlainString)&Chr(3), 6)
    OStream.Write(s.GetBytes("UTF-8"))
End Sub

Public Sub Read As String
    Dim s As String = RXData
    RXData = ""
    Log("### lido dados: "&s)
    Return(s)
End Sub

'Remove anything that is not in the range 0 -> 9
Private Sub OnlyNumbers(Value As String) As String
    Dim s As String
    s = ""
    For i = 0 To Value.Length - 1
        If (Asc(Value.CharAt(i)) >= Asc("0")) AND (Asc(Value.CharAt(i)) <= Asc("9")) Then
            s = s & Value.CharAt(i)
        End If
    Next
  
    Return s
End Sub

'Fill Zeroes to the Left according the number of digits
Private Sub FillZeroes(Value As String, Digits As Int) As String
    Dim s As String
    s = Value
    If Value.Length < Digits Then
        For i = 0 To Digits - Value.Length - 1
            s = "0" & s
        Next
    End If

    Return s
End Sub


and this is the part where I call it. You see there is a loop to retry the reading, and that I have the same thing reading from USB and working. Now I made the same thing to read from BT.

B4X:
            Dim I As Int
            Dim s As String
            Dim erro As Boolean
            I = 5
            Do While I > 0
                If FAppManager.GetConfig.GetBoolean("balancabt") Then
                    FAppManagerAct.BTMan.RequestPeso
                Else
                    FAppManagerAct.UsbMan.RequestPeso
                End If
                DoEvents
                DoEvents
                DoEvents
                DoEvents
                DoEvents
                DoEvents
                DoEvents
                erro = False
                If FAppManager.GetConfig.GetBoolean("balancabt") Then
                    s = FAppManagerAct.BTMan.Read
                Else
                    s = FAppManagerAct.UsbMan.Read
                End If
                If s.Length < 5 Then
                    I = I - 1
                    erro = True
                Else
                    FMultiplicador.Initialize(s) 'coloca o peso como multiplicador
                    div.Initialize3(1000)
                    FMultiplicador.Divide(div)
                    I = 0
                    erro = False
                End If
            Loop
          
            If erro Then
                ToastMessageShow("Problema na leitura balanca", True)
                Return -1
            End If


The idea is always the same, I send a request and get back a value. I see that working on the log. But the NEW_DATA event only happens after I get out of this SUB. After 5 times the sub get out and then I see the Log message of the NEW_DATA.

The class yBTManager is declared in a Class_Globals.

Please, I ask for help, I have a delivery date that is way too close now...


EDIT:
I see this is a recurring issue on the questions. I read the tutorials and I besides the "it runs on the main thread" nothing else I could found. In fact I see that somehow there is a kind of message loop at the main thread level. I was hoping that DoEvents would force it. The problem is that i created the application around the USB-Serial library that reads blocking and now I have an Async reading that I am trying to make Blocking.

So I need a trick to solve that, since DoEvents doesnt.
 
Last edited:

EduardoElias

Well-Known Member
Licensed User
Longtime User
If I use a CallSubDelayed to call back my SUB (or another one), it will be processed after I leave the current SUB, doing so, is the main thread able to fire the New_data event?
 
Upvote 0

EduardoElias

Well-Known Member
Licensed User
Longtime User
I have divided my sub in 2 parts:

One that makes the request

and Another that reads the data.

Then I call the second from the first using a SubCallDelayed, with the idea of let it pass thru the main thread.

My program flows correctly but the effect expected does not happen. The New_Data does not arrive.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
While you have a loop on the main thread, it is unlikely that the sub that is called by call sub delayed will ever be called. You should use the event driven process and block the user from doing anything until the data is received, or a time out is reached in case it happens to get lost, then try again.

You could block user actions with a transparent panel and consume the click/touch events, and hide the panel once the data is received.
 
Upvote 0

EduardoElias

Well-Known Member
Licensed User
Longtime User
I dont have a loop on the main thread, it is all user oriented by buttons. The loop was inside a class called by the main to deal with the request.

What I am trying to do was to use the callsubdelayed hoping it could come back to the class in another sub, since it will make the code flow go back to the main, that is happening, and then when callsubdelayed is executed it continues on the sub that then can read the data. Since the device is fast in answering.

However it seems to be not working this way either. On log I see that my data arrives (New_Data) after it goes back to the Main "totally".
 
Upvote 0

EduardoElias

Well-Known Member
Licensed User
Longtime User
While you have a loop on the main thread, it is unlikely that the sub that is called by call sub delayed will ever be called. You should use the event driven process and block the user from doing anything until the data is received, or a time out is reached in case it happens to get lost, then try again.

You could block user actions with a transparent panel and consume the click/touch events, and hide the panel once the data is received.

Thanks, followed you idea and worked. I added a transparent panel and changed instead of the CallSubDelayed from the previous sub, I have made the New_Data call the sub when new data kick in.

But, I had a full day rewriting everything... :)

Thanks
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I'm glad it's working.

The loop was inside a class called by the main to deal with the request.

It will still run on the main thread and block UI interaction.
 
Upvote 0
Top