Android Question AsyncStreamsObject over Bluetooth not working properly between Android and PC

Leonidas Brasileiro

Member
Licensed User
Hello.

I gathered some bits and pieces on the tutorials, examples and so on and was able to developed an app that comnunicates between PCs and Android devices over Wifi and Bluetooth using AsyncStreamsObject.

It works both ways (PC->Android and Android->PC) with Wifi, but when I try to use Bluetooth, it can only send data from Android to PC (Android->PC). Whenever I try sending data from PC to Android (PC->Android), B4A complains about CRC not matching.

Can you take a look at the code and offer some corrections?


B4A Code
B4X:
#Region Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    Private BTAdmin As BluetoothAdmin
    Private serial1 As Serial
    Private btDevices As List
    Type NameAndMac (name As String, mac As String)
    Private searchInProgress As Boolean
    Private astreamO As ASyncStreamsObject
    Private server As ServerSocket
    Private client As Socket
    Private port As Int = 32118
    Private Tipo_Conexao As String
   
End Sub

Sub Globals
    Dim spnrPairedDevices As Spinner
    Dim btnConnectBT As Button
    Dim lblWifiStatus As Label
    Dim lblBTStatus As Label
    Dim btnChooseFile As Button
    Dim lblProgress As Label
    Dim lblFile As Label
    Dim btnBTSearch As Button
   
    Private lbl_DB_Name As Label
    Private Label1 As Label
    Private rdo_Wifi As RadioButton
    Private rdo_BT As RadioButton

    Private rdo_Remoto As RadioButton
    Private Label2 As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("1")
    Activity.Title = "Data Transfer"
    Activity.AddMenuItem("Disconnect", "mnuDisconnect")
    If FirstTime Then
        btDevices.Initialize
        BTAdmin.Initialize("Admin")
        astreamO.Initialize(Me, "astreamO")
        server.Initialize(port, "server")
        client.Initialize("client")
       
    End If
   
End Sub

Sub Activity_Resume
    spnrPairedDevices.Clear
    For Each nm As NameAndMac In btDevices
        spnrPairedDevices.Add(nm.name)
    Next
    'try to turn on Bluetooth if it is disabled
    If BTAdmin.IsEnabled = False Then
        If BTAdmin.Enable = False Then
            ToastMessageShow("Error enabling Bluetooth adapter", True)
        Else
            ToastMessageShow("Enabling Bluetooth adapter... ", False)
            'the StateChanged event will be soon raised
        End If
    End If
    server.Listen   
End Sub

Sub mnuDisconnect_Click
    serial1.Disconnect
    server.Close
    client.Close
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    If UserClosed Then
        astreamO.Close
        serial1.Disconnect
        client.Close
               
    End If
   
End Sub

Sub btnConnectBT_Click
    If spnrPairedDevices.SelectedIndex = -1 Then Return
    Dim nm As NameAndMac = btDevices.Get(spnrPairedDevices.SelectedIndex)
    serial1.Initialize("serial1")
    serial1.Connect(nm.mac)
    Tipo_Conexao = "Bluetooth"
    SetBTStatus("Conectado")
    btnChooseFile.Enabled = True
   
End Sub

'starts a search process
Sub btnBTSearch_Click
    spnrPairedDevices.Clear
    btDevices.Clear
    If BTAdmin.StartDiscovery    = False Then
        ToastMessageShow("Error on search start...", True)
    Else
        searchInProgress = True
        SetBTStatus("Searching Bluetooth...")
    End If
End Sub

Sub SetBTStatus(status As String)
    lblBTStatus.Text = status
End Sub

Sub Admin_DiscoveryFinished
    searchInProgress = False
    If spnrPairedDevices.Size = 0 Then
        SetBTStatus("No Bluetooth device found")
    Else
        SetBTStatus(spnrPairedDevices.Size & " found.")
    End If
       
End Sub

Sub Admin_DeviceFound (Name As String, MacAddress As String)
    Log(Name & ":" & MacAddress)
    spnrPairedDevices.Add(Name)
    Dim nm As NameAndMac
    nm.Initialize
    nm.Name = Name
    nm.mac = MacAddress
    btDevices.Add(nm)
    SetBTStatus("Searching... (" & btDevices.Size & " found")
End Sub



' This is thr code that transfers data from Android to PC
Sub btnChooseFile_Click
    'myFile
    If File.Exists(File.DirDefaultExternal, "myFile.txt") Then
        astreamO.WriteFile("myFile.txt", File.DirDefaultExternal, "myFile.txt")
        lblFile.Text = "Transmitting myFile.txt"
    End If
   
    Msgbox("Data sent!","Confirmed")

End Sub


Sub StartAstream(s As Socket)
    astreamO.Start(s.InputStream, s.OutputStream)
    SetUIState
End Sub

Sub StartAstream_Serial(s As Serial)
    astreamO.Start(s.InputStream, s.OutputStream)
    SetUIState
End Sub

Sub AstreamO_Terminated
    SetUIState
    client.Close
    client.Initialize("client")
    serial1.Disconnect
    serial1.Initialize("")
   
End Sub

Sub client_Connected (Successful As Boolean)
    If Successful Then
        StartAstream(client)
        Tipo_Conexao = "Wifi"
    Else
        ToastMessageShow("Error: " & LastException, True)
    End If
End Sub

Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
        StartAstream(NewSocket)
    Else
        ToastMessageShow("Error: " & LastException, True)
    End If
End Sub

Sub astreamO_NewObject(Key As String, Value As Object)
    Dim fileName As String = Value
    Log("Received file " & Key & " File size: " & File.Size(astreamO.TempFolder, fileName))
   
    If File.Exists(File.DirDefaultExternal, Key) Then
        File.Delete(File.DirDefaultExternal, Key)
    End If

    File.Copy(astreamO.TempFolder, fileName, File.DirDefaultExternal, Key)

    Log(File.ListFiles(File.DirDefaultExternal))
   
End Sub

Sub astreamO_ObjectSent (Key As String)
    Log("Object sent: " & Key)
End Sub

Sub Serial1_Connected (Success As Boolean)
    ProgressDialogHide
    If Success = False Then
        Log(LastException.Message)
        ToastMessageShow("Error on connecting: " & LastException.Message, True)
        btnChooseFile.Enabled = False
    Else
        Log("Connected: " & Success)
        Tipo_Conexao = "Bluetooth"
        StartAstream_Serial(serial1)
       
    End If
End Sub

B4J Code
B4X:
#Region  Project Attributes
    #MainFormWidth: 500
    #MainFormHeight: 500
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private sp As Serial
    Private astreamO As AsyncStreamsObject
    Private client As Socket
    Private port As Int = 32118
    Dim btnConnect As Button
    Private btnSendFile As Button
    Private cmbPort As ComboBox
    Private btn_BT_Connect As Button
    Private Tipo_Conexao As String
    Private diretorio As String
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.Title = "myApp"
    MainForm.RootPane.LoadLayout("Up_Data") 'Load the layout file.
    MainForm.Show
    MainForm.BackColor = fx.Colors.White
           
    sp.Initialize("")
    cmbPort.Items.AddAll(sp.ListPorts)
   
End Sub

Sub MainForm_Closed
    astreamO.Close
    sp.Close
End Sub

Sub btnConnect_Action
    astreamO.Initialize(Me, "astreamO")
    client.Initialize("client")
    client.Connect(txtIP.Text,port, 30000)
    File.WriteString(File.DirApp, "ip.txt", txtIP.Text)
    Tipo_Conexao = "Wifi"
End Sub

Sub StartAstream(s As Socket)
    astreamO.Start(s.InputStream, s.OutputStream)

End Sub

Sub StartAstream_Serial(s As Serial)
    astreamO.Start(s.GetInputStream, s.GetOutputStream)
End Sub

Sub client_Connected (Successful As Boolean)
    If Successful Then
        StartAstream(client)
        Tipo_Conexao = "Wifi"
    Else
        Log("Error: " & LastException)
        txt_Eventos.Text = txt_Eventos.Text & "Error: " & LastException & CRLF
    End If
End Sub

Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
        StartAstream(NewSocket)
        fx.Msgbox(MainForm, LastException.Message, "Exception")
    Else
        fx.Msgbox(MainForm, "Error", "Error")
    End If
End Sub

Sub AstreamO_Terminated
    client.Close
    client.Initialize("client")
    sp.Close
    sp.Initialize("")
End Sub

Sub astreamO_NewObject(Key As String, Value As Object)
    Dim fileName As String = Value
    Log("File " & Key & " received - size: " & File.Size(astreamO.TempFolder, fileName))
    If File.Exists(diretorio, Key) Then
        File.Delete(diretorio, Key)
    End If
    File.Copy(astreamO.TempFolder, fileName, diretorio, Key)
   
End Sub

Sub btnSendFile_Action
    If astreamO.IsConnected = True Then
        Dim res As Int
        diretorio = File.DirData("myApp")
        astreamO.WriteFile("zEPI.cfg", diretorio & "\Temp", "txtfile.txt")
    Else
        fx.Msgbox(MainForm, "Not Connected", "Warning")
    End If
End Sub

Sub btn_BT_Connect_Action
    Tipo_Conexao = "Bluetooth"
    Try
        sp.Initialize("")
        sp.Open(cmbPort.Value)
    Catch
        If LastException.Message.Contains("Port busy.") = True Then
            Dim rspt As Int
            rspt = fx.Msgbox2(MainForm, "Port " & cmbPort.Items.Get(cmbPort.SelectedIndex) & " is busy" & CRLF & CRLF & "Want to close it and try again?", "Warning", "Yes", "", "No", fx.MSGBOX_CONFIRMATION)
            If rspt = fx.DialogResponse.POSITIVE Then
                sp.Close
                sp.Initialize("")
            End If
        End If
        Return
    End Try
   
    astreamO.Initialize(Me, "astreamO")
    StartAstream_Serial(sp)
   
End Sub
 

Leonidas Brasileiro

Member
Licensed User
Hello...

I'm converting my app from using AsyncStreamObject to B4XSerializator, but I can't get pass some instability.
I'm staring at this code for hours and can't figure out what is wrong.

The intended feature is to transfer rather big files (SQLite databases) from PC to Android and back using either Bluetooth or Wifi.

Can you guys take a look?

Once this is completed I think it will be a good example in the forum.

Full code for B4A and B4J are attached.

What is puzzling me is how to sync 'astream' - the variable I'm using for AsyncStreams object on both ends.

What I'm trying is this:

On B4A - buttons for establishing connection:

B4X:
Sub btnConnectWifi_Click
    Tipo_Conexao = "Wifi"
    btnChooseFile.Enabled = True
    server.Initialize(port, "server")
    server.Listen
    client.Initialize("client")
   
End Sub

Sub btnConnectBT_Click
    If spnrPairedDevices.SelectedIndex = -1 Then Return
    Dim nm As NameAndMac = btDevices.Get(spnrPairedDevices.SelectedIndex)
    serial1.Initialize("serial1")
    serial1.Connect(nm.mac)
    Tipo_Conexao = "Bluetooth"
    SetUIState
    btnChooseFile.Enabled = True
   
End Sub

On B4A - events triggering InitializePrefix
B4X:
Sub server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
        client = NewSocket
        astream.InitializePrefix(client.InputStream, False, client.OutputStream, "astream")
    Else
        ToastMessageShow("Error: " & LastException, True)
    End If
    server.Listen
End Sub

Sub serial1_Connected (Success As Boolean)
    If Success = False Then
        Log(LastException.Message)
        ToastMessageShow("Error on concecting: " & LastException.Message, True)
        btnChooseFile.Enabled = False
    Else
        Log("Connected: " & Success)
        Tipo_Conexao = "Bluetooth"
        astream.InitializePrefix(serial1.InputStream, False, serial1.OutputStream, "astream")
    End If   
End Sub

On the B4J side, I have the following:

Buttons for starting connection

B4X:
Sub btn_BT_Connect_Action
    Tipo_Conexao = "Bluetooth"
    Try
        sp.Initialize("")
        sp.Open(cmbPort.Value)
        is_serial_Connected = True
    Catch
        If LastException.Message.Contains("Port busy.") = True Then
            Dim rspt As Int
            rspt = fx.Msgbox2(MainForm, "Port " & cmbPort.Items.Get(cmbPort.SelectedIndex) & " is busy" & CRLF & CRLF & "Wnat to close it and try again?", "Warning", "Yes", "", "No", fx.MSGBOX_CONFIRMATION)
            If rspt = fx.DialogResponse.POSITIVE Then
                sp.Close
                sp.Initialize("")
                Return
            End If
        End If
        is_serial_Connected = False
    End Try
    SetUIState
   
End Sub

Sub btnConnect_Action
    client.Initialize("client")
    client.Connect(txtIP.Text,port, 30000)
    File.WriteString(File.DirApp, "ip.txt", txtIP.Text)
    Tipo_Conexao = "Wifi"
    conta_arquivos = 0
    SetUIState
End Sub

Events triggering InitializePrefix on B4J
B4X:
Sub sp_Connected(Success As Boolean)
    If Success = False Then
        Log(LastException.Message)
    Else
        Log("Conectado: " & Success)
        Tipo_Conexao = "Bluetooth"
        astream.InitializePrefix(sp.GetInputStream, False, sp.GetOutputStream, "astream")
    End If   
   
End Sub

Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
        client = NewSocket
        astream.InitializePrefix(client.InputStream, False, client.OutputStream, "astream")
    Else
        fx.Msgbox(MainForm, "Erro", "Erro")
    End If
End Sub

A further development (not to be adressed now) is to make the Android device retrieve data from a MySQL web server using Remote Database Connector and update internal SQLite databases. But I have to get past this first.

Please advise if this issue fits better on a new thread. I'll happily move it.

Thanks.

Leonidas.
 

Attachments

  • Transferer_B4A.zip
    10.7 KB · Views: 173
  • Transferer_B4J.zip
    5 KB · Views: 130
Upvote 0

Leonidas Brasileiro

Member
Licensed User
Hello..

I reviewed both B4A and B4J projects and I was partially successful in making it work.

Now I can send files from B4J to B4A with either Wifi or Bluetooth. From B4A to B4J it only works with Wifi. Whenever I try to send a file from B4A to B4J I get an error related to 'zLIB too far back' among others. Sorry for that. I know I should have recorded precisely the stack feedback.


First thing I had to correct was to syncronize 'InitializePrefix' on both sides. In order to do that, the routine used to establish connection also Initializes the ASyncStream on both sides.

B4A - Wifi Connection:

B4X:
Sub server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
        client = NewSocket
        astream.InitializePrefix(client.InputStream, False, client.OutputStream, "astream")
    Else
        ToastMessageShow("Error: " & LastException, True)
    End If
    server.Listen
End Sub


B4A Bluetooth Connection:

B4X:
Sub serial1_Connected (Success As Boolean)
    If Success = False Then
        Log(LastException.Message)
        ToastMessageShow("Error on concecting: " & LastException.Message, True)
        btnChooseFile.Enabled = False
        If LastException.Message.Contains("socket might closed or timeout") Then
            Msgbox("Serial Port on the PC is closed", "Warning")
            SetBTStatus("Port closed on PC")
            spnrPairedDevices.SelectedIndex = -1
        End If
    Else
        Log("Connected: " & Success)
        Tipo_Conexao = "Bluetooth"
        astream.InitializePrefix(serial1.InputStream, False, serial1.OutputStream, "astream")
    End If   
End Sub

B4J - Wifi Connection:

B4X:
Sub client_Connected (Successful As Boolean)
    If Successful Then
        txt_Eventos.Text = txt_Eventos.Text & "Conectado a " & File.ReadString(File.DirApp, "ip.txt") & CRLF
        Tipo_Conexao = "Wifi"
        astream.InitializePrefix(client.InputStream, False, client.OutputStream, "astream")
    Else
        Log("Error: " & LastException)
        txt_Eventos.Text = txt_Eventos.Text & "Error: " & LastException & CRLF
    End If
    SetUIState
End Sub

B4J - Bluetooth Connection:

B4X:
Sub btn_BT_Connect_Action
    Tipo_Conexao = "Bluetooth"
    Try
        sp.Initialize("")
        sp.Open(cmbPort.Value)
        is_serial_Connected = True
        astream.InitializePrefix(sp.GetInputStream, False, sp.GetOutputStream, "astream")
    Catch
        If LastException.Message.Contains("Port busy.") = True Then
            Dim rspt As Int
            rspt = fx.Msgbox2(MainForm, "Port " & cmbPort.Items.Get(cmbPort.SelectedIndex) & " is busy" & CRLF & CRLF & "Wnat to close it and try again?", "Warning", "Yes", "", "No", fx.MSGBOX_CONFIRMATION)
            If rspt = fx.DialogResponse.POSITIVE Then
                sp.Close
                sp.Initialize("")
                Return
            End If
        End If
        is_serial_Connected = False
    End Try
    SetUIState
End Sub

I'd rather raise Prefixed Initialization of the ASyncStream object on the 'connected' event of the serial connection (sp, in the present case), something like the code below:

B4X:
Sub sp_Connected (Success As Boolean)
    If Success = False Then
        Log(LastException.Message)
    Else
        Log("Connected: " & Success)
        Tipo_Conexao = "Bluetooth"
        astream.InitializePrefix(sp.GetInputStream, False, sp.GetOutputStream, "astream")
    End If   
End Sub

Unfortunately I couldn't find such event. I tried it by typing 'Sub ' and hitting 'Tab'. No 'Serial' object.

I know I'm close to making it work. Any hints are welcome. What is missing?

Projects are attached.

For the file 'mybigfile.txt' I just made a dummy text file with 200 paragraphs of 'lorem ipsum'

Thanks.
 

Attachments

  • Transferer(B4A).zip
    10.9 KB · Views: 161
  • Transferer(B4J).zip
    5 KB · Views: 167
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
There is no connected event in jSerial as the PC doesn't know whether it is connected or not. This is not related to the communication problem you encounter. Note that I can send data successfully over Bluetooth from the Android to the PC (I haven't tested it with your project).

It is worth testing it on a different PC.
 
Upvote 0

Leonidas Brasileiro

Member
Licensed User
Hello.

I feel it is very close to get this working!

I made some additional tests and noticed that data is being effectively sent over Bluetooth by Androis, although it can't be read properly by the PC.

I also noticed that the 'astream_NewData' event on the PC is only triggered on the second push of the 'Send' button on Android, although the app seems to send data, since the function used to send it is the same as the one presented in the forum.

Any ideas on why this happens?

This is the Log generated on B4J:

B4X:
Waiting for debugger to connect...
Program started.
java.lang.NegativeArraySizeException
    at anywheresoftware.b4a.randomaccessfile.AsyncStreams$AIN.run(AsyncStreams.java:253)
    at java.lang.Thread.run(Thread.java:745)
Class not found: lbmdata.transferer.main$_filetosend, trying: lbmdata.TransfererPC.main$_filetosend
Error occurred on line: 144
java.lang.RuntimeException: java.util.zip.ZipException: incorrect data check
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readType(B4XSerializator.java:295)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readObject(B4XSerializator.java:355)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.ReadObject(B4XSerializator.java:110)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.ConvertBytesToObject(B4XSerializator.java:81)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:612)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:226)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:159)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:93)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:90)
    at anywheresoftware.b4a.BA$3.run(BA.java:178)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.util.zip.ZipException: incorrect data check
    at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:164)
    at java.io.DataInputStream.readFully(DataInputStream.java:195)
    at java.io.DataInputStream.readFully(DataInputStream.java:169)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readObject(B4XSerializator.java:349)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readMap(B4XSerializator.java:229)
    at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readType(B4XSerializator.java:273)
    ... 24 more

What gets my attention is the 'incorrect data check' exception. Does that has something to do with duplicating the size of the amount of bytes sent due to double sending the file? How to overcome this?
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Any ideas on why this happens?
This is a hint that some of the data was lost. AsyncStreams in prefix mode will wait until the number of bytes it expect are collected. To debug this issue you can switch to non-prefix mode (in both sides) and check what you are receiving on the PC. Some of the data will not be there.

Send regular array of bytes to test it.
 
Upvote 0

Leonidas Brasileiro

Member
Licensed User
Hello,

After some tests it seems that there is a limitation on the size of file that can be handled when transferring from Android to PC. It worked with 227 KB and failed to work with 490 KB in my case. Limit must be somewhere in between.

As the data to be transferred by my app in that direction will never be that big, I can live with that... Although I would like to know if this is a local constraint (my Android device and my computer) And where is the issue. Is it an Android or PC issue?
 
Upvote 0
Top